diff --git a/src/routes/assignments/list.ts b/src/routes/assignments/list.ts index 99a3776..cb6b479 100644 --- a/src/routes/assignments/list.ts +++ b/src/routes/assignments/list.ts @@ -126,6 +126,9 @@ export const listAssignments = new Elysia().use(HttpStatusCode()).get( user: () => ({ username: true, displayname: true }), time: true, }), + class: () => ({ + name: true, + }), id: true, }; }); @@ -149,7 +152,6 @@ export const listAssignments = new Elysia().use(HttpStatusCode()).get( }); if (result.isError) { - console.log(result.error); set.status = httpStatus.HTTP_500_INTERNAL_SERVER_ERROR; return DATABASE_READ_FAILED; } @@ -162,15 +164,13 @@ export const listAssignments = new Elysia().use(HttpStatusCode()).get( } const formatted = result.data.assignments.map((assignment) => ({ - subject: assignment.subject, - description: assignment.description, + ...assignment, from: normalDateToCustom(assignment.fromDate), due: normalDateToCustom(assignment.dueDate), updates: assignment.updates.map((upd) => ({ user: upd.user, time: upd.time.getTime(), })), - id: assignment.id, })); return responseBuilder("success", { diff --git a/src/routes/calendar/id.ts b/src/routes/calendar/id.ts new file mode 100644 index 0000000..31c9c1f --- /dev/null +++ b/src/routes/calendar/id.ts @@ -0,0 +1,59 @@ +import e from "@edgedb"; +import { DATABASE_READ_FAILED } from "constants/responses"; +import Elysia from "elysia"; +import { HttpStatusCode } from "elysia-http-status-code"; +import { client } from "index"; +import { normalDateToCustomDateTime } from "utils/dates/customAndNormal"; +import { promiseResult } from "utils/errors"; +import { replaceDateDeep, replaceDateWithTimestamp } from "utils/objects/transform"; +import { responseBuilder } from "utils/response"; + +export const specificCalendar = new Elysia() + .use(HttpStatusCode()) + .get("/:id", async ({ params, set, httpStatus }) => { + const query = e.select(e.Calendar, (c) => ({ + filter_single: e.op(c.id, "=", e.uuid(params.id)), + + title: true, + beginning: true, + ending: true, + location: true, + priority: true, + summary: true, + class: () => ({ + name: true, + school: () => ({ name: true }) + }), + updates: () => ({ + user: () => ({ username: true, displayname: true }), + time: true, + }), + tags: () => ({ + tag: true, + color: true, + }), + id: true, + })); + + const result = await promiseResult(async () => query.run(client)); + + if (result.isError) { + set.status = httpStatus.HTTP_400_BAD_REQUEST; + return DATABASE_READ_FAILED; + } + + if (!result.data) { + set.status = httpStatus.HTTP_404_NOT_FOUND; + return responseBuilder("error", { + error: "Calendar event not found", + }); + } + + return responseBuilder("success", { + message: "Successfully retrieved calendar event", + data: { + ...replaceDateDeep(result.data, normalDateToCustomDateTime), + updates: result.data.updates.map(replaceDateWithTimestamp), + } + }); + }) diff --git a/src/routes/calendar/index.ts b/src/routes/calendar/index.ts index feeef12..227a322 100644 --- a/src/routes/calendar/index.ts +++ b/src/routes/calendar/index.ts @@ -3,9 +3,11 @@ import { createCalendar } from "./create"; import { deleteCalendar } from "./delete"; import { listCalendar } from "./list"; import { updateCalendar } from "./update"; +import { specificCalendar } from "routes/calendar/id"; export const calendarRouter = new Elysia({ prefix: "/calendar" }) - .use(createCalendar) .use(listCalendar) - .use(deleteCalendar) - .use(updateCalendar); + .use(specificCalendar) + .use(createCalendar) + .use(updateCalendar) + .use(deleteCalendar); diff --git a/src/routes/calendar/list.ts b/src/routes/calendar/list.ts index 7609359..d380c02 100644 --- a/src/routes/calendar/list.ts +++ b/src/routes/calendar/list.ts @@ -8,7 +8,7 @@ import { stringArraySchema } from "schemas/stringArray"; import { removeDuplicates } from "utils/arrays/duplicates"; import { filterTruthy } from "utils/arrays/filter"; import { - normalDateToCustom, + normalDateToCustomDateTime, stringToNormal, } from "utils/dates/customAndNormal"; import { strToDir } from "utils/db/direction"; @@ -95,6 +95,11 @@ export const listCalendar = new Elysia().use(HttpStatusCode()).get( ending: true, location: true, priority: true, + summary: true, + class: () => ({ + name: true, + school: () => ({ name: true }) + }), updates: () => ({ user: () => ({ username: true, displayname: true }), time: true, @@ -124,7 +129,7 @@ export const listCalendar = new Elysia().use(HttpStatusCode()).get( } const formatted = result.data.calendar.map((c) => ({ - ...replaceDateDeep(c, normalDateToCustom), + ...replaceDateDeep(c, normalDateToCustomDateTime), updates: c.updates.map((u) => replaceDateDeep(u, (d) => d.getTime())), })); diff --git a/src/routes/notes/create.ts b/src/routes/notes/create.ts index 41be9c0..29d1a3c 100644 --- a/src/routes/notes/create.ts +++ b/src/routes/notes/create.ts @@ -29,7 +29,7 @@ export const createNote = new Elysia() username: auth.username, }), - summary: body.summary, + summary: body.summary || null, priority: body.priority, tags: e.select(e.Tag, (t) => ({ filter: e.op( @@ -57,7 +57,7 @@ export const createNote = new Elysia() set.status = httpStatus.HTTP_201_CREATED; return responseBuilder("success", { message: "Successfully created note", - data: null, + data: result.data }); }, { @@ -65,7 +65,7 @@ export const createNote = new Elysia() class: t.String({ minLength: 1 }), school: t.String({ minLength: 1 }), title: t.String({ minLength: 1 }), - summary: t.Optional(t.String({ minLength: 1 })), + summary: t.Optional(t.String()), tags: t.Optional(t.Array(t.String({ minLength: 1 }))), priority: t.Optional( t.Union([ diff --git a/src/routes/notes/id.ts b/src/routes/notes/id.ts new file mode 100755 index 0000000..b68a045 --- /dev/null +++ b/src/routes/notes/id.ts @@ -0,0 +1,50 @@ +import e from "@edgedb"; +import { DATABASE_READ_FAILED } from "constants/responses"; +import Elysia from "elysia"; +import { HttpStatusCode } from "elysia-http-status-code"; +import { client } from "index"; +import { promiseResult } from "utils/errors"; +import { replaceDateWithTimestamp } from "utils/objects/transform"; +import { responseBuilder } from "utils/response"; + +export const specificNote = new Elysia() + .use(HttpStatusCode()) + .get("/:id", async ({ params, set, httpStatus }) => { + const query = e.select(e.Note, (n) => ({ + filter_single: e.op(n.id, "=", e.uuid(params.id)), + + title: true, + summary: true, + tags: () => ({ + tag: true, + color: true, + }), + class: () => ({ name: true, school: () => ({ name: true }) }), + creator: () => ({ username: true }), + editScope: true, + priority: true, + updates: () => ({ + user: () => ({ username: true, displayname: true }), + time: true, + }), + })); + + const result = await promiseResult(async () => query.run(client)); + + if (result.isError) { + set.status = httpStatus.HTTP_400_BAD_REQUEST; + return DATABASE_READ_FAILED; + } + + if (!result.data) { + set.status = httpStatus.HTTP_404_NOT_FOUND; + return responseBuilder("error", { + error: "Note not found", + }); + } + + return responseBuilder("success", { + message: "Successfully retrieved note", + data: replaceDateWithTimestamp(result.data), + }); + }); diff --git a/src/routes/notes/index.ts b/src/routes/notes/index.ts index 22880a9..b2cc9e6 100644 --- a/src/routes/notes/index.ts +++ b/src/routes/notes/index.ts @@ -3,9 +3,11 @@ import { createNote } from "./create"; import { deleteNote } from "./delete"; import { listNotes } from "./list"; import { updateNote } from "./update"; +import { specificNote } from "./id"; export const noteRouter = new Elysia({ prefix: "/notes" }) .use(createNote) .use(deleteNote) .use(updateNote) - .use(listNotes); + .use(listNotes) + .use(specificNote); diff --git a/src/routes/notes/list.ts b/src/routes/notes/list.ts index b073969..4c0ecf6 100644 --- a/src/routes/notes/list.ts +++ b/src/routes/notes/list.ts @@ -3,7 +3,6 @@ import { DATABASE_READ_FAILED } 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 { stringArraySchema } from "schemas/stringArray"; import { removeDuplicates } from "utils/arrays/duplicates"; import { filterTruthy } from "utils/arrays/filter"; @@ -15,127 +14,129 @@ import { responseBuilder } from "utils/response"; import { split } from "utils/strings/split"; import { surround } from "utils/strings/surround"; -export const listNotes = new Elysia() - .use(HttpStatusCode()) - .use(auth) - .get( - "/", - async ({ set, httpStatus, query, auth }) => { - const classesResult = stringArraySchema.safeParse( - filterTruthy(split(query.classes)), - ); - if (!classesResult.success) { - set.status = httpStatus.HTTP_400_BAD_REQUEST; - return responseBuilder("error", { - error: "Classes must be an array of strings", - }); - } - const classNames = removeDuplicates(classesResult.data).sort(); +export const listNotes = new Elysia().use(HttpStatusCode()).get( + "/", + async ({ set, httpStatus, query }) => { + const classesResult = stringArraySchema.safeParse( + filterTruthy(split(query.classes)), + ); + if (!classesResult.success) { + set.status = httpStatus.HTTP_400_BAD_REQUEST; + return responseBuilder("error", { + error: "Classes must be an array of strings", + }); + } + const classNames = removeDuplicates(classesResult.data).sort(); - const qFilter = query.filter?.query; + const qFilter = query.filter?.query; - const dbQuery = (limit: number, offset: number) => - e.select(e.Note, (n) => { - const internalLimit = limit === -1 ? undefined : limit; + const dbQuery = (limit: number, offset: number) => + e.select(e.Note, (n) => { + const internalLimit = limit === -1 ? undefined : limit; - const classMatches = e.op( - e.op(n.class.name, "=", e.set(...classNames)), - "and", - e.op(n.class.school.name, "=", query.school), - ); + const classMatches = e.op( + e.op(n.class.name, "=", e.set(...classNames)), + "and", + e.op(n.class.school.name, "=", query.school), + ); - const queryFilter = qFilter - ? e.op( - e.op(n.title, "ilike", surround(qFilter, "%")), - "or", - e.op(n.summary, "ilike", surround(qFilter, "%")), - ) - : e.bool(true); + const queryFilter = qFilter + ? e.op( + e.op(n.title, "ilike", surround(qFilter, "%")), + "or", + e.op(n.summary, "ilike", surround(qFilter, "%")), + ) + : e.bool(true); - const orderExpression = { - title: n.title, - summary: n.summary, - "last-update": e.max(n.updates.time), - "first-update": e.min(n.updates.time), - }[query.orderKey]; + const orderExpression = { + title: n.title, + summary: n.summary, + "last-update": e.max(n.updates.time), + "first-update": e.min(n.updates.time), + }[query.orderKey]; - return { - filter: e.op(classMatches, "and", queryFilter), - order_by: { - expression: orderExpression, - direction: strToDir(query.orderDirection), - empty: e.EMPTY_LAST, - }, - limit: internalLimit, - offset, + return { + filter: e.op(classMatches, "and", queryFilter), + order_by: { + expression: orderExpression, + direction: strToDir(query.orderDirection), + empty: e.EMPTY_LAST, + }, + limit: internalLimit, + offset, - title: true, - summary: true, - tags: () => ({ - tag: true, - color: true, - }), - editScope: auth.isAuthorized, - priority: true, - updates: () => ({ - user: () => ({ username: true, displayname: true }), - time: true, - }), - id: true, - }; - }); - const result = await promiseResult(async () => { - const [notes, count] = await Promise.all([ - dbQuery(query.limit, query.offset).run(client), - e.count(dbQuery(-1, 0)).run(client), - ]); - return { notes, count }; + title: true, + summary: true, + tags: () => ({ + tag: true, + color: true, + }), + class: () => ({ name: true, school: () => ({ name: true }) }), + creator: () => ({ username: true }), + editScope: true, + priority: true, + updates: () => ({ + user: () => ({ username: true, displayname: true }), + time: true, + }), + id: true, + }; }); - if (result.isError) { - set.status = httpStatus.HTTP_500_INTERNAL_SERVER_ERROR; - return DATABASE_READ_FAILED; - } + const result = await promiseResult(async () => { + const [notes, count] = await Promise.all([ + dbQuery(query.limit, query.offset).run(client), + e.count(dbQuery(-1, 0)).run(client), + ]); - const formatted = result.data.notes.map((i) => - replaceDateDeep(i, normalDateToCustom), - ); + return { notes, count }; + }); - return responseBuilder("success", { - message: "Successfully retrieved data", - data: { - totalCount: result.data.count, - notes: formatted, - }, - }); - }, - { - query: t.Object({ - school: t.String({ minLength: 1 }), - classes: t.String({ minLength: 1 }), + if (result.isError) { + set.status = httpStatus.HTTP_500_INTERNAL_SERVER_ERROR; + return DATABASE_READ_FAILED; + } - limit: t.Numeric({ minimum: -1, default: 15 }), - offset: t.Numeric({ minimum: 0, default: 0 }), + const formatted = result.data.notes.map((i) => ({ + ...replaceDateDeep(i, normalDateToCustom), + updates: i.updates.map((up) => replaceDateDeep(up, (d) => d.getTime())), + })); - orderDirection: t.Union([t.Literal("asc"), t.Literal("desc")], { - default: "desc", - }), - orderKey: t.Union( - [ - t.Literal("title"), - t.Literal("summary"), - t.Literal("last-update"), - t.Literal("first-update"), - ], - { - default: "last-update", - }, - ), - filter: t.Optional( - t.ObjectString({ - query: t.Optional(t.String()), - }), - ), + return responseBuilder("success", { + message: "Successfully retrieved data", + data: { + totalCount: result.data.count, + notes: formatted, + }, + }); + }, + { + query: t.Object({ + school: t.String({ minLength: 1 }), + classes: t.String({ minLength: 1 }), + + limit: t.Numeric({ minimum: -1, default: 15 }), + offset: t.Numeric({ minimum: 0, default: 0 }), + + orderDirection: t.Union([t.Literal("asc"), t.Literal("desc")], { + default: "desc", }), - }, - ); + orderKey: t.Union( + [ + t.Literal("title"), + t.Literal("summary"), + t.Literal("last-update"), + t.Literal("first-update"), + ], + { + default: "last-update", + }, + ), + filter: t.Optional( + t.ObjectString({ + query: t.Optional(t.String()), + }), + ), + }), + }, +); diff --git a/src/utils/dates/customAndNormal.ts b/src/utils/dates/customAndNormal.ts index 4cb1991..8664475 100644 --- a/src/utils/dates/customAndNormal.ts +++ b/src/utils/dates/customAndNormal.ts @@ -19,6 +19,19 @@ export const normalDateToCustom = (date: Date) => { return { day, month, year }; }; +/** + * Converts a normal js date to a custom date record + */ +export const normalDateToCustomDateTime = (date: Date) => { + const day = date.getDate(); + const month = date.getMonth() + 1; + const year = date.getFullYear(); + const hour = date.getHours() + const min = date.getMinutes() + + return { day, month, year, hour, min }; +}; + /** * Converts a string to a custom date */