backapi
Version:
A simple API framework using Flexible Persistence
566 lines • 22.2 kB
JavaScript
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
;