UNPKG

@n1k1t/mock-server

Version:

Powerful util to setup mocks over HTTP APIs

226 lines 11.3 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __exportStar = (this && this.__exportStar) || function(m, exports) { for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Executor = void 0; const node_gzip_1 = require("node-gzip"); const rxjs_1 = require("rxjs"); const rfdc_1 = __importDefault(require("rfdc")); const lodash_1 = __importDefault(require("lodash")); const errors_1 = require("./errors"); const utils_1 = require("../../../utils"); const logger_1 = require("../../../logger"); const context_1 = require("../context"); __exportStar(require("./errors"), exports); const clone = (0, rfdc_1.default)(); const logger = logger_1.Logger.build('Server.Models.Executor'); class Executor { /** Matches expectation */ async match(context) { return context.provider.storages.expectations.match(context.snapshot); } /** Prepares context right after expectation was manipulated */ async prepare(context) { return context; } /** Uses to handle whole request */ async exec(context, options) { const expectation = options?.expectation ? options.expectation : await this.match(context).catch((error) => { logger.error('Got error while execution [matchExpectation] method', error?.stack ?? error); return null; }); if (!expectation) { if (!context.outgoing) { context.provider.storages.history.unregister(context.history); return context; } if (context.history?.hasStatus('registered')) { context.history.switchStatus('pending'); } return context; } context.assign({ snapshot: expectation.request.manipulate(context.snapshot), expectation: expectation.increaseExecutionsCounter(), }); await this .prepare(context) .catch((error) => logger.error('Got error while execution [prepare] method', error?.stack ?? error)); context.provider.server.exchanges.io.publish('expectation:updated', expectation.toPlain()); logger.info('Expectation has matched as', `"${expectation.name}" [${expectation.id}]`); if (context.history?.hasStatus('registered')) { context.history .switchStatus('pending') .actualizeSnapshot(context.snapshot) .assign({ expectation: context.expectation }); context.provider.server.exchanges.io.publish('history:added', context.history.toPlain()); context.provider.server.services.metrics.register('rate', { count: 1 }); } if (!context.hasStatuses(['handling'])) { return context; } if (context.snapshot.incoming.delay) { logger.info(`Has delayed over [${context.snapshot.incoming.delay}ms]`); await (0, utils_1.wait)(context.snapshot.incoming.delay); } if (context.snapshot.incoming.error) { logger.info(`Has destroyed using [${context.snapshot.incoming.error}]`); context.snapshot.assign({ error: { code: context.snapshot.incoming.error, isManual: true } }); throw errors_1.ExecutorManualError.build(context.snapshot.incoming.error); } if (expectation.forward) { const forwarded = await this.handleForwarding(context).catch((error) => { logger.error('Got error while execution [handleForwarding] method', error?.stack ?? error); return null; }); if (forwarded) { context.snapshot.assign({ outgoing: forwarded.outgoing ?? context.snapshot.outgoing, forwarded: { isCached: forwarded.isCached, messages: clone(forwarded.messages), incoming: Object.assign(clone(lodash_1.default.omit(forwarded.incoming, ['stream'])), lodash_1.default.pick(forwarded.incoming, ['stream'])), ...(forwarded.outgoing && { outgoing: Object.assign(clone(lodash_1.default.omit(forwarded.outgoing, ['stream'])), lodash_1.default.pick(forwarded.outgoing, ['stream'])), }), } }); } if (context.snapshot.forwarded && context.history?.hasStatus('pending')) { context.history.snapshot.assign({ cache: context.snapshot.cache, forwarded: { isCached: context.snapshot.forwarded.isCached, incoming: lodash_1.default.omit(context.snapshot.forwarded.incoming, ['stream']), ...(context.snapshot.forwarded.outgoing && { outgoing: lodash_1.default.omit(context.snapshot.forwarded.outgoing, ['stream']) }), }, }); context.provider.server.exchanges.io.publish('history:updated', context.history.toPlain()); } } if (!context.hasStatuses(['handling'])) { return context; } const outgoing = await this.handleReplying(context).catch((error) => { logger.error('Got error while execution [handleReplying] method', error?.stack ?? error); return null; }); return outgoing ? context.assign({ outgoing }) : context; } async handleForwarding(context) { if (!context.expectation?.forward) { return context.snapshot; } const snapshot = context.snapshot.assign({ cache: context.compileCacheConfiguration() }); if (snapshot.cache.isEnabled) { const cached = await context.provider.server.databases.redis.get(snapshot.cache.key).catch((error) => { logger.error('Got error while redis get', error?.stack ?? error); return null; }); const unziped = cached ? await (0, node_gzip_1.ungzip)(Buffer.from(cached, 'base64')).catch((error) => { logger.error('Got error while cache unzip', error?.stack ?? error); return null; }) : null; const parsed = (unziped ? (0, context_1.parsePayload)('json', unziped) : null); const dataRaw = parsed?.outgoing.dataRaw ? Buffer.from(parsed?.outgoing.dataRaw, 'base64') : undefined; if (parsed) { logger.info(`Got cache [${snapshot.cache.key}]`); if (parsed.messages?.length) { parsed.outgoing.stream = (0, rxjs_1.from)(parsed.messages.map((message) => message.data) ?? []); } return Object.assign(snapshot.pick(['incoming']), { isCached: true, messages: parsed.messages, outgoing: Object.assign(lodash_1.default.omit(parsed.outgoing, ['dataRaw']), { data: dataRaw ? (0, context_1.parsePayload)(parsed.outgoing.type, dataRaw) : undefined, dataRaw, }), }); } } const type = (0, context_1.extractPayloadType)(snapshot.incoming.headers) ?? 'plain'; const dataRaw = snapshot.incoming.data === undefined ? snapshot.incoming.dataRaw : typeof snapshot.incoming.data === 'object' ? (0, context_1.serializePayload)(type, snapshot.incoming.data) : Buffer.from(String(snapshot.incoming.data)); const forwarded = await this .forward(context, Object.assign(snapshot.incoming, { type, dataRaw }), context.expectation.forward) .catch((error) => { logger.error('Got error while execution [forward] method', error?.stack ?? error); return null; }); if (forwarded) { forwarded.messages = []; forwarded.outgoing?.stream?.subscribe({ error: () => null, next: (data) => forwarded.messages.push({ data, location: 'outgoing' }), }); } return forwarded; } async handleReplying(context) { const snapshot = context.expectation?.response ? context.expectation.response.manipulate(context.snapshot) : context.snapshot; const type = (0, context_1.extractPayloadType)(snapshot.outgoing.headers) ?? snapshot.outgoing.type; const dataRaw = snapshot.outgoing.data === undefined ? snapshot.outgoing.dataRaw : typeof snapshot.outgoing.data === 'object' ? (0, context_1.serializePayload)(type, snapshot.outgoing.data) : Buffer.from(String(snapshot.outgoing.data)); const outgoing = await this .reply(context, Object.assign(snapshot.outgoing, { type, dataRaw })) .catch((error) => { logger.error('Got error while execution [reply] method', error?.stack ?? error); return null; }); const shouldBeCached = snapshot.cache.isEnabled && snapshot.forwarded?.outgoing && !snapshot.forwarded.isCached && snapshot.cache.ttl && typeof snapshot.cache.key === 'string'; if (shouldBeCached) { const serialized = (0, context_1.serializePayload)('json', (0, utils_1.cast)({ messages: snapshot.forwarded.messages?.filter((message) => message.location === 'outgoing'), outgoing: Object.assign(lodash_1.default.omit(snapshot.forwarded.outgoing, ['data']), { dataRaw: snapshot.forwarded.outgoing?.dataRaw?.toString('base64'), }), })); if (!serialized) { return outgoing; } const zipped = await (0, node_gzip_1.gzip)(serialized).catch((error) => { logger.error('Got error while zip payload', error?.stack ?? error); return null; }); if (zipped) { await context.provider.server.databases.redis.setex(snapshot.cache.key, snapshot.cache.ttl, zipped.toString('base64')) .then(() => logger.info(`Wrote cache [${snapshot.cache.key}] for [${snapshot.cache.ttl}] seconds`)) .catch((error) => logger.error('Got error while redis set', error?.stack ?? error)); } } return outgoing; } } exports.Executor = Executor; //# sourceMappingURL=index.js.map