From 5747de5ef36f0e8bd017231f4416a4e612600ed4 Mon Sep 17 00:00:00 2001 From: maxkahan Date: Thu, 8 May 2025 15:00:32 +0100 Subject: [PATCH 1/8] add new response field --- messages/src/vonage_messages/responses.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/messages/src/vonage_messages/responses.py b/messages/src/vonage_messages/responses.py index 59a84b50..9806d6ce 100644 --- a/messages/src/vonage_messages/responses.py +++ b/messages/src/vonage_messages/responses.py @@ -1,3 +1,4 @@ +from typing import Optional from pydantic import BaseModel @@ -9,3 +10,4 @@ class SendMessageResponse(BaseModel): """ message_uuid: str + workflow_id: Optional[str] = None From 17f8d4dee6aea1233c5361d893c6c43021fb5b78 Mon Sep 17 00:00:00 2001 From: maxkahan Date: Thu, 8 May 2025 15:35:53 +0100 Subject: [PATCH 2/8] add failover parameter --- messages/src/vonage_messages/messages.py | 18 ++++++++++++++++-- .../src/vonage_messages/models/base_message.py | 1 - pants.toml | 2 +- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/messages/src/vonage_messages/messages.py b/messages/src/vonage_messages/messages.py index 3fd62f65..f1259438 100644 --- a/messages/src/vonage_messages/messages.py +++ b/messages/src/vonage_messages/messages.py @@ -31,21 +31,35 @@ def http_client(self) -> HttpClient: return self._http_client @validate_call - def send(self, message: BaseMessage) -> SendMessageResponse: + def send( + self, message: BaseMessage, failover: list[BaseMessage] = None + ) -> SendMessageResponse: """Send a message using Vonage's Messages API. Args: message (BaseMessage): The message to be sent as a Pydantic model. Use the provided models (in `vonage_messages.models`) to create messages and pass them in to this method. + failover (list[BaseMessage]): A list of failover messages to be attempted if the primary message fails. Returns: SendMessageResponse: Response model containing the unique identifier of the sent message. Access the identifier with the `message_uuid` attribute. """ + body = message.model_dump(by_alias=True, exclude_none=True) or message + + if failover is not None: + failover_body = [ + m.model_dump(by_alias=True, exclude_none=True) or m for m in failover + ] + body = { + 'message': body, + 'failover': failover_body, + } + response = self._http_client.post( self._http_client.api_host, '/v1/messages', - message.model_dump(by_alias=True, exclude_none=True) or message, + body, self._auth_type, ) diff --git a/messages/src/vonage_messages/models/base_message.py b/messages/src/vonage_messages/models/base_message.py index 25959d77..478e7740 100644 --- a/messages/src/vonage_messages/models/base_message.py +++ b/messages/src/vonage_messages/models/base_message.py @@ -2,7 +2,6 @@ from pydantic import BaseModel, Field from vonage_utils.types import PhoneNumber - from .enums import WebhookVersion diff --git a/pants.toml b/pants.toml index fb8de4e5..730bfed9 100644 --- a/pants.toml +++ b/pants.toml @@ -1,5 +1,5 @@ [GLOBAL] -pants_version = '2.24.2' +pants_version = '2.26.0' backend_packages = [ 'pants.backend.python', From 61040a4de375e1dc3e54a413b51e2ce89f3e6a06 Mon Sep 17 00:00:00 2001 From: maxkahan Date: Thu, 8 May 2025 15:39:01 +0100 Subject: [PATCH 3/8] formatting --- messages/src/vonage_messages/models/base_message.py | 1 + messages/src/vonage_messages/responses.py | 1 + network_auth/src/vonage_network_auth/requests.py | 6 +++--- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/messages/src/vonage_messages/models/base_message.py b/messages/src/vonage_messages/models/base_message.py index 478e7740..25959d77 100644 --- a/messages/src/vonage_messages/models/base_message.py +++ b/messages/src/vonage_messages/models/base_message.py @@ -2,6 +2,7 @@ from pydantic import BaseModel, Field from vonage_utils.types import PhoneNumber + from .enums import WebhookVersion diff --git a/messages/src/vonage_messages/responses.py b/messages/src/vonage_messages/responses.py index 9806d6ce..aa7eafe7 100644 --- a/messages/src/vonage_messages/responses.py +++ b/messages/src/vonage_messages/responses.py @@ -1,4 +1,5 @@ from typing import Optional + from pydantic import BaseModel diff --git a/network_auth/src/vonage_network_auth/requests.py b/network_auth/src/vonage_network_auth/requests.py index e7452a11..b4e5f476 100644 --- a/network_auth/src/vonage_network_auth/requests.py +++ b/network_auth/src/vonage_network_auth/requests.py @@ -15,6 +15,6 @@ class CreateOidcUrl(BaseModel): redirect_uri: str state: str login_hint: str - scope: Optional[ - str - ] = 'openid dpv:FraudPreventionAndDetection#number-verification-verify-read' + scope: Optional[str] = ( + 'openid dpv:FraudPreventionAndDetection#number-verification-verify-read' + ) From 4f0305aa6fa7ec34abfc00fcc872b5804c986295 Mon Sep 17 00:00:00 2001 From: maxkahan Date: Fri, 9 May 2025 15:57:51 +0100 Subject: [PATCH 4/8] add failover testing --- messages/src/vonage_messages/responses.py | 1 + .../data/send_message_with_failover.json | 4 ++ messages/tests/test_messages.py | 46 +++++++++++++++++++ 3 files changed, 51 insertions(+) create mode 100644 messages/tests/data/send_message_with_failover.json diff --git a/messages/src/vonage_messages/responses.py b/messages/src/vonage_messages/responses.py index aa7eafe7..99e594ed 100644 --- a/messages/src/vonage_messages/responses.py +++ b/messages/src/vonage_messages/responses.py @@ -8,6 +8,7 @@ class SendMessageResponse(BaseModel): Attributes: message_uuid (str): The UUID of the sent message. + workflow_id [str]: Workflow ID if the `failover` parameter was used in the request. """ message_uuid: str diff --git a/messages/tests/data/send_message_with_failover.json b/messages/tests/data/send_message_with_failover.json new file mode 100644 index 00000000..7a8a12be --- /dev/null +++ b/messages/tests/data/send_message_with_failover.json @@ -0,0 +1,4 @@ +{ + "message_uuid": "d8f86df1-dec6-442f-870a-2241be27d721", + "workflow_id": "3TcNjguHxr2vcCZ9Ddsnq6tw8yQUpZ9rMHv9QXSxLan5ibMxqSzLdx9" +} \ No newline at end of file diff --git a/messages/tests/test_messages.py b/messages/tests/test_messages.py index 51c8767b..65230e26 100644 --- a/messages/tests/test_messages.py +++ b/messages/tests/test_messages.py @@ -1,3 +1,4 @@ +from json import loads from os.path import abspath import responses @@ -51,6 +52,51 @@ def test_send_message(): assert messages._auth_type == 'jwt' +@responses.activate +def test_send_message_with_failover(): + build_response( + path, + 'POST', + 'https://api.nexmo.com/v1/messages', + 'send_message_with_failover.json', + 202, + ) + sms = Sms( + from_='Vonage APIs', + to='1234567890', + text='Hello, World!', + ) + failover = [ + Sms(from_='Vonage APIs', to='1987654321', text='Failover message'), + ] + + response = messages.send(sms, failover=failover) + print(messages._http_client.last_request.body) + assert loads(messages._http_client.last_request.body) == { + "message": { + "to": "1234567890", + "from": "Vonage APIs", + "text": "Hello, World!", + "channel": "sms", + "message_type": "text", + }, + "failover": [ + { + "to": "1987654321", + "from": "Vonage APIs", + "text": "Failover message", + "channel": "sms", + "message_type": "text", + } + ], + } + assert response.message_uuid == 'd8f86df1-dec6-442f-870a-2241be27d721' + assert ( + response.workflow_id == '3TcNjguHxr2vcCZ9Ddsnq6tw8yQUpZ9rMHv9QXSxLan5ibMxqSzLdx9' + ) + assert messages._auth_type == 'jwt' + + @responses.activate def test_send_message_basic_auth(): build_response( From 5b08b01ed7b25c09ef9132a58a4c854840356bd2 Mon Sep 17 00:00:00 2001 From: maxkahan Date: Thu, 15 May 2025 02:55:46 +0100 Subject: [PATCH 5/8] change payload --- messages/src/vonage_messages/messages.py | 2 +- messages/src/vonage_messages/models/rcs.py | 2 +- vonage_utils/src/vonage_utils/_version.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/messages/src/vonage_messages/messages.py b/messages/src/vonage_messages/messages.py index f1259438..6ba05b98 100644 --- a/messages/src/vonage_messages/messages.py +++ b/messages/src/vonage_messages/messages.py @@ -52,7 +52,7 @@ def send( m.model_dump(by_alias=True, exclude_none=True) or m for m in failover ] body = { - 'message': body, + **body, 'failover': failover_body, } diff --git a/messages/src/vonage_messages/models/rcs.py b/messages/src/vonage_messages/models/rcs.py index ef856061..b0e98a1a 100644 --- a/messages/src/vonage_messages/models/rcs.py +++ b/messages/src/vonage_messages/models/rcs.py @@ -30,7 +30,7 @@ class BaseRcs(BaseMessage): """ to: PhoneNumber - from_: str = Field(..., serialization_alias='from', pattern='^[a-zA-Z0-9]+$') + from_: str = Field(..., serialization_alias='from', pattern='^[a-zA-Z0-9-_]+$') ttl: Optional[int] = Field(None, ge=300, le=259200) channel: ChannelType = ChannelType.RCS diff --git a/vonage_utils/src/vonage_utils/_version.py b/vonage_utils/src/vonage_utils/_version.py index 890b8582..bc50bee6 100644 --- a/vonage_utils/src/vonage_utils/_version.py +++ b/vonage_utils/src/vonage_utils/_version.py @@ -1 +1 @@ -__version__ = '1.1.4dev2' +__version__ = '1.1.4' From 971b3af16109402dd5a0ba6b88076f86f154793d Mon Sep 17 00:00:00 2001 From: maxkahan Date: Thu, 15 May 2025 03:00:19 +0100 Subject: [PATCH 6/8] fix tests --- messages/tests/test_messages.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/messages/tests/test_messages.py b/messages/tests/test_messages.py index 65230e26..5cde1bc0 100644 --- a/messages/tests/test_messages.py +++ b/messages/tests/test_messages.py @@ -73,13 +73,11 @@ def test_send_message_with_failover(): response = messages.send(sms, failover=failover) print(messages._http_client.last_request.body) assert loads(messages._http_client.last_request.body) == { - "message": { - "to": "1234567890", - "from": "Vonage APIs", - "text": "Hello, World!", - "channel": "sms", - "message_type": "text", - }, + "to": "1234567890", + "from": "Vonage APIs", + "text": "Hello, World!", + "channel": "sms", + "message_type": "text", "failover": [ { "to": "1987654321", From 65b7aa52e645427fb9efb7939e62af97b307c9df Mon Sep 17 00:00:00 2001 From: maxkahan Date: Fri, 16 May 2025 13:34:54 +0100 Subject: [PATCH 7/8] update messages package for release --- messages/CHANGES.md | 3 +++ messages/src/vonage_messages/_version.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/messages/CHANGES.md b/messages/CHANGES.md index 7183d872..45cf1396 100644 --- a/messages/CHANGES.md +++ b/messages/CHANGES.md @@ -1,3 +1,6 @@ +# 1.5.0 +- Add an optional "failover" property to `vonage_messages.Messages.send` + # 1.4.0 - Make all models originally accessed by `vonage_messages.models.***` available at the top level of the package, i.e. `vonage_messages.***` diff --git a/messages/src/vonage_messages/_version.py b/messages/src/vonage_messages/_version.py index 96e3ce8d..77f1c8e6 100644 --- a/messages/src/vonage_messages/_version.py +++ b/messages/src/vonage_messages/_version.py @@ -1 +1 @@ -__version__ = '1.4.0' +__version__ = '1.5.0' From f7a61eaa126a0976233d98af16269954192b7f01 Mon Sep 17 00:00:00 2001 From: maxkahan Date: Fri, 16 May 2025 13:58:41 +0100 Subject: [PATCH 8/8] prepare for new release --- vonage/CHANGES.md | 3 +++ vonage/pyproject.toml | 6 +++--- vonage/src/vonage/_version.py | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/vonage/CHANGES.md b/vonage/CHANGES.md index 185e6aa3..f75f9a3b 100644 --- a/vonage/CHANGES.md +++ b/vonage/CHANGES.md @@ -1,3 +1,6 @@ +# 4.5.0 +- vonage-messages: add an optional "failover" property to `vonage_messages.Messages.send` + # 4.4.3 - vonage-number-insight: use basic header auth instead of request body auth diff --git a/vonage/pyproject.toml b/vonage/pyproject.toml index c04e105f..3ac25bb2 100644 --- a/vonage/pyproject.toml +++ b/vonage/pyproject.toml @@ -5,11 +5,10 @@ description = "Python Server SDK for using Vonage APIs" readme = "README.md" requires-python = ">=3.9" dependencies = [ - "vonage-utils>=1.1.4", - "vonage-http-client>=1.5.1", "vonage-account>=1.1.1", "vonage-application>=2.0.1", - "vonage-messages>=1.4.0", + "vonage-http-client>=1.5.1", + "vonage-messages>=1.5.0", "vonage-network-auth>=1.0.2", "vonage-network-sim-swap>=1.1.2", "vonage-network-number-verification>=1.0.2", @@ -18,6 +17,7 @@ dependencies = [ "vonage-sms>=1.1.6", "vonage-subaccounts>=1.0.4", "vonage-users>=1.2.1", + "vonage-utils>=1.1.4", "vonage-verify>=2.1.0", "vonage-verify-legacy>=1.0.1", "vonage-video>=1.2.0", diff --git a/vonage/src/vonage/_version.py b/vonage/src/vonage/_version.py index 77ed0d0b..330025d8 100644 --- a/vonage/src/vonage/_version.py +++ b/vonage/src/vonage/_version.py @@ -1 +1 @@ -__version__ = '4.4.3' +__version__ = '4.5.0'