@hpcc-js/comms 2.102.1 → 2.102.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/LICENSE +43 -43
  2. package/README.md +50 -50
  3. package/dist/index.es6.js.map +1 -1
  4. package/dist/index.js.map +1 -1
  5. package/dist/index.min.js.map +1 -1
  6. package/dist/index.node.js.map +1 -1
  7. package/dist/index.node.min.js.map +1 -1
  8. package/package.json +5 -5
  9. package/src/__package__.ts +3 -3
  10. package/src/__tests__/dfuXRef.ts +22 -22
  11. package/src/__tests__/https.ts +69 -69
  12. package/src/__tests__/workunit.ts +35 -35
  13. package/src/clienttools/eclMeta.ts +506 -506
  14. package/src/clienttools/eclcc.ts +628 -628
  15. package/src/connection.ts +295 -295
  16. package/src/ecl/activity.ts +82 -82
  17. package/src/ecl/dfuWorkunit.ts +363 -363
  18. package/src/ecl/graph.ts +196 -196
  19. package/src/ecl/logicalFile.ts +195 -195
  20. package/src/ecl/machine.ts +63 -63
  21. package/src/ecl/query.ts +252 -252
  22. package/src/ecl/queryGraph.ts +813 -813
  23. package/src/ecl/resource.ts +39 -39
  24. package/src/ecl/result.ts +236 -236
  25. package/src/ecl/scope.ts +192 -192
  26. package/src/ecl/sourceFile.ts +34 -34
  27. package/src/ecl/store.ts +154 -154
  28. package/src/ecl/targetCluster.ts +149 -149
  29. package/src/ecl/timer.ts +42 -42
  30. package/src/ecl/topology.ts +131 -131
  31. package/src/ecl/workunit.ts +1314 -1314
  32. package/src/ecl/xsdParser.ts +268 -268
  33. package/src/espConnection.ts +172 -172
  34. package/src/index-common.ts +41 -41
  35. package/src/index.node.ts +68 -68
  36. package/src/index.ts +3 -3
  37. package/src/pem/trustwave.ts +909 -909
  38. package/src/services/fileSpray.ts +48 -48
  39. package/src/services/wsAccess.ts +8 -8
  40. package/src/services/wsAccount.ts +27 -27
  41. package/src/services/wsCloud.ts +73 -73
  42. package/src/services/wsCodesign.ts +94 -94
  43. package/src/services/wsDFU.ts +34 -34
  44. package/src/services/wsDFUXRef.ts +308 -308
  45. package/src/services/wsDali.ts +40 -40
  46. package/src/services/wsEcl.ts +123 -123
  47. package/src/services/wsElk.ts +8 -8
  48. package/src/services/wsLogaccess.ts +263 -263
  49. package/src/services/wsMachine.ts +89 -89
  50. package/src/services/wsPackageProcess.ts +8 -8
  51. package/src/services/wsResources.ts +8 -8
  52. package/src/services/wsSMC.ts +24 -24
  53. package/src/services/wsSasha.ts +7 -7
  54. package/src/services/wsStore.ts +230 -230
  55. package/src/services/wsTopology.ts +45 -45
  56. package/src/services/wsWorkunits.ts +160 -160
  57. package/src/services/wsdl/FileSpray/v1.23/FileSpray.ts +1008 -1008
  58. package/src/services/wsdl/FileSpray/v1.25/FileSpray.ts +1040 -1040
  59. package/src/services/wsdl/FileSpray/v1.26/FileSpray.ts +929 -929
  60. package/src/services/wsdl/WsCloud/v1/WsCloud.ts +38 -38
  61. package/src/services/wsdl/WsCloud/v1.02/WsCloud.ts +77 -77
  62. package/src/services/wsdl/WsDFUXRef/v1.02/WsDFUXRef.ts +224 -224
  63. package/src/services/wsdl/WsDali/v1.04/WsDali.ts +216 -216
  64. package/src/services/wsdl/WsDfu/v1.62/WsDfu.ts +1455 -1455
  65. package/src/services/wsdl/WsDfu/v1.63/WsDfu.ts +1465 -1465
  66. package/src/services/wsdl/WsDfu/v1.65/WsDfu.ts +1244 -1244
  67. package/src/services/wsdl/WsFileIO/v1.01/WsFileIO.ts +107 -107
  68. package/src/services/wsdl/WsPackageProcess/v1.04/WsPackageProcess.ts +519 -519
  69. package/src/services/wsdl/WsResources/v1.01/WsResources.ts +119 -119
  70. package/src/services/wsdl/WsSMC/v1.24/WsSMC.ts +665 -665
  71. package/src/services/wsdl/WsSMC/v1.27/WsSMC.ts +670 -670
  72. package/src/services/wsdl/WsTopology/v1.31/WsTopology.ts +856 -856
  73. package/src/services/wsdl/WsTopology/v1.32/WsTopology.ts +885 -885
  74. package/src/services/wsdl/WsWorkunits/v1.88/WsWorkunits.ts +2944 -2944
  75. package/src/services/wsdl/WsWorkunits/v1.94/WsWorkunits.ts +3072 -3072
  76. package/src/services/wsdl/WsWorkunits/v1.95/WsWorkunits.ts +3073 -3073
  77. package/src/services/wsdl/WsWorkunits/v1.97/WsWorkunits.ts +3134 -3134
  78. package/src/services/wsdl/WsWorkunits/v1.98/WsWorkunits.ts +3182 -3182
  79. package/src/services/wsdl/WsWorkunits/v1.99/WsWorkunits.ts +3162 -3162
  80. package/src/services/wsdl/WsWorkunits/v2/WsWorkunits.ts +3153 -3153
  81. package/src/services/wsdl/WsWorkunits/v2.02/WsWorkunits.ts +3157 -3157
  82. package/src/services/wsdl/ws_access/v1.16/ws_access.ts +1086 -1086
  83. package/src/services/wsdl/ws_access/v1.17/ws_access.ts +1023 -1023
  84. package/src/services/wsdl/ws_account/v1.05/ws_account.ts +111 -111
  85. package/src/services/wsdl/ws_account/v1.06/ws_account.ts +109 -109
  86. package/src/services/wsdl/ws_codesign/v1.1/ws_codesign.ts +100 -100
  87. package/src/services/wsdl/ws_elk/v1/ws_elk.ts +47 -47
  88. package/src/services/wsdl/ws_logaccess/v1/ws_logaccess.ts +83 -83
  89. package/src/services/wsdl/ws_logaccess/v1.02/ws_logaccess.ts +161 -161
  90. package/src/services/wsdl/ws_logaccess/v1.03/ws_logaccess.ts +190 -190
  91. package/src/services/wsdl/ws_logaccess/v1.04/ws_logaccess.ts +215 -215
  92. package/src/services/wsdl/ws_logaccess/v1.05/ws_logaccess.ts +219 -219
  93. package/src/services/wsdl/ws_logaccess/v1.08/ws_logaccess.ts +267 -267
  94. package/src/services/wsdl/ws_machine/v1.17/ws_machine.ts +567 -567
  95. package/src/services/wsdl/wsstore/v1.02/wsstore.ts +250 -250
@@ -1,1314 +1,1314 @@
1
- import { Cache, deepMixinT, IEvent, RecursivePartial, scopedLogger, StateCallback, StateEvents, StateObject, StatePropCallback, StringAnyMap, XMLNode } from "@hpcc-js/util";
2
- import { format as d3Format } from "d3-format";
3
- import { utcFormat, utcParse } from "d3-time-format";
4
- import { IConnection, IOptions } from "../connection";
5
- import { ESPExceptions } from "../espConnection";
6
- import { WsSMC } from "../services/wsSMC";
7
- import * as WsTopology from "../services/wsTopology";
8
- import { WsWorkunits, WUStateID, WorkunitsService, WorkunitsServiceEx, WUUpdate } from "../services/wsWorkunits";
9
- import { createGraph, createXGMMLGraph, ECLGraph, GraphCache, ScopeGraph, XGMMLGraph, XGMMLVertex } from "./graph";
10
- import { Resource } from "./resource";
11
- import { Result, ResultCache } from "./result";
12
- import { BaseScope, Scope } from "./scope";
13
- import { SourceFile } from "./sourceFile";
14
- import { Timer } from "./timer";
15
-
16
- const formatter = utcFormat("%Y-%m-%dT%H:%M:%S.%LZ");
17
- const parser = utcParse("%Y-%m-%dT%H:%M:%S.%LZ");
18
- const d3FormatNum = d3Format(",");
19
- function formatNum(num: number | string): string {
20
- if (num && !isNaN(+num)) {
21
- return d3FormatNum(+num);
22
- }
23
- return num as string;
24
- }
25
-
26
- function safeDelete(obj: { [id: string]: any; }, key: string, prop: string) {
27
- if (obj[key] === undefined || obj[key][prop] === undefined) return;
28
- if (key === "__proto__" || key === "constructor" || key === "prototype") return;
29
- delete obj[key][prop];
30
- }
31
-
32
- const DEFINITION_LIST = "DefinitionList";
33
- const definitionRegex = /([a-zA-Z]:)?(.*[\\\/])(.*)(\((\d+),(\d+)\))/;
34
-
35
- export const PropertyType = ["Avg", "Min", "Max", "Delta", "StdDev"];
36
- export const RelatedProperty = ["SkewMin", "SkewMax", "NodeMin", "NodeMax"];
37
-
38
- export interface IPropertyValue {
39
- Key: string;
40
- Value?: string;
41
-
42
- // Extended properties ---
43
- Avg?: string;
44
- Min?: string;
45
- Max?: string;
46
- Delta?: string;
47
- StdDev?: string;
48
- StdDevs?: number;
49
-
50
- // Related properties ---
51
- SkewMin?: string;
52
- SkewMax?: string;
53
- NodeMin?: string;
54
- NodeMax?: string;
55
- }
56
-
57
- export interface IScope {
58
- __parentName?: string;
59
- __children?: IScope[];
60
- __formattedProps: { [key: string]: any };
61
- __groupedProps: { [key: string]: IPropertyValue };
62
- __StdDevs: number,
63
- __StdDevsSource: string,
64
- id: string;
65
- name: string;
66
- type: string;
67
- Kind: string;
68
- Label: string;
69
- [key: string]: any;
70
- }
71
-
72
- export interface ISplitMetric {
73
- measure: string;
74
- ext: string;
75
- label: string;
76
- }
77
-
78
- const metricKeyRegex = /[A-Z][a-z]*/g;
79
- function _splitMetric(fullLabel: string): ISplitMetric {
80
-
81
- // Related properties ---
82
- for (const relProp of RelatedProperty) {
83
- const index = fullLabel.indexOf(relProp);
84
- if (index === 0) {
85
- const measure = "";
86
- const label = fullLabel.slice(index + relProp.length);
87
- return { measure, ext: relProp, label };
88
- }
89
- }
90
-
91
- // Primary properties ---
92
- const labelParts = fullLabel.match(metricKeyRegex);
93
- if (labelParts?.length) {
94
- const measure = labelParts.shift();
95
- let label = labelParts.join("");
96
- for (const ext of PropertyType) {
97
- const index = label.indexOf(ext);
98
- if (index === 0) {
99
- label = label.slice(index + ext.length);
100
- return { measure, ext, label };
101
- }
102
- }
103
- // Not an aggregate property ---
104
- return { measure, ext: "", label };
105
- }
106
-
107
- // No match found ---
108
- return { measure: "", ext: "", label: fullLabel };
109
- }
110
-
111
- const splitLabelCache: { [key: string]: ISplitMetric } = {};
112
- export function splitMetric(key: string): ISplitMetric {
113
- let retVal = splitLabelCache[key];
114
- if (!retVal) {
115
- retVal = _splitMetric(key);
116
- splitLabelCache[key] = retVal;
117
- }
118
- return retVal;
119
- }
120
-
121
- function formatValue(item: IScope, key: string): string | undefined {
122
- return item.__formattedProps?.[key] ?? item[key];
123
- }
124
-
125
- type DedupProperties = { [key: string]: boolean };
126
-
127
- function safeParseFloat(val: string | undefined): number | undefined {
128
- if (val === undefined) return undefined;
129
- const retVal = parseFloat(val);
130
- return isNaN(retVal) ? undefined : retVal;
131
- }
132
-
133
- function formatValues(item: IScope, key: string, dedup: DedupProperties): IPropertyValue | null {
134
- const keyParts = splitMetric(key);
135
- if (!dedup[keyParts.measure]) {
136
- dedup[keyParts.label] = true;
137
- const avg = safeParseFloat(item[`${keyParts.measure}Avg${keyParts.label}`]);
138
- const min = safeParseFloat(item[`${keyParts.measure}Min${keyParts.label}`]);
139
- const max = safeParseFloat(item[`${keyParts.measure}Max${keyParts.label}`]);
140
- const stdDev = safeParseFloat(item[`${keyParts.measure}StdDev${keyParts.label}`]);
141
- const StdDevs = Math.max((avg - min) / stdDev, (max - avg) / stdDev);
142
-
143
- return {
144
- Key: `${keyParts.measure}${keyParts.label}`,
145
- Value: formatValue(item, `${keyParts.measure}${keyParts.label}`),
146
-
147
- // Extended properties ---
148
- Avg: formatValue(item, `${keyParts.measure}Avg${keyParts.label}`),
149
- Min: formatValue(item, `${keyParts.measure}Min${keyParts.label}`),
150
- Max: formatValue(item, `${keyParts.measure}Max${keyParts.label}`),
151
- Delta: formatValue(item, `${keyParts.measure}Delta${keyParts.label}`),
152
- StdDev: formatValue(item, `${keyParts.measure}StdDev${keyParts.label}`),
153
- StdDevs: isNaN(StdDevs) ? undefined : StdDevs,
154
-
155
- // Related properties ---
156
- SkewMin: formatValue(item, `SkewMin${keyParts.label}`),
157
- SkewMax: formatValue(item, `SkewMax${keyParts.label}`),
158
- NodeMin: formatValue(item, `NodeMin${keyParts.label}`),
159
- NodeMax: formatValue(item, `NodeMax${keyParts.label}`)
160
- };
161
- }
162
- return null;
163
- }
164
-
165
- const logger = scopedLogger("workunit.ts");
166
-
167
- export class WorkunitCache extends Cache<{ BaseUrl: string, Wuid: string }, Workunit> {
168
- constructor() {
169
- super((obj) => {
170
- return `${obj.BaseUrl}-${obj.Wuid}`;
171
- });
172
- }
173
- }
174
- const _workunits = new WorkunitCache();
175
-
176
- export interface DebugState {
177
- sequence: number;
178
- state: string;
179
- [key: string]: any;
180
- }
181
-
182
- export interface IWorkunit {
183
- ResultViews: WsWorkunits.ResultViews;
184
- HelpersCount: number;
185
- }
186
-
187
- export interface IDebugWorkunit {
188
- DebugState?: DebugState;
189
- }
190
-
191
- export interface ITimeElapsed {
192
- scope: string;
193
- start: string;
194
- elapsed: number;
195
- finish: string;
196
- }
197
-
198
- export type WorkunitEvents = "completed" | StateEvents;
199
- export type UWorkunitState = WsWorkunits.ECLWorkunit & WsWorkunits.Workunit & WsSMC.ActiveWorkunit & IWorkunit & IDebugWorkunit;
200
- export type IWorkunitState = WsWorkunits.ECLWorkunit | WsWorkunits.Workunit | WsSMC.ActiveWorkunit | IWorkunit | IDebugWorkunit;
201
- export class Workunit extends StateObject<UWorkunitState, IWorkunitState> implements WsWorkunits.Workunit {
202
- connection: WorkunitsService;
203
- topologyConnection: WsTopology.TopologyService;
204
- get BaseUrl() { return this.connection.baseUrl; }
205
-
206
- private _debugMode: boolean = false;
207
- private _debugAllGraph: any;
208
- private _submitAction: WUUpdate.Action;
209
-
210
- // Accessors ---
211
- get properties(): WsWorkunits.ECLWorkunit & WsWorkunits.Workunit { return this.get(); }
212
- get Wuid(): string { return this.get("Wuid"); }
213
- get Owner(): string { return this.get("Owner", ""); }
214
- get Cluster(): string { return this.get("Cluster", ""); }
215
- get Jobname(): string { return this.get("Jobname", ""); }
216
- get Description(): string { return this.get("Description", ""); }
217
- get ActionEx(): string { return this.get("ActionEx", ""); }
218
- get StateID(): WUStateID { return this.get("StateID", WUStateID.Unknown); }
219
- get State(): string { return this.get("State") || WUStateID[this.StateID]; }
220
- get Protected(): boolean { return this.get("Protected", false); }
221
- get Exceptions(): WsWorkunits.Exceptions2 { return this.get("Exceptions", { ECLException: [] }); }
222
- get ResultViews(): WsWorkunits.ResultViews { return this.get("ResultViews", { View: [] }); }
223
-
224
- private _resultCache = new ResultCache();
225
- get ResultCount(): number { return this.get("ResultCount", 0); }
226
- get Results(): WsWorkunits.Results { return this.get("Results", { ECLResult: [] }); }
227
- get CResults(): Result[] {
228
- return this.Results.ECLResult.map((eclResult) => {
229
- return this._resultCache.get(eclResult, () => {
230
- return Result.attach(this.connection, this.Wuid, eclResult, this.ResultViews.View);
231
- });
232
- });
233
- }
234
- get SequenceResults(): { [key: number]: Result } {
235
- const retVal: { [key: number]: Result } = {};
236
- this.CResults.forEach((result) => {
237
- retVal[result.Sequence] = result;
238
- });
239
- return retVal;
240
- }
241
- get Timers(): WsWorkunits.Timers { return this.get("Timers", { ECLTimer: [] }); }
242
- get CTimers(): Timer[] {
243
- return this.Timers.ECLTimer.map((eclTimer) => {
244
- return new Timer(this.connection, this.Wuid, eclTimer);
245
- });
246
- }
247
-
248
- private _graphCache = new GraphCache();
249
- get GraphCount(): number { return this.get("GraphCount", 0); }
250
- get Graphs(): WsWorkunits.Graphs { return this.get("Graphs", { ECLGraph: [] }); }
251
- get CGraphs(): ECLGraph[] {
252
- return this.Graphs.ECLGraph.map((eclGraph) => {
253
- return this._graphCache.get(eclGraph, () => {
254
- return new ECLGraph(this, eclGraph, this.CTimers);
255
- });
256
- });
257
- }
258
- get ThorLogList(): WsWorkunits.ThorLogList { return this.get("ThorLogList"); }
259
- get ResourceURLCount(): number { return this.get("ResourceURLCount", 0); }
260
- get ResourceURLs(): WsWorkunits.ResourceURLs { return this.get("ResourceURLs", { URL: [] }); }
261
- get CResourceURLs(): Resource[] {
262
- return this.ResourceURLs.URL.map((url) => {
263
- return new Resource(this, url);
264
- });
265
- }
266
- get TotalClusterTime(): string { return this.get("TotalClusterTime", ""); }
267
- get DateTimeScheduled(): string { return this.get("DateTimeScheduled"); }
268
- get IsPausing(): boolean { return this.get("IsPausing"); }
269
- get ThorLCR(): boolean { return this.get("ThorLCR"); }
270
- get ApplicationValues(): WsWorkunits.ApplicationValues { return this.get("ApplicationValues", { ApplicationValue: [] }); }
271
- get HasArchiveQuery(): boolean { return this.get("HasArchiveQuery"); }
272
- get StateEx(): string { return this.get("StateEx"); }
273
- get PriorityClass(): number { return this.get("PriorityClass"); }
274
- get PriorityLevel(): number { return this.get("PriorityLevel"); }
275
- get Snapshot(): string { return this.get("Snapshot"); }
276
- get ResultLimit(): number { return this.get("ResultLimit"); }
277
- get EventSchedule(): number { return this.get("EventSchedule"); }
278
- get Query(): WsWorkunits.Query { return this.get("Query"); }
279
- get HelpersCount(): number { return this.get("HelpersCount", 0); }
280
- get Helpers(): WsWorkunits.Helpers { return this.get("Helpers", { ECLHelpFile: [] }); }
281
- get DebugValues(): WsWorkunits.DebugValues { return this.get("DebugValues"); }
282
- get AllowedClusters(): WsWorkunits.AllowedClusters { return this.get("AllowedClusters"); }
283
- get ErrorCount(): number { return this.get("ErrorCount", 0); }
284
- get WarningCount(): number { return this.get("WarningCount", 0); }
285
- get InfoCount(): number { return this.get("InfoCount", 0); }
286
- get AlertCount(): number { return this.get("AlertCount", 0); }
287
- get SourceFileCount(): number { return this.get("SourceFileCount", 0); }
288
- get SourceFiles(): WsWorkunits.SourceFiles { return this.get("SourceFiles", { ECLSourceFile: [] }); }
289
- get CSourceFiles(): SourceFile[] {
290
- return this.SourceFiles.ECLSourceFile.map(eclSourceFile => new SourceFile(this.connection, this.Wuid, eclSourceFile));
291
- }
292
- get VariableCount(): number { return this.get("VariableCount", 0); }
293
- get Variables(): WsWorkunits.Variables { return this.get("Variables", { ECLResult: [] }); }
294
- get TimerCount(): number { return this.get("TimerCount", 0); }
295
- get HasDebugValue(): boolean { return this.get("HasDebugValue"); }
296
- get ApplicationValueCount(): number { return this.get("ApplicationValueCount", 0); }
297
- get XmlParams(): string { return this.get("XmlParams"); }
298
- get AccessFlag(): number { return this.get("AccessFlag"); }
299
- get ClusterFlag(): number { return this.get("ClusterFlag"); }
300
- get ResultViewCount(): number { return this.get("ResultViewCount", 0); }
301
- get DebugValueCount(): number { return this.get("DebugValueCount", 0); }
302
- get WorkflowCount(): number { return this.get("WorkflowCount", 0); }
303
- get Archived(): boolean { return this.get("Archived"); }
304
- get RoxieCluster(): string { return this.get("RoxieCluster"); }
305
- get DebugState(): DebugState { return this.get("DebugState", {} as DebugState)!; }
306
- get Queue(): string { return this.get("Queue"); }
307
- get Active(): boolean { return this.get("Active"); }
308
- get Action(): number { return this.get("Action"); }
309
- get Scope(): string { return this.get("Scope"); }
310
- get AbortBy(): string { return this.get("AbortBy"); }
311
- get AbortTime(): string { return this.get("AbortTime"); }
312
- get Workflows(): WsWorkunits.Workflows { return this.get("Workflows"); }
313
- get TimingData(): WsWorkunits.TimingData { return this.get("TimingData"); }
314
- get HelpersDesc(): string { return this.get("HelpersDesc"); }
315
- get GraphsDesc(): string { return this.get("GraphsDesc"); }
316
- get SourceFilesDesc(): string { return this.get("SourceFilesDesc"); }
317
- get ResultsDesc(): string { return this.get("ResultsDesc"); }
318
- get VariablesDesc(): string { return this.get("VariablesDesc"); }
319
- get TimersDesc(): string { return this.get("TimersDesc"); }
320
- get DebugValuesDesc(): string { return this.get("DebugValuesDesc"); }
321
- get ApplicationValuesDesc(): string { return this.get("ApplicationValuesDesc"); }
322
- get WorkflowsDesc(): string { return this.get("WorkflowsDesc"); }
323
- get ServiceNames(): WsWorkunits.ServiceNames { return this.get("ServiceNames"); }
324
- get CompileCost(): number { return this.get("CompileCost"); }
325
- get ExecuteCost(): number { return this.get("ExecuteCost"); }
326
- get FileAccessCost(): number { return this.get("FileAccessCost"); }
327
- get NoAccess(): boolean { return this.get("NoAccess"); }
328
- get ECLWUProcessList(): WsWorkunits.ECLWUProcessList { return this.get("ECLWUProcessList"); }
329
-
330
- // Factories ---
331
- static create(optsConnection: IOptions | IConnection): Promise<Workunit> {
332
- const retVal: Workunit = new Workunit(optsConnection);
333
- return retVal.connection.WUCreate().then((response) => {
334
- _workunits.set(retVal);
335
- retVal.set(response.Workunit);
336
- return retVal;
337
- });
338
- }
339
-
340
- static attach(optsConnection: IOptions | IConnection, wuid: string, state?: IWorkunitState): Workunit {
341
- const retVal: Workunit = _workunits.get({ BaseUrl: optsConnection.baseUrl, Wuid: wuid }, () => {
342
- return new Workunit(optsConnection, wuid);
343
- });
344
- if (state) {
345
- retVal.set(state);
346
- }
347
- return retVal;
348
- }
349
-
350
- static existsLocal(baseUrl: string, wuid: string): boolean {
351
- return _workunits.has({ BaseUrl: baseUrl, Wuid: wuid });
352
- }
353
-
354
- static submit(server: IOptions | IConnection, target: string, ecl: string, compileOnly = false): Promise<Workunit> {
355
- return Workunit.create(server).then((wu) => {
356
- return wu.update({ QueryText: ecl });
357
- }).then((wu) => {
358
- return compileOnly ? wu.submit(target, WUUpdate.Action.Compile) : wu.submit(target);
359
- });
360
- }
361
-
362
- static compile(server: IOptions | IConnection, target: string, ecl: string): Promise<Workunit> {
363
- return Workunit.submit(server, target, ecl, true);
364
- }
365
-
366
- static query(server: IOptions | IConnection, opts: Partial<WsWorkunits.WUQuery>): Promise<Workunit[]> {
367
- const wsWorkunits = new WorkunitsService(server);
368
- return wsWorkunits.WUQuery(opts).then((response) => {
369
- return response.Workunits.ECLWorkunit.map(function (wu) {
370
- return Workunit.attach(server, wu.Wuid, wu);
371
- });
372
- });
373
- }
374
-
375
- // --- --- ---
376
- protected constructor(optsConnection: IOptions | IConnection, wuid?: string) {
377
- super();
378
- this.connection = new WorkunitsService(optsConnection);
379
- this.topologyConnection = new WsTopology.TopologyService(optsConnection);
380
- this.clearState(wuid);
381
- }
382
-
383
- clearState(wuid?: string) {
384
- this.clear({
385
- Wuid: wuid,
386
- StateID: WUStateID.Unknown
387
- });
388
- }
389
-
390
- update(request: Partial<WsWorkunits.WUUpdate>): Promise<Workunit> {
391
- return this.connection.WUUpdate({
392
- ...request,
393
- ...{
394
- Wuid: this.Wuid,
395
- StateOrig: this.StateID,
396
- JobnameOrig: this.Jobname,
397
- DescriptionOrig: this.Description,
398
- ProtectedOrig: this.Protected,
399
- ClusterOrig: this.Cluster
400
- }
401
- }).then((response) => {
402
- this.set(response.Workunit);
403
- return this;
404
- });
405
- }
406
-
407
- submit(_cluster?: string, action: WUUpdate.Action = WUUpdate.Action.Run, resultLimit?: number): Promise<Workunit> {
408
- let clusterPromise;
409
- if (_cluster !== void 0) {
410
- clusterPromise = Promise.resolve(_cluster);
411
- } else {
412
- clusterPromise = this.topologyConnection.DefaultTpLogicalClusterQuery().then((response) => {
413
- return response.Name;
414
- });
415
- }
416
-
417
- this._debugMode = false;
418
- if (action === WUUpdate.Action.Debug) {
419
- action = WUUpdate.Action.Run;
420
- this._debugMode = true;
421
- }
422
-
423
- return clusterPromise.then((cluster) => {
424
- return this.connection.WUUpdate({
425
- Wuid: this.Wuid,
426
- Action: action,
427
- ResultLimit: resultLimit,
428
- DebugValues: {
429
- DebugValue: [
430
- {
431
- Name: "Debug",
432
- Value: this._debugMode ? "1" : ""
433
- }
434
- ]
435
- }
436
- }).then((response) => {
437
- this.set(response.Workunit);
438
- this._submitAction = action;
439
- return this.connection.WUSubmit({ Wuid: this.Wuid, Cluster: cluster });
440
- });
441
- }).then(() => {
442
- return this;
443
- });
444
- }
445
-
446
- isComplete(): boolean {
447
- switch (this.StateID) {
448
- case WUStateID.Compiled:
449
- return this.ActionEx === "compile" || this._submitAction === WUUpdate.Action.Compile;
450
- case WUStateID.Completed:
451
- case WUStateID.Failed:
452
- case WUStateID.Aborted:
453
- case WUStateID.NotFound:
454
- return true;
455
- default:
456
- }
457
- return false;
458
- }
459
-
460
- isFailed() {
461
- switch (this.StateID) {
462
- case WUStateID.Aborted:
463
- case WUStateID.Failed:
464
- return true;
465
- default:
466
- }
467
- return false;
468
- }
469
-
470
- isDeleted() {
471
- switch (this.StateID) {
472
- case WUStateID.NotFound:
473
- return true;
474
- default:
475
- }
476
- return false;
477
- }
478
-
479
- isDebugging() {
480
- switch (this.StateID) {
481
- case WUStateID.DebugPaused:
482
- case WUStateID.DebugRunning:
483
- return true;
484
- default:
485
- }
486
- return this._debugMode;
487
- }
488
-
489
- isRunning(): boolean {
490
- switch (this.StateID) {
491
- case WUStateID.Compiled:
492
- case WUStateID.Running:
493
- case WUStateID.Aborting:
494
- case WUStateID.Blocked:
495
- case WUStateID.DebugPaused:
496
- case WUStateID.DebugRunning:
497
- return true;
498
- default:
499
- }
500
- return false;
501
- }
502
-
503
- setToFailed() {
504
- return this.WUAction(WsWorkunits.ECLWUActions.SetToFailed);
505
- }
506
-
507
- pause() {
508
- return this.WUAction(WsWorkunits.ECLWUActions.Pause);
509
- }
510
-
511
- pauseNow() {
512
- return this.WUAction(WsWorkunits.ECLWUActions.PauseNow);
513
- }
514
-
515
- resume() {
516
- return this.WUAction(WsWorkunits.ECLWUActions.Resume);
517
- }
518
-
519
- abort() {
520
- return this.WUAction(WsWorkunits.ECLWUActions.Abort);
521
- }
522
-
523
- protect() {
524
- return this.WUAction(WsWorkunits.ECLWUActions.Protect);
525
- }
526
-
527
- unprotect() {
528
- return this.WUAction(WsWorkunits.ECLWUActions.Unprotect);
529
- }
530
-
531
- delete() {
532
- return this.WUAction(WsWorkunits.ECLWUActions.Delete);
533
- }
534
-
535
- restore() {
536
- return this.WUAction(WsWorkunits.ECLWUActions.Restore);
537
- }
538
-
539
- deschedule() {
540
- return this.WUAction(WsWorkunits.ECLWUActions.Deschedule);
541
- }
542
-
543
- reschedule() {
544
- return this.WUAction(WsWorkunits.ECLWUActions.Reschedule);
545
- }
546
-
547
- resubmit(): Promise<Workunit> {
548
- return this.WUResubmit({
549
- CloneWorkunit: false,
550
- ResetWorkflow: false
551
- }).then(() => {
552
- this.clearState(this.Wuid);
553
- return this.refresh().then(() => {
554
- this._monitor();
555
- return this;
556
- });
557
- });
558
- }
559
-
560
- clone(): Promise<Workunit> {
561
- return this.WUResubmit({
562
- CloneWorkunit: true,
563
- ResetWorkflow: false
564
- }).then((response) => {
565
- return Workunit.attach(this.connection.opts(), response.WUs.WU[0].WUID)
566
- .refresh()
567
- ;
568
- });
569
- }
570
-
571
- async refreshState(): Promise<this> {
572
- await this.WUQuery();
573
- // Ensure "isComplete" is correct for WUs that are only "Compiled".
574
- if (this.StateID === WUStateID.Compiled && !this.ActionEx && !this._submitAction) {
575
- await this.refreshInfo();
576
- }
577
- return this;
578
- }
579
-
580
- async refreshInfo(request?: Partial<WsWorkunits.WUInfo>): Promise<this> {
581
- await this.WUInfo(request);
582
- return this;
583
- }
584
-
585
- async refreshDebug(): Promise<this> {
586
- await this.debugStatus();
587
- return this;
588
- }
589
-
590
- async refresh(full: boolean = false, request?: Partial<WsWorkunits.WUInfo>): Promise<this> {
591
- if (full) {
592
- await Promise.all([this.refreshInfo(request), this.refreshDebug()]);
593
- } else {
594
- await this.refreshState();
595
- }
596
- return this;
597
- }
598
-
599
- eclExceptions(): WsWorkunits.ECLException[] {
600
- return this.Exceptions.ECLException;
601
- }
602
-
603
- fetchArchive(): Promise<string> {
604
- return this.connection.WUFileEx({
605
- Wuid: this.Wuid,
606
- Type: "ArchiveQuery"
607
- });
608
- }
609
-
610
- fetchECLExceptions(): Promise<WsWorkunits.ECLException[]> {
611
- return this.WUInfo({ IncludeExceptions: true }).then(() => {
612
- return this.eclExceptions();
613
- });
614
- }
615
-
616
- fetchResults(): Promise<Result[]> {
617
- return this.WUInfo({ IncludeResults: true }).then(() => {
618
- return this.CResults;
619
- });
620
- }
621
-
622
- fetchGraphs(): Promise<ECLGraph[]> {
623
- return this.WUInfo({ IncludeGraphs: true }).then(() => {
624
- return this.CGraphs;
625
- });
626
- }
627
-
628
- fetchQuery(): Promise<WsWorkunits.Query> {
629
- return this.WUInfo({ IncludeECL: true, TruncateEclTo64k: false }).then(() => {
630
- return this.Query;
631
- });
632
- }
633
-
634
- fetchHelpers(): Promise<WsWorkunits.ECLHelpFile[]> {
635
- return this.WUInfo({ IncludeHelpers: true }).then(() => {
636
- return this.Helpers?.ECLHelpFile || [];
637
- });
638
- }
639
-
640
- fetchAllowedClusters(): Promise<string[]> {
641
- return this.WUInfo({ IncludeAllowedClusters: true }).then(() => {
642
- return this.AllowedClusters?.AllowedCluster || [];
643
- });
644
- }
645
-
646
- fetchTotalClusterTime(): Promise<string> {
647
- return this.WUInfo({ IncludeTotalClusterTime: true }).then(() => {
648
- return this.TotalClusterTime;
649
- });
650
- }
651
-
652
- fetchServiceNames(): Promise<string[]> {
653
- return this.WUInfo({ IncludeServiceNames: true }).then(() => {
654
- return this.ServiceNames?.Item;
655
- });
656
- }
657
-
658
- fetchDetailsMeta(request: RecursivePartial<WsWorkunits.WUDetailsMeta> = {}): Promise<WsWorkunits.WUDetailsMetaResponse> {
659
- return this.WUDetailsMeta(request);
660
- }
661
-
662
- fetchDetailsRaw(request: RecursivePartial<WsWorkunits.WUDetails> = {}): Promise<WsWorkunits.Scope[]> {
663
- return this.WUDetails(request).then(response => response.Scopes.Scope);
664
- }
665
-
666
- normalizeDetails(meta: WsWorkunits.WUDetailsMetaResponse, scopes: WsWorkunits.Scope[]): { meta: WsWorkunits.WUDetailsMetaResponse, columns: { [id: string]: any }, data: IScope[] } {
667
- const columns: { [id: string]: any } = {
668
- id: {
669
- Measure: "label"
670
- },
671
- name: {
672
- Measure: "label"
673
- },
674
- type: {
675
- Measure: "label"
676
- }
677
- };
678
- const data: IScope[] = [];
679
- for (const scope of scopes) {
680
- const props = {};
681
- const formattedProps = {};
682
- if (scope && scope.Id && scope.Properties && scope.Properties.Property) {
683
- for (const key in scope.Properties.Property) {
684
- const scopeProperty = scope.Properties.Property[key];
685
- if (scopeProperty.Measure === "ns") {
686
- scopeProperty.Measure = "s";
687
- }
688
- if (scopeProperty.Name === "Kind") {
689
- const rawValue = parseInt(scopeProperty.RawValue, 10);
690
- scopeProperty.Formatted = meta.Activities.Activity.filter(a => a.Kind === rawValue)[0].Name ?? scopeProperty.RawValue;
691
- }
692
- columns[scopeProperty.Name] = { ...scopeProperty };
693
- safeDelete(columns, scopeProperty.Name, "RawValue");
694
- safeDelete(columns, scopeProperty.Name, "Formatted");
695
- switch (scopeProperty.Measure) {
696
- case "bool":
697
- props[scopeProperty.Name] = !!+scopeProperty.RawValue;
698
- break;
699
- case "sz":
700
- props[scopeProperty.Name] = +scopeProperty.RawValue;
701
- break;
702
- case "s":
703
- props[scopeProperty.Name] = +scopeProperty.RawValue / 1000000000;
704
- break;
705
- case "ns":
706
- props[scopeProperty.Name] = +scopeProperty.RawValue;
707
- break;
708
- case "ts":
709
- props[scopeProperty.Name] = new Date(+scopeProperty.RawValue / 1000).toISOString();
710
- break;
711
- case "cnt":
712
- props[scopeProperty.Name] = +scopeProperty.RawValue;
713
- break;
714
- case "cost":
715
- props[scopeProperty.Name] = +scopeProperty.RawValue / 1000000;
716
- break;
717
- case "node":
718
- props[scopeProperty.Name] = +scopeProperty.RawValue;
719
- break;
720
- case "skw":
721
- props[scopeProperty.Name] = +scopeProperty.RawValue;
722
- break;
723
- case "cpu":
724
- case "ppm":
725
- case "ip":
726
- case "cy":
727
- case "en":
728
- case "txt":
729
- case "id":
730
- case "fname":
731
- default:
732
- props[scopeProperty.Name] = scopeProperty.RawValue;
733
- }
734
- formattedProps[scopeProperty.Name] = formatNum(scopeProperty.Formatted ?? props[scopeProperty.Name]);
735
- }
736
- // Other properties ---
737
- }
738
- const normalizedScope: IScope = {
739
- id: scope.Id,
740
- name: scope.ScopeName,
741
- type: scope.ScopeType,
742
- Kind: scope["Kind"],
743
- Label: scope["Label"],
744
- __formattedProps: formattedProps,
745
- __groupedProps: {},
746
- __groupedRawProps: {},
747
- __StdDevs: 0,
748
- __StdDevsSource: "",
749
- ...props
750
- };
751
- if (normalizedScope[DEFINITION_LIST]) {
752
- try {
753
- const definitionList = JSON.parse(normalizedScope[DEFINITION_LIST].split("\\").join("\\\\"));
754
- normalizedScope[DEFINITION_LIST] = [];
755
- definitionList.forEach((definition, idx) => {
756
- const matches = definition.match(definitionRegex);
757
- if (matches) {
758
- const filePath = (matches[1] ?? "") + matches[2] + matches[3];
759
- const line = parseInt(matches[5]);
760
- const col = parseInt(matches[6]);
761
- normalizedScope[DEFINITION_LIST].push({ filePath, line, col });
762
- }
763
- });
764
- } catch (e) {
765
- logger.error(`Unexpected "DefinitionList": ${normalizedScope[DEFINITION_LIST]}`);
766
- }
767
- }
768
- const dedup: DedupProperties = {};
769
- for (const key in normalizedScope) {
770
- if (key.indexOf("__") !== 0) {
771
- const row = formatValues(normalizedScope, key, dedup);
772
- if (row) {
773
- normalizedScope.__groupedProps[row.Key] = row;
774
- if (!isNaN(row.StdDevs) && normalizedScope.__StdDevs < row.StdDevs) {
775
- normalizedScope.__StdDevs = row.StdDevs;
776
- normalizedScope.__StdDevsSource = row.Key;
777
- }
778
- }
779
- }
780
- }
781
- data.push(normalizedScope);
782
- }
783
- return {
784
- meta,
785
- columns,
786
- data
787
- };
788
- }
789
-
790
- fetchDetailsNormalized(request: RecursivePartial<WsWorkunits.WUDetails> = {}): Promise<{ meta: WsWorkunits.WUDetailsMetaResponse, columns: { [id: string]: any }, data: IScope[] }> {
791
- return Promise.all([this.fetchDetailsMeta(), this.fetchDetailsRaw(request)]).then(promises => {
792
- return this.normalizeDetails(promises[0], promises[1]);
793
- });
794
- }
795
-
796
- fetchInfo(request: Partial<WsWorkunits.WUInfo> = {}): Promise<WsWorkunits.WUInfoResponse> {
797
- return this.WUInfo(request);
798
- }
799
-
800
- fetchDetails(request: RecursivePartial<WsWorkunits.WUDetails> = {}): Promise<Scope[]> {
801
- return this.WUDetails(request).then((response) => {
802
- return response.Scopes.Scope.map((rawScope) => {
803
- return new Scope(this, rawScope);
804
- });
805
- });
806
- }
807
-
808
- fetchDetailsHierarchy(request: Partial<WsWorkunits.WUDetails> = {}): Promise<Scope[]> {
809
- return this.WUDetails(request).then((response) => {
810
- const retVal: Scope[] = [];
811
-
812
- // Recreate Scope Hierarchy and dedup ---
813
- const scopeMap: { [key: string]: Scope } = {};
814
- response.Scopes.Scope.forEach((rawScope) => {
815
- if (scopeMap[rawScope.ScopeName]) {
816
- scopeMap[rawScope.ScopeName].update(rawScope);
817
- return null;
818
- } else {
819
- const scope = new Scope(this, rawScope);
820
- scopeMap[scope.ScopeName] = scope;
821
- return scope;
822
- }
823
- });
824
- for (const key in scopeMap) {
825
- if (scopeMap.hasOwnProperty(key)) {
826
- const scope = scopeMap[key];
827
- const parentScopeID = scope.parentScope();
828
- if (parentScopeID && scopeMap[parentScopeID]) {
829
- scopeMap[parentScopeID].children().push(scope);
830
- } else {
831
- retVal.push(scope);
832
- }
833
- }
834
- }
835
-
836
- return retVal;
837
- });
838
- }
839
-
840
- fetchGraphDetails(graphIDs: string[] = [], rootTypes: string[]): Promise<BaseScope[]> {
841
- return this.fetchDetails({
842
- ScopeFilter: {
843
- MaxDepth: 999999,
844
- Ids: graphIDs,
845
- ScopeTypes: rootTypes,
846
- },
847
- NestedFilter: {
848
- Depth: 999999,
849
- ScopeTypes: ["graph", "subgraph", "activity", "edge", "function"]
850
- },
851
- PropertiesToReturn: {
852
- AllStatistics: true,
853
- AllAttributes: true,
854
- AllHints: true,
855
- AllProperties: true,
856
- AllScopes: true
857
- },
858
- ScopeOptions: {
859
- IncludeId: true,
860
- IncludeScope: true,
861
- IncludeScopeType: true
862
- },
863
- PropertyOptions: {
864
- IncludeName: true,
865
- IncludeRawValue: true,
866
- IncludeFormatted: true,
867
- IncludeMeasure: true,
868
- IncludeCreator: false,
869
- IncludeCreatorType: false
870
- }
871
- });
872
- }
873
-
874
- fetchScopeGraphs(graphIDs: string[] = []): Promise<ScopeGraph> {
875
- return this.fetchGraphDetails(graphIDs, ["graph"]).then((scopes) => {
876
- return createGraph(scopes);
877
- });
878
- }
879
-
880
- fetchTimeElapsed(): Promise<ITimeElapsed[]> {
881
- return this.fetchDetails({
882
- ScopeFilter: {
883
- PropertyFilters: {
884
- PropertyFilter: [{ Name: "TimeElapsed" }]
885
- }
886
- }
887
- }).then((scopes) => {
888
- const scopeInfo: { [key: string]: ITimeElapsed } = {};
889
- scopes.forEach((scope) => {
890
- scopeInfo[scope.ScopeName] = scopeInfo[scope.ScopeName] || {
891
- scope: scope.ScopeName,
892
- start: null,
893
- elapsed: null,
894
- finish: null
895
- };
896
- scope.CAttributes.forEach((attr) => {
897
- if (attr.Name === "TimeElapsed") {
898
- scopeInfo[scope.ScopeName].elapsed = +attr.RawValue;
899
- } else if (attr.Measure === "ts" && attr.Name.indexOf("Started") >= 0) {
900
- scopeInfo[scope.ScopeName].start = attr.Formatted;
901
- }
902
- });
903
- });
904
- // Workaround duplicate scope responses
905
- const retVal: ITimeElapsed[] = [];
906
- for (const key in scopeInfo) {
907
- const scope = scopeInfo[key];
908
- if (scope.start && scope.elapsed) {
909
- const endTime = parser(scope.start);
910
- endTime!.setMilliseconds(endTime!.getMilliseconds() + scope.elapsed / 1000000);
911
- scope.finish = formatter(endTime!);
912
- retVal.push(scope);
913
- }
914
- }
915
- retVal.sort((l, r) => {
916
- if (l.start < r.start) return -1;
917
- if (l.start > r.start) return 1;
918
- return 0;
919
- });
920
- return retVal;
921
- });
922
- }
923
-
924
- // Monitoring ---
925
- protected _monitor(): void {
926
- if (this.isComplete()) {
927
- this._monitorTickCount = 0;
928
- return;
929
- }
930
- super._monitor();
931
- }
932
-
933
- protected _monitorTimeoutDuration(): number {
934
- const retVal = super._monitorTimeoutDuration();
935
- if (this._monitorTickCount <= 1) { // Once
936
- return 1000;
937
- } else if (this._monitorTickCount <= 3) { // Twice
938
- return 3000;
939
- } else if (this._monitorTickCount <= 5) { // Twice
940
- return 5000;
941
- } else if (this._monitorTickCount <= 7) { // Twice
942
- return 10000;
943
- }
944
- return retVal;
945
- }
946
-
947
- // Events ---
948
- on(eventID: WorkunitEvents, propIDorCallback: StateCallback | keyof UWorkunitState, callback?: StatePropCallback): this {
949
- if (this.isCallback(propIDorCallback)) {
950
- switch (eventID) {
951
- case "completed":
952
- super.on("propChanged", "StateID", (changeInfo: IEvent) => {
953
- if (this.isComplete()) {
954
- propIDorCallback([changeInfo]);
955
- }
956
- });
957
- break;
958
- case "changed":
959
- super.on(eventID, propIDorCallback);
960
- break;
961
- default:
962
- }
963
- } else {
964
- switch (eventID) {
965
- case "changed":
966
- super.on(eventID, propIDorCallback, callback!);
967
- break;
968
- default:
969
- }
970
- }
971
- this._monitor();
972
- return this;
973
- }
974
-
975
- watchUntilComplete(callback?: StateCallback): Promise<this> {
976
- return new Promise((resolve, _) => {
977
- const watchHandle = this.watch((changes) => {
978
- if (callback) {
979
- callback(changes);
980
- }
981
- if (this.isComplete()) {
982
- watchHandle.release();
983
- resolve(this);
984
- }
985
- });
986
- });
987
- }
988
-
989
- watchUntilRunning(callback?: StateCallback): Promise<this> {
990
- return new Promise((resolve, _) => {
991
- const watchHandle = this.watch((changes) => {
992
- if (callback) {
993
- callback(changes);
994
- }
995
- if (this.isComplete() || this.isRunning()) {
996
- watchHandle.release();
997
- resolve(this);
998
- }
999
- });
1000
- });
1001
- }
1002
-
1003
- // WsWorkunits passthroughs ---
1004
- protected WUQuery(_request: Partial<WsWorkunits.WUQuery> = {}): Promise<WsWorkunits.WUQueryResponse> {
1005
- return this.connection.WUQuery({ ..._request, Wuid: this.Wuid }).then((response) => {
1006
- this.set(response.Workunits.ECLWorkunit[0]);
1007
- return response;
1008
- }).catch((e: ESPExceptions) => {
1009
- // deleted ---
1010
- const wuMissing = e.Exception.some((exception) => {
1011
- if (exception.Code === 20081) {
1012
- this.clearState(this.Wuid);
1013
- this.set("StateID", WUStateID.NotFound);
1014
- return true;
1015
- }
1016
- return false;
1017
- });
1018
- if (!wuMissing) {
1019
- logger.warning(`Unexpected ESP exception: ${e.message}`);
1020
- throw e;
1021
- }
1022
- return {} as WsWorkunits.WUQueryResponse;
1023
- });
1024
- }
1025
-
1026
- protected WUCreate() {
1027
- return this.connection.WUCreate().then((response) => {
1028
- this.set(response.Workunit);
1029
- _workunits.set(this);
1030
- return response;
1031
- });
1032
- }
1033
-
1034
- protected WUInfo(_request: Partial<WsWorkunits.WUInfo> = {}): Promise<WsWorkunits.WUInfoResponse> {
1035
- const includeResults = _request.IncludeResults || _request.IncludeResultsViewNames;
1036
- return this.connection.WUInfo({
1037
- ..._request,
1038
- Wuid: this.Wuid,
1039
- IncludeResults: includeResults,
1040
- IncludeResultsViewNames: includeResults,
1041
- SuppressResultSchemas: false
1042
- }).then((response) => {
1043
- this.set(response.Workunit);
1044
- if (includeResults) {
1045
- this.set({
1046
- ResultViews: response.ResultViews
1047
- } as IWorkunitState);
1048
- }
1049
- return response;
1050
- }).catch((e: ESPExceptions) => {
1051
- // deleted ---
1052
- const wuMissing = e.Exception.some((exception) => {
1053
- if (exception.Code === 20080) {
1054
- this.clearState(this.Wuid);
1055
- this.set("StateID", WUStateID.NotFound);
1056
- return true;
1057
- }
1058
- return false;
1059
- });
1060
- if (!wuMissing) {
1061
- logger.warning(`Unexpected ESP exception: ${e.message}`);
1062
- throw e;
1063
- }
1064
- return {} as WsWorkunits.WUInfoResponse;
1065
- });
1066
- }
1067
-
1068
- protected WUResubmit(request: Partial<WsWorkunits.WUResubmit>): Promise<WsWorkunits.WUResubmitResponse> {
1069
- return this.connection.WUResubmit(deepMixinT<WsWorkunits.WUResubmit>({}, request, {
1070
- Wuids: { Item: [this.Wuid] }
1071
- }));
1072
- }
1073
-
1074
- protected WUDetailsMeta(request: Partial<WsWorkunits.WUDetailsMeta>): Promise<WsWorkunits.WUDetailsMetaResponse> {
1075
- return this.connection.WUDetailsMeta(request);
1076
- }
1077
-
1078
- protected WUDetails(request: RecursivePartial<WsWorkunits.WUDetails>): Promise<WsWorkunits.WUDetailsResponse> {
1079
- return this.connection.WUDetails(deepMixinT<WsWorkunits.WUDetails>({
1080
- ScopeFilter: {
1081
- MaxDepth: 9999
1082
- },
1083
- ScopeOptions: {
1084
- IncludeMatchedScopesInResults: true,
1085
- IncludeScope: true,
1086
- IncludeId: false,
1087
- IncludeScopeType: false
1088
- },
1089
- PropertyOptions: {
1090
- IncludeName: true,
1091
- IncludeRawValue: false,
1092
- IncludeFormatted: true,
1093
- IncludeMeasure: true,
1094
- IncludeCreator: false,
1095
- IncludeCreatorType: false
1096
- }
1097
- }, request, { WUID: this.Wuid })).then((response) => {
1098
- return deepMixinT<WsWorkunits.WUDetailsResponse>({
1099
- Scopes: {
1100
- Scope: []
1101
- }
1102
- }, response);
1103
- });
1104
- }
1105
-
1106
- protected WUAction(actionType: WsWorkunits.ECLWUActions): Promise<WsWorkunits.WUActionResponse> {
1107
- return this.connection.WUAction({
1108
- Wuids: { Item: [this.Wuid] },
1109
- WUActionType: actionType
1110
- }).then((response) => {
1111
- return this.refresh().then(() => {
1112
- this._monitor();
1113
- return response;
1114
- });
1115
- });
1116
- }
1117
-
1118
- publish(name?: string) {
1119
- return this.connection.WUPublishWorkunit({
1120
- Wuid: this.Wuid,
1121
- Cluster: this.Cluster,
1122
- JobName: name || this.Jobname,
1123
- AllowForeignFiles: true,
1124
- Activate: WsWorkunits.WUQueryActivationMode.ActivateQuery,
1125
- Wait: 5000
1126
- });
1127
- }
1128
-
1129
- publishEx(request: Partial<WsWorkunits.WUPublishWorkunit>) {
1130
- const service = new WorkunitsServiceEx({ baseUrl: "" });
1131
- const publishRequest = {
1132
- Wuid: this.Wuid,
1133
- Cluster: this.Cluster,
1134
- JobName: this.Jobname,
1135
- AllowForeignFiles: true,
1136
- Activate: 1,
1137
- Wait: 5000,
1138
- ...request
1139
- };
1140
- return service.WUPublishWorkunitEx(publishRequest);
1141
- }
1142
-
1143
- protected WUCDebug(command: string, opts: any = {}): Promise<XMLNode | null> {
1144
- let optsStr = "";
1145
- for (const key in opts) {
1146
- if (opts.hasOwnProperty(key)) {
1147
- optsStr += ` ${key}='${opts[key]}'`;
1148
- }
1149
- }
1150
- return this.connection.WUCDebugEx({
1151
- Wuid: this.Wuid,
1152
- Command: `<debug:${command} uid='${this.Wuid}'${optsStr}/>`
1153
- }).then((response) => {
1154
- return response;
1155
- });
1156
- }
1157
-
1158
- debug(command: string, opts?: object): Promise<XMLNode> {
1159
- if (!this.isDebugging()) {
1160
- return Promise.resolve(new XMLNode(command));
1161
- }
1162
- return this.WUCDebug(command, opts).then((response: XMLNode) => {
1163
- const retVal: XMLNode[] = response.children(command);
1164
- if (retVal.length) {
1165
- return retVal[0];
1166
- }
1167
- return new XMLNode(command);
1168
- }).catch((_) => {
1169
- logger.error(_);
1170
- return Promise.resolve(new XMLNode(command));
1171
- });
1172
- }
1173
-
1174
- debugStatus(): Promise<XMLNode> {
1175
- if (!this.isDebugging()) {
1176
- return Promise.resolve<any>({
1177
- DebugState: { state: "unknown" }
1178
- });
1179
- }
1180
- return this.debug("status").then((response) => {
1181
- const debugState = { ...this.DebugState, ...response.$ };
1182
- this.set({
1183
- DebugState: debugState
1184
- });
1185
- return response;
1186
- });
1187
- }
1188
-
1189
- debugContinue(mode = ""): Promise<XMLNode> {
1190
- return this.debug("continue", {
1191
- mode
1192
- });
1193
- }
1194
-
1195
- debugStep(mode: string): Promise<XMLNode> {
1196
- return this.debug("step", {
1197
- mode
1198
- });
1199
- }
1200
-
1201
- debugPause(): Promise<XMLNode> {
1202
- return this.debug("interrupt");
1203
- }
1204
-
1205
- debugQuit(): Promise<XMLNode> {
1206
- return this.debug("quit");
1207
- }
1208
-
1209
- debugDeleteAllBreakpoints(): Promise<XMLNode> {
1210
- return this.debug("delete", {
1211
- idx: 0
1212
- });
1213
- }
1214
-
1215
- protected debugBreakpointResponseParser(rootNode: StringAnyMap) {
1216
- return rootNode.children().map((childNode: XMLNode) => {
1217
- if (childNode.name === "break") {
1218
- return childNode.$;
1219
- }
1220
- });
1221
- }
1222
-
1223
- debugBreakpointAdd(id: string, mode: string, action: string): Promise<XMLNode> {
1224
- return this.debug("breakpoint", {
1225
- id,
1226
- mode,
1227
- action
1228
- }).then((rootNode) => {
1229
- return this.debugBreakpointResponseParser(rootNode);
1230
- });
1231
- }
1232
-
1233
- debugBreakpointList(): Promise<any[]> {
1234
- return this.debug("list").then((rootNode) => {
1235
- return this.debugBreakpointResponseParser(rootNode);
1236
- });
1237
- }
1238
-
1239
- debugGraph(): Promise<XGMMLGraph> {
1240
- if (this._debugAllGraph && this.DebugState["_prevGraphSequenceNum"] === this.DebugState["graphSequenceNum"]) {
1241
- return Promise.resolve(this._debugAllGraph);
1242
- }
1243
- return this.debug("graph", { name: "all" }).then((response) => {
1244
- this.DebugState["_prevGraphSequenceNum"] = this.DebugState["graphSequenceNum"];
1245
- this._debugAllGraph = createXGMMLGraph(this.Wuid, response);
1246
- return this._debugAllGraph;
1247
- });
1248
- }
1249
-
1250
- debugBreakpointValid(path: string): Promise<IECLDefintion[]> {
1251
- return this.debugGraph().then((graph) => {
1252
- return breakpointLocations(graph, path);
1253
- });
1254
- }
1255
-
1256
- debugPrint(edgeID: string, startRow: number = 0, numRows: number = 10): Promise<StringAnyMap[]> {
1257
- return this.debug("print", {
1258
- edgeID,
1259
- startRow,
1260
- numRows
1261
- }).then((response: XMLNode) => {
1262
- return response.children().map((rowNode) => {
1263
- const retVal: StringAnyMap = {};
1264
- rowNode.children().forEach((cellNode) => {
1265
- retVal[cellNode.name] = cellNode.content;
1266
- });
1267
- return retVal;
1268
- });
1269
- });
1270
- }
1271
- }
1272
-
1273
- export interface IECLDefintion {
1274
- id: string;
1275
- file: string;
1276
- line: number;
1277
- column: number;
1278
- }
1279
-
1280
- const ATTR_DEFINITION = "definition";
1281
-
1282
- function hasECLDefinition(vertex: XGMMLVertex): boolean {
1283
- return vertex._![ATTR_DEFINITION] !== undefined;
1284
- }
1285
-
1286
- function getECLDefinition(vertex: XGMMLVertex): IECLDefintion {
1287
- const match = /([a-z]:\\(?:[-\w\.\d]+\\)*(?:[-\w\.\d]+)?|(?:\/[\w\.\-]+)+)\((\d*),(\d*)\)/.exec(vertex._![ATTR_DEFINITION]);
1288
- if (match) {
1289
- const [, _file, _row, _col] = match;
1290
- _file.replace(/\/\.\//g, "/");
1291
- return {
1292
- id: vertex._!["id"],
1293
- file: _file,
1294
- line: +_row,
1295
- column: +_col
1296
- };
1297
- }
1298
- throw new Error(`Bad definition: ${vertex._![ATTR_DEFINITION]}`);
1299
- }
1300
-
1301
- function breakpointLocations(graph: XGMMLGraph, path?: string): IECLDefintion[] {
1302
- const retVal: IECLDefintion[] = [];
1303
- for (const vertex of graph.vertices) {
1304
- if (hasECLDefinition(vertex)) {
1305
- const definition = getECLDefinition(vertex);
1306
- if (definition && !path || path === definition.file) {
1307
- retVal.push(definition);
1308
- }
1309
- }
1310
- }
1311
- return retVal.sort((l, r) => {
1312
- return l.line - r.line;
1313
- });
1314
- }
1
+ import { Cache, deepMixinT, IEvent, RecursivePartial, scopedLogger, StateCallback, StateEvents, StateObject, StatePropCallback, StringAnyMap, XMLNode } from "@hpcc-js/util";
2
+ import { format as d3Format } from "d3-format";
3
+ import { utcFormat, utcParse } from "d3-time-format";
4
+ import { IConnection, IOptions } from "../connection";
5
+ import { ESPExceptions } from "../espConnection";
6
+ import { WsSMC } from "../services/wsSMC";
7
+ import * as WsTopology from "../services/wsTopology";
8
+ import { WsWorkunits, WUStateID, WorkunitsService, WorkunitsServiceEx, WUUpdate } from "../services/wsWorkunits";
9
+ import { createGraph, createXGMMLGraph, ECLGraph, GraphCache, ScopeGraph, XGMMLGraph, XGMMLVertex } from "./graph";
10
+ import { Resource } from "./resource";
11
+ import { Result, ResultCache } from "./result";
12
+ import { BaseScope, Scope } from "./scope";
13
+ import { SourceFile } from "./sourceFile";
14
+ import { Timer } from "./timer";
15
+
16
+ const formatter = utcFormat("%Y-%m-%dT%H:%M:%S.%LZ");
17
+ const parser = utcParse("%Y-%m-%dT%H:%M:%S.%LZ");
18
+ const d3FormatNum = d3Format(",");
19
+ function formatNum(num: number | string): string {
20
+ if (num && !isNaN(+num)) {
21
+ return d3FormatNum(+num);
22
+ }
23
+ return num as string;
24
+ }
25
+
26
+ function safeDelete(obj: { [id: string]: any; }, key: string, prop: string) {
27
+ if (obj[key] === undefined || obj[key][prop] === undefined) return;
28
+ if (key === "__proto__" || key === "constructor" || key === "prototype") return;
29
+ delete obj[key][prop];
30
+ }
31
+
32
+ const DEFINITION_LIST = "DefinitionList";
33
+ const definitionRegex = /([a-zA-Z]:)?(.*[\\\/])(.*)(\((\d+),(\d+)\))/;
34
+
35
+ export const PropertyType = ["Avg", "Min", "Max", "Delta", "StdDev"];
36
+ export const RelatedProperty = ["SkewMin", "SkewMax", "NodeMin", "NodeMax"];
37
+
38
+ export interface IPropertyValue {
39
+ Key: string;
40
+ Value?: string;
41
+
42
+ // Extended properties ---
43
+ Avg?: string;
44
+ Min?: string;
45
+ Max?: string;
46
+ Delta?: string;
47
+ StdDev?: string;
48
+ StdDevs?: number;
49
+
50
+ // Related properties ---
51
+ SkewMin?: string;
52
+ SkewMax?: string;
53
+ NodeMin?: string;
54
+ NodeMax?: string;
55
+ }
56
+
57
+ export interface IScope {
58
+ __parentName?: string;
59
+ __children?: IScope[];
60
+ __formattedProps: { [key: string]: any };
61
+ __groupedProps: { [key: string]: IPropertyValue };
62
+ __StdDevs: number,
63
+ __StdDevsSource: string,
64
+ id: string;
65
+ name: string;
66
+ type: string;
67
+ Kind: string;
68
+ Label: string;
69
+ [key: string]: any;
70
+ }
71
+
72
+ export interface ISplitMetric {
73
+ measure: string;
74
+ ext: string;
75
+ label: string;
76
+ }
77
+
78
+ const metricKeyRegex = /[A-Z][a-z]*/g;
79
+ function _splitMetric(fullLabel: string): ISplitMetric {
80
+
81
+ // Related properties ---
82
+ for (const relProp of RelatedProperty) {
83
+ const index = fullLabel.indexOf(relProp);
84
+ if (index === 0) {
85
+ const measure = "";
86
+ const label = fullLabel.slice(index + relProp.length);
87
+ return { measure, ext: relProp, label };
88
+ }
89
+ }
90
+
91
+ // Primary properties ---
92
+ const labelParts = fullLabel.match(metricKeyRegex);
93
+ if (labelParts?.length) {
94
+ const measure = labelParts.shift();
95
+ let label = labelParts.join("");
96
+ for (const ext of PropertyType) {
97
+ const index = label.indexOf(ext);
98
+ if (index === 0) {
99
+ label = label.slice(index + ext.length);
100
+ return { measure, ext, label };
101
+ }
102
+ }
103
+ // Not an aggregate property ---
104
+ return { measure, ext: "", label };
105
+ }
106
+
107
+ // No match found ---
108
+ return { measure: "", ext: "", label: fullLabel };
109
+ }
110
+
111
+ const splitLabelCache: { [key: string]: ISplitMetric } = {};
112
+ export function splitMetric(key: string): ISplitMetric {
113
+ let retVal = splitLabelCache[key];
114
+ if (!retVal) {
115
+ retVal = _splitMetric(key);
116
+ splitLabelCache[key] = retVal;
117
+ }
118
+ return retVal;
119
+ }
120
+
121
+ function formatValue(item: IScope, key: string): string | undefined {
122
+ return item.__formattedProps?.[key] ?? item[key];
123
+ }
124
+
125
+ type DedupProperties = { [key: string]: boolean };
126
+
127
+ function safeParseFloat(val: string | undefined): number | undefined {
128
+ if (val === undefined) return undefined;
129
+ const retVal = parseFloat(val);
130
+ return isNaN(retVal) ? undefined : retVal;
131
+ }
132
+
133
+ function formatValues(item: IScope, key: string, dedup: DedupProperties): IPropertyValue | null {
134
+ const keyParts = splitMetric(key);
135
+ if (!dedup[keyParts.measure]) {
136
+ dedup[keyParts.label] = true;
137
+ const avg = safeParseFloat(item[`${keyParts.measure}Avg${keyParts.label}`]);
138
+ const min = safeParseFloat(item[`${keyParts.measure}Min${keyParts.label}`]);
139
+ const max = safeParseFloat(item[`${keyParts.measure}Max${keyParts.label}`]);
140
+ const stdDev = safeParseFloat(item[`${keyParts.measure}StdDev${keyParts.label}`]);
141
+ const StdDevs = Math.max((avg - min) / stdDev, (max - avg) / stdDev);
142
+
143
+ return {
144
+ Key: `${keyParts.measure}${keyParts.label}`,
145
+ Value: formatValue(item, `${keyParts.measure}${keyParts.label}`),
146
+
147
+ // Extended properties ---
148
+ Avg: formatValue(item, `${keyParts.measure}Avg${keyParts.label}`),
149
+ Min: formatValue(item, `${keyParts.measure}Min${keyParts.label}`),
150
+ Max: formatValue(item, `${keyParts.measure}Max${keyParts.label}`),
151
+ Delta: formatValue(item, `${keyParts.measure}Delta${keyParts.label}`),
152
+ StdDev: formatValue(item, `${keyParts.measure}StdDev${keyParts.label}`),
153
+ StdDevs: isNaN(StdDevs) ? undefined : StdDevs,
154
+
155
+ // Related properties ---
156
+ SkewMin: formatValue(item, `SkewMin${keyParts.label}`),
157
+ SkewMax: formatValue(item, `SkewMax${keyParts.label}`),
158
+ NodeMin: formatValue(item, `NodeMin${keyParts.label}`),
159
+ NodeMax: formatValue(item, `NodeMax${keyParts.label}`)
160
+ };
161
+ }
162
+ return null;
163
+ }
164
+
165
+ const logger = scopedLogger("workunit.ts");
166
+
167
+ export class WorkunitCache extends Cache<{ BaseUrl: string, Wuid: string }, Workunit> {
168
+ constructor() {
169
+ super((obj) => {
170
+ return `${obj.BaseUrl}-${obj.Wuid}`;
171
+ });
172
+ }
173
+ }
174
+ const _workunits = new WorkunitCache();
175
+
176
+ export interface DebugState {
177
+ sequence: number;
178
+ state: string;
179
+ [key: string]: any;
180
+ }
181
+
182
+ export interface IWorkunit {
183
+ ResultViews: WsWorkunits.ResultViews;
184
+ HelpersCount: number;
185
+ }
186
+
187
+ export interface IDebugWorkunit {
188
+ DebugState?: DebugState;
189
+ }
190
+
191
+ export interface ITimeElapsed {
192
+ scope: string;
193
+ start: string;
194
+ elapsed: number;
195
+ finish: string;
196
+ }
197
+
198
+ export type WorkunitEvents = "completed" | StateEvents;
199
+ export type UWorkunitState = WsWorkunits.ECLWorkunit & WsWorkunits.Workunit & WsSMC.ActiveWorkunit & IWorkunit & IDebugWorkunit;
200
+ export type IWorkunitState = WsWorkunits.ECLWorkunit | WsWorkunits.Workunit | WsSMC.ActiveWorkunit | IWorkunit | IDebugWorkunit;
201
+ export class Workunit extends StateObject<UWorkunitState, IWorkunitState> implements WsWorkunits.Workunit {
202
+ connection: WorkunitsService;
203
+ topologyConnection: WsTopology.TopologyService;
204
+ get BaseUrl() { return this.connection.baseUrl; }
205
+
206
+ private _debugMode: boolean = false;
207
+ private _debugAllGraph: any;
208
+ private _submitAction: WUUpdate.Action;
209
+
210
+ // Accessors ---
211
+ get properties(): WsWorkunits.ECLWorkunit & WsWorkunits.Workunit { return this.get(); }
212
+ get Wuid(): string { return this.get("Wuid"); }
213
+ get Owner(): string { return this.get("Owner", ""); }
214
+ get Cluster(): string { return this.get("Cluster", ""); }
215
+ get Jobname(): string { return this.get("Jobname", ""); }
216
+ get Description(): string { return this.get("Description", ""); }
217
+ get ActionEx(): string { return this.get("ActionEx", ""); }
218
+ get StateID(): WUStateID { return this.get("StateID", WUStateID.Unknown); }
219
+ get State(): string { return this.get("State") || WUStateID[this.StateID]; }
220
+ get Protected(): boolean { return this.get("Protected", false); }
221
+ get Exceptions(): WsWorkunits.Exceptions2 { return this.get("Exceptions", { ECLException: [] }); }
222
+ get ResultViews(): WsWorkunits.ResultViews { return this.get("ResultViews", { View: [] }); }
223
+
224
+ private _resultCache = new ResultCache();
225
+ get ResultCount(): number { return this.get("ResultCount", 0); }
226
+ get Results(): WsWorkunits.Results { return this.get("Results", { ECLResult: [] }); }
227
+ get CResults(): Result[] {
228
+ return this.Results.ECLResult.map((eclResult) => {
229
+ return this._resultCache.get(eclResult, () => {
230
+ return Result.attach(this.connection, this.Wuid, eclResult, this.ResultViews.View);
231
+ });
232
+ });
233
+ }
234
+ get SequenceResults(): { [key: number]: Result } {
235
+ const retVal: { [key: number]: Result } = {};
236
+ this.CResults.forEach((result) => {
237
+ retVal[result.Sequence] = result;
238
+ });
239
+ return retVal;
240
+ }
241
+ get Timers(): WsWorkunits.Timers { return this.get("Timers", { ECLTimer: [] }); }
242
+ get CTimers(): Timer[] {
243
+ return this.Timers.ECLTimer.map((eclTimer) => {
244
+ return new Timer(this.connection, this.Wuid, eclTimer);
245
+ });
246
+ }
247
+
248
+ private _graphCache = new GraphCache();
249
+ get GraphCount(): number { return this.get("GraphCount", 0); }
250
+ get Graphs(): WsWorkunits.Graphs { return this.get("Graphs", { ECLGraph: [] }); }
251
+ get CGraphs(): ECLGraph[] {
252
+ return this.Graphs.ECLGraph.map((eclGraph) => {
253
+ return this._graphCache.get(eclGraph, () => {
254
+ return new ECLGraph(this, eclGraph, this.CTimers);
255
+ });
256
+ });
257
+ }
258
+ get ThorLogList(): WsWorkunits.ThorLogList { return this.get("ThorLogList"); }
259
+ get ResourceURLCount(): number { return this.get("ResourceURLCount", 0); }
260
+ get ResourceURLs(): WsWorkunits.ResourceURLs { return this.get("ResourceURLs", { URL: [] }); }
261
+ get CResourceURLs(): Resource[] {
262
+ return this.ResourceURLs.URL.map((url) => {
263
+ return new Resource(this, url);
264
+ });
265
+ }
266
+ get TotalClusterTime(): string { return this.get("TotalClusterTime", ""); }
267
+ get DateTimeScheduled(): string { return this.get("DateTimeScheduled"); }
268
+ get IsPausing(): boolean { return this.get("IsPausing"); }
269
+ get ThorLCR(): boolean { return this.get("ThorLCR"); }
270
+ get ApplicationValues(): WsWorkunits.ApplicationValues { return this.get("ApplicationValues", { ApplicationValue: [] }); }
271
+ get HasArchiveQuery(): boolean { return this.get("HasArchiveQuery"); }
272
+ get StateEx(): string { return this.get("StateEx"); }
273
+ get PriorityClass(): number { return this.get("PriorityClass"); }
274
+ get PriorityLevel(): number { return this.get("PriorityLevel"); }
275
+ get Snapshot(): string { return this.get("Snapshot"); }
276
+ get ResultLimit(): number { return this.get("ResultLimit"); }
277
+ get EventSchedule(): number { return this.get("EventSchedule"); }
278
+ get Query(): WsWorkunits.Query { return this.get("Query"); }
279
+ get HelpersCount(): number { return this.get("HelpersCount", 0); }
280
+ get Helpers(): WsWorkunits.Helpers { return this.get("Helpers", { ECLHelpFile: [] }); }
281
+ get DebugValues(): WsWorkunits.DebugValues { return this.get("DebugValues"); }
282
+ get AllowedClusters(): WsWorkunits.AllowedClusters { return this.get("AllowedClusters"); }
283
+ get ErrorCount(): number { return this.get("ErrorCount", 0); }
284
+ get WarningCount(): number { return this.get("WarningCount", 0); }
285
+ get InfoCount(): number { return this.get("InfoCount", 0); }
286
+ get AlertCount(): number { return this.get("AlertCount", 0); }
287
+ get SourceFileCount(): number { return this.get("SourceFileCount", 0); }
288
+ get SourceFiles(): WsWorkunits.SourceFiles { return this.get("SourceFiles", { ECLSourceFile: [] }); }
289
+ get CSourceFiles(): SourceFile[] {
290
+ return this.SourceFiles.ECLSourceFile.map(eclSourceFile => new SourceFile(this.connection, this.Wuid, eclSourceFile));
291
+ }
292
+ get VariableCount(): number { return this.get("VariableCount", 0); }
293
+ get Variables(): WsWorkunits.Variables { return this.get("Variables", { ECLResult: [] }); }
294
+ get TimerCount(): number { return this.get("TimerCount", 0); }
295
+ get HasDebugValue(): boolean { return this.get("HasDebugValue"); }
296
+ get ApplicationValueCount(): number { return this.get("ApplicationValueCount", 0); }
297
+ get XmlParams(): string { return this.get("XmlParams"); }
298
+ get AccessFlag(): number { return this.get("AccessFlag"); }
299
+ get ClusterFlag(): number { return this.get("ClusterFlag"); }
300
+ get ResultViewCount(): number { return this.get("ResultViewCount", 0); }
301
+ get DebugValueCount(): number { return this.get("DebugValueCount", 0); }
302
+ get WorkflowCount(): number { return this.get("WorkflowCount", 0); }
303
+ get Archived(): boolean { return this.get("Archived"); }
304
+ get RoxieCluster(): string { return this.get("RoxieCluster"); }
305
+ get DebugState(): DebugState { return this.get("DebugState", {} as DebugState)!; }
306
+ get Queue(): string { return this.get("Queue"); }
307
+ get Active(): boolean { return this.get("Active"); }
308
+ get Action(): number { return this.get("Action"); }
309
+ get Scope(): string { return this.get("Scope"); }
310
+ get AbortBy(): string { return this.get("AbortBy"); }
311
+ get AbortTime(): string { return this.get("AbortTime"); }
312
+ get Workflows(): WsWorkunits.Workflows { return this.get("Workflows"); }
313
+ get TimingData(): WsWorkunits.TimingData { return this.get("TimingData"); }
314
+ get HelpersDesc(): string { return this.get("HelpersDesc"); }
315
+ get GraphsDesc(): string { return this.get("GraphsDesc"); }
316
+ get SourceFilesDesc(): string { return this.get("SourceFilesDesc"); }
317
+ get ResultsDesc(): string { return this.get("ResultsDesc"); }
318
+ get VariablesDesc(): string { return this.get("VariablesDesc"); }
319
+ get TimersDesc(): string { return this.get("TimersDesc"); }
320
+ get DebugValuesDesc(): string { return this.get("DebugValuesDesc"); }
321
+ get ApplicationValuesDesc(): string { return this.get("ApplicationValuesDesc"); }
322
+ get WorkflowsDesc(): string { return this.get("WorkflowsDesc"); }
323
+ get ServiceNames(): WsWorkunits.ServiceNames { return this.get("ServiceNames"); }
324
+ get CompileCost(): number { return this.get("CompileCost"); }
325
+ get ExecuteCost(): number { return this.get("ExecuteCost"); }
326
+ get FileAccessCost(): number { return this.get("FileAccessCost"); }
327
+ get NoAccess(): boolean { return this.get("NoAccess"); }
328
+ get ECLWUProcessList(): WsWorkunits.ECLWUProcessList { return this.get("ECLWUProcessList"); }
329
+
330
+ // Factories ---
331
+ static create(optsConnection: IOptions | IConnection): Promise<Workunit> {
332
+ const retVal: Workunit = new Workunit(optsConnection);
333
+ return retVal.connection.WUCreate().then((response) => {
334
+ _workunits.set(retVal);
335
+ retVal.set(response.Workunit);
336
+ return retVal;
337
+ });
338
+ }
339
+
340
+ static attach(optsConnection: IOptions | IConnection, wuid: string, state?: IWorkunitState): Workunit {
341
+ const retVal: Workunit = _workunits.get({ BaseUrl: optsConnection.baseUrl, Wuid: wuid }, () => {
342
+ return new Workunit(optsConnection, wuid);
343
+ });
344
+ if (state) {
345
+ retVal.set(state);
346
+ }
347
+ return retVal;
348
+ }
349
+
350
+ static existsLocal(baseUrl: string, wuid: string): boolean {
351
+ return _workunits.has({ BaseUrl: baseUrl, Wuid: wuid });
352
+ }
353
+
354
+ static submit(server: IOptions | IConnection, target: string, ecl: string, compileOnly = false): Promise<Workunit> {
355
+ return Workunit.create(server).then((wu) => {
356
+ return wu.update({ QueryText: ecl });
357
+ }).then((wu) => {
358
+ return compileOnly ? wu.submit(target, WUUpdate.Action.Compile) : wu.submit(target);
359
+ });
360
+ }
361
+
362
+ static compile(server: IOptions | IConnection, target: string, ecl: string): Promise<Workunit> {
363
+ return Workunit.submit(server, target, ecl, true);
364
+ }
365
+
366
+ static query(server: IOptions | IConnection, opts: Partial<WsWorkunits.WUQuery>): Promise<Workunit[]> {
367
+ const wsWorkunits = new WorkunitsService(server);
368
+ return wsWorkunits.WUQuery(opts).then((response) => {
369
+ return response.Workunits.ECLWorkunit.map(function (wu) {
370
+ return Workunit.attach(server, wu.Wuid, wu);
371
+ });
372
+ });
373
+ }
374
+
375
+ // --- --- ---
376
+ protected constructor(optsConnection: IOptions | IConnection, wuid?: string) {
377
+ super();
378
+ this.connection = new WorkunitsService(optsConnection);
379
+ this.topologyConnection = new WsTopology.TopologyService(optsConnection);
380
+ this.clearState(wuid);
381
+ }
382
+
383
+ clearState(wuid?: string) {
384
+ this.clear({
385
+ Wuid: wuid,
386
+ StateID: WUStateID.Unknown
387
+ });
388
+ }
389
+
390
+ update(request: Partial<WsWorkunits.WUUpdate>): Promise<Workunit> {
391
+ return this.connection.WUUpdate({
392
+ ...request,
393
+ ...{
394
+ Wuid: this.Wuid,
395
+ StateOrig: this.StateID,
396
+ JobnameOrig: this.Jobname,
397
+ DescriptionOrig: this.Description,
398
+ ProtectedOrig: this.Protected,
399
+ ClusterOrig: this.Cluster
400
+ }
401
+ }).then((response) => {
402
+ this.set(response.Workunit);
403
+ return this;
404
+ });
405
+ }
406
+
407
+ submit(_cluster?: string, action: WUUpdate.Action = WUUpdate.Action.Run, resultLimit?: number): Promise<Workunit> {
408
+ let clusterPromise;
409
+ if (_cluster !== void 0) {
410
+ clusterPromise = Promise.resolve(_cluster);
411
+ } else {
412
+ clusterPromise = this.topologyConnection.DefaultTpLogicalClusterQuery().then((response) => {
413
+ return response.Name;
414
+ });
415
+ }
416
+
417
+ this._debugMode = false;
418
+ if (action === WUUpdate.Action.Debug) {
419
+ action = WUUpdate.Action.Run;
420
+ this._debugMode = true;
421
+ }
422
+
423
+ return clusterPromise.then((cluster) => {
424
+ return this.connection.WUUpdate({
425
+ Wuid: this.Wuid,
426
+ Action: action,
427
+ ResultLimit: resultLimit,
428
+ DebugValues: {
429
+ DebugValue: [
430
+ {
431
+ Name: "Debug",
432
+ Value: this._debugMode ? "1" : ""
433
+ }
434
+ ]
435
+ }
436
+ }).then((response) => {
437
+ this.set(response.Workunit);
438
+ this._submitAction = action;
439
+ return this.connection.WUSubmit({ Wuid: this.Wuid, Cluster: cluster });
440
+ });
441
+ }).then(() => {
442
+ return this;
443
+ });
444
+ }
445
+
446
+ isComplete(): boolean {
447
+ switch (this.StateID) {
448
+ case WUStateID.Compiled:
449
+ return this.ActionEx === "compile" || this._submitAction === WUUpdate.Action.Compile;
450
+ case WUStateID.Completed:
451
+ case WUStateID.Failed:
452
+ case WUStateID.Aborted:
453
+ case WUStateID.NotFound:
454
+ return true;
455
+ default:
456
+ }
457
+ return false;
458
+ }
459
+
460
+ isFailed() {
461
+ switch (this.StateID) {
462
+ case WUStateID.Aborted:
463
+ case WUStateID.Failed:
464
+ return true;
465
+ default:
466
+ }
467
+ return false;
468
+ }
469
+
470
+ isDeleted() {
471
+ switch (this.StateID) {
472
+ case WUStateID.NotFound:
473
+ return true;
474
+ default:
475
+ }
476
+ return false;
477
+ }
478
+
479
+ isDebugging() {
480
+ switch (this.StateID) {
481
+ case WUStateID.DebugPaused:
482
+ case WUStateID.DebugRunning:
483
+ return true;
484
+ default:
485
+ }
486
+ return this._debugMode;
487
+ }
488
+
489
+ isRunning(): boolean {
490
+ switch (this.StateID) {
491
+ case WUStateID.Compiled:
492
+ case WUStateID.Running:
493
+ case WUStateID.Aborting:
494
+ case WUStateID.Blocked:
495
+ case WUStateID.DebugPaused:
496
+ case WUStateID.DebugRunning:
497
+ return true;
498
+ default:
499
+ }
500
+ return false;
501
+ }
502
+
503
+ setToFailed() {
504
+ return this.WUAction(WsWorkunits.ECLWUActions.SetToFailed);
505
+ }
506
+
507
+ pause() {
508
+ return this.WUAction(WsWorkunits.ECLWUActions.Pause);
509
+ }
510
+
511
+ pauseNow() {
512
+ return this.WUAction(WsWorkunits.ECLWUActions.PauseNow);
513
+ }
514
+
515
+ resume() {
516
+ return this.WUAction(WsWorkunits.ECLWUActions.Resume);
517
+ }
518
+
519
+ abort() {
520
+ return this.WUAction(WsWorkunits.ECLWUActions.Abort);
521
+ }
522
+
523
+ protect() {
524
+ return this.WUAction(WsWorkunits.ECLWUActions.Protect);
525
+ }
526
+
527
+ unprotect() {
528
+ return this.WUAction(WsWorkunits.ECLWUActions.Unprotect);
529
+ }
530
+
531
+ delete() {
532
+ return this.WUAction(WsWorkunits.ECLWUActions.Delete);
533
+ }
534
+
535
+ restore() {
536
+ return this.WUAction(WsWorkunits.ECLWUActions.Restore);
537
+ }
538
+
539
+ deschedule() {
540
+ return this.WUAction(WsWorkunits.ECLWUActions.Deschedule);
541
+ }
542
+
543
+ reschedule() {
544
+ return this.WUAction(WsWorkunits.ECLWUActions.Reschedule);
545
+ }
546
+
547
+ resubmit(): Promise<Workunit> {
548
+ return this.WUResubmit({
549
+ CloneWorkunit: false,
550
+ ResetWorkflow: false
551
+ }).then(() => {
552
+ this.clearState(this.Wuid);
553
+ return this.refresh().then(() => {
554
+ this._monitor();
555
+ return this;
556
+ });
557
+ });
558
+ }
559
+
560
+ clone(): Promise<Workunit> {
561
+ return this.WUResubmit({
562
+ CloneWorkunit: true,
563
+ ResetWorkflow: false
564
+ }).then((response) => {
565
+ return Workunit.attach(this.connection.opts(), response.WUs.WU[0].WUID)
566
+ .refresh()
567
+ ;
568
+ });
569
+ }
570
+
571
+ async refreshState(): Promise<this> {
572
+ await this.WUQuery();
573
+ // Ensure "isComplete" is correct for WUs that are only "Compiled".
574
+ if (this.StateID === WUStateID.Compiled && !this.ActionEx && !this._submitAction) {
575
+ await this.refreshInfo();
576
+ }
577
+ return this;
578
+ }
579
+
580
+ async refreshInfo(request?: Partial<WsWorkunits.WUInfo>): Promise<this> {
581
+ await this.WUInfo(request);
582
+ return this;
583
+ }
584
+
585
+ async refreshDebug(): Promise<this> {
586
+ await this.debugStatus();
587
+ return this;
588
+ }
589
+
590
+ async refresh(full: boolean = false, request?: Partial<WsWorkunits.WUInfo>): Promise<this> {
591
+ if (full) {
592
+ await Promise.all([this.refreshInfo(request), this.refreshDebug()]);
593
+ } else {
594
+ await this.refreshState();
595
+ }
596
+ return this;
597
+ }
598
+
599
+ eclExceptions(): WsWorkunits.ECLException[] {
600
+ return this.Exceptions.ECLException;
601
+ }
602
+
603
+ fetchArchive(): Promise<string> {
604
+ return this.connection.WUFileEx({
605
+ Wuid: this.Wuid,
606
+ Type: "ArchiveQuery"
607
+ });
608
+ }
609
+
610
+ fetchECLExceptions(): Promise<WsWorkunits.ECLException[]> {
611
+ return this.WUInfo({ IncludeExceptions: true }).then(() => {
612
+ return this.eclExceptions();
613
+ });
614
+ }
615
+
616
+ fetchResults(): Promise<Result[]> {
617
+ return this.WUInfo({ IncludeResults: true }).then(() => {
618
+ return this.CResults;
619
+ });
620
+ }
621
+
622
+ fetchGraphs(): Promise<ECLGraph[]> {
623
+ return this.WUInfo({ IncludeGraphs: true }).then(() => {
624
+ return this.CGraphs;
625
+ });
626
+ }
627
+
628
+ fetchQuery(): Promise<WsWorkunits.Query> {
629
+ return this.WUInfo({ IncludeECL: true, TruncateEclTo64k: false }).then(() => {
630
+ return this.Query;
631
+ });
632
+ }
633
+
634
+ fetchHelpers(): Promise<WsWorkunits.ECLHelpFile[]> {
635
+ return this.WUInfo({ IncludeHelpers: true }).then(() => {
636
+ return this.Helpers?.ECLHelpFile || [];
637
+ });
638
+ }
639
+
640
+ fetchAllowedClusters(): Promise<string[]> {
641
+ return this.WUInfo({ IncludeAllowedClusters: true }).then(() => {
642
+ return this.AllowedClusters?.AllowedCluster || [];
643
+ });
644
+ }
645
+
646
+ fetchTotalClusterTime(): Promise<string> {
647
+ return this.WUInfo({ IncludeTotalClusterTime: true }).then(() => {
648
+ return this.TotalClusterTime;
649
+ });
650
+ }
651
+
652
+ fetchServiceNames(): Promise<string[]> {
653
+ return this.WUInfo({ IncludeServiceNames: true }).then(() => {
654
+ return this.ServiceNames?.Item;
655
+ });
656
+ }
657
+
658
+ fetchDetailsMeta(request: RecursivePartial<WsWorkunits.WUDetailsMeta> = {}): Promise<WsWorkunits.WUDetailsMetaResponse> {
659
+ return this.WUDetailsMeta(request);
660
+ }
661
+
662
+ fetchDetailsRaw(request: RecursivePartial<WsWorkunits.WUDetails> = {}): Promise<WsWorkunits.Scope[]> {
663
+ return this.WUDetails(request).then(response => response.Scopes.Scope);
664
+ }
665
+
666
+ normalizeDetails(meta: WsWorkunits.WUDetailsMetaResponse, scopes: WsWorkunits.Scope[]): { meta: WsWorkunits.WUDetailsMetaResponse, columns: { [id: string]: any }, data: IScope[] } {
667
+ const columns: { [id: string]: any } = {
668
+ id: {
669
+ Measure: "label"
670
+ },
671
+ name: {
672
+ Measure: "label"
673
+ },
674
+ type: {
675
+ Measure: "label"
676
+ }
677
+ };
678
+ const data: IScope[] = [];
679
+ for (const scope of scopes) {
680
+ const props = {};
681
+ const formattedProps = {};
682
+ if (scope && scope.Id && scope.Properties && scope.Properties.Property) {
683
+ for (const key in scope.Properties.Property) {
684
+ const scopeProperty = scope.Properties.Property[key];
685
+ if (scopeProperty.Measure === "ns") {
686
+ scopeProperty.Measure = "s";
687
+ }
688
+ if (scopeProperty.Name === "Kind") {
689
+ const rawValue = parseInt(scopeProperty.RawValue, 10);
690
+ scopeProperty.Formatted = meta.Activities.Activity.filter(a => a.Kind === rawValue)[0].Name ?? scopeProperty.RawValue;
691
+ }
692
+ columns[scopeProperty.Name] = { ...scopeProperty };
693
+ safeDelete(columns, scopeProperty.Name, "RawValue");
694
+ safeDelete(columns, scopeProperty.Name, "Formatted");
695
+ switch (scopeProperty.Measure) {
696
+ case "bool":
697
+ props[scopeProperty.Name] = !!+scopeProperty.RawValue;
698
+ break;
699
+ case "sz":
700
+ props[scopeProperty.Name] = +scopeProperty.RawValue;
701
+ break;
702
+ case "s":
703
+ props[scopeProperty.Name] = +scopeProperty.RawValue / 1000000000;
704
+ break;
705
+ case "ns":
706
+ props[scopeProperty.Name] = +scopeProperty.RawValue;
707
+ break;
708
+ case "ts":
709
+ props[scopeProperty.Name] = new Date(+scopeProperty.RawValue / 1000).toISOString();
710
+ break;
711
+ case "cnt":
712
+ props[scopeProperty.Name] = +scopeProperty.RawValue;
713
+ break;
714
+ case "cost":
715
+ props[scopeProperty.Name] = +scopeProperty.RawValue / 1000000;
716
+ break;
717
+ case "node":
718
+ props[scopeProperty.Name] = +scopeProperty.RawValue;
719
+ break;
720
+ case "skw":
721
+ props[scopeProperty.Name] = +scopeProperty.RawValue;
722
+ break;
723
+ case "cpu":
724
+ case "ppm":
725
+ case "ip":
726
+ case "cy":
727
+ case "en":
728
+ case "txt":
729
+ case "id":
730
+ case "fname":
731
+ default:
732
+ props[scopeProperty.Name] = scopeProperty.RawValue;
733
+ }
734
+ formattedProps[scopeProperty.Name] = formatNum(scopeProperty.Formatted ?? props[scopeProperty.Name]);
735
+ }
736
+ // Other properties ---
737
+ }
738
+ const normalizedScope: IScope = {
739
+ id: scope.Id,
740
+ name: scope.ScopeName,
741
+ type: scope.ScopeType,
742
+ Kind: scope["Kind"],
743
+ Label: scope["Label"],
744
+ __formattedProps: formattedProps,
745
+ __groupedProps: {},
746
+ __groupedRawProps: {},
747
+ __StdDevs: 0,
748
+ __StdDevsSource: "",
749
+ ...props
750
+ };
751
+ if (normalizedScope[DEFINITION_LIST]) {
752
+ try {
753
+ const definitionList = JSON.parse(normalizedScope[DEFINITION_LIST].split("\\").join("\\\\"));
754
+ normalizedScope[DEFINITION_LIST] = [];
755
+ definitionList.forEach((definition, idx) => {
756
+ const matches = definition.match(definitionRegex);
757
+ if (matches) {
758
+ const filePath = (matches[1] ?? "") + matches[2] + matches[3];
759
+ const line = parseInt(matches[5]);
760
+ const col = parseInt(matches[6]);
761
+ normalizedScope[DEFINITION_LIST].push({ filePath, line, col });
762
+ }
763
+ });
764
+ } catch (e) {
765
+ logger.error(`Unexpected "DefinitionList": ${normalizedScope[DEFINITION_LIST]}`);
766
+ }
767
+ }
768
+ const dedup: DedupProperties = {};
769
+ for (const key in normalizedScope) {
770
+ if (key.indexOf("__") !== 0) {
771
+ const row = formatValues(normalizedScope, key, dedup);
772
+ if (row) {
773
+ normalizedScope.__groupedProps[row.Key] = row;
774
+ if (!isNaN(row.StdDevs) && normalizedScope.__StdDevs < row.StdDevs) {
775
+ normalizedScope.__StdDevs = row.StdDevs;
776
+ normalizedScope.__StdDevsSource = row.Key;
777
+ }
778
+ }
779
+ }
780
+ }
781
+ data.push(normalizedScope);
782
+ }
783
+ return {
784
+ meta,
785
+ columns,
786
+ data
787
+ };
788
+ }
789
+
790
+ fetchDetailsNormalized(request: RecursivePartial<WsWorkunits.WUDetails> = {}): Promise<{ meta: WsWorkunits.WUDetailsMetaResponse, columns: { [id: string]: any }, data: IScope[] }> {
791
+ return Promise.all([this.fetchDetailsMeta(), this.fetchDetailsRaw(request)]).then(promises => {
792
+ return this.normalizeDetails(promises[0], promises[1]);
793
+ });
794
+ }
795
+
796
+ fetchInfo(request: Partial<WsWorkunits.WUInfo> = {}): Promise<WsWorkunits.WUInfoResponse> {
797
+ return this.WUInfo(request);
798
+ }
799
+
800
+ fetchDetails(request: RecursivePartial<WsWorkunits.WUDetails> = {}): Promise<Scope[]> {
801
+ return this.WUDetails(request).then((response) => {
802
+ return response.Scopes.Scope.map((rawScope) => {
803
+ return new Scope(this, rawScope);
804
+ });
805
+ });
806
+ }
807
+
808
+ fetchDetailsHierarchy(request: Partial<WsWorkunits.WUDetails> = {}): Promise<Scope[]> {
809
+ return this.WUDetails(request).then((response) => {
810
+ const retVal: Scope[] = [];
811
+
812
+ // Recreate Scope Hierarchy and dedup ---
813
+ const scopeMap: { [key: string]: Scope } = {};
814
+ response.Scopes.Scope.forEach((rawScope) => {
815
+ if (scopeMap[rawScope.ScopeName]) {
816
+ scopeMap[rawScope.ScopeName].update(rawScope);
817
+ return null;
818
+ } else {
819
+ const scope = new Scope(this, rawScope);
820
+ scopeMap[scope.ScopeName] = scope;
821
+ return scope;
822
+ }
823
+ });
824
+ for (const key in scopeMap) {
825
+ if (scopeMap.hasOwnProperty(key)) {
826
+ const scope = scopeMap[key];
827
+ const parentScopeID = scope.parentScope();
828
+ if (parentScopeID && scopeMap[parentScopeID]) {
829
+ scopeMap[parentScopeID].children().push(scope);
830
+ } else {
831
+ retVal.push(scope);
832
+ }
833
+ }
834
+ }
835
+
836
+ return retVal;
837
+ });
838
+ }
839
+
840
+ fetchGraphDetails(graphIDs: string[] = [], rootTypes: string[]): Promise<BaseScope[]> {
841
+ return this.fetchDetails({
842
+ ScopeFilter: {
843
+ MaxDepth: 999999,
844
+ Ids: graphIDs,
845
+ ScopeTypes: rootTypes,
846
+ },
847
+ NestedFilter: {
848
+ Depth: 999999,
849
+ ScopeTypes: ["graph", "subgraph", "activity", "edge", "function"]
850
+ },
851
+ PropertiesToReturn: {
852
+ AllStatistics: true,
853
+ AllAttributes: true,
854
+ AllHints: true,
855
+ AllProperties: true,
856
+ AllScopes: true
857
+ },
858
+ ScopeOptions: {
859
+ IncludeId: true,
860
+ IncludeScope: true,
861
+ IncludeScopeType: true
862
+ },
863
+ PropertyOptions: {
864
+ IncludeName: true,
865
+ IncludeRawValue: true,
866
+ IncludeFormatted: true,
867
+ IncludeMeasure: true,
868
+ IncludeCreator: false,
869
+ IncludeCreatorType: false
870
+ }
871
+ });
872
+ }
873
+
874
+ fetchScopeGraphs(graphIDs: string[] = []): Promise<ScopeGraph> {
875
+ return this.fetchGraphDetails(graphIDs, ["graph"]).then((scopes) => {
876
+ return createGraph(scopes);
877
+ });
878
+ }
879
+
880
+ fetchTimeElapsed(): Promise<ITimeElapsed[]> {
881
+ return this.fetchDetails({
882
+ ScopeFilter: {
883
+ PropertyFilters: {
884
+ PropertyFilter: [{ Name: "TimeElapsed" }]
885
+ }
886
+ }
887
+ }).then((scopes) => {
888
+ const scopeInfo: { [key: string]: ITimeElapsed } = {};
889
+ scopes.forEach((scope) => {
890
+ scopeInfo[scope.ScopeName] = scopeInfo[scope.ScopeName] || {
891
+ scope: scope.ScopeName,
892
+ start: null,
893
+ elapsed: null,
894
+ finish: null
895
+ };
896
+ scope.CAttributes.forEach((attr) => {
897
+ if (attr.Name === "TimeElapsed") {
898
+ scopeInfo[scope.ScopeName].elapsed = +attr.RawValue;
899
+ } else if (attr.Measure === "ts" && attr.Name.indexOf("Started") >= 0) {
900
+ scopeInfo[scope.ScopeName].start = attr.Formatted;
901
+ }
902
+ });
903
+ });
904
+ // Workaround duplicate scope responses
905
+ const retVal: ITimeElapsed[] = [];
906
+ for (const key in scopeInfo) {
907
+ const scope = scopeInfo[key];
908
+ if (scope.start && scope.elapsed) {
909
+ const endTime = parser(scope.start);
910
+ endTime!.setMilliseconds(endTime!.getMilliseconds() + scope.elapsed / 1000000);
911
+ scope.finish = formatter(endTime!);
912
+ retVal.push(scope);
913
+ }
914
+ }
915
+ retVal.sort((l, r) => {
916
+ if (l.start < r.start) return -1;
917
+ if (l.start > r.start) return 1;
918
+ return 0;
919
+ });
920
+ return retVal;
921
+ });
922
+ }
923
+
924
+ // Monitoring ---
925
+ protected _monitor(): void {
926
+ if (this.isComplete()) {
927
+ this._monitorTickCount = 0;
928
+ return;
929
+ }
930
+ super._monitor();
931
+ }
932
+
933
+ protected _monitorTimeoutDuration(): number {
934
+ const retVal = super._monitorTimeoutDuration();
935
+ if (this._monitorTickCount <= 1) { // Once
936
+ return 1000;
937
+ } else if (this._monitorTickCount <= 3) { // Twice
938
+ return 3000;
939
+ } else if (this._monitorTickCount <= 5) { // Twice
940
+ return 5000;
941
+ } else if (this._monitorTickCount <= 7) { // Twice
942
+ return 10000;
943
+ }
944
+ return retVal;
945
+ }
946
+
947
+ // Events ---
948
+ on(eventID: WorkunitEvents, propIDorCallback: StateCallback | keyof UWorkunitState, callback?: StatePropCallback): this {
949
+ if (this.isCallback(propIDorCallback)) {
950
+ switch (eventID) {
951
+ case "completed":
952
+ super.on("propChanged", "StateID", (changeInfo: IEvent) => {
953
+ if (this.isComplete()) {
954
+ propIDorCallback([changeInfo]);
955
+ }
956
+ });
957
+ break;
958
+ case "changed":
959
+ super.on(eventID, propIDorCallback);
960
+ break;
961
+ default:
962
+ }
963
+ } else {
964
+ switch (eventID) {
965
+ case "changed":
966
+ super.on(eventID, propIDorCallback, callback!);
967
+ break;
968
+ default:
969
+ }
970
+ }
971
+ this._monitor();
972
+ return this;
973
+ }
974
+
975
+ watchUntilComplete(callback?: StateCallback): Promise<this> {
976
+ return new Promise((resolve, _) => {
977
+ const watchHandle = this.watch((changes) => {
978
+ if (callback) {
979
+ callback(changes);
980
+ }
981
+ if (this.isComplete()) {
982
+ watchHandle.release();
983
+ resolve(this);
984
+ }
985
+ });
986
+ });
987
+ }
988
+
989
+ watchUntilRunning(callback?: StateCallback): Promise<this> {
990
+ return new Promise((resolve, _) => {
991
+ const watchHandle = this.watch((changes) => {
992
+ if (callback) {
993
+ callback(changes);
994
+ }
995
+ if (this.isComplete() || this.isRunning()) {
996
+ watchHandle.release();
997
+ resolve(this);
998
+ }
999
+ });
1000
+ });
1001
+ }
1002
+
1003
+ // WsWorkunits passthroughs ---
1004
+ protected WUQuery(_request: Partial<WsWorkunits.WUQuery> = {}): Promise<WsWorkunits.WUQueryResponse> {
1005
+ return this.connection.WUQuery({ ..._request, Wuid: this.Wuid }).then((response) => {
1006
+ this.set(response.Workunits.ECLWorkunit[0]);
1007
+ return response;
1008
+ }).catch((e: ESPExceptions) => {
1009
+ // deleted ---
1010
+ const wuMissing = e.Exception.some((exception) => {
1011
+ if (exception.Code === 20081) {
1012
+ this.clearState(this.Wuid);
1013
+ this.set("StateID", WUStateID.NotFound);
1014
+ return true;
1015
+ }
1016
+ return false;
1017
+ });
1018
+ if (!wuMissing) {
1019
+ logger.warning(`Unexpected ESP exception: ${e.message}`);
1020
+ throw e;
1021
+ }
1022
+ return {} as WsWorkunits.WUQueryResponse;
1023
+ });
1024
+ }
1025
+
1026
+ protected WUCreate() {
1027
+ return this.connection.WUCreate().then((response) => {
1028
+ this.set(response.Workunit);
1029
+ _workunits.set(this);
1030
+ return response;
1031
+ });
1032
+ }
1033
+
1034
+ protected WUInfo(_request: Partial<WsWorkunits.WUInfo> = {}): Promise<WsWorkunits.WUInfoResponse> {
1035
+ const includeResults = _request.IncludeResults || _request.IncludeResultsViewNames;
1036
+ return this.connection.WUInfo({
1037
+ ..._request,
1038
+ Wuid: this.Wuid,
1039
+ IncludeResults: includeResults,
1040
+ IncludeResultsViewNames: includeResults,
1041
+ SuppressResultSchemas: false
1042
+ }).then((response) => {
1043
+ this.set(response.Workunit);
1044
+ if (includeResults) {
1045
+ this.set({
1046
+ ResultViews: response.ResultViews
1047
+ } as IWorkunitState);
1048
+ }
1049
+ return response;
1050
+ }).catch((e: ESPExceptions) => {
1051
+ // deleted ---
1052
+ const wuMissing = e.Exception.some((exception) => {
1053
+ if (exception.Code === 20080) {
1054
+ this.clearState(this.Wuid);
1055
+ this.set("StateID", WUStateID.NotFound);
1056
+ return true;
1057
+ }
1058
+ return false;
1059
+ });
1060
+ if (!wuMissing) {
1061
+ logger.warning(`Unexpected ESP exception: ${e.message}`);
1062
+ throw e;
1063
+ }
1064
+ return {} as WsWorkunits.WUInfoResponse;
1065
+ });
1066
+ }
1067
+
1068
+ protected WUResubmit(request: Partial<WsWorkunits.WUResubmit>): Promise<WsWorkunits.WUResubmitResponse> {
1069
+ return this.connection.WUResubmit(deepMixinT<WsWorkunits.WUResubmit>({}, request, {
1070
+ Wuids: { Item: [this.Wuid] }
1071
+ }));
1072
+ }
1073
+
1074
+ protected WUDetailsMeta(request: Partial<WsWorkunits.WUDetailsMeta>): Promise<WsWorkunits.WUDetailsMetaResponse> {
1075
+ return this.connection.WUDetailsMeta(request);
1076
+ }
1077
+
1078
+ protected WUDetails(request: RecursivePartial<WsWorkunits.WUDetails>): Promise<WsWorkunits.WUDetailsResponse> {
1079
+ return this.connection.WUDetails(deepMixinT<WsWorkunits.WUDetails>({
1080
+ ScopeFilter: {
1081
+ MaxDepth: 9999
1082
+ },
1083
+ ScopeOptions: {
1084
+ IncludeMatchedScopesInResults: true,
1085
+ IncludeScope: true,
1086
+ IncludeId: false,
1087
+ IncludeScopeType: false
1088
+ },
1089
+ PropertyOptions: {
1090
+ IncludeName: true,
1091
+ IncludeRawValue: false,
1092
+ IncludeFormatted: true,
1093
+ IncludeMeasure: true,
1094
+ IncludeCreator: false,
1095
+ IncludeCreatorType: false
1096
+ }
1097
+ }, request, { WUID: this.Wuid })).then((response) => {
1098
+ return deepMixinT<WsWorkunits.WUDetailsResponse>({
1099
+ Scopes: {
1100
+ Scope: []
1101
+ }
1102
+ }, response);
1103
+ });
1104
+ }
1105
+
1106
+ protected WUAction(actionType: WsWorkunits.ECLWUActions): Promise<WsWorkunits.WUActionResponse> {
1107
+ return this.connection.WUAction({
1108
+ Wuids: { Item: [this.Wuid] },
1109
+ WUActionType: actionType
1110
+ }).then((response) => {
1111
+ return this.refresh().then(() => {
1112
+ this._monitor();
1113
+ return response;
1114
+ });
1115
+ });
1116
+ }
1117
+
1118
+ publish(name?: string) {
1119
+ return this.connection.WUPublishWorkunit({
1120
+ Wuid: this.Wuid,
1121
+ Cluster: this.Cluster,
1122
+ JobName: name || this.Jobname,
1123
+ AllowForeignFiles: true,
1124
+ Activate: WsWorkunits.WUQueryActivationMode.ActivateQuery,
1125
+ Wait: 5000
1126
+ });
1127
+ }
1128
+
1129
+ publishEx(request: Partial<WsWorkunits.WUPublishWorkunit>) {
1130
+ const service = new WorkunitsServiceEx({ baseUrl: "" });
1131
+ const publishRequest = {
1132
+ Wuid: this.Wuid,
1133
+ Cluster: this.Cluster,
1134
+ JobName: this.Jobname,
1135
+ AllowForeignFiles: true,
1136
+ Activate: 1,
1137
+ Wait: 5000,
1138
+ ...request
1139
+ };
1140
+ return service.WUPublishWorkunitEx(publishRequest);
1141
+ }
1142
+
1143
+ protected WUCDebug(command: string, opts: any = {}): Promise<XMLNode | null> {
1144
+ let optsStr = "";
1145
+ for (const key in opts) {
1146
+ if (opts.hasOwnProperty(key)) {
1147
+ optsStr += ` ${key}='${opts[key]}'`;
1148
+ }
1149
+ }
1150
+ return this.connection.WUCDebugEx({
1151
+ Wuid: this.Wuid,
1152
+ Command: `<debug:${command} uid='${this.Wuid}'${optsStr}/>`
1153
+ }).then((response) => {
1154
+ return response;
1155
+ });
1156
+ }
1157
+
1158
+ debug(command: string, opts?: object): Promise<XMLNode> {
1159
+ if (!this.isDebugging()) {
1160
+ return Promise.resolve(new XMLNode(command));
1161
+ }
1162
+ return this.WUCDebug(command, opts).then((response: XMLNode) => {
1163
+ const retVal: XMLNode[] = response.children(command);
1164
+ if (retVal.length) {
1165
+ return retVal[0];
1166
+ }
1167
+ return new XMLNode(command);
1168
+ }).catch((_) => {
1169
+ logger.error(_);
1170
+ return Promise.resolve(new XMLNode(command));
1171
+ });
1172
+ }
1173
+
1174
+ debugStatus(): Promise<XMLNode> {
1175
+ if (!this.isDebugging()) {
1176
+ return Promise.resolve<any>({
1177
+ DebugState: { state: "unknown" }
1178
+ });
1179
+ }
1180
+ return this.debug("status").then((response) => {
1181
+ const debugState = { ...this.DebugState, ...response.$ };
1182
+ this.set({
1183
+ DebugState: debugState
1184
+ });
1185
+ return response;
1186
+ });
1187
+ }
1188
+
1189
+ debugContinue(mode = ""): Promise<XMLNode> {
1190
+ return this.debug("continue", {
1191
+ mode
1192
+ });
1193
+ }
1194
+
1195
+ debugStep(mode: string): Promise<XMLNode> {
1196
+ return this.debug("step", {
1197
+ mode
1198
+ });
1199
+ }
1200
+
1201
+ debugPause(): Promise<XMLNode> {
1202
+ return this.debug("interrupt");
1203
+ }
1204
+
1205
+ debugQuit(): Promise<XMLNode> {
1206
+ return this.debug("quit");
1207
+ }
1208
+
1209
+ debugDeleteAllBreakpoints(): Promise<XMLNode> {
1210
+ return this.debug("delete", {
1211
+ idx: 0
1212
+ });
1213
+ }
1214
+
1215
+ protected debugBreakpointResponseParser(rootNode: StringAnyMap) {
1216
+ return rootNode.children().map((childNode: XMLNode) => {
1217
+ if (childNode.name === "break") {
1218
+ return childNode.$;
1219
+ }
1220
+ });
1221
+ }
1222
+
1223
+ debugBreakpointAdd(id: string, mode: string, action: string): Promise<XMLNode> {
1224
+ return this.debug("breakpoint", {
1225
+ id,
1226
+ mode,
1227
+ action
1228
+ }).then((rootNode) => {
1229
+ return this.debugBreakpointResponseParser(rootNode);
1230
+ });
1231
+ }
1232
+
1233
+ debugBreakpointList(): Promise<any[]> {
1234
+ return this.debug("list").then((rootNode) => {
1235
+ return this.debugBreakpointResponseParser(rootNode);
1236
+ });
1237
+ }
1238
+
1239
+ debugGraph(): Promise<XGMMLGraph> {
1240
+ if (this._debugAllGraph && this.DebugState["_prevGraphSequenceNum"] === this.DebugState["graphSequenceNum"]) {
1241
+ return Promise.resolve(this._debugAllGraph);
1242
+ }
1243
+ return this.debug("graph", { name: "all" }).then((response) => {
1244
+ this.DebugState["_prevGraphSequenceNum"] = this.DebugState["graphSequenceNum"];
1245
+ this._debugAllGraph = createXGMMLGraph(this.Wuid, response);
1246
+ return this._debugAllGraph;
1247
+ });
1248
+ }
1249
+
1250
+ debugBreakpointValid(path: string): Promise<IECLDefintion[]> {
1251
+ return this.debugGraph().then((graph) => {
1252
+ return breakpointLocations(graph, path);
1253
+ });
1254
+ }
1255
+
1256
+ debugPrint(edgeID: string, startRow: number = 0, numRows: number = 10): Promise<StringAnyMap[]> {
1257
+ return this.debug("print", {
1258
+ edgeID,
1259
+ startRow,
1260
+ numRows
1261
+ }).then((response: XMLNode) => {
1262
+ return response.children().map((rowNode) => {
1263
+ const retVal: StringAnyMap = {};
1264
+ rowNode.children().forEach((cellNode) => {
1265
+ retVal[cellNode.name] = cellNode.content;
1266
+ });
1267
+ return retVal;
1268
+ });
1269
+ });
1270
+ }
1271
+ }
1272
+
1273
+ export interface IECLDefintion {
1274
+ id: string;
1275
+ file: string;
1276
+ line: number;
1277
+ column: number;
1278
+ }
1279
+
1280
+ const ATTR_DEFINITION = "definition";
1281
+
1282
+ function hasECLDefinition(vertex: XGMMLVertex): boolean {
1283
+ return vertex._![ATTR_DEFINITION] !== undefined;
1284
+ }
1285
+
1286
+ function getECLDefinition(vertex: XGMMLVertex): IECLDefintion {
1287
+ const match = /([a-z]:\\(?:[-\w\.\d]+\\)*(?:[-\w\.\d]+)?|(?:\/[\w\.\-]+)+)\((\d*),(\d*)\)/.exec(vertex._![ATTR_DEFINITION]);
1288
+ if (match) {
1289
+ const [, _file, _row, _col] = match;
1290
+ _file.replace(/\/\.\//g, "/");
1291
+ return {
1292
+ id: vertex._!["id"],
1293
+ file: _file,
1294
+ line: +_row,
1295
+ column: +_col
1296
+ };
1297
+ }
1298
+ throw new Error(`Bad definition: ${vertex._![ATTR_DEFINITION]}`);
1299
+ }
1300
+
1301
+ function breakpointLocations(graph: XGMMLGraph, path?: string): IECLDefintion[] {
1302
+ const retVal: IECLDefintion[] = [];
1303
+ for (const vertex of graph.vertices) {
1304
+ if (hasECLDefinition(vertex)) {
1305
+ const definition = getECLDefinition(vertex);
1306
+ if (definition && !path || path === definition.file) {
1307
+ retVal.push(definition);
1308
+ }
1309
+ }
1310
+ }
1311
+ return retVal.sort((l, r) => {
1312
+ return l.line - r.line;
1313
+ });
1314
+ }