Skip to content

Commit

Permalink
fix: Return UndefinedEntity for unknown notification entity types (#55)
Browse files Browse the repository at this point in the history
  • Loading branch information
davidgrayston-paddle authored Oct 7, 2024
1 parent e9fdded commit 40a5008
Show file tree
Hide file tree
Showing 8 changed files with 48 additions and 10 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`

Expand Down
3 changes: 2 additions & 1 deletion paddle_billing/Entities/Event.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@
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
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:
Expand Down
3 changes: 2 additions & 1 deletion paddle_billing/Entities/Notifications/NotificationEvent.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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:
Expand Down
13 changes: 10 additions & 3 deletions paddle_billing/Notifications/Entities/Entity.py
Original file line number Diff line number Diff line change
@@ -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):
Expand All @@ -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
Expand All @@ -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")
Expand All @@ -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)
6 changes: 6 additions & 0 deletions paddle_billing/Notifications/Entities/EntityDict.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from __future__ import annotations
from typing import Protocol


class EntityDict(Protocol):
def to_dict(self) -> dict: ...
13 changes: 13 additions & 0 deletions paddle_billing/Notifications/Entities/UndefinedEntity.py
Original file line number Diff line number Diff line change
@@ -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
9 changes: 7 additions & 2 deletions tests/Unit/Entities/Notifications/test_NotificationEvent.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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(
Expand All @@ -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",
Expand Down Expand Up @@ -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"
9 changes: 7 additions & 2 deletions tests/Unit/Entities/test_Event.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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(
Expand All @@ -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",
Expand Down Expand Up @@ -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"

0 comments on commit 40a5008

Please sign in to comment.