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: Subscription discount now supports null starts_at #82

Merged
merged 1 commit into from
Dec 17, 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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),

Check our main [developer changelog](https://developer.paddle.com/?utm_source=dx&utm_medium=paddle-python-sdk) for information about changes to the Paddle Billing platform, the Paddle API, and other developer tools.

## 1.2.2 - 2024-12-17

### Fixed

- Subscription discount now supports null `starts_at`

## 1.2.1 - 2024-12-04

### Fixed
Expand Down
2 changes: 1 addition & 1 deletion paddle_billing/Client.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ def build_request_session(self) -> Session:
"Authorization": f"Bearer {self.__api_key}",
"Content-Type": "application/json",
"Paddle-Version": str(self.use_api_version),
"User-Agent": "PaddleSDK/python 1.2.1",
"User-Agent": "PaddleSDK/python 1.2.2",
}
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@
@dataclass
class SubscriptionDiscount:
id: str
starts_at: datetime
ends_at: datetime
starts_at: datetime | None
ends_at: datetime | None

@staticmethod
def from_dict(data: dict) -> SubscriptionDiscount:
return SubscriptionDiscount(
id=data["id"],
starts_at=datetime.fromisoformat(data["starts_at"]),
starts_at=datetime.fromisoformat(data["starts_at"]) if data.get("starts_at") else None,
ends_at=datetime.fromisoformat(data["ends_at"]) if data.get("ends_at") else None,
)
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@
@dataclass
class SubscriptionDiscount:
id: str
starts_at: datetime
ends_at: datetime
starts_at: datetime | None
ends_at: datetime | None

@staticmethod
def from_dict(data: dict) -> SubscriptionDiscount:
return SubscriptionDiscount(
id=data["id"],
starts_at=datetime.fromisoformat(data["starts_at"]),
starts_at=datetime.fromisoformat(data["starts_at"]) if data.get("starts_at") else None,
ends_at=datetime.fromisoformat(data["ends_at"]) if data.get("ends_at") else None,
)
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@


setup(
version="1.2.1",
version="1.2.2",
author="Paddle and contributors",
author_email="[email protected]",
description="Paddle's Python SDK for Paddle Billing",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,11 @@
"update_payment_method": null,
"cancel": "https://sandbox-buyer-portal.paddle.com/subscriptions/sub_01hp463gxfvndqjjyqn2n7tkth/cancel"
},
"discount": null,
"discount": {
"id": "dsc_01hv6scyf7qdnzcdq01t2y8dx4",
"starts_at": null,
"ends_at": null
},
"import_meta": null
},
{
Expand Down Expand Up @@ -467,7 +471,11 @@
"update_payment_method": "https://buyer-portal.paddle.com/subscriptions/sub_01hn0epy6nc46wt9hw92pp2kmt/update-payment-method",
"cancel": "https://buyer-portal.paddle.com/subscriptions/sub_01hn0epy6nc46wt9hw92pp2kmt/cancel"
},
"discount": null,
"discount": {
"id": "dsc_01hv6scyf7qdnzcdq01t2y8dx4",
"starts_at": "2024-04-12T10:18:47.635628Z",
"ends_at": "2024-05-12T10:18:47.635628Z"
},
"import_meta": null
}
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,27 @@ def test_list_subscriptions_returns_expected_response(
str(expected_response_body)
), "The response JSON generated by ResponseParser() doesn't match the expected fixture JSON"

def test_list_subscriptions_supports_null_discount_starts_at(
self,
test_client,
mock_requests,
):
mock_requests.get(
f"{test_client.base_url}/subscriptions",
status_code=200,
text=ReadsFixtures.read_raw_json_fixture("response/list_default"),
)

response = test_client.client.subscriptions.list(ListSubscriptions())

assert isinstance(response, SubscriptionCollection)
assert response.items[0].discount is None
assert response.items[1].discount is None
assert response.items[2].discount.starts_at is None
assert response.items[2].discount.ends_at is None
assert response.items[3].discount.starts_at.isoformat() == "2024-04-12T10:18:47.635628+00:00"
assert response.items[3].discount.ends_at.isoformat() == "2024-05-12T10:18:47.635628+00:00"

@mark.parametrize(
"subscription_id, includes, expected_response_status, expected_response_body, expected_url",
[
Expand Down
44 changes: 44 additions & 0 deletions tests/Unit/Entities/Notifications/test_NotificationEvent.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,3 +209,47 @@ def test_unknown_event_type_is_handled(self):
assert notification_event.occurred_at.isoformat() == "2023-08-21T11:57:47.390028+00:00"
assert isinstance(notification_event.data, UndefinedEntity)
assert notification_event.data.to_dict()["id"] == "add_01hv8gq3318ktkfengj2r75gfx"

def test_subscription_notification_event_with_null_discount(self):
notification_event = NotificationEvent.from_dict(
{
"data": loads(ReadsFixtures.read_raw_json_fixture("notification/entity/subscription.trialing")),
"notification_id": "ntf_01h8bkrfe7w1vwf8xmytwn51e7",
"event_type": "subscription.trialing",
"event_id": "evt_01h8bzakzx3hm2fmen703n5q45",
"occurred_at": "2023-08-21T11:57:47.390028Z",
}
)

assert isinstance(notification_event.data, Subscription)
assert notification_event.data.discount is None

def test_subscription_notification_event_discount_without_starts_and_ends_at(self):
notification_event = NotificationEvent.from_dict(
{
"data": loads(ReadsFixtures.read_raw_json_fixture("notification/entity/subscription.paused")),
"notification_id": "ntf_01h8bkrfe7w1vwf8xmytwn51e7",
"event_type": "subscription.paused",
"event_id": "evt_01h8bzakzx3hm2fmen703n5q45",
"occurred_at": "2023-08-21T11:57:47.390028Z",
}
)

assert isinstance(notification_event.data, Subscription)
assert notification_event.data.discount.starts_at is None
assert notification_event.data.discount.ends_at is None

def test_subscription_notification_event_with_discount(self):
notification_event = NotificationEvent.from_dict(
{
"data": loads(ReadsFixtures.read_raw_json_fixture("notification/entity/subscription.created")),
"notification_id": "ntf_01h8bkrfe7w1vwf8xmytwn51e7",
"event_type": "subscription.created",
"event_id": "evt_01h8bzakzx3hm2fmen703n5q45",
"occurred_at": "2023-08-21T11:57:47.390028Z",
}
)

assert isinstance(notification_event.data, SubscriptionCreated)
assert notification_event.data.discount.starts_at.isoformat() == "2024-04-12T10:18:47.635628+00:00"
assert notification_event.data.discount.ends_at.isoformat() == "2024-05-12T10:18:47.635628+00:00"
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,11 @@
}
],
"status": "active",
"discount": null,
"discount": {
"id": "dsc_01hv6scyf7qdnzcdq01t2y8dx4",
"starts_at": "2024-04-12T10:18:47.635628Z",
"ends_at": "2024-05-12T10:18:47.635628Z"
},
"paused_at": null,
"address_id": "add_01hv8gq3318ktkfengj2r75gfx",
"created_at": "2024-04-12T10:18:48.831Z",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,11 @@
}
],
"status": "paused",
"discount": null,
"discount": {
"id": "dsc_01hv6scyf7qdnzcdq01t2y8dx4",
"starts_at": null,
"ends_at": null
},
"paused_at": "2024-04-12T12:43:43.214Z",
"address_id": "add_01hv8gq3318ktkfengj2r75gfx",
"created_at": "2024-04-12T12:42:27.89Z",
Expand Down
Loading