@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.
Files changed (76) hide show
  1. package/.env.example +3 -0
  2. package/CHANGELOG.md +26 -0
  3. package/audit-resolve.json +71 -1
  4. package/docker/ml-testing-toolkit/spec_files/api_definitions/fspiop_1.1/trigger_templates/transaction_request_followup.json +2 -2
  5. package/docker/ml-testing-toolkit/spec_files/rules_callback/default.json +7 -7
  6. package/docker/ml-testing-toolkit/spec_files/rules_response/default.json +16 -16
  7. package/docker/ml-testing-toolkit/spec_files/rules_response/default_pisp_rules.json +5 -5
  8. package/docker/ml-testing-toolkit/spec_files/rules_validation/default.json +10 -10
  9. package/package.json +4 -1
  10. package/src/ControlAgent/index.js +2 -3
  11. package/src/ControlServer/index.js +2 -2
  12. package/src/InboundServer/handlers.js +114 -52
  13. package/src/InboundServer/index.js +7 -7
  14. package/src/InboundServer/middlewares.js +2 -2
  15. package/src/OutboundServer/api.yaml +54 -3
  16. package/src/OutboundServer/api_interfaces/openapi.d.ts +24 -3
  17. package/src/OutboundServer/api_template/components/schemas/accountsResponse.yaml +9 -0
  18. package/src/OutboundServer/api_template/components/schemas/transferRequest.yaml +3 -0
  19. package/src/OutboundServer/api_template/components/schemas/transferResponse.yaml +28 -2
  20. package/src/OutboundServer/api_template/components/schemas/transferStatusResponse.yaml +8 -1
  21. package/src/OutboundServer/handlers.js +4 -1
  22. package/src/OutboundServer/index.js +10 -11
  23. package/src/config.js +29 -12
  24. package/src/index.js +198 -10
  25. package/src/lib/cache.js +110 -52
  26. package/src/lib/metrics.js +148 -0
  27. package/src/lib/model/AccountsModel.js +17 -12
  28. package/src/lib/model/Async2SyncModel.js +4 -1
  29. package/src/lib/model/InboundTransfersModel.js +170 -25
  30. package/src/lib/model/OutboundBulkQuotesModel.js +4 -1
  31. package/src/lib/model/OutboundBulkTransfersModel.js +4 -1
  32. package/src/lib/model/OutboundRequestToPayModel.js +9 -7
  33. package/src/lib/model/OutboundRequestToPayTransferModel.js +6 -3
  34. package/src/lib/model/OutboundTransfersModel.js +318 -53
  35. package/src/lib/model/PartiesModel.js +1 -1
  36. package/src/lib/model/ProxyModel/index.js +4 -2
  37. package/src/lib/model/common/BackendError.js +28 -4
  38. package/src/lib/model/common/index.js +2 -1
  39. package/src/lib/validate.js +2 -2
  40. package/test/__mocks__/@mojaloop/sdk-standard-components.js +3 -2
  41. package/test/__mocks__/redis.js +4 -0
  42. package/test/config/integration.env +5 -0
  43. package/test/integration/lib/Outbound/parties.test.js +1 -1
  44. package/test/unit/ControlServer/index.js +3 -3
  45. package/test/unit/InboundServer.test.js +10 -10
  46. package/test/unit/TestServer.test.js +11 -13
  47. package/test/unit/api/accounts/data/postAccountsErrorMojaloopResponse.json +11 -3
  48. package/test/unit/api/accounts/data/postAccountsSuccessResponse.json +14 -0
  49. package/test/unit/api/accounts/data/postAccountsSuccessResponseWithError1.json +13 -0
  50. package/test/unit/api/accounts/data/postAccountsSuccessResponseWithError2.json +18 -0
  51. package/test/unit/api/accounts/utils.js +15 -1
  52. package/test/unit/api/transfers/data/getTransfersCommittedResponse.json +18 -15
  53. package/test/unit/api/transfers/data/getTransfersErrorNotFound.json +1 -0
  54. package/test/unit/api/transfers/data/postTransfersErrorMojaloopResponse.json +9 -0
  55. package/test/unit/api/transfers/data/postTransfersErrorTimeoutResponse.json +1 -0
  56. package/test/unit/api/transfers/data/postTransfersSuccessResponse.json +74 -47
  57. package/test/unit/api/transfers/utils.js +85 -4
  58. package/test/unit/api/utils.js +4 -1
  59. package/test/unit/config.test.js +2 -2
  60. package/test/unit/data/commonHttpHeaders.json +1 -0
  61. package/test/unit/data/defaultConfig.json +23 -7
  62. package/test/unit/inboundApi/handlers.test.js +45 -14
  63. package/test/unit/index.test.js +95 -4
  64. package/test/unit/lib/model/AccountsModel.test.js +9 -6
  65. package/test/unit/lib/model/InboundTransfersModel.test.js +210 -30
  66. package/test/unit/lib/model/OutboundRequestToPayModel.test.js +1 -1
  67. package/test/unit/lib/model/OutboundRequestToPayTransferModel.test.js +3 -3
  68. package/test/unit/lib/model/OutboundTransfersModel.test.js +863 -158
  69. package/test/unit/lib/model/data/defaultConfig.json +25 -10
  70. package/test/unit/lib/model/data/mockArguments.json +97 -40
  71. package/test/unit/lib/model/data/payeeParty.json +13 -11
  72. package/test/unit/lib/model/data/quoteResponse.json +36 -25
  73. package/test/unit/lib/model/data/transferFulfil.json +5 -3
  74. package/src/lib/api/index.js +0 -12
  75. package/src/lib/randomphrase/index.js +0 -21
  76. package/src/lib/randomphrase/words.json +0 -3397
@@ -3,27 +3,52 @@
3
3
  "amountType": "SEND",
4
4
  "currency": "USD",
5
5
  "currentState": "COMPLETED",
6
+ "direction": "OUTBOUND",
6
7
  "from": {
7
8
  "displayName": "John Doe",
8
9
  "idType": "MSISDN",
9
10
  "idValue": "123456789"
10
11
  },
11
12
  "fulfil": {
12
- "completedTimestamp": "2017-11-15T14:16:09.663+01:00",
13
- "extensionList": {
14
- "extension": [
15
- {
16
- "key": "treskey1",
17
- "value": "tresvalue1"
13
+ "body": {
14
+ "completedTimestamp": "2017-11-15T14:16:09.663+01:00",
15
+ "extensionList": {
16
+ "extension": [
17
+ {
18
+ "key": "treskey1",
19
+ "value": "tresvalue1"
20
+ },
21
+ {
22
+ "key": "treskey2",
23
+ "value": "tresvalue2"
24
+ }
25
+ ]
26
+ },
27
+ "fulfilment": "87mm1reS3SAi8oIWXgBkLmgWc1MkZ_yLbFDX5XAdo5o",
28
+ "transferState": "COMMITTED"
29
+ }
30
+ },
31
+ "getPartiesResponse": {
32
+ "body": {
33
+ "party": {
34
+ "merchantClassificationCode": "1234",
35
+ "name": "John Doe",
36
+ "partyIdInfo": {
37
+ "fspId": "sim",
38
+ "partyIdType": "PERSONAL_ID",
39
+ "partyIdentifier": "987654321",
40
+ "partySubIdOrType": "PASSPORT"
18
41
  },
19
- {
20
- "key": "treskey2",
21
- "value": "tresvalue2"
42
+ "personalInfo": {
43
+ "complexName": {
44
+ "firstName": "John",
45
+ "lastName": "Doe",
46
+ "middleName": "Someone"
47
+ },
48
+ "dateOfBirth": "1980-01-01"
22
49
  }
23
- ]
24
- },
25
- "fulfilment": "87mm1reS3SAi8oIWXgBkLmgWc1MkZ_yLbFDX5XAdo5o",
26
- "transferState": "COMMITTED"
50
+ }
51
+ }
27
52
  },
28
53
  "homeTransactionId": "123ABC",
29
54
  "note": "test payment",
@@ -39,40 +64,42 @@
39
64
  }
40
65
  ],
41
66
  "quoteResponse": {
42
- "condition": "fH9pAYDQbmoZLPbvv3CSW2RfjU4jvM4ApG_fqGnR7Xs",
43
- "expiration": "2017-11-15T14:17:09.663+01:00",
44
- "extensionList": {
45
- "extension": [
46
- {
47
- "key": "qreskey1",
48
- "value": "qresvalue1"
49
- },
50
- {
51
- "key": "qreskey2",
52
- "value": "qresvalue2"
53
- }
54
- ]
55
- },
56
- "geoCode": {
57
- "latitude": "53.295971",
58
- "longitude": "-0.038500"
59
- },
60
- "ilpPacket": "AQAAAAAAACasIWcuc2UubW9iaWxlbW9uZXkubXNpc2RuLjEyMzQ1Njc4OYIEIXsNCiAgICAidHJhbnNhY3Rpb25JZCI6ICI4NWZlYWMyZi0zOWIyLTQ5MWItODE3ZS00YTAzMjAzZDRmMTQiLA0KICAgICJxdW90ZUlkIjogIjdjMjNlODBjLWQwNzgtNDA3Ny04MjYzLTJjMDQ3ODc2ZmNmNiIsDQogICAgInBheWVlIjogew0KICAgICAgICAicGFydHlJZEluZm8iOiB7DQogICAgICAgICAgICAicGFydHlJZFR5cGUiOiAiTVNJU0ROIiwNCiAgICAgICAgICAgICJwYXJ0eUlkZW50aWZpZXIiOiAiMTIzNDU2Nzg5IiwNCiAgICAgICAgICAgICJmc3BJZCI6ICJNb2JpbGVNb25leSINCiAgICAgICAgfSwNCiAgICAgICAgInBlcnNvbmFsSW5mbyI6IHsNCiAgICAgICAgICAgICJjb21wbGV4TmFtZSI6IHsNCiAgICAgICAgICAgICAgICAiZmlyc3ROYW1lIjogIkhlbnJpayIsDQogICAgICAgICAgICAgICAgImxhc3ROYW1lIjogIkthcmxzc29uIg0KICAgICAgICAgICAgfQ0KICAgICAgICB9DQogICAgfSwNCiAgICAicGF5ZXIiOiB7DQogICAgICAgICJwZXJzb25hbEluZm8iOiB7DQogICAgICAgICAgICAiY29tcGxleE5hbWUiOiB7DQogICAgICAgICAgICAgICAgImZpcnN0TmFtZSI6ICJNYXRzIiwNCiAgICAgICAgICAgICAgICAibGFzdE5hbWUiOiAiSGFnbWFuIg0KICAgICAgICAgICAgfQ0KICAgICAgICB9LA0KICAgICAgICAicGFydHlJZEluZm8iOiB7DQogICAgICAgICAgICAicGFydHlJZFR5cGUiOiAiSUJBTiIsDQogICAgICAgICAgICAicGFydHlJZGVudGlmaWVyIjogIlNFNDU1MDAwMDAwMDA1ODM5ODI1NzQ2NiIsDQogICAgICAgICAgICAiZnNwSWQiOiAiQmFua05yT25lIg0KICAgICAgICB9DQogICAgfSwNCiAgICAiYW1vdW50Ijogew0KICAgICAgICAiYW1vdW50IjogIjEwMCIsDQogICAgICAgICJjdXJyZW5jeSI6ICJVU0QiDQogICAgfSwNCiAgICAidHJhbnNhY3Rpb25UeXBlIjogew0KICAgICAgICAic2NlbmFyaW8iOiAiVFJBTlNGRVIiLA0KICAgICAgICAiaW5pdGlhdG9yIjogIlBBWUVSIiwNCiAgICAgICAgImluaXRpYXRvclR5cGUiOiAiQ09OU1VNRVIiDQogICAgfSwNCiAgICAibm90ZSI6ICJGcm9tIE1hdHMiDQp9DQo==",
61
- "payeeFspCommission": {
62
- "amount": "5",
63
- "currency": "USD"
64
- },
65
- "payeeFspFee": {
66
- "amount": "5",
67
- "currency": "USD"
68
- },
69
- "payeeReceiveAmount": {
70
- "amount": "490",
71
- "currency": "USD"
72
- },
73
- "transferAmount": {
74
- "amount": "500",
75
- "currency": "USD"
67
+ "body": {
68
+ "condition": "fH9pAYDQbmoZLPbvv3CSW2RfjU4jvM4ApG_fqGnR7Xs",
69
+ "expiration": "2017-11-15T14:17:09.663+01:00",
70
+ "extensionList": {
71
+ "extension": [
72
+ {
73
+ "key": "qreskey1",
74
+ "value": "qresvalue1"
75
+ },
76
+ {
77
+ "key": "qreskey2",
78
+ "value": "qresvalue2"
79
+ }
80
+ ]
81
+ },
82
+ "geoCode": {
83
+ "latitude": "53.295971",
84
+ "longitude": "-0.038500"
85
+ },
86
+ "ilpPacket": "AQAAAAAAACasIWcuc2UubW9iaWxlbW9uZXkubXNpc2RuLjEyMzQ1Njc4OYIEIXsNCiAgICAidHJhbnNhY3Rpb25JZCI6ICI4NWZlYWMyZi0zOWIyLTQ5MWItODE3ZS00YTAzMjAzZDRmMTQiLA0KICAgICJxdW90ZUlkIjogIjdjMjNlODBjLWQwNzgtNDA3Ny04MjYzLTJjMDQ3ODc2ZmNmNiIsDQogICAgInBheWVlIjogew0KICAgICAgICAicGFydHlJZEluZm8iOiB7DQogICAgICAgICAgICAicGFydHlJZFR5cGUiOiAiTVNJU0ROIiwNCiAgICAgICAgICAgICJwYXJ0eUlkZW50aWZpZXIiOiAiMTIzNDU2Nzg5IiwNCiAgICAgICAgICAgICJmc3BJZCI6ICJNb2JpbGVNb25leSINCiAgICAgICAgfSwNCiAgICAgICAgInBlcnNvbmFsSW5mbyI6IHsNCiAgICAgICAgICAgICJjb21wbGV4TmFtZSI6IHsNCiAgICAgICAgICAgICAgICAiZmlyc3ROYW1lIjogIkhlbnJpayIsDQogICAgICAgICAgICAgICAgImxhc3ROYW1lIjogIkthcmxzc29uIg0KICAgICAgICAgICAgfQ0KICAgICAgICB9DQogICAgfSwNCiAgICAicGF5ZXIiOiB7DQogICAgICAgICJwZXJzb25hbEluZm8iOiB7DQogICAgICAgICAgICAiY29tcGxleE5hbWUiOiB7DQogICAgICAgICAgICAgICAgImZpcnN0TmFtZSI6ICJNYXRzIiwNCiAgICAgICAgICAgICAgICAibGFzdE5hbWUiOiAiSGFnbWFuIg0KICAgICAgICAgICAgfQ0KICAgICAgICB9LA0KICAgICAgICAicGFydHlJZEluZm8iOiB7DQogICAgICAgICAgICAicGFydHlJZFR5cGUiOiAiSUJBTiIsDQogICAgICAgICAgICAicGFydHlJZGVudGlmaWVyIjogIlNFNDU1MDAwMDAwMDA1ODM5ODI1NzQ2NiIsDQogICAgICAgICAgICAiZnNwSWQiOiAiQmFua05yT25lIg0KICAgICAgICB9DQogICAgfSwNCiAgICAiYW1vdW50Ijogew0KICAgICAgICAiYW1vdW50IjogIjEwMCIsDQogICAgICAgICJjdXJyZW5jeSI6ICJVU0QiDQogICAgfSwNCiAgICAidHJhbnNhY3Rpb25UeXBlIjogew0KICAgICAgICAic2NlbmFyaW8iOiAiVFJBTlNGRVIiLA0KICAgICAgICAiaW5pdGlhdG9yIjogIlBBWUVSIiwNCiAgICAgICAgImluaXRpYXRvclR5cGUiOiAiQ09OU1VNRVIiDQogICAgfSwNCiAgICAibm90ZSI6ICJGcm9tIE1hdHMiDQp9DQo==",
87
+ "payeeFspCommission": {
88
+ "amount": "5",
89
+ "currency": "USD"
90
+ },
91
+ "payeeFspFee": {
92
+ "amount": "5",
93
+ "currency": "USD"
94
+ },
95
+ "payeeReceiveAmount": {
96
+ "amount": "490",
97
+ "currency": "USD"
98
+ },
99
+ "transferAmount": {
100
+ "amount": "500",
101
+ "currency": "USD"
102
+ }
76
103
  }
77
104
  },
78
105
  "quoteResponseSource": "mojaloop-sdk",
@@ -35,7 +35,7 @@ function createGetTransfersTester({ reqInbound, reqOutbound, apiSpecsOutbound })
35
35
 
36
36
  return reqInbound.put(putUrl)
37
37
  .send(putBody)
38
- .set('content-type', 'application/vnd.interoperability.transfers+json;version=1.0')
38
+ .set('content-type', 'application/vnd.interoperability.transfers+json;version=1.1')
39
39
  .set('Date', new Date().toISOString())
40
40
  .set('fspiop-source', 'mojaloop-sdk')
41
41
  .expect(200);
@@ -50,9 +50,49 @@ function createGetTransfersTester({ reqInbound, reqOutbound, apiSpecsOutbound })
50
50
  const res = await reqOutbound.get(`/transfers/${TRANSFER_ID}`);
51
51
  const {body} = res;
52
52
  expect(res.statusCode).toEqual(responseCode);
53
+
54
+ // remove elements of the response we do not want/need to compare for correctness.
55
+ // timestamps on requests/responses for example will be set by the HTTP framework
56
+ // and we dont want to compare against static values.
53
57
  delete body.initiatedTimestamp;
54
58
  if (body.transferState) {
55
59
  delete body.transferState.initiatedTimestamp;
60
+ if(body.transferState.quoteResponse) {
61
+ delete body.transferState.quoteResponse.headers;
62
+ }
63
+ if(body.transferState.getPartiesResponse) {
64
+ delete body.transferState.getPartiesResponse.headers;
65
+ }
66
+ if(body.transferState.fulfil) {
67
+ delete body.transferState.fulfil.headers;
68
+ }
69
+ if(body.transferState.quoteRequest) {
70
+ delete body.transferState.quoteRequest;
71
+ }
72
+ if(body.transferState.getPartiesRequest) {
73
+ delete body.transferState.getPartiesRequest;
74
+ }
75
+ if(body.transferState.prepare) {
76
+ delete body.transferState.prepare;
77
+ }
78
+ }
79
+ if(body.quoteResponse) {
80
+ delete body.quoteResponse.headers;
81
+ }
82
+ if(body.getPartiesResponse) {
83
+ delete body.getPartiesResponse.headers;
84
+ }
85
+ if(body.fulfil) {
86
+ delete body.fulfil.headers;
87
+ }
88
+ if(body.quoteRequest) {
89
+ delete body.quoteRequest;
90
+ }
91
+ if(body.getPartiesRequest) {
92
+ delete body.getPartiesRequest;
93
+ }
94
+ if(body.prepare) {
95
+ delete body.prepare;
56
96
  }
57
97
  expect(body).toEqual(responseBody);
58
98
  const responseValidator = new OpenAPIResponseValidator(apiSpecsOutbound.paths['/transfers/{transferId}'].get);
@@ -108,17 +148,17 @@ function createPostTransfersTester(
108
148
  if (urlPath.startsWith('/parties/')) {
109
149
  putBody = await Promise.resolve(bodyFn.parties.put());
110
150
  putUrl = urlPath;
111
- contentType = 'application/vnd.interoperability.parties+json;version=1.0';
151
+ contentType = 'application/vnd.interoperability.parties+json;version=1.1';
112
152
  } else if (urlPath === '/quotes') {
113
153
  expect(body).toEqual(bodyFn.quotes.post(body));
114
154
  putBody = await Promise.resolve(bodyFn.quotes.put(body));
115
155
  putUrl = `/quotes/${body.quoteId}`;
116
- contentType = 'application/vnd.interoperability.quotes+json;version=1.0';
156
+ contentType = 'application/vnd.interoperability.quotes+json;version=1.1';
117
157
  } else if (urlPath === '/transfers') {
118
158
  expect(body).toEqual(bodyFn.transfers.post(body));
119
159
  putBody = await Promise.resolve(bodyFn.transfers.put(body));
120
160
  putUrl = `/transfers/${body.transferId}`;
121
- contentType = 'application/vnd.interoperability.transfers+json;version=1.0';
161
+ contentType = 'application/vnd.interoperability.transfers+json;version=1.1';
122
162
  } else {
123
163
  throw new Error(`Unexpected url ${urlPath}`);
124
164
  }
@@ -162,10 +202,51 @@ function createPostTransfersTester(
162
202
 
163
203
  const res = await reqOutbound.post('/transfers').send(postTransfersSimpleBody);
164
204
  const {body} = res;
205
+ console.log(body)
165
206
  expect(res.statusCode).toEqual(responseCode);
207
+
208
+ // remove elements of the response we do not want/need to compare for correctness.
209
+ // timestamps on requests/responses for example will be set by the HTTP framework
210
+ // and we dont want to compare against static values.
166
211
  delete body.initiatedTimestamp;
167
212
  if (body.transferState) {
168
213
  delete body.transferState.initiatedTimestamp;
214
+ if(body.transferState.quoteResponse) {
215
+ delete body.transferState.quoteResponse.headers;
216
+ }
217
+ if(body.transferState.getPartiesResponse) {
218
+ delete body.transferState.getPartiesResponse.headers;
219
+ }
220
+ if(body.transferState.fulfil) {
221
+ delete body.transferState.fulfil.headers;
222
+ }
223
+ if(body.transferState.quoteRequest) {
224
+ delete body.transferState.quoteRequest;
225
+ }
226
+ if(body.transferState.getPartiesRequest) {
227
+ delete body.transferState.getPartiesRequest;
228
+ }
229
+ if(body.transferState.prepare) {
230
+ delete body.transferState.prepare;
231
+ }
232
+ }
233
+ if(body.quoteResponse) {
234
+ delete body.quoteResponse.headers;
235
+ }
236
+ if(body.getPartiesResponse) {
237
+ delete body.getPartiesResponse.headers;
238
+ }
239
+ if(body.fulfil) {
240
+ delete body.fulfil.headers;
241
+ }
242
+ if(body.quoteRequest) {
243
+ delete body.quoteRequest;
244
+ }
245
+ if(body.getPartiesRequest) {
246
+ delete body.getPartiesRequest;
247
+ }
248
+ if(body.prepare) {
249
+ delete body.prepare;
169
250
  }
170
251
  expect(body).toEqual(responseBody);
171
252
  const responseValidator = new OpenAPIResponseValidator(apiSpecsOutbound.paths['/transfers'].post);
@@ -6,6 +6,7 @@ const Validate = require('~/lib/validate');
6
6
 
7
7
  const InboundServer = require('~/InboundServer');
8
8
  const OutboundServer = require('~/OutboundServer');
9
+ const { MetricsClient } = require('~/lib/metrics');
9
10
  const { Logger } = require('@mojaloop/sdk-standard-components');
10
11
  const Cache = require('~/lib/cache');
11
12
 
@@ -47,7 +48,9 @@ const createTestServers = async (config) => {
47
48
  });
48
49
  await cache.connect();
49
50
  defConfig.requestProcessingTimeoutSeconds = 2;
50
- const serverOutbound = new OutboundServer(defConfig, logger, cache);
51
+ const metricsClient = new MetricsClient();
52
+ metricsClient._prometheusRegister.clear();
53
+ const serverOutbound = new OutboundServer(defConfig, logger, cache, metricsClient);
51
54
  await serverOutbound.start();
52
55
  const reqOutbound = supertest(serverOutbound._server);
53
56
 
@@ -68,7 +68,7 @@ describe('config', () => {
68
68
  fs.writeFileSync(cert, certContent);
69
69
  process.env.IN_SERVER_CERT_PATH = cert;
70
70
  const config = require('~/config');
71
- const content = config.mutualTLS.inboundRequests.creds.cert.toString();
71
+ const content = config.inbound.tls.creds.cert.toString();
72
72
  expect(content).toBe(certContent);
73
73
  });
74
74
 
@@ -84,7 +84,7 @@ describe('config', () => {
84
84
  certs.forEach((cert, index) => fs.writeFileSync(cert, certContent[index]));
85
85
  process.env.IN_CA_CERT_PATH = certs.join(',');
86
86
  const config = require('~/config');
87
- const content = config.mutualTLS.inboundRequests.creds.ca.map(ca => ca.toString());
87
+ const content = config.inbound.tls.creds.ca.map(ca => ca.toString());
88
88
  expect(content).toStrictEqual(certContent);
89
89
  });
90
90
 
@@ -1,4 +1,5 @@
1
1
  {
2
+ "content-type": "application/vnd.interoperability.parties+json;version=1.1",
2
3
  "fspiop-source": "other-dfsp",
3
4
  "fspiop-destination": "mojaloop-sdk",
4
5
  "fspiop-signature": "{\"signature\":\"aTTa1TTCBJA1K1VoEFgpSicWYU0q1VYXV-bjkk7uoeNicog7QSp9_AbwtYm4u8NJ1HFM_3mekE8wioAs5YNugnTlJ1k-q4Ouvp5Jo3ZnozoPVtnLaqdhxRMUBOHfDp0X8eCHEo7lETjKcCcH4r5_KT_9Vwx5TMytoG_y9Be8PpviJFkOqOV5jCeIl7XzL_pZQoY0pRJdkXDzYpXDu-HTYKr8ckxWQzx4HO-viJQd2ByQkbqPfQom9IQaAX1t4yztCCpOQn1LY9j9sbfEX9RPXG3UbY6UyDsNjUKYP9BAhXwI9pFWlgv2i9FvEtay2QYdwbW7XEpIiGZ_vi5d6yc12w\",\"protectedHeader\":\"eyJhbGciOiJSUzI1NiIsIkZTUElPUC1VUkkiOiIvcGFydGllcy9NU0lTRE4vMTIzNDU2Nzg5IiwiRlNQSU9QLUhUVFAtTWV0aG9kIjoiUFVUIiwiRlNQSU9QLVNvdXJjZSI6InNpbSIsIkZTUElPUC1EZXN0aW5hdGlvbiI6ImRmc3AiLCJEYXRlIjoiVGh1LCAzMSBPY3QgMjAxOSAxMTo0MTo0MyBHTVQifQ\"}",
@@ -1,15 +1,28 @@
1
1
  {
2
- "mutualTLS": {
3
- "inboundRequests": {
4
- "enabled": false,
2
+ "control": {},
3
+ "test": {
4
+ "tls": {
5
+ "mutualTLS": { "enabled": false },
5
6
  "creds": {
6
7
  "ca": null,
7
8
  "cert": null,
8
9
  "key": null
9
10
  }
10
- },
11
- "outboundRequests": {
12
- "enabled": false,
11
+ }
12
+ },
13
+ "inbound": {
14
+ "tls": {
15
+ "mutualTLS": { "enabled": false },
16
+ "creds": {
17
+ "ca": null,
18
+ "cert": null,
19
+ "key": null
20
+ }
21
+ }
22
+ },
23
+ "outbound": {
24
+ "tls": {
25
+ "mutualTLS": { "enabled": false },
13
26
  "creds": {
14
27
  "ca": null,
15
28
  "cert": null,
@@ -54,5 +67,8 @@
54
67
  "rejectExpiredQuoteResponses": false,
55
68
  "rejectExpiredTransferFulfils": false,
56
69
  "rejectTransfersOnExpiredQuotes": false,
57
- "logIndent": 2
70
+ "logIndent": 2,
71
+ "metrics": {
72
+ "port": 4004
73
+ }
58
74
  }
@@ -58,7 +58,7 @@ describe('Inbound API handlers:', () => {
58
58
  await expect(handlers['/quotes'].post(mockContext)).resolves.toBe(undefined);
59
59
 
60
60
  expect(quoteRequestSpy).toHaveBeenCalledTimes(1);
61
- expect(quoteRequestSpy.mock.calls[0][0]).toBe(mockContext.request.body);
61
+ expect(quoteRequestSpy.mock.calls[0][0]).toStrictEqual(mockContext.request);
62
62
  expect(quoteRequestSpy.mock.calls[0][1]).toBe(mockContext.request.headers['fspiop-source']);
63
63
  });
64
64
 
@@ -138,7 +138,10 @@ describe('Inbound API handlers:', () => {
138
138
  expect(triggerDeferredJobSpy).toHaveBeenCalledTimes(1);
139
139
  expect(triggerDeferredJobSpy).toBeCalledWith({
140
140
  cache: mockContext.state.cache,
141
- message: mockContext.request.body,
141
+ message: {
142
+ body: mockContext.request.body,
143
+ headers: mockContext.request.headers,
144
+ },
142
145
  args: {
143
146
  quoteId: mockContext.state.path.params.ID
144
147
  }
@@ -215,8 +218,10 @@ describe('Inbound API handlers:', () => {
215
218
  expect(bulkQuotesSpy).toHaveBeenCalledTimes(1);
216
219
  expect(bulkQuotesSpy.mock.calls[0][1]).toMatchObject({
217
220
  type: 'bulkQuoteResponse',
218
- data: mockContext.request.body,
219
- headers: mockContext.request.headers
221
+ data: {
222
+ body: mockContext.request.body,
223
+ headers: mockContext.request.headers,
224
+ }
220
225
  });
221
226
  });
222
227
  });
@@ -263,7 +268,10 @@ describe('Inbound API handlers:', () => {
263
268
  expect(bulkQuotesSpy).toHaveBeenCalledTimes(1);
264
269
  expect(bulkQuotesSpy.mock.calls[0][1]).toMatchObject({
265
270
  type: 'bulkQuoteResponseError',
266
- data: mockContext.request.body
271
+ data: {
272
+ body: mockContext.request.body,
273
+ headers: mockContext.request.headers,
274
+ }
267
275
  });
268
276
  });
269
277
  });
@@ -372,8 +380,10 @@ describe('Inbound API handlers:', () => {
372
380
  expect(bulkTransfersSpy).toHaveBeenCalledTimes(1);
373
381
  expect(bulkTransfersSpy.mock.calls[0][1]).toMatchObject({
374
382
  type: 'bulkTransferResponse',
375
- data: mockContext.request.body,
376
- headers: mockContext.request.headers
383
+ data: {
384
+ body: mockContext.request.body,
385
+ headers: mockContext.request.headers,
386
+ }
377
387
  });
378
388
  });
379
389
  });
@@ -420,7 +430,10 @@ describe('Inbound API handlers:', () => {
420
430
  expect(bulkTransfersSpy).toHaveBeenCalledTimes(1);
421
431
  expect(bulkTransfersSpy.mock.calls[0][1]).toMatchObject({
422
432
  type: 'bulkTransferResponseError',
423
- data: mockContext.request.body
433
+ data: {
434
+ body: mockContext.request.body,
435
+ headers: mockContext.request.headers,
436
+ }
424
437
  });
425
438
  });
426
439
  });
@@ -601,7 +614,10 @@ describe('Inbound API handlers:', () => {
601
614
  expect(triggerDeferredJobSpy).toHaveBeenCalledTimes(1);
602
615
  expect(triggerDeferredJobSpy).toBeCalledWith({
603
616
  cache: mockContext.state.cache,
604
- message: mockContext.request.body,
617
+ message: {
618
+ body: mockContext.request.body,
619
+ headers: mockContext.request.headers,
620
+ },
605
621
  args: {
606
622
  type: mockContext.state.path.params.Type,
607
623
  id: mockContext.state.path.params.ID
@@ -620,7 +636,10 @@ describe('Inbound API handlers:', () => {
620
636
  expect(triggerDeferredJobSpy).toHaveBeenCalledTimes(1);
621
637
  expect(triggerDeferredJobSpy).toBeCalledWith({
622
638
  cache: mockContext.state.cache,
623
- message: mockContext.request.body,
639
+ message: {
640
+ body: mockContext.request.body,
641
+ headers: mockContext.request.headers,
642
+ },
624
643
  args: {
625
644
  type: mockContext.state.path.params.Type,
626
645
  id: mockContext.state.path.params.ID,
@@ -667,7 +686,10 @@ describe('Inbound API handlers:', () => {
667
686
  expect(triggerDeferredJobSpy).toHaveBeenCalledTimes(1);
668
687
  expect(triggerDeferredJobSpy).toBeCalledWith({
669
688
  cache: mockContext.state.cache,
670
- message: mockContext.request.body,
689
+ message: {
690
+ body: mockContext.request.body,
691
+ headers: mockContext.request.headers,
692
+ },
671
693
  args: {
672
694
  transferId: mockContext.state.path.params.ID
673
695
  }
@@ -682,7 +704,10 @@ describe('Inbound API handlers:', () => {
682
704
  expect(triggerDeferredJobSpy).toHaveBeenCalledTimes(1);
683
705
  expect(triggerDeferredJobSpy).toBeCalledWith({
684
706
  cache: mockContext.state.cache,
685
- message: mockContext.request.body,
707
+ message: {
708
+ body: mockContext.request.body,
709
+ headers: mockContext.request.headers,
710
+ },
686
711
  args: {
687
712
  transferId: mockContext.state.path.params.ID,
688
713
  }
@@ -728,7 +753,10 @@ describe('Inbound API handlers:', () => {
728
753
  expect(triggerDeferredJobSpy).toHaveBeenCalledTimes(1);
729
754
  expect(triggerDeferredJobSpy).toBeCalledWith({
730
755
  cache: mockContext.state.cache,
731
- message: mockContext.request.body,
756
+ message: {
757
+ body: mockContext.request.body,
758
+ headers: mockContext.request.headers,
759
+ },
732
760
  args: {
733
761
  transferId: mockContext.state.path.params.ID
734
762
  }
@@ -743,7 +771,10 @@ describe('Inbound API handlers:', () => {
743
771
  expect(triggerDeferredJobSpy).toHaveBeenCalledTimes(1);
744
772
  expect(triggerDeferredJobSpy).toBeCalledWith({
745
773
  cache: mockContext.state.cache,
746
- message: mockContext.request.body,
774
+ message: {
775
+ body: mockContext.request.body,
776
+ headers: mockContext.request.headers,
777
+ },
747
778
  args: {
748
779
  transferId: mockContext.state.path.params.ID,
749
780
  }
@@ -10,13 +10,18 @@
10
10
 
11
11
  'use strict';
12
12
 
13
- const { Logger } = require('@mojaloop/sdk-standard-components');
14
- const defaultConfig = require('./data/defaultConfig');
15
-
16
13
  jest.mock('dotenv', () => ({
17
14
  config: jest.fn()
18
15
  }));
19
16
 
17
+ const promClient = require('prom-client');
18
+ const defaultConfig = require('./data/defaultConfig.json');
19
+ const { Logger } = require('@mojaloop/sdk-standard-components');
20
+ const { MetricsClient } = require('~/lib/metrics');
21
+
22
+ const TestControlServer = require('./ControlServer');
23
+
24
+
20
25
  process.env.PEER_ENDPOINT = '172.17.0.3:4000';
21
26
  process.env.BACKEND_ENDPOINT = '172.17.0.5:4000';
22
27
  process.env.CACHE_HOST = '172.17.0.2';
@@ -26,6 +31,10 @@ process.env.MGMT_API_WS_URL = '0.0.0.0';
26
31
  const index = require('~/index.js');
27
32
 
28
33
  describe('index.js', () => {
34
+ beforeEach(() => {
35
+ promClient.register.clear();
36
+ });
37
+
29
38
  test('WSO2 error events in OutboundServer propagate to top-level server', () => {
30
39
  const logger = new Logger.Logger({ stringify: () => '' });
31
40
  const svr = new index.Server(defaultConfig, logger);
@@ -50,7 +59,89 @@ describe('index.js', () => {
50
59
  expect(typeof(index.OutboundServerMiddleware)).toBe('object');
51
60
  expect(typeof(index.Router)).toBe('function');
52
61
  expect(typeof(index.Validate)).toBe('function');
53
- expect(typeof(index.RandomPhrase)).toBe('function');
54
62
  expect(typeof(index.Cache)).toBe('function');
55
63
  });
56
64
  });
65
+
66
+ describe('Server', () => {
67
+ let server, controlServer, conf, logger;
68
+
69
+ beforeEach(async () => {
70
+ promClient.register.clear();
71
+ logger = new Logger.Logger({ stringify: () => '' });
72
+ conf = JSON.parse(JSON.stringify(defaultConfig));
73
+ conf.enableTestFeatures = true;
74
+ conf.pm4mlEnabled = true;
75
+ conf.control.mgmtAPIWsUrl = 'localhost';
76
+ conf.control.mgmtAPIWsPort = 4005;
77
+ conf.control.port = conf.control.mgmtAPIWsPort;
78
+ controlServer = new TestControlServer.Server({ logger, appConfig: conf });
79
+ server = new index.Server(conf, logger);
80
+ await server.start();
81
+ });
82
+
83
+ afterEach(async () => {
84
+ await controlServer.stop();
85
+ await server.stop();
86
+ });
87
+
88
+ describe('is reconfigured correctly by the control client', () => {
89
+ let newConf;
90
+ beforeEach(async () => {
91
+ // not every server restarts on every config change, we'll make sure they all restart
92
+ newConf = { ...conf, logIndent: conf.logIndent + 1, control: { ...conf.control, rubbish: 'data' }, test: { trash: 'data' } };
93
+ // Just in case, we'll assert the new configuration is different to the old one
94
+ expect(newConf).not.toEqual(conf);
95
+ });
96
+
97
+ it('reconfigures and restarts constituent servers when triggered by control client', async () => {
98
+ const [restartInbound, restartOutbound, restartControl, restartOAuthTest, restartTest] =
99
+ Array.from({ length: 5 }).map(() => jest.fn());
100
+ server.inboundServer.reconfigure = jest.fn(() => restartInbound);
101
+ server.outboundServer.reconfigure = jest.fn(() => restartOutbound);
102
+ server.testServer.reconfigure = jest.fn(() => restartTest);
103
+ server.oauthTestServer.reconfigure = jest.fn(() => restartOAuthTest);
104
+ server.controlClient.reconfigure = jest.fn(() => restartControl);
105
+
106
+ await controlServer.broadcastConfigChange(newConf);
107
+
108
+ // We wait for the servers to get restarted
109
+ await new Promise((wait) => setTimeout(wait, 1000));
110
+
111
+ expect(server.inboundServer.reconfigure).toHaveBeenCalledTimes(1);
112
+ expect(server.inboundServer.reconfigure).toHaveBeenCalledWith(
113
+ newConf, expect.any(Logger.Logger), expect.any(index.Cache)
114
+ );
115
+ expect(server.outboundServer.reconfigure).toHaveBeenCalledTimes(1);
116
+ const metricsClient = new MetricsClient();
117
+ expect(server.outboundServer.reconfigure).toHaveBeenCalledWith(
118
+ newConf, expect.any(Logger.Logger), expect.any(index.Cache), metricsClient
119
+ );
120
+ expect(server.controlClient.reconfigure).toHaveBeenCalledTimes(1);
121
+ expect(server.controlClient.reconfigure).toHaveBeenCalledWith({
122
+ logger: expect.any(Logger.Logger),
123
+ port: newConf.control.port,
124
+ appConfig: newConf
125
+ });
126
+ expect(server.testServer.reconfigure).toHaveBeenCalledTimes(1);
127
+ expect(server.testServer.reconfigure).toHaveBeenCalledWith({
128
+ logger: expect.any(Logger.Logger),
129
+ cache: expect.any(index.Cache),
130
+ port: newConf.test.port
131
+ });
132
+ expect(server.oauthTestServer.reconfigure).toHaveBeenCalledTimes(1);
133
+ expect(server.oauthTestServer.reconfigure).toHaveBeenCalledWith({
134
+ logger: expect.any(Logger.Logger),
135
+ clientKey: newConf.oauthTestServer.clientKey,
136
+ clientSecret: newConf.oauthTestServer.clientSecret,
137
+ port: newConf.oauthTestServer.listenPort,
138
+ });
139
+
140
+ expect(restartInbound).toHaveBeenCalledTimes(1);
141
+ expect(restartOutbound).toHaveBeenCalledTimes(1);
142
+ expect(restartTest).toHaveBeenCalledTimes(1);
143
+ expect(restartOAuthTest).toHaveBeenCalledTimes(1);
144
+ expect(restartControl).toHaveBeenCalledTimes(1);
145
+ });
146
+ });
147
+ });
@@ -51,14 +51,17 @@ describe('AccountsModel', () => {
51
51
  const response = {
52
52
  type: 'accountsCreationSuccessfulResponse',
53
53
  data: {
54
- partyList: request.partyList.map(party => ({
55
- partyId: party,
56
- // errorInformation: null
57
- })),
58
- currency: request.currency,
54
+ body: {
55
+ partyList: request.partyList.map(party => ({
56
+ partyId: party,
57
+ // errorInformation: null
58
+ })),
59
+ currency: request.currency,
60
+ },
61
+ headers: {}
59
62
  },
60
63
  };
61
- cache.publish(`ac_${request.requestId}`, JSON.stringify(response));
64
+ cache.publish(`ac_${request.requestId}`, response);
62
65
  return Promise.resolve();
63
66
  });
64
67