From 42e93d013427c69b93ad1b096bb480630528a047 Mon Sep 17 00:00:00 2001 From: Kevin Leyow Date: Tue, 28 Nov 2023 09:42:46 -0500 Subject: [PATCH] feat(mojaloop/#3427): add oracle endpoint db caching and oracle request caching (#467) * chore: fix logger statements * chore: fix logger statements * chore(snapshot): 15.0.1-snapshot.0 * chore: log * chore: revert * chore: statement * test * chore(snapshot): 15.0.1-snapshot.1 * dep * chore(snapshot): 15.0.1-snapshot.2 * chore: change * chore: tests * feat: add oracle endpoint db caching * add oracle request caching * tests * coverage * chore(snapshot): 15.2.0-snapshot.0 * chore: header --- config/default.json | 9 +- docker/account-lookup-service/default.json | 2 +- package-lock.json | 5 +- package.json | 5 +- src/domain/oracle/oracle.js | 7 +- src/domain/parties/parties.js | 4 +- src/handlers/endpointcache.js | 2 +- src/handlers/participants/{Type}/{ID}.js | 59 ++---- src/handlers/parties/{Type}/{ID}.js | 2 +- src/lib/cache.js | 126 +++++++++++++ src/lib/config.js | 5 +- src/models/oracle/facade.js | 48 +++-- src/models/oracle/oracleEndpointCached.js | 107 +++++++++++ src/server.js | 8 +- test/integration-config.json | 2 +- test/unit/domain/oracle/oracle.test.js | 10 + .../domain/participants/participants.test.js | 4 + test/unit/domain/parties/parties.test.js | 5 +- test/unit/handlers/health.test.js | 4 + test/unit/handlers/oracles.test.js | 5 + test/unit/handlers/oracles/{ID}.test.js | 4 + test/unit/handlers/participants.test.js | 4 + .../participants/participants.test.js | 4 + .../handlers/participants/{Type}/{ID}.test.js | 12 +- .../participants/{Type}/{ID}/error.test.js | 4 + .../participants/{Type}/{ID}/{SubId}.test.js | 10 +- .../{Type}/{ID}/{SubId}/error.test.js | 4 + .../handlers/parties/endpointcache.test.js | 4 + test/unit/handlers/parties/parties.test.js | 4 + .../unit/handlers/parties/{Type}/{ID}.test.js | 13 +- .../parties/{Type}/{ID}/error.test.js | 4 + .../parties/{Type}/{ID}/{SubId}.test.js | 10 +- test/unit/lib/cache.test.js | 172 ++++++++++++++++++ test/unit/models/currency/currency.test.js | 4 + .../models/endpointType/endpointType.test.js | 4 + test/unit/models/oracle/facade.test.js | 36 ++-- .../unit/models/oracle/oracleEndpoint.test.js | 4 + .../oracle/oracleEndpointCached.test.js | 153 ++++++++++++++++ .../models/participantEndpoint/facade.test.js | 7 +- .../models/partyIdType/partyIdType.test.js | 5 + 40 files changed, 779 insertions(+), 102 deletions(-) create mode 100644 src/lib/cache.js create mode 100644 src/models/oracle/oracleEndpointCached.js create mode 100644 test/unit/lib/cache.test.js create mode 100644 test/unit/models/oracle/oracleEndpointCached.test.js diff --git a/config/default.json b/config/default.json index 6b046a24..99d3c1de 100644 --- a/config/default.json +++ b/config/default.json @@ -36,16 +36,21 @@ }, "DISPLAY_ROUTES": true, "RUN_MIGRATIONS": true, - "ENDPOINT_CACHE_CONFIG": { + "CENTRAL_SHARED_ENDPOINT_CACHE_CONFIG": { "expiresIn": 180000, "generateTimeout": 30000, "getDecoratedValue": true }, - "PARTICIPANT_CACHE_CONFIG": { + "CENTRAL_SHARED_PARTICIPANT_CACHE_CONFIG": { "expiresIn": 61000, "generateTimeout": 30000, "getDecoratedValue": true }, + "ALS_GENERAL_CACHE_CONFIG": { + "CACHE_ENABLED": false, + "MAX_BYTE_SIZE": 10000000, + "EXPIRES_IN_MS": 61000 + }, "ERROR_HANDLING": { "includeCauseExtension": false, "truncateExtensions": true diff --git a/docker/account-lookup-service/default.json b/docker/account-lookup-service/default.json index 873d119a..b424b93d 100644 --- a/docker/account-lookup-service/default.json +++ b/docker/account-lookup-service/default.json @@ -20,7 +20,7 @@ }, "DISPLAY_ROUTES": true, "RUN_MIGRATIONS": true, - "ENDPOINT_CACHE_CONFIG": { + "CENTRAL_SHARED_ENDPOINT_CACHE_CONFIG": { "expiresIn": 180000, "generateTimeout": 30000 }, diff --git a/package-lock.json b/package-lock.json index 950c2746..8187d4c2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,17 @@ { "name": "account-lookup-service", - "version": "15.1.0", + "version": "15.2.0-snapshot.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "account-lookup-service", - "version": "15.1.0", + "version": "15.2.0-snapshot.0", "license": "Apache-2.0", "dependencies": { "@hapi/basic": "7.0.2", "@hapi/boom": "10.0.1", + "@hapi/catbox-memory": "^6.0.1", "@hapi/good": "9.0.1", "@hapi/hapi": "21.3.2", "@hapi/inert": "7.1.0", diff --git a/package.json b/package.json index 60be430d..286da218 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "account-lookup-service", "description": "Account Lookup Service is used to validate Party and Participant lookups.", - "version": "15.1.0", + "version": "15.2.0-snapshot.0", "license": "Apache-2.0", "author": "ModusBox", "contributors": [ @@ -77,17 +77,18 @@ "dependencies": { "@hapi/basic": "7.0.2", "@hapi/boom": "10.0.1", + "@hapi/catbox-memory": "^6.0.1", "@hapi/good": "9.0.1", "@hapi/hapi": "21.3.2", "@hapi/inert": "7.1.0", "@hapi/joi": "17.1.1", "@hapi/vision": "7.0.3", - "@mojaloop/database-lib": "11.0.3", "@mojaloop/central-services-error-handling": "12.0.7", "@mojaloop/central-services-health": "14.0.2", "@mojaloop/central-services-logger": "11.2.2", "@mojaloop/central-services-metrics": "12.0.8", "@mojaloop/central-services-shared": "18.2.0", + "@mojaloop/database-lib": "11.0.3", "@mojaloop/event-sdk": "14.0.0", "@mojaloop/sdk-standard-components": "17.1.3", "@now-ims/hapi-now-auth": "2.1.0", diff --git a/src/domain/oracle/oracle.js b/src/domain/oracle/oracle.js index e589c559..aaa287c5 100644 --- a/src/domain/oracle/oracle.js +++ b/src/domain/oracle/oracle.js @@ -33,6 +33,7 @@ const currency = require('../../models/currency') const ErrorHandler = require('@mojaloop/central-services-error-handling') const Config = require('../../lib/config') const Metrics = require('@mojaloop/central-services-metrics') +const cachedOracleEndpoint = require('../../models/oracle/oracleEndpointCached') /** * @function createOracle @@ -109,11 +110,11 @@ exports.getOracle = async (query) => { isType = true } if (isCurrency && isType) { - oracleEndpointModelList = await oracleEndpoint.getOracleEndpointByTypeAndCurrency(query.type, query.currency) + oracleEndpointModelList = await cachedOracleEndpoint.getOracleEndpointByTypeAndCurrency(query.type, query.currency) } else if (isCurrency && !isType) { - oracleEndpointModelList = await oracleEndpoint.getOracleEndpointByCurrency(query.currency) + oracleEndpointModelList = await cachedOracleEndpoint.getOracleEndpointByCurrency(query.currency) } else if (isType && !isCurrency) { - oracleEndpointModelList = await oracleEndpoint.getOracleEndpointByType(query.type) + oracleEndpointModelList = await cachedOracleEndpoint.getOracleEndpointByType(query.type) } else { oracleEndpointModelList = await oracleEndpoint.getAllOracleEndpoint() } diff --git a/src/domain/parties/parties.js b/src/domain/parties/parties.js index 5a4b7aa4..870943f6 100644 --- a/src/domain/parties/parties.js +++ b/src/domain/parties/parties.js @@ -52,7 +52,7 @@ const Config = require('../../lib/config') * @param {object} query - uri query parameters of the http request * @param {object} span */ -const getPartiesByTypeAndID = async (headers, params, method, query, span = undefined) => { +const getPartiesByTypeAndID = async (headers, params, method, query, span = undefined, cache) => { const histTimerEnd = Metrics.getHistogram( 'getPartiesByTypeAndID', 'Get party by Type and Id', @@ -95,7 +95,7 @@ const getPartiesByTypeAndID = async (headers, params, method, query, span = unde return } - const response = await oracle.oracleRequest(headers, method, params, query) + const response = await oracle.oracleRequest(headers, method, params, query, cache) if (response && response.data && Array.isArray(response.data.partyList) && response.data.partyList.length > 0) { // Oracle's API is a standard rest-style end-point Thus a GET /party on the oracle will return all participant-party records. We must filter the results based on the callbackEndpointType to make sure we remove records containing partySubIdOrType when we are in FSPIOP_CALLBACK_URL_PARTIES_GET mode: diff --git a/src/handlers/endpointcache.js b/src/handlers/endpointcache.js index 8933d250..beeb68de 100644 --- a/src/handlers/endpointcache.js +++ b/src/handlers/endpointcache.js @@ -56,7 +56,7 @@ module.exports = { }, EventSdk.AuditEventAction.start) try { await ParticipantEndpointCache.stopCache() - await ParticipantEndpointCache.initializeCache(Config.ENDPOINT_CACHE_CONFIG) + await ParticipantEndpointCache.initializeCache(Config.CENTRAL_SHARED_ENDPOINT_CACHE_CONFIG) histTimerEnd({ success: true }) } catch (err) { histTimerEnd({ success: false }) diff --git a/src/handlers/participants/{Type}/{ID}.js b/src/handlers/participants/{Type}/{ID}.js index 7e5a21f8..47452cb6 100644 --- a/src/handlers/participants/{Type}/{ID}.js +++ b/src/handlers/participants/{Type}/{ID}.js @@ -26,9 +26,7 @@ 'use strict' const Enum = require('@mojaloop/central-services-shared').Enum -const ErrorHandler = require('@mojaloop/central-services-error-handling') const EventSdk = require('@mojaloop/event-sdk') -const Logger = require('@mojaloop/central-services-logger') const Metrics = require('@mojaloop/central-services-metrics') const LibUtil = require('../../../lib/util') const participants = require('../../../domain/participants') @@ -58,16 +56,11 @@ module.exports = { payload: request.payload }, EventSdk.AuditEventAction.start) const metadata = `${request.method} ${request.path}` - try { - participants.getParticipantsByTypeAndID(request.headers, request.params, request.method, request.query, span).catch(err => { - request.server.log(['error'], `ERROR - getParticipantsByTypeAndID:${metadata}: ${LibUtil.getStackOrInspect(err)}`) - }) - histTimerEnd({ success: true }) - } catch (err) { - Logger.isErrorEnabled && Logger.error(`ERROR - ${metadata}: ${err.stack}`) - histTimerEnd({ success: false }) - throw ErrorHandler.Factory.reformatFSPIOPError(err) - } + participants.getParticipantsByTypeAndID(request.headers, request.params, request.method, request.query, span).catch(err => { + request.server.log(['error'], `ERROR - getParticipantsByTypeAndID:${metadata}: ${LibUtil.getStackOrInspect(err)}`) + }) + histTimerEnd({ success: true }) + return h.response().code(202) }, /** @@ -84,16 +77,10 @@ module.exports = { ['success'] ).startTimer() const metadata = `${request.method} ${request.path}` - try { - participants.putParticipantsByTypeAndID(request.headers, request.params, request.method, request.payload).catch(err => { - request.server.log(['error'], `ERROR - putParticipantsByTypeAndID:${metadata}: ${LibUtil.getStackOrInspect(err)}`) - }) - histTimerEnd({ success: true }) - } catch (err) { - Logger.isErrorEnabled && Logger.error(`ERROR - ${metadata}: ${err.stack}`) - histTimerEnd({ success: false }) - throw ErrorHandler.Factory.reformatFSPIOPError(err) - } + participants.putParticipantsByTypeAndID(request.headers, request.params, request.method, request.payload).catch(err => { + request.server.log(['error'], `ERROR - putParticipantsByTypeAndID:${metadata}: ${LibUtil.getStackOrInspect(err)}`) + }) + histTimerEnd({ success: true }) return h.response().code(200) }, /** @@ -117,16 +104,10 @@ module.exports = { payload: request.payload }, EventSdk.AuditEventAction.start) const metadata = `${request.method} ${request.path}` - try { - participants.postParticipants(request.headers, request.method, request.params, request.payload, span).catch(err => { - request.server.log(['error'], `ERROR - postParticipants:${metadata}: ${LibUtil.getStackOrInspect(err)}`) - }) - histTimerEnd({ success: true }) - } catch (err) { - Logger.isErrorEnabled && Logger.error(`ERROR - ${metadata}: ${err.stack}`) - histTimerEnd({ success: false }) - throw ErrorHandler.Factory.reformatFSPIOPError(err) - } + participants.postParticipants(request.headers, request.method, request.params, request.payload, span).catch(err => { + request.server.log(['error'], `ERROR - postParticipants:${metadata}: ${LibUtil.getStackOrInspect(err)}`) + }) + histTimerEnd({ success: true }) return h.response().code(202) }, /** @@ -143,16 +124,10 @@ module.exports = { ['success'] ).startTimer() const metadata = `${request.method} ${request.path}` - try { - participants.deleteParticipants(request.headers, request.params, request.method, request.query).catch(err => { - request.server.log(['error'], `ERROR - deleteParticipants:${metadata}: ${LibUtil.getStackOrInspect(err)}`) - }) - histTimerEnd({ success: true }) - } catch (err) { - Logger.isErrorEnabled && Logger.error(`ERROR - ${metadata}: ${err.stack}`) - histTimerEnd({ success: false }) - throw ErrorHandler.Factory.reformatFSPIOPError(err) - } + participants.deleteParticipants(request.headers, request.params, request.method, request.query).catch(err => { + request.server.log(['error'], `ERROR - deleteParticipants:${metadata}: ${LibUtil.getStackOrInspect(err)}`) + }) + histTimerEnd({ success: true }) return h.response().code(202) } } diff --git a/src/handlers/parties/{Type}/{ID}.js b/src/handlers/parties/{Type}/{ID}.js index c352b2e2..81576f2b 100644 --- a/src/handlers/parties/{Type}/{ID}.js +++ b/src/handlers/parties/{Type}/{ID}.js @@ -56,7 +56,7 @@ module.exports = { }, EventSdk.AuditEventAction.start) // Here we call an async function- but as we send an immediate sync response, _all_ errors // _must_ be handled by getPartiesByTypeAndID. - parties.getPartiesByTypeAndID(request.headers, request.params, request.method, request.query, span).catch(err => { + parties.getPartiesByTypeAndID(request.headers, request.params, request.method, request.query, span, request.server.app.cache).catch(err => { request.server.log(['error'], `ERROR - getPartiesByTypeAndID: ${LibUtil.getStackOrInspect(err)}`) }) histTimerEnd({ success: true }) diff --git a/src/lib/cache.js b/src/lib/cache.js new file mode 100644 index 00000000..1abb27c1 --- /dev/null +++ b/src/lib/cache.js @@ -0,0 +1,126 @@ +/***** + License + -------------- + Copyright © 2017 Bill & Melinda Gates Foundation + The Mojaloop files are made available by the Bill & Melinda Gates Foundation under the Apache License, Version 2.0 (the "License") and you may not use these files except in compliance with the License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, the Mojaloop files are distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + Contributors + -------------- + This is the official list of the Mojaloop project contributors for this file. + Names of the original copyright holders (individuals or organizations) + should be listed with a '*' in the first column. People who have + contributed from an organization can be listed under the organization + that actually holds the copyright for their contributions (see the + Gates Foundation organization for an example). Those individuals should have + their names indented and be marked with a '-'. Email address can be added + optionally within square brackets . + * Gates Foundation + * Name Surname + + * Kevin Leyow + + -------------- + ******/ + +'use strict' + +const CatboxMemory = require('@hapi/catbox-memory') +const Config = require('../lib/config') + +let enabled = true +let ttl +let catboxMemoryClient = null + +class CacheClient { + constructor (meta) { + this.meta = meta + } + + getMeta () { + return this.meta + } + + createKey (id) { + return { + segment: this.meta.id, + id + } + } + + get (key) { + if (enabled) { + return catboxMemoryClient.get(key) + } + return null + } + + set (key, value) { + catboxMemoryClient.set(key, value, parseInt(ttl)) + } + + drop (key) { + catboxMemoryClient.drop(key) + } +} + +/* + Each client should register itself during module load. + The client meta should be: + { + id [MANDATORY] + preloadCache() [OPTIONAL] + this will be called to preload data + } +*/ +let cacheClients = {} + +const registerCacheClient = (clientMeta) => { + const newClient = new CacheClient(clientMeta) + cacheClients[clientMeta.id] = newClient + return newClient +} + +const initCache = async function () { + // Read config + ttl = Config.ALS_GENERAL_CACHE_CONFIG.EXPIRES_IN_MS + enabled = Config.ALS_GENERAL_CACHE_CONFIG.CACHE_ENABLED + + // Init catbox. + catboxMemoryClient = new CatboxMemory.Engine({ + maxByteSize: Config.ALS_GENERAL_CACHE_CONFIG.MAX_BYTE_SIZE + }) + catboxMemoryClient.start() + + for (const clientId in cacheClients) { + const clientMeta = cacheClients[clientId].getMeta() + await clientMeta.preloadCache() + } +} + +const destroyCache = async function () { + catboxMemoryClient.stop() + catboxMemoryClient = null +} + +const dropClients = function () { + cacheClients = {} +} + +const isCacheEnabled = function () { + return enabled +} + +module.exports = { + // Clients registration + registerCacheClient, + + // Init & destroy the cache + initCache, + destroyCache, + isCacheEnabled, + + // exposed for tests + CatboxMemory, + dropClients +} diff --git a/src/lib/config.js b/src/lib/config.js index 7df4107e..18e231ca 100644 --- a/src/lib/config.js +++ b/src/lib/config.js @@ -142,8 +142,9 @@ const config = { DISPLAY_ROUTES: RC.DISPLAY_ROUTES, RUN_MIGRATIONS: RC.RUN_MIGRATIONS, ADMIN_PORT: RC.ADMIN_PORT, - ENDPOINT_CACHE_CONFIG: RC.ENDPOINT_CACHE_CONFIG, - PARTICIPANT_CACHE_CONFIG: RC.PARTICIPANT_CACHE_CONFIG, + CENTRAL_SHARED_ENDPOINT_CACHE_CONFIG: RC.CENTRAL_SHARED_ENDPOINT_CACHE_CONFIG, + CENTRAL_SHARED_PARTICIPANT_CACHE_CONFIG: RC.CENTRAL_SHARED_PARTICIPANT_CACHE_CONFIG, + ALS_GENERAL_CACHE_CONFIG: RC.ALS_GENERAL_CACHE_CONFIG, ERROR_HANDLING: RC.ERROR_HANDLING, SWITCH_ENDPOINT: RC.SWITCH_ENDPOINT, INSTRUMENTATION_METRICS_DISABLED: RC.INSTRUMENTATION.METRICS.DISABLED, diff --git a/src/models/oracle/facade.js b/src/models/oracle/facade.js index 440bdc25..c421413f 100644 --- a/src/models/oracle/facade.js +++ b/src/models/oracle/facade.js @@ -27,13 +27,13 @@ 'use strict' const request = require('@mojaloop/central-services-shared').Util.Request -const oracleEndpoint = require('../oracle') const Mustache = require('mustache') const Logger = require('@mojaloop/central-services-logger') const Enums = require('@mojaloop/central-services-shared').Enum const ErrorHandler = require('@mojaloop/central-services-error-handling') const Config = require('../../lib/config') const Metrics = require('@mojaloop/central-services-metrics') +const cachedOracleEndpoint = require('../oracle/oracleEndpointCached') /** * @function oracleRequest @@ -48,7 +48,7 @@ const Metrics = require('@mojaloop/central-services-metrics') * * @returns {object} returns the response from the oracle */ -exports.oracleRequest = async (headers, method, params = {}, query = {}, payload = undefined) => { +exports.oracleRequest = async (headers, method, params = {}, query = {}, payload = undefined, cache) => { try { let url const partyIdType = params.Type @@ -75,9 +75,35 @@ exports.oracleRequest = async (headers, method, params = {}, query = {}, payload ['success'] ).startTimer() try { - const resp = await request.sendRequest(url, headers, headers[Enums.Http.Headers.FSPIOP.SOURCE], headers[Enums.Http.Headers.FSPIOP.DESTINATION] || Enums.Http.Headers.FSPIOP.SWITCH.value, method.toUpperCase(), payload || undefined) - histTimerEnd({ success: true }) - return resp + if (isGetRequest) { + let cachedOracleFspResponse + cachedOracleFspResponse = cache && cache.get(`oracleSendRequest_${url}`) + if (!cachedOracleFspResponse) { + cachedOracleFspResponse = await request.sendRequest( + url, + headers, + headers[Enums.Http.Headers.FSPIOP.SOURCE], + headers[Enums.Http.Headers.FSPIOP.DESTINATION] || Enums.Http.Headers.FSPIOP.SWITCH.value, + method.toUpperCase(), + payload || undefined + ) + cache && cache.set(`oracleSendRequest_${url}`, cachedOracleFspResponse) + } else { + Logger.isDebugEnabled && Logger.debug(`${new Date().toISOString()}, [oracleRequest]: cache hit for fsp for partyId lookup`) + } + + histTimerEnd({ success: true }) + return cachedOracleFspResponse + } + + return await request.sendRequest( + url, + headers, + headers[Enums.Http.Headers.FSPIOP.SOURCE], + headers[Enums.Http.Headers.FSPIOP.DESTINATION] || Enums.Http.Headers.FSPIOP.SWITCH.value, + method.toUpperCase(), + payload || undefined + ) } catch (err) { histTimerEnd({ success: false }) throw err @@ -116,7 +142,7 @@ exports.oracleRequest = async (headers, method, params = {}, query = {}, payload */ const _getOracleEndpointByTypeAndCurrency = async (partyIdType, partyIdentifier, currency) => { let url - const oracleEndpointModel = await oracleEndpoint.getOracleEndpointByTypeAndCurrency(partyIdType, currency) + const oracleEndpointModel = await cachedOracleEndpoint.getOracleEndpointByTypeAndCurrency(partyIdType, currency) if (oracleEndpointModel.length > 0) { if (oracleEndpointModel.length > 1) { const defautOracle = oracleEndpointModel.filter(oracle => oracle.isDefault).pop() @@ -151,7 +177,7 @@ const _getOracleEndpointByTypeAndCurrency = async (partyIdType, partyIdentifier, */ const _getOracleEndpointByType = async (partyIdType, partyIdentifier) => { let url - const oracleEndpointModel = await oracleEndpoint.getOracleEndpointByType(partyIdType) + const oracleEndpointModel = await cachedOracleEndpoint.getOracleEndpointByType(partyIdType) if (oracleEndpointModel.length > 0) { if (oracleEndpointModel.length > 1) { const defaultOracle = oracleEndpointModel.filter(oracle => oracle.isDefault).pop() @@ -187,7 +213,7 @@ const _getOracleEndpointByType = async (partyIdType, partyIdentifier) => { */ const _getOracleEndpointByTypeAndSubId = async (partyIdType, partyIdentifier, partySubIdOrType) => { let url - const oracleEndpointModel = await oracleEndpoint.getOracleEndpointByType(partyIdType) + const oracleEndpointModel = await cachedOracleEndpoint.getOracleEndpointByType(partyIdType) if (oracleEndpointModel.length > 0) { if (oracleEndpointModel.length > 1) { const defautOracle = oracleEndpointModel.filter(oracle => oracle.isDefault).pop() @@ -224,7 +250,7 @@ const _getOracleEndpointByTypeAndSubId = async (partyIdType, partyIdentifier, pa */ const _getOracleEndpointByTypeCurrencyAndSubId = async (partyIdType, partyIdentifier, currency, partySubIdOrType) => { let url - const oracleEndpointModel = await oracleEndpoint.getOracleEndpointByTypeAndCurrency(partyIdType, currency) + const oracleEndpointModel = await cachedOracleEndpoint.getOracleEndpointByTypeAndCurrency(partyIdType, currency) if (oracleEndpointModel.length > 0) { if (oracleEndpointModel.length > 1) { const defautOracle = oracleEndpointModel.filter(oracle => oracle.isDefault).pop() @@ -265,9 +291,9 @@ exports.oracleBatchRequest = async (headers, method, requestPayload, type, paylo let oracleEndpointModel let url if ((requestPayload && requestPayload.currency && requestPayload.currency.length !== 0)) { - oracleEndpointModel = await oracleEndpoint.getOracleEndpointByTypeAndCurrency(type, requestPayload.currency) + oracleEndpointModel = await cachedOracleEndpoint.getOracleEndpointByTypeAndCurrency(type, requestPayload.currency) } else { - oracleEndpointModel = await oracleEndpoint.getOracleEndpointByType(type) + oracleEndpointModel = await cachedOracleEndpoint.getOracleEndpointByType(type) } if (oracleEndpointModel.length > 0) { if (oracleEndpointModel.length > 1) { diff --git a/src/models/oracle/oracleEndpointCached.js b/src/models/oracle/oracleEndpointCached.js new file mode 100644 index 00000000..cd88220a --- /dev/null +++ b/src/models/oracle/oracleEndpointCached.js @@ -0,0 +1,107 @@ +/***** + License + -------------- + Copyright © 2017 Bill & Melinda Gates Foundation + The Mojaloop files are made available by the Bill & Melinda Gates Foundation under the Apache License, Version 2.0 (the "License") and you may not use these files except in compliance with the License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, the Mojaloop files are distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + Contributors + -------------- + This is the official list of the Mojaloop project contributors for this file. + Names of the original copyright holders (individuals or organizations) + should be listed with a '*' in the first column. People who have + contributed from an organization can be listed under the organization + that actually holds the copyright for their contributions (see the + Gates Foundation organization for an example). Those individuals should have + their names indented and be marked with a '-'. Email address can be added + optionally within square brackets . + * Gates Foundation + * Name Surname + + * Kevin Leyow + + -------------- + ******/ + +'use strict' + +const ErrorHandler = require('@mojaloop/central-services-error-handling') +const Cache = require('../../lib/cache') +const Metrics = require('@mojaloop/central-services-metrics') +const OracleEndpointUncached = require('./oracleEndpoint') + +let cacheClient + +const getCacheKey = (params) => { + return `${Object.values(params).join('__')}` +} + +const getOracleEndpointCached = async (params) => { + const histTimer = Metrics.getHistogram( + 'model_oracleEndpoints', + 'model_getOracleEndpointsCached - Metrics for oracle endpoints cached model', + ['success', 'queryName', 'hit'] + ).startTimer() + const partyIdType = params.partyIdType || null + const currency = params.currency || null + + // Do we have valid participants list in the cache ? + const cacheKey = getCacheKey(params) + let cachedEndpoints = cacheClient.get(cacheKey) + if (!cachedEndpoints) { + // No oracleEndpoint in the cache, so fetch from participant API + let oracleEndpoints + if (partyIdType && currency) { + oracleEndpoints = await OracleEndpointUncached.getOracleEndpointByTypeAndCurrency(partyIdType, currency) + } else if (currency) { + oracleEndpoints = await OracleEndpointUncached.getOracleEndpointByCurrency(currency) + } else { + oracleEndpoints = await OracleEndpointUncached.getOracleEndpointByType(partyIdType) + } + + // store in cache + cacheClient.set(cacheClient, oracleEndpoints) + histTimer({ success: true, queryName: 'model_getOracleEndpointCached', hit: false }) + } else { + // unwrap oracleEnpoints list from catbox structure + cachedEndpoints = cachedEndpoints.item + histTimer({ success: true, queryName: 'model_getOracleEndpointCached', hit: true }) + } + return cachedEndpoints +} + +/* + Public API +*/ +exports.initialize = async () => { + /* Register as cache client */ + const oracleEndpointCacheClientMeta = { + id: 'oracleEndpoints' + } + + cacheClient = Cache.registerCacheClient(oracleEndpointCacheClientMeta) +} + +exports.getOracleEndpointByTypeAndCurrency = async (partyIdType, currency) => { + try { + return await getOracleEndpointCached({ partyIdType, currency }) + } catch (err) { + throw ErrorHandler.Factory.reformatFSPIOPError(err) + } +} + +exports.getOracleEndpointByType = async (partyIdType) => { + try { + return await getOracleEndpointCached({ partyIdType }) + } catch (err) { + throw ErrorHandler.Factory.reformatFSPIOPError(err) + } +} + +exports.getOracleEndpointByCurrency = async (currency) => { + try { + return await getOracleEndpointCached({ currency }) + } catch (err) { + throw ErrorHandler.Factory.reformatFSPIOPError(err) + } +} diff --git a/src/server.js b/src/server.js index 800053fc..858afcd3 100644 --- a/src/server.js +++ b/src/server.js @@ -40,6 +40,7 @@ const RequestLogger = require('./lib/requestLogger') const Migrator = require('./lib/migrator') const Handlers = require('./handlers') const Routes = require('./handlers/routes') +const Cache = require('./lib/cache') const connectDatabase = async () => { return Db.connect(Config.DATABASE) @@ -75,6 +76,9 @@ const createServer = async (port, api, routes, isAdmin) => { } } }) + server.app.cache = Cache.registerCacheClient({ + id: 'serverGeneralCache' + }) await Plugins.registerPlugins(server, api, isAdmin) await server.ext([ { @@ -113,8 +117,8 @@ const initializeApi = async (port = Config.API_PORT) => { const api = await OpenapiBackend.initialise(OpenAPISpecPath, Handlers.ApiHandlers) const server = await createServer(port, api, Routes.APIRoutes(api), false) Logger.isInfoEnabled && Logger.info(`Server running on ${server.info.host}:${server.info.port}`) - await ParticipantEndpointCache.initializeCache(Config.ENDPOINT_CACHE_CONFIG) - await ParticipantCache.initializeCache(Config.PARTICIPANT_CACHE_CONFIG) + await ParticipantEndpointCache.initializeCache(Config.CENTRAL_SHARED_ENDPOINT_CACHE_CONFIG) + await ParticipantCache.initializeCache(Config.CENTRAL_SHARED_PARTICIPANT_CACHE_CONFIG) return server } diff --git a/test/integration-config.json b/test/integration-config.json index 1006558a..2fa2049e 100644 --- a/test/integration-config.json +++ b/test/integration-config.json @@ -38,7 +38,7 @@ }, "DISPLAY_ROUTES": true, "RUN_MIGRATIONS": true, - "ENDPOINT_CACHE_CONFIG": { + "CENTRAL_SHARED_ENDPOINT_CACHE_CONFIG": { "expiresIn": 180000, "generateTimeout": 30000 }, diff --git a/test/unit/domain/oracle/oracle.test.js b/test/unit/domain/oracle/oracle.test.js index af49e3a1..c47f25f1 100644 --- a/test/unit/domain/oracle/oracle.test.js +++ b/test/unit/domain/oracle/oracle.test.js @@ -35,6 +35,12 @@ const oracleEndpoint = require('../../../../src/models/oracle') const currency = require('../../../../src/models/currency') const partyIdType = require('../../../../src/models/partyIdType') const Db = require('../../../../src/lib/db') +const oracleEndpointCached = require('../../../../src/models/oracle/oracleEndpointCached') +const Logger = require('@mojaloop/central-services-logger') + +Logger.isDebugEnabled = jest.fn(() => true) +Logger.isErrorEnabled = jest.fn(() => true) +Logger.isInfoEnabled = jest.fn(() => true) const partyIdTypeResponse = { partyIdTypeId: 1, @@ -106,6 +112,10 @@ describe('Oracle tests', () => { Db.oracleEndpoint.query.returns(getOracleDatabaseResponse) Db.oracleEndpoint.update.returns(true) + sandbox.stub(oracleEndpointCached, 'getOracleEndpointByTypeAndCurrency').returns(getOracleDatabaseResponse) + sandbox.stub(oracleEndpointCached, 'getOracleEndpointByType').returns(getOracleDatabaseResponse) + sandbox.stub(oracleEndpointCached, 'getOracleEndpointByCurrency').returns(getOracleDatabaseResponse) + SpanStub = { audit: sandbox.stub().callsFake(), error: sandbox.stub().callsFake(), diff --git a/test/unit/domain/participants/participants.test.js b/test/unit/domain/participants/participants.test.js index ebbdf5f0..7982984a 100644 --- a/test/unit/domain/participants/participants.test.js +++ b/test/unit/domain/participants/participants.test.js @@ -41,6 +41,10 @@ const oracle = require('../../../../src/models/oracle/facade') const Helper = require('../../../util/helper') const Config = require('../../../../src/lib/config') +Logger.isDebugEnabled = jest.fn(() => true) +Logger.isErrorEnabled = jest.fn(() => true) +Logger.isInfoEnabled = jest.fn(() => true) + describe('participant Tests', () => { describe('getParticipantsByTypeAndID', () => { let sandbox diff --git a/test/unit/domain/parties/parties.test.js b/test/unit/domain/parties/parties.test.js index e93bbe6c..a3245e7b 100644 --- a/test/unit/domain/parties/parties.test.js +++ b/test/unit/domain/parties/parties.test.js @@ -45,11 +45,14 @@ const Config = require('../../../../src/lib/config') const participant = require('../../../../src/models/participantEndpoint/facade') const oracle = require('../../../../src/models/oracle/facade') +Logger.isDebugEnabled = jest.fn(() => true) +Logger.isErrorEnabled = jest.fn(() => true) +Logger.isInfoEnabled = jest.fn(() => true) let sandbox describe('Parties Tests', () => { beforeEach(async () => { - await Endpoints.initializeCache(Config.ENDPOINT_CACHE_CONFIG) + await Endpoints.initializeCache(Config.CENTRAL_SHARED_ENDPOINT_CACHE_CONFIG) sandbox = Sinon.createSandbox() sandbox.stub(request) sandbox.stub(Util.Http, 'SwitchDefaultHeaders').returns(Helper.defaultSwitchHeaders) diff --git a/test/unit/handlers/health.test.js b/test/unit/handlers/health.test.js index b861823e..e3018dc0 100644 --- a/test/unit/handlers/health.test.js +++ b/test/unit/handlers/health.test.js @@ -36,7 +36,11 @@ const initServer = require('../../../src/server').initializeApi const getPort = require('get-port') const Sinon = require('sinon') const MigrationLockModel = require('../../../src/models/misc/migrationLock') +const Logger = require('@mojaloop/central-services-logger') +Logger.isDebugEnabled = jest.fn(() => true) +Logger.isErrorEnabled = jest.fn(() => true) +Logger.isInfoEnabled = jest.fn(() => true) let sandbox let server diff --git a/test/unit/handlers/oracles.test.js b/test/unit/handlers/oracles.test.js index c913cfa6..cca0e3cf 100644 --- a/test/unit/handlers/oracles.test.js +++ b/test/unit/handlers/oracles.test.js @@ -36,6 +36,11 @@ const initServer = require('../../../src/server').initializeAdmin const Db = require('../../../src/lib/db') const getPort = require('get-port') const Migrator = require('../../../src/lib/migrator') +const Logger = require('@mojaloop/central-services-logger') + +Logger.isDebugEnabled = jest.fn(() => true) +Logger.isErrorEnabled = jest.fn(() => true) +Logger.isInfoEnabled = jest.fn(() => true) const getResponse = [{ oracleId: '1', diff --git a/test/unit/handlers/oracles/{ID}.test.js b/test/unit/handlers/oracles/{ID}.test.js index 00534d3f..997ca0a1 100644 --- a/test/unit/handlers/oracles/{ID}.test.js +++ b/test/unit/handlers/oracles/{ID}.test.js @@ -37,7 +37,11 @@ const initServer = require('../../../../src/server').initializeAdmin const getPort = require('get-port') const Db = require('../../../../src/lib/db') const Migrator = require('../../../../src/lib/migrator') +const Logger = require('@mojaloop/central-services-logger') +Logger.isDebugEnabled = jest.fn(() => true) +Logger.isErrorEnabled = jest.fn(() => true) +Logger.isInfoEnabled = jest.fn(() => true) let sandbox let server diff --git a/test/unit/handlers/participants.test.js b/test/unit/handlers/participants.test.js index f390a1f1..d137602b 100644 --- a/test/unit/handlers/participants.test.js +++ b/test/unit/handlers/participants.test.js @@ -34,7 +34,11 @@ const initServer = require('../../../src/server').initializeApi const Helper = require('../../util/helper') const Db = require('../../../src/lib/db') const getPort = require('get-port') +const Logger = require('@mojaloop/central-services-logger') +Logger.isDebugEnabled = jest.fn(() => true) +Logger.isErrorEnabled = jest.fn(() => true) +Logger.isInfoEnabled = jest.fn(() => true) let sandbox let server diff --git a/test/unit/handlers/participants/participants.test.js b/test/unit/handlers/participants/participants.test.js index ea34cc07..2dc29b81 100644 --- a/test/unit/handlers/participants/participants.test.js +++ b/test/unit/handlers/participants/participants.test.js @@ -35,7 +35,11 @@ const Helper = require('../../../util/helper') const participants = require('../../../../src/domain/participants') const initServer = require('../../../../src/server').initializeApi const getPort = require('get-port') +const Logger = require('@mojaloop/central-services-logger') +Logger.isDebugEnabled = jest.fn(() => true) +Logger.isErrorEnabled = jest.fn(() => true) +Logger.isInfoEnabled = jest.fn(() => true) let server let sandbox diff --git a/test/unit/handlers/participants/{Type}/{ID}.test.js b/test/unit/handlers/participants/{Type}/{ID}.test.js index 564e2c98..8ca95639 100644 --- a/test/unit/handlers/participants/{Type}/{ID}.test.js +++ b/test/unit/handlers/participants/{Type}/{ID}.test.js @@ -32,7 +32,7 @@ const Sinon = require('sinon') const Db = require('../../../../../src/lib/db') -const oracleEndpoint = require('../../../../../src/models/oracle') +const oracleEndpointCached = require('../../../../../src/models/oracle/oracleEndpointCached') const participant = require('../../../../../src/models/participantEndpoint/facade') const participants = require('../../../../../src/domain/participants') const requestLogger = require('../../../../../src/lib/requestLogger') @@ -40,9 +40,13 @@ const Helper = require('../../../../util/helper') const initServer = require('../../../../../src/server').initializeApi const getPort = require('get-port') const ErrorHandler = require('@mojaloop/central-services-error-handling') +const Logger = require('@mojaloop/central-services-logger') const requestUtil = require('@mojaloop/central-services-shared').Util.Request const Enums = require('@mojaloop/central-services-shared').Enum +Logger.isDebugEnabled = jest.fn(() => true) +Logger.isErrorEnabled = jest.fn(() => true) +Logger.isInfoEnabled = jest.fn(() => true) let server let sandbox @@ -53,6 +57,8 @@ describe('/participants/{Type}/{ID}', () => { sandbox.stub(requestLogger, 'logRequest').returns({}) sandbox.stub(requestLogger, 'logResponse').returns({}) server = await initServer(await getPort()) + sandbox.stub(Logger) + Logger.error = sandbox.stub() }) afterAll(async () => { @@ -102,7 +108,7 @@ describe('/participants/{Type}/{ID}', () => { const stubs = [ sandbox.stub(participant, 'sendErrorToParticipant').resolves({}), sandbox.stub(participant, 'validateParticipant').resolves(true), - sandbox.stub(oracleEndpoint, 'getOracleEndpointByType').resolves(['whatever']), + sandbox.stub(oracleEndpointCached, 'getOracleEndpointByType').resolves(['whatever']), sandbox.stub(requestUtil, 'sendRequest').rejects(badRequestError) ] const response = await server.inject(options) @@ -138,7 +144,7 @@ describe('/participants/{Type}/{ID}', () => { const stubs = [ sandbox.stub(participant, 'sendErrorToParticipant').resolves({}), sandbox.stub(participant, 'validateParticipant').resolves(true), - sandbox.stub(oracleEndpoint, 'getOracleEndpointByType').resolves(['whatever']), + sandbox.stub(oracleEndpointCached, 'getOracleEndpointByType').resolves(['whatever']), sandbox.stub(requestUtil, 'sendRequest').rejects(badRequestError) ] const response = await server.inject(options) diff --git a/test/unit/handlers/participants/{Type}/{ID}/error.test.js b/test/unit/handlers/participants/{Type}/{ID}/error.test.js index d7b7e1eb..1e011722 100644 --- a/test/unit/handlers/participants/{Type}/{ID}/error.test.js +++ b/test/unit/handlers/participants/{Type}/{ID}/error.test.js @@ -37,7 +37,11 @@ const participants = require(`${src}/domain/participants`) const ErrHandler = require(`${src}/handlers/participants/{Type}/{ID}/error`) const Helper = require('../../../../../util/helper') const LibUtil = require(`${src}/lib/util`) +const Logger = require('@mojaloop/central-services-logger') +Logger.isDebugEnabled = jest.fn(() => true) +Logger.isErrorEnabled = jest.fn(() => true) +Logger.isInfoEnabled = jest.fn(() => true) let server let sandbox const mockContext = jest.fn() diff --git a/test/unit/handlers/participants/{Type}/{ID}/{SubId}.test.js b/test/unit/handlers/participants/{Type}/{ID}/{SubId}.test.js index 0babc02f..996ef960 100644 --- a/test/unit/handlers/participants/{Type}/{ID}/{SubId}.test.js +++ b/test/unit/handlers/participants/{Type}/{ID}/{SubId}.test.js @@ -32,13 +32,17 @@ const ErrorHandler = require('@mojaloop/central-services-error-handling') const requestUtil = require('@mojaloop/central-services-shared').Util.Request const Enums = require('@mojaloop/central-services-shared').Enum const Db = require('../../../../../../src/lib/db') -const oracleEndpoint = require('../../../../../../src/models/oracle') +const oracleEndpointCached = require('../../../../../../src/models/oracle/oracleEndpointCached') const participant = require('../../../../../../src/models/participantEndpoint/facade') const participants = require('../../../../../../src/domain/participants') const requestLogger = require('../../../../../../src/lib/requestLogger') const Helper = require('../../../../../util/helper') const initServer = require('../../../../../../src/server').initializeApi +const Logger = require('@mojaloop/central-services-logger') +Logger.isDebugEnabled = jest.fn(() => true) +Logger.isErrorEnabled = jest.fn(() => true) +Logger.isInfoEnabled = jest.fn(() => true) let server let sandbox @@ -98,7 +102,7 @@ describe('/participants/{Type}/{ID}/{SubId}', () => { const stubs = [ sandbox.stub(participant, 'sendErrorToParticipant').resolves({}), sandbox.stub(participant, 'validateParticipant').resolves(true), - sandbox.stub(oracleEndpoint, 'getOracleEndpointByType').resolves(['whatever']), + sandbox.stub(oracleEndpointCached, 'getOracleEndpointByType').resolves(['whatever']), sandbox.stub(requestUtil, 'sendRequest').rejects(badRequestError) ] const response = await server.inject(options) @@ -134,7 +138,7 @@ describe('/participants/{Type}/{ID}/{SubId}', () => { const stubs = [ sandbox.stub(participant, 'sendErrorToParticipant').resolves({}), sandbox.stub(participant, 'validateParticipant').resolves(true), - sandbox.stub(oracleEndpoint, 'getOracleEndpointByType').resolves(['whatever']), + sandbox.stub(oracleEndpointCached, 'getOracleEndpointByType').resolves(['whatever']), sandbox.stub(requestUtil, 'sendRequest').rejects(badRequestError) ] const response = await server.inject(options) diff --git a/test/unit/handlers/participants/{Type}/{ID}/{SubId}/error.test.js b/test/unit/handlers/participants/{Type}/{ID}/{SubId}/error.test.js index 0ffb5441..0e91166d 100644 --- a/test/unit/handlers/participants/{Type}/{ID}/{SubId}/error.test.js +++ b/test/unit/handlers/participants/{Type}/{ID}/{SubId}/error.test.js @@ -33,7 +33,11 @@ const Db = require('../../../../../../../src/lib/db') const participants = require('../../../../../../../src/domain/participants') const ErrHandler = require('../../../../../../../src/handlers/participants/{Type}/{ID}/{SubId}/error') const Helper = require('../../../../../../util/helper') +const Logger = require('@mojaloop/central-services-logger') +Logger.isDebugEnabled = jest.fn(() => true) +Logger.isErrorEnabled = jest.fn(() => true) +Logger.isInfoEnabled = jest.fn(() => true) let server let sandbox const mockContext = jest.fn() diff --git a/test/unit/handlers/parties/endpointcache.test.js b/test/unit/handlers/parties/endpointcache.test.js index c6b08a4d..aaea7f84 100644 --- a/test/unit/handlers/parties/endpointcache.test.js +++ b/test/unit/handlers/parties/endpointcache.test.js @@ -33,7 +33,11 @@ const initServer = require('../../../../src/server').initializeApi const getPort = require('get-port') const Sinon = require('sinon') const MigrationLockModel = require('../../../../src/models/misc/migrationLock') +const Logger = require('@mojaloop/central-services-logger') +Logger.isDebugEnabled = jest.fn(() => true) +Logger.isErrorEnabled = jest.fn(() => true) +Logger.isInfoEnabled = jest.fn(() => true) let sandbox let server diff --git a/test/unit/handlers/parties/parties.test.js b/test/unit/handlers/parties/parties.test.js index d55f1bff..b14182b0 100644 --- a/test/unit/handlers/parties/parties.test.js +++ b/test/unit/handlers/parties/parties.test.js @@ -35,7 +35,11 @@ const Helper = require('../../../util/helper') const participants = require('../../../../src/domain/participants') const initServer = require('../../../../src/server').initializeApi const getPort = require('get-port') +const Logger = require('@mojaloop/central-services-logger') +Logger.isDebugEnabled = jest.fn(() => true) +Logger.isErrorEnabled = jest.fn(() => true) +Logger.isInfoEnabled = jest.fn(() => true) let server let sandbox diff --git a/test/unit/handlers/parties/{Type}/{ID}.test.js b/test/unit/handlers/parties/{Type}/{ID}.test.js index 8f619c42..6c13224b 100644 --- a/test/unit/handlers/parties/{Type}/{ID}.test.js +++ b/test/unit/handlers/parties/{Type}/{ID}.test.js @@ -32,7 +32,6 @@ const Sinon = require('sinon') const initServer = require('../../../../../src/server').initializeApi const Db = require('../../../../../src/lib/db') -const oracleEndpoint = require('../../../../../src/models/oracle') const parties = require('../../../../../src/domain/parties') const participant = require('../../../../../src/models/participantEndpoint/facade') const getPort = require('get-port') @@ -40,7 +39,12 @@ const Helper = require('../../../../util/helper') const ErrorHandler = require('@mojaloop/central-services-error-handling') const requestUtil = require('@mojaloop/central-services-shared').Util.Request const Enums = require('@mojaloop/central-services-shared').Enum +const oracleEndpointCached = require('../../../../../src/models/oracle/oracleEndpointCached') +const Logger = require('@mojaloop/central-services-logger') +Logger.isDebugEnabled = jest.fn(() => true) +Logger.isErrorEnabled = jest.fn(() => true) +Logger.isInfoEnabled = jest.fn(() => true) let server let sandbox @@ -128,7 +132,9 @@ describe('/parties', () => { const stubs = [ sandbox.stub(participant, 'sendErrorToParticipant').resolves({}), sandbox.stub(participant, 'validateParticipant').resolves(true), - sandbox.stub(oracleEndpoint, 'getOracleEndpointByType').resolves(['whatever']), + sandbox.stub(oracleEndpointCached, 'getOracleEndpointByType').resolves(['whatever']), + sandbox.stub(oracleEndpointCached, 'getOracleEndpointByTypeAndCurrency').resolves(['whatever']), + sandbox.stub(oracleEndpointCached, 'getOracleEndpointByCurrency').resolves(['whatever']), sandbox.stub(requestUtil, 'sendRequest').rejects(badRequestError) ] @@ -137,6 +143,7 @@ describe('/parties', () => { // Assert const errorCallStub = stubs[0] + console.log(errorCallStub.args[0][2].errorInformation) expect(errorCallStub.args[0][2].errorInformation.errorCode).toBe('3204') expect(errorCallStub.args[0][1]).toBe(Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_PUT_ERROR) expect(response.statusCode).toBe(202) @@ -170,7 +177,7 @@ describe('/parties', () => { const stubs = [ sandbox.stub(participant, 'sendErrorToParticipant').resolves({}), sandbox.stub(participant, 'validateParticipant').resolves(true), - sandbox.stub(oracleEndpoint, 'getOracleEndpointByType').resolves(['whatever']), + sandbox.stub(oracleEndpointCached, 'getOracleEndpointByType').resolves(['whatever']), sandbox.stub(requestUtil, 'sendRequest').rejects(badRequestError) ] diff --git a/test/unit/handlers/parties/{Type}/{ID}/error.test.js b/test/unit/handlers/parties/{Type}/{ID}/error.test.js index 6eb6ba32..4c0feb66 100644 --- a/test/unit/handlers/parties/{Type}/{ID}/error.test.js +++ b/test/unit/handlers/parties/{Type}/{ID}/error.test.js @@ -37,7 +37,11 @@ const parties = require(`${src}/domain/parties`) const ErrHandler = require(`${src}/handlers/parties/{Type}/{ID}/error`) const Helper = require('../../../../../util/helper') const LibUtil = require(`${src}/lib/util`) +const Logger = require('@mojaloop/central-services-logger') +Logger.isDebugEnabled = jest.fn(() => true) +Logger.isErrorEnabled = jest.fn(() => true) +Logger.isInfoEnabled = jest.fn(() => true) let server let sandbox const mockContext = jest.fn() diff --git a/test/unit/handlers/parties/{Type}/{ID}/{SubId}.test.js b/test/unit/handlers/parties/{Type}/{ID}/{SubId}.test.js index c0ac633e..dfbb1e54 100644 --- a/test/unit/handlers/parties/{Type}/{ID}/{SubId}.test.js +++ b/test/unit/handlers/parties/{Type}/{ID}/{SubId}.test.js @@ -33,11 +33,15 @@ const requestUtil = require('@mojaloop/central-services-shared').Util.Request const Enums = require('@mojaloop/central-services-shared').Enum const initServer = require('../../../../../../src/server').initializeApi const Db = require('../../../../../../src/lib/db') -const oracleEndpoint = require('../../../../../../src/models/oracle') const parties = require('../../../../../../src/domain/parties') const participant = require('../../../../../../src/models/participantEndpoint/facade') const Helper = require('../../../../../util/helper') +const oracleEndpointCached = require('../../../../../../src/models/oracle/oracleEndpointCached') +const Logger = require('@mojaloop/central-services-logger') +Logger.isDebugEnabled = jest.fn(() => true) +Logger.isErrorEnabled = jest.fn(() => true) +Logger.isInfoEnabled = jest.fn(() => true) let server let sandbox @@ -121,7 +125,7 @@ describe('/parties/{Type}/{ID}/{SubId}', () => { const stubs = [ sandbox.stub(participant, 'sendErrorToParticipant').resolves({}), sandbox.stub(participant, 'validateParticipant').resolves(true), - sandbox.stub(oracleEndpoint, 'getOracleEndpointByType').resolves(['whatever']), + sandbox.stub(oracleEndpointCached, 'getOracleEndpointByType').resolves(['whatever']), sandbox.stub(requestUtil, 'sendRequest').rejects(badRequestError) ] @@ -163,7 +167,7 @@ describe('/parties/{Type}/{ID}/{SubId}', () => { const stubs = [ sandbox.stub(participant, 'sendErrorToParticipant').resolves({}), sandbox.stub(participant, 'validateParticipant').resolves(true), - sandbox.stub(oracleEndpoint, 'getOracleEndpointByType').resolves(['whatever']), + sandbox.stub(oracleEndpointCached, 'getOracleEndpointByType').resolves(['whatever']), sandbox.stub(requestUtil, 'sendRequest').rejects(badRequestError) ] diff --git a/test/unit/lib/cache.test.js b/test/unit/lib/cache.test.js new file mode 100644 index 00000000..0a9391a6 --- /dev/null +++ b/test/unit/lib/cache.test.js @@ -0,0 +1,172 @@ +'use strict' + +const Sinon = require('sinon') +const Config = require('../../../src/lib/config') +const Cache = require('../../../src/lib/cache') + +describe('Config tests', () => { + let sandbox + beforeEach(() => { + sandbox = Sinon.createSandbox() + Cache.registerCacheClient({ + id: 'testCacheClient', + preloadCache: async () => sandbox.stub() + }) + }) + + afterEach(() => { + sandbox.restore() + }) + + describe('Cache should', () => { + it('call constructor of CatboxMemory', async () => { + sandbox.stub(Cache.CatboxMemory.Engine) + const catboxMemoryConstructorSpy = sandbox.spy(Cache.CatboxMemory, 'Engine') + await Cache.initCache() + expect(catboxMemoryConstructorSpy.calledOnce).toBeTruthy() + await Cache.destroyCache() + }) + + it('init+start and then stop CatboxMemory', async () => { + sandbox.spy(Cache.CatboxMemory.Engine.prototype, 'start') + sandbox.spy(Cache.CatboxMemory.Engine.prototype, 'stop') + await Cache.initCache() + expect(Cache.CatboxMemory.Engine.prototype.start.calledOnce).toBeTruthy() + expect(Cache.CatboxMemory.Engine.prototype.stop.calledOnce).toBeFalsy() + await Cache.destroyCache() + expect(Cache.CatboxMemory.Engine.prototype.start.calledOnce).toBeTruthy() + expect(Cache.CatboxMemory.Engine.prototype.stop.calledOnce).toBeTruthy() + }) + }) + + describe('Cache client', () => { + it('preload should be called once during Cache.initCache()', async () => { + let preloadCacheCalledCnt = 0 + Cache.registerCacheClient({ + id: 'testCacheClient', + preloadCache: async () => { + preloadCacheCalledCnt++ + } + }) + + // Test participant-getAll gets called during cache init + expect(preloadCacheCalledCnt === 0).toBeTruthy() + await Cache.initCache() + expect(preloadCacheCalledCnt === 1).toBeTruthy() + + // end + await Cache.destroyCache() + }) + + it('get() should call Catbox Memory get()', async () => { + Config.ALS_GENERAL_CACHE_CONFIG.CACHE_ENABLED = true + const getSpy = sandbox.spy(Cache.CatboxMemory.Engine.prototype, 'get') + + const cacheClient = Cache.registerCacheClient({ + id: 'testCacheClient', + preloadCache: async () => {} + }) + + // Test get() + expect(getSpy.called).toBeFalsy() + await Cache.initCache() + getSpy.resetHistory() + await cacheClient.get('') + expect(getSpy.called).toBeTruthy() + + // end + await Cache.destroyCache() + }) + + it('get() should NOT call Catbox Memory get() when cache is disabled', async () => { + Config.ALS_GENERAL_CACHE_CONFIG.CACHE_ENABLED = false + const getSpy = sandbox.spy(Cache.CatboxMemory.Engine.prototype, 'get') + + const cacheClient = Cache.registerCacheClient({ + id: 'testCacheClient', + preloadCache: async () => {} + }) + + // Test get() + expect(getSpy.called).toBeFalsy() + await Cache.initCache() + getSpy.resetHistory() + await cacheClient.get('') + expect(getSpy.called).toBeFalsy() + + // end + await Cache.destroyCache() + }) + + it('set() should call Catbox Memory set() and should work', async () => { + Config.ALS_GENERAL_CACHE_CONFIG.CACHE_ENABLED = true + const getSpy = sandbox.spy(Cache.CatboxMemory.Engine.prototype, 'get') + const setSpy = sandbox.spy(Cache.CatboxMemory.Engine.prototype, 'set') + const cacheClient = Cache.registerCacheClient({ + id: 'testCacheClient', + preloadCache: async () => {} + }) + const testKey = cacheClient.createKey('testKeyName') + const valueToCache = { a: 'some random value', b: 10 } + + // Init cache + expect(setSpy.called).toBeFalsy() + await Cache.initCache() + setSpy.resetHistory() + getSpy.resetHistory() + + // Test set() + await cacheClient.set(testKey, valueToCache) + expect(setSpy.called).toBeTruthy() + + // Verify the value with get() + const valueFromCache = await cacheClient.get(testKey) + expect(getSpy.called).toBeTruthy() + expect(valueFromCache.item).toEqual(valueToCache) + + // end + await Cache.destroyCache() + }) + + it('drop() works', async () => { + Config.ALS_GENERAL_CACHE_CONFIG.CACHE_ENABLED = true + const getSpy = sandbox.spy(Cache.CatboxMemory.Engine.prototype, 'get') + const setSpy = sandbox.spy(Cache.CatboxMemory.Engine.prototype, 'set') + const dropSpy = sandbox.spy(Cache.CatboxMemory.Engine.prototype, 'drop') + const cacheClient = Cache.registerCacheClient({ + id: 'testCacheClient', + preloadCache: async () => {} + }) + const testKey = cacheClient.createKey('testKeyName') + const valueToCache = { a: 'some random value', b: 10 } + + // Init cache + expect(dropSpy.called).toBeFalsy() + expect(getSpy.called).toBeFalsy() + await Cache.initCache() + setSpy.resetHistory() + getSpy.resetHistory() + + // Test set() + await cacheClient.set(testKey, valueToCache) + expect(setSpy.called).toBeTruthy() + + // Verify the value with get() + const valueFromCache = await cacheClient.get(testKey) + expect(getSpy.called).toBeTruthy() + expect(valueFromCache.item).toEqual(valueToCache) + + // Test drop() + await cacheClient.drop(testKey) + expect(setSpy.called).toBeTruthy() + + // Verify the value doesn't exist in cache with get() + const valueFromCacheAfterDrop = await cacheClient.get(testKey) + expect(getSpy.called).toBeTruthy() + expect(valueFromCacheAfterDrop).toBeNull() + + // end + await Cache.destroyCache() + }) + }) +}) diff --git a/test/unit/models/currency/currency.test.js b/test/unit/models/currency/currency.test.js index 8519958f..0371fc62 100644 --- a/test/unit/models/currency/currency.test.js +++ b/test/unit/models/currency/currency.test.js @@ -30,7 +30,11 @@ const Sinon = require('sinon') const Db = require('../../../../src/lib/db') const { getCurrencyById } = require('../../../../src/models/currency') +const Logger = require('@mojaloop/central-services-logger') +Logger.isDebugEnabled = jest.fn(() => true) +Logger.isErrorEnabled = jest.fn(() => true) +Logger.isInfoEnabled = jest.fn(() => true) let sandbox describe('currency model', () => { diff --git a/test/unit/models/endpointType/endpointType.test.js b/test/unit/models/endpointType/endpointType.test.js index 69e2eb0d..9a791674 100644 --- a/test/unit/models/endpointType/endpointType.test.js +++ b/test/unit/models/endpointType/endpointType.test.js @@ -30,7 +30,11 @@ const Sinon = require('sinon') const Db = require('../../../../src/lib/db') const { getEndpointTypeByType } = require('../../../../src/models/endpointType') +const Logger = require('@mojaloop/central-services-logger') +Logger.isDebugEnabled = jest.fn(() => true) +Logger.isErrorEnabled = jest.fn(() => true) +Logger.isInfoEnabled = jest.fn(() => true) let sandbox describe('endpointType model', () => { diff --git a/test/unit/models/oracle/facade.test.js b/test/unit/models/oracle/facade.test.js index e54ac5df..f9abe03d 100644 --- a/test/unit/models/oracle/facade.test.js +++ b/test/unit/models/oracle/facade.test.js @@ -32,8 +32,12 @@ const Enums = require('@mojaloop/central-services-shared').Enum const request = require('@mojaloop/central-services-shared').Util.Request const OracleFacade = require('../../../../src/models/oracle/facade') -const oracleEndpoint = require('../../../../src/models/oracle') +const oracleEndpointCached = require('../../../../src/models/oracle/oracleEndpointCached') +const Logger = require('@mojaloop/central-services-logger') +Logger.isDebugEnabled = jest.fn(() => true) +Logger.isErrorEnabled = jest.fn(() => true) +Logger.isInfoEnabled = jest.fn(() => true) let sandbox describe('Oracle Facade', () => { @@ -71,7 +75,7 @@ describe('Oracle Facade', () => { }, isDefault: false }] - sandbox.stub(oracleEndpoint, 'getOracleEndpointByTypeAndCurrency').resolves(getOracleResponse) + sandbox.stub(oracleEndpointCached, 'getOracleEndpointByTypeAndCurrency').returns(getOracleResponse) const headers = { } @@ -106,7 +110,7 @@ describe('Oracle Facade', () => { }, isDefault: true }] - sandbox.stub(oracleEndpoint, 'getOracleEndpointByTypeAndCurrency').resolves(getOracleResponse) + sandbox.stub(oracleEndpointCached, 'getOracleEndpointByTypeAndCurrency').returns(getOracleResponse) const headers = {} headers[Enums.Http.Headers.FSPIOP.SOURCE] = 'fsp01' headers[Enums.Http.Headers.FSPIOP.DESTINATION] = 'fsp02' @@ -139,7 +143,7 @@ describe('Oracle Facade', () => { }, isDefault: true }] - sandbox.stub(oracleEndpoint, 'getOracleEndpointByTypeAndCurrency').resolves(getOracleResponse) + sandbox.stub(oracleEndpointCached, 'getOracleEndpointByTypeAndCurrency').returns(getOracleResponse) const headers = {} headers[Enums.Http.Headers.FSPIOP.SOURCE] = 'fsp01' headers[Enums.Http.Headers.FSPIOP.DESTINATION] = 'fsp02' @@ -173,7 +177,7 @@ describe('Oracle Facade', () => { }, isDefault: true }] - sandbox.stub(oracleEndpoint, 'getOracleEndpointByType').resolves(getOracleResponse) + sandbox.stub(oracleEndpointCached, 'getOracleEndpointByType').returns(getOracleResponse) const headers = {} headers[Enums.Http.Headers.FSPIOP.SOURCE] = 'fsp01' headers[Enums.Http.Headers.FSPIOP.DESTINATION] = 'fsp02' @@ -216,7 +220,7 @@ describe('Oracle Facade', () => { }, isDefault: false }] - sandbox.stub(oracleEndpoint, 'getOracleEndpointByType').resolves(getOracleResponse) + sandbox.stub(oracleEndpointCached, 'getOracleEndpointByType').returns(getOracleResponse) const headers = {} headers[Enums.Http.Headers.FSPIOP.SOURCE] = 'fsp01' headers[Enums.Http.Headers.FSPIOP.DESTINATION] = 'fsp02' @@ -242,7 +246,7 @@ describe('Oracle Facade', () => { requestStub.resolves(true) const getOracleResponse = [] - sandbox.stub(oracleEndpoint, 'getOracleEndpointByType').resolves(getOracleResponse) + sandbox.stub(oracleEndpointCached, 'getOracleEndpointByType').returns(getOracleResponse) const headers = {} headers[Enums.Http.Headers.FSPIOP.SOURCE] = 'fsp01' headers[Enums.Http.Headers.FSPIOP.DESTINATION] = 'fsp02' @@ -282,7 +286,7 @@ describe('Oracle Facade', () => { }, isDefault: false }] - sandbox.stub(oracleEndpoint, 'getOracleEndpointByTypeAndCurrency').resolves(getOracleResponse) + sandbox.stub(oracleEndpointCached, 'getOracleEndpointByTypeAndCurrency').returns(getOracleResponse) const headers = {} headers[Enums.Http.Headers.FSPIOP.SOURCE] = 'fsp01' headers[Enums.Http.Headers.FSPIOP.DESTINATION] = 'fsp02' @@ -308,7 +312,7 @@ describe('Oracle Facade', () => { requestStub.resolves(true) const getOracleResponse = [] - sandbox.stub(oracleEndpoint, 'getOracleEndpointByTypeAndCurrency').resolves(getOracleResponse) + sandbox.stub(oracleEndpointCached, 'getOracleEndpointByTypeAndCurrency').returns(getOracleResponse) const headers = {} headers[Enums.Http.Headers.FSPIOP.SOURCE] = 'fsp01' headers[Enums.Http.Headers.FSPIOP.DESTINATION] = 'fsp02' @@ -331,7 +335,7 @@ describe('Oracle Facade', () => { requestStub.resolves(true) const getOracleResponse = [] - sandbox.stub(oracleEndpoint, 'getOracleEndpointByTypeAndCurrency').resolves(getOracleResponse) + sandbox.stub(oracleEndpointCached, 'getOracleEndpointByTypeAndCurrency').returns(getOracleResponse) const headers = {} headers[Enums.Http.Headers.FSPIOP.SOURCE] = 'fsp01' headers[Enums.Http.Headers.FSPIOP.DESTINATION] = 'fsp02' @@ -364,7 +368,7 @@ describe('Oracle Facade', () => { }, isDefault: true }] - sandbox.stub(oracleEndpoint, 'getOracleEndpointByType').resolves(getOracleResponse) + sandbox.stub(oracleEndpointCached, 'getOracleEndpointByType').returns(getOracleResponse) const headers = {} headers[Enums.Http.Headers.FSPIOP.SOURCE] = 'fsp01' headers[Enums.Http.Headers.FSPIOP.DESTINATION] = 'fsp02' @@ -402,7 +406,7 @@ describe('Oracle Facade', () => { isDefault: false }] - sandbox.stub(oracleEndpoint, 'getOracleEndpointByType').resolves(getOracleResponse) + sandbox.stub(oracleEndpointCached, 'getOracleEndpointByType').returns(getOracleResponse) const headers = {} headers[Enums.Http.Headers.FSPIOP.SOURCE] = 'fsp01' headers[Enums.Http.Headers.FSPIOP.DESTINATION] = 'fsp02' @@ -422,7 +426,7 @@ describe('Oracle Facade', () => { requestStub.resolves(true) const getOracleResponse = [] - sandbox.stub(oracleEndpoint, 'getOracleEndpointByType').resolves(getOracleResponse) + sandbox.stub(oracleEndpointCached, 'getOracleEndpointByType').returns(getOracleResponse) const headers = {} headers[Enums.Http.Headers.FSPIOP.SOURCE] = 'fsp01' headers[Enums.Http.Headers.FSPIOP.DESTINATION] = 'fsp02' @@ -467,7 +471,7 @@ describe('Oracle Facade', () => { isDefault: false }] - sandbox.stub(oracleEndpoint, 'getOracleEndpointByType').resolves(getOracleResponse) + sandbox.stub(oracleEndpointCached, 'getOracleEndpointByType').returns(getOracleResponse) const headers = {} headers[Enums.Http.Headers.FSPIOP.SOURCE] = 'fsp01' headers[Enums.Http.Headers.FSPIOP.DESTINATION] = 'fsp02' @@ -498,7 +502,7 @@ describe('Oracle Facade', () => { isDefault: true }] - sandbox.stub(oracleEndpoint, 'getOracleEndpointByTypeAndCurrency').resolves(getOracleResponse) + sandbox.stub(oracleEndpointCached, 'getOracleEndpointByTypeAndCurrency').returns(getOracleResponse) const headers = {} headers[Enums.Http.Headers.FSPIOP.SOURCE] = 'fsp01' headers[Enums.Http.Headers.FSPIOP.DESTINATION] = 'fsp02' @@ -522,7 +526,7 @@ describe('Oracle Facade', () => { requestStub.resolves(true) const getOracleResponse = [] - sandbox.stub(oracleEndpoint, 'getOracleEndpointByTypeAndCurrency').resolves(getOracleResponse) + sandbox.stub(oracleEndpointCached, 'getOracleEndpointByTypeAndCurrency').returns(getOracleResponse) const headers = {} headers[Enums.Http.Headers.FSPIOP.SOURCE] = 'fsp01' headers[Enums.Http.Headers.FSPIOP.DESTINATION] = 'fsp02' diff --git a/test/unit/models/oracle/oracleEndpoint.test.js b/test/unit/models/oracle/oracleEndpoint.test.js index 2540eb1b..6c0c9615 100644 --- a/test/unit/models/oracle/oracleEndpoint.test.js +++ b/test/unit/models/oracle/oracleEndpoint.test.js @@ -30,7 +30,11 @@ const Sinon = require('sinon') const Db = require('../../../../src/lib/db') const oracleEndpoint = require('../../../../src/models/oracle/oracleEndpoint') +const Logger = require('@mojaloop/central-services-logger') +Logger.isDebugEnabled = jest.fn(() => true) +Logger.isErrorEnabled = jest.fn(() => true) +Logger.isInfoEnabled = jest.fn(() => true) let sandbox const getOracleDatabaseResponse = [{ diff --git a/test/unit/models/oracle/oracleEndpointCached.test.js b/test/unit/models/oracle/oracleEndpointCached.test.js new file mode 100644 index 00000000..6166f7e8 --- /dev/null +++ b/test/unit/models/oracle/oracleEndpointCached.test.js @@ -0,0 +1,153 @@ +/***** + License + -------------- + Copyright © 2017 Bill & Melinda Gates Foundation + The Mojaloop files are made available by the Bill & Melinda Gates Foundation under the Apache License, Version 2.0 (the "License") and you may not use these files except in compliance with the License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, the Mojaloop files are distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + Contributors + -------------- + This is the official list of the Mojaloop project contributors for this file. + Names of the original copyright holders (individuals or organizations) + should be listed with a '*' in the first column. People who have + contributed from an organization can be listed under the organization + that actually holds the copyright for their contributions (see the + Gates Foundation organization for an example). Those individuals should have + their names indented and be marked with a '-'. Email address can be added + optionally within square brackets . + * Gates Foundation + * Name Surname + + * Kevin Leyow + + -------------- + ******/ + +'use strict' + +const Sinon = require('sinon') +const OracleEndpointUncached = require('../../../../src/models/oracle/oracleEndpoint') +const Cache = require('../../../../src/lib/cache') +const Model = require('../../../../src/models/oracle/oracleEndpointCached') +const Db = require('../../../../src/lib/db') +const Logger = require('@mojaloop/central-services-logger') + +Logger.isDebugEnabled = jest.fn(() => true) +Logger.isErrorEnabled = jest.fn(() => true) +Logger.isInfoEnabled = jest.fn(() => true) +let sandbox + +describe('ParticipantCurrency cached model', () => { + const oracleEndpoints = [ + { + oracleEndpointId: 1, + partyIdType: 1, // MSISDN + endpointTypeId: 1, // URL + currencyId: 'USD', + value: 'http://endpoint', + isDefault: 1, + isActive: 1, + createdDate: '2023-11-16 14:57:55', + createdBy: 'Admin' + } + ] + beforeEach(() => { + sandbox = Sinon.createSandbox() + sandbox.stub(Cache) + sandbox.stub(Db, 'connect').returns(Promise.resolve({})) + sandbox.stub(OracleEndpointUncached, 'getOracleEndpointByTypeAndCurrency').returns(oracleEndpoints) + sandbox.stub(OracleEndpointUncached, 'getOracleEndpointByCurrency').returns(oracleEndpoints) + sandbox.stub(OracleEndpointUncached, 'getOracleEndpointByType').returns(oracleEndpoints) + }) + + afterEach(() => { + sandbox.restore() + }) + + it('initializes cache correctly', async () => { + const cacheClient = { + createKey: sandbox.stub().returns({}) + } + Cache.registerCacheClient.returns(cacheClient) + + // initialize calls registerCacheClient and createKey + expect(Cache.registerCacheClient.calledOnce).toBeFalsy() + expect(cacheClient.createKey.calledOnce).toBeFalsy() + await Model.initialize() + expect(Cache.registerCacheClient.calledOnce).toBeTruthy() + }) + + it('getOracleEndpointByTypeAndCurrency calls correct uncached function', async () => { + let cache = null + const cacheClient = { + createKey: sandbox.stub().returns({}), + get: () => cache, + set: (key, x) => { + cache = { item: x } // the cache returns {item: } structure + } + } + Cache.registerCacheClient.returns(cacheClient) + await Model.initialize() + + await Model.getOracleEndpointByTypeAndCurrency('MSISDN', 'USD') + expect(OracleEndpointUncached.getOracleEndpointByTypeAndCurrency.calledOnce).toBeTruthy() + }) + + it('getOracleEndpointByType calls correct uncached function', async () => { + let cache = null + const cacheClient = { + createKey: sandbox.stub().returns({}), + get: () => cache, + set: (key, x) => { + cache = { item: x } // the cache returns {item: } structure + } + } + Cache.registerCacheClient.returns(cacheClient) + await Model.initialize() + await Model.getOracleEndpointByType('MSISDN') + expect(OracleEndpointUncached.getOracleEndpointByType.calledOnce).toBeTruthy() + }) + + it('getOracleEndpointByCurrency calls correct uncached function', async () => { + let cache = null + const cacheClient = { + createKey: sandbox.stub().returns({}), + get: () => cache, + set: (key, x) => { + cache = { item: x } // the cache returns {item: } structure + } + } + Cache.registerCacheClient.returns(cacheClient) + await Model.initialize() + + await Model.getOracleEndpointByCurrency('USD') + expect(OracleEndpointUncached.getOracleEndpointByCurrency.calledOnce).toBeTruthy() + }) + + it('queries call set on null cache', async () => { + const cache = null + const cacheClient = { + createKey: sandbox.stub().returns({}), + get: () => cache, + set: sandbox.stub().returns({}) + } + Cache.registerCacheClient.returns(cacheClient) + await Model.initialize() + + await Model.getOracleEndpointByTypeAndCurrency('MSISDN', 'USD') + expect(cacheClient.set.called).toBeTruthy() + }) + + it('queries hit cache when item is found', async () => { + const cacheClient = { + createKey: sandbox.stub().returns({}), + get: sandbox.stub().returns({ item: oracleEndpoints }), + set: sandbox.stub().returns({}) + } + Cache.registerCacheClient.returns(cacheClient) + await Model.initialize() + + await Model.getOracleEndpointByTypeAndCurrency('MSISDN', 'USD') + expect(cacheClient.get.called).toBeTruthy() + }) +}) diff --git a/test/unit/models/participantEndpoint/facade.test.js b/test/unit/models/participantEndpoint/facade.test.js index 5a06108e..419a5714 100644 --- a/test/unit/models/participantEndpoint/facade.test.js +++ b/test/unit/models/participantEndpoint/facade.test.js @@ -51,9 +51,14 @@ jest.mock('@mojaloop/central-services-shared', () => ({ }, Enum: mockEnums })) +const Logger = require('@mojaloop/central-services-logger') + +Logger.isDebugEnabled = jest.fn(() => true) +Logger.isErrorEnabled = jest.fn(() => true) +Logger.isInfoEnabled = jest.fn(() => true) describe('participantEndpoint Facade', () => { - beforeEach(() => { + afterEach(() => { jest.resetModules() jest.clearAllMocks() }) diff --git a/test/unit/models/partyIdType/partyIdType.test.js b/test/unit/models/partyIdType/partyIdType.test.js index c5d3ddd7..b1db0843 100644 --- a/test/unit/models/partyIdType/partyIdType.test.js +++ b/test/unit/models/partyIdType/partyIdType.test.js @@ -33,6 +33,11 @@ const Sinon = require('sinon') const { getPartyIdTypeByName } = require('../../../../src/models/partyIdType/partyIdType') const Db = require('../../../../src/lib/db') +const Logger = require('@mojaloop/central-services-logger') + +Logger.isDebugEnabled = jest.fn(() => true) +Logger.isErrorEnabled = jest.fn(() => true) +Logger.isInfoEnabled = jest.fn(() => true) describe('partyIdType Model', () => { let sandbox