invites_repo.rb

 1# frozen_string_literal: true
 2
 3class InvitesRepo
 4	class Invalid < StandardError; end
 5
 6	def initialize(db=DB)
 7		@db = db
 8	end
 9
10	def unused_invites(customer_id)
11		promise = @db.query_defer(<<~SQL, [customer_id])
12			SELECT code FROM unused_invites WHERE creator_id=$1
13		SQL
14		promise.then { |result| result.map { |row| row["code"] } }
15	end
16
17	def claim_code(customer_id, code, &blk)
18		EMPromise.resolve(nil).then do
19			@db.transaction do
20				valid = @db.exec(<<~SQL, [customer_id, code]).cmd_tuples.positive?
21					UPDATE invites SET used_by_id=$1, used_at=LOCALTIMESTAMP
22					WHERE code=$2 AND used_by_id IS NULL
23				SQL
24				raise Invalid, "Not a valid invite code: #{code}" unless valid
25
26				blk.call
27			end
28		end
29	end
30
31	CREATE_N_SQL = <<~SQL
32		INSERT INTO invites
33			SELECT unnest(array_fill($1::text, array[$2::int]))
34		RETURNING code
35	SQL
36
37	def create_n_codes(customer_id, num)
38		EMPromise.resolve(nil).then {
39			codes = @db.exec(CREATE_N_SQL, [customer_id, num])
40			raise Invalid, "Failed to fetch codes" unless codes.cmd_tuples.positive?
41
42			codes.map { |row| row["code"] }
43		}
44	end
45
46	def any_existing?(codes)
47		promise = @db.query_one(<<~SQL, [codes])
48			SELECT count(1) FROM invites WHERE code = ANY($1)
49		SQL
50		promise.then { |result| result[:count].positive? }
51	end
52
53	def any_claimed?(codes)
54		promise = @db.query_one(<<~SQL, [codes])
55			SELECT count(1) FROM invites WHERE code = ANY($1) AND used_by_id IS NOT NULL
56		SQL
57		promise.then { |result| result[:count].positive? }
58	end
59
60	def create_codes(customer_id, codes)
61		custs = [customer_id] * codes.length
62		EMPromise.resolve(nil).then {
63			@db.transaction do
64				valid = @db.exec(<<~SQL, [custs, codes]).cmd_tuples.positive?
65					INSERT INTO invites(creator_id, code) SELECT unnest($1), unnest($2)
66				SQL
67				raise Invalid, "Failed to insert one of: #{codes}" unless valid
68			end
69		}
70	end
71
72	def delete_codes(codes)
73		EMPromise.resolve(nil).then {
74			@db.exec(<<~SQL, [codes])
75				DELETE FROM invites WHERE code = ANY($1)
76			SQL
77		}
78	end
79end