diff --git a/.ncurc.json b/.ncurc.json index eb3c8d246..4bee8b77e 100644 --- a/.ncurc.json +++ b/.ncurc.json @@ -1,5 +1,6 @@ { "reject": [ + "hapi-swagger", "tape", "ilp-packet" ] diff --git a/audit-resolve.json b/audit-resolve.json index c62f63680..e69de29bb 100644 --- a/audit-resolve.json +++ b/audit-resolve.json @@ -1,166 +0,0 @@ -{ - "decisions": { - "1523|@mojaloop/central-object-store>@mojaloop/central-services-logger>winston>async>lodash": { - "decision": "ignore", - "madeAt": 1594103543707, - "expiresAt": 1596695532690 - }, - "1523|@mojaloop/central-services-health>@mojaloop/central-services-logger>winston>async>lodash": { - "decision": "ignore", - "madeAt": 1594103543707, - "expiresAt": 1596695532690 - }, - "1523|@mojaloop/central-services-health>@mojaloop/central-services-shared>@mojaloop/central-services-logger>winston>async>lodash": { - "decision": "ignore", - "madeAt": 1594103543707, - "expiresAt": 1596695532690 - }, - "1523|@mojaloop/central-services-health>@mojaloop/central-services-shared>@mojaloop/event-sdk>@mojaloop/central-services-logger>winston>async>lodash": { - "decision": "ignore", - "madeAt": 1594103543707, - "expiresAt": 1596695532690 - }, - "1523|@mojaloop/central-services-shared>@mojaloop/event-sdk>@mojaloop/central-services-logger>winston>async>lodash": { - "decision": "ignore", - "madeAt": 1594103543707, - "expiresAt": 1596695532690 - }, - "1523|@mojaloop/event-sdk>@mojaloop/central-services-logger>winston>async>lodash": { - "decision": "ignore", - "madeAt": 1594103543707, - "expiresAt": 1596695532690 - }, - "1523|@mojaloop/central-services-logger>winston>async>lodash": { - "decision": "ignore", - "madeAt": 1594103543707, - "expiresAt": 1596695532690 - }, - "1523|@mojaloop/central-services-shared>@mojaloop/central-services-logger>winston>async>lodash": { - "decision": "ignore", - "madeAt": 1594103543707, - "expiresAt": 1596695532690 - }, - "1523|@mojaloop/central-services-health>@mojaloop/central-services-shared>@mojaloop/event-sdk>winston>async>lodash": { - "decision": "ignore", - "madeAt": 1594103543708, - "expiresAt": 1596695532690 - }, - "1523|@mojaloop/central-services-shared>@mojaloop/event-sdk>winston>async>lodash": { - "decision": "ignore", - "madeAt": 1594103543708, - "expiresAt": 1596695532690 - }, - "1523|@mojaloop/event-sdk>winston>async>lodash": { - "decision": "ignore", - "madeAt": 1594103543708, - "expiresAt": 1596695532690 - }, - "1523|@mojaloop/central-services-stream>@mojaloop/central-services-logger>winston>async>lodash": { - "decision": "ignore", - "madeAt": 1594103543708, - "expiresAt": 1596695532690 - }, - "1523|@mojaloop/forensic-logging-client>@mojaloop/central-services-logger>winston>async>lodash": { - "decision": "ignore", - "madeAt": 1594103543708, - "expiresAt": 1596695532690 - }, - "1523|@mojaloop/central-services-database>knex>lodash": { - "decision": "ignore", - "madeAt": 1594103543708, - "expiresAt": 1596695532690 - }, - "1523|@mojaloop/central-services-database>lodash": { - "decision": "ignore", - "madeAt": 1594103543708, - "expiresAt": 1596695532690 - }, - "1523|@mojaloop/central-services-error-handling>lodash": { - "decision": "ignore", - "madeAt": 1594103543708, - "expiresAt": 1596695532690 - }, - "1523|@mojaloop/central-services-health>@mojaloop/central-services-error-handling>lodash": { - "decision": "ignore", - "madeAt": 1594103543708, - "expiresAt": 1596695532690 - }, - "1523|@mojaloop/central-services-health>@mojaloop/central-services-shared>@mojaloop/central-services-error-handling>lodash": { - "decision": "ignore", - "madeAt": 1594103543708, - "expiresAt": 1596695532690 - }, - "1523|@mojaloop/central-services-shared>@mojaloop/central-services-error-handling>lodash": { - "decision": "ignore", - "madeAt": 1594103543708, - "expiresAt": 1596695532690 - }, - "1523|@mojaloop/central-services-health>@mojaloop/central-services-shared>@mojaloop/event-sdk>lodash": { - "decision": "ignore", - "madeAt": 1594103543708, - "expiresAt": 1596695532690 - }, - "1523|@mojaloop/central-services-shared>@mojaloop/event-sdk>lodash": { - "decision": "ignore", - "madeAt": 1594103543708, - "expiresAt": 1596695532690 - }, - "1523|@mojaloop/event-sdk>lodash": { - "decision": "ignore", - "madeAt": 1594103543708, - "expiresAt": 1596695532690 - }, - "1523|@mojaloop/central-services-health>@mojaloop/central-services-shared>lodash": { - "decision": "ignore", - "madeAt": 1594103543708, - "expiresAt": 1596695532690 - }, - "1523|@mojaloop/central-services-health>@mojaloop/central-services-shared>openapi-backend>lodash": { - "decision": "ignore", - "madeAt": 1594103543708, - "expiresAt": 1596695532690 - }, - "1523|@mojaloop/central-services-health>@mojaloop/central-services-shared>openapi-backend>mock-json-schema>lodash": { - "decision": "ignore", - "madeAt": 1594103543708, - "expiresAt": 1596695532690 - }, - "1523|@mojaloop/central-services-shared>openapi-backend>mock-json-schema>lodash": { - "decision": "ignore", - "madeAt": 1594103543708, - "expiresAt": 1596695532690 - }, - "1523|@mojaloop/central-services-shared>lodash": { - "decision": "ignore", - "madeAt": 1594103543708, - "expiresAt": 1596695532690 - }, - "1523|@mojaloop/central-services-shared>openapi-backend>lodash": { - "decision": "ignore", - "madeAt": 1594103543708, - "expiresAt": 1596695532690 - }, - "1523|@mojaloop/central-services-stream>@mojaloop/central-services-error-handling>@mojaloop/sdk-standard-components>request-promise-native>request-promise-core>lodash": { - "decision": "ignore", - "madeAt": 1594103543708, - "expiresAt": 1596695532690 - }, - "1523|@mojaloop/central-services-stream>@mojaloop/central-services-error-handling>lodash": { - "decision": "ignore", - "madeAt": 1594103543708, - "expiresAt": 1596695532690 - }, - "1523|knex>lodash": { - "decision": "ignore", - "madeAt": 1594103543708, - "expiresAt": 1596695532690 - }, - "1523|lodash": { - "decision": "ignore", - "madeAt": 1594103543708, - "expiresAt": 1596695532690 - } - }, - "rules": {}, - "version": 1 -} \ No newline at end of file diff --git a/config/default.json b/config/default.json index 7f502d3ad..52822273e 100644 --- a/config/default.json +++ b/config/default.json @@ -170,6 +170,29 @@ "auto.offset.reset": "earliest" } } + }, + "GET": { + "config": { + "options": { + "mode": 2, + "batchSize": 1, + "pollFrequency": 10, + "recursiveTimeout": 100, + "messageCharset": "utf8", + "messageAsJSON": true, + "sync": true, + "consumeTimeout": 1000 + }, + "rdkafkaConf": { + "client.id": "cl-con-bulk-get", + "group.id": "cl-group-bulk-get", + "metadata.broker.list": "localhost:9092", + "socket.keepalive.enable": true + }, + "topicConf": { + "auto.offset.reset": "earliest" + } + } } }, "TRANSFER": { diff --git a/docker-compose.yml b/docker-compose.yml index b313c5c15..5456a0b19 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -101,10 +101,10 @@ services: links: - mockserver volumes: - - ./docker/wait-for/wait-for-mockserver.sh:/opt/wait-for/wait-for-mockserver.sh + - ./docker/wait-for:/opt/wait-for entrypoint: [ "sh", "-c" ] command: - - /opt/wait-for-mockserver.sh + - /opt/wait-for/wait-for-mockserver.sh environment: [] networks: - cl-mojaloop-net diff --git a/docker/central-ledger/default.json b/docker/central-ledger/default.json index 0536ddb08..199a8b452 100644 --- a/docker/central-ledger/default.json +++ b/docker/central-ledger/default.json @@ -170,6 +170,29 @@ "auto.offset.reset": "earliest" } } + }, + "GET": { + "config": { + "options": { + "mode": 2, + "batchSize": 1, + "pollFrequency": 10, + "recursiveTimeout": 100, + "messageCharset": "utf8", + "messageAsJSON": true, + "sync": true, + "consumeTimeout": 1000 + }, + "rdkafkaConf": { + "client.id": "cl-con-bulk-get", + "group.id": "cl-group-bulk-get", + "metadata.broker.list": "localhost:29092", + "socket.keepalive.enable": true + }, + "topicConf": { + "auto.offset.reset": "earliest" + } + } } }, "TRANSFER": { diff --git a/package-lock.json b/package-lock.json index 6a5a017f6..b47b1c43e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@mojaloop/central-ledger", - "version": "10.5.2", + "version": "11.1.4", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -48,19 +48,19 @@ } }, "@babel/core": { - "version": "7.11.5", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.11.5.tgz", - "integrity": "sha512-fsEANVOcZHzrsV6dMVWqpSeXClq3lNbYrfFGme6DE25FQWe7pyeYpXyx9guqUnpy466JLzZ8z4uwSr2iv60V5Q==", + "version": "7.11.1", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.11.1.tgz", + "integrity": "sha512-XqF7F6FWQdKGGWAzGELL+aCO1p+lRY5Tj5/tbT3St1G8NaH70jhhDIKknIZaDans0OQBG5wRAldROLHSt44BgQ==", "dev": true, "requires": { "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.11.5", + "@babel/generator": "^7.11.0", "@babel/helper-module-transforms": "^7.11.0", "@babel/helpers": "^7.10.4", - "@babel/parser": "^7.11.5", + "@babel/parser": "^7.11.1", "@babel/template": "^7.10.4", - "@babel/traverse": "^7.11.5", - "@babel/types": "^7.11.5", + "@babel/traverse": "^7.11.0", + "@babel/types": "^7.11.0", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.1", @@ -68,7 +68,7 @@ "lodash": "^4.17.19", "resolve": "^1.3.2", "semver": "^5.4.1", - "source-map": "^0.6.1" + "source-map": "^0.5.0" }, "dependencies": { "debug": { @@ -79,32 +79,18 @@ "requires": { "ms": "^2.1.1" } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true } } }, "@babel/generator": { - "version": "7.11.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.5.tgz", - "integrity": "sha512-9UqHWJ4IwRTy4l0o8gq2ef8ws8UPzvtMkVKjTLAiRmza9p9V6Z+OfuNd9fB1j5Q67F+dVJtPC2sZXI8NM9br4g==", + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.0.tgz", + "integrity": "sha512-fEm3Uzw7Mc9Xi//qU20cBKatTfs2aOtKqmvy/Vm7RkJEGFQ4xc9myCfbXxqK//ZS8MR/ciOHw6meGASJuKmDfQ==", "dev": true, "requires": { - "@babel/types": "^7.11.5", + "@babel/types": "^7.11.0", "jsesc": "^2.5.1", - "source-map": "^0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } + "source-map": "^0.5.0" } }, "@babel/helper-function-name": { @@ -229,9 +215,9 @@ } }, "@babel/parser": { - "version": "7.11.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.5.tgz", - "integrity": "sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q==", + "version": "7.11.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.3.tgz", + "integrity": "sha512-REo8xv7+sDxkKvoxEywIdsNFiZLybwdI7hcT5uEPyQrSMB4YQ973BfC9OOrD/81MaIjh6UxdulIQXkjmiH3PcA==", "dev": true }, "@babel/template": { @@ -246,17 +232,17 @@ } }, "@babel/traverse": { - "version": "7.11.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.11.5.tgz", - "integrity": "sha512-EjiPXt+r7LiCZXEfRpSJd+jUMnBd4/9OUv7Nx3+0u9+eimMwJmG0Q98lw4/289JCoxSE8OolDMNZaaF/JZ69WQ==", + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.11.0.tgz", + "integrity": "sha512-ZB2V+LskoWKNpMq6E5UUCrjtDUh5IOTAyIl0dTjIEoXum/iKWkoIEKIRDnUucO6f+2FzNkE0oD4RLKoPIufDtg==", "dev": true, "requires": { "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.11.5", + "@babel/generator": "^7.11.0", "@babel/helper-function-name": "^7.10.4", "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/parser": "^7.11.5", - "@babel/types": "^7.11.5", + "@babel/parser": "^7.11.0", + "@babel/types": "^7.11.0", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.19" @@ -274,9 +260,9 @@ } }, "@babel/types": { - "version": "7.11.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz", - "integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==", + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", + "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", @@ -555,9 +541,9 @@ } }, "@hapi/shot": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/@hapi/shot/-/shot-5.0.3.tgz", - "integrity": "sha512-qbccs8KL4YSL9x0J/17Z6Udmtrrn32ieGbrCW8iivl2ha8YzlDy9Wvv1pFKh3mzbTsomWHGLF3UsKcQFk/BqPg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@hapi/shot/-/shot-5.0.1.tgz", + "integrity": "sha512-AnhIhB0UCBi8JpJ+BEkNs29FQGpp9yrFWDzPO/J9maS11VyDRh5cd1c6di6dX8IpYs/rUYr6uchXZ94OCo1ZDA==", "requires": { "@hapi/hoek": "9.x.x", "@hapi/validate": "1.x.x" @@ -937,9 +923,9 @@ } }, "@mojaloop/central-services-shared": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/@mojaloop/central-services-shared/-/central-services-shared-11.2.0.tgz", - "integrity": "sha512-0x52CrQurhV1kX7ODT8WR8j1JwlGyvNZh9EPL7gSSAxRqNDlJym6rOloLKdoWkyD1BttFDRudKtZgKAhiBcR2w==", + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/@mojaloop/central-services-shared/-/central-services-shared-11.3.0.tgz", + "integrity": "sha512-DkZYoCe3Kla6JW2KoR+14Jt0f2uBQSoci3jCnMHZPol0z8MfOzfwoai9FEX2VETEpuDzD/zL1grQecrMFjIHvg==", "requires": { "@hapi/catbox": "11.1.1", "@hapi/catbox-memory": "5.0.0", @@ -953,10 +939,13 @@ "base64url": "3.0.1", "clone": "2.1.2", "data-urls": "2.0.0", + "dotenv": "8.2.0", + "env-var": "6.3.0", "immutable": "3.8.2", "lodash": "4.17.20", "mustache": "4.0.1", "openapi-backend": "3.5.2", + "parse-strings-in-object": "1.3.2", "raw-body": "2.4.1", "uuid4": "2.0.2" }, @@ -1005,6 +994,11 @@ "qs": "^6.9.3", "swagger-parser": "^9.0.1" } + }, + "parse-strings-in-object": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/parse-strings-in-object/-/parse-strings-in-object-1.3.2.tgz", + "integrity": "sha512-3teTMOr3r9W8+Pu11XhCRIu3MUyfqVFW8il4wCRH8bd0MVh/2ssf0rRiQXk6YpFVfDdtm22JQPTRdNOAZvOeQw==" } } }, @@ -1186,9 +1180,9 @@ "dev": true }, "@npmcli/git": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-2.0.3.tgz", - "integrity": "sha512-c/ODsV5ppjB12VDXKc6hzVNgg6ZJX/etILUn3WgF5NLAYBhQLJ3fBq6uB2jQD4OwqOzJdPT1/xA3Xh3aaWGk5w==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-2.0.4.tgz", + "integrity": "sha512-OJZCmJ9DNn1cz9HPXXsPmUBnqaArot3CGYo63CyajHQk+g87rPXVOJByGsskQJhPsUUEXJcsZ2Q6bWd2jSwnBA==", "dev": true, "requires": { "@npmcli/promise-spawn": "^1.1.0", @@ -1481,9 +1475,9 @@ } }, "aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.0.1.tgz", + "integrity": "sha512-quoaXsZ9/BLNae5yiNoUz+Nhkwz83GhWwtYFglcjEQB2NDHCIpApbqXxIFnm4Pq/Nvhrsq5sYJFyohrrxnTGAA==", "dev": true, "requires": { "clean-stack": "^2.0.0", @@ -2500,9 +2494,9 @@ "dev": true }, "cli-boxes": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", - "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.0.tgz", + "integrity": "sha512-gpaBrMAizVEANOpfZp/EEUixTXDyGt7DFzdK5hU+UbWt/J0lB0w20ncZj59Z9a93xHb9u12zF5BS6i9RKbtg4w==", "dev": true }, "cli-cursor": { @@ -3069,6 +3063,11 @@ "is-obj": "^2.0.0" } }, + "dotenv": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", + "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==" + }, "dotignore": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/dotignore/-/dotignore-0.1.2.tgz", @@ -3198,6 +3197,11 @@ "integrity": "sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA==", "dev": true }, + "env-var": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/env-var/-/env-var-6.3.0.tgz", + "integrity": "sha512-gaNzDZuVaJQJlP2SigAZLu/FieZN5MzdN7lgHNehESwlRanHwGQ/WUtJ7q//dhrj3aGBZM45yEaKOuvSJaf4mA==" + }, "env-variable": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/env-variable/-/env-variable-0.0.6.tgz", @@ -3726,20 +3730,12 @@ } }, "esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", "dev": true, "requires": { - "estraverse": "^5.2.0" - }, - "dependencies": { - "estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true - } + "estraverse": "^4.1.0" } }, "estraverse": { @@ -4644,9 +4640,9 @@ } }, "hapi-swagger": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/hapi-swagger/-/hapi-swagger-14.0.0.tgz", - "integrity": "sha512-ppMlFdUK8g3IMlre2Fd932ngnB6bBNzSLPFzjvbFBiC6i7sJvvvqWA2FdYDW/u3on9JvziikQFxTj/9zxvE/+A==", + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/hapi-swagger/-/hapi-swagger-13.1.0.tgz", + "integrity": "sha512-WzhMk4BcaC36poqLpBGITGiyytGjVYXqwU3DdECJGKZ5AVfAqaJuhlWQg5MkmZ0Be1JglUzOYPTPSvqqN9XHBw==", "requires": { "@hapi/boom": "^9.1.0", "@hapi/hoek": "^9.0.2", @@ -5709,9 +5705,9 @@ "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" }, "json-parse-even-better-errors": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.0.tgz", - "integrity": "sha512-o3aP+RsWDJZayj1SbHNQAI8x0v3T3SKiGoZlNYfbUP1S3omJQ6i9CnqADqkSPaOAxwua4/1YWx5CM7oiChJt2Q==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true }, "json-parse-helpfulerror": { @@ -6955,9 +6951,9 @@ } }, "npm-check-updates": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-8.0.2.tgz", - "integrity": "sha512-YsJSmVPXWzBRSbzuVkMRV4M7tNtR3ptMQh0ZYzE3QXdZ8pxAYqrfvE8bJeZYcdsSBSRIJ6TZTmF6uS4rP/xaVQ==", + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-8.0.5.tgz", + "integrity": "sha512-EZ3DIFvKFNZzwV9OdEYITTAY6fmuD7evItEmf9XyntG4p29n8JpFaGrf9uCQIR0Hx3G5RFNsdCeURHzF6YXIAw==", "dev": true, "requires": { "chalk": "^4.1.0", @@ -9540,9 +9536,9 @@ "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" }, "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", "requires": { "has-flag": "^4.0.0" } @@ -9566,9 +9562,9 @@ "integrity": "sha1-cAcEaNbSl3ylI3suUZyn0Gouo/0=" }, "swagger-ui-dist": { - "version": "3.32.5", - "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-3.32.5.tgz", - "integrity": "sha512-3SKHv8UVqsKKknivtACHbFDGcn297jkoZN2h6zAZ7b2yoaJNMaRadQpC3qFw3GobZTGzqHCgHph4ZH9NkaCjrQ==" + "version": "3.32.1", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-3.32.1.tgz", + "integrity": "sha512-CaIKxDo91McgoesukS5v3nwQ8iur0MQmwNvJ+bPeyd8sOtoiNyXp55ZjO4pXewBdFHD0f9PvGovf2m5x/1typA==" }, "table": { "version": "5.4.6", @@ -10089,9 +10085,9 @@ "dev": true }, "uglify-js": { - "version": "3.10.3", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.10.3.tgz", - "integrity": "sha512-Lh00i69Uf6G74mvYpHCI9KVVXLcHW/xu79YTvH7Mkc9zyKUeSPz0owW0dguj0Scavns3ZOh3wY63J0Zb97Za2g==", + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.10.1.tgz", + "integrity": "sha512-RjxApKkrPJB6kjJxQS3iZlf///REXWYxYJxO/MpmlQzVkDWVI3PSnCBWezMecmTU/TRkNxrl8bmsfFQCp+LO+Q==", "optional": true }, "unc-path-regex": { @@ -10275,9 +10271,9 @@ } }, "uri-js": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz", - "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", "requires": { "punycode": "^2.1.0" } @@ -10374,9 +10370,9 @@ } }, "webidl-conversions": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", - "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==" + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", + "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==" }, "whatwg-mimetype": { "version": "2.3.0", @@ -10384,13 +10380,13 @@ "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==" }, "whatwg-url": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.2.1.tgz", - "integrity": "sha512-ZmVCr6nfBeaMxEHALLEGy0LszYjpJqf6PVNQUQ1qd9Et+q7Jpygd4rGGDXgHjD8e99yLFseD69msHDM4YwPZ4A==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.1.0.tgz", + "integrity": "sha512-vEIkwNi9Hqt4TV9RdnaBPNt+E2Sgmo3gePebCRgZ1R7g6d23+53zCTnuB0amKI4AXq6VM8jj2DUAa0S1vjJxkw==", "requires": { "lodash.sortby": "^4.7.0", "tr46": "^2.0.2", - "webidl-conversions": "^6.1.0" + "webidl-conversions": "^5.0.0" } }, "which": { diff --git a/package.json b/package.json index a5ab0659d..35013effc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@mojaloop/central-ledger", - "version": "10.5.2", + "version": "11.1.4", "description": "Central ledger hosted by a scheme to record and settle transfers", "license": "Apache-2.0", "author": "ModusBox", @@ -80,7 +80,7 @@ "@hapi/good": "9.0.0", "@hapi/hapi": "20.0.0", "@hapi/inert": "6.0.2", - "@hapi/joi": "^17.1.1", + "@hapi/joi": "17.1.1", "@hapi/vision": "6.0.1", "@mojaloop/central-object-store": "11.0.0-snapshot", "@mojaloop/central-services-database": "10.6.0", @@ -88,7 +88,7 @@ "@mojaloop/central-services-health": "10.6.0", "@mojaloop/central-services-logger": "10.6.0", "@mojaloop/central-services-metrics": "9.5.0", - "@mojaloop/central-services-shared": "11.2.0", + "@mojaloop/central-services-shared": "11.3.0", "@mojaloop/central-services-stream": "10.6.0", "@mojaloop/event-sdk": "10.6.0", "@mojaloop/forensic-logging-client": "8.3.0", @@ -106,7 +106,7 @@ "glob": "7.1.6", "hapi-auth-basic": "5.0.0", "hapi-auth-bearer-token": "6.2.1", - "hapi-swagger": "14.0.0", + "hapi-swagger": "13.1.0", "ilp-packet": "2.2.0", "joi": "^17.2.1", "knex": "0.21.5", @@ -129,7 +129,7 @@ "jsonpath": "1.0.2", "nodemon": "2.0.4", "npm-audit-resolver": "2.2.1", - "npm-check-updates": "8.0.2", + "npm-check-updates": "8.0.5", "nyc": "15.1.0", "pre-commit": "1.2.2", "proxyquire": "2.1.3", diff --git a/seeds/endpointType.js b/seeds/endpointType.js index 6c49ee401..ceb79bce6 100644 --- a/seeds/endpointType.js +++ b/seeds/endpointType.js @@ -138,6 +138,10 @@ const endpointTypes = [ name: 'FSPIOP_CALLBACK_URL_TRX_REQ_SERVICE', description: 'Participant callback URL to which transaction requests can be sent' }, + { + name: 'FSPIOP_CALLBACK_URL_BULK_QUOTES', + description: 'Bulk Quotes callback URL to which put bulkQuotes requests can be sent' + }, { name: 'TP_CB_URL_TRANSACTION_REQUEST_POST', description: 'Participant callback URL where POST /thirdpartyRequests/transactions can be sent' diff --git a/src/api/interface/swagger.json b/src/api/interface/swagger.json index 88e82a4b4..85e772b67 100644 --- a/src/api/interface/swagger.json +++ b/src/api/interface/swagger.json @@ -7,7 +7,7 @@ ], "info": { "title": "Central Ledger API Documentation", - "version": "10.3.0" + "version": "1.0" }, "tags": [], "paths": { diff --git a/src/domain/bulkTransfer/index.js b/src/domain/bulkTransfer/index.js index cf713e3d7..d1a17c641 100644 --- a/src/domain/bulkTransfer/index.js +++ b/src/domain/bulkTransfer/index.js @@ -24,7 +24,7 @@ 'use strict' /** - * @module src/domain/transfer/ + * @module src/domain/bulkTransfer/ */ const Enum = require('@mojaloop/central-services-shared').Enum @@ -54,7 +54,7 @@ const getBulkTransferById = async (id) => { } if (transfer.errorCode) { result.errorInformation = { - errorCode: transfer.errorCode, + errorCode: transfer.errorCode.toString(), errorDescription: transfer.errorDescription } } else { @@ -124,7 +124,7 @@ const getBulkTransferById = async (id) => { payerFsp: bulkTransfer.payerFsp, payeeFsp: bulkTransfer.payeeFsp, expiration: bulkTransfer.expirationDate, - completedDate: bulkTransfer.completedDate, + completedDate: bulkTransfer.completedTimestamp, payerBulkTransfer, payeeBulkTransfer } diff --git a/src/handlers/bulk/get/handler.js b/src/handlers/bulk/get/handler.js new file mode 100644 index 000000000..efae773a8 --- /dev/null +++ b/src/handlers/bulk/get/handler.js @@ -0,0 +1,203 @@ +/***** + License + -------------- + Copyright © 2017 Bill & Melinda Gates Foundation + The Mojaloop files are made available by the Bill & Melinda Gates Foundation under the Apache License, Version 2.0 (the "License") and you may not use these files except in compliance with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, the Mojaloop files are distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + + Contributors + -------------- + This is the official list of the Mojaloop project contributors for this file. + Names of the original copyright holders (individuals or organizations) + should be listed with a '*' in the first column. People who have + contributed from an organization can be listed under the organization + that actually holds the copyright for their contributions (see the + Gates Foundation organization for an example). Those individuals should have + their names indented and be marked with a '-'. Email address can be added + optionally within square brackets . + + * Gates Foundation + - Name Surname + + * ModusBox + - Steven Oderayi + -------------- + ******/ +'use strict' + +const Logger = require('@mojaloop/central-services-logger') +const EventSdk = require('@mojaloop/event-sdk') +const Util = require('@mojaloop/central-services-shared').Util +const Kafka = require('@mojaloop/central-services-shared').Util.Kafka +const Producer = require('@mojaloop/central-services-stream').Util.Producer +const Consumer = require('@mojaloop/central-services-stream').Util.Consumer +const Enum = require('@mojaloop/central-services-shared').Enum +const Metrics = require('@mojaloop/central-services-metrics') +const ErrorHandler = require('@mojaloop/central-services-error-handling') +const BulkTransferService = require('../../../domain/bulkTransfer') +const BulkTransferModel = require('../../../models/bulkTransfer/bulkTransfer') +const Validator = require('../shared/validator') +const Config = require('../../../lib/config') +const { ERROR_HANDLING } = require('../../../lib/config') + +const location = { module: 'BulkGetHandler', method: '', path: '' } +const consumerCommit = true +const fromSwitch = true + +/** + * @function BulkGetHandler + * + * @async + * @description Gets a bulk transfer by id. Gets Kafka config from default.json + * + * Calls createHandler to register the handler against the Stream Processing API. + * + * @param {error} error - error thrown if something fails within Kafka + * @param {array} messages - a list of messages to consume for the relevant topic + * + * @returns {object} - Returns a boolean: true if successful, or throws an error if failed + */ +const getBulkTransfer = async (error, messages) => { + const histTimerEnd = Metrics.getHistogram( + 'bulk_transfer_get', + 'Consume a get bulk transfer message from the kafka topic and process it accordingly', + ['success', 'fspId'] + ).startTimer() + if (error) { + throw ErrorHandler.Factory.reformatFSPIOPError(error) + } + const message = Array.isArray(messages) ? messages[0] : messages + const contextFromMessage = EventSdk.Tracer.extractContextFromMessage(message.value) + const span = EventSdk.Tracer.createChildSpanFromContext('cl_bulk_transfer_get', contextFromMessage) + try { + await span.audit(message, EventSdk.AuditEventAction.start) + const metadata = message.value.metadata + const action = metadata.event.action + const bulkTransferId = message.value.content.uriParams.id + const kafkaTopic = message.topic + Logger.isInfoEnabled && Logger.info(Util.breadcrumb(location, { method: `getBulkTransfer:${action}` })) + + const actionLetter = Enum.Events.ActionLetter.get + const params = { message, kafkaTopic, span, consumer: Consumer, producer: Producer } + const eventDetail = { functionality: Enum.Events.Event.Type.NOTIFICATION, action: Enum.Events.Event.Action.BULK_GET } + + Util.breadcrumb(location, { path: 'validationFailed' }) + + if (!(await Validator.validateParticipantByName(message.value.from)).isValid) { + Logger.isInfoEnabled && Logger.info(Util.breadcrumb(location, `breakParticipantDoesntExist--${actionLetter}1`)) + await Kafka.proceed(Config.KAFKA_CONFIG, params, { consumerCommit, histTimerEnd }) + histTimerEnd({ success: true, fspId: Config.INSTRUMENTATION_METRICS_LABELS.fspId }) + return true + } + // TODO: Validate this. Is this sufficient for checking existence of bulk transfer? + const bulkTransferLight = await BulkTransferModel.getById(bulkTransferId) + if (!bulkTransferLight) { + Logger.isInfoEnabled && Logger.info(Util.breadcrumb(location, `callbackErrorBulkTransferNotFound--${actionLetter}3`)) + const fspiopError = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.BULK_TRANSFER_ID_NOT_FOUND, 'Provided Bulk Transfer ID was not found on the server.') + await Kafka.proceed(Config.KAFKA_CONFIG, params, { consumerCommit, fspiopError: fspiopError.toApiErrorObject(Config.ERROR_HANDLING), eventDetail, fromSwitch }) + throw fspiopError + } + // The SD says this should be 404 response which I think will not be constent with single transfers + // which responds with CLIENT_ERROR instead + const participants = await BulkTransferService.getParticipantsById(bulkTransferId) + if (![participants.payeeFsp, participants.payerFsp].includes(message.value.from)) { + Logger.isInfoEnabled && Logger.info(Util.breadcrumb(location, `callbackErrorNotBulkTransferParticipant--${actionLetter}2`)) + const fspiopError = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.CLIENT_ERROR) + await Kafka.proceed(Config.KAFKA_CONFIG, params, { consumerCommit, fspiopError: fspiopError.toApiErrorObject(Config.ERROR_HANDLING), eventDetail, fromSwitch }) + throw fspiopError + } + const isPayeeRequest = participants.payeeFsp === message.value.from + Util.breadcrumb(location, { path: 'validationPassed' }) + Logger.isInfoEnabled && Logger.info(Util.breadcrumb(location, `callbackMessage--${actionLetter}4`)) + const bulkTransferResult = await BulkTransferService.getBulkTransferById(bulkTransferId) + const bulkTransfer = isPayeeRequest ? bulkTransferResult.payeeBulkTransfer : bulkTransferResult.payerBulkTransfer + let payload = { + bulkTransferState: bulkTransfer.bulkTransferState + } + let fspiopError + if (bulkTransfer.bulkTransferState === Enum.Transfers.BulkTransferState.REJECTED) { + payload = { + errorInformation: bulkTransfer.individualTransferResults[0].errorInformation + } + fspiopError = ErrorHandler.Factory.createFSPIOPErrorFromErrorInformation(payload.errorInformation) + } else if (bulkTransfer.bulkTransferState !== Enum.Transfers.BulkTransferState.PROCESSING) { + payload = { + ...payload, + completedTimestamp: bulkTransfer.completedTimestamp, + individualTransferResults: bulkTransfer.individualTransferResults, + extensionList: bulkTransfer.extensionList + } + } + message.value.content.payload = payload + if (fspiopError) { + await Kafka.proceed(Config.KAFKA_CONFIG, params, { consumerCommit, fspiopError: fspiopError.toApiErrorObject(ERROR_HANDLING), eventDetail, fromSwitch }) + } else { + await Kafka.proceed(Config.KAFKA_CONFIG, params, { consumerCommit, eventDetail, fromSwitch }) + } + histTimerEnd({ success: true, fspId: Config.INSTRUMENTATION_METRICS_LABELS.fspId }) + return true + } catch (err) { + histTimerEnd({ success: false, fspId: Config.INSTRUMENTATION_METRICS_LABELS.fspId }) + const fspiopError = ErrorHandler.Factory.reformatFSPIOPError(err) + Logger.isErrorEnabled && Logger.error(`${Util.breadcrumb(location)}::${err.message}--G0`) + const state = new EventSdk.EventStateMetadata(EventSdk.EventStatusType.failed, fspiopError.apiErrorCode.code, fspiopError.apiErrorCode.message) + await span.error(fspiopError, state) + await span.finish(fspiopError.message, state) + return true + } finally { + if (!span.isFinished) { + await span.finish() + } + } +} + +/** + * @function registerBulkFulfilHandler + * + * @async + * @description Registers the handler for bulk-transfer topic. Gets Kafka config from default.json + * + * @returns {boolean} - Returns a boolean: true if successful, or throws and error if failed + */ +const registerGetBulkTransferHandler = async () => { + try { + const bulkGetHandler = { + command: getBulkTransfer, + topicName: Kafka.transformGeneralTopicName(Config.KAFKA_CONFIG.TOPIC_TEMPLATES.GENERAL_TOPIC_TEMPLATE.TEMPLATE, Enum.Events.Event.Type.BULK, Enum.Events.Event.Action.GET), + config: Kafka.getKafkaConfig(Config.KAFKA_CONFIG, Enum.Kafka.Config.CONSUMER, Enum.Events.Event.Type.BULK.toUpperCase(), Enum.Events.Event.Action.GET.toUpperCase()) + } + bulkGetHandler.config.rdkafkaConf['client.id'] = bulkGetHandler.topicName + await Consumer.createHandler(bulkGetHandler.topicName, bulkGetHandler.config, bulkGetHandler.command) + return true + } catch (err) { + Logger.isErrorEnabled && Logger.error(err) + throw ErrorHandler.Factory.reformatFSPIOPError(err) + } +} + +/** + * @function RegisterAllHandlers + * + * @async + * @description Registers all module handlers + * + * @returns {boolean} - Returns a boolean: true if successful, or throws and error if failed + */ +const registerAllHandlers = async () => { + try { + await registerGetBulkTransferHandler() + return true + } catch (err) { + Logger.isErrorEnabled && Logger.error(err) + throw ErrorHandler.Factory.reformatFSPIOPError(err) + } +} + +module.exports = { + getBulkTransfer, + registerGetBulkTransferHandler, + registerAllHandlers +} diff --git a/src/handlers/bulk/index.js b/src/handlers/bulk/index.js index f8d28a3e8..6f129ceb0 100644 --- a/src/handlers/bulk/index.js +++ b/src/handlers/bulk/index.js @@ -34,6 +34,7 @@ const BulkPrepareHandlers = require('./prepare/handler') const BulkFulfilHandlers = require('./fulfil/handler') const BulkProcessingHandlers = require('./processing/handler') +const GetBulkTransferHandlers = require('./get/handler') const Logger = require('@mojaloop/central-services-logger') /** @@ -49,6 +50,7 @@ const registerAllHandlers = async () => { await BulkPrepareHandlers.registerAllHandlers() await BulkFulfilHandlers.registerAllHandlers() await BulkProcessingHandlers.registerAllHandlers() + await GetBulkTransferHandlers.registerAllHandlers() return true } catch (err) { Logger.isErrorEnabled && Logger.error(err) @@ -60,5 +62,6 @@ module.exports = { registerBulkPrepareHandler: BulkPrepareHandlers.registerBulkPrepareHandler, registerBulkFulfilHandler: BulkFulfilHandlers.registerBulkFulfilHandler, registerBulkProcessingHandler: BulkProcessingHandlers.registerBulkProcessingHandler, + registerGetBulkTransferHandler: GetBulkTransferHandlers.registerGetBulkTransferHandler, registerAllHandlers } diff --git a/src/handlers/bulk/processing/handler.js b/src/handlers/bulk/processing/handler.js index 80542d550..feffbe750 100644 --- a/src/handlers/bulk/processing/handler.js +++ b/src/handlers/bulk/processing/handler.js @@ -300,21 +300,30 @@ const bulkProcessing = async (error, messages) => { await (new BulkTransferResultModel(payeeBulkResponse)).save() const payerParams = Util.clone(params) const payeeParams = Util.clone(params) + let payerPayload + let payeePayload + + if (action === Enum.Events.Event.Action.BULK_ABORT && params.decodedPayload.errorInformation) { + payerPayload = { bulkTransferId: payerBulkResponse.bulkTransferId, errorInformation: params.decodedPayload.errorInformation } + payeePayload = { bulkTransferId: payeeBulkResponse.bulkTransferId, errorInformation: params.decodedPayload.errorInformation } + } else { + payerPayload = Util.omitNil({ + bulkTransferId: payerBulkResponse.bulkTransferId, + bulkTransferState: payerBulkResponse.bulkTransferState, + completedTimestamp: payerBulkResponse.completedTimestamp, + extensionList: payerBulkResponse.extensionList + }) + payeePayload = Util.omitNil({ + bulkTransferId: payeeBulkResponse.bulkTransferId, + bulkTransferState: payeeBulkResponse.bulkTransferState, + completedTimestamp: payeeBulkResponse.completedTimestamp, + extensionList: payeeBulkResponse.extensionList + }) + } - const payerPayload = Util.omitNil({ - bulkTransferId: payerBulkResponse.bulkTransferId, - bulkTransferState: payerBulkResponse.bulkTransferState, - completedTimestamp: payerBulkResponse.completedTimestamp, - extensionList: payerBulkResponse.extensionList - }) const payerMetadata = Util.StreamingProtocol.createMetadataWithCorrelatedEvent(params.message.value.metadata.event.id, payerParams.message.value.metadata.type, payerParams.message.value.metadata.action, Enum.Events.EventStatus.SUCCESS) payerParams.message.value = Util.StreamingProtocol.createMessage(params.message.value.id, participants.payerFsp, payerBulkResponse.headers[normalizedKeys[Enum.Http.Headers.FSPIOP.SOURCE]], payerMetadata, payerBulkResponse.headers, payerPayload) - const payeePayload = Util.omitNil({ - bulkTransferId: payeeBulkResponse.bulkTransferId, - bulkTransferState: payeeBulkResponse.bulkTransferState, - completedTimestamp: payeeBulkResponse.completedTimestamp, - extensionList: payeeBulkResponse.extensionList - }) + const payeeMetadata = Util.StreamingProtocol.createMetadataWithCorrelatedEvent(params.message.value.metadata.event.id, payeeParams.message.value.metadata.type, payeeParams.message.value.metadata.action, Enum.Events.EventStatus.SUCCESS) payeeParams.message.value = Util.StreamingProtocol.createMessage(params.message.value.id, participants.payeeFsp, Enum.Http.Headers.FSPIOP.SWITCH.value, payeeMetadata, payeeBulkResponse.headers, payeePayload) if ([Enum.Events.Event.Action.BULK_TIMEOUT_RECEIVED, Enum.Events.Event.Action.BULK_TIMEOUT_RESERVED].includes(action)) { diff --git a/src/handlers/bulk/shared/validator.js b/src/handlers/bulk/shared/validator.js index e784dd64a..6b55a049e 100644 --- a/src/handlers/bulk/shared/validator.js +++ b/src/handlers/bulk/shared/validator.js @@ -25,6 +25,7 @@ * ModusBox - Georgi Georgiev - Rajiv Mothilal + - Steven Oderayi -------------- ******/ 'use strict' @@ -130,7 +131,18 @@ const validateBulkTransferFulfilment = async (payload, headers) => { return { isValid, reasons } } +const validateParticipantBulkTransferId = async function (participantName, bulkTransferId) { + const bulkTransferParticipant = await BulkTransferService.getBulkTransferParticipant(participantName, bulkTransferId) + let validationPassed = false + if (Array.isArray(bulkTransferParticipant) && bulkTransferParticipant.length > 0) { + validationPassed = true + } + return validationPassed +} + module.exports = { validateBulkTransfer, - validateBulkTransferFulfilment + validateBulkTransferFulfilment, + validateParticipantByName, + validateParticipantBulkTransferId } diff --git a/src/handlers/index.js b/src/handlers/index.js index 2d3d547d5..b1b281730 100644 --- a/src/handlers/index.js +++ b/src/handlers/index.js @@ -59,6 +59,7 @@ Program.command('handler') // sub-command name, coffeeType = type, required .option('--bulkprepare', 'Start the Bulk Prepare Handler') .option('--bulkfulfil', 'Start the Bulk Fulfil Handler') .option('--bulkprocessing', 'Start the Bulk Processing Handler') + .option('--bulkget', 'Start the Bulk Get Handler') // .option('--reject', 'Start the Reject Handler') // function to execute when command is uses @@ -136,6 +137,14 @@ Program.command('handler') // sub-command name, coffeeType = type, required } handlerList.push(handler) } + if (args.bulkget) { + Logger.isDebugEnabled && Logger.debug('CLI: Executing --bulkget') + const handler = { + type: 'bulkget', + enabled: true + } + handlerList.push(handler) + } module.exports = Setup.initialize({ service: 'handler', diff --git a/src/handlers/register.js b/src/handlers/register.js index d1d29a723..e4415e01a 100644 --- a/src/handlers/register.js +++ b/src/handlers/register.js @@ -101,6 +101,7 @@ module.exports = { bulk: { registerBulkPrepareHandler: BulkHandlers.registerBulkPrepareHandler, registerBulkFulfilHandler: BulkHandlers.registerBulkFulfilHandler, - registerBulkProcessingHandler: BulkHandlers.registerBulkProcessingHandler + registerBulkProcessingHandler: BulkHandlers.registerBulkProcessingHandler, + registerBulkGetHandler: BulkHandlers.registerGetBulkTransferHandler } } diff --git a/src/handlers/transfers/handler.js b/src/handlers/transfers/handler.js index 682073485..c350b2586 100644 --- a/src/handlers/transfers/handler.js +++ b/src/handlers/transfers/handler.js @@ -292,6 +292,18 @@ const fulfil = async (error, messages) => { const params = { message, kafkaTopic, decodedPayload: payload, span, consumer: Consumer, producer: Producer } Logger.isInfoEnabled && Logger.info(Util.breadcrumb(location, { path: 'getById' })) + + // We fail early and silently to allow timeout handler abort transfer + // if 'RESERVED' transfer state is sent in with v1.0 content-type + if (headers['content-type'].split('=')[1] === '1.0' && payload.transferState === TransferState.RESERVED) { + Logger.isInfoEnabled && Logger.info(Util.breadcrumb(location, `failSilentlyforReservedStateWith1.0ContentType--${actionLetter}0`)) + const errorMessage = 'action "RESERVE" is not allowed in fulfil handler for v1.0 clients.' + Logger.isErrorEnabled && Logger.error(errorMessage) + !!span && span.error(errorMessage) + histTimerEnd({ success: true, fspId: Config.INSTRUMENTATION_METRICS_LABELS.fspId }) + return true + } + const transfer = await TransferService.getById(transferId) const transferStateEnum = transfer && transfer.transferStateEnumeration diff --git a/src/shared/setup.js b/src/shared/setup.js index 4e7e26b8b..cafc77e37 100644 --- a/src/shared/setup.js +++ b/src/shared/setup.js @@ -186,6 +186,10 @@ const createHandlers = async (handlers) => { await RegisterHandlers.bulk.registerBulkProcessingHandler() break } + case 'bulkget': { + await RegisterHandlers.bulk.registerBulkGetHandler() + break + } default: { const error = `Handler Setup - ${JSON.stringify(handler)} is not a valid handler to register!` Logger.isErrorEnabled && Logger.error(error) diff --git a/test/integration/handlers/handlers.test.js b/test/integration/handlers/handlers.test.js index dba70e02c..649ceef9d 100644 --- a/test/integration/handlers/handlers.test.js +++ b/test/integration/handlers/handlers.test.js @@ -143,11 +143,13 @@ const prepareTestData = async (dataObj) => { const prepareHeaders = { 'fspiop-source': payer.participant.name, - 'fspiop-destination': payee.participant.name + 'fspiop-destination': payee.participant.name, + 'content-type': 'application/vnd.interoperability.transfers+json;version=1.1' } const fulfilAbortRejectHeaders = { 'fspiop-source': payee.participant.name, - 'fspiop-destination': payer.participant.name + 'fspiop-destination': payer.participant.name, + 'content-type': 'application/vnd.interoperability.transfers+json;version=1.1' } const fulfilPayload = { diff --git a/test/unit/handlers/bulk/get/handler.test.js b/test/unit/handlers/bulk/get/handler.test.js new file mode 100644 index 000000000..dfab7abfb --- /dev/null +++ b/test/unit/handlers/bulk/get/handler.test.js @@ -0,0 +1,401 @@ +/***** + License + -------------- + Copyright © 2017 Bill & Melinda Gates Foundation + The Mojaloop files are made available by the Bill & Melinda Gates Foundation under the Apache License, Version 2.0 (the "License") and you may not use these files except in compliance with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, the Mojaloop files are distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + + Contributors + -------------- + This is the official list of the Mojaloop project contributors for this file. + Names of the original copyright holders (individuals or organizations) + should be listed with a '*' in the first column. People who have + contributed from an organization can be listed under the organization + that actually holds the copyright for their contributions (see the + Gates Foundation organization for an example). Those individuals should have + their names indented and be marked with a '-'. Email address can be added + optionally within square brackets . + + * Gates Foundation + - Name Surname + + * Steven Oderayi + -------------- + ******/ +'use strict' + +const Uuid = require('uuid4') +const Sinon = require('sinon') +const Proxyquire = require('proxyquire') +const Test = require('tapes')(require('tape')) +const EventSdk = require('@mojaloop/event-sdk') +const Kafka = require('@mojaloop/central-services-shared').Util.Kafka +const MainUtil = require('@mojaloop/central-services-shared').Util +const KafkaConsumer = require('@mojaloop/central-services-stream').Kafka.Consumer +const Consumer = require('@mojaloop/central-services-stream').Util.Consumer +const Enum = require('@mojaloop/central-services-shared').Enum +const Comparators = require('@mojaloop/central-services-shared').Util.Comparators +const Validator = require('../../../../../src/handlers/bulk/shared/validator') +const BulkTransferService = require('../../../../../src/domain/bulkTransfer') +const BulkTransferModel = require('../../../../../src/models/bulkTransfer/bulkTransfer') +const ilp = require('../../../../../src/models/transfer/ilpPacket') +// const TransferState = Enum.Transfers.TransferState +// const TransferInternalState = Enum.Transfers.TransferInternalState + +const bulkTransfer = { + bulkTransferId: 'fake-bulk-transfer-id', + bulkQuoteId: 'fake-bulk-quote-id', + payerFsp: 'dfsp1', + payeeFsp: 'dfsp2', + expiration: '2016-05-24T08:38:08.699-04:00', + individualTransfers: [ + { + transferId: 'b51ec534-ee48-4575-b6a9-ead2955b8999', + transferAmount: { + currency: 'USD', + amount: '433.88' + }, + ilpPacket: 'AYIBgQAAAAAAAASwNGxldmVsb25lLmRmc3AxLm1lci45T2RTOF81MDdqUUZERmZlakgyOVc4bXFmNEpLMHlGTFGCAUBQU0svMS4wCk5vbmNlOiB1SXlweUYzY3pYSXBFdzVVc05TYWh3CkVuY3J5cHRpb246IG5vbmUKUGF5bWVudC1JZDogMTMyMzZhM2ItOGZhOC00MTYzLTg0NDctNGMzZWQzZGE5OGE3CgpDb250ZW50LUxlbmd0aDogMTM1CkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vanNvbgpTZW5kZXItSWRlbnRpZmllcjogOTI4MDYzOTEKCiJ7XCJmZWVcIjowLFwidHJhbnNmZXJDb2RlXCI6XCJpbnZvaWNlXCIsXCJkZWJpdE5hbWVcIjpcImFsaWNlIGNvb3BlclwiLFwiY3JlZGl0TmFtZVwiOlwibWVyIGNoYW50XCIsXCJkZWJpdElkZW50aWZpZXJcIjpcIjkyODA2MzkxXCJ9IgA', + condition: 'YlK5TZyhflbXaDRPtR5zhCu8FrbgvrQwwmzuH0iQ0AI' + } + ], + extensionList: { + extension: [ + { + key: 'key1', + value: 'value1' + }, + { + key: 'key2', + value: 'value2' + } + ] + } +} + +const bulkTransferReturn = { + bulkTransferId: bulkTransfer.bulkTransferId, + bulkTransferStateId: 'COMPLETED', + completedTimestamp: new Date().toISOString(), + payerFsp: 'payerfsp', + payeeFsp: 'payeefsp', + bulkQuoteId: bulkTransfer.bulkQuoteId, + expirationDate: new Date().toISOString() +} + +const messageProtocol = { + id: Uuid(), + from: bulkTransfer.payerFsp, + to: bulkTransfer.payeeFsp, + type: 'application/json', + content: { + headers: { + 'fspiop-source': bulkTransfer.payeeFsp, + 'fspiop-destination': 'source' + }, + uriParams: { id: bulkTransfer.bulkTransferId } + }, + metadata: { + event: { + id: Uuid(), + type: Enum.Events.Event.Type.BULK, + action: Enum.Events.Event.Action.GET, + createdAt: new Date(), + state: { + status: 'success', + code: 0 + } + } + }, + pp: '' +} + +const topicName = 'topic-test' + +const messages = [ + { + topic: topicName, + value: messageProtocol + } +] + +const config = { + options: { + mode: 2, + batchSize: 1, + pollFrequency: 10, + recursiveTimeout: 100, + messageCharset: 'utf8', + messageAsJSON: true, + sync: true, + consumeTimeout: 1000 + }, + rdkafkaConf: { + 'client.id': 'kafka-test', + debug: 'all', + 'group.id': 'central-ledger-kafka', + 'metadata.broker.list': 'localhost:9092', + 'enable.auto.commit': false + } +} + +const command = () => { } + +let SpanStub +let allBulkTransferHandlers + +Test('Bulk Transfer GET handler', getHandlerTest => { + let sandbox + + getHandlerTest.beforeEach(test => { + sandbox = Sinon.createSandbox() + SpanStub = { + audit: sandbox.stub().callsFake(), + error: sandbox.stub().callsFake(), + finish: sandbox.stub().callsFake(), + debug: sandbox.stub().callsFake(), + info: sandbox.stub().callsFake(), + getChild: sandbox.stub().returns(SpanStub), + setTags: sandbox.stub().callsFake() + } + + const TracerStub = { + extractContextFromMessage: sandbox.stub().callsFake(() => { + return {} + }), + createChildSpanFromContext: sandbox.stub().callsFake(() => { + return SpanStub + }) + } + + const EventSdkStub = { + Tracer: TracerStub + } + + allBulkTransferHandlers = Proxyquire('../../../../../src/handlers/bulk/get/handler', { + '@mojaloop/event-sdk': EventSdkStub + }) + + sandbox.stub(KafkaConsumer.prototype, 'constructor').returns(Promise.resolve()) + sandbox.stub(KafkaConsumer.prototype, 'connect').returns(Promise.resolve()) + sandbox.stub(KafkaConsumer.prototype, 'consume').returns(Promise.resolve()) + sandbox.stub(KafkaConsumer.prototype, 'commitMessageSync').returns(Promise.resolve()) + sandbox.stub(Comparators) + sandbox.stub(Validator) + sandbox.stub(BulkTransferService) + sandbox.stub(BulkTransferModel) + sandbox.stub(Consumer, 'getConsumer').returns({ + commitMessageSync: async function () { + return true + } + }) + sandbox.stub(Consumer, 'isConsumerAutoCommitEnabled').returns(false) + sandbox.stub(ilp) + sandbox.stub(Kafka) + sandbox.stub(MainUtil.StreamingProtocol) + Kafka.produceGeneralMessage.returns(Promise.resolve()) + test.end() + }) + + getHandlerTest.afterEach(test => { + sandbox.restore() + test.end() + }) + + getHandlerTest.test('registerGetBulkTransferHandler should', registerHandlerTest => { + registerHandlerTest.test('returns true when registering the GET bulk transfer handler', async (test) => { + await Consumer.createHandler(topicName, config, command) + Kafka.transformAccountToTopicName.returns(topicName) + Kafka.proceed.returns(true) + Kafka.getKafkaConfig.returns(config) + const result = await allBulkTransferHandlers.registerGetBulkTransferHandler() + test.equal(result, true) + test.end() + }) + + registerHandlerTest.test('return an error when registering GET bulk transfer handler.', async (test) => { + try { + await Consumer.createHandler(topicName, config, command) + Kafka.transformGeneralTopicName.returns(topicName) + Kafka.getKafkaConfig.throws(new Error()) + await allBulkTransferHandlers.registerGetBulkTransferHandler() + test.fail('Error not thrown') + test.end() + } catch (e) { + test.pass('Error thrown') + test.end() + } + }) + registerHandlerTest.end() + }) + + getHandlerTest.test('get bulk transfer by id should', getBulkTransferTest => { + getBulkTransferTest.test('return true on a single message', async (test) => { + const localMessages = MainUtil.clone(messages) + await Consumer.createHandler(topicName, config, command) + Kafka.transformAccountToTopicName.returns(topicName) + Kafka.proceed.returns(true) + Kafka.getKafkaConfig.returns(config) + const result = await allBulkTransferHandlers.getBulkTransfer(null, localMessages[0]) + test.equal(result, true) + test.end() + }) + + getBulkTransferTest.test('return true on an array of messages', async (test) => { + const localMessages = MainUtil.clone(messages) + await Consumer.createHandler(topicName, config, command) + Kafka.transformAccountToTopicName.returns(topicName) + Kafka.proceed.returns(true) + Kafka.getKafkaConfig.returns(config) + Consumer.isConsumerAutoCommitEnabled.returns(true) + const result = await allBulkTransferHandlers.getBulkTransfer(null, localMessages) + test.equal(result, true) + test.end() + }) + + getBulkTransferTest.test('return an error when an error is passed in', async (test) => { + try { + const localMessages = MainUtil.clone(messages) + await Consumer.createHandler(topicName, config, command) + Kafka.transformGeneralTopicName.returns(topicName) + Kafka.getKafkaConfig.returns(config) + await allBulkTransferHandlers.getBulkTransfer(true, localMessages) + test.fail('Error not thrown') + test.end() + } catch (e) { + test.pass('Error thrown') + test.end() + } + }) + + getBulkTransferTest.test('return an error when the Kafka topic is invalid', async (test) => { + const localMessages = MainUtil.clone(messages) + await Consumer.createHandler(topicName, config, command) + Consumer.getConsumer.throws(new Error()) + Kafka.getKafkaConfig.returns(config) + const result = await allBulkTransferHandlers.getBulkTransfer(null, localMessages) + test.equal(result, true) + test.end() + }) + + getBulkTransferTest.test('return an error when the bulk transfer by id is not found', async (test) => { + const localMessages = MainUtil.clone(messages) + await Consumer.createHandler(topicName, config, command) + Kafka.transformAccountToTopicName.returns(topicName) + Kafka.proceed.returns(true) + Kafka.getKafkaConfig.returns(config) + Validator.validateParticipantByName.returns({ isValid: true }) + BulkTransferService.getParticipantsById.withArgs(bulkTransfer.bulkTransferId).returns({ payeeFsp: bulkTransfer.payeeFsp, payerFsp: bulkTransfer.payerFsp }) + BulkTransferModel.getById.returns(null) + const result = await allBulkTransferHandlers.getBulkTransfer(null, localMessages) + test.equal(result, true) + test.end() + }) + + getBulkTransferTest.test('return an error when the bulk transfer by id is not found - autocommit enabled', async (test) => { + const localMessages = MainUtil.clone(messages) + await Consumer.createHandler(topicName, config, command) + Kafka.transformAccountToTopicName.returns(topicName) + Kafka.proceed.returns(true) + Kafka.getKafkaConfig.returns(config) + Validator.validateParticipantByName.returns({ isValid: true }) + BulkTransferService.getParticipantsById.withArgs(bulkTransfer.bulkTransferId).returns({ payeeFsp: bulkTransfer.payeeFsp, payerFsp: bulkTransfer.payerFsp }) + BulkTransferModel.getById.returns(null) + Consumer.isConsumerAutoCommitEnabled.returns(true) + const result = await allBulkTransferHandlers.getBulkTransfer(null, localMessages) + test.equal(result, true) + test.end() + }) + + getBulkTransferTest.test('return an error when the requester is not involved in the bulk transfer', async (test) => { + const localMessages = MainUtil.clone(messages) + await Consumer.createHandler(topicName, config, command) + Kafka.transformAccountToTopicName.returns(topicName) + Kafka.proceed.returns(true) + Kafka.getKafkaConfig.returns(config) + Validator.validateParticipantByName.returns({ isValid: true }) + Validator.validateParticipantBulkTransferId.returns(false) + BulkTransferService.getParticipantsById.withArgs(bulkTransfer.bulkTransferId).returns({ payeeFsp: bulkTransfer.payeeFsp, payerFsp: bulkTransfer.payerFsp }) + BulkTransferModel.getById.returns({}) + Consumer.isConsumerAutoCommitEnabled.returns(true) + localMessages[0].value.from = 'invalidfsp' + const result = await allBulkTransferHandlers.getBulkTransfer(null, localMessages) + test.equal(result, true) + test.end() + }) + + getBulkTransferTest.test('return an error when the requester is not involved in the bulk transfer - autocommit disabled', async (test) => { + const localMessages = MainUtil.clone(messages) + await Consumer.createHandler(topicName, config, command) + Kafka.transformAccountToTopicName.returns(topicName) + Kafka.proceed.returns(true) + Kafka.getKafkaConfig.returns(config) + Validator.validateParticipantByName.returns({ isValid: true }) + Validator.validateParticipantBulkTransferId.returns(false) + BulkTransferService.getParticipantsById.withArgs(bulkTransfer.bulkTransferId).returns({ payeeFsp: bulkTransfer.payeeFsp, payerFsp: bulkTransfer.payerFsp }) + BulkTransferModel.getById.returns({}) + Consumer.isConsumerAutoCommitEnabled.returns(false) + localMessages[0].value.from = 'invalidfsp' + const result = await allBulkTransferHandlers.getBulkTransfer(null, localMessages) + test.equal(result, true) + test.end() + }) + + getBulkTransferTest.test('return an error when the bulk transfer by id is found - autocommit enabled', async (test) => { + const localMessages = MainUtil.clone(messages) + await Consumer.createHandler(topicName, config, command) + Kafka.transformAccountToTopicName.returns(topicName) + Kafka.proceed.returns(true) + Kafka.getKafkaConfig.returns(config) + Validator.validateParticipantByName.returns({ isValid: true }) + BulkTransferService.getParticipantsById.withArgs(bulkTransfer.bulkTransferId).returns({ payeeFsp: bulkTransfer.payeeFsp, payerFsp: bulkTransfer.payerFsp }) + BulkTransferModel.getById.withArgs(bulkTransfer.bulkTransferId).returns(Promise.resolve(bulkTransferReturn)) + Consumer.isConsumerAutoCommitEnabled.returns(true) + const result = await allBulkTransferHandlers.getBulkTransfer(null, localMessages) + test.equal(result, true) + test.end() + }) + + getBulkTransferTest.test('log an error when general message cannot be produced', async (test) => { + const localMessages = MainUtil.clone(messages) + await Consumer.createHandler(topicName, config, command) + Kafka.proceed.throws(new Error()) + Validator.validateParticipantByName.returns({ isValid: true }) + BulkTransferService.getParticipantsById.withArgs(bulkTransfer.bulkTransferId).returns({ payeeFsp: bulkTransfer.payeeFsp, payerFsp: bulkTransfer.payerFsp }) + const bulkTransferResult = MainUtil.clone(bulkTransferReturn) + bulkTransferResult.bulkTransferStateId = Enum.Transfers.BulkProcessingState.PROCESSING + bulkTransferResult.extensionList = [] + BulkTransferModel.getById.withArgs(bulkTransfer.bulkTransferId).returns(Promise.resolve(bulkTransferResult)) + const bulkTransferResult2 = { + ...bulkTransferResult, + bulkTransferState: 'COMPLETED', + payerBulkTransfer: { + bulkTransferState: 'COMPLETED', + individualTransferResults: [] + }, + payeeBulkTransfer: { + bulkTransferState: 'COMPLETED', + individualTransferResults: [] + } + } + BulkTransferService.getBulkTransferById.withArgs(bulkTransfer.bulkTransferId).returns(Promise.resolve(bulkTransferResult2)) + + try { + await allBulkTransferHandlers.getBulkTransfer(null, localMessages) + const expectedState = new EventSdk.EventStateMetadata(EventSdk.EventStatusType.failed, '2001', 'Internal server error') + test.ok(SpanStub.finish.calledWith('', expectedState)) + test.end() + } catch (e) { + test.fail('Error thrown') + test.end() + } + }) + + getBulkTransferTest.end() + }) + + getHandlerTest.end() +}) diff --git a/test/unit/handlers/transfers/handler.test.js b/test/unit/handlers/transfers/handler.test.js index 3125c6495..bb2d50623 100644 --- a/test/unit/handlers/transfers/handler.test.js +++ b/test/unit/handlers/transfers/handler.test.js @@ -131,7 +131,7 @@ const messageProtocol = { to: transfer.payeeFsp, type: 'application/json', content: { - headers: { 'fspiop-destination': transfer.payerFsp }, + headers: { 'fspiop-destination': transfer.payerFsp, 'content-type': 'application/vnd.interoperability.transfers+json;version=1.1' }, uriParams: { id: transfer.transferId }, payload: transfer }, @@ -177,7 +177,8 @@ const fulfilMessages = [ uriParams: { id: messageProtocol.content.uriParams.id }, headers: { 'fspiop-source': 'dfsp1', - 'fspiop-destination': 'dfsp2' + 'fspiop-destination': 'dfsp2', + 'content-type': 'application/vnd.interoperability.transfers+json;version=1.1' } }, metadata: { @@ -196,7 +197,8 @@ const fulfilMessages = [ uriParams: { id: messageProtocolBulkCommit.content.uriParams.id }, headers: { 'fspiop-source': 'dfsp1', - 'fspiop-destination': 'dfsp2' + 'fspiop-destination': 'dfsp2', + 'content-type': 'application/vnd.interoperability.transfers+json;version=1.1' } }, metadata: { @@ -957,6 +959,20 @@ Test('Transfer handler', transferHandlerTest => { }) transferHandlerTest.test('fulfil should', fulfilTest => { + fulfilTest.test('fail validation when when RESERVED transfer state is received from v1.0 clients', async (test) => { + const localfulfilMessages = MainUtil.clone(fulfilMessages) + localfulfilMessages[0].value.content.headers['content-type'] = 'application/vnd.interoperability.transfers+json;version=1.0' + localfulfilMessages[0].value.content.payload.transferState = 'RESERVED' + await Consumer.createHandler(topicName, config, command) + Kafka.transformGeneralTopicName.returns(topicName) + TransferService.getById.returns(Promise.resolve(null)) + Kafka.proceed.returns(true) + + const result = await allTransferHandlers.fulfil(null, localfulfilMessages) + test.equal(result, true) + test.end() + }) + fulfilTest.test('fail validation when invalid event action is provided', async (test) => { const localfulfilMessages = MainUtil.clone(fulfilMessages) await Consumer.createHandler(topicName, config, command) diff --git a/test/util/scripts/populateTestData.sh b/test/util/scripts/populateTestData.sh index c13ecd17a..26b0e8e02 100755 --- a/test/util/scripts/populateTestData.sh +++ b/test/util/scripts/populateTestData.sh @@ -240,6 +240,16 @@ do \"value\": \"http://localhost:1080/${FSP}\" }'" + sh -c "curl -X POST \ + ${CENTRAL_LEDGER_ADMIN_URI_PREFIX}://${CENTRAL_LEDGER_ADMIN_HOST}:${CENTRAL_LEDGER_ADMIN_PORT}${CENTRAL_LEDGER_ADMIN_BASE}participants/${FSP}/endpoints \ + -H 'Cache-Control: no-cache' \ + -H 'Content-Type: application/json' \ + -d '{ + \"type\": \"FSPIOP_CALLBACK_URL_BULK_QUOTES\", + \"value\": \"http://localhost:1080\" + }'" + + echo echo "Retrieving EndPoints for '$FSP'" echo "---------------------------------------------------------------------" diff --git a/test/util/scripts/populateTestDataForLegacySimulator.sh b/test/util/scripts/populateTestDataForLegacySimulator.sh new file mode 100755 index 000000000..ced737b90 --- /dev/null +++ b/test/util/scripts/populateTestDataForLegacySimulator.sh @@ -0,0 +1,261 @@ +#!/usr/bin/env bash +echo "---------------------------------------------------------------------" +echo "Starting script to populate test data.." +echo "---------------------------------------------------------------------" +echo + +CWD="${0%/*}" + +if [[ "$CWD" =~ ^(.*)\.sh$ ]]; +then + CWD="." +fi + +echo "Loading env vars..." +source $CWD/env.sh + +echo +echo "---------------------------------------------------------------------" +echo " Creating TestData for $FSPList" +echo "---------------------------------------------------------------------" + +echo "---------------------------------------------------------------------" +echo "Creating Hub Reconciliation account for the Scheme so that participant accounts in that currency can be created." +echo "---------------------------------------------------------------------" +curl -X POST \ + ${CENTRAL_LEDGER_ADMIN_URI_PREFIX}://${CENTRAL_LEDGER_ADMIN_HOST}:${CENTRAL_LEDGER_ADMIN_PORT}${CENTRAL_LEDGER_ADMIN_BASE}participants/Hub/accounts \ + -H 'Cache-Control: no-cache' \ + -H 'Content-Type: application/json' \ + -H 'cache-control: no-cache' \ + -d '{ + "currency": "USD", + "type": "HUB_RECONCILIATION" +}' + +echo "---------------------------------------------------------------------" +echo "Creating Hub Multilateral Net Settlement account for the Scheme so that participant accounts in that currency can be created." +echo "---------------------------------------------------------------------" +curl -X POST \ + ${CENTRAL_LEDGER_ADMIN_URI_PREFIX}://${CENTRAL_LEDGER_ADMIN_HOST}:${CENTRAL_LEDGER_ADMIN_PORT}${CENTRAL_LEDGER_ADMIN_BASE}participants/Hub/accounts \ + -H 'Cache-Control: no-cache' \ + -H 'Content-Type: application/json' \ + -H 'cache-control: no-cache' \ + -d '{ + "currency": "USD", + "type": "HUB_MULTILATERAL_SETTLEMENT" +}' + +echo +echo "---------------------------------------------------------------------" +echo " Creating TestData for $FSPList" +echo "---------------------------------------------------------------------" +echo " Prerequisites for Central-Ledger:" +echo " 1. Ensure you run 'npm run migrate'" +echo " 2. The below requests only work for the 'ADMIN' API" + +for FSP in "${FSPList[@]}" +do + echo '' + echo "*********************************************************************" + echo '' + echo + echo "Creating participants '$FSP'" + echo "---------------------------------------------------------------------" + sh -c "curl -i -X POST \ + ${CENTRAL_LEDGER_ADMIN_URI_PREFIX}://${CENTRAL_LEDGER_ADMIN_HOST}:${CENTRAL_LEDGER_ADMIN_PORT}${CENTRAL_LEDGER_ADMIN_BASE}participants \ + -H 'Cache-Control: no-cache' \ + -H 'Content-Type: application/json' \ + -d '{ + \"name\": \"$FSP\", + \"currency\":\"USD\" + }'" + + echo + echo "Setting limits and initial position for '$FSP'" + echo "---------------------------------------------------------------------" + sh -c "curl -i -X POST \ + ${CENTRAL_LEDGER_ADMIN_URI_PREFIX}://${CENTRAL_LEDGER_ADMIN_HOST}:${CENTRAL_LEDGER_ADMIN_PORT}${CENTRAL_LEDGER_ADMIN_BASE}participants/${FSP}/initialPositionAndLimits \ + -H 'Cache-Control: no-cache' \ + -H 'Content-Type: application/json' \ + -d '{ + \"currency\": \"USD\", + \"limit\": { + \"type\": \"NET_DEBIT_CAP\", + \"value\": ${DEFAULT_NET_DEBIT_CAP} + }, + \"initialPosition\": 0 + }'" + + echo + echo "Retrieving limits for '$FSP'" + echo "---------------------------------------------------------------------" + curl -X GET \ + ${CENTRAL_LEDGER_ADMIN_URI_PREFIX}://${CENTRAL_LEDGER_ADMIN_HOST}:${CENTRAL_LEDGER_ADMIN_PORT}${CENTRAL_LEDGER_ADMIN_BASE}participants/${FSP}/limits \ + -H 'Cache-Control: no-cache' + + echo + echo "Set callback URIs for each FSP '$FSP'" + echo "---------------------------------------------------------------------" + sh -c "curl -X POST \ + ${CENTRAL_LEDGER_ADMIN_URI_PREFIX}://${CENTRAL_LEDGER_ADMIN_HOST}:${CENTRAL_LEDGER_ADMIN_PORT}${CENTRAL_LEDGER_ADMIN_BASE}participants/${FSP}/endpoints \ + -H 'Cache-Control: no-cache' \ + -H 'Content-Type: application/json' \ + -d '{ + \"type\": \"FSPIOP_CALLBACK_URL_TRANSFER_POST\", + \"value\": \"http://localhost:8444/${FSP}/transfers\" + }'" + + sh -c "curl -X POST \ + ${CENTRAL_LEDGER_ADMIN_URI_PREFIX}://${CENTRAL_LEDGER_ADMIN_HOST}:${CENTRAL_LEDGER_ADMIN_PORT}${CENTRAL_LEDGER_ADMIN_BASE}participants/${FSP}/endpoints \ + -H 'Cache-Control: no-cache' \ + -H 'Content-Type: application/json' \ + -d '{ + \"type\": \"FSPIOP_CALLBACK_URL_PARTICIPANT_PUT\", + \"value\": \"http://localhost:8444/${FSP}/participants/{{partyIdType}}/{{partyIdentifier}}\" + }'" + + sh -c "curl -X POST \ + ${CENTRAL_LEDGER_ADMIN_URI_PREFIX}://${CENTRAL_LEDGER_ADMIN_HOST}:${CENTRAL_LEDGER_ADMIN_PORT}${CENTRAL_LEDGER_ADMIN_BASE}participants/${FSP}/endpoints \ + -H 'Cache-Control: no-cache' \ + -H 'Content-Type: application/json' \ + -d '{ + \"type\": \"FSPIOP_CALLBACK_URL_PARTIES_GET\", + \"value\": \"http://localhost:8444/${FSP}/parties/{{partyIdType}}/{{partyIdentifier}}\" + }'" + + sh -c "curl -X POST \ + ${CENTRAL_LEDGER_ADMIN_URI_PREFIX}://${CENTRAL_LEDGER_ADMIN_HOST}:${CENTRAL_LEDGER_ADMIN_PORT}${CENTRAL_LEDGER_ADMIN_BASE}participants/${FSP}/endpoints \ + -H 'Cache-Control: no-cache' \ + -H 'Content-Type: application/json' \ + -d '{ + \"type\": \"FSPIOP_CALLBACK_URL_PARTICIPANT_PUT_ERROR\", + \"value\": \"http://localhost:8444/${FSP}/participants/{{partyIdType}}/{{partyIdentifier}}/error\" + }'" + + sh -c "curl -X POST \ + ${CENTRAL_LEDGER_ADMIN_URI_PREFIX}://${CENTRAL_LEDGER_ADMIN_HOST}:${CENTRAL_LEDGER_ADMIN_PORT}${CENTRAL_LEDGER_ADMIN_BASE}participants/${FSP}/endpoints \ + -H 'Cache-Control: no-cache' \ + -H 'Content-Type: application/json' \ + -d '{ + \"type\": \"FSPIOP_CALLBACK_URL_PARTICIPANT_BATCH_PUT\", + \"value\": \"http://localhost:8444/${FSP}/participants/{{requestId}}\" + }'" + + sh -c "curl -X POST \ + ${CENTRAL_LEDGER_ADMIN_URI_PREFIX}://${CENTRAL_LEDGER_ADMIN_HOST}:${CENTRAL_LEDGER_ADMIN_PORT}${CENTRAL_LEDGER_ADMIN_BASE}participants/${FSP}/endpoints \ + -H 'Cache-Control: no-cache' \ + -H 'Content-Type: application/json' \ + -d '{ + \"type\": \"FSPIOP_CALLBACK_URL_PARTICIPANT_BATCH_PUT_ERROR\", + \"value\": \"http://localhost:8444/${FSP}\/participants/{{requestId}}/error\" + }'" + + sh -c "curl -X POST \ + ${CENTRAL_LEDGER_ADMIN_URI_PREFIX}://${CENTRAL_LEDGER_ADMIN_HOST}:${CENTRAL_LEDGER_ADMIN_PORT}${CENTRAL_LEDGER_ADMIN_BASE}participants/${FSP}/endpoints \ + -H 'Cache-Control: no-cache' \ + -H 'Content-Type: application/json' \ + -d '{ + \"type\": \"FSPIOP_CALLBACK_URL_PARTIES_PUT\", + \"value\": \"http://localhost:8444/${FSP}/parties/{{partyIdType}}/{{partyIdentifier}}\" + }'" + + sh -c "curl -X POST \ + ${CENTRAL_LEDGER_ADMIN_URI_PREFIX}://${CENTRAL_LEDGER_ADMIN_HOST}:${CENTRAL_LEDGER_ADMIN_PORT}${CENTRAL_LEDGER_ADMIN_BASE}participants/${FSP}/endpoints \ + -H 'Cache-Control: no-cache' \ + -H 'Content-Type: application/json' \ + -d '{ + \"type\": \"FSPIOP_CALLBACK_URL_PARTIES_PUT_ERROR\", + \"value\": \"http://localhost:8444/${FSP}/parties/{{partyIdType}}/{{partyIdentifier}}\" + }'" + + sh -c "curl -X POST \ + ${CENTRAL_LEDGER_ADMIN_URI_PREFIX}://${CENTRAL_LEDGER_ADMIN_HOST}:${CENTRAL_LEDGER_ADMIN_PORT}${CENTRAL_LEDGER_ADMIN_BASE}participants/${FSP}/endpoints \ + -H 'Cache-Control: no-cache' \ + -H 'Content-Type: application/json' \ + -d '{ + \"type\": \"FSPIOP_CALLBACK_URL_TRANSFER_PUT\", + \"value\": \"http://localhost:8444/${FSP}/transfers/{{transferId}}\" + }'" + + sh -c "curl -X POST \ + ${CENTRAL_LEDGER_ADMIN_URI_PREFIX}://${CENTRAL_LEDGER_ADMIN_HOST}:${CENTRAL_LEDGER_ADMIN_PORT}${CENTRAL_LEDGER_ADMIN_BASE}participants/${FSP}/endpoints \ + -H 'Cache-Control: no-cache' \ + -H 'Content-Type: application/json' \ + -d '{ + \"type\": \"FSPIOP_CALLBACK_URL_TRANSFER_ERROR\", + \"value\": \"http://localhost:8444/${FSP}/transfers/{{transferId}}/error\" + }'" + + sh -c "curl -X POST \ + ${CENTRAL_LEDGER_ADMIN_URI_PREFIX}://${CENTRAL_LEDGER_ADMIN_HOST}:${CENTRAL_LEDGER_ADMIN_PORT}${CENTRAL_LEDGER_ADMIN_BASE}participants/${FSP}/endpoints \ + -H 'Cache-Control: no-cache' \ + -H 'Content-Type: application/json' \ + -d '{ + \"type\": \"FSPIOP_CALLBACK_URL_BULK_TRANSFER_POST\", + \"value\": \"http://localhost:8444/${FSP}/bulkTransfers\" + }'" + + sh -c "curl -X POST \ + ${CENTRAL_LEDGER_ADMIN_URI_PREFIX}://${CENTRAL_LEDGER_ADMIN_HOST}:${CENTRAL_LEDGER_ADMIN_PORT}${CENTRAL_LEDGER_ADMIN_BASE}participants/${FSP}/endpoints \ + -H 'Cache-Control: no-cache' \ + -H 'Content-Type: application/json' \ + -d '{ + \"type\": \"FSPIOP_CALLBACK_URL_BULK_TRANSFER_PUT\", + \"value\": \"http://localhost:8444/${FSP}/bulkTransfers/{{id}}\" + }'" + + sh -c "curl -X POST \ + ${CENTRAL_LEDGER_ADMIN_URI_PREFIX}://${CENTRAL_LEDGER_ADMIN_HOST}:${CENTRAL_LEDGER_ADMIN_PORT}${CENTRAL_LEDGER_ADMIN_BASE}participants/${FSP}/endpoints \ + -H 'Cache-Control: no-cache' \ + -H 'Content-Type: application/json' \ + -d '{ + \"type\": \"FSPIOP_CALLBACK_URL_BULK_TRANSFER_ERROR\", + \"value\": \"http://localhost:8444/${FSP}/bulkTransfers/{{id}}/error\" + }'" + + sh -c "curl -X POST \ + ${CENTRAL_LEDGER_ADMIN_URI_PREFIX}://${CENTRAL_LEDGER_ADMIN_HOST}:${CENTRAL_LEDGER_ADMIN_PORT}${CENTRAL_LEDGER_ADMIN_BASE}participants/${FSP}/endpoints \ + -H 'Cache-Control: no-cache' \ + -H 'Content-Type: application/json' \ + -d '{ + \"type\": \"FSPIOP_CALLBACK_URL_QUOTES\", + \"value\": \"http://localhost:8444/${FSP}\" + }'" + + sh -c "curl -X POST \ + ${CENTRAL_LEDGER_ADMIN_URI_PREFIX}://${CENTRAL_LEDGER_ADMIN_HOST}:${CENTRAL_LEDGER_ADMIN_PORT}${CENTRAL_LEDGER_ADMIN_BASE}participants/${FSP}/endpoints \ + -H 'Cache-Control: no-cache' \ + -H 'Content-Type: application/json' \ + -d '{ + \"type\": \"FSPIOP_CALLBACK_URL_AUTHORIZATIONS\", + \"value\": \"http://localhost:8444/${FSP}\" + }'" + + sh -c "curl -X POST \ + ${CENTRAL_LEDGER_ADMIN_URI_PREFIX}://${CENTRAL_LEDGER_ADMIN_HOST}:${CENTRAL_LEDGER_ADMIN_PORT}${CENTRAL_LEDGER_ADMIN_BASE}participants/${FSP}/endpoints \ + -H 'Cache-Control: no-cache' \ + -H 'Content-Type: application/json' \ + -d '{ + \"type\": \"FSPIOP_CALLBACK_URL_TRX_REQ_SERVICE\", + \"value\": \"http://localhost:8444/${FSP}\" + }'" + + sh -c "curl -X POST \ + ${CENTRAL_LEDGER_ADMIN_URI_PREFIX}://${CENTRAL_LEDGER_ADMIN_HOST}:${CENTRAL_LEDGER_ADMIN_PORT}${CENTRAL_LEDGER_ADMIN_BASE}participants/${FSP}/endpoints \ + -H 'Cache-Control: no-cache' \ + -H 'Content-Type: application/json' \ + -d '{ + \"type\": \"FSPIOP_CALLBACK_URL_BULK_QUOTES\", + \"value\": \"http://localhost:8444\" + }'" + + echo + echo "Retrieving EndPoints for '$FSP'" + echo "---------------------------------------------------------------------" + curl -X GET \ + ${CENTRAL_LEDGER_ADMIN_URI_PREFIX}://${CENTRAL_LEDGER_ADMIN_HOST}:${CENTRAL_LEDGER_ADMIN_PORT}${CENTRAL_LEDGER_ADMIN_BASE}participants/${FSP}/endpoints \ + -H 'Cache-Control: no-cache' + +done + +echo