diff --git a/db/tables/applications.ts b/db/tables/applications.ts index b375563..264868c 100644 --- a/db/tables/applications.ts +++ b/db/tables/applications.ts @@ -1,6 +1,13 @@ import { mainSchema } from "@/db/tables/schema"; import { relations } from "drizzle-orm"; -import { date, integer, serial, text } from "drizzle-orm/pg-core"; +import { + boolean, + date, + integer, + primaryKey, + serial, + text, +} from "drizzle-orm/pg-core"; import { teamsTable } from "@/db/tables/teams"; import { fieldsOfStudyTable } from "./fields-of-study"; @@ -42,16 +49,24 @@ export const applicationsRelations = relations( }), ); -export const teamApplicationsTable = mainSchema.table("teamApplications", { - id: integer("id") - .primaryKey() - .references(() => applicationsTable.id), - teamId: integer("teamId") - .notNull() - .references(() => teamsTable.id), - motivationText: text("motivationText").notNull(), - biography: text("biography").notNull(), -}); +export const teamApplicationsTable = mainSchema.table( + "teamApplications", + { + id: serial("id"), + applicationParentId: integer("applicationParentId").references( + () => applicationsTable.id, + ), + teamId: integer("teamId") + .notNull() + .references(() => teamsTable.id), + motivationText: text("motivationText"), + biography: text("biography"), + teamInterest: boolean("teamInterest").notNull(), + }, + (table) => ({ + primaryKey: primaryKey({ columns: [table.id, table.applicationParentId] }), + }), +); export const teamApplicationsRelations = relations( teamApplicationsTable, diff --git a/src/db-access/applications.ts b/src/db-access/applications.ts index b2a7893..2fa95b2 100644 --- a/src/db-access/applications.ts +++ b/src/db-access/applications.ts @@ -3,10 +3,11 @@ import { applicationsTable, teamApplicationsTable, } from "@/db/tables/applications"; -import type { OrmResult } from "@/src/error/orm-error"; +import { type OrmResult, ormError } from "@/src/error/orm-error"; import type { NewApplication, NewTeamApplication, + NewTeamInterestApplication, } from "@/src/request-handling/applications"; import type { QueryParameters } from "@/src/request-handling/common"; import type { @@ -14,7 +15,7 @@ import type { TeamApplication, TeamKey, } from "@/src/response-handling/applications"; -import { eq, inArray } from "drizzle-orm"; +import { and, eq, inArray } from "drizzle-orm"; import { newDatabaseTransaction } from "./common"; export const selectTeamApplications = async ( @@ -24,6 +25,7 @@ export const selectTeamApplications = async ( const teamApplications = await tx .select({ id: applicationsTable.id, + applicationParentId: teamApplicationsTable.applicationParentId, teamId: teamApplicationsTable.teamId, firstName: applicationsTable.firstName, lastName: applicationsTable.lastName, @@ -34,12 +36,13 @@ export const selectTeamApplications = async ( phonenumber: applicationsTable.phonenumber, motivationText: teamApplicationsTable.motivationText, biography: teamApplicationsTable.biography, + teamInterest: teamApplicationsTable.teamInterest, submitDate: applicationsTable.submitDate, }) .from(teamApplicationsTable) .innerJoin( applicationsTable, - eq(teamApplicationsTable.id, applicationsTable.id), + eq(teamApplicationsTable.applicationParentId, applicationsTable.id), ) .limit(parameters.limit) .offset(parameters.offset); @@ -56,6 +59,7 @@ export const selectTeamApplicationsByTeamId = async ( const selectResult = await tx .select({ id: applicationsTable.id, + applicationParentId: teamApplicationsTable.applicationParentId, teamId: teamApplicationsTable.teamId, firstName: applicationsTable.firstName, lastName: applicationsTable.lastName, @@ -66,13 +70,14 @@ export const selectTeamApplicationsByTeamId = async ( phonenumber: applicationsTable.phonenumber, motivationText: teamApplicationsTable.motivationText, biography: teamApplicationsTable.biography, + teamInterest: teamApplicationsTable.teamInterest, submitDate: applicationsTable.submitDate, }) .from(teamApplicationsTable) .where(inArray(teamApplicationsTable.id, teamId)) .innerJoin( applicationsTable, - eq(teamApplicationsTable.id, applicationsTable.id), + eq(teamApplicationsTable.applicationParentId, applicationsTable.id), ) .limit(parameters.limit) .offset(parameters.offset); @@ -83,11 +88,13 @@ export const selectTeamApplicationsByTeamId = async ( export const selectTeamApplicationsById = async ( applicationIds: ApplicationKey[], + teamApplicationIds: number[], ): Promise> => { return await newDatabaseTransaction(database, async (tx) => { const selectResult = await tx .select({ - id: applicationsTable.id, + id: teamApplicationsTable.id, + applicationParentId: teamApplicationsTable.applicationParentId, teamId: teamApplicationsTable.teamId, firstName: applicationsTable.firstName, lastName: applicationsTable.lastName, @@ -98,13 +105,19 @@ export const selectTeamApplicationsById = async ( phonenumber: applicationsTable.phonenumber, motivationText: teamApplicationsTable.motivationText, biography: teamApplicationsTable.biography, + teamInterest: teamApplicationsTable.teamInterest, submitDate: applicationsTable.submitDate, }) .from(teamApplicationsTable) - .where(inArray(teamApplicationsTable.id, applicationIds)) + .where( + and( + inArray(teamApplicationsTable.applicationParentId, applicationIds), + inArray(teamApplicationsTable.id, teamApplicationIds), + ), + ) .innerJoin( applicationsTable, - eq(teamApplicationsTable.id, applicationsTable.id), + eq(teamApplicationsTable.applicationParentId, applicationsTable.id), ); return selectResult; @@ -132,10 +145,11 @@ export async function insertTeamApplication( const newTeamApplicationResult = await tx .insert(teamApplicationsTable) .values({ - id: newApplicationId, + applicationParentId: newApplicationId, teamId: teamApplication.teamId, motivationText: teamApplication.motivationText, biography: teamApplication.biography, + teamInterest: teamApplication.teamInterest, }) .returning(); @@ -145,3 +159,30 @@ export async function insertTeamApplication( }; }); } + +export async function createTeamApplicationFromAssistantApplication( + teamInterestApplication: NewTeamInterestApplication, +): Promise> { + return await newDatabaseTransaction(database, async (tx) => { + const newTeamApplicationResult = await tx + .insert(teamApplicationsTable) + .values({ + applicationParentId: teamInterestApplication.applicationParentId, + teamId: teamInterestApplication.teamId, + motivationText: teamInterestApplication.motivationText, + biography: teamInterestApplication.biography, + teamInterest: true, + }) + .returning(); + + const teamApplicationResult = await selectTeamApplicationsById( + [teamInterestApplication.applicationParentId], + [newTeamApplicationResult[0].id], + ); + if (!teamApplicationResult.success) { + throw ormError("Transaction failed", teamApplicationResult.error); + } + + return teamApplicationResult.data; + }); +} diff --git a/src/request-handling/applications.ts b/src/request-handling/applications.ts index 57e19a7..101c6e2 100644 --- a/src/request-handling/applications.ts +++ b/src/request-handling/applications.ts @@ -86,8 +86,16 @@ export const assistantApplicationToInsertParser = assistantApplicationParser .readonly(), ); +export const teamInterestParser = z.object({ + applicationParentId: serialIdParser, + teamId: teamApplicationParser.shape.teamId, + biography: teamApplicationParser.shape.biography.nullable(), + motivationText: teamApplicationParser.shape.motivationText.nullable(), +}); + export type NewApplication = z.infer; export type NewTeamApplication = z.infer; export type NewAssistantApplication = z.infer< typeof assistantApplicationToInsertParser >; +export type NewTeamInterestApplication = z.infer; diff --git a/src/response-handling/applications.ts b/src/response-handling/applications.ts index fa09a77..517a677 100644 --- a/src/response-handling/applications.ts +++ b/src/response-handling/applications.ts @@ -21,5 +21,8 @@ export const teamApplicationSelectSchema = createSelectSchema( .readonly(); export type TeamApplication = z.infer; -export type TeamApplicationKey = TeamApplication["id"]; +export type TeamApplicationKey = { + id: TeamApplication["id"]; + applicationParentId: TeamApplication["applicationParentId"]; +}; export type TeamKey = TeamApplication["teamId"]; diff --git a/src/routers/applications.ts b/src/routers/applications.ts index d2a2c33..e517927 100644 --- a/src/routers/applications.ts +++ b/src/routers/applications.ts @@ -1,10 +1,14 @@ import { + createTeamApplicationFromAssistantApplication, insertTeamApplication, selectTeamApplications, selectTeamApplicationsByTeamId, } from "@/src/db-access/applications"; import { clientError } from "@/src/error/http-errors"; -import { teamApplicationToInsertParser } from "@/src/request-handling/applications"; +import { + teamApplicationToInsertParser, + teamInterestParser, +} from "@/src/request-handling/applications"; import { toListQueryParser, toSerialIdParser, @@ -147,3 +151,52 @@ teamApplicationRouter.post("/", async (req, res, next) => { } res.status(201).json(databaseResult.data); }); + +/** + * @openapi + * /teamapplications/createFromAssistantApplication: + * post: + * tags: [teamapplications] + * summary: Make teamapplication from assistantapplication + * description: Make teamapplication from assistantapplication + * requestBody: + * required: true + * content: + * json: + * schema: + * $ref: "#/components/schemas/teamApplicationRequest" + * responses: + * 201: + * description: Successfull response + * content: + * application/json: + * schema: + * $ref: "#/components/schemas/teamApplication" + */ +teamApplicationRouter.post( + "/createFromAssistantApplication/", + async (req, res, next) => { + const teamApplicationBodyResult = teamInterestParser.safeParse(req.body); + + if (!teamApplicationBodyResult.success) { + const error = clientError( + 400, + "Invalid request format", + teamApplicationBodyResult.error, + ); + return next(error); + } + const databaseResult = await createTeamApplicationFromAssistantApplication( + teamApplicationBodyResult.data, + ); + if (!databaseResult.success) { + const error = clientError( + 400, + "Failed to execute the database command", + databaseResult.error, + ); + return next(error); + } + res.status(201).json(databaseResult.data); + }, +);