nexmo 4.5.0 → 4.6.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![Gem Version](https://badge.fury.io/rb/nexmo.svg)](https://badge.fury.io/rb/nexmo) [![Build Status](https://api.travis-ci.org/Nexmo/nexmo-ruby.svg?branch=master)](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
|