Merge branch 'add-oauth' of https://git.secluded.site/sgx-jmp

Stephen Paul Weber created

* 'add-oauth' of https://git.secluded.site/sgx-jmp:
  Switch BW clients from Basic to Client creds
  Update bandwidth gems/use for OAuth

Change summary

Gemfile                              |  8 +-
bin/porting                          |  1 
config-schema.dhall                  |  8 ++
config.dhall.sample                  |  2 
lib/bandwidth_tn_order.rb            |  1 
lib/bandwidth_tn_repo.rb             |  4 
lib/bandwidth_tn_reservation_repo.rb |  1 
lib/customer_fwd.rb                  | 14 ++--
lib/tel_selections.rb                |  1 
sgx_jmp.rb                           | 16 +++--
test/test_admin_command.rb           |  2 
test/test_bandwidth_tn_repo.rb       |  4 
test/test_helper.rb                  | 16 ++++++
test/test_web.rb                     | 78 +++++++++++++++---------------
web.rb                               | 10 +-
15 files changed, 99 insertions(+), 67 deletions(-)

Detailed changes

Gemfile 🔗

@@ -3,7 +3,7 @@
 source "https://rubygems.org"
 
 gem "amazing_print"
-gem "bandwidth-sdk", "<= 6.1.0"
+gem "bandwidth-sdk", "~> 17.1"
 gem "blather", git: "https://github.com/singpolyma/blather", branch: "better-ids"
 gem "braintree"
 gem "countries"
@@ -15,15 +15,17 @@ gem "em-pg-client", git: "https://github.com/singpolyma/ruby-em-pg-client", bran
 gem "em_promise.rb", "~> 0.0.4"
 gem "em-synchrony"
 gem "eventmachine"
-gem "faraday-em_http", git: "https://github.com/singpolyma/faraday-em_http", branch: "fix-gzip"
+gem "faraday-em_http", git: "https://github.com/Amolith/faraday-em_http", branch: "fix-gzip"
+gem "faraday-em_synchrony", git: "https://github.com/Amolith/faraday-em_synchrony", branch: "update-for-faraday-2"
 gem "link-header-parser", ">= 7.0.0"
 gem "money-open-exchange-rates"
 gem "multibases"
 gem "multihashes"
+gem "multi_json"
 gem "ougai"
 gem "relative_time"
 gem "roda"
-gem "ruby-bandwidth-iris"
+gem "ruby-bandwidth-iris", "~> 7.4"
 gem "sentry-ruby", "<= 4.3.1"
 gem "slim"
 gem "statsd-instrument", git: "https://github.com/singpolyma/statsd-instrument.git", branch: "graphite"

bin/porting 🔗

@@ -7,6 +7,7 @@ require "em-hiredis"
 require "pg/em/connection_pool"
 require "em-http"
 require "em_promise"
+require "faraday/em_synchrony"
 require "json"
 require "optparse"
 require "ruby-bandwidth-iris"

config-schema.dhall 🔗

@@ -17,7 +17,13 @@
 , churnbuster : { account_id : Text, api_key : Text }
 , component : { jid : Text, secret : Text }
 , credit_card_url : forall (jid : Text) -> forall (customer_id : Text) -> Text
-, creds : { account : Text, password : Text, username : Text }
+, creds :
+    { account : Text
+    , client_id : Text
+    , client_secret : Text
+    , password : Text
+    , username : Text
+    }
 , direct_sources : List { mapKey : Text, mapValue : Text }
 , direct_targets : List { mapKey : Text, mapValue : Text }
 , electrum :

config.dhall.sample 🔗

@@ -19,6 +19,8 @@ in
 	],
 	creds = {
 		account = "00000",
+		client_id = "oauth client id",
+		client_secret = "oauth client secret",
 		username = "dashboard user",
 		password = "dashboard password"
 	},

lib/bandwidth_tn_order.rb 🔗

@@ -2,6 +2,7 @@
 
 require "forwardable"
 require "ruby-bandwidth-iris"
+require "faraday/em_synchrony"
 Faraday.default_adapter = :em_synchrony
 
 class BandwidthTNOrder

lib/bandwidth_tn_repo.rb 🔗

@@ -25,8 +25,8 @@ class BandwidthTnRepo
 		@move_client =
 			BandwidthIris::Client.new(
 				account_id: CONFIG[:keep_area_codes_in][:account],
-				username: CONFIG[:creds][:username],
-				password: CONFIG[:creds][:password]
+				client_id: CONFIG[:creds][:client_id],
+				client_secret: CONFIG[:creds][:client_secret]
 			)
 	end
 

lib/bandwidth_tn_reservation_repo.rb 🔗

@@ -2,6 +2,7 @@
 
 require "forwardable"
 require "ruby-bandwidth-iris"
+require "faraday/em_synchrony"
 Faraday.default_adapter = :em_synchrony
 
 class BandwidthTnReservationRepo

lib/customer_fwd.rb 🔗

@@ -1,6 +1,6 @@
 # frozen_string_literal: true
 
-require "bandwidth"
+require "bandwidth-sdk"
 require "value_semantics/monkey_patched"
 require "uri"
 
@@ -108,12 +108,12 @@ class CustomerFwd
 	def create_call(account)
 		raise InfiniteTimeout, self if timeout.infinite?
 
-		request = Bandwidth::ApiCreateCallRequest.new.tap { |cc|
-			cc.to = to
-			cc.call_timeout = timeout.to_i
-			yield cc if block_given?
-		}
-		BANDWIDTH_VOICE.create_call(account, body: request).data.call_id
+		request = OpenStruct.new(to: to, call_timeout: timeout.to_i)
+		yield request if block_given?
+		BANDWIDTH_VOICE.create_call(
+			account,
+			Bandwidth::CreateCall.new(request.to_h)
+		).call_id
 	end
 
 	def as_json(*)

lib/tel_selections.rb 🔗

@@ -1,6 +1,7 @@
 # frozen_string_literal: true
 
 require "ruby-bandwidth-iris"
+require "faraday/em_synchrony"
 Faraday.default_adapter = :em_synchrony
 
 require "cbor"

sgx_jmp.rb 🔗

@@ -1,13 +1,14 @@
 # frozen_string_literal: true
 
 require "pg/em/connection_pool"
-require "bandwidth"
+require "bandwidth-sdk"
 require "bigdecimal"
 require "blather/client/dsl"
 require "date"
 require "dhall"
 require "em-hiredis"
 require "em_promise"
+require "faraday/em_synchrony"
 require "ougai"
 require "ruby-bandwidth-iris"
 require "sentry-ruby"
@@ -120,13 +121,14 @@ EM::Hiredis::Client.load_scripts_from("#{__dir__}/redis_lua")
 Faraday.default_adapter = :em_synchrony
 BandwidthIris::Client.global_options = {
 	account_id: CONFIG[:creds][:account],
-	username: CONFIG[:creds][:username],
-	password: CONFIG[:creds][:password]
+	client_id: CONFIG[:creds][:client_id],
+	client_secret: CONFIG[:creds][:client_secret]
 }
-BANDWIDTH_VOICE = Bandwidth::Client.new(
-	voice_basic_auth_user_name: CONFIG[:creds][:username],
-	voice_basic_auth_password: CONFIG[:creds][:password]
-).voice_client.client
+Bandwidth.configure do |config|
+	config.client_id = CONFIG[:creds][:client_id]
+	config.client_secret = CONFIG[:creds][:client_secret]
+end
+BANDWIDTH_VOICE = Bandwidth::CallsApi.new
 
 class AuthError < StandardError; end
 

test/test_admin_command.rb 🔗

@@ -367,7 +367,7 @@ class AdminCommandTest < Minitest::Test
 			headers: {
 				"Accept" => "application/xml",
 				"Accept-Encoding" => "gzip, compressed",
-				"Authorization" => "Basic dGVzdF9id191c2VyOnRlc3RfYndfcGFzc3dvcmQ=",
+				"Authorization" => "Bearer test_bw_oauth_token",
 				"User-Agent" => "Ruby-Bandwidth-Iris"
 			}
 		).to_return(status: 200, body: details_response, headers: {})

test/test_bandwidth_tn_repo.rb 🔗

@@ -15,7 +15,7 @@ class BandwidthTnRepoTest < Minitest::Test
 				headers: {
 					"Accept" => "application/xml",
 					"Accept-Encoding" => "gzip, compressed",
-					"Authorization" => "Basic dGVzdF9id191c2VyOnRlc3RfYndfcGFzc3dvcmQ=",
+					"Authorization" => "Bearer test_bw_oauth_token",
 					"User-Agent" => "Ruby-Bandwidth-Iris"
 				}
 			).to_return(status: 200, body: <<~XML, headers: {})
@@ -53,7 +53,7 @@ class BandwidthTnRepoTest < Minitest::Test
 				headers: {
 					"Accept" => "application/xml",
 					"Accept-Encoding" => "gzip, compressed",
-					"Authorization" => "Basic dGVzdF9id191c2VyOnRlc3RfYndfcGFzc3dvcmQ=",
+					"Authorization" => "Bearer test_bw_oauth_token",
 					"User-Agent" => "Ruby-Bandwidth-Iris"
 				}
 			).to_return(status: 200, body: <<~XML, headers: {})

test/test_helper.rb 🔗

@@ -85,6 +85,8 @@ CONFIG = {
 	},
 	creds: {
 		account: "test_bw_account",
+		client_id: "test_bw_client_id",
+		client_secret: "test_bw_client_secret",
 		username: "test_bw_user",
 		password: "test_bw_password"
 	},
@@ -501,6 +503,20 @@ end
 
 module Minitest
 	class Test
+		def setup
+			oauth_body = {
+				access_token: "test_bw_oauth_token",
+				token_type: "Bearer",
+				expires_in: 3600
+			}.to_json
+
+			WebMock.stub_request(:post, "https://api.bandwidth.com/api/v1/oauth2/token")
+				.to_return(status: 200, body: oauth_body, headers: {
+					"Content-Type" => "application/json"
+				})
+			super
+		end
+
 		def self.property(m, &block)
 			define_method("test_#{m}") do
 				property_of(&block).check { |args| send(m, *args) }

test/test_web.rb 🔗

@@ -534,27 +534,27 @@ class WebTest < Minitest::Test
 	em :test_outbound_disconnect_tombed
 
 	def test_inbound
+		expected_answer_url =
+			"http://example.org/inbound/calls/acall?customer_id=customerid"
+
 		CustomerPlan::DB.expect(
 			:query_one, {}, [String, "customerid"]
 		)
 		CustomerFwd::BANDWIDTH_VOICE.expect(
 			:create_call,
-			OpenStruct.new(data: OpenStruct.new(call_id: "ocall")),
-			["test_bw_account"],
-			body: Matching.new do |arg|
-				assert_equal(
-					"http://example.org/inbound/calls/acall?customer_id=customerid",
-					arg.answer_url
-				)
-			end
-		)
+			OpenStruct.new(call_id: "ocall")
+		) do |account, request, *|
+			assert_equal("test_bw_account", account)
+			assert_equal(expected_answer_url, request.answer_url)
+		end
 
 		post(
 			"/inbound/calls",
 			{
 				from: "+15557654321",
 				to: "+15551234567",
-				callId: "acall"
+				callId: "acall",
+				applicationId: "app123"
 			}.to_json,
 			{ "CONTENT_TYPE" => "application/json" }
 		)
@@ -572,20 +572,19 @@ class WebTest < Minitest::Test
 	em :test_inbound
 
 	def test_inbound_from_reachability
+		expected_answer_url =
+			"http://example.org/inbound/calls/acall?customer_id=customerid"
+
 		CustomerPlan::DB.expect(
 			:query_one, {}, [String, "customerid"]
 		)
 		CustomerFwd::BANDWIDTH_VOICE.expect(
 			:create_call,
-			OpenStruct.new(data: OpenStruct.new(call_id: "ocall")),
-			["test_bw_account"],
-			body: Matching.new do |arg|
-				assert_equal(
-					"http://example.org/inbound/calls/acall?customer_id=customerid",
-					arg.answer_url
-				)
-			end
-		)
+			OpenStruct.new(call_id: "ocall")
+		) do |account, request, *|
+			assert_equal("test_bw_account", account)
+			assert_equal(expected_answer_url, request.answer_url)
+		end
 
 		ReachableRedis.expect(
 			:exists,
@@ -598,7 +597,8 @@ class WebTest < Minitest::Test
 			{
 				from: "+14445556666",
 				to: "+15551234567",
-				callId: "acall"
+				callId: "acall",
+				applicationId: "app123"
 			}.to_json,
 			{ "CONTENT_TYPE" => "application/json" }
 		)
@@ -617,27 +617,27 @@ class WebTest < Minitest::Test
 	em :test_inbound_from_reachability
 
 	def test_inbound_no_bwmsgsv2
+		expected_answer_url =
+			"http://example.org/inbound/calls/acall?customer_id=customerid2"
+
 		CustomerPlan::DB.expect(
 			:query_one, {}, [String, "customerid2"]
 		)
 		CustomerFwd::BANDWIDTH_VOICE.expect(
 			:create_call,
-			OpenStruct.new(data: OpenStruct.new(call_id: "ocall")),
-			["test_bw_account"],
-			body: Matching.new do |arg|
-				assert_equal(
-					"http://example.org/inbound/calls/acall?customer_id=customerid2",
-					arg.answer_url
-				)
-			end
-		)
+			OpenStruct.new(call_id: "ocall")
+		) do |account, request, *|
+			assert_equal("test_bw_account", account)
+			assert_equal(expected_answer_url, request.answer_url)
+		end
 
 		post(
 			"/inbound/calls",
 			{
 				from: "+15557654321",
 				to: "+15551230000",
-				callId: "acall"
+				callId: "acall",
+				applicationId: "app123"
 			}.to_json,
 			{ "CONTENT_TYPE" => "application/json" }
 		)
@@ -775,18 +775,18 @@ class WebTest < Minitest::Test
 
 	def test_inbound_limit_hangup
 		Web::BANDWIDTH_VOICE.expect(
-			:modify_call,
+			:update_call,
 			nil,
 			[
 				"test_bw_account",
-				"bcall"
-			],
-			body: Matching.new do |arg|
-				assert_equal(
-					"http://example.org/inbound/calls/oocall/voicemail",
-					arg.redirect_url
-				)
-			end
+				"bcall",
+				Matching.new do |arg|
+					assert_equal(
+						"http://example.org/inbound/calls/oocall/voicemail",
+						arg.redirect_url
+					)
+				end
+			]
 		)
 
 		post(

web.rb 🔗

@@ -181,17 +181,17 @@ class Web < Roda
 	end
 
 	def modify_call(call_id)
-		body = Bandwidth::ApiModifyCallRequest.new
+		body = Bandwidth::UpdateCall.new
 		yield body
-		BANDWIDTH_VOICE.modify_call(
+		BANDWIDTH_VOICE.update_call(
 			CONFIG[:creds][:account],
 			call_id,
-			body: body
+			body
 		)
-	rescue Bandwidth::APIException
+	rescue Bandwidth::ApiError
 		# If call does not exist, don't need to hang up or send to voicemail
 		# Other side must have hung up already
-		raise $! unless [404, 409].include?($!.response_code)
+		raise $! unless [404, 409].include?($!.code)
 	end
 
 	def start_transcription(customer, call_id, media_url)