Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(csi-128): updated sdk-standard-components with axios impl.; fixed unit-tests #513

Merged
merged 26 commits into from
Nov 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
8db4ffa
feat(csi-128): updated sdk-standard-components with axios impl.; fixe…
geka-evk Nov 11, 2024
d9551ef
feat(csi-128): added logs to artifacts in CI
geka-evk Nov 11, 2024
8ff6b0f
feat(csi-128): added logs to artifacts in CI
geka-evk Nov 11, 2024
258ced6
feat(csi-128): added NODE_TLS_REJECT_UNAUTHORIZED=0
geka-evk Nov 11, 2024
9c9e424
feat(csi-128): updated sdk-standard-components with agent field
geka-evk Nov 11, 2024
19e3690
feat(csi-128): added NODE_TLS_REJECT_UNAUTHORIZED=0
geka-evk Nov 11, 2024
90c6c90
feat(csi-128): updated sdk components
geka-evk Nov 12, 2024
c120eba
feat(csi-128): updated from minor/iso
geka-evk Nov 12, 2024
356d47f
feat(csi-128): added log on start
geka-evk Nov 12, 2024
c0bb3fb
feat(csi-128): updated sdk components
geka-evk Nov 12, 2024
34c38a7
feat(csi-128): updated sdk components with httpConfig
geka-evk Nov 12, 2024
0367c4e
feat(csi-128): removed NODE_TLS_REJECT_UNAUTHORIZED env var
geka-evk Nov 13, 2024
5c76035
feat(csi-128): disabled coverageThreshold for outbound handlers
geka-evk Nov 13, 2024
8fecbc7
feat(csi-128): updated sdk-components with retry logic
geka-evk Nov 13, 2024
3dbcd04
feat(csi-128): fixed BR.sendRequest error handling
geka-evk Nov 13, 2024
537bf8d
feat(csi-128): clean up
geka-evk Nov 13, 2024
9cc45a8
chore(snapshot): 23.6.0-snapshot.0
geka-evk Nov 13, 2024
c19d673
chore(snapshot): 23.6.0-snapshot.1
geka-evk Nov 13, 2024
39aeab4
chore(snapshot): 23.7.0-snapshot.0
geka-evk Nov 13, 2024
39b7c47
feat(csi-128): skipped test coverage check for outbound handlers
geka-evk Nov 13, 2024
6c8f07f
chore(snapshot): 23.7.0-snapshot.1
geka-evk Nov 13, 2024
6776c07
feat(csi-128): updated deps
geka-evk Nov 19, 2024
5c34194
feat(csi-128): reverted iso version
geka-evk Nov 19, 2024
0f04fbe
feat(csi-128): reverted iso version
geka-evk Nov 19, 2024
4ef4fd3
feat(csi-128): added audit:check to pre-commit
geka-evk Nov 19, 2024
78c8dd7
feat(csi-128): updated deps
geka-evk Nov 19, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
yarn dep:check
yarn audit:check
yarn lint
4 changes: 3 additions & 1 deletion .ncurc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,7 @@ reject: [
## TODO: The new version of npm-check-updates uses new Glob v9.x and it is introducing a dependency Package "[email protected]" which is licensed under "BlueOak-1.0.0" which is not permitted by the Mojaloop License Policy
'npm-check-updates',
## TODO: The new version of nx causes submodules to be broken.
"nx"
"nx",
## esLint 9.15.0 caused the error: TypeError: Error while loading rule '@typescript-eslint/no-unused-expressions': Cannot read properties of undefined (reading 'allowShortCircuit')
"eslint"
]
3 changes: 2 additions & 1 deletion audit-ci.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
"GHSA-m95q-7qp3-xv42",
"GHSA-qw6h-vgh9-j6wx",
"GHSA-qwcr-r2fm-qrc7",
"GHSA-rv95-896h-c2vc"
"GHSA-rv95-896h-c2vc",
"GHSA-3xgq-45jj-v275" // https://github.com/advisories/GHSA-3xgq-45jj-v275
]
}
12 changes: 6 additions & 6 deletions modules/api-svc/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@mojaloop/sdk-scheme-adapter-api-svc",
"version": "20.8.0-snapshot.34",
"version": "20.8.0-snapshot.38",
"description": "An adapter for connecting to Mojaloop API enabled switches.",
"main": "src/index.js",
"types": "src/index.d.ts",
Expand Down Expand Up @@ -67,12 +67,12 @@
"@mojaloop/api-snippets": "17.7.4",
"@mojaloop/central-services-error-handling": "^13.0.2",
"@mojaloop/central-services-logger": "^11.5.1",
"@mojaloop/central-services-metrics": "^12.0.8",
"@mojaloop/central-services-shared": "18.11.0",
"@mojaloop/central-services-metrics": "^12.1.0",
"@mojaloop/central-services-shared": "18.11.1",
"@mojaloop/event-sdk": "^14.1.1",
"@mojaloop/ml-schema-transformer-lib": "2.3.4",
"@mojaloop/ml-schema-transformer-lib": "2.4.1",
"@mojaloop/sdk-scheme-adapter-private-shared-lib": "workspace:^",
"@mojaloop/sdk-standard-components": "^19.4.1",
"@mojaloop/sdk-standard-components": "19.5.1",
"ajv": "8.17.1",
"axios": "^1.7.7",
"co-body": "^6.2.0",
Expand Down Expand Up @@ -102,14 +102,14 @@
"@babel/preset-env": "^7.26.0",
"@redocly/openapi-cli": "^1.0.0-beta.94",
"@types/jest": "^29.5.14",
"axios-mock-adapter": "^2.1.0",
"babel-jest": "^29.7.0",
"eslint": "^9.14.0",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-jest": "^28.9.0",
"jest": "^29.7.0",
"jest-junit": "^16.0.0",
"nock": "^13.5.6",
"npm-check-updates": "^16.7.10",
"openapi-response-validator": "^12.1.3",
"openapi-typescript": "^7.4.3",
Expand Down
2 changes: 1 addition & 1 deletion modules/api-svc/src/InboundServer/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ class InboundApi extends EventEmitter {
api.use(middlewares.cacheRequest(cache));
}
api.use(router(handlers));
api.use(middlewares.createResponseBodyHandler());
api.use(middlewares.createResponseBodyHandler(logger));

api.context.resourceVersions = conf.resourceVersions;

Expand Down
11 changes: 9 additions & 2 deletions modules/api-svc/src/InboundServer/middlewares.js
Original file line number Diff line number Diff line change
Expand Up @@ -206,8 +206,9 @@ const cacheRequest = (cache) => async (ctx, next) => {
*/
const createRequestIdGenerator = (logger) => async (ctx, next) => {
ctx.request.id = generateSlug(4);
ctx.state.receivedAt = Date.now();
const { method, path, id } = ctx.request;
logger.info(`[==> req] ${method?.toUpperCase()} ${path} - requestId: ${id}`);
logger.isInfoEnabled && logger.info(`[==> req] ${method?.toUpperCase()} ${path} - requestId: ${id}`);
await next();
};

Expand Down Expand Up @@ -477,10 +478,16 @@ const createRequestValidator = (validator) => async (ctx, next) => {
* does not respect this convention and requires a 200.
* @return {Function}
*/
const createResponseBodyHandler = () => async (ctx, next) => {
const createResponseBodyHandler = (logger) => async (ctx, next) => {
if (ctx.response.body === undefined) {
ctx.response.body = '';
}

const { method, path, id } = ctx.request;
const { status = 'n/a' } = ctx.response;
const processTime = ((Date.now() - ctx.state.receivedAt) / 1000).toFixed(1);
logger.isInfoEnabled && logger.info(`[<== ${status}][${processTime}sec] ${method?.toUpperCase()} ${path} - requestId: ${id}`);

return await next();
};

Expand Down
3 changes: 1 addition & 2 deletions modules/api-svc/src/OutboundServer/middlewares.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@
const { Enum } = require('@mojaloop/central-services-shared');
const { ReturnCodes } = Enum.Http;
const { ProxyModel } = require('../lib/model');
const { applyState, createErrorHandler, createLogger, createRequestIdGenerator } =
require('../InboundServer/middlewares');
const { applyState, createErrorHandler, createLogger, createRequestIdGenerator } = require('../InboundServer/middlewares');


/**
Expand Down
2 changes: 1 addition & 1 deletion modules/api-svc/src/TestServer/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class TestApi {

this._api.use(middlewares.createRequestValidator(validator));
this._api.use(router(handlers));
this._api.use(middlewares.createResponseBodyHandler());
this._api.use(middlewares.createResponseBodyHandler(logger));
}

callback() {
Expand Down
2 changes: 2 additions & 0 deletions modules/api-svc/src/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ module.exports = {
clientKey: env.get('OAUTH_CLIENT_KEY').asString(),
clientSecret: env.get('OAUTH_CLIENT_SECRET').asString(),
refreshSeconds: env.get('OAUTH_REFRESH_SECONDS').default('60').asIntPositive(),
refreshRetrySeconds: env.get('OAUTH_REFRESH_RETRY_SECONDS').default('10').asIntPositive(),
},
mTlsEnabled: env.get('OAUTH_MUTUAL_TLS_ENABLED').default('false').asBool(),
requestAuthFailureRetryTimes: env.get('WSO2_AUTH_FAILURE_REQUEST_RETRIES').default('0').asIntPositive(),
Expand All @@ -198,6 +199,7 @@ module.exports = {
requestProcessingTimeoutSeconds: env.get('REQUEST_PROCESSING_TIMEOUT_SECONDS').default('30').asIntPositive(),

logIndent: env.get('LOG_INDENT').default('2').asIntPositive(),
isJsonOutput: env.get('LOG_IS_JSON_OUTPUT').default('false').asBool(),

allowTransferWithoutQuote: env.get('ALLOW_TRANSFER_WITHOUT_QUOTE').default('false').asBool(),

Expand Down
57 changes: 29 additions & 28 deletions modules/api-svc/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,16 @@ const { hostname } = require('os');
const EventEmitter = require('events');
const _ = require('lodash');
const { Logger } = require('@mojaloop/sdk-standard-components');
const config = require('./config');
const { name, version } = require('../../../package.json');

const config = require('./config');
const InboundServer = require('./InboundServer');
const OutboundServer = require('./OutboundServer');
const OAuthTestServer = require('./OAuthTestServer');
const { BackendEventHandler } = require('./BackendEventHandler');
const { FSPIOPEventHandler } = require('./FSPIOPEventHandler');
const TestServer = require('./TestServer');
const { MetricsServer, MetricsClient } = require('./lib/metrics');
const TestServer = require('./TestServer');
const ControlAgent = require('./ControlAgent');

// import things we want to expose e.g. for unit tests and users who dont want to use the entire
Expand All @@ -48,6 +49,19 @@ const LOG_ID = {
CACHE: { component: 'cache' },
};

const createLogger = (conf) => new Logger.Logger({
context: {
// If we're running from a Mojaloop helm chart deployment, we'll have a SIM_NAME
simulator: process.env['SIM_NAME'],
hostname: hostname(),
},
opts: {
levels: SDK_LOGGER_HIERARCHY.slice(SDK_LOGGER_HIERARCHY.indexOf(conf.logLevel)),
isJsonOutput: conf.isJsonOutput,
},
stringify: Logger.buildStringify({ isJsonOutput: conf.isJsonOutput }),
});

const createCache = (config, logger) => new Cache({
cacheUrl: config.cacheUrl,
logger: logger.push(LOG_ID.CACHE),
Expand Down Expand Up @@ -169,17 +183,9 @@ class Server extends EventEmitter {
// (!) lots of code duplication
async restart(newConf) {
this.logger.isDebugEnabled && this.logger.debug('Server is restarting...');
const updateLogger = !_.isEqual(newConf.logIndent, this.conf.logIndent);
const updateLogger = !_.isEqual(newConf.isJsonOutput, this.conf.isJsonOutput);
if (updateLogger) {
this.logger = new Logger.Logger({
context: {
// If we're running from a Mojaloop helm chart deployment, we'll have a SIM_NAME
simulator: process.env['SIM_NAME'],
hostname: hostname(),
},
opts: {levels: SDK_LOGGER_HIERARCHY.slice(SDK_LOGGER_HIERARCHY.indexOf(config.logLevel))},
stringify: Logger.buildStringify({ space: this.conf.logIndent }),
});
this.logger = createLogger(newConf);
}

let oldCache;
Expand Down Expand Up @@ -337,35 +343,28 @@ async function _GetUpdatedConfigFromMgmtAPI(conf, logger, client) {
return responseRead.data;
}

if(require.main === module) {
if (require.main === module) {
(async () => {
// this module is main i.e. we were started as a server;
// not used in unit test or "require" scenarios
const logger = new Logger.Logger({
context: {
// If we're running from a Mojaloop helm chart deployment, we'll have a SIM_NAME
simulator: process.env['SIM_NAME'],
hostname: hostname(),
},
opts: {levels: SDK_LOGGER_HIERARCHY.slice(SDK_LOGGER_HIERARCHY.indexOf(config.logLevel))},
stringify: Logger.buildStringify({ space: config.logIndent }),
});
if(config.pm4mlEnabled) {
const logger = createLogger(config);

if (config.pm4mlEnabled) {
const controlClient = await ControlAgent.Client.Create({
address: config.control.mgmtAPIWsUrl,
port: config.control.mgmtAPIWsPort,
logger: logger,
appConfig: config,
});
const updatedConfigFromMgmtAPI = await _GetUpdatedConfigFromMgmtAPI(config, logger, controlClient);
logger.isInfoEnabled && logger.info(`updatedConfigFromMgmtAPI: ${JSON.stringify(updatedConfigFromMgmtAPI)}`);
logger.isInfoEnabled && logger.push({ updatedConfigFromMgmtAPI }).info('updatedConfigFromMgmtAPI:');
_.merge(config, updatedConfigFromMgmtAPI);
controlClient.terminate();
}
const svr = new Server(config, logger);
svr.on('error', (err) => {
logger.isErrorEnabled && logger.push({ err }).error('Unhandled server error');
process.exit(1);
logger.push({ err }).error('Unhandled server error');
process.exit(2);
});

// handle SIGTERM to exit gracefully
Expand All @@ -375,10 +374,12 @@ if(require.main === module) {
process.exit(0);
});

svr.start().catch(err => {
logger.isErrorEnabled && logger.push({ err }).error('Error starting server');
await svr.start().catch(err => {
logger.push({ err }).error('Error starting server');
process.exit(1);
});

logger.isInfoEnabled && logger.push({ name, version }).info('SDK server is started!');
})();
}

Expand Down
28 changes: 19 additions & 9 deletions modules/api-svc/src/lib/model/lib/requests/backendRequests.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

const http = require('http');
const { request } = require('@mojaloop/sdk-standard-components');
const { buildUrl, throwOrJson, HTTPResponseError } = require('./common');
const { buildUrl, HTTPResponseError } = require('./common');


/**
Expand All @@ -21,7 +21,7 @@ const { buildUrl, throwOrJson, HTTPResponseError } = require('./common');
class BackendRequests {
constructor(config) {
this.config = config;
this.logger = config.logger;
this.logger = config.logger.push({ component: BackendRequests.name });

// FSPID of THIS DFSP
this.dfspId = config.dfspId;
Expand Down Expand Up @@ -238,7 +238,7 @@ class BackendRequests {
};
return this.sendRequest(reqOpts);
}

_patch(url, body) {
const reqOpts = {
method: 'PATCH',
Expand All @@ -250,13 +250,23 @@ class BackendRequests {
}

async sendRequest(reqOptions) {
this.logger.isDebugEnabled && this.logger.push({ reqOptions }).debug(`Executing HTTP ${reqOptions?.method}...`);
return request({ ...reqOptions, agent: this.agent })
.then(throwOrJson)
.catch(e => {
this.logger.push({ e }).error(`Error attempting ${reqOptions?.method} ${reqOptions?.uri}`);
throw e;
try {
this.logger.isVerboseEnabled && this.logger.push({ reqOptions }).verbose(`Executing HTTP ${reqOptions?.method}...`);
const res = await request({ ...reqOptions, agent: this.agent });

const data = (res.headers['content-length'] === '0' || res.statusCode === 204)
? null
: res.data;
this.logger.isVerboseEnabled && this.logger.push({ data }).verbose('Received HTTP response data');
return data;
} catch (err) {
this.logger.push({ err }).error(`Error attempting ${reqOptions?.method} ${reqOptions?.uri}`);
const { data, headers, status } = err.response || err;
throw new HTTPResponseError({
res: { data, headers, status },
msg: err?.message
});
}
}
}

Expand Down
20 changes: 0 additions & 20 deletions modules/api-svc/src/lib/model/lib/requests/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,27 +50,7 @@ const buildUrl = (...args) => {
};


const throwOrJson = async (res) => {
// TODO: will a 503 or 500 with content-length zero generate an error?
// or a 404 for that matter?!

if (res.headers['content-length'] === '0' || res.statusCode === 204) {
// success but no content, return null
return null;
}
if (res.statusCode < 200 || res.statusCode >= 300) {
// not a successful request
throw new HTTPResponseError({ msg: `Request returned non-success status code ${res.statusCode}`,
res
});
}

return res.data;
};


module.exports = {
HTTPResponseError,
buildUrl,
throwOrJson,
};
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
const assert = require('assert').strict;
const util = require('util');
const {
axios,
MojaloopRequests, Errors, WSO2Auth, Jws, Logger, common,
Ilp: { ILP_VERSIONS }
} = jest.requireActual('@mojaloop/sdk-standard-components');
Expand Down Expand Up @@ -178,6 +179,7 @@ class MockJwsSigner {


module.exports = {
axios,
Ilp,
MojaloopRequests: MockMojaloopRequests,
Jws: {
Expand Down
3 changes: 2 additions & 1 deletion modules/api-svc/test/config/integration-pm4ml.env
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ OAUTH_TOKEN_ENDPOINT=
OAUTH_CLIENT_KEY=
OAUTH_CLIENT_SECRET=
OAUTH_REFRESH_SECONDS=3600
# OAUTH_REFRESH_RETRY_SECONDS=60

# Set to true to respect expirity timestamps
REJECT_EXPIRED_QUOTE_RESPONSES=false
Expand Down Expand Up @@ -177,4 +178,4 @@ ENABLE_FSPIOP_EVENT_HANDLER=true
ENABLE_BACKEND_EVENT_HANDLER=false

# Supported Currencies
SUPPORTED_CURRENCIES="USD"
SUPPORTED_CURRENCIES="USD"
5 changes: 3 additions & 2 deletions modules/api-svc/test/config/integration.env
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ ENABLE_OAUTH_TOKEN_ENDPOINT=false
OAUTH_TOKEN_ENDPOINT_CLIENT_KEY=test-client-key
OAUTH_TOKEN_ENDPOINT_CLIENT_SECRET=test-client-secret
OAUTH_TOKEN_ENDPOINT_LISTEN_PORT=6000
SUPPORTED_CURRENCIES=USD,MXN
#SUPPORTED_CURRENCIES=USD,MXN
KYC_INFORMATION=TODO

# WSO2 Bearer Token specific to golden-fsp instance and environment
Expand All @@ -133,6 +133,7 @@ OAUTH_TOKEN_ENDPOINT=
OAUTH_CLIENT_KEY=
OAUTH_CLIENT_SECRET=
OAUTH_REFRESH_SECONDS=3600
# OAUTH_REFRESH_RETRY_SECONDS=60

# Set to true to respect expirity timestamps
REJECT_EXPIRED_QUOTE_RESPONSES=false
Expand Down Expand Up @@ -183,4 +184,4 @@ ENABLE_FSPIOP_EVENT_HANDLER=false
ENABLE_BACKEND_EVENT_HANDLER=false

# Supported Currencies
SUPPORTED_CURRENCIES="USD"
SUPPORTED_CURRENCIES="USD"
Loading