From d15310ad4b2cd90946819ed735b8804aec821fa7 Mon Sep 17 00:00:00 2001 From: superchilled Date: Fri, 23 Aug 2024 12:47:45 +0100 Subject: [PATCH 1/5] Implementing RCS channel in Messages API --- lib/vonage.rb | 1 + lib/vonage/messaging/channels/rcs.rb | 42 ++++ test/vonage/messaging/channels/rcs_test.rb | 226 ++++++++++++++++++ .../messaging/channels/whats_app_test.rb | 2 +- 4 files changed, 270 insertions(+), 1 deletion(-) create mode 100644 lib/vonage/messaging/channels/rcs.rb create mode 100644 test/vonage/messaging/channels/rcs_test.rb diff --git a/lib/vonage.rb b/lib/vonage.rb index 53e28e98..9c77ef63 100644 --- a/lib/vonage.rb +++ b/lib/vonage.rb @@ -13,6 +13,7 @@ module Vonage 'http' => 'HTTP', 'json' => 'JSON', 'jwt' => 'JWT', + 'rcs' => 'RCS', 'sip' => 'SIP', 'sms' => 'SMS', 'network_sim_swap' => 'NetworkSIMSwap', diff --git a/lib/vonage/messaging/channels/rcs.rb b/lib/vonage/messaging/channels/rcs.rb new file mode 100644 index 00000000..e473796d --- /dev/null +++ b/lib/vonage/messaging/channels/rcs.rb @@ -0,0 +1,42 @@ +# typed: true + +module Vonage + class Messaging::Channels::RCS < Messaging::Message + MESSAGE_TYPES = ['text', 'image', 'video', 'file', 'custom'] + + attr_reader :data + + def initialize(attributes = {}) + @type = attributes.fetch(:type, nil) + @message = attributes.fetch(:message, nil) + @opts = attributes.fetch(:opts, {}) + @data = {} + + after_initialize! + end + + private + + def build + data[:channel] = 'rcs' + super + end + + def verify_type + raise ClientError.new("Invalid message type") unless MESSAGE_TYPES.include?(type) + end + + def verify_message + case type + when 'text' + raise Vonage::ClientError.new("Invalid parameter type. `:message` must be a String") unless message.is_a? String + when 'custom' + raise Vonage::ClientError.new("Invalid parameter type. `:message` must be a Hash") unless message.is_a? Hash + raise Vonage::ClientError.new("Invalid parameter content. `:message` must not be empty") if message.empty? + else + raise Vonage::ClientError.new("Invalid parameter type. `:message` must be a Hash") unless message.is_a? Hash + raise Vonage::ClientError.new("Missing parameter. `:message` must contain a `:url` key") unless message[:url] + end + end + end +end diff --git a/test/vonage/messaging/channels/rcs_test.rb b/test/vonage/messaging/channels/rcs_test.rb new file mode 100644 index 00000000..fb0e8d08 --- /dev/null +++ b/test/vonage/messaging/channels/rcs_test.rb @@ -0,0 +1,226 @@ +# typed: false + + +class Vonage::Messaging::Channels::RCSTest < Vonage::Test + def test_rcs_initialize + message = Vonage::Messaging::Channels::RCS.new(type: 'text', message: 'Hello world!') + + assert_kind_of Vonage::Messaging::Channels::RCS, message + end + + def test_rcs_text_message + expected = { + channel: 'rcs', + message_type: 'text', + text: 'Hello world!' + } + + message = Vonage::Messaging::Channels::RCS.new( + type: 'text', + message: 'Hello world!' + ) + + assert_equal expected, message.data + end + + def test_rcs_text_message_wth_optional_parameters + expected = { + channel: 'rcs', + message_type: 'text', + text: 'Hello world!', + ttl: 600, + client_ref: "abc123", + webhook_url: "https://example.com/status" + } + + message = Vonage::Messaging::Channels::RCS.new( + type: 'text', + message: 'Hello world!', + opts: { + ttl: 600, + client_ref: "abc123", + webhook_url: "https://example.com/status" + } + ) + + assert_equal expected, message.data + end + + def test_rcs_image_message + expected = { + channel: 'rcs', + message_type: 'image', + image: { + url: 'https://example.com/image.jpg' + } + } + + message = Vonage::Messaging::Channels::RCS.new( + type: 'image', + message: { + url: 'https://example.com/image.jpg' + } + ) + + assert_equal expected, message.data + end + + def test_rcs_video_message + expected = { + channel: 'rcs', + message_type: 'image', + image: { + url: 'https://example.com/video.webm' + } + } + + message = Vonage::Messaging::Channels::RCS.new( + type: 'image', + message: { + url: 'https://example.com/video.webm' + } + ) + + assert_equal expected, message.data + end + + def test_rcs_file_message + expected = { + channel: 'rcs', + message_type: 'file', + file: { + url: 'https://example.com/file.pdf' + } + } + + message = Vonage::Messaging::Channels::RCS.new( + type: 'file', + message: { + url: 'https://example.com/file.pdf' + } + ) + + assert_equal expected, message.data + end + + def test_rcs_custom_message + expected = { + channel: 'rcs', + message_type: 'custom', + custom: { + contentMessage: { + text: 'Which ice-cream flavour do you prefer?', + suggestions: [ + { + reply: { + text: 'Vanilla', + postback: 'vanilla' + } + }, + { + reply: { + text: 'Chocolate', + postback: 'chocolate' + } + } + ] + } + } + } + + message = Vonage::Messaging::Channels::RCS.new( + type: 'custom', + message: { + contentMessage: { + text: 'Which ice-cream flavour do you prefer?', + suggestions: [ + { + reply: { + text: 'Vanilla', + postback: 'vanilla' + } + }, + { + reply: { + text: 'Chocolate', + postback: 'chocolate' + } + } + ] + } + } + ) + + assert_equal expected, message.data + end + + def test_rcs_invalid_message_type + exception = assert_raises { Vonage::Messaging::Channels::RCS.new(type: 'invalid', message: 'Hello world!') } + + assert_instance_of Vonage::ClientError, exception + assert_match "Invalid message type", exception.message + end + + def test_rcs_text_message_invalid_type + exception = assert_raises { Vonage::Messaging::Channels::RCS.new(type: 'text', message: 123) } + + assert_instance_of Vonage::ClientError, exception + assert_match "Invalid parameter type. `:message` must be a String", exception.message + end + + def test_rcs_image_message_invalid_type + exception = assert_raises { Vonage::Messaging::Channels::RCS.new(type: 'image', message: 'https://example.com/image.jpg') } + + assert_instance_of Vonage::ClientError, exception + assert_match "Invalid parameter type. `:message` must be a Hash", exception.message + end + + def test_rcs_image_message_missing_url + exception = assert_raises { Vonage::Messaging::Channels::RCS.new(type: 'image', message: {}) } + + assert_instance_of Vonage::ClientError, exception + assert_match "Missing parameter. `:message` must contain a `:url` key", exception.message + end + + def test_rcs_video_message_invalid_type + exception = assert_raises { Vonage::Messaging::Channels::RCS.new(type: 'video', message: 'https://example.com/video.webm') } + + assert_instance_of Vonage::ClientError, exception + assert_match "Invalid parameter type. `:message` must be a Hash", exception.message + end + + def test_rcs_video_message_missing_url + exception = assert_raises { Vonage::Messaging::Channels::RCS.new(type: 'video', message: {}) } + + assert_instance_of Vonage::ClientError, exception + assert_match "Missing parameter. `:message` must contain a `:url` key", exception.message + end + + def test_rcs_file_message_invalid_type + exception = assert_raises { Vonage::Messaging::Channels::RCS.new(type: 'file', message: 'https://example.com/file.pdf') } + + assert_instance_of Vonage::ClientError, exception + assert_match "Invalid parameter type. `:message` must be a Hash", exception.message + end + + def test_rcs_file_message_missing_url + exception = assert_raises { Vonage::Messaging::Channels::RCS.new(type: 'file', message: {}) } + + assert_instance_of Vonage::ClientError, exception + assert_match "Missing parameter. `:message` must contain a `:url` key", exception.message + end + + def test_rcs_custom_message_invalid_type + exception = assert_raises { Vonage::Messaging::Channels::RCS.new(type: 'custom', message: 'Hello world!') } + + assert_instance_of Vonage::ClientError, exception + assert_match "Invalid parameter type. `:message` must be a Hash", exception.message + end + + def test_rcs_custom_message_with_empty_message_hash + exception = assert_raises { Vonage::Messaging::Channels::RCS.new(type: 'custom', message: {}) } + + assert_instance_of Vonage::ClientError, exception + assert_match "Invalid parameter content. `:message` must not be empty", exception.message + end +end diff --git a/test/vonage/messaging/channels/whats_app_test.rb b/test/vonage/messaging/channels/whats_app_test.rb index 3c90629d..9d45bd11 100644 --- a/test/vonage/messaging/channels/whats_app_test.rb +++ b/test/vonage/messaging/channels/whats_app_test.rb @@ -2,7 +2,7 @@ class Vonage::Messaging::Channels::WhatsAppTest < Vonage::Test - def test_messenger_initialize + def test_whats_app_initialize whatsapp = Vonage::Messaging::Channels::WhatsApp.new(type: 'text', message: 'Hello world!') assert_kind_of Vonage::Messaging::Channels::WhatsApp, whatsapp From ebdec0f8c6bace8d474efc8fc88d1556c95d7d31 Mon Sep 17 00:00:00 2001 From: superchilled Date: Fri, 23 Aug 2024 15:49:11 +0100 Subject: [PATCH 2/5] Implementing Messaging update method --- lib/vonage/messaging.rb | 4 ++++ test/vonage/messaging_test.rb | 14 ++++++++++++++ test/vonage/test.rb | 4 ++++ 3 files changed, 22 insertions(+) diff --git a/lib/vonage/messaging.rb b/lib/vonage/messaging.rb index f06d76b8..9d5cdba6 100644 --- a/lib/vonage/messaging.rb +++ b/lib/vonage/messaging.rb @@ -31,6 +31,10 @@ def send(to:, from:, **message) request('/v1/messages', params: {to: to, from: from, **message}, type: Post) end + def update(message_uuid:, **params) + request("/v1/messages/#{message_uuid}", params: params, type: Patch) + end + # Validate a JSON Web Token from a Messages API Webhook. # # @param [String, required] :token The JWT from the Webhook's Authorization header diff --git a/test/vonage/messaging_test.rb b/test/vonage/messaging_test.rb index 66f70d4d..be4c09df 100644 --- a/test/vonage/messaging_test.rb +++ b/test/vonage/messaging_test.rb @@ -6,6 +6,14 @@ def messaging Vonage::Messaging.new(config) end + def geo_specific_host + 'api-eu.vonage.com' + end + + def geo_specific_messaging + Vonage::Messaging.new(config.merge(api_host: geo_specific_host)) + end + def messaging_uri 'https://api.nexmo.com/v1/messages' end @@ -65,4 +73,10 @@ def test_verify_webhook_token_method_with_invalid_secret def test_verify_webhook_token_method_with_no_token assert_raises(ArgumentError) { messaging.verify_webhook_token } end + + def test_update_method + stub_request(:patch, 'https://' + geo_specific_host + '/v1/messages/' + message_uuid).with(request(body: {status: 'read'})).to_return(response) + + assert_kind_of Vonage::Response, geo_specific_messaging.update(message_uuid: message_uuid, status: 'read') + end end diff --git a/test/vonage/test.rb b/test/vonage/test.rb index 1b51b1eb..84e49385 100644 --- a/test/vonage/test.rb +++ b/test/vonage/test.rb @@ -406,6 +406,10 @@ def meetings_id "MEET-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" end + def message_uuid + "aaaaaaaa-bbbb-4ccc-8ddd-0123456789ab" + end + def video_id 'VIDEO-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' end From 0d7f61af3feaecc602fb564c347ccd04a4bc9d2e Mon Sep 17 00:00:00 2001 From: superchilled Date: Fri, 23 Aug 2024 17:29:25 +0100 Subject: [PATCH 3/5] Adding code somments for update method --- lib/vonage/messaging.rb | 12 +++++++++++- test/vonage/messaging_test.rb | 6 +++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/lib/vonage/messaging.rb b/lib/vonage/messaging.rb index 9d5cdba6..904d177c 100644 --- a/lib/vonage/messaging.rb +++ b/lib/vonage/messaging.rb @@ -25,12 +25,22 @@ class Messaging < Namespace # @option params [required, Hash] **message # The Vonage Message object to use for this message. # - # @see https://developer.vonage.com/api/messages-olympus#SendMessage + # @see https://developer.vonage.com/api/messages#SendMessage # def send(to:, from:, **message) request('/v1/messages', params: {to: to, from: from, **message}, type: Post) end + # Update a Message Object. + # + # @example + # message = client.messaging.update(message_uuid: "aaaaaaaa-bbbb-4ccc-8ddd-0123456789ab", status: "read") + # + # @option params [required, String] :message_uuid. the UUID of the message to update. + # + # `:message_uuid` is always required. Other parameters will depend on the message channel and the specific action being performed. + # @see https://developer.vonage.com/api/messages#UpdateMessage + # def update(message_uuid:, **params) request("/v1/messages/#{message_uuid}", params: params, type: Patch) end diff --git a/test/vonage/messaging_test.rb b/test/vonage/messaging_test.rb index be4c09df..06ba0069 100644 --- a/test/vonage/messaging_test.rb +++ b/test/vonage/messaging_test.rb @@ -6,12 +6,12 @@ def messaging Vonage::Messaging.new(config) end - def geo_specific_host + def geo_specific_messaging_host 'api-eu.vonage.com' end def geo_specific_messaging - Vonage::Messaging.new(config.merge(api_host: geo_specific_host)) + Vonage::Messaging.new(config.merge(api_host: geo_specific_messaging_host)) end def messaging_uri @@ -75,7 +75,7 @@ def test_verify_webhook_token_method_with_no_token end def test_update_method - stub_request(:patch, 'https://' + geo_specific_host + '/v1/messages/' + message_uuid).with(request(body: {status: 'read'})).to_return(response) + stub_request(:patch, 'https://' + geo_specific_messaging_host + '/v1/messages/' + message_uuid).with(request(body: {status: 'read'})).to_return(response) assert_kind_of Vonage::Response, geo_specific_messaging.update(message_uuid: message_uuid, status: 'read') end From 51770f14edfd9e16888371381c67b163cf807aa8 Mon Sep 17 00:00:00 2001 From: superchilled Date: Fri, 23 Aug 2024 20:56:28 +0100 Subject: [PATCH 4/5] Adding rcs to CHANNELS hash in Message class --- lib/vonage/messaging/message.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/vonage/messaging/message.rb b/lib/vonage/messaging/message.rb index 8d2a7bac..0fcd1537 100644 --- a/lib/vonage/messaging/message.rb +++ b/lib/vonage/messaging/message.rb @@ -5,6 +5,7 @@ class Messaging::Message CHANNELS = { sms: Vonage::Messaging::Channels::SMS, mms: Vonage::Messaging::Channels::MMS, + rcs: Vonage::Messaging::Channels::RCS, whatsapp: Vonage::Messaging::Channels::WhatsApp, messenger: Vonage::Messaging::Channels::Messenger, viber: Vonage::Messaging::Channels::Viber From 20d5a59eefee6d14994ff5c0dc4c66d0abb8e5aa Mon Sep 17 00:00:00 2001 From: superchilled Date: Tue, 27 Aug 2024 14:49:32 +0100 Subject: [PATCH 5/5] Bumping version and updating changelog --- CHANGES.md | 7 +++++++ lib/vonage/version.rb | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 179f8266..77001cdb 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,10 @@ +# 7.27.0 + +* Updates Messages API implementation to add RCS channel as well as a new `PATCH` endpoint for RCS message revocation and WhatsApp Mark as Read features. [#316](https://github.com/Vonage/vonage-ruby-sdk/pull/316) +* Updates to `talk`, `stream`, `input`, and `record` NCCOs in Voice API implementation. [#315](https://github.com/Vonage/vonage-ruby-sdk/pull/315) +* Adds deprecation warnings to Meetings API and Proactive Connect API implementations, and updates code comments for Numbers API. [#314](https://github.com/Vonage/vonage-ruby-sdk/pull/314) + + # 7.26.0 * Implements the Network Number Verification and Network SIM Swap APIs. [#313](https://github.com/Vonage/vonage-ruby-sdk/pull/313) diff --git a/lib/vonage/version.rb b/lib/vonage/version.rb index a7a02697..c65edb93 100644 --- a/lib/vonage/version.rb +++ b/lib/vonage/version.rb @@ -1,5 +1,5 @@ # typed: strong module Vonage - VERSION = '7.26.0' + VERSION = '7.27.0' end