UNPKG

@godspeedsystems/core

Version:

> 4th Generation Declarative Microservice Framework

296 lines (295 loc) 16.1 kB
/* * 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; }