telnyx 0.0.1 → 0.0.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 16c98a1dc79356b27c3c8155c14fd316000665e4316aa528d519c3e14ab12396
4
- data.tar.gz: c174b8954c79aae466162c0ce0f3507ec627ca91571f482d962254e03b367548
3
+ metadata.gz: cf0a68b78d4fd1ab92bebdeeca23496359d7b3de38296e9dbdd22aa271147c1a
4
+ data.tar.gz: 231d2e6e1e002234e8560b7f954588ed2a156e10f41c51a34dc62737eb9203b5
5
5
  SHA512:
6
- metadata.gz: b3c9c49a9e5442607f606237cbb03465f311590668457eded220a7516d4a7e1cae0a3d26268d19d656eb7f0b6d94cd48538a2513cb2ce19aeaddd67416e353e7
7
- data.tar.gz: 1aaa22d941afd491b3a5be114c0cab65b594a79b0d929f224a701618e53c782dd9a1b0357c2b92faf57198223c4ee20b0dd689b29b8ee77c05ab0b335f86fbf6
6
+ metadata.gz: 205abab8d0d437ed164f84346f96172f1d8b19bbe5f2e5d66ce0a14d6d09573dd117399a01b7663a03f3e0611140f5c2a0b39a16350b65c47022b80be675db2e
7
+ data.tar.gz: 3659223c70977575d8d7a81e994d210b0aa37af8d05c24821d5742c1067cdb0a0aad04c3f89dadb93e33bb709887ea3b8a7ca3935714aeacb97d730307cbfcd1
data/.gitignore CHANGED
@@ -6,4 +6,3 @@ tags
6
6
  /.bundle/
7
7
  coverage/
8
8
  .idea/
9
- .vscode/
@@ -1,4 +1,3 @@
1
- inherit_from: .rubocop_todo.yml
2
1
 
3
2
  AllCops:
4
3
  DisplayCopNames: true
@@ -30,3 +29,33 @@ Style/StringLiterals:
30
29
 
31
30
  Style/TrailingCommaInLiteral:
32
31
  EnforcedStyleForMultiline: consistent_comma
32
+
33
+ Metrics/AbcSize:
34
+ Max: 52
35
+
36
+ Metrics/BlockLength:
37
+ Max: 498
38
+
39
+ Metrics/ClassLength:
40
+ Max: 659
41
+
42
+ # Offense count: 11
43
+ Metrics/CyclomaticComplexity:
44
+ Max: 15
45
+
46
+ Metrics/LineLength:
47
+ Max: 310
48
+
49
+ Metrics/ParameterLists:
50
+ Max: 7
51
+
52
+ Metrics/PerceivedComplexity:
53
+ Max: 17
54
+
55
+ Style/ClassVars:
56
+ Exclude:
57
+ - 'lib/telnyx/telnyx_object.rb'
58
+ - 'test/telnyx/api_resource_test.rb'
59
+
60
+ Style/Documentation:
61
+ Enabled: false
@@ -1,23 +1,32 @@
1
1
  language: ruby
2
2
 
3
+
3
4
  rvm:
4
5
  - 2.1
5
6
  - 2.2
6
7
  - 2.3
7
8
  - 2.4
8
9
  - 2.5
9
- - jruby-9.0.5.0
10
+ - 2.6
11
+
12
+ matrix:
13
+ include:
14
+ - rvm: jruby-head
15
+ jdk: oraclejdk11
16
+
10
17
 
11
18
  notifications:
12
19
  email:
13
20
  on_success: never
21
+ slack:
22
+ secure: AMXcZSwIL8SRZQ+opSFrlvNKoUXv1rZkWDgorBmz+BEHwGKWgVzYcZ4GwD5p6Z5uNdtvl9FZz6oLvvAcbW6CblbAPM+f2qLVDlZ/sazpOK8l6QIo7X86U3SuwJicY2CbbKvqN/A3u23Bbvf5u4djvm5oc73qASZY/RJHxm2xcmD57+z6hY12AvWtLJN95BsjVZ9RHXy8/qkJehqGnzSi9VGojNmd0voU9UrxJU0xS10kBA7dQFCCf+NZv9utguyFfAATpa9JTlD1a8QiB2fzvdPkBym1bnqr3nQPk5rNbgiFHf14OIlq7C2jwaNNoB1dDpkT/Vfvmn5EHzBDZQ30PrVpq9uNgQg45pOIMXp9ZLY0zYi/Gzk5tF/lTKUxk5evJ2+2Dtmzv4mzbk98pvGrA+MIkSXuYy6GHZuXanb3OQ5y42dSYVdy1c+WHdbYx1LOJSEGtALr9ADyjDu9KAu2eJMnmGQ14cJarl/33BF4UzCRKpPxV5CwOqI82+fK9pNiW0CLijfxpkFr9aaxViVsf43r5Ag12Jqme18IWCGJ1P5sMEo6bz/Gp4BuVMXQtYExorK+fWkrm1Wus6HGINlRonUswJ9LJ995M384j6KyP1121MJuiPAc1AdNqS1C992j/cDoUDlxsxW9HTX15nGoM712w00wNrj/vdQt0TlmENo=
14
23
 
15
24
  sudo: false
16
25
 
17
26
  env:
18
27
  global:
19
28
  # If changing this number, please also change it in `test/test_helper.rb`.
20
- - TELNYX_MOCK_VERSION=0.40.1
29
+ - TELNYX_MOCK_VERSION=0.2.0
21
30
 
22
31
  cache:
23
32
  directories:
@@ -29,14 +38,14 @@ before_install:
29
38
  # Unpack and start telnyx-mock so that the test suite can talk to it
30
39
  - |
31
40
  if [ ! -d "telnyx-mock/telnyx-mock_${TELNYX_MOCK_VERSION}" ]; then
32
- mkdir -p telnyx-mock/telnyx-mock_${TELNYX_MOCK_VERSION}/
33
- curl -L "https://github.com/telnyx/telnyx-mock/releases/download/v${TELNYX_MOCK_VERSION}/telnyx-mock_${TELNYX_MOCK_VERSION}_linux_amd64.tar.gz" -o "telnyx-mock/telnyx-mock_${TELNYX_MOCK_VERSION}_linux_amd64.tar.gz"
34
- tar -zxf "telnyx-mock/telnyx-mock_${TELNYX_MOCK_VERSION}_linux_amd64.tar.gz" -C "telnyx-mock/telnyx-mock_${TELNYX_MOCK_VERSION}/"
41
+ mkdir -p telnyx-mock/${TELNYX_MOCK_VERSION}/
42
+ curl -L "https://github.com/team-telnyx/telnyx-mock/releases/download/v${TELNYX_MOCK_VERSION}/telnyx-mock_${TELNYX_MOCK_VERSION}_linux_amd64.tar.gz" -o "telnyx-mock/telnyx-mock_${TELNYX_MOCK_VERSION}_linux_amd64.tar.gz"
43
+ tar -zxf "telnyx-mock/telnyx-mock_${TELNYX_MOCK_VERSION}_linux_amd64.tar.gz" -C "telnyx-mock/${TELNYX_MOCK_VERSION}/"
35
44
  fi
36
45
  - |
37
- telnyx-mock/telnyx-mock_${TELNYX_MOCK_VERSION}/telnyx-mock > /dev/null &
46
+ telnyx-mock/${TELNYX_MOCK_VERSION}/telnyx-mock > /dev/null &
38
47
  TELNYX_MOCK_PID=$!
39
- - export PATH="${PATH}:${PWD}/telnyx-mock/telnyx-mock_${TELNYX_MOCK_VERSION}"
48
+ - export PATH="${PATH}:${PWD}/telnyx-mock/${TELNYX_MOCK_VERSION}"
40
49
 
41
50
  script:
42
51
  - bundle exec rake
data/Gemfile CHANGED
@@ -20,6 +20,7 @@ group :development do
20
20
  # up-to-date, but it's not the end of the world if it's not.
21
21
  gem "guard"
22
22
  gem "guard-rake"
23
+ gem "guard-rubocop"
23
24
  gem "rubocop", "0.50.0"
24
25
  gem "solargraph"
25
26
 
data/Guardfile CHANGED
@@ -3,6 +3,10 @@
3
3
  directories(%w[lib test]) \
4
4
  .select { |d| Dir.exist?(d) ? d : UI.warning("Directory #{d} does not exist") }
5
5
 
6
+ guard :rubocop, all_on_start: true, cli: ["-a"] do
7
+ watch(/.*/)
8
+ end
9
+
6
10
  guard "rake", task: "test" do
7
11
  watch(/.*/)
8
12
  end
data/README.md CHANGED
@@ -19,7 +19,7 @@ The library also provides other features. For example:
19
19
 
20
20
  ## Documentation
21
21
 
22
- See the [API docs](https://developers.telnyx.com/docs/v2/overview).
22
+ See the [API docs](https://developers.telnyx.com/docs/api/v2/overview).
23
23
 
24
24
  ## Installation
25
25
 
@@ -56,7 +56,7 @@ value:
56
56
 
57
57
  ``` ruby
58
58
  require "telnyx"
59
- Telnyx.api_key = "super-secret-key"
59
+ Telnyx.api_key = "YOUR_API_KEY"
60
60
 
61
61
  # list messaging profiles
62
62
  Telnyx::MessagingProfile.list()
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.0b0
1
+ 0.0.2
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Telnyx Ruby bindings
4
- # API spec at https://developers.telnyx.com/clients
4
+ # API spec at https://developers.telnyx.com
5
5
  require "cgi"
6
6
  require "faraday"
7
7
  require "json"
@@ -23,6 +23,7 @@ require "telnyx/api_operations/list"
23
23
  require "telnyx/api_operations/nested_resource"
24
24
  require "telnyx/api_operations/request"
25
25
  require "telnyx/api_operations/save"
26
+ require "telnyx/api_operations/param_wrapper"
26
27
 
27
28
  # API resource support classes
28
29
  require "telnyx/errors"
@@ -35,6 +36,8 @@ require "telnyx/api_resource"
35
36
  require "telnyx/singleton_api_resource"
36
37
  require "telnyx/webhook"
37
38
 
39
+ require "telnyx/call"
40
+ require "telnyx/conferences"
38
41
  require "telnyx/number_order"
39
42
  require "telnyx/number_reservation"
40
43
  require "telnyx/message"
@@ -9,18 +9,30 @@ module Telnyx
9
9
  # For examle, a transfer gains the static methods for reversals so that the
10
10
  # methods `.create_reversal`, `.retrieve_reversal`, `.update_reversal`,
11
11
  # etc. all become available.
12
+ # rubocop:disable Metrics/AbcSize
13
+ # rubocop:disable Metrics/MethodLength
12
14
  module NestedResource
13
- def nested_resource_class_methods(resource, path: nil, operations: nil)
15
+ def nested_resource_class_methods(resource, path: nil, operations: nil, instance_methods: {})
14
16
  path ||= "#{resource}s"
17
+ path = Array(path).map { |el| CGI.escape(el) }.join("/")
15
18
  raise ArgumentError, "operations array required" if operations.nil?
16
19
 
17
20
  resource_url_method = :"#{resource}s_url"
18
21
  define_singleton_method(resource_url_method) do |id, nested_id = nil|
19
- url = "#{resource_url}/#{CGI.escape(id)}/#{CGI.escape(path)}"
22
+ url = "#{resource_url}/#{CGI.escape(id)}/#{path}"
20
23
  url += "/#{CGI.escape(nested_id)}" unless nested_id.nil?
21
24
  url
22
25
  end
23
26
 
27
+ # proxy class method to custom instance method
28
+ define_instance_method = lambda do |target_name, operation|
29
+ return unless instance_methods.keys.include? operation
30
+
31
+ define_method(instance_methods[operation] || target_name) do |*opts|
32
+ self.class.send(target_name, id, *opts)
33
+ end
34
+ end
35
+
24
36
  operations.each do |operation|
25
37
  case operation
26
38
  when :create
@@ -29,30 +41,35 @@ module Telnyx
29
41
  resp, opts = request(:post, url, params, opts)
30
42
  Util.convert_to_telnyx_object(resp.data, opts)
31
43
  end
44
+ define_instance_method.call(:"create_#{resource}", operation)
32
45
  when :retrieve
33
46
  define_singleton_method(:"retrieve_#{resource}") do |id, nested_id, opts = {}|
34
47
  url = send(resource_url_method, id, nested_id)
35
48
  resp, opts = request(:get, url, {}, opts)
36
49
  Util.convert_to_telnyx_object(resp.data, opts)
37
50
  end
51
+ define_instance_method.call(:"retrieve_#{resource}", operation)
38
52
  when :update
39
53
  define_singleton_method(:"update_#{resource}") do |id, nested_id, params = {}, opts = {}|
40
54
  url = send(resource_url_method, id, nested_id)
41
55
  resp, opts = request(:patch, url, params, opts)
42
56
  Util.convert_to_telnyx_object(resp.data, opts)
43
57
  end
58
+ define_instance_method.call(:"update_#{resource}", operation)
44
59
  when :delete
45
60
  define_singleton_method(:"delete_#{resource}") do |id, nested_id, params = {}, opts = {}|
46
61
  url = send(resource_url_method, id, nested_id)
47
62
  resp, opts = request(:delete, url, params, opts)
48
63
  Util.convert_to_telnyx_object(resp.data, opts)
49
64
  end
65
+ define_instance_method.call(:"delete_#{resource}", operation)
50
66
  when :list
51
67
  define_singleton_method(:"list_#{resource}s") do |id, params = {}, opts = {}|
52
68
  url = send(resource_url_method, id)
53
69
  resp, opts = request(:get, url, params, opts)
54
70
  Util.convert_to_telnyx_object(resp.data, opts)
55
71
  end
72
+ define_instance_method.call(:"list_#{resource}s", operation)
56
73
  else
57
74
  raise ArgumentError, "Unknown operation: #{operation.inspect}"
58
75
  end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Telnyx
4
+ module APIOperations
5
+ # Intercepts request params passed to api operation methods and wraps them in a single param.
6
+ # Usage:
7
+ # class << self
8
+ # prepend Telnyx::ParamWrapper
9
+ # wrap :list, 'filter'
10
+ # end
11
+ module ParamWrapper
12
+ protected
13
+
14
+ def wrap(method_name, wrapper)
15
+ define_singleton_method(method_name) do |filters = {}, opts = {}|
16
+ return super(filters, opts) if filters.keys == [wrapper]
17
+
18
+ filters = { wrapper => filters }
19
+ super filters, opts
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -3,6 +3,8 @@
3
3
  module Telnyx
4
4
  class AvailablePhoneNumber < APIResource
5
5
  extend Telnyx::APIOperations::List
6
+ extend Telnyx::APIOperations::ParamWrapper
7
+ wrap "list", "filter"
6
8
 
7
9
  OBJECT_NAME = "available_phone_number".freeze
8
10
  end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Telnyx
4
+ class Call < APIResource
5
+ extend Telnyx::APIOperations::Create
6
+ extend Telnyx::APIOperations::NestedResource
7
+
8
+ def id
9
+ call_control_id if defined? call_control_id
10
+ end
11
+
12
+ def id=(val)
13
+ initialize_from({ call_control_id: val }, {}, true)
14
+ end
15
+
16
+ %w[call_leg_id call_session_id].each do |attribute|
17
+ define_method attribute do
18
+ send(attribute) if respond_to?(attribute)
19
+ end
20
+ define_method attribute + "=" do |val|
21
+ initialize_from({ attribute.to_sym => val }, {}, true)
22
+ end
23
+ end
24
+
25
+ ACTIONS = %w[reject answer hangup bridge speak fork_start fork_stop
26
+ gather_using_audio gather_using_speak playback_start
27
+ playback_stop record_start record_stop send_dtmf transfer].freeze
28
+
29
+ ACTIONS.each do |action|
30
+ nested_resource_class_methods action,
31
+ path: ["actions", action],
32
+ operations: [:create],
33
+ instance_methods: { create: action }
34
+ end
35
+
36
+ OBJECT_NAME = "call".freeze
37
+ end
38
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Telnyx
4
+ class Conferences < APIResource
5
+ extend APIOperations::List
6
+ extend APIOperations::Create
7
+ extend APIOperations::NestedResource
8
+
9
+ ACTIONS = %w[join mute unmute hold unhold].freeze
10
+
11
+ ACTIONS.each do |action|
12
+ nested_resource_class_methods action,
13
+ path: ["actions", action],
14
+ operations: [:create],
15
+ instance_methods: { create: action }
16
+ end
17
+ OBJECT_NAME = "conference".freeze
18
+ end
19
+ end
@@ -11,22 +11,13 @@ module Telnyx
11
11
  OBJECT_NAME = "messaging_profile".freeze
12
12
 
13
13
  nested_resource_class_methods :phone_number,
14
- operations: %i[list]
14
+ operations: %i[list],
15
+ instance_methods: { list: "phone_numbers" }
15
16
  nested_resource_class_methods :sender_id,
16
- operations: %i[list]
17
+ operations: %i[list],
18
+ instance_methods: { list: "sender_ids" }
17
19
  nested_resource_class_methods :short_code,
18
- operations: %i[list]
19
-
20
- def phone_numbers(params = {}, opts = {})
21
- self.class.list_phone_numbers(id, params, opts)
22
- end
23
-
24
- def sender_ids(params = {}, opts = {})
25
- self.class.list_sender_ids(id, params, opts)
26
- end
27
-
28
- def short_codes(params = {}, opts = {})
29
- self.class.list_short_codes(id, params, opts)
30
- end
20
+ operations: %i[list],
21
+ instance_methods: { list: "short_codes" }
31
22
  end
32
23
  end
@@ -4,8 +4,17 @@ module Telnyx
4
4
  class NumberReservation < APIResource
5
5
  extend Telnyx::APIOperations::List
6
6
  extend Telnyx::APIOperations::Create
7
+ extend Telnyx::APIOperations::NestedResource
7
8
  include Telnyx::APIOperations::Save
8
9
 
10
+ nested_resource_class_methods :extend,
11
+ path: %w[actions extend],
12
+ operations: [:create],
13
+ instance_methods: { create: "extend_number" }
14
+ class << self
15
+ alias extend_number create_extend
16
+ end
17
+
9
18
  OBJECT_NAME = "number_reservation".freeze
10
19
  end
11
20
  end
@@ -48,9 +48,16 @@ module Telnyx
48
48
  PublicKey::OBJECT_NAME => PublicKey,
49
49
  NumberOrder::OBJECT_NAME => NumberOrder,
50
50
  NumberReservation::OBJECT_NAME => NumberReservation,
51
+ Call::OBJECT_NAME => Call,
52
+ Conferences::OBJECT_NAME => Conferences,
51
53
  }
52
54
  end
53
55
 
56
+ def self.push_object_class(key, klass)
57
+ object_classes
58
+ @object_classes[key] = klass
59
+ end
60
+
54
61
  # Converts a hash of fields or an array of hashes into a +TelnyxObject+ or
55
62
  # array of +TelnyxObject+s. These new objects will be created as a concrete
56
63
  # type as dictated by their `record_type` field (e.g. a `record_type` value of
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "openssl"
4
4
  require "base64"
5
+ require "ed25519"
5
6
 
6
7
  module Telnyx
7
8
  module Webhook
@@ -35,31 +36,34 @@ module Telnyx
35
36
  # Returns true otherwise
36
37
  def self.verify(payload, signature_header, timestamp, tolerance: nil)
37
38
  signature = Base64.decode64(signature_header)
39
+ timestamp = timestamp.to_i
38
40
  signed_payload = "#{timestamp}|#{payload}"
39
41
 
40
- unless public_key.verify(digest, signature, signed_payload)
42
+ if tolerance && timestamp < Time.now.to_f - tolerance
41
43
  raise SignatureVerificationError.new(
42
- "Signature is invalid and does not match the payload",
43
- signature, http_body: payload
44
+ "Timestamp outside the tolerance zone (#{Time.at(timestamp)})",
45
+ signature_header, http_body: payload
44
46
  )
45
47
  end
46
48
 
47
- if tolerance && timestamp < Time.now.to_f - tolerance
49
+ begin
50
+ verify_key.verify(signature, signed_payload)
51
+ rescue Ed25519::VerifyError
48
52
  raise SignatureVerificationError.new(
49
- "Timestamp outside the tolerance zone (#{Time.at(timestamp)})",
50
- signature_header, http_body: payload
53
+ "Signature is invalid and does not match the payload",
54
+ signature, http_body: payload
51
55
  )
52
56
  end
53
57
 
54
58
  true
55
59
  end
56
60
 
57
- def self.public_key
58
- @public_key ||= OpenSSL::PKey::RSA.new(ENV.fetch("TELNYX_PUBLIC_KEY"))
61
+ def self.verify_key
62
+ @verify_key ||= reload_verify_key
59
63
  end
60
64
 
61
- def self.digest
62
- @digest ||= OpenSSL::Digest::SHA256.new
65
+ def self.reload_verify_key
66
+ @verify_key = Ed25519::VerifyKey.new(Base64.decode64(ENV.fetch("TELNYX_PUBLIC_KEY")))
63
67
  end
64
68
  end
65
69
  end
@@ -6,17 +6,25 @@ require "telnyx/version"
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "telnyx"
9
- s.version = Telnyx::VERSION
9
+ s.version = '0.0.2'
10
10
  s.required_ruby_version = ">= 2.1.0"
11
11
  s.summary = "Ruby bindings for the Telnyx API"
12
- s.description = "Telnyx. See https://www.telnyx.com for details."
12
+ s.description = "Telnyx enables anyone to deliver enterprise-grade real-time communications over the internet. See https://telnyx.com for details."
13
13
  s.author = "Telnyx"
14
14
  s.email = "support@telnyx.com"
15
- s.homepage = "https://developers.telnyx.com/docs/api/ruby"
15
+ s.homepage = "https://developers.telnyx.com"
16
16
  s.license = "MIT"
17
17
 
18
+ s.metadata = {
19
+ "documentation_uri" => "https://developers.telnyx.com/docs/api/v2/overview",
20
+ "github_repo" => "ssh://github.com/team-telnyx/telnyx-ruby",
21
+ "homepage_uri" => "https://telnyx.com",
22
+ "source_code_uri" => "https://github.com/team-telnyx/telnyx-ruby",
23
+ }
24
+
18
25
  s.add_dependency("faraday", "~> 0.13")
19
26
  s.add_dependency("net-http-persistent", "~> 3.0")
27
+ s.add_dependency("ed25519", "~> 1")
20
28
 
21
29
  s.files = `git ls-files`.split("\n")
22
30
  s.test_files = `git ls-files -- test/*`.split("\n")
@@ -37,8 +37,10 @@ module Telnyx
37
37
  class MainResource < APIResource
38
38
  extend Telnyx::APIOperations::NestedResource
39
39
  OBJECT_NAME = "mainresource".freeze
40
+ Telnyx::Util.push_object_class OBJECT_NAME, self
40
41
  nested_resource_class_methods :nested,
41
- operations: %i[create retrieve update delete list]
42
+ operations: %i[create retrieve update delete list],
43
+ instance_methods: { create: nil, retrieve: nil, update: nil, delete: nil, list: nil }
42
44
  end
43
45
 
44
46
  should "define a create method" do
@@ -49,6 +51,15 @@ module Telnyx
49
51
  assert_equal "bar", nested_resource.foo
50
52
  end
51
53
 
54
+ should "define a create instance method" do
55
+ stub_request(:post, "#{Telnyx.api_base}/v2/mainresources/id/nesteds")
56
+ .with(body: { foo: "bar" })
57
+ .to_return(body: JSON.generate(id: "nested_id", object: "nested", foo: "bar"))
58
+ resource = Telnyx::Util.convert_to_telnyx_object(id: "id", record_type: "mainresource")
59
+ nested_resource = resource.create_nested(foo: "bar")
60
+ assert_equal "bar", nested_resource.foo
61
+ end
62
+
52
63
  should "define a retrieve method" do
53
64
  stub_request(:get, "#{Telnyx.api_base}/v2/mainresources/id/nesteds/nested_id")
54
65
  .to_return(body: JSON.generate(id: "nested_id", object: "nested", foo: "bar"))
@@ -56,6 +67,14 @@ module Telnyx
56
67
  assert_equal "bar", nested_resource.foo
57
68
  end
58
69
 
70
+ should "define a retrieve instance method" do
71
+ stub_request(:get, "#{Telnyx.api_base}/v2/mainresources/id/nesteds/nested_id")
72
+ .to_return(body: JSON.generate(id: "nested_id", object: "nested", foo: "bar"))
73
+ resource = Telnyx::Util.convert_to_telnyx_object(id: "id", record_type: "mainresource")
74
+ nested_resource = resource.retrieve_nested("nested_id")
75
+ assert_equal "bar", nested_resource.foo
76
+ end
77
+
59
78
  should "define an update method" do
60
79
  stub_patch = stub_request(:patch, "#{Telnyx.api_base}/v2/mainresources/id/nesteds/nested_id")
61
80
  .with(body: { foo: "baz" })
@@ -65,6 +84,16 @@ module Telnyx
65
84
  assert_equal "baz", nested_resource.foo
66
85
  end
67
86
 
87
+ should "define an update instance method" do
88
+ stub_patch = stub_request(:patch, "#{Telnyx.api_base}/v2/mainresources/id/nesteds/nested_id")
89
+ .with(body: { foo: "baz" })
90
+ .to_return(body: JSON.generate(id: "nested_id", object: "nested", foo: "baz"))
91
+ resource = Telnyx::Util.convert_to_telnyx_object(id: "id", record_type: "mainresource")
92
+ nested_resource = resource.update_nested("nested_id", foo: "baz")
93
+ assert_requested(stub_patch)
94
+ assert_equal "baz", nested_resource.foo
95
+ end
96
+
68
97
  should "define a delete method" do
69
98
  stub_request(:delete, "#{Telnyx.api_base}/v2/mainresources/id/nesteds/nested_id")
70
99
  .to_return(body: JSON.generate(id: "nested_id", object: "nested", deleted: true))
@@ -72,6 +101,14 @@ module Telnyx
72
101
  assert_equal true, nested_resource.deleted
73
102
  end
74
103
 
104
+ should "define a delete instance method" do
105
+ stub_request(:delete, "#{Telnyx.api_base}/v2/mainresources/id/nesteds/nested_id")
106
+ .to_return(body: JSON.generate(id: "nested_id", object: "nested", deleted: true))
107
+ resource = Telnyx::Util.convert_to_telnyx_object(id: "id", record_type: "mainresource")
108
+ nested_resource = resource.delete_nested("nested_id")
109
+ assert_equal true, nested_resource.deleted
110
+ end
111
+
75
112
  should "define a list method" do
76
113
  stub_get = stub_request(:get, "#{Telnyx.api_base}/v2/mainresources/id/nesteds")
77
114
  .to_return(body: JSON.generate(data: [{ record_type: "foo" }]))
@@ -80,6 +117,16 @@ module Telnyx
80
117
  assert nested_resources.is_a?(ListObject)
81
118
  assert nested_resources.data.is_a?(Array)
82
119
  end
120
+
121
+ should "define a list instance method" do
122
+ stub_get = stub_request(:get, "#{Telnyx.api_base}/v2/mainresources/id/nesteds")
123
+ .to_return(body: JSON.generate(data: [{ record_type: "foo" }]))
124
+ resource = Telnyx::Util.convert_to_telnyx_object(id: "id", record_type: "mainresource")
125
+ nested_resources = resource.list_nesteds
126
+ assert_requested(stub_get)
127
+ assert nested_resources.is_a?(ListObject)
128
+ assert nested_resources.data.is_a?(Array)
129
+ end
83
130
  end
84
131
  end
85
132
  end
@@ -10,5 +10,10 @@ module Telnyx
10
10
  assert available_phone_numbers.data.is_a?(Array)
11
11
  assert available_phone_numbers.first.is_a?(Telnyx::AvailablePhoneNumber)
12
12
  end
13
+
14
+ should "accept params for list" do
15
+ Telnyx::AvailablePhoneNumber.list phone_number: { starts_with: "2&&", ends_with: "ABC" }
16
+ assert_requested(:get, /#{"#{Telnyx.api_base}/v2/available_phone_numbers"}/)
17
+ end
13
18
  end
14
19
  end
@@ -0,0 +1,157 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../test_helper"
4
+ require "securerandom"
5
+
6
+ module Telnyx
7
+ class CallControlTest < Test::Unit::TestCase
8
+ setup do
9
+ @call = create_call
10
+ end
11
+ context "call instance" do
12
+ should "be correct class" do
13
+ assert create_call.is_a? Telnyx::Call
14
+ end
15
+
16
+ should "be initialized with data" do
17
+ refute_nil @call.call_control_id
18
+ refute_nil @call.call_leg_id
19
+ refute_nil @call.call_session_id
20
+ refute_nil @call.is_alive
21
+ refute_nil @call.record_type
22
+ end
23
+
24
+ should "have generated instance methods methods" do
25
+ assert defined? @call.reject
26
+ assert defined? @call.answer
27
+ assert defined? @call.hangup
28
+ assert defined? @call.bridge
29
+ assert defined? @call.speak
30
+ assert defined? @call.fork_start
31
+ assert defined? @call.fork_stop
32
+ assert defined? @call.gather_using_audio
33
+ assert defined? @call.gather_using_speak
34
+ assert defined? @call.playback_start
35
+ assert defined? @call.playback_stop
36
+ assert defined? @call.record_start
37
+ assert defined? @call.record_stop
38
+ assert defined? @call.send_dtmf
39
+ assert defined? @call.transfer
40
+ end
41
+ end
42
+
43
+ context "ojbect created through #new" do
44
+ should "get and set call_control_id through alias" do
45
+ call = Call.new
46
+ refute call.id
47
+ call.id = "123"
48
+ assert_equal "123", call.id
49
+ end
50
+
51
+ should "have initialize_object accessors" do
52
+ call = Call.new
53
+ call.id = SecureRandom.base64(20)
54
+ call.call_leg_id = SecureRandom.base64(20)
55
+ call.call_session_id = SecureRandom.base64(20)
56
+
57
+ assert call.id
58
+ assert call.call_leg_id
59
+ assert call.call_session_id
60
+ end
61
+
62
+ should "send all commands" do
63
+ @call = Call.new
64
+ @call.id = "1234"
65
+ @call.reject
66
+ assert_requested :post, format_url(@call, "reject")
67
+ @call.answer
68
+ assert_requested :post, format_url(@call, "answer")
69
+ @call.hangup
70
+ assert_requested :post, format_url(@call, "hangup")
71
+ @call.bridge call_control_id: SecureRandom.base64(20)
72
+ assert_requested :post, format_url(@call, "bridge")
73
+ @call.speak language: "en-US", voice: "female", payload: "Telnyx call control test"
74
+ assert_requested :post, format_url(@call, "speak")
75
+ @call.fork_start call_control_id: SecureRandom.base64(20)
76
+ assert_requested :post, format_url(@call, "fork_start")
77
+ @call.fork_stop
78
+ assert_requested :post, format_url(@call, "fork_stop")
79
+ @call.gather_using_audio audio_url: "https://audio.example.com"
80
+ assert_requested :post, format_url(@call, "gather_using_audio")
81
+ @call.gather_using_speak language: "en-US", voice: "female", payload: "Telnyx call control test"
82
+ assert_requested :post, format_url(@call, "gather_using_speak")
83
+ @call.playback_start audio_url: "https://audio.example.com"
84
+ assert_requested :post, format_url(@call, "playback_start")
85
+ @call.playback_stop
86
+ assert_requested :post, format_url(@call, "playback_stop")
87
+ @call.send_dtmf digits: "1www2WABCDw9"
88
+ assert_requested :post, format_url(@call, "send_dtmf")
89
+ @call.transfer to: "+15552223333"
90
+ assert_requested :post, format_url(@call, "transfer")
91
+ end
92
+ end
93
+
94
+ context "commands" do
95
+ should "reject" do
96
+ @call.reject
97
+ assert_requested :post, format_url(@call, "reject")
98
+ end
99
+ should "answer" do
100
+ @call.answer
101
+ assert_requested :post, format_url(@call, "answer")
102
+ end
103
+ should "hangup" do
104
+ @call.hangup
105
+ assert_requested :post, format_url(@call, "hangup")
106
+ end
107
+ should "bridge" do
108
+ @call.bridge call_control_id: SecureRandom.base64(20)
109
+ assert_requested :post, format_url(@call, "bridge")
110
+ end
111
+ should "speak" do
112
+ @call.speak language: "en-US", voice: "female", payload: "Telnyx call control test"
113
+ assert_requested :post, format_url(@call, "speak")
114
+ end
115
+ should "start fork" do
116
+ @call.fork_start call_control_id: SecureRandom.base64(20)
117
+ assert_requested :post, format_url(@call, "fork_start")
118
+ end
119
+ should "stop fork" do
120
+ @call.fork_stop
121
+ assert_requested :post, format_url(@call, "fork_stop")
122
+ end
123
+ should "gather using audio" do
124
+ @call.gather_using_audio audio_url: "https://audio.example.com"
125
+ assert_requested :post, format_url(@call, "gather_using_audio")
126
+ end
127
+ should "gather using speak" do
128
+ @call.gather_using_speak language: "en-US", voice: "female", payload: "Telnyx call control test"
129
+ assert_requested :post, format_url(@call, "gather_using_speak")
130
+ end
131
+ should "playback start" do
132
+ @call.playback_start audio_url: "https://audio.example.com"
133
+ assert_requested :post, format_url(@call, "playback_start")
134
+ end
135
+ should "playback stop" do
136
+ @call.playback_stop
137
+ assert_requested :post, format_url(@call, "playback_stop")
138
+ end
139
+ should "send dtmf" do
140
+ @call.send_dtmf digits: "1www2WABCDw9"
141
+ assert_requested :post, format_url(@call, "send_dtmf")
142
+ end
143
+ should "transfer" do
144
+ @call.transfer to: "+15552223333"
145
+ assert_requested :post, format_url(@call, "transfer")
146
+ end
147
+ end
148
+
149
+ def create_call
150
+ Telnyx::Call.create connection_id: "12345", to: "+15550001111", from: "+15550002222"
151
+ end
152
+
153
+ def format_url(call, action)
154
+ "#{Telnyx.api_base}/v2/calls/#{call.call_control_id}/actions/#{action}"
155
+ end
156
+ end
157
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../test_helper"
4
+
5
+ module Telnyx
6
+ class ConferencesTest < Test::Unit::TestCase
7
+ setup do
8
+ @call = create_call
9
+ @conference = Conferences.create call_control_id: @call.id, name: "conference!"
10
+ end
11
+ should "create conference" do
12
+ assert_requested :post, "#{Telnyx.api_base}/v2/conferences"
13
+ assert_kind_of Conferences, @conference
14
+ end
15
+
16
+ should "list conferences" do
17
+ conferences = Conferences.list
18
+
19
+ assert_requested :get, "#{Telnyx.api_base}/v2/conferences"
20
+ assert_kind_of ListObject, conferences
21
+ assert_kind_of Conferences, conferences.first
22
+ end
23
+
24
+ should "have nested command instance methods" do
25
+ assert defined? @conference.join
26
+ assert defined? @conference.mute
27
+ assert defined? @conference.unmute
28
+ assert defined? @conference.unhold
29
+ end
30
+
31
+ context "commands" do
32
+ should "join" do
33
+ stub = stub_request(:post, format_url(@conference, "join"))
34
+ .to_return(body: JSON.generate(result: "ok"))
35
+ @conference.join
36
+ assert_requested stub
37
+ end
38
+
39
+ should "mute" do
40
+ stub = stub_request(:post, format_url(@conference, "mute"))
41
+ .to_return(body: JSON.generate(result: "ok"))
42
+ @conference.mute
43
+ assert_requested stub
44
+ end
45
+
46
+ should "unmute" do
47
+ stub = stub_request(:post, format_url(@conference, "unmute"))
48
+ .to_return(body: JSON.generate(result: "ok"))
49
+ @conference.unmute
50
+ assert_requested stub
51
+ end
52
+
53
+ should "hold" do
54
+ stub = stub_request(:post, format_url(@conference, "hold"))
55
+ .to_return(body: JSON.generate(result: "ok"))
56
+ @conference.hold
57
+ assert_requested stub
58
+ end
59
+
60
+ should "unhold" do
61
+ stub = stub_request(:post, format_url(@conference, "unhold"))
62
+ .to_return(body: JSON.generate(result: "ok"))
63
+ @conference.unhold
64
+ assert_requested stub
65
+ end
66
+ end
67
+
68
+ def create_call
69
+ Telnyx::Call.create connection_id: "12345", to: "+15550001111", from: "+15550002222"
70
+ end
71
+
72
+ def format_url(conf, action)
73
+ "#{Telnyx.api_base}/v2/conferences/#{conf.id}/actions/#{action}"
74
+ end
75
+ end
76
+ end
@@ -8,5 +8,13 @@ module Telnyx
8
8
  assert_kind_of Telnyx::ListObject, number_reservations
9
9
  assert_kind_of Telnyx::NumberReservation, number_reservations.first
10
10
  end
11
+
12
+ should "call extend" do
13
+ number_reservation = Telnyx::NumberReservation.list.first
14
+ stub = stub_request(:post, "#{Telnyx.api_base}/v2/number_reservations/#{number_reservation.id}/actions/extend")
15
+ .to_return(body: JSON.generate(id: "123"))
16
+ number_reservation.extend_number
17
+ assert_requested stub
18
+ end
11
19
  end
12
20
  end
@@ -1,48 +1,95 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require ::File.expand_path("../../test_helper", __FILE__)
4
+ require "securerandom"
4
5
 
5
6
  module Telnyx
6
7
  class WebhookTest < Test::Unit::TestCase
7
8
  EVENT_PAYLOAD = <<-PAYLOAD.freeze
8
9
  {
9
10
  "data": {
10
- "record_type": "event",
11
- "id": "0ccc7b54-4df3-4bca-a65a-3da1ecc777f0",
12
- "event_type": "port_request.ported",
13
- "created_at": "2018-02-02T22:25:27.521992Z",
11
+ "event_type": "message.received",
12
+ "id": "39547d14-fe28-4759-8650-0a974f80a612",
13
+ "occurred_at": "2019-07-30T23:07:36.628+00:00",
14
14
  "payload": {
15
- "id": "5ccc7b54-4df3-4bca-a65a-3da1ecc777f0"
16
- }
15
+ "completed_at": null,
16
+ "cost": null,
17
+ "direction": "inbound",
18
+ "encoding": "GSM-7",
19
+ "errors": [],
20
+ "from": {
21
+ "carrier": "T-Mobile USA",
22
+ "line_type": "long_code",
23
+ "phone_number": "+15745203340",
24
+ "status": "webhook_delivered"
25
+ },
26
+ "id": "1e9ee507-56a5-4d1a-b26b-db3cebcc267b",
27
+ "media": [],
28
+ "messaging_profile_id": "13c22476-7a37-4b79-b432-e629fc3b529c",
29
+ "organization_id": "4fc14b9e-5f17-493d-bb3d-01a8e575566d",
30
+ "parts": 1,
31
+ "received_at": "2019-07-30T23:07:36.623+00:00",
32
+ "record_type": "message",
33
+ "sent_at": null,
34
+ "text": "And again",
35
+ "to": "+18446087849",
36
+ "type": "SMS",
37
+ "valid_until": null,
38
+ "webhook_failover_url": null,
39
+ "webhook_url": "http://webhook.site/b041b025-0111-49dc-be4f-6b5f4bca31e4"
40
+ },
41
+ "record_type": "event"
42
+ },
43
+ "meta": {
44
+ "attempt": 1,
45
+ "delivered_to": "http://webhook.site/b041b025-0111-49dc-be4f-6b5f4bca31e4"
17
46
  }
18
47
  }
19
48
  PAYLOAD
20
49
 
21
- # rubocop:disable Metrics/LineLength
22
- PUBLIC_KEY = "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwy/jPkkgBo7oQermYujj\nAmSqN+aHNg+D4K85lKn6T3khJ8O2t/FrgN5qSGqg+0U5hoIHZflEon28lbLdf6gZ\njPeKQ2a24w5zroR6e4MM00RyJWA6MWXdo6Tn6xqKMYuT8LffEJGnXCH4yTIkxAVD\nyK0dfewhtrlpmW5ojXcDCrZ3Oo1o588PLNwSIuQwU7wHZwOLglWxFt6LZ9Ps8zYf\nQNH/pXNczf1E4rGZ1QxrzqFbndvjCE5VDRhULhycT/X0H2EMvNgHsDQk4OhENnzo\nCal3vO5+P9MgC7NSZCR8Ubebq0tanL5dj5GGYyjWmeq3QhfDLX2mTpIv/B0e8+hg\n8QIDAQAB\n-----END PUBLIC KEY-----\n".freeze
23
-
24
- PRIVATE_KEY = "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAwy/jPkkgBo7oQermYujjAmSqN+aHNg+D4K85lKn6T3khJ8O2\nt/FrgN5qSGqg+0U5hoIHZflEon28lbLdf6gZjPeKQ2a24w5zroR6e4MM00RyJWA6\nMWXdo6Tn6xqKMYuT8LffEJGnXCH4yTIkxAVDyK0dfewhtrlpmW5ojXcDCrZ3Oo1o\n588PLNwSIuQwU7wHZwOLglWxFt6LZ9Ps8zYfQNH/pXNczf1E4rGZ1QxrzqFbndvj\nCE5VDRhULhycT/X0H2EMvNgHsDQk4OhENnzoCal3vO5+P9MgC7NSZCR8Ubebq0ta\nnL5dj5GGYyjWmeq3QhfDLX2mTpIv/B0e8+hg8QIDAQABAoIBAQCNwoP6wsVdvgD1\njxNQlu/41v/Bpc5h9xbC4sChNmqzubfY144nPlHjwKXUfoz4sag8Bsg0ybuNgGCt\nIME6a+5SsZ5boYgGlIJ0J4eFmQKBll6IwsDBC8jTh3thB1+C6GrEE+cQc5jnk0zL\nY33MWD6IyyJ2SD+cJEGLy+JnjB5LckGCQXWPQXwvpIKgGmFoLQzHCKfeKHZ3olB8\nC1+YKrQzLtyuuH9obDWxRSrqI5gOI/76PWmo+weNa4OrfFtBf5O9bo5OD17ilIT/\nuNpxb/7rOkpwU9x6D00/D/S7ecCdVoL2yBB5L635TNQKXxhvdSmBg1ceLlztwsUL\nOHIlglTZAoGBAOY3wyincm8iAUxLE+Z3AeTD94pjND4g2JXFF9E7UDxgRD6E3n38\nubNRdAMkxDmDYgyIOZsebykMadQ2vNiWqTjOBr6hxyQMFutHWrIJOU+peFCepn8u\nNX3Xg44l7KcwwqX5svoqgFl1FKwNpBOSo50oGX4lAgqtjYqEeMInfjZ7AoGBANkL\nz0wBNAr9oXsc0BN2WkQXB34RU/WcrymhxHfc+ZRzRShk9LOdBTuMYnj4rtjdG849\nJDDWlMk7UlzGjI07G5aT+n8Aq69BhV0IARC9PafTncE6G3sHswAQudHiurLflP9C\noj6kTakunrq8Kgj3Q1p6Ie+Hv7E01A3D0Difr4CDAoGAXORrLuBB4G3MMEirAvdK\nIFCidYiJ7/e47NXWQmq4eWQupTtfu15aX+yh7xLKypok2gGtnNWu7NVBbouXr507\nMtyPBCSrAfSO2uizw9rM8UPkdENP00mF8/0d7CGJV/zozafve9niaDZB3Rqz9eHZ\nevRPNQMhy8Uzs4y4XT8qQjkCgYBNuLjmkpe8R86Hc23fSkZQk56POk1CanUfB1p/\nQZXt3skpCd3GY7f39vFcOFEEP0kxtRs8kdp9pMx9hGvYNw5OAXd1+xt/iorjIXag\nM+PcMR8QjmpAyCUFJPglfHc2jnGgZpAKtnNI3fThEXhL9Z8cyxdT2tx97FjzBOeP\nHz+NWQKBgQCU0bSxTp2rbOCxHosQ/GDDTY0JkQ2z5q1SkibSiEnyAZ3yCHpXZRD7\nsa5BWs4qlasSKmxdmT9xgRDAL6CJH6kJizF3UIaIPOvPjIroOa7Mk1OFNbOi6Cao\n0LcWp5w1I2r5g7sOIRM/AcS3yVT5RJO4KB8WyDOvxCfP8cFsTacZmQ==\n-----END RSA PRIVATE KEY-----\n".freeze
25
- # rubocop:enable Metrics/LineLength
50
+ def timestamp
51
+ @timestamp ||= Time.now.to_i
52
+ end
26
53
 
27
54
  def generate_signature(opts = {})
28
- opts[:timestamp] ||= Time.now.to_i
55
+ opts[:timestamp] ||= timestamp
29
56
  opts[:payload] ||= EVENT_PAYLOAD
30
- opts[:private_key] ||= PRIVATE_KEY
57
+ opts[:private_key] ||= signing_key
58
+
59
+ stamped_payload = "#{opts[:timestamp]}|#{opts[:payload]}"
60
+ Base64.encode64 opts[:private_key].sign(stamped_payload)
61
+ end
31
62
 
32
- private_key = OpenSSL::PKey::RSA.new(opts[:private_key])
33
- signature = private_key.sign(OpenSSL::Digest::SHA256.new, "#{opts[:timestamp]}|#{opts[:payload]}")
34
- Base64.encode64(signature)
63
+ def signing_key
64
+ @signing_key ||= Ed25519::SigningKey.generate
35
65
  end
36
66
 
37
67
  def setup
38
68
  super
39
- ENV["TELNYX_PUBLIC_KEY"] = PUBLIC_KEY
69
+ ENV["TELNYX_PUBLIC_KEY"] = Base64.encode64(signing_key.verify_key.to_bytes)
70
+ Telnyx::Webhook::Signature.reload_verify_key
71
+ end
72
+
73
+ context "generated test keys" do
74
+ should "sign and verify" do
75
+ message = "foobar"
76
+ signature = signing_key.sign message
77
+ verify_key = Ed25519::VerifyKey.new(Base64.decode64(ENV.fetch("TELNYX_PUBLIC_KEY")))
78
+ assert verify_key.verify(signature, message)
79
+ end
80
+
81
+ should "create and decode signature" do
82
+ message = "lorem ipsum"
83
+ stamped_message = "#{timestamp}|#{message}"
84
+ signature = generate_signature(payload: message)
85
+ verify_key = signing_key.verify_key
86
+ assert verify_key.verify(Base64.decode64(signature), stamped_message)
87
+ end
40
88
  end
41
89
 
42
90
  context ".construct_event" do
43
91
  should "return an Event instance from a valid JSON payload and valid signature header" do
44
- timestamp = Time.now.to_i
45
- signature = generate_signature(timestamp: timestamp)
92
+ signature = generate_signature
46
93
  event = Telnyx::Webhook.construct_event(EVENT_PAYLOAD, signature, timestamp)
47
94
  assert event.is_a?(Telnyx::Event)
48
95
  end
@@ -50,15 +97,13 @@ module Telnyx
50
97
  should "raise a JSON::ParserError from an invalid JSON payload" do
51
98
  assert_raises JSON::ParserError do
52
99
  payload = "this is not valid JSON"
53
- timestamp = Time.now.to_i
54
- signature = generate_signature(payload: payload, timestamp: timestamp)
100
+ signature = generate_signature payload: payload
55
101
  Telnyx::Webhook.construct_event(payload, signature, timestamp)
56
102
  end
57
103
  end
58
104
 
59
105
  should "raise a SignatureVerificationError from a valid JSON payload and an invalid signature header" do
60
- signature = "bad_signature"
61
- timestamp = Time.now.to_i
106
+ signature = SecureRandom.base64(64)
62
107
  assert_raises Telnyx::SignatureVerificationError do
63
108
  Telnyx::Webhook.construct_event(EVENT_PAYLOAD, signature, timestamp)
64
109
  end
@@ -67,15 +112,14 @@ module Telnyx
67
112
 
68
113
  context ".verify" do
69
114
  should "raise a SignatureVerificationError when the signature does not have the expected format" do
70
- signature = "FAKEFAKEFAKE"
115
+ signature = SecureRandom.base64(64)
71
116
  e = assert_raises(Telnyx::SignatureVerificationError) do
72
- Telnyx::Webhook::Signature.verify(EVENT_PAYLOAD, signature, Time.now.to_i)
117
+ Telnyx::Webhook::Signature.verify(EVENT_PAYLOAD, signature, timestamp)
73
118
  end
74
119
  assert_match("Signature is invalid and does not match the payload", e.message)
75
120
  end
76
121
 
77
122
  should "raise a SignatureVerificationError when there are no valid signatures for the payload" do
78
- timestamp = Time.now.to_i
79
123
  signature = generate_signature(payload: "foo", timestamp: timestamp)
80
124
  e = assert_raises(Telnyx::SignatureVerificationError) do
81
125
  Telnyx::Webhook::Signature.verify(EVENT_PAYLOAD, signature, timestamp)
@@ -84,16 +128,14 @@ module Telnyx
84
128
  end
85
129
 
86
130
  should "raise a SignatureVerificationError when the timestamp is not within the tolerance" do
87
- timestamp = Time.now.to_i - 15
88
131
  signature = generate_signature(timestamp: Time.now.to_i - 15)
89
132
  e = assert_raises(Telnyx::SignatureVerificationError) do
90
- Telnyx::Webhook::Signature.verify(EVENT_PAYLOAD, signature, timestamp, tolerance: 10)
133
+ Telnyx::Webhook::Signature.verify(EVENT_PAYLOAD, signature, timestamp - 15, tolerance: 10)
91
134
  end
92
135
  assert_match("Timestamp outside the tolerance zone", e.message)
93
136
  end
94
137
 
95
138
  should "return true when the signature is valid and the timestamp is within the tolerance" do
96
- timestamp = Time.now.to_i
97
139
  signature = generate_signature
98
140
  assert(Telnyx::Webhook::Signature.verify(EVENT_PAYLOAD, signature, timestamp, tolerance: 10))
99
141
  end
@@ -17,7 +17,7 @@ require ::File.expand_path("../test_data", __FILE__)
17
17
  require ::File.expand_path("../telnyx_mock", __FILE__)
18
18
 
19
19
  # If changing this number, please also change it in `.travis.yml`.
20
- MOCK_MINIMUM_VERSION = "0.40.1".freeze
20
+ MOCK_MINIMUM_VERSION = "0.2.0".freeze
21
21
  MOCK_PORT = Telnyx::TelnyxMock.start
22
22
 
23
23
  # Disable all real network connections except those that are outgoing to
@@ -53,7 +53,7 @@ module Test
53
53
  include Mocha
54
54
 
55
55
  setup do
56
- Telnyx.api_key = "KEYSUPERSECRET"
56
+ Telnyx.api_key = "KEYSUPERSECRET" # mock server expects this exact string
57
57
  Telnyx.api_base = "http://localhost:#{MOCK_PORT}"
58
58
 
59
59
  # stub_connect
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: telnyx
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Telnyx
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-07-17 00:00:00.000000000 Z
11
+ date: 2019-08-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -38,18 +38,31 @@ dependencies:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '3.0'
41
- description: Telnyx. See https://www.telnyx.com for details.
41
+ - !ruby/object:Gem::Dependency
42
+ name: ed25519
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1'
55
+ description: Telnyx enables anyone to deliver enterprise-grade real-time communications
56
+ over the internet. See https://telnyx.com for details.
42
57
  email: support@telnyx.com
43
58
  executables:
44
59
  - telnyx-console
45
60
  extensions: []
46
61
  extra_rdoc_files: []
47
62
  files:
48
- - ".gitattributes"
49
63
  - ".github/ISSUE_TEMPLATE.md"
50
64
  - ".gitignore"
51
65
  - ".rubocop.yml"
52
- - ".rubocop_todo.yml"
53
66
  - ".travis.yml"
54
67
  - CHANGELOG.md
55
68
  - CONTRIBUTORS
@@ -65,10 +78,13 @@ files:
65
78
  - lib/telnyx/api_operations/delete.rb
66
79
  - lib/telnyx/api_operations/list.rb
67
80
  - lib/telnyx/api_operations/nested_resource.rb
81
+ - lib/telnyx/api_operations/param_wrapper.rb
68
82
  - lib/telnyx/api_operations/request.rb
69
83
  - lib/telnyx/api_operations/save.rb
70
84
  - lib/telnyx/api_resource.rb
71
85
  - lib/telnyx/available_phone_number.rb
86
+ - lib/telnyx/call.rb
87
+ - lib/telnyx/conferences.rb
72
88
  - lib/telnyx/errors.rb
73
89
  - lib/telnyx/event.rb
74
90
  - lib/telnyx/list_object.rb
@@ -93,6 +109,8 @@ files:
93
109
  - test/telnyx/api_operations_test.rb
94
110
  - test/telnyx/api_resource_test.rb
95
111
  - test/telnyx/available_phone_number_test.rb
112
+ - test/telnyx/call_control_test.rb
113
+ - test/telnyx/conferences_test.rb
96
114
  - test/telnyx/errors_test.rb
97
115
  - test/telnyx/list_object_test.rb
98
116
  - test/telnyx/message_test.rb
@@ -112,10 +130,14 @@ files:
112
130
  - test/telnyx_test.rb
113
131
  - test/test_data.rb
114
132
  - test/test_helper.rb
115
- homepage: https://developers.telnyx.com/docs/api/ruby
133
+ homepage: https://developers.telnyx.com
116
134
  licenses:
117
135
  - MIT
118
- metadata: {}
136
+ metadata:
137
+ documentation_uri: https://developers.telnyx.com/docs/api/v2/overview
138
+ github_repo: ssh://github.com/team-telnyx/telnyx-ruby
139
+ homepage_uri: https://telnyx.com
140
+ source_code_uri: https://github.com/team-telnyx/telnyx-ruby
119
141
  post_install_message:
120
142
  rdoc_options: []
121
143
  require_paths:
@@ -141,6 +163,8 @@ test_files:
141
163
  - test/telnyx/api_operations_test.rb
142
164
  - test/telnyx/api_resource_test.rb
143
165
  - test/telnyx/available_phone_number_test.rb
166
+ - test/telnyx/call_control_test.rb
167
+ - test/telnyx/conferences_test.rb
144
168
  - test/telnyx/errors_test.rb
145
169
  - test/telnyx/list_object_test.rb
146
170
  - test/telnyx/message_test.rb
@@ -1,4 +0,0 @@
1
- # may be useful in hiding large schema changes in pull request diffs (but not
2
- # using it for now)
3
- spec/*.json binary
4
- spec/*.yaml binary
@@ -1,50 +0,0 @@
1
- # This configuration was generated by
2
- # `rubocop --auto-gen-config`
3
- # on 2018-07-19 14:22:24 +0200 using RuboCop version 0.50.0.
4
- # The point is for the user to remove these configuration records
5
- # one by one as the offenses are removed from the code base.
6
- # Note that changes in the inspected code, or installation of new
7
- # versions of RuboCop, may require this file to be generated again.
8
-
9
- # Offense count: 19
10
- Metrics/AbcSize:
11
- Max: 52
12
-
13
- # Offense count: 27
14
- # Configuration parameters: CountComments, ExcludedMethods.
15
- Metrics/BlockLength:
16
- Max: 498
17
-
18
- # Offense count: 8
19
- # Configuration parameters: CountComments.
20
- Metrics/ClassLength:
21
- Max: 659
22
-
23
- # Offense count: 11
24
- Metrics/CyclomaticComplexity:
25
- Max: 15
26
-
27
- # Offense count: 278
28
- # Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
29
- # URISchemes: http, https
30
- Metrics/LineLength:
31
- Max: 310
32
-
33
- # Offense count: 6
34
- # Configuration parameters: CountKeywordArgs.
35
- Metrics/ParameterLists:
36
- Max: 7
37
-
38
- # Offense count: 5
39
- Metrics/PerceivedComplexity:
40
- Max: 17
41
-
42
- # Offense count: 2
43
- Style/ClassVars:
44
- Exclude:
45
- - 'lib/telnyx/telnyx_object.rb'
46
- - 'test/telnyx/api_resource_test.rb'
47
-
48
- # Offense count: 56
49
- Style/Documentation:
50
- Enabled: false