Tasks query API, yay!
This commit is contained in:
parent
e9e4d8b51d
commit
c996881547
25
package-lock.json
generated
25
package-lock.json
generated
@ -336,6 +336,25 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.7.5.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.7.5.tgz",
|
||||||
"integrity": "sha512-9fq4jZVhPNW8r+UYKnxF1e2HkDWOWKM5bC2/7c9wPV835I0aOrVbS/Hw/pWPk2uKrNXQqg9Z959Kz+IYDd5p3w=="
|
"integrity": "sha512-9fq4jZVhPNW8r+UYKnxF1e2HkDWOWKM5bC2/7c9wPV835I0aOrVbS/Hw/pWPk2uKrNXQqg9Z959Kz+IYDd5p3w=="
|
||||||
},
|
},
|
||||||
|
"@types/pg": {
|
||||||
|
"version": "7.11.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/pg/-/pg-7.11.2.tgz",
|
||||||
|
"integrity": "sha512-4+rj7fnidA77jFURNanuPPc1HrQv+RkhI6s+K18G9zOKbOUUpChA/rbNMqFukNuZ89LoIt/I9dAlxf329TjCNw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@types/node": "*",
|
||||||
|
"@types/pg-types": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@types/pg-types": {
|
||||||
|
"version": "1.11.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/pg-types/-/pg-types-1.11.4.tgz",
|
||||||
|
"integrity": "sha512-WdIiQmE347LGc1Vq3Ki8sk3iyCuLgnccqVzgxek6gEHp2H0p3MQ3jniIHt+bRODXKju4kNQ+mp53lmP5+/9moQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"moment": ">=2.14.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@types/pino": {
|
"@types/pino": {
|
||||||
"version": "5.8.10",
|
"version": "5.8.10",
|
||||||
"resolved": "https://registry.npmjs.org/@types/pino/-/pino-5.8.10.tgz",
|
"resolved": "https://registry.npmjs.org/@types/pino/-/pino-5.8.10.tgz",
|
||||||
@ -4778,6 +4797,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"moment": {
|
||||||
|
"version": "2.24.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz",
|
||||||
|
"integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"monet": {
|
"monet": {
|
||||||
"version": "0.9.0",
|
"version": "0.9.0",
|
||||||
"resolved": "https://registry.npmjs.org/monet/-/monet-0.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/monet/-/monet-0.9.0.tgz",
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
"http-errors": "^1.7.3",
|
"http-errors": "^1.7.3",
|
||||||
"luxon": "^1.19.2",
|
"luxon": "^1.19.2",
|
||||||
"monet": "^0.9.0",
|
"monet": "^0.9.0",
|
||||||
|
"pg": "^7.12.1",
|
||||||
"pg-promise": "^9.2.1",
|
"pg-promise": "^9.2.1",
|
||||||
"pino": "^5.13.3",
|
"pino": "^5.13.3",
|
||||||
"pug": "^2.0.4",
|
"pug": "^2.0.4",
|
||||||
@ -48,6 +49,7 @@
|
|||||||
"@types/http-errors": "^1.6.2",
|
"@types/http-errors": "^1.6.2",
|
||||||
"@types/luxon": "^1.15.2",
|
"@types/luxon": "^1.15.2",
|
||||||
"@types/node": "^12.7.5",
|
"@types/node": "^12.7.5",
|
||||||
|
"@types/pg": "^7.11.2",
|
||||||
"@types/pino": "^5.8.10",
|
"@types/pino": "^5.8.10",
|
||||||
"@types/webpack-dev-middleware": "^2.0.3",
|
"@types/webpack-dev-middleware": "^2.0.3",
|
||||||
"css-loader": "^3.2.0",
|
"css-loader": "^3.2.0",
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
DROP TYPE task_schedule;
|
@ -0,0 +1 @@
|
|||||||
|
CREATE TYPE task_schedule AS ENUM('once', 'daily', 'weekly', 'monthly', 'yearly');
|
1
sql/migrations/patches/20191007000601-tasks/down.sql
Normal file
1
sql/migrations/patches/20191007000601-tasks/down.sql
Normal file
@ -0,0 +1 @@
|
|||||||
|
DROP TABLE tasks;
|
14
sql/migrations/patches/20191007000601-tasks/up.sql
Normal file
14
sql/migrations/patches/20191007000601-tasks/up.sql
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
CREATE TABLE tasks (
|
||||||
|
id integer GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||||
|
owner integer NOT NULL REFERENCES users(id),
|
||||||
|
name text NOT NULL,
|
||||||
|
notes text,
|
||||||
|
schedule task_schedule NOT NULL DEFAULT 'daily',
|
||||||
|
min_frequency integer,
|
||||||
|
max_frequency integer,
|
||||||
|
created_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
CHECK(min_frequency IS NULL OR max_frequency IS NULL OR max_frequency > min_frequency)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TRIGGER set_tasks_updated BEFORE UPDATE ON tasks FOR EACH ROW EXECUTE PROCEDURE set_updated_timestamp();
|
14
sql/tasks/list.sql
Normal file
14
sql/tasks/list.sql
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
notes,
|
||||||
|
schedule,
|
||||||
|
min_frequency,
|
||||||
|
max_frequency,
|
||||||
|
created_at
|
||||||
|
FROM
|
||||||
|
tasks
|
||||||
|
WHERE
|
||||||
|
owner = $1
|
||||||
|
ORDER BY created_at ASC
|
||||||
|
LIMIT $2 OFFSET $3;
|
55
src/api.ts
55
src/api.ts
@ -14,6 +14,7 @@
|
|||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import { db } from "@kredens/db";
|
import { db } from "@kredens/db";
|
||||||
|
import { User } from "@kredens/db/models";
|
||||||
import { ApolloServer, AuthenticationError, gql } from "apollo-server-express";
|
import { ApolloServer, AuthenticationError, gql } from "apollo-server-express";
|
||||||
import { Kind } from "graphql/language";
|
import { Kind } from "graphql/language";
|
||||||
import { GraphQLScalarType, GraphQLScalarTypeConfig } from "graphql/type";
|
import { GraphQLScalarType, GraphQLScalarTypeConfig } from "graphql/type";
|
||||||
@ -24,15 +25,40 @@ const typeDefs = gql`
|
|||||||
"A simple type for getting started"
|
"A simple type for getting started"
|
||||||
hello: String
|
hello: String
|
||||||
migrations: [Migration]!
|
migrations: [Migration]!
|
||||||
|
user(id: ID): User
|
||||||
}
|
}
|
||||||
|
|
||||||
type Migration {
|
type Migration {
|
||||||
id: ID
|
id: ID!
|
||||||
name: String!
|
name: String!
|
||||||
applied_at: DateTime!
|
applied_at: DateTime!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type User {
|
||||||
|
id: ID!
|
||||||
|
email: String!
|
||||||
|
tasks: [Task]!
|
||||||
|
}
|
||||||
|
|
||||||
|
type Task {
|
||||||
|
id: ID
|
||||||
|
name: String!
|
||||||
|
notes: String
|
||||||
|
schedule: ScheduleType!
|
||||||
|
min_frequency: Int
|
||||||
|
max_frequency: Int
|
||||||
|
created_at: DateTime!
|
||||||
|
}
|
||||||
|
|
||||||
scalar DateTime
|
scalar DateTime
|
||||||
|
|
||||||
|
enum ScheduleType {
|
||||||
|
ONCE
|
||||||
|
DAILY
|
||||||
|
WEEKLY
|
||||||
|
MONTHLY
|
||||||
|
YEARLY
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const dateTimeConfig: GraphQLScalarTypeConfig<DateTime, string> = {
|
const dateTimeConfig: GraphQLScalarTypeConfig<DateTime, string> = {
|
||||||
@ -56,12 +82,29 @@ const dateTimeConfig: GraphQLScalarTypeConfig<DateTime, string> = {
|
|||||||
const resolvers = {
|
const resolvers = {
|
||||||
DateTime: new GraphQLScalarType(dateTimeConfig),
|
DateTime: new GraphQLScalarType(dateTimeConfig),
|
||||||
Query: {
|
Query: {
|
||||||
hello: async () => {
|
hello: () => `Hello, world!`,
|
||||||
return `Hello, world!`;
|
migrations: () => db.migrations.applied(),
|
||||||
},
|
user: async (
|
||||||
migrations: async () => {
|
parent: any,
|
||||||
return db.migrations.applied();
|
{ id }: { id: string },
|
||||||
|
context: { user?: User }
|
||||||
|
) => {
|
||||||
|
if (!context.user || context.user.id !== +id) {
|
||||||
|
throw new AuthenticationError(
|
||||||
|
"You cannot query users other than yourself"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return db.users.details(+id).then(user => user.orNull());
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
User: {
|
||||||
|
tasks: (user: User) =>
|
||||||
|
db.tasks.list(user.id).then(tasks =>
|
||||||
|
tasks.map(t => ({
|
||||||
|
...t,
|
||||||
|
schedule: t.schedule.toUpperCase()
|
||||||
|
}))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -16,16 +16,23 @@
|
|||||||
import {
|
import {
|
||||||
Extensions,
|
Extensions,
|
||||||
MigrationRepository,
|
MigrationRepository,
|
||||||
|
TaskRepository,
|
||||||
UserRepository
|
UserRepository
|
||||||
} from "@kredens/db/repos";
|
} from "@kredens/db/repos";
|
||||||
|
import { DateTime } from "luxon";
|
||||||
|
import pg from "pg";
|
||||||
import pgPromise, { IDatabase, IInitOptions } from "pg-promise";
|
import pgPromise, { IDatabase, IInitOptions } from "pg-promise";
|
||||||
|
|
||||||
|
const types = pg.types;
|
||||||
|
types.setTypeParser(types.builtins.TIMESTAMPTZ, DateTime.fromSQL);
|
||||||
|
types.setTypeParser(types.builtins.TIMESTAMP, DateTime.fromSQL);
|
||||||
|
|
||||||
type ExtendedProtocol = IDatabase<Extensions> & Extensions;
|
type ExtendedProtocol = IDatabase<Extensions> & Extensions;
|
||||||
|
|
||||||
const initOptions: IInitOptions<Extensions> = {
|
const initOptions: IInitOptions<Extensions> = {
|
||||||
extend(obj: ExtendedProtocol, dc: any) {
|
extend(obj: ExtendedProtocol, dc: any) {
|
||||||
obj.migrations = new MigrationRepository(obj, pgp);
|
obj.migrations = new MigrationRepository(obj, pgp);
|
||||||
|
obj.tasks = new TaskRepository(obj, pgp);
|
||||||
obj.users = new UserRepository(obj, pgp);
|
obj.users = new UserRepository(obj, pgp);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -25,3 +25,16 @@ export interface User {
|
|||||||
id: number;
|
id: number;
|
||||||
email: string;
|
email: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ScheduleType = "once" | "daily" | "weekly" | "monthly" | "yearly";
|
||||||
|
|
||||||
|
export interface Task {
|
||||||
|
id: number;
|
||||||
|
owner: number;
|
||||||
|
name: string;
|
||||||
|
notes?: string;
|
||||||
|
schedule: ScheduleType;
|
||||||
|
min_frequency?: number;
|
||||||
|
max_frequency?: number;
|
||||||
|
created_at: DateTime;
|
||||||
|
}
|
||||||
|
@ -14,11 +14,13 @@
|
|||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import { MigrationRepository } from "@kredens/db/repos/migrations";
|
import { MigrationRepository } from "@kredens/db/repos/migrations";
|
||||||
|
import { TaskRepository } from "@kredens/db/repos/tasks";
|
||||||
import { UserRepository } from "@kredens/db/repos/users";
|
import { UserRepository } from "@kredens/db/repos/users";
|
||||||
|
|
||||||
export interface Extensions {
|
export interface Extensions {
|
||||||
migrations: MigrationRepository;
|
migrations: MigrationRepository;
|
||||||
users: UserRepository;
|
users: UserRepository;
|
||||||
|
tasks: TaskRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
export { MigrationRepository, UserRepository };
|
export { MigrationRepository, UserRepository, TaskRepository };
|
||||||
|
@ -50,7 +50,7 @@ export class MigrationRepository {
|
|||||||
public async applied(): Promise<Migration[]> {
|
public async applied(): Promise<Migration[]> {
|
||||||
return this.db.map<Migration>(sql.applied, [], row => {
|
return this.db.map<Migration>(sql.applied, [], row => {
|
||||||
return {
|
return {
|
||||||
applied_at: DateTime.fromSQL(row.applied_at),
|
applied_at: row.applied_at as DateTime,
|
||||||
id: +row.id,
|
id: +row.id,
|
||||||
name: row.name
|
name: row.name
|
||||||
};
|
};
|
||||||
|
32
src/db/repos/tasks.ts
Normal file
32
src/db/repos/tasks.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
// Copyright (C) 2019 ModZero <modzero@modzero.xyz>
|
||||||
|
//
|
||||||
|
// 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import { ScheduleType, Task } from "@kredens/db/models";
|
||||||
|
import { tasks as sql } from "@kredens/db/sql";
|
||||||
|
import { IDatabase, IMain } from "pg-promise";
|
||||||
|
|
||||||
|
export class TaskRepository {
|
||||||
|
private db: IDatabase<any>;
|
||||||
|
|
||||||
|
constructor(db: IDatabase<any>, pgp: IMain) {
|
||||||
|
this.db = db;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async list(owner: number): Promise<Task[]> {
|
||||||
|
return this.db
|
||||||
|
.manyOrNone<Task>(sql.list, [owner, 10, 0])
|
||||||
|
.then(rows => (rows ? rows : []));
|
||||||
|
}
|
||||||
|
}
|
@ -13,11 +13,11 @@
|
|||||||
// You should have received a copy of the GNU Affero General Public License
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import { User } from "@kredens/db/models";
|
||||||
import { users as sql } from "@kredens/db/sql";
|
import { users as sql } from "@kredens/db/sql";
|
||||||
import argon2 from "argon2";
|
import argon2 from "argon2";
|
||||||
import { Maybe, None, Some } from "monet";
|
import { Maybe, None, Some } from "monet";
|
||||||
import { IDatabase, IMain } from "pg-promise";
|
import { IDatabase, IMain } from "pg-promise";
|
||||||
import { User } from "@kredens/db/models";
|
|
||||||
|
|
||||||
export class UserRepository {
|
export class UserRepository {
|
||||||
private db: IDatabase<any>;
|
private db: IDatabase<any>;
|
||||||
|
@ -41,7 +41,11 @@ const users = {
|
|||||||
login: sql("users/login.sql")
|
login: sql("users/login.sql")
|
||||||
};
|
};
|
||||||
|
|
||||||
export { migrations, users };
|
const tasks = {
|
||||||
|
list: sql("tasks/list.sql")
|
||||||
|
};
|
||||||
|
|
||||||
|
export { migrations, users, tasks };
|
||||||
|
|
||||||
/** Helper for linking to external query files */
|
/** Helper for linking to external query files */
|
||||||
function sql(file: string): QueryFile {
|
function sql(file: string): QueryFile {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user