From 63f595a212d933a25b008f90855f2f475bc124a8 Mon Sep 17 00:00:00 2001 From: solvhold Date: Mon, 16 Feb 2026 17:55:36 +0100 Subject: [PATCH 1/3] feat: :sparkles: adding api call for returing all team applications given a semester #73 --- src/db-access/applications.ts | 39 +++++++++++++++++++++++++++++ src/routers/applications.ts | 46 +++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) diff --git a/src/db-access/applications.ts b/src/db-access/applications.ts index 328a731..7d42fde 100644 --- a/src/db-access/applications.ts +++ b/src/db-access/applications.ts @@ -17,6 +17,7 @@ import type { } from "@/src/response-handling/applications"; import { and, eq, inArray } from "drizzle-orm"; import { newDatabaseTransaction } from "./common"; +import { SemesterKey } from "../response-handling/semesters"; export const selectTeamApplications = async ( parameters: QueryParameters, @@ -127,6 +128,44 @@ export const selectTeamApplicationsById = async ( }); }; +export const selectTeamApplicationsBySemester = async ( + semesterId: SemesterKey[], + parameters: QueryParameters, +): Promise> => { + return await newDatabaseTransaction(database, async (tx) => { + const selectResult = await tx + .select({ + id: teamApplicationsTable.id, + applicationParentId: teamApplicationsTable.applicationParentId, + teamId: teamApplicationsTable.teamId, + firstName: applicationsTable.firstName, + lastName: applicationsTable.lastName, + gender: applicationsTable.gender, + email: applicationsTable.email, + fieldOfStudyId: applicationsTable.fieldOfStudyId, + yearOfStudy: applicationsTable.yearOfStudy, + phonenumber: applicationsTable.phonenumber, + motivationText: teamApplicationsTable.motivationText, + biography: teamApplicationsTable.biography, + teamInterest: teamApplicationsTable.teamInterest, + semester: applicationsTable.semester, + submitDate: applicationsTable.submitDate, + }) + .from(teamApplicationsTable) + .where( + inArray(applicationsTable.semester, semesterId), + ) + .innerJoin( + applicationsTable, + eq(teamApplicationsTable.applicationParentId, applicationsTable.id), + ) + .limit(parameters.limit) + .offset(parameters.offset); + + return selectResult; + }); +}; + export async function insertTeamApplication( teamApplication: NewTeamApplication & NewApplication, ): Promise> { diff --git a/src/routers/applications.ts b/src/routers/applications.ts index e517927..a0c202a 100644 --- a/src/routers/applications.ts +++ b/src/routers/applications.ts @@ -2,6 +2,7 @@ import { createTeamApplicationFromAssistantApplication, insertTeamApplication, selectTeamApplications, + selectTeamApplicationsBySemester, selectTeamApplicationsByTeamId, } from "@/src/db-access/applications"; import { clientError } from "@/src/error/http-errors"; @@ -104,6 +105,51 @@ teamApplicationRouter.get("/:teamID/", async (req, res, next) => { res.json(databaseResult.data); }); +/** + * @openapi + * /teamapplications/{semesterId}/: + * get: + * tags: [teamapplications] + * summary: Get teamapplications with semesterid + * description: Get teamapplications with semesterid + * parameters: + * - $ref: "#/components/parameters/offset" + * - $ref: "#/components/parameters/limit" + * responses: + * 200: + * description: Successfull response + * content: + * application/json: + * schema: + * $ref: "#/components/schemas/teamApplication" + */ +teamApplicationRouter.get("/semester/:semesterID/", async (req, res, next) => { + const semesterIdResult = toSerialIdParser.safeParse(req.params.semesterID); + if (!semesterIdResult.success) { + return next(clientError(400, "Invalid request format", semesterIdResult.error)); + } + const queryParametersResult = toListQueryParser.safeParse(req.query); + if (!queryParametersResult.success) { + return next( + clientError(400, "Invalid request format", queryParametersResult.error), + ); + } + const databaseResult = await selectTeamApplicationsBySemester( + [semesterIdResult.data], + queryParametersResult.data, + ); + if (!databaseResult.success) { + return next( + clientError( + 400, + "Failed to retrieve data from the database", + databaseResult.error, + ), + ); + } + res.json(databaseResult.data); +}); + /** * @openapi * /teamapplications/: From 17d8a2a704fc1e6cb26d67f8513f04683f943df3 Mon Sep 17 00:00:00 2001 From: solvhold Date: Mon, 16 Feb 2026 18:25:43 +0100 Subject: [PATCH 2/3] feat: :sparkles: adding api call for returning all assistant applications given a semester #73 --- src/db-access/applications.ts | 35 ++++++++++++++++++ src/main.ts | 5 ++- src/response-handling/applications.ts | 9 +++++ src/routers/applications.ts | 51 ++++++++++++++++++++++++++- 4 files changed, 98 insertions(+), 2 deletions(-) diff --git a/src/db-access/applications.ts b/src/db-access/applications.ts index 7d42fde..486c408 100644 --- a/src/db-access/applications.ts +++ b/src/db-access/applications.ts @@ -1,6 +1,7 @@ import { database } from "@/db/setup/query-postgres"; import { applicationsTable, + assistantApplicationsTable, teamApplicationsTable, } from "@/db/tables/applications"; import { type OrmResult, ormError } from "@/src/error/orm-error"; @@ -12,6 +13,7 @@ import type { import type { QueryParameters } from "@/src/request-handling/common"; import type { ApplicationKey, + AssistantApplication, TeamApplication, TeamKey, } from "@/src/response-handling/applications"; @@ -166,6 +168,39 @@ export const selectTeamApplicationsBySemester = async ( }); }; +export const selectAssistantApplicationsBySemester = async ( + semesterId: SemesterKey[], + parameters: QueryParameters, +): Promise> => { + return await newDatabaseTransaction(database, async (tx) => { + const selectResult = await tx + .select({ + id: assistantApplicationsTable.id, + firstName: applicationsTable.firstName, + lastName: applicationsTable.lastName, + gender: applicationsTable.gender, + email: applicationsTable.email, + fieldOfStudyId: applicationsTable.fieldOfStudyId, + yearOfStudy: applicationsTable.yearOfStudy, + phonenumber: applicationsTable.phonenumber, + semester: applicationsTable.semester, + submitDate: applicationsTable.submitDate, + }) + .from(assistantApplicationsTable) + .where( + inArray(applicationsTable.semester, semesterId), + ) + .innerJoin( + applicationsTable, + eq(assistantApplicationsTable.id, applicationsTable.id), + ) + .limit(parameters.limit) + .offset(parameters.offset); + + return selectResult; + }); +}; + export async function insertTeamApplication( teamApplication: NewTeamApplication & NewApplication, ): Promise> { diff --git a/src/main.ts b/src/main.ts index 8541087..e02bcb8 100644 --- a/src/main.ts +++ b/src/main.ts @@ -8,7 +8,7 @@ import { zodErrorHandler, } from "@/src/middleware/error-middleware"; import { logger } from "@/src/middleware/logging-middleware"; -import { teamApplicationRouter } from "@/src/routers/applications"; +import { assistantApplicationRouter, teamApplicationRouter } from "@/src/routers/applications"; import { expensesRouter } from "@/src/routers/expenses"; import { sponsorsRouter } from "@/src/routers/sponsors"; import { teamsRouter } from "@/src/routers/teams"; @@ -37,6 +37,9 @@ api.use("/sponsors", sponsorsRouter); api.use("/users", usersRouter); api.use("/teamapplications", teamApplicationRouter); + +api.use("/assistantapplications", assistantApplicationRouter); + api.use("/teams", teamsRouter); // Error handling diff --git a/src/response-handling/applications.ts b/src/response-handling/applications.ts index 517a677..c62b69a 100644 --- a/src/response-handling/applications.ts +++ b/src/response-handling/applications.ts @@ -4,6 +4,7 @@ import type { z } from "zod"; import { applicationsTable, teamApplicationsTable, + assistantApplicationsTable } from "@/db/tables/applications"; export const applicationSelectSchema = createSelectSchema(applicationsTable) @@ -26,3 +27,11 @@ export type TeamApplicationKey = { applicationParentId: TeamApplication["applicationParentId"]; }; export type TeamKey = TeamApplication["teamId"]; + +export const assistantApplicationSelectSchema = createSelectSchema( + assistantApplicationsTable, +) + .merge(createSelectSchema(applicationsTable)) + .strict() + .readonly(); +export type AssistantApplication = z.infer; diff --git a/src/routers/applications.ts b/src/routers/applications.ts index a0c202a..bed4305 100644 --- a/src/routers/applications.ts +++ b/src/routers/applications.ts @@ -1,6 +1,7 @@ import { createTeamApplicationFromAssistantApplication, insertTeamApplication, + selectAssistantApplicationsBySemester, selectTeamApplications, selectTeamApplicationsBySemester, selectTeamApplicationsByTeamId, @@ -17,8 +18,10 @@ import { import { Router, json } from "express"; export const teamApplicationRouter = Router(); +export const assistantApplicationRouter = Router(); teamApplicationRouter.use(json()); +assistantApplicationRouter.use(json()); /** * @openapi @@ -107,7 +110,7 @@ teamApplicationRouter.get("/:teamID/", async (req, res, next) => { /** * @openapi - * /teamapplications/{semesterId}/: + * /teamapplications/semester/{semesterID}/: * get: * tags: [teamapplications] * summary: Get teamapplications with semesterid @@ -150,6 +153,52 @@ teamApplicationRouter.get("/semester/:semesterID/", async (req, res, next) => { res.json(databaseResult.data); }); + +/** + * @openapi + * /assistantapplications/semester/{semesterId}/: + * get: + * tags: [assistantapplications] + * summary: Get assistantapplications with semesterid + * description: Get assistantapplications with semesterid + * parameters: + * - $ref: "#/components/parameters/offset" + * - $ref: "#/components/parameters/limit" + * responses: + * 200: + * description: Successfull response + * content: + * application/json: + * schema: + * $ref: "#/components/schemas/assistantApplication" + */ +assistantApplicationRouter.get("/semester/:semesterID/", async (req, res, next) => { + const semesterIdResult = toSerialIdParser.safeParse(req.params.semesterID); + if (!semesterIdResult.success) { + return next(clientError(400, "Invalid request format", semesterIdResult.error)); + } + const queryParametersResult = toListQueryParser.safeParse(req.query); + if (!queryParametersResult.success) { + return next( + clientError(400, "Invalid request format", queryParametersResult.error), + ); + } + const databaseResult = await selectAssistantApplicationsBySemester( + [semesterIdResult.data], + queryParametersResult.data, + ); + if (!databaseResult.success) { + return next( + clientError( + 400, + "Failed to retrieve data from the database", + databaseResult.error, + ), + ); + } + res.json(databaseResult.data); +}); + /** * @openapi * /teamapplications/: From fe219834d9d2e150331b1b5dc01adec226642f21 Mon Sep 17 00:00:00 2001 From: solvhold Date: Mon, 16 Feb 2026 18:26:56 +0100 Subject: [PATCH 3/3] refactor: :art: run pnpm check #73 --- src/db-access/applications.ts | 10 ++--- src/main.ts | 5 ++- src/response-handling/applications.ts | 6 ++- src/routers/applications.ts | 60 +++++++++++++++------------ 4 files changed, 44 insertions(+), 37 deletions(-) diff --git a/src/db-access/applications.ts b/src/db-access/applications.ts index 486c408..9d834c7 100644 --- a/src/db-access/applications.ts +++ b/src/db-access/applications.ts @@ -18,8 +18,8 @@ import type { TeamKey, } from "@/src/response-handling/applications"; import { and, eq, inArray } from "drizzle-orm"; +import type { SemesterKey } from "../response-handling/semesters"; import { newDatabaseTransaction } from "./common"; -import { SemesterKey } from "../response-handling/semesters"; export const selectTeamApplications = async ( parameters: QueryParameters, @@ -154,9 +154,7 @@ export const selectTeamApplicationsBySemester = async ( submitDate: applicationsTable.submitDate, }) .from(teamApplicationsTable) - .where( - inArray(applicationsTable.semester, semesterId), - ) + .where(inArray(applicationsTable.semester, semesterId)) .innerJoin( applicationsTable, eq(teamApplicationsTable.applicationParentId, applicationsTable.id), @@ -187,9 +185,7 @@ export const selectAssistantApplicationsBySemester = async ( submitDate: applicationsTable.submitDate, }) .from(assistantApplicationsTable) - .where( - inArray(applicationsTable.semester, semesterId), - ) + .where(inArray(applicationsTable.semester, semesterId)) .innerJoin( applicationsTable, eq(assistantApplicationsTable.id, applicationsTable.id), diff --git a/src/main.ts b/src/main.ts index e02bcb8..6c11fc8 100644 --- a/src/main.ts +++ b/src/main.ts @@ -8,7 +8,10 @@ import { zodErrorHandler, } from "@/src/middleware/error-middleware"; import { logger } from "@/src/middleware/logging-middleware"; -import { assistantApplicationRouter, teamApplicationRouter } from "@/src/routers/applications"; +import { + assistantApplicationRouter, + teamApplicationRouter, +} from "@/src/routers/applications"; import { expensesRouter } from "@/src/routers/expenses"; import { sponsorsRouter } from "@/src/routers/sponsors"; import { teamsRouter } from "@/src/routers/teams"; diff --git a/src/response-handling/applications.ts b/src/response-handling/applications.ts index c62b69a..9049932 100644 --- a/src/response-handling/applications.ts +++ b/src/response-handling/applications.ts @@ -3,8 +3,8 @@ import type { z } from "zod"; import { applicationsTable, + assistantApplicationsTable, teamApplicationsTable, - assistantApplicationsTable } from "@/db/tables/applications"; export const applicationSelectSchema = createSelectSchema(applicationsTable) @@ -34,4 +34,6 @@ export const assistantApplicationSelectSchema = createSelectSchema( .merge(createSelectSchema(applicationsTable)) .strict() .readonly(); -export type AssistantApplication = z.infer; +export type AssistantApplication = z.infer< + typeof assistantApplicationSelectSchema +>; diff --git a/src/routers/applications.ts b/src/routers/applications.ts index bed4305..e883343 100644 --- a/src/routers/applications.ts +++ b/src/routers/applications.ts @@ -129,7 +129,9 @@ teamApplicationRouter.get("/:teamID/", async (req, res, next) => { teamApplicationRouter.get("/semester/:semesterID/", async (req, res, next) => { const semesterIdResult = toSerialIdParser.safeParse(req.params.semesterID); if (!semesterIdResult.success) { - return next(clientError(400, "Invalid request format", semesterIdResult.error)); + return next( + clientError(400, "Invalid request format", semesterIdResult.error), + ); } const queryParametersResult = toListQueryParser.safeParse(req.query); if (!queryParametersResult.success) { @@ -153,7 +155,6 @@ teamApplicationRouter.get("/semester/:semesterID/", async (req, res, next) => { res.json(databaseResult.data); }); - /** * @openapi * /assistantapplications/semester/{semesterId}/: @@ -172,32 +173,37 @@ teamApplicationRouter.get("/semester/:semesterID/", async (req, res, next) => { * schema: * $ref: "#/components/schemas/assistantApplication" */ -assistantApplicationRouter.get("/semester/:semesterID/", async (req, res, next) => { - const semesterIdResult = toSerialIdParser.safeParse(req.params.semesterID); - if (!semesterIdResult.success) { - return next(clientError(400, "Invalid request format", semesterIdResult.error)); - } - const queryParametersResult = toListQueryParser.safeParse(req.query); - if (!queryParametersResult.success) { - return next( - clientError(400, "Invalid request format", queryParametersResult.error), - ); - } - const databaseResult = await selectAssistantApplicationsBySemester( - [semesterIdResult.data], - queryParametersResult.data, - ); - if (!databaseResult.success) { - return next( - clientError( - 400, - "Failed to retrieve data from the database", - databaseResult.error, - ), +assistantApplicationRouter.get( + "/semester/:semesterID/", + async (req, res, next) => { + const semesterIdResult = toSerialIdParser.safeParse(req.params.semesterID); + if (!semesterIdResult.success) { + return next( + clientError(400, "Invalid request format", semesterIdResult.error), + ); + } + const queryParametersResult = toListQueryParser.safeParse(req.query); + if (!queryParametersResult.success) { + return next( + clientError(400, "Invalid request format", queryParametersResult.error), + ); + } + const databaseResult = await selectAssistantApplicationsBySemester( + [semesterIdResult.data], + queryParametersResult.data, ); - } - res.json(databaseResult.data); -}); + if (!databaseResult.success) { + return next( + clientError( + 400, + "Failed to retrieve data from the database", + databaseResult.error, + ), + ); + } + res.json(databaseResult.data); + }, +); /** * @openapi