Add NotificationClient methods for config and subscription management with tests
This commit is contained in:
parent
8f31f433d2
commit
1a9f924764
3 changed files with 243 additions and 0 deletions
|
|
@ -69,6 +69,8 @@ class ApiTransport:
|
||||||
response = self.request(method, path, **kwargs)
|
response = self.request(method, path, **kwargs)
|
||||||
if response.status_code == 204:
|
if response.status_code == 204:
|
||||||
return None
|
return None
|
||||||
|
if not response.content or not response.content.strip():
|
||||||
|
return None
|
||||||
try:
|
try:
|
||||||
return response.json()
|
return response.json()
|
||||||
except ValueError as exc:
|
except ValueError as exc:
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,16 @@ from __future__ import annotations
|
||||||
|
|
||||||
from ebay_client.core.http.transport import ApiTransport
|
from ebay_client.core.http.transport import ApiTransport
|
||||||
from ebay_client.generated.notification.models import (
|
from ebay_client.generated.notification.models import (
|
||||||
|
Config,
|
||||||
|
CreateSubscriptionFilterRequest,
|
||||||
CreateSubscriptionRequest,
|
CreateSubscriptionRequest,
|
||||||
|
Destination,
|
||||||
DestinationRequest,
|
DestinationRequest,
|
||||||
DestinationSearchResponse,
|
DestinationSearchResponse,
|
||||||
PublicKey,
|
PublicKey,
|
||||||
|
Subscription,
|
||||||
|
SubscriptionFilter,
|
||||||
|
UpdateSubscriptionRequest,
|
||||||
SubscriptionSearchResponse,
|
SubscriptionSearchResponse,
|
||||||
Topic,
|
Topic,
|
||||||
TopicSearchResponse,
|
TopicSearchResponse,
|
||||||
|
|
@ -20,6 +26,23 @@ class NotificationClient:
|
||||||
def __init__(self, transport: ApiTransport) -> None:
|
def __init__(self, transport: ApiTransport) -> None:
|
||||||
self.transport = transport
|
self.transport = transport
|
||||||
|
|
||||||
|
def get_config(self) -> Config:
|
||||||
|
return self.transport.request_model(
|
||||||
|
Config,
|
||||||
|
"GET",
|
||||||
|
"/commerce/notification/v1/config",
|
||||||
|
scopes=[NOTIFICATION_SCOPE],
|
||||||
|
)
|
||||||
|
|
||||||
|
def update_config(self, payload: Config) -> None:
|
||||||
|
self.transport.request_json(
|
||||||
|
"PUT",
|
||||||
|
"/commerce/notification/v1/config",
|
||||||
|
scopes=[NOTIFICATION_SCOPE],
|
||||||
|
headers={"Content-Type": "application/json"},
|
||||||
|
json_body=payload.model_dump(by_alias=True, exclude_none=True),
|
||||||
|
)
|
||||||
|
|
||||||
def get_topics(self, *, limit: int | None = None, continuation_token: str | None = None) -> TopicSearchResponse:
|
def get_topics(self, *, limit: int | None = None, continuation_token: str | None = None) -> TopicSearchResponse:
|
||||||
return self.transport.request_model(
|
return self.transport.request_model(
|
||||||
TopicSearchResponse,
|
TopicSearchResponse,
|
||||||
|
|
@ -55,6 +78,30 @@ class NotificationClient:
|
||||||
json_body=payload.model_dump(by_alias=True, exclude_none=True),
|
json_body=payload.model_dump(by_alias=True, exclude_none=True),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def get_destination(self, destination_id: str) -> Destination:
|
||||||
|
return self.transport.request_model(
|
||||||
|
Destination,
|
||||||
|
"GET",
|
||||||
|
f"/commerce/notification/v1/destination/{destination_id}",
|
||||||
|
scopes=[NOTIFICATION_SCOPE],
|
||||||
|
)
|
||||||
|
|
||||||
|
def update_destination(self, destination_id: str, payload: DestinationRequest) -> None:
|
||||||
|
self.transport.request_json(
|
||||||
|
"PUT",
|
||||||
|
f"/commerce/notification/v1/destination/{destination_id}",
|
||||||
|
scopes=[NOTIFICATION_SCOPE],
|
||||||
|
headers={"Content-Type": "application/json"},
|
||||||
|
json_body=payload.model_dump(by_alias=True, exclude_none=True),
|
||||||
|
)
|
||||||
|
|
||||||
|
def delete_destination(self, destination_id: str) -> None:
|
||||||
|
self.transport.request_json(
|
||||||
|
"DELETE",
|
||||||
|
f"/commerce/notification/v1/destination/{destination_id}",
|
||||||
|
scopes=[NOTIFICATION_SCOPE],
|
||||||
|
)
|
||||||
|
|
||||||
def get_subscriptions(self, *, limit: int | None = None, continuation_token: str | None = None) -> SubscriptionSearchResponse:
|
def get_subscriptions(self, *, limit: int | None = None, continuation_token: str | None = None) -> SubscriptionSearchResponse:
|
||||||
return self.transport.request_model(
|
return self.transport.request_model(
|
||||||
SubscriptionSearchResponse,
|
SubscriptionSearchResponse,
|
||||||
|
|
@ -73,6 +120,72 @@ class NotificationClient:
|
||||||
json_body=payload.model_dump(by_alias=True, exclude_none=True),
|
json_body=payload.model_dump(by_alias=True, exclude_none=True),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def get_subscription(self, subscription_id: str) -> Subscription:
|
||||||
|
return self.transport.request_model(
|
||||||
|
Subscription,
|
||||||
|
"GET",
|
||||||
|
f"/commerce/notification/v1/subscription/{subscription_id}",
|
||||||
|
scopes=[NOTIFICATION_SCOPE, NOTIFICATION_SUBSCRIPTION_READ_SCOPE],
|
||||||
|
)
|
||||||
|
|
||||||
|
def update_subscription(self, subscription_id: str, payload: UpdateSubscriptionRequest) -> None:
|
||||||
|
self.transport.request_json(
|
||||||
|
"PUT",
|
||||||
|
f"/commerce/notification/v1/subscription/{subscription_id}",
|
||||||
|
scopes=[NOTIFICATION_SCOPE, NOTIFICATION_SUBSCRIPTION_SCOPE],
|
||||||
|
headers={"Content-Type": "application/json"},
|
||||||
|
json_body=payload.model_dump(by_alias=True, exclude_none=True),
|
||||||
|
)
|
||||||
|
|
||||||
|
def delete_subscription(self, subscription_id: str) -> None:
|
||||||
|
self.transport.request_json(
|
||||||
|
"DELETE",
|
||||||
|
f"/commerce/notification/v1/subscription/{subscription_id}",
|
||||||
|
scopes=[NOTIFICATION_SCOPE, NOTIFICATION_SUBSCRIPTION_SCOPE],
|
||||||
|
)
|
||||||
|
|
||||||
|
def create_subscription_filter(
|
||||||
|
self,
|
||||||
|
subscription_id: str,
|
||||||
|
payload: CreateSubscriptionFilterRequest,
|
||||||
|
) -> dict[str, object] | None:
|
||||||
|
return self.transport.request_json(
|
||||||
|
"POST",
|
||||||
|
f"/commerce/notification/v1/subscription/{subscription_id}/filter",
|
||||||
|
scopes=[NOTIFICATION_SCOPE, NOTIFICATION_SUBSCRIPTION_SCOPE],
|
||||||
|
headers={"Content-Type": "application/json"},
|
||||||
|
json_body=payload.model_dump(by_alias=True, exclude_none=True),
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_subscription_filter(self, subscription_id: str, filter_id: str) -> SubscriptionFilter:
|
||||||
|
return self.transport.request_model(
|
||||||
|
SubscriptionFilter,
|
||||||
|
"GET",
|
||||||
|
f"/commerce/notification/v1/subscription/{subscription_id}/filter/{filter_id}",
|
||||||
|
scopes=[NOTIFICATION_SCOPE, NOTIFICATION_SUBSCRIPTION_READ_SCOPE],
|
||||||
|
)
|
||||||
|
|
||||||
|
def delete_subscription_filter(self, subscription_id: str, filter_id: str) -> None:
|
||||||
|
self.transport.request_json(
|
||||||
|
"DELETE",
|
||||||
|
f"/commerce/notification/v1/subscription/{subscription_id}/filter/{filter_id}",
|
||||||
|
scopes=[NOTIFICATION_SCOPE, NOTIFICATION_SUBSCRIPTION_SCOPE],
|
||||||
|
)
|
||||||
|
|
||||||
|
def disable_subscription(self, subscription_id: str) -> None:
|
||||||
|
self.transport.request_json(
|
||||||
|
"POST",
|
||||||
|
f"/commerce/notification/v1/subscription/{subscription_id}/disable",
|
||||||
|
scopes=[NOTIFICATION_SCOPE, NOTIFICATION_SUBSCRIPTION_SCOPE],
|
||||||
|
)
|
||||||
|
|
||||||
|
def enable_subscription(self, subscription_id: str) -> None:
|
||||||
|
self.transport.request_json(
|
||||||
|
"POST",
|
||||||
|
f"/commerce/notification/v1/subscription/{subscription_id}/enable",
|
||||||
|
scopes=[NOTIFICATION_SCOPE, NOTIFICATION_SUBSCRIPTION_SCOPE],
|
||||||
|
)
|
||||||
|
|
||||||
def test_subscription(self, subscription_id: str) -> None:
|
def test_subscription(self, subscription_id: str) -> None:
|
||||||
self.transport.request_json(
|
self.transport.request_json(
|
||||||
"POST",
|
"POST",
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,16 @@ from ebay_client.generated.feed.models import TaskCollection
|
||||||
from ebay_client.generated.fulfillment.models import Order
|
from ebay_client.generated.fulfillment.models import Order
|
||||||
from ebay_client.generated.inventory.models import InventoryItemWithSkuLocaleGroupid
|
from ebay_client.generated.inventory.models import InventoryItemWithSkuLocaleGroupid
|
||||||
from ebay_client.generated.notification.models import (
|
from ebay_client.generated.notification.models import (
|
||||||
|
Config,
|
||||||
|
CreateSubscriptionFilterRequest,
|
||||||
|
CreateSubscriptionRequest,
|
||||||
DeliveryConfig,
|
DeliveryConfig,
|
||||||
|
Subscription,
|
||||||
|
SubscriptionFilter,
|
||||||
|
SubscriptionPayloadDetail,
|
||||||
DestinationRequest,
|
DestinationRequest,
|
||||||
TopicSearchResponse,
|
TopicSearchResponse,
|
||||||
|
UpdateSubscriptionRequest,
|
||||||
)
|
)
|
||||||
from ebay_client.inventory.client import InventoryClient
|
from ebay_client.inventory.client import InventoryClient
|
||||||
from ebay_client.notification.client import NotificationClient
|
from ebay_client.notification.client import NotificationClient
|
||||||
|
|
@ -74,6 +81,127 @@ def test_notification_wrapper_serializes_typed_request_model(httpx_mock: HTTPXMo
|
||||||
assert body["deliveryConfig"]["endpoint"] == "https://example.test/webhook"
|
assert body["deliveryConfig"]["endpoint"] == "https://example.test/webhook"
|
||||||
|
|
||||||
|
|
||||||
|
def test_notification_config_wrapper_round_trip(httpx_mock: HTTPXMock) -> None:
|
||||||
|
httpx_mock.add_response(
|
||||||
|
method="GET",
|
||||||
|
url="https://api.ebay.com/commerce/notification/v1/config",
|
||||||
|
json={"alertEmail": "alerts@example.test"},
|
||||||
|
)
|
||||||
|
httpx_mock.add_response(
|
||||||
|
method="PUT",
|
||||||
|
url="https://api.ebay.com/commerce/notification/v1/config",
|
||||||
|
status_code=204,
|
||||||
|
)
|
||||||
|
|
||||||
|
client = NotificationClient(build_transport())
|
||||||
|
config = client.get_config()
|
||||||
|
client.update_config(Config(alertEmail="ops@example.test"))
|
||||||
|
|
||||||
|
assert isinstance(config, Config)
|
||||||
|
assert config.alertEmail == "alerts@example.test"
|
||||||
|
request = httpx_mock.get_requests()[1]
|
||||||
|
body = json.loads(request.content.decode("utf-8"))
|
||||||
|
assert body["alertEmail"] == "ops@example.test"
|
||||||
|
|
||||||
|
|
||||||
|
def test_notification_subscription_wrapper_serializes_and_returns_models(httpx_mock: HTTPXMock) -> None:
|
||||||
|
httpx_mock.add_response(
|
||||||
|
method="POST",
|
||||||
|
url="https://api.ebay.com/commerce/notification/v1/subscription",
|
||||||
|
json={},
|
||||||
|
status_code=201,
|
||||||
|
)
|
||||||
|
httpx_mock.add_response(
|
||||||
|
method="GET",
|
||||||
|
url="https://api.ebay.com/commerce/notification/v1/subscription/SUB-1",
|
||||||
|
json={"subscriptionId": "SUB-1", "topicId": "TOPIC-1", "status": "ENABLED"},
|
||||||
|
)
|
||||||
|
httpx_mock.add_response(
|
||||||
|
method="PUT",
|
||||||
|
url="https://api.ebay.com/commerce/notification/v1/subscription/SUB-1",
|
||||||
|
status_code=204,
|
||||||
|
)
|
||||||
|
httpx_mock.add_response(
|
||||||
|
method="POST",
|
||||||
|
url="https://api.ebay.com/commerce/notification/v1/subscription/SUB-1/enable",
|
||||||
|
status_code=204,
|
||||||
|
)
|
||||||
|
httpx_mock.add_response(
|
||||||
|
method="POST",
|
||||||
|
url="https://api.ebay.com/commerce/notification/v1/subscription/SUB-1/test",
|
||||||
|
status_code=202,
|
||||||
|
)
|
||||||
|
|
||||||
|
client = NotificationClient(build_transport())
|
||||||
|
create_payload = CreateSubscriptionRequest(
|
||||||
|
destinationId="DEST-1",
|
||||||
|
topicId="TOPIC-1",
|
||||||
|
status="DISABLED",
|
||||||
|
payload=SubscriptionPayloadDetail(
|
||||||
|
deliveryProtocol="HTTPS",
|
||||||
|
format="JSON",
|
||||||
|
schemaVersion="1.0",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
update_payload = UpdateSubscriptionRequest(status="ENABLED", destinationId="DEST-1")
|
||||||
|
|
||||||
|
client.create_subscription(create_payload)
|
||||||
|
subscription = client.get_subscription("SUB-1")
|
||||||
|
client.update_subscription("SUB-1", update_payload)
|
||||||
|
client.enable_subscription("SUB-1")
|
||||||
|
client.test_subscription("SUB-1")
|
||||||
|
|
||||||
|
assert isinstance(subscription, Subscription)
|
||||||
|
assert subscription.subscriptionId == "SUB-1"
|
||||||
|
create_request = httpx_mock.get_requests()[0]
|
||||||
|
create_body = json.loads(create_request.content.decode("utf-8"))
|
||||||
|
assert create_body["destinationId"] == "DEST-1"
|
||||||
|
assert create_body["payload"]["deliveryProtocol"] == "HTTPS"
|
||||||
|
update_request = httpx_mock.get_requests()[2]
|
||||||
|
update_body = json.loads(update_request.content.decode("utf-8"))
|
||||||
|
assert update_body == {"destinationId": "DEST-1", "status": "ENABLED"}
|
||||||
|
|
||||||
|
|
||||||
|
def test_notification_subscription_filter_wrapper_serializes_and_returns_model(httpx_mock: HTTPXMock) -> None:
|
||||||
|
httpx_mock.add_response(
|
||||||
|
method="POST",
|
||||||
|
url="https://api.ebay.com/commerce/notification/v1/subscription/SUB-1/filter",
|
||||||
|
json={},
|
||||||
|
status_code=201,
|
||||||
|
)
|
||||||
|
httpx_mock.add_response(
|
||||||
|
method="GET",
|
||||||
|
url="https://api.ebay.com/commerce/notification/v1/subscription/SUB-1/filter/FILTER-1",
|
||||||
|
json={"filterId": "FILTER-1", "subscriptionId": "SUB-1", "filterStatus": "ENABLED"},
|
||||||
|
)
|
||||||
|
httpx_mock.add_response(
|
||||||
|
method="DELETE",
|
||||||
|
url="https://api.ebay.com/commerce/notification/v1/subscription/SUB-1/filter/FILTER-1",
|
||||||
|
status_code=204,
|
||||||
|
)
|
||||||
|
|
||||||
|
client = NotificationClient(build_transport())
|
||||||
|
payload = CreateSubscriptionFilterRequest(
|
||||||
|
filterSchema={
|
||||||
|
"properties": {
|
||||||
|
"data": {
|
||||||
|
"type": "object",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
client.create_subscription_filter("SUB-1", payload)
|
||||||
|
subscription_filter = client.get_subscription_filter("SUB-1", "FILTER-1")
|
||||||
|
client.delete_subscription_filter("SUB-1", "FILTER-1")
|
||||||
|
|
||||||
|
assert isinstance(subscription_filter, SubscriptionFilter)
|
||||||
|
assert subscription_filter.filterId == "FILTER-1"
|
||||||
|
request = httpx_mock.get_requests()[0]
|
||||||
|
body = json.loads(request.content.decode("utf-8"))
|
||||||
|
assert body["filterSchema"]["properties"]["data"]["type"] == "object"
|
||||||
|
|
||||||
|
|
||||||
def test_inventory_wrapper_returns_inventory_item_model(httpx_mock: HTTPXMock) -> None:
|
def test_inventory_wrapper_returns_inventory_item_model(httpx_mock: HTTPXMock) -> None:
|
||||||
httpx_mock.add_response(
|
httpx_mock.add_response(
|
||||||
method="GET",
|
method="GET",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue