@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
|
@@ -28,18 +28,19 @@ const middlewares = require('./middlewares');
|
|
|
28
28
|
const endpointRegex = /\/.*/g;
|
|
29
29
|
|
|
30
30
|
class OutboundApi extends EventEmitter {
|
|
31
|
-
constructor(conf, logger, cache, validator) {
|
|
31
|
+
constructor(conf, logger, cache, validator, metricsClient) {
|
|
32
32
|
super({ captureExceptions: true });
|
|
33
33
|
this._logger = logger;
|
|
34
34
|
this._api = new Koa();
|
|
35
35
|
this._conf = conf;
|
|
36
36
|
this._cache = cache;
|
|
37
|
+
this._metricsClient = metricsClient;
|
|
37
38
|
|
|
38
39
|
this._wso2 = {
|
|
39
40
|
auth: new WSO2Auth({
|
|
40
41
|
...this._conf.wso2.auth,
|
|
41
42
|
logger: this._logger,
|
|
42
|
-
tlsCreds: this._conf.mutualTLS.
|
|
43
|
+
tlsCreds: this._conf.outbound.tls.mutualTLS.enabled && this._conf.outbound.tls.creds,
|
|
43
44
|
}),
|
|
44
45
|
retryWso2AuthFailureTimes: conf.wso2.requestAuthFailureRetryTimes,
|
|
45
46
|
};
|
|
@@ -54,7 +55,7 @@ class OutboundApi extends EventEmitter {
|
|
|
54
55
|
this._api.use(middlewares.createErrorHandler(this._logger));
|
|
55
56
|
this._api.use(middlewares.createRequestIdGenerator());
|
|
56
57
|
this._api.use(koaBody()); // outbound always expects application/json
|
|
57
|
-
this._api.use(middlewares.applyState({ cache, wso2: this._wso2, conf }));
|
|
58
|
+
this._api.use(middlewares.applyState({ cache, wso2: this._wso2, conf, metricsClient }));
|
|
58
59
|
this._api.use(middlewares.createLogger(this._logger));
|
|
59
60
|
|
|
60
61
|
//Note that we strip off any path on peerEndpoint config after the origin.
|
|
@@ -67,7 +68,7 @@ class OutboundApi extends EventEmitter {
|
|
|
67
68
|
proxyConfig: conf.proxyConfig,
|
|
68
69
|
logger: this._logger,
|
|
69
70
|
wso2Auth: this._wso2.auth,
|
|
70
|
-
tls: conf.
|
|
71
|
+
tls: conf.outbound.tls,
|
|
71
72
|
}));
|
|
72
73
|
}
|
|
73
74
|
|
|
@@ -91,7 +92,7 @@ class OutboundApi extends EventEmitter {
|
|
|
91
92
|
}
|
|
92
93
|
|
|
93
94
|
class OutboundServer extends EventEmitter {
|
|
94
|
-
constructor(conf, logger, cache) {
|
|
95
|
+
constructor(conf, logger, cache, metricsClient) {
|
|
95
96
|
super({ captureExceptions: true });
|
|
96
97
|
this._validator = new Validate();
|
|
97
98
|
this._conf = conf;
|
|
@@ -101,7 +102,8 @@ class OutboundServer extends EventEmitter {
|
|
|
101
102
|
conf,
|
|
102
103
|
this._logger.push({ component: 'api' }),
|
|
103
104
|
cache,
|
|
104
|
-
this._validator
|
|
105
|
+
this._validator,
|
|
106
|
+
metricsClient
|
|
105
107
|
);
|
|
106
108
|
this._api.on('error', (...args) => {
|
|
107
109
|
this.emit('error', ...args);
|
|
@@ -111,14 +113,11 @@ class OutboundServer extends EventEmitter {
|
|
|
111
113
|
|
|
112
114
|
async start() {
|
|
113
115
|
await this._api.start();
|
|
114
|
-
|
|
115
116
|
const specPath = path.join(__dirname, 'api.yaml');
|
|
116
117
|
const apiSpecs = yaml.load(fs.readFileSync(specPath));
|
|
117
118
|
await this._validator.initialise(apiSpecs);
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
this._logger.log(`Serving outbound API on port ${this._conf.outboundServerPort}`);
|
|
119
|
+
await new Promise((resolve) => this._server.listen(this._conf.outbound.port, resolve));
|
|
120
|
+
this._logger.log(`Serving outbound API on port ${this._conf.outbound.port}`);
|
|
122
121
|
}
|
|
123
122
|
|
|
124
123
|
async stop() {
|
package/src/config.js
CHANGED
|
@@ -58,17 +58,29 @@ const env = from(process.env, {
|
|
|
58
58
|
|
|
59
59
|
module.exports = {
|
|
60
60
|
__parseResourceVersion: parseResourceVersions,
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
61
|
+
control: {
|
|
62
|
+
mgmtAPIWsUrl: env.get('MGMT_API_WS_URL').default('127.0.0.1').asString(),
|
|
63
|
+
mgmtAPIWsPort: env.get('MGMT_API_WS_PORT').default('4005').asPortNumber()
|
|
64
|
+
},
|
|
65
|
+
inbound: {
|
|
66
|
+
port: env.get('INBOUND_LISTEN_PORT').default('4000').asPortNumber(),
|
|
67
|
+
tls: {
|
|
68
|
+
mutualTLS: {
|
|
69
|
+
enabled: env.get('INBOUND_MUTUAL_TLS_ENABLED').default('false').asBool(),
|
|
70
|
+
},
|
|
64
71
|
creds: {
|
|
65
72
|
ca: env.get('IN_CA_CERT_PATH').asFileListContent(),
|
|
66
73
|
cert: env.get('IN_SERVER_CERT_PATH').asFileContent(),
|
|
67
74
|
key: env.get('IN_SERVER_KEY_PATH').asFileContent(),
|
|
68
75
|
},
|
|
69
76
|
},
|
|
70
|
-
|
|
71
|
-
|
|
77
|
+
},
|
|
78
|
+
outbound: {
|
|
79
|
+
port: env.get('OUTBOUND_LISTEN_PORT').default('4001').asPortNumber(),
|
|
80
|
+
tls: {
|
|
81
|
+
mutualTLS: {
|
|
82
|
+
enabled: env.get('OUTBOUND_MUTUAL_TLS_ENABLED').default('false').asBool(),
|
|
83
|
+
},
|
|
72
84
|
creds: {
|
|
73
85
|
ca: env.get('OUT_CA_CERT_PATH').asFileListContent(),
|
|
74
86
|
cert: env.get('OUT_CLIENT_CERT_PATH').asFileContent(),
|
|
@@ -76,9 +88,9 @@ module.exports = {
|
|
|
76
88
|
},
|
|
77
89
|
},
|
|
78
90
|
},
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
91
|
+
test: {
|
|
92
|
+
port: env.get('TEST_LISTEN_PORT').default('4002').asPortNumber(),
|
|
93
|
+
},
|
|
82
94
|
peerEndpoint: env.get('PEER_ENDPOINT').required().asString(),
|
|
83
95
|
alsEndpoint: env.get('ALS_ENDPOINT').asString(),
|
|
84
96
|
quotesEndpoint: env.get('QUOTES_ENDPOINT').asString(),
|
|
@@ -93,6 +105,9 @@ module.exports = {
|
|
|
93
105
|
checkIlp: env.get('CHECK_ILP').default('true').asBool(),
|
|
94
106
|
expirySeconds: env.get('EXPIRY_SECONDS').default('60').asIntPositive(),
|
|
95
107
|
|
|
108
|
+
multiplePartiesResponse: env.get('MULTIPLE_PARTIES_RESPONSE').default('false').asBool(),
|
|
109
|
+
multiplePartiesResponseSeconds: env.get('MULTIPLE_PARTIES_RESPONSE_SECONDS').default('30').asIntPositive(),
|
|
110
|
+
|
|
96
111
|
autoAcceptQuotes: env.get('AUTO_ACCEPT_QUOTES').default('true').asBool(),
|
|
97
112
|
autoAcceptParty: env.get('AUTO_ACCEPT_PARTY').default('true').asBool(),
|
|
98
113
|
autoAcceptR2PBusinessQuotes: env.get('AUTO_ACCEPT_R2P_BUSINESS_QUOTES').default('false').asBool(),
|
|
@@ -153,17 +168,19 @@ module.exports = {
|
|
|
153
168
|
|
|
154
169
|
proxyConfig: env.get('PROXY_CONFIG_PATH').asYamlConfig(),
|
|
155
170
|
reserveNotification: env.get('RESERVE_NOTIFICATION').default('false').asBool(),
|
|
171
|
+
sendFinalNotificationIfRequested: env.get('SEND_FINAL_NOTIFICATION_IF_REQUESTED').default('false').asBool(),
|
|
172
|
+
|
|
156
173
|
// resourceVersions config should be string in format: "resourceOneName=1.0,resourceTwoName=1.1"
|
|
157
174
|
resourceVersions: env.get('RESOURCE_VERSIONS').default('').asResourceVersions(),
|
|
158
175
|
|
|
176
|
+
metrics: {
|
|
177
|
+
port: env.get('METRICS_SERVER_LISTEN_PORT').default('4004').asPortNumber()
|
|
178
|
+
},
|
|
179
|
+
|
|
159
180
|
// in 3PPI DFSP's generate their own `transferId` which is associated with
|
|
160
181
|
// a transactionRequestId. this option decodes the ilp packet for
|
|
161
182
|
// the `transactionId` to retrieve the quote from cache
|
|
162
183
|
allowDifferentTransferTransactionId: env.get('ALLOW_DIFFERENT_TRANSFER_TRANSACTION_ID').default('false').asBool(),
|
|
163
184
|
|
|
164
185
|
pm4mlEnabled: env.get('PM4ML_ENABLED').default('false').asBool(),
|
|
165
|
-
control: {
|
|
166
|
-
mgmtAPIWsUrl: env.get('MGMT_API_WS_URL').default('127.0.0.1').asString(),
|
|
167
|
-
mgmtAPIWsPort: env.get('MGMT_API_WS_PORT').default('4005').asPortNumber()
|
|
168
|
-
},
|
|
169
186
|
};
|
package/src/index.js
CHANGED
|
@@ -10,14 +10,18 @@
|
|
|
10
10
|
|
|
11
11
|
'use strict';
|
|
12
12
|
|
|
13
|
+
const assert = require('assert/strict');
|
|
13
14
|
const { hostname } = require('os');
|
|
14
15
|
const config = require('./config');
|
|
15
16
|
const EventEmitter = require('events');
|
|
17
|
+
const _ = require('lodash');
|
|
16
18
|
|
|
17
19
|
const InboundServer = require('./InboundServer');
|
|
18
20
|
const OutboundServer = require('./OutboundServer');
|
|
19
21
|
const OAuthTestServer = require('./OAuthTestServer');
|
|
20
22
|
const TestServer = require('./TestServer');
|
|
23
|
+
const { MetricsServer, MetricsClient } = require('./lib/metrics');
|
|
24
|
+
const ControlAgent = require('./ControlAgent');
|
|
21
25
|
|
|
22
26
|
// import things we want to expose e.g. for unit tests and users who dont want to use the entire
|
|
23
27
|
// scheme adapter as a service
|
|
@@ -25,10 +29,20 @@ const InboundServerMiddleware = require('./InboundServer/middlewares.js');
|
|
|
25
29
|
const OutboundServerMiddleware = require('./OutboundServer/middlewares.js');
|
|
26
30
|
const Router = require('./lib/router');
|
|
27
31
|
const Validate = require('./lib/validate');
|
|
28
|
-
const RandomPhrase = require('./lib/randomphrase');
|
|
29
32
|
const Cache = require('./lib/cache');
|
|
33
|
+
const check = require('./lib/check');
|
|
30
34
|
const { Logger } = require('@mojaloop/sdk-standard-components');
|
|
31
35
|
|
|
36
|
+
const LOG_ID = {
|
|
37
|
+
INBOUND: { app: 'mojaloop-connector-inbound-api' },
|
|
38
|
+
OUTBOUND: { app: 'mojaloop-connector-outbound-api' },
|
|
39
|
+
TEST: { app: 'mojaloop-connector-test-api' },
|
|
40
|
+
OAUTHTEST: { app: 'mojaloop-connector-oauth-test-server' },
|
|
41
|
+
CONTROL: { app: 'mojaloop-connector-control-client' },
|
|
42
|
+
METRICS: { app: 'mojaloop-connector-metrics' },
|
|
43
|
+
CACHE: { component: 'cache' },
|
|
44
|
+
};
|
|
45
|
+
|
|
32
46
|
/**
|
|
33
47
|
* Class that creates and manages http servers that expose the scheme adapter APIs.
|
|
34
48
|
*/
|
|
@@ -39,14 +53,22 @@ class Server extends EventEmitter {
|
|
|
39
53
|
this.logger = logger;
|
|
40
54
|
this.cache = new Cache({
|
|
41
55
|
...conf.cacheConfig,
|
|
42
|
-
logger: this.logger.push(
|
|
56
|
+
logger: this.logger.push(LOG_ID.CACHE),
|
|
43
57
|
enableTestFeatures: conf.enableTestFeatures,
|
|
44
58
|
});
|
|
45
59
|
|
|
60
|
+
this.metricsClient = new MetricsClient();
|
|
61
|
+
|
|
62
|
+
this.metricsServer = new MetricsServer({
|
|
63
|
+
port: this.conf.metrics.port,
|
|
64
|
+
logger: this.logger.push(LOG_ID.METRICS)
|
|
65
|
+
});
|
|
66
|
+
|
|
46
67
|
this.inboundServer = new InboundServer(
|
|
47
68
|
this.conf,
|
|
48
|
-
this.logger.push(
|
|
49
|
-
this.cache
|
|
69
|
+
this.logger.push(LOG_ID.INBOUND),
|
|
70
|
+
this.cache,
|
|
71
|
+
this.metricsClient
|
|
50
72
|
);
|
|
51
73
|
this.inboundServer.on('error', (...args) => {
|
|
52
74
|
this.logger.push({ args }).log('Unhandled error in Inbound Server');
|
|
@@ -55,8 +77,9 @@ class Server extends EventEmitter {
|
|
|
55
77
|
|
|
56
78
|
this.outboundServer = new OutboundServer(
|
|
57
79
|
this.conf,
|
|
58
|
-
this.logger.push(
|
|
59
|
-
this.cache
|
|
80
|
+
this.logger.push(LOG_ID.OUTBOUND),
|
|
81
|
+
this.cache,
|
|
82
|
+
this.metricsClient
|
|
60
83
|
);
|
|
61
84
|
this.outboundServer.on('error', (...args) => {
|
|
62
85
|
this.logger.push({ args }).log('Unhandled error in Outbound Server');
|
|
@@ -67,16 +90,140 @@ class Server extends EventEmitter {
|
|
|
67
90
|
clientKey: this.conf.oauthTestServer.clientKey,
|
|
68
91
|
clientSecret: this.conf.oauthTestServer.clientSecret,
|
|
69
92
|
port: this.conf.oauthTestServer.listenPort,
|
|
70
|
-
logger: this.logger.push(
|
|
93
|
+
logger: this.logger.push(LOG_ID.OAUTHTEST),
|
|
71
94
|
});
|
|
72
95
|
|
|
73
96
|
this.testServer = new TestServer({
|
|
74
|
-
port: this.conf.
|
|
75
|
-
logger: this.logger.push(
|
|
97
|
+
port: this.conf.test.port,
|
|
98
|
+
logger: this.logger.push(LOG_ID.TEST),
|
|
76
99
|
cache: this.cache,
|
|
77
100
|
});
|
|
78
101
|
}
|
|
79
102
|
|
|
103
|
+
async restart(newConf) {
|
|
104
|
+
// Figuring out what config is necessary in each server and component is a pretty big job
|
|
105
|
+
// that we'll have to save for later. For now, when the config changes, we'll restart
|
|
106
|
+
// more than we might have to.
|
|
107
|
+
// We'll do this by:
|
|
108
|
+
// 0. creating a new instance of the logger, if necessary
|
|
109
|
+
// 1. creating a new instance of the cache, if necessary
|
|
110
|
+
// 2. calling the async reconfigure method of each of the servers as necessary- this will
|
|
111
|
+
// return a synchronous function that we can call to swap over the server events and
|
|
112
|
+
// object properties to the new ones. It will:
|
|
113
|
+
// 1. remove the `request` listener for each of the HTTP servers
|
|
114
|
+
// 2. add the new appropriate `request` listener
|
|
115
|
+
// This results in a completely synchronous listener changeover to the new config and
|
|
116
|
+
// therefore hopefully avoids any concurrency issues arising from restarting different
|
|
117
|
+
// servers or components concurrently.
|
|
118
|
+
// TODO: in the sense of being able to reason about the code, it would make some sense to
|
|
119
|
+
// turn the config items or object passed to each server into an event emitter, or pass an
|
|
120
|
+
// additional event emitter to the server constructor for the server to listen to and act
|
|
121
|
+
// on changes. Before this, however, it's probably necessary to ensure each server gets
|
|
122
|
+
// _only_ the config it needs, not the entire config object.
|
|
123
|
+
// Further: it might be possible to use Object.observe for this functionality.
|
|
124
|
+
// TODO: what happens if this is run concurrently? I.e. if it is called twice in rapid
|
|
125
|
+
// succession. This question probably needs to be asked of the reconfigure message on every
|
|
126
|
+
// server.
|
|
127
|
+
// Note that it should be possible to reconfigure ports on a running server by reassigning
|
|
128
|
+
// servers, e.g.
|
|
129
|
+
// this.inboundServer._server = createHttpServer();
|
|
130
|
+
// this.inboundServer._server.listen(newPort);
|
|
131
|
+
// If there are conflicts, for example if the new configuration specifies the new inbound
|
|
132
|
+
// port to be the same value as the old outbound port, this will require either
|
|
133
|
+
// 1. some juggling of HTTP servers, e.g.
|
|
134
|
+
// const oldInboundServer = this.inboundServer._server;
|
|
135
|
+
// this.inboundServer._server = this.outboundServer._server;
|
|
136
|
+
// .. etc.
|
|
137
|
+
// 2. some juggling of sockets between servers, if possible
|
|
138
|
+
// 3. rearchitecting of the servers, perhaps splitting the .start() method on the servers
|
|
139
|
+
// to an .init() and .listen() methods, with the latter optionally taking an HTTP server
|
|
140
|
+
// as argument
|
|
141
|
+
// This _might_ introduce some confusion/complexity for existing websocket clients, but as
|
|
142
|
+
// the event handlers _should_ not be modified this shouldn't be a problem. A careful
|
|
143
|
+
// analysis of this will be necessary.
|
|
144
|
+
assert(newConf.inbound.port === this.conf.inbound.port
|
|
145
|
+
&& newConf.outbound.port === this.conf.outbound.port
|
|
146
|
+
&& newConf.test.port === this.conf.test.port
|
|
147
|
+
&& newConf.oauthTestServer.listenPort === this.conf.oauthTestServer.listenPort
|
|
148
|
+
&& newConf.control.mgmtAPIWsPort === this.conf.control.mgmtAPIWsPort,
|
|
149
|
+
'Cannot reconfigure ports on running server');
|
|
150
|
+
const doNothing = () => {};
|
|
151
|
+
const updateLogger = check.notDeepEqual(newConf.logIndent, this.conf.logIndent);
|
|
152
|
+
if (updateLogger) {
|
|
153
|
+
this.logger = new Logger.Logger({
|
|
154
|
+
context: {
|
|
155
|
+
// If we're running from a Mojaloop helm chart deployment, we'll have a SIM_NAME
|
|
156
|
+
simulator: process.env['SIM_NAME'],
|
|
157
|
+
hostname: hostname(),
|
|
158
|
+
},
|
|
159
|
+
stringify: Logger.buildStringify({ space: this.conf.logIndent }),
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
let oldCache;
|
|
163
|
+
const updateCache = (
|
|
164
|
+
updateLogger ||
|
|
165
|
+
check.notDeepEqual(this.conf.cacheConfig, newConf.cacheConfig) ||
|
|
166
|
+
check.notDeepEqual(this.conf.enableTestFeatures, newConf.enableTestFeatures)
|
|
167
|
+
);
|
|
168
|
+
if (updateCache) {
|
|
169
|
+
oldCache = this.cache;
|
|
170
|
+
this.cache = new Cache({
|
|
171
|
+
...newConf.cacheConfig,
|
|
172
|
+
logger: this.logger.push(LOG_ID.CACHE),
|
|
173
|
+
enableTestFeatures: newConf.enableTestFeatures,
|
|
174
|
+
});
|
|
175
|
+
await this.cache.connect();
|
|
176
|
+
}
|
|
177
|
+
const confChanged = !check.deepEqual(newConf, this.conf);
|
|
178
|
+
// TODO: find better naming than "restart", because that's not really what's happening.
|
|
179
|
+
const [restartInboundServer, restartOutboundServer, restartControlClient] = confChanged
|
|
180
|
+
? await Promise.all([
|
|
181
|
+
this.inboundServer.reconfigure(newConf, this.logger.push(LOG_ID.INBOUND), this.cache),
|
|
182
|
+
this.outboundServer.reconfigure(newConf, this.logger.push(LOG_ID.OUTBOUND), this.cache, this.metricsClient),
|
|
183
|
+
this.controlClient.reconfigure({
|
|
184
|
+
logger: this.logger.push(LOG_ID.CONTROL),
|
|
185
|
+
port: newConf.control.mgmtAPIWsPort,
|
|
186
|
+
appConfig: newConf
|
|
187
|
+
}),
|
|
188
|
+
])
|
|
189
|
+
: [doNothing, doNothing, doNothing];
|
|
190
|
+
const updateOAuthTestServer = (
|
|
191
|
+
updateLogger || check.notDeepEqual(newConf.oauthTestServer, this.conf.oauthTestServer)
|
|
192
|
+
);
|
|
193
|
+
const restartOAuthTestServer = updateOAuthTestServer
|
|
194
|
+
? await this.oauthTestServer.reconfigure({
|
|
195
|
+
clientKey: this.conf.oauthTestServer.clientKey,
|
|
196
|
+
clientSecret: this.conf.oauthTestServer.clientSecret,
|
|
197
|
+
port: this.conf.oauthTestServer.listenPort,
|
|
198
|
+
logger: this.logger.push(LOG_ID.OAUTHTEST),
|
|
199
|
+
})
|
|
200
|
+
: doNothing;
|
|
201
|
+
const updateTestServer = (
|
|
202
|
+
updateLogger || updateCache || check.notDeepEqual(newConf.test.port, this.conf.test.port)
|
|
203
|
+
);
|
|
204
|
+
const restartTestServer = updateTestServer
|
|
205
|
+
? await this.testServer.reconfigure({
|
|
206
|
+
port: newConf.test.port,
|
|
207
|
+
logger: this.logger.push(LOG_ID.TEST),
|
|
208
|
+
cache: this.cache,
|
|
209
|
+
})
|
|
210
|
+
: doNothing;
|
|
211
|
+
// You may not return an async restart function. Perform any required async activity in the
|
|
212
|
+
// reconfigure function and return a sync restart function. See the note at the top of this
|
|
213
|
+
// file.
|
|
214
|
+
[restartTestServer, restartOAuthTestServer, restartInboundServer, restartOutboundServer, restartControlClient]
|
|
215
|
+
.map(f => assert(Promise.resolve(f) !== f, 'Restart functions must be synchronous'));
|
|
216
|
+
restartTestServer();
|
|
217
|
+
restartOAuthTestServer();
|
|
218
|
+
restartInboundServer();
|
|
219
|
+
restartOutboundServer();
|
|
220
|
+
restartControlClient();
|
|
221
|
+
this.conf = newConf;
|
|
222
|
+
await Promise.all([
|
|
223
|
+
oldCache && oldCache.disconnect(),
|
|
224
|
+
]);
|
|
225
|
+
}
|
|
226
|
+
|
|
80
227
|
async start() {
|
|
81
228
|
await this.cache.connect();
|
|
82
229
|
|
|
@@ -84,9 +231,25 @@ class Server extends EventEmitter {
|
|
|
84
231
|
const startOauthTestServer = this.conf.oauthTestServer.enabled
|
|
85
232
|
? this.oauthTestServer.start()
|
|
86
233
|
: null;
|
|
234
|
+
|
|
235
|
+
// We only start the control client if we're running within Mojaloop Payment Manager.
|
|
236
|
+
// The control server is the Payment Manager Management API Service.
|
|
237
|
+
// We only start the client to connect to and listen to the Management API service for
|
|
238
|
+
// management protocol messages e.g configuration changes, certificate updates etc.
|
|
239
|
+
if (this.conf.pm4mlEnabled) {
|
|
240
|
+
this.controlClient = await ControlAgent.Client.Create({
|
|
241
|
+
address: this.conf.control.mgmtAPIWsUrl,
|
|
242
|
+
port: this.conf.control.mgmtAPIWsPort,
|
|
243
|
+
logger: this.logger.push(LOG_ID.CONTROL),
|
|
244
|
+
appConfig: this.conf,
|
|
245
|
+
});
|
|
246
|
+
this.controlClient.on(ControlAgent.EVENT.RECONFIGURE, this.restart.bind(this));
|
|
247
|
+
}
|
|
248
|
+
|
|
87
249
|
await Promise.all([
|
|
88
250
|
this.inboundServer.start(),
|
|
89
251
|
this.outboundServer.start(),
|
|
252
|
+
this.metricsServer.start(),
|
|
90
253
|
startTestServer,
|
|
91
254
|
startOauthTestServer,
|
|
92
255
|
]);
|
|
@@ -98,10 +261,23 @@ class Server extends EventEmitter {
|
|
|
98
261
|
this.outboundServer.stop(),
|
|
99
262
|
this.oauthTestServer.stop(),
|
|
100
263
|
this.testServer.stop(),
|
|
264
|
+
this.controlClient.stop(),
|
|
265
|
+
this.metricsServer.stop(),
|
|
101
266
|
]);
|
|
102
267
|
}
|
|
103
268
|
}
|
|
104
269
|
|
|
270
|
+
/*
|
|
271
|
+
* Call the Connector Manager in Management API to get the updated config
|
|
272
|
+
*/
|
|
273
|
+
async function _GetUpdatedConfigFromMgmtAPI(conf, logger, client) {
|
|
274
|
+
logger.log(`Getting updated config from Management API at ${conf.control.mgmtAPIWsUrl}:${conf.control.mgmtAPIWsPort}...`);
|
|
275
|
+
const clientSendResponse = await client.send(ControlAgent.build.CONFIGURATION.READ());
|
|
276
|
+
logger.log('client send returned:: ', clientSendResponse);
|
|
277
|
+
const responseRead = await client.receive();
|
|
278
|
+
logger.log('client receive returned:: ', responseRead);
|
|
279
|
+
return responseRead.data;
|
|
280
|
+
}
|
|
105
281
|
|
|
106
282
|
if(require.main === module) {
|
|
107
283
|
(async () => {
|
|
@@ -115,6 +291,18 @@ if(require.main === module) {
|
|
|
115
291
|
},
|
|
116
292
|
stringify: Logger.buildStringify({ space: config.logIndent }),
|
|
117
293
|
});
|
|
294
|
+
if(config.pm4mlEnabled) {
|
|
295
|
+
const controlClient = await ControlAgent.Client.Create({
|
|
296
|
+
address: config.control.mgmtAPIWsUrl,
|
|
297
|
+
port: config.control.mgmtAPIWsPort,
|
|
298
|
+
logger: logger,
|
|
299
|
+
appConfig: config,
|
|
300
|
+
});
|
|
301
|
+
const updatedConfigFromMgmtAPI = await _GetUpdatedConfigFromMgmtAPI(config, logger, controlClient);
|
|
302
|
+
logger.log(`updatedConfigFromMgmtAPI: ${JSON.stringify(updatedConfigFromMgmtAPI)}`);
|
|
303
|
+
_.merge(config, updatedConfigFromMgmtAPI);
|
|
304
|
+
controlClient.terminate();
|
|
305
|
+
}
|
|
118
306
|
const svr = new Server(config, logger);
|
|
119
307
|
svr.on('error', (err) => {
|
|
120
308
|
logger.push({ err }).log('Unhandled server error');
|
|
@@ -140,9 +328,9 @@ if(require.main === module) {
|
|
|
140
328
|
// scheme adapter as a service
|
|
141
329
|
module.exports = {
|
|
142
330
|
Cache,
|
|
331
|
+
ControlAgent,
|
|
143
332
|
InboundServerMiddleware,
|
|
144
333
|
OutboundServerMiddleware,
|
|
145
|
-
RandomPhrase,
|
|
146
334
|
Router,
|
|
147
335
|
Server,
|
|
148
336
|
Validate,
|