@mojaloop/bulk-api-adapter 17.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/.circleci/config.yml +11 -0
  2. package/.dockerignore +17 -0
  3. package/.editorconfig +10 -0
  4. package/.env +0 -0
  5. package/.gitattributes +2 -0
  6. package/.ncurc.yaml +5 -0
  7. package/.nvmrc +1 -0
  8. package/.nycrc.yml +20 -0
  9. package/.versionrc +15 -0
  10. package/API.md +2 -0
  11. package/CHANGELOG.md +257 -0
  12. package/CODEOWNERS +38 -0
  13. package/Dockerfile +44 -0
  14. package/LICENSE.md +10 -0
  15. package/README.md +40 -0
  16. package/audit-ci.jsonc +9 -0
  17. package/config/default.json +164 -0
  18. package/docker/bulk-api-adapter/default.json +147 -0
  19. package/docker/central-ledger/default.json +460 -0
  20. package/docker/kafka/scripts/provision.sh +42 -0
  21. package/docker/sql-init/01_permissions.sql +2 -0
  22. package/docker/wait-for/wait-for-bulk-api-adapter.sh +11 -0
  23. package/docker/wait-for/wait-for-central-ledger.sh +13 -0
  24. package/docker/wait-for/wait-for-kafka.sh +7 -0
  25. package/docker/wait-for/wait-for-mockserver.sh +20 -0
  26. package/docker/wait-for/wait-for-mysql.sh +11 -0
  27. package/docker/wait-for/wait-for-objstore.sh +7 -0
  28. package/docker/wait-for/wait-for.env +11 -0
  29. package/docker/wait-for/wait-for.sh +81 -0
  30. package/docker-compose.yml +167 -0
  31. package/jsdoc.json +38 -0
  32. package/package.json +152 -0
  33. package/sonar-project.properties +17 -0
  34. package/src/api/handlers/bulkTransfers/{id}/error.js +71 -0
  35. package/src/api/handlers/bulkTransfers/{id}.js +98 -0
  36. package/src/api/handlers/bulkTransfers.js +77 -0
  37. package/src/api/handlers/endpointcache.js +48 -0
  38. package/src/api/handlers/health.js +52 -0
  39. package/src/api/handlers/metrics.js +50 -0
  40. package/src/api/index.js +42 -0
  41. package/src/api/routes.js +42 -0
  42. package/src/domain/bulkTransfer/index.js +219 -0
  43. package/src/domain/participant/index.js +61 -0
  44. package/src/domain/participant/lib/cache/participantEndpoint.js +127 -0
  45. package/src/handlers/api/health/plugin.js +51 -0
  46. package/src/handlers/api/health/routes.js +42 -0
  47. package/src/handlers/api/metrics/handler.js +34 -0
  48. package/src/handlers/api/metrics/plugin.js +52 -0
  49. package/src/handlers/api/metrics/routes.js +43 -0
  50. package/src/handlers/index.js +82 -0
  51. package/src/handlers/notification/index.js +362 -0
  52. package/src/handlers/register.js +54 -0
  53. package/src/interface/swagger.yaml +672 -0
  54. package/src/lib/config.js +100 -0
  55. package/src/lib/headers.js +78 -0
  56. package/src/lib/healthCheck/subServiceHealth.js +88 -0
  57. package/src/models/participant/facade.js +61 -0
  58. package/src/models/participant/participantEndpoint.js +79 -0
  59. package/src/shared/plugins.js +111 -0
  60. package/src/shared/setup.js +204 -0
  61. package/test/fixtures/.gitkeep +0 -0
  62. package/test/functional/.gitkeep +0 -0
  63. package/test/helpers.js +79 -0
  64. package/test/integration/.gitkeep +0 -0
  65. package/test/unit/api/handlers/bulkTransfers/{id}/error.test.js +140 -0
  66. package/test/unit/api/handlers/bulkTransfers/{id}.test.js +209 -0
  67. package/test/unit/api/handlers/notification/index.test.js +252 -0
  68. package/test/unit/data/bulkTransfers/{id}/error.js +28 -0
  69. package/test/unit/data/bulkTransfers/{id}.js +49 -0
  70. package/test/unit/data/bulkTransfers.js +28 -0
  71. package/test/unit/data/metrics.js +28 -0
  72. package/test/unit/data/mockgen.js +13 -0
  73. package/test/unit/data/transfers/{id}/error.js +28 -0
  74. package/test/unit/domain/bulkTransfers/index.test.js +181 -0
  75. package/test/unit/endpointcache.test.js +52 -0
  76. package/test/unit/handlers/notification/handler.test.js +323 -0
  77. package/test/unit/health.test.js +75 -0
  78. package/test/unit/lib/config.test.js +83 -0
  79. package/test/unit/lib/headers.test.js +111 -0
  80. package/test/unit/metrics.test.js +71 -0
  81. package/test-integration.Dockerfile +24 -0
  82. package/test.Dockerfile +22 -0
@@ -0,0 +1,20 @@
1
+ #!/bin/sh
2
+ # wait-for-mockserver.sh
3
+
4
+ source /opt/wait-for/wait-for.env
5
+
6
+ function healthCheck() {
7
+ curl -s -X GET "http://$WAIT_FOR_MOCK_HOST:$WAIT_FOR_MOCK_PORT"
8
+ }
9
+
10
+ function command() {
11
+ curl -s -X PUT "http://$WAIT_FOR_MOCK_HOST:$WAIT_FOR_MOCK_PORT/expectation" -d '{ "httpRequest": { "method": ".*", "path": "/.*transfers.*" }, "times" : { "remainingTimes" : 0, "unlimited" : true }, "timeToLive" : { "unlimited" : true }, "httpResponse": { "statusCode": 200, "body": "{}" } }';
12
+ }
13
+
14
+ until healthCheck; do
15
+ >&2 echo "mockserver is unavailable - sleeping"
16
+ sleep 1
17
+ done
18
+
19
+ >&2 echo "mockserver is up - executing command"
20
+ command
@@ -0,0 +1,11 @@
1
+ #!/bin/sh
2
+
3
+ echo "** STARTUP - Checking for DB connection..."
4
+
5
+ source /opt/wait-for/wait-for.env
6
+
7
+ apk --no-cache add mysql-client
8
+
9
+ until result=$(mysql -h $WAIT_FOR_DB_HOST -P $WAIT_FOR_DB_PORT -u $WAIT_FOR_DB_USER --password=$WAIT_FOR_DB_PASSWORD $WAIT_FOR_DB_DATABASE -ss -N -e 'select 1;') && eval 'echo is_connected=$result' && if [ -z $result ]; then false; fi && if [ $result -ne 1 ]; then false; fi; do echo waiting for MySQL; sleep 2; done;
10
+
11
+ echo "** STARTUP - DB connection successful!"
@@ -0,0 +1,7 @@
1
+ #!/bin/sh
2
+
3
+ echo "** STARTUP - Checking for Object Store connection..."
4
+
5
+ source /opt/wait-for/wait-for.env
6
+
7
+ sh /opt/wait-for/wait-for.sh $WAIT_FOR_OBJSTORE_SERVER -t 240 -- echo "** STARTUP - Object Store connection successful!"
@@ -0,0 +1,11 @@
1
+ # Environment config for wait-for scripts
2
+
3
+ WAIT_FOR_DB_HOST=mysql
4
+ WAIT_FOR_DB_PORT=3306
5
+ WAIT_FOR_DB_USER=central_ledger
6
+ WAIT_FOR_DB_PASSWORD=password
7
+ WAIT_FOR_DB_DATABASE=central_ledger
8
+ WAIT_FOR_OBJSTORE_SERVER=objstore:27017
9
+ WAIT_FOR_DB_KAFKA_BROKER=kafka:29092
10
+ WAIT_FOR_MOCK_HOST=mockserver
11
+ WAIT_FOR_MOCK_PORT=1080
@@ -0,0 +1,81 @@
1
+ #!/bin/sh
2
+
3
+ # Script from https://github.com/eficode/wait-for
4
+
5
+ TIMEOUT=15
6
+ QUIET=0
7
+
8
+ echoerr() {
9
+ if [ "$QUIET" -ne 1 ]; then printf "%s\n" "$*" 1>&2; fi
10
+ }
11
+
12
+ usage() {
13
+ exitcode="$1"
14
+ cat << USAGE >&2
15
+ Usage:
16
+ $cmdname host:port [-t timeout] [-- command args]
17
+ -q | --quiet Do not output any status messages
18
+ -t TIMEOUT | --timeout=timeout Timeout in seconds, zero for no timeout
19
+ -- COMMAND ARGS Execute command with args after the test finishes
20
+ USAGE
21
+ exit "$exitcode"
22
+ }
23
+
24
+ wait_for() {
25
+ for i in `seq $TIMEOUT` ; do
26
+ nc -z "$HOST" "$PORT" > /dev/null 2>&1
27
+
28
+ result=$?
29
+ if [ $result -eq 0 ] ; then
30
+ if [ $# -gt 0 ] ; then
31
+ exec "$@"
32
+ fi
33
+ exit 0
34
+ fi
35
+ sleep 1
36
+ done
37
+ echo "Operation timed out" >&2
38
+ exit 1
39
+ }
40
+
41
+ while [ $# -gt 0 ]
42
+ do
43
+ case "$1" in
44
+ *:* )
45
+ HOST=$(printf "%s\n" "$1"| cut -d : -f 1)
46
+ PORT=$(printf "%s\n" "$1"| cut -d : -f 2)
47
+ shift 1
48
+ ;;
49
+ -q | --quiet)
50
+ QUIET=1
51
+ shift 1
52
+ ;;
53
+ -t)
54
+ TIMEOUT="$2"
55
+ if [ "$TIMEOUT" = "" ]; then break; fi
56
+ shift 2
57
+ ;;
58
+ --timeout=*)
59
+ TIMEOUT="${1#*=}"
60
+ shift 1
61
+ ;;
62
+ --)
63
+ shift
64
+ break
65
+ ;;
66
+ --help)
67
+ usage 0
68
+ ;;
69
+ *)
70
+ echoerr "Unknown argument: $1"
71
+ usage 1
72
+ ;;
73
+ esac
74
+ done
75
+
76
+ if [ "$HOST" = "" -o "$PORT" = "" ]; then
77
+ echoerr "Error: you need to provide a host and port to test."
78
+ usage 2
79
+ fi
80
+
81
+ wait_for "$@"
@@ -0,0 +1,167 @@
1
+ networks:
2
+ ba-mojaloop-net:
3
+ name: ba-mojaloop-net
4
+
5
+ services:
6
+ bulk-api-adapter:
7
+ build:
8
+ context: .
9
+ cache_from:
10
+ - mojaloop/bulk-api-adapter
11
+ - bulk-api-adapter
12
+ container_name: ba_bulk-api-adapter
13
+ user: root
14
+ command:
15
+ - "sh"
16
+ - "-c"
17
+ - "sh /opt/wait-for/wait-for-bulk-api-adapter.sh && node src/api/index.js"
18
+ ports:
19
+ - "3003:3003"
20
+ environment:
21
+ - LOG_LEVEL=info
22
+ - CSL_LOG_TRANSPORT=file
23
+ volumes:
24
+ - ./docker/bulk-api-adapter/default.json:/opt/app/config/default.json
25
+ - ./docker/wait-for:/opt/wait-for
26
+ networks:
27
+ - ba-mojaloop-net
28
+ depends_on:
29
+ - central-ledger
30
+ - kafka
31
+ - objstore
32
+ healthcheck:
33
+ test: ["CMD", "sh", "-c" ,"apk --no-cache add curl", ";", "curl", "http://localhost:3003/health"]
34
+ timeout: 20s
35
+ retries: 10
36
+ interval: 30s
37
+
38
+ objstore:
39
+ image: mongo:8.0
40
+ container_name: ba_objstore
41
+ logging:
42
+ driver: none
43
+ ports:
44
+ - "27017:27017"
45
+ networks:
46
+ - ba-mojaloop-net
47
+ healthcheck:
48
+ test: mongo localhost:27017/test --quiet 1
49
+ interval: 10s
50
+ timeout: 10s
51
+ retries: 5
52
+ start_period: 40s
53
+
54
+ central-ledger:
55
+ image: mojaloop/central-ledger:v18.0.1
56
+ container_name: ba_central-ledger
57
+ user: root
58
+ command:
59
+ - "sh"
60
+ - "-c"
61
+ - "sh /opt/wait-for/wait-for-central-ledger.sh && node src/api/index.js"
62
+ links:
63
+ - mysql
64
+ - kafka
65
+ ports:
66
+ - "3001:3001"
67
+ volumes:
68
+ - ./docker/central-ledger/default.json:/opt/app/config/default.json
69
+ - ./docker/wait-for:/opt/wait-for
70
+ depends_on:
71
+ - mysql
72
+ - kafka
73
+ environment:
74
+ - CLEDG_DATABASE_URI=mysql://central_ledger:password@mysql:3306/central_ledger
75
+ - CLEDG_SIDECAR__DISABLED=true
76
+ networks:
77
+ - ba-mojaloop-net
78
+ healthcheck:
79
+ test: ["CMD", "bash", "-c" ,"apk --no-cache add curl", ";", "curl", "http://localhost:3001/health"]
80
+ timeout: 20s
81
+ retries: 10
82
+ interval: 30s
83
+
84
+ mysql:
85
+ image: mysql/mysql-server:8.0
86
+ container_name: ba_mysql
87
+ logging:
88
+ driver: none
89
+ ports:
90
+ - "3306:3306"
91
+ volumes:
92
+ # this fixes the permissions issue, but docker-compose up will fail on first attempt
93
+ - ./docker/sql-init/:/docker-entrypoint-initdb.d/
94
+ environment:
95
+ - MYSQL_USER=${DBUSER:-central_ledger}
96
+ - MYSQL_PASSWORD=${DBPASS:-password}
97
+ - MYSQL_DATABASE=${DBUSER:-central_ledger}
98
+ - MYSQL_ALLOW_EMPTY_PASSWORD=true
99
+ networks:
100
+ - ba-mojaloop-net
101
+ healthcheck:
102
+ test: ["CMD", "mysqladmin" ,"ping", "-h", "mysql"]
103
+ timeout: 20s
104
+ retries: 10
105
+ start_period: 40s
106
+ interval: 30s
107
+
108
+ kafka:
109
+ image: docker.io/bitnami/kafka:3.9
110
+ container_name: cs_kafka
111
+ networks:
112
+ - ba-mojaloop-net
113
+ ports:
114
+ - "9092:9092"
115
+ environment:
116
+ # BITNAMI_DEBUG: "yes"
117
+ ALLOW_PLAINTEXT_LISTENER: "yes"
118
+ KAFKA_ADVERTISED_HOST_NAME: kafka
119
+ KAFKA_CFG_LISTENERS: CONTROLLER://:9093,LISTENER_DOCKER://:29092,LISTENER_EXTERN://:9092
120
+ KAFKA_CFG_ADVERTISED_LISTENERS: LISTENER_DOCKER://kafka:29092,LISTENER_EXTERN://localhost:9092
121
+ KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP: CONTROLLER:PLAINTEXT,LISTENER_DOCKER:PLAINTEXT,LISTENER_EXTERN:PLAINTEXT
122
+ KAFKA_CFG_INTER_BROKER_LISTENER_NAME: LISTENER_DOCKER
123
+ KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
124
+ KAFKA_CFG_MESSAGE_MAX_BYTES: 200000000
125
+ KAFKA_CFG_NODE_ID: 1
126
+ KAFKA_CFG_PROCESS_ROLES: broker,controller
127
+ KAFKA_CFG_CONTROLLER_LISTENER_NAMES: CONTROLLER
128
+ KAFKA_CFG_CONTROLLER_QUORUM_VOTERS: 1@127.0.0.1:9093
129
+ KAFKA_AUTO_CREATE_TOPICS_ENABLE: "true"
130
+ KAFKA_ENABLE_KRAFT: "true"
131
+ healthcheck:
132
+ test: ["CMD" ,"/opt/bitnami/kafka/bin/kafka-broker-api-versions.sh","--bootstrap-server","kafka:29092"]
133
+ timeout: 20s
134
+ retries: 10
135
+ start_period: 40s
136
+ interval: 30s
137
+
138
+ kafka-provisioning:
139
+ container_name: cs_kafka-provisioning
140
+ networks:
141
+ - ba-mojaloop-net
142
+ image: docker.io/bitnami/kafka:3.9
143
+ depends_on:
144
+ - kafka
145
+ volumes:
146
+ - ./docker/kafka/scripts:/tmp/kafka/scripts
147
+ command: bash /tmp/kafka/scripts/provision.sh
148
+
149
+ simulator:
150
+ image: mojaloop/simulator:v12.2.1
151
+ container_name: ba_simulator
152
+ ports:
153
+ - "8444:8444"
154
+ environment:
155
+ - LOG_LEVEL=info
156
+ - TRANSFERS_ENDPOINT=http://host.docker.internal:3000
157
+ - QUOTES_ENDPOINT=http://host.docker.internal:3002
158
+ - PARTIES_ENDPOINT=http://host.docker.internal:4002
159
+ - TRANSFERS_FULFIL_RESPONSE_DISABLED=false
160
+ networks:
161
+ - ba-mojaloop-net
162
+ healthcheck:
163
+ test: ["CMD", "sh", "-c" ,"apk --no-cache add curl", ";", "curl", "http://localhost:8444/health"]
164
+ timeout: 20s
165
+ retries: 10
166
+ interval: 30s
167
+
package/jsdoc.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "tags": {
3
+ "allowUnknownTags": true
4
+ },
5
+ "source": {
6
+ "include": [
7
+ "src"
8
+ ],
9
+ "includePattern": ".js$",
10
+ "excludePattern": "(node_modules/|docs)"
11
+ },
12
+ "plugins": [
13
+ "plugins/markdown"
14
+ ],
15
+ "opts": {
16
+ "template": "node_modules/docdash",
17
+ "encoding": "utf8",
18
+ "destination": "docs/",
19
+ "recurse": true,
20
+ "verbose": true
21
+ },
22
+ "markdown": {
23
+ "parser": "gfm",
24
+ "hardwrap": true
25
+ },
26
+ "templates": {
27
+ "cleverLinks": false,
28
+ "monospaceLinks": false,
29
+ "default": {
30
+ "outputSourceFiles": true,
31
+ "includeDate": false
32
+ }
33
+ },
34
+ "docdash": {
35
+ "static": false,
36
+ "sort": true
37
+ }
38
+ }
package/package.json ADDED
@@ -0,0 +1,152 @@
1
+ {
2
+ "name": "@mojaloop/bulk-api-adapter",
3
+ "version": "17.1.6",
4
+ "description": "Mojaloop Bulk API Adapter",
5
+ "license": "Apache-2.0",
6
+ "author": "ModusBox",
7
+ "contributors": [
8
+ "Georgi Georgiev <georgi.georgiev@modusbox.com>",
9
+ "Miguel de Barros <miguel.debarros@modusbox.com>",
10
+ "Sam Kummary <sam@mojaloop.io",
11
+ "Steven Oderayi <steven.oderayi@modusbox.com>",
12
+ "Valentin Genev <valentin.genev@modusbox.com>"
13
+ ],
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git://github.com/mojaloop/bulk-api-adapter.git"
17
+ },
18
+ "publishConfig": {
19
+ "registry": "https://registry.npmjs.org"
20
+ },
21
+ "engines": {
22
+ "node": ">=18.x"
23
+ },
24
+ "imports": {
25
+ "#src/*": "./src/*.js",
26
+ "#test/*": "./test/*.js"
27
+ },
28
+ "main": "./src/api/index.js",
29
+ "pre-commit": [
30
+ "lint",
31
+ "dep:check",
32
+ "test"
33
+ ],
34
+ "scripts": {
35
+ "start": "npm run start:api",
36
+ "start:api": "node src/api/index.js",
37
+ "watch:api": "npx nodemon src/api/index.js",
38
+ "regenerate": "yo swaggerize:test --framework hapi --apiPath './src/interface/swagger.yaml'",
39
+ "standard": "npx standard",
40
+ "standard:fix": "npx standard --fix",
41
+ "lint": "npm run standard",
42
+ "lint:fix": "npm run standard:fix",
43
+ "test": "npm run test:unit",
44
+ "test:all": "npm run test",
45
+ "test:unit": "tape 'test/unit/**/*.test.js' | tap-spec",
46
+ "test:xunit": "tape 'test/unit/**/*.test.js' | tap-xunit",
47
+ "test:coverage": "npx nyc --reporter=lcov --reporter=text-summary tapes -- 'test/unit/**/**.test.js'",
48
+ "test:coverage-check": "npm run test:coverage && nyc check-coverage",
49
+ "test:functional": "echo 'No functional tests defined'",
50
+ "test:integration": "echo 'No integration tests defined'",
51
+ "docker:build": "docker build --build-arg NODE_VERSION=\"$(cat .nvmrc)-alpine\" -t mojaloop/bulk-api-adapter:local .",
52
+ "docker:up": "docker-compose -f docker-compose.yml up -d",
53
+ "docker:stop": "docker-compose -f docker-compose.yml stop",
54
+ "docker:rm": "docker-compose -f docker-compose.yml rm -f -v",
55
+ "docker:down": "docker-compose -f docker-compose.yml down -v",
56
+ "docker:clean": "docker-compose -f docker-compose.yml down --rmi local",
57
+ "audit:fix": "npm audit fix",
58
+ "audit:check": "npx audit-ci --config ./audit-ci.jsonc",
59
+ "dep:check": "npx ncu -e 2",
60
+ "dep:update": "npx ncu -u",
61
+ "release": "npx standard-version --releaseCommitMessageFormat 'chore(release): {{currentTag}} [skip ci]'",
62
+ "snapshot": "npx standard-version --no-verify --skip.changelog --prerelease snapshot --releaseCommitMessageFormat 'chore(snapshot): {{currentTag}}'"
63
+ },
64
+ "dependencies": {
65
+ "@hapi/basic": "7.0.2",
66
+ "@hapi/boom": "10.0.1",
67
+ "@hapi/catbox": "12.1.1",
68
+ "@hapi/catbox-memory": "6.0.2",
69
+ "@hapi/good": "9.0.1",
70
+ "@hapi/hapi": "21.3.12",
71
+ "@hapi/inert": "7.1.0",
72
+ "@hapi/vision": "7.0.3",
73
+ "@mojaloop/central-services-error-handling": "13.0.6",
74
+ "@mojaloop/central-services-health": "15.0.2",
75
+ "@mojaloop/central-services-logger": "11.5.5",
76
+ "@mojaloop/central-services-metrics": "12.4.5",
77
+ "@mojaloop/central-services-shared": "18.15.1",
78
+ "@mojaloop/central-services-stream": "11.4.3",
79
+ "@mojaloop/sdk-standard-components": "19.6.4",
80
+ "@mojaloop/event-sdk": "14.1.3",
81
+ "@mojaloop/object-store-lib": "12.0.5",
82
+ "@now-ims/hapi-now-auth": "2.1.0",
83
+ "axios": "1.7.9",
84
+ "blipp": "4.0.2",
85
+ "commander": "13.1.0",
86
+ "hapi-auth-bearer-token": "8.0.0",
87
+ "hapi-openapi": "3.0.0",
88
+ "hapi-swagger": "17.3.2",
89
+ "immutable": "5.0.3",
90
+ "joi": "^17.13.3",
91
+ "mongo-uri-builder": "^4.0.0",
92
+ "mustache": "4.2.0",
93
+ "parse-strings-in-object": "2.0.0",
94
+ "rc": "1.2.8",
95
+ "uuid4": "2.0.3"
96
+ },
97
+ "overrides": {
98
+ "ansi-regex": "5.0.1",
99
+ "postcss": {
100
+ "nanoid": "^3.3.8"
101
+ },
102
+ "swagmock": {
103
+ "validator": "13.12.0"
104
+ },
105
+ "shins": {
106
+ "ajv": "6.12.3",
107
+ "ejs": "3.1.10",
108
+ "path-to-regexp": "0.1.12",
109
+ "sanitize-html": "2.12.1",
110
+ "markdown-it": "12.3.2",
111
+ "undici": "6.21.1"
112
+ },
113
+ "widdershins": {
114
+ "swagger2openapi": "7.0.8",
115
+ "markdown-it": "12.3.2"
116
+ },
117
+ "request": {
118
+ "tough-cookie": "4.1.3"
119
+ },
120
+ "jsonwebtoken": "9.0.0",
121
+ "jsonpointer": "5.0.0",
122
+ "cross-spawn": "7.0.6",
123
+ "trim": "0.0.3",
124
+ "yargs-parser": "21.1.1"
125
+ },
126
+ "devDependencies": {
127
+ "audit-ci": "^7.1.0",
128
+ "nyc": "17.1.0",
129
+ "nodemon": "3.1.9",
130
+ "npm-check-updates": "17.1.14",
131
+ "pre-commit": "1.2.2",
132
+ "proxyquire": "2.1.3",
133
+ "replace": "^1.2.2",
134
+ "rewire": "7.0.0",
135
+ "sinon": "19.0.2",
136
+ "standard": "17.1.2",
137
+ "standard-version": "^9.5.0",
138
+ "swagmock": "1.0.0",
139
+ "tap-spec": "^5.0.0",
140
+ "tap-xunit": "2.4.1",
141
+ "tape": "5.9.0",
142
+ "tapes": "4.1.0"
143
+ },
144
+ "generator-swaggerize": {
145
+ "version": "4.1.0"
146
+ },
147
+ "standard-version": {
148
+ "scripts": {
149
+ "postchangelog": "replace '\\[mojaloop/#(\\d+)\\]\\(https://github.com/mojaloop/(.*)/issues/(\\d+)\\)' '[mojaloop/#$1](https://github.com/mojaloop/project/issues/$1)' CHANGELOG.md"
150
+ }
151
+ }
152
+ }
@@ -0,0 +1,17 @@
1
+ # Required metadata
2
+ sonar.projectKey=bulk-api-adapter
3
+ sonar.projectName=bulk-api-adapter
4
+ sonar.projectVersion=1.0
5
+
6
+ # Comma-separated paths to directories with sources (required)
7
+ sonar.sources=src,test
8
+
9
+ # Language
10
+ sonar.language=js
11
+
12
+ # Encoding of the source files
13
+ sonar.sourceEncoding=UTF-8
14
+ sonar.exclusions=migrations
15
+
16
+ # To import the LCOV report
17
+ sonar.javascript.lcov.reportPath=lcov.info
@@ -0,0 +1,71 @@
1
+ /*****
2
+ License
3
+ --------------
4
+ Copyright © 2020-2025 Mojaloop Foundation
5
+ The Mojaloop files are made available by the Mojaloop 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
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ 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.
10
+
11
+ Contributors
12
+ --------------
13
+ This is the official list of the Mojaloop project contributors for this file.
14
+ Names of the original copyright holders (individuals or organizations)
15
+ should be listed with a '*' in the first column. People who have
16
+ contributed from an organization can be listed under the organization
17
+ that actually holds the copyright for their contributions (see the
18
+ Mojaloop Foundation organization for an example). Those individuals should have
19
+ their names indented and be marked with a '-'. Email address can be added
20
+ optionally within square brackets <email>.
21
+
22
+ * Mojaloop Foundation
23
+ - Name Surname <name.surname@mojaloop.io>
24
+
25
+ * ModusBox
26
+ - Steven Oderayi <steven.oderayi@modusbox.com>
27
+ --------------
28
+ ******/
29
+
30
+ 'use strict'
31
+
32
+ const TransferService = require('../../../../domain/bulkTransfer')
33
+ const Logger = require('@mojaloop/central-services-logger')
34
+ const ErrorHandler = require('@mojaloop/central-services-error-handling')
35
+ const Hash = require('@mojaloop/central-services-shared').Util.Hash
36
+ const Uuid = require('uuid4')
37
+ const HTTPENUM = require('@mojaloop/central-services-shared').Enum.Http
38
+ const BulkTransferModels = require('@mojaloop/object-store-lib').Models.BulkTransfer
39
+
40
+ /**
41
+ * Operations on /bulkTransfers/{id}/error
42
+ */
43
+ module.exports = {
44
+ /**
45
+ * summary: Handles bulk transfer error callback
46
+ * description: If the server is unable to find or create a bulk transfer, or another processing error occurs, the error callback PUT /bulkTransfers/<ID>/error is used. The <ID> in the URI should contain the bulkTransferId that was used for the creation request of the bulk transfer (POST /bulkTransfers), or the <ID> that was used in the GET /bulkTransfers/<ID>.
47
+ * parameters: id, body, Content-Length, Content-Type, Date, X-Forwarded-For, FSPIOP-Source, FSPIOP-Destination, FSPIOP-Encryption, FSPIOP-Signature, FSPIOP-URI, FSPIOP-HTTP-Method
48
+ * produces: application/json
49
+ * responses: default
50
+ */
51
+ put: async function putBulkTransferErrorById (request, h) {
52
+ try {
53
+ Logger.debug('error::payload(%s)', JSON.stringify(request.payload))
54
+
55
+ const bulkTransferId = request.params.id
56
+ const { errorInformation, extensionList } = request.payload
57
+ const hash = Hash.generateSha256(JSON.stringify(request.payload))
58
+ const messageId = Uuid()
59
+ const message = { bulkTransferId, errorInformation, extensionList, hash }
60
+
61
+ const IndividualTransferFulfilModel = BulkTransferModels.getIndividualTransferFulfilModel()
62
+ await new IndividualTransferFulfilModel({ messageId, bulkTransferId, payload: request.payload }).save()
63
+ await TransferService.bulkTransferError(messageId, request.headers, message)
64
+
65
+ return h.response().code(HTTPENUM.ReturnCodes.OK.CODE)
66
+ } catch (err) {
67
+ Logger.error(err)
68
+ throw ErrorHandler.Factory.reformatFSPIOPError(err)
69
+ }
70
+ }
71
+ }
@@ -0,0 +1,98 @@
1
+ /*****
2
+ License
3
+ --------------
4
+ Copyright © 2020-2025 Mojaloop Foundation
5
+ The Mojaloop files are made available by the Mojaloop 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
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ 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.
10
+
11
+ Contributors
12
+ --------------
13
+ This is the official list of the Mojaloop project contributors for this file.
14
+ Names of the original copyright holders (individuals or organizations)
15
+ should be listed with a '*' in the first column. People who have
16
+ contributed from an organization can be listed under the organization
17
+ that actually holds the copyright for their contributions (see the
18
+ Mojaloop Foundation organization for an example). Those individuals should have
19
+ their names indented and be marked with a '-'. Email address can be added
20
+ optionally within square brackets <email>.
21
+
22
+ * Mojaloop Foundation
23
+ - Name Surname <name.surname@mojaloop.io>
24
+
25
+ * ModusBox
26
+ - Georgi Georgiev <georgi.georgiev@modusbox.com>
27
+ - Valentin Genev <valentin.genev@modusbox.com>
28
+ - Steven Oderayi <steven.oderayi@modusbox.com>
29
+ --------------
30
+ ******/
31
+ 'use strict'
32
+
33
+ const Uuid = require('uuid4')
34
+ const Logger = require('@mojaloop/central-services-logger')
35
+ const ErrorHandler = require('@mojaloop/central-services-error-handling')
36
+ const BulkTransferModels = require('@mojaloop/object-store-lib').Models.BulkTransfer
37
+ const Hash = require('@mojaloop/central-services-shared').Util.Hash
38
+ const HTTPENUM = require('@mojaloop/central-services-shared').Enum.Http
39
+ const TransferService = require('../../../domain/bulkTransfer')
40
+
41
+ /**
42
+ * Operations on /bulkTransfers/{id}
43
+ */
44
+ module.exports = {
45
+ /**
46
+ * summary: Get a bulk transfer by Id
47
+ * description:
48
+ * parameters: accept, content-type, date, x-forwarded-for, fspiop-source, fspiop-destination, fspiop-encryption, fspiop-signature, fspiop-uri, fspiop-http-method, id
49
+ * produces:
50
+ * responses: default
51
+ */
52
+ get: async function getBulkTransfersId (request, h) {
53
+ try {
54
+ Logger.isInfoEnabled && Logger.info(`getBulkTransfersId::id(${request.params.id})`)
55
+ const messageId = Uuid()
56
+ await TransferService.getBulkTransferById(messageId, request.headers, request.params)
57
+ return h.response().code(HTTPENUM.ReturnCodes.ACCEPTED.CODE)
58
+ } catch (err) {
59
+ Logger.error(err)
60
+ throw ErrorHandler.Factory.reformatFSPIOPError(err)
61
+ }
62
+ },
63
+ /**
64
+ * summary: Fulfil bulkTransfer
65
+ * description:
66
+ * parameters: content-type, date, x-forwarded-for, fspiop-source, fspiop-destination, fspiop-encryption, fspiop-signature, fspiop-uri, fspiop-http-method, id, body
67
+ * produces:
68
+ * responses: default
69
+ */
70
+ put: async function BulkTransfersByIDPut (request, h) {
71
+ try {
72
+ Logger.debug('create::payload(%s)', JSON.stringify(request.payload))
73
+ const bulkTransferId = request.params.id
74
+ const { bulkTransferState, completedTimestamp, extensionList } = request.payload
75
+ const hash = Hash.generateSha256(JSON.stringify(request.payload))
76
+ const messageId = Uuid()
77
+ /**
78
+ * Disabled writing to ML Object Store (bulkTransferFulfils) as it is not used:
79
+ */
80
+ // const BulkTransferFulfilModel = BulkTransferModels.getBulkTransferFulfilModel()
81
+ // const doc = Object.assign({}, { messageId, headers: request.headers, bulkTransferId }, request.payload)
82
+ // await new BulkTransferFulfilModel(doc).save()
83
+
84
+ const IndividualTransferFulfilModel = BulkTransferModels.getIndividualTransferFulfilModel()
85
+ await Promise.all(request.payload.individualTransferResults.map(payload => {
86
+ new IndividualTransferFulfilModel({ messageId, bulkTransferId, payload }).save()
87
+ return null
88
+ }))
89
+ const count = request.payload.individualTransferResults.length
90
+ const message = { bulkTransferId, bulkTransferState, completedTimestamp, extensionList, count, hash }
91
+ await TransferService.bulkFulfil(messageId, request.headers, message)
92
+ return h.response().code(HTTPENUM.ReturnCodes.OK.CODE)
93
+ } catch (err) {
94
+ Logger.error(err)
95
+ throw ErrorHandler.Factory.reformatFSPIOPError(err)
96
+ }
97
+ }
98
+ }