Skip to content

Commit

Permalink
feature/mx-1648 fix endpoint serializing (#119)
Browse files Browse the repository at this point in the history
### Changes

- BREAKING: use `items` instead of `results` in paged wiki response
- downgrade query logging to log level `debug`

### Fixed

- fix endpoint serializing not working with `mex.common.types`
  • Loading branch information
cutoffthetop authored Jul 29, 2024
1 parent d4f77a6 commit ca915ba
Show file tree
Hide file tree
Showing 18 changed files with 59 additions and 51 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changes

- BREAKING: use `items` instead of `results` in paged wiki response
- downgrade query logging to log level `debug`

### Deprecated

### Removed

### Fixed

- fix endpoint serializing not working with `mex.common.types`

### Security

## [0.16.0] - 2024-07-26
Expand Down
10 changes: 4 additions & 6 deletions mex/backend/auxiliary/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,11 @@

from pydantic import BaseModel

T = TypeVar("T")
ResponseItemT = TypeVar("ResponseItemT", bound=BaseModel)


class PagedResponseSchema(BaseModel, Generic[T]):
"""Response schema for any paged API."""
class PagedAuxiliaryResponse(BaseModel, Generic[ResponseItemT]):
"""Response model for any paged aux API."""

items: list[ResponseItemT]
total: int
offset: int
limit: int
results: list[T]
16 changes: 8 additions & 8 deletions mex/backend/auxiliary/wikidata.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
from typing import Annotated

from fastapi import APIRouter, Query
from fastapi.responses import JSONResponse

from mex.backend.auxiliary.models import PagedResponseSchema
from mex.backend.auxiliary.models import PagedAuxiliaryResponse
from mex.backend.transform import to_primitive
from mex.common.models import ExtractedOrganization, ExtractedPrimarySource
from mex.common.primary_source.extract import extract_seed_primary_sources
from mex.common.primary_source.transform import (
Expand All @@ -22,13 +24,13 @@
router = APIRouter()


@router.get("/wikidata", status_code=200, tags=["wikidata"])
@router.get("/wikidata", status_code=200, tags=["editor"])
def search_organization_in_wikidata(
q: Annotated[str, Query(min_length=1, max_length=1000)],
offset: Annotated[int, Query(ge=0, le=10e10)] = 0,
limit: Annotated[int, Query(ge=1, le=100)] = 10,
lang: TextLanguage = TextLanguage.EN,
) -> PagedResponseSchema[ExtractedOrganization]:
) -> PagedAuxiliaryResponse[ExtractedOrganization]:
"""Search an organization in wikidata.
Args:
Expand All @@ -49,12 +51,10 @@ def search_organization_in_wikidata(
)
)

return PagedResponseSchema(
total=total_orgs,
offset=offset,
limit=limit,
results=[organization for organization in extracted_organizations],
response = PagedAuxiliaryResponse[ExtractedOrganization](
items=extracted_organizations, total=total_orgs
)
return JSONResponse(to_primitive(response)) # type: ignore[return-value]


@cache
Expand Down
5 changes: 4 additions & 1 deletion mex/backend/extracted/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
from typing import Annotated

from fastapi import APIRouter, Query
from fastapi.responses import JSONResponse

from mex.backend.extracted.models import ExtractedItemSearchResponse
from mex.backend.graph.connector import GraphConnector
from mex.backend.transform import to_primitive
from mex.backend.types import ExtractedType
from mex.common.types import Identifier

Expand All @@ -30,4 +32,5 @@ def search_extracted_items(
skip,
limit,
)
return ExtractedItemSearchResponse.model_validate(result.one())
response = ExtractedItemSearchResponse.model_validate(result.one())
return JSONResponse(to_primitive(response)) # type: ignore[return-value]
2 changes: 1 addition & 1 deletion mex/backend/extracted/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@
class ExtractedItemSearchResponse(BaseModel):
"""Response body for the extracted item search endpoint."""

total: int
items: Annotated[list[AnyExtractedModel], Field(discriminator="entityType")]
total: int
4 changes: 2 additions & 2 deletions mex/backend/graph/connector.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,9 +150,9 @@ def commit(self, query: str, **parameters: Any) -> Result:
logger.error("\n%s\n%s", message, error)
raise
if counters := result.get_update_counters():
logger.info("\n%s\n%s", message, json.dumps(counters, indent=4))
logger.debug("\n%s\n%s", message, json.dumps(counters, indent=4))
else:
logger.info("\n%s", message)
logger.debug("\n%s", message)
return result

def fetch_extracted_data(
Expand Down
4 changes: 2 additions & 2 deletions mex/backend/identity/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@ class IdentityAssignRequest(BaseModel):
class IdentityFetchResponse(BaseModel):
"""Response body for identity fetch requests."""

items: list[Identity] = []
total: int = 0
items: list[Identity]
total: int
5 changes: 4 additions & 1 deletion mex/backend/ingest/main.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
from fastapi import APIRouter
from fastapi.responses import JSONResponse

from mex.backend.graph.connector import GraphConnector
from mex.backend.ingest.models import BulkIngestRequest, BulkIngestResponse
from mex.backend.transform import to_primitive

router = APIRouter()

Expand All @@ -12,4 +14,5 @@ def ingest_extracted_items(request: BulkIngestRequest) -> BulkIngestResponse:
connector = GraphConnector.get()
models = request.get_all()
identifiers = connector.ingest(models)
return BulkIngestResponse(identifiers=identifiers)
response = BulkIngestResponse(identifiers=identifiers)
return JSONResponse(to_primitive(response), 201) # type: ignore[return-value]
5 changes: 4 additions & 1 deletion mex/backend/merged/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
from typing import Annotated

from fastapi import APIRouter, Query
from fastapi.responses import JSONResponse

from mex.backend.graph.connector import GraphConnector
from mex.backend.merged.models import MergedItemSearchResponse
from mex.backend.transform import to_primitive
from mex.backend.types import MergedType, UnprefixedType
from mex.common.transform import ensure_prefix
from mex.common.types import Identifier
Expand Down Expand Up @@ -46,4 +48,5 @@ def search_merged_items_facade(
item["identifier"] = item.pop("stableTargetId")
item["entityType"] = item["entityType"].replace("Extracted", "Merged")

return MergedItemSearchResponse.model_validate(result.one())
response = MergedItemSearchResponse.model_validate(result.one())
return JSONResponse(to_primitive(response)) # type: ignore[return-value]
2 changes: 1 addition & 1 deletion mex/backend/merged/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@
class MergedItemSearchResponse(BaseModel):
"""Response body for the merged item search endpoint."""

total: int
items: Annotated[list[AnyMergedModel], Field(discriminator="entityType")]
total: int
5 changes: 2 additions & 3 deletions mex/backend/rules/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,5 @@
def create_rule(rule: AnyRuleModel) -> AnyRuleModel:
"""Create a new rule."""
connector = GraphConnector.get()
return JSONResponse( # type: ignore[return-value]
to_primitive(connector.create_rule(rule)),
)
response = connector.create_rule(rule)
return JSONResponse(to_primitive(response)) # type: ignore[return-value]
6 changes: 3 additions & 3 deletions tests/auxiliary/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,22 @@
TEST_DATA_DIR = Path(__file__).parent / "test_data"


@pytest.fixture
@pytest.fixture()
def wikidata_organization_raw() -> dict[str, Any]:
"""Return a raw wikidata organization."""
with open(TEST_DATA_DIR / "wikidata_organization_raw.json") as fh:
return json.load(fh)


@pytest.fixture
@pytest.fixture()
def wikidata_organization(
wikidata_organization_raw: dict[str, Any],
) -> WikidataOrganization:
"""Return a wikidata organization instance."""
return WikidataOrganization.model_validate(wikidata_organization_raw)


@pytest.fixture
@pytest.fixture()
def mocked_wikidata(
monkeypatch: MonkeyPatch, wikidata_organization_raw: dict[str, Any]
) -> None:
Expand Down
9 changes: 3 additions & 6 deletions tests/auxiliary/test_wikidata.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@
from mex.common.types import Text


@pytest.mark.usefixtures(
"mocked_wikidata",
)
@pytest.mark.usefixtures("mocked_wikidata")
def test_search_organization_in_wikidata_mocked(
client_with_api_key_read_permission: TestClient, monkeypatch: MonkeyPatch
) -> None:
Expand Down Expand Up @@ -39,10 +37,9 @@ def extracted_primary_source_wikidata() -> ExtractedPrimarySource:

assert organizations["total"] == expected_total
assert (
organizations["results"][0]["identifierInPrimarySource"]
organizations["items"][0]["identifierInPrimarySource"]
== expected_organization_identifier
)
assert (
organizations["results"][0]["officialName"]
== expected_organization_official_name
organizations["items"][0]["officialName"] == expected_organization_official_name
)
18 changes: 9 additions & 9 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,28 +56,28 @@ def skip_integration_test_in_ci(is_integration_test: bool) -> None:
"""Overwrite fixture from plugin to not skip integration tests in ci."""


@pytest.fixture
@pytest.fixture()
def client() -> TestClient:
"""Return a fastAPI test client initialized with our app."""
with TestClient(app, raise_server_exceptions=False) as test_client:
return test_client


@pytest.fixture
@pytest.fixture()
def client_with_api_key_write_permission(client: TestClient) -> TestClient:
"""Return a fastAPI test client with write permission initialized with our app."""
client.headers.update({"X-API-Key": "write_key"})
return client


@pytest.fixture
@pytest.fixture()
def client_with_api_key_read_permission(client: TestClient) -> TestClient:
"""Return a fastAPI test client with read permission granted by API key."""
client.headers.update({"X-API-Key": "read_key"})
return client


@pytest.fixture
@pytest.fixture()
def client_with_basic_auth_read_permission(client: TestClient) -> TestClient:
"""Return a fastAPI test client with read permission granted by basic auth."""
client.headers.update(
Expand All @@ -86,7 +86,7 @@ def client_with_basic_auth_read_permission(client: TestClient) -> TestClient:
return client


@pytest.fixture
@pytest.fixture()
def client_with_basic_auth_write_permission(client: TestClient) -> TestClient:
"""Return a fastAPI test client with write permission granted by basic auth."""
client.headers.update(
Expand Down Expand Up @@ -121,7 +121,7 @@ def call_args_list(self) -> list[Any]:
return self.run.call_args_list


@pytest.fixture
@pytest.fixture()
def mocked_graph(monkeypatch: MonkeyPatch) -> MockedGraph:
"""Mock the graph connector and return the mocked `run` for easy manipulation."""
records: list[Any] = []
Expand Down Expand Up @@ -183,7 +183,7 @@ def isolate_graph_database(
driver.execute_query(f"DROP INDEX {row['name']};")


@pytest.fixture
@pytest.fixture()
def dummy_data() -> list[AnyExtractedModel]:
"""Create a set of interlinked dummy data."""
primary_source_1 = ExtractedPrimarySource(
Expand Down Expand Up @@ -237,14 +237,14 @@ def dummy_data() -> list[AnyExtractedModel]:
]


@pytest.fixture
@pytest.fixture()
def load_dummy_data(dummy_data: list[AnyExtractedModel]) -> list[AnyExtractedModel]:
"""Ingest dummy data into the graph."""
GraphConnector.get().ingest(dummy_data)
return dummy_data


@pytest.fixture
@pytest.fixture()
def additive_organizational_unit(
dummy_data: list[AnyExtractedModel],
) -> AdditiveOrganizationalUnit:
Expand Down
2 changes: 1 addition & 1 deletion tests/graph/test_connector.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from tests.conftest import MockedGraph


@pytest.fixture
@pytest.fixture()
def mocked_query_builder(monkeypatch: MonkeyPatch) -> None:
def __getattr__(_: QueryBuilder, query: str) -> Callable[..., str]:
return lambda **parameters: format_str(
Expand Down
8 changes: 4 additions & 4 deletions tests/graph/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from mex.common.testing import Joker


@pytest.fixture
@pytest.fixture()
def summary() -> Mock:
class SummaryCounters:
def __init__(self) -> None:
Expand All @@ -27,7 +27,7 @@ def __init__(self) -> None:
return Mock(spec=Neo4jResultSummary, counters=SummaryCounters())


@pytest.fixture
@pytest.fixture()
def multiple_results(summary: Mock) -> Mock:
records = [
Mock(spec=Neo4jRecord, data=MagicMock(return_value={"num": 40})),
Expand All @@ -39,14 +39,14 @@ def multiple_results(summary: Mock) -> Mock:
)


@pytest.fixture
@pytest.fixture()
def no_result(summary: Mock) -> Mock:
return Mock(
spec=Neo4jResult, to_eager_result=MagicMock(return_value=([], summary, []))
)


@pytest.fixture
@pytest.fixture()
def single_result(summary: Mock) -> Mock:
records = [
Mock(
Expand Down
2 changes: 1 addition & 1 deletion tests/graph/test_query.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from mex.backend.graph.query import QueryBuilder, render_constraints


@pytest.fixture
@pytest.fixture()
def query_builder() -> QueryBuilder:
builder = QueryBuilder.get()
builder._env.globals.update(
Expand Down
2 changes: 1 addition & 1 deletion tests/ingest/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
Payload = dict[str, list[dict[str, Any]]]


@pytest.fixture
@pytest.fixture()
def post_payload(dummy_data: list[AnyExtractedModel]) -> Payload:
payload = defaultdict(list)
for model in dummy_data:
Expand Down

0 comments on commit ca915ba

Please sign in to comment.