UNPKG

backapi

Version:

A simple API framework using Flexible Persistence

566 lines 22.2 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); /* eslint-disable no-unused-vars */ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ /* eslint-disable @typescript-eslint/ban-ts-comment */ // file deepcode ignore no-any: any needed // file deepcode ignore object-literal-shorthand: argh const default_initializer_1 = require("@flexiblepersistence/default-initializer"); const flexiblepersistence_1 = require("flexiblepersistence"); const missingMethodError_1 = __importDefault(require("../error/missingMethodError")); const ts_mixer_1 = require("ts-mixer"); ts_mixer_1.settings.initFunction = 'init'; class AbstractControllerDefault extends default_initializer_1.Default { async mainRequestHandler(args, operation) { const { requestOrData, responseOrSocket } = this.parseArgs(args); try { let response; const method = requestOrData.method ? requestOrData.method.toUpperCase() : undefined; if (method && this.method[method] && this[this.method[method]]) { response = await this[this.method[method]](...args); } else { const error = new Error('Missing HTTP method:' + method); // console.log(requestOrData); throw error; } return response; } catch (error) { console.error(error); return new Promise(() => this.generateError(requestOrData, responseOrSocket, {}, error, operation)); } } errorStatus(error) { if (error) return this.regularErrorStatus[error]; return this.regularErrorStatus; } constructor(initDefault) { super(initDefault); this.regularErrorStatus = { error: 400, Error: 400, RemoveError: 400, RequestError: 400, MongoServerError: 400, JsonWebTokenError: 401, Unauthorized: 401, PaymentRequired: 402, TypeError: 403, NotFound: 404, MethodNotAllowed: 405, UnknownError: 500, MissingMethodError: 500, }; this.method = { POST: 'create', GET: 'read', PATCH: 'update', PUT: 'replaceUpdate', DELETE: 'delete', OPTIONS: 'options', CONNECT: 'connect', HEAD: 'head', TRACE: 'trace', post: 'create', get: 'read', patch: 'update', put: 'replaceUpdate', delete: 'delete', options: 'options', connect: 'connect', head: 'head', trace: 'trace', }; } init(initDefault) { super.init(initDefault); if (initDefault) { this.handler = initDefault.handler; this.middlewares = []; if (initDefault.middlewares) this.middlewares.push(...initDefault.middlewares); } // console.log(this.handler); } // eslint-disable-next-line @typescript-eslint/no-explicit-any async event(event) { return new Promise(async (resolve, reject) => { if (!this.journaly) reject(new Error('No journaly connected!')); if (this.handler) { this.handler .addEvent(event) .then((value) => resolve(value)) .catch((error) => reject(error)); } else reject(new Error('No handler connected!')); }); } runMiddleware(requestOrData, responseOrSocket, fn) { return new Promise((resolve, reject) => { fn(requestOrData, responseOrSocket, (result) => { if (result instanceof Error) { return reject(result); } return resolve(result); }); }); } async runMiddlewares(requestOrData, responseOrSocket) { if (this.middlewares) for (const middleware of this.middlewares) await this.runMiddleware(requestOrData, responseOrSocket, middleware); } generateName() { this.setName(this.getClassName().replace('Controller', this.getType())); } async generateError(requestOrData, responseOrSocket, headers, error, operation) { if (error === undefined) error = new missingMethodError_1.default(); if (error.message.includes('does not exist')) error.name = 'NotFound'; console.error(error); const message = error.message || error.name; const object = { ...error, message, operation }; let status; if (!this.errorStatus() || this.errorStatus(error.name) === undefined) { status = this.errorStatus('UnknownError'); } else { status = this.errorStatus(error.name); } await this.emit(requestOrData, responseOrSocket, headers, operation, status, object); return responseOrSocket; } hasObjectName() { if (process.env.API_HAS_OBJECT_NAME) return /^true$/i.test(process.env.API_HAS_OBJECT_NAME); return false; } getObject(object) { if (this.hasObjectName()) return object[this.getName()]; return object; } setObject(object, value) { if (value === undefined) value = {}; if (this.hasObjectName()) { if (!this.getName()) throw new Error('Element is not specified.'); object[this.getName()] = value; } else object = value; return object; } formatName() { const name = this.getClassName().replace('Controller', ''); return name; } formatContent(requestOrData) { const content = requestOrData.body; return content; } formatParams(requestOrData) { return requestOrData['params'] || requestOrData.query; } formatQuery(requestOrData) { const { query } = requestOrData; return query; } formatBoolean(name, headers, defaultValue) { let content = headers ? headers[name] : undefined; if (content !== undefined) { content = typeof content === 'string' && content.toLowerCase(); content = content === 'true' || content === '1' || content === 1 || content === true; } else content = defaultValue; return content; } formatSelection(params, query) { let selection; // deepcode ignore HTTPSourceWithUncheckedType: <please specify a reason of ignoring this>, deepcode ignore HTTPSourceWithUncheckedType: <please specify a reason of ignoring this> if (params && params.filter) selection = params.filter; else selection = query; for (const key in selection) { if (Object.prototype.hasOwnProperty.call(selection, key)) { const element = selection[key]; const newKey = key.split(/[\s,"'=;\-\/\\]+/)[0]; try { selection[newKey] = JSON.parse(element); } catch (error) { if (Array.isArray(element)) selection[newKey] = element.map((e) => { try { return JSON.parse(e); } catch (error) { return e; } }); else selection[newKey] = element; } if (key != newKey) { selection[key] = undefined; delete selection[key]; } } } return selection; } formatEvent(requestOrData, operation, singleDefault, replace) { const params = this.formatParams(requestOrData); const name = this.formatName(); if (requestOrData?.headers) { requestOrData.headers.pageSize = requestOrData.headers.pageSize || requestOrData.headers.pagesize; } const event = new flexiblepersistence_1.Event({ operation, single: this.formatBoolean('single', requestOrData?.headers, singleDefault), content: this.formatContent(requestOrData), selection: this.formatSelection(params, this.formatQuery(requestOrData)), name, options: requestOrData.headers, correct: this.formatBoolean('correct', requestOrData?.headers), replace: this.formatBoolean('replace', requestOrData?.headers) || replace, }); requestOrData['event'] = { operation, name, }; return event; } generateStatus(operation, object, correct) { const resultObject = this.getObject(object); switch (operation) { case flexiblepersistence_1.Operation.create: return 201; case flexiblepersistence_1.Operation.delete: if (correct) return 410; default: if (resultObject === undefined || Object.keys(resultObject).length === 0 || resultObject.length === 0) return 204; else return 200; } // 206 Partial Content - pagination } // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types async generateObject(useFunction, event) { return this.setObject({}, (await useFunction(event))['receivedItem']); } async generateHeaders(requestOrData, responseOrSocket, event) { let headers = {}; const page = event?.options?.page; const pageSize = event?.options?.pageSize; const pages = event?.options?.pages; if (responseOrSocket) { if (page) headers = { ...headers, ...this.setHeader(requestOrData, responseOrSocket, 'page', page), }; if (pageSize) headers = { ...headers, ...this.setHeader(requestOrData, responseOrSocket, 'pageSize', pageSize), }; if (pages) headers = { ...headers, ...this.setHeader(requestOrData, responseOrSocket, 'pages', pages), }; } return headers; } async setHeader(requestOrData, responseOrSocket, name, value) { if (responseOrSocket.setHeader) responseOrSocket.setHeader(name, value); if (requestOrData.setHeader) requestOrData.setHeader(name, value); const HeaderFragment = {}; HeaderFragment[name] = value; return HeaderFragment; } async enableOptions(requestOrData, responseOrSocket, headers, operation) { if (process.env.CORS_ENABLED?.toLocaleLowerCase() === 'true' || process.env.ALLOWED_ORIGIN === '*') { const method = requestOrData.method ? requestOrData.method?.toLowerCase() : undefined; console.log('CORS enabled'); headers = { ...headers, ...this.setHeader(requestOrData, responseOrSocket, 'Access-Control-Allow-Origin', '*'), }; headers = { ...headers, ...this.setHeader(requestOrData, responseOrSocket, 'Access-Control-Allow-Credentials', 'true'), }; headers = { ...headers, ...this.setHeader(requestOrData, responseOrSocket, 'Access-Control-Allow-Methods', 'GET,POST,PUT,PATCH,DELETE,HEAD,OPTIONS,OTHER,*'), }; const exposedHeaders = 'Access-Control-Allow-Headers, Origin, Accept, accept, authority, method, path, scheme, ' + 'X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers, ' + 'Authorization, authorization, Access-Control-Allow-Origin, Cache-Control, If-Modified-Since, ' + 'Sec-CH-UA-Arch, Sec-CH-UA-Bitness, Sec-CH-UA-Full-Version-List, Sec-CH-UA-Full-Version, Sec-CH-UA-Mobile, ' + 'Sec-CH-UA-Model, Sec-CH-UA-Platform-Version, Sec-CH-UA-PlatformSec-CH-UA, Sec-Fetch-Dest, Sec-Fetch-Mode, ' + 'Sec-Fetch-Site, Sec-Fetch-User, Sec-WebSocket-Accept, Connection, Content-Disposition, Content-Encoding, ' + 'If-None-Match, cache-control, if-modified-since, accept-encoding, accept-language, ' + 'If-Match, if-match, If-Range, if-range, If-Unmodified-Since, if-unmodified-since, ' + 'Accept-Encoding, Accept-Language, origin, referer, sec-ch-ua, sec-ch-ua-mobile, ' + 'sec-ch-ua-platform, sec-fetch-dest, sec-fetch-mode, sec-fetch-site, user-agent, ' + 'pages, page, pageSize, numberOfPages, pagesize, numberofpages, pageNumber, ' + 'pagenumber, type, token, filter, single, sort, sortBy, sortByDesc, ' + 'sortByDescending, sortByAsc, sortByAscending, sortByDescending, ' + 'correct, replace, id, name, description, createdAt, updatedAt, *'; headers = { ...headers, ...this.setHeader(requestOrData, responseOrSocket, 'Access-Control-Allow-Headers', process.env.ALLOWED_HEADERS ? process.env.ALLOWED_HEADERS : exposedHeaders), }; headers = { ...headers, ...this.setHeader(requestOrData, responseOrSocket, 'Access-Control-Expose-Headers', process.env.ALLOWED_HEADERS ? process.env.ALLOWED_HEADERS : exposedHeaders), }; if (method === 'options' || method === 'option') { await this.emit(requestOrData, responseOrSocket, headers, operation, 200, {}); return true; } } return false; } getHandshakeHeaders(requestOrData, responseOrSocket) { if (responseOrSocket?.handshake?.headers) { const setHeader = (key, value) => { responseOrSocket.handshake.headers[key] = value; }; const removeHeader = (key) => { delete responseOrSocket.handshake.headers[key]; }; responseOrSocket.headers = responseOrSocket.handshake.headers; requestOrData.headers = responseOrSocket.handshake.headers; responseOrSocket.query = responseOrSocket.handshake.query; requestOrData.query = responseOrSocket.handshake.query; responseOrSocket.auth = responseOrSocket.handshake.auth; requestOrData.auth = responseOrSocket.handshake.auth; responseOrSocket.setHeader = setHeader; responseOrSocket.removeHeader = removeHeader; requestOrData.setHeader = setHeader; requestOrData.removeHeader = removeHeader; } } parseExpressArgs(args) { const request = args[0]; const response = args[1]; if (request.path === undefined) request.path = request?.route?.path; response.sendResponse = (object) => { request.headers = object?.headers; args[1].headers = object?.headers; if (args[1]?.setHeader) for (const key in object?.headers) { if (Object.prototype.hasOwnProperty.call(object?.headers, key)) { const header = object?.headers[key]; args[1]?.setHeader(key, header); } } return args[1].status(object?.status).json(object?.body); }; return { request: request, response: response, }; } parseNextArgs(args) { const request = args[0]; const response = args[1]; if (request.path === undefined) request.path = request?.url?.split('?')?.[0]; response.sendResponse = (object) => { request.headers = object?.headers; args[1].headers = object?.headers; if (args[1]?.setHeader) for (const key in object?.headers) { if (Object.prototype.hasOwnProperty.call(object?.headers, key)) { const header = object?.headers[key]; args[1]?.setHeader(key, header); } } return args[1].status(object?.status).json(object?.body); }; return { request: request, response: response, }; } parseAWSBody(received) { try { return JSON.parse(received); } catch (error) { return received; } } parseAWSArgs(args) { const event = args[0]; const context = args[1]; const callback = args[2]; const request = { ...event, method: event.httpMethod, query: event.queryStringParameters, request: this.parseAWSBody(event.body), }; const response = { ...context, currentHeaders: {}, headers: {}, sendResponse: (object) => { const newObject = JSON.parse(JSON.stringify(object)); response.currentHeaders = { ...newObject.headers, ...response.currentHeaders, }; newObject.headers = response.currentHeaders; if (newObject) newObject.statusCode = newObject?.status; delete newObject?.status; response.body = newObject.body; callback.bind(response)(null, newObject); return response; }, setHeader: (name, value) => { response.currentHeaders[name] = value; return value; }, removeHeader: (name, value) => { delete response.currentHeaders[name]; return value; }, }; return { event: event, request: request, response: response, context: context, callback: callback, }; } parseRestArgs(args) { switch (this.restFramework) { case 'express': return this.parseExpressArgs(args); case 'next': return this.parseNextArgs(args); case 'aws': return this.parseAWSArgs(args); default: return this.parseNextArgs(args); } } parseWebArgs(args) { return { data: args[0], socket: args[1], server: args[2], }; } parseIoArgs(args) { return { data: args[0], socket: args[1], server: args[2], }; } parseSocketArgs(args) { switch (this.socketFramework) { case 'web': return this.parseWebArgs(args); case 'io': return this.parseIoArgs(args); default: return this.parseIoArgs(args); } } parseArgs(args) { let newArgs; switch (this.communication) { case 'rest': newArgs = this.parseRestArgs(args); break; case 'socket': newArgs = this.parseSocketArgs(args); break; default: newArgs = this.parseRestArgs(args); break; } return { requestOrData: newArgs.request || newArgs.data, responseOrSocket: newArgs.response || newArgs.socket, server: newArgs.server, context: newArgs.context, }; } async generateEvent(args, operation, useFunction, singleDefault, replace) { const { requestOrData, responseOrSocket, context, server } = this.parseArgs(args); let headers = {}; try { if (server) this.server = server; if (context) this.context = context; this.getHandshakeHeaders(requestOrData, responseOrSocket); if (await this.enableOptions(requestOrData, responseOrSocket, headers, operation)) { return responseOrSocket; } const event = this.formatEvent(requestOrData, operation, singleDefault, replace); await this.runMiddlewares(requestOrData, responseOrSocket); const object = await this.generateObject(useFunction, event); const status = this.generateStatus(operation, object); headers = { ...headers, ...(await this.generateHeaders(requestOrData, responseOrSocket, event)), }; await this.emit(requestOrData, responseOrSocket, headers, operation, status, object); return responseOrSocket; } catch (error) { console.error(error); return this.generateError(requestOrData, responseOrSocket, headers, error, operation); } } async options(...args) { return this.generateEvent(args, flexiblepersistence_1.Operation.other, this.event.bind(this)); } } exports.default = AbstractControllerDefault; //# sourceMappingURL=abstractControllerDefault.js.map