A REST API for cloud embedded board reservation.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

139 lines
3.9 KiB

  1. #
  2. # Copyright (c) 2020 The FreeBSD Foundation
  3. #
  4. # This software1 was developed by John-Mark Gurney under sponsorship
  5. # from the FreeBSD Foundation.
  6. #
  7. # Redistribution and use in source and binary forms, with or without
  8. # modification, are permitted provided that the following conditions
  9. # are met:
  10. # 1. Redistributions of source code must retain the above copyright
  11. # notice, this list of conditions and the following disclaimer.
  12. # 2. Redistributions in binary form must reproduce the above copyright
  13. # notice, this list of conditions and the following disclaimer in the
  14. # documentation and/or other materials provided with the distribution.
  15. #
  16. # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  17. # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  18. # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  19. # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  20. # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  21. # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  22. # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  23. # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  24. # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  25. # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  26. # SUCH DAMAGE.
  27. #
  28. # Documentation for orm:
  29. # https://github.com/encode/orm
  30. #
  31. # To interactively work with a database:
  32. # import bitelab.data
  33. # import databases
  34. # database = databases.Database('sqlite:///' + dbpath)
  35. # data = bitelab.data.make_orm(database)
  36. from typing import Optional, Union, Dict, Any
  37. from pydantic import BaseModel, Field
  38. from datetime import datetime
  39. import databases
  40. import orm
  41. import sqlalchemy
  42. import tempfile
  43. import unittest
  44. __all__ = [ 'make_orm', 'BoardClassInfo', 'Board', 'Error' ]
  45. class BoardClassInfo(BaseModel):
  46. clsname: str
  47. arch: str
  48. class Board(BaseModel):
  49. name: str
  50. brdclass: str
  51. reserved: bool
  52. attrs: Dict[str, Any] = Field(default_factory=dict)
  53. class Config:
  54. orm_mode = True
  55. class Error(BaseModel):
  56. error: str
  57. board: Optional[Board]
  58. def _issubclass(a, b):
  59. try:
  60. return issubclass(a, b)
  61. except TypeError:
  62. return False
  63. class DataWrapper(object):
  64. pass
  65. def make_orm(database):
  66. metadata = sqlalchemy.MetaData()
  67. class BoardStatus(orm.Model):
  68. __tablename__ = 'boardstatus'
  69. __database__ = database
  70. __metadata__ = metadata
  71. board = orm.Text(primary_key=True)
  72. user = orm.Text(index=True)
  73. time_reserved = orm.DateTime(default=lambda: datetime.utcnow())
  74. class APIKey(orm.Model):
  75. __tablename__ = 'apikeys'
  76. __database__ = database
  77. __metadata__ = metadata
  78. user = orm.Text(index=True)
  79. key = orm.Text(primary_key=True)
  80. engine = sqlalchemy.create_engine(str(database.url))
  81. metadata.create_all(engine)
  82. r = DataWrapper()
  83. lcls = locals()
  84. for i in [ 'engine', 'metadata', ] + [ x for x in lcls if _issubclass(lcls[x], orm.Model) ]:
  85. setattr(r, i, lcls[i])
  86. return r
  87. async def _setup_data(data):
  88. await data.APIKey.objects.create(user='foo', key='thisisanapikey')
  89. await data.APIKey.objects.create(user='bar', key='anotherlongapikey')
  90. class TestDatabase(unittest.IsolatedAsyncioTestCase):
  91. def setUp(self):
  92. # setup temporary directory
  93. self.dbtempfile = tempfile.NamedTemporaryFile()
  94. self.database = databases.Database('sqlite:///' +
  95. self.dbtempfile.name)
  96. self.data = make_orm(self.database)
  97. def tearDown(self):
  98. self.data = None
  99. self.database = None
  100. self.dbtempfile = None
  101. async def test_apikey(self):
  102. data = self.data
  103. # that the test database starts empty
  104. self.assertEqual(await data.APIKey.objects.all(), [])
  105. # that when it is populated with test data
  106. await _setup_data(data)
  107. # the data can be accessed
  108. self.assertEqual((await data.APIKey.objects.get(
  109. key='thisisanapikey')).user, 'foo')
  110. self.assertEqual((await data.APIKey.objects.get(
  111. key='anotherlongapikey')).user, 'bar')