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

Custom root level queries and mutations via inheritance #77

Merged
merged 3 commits into from
Jul 11, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
53 changes: 53 additions & 0 deletions docs/custom_code.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# How To: Extending Platformics Generated Code
Platformics will generate a GraphQL API that exposes CRUD operations for each entity type in your LinkML schema. It is often the case, however, that you will need to expose more functionality in your API than just CRUD. There are a few options for extending generated code to add custom business logic to your application.

## Adding root-level queries and mutations
Platformics will generate basic read and aggregate queries in `api/queries.py`. These are inherited by a query class in `custom_queries.py`, which will gain access to all the codegenned queries as well as allow you to implement your own queries. To add a new root level field (query), add the field to the query class in `custom_queries.py`:

```
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: you can do ```python for python syntax highlighting!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

ooh!

@strawberry.type
class Query(BaseQuery):
foo: str = my_custom_field
```
The resolver for the custom field should be decorated with `@strawberry.field`:
```
@strawberry.field
def my_custom_field(self) -> str:
return "foo"
```

Similarly, for custom mutations, root level mutations can be added to the `Mutation` class in `custom_mutations.py`, which inherits from the codegenned `Mutation` class in `api/mutations.py`

```
@strawberry.type
class Mutation(BaseMutation):
bar: str = my_custom_mutation
```
The resolver for the custom mutation should be decorated with `@strawberry.mutation`:
```
@strawberry.mutation
def my_custom_mutation(self) -> str:
return "bar"
```
Both the custom query and mutation classes are imported into `main.py` and used by Strawberry to generate an API at runtime:
```
from custom_mutations import Mutation
from custom_queries import Query

schema = strawberry.Schema(query=Query, mutation=Mutation, config=get_strawberry_config(), extensions=[HandleErrors()])
```

### Overriding queries and mutations
By creating a field with the same name as an existing codegenned query or mutation, you can override the codegenned resolver.
```
@strawberry.mutation
def my_override_mutation(self) -> str:
return "Sorry, I overrode the mutation"

@strawberry.type
class Mutation(BaseMutation):
create_sample: str = my_override_mutation
```

## Using your own templates
TODO: fill this out. Existing functionality.
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
17 changes: 17 additions & 0 deletions test_app/custom_mutations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import strawberry
from api.mutations import Mutation as BaseMutation


@strawberry.mutation
def my_custom_mutation(self) -> str:
return "bar"

@strawberry.mutation
def my_override_mutation(self) -> str:
return "Sorry, I overrode the mutation"


@strawberry.type
class Mutation(BaseMutation):
bar: str = my_custom_mutation
create_sample: str = my_override_mutation
12 changes: 12 additions & 0 deletions test_app/custom_queries.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import strawberry
from api.queries import Query as BaseQuery


@strawberry.field
def my_custom_field(self) -> str:
return "foo"


@strawberry.type
class Query(BaseQuery):
foo: str = my_custom_field
5 changes: 3 additions & 2 deletions test_app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@
from platformics.settings import APISettings
from database import models

from api.mutations import Mutation
from api.queries import Query
from custom_mutations import Mutation
from custom_queries import Query


settings = APISettings.model_validate({}) # Workaround for https://github.com/pydantic/pydantic/issues/3753
schema = strawberry.Schema(query=Query, mutation=Mutation, config=get_strawberry_config(), extensions=[HandleErrors()])
Expand Down
Loading