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: query parameter model #151

Merged
merged 4 commits into from
Dec 2, 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 README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,9 @@ The result set can be mapped to a nested Pydantic model like so:
```python
from typing import Annotated

from fastapi import FastAPI
from fastapi import FastAPI, Query
from pydantic import BaseModel, ConfigDict
from rdfproxy import Page, SPARQLBinding, SPARQLModelAdapter
from rdfproxy import Page, QueryParameters, SPARQLBinding, SPARQLModelAdapter

class Work(BaseModel):
model_config = ConfigDict(group_by="workName")
Expand Down Expand Up @@ -77,8 +77,8 @@ The `SPARQLModelAdapter.query` method runs the query and constructs a `Page` obj
app = FastAPI()

@app.get("/")
def base_route(page: int = 1, size: int = 100) -> Page[Author]:
return adapter.query(page=page, size=size)
def base_route(query_parameters: Annotated[QueryParameters, Query()]) -> Page[Author]:
return adapter.query(query_parameters)
```

This results in the following JSON output:
Expand Down
8 changes: 4 additions & 4 deletions examples/full_static_fastapi_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

from typing import Annotated

from fastapi import FastAPI
from fastapi import FastAPI, Query
from pydantic import BaseModel, ConfigDict
from rdfproxy import Page, SPARQLBinding, SPARQLModelAdapter
from rdfproxy import Page, QueryParameters, SPARQLBinding, SPARQLModelAdapter


query = """
Expand Down Expand Up @@ -48,5 +48,5 @@ class Author(BaseModel):


@app.get("/")
def base_route(page: int = 1, size: int = 100) -> Page[Author]:
return adapter.query(page=page, size=size)
def base_route(query_parameters: Annotated[QueryParameters, Query()]) -> Page[Author]:
return adapter.query(query_parameters)
12 changes: 8 additions & 4 deletions examples/releven_person_fastapi_example.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
"""RDFProxy-based FastAPI route example: CRM query targeting Releven GraphDB with simple ungrouped Person model."""

from fastapi import FastAPI
from typing import Annotated

from fastapi import FastAPI, Query
from pydantic import BaseModel
from rdfproxy import Page, SPARQLModelAdapter
from rdfproxy import Page, QueryParameters, SPARQLModelAdapter


query = """
Expand Down Expand Up @@ -66,5 +68,7 @@ class R11PersonModel(BaseModel):


@app.get("/")
def base(page: int = 1, size: int = 100) -> Page[R11PersonModel]:
return adapter.query(page=page, size=size)
def base_route(
query_parameters: Annotated[QueryParameters, Query()],
) -> Page[R11PersonModel]:
return adapter.query(query_parameters)
8 changes: 4 additions & 4 deletions examples/wikidata_grouped_person_fastapi_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

from typing import Annotated

from fastapi import FastAPI
from fastapi import FastAPI, Query
from pydantic import BaseModel, ConfigDict
from rdfproxy import Page, SPARQLBinding, SPARQLModelAdapter
from rdfproxy import Page, QueryParameters, SPARQLBinding, SPARQLModelAdapter


query = """
Expand Down Expand Up @@ -40,5 +40,5 @@ class Person(BaseModel):


@app.get("/")
def base(page: int = 1, size: int = 100) -> Page[Person]:
return adapter.query(page=page, size=size)
def base_route(query_parameters: Annotated[QueryParameters, Query()]) -> Page[Person]:
return adapter.query(query_parameters)
8 changes: 4 additions & 4 deletions examples/wikidata_ungrouped_person_fastapi_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

from typing import Annotated

from fastapi import FastAPI
from fastapi import FastAPI, Query
from pydantic import BaseModel
from rdfproxy import Page, SPARQLBinding, SPARQLModelAdapter
from rdfproxy import Page, QueryParameters, SPARQLBinding, SPARQLModelAdapter


query = """
Expand Down Expand Up @@ -38,5 +38,5 @@ class Person(BaseModel):


@app.get("/")
def base(page: int = 1, size: int = 100) -> Page[Person]:
return adapter.query(page=page, size=size)
def base_route(query_parameters: Annotated[QueryParameters, Query()]) -> Page[Person]:
return adapter.query(query_parameters)
2 changes: 1 addition & 1 deletion rdfproxy/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from rdfproxy.adapter import SPARQLModelAdapter # noqa: F401
from rdfproxy.mapper import ModelBindingsMapper # noqa: F401
from rdfproxy.utils._types import SPARQLBinding # noqa: F401
from rdfproxy.utils.models import Page # noqa: F401
from rdfproxy.utils.models import Page, QueryParameters # noqa: F401
25 changes: 13 additions & 12 deletions rdfproxy/adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from SPARQLWrapper import JSON, SPARQLWrapper
from rdfproxy.mapper import ModelBindingsMapper
from rdfproxy.utils._types import _TModelInstance
from rdfproxy.utils.models import Page
from rdfproxy.utils.models import Page, QueryParameters
from rdfproxy.utils.sparql_utils import (
calculate_offset,
construct_count_query,
Expand Down Expand Up @@ -42,19 +42,14 @@ def __init__(
)
self.sparql_wrapper.setReturnFormat(JSON)

def query(
self,
*,
page: int = 1,
size: int = 100,
) -> Page[_TModelInstance]:
def query(self, query_parameters: QueryParameters) -> Page[_TModelInstance]:
"""Run a query against an endpoint and return a Page model object."""
count_query: str = construct_count_query(query=self._query, model=self._model)
items_query: str = construct_items_query(
query=self._query,
model=self._model,
limit=size,
offset=calculate_offset(page, size),
limit=query_parameters.size,
offset=calculate_offset(query_parameters.page, query_parameters.size),
)

items_query_bindings: Iterator[dict] = query_with_wrapper(
Expand All @@ -65,9 +60,15 @@ def query(

items: list[_TModelInstance] = mapper.get_models()
total: int = self._get_count(count_query)
pages: int = math.ceil(total / size)

return Page(items=items, page=page, size=size, total=total, pages=pages)
pages: int = math.ceil(total / query_parameters.size)

return Page(
items=items,
page=query_parameters.page,
size=query_parameters.size,
total=total,
pages=pages,
)

def _get_count(self, query: str) -> int:
"""Run a count query and return the count result.
Expand Down
12 changes: 11 additions & 1 deletion rdfproxy/utils/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from typing import Generic

from pydantic import BaseModel
from pydantic import BaseModel, Field
from rdfproxy.utils._types import _TModelInstance


Expand All @@ -21,3 +21,13 @@ class Page(BaseModel, Generic[_TModelInstance]):
size: int
total: int
pages: int


class QueryParameters(BaseModel):
"""Query parameter model for SPARQLModelAdapter.query.

See https://fastapi.tiangolo.com/tutorial/query-param-models/
"""

page: int = Field(default=1, gt=0)
size: int = Field(default=100, ge=1)
5 changes: 3 additions & 2 deletions tests/tests_adapter/test_adapter_grouped_pagination.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import pytest

from pydantic import BaseModel, ConfigDict
from rdfproxy import Page, SPARQLModelAdapter
from rdfproxy import Page, QueryParameters, SPARQLModelAdapter


query = """
Expand Down Expand Up @@ -104,4 +104,5 @@ class AdapterParameter(NamedTuple):
["adapter", "query_parameters", "expected"], adapter_parameters
)
def test_basic_adapter_grouped_pagination(adapter, query_parameters, expected):
assert adapter.query(**query_parameters) == expected
parameters = QueryParameters(**query_parameters)
assert adapter.query(parameters) == expected