From 71b568b5b2e8c0b7b6df2d7228a2d60550852990 Mon Sep 17 00:00:00 2001 From: Frank Date: Fri, 7 Jun 2024 11:56:02 +0200 Subject: [PATCH] feat: added driveId exists middleware --- api/src/errors/NotFoundError.ts | 32 +++++++++++++++++++ .../graphql/server/generated/index/nexus.ts | 21 +++++++----- .../server/generated/index/schema.graphql | 3 +- api/src/graphql/server/index.ts | 18 +++++++++++ api/src/middleware/errors.ts | 11 ++++--- .../modules/document-drive/drive-resolver.ts | 4 +-- .../modules/document-drive/drives-resolver.ts | 21 ++++++++++-- api/src/modules/document/model.ts | 9 ++++++ 8 files changed, 101 insertions(+), 18 deletions(-) create mode 100644 api/src/errors/NotFoundError.ts diff --git a/api/src/errors/NotFoundError.ts b/api/src/errors/NotFoundError.ts new file mode 100644 index 00000000..c30a5191 --- /dev/null +++ b/api/src/errors/NotFoundError.ts @@ -0,0 +1,32 @@ +import { CustomError } from "./CustomError"; + +export default class NotFoundError extends CustomError { + private static readonly _statusCode = 404; + private readonly _code: number; + private readonly _logging: boolean; + private readonly _context: { [key: string]: any }; + + constructor(params?: { code?: number, message?: string, logging?: boolean, context?: { [key: string]: any } }) { + const { code, message, logging } = params || {}; + + super(message || "Not Found"); + this._code = code || NotFoundError._statusCode; + this._logging = logging || false; + this._context = params?.context || {}; + + // Only because we are extending a built in class + Object.setPrototypeOf(this, NotFoundError.prototype); + } + + get errors() { + return [{ message: this.message, context: this._context }]; + } + + get statusCode() { + return this._code; + } + + get logging() { + return this._logging; + } +} diff --git a/api/src/graphql/server/generated/index/nexus.ts b/api/src/graphql/server/generated/index/nexus.ts index 45e7f81c..4fb0157f 100644 --- a/api/src/graphql/server/generated/index/nexus.ts +++ b/api/src/graphql/server/generated/index/nexus.ts @@ -44,7 +44,7 @@ declare global { declare global { - interface NexusGen extends NexusGenTypes {} + interface NexusGen extends NexusGenTypes { } } export interface NexusGenInputs { @@ -205,6 +205,7 @@ export interface NexusGenFieldTypes { Query: { // field return type coreUnit: NexusGenRootTypes['CoreUnit'] | null; // CoreUnit coreUnits: Array | null; // [CoreUnit] + driveIdBySlug: string | null; // String drives: Array | null; // [String] system: NexusGenRootTypes['SwitchboardHost'] | null; // SwitchboardHost } @@ -288,6 +289,7 @@ export interface NexusGenFieldTypeNames { Query: { // field return type name coreUnit: 'CoreUnit' coreUnits: 'CoreUnit' + driveIdBySlug: 'String' drives: 'String' system: 'SwitchboardHost' } @@ -345,6 +347,9 @@ export interface NexusGenArgTypes { coreUnit: { // args id?: string | null; // String } + driveIdBySlug: { // args + slug?: string | null; // String + } } } @@ -422,15 +427,15 @@ declare global { * resolver from executing. */ authorize?: FieldAuthorizeResolver - + /** * Async validation function. Reject when validation fails. Resolve otherwise. */ - validate?: - NexusGenArgTypes extends HasTypeField - ? ArgsValidationConfig - : never - + validate?: + NexusGenArgTypes extends HasTypeField + ? ArgsValidationConfig + : never + } interface NexusGenPluginInputFieldConfig { } @@ -438,4 +443,4 @@ declare global { } interface NexusGenPluginArgConfig { } -} \ No newline at end of file +} diff --git a/api/src/graphql/server/generated/index/schema.graphql b/api/src/graphql/server/generated/index/schema.graphql index 6c692448..fd606f5c 100644 --- a/api/src/graphql/server/generated/index/schema.graphql +++ b/api/src/graphql/server/generated/index/schema.graphql @@ -80,6 +80,7 @@ type Node { type Query { coreUnit(id: String): CoreUnit coreUnits: [CoreUnit] + driveIdBySlug(slug: String): String drives: [String] system: SwitchboardHost } @@ -118,4 +119,4 @@ interface System { type User { address: String! createdAt: Date! -} \ No newline at end of file +} diff --git a/api/src/graphql/server/index.ts b/api/src/graphql/server/index.ts index 539621ee..41a4cbcf 100644 --- a/api/src/graphql/server/index.ts +++ b/api/src/graphql/server/index.ts @@ -8,6 +8,8 @@ import { schemaWithMiddleware as indexSchema } from './index/schema'; import { schemaWithMiddleware as driveSchema } from './drive/schema'; import { Context, Context as IndexContext, createContext as createIndexContext } from './index/context'; import { Context as DriveContext, createContext as createDriveContext } from './drive/context'; +import { getExtendedPrisma } from '../../importedModules'; +import NotFoundError from '../../errors/NotFoundError'; function loggerPlugin(): ApolloServerPlugin { return { @@ -57,9 +59,25 @@ export const addGraphqlRoutes = async ( '/d/:driveId', cors(), cookierParser(undefined, { decode: (value: string) => value }), + async (req, res, next) => { + const prisma = getExtendedPrisma(); + const { driveId } = req.params; + + if (!driveId) { + throw new Error("driveId required") + } + + try { + await prisma.document.getDrive(driveId); + } catch (e) { + throw new NotFoundError({ message: (e as Error).message }) + } + next(); + }, expressMiddleware(apolloDrive, { context: async (params) => createDriveContext(params), }), + ); }; diff --git a/api/src/middleware/errors.ts b/api/src/middleware/errors.ts index bdef4ddc..a8174780 100644 --- a/api/src/middleware/errors.ts +++ b/api/src/middleware/errors.ts @@ -1,11 +1,12 @@ import { NextFunction, Request, Response } from "express"; import { getChildLogger } from "../logger"; +import { CustomError } from "../errors/CustomError"; const logger = getChildLogger({ msgPrefix: 'Generic Error Handler', }); -export const errorHandler = (err: Error, req: Request, res: Response, next: NextFunction) => { - const errorId = (res as { sentry?: string }).sentry; - err.cause = err.cause || 'Unknown'; - logger.error(err, err.message, { errorId }); - res.status(500).send({ errors: err.message, errorId }); +export const errorHandler = (err: CustomError, req: Request, res: Response, next: NextFunction) => { + const errorId = (res as { sentry?: string }).sentry; + err.cause = err.cause || 'Unknown'; + logger.error(err, err.message, { errorId }); + res.status(err.statusCode ?? 500).send({ errors: err.message, errorId }); }; diff --git a/api/src/modules/document-drive/drive-resolver.ts b/api/src/modules/document-drive/drive-resolver.ts index a982375e..5f64b729 100644 --- a/api/src/modules/document-drive/drive-resolver.ts +++ b/api/src/modules/document-drive/drive-resolver.ts @@ -32,7 +32,7 @@ export const Node = objectType({ }, }); -export const DocumentDriveState = objectType({ +export const DocumentDriveStateObject = objectType({ name: 'DocumentDriveState', definition(t) { t.nonNull.id('id'); @@ -307,7 +307,7 @@ export const driveSystemQueryField = queryField('system', { }); export const getDrive = queryField('drive', { - type: DocumentDriveState, + type: DocumentDriveStateObject, resolve: async (_parent, _args, ctx: Context) => { try { const drive = await ctx.prisma.document.getDrive(ctx.driveId ?? '1') as DocumentDriveState; diff --git a/api/src/modules/document-drive/drives-resolver.ts b/api/src/modules/document-drive/drives-resolver.ts index 996d0033..a357b133 100644 --- a/api/src/modules/document-drive/drives-resolver.ts +++ b/api/src/modules/document-drive/drives-resolver.ts @@ -5,8 +5,9 @@ import { nonNull, objectType, queryField, + stringArg, } from 'nexus'; -import { DocumentDriveState } from './drive-resolver'; +import { DocumentDriveStateObject } from './drive-resolver'; import { Context } from '../../graphql/server/drive/context'; import logger from '../../logger'; import DocumentDriveError from '../../errors/DocumentDriveError'; @@ -49,11 +50,27 @@ export const getDrives = queryField('drives', { }, }); +export const getDriveBySlug = queryField('driveIdBySlug', { + type: 'String', + args: { + slug: stringArg() + }, + resolve: async (_parent, args, ctx: Context) => { + try { + const drive = await ctx.prisma.document.getDriveBySlug(args.slug); + return drive.id; + } catch (e) { + logger.error(e); + throw new Error('Drive not found.'); + } + }, +}); + const addDriveResponseDefinition = objectType({ name: 'AddDriveResponse', definition(t) { t.nonNull.field('global', { - type: DocumentDriveState + type: DocumentDriveStateObject }); t.nonNull.field('local', { type: DocumentDriveLocalState, diff --git a/api/src/modules/document/model.ts b/api/src/modules/document/model.ts index 82d636bb..c92f551e 100644 --- a/api/src/modules/document/model.ts +++ b/api/src/modules/document/model.ts @@ -111,6 +111,15 @@ export function getDocumentDriveCRUD(prisma: Prisma.TransactionClient) { throw new Error("Couldn't get drive"); } }, + getDriveBySlug: async (slug: string) => { + try { + const { state } = await driveServer.getDriveBySlug(slug); + return state.global; + } catch (e) { + logger.error(e); + throw new Error("Couldn't get drive"); + } + }, getDrives: async () => { try { const driveIds = await driveServer.getDrives()