Helper to allow ordering phone number from Bandwidth v2
Stephen Paul Weber
created 4 years ago
Uses their gem which uses Faraday. Set Faraday to em-synchrony so their gem is
now using EventMachine but still sync so their code will work unchanged. Wrap
all uses of the gem in EM.promise_fiber to get a promise back out of that.
Implement a poll helper that can wait until a new order is complete at Bandwidth
before continuing. They support an HTTP callback method, but only global on
account? This is much easier to work with in our context.
Change summary
.rubocop.yml | 3 +
Gemfile | 2
lib/bandwidth_tn_order.rb | 87 ++++++++++++++++++++++++++++++++++
test/test_bandwidth_tn_order.rb | 89 +++++++++++++++++++++++++++++++++++
test/test_helper.rb | 17 ++++++
5 files changed, 198 insertions(+)
Detailed changes
@@ -45,6 +45,9 @@ Layout/SpaceAroundEqualsInParameterDefault:
Layout/AccessModifierIndentation:
EnforcedStyle: outdent
+Layout/FirstParameterIndentation:
+ EnforcedStyle: consistent
+
Style/BlockDelimiters:
EnforcedStyle: braces_for_chaining
@@ -8,9 +8,11 @@ gem "dhall"
gem "em-hiredis"
gem "em-http-request"
gem "em-pg-client", git: "https://github.com/royaltm/ruby-em-pg-client"
+gem "em-synchrony"
gem "em_promise.rb"
gem "eventmachine"
gem "money-open-exchange-rates"
+gem "ruby-bandwidth-iris"
group(:development) do
gem "pry-reload"
@@ -0,0 +1,87 @@
+# frozen_string_literal: true
+
+require "forwardable"
+require "ruby-bandwidth-iris"
+Faraday.default_adapter = :em_synchrony
+
+class BandwidthTNOrder
+ def self.get(id)
+ EM.promise_fiber do
+ self.for(BandwidthIris::Order.get_order_response(
+ # https://github.com/Bandwidth/ruby-bandwidth-iris/issues/44
+ BandwidthIris::Client.new,
+ id
+ ))
+ end
+ end
+
+ def self.create(tel, name: "sgx-jmp order #{tel}")
+ bw_tel = tel.sub(/^\+?1?/, "")
+ EM.promise_fiber do
+ Received.new(BandwidthIris::Order.create(
+ name: name,
+ site_id: CONFIG[:bandwidth_site],
+ existing_telephone_number_order_type: {
+ telephone_number_list: { telephone_number: [bw_tel] }
+ }
+ ))
+ end
+ end
+
+ def self.for(bandwidth_order)
+ const_get(bandwidth_order.order_status.capitalize).new(bandwidth_order)
+ rescue NameError
+ new(bandwidth_order)
+ end
+
+ extend Forwardable
+ def_delegators :@order, :id
+
+ def initialize(bandwidth_order)
+ @order = bandwidth_order
+ end
+
+ def status
+ @order[:order_status]&.downcase&.to_sym
+ end
+
+ def error_description
+ @order[:error_list]&.dig(:error, :description)
+ end
+
+ def poll
+ raise "Unknown order status: #{status}"
+ end
+
+ class Received < BandwidthTNOrder
+ def status
+ :received
+ end
+
+ def poll
+ EM.promise_timer(1).then do
+ BandwidthTNOrder.get(id).then(&:poll)
+ end
+ end
+ end
+
+ class Complete < BandwidthTNOrder
+ def status
+ :complete
+ end
+
+ def poll
+ EMPromise.resolve(self)
+ end
+ end
+
+ class Failed < BandwidthTNOrder
+ def status
+ :failed
+ end
+
+ def poll
+ raise "Order failed: #{id} #{error_description}"
+ end
+ end
+end
@@ -0,0 +1,89 @@
+# frozen_string_literal: true
+
+require "test_helper"
+require "bandwidth_tn_order"
+
+class BandwidthTNOrderTest < Minitest::Test
+ def test_for_received
+ order = BandwidthTNOrder.for(BandwidthIris::Order.new(
+ order_status: "RECEIVED"
+ ))
+ assert_kind_of BandwidthTNOrder::Received, order
+ end
+
+ def test_for_complete
+ order = BandwidthTNOrder.for(BandwidthIris::Order.new(
+ order_status: "COMPLETE"
+ ))
+ assert_kind_of BandwidthTNOrder::Complete, order
+ end
+
+ def test_for_failed
+ order = BandwidthTNOrder.for(BandwidthIris::Order.new(
+ order_status: "FAILED"
+ ))
+ assert_kind_of BandwidthTNOrder::Failed, order
+ end
+
+ def test_for_unknown
+ order = BandwidthTNOrder.for(BandwidthIris::Order.new(
+ order_status: "randOmgarBagE"
+ ))
+ assert_kind_of BandwidthTNOrder, order
+ assert_equal :randomgarbage, order.status
+ end
+
+ def test_poll
+ order = BandwidthTNOrder.new(BandwidthIris::Order.new)
+ assert_raises { order.poll.sync }
+ end
+ em :test_poll
+
+ class TestReceived < Minitest::Test
+ def setup
+ @order = BandwidthTNOrder::Received.new(
+ BandwidthIris::Order.new(id: "oid")
+ )
+ end
+
+ def test_poll
+ req = stub_request(
+ :get,
+ "https://dashboard.bandwidth.com/v1.0/accounts//orders/oid"
+ ).to_return(status: 200, body: <<~RESPONSE)
+ <OrderResponse>
+ <OrderStatus>COMPLETE</OrderStatus>
+ </OrderResponse>
+ RESPONSE
+ new_order = PromiseMock.new
+ new_order.expect(:poll, nil)
+ @order.poll.sync
+ assert_requested req
+ end
+ em :test_poll
+ end
+
+ class TestComplete < Minitest::Test
+ def setup
+ @order = BandwidthTNOrder::Complete.new(BandwidthIris::Order.new)
+ end
+
+ def test_poll
+ assert_equal @order, @order.poll.sync
+ end
+ em :test_poll
+ end
+
+ class TestFailed < Minitest::Test
+ def setup
+ @order = BandwidthTNOrder::Failed.new(
+ BandwidthIris::Order.new(id: "oid")
+ )
+ end
+
+ def test_poll
+ assert_raises { @order.poll.sync }
+ end
+ em :test_poll
+ end
+end
@@ -70,6 +70,23 @@ class Matching
end
end
+class PromiseMock < Minitest::Mock
+ def then
+ yield self
+ end
+end
+
+module EventMachine
+ class << self
+ # Patch EM.add_timer to be instant in tests
+ alias old_add_timer add_timer
+ def add_timer(*args, &block)
+ args[0] = 0
+ old_add_timer(*args, &block)
+ end
+ end
+end
+
module Minitest
class Test
def self.property(m, &block)