@mojaloop/sdk-scheme-adapter 12.2.2 → 13.0.0
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.
- package/.env.example +3 -0
- package/CHANGELOG.md +26 -0
- package/audit-resolve.json +71 -1
- package/docker/ml-testing-toolkit/spec_files/api_definitions/fspiop_1.1/trigger_templates/transaction_request_followup.json +2 -2
- package/docker/ml-testing-toolkit/spec_files/rules_callback/default.json +7 -7
- package/docker/ml-testing-toolkit/spec_files/rules_response/default.json +16 -16
- package/docker/ml-testing-toolkit/spec_files/rules_response/default_pisp_rules.json +5 -5
- package/docker/ml-testing-toolkit/spec_files/rules_validation/default.json +10 -10
- package/package.json +4 -1
- package/src/ControlAgent/index.js +2 -3
- package/src/ControlServer/index.js +2 -2
- package/src/InboundServer/handlers.js +114 -52
- package/src/InboundServer/index.js +7 -7
- package/src/InboundServer/middlewares.js +2 -2
- package/src/OutboundServer/api.yaml +54 -3
- package/src/OutboundServer/api_interfaces/openapi.d.ts +24 -3
- package/src/OutboundServer/api_template/components/schemas/accountsResponse.yaml +9 -0
- package/src/OutboundServer/api_template/components/schemas/transferRequest.yaml +3 -0
- package/src/OutboundServer/api_template/components/schemas/transferResponse.yaml +28 -2
- package/src/OutboundServer/api_template/components/schemas/transferStatusResponse.yaml +8 -1
- package/src/OutboundServer/handlers.js +4 -1
- package/src/OutboundServer/index.js +10 -11
- package/src/config.js +29 -12
- package/src/index.js +198 -10
- package/src/lib/cache.js +110 -52
- package/src/lib/metrics.js +148 -0
- package/src/lib/model/AccountsModel.js +17 -12
- package/src/lib/model/Async2SyncModel.js +4 -1
- package/src/lib/model/InboundTransfersModel.js +170 -25
- package/src/lib/model/OutboundBulkQuotesModel.js +4 -1
- package/src/lib/model/OutboundBulkTransfersModel.js +4 -1
- package/src/lib/model/OutboundRequestToPayModel.js +9 -7
- package/src/lib/model/OutboundRequestToPayTransferModel.js +6 -3
- package/src/lib/model/OutboundTransfersModel.js +318 -53
- package/src/lib/model/PartiesModel.js +1 -1
- package/src/lib/model/ProxyModel/index.js +4 -2
- package/src/lib/model/common/BackendError.js +28 -4
- package/src/lib/model/common/index.js +2 -1
- package/src/lib/validate.js +2 -2
- package/test/__mocks__/@mojaloop/sdk-standard-components.js +3 -2
- package/test/__mocks__/redis.js +4 -0
- package/test/config/integration.env +5 -0
- package/test/integration/lib/Outbound/parties.test.js +1 -1
- package/test/unit/ControlServer/index.js +3 -3
- package/test/unit/InboundServer.test.js +10 -10
- package/test/unit/TestServer.test.js +11 -13
- package/test/unit/api/accounts/data/postAccountsErrorMojaloopResponse.json +11 -3
- package/test/unit/api/accounts/data/postAccountsSuccessResponse.json +14 -0
- package/test/unit/api/accounts/data/postAccountsSuccessResponseWithError1.json +13 -0
- package/test/unit/api/accounts/data/postAccountsSuccessResponseWithError2.json +18 -0
- package/test/unit/api/accounts/utils.js +15 -1
- package/test/unit/api/transfers/data/getTransfersCommittedResponse.json +18 -15
- package/test/unit/api/transfers/data/getTransfersErrorNotFound.json +1 -0
- package/test/unit/api/transfers/data/postTransfersErrorMojaloopResponse.json +9 -0
- package/test/unit/api/transfers/data/postTransfersErrorTimeoutResponse.json +1 -0
- package/test/unit/api/transfers/data/postTransfersSuccessResponse.json +74 -47
- package/test/unit/api/transfers/utils.js +85 -4
- package/test/unit/api/utils.js +4 -1
- package/test/unit/config.test.js +2 -2
- package/test/unit/data/commonHttpHeaders.json +1 -0
- package/test/unit/data/defaultConfig.json +23 -7
- package/test/unit/inboundApi/handlers.test.js +45 -14
- package/test/unit/index.test.js +95 -4
- package/test/unit/lib/model/AccountsModel.test.js +9 -6
- package/test/unit/lib/model/InboundTransfersModel.test.js +210 -30
- package/test/unit/lib/model/OutboundRequestToPayModel.test.js +1 -1
- package/test/unit/lib/model/OutboundRequestToPayTransferModel.test.js +3 -3
- package/test/unit/lib/model/OutboundTransfersModel.test.js +863 -158
- package/test/unit/lib/model/data/defaultConfig.json +25 -10
- package/test/unit/lib/model/data/mockArguments.json +97 -40
- package/test/unit/lib/model/data/payeeParty.json +13 -11
- package/test/unit/lib/model/data/quoteResponse.json +36 -25
- package/test/unit/lib/model/data/transferFulfil.json +5 -3
- package/src/lib/api/index.js +0 -12
- package/src/lib/randomphrase/index.js +0 -21
- package/src/lib/randomphrase/words.json +0 -3397
|
@@ -55,6 +55,12 @@ describe('inboundModel', () => {
|
|
|
55
55
|
beforeEach(async () => {
|
|
56
56
|
expectedQuoteResponseILP = Ilp.__response;
|
|
57
57
|
BackendRequests.__postQuoteRequests = jest.fn().mockReturnValue(Promise.resolve(mockArgs.internalQuoteResponse));
|
|
58
|
+
MojaloopRequests.__putQuotes = jest.fn().mockReturnValue(Promise.resolve({
|
|
59
|
+
originalRequest: {
|
|
60
|
+
headers: {},
|
|
61
|
+
body: {},
|
|
62
|
+
}
|
|
63
|
+
}));
|
|
58
64
|
|
|
59
65
|
cache = new Cache({
|
|
60
66
|
host: 'dummycachehost',
|
|
@@ -78,11 +84,18 @@ describe('inboundModel', () => {
|
|
|
78
84
|
test('calls `mojaloopRequests.putQuotes` with the expected arguments.', async () => {
|
|
79
85
|
await model.quoteRequest(mockArgs.quoteRequest, mockArgs.fspId);
|
|
80
86
|
|
|
87
|
+
expect(BackendRequests.__postQuoteRequests).toHaveBeenCalledTimes(1);
|
|
88
|
+
expect(BackendRequests.__postQuoteRequests.mock.calls[0][0]).toEqual(mockArgs.internalQuoteRequest);
|
|
89
|
+
|
|
81
90
|
expect(MojaloopRequests.__putQuotes).toHaveBeenCalledTimes(1);
|
|
82
91
|
expect(MojaloopRequests.__putQuotes.mock.calls[0][1].expiration).toBe(mockArgs.internalQuoteResponse.expiration);
|
|
83
92
|
expect(MojaloopRequests.__putQuotes.mock.calls[0][1].ilpPacket).toBe(expectedQuoteResponseILP.ilpPacket);
|
|
84
93
|
expect(MojaloopRequests.__putQuotes.mock.calls[0][1].condition).toBe(expectedQuoteResponseILP.condition);
|
|
85
94
|
expect(MojaloopRequests.__putQuotes.mock.calls[0][2]).toBe(mockArgs.fspId);
|
|
95
|
+
|
|
96
|
+
// check the extension list gets translated correctly to the mojaloop form
|
|
97
|
+
expect(MojaloopRequests.__putQuotes.mock.calls[0][1].extensionList)
|
|
98
|
+
.toStrictEqual(mockArgs.internalQuoteResponse.extensionList);
|
|
86
99
|
});
|
|
87
100
|
|
|
88
101
|
test('adds a custom `expiration` property in case it is not defined.', async() => {
|
|
@@ -247,7 +260,12 @@ describe('inboundModel', () => {
|
|
|
247
260
|
beforeEach(async () => {
|
|
248
261
|
MojaloopRequests.__putTransfersError.mockClear();
|
|
249
262
|
BackendRequests.__postTransfers = jest.fn().mockReturnValue(Promise.resolve({}));
|
|
250
|
-
MojaloopRequests.__putTransfers = jest.fn().mockReturnValue(Promise.resolve({
|
|
263
|
+
MojaloopRequests.__putTransfers = jest.fn().mockReturnValue(Promise.resolve({
|
|
264
|
+
originalRequest: {
|
|
265
|
+
headers: {},
|
|
266
|
+
body: {},
|
|
267
|
+
}
|
|
268
|
+
}));
|
|
251
269
|
|
|
252
270
|
cache = new Cache({
|
|
253
271
|
host: 'dummycachehost',
|
|
@@ -269,13 +287,17 @@ describe('inboundModel', () => {
|
|
|
269
287
|
logger,
|
|
270
288
|
rejectTransfersOnExpiredQuotes: true,
|
|
271
289
|
});
|
|
272
|
-
cache.set(`
|
|
273
|
-
|
|
274
|
-
|
|
290
|
+
cache.set(`transferModel_in_${TRANSFER_ID}`, {
|
|
291
|
+
quote: {
|
|
292
|
+
mojaloopResponse: {
|
|
293
|
+
expiration: new Date(new Date().getTime() - 1000).toISOString(),
|
|
294
|
+
}
|
|
275
295
|
}
|
|
276
296
|
});
|
|
277
297
|
const args = {
|
|
278
|
-
|
|
298
|
+
body: {
|
|
299
|
+
transferId: TRANSFER_ID,
|
|
300
|
+
}
|
|
279
301
|
};
|
|
280
302
|
|
|
281
303
|
await model.prepareTransfer(args, mockArgs.fspId);
|
|
@@ -358,13 +380,15 @@ describe('inboundModel', () => {
|
|
|
358
380
|
test('fail on transfer without quote.', async () => {
|
|
359
381
|
const TRANSFER_ID = 'without_quote-transfer-id';
|
|
360
382
|
const args = {
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
383
|
+
body: {
|
|
384
|
+
transferId: TRANSFER_ID,
|
|
385
|
+
amount: {
|
|
386
|
+
currency: 'USD',
|
|
387
|
+
amount: 20.13
|
|
388
|
+
},
|
|
389
|
+
ilpPacket: 'mockBase64encodedIlpPacket',
|
|
390
|
+
condition: 'mockGeneratedCondition'
|
|
391
|
+
}
|
|
368
392
|
};
|
|
369
393
|
|
|
370
394
|
const model = new Model({
|
|
@@ -382,16 +406,76 @@ describe('inboundModel', () => {
|
|
|
382
406
|
expect(call[1].errorInformation.errorCode).toEqual('2001');
|
|
383
407
|
});
|
|
384
408
|
|
|
385
|
-
test('
|
|
386
|
-
const TRANSFER_ID = '
|
|
409
|
+
test('stores homeTransactionId in cache when received by dfsp acting as payee', async () => {
|
|
410
|
+
const TRANSFER_ID = 'transfer-id';
|
|
411
|
+
const HOME_TRANSACTION_ID = 'mockHomeTransactionId';
|
|
412
|
+
shared.mojaloopPrepareToInternalTransfer = jest.fn().mockReturnValueOnce({});
|
|
413
|
+
|
|
414
|
+
// mock response from dfsp acting as payee
|
|
415
|
+
BackendRequests.__postTransfers = jest.fn().mockReturnValueOnce(Promise.resolve({
|
|
416
|
+
homeTransactionId: HOME_TRANSACTION_ID,
|
|
417
|
+
transferId: TRANSFER_ID
|
|
418
|
+
}));
|
|
419
|
+
|
|
387
420
|
const args = {
|
|
421
|
+
body: {
|
|
422
|
+
transferId: TRANSFER_ID,
|
|
423
|
+
amount: {
|
|
424
|
+
currency: 'USD',
|
|
425
|
+
amount: 20.13
|
|
426
|
+
},
|
|
427
|
+
ilpPacket: 'mockBase64encodedIlpPacket',
|
|
428
|
+
condition: 'mockGeneratedCondition'
|
|
429
|
+
}
|
|
430
|
+
};
|
|
431
|
+
|
|
432
|
+
const model = new Model({
|
|
433
|
+
...config,
|
|
434
|
+
cache,
|
|
435
|
+
logger,
|
|
436
|
+
checkIlp: false,
|
|
437
|
+
rejectTransfersOnExpiredQuotes: false
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
cache.set(`transferModel_in_${TRANSFER_ID}`, {
|
|
388
441
|
transferId: TRANSFER_ID,
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
442
|
+
quote: {
|
|
443
|
+
fulfilment: 'mockFulfilment',
|
|
444
|
+
mojaloopResponse: {
|
|
445
|
+
condition: 'mockCondition',
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
await model.prepareTransfer(args, mockArgs.fspId);
|
|
451
|
+
|
|
452
|
+
expect(MojaloopRequests.__putTransfersError).toHaveBeenCalledTimes(0);
|
|
453
|
+
expect(BackendRequests.__postTransfers).toHaveBeenCalledTimes(1);
|
|
454
|
+
expect(MojaloopRequests.__putTransfers).toHaveBeenCalledTimes(1);
|
|
455
|
+
expect((await cache.get(`transferModel_in_${TRANSFER_ID}`)).homeTransactionId)
|
|
456
|
+
.toEqual(HOME_TRANSACTION_ID);
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
test('pass on transfer without quote.', async () => {
|
|
460
|
+
const TRANSFER_ID = 'without_quote-transfer-id';
|
|
461
|
+
cache.set(`transferModel_in_${TRANSFER_ID}`, {
|
|
462
|
+
fulfilment: '',
|
|
463
|
+
mojaloopResponse: {
|
|
464
|
+
response: ''
|
|
392
465
|
},
|
|
393
|
-
|
|
394
|
-
|
|
466
|
+
quote: null
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
const args = {
|
|
470
|
+
body: {
|
|
471
|
+
transferId: TRANSFER_ID,
|
|
472
|
+
amount: {
|
|
473
|
+
currency: 'USD',
|
|
474
|
+
amount: 20.13
|
|
475
|
+
},
|
|
476
|
+
ilpPacket: 'mockBase64encodedIlpPacket',
|
|
477
|
+
condition: 'mockGeneratedCondition'
|
|
478
|
+
}
|
|
395
479
|
};
|
|
396
480
|
|
|
397
481
|
const model = new Model({
|
|
@@ -413,21 +497,29 @@ describe('inboundModel', () => {
|
|
|
413
497
|
const TRANSFER_ID = 'transfer-id';
|
|
414
498
|
shared.mojaloopPrepareToInternalTransfer = jest.fn().mockReturnValueOnce({});
|
|
415
499
|
|
|
416
|
-
cache.set(`
|
|
500
|
+
cache.set(`transferModel_in_${transactionId}`, {
|
|
417
501
|
fulfilment: '',
|
|
418
502
|
mojaloopResponse: {
|
|
419
503
|
response: ''
|
|
504
|
+
},
|
|
505
|
+
quote: {
|
|
506
|
+
fulfilment: 'mockFulfilment',
|
|
507
|
+
mojaloopResponse: {
|
|
508
|
+
condition: 'mockCondition',
|
|
509
|
+
}
|
|
420
510
|
}
|
|
421
511
|
});
|
|
422
512
|
|
|
423
513
|
const args = {
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
514
|
+
body: {
|
|
515
|
+
transferId: TRANSFER_ID,
|
|
516
|
+
amount: {
|
|
517
|
+
currency: 'USD',
|
|
518
|
+
amount: 20.13
|
|
519
|
+
},
|
|
520
|
+
ilpPacket: 'mockIlpPacket',
|
|
521
|
+
condition: 'mockGeneratedCondition'
|
|
522
|
+
}
|
|
431
523
|
};
|
|
432
524
|
|
|
433
525
|
const model = new Model({
|
|
@@ -648,7 +740,12 @@ describe('inboundModel', () => {
|
|
|
648
740
|
|
|
649
741
|
test('sends notification to fsp backend', async () => {
|
|
650
742
|
BackendRequests.__putTransfersNotification = jest.fn().mockReturnValue(Promise.resolve({}));
|
|
651
|
-
const
|
|
743
|
+
const notif = JSON.parse(JSON.stringify(notificationToPayee));
|
|
744
|
+
|
|
745
|
+
const expectedRequest = {
|
|
746
|
+
currentState: 'COMPLETED',
|
|
747
|
+
finalNotification: notif.data,
|
|
748
|
+
};
|
|
652
749
|
|
|
653
750
|
const model = new Model({
|
|
654
751
|
...config,
|
|
@@ -656,11 +753,94 @@ describe('inboundModel', () => {
|
|
|
656
753
|
logger,
|
|
657
754
|
});
|
|
658
755
|
|
|
659
|
-
await model.sendNotificationToPayee(
|
|
756
|
+
await model.sendNotificationToPayee(notif.data, transferId);
|
|
660
757
|
expect(BackendRequests.__putTransfersNotification).toHaveBeenCalledTimes(1);
|
|
661
758
|
const call = BackendRequests.__putTransfersNotification.mock.calls[0];
|
|
662
|
-
expect(call[0]).toEqual(
|
|
759
|
+
expect(call[0]).toEqual(expectedRequest);
|
|
663
760
|
expect(call[1]).toEqual(transferId);
|
|
664
761
|
});
|
|
665
762
|
});
|
|
763
|
+
|
|
764
|
+
describe('error handling:', () => {
|
|
765
|
+
let cache;
|
|
766
|
+
beforeEach(async () => {
|
|
767
|
+
cache = new Cache({
|
|
768
|
+
host: 'dummycachehost',
|
|
769
|
+
port: 1234,
|
|
770
|
+
logger,
|
|
771
|
+
});
|
|
772
|
+
await cache.connect();
|
|
773
|
+
});
|
|
774
|
+
afterEach(async () => {
|
|
775
|
+
await cache.disconnect();
|
|
776
|
+
});
|
|
777
|
+
test('creates mojaloop spec error body when backend returns standard error code', async () => {
|
|
778
|
+
const model = new Model({
|
|
779
|
+
...config,
|
|
780
|
+
cache,
|
|
781
|
+
logger,
|
|
782
|
+
});
|
|
783
|
+
const testErr = new HTTPResponseError({
|
|
784
|
+
msg: 'Request returned non-success status code 500',
|
|
785
|
+
res: {
|
|
786
|
+
data: {
|
|
787
|
+
statusCode: '3200',
|
|
788
|
+
},
|
|
789
|
+
}
|
|
790
|
+
});
|
|
791
|
+
const err = await model._handleError(testErr);
|
|
792
|
+
expect(err).toBeDefined();
|
|
793
|
+
expect(err.errorInformation).toBeDefined();
|
|
794
|
+
expect(err.errorInformation.errorCode).toEqual('3200');
|
|
795
|
+
// error message should be the default one, not custom.
|
|
796
|
+
// it is debatibale whether this is truly correct, to overwrite
|
|
797
|
+
// and custom error message; but it is the case for now.
|
|
798
|
+
expect(err.errorInformation.errorDescription).toEqual('Generic ID not found');
|
|
799
|
+
});
|
|
800
|
+
test('creates custom error body when backend returns custom error code', async () => {
|
|
801
|
+
const model = new Model({
|
|
802
|
+
...config,
|
|
803
|
+
cache,
|
|
804
|
+
logger,
|
|
805
|
+
});
|
|
806
|
+
const customMessage = 'some custom message';
|
|
807
|
+
const testErr = new HTTPResponseError({
|
|
808
|
+
msg: 'Request returned non-success status code 500',
|
|
809
|
+
res: {
|
|
810
|
+
data: {
|
|
811
|
+
statusCode: '3299',
|
|
812
|
+
message: customMessage,
|
|
813
|
+
},
|
|
814
|
+
}
|
|
815
|
+
});
|
|
816
|
+
const err = await model._handleError(testErr);
|
|
817
|
+
expect(err).toBeDefined();
|
|
818
|
+
expect(err.errorInformation).toBeDefined();
|
|
819
|
+
expect(err.errorInformation.errorCode).toEqual('3299');
|
|
820
|
+
expect(err.errorInformation.errorDescription).toEqual(customMessage);
|
|
821
|
+
});
|
|
822
|
+
test('creates custom error message when backend returns standard error code and message', async () => {
|
|
823
|
+
const model = new Model({
|
|
824
|
+
...config,
|
|
825
|
+
cache,
|
|
826
|
+
logger,
|
|
827
|
+
});
|
|
828
|
+
const customMessage = 'some custom message';
|
|
829
|
+
const testErr = new HTTPResponseError({
|
|
830
|
+
msg: 'Request returned non-success status code 500',
|
|
831
|
+
res: {
|
|
832
|
+
data: {
|
|
833
|
+
statusCode: '3200',
|
|
834
|
+
message: customMessage,
|
|
835
|
+
},
|
|
836
|
+
}
|
|
837
|
+
});
|
|
838
|
+
const err = await model._handleError(testErr);
|
|
839
|
+
expect(err).toBeDefined();
|
|
840
|
+
expect(err.errorInformation).toBeDefined();
|
|
841
|
+
expect(err.errorInformation.errorCode).toEqual('3200');
|
|
842
|
+
// error message should be custom
|
|
843
|
+
expect(err.errorInformation.errorDescription).toEqual(customMessage);
|
|
844
|
+
});
|
|
845
|
+
});
|
|
666
846
|
});
|
|
@@ -27,7 +27,7 @@ const payeeParty = require('./data/payeeParty');
|
|
|
27
27
|
const transactionRequestResponseTemplate = require('./data/transactionRequestResponse');
|
|
28
28
|
|
|
29
29
|
const genPartyId = (party) => {
|
|
30
|
-
const { partyIdType, partyIdentifier, partySubIdOrType } = party.party.partyIdInfo;
|
|
30
|
+
const { partyIdType, partyIdentifier, partySubIdOrType } = party.body.party.partyIdInfo;
|
|
31
31
|
return PartiesModel.channelName({
|
|
32
32
|
type: partyIdType,
|
|
33
33
|
id: partyIdentifier,
|
|
@@ -121,7 +121,7 @@ describe('outboundRequestToPayTransferModel', () => {
|
|
|
121
121
|
MojaloopRequests.__postTransfers = jest.fn((postTransfersBody, destFspId) => {
|
|
122
122
|
//ensure that the `MojaloopRequests.postTransfers` method has been called with the correct arguments
|
|
123
123
|
// set as the destination FSPID, picked up from the header's value `fspiop-source`
|
|
124
|
-
expect(model.data.quoteResponseSource).toBe(quoteResponse.headers['fspiop-source']);
|
|
124
|
+
expect(model.data.quoteResponseSource).toBe(quoteResponse.data.headers['fspiop-source']);
|
|
125
125
|
|
|
126
126
|
const extensionList = postTransfersBody.extensionList.extension;
|
|
127
127
|
expect(extensionList).toBeTruthy();
|
|
@@ -129,8 +129,8 @@ describe('outboundRequestToPayTransferModel', () => {
|
|
|
129
129
|
expect(extensionList[0]).toEqual({ key: 'tkey1', value: 'tvalue1' });
|
|
130
130
|
expect(extensionList[1]).toEqual({ key: 'tkey2', value: 'tvalue2' });
|
|
131
131
|
|
|
132
|
-
expect(destFspId).toBe(quoteResponse.headers['fspiop-source']);
|
|
133
|
-
expect(quoteResponse.headers['fspiop-source']).not.toBe(model.data.to.fspId);
|
|
132
|
+
expect(destFspId).toBe(quoteResponse.data.headers['fspiop-source']);
|
|
133
|
+
expect(quoteResponse.data.headers['fspiop-source']).not.toBe(model.data.to.fspId);
|
|
134
134
|
|
|
135
135
|
// simulate a callback with the transfer fulfilment
|
|
136
136
|
emitTransferFulfilCacheMessage(cache, postTransfersBody.transferId, transferFulfil);
|