Skip to content

Commit

Permalink
feat: support LinkML field names with spaces. (#75)
Browse files Browse the repository at this point in the history
* Temp commit, improve error msgs.

* Don't depend on a base Entity class in the platformics lib.

* Convert spaces in field names to underscores in python, and camelCase in GQL

* Make sure there's an actual value there.
  • Loading branch information
jgadling authored Jul 10, 2024
1 parent a783d89 commit f7636ed
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 8 deletions.
8 changes: 4 additions & 4 deletions platformics/codegen/lib/linkml_wrappers.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,19 @@ 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:
return self.wrapped_field.identifier

@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:
Expand Down Expand Up @@ -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:
Expand Down
8 changes: 4 additions & 4 deletions platformics/codegen/templates/api/types/class_name.py.j2
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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).
"""
Expand Down Expand Up @@ -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).
"""
Expand Down Expand Up @@ -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).
"""
Expand Down
2 changes: 2 additions & 0 deletions test_app/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -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:
"""
Expand Down
2 changes: 2 additions & 0 deletions test_app/schema/schema.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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}'
Expand Down
44 changes: 44 additions & 0 deletions test_app/tests/test_field_with_spaces.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
"""
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"]
assert output["data"]["constraintCheckedTypes"][0]["fieldWithSpaces"]

0 comments on commit f7636ed

Please sign in to comment.