diff --git a/Dockerfile b/Dockerfile index d24bc5f3..719cb390 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,7 +18,7 @@ RUN apk --no-cache add git RUN apk add --no-cache -t build-dependencies make gcc g++ python3 py3-setuptools libtool openssl-dev autoconf automake bash \ && cd $(npm root -g)/npm -COPY package.json package-lock.json* /opt/app/ +COPY package.json package-lock.json /opt/app/ RUN npm ci @@ -26,7 +26,6 @@ COPY src /opt/app/src COPY config /opt/app/config COPY migrations /opt/app/migrations COPY seeds /opt/app/seeds -COPY test /opt/app/test FROM node:${NODE_VERSION} WORKDIR /opt/app diff --git a/config/default.json b/config/default.json index dc521cf7..ee054293 100644 --- a/config/default.json +++ b/config/default.json @@ -57,10 +57,11 @@ }, "PROXY_CACHE": { "enabled": true, - "type": "redis", + "type": "redis-cluster", "proxyConfig": { - "host": "localhost", - "port": 6379 + "cluster": [ + { "host": "localhost", "port": 6379 } + ] } }, "ERROR_HANDLING": { diff --git a/docker-compose.yml b/docker-compose.yml index cb97a72b..fa7395bd 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,6 +4,14 @@ networks: als-mojaloop-net: name: als-mojaloop-net +x-redis-node: &REDIS_NODE + image: docker.io/bitnami/redis-cluster:6.2.14 + environment: &REDIS_ENVS + ALLOW_EMPTY_PASSWORD: yes + REDIS_NODES: redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5 + networks: + - als-mojaloop-net + services: account-lookup-service: image: mojaloop/account-lookup-service:local @@ -25,7 +33,7 @@ services: - central-ledger - proxy - mysql-als - - redis + - redis-node-0 volumes: - ./secrets:/opt/app/secrets - ./src:/opt/app/src @@ -139,15 +147,39 @@ services: start_period: 40s interval: 30s - redis: - image: redis:6.2.4-alpine - restart: "unless-stopped" + # To use with proxyCache.type === 'redis-cluster' + redis-node-0: + <<: *REDIS_NODE environment: - - ALLOW_EMPTY_PASSWORD=yes - - REDIS_PORT=6379 - - REDIS_REPLICATION_MODE=master - - REDIS_TLS_ENABLED=no + <<: *REDIS_ENVS + REDIS_CLUSTER_CREATOR: yes + depends_on: + - redis-node-1 + - redis-node-2 + - redis-node-3 + - redis-node-4 + - redis-node-5 ports: - "6379:6379" - networks: - - als-mojaloop-net + + redis-node-1: + <<: *REDIS_NODE + redis-node-2: + <<: *REDIS_NODE + redis-node-3: + <<: *REDIS_NODE + redis-node-4: + <<: *REDIS_NODE + redis-node-5: + <<: *REDIS_NODE + +## To use with proxyCache.type === 'redis' +# redis: +# image: redis:6.2.4-alpine +# restart: "unless-stopped" +# environment: +# - ALLOW_EMPTY_PASSWORD=yes +# - REDIS_PORT=6379 +# - REDIS_REPLICATION_MODE=master +# ports: +# - "6379:6379" diff --git a/docker/account-lookup-service/default.json b/docker/account-lookup-service/default.json index efa3d291..a94dc1fd 100644 --- a/docker/account-lookup-service/default.json +++ b/docker/account-lookup-service/default.json @@ -58,11 +58,11 @@ }, "PROXY_CACHE": { "enabled": true, - "enabled": true, - "type": "redis", + "type": "redis-cluster", "proxyConfig": { - "host": "redis", - "port": 6379 + "cluster": [ + { "host": "redis-node-0", "port": 6379 } + ] } }, "ERROR_HANDLING": { diff --git a/docker/account-lookup-service/override.json b/docker/account-lookup-service/override.json index 2f75b518..5c1477d6 100644 --- a/docker/account-lookup-service/override.json +++ b/docker/account-lookup-service/override.json @@ -3,8 +3,11 @@ "HOST": "mysql-als" }, "PROXY_CACHE": { + "type": "redis-cluster", "proxyConfig": { - "host": "redis" + "cluster": [ + { "host": "redis-node-0", "port": 6379 } + ] } }, "SWITCH_ENDPOINT": "http://central-ledger:3001", diff --git a/package-lock.json b/package-lock.json index 45f7eecd..138d0d83 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "account-lookup-service", - "version": "15.4.0-snapshot.18", + "version": "15.4.0-snapshot.25", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "account-lookup-service", - "version": "15.4.0-snapshot.18", + "version": "15.4.0-snapshot.25", "license": "Apache-2.0", "dependencies": { "@hapi/basic": "7.0.2", @@ -19,14 +19,14 @@ "@hapi/vision": "7.0.3", "@mojaloop/central-services-error-handling": "13.0.1", "@mojaloop/central-services-health": "15.0.0", - "@mojaloop/central-services-logger": "11.3.1", + "@mojaloop/central-services-logger": "11.5.0", "@mojaloop/central-services-metrics": "12.0.8", - "@mojaloop/central-services-shared": "^18.6.0-snapshot.8", + "@mojaloop/central-services-shared": "18.6.0-snapshot.9", "@mojaloop/central-services-stream": "11.3.1", "@mojaloop/database-lib": "11.0.6", "@mojaloop/event-sdk": "14.1.1", - "@mojaloop/inter-scheme-proxy-cache-lib": "^1.4.0", - "@mojaloop/sdk-standard-components": "^18.4.0-snapshot.0", + "@mojaloop/inter-scheme-proxy-cache-lib": "2.2.0", + "@mojaloop/sdk-standard-components": "18.4.0-snapshot.0", "@now-ims/hapi-now-auth": "2.1.0", "ajv": "8.17.1", "ajv-keywords": "5.1.0", @@ -46,6 +46,7 @@ "@types/jest": "29.5.12", "audit-ci": "^7.1.0", "axios": "1.7.2", + "axios-retry": "^4.4.2", "docdash": "2.0.2", "dotenv": "^16.4.5", "get-port": "5.1.1", @@ -128,30 +129,30 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.7.tgz", - "integrity": "sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw==", + "version": "7.24.9", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.9.tgz", + "integrity": "sha512-e701mcfApCJqMMueQI0Fb68Amflj83+dvAvHawoBpAz+GDjCIyGHzNwnefjsWJ3xiYAqqiQFoWbspGYBdb2/ng==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.7.tgz", - "integrity": "sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g==", + "version": "7.24.9", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.9.tgz", + "integrity": "sha512-5e3FI4Q3M3Pbr21+5xJwCv6ZT6KmGkI0vw3Tozy5ODAQFTIWe37iT8Cr7Ice2Ntb+M3iSKCEWMB1MBgKrW3whg==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.7", - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helpers": "^7.24.7", - "@babel/parser": "^7.24.7", + "@babel/generator": "^7.24.9", + "@babel/helper-compilation-targets": "^7.24.8", + "@babel/helper-module-transforms": "^7.24.9", + "@babel/helpers": "^7.24.8", + "@babel/parser": "^7.24.8", "@babel/template": "^7.24.7", - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7", + "@babel/traverse": "^7.24.8", + "@babel/types": "^7.24.9", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -176,12 +177,12 @@ } }, "node_modules/@babel/generator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.7.tgz", - "integrity": "sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==", + "version": "7.24.10", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.10.tgz", + "integrity": "sha512-o9HBZL1G2129luEUlG1hB4N/nlYNWHnpwlND9eOMclRqqu1YDy2sSYVCFUZwl8I1Gxh+QSRrP2vD7EpUmFVXxg==", "dev": true, "dependencies": { - "@babel/types": "^7.24.7", + "@babel/types": "^7.24.9", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^2.5.1" @@ -191,14 +192,14 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.7.tgz", - "integrity": "sha512-ctSdRHBi20qWOfy27RUb4Fhp07KSJ3sXcuSvTrXrc4aG8NSYDo1ici3Vhg9bg69y5bj0Mr1lh0aeEgTvc12rMg==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.8.tgz", + "integrity": "sha512-oU+UoqCHdp+nWVDkpldqIQL/i/bvAv53tRqLG/s+cOXxe66zOYLU7ar/Xs3LdmBihrUMEUhwu6dMZwbNOYDwvw==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.24.7", - "@babel/helper-validator-option": "^7.24.7", - "browserslist": "^4.22.2", + "@babel/compat-data": "^7.24.8", + "@babel/helper-validator-option": "^7.24.8", + "browserslist": "^4.23.1", "lru-cache": "^5.1.1", "semver": "^6.3.1" }, @@ -281,9 +282,9 @@ } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.7.tgz", - "integrity": "sha512-1fuJEwIrp+97rM4RWdO+qrRsZlAeL1lQJoPqtCYWv0NL115XM93hIH4CSRln2w52SqvmY5hqdtauB6QFCDiZNQ==", + "version": "7.24.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.9.tgz", + "integrity": "sha512-oYbh+rtFKj/HwBQkFlUzvcybzklmVdVV3UU+mN7n2t/q3yGHbuVdNxyFvSBO1tfvjyArpHNcWMAzsSPdyI46hw==", "dev": true, "dependencies": { "@babel/helper-environment-visitor": "^7.24.7", @@ -300,9 +301,9 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.7.tgz", - "integrity": "sha512-Rq76wjt7yz9AAc1KnlRKNAi/dMSVWgDRx43FHoJEbcYU6xOWaE2dVPwcdTukJrjxS65GITyfbvEYHvkirZ6uEg==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.8.tgz", + "integrity": "sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==", "dev": true, "engines": { "node": ">=6.9.0" @@ -334,9 +335,9 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.7.tgz", - "integrity": "sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", + "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==", "dev": true, "engines": { "node": ">=6.9.0" @@ -352,22 +353,22 @@ } }, "node_modules/@babel/helper-validator-option": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.7.tgz", - "integrity": "sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz", + "integrity": "sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.7.tgz", - "integrity": "sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.8.tgz", + "integrity": "sha512-gV2265Nkcz7weJJfvDoAEVzC1e2OTDpkGbEsebse8koXUJUXPsCMi7sRo/+SPMuMZ9MtUPnGwITTnQnU5YjyaQ==", "dev": true, "dependencies": { "@babel/template": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/types": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -460,9 +461,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.7.tgz", - "integrity": "sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.8.tgz", + "integrity": "sha512-WzfbgXOkGzZiXXCqk43kKwZjzwx4oulxZi3nq2TYL9mOjQv6kYwul9mz6ID36njuL7Xkp6nJEfok848Zj10j/w==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -663,19 +664,19 @@ } }, "node_modules/@babel/traverse": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.7.tgz", - "integrity": "sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.8.tgz", + "integrity": "sha512-t0P1xxAPzEDcEPmjprAQq19NWum4K0EQPjMwZQZbHt+GiZqvjCHjj755Weq1YRPVzBI+3zSfvScfpnuIecVFJQ==", "dev": true, "dependencies": { "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.7", + "@babel/generator": "^7.24.8", "@babel/helper-environment-visitor": "^7.24.7", "@babel/helper-function-name": "^7.24.7", "@babel/helper-hoist-variables": "^7.24.7", "@babel/helper-split-export-declaration": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/types": "^7.24.7", + "@babel/parser": "^7.24.8", + "@babel/types": "^7.24.8", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -684,12 +685,12 @@ } }, "node_modules/@babel/types": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.7.tgz", - "integrity": "sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==", + "version": "7.24.9", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.9.tgz", + "integrity": "sha512-xm8XrMKz0IlUdocVbYJe0Z9xEgidU7msskG8BbhnTPK/HZ2z/7FP7ykqPgrUH+C+r414mNfNWam1f2vqOjqjYQ==", "dev": true, "dependencies": { - "@babel/helper-string-parser": "^7.24.7", + "@babel/helper-string-parser": "^7.24.8", "@babel/helper-validator-identifier": "^7.24.7", "to-fast-properties": "^2.0.0" }, @@ -858,9 +859,9 @@ "dev": true }, "node_modules/@grpc/grpc-js": { - "version": "1.10.10", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.10.10.tgz", - "integrity": "sha512-HPa/K5NX6ahMoeBv15njAc/sfF4/jmiXLar9UlC2UfHFKZzsCVLc3wbe7+7qua7w9VPh2/L6EBxyAV7/E8Wftg==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.11.1.tgz", + "integrity": "sha512-gyt/WayZrVPH2w/UTLansS7F9Nwld472JxxaETamrM8HNlsa+jSLNyKAZmhxI2Me4c3mQHFiS1wWHDY1g1Kthw==", "dependencies": { "@grpc/proto-loader": "^0.7.13", "@js-sdsl/ordered-map": "^4.4.2" @@ -1952,15 +1953,14 @@ } }, "node_modules/@mojaloop/central-services-logger": { - "version": "11.3.1", - "resolved": "https://registry.npmjs.org/@mojaloop/central-services-logger/-/central-services-logger-11.3.1.tgz", - "integrity": "sha512-XVU2K5grE1ZcIyxUXeMlvoVkeIcs9y1/0EKxa2Bk5sEbqXUtHuR8jqbAGlwaUIi9T9YWZRJyVC77nOQe/X1teA==", + "version": "11.5.0", + "resolved": "https://registry.npmjs.org/@mojaloop/central-services-logger/-/central-services-logger-11.5.0.tgz", + "integrity": "sha512-pH73RiJ5fKTBTSdLocp1vPBad1D+Kh0HufdcfjLaBQj3dIBq72si0k+Z3L1MeOmMqMzpj+8M/he/izlgqJjVJA==", "dependencies": { - "@types/node": "^20.12.7", "parse-strings-in-object": "2.0.0", "rc": "1.2.8", "safe-stable-stringify": "^2.4.3", - "winston": "3.13.0" + "winston": "3.13.1" } }, "node_modules/@mojaloop/central-services-metrics": { @@ -1972,9 +1972,9 @@ } }, "node_modules/@mojaloop/central-services-shared": { - "version": "18.6.0-snapshot.8", - "resolved": "https://registry.npmjs.org/@mojaloop/central-services-shared/-/central-services-shared-18.6.0-snapshot.8.tgz", - "integrity": "sha512-O06V2ibGLjHhG0868M+M1YD6Z45zDQDOTs129Y6Gg27JAVFtw1HpbJS09c50WFXzyO+EJkBAPSrMWOKg6dNSfQ==", + "version": "18.6.0-snapshot.9", + "resolved": "https://registry.npmjs.org/@mojaloop/central-services-shared/-/central-services-shared-18.6.0-snapshot.9.tgz", + "integrity": "sha512-JU7+su7ocESOOfaof3hY7qYr0R0HAvE1uDsQwQS+Sz3SEq2hXFaaifn6bUSOQmGlZRdF6uCplhqdJ8LPXPi7Pg==", "dependencies": { "@hapi/catbox": "12.1.1", "@hapi/catbox-memory": "5.0.1", @@ -1983,6 +1983,7 @@ "dotenv": "16.4.5", "env-var": "7.5.0", "event-stream": "4.0.1", + "fast-safe-stringify": "^2.1.1", "immutable": "4.3.6", "lodash": "4.17.21", "mustache": "4.2.0", @@ -2111,18 +2112,60 @@ } } }, + "node_modules/@mojaloop/event-sdk/node_modules/@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@mojaloop/event-sdk/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/@mojaloop/event-sdk/node_modules/tslib": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" }, + "node_modules/@mojaloop/event-sdk/node_modules/winston": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.13.0.tgz", + "integrity": "sha512-rwidmA1w3SE4j0E5MuIufFhyJPBDG7Nu71RkZor1p2+qHvJSZ9GYDA81AyleQcZbh/+V6HjeBdfnTZJm9rSeQQ==", + "dependencies": { + "@colors/colors": "^1.6.0", + "@dabh/diagnostics": "^2.0.2", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.4.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.7.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, "node_modules/@mojaloop/inter-scheme-proxy-cache-lib": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@mojaloop/inter-scheme-proxy-cache-lib/-/inter-scheme-proxy-cache-lib-1.4.0.tgz", - "integrity": "sha512-jmAWWdjZxjxlSQ+wt8aUcMYOneVo1GNbIIs7yK/R2K9DBtKb0aYle2mWwdjm9ovk6zSWL2a9lH+n3hq7kb08Wg==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@mojaloop/inter-scheme-proxy-cache-lib/-/inter-scheme-proxy-cache-lib-2.2.0.tgz", + "integrity": "sha512-QrbJlhy7f7Tf1DTjspxqtw0oN3eUAm5zKfCm7moQIYFEV3MYF3rsbODLpgxyzmAO8FFi2Dky/ff7QMVnlA/P9A==", "dependencies": { - "@mojaloop/central-services-logger": "^11.3.1", - "ajv": "^8.16.0", + "@mojaloop/central-services-logger": "11.5.0", + "ajv": "^8.17.1", "convict": "^6.2.4", "fast-safe-stringify": "^2.1.1", "ioredis": "^5.4.1" @@ -2827,9 +2870,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.14.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.10.tgz", - "integrity": "sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ==", + "version": "20.14.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.11.tgz", + "integrity": "sha512-kprQpL8MMeszbz6ojB5/tU8PLN4kesnN8Gjzw349rDlNgsSzg90lAVj3llK99Dh7JON+t9AuscPPFW6mPbTnSA==", "dependencies": { "undici-types": "~5.26.4" } @@ -3282,18 +3325,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/array.prototype.toreversed": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/array.prototype.toreversed/-/array.prototype.toreversed-1.1.2.tgz", - "integrity": "sha512-wwDCoT4Ck4Cz7sLtgUmzR5UV3YF5mFHUlbChCzZBQZ+0m2cl/DH3tKgvphv1nKgFsJ48oCSg6p91q2Vm0I/ZMA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" - } - }, "node_modules/array.prototype.tosorted": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", @@ -3425,6 +3456,18 @@ "proxy-from-env": "^1.1.0" } }, + "node_modules/axios-retry": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/axios-retry/-/axios-retry-4.4.2.tgz", + "integrity": "sha512-2fjo9uDNBQjX8+GMEGOFG5TrLMZ3QijjeRYcgBI2MhrsabnvcIAfLxxIhG7+CCD68vPEQY3IM2llV70ZsrqPvA==", + "dev": true, + "dependencies": { + "is-retry-allowed": "^2.2.0" + }, + "peerDependencies": { + "axios": "0.x || 1.x" + } + }, "node_modules/babel-jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", @@ -4122,9 +4165,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001641", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001641.tgz", - "integrity": "sha512-Phv5thgl67bHYo1TtMY/MurjkHhV4EDaCosezRXgZ8jzA/Ub+wjxAvbGvjoFENStinwi5kCyOYV3mi5tOGykwA==", + "version": "1.0.30001643", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001643.tgz", + "integrity": "sha512-ERgWGNleEilSrHM6iUz/zJNSQTP8Mr21wDWpdgvRwcTXGAq6jMtOUPP4dqFPTdKqZ2wKTdtB+uucZ3MRpAUSmg==", "dev": true, "funding": [ { @@ -5741,9 +5784,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.823", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.823.tgz", - "integrity": "sha512-4h+oPeAiGQOHFyUJOqpoEcPj/xxlicxBzOErVeYVMMmAiXUXsGpsFd0QXBMaUUbnD8hhSfLf9uw+MlsoIA7j5w==", + "version": "1.4.832", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.832.tgz", + "integrity": "sha512-cTen3SB0H2SGU7x467NRe1eVcQgcuS6jckKfWJHia2eo0cHIGOqHoAxevIYZD4eRHcWjkvFzo93bi3vJ9W+1lA==", "dev": true }, "node_modules/emittery": { @@ -6327,9 +6370,9 @@ } }, "node_modules/eslint-plugin-promise": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.4.0.tgz", - "integrity": "sha512-/KWWRaD3fGkVCZsdR0RU53PSthFmoHVhZl+y9+6DqeDLSikLdlUVpVEAmI6iCRR5QyOjBYBqHZV/bdv4DJ4Gtw==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.6.0.tgz", + "integrity": "sha512-57Zzfw8G6+Gq7axm2Pdo3gW/Rx3h9Yywgn61uE/3elTCOePEHVrn2i5CdfBwA1BLK0Q0WqctICIUSqXZW/VprQ==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -6342,35 +6385,35 @@ } }, "node_modules/eslint-plugin-react": { - "version": "7.34.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.34.3.tgz", - "integrity": "sha512-aoW4MV891jkUulwDApQbPYTVZmeuSyFrudpbTAQuj5Fv8VL+o6df2xIGpw8B0hPjAaih1/Fb0om9grCdyFYemA==", + "version": "7.35.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.35.0.tgz", + "integrity": "sha512-v501SSMOWv8gerHkk+IIQBkcGRGrO2nfybfj5pLxuJNFTPxxA3PSryhXTK+9pNbtkggheDdsC0E9Q8CuPk6JKA==", "dev": true, "dependencies": { "array-includes": "^3.1.8", "array.prototype.findlast": "^1.2.5", "array.prototype.flatmap": "^1.3.2", - "array.prototype.toreversed": "^1.1.2", "array.prototype.tosorted": "^1.1.4", "doctrine": "^2.1.0", "es-iterator-helpers": "^1.0.19", "estraverse": "^5.3.0", + "hasown": "^2.0.2", "jsx-ast-utils": "^2.4.1 || ^3.0.0", "minimatch": "^3.1.2", "object.entries": "^1.1.8", "object.fromentries": "^2.0.8", - "object.hasown": "^1.1.4", "object.values": "^1.2.0", "prop-types": "^15.8.1", "resolve": "^2.0.0-next.5", "semver": "^6.3.1", - "string.prototype.matchall": "^4.0.11" + "string.prototype.matchall": "^4.0.11", + "string.prototype.repeat": "^1.0.0" }, "engines": { "node": ">=4" }, "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" } }, "node_modules/eslint-plugin-react/node_modules/doctrine": { @@ -7875,9 +7918,9 @@ } }, "node_modules/handlebars/node_modules/uglify-js": { - "version": "3.18.0", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.18.0.tgz", - "integrity": "sha512-SyVVbcNBCk0dzr9XL/R/ySrmYf0s372K6/hFklzgcp2lBFyXtw4I7BOdDjlLhE1aVqaI/SHWXWmYdlZxuyF38A==", + "version": "3.19.0", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.0.tgz", + "integrity": "sha512-wNKHUY2hYYkf6oSFfhwwiHo4WCHzHmzcXsqXYTN9ja3iApYIFbb2U6ics9hBcYLHcYGQoAlwnZlTrf3oF+BL/Q==", "dev": true, "optional": true, "bin": { @@ -8512,9 +8555,9 @@ } }, "node_modules/import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", "dev": true, "dependencies": { "pkg-dir": "^4.2.0", @@ -8773,9 +8816,9 @@ } }, "node_modules/is-core-module": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.14.0.tgz", - "integrity": "sha512-a5dFJih5ZLYlRtDc0dZWP7RiKr6xIKzmn/oAYCDvdLThadVgyJwlaoQPmRtMSpz+rk0OGAgIu+TcM9HUF0fk1A==", + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.0.tgz", + "integrity": "sha512-Dd+Lb2/zvk9SKy1TGCt1wFJFo/MWBPMX5x7KcvLajWTGuomczdQX61PvY5yK6SVACwpoexWo81IfFyoKY2QnTA==", "dependencies": { "hasown": "^2.0.2" }, @@ -9023,6 +9066,18 @@ "node": ">=0.10.0" } }, + "node_modules/is-retry-allowed": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-2.2.0.tgz", + "integrity": "sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-set": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", @@ -9332,16 +9387,13 @@ } }, "node_modules/jackspeak": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.2.tgz", - "integrity": "sha512-qH3nOSj8q/8+Eg8LUPOq3C+6HWkpUioIjDsq1+D4zY91oZvpPttw8GwtF1nReRYKXl+1AORyFqtm2f5Q1SB6/Q==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", "dev": true, "dependencies": { "@isaacs/cliui": "^8.0.2" }, - "engines": { - "node": "14 >=14.21 || 16 >=16.20 || >=18" - }, "funding": { "url": "https://github.com/sponsors/isaacs" }, @@ -9350,9 +9402,9 @@ } }, "node_modules/jake": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.1.tgz", - "integrity": "sha512-61btcOHNnLnsOdtLgA5efqQWjnSi/vow5HbI7HMdKKWqvrKR1bLK3BPlJn9gcSaP2ewuamUSMB5XEy76KUIS2w==", + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", + "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", "dependencies": { "async": "^3.2.3", "chalk": "^4.0.2", @@ -10573,9 +10625,9 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" }, "node_modules/logform": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.6.0.tgz", - "integrity": "sha512-1ulHeNPp6k/LD8H91o7VYFBng5i1BDE7HoKxVbZiGFidS1Rj65qcywLxX+pVfAPoQJEjRdvKcusKwOupHCVOVQ==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.6.1.tgz", + "integrity": "sha512-CdaO738xRapbKIMVn2m4F6KTj4j7ooJ8POVnebSgKo3KBz5axNXRAL7ZdRjIV6NOr2Uf4vjtRkxrFETOioCqSA==", "dependencies": { "@colors/colors": "1.6.0", "@types/triple-beam": "^1.3.2", @@ -11031,9 +11083,9 @@ } }, "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "version": "1.53.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.53.0.tgz", + "integrity": "sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg==", "engines": { "node": ">= 0.6" } @@ -11049,6 +11101,14 @@ "node": ">= 0.6" } }, + "node_modules/mime-types/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", @@ -11770,9 +11830,9 @@ "integrity": "sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==" }, "node_modules/node-releases": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", - "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", "dev": true }, "node_modules/nodemon": { @@ -12765,23 +12825,6 @@ "node": ">= 0.4" } }, - "node_modules/object.hasown": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.4.tgz", - "integrity": "sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg==", - "dev": true, - "dependencies": { - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/object.values": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz", @@ -15190,9 +15233,9 @@ } }, "node_modules/semver": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", - "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "bin": { "semver": "bin/semver.js" }, @@ -16361,6 +16404,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/string.prototype.repeat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", + "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, "node_modules/string.prototype.trim": { "version": "1.2.9", "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", @@ -17860,15 +17913,15 @@ } }, "node_modules/winston": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.13.0.tgz", - "integrity": "sha512-rwidmA1w3SE4j0E5MuIufFhyJPBDG7Nu71RkZor1p2+qHvJSZ9GYDA81AyleQcZbh/+V6HjeBdfnTZJm9rSeQQ==", + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.13.1.tgz", + "integrity": "sha512-SvZit7VFNvXRzbqGHsv5KSmgbEYR5EiQfDAL9gxYkRqa934Hnk++zze0wANKtMHcy/gI4W/3xmSDwlhf865WGw==", "dependencies": { "@colors/colors": "^1.6.0", "@dabh/diagnostics": "^2.0.2", "async": "^3.2.3", "is-stream": "^2.0.0", - "logform": "^2.4.0", + "logform": "^2.6.0", "one-time": "^1.0.0", "readable-stream": "^3.4.0", "safe-stable-stringify": "^2.3.1", @@ -17881,12 +17934,12 @@ } }, "node_modules/winston-transport": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.7.0.tgz", - "integrity": "sha512-ajBj65K5I7denzer2IYW6+2bNIVqLGDHqDw3Ow8Ohh+vdW+rv4MZ6eiDvHoKhfJFZ2auyN8byXieDDJ96ViONg==", + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.7.1.tgz", + "integrity": "sha512-wQCXXVgfv/wUPOfb2x0ruxzwkcZfxcktz6JIMUaPLmcNhO4bZTwA/WtDWK74xV3F2dKu8YadrFv0qhwYjVEwhA==", "dependencies": { - "logform": "^2.3.2", - "readable-stream": "^3.6.0", + "logform": "^2.6.1", + "readable-stream": "^3.6.2", "triple-beam": "^1.3.0" }, "engines": { diff --git a/package.json b/package.json index 08dd4e13..33c15d86 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.4.0-snapshot.18", + "version": "15.4.0-snapshot.25", "license": "Apache-2.0", "author": "ModusBox", "contributors": [ @@ -37,7 +37,7 @@ "test" ], "scripts": { - "start": "node src/index.js server", + "start": "LOG_LEVEL=debug node src/index.js server", "start:all": "run-p start:api start:admin", "start:api": "node src/index.js server --api", "start:admin": "node src/index.js server --admin", @@ -90,14 +90,14 @@ "@hapi/vision": "7.0.3", "@mojaloop/central-services-error-handling": "13.0.1", "@mojaloop/central-services-health": "15.0.0", - "@mojaloop/central-services-logger": "11.3.1", + "@mojaloop/central-services-logger": "11.5.0", "@mojaloop/central-services-metrics": "12.0.8", - "@mojaloop/central-services-shared": "^18.6.0-snapshot.8", + "@mojaloop/central-services-shared": "18.6.0-snapshot.9", "@mojaloop/central-services-stream": "11.3.1", "@mojaloop/database-lib": "11.0.6", "@mojaloop/event-sdk": "14.1.1", - "@mojaloop/inter-scheme-proxy-cache-lib": "^1.4.0", - "@mojaloop/sdk-standard-components": "^18.4.0-snapshot.0", + "@mojaloop/inter-scheme-proxy-cache-lib": "2.2.0", + "@mojaloop/sdk-standard-components": "18.4.0-snapshot.0", "@now-ims/hapi-now-auth": "2.1.0", "ajv": "8.17.1", "ajv-keywords": "5.1.0", @@ -117,6 +117,15 @@ "@mojaloop/central-services-error-handling": { "@mojaloop/sdk-standard-components": "$@mojaloop/sdk-standard-components" }, + "@mojaloop/central-services-health": { + "@mojaloop/central-services-logger": ">=11.4.0" + }, + "@mojaloop/central-services-shared": { + "@mojaloop/central-services-logger": ">=11.4.0" + }, + "@mojaloop/central-services-stream": { + "@mojaloop/central-services-logger": ">=11.4.0" + }, "shins": { "ajv": "6.12.3", "ejs": "3.1.10", @@ -139,6 +148,7 @@ "@types/jest": "29.5.12", "audit-ci": "^7.1.0", "axios": "1.7.2", + "axios-retry": "^4.4.2", "docdash": "2.0.2", "dotenv": "^16.4.5", "get-port": "5.1.1", diff --git a/src/domain/parties/getPartiesByTypeAndID.js b/src/domain/parties/getPartiesByTypeAndID.js index 7c3c79f9..ea540ae0 100644 --- a/src/domain/parties/getPartiesByTypeAndID.js +++ b/src/domain/parties/getPartiesByTypeAndID.js @@ -42,6 +42,29 @@ const { Headers, RestMethods } = Enum.Http const proxyCacheTtlSec = 40 // todo: make configurable +const validateRequester = async ({ source, proxy, proxyCache }) => { + const sourceParticipant = await participant.validateParticipant(source) + if (sourceParticipant) return source + + if (!proxy) { + const errMessage = ERROR_MESSAGES.partySourceFspNotFound + Logger.isErrorEnabled && Logger.error(errMessage) + throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, errMessage) + } + + const proxyParticipant = await participant.validateParticipant(proxy) + if (!proxyParticipant) { + const errMessage = ERROR_MESSAGES.partyProxyNotFound + Logger.isErrorEnabled && Logger.error(errMessage) + throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, errMessage) + } + + const isCached = await proxyCache.addDfspIdToProxyMapping(source, proxy) + // think, what if isCached !== true? + Logger.isInfoEnabled && Logger.info(`source is added to proxyMapping cache: ${stringify({ source, proxy, isCached })}`) + return proxy +} + /** * @function getPartiesByTypeAndID * @@ -61,27 +84,21 @@ const getPartiesByTypeAndID = async (headers, params, method, query, span, cache 'Get party by Type and Id', ['success'] ).startTimer() + const proxyEnabled = !!(Config.proxyCacheConfig.enabled && proxyCache) const type = params.Type const partySubId = params.SubId const source = headers[Headers.FSPIOP.SOURCE] + const proxy = proxyEnabled && headers[Headers.FSPIOP.PROXY] const callbackEndpointType = utils.getPartyCbType(partySubId) - const proxyEnabled = !!(Config.proxyCacheConfig.enabled && proxyCache) const childSpan = span ? span.getChild('getPartiesByTypeAndID') : undefined Logger.isInfoEnabled && Logger.info('parties::getPartiesByTypeAndID::begin') + let requester let fspiopError + try { - const proxy = proxyEnabled && headers[Headers.FSPIOP.PROXY] - const requesterId = proxy || source - - const requesterParticipantModel = await participant.validateParticipant(requesterId) - if (!requesterParticipantModel) { - // assuming adjacent scheme participants are not participants of the scheme - const errMessage = proxy ? ERROR_MESSAGES.partyProxyNotFound : ERROR_MESSAGES.partySourceFspNotFound - Logger.isErrorEnabled && Logger.error(errMessage) - throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, errMessage) - } + requester = await validateRequester({ source, proxy, proxyCache }) const options = { partyIdType: type, @@ -129,8 +146,9 @@ const getPartiesByTypeAndID = async (headers, params, method, query, span, cache } if (!Array.isArray(filteredResponsePartyList) || !filteredResponsePartyList.length) { - Logger.isErrorEnabled && Logger.error('Requested FSP/Party not found') - throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, 'Requested FSP/Party not found') + const errMessage = 'Requested FSP/Party not found' + Logger.isErrorEnabled && Logger.error(errMessage) + throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, errMessage) } let atLeastOneSent = false // if false after sending, we should restart the whole process @@ -181,7 +199,7 @@ const getPartiesByTypeAndID = async (headers, params, method, query, span, cache }) fspiopError = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.PARTY_NOT_FOUND) const errorCallbackEndpointType = utils.errorPartyCbType(partySubId) - await participant.sendErrorToParticipant(source, errorCallbackEndpointType, + await participant.sendErrorToParticipant(requester, errorCallbackEndpointType, fspiopError.toApiErrorObject(config.ERROR_HANDLING), callbackHeaders, params, childSpan) } else { const alsReq = utils.alsRequestDto(source, params) @@ -208,7 +226,7 @@ const getPartiesByTypeAndID = async (headers, params, method, query, span, cache } histTimerEnd({ success: true }) } catch (err) { - fspiopError = await utils.handleErrorOnSendingCallback(err, headers, params) + fspiopError = await utils.handleErrorOnSendingCallback(err, headers, params, requester) histTimerEnd({ success: false }) } finally { await utils.finishSpanWithError(childSpan, fspiopError) diff --git a/src/domain/parties/parties.js b/src/domain/parties/parties.js index 376fc561..6d9021ad 100644 --- a/src/domain/parties/parties.js +++ b/src/domain/parties/parties.js @@ -69,9 +69,9 @@ const putPartiesByTypeAndID = async (headers, params, method, payload, dataUri, const source = headers[Headers.FSPIOP.SOURCE] const destination = headers[Headers.FSPIOP.DESTINATION] const proxyEnabled = !!(Config.proxyCacheConfig.enabled && proxyCache) - Logger.isInfoEnabled && Logger.info(`parties::putPartiesByTypeAndID::begin - ${stringify({ source, destination, params })}`) + let sendTo try { const requesterParticipant = await participant.validateParticipant(source) if (!requesterParticipant) { @@ -82,19 +82,19 @@ const putPartiesByTypeAndID = async (headers, params, method, payload, dataUri, throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, errMessage) } else { const isCached = await proxyCache.addDfspIdToProxyMapping(source, proxy) - // todo: think, if we should throw error if isCached === false? + // think,if we should throw error if isCached === false? Logger.isDebugEnabled && Logger.debug(`addDfspIdToProxyMapping is done: ${stringify({ source, proxy, isCached })}`) } } + // todo: recheck the logic (only if ) if (proxyEnabled) { const alsReq = utils.alsRequestDto(destination, params) // or source? const isExists = await proxyCache.receivedSuccessResponse(alsReq) if (!isExists) { Logger.isWarnEnabled && Logger.warn(`destination is NOT in scheme, and no cached sendToProxiesList - ${stringify({ destination, alsReq })}`) - // todo: think, if we need to throw an error here + // think, if we need to throw an error here } else { - // todo: add unit-tests const mappingPayload = { fspId: source } @@ -109,18 +109,16 @@ const putPartiesByTypeAndID = async (headers, params, method, payload, dataUri, } const destinationParticipant = await participant.validateParticipant(destination) - let sentTo if (!destinationParticipant) { - // todo: add unit-tests const proxyName = proxyEnabled && await proxyCache.lookupProxyByDfspId(destination) if (!proxyName) { const errMessage = ERROR_MESSAGES.partyDestinationFspNotFound Logger.isErrorEnabled && Logger.error(errMessage) throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.DESTINATION_FSP_ERROR, errMessage) } - sentTo = proxyName + sendTo = proxyName } else { - sentTo = destinationParticipant.name + sendTo = destinationParticipant.name } const decodedPayload = decodePayload(dataUri, { asParsed: false }) @@ -130,12 +128,12 @@ const putPartiesByTypeAndID = async (headers, params, method, payload, dataUri, partyIdentifier: params.ID, ...(partySubId && { partySubIdOrType: partySubId }) } - await participant.sendRequest(headers, sentTo, callbackEndpointType, RestMethods.PUT, decodedPayload.body.toString(), options) + await participant.sendRequest(headers, sendTo, callbackEndpointType, RestMethods.PUT, decodedPayload.body.toString(), options) - Logger.isInfoEnabled && Logger.info(`parties::putPartiesByTypeAndID::callback was sent - ${stringify({ sentTo, options })}`) + Logger.isInfoEnabled && Logger.info(`parties::putPartiesByTypeAndID::callback was sent - ${stringify({ sentTo: sendTo, options })}`) histTimerEnd({ success: true }) } catch (err) { - await utils.handleErrorOnSendingCallback(err, headers, params) + await utils.handleErrorOnSendingCallback(err, headers, params, sendTo) histTimerEnd({ success: false }) } } @@ -166,7 +164,9 @@ const putPartiesErrorByTypeAndID = async (headers, params, payload, dataUri, spa const childSpan = span ? span.getChild('putPartiesErrorByTypeAndID') : undefined + let sendTo let fspiopError + try { const proxy = proxyEnabled && headers[Headers.FSPIOP.PROXY] if (proxy) { @@ -188,11 +188,10 @@ const putPartiesErrorByTypeAndID = async (headers, params, payload, dataUri, spa } } - let sentTo const destinationParticipant = await participant.validateParticipant(destination) if (destinationParticipant) { - sentTo = destination + sendTo = destination } else { const proxyName = proxyEnabled && await proxyCache.lookupProxyByDfspId(destination) if (!proxyName) { @@ -200,15 +199,15 @@ const putPartiesErrorByTypeAndID = async (headers, params, payload, dataUri, spa Logger.isErrorEnabled && Logger.error(errMessage) throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.DESTINATION_FSP_ERROR, errMessage) } - sentTo = proxyName + sendTo = proxyName } const decodedPayload = decodePayload(dataUri, { asParsed: false }) - await participant.sendErrorToParticipant(sentTo, callbackEndpointType, decodedPayload.body.toString(), headers, params, childSpan) + await participant.sendErrorToParticipant(sendTo, callbackEndpointType, decodedPayload.body.toString(), headers, params, childSpan) - Logger.isInfoEnabled && Logger.info(`putPartiesErrorByTypeAndID callback was sent to ${sentTo}`) + Logger.isInfoEnabled && Logger.info(`putPartiesErrorByTypeAndID callback was sent to ${sendTo}`) histTimerEnd({ success: true }) } catch (err) { - fspiopError = await utils.handleErrorOnSendingCallback(err, headers, params) + fspiopError = await utils.handleErrorOnSendingCallback(err, headers, params, sendTo) histTimerEnd({ success: false }) } finally { await utils.finishSpanWithError(childSpan, fspiopError) diff --git a/src/domain/parties/utils.js b/src/domain/parties/utils.js index 506edbde..da5f1d20 100644 --- a/src/domain/parties/utils.js +++ b/src/domain/parties/utils.js @@ -1,11 +1,10 @@ const { Enum } = require('@mojaloop/central-services-shared') const EventSdk = require('@mojaloop/event-sdk') const ErrorHandler = require('@mojaloop/central-services-error-handling') -const Logger = require('@mojaloop/central-services-logger') -const stringify = require('fast-safe-stringify') const participant = require('../../models/participantEndpoint/facade') const Config = require('../../lib/config') +const { logger } = require('../../lib') const { FspEndpointTypes } = Enum.EndPoints const { Headers } = Enum.Http @@ -54,22 +53,23 @@ const swapSourceDestinationHeaders = (headers) => { } } -const handleErrorOnSendingCallback = async (err, headers, params) => { +// change signature to accept object +const handleErrorOnSendingCallback = async (err, headers, params, requester) => { try { - Logger.isErrorEnabled && Logger.error(err) - const source = headers[Headers.FSPIOP.SOURCE] + logger.error('error in sending parties callback', err) + const sendTo = requester || headers[Headers.FSPIOP.SOURCE] const errorCallbackEndpointType = errorPartyCbType(params.SubId) const fspiopError = ErrorHandler.Factory.reformatFSPIOPError(err) const errInfo = fspiopError.toApiErrorObject(Config.ERROR_HANDLING) - await participant.sendErrorToParticipant(source, errorCallbackEndpointType, errInfo, headers, params) + await participant.sendErrorToParticipant(sendTo, errorCallbackEndpointType, errInfo, headers, params) - Logger.isInfoEnabled && Logger.info(`handleErrorOnSendingCallback in done: ${stringify({ source, params, errInfo })}`) + logger.info('handleErrorOnSendingCallback in done', { sendTo, params, errInfo }) return fspiopError } catch (exc) { // We can't do anything else here- we _must_ handle all errors _within_ this function because // we've already sent a sync response- we cannot throw. - Logger.isErrorEnabled && Logger.error(exc) + logger.error('error in handleErrorOnSendingCallback', exc) } } diff --git a/src/lib/config.js b/src/lib/config.js index b9977922..40fd867f 100644 --- a/src/lib/config.js +++ b/src/lib/config.js @@ -99,7 +99,7 @@ const getProtocolVersions = (defaultProtocolVersions, overrideProtocolVersions) return T_PROTOCOL_VERSION } -if (!storageTypeValues.includes(RC.PROXY_CACHE.type)) { +if (RC.PROXY_CACHE?.enabled && !storageTypeValues.includes(RC.PROXY_CACHE.type)) { throw new TypeError(`Incorrect proxyCache type: ${RC.PROXY_CACHE.type}`) } diff --git a/src/lib/index.js b/src/lib/index.js new file mode 100644 index 00000000..a9d7e5b3 --- /dev/null +++ b/src/lib/index.js @@ -0,0 +1,9 @@ +const { loggerFactory, asyncStorage } = require('@mojaloop/central-services-logger/src/contextLogger') + +const logger = loggerFactory('ALS') // global logger without context + +module.exports = { + logger, + loggerFactory, + asyncStorage +} diff --git a/src/lib/requestLogger.js b/src/lib/requestLogger.js index ddc7d33e..f3b1dac5 100644 --- a/src/lib/requestLogger.js +++ b/src/lib/requestLogger.js @@ -24,32 +24,27 @@ 'use strict' -const Logger = require('@mojaloop/central-services-logger') -const Util = require('util') +const { logger, asyncStorage } = require('./index') const logRequest = function (request) { - const traceId = request.headers.traceid - Logger.isDebugEnabled && Logger.debug(`ALS-Trace=${traceId} - Method: ${request.method} Path: ${request.path} Query: ${JSON.stringify(request.query)}`) - Logger.isDebugEnabled && Logger.debug(`ALS-Trace=${traceId} - Headers: ${JSON.stringify(request.headers, null, 2)}`) - if (request.payload) { - Logger.isDebugEnabled && Logger.debug(`ALS-Trace=${traceId} - Body: ${JSON.stringify(request.payload, null, 2)}`) - } + const { path, method, headers, payload, query } = request + const requestId = request.info.id = `${request.info.id}__${headers.traceid}` + asyncStorage.enterWith({ requestId }) + + logger.isInfoEnabled && logger.info(`[==> req] ${method.toUpperCase()} ${path}`, { headers, payload, query }) } const logResponse = function (request) { - if (Logger.isDebugEnabled && request.response) { - const traceId = request.headers.traceid - let response - try { - response = JSON.stringify(request.response, null, 2) - } catch (e) { - response = Util.inspect(request.response) - } - if (!response) { - Logger.isDebugEnabled && Logger.debug(`ALS-Trace=${traceId} - Response: ${request.response}`) - } else { - Logger.isDebugEnabled && Logger.debug(`ALS-Trace=${traceId} - Status: ${request.response.statusCode || request.response.httpStatusCode}, Stack: ${request.response.stack}`) - } + if (logger.isInfoEnabled) { + const { path, method, headers, payload, query, response } = request + const { received } = request.info + + const statusCode = response instanceof Error + ? response.output?.statusCode + : response.statusCode + const respTimeSec = ((Date.now() - received) / 1000).toFixed(3) + + logger.info(`[<== ${statusCode}][${respTimeSec} s] ${method.toUpperCase()} ${path}`, { headers, payload, query }) } } diff --git a/src/models/participantEndpoint/facade.js b/src/models/participantEndpoint/facade.js index 41a338c8..e84806a3 100644 --- a/src/models/participantEndpoint/facade.js +++ b/src/models/participantEndpoint/facade.js @@ -33,7 +33,7 @@ const ErrorHandler = require('@mojaloop/central-services-error-handling') const JwsSigner = require('@mojaloop/sdk-standard-components').Jws.signer const Metrics = require('@mojaloop/central-services-metrics') const Config = require('../../lib/config') -const hubNameRegex = require('../../lib/util').hubNameConfig.hubNameRegex +const { hubNameRegex } = require('../../lib/util').hubNameConfig const uriRegex = /(?:^.*)(\/(participants|parties|quotes|transfers)(\/.*)*)$/ /** @@ -86,7 +86,7 @@ exports.sendRequest = async (headers, requestedParticipant, endpointType, method Logger.isDebugEnabled && Logger.debug(`participant endpoint url: ${requestedEndpoint} for endpoint type ${endpointType}`) } catch (err) { histTimerEndGetParticipantEndpoint({ success: false, endpointType, participantName: requestedParticipant }) - Logger.isErrorEnabled && Logger.error(err) + Logger.isErrorEnabled && Logger.error(`error in getEndpoint: ${err?.stack}`) throw ErrorHandler.Factory.reformatFSPIOPError(err) } @@ -121,7 +121,8 @@ exports.sendRequest = async (headers, requestedParticipant, endpointType, method return resp } catch (err) { histTimerEndSendRequestToParticipant({ success: false, endpointType, participantName: requestedParticipant }) - Logger.isErrorEnabled && Logger.error(err) + Logger.isErrorEnabled && Logger.error(`error in sendRequest: ${err?.stack}`) + throw ErrorHandler.Factory.reformatFSPIOPError(err) } } @@ -146,7 +147,7 @@ exports.validateParticipant = async (fsp) => { return resp } catch (err) { histTimerEnd({ success: false }) - Logger.isErrorEnabled && Logger.error(err) + Logger.isErrorEnabled && Logger.error(`error in validateParticipant: ${err?.stack}`) throw ErrorHandler.Factory.reformatFSPIOPError(err) } } @@ -175,20 +176,18 @@ exports.sendErrorToParticipant = async (participantName, endpointType, errorInfo ['success', 'endpointType', 'participantName'] ).startTimer() try { - let requestIdExists = false - if (payload && payload.requestId) { - requestIdExists = true - } + const { requestId } = payload || {} + requesterErrorEndpoint = await Util.Endpoints.getEndpoint(Config.SWITCH_ENDPOINT, participantName, endpointType, { partyIdType: params.Type || undefined, partyIdentifier: params.ID || undefined, partySubIdOrType: params.SubId || undefined, - requestId: requestIdExists ? payload.requestId : undefined + requestId }) histTimerEndGetParticipantEndpoint({ success: true, endpointType, participantName }) } catch (err) { histTimerEndGetParticipantEndpoint({ success: false, endpointType, participantName }) - Logger.isErrorEnabled && Logger.error(err) + Logger.isWarnEnabled && Logger.warn(`error in getEndpoint: ${err?.message}`) throw ErrorHandler.Factory.reformatFSPIOPError(err) } @@ -215,12 +214,23 @@ exports.sendErrorToParticipant = async (participantName, endpointType, errorInfo Logger.isDebugEnabled && Logger.debug(`participant endpoint url: ${requesterErrorEndpoint} for endpoint type ${endpointType}`) const jwsSigner = defineJwsSigner(Config, clonedHeaders, requesterErrorEndpoint) - await Util.Request.sendRequest(requesterErrorEndpoint, clonedHeaders, clonedHeaders[Enums.Http.Headers.FSPIOP.SOURCE], - clonedHeaders[Enums.Http.Headers.FSPIOP.DESTINATION], Enums.Http.RestMethods.PUT, errorInformation, Enums.Http.ResponseTypes.JSON, span, jwsSigner, protocolVersions) + await Util.Request.sendRequest({ + url: requesterErrorEndpoint, + headers: clonedHeaders, + source: clonedHeaders[Enums.Http.Headers.FSPIOP.SOURCE], + destination: clonedHeaders[Enums.Http.Headers.FSPIOP.DESTINATION], + method: Enums.Http.RestMethods.PUT, + payload: errorInformation, + responseType: Enums.Http.ResponseTypes.JSON, + hubNameRegex, + span, + jwsSigner, + protocolVersions + }) histTimerEndSendRequestToParticipant({ success: true, endpointType, participantName }) } catch (err) { histTimerEndSendRequestToParticipant({ success: false, endpointType, participantName }) - Logger.isErrorEnabled && Logger.error(err) + Logger.isWarnEnabled && Logger.warn(`error in sendErrorToParticipant: ${err?.message}`) throw ErrorHandler.Factory.reformatFSPIOPError(err) } } diff --git a/src/server.js b/src/server.js index 36e48943..2bbd5c5c 100644 --- a/src/server.js +++ b/src/server.js @@ -26,13 +26,11 @@ const { randomUUID } = require('node:crypto') const Hapi = require('@hapi/hapi') const Boom = require('@hapi/boom') -const ParticipantEndpointCache = require('@mojaloop/central-services-shared').Util.Endpoints -const ParticipantCache = require('@mojaloop/central-services-shared').Util.Participants -const proxies = require('@mojaloop/central-services-shared').Util.proxies -const OpenapiBackend = require('@mojaloop/central-services-shared').Util.OpenapiBackend + const ErrorHandler = require('@mojaloop/central-services-error-handling') const Logger = require('@mojaloop/central-services-logger') const Metrics = require('@mojaloop/central-services-metrics') +const { Endpoints, Participants, proxies, OpenapiBackend } = require('@mojaloop/central-services-shared').Util const { createProxyCache } = require('@mojaloop/inter-scheme-proxy-cache-lib') const { name, version } = require('../package.json') @@ -89,6 +87,8 @@ const createServer = async (port, api, routes, isAdmin, proxyCacheConfig) => { } } }) + server.app.isAdmin = isAdmin + server.app.cache = Cache.registerCacheClient({ id: 'serverGeneralCache', preloadCache: async () => Promise.resolve() @@ -98,12 +98,9 @@ const createServer = async (port, api, routes, isAdmin, proxyCacheConfig) => { server.app.proxyCache = await createConnectedProxyCache(proxyCacheConfig) } - server.app.isAdmin = isAdmin - - await Plugins.registerPlugins(server, api, isAdmin) await server.ext([ { - type: 'onPostAuth', + type: 'onPreHandler', method: (request, h) => { request.headers.traceid = request.headers.traceid || randomUUID() RequestLogger.logRequest(request) @@ -118,10 +115,13 @@ const createServer = async (port, api, routes, isAdmin, proxyCacheConfig) => { } } ]) + await Plugins.registerPlugins(server, api, isAdmin) server.route(routes) // TODO: follow instructions https://github.com/anttiviljami/openapi-backend/blob/master/DOCS.md#postresponsehandler-handler await server.start() + + Logger.isInfoEnabled && Logger.info(`${name}${isAdmin ? '-admin' : ''}@${version} is running on port ${server.info.port}...`) return server } @@ -146,14 +146,16 @@ const initializeApi = async (appConfig) => { await connectDatabase(DATABASE) const OpenAPISpecPath = Util.pathForInterface({ isAdmin: false, isMockInterface: false }) const api = await OpenapiBackend.initialise(OpenAPISpecPath, Handlers.ApiHandlers) - const server = await createServer(API_PORT, api, Routes.APIRoutes(api), false, proxyCacheConfig) - Logger.isInfoEnabled && Logger.info(`${name}@${version} is running on ${server.info.host}:${server.info.port}`) - await ParticipantEndpointCache.initializeCache(CENTRAL_SHARED_ENDPOINT_CACHE_CONFIG, Util.hubNameConfig) - await ParticipantCache.initializeCache(CENTRAL_SHARED_PARTICIPANT_CACHE_CONFIG, Util.hubNameConfig) - await proxies.initializeCache(CENTRAL_SHARED_PARTICIPANT_CACHE_CONFIG, Util.hubNameConfig) - await OracleEndpointCache.initialize() - await Cache.initCache() - return server + + await Promise.all([ + Endpoints.initializeCache(CENTRAL_SHARED_ENDPOINT_CACHE_CONFIG, Util.hubNameConfig), + Participants.initializeCache(CENTRAL_SHARED_PARTICIPANT_CACHE_CONFIG, Util.hubNameConfig), + proxies.initializeCache(CENTRAL_SHARED_PARTICIPANT_CACHE_CONFIG, Util.hubNameConfig), + OracleEndpointCache.initialize(), + Cache.initCache() + ]) + + return createServer(API_PORT, api, Routes.APIRoutes(api), false, proxyCacheConfig) } const initializeAdmin = async (appConfig) => { @@ -173,9 +175,8 @@ const initializeAdmin = async (appConfig) => { RUN_MIGRATIONS && await migrate() const OpenAPISpecPath = Util.pathForInterface({ isAdmin: true, isMockInterface: false }) const api = await OpenapiBackend.initialise(OpenAPISpecPath, Handlers.AdminHandlers) - const server = await createServer(ADMIN_PORT, api, Routes.AdminRoutes(api), true, proxyCacheConfig) - Logger.isInfoEnabled && Logger.info(`${name}-admin@${version} running on ${server.info.host}:${server.info.port}`) - return server + + return createServer(ADMIN_PORT, api, Routes.AdminRoutes(api), true, proxyCacheConfig) } module.exports = { diff --git a/test/fixtures.js b/test/fixtures.js index 5dabe99b..8522be4f 100644 --- a/test/fixtures.js +++ b/test/fixtures.js @@ -1,3 +1,4 @@ +const { randomUUID } = require('node:crypto') const { Enum } = require('@mojaloop/central-services-shared') const { Headers } = Enum.Http @@ -17,6 +18,17 @@ const headersDto = ({ 'content-type': accept }) +const protocolVersionsDto = () => ({ + CONTENT: { + DEFAULT: '2.1', + VALIDATELIST: ['2.1'] + }, + ACCEPT: { + DEFAULT: '2', + VALIDATELIST: ['2', '2.1'] + } +}) + const partiesCallHeadersDto = ({ source, destination, @@ -73,10 +85,25 @@ const mockAlsRequestDto = (sourceId, type, partyId) => ({ partyId }) +const mockHapiRequestDto = ({ // https://hapi.dev/api/?v=21.3.3#request-properties + method = 'GET', + traceid = randomUUID(), + id = randomUUID() +} = {}) => ({ + method, + headers: { traceid }, + info: { + id, + received: 123456789 + } +}) + module.exports = { partiesCallHeadersDto, participantsCallHeadersDto, oracleRequestResponseDto, errorCallbackResponseDto, - mockAlsRequestDto + mockAlsRequestDto, + protocolVersionsDto, + mockHapiRequestDto } diff --git a/test/integration-config.json b/test/integration-config.json index 8187c67c..f5b41c3a 100644 --- a/test/integration-config.json +++ b/test/integration-config.json @@ -48,10 +48,11 @@ }, "PROXY_CACHE": { "enabled": true, - "type": "redis", + "type": "redis-cluster", "proxyConfig": { - "host": "redis", - "port": 6379 + "cluster": [ + { "host": "redis-node-0", "port": 6379 } + ] } }, "SWITCH_ENDPOINT": "http://localhost:3001", diff --git a/test/integration/handlers/parties.test.js b/test/integration/handlers/parties.test.js index 224d83dd..cef5fff8 100644 --- a/test/integration/handlers/parties.test.js +++ b/test/integration/handlers/parties.test.js @@ -74,6 +74,21 @@ describe('Parties Endpoints Tests -->', () => { isExists = await proxyCache.receivedSuccessResponse(alsReq) expect(isExists).toBe(true) }) + + test('should handle PUT /parties/{Type}/{ID}/error without accept-header', async () => { + const partyId = `PT-${Date.now()}` + const url = `${alsUrl}/parties/${PARTY_ID_TYPE}/${partyId}/error` + const body = fixtures.errorCallbackResponseDto() + + const { accept, ...headers } = fixtures.partiesCallHeadersDto({ proxy: 'proxyAB' }) + expect(headers.accept).toBeUndefined() + + const result = await axios.put(url, body, { headers }) + .catch(err => { + throw err + }) + expect(result.status).toBe(200) + }) // todo: add test of sending PUT /parties callback }) }) diff --git a/test/integration/prepareTestParticipants.js b/test/integration/prepareTestParticipants.js index eb41a78a..effef3e7 100644 --- a/test/integration/prepareTestParticipants.js +++ b/test/integration/prepareTestParticipants.js @@ -5,12 +5,12 @@ const { onboarding } = require('../util') const { PROXY_NAME, PAYER_DFSP } = require('../integration/constants') const pause = async (ms = 1000) => new Promise(resolve => { - Logger.info(`pause for ${ms/1000} sec....`) + Logger.info(`pause for ${ms / 1000} sec....`) setTimeout(resolve, ms) }) const prepareTestParticipants = async () => { - await pause(5000) + await pause(10_000) // sometimes on CircleCI env we have error: socket hang up await onboarding.createHubAccounts() await pause() diff --git a/test/unit/domain/parties/parties.test.js b/test/unit/domain/parties/parties.test.js index 4eb88138..d2519222 100644 --- a/test/unit/domain/parties/parties.test.js +++ b/test/unit/domain/parties/parties.test.js @@ -44,12 +44,14 @@ const participantsDomain = require('../../../../src/domain/participants') const participant = require('../../../../src/models/participantEndpoint/facade') const oracle = require('../../../../src/models/oracle/facade') const libUtil = require('../../../../src/lib/util') +const { logger } = require('../../../../src/lib') const { ERROR_MESSAGES } = require('../../../../src/constants') -const { type: proxyCacheType, proxyConfig: proxyCacheConfig } = Config.proxyCacheConfig const Helper = require('../../../util/helper') const fixtures = require('../../../fixtures') +const { type: proxyCacheType, proxyConfig: proxyCacheConfig } = Config.proxyCacheConfig + const { encodePayload } = Util.StreamingProtocol Logger.isDebugEnabled = jest.fn(() => true) @@ -159,6 +161,27 @@ describe('Parties Tests', () => { expect(lastCallHeaderArgs[1]).toBe('destfsp') }) + it('should set source proxyMapping if source is not in scheme, and there is proxy-header', async () => { + Config.proxyCacheConfig.enabled = true + participant.validateParticipant = sandbox.stub() + .onFirstCall().resolves(null) // source + .onSecondCall().resolves({}) // proxy + const source = `source-${Date.now()}` + const proxy = `proxy-${Date.now()}` + + let cached = await proxyCache.lookupProxyByDfspId(source) + expect(cached).toBe(null) + + const headers = fixtures.partiesCallHeadersDto({ source, proxy }) + const { params, method, query } = Helper.getByTypeIdRequest + + await partiesDomain.getPartiesByTypeAndID(headers, params, method, query, Helper.mockSpan(), null, proxyCache) + await new Promise(resolve => setTimeout(resolve, 1000)) + + cached = await proxyCache.lookupProxyByDfspId(source) + expect(cached).toBe(proxy) + }) + it('should send error callback if destination is not in the scheme, and not in proxyCache', async () => { participant.validateParticipant = sandbox.stub() .onFirstCall().resolves({}) // source @@ -207,21 +230,18 @@ describe('Parties Tests', () => { // Arrange participant.validateParticipant = sandbox.stub().resolves(null) participant.sendErrorToParticipant = sandbox.stub().resolves(null) - const loggerStub = sandbox.stub(Logger, 'error') + const loggerStub = sandbox.stub(logger.mlLogger, 'error') // Act await partiesDomain.getPartiesByTypeAndID(Helper.getByTypeIdRequest.headers, Helper.getByTypeIdRequest.params, Helper.getByTypeIdRequest.method, Helper.getByTypeIdRequest.query) // Assert - /* - The function catches all exceptions. The only way to inspect what happens - in the error cases is by mocking out the logger and ensuring it gets - called correctly - */ - const firstCallArgs = loggerStub.getCall(0).args - const secondCallArgs = loggerStub.getCall(1).args - expect(firstCallArgs[0]).toBe(ERROR_MESSAGES.partySourceFspNotFound) - expect(secondCallArgs[0].name).toBe('FSPIOPError') + expect(loggerStub.callCount).toBe(1) + expect(participant.sendErrorToParticipant.callCount).toBe(1) + + const { errorInformation } = participant.sendErrorToParticipant.getCall(0).args[2] + expect(errorInformation.errorCode).toBe('3200') + expect(errorInformation.errorDescription).toContain(ERROR_MESSAGES.partySourceFspNotFound) }) it('should send error callback, if proxy-header is present, but no proxy in the scheme', async () => { @@ -255,7 +275,7 @@ describe('Parties Tests', () => { }) participant.sendRequest = sandbox.stub().throws(new Error('Error sending request')) participant.sendErrorToParticipant = sandbox.stub().throws(new Error('Error sending Error')) - const loggerStub = sandbox.stub(Logger, 'error') + const loggerStub = sandbox.stub(logger.mlLogger, 'error') const headers = { accept: 'application/vnd.interoperability.participants+json;version=1', @@ -270,15 +290,8 @@ describe('Parties Tests', () => { await partiesDomain.getPartiesByTypeAndID(headers, Helper.getByTypeIdRequest.params, Helper.getByTypeIdRequest.method, Helper.getByTypeIdRequest.query) // Assert - /* - The function catches all exceptions. The only way to inspect what happens - in the error cases is by mocking out the logger and ensuring it gets - called correctly - */ - const firstCallArgs = loggerStub.getCall(0).args - const secondCallArgs = loggerStub.getCall(1).args - expect(firstCallArgs[0].name).toBe('Error') - expect(secondCallArgs[0].name).toBe('Error') + expect(participant.sendRequest.callCount).toBe(1) + expect(participant.sendErrorToParticipant.callCount).toBe(1) expect(loggerStub.callCount).toBe(2) }) @@ -829,20 +842,21 @@ describe('Parties Tests', () => { const payload = JSON.stringify({ errorPayload: true }) // Send a data uri that will cause `decodePayload` to throw const invalidDataUri = () => 'invalid uri' + const { headers, params } = Helper.putByTypeIdRequest // Act - await partiesDomain.putPartiesErrorByTypeAndID(Helper.putByTypeIdRequest.headers, Helper.putByTypeIdRequest.params, payload, invalidDataUri, Helper.mockSpan()) + await partiesDomain.putPartiesErrorByTypeAndID(headers, params, payload, invalidDataUri, Helper.mockSpan(), null) // Assert expect(participant.sendErrorToParticipant.callCount).toBe(1) const sendErrorCallArgs = participant.sendErrorToParticipant.getCall(0).args - expect(sendErrorCallArgs[0]).toStrictEqual('payerfsp') + expect(sendErrorCallArgs[0]).toStrictEqual(headers['fspiop-destination']) }) it('handles error when `validateParticipant()` fails', async () => { expect.hasAssertions() // Arrange) - const loggerStub = sandbox.stub(Logger, 'error') + const loggerStub = sandbox.stub(logger.mlLogger, 'error') participant.validateParticipant = sandbox.stub().throws(new Error('Validation fails')) participant.sendErrorToParticipant = sandbox.stub().resolves({}) const payload = JSON.stringify({ errorPayload: true }) @@ -860,8 +874,9 @@ describe('Parties Tests', () => { it('handles error when SubID is supplied but `validateParticipant()` fails', async () => { expect.hasAssertions() - // Arrange) - const loggerStub = sandbox.stub(Logger, 'error') + // Arrange + + const loggerStub = sandbox.stub(logger.mlLogger, 'error') participant.validateParticipant = sandbox.stub().throws(new Error('Validation fails')) participant.sendErrorToParticipant = sandbox.stub().resolves({}) const payload = JSON.stringify({ errorPayload: true }) diff --git a/test/unit/lib/requestLogger.test.js b/test/unit/lib/requestLogger.test.js index e3157c0f..e1a57198 100644 --- a/test/unit/lib/requestLogger.test.js +++ b/test/unit/lib/requestLogger.test.js @@ -26,41 +26,34 @@ 'use strict' -const Uuid = require('node:crypto').randomUUID const Sinon = require('sinon') +const ErrorHandler = require('@mojaloop/central-services-error-handling') const requestLogger = require('../../../src/lib/requestLogger') -const Logger = require('@mojaloop/central-services-logger') -const ErrorHandler = require('@mojaloop/central-services-error-handling') +const { logger } = require('../../../src/lib') +const fixtures = require('../../fixtures') let sandbox -let currentLoggerIsDebugEnabled describe('requestLogger', () => { beforeEach(() => { sandbox = Sinon.createSandbox() - currentLoggerIsDebugEnabled = Logger.isDebugEnabled }) afterEach(() => { sandbox.restore() - Logger.isDebugEnabled = currentLoggerIsDebugEnabled }) describe('logRequest', () => { it('prints the request.payload if it exists', async () => { // Arrange - const debugSpy = sandbox.spy(Logger, 'debug') - Logger.isDebugEnabled = true + const infoSpy = sandbox.spy(logger.mlLogger, 'info') const req = { - method: 'GET', + ...fixtures.mockHapiRequestDto(), url: { path: '/123/456' }, query: {}, - headers: { - traceid: Uuid() - }, payload: { itemA: 123, itemB: 456 @@ -71,54 +64,21 @@ describe('requestLogger', () => { requestLogger.logRequest(req) // Assert - expect(debugSpy.calledThrice).toBe(true) + expect(infoSpy.calledOnce).toBe(true) + const logLine = infoSpy.firstCall.args[0] + expect(logLine).toContain(JSON.stringify(req.headers)) + expect(logLine).toContain(JSON.stringify(req.query)) + expect(logLine).toContain(JSON.stringify(req.payload)) }) }) describe('logResponse', () => { - it('handles deseralizing invalid JSON', async () => { - // Arrange - const infoSpy = sandbox.spy(Logger, 'debug') - Logger.isDebugEnabled = true - const req = { - headers: { - traceid: Uuid() - }, - response: { - source: { - itemA: true - }, - statusCode: 500 - } - } - /* Make some circular JSON to break JSON.stringify() */ - const inner = {} - const outer = { - inner - } - inner[outer] = outer - req.response.source = outer - - // Act - requestLogger.logResponse(req) - - // Assert - const result = infoSpy.calledWith(`ALS-Trace=${req.headers.traceid} - Status: ${req.response.statusCode}, Stack: ${req.response.stack}`) - expect(result).toBe(true) - }) - - it('handles valid json', async () => { + it('should log response statusCode', async () => { // Arrange - const infoSpy = sandbox.spy(Logger, 'debug') - Logger.isDebugEnabled = true + const infoSpy = sandbox.spy(logger.mlLogger, 'info') const req = { - headers: { - traceid: Uuid() - }, + ...fixtures.mockHapiRequestDto(), response: { - source: { - itemA: true - }, statusCode: 500 } } @@ -127,47 +87,29 @@ describe('requestLogger', () => { requestLogger.logResponse(req) // Assert - const result = infoSpy.calledWith(`ALS-Trace=${req.headers.traceid} - Status: ${req.response.statusCode}, Stack: ${req.response.stack}`) - expect(result).toBe(true) + const logLine = infoSpy.firstCall.args[0] + expect(logLine).toContain(JSON.stringify(req.response.statusCode)) }) it('handles valid json error response', async () => { // Arrange - const infoSpy = sandbox.spy(Logger, 'debug') - Logger.isDebugEnabled = true - const req = { - headers: { - traceid: Uuid() - }, - response: ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.VALIDATION_ERROR, 'Invalid currency code') + const infoSpy = sandbox.spy(logger.mlLogger, 'info') + const response = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.VALIDATION_ERROR, 'Invalid currency code') + const statusCode = 123 + response.output = { + statusCode } - - // Act - requestLogger.logResponse(req) - - // Assert - const result = infoSpy.calledWith(`ALS-Trace=${req.headers.traceid} - Status: ${req.response.httpStatusCode}, Stack: ${req.response.stack}`) - expect(result).toBe(true) - }) - - it('handles if response is null or undefined after JSON stringifying', async () => { - // Arrange - const infoSpy = sandbox.spy(Logger, 'debug') - Logger.isDebugEnabled = true const req = { - headers: { - traceid: Uuid() - }, - response: { - statusCode: 500 - } + ...fixtures.mockHapiRequestDto(), + response } + // Act requestLogger.logResponse(req) // Assert - const result = infoSpy.calledWith(`ALS-Trace=${req.headers.traceid} - Status: ${req.response.statusCode}, Stack: ${req.response.stack}`) - expect(result).toBe(true) + const logLine = infoSpy.firstCall.args[0] + expect(logLine).toContain(JSON.stringify(statusCode)) }) }) }) diff --git a/test/unit/mocks.js b/test/unit/mocks.js index 9371d007..6bcfb7fc 100644 --- a/test/unit/mocks.js +++ b/test/unit/mocks.js @@ -30,7 +30,9 @@ const RedisMock = require('ioredis-mock') */ class MockIoRedis extends RedisMock { connected = false - + /** + @param opts RedisOptions + */ constructor (opts) { super(opts) this.lazyConnect = Boolean(opts?.lazyConnect) @@ -41,6 +43,20 @@ class MockIoRedis extends RedisMock { } } +class IoRedisMockCluster extends MockIoRedis { + /** + @param nodesList BasicConnectionConfig[] + @param redisOptions RedisClusterOptions + */ + constructor (nodesList, redisOptions) { + super(redisOptions) + this.nodes = [] + nodesList.forEach((connOpts) => this.nodes.push(new MockIoRedis({ ...connOpts, ...redisOptions }))) + } +} + +MockIoRedis.Cluster = IoRedisMockCluster + module.exports = { MockIoRedis } diff --git a/test/unit/models/participantEndpoint/facade.test.js b/test/unit/models/participantEndpoint/facade.test.js index 601cbd24..6b36705d 100644 --- a/test/unit/models/participantEndpoint/facade.test.js +++ b/test/unit/models/participantEndpoint/facade.test.js @@ -56,6 +56,7 @@ jest.mock('@mojaloop/central-services-shared', () => ({ })) const Logger = require('@mojaloop/central-services-logger') +const fixtures = require('../../../fixtures') Logger.isDebugEnabled = jest.fn(() => true) Logger.isErrorEnabled = jest.fn(() => true) @@ -74,21 +75,7 @@ describe('participantEndpoint Facade', () => { const mockedConfig = { JWS_SIGN: false, FSPIOP_SOURCE_TO_SIGN: mockHubName, - PROTOCOL_VERSIONS: { - CONTENT: { - DEFAULT: '2.1', - VALIDATELIST: [ - '2.1' - ] - }, - ACCEPT: { - DEFAULT: '2', - VALIDATELIST: [ - '2', - '2.1' - ] - } - } + PROTOCOL_VERSIONS: fixtures.protocolVersionsDto() } jest.mock('../../../../src/lib/config', () => (mockedConfig)) @@ -203,29 +190,17 @@ describe('participantEndpoint Facade', () => { }) describe('sendErrorToParticipant', () => { + const mockConfigDto = ({ jwsSign = false } = {}) => ({ + JWS_SIGN: jwsSign, + FSPIOP_SOURCE_TO_SIGN: mockHubName, + JWS_SIGNING_KEY_PATH: 'secrets/jwsSigningKey.key', + JWS_SIGNING_KEY: 'somekey', + PROTOCOL_VERSIONS: fixtures.protocolVersionsDto() + }) + it('throws an error when the request fails', async () => { // Arrange - jest.mock('../../../../src/lib/config', () => ({ - JWS_SIGN: false, - FSPIOP_SOURCE_TO_SIGN: mockHubName, - JWS_SIGNING_KEY_PATH: 'secrets/jwsSigningKey.key', - JWS_SIGNING_KEY: 'somekey', - PROTOCOL_VERSIONS: { - CONTENT: { - DEFAULT: '2.1', - VALIDATELIST: [ - '2.1' - ] - }, - ACCEPT: { - DEFAULT: '2', - VALIDATELIST: [ - '2', - '2.1' - ] - } - } - })) + jest.mock('../../../../src/lib/config', () => mockConfigDto()) mockGetEndpoint.mockImplementation(() => 'https://example.com/12345') mockSendRequest.mockImplementation(() => { throw new Error('Request failed') }) @@ -249,28 +224,8 @@ describe('participantEndpoint Facade', () => { it('Success without JWS', async () => { // Arrange - const mockedConfig = { - JWS_SIGN: false, - FSPIOP_SOURCE_TO_SIGN: mockHubName, - JWS_SIGNING_KEY_PATH: 'secrets/jwsSigningKey.key', - JWS_SIGNING_KEY: 'somekey', - PROTOCOL_VERSIONS: { - CONTENT: { - DEFAULT: '2.1', - VALIDATELIST: [ - '2.1' - ] - }, - ACCEPT: { - DEFAULT: '2', - VALIDATELIST: [ - '2', - '2.1' - ] - } - } - } - jest.mock('../../../../src/lib/config', () => (mockedConfig)) + const mockedConfig = mockConfigDto() + jest.mock('../../../../src/lib/config', () => mockedConfig) mockGetEndpoint.mockImplementation(() => 'https://example.com/12345') mockSendRequest.mockImplementation(() => Promise.resolve(true)) @@ -293,8 +248,9 @@ describe('participantEndpoint Facade', () => { // Assert expect(spy).toHaveBeenCalled() - expect(mockSendRequest.mock.calls[0][8]).toBe(null) - expect(mockSendRequest.mock.calls[0][9]).toMatchObject({ + const { jwsSigner, protocolVersions } = mockSendRequest.mock.calls[0][0] + expect(jwsSigner).toBe(null) + expect(protocolVersions).toMatchObject({ accept: mockedConfig.PROTOCOL_VERSIONS.ACCEPT.DEFAULT, content: mockedConfig.PROTOCOL_VERSIONS.CONTENT.DEFAULT }) @@ -303,29 +259,8 @@ describe('participantEndpoint Facade', () => { it('adds jws signature when enabled', async () => { // Arrange - const mockedConfig = { - JWS_SIGN: true, - FSPIOP_SOURCE_TO_SIGN: mockHubName, - JWS_SIGNING_KEY_PATH: 'secrets/jwsSigningKey.key', - JWS_SIGNING_KEY: 'somekey', - PROTOCOL_VERSIONS: { - CONTENT: { - DEFAULT: '2.1', - VALIDATELIST: [ - '2.1' - ] - }, - ACCEPT: { - DEFAULT: '2', - VALIDATELIST: [ - '2', - '2.1' - ] - } - } - } - - jest.mock('../../../../src/lib/config', () => (mockedConfig)) + const mockedConfig = mockConfigDto({ jwsSign: true }) + jest.mock('../../../../src/lib/config', () => mockedConfig) mockGetEndpoint.mockImplementation(() => 'https://example.com/parties/MSISDN12345') mockSendRequest.mockImplementation(() => Promise.resolve(true)) @@ -348,8 +283,9 @@ describe('participantEndpoint Facade', () => { // Assert expect(spy).toHaveBeenCalled() - expect(typeof (mockSendRequest.mock.calls[0][8])).toBe('object') - expect(mockSendRequest.mock.calls[0][9]).toMatchObject({ + const { jwsSigner, protocolVersions } = mockSendRequest.mock.calls[0][0] + expect(jwsSigner).toBeTruthy() + expect(protocolVersions).toMatchObject({ accept: mockedConfig.PROTOCOL_VERSIONS.ACCEPT.DEFAULT, content: mockedConfig.PROTOCOL_VERSIONS.CONTENT.DEFAULT }) diff --git a/test/util/onboarding.js b/test/util/onboarding.js index 3cabf8c5..a435ce8e 100644 --- a/test/util/onboarding.js +++ b/test/util/onboarding.js @@ -1,10 +1,13 @@ const axios = require('axios') +const axiosRetry = require('axios-retry').default const { FspEndpointTypes } = require('@mojaloop/central-services-shared').Enum.EndPoints const Logger = require('@mojaloop/central-services-logger') const config = require('../../src/lib/config') const fixtures = require('../fixtures') const { CL_PORT, PROXY_HOST, PROXY_PORT, PARTY_ID_TYPE } = require('../integration/constants') +axiosRetry(axios, { retries: 5 }) + const alsAdminUrl = `http://localhost:${config.ADMIN_PORT}` const clUrl = `http://localhost:${CL_PORT}` const proxyUrl = `http://${PROXY_HOST}:${PROXY_PORT}` @@ -119,28 +122,6 @@ const createOracle = async ({ return oracle } -// const deleteAllOracles = async ({ -// oracleIdType = 'MSISDN', -// currency = 'EUR', -// endpointValue = `${proxyUrl}/oracle`, -// isDefault = false -// } = {}) => { -// const headers = fixtures.participantsCallHeadersDto() -// const body = { -// oracleIdType, -// endpoint: { -// value: endpointValue, -// endpointType: 'URL' -// }, -// currency, -// isDefault -// } -// const oracle = await axios.post(`${alsAdminUrl}/oracles`, body, { headers }) -// -// Logger.info(`createOracle is finished`) -// return oracle -// } - module.exports = { createHubAccounts, createTestParticipant,