From dd7d83fccb6a9e57ed8d74847286e8715e0d98e3 Mon Sep 17 00:00:00 2001 From: Jessica Gadling Date: Tue, 9 Jul 2024 12:04:33 -0400 Subject: [PATCH 1/4] Temp commit, improve error msgs. --- platformics/codegen/lib/linkml_wrappers.py | 4 ++-- test_app/conftest.py | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/platformics/codegen/lib/linkml_wrappers.py b/platformics/codegen/lib/linkml_wrappers.py index a17fd6a..1a9745a 100644 --- a/platformics/codegen/lib/linkml_wrappers.py +++ b/platformics/codegen/lib/linkml_wrappers.py @@ -25,7 +25,7 @@ def __getattr__(self, attr: str) -> str: """ Error if a property doesn't exist """ - raise NotImplementedError(f"please define field property {attr}") + raise NotImplementedError(f"please define field property {self.wrapped_field.name}.{attr}") @cached_property def identifier(self) -> str: @@ -207,7 +207,7 @@ def __init__(self, view: SchemaView, wrapped_class: ClassDefinition): # Blow up if a property doesn't exist def __getattr__(self, attr: str) -> str: - raise NotImplementedError(f"please define entity property {attr}") + raise NotImplementedError(f"please define entity property {self.wrapped_class.name}.{attr}") @cached_property def name(self) -> str: diff --git a/test_app/conftest.py b/test_app/conftest.py index 2c48927..0c6dfd1 100644 --- a/test_app/conftest.py +++ b/test_app/conftest.py @@ -231,6 +231,7 @@ async def patched_session() -> typing.AsyncGenerator[AsyncSession, None]: def raise_exception() -> str: raise Exception("Unexpected error") + # Subclass Query with an additional field to test Exception handling. @strawberry.type class MyQuery(Query): @@ -239,6 +240,7 @@ def uncaught_exception(self) -> str: # Trigger an AttributeException return self.kaboom # type: ignore + @pytest_asyncio.fixture() async def api_test_schema(async_db: AsyncDB) -> FastAPI: """ From 6dc15eed19770149c7d9569971a2fbd2ca51f73c Mon Sep 17 00:00:00 2001 From: Jessica Gadling Date: Tue, 9 Jul 2024 17:50:34 -0400 Subject: [PATCH 2/4] Don't depend on a base Entity class in the platformics lib. --- platformics/codegen/templates/api/types/class_name.py.j2 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/platformics/codegen/templates/api/types/class_name.py.j2 b/platformics/codegen/templates/api/types/class_name.py.j2 index 559cd12..fbc9190 100644 --- a/platformics/codegen/templates/api/types/class_name.py.j2 +++ b/platformics/codegen/templates/api/types/class_name.py.j2 @@ -62,7 +62,7 @@ from support.enums import {%- endfor %} -E = typing.TypeVar("E", base_db.File, base_db.Entity) +E = typing.TypeVar("E", base_db.File, db.{{ cls.name }}) T = typing.TypeVar("T") if TYPE_CHECKING: @@ -507,7 +507,7 @@ async def create_{{ cls.snake_name }}( cerbos_client: CerbosClient = Depends(get_cerbos_client), principal: Principal = Depends(require_auth_principal), is_system_user: bool = Depends(is_system_user), -) -> db.Entity: +) -> db.{{ cls.name }}: """ Create a new {{ cls.name }} object. Used for mutations (see api/mutations.py). """ @@ -567,7 +567,7 @@ async def update_{{ cls.snake_name }}( cerbos_client: CerbosClient = Depends(get_cerbos_client), principal: Principal = Depends(require_auth_principal), is_system_user: bool = Depends(is_system_user), -) -> Sequence[db.Entity]: +) -> Sequence[db.{{ cls.name }}]: """ Update {{ cls.name }} objects. Used for mutations (see api/mutations.py). """ @@ -640,7 +640,7 @@ async def delete_{{ cls.snake_name }}( session: AsyncSession = Depends(get_db_session, use_cache=False), cerbos_client: CerbosClient = Depends(get_cerbos_client), principal: Principal = Depends(require_auth_principal), -) -> Sequence[db.Entity]: +) -> Sequence[db.{{ cls.name }}]: """ Delete {{ cls.name }} objects. Used for mutations (see api/mutations.py). """ From 7374ca95c98dc4b510ae0d5ac2ef9ee675ecc50d Mon Sep 17 00:00:00 2001 From: Jessica Gadling Date: Wed, 10 Jul 2024 11:36:12 -0400 Subject: [PATCH 3/4] Convert spaces in field names to underscores in python, and camelCase in GQL --- platformics/codegen/lib/linkml_wrappers.py | 4 +- test_app/schema/schema.yaml | 2 + test_app/tests/test_field_with_spaces.py | 43 ++++++++++++++++++++++ 3 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 test_app/tests/test_field_with_spaces.py diff --git a/platformics/codegen/lib/linkml_wrappers.py b/platformics/codegen/lib/linkml_wrappers.py index 1a9745a..58c1582 100644 --- a/platformics/codegen/lib/linkml_wrappers.py +++ b/platformics/codegen/lib/linkml_wrappers.py @@ -33,11 +33,11 @@ def identifier(self) -> str: @cached_property def name(self) -> str: - return self.wrapped_field.name + return self.wrapped_field.name.replace(" ", "_") @cached_property def camel_name(self) -> str: - return strcase.to_lower_camel(self.wrapped_field.name) + return strcase.to_lower_camel(self.name) @cached_property def multivalued(self) -> str: diff --git a/test_app/schema/schema.yaml b/test_app/schema/schema.yaml index 49620f9..6618966 100644 --- a/test_app/schema/schema.yaml +++ b/test_app/schema/schema.yaml @@ -293,6 +293,8 @@ classes: annotations: minimum_length: 3 maximum_length: 8 + field with spaces: + range: string regex_format_check: range: string pattern: '\d{3}-\d{2}-\d{4}' diff --git a/test_app/tests/test_field_with_spaces.py b/test_app/tests/test_field_with_spaces.py new file mode 100644 index 0000000..66db431 --- /dev/null +++ b/test_app/tests/test_field_with_spaces.py @@ -0,0 +1,43 @@ +""" +Test basic queries and mutations +""" + +import datetime +import pytest +from platformics.database.connect import SyncDB +from conftest import GQLTestClient, SessionStorage +from test_infra.factories.constraint_checked_type import ConstraintCheckedTypeFactory + +date_now = datetime.datetime.now() + + +@pytest.mark.asyncio +async def test_field_with_spaces( + sync_db: SyncDB, + gql_client: GQLTestClient, +) -> None: + """ + Test that we can only fetch samples from the database that we have access to + """ + user_id = 12345 + project_id = 123 + + # Create mock data + with sync_db.session() as session: + SessionStorage.set_session(session) + cct = ConstraintCheckedTypeFactory.create( + owner_user_id=user_id, + collection_id=project_id, + ) + + # Fetch the row via gql + query = """ + query MyQuery { + constraintCheckedTypes { + id, + fieldWithSpaces + } + } + """ + output = await gql_client.query(query, user_id=user_id, member_projects=[project_id]) + assert cct.field_with_spaces == output["data"]["constraintCheckedTypes"][0]["fieldWithSpaces"] From e14a0c5f3c334d93de05e28c2ebb41d5b059bd4d Mon Sep 17 00:00:00 2001 From: Jessica Gadling Date: Wed, 10 Jul 2024 11:45:18 -0400 Subject: [PATCH 4/4] Make sure there's an actual value there. --- test_app/tests/test_field_with_spaces.py | 1 + 1 file changed, 1 insertion(+) diff --git a/test_app/tests/test_field_with_spaces.py b/test_app/tests/test_field_with_spaces.py index 66db431..a3fcb57 100644 --- a/test_app/tests/test_field_with_spaces.py +++ b/test_app/tests/test_field_with_spaces.py @@ -41,3 +41,4 @@ async def test_field_with_spaces( """ output = await gql_client.query(query, user_id=user_id, member_projects=[project_id]) assert cct.field_with_spaces == output["data"]["constraintCheckedTypes"][0]["fieldWithSpaces"] + assert output["data"]["constraintCheckedTypes"][0]["fieldWithSpaces"]