@hpcc-js/comms 3.22.0 → 3.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +66 -0
- package/dist/browser/index.js +349 -32
- package/dist/browser/index.js.map +1 -1
- package/dist/browser/index.umd.cjs +2 -2
- package/dist/browser/index.umd.cjs.map +1 -1
- package/dist/node/index.cjs +14 -14
- package/dist/node/index.cjs.map +1 -7
- package/dist/node/index.js +7151 -923
- package/dist/node/index.js.map +1 -7
- package/package.json +11 -10
- package/src/connection.ts +11 -2
- package/src/ecl/dfuWorkunit.ts +3 -0
- package/src/ecl/sql2.ts +376 -0
- package/src/index.common.ts +2 -0
- package/src/services/fileSpray.ts +1 -1
- package/src/services/wsCloud.ts +4 -2
- package/src/services/wsEcl.ts +14 -2
- package/src/services/wsLogaccess.ts +13 -12
- package/src/services/wsSQL2.ts +8 -0
- package/src/services/wsdl/FileSpray/current/FileSpray.ts +959 -0
- package/src/services/wsdl/FileSpray/v1.30/FileSpray.ts +959 -0
- package/src/services/wsdl/WsSQL2/current/WsSQL2.ts +222 -0
- package/src/services/wsdl/WsSQL2/v1.00/WsSQL2.ts +222 -0
- package/types/ecl/dfuWorkunit.d.ts +3 -0
- package/types/ecl/sql2.d.ts +74 -0
- package/types/index.common.d.ts +2 -0
- package/types/services/fileSpray.d.ts +1 -1
- package/types/services/wsSQL2.d.ts +4 -0
- package/types/services/wsdl/FileSpray/{v1.29 → v1.30}/FileSpray.d.ts +10 -0
- package/types/services/wsdl/WsSQL2/v1.00/WsSQL2.d.ts +164 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hpcc-js/comms",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.23.0",
|
|
4
4
|
"description": "hpcc-js - Communications",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/node/index.cjs",
|
|
@@ -35,9 +35,9 @@
|
|
|
35
35
|
],
|
|
36
36
|
"scripts": {
|
|
37
37
|
"clean": "rimraf --glob lib* types types-3.4 dist *.tsbuildinfo .turbo",
|
|
38
|
-
"bundle-node": "
|
|
38
|
+
"bundle-node": "vite build --config vite.node.config.ts",
|
|
39
39
|
"bundle-browser": "vite build",
|
|
40
|
-
"bundle": "run-
|
|
40
|
+
"bundle": "run-p bundle-browser bundle-node",
|
|
41
41
|
"bundle-watch": "vite build --watch",
|
|
42
42
|
"bundle-serve": "vite",
|
|
43
43
|
"gen-types": "tsc --project tsconfig.json",
|
|
@@ -69,6 +69,7 @@
|
|
|
69
69
|
"wsdl-packageprocess": "node ./lib-esm/index.js -k --url=http://localhost:8010/WsPackageProcess?wsdl --outDir=./src/services/wsdl",
|
|
70
70
|
"wsdl-sasha": "node ./lib-esm/index.js -k --url=http://localhost:8010/WsSasha?wsdl --outDir=./src/services/wsdl",
|
|
71
71
|
"wsdl-smc": "node ./lib-esm/index.js -k --url=http://localhost:8010/WsSMC?wsdl --outDir=./src/services/wsdl",
|
|
72
|
+
"wsdl-sql2": "node ./lib-esm/index.js -k --url=http://localhost:8510/WsSQL2?wsdl --outDir=./src/services/wsdl",
|
|
72
73
|
"wsdl-resources": "node ./lib-esm/index.js -k --url=http://localhost:8010/WsResources?wsdl --outDir=./src/services/wsdl",
|
|
73
74
|
"wsdl-store": "node ./lib-esm/index.js -k --url=http://localhost:8010/WsStore?wsdl --outDir=./src/services/wsdl",
|
|
74
75
|
"wsdl-topology": "node ./lib-esm/index.js -k --url=http://localhost:8010/WsTopology?wsdl --outDir=./src/services/wsdl",
|
|
@@ -76,14 +77,14 @@
|
|
|
76
77
|
"wsdl-all": "npm-run-all --aggregate-output -c --serial build --parallel wsdl-*"
|
|
77
78
|
},
|
|
78
79
|
"dependencies": {
|
|
79
|
-
"@hpcc-js/util": "^3.8.
|
|
80
|
+
"@hpcc-js/util": "^3.8.2",
|
|
80
81
|
"@xmldom/xmldom": "0.9.12",
|
|
81
|
-
"undici": "8.
|
|
82
|
+
"undici": "8.12.3"
|
|
82
83
|
},
|
|
83
84
|
"devDependencies": {
|
|
84
|
-
"@hpcc-js/ddl-shim": "^3.5.
|
|
85
|
-
"@hpcc-js/vite-plugins": "^1.5.
|
|
86
|
-
"@kubernetes/client-node": "2.0.
|
|
85
|
+
"@hpcc-js/ddl-shim": "^3.5.5",
|
|
86
|
+
"@hpcc-js/vite-plugins": "^1.5.2",
|
|
87
|
+
"@kubernetes/client-node": "2.0.1",
|
|
87
88
|
"@types/d3-time-format": "4.0.3",
|
|
88
89
|
"@types/node": "^26",
|
|
89
90
|
"@types/xmldom": "0.1.34",
|
|
@@ -91,7 +92,7 @@
|
|
|
91
92
|
"d3-format": "3.1.2",
|
|
92
93
|
"d3-time-format": "4.1.0",
|
|
93
94
|
"data-uri-to-buffer": "8.0.0",
|
|
94
|
-
"soap": "1.
|
|
95
|
+
"soap": "1.14.0",
|
|
95
96
|
"typescript-formatter": "^7.2.2"
|
|
96
97
|
},
|
|
97
98
|
"repository": {
|
|
@@ -111,5 +112,5 @@
|
|
|
111
112
|
"esp",
|
|
112
113
|
"HPCC-Platform"
|
|
113
114
|
],
|
|
114
|
-
"gitHead": "
|
|
115
|
+
"gitHead": "8c3937e3502133c20cf6e5dc3a1c3faef890103f"
|
|
115
116
|
}
|
package/src/connection.ts
CHANGED
|
@@ -92,7 +92,12 @@ export function serializeRequest(obj: any, encodeRequest: boolean = true, prefix
|
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
export function deserializeResponse(body: string) {
|
|
95
|
-
|
|
95
|
+
try {
|
|
96
|
+
return JSON.parse(body);
|
|
97
|
+
} catch (error) {
|
|
98
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
99
|
+
throw Object.assign(new Error(`Error parsing JSON response '${message}'`), { cause: error });
|
|
100
|
+
}
|
|
96
101
|
}
|
|
97
102
|
|
|
98
103
|
export function jsonp(opts: IOptions, action: string, request: any = {}, responseType: ResponseType = "json", header?: any): Promise<any> {
|
|
@@ -106,7 +111,11 @@ export function jsonp(opts: IOptions, action: string, request: any = {}, respons
|
|
|
106
111
|
(window as any)[callbackName] = function (response: any) {
|
|
107
112
|
respondedTimeout = 0;
|
|
108
113
|
doCallback();
|
|
109
|
-
|
|
114
|
+
try {
|
|
115
|
+
resolve(responseType === "json" && typeof response === "string" ? deserializeResponse(response) : response);
|
|
116
|
+
} catch (error) {
|
|
117
|
+
reject(error);
|
|
118
|
+
}
|
|
110
119
|
};
|
|
111
120
|
const script = document.createElement("script");
|
|
112
121
|
let url = join(opts.baseUrl, action);
|
package/src/ecl/dfuWorkunit.ts
CHANGED
|
@@ -98,6 +98,9 @@ export class DFUWorkunit extends StateObject<UDFUWorkunitState, IDFUWorkunitStat
|
|
|
98
98
|
get FileAccessCost(): number { return this.get("FileAccessCost"); }
|
|
99
99
|
get KbPerSecAve(): number { return this.get("KbPerSecAve"); }
|
|
100
100
|
get KbPerSec(): number { return this.get("KbPerSec"); }
|
|
101
|
+
get SubTaskCount(): number { return this.get("SubTaskCount"); }
|
|
102
|
+
get SubTasksFinished(): number { return this.get("SubTasksFinished"); }
|
|
103
|
+
get SubTasksFailed(): number { return this.get("SubTasksFailed"); }
|
|
101
104
|
|
|
102
105
|
// Factories ---
|
|
103
106
|
static create(optsConnection: IOptions | IConnection, dfuServerQueue: string): Promise<DFUWorkunit> {
|
package/src/ecl/sql2.ts
ADDED
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
import { IConnection, IOptions } from "../connection.ts";
|
|
2
|
+
import { SQL2Service, WsSQL2 } from "../services/wsSQL2.ts";
|
|
3
|
+
|
|
4
|
+
export type SQLValue = string | number | boolean | null | SQLValue[] | { [key: string]: SQLValue };
|
|
5
|
+
export type SQLRow = Record<string, SQLValue>;
|
|
6
|
+
|
|
7
|
+
export class SQLCleanupError extends Error {
|
|
8
|
+
constructor(message: string, public readonly cause: unknown, public readonly cleanupErrors: unknown[]) {
|
|
9
|
+
super(message);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function checkExceptions(response: { Exceptions?: WsSQL2.Exceptions }): void {
|
|
14
|
+
if (response.Exceptions) {
|
|
15
|
+
throw new Error(response.Exceptions.Exception?.map(e => e.Message ?? e.Code).join("; ") || "WsSQL2 request failed");
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function rowsFrom(response: { Rows?: string }): SQLValue[][] {
|
|
20
|
+
if (typeof response.Rows !== "string") {
|
|
21
|
+
throw new Error("WsSQL2 response is missing Rows");
|
|
22
|
+
}
|
|
23
|
+
let rows: unknown;
|
|
24
|
+
try {
|
|
25
|
+
rows = JSON.parse(response.Rows);
|
|
26
|
+
} catch (error) {
|
|
27
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
28
|
+
throw Object.assign(new Error(`Error parsing WsSQL2 Rows JSON '${message}'`), { cause: error });
|
|
29
|
+
}
|
|
30
|
+
if (!Array.isArray(rows) || !rows.every(row => Array.isArray(row))) {
|
|
31
|
+
throw new Error("WsSQL2 Rows must be an array of arrays");
|
|
32
|
+
}
|
|
33
|
+
return rows as SQLValue[][];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export class SQLConnection {
|
|
37
|
+
protected readonly service: SQL2Service;
|
|
38
|
+
protected readonly statements = new Set<SQLStatement>();
|
|
39
|
+
protected closed = false;
|
|
40
|
+
protected closing?: Promise<void>;
|
|
41
|
+
|
|
42
|
+
constructor(options: IOptions | IConnection | SQL2Service) {
|
|
43
|
+
this.service = options instanceof SQL2Service ? options : new SQL2Service(options);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
createStatement(): SQLStatement {
|
|
47
|
+
this.checkOpen();
|
|
48
|
+
const statement = new SQLStatement(this.service, () => this.statements.delete(statement));
|
|
49
|
+
this.statements.add(statement);
|
|
50
|
+
return statement;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async queryRows(sql: string, fetchSize?: number): Promise<SQLRow[]> {
|
|
54
|
+
const statement = this.createStatement();
|
|
55
|
+
const rows: SQLRow[] = [];
|
|
56
|
+
try {
|
|
57
|
+
if (fetchSize !== undefined) statement.setFetchSize(fetchSize);
|
|
58
|
+
const result = await statement.executeQuery(sql);
|
|
59
|
+
while (await result.next()) rows.push(result.getRow());
|
|
60
|
+
} catch (error) {
|
|
61
|
+
try {
|
|
62
|
+
await statement.close();
|
|
63
|
+
} catch (closeError) {
|
|
64
|
+
throw new SQLCleanupError("Failed to query rows and close statement", error, [closeError]);
|
|
65
|
+
}
|
|
66
|
+
throw error;
|
|
67
|
+
}
|
|
68
|
+
await statement.close();
|
|
69
|
+
return rows;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async explainQuery(sql: string): Promise<WsSQL2.ExplainQueryResponse> {
|
|
73
|
+
this.checkOpen();
|
|
74
|
+
if (!sql.trim()) throw new Error("SQL text is required");
|
|
75
|
+
const response = await this.service.ExplainQuery({ SqlText: sql });
|
|
76
|
+
checkExceptions(response);
|
|
77
|
+
return response;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async logicalFileMeta(logicalName: string): Promise<WsSQL2.LogicalFileMeta | undefined> {
|
|
81
|
+
this.checkOpen();
|
|
82
|
+
if (!logicalName.trim()) throw new Error("Logical file name is required");
|
|
83
|
+
const response = await this.service.LogicalFileMeta({ LogicalName: logicalName });
|
|
84
|
+
checkExceptions(response);
|
|
85
|
+
return response.LogicalFileMeta;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async ping(): Promise<boolean> {
|
|
89
|
+
this.checkOpen();
|
|
90
|
+
await this.service.Ping({});
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async splitQuery(sql: string): Promise<string[]> {
|
|
95
|
+
this.checkOpen();
|
|
96
|
+
if (!sql.trim()) return [];
|
|
97
|
+
const response = await this.service.SplitQuery({ SqlText: sql });
|
|
98
|
+
checkExceptions(response);
|
|
99
|
+
const statements = response.Statements?.Statement ?? [];
|
|
100
|
+
if (!Array.isArray(statements) || !statements.every(statement => typeof statement === "string")) {
|
|
101
|
+
throw new Error("WsSQL2 response has invalid Statements");
|
|
102
|
+
}
|
|
103
|
+
return statements;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
close(): Promise<void> {
|
|
107
|
+
if (this.closing) return this.closing;
|
|
108
|
+
this.closed = true;
|
|
109
|
+
this.closing = (async () => {
|
|
110
|
+
const results = await Promise.allSettled([...this.statements].map(statement => statement.close()));
|
|
111
|
+
const errors = results.filter((result): result is PromiseRejectedResult => result.status === "rejected").map(result => result.reason);
|
|
112
|
+
if (errors.length) throw new SQLCleanupError("Failed to close SQL statements", errors[0], errors);
|
|
113
|
+
})();
|
|
114
|
+
void this.closing.then(
|
|
115
|
+
() => { this.closing = undefined; },
|
|
116
|
+
() => { this.closing = undefined; }
|
|
117
|
+
);
|
|
118
|
+
return this.closing;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
[Symbol.asyncDispose](): Promise<void> {
|
|
122
|
+
return this.close();
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
checkOpen(): void {
|
|
126
|
+
if (this.closed) throw new Error("SQL connection is closed");
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export class SQLStatement {
|
|
131
|
+
protected result?: SQLResultSet;
|
|
132
|
+
protected closed = false;
|
|
133
|
+
protected fetchSize = 0;
|
|
134
|
+
protected executing?: Promise<SQLResultSet>;
|
|
135
|
+
protected closing?: Promise<void>;
|
|
136
|
+
|
|
137
|
+
constructor(protected readonly service: SQL2Service, protected readonly onClose: () => void) {
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
setFetchSize(size: number): void {
|
|
141
|
+
this.checkOpen();
|
|
142
|
+
if (!Number.isSafeInteger(size) || size < 0) throw new RangeError("Fetch size must be a non-negative integer");
|
|
143
|
+
this.fetchSize = size;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
cancel(): Promise<void> {
|
|
147
|
+
this.checkOpen();
|
|
148
|
+
if (this.executing) return this.executing.then(result => result.cancel());
|
|
149
|
+
return this.result?.cancel() ?? Promise.resolve();
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
executeQuery(sql: string): Promise<SQLResultSet> {
|
|
153
|
+
this.checkOpen();
|
|
154
|
+
if (this.executing) throw new Error("SQL statement is already executing");
|
|
155
|
+
if (!sql.trim()) throw new Error("SQL text is required");
|
|
156
|
+
return this.execute(sql);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
protected async execute(sql: string): Promise<SQLResultSet> {
|
|
160
|
+
const operation = this.runQuery(sql);
|
|
161
|
+
this.executing = operation;
|
|
162
|
+
try {
|
|
163
|
+
return await operation;
|
|
164
|
+
} finally {
|
|
165
|
+
this.executing = undefined;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
protected async runQuery(sql: string): Promise<SQLResultSet> {
|
|
170
|
+
await this.result?.close();
|
|
171
|
+
this.result = undefined;
|
|
172
|
+
if (this.closed) throw new Error("SQL statement is closed");
|
|
173
|
+
const response = await this.service.ExecuteQuery({
|
|
174
|
+
SqlText: sql, IncludeResultSetMetadata: true
|
|
175
|
+
});
|
|
176
|
+
const columns = response.ResultSetMetadata?.Column;
|
|
177
|
+
const result = response.CursorId ? new SQLResultSet(this.service, columns ?? [], response.CursorId, this.fetchSize) : undefined;
|
|
178
|
+
this.result = result;
|
|
179
|
+
try {
|
|
180
|
+
checkExceptions(response);
|
|
181
|
+
if (!response.CursorId) throw new Error("WsSQL2 response is missing CursorId");
|
|
182
|
+
if (!columns) throw new Error("WsSQL2 response is missing result-set metadata");
|
|
183
|
+
if (this.closed) throw new Error("SQL statement is closed");
|
|
184
|
+
return result;
|
|
185
|
+
} catch (error) {
|
|
186
|
+
if (result) {
|
|
187
|
+
try {
|
|
188
|
+
await result.close();
|
|
189
|
+
} catch (closeError) {
|
|
190
|
+
throw new SQLCleanupError("Failed to parse result and close cursor", error, [closeError]);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
throw error;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
close(): Promise<void> {
|
|
198
|
+
if (this.closing) return this.closing;
|
|
199
|
+
this.closed = true;
|
|
200
|
+
this.closing = (async () => {
|
|
201
|
+
if (this.executing) {
|
|
202
|
+
try {
|
|
203
|
+
await this.executing;
|
|
204
|
+
} catch {
|
|
205
|
+
// The query caller receives its own error; close still releases its cursor.
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
await this.result?.close();
|
|
209
|
+
this.onClose();
|
|
210
|
+
})();
|
|
211
|
+
void this.closing.then(
|
|
212
|
+
() => { this.closing = undefined; },
|
|
213
|
+
() => { this.closing = undefined; }
|
|
214
|
+
);
|
|
215
|
+
return this.closing;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
[Symbol.asyncDispose](): Promise<void> {
|
|
219
|
+
return this.close();
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
protected checkOpen(): void {
|
|
223
|
+
if (this.closed) throw new Error("SQL statement is closed");
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export class SQLResultSet {
|
|
228
|
+
protected index = -1;
|
|
229
|
+
protected row?: SQLValue[];
|
|
230
|
+
protected wasNullValue = false;
|
|
231
|
+
protected closed = false;
|
|
232
|
+
protected advancing?: Promise<boolean>;
|
|
233
|
+
protected closing?: Promise<void>;
|
|
234
|
+
|
|
235
|
+
constructor(
|
|
236
|
+
protected readonly service: SQL2Service,
|
|
237
|
+
protected readonly columns: WsSQL2.Column[],
|
|
238
|
+
protected cursor: string | undefined,
|
|
239
|
+
protected readonly fetchSize: number
|
|
240
|
+
) {
|
|
241
|
+
}
|
|
242
|
+
protected rows: SQLValue[][] = [];
|
|
243
|
+
protected complete = false;
|
|
244
|
+
|
|
245
|
+
getMetaData(): readonly WsSQL2.Column[] {
|
|
246
|
+
this.checkOpen();
|
|
247
|
+
return this.columns;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
findColumn(label: string): number {
|
|
251
|
+
this.checkOpen();
|
|
252
|
+
const index = this.columns.findIndex(column => column.ColumnLabel?.toLowerCase() === label.toLowerCase());
|
|
253
|
+
if (index < 0) throw new Error(`Unknown SQL column: ${label}`);
|
|
254
|
+
return index + 1;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
next(): Promise<boolean> {
|
|
258
|
+
this.checkOpen();
|
|
259
|
+
if (this.advancing) throw new Error("SQL result set is already fetching");
|
|
260
|
+
return this.advance();
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
private async advance(): Promise<boolean> {
|
|
264
|
+
const operation = this.advancePage();
|
|
265
|
+
this.advancing = operation;
|
|
266
|
+
try {
|
|
267
|
+
return await operation;
|
|
268
|
+
} finally {
|
|
269
|
+
this.advancing = undefined;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
private async advancePage(): Promise<boolean> {
|
|
274
|
+
this.row = undefined;
|
|
275
|
+
try {
|
|
276
|
+
while (++this.index >= this.rows.length) {
|
|
277
|
+
this.checkOpen();
|
|
278
|
+
if (this.complete) return false;
|
|
279
|
+
const response = await this.service.FetchResultSet({ CursorId: this.cursor, FetchSize: this.fetchSize });
|
|
280
|
+
checkExceptions(response);
|
|
281
|
+
if (typeof response.Complete !== "boolean") throw new Error("WsSQL2 fetch response is missing Complete");
|
|
282
|
+
const rows = rowsFrom(response);
|
|
283
|
+
this.checkOpen();
|
|
284
|
+
this.complete = response.Complete;
|
|
285
|
+
if (this.complete) this.cursor = undefined;
|
|
286
|
+
this.rows = rows;
|
|
287
|
+
this.index = -1;
|
|
288
|
+
}
|
|
289
|
+
this.checkOpen();
|
|
290
|
+
const row = this.rows[this.index];
|
|
291
|
+
if (row.length !== this.columns.length) throw new Error("WsSQL2 row width does not match result metadata");
|
|
292
|
+
this.row = row;
|
|
293
|
+
return true;
|
|
294
|
+
} catch (error) {
|
|
295
|
+
this.closed = true;
|
|
296
|
+
try {
|
|
297
|
+
await this.close();
|
|
298
|
+
} catch (closeError) {
|
|
299
|
+
throw new SQLCleanupError("Failed to read result and close cursor", error, [closeError]);
|
|
300
|
+
}
|
|
301
|
+
throw error;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
getObject(column: number | string): SQLValue {
|
|
306
|
+
this.checkOpen();
|
|
307
|
+
if (!this.row) throw new Error("SQL result set is not positioned on a row");
|
|
308
|
+
const index = typeof column === "string" ? this.findColumn(column) : column;
|
|
309
|
+
if (!Number.isInteger(index) || index < 1 || index > this.columns.length) {
|
|
310
|
+
throw new RangeError(`SQL column index out of range: ${index}`);
|
|
311
|
+
}
|
|
312
|
+
const value = this.row[index - 1];
|
|
313
|
+
this.wasNullValue = value === null;
|
|
314
|
+
return value;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
getString(column: number | string): string | null {
|
|
318
|
+
const value = this.getObject(column);
|
|
319
|
+
return value === null ? null : String(value);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
getRow(): SQLRow {
|
|
323
|
+
this.checkOpen();
|
|
324
|
+
const row = this.row;
|
|
325
|
+
if (!row) throw new Error("SQL result set is not positioned on a row");
|
|
326
|
+
const result: SQLRow = Object.create(null);
|
|
327
|
+
const labels = new Set<string>();
|
|
328
|
+
this.columns.forEach((column, index) => {
|
|
329
|
+
const label = column.ColumnLabel;
|
|
330
|
+
if (!label) throw new Error("WsSQL2 column is missing a label");
|
|
331
|
+
if (labels.has(label.toLowerCase())) throw new Error(`Duplicate SQL column label: ${label}`);
|
|
332
|
+
labels.add(label.toLowerCase());
|
|
333
|
+
result[label] = row[index];
|
|
334
|
+
});
|
|
335
|
+
return result;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
wasNull(): boolean {
|
|
339
|
+
this.checkOpen();
|
|
340
|
+
return this.wasNullValue;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
close(): Promise<void> {
|
|
344
|
+
if (this.closing) return this.closing;
|
|
345
|
+
this.closed = true;
|
|
346
|
+
this.closing = (async () => {
|
|
347
|
+
await this.closeCursor();
|
|
348
|
+
})();
|
|
349
|
+
void this.closing.then(
|
|
350
|
+
() => { this.closing = undefined; },
|
|
351
|
+
() => { this.closing = undefined; }
|
|
352
|
+
);
|
|
353
|
+
return this.closing;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
[Symbol.asyncDispose](): Promise<void> {
|
|
357
|
+
return this.close();
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
cancel(): Promise<void> {
|
|
361
|
+
return this.close();
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
protected async closeCursor(): Promise<void> {
|
|
365
|
+
if (this.cursor) {
|
|
366
|
+
const cursor = this.cursor;
|
|
367
|
+
const response = await this.service.CloseResultSet({ CursorId: cursor });
|
|
368
|
+
checkExceptions(response);
|
|
369
|
+
this.cursor = undefined;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
protected checkOpen(): void {
|
|
374
|
+
if (this.closed) throw new Error("SQL result set is closed");
|
|
375
|
+
}
|
|
376
|
+
}
|
package/src/index.common.ts
CHANGED
|
@@ -15,6 +15,7 @@ export * from "./services/wsPackageProcess.ts";
|
|
|
15
15
|
export * from "./services/wsResources.ts";
|
|
16
16
|
export * from "./services/wsSasha.ts";
|
|
17
17
|
export * from "./services/wsSMC.ts";
|
|
18
|
+
export * from "./services/wsSQL2.ts";
|
|
18
19
|
export * from "./services/wsStore.ts";
|
|
19
20
|
export * from "./services/wsTopology.ts";
|
|
20
21
|
export * from "./services/wsWorkunits.ts";
|
|
@@ -27,6 +28,7 @@ export * from "./ecl/query.ts";
|
|
|
27
28
|
export * from "./ecl/resource.ts";
|
|
28
29
|
export * from "./ecl/result.ts";
|
|
29
30
|
export * from "./ecl/scope.ts";
|
|
31
|
+
export * from "./ecl/sql2.ts";
|
|
30
32
|
export * from "./ecl/sourceFile.ts";
|
|
31
33
|
export * from "./ecl/store.ts";
|
|
32
34
|
export * from "./ecl/timer.ts";
|
package/src/services/wsCloud.ts
CHANGED
|
@@ -65,8 +65,10 @@ export class CloudService extends CloudServiceBase {
|
|
|
65
65
|
const obj = typeof response.Result === "string" ? JSON.parse(response.Result) : response.Result;
|
|
66
66
|
return mapPods(obj?.items ?? []);
|
|
67
67
|
} catch (error) {
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
69
|
+
const parseError = Object.assign(new Error(`Error parsing V1Pods json '${message}'`), { cause: error });
|
|
70
|
+
logger.error(parseError.message);
|
|
71
|
+
throw parseError;
|
|
70
72
|
}
|
|
71
73
|
});
|
|
72
74
|
}
|
package/src/services/wsEcl.ts
CHANGED
|
@@ -82,7 +82,13 @@ export class EclService extends Service {
|
|
|
82
82
|
requestJson(querySet: string, queryId: string): Promise<IWsEclRequest> {
|
|
83
83
|
// http://192.168.3.22:8002/WsEcl/example/request/query/roxie/peopleaccounts/json?display
|
|
84
84
|
return this._connection.send(`example/request/query/${querySet}/${queryId}/json`, {}, "text").then(response => {
|
|
85
|
-
|
|
85
|
+
let requestSchema;
|
|
86
|
+
try {
|
|
87
|
+
requestSchema = JSON.parse(response);
|
|
88
|
+
} catch (error) {
|
|
89
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
90
|
+
throw Object.assign(new Error(`Error parsing WsEcl request JSON '${message}'`), { cause: error });
|
|
91
|
+
}
|
|
86
92
|
for (const key in requestSchema) {
|
|
87
93
|
return requestSchema[key];
|
|
88
94
|
}
|
|
@@ -93,7 +99,13 @@ export class EclService extends Service {
|
|
|
93
99
|
responseJson(querySet: string, queryId: string): Promise<IWsEclResponse> {
|
|
94
100
|
// http://192.168.3.22:8002/WsEcl/example/response/query/roxie/peopleaccounts/json?display
|
|
95
101
|
return this._connection.send(`example/response/query/${querySet}/${queryId}/json`, {}, "text").then(response => {
|
|
96
|
-
|
|
102
|
+
let responseSchema;
|
|
103
|
+
try {
|
|
104
|
+
responseSchema = JSON.parse(response);
|
|
105
|
+
} catch (error) {
|
|
106
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
107
|
+
throw Object.assign(new Error(`Error parsing WsEcl response JSON '${message}'`), { cause: error });
|
|
108
|
+
}
|
|
97
109
|
for (const key in responseSchema) {
|
|
98
110
|
return responseSchema[key].Results;
|
|
99
111
|
}
|
|
@@ -249,21 +249,22 @@ export class LogaccessService extends LogaccessServiceBase {
|
|
|
249
249
|
};
|
|
250
250
|
|
|
251
251
|
return this.GetLogs(getLogsRequest).then(response => {
|
|
252
|
+
let logLines;
|
|
252
253
|
try {
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
total: response.TotalLogLinesAvailable ?? 10000
|
|
260
|
-
};
|
|
261
|
-
} catch (e: any) {
|
|
262
|
-
logger.error(e.message ?? e);
|
|
254
|
+
logLines = JSON.parse(response.LogLines);
|
|
255
|
+
} catch (error) {
|
|
256
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
257
|
+
const parseError = Object.assign(new Error(`Error parsing log lines json '${message}'`), { cause: error });
|
|
258
|
+
logger.error(parseError.message);
|
|
259
|
+
throw parseError;
|
|
263
260
|
}
|
|
261
|
+
|
|
262
|
+
const lines = knownLogManagerTypes.has(logInfo.RemoteLogManagerType)
|
|
263
|
+
? (logLines.lines?.map((line: any) => this.convertLogLine(columnMap, line)) ?? [])
|
|
264
|
+
: (logger.warning(`Unknown RemoteLogManagerType: ${logInfo.RemoteLogManagerType}`), []);
|
|
264
265
|
return {
|
|
265
|
-
lines
|
|
266
|
-
total:
|
|
266
|
+
lines,
|
|
267
|
+
total: response.TotalLogLinesAvailable ?? 10000
|
|
267
268
|
};
|
|
268
269
|
});
|
|
269
270
|
}
|