Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support LinkML field names with spaces. #75

Merged
merged 4 commits into from
Jul 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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}")
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just making the error messages a little more verbose / easier to debug.


@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 }}:
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These return the current type, there's no reason to tell the type checker that we return the base type. I think. 😅

"""
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
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An example of a field with spaces in its name!

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"]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check that this field is wired up properly from the test infrastructure all the way to the GQL interface!

assert output["data"]["constraintCheckedTypes"][0]["fieldWithSpaces"]
Loading