bandwidth_tn_repo.rb

  1# frozen_string_literal: true
  2
  3require "lazy_object"
  4require "ruby-bandwidth-iris"
  5require_relative "geo_code_repo"
  6require_relative "local_calling_guide_repo"
  7
  8class BandwidthTnRepo
  9	class RateCenter
 10		# @return [String, nil]
 11		attr_reader :region
 12
 13		# @return [String, nil]
 14		attr_reader :locality
 15
 16		# @return [String, nil]
 17		attr_reader :lcg_region
 18
 19		# @return [String, nil]
 20		attr_reader :lcg_locality
 21
 22		# @return [Array(String, String)]
 23		def to_a
 24			[region, locality]
 25		end
 26
 27		# @param btn [BandwidthIris::Tn]
 28		# @param lcg_repo [LocalCallingGuideRepo]
 29		# @param geo_code_repo [GeoCodeRepo]
 30		# @return [EMPromise<RateCenter>]
 31		def self.for(
 32			btn,
 33			lcg_repo: LazyObject.new { LocalCallingGuideRepo.new },
 34			geo_code_repo: LazyObject.new { GeoCodeRepo.new }
 35		)
 36			[
 37				Source::Bandwidth.new(btn),
 38				Source::Geocode.new(btn, lcg_repo, geo_code_repo),
 39				Source::LCG.new(btn, lcg_repo)
 40			].reduce(EMPromise.resolve(new)) { |promise, source|
 41				promise.then { |acc|
 42					next acc if acc.settled?
 43
 44					source.fetch(acc).then(&acc.method(:fold))
 45				}
 46			}
 47		end
 48
 49		# @return [Boolean]
 50		def settled?
 51			return false unless readable(@region)
 52			return false unless readable(@locality)
 53
 54			true
 55		end
 56
 57	protected
 58
 59		# @param value [String, nil]
 60		# @return [String, nil]
 61		def readable(value)
 62			value if value&.match?(/\d/) == false
 63		end
 64
 65		# @param kwargs [Hash]
 66		# @return [self]
 67		def fold(kwargs)
 68			kwargs.each_pair do |k, v|
 69				instance_variable_set(
 70					:"@#{k}",
 71					readable(instance_variable_get(:"@#{k}")) || v
 72				)
 73			end
 74			self
 75		end
 76
 77		class Source
 78			# @param acc [RateCenter]
 79			# @return [EMPromise<Hash>]
 80			def fetch(acc)
 81				raise NotImplementedError
 82			end
 83
 84			class Bandwidth < self
 85				# @param btn [BandwidthIris::Tn]
 86				def initialize(btn)
 87					@btn = btn
 88				end
 89
 90				# @param _acc [RateCenter]
 91				# @return [EMPromise<Hash>]
 92				def fetch(_acc)
 93					city, state = @btn.get_details.values_at(
 94						:city, :state
 95					)
 96					EMPromise.resolve(
 97						{ locality: city, region: state }
 98					)
 99				rescue BandwidthIris::Errors::GenericError
100					EMPromise.resolve({})
101				end
102			end
103
104			class LCG < self
105				# @param btn [BandwidthIris::Tn]
106				# @param lcg_repo [LocalCallingGuideRepo]
107				def initialize(btn, lcg_repo)
108					@btn = btn
109					@lcg_repo = lcg_repo
110				end
111
112				# @param acc [RateCenter]
113				# @return [EMPromise<Hash>]
114				def fetch(acc)
115					EMPromise.resolve(
116						{
117							locality: acc.lcg_locality,
118							region: acc.lcg_region
119						}
120					)
121				end
122			end
123
124			class Geocode < self
125				# @param btn [BandwidthIris::Tn]
126				# @param lcg_repo [LocalCallingGuideRepo]
127				# @param geo_code_repo [GeoCodeRepo]
128				def initialize(btn, lcg_repo, geo_code_repo)
129					@btn = btn
130					@geo_code_repo = geo_code_repo
131					@lcg_repo = lcg_repo
132				end
133
134				# @param _acc [RateCenter]
135				# @return [EMPromise<Hash>]
136				def fetch(_acc)
137					npa, nxx, * = @btn.telephone_number.scan(/.{1,3}/).to_a
138					@lcg_repo.find(npa, nxx).then { |lcg|
139						cache = lcg.to_h.transform_keys(&"lcg_".method(:+))
140						@geo_code_repo.reverse(lcg.lat, lcg.lon).then { |gc|
141							gc.to_h.slice(:locality, :region).merge(cache)
142						}.catch { cache }
143					}
144				end
145			end
146		end
147	end
148
149	STASH_QUERY = <<~SQL
150		INSERT INTO tel_inventory (
151			tel,
152			region,
153			locality,
154			source,
155			available_after,
156			premium_price
157		) VALUES (
158			$1,
159			$2,
160			$3,
161			$4,
162			LOCALTIMESTAMP + '1 MONTH',
163			$5
164		)
165	SQL
166
167	# @param local_calling_guide_repo [LocalCallingGuideRepo]
168	# @param geo_code_repo [GeoCodeRepo]
169	def initialize(
170		local_calling_guide_repo: LazyObject.new { LocalCallingGuideRepo.new },
171		geo_code_repo: LazyObject.new { GeoCodeRepo.new }
172	)
173		@move_client =
174			BandwidthIris::Client.new(
175				account_id: CONFIG[:keep_area_codes_in][:account],
176				client_id: CONFIG[:creds][:client_id],
177				client_secret: CONFIG[:creds][:client_secret]
178			)
179		@local_calling_guide_repo = local_calling_guide_repo
180		@geo_code_repo = geo_code_repo
181	end
182
183	def find(tel)
184		BandwidthIris::Tn.new(telephone_number: tel).get_details
185	rescue StandardError
186		{}
187	end
188
189	def put_lidb_name(tel, lidb_name)
190		BandwidthIris::Lidb.create(
191			lidb_tn_groups: { lidb_tn_group: {
192				telephone_numbers: { telephone_number: tel.sub(/\A\+1/, "") },
193				subscriber_information: lidb_name,
194				use_type: "RESIDENTIAL", visibility: "PUBLIC"
195			} }
196		)
197	rescue BandwidthIris::Errors::GenericError
198		raise "Could not set CNAM, please contact support"
199	end
200
201	# @param btn [BandwidthIris::Tn]
202	# @param premium_price [Numeric]
203	# @return [EMPromise]
204	def stash_for_later(btn, premium_price)
205		tel = "+1#{btn.telephone_number}"
206		LOG.info "stash_for_later #{tel}\n#{caller}"
207		RateCenter.for(
208			btn,
209			lcg_repo: @local_calling_guide_repo,
210			geo_code_repo: @geo_code_repo
211		).then { |loc|
212			DB.exec(STASH_QUERY, [tel, *loc, CONFIG[:creds][:account], premium_price])
213		}
214	end
215
216	def disconnect(tel, order_name)
217		tn = tel.sub(/\A\+1/, "")
218		code_and_price = CONFIG[:keep_area_codes].find { |k|
219			tn.start_with?(k[:area_code])
220		}
221		unless code_and_price
222			return BandwidthIris::Disconnect.create(order_name, tn)
223		end
224
225		btn = BandwidthIris::Tn.new({ telephone_number: tn }, @move_client)
226		stash_for_later(btn, code_and_price[:premium_price] || 0)
227	end
228
229	def move(tel, order_name, source_account_id)
230		tn = tel.sub(/\A\+1/, "")
231		BandwidthIris::Tn.new({ telephone_number: tn }, @move_client).move(
232			customer_order_id: order_name,
233			source_account_id: source_account_id,
234			site_id: CONFIG[:bandwidth_site],
235			sip_peer_id: CONFIG[:bandwidth_peer]
236		)
237	end
238end