Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 26 additions & 11 deletions db/tables/applications.ts
Original file line number Diff line number Diff line change
@@ -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";
Expand Down Expand Up @@ -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,
Expand Down
57 changes: 49 additions & 8 deletions src/db-access/applications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,19 @@ 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 {
ApplicationKey,
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 (
Expand All @@ -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,
Expand All @@ -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);
Expand All @@ -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,
Expand All @@ -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);
Expand All @@ -83,11 +88,13 @@ export const selectTeamApplicationsByTeamId = async (

export const selectTeamApplicationsById = async (
applicationIds: ApplicationKey[],
teamApplicationIds: number[],
): Promise<OrmResult<TeamApplication[]>> => {
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,
Expand All @@ -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;
Expand Down Expand Up @@ -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();

Expand All @@ -145,3 +159,30 @@ export async function insertTeamApplication(
};
});
}

export async function createTeamApplicationFromAssistantApplication(
teamInterestApplication: NewTeamInterestApplication,
): Promise<OrmResult<TeamApplication[]>> {
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;
});
}
8 changes: 8 additions & 0 deletions src/request-handling/applications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<typeof applicationToInsertParser>;
export type NewTeamApplication = z.infer<typeof teamApplicationToInsertParser>;
export type NewAssistantApplication = z.infer<
typeof assistantApplicationToInsertParser
>;
export type NewTeamInterestApplication = z.infer<typeof teamInterestParser>;
5 changes: 4 additions & 1 deletion src/response-handling/applications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,8 @@ export const teamApplicationSelectSchema = createSelectSchema(
.readonly();

export type TeamApplication = z.infer<typeof teamApplicationSelectSchema>;
export type TeamApplicationKey = TeamApplication["id"];
export type TeamApplicationKey = {
id: TeamApplication["id"];
applicationParentId: TeamApplication["applicationParentId"];
};
export type TeamKey = TeamApplication["teamId"];
55 changes: 54 additions & 1 deletion src/routers/applications.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -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);
},
);