number_change.rb

  1# frozen_string_literal: true
  2
  3require "value_semantics/monkey_patched"
  4require_relative "../admin_action"
  5require_relative "../form_to_h"
  6require_relative "../utils"
  7
  8class AdminAction
  9	class NumberChange < AdminAction
 10		include Isomorphic
 11		class Command
 12			using FormToH
 13
 14			def self.for(target_customer, reply:)
 15				unless (registration = target_customer.registered?)
 16					return EMPromise.reject("Customer not registered")
 17				end
 18
 19				EMPromise.resolve(
 20					new(
 21						customer_id: target_customer.customer_id,
 22						old_backend: target_customer.sgx.strip!.to_s,
 23						old_tel: registration.phone
 24					)
 25				).then { |x| reply.call(x.form).then(&x.method(:change)) }
 26			end
 27
 28			def initialize(**bag)
 29				@bag = bag
 30			end
 31
 32			def form
 33				FormTemplate.render("admin_number_change")
 34			end
 35
 36			def change(result)
 37				AdminAction::NumberChange.for(
 38					**@bag,
 39					**result.form.to_h
 40						.reject { |_k, v| v == "nil" || v.to_s.empty? }
 41						.tap { |form_h|
 42							form_h["new_backend"] ||= @bag[:old_backend]
 43							form_h["new_tel"] ||= @bag[:old_tel]
 44						}
 45						.transform_keys(&:to_sym)
 46				)
 47			end
 48		end
 49
 50		NilKey = Struct.new(:key) {
 51			def to_s
 52				"Expected a key with a value, but #{key} has no value."
 53			end
 54		}
 55
 56		UnknownBackend = Struct.new(:backend, :expected) {
 57			def to_s
 58				"Got unknown backend: #{backend}, expected one of #{expected}"
 59			end
 60		}
 61
 62		def customer_id
 63			@attributes[:customer_id]
 64		end
 65
 66		def old_tel
 67			@attributes[:old_tel]
 68		end
 69
 70		def new_tel
 71			@attributes[:new_tel]
 72		end
 73
 74		def old_backend
 75			@attributes[:old_backend]
 76		end
 77
 78		def new_backend
 79			@attributes[:new_backend]
 80		end
 81
 82		def should_delete?
 83			["1", "true"].include?(@attributes[:should_delete])
 84		end
 85
 86		def check_forward
 87			EMPromise.all([check_noop, check_cat_jid, check_backend])
 88		end
 89
 90		def forward
 91			repo = TrivialBackendSgxRepo.new(redis: REDIS)
 92			EMPromise.all([
 93				new_tel != old_tel && change_catapult_fwd!,
 94				repo.put(customer_id, new_backend).then { |sgx|
 95					sgx.register!(new_tel)
 96				},
 97				should_disconnect_old_number? && disconnect_bandwidth_number
 98			]).then { self }
 99		end
100
101		def change_catapult_fwd!
102			REDIS.rename("catapult_fwd-#{old_tel}", "catapult_fwd-#{new_tel}")
103		end
104
105		def to_reverse
106			with(
107				old_tel: new_tel,
108				new_tel: old_tel,
109				old_backend: new_backend,
110				new_backend: old_backend
111			)
112		end
113
114		def to_s
115			base = "Change Number\n"
116			base += "	[move backend?]: #{old_backend} -> #{new_backend}\n"
117			base += "	[change number?]: #{old_tel} -> #{new_tel}"
118			base + delete_warning
119		end
120
121	protected
122
123		def disconnect_bandwidth_number
124			# Order name is limited to 40 characters
125			# Assuming 12 chars for new_tel and 12 for customer_id, this is tight
126			# but ok
127			BandwidthTnRepo.new.disconnect(
128				old_tel,
129				"cust #{customer_id} swap to #{new_tel}"
130			)
131		end
132
133		def delete_warning
134			return "" unless should_delete? && !first_time?
135
136			" * NOT DELETING"
137		end
138
139		def check_noop
140			if new_tel == old_tel && new_backend == old_backend
141				EMPromise.reject(NoOp.new)
142			else
143				EMPromise.resolve(nil)
144			end
145		end
146
147		def check_cat_jid
148			cat_jid = "catapult_jid-#{old_tel}"
149			REDIS.exists(cat_jid).then { |v|
150				EMPromise.reject(NilKey.new(cat_jid)) unless v == 1
151			}
152		end
153
154		def check_backend
155			unless (expected = CONFIG[:sgx_creds].keys.map(
156				&:to_s
157			).push(CONFIG[:sgx])).include?(new_backend)
158				EMPromise.reject(UnknownBackend.new(new_backend, expected))
159			end
160		end
161
162		def should_disconnect_old_number?
163			should_delete? &&
164				first_time? &&
165				old_tel != new_tel &&
166				old_backend == CONFIG[:sgx]
167		end
168	end
169end