vonage 7.2.0

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 (60) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +190 -0
  3. data/README.md +191 -0
  4. data/lib/vonage.rb +29 -0
  5. data/lib/vonage/abstract_authentication.rb +9 -0
  6. data/lib/vonage/account.rb +61 -0
  7. data/lib/vonage/alerts.rb +72 -0
  8. data/lib/vonage/applications.rb +148 -0
  9. data/lib/vonage/applications/list_response.rb +11 -0
  10. data/lib/vonage/authentication_error.rb +6 -0
  11. data/lib/vonage/basic.rb +13 -0
  12. data/lib/vonage/bearer_token.rb +14 -0
  13. data/lib/vonage/client.rb +134 -0
  14. data/lib/vonage/client_error.rb +6 -0
  15. data/lib/vonage/config.rb +208 -0
  16. data/lib/vonage/conversations.rb +210 -0
  17. data/lib/vonage/conversations/events.rb +73 -0
  18. data/lib/vonage/conversations/legs.rb +30 -0
  19. data/lib/vonage/conversations/members.rb +104 -0
  20. data/lib/vonage/conversations/users.rb +93 -0
  21. data/lib/vonage/conversions.rb +19 -0
  22. data/lib/vonage/entity.rb +51 -0
  23. data/lib/vonage/error.rb +6 -0
  24. data/lib/vonage/errors.rb +51 -0
  25. data/lib/vonage/files.rb +26 -0
  26. data/lib/vonage/form_data.rb +11 -0
  27. data/lib/vonage/gsm7.rb +13 -0
  28. data/lib/vonage/http.rb +43 -0
  29. data/lib/vonage/json.rb +17 -0
  30. data/lib/vonage/jwt.rb +43 -0
  31. data/lib/vonage/key_secret_params.rb +20 -0
  32. data/lib/vonage/keys.rb +51 -0
  33. data/lib/vonage/logger.rb +60 -0
  34. data/lib/vonage/messages.rb +25 -0
  35. data/lib/vonage/namespace.rb +118 -0
  36. data/lib/vonage/number_insight.rb +140 -0
  37. data/lib/vonage/numbers.rb +196 -0
  38. data/lib/vonage/numbers/list_response.rb +11 -0
  39. data/lib/vonage/numbers/response.rb +8 -0
  40. data/lib/vonage/params.rb +27 -0
  41. data/lib/vonage/pricing.rb +30 -0
  42. data/lib/vonage/pricing_types.rb +18 -0
  43. data/lib/vonage/redact.rb +37 -0
  44. data/lib/vonage/response.rb +25 -0
  45. data/lib/vonage/secrets.rb +85 -0
  46. data/lib/vonage/secrets/list_response.rb +11 -0
  47. data/lib/vonage/server_error.rb +6 -0
  48. data/lib/vonage/signature.rb +53 -0
  49. data/lib/vonage/sms.rb +121 -0
  50. data/lib/vonage/tfa.rb +14 -0
  51. data/lib/vonage/user_agent.rb +16 -0
  52. data/lib/vonage/verify.rb +253 -0
  53. data/lib/vonage/version.rb +5 -0
  54. data/lib/vonage/voice.rb +250 -0
  55. data/lib/vonage/voice/dtmf.rb +26 -0
  56. data/lib/vonage/voice/list_response.rb +11 -0
  57. data/lib/vonage/voice/stream.rb +44 -0
  58. data/lib/vonage/voice/talk.rb +48 -0
  59. data/vonage.gemspec +26 -0
  60. metadata +155 -0
@@ -0,0 +1,19 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module Vonage
5
+ class Conversions < Namespace
6
+ extend T::Sig
7
+ include Keys
8
+
9
+ sig { params(params: T::Hash[Symbol, T.untyped]).returns(Vonage::Response) }
10
+ def track_sms(params)
11
+ request('/conversions/sms', params: hyphenate(params), type: Post)
12
+ end
13
+
14
+ sig { params(params: T::Hash[Symbol, T.untyped]).returns(Vonage::Response) }
15
+ def track_voice(params)
16
+ request('/conversions/voice', params: hyphenate(params), type: Post)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,51 @@
1
+ # typed: true
2
+
3
+ module Vonage
4
+ class Entity
5
+ include Keys
6
+
7
+ def initialize(attributes = nil)
8
+ @attributes = attributes || {}
9
+ end
10
+
11
+ def [](key)
12
+ @attributes[attribute_key(key)]
13
+ end
14
+
15
+ def []=(key, value)
16
+ @attributes[attribute_key(key)] = value
17
+ end
18
+
19
+ def respond_to_missing?(name, include_private = false)
20
+ @attributes.key?(name) or super
21
+ end
22
+
23
+ def method_missing(name, *args)
24
+ return super unless @attributes.key?(name)
25
+
26
+ @attributes[name]
27
+ end
28
+
29
+ def ==(entity)
30
+ entity.class == self.class && entity.attributes == @attributes
31
+ end
32
+
33
+ def to_h
34
+ @attributes
35
+ end
36
+
37
+ attr_reader :attributes
38
+
39
+ protected :attributes
40
+
41
+ def each_pair(&block)
42
+ return to_enum(:each_pair) unless block
43
+
44
+ @attributes.each_pair(&block)
45
+ end
46
+
47
+ alias_method :each, :each_pair
48
+
49
+ include Enumerable
50
+ end
51
+ end
@@ -0,0 +1,6 @@
1
+ # typed: strong
2
+
3
+ module Vonage
4
+ class Error < StandardError
5
+ end
6
+ end
@@ -0,0 +1,51 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+ require 'json'
4
+
5
+ module Vonage
6
+ module Errors
7
+ extend T::Sig
8
+
9
+ sig {params(response: T.any(Net::HTTPUnauthorized, Net::HTTPClientError, Net::HTTPServerError, T.untyped)).returns(Vonage::Error)}
10
+ def self.parse(response)
11
+ exception_class = case response
12
+ when Net::HTTPUnauthorized
13
+ AuthenticationError
14
+ when Net::HTTPClientError
15
+ ClientError
16
+ when Net::HTTPServerError
17
+ ServerError
18
+ else
19
+ Error
20
+ end
21
+
22
+ message = if response.content_type == 'application/json'
23
+ hash = ::JSON.parse(response.body)
24
+
25
+ if hash.key?('error_title')
26
+ hash['error_title']
27
+ elsif hash.key?('error-code-label')
28
+ hash['error-code-label']
29
+ elsif hash.key?('description')
30
+ hash['description']
31
+ elsif problem_details?(hash)
32
+ problem_details_message(hash)
33
+ end
34
+ end
35
+
36
+ exception_class.new(message)
37
+ end
38
+
39
+ sig { params(hash: T::Hash[String, T.untyped]).returns(T::Boolean) }
40
+ def self.problem_details?(hash)
41
+ hash.key?('title') && hash.key?('detail') && hash.key?('type')
42
+ end
43
+
44
+ sig { params(hash: T::Hash[String, T.untyped]).returns(String) }
45
+ def self.problem_details_message(hash)
46
+ "#{hash['title']}. #{hash['detail']} See #{hash['type']} for more info, or email support@nexmo.com if you have any questions."
47
+ end
48
+ end
49
+
50
+ private_constant :Errors
51
+ end
@@ -0,0 +1,26 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module Vonage
5
+ class Files < Namespace
6
+ extend T::Sig
7
+
8
+ self.authentication = BearerToken
9
+
10
+ sig { params(id: String).returns(T.nilable(Vonage::Response)) }
11
+ def get(id)
12
+ request('/v1/files/' + T.must(id.split('/').last))
13
+ end
14
+
15
+ sig { params(id: String, filename: String).returns(T.nilable(Vonage::Response)) }
16
+ def save(id, filename)
17
+ request('/v1/files/' + T.must(id.split('/').last)) do |response|
18
+ File.open(filename, 'wb') do |file|
19
+ response.read_body do |chunk|
20
+ file.write(chunk)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,11 @@
1
+ # typed: ignore
2
+
3
+ module Vonage
4
+ module FormData
5
+ def self.update(http_request, params)
6
+ http_request.form_data = params
7
+ end
8
+ end
9
+
10
+ private_constant :FormData
11
+ end
@@ -0,0 +1,13 @@
1
+ # typed: ignore
2
+
3
+ module Vonage
4
+ module GSM7
5
+ CHARACTERS = "\n\f\r !\"\#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_abcdefghijklmnopqrstuvwxyz{|}~ ¡£¤¥§¿ÄÅÆÉÑÖØÜßàäåæçèéìñòöøùüΓΔΘΛΞΠΣΦΨΩ€"
6
+
7
+ REGEXP = /\A[#{Regexp.escape(CHARACTERS)}]*\z/
8
+
9
+ def self.encoded?(string)
10
+ REGEXP =~ string
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,43 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+ require 'net/http'
4
+
5
+ module Vonage
6
+ module HTTP
7
+ class Options
8
+ extend T::Sig
9
+
10
+ sig { params(hash: T::Hash[Symbol, T.untyped]).void }
11
+ def initialize(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
16
+
17
+ @hash.each_key do |name|
18
+ next if defined_options.key?(name)
19
+
20
+ raise ArgumentError, "#{name.inspect} is not a valid option"
21
+ end
22
+ end
23
+
24
+ sig { params(http: Net::HTTP).returns(T::Hash[Symbol, T.untyped]) }
25
+ def set(http)
26
+ @hash.each do |name, value|
27
+ http.public_send(defined_options.fetch(name), value)
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ sig { returns(T::Hash[Symbol, T.untyped]) }
34
+ def defined_options
35
+ @defined_options = T.let(@defined_options, T.nilable(T::Hash[Symbol, T.untyped]))
36
+
37
+ @defined_options ||= Net::HTTP.instance_methods.grep(/\w=\z/).each_with_object({}) do |name, hash|
38
+ hash[name.to_s.chomp('=').to_sym] = name
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,17 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+ require 'json'
4
+
5
+ module Vonage
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 }
10
+ def self.update(http_request, params)
11
+ http_request['Content-Type'] = 'application/json'
12
+ http_request.body = ::JSON.generate(params)
13
+ end
14
+ end
15
+
16
+ private_constant :JSON
17
+ end
@@ -0,0 +1,43 @@
1
+ # typed: false
2
+ # frozen_string_literal: true
3
+ require 'securerandom'
4
+ require 'openssl'
5
+ require 'nexmo-jwt'
6
+
7
+ module Vonage
8
+ class JWT
9
+ # Generate an encoded JSON Web Token.
10
+ #
11
+ # By default the Vonage Ruby SDK generates a short lived JWT per request.
12
+ #
13
+ # To generate a long lived JWT for multiple requests or to specify JWT claims
14
+ # directly call {Vonage::JWT.generate} to generate a token, and set the token
15
+ # attribute on the client object.
16
+ #
17
+ # Documentation for the Vonage Ruby JWT generator gem can be found at
18
+ # https://www.rubydoc.info/github/Nexmo/nexmo-jwt-ruby
19
+ #
20
+ # @example
21
+ # claims = {
22
+ # application_id: application_id,
23
+ # ttl: 800,
24
+ # subject: 'My_Subject'
25
+ # }
26
+ #
27
+ # private_key = File.read('path/to/private.key')
28
+ #
29
+ # client.config.token = Vonage::JWT.generate(claims, private_key)
30
+ #
31
+ # @param [Hash] payload
32
+ # @param [String, OpenSSL::PKey::RSA] private_key
33
+ #
34
+ # @return [String]
35
+ #
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
38
+
39
+ payload[:private_key] = private_key if private_key && !payload[:private_key]
40
+ @token = Nexmo::JWTBuilder.new(payload).jwt.generate
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,20 @@
1
+ # typed: strict
2
+
3
+ module Vonage
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 }
10
+ def update(object)
11
+ return unless object.is_a?(Hash)
12
+
13
+ @config = T.let(@config, T.nilable(Vonage::Config))
14
+ object[:api_key] = T.must(@config).api_key
15
+ object[:api_secret] = T.must(@config).api_secret
16
+ end
17
+ end
18
+
19
+ private_constant :KeySecretParams
20
+ end
@@ -0,0 +1,51 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module Vonage
5
+ module Keys
6
+ extend T::Sig
7
+
8
+ sig { params(hash: T::Hash[T.untyped, T.untyped]).returns(T::Hash[String, T.untyped]) }
9
+ def hyphenate(hash)
10
+ hash.transform_keys { |k| k.to_s.tr('_', '-') }
11
+ end
12
+
13
+ sig { params(hash: T::Hash[T.untyped, T.untyped]).returns(T::Hash[T.untyped, T.untyped]) }
14
+ def camelcase(hash)
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
32
+ end
33
+
34
+ ATTRIBUTE_KEYS = T.let(Hash.new { |h, k| h[k] = k.split(PATTERN).join('_').downcase.to_sym }, T::Hash[T.untyped, T.untyped])
35
+
36
+ PATTERN = /[\-_]|(?<=\w)(?=[A-Z])/
37
+
38
+ private_constant :ATTRIBUTE_KEYS
39
+
40
+ private_constant :PATTERN
41
+
42
+ sig { params(k: T.any(Symbol, String)).returns(Symbol) }
43
+ def attribute_key(k)
44
+ return k if k.is_a?(Symbol)
45
+
46
+ ATTRIBUTE_KEYS[k]
47
+ end
48
+ end
49
+
50
+ private_constant :Keys
51
+ end
@@ -0,0 +1,60 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+ require 'logger'
4
+ require 'forwardable'
5
+
6
+ module Vonage
7
+ class Logger
8
+ extend T::Sig
9
+
10
+ sig { params(logger: T.nilable(T.any(::Logger, Vonage::Logger))).void }
11
+ def initialize(logger)
12
+ @logger = logger || ::Logger.new(nil)
13
+ end
14
+
15
+ extend Forwardable
16
+
17
+ ::Logger::Severity.constants.map(&:downcase).each do |name|
18
+ def_delegator :@logger, name, name
19
+ end
20
+
21
+ sig { params(request: T.any(Net::HTTP::Post, Net::HTTP::Get, Net::HTTP::Delete, Net::HTTP::Put)).void }
22
+ def log_request_info(request)
23
+ @logger = T.let(@logger, T.nilable(T.any(::Logger, Vonage::Logger)))
24
+
25
+ T.must(@logger).info do
26
+ format('Vonage API request', {
27
+ method: request.method,
28
+ path: request.uri.path
29
+ })
30
+ end
31
+ end
32
+
33
+ sig { params(
34
+ response: T.any(Net::HTTPOK, Net::HTTPNoContent, Net::HTTPBadRequest, Net::HTTPInternalServerError, Net::HTTPResponse),
35
+ host: String
36
+ ).void }
37
+ def log_response_info(response, host)
38
+ T.must(@logger).info do
39
+ format('Vonage API response', {
40
+ host: host,
41
+ status: response.code,
42
+ type: response.content_type,
43
+ length: response.content_length,
44
+ trace_id: response['x-Vonage-trace-id']
45
+ })
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ sig { params(message: String, hash: T::Hash[Symbol, T.untyped]).returns(String) }
52
+ def format(message, hash)
53
+ return message if hash.nil?
54
+
55
+ fields = hash.map { |key, value| "#{key}=#{value}" if value }.compact
56
+ fields.unshift(message)
57
+ fields.join(' ')
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,25 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module Vonage
5
+ class Messages < Namespace
6
+ extend T::Sig
7
+
8
+ self.host = :rest_host
9
+
10
+ sig { params(id: String).returns(Vonage::Response) }
11
+ def get(id)
12
+ request('/search/message', params: {id: id})
13
+ end
14
+
15
+ sig { params(params: T::Hash[Symbol, T.untyped]).returns(Vonage::Response) }
16
+ def search(params)
17
+ request('/search/messages', params: params)
18
+ end
19
+
20
+ sig { params(params: T::Hash[Symbol, T.untyped]).returns(Vonage::Response) }
21
+ def rejections(params)
22
+ request('/search/rejections', params: params)
23
+ end
24
+ end
25
+ end