diff --git a/package-lock.json b/package-lock.json index 94e6133..53534f2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,7 @@ "cors": "^2.8.5", "dotenv": "^16.4.1", "express": "^4.18.1", - "express-openapi-validator": "^4.13.8", + "express-openapi-validator": "^5.1.6", "ioredis": "^5.0.6", "libsodium-wrappers": "^0.7.9", "lodash": "^4.17.21", @@ -57,9 +57,9 @@ } }, "node_modules/@apidevtools/json-schema-ref-parser": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.0.9.tgz", - "integrity": "sha512-GBD2Le9w2+lVFoc4vswGI/TjkNIZSVp7+9xPf+X3uidBfWnAeUWmquteSyt0+VCrhNMWj/FTABISQrD3Z/YA+w==", + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.1.2.tgz", + "integrity": "sha512-r1w81DpR+KyRWd3f+rk6TNqMgedmAxZP5v5KWlXQWlgMUUtyEJch0DKEci1SorPMiSeM8XPl7MZ3miJ60JIpQg==", "dependencies": { "@jsdevtools/ono": "^7.1.3", "@types/json-schema": "^7.0.6", @@ -1621,7 +1621,6 @@ "version": "8.12.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "dev": true, "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -1633,6 +1632,35 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ajv-draft-04": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", + "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", + "peerDependencies": { + "ajv": "^8.5.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, "node_modules/amqplib": { "version": "0.10.3", "resolved": "https://registry.npmjs.org/amqplib/-/amqplib-0.10.3.tgz", @@ -2708,44 +2736,25 @@ } }, "node_modules/express-openapi-validator": { - "version": "4.13.8", - "resolved": "https://registry.npmjs.org/express-openapi-validator/-/express-openapi-validator-4.13.8.tgz", - "integrity": "sha512-89/sdkq+BKBuIyykaMl/vR9grFc3WFUPTjFo0THHbu+5g+q8rA7fKeoMfz+h84yOQIBcztmJ5ZJdk5uhEls31A==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/express-openapi-validator/-/express-openapi-validator-5.1.6.tgz", + "integrity": "sha512-CF24Pef5uThjdsCbjo1UP2mYx2YCkQl1HFoikCFFafFpZBCZ0YErD/RbqlcnKbKM9tMwXZsjAuuO84b2hmdF4g==", "dependencies": { + "@apidevtools/json-schema-ref-parser": "^9.1.2", "@types/multer": "^1.4.7", - "ajv": "^6.12.6", - "content-type": "^1.0.4", - "json-schema-ref-parser": "^9.0.9", + "ajv": "^8.11.2", + "ajv-draft-04": "^1.0.0", + "ajv-formats": "^2.1.1", + "content-type": "^1.0.5", + "json-schema-traverse": "^1.0.0", "lodash.clonedeep": "^4.5.0", "lodash.get": "^4.4.2", - "lodash.uniq": "^4.5.0", - "lodash.zipobject": "^4.1.3", "media-typer": "^1.1.0", "multer": "^1.4.5-lts.1", "ono": "^7.1.3", "path-to-regexp": "^6.2.0" } }, - "node_modules/express-openapi-validator/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/express-openapi-validator/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, "node_modules/express-openapi-validator/node_modules/path-to-regexp": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.1.tgz", @@ -2794,11 +2803,6 @@ "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", "dev": true }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" - }, "node_modules/fast-xml-parser": { "version": "4.2.5", "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.5.tgz", @@ -3298,23 +3302,10 @@ "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==" }, - "node_modules/json-schema-ref-parser": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/json-schema-ref-parser/-/json-schema-ref-parser-9.0.9.tgz", - "integrity": "sha512-qcP2lmGy+JUoQJ4DOQeLaZDqH9qSkeGCK3suKWxJXS82dg728Mn3j97azDMaOUmJAN4uCq91LdPx4K7E8F1a7Q==", - "deprecated": "Please switch to @apidevtools/json-schema-ref-parser", - "dependencies": { - "@apidevtools/json-schema-ref-parser": "9.0.9" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" }, "node_modules/json5": { "version": "2.2.3", @@ -3418,16 +3409,6 @@ "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==" }, - "node_modules/lodash.uniq": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==" - }, - "node_modules/lodash.zipobject": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/lodash.zipobject/-/lodash.zipobject-4.1.3.tgz", - "integrity": "sha512-A9SzX4hMKWS25MyalwcOnNoplyHbkNVsjidhTp8ru0Sj23wY9GWBKS8gAIGDSAqeWjIjvE4KBEl24XXAs+v4wQ==" - }, "node_modules/logform": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/logform/-/logform-2.6.0.tgz", @@ -3965,7 +3946,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true, "engines": { "node": ">=0.10.0" } diff --git a/package.json b/package.json index 9af117f..b8bbbcc 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "cors": "^2.8.5", "dotenv": "^16.4.1", "express": "^4.18.1", - "express-openapi-validator": "^4.13.8", + "express-openapi-validator": "^5.1.6", "ioredis": "^5.0.6", "libsodium-wrappers": "^0.7.9", "lodash": "^4.17.21", diff --git a/src/controllers/bap.trigger.controller.ts b/src/controllers/bap.trigger.controller.ts index 579be4f..f887d86 100644 --- a/src/controllers/bap.trigger.controller.ts +++ b/src/controllers/bap.trigger.controller.ts @@ -106,7 +106,6 @@ export const bapClientTriggerSettler = async ( const context = JSON.parse(JSON.stringify(requestBody.context)); const axios_config = await createAuthHeaderConfig(requestBody); - const bpp_id = requestBody.context.bpp_id; const bpp_uri = requestBody.context.bpp_uri; const action = requestBody.context.action; @@ -122,27 +121,58 @@ export const bapClientTriggerSettler = async ( for (let i = 0; i < subscribers!.length; i++) { subscribers![i].subscriber_url = bpp_uri; } - - response = await callNetwork( + callNetwork( subscribers!, requestBody, axios_config, action - ); + ).then((response) => { + responseHandler(response, requestBody, action); + }); } else { const subscribers = await registryLookup({ type: NetworkPaticipantType.BG, domain: requestBody.context.domain }); - response = await callNetwork( + callNetwork( subscribers!, requestBody, axios_config, action + ).then((response) => { + responseHandler(response, requestBody, action); + }); + } + + return; + } catch (err) { + let exception: Exception | null = null; + if (err instanceof Exception) { + exception = err; + } else { + exception = new Exception( + ExceptionType.Request_Failed, + "BAP Request Failed at bapClientTriggerSettler", + 500, + err ); } + logger.error(exception); + } +}; + + +const responseHandler = async(res:any, requestBody:any, action:any) => { + try{ + const response = { + data: JSON.stringify(res.data), + status: res.status + }; + logger.info( + `Result : Request Successful \nStatus: ${response.status} \nData : ${JSON.stringify(response.data)}` + ); if ( response.status == 200 || response.status == 202 || @@ -150,44 +180,42 @@ export const bapClientTriggerSettler = async ( ) { // Network Calls Succeeded. const additionalCustomAttrsConfig = getConfig().app.telemetry.messageProperties; - const additionalCustomAttrs = customAttributes(requestBody, additionalCustomAttrsConfig); + const additionalCustomAttrs = customAttributes(requestBody, additionalCustomAttrsConfig); telemetrySDK.onApi({ data: { attributes: { "http.status.code": response.status, ...additionalCustomAttrs } } })(requestBody, response); - return; - } - - switch (getConfig().client.type) { - case ClientConfigType.synchronous: { - const message_id = requestBody.context.message_id; - await SyncCache.getInstance().recordError( - message_id, - action as RequestActions, - { + } else { + switch (getConfig().client.type) { + case ClientConfigType.synchronous: { + const message_id = requestBody.context.message_id; + await SyncCache.getInstance().recordError( + message_id, + action as RequestActions, + { + // TODO: change this error code. + code: 651641, + type: BecknErrorType.coreError, + message: "Network Participant Request Failed...", + data: [response] + } + ); + break; + } + case ClientConfigType.messageQueue: { + // TODO: Implement message queue. + break; + } + case ClientConfigType.webhook: { + const context = JSON.parse(JSON.stringify(requestBody.context)); + await errorCallback(context, { // TODO: change this error code. code: 651641, type: BecknErrorType.coreError, message: "Network Participant Request Failed...", data: [response] - } - ); - break; - } - case ClientConfigType.messageQueue: { - // TODO: Implement message queue. - break; - } - case ClientConfigType.webhook: { - await errorCallback(context, { - // TODO: change this error code. - code: 651641, - type: BecknErrorType.coreError, - message: "Network Participant Request Failed...", - data: [response] - }); - break; + }); + break; + } } } - - return; } catch (err) { let exception: Exception | null = null; if (err instanceof Exception) { @@ -203,4 +231,4 @@ export const bapClientTriggerSettler = async ( logger.error(exception); } -}; +}; \ No newline at end of file diff --git a/src/controllers/bpp.response.controller.ts b/src/controllers/bpp.response.controller.ts index a80b5f0..31d9f20 100644 --- a/src/controllers/bpp.response.controller.ts +++ b/src/controllers/bpp.response.controller.ts @@ -86,12 +86,14 @@ export const bppClientResponseSettler = async ( if (requestCache.sender.type == NetworkPaticipantType.BG) { const subscribers = [requestCache.sender]; - response = await callNetwork( + callNetwork( subscribers, responseBody, axios_config, action - ); + ).then((response) => { + responseHandler(response, responseBody, action); + }); } else { const subscribers: Array = [ { @@ -100,14 +102,39 @@ export const bppClientResponseSettler = async ( } ]; - response = await callNetwork( + callNetwork( subscribers, responseBody, axios_config, action + ).then((response) => { + responseHandler(response, responseBody, action); + }); + } + return; + } catch (error) { + let exception: Exception | null = null; + if (error instanceof Exception) { + exception = error; + } else { + exception = new Exception( + ExceptionType.Request_Failed, + "BPP Response Failed at bppClientResponseSettler", + 500, + error ); } + logger.error(exception); + } +}; + +const responseHandler = async(res:any, responseBody:any, action:any) => { + try{ + const response = { + data: JSON.stringify(res.data), + status: res.status + }; if ( response.status == 200 || response.status == 202 || @@ -117,36 +144,36 @@ export const bppClientResponseSettler = async ( const additionalCustomAttrsConfig = getConfig().app.telemetry.messageProperties; const additionalCustomAttrs = customAttributes(responseBody, additionalCustomAttrsConfig); telemetrySDK.onApi({ data: { attributes: { "http.status.code": response.status, ...additionalCustomAttrs } } })(responseBody, response); - return; - } - - switch (getConfig().client.type) { - case ClientConfigType.synchronous: { - throw new Exception( - ExceptionType.Config_ClientConfig_Invalid, - "Synchronous mode is not available for BPP.", - 400 - ); - break; - } - case ClientConfigType.messageQueue: { - // TODO: implement message queue. - break; - } - case ClientConfigType.webhook: { - if (getConfig().app.gateway.mode !== GatewayMode.network) { - errorCallback(context, { - // TODO: change the error code. - code: 354845, - message: "Network call failed", - type: BecknErrorType.coreError, - data: [response] - }); + } else { + switch (getConfig().client.type) { + case ClientConfigType.synchronous: { + throw new Exception( + ExceptionType.Config_ClientConfig_Invalid, + "Synchronous mode is not available for BPP.", + 400 + ); + break; + } + case ClientConfigType.messageQueue: { + // TODO: implement message queue. + break; + } + case ClientConfigType.webhook: { + if (getConfig().app.gateway.mode !== GatewayMode.network) { + const context = JSON.parse(JSON.stringify(responseBody.context)); + errorCallback(context, { + // TODO: change the error code. + code: 354845, + message: "Network call failed", + type: BecknErrorType.coreError, + data: [response] + }); + } + break; } - break; } } - } catch (error) { + }catch (error) { let exception: Exception | null = null; if (error instanceof Exception) { exception = error; @@ -161,4 +188,4 @@ export const bppClientResponseSettler = async ( logger.error(exception); } -}; +} \ No newline at end of file diff --git a/src/middlewares/schemaValidator.middleware.ts b/src/middlewares/schemaValidator.middleware.ts index d898249..94f9da4 100644 --- a/src/middlewares/schemaValidator.middleware.ts +++ b/src/middlewares/schemaValidator.middleware.ts @@ -1,4 +1,4 @@ -import { NextFunction, Request, Response } from "express"; +import { NextFunction, Request, Response} from "express"; import * as OpenApiValidator from "express-openapi-validator"; import { Exception, ExceptionType } from "../models/exception.model"; import { Locals } from "../interfaces/locals.interface"; @@ -8,6 +8,8 @@ import path from "path"; import { OpenAPIV3 } from 'express-openapi-validator/dist/framework/types'; import YAML from 'yaml'; const protocolServerLevel = `${getConfig().app.mode.toUpperCase()}-${getConfig().app.gateway.mode.toUpperCase()}`; +import express from 'express'; +import logger from "../utils/logger.utils"; // Cache object const apiSpecCache: { [filename: string]: OpenAPIV3.Document } = {}; @@ -15,7 +17,7 @@ const apiSpecCache: { [filename: string]: OpenAPIV3.Document } = {}; // Function to load and cache the API spec const loadApiSpec = (specFile: string): OpenAPIV3.Document => { if (!apiSpecCache[specFile]) { - console.log("Cache Not found. Loading....", specFile) + logger.info(`Cache Not found loadApiSpec file. Loading.... ${specFile}`) const apiSpecYAML = fs.readFileSync(specFile, 'utf8'); const apiSpec = YAML.parse(apiSpecYAML); apiSpecCache[specFile] = apiSpec; @@ -23,6 +25,26 @@ const loadApiSpec = (specFile: string): OpenAPIV3.Document => { return apiSpecCache[specFile]; }; +let cachedOpenApiValidator: express.RequestHandler[] | null = null; + +// Function to initialize and cache the OpenAPI validator middleware +const getOpenApiValidatorMiddleware = (specFile: string) => { + if (!cachedOpenApiValidator) { + logger.info(`Cache Not found for OpenApiValidator middleware. Loading.... ${specFile}`) + const apiSpec = loadApiSpec(specFile); + cachedOpenApiValidator = OpenApiValidator.middleware({ + apiSpec, + validateRequests: true, + validateResponses: false, + $refParser: { + mode: "dereference" + } + }); + } + return cachedOpenApiValidator; +}; + + export const schemaErrorHandler = ( err: any, req: Request, @@ -80,14 +102,7 @@ export const openApiValidatorMiddleware = async ( } } - const openApiValidator = OpenApiValidator.middleware({ - apiSpec: loadApiSpec(specFile), - validateRequests: true, - validateResponses: false, - $refParser: { - mode: "dereference" - } - }); + const openApiValidator = getOpenApiValidatorMiddleware(specFile) const walkSubstack = function ( stack: any, diff --git a/src/routes/requests.routes.ts b/src/routes/requests.routes.ts index 061e890..d5e3562 100644 --- a/src/routes/requests.routes.ts +++ b/src/routes/requests.routes.ts @@ -70,7 +70,7 @@ if ( req, res, next, - action as RequestActions + action as RequestActions, ); } ); diff --git a/src/utils/becknRequester.utils.ts b/src/utils/becknRequester.utils.ts index 49f08d8..0184ff9 100644 --- a/src/utils/becknRequester.utils.ts +++ b/src/utils/becknRequester.utils.ts @@ -15,16 +15,10 @@ export const makeBecknRequest = async ( ): Promise => { try { const requestURL = combineURLs(subscriberUrl, `/${action}`); - - const response = await axios.post(requestURL, body, { + return axios.post(requestURL, body, { ...axios_config, timeout: 10000 }); - - return { - data: JSON.stringify(response.data), - status: response.status - }; } catch (error) { console.log("Error at Make Beckn Request=====>", error); let response: BecknResponse | undefined; @@ -66,7 +60,6 @@ export async function callNetwork( status: 500 }; } - for (let i = 0; i < subscribers.length; i++) { logger.info(`Attempt Number: ${i + 1} \nAction : ${action}`); logger.info(`sending Response to BAP: ${subscribers[i].subscriber_url}`); @@ -76,28 +69,13 @@ export async function callNetwork( getConfig().app.httpRetryCount, "\n" ); - const response = await makeBecknRequest( + return makeBecknRequest( subscribers[i].subscriber_url, body, axios_config, getConfig().app.httpRetryCount, action - ); - if ( - response.status == 200 || - response.status == 201 || - response.status == 202 || - response.status == 204 - ) { - logger.info( - `Result : Request Successful \nStatus: ${response.status} \nData : ${response.data} \nSubscriber URL: ${subscribers[i].subscriber_url}` - ); - return response; - } - - logger.error( - `Result : Failed call to Subscriber: ${subscribers[i].subscriber_url}, \nStatus: ${response.status}, \nData: ${response.data}` - ); + ) } return {