# frozen_string_literal: true require "lazy_object" require "value_semantics/monkey_patched" require_relative "bandwidth_tn_repo" require_relative "customer" require_relative "polyfill" class CustomerRepo class NotFound < RuntimeError; end value_semantics do redis Anything(), default: LazyObject.new { REDIS } db Anything(), default: LazyObject.new { DB } braintree Anything(), default: LazyObject.new { BRAINTREE } sgx_repo Anything(), default: TrivialBackendSgxRepo.new bandwidth_tn_repo Anything(), default: BandwidthTnRepo.new end def find(customer_id) @redis.get("jmp_customer_jid-#{customer_id}").then do |jid| raise NotFound, "No jid" unless jid find_inner(customer_id, jid) end end def find_by_jid(jid) if jid.to_s =~ /\Acustomer_(.+)@#{CONFIG[:component][:jid]}\Z/ find($1) else @redis.get("jmp_customer_id-#{jid}").then do |customer_id| raise NotFound, "No customer" unless customer_id find_inner(customer_id, jid) end end end def find_by_tel(tel) @redis.get("catapult_jid-#{tel}").then do |jid| raise NotFound, "No jid" unless jid find_by_jid(jid) end end def create(jid) @braintree.customer.create.then do |result| raise "Braintree customer create failed" unless result.success? cid = result.customer.id @redis.msetnx( "jmp_customer_id-#{jid}", cid, "jmp_customer_jid-#{cid}", jid ).then do |redis_result| raise "Saving new customer to redis failed" unless redis_result == 1 Customer.new(cid, Blather::JID.new(jid), sgx: new_sgx(cid)) end end end def put_lidb_name(customer, lidb_name) @bandwidth_tn_repo.put_lidb_name(customer.registered?.phone, lidb_name) end def put_transcription_enabled(customer, enabled) @sgx_repo.put_transcription_enabled(customer.customer_id, enabled) end def put_fwd(customer, customer_fwd) tel = customer.registered?.phone @sgx_repo.put_fwd(customer.customer_id, tel, customer_fwd) end def put_monthly_overage_limit(customer, limit) k = "jmp_customer_monthly_overage_limit-#{customer.customer_id}" @redis.put(k, limit) end protected def new_sgx(customer_id) TrivialBackendSgxRepo.new.get(customer_id).with(registered?: false) end def mget(*keys) @redis.mget(*keys).then { |values| Hash[keys.zip(values.map(&:to_i))] } end def fetch_redis(customer_id) mget( "jmp_customer_auto_top_up_amount-#{customer_id}", "jmp_customer_monthly_overage_limit-#{customer_id}" ).then { |r| r.transform_keys { |k| k.match(/^jmp_customer_([^-]+)/)[1].to_sym } } end SQL = <<~SQL SELECT COALESCE(balance,0) AS balance, plan_name, expires_at FROM customer_plans LEFT JOIN balances USING (customer_id) WHERE customer_id=$1 LIMIT 1 SQL def fetch_sql(customer_id) @db.query_defer(SQL, [customer_id]).then do |rows| rows.first&.transform_keys(&:to_sym) || {} end end def fetch_all(customer_id) EMPromise.all([ @sgx_repo.get(customer_id), fetch_sql(customer_id), fetch_redis(customer_id) ]).then { |sgx, sql, redis| [sgx, sql.merge(redis)] } end def tndetails(sgx) return unless sgx.registered? LazyObject.new { @bandwidth_tn_repo.find(sgx.registered?.phone) } end def find_inner(customer_id, jid) fetch_all(customer_id).then do |(sgx, data)| Customer.new( customer_id, Blather::JID.new(jid), sgx: sgx, tndetails: tndetails(sgx), plan: CustomerPlan.for( customer_id, **data.reject { |(k, _)| k == :balance } ), **data.slice(:balance) ) end end end