nexmo 6.3.0 → 7.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +28 -9
  3. data/lib/nexmo.rb +1 -1
  4. data/lib/nexmo/abstract_authentication.rb +0 -2
  5. data/lib/nexmo/account.rb +5 -1
  6. data/lib/nexmo/alerts.rb +5 -1
  7. data/lib/nexmo/applications.rb +24 -4
  8. data/lib/nexmo/client.rb +45 -24
  9. data/lib/nexmo/config.rb +43 -10
  10. data/lib/nexmo/conversations.rb +24 -1
  11. data/lib/nexmo/conversations/events.rb +1 -1
  12. data/lib/nexmo/conversations/legs.rb +1 -1
  13. data/lib/nexmo/conversations/members.rb +1 -1
  14. data/lib/nexmo/conversations/users.rb +1 -1
  15. data/lib/nexmo/conversions.rb +4 -1
  16. data/lib/nexmo/entity.rb +2 -2
  17. data/lib/nexmo/errors.rb +8 -1
  18. data/lib/nexmo/files.rb +7 -3
  19. data/lib/nexmo/gsm7.rb +0 -2
  20. data/lib/nexmo/http.rb +12 -4
  21. data/lib/nexmo/json.rb +4 -1
  22. data/lib/nexmo/jwt.rb +11 -12
  23. data/lib/nexmo/key_secret_params.rb +9 -3
  24. data/lib/nexmo/keys.rb +24 -3
  25. data/lib/nexmo/logger.rb +14 -5
  26. data/lib/nexmo/messages.rb +6 -1
  27. data/lib/nexmo/namespace.rb +2 -10
  28. data/lib/nexmo/number_insight.rb +21 -7
  29. data/lib/nexmo/numbers.rb +1 -1
  30. data/lib/nexmo/pricing.rb +1 -1
  31. data/lib/nexmo/pricing_types.rb +1 -1
  32. data/lib/nexmo/redact.rb +4 -1
  33. data/lib/nexmo/response.rb +1 -1
  34. data/lib/nexmo/secrets.rb +1 -1
  35. data/lib/nexmo/signature.rb +1 -1
  36. data/lib/nexmo/sms.rb +10 -8
  37. data/lib/nexmo/tfa.rb +1 -1
  38. data/lib/nexmo/verify.rb +93 -18
  39. data/lib/nexmo/version.rb +1 -1
  40. data/lib/nexmo/{calls.rb → voice.rb} +12 -12
  41. data/lib/nexmo/{calls → voice}/dtmf.rb +2 -2
  42. data/lib/nexmo/{calls → voice}/list_response.rb +1 -1
  43. data/lib/nexmo/{calls → voice}/stream.rb +2 -2
  44. data/lib/nexmo/{calls → voice}/talk.rb +2 -2
  45. data/nexmo.gemspec +2 -1
  46. metadata +25 -14
  47. data/lib/nexmo/number_insight/response.rb +0 -7
  48. data/lib/nexmo/sms/response.rb +0 -8
  49. data/lib/nexmo/verify/response.rb +0 -7
@@ -1,8 +1,10 @@
1
- # typed: false
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
@@ -1,4 +1,4 @@
1
- # typed: false
1
+ # typed: true
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Nexmo
@@ -1,4 +1,4 @@
1
- # typed: false
1
+ # typed: true
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Nexmo
@@ -1,4 +1,4 @@
1
- # typed: false
1
+ # typed: true
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Nexmo
@@ -1,4 +1,4 @@
1
- # typed: false
1
+ # typed: true
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Nexmo
@@ -1,14 +1,17 @@
1
- # typed: false
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
@@ -1,4 +1,4 @@
1
- # typed: false
1
+ # typed: true
2
2
 
3
3
  module Nexmo
4
4
  class Entity
@@ -48,4 +48,4 @@ module Nexmo
48
48
 
49
49
  include Enumerable
50
50
  end
51
- end
51
+ end
@@ -1,9 +1,12 @@
1
- # typed: ignore
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
@@ -1,16 +1,20 @@
1
- # typed: false
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)
@@ -10,6 +10,4 @@ module Nexmo
10
10
  REGEXP =~ string
11
11
  end
12
12
  end
13
-
14
- private_constant :GSM7
15
13
  end
@@ -1,12 +1,18 @@
1
- # typed: ignore
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
- @hash = hash || {}
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
@@ -1,9 +1,12 @@
1
- # typed: ignore
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)
@@ -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
- module JWT
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
- # nbf: 1483315200,
21
- # exp: 1514764800,
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[:iat] = iat = Time.now.to_i unless payload.key?(:iat) || payload.key?('iat')
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
- ::JWT.encode(payload, private_key, 'RS256')
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: ignore
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
- object[:api_key] = @config.api_key
9
- object[:api_secret] = @config.api_secret
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
 
@@ -1,17 +1,37 @@
1
- # typed: ignore
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
- hash.transform_keys { |k| k.to_s.gsub(/_(\w)/) { $1.upcase } }
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
 
@@ -1,10 +1,13 @@
1
- # typed: ignore
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.info do
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