@hpcc-js/comms 3.15.4 → 3.15.6
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/LICENSE +43 -43
- package/README.md +50 -50
- package/dist/browser/index.js +1 -1
- package/dist/browser/index.js.map +1 -1
- package/dist/browser/index.umd.cjs +1 -1
- package/dist/browser/index.umd.cjs.map +1 -1
- package/dist/node/index.cjs +10 -10
- package/dist/node/index.cjs.map +4 -4
- package/dist/node/index.js +9 -9
- package/dist/node/index.js.map +4 -4
- package/package.json +7 -7
- package/src/__package__.ts +3 -3
- package/src/clienttools/eclMeta.ts +506 -506
- package/src/clienttools/eclcc.ts +628 -628
- package/src/connection.ts +288 -288
- package/src/ecl/activity.ts +82 -82
- package/src/ecl/dfuWorkunit.ts +363 -363
- package/src/ecl/graph.ts +196 -196
- package/src/ecl/logicalFile.ts +196 -196
- package/src/ecl/machine.ts +63 -63
- package/src/ecl/query.ts +265 -265
- package/src/ecl/queryGraph.ts +813 -813
- package/src/ecl/resource.ts +39 -39
- package/src/ecl/result.ts +245 -245
- package/src/ecl/scope.ts +188 -188
- package/src/ecl/sourceFile.ts +34 -34
- package/src/ecl/store.ts +154 -154
- package/src/ecl/targetCluster.ts +149 -149
- package/src/ecl/timer.ts +42 -42
- package/src/ecl/topology.ts +131 -131
- package/src/ecl/workunit.ts +1340 -1340
- package/src/ecl/xsdParser.ts +267 -267
- package/src/espConnection.ts +164 -164
- package/src/index.browser.ts +1 -1
- package/src/index.common.ts +40 -40
- package/src/index.node.ts +48 -48
- package/src/pem/trustwave.ts +909 -909
- package/src/services/fileSpray.ts +73 -73
- package/src/services/wsAccess.ts +8 -8
- package/src/services/wsAccount.ts +27 -27
- package/src/services/wsCloud.ts +73 -73
- package/src/services/wsCodesign.ts +18 -18
- package/src/services/wsDFU.ts +34 -34
- package/src/services/wsDFUXRef.ts +121 -121
- package/src/services/wsDali.ts +8 -8
- package/src/services/wsEcl.ts +123 -123
- package/src/services/wsElk.ts +8 -8
- package/src/services/wsLogaccess.ts +270 -270
- package/src/services/wsMachine.ts +89 -89
- package/src/services/wsPackageProcess.ts +8 -8
- package/src/services/wsResources.ts +8 -8
- package/src/services/wsSMC.ts +80 -80
- package/src/services/wsSasha.ts +7 -7
- package/src/services/wsStore.ts +32 -32
- package/src/services/wsTopology.ts +45 -45
- package/src/services/wsWorkunits.ts +151 -151
- package/src/services/wsdl/FileSpray/v1.23/FileSpray.ts +1008 -1008
- package/src/services/wsdl/FileSpray/v1.25/FileSpray.ts +1040 -1040
- package/src/services/wsdl/FileSpray/v1.26/FileSpray.ts +929 -929
- package/src/services/wsdl/FileSpray/v1.27/FileSpray.ts +930 -930
- package/src/services/wsdl/WsCloud/v1/WsCloud.ts +38 -38
- package/src/services/wsdl/WsCloud/v1.02/WsCloud.ts +77 -77
- package/src/services/wsdl/WsDFUXRef/v1.02/WsDFUXRef.ts +224 -224
- package/src/services/wsdl/WsDFUXRef/v1.04/WsDFUXRef.ts +228 -227
- package/src/services/wsdl/WsDali/v1.04/WsDali.ts +216 -216
- package/src/services/wsdl/WsDali/v1.07/WsDali.ts +72 -72
- package/src/services/wsdl/WsDfu/v1.62/WsDfu.ts +1455 -1455
- package/src/services/wsdl/WsDfu/v1.63/WsDfu.ts +1465 -1465
- package/src/services/wsdl/WsDfu/v1.65/WsDfu.ts +1244 -1244
- package/src/services/wsdl/WsDfu/v1.66/WsDfu.ts +1267 -1267
- package/src/services/wsdl/WsDfu/v1.67/WsDfu.ts +1268 -1268
- package/src/services/wsdl/WsDfu/v1.68/WsDfu.ts +1301 -0
- package/src/services/wsdl/WsESDLConfig/v1.5/WsESDLConfig.ts +366 -0
- package/src/services/wsdl/WsFileIO/v1.01/WsFileIO.ts +104 -104
- package/src/services/wsdl/WsPackageProcess/v1.04/WsPackageProcess.ts +519 -519
- package/src/services/wsdl/WsPackageProcess/v1.07/WsPackageProcess.ts +500 -500
- package/src/services/wsdl/WsPackageProcess/v1.08/WsPackageProcess.ts +503 -0
- package/src/services/wsdl/WsResources/v1.01/WsResources.ts +119 -119
- package/src/services/wsdl/WsSMC/v1.24/WsSMC.ts +665 -665
- package/src/services/wsdl/WsSMC/v1.27/WsSMC.ts +591 -591
- package/src/services/wsdl/WsSMC/v1.28/WsSMC.ts +645 -645
- package/src/services/wsdl/WsSMC/v1.29/WsSMC.ts +660 -660
- package/src/services/wsdl/WsSasha/v1.01/WsSasha.ts +18 -18
- package/src/services/wsdl/WsTopology/v1.31/WsTopology.ts +856 -856
- package/src/services/wsdl/WsTopology/v1.32/WsTopology.ts +786 -786
- package/src/services/wsdl/WsTopology/v1.33/WsTopology.ts +835 -835
- package/src/services/wsdl/WsWorkunits/v1.88/WsWorkunits.ts +2944 -2944
- package/src/services/wsdl/WsWorkunits/v1.94/WsWorkunits.ts +3072 -3072
- package/src/services/wsdl/WsWorkunits/v1.95/WsWorkunits.ts +3073 -3073
- package/src/services/wsdl/WsWorkunits/v1.97/WsWorkunits.ts +3134 -3134
- package/src/services/wsdl/WsWorkunits/v1.98/WsWorkunits.ts +3182 -3182
- package/src/services/wsdl/WsWorkunits/v1.99/WsWorkunits.ts +3162 -3162
- package/src/services/wsdl/WsWorkunits/v2/WsWorkunits.ts +3153 -3153
- package/src/services/wsdl/WsWorkunits/v2.02/WsWorkunits.ts +3162 -3162
- package/src/services/wsdl/WsWorkunits/v2.03/WsWorkunits.ts +3164 -3164
- package/src/services/wsdl/WsWorkunits/v2.04/WsWorkunits.ts +3171 -3171
- package/src/services/wsdl/WsWorkunits/v2.05/WsWorkunits.ts +3177 -0
- package/src/services/wsdl/ws_access/v1.16/ws_access.ts +1086 -1086
- package/src/services/wsdl/ws_access/v1.17/ws_access.ts +1023 -1023
- package/src/services/wsdl/ws_account/v1.05/ws_account.ts +111 -111
- package/src/services/wsdl/ws_account/v1.06/ws_account.ts +109 -109
- package/src/services/wsdl/ws_account/v1.07/ws_account.ts +114 -114
- package/src/services/wsdl/ws_codesign/v1.1/ws_codesign.ts +95 -95
- package/src/services/wsdl/ws_elk/v1/ws_elk.ts +47 -47
- package/src/services/wsdl/ws_logaccess/v1/ws_logaccess.ts +83 -83
- package/src/services/wsdl/ws_logaccess/v1.02/ws_logaccess.ts +161 -161
- package/src/services/wsdl/ws_logaccess/v1.03/ws_logaccess.ts +190 -190
- package/src/services/wsdl/ws_logaccess/v1.04/ws_logaccess.ts +215 -215
- package/src/services/wsdl/ws_logaccess/v1.05/ws_logaccess.ts +219 -219
- package/src/services/wsdl/ws_logaccess/v1.08/ws_logaccess.ts +267 -267
- package/src/services/wsdl/ws_machine/v1.17/ws_machine.ts +567 -567
- package/src/services/wsdl/ws_machine/v1.18/ws_machine.ts +497 -497
- package/src/services/wsdl/ws_machine/v1.19/ws_machine.ts +497 -497
- package/src/services/wsdl/wsstore/v1.02/wsstore.ts +239 -239
- package/types/services/wsPackageProcess.d.ts +1 -1
- package/types/services/wsWorkunits.d.ts +1 -1
- package/types/services/wsdl/FileSpray/v1.27/FileSpray.d.ts +506 -506
- package/types/services/wsdl/WsCloud/v1.02/WsCloud.d.ts +18 -18
- package/types/services/wsdl/WsDFUXRef/v1.04/WsDFUXRef.d.ts +58 -57
- package/types/services/wsdl/WsDali/v1.07/WsDali.d.ts +42 -42
- package/types/services/wsdl/WsPackageProcess/{v1.07 → v1.08}/WsPackageProcess.d.ts +121 -118
- package/types/services/wsdl/WsSasha/v1.01/WsSasha.d.ts +13 -13
- package/types/services/wsdl/WsTopology/v1.33/WsTopology.d.ts +360 -360
- package/types/services/wsdl/WsWorkunits/v2.05/WsWorkunits.d.ts +2571 -0
- package/types/services/wsdl/ws_access/v1.17/ws_access.d.ts +268 -268
- package/types/services/wsdl/ws_account/v1.07/ws_account.d.ts +34 -34
- package/types/services/wsdl/ws_codesign/v1.1/ws_codesign.d.ts +22 -22
- package/types/services/wsdl/ws_elk/v1/ws_elk.d.ts +12 -12
- package/types/services/wsdl/wsstore/v1.02/wsstore.d.ts +61 -61
- package/types/services/wsdl/WsWorkunits/v2.04/WsWorkunits.d.ts +0 -2565
package/src/connection.ts
CHANGED
|
@@ -1,288 +1,288 @@
|
|
|
1
|
-
import { join, promiseTimeout, root, scopedLogger, utf8ToBase64 } from "@hpcc-js/util";
|
|
2
|
-
|
|
3
|
-
const logger = scopedLogger("comms/connection.ts");
|
|
4
|
-
|
|
5
|
-
export type RequestType = "post" | "get" | "jsonp";
|
|
6
|
-
export type ResponseType = "json" | "text";
|
|
7
|
-
|
|
8
|
-
export type IOptionsSend = (options: IOptions, action: string, request: any, responseType: ResponseType, defaultSend: SendFunc, header?: any) => Promise<any>;
|
|
9
|
-
export interface IOptions {
|
|
10
|
-
baseUrl: string;
|
|
11
|
-
type?: RequestType;
|
|
12
|
-
userID?: string;
|
|
13
|
-
password?: string;
|
|
14
|
-
rejectUnauthorized?: boolean;
|
|
15
|
-
timeoutSecs?: number;
|
|
16
|
-
hookSend?: IOptionsSend;
|
|
17
|
-
encodeRequest?: boolean; // defaults to true
|
|
18
|
-
}
|
|
19
|
-
export function instanceOfIOptions(object: any): object is IOptions {
|
|
20
|
-
return "baseUrl" in object;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const DefaultOptions: IOptions = {
|
|
24
|
-
type: "post",
|
|
25
|
-
baseUrl: "",
|
|
26
|
-
userID: "",
|
|
27
|
-
password: "",
|
|
28
|
-
rejectUnauthorized: true,
|
|
29
|
-
timeoutSecs: 60
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
export interface IConnection {
|
|
33
|
-
opts(_: Partial<IOptions>): this;
|
|
34
|
-
opts(): IOptions;
|
|
35
|
-
baseUrl: string;
|
|
36
|
-
|
|
37
|
-
send(action: string, request: any, responseType?: ResponseType): Promise<any>;
|
|
38
|
-
clone(): IConnection;
|
|
39
|
-
}
|
|
40
|
-
export function instanceOfIConnection(object: any): object is IConnection {
|
|
41
|
-
return typeof object.opts === "function" &&
|
|
42
|
-
typeof object.send === "function" &&
|
|
43
|
-
typeof object.clone === "function";
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// comms ---
|
|
47
|
-
|
|
48
|
-
function encode(uriComponent: string | number | boolean, encodeRequest: boolean): string {
|
|
49
|
-
return (encodeRequest === undefined || encodeRequest === true) ? encodeURIComponent(uriComponent) : "" + uriComponent;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export function serializeRequest(obj: any, encodeRequest: boolean = true, prefix: string = ""): string {
|
|
53
|
-
if (prefix) {
|
|
54
|
-
prefix += ".";
|
|
55
|
-
}
|
|
56
|
-
if (typeof obj !== "object") {
|
|
57
|
-
return encode(obj, encodeRequest);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const str: string[] = [];
|
|
61
|
-
for (const key in obj) {
|
|
62
|
-
if (obj.hasOwnProperty(key)) {
|
|
63
|
-
if (obj[key] instanceof Array) {
|
|
64
|
-
// Specific to ESP - but no REST standard exists...
|
|
65
|
-
let includeItemCount = false;
|
|
66
|
-
obj[key].forEach((row: any, i: number) => {
|
|
67
|
-
if (typeof row === "object") {
|
|
68
|
-
includeItemCount = true;
|
|
69
|
-
str.push(serializeRequest(row, encodeRequest, prefix + encode(`${key}.${i}`, encodeRequest)));
|
|
70
|
-
} else {
|
|
71
|
-
str.push(prefix + encode(`${key}_i${i}`, encodeRequest) + "=" + serializeRequest(row, encodeRequest));
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
if (includeItemCount) {
|
|
75
|
-
str.push(prefix + encode(`${key}.itemcount`, encodeRequest) + "=" + obj[key].length);
|
|
76
|
-
}
|
|
77
|
-
} else if (typeof obj[key] === "object") {
|
|
78
|
-
if (obj[key] && obj[key]["Item"] instanceof Array) { // Specific to ws_machine.GetTargetClusterInfo?
|
|
79
|
-
str.push(serializeRequest(obj[key]["Item"], encodeRequest, prefix + encode(key, encodeRequest)));
|
|
80
|
-
str.push(prefix + encode(`${key}.itemcount`, encodeRequest) + "=" + obj[key]["Item"].length);
|
|
81
|
-
} else {
|
|
82
|
-
str.push(serializeRequest(obj[key], encodeRequest, prefix + encode(key, encodeRequest)));
|
|
83
|
-
}
|
|
84
|
-
} else if (obj[key] !== undefined) {
|
|
85
|
-
str.push(prefix + encode(key, encodeRequest) + "=" + encode(obj[key], encodeRequest));
|
|
86
|
-
} else {
|
|
87
|
-
str.push(prefix + encode(key, encodeRequest));
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
return str.join("&");
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
export function deserializeResponse(body: string) {
|
|
95
|
-
return JSON.parse(body);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
export function jsonp(opts: IOptions, action: string, request: any = {}, responseType: ResponseType = "json", header?: any): Promise<any> {
|
|
99
|
-
if (header) {
|
|
100
|
-
console.warn("Header attributes ignored for JSONP connections");
|
|
101
|
-
}
|
|
102
|
-
return new Promise<any>((resolve, reject) => {
|
|
103
|
-
let respondedTimeout = opts.timeoutSecs! * 1000;
|
|
104
|
-
const respondedTick = 5000;
|
|
105
|
-
const callbackName = "jsonp_callback_" + Math.round(Math.random() * 999999);
|
|
106
|
-
(window as any)[callbackName] = function (response: any) {
|
|
107
|
-
respondedTimeout = 0;
|
|
108
|
-
doCallback();
|
|
109
|
-
resolve(responseType === "json" && typeof response === "string" ? deserializeResponse(response) : response);
|
|
110
|
-
};
|
|
111
|
-
const script = document.createElement("script");
|
|
112
|
-
let url = join(opts.baseUrl, action);
|
|
113
|
-
url += url.indexOf("?") >= 0 ? "&" : "?";
|
|
114
|
-
script.src = url + "jsonp=" + callbackName + "&" + serializeRequest(request, opts.encodeRequest);
|
|
115
|
-
document.body.appendChild(script);
|
|
116
|
-
const progress = setInterval(function () {
|
|
117
|
-
if (respondedTimeout <= 0) {
|
|
118
|
-
clearInterval(progress);
|
|
119
|
-
} else {
|
|
120
|
-
respondedTimeout -= respondedTick;
|
|
121
|
-
if (respondedTimeout <= 0) {
|
|
122
|
-
clearInterval(progress);
|
|
123
|
-
logger.error("Request timeout: " + script.src);
|
|
124
|
-
doCallback();
|
|
125
|
-
reject(Error("Request timeout: " + script.src));
|
|
126
|
-
} else {
|
|
127
|
-
logger.debug("Request pending (" + respondedTimeout / 1000 + " sec): " + script.src);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
}, respondedTick);
|
|
131
|
-
|
|
132
|
-
function doCallback() {
|
|
133
|
-
delete (window as any)[callbackName];
|
|
134
|
-
document.body.removeChild(script);
|
|
135
|
-
}
|
|
136
|
-
});
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
function authHeader(opts: IOptions): object {
|
|
140
|
-
return opts.userID ? { Authorization: `Basic ${utf8ToBase64(`${opts.userID}:${opts.password}`)}` } : {};
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// _omitMap is a workaround for older HPCC-Platform instances without credentials ---
|
|
144
|
-
const _omitMap: { [baseUrl: string]: boolean } = {};
|
|
145
|
-
function doFetch(opts: IOptions, action: string, requestInit: RequestInit, headersInit: HeadersInit, responseType: string) {
|
|
146
|
-
headersInit = {
|
|
147
|
-
...authHeader(opts),
|
|
148
|
-
...headersInit
|
|
149
|
-
};
|
|
150
|
-
|
|
151
|
-
requestInit = {
|
|
152
|
-
credentials: _omitMap[opts.baseUrl] ? "omit" : "include",
|
|
153
|
-
...requestInit,
|
|
154
|
-
headers: headersInit
|
|
155
|
-
};
|
|
156
|
-
|
|
157
|
-
if (opts.baseUrl.indexOf("https:") === 0) {
|
|
158
|
-
// NodeJS / node-fetch only ---
|
|
159
|
-
if (opts.rejectUnauthorized === false && root.__hpcc_rejectUnauthorizedAgent) {
|
|
160
|
-
requestInit["dispatcher"] = root.__hpcc_rejectUnauthorizedAgent;
|
|
161
|
-
} else if (root.__hpcc_trustwaveAgent) {
|
|
162
|
-
requestInit["agent"] = root.__hpcc_trustwaveAgent;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
function handleResponse(response: Response): Promise<any> {
|
|
167
|
-
if (response.ok) {
|
|
168
|
-
return responseType === "json" ? response.json() : response.text();
|
|
169
|
-
}
|
|
170
|
-
throw new Error(response.statusText);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
const fetchOverride = root.__hpcc_undiciFetch ?? fetch;
|
|
174
|
-
return promiseTimeout(opts.timeoutSecs! * 1000, fetchOverride(join(opts.baseUrl, action), requestInit)
|
|
175
|
-
.then(handleResponse)
|
|
176
|
-
.catch(e => {
|
|
177
|
-
// Try again with the opposite credentials mode ---
|
|
178
|
-
requestInit.credentials = !_omitMap[opts.baseUrl] ? "omit" : "include";
|
|
179
|
-
return fetchOverride(join(opts.baseUrl, action), requestInit)
|
|
180
|
-
.then(handleResponse)
|
|
181
|
-
.then(responseBody => {
|
|
182
|
-
_omitMap[opts.baseUrl] = !_omitMap[opts.baseUrl]; // The "opposite" credentials mode is known to work ---
|
|
183
|
-
return responseBody;
|
|
184
|
-
});
|
|
185
|
-
})
|
|
186
|
-
);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
export function post(opts: IOptions, action: string, request: any, responseType: ResponseType = "json", header?: any): Promise<any> {
|
|
190
|
-
if (request.upload_) {
|
|
191
|
-
delete request.upload_;
|
|
192
|
-
action += "?upload_";
|
|
193
|
-
}
|
|
194
|
-
let abortSignal;
|
|
195
|
-
if (request.abortSignal_) {
|
|
196
|
-
abortSignal = request.abortSignal_;
|
|
197
|
-
delete request.abortSignal_;
|
|
198
|
-
}
|
|
199
|
-
return doFetch(opts, action, {
|
|
200
|
-
method: "post",
|
|
201
|
-
body: serializeRequest(request, opts.encodeRequest),
|
|
202
|
-
signal: abortSignal
|
|
203
|
-
}, {
|
|
204
|
-
"Content-Type": "application/x-www-form-urlencoded",
|
|
205
|
-
...header
|
|
206
|
-
} as any, responseType);
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
export function get(opts: IOptions, action: string, request: any, responseType: ResponseType = "json", header?: any): Promise<any> {
|
|
210
|
-
let abortSignal;
|
|
211
|
-
if (request.abortSignal_) {
|
|
212
|
-
abortSignal = request.abortSignal_;
|
|
213
|
-
delete request.abortSignal_;
|
|
214
|
-
}
|
|
215
|
-
return doFetch(opts, `${action}?${serializeRequest(request, opts.encodeRequest)}`, {
|
|
216
|
-
method: "get",
|
|
217
|
-
signal: abortSignal
|
|
218
|
-
}, {
|
|
219
|
-
...header
|
|
220
|
-
} as any, responseType);
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
export type SendFunc = (opts: IOptions, action: string, request: any, responseType: ResponseType, header?: any) => Promise<any>;
|
|
224
|
-
export function send(opts: IOptions, action: string, request: any, responseType: ResponseType = "json", header?: any): Promise<any> {
|
|
225
|
-
let retVal: Promise<any>;
|
|
226
|
-
switch (opts.type) {
|
|
227
|
-
case "jsonp":
|
|
228
|
-
retVal = jsonp(opts, action, request, responseType, header);
|
|
229
|
-
break;
|
|
230
|
-
case "get":
|
|
231
|
-
retVal = get(opts, action, request, responseType, header);
|
|
232
|
-
break;
|
|
233
|
-
case "post":
|
|
234
|
-
default:
|
|
235
|
-
retVal = post(opts, action, request, responseType, header);
|
|
236
|
-
break;
|
|
237
|
-
}
|
|
238
|
-
return retVal;
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
let hookedSend: SendFunc = send;
|
|
242
|
-
export function hookSend(newSend?: SendFunc): SendFunc {
|
|
243
|
-
const retVal = hookedSend;
|
|
244
|
-
if (newSend) {
|
|
245
|
-
hookedSend = newSend;
|
|
246
|
-
}
|
|
247
|
-
return retVal;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
export class Connection implements IConnection {
|
|
251
|
-
protected _opts: IOptions;
|
|
252
|
-
get baseUrl() { return this._opts.baseUrl; }
|
|
253
|
-
|
|
254
|
-
constructor(opts: IOptions) {
|
|
255
|
-
this.opts(opts);
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
// IConnection ---
|
|
259
|
-
opts(_: Partial<IOptions>): this;
|
|
260
|
-
opts(): IOptions;
|
|
261
|
-
opts(_?: Partial<IOptions>): this | IOptions {
|
|
262
|
-
if (arguments.length === 0) return this._opts;
|
|
263
|
-
this._opts = { ...DefaultOptions, ..._ };
|
|
264
|
-
return this;
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
send(action: string, request: any, responseType: ResponseType = "json", header?: any): Promise<any> {
|
|
268
|
-
if (this._opts.hookSend) {
|
|
269
|
-
return this._opts.hookSend(this._opts, action, request, responseType, hookedSend, header);
|
|
270
|
-
}
|
|
271
|
-
return hookedSend(this._opts, action, request, responseType, header);
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
clone() {
|
|
275
|
-
return new Connection(this.opts());
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
export type IConnectionFactory = (opts: IOptions) => IConnection;
|
|
280
|
-
export let createConnection: IConnectionFactory = function (opts: IOptions): IConnection {
|
|
281
|
-
return new Connection(opts);
|
|
282
|
-
};
|
|
283
|
-
|
|
284
|
-
export function setTransportFactory(newFunc: IConnectionFactory): IConnectionFactory {
|
|
285
|
-
const retVal = createConnection;
|
|
286
|
-
createConnection = newFunc;
|
|
287
|
-
return retVal;
|
|
288
|
-
}
|
|
1
|
+
import { join, promiseTimeout, root, scopedLogger, utf8ToBase64 } from "@hpcc-js/util";
|
|
2
|
+
|
|
3
|
+
const logger = scopedLogger("comms/connection.ts");
|
|
4
|
+
|
|
5
|
+
export type RequestType = "post" | "get" | "jsonp";
|
|
6
|
+
export type ResponseType = "json" | "text";
|
|
7
|
+
|
|
8
|
+
export type IOptionsSend = (options: IOptions, action: string, request: any, responseType: ResponseType, defaultSend: SendFunc, header?: any) => Promise<any>;
|
|
9
|
+
export interface IOptions {
|
|
10
|
+
baseUrl: string;
|
|
11
|
+
type?: RequestType;
|
|
12
|
+
userID?: string;
|
|
13
|
+
password?: string;
|
|
14
|
+
rejectUnauthorized?: boolean;
|
|
15
|
+
timeoutSecs?: number;
|
|
16
|
+
hookSend?: IOptionsSend;
|
|
17
|
+
encodeRequest?: boolean; // defaults to true
|
|
18
|
+
}
|
|
19
|
+
export function instanceOfIOptions(object: any): object is IOptions {
|
|
20
|
+
return "baseUrl" in object;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const DefaultOptions: IOptions = {
|
|
24
|
+
type: "post",
|
|
25
|
+
baseUrl: "",
|
|
26
|
+
userID: "",
|
|
27
|
+
password: "",
|
|
28
|
+
rejectUnauthorized: true,
|
|
29
|
+
timeoutSecs: 60
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export interface IConnection {
|
|
33
|
+
opts(_: Partial<IOptions>): this;
|
|
34
|
+
opts(): IOptions;
|
|
35
|
+
baseUrl: string;
|
|
36
|
+
|
|
37
|
+
send(action: string, request: any, responseType?: ResponseType): Promise<any>;
|
|
38
|
+
clone(): IConnection;
|
|
39
|
+
}
|
|
40
|
+
export function instanceOfIConnection(object: any): object is IConnection {
|
|
41
|
+
return typeof object.opts === "function" &&
|
|
42
|
+
typeof object.send === "function" &&
|
|
43
|
+
typeof object.clone === "function";
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// comms ---
|
|
47
|
+
|
|
48
|
+
function encode(uriComponent: string | number | boolean, encodeRequest: boolean): string {
|
|
49
|
+
return (encodeRequest === undefined || encodeRequest === true) ? encodeURIComponent(uriComponent) : "" + uriComponent;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function serializeRequest(obj: any, encodeRequest: boolean = true, prefix: string = ""): string {
|
|
53
|
+
if (prefix) {
|
|
54
|
+
prefix += ".";
|
|
55
|
+
}
|
|
56
|
+
if (typeof obj !== "object") {
|
|
57
|
+
return encode(obj, encodeRequest);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const str: string[] = [];
|
|
61
|
+
for (const key in obj) {
|
|
62
|
+
if (obj.hasOwnProperty(key)) {
|
|
63
|
+
if (obj[key] instanceof Array) {
|
|
64
|
+
// Specific to ESP - but no REST standard exists...
|
|
65
|
+
let includeItemCount = false;
|
|
66
|
+
obj[key].forEach((row: any, i: number) => {
|
|
67
|
+
if (typeof row === "object") {
|
|
68
|
+
includeItemCount = true;
|
|
69
|
+
str.push(serializeRequest(row, encodeRequest, prefix + encode(`${key}.${i}`, encodeRequest)));
|
|
70
|
+
} else {
|
|
71
|
+
str.push(prefix + encode(`${key}_i${i}`, encodeRequest) + "=" + serializeRequest(row, encodeRequest));
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
if (includeItemCount) {
|
|
75
|
+
str.push(prefix + encode(`${key}.itemcount`, encodeRequest) + "=" + obj[key].length);
|
|
76
|
+
}
|
|
77
|
+
} else if (typeof obj[key] === "object") {
|
|
78
|
+
if (obj[key] && obj[key]["Item"] instanceof Array) { // Specific to ws_machine.GetTargetClusterInfo?
|
|
79
|
+
str.push(serializeRequest(obj[key]["Item"], encodeRequest, prefix + encode(key, encodeRequest)));
|
|
80
|
+
str.push(prefix + encode(`${key}.itemcount`, encodeRequest) + "=" + obj[key]["Item"].length);
|
|
81
|
+
} else {
|
|
82
|
+
str.push(serializeRequest(obj[key], encodeRequest, prefix + encode(key, encodeRequest)));
|
|
83
|
+
}
|
|
84
|
+
} else if (obj[key] !== undefined) {
|
|
85
|
+
str.push(prefix + encode(key, encodeRequest) + "=" + encode(obj[key], encodeRequest));
|
|
86
|
+
} else {
|
|
87
|
+
str.push(prefix + encode(key, encodeRequest));
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return str.join("&");
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function deserializeResponse(body: string) {
|
|
95
|
+
return JSON.parse(body);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function jsonp(opts: IOptions, action: string, request: any = {}, responseType: ResponseType = "json", header?: any): Promise<any> {
|
|
99
|
+
if (header) {
|
|
100
|
+
console.warn("Header attributes ignored for JSONP connections");
|
|
101
|
+
}
|
|
102
|
+
return new Promise<any>((resolve, reject) => {
|
|
103
|
+
let respondedTimeout = opts.timeoutSecs! * 1000;
|
|
104
|
+
const respondedTick = 5000;
|
|
105
|
+
const callbackName = "jsonp_callback_" + Math.round(Math.random() * 999999);
|
|
106
|
+
(window as any)[callbackName] = function (response: any) {
|
|
107
|
+
respondedTimeout = 0;
|
|
108
|
+
doCallback();
|
|
109
|
+
resolve(responseType === "json" && typeof response === "string" ? deserializeResponse(response) : response);
|
|
110
|
+
};
|
|
111
|
+
const script = document.createElement("script");
|
|
112
|
+
let url = join(opts.baseUrl, action);
|
|
113
|
+
url += url.indexOf("?") >= 0 ? "&" : "?";
|
|
114
|
+
script.src = url + "jsonp=" + callbackName + "&" + serializeRequest(request, opts.encodeRequest);
|
|
115
|
+
document.body.appendChild(script);
|
|
116
|
+
const progress = setInterval(function () {
|
|
117
|
+
if (respondedTimeout <= 0) {
|
|
118
|
+
clearInterval(progress);
|
|
119
|
+
} else {
|
|
120
|
+
respondedTimeout -= respondedTick;
|
|
121
|
+
if (respondedTimeout <= 0) {
|
|
122
|
+
clearInterval(progress);
|
|
123
|
+
logger.error("Request timeout: " + script.src);
|
|
124
|
+
doCallback();
|
|
125
|
+
reject(Error("Request timeout: " + script.src));
|
|
126
|
+
} else {
|
|
127
|
+
logger.debug("Request pending (" + respondedTimeout / 1000 + " sec): " + script.src);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}, respondedTick);
|
|
131
|
+
|
|
132
|
+
function doCallback() {
|
|
133
|
+
delete (window as any)[callbackName];
|
|
134
|
+
document.body.removeChild(script);
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function authHeader(opts: IOptions): object {
|
|
140
|
+
return opts.userID ? { Authorization: `Basic ${utf8ToBase64(`${opts.userID}:${opts.password}`)}` } : {};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// _omitMap is a workaround for older HPCC-Platform instances without credentials ---
|
|
144
|
+
const _omitMap: { [baseUrl: string]: boolean } = {};
|
|
145
|
+
function doFetch(opts: IOptions, action: string, requestInit: RequestInit, headersInit: HeadersInit, responseType: string) {
|
|
146
|
+
headersInit = {
|
|
147
|
+
...authHeader(opts),
|
|
148
|
+
...headersInit
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
requestInit = {
|
|
152
|
+
credentials: _omitMap[opts.baseUrl] ? "omit" : "include",
|
|
153
|
+
...requestInit,
|
|
154
|
+
headers: headersInit
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
if (opts.baseUrl.indexOf("https:") === 0) {
|
|
158
|
+
// NodeJS / node-fetch only ---
|
|
159
|
+
if (opts.rejectUnauthorized === false && root.__hpcc_rejectUnauthorizedAgent) {
|
|
160
|
+
requestInit["dispatcher"] = root.__hpcc_rejectUnauthorizedAgent;
|
|
161
|
+
} else if (root.__hpcc_trustwaveAgent) {
|
|
162
|
+
requestInit["agent"] = root.__hpcc_trustwaveAgent;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function handleResponse(response: Response): Promise<any> {
|
|
167
|
+
if (response.ok) {
|
|
168
|
+
return responseType === "json" ? response.json() : response.text();
|
|
169
|
+
}
|
|
170
|
+
throw new Error(response.statusText);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const fetchOverride = root.__hpcc_undiciFetch ?? fetch;
|
|
174
|
+
return promiseTimeout(opts.timeoutSecs! * 1000, fetchOverride(join(opts.baseUrl, action), requestInit)
|
|
175
|
+
.then(handleResponse)
|
|
176
|
+
.catch(e => {
|
|
177
|
+
// Try again with the opposite credentials mode ---
|
|
178
|
+
requestInit.credentials = !_omitMap[opts.baseUrl] ? "omit" : "include";
|
|
179
|
+
return fetchOverride(join(opts.baseUrl, action), requestInit)
|
|
180
|
+
.then(handleResponse)
|
|
181
|
+
.then(responseBody => {
|
|
182
|
+
_omitMap[opts.baseUrl] = !_omitMap[opts.baseUrl]; // The "opposite" credentials mode is known to work ---
|
|
183
|
+
return responseBody;
|
|
184
|
+
});
|
|
185
|
+
})
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export function post(opts: IOptions, action: string, request: any, responseType: ResponseType = "json", header?: any): Promise<any> {
|
|
190
|
+
if (request.upload_) {
|
|
191
|
+
delete request.upload_;
|
|
192
|
+
action += "?upload_";
|
|
193
|
+
}
|
|
194
|
+
let abortSignal;
|
|
195
|
+
if (request.abortSignal_) {
|
|
196
|
+
abortSignal = request.abortSignal_;
|
|
197
|
+
delete request.abortSignal_;
|
|
198
|
+
}
|
|
199
|
+
return doFetch(opts, action, {
|
|
200
|
+
method: "post",
|
|
201
|
+
body: serializeRequest(request, opts.encodeRequest),
|
|
202
|
+
signal: abortSignal
|
|
203
|
+
}, {
|
|
204
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
205
|
+
...header
|
|
206
|
+
} as any, responseType);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export function get(opts: IOptions, action: string, request: any, responseType: ResponseType = "json", header?: any): Promise<any> {
|
|
210
|
+
let abortSignal;
|
|
211
|
+
if (request.abortSignal_) {
|
|
212
|
+
abortSignal = request.abortSignal_;
|
|
213
|
+
delete request.abortSignal_;
|
|
214
|
+
}
|
|
215
|
+
return doFetch(opts, `${action}?${serializeRequest(request, opts.encodeRequest)}`, {
|
|
216
|
+
method: "get",
|
|
217
|
+
signal: abortSignal
|
|
218
|
+
}, {
|
|
219
|
+
...header
|
|
220
|
+
} as any, responseType);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export type SendFunc = (opts: IOptions, action: string, request: any, responseType: ResponseType, header?: any) => Promise<any>;
|
|
224
|
+
export function send(opts: IOptions, action: string, request: any, responseType: ResponseType = "json", header?: any): Promise<any> {
|
|
225
|
+
let retVal: Promise<any>;
|
|
226
|
+
switch (opts.type) {
|
|
227
|
+
case "jsonp":
|
|
228
|
+
retVal = jsonp(opts, action, request, responseType, header);
|
|
229
|
+
break;
|
|
230
|
+
case "get":
|
|
231
|
+
retVal = get(opts, action, request, responseType, header);
|
|
232
|
+
break;
|
|
233
|
+
case "post":
|
|
234
|
+
default:
|
|
235
|
+
retVal = post(opts, action, request, responseType, header);
|
|
236
|
+
break;
|
|
237
|
+
}
|
|
238
|
+
return retVal;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
let hookedSend: SendFunc = send;
|
|
242
|
+
export function hookSend(newSend?: SendFunc): SendFunc {
|
|
243
|
+
const retVal = hookedSend;
|
|
244
|
+
if (newSend) {
|
|
245
|
+
hookedSend = newSend;
|
|
246
|
+
}
|
|
247
|
+
return retVal;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export class Connection implements IConnection {
|
|
251
|
+
protected _opts: IOptions;
|
|
252
|
+
get baseUrl() { return this._opts.baseUrl; }
|
|
253
|
+
|
|
254
|
+
constructor(opts: IOptions) {
|
|
255
|
+
this.opts(opts);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// IConnection ---
|
|
259
|
+
opts(_: Partial<IOptions>): this;
|
|
260
|
+
opts(): IOptions;
|
|
261
|
+
opts(_?: Partial<IOptions>): this | IOptions {
|
|
262
|
+
if (arguments.length === 0) return this._opts;
|
|
263
|
+
this._opts = { ...DefaultOptions, ..._ };
|
|
264
|
+
return this;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
send(action: string, request: any, responseType: ResponseType = "json", header?: any): Promise<any> {
|
|
268
|
+
if (this._opts.hookSend) {
|
|
269
|
+
return this._opts.hookSend(this._opts, action, request, responseType, hookedSend, header);
|
|
270
|
+
}
|
|
271
|
+
return hookedSend(this._opts, action, request, responseType, header);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
clone() {
|
|
275
|
+
return new Connection(this.opts());
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
export type IConnectionFactory = (opts: IOptions) => IConnection;
|
|
280
|
+
export let createConnection: IConnectionFactory = function (opts: IOptions): IConnection {
|
|
281
|
+
return new Connection(opts);
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
export function setTransportFactory(newFunc: IConnectionFactory): IConnectionFactory {
|
|
285
|
+
const retVal = createConnection;
|
|
286
|
+
createConnection = newFunc;
|
|
287
|
+
return retVal;
|
|
288
|
+
}
|