@n1k1t/mock-server
Version:
Powerful util to setup mocks over HTTP APIs
226 lines • 11.3 kB
JavaScript
;
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