From 4c35e9f33d00ca5b5018e8bb04be41b9b23e5693 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20B=C3=BCttner?= Date: Fri, 17 May 2024 19:31:29 +0200 Subject: [PATCH] Issue 565 complexity reduction (#11) * refactor validation fixes idrinth-api-bench/issues#565 * refactor validation fixes idrinth-api-bench/issues#565 * fix merge fixes idrinth-api-bench/issues#565 * fix merge fixes idrinth-api-bench/issues#565 --- .gitignore | 4 +- src/cli/check-routes.ts | 151 +---------------------------- src/routes/middleware-loader.ts | 2 +- src/routes/validate-tasks.ts | 16 +-- src/validation/check-middleware.ts | 23 +++++ src/validation/check-request.ts | 89 +++++++++++++++++ src/validation/check-type.ts | 49 ++++++++++ src/validation/error.ts | 10 ++ src/validation/no-duplicate-ids.ts | 13 +++ src/validation/warn.ts | 10 ++ tsconfig.json | 1 - 11 files changed, 205 insertions(+), 163 deletions(-) create mode 100644 src/validation/check-middleware.ts create mode 100644 src/validation/check-request.ts create mode 100644 src/validation/check-type.ts create mode 100644 src/validation/error.ts create mode 100644 src/validation/no-duplicate-ids.ts create mode 100644 src/validation/warn.ts diff --git a/.gitignore b/.gitignore index ba6f90e..8988168 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -/node_modules -/coverage /.idea +/node_modules /src/locales +/coverage \ No newline at end of file diff --git a/src/cli/check-routes.ts b/src/cli/check-routes.ts index 46e3f03..6e7654b 100644 --- a/src/cli/check-routes.ts +++ b/src/cli/check-routes.ts @@ -1,166 +1,23 @@ -/* eslint no-console: 0 */ import jobCreator from '../routes/job-creator.js'; import language, { locale, } from '../helper/language.js'; import { DEFAULT_LANGUAGE, - ONE, EMPTY, FIRST_ARGUMENT, } from '../constants.js'; -import validateTasks from '../routes/validate-tasks.js'; import taskTypes from '../routes/task-types.js'; -import Task from '../routes/task.js'; -import Job from '../routes/job.js'; -import taskType from '../routes/task-type.js'; -import Request from '../routes/request.js'; import logSymbols from 'log-symbols'; -import languageKey from '../locales/language-key.js'; - -const warn = (key: languageKey, ...argList: string[]) => { - console.warn(logSymbols.warning + ' ' + language(key, ...argList,),); -}; -const error = (key: languageKey, ...argList: string[]) => { - console.error(logSymbols.error + ' ' + language(key, ...argList,),); -}; - -// eslint-disable-next-line complexity -const checkMiddleware = (type: 'pre'|'post', route: Task,) => { - if (typeof route[type] === 'undefined') { - return true; - } - const data = route[type]; - delete route[type]; - if (typeof data !== 'object' || ! Array.isArray(data,)) { - error(`invalid_${ type }_definition`, route.id,); - return false; - } - for (const middleware of data) { - if (typeof middleware !== 'string') { - error(`invalid_${ type }_definition`, route.id,); - return false; - } - } - return true; -}; - -// eslint-disable-next-line complexity -const checkRequest = (main: Request, id: string,): { - invalid: boolean, - risky: boolean, -} => { - const properties = [ - { - name: 'method', - type: 'string', - required: true, - }, - { - name: 'headers', - type: 'object', - required: false, - }, - { - name: 'cookies', - type: 'object', - required: false, - }, - { - name: 'body', - type: [ - 'string', - 'object', - ], - required: false, - }, - { - name: 'autohandle', - type: 'string', - required: false, - }, - { - name: 'url', - type: 'string', - required: true, - }, - { - name: 'maxDuration', - type: 'number', - required: false, - }, - ]; - let valid = true; - for (const property of properties) { - if (property.required && typeof main[property.name] === 'undefined') { - error('invalid_request_property', id, property.name,); - valid = false; - } else if (typeof main[property.name] !== property.type) { - error('invalid_request_property', id, property.name,); - valid = false; - delete main[property.name]; - } else { - delete main[property.name]; - } - } - if (Object.keys(main,).length === EMPTY) { - warn('invalid_request', id,); - return { - invalid: ! valid, - risky: true, - }; - } - return { - invalid: ! valid, - risky: false, - }; -}; - -// eslint-disable-next-line complexity -const checkType = (job: Job, type: taskType,) => { - if (typeof job[type] === 'undefined') { - return { - errors: 0, - warnings: 0, - }; - } - let errors = 0; - let warnings = 0; - if (job[type].length > EMPTY) { - for (const route of job[type]) { - if (! checkMiddleware('pre', route,)) { - errors ++; - } - if (! checkMiddleware('post', route,)) { - errors ++; - } - const id = route.id; - delete route.id; - const result = checkRequest(route.main, id,); - if (result.invalid) { - errors ++; - } - if (result.risky) { - warnings ++; - } - delete route.main; - for (const key of Object.keys(route,)) { - warn('unknown_route_property', key, id,); - warnings ++; - } - } - } - return { - errors, - warnings, - }; -}; +import error from '../validation/error.js'; +import checkType from '../validation/check-type.js'; +import noDuplicateIds from '../validation/no-duplicate-ids.js'; // eslint-disable-next-line complexity export default async(args: string[], cwd: string,): Promise => { await locale(args[FIRST_ARGUMENT] || DEFAULT_LANGUAGE,); const job = await jobCreator(cwd,); - validateTasks(ONE, ONE, job.main,); + noDuplicateIds(job.main,); let errors = 0; let warnings = 0; for (const type of taskTypes) { diff --git a/src/routes/middleware-loader.ts b/src/routes/middleware-loader.ts index 3df7c1d..41104c8 100644 --- a/src/routes/middleware-loader.ts +++ b/src/routes/middleware-loader.ts @@ -27,6 +27,6 @@ const resolve = (path: string,): string => { }; const load = async(path: string,): Promise => { const req = cache[path] || (cache[path] = resolve(path,)); - return new (await include(req,)) as Middleware; + return new (await include(req,))() as Middleware; }; export default load; diff --git a/src/routes/validate-tasks.ts b/src/routes/validate-tasks.ts index 001f5d6..4e12a56 100644 --- a/src/routes/validate-tasks.ts +++ b/src/routes/validate-tasks.ts @@ -1,18 +1,10 @@ import Task from './task.js'; -import language from '../helper/language.js'; +import language from './helper/language.js'; import { EMPTY, -} from '../constants.js'; +} from './constants.js'; +import noDuplicateIds from './no-duplicate-ids.js'; -const noDuplicateIDs = (tasks: Array,) => { - const ids: Array = []; - for (const task of tasks) { - if (ids.includes(task.id,)) { - throw new Error(language('duplicate_task_id', task.id,),); - } - ids.push(task.id,); - } -}; const executableAmount = ( repetitions: number, threads: number, @@ -29,5 +21,5 @@ export default function validateTasks( tasks: Array, ): void { executableAmount(repetitions, threads, tasks,); - noDuplicateIDs(tasks,); + noDuplicateIds(tasks,); } diff --git a/src/validation/check-middleware.ts b/src/validation/check-middleware.ts new file mode 100644 index 0000000..0f4e709 --- /dev/null +++ b/src/validation/check-middleware.ts @@ -0,0 +1,23 @@ +import Task from '../task.js'; +import error from './error.js'; + +const checkMiddleware = (type: 'pre' | 'post', route: Task,) => { + if (typeof route[type] === 'undefined') { + return true; + } + const data = route[type]; + delete route[type]; + if (typeof data !== 'object' || ! Array.isArray(data,)) { + error(`invalid_${ type }_definition`, route.id,); + return false; + } + for (const middleware of data) { + if (typeof middleware !== 'string') { + error(`invalid_${ type }_definition`, route.id,); + return false; + } + } + return true; +}; + +export default checkMiddleware; diff --git a/src/validation/check-request.ts b/src/validation/check-request.ts new file mode 100644 index 0000000..d4d8dbf --- /dev/null +++ b/src/validation/check-request.ts @@ -0,0 +1,89 @@ +import Request from '../request.js'; +import error from './error.js'; +import { + EMPTY, +} from '../constants.js'; +import warn from './warn.js'; + +interface Property { + name: string; + type: string|string[]; + required: boolean; +} + +const properties: Property[] = [ + { + name: 'method', + type: 'string', + required: true, + }, + { + name: 'headers', + type: 'object', + required: false, + }, + { + name: 'cookies', + type: 'object', + required: false, + }, + { + name: 'body', + type: [ + 'string', + 'object', + ], + required: false, + }, + { + name: 'autohandle', + type: 'string', + required: false, + }, + { + name: 'url', + type: 'string', + required: true, + }, + { + name: 'maxDuration', + type: 'number', + required: false, + }, +]; + +const validateProperty = (property: Property, id: string, main: Request,) => { + if (property.required && typeof main[property.name] === 'undefined') { + error('invalid_request_property', id, property.name,); + return false; + } + const allowedTypes: string[] = Array.isArray(property.type,) ? property.type : [ property.type, ]; + if (! allowedTypes.includes(typeof main[property.name],)) { + error('invalid_request_property', id, property.name,); + delete main[property.name]; + return false; + } + delete main[property.name]; + return true; +}; +const checkRequest = (main: Request, id: string,): { + invalid: boolean, + risky: boolean, +} => { + let valid = true; + for (const property of properties) { + valid = validateProperty(property, id, main,) && valid; + } + if (Object.keys(main,).length !== EMPTY) { + warn('invalid_request', id,); + return { + invalid: ! valid, + risky: true, + }; + } + return { + invalid: ! valid, + risky: false, + }; +}; +export default checkRequest; diff --git a/src/validation/check-type.ts b/src/validation/check-type.ts new file mode 100644 index 0000000..115ae2b --- /dev/null +++ b/src/validation/check-type.ts @@ -0,0 +1,49 @@ +import Job from '../job.js'; +import taskType from '../task-type.js'; +import { + EMPTY, +} from '../constants.js'; +import checkRequest from './check-request.js'; +import warn from './warn.js'; +import checkMiddleware from './check-middleware.js'; + +const checkType = (job: Job, type: taskType,) => { + if (typeof job[type] === 'undefined') { + return { + errors: 0, + warnings: 0, + }; + } + let errors = 0; + let warnings = 0; + if (job[type].length > EMPTY) { + for (const route of job[type]) { + if (! checkMiddleware('pre', route,)) { + errors ++; + } + if (! checkMiddleware('post', route,)) { + errors ++; + } + const id = route.id; + delete route.id; + const result = checkRequest(route.main, id,); + if (result.invalid) { + errors ++; + } + if (result.risky) { + warnings ++; + } + delete route.main; + for (const key of Object.keys(route,)) { + warn('unknown_route_property', key, id,); + warnings ++; + } + } + } + return { + errors, + warnings, + }; +}; + +export default checkType; diff --git a/src/validation/error.ts b/src/validation/error.ts new file mode 100644 index 0000000..253150d --- /dev/null +++ b/src/validation/error.ts @@ -0,0 +1,10 @@ +/* eslint no-console: 0 */ +import language from '../helper/language.js'; +import logSymbols from 'log-symbols'; +import languageKey from '../locales/language-key.js'; + +const error = (key: languageKey, ...argList: string[]) => { + console.error(logSymbols.error + ' ' + language(key, ...argList,),); +}; + +export default error; diff --git a/src/validation/no-duplicate-ids.ts b/src/validation/no-duplicate-ids.ts new file mode 100644 index 0000000..a0b57f3 --- /dev/null +++ b/src/validation/no-duplicate-ids.ts @@ -0,0 +1,13 @@ +import Task from '../routes/task.js'; +import language from '../helper/language.js'; + +const noDuplicateIds = (tasks: Array,) => { + const ids: Array = []; + for (const task of tasks) { + if (ids.includes(task.id,)) { + throw new Error(language('duplicate_task_id', task.id,),); + } + ids.push(task.id,); + } +}; +export default noDuplicateIds; diff --git a/src/validation/warn.ts b/src/validation/warn.ts new file mode 100644 index 0000000..156ccae --- /dev/null +++ b/src/validation/warn.ts @@ -0,0 +1,10 @@ +/* eslint no-console: 0 */ +import language from '../helper/language.js'; +import logSymbols from 'log-symbols'; +import languageKey from '../locales/language-key.js'; + +const warn = (key: languageKey, ...argList: string[]) => { + console.warn(logSymbols.warning + ' ' + language(key, ...argList,),); +}; + +export default warn; diff --git a/tsconfig.json b/tsconfig.json index c8dc798..8e5fdd6 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,7 +2,6 @@ "compilerOptions": { "experimentalDecorators": true, "module": "NodeNext", - "lib": [], "target": "ESNext", "esModuleInterop": true, "moduleResolution": "NodeNext",