1# frozen_string_literal: true
2
3require "value_semantics/monkey_patched"
4
5class ReachabilityRepo
6 value_semantics do
7 redis Anything(), default: LazyObject.new { REDIS }
8 senders ArrayOf(String), default: CONFIG[:reachability_senders]
9 end
10
11 EXPIRY = 60 * 60 # 1 hr
12
13 class SMS < self
14 def type
15 "sms"
16 end
17 end
18
19 class Voice < self
20 def type
21 "voice"
22 end
23 end
24
25 class NotTest
26 def filter(**)
27 EMPromise.resolve(yield)
28 end
29 end
30
31 class Test
32 def initialize(redis, key)
33 @redis = redis
34 @key = key
35 end
36
37 def filter(if_yes: ->(_) {}, **)
38 @redis.incr(@key).then(&if_yes)
39 end
40 end
41
42 # The customer is who is being contacted
43 # The initiator is the phone number trying to reach them
44 def find(customer, initiator)
45 return EMPromise.resolve(NotTest.new) unless potential?(initiator)
46
47 testing?(customer).then do |active|
48 active ? Test.new(redis, key(customer)) : NotTest.new
49 end
50 end
51
52 # This creates the keys if they don't exist, and returns the value
53 def get_or_create(customer)
54 redis.set(key(customer), 0, "NX", "EX", EXPIRY).then {
55 redis.get(key(customer))
56 }
57 end
58
59protected
60
61 # This is basically an optimization to bail early without hitting a
62 # datastore for 99% of all messages
63 def potential?(tel)
64 senders.include?(tel)
65 end
66
67 # This checks if this particular customer has reachability turned on
68 def testing?(customer)
69 redis.exists(key(customer)).then { |v| v == 1 }
70 end
71
72 # This gets the particular key for this check
73 def key(customer)
74 "jmp_customer_reachability_#{type}-#{customer.customer_id}"
75 end
76end