Skip to content

Commit

Permalink
Add an endpoint to create a new note
Browse files Browse the repository at this point in the history
  • Loading branch information
Dlurak committed Apr 11, 2024
1 parent b51fbef commit 649b6a2
Show file tree
Hide file tree
Showing 7 changed files with 168 additions and 0 deletions.
25 changes: 25 additions & 0 deletions dbschema/default.esdl
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,31 @@ module default {
};
}

scalar type EditScope extending enum<Self, Class, School>;

type Note {
required class: Class {
readonly := true;
};

required title: str;
summary: str;

multi tags: Tag {
on target delete allow;
};
priority: Priority;

required editScope: EditScope {
default := EditScope.Self;
};

multi updates: Change {
on target delete allow;
on source delete delete target;
};
}

type Tag {
required tag: str;
color: str;
Expand Down
28 changes: 28 additions & 0 deletions dbschema/migrations/00019.edgeql
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
CREATE MIGRATION m1mzn3ehkgkjrnujdwfxjhe5optsk3cmyqkdagwev6vq2p5a4xxbsa
ONTO m1xsbfzwxprtxttepfmhlwzsayxv3keyigzrfa2o2zk3g4zttajubq
{
ALTER TYPE default::Calendar {
ALTER LINK tags {
ON TARGET DELETE ALLOW;
};
};
CREATE SCALAR TYPE default::EditScope EXTENDING enum<Self, Class, School>;
CREATE TYPE default::Note {
CREATE REQUIRED LINK class: default::Class {
SET readonly := true;
};
CREATE MULTI LINK tags: default::Tag {
ON TARGET DELETE ALLOW;
};
CREATE MULTI LINK updates: default::Change {
ON SOURCE DELETE DELETE TARGET;
ON TARGET DELETE ALLOW;
};
CREATE PROPERTY editScope: default::EditScope {
SET default := (default::EditScope.Self);
};
CREATE PROPERTY priority: default::Priority;
CREATE PROPERTY summary: std::str;
CREATE REQUIRED PROPERTY title: std::str;
};
};
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { registerRouter } from "routes/auth/register";
import { calendarRouter } from "routes/calendar";
import { classRouter } from "routes/classes";
import { moderationRouter } from "routes/moderation";
import { noteRouter } from "routes/notes";
import { schoolRouter } from "routes/school";
import { tagRouter } from "routes/tags";
import { deleteUser } from "routes/user/delete";
Expand All @@ -26,6 +27,7 @@ const app = new Elysia()
.use(assignmentsRouter)
.use(calendarRouter)
.use(tagRouter)
.use(noteRouter)
.get(
"/",
() => ({
Expand Down
83 changes: 83 additions & 0 deletions src/routes/notes/create.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import e from "@edgedb";
import { 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 { fallback } from "utils/arrays/fallback";
import { promiseResult } from "utils/errors";
import { usersClassBySchoolAndName } from "utils/queries/class";
import { userByUsername } from "utils/queries/user";
import { responseBuilder } from "utils/response";

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

const query = e.insert(e.Note, {
title: body.title,
class: usersClassBySchoolAndName({
schoolName: body.school,
className: body.class,
username: auth.username,
}),

summary: body.summary,
priority: body.priority,
tags: e.select(e.Tag, (t) => ({
filter: e.op(
e.op(
e.op(t.class.name, "=", body.class),
"and",
e.op(t.class.school.name, "=", body.school),
),
"and",
e.op(t.tag, "in", e.set(...fallback(body.tags || [], [""]))),
),
})),
editScope: body.editScope,

updates: e.insert(e.Change, { user: userByUsername(auth.username) }),
});
const result = await promiseResult(() => query.run(client));

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

set.status = httpStatus.HTTP_201_CREATED;
return responseBuilder("success", {
message: "Successfully created note",
data: null,
});
},
{
body: t.Object({
class: t.String({ minLength: 1 }),
school: t.String({ minLength: 1 }),
title: t.String({ minLength: 1 }),
summary: t.Optional(t.String({ minLength: 1 })),
tags: t.Optional(t.Array(t.String({ minLength: 1 }))),
priority: t.Optional(
t.Union([
t.Literal("Critical"),
t.Literal("High"),
t.Literal("Medium"),
t.Literal("Low"),
t.Literal("Minimal"),
]),
),
editScope: t.Optional(
t.Union([t.Literal("Self"), t.Literal("Class"), t.Literal("School")]),
),
}),
},
);
4 changes: 4 additions & 0 deletions src/routes/notes/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import Elysia from "elysia";
import { createNote } from "./create";

export const noteRouter = new Elysia({ prefix: "/notes" }).use(createNote);
20 changes: 20 additions & 0 deletions src/utils/queries/class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ interface ClassBySchoolAndNameProps {
className: string;
}

type UsersClassBySchoolAndNameProps = ClassBySchoolAndNameProps & {
username: string;
};

export const classBySchoolAndName = ({
schoolName,
className,
Expand All @@ -17,3 +21,19 @@ export const classBySchoolAndName = ({
),
}));
};

export const usersClassBySchoolAndName = (
props: UsersClassBySchoolAndNameProps,
) => {
return e.select(e.Class, (c) => ({
filter_single: e.op(
e.op(
e.op(c.name, "=", props.className),
"and",
e.op(c.school.name, "=", props.schoolName),
),
"and",
e.op(props.username, "in", c.students.username),
),
}));
};
6 changes: 6 additions & 0 deletions src/utils/queries/user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import e from "@edgedb";

export const userByUsername = (username: string) =>
e.select(e.User, (u) => ({
filter_single: e.op(u.username, "=", username),
}));

0 comments on commit 649b6a2

Please sign in to comment.