Skip to content

Commit

Permalink
Add unique constraint to Hacker and update relations (#66)
Browse files Browse the repository at this point in the history
* Add unique constraint to Hacker and update relations

This commit ensures that each hacker-user pairing for a hackathon is unique by adding a new unique constraint on the `Hacker` table. It also modifies the `User` model to reflect a one-to-many relationship with `Hacker` instead of a one-to-one relationship.

* Remove unique constraint from HackerApplication model

The unique constraint on the combined fields userId and hackathonId in the HackerApplication model has been removed. This change allows for multiple entries with the same userId and hackathonId combination. The modification addresses issues related to duplicative data entries for users in the same hackathon.

* Remove unique index on Hacker table

This commit drops the unique index "Hacker_userId_hackathonId_key". This change allows multiple records with the same combination of userId and hackathonId.

* ```
feat: add composite unique constraint to Hacker model

Introduce a composite unique constraint on userId and hackathonId in the Hacker model to ensure unique applications per user per hackathon. Also, update related application logic to accommodate the new constraint.
```

* feat: add composite unique index to Hacker table

Ensure unique constraint on userId and hackathonId columns. This prevents duplicate entries and enforces data integrity for hacker-hackathon pairs.

* feat: include hackathonId in getApplicationFormStep

Added hackathonId parameter to getApplicationFormStep function to better filter hacker records. This change ensures that hackers are correctly associated with their respective hackathons.

* feat: add hackathonId to session and form step retrieval

Integrated the retrieval of the active hackathon ID into hacker session validation and form step data fetching. This ensures that operations are scoped to the current active hackathon.

* refactor: streamline hacker session and application handling

Removed unnecessary hackathon ID check in requireHackerSession and updated query to order by creation date. Enhanced isApplicationComplete to include detailed application and hacker validation.
  • Loading branch information
MatejMa2ur authored Oct 15, 2024
1 parent f9b4d6d commit f1e7b99
Show file tree
Hide file tree
Showing 9 changed files with 76 additions and 8 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/*
Warnings:
- A unique constraint covering the columns `[userId,hackathonId]` on the table `Hacker` will be added. If there are existing duplicate values, this will fail.
*/
-- DropIndex
DROP INDEX "Hacker_userId_key";

-- CreateIndex
CREATE UNIQUE INDEX "Hacker_userId_hackathonId_key" ON "Hacker"("userId", "hackathonId");
2 changes: 2 additions & 0 deletions prisma/migrations/20241015132032_remove_unique/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- DropIndex
DROP INDEX "Hacker_userId_hackathonId_key";
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
Warnings:
- A unique constraint covering the columns `[userId,hackathonId]` on the table `Hacker` will be added. If there are existing duplicate values, this will fail.
*/
-- CreateIndex
CREATE UNIQUE INDEX "Hacker_userId_hackathonId_key" ON "Hacker"("userId", "hackathonId");
5 changes: 3 additions & 2 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ model User {
forgotPasswordLastRequest DateTime?
image String?
accounts Account[]
hacker Hacker?
hacker Hacker[]
organizer Organizer?
sponsor Sponsor?
}
Expand Down Expand Up @@ -70,7 +70,7 @@ model Hackathon {
model Hacker {
id Int @id @default(autoincrement())
user User @relation(fields: [userId], references: [id])
userId Int @unique
userId Int
team Team? @relation(fields: [teamId], references: [id])
ownedTeam Team? @relation(name: "TeamOwner")
teamId Int?
Expand All @@ -80,6 +80,7 @@ model Hacker {
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
travelReimbursementRequest TravelReimbursementRequest?
@@unique([userId, hackathonId], name: "hackerApplication")
}

model Team {
Expand Down
10 changes: 9 additions & 1 deletion src/app/application/form/step/[stepId]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import ApplicationFormStep from "@/scenes/ApplicationFormStep/ApplicationFormSte
import getApplicationFormStep from "@/server/getters/application/applicationFormStep";
import { Metadata } from "next";
import requireNonOrganizer from "@/services/helpers/requireNonOrganizer";
import getActiveHackathonId from "@/server/getters/getActiveHackathonId";
import { prisma } from "@/services/prisma";

export const metadata: Metadata = {
title: "Application Form",
Expand All @@ -14,8 +16,14 @@ const ApplicationFormStepPage = async ({
params: { stepId: string };
}) => {
await requireNonOrganizer();
const hackathonId = await getActiveHackathonId(prisma);
if (!hackathonId) {
return null;
}

const applicationFormStepData = await getApplicationFormStep(
Number(params.stepId)
Number(params.stepId),
hackathonId
);
return (
<ApplicationFormStep
Expand Down
7 changes: 5 additions & 2 deletions src/server/getters/application/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ const getApplicationData = async ({
},
},
},
hackathonId: true,
},
},
},
Expand Down Expand Up @@ -185,7 +186,8 @@ const getApplicationData = async ({
steps.every((step) => step.isCompleted) && session.emailVerified;

// Get list of all free tables if user doesn't have table assigned
const tableCode = user.hacker?.team?.table?.code;
const hacker = user.hacker.find((h) => h.hackathonId === hackathonId);
const tableCode = hacker?.team?.table?.code;
let freeTables = [] as string[];
if (!tableCode) {
const tables = await prisma.table.findMany({
Expand Down Expand Up @@ -217,7 +219,8 @@ const getApplicationData = async ({
steps,
canSubmit,
hackathonName: hackathon.name,
tableCode: user.hacker?.team?.table?.code,
tableCode: user.hacker.find((h) => h.hackathonId === hackathonId)?.team
?.table?.code,
freeTables,
},
};
Expand Down
6 changes: 4 additions & 2 deletions src/server/getters/application/applicationFormStep.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ export type ApplicationFormStepData = {
};

const getApplicationFormStep = async (
stepId: number
stepId: number,
hackathonId: number
): Promise<ApplicationFormStepData> => {
const session = await getServerSession(authOptions);

Expand All @@ -38,9 +39,10 @@ const getApplicationFormStep = async (
};
}

const hacker = await prisma.hacker.findUnique({
const hacker = await prisma.hacker.findFirst({
where: {
userId: session.id,
hackathonId: hackathonId,
},
});

Expand Down
29 changes: 29 additions & 0 deletions src/server/services/helpers/applications/isApplicationComplete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,36 @@ const isApplicationComplete = async (
},
});

const application = await prisma.application.findUnique({
where: {
id: applicationId,
},
select: {
id: true,
hacker: {
select: {
hackathonId: true,
},
},
formValues: {
select: {
fieldId: true,
value: true,
},
},
},
});

if (!application || !application.hacker) {
throw new Error("Application or hacker not found");
}

const { hackathonId } = application.hacker;

const stepsDb = await prisma.applicationFormStep.findMany({
where: {
hackathonId,
},
select: {
formFields: {
select: {
Expand Down
6 changes: 5 additions & 1 deletion src/server/services/helpers/auth/requireHackerSession.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import "server-only";
import { getServerSession } from "next-auth/next";
import { authOptions } from "@/app/api/auth/[...nextauth]/route";
import { prisma } from "@/services/prisma";
import getActiveHackathonId from "@/server/getters/getActiveHackathonId";

type RequireHackerSessionOptions = {
verified?: boolean;
Expand All @@ -20,10 +21,13 @@ const requireHackerSession = async ({
throw new Error("User email not verified");
}

const hacker = await prisma.hacker.findUnique({
const hacker = await prisma.hacker.findFirst({
where: {
userId: session.id,
},
orderBy: {
createdAt: "desc",
},
});

if (!hacker) {
Expand Down

0 comments on commit f1e7b99

Please sign in to comment.