From 5858d642ead2360673c1a1c832ca1bc330bf3f13 Mon Sep 17 00:00:00 2001 From: AJaccP Date: Sat, 15 Jun 2024 01:55:12 +0530 Subject: [PATCH] Added submissions service findByUuid method --- .../services/challenges-service.ts | 34 +++++- .../services/participants-service.ts | 43 ++++++-- .../services/submissions-service.ts | 100 +++++++++++++++--- .../services/challenges-service.test.ts | 37 +++++++ .../services/participants-service.test.ts | 42 ++++++++ .../services/submissions-service.test.ts | 39 +++++++ 6 files changed, 269 insertions(+), 26 deletions(-) diff --git a/app/challenges-platform/services/challenges-service.ts b/app/challenges-platform/services/challenges-service.ts index e6604f4..861a0f7 100644 --- a/app/challenges-platform/services/challenges-service.ts +++ b/app/challenges-platform/services/challenges-service.ts @@ -18,12 +18,42 @@ export const findByUuid = async ( .from(challenges) .where(eq(challenges.uuid, id)); - const transformer = challengesPlatform.findTransformer(result[0].type); - const challenge = transformer.newChallenge(result[0]); + if (result.length === 0) { + return Err(new Error("Challenge not found")); + } + + const challenge = await convert(result[0]); + + return Ok(challenge); +} + +export const findById = async ( + id: number, +): Promise> => { + + const result = await db + .select() + .from(challenges) + .where(eq(challenges.id, id)); + + if (result.length === 0) { + return Err(new Error("Challenge not found")); + } + + const challenge = await convert(result[0]); return Ok(challenge); }; +export const convert = async ( + result: any, +): Promise => { + const transformer = challengesPlatform.findTransformer(result.type); + const challenge = transformer.newChallenge(result); + + return challenge; +} + export const create = async ({ title, body, diff --git a/app/challenges-platform/services/participants-service.ts b/app/challenges-platform/services/participants-service.ts index c136bdf..5e73da3 100644 --- a/app/challenges-platform/services/participants-service.ts +++ b/app/challenges-platform/services/participants-service.ts @@ -17,14 +17,43 @@ export const findByUuid = async ( .from(participants) .where(eq(participants.uuid, id)); - const challenge = new Participant({ - id: result[0].id, - uuid: result[0].uuid, - email: result[0].email, - }); + if (result.length === 0) { + return Err(new Error("Participant not found")); + } + + const participant = await convert(result[0]); + + return Ok(participant); +} - return Ok(challenge); -}; +export const findById = async ( + id: number, +): Promise> => { + + const result = await db + .select() + .from(participants) + .where(eq(participants.id, id)); + + if (result.length === 0) { + return Err(new Error("Participant not found")); + } + + const participant = await convert(result[0]); + + return Ok(participant); +} + +export const convert = async ( + result: any, +): Promise => { + const participant = new Participant({ + id: result.id, + uuid: result.uuid, + email: result.email, + }); + return participant; +} export const findByEmail = async ( email?: string, diff --git a/app/challenges-platform/services/submissions-service.ts b/app/challenges-platform/services/submissions-service.ts index c45f63b..3a098bf 100644 --- a/app/challenges-platform/services/submissions-service.ts +++ b/app/challenges-platform/services/submissions-service.ts @@ -1,31 +1,64 @@ +import { eq } from "drizzle-orm"; import { Ok, Err, Result } from "ts-results"; import { db } from "../../../db"; import { submissions } from "../../../db/schema"; import { uuid } from "../../../app/common"; -import { Submission } from "../models"; -import { ChallengesService, ParticipantsService } from "../services"; +import { Challenge, Participant, Submission } from "../models"; +import { + AccessibleChallengesService, + ChallengesService, + ParticipantsService, +} from "../services"; import { challengesPlatform } from ".."; -export const create = async ( - challengeId: string, - participantId: string, +export const findByUuid = async ( + id: string, type: string = "base", - metadata?: any, ): Promise> => { - const challengeResult = await ChallengesService.findByUuid(challengeId); + if (!uuid.isValid(id)) { + return Err(new Error("Invalid UUID")); + } + + const result = await db + .select() + .from(submissions) + .where(eq(submissions.uuid, id)); + + if (result.length === 0) { + return Err(new Error("Submission not found")); + } + + const record = result[0]; + if (!record.challengeId || !record.participantId) { + return Err(new Error("Invalid submission")); + } + + const challengeResult = await ChallengesService.findById(record.challengeId); if (!challengeResult.ok) { return Err(new Error("Failed to find challenge")); } - if (challengeResult.val.deleted === true) { - return Err(new Error("Challenge is deleted")); - } - const participantResult = await ParticipantsService.findByUuid(participantId); + + const participantResult = await ParticipantsService.findById(record.participantId); if (!participantResult.ok) { return Err(new Error("Failed to find participant")); } - // TODO: Validate that the participant is allowed to submit this challenge (available challenges) - // throw new Error("Participant is not allowed to submit this challenge") if not allowed (use participant-service) + const transformer = challengesPlatform.findTransformer(type); + const submission = transformer.newSubmission(result[0], challengeResult.val, participantResult.val); + + return Ok(submission); +} + +export const create = async ( + challengeId: string, + participantId: string, + type: string = "base", + metadata?: any, +): Promise> => { + const result = await beforeCreate(challengeId, participantId); + if (!result.ok) return result; + + const [challenge, participant] = result.val; // TODO: switch on submission.challenge.evaluation // if submission.challenge.evaluation is MANUAL, than save it to the database @@ -46,18 +79,51 @@ export const create = async ( .insert(submissions) .values({ uuid: id.toString(), - challengeId: challengeResult.val.id, - participantId: participantResult.val.id, + challengeId: challenge.id, + participantId: participant.id, }) .returning(); const submission = transformer.newSubmission( result[0], - challengeResult.val, - participantResult.val, + challenge, + participant, ); return Ok(submission); } catch (e) { return Err(new Error("Failed to create submission")); } }; + +//private +const beforeCreate = async ( + challengeId: string, + participantId: string, +): Promise> => { + const challengeResult = await ChallengesService.findByUuid(challengeId); + if (!challengeResult.ok) { + return Err(new Error("Failed to find challenge")); + } + + const participantResult = await ParticipantsService.findByUuid(participantId); + if (!participantResult.ok) { + return Err(new Error("Failed to find participant")); + } + + const countResult = await AccessibleChallengesService.count( + challengeResult.val, + participantResult.val, + ); + if (!countResult.ok) { + return Err(new Error("Failed to count accessible challenges")); + } + + const count = countResult.val; + if (count === 0) { + return Err( + new Error("Participant is not allowed to submit this challenge"), + ); + } + + return Ok([challengeResult.val, participantResult.val]); +}; diff --git a/test/challenges-platform/services/challenges-service.test.ts b/test/challenges-platform/services/challenges-service.test.ts index aa47990..449247f 100644 --- a/test/challenges-platform/services/challenges-service.test.ts +++ b/test/challenges-platform/services/challenges-service.test.ts @@ -2,6 +2,8 @@ import { ChallengesService } from "../../../app/challenges-platform"; import { challengeFactory } from "../factories/challenge-factory"; import { CustomTransformer, CustomChallenge } from "../custom-transformer"; import { challengesPlatform } from "../../../app/challenges-platform"; +import { desc } from "drizzle-orm"; +import { uuid } from "../../../app/common"; describe("ChallengesService", () => { describe("findByUuid", () => { @@ -26,8 +28,43 @@ describe("ChallengesService", () => { expect(result.val.points).toBe(100); }); }); + + describe("when there is no record", () => { + it("returns an error", async () => { + const testUuid = uuid.create(); + const result = await ChallengesService.findByUuid(testUuid); + + expect(result.err).toBe(true); + expect(result.val.toString()).toBe("Error: Challenge not found"); + }); + }); }); + describe("findById", () => { + describe("when there is an existing record", () => { + it("returns the challenge", async () => { + const challenge = await challengeFactory(); + + const result = await ChallengesService.findById(challenge.id); + + if (!result.ok) fail("Expected result to be Ok"); + expect(result.val.title).toBe("Test Challenge"); + expect(result.val.body).toBe("This is a test challenge"); + expect(result.val.points).toBe(100); + }); + }); + + describe("when there is no record", () => { + it("returns an error", async () => { + const testId = -1; + const result = await ChallengesService.findById(testId); + + expect(result.err).toBe(true); + expect(result.val.toString()).toBe("Error: Challenge not found"); + }); + }); + }); + describe("create", () => { it("succesfully creates a challenge", async () => { const title = "Test Challenge"; diff --git a/test/challenges-platform/services/participants-service.test.ts b/test/challenges-platform/services/participants-service.test.ts index 9121ff7..66e8885 100644 --- a/test/challenges-platform/services/participants-service.test.ts +++ b/test/challenges-platform/services/participants-service.test.ts @@ -1,5 +1,6 @@ import { ParticipantsService } from "../../../app/challenges-platform"; import { participantFactory } from "../factories/participant-factory"; +import { uuid } from "../../../app/common"; describe("ParticipantsService", () => { describe("create", () => { @@ -16,6 +17,15 @@ describe("ParticipantsService", () => { }); describe("findByUuid", () => { + describe("when the id is invalid", () => { + it("returns an error", async () => { + const result = await ParticipantsService.findByUuid("invalid-id"); + + expect(result.err).toBe(true); + expect(result.val.toString()).toBe("Error: Invalid UUID"); + }); + }); + describe("when there is an existing record", () => { it("returns the participant", async () => { const participant = await participantFactory(); @@ -25,6 +35,38 @@ describe("ParticipantsService", () => { expect(result.val.email).toBe(participant.email); }); }); + + describe("when there is no record", () => { + it("returns an error", async () => { + const testUuid = uuid.create(); + const result = await ParticipantsService.findByUuid(testUuid); + + expect(result.err).toBe(true); + expect(result.val.toString()).toBe("Error: Participant not found"); + }); + }); + }); + + describe("findById", () => { + describe("when there is an existing record", () => { + it("returns the participant", async () => { + const participant = await participantFactory(); + const result = await ParticipantsService.findById(participant.id); + + if (!result.ok) fail("Expected result to be Ok"); + expect(result.val.email).toBe(participant.email); + }); + }); + + describe("when there is no record", () => { + it("returns an error", async () => { + const testId = -1; + const result = await ParticipantsService.findById(testId); + + expect(result.err).toBe(true); + expect(result.val.toString()).toBe("Error: Participant not found"); + }); + }); }); describe("findByEmail", () => { diff --git a/test/challenges-platform/services/submissions-service.test.ts b/test/challenges-platform/services/submissions-service.test.ts index a17a1ba..735d5a4 100644 --- a/test/challenges-platform/services/submissions-service.test.ts +++ b/test/challenges-platform/services/submissions-service.test.ts @@ -4,8 +4,47 @@ import { } from "../../../app/challenges-platform"; import { challengeFactory } from "../factories/challenge-factory"; import { participantFactory } from "../factories/participant-factory"; +import { uuid } from "../../../app/common"; describe("SubmissionService", () => { + describe("findByUuid", () => { + describe("when the id is invalid", () => { + it("returns an error", async () => { + const result = await SubmissionService.findByUuid("invalid-id"); + + expect(result.err).toBe(true); + expect(result.val.toString()).toBe("Error: Invalid UUID"); + }); + }); + describe("when there is an existing record", () => { + it("returns the submission", async () => { + const challenge = await challengeFactory(); + const participant = await participantFactory(); + + const submission = await SubmissionService.create( + challenge.uuid, + participant.uuid, + ); + if (!submission.ok) fail("Expected submission to be Ok"); + + const result = await SubmissionService.findByUuid(submission.val.uuid); + + if (!result.ok) fail("Expected result to be Ok"); + expect(result.val.challenge.id).toBe(challenge.id); + expect(result.val.participant.id).toBe(participant.id); + }); + }); + describe("when there is no record", () => { + it("returns an error", async () => { + const testUuid = uuid.create(); + const result = await SubmissionService.findByUuid(testUuid); + + expect(result.err).toBe(true); + expect(result.val.toString()).toBe("Error: Submission not found"); + }); + }); + }); + describe("create", () => { describe("when challenge and participant exist", () => { it("succesfully creates a submission", async () => {