diff --git a/openapi/__init__.py b/openapi/__init__.py index 2ef6be4..2bb837b 100644 --- a/openapi/__init__.py +++ b/openapi/__init__.py @@ -1,4 +1,4 @@ """Minimal OpenAPI asynchronous server application """ -__version__ = '0.9.1' +__version__ = '0.9.2' diff --git a/openapi/db/path.py b/openapi/db/path.py index e6d6044..f1274d0 100644 --- a/openapi/db/path.py +++ b/openapi/db/path.py @@ -7,7 +7,7 @@ from ..db.dbmodel import CrudDB from ..spec.path import ApiPath -unique_regex = re.compile(r'Key \((?P\w+)\)=\((?P.+)\)') +unique_regex = re.compile(r'Key \((?P(\w+,? ?)+)\)=\((?P.+)\)') class SqlApiPath(ApiPath): diff --git a/tests/example/db.py b/tests/example/db.py index 33a6b91..c662c2c 100644 --- a/tests/example/db.py +++ b/tests/example/db.py @@ -66,4 +66,11 @@ def meta(meta=None): nullable=False) ) + sa.Table( + 'multi_key_unique', meta, + sa.Column('x', sa.Integer, nullable=False), + sa.Column('y', sa.Integer, nullable=False), + sa.UniqueConstraint('x', 'y') + ) + return meta diff --git a/tests/example/endpoints.py b/tests/example/endpoints.py index 6be1b8a..4b38304 100644 --- a/tests/example/endpoints.py +++ b/tests/example/endpoints.py @@ -8,6 +8,7 @@ from .models import ( Task, TaskAdd, TaskQuery, TaskPathSchema, TaskUpdate, TaskPathSchema2, TaskOrderableQuery, + MultiKey, ) @@ -424,3 +425,26 @@ class NoTagDescriptionPath(SqlApiPath): - Random """ pass + + +@routes.view('/multikey') +class MultiKeyPath(SqlApiPath): + """ + --- + summary: Create rows in multikey constraint table + tags: + - MultiKey + """ + table = 'multi_key_unique' + + @op(response_schema=MultiKey, body_schema=MultiKey) + async def post(self): + """ + --- + summary: Create row in multi-column constrained table + responses: + 201: + description: New row + """ + data = await self.create_one() + return self.json_response(data, status=201) diff --git a/tests/example/models.py b/tests/example/models.py index 38916e5..47c37a9 100644 --- a/tests/example/models.py +++ b/tests/example/models.py @@ -77,3 +77,9 @@ class TaskPathSchema: @dataclass class TaskPathSchema2: task_id: str = uuid_field(required=True, description='Task ID') + + +@dataclass +class MultiKey: + x: int + y: int diff --git a/tests/test_db.py b/tests/test_db.py index 5d9fa6b..cde5d48 100644 --- a/tests/test_db.py +++ b/tests/test_db.py @@ -242,9 +242,9 @@ async def test_spec_root(cli): spec = await jsonBody(response) assert 'paths' in spec assert 'tags' in spec - assert len(spec['tags']) == 3 - assert spec['tags'][1]['name'] == 'Task' - assert spec['tags'][1]['description'] == 'Simple description' + assert len(spec['tags']) == 4 + assert spec['tags'][2]['name'] == 'Task' + assert spec['tags'][2]['description'] == 'Simple description' async def test_transaction_create(cli): @@ -387,3 +387,21 @@ async def test_decimal_zero_returned(cli): get_body = await jsonBody(get_resp, status=200) assert get_body['story_points'] == Decimal(0) + + +async def test_multicolumn_unique_constraint(cli): + row = { + 'x': 1, + 'y': 2 + } + resp = await cli.post( + '/multikey', + json=row + ) + await jsonBody(resp, status=201) + + resp = await cli.post( + '/multikey', + json=row + ) + await jsonBody(resp, status=422)