1# frozen_string_literal: true
2
3require "lazy_object"
4require "value_semantics/monkey_patched"
5
6require_relative "bandwidth_tn_repo"
7require_relative "customer"
8require_relative "polyfill"
9
10class CustomerRepo
11 class NotFound < RuntimeError; end
12
13 value_semantics do
14 redis Anything(), default: LazyObject.new { REDIS }
15 db Anything(), default: LazyObject.new { DB }
16 braintree Anything(), default: LazyObject.new { BRAINTREE }
17 sgx_repo Anything(), default: TrivialBackendSgxRepo.new
18 bandwidth_tn_repo Anything(), default: BandwidthTnRepo.new
19 end
20
21 def find(customer_id)
22 @redis.get("jmp_customer_jid-#{customer_id}").then do |jid|
23 raise NotFound, "No jid" unless jid
24
25 find_inner(customer_id, jid)
26 end
27 end
28
29 def find_by_jid(jid)
30 if jid.to_s =~ /\Acustomer_(.+)@#{CONFIG[:component][:jid]}\Z/
31 find($1)
32 else
33 @redis.get("jmp_customer_id-#{jid}").then do |customer_id|
34 raise NotFound, "No customer" unless customer_id
35
36 find_inner(customer_id, jid)
37 end
38 end
39 end
40
41 def find_by_tel(tel)
42 @redis.get("catapult_jid-#{tel}").then do |jid|
43 raise NotFound, "No jid" unless jid
44
45 find_by_jid(jid)
46 end
47 end
48
49 def create(jid)
50 @braintree.customer.create.then do |result|
51 raise "Braintree customer create failed" unless result.success?
52
53 cid = result.customer.id
54 @redis.msetnx(
55 "jmp_customer_id-#{jid}", cid, "jmp_customer_jid-#{cid}", jid
56 ).then do |redis_result|
57 raise "Saving new customer to redis failed" unless redis_result == 1
58
59 Customer.new(cid, Blather::JID.new(jid), sgx: new_sgx(cid))
60 end
61 end
62 end
63
64 def put_lidb_name(customer, lidb_name)
65 @bandwidth_tn_repo.put_lidb_name(customer.registered?.phone, lidb_name)
66 end
67
68 def put_transcription_enabled(customer, enabled)
69 @sgx_repo.put_transcription_enabled(customer.customer_id, enabled)
70 end
71
72 def put_fwd(customer, customer_fwd)
73 tel = customer.registered?.phone
74 @sgx_repo.put_fwd(customer.customer_id, tel, customer_fwd)
75 end
76
77 def put_monthly_overage_limit(customer, limit)
78 k = "jmp_customer_monthly_overage_limit-#{customer.customer_id}"
79 @redis.set(k, limit)
80 end
81
82protected
83
84 def new_sgx(customer_id)
85 TrivialBackendSgxRepo.new.get(customer_id).with(registered?: false)
86 end
87
88 def mget(*keys)
89 @redis.mget(*keys).then { |values| Hash[keys.zip(values.map(&:to_i))] }
90 end
91
92 def fetch_redis(customer_id)
93 mget(
94 "jmp_customer_auto_top_up_amount-#{customer_id}",
95 "jmp_customer_monthly_overage_limit-#{customer_id}"
96 ).then { |r|
97 r.transform_keys { |k| k.match(/^jmp_customer_([^-]+)/)[1].to_sym }
98 }
99 end
100
101 SQL = <<~SQL
102 SELECT COALESCE(balance,0) AS balance, plan_name, expires_at
103 FROM customer_plans LEFT JOIN balances USING (customer_id)
104 WHERE customer_id=$1 LIMIT 1
105 SQL
106
107 def fetch_sql(customer_id)
108 @db.query_defer(SQL, [customer_id]).then do |rows|
109 rows.first&.transform_keys(&:to_sym) || {}
110 end
111 end
112
113 def fetch_all(customer_id)
114 EMPromise.all([
115 @sgx_repo.get(customer_id),
116 fetch_sql(customer_id),
117 fetch_redis(customer_id)
118 ]).then { |sgx, sql, redis| [sgx, sql.merge(redis)] }
119 end
120
121 def tndetails(sgx)
122 return unless sgx.registered?
123
124 LazyObject.new { @bandwidth_tn_repo.find(sgx.registered?.phone) }
125 end
126
127 def find_inner(customer_id, jid)
128 fetch_all(customer_id).then do |(sgx, data)|
129 Customer.new(
130 customer_id, Blather::JID.new(jid),
131 sgx: sgx, tndetails: tndetails(sgx),
132 plan: CustomerPlan.for(
133 customer_id,
134 **data.reject { |(k, _)| k == :balance }
135 ), **data.slice(:balance)
136 )
137 end
138 end
139end