From ec214d5beb16c492b50ed8e7e0e463690e3cd64d Mon Sep 17 00:00:00 2001 From: Tau Date: Tue, 28 May 2019 15:54:10 -0400 Subject: [PATCH] idz: Add team "reservation" repository This is a hack necessary to support certain quirks of the Initial D Zero profile creation process. --- src/idz/db/index.ts | 5 ++ src/idz/db/teamReservation.ts | 122 ++++++++++++++++++++++++++++++++++ src/idz/repo.ts | 15 +++++ 3 files changed, 142 insertions(+) create mode 100644 src/idz/db/teamReservation.ts diff --git a/src/idz/db/index.ts b/src/idz/db/index.ts index b42e5ef..ef21cc2 100644 --- a/src/idz/db/index.ts +++ b/src/idz/db/index.ts @@ -11,6 +11,7 @@ import { SqlStoryRepository } from "./story"; import { SqlTeamRepository } from "./team"; import { SqlTeamAutoRepository } from "./teamAuto"; import { SqlTeamMemberRepository } from "./teamMember"; +import { SqlTeamReservationRepository } from "./teamReservation"; import { SqlTicketsRepository } from "./tickets"; import { SqlTimeAttackRepository } from "./timeAttack"; import { SqlTitlesRepository } from "./titles"; @@ -66,6 +67,10 @@ class TransactionImpl implements Repo.Transaction { return new SqlTeamMemberRepository(this._conn); } + teamReservations(): Repo.TeamReservationRepository { + return new SqlTeamReservationRepository(this._conn); + } + tickets(): Repo.FacetRepository { return new SqlTicketsRepository(this._conn); } diff --git a/src/idz/db/teamReservation.ts b/src/idz/db/teamReservation.ts new file mode 100644 index 0000000..647b299 --- /dev/null +++ b/src/idz/db/teamReservation.ts @@ -0,0 +1,122 @@ +import * as sql from "sql-bricks-postgres"; +import { ClientBase } from "pg"; + +import { Team } from "../model/team"; +import { TeamReservationRepository } from "../repo"; +import { Id } from "../../db"; +import { AimeId } from "../../model"; + +export class SqlTeamReservationRepository + implements TeamReservationRepository { + constructor(private readonly _conn: ClientBase) {} + + private async _lockTeam(teamId: Id): Promise { + const lockSql = sql + .select("t.id") + .from("idz.team t") + .where("t.id", teamId) + .forUpdate() + .toParams(); + + await this._conn.query(lockSql); + } + + async occupancyHack(teamId: Id): Promise { + await this._lockTeam(teamId); + + // counts get returned as strings, so 1 + 0 = 10. + // it hardly needs to be said but fuck javascript. + + const memberSql = sql + .select("count(*) as count") + .from("idz.team_member tm") + .where("tm.team_id", teamId) + .toParams(); + + const memberRes = await this._conn.query(memberSql); + const memberCount = parseInt(memberRes.rows[0].count, 10); + + const reservSql = sql + .select("count(*) as count") + .from("idz.team_reservation tr") + .where("tr.team_id", teamId) + .toParams(); + + const reservRes = await this._conn.query(reservSql); + const reservCount = parseInt(reservRes.rows[0].count, 10); + + return memberCount + reservCount; + } + + async reserveHack( + teamId: Id, + aimeId: AimeId, + timestamp: Date, + leader?: "leader" + ): Promise { + const lookupSql = sql + .select("r.id") + .from("aime.player r") + .where("r.ext_id", aimeId) + .toParams(); + + const { rows } = await this._conn.query(lookupSql); + const row = rows[0]; + + if (row === undefined) { + throw new Error(`Unknown Aime ID ${aimeId}`); + } + + const playerId = row.id; + + const insertSql = sql + .insert("idz.team_reservation", { + id: playerId, + team_id: teamId, + join_time: timestamp, + leader: leader === "leader", + }) + .onConflict("id") + .doUpdate(["team_id"]) + .toParams(); + + await this._conn.query(insertSql); + } + + async commitHack(aimeId: AimeId): Promise { + const lookupSql = sql + .select("p.id as profile_id", "tr.*") + .from("idz.profile p") + .join("aime.player r", { "p.player_id": "r.id" }) + .join("idz.team_reservation tr", { "r.id": "tr.id" }) + .where("r.ext_id", aimeId) + .toParams(); + + const { rows } = await this._conn.query(lookupSql); + const row = rows[0]; + + if (row === undefined) { + throw new Error(`Reservation not found for Aime ID ${aimeId}`); + } + + console.log(row); + + const insertSql = sql + .insert("idz.team_member", { + id: row.profile_id, + team_id: row.team_id, + join_time: row.join_time, + leader: row.leader, + }) + .toParams(); + + await this._conn.query(insertSql); + + const cleanupSql = sql + .delete("idz.team_reservation") + .where("id", row.id) + .toParams(); + + await this._conn.query(cleanupSql); + } +} diff --git a/src/idz/repo.ts b/src/idz/repo.ts index 6cb8111..f578371 100644 --- a/src/idz/repo.ts +++ b/src/idz/repo.ts @@ -96,6 +96,19 @@ export interface TeamMemberRepository { ): Promise; } +export interface TeamReservationRepository { + occupancyHack(teamId: Id): Promise; + + reserveHack( + teamId: Id, + aimeId: AimeId, + timestamp: Date, + leader?: "leader" + ): Promise; + + commitHack(aimeId: AimeId): Promise; +} + // TODO extend and factorize export interface TopTenResult { driverName: string; @@ -141,6 +154,8 @@ export interface Repositories { teamMembers(): TeamMemberRepository; + teamReservations(): TeamReservationRepository; + tickets(): FacetRepository; timeAttack(): TimeAttackRepository;