Skip to content
This repository has been archived by the owner on Jul 31, 2024. It is now read-only.

Commit

Permalink
feat: tutorial requirements
Browse files Browse the repository at this point in the history
  • Loading branch information
BrandonHowe committed Jul 15, 2020
1 parent d562514 commit 8219423
Show file tree
Hide file tree
Showing 10 changed files with 75 additions and 17 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import Knex from "knex";

export const up = async (knex: Knex) =>
knex.schema.table("texts", table => table.jsonb("requirements"));

export const down = async (knex: Knex) =>
knex.schema.table("texts", table => table.dropColumn("requirements"));
6 changes: 4 additions & 2 deletions packages/api/src/modules/texts/actions/addText.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,16 @@ export default async (
difficulty: number,
author: number,
ordered = true,
tutorial = false
tutorial = false,
requirements
) => {
await knex<Text>("texts").insert({
title,
text,
difficulty,
author,
ordered,
tutorial
tutorial,
requirements
});
};
8 changes: 4 additions & 4 deletions packages/api/src/modules/texts/router.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ describe("Texts routes", async () => {
.expect(200);

expect(response.body).to.deep.equal([
{ ...newText, id: 1 },
{ ...newRandomText, id: 2 }
{ ...newText, id: 1, requirements: null },
{ ...newRandomText, id: 2, requirements: null }
]);
});

Expand All @@ -70,8 +70,8 @@ describe("Texts routes", async () => {
.expect(200);

expect([
{ ...newText, id: 1 },
{ ...newRandomText, id: 2 }
{ ...newText, id: 1, requirements: null },
{ ...newRandomText, id: 2, requirements: null }
]).to.deep.include(response.body);
});
});
18 changes: 17 additions & 1 deletion packages/api/src/modules/texts/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { addTextBody } from "./schema/addTextBody";
import { validateSchema } from "../schema/middleware/validateSchema";
import deleteText from "./actions/deleteText";
import editText from "./actions/editText";
import { HttpError } from "../../common/error/classes/httpError";

const router = new Router({ prefix: "/texts" });

Expand All @@ -16,8 +17,23 @@ router.post(
validateSchema(addTextBody, "body"),
async (ctx, next) => {
const { title, text, difficulty, ordered, tutorial } = ctx.request.body;
const requirements = ctx.request.body.requirements;
if (tutorial && !requirements) {
throw new HttpError(
400,
"You need to have requirements for a tutorial!"
);
}
const { user } = ctx.session!;
await addText(title, text, difficulty, user, ordered, tutorial);
await addText(
title,
text,
difficulty,
user,
ordered,
tutorial,
requirements
);
ctx.status = 201;
ctx.body = {
message: "Successfully added text"
Expand Down
3 changes: 3 additions & 0 deletions packages/api/src/modules/texts/types/Text.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
type Requirement = "wpm" | "rawwpm" | "acc";

export default interface Text {
id: number;
title: string;
Expand All @@ -6,4 +8,5 @@ export default interface Text {
tutorial: boolean;
difficulty: number;
author: number;
requirements?: Record<Requirement, number>;
}
12 changes: 12 additions & 0 deletions packages/api/src/modules/tutorials/actions/compareRequirements.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import Requirement from "../types/Requirement";

const typesafeKeys = Object.keys as <T extends object>(o: T) => Array<keyof T>;

export default (
base: Record<Requirement, number>,
head: Record<Requirement, number>
) => {
return typesafeKeys(base)
.map(l => [base[l], head[l]])
.every(l => l[1] > l[0]);
};
13 changes: 11 additions & 2 deletions packages/api/src/modules/tutorials/actions/completeTutorial.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import { Tutorial } from "../types/Tutorial";
import knex from "../../../../db/knex";
import User from "../../users/types/User";
import compareRequirements from "./compareRequirements";
import Requirement from "../types/Requirement";

export default async (tutorialid: number, userid: number) => {
export default async (
tutorialid: number,
userid: number,
requirements: Record<Requirement, number>
) => {
const tutorial = await knex<Tutorial>("texts")
.where({
id: tutorialid,
Expand All @@ -14,7 +20,10 @@ export default async (tutorialid: number, userid: number) => {
id: userid
})
.first();
if (!tutorial || !user) return false;
if (!tutorial || !user) return null;
if (!compareRequirements(tutorial.requirements!, requirements)) {
return false;
}
await knex("users")
.update({
tutorials: knex.raw("array_append(tutorials, ?)", [tutorialid])
Expand Down
12 changes: 7 additions & 5 deletions packages/api/src/modules/tutorials/router.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ describe("Tutorials routes", async () => {
password: "MaoGay"
});

await addText("blah", "blah", 1, 1, false, true);
await addText("blah", "blah", 1, 1, false, true, { wpm: 100 });
const response = await agent
.post(`/api/tutorials/completeTutorial/`)
.send({ id: 3 })
.send({ id: 3, requirements: { wpm: 110 } })
.set("Accept", "application/json")
.expect("Content-Type", /json/)
.expect(200);
Expand All @@ -30,15 +30,17 @@ describe("Tutorials routes", async () => {
);
});

it("Cannot complete a non-existent tutorial", async () => {
it("Cannot complete a tutorial without meeting requirements", async () => {
const response = await agent
.post(`/api/tutorials/completeTutorial/`)
.send({ id: 1 })
.send({ id: 3, requirements: { wpm: 90 } })
.set("Accept", "application/json")
.expect("Content-Type", /json/)
.expect(400);

expect(response.body.message).to.equal("That tutorial does not exist!");
expect(response.body.message).to.equal(
"You do not meet the requirements for this tutorial"
);
});

it("Gets tutorials completed for the user", async () => {
Expand Down
9 changes: 6 additions & 3 deletions packages/api/src/modules/tutorials/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,14 @@ import getAllTutorials from "./actions/getAllTutorials";
const router = new Router({ prefix: "/tutorials" });

router.post("/completeTutorial", requireAuthenticated(), async (ctx, next) => {
const { id } = ctx.request.body;
const { id, requirements } = ctx.request.body;
const { user } = ctx.session!;
const success = await completeTutorial(id, user);
const success = await completeTutorial(id, user, requirements);
if (!success) {
throw new HttpError(400, "That tutorial does not exist!");
throw new HttpError(
400,
"You do not meet the requirements for this tutorial"
);
}
ctx.status = 200;
ctx.body = {
Expand Down
4 changes: 4 additions & 0 deletions packages/api/src/modules/tutorials/types/Requirement.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
type Requirement = "wpm" | "rawwpm" | "acc";

// eslint-disable-next-line no-undef
export default Requirement;

0 comments on commit 8219423

Please sign in to comment.