Skip to content

Commit 44f89db

Browse files
superchilledCopilot
andauthored
feat: Voice API Updates Apr 2026 (#352)
* DEVX-10728: adding support for authorization on websocket endpoints Co-authored-by: Copilot <copilot@github.com> * DEVX-10409: Adding provider support to Input NCCO action Co-authored-by: Copilot <copilot@github.com> * DEVX-10402: Adding provider and providerOptions params to talk NCCO Co-authored-by: Copilot <copilot@github.com> --------- Co-authored-by: Copilot <copilot@github.com>
1 parent f2e91ad commit 44f89db

7 files changed

Lines changed: 136 additions & 6 deletions

File tree

lib/vonage/voice/actions/connect.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ def websocket_endpoint(endpoint_attrs)
206206
}
207207

208208
hash.merge!(headers: endpoint_attrs[:headers]) if endpoint_attrs[:headers]
209+
hash.merge!(authorization: endpoint_attrs[:authorization]) if endpoint_attrs[:authorization]
209210

210211
hash
211212
end

lib/vonage/voice/actions/input.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,13 @@ def validate_speech
8585
if self.speech[:maxDuration]
8686
raise ClientError.new("Expected 'maxDuration' to not be more than 60 seconds") unless self.speech[:maxDuration] <= 60 && self.speech[:maxDuration] >= 0
8787
end
88+
89+
if self.speech[:provider]
90+
valid_providers = ['google']
91+
92+
raise ClientError.new("Invalid 'provider' value, must be one of: #{valid_providers.join(', ')}") unless valid_providers.include?(self.speech[:provider])
93+
raise ClientError.new("The `providerOptions` parameter is required when specifying a `provider`") unless self.speech[:providerOptions]
94+
end
8895
end
8996

9097
def validate_event_url

lib/vonage/voice/actions/talk.rb

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# frozen_string_literal: true
33
module Vonage
44
class Voice::Actions::Talk
5-
attr_accessor :text, :bargeIn, :loop, :level, :language, :style, :premium
5+
attr_accessor :text, :bargeIn, :loop, :level, :language, :style, :premium, :provider, :providerOptions
66

77
def initialize(attributes= {})
88
@text = attributes.fetch(:text)
@@ -12,6 +12,8 @@ def initialize(attributes= {})
1212
@language = attributes.fetch(:language, nil)
1313
@style = attributes.fetch(:style, nil)
1414
@premium = attributes.fetch(:premium, nil)
15+
@provider = attributes.fetch(:provider, nil)
16+
@providerOptions = attributes.fetch(:providerOptions, nil)
1517

1618
after_initialize!
1719
end
@@ -36,6 +38,10 @@ def after_initialize!
3638
if self.premium || self.premium == false
3739
verify_premium
3840
end
41+
42+
if self.provider
43+
verify_provider
44+
end
3945
end
4046

4147
def verify_barge_in
@@ -58,6 +64,13 @@ def verify_premium
5864
raise ClientError.new("Expected 'premium' value to be a Boolean") unless self.premium == true || self.premium == false
5965
end
6066

67+
def verify_provider
68+
valid_providers = ['google']
69+
70+
raise ClientError.new("Invalid 'provider' value, must be one of: #{valid_providers.join(', ')}") unless valid_providers.include?(self.provider)
71+
raise ClientError.new("The `providerOptions` parameter is required when specifying a `provider`") unless self.providerOptions
72+
end
73+
6174
def action
6275
create_talk!(self)
6376
end
@@ -75,7 +88,9 @@ def create_talk!(builder)
7588
ncco[0].merge!(level: builder.level) if builder.level
7689
ncco[0].merge!(language: builder.language) if builder.language
7790
ncco[0].merge!(style: builder.style) if builder.style
78-
ncco[0].merge!(premium: builder.premium) if (builder.bargeIn || builder.bargeIn == false)
91+
ncco[0].merge!(premium: builder.premium) if (builder.premium || builder.premium == false)
92+
ncco[0].merge!(provider: builder.provider) if builder.provider
93+
ncco[0].merge!(providerOptions: builder.providerOptions) if builder.providerOptions
7994

8095
ncco
8196
end

test/vonage/voice/actions/connect_test.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,20 @@ def create_endpoint_with_websocket_with_invalid_audio_rate
8181
assert_match "Expected 'content-type' parameter to be either 'audio/l16;rate=16000', 'audio/l16;rate=8000', or 'audio/l16;rate=24000'", exception.message
8282
end
8383

84+
def test_create_endpoint_with_websocket_with_vonage_authorization_header
85+
expected = { type: 'websocket', uri: 'wss://example.com/socket', :'content-type' => 'audio/l16;rate=8000', authorization: { type: 'vonage' } }
86+
connect = Vonage::Voice::Actions::Connect.new(endpoint: { type: 'websocket', uri: 'wss://example.com/socket', :'content-type' => 'audio/l16;rate=8000', authorization: { type: 'vonage' } })
87+
88+
assert_equal expected, connect.create_endpoint(connect)
89+
end
90+
91+
def test_create_endpoint_with_websocket_with_custom_authorization_header
92+
expected = { type: 'websocket', uri: 'wss://example.com/socket', :'content-type' => 'audio/l16;rate=8000', authorization: { type: 'custom', value: 'Bearer abc123def456ghi789' } }
93+
connect = Vonage::Voice::Actions::Connect.new(endpoint: { type: 'websocket', uri: 'wss://example.com/socket', :'content-type' => 'audio/l16;rate=8000', authorization: { type: 'custom', value: 'Bearer abc123def456ghi789' } })
94+
95+
assert_equal expected, connect.create_endpoint(connect)
96+
end
97+
8498
def test_create_endpoint_with_sip_uri
8599
expected = { type: 'sip', uri: 'sip:joe@domain.com' }
86100
connect = Vonage::Voice::Actions::Connect.new(endpoint: { type: 'sip', uri: 'sip:joe@domain.com' })

test/vonage/voice/actions/input_test.rb

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,9 @@ def test_create_input_with_optional_params
3838
startTimeout: 5,
3939
maxDuration: 50,
4040
saveAudio: true,
41-
sensitivity: 50
41+
sensitivity: 50,
42+
provider: "google",
43+
providerOptions: {}
4244
},
4345
eventUrl: [
4446
'https://example.com/webhooks/events',
@@ -67,7 +69,9 @@ def test_create_input_with_optional_params
6769
startTimeout: 5,
6870
maxDuration: 50,
6971
saveAudio: true,
70-
sensitivity: 50
72+
sensitivity: 50,
73+
provider: "google",
74+
providerOptions: {}
7175
},
7276
eventUrl: [
7377
'https://example.com/webhooks/events',
@@ -151,6 +155,18 @@ def test_input_with_invalid_speech_max_duration_value
151155
assert_match "Expected 'maxDuration' to not be more than 60 seconds", exception.message
152156
end
153157

158+
def test_input_with_invalid_speech_provider_value
159+
exception = assert_raises { Vonage::Voice::Actions::Input.new(type: ['speech'], speech: { provider: 'foo', providerOptions: {} }) }
160+
161+
assert_match "Invalid 'provider' value, must be one of: google", exception.message
162+
end
163+
164+
def test_input_with_provider_but_no_provider_options
165+
exception = assert_raises { Vonage::Voice::Actions::Input.new(type: ['speech'], speech: { provider: 'google' }) }
166+
167+
assert_match "The `providerOptions` parameter is required when specifying a `provider`", exception.message
168+
end
169+
154170
def test_input_with_invalid_event_url_type
155171
exception = assert_raises { Vonage::Voice::Actions::Input.new(type: ['speech'], eventUrl: 'https://example.com/event') }
156172

test/vonage/voice/actions/talk_test.rb

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ def test_create_talk_with_optional_params
2626
level: 0.5,
2727
language: 'en-GB',
2828
style: 1,
29-
premium: true
29+
premium: true,
30+
provider: "google",
31+
providerOptions: {}
3032
}
3133
]
3234

@@ -37,7 +39,9 @@ def test_create_talk_with_optional_params
3739
level: 0.5,
3840
language: 'en-GB',
3941
style: 1,
40-
premium: true
42+
premium: true,
43+
provider: "google",
44+
providerOptions: {}
4145
)
4246

4347
assert_equal expected, talk.create_talk!(talk)
@@ -72,4 +76,16 @@ def test_talk_with_invalid_premium_value
7276

7377
assert_match "Expected 'premium' value to be a Boolean", exception.message
7478
end
79+
80+
def test_talk_with_invalid_provider_value
81+
exception = assert_raises { Vonage::Voice::Actions::Talk.new({ text: 'Sample Text', provider: 'foo', providerOptions: {} }) }
82+
83+
assert_match "Invalid 'provider' value, must be one of: google", exception.message
84+
end
85+
86+
def test_talk_with_provider_but_no_provider_options
87+
exception = assert_raises { Vonage::Voice::Actions::Talk.new({ text: 'Sample Text', provider: 'google' }) }
88+
89+
assert_match "The `providerOptions` parameter is required when specifying a `provider`", exception.message
90+
end
7591
end

test/vonage/voice_test.rb

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,67 @@ def test_create_method_with_connect_to_sip_standard_headers
6868
assert_kind_of Vonage::Response, calls.create(params)
6969
end
7070

71+
def test_create_method_with_connect_to_websocket
72+
params = {
73+
to: [
74+
{
75+
type: 'websocket',
76+
uri: 'wss://example.com/socket',
77+
:'Content-Type' => 'audio/l16;rate=16000'
78+
}
79+
],
80+
from: {type: 'phone', number: '14843335555'},
81+
answer_url: ['https://example.com/answer']
82+
}
83+
84+
stub_request(:post, calls_uri).with(request(body: params)).to_return(response)
85+
86+
assert_kind_of Vonage::Response, calls.create(params)
87+
end
88+
89+
def test_create_method_with_connect_to_websocket_with_vonage_authorization_header
90+
params = {
91+
to: [
92+
{
93+
type: 'websocket',
94+
uri: 'wss://example.com/socket',
95+
:'Content-Type' => 'audio/l16;rate=16000',
96+
authorization: {
97+
type: 'vonage'
98+
}
99+
}
100+
],
101+
from: {type: 'phone', number: '14843335555'},
102+
answer_url: ['https://example.com/answer']
103+
}
104+
105+
stub_request(:post, calls_uri).with(request(body: params)).to_return(response)
106+
107+
assert_kind_of Vonage::Response, calls.create(params)
108+
end
109+
110+
def test_create_method_with_connect_to_websocket_with_custom_authorization_header
111+
params = {
112+
to: [
113+
{
114+
type: 'websocket',
115+
uri: 'wss://example.com/socket',
116+
:'Content-Type' => 'audio/l16;rate=16000',
117+
authorization: {
118+
type: 'custom',
119+
value: 'Bearer abc123def456ghi789'
120+
}
121+
}
122+
],
123+
from: {type: 'phone', number: '14843335555'},
124+
answer_url: ['https://example.com/answer']
125+
}
126+
127+
stub_request(:post, calls_uri).with(request(body: params)).to_return(response)
128+
129+
assert_kind_of Vonage::Response, calls.create(params)
130+
end
131+
71132
def test_create_method_raises_error_if_from_set_and_random_from_number_true
72133
params = {
73134
to: [{type: 'phone', number: '14843331234'}],

0 commit comments

Comments
 (0)