diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d61b2a..b8ddb03 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,7 +32,7 @@ Check our main [developer changelog](https://developer.paddle.com/?utm_source=dx - Replaced `AdjustmentTimePeriod`, `SubscriptionTimePeriod` and `TransactionTimePeriod` with shared `TimePeriod` - Replaced `AdjustmentProration`, `SubscriptionProration` and `TransactionProration` with shared `Proration` - `paddle_billing.Entities.Event` `data` will now be `paddle_billing.Notifications.Entities.SubscriptionCreated` for `subscription.created` events -- `paddle_billing.Entities.Event` `data` will now be `dict` for unknown event types +- `paddle_billing.Entities.Event` `data` will now be `paddle_billing.Notifications.Entities.UndefinedEntity` for unknown event types - `paddle_billing.Resources.Reports.Operations.CreateReport` is replaced by report specific operations `CreateAdjustmentsReport` | `CreateDiscountsReport` | `CreateProductsAndPricesReport` | `CreateTransactionsReport` - `paddle_billing.Entities.Notification` `payload` is now `paddle_billing.Entities.Notifications.NotificationEvent` diff --git a/paddle_billing/Entities/Event.py b/paddle_billing/Entities/Event.py index 0ff7676..8583747 100644 --- a/paddle_billing/Entities/Event.py +++ b/paddle_billing/Entities/Event.py @@ -7,6 +7,7 @@ from paddle_billing.Entities.Events import EventTypeName from paddle_billing.Notifications.Entities.Entity import Entity as NotificationEntity +from paddle_billing.Notifications.Entities.UndefinedEntity import UndefinedEntity @dataclass @@ -14,7 +15,7 @@ class Event(Entity, ABC): event_id: str event_type: EventTypeName occurred_at: datetime - data: NotificationEntity | dict + data: NotificationEntity | UndefinedEntity @staticmethod def from_dict(data: dict) -> Event: diff --git a/paddle_billing/Entities/Notifications/NotificationEvent.py b/paddle_billing/Entities/Notifications/NotificationEvent.py index acedc52..350390f 100644 --- a/paddle_billing/Entities/Notifications/NotificationEvent.py +++ b/paddle_billing/Entities/Notifications/NotificationEvent.py @@ -8,6 +8,7 @@ from paddle_billing.Entities.Events import EventTypeName from paddle_billing.Notifications.Entities.Entity import Entity as NotificationEntity +from paddle_billing.Notifications.Entities.UndefinedEntity import UndefinedEntity from paddle_billing.Notifications.Requests import Request @@ -18,7 +19,7 @@ class NotificationEvent(Entity, ABC): event_id: str event_type: EventTypeName occurred_at: datetime - data: NotificationEntity | dict + data: NotificationEntity | UndefinedEntity @staticmethod def from_dict(data: dict) -> NotificationEvent: diff --git a/paddle_billing/Notifications/Entities/Entity.py b/paddle_billing/Notifications/Entities/Entity.py index ea27660..c061bfb 100644 --- a/paddle_billing/Notifications/Entities/Entity.py +++ b/paddle_billing/Notifications/Entities/Entity.py @@ -1,9 +1,13 @@ from __future__ import annotations from abc import ABC, abstractmethod +from dataclasses import asdict, dataclass from importlib import import_module +from paddle_billing.Notifications.Entities.EntityDict import EntityDict +from paddle_billing.Notifications.Entities.UndefinedEntity import UndefinedEntity -class Entity(ABC): +@dataclass +class Entity(ABC, EntityDict): @staticmethod @abstractmethod def from_dict(data: dict): @@ -13,7 +17,7 @@ def from_dict(data: dict): pass @staticmethod - def from_dict_for_event_type(data: dict, event_type: str) -> Entity | dict: + def from_dict_for_event_type(data: dict, event_type: str) -> Entity | UndefinedEntity: entity_class_name = Entity._resolve_event_class_name(event_type) entity_class = None @@ -29,7 +33,7 @@ def from_dict_for_event_type(data: dict, event_type: str) -> Entity | dict: print(f"Error dynamically instantiating a '{entity_module_path}.{entity_class_name}' object: {error}") if not instantiated_class: - return data + return UndefinedEntity(data) if not issubclass(entity_class, Entity): raise ValueError(f"Event type '{entity_class_name}' is not of NotificationEntity") @@ -44,3 +48,6 @@ def _resolve_event_class_name(event_type) -> str: event_entity = event_type.split(".")[0] or "" return event_entity.lower().title() + + def to_dict(self) -> dict: + return asdict(self) diff --git a/paddle_billing/Notifications/Entities/EntityDict.py b/paddle_billing/Notifications/Entities/EntityDict.py new file mode 100644 index 0000000..c5dd21c --- /dev/null +++ b/paddle_billing/Notifications/Entities/EntityDict.py @@ -0,0 +1,6 @@ +from __future__ import annotations +from typing import Protocol + + +class EntityDict(Protocol): + def to_dict(self) -> dict: ... diff --git a/paddle_billing/Notifications/Entities/UndefinedEntity.py b/paddle_billing/Notifications/Entities/UndefinedEntity.py new file mode 100644 index 0000000..af2d319 --- /dev/null +++ b/paddle_billing/Notifications/Entities/UndefinedEntity.py @@ -0,0 +1,13 @@ +from __future__ import annotations +from paddle_billing.Notifications.Entities.EntityDict import EntityDict + + +class UndefinedEntity(EntityDict): + def __init__( + self, + data: dict, + ): + self._data = data + + def to_dict(self) -> dict: + return self._data diff --git a/tests/Unit/Entities/Notifications/test_NotificationEvent.py b/tests/Unit/Entities/Notifications/test_NotificationEvent.py index a1709b0..b3a2695 100644 --- a/tests/Unit/Entities/Notifications/test_NotificationEvent.py +++ b/tests/Unit/Entities/Notifications/test_NotificationEvent.py @@ -6,6 +6,7 @@ from paddle_billing.Notifications.Entities.Subscription import Subscription from paddle_billing.Notifications.Entities.SubscriptionCreated import SubscriptionCreated +from paddle_billing.Notifications.Entities.UndefinedEntity import UndefinedEntity from tests.Utils.ReadsFixture import ReadsFixtures @@ -127,6 +128,8 @@ def test_notification_event_from_dict( assert notification_event.event_id == "evt_01h8bzakzx3hm2fmen703n5q45" assert notification_event.event_type == event_type assert notification_event.occurred_at.isoformat() == "2023-08-21T11:57:47.390028+00:00" + assert isinstance(notification_event.data.to_dict(), dict) + assert notification_event.data.to_dict()["id"] is not None def test_subscription_created_notification_event_transaction_id(self): notification_event = NotificationEvent.from_dict( @@ -141,6 +144,8 @@ def test_subscription_created_notification_event_transaction_id(self): assert isinstance(notification_event.data, SubscriptionCreated) assert notification_event.data.transaction_id == "txn_01hv8wptq8987qeep44cyrewp9" + assert isinstance(notification_event.data.to_dict(), dict) + assert notification_event.data.to_dict()["transaction_id"] == "txn_01hv8wptq8987qeep44cyrewp9" @mark.parametrize( "event_type", @@ -198,5 +203,5 @@ def test_unknown_event_type_is_handled(self): assert notification_event.event_id == "evt_01h8bzakzx3hm2fmen703n5q45" assert notification_event.event_type == "some_unknown_entity.created" assert notification_event.occurred_at.isoformat() == "2023-08-21T11:57:47.390028+00:00" - assert isinstance(notification_event.data, dict) - assert notification_event.data["id"] == "add_01hv8gq3318ktkfengj2r75gfx" + assert isinstance(notification_event.data, UndefinedEntity) + assert notification_event.data.to_dict()["id"] == "add_01hv8gq3318ktkfengj2r75gfx" diff --git a/tests/Unit/Entities/test_Event.py b/tests/Unit/Entities/test_Event.py index 0c13067..d78a6cd 100644 --- a/tests/Unit/Entities/test_Event.py +++ b/tests/Unit/Entities/test_Event.py @@ -6,6 +6,7 @@ from paddle_billing.Notifications.Entities.Subscription import Subscription from paddle_billing.Notifications.Entities.SubscriptionCreated import SubscriptionCreated +from paddle_billing.Notifications.Entities.UndefinedEntity import UndefinedEntity from tests.Utils.ReadsFixture import ReadsFixtures @@ -125,6 +126,8 @@ def test_event_from_dict( assert event.event_id == "evt_01h8bzakzx3hm2fmen703n5q45" assert event.event_type == event_type assert event.occurred_at.isoformat() == "2023-08-21T11:57:47.390028+00:00" + assert isinstance(event.data.to_dict(), dict) + assert event.data.to_dict()["id"] is not None def test_subscription_created_event_transaction_id(self): event = Event.from_dict( @@ -138,6 +141,8 @@ def test_subscription_created_event_transaction_id(self): assert isinstance(event.data, SubscriptionCreated) assert event.data.transaction_id == "txn_01hv8wptq8987qeep44cyrewp9" + assert isinstance(event.data.to_dict(), dict) + assert event.data.to_dict()["transaction_id"] == "txn_01hv8wptq8987qeep44cyrewp9" @mark.parametrize( "event_type", @@ -192,5 +197,5 @@ def test_unknown_event_type_is_handled(self): assert event.event_id == "evt_01h8bzakzx3hm2fmen703n5q45" assert event.event_type == "some_unknown_entity.created" assert event.occurred_at.isoformat() == "2023-08-21T11:57:47.390028+00:00" - assert isinstance(event.data, dict) - assert event.data["id"] == "add_01hv8gq3318ktkfengj2r75gfx" + assert isinstance(event.data, UndefinedEntity) + assert event.data.to_dict()["id"] == "add_01hv8gq3318ktkfengj2r75gfx"