UNPKG

castelog

Version:

Programación JavaScript en castellano.

990 lines (984 loc) 189 kB
//RestUtils.require("fs").writeFileSync(__dirname + "/../process.json", "" + process.pid, "utf8"); (function (scope, factory) { const jsmodule = factory(); if (typeof window !== "undefined") { window.Automatic_http_rest_api_interface = jsmodule; } if (typeof global !== "undefined") { global.Automatic_http_rest_api_interface = jsmodule; } if (typeof define === "function") { define("Automatic_http_rest_api_interface", jsmodule); } if (typeof module !== "undefined") { module.exports = jsmodule; } return jsmodule; })(this, () => function (factoryParameters) { const defaultConfigurations = { platform: "node", // "browser" environment: "production", // "development", "testing" debug: true, debugSQL: true, debugErrors: true, responseWrapper: { app: { title: "Automatic HTTP REST API development", author: { name: "allnulled", telephone: "+34 619 98 26 22", url: "https://www.github.com/allnulled", } } }, traceCallback: function (id) { if (configurations.debug) { if (configurations.platform === "node") { console.log("\u001b[32m[TRACE]\u001b[0m " + id); } else { console.log("[TRACE] " + id); } } }, traceSQLCallback: function (id) { if (configurations.debugSQL) { if (configurations.platform === "node") { console.log("\u001b[33m[·SQL·]\u001b[0m " + id.split("\n").join("\n ")); } else { console.log("[·SQL·] " + id); } } }, traceErrorCallback: function (error) { try { if (configurations.debugErrors) { let id = undefined; if (typeof error === "string") { id = error; } else if (error instanceof Error) { id = error.name + ": " + error.message + "\n " + error.stack + ""; } if (configurations.platform === "node") { console.log("\u001b[31m[ERROR]\u001b[0m " + id.split("\n").join("\n ")); } else { console.log("[ERROR] " + id); } } } catch (error) { console.log(error); } }, }; const configurations = Object.assign(defaultConfigurations, factoryParameters); const trace = configurations.traceCallback; const traceSQL = configurations.traceSQLCallback; const traceError = configurations.traceErrorCallback; const RestClient = function (baseUrl, client = {}) { Object.assign(client, { defaults: { headers: { common: { // @CONFIGURABLE } } }, request: (method, url, requestArgs = {}, requestConfigArgs = {}, responseArgs = {}, ...args) => { trace("DataServer.prototype.createClient:request"); const parsedUrl = RestUtils.require("url").parse(url); trace("RestClient is requesting: " + url); const requestParameters = Object.assign({}, requestArgs); const responseParameters = Object.assign({}, responseArgs); if (configurations.platform === "browser") { const queryParameters = Object.assign({}, requestParameters); requestParameters.headers = Object.assign({}, client.defaults.headers.common, requestConfigArgs.headers || {}); requestParameters.query = queryParameters; if (parsedUrl.pathname.startsWith(client.server.basePathForData)) { return client.server.dispatchSelf(method, url, requestParameters, responseParameters, ...args); } else if (parsedUrl.pathname.startsWith(client.server.basePathForAuth)) { return client.server.dispatchSelf(method, url, requestParameters, responseParameters, ...args); } else if (parsedUrl.pathname.startsWith(client.server.basePathForProcess)) { return client.server.dispatchSelf(method, url, requestParameters, responseParameters, ...args); } else if (parsedUrl.pathname.startsWith(client.server.basePathForQuery)) { return client.server.dispatchSelf(method, url, requestParameters, responseParameters, ...args); } else { throw new Error("Required parameter «url» to start as a valid basepath on browser in order to «createClient:request»"); } } else if (configurations.platform === "node") { const requestConfigParameters = Object.assign({}, { headers: {} }, requestConfigArgs); Object.assign(requestConfigParameters.headers, client.defaults.headers.common,) if (parsedUrl.pathname.startsWith(client.server.basePathForData)) { return require("axios").create()[method](url, requestParameters, requestConfigParameters, ...args); } else if (parsedUrl.pathname.startsWith(client.server.basePathForAuth)) { return require("axios").create()[method](url, requestParameters, requestConfigParameters, ...args); } else if (parsedUrl.pathname.startsWith(client.server.basePathForProcess)) { return require("axios").create()[method](url, requestParameters, requestConfigParameters, ...args); } else if (parsedUrl.pathname.startsWith(client.server.basePathForQuery)) { return require("axios").create()[method](url, requestParameters, requestConfigParameters, ...args); } else { throw new Error("Required parameter «url» to start as a valid basepath on browser in order to «createClient:request»"); } } else { throw new Error("Required configuration «platform» to be a valid platform on node in order to «createClient:request»"); } }, auth: { login: (user, password) => { trace("DataServer.prototype.createClient:auth:login"); const finalUrl = baseUrl + RestUtils.require("path").join(client.server.basePathForAuth, "/login"); return this.request("get", finalUrl + "?" + new URLSearchParams({ user, password, }).toString()); }, logout: (session_token) => { trace("DataServer.prototype.createClient:auth:logout"); const finalUrl = baseUrl + RestUtils.require("path").join(client.server.basePathForAuth, "/logout"); return this.request("get", finalUrl + "?" + new URLSearchParams({ session_token: session_token, }).toString()); }, register: (user, password, email) => { trace("DataServer.prototype.createClient:auth:register"); const finalUrl = baseUrl + RestUtils.require("path").join(client.server.basePathForAuth, "/register"); return this.request("get", finalUrl + "?" + new URLSearchParams({ user, password, email, }).toString()); }, confirm: (token) => { trace("DataServer.prototype.createClient:auth:confirm"); const finalUrl = baseUrl + RestUtils.require("path").join(client.server.basePathForAuth, "/confirm"); return this.request("get", finalUrl + "?" + new URLSearchParams({ token, }).toString()); }, forgot: (user) => { trace("DataServer.prototype.createClient:auth:forgot"); const finalUrl = baseUrl + RestUtils.require("path").join(client.server.basePathForAuth, "/forgot"); return this.request("get", finalUrl + "?" + new URLSearchParams({ user, }).toString()); }, recover: (token) => { trace("DataServer.prototype.createClient:auth:recover"); const finalUrl = baseUrl + RestUtils.require("path").join(client.server.basePathForAuth, "/recover"); return this.request("get", finalUrl + "?" + new URLSearchParams({ token, }).toString()); }, unregister: (user, password) => { trace("DataServer.prototype.createClient:auth:unregister"); const finalUrl = baseUrl + RestUtils.require("path").join(client.server.basePathForAuth, "/unregister"); return this.request("get", finalUrl + "?" + new URLSearchParams({ user, password, }).toString()); }, }, rest: { selectOne: (model, where) => { trace("DataServer.prototype.createClient:rest:selectOne"); const finalUrl = baseUrl + RestUtils.require("path").join(client.server.basePathForData, model, "/select/one"); trace(finalUrl); return this.request("get", finalUrl + "?" + new URLSearchParams({ where: JSON.stringify(where) }).toString()); }, selectMany: (model, where = [], order = [], group = [], pagination = []) => { trace("DataServer.prototype.createClient:rest:selectMany"); const finalUrl = baseUrl + RestUtils.require("path").join(client.server.basePathForData, model, "/select/many"); trace(finalUrl); return this.request("get", finalUrl + "?" + new URLSearchParams({ where: JSON.stringify(where), group: JSON.stringify(group), order: JSON.stringify(order), pagination: JSON.stringify(pagination) }).toString()); }, insertOne: (model, item) => { trace("DataServer.prototype.createClient:rest:insertOne"); const finalUrl = baseUrl + RestUtils.require("path").join(client.server.basePathForData, model, "/insert/one"); trace(finalUrl); return this.request("get", finalUrl + "?" + new URLSearchParams({ item: JSON.stringify(item), }).toString()); }, insertMany: (model, items) => { trace("DataServer.prototype.createClient:rest:insertMany"); const finalUrl = baseUrl + RestUtils.require("path").join(client.server.basePathForData, model, "/insert/many"); trace(finalUrl); return this.request("get", finalUrl + "?" + new URLSearchParams({ items: JSON.stringify(items), }).toString()); }, updateOne: (model, where, values) => { trace("DataServer.prototype.createClient:rest:updateOne"); const finalUrl = baseUrl + RestUtils.require("path").join(client.server.basePathForData, model, "/update/one"); trace(finalUrl); return this.request("get", finalUrl + "?" + new URLSearchParams({ where: JSON.stringify(where), values: JSON.stringify(values), }).toString()); }, updateMany: (model, where, values) => { trace("DataServer.prototype.createClient:rest:updateMany"); const finalUrl = baseUrl + RestUtils.require("path").join(client.server.basePathForData, model, "/update/many"); trace(finalUrl); return this.request("get", finalUrl + "?" + new URLSearchParams({ where: JSON.stringify(where), values: JSON.stringify(values), }).toString()); }, deleteOne: (model, where) => { trace("DataServer.prototype.createClient:rest:deleteOne"); const finalUrl = baseUrl + RestUtils.require("path").join(client.server.basePathForData, model, "/delete/one"); trace(finalUrl); return this.request("get", finalUrl + "?" + new URLSearchParams({ where: JSON.stringify(where), }).toString()); }, deleteMany: (model, where) => { trace("DataServer.prototype.createClient:rest:deleteMany"); const finalUrl = baseUrl + RestUtils.require("path").join(client.server.basePathForData, model, "/delete/many"); trace(finalUrl); return this.request("get", finalUrl + "?" + new URLSearchParams({ where: JSON.stringify(where), }).toString()); }, }, queries: client.server.queries.reduce((output, item) => { output[item.id] = (parameters, ...args) => { trace("DataServer.prototype.createClient:queries:" + item.id); const finalUrl = baseUrl + RestUtils.require("path").join(client.server.basePathForQuery, item.path); trace(finalUrl); return this.request("get", finalUrl + "?" + new URLSearchParams(parameters), ...args); }; return output; }, {}), processes: client.server.processes.reduce((output, item) => { output[item.id] = (parameters, ...args) => { trace("DataServer.prototype.createClient:processes:" + item.id); const finalUrl = baseUrl + RestUtils.require("path").join(client.server.basePathForProcess, item.path); trace(finalUrl); return this.request("get", finalUrl + "?" + new URLSearchParams(parameters), ...args); }; return output; }, {}) }); return Object.assign(this, client); }; //////////////////////////////////////////////////////////////////////// // -1. Hooks class: const Hooks = function () { this.hooks = {}; }; Hooks.create = function (...args) { trace("Hooks.create"); return new Hooks(...args); }; Hooks.prototype.addHook = function (selectorString, hookId, event) { trace("Hooks.prototype.addHook"); if (typeof selectorString !== "string") { throw new Error("Required argument «selectorString» to be an array in order to «addHook»"); } else if (selectorString.length === 0) { throw new Error("Required argument «selectorString» to have one or more items in order to «addHook»"); } if (typeof hookId !== "string") { throw new Error("Required argument «hookId» to be a string in order to «addHook»"); } if (typeof event !== "function") { throw new Error("Required argument «event» to be a function in order to «addHook»"); } if (!(selectorString in this.hooks)) { this.hooks[selectorString] = []; } this.hooks[selectorString].push({ id: hookId, event }); return this; }; Hooks.prototype.addHooks = function (selectorList, hookId, event) { trace("Hooks.prototype.addHooks"); if (!Array.isArray(selectorList)) { throw new Error("Required argument «selectorList» to be an array in order to «addHooks»"); } else if (selectorList.length === 0) { throw new Error("Required argument «selectorList» to have one or more items in order to «addHooks»"); } if (typeof hookId !== "string") { throw new Error("Required argument «hookId» to be a string in order to «addHooks»"); } if (typeof event !== "function") { throw new Error("Required argument «event» to be a function in order to «addHooks»"); } const selectors = Array.isArray(selectorList) ? selectorList : [selectorList]; for (let indexSelectors = 0; indexSelectors < selectors.length; indexSelectors++) { const selector = selectors[indexSelectors]; this.addHook(selector, hookId, event); } return this; }; Hooks.prototype.useHook = async function (selectorString, parameters = {}) { trace("Hooks.prototype.useHook"); try { trace("Throwing hook: " + selectorString); if (typeof selectorString !== "string") { throw new Error("Required argument «selectorString» to be a string in order to «useHook»"); } if (typeof parameters !== "object") { throw new Error("Required argument «parameters» to be an object or omitted in order to «useHook»"); } if (!(selectorString in this.hooks)) { return parameters; } const hookEvents = this.hooks[selectorString]; IteratingSelection: for (let indexList = 0; indexList < hookEvents.length; indexList++) { const hookEvent = hookEvents[indexList]; let hookFunction = undefined; if (typeof hookEvent === "object") { if (typeof hookEvent.event === "function") { hookFunction = hookEvent.event; } else throw new Error("Required hook «" + selectorString + "» on index «" + indexList + "» on property «event» to be a function in order to «useHook»"); } else throw new Error("Required hook «" + selectorString + "» on index «" + indexList + "» to be an object in order to «useHook»"); const result = await hookFunction(parameters); if (typeof result !== "undefined") { parameters = result; } } return parameters; } catch (error) { this.onError(error); } }; Hooks.prototype.replaceHook = function (selectorString, hookId, eventSource) { trace("Hooks.prototype.replaceHook"); try { if (typeof selectorString !== "string") { throw new Error("Required argument «selectorString» to be a string in order to «replaceHook»"); } if (!(selectorString in this.hooks)) { throw new Error("Required argument «selectorString» to be a valid hook id in order to «replaceHook»"); } if (typeof hookId !== "string") { throw new Error("Required argument «hookId» to be a string in order to «replaceHook»"); } if (typeof eventSource !== "object") { throw new Error("Required argument «eventSource» to be an object in order to «replaceHook»"); } if (typeof eventSource.id !== "string") { throw new Error("Required argument «eventSource.id» to be a string in order to «replaceHook»"); } if (typeof eventSource.event !== "function") { throw new Error("Required argument «eventSource.event» to be a function in order to «replaceHook»"); } let count = 0; const hookEvents = this.hooks[selectorString]; IteratingSelection: for (let indexList = 0; indexList < hookEvents.length; indexList++) { const hookEvent = hookEvents[indexList]; if (hookEvent.id === hookId) { this.hooks[selectorString].splice(indexList, 1, eventSource); count++; } } return count; } catch (error) { this.onError(error); } }; Hooks.prototype.removeHook = function (selectorString, hookId) { trace("Hooks.prototype.removeHook"); try { if (typeof selectorString !== "string") { throw new Error("Required argument «selectorString» to be a string in order to «removeHook»"); } if (!(selectorString in this.hooks)) { throw new Error("Required argument «selectorString» to be a valid hook id in order to «removeHook»"); } if (typeof hookId !== "string") { throw new Error("Required argument «hookId» to be an string in order to «removeHook»"); } let count = 0; const hookEvents = this.hooks[selectorString]; IteratingSelection: for (let indexList = 0; indexList < hookEvents.length; indexList++) { const hookEvent = hookEvents[indexList]; if (typeof hookEvent === "object") { if (hookEvent.id === hookId) { this.hooks[selectorString].splice(indexList, 1); count++; } } } return count; } catch (error) { this.onError(error); } }; //////////////////////////////////////////////////////////////////////// // 0. RestUtils object: const RestUtils = { prototype: {}, modules: {}, modulePolyfills: { "mysql2/promise": { createConnection: function () { trace("polyfill://require:mysql2/promise:createConnection"); return { ping() { trace("polyfill://require:mysql2/promise:createConnection:ping"); // throw new Error("Method ping must be overriden"); }, query(query, ...args) { trace("polyfill://require:mysql2/promise:createConnection:query"); console.log("Function «RestUtils.modulePolyfills.mysql2/promises.createConnection.query» to be overwritten"); console.log(query); return [[], []]; }, proxifiedQuery(query, ...args) { trace("polyfill://require:mysql2/promise:createConnection:proxifiedQuery"); traceSQL(query); return this.query(query, ...args); } } } }, "url": { parse: function (parameter) { trace("polyfill://require:url:parse x " + parameter); return new URL(parameter); }, }, "sqlstring": { sanitize: function (arg) { trace("polyfill://require:sqlstring:sanitize"); return arg; }, sanitizeId: function (arg) { trace("polyfill://require:sqlstring:sanitizeId"); return arg; }, }, "path": { resolve: function (...args) { trace("polyfill://require:path:resolve"); return ["", ...args].map(i => i.replace(/^\//g, "").replace(/\/$/g, "")).join("/"); }, join: function (...args) { trace("polyfill://require:path:join"); return ["", ...args].filter(i => typeof i !== "undefined").map(i => i.replace(/^\//g, "").replace(/\/$/g, "")).join("/"); }, }, "http": { createServer: function (controller) { trace("polyfill://require:http:createServer"); return { listen: function (options, callback) { trace("polyfill://require:http:createServer:listen"); setTimeout(callback, 0); } } } }, }, require: function (modulepath) { trace("RestUtils.require"); if (modulepath in RestUtils.modules) { return RestUtils.modules[modulepath]; } if (configurations.platform === "browser") { if (modulepath in RestUtils.modulePolyfills) { return RestUtils.modulePolyfills[modulepath]; } else { throw new Error("Not identified package: " + modulepath); } } RestUtils.modules[modulepath] = require(modulepath); return RestUtils.modules[modulepath]; }, define: function (name, value) { trace("RestUtils.define"); RestUtils.modules[name] = value; }, definePolyfill: function (name, value) { trace("RestUtils.definePolyfill"); RestUtils.modulePolyfills[name] = value; }, wrapResponse: function (data) { trace("RestUtils.wrapResponse"); return { ...configurations.responseWrapper, ...data, time: new Date().toString() }; }, respondContext: function (context) { trace("RestUtils.respondContext"); const response = context.output; const responseWrapped = RestUtils.wrapResponse(response); const responseJson = JSON.stringify(responseWrapped, null, 2); context.response.writeHead(200, { "Content-type": "application/json" }); context.response.write(responseJson); return context.response.end(); }, generateRandomToken: function (len, alphabet = "abcdefghijklmnopqrstuvwxyz0123456789") { trace("RestUtils.generateRandomToken"); let output = ""; for (let index = 0; index < len; index++) { output += alphabet[Math.floor(Math.random() * alphabet.length)]; } return output; }, generateOnErrorFunction: function (id) { trace("RestUtils.generateOnErrorFunction"); return function (error, propagate = true) { trace(id); traceError(error); if (propagate) { throw error; } }; }, generateContextByRequestResponseFactory: function (id) { trace("RestUtils.generateContextByRequestResponseFactory"); return function (request, response) { trace(id); const parsedURL = RestUtils.require("url").parse(request.url, true); return { output: {}, parameters: {}, state: {}, input: { url: parsedURL.pathname, query: RestUtils.fromURLToQuerystringObject(request.url), body: request.body, params: request.params, request, response }, request, response, }; }; }, generateOnDispatchErrorFunction: function (traceId) { trace("RestUtils.generateOnDispatchErrorFunction"); return function (error, request, response) { trace(traceId); traceError(error); response.writeHead(500, { "Content-Type": "application/json" }); const data = { name: error.name, message: error.message, stack: Object.assign({}, error.stack.split(/\n /g)) }; const wrappedData = RestUtils.wrapResponse({ status: "error", error: data }); const wrappedJson = JSON.stringify(wrappedData, null, 2); response.write(wrappedJson); return response.end(); }; }, noop: function () { }, basicServiceFactory: function () { trace("RestUtils.basicServiceFactory"); return function (modifications) { trace("RestUtils.basicServiceFactory:Service"); Object.assign(this, modifications); return this; } }, basicQueryFactory: function () { trace("RestUtils.basicQueryFactory"); return function () { trace("RestUtils.basicQueryFactory:Query"); return this; } }, basicProcessFactory: function () { trace("RestUtils.basicProcessFactory"); return function () { trace("RestUtils.basicProcessFactory:Process"); return this; } }, basicControllerFallback: function (request, response) { trace("RestUtils.basicControllerFallback"); response.writeHead(404); response.write("Error 404: Page was not found."); return response.end(); }, sanitize: function (value) { trace("RestUtils.sanitize"); return RestUtils.require("sqlstring").escape(value); }, sanitizeId: function (id) { trace("RestUtils.sanitizeId"); return RestUtils.require("sqlstring").escapeId(id); }, availableOperators: { "<": "<", "<=": "<=", ">": ">", ">=": ">=", "=": "=", "!=": "!=", "in": "IN", "!in": "NOT IN", // "contains": "contains", // "!contains": "!contains", }, validateStaticServiceInterface: function (staticInterface) { trace("RestUtils.validateStaticServiceInterface"); if (typeof staticInterface !== "function") { throw new Error("Required parameter «staticInterface» to be an function in order to «validateStaticServiceInterface»") } if (typeof staticInterface.table !== "string") { throw new Error("Required parameter «staticInterface.table» to be a string in order to «validateStaticServiceInterface»") } if (typeof staticInterface.path !== "string") { throw new Error("Required parameter «staticInterface.path» to be a string in order to «validateStaticServiceInterface»") } if (typeof staticInterface.creationScript !== "string") { throw new Error("Required parameter «staticInterface.creationScript» to be a string in order to «validateStaticServiceInterface»") } if (typeof staticInterface.schema !== "object") { throw new Error("Required parameter «staticInterface.schema» to be an object in order to «validateStaticServiceInterface»") } return true; }, validateDynamicServiceInterface: function (dynamicInterface) { trace("RestUtils.validateDynamicServiceInterface"); return true; }, validateStaticQueryInterface: function (staticInterface) { trace("RestUtils.validateStaticQueryInterface"); if (typeof staticInterface.path !== "string") { throw new Error("Required parameter «staticInterface.path» to be a string in order to «validateStaticQueryInterface»") } if (typeof staticInterface.query !== "function") { throw new Error("Required parameter «staticInterface.query» to be a function in order to «validateStaticQueryInterface»") } return true; }, validateDynamicQueryInterface: function (dynamicInterface) { trace("RestUtils.validateDynamicQueryInterface"); return true; }, validateStaticProcessInterface: function (staticInterface) { trace("RestUtils.validateStaticProcessInterface"); if (typeof staticInterface.path !== "string") { throw new Error("Required parameter «staticInterface.path» to be a string in order to «validateStaticProcessInterface»") } if (typeof staticInterface.process !== "function") { throw new Error("Required parameter «staticInterface.process» to be a function in order to «validateStaticProcessInterface»") } return true; }, validateDynamicProcessInterface: function (dynamicInterface) { trace("RestUtils.validateDynamicProcessInterface"); return true; }, expandConnection: function (connection) { trace("RestUtils.expandConnection"); connection.proxifiedQuery = function (query) { trace("RestUtils.expandConnection:connection.proxifiedQuery"); traceSQL(query); return this.query(query); } return connection; }, formatTableFromRequest: function (context, { start, end }) { trace("RestUtils.formatTableFromRequest"); return RestUtils.require("url").parse(context.request.url).pathname .replace(start, "") .split("") .reverse() .join("") .replace(end.split("").reverse().join(""), "") .split("") .reverse() .join(""); }, formatWhereFromRequest: function (context) { trace("RestUtils.formatWhereFromRequest"); const where = context.input.query.where || "[]"; if (typeof where === "object") { return where; } let whereData = undefined; try { whereData = JSON.parse(where); } catch (error) { throw new Error("Required parameter «where» to be a well-formed JSON object in order to «formatWhereFromRequest»"); } if (!Array.isArray(whereData)) { throw new Error("Required parameter «where» to be a JSON array in order to «formatWhereFromRequest»"); } if (whereData.length === 0) { return whereData; } for (let index = 0; index < whereData.length; index++) { const whereRule = whereData[index]; if (!Array.isArray(whereRule)) { throw new Error("Required parameter «where[" + index + "]» to be an array in order to «formatWhereFromRequest»"); } if (whereRule.length < 3) { throw new Error("Required parameter «where[" + index + "]» to be an array of 3 or more items in order to «formatWhereFromRequest»"); } if (typeof whereRule[0] !== "string") { throw new Error("Required parameter «where[" + index + "][0]» to be a string in order to «formatWhereFromRequest»"); } if (typeof whereRule[1] !== "string") { throw new Error("Required parameter «where[" + index + "][1]» to be a string in order to «formatWhereFromRequest»"); } if (!(whereRule[1] in RestUtils.availableOperators)) { throw new Error("Required parameter «where[" + index + "][1]» to be a valid query operator in order to «formatWhereFromRequest»"); } } return whereData; }, formatOrderFromRequest: function (context) { trace("RestUtils.formatOrderFromRequest"); const order = context.input.query.order || "[]"; let orderData = undefined; if (typeof order === "object") { orderData = order; } else if (typeof order !== "string") { throw new Error("Required parameter «context.input.query.order» to be an (optionally JSON) array in order to «formatOrderFromRequest»"); } else { try { orderData = JSON.parse(order); } catch (error) { throw new Error("Required parameter «order» to be a well-formed JSON object in order to «formatOrderFromRequest»"); } } if (!Array.isArray(orderData)) { throw new Error("Required parameter «order» to be a JSON array in order to «formatOrderFromRequest»"); } if (orderData.length === 0) { orderData.push("id"); } for (let index = 0; index < orderData.length; index++) { const orderRule = orderData[index]; if (typeof orderRule !== "string") { throw new Error("Required parameter «order[" + index + "]» to be a string in order to «formatOrderFromRequest»"); } } return orderData; }, formatGroupFromRequest: function (context) { trace("RestUtils.formatGroupFromRequest"); const group = context.input.query.group || "[]"; let groupData = undefined; if (typeof group === "object") { groupData = group; } else if (typeof group !== "string") { throw new Error("Required parameter «context.input.query.group» to be an (optionally JSON) array in order to «formatGroupFromRequest»"); } else { try { groupData = JSON.parse(group); } catch (error) { throw new Error("Required parameter «group» to be a well-formed JSON object in group to «formatGroupFromRequest»"); } } if (!Array.isArray(groupData)) { throw new Error("Required parameter «group» to be a JSON array in group to «formatGroupFromRequest»"); } if (groupData.length === 0) { return groupData; } for (let index = 0; index < groupData.length; index++) { const groupRule = groupData[index]; if (typeof groupRule !== "string") { throw new Error("Required parameter «group[" + index + "]» to be a string in group to «formatGroupFromRequest»"); } } return groupData; }, formatPaginationFromRequest: function (context) { trace("RestUtils.formatPaginationFromRequest"); const pagination = context.input.query.pagination || "[1,20]"; let paginationData = undefined; if (typeof pagination === "object") { paginationData = pagination; } else if (typeof pagination !== "string") { throw new Error("Required parameter «context.input.query.pagination» to be an (optionally JSON) array in order to «formatPaginationFromRequest»"); } else { try { paginationData = JSON.parse(pagination); } catch (error) { throw new Error("Required parameter «pagination» to be a well-formed JSON object in pagination to «formatPaginationFromRequest»"); } } if (!Array.isArray(paginationData)) { throw new Error("Required parameter «pagination» to be a JSON array in pagination to «formatPaginationFromRequest»"); } if (paginationData.length === 0) { return paginationData; } const [page = 1, items = 20] = paginationData; if (typeof page !== "number") { throw new Error("Required parameter «page» to be a number in order to «formatPaginationFromRequest»"); } if (typeof items !== "number") { throw new Error("Required parameter «items» to be a number in order to «formatPaginationFromRequest»"); } if (page < 0) { throw new Error("Required parameter «pagination[0]» to be a number higher or equal to 0 in order to «formatPaginationFromRequest»"); } if (items < 1) { throw new Error("Required parameter «pagination[1]» to be a number higher or equal to 1 in order to «formatPaginationFromRequest»"); } return paginationData; }, formatItemFromRequest: function (context) { trace("RestUtils.formatItemFromRequest"); const item = context.input.query.item || "{}"; let itemData = undefined; if (typeof item === "object") { itemData = item; } else if (typeof item !== "string") { throw new Error("Required parameter «context.input.query.item» to be an (optionally JSON) object in order to «formatItemFromRequest»"); } else { try { itemData = JSON.parse(item); } catch (error) { throw new Error("Required parameter «item» to be a well-formed JSON object to «formatItemFromRequest»"); } } if (typeof itemData !== "object") { throw new Error("Required parameter «item» to be a JSON object to «formatItemFromRequest»"); } return itemData; }, formatItemsFromRequest: function (context) { trace("RestUtils.formatItemsFromRequest"); const items = context.input.query.items || "{}"; let itemsData = undefined; if (typeof items === "object") { itemsData = items; } else if (typeof items !== "string") { throw new Error("Required parameter «context.input.query.items» to be an (optionally JSON) object in order to «formatItemsFromRequest»"); } else { try { itemsData = JSON.parse(items); } catch (error) { throw new Error("Required parameter «item» to be a well-formed JSON object to «formatItemsFromRequest»"); } } if (!Array.isArray(itemsData)) { throw new Error("Required parameter «items» to be an (optionally JSON) array in order to «formatItemsFromRequest»"); } for (let index = 0; index < itemsData.length; index++) { const itemData = itemsData[index]; if (typeof itemData !== "object") { throw new Error("Required parameter «items[" + index + "]» to be a JSON object to «formatItemsFromRequest»"); } } return itemsData; }, formatValuesFromRequest: function (context) { trace("RestUtils.formatValuesFromRequest"); const values = context.input.query.values || "{}"; let valuesData = undefined; if (typeof values === "object") { valuesData = values; } else if (typeof values !== "string") { throw new Error("Required parameter «context.input.query.values» to be an (optionally JSON) object in order to «formatValuesFromRequest»"); } else { try { valuesData = JSON.parse(values); } catch (error) { throw new Error("Required parameter «values» to be a well-formed JSON object to «formatValuesFromRequest»"); } } if (Array.isArray(valuesData)) { throw new Error("Required parameter «values» to be an object and not an array in order to «formatValuesFromRequest»"); } else if (typeof valuesData !== "object") { throw new Error("Required parameter «values» to be an object in order to «formatValuesFromRequest»"); } return valuesData; }, fromWhereToSQL: function (where, wholeClause = false) { trace("RestUtils.fromWhereToSQL"); if (!Array.isArray(where)) { throw new Error("Required parameter «where» to be an array in order to «fromWhereToSQL»"); } if (where.length === 0) { return "# No filtering rules"; } let query = where.map((whereRule, index) => { const sanitizedSubject = RestUtils.sanitizeId(whereRule[0]); const sanitizedOperation = RestUtils.availableOperators[whereRule[1]]; const unsanitizedObject = whereRule[2]; const thirdArgumentMode = whereRule[3] || "default"; let sanitizedObject = whereRule[2]; if (thirdArgumentMode === "default") { if ((typeof unsanitizedObject !== "string") && (typeof unsanitizedObject !== "number")) { throw new Error("Required argument «where[" + index + "][2]» to be a string or a number (on «default» mode) in order to «RestUtils.fromWhereToSQL»"); } sanitizedObject = RestUtils.sanitize(unsanitizedObject); } else if (thirdArgumentMode === "column") { throw new Error("Required argument «where[" + index + "][3]» to be a valid mode and 'column' mode is not allowed in order to «RestUtils.fromWhereToSQL»"); sanitizedObject = RestUtils.sanitizeId(unsanitizedObject); } else if (thirdArgumentMode === "null") { sanitizedObject = "NULL"; } else if (thirdArgumentMode === "array") { let parsedObject = undefined; if (typeof unsanitizedObject === "string") { try { parsedObject = JSON.parse(unsanitizedObject); } catch (error) { throw new Error("Required argument «where[" + index + "][3]» to be a well-formed JSON in order to «RestUtils.fromWhereToSQL»"); } } else if (Array.isArray(unsanitizedObject)) { parsedObject = unsanitizedObject; } else { throw new Error("Required argument «where[" + index + "][2]» to be an (optionally JSON) array in order to «RestUtils.fromWhereToSQL»"); } if (!Array.isArray(parsedObject)) { throw new Error("Required argument «where[" + index + "][2]» to be a JSON array in order to «RestUtils.fromWhereToSQL»"); } else if (parsedObject.length === 0) { throw new Error("Required argument «where[" + index + "][2]» to be a JSON array with 1 or more items in order to «RestUtils.fromWhereToSQL»"); } sanitizedObject = "(" + parsedObject.map(item => RestUtils.sanitize(item)) + ")"; } else { throw new Error("Required argument «where[" + index + "][3]» to be a a known mode in order to «RestUtils.fromWhereToSQL»"); } return ` AND ${sanitizedSubject} ${sanitizedOperation} ${sanitizedObject}`; }).join("\n"); if (wholeClause) { query = query.replace(' AND ', ' WHERE '); } return query; }, fromOrderToSQL: function (order, wholeClause = true) { trace("RestUtils.fromOrderToSQL"); if (!Array.isArray(order)) { throw new Error("Required parameter «order» to be an array in order to «fromOrderToSQL»"); } if (order.length === 0) { return "# No ordering rules"; } let query = order.map((orderRule, index) => { if (typeof orderRule !== "string") { throw new Error("Required parameter «order[" + index + "]» to be a string in order «fromOrderToSQL»"); } const isDescending = orderRule.startsWith("!"); const orderColumn = isDescending ? orderRule.substr(1) : orderRule; return ", " + RestUtils.sanitizeId(orderColumn) + (isDescending ? ' DESC' : ' ASC'); }).join(", "); if (wholeClause) { query = query.replace(', ', ' ORDER BY '); } return query; }, fromGroupToSQL: function (groups, wholeClause = true) { trace("RestUtils.fromGroupToSQL"); if (!Array.isArray(groups)) { throw new Error("Required parameter «groups» to be an array in order to «fromGroup