reachability_repo.rb

 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