nexmo 6.1.0 → 7.1.1

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.
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