nexmo 6.1.0 → 7.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +24 -4
  4. data/lib/nexmo.rb +12 -14
  5. data/lib/nexmo/abstract_authentication.rb +2 -0
  6. data/lib/nexmo/account.rb +6 -1
  7. data/lib/nexmo/alerts.rb +6 -1
  8. data/lib/nexmo/applications.rb +24 -3
  9. data/lib/nexmo/applications/list_response.rb +2 -0
  10. data/lib/nexmo/authentication_error.rb +2 -0
  11. data/lib/nexmo/basic.rb +2 -0
  12. data/lib/nexmo/bearer_token.rb +1 -0
  13. data/lib/nexmo/client.rb +46 -23
  14. data/lib/nexmo/client_error.rb +2 -0
  15. data/lib/nexmo/config.rb +49 -9
  16. data/lib/nexmo/conversations.rb +24 -0
  17. data/lib/nexmo/conversations/events.rb +1 -0
  18. data/lib/nexmo/conversations/legs.rb +1 -0
  19. data/lib/nexmo/conversations/members.rb +1 -0
  20. data/lib/nexmo/conversations/users.rb +1 -0
  21. data/lib/nexmo/conversions.rb +4 -0
  22. data/lib/nexmo/entity.rb +5 -3
  23. data/lib/nexmo/error.rb +2 -0
  24. data/lib/nexmo/errors.rb +8 -0
  25. data/lib/nexmo/files.rb +7 -2
  26. data/lib/nexmo/form_data.rb +2 -0
  27. data/lib/nexmo/gsm7.rb +2 -0
  28. data/lib/nexmo/http.rb +12 -3
  29. data/lib/nexmo/json.rb +4 -0
  30. data/lib/nexmo/jwt.rb +5 -1
  31. data/lib/nexmo/key_secret_params.rb +10 -2
  32. data/lib/nexmo/keys.rb +7 -1
  33. data/lib/nexmo/logger.rb +14 -4
  34. data/lib/nexmo/messages.rb +7 -1
  35. data/lib/nexmo/namespace.rb +15 -18
  36. data/lib/nexmo/number_insight.rb +21 -6
  37. data/lib/nexmo/numbers.rb +24 -20
  38. data/lib/nexmo/numbers/list_response.rb +2 -0
  39. data/lib/nexmo/numbers/response.rb +1 -0
  40. data/lib/nexmo/params.rb +1 -0
  41. data/lib/nexmo/pricing.rb +2 -1
  42. data/lib/nexmo/pricing_types.rb +1 -0
  43. data/lib/nexmo/redact.rb +2 -1
  44. data/lib/nexmo/response.rb +2 -0
  45. data/lib/nexmo/secrets.rb +1 -0
  46. data/lib/nexmo/secrets/list_response.rb +2 -0
  47. data/lib/nexmo/server_error.rb +2 -0
  48. data/lib/nexmo/signature.rb +1 -0
  49. data/lib/nexmo/sms.rb +16 -10
  50. data/lib/nexmo/tfa.rb +2 -1
  51. data/lib/nexmo/user_agent.rb +1 -0
  52. data/lib/nexmo/verify.rb +93 -17
  53. data/lib/nexmo/version.rb +3 -1
  54. data/lib/nexmo/{calls.rb → voice.rb} +12 -11
  55. data/lib/nexmo/{calls → voice}/dtmf.rb +2 -1
  56. data/lib/nexmo/{calls → voice}/list_response.rb +3 -1
  57. data/lib/nexmo/{calls → voice}/stream.rb +2 -1
  58. data/lib/nexmo/{calls → voice}/talk.rb +2 -1
  59. data/nexmo.gemspec +2 -7
  60. metadata +17 -85
  61. data/lib/nexmo/key_secret_query.rb +0 -20
  62. data/lib/nexmo/number_insight/response.rb +0 -5
  63. data/lib/nexmo/sms/response.rb +0 -7
  64. data/lib/nexmo/verify/response.rb +0 -5
@@ -1,8 +1,12 @@
1
+ # typed: strict
1
2
  # frozen_string_literal: true
2
3
  require 'json'
3
4
 
4
5
  module Nexmo
5
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 }
6
10
  def self.update(http_request, params)
7
11
  http_request['Content-Type'] = 'application/json'
8
12
  http_request.body = ::JSON.generate(params)
@@ -1,3 +1,4 @@
1
+ # typed: strict
1
2
  # frozen_string_literal: true
2
3
  require 'securerandom'
3
4
  require 'openssl'
@@ -5,6 +6,8 @@ require 'jwt'
5
6
 
6
7
  module Nexmo
7
8
  module JWT
9
+ extend T::Sig
10
+
8
11
  # Generate an encoded JSON Web Token.
9
12
  #
10
13
  # By default the Nexmo Ruby SDK generates a short lived JWT per request.
@@ -30,9 +33,10 @@ module Nexmo
30
33
  #
31
34
  # @return [String]
32
35
  #
36
+ sig { params(payload: T::Hash[T.any(Symbol, String), T.any(String, Integer)], private_key: T.any(OpenSSL::PKey::RSA, String)).returns(String) }
33
37
  def self.generate(payload, private_key)
34
38
  payload[:iat] = iat = Time.now.to_i unless payload.key?(:iat) || payload.key?('iat')
35
- payload[:exp] = iat + 60 unless payload.key?(:exp) || payload.key?('exp')
39
+ payload[:exp] = T.must(iat) + 60 unless payload.key?(:exp) || payload.key?('exp')
36
40
  payload[:jti] = SecureRandom.uuid unless payload.key?(:jti) || payload.key?('jti')
37
41
 
38
42
  private_key = OpenSSL::PKey::RSA.new(private_key) unless private_key.respond_to?(:sign)
@@ -1,10 +1,18 @@
1
+ # typed: strict
2
+
1
3
  module Nexmo
2
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 }
3
10
  def update(object)
4
11
  return unless object.is_a?(Hash)
5
12
 
6
- object[:api_key] = @config.api_key
7
- 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
8
16
  end
9
17
  end
10
18
 
@@ -1,16 +1,21 @@
1
+ # typed: strict
1
2
  # frozen_string_literal: true
2
3
 
3
4
  module Nexmo
4
5
  module Keys
6
+ extend T::Sig
7
+
8
+ sig { params(hash: T::Hash[T.untyped, T.untyped]).returns(T::Hash[String, T.untyped]) }
5
9
  def hyphenate(hash)
6
10
  hash.transform_keys { |k| k.to_s.tr('_', '-') }
7
11
  end
8
12
 
13
+ sig { params(hash: T::Hash[T.untyped, T.untyped]).returns(T::Hash[String, T.untyped]) }
9
14
  def camelcase(hash)
10
15
  hash.transform_keys { |k| k.to_s.gsub(/_(\w)/) { $1.upcase } }
11
16
  end
12
17
 
13
- ATTRIBUTE_KEYS = Hash.new { |h, k| h[k] = k.split(PATTERN).join('_').downcase.to_sym }
18
+ ATTRIBUTE_KEYS = T.let(Hash.new { |h, k| h[k] = k.split(PATTERN).join('_').downcase.to_sym }, T::Hash[T.untyped, T.untyped])
14
19
 
15
20
  PATTERN = /[\-_]|(?<=\w)(?=[A-Z])/
16
21
 
@@ -18,6 +23,7 @@ module Nexmo
18
23
 
19
24
  private_constant :PATTERN
20
25
 
26
+ sig { params(k: T.any(Symbol, String)).returns(Symbol) }
21
27
  def attribute_key(k)
22
28
  return k if k.is_a?(Symbol)
23
29
 
@@ -1,9 +1,13 @@
1
+ # typed: false
1
2
  # frozen_string_literal: true
2
3
  require 'logger'
3
4
  require 'forwardable'
4
5
 
5
6
  module Nexmo
6
7
  class Logger
8
+ extend T::Sig
9
+
10
+ sig { params(logger: T.nilable(T.any(::Logger, Nexmo::Logger))).void }
7
11
  def initialize(logger)
8
12
  @logger = logger || ::Logger.new(nil)
9
13
  end
@@ -14,8 +18,11 @@ module Nexmo
14
18
  def_delegator :@logger, name, name
15
19
  end
16
20
 
21
+ sig { params(request: T.any(Net::HTTP::Post, Net::HTTP::Get, Net::HTTP::Delete, Net::HTTP::Put)).void }
17
22
  def log_request_info(request)
18
- info do
23
+ @logger = T.let(@logger, T.nilable(T.any(::Logger, Nexmo::Logger)))
24
+
25
+ T.must(@logger).info do
19
26
  format('Nexmo API request', {
20
27
  method: request.method,
21
28
  path: request.uri.path
@@ -23,8 +30,12 @@ module Nexmo
23
30
  end
24
31
  end
25
32
 
33
+ sig { params(
34
+ response: T.any(Net::HTTPOK, Net::HTTPNoContent, Net::HTTPBadRequest, Net::HTTPInternalServerError, Net::HTTPResponse),
35
+ host: String
36
+ ).void }
26
37
  def log_response_info(response, host)
27
- info do
38
+ T.must(@logger).info do
28
39
  format('Nexmo API response', {
29
40
  host: host,
30
41
  status: response.code,
@@ -37,6 +48,7 @@ module Nexmo
37
48
 
38
49
  private
39
50
 
51
+ sig { params(message: String, hash: T::Hash[Symbol, T.untyped]).returns(String) }
40
52
  def format(message, hash)
41
53
  return message if hash.nil?
42
54
 
@@ -45,6 +57,4 @@ module Nexmo
45
57
  fields.join(' ')
46
58
  end
47
59
  end
48
-
49
- private_constant :Logger
50
60
  end
@@ -1,17 +1,23 @@
1
+ # typed: strict
1
2
  # frozen_string_literal: true
2
3
 
3
4
  module Nexmo
4
5
  class Messages < Namespace
5
- self.host = 'rest.nexmo.com'
6
+ extend T::Sig
6
7
 
8
+ self.host = :rest_host
9
+
10
+ sig { params(id: String).returns(Nexmo::Response) }
7
11
  def get(id)
8
12
  request('/search/message', params: {id: id})
9
13
  end
10
14
 
15
+ sig { params(params: T::Hash[Symbol, T.untyped]).returns(Nexmo::Response) }
11
16
  def search(params)
12
17
  request('/search/messages', params: params)
13
18
  end
14
19
 
20
+ sig { params(params: T::Hash[Symbol, T.untyped]).returns(Nexmo::Response) }
15
21
  def rejections(params)
16
22
  request('/search/rejections', params: params)
17
23
  end
@@ -1,3 +1,4 @@
1
+ # typed: ignore
1
2
  # frozen_string_literal: true
2
3
  require 'net/http'
3
4
  require 'json'
@@ -7,9 +8,7 @@ module Nexmo
7
8
  def initialize(config)
8
9
  @config = config
9
10
 
10
- @logger = config.logger
11
-
12
- @host = self.class.host
11
+ @host = self.class.host == :api_host ? @config.api_host : @config.rest_host
13
12
 
14
13
  @http = Net::HTTP.new(@host, Net::HTTP.https_default_port, p_addr = nil)
15
14
  @http.use_ssl = true
@@ -18,10 +17,12 @@ module Nexmo
18
17
  end
19
18
 
20
19
  def self.host
21
- @host ||= 'api.nexmo.com'
20
+ @host ||= :api_host
22
21
  end
23
22
 
24
23
  def self.host=(host)
24
+ raise ArgumentError unless host == :rest_host
25
+
25
26
  @host = host
26
27
  end
27
28
 
@@ -45,22 +46,14 @@ module Nexmo
45
46
  @request_headers ||= {}
46
47
  end
47
48
 
48
- def self.response_class
49
- @response_class ||= Response
50
- end
51
-
52
- def self.response_class=(response_class)
53
- @response_class = response_class
54
- end
55
-
56
- private
49
+ protected
57
50
 
58
51
  Get = Net::HTTP::Get
59
52
  Put = Net::HTTP::Put
60
53
  Post = Net::HTTP::Post
61
54
  Delete = Net::HTTP::Delete
62
55
 
63
- def request(path, params: nil, type: Get, response_class: nil, &block)
56
+ def request(path, params: nil, type: Get, response_class: Response, &block)
64
57
  uri = URI('https://' + @host + path)
65
58
 
66
59
  params ||= {}
@@ -86,17 +79,17 @@ module Nexmo
86
79
 
87
80
  self.class.request_body.update(message, params) if type::REQUEST_HAS_BODY
88
81
 
89
- @logger.log_request_info(message)
82
+ logger.log_request_info(message)
90
83
 
91
84
  response = @http.request(message, &block)
92
85
 
93
- @logger.log_response_info(response, @host)
86
+ logger.log_response_info(response, @host)
94
87
 
95
88
  return if block
96
89
 
97
- @logger.debug(response.body) if response.body
90
+ logger.debug(response.body) if response.body
98
91
 
99
- parse(response, response_class || self.class.response_class)
92
+ parse(response, response_class)
100
93
  end
101
94
 
102
95
  def parse(response, response_class)
@@ -115,6 +108,10 @@ module Nexmo
115
108
  raise Errors.parse(response)
116
109
  end
117
110
  end
111
+
112
+ def logger
113
+ @config.logger
114
+ end
118
115
  end
119
116
 
120
117
  private_constant :Namespace
@@ -1,9 +1,8 @@
1
+ # typed: true
1
2
  # frozen_string_literal: true
2
3
 
3
4
  module Nexmo
4
5
  class NumberInsight < Namespace
5
- self.response_class = Response
6
-
7
6
  # Provides basic number insight information about a number.
8
7
  #
9
8
  # @example
@@ -24,7 +23,11 @@ module Nexmo
24
23
  # @see https://developer.nexmo.com/api/number-insight#getNumberInsightBasic
25
24
  #
26
25
  def basic(params)
27
- request('/ni/basic/json', params: params)
26
+ response = request('/ni/basic/json', params: params)
27
+
28
+ raise Error, response[:status_message] unless response.status.zero?
29
+
30
+ response
28
31
  end
29
32
 
30
33
  # Provides standard number insight information about a number.
@@ -52,7 +55,11 @@ module Nexmo
52
55
  # @see https://developer.nexmo.com/api/number-insight#getNumberInsightStandard
53
56
  #
54
57
  def standard(params)
55
- request('/ni/standard/json', params: params)
58
+ response = request('/ni/standard/json', params: params)
59
+
60
+ raise Error, response[:status_message] unless response.status.zero?
61
+
62
+ response
56
63
  end
57
64
 
58
65
  # Provides advanced number insight information about a number synchronously.
@@ -84,7 +91,11 @@ module Nexmo
84
91
  # @see https://developer.nexmo.com/api/number-insight#getNumberInsightAdvanced
85
92
  #
86
93
  def advanced(params)
87
- request('/ni/advanced/json', params: params)
94
+ response = request('/ni/advanced/json', params: params)
95
+
96
+ raise Error, response[:status_message] unless response.status.zero?
97
+
98
+ response
88
99
  end
89
100
 
90
101
  # Provides advanced number insight number information *asynchronously* using the URL specified in the callback parameter.
@@ -119,7 +130,11 @@ module Nexmo
119
130
  # @see https://developer.nexmo.com/api/number-insight#getNumberInsightAsync
120
131
  #
121
132
  def advanced_async(params)
122
- request('/ni/advanced/async/json', params: params)
133
+ response = request('/ni/advanced/async/json', params: params)
134
+
135
+ raise Error, response[:status_message] unless response.status.zero?
136
+
137
+ response
123
138
  end
124
139
  end
125
140
  end
@@ -1,10 +1,11 @@
1
+ # typed: true
1
2
  # frozen_string_literal: true
2
3
 
3
4
  module Nexmo
4
5
  class Numbers < Namespace
5
6
  include Keys
6
7
 
7
- self.host = 'rest.nexmo.com'
8
+ self.host = :rest_host
8
9
 
9
10
  # Retrieve all the inbound numbers associated with your Nexmo account.
10
11
  #
@@ -14,11 +15,16 @@ module Nexmo
14
15
  # puts "#{item.msisdn} #{item.country} #{item.type}"
15
16
  # end
16
17
  #
17
- # @option params [Integer] :index
18
- # Page index.
18
+ # @option params [String] :application_id
19
+ # The application that you want to return the numbers for.
19
20
  #
20
- # @option params [Integer] :size
21
- # Page size.
21
+ # @option params [Boolean] :has_application
22
+ # Set this optional field to `true` to restrict your results to numbers associated with an application (any application).
23
+ # Set to `false` to find all numbers not associated with any application.
24
+ # Omit the field to avoid filtering on whether or not the number is assigned to an application.
25
+ #
26
+ # @option params [String] :country
27
+ # The two character country code to filter on (in ISO 3166-1 alpha-2 format).
22
28
  #
23
29
  # @option params [String] :pattern
24
30
  # The number pattern you want to search for. Use in conjunction with **:search_pattern**.
@@ -29,13 +35,11 @@ module Nexmo
29
35
  # - `1` - Search for numbers that contain **:pattern**
30
36
  # - `2` - Search for numbers that end with **:pattern**
31
37
  #
32
- # @option params [Boolean] :has_application
33
- # Set this optional field to `true` to restrict your results to numbers associated with an application (any application).
34
- # Set to `false` to find all numbers not associated with any application.
35
- # Omit the field to avoid filtering on whether or not the number is assigned to an application.
38
+ # @option params [Integer] :size
39
+ # Page size.
36
40
  #
37
- # @option params [String] :application_id
38
- # The application that you want to return the numbers for.
41
+ # @option params [Integer] :index
42
+ # Page index.
39
43
  #
40
44
  # @param [Hash] params
41
45
  #
@@ -102,6 +106,10 @@ module Nexmo
102
106
  # @option params [required, String] :msisdn
103
107
  # An available inbound virtual number.
104
108
  #
109
+ # @option params [String] :target_api_key
110
+ # If you'd like to perform an action on a subaccount, provide the `api_key` of that account here.
111
+ # If you'd like to perform an action on your own account, you do not need to provide this field.
112
+ #
105
113
  # @param [Hash] params
106
114
  #
107
115
  # @return [Response]
@@ -123,6 +131,10 @@ module Nexmo
123
131
  # @option params [required, String] :msisdn
124
132
  # An available inbound virtual number.
125
133
  #
134
+ # @option params [String] :target_api_key
135
+ # If you'd like to perform an action on a subaccount, provide the `api_key` of that account here.
136
+ # If you'd like to perform an action on your own account, you do not need to provide this field.
137
+ #
126
138
  # @param [Hash] params
127
139
  #
128
140
  # @return [Response]
@@ -160,16 +172,8 @@ module Nexmo
160
172
  # @option params [String] :mo_smpp_sys_type
161
173
  # The associated system type for your SMPP client.
162
174
  #
163
- # @option params [String] :messages_callback_type
164
- # The SMS webhook type (always `app`).
165
- # Must be used with the **:messages_callback_value** option.
166
- #
167
- # @option params [String] :messages_callback_value
168
- # A Nexmo Application ID.
169
- # Must be used with the **:messages_callback_type** option.
170
- #
171
175
  # @option params [String] :voice_callback_type
172
- # The voice webhook type.
176
+ # Specify whether inbound voice calls on your number are handled by your Application configuration, or forwarded to a SIP or a telephone number.
173
177
  # Must be used with the **:voice_callback_value** option.
174
178
  #
175
179
  # @option params [String] :voice_callback_value
@@ -1,3 +1,5 @@
1
+ # typed: ignore
2
+
1
3
  class Nexmo::Numbers::ListResponse < Nexmo::Response
2
4
  include Enumerable
3
5
 
@@ -1,3 +1,4 @@
1
+ # typed: false
1
2
  # frozen_string_literal: true
2
3
 
3
4
  class Nexmo::Numbers::Response < Nexmo::Response
@@ -1,3 +1,4 @@
1
+ # typed: ignore
1
2
  # frozen_string_literal: true
2
3
  require 'cgi'
3
4
 
@@ -1,8 +1,9 @@
1
+ # typed: true
1
2
  # frozen_string_literal: true
2
3
 
3
4
  module Nexmo
4
5
  class Pricing < Namespace
5
- self.host = 'rest.nexmo.com'
6
+ self.host = :rest_host
6
7
 
7
8
  def initialize(config, type: nil)
8
9
  raise ArgumentError if type.nil?
@@ -1,3 +1,4 @@
1
+ # typed: true
1
2
  # frozen_string_literal: true
2
3
 
3
4
  module Nexmo
@@ -1,8 +1,9 @@
1
+ # typed: true
1
2
  # frozen_string_literal: true
2
3
 
3
4
  module Nexmo
4
5
  class Redact < Namespace
5
- self.authentication = KeySecretQuery
6
+ self.authentication = Basic
6
7
 
7
8
  self.request_body = JSON
8
9