From e199a02068e09ce5400067c5c5c9ae6face0c6ef Mon Sep 17 00:00:00 2001 From: Amolith Date: Thu, 5 Mar 2026 13:37:01 -0700 Subject: [PATCH 1/2] Update bandwidth gems/use for OAuth --- Gemfile | 8 +-- bin/porting | 1 + lib/bandwidth_tn_order.rb | 1 + lib/bandwidth_tn_reservation_repo.rb | 1 + lib/customer_fwd.rb | 14 ++--- lib/tel_selections.rb | 1 + sgx_jmp.rb | 12 +++-- test/test_admin_command.rb | 2 +- test/test_bandwidth_tn_repo.rb | 4 +- test/test_helper.rb | 14 +++++ test/test_web.rb | 78 ++++++++++++++-------------- web.rb | 10 ++-- 12 files changed, 84 insertions(+), 62 deletions(-) diff --git a/Gemfile b/Gemfile index 7debb5d3d23ae3b1e5359f85cdf925bf7bb4af01..6788c00316463f22e608634b50f7142d43ffd750 100644 --- a/Gemfile +++ b/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" diff --git a/bin/porting b/bin/porting index a2ecc9f62b5c2b392cf485b92fbf1089c8233b8b..a94ca345cdd8bbc33bbf7172f3a9fbc79be816a1 100755 --- a/bin/porting +++ b/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" diff --git a/lib/bandwidth_tn_order.rb b/lib/bandwidth_tn_order.rb index 2ae770b344b0c701a974e64e5582288308d8e960..e376fc33f9b473d72f4780614a0851a526a843e5 100644 --- a/lib/bandwidth_tn_order.rb +++ b/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 diff --git a/lib/bandwidth_tn_reservation_repo.rb b/lib/bandwidth_tn_reservation_repo.rb index e425f05b64147d3edc68c3e88a94529c5806e6b0..b9112cdf962a98b6540df6e4d472be0f965162b5 100644 --- a/lib/bandwidth_tn_reservation_repo.rb +++ b/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 diff --git a/lib/customer_fwd.rb b/lib/customer_fwd.rb index a584fd1888e962b393c929af2337a34e088b647e..e7320eaa21ddcddbe74dc5b37bc584cfc8dcd557 100644 --- a/lib/customer_fwd.rb +++ b/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(*) diff --git a/lib/tel_selections.rb b/lib/tel_selections.rb index 146869a81231549539a3c51bd5c74d470953e5a2..4c7ebe43276ef1560992c6a2809720b06e420863 100644 --- a/lib/tel_selections.rb +++ b/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" diff --git a/sgx_jmp.rb b/sgx_jmp.rb index 58fd53a704293546f19cb45d8c718df919ba995a..32072121a736c0623ab7a9f8f7e6db1fb9ed240d 100644 --- a/sgx_jmp.rb +++ b/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" @@ -123,10 +124,11 @@ BandwidthIris::Client.global_options = { username: CONFIG[:creds][:username], password: CONFIG[:creds][:password] } -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][:username] + config.client_secret = CONFIG[:creds][:password] +end +BANDWIDTH_VOICE = Bandwidth::CallsApi.new class AuthError < StandardError; end diff --git a/test/test_admin_command.rb b/test/test_admin_command.rb index 040805082ebf0c7f5b0f8a2a772e3aa2acf74984..0ebaba9b7061e035e5d610d23b47f9151adc2529 100644 --- a/test/test_admin_command.rb +++ b/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: {}) diff --git a/test/test_bandwidth_tn_repo.rb b/test/test_bandwidth_tn_repo.rb index a90b4fd38bb000ac55c726eb50aa11d4513e6b9b..ad258e14ac39e156800eba5b95ca9b3338d2b0c1 100644 --- a/test/test_bandwidth_tn_repo.rb +++ b/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: {}) diff --git a/test/test_helper.rb b/test/test_helper.rb index 91c9b5b03e15f23de26405803034858e6e1bd934..6cb4956b78d259bf5ba3598604d50d5cdc0c28e6 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -501,6 +501,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) } diff --git a/test/test_web.rb b/test/test_web.rb index 8677f382159312555b27ef12e1dfd714a9a63deb..16af45cf832ae5308d4fcd99fcba990d53115373 100644 --- a/test/test_web.rb +++ b/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( diff --git a/web.rb b/web.rb index 333957afa4846976bab02a916a65491756b6e152..f384a7d772fca1aa2abb3cf7eb8b641e90743c32 100644 --- a/web.rb +++ b/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) From e89c3dfb313d2f4d7cf45c1294a3a0b93ddfc9eb Mon Sep 17 00:00:00 2001 From: Amolith Date: Mon, 9 Mar 2026 12:30:46 -0600 Subject: [PATCH 2/2] Switch BW clients from Basic to Client creds Basic credentials are only usable for id.bandwidth.com, both gems prefer api.bandwidth.com, and only iris seems to fall back to id. This adds the new client cred fields to the config and uses them throughout. --- config-schema.dhall | 8 +++++++- config.dhall.sample | 2 ++ lib/bandwidth_tn_repo.rb | 4 ++-- sgx_jmp.rb | 8 ++++---- test/test_helper.rb | 2 ++ 5 files changed, 17 insertions(+), 7 deletions(-) diff --git a/config-schema.dhall b/config-schema.dhall index 27bcb68a08211cf8f3b303b2754739490199dfef..a5c5e65c41f519aab2c7f8f54b9f3bd2e266fa8a 100644 --- a/config-schema.dhall +++ b/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 : diff --git a/config.dhall.sample b/config.dhall.sample index 01047c72a341ba57504dcdd8a0e5e9e6ff69050f..010940054b8020dd7f9ecddda2eec0cb49780b09 100644 --- a/config.dhall.sample +++ b/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" }, diff --git a/lib/bandwidth_tn_repo.rb b/lib/bandwidth_tn_repo.rb index b8f8d6bc1b01727babc3ed6ea911b5afdd565570..4e936f5fd550588c150839f6824238b25382f8be 100644 --- a/lib/bandwidth_tn_repo.rb +++ b/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 diff --git a/sgx_jmp.rb b/sgx_jmp.rb index 32072121a736c0623ab7a9f8f7e6db1fb9ed240d..ab52fdc2150e877cc1b6d64b7986cc4a5d566238 100644 --- a/sgx_jmp.rb +++ b/sgx_jmp.rb @@ -121,12 +121,12 @@ 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.configure do |config| - config.client_id = CONFIG[:creds][:username] - config.client_secret = CONFIG[:creds][:password] + config.client_id = CONFIG[:creds][:client_id] + config.client_secret = CONFIG[:creds][:client_secret] end BANDWIDTH_VOICE = Bandwidth::CallsApi.new diff --git a/test/test_helper.rb b/test/test_helper.rb index 6cb4956b78d259bf5ba3598604d50d5cdc0c28e6..a4102aed39f778cff0a0febf7400e1643d4bb19d 100644 --- a/test/test_helper.rb +++ b/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" },