UNPKG

trader-server

Version:

OData server for testing strategies, simulating and real trading.

326 lines 13.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const odata_v4_service_metadata_1 = require("odata-v4-service-metadata"); const odata_v4_service_document_1 = require("odata-v4-service-document"); const odata_v4_metadata_1 = require("odata-v4-metadata"); const ODataParser = require("odata-v4-parser"); const express = require("express"); const bodyParser = require("body-parser"); const cors = require("cors"); const stream_1 = require("stream"); const controller_1 = require("./controller"); const odata = require("./odata"); const odata_1 = require("./odata"); const metadata_1 = require("./metadata"); const processor_1 = require("./processor"); const error_1 = require("./error"); const edm_1 = require("./edm"); const stream_2 = require("stream"); function ensureODataMetadataType(req, res) { let metadata = processor_1.ODataMetadataType.minimal; if (req.headers && req.headers.accept && req.headers.accept.indexOf("odata.metadata=") >= 0) { if (req.headers.accept.indexOf("odata.metadata=full") >= 0) metadata = processor_1.ODataMetadataType.full; else if (req.headers.accept.indexOf("odata.metadata=none") >= 0) metadata = processor_1.ODataMetadataType.none; } res["metadata"] = metadata; } function ensureODataContentType(req, res, contentType) { contentType = contentType || "application/json"; if (contentType.indexOf("odata.metadata=") < 0) contentType += `;odata.metadata=${processor_1.ODataMetadataType[res["metadata"]]}`; if (contentType.indexOf("odata.streaming=") < 0) contentType += ";odata.streaming=true"; if (contentType.indexOf("IEEE754Compatible=") < 0) contentType += ";IEEE754Compatible=false"; if (req.headers.accept && req.headers.accept.indexOf("charset") > 0) { contentType += `;charset=${res["charset"]}`; } res.contentType(contentType); } function ensureODataHeaders(req, res, next) { res.setHeader("OData-Version", "4.0"); ensureODataMetadataType(req, res); let charset = req.headers["accept-charset"] || "utf-8"; res["charset"] = charset; ensureODataContentType(req, res); if ((req.headers.accept && req.headers.accept.indexOf("charset") < 0) || req.headers["accept-charset"]) { const bufferEncoding = { "utf-8": "utf8", "utf-16": "utf16le" }; let origsend = res.send; res.send = ((data) => { if (typeof data == "object") data = JSON.stringify(data); origsend.call(res, Buffer.from(data, bufferEncoding[charset])); }); } if (typeof next == "function") next(); } /** ODataServer base class to be extended by concrete OData Server data sources */ class ODataServerBase extends stream_1.Transform { constructor(opts) { super(Object.assign({ objectMode: true }, opts)); this.serverType = Object.getPrototypeOf(this).constructor; } static requestHandler() { return (req, res, next) => { try { ensureODataHeaders(req, res); let processor = this.createProcessor({ url: req.url, method: req.method, protocol: req.secure ? "https" : "http", host: req.headers.host, base: req.baseUrl, request: req, response: res }, { metadata: res["metadata"] }); processor.on("header", (headers) => { for (let prop in headers) { if (prop.toLowerCase() == "content-type") { ensureODataContentType(req, res, headers[prop]); } else { res.setHeader(prop, headers[prop]); } } }); let hasError = false; processor.on("data", (chunk, encoding, done) => { if (!hasError) { res.write(chunk, encoding, done); } }); let body = req.body && Object.keys(req.body).length > 0 ? req.body : req; let origStatus = res.statusCode; processor.execute(body).then((result) => { try { if (result) { res.status((origStatus != res.statusCode && res.statusCode) || result.statusCode || 200); if (!res.headersSent) { ensureODataContentType(req, res, result.contentType || "text/plain"); } if (typeof result.body != "undefined") { if (typeof result.body != "object") res.send("" + result.body); else if (!res.headersSent) res.send(result.body); } } res.end(); } catch (err) { hasError = true; next(err); } }, (err) => { hasError = true; next(err); }); } catch (err) { next(err); } }; } static execute(url, method, body) { let context = {}; if (typeof url == "object") { context = Object.assign(context, url); if (typeof method == "object") { body = method; } url = undefined; method = undefined; } else if (typeof url == "string") { context.url = url; if (typeof method == "object") { body = method; method = "POST"; } context.method = method || "GET"; } context.method = context.method || "GET"; let processor = this.createProcessor(context, { objectMode: true, metadata: context.metadata || processor_1.ODataMetadataType.minimal }); let values = []; let flushObject; let response = ""; if (context.response instanceof stream_2.Writable) processor.pipe(context.response); processor.on("data", (chunk) => { if (!(typeof chunk == "string" || chunk instanceof Buffer)) { if (chunk["@odata.context"] && chunk.value && Array.isArray(chunk.value) && chunk.value.length == 0) { flushObject = chunk; flushObject.value = values; } else { values.push(chunk); } } else response += chunk.toString(); }); return processor.execute(context.body || body).then((result) => { if (flushObject) { result.body = flushObject; if (!result.elementType || typeof result.elementType == "object") result.elementType = flushObject.elementType; delete flushObject.elementType; result.contentType = result.contentType || "application/json"; } else if (result && response) { result.body = response; } return result; }); } _transform(chunk, _, done) { if ((chunk instanceof Buffer) || typeof chunk == "string") { try { chunk = JSON.parse(chunk.toString()); } catch (err) { return done(err); } } this.serverType.execute(chunk).then((result) => { this.push(result); if (typeof done == "function") done(); }, done); } _flush(done) { if (typeof done == "function") done(); } static createProcessor(context, options) { return new processor_1.ODataProcessor(context, this, options); } static $metadata(metadata) { if (metadata) { if (!(metadata instanceof odata_v4_metadata_1.Edm.Edmx)) { if (metadata.version && metadata.dataServices && Array.isArray(metadata.dataServices.schema)) this._metadataCache = odata_v4_service_metadata_1.ServiceMetadata.processMetadataJson(metadata); else this._metadataCache = odata_v4_service_metadata_1.ServiceMetadata.defineEntities(metadata); } } return this._metadataCache || (this._metadataCache = odata_v4_service_metadata_1.ServiceMetadata.processMetadataJson(metadata_1.createMetadataJSON(this))); } static document() { return odata_v4_service_document_1.ServiceDocument.processEdmx(this.$metadata().edmx); } static addController(controller, entitySetName, elementType) { odata.controller(controller, entitySetName, elementType)(this); } static getController(elementType) { for (let i in this.prototype) { if (this.prototype[i] && this.prototype[i].prototype && this.prototype[i].prototype instanceof controller_1.ODataController && this.prototype[i].prototype.elementType == elementType) { return this.prototype[i]; } } return null; } static create(path, port, hostname) { let server = this; let router = express.Router(); router.use((req, _, next) => { req.url = req.url.replace(/[\/]+/g, "/").replace(":/", "://"); if (req.headers["odata-maxversion"] && req.headers["odata-maxversion"] < "4.0") return next(new error_1.HttpRequestError(500, "Only OData version 4.0 supported")); next(); }); router.use(bodyParser.json()); if (server.cors) router.use(cors()); router.use((req, res, next) => { res.setHeader("OData-Version", "4.0"); if (req.headers.accept && req.headers.accept.indexOf("application/json") < 0 && req.headers.accept.indexOf("text/html") < 0 && req.headers.accept.indexOf("*/*") < 0 && req.headers.accept.indexOf("xml") < 0) { next(new error_1.UnsupportedMediaTypeError()); } else next(); }); router.get("/", ensureODataHeaders, (req, _, next) => { if (typeof req.query == "object" && Object.keys(req.query).length > 0) return next(new error_1.HttpRequestError(500, "Unsupported query")); next(); }, server.document().requestHandler()); router.get("/\\$metadata", server.$metadata().requestHandler()); router.use(server.requestHandler()); router.use(server.errorHandler); if (typeof path == "number") { if (typeof port == "string") { hostname = "" + port; } port = parseInt(path, 10); path = undefined; } if (typeof port == "number") { let app = express(); app.use(path || "/", router); return app.listen(port, hostname); } return router; } } ODataServerBase.container = new edm_1.ContainerBase(); ODataServerBase.parser = ODataParser; ODataServerBase.errorHandler = ODataErrorHandler; exports.ODataServerBase = ODataServerBase; class ODataServer extends odata_1.ODataBase(ODataServerBase) { } exports.ODataServer = ODataServer; /** ?????????? */ /** Create Express middleware for OData error handling */ function ODataErrorHandler(err, _, res, next) { if (err) { if (res.headersSent) { return next(err); } let statusCode = err.statusCode || err.status || (res.statusCode < 400 ? 500 : res.statusCode); if (!res.statusCode || res.statusCode < 400) res.status(statusCode); res.send({ error: { code: statusCode, message: err.message, stack: process.env.ODATA_V4_DISABLE_STACKTRACE ? undefined : err.stack } }); } else next(); } exports.ODataErrorHandler = ODataErrorHandler; /** Create Express server for OData Server * @param server OData Server instance * @param path routing path for Express * @param port port number for Express to listen to * @param hostname hostname for Express * @return Express Router object */ function createODataServer(server, path, port, hostname) { return server.create(path, port, hostname); } exports.createODataServer = createODataServer; //# sourceMappingURL=server.js.map