Import newly-purchased numbers to Catapult

Stephen Paul Weber created

Since we still use Catapult ("v1") for voice application, we need to import all
numbers there and set their applicationId to match.

Change summary

.rubocop.yml                        |  4 ++
config.dhall.sample                 |  6 +++
lib/bandwidth_tn_order.rb           | 54 ++++++++++++++++++++++++++++++
test/data/catapult_import_body.json |  1 
test/test_bandwidth_tn_order.rb     | 34 +++++++++++++++++-
test/test_helper.rb                 |  6 +++
test/test_registration.rb           | 15 ++++++++
7 files changed, 116 insertions(+), 4 deletions(-)

Detailed changes

.rubocop.yml 🔗

@@ -6,6 +6,10 @@ Metrics/LineLength:
   Exclude:
     - Gemfile
 
+Metrics/ClassLength:
+  Exclude:
+    - test/*
+
 Metrics/MethodLength:
   Exclude:
     - test/*

config.dhall.sample 🔗

@@ -13,6 +13,12 @@
 		username = "dashboard user",
 		password = "dashboard password"
 	},
+	catapult = {
+		user = "",
+		token = "",
+		secret = "",
+		application_id = ""
+	},
 	bandwidth_site = "",
 	braintree = {
 		environment = "sandbox",

lib/bandwidth_tn_order.rb 🔗

@@ -70,8 +70,60 @@ class BandwidthTNOrder
 			:complete
 		end
 
+		def tel
+			"+1#{@order.completed_numbers[:telephone_number][:full_number]}"
+		end
+
 		def poll
-			EMPromise.resolve(self)
+			catapult_import.then do |http|
+				raise "Catapult import failed" unless http.response_header.status == 201
+
+				self
+			end
+		end
+
+	protected
+
+		# After buying, import to catapult and set v1 voice app
+		def catapult_import
+			catapult_request.apost(
+				head: catapult_headers,
+				body: {
+					number: tel,
+					applicationId: catapult[:application_id],
+					provider: dashboard_provider
+				}.to_json
+			)
+		end
+
+		def catapult
+			CONFIG[:catapult]
+		end
+
+		def catapult_request
+			EM::HttpRequest.new(
+				"https://api.catapult.inetwork.com/v1/users/" \
+				"#{catapult[:user]}/phoneNumbers",
+				tls: { verify_peer: true }
+			)
+		end
+
+		def catapult_headers
+			{
+				"Authorization" => [catapult[:token], catapult[:secret]],
+				"Content-Type" => "application/json"
+			}
+		end
+
+		def dashboard_provider
+			{
+				providerName: "bandwidth-dashboard",
+				properties: {
+					accountId: CONFIG[:creds][:account],
+					userName: CONFIG[:creds][:username],
+					password: CONFIG[:creds][:password]
+				}
+			}
 		end
 	end
 

test/data/catapult_import_body.json 🔗

@@ -0,0 +1 @@
+{"number":"+15555550000","applicationId":"catapult_app","provider":{"providerName":"bandwidth-dashboard","properties":{"accountId":"test_bw_account","userName":"test_bw_user","password":"test_bw_password"}}}

test/test_bandwidth_tn_order.rb 🔗

@@ -53,10 +53,23 @@ class BandwidthTNOrderTest < Minitest::Test
 			).to_return(status: 200, body: <<~RESPONSE)
 				<OrderResponse>
 					<OrderStatus>COMPLETE</OrderStatus>
+					<CompletedNumbers>
+						<TelephoneNumber>
+							<FullNumber>5555550000</FullNumber>
+						</TelephoneNumber>
+					</CompletedNumbers>
 				</OrderResponse>
 			RESPONSE
-			new_order = PromiseMock.new
-			new_order.expect(:poll, nil)
+			stub_request(
+				:post,
+				"https://api.catapult.inetwork.com/v1/users/catapult_user/phoneNumbers"
+			).with(
+				body: open(__dir__ + "/data/catapult_import_body.json").read.chomp,
+				headers: {
+					"Authorization" => "Basic Y2F0YXB1bHRfdG9rZW46Y2F0YXB1bHRfc2VjcmV0",
+					"Content-Type" => "application/json"
+				}
+			).to_return(status: 200)
 			@order.poll.sync
 			assert_requested req
 		end
@@ -65,11 +78,26 @@ class BandwidthTNOrderTest < Minitest::Test
 
 	class TestComplete < Minitest::Test
 		def setup
-			@order = BandwidthTNOrder::Complete.new(BandwidthIris::Order.new)
+			@order = BandwidthTNOrder::Complete.new(BandwidthIris::Order.new(
+				completed_numbers: {
+					telephone_number: { full_number: "5555550000" }
+				}
+			))
 		end
 
 		def test_poll
+			req = stub_request(
+				:post,
+				"https://api.catapult.inetwork.com/v1/users/catapult_user/phoneNumbers"
+			).with(
+				body: open(__dir__ + "/data/catapult_import_body.json").read.chomp,
+				headers: {
+					"Authorization" => "Basic Y2F0YXB1bHRfdG9rZW46Y2F0YXB1bHRfc2VjcmV0",
+					"Content-Type" => "application/json"
+				}
+			).to_return(status: 200)
 			assert_equal @order, @order.poll.sync
+			assert_requested req
 		end
 		em :test_poll
 	end

test/test_helper.rb 🔗

@@ -44,6 +44,12 @@ CONFIG = {
 		username: "test_bw_user",
 		password: "test_bw_password"
 	},
+	catapult: {
+		user: "catapult_user",
+		token: "catapult_token",
+		secret: "catapult_secret",
+		application_id: "catapult_app"
+	},
 	activation_amount: 1,
 	plans: [
 		{

test/test_registration.rb 🔗

@@ -273,8 +273,23 @@ class RegistrationTest < Minitest::Test
 			).to_return(status: 201, body: <<~RESPONSE)
 				<OrderResponse>
 					<OrderStatus>COMPLETE</OrderStatus>
+					<CompletedNumbers>
+						<TelephoneNumber>
+							<FullNumber>5555550000</FullNumber>
+						</TelephoneNumber>
+					</CompletedNumbers>
 				</OrderResponse>
 			RESPONSE
+			stub_request(
+				:post,
+				"https://api.catapult.inetwork.com/v1/users/catapult_user/phoneNumbers"
+			).with(
+				body: open(__dir__ + "/data/catapult_import_body.json").read.chomp,
+				headers: {
+					"Authorization" => "Basic Y2F0YXB1bHRfdG9rZW46Y2F0YXB1bHRfc2VjcmV0",
+					"Content-Type" => "application/json"
+				}
+			).to_return(status: 200)
 			BACKEND_SGX.expect(
 				:register!,
 				EMPromise.resolve(OpenStruct.new(error?: false)),