@godspeedsystems/core
Version:
> 4th Generation Declarative Microservice Framework
296 lines (295 loc) • 16.1 kB
JavaScript
/*
* You are allowed to study this software for learning and local * development purposes only. Any other use without explicit permission by Mindgrep, is prohibited.
* © 2022 Mindgrep Technologies Pvt Ltd
*/ "use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
function _export(target, all) {
for(var name in all)Object.defineProperty(target, name, {
enumerable: true,
get: all[name]
});
}
_export(exports, {
loadJsonSchemaForEvents: function() {
return loadJsonSchemaForEvents;
},
validateRequestSchema: function() {
return validateRequestSchema;
},
validateResponseSchema: function() {
return validateResponseSchema;
}
});
const _interfaces = require("./interfaces");
const _logger = require("../logger");
const _validation = /*#__PURE__*/ _interop_require_wildcard(require("./validation"));
function _getRequireWildcardCache(nodeInterop) {
if (typeof WeakMap !== "function") return null;
var cacheBabelInterop = new WeakMap();
var cacheNodeInterop = new WeakMap();
return (_getRequireWildcardCache = function(nodeInterop) {
return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
})(nodeInterop);
}
function _interop_require_wildcard(obj, nodeInterop) {
if (!nodeInterop && obj && obj.__esModule) {
return obj;
}
if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
return {
default: obj
};
}
var cache = _getRequireWildcardCache(nodeInterop);
if (cache && cache.has(obj)) {
return cache.get(obj);
}
var newObj = {
__proto__: null
};
var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
for(var key in obj){
if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
if (desc && (desc.get || desc.set)) {
Object.defineProperty(newObj, key, desc);
} else {
newObj[key] = obj[key];
}
}
}
newObj.default = obj;
if (cache) {
cache.set(obj, newObj);
}
return newObj;
}
function loadJsonSchemaForEvents(allEventsConfigs) {
_logger.logger.debug('Loading JSON Schema for events %s', Object.keys(allEventsConfigs));
// logger.debug('eventObj: %o', eventObj);
return new Promise((resolve, reject)=>{
Object.keys(allEventsConfigs).forEach(function(route) {
// Add body schema in ajv for each content_type per topic
/* TODO: Right now, we are assuming that there is going to be one content_type only i.e. application/json
This needs to be enhanced in fututre when multiple content_type will be supported
*/ const eventConfig = allEventsConfigs[route];
if ((0, _validation.isValidEvent)(eventConfig, route)) {
var _eventConfig_body, //just like OpenAPI Spec but with body instead of requestBody
_eventConfig_data_schema_body, _eventConfig_data_schema, _eventConfig_data, _eventConfig_data_schema1, _eventConfig_data1;
//Set the key in the event config. This will be needed later for ajv validation of incoming requests
// the ajv validators for each event are stored against these keys (routes)
eventConfig.key = route;
//Object.keys(eventObjTopic).forEach(function(topic) {
const body_content = (eventConfig === null || eventConfig === void 0 ? void 0 : (_eventConfig_body = eventConfig.body) === null || _eventConfig_body === void 0 ? void 0 : _eventConfig_body.content) || (eventConfig === null || eventConfig === void 0 ? void 0 : (_eventConfig_data = eventConfig.data) === null || _eventConfig_data === void 0 ? void 0 : (_eventConfig_data_schema = _eventConfig_data.schema) === null || _eventConfig_data_schema === void 0 ? void 0 : (_eventConfig_data_schema_body = _eventConfig_data_schema.body) === null || _eventConfig_data_schema_body === void 0 ? void 0 : _eventConfig_data_schema_body.content); //Legacy
if (body_content) {
Object.keys(body_content).forEach(function(k) {
const content_schema = body_content[k].schema;
if (content_schema) {
_logger.logger.debug('adding body schema for %s', route);
// logger.debug('content_schema %o', content_schema);
if (!_validation.default.getSchema(route)) {
_validation.default.addSchema(content_schema, route);
}
}
});
}
// Add params schema in ajv for each param per topic
const params = (eventConfig === null || eventConfig === void 0 ? void 0 : eventConfig.parameters) || (eventConfig === null || eventConfig === void 0 ? void 0 : eventConfig.params) || (eventConfig === null || eventConfig === void 0 ? void 0 : (_eventConfig_data1 = eventConfig.data) === null || _eventConfig_data1 === void 0 ? void 0 : (_eventConfig_data_schema1 = _eventConfig_data1.schema) === null || _eventConfig_data_schema1 === void 0 ? void 0 : _eventConfig_data_schema1.params);
let paramSchema = {};
if (params) {
for (let param of params){
if (param.schema) {
if (!paramSchema[param.in]) {
paramSchema[param.in] = {
type: 'object',
required: [],
properties: {}
};
}
if (param.required) {
paramSchema[param.in].required.push(param.name);
}
let schema = param.schema;
if (param.allow_empty_value) {
param.schema.nullable = true;
}
paramSchema[param.in].properties[param.name] = schema;
}
}
}
for(let schema in paramSchema){
// logger.debug('adding param schema for %s', topic);
// logger.debug('param schema: %o', paramSchema[schema]);
const topic_param = route + ':' + schema;
if (!_validation.default.getSchema(topic_param)) {
try {
_validation.default.addSchema(paramSchema[schema], topic_param);
} catch (err) {
_logger.logger.fatal('error in adding schema %o', err);
process.exit(1);
}
}
}
// Add responses schema in ajv for each response per topic
const responsesSchema = eventConfig === null || eventConfig === void 0 ? void 0 : eventConfig.responses;
if (responsesSchema) {
Object.keys(responsesSchema).forEach(function(k) {
var _responsesSchema_k_content_applicationjson, _responsesSchema_k_content, _responsesSchema_k, //Exactly as OpenApi spec
_responsesSchema_k_schema_data_content_applicationjson, _responsesSchema_k_schema_data_content, _responsesSchema_k_schema_data, _responsesSchema_k_schema, _responsesSchema_k1;
const response_s = ((_responsesSchema_k = responsesSchema[k]) === null || _responsesSchema_k === void 0 ? void 0 : (_responsesSchema_k_content = _responsesSchema_k.content) === null || _responsesSchema_k_content === void 0 ? void 0 : (_responsesSchema_k_content_applicationjson = _responsesSchema_k_content['application/json']) === null || _responsesSchema_k_content_applicationjson === void 0 ? void 0 : _responsesSchema_k_content_applicationjson.schema) || ((_responsesSchema_k1 = responsesSchema[k]) === null || _responsesSchema_k1 === void 0 ? void 0 : (_responsesSchema_k_schema = _responsesSchema_k1.schema) === null || _responsesSchema_k_schema === void 0 ? void 0 : (_responsesSchema_k_schema_data = _responsesSchema_k_schema.data) === null || _responsesSchema_k_schema_data === void 0 ? void 0 : (_responsesSchema_k_schema_data_content = _responsesSchema_k_schema_data.content) === null || _responsesSchema_k_schema_data_content === void 0 ? void 0 : (_responsesSchema_k_schema_data_content_applicationjson = _responsesSchema_k_schema_data_content['application/json']) === null || _responsesSchema_k_schema_data_content_applicationjson === void 0 ? void 0 : _responsesSchema_k_schema_data_content_applicationjson.schema); //Legacy implementation
if (response_s) {
const response_schema = response_s;
const _topic = route.replace(/{(.*?)}/g, ':$1'); //removing curly braces in topic (event key)
const endpoint = _topic.split('.').pop(); //extracting endpoint from eventkey
const topic_response = endpoint + ':responses:' + k;
if (!_validation.default.getSchema(topic_response)) {
_validation.default.addSchema(response_schema, topic_response);
}
}
});
}
} else {
_logger.logger.error(`Event config validation failed during load time for ${route}. Process exiting`);
process.exit(1);
}
});
resolve(1);
});
}
function validateRequestSchema(topic, event, eventSpec) {
var _eventSpec_data_schema, _eventSpec_data, //structure like open api spec
_eventSpec_data_schema1, _eventSpec_data1;
let status;
// Validate event.data.body
const hasSchema = (eventSpec === null || eventSpec === void 0 ? void 0 : eventSpec.body) || (eventSpec === null || eventSpec === void 0 ? void 0 : (_eventSpec_data = eventSpec.data) === null || _eventSpec_data === void 0 ? void 0 : (_eventSpec_data_schema = _eventSpec_data.schema) === null || _eventSpec_data_schema === void 0 ? void 0 : _eventSpec_data_schema.body);
if (event.data.body && hasSchema) {
// childLogger.info('event body and eventSpec exist');
// childLogger.debug('event.data.body: %o', event.data.body);
// if (!eventSpec.key) {
// }
const ajv_validate = _validation.default.getSchema(eventSpec.key);
if (ajv_validate) {
// childLogger.debug('ajv_validate for body');
if (!ajv_validate(event.data.body)) {
_logger.logger.error({
event: eventSpec.key
}, 'event.data.body validation failed %o \n Request body %o', ajv_validate.errors, event.data.body);
status = {
success: false,
code: 400,
message: ajv_validate.errors[0].message,
data: {
message: "The API cannot be executed due to a failure in request body schema validation.",
error: ajv_validate.errors,
eventBody: event.data.body
}
};
return status;
} else {
// childLogger.info('ajv_validate success for body');
status = {
success: true
};
}
} else {
status = {
success: true
};
}
} else if (!event.data.body && hasSchema) {
status = {
success: false,
code: 400,
message: 'Body not found in request but specified in the event schema'
};
return status;
} else {
//Body is not present in request and not specified in the event schema
status = {
success: true
};
}
const params = (eventSpec === null || eventSpec === void 0 ? void 0 : eventSpec.parameters) || (eventSpec === null || eventSpec === void 0 ? void 0 : eventSpec.params) || (eventSpec === null || eventSpec === void 0 ? void 0 : (_eventSpec_data1 = eventSpec.data) === null || _eventSpec_data1 === void 0 ? void 0 : (_eventSpec_data_schema1 = _eventSpec_data1.schema) === null || _eventSpec_data_schema1 === void 0 ? void 0 : _eventSpec_data_schema1.params); //Legacy
// Validate event.data['params']
let MAP = {
path: 'params',
header: 'headers',
query: 'query',
cookie: 'cookie'
};
// childLogger.debug('ajv_validate for params');
if (params) {
for(let param in MAP){
const topic_param = eventSpec.key + ':' + param;
const ajv_validate = _validation.default.getSchema(topic_param);
// childLogger.debug('topic_param: %s', topic_param);
if (ajv_validate) {
if (!ajv_validate(event.data[MAP[param]])) {
_logger.logger.debug({
event: eventSpec.key
}, `Event param validation failed ${event.data[MAP[param]]} %s`, topic_param);
ajv_validate.errors[0].message += ' in ' + param;
status = {
success: false,
code: 400,
message: ajv_validate.errors[0].message,
data: {
message: "The API cannot be executed due to a failure in request params schema validation.",
error: ajv_validate.errors[0]
}
};
return status;
} else {
// childLogger.info('ajv_validate success for params');
status = {
success: true
};
}
}
}
}
return status;
}
function validateResponseSchema(topic, gsStatus) {
let status;
if (gsStatus) {
const topicResponse = topic + ':responses:' + gsStatus.code;
const ajvValidate = _validation.default.getSchema(topicResponse);
if (ajvValidate) {
if (!ajvValidate(gsStatus.data)) {
_logger.logger.error({
event: topic,
response_code: gsStatus.code
}, 'ajv_validation of the response data failed %o', gsStatus.data);
let message;
if (gsStatus.success) {
message = `The API execution was successful. But, there was a failure in validating the response body as per the API schema for response with status code ${gsStatus.code}.`;
} else {
message = `The API execution was unsuccessful. Further on top of that, there was a failure in validating the API's response body as per the API schema for response with status code ${gsStatus.code}.`;
}
return new _interfaces.GSStatus(false, 500, undefined, {
message: message,
errors: ajvValidate.errors,
originalResponseBody: gsStatus.data,
originalResponseCode: gsStatus.code
});
} else {
// childLogger.debug('ajv_validate success');
status = {
success: true
};
}
} else {
status = {
success: true
};
}
} else {
status = {
success: true
};
}
return status;
}