Skip to content

Commit

Permalink
DEVX-8697: Messages API Updates for RCS and WhatsApp (#316)
Browse files Browse the repository at this point in the history
* Implementing RCS channel in Messages API

* Implementing Messaging update method

* Adding code somments for update method

* Adding rcs to CHANNELS hash in Message class

* Bumping version and updating changelog
  • Loading branch information
superchilled authored Aug 28, 2024
1 parent e5c41f1 commit 6e799a0
Show file tree
Hide file tree
Showing 10 changed files with 312 additions and 3 deletions.
7 changes: 7 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -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)
Expand Down
1 change: 1 addition & 0 deletions lib/vonage.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ module Vonage
'http' => 'HTTP',
'json' => 'JSON',
'jwt' => 'JWT',
'rcs' => 'RCS',
'sip' => 'SIP',
'sms' => 'SMS',
'network_sim_swap' => 'NetworkSIMSwap',
Expand Down
16 changes: 15 additions & 1 deletion lib/vonage/messaging.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,26 @@ 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

# Validate a JSON Web Token from a Messages API Webhook.
#
# @param [String, required] :token The JWT from the Webhook's Authorization header
Expand Down
42 changes: 42 additions & 0 deletions lib/vonage/messaging/channels/rcs.rb
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions lib/vonage/messaging/message.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion lib/vonage/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# typed: strong

module Vonage
VERSION = '7.26.0'
VERSION = '7.27.0'
end
226 changes: 226 additions & 0 deletions test/vonage/messaging/channels/rcs_test.rb
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion test/vonage/messaging/channels/whats_app_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
14 changes: 14 additions & 0 deletions test/vonage/messaging_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ def messaging
Vonage::Messaging.new(config)
end

def geo_specific_messaging_host
'api-eu.vonage.com'
end

def geo_specific_messaging
Vonage::Messaging.new(config.merge(api_host: geo_specific_messaging_host))
end

def messaging_uri
'https://api.nexmo.com/v1/messages'
end
Expand Down Expand Up @@ -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_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
end
4 changes: 4 additions & 0 deletions test/vonage/test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 6e799a0

Please sign in to comment.