telnyx 0.0.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.
- checksums.yaml +7 -0
- data/.gitattributes +4 -0
- data/.github/ISSUE_TEMPLATE.md +5 -0
- data/.gitignore +9 -0
- data/.rubocop.yml +32 -0
- data/.rubocop_todo.yml +50 -0
- data/.travis.yml +42 -0
- data/CHANGELOG.md +2 -0
- data/CONTRIBUTORS +0 -0
- data/Gemfile +40 -0
- data/Guardfile +8 -0
- data/LICENSE +22 -0
- data/README.md +173 -0
- data/Rakefile +28 -0
- data/VERSION +1 -0
- data/bin/telnyx-console +16 -0
- data/lib/telnyx.rb +151 -0
- data/lib/telnyx/api_operations/create.rb +12 -0
- data/lib/telnyx/api_operations/delete.rb +13 -0
- data/lib/telnyx/api_operations/list.rb +29 -0
- data/lib/telnyx/api_operations/nested_resource.rb +63 -0
- data/lib/telnyx/api_operations/request.rb +57 -0
- data/lib/telnyx/api_operations/save.rb +103 -0
- data/lib/telnyx/api_resource.rb +69 -0
- data/lib/telnyx/available_phone_number.rb +9 -0
- data/lib/telnyx/errors.rb +166 -0
- data/lib/telnyx/event.rb +9 -0
- data/lib/telnyx/list_object.rb +155 -0
- data/lib/telnyx/message.rb +9 -0
- data/lib/telnyx/messaging_phone_number.rb +10 -0
- data/lib/telnyx/messaging_profile.rb +32 -0
- data/lib/telnyx/messaging_sender_id.rb +12 -0
- data/lib/telnyx/messaging_short_code.rb +10 -0
- data/lib/telnyx/number_order.rb +11 -0
- data/lib/telnyx/number_reservation.rb +11 -0
- data/lib/telnyx/public_key.rb +7 -0
- data/lib/telnyx/singleton_api_resource.rb +24 -0
- data/lib/telnyx/telnyx_client.rb +545 -0
- data/lib/telnyx/telnyx_object.rb +521 -0
- data/lib/telnyx/telnyx_response.rb +50 -0
- data/lib/telnyx/util.rb +328 -0
- data/lib/telnyx/version.rb +5 -0
- data/lib/telnyx/webhook.rb +66 -0
- data/telnyx.gemspec +25 -0
- data/test/api_stub_helpers.rb +1 -0
- data/test/openapi/README.md +9 -0
- data/test/telnyx/api_operations_test.rb +85 -0
- data/test/telnyx/api_resource_test.rb +293 -0
- data/test/telnyx/available_phone_number_test.rb +14 -0
- data/test/telnyx/errors_test.rb +23 -0
- data/test/telnyx/list_object_test.rb +244 -0
- data/test/telnyx/message_test.rb +19 -0
- data/test/telnyx/messaging_phone_number_test.rb +33 -0
- data/test/telnyx/messaging_profile_test.rb +70 -0
- data/test/telnyx/messaging_sender_id_test.rb +46 -0
- data/test/telnyx/messaging_short_code_test.rb +33 -0
- data/test/telnyx/number_order_test.rb +39 -0
- data/test/telnyx/number_reservation_test.rb +12 -0
- data/test/telnyx/public_key_test.rb +13 -0
- data/test/telnyx/telnyx_client_test.rb +631 -0
- data/test/telnyx/telnyx_object_test.rb +497 -0
- data/test/telnyx/telnyx_response_test.rb +49 -0
- data/test/telnyx/util_test.rb +380 -0
- data/test/telnyx/webhook_test.rb +108 -0
- data/test/telnyx_mock.rb +78 -0
- data/test/telnyx_test.rb +40 -0
- data/test/test_data.rb +149 -0
- data/test/test_helper.rb +73 -0
- metadata +162 -0
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.0.0b0
|
data/bin/telnyx-console
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# frozen_string_literal: true
|
4
|
+
|
5
|
+
require "irb"
|
6
|
+
require "irb/completion"
|
7
|
+
|
8
|
+
require "#{::File.dirname(__FILE__)}/../lib/telnyx"
|
9
|
+
|
10
|
+
# Config IRB to enable --simple-prompt and auto indent
|
11
|
+
IRB.conf[:PROMPT_MODE] = :SIMPLE
|
12
|
+
IRB.conf[:AUTO_INDENT] = true
|
13
|
+
|
14
|
+
puts "Loaded gem 'telnyx'"
|
15
|
+
|
16
|
+
IRB.start
|
data/lib/telnyx.rb
ADDED
@@ -0,0 +1,151 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Telnyx Ruby bindings
|
4
|
+
# API spec at https://developers.telnyx.com/clients
|
5
|
+
require "cgi"
|
6
|
+
require "faraday"
|
7
|
+
require "json"
|
8
|
+
require "logger"
|
9
|
+
require "openssl"
|
10
|
+
require "rbconfig"
|
11
|
+
require "securerandom"
|
12
|
+
require "set"
|
13
|
+
require "socket"
|
14
|
+
require "uri"
|
15
|
+
|
16
|
+
# Version
|
17
|
+
require "telnyx/version"
|
18
|
+
|
19
|
+
# API operations
|
20
|
+
require "telnyx/api_operations/create"
|
21
|
+
require "telnyx/api_operations/delete"
|
22
|
+
require "telnyx/api_operations/list"
|
23
|
+
require "telnyx/api_operations/nested_resource"
|
24
|
+
require "telnyx/api_operations/request"
|
25
|
+
require "telnyx/api_operations/save"
|
26
|
+
|
27
|
+
# API resource support classes
|
28
|
+
require "telnyx/errors"
|
29
|
+
require "telnyx/util"
|
30
|
+
require "telnyx/telnyx_client"
|
31
|
+
require "telnyx/telnyx_object"
|
32
|
+
require "telnyx/telnyx_response"
|
33
|
+
require "telnyx/list_object"
|
34
|
+
require "telnyx/api_resource"
|
35
|
+
require "telnyx/singleton_api_resource"
|
36
|
+
require "telnyx/webhook"
|
37
|
+
|
38
|
+
require "telnyx/number_order"
|
39
|
+
require "telnyx/number_reservation"
|
40
|
+
require "telnyx/message"
|
41
|
+
require "telnyx/messaging_profile"
|
42
|
+
require "telnyx/event"
|
43
|
+
require "telnyx/messaging_phone_number"
|
44
|
+
require "telnyx/messaging_sender_id"
|
45
|
+
require "telnyx/messaging_short_code"
|
46
|
+
require "telnyx/available_phone_number"
|
47
|
+
require "telnyx/public_key"
|
48
|
+
|
49
|
+
module Telnyx
|
50
|
+
@app_info = nil
|
51
|
+
|
52
|
+
@api_base = ENV.fetch("TELNYX_API_BASE", "https://api.telnyx.com")
|
53
|
+
|
54
|
+
@log_level = nil
|
55
|
+
@logger = nil
|
56
|
+
|
57
|
+
@max_network_retries = 0
|
58
|
+
@max_network_retry_delay = 2
|
59
|
+
@initial_network_retry_delay = 0.5
|
60
|
+
|
61
|
+
@verify_ssl_certs = true
|
62
|
+
|
63
|
+
@open_timeout = 30
|
64
|
+
@read_timeout = 80
|
65
|
+
|
66
|
+
class << self
|
67
|
+
attr_accessor :telnyx_account, :api_key, :api_base, :verify_ssl_certs, :api_version, :client_id,
|
68
|
+
:open_timeout, :read_timeout
|
69
|
+
|
70
|
+
attr_reader :max_network_retry_delay, :initial_network_retry_delay
|
71
|
+
end
|
72
|
+
|
73
|
+
# Gets the application for a plugin that's identified some. See
|
74
|
+
# #set_app_info.
|
75
|
+
def self.app_info
|
76
|
+
@app_info
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.app_info=(info)
|
80
|
+
@app_info = info
|
81
|
+
end
|
82
|
+
|
83
|
+
# map to the same values as the standard library's logger
|
84
|
+
LEVEL_DEBUG = Logger::DEBUG
|
85
|
+
LEVEL_ERROR = Logger::ERROR
|
86
|
+
LEVEL_INFO = Logger::INFO
|
87
|
+
|
88
|
+
# When set prompts the library to log some extra information to $stdout and
|
89
|
+
# $stderr about what it's doing. For example, it'll produce information about
|
90
|
+
# requests, responses, and errors that are received. Valid log levels are
|
91
|
+
# `debug` and `info`, with `debug` being a little more verbose in places.
|
92
|
+
#
|
93
|
+
# Use of this configuration is only useful when `.logger` is _not_ set. When
|
94
|
+
# it is, the decision what levels to print is entirely deferred to the logger.
|
95
|
+
def self.log_level
|
96
|
+
@log_level
|
97
|
+
end
|
98
|
+
|
99
|
+
def self.log_level=(val)
|
100
|
+
# Backwards compatibility for values that we briefly allowed
|
101
|
+
if val == "debug"
|
102
|
+
val = LEVEL_DEBUG
|
103
|
+
elsif val == "info"
|
104
|
+
val = LEVEL_INFO
|
105
|
+
end
|
106
|
+
|
107
|
+
if !val.nil? && ![LEVEL_DEBUG, LEVEL_ERROR, LEVEL_INFO].include?(val)
|
108
|
+
raise ArgumentError, "log_level should only be set to `nil`, `debug` or `info`"
|
109
|
+
end
|
110
|
+
@log_level = val
|
111
|
+
end
|
112
|
+
|
113
|
+
# Sets a logger to which logging output will be sent. The logger should
|
114
|
+
# support the same interface as the `Logger` class that's part of Ruby's
|
115
|
+
# standard library (hint, anything in `Rails.logger` will likely be
|
116
|
+
# suitable).
|
117
|
+
#
|
118
|
+
# If `.logger` is set, the value of `.log_level` is ignored. The decision on
|
119
|
+
# what levels to print is entirely deferred to the logger.
|
120
|
+
def self.logger
|
121
|
+
@logger
|
122
|
+
end
|
123
|
+
|
124
|
+
def self.logger=(val)
|
125
|
+
@logger = val
|
126
|
+
end
|
127
|
+
|
128
|
+
def self.max_network_retries
|
129
|
+
@max_network_retries
|
130
|
+
end
|
131
|
+
|
132
|
+
def self.max_network_retries=(val)
|
133
|
+
@max_network_retries = val.to_i
|
134
|
+
end
|
135
|
+
|
136
|
+
# Sets some basic information about the running application that's sent along
|
137
|
+
# with API requests. Useful for plugin authors to identify their plugin when
|
138
|
+
# communicating with Telnyx.
|
139
|
+
#
|
140
|
+
# Takes a name and optional partner program ID, plugin URL, and version.
|
141
|
+
def self.set_app_info(name, partner_id: nil, url: nil, version: nil)
|
142
|
+
@app_info = {
|
143
|
+
name: name,
|
144
|
+
partner_id: partner_id,
|
145
|
+
url: url,
|
146
|
+
version: version,
|
147
|
+
}
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
Telnyx.log_level = ENV["TELNYX_LOG_LEVEL"] unless ENV["TELNYX_LOG_LEVEL"].nil?
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Telnyx
|
4
|
+
module APIOperations
|
5
|
+
module Create
|
6
|
+
def create(params = {}, opts = {})
|
7
|
+
resp, opts = request(:post, resource_url, params, opts)
|
8
|
+
Util.convert_to_telnyx_object(resp.data, opts)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Telnyx
|
4
|
+
module APIOperations
|
5
|
+
module Delete
|
6
|
+
def delete(params = {}, opts = {})
|
7
|
+
opts = Util.normalize_opts(opts)
|
8
|
+
resp, opts = request(:delete, resource_url, params, opts)
|
9
|
+
initialize_from(resp.data[:data], opts)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Telnyx
|
4
|
+
module APIOperations
|
5
|
+
module List
|
6
|
+
def list(filters = {}, opts = {})
|
7
|
+
opts = Util.normalize_opts(opts)
|
8
|
+
|
9
|
+
resp, opts = request(:get, resource_url, filters, opts)
|
10
|
+
obj = ListObject.construct_from(resp.data.merge(url: resource_url), opts)
|
11
|
+
|
12
|
+
# set filters so that we can fetch the same limit, expansions, and
|
13
|
+
# predicates when accessing the next and previous pages
|
14
|
+
#
|
15
|
+
# just for general cleanliness, remove any paging options
|
16
|
+
obj.filters = filters.dup
|
17
|
+
obj.filters.delete(:page)
|
18
|
+
|
19
|
+
obj
|
20
|
+
end
|
21
|
+
|
22
|
+
# The original version of #list was given the somewhat unfortunate name of
|
23
|
+
# #all, and this alias allows us to maintain backward compatibility (the
|
24
|
+
# choice was somewhat misleading in the way that it only returned a single
|
25
|
+
# page rather than all objects).
|
26
|
+
alias all list
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Telnyx
|
4
|
+
module APIOperations
|
5
|
+
# Adds methods to help manipulate a subresource from its parent resource so
|
6
|
+
# that it's possible to do so from a static context (i.e. without a
|
7
|
+
# pre-existing collection of subresources on the parent).
|
8
|
+
#
|
9
|
+
# For examle, a transfer gains the static methods for reversals so that the
|
10
|
+
# methods `.create_reversal`, `.retrieve_reversal`, `.update_reversal`,
|
11
|
+
# etc. all become available.
|
12
|
+
module NestedResource
|
13
|
+
def nested_resource_class_methods(resource, path: nil, operations: nil)
|
14
|
+
path ||= "#{resource}s"
|
15
|
+
raise ArgumentError, "operations array required" if operations.nil?
|
16
|
+
|
17
|
+
resource_url_method = :"#{resource}s_url"
|
18
|
+
define_singleton_method(resource_url_method) do |id, nested_id = nil|
|
19
|
+
url = "#{resource_url}/#{CGI.escape(id)}/#{CGI.escape(path)}"
|
20
|
+
url += "/#{CGI.escape(nested_id)}" unless nested_id.nil?
|
21
|
+
url
|
22
|
+
end
|
23
|
+
|
24
|
+
operations.each do |operation|
|
25
|
+
case operation
|
26
|
+
when :create
|
27
|
+
define_singleton_method(:"create_#{resource}") do |id, params = {}, opts = {}|
|
28
|
+
url = send(resource_url_method, id)
|
29
|
+
resp, opts = request(:post, url, params, opts)
|
30
|
+
Util.convert_to_telnyx_object(resp.data, opts)
|
31
|
+
end
|
32
|
+
when :retrieve
|
33
|
+
define_singleton_method(:"retrieve_#{resource}") do |id, nested_id, opts = {}|
|
34
|
+
url = send(resource_url_method, id, nested_id)
|
35
|
+
resp, opts = request(:get, url, {}, opts)
|
36
|
+
Util.convert_to_telnyx_object(resp.data, opts)
|
37
|
+
end
|
38
|
+
when :update
|
39
|
+
define_singleton_method(:"update_#{resource}") do |id, nested_id, params = {}, opts = {}|
|
40
|
+
url = send(resource_url_method, id, nested_id)
|
41
|
+
resp, opts = request(:patch, url, params, opts)
|
42
|
+
Util.convert_to_telnyx_object(resp.data, opts)
|
43
|
+
end
|
44
|
+
when :delete
|
45
|
+
define_singleton_method(:"delete_#{resource}") do |id, nested_id, params = {}, opts = {}|
|
46
|
+
url = send(resource_url_method, id, nested_id)
|
47
|
+
resp, opts = request(:delete, url, params, opts)
|
48
|
+
Util.convert_to_telnyx_object(resp.data, opts)
|
49
|
+
end
|
50
|
+
when :list
|
51
|
+
define_singleton_method(:"list_#{resource}s") do |id, params = {}, opts = {}|
|
52
|
+
url = send(resource_url_method, id)
|
53
|
+
resp, opts = request(:get, url, params, opts)
|
54
|
+
Util.convert_to_telnyx_object(resp.data, opts)
|
55
|
+
end
|
56
|
+
else
|
57
|
+
raise ArgumentError, "Unknown operation: #{operation.inspect}"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Telnyx
|
4
|
+
module APIOperations
|
5
|
+
module Request
|
6
|
+
module ClassMethods
|
7
|
+
def request(method, url, params = {}, opts = {})
|
8
|
+
warn_on_opts_in_params(params)
|
9
|
+
|
10
|
+
opts = Util.normalize_opts(opts)
|
11
|
+
opts[:client] ||= TelnyxClient.active_client
|
12
|
+
|
13
|
+
headers = opts.clone
|
14
|
+
api_key = headers.delete(:api_key)
|
15
|
+
api_base = headers.delete(:api_base)
|
16
|
+
client = headers.delete(:client)
|
17
|
+
# Assume all remaining opts must be headers
|
18
|
+
|
19
|
+
resp, opts[:api_key] = client.execute_request(
|
20
|
+
method, url,
|
21
|
+
api_base: api_base, api_key: api_key,
|
22
|
+
headers: headers, params: params
|
23
|
+
)
|
24
|
+
|
25
|
+
# Hash#select returns an array before 1.9
|
26
|
+
opts_to_persist = {}
|
27
|
+
opts.each do |k, v|
|
28
|
+
opts_to_persist[k] = v if Util::OPTS_PERSISTABLE.include?(k)
|
29
|
+
end
|
30
|
+
|
31
|
+
[resp, opts_to_persist]
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def warn_on_opts_in_params(params)
|
37
|
+
Util::OPTS_USER_SPECIFIED.each do |opt|
|
38
|
+
if params.key?(opt)
|
39
|
+
$stderr.puts("WARNING: #{opt} should be in opts instead of params.")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.included(base)
|
46
|
+
base.extend(ClassMethods)
|
47
|
+
end
|
48
|
+
|
49
|
+
protected
|
50
|
+
|
51
|
+
def request(method, url, params = {}, opts = {})
|
52
|
+
opts = @opts.merge(Util.normalize_opts(opts))
|
53
|
+
self.class.request(method, url, params, opts)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Telnyx
|
4
|
+
module APIOperations
|
5
|
+
module Save
|
6
|
+
module ClassMethods
|
7
|
+
# Updates an API resource
|
8
|
+
#
|
9
|
+
# Updates the identified resource with the passed in parameters.
|
10
|
+
#
|
11
|
+
# ==== Attributes
|
12
|
+
#
|
13
|
+
# * +id+ - ID of the resource to update.
|
14
|
+
# * +params+ - A hash of parameters to pass to the API
|
15
|
+
# * +opts+ - A Hash of additional options (separate from the params /
|
16
|
+
# object values) to be added to the request. E.g. to allow for the
|
17
|
+
# api_key to be overwritten. See {APIOperations::Request.request}.
|
18
|
+
def update(id, params = {}, opts = {})
|
19
|
+
params.each_key do |k|
|
20
|
+
if protected_fields.include?(k)
|
21
|
+
raise ArgumentError, "Cannot update protected field: #{k}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
resp, opts = request(:patch, "#{resource_url}/#{id}", params, opts)
|
26
|
+
Util.convert_to_telnyx_object(resp.data, opts)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Creates or updates an API resource.
|
31
|
+
#
|
32
|
+
# If the resource doesn't yet have an assigned ID and the resource is one
|
33
|
+
# that can be created, then the method attempts to create the resource.
|
34
|
+
# The resource is updated otherwise.
|
35
|
+
#
|
36
|
+
# ==== Attributes
|
37
|
+
#
|
38
|
+
# * +params+ - Overrides any parameters in the resource's serialized data
|
39
|
+
# and includes them in the create or update. If +:req_url:+ is included
|
40
|
+
# in the list, it overrides the update URL used for the create or
|
41
|
+
# update.
|
42
|
+
# * +opts+ - A Hash of additional options (separate from the params /
|
43
|
+
# object values) to be added to the request. E.g. to allow for the
|
44
|
+
# api_key to be overwritten. See {APIOperations::Request.request}.
|
45
|
+
def save(params = {}, opts = {})
|
46
|
+
# We started unintentionally (sort of) allowing attributes sent to
|
47
|
+
# +save+ to override values used during the update. So as not to break
|
48
|
+
# the API, this makes that official here.
|
49
|
+
update_attributes(params)
|
50
|
+
|
51
|
+
# Now remove any parameters that look like object attributes.
|
52
|
+
params = params.reject { |k, _| respond_to?(k) }
|
53
|
+
|
54
|
+
values = serialize_params(self).merge(params)
|
55
|
+
|
56
|
+
# note that id gets removed here our call to #url above has already
|
57
|
+
# generated a uri for this object with an identifier baked in
|
58
|
+
values.delete(:id)
|
59
|
+
|
60
|
+
resp, opts = request(save_method, save_url, values, opts)
|
61
|
+
|
62
|
+
initialize_from(resp.data[:data], opts)
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.included(base)
|
66
|
+
# Set `metadata` as additive so that when it's set directly we remember
|
67
|
+
# to clear keys that may have been previously set by sending empty
|
68
|
+
# values for them.
|
69
|
+
#
|
70
|
+
# It's possible that not every object with `Save` has `metadata`, but
|
71
|
+
# it's a close enough heuristic, and having this option set when there
|
72
|
+
# is no `metadata` field is not harmful.
|
73
|
+
base.additive_object_param(:metadata)
|
74
|
+
|
75
|
+
base.extend(ClassMethods)
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def save_url
|
81
|
+
# This switch essentially allows us "upsert"-like functionality. If the
|
82
|
+
# API resource doesn't have an ID set (suggesting that it's new) and
|
83
|
+
# its class responds to .create (which comes from
|
84
|
+
# Telnyx::APIOperations::Create), then use the URL to create a new
|
85
|
+
# resource. Otherwise, generate a URL based on the object's identifier
|
86
|
+
# for a normal update.
|
87
|
+
if self[:id].nil? && self.class.respond_to?(:create)
|
88
|
+
self.class.resource_url
|
89
|
+
else
|
90
|
+
resource_url
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def save_method
|
95
|
+
if self[:id].nil? && self.class.respond_to?(:create)
|
96
|
+
:post
|
97
|
+
else
|
98
|
+
:patch
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|