Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Return UndefinedEntity for unknown notification entity types #55

Merged
merged 1 commit into from
Oct 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
mikeymike marked this conversation as resolved.
Show resolved Hide resolved

@staticmethod
def from_dict(data: dict) -> Event:
Expand Down
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"