nexmo 6.3.0 → 7.2.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 +28 -9
- data/lib/nexmo.rb +1 -1
- data/lib/nexmo/abstract_authentication.rb +0 -2
- data/lib/nexmo/account.rb +5 -1
- data/lib/nexmo/alerts.rb +5 -1
- data/lib/nexmo/applications.rb +24 -4
- data/lib/nexmo/client.rb +45 -24
- data/lib/nexmo/config.rb +43 -10
- data/lib/nexmo/conversations.rb +24 -1
- data/lib/nexmo/conversations/events.rb +1 -1
- data/lib/nexmo/conversations/legs.rb +1 -1
- data/lib/nexmo/conversations/members.rb +1 -1
- data/lib/nexmo/conversations/users.rb +1 -1
- data/lib/nexmo/conversions.rb +4 -1
- data/lib/nexmo/entity.rb +2 -2
- data/lib/nexmo/errors.rb +8 -1
- data/lib/nexmo/files.rb +7 -3
- data/lib/nexmo/gsm7.rb +0 -2
- data/lib/nexmo/http.rb +12 -4
- data/lib/nexmo/json.rb +4 -1
- data/lib/nexmo/jwt.rb +11 -12
- data/lib/nexmo/key_secret_params.rb +9 -3
- data/lib/nexmo/keys.rb +24 -3
- data/lib/nexmo/logger.rb +14 -5
- data/lib/nexmo/messages.rb +6 -1
- data/lib/nexmo/namespace.rb +2 -10
- data/lib/nexmo/number_insight.rb +21 -7
- data/lib/nexmo/numbers.rb +1 -1
- data/lib/nexmo/pricing.rb +1 -1
- data/lib/nexmo/pricing_types.rb +1 -1
- data/lib/nexmo/redact.rb +4 -1
- data/lib/nexmo/response.rb +1 -1
- data/lib/nexmo/secrets.rb +1 -1
- data/lib/nexmo/signature.rb +1 -1
- data/lib/nexmo/sms.rb +10 -8
- data/lib/nexmo/tfa.rb +1 -1
- data/lib/nexmo/verify.rb +93 -18
- data/lib/nexmo/version.rb +1 -1
- data/lib/nexmo/{calls.rb → voice.rb} +12 -12
- data/lib/nexmo/{calls → voice}/dtmf.rb +2 -2
- data/lib/nexmo/{calls → voice}/list_response.rb +1 -1
- data/lib/nexmo/{calls → voice}/stream.rb +2 -2
- data/lib/nexmo/{calls → voice}/talk.rb +2 -2
- data/nexmo.gemspec +2 -1
- metadata +25 -14
- data/lib/nexmo/number_insight/response.rb +0 -7
- data/lib/nexmo/sms/response.rb +0 -8
- data/lib/nexmo/verify/response.rb +0 -7
data/lib/nexmo/conversations.rb
CHANGED
@@ -1,8 +1,10 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
module Nexmo
|
5
5
|
class Conversations < Namespace
|
6
|
+
extend T::Sig
|
7
|
+
|
6
8
|
self.authentication = BearerToken
|
7
9
|
|
8
10
|
self.request_body = JSON
|
@@ -33,6 +35,7 @@ module Nexmo
|
|
33
35
|
#
|
34
36
|
# @see https://developer.nexmo.com/api/conversation#createConversation
|
35
37
|
#
|
38
|
+
sig { params(params: T::Hash[Symbol, T.untyped]).returns(Nexmo::Response) }
|
36
39
|
def create(params)
|
37
40
|
request('/beta/conversations', params: params, type: Post)
|
38
41
|
end
|
@@ -63,6 +66,7 @@ module Nexmo
|
|
63
66
|
#
|
64
67
|
# @see https://developer.nexmo.com/api/conversation#replaceConversation
|
65
68
|
#
|
69
|
+
sig { params(params: T.nilable(T::Hash[Symbol, T.untyped])).returns(Nexmo::Response) }
|
66
70
|
def list(params = nil)
|
67
71
|
request('/beta/conversations', params: params)
|
68
72
|
end
|
@@ -78,6 +82,7 @@ module Nexmo
|
|
78
82
|
#
|
79
83
|
# @see https://developer.nexmo.com/api/conversation#retrieveConversation
|
80
84
|
#
|
85
|
+
sig { params(id: String).returns(Nexmo::Response) }
|
81
86
|
def get(id)
|
82
87
|
request('/beta/conversations/' + id)
|
83
88
|
end
|
@@ -110,6 +115,10 @@ module Nexmo
|
|
110
115
|
#
|
111
116
|
# @see https://developer.nexmo.com/api/conversation#replaceConversation
|
112
117
|
#
|
118
|
+
sig { params(
|
119
|
+
id: String,
|
120
|
+
params: T::Hash[Symbol, T.untyped]
|
121
|
+
).returns(Nexmo::Response) }
|
113
122
|
def update(id, params)
|
114
123
|
request('/beta/conversations/' + id, params: params, type: Put)
|
115
124
|
end
|
@@ -125,6 +134,7 @@ module Nexmo
|
|
125
134
|
#
|
126
135
|
# @see https://developer.nexmo.com/api/conversation#deleteConversation
|
127
136
|
#
|
137
|
+
sig { params(id: String).returns(Nexmo::Response) }
|
128
138
|
def delete(id)
|
129
139
|
request('/beta/conversations/' + id, type: Delete)
|
130
140
|
end
|
@@ -156,31 +166,44 @@ module Nexmo
|
|
156
166
|
#
|
157
167
|
# @see https://developer.nexmo.com/api/conversation#recordConversation
|
158
168
|
#
|
169
|
+
sig { params(
|
170
|
+
id: String,
|
171
|
+
params: T::Hash[Symbol, T.untyped]
|
172
|
+
).returns(Nexmo::Response) }
|
159
173
|
def record(id, params)
|
160
174
|
request('/v1/conversations/' + id + '/record', params: params, type: Put)
|
161
175
|
end
|
162
176
|
|
163
177
|
# @return [Events]
|
164
178
|
#
|
179
|
+
sig { returns(T.nilable(Nexmo::Conversations::Events)) }
|
165
180
|
def events
|
181
|
+
@events = T.let(@events, T.nilable(Nexmo::Conversations::Events))
|
182
|
+
@config = T.let(@config, T.nilable(Nexmo::Config))
|
166
183
|
@events ||= Events.new(@config)
|
167
184
|
end
|
168
185
|
|
169
186
|
# @return [Legs]
|
170
187
|
#
|
188
|
+
sig { returns(T.nilable(Nexmo::Conversations::Legs)) }
|
171
189
|
def legs
|
190
|
+
@legs = T.let(@legs, T.nilable(Nexmo::Conversations::Legs))
|
172
191
|
@legs ||= Legs.new(@config)
|
173
192
|
end
|
174
193
|
|
175
194
|
# @return [Members]
|
176
195
|
#
|
196
|
+
sig { returns(T.nilable(Nexmo::Conversations::Members)) }
|
177
197
|
def members
|
198
|
+
@members = T.let(@members, T.nilable(Nexmo::Conversations::Members))
|
178
199
|
@members ||= Members.new(@config)
|
179
200
|
end
|
180
201
|
|
181
202
|
# @return [Users]
|
182
203
|
#
|
204
|
+
sig { returns(T.nilable(Nexmo::Conversations::Users)) }
|
183
205
|
def users
|
206
|
+
@users = T.let(@users, T.nilable(Nexmo::Conversations::Users))
|
184
207
|
@users ||= Users.new(@config)
|
185
208
|
end
|
186
209
|
end
|
data/lib/nexmo/conversions.rb
CHANGED
@@ -1,14 +1,17 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
module Nexmo
|
5
5
|
class Conversions < Namespace
|
6
|
+
extend T::Sig
|
6
7
|
include Keys
|
7
8
|
|
9
|
+
sig { params(params: T::Hash[Symbol, T.untyped]).returns(Nexmo::Response) }
|
8
10
|
def track_sms(params)
|
9
11
|
request('/conversions/sms', params: hyphenate(params), type: Post)
|
10
12
|
end
|
11
13
|
|
14
|
+
sig { params(params: T::Hash[Symbol, T.untyped]).returns(Nexmo::Response) }
|
12
15
|
def track_voice(params)
|
13
16
|
request('/conversions/voice', params: hyphenate(params), type: Post)
|
14
17
|
end
|
data/lib/nexmo/entity.rb
CHANGED
data/lib/nexmo/errors.rb
CHANGED
@@ -1,9 +1,12 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
require 'json'
|
4
4
|
|
5
5
|
module Nexmo
|
6
6
|
module Errors
|
7
|
+
extend T::Sig
|
8
|
+
|
9
|
+
sig {params(response: T.any(Net::HTTPUnauthorized, Net::HTTPClientError, Net::HTTPServerError, T.untyped)).returns(Nexmo::Error)}
|
7
10
|
def self.parse(response)
|
8
11
|
exception_class = case response
|
9
12
|
when Net::HTTPUnauthorized
|
@@ -21,6 +24,8 @@ module Nexmo
|
|
21
24
|
|
22
25
|
if hash.key?('error_title')
|
23
26
|
hash['error_title']
|
27
|
+
elsif hash.key?('error-code-label')
|
28
|
+
hash['error-code-label']
|
24
29
|
elsif hash.key?('description')
|
25
30
|
hash['description']
|
26
31
|
elsif problem_details?(hash)
|
@@ -31,10 +36,12 @@ module Nexmo
|
|
31
36
|
exception_class.new(message)
|
32
37
|
end
|
33
38
|
|
39
|
+
sig { params(hash: T::Hash[String, T.untyped]).returns(T::Boolean) }
|
34
40
|
def self.problem_details?(hash)
|
35
41
|
hash.key?('title') && hash.key?('detail') && hash.key?('type')
|
36
42
|
end
|
37
43
|
|
44
|
+
sig { params(hash: T::Hash[String, T.untyped]).returns(String) }
|
38
45
|
def self.problem_details_message(hash)
|
39
46
|
"#{hash['title']}. #{hash['detail']} See #{hash['type']} for more info, or email support@nexmo.com if you have any questions."
|
40
47
|
end
|
data/lib/nexmo/files.rb
CHANGED
@@ -1,16 +1,20 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
module Nexmo
|
5
5
|
class Files < Namespace
|
6
|
+
extend T::Sig
|
7
|
+
|
6
8
|
self.authentication = BearerToken
|
7
9
|
|
10
|
+
sig { params(id: String).returns(T.nilable(Nexmo::Response)) }
|
8
11
|
def get(id)
|
9
|
-
request('/v1/files/' + id.split('/').last)
|
12
|
+
request('/v1/files/' + T.must(id.split('/').last))
|
10
13
|
end
|
11
14
|
|
15
|
+
sig { params(id: String, filename: String).returns(T.nilable(Nexmo::Response)) }
|
12
16
|
def save(id, filename)
|
13
|
-
request('/v1/files/' + id.split('/').last) do |response|
|
17
|
+
request('/v1/files/' + T.must(id.split('/').last)) do |response|
|
14
18
|
File.open(filename, 'wb') do |file|
|
15
19
|
response.read_body do |chunk|
|
16
20
|
file.write(chunk)
|
data/lib/nexmo/gsm7.rb
CHANGED
data/lib/nexmo/http.rb
CHANGED
@@ -1,12 +1,18 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
require 'net/http'
|
4
4
|
|
5
5
|
module Nexmo
|
6
6
|
module HTTP
|
7
7
|
class Options
|
8
|
+
extend T::Sig
|
9
|
+
|
10
|
+
sig { params(hash: T::Hash[Symbol, T.untyped]).void }
|
8
11
|
def initialize(hash)
|
9
|
-
|
12
|
+
raise ArgumentError, 'hash parameter cannot be empty or nil' if hash == {} || hash.nil?
|
13
|
+
|
14
|
+
@hash = T.let(@hash, T::Hash[Symbol, T.untyped]) if defined? @hash
|
15
|
+
@hash = hash
|
10
16
|
|
11
17
|
@hash.each_key do |name|
|
12
18
|
next if defined_options.key?(name)
|
@@ -15,6 +21,7 @@ module Nexmo
|
|
15
21
|
end
|
16
22
|
end
|
17
23
|
|
24
|
+
sig { params(http: Net::HTTP).returns(T::Hash[Symbol, T.untyped]) }
|
18
25
|
def set(http)
|
19
26
|
@hash.each do |name, value|
|
20
27
|
http.public_send(defined_options.fetch(name), value)
|
@@ -23,13 +30,14 @@ module Nexmo
|
|
23
30
|
|
24
31
|
private
|
25
32
|
|
33
|
+
sig { returns(T::Hash[Symbol, T.untyped]) }
|
26
34
|
def defined_options
|
35
|
+
@defined_options = T.let(@defined_options, T.nilable(T::Hash[Symbol, T.untyped]))
|
36
|
+
|
27
37
|
@defined_options ||= Net::HTTP.instance_methods.grep(/\w=\z/).each_with_object({}) do |name, hash|
|
28
38
|
hash[name.to_s.chomp('=').to_sym] = name
|
29
39
|
end
|
30
40
|
end
|
31
41
|
end
|
32
42
|
end
|
33
|
-
|
34
|
-
private_constant :HTTP
|
35
43
|
end
|
data/lib/nexmo/json.rb
CHANGED
@@ -1,9 +1,12 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
require 'json'
|
4
4
|
|
5
5
|
module Nexmo
|
6
6
|
module JSON
|
7
|
+
extend T::Sig
|
8
|
+
|
9
|
+
sig { params(http_request: T.any(Net::HTTP::Put, Net::HTTP::Post), params: T::Hash[Symbol, T.untyped]).void }
|
7
10
|
def self.update(http_request, params)
|
8
11
|
http_request['Content-Type'] = 'application/json'
|
9
12
|
http_request.body = ::JSON.generate(params)
|
data/lib/nexmo/jwt.rb
CHANGED
@@ -2,10 +2,10 @@
|
|
2
2
|
# frozen_string_literal: true
|
3
3
|
require 'securerandom'
|
4
4
|
require 'openssl'
|
5
|
-
require 'jwt'
|
5
|
+
require 'nexmo-jwt'
|
6
6
|
|
7
7
|
module Nexmo
|
8
|
-
|
8
|
+
class JWT
|
9
9
|
# Generate an encoded JSON Web Token.
|
10
10
|
#
|
11
11
|
# By default the Nexmo Ruby SDK generates a short lived JWT per request.
|
@@ -14,12 +14,14 @@ module Nexmo
|
|
14
14
|
# directly call {Nexmo::JWT.generate} to generate a token, and set the token
|
15
15
|
# attribute on the client object.
|
16
16
|
#
|
17
|
+
# Documentation for the Nexmo Ruby JWT generator gem can be found at
|
18
|
+
# https://www.rubydoc.info/github/nexmo/nexmo-jwt-ruby
|
19
|
+
#
|
17
20
|
# @example
|
18
21
|
# claims = {
|
19
22
|
# application_id: application_id,
|
20
|
-
#
|
21
|
-
#
|
22
|
-
# iat: 1483228800
|
23
|
+
# ttl: 800,
|
24
|
+
# subject: 'My_Subject'
|
23
25
|
# }
|
24
26
|
#
|
25
27
|
# private_key = File.read('path/to/private.key')
|
@@ -31,14 +33,11 @@ module Nexmo
|
|
31
33
|
#
|
32
34
|
# @return [String]
|
33
35
|
#
|
34
|
-
def self.generate(payload, private_key)
|
35
|
-
payload
|
36
|
-
payload[:exp] = iat + 60 unless payload.key?(:exp) || payload.key?('exp')
|
37
|
-
payload[:jti] = SecureRandom.uuid unless payload.key?(:jti) || payload.key?('jti')
|
38
|
-
|
39
|
-
private_key = OpenSSL::PKey::RSA.new(private_key) unless private_key.respond_to?(:sign)
|
36
|
+
def self.generate(payload, private_key = nil)
|
37
|
+
raise "Expecting 'private_key' in either the payload or as a separate parameter" if !payload[:private_key] && !private_key
|
40
38
|
|
41
|
-
|
39
|
+
payload[:private_key] = private_key if private_key && !payload[:private_key]
|
40
|
+
@token = Nexmo::JWTBuilder.new(payload).jwt.generate
|
42
41
|
end
|
43
42
|
end
|
44
43
|
end
|
@@ -1,12 +1,18 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
|
3
3
|
module Nexmo
|
4
4
|
class KeySecretParams < AbstractAuthentication
|
5
|
+
extend T::Sig
|
6
|
+
|
7
|
+
sig { params(
|
8
|
+
object: T.any(T::Hash[T.untyped, T.untyped], URI::HTTPS, Net::HTTP::Post, Net::HTTP::Get)
|
9
|
+
).void }
|
5
10
|
def update(object)
|
6
11
|
return unless object.is_a?(Hash)
|
7
12
|
|
8
|
-
|
9
|
-
object[:
|
13
|
+
@config = T.let(@config, T.nilable(Nexmo::Config))
|
14
|
+
object[:api_key] = T.must(@config).api_key
|
15
|
+
object[:api_secret] = T.must(@config).api_secret
|
10
16
|
end
|
11
17
|
end
|
12
18
|
|
data/lib/nexmo/keys.rb
CHANGED
@@ -1,17 +1,37 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
module Nexmo
|
5
5
|
module Keys
|
6
|
+
extend T::Sig
|
7
|
+
|
8
|
+
sig { params(hash: T::Hash[T.untyped, T.untyped]).returns(T::Hash[String, T.untyped]) }
|
6
9
|
def hyphenate(hash)
|
7
10
|
hash.transform_keys { |k| k.to_s.tr('_', '-') }
|
8
11
|
end
|
9
12
|
|
13
|
+
sig { params(hash: T::Hash[T.untyped, T.untyped]).returns(T::Hash[T.untyped, T.untyped]) }
|
10
14
|
def camelcase(hash)
|
11
|
-
|
15
|
+
exceptions = [
|
16
|
+
'dr_call_back_url',
|
17
|
+
'mo_http_url',
|
18
|
+
'mo_smpp_sys_type',
|
19
|
+
'mo_call_back_url',
|
20
|
+
'voice_callback_type',
|
21
|
+
'voice_callback_value',
|
22
|
+
'voice_status_callback',
|
23
|
+
'messages_callback_value',
|
24
|
+
'messages_callback_type'
|
25
|
+
]
|
26
|
+
hash.transform_keys do |k|
|
27
|
+
if exceptions.include?(k.to_s)
|
28
|
+
next k.to_s.gsub(/_(\w)/) { $1.upcase.to_s }
|
29
|
+
end
|
30
|
+
k
|
31
|
+
end
|
12
32
|
end
|
13
33
|
|
14
|
-
ATTRIBUTE_KEYS = Hash.new { |h, k| h[k] = k.split(PATTERN).join('_').downcase.to_sym }
|
34
|
+
ATTRIBUTE_KEYS = T.let(Hash.new { |h, k| h[k] = k.split(PATTERN).join('_').downcase.to_sym }, T::Hash[T.untyped, T.untyped])
|
15
35
|
|
16
36
|
PATTERN = /[\-_]|(?<=\w)(?=[A-Z])/
|
17
37
|
|
@@ -19,6 +39,7 @@ module Nexmo
|
|
19
39
|
|
20
40
|
private_constant :PATTERN
|
21
41
|
|
42
|
+
sig { params(k: T.any(Symbol, String)).returns(Symbol) }
|
22
43
|
def attribute_key(k)
|
23
44
|
return k if k.is_a?(Symbol)
|
24
45
|
|
data/lib/nexmo/logger.rb
CHANGED
@@ -1,10 +1,13 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: false
|
2
2
|
# frozen_string_literal: true
|
3
3
|
require 'logger'
|
4
4
|
require 'forwardable'
|
5
5
|
|
6
6
|
module Nexmo
|
7
7
|
class Logger
|
8
|
+
extend T::Sig
|
9
|
+
|
10
|
+
sig { params(logger: T.nilable(T.any(::Logger, Nexmo::Logger))).void }
|
8
11
|
def initialize(logger)
|
9
12
|
@logger = logger || ::Logger.new(nil)
|
10
13
|
end
|
@@ -15,8 +18,11 @@ module Nexmo
|
|
15
18
|
def_delegator :@logger, name, name
|
16
19
|
end
|
17
20
|
|
21
|
+
sig { params(request: T.any(Net::HTTP::Post, Net::HTTP::Get, Net::HTTP::Delete, Net::HTTP::Put)).void }
|
18
22
|
def log_request_info(request)
|
19
|
-
@logger.
|
23
|
+
@logger = T.let(@logger, T.nilable(T.any(::Logger, Nexmo::Logger)))
|
24
|
+
|
25
|
+
T.must(@logger).info do
|
20
26
|
format('Nexmo API request', {
|
21
27
|
method: request.method,
|
22
28
|
path: request.uri.path
|
@@ -24,8 +30,12 @@ module Nexmo
|
|
24
30
|
end
|
25
31
|
end
|
26
32
|
|
33
|
+
sig { params(
|
34
|
+
response: T.any(Net::HTTPOK, Net::HTTPNoContent, Net::HTTPBadRequest, Net::HTTPInternalServerError, Net::HTTPResponse),
|
35
|
+
host: String
|
36
|
+
).void }
|
27
37
|
def log_response_info(response, host)
|
28
|
-
@logger.info do
|
38
|
+
T.must(@logger).info do
|
29
39
|
format('Nexmo API response', {
|
30
40
|
host: host,
|
31
41
|
status: response.code,
|
@@ -38,6 +48,7 @@ module Nexmo
|
|
38
48
|
|
39
49
|
private
|
40
50
|
|
51
|
+
sig { params(message: String, hash: T::Hash[Symbol, T.untyped]).returns(String) }
|
41
52
|
def format(message, hash)
|
42
53
|
return message if hash.nil?
|
43
54
|
|
@@ -46,6 +57,4 @@ module Nexmo
|
|
46
57
|
fields.join(' ')
|
47
58
|
end
|
48
59
|
end
|
49
|
-
|
50
|
-
private_constant :Logger
|
51
60
|
end
|