UNPKG

qcobjects

Version:

QCObjects is an Open-source framework that empowers full-stack developers to make micro-services and micro-frontends into an N-Tier architecture.

307 lines (287 loc) 13.4 kB
import { IService, TCacheController } from "types"; import { asyncLoad } from "./asyncLoad"; import { ComplexStorageCache } from "./ComplexStorageCache"; import { _DataStringify } from "./DataStringify"; import { logger } from "./Logger"; import { _require_, isBrowser } from "./platform"; import { _top } from "./top"; /** * Loads a simple component from a template * * @author: Jean Machuca <correojean@gmail.com> * @param service a Service object */ export const serviceLoader = function (service:IService, _async = false):Promise<unknown>|undefined { const _serviceLoaderInBrowser = function (service:IService):Promise<unknown> { var _promise = new Promise( function (resolve, reject) { logger.debug("LOADING SERVICE DATA {{DATA}} FROM {{URL}}".replace("{{DATA}}", _DataStringify(service.data)).replace("{{URL}}", service.url)); const xhr = new XMLHttpRequest(); xhr.withCredentials = service.withCredentials; const xhrasync = true; // always async because xhr sync is deprecated xhr.open(service.method, service.url, xhrasync); for (const header in service.headers) { try { if (typeof service.headers[header] !== "function") { xhr.setRequestHeader(header, service.headers[header]); } } catch (e:any) { logger.debug("Something went wrong when assign the header " + header); logger.debug(`An error ocurred: ${e}`); } } xhr.onload = function () { if (xhr.status === 200) { const response = xhr.responseText; logger.debug("Data received {{DATA}}".replace("{{DATA}}", _DataStringify(response))); logger.debug("CREATING SERVICE {{NAME}}".replace("{{NAME}}", service.name)); service.template = response; if (service.cached && (typeof cache !== "undefined")) { cache.save(service.name, service.template); } if (typeof service.done === "function") { var standardResponse = { "request": xhr, service }; service.done.call(service, standardResponse); resolve.call(_promise, standardResponse); } } else { if (typeof service.fail === "function") { var standardResponse = { "request": xhr, service }; service.fail.call(service, standardResponse); reject.call(_promise, standardResponse); } } }; const _directLoad = function () { logger.debug("SENDING THE NORMAL REQUEST "); try { xhr.send(_DataStringify(service.data)); } catch (e:any) { logger.debug("SOMETHING WRONG WITH REQUEST "); logger.debug(`An error ocurred: ${e}`); reject.call(_promise, { request: xhr, service }); } }; if (service.cached) { var cache = new ComplexStorageCache({ index: service.data, load() { _directLoad.call(this); }, alternate(cacheController:TCacheController) { if (service.method === "GET") { service.template = cacheController.cache.getCached(service.name); if (typeof service.done === "function") { const standardResponse = { "request": xhr, service }; service.done.call(service, standardResponse); resolve.call(_promise, standardResponse); } } else { _directLoad(); } } }); (_top as any).lastCache = cache; } else { _directLoad(); } return xhr; } ); return _promise; }; const _serviceLoaderInNode = function (service:IService) { var _promise = new Promise( function (resolve, reject) { if (typeof URL === "undefined") { global.URL = (_require_("url")).URL; // eslint-disable-next-line no-unused-vars const URL = global.URL; } const serviceURL = new URL(service.url); var req; service.useHTTP2 = Object.hasOwn(service, "useHTTP2") && service.useHTTP2; const captureEvents = function (req:any) { logger.debug("LOADING SERVICE DATA (non-browser) {{DATA}} FROM {{URL}}".replace("{{DATA}}", _DataStringify(service.data)).replace("{{URL}}", service.url)); let dataXML:any; const standardResponse = { "http2Client": client, "request": req, service, "responseHeaders": null }; if (typeof service.data === "object" && service.data !== null) { if (service.useHTTP2) { try { logger.debug("Sending data..."); const buffer = new Buffer(_DataStringify(service.data)); req.write(buffer); } catch (e:any) { logger.debug("It was not possible to send any data"); logger.debug(`An error ocurred: ${e}`); } } } dataXML = ""; req.on("response", (responseHeaders:any) => { logger.debug("receiving response..."); standardResponse.responseHeaders = responseHeaders; /* for (const name in responseHeaders) { logger.debug(`${name}: ${responseHeaders[name]}`); } */ dataXML = ""; }); req.on("data", (chunk:any) => { logger.debug("receiving data..."); // do something with the data dataXML += "" + chunk.toString(); service.template = dataXML; }); if (service.useHTTP2) { req.resume(); } req.on("end", () => { logger.debug("ending call..."); service.template = dataXML; if (Object.hasOwn(service, "useHTTP2") && service.useHTTP2) { client.destroy(); } else { req.destroy(); } service.done.call(service, standardResponse); resolve.call(_promise, standardResponse); }); if (service.useHTTP2) { req.end(); } }; try { let requestOptions; if (service.useHTTP2) { logger.debug("using http2"); const http2 = _require_("http2"); var client = (http2).connect(serviceURL.origin); requestOptions = Object.assign({ ":method": service.method, ":path": serviceURL.pathname }, service.options); requestOptions = Object.assign(requestOptions, service.headers); req = client.request(requestOptions); req.setEncoding("utf8"); captureEvents(req); } else { if (serviceURL.protocol === "http:") { const http = _require_("http"); const request = (http).request; requestOptions = Object.assign({ "url": service.url, headers: service.headers }, service.options); req = request(service.url); captureEvents(req); } else if (serviceURL.protocol === "https:") { const https = _require_("https"); requestOptions = Object.assign({ hostname: serviceURL.hostname, port: serviceURL.port, path: serviceURL.pathname, method: service.method, headers: service.headers }, service.options); const _req_ = (https).request(requestOptions, function (req:any) { captureEvents(req); }); _req_.end(); } else { const e = "Protocol not supported: " + serviceURL.protocol; logger.debug(e); throw new Error(e); } } } catch (e:any) { logger.debug(e); service.fail.call(service, e); reject.call(_promise, e); } }).catch( (e:any) => { logger.debug(`Something happened when trying to call the service: ${service.name}. Error: ${e}`); service.fail.call(service, e); }); return _promise; }; const _serviceLoaderMockup = function (service:IService) { var _promise = new Promise( function (resolve) { logger.debug(`Calling mockup service ${service.name} ...`); const standardResponse = { "request": null, service, "responseHeaders": service.responseHeaders }; if (typeof service.mockup === "function") { service.mockup.call(service, standardResponse); } else { service.done.call(service, standardResponse); } resolve.call(_promise, standardResponse); }); return _promise; }; const _serviceLoaderLocal = function (service:IService) { var _promise = new Promise( function (resolve) { logger.debug(`Calling local service ${service.name} ...`); const standardResponse = { "request": null, service, "responseHeaders": service.responseHeaders }; if (typeof service.local === "function") { service.local.call(service, standardResponse); } else { service.done.call(service, standardResponse); } resolve.call(_promise, standardResponse); }); return _promise; }; let _ret_: Promise<unknown>; switch (service.kind) { case "rest": if (isBrowser) { if (typeof _async !== "undefined" && _async) { _ret_ = asyncLoad(_serviceLoaderInBrowser, [service, _async]); } else { _ret_ = _serviceLoaderInBrowser(service); } } else { _ret_ = _serviceLoaderInNode(service); } break; case "mockup": _ret_ = _serviceLoaderMockup(service); break; case "local": _ret_ = _serviceLoaderLocal(service); break; default: logger.debug(`The value of the kind property of the service ${service.name} is not valid`); _ret_ = Promise.resolve(); break; } return _ret_; };