Skip to content

Commit

Permalink
Create an endpoint to add new schools
Browse files Browse the repository at this point in the history
  • Loading branch information
Dlurak committed Mar 29, 2024
1 parent 4ff36d4 commit cd41e4b
Show file tree
Hide file tree
Showing 8 changed files with 228 additions and 36 deletions.
35 changes: 35 additions & 0 deletions dbschema/default.esdl
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ module default {
default := datetime_current();
readonly := true;
};

classes := .<students[is Class];
}

type RefreshToken {
Expand All @@ -31,4 +33,37 @@ module default {
readonly := true;
};
}

type School {
required name: str {
constraint exclusive;
};
required description: str;

required created: datetime {
default := datetime_current();
readonly := true;
};

multi classes: Class {
constraint exclusive;
};
}

type Class {
required name: str;

multi students: User {
joinedAt: datetime {
default := datetime_current();
readonly := true;
};
};

single school := .<classes[is School];
required created: datetime {
default := datetime_current();
readonly := true;
};
}
}
33 changes: 33 additions & 0 deletions dbschema/migrations/00006.edgeql
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
CREATE MIGRATION m14j4szqlr74t67qbfcb55ukpjenf6n7bsg444albnz6y3ca3m7nsq
ONTO m12drjaftq6ko7ez5n75r6gmuid4nzrxufl7pnyv5cx52zaglhqt7q
{
CREATE TYPE default::Class {
CREATE MULTI LINK students: default::User {
CREATE PROPERTY joinedAt: std::datetime {
SET default := (std::datetime_current());
SET readonly := true;
};
};
CREATE REQUIRED PROPERTY created: std::datetime {
SET default := (std::datetime_current());
SET readonly := true;
};
CREATE REQUIRED PROPERTY name: std::str;
};
CREATE TYPE default::School {
CREATE MULTI LINK classes: default::Class {
CREATE CONSTRAINT std::exclusive;
};
CREATE REQUIRED PROPERTY created: std::datetime {
SET default := (std::datetime_current());
SET readonly := true;
};
CREATE REQUIRED PROPERTY description: std::str;
CREATE REQUIRED PROPERTY name: std::str {
CREATE CONSTRAINT std::exclusive;
};
};
ALTER TYPE default::Class {
CREATE SINGLE LINK school := (.<classes[IS default::School]);
};
};
7 changes: 7 additions & 0 deletions dbschema/migrations/00007.edgeql
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
CREATE MIGRATION m1psgxwipft63xhdvv24e4bboazf26v6doqseu3nyzyoa3oecmjp4q
ONTO m14j4szqlr74t67qbfcb55ukpjenf6n7bsg444albnz6y3ca3m7nsq
{
ALTER TYPE default::User {
CREATE LINK classes := (.<students[IS default::Class]);
};
};
28 changes: 28 additions & 0 deletions src/constants/documentation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { VERSION } from "./general";

/**
* The options for the swagger plugin
* It can't be a standalone module, but this is possible
*/
export const DOCUMENTATION_OPTIONS = {
documentation: {
info: {
title: "Dlool API",
license: {
name: "GPL-3.0",
url: "https://www.gnu.org/licenses/gpl-3.0.html",
},
version: VERSION,
},
externalDocs: {
description:
"The Dlool documentation for general information and usage of the frontend.",
url: "https://dlool.me/documentation",
},
tags: [
{ name: "App", description: "General app information" },
{ name: "Auth", description: "Authentication endpoints" },
{ name: "User", description: "User information endpoints" },
],
},
};
20 changes: 20 additions & 0 deletions src/constants/responses.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,25 @@
import { responseBuilder } from "utils/response";

/**
* A response indicating that the user is not authorized to access the resource
* Use with `401 Unauthorized`
*/
export const UNAUTHORIZED = responseBuilder("error", {
error: "Unauthorized",
});

/**
* A response indicating that an error occurred while writing to the database
* Use with `500 Internal Server Error`
*/
export const DATABASE_WRITE_FAILED = responseBuilder("error", {
error: "An error occurred while writing to the database",
});

/**
* A response indicating that an error occurred while reading from the database
* Use with `500 Internal Server Error`
*/
export const DATABASE_READ_FAILED = responseBuilder("error", {
error: "An error occurred while reading from the database",
});
28 changes: 4 additions & 24 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,39 +1,19 @@
import { swagger } from "@elysiajs/swagger";
import { DOCUMENTATION_OPTIONS } from "constants/documentation";
import { VERSION } from "constants/general";
import { Elysia } from "elysia";
import { accessTokenRouter } from "routes/auth/accessToken";
import { refreshTokenRouter } from "routes/auth/refreshToken";
import { registerRouter } from "routes/auth/register";
import { schoolRouter } from "routes/school";
import { userInfoRouter } from "routes/user/info";
import { edgedb } from "../dbschema/edgeql-js/imports";

export const client = edgedb.createClient();

const app = new Elysia()
.use(
swagger({
documentation: {
info: {
title: "Dlool API",
license: {
name: "GPL-3.0",
url: "https://www.gnu.org/licenses/gpl-3.0.html",
},
version: VERSION,
},
externalDocs: {
description:
"The Dlool documentation for general information and usage of the frontend.",
url: "https://dlool.me/documentation",
},
tags: [
{ name: "App", description: "General app information" },
{ name: "Auth", description: "Authentication endpoints" },
{ name: "User", description: "User information endpoints" },
],
},
}),
)
.use(swagger(DOCUMENTATION_OPTIONS))
.use(schoolRouter)
.get(
"/",
() => ({
Expand Down
84 changes: 84 additions & 0 deletions src/routes/school/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import e from "@edgedb";
import {
DATABASE_READ_FAILED,
DATABASE_WRITE_FAILED,
UNAUTHORIZED,
} from "constants/responses";
import Elysia, { t } from "elysia";
import { HttpStatusCode } from "elysia-http-status-code";
import { client } from "index";
import { auth } from "plugins/auth";
import { promiseResult } from "utils/errors";
import { responseBuilder } from "utils/response";

export const schoolRouter = new Elysia({ prefix: "/school" })
.use(auth)
.use(HttpStatusCode())
.post(
"/",
async ({ auth, body, set, httpStatus }) => {
if (!auth.isAuthorized) {
set.status = httpStatus.HTTP_401_UNAUTHORIZED;
return UNAUTHORIZED;
}

const countQuery = e.count(
e.select(e.School, (s) => ({
filter: e.op(s.name, "=", body.name),
})),
);
const count = await promiseResult(() => countQuery.run(client));

if (count.status === "error") {
set.status = httpStatus.HTTP_500_INTERNAL_SERVER_ERROR;
return DATABASE_READ_FAILED;
}
if (count.data >= 1) {
set.status = httpStatus.HTTP_400_BAD_REQUEST;
return responseBuilder("error", {
error: `A school with the name ${body.name} already exists`,
});
}

const createSchoolQuery = e.insert(e.School, {
name: body.name,
description: body.description,
});
const result = await promiseResult(() => createSchoolQuery.run(client));

if (result.status === "error") {
set.status = httpStatus.HTTP_500_INTERNAL_SERVER_ERROR;
return DATABASE_WRITE_FAILED;
}

set.status = httpStatus.HTTP_201_CREATED;
return responseBuilder("success", {
message: `Successfully created school ${body.name}`,
data: null,
});
},
{
body: t.Object({
name: t.String({
minLength: 1,
description: "The name of the school",
examples: ["Dlool High School", "Humboldt Gymnasium", "Hogwarts"],
}),
description: t.String({
minLength: 1,
description:
"A description of the school, this is only so other users can distinguish between schools with similar names",
examples: [
"The Dlool High School in Chicago",
"Das Humboldt Gymnasium in Köln",
"Hogwarts straight out of the Harry Potter books",
],
}),
}),
detail: {
description:
"Create a new school, this requires the user to be authenthicated",
tags: ["school"],
},
},
);
29 changes: 17 additions & 12 deletions src/routes/user/me.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,22 @@ import { auth } from "plugins/auth";
export const userOwnInfoRouter = new Elysia({ prefix: "/me" })
.use(auth)
.use(HttpStatusCode())
.get("/", ({ auth, set, httpStatus }) => {
if (!auth.isAuthorized) {
set.status = httpStatus.HTTP_401_UNAUTHORIZED;
return UNAUTHORIZED;
}
.get(
"/",
({ auth, set, httpStatus }) => {
if (!auth.isAuthorized) {
set.status = httpStatus.HTTP_401_UNAUTHORIZED;
return UNAUTHORIZED;
}

// An authorized request to that endpoint will give a lot of info
set.redirect = `/user/info?username=${auth.username}`;
}, {
detail: {
description: "Get information about yourself. For this the user needs to authenthicated",
tags: ["User"],
// An authorized request to that endpoint will give a lot of info
set.redirect = `/user/info?username=${auth.username}`;
},
});
{
detail: {
description:
"Get information about yourself. For this the user needs to authenthicated",
tags: ["User"],
},
},
);

0 comments on commit cd41e4b

Please sign in to comment.