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|