UNPKG

alpha-command-bus-rpc-server

Version:

RPC server over http for alpha-command-bus

124 lines (123 loc) 4.94 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.rpcServer = void 0; const express = require("express"); const alpha_serializer_1 = require("alpha-serializer"); const is = require("predicates"); const asyncHandler = require("express-async-handler"); const common_errors_1 = require("@pallad/common-errors"); const body_parser_1 = require("body-parser"); const typeIs = require("type-is"); const stream_1 = require("stream"); const busboy = require("busboy"); const isReadableStream_1 = require("./isReadableStream"); function createNonCommandBodyError() { return new common_errors_1.RemoteServerError('Request body does not look like a command object. Make sure you have sent proper command object'); } function rpcServer(commandBus, options = {}) { const serializer = options.serializer || alpha_serializer_1.serializer; const router = express.Router(); router.use((0, body_parser_1.raw)({ type: ['application/json'] })); router.use(asyncHandler(async function (req, res) { try { const command = await createCommandFromRequest(req); const result = await commandBus.handle(command); if ((0, isReadableStream_1.isReadableStream)(result)) { res.status(200).header('Content-Type', 'application/octet-stream'); result.pipe(res); } else { res.status(200) .header('Content-Type', 'application/json') .send(serializer.serialize(result)); } (options === null || options === void 0 ? void 0 : options.onResult) && options.onResult(result); } catch (e) { res.status(200) .header('X-command-bus-error', '1') .header('Content-type', 'application/json') .send(serializer.serialize(e)); (options === null || options === void 0 ? void 0 : options.onError) && options.onError(e); } })); function getCommandBodyFromMultipart(serializer, req) { return new Promise((resolve, reject) => { let result = {}; const parser = busboy({ headers: req.headers, fileHwm: 1024 * 1024 * 100 }); parser.on('file', (fieldname, file) => { const finalFile = file; result[fieldname] = finalFile.pipe(new stream_1.Transform({ highWaterMark: finalFile.readableHighWaterMark, transform(chunk, encoding, callback) { this.push(chunk); callback(); } })); }); parser.on('field', (name, value) => { if (name === 'commandBody') { Object.assign(result, serializer.deserialize(value)); } }); parser.on('finish', () => { resolve(result); }); parser.on('error', (e) => { reject(e); }); req.pipe(parser); }); } async function createCommandFromRequest(req) { let command; const mediaType = req.header('content-type'); if (!mediaType) { throw new common_errors_1.RemoteServerError(`Missing content-type`); } if (typeIs.is(mediaType, ['json'])) { const body = req.body; if (is.empty(body)) { throw new common_errors_1.RemoteServerError('Missing body for command'); } try { if (is.string(body)) { command = serializer.deserialize(body); } else if (Buffer.isBuffer(body)) { command = serializer.deserialize(body.toString('utf8')); } else { command = serializer.normalizer.denormalize(body); } } catch (e) { throw new common_errors_1.RemoteServerError(`Cannot deserialize body: ${e.message}`); } } else if (typeIs.is(mediaType, ['multipart'])) { command = await getCommandBodyFromMultipart(serializer, req); } else { throw new common_errors_1.RemoteServerError(`Unsupported content-type: ${req.header('content-type')}`); } if (!command) { throw createNonCommandBodyError(); } if (!is.prop('command', String)(command)) { throw createNonCommandBodyError(); } if (options.prepareCommand) { command = await options.prepareCommand(command, req); } (options === null || options === void 0 ? void 0 : options.onCommand) && options.onCommand(command); return command; } return router; } exports.rpcServer = rpcServer;