diff --git a/examples/sinch_events/.env.example b/examples/sinch_events/.env.example index 13561254..02133356 100644 --- a/examples/sinch_events/.env.example +++ b/examples/sinch_events/.env.example @@ -1,11 +1,11 @@ # Server Configuration SERVER_PORT = -# Webhook Configuration -# The secret value used for webhook calls validation +# Sinch Event Configuration +# The secret value used for Sinch Event callback validation # See https://developers.sinch.com/docs/numbers/api-reference/numbers/tag/Numbers-Callbacks/ -NUMBERS_WEBHOOKS_SECRET = NUMBERS_WEBHOOKS_SECRET +NUMBERS_SINCH_EVENT_SECRET = NUMBERS_SINCH_EVENT_SECRET # See https://developers.sinch.com/docs/sms/api-reference/sms/tag/Webhooks/#tag/Webhooks/section/Callbacks -SMS_WEBHOOKS_SECRET = SMS_WEBHOOKS_SECRET +SMS_SINCH_EVENT_SECRET = SMS_SINCH_EVENT_SECRET # See https://developers.sinch.com/docs/conversation/callbacks -CONVERSATION_WEBHOOKS_SECRET = CONVERSATION_WEBHOOKS_SECRET \ No newline at end of file +CONVERSATION_SINCH_EVENT_SECRET = CONVERSATION_SINCH_EVENT_SECRET \ No newline at end of file diff --git a/examples/sinch_events/README.md b/examples/sinch_events/README.md index 20693812..826fe987 100644 --- a/examples/sinch_events/README.md +++ b/examples/sinch_events/README.md @@ -3,12 +3,12 @@ This directory contains a server application built with [Sinch Python SDK](https://github.com/sinch/sinch-sdk-python) to process incoming webhooks from Sinch services. -The webhook handlers are organized by service: -- **SMS**: Handlers for SMS webhook events (`sms_api/`) -- **Numbers**: Handlers for Numbers API webhook events (`numbers_api/`) -- **Conversation**: Handlers for Conversation API webhook events (`conversation_api/`) +The Sinch Events Handlers are organized by service: +- **SMS**: Handlers for SMS events (`sms_api/`) +- **Numbers**: Handlers for Numbers API events (`numbers_api/`) +- **Conversation**: Handlers for Conversation API events (`conversation_api/`) -This directory contains both the webhook handlers and the server application (`server.py`) that uses them. +This directory contains both the Event handlers and the server application (`server.py`) that uses them. ## Requirements @@ -32,17 +32,17 @@ This directory contains both the webhook handlers and the server application (`s - Controller Settings - Numbers controller: Set the `numbers` Sinch Event secret. You can retrieve it using the `/event_destination` endpoint (see SDK implementation: [event_destinations_apis.py](https://github.com/sinch/sinch-sdk-python/blob/v2.0/sinch/domains/numbers/api/v1/event_destinations_apis.py); for additional details, refer to the [Numbers API callbacks documentation](https://developers.sinch.com/docs/numbers/api-reference/numbers/tag/Numbers-Callbacks/)): ``` - NUMBERS_WEBHOOKS_SECRET=Your Sinch Numbers Webhook Secret + NUMBERS_SINCH_EVENT_SECRET=Your Sinch Numbers Sinch Event Secret ``` - - SMS controller: To configure the `sms` webhooks secret, contact your account manager to enable authentication for SMS callbacks. For more details, refer to + - SMS controller: To configure the `sms` Sinch Event secret, contact your account manager to enable authentication for SMS callbacks. For more details, refer to [SMS API](https://developers.sinch.com/docs/sms/api-reference/sms/tag/Webhooks/#tag/Webhooks/section/Callbacks), ``` - SMS_WEBHOOKS_SECRET=Your Sinch SMS Webhook Secret + SMS_SINCH_EVENT_SECRET=Your Sinch SMS Sinch Event Secret ``` - Conversation controller: Set the webhook secret you configured when creating the webhook (see [Conversation API callbacks](https://developers.sinch.com/docs/conversation/callbacks)): ``` - CONVERSATION_WEBHOOKS_SECRET=Your Conversation Webhook Secret + CONVERSATION_SINCH_EVENT_SECRET=Your Conversation Sinch Event Secret ``` ## Usage @@ -96,18 +96,18 @@ ngrok ... Forwarding https://adbd-79-148-170-158.ngrok-free.app -> http://localhost:3001 ``` -Use the `https` forwarding URL in your callback configuration. For example: +Use the `https` forwarding URL in your event destination configuration. For example: - Numbers: https://adbd-79-148-170-158.ngrok-free.app/NumbersEvent - SMS: https://adbd-79-148-170-158.ngrok-free.app/SmsEvent - Conversation: https://adbd-79-148-170-158.ngrok-free.app/ConversationEvent -Use this value to configure the callback URLs: -- **Numbers**: Set the `callback_url` parameter when renting or updating a number via the SDK (e.g., `available_numbers_apis` rent/update flow: [rent](https://github.com/sinch/sinch-sdk-python/blob/v2.0/sinch/domains/numbers/api/v1/available_numbers_apis.py#L69), [update](https://github.com/sinch/sinch-sdk-python/blob/v2.0/sinch/domains/numbers/api/v1/available_numbers_apis.py#L89)); you can also update active numbers via `active_numbers_apis` ([example](https://github.com/sinch/sinch-sdk-python/blob/v2.0/sinch/domains/numbers/api/v1/active_numbers_apis.py#L64)). -- **SMS**: Set the `callback_url` parameter when configuring your SMS service plan via the SDK (see `batches_apis` examples: [send/dry-run callbacks](https://github.com/sinch/sinch-sdk-python/blob/v2.0/sinch/domains/sms/api/v1/batches_apis.py#L146), [update/replace callbacks](https://github.com/sinch/sinch-sdk-python/blob/v2.0/sinch/domains/sms/api/v1/batches_apis.py#L491)); you can also set it directly via the SMS API. +Use this value to configure the Sinch Events URLs: +- **Numbers**: Set the `event_destination_target` parameter when renting or updating a number via the SDK (e.g., `available_numbers_apis` rent/update flow: [rent](https://github.com/sinch/sinch-sdk-python/blob/v2.0/sinch/domains/numbers/api/v1/available_numbers_apis.py#L69), [update](https://github.com/sinch/sinch-sdk-python/blob/v2.0/sinch/domains/numbers/api/v1/available_numbers_apis.py#L89)); you can also update active numbers via `active_numbers_apis` ([example](https://github.com/sinch/sinch-sdk-python/blob/v2.0/sinch/domains/numbers/api/v1/active_numbers_apis.py#L64)). +- **SMS**: Set the `event_destination_target` parameter when configuring your SMS service plan via the SDK (see `batches_apis` examples: [send/dry-run callbacks](https://github.com/sinch/sinch-sdk-python/blob/v2.0/sinch/domains/sms/api/v1/batches_apis.py#L146), [update/replace callbacks](https://github.com/sinch/sinch-sdk-python/blob/v2.0/sinch/domains/sms/api/v1/batches_apis.py#L491)); you can also set it directly via the SMS API. - **Conversation**: Set the `callback_url` parameter when sending a message via the SDK (see `messages_apis` example: [send_text_message](https://github.com/sinch/sinch-sdk-python/blob/v2.0/sinch/domains/conversation/api/v1/messages_apis.py#L420)). -You can also set these callback URLs in the Sinch dashboard; the API parameters above override the default values configured there. +You can also set these Sinch Events URLs in the Sinch dashboard; the API parameters above override the default values configured there. -> **Note**: If you have set a webhook secret (e.g., `SMS_WEBHOOKS_SECRET`), the webhook URL must be configured in the Sinch dashboard -> and cannot be overridden via API parameters. The webhook secret is used to validate incoming webhook requests, +> **Note**: If you have set a Sinch Event secret (e.g., `SMS_SINCH_EVENT_SECRET`), the Sinch Event URL must be configured in the Sinch dashboard +> and cannot be overridden via API parameters. The Sinch Event secret is used to validate incoming webhook requests, > and the URL associated with it must be set in the dashboard. diff --git a/examples/sinch_events/conversation_api/controller.py b/examples/sinch_events/conversation_api/controller.py index 74e47888..b71a287b 100644 --- a/examples/sinch_events/conversation_api/controller.py +++ b/examples/sinch_events/conversation_api/controller.py @@ -1,18 +1,20 @@ from flask import request, Response -from webhooks.conversation_api.server_business_logic import handle_conversation_event +from sinch_events.conversation_api.server_business_logic import handle_conversation_event class ConversationController: - def __init__(self, sinch_client, webhooks_secret): + def __init__(self, sinch_client, sinch_event_secret): self.sinch_client = sinch_client - self.webhooks_secret = webhooks_secret + self.sinch_event_secret = sinch_event_secret self.logger = self.sinch_client.configuration.logger def conversation_event(self): headers = dict(request.headers) raw_body = request.raw_body if request.raw_body else b"" - webhooks_service = self.sinch_client.conversation.webhooks(self.webhooks_secret) + webhooks_service = self.sinch_client.conversation.webhooks( + self.sinch_event_secret + ) # Set to True to enforce signature validation (recommended in production) ensure_valid_signature = False diff --git a/examples/sinch_events/numbers_api/controller.py b/examples/sinch_events/numbers_api/controller.py index 00eaac47..cdc43e22 100644 --- a/examples/sinch_events/numbers_api/controller.py +++ b/examples/sinch_events/numbers_api/controller.py @@ -1,11 +1,11 @@ from flask import request, Response -from webhooks.numbers_api.server_business_logic import handle_numbers_event +from sinch_events.numbers_api.server_business_logic import handle_numbers_event class NumbersController: - def __init__(self, sinch_client, webhooks_secret): + def __init__(self, sinch_client, sinch_event_secret): self.sinch_client = sinch_client - self.webhooks_secret = webhooks_secret + self.sinch_event_secret = sinch_event_secret self.logger = self.sinch_client.configuration.logger def numbers_event(self): @@ -13,7 +13,7 @@ def numbers_event(self): raw_body = request.raw_body if request.raw_body else b"" sinch_events_service = self.sinch_client.numbers.sinch_events( - self.webhooks_secret + self.sinch_event_secret ) ensure_valid_authentication = False diff --git a/examples/sinch_events/server.py b/examples/sinch_events/server.py index 98caa89a..368cec68 100644 --- a/examples/sinch_events/server.py +++ b/examples/sinch_events/server.py @@ -2,33 +2,35 @@ import sys from pathlib import Path -# Add examples directory to Python path to allow importing webhooks +# Add examples directory to Python path to allow importing sinch_events examples_dir = Path(__file__).resolve().parent.parent if str(examples_dir) not in sys.path: sys.path.insert(0, str(examples_dir)) from flask import Flask, request -from webhooks.numbers_api.controller import NumbersController -from webhooks.sms_api.controller import SmsController -from webhooks.conversation_api.controller import ConversationController -from webhooks.sinch_client_helper import get_sinch_client, load_config +from sinch_events.numbers_api.controller import NumbersController +from sinch_events.sms_api.controller import SmsController +from sinch_events.conversation_api.controller import ConversationController +from sinch_events.sinch_client_helper import get_sinch_client, load_config app = Flask(__name__) config = load_config() port = int(config.get('SERVER_PORT') or 3001) -numbers_webhooks_secret = config.get('NUMBERS_WEBHOOKS_SECRET') -sms_webhooks_secret = config.get('SMS_WEBHOOKS_SECRET') -conversation_webhooks_secret = config.get('CONVERSATION_WEBHOOKS_SECRET') +numbers_sinch_event_secret = config.get('NUMBERS_SINCH_EVENT_SECRET') +sms_sinch_event_secret = config.get('SMS_SINCH_EVENT_SECRET') +conversation_sinch_event_secret = config.get('CONVERSATION_SINCH_EVENT_SECRET') sinch_client = get_sinch_client(config) # Set up logging at the INFO level logging.basicConfig() sinch_client.configuration.logger.setLevel(logging.INFO) -numbers_controller = NumbersController(sinch_client, numbers_webhooks_secret) -sms_controller = SmsController(sinch_client, sms_webhooks_secret) -conversation_controller = ConversationController(sinch_client, conversation_webhooks_secret or '') +numbers_controller = NumbersController(sinch_client, numbers_sinch_event_secret) +sms_controller = SmsController(sinch_client, sms_sinch_event_secret) +conversation_controller = ConversationController( + sinch_client, conversation_sinch_event_secret or '' +) # Middleware to capture raw body diff --git a/examples/sinch_events/sinch_client_helper.py b/examples/sinch_events/sinch_client_helper.py index 109fce1c..fdc662ab 100644 --- a/examples/sinch_events/sinch_client_helper.py +++ b/examples/sinch_events/sinch_client_helper.py @@ -5,7 +5,7 @@ def load_config() -> dict[str, str]: """ - Load configuration from the .env file in the webhooks directory. + Load configuration from the .env file in the sinch_events directory. Returns: dict[str, str]: Dictionary containing configuration values @@ -15,7 +15,7 @@ def load_config() -> dict[str, str]: env_file = current_dir / '.env' if not env_file.exists(): - raise FileNotFoundError(f"Could not find .env file in webhooks directory: {env_file}") + raise FileNotFoundError(f"Could not find .env file in sinch_events directory: {env_file}") config_dict = dotenv_values(env_file) diff --git a/examples/sinch_events/sms_api/controller.py b/examples/sinch_events/sms_api/controller.py index b5bd50f5..2ebfda6c 100644 --- a/examples/sinch_events/sms_api/controller.py +++ b/examples/sinch_events/sms_api/controller.py @@ -1,27 +1,27 @@ from flask import request, Response -from webhooks.sms_api.server_business_logic import ( +from sinch_events.sms_api.server_business_logic import ( handle_sms_event, ) class SmsController: - def __init__(self, sinch_client, webhooks_secret): + def __init__(self, sinch_client, sinch_event_secret): self.sinch_client = sinch_client - self.webhooks_secret = webhooks_secret + self.sinch_event_secret = sinch_event_secret self.logger = self.sinch_client.configuration.logger def sms_event(self): headers = dict(request.headers) raw_body = request.raw_body if request.raw_body else b"" - webhooks_service = self.sinch_client.sms.webhooks(self.webhooks_secret) + sinch_events_service = self.sinch_client.sms.sinch_events(self.sinch_event_secret) # Signature headers may be absent unless your account manager enables them # (see README: Configuration -> Controller Settings -> SMS controller); # leave auth disabled here unless SMS callbacks are configured. ensure_valid_authentication = False if ensure_valid_authentication: - valid_auth = webhooks_service.validate_authentication_header( + valid_auth = sinch_events_service.validate_authentication_header( headers=headers, json_payload=raw_body, ) @@ -29,7 +29,7 @@ def sms_event(self): if not valid_auth: return Response(status=401) - event = webhooks_service.parse_event(raw_body, headers) + event = sinch_events_service.parse_event(raw_body, headers) handle_sms_event(sms_event=event, logger=self.logger) diff --git a/examples/sinch_events/sms_api/server_business_logic.py b/examples/sinch_events/sms_api/server_business_logic.py index aa47e470..7061394d 100644 --- a/examples/sinch_events/sms_api/server_business_logic.py +++ b/examples/sinch_events/sms_api/server_business_logic.py @@ -1,11 +1,13 @@ -from sinch.domains.sms.webhooks.v1.events.sms_webhooks_event import IncomingSMSWebhookEvent +from sinch.domains.sms.sinch_events.v1.events.sms_sinch_event import ( + IncomingSMSSinchEvent, +) -def handle_sms_event(sms_event: IncomingSMSWebhookEvent, logger): +def handle_sms_event(sms_event: IncomingSMSSinchEvent, logger): """ This method handles an SMS event. Args: - sms_event (SmsWebhooksEvent): The SMS event data. + sms_event (IncomingSMSSinchEvent): The SMS event data. logger (logging.Logger, optional): Logger instance for logging. Defaults to None. """ logger.info(f'Handling SMS event:\n{sms_event.model_dump_json(indent=2)}') diff --git a/sinch/domains/sms/api/v1/batches_apis.py b/sinch/domains/sms/api/v1/batches_apis.py index 843ad482..e4a34576 100644 --- a/sinch/domains/sms/api/v1/batches_apis.py +++ b/sinch/domains/sms/api/v1/batches_apis.py @@ -143,7 +143,7 @@ def dry_run_sms( delivery_report: Optional[DeliveryReportType] = None, send_at: Optional[datetime] = None, expire_at: Optional[datetime] = None, - callback_url: Optional[str] = None, + event_destination_target: Optional[str] = None, client_reference: Optional[str] = None, feedback_enabled: Optional[bool] = None, flash_message: Optional[bool] = None, @@ -175,8 +175,8 @@ def dry_run_sms( :type send_at: Optional[datetime] :param expire_at: The time to expire the message at. (optional) :type expire_at: Optional[datetime] - :param callback_url: The callback URL to receive the delivery report. (optional) - :type callback_url: Optional[str] + :param event_destination_target: The callback URL to receive the delivery report. (optional) + :type event_destination_target: Optional[str] :param client_reference: The client reference to identify the message. (optional) :type client_reference: Optional[str] :param feedback_enabled: Whether to enable feedback. (optional) @@ -209,7 +209,7 @@ def dry_run_sms( delivery_report=delivery_report, send_at=send_at, expire_at=expire_at, - callback_url=callback_url, + event_destination_target=event_destination_target, client_reference=client_reference, feedback_enabled=feedback_enabled, flash_message=flash_message, @@ -232,7 +232,7 @@ def dry_run_binary( delivery_report: Optional[DeliveryReportType] = None, send_at: Optional[datetime] = None, expire_at: Optional[datetime] = None, - callback_url: Optional[str] = None, + event_destination_target: Optional[str] = None, client_reference: Optional[str] = None, feedback_enabled: Optional[bool] = None, from_ton: Optional[int] = None, @@ -261,8 +261,8 @@ def dry_run_binary( :type send_at: Optional[datetime] :param expire_at: The time to expire the message at. (optional) :type expire_at: Optional[datetime] - :param callback_url: The callback URL to receive the delivery report. (optional) - :type callback_url: Optional[str] + :param event_destination_target: The callback URL to receive the delivery report. (optional) + :type event_destination_target: Optional[str] :param client_reference: The client reference to identify the message. (optional) :type client_reference: Optional[str] :param feedback_enabled: Whether to enable feedback. (optional) @@ -289,7 +289,7 @@ def dry_run_binary( delivery_report=delivery_report, send_at=send_at, expire_at=expire_at, - callback_url=callback_url, + event_destination_target=event_destination_target, client_reference=client_reference, feedback_enabled=feedback_enabled, from_ton=from_ton, @@ -309,7 +309,7 @@ def dry_run_mms( delivery_report: Optional[DeliveryReportType] = None, send_at: Optional[datetime] = None, expire_at: Optional[datetime] = None, - callback_url: Optional[str] = None, + event_destination_target: Optional[str] = None, client_reference: Optional[str] = None, feedback_enabled: Optional[bool] = None, strict_validation: Optional[bool] = None, @@ -337,8 +337,8 @@ def dry_run_mms( :type send_at: Optional[datetime] :param expire_at: The time to expire the message at. (optional) :type expire_at: Optional[datetime] - :param callback_url: The callback URL to receive the delivery report. (optional) - :type callback_url: Optional[str] + :param event_destination_target: The callback URL to receive the delivery report. (optional) + :type event_destination_target: Optional[str] :param client_reference: The client reference to identify the message. (optional) :type client_reference: Optional[str] :param feedback_enabled: Whether to enable feedback. (optional) @@ -363,7 +363,7 @@ def dry_run_mms( delivery_report=delivery_report, send_at=send_at, expire_at=expire_at, - callback_url=callback_url, + event_destination_target=event_destination_target, client_reference=client_reference, feedback_enabled=feedback_enabled, strict_validation=strict_validation, @@ -487,7 +487,7 @@ def replace_sms( delivery_report: Optional[DeliveryReportType] = None, send_at: Optional[datetime] = None, expire_at: Optional[datetime] = None, - callback_url: Optional[str] = None, + event_destination_target: Optional[str] = None, client_reference: Optional[str] = None, feedback_enabled: Optional[bool] = None, flash_message: Optional[bool] = None, @@ -516,8 +516,8 @@ def replace_sms( :type send_at: Optional[datetime] :param expire_at: The time to expire the message at. (optional) :type expire_at: Optional[datetime] - :param callback_url: The callback URL to receive the delivery report. (optional) - :type callback_url: Optional[str] + :param event_destination_target: The callback URL to receive the delivery report. (optional) + :type event_destination_target: Optional[str] :param client_reference: The client reference to identify the message. (optional) :type client_reference: Optional[str] :param feedback_enabled: Whether to enable feedback. (optional) @@ -550,7 +550,7 @@ def replace_sms( delivery_report=delivery_report, send_at=send_at, expire_at=expire_at, - callback_url=callback_url, + event_destination_target=event_destination_target, client_reference=client_reference, feedback_enabled=feedback_enabled, flash_message=flash_message, @@ -573,7 +573,7 @@ def replace_binary( delivery_report: Optional[DeliveryReportType] = None, send_at: Optional[datetime] = None, expire_at: Optional[datetime] = None, - callback_url: Optional[str] = None, + event_destination_target: Optional[str] = None, client_reference: Optional[str] = None, feedback_enabled: Optional[bool] = None, from_ton: Optional[int] = None, @@ -600,8 +600,8 @@ def replace_binary( :type send_at: Optional[datetime] :param expire_at: The time to expire the message at. (optional) :type expire_at: Optional[datetime] - :param callback_url: The callback URL to receive the delivery report. (optional) - :type callback_url: Optional[str] + :param event_destination_target: The callback URL to receive the delivery report. (optional) + :type event_destination_target: Optional[str] :param client_reference: The client reference to identify the message. (optional) :type client_reference: Optional[str] :param feedback_enabled: Whether to enable feedback. (optional) @@ -627,7 +627,7 @@ def replace_binary( delivery_report=delivery_report, send_at=send_at, expire_at=expire_at, - callback_url=callback_url, + event_destination_target=event_destination_target, client_reference=client_reference, feedback_enabled=feedback_enabled, from_ton=from_ton, @@ -645,7 +645,7 @@ def replace_mms( delivery_report: Optional[DeliveryReportType] = None, send_at: Optional[datetime] = None, expire_at: Optional[datetime] = None, - callback_url: Optional[str] = None, + event_destination_target: Optional[str] = None, client_reference: Optional[str] = None, feedback_enabled: Optional[bool] = None, strict_validation: Optional[bool] = None, @@ -670,8 +670,8 @@ def replace_mms( :type send_at: Optional[datetime] :param expire_at: The time to expire the message at. (optional) :type expire_at: Optional[datetime] - :param callback_url: The callback URL to receive the delivery report. (optional) - :type callback_url: Optional[str] + :param event_destination_target: The callback URL to receive the delivery report. (optional) + :type event_destination_target: Optional[str] :param client_reference: The client reference to identify the message. (optional) :type client_reference: Optional[str] :param feedback_enabled: Whether to enable feedback. (optional) @@ -696,7 +696,7 @@ def replace_mms( delivery_report=delivery_report, send_at=send_at, expire_at=expire_at, - callback_url=callback_url, + event_destination_target=event_destination_target, client_reference=client_reference, feedback_enabled=feedback_enabled, strict_validation=strict_validation, @@ -751,7 +751,7 @@ def send_sms( delivery_report: Optional[DeliveryReportType] = None, send_at: Optional[datetime] = None, expire_at: Optional[datetime] = None, - callback_url: Optional[str] = None, + event_destination_target: Optional[str] = None, client_reference: Optional[str] = None, feedback_enabled: Optional[bool] = None, flash_message: Optional[bool] = None, @@ -784,8 +784,8 @@ def send_sms( :type send_at: Optional[datetime] :param expire_at: The time to expire the message at. (optional) :type expire_at: Optional[datetime] - :param callback_url: The callback URL to receive the delivery report. (optional) - :type callback_url: Optional[str] + :param event_destination_target: The callback URL to receive the delivery report. (optional) + :type event_destination_target: Optional[str] :param client_reference: The client reference to identify the message. (optional) :type client_reference: Optional[str] :param feedback_enabled: Whether to enable feedback. (optional) @@ -817,7 +817,7 @@ def send_sms( delivery_report=delivery_report, send_at=send_at, expire_at=expire_at, - callback_url=callback_url, + event_destination_target=event_destination_target, client_reference=client_reference, feedback_enabled=feedback_enabled, flash_message=flash_message, @@ -839,7 +839,7 @@ def send_binary( delivery_report: Optional[DeliveryReportType] = None, send_at: Optional[datetime] = None, expire_at: Optional[datetime] = None, - callback_url: Optional[str] = None, + event_destination_target: Optional[str] = None, client_reference: Optional[str] = None, feedback_enabled: Optional[bool] = None, from_ton: Optional[int] = None, @@ -870,8 +870,8 @@ def send_binary( :type send_at: Optional[datetime] :param expire_at: The time to expire the message at. (optional) :type expire_at: Optional[datetime] - :param callback_url: The callback URL to receive the delivery report. (optional) - :type callback_url: Optional[str] + :param event_destination_target: The callback URL to receive the delivery report. (optional) + :type event_destination_target: Optional[str] :param client_reference: The client reference to identify the message. (optional) :type client_reference: Optional[str] :param feedback_enabled: Whether to enable feedback. (optional) @@ -896,7 +896,7 @@ def send_binary( delivery_report=delivery_report, send_at=send_at, expire_at=expire_at, - callback_url=callback_url, + event_destination_target=event_destination_target, client_reference=client_reference, feedback_enabled=feedback_enabled, from_ton=from_ton, @@ -913,7 +913,7 @@ def send_mms( delivery_report: Optional[DeliveryReportType] = None, send_at: Optional[datetime] = None, expire_at: Optional[datetime] = None, - callback_url: Optional[str] = None, + event_destination_target: Optional[str] = None, client_reference: Optional[str] = None, feedback_enabled: Optional[bool] = None, strict_validation: Optional[bool] = None, @@ -942,8 +942,8 @@ def send_mms( :type send_at: Optional[datetime] :param expire_at: The time to expire the message at. (optional) :type expire_at: Optional[datetime] - :param callback_url: The callback URL to receive the delivery report. (optional) - :type callback_url: Optional[str] + :param event_destination_target: The callback URL to receive the delivery report. (optional) + :type event_destination_target: Optional[str] :param client_reference: The client reference to identify the message. (optional) :type client_reference: Optional[str] :param feedback_enabled: Whether to enable feedback. (optional) @@ -967,7 +967,7 @@ def send_mms( delivery_report=delivery_report, send_at=send_at, expire_at=expire_at, - callback_url=callback_url, + event_destination_target=event_destination_target, client_reference=client_reference, feedback_enabled=feedback_enabled, strict_validation=strict_validation, @@ -1057,7 +1057,7 @@ def update_sms( delivery_report: Optional[DeliveryReportType] = None, send_at: Optional[datetime] = None, expire_at: Optional[datetime] = None, - callback_url: Optional[str] = None, + event_destination_target: Optional[str] = None, client_reference: Optional[str] = None, feedback_enabled: Optional[bool] = None, parameters: Optional[Dict[str, Dict[str, str]]] = None, @@ -1087,8 +1087,8 @@ def update_sms( :type send_at: Optional[datetime] :param expire_at: The time to expire the message at. (optional) :type expire_at: Optional[datetime] - :param callback_url: The callback URL to receive the delivery report. (optional) - :type callback_url: Optional[str] + :param event_destination_target: The callback URL to receive the delivery report. (optional) + :type event_destination_target: Optional[str] :param client_reference: The client reference to identify the message. (optional) :type client_reference: Optional[str] :param feedback_enabled: Whether to enable feedback. (optional) @@ -1122,7 +1122,7 @@ def update_sms( delivery_report=delivery_report, send_at=send_at, expire_at=expire_at, - callback_url=callback_url, + event_destination_target=event_destination_target, client_reference=client_reference, feedback_enabled=feedback_enabled, parameters=parameters, @@ -1146,7 +1146,7 @@ def update_binary( delivery_report: Optional[DeliveryReportType] = None, send_at: Optional[datetime] = None, expire_at: Optional[datetime] = None, - callback_url: Optional[str] = None, + event_destination_target: Optional[str] = None, client_reference: Optional[str] = None, feedback_enabled: Optional[bool] = None, from_ton: Optional[int] = None, @@ -1174,8 +1174,8 @@ def update_binary( :type send_at: Optional[datetime] :param expire_at: The time to expire the message at. (optional) :type expire_at: Optional[datetime] - :param callback_url: The callback URL to receive the delivery report. (optional) - :type callback_url: Optional[str] + :param event_destination_target: The callback URL to receive the delivery report. (optional) + :type event_destination_target: Optional[str] :param client_reference: The client reference to identify the message. (optional) :type client_reference: Optional[str] :param feedback_enabled: Whether to enable feedback. (optional) @@ -1202,7 +1202,7 @@ def update_binary( delivery_report=delivery_report, send_at=send_at, expire_at=expire_at, - callback_url=callback_url, + event_destination_target=event_destination_target, client_reference=client_reference, feedback_enabled=feedback_enabled, from_ton=from_ton, @@ -1221,7 +1221,7 @@ def update_mms( delivery_report: Optional[DeliveryReportType] = None, send_at: Optional[datetime] = None, expire_at: Optional[datetime] = None, - callback_url: Optional[str] = None, + event_destination_target: Optional[str] = None, client_reference: Optional[str] = None, feedback_enabled: Optional[bool] = None, parameters: Optional[Dict[str, Dict[str, str]]] = None, @@ -1247,8 +1247,8 @@ def update_mms( :type send_at: Optional[datetime] :param expire_at: The time to expire the message at. (optional) :type expire_at: Optional[datetime] - :param callback_url: The callback URL to receive the delivery report. (optional) - :type callback_url: Optional[str] + :param event_destination_target: The callback URL to receive the delivery report. (optional) + :type event_destination_target: Optional[str] :param client_reference: The client reference to identify the message. (optional) :type client_reference: Optional[str] :param feedback_enabled: Whether to enable feedback. (optional) @@ -1274,7 +1274,7 @@ def update_mms( delivery_report=delivery_report, send_at=send_at, expire_at=expire_at, - callback_url=callback_url, + event_destination_target=event_destination_target, client_reference=client_reference, feedback_enabled=feedback_enabled, parameters=parameters, diff --git a/sinch/domains/sms/models/v1/internal/update_binary_request.py b/sinch/domains/sms/models/v1/internal/update_binary_request.py index a73f090f..f23c5182 100644 --- a/sinch/domains/sms/models/v1/internal/update_binary_request.py +++ b/sinch/domains/sms/models/v1/internal/update_binary_request.py @@ -34,8 +34,9 @@ class UpdateBinaryRequest(BaseModelConfigurationRequest): default=None, description="If set, the system will stop trying to deliver the message at this point. Constraints: Must be after `send_at` Default: 3 days after `send_at` ", ) - callback_url: Optional[StrictStr] = Field( + event_destination_target: Optional[StrictStr] = Field( default=None, + alias="callback_url", description="Override the default callback URL for this batch. Constraints: Must be valid URL. ", ) client_reference: Optional[StrictStr] = Field( diff --git a/sinch/domains/sms/models/v1/internal/update_media_request.py b/sinch/domains/sms/models/v1/internal/update_media_request.py index 1aa5f6dc..ed347d3d 100644 --- a/sinch/domains/sms/models/v1/internal/update_media_request.py +++ b/sinch/domains/sms/models/v1/internal/update_media_request.py @@ -32,8 +32,9 @@ class UpdateMediaRequest(BaseModelConfigurationRequest): default=None, description="If set, the system will stop trying to deliver the message at this point. Constraints: Must be after `send_at` Default: 3 days after `send_at` ", ) - callback_url: Optional[StrictStr] = Field( + event_destination_target: Optional[StrictStr] = Field( default=None, + alias="callback_url", description="Override the default callback URL for this batch. Constraints: Must be valid URL. ", ) client_reference: Optional[StrictStr] = Field( diff --git a/sinch/domains/sms/models/v1/internal/update_text_request.py b/sinch/domains/sms/models/v1/internal/update_text_request.py index 81e4e2ea..d43ee83f 100644 --- a/sinch/domains/sms/models/v1/internal/update_text_request.py +++ b/sinch/domains/sms/models/v1/internal/update_text_request.py @@ -39,8 +39,9 @@ class UpdateTextRequest(BaseModelConfigurationRequest): default=None, description="If set, the system will stop trying to deliver the message at this point. Constraints: Must be after `send_at` Default: 3 days after `send_at` ", ) - callback_url: Optional[StrictStr] = Field( + event_destination_target: Optional[StrictStr] = Field( default=None, + alias="callback_url", description="Override the default callback URL for this batch. Constraints: Must be valid URL. ", ) client_reference: Optional[StrictStr] = Field( diff --git a/sinch/domains/sms/models/v1/shared/binary_request.py b/sinch/domains/sms/models/v1/shared/binary_request.py index 027d98e9..35fc4146 100644 --- a/sinch/domains/sms/models/v1/shared/binary_request.py +++ b/sinch/domains/sms/models/v1/shared/binary_request.py @@ -40,8 +40,9 @@ class BinaryRequest(BaseModelConfigurationRequest): default=None, description="If set, the system will stop trying to deliver the message at this point. Must be after `send_at`. Default and max is 3 days after `send_at`. Formatted as [ISO-8601](https://en.wikipedia.org/wiki/ISO_8601). For example: `YYYY-MM-DDThh:mm:ss.SSSZ`.", ) - callback_url: Optional[StrictStr] = Field( + event_destination_target: Optional[StrictStr] = Field( default=None, + alias="callback_url", description="Override the *default* callback URL for this batch. Must be a valid URL. Learn how to set a default callback URL [here](https://community.sinch.com/t5/SMS/How-do-I-assign-a-callback-URL-to-an-SMS-service-plan/ta-p/8414).", ) client_reference: Optional[StrictStr] = Field( diff --git a/sinch/domains/sms/models/v1/shared/binary_response.py b/sinch/domains/sms/models/v1/shared/binary_response.py index 171b2fd2..96309913 100644 --- a/sinch/domains/sms/models/v1/shared/binary_response.py +++ b/sinch/domains/sms/models/v1/shared/binary_response.py @@ -61,8 +61,10 @@ class BinaryResponse(BaseModelConfigurationResponse): default=None, description="If set, the date and time the message will expire. Formatted as [ISO-8601](https://en.wikipedia.org/wiki/ISO_8601). For example: `YYYY-MM-DDThh:mm:ss.SSSZ`.", ) - callback_url: Optional[StrictStr] = Field( - default=None, description="The callback URL provided in the request." + event_destination_target: Optional[StrictStr] = Field( + default=None, + alias="callback_url", + description="The callback URL provided in the request.", ) client_reference: Optional[StrictStr] = Field( default=None, diff --git a/sinch/domains/sms/models/v1/shared/media_request.py b/sinch/domains/sms/models/v1/shared/media_request.py index 54ef9203..57d5311a 100644 --- a/sinch/domains/sms/models/v1/shared/media_request.py +++ b/sinch/domains/sms/models/v1/shared/media_request.py @@ -35,8 +35,9 @@ class MediaRequest(BaseModelConfigurationRequest): default=None, description="If set, the system will stop trying to deliver the message at this point. Must be after `send_at`. Default and max is 3 days after `send_at`. Formatted as [ISO-8601](https://en.wikipedia.org/wiki/ISO_8601): `YYYY-MM-DDThh:mm:ss.SSSZ`. ", ) - callback_url: Optional[StrictStr] = Field( + event_destination_target: Optional[StrictStr] = Field( default=None, + alias="callback_url", description="Override the default callback URL for this batch. Must be valid URL.", ) client_reference: Optional[StrictStr] = Field( diff --git a/sinch/domains/sms/models/v1/shared/media_response.py b/sinch/domains/sms/models/v1/shared/media_response.py index 7da3a82f..5971208a 100644 --- a/sinch/domains/sms/models/v1/shared/media_response.py +++ b/sinch/domains/sms/models/v1/shared/media_response.py @@ -50,8 +50,9 @@ class MediaResponse(BaseModelConfigurationResponse): default=None, description="If set the system will stop trying to deliver the message at this point. Must be after `send_at`. Default and max is 3 days after send_at. YYYY-MM-DDThh:mm:ss.SSSZ format", ) - callback_url: Optional[StrictStr] = Field( + event_destination_target: Optional[StrictStr] = Field( default=None, + alias="callback_url", description="Override the default callback URL for this batch. Must be valid URL.", ) client_reference: Optional[StrictStr] = Field( diff --git a/sinch/domains/sms/models/v1/shared/text_request.py b/sinch/domains/sms/models/v1/shared/text_request.py index 2416633b..e5ecce0f 100644 --- a/sinch/domains/sms/models/v1/shared/text_request.py +++ b/sinch/domains/sms/models/v1/shared/text_request.py @@ -42,8 +42,9 @@ class TextRequest(BaseModelConfigurationRequest): default=None, description="If set, the system will stop trying to deliver the message at this point. Must be after `send_at`. Default and max is 3 days after `send_at`. Formatted as [ISO-8601](https://en.wikipedia.org/wiki/ISO_8601): `YYYY-MM-DDThh:mm:ss.SSSZ`.", ) - callback_url: Optional[StrictStr] = Field( + event_destination_target: Optional[StrictStr] = Field( default=None, + alias="callback_url", description="Override the *default* callback URL for this batch. Must be a valid URL. Learn how to set a default callback URL [here](https://community.sinch.com/t5/SMS/How-do-I-assign-a-callback-URL-to-an-SMS-service-plan/ta-p/8414).", ) client_reference: Optional[StrictStr] = Field( diff --git a/sinch/domains/sms/models/v1/shared/text_response.py b/sinch/domains/sms/models/v1/shared/text_response.py index 650906e5..f5fc5975 100644 --- a/sinch/domains/sms/models/v1/shared/text_response.py +++ b/sinch/domains/sms/models/v1/shared/text_response.py @@ -51,8 +51,9 @@ class TextResponse(BaseModelConfigurationResponse): default=None, description="If set, the system will stop trying to deliver the message at this point. Must be after `send_at`. Default and max is 3 days after `send_at`. Formatted as [ISO-8601](https://en.wikipedia.org/wiki/ISO_8601): `YYYY-MM-DDThh:mm:ss.SSSZ`.", ) - callback_url: Optional[StrictStr] = Field( + event_destination_target: Optional[StrictStr] = Field( default=None, + alias="callback_url", description="Override the default callback URL for this batch. Must be valid URL.", ) client_reference: Optional[StrictStr] = Field( diff --git a/sinch/domains/sms/sinch_events/v1/__init__.py b/sinch/domains/sms/sinch_events/v1/__init__.py new file mode 100644 index 00000000..522d374f --- /dev/null +++ b/sinch/domains/sms/sinch_events/v1/__init__.py @@ -0,0 +1,5 @@ +from sinch.domains.sms.sinch_events.v1.sms_sinch_event import ( + SmsSinchEvent, +) + +__all__ = ["SmsSinchEvent"] diff --git a/sinch/domains/sms/sinch_events/v1/events/__init__.py b/sinch/domains/sms/sinch_events/v1/events/__init__.py new file mode 100644 index 00000000..00aba842 --- /dev/null +++ b/sinch/domains/sms/sinch_events/v1/events/__init__.py @@ -0,0 +1,17 @@ +from sinch.domains.sms.sinch_events.v1.events.sms_sinch_event import ( + IncomingSMSSinchEvent, + MOTextSinchEvent, + MOBinarySinchEvent, + MOMediaSinchEvent, + MediaBody, + MediaItem, +) + +__all__ = [ + "IncomingSMSSinchEvent", + "MOTextSinchEvent", + "MOBinarySinchEvent", + "MOMediaSinchEvent", + "MediaBody", + "MediaItem", +] diff --git a/sinch/domains/sms/webhooks/v1/events/sms_webhooks_event.py b/sinch/domains/sms/sinch_events/v1/events/sms_sinch_event.py similarity index 84% rename from sinch/domains/sms/webhooks/v1/events/sms_webhooks_event.py rename to sinch/domains/sms/sinch_events/v1/events/sms_sinch_event.py index f8abe3cb..fc87e608 100644 --- a/sinch/domains/sms/webhooks/v1/events/sms_webhooks_event.py +++ b/sinch/domains/sms/sinch_events/v1/events/sms_sinch_event.py @@ -1,10 +1,10 @@ from datetime import datetime from typing import Optional, Union, Literal, Annotated from pydantic import Field, StrictStr, StrictInt, conlist -from sinch.domains.sms.webhooks.v1.internal import WebhookEvent +from sinch.domains.sms.sinch_events.v1.internal import SinchEvent -class MediaItem(WebhookEvent): +class MediaItem(SinchEvent): url: StrictStr = Field(..., description="URL to the media file") content_type: StrictStr = Field( ..., description="Content type of the media file" @@ -15,7 +15,7 @@ class MediaItem(WebhookEvent): code: StrictInt = Field(..., description="Status code") -class MediaBody(WebhookEvent): +class MediaBody(SinchEvent): subject: Optional[StrictStr] = Field( default=None, description="The subject text" ) @@ -25,7 +25,7 @@ class MediaBody(WebhookEvent): media: conlist(MediaItem) = Field(..., description="Array of media items") -class BaseIncomingSMSWebhookEvent(WebhookEvent): +class BaseIncomingSMSSinchEvent(SinchEvent): from_: StrictStr = Field( ..., alias="from", @@ -54,7 +54,7 @@ class BaseIncomingSMSWebhookEvent(WebhookEvent): ) -class MOTextWebhookEvent(BaseIncomingSMSWebhookEvent): +class MOTextSinchEvent(BaseIncomingSMSSinchEvent): body: StrictStr = Field( ..., description="The incoming message body. Maximum 2000 characters.", @@ -64,7 +64,7 @@ class MOTextWebhookEvent(BaseIncomingSMSWebhookEvent): ) -class MOBinaryWebhookEvent(BaseIncomingSMSWebhookEvent): +class MOBinarySinchEvent(BaseIncomingSMSSinchEvent): body: StrictStr = Field( ..., description="The incoming message body (Base64 encoded)." ) @@ -76,7 +76,7 @@ class MOBinaryWebhookEvent(BaseIncomingSMSWebhookEvent): ) -class MOMediaWebhookEvent(BaseIncomingSMSWebhookEvent): +class MOMediaSinchEvent(BaseIncomingSMSSinchEvent): body: MediaBody = Field( ..., description="The media message body containing subject, message, and media items.", @@ -87,11 +87,11 @@ class MOMediaWebhookEvent(BaseIncomingSMSWebhookEvent): # Union type for isinstance checks -_IncomingSMSWebhookEventUnion = Union[ - MOTextWebhookEvent, MOBinaryWebhookEvent, MOMediaWebhookEvent +_IncomingSMSSinchEventUnion = Union[ + MOTextSinchEvent, MOBinarySinchEvent, MOMediaSinchEvent ] # Discriminated union for validation -IncomingSMSWebhookEvent = Annotated[ - _IncomingSMSWebhookEventUnion, Field(discriminator="type") +IncomingSMSSinchEvent = Annotated[ + _IncomingSMSSinchEventUnion, Field(discriminator="type") ] diff --git a/sinch/domains/sms/sinch_events/v1/internal/__init__.py b/sinch/domains/sms/sinch_events/v1/internal/__init__.py new file mode 100644 index 00000000..43b3a8dd --- /dev/null +++ b/sinch/domains/sms/sinch_events/v1/internal/__init__.py @@ -0,0 +1,5 @@ +from sinch.domains.sms.sinch_events.v1.internal.sinch_event import ( + SinchEvent, +) + +__all__ = ["SinchEvent"] diff --git a/sinch/domains/sms/webhooks/v1/internal/webhook_event.py b/sinch/domains/sms/sinch_events/v1/internal/sinch_event.py similarity index 66% rename from sinch/domains/sms/webhooks/v1/internal/webhook_event.py rename to sinch/domains/sms/sinch_events/v1/internal/sinch_event.py index 0d2857ed..184012f9 100644 --- a/sinch/domains/sms/webhooks/v1/internal/webhook_event.py +++ b/sinch/domains/sms/sinch_events/v1/internal/sinch_event.py @@ -3,5 +3,5 @@ ) -class WebhookEvent(BaseModelConfigurationResponse): +class SinchEvent(BaseModelConfigurationResponse): pass diff --git a/sinch/domains/sms/webhooks/v1/sms_webhooks.py b/sinch/domains/sms/sinch_events/v1/sms_sinch_event.py similarity index 89% rename from sinch/domains/sms/webhooks/v1/sms_webhooks.py rename to sinch/domains/sms/sinch_events/v1/sms_sinch_event.py index c64dde6e..8f8daee9 100644 --- a/sinch/domains/sms/webhooks/v1/sms_webhooks.py +++ b/sinch/domains/sms/sinch_events/v1/sms_sinch_event.py @@ -9,11 +9,11 @@ parse_json, normalize_iso_timestamp, ) -from sinch.domains.sms.webhooks.v1.events import ( - IncomingSMSWebhookEvent, - MOTextWebhookEvent, - MOBinaryWebhookEvent, - MOMediaWebhookEvent, +from sinch.domains.sms.sinch_events.v1.events import ( + IncomingSMSSinchEvent, + MOTextSinchEvent, + MOBinarySinchEvent, + MOMediaSinchEvent, ) from sinch.domains.sms.models.v1.response import ( BatchDeliveryReport, @@ -21,16 +21,16 @@ ) -SmsCallback = Union[ +SmsSinchEventPayload = Union[ BatchDeliveryReport, RecipientDeliveryReport, - MOTextWebhookEvent, - MOBinaryWebhookEvent, - MOMediaWebhookEvent, + MOTextSinchEvent, + MOBinarySinchEvent, + MOMediaSinchEvent, ] -class SmsWebhooks: +class SmsSinchEvent: def __init__(self, app_secret: Optional[str] = None): self.app_secret = app_secret @@ -64,7 +64,7 @@ def parse_event( self, event_body: Union[str, bytes, Dict[str, Any]], headers: Optional[Dict[str, str]] = None, - ) -> SmsCallback: + ) -> SmsSinchEventPayload: """ Parse the event payload into an SMS callback object. @@ -75,8 +75,8 @@ def parse_event( :type event_body: Union[str, bytes, Dict[str, Any]] :param headers: Request headers (used to decode charset when event_body is bytes). :type headers: Optional[Dict[str, str]] - :returns: A parsed SMS callback object. - :rtype: SmsCallback + :returns: A parsed SMS Sinch Event payload object. + :rtype: SmsSinchEventPayload :raises ValueError: If the event type is unknown or parsing fails. """ if isinstance(event_body, bytes): @@ -114,7 +114,7 @@ def parse_event( event_body["sent_at"] ) - adapter = TypeAdapter(IncomingSMSWebhookEvent) + adapter = TypeAdapter(IncomingSMSSinchEvent) return adapter.validate_python(event_body) raise ValueError(f"Unknown SMS event type: {event_type}") diff --git a/sinch/domains/sms/sms.py b/sinch/domains/sms/sms.py index 3c5c3ff3..c312c2de 100644 --- a/sinch/domains/sms/sms.py +++ b/sinch/domains/sms/sms.py @@ -2,7 +2,7 @@ Batches, DeliveryReports, ) -from sinch.domains.sms.webhooks.v1.sms_webhooks import SmsWebhooks +from sinch.domains.sms.sinch_events.v1.sms_sinch_event import SmsSinchEvent class SMS: @@ -17,13 +17,13 @@ def __init__(self, sinch): self.batches = Batches(self._sinch) self.delivery_reports = DeliveryReports(self._sinch) - def webhooks(self, callback_secret: str) -> SmsWebhooks: + def sinch_events(self, sinch_event_secret: str) -> SmsSinchEvent: """ - Create an SMS webhooks handler with the specified callback secret. + Create an SMS Sinch Events handler with the specified Sinch Event secret. - :param callback_secret: Secret used for webhook validation. - :type callback_secret: str - :returns: A configured webhooks handler - :rtype: SmsWebhooks + :param sinch_event_secret: Secret used for Sinch Event validation. + :type sinch_event_secret: str + :returns: A configured Sinch Events handler + :rtype: SmsSinchEvent """ - return SmsWebhooks(callback_secret) + return SmsSinchEvent(sinch_event_secret) diff --git a/sinch/domains/sms/webhooks/v1/events/__init__.py b/sinch/domains/sms/webhooks/v1/events/__init__.py deleted file mode 100644 index bb5a10da..00000000 --- a/sinch/domains/sms/webhooks/v1/events/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -from sinch.domains.sms.webhooks.v1.events.sms_webhooks_event import ( - IncomingSMSWebhookEvent, - MOTextWebhookEvent, - MOBinaryWebhookEvent, - MOMediaWebhookEvent, - MediaBody, - MediaItem, -) - -__all__ = [ - "IncomingSMSWebhookEvent", - "MOTextWebhookEvent", - "MOBinaryWebhookEvent", - "MOMediaWebhookEvent", - "MediaBody", - "MediaItem", -] diff --git a/sinch/domains/sms/webhooks/v1/internal/__init__.py b/sinch/domains/sms/webhooks/v1/internal/__init__.py deleted file mode 100644 index 329bdf65..00000000 --- a/sinch/domains/sms/webhooks/v1/internal/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from sinch.domains.sms.webhooks.v1.internal.webhook_event import ( - WebhookEvent, -) - -__all__ = ["WebhookEvent"] diff --git a/tests/e2e/sms/features/steps/webhooks.steps.py b/tests/e2e/sms/features/steps/webhooks.steps.py index 096ae9cd..99907fc4 100644 --- a/tests/e2e/sms/features/steps/webhooks.steps.py +++ b/tests/e2e/sms/features/steps/webhooks.steps.py @@ -1,9 +1,9 @@ import requests from datetime import datetime, timezone from behave import given, when, then -from sinch.domains.sms.webhooks.v1.sms_webhooks import SmsWebhooks -from sinch.domains.sms.webhooks.v1.events import ( - MOTextWebhookEvent, +from sinch.domains.sms.sinch_events.v1.sms_sinch_event import SmsSinchEvent +from sinch.domains.sms.sinch_events.v1.events import ( + MOTextSinchEvent, ) from sinch.domains.sms.models.v1.response import ( BatchDeliveryReport, @@ -11,32 +11,32 @@ ) from tests.e2e.helpers import store_webhook_response -SINCH_SMS_CALLBACK_SECRET = 'KayakingTheSwell' +SINCH_SMS_SINCH_EVENT_SECRET = 'KayakingTheSwell' @given('the SMS Webhooks handler is available') def step_webhook_handler_is_available(context): - context.sms_webhook = SmsWebhooks(SINCH_SMS_CALLBACK_SECRET) + context.sms_sinch_event = SmsSinchEvent(SINCH_SMS_SINCH_EVENT_SECRET) @when('I send a request to trigger an "incoming SMS" event') def step_send_incoming_sms_event(context): response = requests.get('http://localhost:3017/webhooks/sms/incoming-sms') store_webhook_response(context, response) - context.event = context.sms_webhook.parse_event(context.raw_event) + context.event = context.sms_sinch_event.parse_event(context.raw_event) @then('the header of the event "{event_type}" contains a valid signature') @then('the header of the event "{event_type}" with the status "{status}" contains a valid signature') def step_check_valid_signature(context, event_type, status=None): - assert context.sms_webhook.validate_authentication_header( + assert context.sms_sinch_event.validate_authentication_header( context.webhook_headers, context.raw_event ), 'Signature validation failed' @then('the SMS event describes an "incoming SMS" event') def step_check_incoming_sms_event(context): - incoming_sms_event: MOTextWebhookEvent = context.event + incoming_sms_event: MOTextSinchEvent = context.event assert incoming_sms_event.id == '01W4FFL35P4NC4K35SMSBATCH8' assert incoming_sms_event.from_ == '12015555555' assert incoming_sms_event.to == '12017777777' @@ -51,7 +51,7 @@ def step_check_incoming_sms_event(context): def step_send_delivery_report_event(context): response = requests.get('http://localhost:3017/webhooks/sms/delivery-report-sms') store_webhook_response(context, response) - context.event = context.sms_webhook.parse_event(context.raw_event) + context.event = context.sms_sinch_event.parse_event(context.raw_event) @then('the SMS event describes an "SMS delivery report" event') @@ -79,7 +79,7 @@ def step_send_recipient_delivery_report_event_delivered(context): 'http://localhost:3017/webhooks/sms/recipient-delivery-report-sms-delivered' ) store_webhook_response(context, response) - context.event = context.sms_webhook.parse_event(context.raw_event) + context.event = context.sms_sinch_event.parse_event(context.raw_event) @when('I send a request to trigger an "SMS recipient delivery report" event with the status "Aborted"') @@ -88,7 +88,7 @@ def step_send_recipient_delivery_report_event_aborted(context): 'http://localhost:3017/webhooks/sms/recipient-delivery-report-sms-aborted' ) store_webhook_response(context, response) - context.event = context.sms_webhook.parse_event(context.raw_event) + context.event = context.sms_sinch_event.parse_event(context.raw_event) @then('the SMS event describes an SMS recipient delivery report event with the status "Delivered"') diff --git a/tests/unit/domains/sms/v1/endpoints/batches/test_send_batches_endpoint.py b/tests/unit/domains/sms/v1/endpoints/batches/test_send_batches_endpoint.py index f20303b1..41aee1f7 100644 --- a/tests/unit/domains/sms/v1/endpoints/batches/test_send_batches_endpoint.py +++ b/tests/unit/domains/sms/v1/endpoints/batches/test_send_batches_endpoint.py @@ -92,6 +92,21 @@ def test_request_body_expects_text_request_data(text_request_data): assert body["body"] == "Your verification code is 123456" +def test_request_body_uses_callback_url_alias_for_event_destination_target(): + """Ensure event_destination_target is serialized as backend callback_url.""" + request = TextRequest( + to=["+46701234567"], + from_="+46701111111", + body="Hello", + event_destination_target="https://example.com/callback", + ) + endpoint = SendSMSEndpoint("test_project_id", request) + body = json.loads(endpoint.request_body()) + + assert body["callback_url"] == "https://example.com/callback" + assert "event_destination_target" not in body + + def test_request_body_expects_binary_request_data(binary_request_data): """Test that binary request body contains correct fields.""" endpoint = SendSMSEndpoint("test_project_id", binary_request_data) diff --git a/tests/unit/domains/sms/v1/models/internal/test_dry_run_request_model.py b/tests/unit/domains/sms/v1/models/internal/test_dry_run_request_model.py index c308af5a..a3560f01 100644 --- a/tests/unit/domains/sms/v1/models/internal/test_dry_run_request_model.py +++ b/tests/unit/domains/sms/v1/models/internal/test_dry_run_request_model.py @@ -119,7 +119,7 @@ def test_dry_run_text_request_expects_valid_inputs_and_all_fields( delivery_report="summary", send_at=send_at, expire_at=expire_at, - callback_url="https://capybara.com/callback", + event_destination_target="https://capybara.com/callback", client_reference="test-ref", feedback_enabled=True, flash_message=False, @@ -134,7 +134,7 @@ def test_dry_run_text_request_expects_valid_inputs_and_all_fields( assert request.delivery_report == "summary" assert request.send_at == send_at assert request.expire_at == expire_at - assert request.callback_url == "https://capybara.com/callback" + assert request.event_destination_target == "https://capybara.com/callback" assert request.client_reference == "test-ref" assert request.feedback_enabled is True assert request.flash_message is False @@ -191,7 +191,7 @@ def test_dry_run_binary_request_expects_valid_inputs_and_all_fields( delivery_report="full", send_at=send_at, expire_at=expire_at, - callback_url="https://capybara.com/callback", + event_destination_target="https://capybara.com/callback", client_reference="binary-ref", feedback_enabled=False, from_ton=0, @@ -203,7 +203,7 @@ def test_dry_run_binary_request_expects_valid_inputs_and_all_fields( assert request.delivery_report == "full" assert request.send_at == send_at assert request.expire_at == expire_at - assert request.callback_url == "https://capybara.com/callback" + assert request.event_destination_target == "https://capybara.com/callback" assert request.client_reference == "binary-ref" assert request.feedback_enabled is False assert request.from_ton == 0 @@ -256,7 +256,7 @@ def test_dry_run_media_request_expects_valid_inputs_and_all_fields( delivery_report="summary", send_at=send_at, expire_at=expire_at, - callback_url="https://capybara.com/callback", + event_destination_target="https://capybara.com/callback", client_reference="media-ref", feedback_enabled=True, ) @@ -266,7 +266,7 @@ def test_dry_run_media_request_expects_valid_inputs_and_all_fields( assert request.delivery_report == "summary" assert request.send_at == send_at assert request.expire_at == expire_at - assert request.callback_url == "https://capybara.com/callback" + assert request.event_destination_target == "https://capybara.com/callback" assert request.client_reference == "media-ref" assert request.feedback_enabled is True diff --git a/tests/unit/domains/sms/v1/models/internal/test_replace_binary_request_model.py b/tests/unit/domains/sms/v1/models/internal/test_replace_binary_request_model.py index 7e632deb..10eded9c 100644 --- a/tests/unit/domains/sms/v1/models/internal/test_replace_binary_request_model.py +++ b/tests/unit/domains/sms/v1/models/internal/test_replace_binary_request_model.py @@ -39,7 +39,7 @@ def test_replace_binary_request_expects_valid_inputs_and_all_fields( delivery_report="summary", send_at=send_at, expire_at=expire_at, - callback_url="https://capybara.com/callback", + event_destination_target="https://capybara.com/callback", client_reference="test-ref", feedback_enabled=True, from_ton=1, @@ -49,7 +49,7 @@ def test_replace_binary_request_expects_valid_inputs_and_all_fields( assert request.delivery_report == "summary" assert request.send_at == send_at assert request.expire_at == expire_at - assert request.callback_url == "https://capybara.com/callback" + assert request.event_destination_target == "https://capybara.com/callback" assert request.client_reference == "test-ref" assert request.feedback_enabled is True assert request.from_ton == 1 diff --git a/tests/unit/domains/sms/v1/models/internal/test_replace_media_request_model.py b/tests/unit/domains/sms/v1/models/internal/test_replace_media_request_model.py index 49b3b9a5..6b8b30e7 100644 --- a/tests/unit/domains/sms/v1/models/internal/test_replace_media_request_model.py +++ b/tests/unit/domains/sms/v1/models/internal/test_replace_media_request_model.py @@ -49,7 +49,7 @@ def test_replace_media_request_expects_valid_inputs_and_all_fields( delivery_report="full", send_at=send_at, expire_at=expire_at, - callback_url="https://capybara.com/webhook", + event_destination_target="https://capybara.com/webhook", client_reference="capybara-media-batch-123", feedback_enabled=True, strict_validation=True, @@ -62,7 +62,7 @@ def test_replace_media_request_expects_valid_inputs_and_all_fields( assert request.delivery_report == "full" assert request.send_at == send_at assert request.expire_at == expire_at - assert request.callback_url == "https://capybara.com/webhook" + assert request.event_destination_target == "https://capybara.com/webhook" assert request.client_reference == "capybara-media-batch-123" assert request.feedback_enabled is True assert request.strict_validation is True diff --git a/tests/unit/domains/sms/v1/models/internal/test_update_binary_request_with_batch_id_model.py b/tests/unit/domains/sms/v1/models/internal/test_update_binary_request_with_batch_id_model.py index 5587ee7b..90d95c38 100644 --- a/tests/unit/domains/sms/v1/models/internal/test_update_binary_request_with_batch_id_model.py +++ b/tests/unit/domains/sms/v1/models/internal/test_update_binary_request_with_batch_id_model.py @@ -37,7 +37,7 @@ def test_update_binary_request_expects_valid_inputs_and_all_fields( delivery_report="full", send_at=send_at, expire_at=expire_at, - callback_url="https://capybara.com/binary-callback", + event_destination_target="https://capybara.com/binary-callback", client_reference="binary-update-456", feedback_enabled=True, from_ton=3, @@ -52,7 +52,7 @@ def test_update_binary_request_expects_valid_inputs_and_all_fields( assert request.delivery_report == "full" assert request.send_at == send_at assert request.expire_at == expire_at - assert request.callback_url == "https://capybara.com/binary-callback" + assert request.event_destination_target == "https://capybara.com/binary-callback" assert request.client_reference == "binary-update-456" assert request.feedback_enabled is True assert request.from_ton == 3 diff --git a/tests/unit/domains/sms/v1/models/internal/test_update_media_request_with_batch_id_model.py b/tests/unit/domains/sms/v1/models/internal/test_update_media_request_with_batch_id_model.py index fe6b3700..06dda0de 100644 --- a/tests/unit/domains/sms/v1/models/internal/test_update_media_request_with_batch_id_model.py +++ b/tests/unit/domains/sms/v1/models/internal/test_update_media_request_with_batch_id_model.py @@ -46,7 +46,7 @@ def test_update_media_request_expects_valid_inputs_and_all_fields( delivery_report="none", send_at=send_at, expire_at=expire_at, - callback_url="https://capybara.com/media-callback", + event_destination_target="https://capybara.com/media-callback", client_reference="media-update-789", feedback_enabled=True, strict_validation=True, @@ -62,7 +62,7 @@ def test_update_media_request_expects_valid_inputs_and_all_fields( assert request.delivery_report == "none" assert request.send_at == send_at assert request.expire_at == expire_at - assert request.callback_url == "https://capybara.com/media-callback" + assert request.event_destination_target == "https://capybara.com/media-callback" assert request.client_reference == "media-update-789" assert request.feedback_enabled is True assert request.strict_validation is True diff --git a/tests/unit/domains/sms/v1/models/internal/test_update_text_request_with_batch_id_model.py b/tests/unit/domains/sms/v1/models/internal/test_update_text_request_with_batch_id_model.py index 8737571c..23df2edb 100644 --- a/tests/unit/domains/sms/v1/models/internal/test_update_text_request_with_batch_id_model.py +++ b/tests/unit/domains/sms/v1/models/internal/test_update_text_request_with_batch_id_model.py @@ -35,7 +35,7 @@ def test_update_text_request_expects_valid_inputs_and_all_fields( delivery_report="summary", send_at=send_at, expire_at=expire_at, - callback_url="https://capybara.com/webhook", + event_destination_target="https://capybara.com/webhook", client_reference="update-ref-123", feedback_enabled=True, flash_message=True, @@ -54,7 +54,7 @@ def test_update_text_request_expects_valid_inputs_and_all_fields( assert request.delivery_report == "summary" assert request.send_at == send_at assert request.expire_at == expire_at - assert request.callback_url == "https://capybara.com/webhook" + assert request.event_destination_target == "https://capybara.com/webhook" assert request.client_reference == "update-ref-123" assert request.feedback_enabled is True assert request.flash_message is True diff --git a/tests/unit/domains/sms/v1/test_batches.py b/tests/unit/domains/sms/v1/test_batches.py index 23064769..a95c3d64 100644 --- a/tests/unit/domains/sms/v1/test_batches.py +++ b/tests/unit/domains/sms/v1/test_batches.py @@ -193,7 +193,7 @@ def test_batches_send_sms_expects_correct_request( delivery_report="full", send_at=datetime(2024, 6, 6, 9, 25, 0, tzinfo=timezone.utc), expire_at=datetime(2024, 6, 10, 9, 25, 0, tzinfo=timezone.utc), - callback_url="https://example.com/callback", + event_destination_target="https://example.com/callback", client_reference="test-ref", feedback_enabled=True, flash_message=False, @@ -262,7 +262,7 @@ def test_batches_send_binary_expects_correct_request( delivery_report="summary", send_at=datetime(2024, 6, 6, 9, 25, 0, tzinfo=timezone.utc), expire_at=datetime(2024, 6, 10, 9, 25, 0, tzinfo=timezone.utc), - callback_url="https://example.com/callback", + event_destination_target="https://example.com/callback", client_reference="test-ref", feedback_enabled=True, from_ton=1, @@ -330,7 +330,7 @@ def test_batches_send_mms_expects_correct_request( delivery_report="full", send_at=datetime(2024, 6, 6, 9, 25, 0, tzinfo=timezone.utc), expire_at=datetime(2024, 6, 10, 9, 25, 0, tzinfo=timezone.utc), - callback_url="https://example.com/callback", + event_destination_target="https://example.com/callback", client_reference="test-ref", feedback_enabled=True, strict_validation=True,