From ab791efe422e5c4b5af92d7a8f0d55588e3f7c6a Mon Sep 17 00:00:00 2001 From: ModZero Date: Sun, 27 Oct 2019 00:24:58 +0200 Subject: [PATCH] A simple basic API Included: removal of remains of GraphQL adaptation. IMO the after/before behaviour is actually pretty badly defined if entities can be deleted, and delivers complexity trying to fix behaviour that users generally expect and are comfortable coping with. Of course special cases _do_ justify going extra length to provide consistent pagination. --- sql/tasks/hasNext.sql | 1 - sql/tasks/hasPrev.sql | 1 - sql/tasks/list.sql | 10 +++---- sql/tasks/listReverse.sql | 14 --------- src/server/auth.ts | 14 ++++++--- src/server/db/models.ts | 4 +-- src/server/db/repos/tasks.ts | 30 ++++--------------- src/server/db/sql/index.ts | 5 +--- src/server/routes/api.ts | 57 ++++++++++++++++++++++++++++++++++++ src/server/routes/index.ts | 2 ++ 10 files changed, 81 insertions(+), 57 deletions(-) delete mode 100644 sql/tasks/hasNext.sql delete mode 100644 sql/tasks/hasPrev.sql delete mode 100644 sql/tasks/listReverse.sql create mode 100644 src/server/routes/api.ts diff --git a/sql/tasks/hasNext.sql b/sql/tasks/hasNext.sql deleted file mode 100644 index dfaa571..0000000 --- a/sql/tasks/hasNext.sql +++ /dev/null @@ -1 +0,0 @@ -SELECT COUNT(*) > 0 r FROM "tasks" WHERE owner = $1 AND "id" > $2; \ No newline at end of file diff --git a/sql/tasks/hasPrev.sql b/sql/tasks/hasPrev.sql deleted file mode 100644 index d6f7690..0000000 --- a/sql/tasks/hasPrev.sql +++ /dev/null @@ -1 +0,0 @@ -SELECT COUNT(*) > 0 r FROM "tasks" WHERE owner = $1 AND "id" < $2; \ No newline at end of file diff --git a/sql/tasks/list.sql b/sql/tasks/list.sql index 8bb2ba8..fdf222d 100644 --- a/sql/tasks/list.sql +++ b/sql/tasks/list.sql @@ -1,14 +1,14 @@ + SELECT id, + owner, name, notes, schedule, - min_frequency, - max_frequency, created_at FROM tasks WHERE - owner = $1 AND ($2 IS NULL OR id > $2) AND ($3 IS NULL OR id < $3) -ORDER BY created_at ASC -LIMIT $4; + owner = $1 +ORDER BY created_at ASC, id ASC +LIMIT $2 OFFSET $3; diff --git a/sql/tasks/listReverse.sql b/sql/tasks/listReverse.sql deleted file mode 100644 index 2d1e01b..0000000 --- a/sql/tasks/listReverse.sql +++ /dev/null @@ -1,14 +0,0 @@ -SELECT - id, - name, - notes, - schedule, - min_frequency, - max_frequency, - created_at -FROM - tasks -WHERE - owner = $1 AND ($2 IS NULL OR id > $2) AND ($3 IS NULL OR id < $3) -ORDER BY created_at DESC -LIMIT $4; diff --git a/src/server/auth.ts b/src/server/auth.ts index cdecb5a..f93be40 100644 --- a/src/server/auth.ts +++ b/src/server/auth.ts @@ -40,10 +40,16 @@ export const authMiddleware: () => express.Handler = () => async ( next(); }; -export const requireAuthMiddleware: express.Handler = (req, res, next) => { +export const requireAuthMiddleware: (redirect?: string) => express.Handler = ( + redirect = null +) => (req, res, next) => { if (!req.user) { - next(createHttpError(401)); + if (redirect) { + res.redirect(redirect); + } else { + next(createHttpError(401)); + } + } else { + next(); } - - next(); }; diff --git a/src/server/db/models.ts b/src/server/db/models.ts index c5335fc..2e15080 100644 --- a/src/server/db/models.ts +++ b/src/server/db/models.ts @@ -39,9 +39,7 @@ export interface Task { owner: number; name: string; notes?: string; - schedule: ScheduleType; - minFrequency?: number; - maxFrequency?: number; + schedule: object; createdAt: DateTime; } diff --git a/src/server/db/repos/tasks.ts b/src/server/db/repos/tasks.ts index 2587efb..257309d 100644 --- a/src/server/db/repos/tasks.ts +++ b/src/server/db/repos/tasks.ts @@ -23,9 +23,7 @@ function rowToTask(row: any): Task { owner: +row.owner, name: row.name, notes: row.notes, - schedule: row.schedule as ScheduleType, - minFrequency: +row.minFrequency, - maxFrequency: +row.maxFrequency, + schedule: row.schedule, createdAt: row.created_at }; } @@ -40,32 +38,14 @@ export class TaskRepository { public async list( owner: number, limit?: number, - after?: number, - before?: number + offset?: number ): Promise { - return this.db.map(sql.list, [owner, after, before, limit || 10], row => + return this.db.map(sql.list, [owner, limit || 10, offset || 0], row => rowToTask(row) ); } - public async listReverse( - owner: number, - limit?: number, - after?: number, - before?: number - ): Promise { - return this.db.map( - sql.listReverse, - [owner, after, before, limit || 10], - row => rowToTask(row) - ); - } - - public async hasNext(owner: number, after: number): Promise { - return this.db.one(sql.hasNext, [owner, after]).then(row => row.r); - } - - public async hasPrev(owner: number, before: number): Promise { - return this.db.one(sql.hasPrev, [owner, before]).then(row => row.r); + public async count(owner: number): Promise { + return this.db.one(sql.count, [owner], row => +row.c); } } diff --git a/src/server/db/sql/index.ts b/src/server/db/sql/index.ts index 42a3f3e..f3da348 100644 --- a/src/server/db/sql/index.ts +++ b/src/server/db/sql/index.ts @@ -45,10 +45,7 @@ const users = { const tasks = { count: sql("tasks/count.sql"), - hasNext: sql("tasks/hasNext.sql"), - hasPrev: sql("tasks/hasPrev.sql"), - list: sql("tasks/list.sql"), - listReverse: sql("tasks/listReverse.sql") + list: sql("tasks/list.sql") }; const sessions = { diff --git a/src/server/routes/api.ts b/src/server/routes/api.ts new file mode 100644 index 0000000..04b443c --- /dev/null +++ b/src/server/routes/api.ts @@ -0,0 +1,57 @@ +// Copyright (C) 2019 ModZero +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +import { requireAuthMiddleware } from "@kredens/server/auth"; +import { db } from "@kredens/server/db"; +import express from "express"; + +const router = express.Router(); + +function tryInt(v: string, def?: number): number | undefined { + if (v) { + const value = parseInt(v, 10); + return isNaN(value) ? def : value; + } + + return def; +} + +router.use(requireAuthMiddleware()); +router.get("/tasks", async (req, res, next) => { + const limit = tryInt(req.query.limit, 10); + const offset = tryInt(req.query.offset, 0); + + const result = await db.tx(async tx => { + const tasks = await tx.tasks.list(req.user.id, limit, offset); + const count = await tx.tasks.count(req.user.id); + return { + tasks, + count + }; + }); + + res.json({ + tasks: result.tasks.map(t => ({ + id: t.id, + name: t.name, + notes: t.notes, + schedule: t.schedule, + createdAt: t.createdAt.toISO + })), + count: result.count + }); +}); + +export default router; diff --git a/src/server/routes/index.ts b/src/server/routes/index.ts index d1b9294..55e969f 100644 --- a/src/server/routes/index.ts +++ b/src/server/routes/index.ts @@ -14,12 +14,14 @@ // along with this program. If not, see . import express from "express"; +import apiRouter from "./api"; import authRouter from "./auth"; import homeRouter from "./home"; const router = express.Router({ strict: true }); router.use("/auth$", authRouter); +router.use("/api/", apiRouter); router.use("/*", homeRouter); export default router;