From 345895de5e28ddf4e57ae13599924149f5d44d5c Mon Sep 17 00:00:00 2001 From: Stephen Paul Weber Date: Wed, 23 Jun 2021 12:46:43 -0500 Subject: [PATCH 1/2] Show commands conditionally (plus add voicemail record command) Refactor to a factory that allows commands to be shown conditionally, so that the voicemail record command can only be shown if the customer has a forwarding target set up. --- lib/command_list.rb | 43 +++++++++++++++++++++++++++ sgx_jmp.rb | 36 +++++++++++------------ test/test_command_list.rb | 62 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 123 insertions(+), 18 deletions(-) create mode 100644 lib/command_list.rb create mode 100644 test/test_command_list.rb diff --git a/lib/command_list.rb b/lib/command_list.rb new file mode 100644 index 0000000000000000000000000000000000000000..cb3c2d4254cf20b94c51a3e0a0b66bd13e08e073 --- /dev/null +++ b/lib/command_list.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +class CommandList + include Enumerable + + def self.for(jid) + Customer.for_jid(jid).then(&:registered?).catch { nil }.then { |reg| + next [] unless reg + REDIS.get("catapult_fwd-#{reg.phone}").then { |fwd| [reg, fwd] } + }.then do |(reg, fwd)| + next RegisteredAndHasForwarding.new if reg && fwd + next Registered.new if reg + CommandList.new + end + end + + def each + yield node: "jabber:iq:register", name: "Register" + end + + class Registered < CommandList + def each + super + yield node: "number-display", name: "Display JMP Number" + yield node: "configure-calls", name: "Configure Calls" + # TODO: don't show this item if no braintree methods available + # TODO: don't show this item if no plan for this customer + yield node: "buy-credit", name: "Buy account credit" + yield node: "usage", name: "Show Monthly Usage" + yield node: "reset sip account", name: "Create or Reset SIP Account" + end + end + + class RegisteredAndHasForwarding < Registered + def each + super + yield( + node: "record-voicemail-greeting", + name: "Record Voicemail Greeting" + ) + end + end +end diff --git a/sgx_jmp.rb b/sgx_jmp.rb index 5c70c2b4118955f627316903da92efdcffdc9a50..8d460f388e12ab16f28611871f7b47c39d432759 100644 --- a/sgx_jmp.rb +++ b/sgx_jmp.rb @@ -29,6 +29,7 @@ require_relative "lib/backend_sgx" require_relative "lib/bandwidth_tn_order" require_relative "lib/btc_sell_prices" require_relative "lib/buy_account_credit_form" +require_relative "lib/command_list" require_relative "lib/customer" require_relative "lib/electrum" require_relative "lib/em" @@ -241,24 +242,19 @@ disco_info to: Blather::JID.new(CONFIG[:component][:jid]) do |iq| end disco_items node: "http://jabber.org/protocol/commands" do |iq| + sentry_hub = new_sentry_hub(iq, name: iq.node) reply = iq.reply - reply.items = [ - { node: "number-display", name: "Display JMP Number" }, - { node: "configure-calls", name: "Configure Calls" }, - # TODO: don't show this item if no braintree methods available - # TODO: don't show this item if no plan for this customer - { node: "buy-credit", name: "Buy account credit" }, - { node: "jabber:iq:register", name: "Register" }, - { node: "usage", name: "Show Monthly Usage" }, - { node: "reset sip account", name: "Create or Reset SIP Account" } - ].map do |item| - Blather::Stanza::DiscoItems::Item.new( - iq.to, - item[:node], - item[:name] - ) - end - self << reply + + CommandList.for(iq.from.stripped).then { |list| + reply.items = list.map do |item| + Blather::Stanza::DiscoItems::Item.new( + iq.to, + item[:node], + item[:name] + ) + end + self << reply + }.catch { |e| panic(e, sentry_hub) } end command :execute?, node: "jabber:iq:register", sessionid: nil do |iq| @@ -296,7 +292,11 @@ def reply_with_note(iq, text, type: :info) end # Commands that just pass through to the SGX -command node: ["number-display", "configure-calls"] do |iq| +command node: [ + "number-display", + "configure-calls", + "record-voicemail-greeting" +] do |iq| sentry_hub = new_sentry_hub(iq, name: iq.node) Customer.for_jid(iq.from.stripped).then { |customer| sentry_hub.current_scope.set_user( diff --git a/test/test_command_list.rb b/test/test_command_list.rb new file mode 100644 index 0000000000000000000000000000000000000000..4775ed66706024e5d138dce0436d9c76db741c5b --- /dev/null +++ b/test/test_command_list.rb @@ -0,0 +1,62 @@ +# frozen_string_literal: true + +require "test_helper" +require "command_list" + +CommandList::Customer = Minitest::Mock.new +CommandList::REDIS = Minitest::Mock.new + +class CommandListTest < Minitest::Test + def test_for_no_customer + CommandList::Customer.expect( + :for_jid, + EMPromise.reject("not found"), + ["none"] + ) + assert_instance_of CommandList, CommandList.for("none").sync + end + em :test_for_no_customer + + def test_for_unregistered + CommandList::Customer.expect( + :for_jid, + EMPromise.resolve(OpenStruct.new(registered?: false)), + ["unregistered"] + ) + assert_instance_of CommandList, CommandList.for("unregistered").sync + end + em :test_for_unregistered + + def test_for_registered + CommandList::REDIS.expect( + :get, + EMPromise.resolve(nil), + ["catapult_fwd-1"] + ) + CommandList::Customer.expect( + :for_jid, + EMPromise.resolve(OpenStruct.new(registered?: OpenStruct.new(phone: "1"))), + ["registered"] + ) + assert_instance_of CommandList::Registered, CommandList.for("registered").sync + end + em :test_for_registered + + def test_for_registered_with_fwd + CommandList::REDIS.expect( + :get, + EMPromise.resolve("tel:1"), + ["catapult_fwd-1"] + ) + CommandList::Customer.expect( + :for_jid, + EMPromise.resolve(OpenStruct.new(registered?: OpenStruct.new(phone: "1"))), + ["registered"] + ) + assert_instance_of( + CommandList::RegisteredAndHasForwarding, + CommandList.for("registered").sync + ) + end + em :test_for_registered_with_fwd +end From 3e6ca1ef8e3e0d7708a1541d05ad3e9ac4702908 Mon Sep 17 00:00:00 2001 From: Stephen Paul Weber Date: Mon, 28 Jun 2021 11:34:40 -0500 Subject: [PATCH 2/2] Only show buy credit option if customer has a plan and a credit card --- lib/command_list.rb | 36 +++++++++++++++------- lib/payment_methods.rb | 8 +++++ test/test_command_list.rb | 63 +++++++++++++++++++++++++++++++++++---- 3 files changed, 91 insertions(+), 16 deletions(-) diff --git a/lib/command_list.rb b/lib/command_list.rb index cb3c2d4254cf20b94c51a3e0a0b66bd13e08e073..2621b86c6046edba43ef43abc1f3b02e30d77423 100644 --- a/lib/command_list.rb +++ b/lib/command_list.rb @@ -4,13 +4,11 @@ class CommandList include Enumerable def self.for(jid) - Customer.for_jid(jid).then(&:registered?).catch { nil }.then { |reg| - next [] unless reg - REDIS.get("catapult_fwd-#{reg.phone}").then { |fwd| [reg, fwd] } - }.then do |(reg, fwd)| - next RegisteredAndHasForwarding.new if reg && fwd - next Registered.new if reg - CommandList.new + Customer.for_jid(jid).catch { nil }.then do |customer| + EMPromise.resolve(customer&.registered?).catch { nil }.then do |reg| + next Registered.for(customer, reg.phone) if reg + CommandList.new + end end end @@ -19,19 +17,28 @@ class CommandList end class Registered < CommandList + def self.for(customer, tel) + EMPromise.all([ + REDIS.get("catapult_fwd-#{tel}"), + customer.plan_name ? customer.payment_methods : [] + ]).then do |(fwd, payment_methods)| + klass = Class.new(Registered) + klass.include(HasBilling) unless payment_methods.empty? + klass.include(HasForwarding) if fwd + klass.new + end + end + def each super yield node: "number-display", name: "Display JMP Number" yield node: "configure-calls", name: "Configure Calls" - # TODO: don't show this item if no braintree methods available - # TODO: don't show this item if no plan for this customer - yield node: "buy-credit", name: "Buy account credit" yield node: "usage", name: "Show Monthly Usage" yield node: "reset sip account", name: "Create or Reset SIP Account" end end - class RegisteredAndHasForwarding < Registered + module HasForwarding def each super yield( @@ -40,4 +47,11 @@ class CommandList ) end end + + module HasBilling + def each + super + yield node: "buy-credit", name: "Buy account credit" + end + end end diff --git a/lib/payment_methods.rb b/lib/payment_methods.rb index 1375d5d39a6036c6ab0d199cdc911df95ce1fa5a..33d4f33c54f1447e10abf4652de017c7c72d43c6 100644 --- a/lib/payment_methods.rb +++ b/lib/payment_methods.rb @@ -46,11 +46,19 @@ class PaymentMethods }.merge(kwargs) end + def empty? + false + end + class Empty def default_payment_method; end def to_list_single(*) raise "No payment methods available" end + + def empty? + true + end end end diff --git a/test/test_command_list.rb b/test/test_command_list.rb index 4775ed66706024e5d138dce0436d9c76db741c5b..7741a3f08d9bd99e598833ce8ffc57ae41c62281 100644 --- a/test/test_command_list.rb +++ b/test/test_command_list.rb @@ -35,10 +35,17 @@ class CommandListTest < Minitest::Test ) CommandList::Customer.expect( :for_jid, - EMPromise.resolve(OpenStruct.new(registered?: OpenStruct.new(phone: "1"))), + EMPromise.resolve(OpenStruct.new( + registered?: OpenStruct.new(phone: "1"), + payment_methods: EMPromise.resolve([]) + )), ["registered"] ) - assert_instance_of CommandList::Registered, CommandList.for("registered").sync + assert_equal( + ["CommandList::Registered"], + CommandList.for("registered").sync + .class.ancestors.map(&:name).grep(/\ACommandList::/) + ) end em :test_for_registered @@ -50,13 +57,59 @@ class CommandListTest < Minitest::Test ) CommandList::Customer.expect( :for_jid, - EMPromise.resolve(OpenStruct.new(registered?: OpenStruct.new(phone: "1"))), + EMPromise.resolve(OpenStruct.new( + registered?: OpenStruct.new(phone: "1"), + payment_methods: EMPromise.resolve([]) + )), ["registered"] ) - assert_instance_of( - CommandList::RegisteredAndHasForwarding, + assert_kind_of( + CommandList::HasForwarding, CommandList.for("registered").sync ) end em :test_for_registered_with_fwd + + def test_for_registered_with_billing + CommandList::REDIS.expect( + :get, + EMPromise.resolve(nil), + ["catapult_fwd-1"] + ) + CommandList::Customer.expect( + :for_jid, + EMPromise.resolve(OpenStruct.new( + registered?: OpenStruct.new(phone: "1"), + plan_name: "test", + payment_methods: EMPromise.resolve([:boop]) + )), + ["registered"] + ) + assert_kind_of( + CommandList::HasBilling, + CommandList.for("registered").sync + ) + end + em :test_for_registered_with_billing + + def test_for_registered_with_forwarding_and_billing + CommandList::REDIS.expect( + :get, + EMPromise.resolve("tel:1"), + ["catapult_fwd-1"] + ) + CommandList::Customer.expect( + :for_jid, + EMPromise.resolve(OpenStruct.new( + registered?: OpenStruct.new(phone: "1"), + plan_name: "test", + payment_methods: EMPromise.resolve([:boop]) + )), + ["registered"] + ) + result = CommandList.for("registered").sync + assert_kind_of CommandList::HasForwarding, result + assert_kind_of CommandList::HasBilling, result + end + em :test_for_registered_with_forwarding_and_billing end