From e115e583099ac3db8af932c86e101e597a213f4a Mon Sep 17 00:00:00 2001 From: JackAttack-365 <142643773+jackattack-4@users.noreply.github.com> Date: Fri, 3 Jul 2026 10:13:41 -0700 Subject: [PATCH 1/8] add endpoint to check if match exists --- src/handler/manager/checkMatchExists.ts | 32 ++++++++++++++++++++++++ src/routes/manager/manager.routes.ts | 33 ++++++++++++++++++++++++- 2 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 src/handler/manager/checkMatchExists.ts diff --git a/src/handler/manager/checkMatchExists.ts b/src/handler/manager/checkMatchExists.ts new file mode 100644 index 00000000..32a279b2 --- /dev/null +++ b/src/handler/manager/checkMatchExists.ts @@ -0,0 +1,32 @@ +import z from "zod"; +import { addTournamentMatches } from "./addTournamentMatches"; +import { Request, Response } from "express"; +import prismaClient from "../../prismaClient"; +import { MatchType } from "@prisma/client"; + +export const checkMatchExists = async ( + req: Request, + res: Response, +): Promise => { + try { + const params = z + .object({ + tournamentKey: z.string(), + teamNumber: z.coerce.number(), + matchNumber: z.coerce.number(), + matchType: z.enum(MatchType), + }) + .parse(req.query); + + await addTournamentMatches(params.tournamentKey); + + const match = await prismaClient.teamMatchData.findFirst({ + where: params, + }); + + res.status(200).send(match); + } catch (error) { + console.log(error); + res.status(500).send(error); + } +}; diff --git a/src/routes/manager/manager.routes.ts b/src/routes/manager/manager.routes.ts index 6291cca1..0c9e2400 100644 --- a/src/routes/manager/manager.routes.ts +++ b/src/routes/manager/manager.routes.ts @@ -29,8 +29,14 @@ import { getTeamTournamentStatus } from "../../handler/manager/getTeamTournament import { getMatchResults } from "../../handler/manager/getMatchResults.js"; import { registry } from "../../lib/openapi.js"; import { z } from "zod"; -import { TeamSchema, TournamentSchema } from "../../lib/prisma-zod.js"; +import { + TeamMatchDataSchema, + TeamSchema, + TournamentSchema, +} from "../../lib/prisma-zod.js"; import { requireVerifiedTeam } from "../../lib/middleware/requireVerifiedTeam.js"; +import { MatchType } from "@prisma/client"; +import { checkMatchExists } from "../../handler/manager/checkMatchExists.js"; const router = Router(); @@ -126,6 +132,29 @@ registry.registerPath({ security: [{ bearerAuth: [] }], }); +registry.registerPath({ + method: "get", + path: "/v1/manager/checkmatch", + tags: ["Manager - Matches"], + summary: "Check if a match exists and if a team is in the match", + request: { + query: z.object({ + tournamentKey: z.string(), + teamNumber: z.number(), + matchNumber: z.number(), + matchType: z.enum(MatchType), + }), + }, + responses: { + 200: { + description: "Match Data Row", + content: { "application/json": { schema: TeamMatchDataSchema } }, + }, + 400: { description: "Invalid parameters" }, + }, + security: [{ bearerAuth: [] }], +}); + // Profile const ProfileSchema = z.object({ id: z.string(), @@ -418,6 +447,8 @@ router.use("/apikey", apikey); router.get("/teams", requireAuth, getTeams); router.get("/tournaments", requireAuth, getTournaments); +router.get("/checkmatch", checkMatchExists); + router.get( "/matches/:tournament", requireAuth, From a49505697d07ec312514f386aa4c2d5224a05cca Mon Sep 17 00:00:00 2001 From: jackattack-4 <142643773+jackattack-4@users.noreply.github.com> Date: Fri, 3 Jul 2026 10:21:01 -0700 Subject: [PATCH 2/8] Fix doc types Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/routes/manager/manager.routes.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/routes/manager/manager.routes.ts b/src/routes/manager/manager.routes.ts index 0c9e2400..4dbec180 100644 --- a/src/routes/manager/manager.routes.ts +++ b/src/routes/manager/manager.routes.ts @@ -140,9 +140,9 @@ registry.registerPath({ request: { query: z.object({ tournamentKey: z.string(), - teamNumber: z.number(), - matchNumber: z.number(), - matchType: z.enum(MatchType), + teamNumber: z.coerce.number().int(), + matchNumber: z.coerce.number().int(), + matchType: z.nativeEnum(MatchType), }), }, responses: { From 2aecc00a52ee17fe9447c8081321646ac1d224b2 Mon Sep 17 00:00:00 2001 From: jackattack-4 <142643773+jackattack-4@users.noreply.github.com> Date: Fri, 3 Jul 2026 10:21:28 -0700 Subject: [PATCH 3/8] Add nullable to doc Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/routes/manager/manager.routes.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/routes/manager/manager.routes.ts b/src/routes/manager/manager.routes.ts index 4dbec180..d9d5c20f 100644 --- a/src/routes/manager/manager.routes.ts +++ b/src/routes/manager/manager.routes.ts @@ -147,11 +147,11 @@ registry.registerPath({ }, responses: { 200: { - description: "Match Data Row", - content: { "application/json": { schema: TeamMatchDataSchema } }, + description: "Match Data Row (or null if not found)", + content: { "application/json": { schema: TeamMatchDataSchema.nullable() } }, }, 400: { description: "Invalid parameters" }, - }, + 401: { description: "Unauthorized" }, security: [{ bearerAuth: [] }], }); From cecbad01f989879b3ac40bbc93289ab54530285a Mon Sep 17 00:00:00 2001 From: jackattack-4 <142643773+jackattack-4@users.noreply.github.com> Date: Fri, 3 Jul 2026 10:22:13 -0700 Subject: [PATCH 4/8] add catch for invalid params Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/handler/manager/checkMatchExists.ts | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/handler/manager/checkMatchExists.ts b/src/handler/manager/checkMatchExists.ts index 32a279b2..1f559f08 100644 --- a/src/handler/manager/checkMatchExists.ts +++ b/src/handler/manager/checkMatchExists.ts @@ -9,14 +9,21 @@ export const checkMatchExists = async ( res: Response, ): Promise => { try { - const params = z + const parsed = z .object({ tournamentKey: z.string(), - teamNumber: z.coerce.number(), - matchNumber: z.coerce.number(), - matchType: z.enum(MatchType), + teamNumber: z.coerce.number().int(), + matchNumber: z.coerce.number().int(), + matchType: z.nativeEnum(MatchType), }) - .parse(req.query); + .safeParse(req.query); + + if (!parsed.success) { + res.status(400).send(parsed.error.flatten()); + return; + } + + const params = parsed.data; await addTournamentMatches(params.tournamentKey); From 92f1427adeb1e0a0a8357d9e43db75d5329e52ba Mon Sep 17 00:00:00 2001 From: JackAttack-365 <142643773+jackattack-4@users.noreply.github.com> Date: Fri, 3 Jul 2026 11:40:50 -0700 Subject: [PATCH 5/8] fix docs more and add 404 response --- src/handler/manager/checkMatchExists.ts | 23 ++++++++++++++++++----- src/routes/manager/manager.routes.ts | 9 ++++++--- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/handler/manager/checkMatchExists.ts b/src/handler/manager/checkMatchExists.ts index 1f559f08..ef315b5e 100644 --- a/src/handler/manager/checkMatchExists.ts +++ b/src/handler/manager/checkMatchExists.ts @@ -1,7 +1,7 @@ import z from "zod"; -import { addTournamentMatches } from "./addTournamentMatches"; +import { addTournamentMatches } from "./addTournamentMatches.js"; import { Request, Response } from "express"; -import prismaClient from "../../prismaClient"; +import prismaClient from "../../prismaClient.js"; import { MatchType } from "@prisma/client"; export const checkMatchExists = async ( @@ -14,7 +14,7 @@ export const checkMatchExists = async ( tournamentKey: z.string(), teamNumber: z.coerce.number().int(), matchNumber: z.coerce.number().int(), - matchType: z.nativeEnum(MatchType), + isElim: z.coerce.boolean(), }) .safeParse(req.query); @@ -28,10 +28,23 @@ export const checkMatchExists = async ( await addTournamentMatches(params.tournamentKey); const match = await prismaClient.teamMatchData.findFirst({ - where: params, + where: { + matchNumber: params.matchNumber, + tournamentKey: params.tournamentKey, + teamNumber: params.teamNumber, + matchType: params.isElim + ? MatchType.QUALIFICATION + : MatchType.ELIMINATION, + }, }); - res.status(200).send(match); + if (match !== null) { + res + .status(200) + .send({ match, alliance: Number(match.key[-1]) < 3 ? "RED" : "BLUE" }); + return; + } + res.status(404).send("MATCH_NOT_FOUND"); } catch (error) { console.log(error); res.status(500).send(error); diff --git a/src/routes/manager/manager.routes.ts b/src/routes/manager/manager.routes.ts index d9d5c20f..3cd0834d 100644 --- a/src/routes/manager/manager.routes.ts +++ b/src/routes/manager/manager.routes.ts @@ -142,17 +142,20 @@ registry.registerPath({ tournamentKey: z.string(), teamNumber: z.coerce.number().int(), matchNumber: z.coerce.number().int(), - matchType: z.nativeEnum(MatchType), + isElim: z.coerce.boolean(), }), }, responses: { 200: { description: "Match Data Row (or null if not found)", - content: { "application/json": { schema: TeamMatchDataSchema.nullable() } }, + content: { + "application/json": { schema: TeamMatchDataSchema.nullable() }, + }, }, 400: { description: "Invalid parameters" }, 401: { description: "Unauthorized" }, - security: [{ bearerAuth: [] }], + }, + security: [], }); // Profile From fc94cb541c283e1a8c10d9b1d2ed041b244082de Mon Sep 17 00:00:00 2001 From: JackAttack-365 <142643773+jackattack-4@users.noreply.github.com> Date: Tue, 7 Jul 2026 20:24:29 -0700 Subject: [PATCH 6/8] fix boolean coercion and documentation --- src/handler/manager/checkMatchExists.ts | 15 +++++++++------ src/routes/manager/manager.routes.ts | 10 +++++++--- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/handler/manager/checkMatchExists.ts b/src/handler/manager/checkMatchExists.ts index ef315b5e..a8bd44c0 100644 --- a/src/handler/manager/checkMatchExists.ts +++ b/src/handler/manager/checkMatchExists.ts @@ -14,7 +14,9 @@ export const checkMatchExists = async ( tournamentKey: z.string(), teamNumber: z.coerce.number().int(), matchNumber: z.coerce.number().int(), - isElim: z.coerce.boolean(), + isElim: z + .union([z.literal("true"), z.literal("false"), z.boolean()]) + .transform((value) => value === true || value === "true"), }) .safeParse(req.query); @@ -33,15 +35,16 @@ export const checkMatchExists = async ( tournamentKey: params.tournamentKey, teamNumber: params.teamNumber, matchType: params.isElim - ? MatchType.QUALIFICATION - : MatchType.ELIMINATION, + ? MatchType.ELIMINATION + : MatchType.QUALIFICATION, }, }); if (match !== null) { - res - .status(200) - .send({ match, alliance: Number(match.key[-1]) < 3 ? "RED" : "BLUE" }); + res.status(200).send({ + match, + alliance: Number(match.key.at(-1)) < 3 ? "red" : "blue", + }); return; } res.status(404).send("MATCH_NOT_FOUND"); diff --git a/src/routes/manager/manager.routes.ts b/src/routes/manager/manager.routes.ts index 3cd0834d..be18f8bb 100644 --- a/src/routes/manager/manager.routes.ts +++ b/src/routes/manager/manager.routes.ts @@ -35,7 +35,6 @@ import { TournamentSchema, } from "../../lib/prisma-zod.js"; import { requireVerifiedTeam } from "../../lib/middleware/requireVerifiedTeam.js"; -import { MatchType } from "@prisma/client"; import { checkMatchExists } from "../../handler/manager/checkMatchExists.js"; const router = Router(); @@ -149,11 +148,16 @@ registry.registerPath({ 200: { description: "Match Data Row (or null if not found)", content: { - "application/json": { schema: TeamMatchDataSchema.nullable() }, + "application/json": { + schema: z.object({ + match: TeamMatchDataSchema, + alliance: z.string(), + }), + }, }, }, 400: { description: "Invalid parameters" }, - 401: { description: "Unauthorized" }, + 404: { description: "Match not found" }, }, security: [], }); From 3759acc60d277b78700afbd22dc0a2d7b4c7edb1 Mon Sep 17 00:00:00 2001 From: jackattack-4 <142643773+jackattack-4@users.noreply.github.com> Date: Tue, 7 Jul 2026 20:30:23 -0700 Subject: [PATCH 7/8] Fix documentation Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/routes/manager/manager.routes.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/routes/manager/manager.routes.ts b/src/routes/manager/manager.routes.ts index be18f8bb..ecfc1fbc 100644 --- a/src/routes/manager/manager.routes.ts +++ b/src/routes/manager/manager.routes.ts @@ -141,7 +141,9 @@ registry.registerPath({ tournamentKey: z.string(), teamNumber: z.coerce.number().int(), matchNumber: z.coerce.number().int(), - isElim: z.coerce.boolean(), + isElim: z + .union([z.literal("true"), z.literal("false"), z.boolean()]) + .transform((value) => value === true || value === "true"), }), }, responses: { From 0eb26ea58d83bb05020268cf9235a1604a3c3cd0 Mon Sep 17 00:00:00 2001 From: jackattack-4 <142643773+jackattack-4@users.noreply.github.com> Date: Tue, 7 Jul 2026 20:30:39 -0700 Subject: [PATCH 8/8] Fix documentation more Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/routes/manager/manager.routes.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/manager/manager.routes.ts b/src/routes/manager/manager.routes.ts index ecfc1fbc..333be0c5 100644 --- a/src/routes/manager/manager.routes.ts +++ b/src/routes/manager/manager.routes.ts @@ -148,7 +148,7 @@ registry.registerPath({ }, responses: { 200: { - description: "Match Data Row (or null if not found)", + description: "Match data row and alliance", content: { "application/json": { schema: z.object({