diff --git a/lib/reachability_repo.rb b/lib/reachability_repo.rb index f9ed9e563a3fb3182b7d2b9881ed428dc1875364..db074b7b364e390b910ff88151fb3bd86494a129 100644 --- a/lib/reachability_repo.rb +++ b/lib/reachability_repo.rb @@ -21,7 +21,7 @@ class ReachabilityRepo end class NotTest - def filter + def filter(**) EMPromise.resolve(yield) end end @@ -32,8 +32,8 @@ class ReachabilityRepo @key = key end - def filter - @redis.incr(@key).then { nil } + def filter(if_yes: ->(_) {}, **) + @redis.incr(@key).then(&if_yes) end end diff --git a/test/test_helper.rb b/test/test_helper.rb index f68171e4ccf09afab54898a287b94e0a172c6a98..94ca984f6e85b5ce8312100758234d5402a70470 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -274,6 +274,13 @@ class FakeRedis def lindex(key, index) get(key).then { |v| v&.fetch(index) } end + + def incr(key) + get(key).then { |v| + n = v ? v + 1 : 0 + set(key, n).then { n } + } + end end class FakeDB diff --git a/test/test_web.rb b/test/test_web.rb index a57d1704c88593627a97f003297e6549e23c376b..f7c7573c65ab2c04d65bf785b6a53e083a17e5cf 100644 --- a/test/test_web.rb +++ b/test/test_web.rb @@ -12,6 +12,8 @@ CustomerFwd::BANDWIDTH_VOICE = Minitest::Mock.new Web::BANDWIDTH_VOICE = Minitest::Mock.new LowBalance::AutoTopUp::CreditCardSale = Minitest::Mock.new +ReachableRedis = Minitest::Mock.new + class WebTest < Minitest::Test include Rack::Test::Methods @@ -28,6 +30,8 @@ class WebTest < Minitest::Test "catapult_jid-+15551234562" => "customer_customerid_topup@component", "jmp_customer_jid-customerid_limit" => "customer@example.com", "catapult_jid-+15551234561" => "customer_customerid_limit@component", + "jmp_customer_jid-customerid_reach" => "customerid_reach@example.com", + "catapult_jid-+15551234563" => "customer_customerid_reach@component", "jmp_customer_jid-customerid2" => "customer2@example.com", "catapult_jid-+15551230000" => "customer_customerid2@component" ), @@ -52,6 +56,11 @@ class WebTest < Minitest::Test "plan_name" => "test_usd", "expires_at" => Time.now + 100 }], + ["customerid_reach"] => [{ + "balance" => BigDecimal(10), + "plan_name" => "test_usd", + "expires_at" => Time.now + 100 + }], ["customerid_limit"] => [{ "balance" => BigDecimal(10), "plan_name" => "test_usd", @@ -66,6 +75,8 @@ class WebTest < Minitest::Test "catapult_fwd_timeout-customer_customerid_low@component" => "30", "catapult_fwd-+15551234561" => "xmpp:customer@example.com", "catapult_fwd_timeout-customer_customerid_limit@component" => "30", + "catapult_fwd-+15551234563" => "xmpp:customer@example.com", + "catapult_fwd_timeout-customer_customerid_reach@component" => "30", "catapult_fwd-+15551230000" => "xmpp:customer2@example.com", "catapult_fwd_timeout-customer_customerid2@component" => "30" ), @@ -83,6 +94,10 @@ class WebTest < Minitest::Test Blather::Stanza::Iq::IBR.new.tap do |ibr| ibr.phone = "+15551234567" end, + "customer_customerid_reach@component" => + Blather::Stanza::Iq::IBR.new.tap do |ibr| + ibr.phone = "+15551234563" + end, "customer_customerid_limit@component" => Blather::Stanza::Iq::IBR.new.tap do |ibr| ibr.phone = "+15551234567" @@ -96,6 +111,7 @@ class WebTest < Minitest::Test db: FakeDB.new( ["test_usd", "+15557654321", :outbound] => [{ "rate" => 0.01 }], ["test_usd", "+15557654321", :inbound] => [{ "rate" => 0.01 }], + ["test_usd", "+14445556666", :inbound] => [{ "rate" => 0.01 }], ["customerid_limit"] => FakeDB::MultiResult.new( [{ "a" => 1000 }], [{ "settled_amount" => 15 }] @@ -111,6 +127,10 @@ class WebTest < Minitest::Test ) ) Web.opts[:common_logger] = FakeLog.new + Web.opts[:reachability_repo] = ReachabilityRepo::Voice.new( + redis: ReachableRedis, + senders: ["+14445556666"] + ) Web.instance_variable_set(:@outbound_transfers, { "bcall" => "oocall" }) Web.app end @@ -351,6 +371,47 @@ class WebTest < Minitest::Test end em :test_inbound + def test_inbound_from_reachability + 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 + ) + + ReachableRedis.expect( + :exists, + EMPromise.resolve(0), + ["jmp_customer_reachability_voice-customerid"] + ) + + post( + "/inbound/calls", + { + from: "+14445556666", + to: "+15551234567", + callId: "acall" + }.to_json, + { "CONTENT_TYPE" => "application/json" } + ) + + assert last_response.ok? + assert_equal( + "" \ + "" \ + "", + last_response.body + ) + assert_mock CustomerFwd::BANDWIDTH_VOICE + assert_mock ReachableRedis + end + em :test_inbound_from_reachability + def test_inbound_no_bwmsgsv2 CustomerFwd::BANDWIDTH_VOICE.expect( :create_call, @@ -645,4 +706,38 @@ class WebTest < Minitest::Test ) end em :test_voicemail_no_customer + + def test_inbound_from_reachability_during_reachability + ReachableRedis.expect( + :exists, + EMPromise.resolve(1), + ["jmp_customer_reachability_voice-customerid_reach"] + ) + ReachableRedis.expect( + :incr, + EMPromise.resolve(1), + ["jmp_customer_reachability_voice-customerid_reach"] + ) + + post( + "/inbound/calls", + { + from: "+14445556666", + to: "+15551234563", + callId: "acall" + }.to_json, + { "CONTENT_TYPE" => "application/json" } + ) + + assert last_response.ok? + assert_equal( + "" \ + "" \ + "", + last_response.body + ) + assert_mock CustomerFwd::BANDWIDTH_VOICE + assert_mock ReachableRedis + end + em :test_inbound_from_reachability_during_reachability end diff --git a/web.rb b/web.rb index 52dff555cb9b9973c83a6bc64f8062efd641b237..a4cd33dd2784222b4b06ac40bbc8dda354e5f6a8 100644 --- a/web.rb +++ b/web.rb @@ -16,6 +16,7 @@ require_relative "lib/rev_ai" require_relative "lib/roda_capture" require_relative "lib/roda_em_promise" require_relative "lib/rack_fiber" +require_relative "lib/reachability_repo" class OGMDownload def initialize(url) @@ -107,6 +108,10 @@ class Web < Roda opts[:customer_repo] || CustomerRepo.new(**kwargs) end + def reachability_repo(**kwargs) + opts[:reachability_repo] || ReachabilityRepo::Voice.new(**kwargs) + end + def find_by_tel_with_fallback(sgx_repo:, **kwargs) customer_repo(sgx_repo: sgx_repo).find_by_tel(params["to"]).catch { |e| next EMPromise.reject(e) if e.is_a?(CustomerRepo::NotFound) @@ -193,6 +198,24 @@ class Web < Roda ) end + def call_inputs(customer, from, call_id) + EMPromise.all([ + customer.customer_id, customer.fwd, + call_attempt_repo.find_inbound(customer, from, call_id: call_id) + ]) + end + + def create_call(customer, from, call_id, application_id) + call_inputs(customer, from, call_id).then do |(customer_id, fwd, ca)| + ca.create_call(fwd, CONFIG[:creds][:account]) do |cc| + cc.from = from + cc.application_id = application_id + cc.answer_url = url inbound_calls_path(nil, customer_id) + cc.disconnect_url = url inbound_calls_path(:transfer_complete) + end + end + end + route do |r| r.on "inbound" do r.on "calls" do @@ -330,24 +353,21 @@ class Web < Roda customer_repo( sgx_repo: Bwmsgsv2Repo.new ).find_by_tel(params["to"]).then { |customer| - EMPromise.all([ - customer.customer_id, customer.fwd, - call_attempt_repo.find_inbound( - customer, params["from"], call_id: params["callId"] - ) - ]) - }.then { |(customer_id, fwd, ca)| - call = ca.create_call(fwd, CONFIG[:creds][:account]) { |cc| - cc.from = params["from"] - cc.application_id = params["applicationId"] - cc.answer_url = url inbound_calls_path(nil, customer_id) - cc.disconnect_url = url inbound_calls_path(:transfer_complete) - } - - next EMPromise.reject(:voicemail) unless call - - outbound_transfers[params["callId"]] = call - render :ring, locals: { duration: 300 } + reachability_repo.find(customer, params["from"]).then do |reach| + reach.filter(if_yes: ->(_) { render :hangup }) do + create_call( + customer, + params["from"], + params["callId"], + params["applicationId"] + ).then { |call| + next EMPromise.reject(:voicemail) unless call + + outbound_transfers[params["callId"]] = call + render :ring, locals: { duration: 300 } + } + end + end }.catch_only(CustomerFwd::InfiniteTimeout) { |e| render :forward, locals: { fwd: e.fwd, from: params["from"] } }.catch { |e|