Skip to content

Commit

Permalink
Add submissions service find by UUID method (#17)
Browse files Browse the repository at this point in the history
* Added submissions service findByUuid method

* linting

* bug fix

* fix failing tests

* linting

* small challenge deletion fix

* Add factory tweak

---------

Co-authored-by: Matthew M-B <[email protected]>
  • Loading branch information
AJaccP and MathyouMB authored Jun 22, 2024
1 parent 3a131ce commit 39e6170
Show file tree
Hide file tree
Showing 8 changed files with 300 additions and 30 deletions.
31 changes: 29 additions & 2 deletions app/challenges-platform/services/challenges-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,39 @@ 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<Result<Challenge, Error>> => {
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<Challenge> => {
const transformer = challengesPlatform.findTransformer(result.type);
const challenge = transformer.newChallenge(result);

return challenge;
};

export const create = async ({
title,
body,
Expand Down
38 changes: 32 additions & 6 deletions app/challenges-platform/services/participants-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,39 @@ 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);
};

export const findById = async (
id: number,
): Promise<Result<Participant, Error>> => {
const result = await db
.select()
.from(participants)
.where(eq(participants.id, id));

if (result.length === 0) {
return Err(new Error("Participant not found"));
}

return Ok(challenge);
const participant = await convert(result[0]);

return Ok(participant);
};

export const convert = async (result: any): Promise<Participant> => {
const participant = new Participant({
id: result.id,
uuid: result.uuid,
email: result.email,
});
return participant;
};

export const findByEmail = async (
Expand Down
110 changes: 92 additions & 18 deletions app/challenges-platform/services/submissions-service.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,70 @@
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<Result<Submission, Error>> => {
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<Result<Submission, Error>> => {
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
Expand All @@ -45,19 +84,54 @@ export const create = async (
const result = await db
.insert(submissions)
.values({
uuid: id,
challengeId: challengeResult.val.id,
participantId: participantResult.val.id,
uuid: id.toString(),
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<Result<[Challenge, Participant], Error>> => {
const challengeResult = await ChallengesService.findByUuid(challengeId);
if (!challengeResult.ok) {
return Err(new Error("Failed to find challenge"));
} else if (challengeResult.val.deleted === true) {
return Err(new Error("Challenge is deleted"));
}

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]);
};
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export const accessibleChallengeFactory = async ({
}: {
challenge?: Challenge;
participant?: Participant;
} = {}): Promise<any> => {
} = {}): Promise<[Challenge, Participant]> => {
const c = challenge || (await challengeFactory());
const p = participant || (await participantFactory());

Expand All @@ -24,4 +24,6 @@ export const accessibleChallengeFactory = async ({
participantId: p.id,
})
.returning();

return [c, p];
};
37 changes: 37 additions & 0 deletions test/challenges-platform/services/challenges-service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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", () => {
Expand All @@ -26,6 +28,41 @@ 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", () => {
Expand Down
42 changes: 42 additions & 0 deletions test/challenges-platform/services/participants-service.test.ts
Original file line number Diff line number Diff line change
@@ -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", () => {
Expand All @@ -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();
Expand All @@ -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", () => {
Expand Down
Loading

0 comments on commit 39e6170

Please sign in to comment.