Skip to content

Commit

Permalink
Clean up enum, add tests, bump versions
Browse files Browse the repository at this point in the history
  • Loading branch information
cutoffthetop committed Jan 19, 2024
1 parent 0bcac39 commit 5af07fa
Show file tree
Hide file tree
Showing 14 changed files with 366 additions and 65 deletions.
2 changes: 1 addition & 1 deletion mex/backend/extracted/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def search_extracted_items(
query_results = graph.query_nodes(
q,
stableTargetId,
[t.value for t in entityType or ExtractedType],
[str(t.value) for t in entityType or ExtractedType],
skip,
limit,
)
Expand Down
20 changes: 5 additions & 15 deletions mex/backend/extracted/models.py
Original file line number Diff line number Diff line change
@@ -1,31 +1,21 @@
from enum import Enum, EnumMeta, _EnumDict
from enum import Enum
from typing import TYPE_CHECKING, Union

from pydantic import Field

from mex.backend.types import DynamicStrEnum
from mex.common.models import (
EXTRACTED_MODEL_CLASSES_BY_NAME,
BaseExtractedData,
BaseModel,
)
from mex.common.transform import dromedary_to_snake


class ExtractedTypeMeta(EnumMeta):
"""Meta class to dynamically populate the entity type enumeration."""

def __new__(
cls: type["ExtractedTypeMeta"], name: str, bases: tuple[type], dct: _EnumDict
) -> "ExtractedTypeMeta":
"""Create a new entity type enum by adding an entry for each model."""
for entity_type in EXTRACTED_MODEL_CLASSES_BY_NAME:
dct[dromedary_to_snake(entity_type).upper()] = entity_type
return super().__new__(cls, name, bases, dct)


class ExtractedType(Enum, metaclass=ExtractedTypeMeta):
class ExtractedType(Enum, metaclass=DynamicStrEnum):
"""Enumeration of possible types for extracted items."""

__names__ = list(EXTRACTED_MODEL_CLASSES_BY_NAME)


if TYPE_CHECKING: # pragma: no cover
AnyExtractedModel = BaseExtractedData
Expand Down
3 changes: 1 addition & 2 deletions mex/backend/graph/connector.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,13 @@
from mex.common.logging import logger
from mex.common.models import (
EXTRACTED_MODEL_CLASSES_BY_NAME,
MEX_PRIMARY_SOURCE_IDENTIFIER,
MEX_PRIMARY_SOURCE_IDENTIFIER_IN_PRIMARY_SOURCE,
MEX_PRIMARY_SOURCE_STABLE_TARGET_ID,
ExtractedPrimarySource,
)
from mex.common.types import Identifier

MEX_PRIMARY_SOURCE_IDENTIFIER = Identifier("00000000000000")


class GraphConnector(BaseConnector):
"""Connector to handle authentication and transactions with the graph database."""
Expand Down
18 changes: 0 additions & 18 deletions mex/backend/identity/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,6 @@
from mex.backend.graph.transform import transform_identity_result_to_identity
from mex.common.exceptions import MExError
from mex.common.identity import BaseProvider, Identity
from mex.common.models import (
MEX_PRIMARY_SOURCE_IDENTIFIER_IN_PRIMARY_SOURCE,
MEX_PRIMARY_SOURCE_STABLE_TARGET_ID,
)
from mex.common.types import Identifier, PrimarySourceID


Expand All @@ -29,20 +25,6 @@ def assign(
raise MExError("found multiple identities indicating graph inconsistency")
if len(graph_result.data) == 1:
return transform_identity_result_to_identity(graph_result.data[0])
if (
identifier_in_primary_source
== MEX_PRIMARY_SOURCE_IDENTIFIER_IN_PRIMARY_SOURCE
and had_primary_source == MEX_PRIMARY_SOURCE_STABLE_TARGET_ID
):
# This is to deal with the edge case where primary source is the parent of
# all primary sources and has no parents for itself,
# this will add itself as its parent.
return Identity(
hadPrimarySource=had_primary_source,
identifier=MEX_PRIMARY_SOURCE_STABLE_TARGET_ID,
identifierInPrimarySource=identifier_in_primary_source,
stableTargetId=MEX_PRIMARY_SOURCE_STABLE_TARGET_ID,
)
return Identity(
hadPrimarySource=had_primary_source,
identifier=Identifier.generate(),
Expand Down
2 changes: 1 addition & 1 deletion mex/backend/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ def close_connectors() -> None:


@asynccontextmanager
async def lifespan(app: FastAPI) -> AsyncIterator[None]:
async def lifespan(_: FastAPI) -> AsyncIterator[None]:
"""Async context manager to execute setup and teardown of the FastAPI app."""
yield None
close_connectors()
Expand Down
15 changes: 12 additions & 3 deletions mex/backend/merged/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@
from fastapi import APIRouter, Query

from mex.backend.graph.connector import GraphConnector
from mex.backend.merged.models import MergedItemSearchResponse, MergedType
from mex.backend.merged.models import (
MergedItemSearchResponse,
MergedType,
UnprefixedType,
)
from mex.backend.merged.transform import (
transform_graph_results_to_merged_item_search_response_facade,
)
Expand All @@ -16,7 +20,7 @@
def search_merged_items_facade(
q: str = Query("", max_length=1000),
stableTargetId: Identifier | None = Query(None), # noqa: N803
entityType: Sequence[MergedType] = Query([]), # noqa: N803
entityType: Sequence[MergedType | UnprefixedType] = Query([]), # noqa: N803
skip: int = Query(0, ge=0, le=10e10),
limit: int = Query(10, ge=1, le=100),
) -> MergedItemSearchResponse:
Expand All @@ -27,7 +31,12 @@ def search_merged_items_facade(
query_results = graph.query_nodes(
q,
stableTargetId,
[t.value.replace("Merged", "Extracted") for t in entityType or MergedType],
[
# Allow 'MergedPerson' as well as 'Person' as an entityType for this
# endpoint to keep compatibility with previous API clients.
f"Extracted{t.value.removeprefix('Merged')}"
for t in entityType or MergedType
],
skip,
limit,
)
Expand Down
20 changes: 8 additions & 12 deletions mex/backend/merged/models.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,24 @@
from enum import Enum, EnumMeta, _EnumDict
from enum import Enum
from typing import TYPE_CHECKING, Union

from pydantic import Field

from mex.backend.types import DynamicStrEnum
from mex.common.models import MERGED_MODEL_CLASSES_BY_NAME, MergedItem
from mex.common.models.base import BaseModel
from mex.common.transform import dromedary_to_snake


class MergedTypeMeta(EnumMeta):
"""Meta class to dynamically populate the entity type enumeration."""
class UnprefixedType(Enum, metaclass=DynamicStrEnum):
"""Enumeration of possible unprefixed types for merged items."""

def __new__(
cls: type["MergedTypeMeta"], name: str, bases: tuple[type], dct: _EnumDict
) -> "MergedTypeMeta":
"""Create a new entity type enum by adding an entry for each model."""
for entity_type in MERGED_MODEL_CLASSES_BY_NAME:
dct[dromedary_to_snake(entity_type).upper()] = entity_type
return super().__new__(cls, name, bases, dct)
__names__ = list(m.removeprefix("Merged") for m in MERGED_MODEL_CLASSES_BY_NAME)


class MergedType(Enum, metaclass=MergedTypeMeta):
class MergedType(Enum, metaclass=DynamicStrEnum):
"""Enumeration of possible types for merged items."""

__names__ = list(MERGED_MODEL_CLASSES_BY_NAME)


if TYPE_CHECKING: # pragma: no cover
AnyMergedModel = MergedItem
Expand Down
15 changes: 14 additions & 1 deletion mex/backend/types.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from enum import Enum
from enum import Enum, EnumMeta, _EnumDict

from pydantic import SecretStr

from mex.common.models import BaseModel
from mex.common.transform import dromedary_to_snake


class AccessLevel(Enum):
Expand Down Expand Up @@ -42,3 +43,15 @@ class BackendIdentityProvider(Enum):
"""Identity providers implemented by mex-backend."""

GRAPH = "graph"


class DynamicStrEnum(EnumMeta):
"""Meta class to dynamically populate the an enumeration from a list of strings."""

def __new__(
cls: type["DynamicStrEnum"], name: str, bases: tuple[type], dct: _EnumDict
) -> "DynamicStrEnum":
"""Create a new enum by adding an entry for each name in the source."""
for name in dct.pop("__names__"):
dct[dromedary_to_snake(name).upper()] = name
return super().__new__(cls, name, bases, dct)
12 changes: 6 additions & 6 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ packages = [{ include = "mex" }]
python = "^3.11"
fastapi = "^0.108.0"
httpx = "^0.26.0"
mex-common = { git = "https://github.com/robert-koch-institut/mex-common.git", rev = "0.19.0" }
mex-common = { git = "https://github.com/robert-koch-institut/mex-common.git", rev = "feature/mx-1533-prep-graph-id-provider" }
neo4j = "^5.16.0"
pydantic = "^2.5.3"
uvicorn = { version = "^0.25.0", extras = ["standard"] }
Expand Down
3 changes: 2 additions & 1 deletion tests/identity/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from fastapi.testclient import TestClient

from mex.common.models import (
MEX_PRIMARY_SOURCE_IDENTIFIER,
MEX_PRIMARY_SOURCE_IDENTIFIER_IN_PRIMARY_SOURCE,
MEX_PRIMARY_SOURCE_STABLE_TARGET_ID,
)
Expand Down Expand Up @@ -142,7 +143,7 @@ def test_fetch_identity_invalid_query_params_mocked(
},
{
"hadPrimarySource": MEX_PRIMARY_SOURCE_STABLE_TARGET_ID,
"identifier": MEX_PRIMARY_SOURCE_STABLE_TARGET_ID,
"identifier": MEX_PRIMARY_SOURCE_IDENTIFIER,
"identifierInPrimarySource": MEX_PRIMARY_SOURCE_IDENTIFIER_IN_PRIMARY_SOURCE,
"stableTargetId": MEX_PRIMARY_SOURCE_STABLE_TARGET_ID,
},
Expand Down
Loading

0 comments on commit 5af07fa

Please sign in to comment.