nexmo 4.5.0 → 4.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +29 -10
- data/lib/nexmo.rb +1 -351
- data/lib/nexmo/client.rb +350 -0
- data/lib/nexmo/jwt.rb +5 -7
- data/lib/nexmo/version.rb +1 -1
- data/spec/{nexmo_spec.rb → nexmo/client_spec.rb} +37 -11
- data/spec/nexmo/jwt_spec.rb +53 -0
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fcd725644e13c80c7c86b80e347c0bef98c8805d
|
4
|
+
data.tar.gz: 7c0eafc7558cb0d68c9faa93d1f3e9776ddf221c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 29d45a64e0aeaec4c647c54d1db995b9c22a58b071a815f3e46d52648eff3a216c73dfb72e67777a19c9ae6499957a7e6d3900ae641a829fcef174884071d196
|
7
|
+
data.tar.gz: da8b6ec8aaaa5b55645940e2d6b0b59260c17bf717cf8191bf430687636a147e26b885a23ccdcff82fbfd1cbee20c084a553ed217be82ae42582fe6f36e481e9
|
data/README.md
CHANGED
@@ -1,5 +1,4 @@
|
|
1
|
-
Nexmo Client Library for Ruby
|
2
|
-
=============================
|
1
|
+
# Nexmo Client Library for Ruby
|
3
2
|
|
4
3
|
[](https://badge.fury.io/rb/nexmo) [](https://travis-ci.org/Nexmo/nexmo-ruby)
|
5
4
|
|
@@ -16,8 +15,7 @@ need a Nexmo account. Sign up [for free at nexmo.com][signup].
|
|
16
15
|
* [License](#license)
|
17
16
|
|
18
17
|
|
19
|
-
Installation
|
20
|
-
------------
|
18
|
+
## Installation
|
21
19
|
|
22
20
|
To install the Ruby client library using Rubygems:
|
23
21
|
|
@@ -28,8 +26,7 @@ Alternatively you can clone the repository:
|
|
28
26
|
git clone git@github.com:Nexmo/nexmo-ruby.git
|
29
27
|
|
30
28
|
|
31
|
-
Usage
|
32
|
-
-----
|
29
|
+
## Usage
|
33
30
|
|
34
31
|
Begin by requiring the nexmo library:
|
35
32
|
|
@@ -252,6 +249,30 @@ response = client.delete_application(uuid)
|
|
252
249
|
Docs: [https://docs.nexmo.com/tools/application-api/api-reference#delete](https://docs.nexmo.com/tools/application-api/api-reference#delete?utm_source=DEV_REL&utm_medium=github&utm_campaign=ruby-client-library)
|
253
250
|
|
254
251
|
|
252
|
+
## JWT authentication
|
253
|
+
|
254
|
+
By default the library generates a short lived JWT per request.
|
255
|
+
|
256
|
+
To generate a long lived JWT for multiple requests or to specify JWT claims
|
257
|
+
directly call Nexmo::JWT.auth_token to generate a token, and set the auth_token
|
258
|
+
attribute on the client object. For example:
|
259
|
+
|
260
|
+
```ruby
|
261
|
+
claims = {
|
262
|
+
application_id: application_id,
|
263
|
+
nbf: 1483315200,
|
264
|
+
exp: 1514764800,
|
265
|
+
iat: 1483228800
|
266
|
+
}
|
267
|
+
|
268
|
+
private_key = File.read('path/to/private.key')
|
269
|
+
|
270
|
+
auth_token = Nexmo::JWT.auth_token(claims, private_key)
|
271
|
+
|
272
|
+
client.auth_token = auth_token
|
273
|
+
````
|
274
|
+
|
275
|
+
|
255
276
|
## Validate webhook signatures
|
256
277
|
|
257
278
|
```ruby
|
@@ -270,8 +291,7 @@ Note: you'll need to contact support@nexmo.com to enable message signing on
|
|
270
291
|
your account before you can validate webhook signatures.
|
271
292
|
|
272
293
|
|
273
|
-
API Coverage
|
274
|
-
------------
|
294
|
+
## API Coverage
|
275
295
|
|
276
296
|
* Account
|
277
297
|
* [X] Balance
|
@@ -313,8 +333,7 @@ API Coverage
|
|
313
333
|
* [X] Text-To-Speech Prompt
|
314
334
|
|
315
335
|
|
316
|
-
License
|
317
|
-
-------
|
336
|
+
## License
|
318
337
|
|
319
338
|
This library is released under the [MIT License][license]
|
320
339
|
|
data/lib/nexmo.rb
CHANGED
@@ -6,354 +6,4 @@ require 'nexmo/errors/error'
|
|
6
6
|
require 'nexmo/errors/client_error'
|
7
7
|
require 'nexmo/errors/server_error'
|
8
8
|
require 'nexmo/errors/authentication_error'
|
9
|
-
require '
|
10
|
-
require 'json'
|
11
|
-
|
12
|
-
module Nexmo
|
13
|
-
class Client
|
14
|
-
attr_accessor :key, :secret
|
15
|
-
|
16
|
-
def initialize(options = {})
|
17
|
-
@key = options.fetch(:key) { ENV.fetch('NEXMO_API_KEY') }
|
18
|
-
|
19
|
-
@secret = options.fetch(:secret) { ENV.fetch('NEXMO_API_SECRET') }
|
20
|
-
|
21
|
-
@signature_secret = options.fetch(:signature_secret) { ENV['NEXMO_SIGNATURE_SECRET'] }
|
22
|
-
|
23
|
-
@application_id = options[:application_id]
|
24
|
-
|
25
|
-
@private_key = options[:private_key]
|
26
|
-
|
27
|
-
@host = options.fetch(:host) { 'rest.nexmo.com' }
|
28
|
-
|
29
|
-
@api_host = options.fetch(:api_host) { 'api.nexmo.com' }
|
30
|
-
|
31
|
-
@sns_host = options.fetch(:sns_host) { 'sns.nexmo.com' }
|
32
|
-
|
33
|
-
@user_agent = "nexmo-ruby/#{VERSION} ruby/#{RUBY_VERSION}"
|
34
|
-
|
35
|
-
if options.key?(:app_name) && options.key?(:app_version)
|
36
|
-
@user_agent << " #{options[:app_name]}/#{options[:app_version]}"
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
def send_message(params)
|
41
|
-
post(@host, '/sms/json', params)
|
42
|
-
end
|
43
|
-
|
44
|
-
def track_message_conversion(message_id, params = {})
|
45
|
-
post(@api_host, '/conversions/sms', {'message-id' => message_id}.merge(params))
|
46
|
-
end
|
47
|
-
|
48
|
-
def track_voice_conversion(message_id, params = {})
|
49
|
-
post(@api_host, '/conversions/voice', {'message-id' => message_id}.merge(params))
|
50
|
-
end
|
51
|
-
|
52
|
-
def get_balance
|
53
|
-
get(@host, '/account/get-balance')
|
54
|
-
end
|
55
|
-
|
56
|
-
def get_country_pricing(country_code)
|
57
|
-
get(@host, '/account/get-pricing/outbound', country: country_code)
|
58
|
-
end
|
59
|
-
|
60
|
-
def get_prefix_pricing(prefix)
|
61
|
-
get(@host, '/account/get-prefix-pricing/outbound', prefix: prefix)
|
62
|
-
end
|
63
|
-
|
64
|
-
def get_sms_pricing(number)
|
65
|
-
get(@host, '/account/get-phone-pricing/outbound/sms', phone: number)
|
66
|
-
end
|
67
|
-
|
68
|
-
def get_voice_pricing(number)
|
69
|
-
get(@host, '/account/get-phone-pricing/outbound/voice', phone: number)
|
70
|
-
end
|
71
|
-
|
72
|
-
def update_settings(params)
|
73
|
-
post(@host, '/account/settings', params)
|
74
|
-
end
|
75
|
-
|
76
|
-
def topup(params)
|
77
|
-
post(@host, '/account/top-up', params)
|
78
|
-
end
|
79
|
-
|
80
|
-
def get_account_numbers(params)
|
81
|
-
get(@host, '/account/numbers', params)
|
82
|
-
end
|
83
|
-
|
84
|
-
def get_available_numbers(country_code, params = {})
|
85
|
-
get(@host, '/number/search', {country: country_code}.merge(params))
|
86
|
-
end
|
87
|
-
|
88
|
-
def buy_number(params)
|
89
|
-
post(@host, '/number/buy', params)
|
90
|
-
end
|
91
|
-
|
92
|
-
def cancel_number(params)
|
93
|
-
post(@host, '/number/cancel', params)
|
94
|
-
end
|
95
|
-
|
96
|
-
def update_number(params)
|
97
|
-
post(@host, '/number/update', params)
|
98
|
-
end
|
99
|
-
|
100
|
-
def get_message(id)
|
101
|
-
get(@host, '/search/message', id: id)
|
102
|
-
end
|
103
|
-
|
104
|
-
def get_message_rejections(params)
|
105
|
-
get(@host, '/search/rejections', params)
|
106
|
-
end
|
107
|
-
|
108
|
-
def search_messages(params)
|
109
|
-
get(@host, '/search/messages', Hash === params ? params : {ids: Array(params)})
|
110
|
-
end
|
111
|
-
|
112
|
-
def send_ussd_push_message(params)
|
113
|
-
post(@host, '/ussd/json', params)
|
114
|
-
end
|
115
|
-
|
116
|
-
def send_ussd_prompt_message(params)
|
117
|
-
post(@host, '/ussd-prompt/json', params)
|
118
|
-
end
|
119
|
-
|
120
|
-
def send_2fa_message(params)
|
121
|
-
post(@host, '/sc/us/2fa/json', params)
|
122
|
-
end
|
123
|
-
|
124
|
-
def send_event_alert_message(params)
|
125
|
-
post(@host, '/sc/us/alert/json', params)
|
126
|
-
end
|
127
|
-
|
128
|
-
def send_marketing_message(params)
|
129
|
-
post(@host, '/sc/us/marketing/json', params)
|
130
|
-
end
|
131
|
-
|
132
|
-
def get_event_alert_numbers
|
133
|
-
get(@host, '/sc/us/alert/opt-in/query/json')
|
134
|
-
end
|
135
|
-
|
136
|
-
def resubscribe_event_alert_number(params)
|
137
|
-
post(@host, '/sc/us/alert/opt-in/manage/json', params)
|
138
|
-
end
|
139
|
-
|
140
|
-
def sns_publish(message, params)
|
141
|
-
post(@sns_host, '/sns/json', {cmd: 'publish', message: message}.merge(params))
|
142
|
-
end
|
143
|
-
|
144
|
-
def sns_subscribe(recipient, params)
|
145
|
-
post(@sns_host, '/sns/json', {cmd: 'subscribe', to: recipient}.merge(params))
|
146
|
-
end
|
147
|
-
|
148
|
-
def initiate_call(params)
|
149
|
-
Kernel.warn "#{self.class}##{__method__} is deprecated (use the Voice API instead)."
|
150
|
-
|
151
|
-
post(@host, '/call/json', params)
|
152
|
-
end
|
153
|
-
|
154
|
-
def initiate_tts_call(params)
|
155
|
-
Kernel.warn "#{self.class}##{__method__} is deprecated (use the Voice API instead)."
|
156
|
-
|
157
|
-
post(@api_host, '/tts/json', params)
|
158
|
-
end
|
159
|
-
|
160
|
-
def initiate_tts_prompt_call(params)
|
161
|
-
Kernel.warn "#{self.class}##{__method__} is deprecated (use the Voice API instead)."
|
162
|
-
|
163
|
-
post(@api_host, '/tts-prompt/json', params)
|
164
|
-
end
|
165
|
-
|
166
|
-
def start_verification(params)
|
167
|
-
post(@api_host, '/verify/json', params)
|
168
|
-
end
|
169
|
-
|
170
|
-
def check_verification(request_id, params)
|
171
|
-
post(@api_host, '/verify/check/json', params.merge(request_id: request_id))
|
172
|
-
end
|
173
|
-
|
174
|
-
def get_verification(request_id)
|
175
|
-
get(@api_host, '/verify/search/json', request_id: request_id)
|
176
|
-
end
|
177
|
-
|
178
|
-
def cancel_verification(request_id)
|
179
|
-
post(@api_host, '/verify/control/json', request_id: request_id, cmd: 'cancel')
|
180
|
-
end
|
181
|
-
|
182
|
-
def trigger_next_verification_event(request_id)
|
183
|
-
post(@api_host, '/verify/control/json', request_id: request_id, cmd: 'trigger_next_event')
|
184
|
-
end
|
185
|
-
|
186
|
-
def get_basic_number_insight(params)
|
187
|
-
get(@api_host, '/ni/basic/json', params)
|
188
|
-
end
|
189
|
-
|
190
|
-
def get_standard_number_insight(params)
|
191
|
-
get(@api_host, '/ni/standard/json', params)
|
192
|
-
end
|
193
|
-
|
194
|
-
def get_number_insight(params)
|
195
|
-
Kernel.warn "#{self.class}##{__method__} is deprecated (use #get_standard_number_insight instead)."
|
196
|
-
|
197
|
-
get(@api_host, '/number/lookup/json', params)
|
198
|
-
end
|
199
|
-
|
200
|
-
def get_advanced_number_insight(params)
|
201
|
-
get(@api_host, '/ni/advanced/json', params)
|
202
|
-
end
|
203
|
-
|
204
|
-
def get_advanced_async_number_insight(params)
|
205
|
-
get(@api_host, '/ni/advanced/async/json', params)
|
206
|
-
end
|
207
|
-
|
208
|
-
def request_number_insight(params)
|
209
|
-
post(@host, '/ni/json', params)
|
210
|
-
end
|
211
|
-
|
212
|
-
def get_applications(params = {})
|
213
|
-
get(@api_host, '/v1/applications', params)
|
214
|
-
end
|
215
|
-
|
216
|
-
def get_application(id)
|
217
|
-
get(@api_host, "/v1/applications/#{id}")
|
218
|
-
end
|
219
|
-
|
220
|
-
def create_application(params)
|
221
|
-
post(@api_host, '/v1/applications', params)
|
222
|
-
end
|
223
|
-
|
224
|
-
def update_application(id, params)
|
225
|
-
put(@api_host, "/v1/applications/#{id}", params)
|
226
|
-
end
|
227
|
-
|
228
|
-
def delete_application(id)
|
229
|
-
delete(@api_host, "/v1/applications/#{id}")
|
230
|
-
end
|
231
|
-
|
232
|
-
def create_call(params)
|
233
|
-
api_request(Net::HTTP::Post, '/v1/calls', params)
|
234
|
-
end
|
235
|
-
|
236
|
-
def get_calls(params = nil)
|
237
|
-
api_request(Net::HTTP::Get, '/v1/calls', params)
|
238
|
-
end
|
239
|
-
|
240
|
-
def get_call(uuid)
|
241
|
-
api_request(Net::HTTP::Get, "/v1/calls/#{uuid}")
|
242
|
-
end
|
243
|
-
|
244
|
-
def update_call(uuid, params)
|
245
|
-
api_request(Net::HTTP::Put, "/v1/calls/#{uuid}", params)
|
246
|
-
end
|
247
|
-
|
248
|
-
def send_audio(uuid, params)
|
249
|
-
api_request(Net::HTTP::Put, "/v1/calls/#{uuid}/stream", params)
|
250
|
-
end
|
251
|
-
|
252
|
-
def stop_audio(uuid)
|
253
|
-
api_request(Net::HTTP::Delete, "/v1/calls/#{uuid}/stream")
|
254
|
-
end
|
255
|
-
|
256
|
-
def send_speech(uuid, params)
|
257
|
-
api_request(Net::HTTP::Put, "/v1/calls/#{uuid}/talk", params)
|
258
|
-
end
|
259
|
-
|
260
|
-
def stop_speech(uuid)
|
261
|
-
api_request(Net::HTTP::Delete, "/v1/calls/#{uuid}/talk")
|
262
|
-
end
|
263
|
-
|
264
|
-
def send_dtmf(uuid, params)
|
265
|
-
api_request(Net::HTTP::Put, "/v1/calls/#{uuid}/dtmf", params)
|
266
|
-
end
|
267
|
-
|
268
|
-
def check_signature(params)
|
269
|
-
Signature.check(params, @signature_secret)
|
270
|
-
end
|
271
|
-
|
272
|
-
private
|
273
|
-
|
274
|
-
def get(host, request_uri, params = {})
|
275
|
-
uri = URI('https://' + host + request_uri)
|
276
|
-
uri.query = Params.encode(params.merge(api_key: @key, api_secret: @secret))
|
277
|
-
|
278
|
-
message = Net::HTTP::Get.new(uri.request_uri)
|
279
|
-
|
280
|
-
request(uri, message)
|
281
|
-
end
|
282
|
-
|
283
|
-
def post(host, request_uri, params)
|
284
|
-
uri = URI('https://' + host + request_uri)
|
285
|
-
|
286
|
-
message = Net::HTTP::Post.new(uri.request_uri)
|
287
|
-
message.form_data = params.merge(api_key: @key, api_secret: @secret)
|
288
|
-
|
289
|
-
request(uri, message)
|
290
|
-
end
|
291
|
-
|
292
|
-
def put(host, request_uri, params)
|
293
|
-
uri = URI('https://' + host + request_uri)
|
294
|
-
|
295
|
-
message = Net::HTTP::Put.new(uri.request_uri)
|
296
|
-
message.form_data = params.merge(api_key: @key, api_secret: @secret)
|
297
|
-
|
298
|
-
request(uri, message)
|
299
|
-
end
|
300
|
-
|
301
|
-
def delete(host, request_uri)
|
302
|
-
uri = URI('https://' + host + request_uri)
|
303
|
-
uri.query = Params.encode({api_key: @key, api_secret: @secret})
|
304
|
-
|
305
|
-
message = Net::HTTP::Delete.new(uri.request_uri)
|
306
|
-
|
307
|
-
request(uri, message)
|
308
|
-
end
|
309
|
-
|
310
|
-
def api_request(message_class, path, params = nil)
|
311
|
-
uri = URI('https://' + @api_host + path)
|
312
|
-
|
313
|
-
unless message_class::REQUEST_HAS_BODY || params.nil? || params.empty?
|
314
|
-
uri.query = Params.encode(params)
|
315
|
-
end
|
316
|
-
|
317
|
-
message = message_class.new(uri.request_uri)
|
318
|
-
|
319
|
-
if message_class::REQUEST_HAS_BODY
|
320
|
-
message['Content-Type'] = 'application/json'
|
321
|
-
message.body = JSON.generate(params)
|
322
|
-
end
|
323
|
-
|
324
|
-
auth_payload = {application_id: @application_id}
|
325
|
-
|
326
|
-
message['Authorization'] = JWT.auth_header(auth_payload, @private_key)
|
327
|
-
|
328
|
-
request(uri, message)
|
329
|
-
end
|
330
|
-
|
331
|
-
def request(uri, message)
|
332
|
-
http = Net::HTTP.new(uri.host, Net::HTTP.https_default_port)
|
333
|
-
http.use_ssl = true
|
334
|
-
|
335
|
-
message['User-Agent'] = @user_agent
|
336
|
-
|
337
|
-
http_response = http.request(message)
|
338
|
-
|
339
|
-
case http_response
|
340
|
-
when Net::HTTPNoContent
|
341
|
-
:no_content
|
342
|
-
when Net::HTTPSuccess
|
343
|
-
if http_response['Content-Type'].split(';').first == 'application/json'
|
344
|
-
JSON.parse(http_response.body)
|
345
|
-
else
|
346
|
-
http_response.body
|
347
|
-
end
|
348
|
-
when Net::HTTPUnauthorized
|
349
|
-
raise AuthenticationError, "#{http_response.code} response from #{uri.host}"
|
350
|
-
when Net::HTTPClientError
|
351
|
-
raise ClientError, "#{http_response.code} response from #{uri.host}"
|
352
|
-
when Net::HTTPServerError
|
353
|
-
raise ServerError, "#{http_response.code} response from #{uri.host}"
|
354
|
-
else
|
355
|
-
raise Error, "#{http_response.code} response from #{uri.host}"
|
356
|
-
end
|
357
|
-
end
|
358
|
-
end
|
359
|
-
end
|
9
|
+
require 'nexmo/client'
|
data/lib/nexmo/client.rb
ADDED
@@ -0,0 +1,350 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module Nexmo
|
5
|
+
class Client
|
6
|
+
attr_accessor :key, :secret, :auth_token
|
7
|
+
|
8
|
+
def initialize(options = {})
|
9
|
+
@key = options.fetch(:key) { ENV.fetch('NEXMO_API_KEY') }
|
10
|
+
|
11
|
+
@secret = options.fetch(:secret) { ENV.fetch('NEXMO_API_SECRET') }
|
12
|
+
|
13
|
+
@signature_secret = options.fetch(:signature_secret) { ENV['NEXMO_SIGNATURE_SECRET'] }
|
14
|
+
|
15
|
+
@application_id = options[:application_id]
|
16
|
+
|
17
|
+
@private_key = options[:private_key]
|
18
|
+
|
19
|
+
@host = options.fetch(:host) { 'rest.nexmo.com' }
|
20
|
+
|
21
|
+
@api_host = options.fetch(:api_host) { 'api.nexmo.com' }
|
22
|
+
|
23
|
+
@sns_host = options.fetch(:sns_host) { 'sns.nexmo.com' }
|
24
|
+
|
25
|
+
@user_agent = "nexmo-ruby/#{VERSION} ruby/#{RUBY_VERSION}"
|
26
|
+
|
27
|
+
if options.key?(:app_name) && options.key?(:app_version)
|
28
|
+
@user_agent << " #{options[:app_name]}/#{options[:app_version]}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def send_message(params)
|
33
|
+
post(@host, '/sms/json', params)
|
34
|
+
end
|
35
|
+
|
36
|
+
def track_message_conversion(message_id, params = {})
|
37
|
+
post(@api_host, '/conversions/sms', {'message-id' => message_id}.merge(params))
|
38
|
+
end
|
39
|
+
|
40
|
+
def track_voice_conversion(message_id, params = {})
|
41
|
+
post(@api_host, '/conversions/voice', {'message-id' => message_id}.merge(params))
|
42
|
+
end
|
43
|
+
|
44
|
+
def get_balance
|
45
|
+
get(@host, '/account/get-balance')
|
46
|
+
end
|
47
|
+
|
48
|
+
def get_country_pricing(country_code)
|
49
|
+
get(@host, '/account/get-pricing/outbound', country: country_code)
|
50
|
+
end
|
51
|
+
|
52
|
+
def get_prefix_pricing(prefix)
|
53
|
+
get(@host, '/account/get-prefix-pricing/outbound', prefix: prefix)
|
54
|
+
end
|
55
|
+
|
56
|
+
def get_sms_pricing(number)
|
57
|
+
get(@host, '/account/get-phone-pricing/outbound/sms', phone: number)
|
58
|
+
end
|
59
|
+
|
60
|
+
def get_voice_pricing(number)
|
61
|
+
get(@host, '/account/get-phone-pricing/outbound/voice', phone: number)
|
62
|
+
end
|
63
|
+
|
64
|
+
def update_settings(params)
|
65
|
+
post(@host, '/account/settings', params)
|
66
|
+
end
|
67
|
+
|
68
|
+
def topup(params)
|
69
|
+
post(@host, '/account/top-up', params)
|
70
|
+
end
|
71
|
+
|
72
|
+
def get_account_numbers(params)
|
73
|
+
get(@host, '/account/numbers', params)
|
74
|
+
end
|
75
|
+
|
76
|
+
def get_available_numbers(country_code, params = {})
|
77
|
+
get(@host, '/number/search', {country: country_code}.merge(params))
|
78
|
+
end
|
79
|
+
|
80
|
+
def buy_number(params)
|
81
|
+
post(@host, '/number/buy', params)
|
82
|
+
end
|
83
|
+
|
84
|
+
def cancel_number(params)
|
85
|
+
post(@host, '/number/cancel', params)
|
86
|
+
end
|
87
|
+
|
88
|
+
def update_number(params)
|
89
|
+
post(@host, '/number/update', params)
|
90
|
+
end
|
91
|
+
|
92
|
+
def get_message(id)
|
93
|
+
get(@host, '/search/message', id: id)
|
94
|
+
end
|
95
|
+
|
96
|
+
def get_message_rejections(params)
|
97
|
+
get(@host, '/search/rejections', params)
|
98
|
+
end
|
99
|
+
|
100
|
+
def search_messages(params)
|
101
|
+
get(@host, '/search/messages', Hash === params ? params : {ids: Array(params)})
|
102
|
+
end
|
103
|
+
|
104
|
+
def send_ussd_push_message(params)
|
105
|
+
post(@host, '/ussd/json', params)
|
106
|
+
end
|
107
|
+
|
108
|
+
def send_ussd_prompt_message(params)
|
109
|
+
post(@host, '/ussd-prompt/json', params)
|
110
|
+
end
|
111
|
+
|
112
|
+
def send_2fa_message(params)
|
113
|
+
post(@host, '/sc/us/2fa/json', params)
|
114
|
+
end
|
115
|
+
|
116
|
+
def send_event_alert_message(params)
|
117
|
+
post(@host, '/sc/us/alert/json', params)
|
118
|
+
end
|
119
|
+
|
120
|
+
def send_marketing_message(params)
|
121
|
+
post(@host, '/sc/us/marketing/json', params)
|
122
|
+
end
|
123
|
+
|
124
|
+
def get_event_alert_numbers
|
125
|
+
get(@host, '/sc/us/alert/opt-in/query/json')
|
126
|
+
end
|
127
|
+
|
128
|
+
def resubscribe_event_alert_number(params)
|
129
|
+
post(@host, '/sc/us/alert/opt-in/manage/json', params)
|
130
|
+
end
|
131
|
+
|
132
|
+
def sns_publish(message, params)
|
133
|
+
post(@sns_host, '/sns/json', {cmd: 'publish', message: message}.merge(params))
|
134
|
+
end
|
135
|
+
|
136
|
+
def sns_subscribe(recipient, params)
|
137
|
+
post(@sns_host, '/sns/json', {cmd: 'subscribe', to: recipient}.merge(params))
|
138
|
+
end
|
139
|
+
|
140
|
+
def initiate_call(params)
|
141
|
+
Kernel.warn "#{self.class}##{__method__} is deprecated (use the Voice API instead)."
|
142
|
+
|
143
|
+
post(@host, '/call/json', params)
|
144
|
+
end
|
145
|
+
|
146
|
+
def initiate_tts_call(params)
|
147
|
+
Kernel.warn "#{self.class}##{__method__} is deprecated (use the Voice API instead)."
|
148
|
+
|
149
|
+
post(@api_host, '/tts/json', params)
|
150
|
+
end
|
151
|
+
|
152
|
+
def initiate_tts_prompt_call(params)
|
153
|
+
Kernel.warn "#{self.class}##{__method__} is deprecated (use the Voice API instead)."
|
154
|
+
|
155
|
+
post(@api_host, '/tts-prompt/json', params)
|
156
|
+
end
|
157
|
+
|
158
|
+
def start_verification(params)
|
159
|
+
post(@api_host, '/verify/json', params)
|
160
|
+
end
|
161
|
+
|
162
|
+
def check_verification(request_id, params)
|
163
|
+
post(@api_host, '/verify/check/json', params.merge(request_id: request_id))
|
164
|
+
end
|
165
|
+
|
166
|
+
def get_verification(request_id)
|
167
|
+
get(@api_host, '/verify/search/json', request_id: request_id)
|
168
|
+
end
|
169
|
+
|
170
|
+
def cancel_verification(request_id)
|
171
|
+
post(@api_host, '/verify/control/json', request_id: request_id, cmd: 'cancel')
|
172
|
+
end
|
173
|
+
|
174
|
+
def trigger_next_verification_event(request_id)
|
175
|
+
post(@api_host, '/verify/control/json', request_id: request_id, cmd: 'trigger_next_event')
|
176
|
+
end
|
177
|
+
|
178
|
+
def get_basic_number_insight(params)
|
179
|
+
get(@api_host, '/ni/basic/json', params)
|
180
|
+
end
|
181
|
+
|
182
|
+
def get_standard_number_insight(params)
|
183
|
+
get(@api_host, '/ni/standard/json', params)
|
184
|
+
end
|
185
|
+
|
186
|
+
def get_advanced_number_insight(params)
|
187
|
+
get(@api_host, '/ni/advanced/json', params)
|
188
|
+
end
|
189
|
+
|
190
|
+
def get_advanced_async_number_insight(params)
|
191
|
+
get(@api_host, '/ni/advanced/async/json', params)
|
192
|
+
end
|
193
|
+
|
194
|
+
def request_number_insight(params)
|
195
|
+
post(@host, '/ni/json', params)
|
196
|
+
end
|
197
|
+
|
198
|
+
def get_applications(params = {})
|
199
|
+
get(@api_host, '/v1/applications', params)
|
200
|
+
end
|
201
|
+
|
202
|
+
def get_application(id)
|
203
|
+
get(@api_host, "/v1/applications/#{id}")
|
204
|
+
end
|
205
|
+
|
206
|
+
def create_application(params)
|
207
|
+
post(@api_host, '/v1/applications', params)
|
208
|
+
end
|
209
|
+
|
210
|
+
def update_application(id, params)
|
211
|
+
put(@api_host, "/v1/applications/#{id}", params)
|
212
|
+
end
|
213
|
+
|
214
|
+
def delete_application(id)
|
215
|
+
delete(@api_host, "/v1/applications/#{id}")
|
216
|
+
end
|
217
|
+
|
218
|
+
def create_call(params)
|
219
|
+
api_request(Net::HTTP::Post, '/v1/calls', params)
|
220
|
+
end
|
221
|
+
|
222
|
+
def get_calls(params = nil)
|
223
|
+
api_request(Net::HTTP::Get, '/v1/calls', params)
|
224
|
+
end
|
225
|
+
|
226
|
+
def get_call(uuid)
|
227
|
+
api_request(Net::HTTP::Get, "/v1/calls/#{uuid}")
|
228
|
+
end
|
229
|
+
|
230
|
+
def update_call(uuid, params)
|
231
|
+
api_request(Net::HTTP::Put, "/v1/calls/#{uuid}", params)
|
232
|
+
end
|
233
|
+
|
234
|
+
def send_audio(uuid, params)
|
235
|
+
api_request(Net::HTTP::Put, "/v1/calls/#{uuid}/stream", params)
|
236
|
+
end
|
237
|
+
|
238
|
+
def stop_audio(uuid)
|
239
|
+
api_request(Net::HTTP::Delete, "/v1/calls/#{uuid}/stream")
|
240
|
+
end
|
241
|
+
|
242
|
+
def send_speech(uuid, params)
|
243
|
+
api_request(Net::HTTP::Put, "/v1/calls/#{uuid}/talk", params)
|
244
|
+
end
|
245
|
+
|
246
|
+
def stop_speech(uuid)
|
247
|
+
api_request(Net::HTTP::Delete, "/v1/calls/#{uuid}/talk")
|
248
|
+
end
|
249
|
+
|
250
|
+
def send_dtmf(uuid, params)
|
251
|
+
api_request(Net::HTTP::Put, "/v1/calls/#{uuid}/dtmf", params)
|
252
|
+
end
|
253
|
+
|
254
|
+
def get_file(id)
|
255
|
+
api_request(Net::HTTP::Get, "/v1/files/#{id.split('/').last}")
|
256
|
+
end
|
257
|
+
|
258
|
+
def check_signature(params)
|
259
|
+
Signature.check(params, @signature_secret)
|
260
|
+
end
|
261
|
+
|
262
|
+
private
|
263
|
+
|
264
|
+
def get(host, request_uri, params = {})
|
265
|
+
uri = URI('https://' + host + request_uri)
|
266
|
+
uri.query = Params.encode(params.merge(api_key: @key, api_secret: @secret))
|
267
|
+
|
268
|
+
message = Net::HTTP::Get.new(uri.request_uri)
|
269
|
+
|
270
|
+
request(uri, message)
|
271
|
+
end
|
272
|
+
|
273
|
+
def post(host, request_uri, params)
|
274
|
+
uri = URI('https://' + host + request_uri)
|
275
|
+
|
276
|
+
message = Net::HTTP::Post.new(uri.request_uri)
|
277
|
+
message.form_data = params.merge(api_key: @key, api_secret: @secret)
|
278
|
+
|
279
|
+
request(uri, message)
|
280
|
+
end
|
281
|
+
|
282
|
+
def put(host, request_uri, params)
|
283
|
+
uri = URI('https://' + host + request_uri)
|
284
|
+
|
285
|
+
message = Net::HTTP::Put.new(uri.request_uri)
|
286
|
+
message['Content-Type'] = 'application/json'
|
287
|
+
message.body = JSON.generate(params.merge(api_key: @key, api_secret: @secret))
|
288
|
+
|
289
|
+
request(uri, message)
|
290
|
+
end
|
291
|
+
|
292
|
+
def delete(host, request_uri)
|
293
|
+
uri = URI('https://' + host + request_uri)
|
294
|
+
uri.query = Params.encode({api_key: @key, api_secret: @secret})
|
295
|
+
|
296
|
+
message = Net::HTTP::Delete.new(uri.request_uri)
|
297
|
+
|
298
|
+
request(uri, message)
|
299
|
+
end
|
300
|
+
|
301
|
+
def api_request(message_class, path, params = nil)
|
302
|
+
uri = URI('https://' + @api_host + path)
|
303
|
+
|
304
|
+
unless message_class::REQUEST_HAS_BODY || params.nil? || params.empty?
|
305
|
+
uri.query = Params.encode(params)
|
306
|
+
end
|
307
|
+
|
308
|
+
message = message_class.new(uri.request_uri)
|
309
|
+
|
310
|
+
if message_class::REQUEST_HAS_BODY
|
311
|
+
message['Content-Type'] = 'application/json'
|
312
|
+
message.body = JSON.generate(params)
|
313
|
+
end
|
314
|
+
|
315
|
+
token = auth_token || JWT.auth_token({application_id: @application_id}, @private_key)
|
316
|
+
|
317
|
+
message['Authorization'] = "Bearer #{token}"
|
318
|
+
|
319
|
+
request(uri, message)
|
320
|
+
end
|
321
|
+
|
322
|
+
def request(uri, message)
|
323
|
+
http = Net::HTTP.new(uri.host, Net::HTTP.https_default_port)
|
324
|
+
http.use_ssl = true
|
325
|
+
|
326
|
+
message['User-Agent'] = @user_agent
|
327
|
+
|
328
|
+
http_response = http.request(message)
|
329
|
+
|
330
|
+
case http_response
|
331
|
+
when Net::HTTPNoContent
|
332
|
+
:no_content
|
333
|
+
when Net::HTTPSuccess
|
334
|
+
if http_response['Content-Type'].split(';').first == 'application/json'
|
335
|
+
JSON.parse(http_response.body)
|
336
|
+
else
|
337
|
+
http_response.body
|
338
|
+
end
|
339
|
+
when Net::HTTPUnauthorized
|
340
|
+
raise AuthenticationError, "#{http_response.code} response from #{uri.host}"
|
341
|
+
when Net::HTTPClientError
|
342
|
+
raise ClientError, "#{http_response.code} response from #{uri.host}"
|
343
|
+
when Net::HTTPServerError
|
344
|
+
raise ServerError, "#{http_response.code} response from #{uri.host}"
|
345
|
+
else
|
346
|
+
raise Error, "#{http_response.code} response from #{uri.host}"
|
347
|
+
end
|
348
|
+
end
|
349
|
+
end
|
350
|
+
end
|
data/lib/nexmo/jwt.rb
CHANGED
@@ -4,16 +4,14 @@ require 'jwt'
|
|
4
4
|
|
5
5
|
module Nexmo
|
6
6
|
module JWT
|
7
|
-
def self.
|
8
|
-
payload[:iat] = iat = Time.now.to_i
|
9
|
-
payload[:exp] = iat + 60
|
10
|
-
payload[:jti] = SecureRandom.uuid
|
7
|
+
def self.auth_token(payload, private_key)
|
8
|
+
payload[:iat] = iat = Time.now.to_i unless payload.key?(:iat) || payload.key?('iat')
|
9
|
+
payload[:exp] = iat + 60 unless payload.key?(:exp) || payload.key?('exp')
|
10
|
+
payload[:jti] = SecureRandom.uuid unless payload.key?(:jti) || payload.key?('jti')
|
11
11
|
|
12
12
|
private_key = OpenSSL::PKey::RSA.new(private_key) unless private_key.respond_to?(:sign)
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
"Bearer #{token}"
|
14
|
+
::JWT.encode(payload, private_key, 'RS256')
|
17
15
|
end
|
18
16
|
end
|
19
17
|
end
|
data/lib/nexmo/version.rb
CHANGED
@@ -333,16 +333,6 @@ describe 'Nexmo::Client' do
|
|
333
333
|
end
|
334
334
|
end
|
335
335
|
|
336
|
-
describe 'get_number_insight method' do
|
337
|
-
it 'fetches the number lookup resource and returns the response object' do
|
338
|
-
expect_get "#@api_base_url/number/lookup/json?api_key=#@api_key&api_secret=#@api_secret&number=447525856424"
|
339
|
-
|
340
|
-
Kernel.stub :warn, proc { |message| message.must_match(/get_number_insight is deprecated/) } do
|
341
|
-
@client.get_number_insight(number: '447525856424').must_equal(@response_object)
|
342
|
-
end
|
343
|
-
end
|
344
|
-
end
|
345
|
-
|
346
336
|
describe 'get_advanced_number_insight method' do
|
347
337
|
it 'fetches the ni advanced resource and returns the response object' do
|
348
338
|
expect_get "#@api_base_url/ni/advanced/json?api_key=#@api_key&api_secret=#@api_secret&number=447525856424"
|
@@ -485,6 +475,30 @@ describe 'Nexmo::Client' do
|
|
485
475
|
end
|
486
476
|
end
|
487
477
|
|
478
|
+
describe 'get_file method' do
|
479
|
+
before do
|
480
|
+
@url = "#@api_base_url/v1/files/xx-xx-xx-xx"
|
481
|
+
|
482
|
+
@request_headers = {'Authorization' => /\ABearer (.+)\.(.+)\.(.+)\z/}
|
483
|
+
|
484
|
+
@response_body = 'BODY'
|
485
|
+
|
486
|
+
@response = {body: @response_body, headers: {'Content-Type' => 'application/octet-stream'}}
|
487
|
+
end
|
488
|
+
|
489
|
+
it 'fetches the file resource with the given id and returns the response body' do
|
490
|
+
@request = stub_request(:get, @url).with(headers: @request_headers).to_return(@response)
|
491
|
+
|
492
|
+
@client.get_file('xx-xx-xx-xx').must_equal(@response_body)
|
493
|
+
end
|
494
|
+
|
495
|
+
it 'fetches the file resource with the given url and returns the response body' do
|
496
|
+
@request = stub_request(:get, @url).with(headers: @request_headers).to_return(@response)
|
497
|
+
|
498
|
+
@client.get_file(@url).must_equal(@response_body)
|
499
|
+
end
|
500
|
+
end
|
501
|
+
|
488
502
|
describe 'check_signature method' do
|
489
503
|
it 'returns true if the signature in the given params is correct' do
|
490
504
|
params = {'a' => '1', 'b' => '2', 'timestamp' => '1461605396', 'sig' => '6af838ef94998832dbfc29020b564830'}
|
@@ -513,6 +527,18 @@ describe 'Nexmo::Client' do
|
|
513
527
|
proc { @client.get_balance }.must_raise(Nexmo::ServerError)
|
514
528
|
end
|
515
529
|
|
530
|
+
it 'provides an accessor to set the authorization token' do
|
531
|
+
auth_token = 'auth_token_xxx'
|
532
|
+
|
533
|
+
headers = {'Authorization' => "Bearer #{auth_token}"}
|
534
|
+
|
535
|
+
@request = stub_request(:get, "#@api_base_url/v1/calls").with(headers: headers).to_return(@response_body)
|
536
|
+
|
537
|
+
@client.auth_token = auth_token
|
538
|
+
|
539
|
+
@client.get_calls
|
540
|
+
end
|
541
|
+
|
516
542
|
it 'includes a user-agent header with the library version number and ruby version number' do
|
517
543
|
headers = {'User-Agent' => "nexmo-ruby/#{Nexmo::VERSION} ruby/#{RUBY_VERSION}"}
|
518
544
|
|
@@ -582,7 +608,7 @@ describe 'Nexmo::Client' do
|
|
582
608
|
def expect_put(url, data)
|
583
609
|
body = WebMock::Util::QueryMapper.query_to_values(data)
|
584
610
|
|
585
|
-
headers = {'Content-Type' => 'application/
|
611
|
+
headers = {'Content-Type' => 'application/json'}
|
586
612
|
|
587
613
|
@request = stub_request(:put, url).with(body: body, headers: headers).to_return(@response_body)
|
588
614
|
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'nexmo'
|
3
|
+
|
4
|
+
describe 'Nexmo::JWT' do
|
5
|
+
describe 'auth_token method' do
|
6
|
+
it 'returns the payload encoded with the private key' do
|
7
|
+
time = Time.now.to_i
|
8
|
+
|
9
|
+
payload = {
|
10
|
+
'application_id' => application_id,
|
11
|
+
'iat' => time,
|
12
|
+
'exp' => time + 3600,
|
13
|
+
'jti' => 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
|
14
|
+
}
|
15
|
+
|
16
|
+
encoded_token = Nexmo::JWT.auth_token(payload, private_key)
|
17
|
+
|
18
|
+
decode(encoded_token).must_equal(payload)
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'sets a default value for the iat parameter' do
|
22
|
+
encoded_token = Nexmo::JWT.auth_token({}, private_key)
|
23
|
+
|
24
|
+
decode(encoded_token).fetch('iat').must_be_kind_of(Integer)
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'sets a default value for the exp parameter' do
|
28
|
+
encoded_token = Nexmo::JWT.auth_token({}, private_key)
|
29
|
+
|
30
|
+
decode(encoded_token).fetch('exp').must_be_kind_of(Integer)
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'sets a default value for the jti parameter' do
|
34
|
+
encoded_token = Nexmo::JWT.auth_token({}, private_key)
|
35
|
+
|
36
|
+
decode(encoded_token).fetch('jti').must_match(/\A[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\z/)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def decode(encoded_token)
|
43
|
+
JWT.decode(encoded_token, private_key, 'RS256').first
|
44
|
+
end
|
45
|
+
|
46
|
+
def application_id
|
47
|
+
@application_id ||= SecureRandom.uuid
|
48
|
+
end
|
49
|
+
|
50
|
+
def private_key
|
51
|
+
@private_key ||= OpenSSL::PKey::RSA.new(1024)
|
52
|
+
end
|
53
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nexmo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.
|
4
|
+
version: 4.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tim Craft
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-04-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: jwt
|
@@ -76,6 +76,7 @@ files:
|
|
76
76
|
- LICENSE.txt
|
77
77
|
- README.md
|
78
78
|
- lib/nexmo.rb
|
79
|
+
- lib/nexmo/client.rb
|
79
80
|
- lib/nexmo/errors/authentication_error.rb
|
80
81
|
- lib/nexmo/errors/client_error.rb
|
81
82
|
- lib/nexmo/errors/error.rb
|
@@ -85,7 +86,8 @@ files:
|
|
85
86
|
- lib/nexmo/signature.rb
|
86
87
|
- lib/nexmo/version.rb
|
87
88
|
- nexmo.gemspec
|
88
|
-
- spec/
|
89
|
+
- spec/nexmo/client_spec.rb
|
90
|
+
- spec/nexmo/jwt_spec.rb
|
89
91
|
homepage: https://github.com/Nexmo/nexmo-ruby
|
90
92
|
licenses:
|
91
93
|
- MIT
|