unmock-core
Version:
[][npmjs] [](https://circleci.com/gh/unmock/unmock-js) [](h
180 lines • 7.19 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const jsf = require("json-schema-faker");
const lodash_1 = require("lodash");
function firstOrRandomOrUndefined(arr) {
return arr.length === 0
? undefined
: arr.length === 1
? arr[0]
: arr[Math.floor(Math.random() * arr.length)];
}
function responseCreatorFactory({ listeners = [], options, store, }) {
const match = (sreq) => Object.values(store.cores)
.map(service => service.match(sreq))
.filter(res => res !== undefined)
.shift();
return (req) => {
setupJSFUnmockProperties(req);
const matcherResponse = match(req);
const res = generateMockFromTemplate(options, matcherResponse);
if (typeof matcherResponse !== "undefined" && typeof res !== "undefined") {
matcherResponse.service.track({ req, res });
}
listeners.forEach((listener) => listener.notify({ req, res }));
jsf.reset();
return res;
};
}
exports.responseCreatorFactory = responseCreatorFactory;
const normalizeHeaders = (headers) => headers === undefined
? undefined
: Object.keys(headers).reduce((acc, h) => (Object.assign(Object.assign({}, acc), { [h]: headers[h].schema })), {});
const toJSONSchemaType = (input) => Array.isArray(input)
? "array"
: input === null || input === undefined
? "null"
: typeof input;
const setupJSFUnmockProperties = (sreq) => {
jsf.extend("faker", () => require("faker"));
jsf.define("unmock-function", (fn, schema) => {
const res = fn(sreq);
delete schema.format;
schema.type = toJSONSchemaType(res);
return res;
});
};
const chooseResponseCode = (codes) => {
if (codes.length === 1) {
return codes[0];
}
const validCodes = codes.filter((cd) => parseInt(cd) > 199 && parseInt(cd) < 300);
if (validCodes.length > 1) {
return validCodes;
}
return validCodes[0];
};
const getStateForOperation = (operation, state, deref, genOptions) => {
const responses = operation.responses;
const operationCodes = Object.keys(responses);
if (state === undefined || Object.keys(state).length === 0) {
return undefined;
}
const stateCodes = Object.keys(state);
const possibleResponseCodes = stateCodes.filter((code) => operationCodes.includes(code));
let operationStatusCode;
let stateStatusCode;
if (possibleResponseCodes.length === 0) {
if (operationCodes.indexOf("default") === -1) {
return undefined;
}
operationStatusCode = "default";
stateStatusCode = firstOrRandomOrUndefined(stateCodes);
}
else {
stateStatusCode = operationStatusCode = genOptions.isFlaky
? firstOrRandomOrUndefined(possibleResponseCodes)
: chooseResponseCode(possibleResponseCodes);
}
if (operationStatusCode === undefined || stateStatusCode === undefined) {
throw new Error(`Too many matching response codes to choose from in '${operation.description}'!\n` +
`Try flaky mode (\`unmock.flaky()\`) or explictly set a status code (\`{ $code: N }\`)`);
}
else if (Array.isArray(operationStatusCode) ||
Array.isArray(stateStatusCode)) {
throw new Error(`Too many 2XX responses to choose from in '${operation.description}'!\n` +
`Try flaky mode (\`unmock.flaky()\`) or explictly set a status code (\`{ $code: N }\`)`);
}
const operationResponse = responses[operationStatusCode];
if (operationResponse === undefined) {
return undefined;
}
const resolvedResponse = deref(operationResponse);
const operationContent = resolvedResponse.content;
if (operationContent === undefined) {
return undefined;
}
const operationContentKeys = Object.keys(operationContent);
const mediaTypes = Object.keys(state[stateStatusCode]).filter((type) => operationContentKeys.includes(type));
const mediaType = firstOrRandomOrUndefined(mediaTypes);
if (mediaType === undefined) {
return undefined;
}
const requestedState = state[stateStatusCode][mediaType];
const matchedOperation = operationContent[mediaType].schema;
return {
$code: stateStatusCode,
template: lodash_1.defaultsDeep(requestedState, matchedOperation),
headers: deref(resolvedResponse.headers),
};
};
const chooseResponseFromOperation = (operation, deref, genOptions) => {
const responses = operation.responses;
const codes = Object.keys(responses);
const chosenCode = genOptions.isFlaky
? firstOrRandomOrUndefined(codes)
: chooseResponseCode(codes);
if (chosenCode === undefined) {
if (genOptions.isFlaky) {
throw new Error(`Could not find any responses in operation '${operation.description}'`);
}
else {
throw new Error(`No valid 2XX responses in '${operation.description}'`);
}
}
else if (Array.isArray(chosenCode)) {
throw new Error(`Too many 2XX responses to choose from in '${operation.description}'!\n` +
`Try flaky mode (\`unmock.flaky()\`) or explictly set a status code (\`{ $code: N }\`)`);
}
const response = responses[chosenCode];
if (response === undefined) {
throw new Error(`Could not load response for status code '${chosenCode}' in '${operation.description}'`);
}
const deRefedResponse = deref(response);
const content = deRefedResponse.content;
if (content === undefined) {
return {
$code: chosenCode,
template: undefined,
headers: deref(deRefedResponse.headers),
};
}
const chosenMediaType = firstOrRandomOrUndefined(Object.keys(content));
if (chosenMediaType === undefined) {
throw new Error(`Chosen response (${JSON.stringify(content)}) does not have any content!`);
}
const schema = content[chosenMediaType].schema;
if (schema === undefined) {
throw new Error("Missing schema for a response!");
}
return {
$code: chosenCode,
template: deref(schema),
headers: deref(deRefedResponse.headers),
};
};
const generateMockFromTemplate = (options, matchedService) => {
if (matchedService === undefined) {
return undefined;
}
const { operation, state, service } = matchedService;
const { template, $code, headers } = getStateForOperation(operation, state, service.dereferencer, {
isFlaky: options.flaky(),
}) ||
chooseResponseFromOperation(operation, service.dereferencer, {
isFlaky: options.flaky(),
});
jsf.option("alwaysFakeOptionals", true);
jsf.option("useDefaultValue", false);
const resolvedTemplate = jsf.generate(template);
const body = JSON.stringify(resolvedTemplate);
jsf.option("useDefaultValue", true);
const resHeaders = jsf.generate(normalizeHeaders(headers));
jsf.option("useDefaultValue", false);
return {
body,
headers: resHeaders,
statusCode: +$code || 200,
};
};
//# sourceMappingURL=generator.js.map