telnyx 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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