@itentialopensource/adapter-utils 4.44.9
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/.eslintignore +3 -0
- package/.eslintrc.js +18 -0
- package/.jshintrc +3 -0
- package/CHANGELOG.md +1398 -0
- package/CODE_OF_CONDUCT.md +48 -0
- package/CONTRIBUTING.md +173 -0
- package/LICENSE +201 -0
- package/README.md +12 -0
- package/actionSchema.json +186 -0
- package/error.json +148 -0
- package/index.js +7 -0
- package/lib/connectorRest.js +4083 -0
- package/lib/dbUtil.js +1300 -0
- package/lib/propertyUtil.js +1012 -0
- package/lib/requestHandler.js +1175 -0
- package/lib/restHandler.js +1309 -0
- package/lib/throttle.js +1289 -0
- package/lib/translatorUtil.js +1137 -0
- package/package.json +61 -0
- package/propertiesSchema.json +840 -0
- package/utils/pre-commit.sh +26 -0
- package/utils/setup.js +32 -0
- package/utils/testRunner.js +259 -0
package/error.json
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
{
|
|
2
|
+
"errors": [
|
|
3
|
+
{
|
|
4
|
+
"key": "Unable To Save To Database",
|
|
5
|
+
"icode": "AD.100",
|
|
6
|
+
"displayString": "Error on saving to Database $VARIABLE$",
|
|
7
|
+
"recommendation": "Verify the database connectivity and credentials"
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"key": "Database Error",
|
|
11
|
+
"icode": "AD.101",
|
|
12
|
+
"displayString": "Database Error: $VARIABLE$",
|
|
13
|
+
"recommendation": "Verify the database connectivity, schema and permissions"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"key": "Database Credentials",
|
|
17
|
+
"icode": "AD.102",
|
|
18
|
+
"displayString": "Database Error: Invalid Credentials",
|
|
19
|
+
"recommendation": "Verify the database credentials"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"key": "No Queue Item",
|
|
23
|
+
"icode": "AD.110",
|
|
24
|
+
"displayString": "Queue Item $VARIABLE$ not found in queue",
|
|
25
|
+
"recommendation": "Make sure the queue is working properly"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"key": "Unable To Create Queue",
|
|
29
|
+
"icode": "AD.111",
|
|
30
|
+
"displayString": "Unable to create queue: $VARIABLE$",
|
|
31
|
+
"recommendation": "Verify access to the database, redis or system memory"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"key": "Queue Full",
|
|
35
|
+
"icode": "AD.112",
|
|
36
|
+
"displayString": "Request $VARIABLE$ Transaction $VARIABLE$ rejected - queue full at $VARIABLE$",
|
|
37
|
+
"recommendation": "Make sure the queue is working properly"
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"key": "Unable To Clear Queue",
|
|
41
|
+
"icode": "AD.113",
|
|
42
|
+
"displayString": "Unable to clear queue: $VARIABLE$",
|
|
43
|
+
"recommendation": "Make sure the queue exists"
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"key": "Unable To Claim Turn",
|
|
47
|
+
"icode": "AD.114",
|
|
48
|
+
"displayString": "Request $VARIABLE$ Transaction $VARIABLE$ unable to claim license: $VARIABLE$",
|
|
49
|
+
"recommendation": "Make sure the queue is working properly"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"key": "Unable To Free Turn",
|
|
53
|
+
"icode": "AD.115",
|
|
54
|
+
"displayString": "Request $VARIABLE$ Transaction $VARIABLE$ unable to free license: $VARIABLE$",
|
|
55
|
+
"recommendation": "Make sure the queue is working properly"
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"key": "Item In Wrong State",
|
|
59
|
+
"icode": "AD.116",
|
|
60
|
+
"displayString": "Queue item is in the wrong state: $VARIABLE$ ",
|
|
61
|
+
"recommendation": "Make sure the queue is working properly"
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"key": "Missing Data",
|
|
65
|
+
"icode": "AD.300",
|
|
66
|
+
"displayString": "$VARIABLE$ is required",
|
|
67
|
+
"recommendation": "Please provide the required data"
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"key": "Missing File",
|
|
71
|
+
"icode": "AD.301",
|
|
72
|
+
"displayString": "Can not open file $VARIABLE$",
|
|
73
|
+
"recommendation": "Verify the file exists and has proper permissions"
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"key": "Invalid Action File",
|
|
77
|
+
"icode": "AD.302",
|
|
78
|
+
"displayString": "Invalid action file: $VARIABLE$ in $VARIABLE$",
|
|
79
|
+
"recommendation": "Verify the action file"
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
"key": "Unsupported Protocol",
|
|
83
|
+
"icode": "AD.303",
|
|
84
|
+
"displayString": "Protocol $VARIABLE$ not currently supported",
|
|
85
|
+
"recommendation": "Verify the protocol on the action"
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
"key": "Invalid Schema File",
|
|
89
|
+
"icode": "AD.304",
|
|
90
|
+
"displayString": "Invalid schema file: $VARIABLE$ in $VARIABLE$",
|
|
91
|
+
"recommendation": "Verify the action file"
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"key": "Query Not Translated",
|
|
95
|
+
"icode": "AD.310",
|
|
96
|
+
"displayString": "Query Not Translated",
|
|
97
|
+
"recommendation": "Verify the information passed in the query object"
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
"key": "Payload Not Translated",
|
|
101
|
+
"icode": "AD.311",
|
|
102
|
+
"displayString": "Payload Not Translated",
|
|
103
|
+
"recommendation": "Verify the information passed in the payload object"
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
"key": "Schema Validation Failure",
|
|
107
|
+
"icode": "AD.312",
|
|
108
|
+
"displayString": "Schema validation failed on $VARIABLE$",
|
|
109
|
+
"recommendation": "Verify the information provided is in the correct format with everything required"
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
"key": "Unable To Authenticate",
|
|
113
|
+
"icode": "AD.400",
|
|
114
|
+
"displayString": "Unable to authenticate with $VARIABLE$, response $VARIABLE$",
|
|
115
|
+
"recommendation": "Verify the user credentials and check the response for more information"
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
"key": "Unable To Encode",
|
|
119
|
+
"icode": "AD.401",
|
|
120
|
+
"displayString": "Can not perform base64 encoding on $VARIABLE$",
|
|
121
|
+
"recommendation": "Check the authentication format"
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
"key": "Unable To Get Token",
|
|
125
|
+
"icode": "AD.402",
|
|
126
|
+
"displayString": "Unable to get token for user: $VARIABLE$",
|
|
127
|
+
"recommendation": "Verify the user credentials and the system information"
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
"key": "Error On Request",
|
|
131
|
+
"icode": "AD.500",
|
|
132
|
+
"displayString": "Error $VARIABLE$ received on request",
|
|
133
|
+
"recommendation": "Verify the request is accurate via debug logs and postman"
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
"key": "Request Timeout",
|
|
137
|
+
"icode": "AD.501",
|
|
138
|
+
"displayString": "The Adapter has run out of time for the request",
|
|
139
|
+
"recommendation": "Increase your adapter request.attempt_timeout property"
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
"key": "Caught Exception",
|
|
143
|
+
"icode": "AD.900",
|
|
144
|
+
"displayString": "Caught Exception $VARIABLE$",
|
|
145
|
+
"recommendation": "Evaluate why the exception took place"
|
|
146
|
+
}
|
|
147
|
+
]
|
|
148
|
+
}
|
package/index.js
ADDED
|
@@ -0,0 +1,4083 @@
|
|
|
1
|
+
/* @copyright Itential, LLC 2018 */
|
|
2
|
+
|
|
3
|
+
// Set globals
|
|
4
|
+
/* global adapters brokers g_redis log */
|
|
5
|
+
/* eslint class-methods-use-this:warn */
|
|
6
|
+
/* eslint consistent-return:warn */
|
|
7
|
+
/* eslint import/no-dynamic-require:warn */
|
|
8
|
+
/* eslint no-underscore-dangle: [2, { "allow": ["_id"] }] */
|
|
9
|
+
/* eslint no-unused-vars:warn */
|
|
10
|
+
/* eslint no-use-before-define:warn */
|
|
11
|
+
/* eslint prefer-destructuring:warn */
|
|
12
|
+
/* eslint prefer-object-spread:warn */
|
|
13
|
+
/* eslint no-param-reassign:warn */
|
|
14
|
+
|
|
15
|
+
/* NodeJS internal API utilities */
|
|
16
|
+
const uuid = require('uuid');
|
|
17
|
+
const os = require('os');
|
|
18
|
+
const fs = require('fs');
|
|
19
|
+
const jwt = require('jsonwebtoken');
|
|
20
|
+
|
|
21
|
+
/* Fetch in the other needed components */
|
|
22
|
+
const path = require('path');
|
|
23
|
+
const https = require('https');
|
|
24
|
+
const http = require('http');
|
|
25
|
+
const cookieHandler = require('cookie');
|
|
26
|
+
const jsonQuery = require('json-query');
|
|
27
|
+
const xml2js = require('xml2js');
|
|
28
|
+
const querystring = require('querystring');
|
|
29
|
+
const HttpsProxyAgent = require('https-proxy-agent');
|
|
30
|
+
const SocksProxyAgent = require('socks-proxy-agent');
|
|
31
|
+
const AsyncLockCl = require('async-lock');
|
|
32
|
+
const FormData = require('form-data');
|
|
33
|
+
|
|
34
|
+
const ThrottleCl = require(path.join(__dirname, '/throttle.js'));
|
|
35
|
+
|
|
36
|
+
const allowFailover = 'AD.300';
|
|
37
|
+
const noFailover = 'AD.500';
|
|
38
|
+
let transUtilInst = null;
|
|
39
|
+
let propUtilInst = null;
|
|
40
|
+
let dbUtilInst = null;
|
|
41
|
+
|
|
42
|
+
// Global Variables - From Properties
|
|
43
|
+
let props = {};
|
|
44
|
+
let host = null;
|
|
45
|
+
let port = null;
|
|
46
|
+
let basepath = null;
|
|
47
|
+
let version = null;
|
|
48
|
+
let authMethod = null;
|
|
49
|
+
let authField = null;
|
|
50
|
+
let authFormat = null;
|
|
51
|
+
let authLogging = false;
|
|
52
|
+
let username = null;
|
|
53
|
+
let password = null;
|
|
54
|
+
let clientId = null;
|
|
55
|
+
let clientSecret = null;
|
|
56
|
+
let grantType = null;
|
|
57
|
+
let staticToken = null;
|
|
58
|
+
let tokenUserField = 'username';
|
|
59
|
+
let tokenPwdField = 'password';
|
|
60
|
+
let tokenResField = 'token';
|
|
61
|
+
let tokenTimeout = -1;
|
|
62
|
+
let tokenError = 401;
|
|
63
|
+
let tokenPath = null;
|
|
64
|
+
let tokenCache = 'local';
|
|
65
|
+
const tokenList = [];
|
|
66
|
+
const tokenlock = 0;
|
|
67
|
+
let stub = false;
|
|
68
|
+
let protocol = 'http';
|
|
69
|
+
let healthcheckpath = null;
|
|
70
|
+
let throttleEnabled = false;
|
|
71
|
+
let numberPhs = 1;
|
|
72
|
+
let ecdhAuto = false;
|
|
73
|
+
let sslEnabled = false;
|
|
74
|
+
let sslAcceptInvalid = false;
|
|
75
|
+
let sslCAFile = null;
|
|
76
|
+
let sslKeyFile = null;
|
|
77
|
+
let sslCertFile = null;
|
|
78
|
+
let sslCiphers = null;
|
|
79
|
+
let secureProtocol = null;
|
|
80
|
+
let proxyEnabled = false;
|
|
81
|
+
let proxyHost = null;
|
|
82
|
+
let proxyPort = null;
|
|
83
|
+
let proxyProtocol = 'http';
|
|
84
|
+
let proxyUser = null;
|
|
85
|
+
let proxyPassword = null;
|
|
86
|
+
let numRetries = 3;
|
|
87
|
+
let numRedirects = 0;
|
|
88
|
+
let limitRetryError = [0];
|
|
89
|
+
let attemptTimeout = 5000;
|
|
90
|
+
let healthcheckOnTimeout = true;
|
|
91
|
+
let globalRequest = null;
|
|
92
|
+
let archiving = false;
|
|
93
|
+
let returnRequest = false;
|
|
94
|
+
let healthy = false;
|
|
95
|
+
const healthlock = 0;
|
|
96
|
+
let healthcheck = false;
|
|
97
|
+
const healthchecklock = 0;
|
|
98
|
+
|
|
99
|
+
// Other global variables
|
|
100
|
+
let id = null;
|
|
101
|
+
let phInstance = null;
|
|
102
|
+
let requestId = 0;
|
|
103
|
+
let archiveColl = '_results';
|
|
104
|
+
const NS_PER_SEC = 1e9;
|
|
105
|
+
let throttleEng = null;
|
|
106
|
+
let tlock = null;
|
|
107
|
+
let hlock = null;
|
|
108
|
+
let hclock = null;
|
|
109
|
+
|
|
110
|
+
let crest = null;
|
|
111
|
+
let cacheHHead = null;
|
|
112
|
+
let cacheHSchema = null;
|
|
113
|
+
let cacheHPay = null;
|
|
114
|
+
|
|
115
|
+
/* CONNECTOR ENGINE INTERNAL FUNCTIONS */
|
|
116
|
+
/** Wait for adapter-mongo to be available.
|
|
117
|
+
* @summary adapter may load before adapter-mongo but it requires UPDATE: test if dbUtil object can connect.
|
|
118
|
+
* mongo to be available before. This function loops until mongo is available.
|
|
119
|
+
*/
|
|
120
|
+
function waitForMongo() {
|
|
121
|
+
const origin = `${id}-connectorRest-waitForMongo`;
|
|
122
|
+
log.trace(origin);
|
|
123
|
+
|
|
124
|
+
return new Promise((resolve, reject) => {
|
|
125
|
+
const INTERVAL = 5000;
|
|
126
|
+
let count = 0;
|
|
127
|
+
|
|
128
|
+
// Run this interval until we can connect to the Mongo Adapter
|
|
129
|
+
const intervalObject = setInterval(() => {
|
|
130
|
+
count += INTERVAL / 1000;
|
|
131
|
+
|
|
132
|
+
// try to see if mongo is available
|
|
133
|
+
return dbUtilInst.connect((alive, db) => {
|
|
134
|
+
if (!alive) {
|
|
135
|
+
log.warn(`${origin}: No database connections available`);
|
|
136
|
+
if (count === 100 * (INTERVAL / 1000)) {
|
|
137
|
+
log.warn(`${origin}: attempted mongo enough times - will be saving to local JSONs`);
|
|
138
|
+
resolve();
|
|
139
|
+
}
|
|
140
|
+
} else {
|
|
141
|
+
log.info(`${origin}: dbUtil reports a status = success will now be able to connect.`);
|
|
142
|
+
db.close();
|
|
143
|
+
resolve();
|
|
144
|
+
clearInterval(intervalObject);
|
|
145
|
+
}
|
|
146
|
+
log.info(`${origin}: Waiting for dbUtil to be available. Seconds passed: ${count}.`);
|
|
147
|
+
});
|
|
148
|
+
}, INTERVAL);
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** Wait for system to be available.
|
|
153
|
+
* @summary assumes System has gone down and we are not able to connect
|
|
154
|
+
* to it and receive any data
|
|
155
|
+
*/
|
|
156
|
+
function waitForSystem(callProperties) {
|
|
157
|
+
const origin = `${id}-connectorRest-waitForSystem`;
|
|
158
|
+
log.trace(origin);
|
|
159
|
+
|
|
160
|
+
return new Promise((resolve, reject) => {
|
|
161
|
+
// if not running healthchecks on timeout just return
|
|
162
|
+
if (callProperties && callProperties.host) {
|
|
163
|
+
log.debug(`${origin}: skipping healthcheck after abort based on changed host`);
|
|
164
|
+
resolve();
|
|
165
|
+
} else if (callProperties && callProperties.request && Object.hasOwnProperty.call(callProperties.request, 'healthcheck_on_timeout')) {
|
|
166
|
+
if (!callProperties.request.healthcheck_on_timeout) {
|
|
167
|
+
log.debug(`${origin}: skipping healthcheck after abort based on call propertues`);
|
|
168
|
+
resolve();
|
|
169
|
+
}
|
|
170
|
+
} else if (!healthcheckOnTimeout) {
|
|
171
|
+
log.debug(`${origin}: skipping healthcheck after abort based on configuration`);
|
|
172
|
+
resolve();
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
let useTimeout = attemptTimeout;
|
|
176
|
+
if (callProperties && callProperties.request && callProperties.request.attempt_timeout) {
|
|
177
|
+
useTimeout = callProperties.request.attempt_timeout;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Find out if currently healthy
|
|
181
|
+
hlock.acquire(healthlock, (doneH) => {
|
|
182
|
+
doneH(healthy);
|
|
183
|
+
}, (retH) => {
|
|
184
|
+
// healthy
|
|
185
|
+
if (retH) {
|
|
186
|
+
log.debug(`${origin}: skipping healthcheck since another command has executed successfully`);
|
|
187
|
+
resolve();
|
|
188
|
+
} else {
|
|
189
|
+
// unhealthy
|
|
190
|
+
let count = 0;
|
|
191
|
+
|
|
192
|
+
// Run this interval until we can connect to the System
|
|
193
|
+
const intervalObject = setInterval(() => {
|
|
194
|
+
count += useTimeout / 1000;
|
|
195
|
+
|
|
196
|
+
// Lock the healthcheck so only one healthcheck at a time
|
|
197
|
+
hclock.acquire(healthchecklock, (doneHC) => {
|
|
198
|
+
// if not already running
|
|
199
|
+
if (!healthcheck) {
|
|
200
|
+
healthcheck = true;
|
|
201
|
+
doneHC(true);
|
|
202
|
+
} else {
|
|
203
|
+
doneHC(false);
|
|
204
|
+
}
|
|
205
|
+
}, (retHC) => {
|
|
206
|
+
// need to run healthcheck
|
|
207
|
+
if (retHC) {
|
|
208
|
+
crest.healthCheck(cacheHSchema, cacheHPay, cacheHHead, callProperties, (hc, error) => {
|
|
209
|
+
if (hc) {
|
|
210
|
+
if (hc === 'fail') {
|
|
211
|
+
log.error(`${origin}: healthcheck reports a status = fail with error ${JSON.stringify(error)}`);
|
|
212
|
+
|
|
213
|
+
if (count === 100 * (useTimeout / 1000)) {
|
|
214
|
+
log.error(`${origin}: attempted healthcheck enough times - failing`);
|
|
215
|
+
|
|
216
|
+
// turn healthcheck false - since no longer active
|
|
217
|
+
hclock.acquire(healthchecklock, (doneHC2) => {
|
|
218
|
+
healthcheck = false;
|
|
219
|
+
doneHC2(true);
|
|
220
|
+
}, (retHC2) => {
|
|
221
|
+
reject();
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
} else if (hc === 'success') {
|
|
225
|
+
log.info(`${origin}: healthcheck reports a status = success. adapter will now restart processing.`);
|
|
226
|
+
|
|
227
|
+
// turn healthcheck false - since no longer active
|
|
228
|
+
hclock.acquire(healthchecklock, (doneHC3) => {
|
|
229
|
+
healthcheck = false;
|
|
230
|
+
doneHC3(true);
|
|
231
|
+
}, (retHC3) => {
|
|
232
|
+
resolve();
|
|
233
|
+
clearInterval(intervalObject);
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
log.info(`${origin}: Waiting for system to be available. Seconds passed: ${count}.`);
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
}, useTimeout);
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/*
|
|
249
|
+
* INTERNAL FUNCTION: Get the best match for the moc k data response
|
|
250
|
+
*/
|
|
251
|
+
function matchMock(uriPath, method, type, mockresponses, respDatatype) {
|
|
252
|
+
// Go through the mock data keys to find the proper data to return
|
|
253
|
+
for (let p = 0; p < mockresponses.length; p += 1) {
|
|
254
|
+
// is this the mock data for this call
|
|
255
|
+
if (Object.hasOwnProperty.call(mockresponses[p], 'name')
|
|
256
|
+
&& uriPath === mockresponses[p].name) {
|
|
257
|
+
if (Object.hasOwnProperty.call(mockresponses[p], 'method')
|
|
258
|
+
&& method.toUpperCase() === mockresponses[p].method.toUpperCase()) {
|
|
259
|
+
if (Object.hasOwnProperty.call(mockresponses[p], 'type')
|
|
260
|
+
&& type.toUpperCase() === mockresponses[p].type.toUpperCase()) {
|
|
261
|
+
// This is the mock data we really want as it best matches the request
|
|
262
|
+
const specificResp = {
|
|
263
|
+
status: 'success',
|
|
264
|
+
code: 200,
|
|
265
|
+
response: {}
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
if (Object.hasOwnProperty.call(mockresponses[p], 'response')) {
|
|
269
|
+
// if a status is defined in the response, use it
|
|
270
|
+
if (Object.hasOwnProperty.call(mockresponses[p].response, 'response')
|
|
271
|
+
&& Object.hasOwnProperty.call(mockresponses[p].response, 'status')) {
|
|
272
|
+
specificResp.code = mockresponses[p].response.status;
|
|
273
|
+
|
|
274
|
+
if (respDatatype && respDatatype.toUpperCase() === 'XML2JSON') {
|
|
275
|
+
specificResp.response = mockresponses[p].response.response;
|
|
276
|
+
} else {
|
|
277
|
+
specificResp.response = JSON.stringify(mockresponses[p].response.response);
|
|
278
|
+
}
|
|
279
|
+
} else if (respDatatype && respDatatype.toUpperCase() === 'XML2JSON') {
|
|
280
|
+
// if no response field, assume that the entire data is the response
|
|
281
|
+
specificResp.response = mockresponses[p].response;
|
|
282
|
+
} else {
|
|
283
|
+
specificResp.response = JSON.stringify(mockresponses[p].response);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
return specificResp;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
return null;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/*
|
|
297
|
+
* INTERNAL FUNCTION: recursively inspect body data if heirarchical
|
|
298
|
+
*/
|
|
299
|
+
function checkBodyData(uriPath, method, reqBdObj, mockresponses, respDatatype) {
|
|
300
|
+
let specificResp = null;
|
|
301
|
+
|
|
302
|
+
if (reqBdObj) {
|
|
303
|
+
const reqBKeys = Object.keys(reqBdObj);
|
|
304
|
+
|
|
305
|
+
// go through each key in the passed in object
|
|
306
|
+
for (let k = 0; k < reqBKeys.length; k += 1) {
|
|
307
|
+
const bVal = reqBdObj[reqBKeys[k]];
|
|
308
|
+
|
|
309
|
+
if (bVal !== undefined && bVal !== null && bVal !== '') {
|
|
310
|
+
// if the field is an object and not an array - recursively call with the new field value
|
|
311
|
+
if (typeof bVal === 'object' && !Array.isArray(bVal)) {
|
|
312
|
+
specificResp = checkBodyData(uriPath, method, bVal, mockresponses, respDatatype);
|
|
313
|
+
} else if (Array.isArray(bVal) && bVal.length > 0 && (typeof bVal[0] === 'object')) {
|
|
314
|
+
// if the field is an array containing objects - recursively call with each object in the array
|
|
315
|
+
for (let a = 0; a < bVal.length; a += 1) {
|
|
316
|
+
specificResp = checkBodyData(uriPath, method, bVal[a], mockresponses, respDatatype);
|
|
317
|
+
|
|
318
|
+
// if the data match is found break the for loop - will return below
|
|
319
|
+
if (specificResp !== null) {
|
|
320
|
+
break;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
} else if (Array.isArray(bVal)) {
|
|
324
|
+
// if an array of data, need to check each data in the array
|
|
325
|
+
for (let a = 0; a < bVal.length; a += 1) {
|
|
326
|
+
// should match fieldName-fieldValue
|
|
327
|
+
const compStr = `${reqBKeys[k]}-${bVal[a]}`;
|
|
328
|
+
specificResp = matchMock(uriPath, method, compStr, mockresponses, respDatatype);
|
|
329
|
+
|
|
330
|
+
// if the data match is found break the for loop - will return below
|
|
331
|
+
if (specificResp !== null) {
|
|
332
|
+
break;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
} else {
|
|
336
|
+
// should match fieldName-fieldValue
|
|
337
|
+
const compStr = `${reqBKeys[k]}-${bVal}`;
|
|
338
|
+
specificResp = matchMock(uriPath, method, compStr, mockresponses, respDatatype);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
if (specificResp !== null) {
|
|
342
|
+
break;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
return specificResp;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/*
|
|
351
|
+
* INTERNAL FUNCTION: return stub results based on the path and method
|
|
352
|
+
*/
|
|
353
|
+
function returnStub(request, entitySchema, callProperties) {
|
|
354
|
+
const origin = `${id}-connectorRest-returnStub`;
|
|
355
|
+
log.trace(origin);
|
|
356
|
+
const uriPath = request.origPath;
|
|
357
|
+
const method = request.header.method.toUpperCase();
|
|
358
|
+
const reqBody = request.body;
|
|
359
|
+
const reqPath = request.header.path;
|
|
360
|
+
|
|
361
|
+
// these logs are very useful when debugging - however there is the potential for credentials to be exposed.
|
|
362
|
+
if (authLogging) {
|
|
363
|
+
log.debug(`FULL STUB REQUEST: ${JSON.stringify(request.header)}`);
|
|
364
|
+
log.debug(`FULL STUB BODY: ${request.body}`);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
const callResp = {
|
|
368
|
+
status: 'success',
|
|
369
|
+
code: 200,
|
|
370
|
+
response: '{}'
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
// if there is no entity schema this should be a healthcheck
|
|
374
|
+
if (!entitySchema) {
|
|
375
|
+
log.error(`${origin}: healthcheck? no entity schema - no mock data to return`);
|
|
376
|
+
return callResp;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
log.trace(`${origin}: ${JSON.stringify(entitySchema)}`);
|
|
380
|
+
|
|
381
|
+
// if there are no mock responses defined in the entity schema, stubbing will not work
|
|
382
|
+
if (!Object.hasOwnProperty.call(entitySchema, 'mockresponses')) {
|
|
383
|
+
callResp.status = 'failure';
|
|
384
|
+
callResp.code = 400;
|
|
385
|
+
callResp.message = 'no mock data defined in entity scheme - no mock data to return';
|
|
386
|
+
delete callResp.response;
|
|
387
|
+
|
|
388
|
+
log.error(`${origin}: ${callResp.message}`);
|
|
389
|
+
return callResp;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
const mockresponses = entitySchema.mockresponses;
|
|
393
|
+
let specificResp = null;
|
|
394
|
+
|
|
395
|
+
// if there is a request body, see if there is something that matches a specific input
|
|
396
|
+
if (reqBody && (!entitySchema || !entitySchema.requestDatatype
|
|
397
|
+
|| entitySchema.requestDatatype.toUpperCase() === 'JSON' || entitySchema.requestDatatype.toUpperCase() === 'URLENCODE')) {
|
|
398
|
+
let reqBdObj = null;
|
|
399
|
+
if (entitySchema && entitySchema.requestDatatype && entitySchema.requestDatatype.toUpperCase() === 'URLENCODE') {
|
|
400
|
+
reqBdObj = querystring.parse(reqBody.trim());
|
|
401
|
+
} else {
|
|
402
|
+
reqBdObj = JSON.parse(reqBody.trim());
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
specificResp = checkBodyData(uriPath, method, reqBdObj, mockresponses, entitySchema.responseDatatype);
|
|
406
|
+
|
|
407
|
+
if (specificResp !== null) {
|
|
408
|
+
return specificResp;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// if there are path variables, see if there is something that matches a specific variable
|
|
413
|
+
if (uriPath.indexOf('{pathv') >= 0) {
|
|
414
|
+
const uriTemp = uriPath.split('?');
|
|
415
|
+
const actTemp = reqPath.split('?');
|
|
416
|
+
uriTemp[0] = uriTemp[0].replace(/{pathv/g, '/{pathv');
|
|
417
|
+
uriTemp[0] = uriTemp[0].replace(/{version/g, '/{version');
|
|
418
|
+
uriTemp[0] = uriTemp[0].replace(/{base/g, '/{base');
|
|
419
|
+
uriTemp[0] = uriTemp[0].replace(/\/\//g, '/');
|
|
420
|
+
|
|
421
|
+
// remove basepath from both paths
|
|
422
|
+
// get rid of base path from the uriPath
|
|
423
|
+
uriTemp[0] = uriTemp[0].replace(/\/{base_path}/g, '');
|
|
424
|
+
// if a base path was added to the request, remove it
|
|
425
|
+
if (callProperties && callProperties.base_path && callProperties.base_path !== '/') {
|
|
426
|
+
actTemp[0] = actTemp[0].replace(callProperties.base_path, '');
|
|
427
|
+
} else if (basepath && basepath !== '/') {
|
|
428
|
+
actTemp[0] = actTemp[0].replace(basepath, '');
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// remove version from both paths
|
|
432
|
+
// get rid of version from the uriPath
|
|
433
|
+
uriTemp[0] = uriTemp[0].replace(/\/{version}/g, '');
|
|
434
|
+
// if a version was added to the request, remove it
|
|
435
|
+
if (callProperties && callProperties.version) {
|
|
436
|
+
actTemp[0] = actTemp[0].replace(`/${callProperties.version}`, '');
|
|
437
|
+
} else if (version && version !== '/') {
|
|
438
|
+
actTemp[0] = actTemp[0].replace(`/${version}`, '');
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
const uriArray = uriTemp[0].split('/');
|
|
442
|
+
const actArray = actTemp[0].split('/');
|
|
443
|
+
|
|
444
|
+
// the number of items in both should be the same
|
|
445
|
+
if (uriArray.length === actArray.length) {
|
|
446
|
+
for (let i = 0; i < uriArray.length; i += 1) {
|
|
447
|
+
if (uriArray[i].indexOf('{pathv') >= 0) {
|
|
448
|
+
specificResp = matchMock(uriPath, method, actArray[i], mockresponses, entitySchema.responseDatatype);
|
|
449
|
+
|
|
450
|
+
if (specificResp !== null) {
|
|
451
|
+
return specificResp;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// if there are queiries or options, see if there is something that matches a specific input
|
|
459
|
+
if (reqPath.indexOf('?') >= 0) {
|
|
460
|
+
const queries = reqPath.substring(reqPath.indexOf('?') + 1);
|
|
461
|
+
const queryArr = queries.split('&');
|
|
462
|
+
|
|
463
|
+
// go through each query parameter
|
|
464
|
+
for (let q = 0; q < queryArr.length; q += 1) {
|
|
465
|
+
let qval = queryArr[q];
|
|
466
|
+
if (qval !== undefined && qval !== null && qval !== '') {
|
|
467
|
+
// stringifies it - in case it was not a string
|
|
468
|
+
qval = `${qval}`;
|
|
469
|
+
specificResp = matchMock(uriPath, method, qval, mockresponses, entitySchema.responseDatatype);
|
|
470
|
+
|
|
471
|
+
if (specificResp !== null) {
|
|
472
|
+
return specificResp;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// if there is a request body, see if there is a specific response for body
|
|
479
|
+
if (reqBody) {
|
|
480
|
+
specificResp = matchMock(uriPath, method, 'WITHBODY', mockresponses, entitySchema.responseDatatype);
|
|
481
|
+
|
|
482
|
+
if (specificResp !== null) {
|
|
483
|
+
return specificResp;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// if there are path variables, see if there is a specific response for path vars
|
|
488
|
+
if (uriPath.indexOf('{pathv') >= 0) {
|
|
489
|
+
const uriTemp = uriPath.split('?');
|
|
490
|
+
const actTemp = reqPath.split('?');
|
|
491
|
+
uriTemp[0] = uriTemp[0].replace(/{pathv/g, '/{pathv');
|
|
492
|
+
uriTemp[0] = uriTemp[0].replace(/{version/g, '/{version');
|
|
493
|
+
uriTemp[0] = uriTemp[0].replace(/{base_path}/g, '/{base_path}');
|
|
494
|
+
uriTemp[0] = uriTemp[0].replace(/\/\//g, '/');
|
|
495
|
+
|
|
496
|
+
// remove basepath from both paths
|
|
497
|
+
// get rid of base path from the uriPath
|
|
498
|
+
uriTemp[0] = uriTemp[0].replace(/\/{base_path}/g, '');
|
|
499
|
+
// if a base path was added to the request, remove it
|
|
500
|
+
if (callProperties && callProperties.base_path && callProperties.base_path !== '/') {
|
|
501
|
+
actTemp[0] = actTemp[0].replace(callProperties.base_path, '');
|
|
502
|
+
} else if (basepath && basepath !== '/') {
|
|
503
|
+
actTemp[0] = actTemp[0].replace(basepath, '');
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// remove version from both paths
|
|
507
|
+
// get rid of version from the uriPath
|
|
508
|
+
uriTemp[0] = uriTemp[0].replace(/\/{version}/g, '');
|
|
509
|
+
// if a version was added to the request, remove it
|
|
510
|
+
if (callProperties && callProperties.version) {
|
|
511
|
+
actTemp[0] = actTemp[0].replace(`/${callProperties.version}`, '');
|
|
512
|
+
} else if (version && version !== '/') {
|
|
513
|
+
actTemp[0] = actTemp[0].replace(`/${version}`, '');
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
const uriArray = uriTemp[0].split('/');
|
|
517
|
+
const actArray = actTemp[0].split('/');
|
|
518
|
+
|
|
519
|
+
// the number of items in both should be the same
|
|
520
|
+
if (uriArray.length === actArray.length) {
|
|
521
|
+
let cnt = 1;
|
|
522
|
+
for (let i = 0; i < uriArray.length; i += 1) {
|
|
523
|
+
if (uriArray[i].indexOf('{pathv') >= 0) {
|
|
524
|
+
specificResp = matchMock(uriPath, method, `WITHPATHV${cnt}`, mockresponses, entitySchema.responseDatatype);
|
|
525
|
+
|
|
526
|
+
if (specificResp !== null) {
|
|
527
|
+
return specificResp;
|
|
528
|
+
}
|
|
529
|
+
cnt += 1;
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
// if there are queiries or options, see if there is a specific response for query or options
|
|
536
|
+
if (uriPath.indexOf('?') >= 0) {
|
|
537
|
+
specificResp = matchMock(uriPath, method, 'WITHQUERY', mockresponses, entitySchema.responseDatatype);
|
|
538
|
+
|
|
539
|
+
if (specificResp !== null) {
|
|
540
|
+
return specificResp;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
specificResp = matchMock(uriPath, method, 'WITHOPTIONS', mockresponses, entitySchema.responseDatatype);
|
|
544
|
+
|
|
545
|
+
if (specificResp !== null) {
|
|
546
|
+
return specificResp;
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
specificResp = matchMock(uriPath, method, 'DEFAULT', mockresponses, entitySchema.responseDatatype);
|
|
551
|
+
|
|
552
|
+
if (specificResp !== null) {
|
|
553
|
+
return specificResp;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
callResp.status = 'failure';
|
|
557
|
+
callResp.code = 400;
|
|
558
|
+
callResp.message = `no mock data for ${uriPath} - no mock data to return`;
|
|
559
|
+
delete callResp.response;
|
|
560
|
+
|
|
561
|
+
log.error(`${origin}: ${callResp.message}`);
|
|
562
|
+
return callResp;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
/*
|
|
566
|
+
* INTERNAL FUNCTION: makeRequest makes the actual call to System
|
|
567
|
+
*/
|
|
568
|
+
function makeRequest(request, entitySchema, callProperties, startTrip, attempt, callback) {
|
|
569
|
+
const origin = `${id}-connectorRest-makeRequest`;
|
|
570
|
+
log.trace(origin);
|
|
571
|
+
let myTimeout = attemptTimeout;
|
|
572
|
+
const roundTTime = startTrip || process.hrtime();
|
|
573
|
+
const stTime = new Date().getTime();
|
|
574
|
+
|
|
575
|
+
// remove any double slashes that may be in the path - can happen if base path is / or ends in a /
|
|
576
|
+
request.header.path = request.header.path.replace(/\/\//g, '/');
|
|
577
|
+
|
|
578
|
+
// if there is a timeout from the action (schema) use it instead
|
|
579
|
+
if (entitySchema && entitySchema.timeout && entitySchema.timeout > 0) {
|
|
580
|
+
myTimeout = entitySchema.timeout;
|
|
581
|
+
}
|
|
582
|
+
if (callProperties && callProperties.request && callProperties.request.attempt_timeout) {
|
|
583
|
+
myTimeout = callProperties.request.attempt_timeout;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
// if we need ecdhCurve set to auto
|
|
587
|
+
if (callProperties && callProperties.ssl && Object.hasOwnProperty.call(callProperties.ssl, 'ecdhCurve')) {
|
|
588
|
+
if (callProperties.ssl.ecdhCurve) {
|
|
589
|
+
request.header.ecdhCurve = 'auto';
|
|
590
|
+
}
|
|
591
|
+
} else if (ecdhAuto) {
|
|
592
|
+
request.header.ecdhCurve = 'auto';
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
// STUB CODE - this is good for an initial test - set stub in Pronghorn property file to true
|
|
596
|
+
if (callProperties && Object.hasOwnProperty.call(callProperties, 'stub')) {
|
|
597
|
+
if (callProperties.stub) {
|
|
598
|
+
return callback(returnStub(request, entitySchema, callProperties));
|
|
599
|
+
}
|
|
600
|
+
} else if (stub) {
|
|
601
|
+
return callback(returnStub(request, entitySchema, callProperties));
|
|
602
|
+
}
|
|
603
|
+
let useProt = http;
|
|
604
|
+
let protStr = 'http';
|
|
605
|
+
|
|
606
|
+
try {
|
|
607
|
+
// determine proper protocol for primary request
|
|
608
|
+
if (callProperties && callProperties.protocol) {
|
|
609
|
+
if (callProperties.protocol === 'https') {
|
|
610
|
+
log.debug(`${origin}: Switching to https protocol`);
|
|
611
|
+
useProt = https;
|
|
612
|
+
protStr = 'https';
|
|
613
|
+
}
|
|
614
|
+
} else if (entitySchema && entitySchema.sso && entitySchema.sso.protocol) {
|
|
615
|
+
// this is for single signon - should we limit to token???
|
|
616
|
+
if (entitySchema.sso.protocol === 'https') {
|
|
617
|
+
log.debug(`${origin}: Switching to https protocol`);
|
|
618
|
+
useProt = https;
|
|
619
|
+
protStr = 'https';
|
|
620
|
+
}
|
|
621
|
+
} else if (protocol === 'https') {
|
|
622
|
+
log.debug(`${origin}: Switching to https protocol`);
|
|
623
|
+
useProt = https;
|
|
624
|
+
protStr = 'https';
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
// add the proxy information to the request
|
|
628
|
+
if (callProperties && callProperties.proxy && Object.hasOwnProperty.call(callProperties.proxy, 'enabled')) {
|
|
629
|
+
if (callProperties.proxy.enabled) {
|
|
630
|
+
let proxy = `${callProperties.proxy.protocol}://${callProperties.proxy.host}:${callProperties.proxy.port}`;
|
|
631
|
+
if (callProperties.proxy.username && callProperties.proxy.password) {
|
|
632
|
+
proxy = `${callProperties.proxy.protocol}://${callProperties.proxy.username}:${callProperties.proxy.password}@${callProperties.proxy.host}:${callProperties.proxy.port}`;
|
|
633
|
+
}
|
|
634
|
+
request.header.agent = new HttpsProxyAgent(proxy);
|
|
635
|
+
|
|
636
|
+
if (callProperties.proxy.protocol.indexOf('socks') >= 0) {
|
|
637
|
+
request.header.agent = new SocksProxyAgent(proxy);
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
} else if (proxyEnabled) {
|
|
641
|
+
let proxy = `${proxyProtocol}://${proxyHost}:${proxyPort}`;
|
|
642
|
+
if (proxyUser && proxyPassword) {
|
|
643
|
+
proxy = `${callProperties.proxy.protocol}://${proxyUser}:${proxyPassword}@${callProperties.proxy.host}:${callProperties.proxy.port}`;
|
|
644
|
+
}
|
|
645
|
+
request.header.agent = new HttpsProxyAgent(proxy);
|
|
646
|
+
|
|
647
|
+
if (proxyProtocol.indexOf('socks') >= 0) {
|
|
648
|
+
request.header.agent = new SocksProxyAgent(proxy);
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
// if the request uses form-data type, need to add it to the headers
|
|
653
|
+
const formData = new FormData();
|
|
654
|
+
if (entitySchema.requestDatatype.toUpperCase() === 'FORM') {
|
|
655
|
+
// need to convert request.body back to JSON
|
|
656
|
+
let mybody = request.body;
|
|
657
|
+
if (typeof mybody === 'string') {
|
|
658
|
+
mybody = JSON.parse(request.body);
|
|
659
|
+
}
|
|
660
|
+
// add the data for each field into the form
|
|
661
|
+
const mykeys = Object.keys(mybody);
|
|
662
|
+
for (let k = 0; k < mykeys.length; k += 1) {
|
|
663
|
+
if (mykeys[k] === 'file') {
|
|
664
|
+
let fileVal = mybody[mykeys[k]];
|
|
665
|
+
if (fileVal.indexOf('@') === 0) {
|
|
666
|
+
// if there are multiple parts - first part is file, other part can be name
|
|
667
|
+
const filePart = fileVal.split(';');
|
|
668
|
+
let fileName = null;
|
|
669
|
+
fileVal = fs.readFileSync(`node_modules/@itentialopensource/adapter-forwardnetworks/uploads/${filePart[0].substring(1)}`);
|
|
670
|
+
|
|
671
|
+
// see if we have a filename that we should add to the formdata
|
|
672
|
+
for (let p = 1; p < filePart.length; p += 1) {
|
|
673
|
+
if (filePart[p].indexOf('name=') === 0) {
|
|
674
|
+
fileName = filePart[p].substring(5);
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
if (fileName) {
|
|
678
|
+
// with filename
|
|
679
|
+
log.debug(`APPENDING: ${mykeys[k]}, WITH VALUE ${fileVal}, AND NAME ${fileName}`);
|
|
680
|
+
formData.append(mykeys[k], fileVal, fileName);
|
|
681
|
+
} else {
|
|
682
|
+
// with read in file but no file name
|
|
683
|
+
log.debug(`APPENDING: ${mykeys[k]}, WITH VALUE ${fileVal}`);
|
|
684
|
+
formData.append(mykeys[k], fileVal);
|
|
685
|
+
}
|
|
686
|
+
} else {
|
|
687
|
+
// no file or file name just contents - original
|
|
688
|
+
log.debug(`APPENDING: ${mykeys[k]}, WITH VALUE ${fileVal}`);
|
|
689
|
+
formData.append(mykeys[k], fileVal);
|
|
690
|
+
}
|
|
691
|
+
} else {
|
|
692
|
+
log.debug(`APPENDING: ${mykeys[k]}, WITH VALUE ${mybody[mykeys[k]]}`);
|
|
693
|
+
formData.append(mykeys[k], mybody[mykeys[k]]);
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
// get the new headers
|
|
697
|
+
request.header.headers = Object.assign(request.header.headers, formData.getHeaders());
|
|
698
|
+
// seems to not use Content-Type so fix that
|
|
699
|
+
if (request.header.headers['content-type']) {
|
|
700
|
+
request.header.headers['Content-Type'] = request.header.headers['content-type'];
|
|
701
|
+
}
|
|
702
|
+
delete request.header.headers['Content-length'];
|
|
703
|
+
log.debug(`${origin}: Changing Headers for formData: ${JSON.stringify(request.header.headers)}`);
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
// these logs are very useful when debugging - however there is the potential for credentials to be exposed.
|
|
707
|
+
if (authLogging) {
|
|
708
|
+
log.debug(`FULL REQUEST: ${JSON.stringify(request.header)}`);
|
|
709
|
+
log.debug(`FULL BODY: ${request.body}`);
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
// make the call to System
|
|
713
|
+
const httpRequest = useProt.request(request.header, (res) => {
|
|
714
|
+
let respStr = '';
|
|
715
|
+
res.setEncoding('utf8');
|
|
716
|
+
|
|
717
|
+
// process data from response
|
|
718
|
+
res.on('data', (replyData) => {
|
|
719
|
+
respStr += replyData;
|
|
720
|
+
});
|
|
721
|
+
|
|
722
|
+
// process end of response
|
|
723
|
+
res.on('end', () => {
|
|
724
|
+
const tripDiff = process.hrtime(roundTTime);
|
|
725
|
+
const tripEnd = `${Math.round(((tripDiff[0] * NS_PER_SEC) + tripDiff[1]) / 1000000)}ms`;
|
|
726
|
+
const callResp = {
|
|
727
|
+
status: 'success',
|
|
728
|
+
code: res.statusCode,
|
|
729
|
+
headers: res.headers,
|
|
730
|
+
response: respStr,
|
|
731
|
+
request: {
|
|
732
|
+
protocol: protStr,
|
|
733
|
+
host: request.header.hostname,
|
|
734
|
+
port: request.header.port,
|
|
735
|
+
uri: request.header.path,
|
|
736
|
+
method: request.header.method,
|
|
737
|
+
payload: request.body
|
|
738
|
+
},
|
|
739
|
+
redirects: attempt,
|
|
740
|
+
tripTime: tripEnd
|
|
741
|
+
};
|
|
742
|
+
|
|
743
|
+
// if there was a cookie or biscotti returned, need to set cookie on the new request
|
|
744
|
+
if (request.header.headers) {
|
|
745
|
+
const headKeys = Object.keys(request.header.headers);
|
|
746
|
+
for (let k = 0; k < headKeys.length; k += 1) {
|
|
747
|
+
if (headKeys[k].toLowerCase() === 'cookie') {
|
|
748
|
+
callResp.requestCookie = request.header.headers[headKeys[k]];
|
|
749
|
+
if (!callResp.headers['set-cookie']) {
|
|
750
|
+
callResp.headers['set-cookie'] = request.header.headers[headKeys[k]];
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
if (headKeys[k].toLowerCase() === 'x-biscotti') {
|
|
754
|
+
if (!callResp.headers['x-biscotti']) {
|
|
755
|
+
callResp.headers['x-biscotti'] = request.header.headers[headKeys[k]];
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
// handling redirects
|
|
762
|
+
let useRedirect = numRedirects;
|
|
763
|
+
if (callProperties && callProperties.request && typeof callProperties.request.number_redirects === 'number') {
|
|
764
|
+
useRedirect = Number(callProperties.request.number_redirects);
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
if (attempt < useRedirect && res.statusCode >= 300 && res.statusCode <= 308 && res.headers.location) {
|
|
768
|
+
// retries = 0 go here, retries = 1 it doesn't
|
|
769
|
+
const newProp = Object.assign({}, callProperties);
|
|
770
|
+
const newRequest = Object.assign({}, request);
|
|
771
|
+
const nextAtt = attempt + 1;
|
|
772
|
+
|
|
773
|
+
// if there is a protocol on the new location use it
|
|
774
|
+
if (res.headers.location.indexOf('://') >= 0) {
|
|
775
|
+
const protSplit = res.headers.location.split('://');
|
|
776
|
+
newProp.protocol = protSplit[0];
|
|
777
|
+
|
|
778
|
+
if (protSplit.length > 1) {
|
|
779
|
+
const pathSplit = protSplit[1].split('/');
|
|
780
|
+
|
|
781
|
+
// grab the path
|
|
782
|
+
if (pathSplit.length > 1) {
|
|
783
|
+
newRequest.header.path = `/${protSplit[1].substring(protSplit[1].indexOf('/'))}`;
|
|
784
|
+
} else {
|
|
785
|
+
newRequest.header.path = '';
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
const portSplit = pathSplit[0].split(':');
|
|
789
|
+
|
|
790
|
+
// grab the hostname
|
|
791
|
+
newRequest.header.hostname = `${portSplit[0]}`;
|
|
792
|
+
|
|
793
|
+
// grab the port
|
|
794
|
+
if (portSplit.length > 1) {
|
|
795
|
+
newRequest.header.port = portSplit[1];
|
|
796
|
+
} else if (newProp.protocol === 'https') {
|
|
797
|
+
newRequest.header.port = 443;
|
|
798
|
+
} else {
|
|
799
|
+
newRequest.header.port = 80;
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
} else {
|
|
803
|
+
// must just have the path - no protocol, host or port
|
|
804
|
+
newRequest.header.path = res.headers.location;
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
// if there was a cookie or biscotti returned, need to set cookie on the new request
|
|
808
|
+
if (res.headers) {
|
|
809
|
+
const headKeys = Object.keys(res.headers);
|
|
810
|
+
for (let k = 0; k < headKeys.length; k += 1) {
|
|
811
|
+
if (headKeys[k].toLowerCase() === 'set-cookie') {
|
|
812
|
+
newRequest.header.headers.Cookie = res.headers[headKeys[k]];
|
|
813
|
+
}
|
|
814
|
+
if (headKeys[k].toLowerCase() === 'x-biscotti') {
|
|
815
|
+
newRequest.header.headers['x-biscotti'] = res.headers[headKeys[k]];
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
// make the modified request
|
|
821
|
+
log.debug(`${origin}: REDIRECTING TO - ${res.headers.location}`);
|
|
822
|
+
return makeRequest(newRequest, entitySchema, newProp, roundTTime, nextAtt, callback);
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
// callResp.timeouts = myTimeout;
|
|
826
|
+
// turn healthy true - since valid return
|
|
827
|
+
hlock.acquire(healthlock, (doneH2) => {
|
|
828
|
+
healthy = true;
|
|
829
|
+
doneH2(true);
|
|
830
|
+
}, (retH2) => {
|
|
831
|
+
log.debug(`${origin}: CALL RETURN ${JSON.stringify(callResp)}`);
|
|
832
|
+
useProt = undefined;
|
|
833
|
+
callResp.reqHdr = request.header.headers;
|
|
834
|
+
return callback(callResp);
|
|
835
|
+
});
|
|
836
|
+
});
|
|
837
|
+
});
|
|
838
|
+
|
|
839
|
+
// handle post error
|
|
840
|
+
httpRequest.on('error', (cerror) => {
|
|
841
|
+
const tripDiff = process.hrtime(roundTTime);
|
|
842
|
+
const tripEnd = `${Math.round(((tripDiff[0] * NS_PER_SEC) + tripDiff[1]) / 1000000)}ms`;
|
|
843
|
+
const callResp = {
|
|
844
|
+
status: 'failure',
|
|
845
|
+
code: -1,
|
|
846
|
+
message: cerror,
|
|
847
|
+
request: {
|
|
848
|
+
protocol: protStr,
|
|
849
|
+
host: request.header.hostname,
|
|
850
|
+
port: request.header.port,
|
|
851
|
+
uri: request.header.path,
|
|
852
|
+
method: request.header.method
|
|
853
|
+
},
|
|
854
|
+
redirects: attempt,
|
|
855
|
+
tripTime: tripEnd
|
|
856
|
+
};
|
|
857
|
+
let didTimeout = 0;
|
|
858
|
+
|
|
859
|
+
// if the connection was reset - need to determine if client or server cut it
|
|
860
|
+
if (cerror.code === 'ECONNRESET') {
|
|
861
|
+
const endTime = new Date().getTime();
|
|
862
|
+
|
|
863
|
+
// see if this is a client timeout
|
|
864
|
+
if (endTime - stTime >= myTimeout) {
|
|
865
|
+
callResp.code = -2;
|
|
866
|
+
didTimeout += 1;
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
// turn healthy false - since error return
|
|
871
|
+
hlock.acquire(healthlock, (doneH2) => {
|
|
872
|
+
healthy = false;
|
|
873
|
+
doneH2(true);
|
|
874
|
+
}, (retH2) => {
|
|
875
|
+
log.debug(`${origin}: ERROR RETURN ${JSON.stringify(callResp)}`);
|
|
876
|
+
useProt = undefined;
|
|
877
|
+
callResp.timeouts = didTimeout;
|
|
878
|
+
callResp.reqHdr = request.header.headers;
|
|
879
|
+
return callback(callResp);
|
|
880
|
+
});
|
|
881
|
+
});
|
|
882
|
+
|
|
883
|
+
httpRequest.setTimeout(myTimeout, () => {
|
|
884
|
+
httpRequest.abort();
|
|
885
|
+
}, myTimeout);
|
|
886
|
+
|
|
887
|
+
// write to the http request data to the body if not a get (gets do not have data in the body)
|
|
888
|
+
if ((request.header.method !== 'GET' || entitySchema.sendGetBody)
|
|
889
|
+
&& ((typeof request.body === 'object' && Object.keys(request.body).length > 0)
|
|
890
|
+
|| (typeof request.body === 'string' && request.body !== '{}') || entitySchema.sendEmpty)) {
|
|
891
|
+
if (entitySchema.requestDatatype.toUpperCase() === 'FORM') {
|
|
892
|
+
// pass the formData to the request
|
|
893
|
+
log.debug(`${origin}: Sending request body through formData`);
|
|
894
|
+
log.debug(`Form Buffer: ${formData.getBuffer().toString()}`);
|
|
895
|
+
formData.pipe(httpRequest);
|
|
896
|
+
} else {
|
|
897
|
+
// pass the body to the request
|
|
898
|
+
log.debug(`${origin}: Sending request body normally`);
|
|
899
|
+
httpRequest.write(request.body);
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
// end the http request
|
|
904
|
+
httpRequest.end();
|
|
905
|
+
} catch (e) {
|
|
906
|
+
// handle any exception
|
|
907
|
+
const tripDiff = process.hrtime(roundTTime);
|
|
908
|
+
const tripEnd = `${Math.round(((tripDiff[0] * NS_PER_SEC) + tripDiff[1]) / 1000000)}ms`;
|
|
909
|
+
const errorObj = transUtilInst.checkAndReturn(e, origin, 'Issue during make request');
|
|
910
|
+
errorObj.metrics = {
|
|
911
|
+
code: 'translate_error',
|
|
912
|
+
redirects: attempt,
|
|
913
|
+
tripTime: tripEnd
|
|
914
|
+
};
|
|
915
|
+
errorObj.request = {
|
|
916
|
+
protocol: protStr,
|
|
917
|
+
host: request.header.hostname,
|
|
918
|
+
port: request.header.port,
|
|
919
|
+
uri: request.header.path,
|
|
920
|
+
method: request.header.method,
|
|
921
|
+
payload: request.body
|
|
922
|
+
};
|
|
923
|
+
return callback(null, errorObj);
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
/*
|
|
928
|
+
* INTERNAL FUNCTION: finds the token in the object that was returned
|
|
929
|
+
*/
|
|
930
|
+
function findPrimaryTokenInResult(result, front, end) {
|
|
931
|
+
const origin = `${id}-connectorRest-findPrimaryTokenInResult`;
|
|
932
|
+
log.trace(origin);
|
|
933
|
+
let token = null;
|
|
934
|
+
|
|
935
|
+
if (!result) {
|
|
936
|
+
return token;
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
// if the result is a string, need to assume that is the token
|
|
940
|
+
if (typeof result === 'string') {
|
|
941
|
+
let modRes = result;
|
|
942
|
+
// if we need to pull out part of the string
|
|
943
|
+
if (front && result.indexOf(front) > 0) {
|
|
944
|
+
const stInd = result.indexOf(front) + front.length;
|
|
945
|
+
modRes = result.substring(stInd);
|
|
946
|
+
}
|
|
947
|
+
if (end && modRes.indexOf(end) > 0) {
|
|
948
|
+
modRes = modRes.substring(0, modRes.indexOf(end));
|
|
949
|
+
}
|
|
950
|
+
return modRes;
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
// if the token is in this object
|
|
954
|
+
if (result.token) {
|
|
955
|
+
// if we need to pull out part of the string
|
|
956
|
+
if (typeof result.token === 'string') {
|
|
957
|
+
let modRes = result.token;
|
|
958
|
+
if (front && modRes.indexOf(front) > 0) {
|
|
959
|
+
const stInd = modRes.indexOf(front) + front.length;
|
|
960
|
+
modRes = modRes.substring(stInd);
|
|
961
|
+
}
|
|
962
|
+
if (end && modRes.indexOf(end) > 0) {
|
|
963
|
+
modRes = modRes.substring(0, modRes.indexOf(end));
|
|
964
|
+
}
|
|
965
|
+
return modRes;
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
return result.token;
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
// need to check objects within this object
|
|
972
|
+
const keys = Object.keys(result);
|
|
973
|
+
|
|
974
|
+
for (let k = 0; k < keys.length; k += 1) {
|
|
975
|
+
if (typeof result[keys[k]] === 'object') {
|
|
976
|
+
const found = findPrimaryTokenInResult(result[keys[k]], front, end);
|
|
977
|
+
|
|
978
|
+
// if we found the token
|
|
979
|
+
if (found !== null) {
|
|
980
|
+
token = found;
|
|
981
|
+
break;
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
// return the found token
|
|
987
|
+
return token;
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
/*
|
|
991
|
+
* INTERNAL FUNCTION: finds the second token in the object that was returned
|
|
992
|
+
*/
|
|
993
|
+
function findSecondaryTokenInResult(result, front, end) {
|
|
994
|
+
const origin = `${id}-connectorRest-findSecondaryTokenInResult`;
|
|
995
|
+
log.trace(origin);
|
|
996
|
+
let tokenp2 = null;
|
|
997
|
+
|
|
998
|
+
if (!result) {
|
|
999
|
+
return tokenp2;
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
// if the result is a string, need to assume that is the second token
|
|
1003
|
+
if (typeof result === 'string') {
|
|
1004
|
+
let modRes = result;
|
|
1005
|
+
// if we need to pull out part of the string
|
|
1006
|
+
if (front && result.indexOf(front) > 0) {
|
|
1007
|
+
const stInd = result.indexOf(front) + front.length;
|
|
1008
|
+
modRes = result.substring(stInd);
|
|
1009
|
+
}
|
|
1010
|
+
if (end && modRes.indexOf(end) > 0) {
|
|
1011
|
+
modRes = modRes.substring(0, modRes.indexOf(end));
|
|
1012
|
+
}
|
|
1013
|
+
return modRes;
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
// if the second token is in this object
|
|
1017
|
+
if (result.tokenp2) {
|
|
1018
|
+
// if we need to pull out part of the string
|
|
1019
|
+
if (typeof result.tokenp2 === 'string') {
|
|
1020
|
+
let modRes = result.tokenp2;
|
|
1021
|
+
if (front && modRes.indexOf(front) > 0) {
|
|
1022
|
+
const stInd = modRes.indexOf(front) + front.length;
|
|
1023
|
+
modRes = modRes.substring(stInd);
|
|
1024
|
+
}
|
|
1025
|
+
if (end && modRes.indexOf(end) > 0) {
|
|
1026
|
+
modRes = modRes.substring(0, modRes.indexOf(end));
|
|
1027
|
+
}
|
|
1028
|
+
return modRes;
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
return result.tokenp2;
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
// need to check objects within this object
|
|
1035
|
+
const keys = Object.keys(result);
|
|
1036
|
+
|
|
1037
|
+
for (let k = 0; k < keys.length; k += 1) {
|
|
1038
|
+
if (typeof result[keys[k]] === 'object') {
|
|
1039
|
+
const found = findSecondaryTokenInResult(result[keys[k]]);
|
|
1040
|
+
|
|
1041
|
+
// if we found the second token
|
|
1042
|
+
if (found !== null) {
|
|
1043
|
+
tokenp2 = found;
|
|
1044
|
+
break;
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
// return the found second token
|
|
1050
|
+
return tokenp2;
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
/*
|
|
1054
|
+
* INTERNAL FUNCTION: finds the expiration in the object that was returned
|
|
1055
|
+
*/
|
|
1056
|
+
function findExpireInResult(result) {
|
|
1057
|
+
const origin = `${id}-connectorRest-findExpireInResult`;
|
|
1058
|
+
log.trace(origin);
|
|
1059
|
+
let expire = null;
|
|
1060
|
+
|
|
1061
|
+
if (!result) {
|
|
1062
|
+
return expire;
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
// if the expires is in this object
|
|
1066
|
+
if (result.expires) {
|
|
1067
|
+
return new Date(result.expires).getTime();
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
// need to check objects within this object
|
|
1071
|
+
const keys = Object.keys(result);
|
|
1072
|
+
|
|
1073
|
+
for (let k = 0; k < keys; k += 1) {
|
|
1074
|
+
if (typeof result[k] === 'object') {
|
|
1075
|
+
const found = findExpireInResult(result[k]);
|
|
1076
|
+
|
|
1077
|
+
// if we found the expiration
|
|
1078
|
+
if (found !== null) {
|
|
1079
|
+
expire = new Date(result).getTime();
|
|
1080
|
+
break;
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
// return the found expiration
|
|
1086
|
+
return expire;
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
/*
|
|
1090
|
+
* INTERNAL FUNCTION: makes the request and processes the response
|
|
1091
|
+
* for the request to get the token
|
|
1092
|
+
*/
|
|
1093
|
+
function getToken(reqPath, options, tokenSchema, bodyString, callProperties, callback) {
|
|
1094
|
+
const origin = `${id}-connectorRest-getToken`;
|
|
1095
|
+
log.trace(origin);
|
|
1096
|
+
|
|
1097
|
+
try {
|
|
1098
|
+
// no need for orig path since we handled the stub case
|
|
1099
|
+
const request = {
|
|
1100
|
+
header: options,
|
|
1101
|
+
body: bodyString,
|
|
1102
|
+
origPath: tokenSchema.entitypath
|
|
1103
|
+
};
|
|
1104
|
+
|
|
1105
|
+
// set stub if that is the mode we are in
|
|
1106
|
+
let useStub = false;
|
|
1107
|
+
if (callProperties && Object.hasOwnProperty.call(callProperties, 'stub')) {
|
|
1108
|
+
useStub = callProperties.stub;
|
|
1109
|
+
} else if (stub) {
|
|
1110
|
+
useStub = stub;
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
// if there is a mock result, return that
|
|
1114
|
+
if (useStub && tokenSchema) {
|
|
1115
|
+
// get the data from the mock data file
|
|
1116
|
+
const tokenResp = returnStub(request, tokenSchema, callProperties);
|
|
1117
|
+
|
|
1118
|
+
// if the request failed, return the error
|
|
1119
|
+
if (tokenResp.code < 200 || tokenResp.code > 299) {
|
|
1120
|
+
const errorObj = transUtilInst.formatErrorObject(origin, 'Unable To Authenticate', ['Token', tokenResp.code], null, null, null);
|
|
1121
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
1122
|
+
return callback(null, errorObj);
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
if (!tokenSchema.responseDatatype || tokenSchema.responseDatatype === 'JSON') {
|
|
1126
|
+
tokenResp.response = JSON.parse(tokenResp.response);
|
|
1127
|
+
}
|
|
1128
|
+
log.debug(`${origin}: ${JSON.stringify(tokenResp.response)}`);
|
|
1129
|
+
|
|
1130
|
+
// return the token from the token schema
|
|
1131
|
+
let translated = null;
|
|
1132
|
+
if (typeof tokenResp.response !== 'string') {
|
|
1133
|
+
translated = transUtilInst.mapFromOutboundEntity(tokenResp.response, tokenSchema.responseSchema);
|
|
1134
|
+
} else {
|
|
1135
|
+
translated = tokenResp.response;
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
// if what we got back is an array, just return the first element
|
|
1139
|
+
// should only have one token!!!
|
|
1140
|
+
if (translated && Array.isArray(translated)) {
|
|
1141
|
+
translated[0].front = tokenSchema.responseSchema.properties.token.front;
|
|
1142
|
+
translated[0].end = tokenSchema.responseSchema.properties.token.end;
|
|
1143
|
+
return callback(translated[0]);
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
// return the token that we find in the translated object
|
|
1147
|
+
translated.front = tokenSchema.responseSchema.properties.token.front;
|
|
1148
|
+
translated.end = tokenSchema.responseSchema.properties.token.end;
|
|
1149
|
+
return callback(translated);
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
if (useStub || reqPath === tokenPath || (tokenSchema && reqPath === tokenSchema.entitypath)) {
|
|
1153
|
+
// do not make the call to return a token if the request is actually
|
|
1154
|
+
// to get a token. Getting a token to get a token -- that should go
|
|
1155
|
+
// direct to make request
|
|
1156
|
+
return callback({ token: 'faketoken' });
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
log.debug(`${origin}: OPTIONS: ${JSON.stringify(options)}`);
|
|
1160
|
+
|
|
1161
|
+
// request the token
|
|
1162
|
+
return makeRequest(request, tokenSchema, callProperties, null, 0, (result, merror) => {
|
|
1163
|
+
if (merror) {
|
|
1164
|
+
return callback(null, merror);
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
// if the request is a redirect, try to pull token but log a warning
|
|
1168
|
+
let handleTokenRedirect = false;
|
|
1169
|
+
if (result.code >= 300 && result.code <= 308 && numRedirects === 0) {
|
|
1170
|
+
log.warn(`${origin}: Going to attempt to get token from redirect message!`);
|
|
1171
|
+
handleTokenRedirect = true;
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
// if the request failed, return the error
|
|
1175
|
+
if ((result.code < 200 || result.code > 299) && !handleTokenRedirect) {
|
|
1176
|
+
const errorObj = transUtilInst.formatErrorObject(origin, 'Unable To Authenticate', ['Token', result.code], null, null, null);
|
|
1177
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
1178
|
+
return callback(null, errorObj);
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
if (!tokenSchema) {
|
|
1182
|
+
// if no token schema, can not determine what to return
|
|
1183
|
+
const errorObj = transUtilInst.formatErrorObject(origin, 'Unable To Authenticate', ['Token', result.code], null, null, null);
|
|
1184
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
1185
|
+
return callback(null, errorObj);
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
// parse the token out of the result
|
|
1189
|
+
const currResult = {
|
|
1190
|
+
token: null,
|
|
1191
|
+
tokenp2: null,
|
|
1192
|
+
front: tokenSchema.responseSchema.properties.token.front,
|
|
1193
|
+
end: tokenSchema.responseSchema.properties.token.end
|
|
1194
|
+
};
|
|
1195
|
+
|
|
1196
|
+
// process primary token from header
|
|
1197
|
+
if (tokenSchema.responseSchema && tokenSchema.responseSchema.properties && tokenSchema.responseSchema.properties.token
|
|
1198
|
+
&& tokenSchema.responseSchema.properties.token.placement && tokenSchema.responseSchema.properties.token.placement.toUpperCase() === 'HEADER') {
|
|
1199
|
+
if (!tokenSchema.responseSchema.properties.token.external_name) {
|
|
1200
|
+
const errorObj = transUtilInst.formatErrorObject(origin, 'Unable To Get Primary Token', ['Primary Token', result.code], null, null, null);
|
|
1201
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
1202
|
+
return callback(null, errorObj);
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
const exName = tokenSchema.responseSchema.properties.token.external_name.toLowerCase();
|
|
1206
|
+
const headKeys = Object.keys(result.headers);
|
|
1207
|
+
let fullToken = null;
|
|
1208
|
+
let setCookie = null;
|
|
1209
|
+
|
|
1210
|
+
// go through and find the token
|
|
1211
|
+
for (let h = 0; h < headKeys.length; h += 1) {
|
|
1212
|
+
if (headKeys[h].toLowerCase() === exName) {
|
|
1213
|
+
fullToken = result.headers[headKeys[h]];
|
|
1214
|
+
currResult.token = result.headers[headKeys[h]];
|
|
1215
|
+
}
|
|
1216
|
+
if (headKeys[h].toLowerCase() === 'set-cookie') {
|
|
1217
|
+
setCookie = result.headers[headKeys[h]];
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
// if the token is in the requestToken
|
|
1222
|
+
if (exName === 'requestcookie' && result.requestCookie) {
|
|
1223
|
+
currResult.token = result.requestCookie;
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
// if the exName field is an array
|
|
1227
|
+
if (exName === 'set-cookie' && fullToken && Array.isArray(fullToken)) {
|
|
1228
|
+
currResult.token = fullToken[0];
|
|
1229
|
+
for (let ex = 1; ex < fullToken.length; ex += 1) {
|
|
1230
|
+
currResult.token += `; ${fullToken[ex]}`;
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
// if the token has been returned in the cookie
|
|
1235
|
+
if (exName.substring(0, 11) === 'set-cookie.') {
|
|
1236
|
+
const fname = exName.substring(11).toLowerCase();
|
|
1237
|
+
let thisCook = null;
|
|
1238
|
+
|
|
1239
|
+
// if the cookie is an array - usual case
|
|
1240
|
+
if (setCookie && Array.isArray(setCookie)) {
|
|
1241
|
+
// go through the array looking for the defined token field
|
|
1242
|
+
for (let sc = 0; sc < setCookie.length; sc += 1) {
|
|
1243
|
+
// parses the cookie into an object
|
|
1244
|
+
thisCook = cookieHandler.parse(setCookie[sc]);
|
|
1245
|
+
const cookKeys = Object.keys(thisCook);
|
|
1246
|
+
let set = false;
|
|
1247
|
+
|
|
1248
|
+
// go through the cookie to find the token
|
|
1249
|
+
for (let h = 0; h < cookKeys.length; h += 1) {
|
|
1250
|
+
if (cookKeys[h].toLowerCase() === fname) {
|
|
1251
|
+
currResult.token = thisCook[cookKeys[h]];
|
|
1252
|
+
set = true;
|
|
1253
|
+
break;
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
if (set) {
|
|
1257
|
+
break;
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
} else if (setCookie) {
|
|
1261
|
+
// if the cookie is just one string, parse into an object
|
|
1262
|
+
thisCook = cookieHandler.parse(setCookie);
|
|
1263
|
+
const cookKeys = Object.keys(thisCook);
|
|
1264
|
+
|
|
1265
|
+
// go through the cookie to find the token
|
|
1266
|
+
for (let h = 0; h < cookKeys.length; h += 1) {
|
|
1267
|
+
if (cookKeys[h].toLowerCase() === fname) {
|
|
1268
|
+
currResult.token = thisCook[cookKeys[h]];
|
|
1269
|
+
break;
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
// process second token from header
|
|
1277
|
+
if (tokenSchema.responseSchema && tokenSchema.responseSchema.properties && tokenSchema.responseSchema.properties.tokenp2
|
|
1278
|
+
&& tokenSchema.responseSchema.properties.tokenp2.placement && tokenSchema.responseSchema.properties.tokenp2.placement.toUpperCase() === 'HEADER') {
|
|
1279
|
+
if (!tokenSchema.responseSchema.properties.tokenp2.external_name) {
|
|
1280
|
+
const errorObj = transUtilInst.formatErrorObject(origin, 'Unable To Get Secondary Token', ['Secondary Token', result.code], null, null, null);
|
|
1281
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
1282
|
+
return callback(null, errorObj);
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
const exName = tokenSchema.responseSchema.properties.tokenp2.external_name.toLowerCase();
|
|
1286
|
+
const headKeys = Object.keys(result.headers);
|
|
1287
|
+
let fullToken = null;
|
|
1288
|
+
let setCookie = null;
|
|
1289
|
+
|
|
1290
|
+
// go through and find the token
|
|
1291
|
+
for (let h = 0; h < headKeys.length; h += 1) {
|
|
1292
|
+
if (headKeys[h].toLowerCase() === exName) {
|
|
1293
|
+
fullToken = result.headers[headKeys[h]];
|
|
1294
|
+
currResult.tokenp2 = result.headers[headKeys[h]];
|
|
1295
|
+
}
|
|
1296
|
+
if (headKeys[h].toLowerCase() === 'set-cookie') {
|
|
1297
|
+
setCookie = result.headers[headKeys[h]];
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
// if the token is in the requestToken
|
|
1302
|
+
if (exName === 'requestcookie' && result.requestCookie) {
|
|
1303
|
+
currResult.token = result.requestCookie;
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
// if the exName field is an array
|
|
1307
|
+
if (exName === 'set-cookie' && fullToken && Array.isArray(fullToken)) {
|
|
1308
|
+
currResult.tokenp2 = fullToken[0];
|
|
1309
|
+
for (let ex = 1; ex < fullToken.length; ex += 1) {
|
|
1310
|
+
currResult.tokenp2 += `; ${fullToken[ex]}`;
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
// if the token has been returned in the cookie
|
|
1315
|
+
if (exName.substring(0, 11) === 'set-cookie.') {
|
|
1316
|
+
const fname = exName.substring(11).toLowerCase();
|
|
1317
|
+
let thisCook = null;
|
|
1318
|
+
|
|
1319
|
+
// if the cookie is an array - usual case
|
|
1320
|
+
if (setCookie && Array.isArray(setCookie)) {
|
|
1321
|
+
// go through the array looking for the defined token field
|
|
1322
|
+
for (let sc = 0; sc < setCookie.length; sc += 1) {
|
|
1323
|
+
// parses the cookie into an object
|
|
1324
|
+
thisCook = cookieHandler.parse(setCookie[sc]);
|
|
1325
|
+
const cookKeys = Object.keys(thisCook);
|
|
1326
|
+
let set = false;
|
|
1327
|
+
|
|
1328
|
+
// go through the cookie to find the token
|
|
1329
|
+
for (let h = 0; h < cookKeys.length; h += 1) {
|
|
1330
|
+
if (cookKeys[h].toLowerCase() === fname) {
|
|
1331
|
+
currResult.tokenp2 = thisCook[cookKeys[h]];
|
|
1332
|
+
set = true;
|
|
1333
|
+
break;
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
if (set) {
|
|
1337
|
+
break;
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
} else if (setCookie) {
|
|
1341
|
+
// if the cookie is just one string, parse into an object
|
|
1342
|
+
thisCook = cookieHandler.parse(setCookie);
|
|
1343
|
+
const cookKeys = Object.keys(thisCook);
|
|
1344
|
+
|
|
1345
|
+
// go through the cookie to find the token
|
|
1346
|
+
for (let h = 0; h < cookKeys.length; h += 1) {
|
|
1347
|
+
if (cookKeys[h].toLowerCase() === fname) {
|
|
1348
|
+
currResult.tokenp2 = thisCook[cookKeys[h]];
|
|
1349
|
+
break;
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
// process the body
|
|
1357
|
+
// if response is just a string
|
|
1358
|
+
if (Object.hasOwnProperty.call(tokenSchema, 'responseDatatype') && tokenSchema.responseDatatype.toUpperCase() === 'PLAIN') {
|
|
1359
|
+
if (tokenSchema.responseSchema && tokenSchema.responseSchema.properties && tokenSchema.responseSchema.properties.token
|
|
1360
|
+
&& tokenSchema.responseSchema.properties.token.placement && tokenSchema.responseSchema.properties.token.placement.toUpperCase() === 'BODY') {
|
|
1361
|
+
currResult.token = result.response;
|
|
1362
|
+
|
|
1363
|
+
// if we got a stringified string - we can remove the double quotes wrapping it
|
|
1364
|
+
if (currResult.token.substring(0, 1) === '"' && currResult.token.substring(-1, 1) === '"') {
|
|
1365
|
+
currResult.token = currResult.token.substring(1, currResult.token.length - 1);
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
if (tokenSchema.responseSchema && tokenSchema.responseSchema.properties && tokenSchema.responseSchema.properties.tokenp2
|
|
1369
|
+
&& tokenSchema.responseSchema.properties.tokenp2.placement && tokenSchema.responseSchema.properties.tokenp2.placement.toUpperCase() === 'BODY') {
|
|
1370
|
+
currResult.tokenp2 = result.response;
|
|
1371
|
+
|
|
1372
|
+
// if we got a stringified string - we can remove the double quotes wrapping it
|
|
1373
|
+
if (currResult.token.substring(0, 1) === '"' && currResult.token.substring(-1, 1) === '"') {
|
|
1374
|
+
currResult.token = currResult.token.substring(1, currResult.token.length - 1);
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
// return the string as there is no other processing needed
|
|
1379
|
+
return callback(currResult);
|
|
1380
|
+
}
|
|
1381
|
+
if (Object.hasOwnProperty.call(tokenSchema, 'responseDatatype') && tokenSchema.responseDatatype.toUpperCase() === 'XML') {
|
|
1382
|
+
if (tokenSchema.responseSchema && tokenSchema.responseSchema.properties && tokenSchema.responseSchema.properties.token
|
|
1383
|
+
&& tokenSchema.responseSchema.properties.token.placement && tokenSchema.responseSchema.properties.token.placement.toUpperCase() === 'BODY') {
|
|
1384
|
+
currResult.token = result.response;
|
|
1385
|
+
}
|
|
1386
|
+
if (tokenSchema.responseSchema && tokenSchema.responseSchema.properties && tokenSchema.responseSchema.properties.tokenp2
|
|
1387
|
+
&& tokenSchema.responseSchema.properties.tokenp2.placement && tokenSchema.responseSchema.properties.tokenp2.placement.toUpperCase() === 'BODY') {
|
|
1388
|
+
currResult.tokenp2 = result.response;
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
// return the xml as there is no other processing needed
|
|
1392
|
+
return callback(currResult);
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
// if response can be put into a JSON object
|
|
1396
|
+
let tempResult = null;
|
|
1397
|
+
if (result.response) {
|
|
1398
|
+
if (Object.hasOwnProperty.call(tokenSchema, 'responseDatatype') && tokenSchema.responseDatatype.toUpperCase() === 'XML2JSON') {
|
|
1399
|
+
try {
|
|
1400
|
+
const parser = new xml2js.Parser({ explicitArray: false, attrkey: '_attr' });
|
|
1401
|
+
parser.parseString(result.response, (error, presult) => {
|
|
1402
|
+
if (error) {
|
|
1403
|
+
log.warn(`${origin}: Unable to parse xml to json ${error}`);
|
|
1404
|
+
return callback(parser.toJson(presult));
|
|
1405
|
+
}
|
|
1406
|
+
tempResult = presult;
|
|
1407
|
+
});
|
|
1408
|
+
} catch (ex) {
|
|
1409
|
+
log.warn(`${origin}: Unable to get json from xml ${ex}`);
|
|
1410
|
+
if (tokenSchema.responseSchema && tokenSchema.responseSchema.properties && tokenSchema.responseSchema.properties.token
|
|
1411
|
+
&& tokenSchema.responseSchema.properties.token.placement && tokenSchema.responseSchema.properties.token.placement.toUpperCase() === 'BODY') {
|
|
1412
|
+
currResult.token = result.response;
|
|
1413
|
+
}
|
|
1414
|
+
if (tokenSchema.responseSchema && tokenSchema.responseSchema.properties && tokenSchema.responseSchema.properties.tokenp2
|
|
1415
|
+
&& tokenSchema.responseSchema.properties.tokenp2.placement && tokenSchema.responseSchema.properties.tokenp2.placement.toUpperCase() === 'BODY') {
|
|
1416
|
+
currResult.tokenp2 = result.response;
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
if (Object.hasOwnProperty.call(tokenSchema, 'responseDatatype') && tokenSchema.responseDatatype.toUpperCase() === 'URLENCODE') {
|
|
1421
|
+
tempResult = querystring.parse(result.response.trim());
|
|
1422
|
+
} else {
|
|
1423
|
+
try {
|
|
1424
|
+
tempResult = JSON.parse(result.response.trim());
|
|
1425
|
+
} catch (exc) {
|
|
1426
|
+
log.warn(exc);
|
|
1427
|
+
}
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1431
|
+
// at this point if there is nothing in tempResult nothing further to do
|
|
1432
|
+
if (!tempResult) {
|
|
1433
|
+
return callback(currResult);
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1436
|
+
// need to see if there is a response key
|
|
1437
|
+
if (tokenSchema.responseObjects) {
|
|
1438
|
+
let tKey = null;
|
|
1439
|
+
|
|
1440
|
+
// it should be an array - then take the first element of array
|
|
1441
|
+
if (Array.isArray(tokenSchema.responseObjects)) {
|
|
1442
|
+
if (tokenSchema.responseObjects[0].key) {
|
|
1443
|
+
tKey = tokenSchema.responseObjects[0].key;
|
|
1444
|
+
}
|
|
1445
|
+
} else if (tokenSchema.responseObjects.key) {
|
|
1446
|
+
tKey = tokenSchema.responseObjects.key;
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
// if we found a key, use it
|
|
1450
|
+
if (tKey) {
|
|
1451
|
+
tempResult = jsonQuery(tKey, { data: tempResult }).value;
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1454
|
+
|
|
1455
|
+
// the token should not be an array so if it is, return the 0 item.
|
|
1456
|
+
if (Array.isArray(tempResult)) {
|
|
1457
|
+
tempResult = tempResult[0];
|
|
1458
|
+
}
|
|
1459
|
+
|
|
1460
|
+
// return the token from the token schema
|
|
1461
|
+
let translated = transUtilInst.mapFromOutboundEntity(tempResult, tokenSchema.responseSchema);
|
|
1462
|
+
|
|
1463
|
+
// if what we got back is an array, just return the first element
|
|
1464
|
+
// should only have one token!!!
|
|
1465
|
+
if (translated && Array.isArray(translated)) {
|
|
1466
|
+
translated = translated[0];
|
|
1467
|
+
}
|
|
1468
|
+
|
|
1469
|
+
if (tokenSchema.responseSchema && tokenSchema.responseSchema.properties && tokenSchema.responseSchema.properties.token
|
|
1470
|
+
&& tokenSchema.responseSchema.properties.token.placement && tokenSchema.responseSchema.properties.token.placement.toUpperCase() === 'BODY') {
|
|
1471
|
+
currResult.token = translated.token;
|
|
1472
|
+
}
|
|
1473
|
+
if (tokenSchema.responseSchema && tokenSchema.responseSchema.properties && tokenSchema.responseSchema.properties.tokenp2
|
|
1474
|
+
&& tokenSchema.responseSchema.properties.tokenp2.placement && tokenSchema.responseSchema.properties.tokenp2.placement.toUpperCase() === 'BODY') {
|
|
1475
|
+
currResult.tokenp2 = translated.tokenp2;
|
|
1476
|
+
}
|
|
1477
|
+
|
|
1478
|
+
// return the token that we find in the translated object
|
|
1479
|
+
return callback(currResult);
|
|
1480
|
+
});
|
|
1481
|
+
} catch (e) {
|
|
1482
|
+
// handle any exception
|
|
1483
|
+
const errorObj = transUtilInst.checkAndReturn(e, origin, 'Issue retrieving a token');
|
|
1484
|
+
return callback(null, errorObj);
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
/*
|
|
1489
|
+
* INTERNAL FUNCTION: prepares a request to get a token from the system
|
|
1490
|
+
*/
|
|
1491
|
+
function buildTokenRequest(reqPath, reqBody, callProperties, callback) {
|
|
1492
|
+
const origin = `${id}-connectorRest-buildTokenRequest`;
|
|
1493
|
+
log.trace(origin);
|
|
1494
|
+
|
|
1495
|
+
try {
|
|
1496
|
+
// Get the entity schema from the file system
|
|
1497
|
+
return propUtilInst.getEntitySchema('.system', 'getToken', this.dbUtil, (tokenSchema, healthError) => {
|
|
1498
|
+
if (healthError || !tokenSchema || Object.keys(tokenSchema).length === 0) {
|
|
1499
|
+
log.debug(`${origin}: Using adapter properties for token information`);
|
|
1500
|
+
tokenSchema = null;
|
|
1501
|
+
} else {
|
|
1502
|
+
log.debug(`${origin}: Using action and schema for token information`);
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1505
|
+
// prepare the additional headers we received
|
|
1506
|
+
let thisAHdata = null;
|
|
1507
|
+
|
|
1508
|
+
if (tokenSchema) {
|
|
1509
|
+
thisAHdata = transUtilInst.mergeObjects(thisAHdata, tokenSchema.headers);
|
|
1510
|
+
}
|
|
1511
|
+
if (globalRequest) {
|
|
1512
|
+
thisAHdata = transUtilInst.mergeObjects(thisAHdata, globalRequest.addlHeaders);
|
|
1513
|
+
}
|
|
1514
|
+
|
|
1515
|
+
// set up the right credentials - passed in overrides default
|
|
1516
|
+
let useUser = username;
|
|
1517
|
+
let usePass = password;
|
|
1518
|
+
|
|
1519
|
+
if (callProperties && callProperties.authentication && callProperties.authentication.username) {
|
|
1520
|
+
useUser = callProperties.authentication.username;
|
|
1521
|
+
}
|
|
1522
|
+
if (callProperties && callProperties.authentication && callProperties.authentication.password) {
|
|
1523
|
+
usePass = callProperties.authentication.password;
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1526
|
+
// if no header data passed in create empty - will add data below
|
|
1527
|
+
if (!thisAHdata) {
|
|
1528
|
+
thisAHdata = {};
|
|
1529
|
+
} else {
|
|
1530
|
+
const hKeys = Object.keys(thisAHdata);
|
|
1531
|
+
|
|
1532
|
+
for (let h = 0; h < hKeys.length; h += 1) {
|
|
1533
|
+
let tempStr = thisAHdata[hKeys[h]];
|
|
1534
|
+
|
|
1535
|
+
// replace username variable
|
|
1536
|
+
if (tempStr.indexOf('{username}') >= 0) {
|
|
1537
|
+
tempStr = tempStr.replace('{username}', useUser);
|
|
1538
|
+
}
|
|
1539
|
+
// replace password variable
|
|
1540
|
+
if (tempStr.indexOf('{password}') >= 0) {
|
|
1541
|
+
tempStr = tempStr.replace('{password}', usePass);
|
|
1542
|
+
}
|
|
1543
|
+
thisAHdata[hKeys[h]] = tempStr;
|
|
1544
|
+
|
|
1545
|
+
// handle any base64 encoding required on the authStr
|
|
1546
|
+
if (thisAHdata[hKeys[h]].indexOf('{b64}') >= 0) {
|
|
1547
|
+
// get the range to be encoded
|
|
1548
|
+
const sIndex = thisAHdata[hKeys[h]].indexOf('{b64}');
|
|
1549
|
+
const eIndex = thisAHdata[hKeys[h]].indexOf('{/b64}');
|
|
1550
|
+
|
|
1551
|
+
// if start but no end - return an error
|
|
1552
|
+
if (sIndex >= 0 && eIndex < sIndex + 5) {
|
|
1553
|
+
const errorObj = transUtilInst.formatErrorObject(origin, 'Unable To Encode', [thisAHdata[hKeys[h]]], null, null, null);
|
|
1554
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
1555
|
+
return callback(null, errorObj);
|
|
1556
|
+
}
|
|
1557
|
+
|
|
1558
|
+
// get the string to be encoded
|
|
1559
|
+
const bufString = thisAHdata[hKeys[h]].substring(sIndex + 5, eIndex);
|
|
1560
|
+
|
|
1561
|
+
// encode the string
|
|
1562
|
+
const encString = Buffer.from(bufString).toString('base64');
|
|
1563
|
+
let tempAuthStr = '';
|
|
1564
|
+
|
|
1565
|
+
// build the new auth field with the encoded string
|
|
1566
|
+
if (sIndex > 0) {
|
|
1567
|
+
// add the start of the string that did not need encoding
|
|
1568
|
+
tempAuthStr = thisAHdata[hKeys[h]].substring(0, sIndex);
|
|
1569
|
+
}
|
|
1570
|
+
// add the encoded string
|
|
1571
|
+
tempAuthStr += encString;
|
|
1572
|
+
if (eIndex + 5 < thisAHdata[hKeys[h]].length) {
|
|
1573
|
+
// add the end of the string that did not need encoding
|
|
1574
|
+
tempAuthStr += thisAHdata[hKeys[h]].substring(eIndex + 6);
|
|
1575
|
+
}
|
|
1576
|
+
|
|
1577
|
+
// put the temp string into the auth string we will add to the request
|
|
1578
|
+
thisAHdata[hKeys[h]] = tempAuthStr;
|
|
1579
|
+
}
|
|
1580
|
+
}
|
|
1581
|
+
}
|
|
1582
|
+
|
|
1583
|
+
// set the Content Type headers based on the type of request data for the call
|
|
1584
|
+
if (thisAHdata['Content-Type'] === undefined || thisAHdata['Content-Type'] === null) {
|
|
1585
|
+
if (tokenSchema && tokenSchema.requestDatatype && tokenSchema.requestDatatype.toUpperCase() === 'PLAIN') {
|
|
1586
|
+
// add the Plain headers if they were not set already
|
|
1587
|
+
thisAHdata['Content-Type'] = 'text/plain';
|
|
1588
|
+
} else if (tokenSchema && tokenSchema.requestDatatype && tokenSchema.requestDatatype.toUpperCase() === 'XML') {
|
|
1589
|
+
// add the XML headers if they were not set already
|
|
1590
|
+
thisAHdata['Content-Type'] = 'application/xml';
|
|
1591
|
+
} else if (tokenSchema && tokenSchema.requestDatatype && tokenSchema.requestDatatype.toUpperCase() === 'URLENCODE') {
|
|
1592
|
+
// add the URLENCODE headers if they were not set already
|
|
1593
|
+
thisAHdata['Content-Type'] = 'application/x-www-form-urlencoded';
|
|
1594
|
+
} else {
|
|
1595
|
+
// add the JSON headers if they were not set already
|
|
1596
|
+
thisAHdata['Content-Type'] = 'application/json';
|
|
1597
|
+
}
|
|
1598
|
+
}
|
|
1599
|
+
// set the Accept headers based on the type of response data for the call
|
|
1600
|
+
if (thisAHdata.Accept === undefined || thisAHdata.Accept === null) {
|
|
1601
|
+
if (tokenSchema && tokenSchema.responseDatatype && tokenSchema.responseDatatype.toUpperCase() === 'PLAIN') {
|
|
1602
|
+
// add the Plain headers if they were not set already
|
|
1603
|
+
thisAHdata.Accept = 'text/plain';
|
|
1604
|
+
} else if (tokenSchema && tokenSchema.responseDatatype && tokenSchema.responseDatatype.toUpperCase() === 'XML') {
|
|
1605
|
+
// add the XML headers if they were not set already
|
|
1606
|
+
thisAHdata.Accept = 'application/xml';
|
|
1607
|
+
} else if (tokenSchema && tokenSchema.responseDatatype && tokenSchema.responseDatatype.toUpperCase() === 'URLENCODE') {
|
|
1608
|
+
// add the URLENCODE headers if they were not set already
|
|
1609
|
+
thisAHdata.Accept = 'application/x-www-form-urlencoded';
|
|
1610
|
+
} else {
|
|
1611
|
+
// add the JSON headers if they were not set already
|
|
1612
|
+
thisAHdata.Accept = 'application/json';
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1616
|
+
if (thisAHdata.Accept === '') {
|
|
1617
|
+
delete thisAHdata.Accept;
|
|
1618
|
+
}
|
|
1619
|
+
if (thisAHdata['Content-Type'] === '') {
|
|
1620
|
+
delete thisAHdata['Content-Type'];
|
|
1621
|
+
}
|
|
1622
|
+
|
|
1623
|
+
// set up the options for the call to get incidents - default is all
|
|
1624
|
+
const options = {
|
|
1625
|
+
hostname: host,
|
|
1626
|
+
port,
|
|
1627
|
+
path: tokenPath,
|
|
1628
|
+
method: 'POST',
|
|
1629
|
+
headers: thisAHdata
|
|
1630
|
+
};
|
|
1631
|
+
|
|
1632
|
+
// passed in properties override defaults
|
|
1633
|
+
if (callProperties && callProperties.host) {
|
|
1634
|
+
options.hostname = callProperties.host;
|
|
1635
|
+
}
|
|
1636
|
+
if (callProperties && callProperties.port) {
|
|
1637
|
+
options.port = callProperties.port;
|
|
1638
|
+
}
|
|
1639
|
+
|
|
1640
|
+
// specific token properties override everything (Single Sign On System)
|
|
1641
|
+
if (tokenSchema && tokenSchema.sso && tokenSchema.sso.host) {
|
|
1642
|
+
options.hostname = tokenSchema.sso.host;
|
|
1643
|
+
}
|
|
1644
|
+
if (tokenSchema && tokenSchema.sso && tokenSchema.sso.port) {
|
|
1645
|
+
options.port = tokenSchema.sso.port;
|
|
1646
|
+
}
|
|
1647
|
+
|
|
1648
|
+
// If there is a token schema, need to take the data from there
|
|
1649
|
+
if (tokenSchema) {
|
|
1650
|
+
options.path = tokenSchema.entitypath;
|
|
1651
|
+
options.method = tokenSchema.method;
|
|
1652
|
+
}
|
|
1653
|
+
|
|
1654
|
+
// if the path has a base path parameter in it, need to replace it
|
|
1655
|
+
let bpathStr = '{base_path}';
|
|
1656
|
+
if (options.path.indexOf(bpathStr) >= 0) {
|
|
1657
|
+
// be able to support this if the base path has a slash before it or not
|
|
1658
|
+
if (options.path.indexOf('/{base_path}') >= 0) {
|
|
1659
|
+
bpathStr = '/{base_path}';
|
|
1660
|
+
}
|
|
1661
|
+
|
|
1662
|
+
// replace with base path if we have one, otherwise remove base path
|
|
1663
|
+
if (callProperties && callProperties.base_path) {
|
|
1664
|
+
// if no leading /, insert one
|
|
1665
|
+
if (callProperties.base_path.indexOf('/') !== 0) {
|
|
1666
|
+
options.path = options.path.replace(bpathStr, `/${callProperties.base_path}`);
|
|
1667
|
+
} else {
|
|
1668
|
+
options.path = options.path.replace(bpathStr, callProperties.base_path);
|
|
1669
|
+
}
|
|
1670
|
+
} else if (basepath) {
|
|
1671
|
+
// if no leading /, insert one
|
|
1672
|
+
if (basepath.indexOf('/') !== 0) {
|
|
1673
|
+
options.path = options.path.replace(bpathStr, `/${basepath}`);
|
|
1674
|
+
} else {
|
|
1675
|
+
options.path = options.path.replace(bpathStr, basepath);
|
|
1676
|
+
}
|
|
1677
|
+
} else {
|
|
1678
|
+
options.path = options.path.replace(bpathStr, '');
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1681
|
+
|
|
1682
|
+
// if the path has a version parameter in it, need to replace it
|
|
1683
|
+
let versStr = '{version}';
|
|
1684
|
+
if (options.path.indexOf(versStr) >= 0) {
|
|
1685
|
+
// be able to support this if the version has a slash before it or not
|
|
1686
|
+
if (options.path.indexOf('/{version}') >= 0) {
|
|
1687
|
+
versStr = '/{version}';
|
|
1688
|
+
}
|
|
1689
|
+
|
|
1690
|
+
// replace with version if we have one, otherwise remove version
|
|
1691
|
+
if (callProperties && callProperties.version) {
|
|
1692
|
+
options.path = options.path.replace(versStr, `/${encodeURIComponent(callProperties.version)}`);
|
|
1693
|
+
} else if (version) {
|
|
1694
|
+
options.path = options.path.replace(versStr, `/${encodeURIComponent(version)}`);
|
|
1695
|
+
} else {
|
|
1696
|
+
options.path = options.path.replace(versStr, '');
|
|
1697
|
+
}
|
|
1698
|
+
}
|
|
1699
|
+
|
|
1700
|
+
// if there are URI path variables that have been provided, need to add
|
|
1701
|
+
// them to the path
|
|
1702
|
+
if (reqBody && reqBody.uriPathVars && reqBody.uriPathVars.length > 0) {
|
|
1703
|
+
for (let p = 0; p < reqBody.uriPathVars.length; p += 1) {
|
|
1704
|
+
const vnum = p + 1;
|
|
1705
|
+
const holder = `pathv${vnum.toString()}`;
|
|
1706
|
+
const hindex = options.path.indexOf(holder);
|
|
1707
|
+
|
|
1708
|
+
// if path variable is in the url, replace it!!!
|
|
1709
|
+
if (hindex >= 0 && reqBody.uriPathVars[p] !== null && reqBody.uriPathVars[p] !== '') {
|
|
1710
|
+
// with the provided id
|
|
1711
|
+
let idString = '';
|
|
1712
|
+
|
|
1713
|
+
// check if the current URI path ends with a slash (may require
|
|
1714
|
+
// slash at end)
|
|
1715
|
+
if (options.path[hindex - 2] === '/' || options.path[hindex - 2] === ':') {
|
|
1716
|
+
// ends with a slash need to add slash to end
|
|
1717
|
+
idString = encodeURIComponent(reqBody.uriPathVars[p]);
|
|
1718
|
+
} else {
|
|
1719
|
+
// otherwise add / to start
|
|
1720
|
+
idString = '/';
|
|
1721
|
+
idString += encodeURIComponent(reqBody.uriPathVars[p]);
|
|
1722
|
+
}
|
|
1723
|
+
|
|
1724
|
+
// replace the id in url with the id string
|
|
1725
|
+
options.path = options.path.replace(`{${holder}}`, idString);
|
|
1726
|
+
}
|
|
1727
|
+
}
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1730
|
+
// need to remove all of the remaining path holders from the URI
|
|
1731
|
+
while (options.path.indexOf('{pathv') >= 0) {
|
|
1732
|
+
let sIndex = options.path.indexOf('{pathv');
|
|
1733
|
+
const eIndex = options.path.indexOf('}', sIndex);
|
|
1734
|
+
|
|
1735
|
+
// if there is a / before the {pathv} need to remove it
|
|
1736
|
+
if (options.path[sIndex - 1] === '/' || options.path[sIndex - 1] === ':') {
|
|
1737
|
+
sIndex -= 1;
|
|
1738
|
+
}
|
|
1739
|
+
|
|
1740
|
+
if (sIndex > 0) {
|
|
1741
|
+
// add the start of the path
|
|
1742
|
+
let tempStr = options.path.substring(0, sIndex);
|
|
1743
|
+
|
|
1744
|
+
if (eIndex < options.path.length) {
|
|
1745
|
+
// add the end of the path
|
|
1746
|
+
tempStr += options.path.substring(eIndex + 1);
|
|
1747
|
+
}
|
|
1748
|
+
|
|
1749
|
+
options.path = tempStr;
|
|
1750
|
+
} else if (eIndex > 0 && eIndex < options.path.length) {
|
|
1751
|
+
// add the end of the path
|
|
1752
|
+
options.path = options.path.substring(eIndex + 1);
|
|
1753
|
+
} else {
|
|
1754
|
+
// should not get here - there is some issue in the uripath - missing
|
|
1755
|
+
// an end or the path is just {pathv#}
|
|
1756
|
+
// add the specific pieces of the error object
|
|
1757
|
+
const errorObj = this.transUtil.formatErrorObject(origin, 'Invalid Action File', ['missing entity path', '.system/getToken'], null, null, null);
|
|
1758
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
1759
|
+
return callback(null, errorObj);
|
|
1760
|
+
}
|
|
1761
|
+
}
|
|
1762
|
+
|
|
1763
|
+
// remove the path vars from the reqBody
|
|
1764
|
+
const actReqBody = Object.assign({}, reqBody);
|
|
1765
|
+
if (actReqBody && actReqBody.uriPathVars) {
|
|
1766
|
+
delete actReqBody.uriPathVars;
|
|
1767
|
+
}
|
|
1768
|
+
|
|
1769
|
+
// if ssl enabled add the options for ssl
|
|
1770
|
+
if (callProperties && callProperties.ssl && Object.hasOwnProperty.call(callProperties.ssl, 'enabled')) {
|
|
1771
|
+
if (callProperties.ssl.enabled) {
|
|
1772
|
+
if (callProperties.ssl.accept_invalid_cert) {
|
|
1773
|
+
// if we are accepting invalid certificates (ok for lab not so much production)
|
|
1774
|
+
options.rejectUnauthorized = false;
|
|
1775
|
+
} else {
|
|
1776
|
+
// if we are not accepting invalid certs, need the ca file in the options
|
|
1777
|
+
try {
|
|
1778
|
+
options.rejectUnauthorized = true;
|
|
1779
|
+
options.ca = [fs.readFileSync(callProperties.ssl.ca_file)];
|
|
1780
|
+
} catch (e) {
|
|
1781
|
+
const errorObj = this.transUtil.formatErrorObject(origin, 'Missing File', [callProperties.ssl.ca_file], null, null, null);
|
|
1782
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
1783
|
+
return callback(null, errorObj);
|
|
1784
|
+
}
|
|
1785
|
+
}
|
|
1786
|
+
|
|
1787
|
+
if (callProperties.ssl.ciphers) {
|
|
1788
|
+
options.ciphers = callProperties.ssl.ciphers;
|
|
1789
|
+
}
|
|
1790
|
+
if (callProperties.ssl.secure_protocol) {
|
|
1791
|
+
options.secureProtocol = callProperties.ssl.secure_protocol;
|
|
1792
|
+
}
|
|
1793
|
+
|
|
1794
|
+
log.info(`${origin}: Connector SSL connections enabled`);
|
|
1795
|
+
}
|
|
1796
|
+
} else if (sslEnabled) {
|
|
1797
|
+
if (sslAcceptInvalid) {
|
|
1798
|
+
// if we are accepting invalid certificates (ok for lab not so much production)
|
|
1799
|
+
options.rejectUnauthorized = false;
|
|
1800
|
+
} else {
|
|
1801
|
+
// if we are not accepting invalid certs, need the ca file in the options
|
|
1802
|
+
try {
|
|
1803
|
+
options.rejectUnauthorized = true;
|
|
1804
|
+
options.ca = [fs.readFileSync(sslCAFile)];
|
|
1805
|
+
} catch (e) {
|
|
1806
|
+
const errorObj = this.transUtil.formatErrorObject(origin, 'Missing File', [sslCAFile], null, null, null);
|
|
1807
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
1808
|
+
return callback(null, errorObj);
|
|
1809
|
+
}
|
|
1810
|
+
// if there is a cert file, try to read in a cert file in the options
|
|
1811
|
+
if (sslCertFile) {
|
|
1812
|
+
try {
|
|
1813
|
+
options.cert = [fs.readFileSync(sslCertFile)];
|
|
1814
|
+
} catch (e) {
|
|
1815
|
+
const errorObj = this.transUtil.formatErrorObject(origin, 'Missing File', [sslCertFile], null, null, null);
|
|
1816
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
1817
|
+
return callback(null, errorObj);
|
|
1818
|
+
}
|
|
1819
|
+
}
|
|
1820
|
+
// if there is a key file, try to read in a key file in the options
|
|
1821
|
+
if (sslKeyFile) {
|
|
1822
|
+
try {
|
|
1823
|
+
options.key = [fs.readFileSync(sslKeyFile)];
|
|
1824
|
+
} catch (e) {
|
|
1825
|
+
const errorObj = this.transUtil.formatErrorObject(origin, 'Missing File', [sslKeyFile], null, null, null);
|
|
1826
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
1827
|
+
return callback(null, errorObj);
|
|
1828
|
+
}
|
|
1829
|
+
}
|
|
1830
|
+
}
|
|
1831
|
+
|
|
1832
|
+
if (sslCiphers) {
|
|
1833
|
+
options.ciphers = sslCiphers;
|
|
1834
|
+
}
|
|
1835
|
+
if (secureProtocol) {
|
|
1836
|
+
options.secureProtocol = secureProtocol;
|
|
1837
|
+
}
|
|
1838
|
+
|
|
1839
|
+
log.info(`${origin}: Connector SSL connections enabled`);
|
|
1840
|
+
}
|
|
1841
|
+
|
|
1842
|
+
const reqData = {};
|
|
1843
|
+
let bodyString = null;
|
|
1844
|
+
|
|
1845
|
+
// if we have been passed a schema and the Authorization is in a header
|
|
1846
|
+
if (tokenSchema && tokenSchema.headers && tokenSchema.headers.Authorization) {
|
|
1847
|
+
// request the token
|
|
1848
|
+
options.headers['Content-length'] = 0;
|
|
1849
|
+
return getToken(reqPath, options, tokenSchema, '', callProperties, callback);
|
|
1850
|
+
}
|
|
1851
|
+
if (tokenSchema) {
|
|
1852
|
+
// if this is a get, need to put the username on the url
|
|
1853
|
+
if (options.path.indexOf('/{username}') >= 0) {
|
|
1854
|
+
options.path = options.path.replace('/{username}', useUser);
|
|
1855
|
+
} else {
|
|
1856
|
+
options.path = options.path.replace('{username}', useUser);
|
|
1857
|
+
}
|
|
1858
|
+
|
|
1859
|
+
// if this is a get, need to put the password on the url
|
|
1860
|
+
if (options.path.indexOf('/{password}') >= 0) {
|
|
1861
|
+
options.path = options.path.replace('/{password}', usePass);
|
|
1862
|
+
} else {
|
|
1863
|
+
options.path = options.path.replace('{password}', usePass);
|
|
1864
|
+
}
|
|
1865
|
+
|
|
1866
|
+
// if this is not a get, need to add the info to the request
|
|
1867
|
+
if (options.method !== 'GET') {
|
|
1868
|
+
let creds = {
|
|
1869
|
+
username: useUser,
|
|
1870
|
+
password: usePass
|
|
1871
|
+
};
|
|
1872
|
+
if (clientId) {
|
|
1873
|
+
creds.client_id = clientId;
|
|
1874
|
+
}
|
|
1875
|
+
if (clientSecret) {
|
|
1876
|
+
creds.client_secret = clientSecret;
|
|
1877
|
+
}
|
|
1878
|
+
if (grantType) {
|
|
1879
|
+
creds.grant_type = grantType;
|
|
1880
|
+
}
|
|
1881
|
+
if (callProperties && callProperties.authentication && callProperties.authentication.client_id) {
|
|
1882
|
+
creds.client_id = callProperties.authentication.client_id;
|
|
1883
|
+
}
|
|
1884
|
+
if (callProperties && callProperties.authentication && callProperties.authentication.client_secret) {
|
|
1885
|
+
creds.client_secret = callProperties.authentication.client_secret;
|
|
1886
|
+
}
|
|
1887
|
+
if (callProperties && callProperties.authentication && callProperties.authentication.grant_type) {
|
|
1888
|
+
creds.grant_type = callProperties.authentication.grant_type;
|
|
1889
|
+
}
|
|
1890
|
+
|
|
1891
|
+
// if there is body data to add to the token request body
|
|
1892
|
+
creds = transUtilInst.mergeObjects(actReqBody, creds);
|
|
1893
|
+
|
|
1894
|
+
if (globalRequest) {
|
|
1895
|
+
creds = transUtilInst.mergeObjects(creds, globalRequest.authData);
|
|
1896
|
+
}
|
|
1897
|
+
|
|
1898
|
+
// if there is body data to add to the token request body
|
|
1899
|
+
if (actReqBody) {
|
|
1900
|
+
const bodyKey = Object.keys(actReqBody);
|
|
1901
|
+
|
|
1902
|
+
for (let k = 0; k < bodyKey.length; k += 1) {
|
|
1903
|
+
creds[bodyKey[k]] = actReqBody[bodyKey[k]];
|
|
1904
|
+
}
|
|
1905
|
+
}
|
|
1906
|
+
|
|
1907
|
+
// map the data we received to an Entity - will get back the defaults
|
|
1908
|
+
const tokenEntity = transUtilInst.mapToOutboundEntity(creds, tokenSchema.requestSchema);
|
|
1909
|
+
bodyString = tokenEntity;
|
|
1910
|
+
|
|
1911
|
+
// if it is JSON or URLENCODE need to put body into right format
|
|
1912
|
+
if (!tokenSchema.requestDatatype || tokenSchema.requestDatatype.toUpperCase() === 'JSON' || tokenSchema.requestDatatype.toUpperCase() === 'FORM') {
|
|
1913
|
+
bodyString = JSON.stringify(tokenEntity);
|
|
1914
|
+
} else if (tokenSchema.requestDatatype && tokenSchema.requestDatatype.toUpperCase() === 'URLENCODE') {
|
|
1915
|
+
bodyString = querystring.stringify(tokenEntity);
|
|
1916
|
+
}
|
|
1917
|
+
|
|
1918
|
+
// if there is a body, set the content length of the body and add it to
|
|
1919
|
+
// the header
|
|
1920
|
+
if (Object.keys(tokenEntity).length > 0) {
|
|
1921
|
+
options.headers['Content-length'] = Buffer.byteLength(bodyString);
|
|
1922
|
+
}
|
|
1923
|
+
|
|
1924
|
+
// request the token
|
|
1925
|
+
return getToken(reqPath, options, tokenSchema, bodyString, callProperties, callback);
|
|
1926
|
+
}
|
|
1927
|
+
} else {
|
|
1928
|
+
// set the user and password for the token request
|
|
1929
|
+
reqData[tokenUserField] = useUser;
|
|
1930
|
+
reqData[tokenPwdField] = usePass;
|
|
1931
|
+
bodyString = reqData;
|
|
1932
|
+
|
|
1933
|
+
// since not a get call, convert reqData to a string and add length
|
|
1934
|
+
bodyString = JSON.stringify(reqData);
|
|
1935
|
+
options.headers['Content-length'] = Buffer.byteLength(bodyString);
|
|
1936
|
+
}
|
|
1937
|
+
|
|
1938
|
+
// request the token
|
|
1939
|
+
return getToken(reqPath, options, tokenSchema, bodyString, callProperties, callback);
|
|
1940
|
+
});
|
|
1941
|
+
} catch (e) {
|
|
1942
|
+
// handle any exception
|
|
1943
|
+
const errorObj = transUtilInst.checkAndReturn(e, origin, 'Issue requesting token');
|
|
1944
|
+
return callback(null, errorObj);
|
|
1945
|
+
}
|
|
1946
|
+
}
|
|
1947
|
+
|
|
1948
|
+
/*
|
|
1949
|
+
* INTERNAL FUNCTION: addTokenItem is used to add a token item to the
|
|
1950
|
+
* memory based on the user provided
|
|
1951
|
+
*/
|
|
1952
|
+
function addTokenItem(user, reqBody, token, timeout, callback) {
|
|
1953
|
+
const origin = `${id}-connectorRest-addTokenItem`;
|
|
1954
|
+
log.trace(origin);
|
|
1955
|
+
|
|
1956
|
+
try {
|
|
1957
|
+
// there is already a lock from the get, so no need to lock the tokenList
|
|
1958
|
+
const now = new Date();
|
|
1959
|
+
|
|
1960
|
+
// create the token item
|
|
1961
|
+
let tkey = `${id}__%%__${user}`;
|
|
1962
|
+
const tokenItem = {
|
|
1963
|
+
adapter: id,
|
|
1964
|
+
username: user,
|
|
1965
|
+
token,
|
|
1966
|
+
start: now.getTime(),
|
|
1967
|
+
expire: timeout
|
|
1968
|
+
};
|
|
1969
|
+
|
|
1970
|
+
if (reqBody) {
|
|
1971
|
+
tkey += `__%%__${JSON.stringify(reqBody)}`;
|
|
1972
|
+
}
|
|
1973
|
+
|
|
1974
|
+
tokenItem.tkey = tkey;
|
|
1975
|
+
|
|
1976
|
+
// add the token item to the tokenlist and return it
|
|
1977
|
+
if (tokenCache === 'local') {
|
|
1978
|
+
tokenList.push(tokenItem);
|
|
1979
|
+
return callback(token);
|
|
1980
|
+
}
|
|
1981
|
+
|
|
1982
|
+
// try to add the token item to the redis local memory
|
|
1983
|
+
return g_redis.set(tkey, JSON.stringify(tokenItem), (err) => {
|
|
1984
|
+
if (err) {
|
|
1985
|
+
log.error(`${origin}: ${tkey} not stored in redis`);
|
|
1986
|
+
}
|
|
1987
|
+
|
|
1988
|
+
// return the token
|
|
1989
|
+
return callback(token);
|
|
1990
|
+
});
|
|
1991
|
+
} catch (e) {
|
|
1992
|
+
// handle any exception
|
|
1993
|
+
const errorObj = transUtilInst.checkAndReturn(e, origin, 'Issue adding token');
|
|
1994
|
+
return callback(null, errorObj);
|
|
1995
|
+
}
|
|
1996
|
+
}
|
|
1997
|
+
|
|
1998
|
+
/*
|
|
1999
|
+
* INTERNAL FUNCTION: retrieve the token from cache and determine if
|
|
2000
|
+
* it valid. if valid, return it otherwise return null
|
|
2001
|
+
*/
|
|
2002
|
+
function validToken(user, reqBody, invalidToken, callback) {
|
|
2003
|
+
const origin = `${id}-connectorRest-validToken`;
|
|
2004
|
+
log.trace(origin);
|
|
2005
|
+
|
|
2006
|
+
try {
|
|
2007
|
+
// time to see if the token is expired
|
|
2008
|
+
const expireCheck = new Date().getTime() + 60000;
|
|
2009
|
+
|
|
2010
|
+
// get the token from redis
|
|
2011
|
+
let tkey = `${id}__%%__${user}`;
|
|
2012
|
+
|
|
2013
|
+
if (reqBody) {
|
|
2014
|
+
tkey += `__%%__${JSON.stringify(reqBody)}`;
|
|
2015
|
+
}
|
|
2016
|
+
|
|
2017
|
+
// determine where to get the token
|
|
2018
|
+
if (tokenCache === 'redis') {
|
|
2019
|
+
return g_redis.get(tkey, (err, res) => {
|
|
2020
|
+
if (err) {
|
|
2021
|
+
log.error(`${origin}: Error on retrieve token for ${tkey}`);
|
|
2022
|
+
}
|
|
2023
|
+
|
|
2024
|
+
// if there was no token returned
|
|
2025
|
+
if (!res) {
|
|
2026
|
+
return callback(null);
|
|
2027
|
+
}
|
|
2028
|
+
|
|
2029
|
+
const parsedToken = JSON.parse(res);
|
|
2030
|
+
|
|
2031
|
+
// if the token expired (or will expire within a minute),
|
|
2032
|
+
// or it is invalid (failed) remove it from the token list
|
|
2033
|
+
if (parsedToken.expire < expireCheck || parsedToken.token.token === invalidToken) {
|
|
2034
|
+
return g_redis.del(tkey, (derr) => {
|
|
2035
|
+
if (derr) {
|
|
2036
|
+
log.debug(`${origin}: Expired token for ${tkey} not removed from redis`);
|
|
2037
|
+
}
|
|
2038
|
+
|
|
2039
|
+
return callback(null);
|
|
2040
|
+
});
|
|
2041
|
+
}
|
|
2042
|
+
|
|
2043
|
+
// return the retrieved token
|
|
2044
|
+
return callback(parsedToken.token);
|
|
2045
|
+
});
|
|
2046
|
+
}
|
|
2047
|
+
|
|
2048
|
+
let retToken = null;
|
|
2049
|
+
|
|
2050
|
+
// find this user's token in the list
|
|
2051
|
+
for (let i = 0; i < tokenList.length; i += 1) {
|
|
2052
|
+
if (tokenList[i].tkey === tkey) {
|
|
2053
|
+
// if the token expired (or will expire within a minute),
|
|
2054
|
+
// or it is invalid (failed) remove it from the token list
|
|
2055
|
+
if (tokenList[i].expire < expireCheck
|
|
2056
|
+
|| tokenList[i].token.token === invalidToken) {
|
|
2057
|
+
tokenList.splice(i, 1);
|
|
2058
|
+
break;
|
|
2059
|
+
}
|
|
2060
|
+
|
|
2061
|
+
retToken = tokenList[i].token;
|
|
2062
|
+
break;
|
|
2063
|
+
}
|
|
2064
|
+
}
|
|
2065
|
+
return callback(retToken);
|
|
2066
|
+
} catch (e) {
|
|
2067
|
+
// handle any exception
|
|
2068
|
+
const errorObj = transUtilInst.checkAndReturn(e, origin, 'Issue validating cached token');
|
|
2069
|
+
return callback(null, errorObj);
|
|
2070
|
+
}
|
|
2071
|
+
}
|
|
2072
|
+
|
|
2073
|
+
/*
|
|
2074
|
+
* INTERNAL FUNCTION: getTokenItem is used to retrieve a token items from
|
|
2075
|
+
* memory based on the user provided
|
|
2076
|
+
*/
|
|
2077
|
+
function getTokenItem(pathForToken, user, reqBody, invalidToken, callProperties, callback) {
|
|
2078
|
+
const origin = `${id}-connectorRest-getTokenItem`;
|
|
2079
|
+
log.trace(origin);
|
|
2080
|
+
|
|
2081
|
+
try {
|
|
2082
|
+
// Lock the tokenList while getting the token
|
|
2083
|
+
return tlock.acquire(tokenlock, (done) => {
|
|
2084
|
+
validToken(user, reqBody, invalidToken, (retToken, verror) => {
|
|
2085
|
+
if (verror) {
|
|
2086
|
+
done(null, verror);
|
|
2087
|
+
}
|
|
2088
|
+
|
|
2089
|
+
// If valid token found, return it and skip the rest
|
|
2090
|
+
if (retToken !== null) {
|
|
2091
|
+
done(retToken, null);
|
|
2092
|
+
} else {
|
|
2093
|
+
// No valid token found, Need to get a new token and add it to the token list
|
|
2094
|
+
buildTokenRequest(pathForToken, reqBody, callProperties, (dyntoken, berror) => {
|
|
2095
|
+
if (berror) {
|
|
2096
|
+
done(null, berror);
|
|
2097
|
+
}
|
|
2098
|
+
|
|
2099
|
+
let timeout = tokenTimeout;
|
|
2100
|
+
|
|
2101
|
+
// if we should use the timeout from the token request
|
|
2102
|
+
if (timeout === 0) {
|
|
2103
|
+
timeout = findExpireInResult(dyntoken);
|
|
2104
|
+
} else {
|
|
2105
|
+
// otherwise add the timeout to the current time
|
|
2106
|
+
timeout += new Date().getTime();
|
|
2107
|
+
}
|
|
2108
|
+
|
|
2109
|
+
const tokenObj = {
|
|
2110
|
+
token: findPrimaryTokenInResult(dyntoken, dyntoken.front, dyntoken.end),
|
|
2111
|
+
tokenp2: findSecondaryTokenInResult(dyntoken, dyntoken.front, dyntoken.end)
|
|
2112
|
+
};
|
|
2113
|
+
|
|
2114
|
+
// if this is worth caching
|
|
2115
|
+
if (timeout && timeout > 0) {
|
|
2116
|
+
// since this is adding the token for future use, do not care when it comes back
|
|
2117
|
+
addTokenItem(user, reqBody, tokenObj, timeout, (addedtoken, aerror) => {
|
|
2118
|
+
if (aerror) {
|
|
2119
|
+
done(null, aerror);
|
|
2120
|
+
}
|
|
2121
|
+
done(tokenObj, null);
|
|
2122
|
+
});
|
|
2123
|
+
} else {
|
|
2124
|
+
done(tokenObj, null);
|
|
2125
|
+
}
|
|
2126
|
+
});
|
|
2127
|
+
}
|
|
2128
|
+
});
|
|
2129
|
+
}, (ret, error) => {
|
|
2130
|
+
if (error) {
|
|
2131
|
+
return callback(null, error);
|
|
2132
|
+
}
|
|
2133
|
+
|
|
2134
|
+
return callback(ret);
|
|
2135
|
+
});
|
|
2136
|
+
} catch (e) {
|
|
2137
|
+
// handle any exception
|
|
2138
|
+
const errorObj = transUtilInst.checkAndReturn(e, origin, 'Issue getting token item');
|
|
2139
|
+
return callback(null, errorObj);
|
|
2140
|
+
}
|
|
2141
|
+
}
|
|
2142
|
+
|
|
2143
|
+
/*
|
|
2144
|
+
* INTERNAL FUNCTION: addAuthToRequest determines the place to add the authentication
|
|
2145
|
+
* and adds the authentication string
|
|
2146
|
+
*/
|
|
2147
|
+
function addAuthToRequest(request, authStrs, callProperties, callback) {
|
|
2148
|
+
const origin = `${id}-connectorRest-addAuthToRequest`;
|
|
2149
|
+
log.trace(origin);
|
|
2150
|
+
|
|
2151
|
+
try {
|
|
2152
|
+
const newAuthStrs = [];
|
|
2153
|
+
for (let a = 0; a < authStrs.length; a += 1) {
|
|
2154
|
+
// handle any base64 encoding required on the authStrs
|
|
2155
|
+
if (authStrs[a].indexOf('{b64}') >= 0) {
|
|
2156
|
+
// get the range to be encoded
|
|
2157
|
+
const sIndex = authStrs[a].indexOf('{b64}');
|
|
2158
|
+
const eIndex = authStrs[a].indexOf('{/b64}');
|
|
2159
|
+
|
|
2160
|
+
// if start but no end - return an error
|
|
2161
|
+
if (sIndex >= 0 && eIndex < sIndex + 5) {
|
|
2162
|
+
const errorObj = transUtilInst.formatErrorObject(origin, 'Unable To Encode', [authStrs[a]], null, null, null);
|
|
2163
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
2164
|
+
return callback(null, errorObj);
|
|
2165
|
+
}
|
|
2166
|
+
|
|
2167
|
+
// get the string to be encoded
|
|
2168
|
+
const bufString = authStrs[a].substring(sIndex + 5, eIndex);
|
|
2169
|
+
|
|
2170
|
+
// encode the string
|
|
2171
|
+
const encString = Buffer.from(bufString).toString('base64');
|
|
2172
|
+
let tempAuthStr = '';
|
|
2173
|
+
|
|
2174
|
+
// build the new auth field with the encoded string
|
|
2175
|
+
if (sIndex > 0) {
|
|
2176
|
+
// add the start of the string that did not need encoding
|
|
2177
|
+
tempAuthStr = authStrs[a].substring(0, sIndex);
|
|
2178
|
+
}
|
|
2179
|
+
// add the encoded string
|
|
2180
|
+
tempAuthStr += encString;
|
|
2181
|
+
if (eIndex + 5 < authStrs[a].length) {
|
|
2182
|
+
// add the end of the string that did not need encoding
|
|
2183
|
+
tempAuthStr += authStrs[a].substring(eIndex + 6);
|
|
2184
|
+
}
|
|
2185
|
+
|
|
2186
|
+
// put the temp string into the auth string we will add to the request
|
|
2187
|
+
newAuthStrs.push(tempAuthStr);
|
|
2188
|
+
} else {
|
|
2189
|
+
newAuthStrs.push(authStrs[a]);
|
|
2190
|
+
}
|
|
2191
|
+
}
|
|
2192
|
+
|
|
2193
|
+
// add the authentication field to the request
|
|
2194
|
+
let useAuthField = authField;
|
|
2195
|
+
if (callProperties && callProperties.authentication && callProperties.authentication.auth_field) {
|
|
2196
|
+
if (Array.isArray(callProperties.authentication.auth_field)) {
|
|
2197
|
+
useAuthField = callProperties.authentication.auth_field;
|
|
2198
|
+
} else {
|
|
2199
|
+
useAuthField = [callProperties.authentication.auth_field];
|
|
2200
|
+
}
|
|
2201
|
+
}
|
|
2202
|
+
|
|
2203
|
+
// Auth Strings need to be at least as long as auth fields so if less, buffer with the first auth string
|
|
2204
|
+
if (useAuthField.length > newAuthStrs.length) {
|
|
2205
|
+
for (let t = newAuthStrs.length; t < useAuthField.length; t += 1) {
|
|
2206
|
+
newAuthStrs.push(newAuthStrs[0]);
|
|
2207
|
+
}
|
|
2208
|
+
}
|
|
2209
|
+
|
|
2210
|
+
// need to go through and put in place all of the auth strings.
|
|
2211
|
+
for (let af = 0; af < useAuthField.length; af += 1) {
|
|
2212
|
+
const authPath = useAuthField[af].split('.');
|
|
2213
|
+
|
|
2214
|
+
// if the token is going to be put in the url (url.xxxx)
|
|
2215
|
+
if (authPath[0] === 'urlpath') {
|
|
2216
|
+
// if there is already query info, insert the token
|
|
2217
|
+
if (request.header.path.indexOf('?') >= 0) {
|
|
2218
|
+
let temp = request.header.path.substring(0, request.header.path.indexOf('?'));
|
|
2219
|
+
temp += `/${newAuthStrs[af]}?`;
|
|
2220
|
+
temp += request.header.path.substring(request.header.path.indexOf('?') + 1);
|
|
2221
|
+
request.header.path = temp;
|
|
2222
|
+
} else {
|
|
2223
|
+
// append the token
|
|
2224
|
+
request.header.path += `/${newAuthStrs[af]}`;
|
|
2225
|
+
}
|
|
2226
|
+
} else if (authPath[0] === 'url') {
|
|
2227
|
+
// if there is already query info, insert the token
|
|
2228
|
+
if (request.header.path.indexOf('?') >= 0) {
|
|
2229
|
+
let temp = request.header.path.substring(0, request.header.path.indexOf('?'));
|
|
2230
|
+
temp += `?${newAuthStrs[af]}&`;
|
|
2231
|
+
temp += request.header.path.substring(request.header.path.indexOf('?') + 1);
|
|
2232
|
+
request.header.path = temp;
|
|
2233
|
+
} else {
|
|
2234
|
+
// append the token
|
|
2235
|
+
request.header.path += `?${newAuthStrs[af]}`;
|
|
2236
|
+
}
|
|
2237
|
+
} else if (authPath[0] === 'body' && typeof request.body === 'string') {
|
|
2238
|
+
// if adding to body and it is already stringified
|
|
2239
|
+
let jbody = JSON.parse(request.body);
|
|
2240
|
+
// get to the field in the object (header.xxxx or body.xxxxx)
|
|
2241
|
+
for (let a = 1; a < authPath.length; a += 1) {
|
|
2242
|
+
// if we are at the point to add the token
|
|
2243
|
+
if (a === authPath.length - 1) {
|
|
2244
|
+
jbody[authPath[a]] = newAuthStrs[af];
|
|
2245
|
+
} else {
|
|
2246
|
+
// if the field is not defined in the request, add it
|
|
2247
|
+
if (jbody[authPath[a]] === undefined) {
|
|
2248
|
+
jbody[authPath[a]] = {};
|
|
2249
|
+
}
|
|
2250
|
+
|
|
2251
|
+
// set the jbody to the next field in the path
|
|
2252
|
+
jbody = jbody[authPath[a]];
|
|
2253
|
+
}
|
|
2254
|
+
}
|
|
2255
|
+
request.body = JSON.stringify(jbody);
|
|
2256
|
+
request.header.headers['Content-length'] = Buffer.byteLength(request.body);
|
|
2257
|
+
} else {
|
|
2258
|
+
let tempField = request;
|
|
2259
|
+
|
|
2260
|
+
// get to the field in the object (header.xxxx or body.xxxxx)
|
|
2261
|
+
for (let a = 0; a < authPath.length; a += 1) {
|
|
2262
|
+
// if we are at the point to add the token
|
|
2263
|
+
if (a === authPath.length - 1) {
|
|
2264
|
+
tempField[authPath[a]] = newAuthStrs[af];
|
|
2265
|
+
} else {
|
|
2266
|
+
// if the field is not defined in the request, add it
|
|
2267
|
+
if (tempField[authPath[a]] === undefined) {
|
|
2268
|
+
tempField[authPath[a]] = {};
|
|
2269
|
+
}
|
|
2270
|
+
|
|
2271
|
+
// set the tempField to the next field in the path
|
|
2272
|
+
tempField = tempField[authPath[a]];
|
|
2273
|
+
}
|
|
2274
|
+
}
|
|
2275
|
+
}
|
|
2276
|
+
}
|
|
2277
|
+
|
|
2278
|
+
// auth string is already in the request object
|
|
2279
|
+
return callback(newAuthStrs);
|
|
2280
|
+
} catch (e) {
|
|
2281
|
+
// handle any exception
|
|
2282
|
+
const errorObj = transUtilInst.checkAndReturn(e, origin, 'Issue adding authentication');
|
|
2283
|
+
return callback(null, errorObj);
|
|
2284
|
+
}
|
|
2285
|
+
}
|
|
2286
|
+
|
|
2287
|
+
/*
|
|
2288
|
+
* INTERNAL FUNCTION: requestAuthenticate determines the authentication for System,
|
|
2289
|
+
* and takes appropriate action to authenticate and then makes the request
|
|
2290
|
+
*/
|
|
2291
|
+
function requestAuthenticate(request, entitySchema, invalidToken, callProperties, callback) {
|
|
2292
|
+
const origin = `${id}-connectorRest-requestAuthenticate`;
|
|
2293
|
+
log.trace(origin);
|
|
2294
|
+
|
|
2295
|
+
try {
|
|
2296
|
+
// set up the right credentials - passed in overrides default
|
|
2297
|
+
let useUser = username;
|
|
2298
|
+
let usePass = password;
|
|
2299
|
+
|
|
2300
|
+
if (callProperties && callProperties.authentication && callProperties.authentication.username) {
|
|
2301
|
+
useUser = callProperties.authentication.username;
|
|
2302
|
+
}
|
|
2303
|
+
if (callProperties && callProperties.authentication && callProperties.authentication.password) {
|
|
2304
|
+
usePass = callProperties.authentication.password;
|
|
2305
|
+
}
|
|
2306
|
+
|
|
2307
|
+
if (authMethod === 'request_token') {
|
|
2308
|
+
// are we working with reusing tokens until they expire?
|
|
2309
|
+
if (tokenTimeout >= 0) {
|
|
2310
|
+
// get the token from the token list
|
|
2311
|
+
return getTokenItem(request.header.path, useUser, request.authData, invalidToken, callProperties, (tres, terror) => {
|
|
2312
|
+
if (terror) {
|
|
2313
|
+
return callback(null, terror);
|
|
2314
|
+
}
|
|
2315
|
+
|
|
2316
|
+
// if we got a valid token, use it
|
|
2317
|
+
if (!tres || !tres.token) {
|
|
2318
|
+
const errorObj = transUtilInst.formatErrorObject(origin, 'Unable To Get Token', [useUser], null, null, null);
|
|
2319
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
2320
|
+
return callback(null, errorObj);
|
|
2321
|
+
}
|
|
2322
|
+
|
|
2323
|
+
// format the authentication string
|
|
2324
|
+
log.debug(`${origin}: ${JSON.stringify(tres)} being used for user: ${useUser}`);
|
|
2325
|
+
const authStrs = [];
|
|
2326
|
+
if (callProperties && callProperties.authentication && callProperties.authentication.auth_field_format) {
|
|
2327
|
+
if (Array.isArray(callProperties.authentication.auth_field_format)) {
|
|
2328
|
+
for (let a = 0; a < callProperties.authentication.auth_field_format.length; a += 1) {
|
|
2329
|
+
let authStr = callProperties.authentication.auth_field_format[a].replace('{token}', tres.token);
|
|
2330
|
+
authStr = authStr.replace('{tokenp2}', tres.tokenp2);
|
|
2331
|
+
authStr = authStr.replace('{username}', useUser);
|
|
2332
|
+
authStr = authStr.replace('{password}', usePass);
|
|
2333
|
+
authStrs.push(authStr);
|
|
2334
|
+
}
|
|
2335
|
+
} else {
|
|
2336
|
+
let authStr = callProperties.authentication.auth_field_format.replace('{token}', tres.token);
|
|
2337
|
+
authStr = authStr.replace('{tokenp2}', tres.tokenp2);
|
|
2338
|
+
authStr = authStr.replace('{username}', useUser);
|
|
2339
|
+
authStr = authStr.replace('{password}', usePass);
|
|
2340
|
+
authStrs.push(authStr);
|
|
2341
|
+
}
|
|
2342
|
+
} else {
|
|
2343
|
+
for (let a = 0; a < authFormat.length; a += 1) {
|
|
2344
|
+
let authStr = authFormat[a].replace('{token}', tres.token);
|
|
2345
|
+
authStr = authStr.replace('{tokenp2}', tres.tokenp2);
|
|
2346
|
+
authStr = authStr.replace('{username}', useUser);
|
|
2347
|
+
authStr = authStr.replace('{password}', usePass);
|
|
2348
|
+
authStrs.push(authStr);
|
|
2349
|
+
}
|
|
2350
|
+
}
|
|
2351
|
+
|
|
2352
|
+
return addAuthToRequest(request, authStrs, callProperties, (authReq, aerror) => {
|
|
2353
|
+
if (aerror) {
|
|
2354
|
+
return callback(aerror);
|
|
2355
|
+
}
|
|
2356
|
+
|
|
2357
|
+
request.tokenUsed = tres.token;
|
|
2358
|
+
|
|
2359
|
+
// actually make the request now that the authentication has been added
|
|
2360
|
+
return makeRequest(request, entitySchema, callProperties, null, 0, callback);
|
|
2361
|
+
});
|
|
2362
|
+
});
|
|
2363
|
+
}
|
|
2364
|
+
|
|
2365
|
+
// if no token timeout then no token list - just get the token and continue
|
|
2366
|
+
return buildTokenRequest(request.header.path, request.authData, callProperties, (dyntoken, berror) => {
|
|
2367
|
+
if (berror) {
|
|
2368
|
+
return callback(null, berror);
|
|
2369
|
+
}
|
|
2370
|
+
|
|
2371
|
+
// format the authentication string
|
|
2372
|
+
const tokenObj = {
|
|
2373
|
+
token: findPrimaryTokenInResult(dyntoken, dyntoken.front, dyntoken.end),
|
|
2374
|
+
tokenp2: findSecondaryTokenInResult(dyntoken, dyntoken.front, dyntoken.end)
|
|
2375
|
+
};
|
|
2376
|
+
|
|
2377
|
+
if (!tokenObj || !tokenObj.token) {
|
|
2378
|
+
const errorObj = transUtilInst.formatErrorObject(origin, 'Unable To Get Token', [useUser], null, null, null);
|
|
2379
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
2380
|
+
return callback(null, errorObj);
|
|
2381
|
+
}
|
|
2382
|
+
|
|
2383
|
+
log.debug(`${origin}: ${JSON.stringify(tokenObj)} being used for user: ${useUser}`);
|
|
2384
|
+
const authStrs = [];
|
|
2385
|
+
if (callProperties && callProperties.authentication && callProperties.authentication.auth_field_format) {
|
|
2386
|
+
if (Array.isArray(callProperties.authentication.auth_field_format)) {
|
|
2387
|
+
for (let a = 0; a < callProperties.authentication.auth_field_format.length; a += 1) {
|
|
2388
|
+
let authStr = callProperties.authentication.auth_field_format[a].replace('{token}', tokenObj.token);
|
|
2389
|
+
authStr = authStr.replace('{tokenp2}', tokenObj.tokenp2);
|
|
2390
|
+
authStr = authStr.replace('{username}', useUser);
|
|
2391
|
+
authStr = authStr.replace('{password}', usePass);
|
|
2392
|
+
authStrs.push(authStr);
|
|
2393
|
+
}
|
|
2394
|
+
} else {
|
|
2395
|
+
let authStr = callProperties.authentication.auth_field_format.replace('{token}', tokenObj.token);
|
|
2396
|
+
authStr = authStr.replace('{tokenp2}', tokenObj.tokenp2);
|
|
2397
|
+
authStr = authStr.replace('{username}', useUser);
|
|
2398
|
+
authStr = authStr.replace('{password}', usePass);
|
|
2399
|
+
authStrs.push(authStr);
|
|
2400
|
+
}
|
|
2401
|
+
} else {
|
|
2402
|
+
for (let a = 0; a < authFormat.length; a += 1) {
|
|
2403
|
+
let authStr = authFormat[a].replace('{token}', tokenObj.token);
|
|
2404
|
+
authStr = authStr.replace('{tokenp2}', tokenObj.tokenp2);
|
|
2405
|
+
authStr = authStr.replace('{username}', useUser);
|
|
2406
|
+
authStr = authStr.replace('{password}', usePass);
|
|
2407
|
+
authStrs.push(authStr);
|
|
2408
|
+
}
|
|
2409
|
+
}
|
|
2410
|
+
|
|
2411
|
+
return addAuthToRequest(request, authStrs, callProperties, (authReq, aerror) => {
|
|
2412
|
+
if (aerror) {
|
|
2413
|
+
return callback(aerror);
|
|
2414
|
+
}
|
|
2415
|
+
|
|
2416
|
+
// actually make the request now that the authentication has been added
|
|
2417
|
+
return makeRequest(request, entitySchema, callProperties, null, 0, callback);
|
|
2418
|
+
});
|
|
2419
|
+
});
|
|
2420
|
+
}
|
|
2421
|
+
|
|
2422
|
+
if (authMethod === 'jwt_token') {
|
|
2423
|
+
// format the authentication string
|
|
2424
|
+
let useToken = staticToken;
|
|
2425
|
+
|
|
2426
|
+
// create payload and secret (from prepoerties)
|
|
2427
|
+
const secret = usePass;
|
|
2428
|
+
const payload = {
|
|
2429
|
+
iss: useUser,
|
|
2430
|
+
exp: new Date().getTime() + (tokenTimeout / 1000) + 30
|
|
2431
|
+
};
|
|
2432
|
+
|
|
2433
|
+
// sign token with API Secret
|
|
2434
|
+
useToken = jwt.sign(payload, secret);
|
|
2435
|
+
|
|
2436
|
+
const authStrs = [];
|
|
2437
|
+
if (callProperties && callProperties.authentication && callProperties.authentication.auth_field_format) {
|
|
2438
|
+
if (Array.isArray(callProperties.authentication.auth_field_format)) {
|
|
2439
|
+
for (let a = 0; a < callProperties.authentication.auth_field_format.length; a += 1) {
|
|
2440
|
+
let authStr = callProperties.authentication.auth_field_format[a].replace('{token}', useToken);
|
|
2441
|
+
authStr = authStr.replace('{username}', useUser);
|
|
2442
|
+
authStr = authStr.replace('{password}', usePass);
|
|
2443
|
+
authStrs.push(authStr);
|
|
2444
|
+
}
|
|
2445
|
+
} else {
|
|
2446
|
+
let authStr = callProperties.authentication.auth_field_format.replace('{token}', useToken);
|
|
2447
|
+
authStr = authStr.replace('{username}', useUser);
|
|
2448
|
+
authStr = authStr.replace('{password}', usePass);
|
|
2449
|
+
authStrs.push(authStr);
|
|
2450
|
+
}
|
|
2451
|
+
} else {
|
|
2452
|
+
for (let a = 0; a < authFormat.length; a += 1) {
|
|
2453
|
+
let authStr = authFormat[a].replace('{token}', useToken);
|
|
2454
|
+
authStr = authStr.replace('{username}', useUser);
|
|
2455
|
+
authStr = authStr.replace('{password}', usePass);
|
|
2456
|
+
authStrs.push(authStr);
|
|
2457
|
+
}
|
|
2458
|
+
}
|
|
2459
|
+
|
|
2460
|
+
return addAuthToRequest(request, authStrs, callProperties, (authReq, aerror) => {
|
|
2461
|
+
if (aerror) {
|
|
2462
|
+
return callback(aerror);
|
|
2463
|
+
}
|
|
2464
|
+
|
|
2465
|
+
// actually make the request now that the authentication has been added
|
|
2466
|
+
return makeRequest(request, entitySchema, callProperties, null, 0, callback);
|
|
2467
|
+
});
|
|
2468
|
+
}
|
|
2469
|
+
|
|
2470
|
+
if (authMethod === 'static_token') {
|
|
2471
|
+
// format the authentication string
|
|
2472
|
+
let useToken = staticToken;
|
|
2473
|
+
if (callProperties && callProperties.authentication && callProperties.authentication.token) {
|
|
2474
|
+
useToken = callProperties.authentication.token;
|
|
2475
|
+
}
|
|
2476
|
+
|
|
2477
|
+
const authStrs = [];
|
|
2478
|
+
if (callProperties && callProperties.authentication && callProperties.authentication.auth_field_format) {
|
|
2479
|
+
if (Array.isArray(callProperties.authentication.auth_field_format)) {
|
|
2480
|
+
for (let a = 0; a < callProperties.authentication.auth_field_format.length; a += 1) {
|
|
2481
|
+
let authStr = callProperties.authentication.auth_field_format[a].replace('{token}', useToken);
|
|
2482
|
+
authStr = authStr.replace('{username}', useUser);
|
|
2483
|
+
authStr = authStr.replace('{password}', usePass);
|
|
2484
|
+
authStrs.push(authStr);
|
|
2485
|
+
}
|
|
2486
|
+
} else {
|
|
2487
|
+
let authStr = callProperties.authentication.auth_field_format.replace('{token}', useToken);
|
|
2488
|
+
authStr = authStr.replace('{username}', useUser);
|
|
2489
|
+
authStr = authStr.replace('{password}', usePass);
|
|
2490
|
+
authStrs.push(authStr);
|
|
2491
|
+
}
|
|
2492
|
+
} else {
|
|
2493
|
+
for (let a = 0; a < authFormat.length; a += 1) {
|
|
2494
|
+
let authStr = authFormat[a].replace('{token}', useToken);
|
|
2495
|
+
authStr = authStr.replace('{username}', useUser);
|
|
2496
|
+
authStr = authStr.replace('{password}', usePass);
|
|
2497
|
+
authStrs.push(authStr);
|
|
2498
|
+
}
|
|
2499
|
+
}
|
|
2500
|
+
|
|
2501
|
+
return addAuthToRequest(request, authStrs, callProperties, (authReq, aerror) => {
|
|
2502
|
+
if (aerror) {
|
|
2503
|
+
return callback(aerror);
|
|
2504
|
+
}
|
|
2505
|
+
|
|
2506
|
+
// actually make the request now that the authentication has been added
|
|
2507
|
+
return makeRequest(request, entitySchema, callProperties, null, 0, callback);
|
|
2508
|
+
});
|
|
2509
|
+
}
|
|
2510
|
+
|
|
2511
|
+
if (authMethod === 'basic user_password') {
|
|
2512
|
+
// format the authentication string
|
|
2513
|
+
let useToken = staticToken;
|
|
2514
|
+
if (callProperties && callProperties.authentication && callProperties.authentication.token) {
|
|
2515
|
+
useToken = callProperties.authentication.token;
|
|
2516
|
+
}
|
|
2517
|
+
|
|
2518
|
+
const authStrs = [];
|
|
2519
|
+
if (callProperties && callProperties.authentication && callProperties.authentication.auth_field_format) {
|
|
2520
|
+
if (Array.isArray(callProperties.authentication.auth_field_format)) {
|
|
2521
|
+
for (let a = 0; a < callProperties.authentication.auth_field_format.length; a += 1) {
|
|
2522
|
+
let authStr = callProperties.authentication.auth_field_format[a].replace('{token}', useToken);
|
|
2523
|
+
authStr = authStr.replace('{username}', useUser);
|
|
2524
|
+
authStr = authStr.replace('{password}', usePass);
|
|
2525
|
+
authStrs.push(authStr);
|
|
2526
|
+
}
|
|
2527
|
+
} else {
|
|
2528
|
+
let authStr = callProperties.authentication.auth_field_format.replace('{token}', useToken);
|
|
2529
|
+
authStr = authStr.replace('{username}', useUser);
|
|
2530
|
+
authStr = authStr.replace('{password}', usePass);
|
|
2531
|
+
authStrs.push(authStr);
|
|
2532
|
+
}
|
|
2533
|
+
} else {
|
|
2534
|
+
for (let a = 0; a < authFormat.length; a += 1) {
|
|
2535
|
+
let authStr = authFormat[a].replace('{token}', useToken);
|
|
2536
|
+
authStr = authStr.replace('{username}', useUser);
|
|
2537
|
+
authStr = authStr.replace('{password}', usePass);
|
|
2538
|
+
authStrs.push(authStr);
|
|
2539
|
+
}
|
|
2540
|
+
}
|
|
2541
|
+
|
|
2542
|
+
return addAuthToRequest(request, authStrs, callProperties, (authReq, aerror) => {
|
|
2543
|
+
if (aerror) {
|
|
2544
|
+
return callback(aerror);
|
|
2545
|
+
}
|
|
2546
|
+
|
|
2547
|
+
// actually make the request now that the authentication has been added
|
|
2548
|
+
return makeRequest(request, entitySchema, callProperties, null, 0, callback);
|
|
2549
|
+
});
|
|
2550
|
+
}
|
|
2551
|
+
// if no_authentication, there is no change to the request!
|
|
2552
|
+
|
|
2553
|
+
// actual call to make the request
|
|
2554
|
+
return makeRequest(request, entitySchema, callProperties, null, 0, callback);
|
|
2555
|
+
} catch (e) {
|
|
2556
|
+
// handle any exception
|
|
2557
|
+
const errorObj = transUtilInst.checkAndReturn(e, origin, 'Issue authentication');
|
|
2558
|
+
return callback(null, errorObj);
|
|
2559
|
+
}
|
|
2560
|
+
}
|
|
2561
|
+
|
|
2562
|
+
/*
|
|
2563
|
+
* INTERNAL FUNCTION: archiveResult is used to archive the results of the request along with
|
|
2564
|
+
* the times that were taken at various steps in the process (wait, request and overall)
|
|
2565
|
+
*/
|
|
2566
|
+
function archiveResult(reqResult, callback) {
|
|
2567
|
+
const origin = `${id}-connectorRest-archiveResult`;
|
|
2568
|
+
log.trace(origin);
|
|
2569
|
+
|
|
2570
|
+
try {
|
|
2571
|
+
if (!archiving) {
|
|
2572
|
+
return 'skipped';
|
|
2573
|
+
}
|
|
2574
|
+
|
|
2575
|
+
// Add an Id to the result object that we are going to store in the database
|
|
2576
|
+
const data = reqResult;
|
|
2577
|
+
data._id = uuid.v4();
|
|
2578
|
+
|
|
2579
|
+
dbUtilInst.findAndModify(archiveColl, { _id: data._id }, null, data, true, null, true, (error, result) => {
|
|
2580
|
+
if (error) {
|
|
2581
|
+
const errorObj = transUtilInst.formatErrorObject(origin, 'Unable To Save To Database', [error], null, null, null);
|
|
2582
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
2583
|
+
return callback(null, errorObj);
|
|
2584
|
+
}
|
|
2585
|
+
|
|
2586
|
+
return callback(result);
|
|
2587
|
+
});
|
|
2588
|
+
|
|
2589
|
+
// save the result object to the database
|
|
2590
|
+
// return brokers.persistence.save(archiveColl, data._id, data, (result, error) => {
|
|
2591
|
+
// if (error) {
|
|
2592
|
+
// const errorObj = transUtilInst.formatErrorObject(origin, 'Unable To Save To Database', [error], null, null, null);
|
|
2593
|
+
// log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
2594
|
+
// return callback(null, errorObj);
|
|
2595
|
+
// }
|
|
2596
|
+
|
|
2597
|
+
// return callback(result);
|
|
2598
|
+
// });
|
|
2599
|
+
} catch (e) {
|
|
2600
|
+
// handle any exception
|
|
2601
|
+
const errorObj = transUtilInst.checkAndReturn(e, origin, 'Issue archiving');
|
|
2602
|
+
return callback(null, errorObj);
|
|
2603
|
+
}
|
|
2604
|
+
}
|
|
2605
|
+
|
|
2606
|
+
/*
|
|
2607
|
+
* INTERNAL FUNCTION: handleEndResponse prepares the response to send back
|
|
2608
|
+
*/
|
|
2609
|
+
function handleEndResponse(request, makeResp, overallTime, callback) {
|
|
2610
|
+
const origin = `${id}-connectorRest-handleEndResponse`;
|
|
2611
|
+
log.trace(origin);
|
|
2612
|
+
|
|
2613
|
+
try {
|
|
2614
|
+
log.info(`${origin}: Request call to ${request.header.method} ${request.header.path}: Call took: ${makeResp.tripTime || 0}`);
|
|
2615
|
+
|
|
2616
|
+
const reqResult = {
|
|
2617
|
+
phInstance,
|
|
2618
|
+
request,
|
|
2619
|
+
overallEnd: makeResp.tripTime || 0,
|
|
2620
|
+
response: makeResp
|
|
2621
|
+
};
|
|
2622
|
+
|
|
2623
|
+
// archive the results
|
|
2624
|
+
try {
|
|
2625
|
+
archiveResult(reqResult, (archived, aerror) => {
|
|
2626
|
+
if (aerror) {
|
|
2627
|
+
log.spam(`${origin}: Error Archived: ${JSON.stringify(aerror)}`);
|
|
2628
|
+
} else {
|
|
2629
|
+
log.spam(`${origin}: Result Archived: ${JSON.stringify(archived)}`);
|
|
2630
|
+
}
|
|
2631
|
+
});
|
|
2632
|
+
} catch (ex) {
|
|
2633
|
+
log.spam(`${origin}: Swallowing the archive error`);
|
|
2634
|
+
}
|
|
2635
|
+
|
|
2636
|
+
// If the request was successful
|
|
2637
|
+
if (makeResp && makeResp.code && Number(makeResp.code) >= 200 && Number(makeResp.code) <= 299) {
|
|
2638
|
+
const newResp = {
|
|
2639
|
+
icode: `AD.${makeResp.code}`,
|
|
2640
|
+
response: makeResp.response,
|
|
2641
|
+
headers: makeResp.headers,
|
|
2642
|
+
requestCookie: makeResp.requestCookie,
|
|
2643
|
+
reqHdr: makeResp.reqHdr,
|
|
2644
|
+
metrics: {
|
|
2645
|
+
code: makeResp.code,
|
|
2646
|
+
timeouts: makeResp.timeouts || 0,
|
|
2647
|
+
redirects: makeResp.redirects || 0,
|
|
2648
|
+
retries: makeResp.retries || 0,
|
|
2649
|
+
tripTime: parseFloat(makeResp.tripTime) || 0,
|
|
2650
|
+
isThrottling: false
|
|
2651
|
+
}
|
|
2652
|
+
};
|
|
2653
|
+
if (returnRequest) {
|
|
2654
|
+
newResp.request = makeResp.request;
|
|
2655
|
+
}
|
|
2656
|
+
|
|
2657
|
+
// return the response object
|
|
2658
|
+
return callback(newResp);
|
|
2659
|
+
}
|
|
2660
|
+
|
|
2661
|
+
// if not successful - check to see if response or error
|
|
2662
|
+
let errorObj = {};
|
|
2663
|
+
if (makeResp) {
|
|
2664
|
+
if (!returnRequest && makeResp.request) {
|
|
2665
|
+
delete makeResp.request;
|
|
2666
|
+
}
|
|
2667
|
+
|
|
2668
|
+
if (makeResp.code === -2) {
|
|
2669
|
+
errorObj = transUtilInst.formatErrorObject(origin, 'Request Timeout', [makeResp.code], makeResp.code, makeResp, null);
|
|
2670
|
+
} else {
|
|
2671
|
+
errorObj = transUtilInst.formatErrorObject(origin, 'Error On Request', [makeResp.code], makeResp.code, makeResp, null);
|
|
2672
|
+
}
|
|
2673
|
+
|
|
2674
|
+
errorObj.metrics = {
|
|
2675
|
+
code: makeResp.code,
|
|
2676
|
+
timeouts: makeResp.timeouts || 0,
|
|
2677
|
+
redirects: makeResp.redirects || 0,
|
|
2678
|
+
retries: makeResp.retries || 0,
|
|
2679
|
+
tripTime: parseFloat(makeResp.tripTime) || 0,
|
|
2680
|
+
isThrottling: false
|
|
2681
|
+
};
|
|
2682
|
+
} else {
|
|
2683
|
+
errorObj = transUtilInst.formatErrorObject(origin, 'Error On Request', ['unknown'], null, null, null);
|
|
2684
|
+
errorObj.metrics = {
|
|
2685
|
+
code: 'unknown',
|
|
2686
|
+
timeouts: 0,
|
|
2687
|
+
redirects: 0,
|
|
2688
|
+
retries: 0,
|
|
2689
|
+
tripTime: 0,
|
|
2690
|
+
isThrottling: false
|
|
2691
|
+
};
|
|
2692
|
+
}
|
|
2693
|
+
errorObj.response = makeResp.response;
|
|
2694
|
+
|
|
2695
|
+
if (makeResp && makeResp.code === -1) {
|
|
2696
|
+
if (typeof errorObj.IAPerror.raw_response === 'object') {
|
|
2697
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}: ${JSON.stringify(errorObj.IAPerror.raw_response)}`);
|
|
2698
|
+
} else {
|
|
2699
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}: ${errorObj.IAPerror.raw_response}`);
|
|
2700
|
+
}
|
|
2701
|
+
} else {
|
|
2702
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
2703
|
+
}
|
|
2704
|
+
|
|
2705
|
+
return callback(null, errorObj);
|
|
2706
|
+
} catch (e) {
|
|
2707
|
+
// handle any exception
|
|
2708
|
+
const errorObj = transUtilInst.checkAndReturn(e, origin, 'Issue handling response');
|
|
2709
|
+
errorObj.metrics = {};
|
|
2710
|
+
return callback(null, errorObj);
|
|
2711
|
+
}
|
|
2712
|
+
}
|
|
2713
|
+
|
|
2714
|
+
/*
|
|
2715
|
+
* INTERNAL FUNCTION: handleEndThrottleResponse prepares the response to send back
|
|
2716
|
+
* specifically if throttling is turned on - to clean up the request in the queue
|
|
2717
|
+
*/
|
|
2718
|
+
function handleEndThrottleResponse(request, myTransTime, claimedLic, makeResp, reqTime, overallTime, waitEnd, callback) {
|
|
2719
|
+
const origin = `${id}-connectorRest-handleEndThrottleResponse`;
|
|
2720
|
+
log.trace(origin);
|
|
2721
|
+
|
|
2722
|
+
const reqDiff = process.hrtime(reqTime);
|
|
2723
|
+
const reqEnd = (reqDiff[0] * NS_PER_SEC) + reqDiff[1];
|
|
2724
|
+
const overallDiff = process.hrtime(overallTime);
|
|
2725
|
+
const overallEnd = `${Math.round(((overallDiff[0] * NS_PER_SEC) + overallDiff[1]) / 1000000)}ms`;
|
|
2726
|
+
|
|
2727
|
+
try {
|
|
2728
|
+
log.info(`${origin}: Request ${claimedLic.request_id} Transaction ${myTransTime} call to ${request.header.method} ${request.header.path}: Call took ${reqEnd} - Overall Time: ${overallEnd}`);
|
|
2729
|
+
|
|
2730
|
+
// finish the turn so the space can be freed
|
|
2731
|
+
return throttleEng.finishTurn(claimedLic, reqEnd, (fres, ferror) => {
|
|
2732
|
+
const reqResult = {
|
|
2733
|
+
phInstance,
|
|
2734
|
+
requestId: claimedLic.request_id,
|
|
2735
|
+
transNum: claimedLic.transNum,
|
|
2736
|
+
request,
|
|
2737
|
+
start: claimedLic.start,
|
|
2738
|
+
end: new Date().getTime(),
|
|
2739
|
+
waitEnd,
|
|
2740
|
+
reqEnd,
|
|
2741
|
+
overallEnd,
|
|
2742
|
+
response: makeResp
|
|
2743
|
+
};
|
|
2744
|
+
|
|
2745
|
+
if (!ferror) {
|
|
2746
|
+
reqResult.end = fres.end;
|
|
2747
|
+
}
|
|
2748
|
+
|
|
2749
|
+
// archive the results
|
|
2750
|
+
try {
|
|
2751
|
+
archiveResult(reqResult, (archived, aerror) => {
|
|
2752
|
+
if (aerror) {
|
|
2753
|
+
log.spam(`${origin}: Error Archived: ${JSON.stringify(aerror)}`);
|
|
2754
|
+
} else {
|
|
2755
|
+
log.spam(`${origin}: Result Archived: ${JSON.stringify(archived)}`);
|
|
2756
|
+
}
|
|
2757
|
+
});
|
|
2758
|
+
} catch (ex) {
|
|
2759
|
+
log.spam(`${origin}: Swallowing the archive error`);
|
|
2760
|
+
}
|
|
2761
|
+
|
|
2762
|
+
// If the request was successful
|
|
2763
|
+
if (makeResp !== null && makeResp.code !== undefined
|
|
2764
|
+
&& Number(makeResp.code) >= 200 && Number(makeResp.code) <= 299) {
|
|
2765
|
+
const newResp = {
|
|
2766
|
+
icode: `AD.${makeResp.code}`,
|
|
2767
|
+
response: makeResp.response,
|
|
2768
|
+
headers: makeResp.headers,
|
|
2769
|
+
requestCookie: makeResp.requestCookie,
|
|
2770
|
+
reqHdr: makeResp.reqHdr,
|
|
2771
|
+
event: claimedLic.event,
|
|
2772
|
+
metrics: {
|
|
2773
|
+
code: makeResp.code,
|
|
2774
|
+
timeouts: makeResp.timeouts || 0,
|
|
2775
|
+
redirects: makeResp.redirects || 0,
|
|
2776
|
+
retries: makeResp.retries || 0,
|
|
2777
|
+
tripTime: parseFloat(makeResp.tripTime) || 0,
|
|
2778
|
+
queueTime: waitEnd,
|
|
2779
|
+
isThrottling: true
|
|
2780
|
+
}
|
|
2781
|
+
};
|
|
2782
|
+
if (returnRequest) {
|
|
2783
|
+
newResp.request = makeResp.request;
|
|
2784
|
+
}
|
|
2785
|
+
|
|
2786
|
+
// return the response object
|
|
2787
|
+
return callback(newResp);
|
|
2788
|
+
}
|
|
2789
|
+
|
|
2790
|
+
// if not successful - check to see if response or error
|
|
2791
|
+
let errorObj = {};
|
|
2792
|
+
if (makeResp) {
|
|
2793
|
+
if (!returnRequest && makeResp.request) {
|
|
2794
|
+
delete makeResp.request;
|
|
2795
|
+
}
|
|
2796
|
+
|
|
2797
|
+
if (makeResp.code === -2) {
|
|
2798
|
+
errorObj = transUtilInst.formatErrorObject(origin, 'Request Timeout', [makeResp.code], makeResp.code, makeResp, null);
|
|
2799
|
+
} else {
|
|
2800
|
+
errorObj = transUtilInst.formatErrorObject(origin, 'Error On Request', [makeResp.code], makeResp.code, makeResp, null);
|
|
2801
|
+
}
|
|
2802
|
+
|
|
2803
|
+
errorObj.metrics = {
|
|
2804
|
+
code: makeResp.code,
|
|
2805
|
+
timeouts: makeResp.timeouts || 0,
|
|
2806
|
+
redirects: makeResp.redirects || 0,
|
|
2807
|
+
retries: makeResp.retries || 0,
|
|
2808
|
+
tripTime: parseFloat(makeResp.tripTime) || 0,
|
|
2809
|
+
queueTime: waitEnd,
|
|
2810
|
+
isThrottling: true
|
|
2811
|
+
};
|
|
2812
|
+
} else {
|
|
2813
|
+
errorObj = transUtilInst.formatErrorObject(origin, 'Error On Request', ['unknown'], null, null, null);
|
|
2814
|
+
errorObj.metrics = {
|
|
2815
|
+
code: 'unknown',
|
|
2816
|
+
timeouts: 0,
|
|
2817
|
+
redirects: 0,
|
|
2818
|
+
retries: 0,
|
|
2819
|
+
tripTime: 0,
|
|
2820
|
+
queueTime: waitEnd,
|
|
2821
|
+
isThrottling: true
|
|
2822
|
+
};
|
|
2823
|
+
}
|
|
2824
|
+
errorObj.response = makeResp.response;
|
|
2825
|
+
|
|
2826
|
+
if (makeResp && makeResp.code === -1) {
|
|
2827
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}: ${errorObj.IAPerror.raw_response}`);
|
|
2828
|
+
} else {
|
|
2829
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
2830
|
+
}
|
|
2831
|
+
|
|
2832
|
+
errorObj.event = claimedLic.event;
|
|
2833
|
+
return callback(null, errorObj);
|
|
2834
|
+
});
|
|
2835
|
+
} catch (e) {
|
|
2836
|
+
// handle any exception
|
|
2837
|
+
const errorObj = transUtilInst.checkAndReturn(e, origin, 'Issue handling response');
|
|
2838
|
+
errorObj.metrics = {};
|
|
2839
|
+
errorObj.event = claimedLic.event;
|
|
2840
|
+
return callback(null, errorObj);
|
|
2841
|
+
}
|
|
2842
|
+
}
|
|
2843
|
+
|
|
2844
|
+
/*
|
|
2845
|
+
* INTERNAL FUNCTION: re-attempt after an invalid response and figure out
|
|
2846
|
+
* how to handle the new response.
|
|
2847
|
+
*/
|
|
2848
|
+
function retryInvalidResponse(request, callProperties, myTransTime, claimedLic, makeResp, reqTime, overallTime, waitEnd, numTries, entitySchema, callback) {
|
|
2849
|
+
const origin = `${id}-connectorRest-retryInvalidResponse`;
|
|
2850
|
+
log.trace(origin);
|
|
2851
|
+
|
|
2852
|
+
try {
|
|
2853
|
+
// Need to get a new token and retry the request
|
|
2854
|
+
return requestAuthenticate(request, entitySchema, request.tokenUsed, callProperties, (mres, merror) => {
|
|
2855
|
+
if (merror) {
|
|
2856
|
+
return callback(null, merror);
|
|
2857
|
+
}
|
|
2858
|
+
|
|
2859
|
+
let retryError = limitRetryError;
|
|
2860
|
+
let retries = numRetries;
|
|
2861
|
+
if (callProperties && callProperties.request && callProperties.request.limit_retry_error) {
|
|
2862
|
+
if (typeof callProperties.request.limit_retry_error === 'number'
|
|
2863
|
+
|| typeof callProperties.request.limit_retry_error === 'string') {
|
|
2864
|
+
retryError = [Number(callProperties.request.limit_retry_error)];
|
|
2865
|
+
} else if (Array.isArray(callProperties.request.limit_retry_error)) {
|
|
2866
|
+
retryError = [];
|
|
2867
|
+
for (let l = 0; l < callProperties.request.limit_retry_error.length; l += 1) {
|
|
2868
|
+
if (typeof callProperties.request.limit_retry_error[l] === 'number') {
|
|
2869
|
+
retryError.push(Number(callProperties.request.limit_retry_error[l]));
|
|
2870
|
+
} else if (typeof callProperties.request.limit_retry_error[l] === 'string'
|
|
2871
|
+
&& callProperties.request.limit_retry_error[l].indexOf('-') < 0) {
|
|
2872
|
+
retryError.push(Number(callProperties.request.limit_retry_error[l]));
|
|
2873
|
+
} else if (typeof callProperties.request.limit_retry_error[l] === 'string'
|
|
2874
|
+
&& callProperties.request.limit_retry_error[l].indexOf('-') >= 0) {
|
|
2875
|
+
const srange = Number(callProperties.request.limit_retry_error[l].split('-')[0]);
|
|
2876
|
+
const erange = Number(callProperties.request.limit_retry_error[l].split('-')[1]);
|
|
2877
|
+
for (let r = srange; r <= erange; r += 1) {
|
|
2878
|
+
retryError.push(r);
|
|
2879
|
+
}
|
|
2880
|
+
}
|
|
2881
|
+
}
|
|
2882
|
+
}
|
|
2883
|
+
}
|
|
2884
|
+
if (callProperties && callProperties.request && callProperties.request.number_retries) {
|
|
2885
|
+
retries = callProperties.request.number_retries;
|
|
2886
|
+
}
|
|
2887
|
+
|
|
2888
|
+
// if the response is valid - good data or legitimate data error
|
|
2889
|
+
// stop trying if we have tried enough
|
|
2890
|
+
if (numTries > retries || (mres !== null && mres.code !== undefined && !retryError.includes(Number(mres.code))
|
|
2891
|
+
&& (authMethod !== 'request_token' || Number(mres.code) !== Number(tokenError)))) {
|
|
2892
|
+
const newresp = mres;
|
|
2893
|
+
newresp.retries = numTries;
|
|
2894
|
+
|
|
2895
|
+
if (throttleEnabled && (!callProperties || !callProperties.host)) {
|
|
2896
|
+
return handleEndThrottleResponse(request, myTransTime, claimedLic, newresp, reqTime, overallTime, waitEnd, callback);
|
|
2897
|
+
}
|
|
2898
|
+
|
|
2899
|
+
return handleEndResponse(request, newresp, overallTime, callback);
|
|
2900
|
+
}
|
|
2901
|
+
if (mres !== null && mres.code !== undefined && Number(mres.code) === Number(tokenError) && authMethod === 'request_token') {
|
|
2902
|
+
// if we took a invalid token error - System out of licenses - try again
|
|
2903
|
+
return handleInvalidToken(request, callProperties, myTransTime, claimedLic, mres, reqTime, overallTime, waitEnd, numTries + 1, entitySchema, callback);
|
|
2904
|
+
}
|
|
2905
|
+
if (mres !== null && mres.code !== undefined && retryError.includes(Number(mres.code))) {
|
|
2906
|
+
// if we took a throughput limitation - System out of licenses - try again
|
|
2907
|
+
return handleLimitResponse(request, callProperties, myTransTime, claimedLic, mres, reqTime, overallTime, waitEnd, numTries + 1, entitySchema, callback);
|
|
2908
|
+
}
|
|
2909
|
+
|
|
2910
|
+
// if we took an http error - like aborting
|
|
2911
|
+
return handleAbortResponse(request, callProperties, myTransTime, claimedLic, mres, reqTime, overallTime, waitEnd, numTries + 1, entitySchema, callback);
|
|
2912
|
+
});
|
|
2913
|
+
} catch (e) {
|
|
2914
|
+
// handle any exception
|
|
2915
|
+
const errorObj = transUtilInst.checkAndReturn(e, origin, 'Issue retrying invalid');
|
|
2916
|
+
return callback(null, errorObj);
|
|
2917
|
+
}
|
|
2918
|
+
}
|
|
2919
|
+
|
|
2920
|
+
/*
|
|
2921
|
+
* INTERNAL FUNCTION: handleInvalidToken handles issues when a token has timed out
|
|
2922
|
+
*/
|
|
2923
|
+
function handleInvalidToken(request, callProperties, myTransTime, claimedLic, makeResp, reqTime, overallTime, waitEnd, numTries, entitySchema, callback) {
|
|
2924
|
+
const origin = `${id}-connectorRest-handleInvalidToken`;
|
|
2925
|
+
log.trace(origin);
|
|
2926
|
+
let reqDiff = process.hrtime(reqTime);
|
|
2927
|
+
let reqEnd = (reqDiff[0] * NS_PER_SEC) + reqDiff[1];
|
|
2928
|
+
log.warn(`${origin}: Request ${claimedLic.request_id} Transaction ${myTransTime} invalid token appear to be hit after ${reqEnd} nanoseconds - Entering handling invalid token`);
|
|
2929
|
+
reqDiff = undefined;
|
|
2930
|
+
reqEnd = undefined;
|
|
2931
|
+
|
|
2932
|
+
try {
|
|
2933
|
+
// Need to retry the request - will pull new token
|
|
2934
|
+
return retryInvalidResponse(request, callProperties, myTransTime, claimedLic, makeResp, reqTime, overallTime, waitEnd, numTries, entitySchema, callback);
|
|
2935
|
+
} catch (e) {
|
|
2936
|
+
// handle any exception
|
|
2937
|
+
const errorObj = transUtilInst.checkAndReturn(e, origin, 'Issue handling invalid token');
|
|
2938
|
+
return callback(null, errorObj);
|
|
2939
|
+
}
|
|
2940
|
+
}
|
|
2941
|
+
|
|
2942
|
+
/*
|
|
2943
|
+
* INTERNAL FUNCTION: handleLimitResponse handles throughput issues with System
|
|
2944
|
+
*/
|
|
2945
|
+
function handleLimitResponse(request, callProperties, myTransTime, claimedLic, makeResp, reqTime, overallTime, waitEnd, numTries, entitySchema, callback) {
|
|
2946
|
+
const origin = `${id}-connectorRest-handleLimitResponse`;
|
|
2947
|
+
log.trace(origin);
|
|
2948
|
+
let reqDiff = process.hrtime(reqTime);
|
|
2949
|
+
let reqEnd = (reqDiff[0] * NS_PER_SEC) + reqDiff[1];
|
|
2950
|
+
log.warn(`${origin}: Request ${claimedLic.request_id} Transaction ${myTransTime} limits appear to be hit after ${reqEnd} nanoseconds - Entering handling limiting requests`);
|
|
2951
|
+
reqDiff = undefined;
|
|
2952
|
+
reqEnd = undefined;
|
|
2953
|
+
let myTimeout = attemptTimeout;
|
|
2954
|
+
|
|
2955
|
+
// if there is a timeout from the action (schema) use it instead
|
|
2956
|
+
if (entitySchema && entitySchema.timeout && entitySchema.timeout > 0) {
|
|
2957
|
+
myTimeout = entitySchema.timeout;
|
|
2958
|
+
}
|
|
2959
|
+
if (callProperties && callProperties.request && callProperties.request.attempt_timeout) {
|
|
2960
|
+
myTimeout = callProperties.request.attempt_timeout;
|
|
2961
|
+
}
|
|
2962
|
+
|
|
2963
|
+
try {
|
|
2964
|
+
// Run this after an interval to see if throughput issues clear
|
|
2965
|
+
const timeoutObject = setTimeout(() => {
|
|
2966
|
+
log.debug(`${origin}: Processing Re-request`);
|
|
2967
|
+
|
|
2968
|
+
// nothing to do but retry
|
|
2969
|
+
return retryInvalidResponse(request, callProperties, myTransTime, claimedLic, makeResp, reqTime, overallTime, waitEnd, numTries, entitySchema, callback);
|
|
2970
|
+
}, myTimeout / 5);
|
|
2971
|
+
} catch (e) {
|
|
2972
|
+
// handle any exception
|
|
2973
|
+
const errorObj = transUtilInst.checkAndReturn(e, origin, 'Issue handling limit');
|
|
2974
|
+
return callback(null, errorObj);
|
|
2975
|
+
}
|
|
2976
|
+
}
|
|
2977
|
+
|
|
2978
|
+
/*
|
|
2979
|
+
* INTERNAL FUNCTION: handleAbortResponse handles timeout issues with System
|
|
2980
|
+
* like when the system has died
|
|
2981
|
+
*/
|
|
2982
|
+
function handleAbortResponse(request, callProperties, myTransTime, claimedLic, makeResp, reqTime, overallTime, waitEnd, numTries, entitySchema, callback) {
|
|
2983
|
+
const origin = `${id}-connectorRest-handleAbortResponse`;
|
|
2984
|
+
log.trace(origin);
|
|
2985
|
+
let reqDiff = process.hrtime(reqTime);
|
|
2986
|
+
let reqEnd = (reqDiff[0] * NS_PER_SEC) + reqDiff[1];
|
|
2987
|
+
log.warn(`${origin}: Request ${claimedLic.request_id} Transaction ${myTransTime} aborted after ${reqEnd} nanoseconds - Entering handling aborted requests`);
|
|
2988
|
+
reqDiff = undefined;
|
|
2989
|
+
reqEnd = undefined;
|
|
2990
|
+
|
|
2991
|
+
try {
|
|
2992
|
+
// wait for System to come back with a good healthcheck
|
|
2993
|
+
return waitForSystem(callProperties)
|
|
2994
|
+
.then(() => {
|
|
2995
|
+
log.debug(`${origin}: Cleared to reattempt`);
|
|
2996
|
+
|
|
2997
|
+
// restart the current request
|
|
2998
|
+
return retryInvalidResponse(request, callProperties, myTransTime, claimedLic, makeResp, reqTime, overallTime, waitEnd, numTries, entitySchema, callback);
|
|
2999
|
+
});
|
|
3000
|
+
} catch (e) {
|
|
3001
|
+
// handle any exception
|
|
3002
|
+
const errorObj = transUtilInst.checkAndReturn(e, origin, 'Issue handling abort');
|
|
3003
|
+
return callback(null, errorObj);
|
|
3004
|
+
}
|
|
3005
|
+
}
|
|
3006
|
+
|
|
3007
|
+
/*
|
|
3008
|
+
* INTERNAL FUNCTION: noQueueRequest is used to perform normal requests.
|
|
3009
|
+
* Normal being request that can run as they come in - there are no license or
|
|
3010
|
+
* throttling limitations on these requests.
|
|
3011
|
+
*/
|
|
3012
|
+
function noQueueRequest(request, callProperties, overallTime, entitySchema, callback) {
|
|
3013
|
+
const origin = `${id}-connectorRest-noQueueRequest`;
|
|
3014
|
+
log.trace(origin);
|
|
3015
|
+
|
|
3016
|
+
try {
|
|
3017
|
+
// call to make the request
|
|
3018
|
+
return requestAuthenticate(request, entitySchema, null, callProperties, (mres, merror) => {
|
|
3019
|
+
if (merror) {
|
|
3020
|
+
return callback(null, merror);
|
|
3021
|
+
}
|
|
3022
|
+
|
|
3023
|
+
let retryError = limitRetryError;
|
|
3024
|
+
let retries = numRetries;
|
|
3025
|
+
if (callProperties && callProperties.request && callProperties.request.limit_retry_error) {
|
|
3026
|
+
if (typeof callProperties.request.limit_retry_error === 'number'
|
|
3027
|
+
|| typeof callProperties.request.limit_retry_error === 'string') {
|
|
3028
|
+
retryError = [Number(callProperties.request.limit_retry_error)];
|
|
3029
|
+
} else if (Array.isArray(callProperties.request.limit_retry_error)) {
|
|
3030
|
+
retryError = [];
|
|
3031
|
+
for (let l = 0; l < callProperties.request.limit_retry_error.length; l += 1) {
|
|
3032
|
+
if (typeof callProperties.request.limit_retry_error[l] === 'number') {
|
|
3033
|
+
retryError.push(Number(callProperties.request.limit_retry_error[l]));
|
|
3034
|
+
} else if (typeof callProperties.request.limit_retry_error[l] === 'string'
|
|
3035
|
+
&& callProperties.request.limit_retry_error[l].indexOf('-') < 0) {
|
|
3036
|
+
retryError.push(Number(callProperties.request.limit_retry_error[l]));
|
|
3037
|
+
} else if (typeof callProperties.request.limit_retry_error[l] === 'string'
|
|
3038
|
+
&& callProperties.request.limit_retry_error[l].indexOf('-') >= 0) {
|
|
3039
|
+
const srange = Number(callProperties.request.limit_retry_error[l].split('-')[0]);
|
|
3040
|
+
const erange = Number(callProperties.request.limit_retry_error[l].split('-')[1]);
|
|
3041
|
+
for (let r = srange; r <= erange; r += 1) {
|
|
3042
|
+
retryError.push(r);
|
|
3043
|
+
}
|
|
3044
|
+
}
|
|
3045
|
+
}
|
|
3046
|
+
}
|
|
3047
|
+
}
|
|
3048
|
+
if (callProperties && callProperties.request && callProperties.request.number_retries) {
|
|
3049
|
+
retries = callProperties.request.number_retries;
|
|
3050
|
+
}
|
|
3051
|
+
|
|
3052
|
+
// if invalid token, handle that and retry
|
|
3053
|
+
if (mres !== null && mres.code !== undefined && Number(mres.code) === Number(tokenError)
|
|
3054
|
+
&& authMethod === 'request_token') {
|
|
3055
|
+
// if we took an invalid token - try one more time
|
|
3056
|
+
return handleInvalidToken(request, callProperties, 0, { request_id: 0 }, mres, overallTime, overallTime, 0, 1, entitySchema, callback);
|
|
3057
|
+
}
|
|
3058
|
+
|
|
3059
|
+
// if the response is valid - good data or legitimate data error
|
|
3060
|
+
if (retries < 1 || (mres !== null && mres.code !== undefined && !retryError.includes(Number(mres.code))
|
|
3061
|
+
&& (authMethod !== 'request_token' || Number(mres.code) !== Number(tokenError)))) {
|
|
3062
|
+
return handleEndResponse(request, mres, overallTime, callback);
|
|
3063
|
+
}
|
|
3064
|
+
|
|
3065
|
+
if (mres !== null && mres.code !== undefined && retryError.includes(Number(mres.code))) {
|
|
3066
|
+
// if we took a out of licenses - try one more time
|
|
3067
|
+
return handleLimitResponse(request, callProperties, 0, { request_id: 0 }, mres, overallTime, overallTime, 0, 1, entitySchema, callback);
|
|
3068
|
+
}
|
|
3069
|
+
|
|
3070
|
+
// if we took an http error - like aborting
|
|
3071
|
+
return handleAbortResponse(request, callProperties, 0, { request_id: 0 }, mres, overallTime, overallTime, 0, 1, entitySchema, callback);
|
|
3072
|
+
});
|
|
3073
|
+
} catch (e) {
|
|
3074
|
+
// handle any exception
|
|
3075
|
+
const errorObj = transUtilInst.checkAndReturn(e, origin, 'Issue with request');
|
|
3076
|
+
return callback(null, errorObj);
|
|
3077
|
+
}
|
|
3078
|
+
}
|
|
3079
|
+
|
|
3080
|
+
/*
|
|
3081
|
+
* INTERNAL FUNCTION: makeThrottleRequest is used to perform throttled request.
|
|
3082
|
+
* Throttled request means that Pronghorn is only allowed to run X concurrent
|
|
3083
|
+
* requests to the system.
|
|
3084
|
+
*
|
|
3085
|
+
* It will put the request in a queue item, wait until
|
|
3086
|
+
* it can get its turn, then run the request and return the results
|
|
3087
|
+
*/
|
|
3088
|
+
function queueThrottleRequest(request, callProperties, myRequestId, myTransTime, priority, event, overallTime, entitySchema, callback) {
|
|
3089
|
+
const origin = `${id}-connectorRest-queueThrottleRequest`;
|
|
3090
|
+
log.trace(`${origin} - ${myRequestId}`);
|
|
3091
|
+
|
|
3092
|
+
try {
|
|
3093
|
+
// request place in Queue
|
|
3094
|
+
return throttleEng.requestQueueItem(myRequestId, myTransTime, priority, event, (myItem, qerror) => {
|
|
3095
|
+
if (qerror) {
|
|
3096
|
+
return callback(null, qerror);
|
|
3097
|
+
}
|
|
3098
|
+
|
|
3099
|
+
let waitStart = process.hrtime();
|
|
3100
|
+
|
|
3101
|
+
// check if it is my turn
|
|
3102
|
+
return throttleEng.waitingMyTurn(myItem, (claimedLic, werror) => {
|
|
3103
|
+
let waitDiff = process.hrtime(waitStart);
|
|
3104
|
+
const waitEnd = (waitDiff[0] * NS_PER_SEC) + waitDiff[1];
|
|
3105
|
+
log.debug(`${origin}: Request ${myRequestId} Transaction ${myTransTime} wait took: ${waitEnd} nanoseconds`);
|
|
3106
|
+
const reqTime = process.hrtime();
|
|
3107
|
+
|
|
3108
|
+
if (werror) {
|
|
3109
|
+
return callback(null, werror);
|
|
3110
|
+
}
|
|
3111
|
+
|
|
3112
|
+
// clean up used variables from memory setting to undefined minimizes memory
|
|
3113
|
+
waitStart = undefined;
|
|
3114
|
+
waitDiff = undefined;
|
|
3115
|
+
|
|
3116
|
+
// call to make the request
|
|
3117
|
+
return requestAuthenticate(request, entitySchema, null, callProperties, (mres, merror) => {
|
|
3118
|
+
if (merror) {
|
|
3119
|
+
return callback(null, merror);
|
|
3120
|
+
}
|
|
3121
|
+
|
|
3122
|
+
let retryError = limitRetryError;
|
|
3123
|
+
let retries = numRetries;
|
|
3124
|
+
if (callProperties && callProperties.request && callProperties.request.limit_retry_error) {
|
|
3125
|
+
if (typeof callProperties.request.limit_retry_error === 'number'
|
|
3126
|
+
|| typeof callProperties.request.limit_retry_error === 'string') {
|
|
3127
|
+
retryError = [Number(callProperties.request.limit_retry_error)];
|
|
3128
|
+
} else if (Array.isArray(callProperties.request.limit_retry_error)) {
|
|
3129
|
+
retryError = [];
|
|
3130
|
+
for (let l = 0; l < callProperties.request.limit_retry_error.length; l += 1) {
|
|
3131
|
+
if (typeof callProperties.request.limit_retry_error[l] === 'number') {
|
|
3132
|
+
retryError.push(Number(callProperties.request.limit_retry_error[l]));
|
|
3133
|
+
} else if (typeof callProperties.request.limit_retry_error[l] === 'string'
|
|
3134
|
+
&& callProperties.request.limit_retry_error[l].indexOf('-') < 0) {
|
|
3135
|
+
retryError.push(Number(callProperties.request.limit_retry_error[l]));
|
|
3136
|
+
} else if (typeof callProperties.request.limit_retry_error[l] === 'string'
|
|
3137
|
+
&& callProperties.request.limit_retry_error[l].indexOf('-') >= 0) {
|
|
3138
|
+
const srange = Number(callProperties.request.limit_retry_error[l].split('-')[0]);
|
|
3139
|
+
const erange = Number(callProperties.request.limit_retry_error[l].split('-')[1]);
|
|
3140
|
+
for (let r = srange; r <= erange; r += 1) {
|
|
3141
|
+
retryError.push(r);
|
|
3142
|
+
}
|
|
3143
|
+
}
|
|
3144
|
+
}
|
|
3145
|
+
}
|
|
3146
|
+
}
|
|
3147
|
+
if (callProperties && callProperties.request && callProperties.request.number_retries) {
|
|
3148
|
+
retries = callProperties.request.number_retries;
|
|
3149
|
+
}
|
|
3150
|
+
|
|
3151
|
+
// if invalid token, handle that and retry
|
|
3152
|
+
if (mres !== null && mres.code !== undefined && Number(mres.code) === Number(tokenError)
|
|
3153
|
+
&& authMethod === 'request_token') {
|
|
3154
|
+
// if we took an invalid token - try one more time
|
|
3155
|
+
return handleInvalidToken(request, callProperties, myTransTime, claimedLic, mres, reqTime, overallTime, waitEnd, 1, entitySchema, callback);
|
|
3156
|
+
}
|
|
3157
|
+
|
|
3158
|
+
// if the response is valid - good data or legitimate data error
|
|
3159
|
+
if (retries < 1 || (mres !== null && mres.code !== undefined && !retryError.includes(Number(mres.code))
|
|
3160
|
+
&& (authMethod !== 'request_token' || Number(mres.code) !== Number(tokenError)))) {
|
|
3161
|
+
return handleEndThrottleResponse(request, myTransTime, claimedLic, mres, reqTime, overallTime, waitEnd, callback);
|
|
3162
|
+
}
|
|
3163
|
+
|
|
3164
|
+
if (mres !== null && mres.code !== undefined && retryError.includes(Number(mres.code))) {
|
|
3165
|
+
// if we took a out of licenses - try one more time
|
|
3166
|
+
return handleLimitResponse(request, callProperties, myTransTime, claimedLic, mres, reqTime, overallTime, waitEnd, 1, entitySchema, callback);
|
|
3167
|
+
}
|
|
3168
|
+
|
|
3169
|
+
// if we took an http error - like aborting
|
|
3170
|
+
return handleAbortResponse(request, callProperties, myTransTime, claimedLic, mres, reqTime, overallTime, waitEnd, 1, entitySchema, callback);
|
|
3171
|
+
});
|
|
3172
|
+
});
|
|
3173
|
+
});
|
|
3174
|
+
} catch (e) {
|
|
3175
|
+
// handle any exception
|
|
3176
|
+
const errorObj = transUtilInst.checkAndReturn(e, origin, 'Issue with queue request');
|
|
3177
|
+
return callback(null, errorObj);
|
|
3178
|
+
}
|
|
3179
|
+
}
|
|
3180
|
+
|
|
3181
|
+
class ConnectorRest {
|
|
3182
|
+
/**
|
|
3183
|
+
* Connector
|
|
3184
|
+
* @constructor
|
|
3185
|
+
*/
|
|
3186
|
+
constructor(prongid, properties, transUtilCl, propUtilCl, dbUtilCl) {
|
|
3187
|
+
this.myid = prongid;
|
|
3188
|
+
id = prongid;
|
|
3189
|
+
|
|
3190
|
+
this.transUtil = transUtilCl;
|
|
3191
|
+
transUtilInst = this.transUtil;
|
|
3192
|
+
this.propUtil = propUtilCl;
|
|
3193
|
+
propUtilInst = this.propUtil;
|
|
3194
|
+
this.dbUtil = dbUtilCl;
|
|
3195
|
+
dbUtilInst = this.dbUtil;
|
|
3196
|
+
|
|
3197
|
+
// this uniquely identifies this adapter on this pronghorn system
|
|
3198
|
+
phInstance = `${id}-${os.hostname()}`;
|
|
3199
|
+
archiveColl = id + archiveColl;
|
|
3200
|
+
this.tlockInst = new AsyncLockCl();
|
|
3201
|
+
tlock = this.tlockInst;
|
|
3202
|
+
this.hlockInst = new AsyncLockCl();
|
|
3203
|
+
hlock = this.hlockInst;
|
|
3204
|
+
this.hclockInst = new AsyncLockCl();
|
|
3205
|
+
hclock = this.hclockInst;
|
|
3206
|
+
this.slock = new AsyncLockCl();
|
|
3207
|
+
crest = this;
|
|
3208
|
+
|
|
3209
|
+
// set up the properties I care about
|
|
3210
|
+
this.refreshProperties(properties);
|
|
3211
|
+
|
|
3212
|
+
this.throttleEngInst = new ThrottleCl(id, properties, this.transUtil, this.dbUtil);
|
|
3213
|
+
throttleEng = this.throttleEngInst;
|
|
3214
|
+
|
|
3215
|
+
this.connect((status) => {
|
|
3216
|
+
const origin = `${this.myid}-connectorRest-constructor`;
|
|
3217
|
+
log.info(`${origin}: connect status ${status}`);
|
|
3218
|
+
});
|
|
3219
|
+
}
|
|
3220
|
+
|
|
3221
|
+
/* CONNECTOR ENGINE EXTERNAL FUNCTIONS */
|
|
3222
|
+
/**
|
|
3223
|
+
* refreshProperties is used to set up all of the properties for the connector.
|
|
3224
|
+
* It allows properties to be changed later by simply calling refreshProperties rather
|
|
3225
|
+
* than having to restart the connector.
|
|
3226
|
+
*
|
|
3227
|
+
* @function refreshProperties
|
|
3228
|
+
* @param {Object} properties - an object containing all of the properties
|
|
3229
|
+
*/
|
|
3230
|
+
refreshProperties(properties) {
|
|
3231
|
+
const origin = `${this.myid}-connectorRest-refreshProperties`;
|
|
3232
|
+
log.trace(origin);
|
|
3233
|
+
props = properties;
|
|
3234
|
+
|
|
3235
|
+
if (!props) {
|
|
3236
|
+
log.error(`${origin}: Connector received no properties!`);
|
|
3237
|
+
return;
|
|
3238
|
+
}
|
|
3239
|
+
|
|
3240
|
+
// REPLACE WITH SCHEMA VALIDATOR???
|
|
3241
|
+
// get all of my properties - use for initial connectivity check
|
|
3242
|
+
// set the host (required - default is null)
|
|
3243
|
+
if (typeof props.host === 'string') {
|
|
3244
|
+
host = props.host;
|
|
3245
|
+
}
|
|
3246
|
+
|
|
3247
|
+
// set the port (required - default is null)
|
|
3248
|
+
if (typeof props.port === 'number' || typeof props.port === 'string') {
|
|
3249
|
+
port = Number(props.port);
|
|
3250
|
+
}
|
|
3251
|
+
|
|
3252
|
+
// set the base path (optional - default is null)
|
|
3253
|
+
if (typeof props.base_path === 'string') {
|
|
3254
|
+
basepath = props.base_path;
|
|
3255
|
+
}
|
|
3256
|
+
|
|
3257
|
+
// set the version (optional - default is null)
|
|
3258
|
+
if (typeof props.version === 'string') {
|
|
3259
|
+
version = props.version;
|
|
3260
|
+
}
|
|
3261
|
+
|
|
3262
|
+
if (props.authentication) {
|
|
3263
|
+
// set the authentication method (required - default is null)
|
|
3264
|
+
if (typeof props.authentication.auth_method === 'string') {
|
|
3265
|
+
authMethod = props.authentication.auth_method;
|
|
3266
|
+
}
|
|
3267
|
+
|
|
3268
|
+
// set the username (required - default is null)
|
|
3269
|
+
if (typeof props.authentication.username === 'string') {
|
|
3270
|
+
username = props.authentication.username;
|
|
3271
|
+
}
|
|
3272
|
+
|
|
3273
|
+
// set the password (required - default is null)
|
|
3274
|
+
if (typeof props.authentication.password === 'string') {
|
|
3275
|
+
password = props.authentication.password;
|
|
3276
|
+
}
|
|
3277
|
+
|
|
3278
|
+
// set the static token (required - default is null)
|
|
3279
|
+
if (typeof props.authentication.token === 'string') {
|
|
3280
|
+
staticToken = props.authentication.token;
|
|
3281
|
+
}
|
|
3282
|
+
|
|
3283
|
+
// set the token user field (required - default is username)
|
|
3284
|
+
if (typeof props.authentication.token_user_field === 'string') {
|
|
3285
|
+
tokenUserField = props.authentication.token_user_field;
|
|
3286
|
+
}
|
|
3287
|
+
|
|
3288
|
+
// set the token password field (required - default is password)
|
|
3289
|
+
if (typeof props.authentication.token_password_field === 'string') {
|
|
3290
|
+
tokenPwdField = props.authentication.token_password_field;
|
|
3291
|
+
}
|
|
3292
|
+
|
|
3293
|
+
// set the token result field (required - default is token)
|
|
3294
|
+
if (typeof props.authentication.token_result_field === 'string') {
|
|
3295
|
+
tokenResField = props.authentication.token_result_field;
|
|
3296
|
+
}
|
|
3297
|
+
|
|
3298
|
+
// set the token URI path (required - default is null)
|
|
3299
|
+
if (typeof props.authentication.token_URI_path === 'string') {
|
|
3300
|
+
tokenPath = props.authentication.token_URI_path;
|
|
3301
|
+
}
|
|
3302
|
+
|
|
3303
|
+
// set the invalid token error (optional - default is null)
|
|
3304
|
+
if (typeof props.authentication.invalid_token_error === 'number'
|
|
3305
|
+
|| typeof props.authentication.invalid_token_error === 'string') {
|
|
3306
|
+
tokenError = Number(props.authentication.invalid_token_error);
|
|
3307
|
+
}
|
|
3308
|
+
|
|
3309
|
+
// set the token timeout (optional - default is null)
|
|
3310
|
+
if (typeof props.authentication.token_timeout === 'number'
|
|
3311
|
+
|| typeof props.authentication.token_timeout === 'string') {
|
|
3312
|
+
tokenTimeout = Number(props.authentication.token_timeout);
|
|
3313
|
+
}
|
|
3314
|
+
|
|
3315
|
+
// set the token cache (required - default is local)
|
|
3316
|
+
if (typeof props.authentication.token_cache === 'string') {
|
|
3317
|
+
tokenCache = props.authentication.token_cache;
|
|
3318
|
+
}
|
|
3319
|
+
|
|
3320
|
+
// set the auth field (required - default is null)
|
|
3321
|
+
if (typeof props.authentication.auth_field === 'string') {
|
|
3322
|
+
authField = [props.authentication.auth_field];
|
|
3323
|
+
} else if (Array.isArray(props.authentication.auth_field)) {
|
|
3324
|
+
authField = props.authentication.auth_field;
|
|
3325
|
+
}
|
|
3326
|
+
|
|
3327
|
+
// set the auth format (required - default is null)
|
|
3328
|
+
if (typeof props.authentication.auth_field_format === 'string') {
|
|
3329
|
+
authFormat = [props.authentication.auth_field_format];
|
|
3330
|
+
} else if (Array.isArray(props.authentication.auth_field_format)) {
|
|
3331
|
+
authFormat = props.authentication.auth_field_format;
|
|
3332
|
+
}
|
|
3333
|
+
|
|
3334
|
+
// set the auth logging (optional - default is false)
|
|
3335
|
+
if (typeof props.authentication.auth_logging === 'boolean') {
|
|
3336
|
+
authLogging = props.authentication.auth_logging;
|
|
3337
|
+
}
|
|
3338
|
+
|
|
3339
|
+
// set the client id (required - default is null)
|
|
3340
|
+
if (typeof props.authentication.client_id === 'string') {
|
|
3341
|
+
clientId = props.authentication.client_id;
|
|
3342
|
+
}
|
|
3343
|
+
|
|
3344
|
+
// set the client secret (required - default is null)
|
|
3345
|
+
if (typeof props.authentication.client_secret === 'string') {
|
|
3346
|
+
clientSecret = props.authentication.client_secret;
|
|
3347
|
+
}
|
|
3348
|
+
|
|
3349
|
+
// set the grant type (required - default is null)
|
|
3350
|
+
if (typeof props.authentication.grant_type === 'string') {
|
|
3351
|
+
grantType = props.authentication.grant_type;
|
|
3352
|
+
}
|
|
3353
|
+
}
|
|
3354
|
+
|
|
3355
|
+
// set the stub mode (optional - default is false)
|
|
3356
|
+
if (typeof props.stub === 'boolean') {
|
|
3357
|
+
stub = props.stub;
|
|
3358
|
+
}
|
|
3359
|
+
|
|
3360
|
+
// set the protocol (optional - default is http)
|
|
3361
|
+
if (typeof props.protocol === 'string') {
|
|
3362
|
+
protocol = props.protocol;
|
|
3363
|
+
}
|
|
3364
|
+
|
|
3365
|
+
// set the healthcheck path (required - default is null)
|
|
3366
|
+
if (props.healthcheck) {
|
|
3367
|
+
if (typeof props.healthcheck.URI_Path === 'string') {
|
|
3368
|
+
healthcheckpath = props.healthcheck.URI_Path;
|
|
3369
|
+
}
|
|
3370
|
+
}
|
|
3371
|
+
|
|
3372
|
+
if (props.throttle) {
|
|
3373
|
+
// set the throttle enabled (optional - default is false)
|
|
3374
|
+
if (typeof props.throttle.throttle_enabled === 'boolean') {
|
|
3375
|
+
throttleEnabled = props.throttle.throttle_enabled;
|
|
3376
|
+
}
|
|
3377
|
+
|
|
3378
|
+
// set the throttle number of pronghorns (optional - default is 1)
|
|
3379
|
+
if (props.throttle.number_pronghorns && Number(props.throttle.number_pronghorns) >= 1) {
|
|
3380
|
+
numberPhs = Number(props.throttle.number_pronghorns);
|
|
3381
|
+
}
|
|
3382
|
+
}
|
|
3383
|
+
|
|
3384
|
+
if (props.request) {
|
|
3385
|
+
// set the number of redirects (optional - default is 0)
|
|
3386
|
+
if (typeof props.request.number_redirects === 'number'
|
|
3387
|
+
|| typeof props.request.number_redirects === 'string') {
|
|
3388
|
+
numRedirects = Number(props.request.number_redirects);
|
|
3389
|
+
}
|
|
3390
|
+
|
|
3391
|
+
// set the number of retries (optional - default is 3)
|
|
3392
|
+
if (typeof props.request.number_retries === 'number'
|
|
3393
|
+
|| typeof props.request.number_retries === 'string') {
|
|
3394
|
+
numRetries = Number(props.request.number_retries);
|
|
3395
|
+
}
|
|
3396
|
+
|
|
3397
|
+
// set the request retry error (optional - default is 0)
|
|
3398
|
+
if (typeof props.request.limit_retry_error === 'number'
|
|
3399
|
+
|| typeof props.request.limit_retry_error === 'string') {
|
|
3400
|
+
limitRetryError = [Number(props.request.limit_retry_error)];
|
|
3401
|
+
} else if (Array.isArray(props.request.limit_retry_error)) {
|
|
3402
|
+
limitRetryError = [];
|
|
3403
|
+
for (let l = 0; l < props.request.limit_retry_error.length; l += 1) {
|
|
3404
|
+
if (typeof props.request.limit_retry_error[l] === 'number') {
|
|
3405
|
+
limitRetryError.push(Number(props.request.limit_retry_error[l]));
|
|
3406
|
+
} else if (typeof props.request.limit_retry_error[l] === 'string'
|
|
3407
|
+
&& props.request.limit_retry_error[l].indexOf('-') < 0) {
|
|
3408
|
+
limitRetryError.push(Number(props.request.limit_retry_error[l]));
|
|
3409
|
+
} else if (typeof props.request.limit_retry_error[l] === 'string'
|
|
3410
|
+
&& props.request.limit_retry_error[l].indexOf('-') >= 0) {
|
|
3411
|
+
const srange = Number(props.request.limit_retry_error[l].split('-')[0]);
|
|
3412
|
+
const erange = Number(props.request.limit_retry_error[l].split('-')[1]);
|
|
3413
|
+
for (let r = srange; r <= erange; r += 1) {
|
|
3414
|
+
limitRetryError.push(r);
|
|
3415
|
+
}
|
|
3416
|
+
}
|
|
3417
|
+
}
|
|
3418
|
+
}
|
|
3419
|
+
|
|
3420
|
+
// set the request attempt timeout (optional - default is 5000)
|
|
3421
|
+
if (typeof props.request.attempt_timeout === 'number'
|
|
3422
|
+
|| typeof props.request.attempt_timeout === 'string') {
|
|
3423
|
+
attemptTimeout = Number(props.request.attempt_timeout);
|
|
3424
|
+
}
|
|
3425
|
+
|
|
3426
|
+
// set the request archiving flag (optional - default is false)
|
|
3427
|
+
if (props.request.global_request && typeof props.request.global_request === 'object') {
|
|
3428
|
+
globalRequest = props.request.global_request;
|
|
3429
|
+
}
|
|
3430
|
+
|
|
3431
|
+
// set the request healthcheck on timeout flag (optional - default is false)
|
|
3432
|
+
if (typeof props.request.healthcheck_on_timeout === 'boolean') {
|
|
3433
|
+
healthcheckOnTimeout = props.request.healthcheck_on_timeout;
|
|
3434
|
+
}
|
|
3435
|
+
|
|
3436
|
+
// set the request archiving flag (optional - default is false)
|
|
3437
|
+
if (typeof props.request.archiving === 'boolean') {
|
|
3438
|
+
archiving = props.request.archiving;
|
|
3439
|
+
}
|
|
3440
|
+
|
|
3441
|
+
// set the request return request (optional - default is false)
|
|
3442
|
+
if (typeof props.request.return_request === 'boolean') {
|
|
3443
|
+
returnRequest = props.request.return_request;
|
|
3444
|
+
}
|
|
3445
|
+
}
|
|
3446
|
+
|
|
3447
|
+
if (props.proxy) {
|
|
3448
|
+
// set the proxy enabled (optional - default is false)
|
|
3449
|
+
if (typeof props.proxy.enabled === 'boolean') {
|
|
3450
|
+
proxyEnabled = props.proxy.enabled;
|
|
3451
|
+
}
|
|
3452
|
+
|
|
3453
|
+
// set the proxy host (optional - default is null)
|
|
3454
|
+
if (typeof props.proxy.host === 'string') {
|
|
3455
|
+
proxyHost = props.proxy.host;
|
|
3456
|
+
}
|
|
3457
|
+
|
|
3458
|
+
// set the proxy port (optional - default is null)
|
|
3459
|
+
if (typeof props.proxy.port === 'number' || typeof props.proxy.port === 'string') {
|
|
3460
|
+
proxyPort = Number(props.proxy.port);
|
|
3461
|
+
}
|
|
3462
|
+
|
|
3463
|
+
// set the proxy protocol (optional - default is same as protocol)
|
|
3464
|
+
if (typeof props.proxy.protocol === 'string') {
|
|
3465
|
+
proxyProtocol = props.proxy.protocol;
|
|
3466
|
+
} else {
|
|
3467
|
+
proxyProtocol = protocol;
|
|
3468
|
+
}
|
|
3469
|
+
|
|
3470
|
+
// set the proxy username (optional - default is null)
|
|
3471
|
+
if (typeof props.proxy.username === 'string') {
|
|
3472
|
+
proxyUser = props.proxy.username;
|
|
3473
|
+
}
|
|
3474
|
+
|
|
3475
|
+
// set the proxy password (optional - default is null)
|
|
3476
|
+
if (typeof props.proxy.password === 'string') {
|
|
3477
|
+
proxyPassword = props.proxy.password;
|
|
3478
|
+
}
|
|
3479
|
+
}
|
|
3480
|
+
|
|
3481
|
+
if (props.ssl) {
|
|
3482
|
+
// set the ssl enabled (optional - default is false)
|
|
3483
|
+
if (typeof props.ssl.enabled === 'boolean') {
|
|
3484
|
+
sslEnabled = props.ssl.enabled;
|
|
3485
|
+
}
|
|
3486
|
+
|
|
3487
|
+
// set the ssl ecdhCurve (optional - default is false)
|
|
3488
|
+
if (typeof props.ssl.ecdhCurve === 'string' && props.ssl.ecdhCurve === 'auto') {
|
|
3489
|
+
ecdhAuto = true;
|
|
3490
|
+
}
|
|
3491
|
+
|
|
3492
|
+
// set the ssl accept invalid cert (optional - default is false)
|
|
3493
|
+
if (typeof props.ssl.accept_invalid_cert === 'boolean') {
|
|
3494
|
+
sslAcceptInvalid = props.ssl.accept_invalid_cert;
|
|
3495
|
+
}
|
|
3496
|
+
|
|
3497
|
+
// set the ssl ca file (optional - default is null)
|
|
3498
|
+
if (typeof props.ssl.ca_file === 'string') {
|
|
3499
|
+
sslCAFile = props.ssl.ca_file;
|
|
3500
|
+
}
|
|
3501
|
+
|
|
3502
|
+
// set the ssl key file (optional - default is null)
|
|
3503
|
+
if (typeof props.ssl.key_file === 'string') {
|
|
3504
|
+
sslKeyFile = props.ssl.key_file;
|
|
3505
|
+
}
|
|
3506
|
+
|
|
3507
|
+
// set the ssl cert file (optional - default is null)
|
|
3508
|
+
if (typeof props.ssl.cert_file === 'string') {
|
|
3509
|
+
sslCertFile = props.ssl.cert_file;
|
|
3510
|
+
}
|
|
3511
|
+
|
|
3512
|
+
// set the ssl ciphers (optional - default is null)
|
|
3513
|
+
if (typeof props.ssl.ciphers === 'string') {
|
|
3514
|
+
sslCiphers = props.ssl.ciphers;
|
|
3515
|
+
}
|
|
3516
|
+
|
|
3517
|
+
// set the ssl ciphers (optional - default is null)
|
|
3518
|
+
if (typeof props.ssl.secure_protocol === 'string') {
|
|
3519
|
+
secureProtocol = props.ssl.secure_protocol;
|
|
3520
|
+
}
|
|
3521
|
+
}
|
|
3522
|
+
|
|
3523
|
+
// if this is truly a refresh and we hae a throttle engine, refresh it
|
|
3524
|
+
if (this.throttleEngInst) {
|
|
3525
|
+
this.throttleEngInst.refreshProperties(properties);
|
|
3526
|
+
}
|
|
3527
|
+
}
|
|
3528
|
+
|
|
3529
|
+
/**
|
|
3530
|
+
* Connect function is used to verify that everything required to connect and communicate
|
|
3531
|
+
* has been provided.
|
|
3532
|
+
*
|
|
3533
|
+
* @function connect
|
|
3534
|
+
* @param {Function} callback - a callback function to return the result - connected?
|
|
3535
|
+
*/
|
|
3536
|
+
connect(callback) {
|
|
3537
|
+
const origin = `${this.myid}-connectorRest-connect`;
|
|
3538
|
+
log.trace(origin);
|
|
3539
|
+
|
|
3540
|
+
// if throttling with the database or archiving, need to make sure the database is there
|
|
3541
|
+
if ((throttleEnabled && numberPhs > 1) || archiving) {
|
|
3542
|
+
// CLEAN UP LOCK & LICENSES
|
|
3543
|
+
return waitForMongo()
|
|
3544
|
+
.then(() => {
|
|
3545
|
+
if (archiving) {
|
|
3546
|
+
const colRes = this.dbUtil.createCollection(archiveColl, (err, res) => {
|
|
3547
|
+
if (err) {
|
|
3548
|
+
log.error(`${origin}: collection ${archiveColl} could not be created.`);
|
|
3549
|
+
return callback(false);
|
|
3550
|
+
}
|
|
3551
|
+
log.info(`${origin}: Result collection check passed`);
|
|
3552
|
+
});
|
|
3553
|
+
}
|
|
3554
|
+
|
|
3555
|
+
if (throttleEnabled) {
|
|
3556
|
+
this.throttleEngInst.verifyReady((res, rerror) => {
|
|
3557
|
+
if (rerror) {
|
|
3558
|
+
return callback(false);
|
|
3559
|
+
}
|
|
3560
|
+
|
|
3561
|
+
log.debug(`${origin}: Throttle verify - ${res}`);
|
|
3562
|
+
});
|
|
3563
|
+
}
|
|
3564
|
+
|
|
3565
|
+
// Make sure all the properties are loaded
|
|
3566
|
+
if (!host) {
|
|
3567
|
+
log.error(`${origin}: FAILED TO LOAD - missing host`);
|
|
3568
|
+
return callback(false);
|
|
3569
|
+
}
|
|
3570
|
+
if (!port) {
|
|
3571
|
+
log.error(`${origin}: FAILED TO LOAD - missing port`);
|
|
3572
|
+
return callback(false);
|
|
3573
|
+
}
|
|
3574
|
+
if (authMethod === 'static_token') {
|
|
3575
|
+
if (!staticToken) {
|
|
3576
|
+
log.error(`${origin}: FAILED TO LOAD - missing static token`);
|
|
3577
|
+
return callback(false);
|
|
3578
|
+
}
|
|
3579
|
+
} else if (authMethod === 'request_token' || authMethod === 'basic user_password') {
|
|
3580
|
+
if (!username) {
|
|
3581
|
+
log.error(`${origin}: FAILED TO LOAD - missing username`);
|
|
3582
|
+
return callback(false);
|
|
3583
|
+
}
|
|
3584
|
+
if (!password) {
|
|
3585
|
+
log.error(`${origin}: FAILED TO LOAD - missing password`);
|
|
3586
|
+
return callback(false);
|
|
3587
|
+
}
|
|
3588
|
+
}
|
|
3589
|
+
if (sslEnabled && !sslAcceptInvalid && !sslCAFile) {
|
|
3590
|
+
log.error(`${origin}: FAILED TO LOAD - missing required CA File`);
|
|
3591
|
+
return callback(false);
|
|
3592
|
+
}
|
|
3593
|
+
|
|
3594
|
+
return callback(true);
|
|
3595
|
+
});
|
|
3596
|
+
}
|
|
3597
|
+
|
|
3598
|
+
// if throttling without database - just need to verify throttle engine is ready
|
|
3599
|
+
if (throttleEnabled) {
|
|
3600
|
+
this.throttleEngInst.verifyReady((res, rerror) => {
|
|
3601
|
+
if (rerror) {
|
|
3602
|
+
return callback(false);
|
|
3603
|
+
}
|
|
3604
|
+
|
|
3605
|
+
log.debug(`${origin}: Throttle verify - ${res}`);
|
|
3606
|
+
});
|
|
3607
|
+
}
|
|
3608
|
+
|
|
3609
|
+
// Make sure all the properties are loaded
|
|
3610
|
+
if (!host) {
|
|
3611
|
+
log.error(`${origin}: FAILED TO LOAD - missing host`);
|
|
3612
|
+
return callback(false);
|
|
3613
|
+
}
|
|
3614
|
+
if (!port) {
|
|
3615
|
+
log.error(`${origin}: FAILED TO LOAD - missing port`);
|
|
3616
|
+
return callback(false);
|
|
3617
|
+
}
|
|
3618
|
+
if (authMethod === 'static_token') {
|
|
3619
|
+
if (!staticToken) {
|
|
3620
|
+
log.error(`${origin}: FAILED TO LOAD - missing static token`);
|
|
3621
|
+
return callback(false);
|
|
3622
|
+
}
|
|
3623
|
+
} else if (authMethod === 'request_token' || authMethod === 'basic user_password') {
|
|
3624
|
+
if (!username) {
|
|
3625
|
+
log.error(`${origin}: FAILED TO LOAD - missing username`);
|
|
3626
|
+
return callback(false);
|
|
3627
|
+
}
|
|
3628
|
+
if (!password) {
|
|
3629
|
+
log.error(`${origin}: FAILED TO LOAD - missing password`);
|
|
3630
|
+
return callback(false);
|
|
3631
|
+
}
|
|
3632
|
+
}
|
|
3633
|
+
if (sslEnabled && !sslAcceptInvalid && !sslCAFile) {
|
|
3634
|
+
log.error(`${origin}: FAILED TO LOAD - missing required CA File`);
|
|
3635
|
+
return callback(false);
|
|
3636
|
+
}
|
|
3637
|
+
|
|
3638
|
+
return callback(true);
|
|
3639
|
+
}
|
|
3640
|
+
|
|
3641
|
+
/**
|
|
3642
|
+
* HealthCheck function is used to provide Pronghorn the status of this adapter.
|
|
3643
|
+
*
|
|
3644
|
+
* @function healthCheck
|
|
3645
|
+
* @param {Object} healthSchema - the schema for the healthcheck (optional)
|
|
3646
|
+
* @param {String} payload - the contents to send with the healthcheck
|
|
3647
|
+
* (optional)
|
|
3648
|
+
* @param {Object} headers - this allows for additional headers to
|
|
3649
|
+
* be added to the request. (optional)
|
|
3650
|
+
* Can be a stringified Object.
|
|
3651
|
+
* @param {Object} callProperties - properties to override on this call (optional)
|
|
3652
|
+
* @param {Function} callback - a callback function to return the result of the healthcheck
|
|
3653
|
+
*/
|
|
3654
|
+
healthCheck(healthSchema, payload, headers, callProperties, callback) {
|
|
3655
|
+
const origin = `${this.myid}-connectorRest-healthCheck`;
|
|
3656
|
+
log.trace(origin);
|
|
3657
|
+
|
|
3658
|
+
try {
|
|
3659
|
+
// healthcheck can not be throttled and put in queue because it would be blocked
|
|
3660
|
+
// so build the request for a simple call and make the request
|
|
3661
|
+
const options = {
|
|
3662
|
+
hostname: host,
|
|
3663
|
+
port,
|
|
3664
|
+
path: healthcheckpath,
|
|
3665
|
+
method: 'GET',
|
|
3666
|
+
headers
|
|
3667
|
+
};
|
|
3668
|
+
|
|
3669
|
+
// passed in properties override defaults
|
|
3670
|
+
if (callProperties && callProperties.host) {
|
|
3671
|
+
options.hostname = callProperties.host;
|
|
3672
|
+
}
|
|
3673
|
+
if (callProperties && callProperties.port) {
|
|
3674
|
+
options.port = callProperties.port;
|
|
3675
|
+
}
|
|
3676
|
+
|
|
3677
|
+
// save headers in memory
|
|
3678
|
+
if (headers) {
|
|
3679
|
+
// save it in memory
|
|
3680
|
+
cacheHHead = headers;
|
|
3681
|
+
}
|
|
3682
|
+
|
|
3683
|
+
// if there is a healthcheck schema, over ride the properties
|
|
3684
|
+
if (healthSchema) {
|
|
3685
|
+
options.path = healthSchema.entitypath;
|
|
3686
|
+
options.method = healthSchema.method;
|
|
3687
|
+
|
|
3688
|
+
// save it in memory
|
|
3689
|
+
cacheHSchema = healthSchema;
|
|
3690
|
+
}
|
|
3691
|
+
|
|
3692
|
+
// if the path has a base path parameter in it, need to replace it
|
|
3693
|
+
let bpathStr = '{base_path}';
|
|
3694
|
+
if (options.path.indexOf(bpathStr) >= 0) {
|
|
3695
|
+
// be able to support this if the base path has a slash before it or not
|
|
3696
|
+
if (options.path.indexOf('/{base_path}') >= 0) {
|
|
3697
|
+
bpathStr = '/{base_path}';
|
|
3698
|
+
}
|
|
3699
|
+
|
|
3700
|
+
// replace with base path if we have one, otherwise remove base path
|
|
3701
|
+
if (callProperties && callProperties.base_path) {
|
|
3702
|
+
// if no leading /, insert one
|
|
3703
|
+
if (callProperties.base_path.indexOf('/') !== 0) {
|
|
3704
|
+
options.path = options.path.replace(bpathStr, `/${callProperties.base_path}`);
|
|
3705
|
+
} else {
|
|
3706
|
+
options.path = options.path.replace(bpathStr, callProperties.base_path);
|
|
3707
|
+
}
|
|
3708
|
+
} else if (basepath) {
|
|
3709
|
+
// if no leading /, insert one
|
|
3710
|
+
if (basepath.indexOf('/') !== 0) {
|
|
3711
|
+
options.path = options.path.replace(bpathStr, `/${basepath}`);
|
|
3712
|
+
} else {
|
|
3713
|
+
options.path = options.path.replace(bpathStr, basepath);
|
|
3714
|
+
}
|
|
3715
|
+
} else {
|
|
3716
|
+
options.path = options.path.replace(bpathStr, '');
|
|
3717
|
+
}
|
|
3718
|
+
}
|
|
3719
|
+
|
|
3720
|
+
// if the path has a version parameter in it, need to replace it
|
|
3721
|
+
let versStr = '{version}';
|
|
3722
|
+
if (options.path.indexOf(versStr) >= 0) {
|
|
3723
|
+
// be able to support this if the version has a slash before it or not
|
|
3724
|
+
if (options.path.indexOf('/{version}') >= 0) {
|
|
3725
|
+
versStr = '/{version}';
|
|
3726
|
+
}
|
|
3727
|
+
|
|
3728
|
+
// replace with version if we have one, otherwise remove version
|
|
3729
|
+
if (callProperties && callProperties.version) {
|
|
3730
|
+
options.path = options.path.replace(versStr, `/${encodeURIComponent(callProperties.version)}`);
|
|
3731
|
+
} else if (version) {
|
|
3732
|
+
options.path = options.path.replace(versStr, `/${encodeURIComponent(version)}`);
|
|
3733
|
+
} else {
|
|
3734
|
+
options.path = options.path.replace(versStr, '');
|
|
3735
|
+
}
|
|
3736
|
+
}
|
|
3737
|
+
|
|
3738
|
+
// if ssl enabled add the options for ssl
|
|
3739
|
+
if (callProperties && callProperties.ssl && Object.hasOwnProperty.call(callProperties.ssl, 'enabled')) {
|
|
3740
|
+
if (callProperties.ssl.enabled) {
|
|
3741
|
+
if (callProperties.ssl.accept_invalid_cert) {
|
|
3742
|
+
// if we are accepting invalid certificates (ok for lab not so much production)
|
|
3743
|
+
options.rejectUnauthorized = false;
|
|
3744
|
+
} else {
|
|
3745
|
+
// if we are not accepting invalid certs, need the ca file in the options
|
|
3746
|
+
try {
|
|
3747
|
+
options.rejectUnauthorized = true;
|
|
3748
|
+
options.ca = [fs.readFileSync(callProperties.ssl.ca_file)];
|
|
3749
|
+
} catch (e) {
|
|
3750
|
+
const errorObj = this.transUtil.formatErrorObject(origin, 'Missing File', [callProperties.ssl.ca_file], null, null, null);
|
|
3751
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
3752
|
+
return callback(null, errorObj);
|
|
3753
|
+
}
|
|
3754
|
+
}
|
|
3755
|
+
|
|
3756
|
+
if (callProperties.ssl.ciphers) {
|
|
3757
|
+
options.ciphers = callProperties.ssl.ciphers;
|
|
3758
|
+
}
|
|
3759
|
+
if (callProperties.ssl.secure_protocol) {
|
|
3760
|
+
options.secureProtocol = callProperties.ssl.secure_protocol;
|
|
3761
|
+
}
|
|
3762
|
+
|
|
3763
|
+
log.info(`${origin}: Connector SSL connections enabled`);
|
|
3764
|
+
}
|
|
3765
|
+
} else if (sslEnabled) {
|
|
3766
|
+
if (sslAcceptInvalid) {
|
|
3767
|
+
// if we are accepting invalid certificates (ok for lab not so much production)
|
|
3768
|
+
options.rejectUnauthorized = false;
|
|
3769
|
+
} else {
|
|
3770
|
+
// if we are not accepting invalid certs, need the ca file in the options
|
|
3771
|
+
try {
|
|
3772
|
+
options.rejectUnauthorized = true;
|
|
3773
|
+
options.ca = [fs.readFileSync(sslCAFile)];
|
|
3774
|
+
} catch (e) {
|
|
3775
|
+
const errorObj = this.transUtil.formatErrorObject(origin, 'Missing File', [sslCAFile], null, null, null);
|
|
3776
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
3777
|
+
return callback(null, errorObj);
|
|
3778
|
+
}
|
|
3779
|
+
// if there is a cert file, try to read in a cert file in the options
|
|
3780
|
+
if (sslCertFile) {
|
|
3781
|
+
try {
|
|
3782
|
+
options.cert = [fs.readFileSync(sslCertFile)];
|
|
3783
|
+
} catch (e) {
|
|
3784
|
+
const errorObj = this.transUtil.formatErrorObject(origin, 'Missing File', [sslCertFile], null, null, null);
|
|
3785
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
3786
|
+
return callback(null, errorObj);
|
|
3787
|
+
}
|
|
3788
|
+
}
|
|
3789
|
+
// if there is a key file, try to read in a key file in the options
|
|
3790
|
+
if (sslKeyFile) {
|
|
3791
|
+
try {
|
|
3792
|
+
options.key = [fs.readFileSync(sslKeyFile)];
|
|
3793
|
+
} catch (e) {
|
|
3794
|
+
const errorObj = this.transUtil.formatErrorObject(origin, 'Missing File', [sslKeyFile], null, null, null);
|
|
3795
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
3796
|
+
return callback(null, errorObj);
|
|
3797
|
+
}
|
|
3798
|
+
}
|
|
3799
|
+
}
|
|
3800
|
+
|
|
3801
|
+
if (sslCiphers) {
|
|
3802
|
+
options.ciphers = sslCiphers;
|
|
3803
|
+
}
|
|
3804
|
+
if (secureProtocol) {
|
|
3805
|
+
options.secureProtocol = secureProtocol;
|
|
3806
|
+
}
|
|
3807
|
+
|
|
3808
|
+
log.info(`${origin}: Connector SSL connections enabled`);
|
|
3809
|
+
}
|
|
3810
|
+
|
|
3811
|
+
log.debug(`${origin}: HEALTHCHECK OPTIONS: ${JSON.stringify(options)}`);
|
|
3812
|
+
|
|
3813
|
+
if (payload !== undefined && payload !== null && payload !== '') {
|
|
3814
|
+
log.debug(`${origin}: REQUEST: ${payload}`);
|
|
3815
|
+
|
|
3816
|
+
// save it in memory
|
|
3817
|
+
cacheHPay = payload;
|
|
3818
|
+
}
|
|
3819
|
+
|
|
3820
|
+
// handle ipv6 hostnames
|
|
3821
|
+
if (host.indexOf('[') === 0) {
|
|
3822
|
+
options.host = host;
|
|
3823
|
+
options.hostname = options.hostname.substring(1, options.hostname.length - 1);
|
|
3824
|
+
options.family = 6;
|
|
3825
|
+
}
|
|
3826
|
+
|
|
3827
|
+
const request = {
|
|
3828
|
+
header: options,
|
|
3829
|
+
body: payload
|
|
3830
|
+
};
|
|
3831
|
+
|
|
3832
|
+
// if there is a healthcheck schema, over ride the properties
|
|
3833
|
+
if (healthSchema !== null) {
|
|
3834
|
+
request.origPath = healthSchema.entitypath;
|
|
3835
|
+
}
|
|
3836
|
+
|
|
3837
|
+
// call to make the request
|
|
3838
|
+
return requestAuthenticate(request, healthSchema, null, callProperties, (pres, perror) => {
|
|
3839
|
+
if (perror) {
|
|
3840
|
+
return callback(null, perror);
|
|
3841
|
+
}
|
|
3842
|
+
|
|
3843
|
+
if (pres !== null && pres.code !== undefined
|
|
3844
|
+
&& (Number(pres.code) < 200 || Number(pres.code) > 299)) {
|
|
3845
|
+
let errorObj = null;
|
|
3846
|
+
|
|
3847
|
+
if (pres.code === -2) {
|
|
3848
|
+
errorObj = transUtilInst.formatErrorObject(origin, 'Request Timeout', [pres.code], pres.code, pres, null);
|
|
3849
|
+
} else {
|
|
3850
|
+
errorObj = transUtilInst.formatErrorObject(origin, 'Error On Request', [pres.code], pres.code, pres, null);
|
|
3851
|
+
}
|
|
3852
|
+
|
|
3853
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
3854
|
+
return callback(null, errorObj);
|
|
3855
|
+
}
|
|
3856
|
+
const returnObj = {
|
|
3857
|
+
code: 'AD.200'
|
|
3858
|
+
};
|
|
3859
|
+
|
|
3860
|
+
returnObj.response = pres;
|
|
3861
|
+
return callback(returnObj);
|
|
3862
|
+
});
|
|
3863
|
+
} catch (e) {
|
|
3864
|
+
// handle any exception
|
|
3865
|
+
const errorObj = this.transUtil.checkAndReturn(e, origin, 'Issue with healthcheck');
|
|
3866
|
+
return callback(null, errorObj);
|
|
3867
|
+
}
|
|
3868
|
+
}
|
|
3869
|
+
|
|
3870
|
+
/**
|
|
3871
|
+
* Generic request handler for System requests. Will take in the incoming request which includes
|
|
3872
|
+
* a path, a method and an optional user and password. This information will be used to set up
|
|
3873
|
+
* the correct request to the system. It will then process the request and return the results.
|
|
3874
|
+
*
|
|
3875
|
+
* @function performRequest
|
|
3876
|
+
* @param {Object} incoming - the information for the request. Must have a path and a method.
|
|
3877
|
+
* Can optionally have user and passwd (required)
|
|
3878
|
+
* @param {String} entitySchema - the entity schema (required)
|
|
3879
|
+
* @param {Object} callProperties - properties to override on this call (optional)
|
|
3880
|
+
* @param {Function} callback - a callback function to return the result of the request
|
|
3881
|
+
*/
|
|
3882
|
+
performRequest(incoming, entitySchema, callProperties, callback) {
|
|
3883
|
+
const origin = `${this.myid}-connectorRest-performRequest`;
|
|
3884
|
+
log.trace(origin);
|
|
3885
|
+
const overallTime = process.hrtime();
|
|
3886
|
+
|
|
3887
|
+
try {
|
|
3888
|
+
// verify data was received
|
|
3889
|
+
if (incoming === null) {
|
|
3890
|
+
const errorObj = this.transUtil.formatErrorObject(origin, 'Missing Data', ['request'], null, null, null);
|
|
3891
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
3892
|
+
return callback(null, errorObj);
|
|
3893
|
+
}
|
|
3894
|
+
if (incoming.path === undefined || incoming.path === null || incoming.path === '') {
|
|
3895
|
+
const errorObj = this.transUtil.formatErrorObject(origin, 'Missing Data', ['request path'], null, null, null);
|
|
3896
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
3897
|
+
return callback(null, errorObj);
|
|
3898
|
+
}
|
|
3899
|
+
if (incoming.method === undefined || incoming.method === null || incoming.method === '') {
|
|
3900
|
+
const errorObj = this.transUtil.formatErrorObject(origin, 'Missing Data', ['request method'], null, null, null);
|
|
3901
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
3902
|
+
return callback(null, errorObj);
|
|
3903
|
+
}
|
|
3904
|
+
|
|
3905
|
+
// set up the options for the call
|
|
3906
|
+
let options = {
|
|
3907
|
+
hostname: host,
|
|
3908
|
+
port,
|
|
3909
|
+
path: incoming.path,
|
|
3910
|
+
method: incoming.method,
|
|
3911
|
+
headers: incoming.addlHeaders
|
|
3912
|
+
};
|
|
3913
|
+
|
|
3914
|
+
// passed in properties override defaults
|
|
3915
|
+
if (callProperties && callProperties.host) {
|
|
3916
|
+
options.hostname = callProperties.host;
|
|
3917
|
+
}
|
|
3918
|
+
if (callProperties && callProperties.port) {
|
|
3919
|
+
options.port = callProperties.port;
|
|
3920
|
+
}
|
|
3921
|
+
|
|
3922
|
+
// if ssl enabled add the options for ssl
|
|
3923
|
+
if (callProperties && callProperties.ssl && Object.hasOwnProperty.call(callProperties.ssl, 'enabled')) {
|
|
3924
|
+
if (callProperties.ssl.enabled) {
|
|
3925
|
+
if (callProperties.ssl.accept_invalid_cert) {
|
|
3926
|
+
// if we are accepting invalid certificates (ok for lab not so much production)
|
|
3927
|
+
options.rejectUnauthorized = false;
|
|
3928
|
+
} else {
|
|
3929
|
+
// if we are not accepting invalid certs, need the ca file in the options
|
|
3930
|
+
try {
|
|
3931
|
+
options.rejectUnauthorized = true;
|
|
3932
|
+
options.ca = [fs.readFileSync(callProperties.ssl.ca_file)];
|
|
3933
|
+
} catch (e) {
|
|
3934
|
+
const errorObj = this.transUtil.formatErrorObject(origin, 'Missing File', [callProperties.ssl.ca_file], null, null, null);
|
|
3935
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
3936
|
+
return callback(null, errorObj);
|
|
3937
|
+
}
|
|
3938
|
+
}
|
|
3939
|
+
|
|
3940
|
+
if (callProperties.ssl.ciphers) {
|
|
3941
|
+
options.ciphers = callProperties.ssl.ciphers;
|
|
3942
|
+
}
|
|
3943
|
+
if (callProperties.ssl.secure_protocol) {
|
|
3944
|
+
options.secureProtocol = callProperties.ssl.secure_protocol;
|
|
3945
|
+
}
|
|
3946
|
+
|
|
3947
|
+
log.info(`${origin}: Connector SSL connections enabled`);
|
|
3948
|
+
}
|
|
3949
|
+
} else if (sslEnabled) {
|
|
3950
|
+
if (sslAcceptInvalid) {
|
|
3951
|
+
// if we are accepting invalid certificates (ok for lab not so much production)
|
|
3952
|
+
options.rejectUnauthorized = false;
|
|
3953
|
+
} else {
|
|
3954
|
+
// if we are not accepting invalid certs, need the ca file in the options
|
|
3955
|
+
try {
|
|
3956
|
+
options.rejectUnauthorized = true;
|
|
3957
|
+
options.ca = [fs.readFileSync(sslCAFile)];
|
|
3958
|
+
} catch (e) {
|
|
3959
|
+
const errorObj = this.transUtil.formatErrorObject(origin, 'Missing File', [sslCAFile], null, null, null);
|
|
3960
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
3961
|
+
return callback(null, errorObj);
|
|
3962
|
+
}
|
|
3963
|
+
// if there is a cert file, try to read in a cert file in the options
|
|
3964
|
+
if (sslCertFile) {
|
|
3965
|
+
try {
|
|
3966
|
+
options.cert = [fs.readFileSync(sslCertFile)];
|
|
3967
|
+
} catch (e) {
|
|
3968
|
+
const errorObj = this.transUtil.formatErrorObject(origin, 'Missing File', [sslCertFile], null, null, null);
|
|
3969
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
3970
|
+
return callback(null, errorObj);
|
|
3971
|
+
}
|
|
3972
|
+
}
|
|
3973
|
+
// if there is a key file, try to read in a key file in the options
|
|
3974
|
+
if (sslKeyFile) {
|
|
3975
|
+
try {
|
|
3976
|
+
options.key = [fs.readFileSync(sslKeyFile)];
|
|
3977
|
+
} catch (e) {
|
|
3978
|
+
const errorObj = this.transUtil.formatErrorObject(origin, 'Missing File', [sslKeyFile], null, null, null);
|
|
3979
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
3980
|
+
return callback(null, errorObj);
|
|
3981
|
+
}
|
|
3982
|
+
}
|
|
3983
|
+
}
|
|
3984
|
+
|
|
3985
|
+
if (sslCiphers) {
|
|
3986
|
+
options.ciphers = sslCiphers;
|
|
3987
|
+
}
|
|
3988
|
+
if (secureProtocol) {
|
|
3989
|
+
options.secureProtocol = secureProtocol;
|
|
3990
|
+
}
|
|
3991
|
+
|
|
3992
|
+
log.info(`${origin}: Connector SSL connections enabled`);
|
|
3993
|
+
}
|
|
3994
|
+
|
|
3995
|
+
log.debug(`${origin}: OPTIONS: ${JSON.stringify(options)}`);
|
|
3996
|
+
|
|
3997
|
+
if (incoming.body !== undefined && incoming.body !== null && incoming.body !== '') {
|
|
3998
|
+
log.debug(`${origin}:REQUEST: ${incoming.body}`);
|
|
3999
|
+
}
|
|
4000
|
+
|
|
4001
|
+
const request = {
|
|
4002
|
+
header: options,
|
|
4003
|
+
body: incoming.body,
|
|
4004
|
+
origPath: incoming.origPath
|
|
4005
|
+
};
|
|
4006
|
+
|
|
4007
|
+
// if there is additional authentication data, add it to the request
|
|
4008
|
+
if (incoming.authData) {
|
|
4009
|
+
request.authData = incoming.authData;
|
|
4010
|
+
}
|
|
4011
|
+
|
|
4012
|
+
// clean up used variables from memory setting to undefined minimizes memory
|
|
4013
|
+
options = undefined;
|
|
4014
|
+
|
|
4015
|
+
// if throttling is not enables or we are talking to a different host than the default
|
|
4016
|
+
if (!throttleEnabled || (callProperties && callProperties.host)) {
|
|
4017
|
+
return noQueueRequest(request, callProperties, overallTime, entitySchema, callback);
|
|
4018
|
+
}
|
|
4019
|
+
|
|
4020
|
+
let myRequest = requestId;
|
|
4021
|
+
let myTransTime = new Date().getTime();
|
|
4022
|
+
|
|
4023
|
+
// Lock the global to increment it and reset the local
|
|
4024
|
+
return this.slock.acquire(requestId, (done) => {
|
|
4025
|
+
requestId += 1;
|
|
4026
|
+
myRequest = requestId;
|
|
4027
|
+
myTransTime = new Date().getTime();
|
|
4028
|
+
done(true);
|
|
4029
|
+
}, (ret) => {
|
|
4030
|
+
if (ret) {
|
|
4031
|
+
return queueThrottleRequest(request, callProperties, myRequest, myTransTime, incoming.priority, incoming.event, overallTime, entitySchema, callback);
|
|
4032
|
+
}
|
|
4033
|
+
|
|
4034
|
+
return queueThrottleRequest(request, callProperties, myRequest, myTransTime, incoming.priority, incoming.event, overallTime, entitySchema, callback);
|
|
4035
|
+
});
|
|
4036
|
+
} catch (e) {
|
|
4037
|
+
// handle any exception
|
|
4038
|
+
const errorObj = this.transUtil.checkAndReturn(e, origin, 'Issue performing the request');
|
|
4039
|
+
return callback(null, errorObj);
|
|
4040
|
+
}
|
|
4041
|
+
}
|
|
4042
|
+
|
|
4043
|
+
/**
|
|
4044
|
+
* getQueue is used to get information for all of the requests currently in the queue.
|
|
4045
|
+
*
|
|
4046
|
+
* @function getQueue
|
|
4047
|
+
* @param {Function} callback - a callback function to return the queue
|
|
4048
|
+
*/
|
|
4049
|
+
getQueue(callback) {
|
|
4050
|
+
const origin = `${this.myid}-connectorRest-getQueue`;
|
|
4051
|
+
log.trace(origin);
|
|
4052
|
+
|
|
4053
|
+
try {
|
|
4054
|
+
if (!throttleEnabled) {
|
|
4055
|
+
log.error(`${origin}: Throttling not enabled, no queue`);
|
|
4056
|
+
return [];
|
|
4057
|
+
}
|
|
4058
|
+
|
|
4059
|
+
return throttleEng.getQueue(callback);
|
|
4060
|
+
} catch (e) {
|
|
4061
|
+
// handle any exception
|
|
4062
|
+
const errorObj = this.transUtil.checkAndReturn(e, origin, 'Issue getting queue');
|
|
4063
|
+
return callback(null, errorObj);
|
|
4064
|
+
}
|
|
4065
|
+
}
|
|
4066
|
+
|
|
4067
|
+
/**
|
|
4068
|
+
* getQueue is used to get information for all of the requests currently in the queue.
|
|
4069
|
+
*
|
|
4070
|
+
* @function makeTokenRequest
|
|
4071
|
+
* @param {String} pathForToken - dummy path ad it is a call (required)
|
|
4072
|
+
* @param {String} user - the user logging in (required)
|
|
4073
|
+
* @param {Object} reqBody - extra data to add to the payload (optional)
|
|
4074
|
+
* @param {String} invalidToken - used if we had a bad token (optional)
|
|
4075
|
+
* @param {String} callProperties - properties to override the adapter properties (optional)
|
|
4076
|
+
* @param {Function} callback - a callback function to return the queue
|
|
4077
|
+
*/
|
|
4078
|
+
makeTokenRequest(pathForToken, user, reqBody, invalidToken, callProperties, callback) {
|
|
4079
|
+
return getTokenItem(pathForToken, user, reqBody, invalidToken, callProperties, callback);
|
|
4080
|
+
}
|
|
4081
|
+
}
|
|
4082
|
+
|
|
4083
|
+
module.exports = ConnectorRest;
|