~singpolyma/sgx-jmp

d8cb729a32b91afb11284d141a5beaf76a279de9 — Stephen Paul Weber 9 months ago 93b7dea
CustomerFwd uses ValueSemantics, translates old XMPP-SIP URI

More of the original data is kept now, so this object could be used for putting
to persistence as well as for loading from it.
4 files changed, 96 insertions(+), 41 deletions(-)

M lib/bwmsgsv2_repo.rb
M lib/customer_fwd.rb
A test/test_customer_fwd.rb
M web.rb
M lib/bwmsgsv2_repo.rb => lib/bwmsgsv2_repo.rb +1 -1
@@ 20,7 20,7 @@ class Bwmsgsv2Repo
		fetch_raw(sgx.from_jid).then do |(((ogm_url, fwd_time, fwd), trans_d), reg)|
			sgx.with({
				ogm_url: ogm_url,
				fwd: CustomerFwd.for(fwd, fwd_time),
				fwd: CustomerFwd.for(uri: fwd, timeout: fwd_time),
				transcription_enabled: !trans_d,
				registered?: reg
			}.compact)

M lib/customer_fwd.rb => lib/customer_fwd.rb +35 -30
@@ 1,17 1,25 @@
# frozen_string_literal: true

require "value_semantics/monkey_patched"
require "uri"

class CustomerFwd
	def self.for(uri, timeout)
	def self.for(uri:, timeout:)
		timeout = Timeout.new(timeout)
		return if !uri || timeout.zero?
		return None.new(uri: uri, timeout: timeout) if !uri || timeout.zero?
		if uri =~ /\Asip:(.*)@sip.cheogram.com\Z/
			uri = "xmpp:#{$1.gsub(/%([0-9A-F]{2})/i) { $1.to_i(16).chr }}"
		end
		URIS.fetch(uri.split(":", 2).first.to_sym) {
			raise "Unknown forward URI: #{uri}"
		}.new(uri, timeout)
		}.new(uri: uri, timeout: timeout)
	end

	class Timeout
		def self.new(s)
			s.is_a?(self) ? s : super
		end

		def initialize(s)
			@timeout = s.nil? || s.to_i.negative? ? 300 : s.to_i
		end


@@ 25,54 33,51 @@ class CustomerFwd
		end
	end

	def create_call_request
	value_semantics do
		uri Either(String, NilClass)
		# rubocop:disable Style/RedundantSelf
		self.timeout Timeout, coerce: Timeout.method(:new)
		# rubocop:enable Style/RedundantSelf
	end

	def with(new_attrs)
		CustomerFwd.for(to_h.merge(new_attrs))
	end

	def create_call(account)
		request = Bandwidth::ApiCreateCallRequest.new.tap do |cc|
			cc.to = to
			cc.call_timeout = timeout.to_i
			yield cc if block_given?
		end
		yield request if block_given?
		request
		BANDWIDTH_VOICE.create_call(account, body: request).data.call_id
	end

	class Tel < CustomerFwd
		attr_reader :timeout

		def initialize(uri, timeout)
			@tel = uri.sub(/^tel:/, "")
			@timeout = timeout
		end

		def to
			@tel
			uri.sub(/^tel:/, "")
		end
	end

	class SIP < CustomerFwd
		attr_reader :timeout

		def initialize(uri, timeout)
			@uri = uri
			@timeout = timeout
		end

		def to
			@uri
			uri
		end
	end

	class XMPP < CustomerFwd
		attr_reader :timeout

		def initialize(uri, timeout)
			@jid = uri.sub(/^xmpp:/, "")
			@timeout = timeout
		end

		def to
			"sip:#{ERB::Util.url_encode(@jid)}@sip.cheogram.com"
			jid = uri.sub(/^xmpp:/, "")
			"sip:#{ERB::Util.url_encode(jid)}@sip.cheogram.com"
		end
	end

	class None < CustomerFwd
		def create_call; end

		def to; end
	end

	URIS = {
		tel: Tel,
		sip: SIP,

A test/test_customer_fwd.rb => test/test_customer_fwd.rb +51 -0
@@ 0,0 1,51 @@
# frozen_string_literal: true

require "test_helper"
require "customer_fwd"

class Rantly
	def jid
		v = Blather::JID.new(Blather::JID.new(string, string).stripped.to_s)
		guard !v.to_s.to_s.empty?
		v
	end
end

class CustomerFwdTest < Minitest::Test
	property(:for_xmpp) { jid }
	def for_xmpp(jid)
		sip = "sip:#{ERB::Util.url_encode(jid.to_s)}@sip.cheogram.com"
		fwd = CustomerFwd.for(uri: "xmpp:#{jid}", timeout: 10)
		assert_kind_of CustomerFwd::XMPP, fwd
		assert_equal sip, fwd.to
	end

	property(:for_xmpp_sip) { jid }
	def for_xmpp_sip(jid)
		sip = "sip:#{ERB::Util.url_encode(jid.to_s)}@sip.cheogram.com"
		fwd = CustomerFwd.for(uri: sip, timeout: 10)
		assert_kind_of CustomerFwd::XMPP, fwd
		assert_equal sip, fwd.to
	end

	property(:for_tel) { "+#{string(:digit)}" }
	def for_tel(tel)
		fwd = CustomerFwd.for(uri: "tel:#{tel}", timeout: 10)
		assert_kind_of CustomerFwd::Tel, fwd
		assert_equal tel, fwd.to
	end

	property(:for_sip) { "#{string(:alnum)}@#{string(:alnum)}.example.com" }
	def for_sip(sip)
		fwd = CustomerFwd.for(uri: "sip:#{sip}", timeout: 10)
		assert_kind_of CustomerFwd::SIP, fwd
		assert_equal "sip:#{sip}", fwd.to
	end

	property(:for_bogus) { string }
	def for_bogus(bogus)
		assert_raises(RuntimeError) do
			CustomerFwd.for(uri: "bogus:#{bogus}", timeout: 10)
		end
	end
end

M web.rb => web.rb +9 -10
@@ 257,17 257,16 @@ class Web < Roda
					CustomerRepo.new(
						sgx_repo: Bwmsgsv2Repo.new
					).find_by_tel(params["to"]).then(&:fwd).then do |fwd|
						if fwd
						call = fwd.create_call(CONFIG[:creds][:account]) do |cc|
							true_inbound_call[pseudo_call_id] = params["callId"]
							outbound_transfers[pseudo_call_id] = BANDWIDTH_VOICE.create_call(
								CONFIG[:creds][:account],
								body: fwd.create_call_request do |cc|
									cc.from = params["from"]
									cc.application_id = params["applicationId"]
									cc.answer_url = url inbound_calls_path(nil)
									cc.disconnect_url = url inbound_calls_path(:transfer_complete)
								end
							).data.call_id
							cc.from = params["from"]
							cc.application_id = params["applicationId"]
							cc.answer_url = url inbound_calls_path(nil)
							cc.disconnect_url = url inbound_calls_path(:transfer_complete)
						end

						if call
							outbound_transfers[pseudo_call_id] = call
							render :pause, locals: { duration: 300 }
						else
							render :redirect, locals: { to: inbound_calls_path(:voicemail) }