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

Commit

Permalink
feat: huge tutorials commit
Browse files Browse the repository at this point in the history
  • Loading branch information
BrandonHowe committed Jul 8, 2020
1 parent 5706dbd commit aa4377a
Show file tree
Hide file tree
Showing 18 changed files with 198 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ export const up = async (knex: Knex) => {
table.string("email").notNullable();
table.string("password").notNullable();
table.string("role").notNullable();
table.integer("rank").notNullable();
table.integer("exp").notNullable();
table.string("description");
table.specificType("tutorials", "INT[]").notNullable();
});
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ export const up = async (knex: Knex) => {
table.increments("id");
table.string("text").notNullable();
table.boolean("ordered");
table.boolean("tutorial");
table.integer("difficulty").notNullable();
table.integer("author").notNullable();
});
};

Expand Down
15 changes: 10 additions & 5 deletions packages/api/db/seeds/examples/users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ interface Users {
password: string;
role?: string | null;
description?: string | null;
rank: number;
exp: number;
tutorials: number[];
}

export default [
Expand All @@ -14,30 +15,34 @@ export default [
password: "UserPass",
role: "admin",
description: null,
rank: 8
exp: 8,
tutorials: []
},
{
name: "MKGUN3",
email: "[email protected]",
password: "MaoGay",
role: "member",
description: null,
rank: 4
exp: 4,
tutorials: []
},
{
name: "NotAUser",
email: "[email protected]",
password: "nothing",
role: "member",
description: null,
rank: 2
exp: 2,
tutorials: []
},
{
name: "Guy2",
email: "[email protected]",
password: "JustAGuy",
role: "member",
description: "But hey, I have a description!",
rank: 0
exp: 0,
tutorials: []
}
] as readonly Users[];
2 changes: 2 additions & 0 deletions packages/api/src/modules/apiRouter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ import Router from "./Router";
import authRouter from "./auth/router";
import gamesRouter from "./games/router";
import textsRouter from "./texts/router";
import tutorialsRouter from "./tutorials/router";
import usersRouter from "./users/router";

const apiRouter = new Router({ prefix: "/api" });

apiRouter.use(authRouter);
apiRouter.use(gamesRouter);
apiRouter.use(textsRouter);
apiRouter.use(tutorialsRouter);
apiRouter.use(usersRouter);

export default apiRouter.routes();
2 changes: 1 addition & 1 deletion packages/api/src/modules/auth/router.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const agent = request.agent(server);

describe("Auth router", () => {
before(async function() {
this.timeout(5000);
this.timeout(20000);
await knex.migrate.latest();
await knex.seed.run();
});
Expand Down
16 changes: 14 additions & 2 deletions packages/api/src/modules/texts/actions/addText.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
import knex from "../../../../db/knex";
import Text from "../types/Text";

export default async (text: string, ordered = true) => {
await knex<Text>("texts").insert({ text, ordered });
export default async (
text: string,
difficulty: number,
author: number,
ordered = true,
tutorial = false
) => {
await knex<Text>("texts").insert({
text,
difficulty,
author,
ordered,
tutorial
});
};
7 changes: 5 additions & 2 deletions packages/api/src/modules/texts/actions/getRandomText.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@ import knex from "../../../../db/knex";

export default async (typed = false, ordered = false) => {
if (typed) {
const texts = await knex<Text>("texts").where({ ordered });
const texts = await knex<Text>("texts").where({
ordered,
tutorial: false
});
return texts[Math.floor(Math.random() * texts.length)];
} else {
const texts = await knex<Text>("texts");
const texts = await knex<Text>("texts").where({ tutorial: false });
return texts[Math.floor(Math.random() * texts.length)];
}
};
8 changes: 7 additions & 1 deletion packages/api/src/modules/texts/router.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,21 @@ const agent = request.agent(server);

const newText = {
text: "This is a text!",
difficulty: 1,
author: 1,
tutorial: false,
ordered: true
};

const newRandomText = {
text: "this is a text",
difficulty: 1,
author: 1,
tutorial: false,
ordered: false
};

describe("Users routes", async () => {
describe("Texts routes", async () => {
// We don't need to rerun migrations or seeds because we did in the auth route

it("Adds texts", async () => {
Expand Down
5 changes: 3 additions & 2 deletions packages/api/src/modules/texts/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ import getRandomText from "./actions/getRandomText";
const router = new Router({ prefix: "/texts" });

router.post("/addText", requireAdmin(), async (ctx, next) => {
const { text, ordered } = ctx.request.body;
await addText(text, ordered);
const { text, difficulty, ordered, tutorial } = ctx.request.body;
const { user } = ctx.session!;
await addText(text, difficulty, user, ordered, tutorial);
ctx.status = 201;
ctx.body = "Successfully added text";
await next();
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
Expand Up @@ -2,4 +2,7 @@ export default interface Text {
id: number;
text: string;
ordered: boolean;
tutorial: boolean;
difficulty: number;
author: number;
}
25 changes: 25 additions & 0 deletions packages/api/src/modules/tutorials/actions/completeTutorial.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Tutorial } from "../types/Tutorial";
import knex from "../../../../db/knex";
import User from "../../users/types/User";

export default async (tutorialid: number, userid: number) => {
const tutorial = await knex<Tutorial>("texts")
.where({
id: tutorialid,
tutorial: true
})
.first();
console.log(tutorial);
const user = await knex<User>("users")
.where({
id: userid
})
.first();
if (!tutorial || !user) return false;
await knex("users")
.update({
tutorials: knex.raw("array_append(tutorials, ?)", [tutorialid])
})
.where({ id: userid });
return true;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import Text from "../../texts/types/Text";
import knex from "../../../../db/knex";

export default async () => {
const texts = await knex<Text>("texts").where({
tutorial: true
});
return texts[Math.floor(Math.random() * texts.length)];
};
9 changes: 9 additions & 0 deletions packages/api/src/modules/tutorials/actions/getTutorial.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Tutorial } from "../types/Tutorial";
import knex from "../../../../db/knex";

export default async (id: number) => {
const tutorial = await knex<Tutorial>("texts")
.where({ id, tutorial: true })
.first();
return tutorial || null;
};
56 changes: 56 additions & 0 deletions packages/api/src/modules/tutorials/router.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import request from "supertest";
import { expect } from "chai";

import { server } from "../../index";
import addText from "../texts/actions/addText";
import User from "../users/types/User";
import knex from "../../../db/knex";

const agent = request.agent(server);

describe("Tutorials routes", async () => {
// We don't need to rerun migrations or seeds because we did in the auth route

it("Completes a tutorial", async () => {
await agent.post("/api/auth/login").send({
email: "[email protected]",
password: "MaoGay"
});

await addText("blah", 1, 1, false, true);
const response = await agent
.post(`/api/tutorials/completeTutorial/`)
.send({ id: 3 })
.set("Accept", "application/text")
.expect("Content-Type", /text/)
.expect(200);

expect(response.text).to.deep.equal("Successfully completed tutorial");
});

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

expect(response.body).to.deep.equal({
status: 400,
message: "That tutorial does not exist!"
});
});

it("Gets tutorials completed for the user", async () => {
const user = await knex<User>("users")
.where({
email: "[email protected]"
})
.first();

console.log(user);

expect(user?.tutorials).to.deep.equal([3]);
});
});
42 changes: 42 additions & 0 deletions packages/api/src/modules/tutorials/router.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import Router from "../Router";
import { requireAuthenticated } from "../auth/middleware/requireAuthenticated";
import completeTutorial from "./actions/completeTutorial";
import { HttpError } from "../../common/error/classes/httpError";
import getTutorial from "./actions/getTutorial";
import getRandomTutorial from "./actions/getRandomTutorial";

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

router.post("/completeTutorial", requireAuthenticated(), async (ctx, next) => {
const { id } = ctx.request.body;
const { user } = ctx.session!;
const success = await completeTutorial(id, user);
if (!success) {
throw new HttpError(400, "That tutorial does not exist!");
}
ctx.status = 200;
ctx.body = "Successfully completed tutorial";
await next();
});

router.get("/:id", async (ctx, next) => {
const { id } = ctx.params;
console.log(`Received ${id}`);
const tutorial = getTutorial(id);
if (!tutorial) {
throw new HttpError(400, "That tutorial does not exist!");
}
console.log(tutorial);
ctx.status = 200;
ctx.body = tutorial;
await next();
});

router.get("/random", async (ctx, next) => {
const tutorial = getRandomTutorial();
ctx.status = 200;
ctx.body = tutorial;
await next();
});

export default router.routes();
3 changes: 3 additions & 0 deletions packages/api/src/modules/tutorials/types/Tutorial.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import Text from "../../texts/types/Text";

export type Tutorial = Text;
3 changes: 2 additions & 1 deletion packages/api/src/modules/users/actions/createUser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ export const createUser = async (
...user,
role: "member",
password: encryptedPassword,
rank: 0
exp: 0,
tutorials: []
},
"*"
)
Expand Down
3 changes: 2 additions & 1 deletion packages/api/src/modules/users/types/User.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ export default interface User {
email: string;
name: string;
password: string;
rank: number;
exp: number;
role?: string | null;
description?: string | null;
tutorials: number[];
}

0 comments on commit aa4377a

Please sign in to comment.