Skip to content

Commit

Permalink
enhance registry with lookup by model instance
Browse files Browse the repository at this point in the history
  • Loading branch information
jensens committed Dec 12, 2024
1 parent 1ac4361 commit 4276b9a
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 34 deletions.
6 changes: 3 additions & 3 deletions src/edutap/wallet_google/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from .models.datatypes.message import Message
from .models.misc import AddMessageRequest
from .models.misc import ObjectWithClassReference
from .registry import lookup_metadata
from .registry import lookup_metadata_by_name
from .registry import lookup_model
from .registry import lookup_model_by_plural_name
from .registry import raise_when_operation_not_allowed
Expand Down Expand Up @@ -149,7 +149,7 @@ def update(
:return: The created model based on the data returned by the Restful API
"""
raise_when_operation_not_allowed(name, "update")
model_metadata = lookup_metadata(name)
model_metadata = lookup_metadata_by_name(name)
model = model_metadata["model"]
if not isinstance(data, Model) and partial:
resource_id = data[model_metadata["resource_id"]]
Expand Down Expand Up @@ -197,7 +197,7 @@ def message(
:return: The created Model object as returned by the Restful API
"""
raise_when_operation_not_allowed(name, "message")
model_metadata = lookup_metadata(name)
model_metadata = lookup_metadata_by_name(name)
model = model_metadata["model"]
if not isinstance(message, Message):
message_validated = Message.model_validate(message)
Expand Down
31 changes: 19 additions & 12 deletions src/edutap/wallet_google/registry.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
from .models.bases import Model
from .models.passes.bases import ClassModel
from .models.passes.bases import ObjectModel
from typing import TypedDict


Expand All @@ -20,7 +18,8 @@ class RegistryMetadataDict(TypedDict, total=False):
can_message: bool


_MODEL_REGISTRY: dict[str, RegistryMetadataDict] = {}
_MODEL_REGISTRY_BY_NAME: dict[str, RegistryMetadataDict] = {}
_MODEL_REGISTRY_BY_MODEL: dict[type[Model], RegistryMetadataDict] = {}


class register_model:
Expand Down Expand Up @@ -78,47 +77,55 @@ def __init__(

def __call__(
self,
cls: type[Model | ClassModel | ObjectModel],
) -> type[Model | ClassModel | ObjectModel]:
cls: type[Model],
) -> type[Model]:
"""
Registers the given class in the registry.
"""
name = self.metadata["name"]
if name in _MODEL_REGISTRY:
if name in _MODEL_REGISTRY_BY_NAME:
raise ValueError(f"Duplicate registration of '{name}'")
self.metadata["model"] = cls
_MODEL_REGISTRY[name] = self.metadata
_MODEL_REGISTRY_BY_NAME[name] = self.metadata
_MODEL_REGISTRY_BY_MODEL[cls] = self.metadata
return cls


def lookup_model(name: str) -> type[Model]:
"""
Returns the model with the given name.
"""
return _MODEL_REGISTRY[name]["model"]
return _MODEL_REGISTRY_BY_NAME[name]["model"]


def lookup_model_by_plural_name(plural_name: str) -> type[Model]:
"""
Returns the model with the given plural name.
"""
for model in _MODEL_REGISTRY.values():
for model in _MODEL_REGISTRY_BY_NAME.values():
if model["plural"] == plural_name:
return model["model"]
raise LookupError(f"Model with plural name '{plural_name}' not found")


def lookup_metadata(name: str) -> RegistryMetadataDict:
def lookup_metadata_by_name(name: str) -> RegistryMetadataDict:
"""
Returns the metadata of the model with the given name.
"""
return _MODEL_REGISTRY[name]
return _MODEL_REGISTRY_BY_NAME[name]


def lookup_metadata_by_model_instance(model: Model) -> RegistryMetadataDict:
"""
Returns the registry metadata by a given instacne of a model
"""
return _MODEL_REGISTRY_BY_MODEL[type(model)]


def raise_when_operation_not_allowed(name: str, operation: str) -> None:
"""Verifies that the given operation is allowed for the given registered name.
:raises: ValueError when the operation is not allowed.
"""
if not _MODEL_REGISTRY[name][f"can_{operation}"]: # type: ignore
if not _MODEL_REGISTRY_BY_NAME[name][f"can_{operation}"]: # type: ignore
raise ValueError(f"Operation '{operation}' not allowed for '{name}'")
4 changes: 2 additions & 2 deletions src/edutap/wallet_google/session.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from .registry import lookup_metadata
from .registry import lookup_metadata_by_name
from .settings import Settings
from google.auth.transport.requests import AuthorizedSession
from google.oauth2.service_account import Credentials
Expand Down Expand Up @@ -87,7 +87,7 @@ def url(self, name: str, additional_path: str = "") -> str:
:return: the url of the google RESTful API endpoint to handle this model
"""
model_metadata = lookup_metadata(name)
model_metadata = lookup_metadata_by_name(name)
return f"{self.settings.base_url}/{model_metadata['url_part']}{additional_path}"


Expand Down
26 changes: 19 additions & 7 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,27 @@


@pytest.fixture
def clean_registry():
def clean_registry_by_name():
"""Fixture to provide a clean the google_wallet model registry."""
from edutap.wallet_google.registry import _MODEL_REGISTRY
from edutap.wallet_google.registry import _MODEL_REGISTRY_BY_NAME

OLD_MODEL_REGISTRY = copy.deepcopy(_MODEL_REGISTRY)
_MODEL_REGISTRY.clear()
yield _MODEL_REGISTRY
_MODEL_REGISTRY.clear()
_MODEL_REGISTRY.update(OLD_MODEL_REGISTRY)
OLD_MODEL_REGISTRY_BY_NAME = copy.deepcopy(_MODEL_REGISTRY_BY_NAME)
_MODEL_REGISTRY_BY_NAME.clear()
yield _MODEL_REGISTRY_BY_NAME
_MODEL_REGISTRY_BY_NAME.clear()
_MODEL_REGISTRY_BY_NAME.update(OLD_MODEL_REGISTRY_BY_NAME)


@pytest.fixture
def clean_registry_by_model():
"""Fixture to provide a clean the google_wallet model registry."""
from edutap.wallet_google.registry import _MODEL_REGISTRY_BY_MODEL

OLD_MODEL_REGISTRY_BY_MODEL = copy.deepcopy(_MODEL_REGISTRY_BY_MODEL)
_MODEL_REGISTRY_BY_MODEL.clear()
yield _MODEL_REGISTRY_BY_MODEL
_MODEL_REGISTRY_BY_MODEL.clear()
_MODEL_REGISTRY_BY_MODEL.update(OLD_MODEL_REGISTRY_BY_MODEL)


@pytest.fixture
Expand Down
60 changes: 51 additions & 9 deletions tests/test_registry.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import pytest


def test_decorator(clean_registry):
def test_decorator(clean_registry_by_name, clean_registry_by_model):
from edutap.wallet_google.registry import register_model

@register_model("Foo", url_part="foo")
class Foo:
pass

assert clean_registry["Foo"] == {
expected = {
"can_create": True,
"can_disable": True,
"can_list": True,
Expand All @@ -21,6 +21,8 @@ class Foo:
"resource_id": "id",
"url_part": "foo",
}
assert clean_registry_by_name["Foo"] == expected
assert clean_registry_by_model[Foo] == expected

with pytest.raises(ValueError):

Expand All @@ -42,7 +44,7 @@ class AnotherFoo:
class Bar:
pass

assert clean_registry["Bar"] == {
expected = {
"can_create": False,
"can_disable": False,
"can_list": False,
Expand All @@ -55,9 +57,11 @@ class Bar:
"resource_id": "id",
"url_part": "baar",
}
assert clean_registry_by_name["Bar"] == expected
assert clean_registry_by_model[Bar] == expected


def test_lookup_model(clean_registry):
def test_lookup_model(clean_registry_by_name, clean_registry_by_model):
from edutap.wallet_google.registry import register_model

@register_model("Foo", url_part="foo")
Expand All @@ -72,16 +76,16 @@ class Foo:
lookup_model("Bar")


def test_lookup_metadata(clean_registry):
def test_lookup_metadata_by_name(clean_registry_by_name, clean_registry_by_model):
from edutap.wallet_google.registry import register_model

@register_model("Foo", url_part="foo")
class Foo:
pass

from edutap.wallet_google.registry import lookup_metadata
from edutap.wallet_google.registry import lookup_metadata_by_name

assert lookup_metadata("Foo") == {
assert lookup_metadata_by_name("Foo") == {
"plural": "foos",
"resource_id": "id",
"can_create": True,
Expand All @@ -96,10 +100,48 @@ class Foo:
}

with pytest.raises(KeyError):
lookup_metadata("Bar")
lookup_metadata_by_name("Bar")


def test_raise_when_operation_not_allowed(clean_registry):
def test_lookup_metadata_by_model_instance(
clean_registry_by_name, clean_registry_by_model
):
from edutap.wallet_google.registry import register_model

@register_model("Foo", url_part="foo")
class Foo:
pass

foo = Foo()

from edutap.wallet_google.registry import lookup_metadata_by_model_instance

assert lookup_metadata_by_model_instance(foo) == {
"plural": "foos",
"resource_id": "id",
"can_create": True,
"can_disable": True,
"can_list": True,
"can_message": True,
"can_read": True,
"can_update": True,
"model": Foo,
"name": "Foo",
"url_part": "foo",
}

class Bar:
pass

bar = Bar()

with pytest.raises(KeyError):
lookup_metadata_by_model_instance(bar)


def test_raise_when_operation_not_allowed(
clean_registry_by_name, clean_registry_by_model
):
from edutap.wallet_google.registry import register_model

@register_model("foo", url_part="foo", can_message=False)
Expand Down
4 changes: 3 additions & 1 deletion tests/test_session.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
from edutap.wallet_google.settings import ROOT_DIR


def test_session_manager_url(clean_registry): # noqa: F811
def test_session_manager_url(
clean_registry_by_name, clean_registry_by_model
): # noqa: F811
from edutap.wallet_google.registry import register_model

@register_model("Foo", url_part="foo")
Expand Down

0 comments on commit 4276b9a

Please sign in to comment.