api-contract-validator
Version:
Plugin for validating API schemas from API documentation
93 lines (76 loc) • 3.31 kB
JavaScript
const ajvUtils = require('./helpers/ajv-utils');
const schemaUtils = require('./helpers/schema-utils');
const responseAdapter = require('./helpers/response-adapter');
const { messages } = require('./helpers/common');
const { filterMissingProps } = require('./helpers/object-utils');
const { setCoverage } = require('./helpers/coverage');
module.exports.schemaValidator = function schemaValidator(obj, options = {}) {
const { apiDefinitionsPath } = options;
const buildSchemaOptions = options.buildSchemaOptions || {};
// load the schema
if (!apiDefinitionsPath) {
throw new Error(messages.REQUIRED_API_DEFINITIONS_PATH);
}
const schema = schemaUtils.getSchemaByFilesPath(apiDefinitionsPath, buildSchemaOptions);
// parse the response object
const parsedResponse = responseAdapter.parseResponse(obj);
// validate the response object contains the required props
validateRequiredProps(parsedResponse);
// extract the request path schema
const { request, response } = parsedResponse;
const { path, method } = parsedResponse.request;
const { status } = parsedResponse.response;
const validator = schemaUtils.getValidatorByPathMethodAndCode(schema, request, response);
if (!(validator instanceof Object) || !(validator.validate instanceof Function)) {
throw new Error(`schema not found for ${JSON.stringify({ path, method, status })}`);
}
// validate
const predicate = validator.validate(response);
const { actual, expected } = ajvUtils.parseErrors(validator.errors, response);
// mark API as covered
setCoverage({ path, method, status });
return {
predicate,
actual,
expected,
errors: validator.errors,
matchMsg: messages.EXPECTED_RESPONSE_TO_MATCH_SCHEMA,
noMatchMsg: messages.EXPECTED_RESPONSE_TO_NOT_MATCH_SCHEMA,
};
};
module.exports.statusValidator = function statusValidator(expectedStatus, obj) {
// parse the response object
const parsedResponse = responseAdapter.parseResponse(obj);
// validate the response object is valid
if (!(parsedResponse instanceof Object)) {
throw new Error(messages.FAILED_TO_EXTRACT_RESPONSE_DETAILS);
}
const { response } = parsedResponse;
const { status, body } = response;
if (!status) {
throw new Error("required properties for validating schema are missing: 'status'");
}
// validate
return {
predicate: status === expectedStatus,
actual: { status, body },
expected: { status: expectedStatus },
matchMsg: `expected http status code ${status} to be ${expectedStatus}`,
noMatchMsg: `expected http status code ${status} to not be ${expectedStatus}`,
};
};
function validateRequiredProps(parsedResponse) {
if (!(parsedResponse instanceof Object)) {
throw new Error(messages.FAILED_TO_EXTRACT_RESPONSE_DETAILS);
}
const { request, response } = parsedResponse;
const missingRequestProps = filterMissingProps(request, ['path', 'method']);
const missingResponseProps = filterMissingProps(response, ['status']);
if (missingRequestProps.length > 0 || missingResponseProps.length > 0) {
const missingProps = missingRequestProps
.concat(missingResponseProps)
.map((prop) => `'${prop}'`)
.toString();
throw new Error(`required properties for validating schema are missing: ${missingProps}`);
}
}