diff --git a/package-lock.json b/package-lock.json index 0f1543ab..c01b97da 100644 --- a/package-lock.json +++ b/package-lock.json @@ -722,6 +722,21 @@ "requires": { "@mojaloop/sdk-standard-components": "8.6.1", "lodash": "4.17.15" + }, + "dependencies": { + "@mojaloop/sdk-standard-components": { + "version": "8.6.1", + "resolved": "https://registry.npmjs.org/@mojaloop/sdk-standard-components/-/sdk-standard-components-8.6.1.tgz", + "integrity": "sha512-KN8OO+uwoIfUZG9XamUNtY+doXlaNSae9ZZDpNhYjsVkaR1uE6TKPXiisH1gHMGkr9nNMCw8ON6EQr8s4jSO+A==", + "requires": { + "base64url": "^3.0.1", + "ilp-packet": "2.2.0", + "jsonwebtoken": "^8.5.1", + "jws": "^3.2.2", + "request": "^2.34", + "request-promise-native": "^1.0.7" + } + } } }, "@mojaloop/central-services-logger": { @@ -853,9 +868,9 @@ } }, "@mojaloop/sdk-standard-components": { - "version": "8.6.1", - "resolved": "https://registry.npmjs.org/@mojaloop/sdk-standard-components/-/sdk-standard-components-8.6.1.tgz", - "integrity": "sha512-KN8OO+uwoIfUZG9XamUNtY+doXlaNSae9ZZDpNhYjsVkaR1uE6TKPXiisH1gHMGkr9nNMCw8ON6EQr8s4jSO+A==", + "version": "8.6.9", + "resolved": "https://registry.npmjs.org/@mojaloop/sdk-standard-components/-/sdk-standard-components-8.6.9.tgz", + "integrity": "sha512-2S1pmYm8rdAlgrzUhS67+vMpKWWWxYnq2p/Hd4idrWHehSz6C4b1oxF/e4wx4SxVbFkMwgdXQt4Dy4tvoswhbQ==", "requires": { "base64url": "^3.0.1", "ilp-packet": "2.2.0", @@ -4703,6 +4718,15 @@ "json-prune": "^1.1.0" } }, + "hasha": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hasha/-/hasha-3.0.0.tgz", + "integrity": "sha1-UqMvq4Vp1BymmmH/GiFPjrfIvTk=", + "dev": true, + "requires": { + "is-stream": "^1.0.1" + } + }, "hoek": { "version": "6.1.3", "resolved": "https://registry.npmjs.org/hoek/-/hoek-6.1.3.tgz", @@ -5258,6 +5282,15 @@ "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", "dev": true }, + "istanbul-lib-hook": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-2.0.7.tgz", + "integrity": "sha512-vrRztU9VRRFDyC+aklfLoeXyNdTfga2EI3udDGn4cZ6fpSXpHLV9X6CHvfoMCPtggg8zvDDmC4b9xfu0z6/llA==", + "dev": true, + "requires": { + "append-transform": "^1.0.0" + } + }, "istanbul-lib-instrument": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz", @@ -5586,7 +5619,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -5607,12 +5641,14 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -5627,17 +5663,20 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -5754,7 +5793,8 @@ "inherits": { "version": "2.0.4", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -5766,6 +5806,7 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -5780,6 +5821,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -5787,12 +5829,14 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.9.0", "bundled": true, "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -5811,6 +5855,7 @@ "version": "0.5.1", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -5900,7 +5945,8 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -5912,6 +5958,7 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -5997,7 +6044,8 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -6033,6 +6081,7 @@ "version": "1.0.2", "bundled": true, "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -6052,6 +6101,7 @@ "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -6095,12 +6145,14 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true } } } diff --git a/package.json b/package.json index ef7ed2b2..b32d085a 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,7 @@ "@mojaloop/central-services-shared": "8.7.1", "@mojaloop/event-sdk": "8.6.2", "@mojaloop/ml-number": "8.2.0", + "@mojaloop/sdk-standard-components": "^8.6.9", "axios": "0.19.0", "blipp": "4.0.1", "eslint-config-standard": "14.1.0", diff --git a/src/handlers/quotes.js b/src/handlers/quotes.js index b66eebcd..36d709e6 100644 --- a/src/handlers/quotes.js +++ b/src/handlers/quotes.js @@ -78,14 +78,13 @@ module.exports = { // call the quote request handler in the model const result = await model.handleQuoteRequest(request.headers, request.payload, span) request.server.log(['info'], `POST quote request succeeded and returned: ${util.inspect(result)}`) + return h.response().code(Enum.Http.ReturnCodes.ACCEPTED.CODE) } catch (err) { // something went wrong, use the model to handle the error in a sensible way request.server.log(['error'], `ERROR - POST /quotes: ${LibUtil.getStackOrInspect(err)}`) const fspiopError = ErrorHandler.ReformatFSPIOPError(err) - await model.handleException(fspiopSource, quoteId, fspiopError, request.headers, span) - } finally { - // eslint-disable-next-line no-unsafe-finally - return h.response().code(Enum.Http.ReturnCodes.ACCEPTED.CODE) + const { body, code } = await model.handleException(fspiopSource, quoteId, fspiopError, request.headers, span) + return h.response(body).code(code) } } } diff --git a/src/handlers/quotes/{id}.js b/src/handlers/quotes/{id}.js index cf601783..1c6c9f26 100644 --- a/src/handlers/quotes/{id}.js +++ b/src/handlers/quotes/{id}.js @@ -78,13 +78,12 @@ module.exports = { // will send the callback to the correct party regardless. const result = await model.handleQuoteGet(request.headers, quoteId, span) request.server.log(['info'], `GET quotes/{id} request succeeded and returned: ${util.inspect(result)}`) + return h.response().code(202) } catch (err) { // something went wrong, use the model to handle the error in a sensible way request.server.log(['error'], `ERROR - GET /quotes/{id}: ${LibUtil.getStackOrInspect(err)}`) - await model.handleException(fspiopSource, quoteId, err, request.headers, span) - } finally { - // eslint-disable-next-line no-unsafe-finally - return h.response().code(202) + const { body, code } = await model.handleException(fspiopSource, quoteId, err, request.headers, span) + return h.response(body).code(code) } }, @@ -121,13 +120,12 @@ module.exports = { // call the quote update handler in the model const result = await model.handleQuoteUpdate(request.headers, quoteId, request.payload, span) request.server.log(['info'], `PUT quote request succeeded and returned: ${util.inspect(result)}`) + return h.response().code(202) } catch (err) { // something went wrong, use the model to handle the error in a sensible way request.server.log(['error'], `ERROR - PUT /quotes/{id}: ${LibUtil.getStackOrInspect(err)}`) - await model.handleException(fspiopSource, quoteId, err, request.headers, span) - } finally { - // eslint-disable-next-line no-unsafe-finally - return h.response().code(202) + const { body, code } = await model.handleException(fspiopSource, quoteId, err, request.headers, span) + return h.response(body).code(code) } } } diff --git a/src/handlers/quotes/{id}/error.js b/src/handlers/quotes/{id}/error.js index 52536f72..c1d035bc 100644 --- a/src/handlers/quotes/{id}/error.js +++ b/src/handlers/quotes/{id}/error.js @@ -76,13 +76,12 @@ module.exports = { // call the quote error handler in the model const result = await model.handleQuoteError(request.headers, quoteId, request.payload.errorInformation, span) request.server.log(['info'], `PUT quote error request succeeded and returned: ${util.inspect(result)}`) + return h.response().code(Enum.Http.ReturnCodes.OK.CODE) } catch (err) { // something went wrong, use the model to handle the error in a sensible way request.server.log(['error'], `ERROR - PUT /quotes/{id}/error: ${LibUtil.getStackOrInspect(err)}`) - await model.handleException(fspiopSource, quoteId, err, request.headers) - } finally { - // eslint-disable-next-line no-unsafe-finally - return h.response().code(Enum.Http.ReturnCodes.OK.CODE) + const { body, code } = await model.handleException(fspiopSource, quoteId, err, request.headers, span) + return h.response(body).code(code) } } } diff --git a/src/model/quotes.js b/src/model/quotes.js index b6e1912a..ca3d357f 100644 --- a/src/model/quotes.js +++ b/src/model/quotes.js @@ -37,6 +37,7 @@ const axios = require('axios') const crypto = require('crypto') const util = require('util') +const { MojaloopApiErrorCodes } = require('@mojaloop/sdk-standard-components').Errors const ENUM = require('@mojaloop/central-services-shared').Enum const ErrorHandler = require('@mojaloop/central-services-error-handling') const EventSdk = require('@mojaloop/event-sdk') @@ -826,7 +827,28 @@ class QuotesModel { const childSpan = span.getChild('qs_quote_sendErrorCallback') try { await childSpan.audit({ headers, params: { quoteId } }, EventSdk.AuditEventAction.start) - return await this.sendErrorCallback(fspiopSource, fspiopError, quoteId, headers, childSpan) + const syncErrorCodes = [ + MojaloopApiErrorCodes.MISSING_ELEMENT.code, + MojaloopApiErrorCodes.PAYEE_ERROR.code, + MojaloopApiErrorCodes.PAYEE_UNSUPPORTED_CURRENCY.code, + MojaloopApiErrorCodes.PAYER_ERROR.code, + MojaloopApiErrorCodes.PAYER_UNSUPPORTED_CURRENCY.code + ]; + if (error.name === 'FSPIOPError' && syncErrorCodes.includes(error.apiErrorCode.code)) { + // We should respond synchronously + const envConfig = new Config() + return { + body: error.toApiErrorObject(envConfig.errorHandling), + code: ENUM.Http.ReturnCodes.BADREQUEST + } + } + else { + // We should respond asynchronously + await this.sendErrorCallback(fspiopSource, fspiopError, quoteId, headers, childSpan) + return { + code: ENUM.Http.ReturnCodes.ACCEPTED.CODE + } + } } catch (err) { // any-error // not much we can do other than log the error diff --git a/test/unit/handlers/quotes.test.js b/test/unit/handlers/quotes.test.js index c1992792..23e7af38 100644 --- a/test/unit/handlers/quotes.test.js +++ b/test/unit/handlers/quotes.test.js @@ -73,7 +73,7 @@ describe('/quotes', () => { it('fails to create a quote', async () => { // Arrange - const handleException = jest.fn() + const handleException = jest.fn(() => ({ code: Enum.Http.ReturnCodes.ACCEPTED.CODE })) QuotesModel.mockImplementationOnce(() => ({ handleQuoteRequest: () => { throw new Error('Create Quote Test Error') diff --git a/test/unit/handlers/quotes/{id}.test.js b/test/unit/handlers/quotes/{id}.test.js index cd3f4187..1a1beb7e 100644 --- a/test/unit/handlers/quotes/{id}.test.js +++ b/test/unit/handlers/quotes/{id}.test.js @@ -63,7 +63,7 @@ describe('/quotes/{id}', () => { it('handles an error with the model', async () => { // Arrange - const handleException = jest.fn() + const handleException = jest.fn(() => ({ code: 202 })) QuotesModel.mockImplementationOnce(() => { return { handleQuoteGet: () => { @@ -113,7 +113,7 @@ describe('/quotes/{id}', () => { it('handles an error with the model', async () => { // Arrange - const handleException = jest.fn() + const handleException = jest.fn(() => ({ code: 202 })) QuotesModel.mockImplementationOnce(() => { return { handleQuoteUpdate: () => { diff --git a/test/unit/handlers/quotes/{id}/error.test.js b/test/unit/handlers/quotes/{id}/error.test.js index 7cee4758..a192fb1a 100644 --- a/test/unit/handlers/quotes/{id}/error.test.js +++ b/test/unit/handlers/quotes/{id}/error.test.js @@ -83,7 +83,7 @@ describe('/quotes/{id}', () => { } } } - const handleException = jest.fn() + const handleException = jest.fn(() => ({ code: Enum.Http.ReturnCodes.OK.CODE })) QuotesModel.mockImplementationOnce(() => { return { handleQuoteError: () => { diff --git a/test/unit/model/quotes.test.js b/test/unit/model/quotes.test.js index 7e8b60b0..196597a2 100644 --- a/test/unit/model/quotes.test.js +++ b/test/unit/model/quotes.test.js @@ -1815,7 +1815,7 @@ describe('QuotesModel', () => { // Assert expect(quotesModel.sendErrorCallback).toHaveBeenCalledWith('payeefsp', expectedError, mockData.quoteId, mockData.headers, mockChildSpan) - expect(result).toBe(true) + expect(result).toStrictEqual({ code: 202 }) expect(mockChildSpan.finish).toHaveBeenCalledTimes(1) })