Skip to content

Commit

Permalink
package upgrades, refactoring, and fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
cdleveille committed Jun 7, 2024
1 parent c8c08ab commit a1800c5
Show file tree
Hide file tree
Showing 16 changed files with 195 additions and 263 deletions.
2 changes: 0 additions & 2 deletions .eslintignore

This file was deleted.

28 changes: 0 additions & 28 deletions .eslintrc.json

This file was deleted.

Binary file modified bun.lockb
Binary file not shown.
19 changes: 19 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// @ts-check
import eslint from "@eslint/js";
import tseslint from "typescript-eslint";

export default [
{
ignores: ["node_modules/"]
},
...tseslint.config(eslint.configs.recommended, ...tseslint.configs.recommended, {
languageOptions: {
ecmaVersion: "latest",
sourceType: "module"
},
rules: {
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/no-var-requires": "off"
}
})
];
30 changes: 13 additions & 17 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
{
"name": "discit-api",
"version": "2.1.0",
"version": "2.2.0",
"description": "RESTful API for disc golf discs.",
"type": "module",
"module": "src/index.ts",
"main": "src/index.ts",
"scripts": {
"lint": "bunx eslint --ext .js,.jsx,.ts,.tsx . && bunx prettier --check .",
"fix": "bunx eslint --fix --ext .js,.jsx,.ts,.tsx . && bunx prettier --write .",
"lint": "bunx eslint . && bunx prettier --check .",
"fix": "bunx eslint --fix . && bunx prettier --write .",
"compose:up": "docker compose -f \".discit-dev-container/docker-compose.yml\" up -d --build",
"dev": "bun --watch src/index.ts",
"start": "bun src/index.ts"
Expand All @@ -23,22 +22,19 @@
},
"homepage": "https://github.com/cdleveille/discit-api#readme",
"dependencies": {
"@elysiajs/cors": "^0.6.0",
"@elysiajs/swagger": "^0.6.2",
"elysia": "^0.6.23",
"elysia-helmet": "^1.0.1",
"mongoose": "^7.5.1",
"@elysiajs/cors": "^1.0.2",
"@elysiajs/swagger": "^1.0.5",
"elysia": "^1.0.22",
"elysia-helmet": "^1.0.2",
"mongoose": "^8.4.1",
"uuid": "^9.0.1"
},
"devDependencies": {
"@types/uuid": "^9.0.4",
"@typescript-eslint/eslint-plugin": "^6.7.0",
"@typescript-eslint/parser": "^6.7.0",
"@types/uuid": "^9.0.8",
"bun-types": "latest",
"eslint": "^8.49.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-prettier": "^5.0.0",
"prettier": "^3.0.3",
"typescript": "^5.2.2"
"eslint": "^9.4.0",
"prettier": "^3.3.1",
"typescript": "^5.4.5",
"typescript-eslint": "^7.12.0"
}
}
181 changes: 73 additions & 108 deletions src/controllers/bag.ts
Original file line number Diff line number Diff line change
@@ -1,135 +1,100 @@
import { Elysia } from "elysia";

import { errorResponse, projection } from "@helpers";
import { BadRequestError, buildBagResponse, NotFoundError, parseBody, projection } from "@helpers";
import { Bag, Disc } from "@models";
import { assertIsRequestAuthorized } from "@services";

export const initBagRoutes = (app: Elysia) => {
/* Get all bags (optionally filter by user id) */
app.get("/bag", async ({ set, request, query }) => {
try {
assertIsRequestAuthorized(request);
const { user_id } = query as Record<string, string>;
const bags = await (user_id ? Bag.find({ user_id }, projection) : Bag.find({}, projection));
if (!bags || bags.length === 0) return [];
return bags;
} catch (error) {
return errorResponse(set, error);
}
app.get("/bag", async ({ request, query }) => {
assertIsRequestAuthorized(request);
const { user_id } = query as Record<string, string>;
const bags = await (user_id ? Bag.find({ user_id }, projection) : Bag.find({}, projection));
if (!bags || bags.length === 0) return [];
return bags;
});

/* Get bag by id */
app.get("/bag/:id", async ({ set, request, params }) => {
try {
assertIsRequestAuthorized(request);
const { id } = params as Record<string, string>;
const bag = await Bag.findOne({ id }, projection);
if (!bag) throw { code: 404, data: "Bag not found." };
return bag;
} catch (error) {
return errorResponse(set, error);
}
app.get("/bag/:id", async ({ request, params }) => {
assertIsRequestAuthorized(request);
const { id } = params as Record<string, string>;
const bag = await Bag.findOne({ id }, projection);
if (!bag) throw new NotFoundError("Bag not found");
return bag;
});

/* Create new bag (bearer auth secured) */
app.post("/bag/create", async ({ set, body, request }) => {
try {
assertIsRequestAuthorized(request);
const { user_id, name } = JSON.parse(body as string) as { user_id: string; name: string };
if (!user_id) throw { code: 400, data: "Required body field missing: user_id" };
if (!name) throw { code: 400, data: "Required body field missing: name" };

if (await Bag.findOne({ user_id, name }))
throw { code: 400, data: "You already have a bag with that name." };

if (name.length < 1) throw { code: 400, data: "Bag name must be at least 1 character." };
if (name.length > 32) throw { code: 400, data: "Bag name must be no more than 32 characters." };

return Bag.create({ user_id, name });
} catch (error) {
return errorResponse(set, error);
}
app.post("/bag/create", async ({ request, body }) => {
assertIsRequestAuthorized(request);
const { user_id, name } = parseBody(body) as {
user_id: string;
name: string;
};
if (!user_id) throw new BadRequestError("Required body field missing: user_id");
if (!name) throw new BadRequestError("Required body field missing: name");
if (await Bag.findOne({ user_id, name })) throw new BadRequestError("You already have a bag with that name");
if (name.length < 1) throw new BadRequestError("Bag name must be at least 1 character");
if (name.length > 32) throw new BadRequestError("Bag name must be no more than 32 characters");
const bag = await Bag.create({ user_id, name });
return buildBagResponse(bag);
});

/* Add disc to bag (bearer auth secured) */
app.put("/bag/add-disc", async ({ set, body, request }) => {
try {
assertIsRequestAuthorized(request);
const { id, disc_id } = JSON.parse(body as string) as { id: string; disc_id: string };
if (!id) throw { code: 400, data: "Required body field missing: id" };
if (!disc_id) throw { code: 400, data: "Required body field missing: disc_id" };

const bag = await Bag.findOne({ id });
if (!bag) throw { code: 404, data: "Bag not found." };

const disc = await Disc.findOne({ id: disc_id });
if (!disc) throw { code: 404, data: "Disc not found." };
if (bag.discs.includes(disc_id)) throw { code: 400, data: "Bag already contains this disc." };

bag.discs.push(disc_id);
return Bag.updateOne({ id }, bag);
} catch (error) {
return errorResponse(set, error);
}
app.put("/bag/add-disc", async ({ request, body }) => {
assertIsRequestAuthorized(request);
const { id, disc_id } = parseBody(body) as { id: string; disc_id: string };
if (!id) throw new BadRequestError("Required body field missing: id");
if (!disc_id) throw new BadRequestError("Required body field missing: disc_id");
const bag = await Bag.findOne({ id });
if (!bag) throw new NotFoundError("Bag not found");
const disc = await Disc.findOne({ id: disc_id });
if (!disc) throw new NotFoundError("Disc not found");
if (bag.discs.includes(disc_id)) throw new BadRequestError("Bag already contains this disc");
bag.discs.push(disc_id);
await Bag.updateOne({ id }, bag);
return buildBagResponse(bag);
});

/* Remove disc from bag (bearer auth secured) */
app.put("/bag/remove-disc", async ({ set, body, request }) => {
try {
assertIsRequestAuthorized(request);
const { id, disc_id } = JSON.parse(body as string) as { id: string; disc_id: string };
if (!id) throw { code: 400, data: "Required body field missing: id" };
if (!disc_id) throw { code: 400, data: "Required body field missing: disc_id" };

const bag = await Bag.findOne({ id });
if (!bag) throw { code: 404, data: "Bag not found." };

const disc = await Disc.findOne({ id: disc_id });
if (!disc) throw { code: 404, data: "Disc not found." };
if (!bag.discs.includes(disc_id)) throw { code: 400, data: "Bag does not contain this disc." };

bag.discs = bag.discs.filter(discId => discId !== disc_id);
return Bag.updateOne({ id }, bag);
} catch (error) {
return errorResponse(set, error);
}
app.put("/bag/remove-disc", async ({ request, body }) => {
assertIsRequestAuthorized(request);
const { id, disc_id } = parseBody(body) as { id: string; disc_id: string };
if (!id) throw new BadRequestError("Required body field missing: id");
if (!disc_id) throw new BadRequestError("Required body field missing: disc_id");
const bag = await Bag.findOne({ id });
if (!bag) throw new NotFoundError("Bag not found");
const disc = await Disc.findOne({ id: disc_id });
if (!disc) throw new NotFoundError("Disc not found");
if (!bag.discs.includes(disc_id)) throw new BadRequestError("Bag does not contain this disc");
bag.discs = bag.discs.filter(discId => discId !== disc_id);
await Bag.updateOne({ id }, bag);
return buildBagResponse(bag);
});

/* Update name of bag (bearer auth secured) */
app.put("/bag/update-name", async ({ set, body, request }) => {
try {
assertIsRequestAuthorized(request);
const { id, name } = JSON.parse(body as string) as { id: string; name: string };
if (!id) throw { code: 400, data: "Required body field missing: id" };
if (!name) throw { code: 400, data: "Required body field missing: name" };

if (name.length < 1) throw { code: 400, data: "Bag name must be at least 1 character." };
if (name.length > 32) throw { code: 400, data: "Bag name must be no more than 32 characters." };

const bag = await Bag.findOne({ id });
if (!bag) throw { code: 404, data: "Bag not found." };

if (await Bag.findOne({ user_id: bag.user_id, name }))
throw { code: 400, data: "You already have a bag with that name." };

bag.name = name;
return Bag.updateOne({ id }, bag);
} catch (error) {
return errorResponse(set, error);
}
app.put("/bag/update-name", async ({ request, body }) => {
assertIsRequestAuthorized(request);
const { id, name } = parseBody(body) as { id: string; name: string };
if (!id) throw new BadRequestError("Required body field missing: id");
if (!name) throw new BadRequestError("Required body field missing: name");
if (name.length < 1) throw new BadRequestError("Bag name must be at least 1 character");
if (name.length > 32) throw new BadRequestError("Bag name must be no more than 32 characters");
const bag = await Bag.findOne({ id });
if (!bag) throw new NotFoundError("Bag not found");
if (await Bag.findOne({ user_id: bag.user_id, name }))
throw new BadRequestError("You already have a bag with that name");
bag.name = name;
await Bag.updateOne({ id }, bag);
return buildBagResponse(bag);
});

/* Delete bag (bearer auth secured) */
app.delete("/bag/delete/:id", async ({ set, params, request }) => {
try {
assertIsRequestAuthorized(request);
const { id } = params as Record<string, string>;
if (!id) throw { code: 400, data: "Required path param missing: id" };
const bag = await Bag.findOne({ id });
if (!bag) throw { code: 404, data: "Bag not found." };
return Bag.deleteOne({ id });
} catch (error) {
return errorResponse(set, error);
}
app.delete("/bag/delete/:id", async ({ request, params }) => {
assertIsRequestAuthorized(request);
const { id } = params as Record<string, string>;
if (!id) throw new BadRequestError("Required path param missing: id");
const bag = await Bag.findOne({ id });
if (!bag) throw new NotFoundError("Bag not found");
return await Bag.deleteOne({ id });
});
};
Loading

0 comments on commit a1800c5

Please sign in to comment.