@hpcc-js/comms 3.14.2 → 3.14.4

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 (110) hide show
  1. package/LICENSE +43 -43
  2. package/README.md +50 -50
  3. package/dist/browser/index.js +1 -1
  4. package/dist/browser/index.js.map +1 -1
  5. package/dist/browser/index.umd.cjs +1 -1
  6. package/dist/browser/index.umd.cjs.map +1 -1
  7. package/dist/node/index.cjs +6 -6
  8. package/dist/node/index.cjs.map +4 -4
  9. package/dist/node/index.js +6 -6
  10. package/dist/node/index.js.map +4 -4
  11. package/package.json +5 -5
  12. package/src/__package__.ts +3 -3
  13. package/src/clienttools/eclMeta.ts +506 -506
  14. package/src/clienttools/eclcc.ts +628 -628
  15. package/src/connection.ts +288 -288
  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 +196 -196
  20. package/src/ecl/machine.ts +63 -63
  21. package/src/ecl/query.ts +265 -265
  22. package/src/ecl/queryGraph.ts +813 -813
  23. package/src/ecl/resource.ts +39 -39
  24. package/src/ecl/result.ts +245 -245
  25. package/src/ecl/scope.ts +188 -188
  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 +1340 -1340
  32. package/src/ecl/xsdParser.ts +267 -267
  33. package/src/espConnection.ts +164 -164
  34. package/src/index.browser.ts +1 -1
  35. package/src/index.common.ts +40 -40
  36. package/src/index.node.ts +48 -48
  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 +18 -18
  43. package/src/services/wsDFU.ts +34 -34
  44. package/src/services/wsDFUXRef.ts +121 -121
  45. package/src/services/wsDali.ts +8 -8
  46. package/src/services/wsEcl.ts +123 -123
  47. package/src/services/wsElk.ts +8 -8
  48. package/src/services/wsLogaccess.ts +267 -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 +80 -80
  53. package/src/services/wsSasha.ts +7 -7
  54. package/src/services/wsStore.ts +32 -32
  55. package/src/services/wsTopology.ts +45 -45
  56. package/src/services/wsWorkunits.ts +151 -151
  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/FileSpray/v1.27/FileSpray.ts +930 -930
  61. package/src/services/wsdl/WsCloud/v1/WsCloud.ts +38 -38
  62. package/src/services/wsdl/WsCloud/v1.02/WsCloud.ts +77 -77
  63. package/src/services/wsdl/WsDFUXRef/v1.02/WsDFUXRef.ts +224 -224
  64. package/src/services/wsdl/WsDFUXRef/v1.04/WsDFUXRef.ts +227 -227
  65. package/src/services/wsdl/WsDali/v1.04/WsDali.ts +216 -216
  66. package/src/services/wsdl/WsDfu/v1.62/WsDfu.ts +1455 -1455
  67. package/src/services/wsdl/WsDfu/v1.63/WsDfu.ts +1465 -1465
  68. package/src/services/wsdl/WsDfu/v1.65/WsDfu.ts +1244 -1244
  69. package/src/services/wsdl/WsDfu/v1.66/WsDfu.ts +1267 -1267
  70. package/src/services/wsdl/WsDfu/v1.67/WsDfu.ts +1268 -1268
  71. package/src/services/wsdl/WsFileIO/v1.01/WsFileIO.ts +104 -104
  72. package/src/services/wsdl/WsPackageProcess/v1.04/WsPackageProcess.ts +519 -519
  73. package/src/services/wsdl/WsPackageProcess/v1.07/WsPackageProcess.ts +500 -500
  74. package/src/services/wsdl/WsResources/v1.01/WsResources.ts +119 -119
  75. package/src/services/wsdl/WsSMC/v1.24/WsSMC.ts +665 -665
  76. package/src/services/wsdl/WsSMC/v1.27/WsSMC.ts +591 -591
  77. package/src/services/wsdl/WsSMC/v1.28/WsSMC.ts +645 -645
  78. package/src/services/wsdl/WsSMC/v1.29/WsSMC.ts +660 -660
  79. package/src/services/wsdl/WsTopology/v1.31/WsTopology.ts +856 -856
  80. package/src/services/wsdl/WsTopology/v1.32/WsTopology.ts +786 -786
  81. package/src/services/wsdl/WsTopology/v1.33/WsTopology.ts +835 -835
  82. package/src/services/wsdl/WsWorkunits/v1.88/WsWorkunits.ts +2944 -2944
  83. package/src/services/wsdl/WsWorkunits/v1.94/WsWorkunits.ts +3072 -3072
  84. package/src/services/wsdl/WsWorkunits/v1.95/WsWorkunits.ts +3073 -3073
  85. package/src/services/wsdl/WsWorkunits/v1.97/WsWorkunits.ts +3134 -3134
  86. package/src/services/wsdl/WsWorkunits/v1.98/WsWorkunits.ts +3182 -3182
  87. package/src/services/wsdl/WsWorkunits/v1.99/WsWorkunits.ts +3162 -3162
  88. package/src/services/wsdl/WsWorkunits/v2/WsWorkunits.ts +3153 -3153
  89. package/src/services/wsdl/WsWorkunits/v2.02/WsWorkunits.ts +3162 -3162
  90. package/src/services/wsdl/WsWorkunits/v2.03/WsWorkunits.ts +3164 -3164
  91. package/src/services/wsdl/WsWorkunits/v2.04/WsWorkunits.ts +3171 -0
  92. package/src/services/wsdl/ws_access/v1.16/ws_access.ts +1086 -1086
  93. package/src/services/wsdl/ws_access/v1.17/ws_access.ts +1023 -1023
  94. package/src/services/wsdl/ws_account/v1.05/ws_account.ts +111 -111
  95. package/src/services/wsdl/ws_account/v1.06/ws_account.ts +109 -109
  96. package/src/services/wsdl/ws_account/v1.07/ws_account.ts +114 -114
  97. package/src/services/wsdl/ws_codesign/v1.1/ws_codesign.ts +95 -95
  98. package/src/services/wsdl/ws_elk/v1/ws_elk.ts +47 -47
  99. package/src/services/wsdl/ws_logaccess/v1/ws_logaccess.ts +83 -83
  100. package/src/services/wsdl/ws_logaccess/v1.02/ws_logaccess.ts +161 -161
  101. package/src/services/wsdl/ws_logaccess/v1.03/ws_logaccess.ts +190 -190
  102. package/src/services/wsdl/ws_logaccess/v1.04/ws_logaccess.ts +215 -215
  103. package/src/services/wsdl/ws_logaccess/v1.05/ws_logaccess.ts +219 -219
  104. package/src/services/wsdl/ws_logaccess/v1.08/ws_logaccess.ts +267 -267
  105. package/src/services/wsdl/ws_machine/v1.17/ws_machine.ts +567 -567
  106. package/src/services/wsdl/ws_machine/v1.18/ws_machine.ts +497 -497
  107. package/src/services/wsdl/ws_machine/v1.19/ws_machine.ts +497 -497
  108. package/src/services/wsdl/wsstore/v1.02/wsstore.ts +239 -239
  109. package/types/services/wsWorkunits.d.ts +1 -1
  110. package/types/services/wsdl/WsWorkunits/{v2.03 → v2.04}/WsWorkunits.d.ts +9 -3
@@ -1,1340 +1,1340 @@
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.ts";
5
- import { ESPExceptions } from "../espConnection.ts";
6
- import { WsSMC } from "../services/wsSMC.ts";
7
- import * as WsTopology from "../services/wsTopology.ts";
8
- import { WsWorkunits, WUStateID, WorkunitsService, WorkunitsServiceEx, WUUpdate } from "../services/wsWorkunits.ts";
9
- import { createGraph, createXGMMLGraph, ECLGraph, GraphCache, ScopeGraph, XGMMLGraph, XGMMLVertex } from "./graph.ts";
10
- import { Resource } from "./resource.ts";
11
- import { Result, ResultCache } from "./result.ts";
12
- import { BaseScope, Scope } from "./scope.ts";
13
- import { SourceFile } from "./sourceFile.ts";
14
- import { Timer } from "./timer.ts";
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
- get CostSavingPotential(): number { return this.get("CostSavingPotential"); }
330
-
331
- // Factories ---
332
- static create(optsConnection: IOptions | IConnection): Promise<Workunit> {
333
- const retVal: Workunit = new Workunit(optsConnection);
334
- return retVal.connection.WUCreate().then((response) => {
335
- _workunits.set(retVal);
336
- retVal.set(response.Workunit);
337
- return retVal;
338
- });
339
- }
340
-
341
- static attach(optsConnection: IOptions | IConnection, wuid: string, state?: IWorkunitState): Workunit {
342
- const retVal: Workunit = _workunits.get({ BaseUrl: optsConnection.baseUrl, Wuid: wuid }, () => {
343
- return new Workunit(optsConnection, wuid);
344
- });
345
- if (state) {
346
- retVal.set(state);
347
- }
348
- return retVal;
349
- }
350
-
351
- static existsLocal(baseUrl: string, wuid: string): boolean {
352
- return _workunits.has({ BaseUrl: baseUrl, Wuid: wuid });
353
- }
354
-
355
- static submit(server: IOptions | IConnection, target: string, ecl: string, compileOnly = false): Promise<Workunit> {
356
- return Workunit.create(server).then((wu) => {
357
- return wu.update({ QueryText: ecl });
358
- }).then((wu) => {
359
- return compileOnly ? wu.submit(target, WUUpdate.Action.Compile) : wu.submit(target);
360
- });
361
- }
362
-
363
- static compile(server: IOptions | IConnection, target: string, ecl: string): Promise<Workunit> {
364
- return Workunit.submit(server, target, ecl, true);
365
- }
366
-
367
- static query(server: IOptions | IConnection, opts: Partial<WsWorkunits.WUQuery>): Promise<Workunit[]> {
368
- const wsWorkunits = new WorkunitsService(server);
369
- return wsWorkunits.WUQuery(opts).then((response) => {
370
- return response.Workunits.ECLWorkunit.map(function (wu) {
371
- return Workunit.attach(server, wu.Wuid, wu);
372
- });
373
- });
374
- }
375
-
376
- // --- --- ---
377
- protected constructor(optsConnection: IOptions | IConnection, wuid?: string) {
378
- super();
379
- this.connection = new WorkunitsService(optsConnection);
380
- this.topologyConnection = new WsTopology.TopologyService(optsConnection);
381
- this.clearState(wuid);
382
- }
383
-
384
- clearState(wuid?: string) {
385
- this.clear({
386
- Wuid: wuid,
387
- StateID: WUStateID.Unknown
388
- });
389
- }
390
-
391
- update(request: Partial<WsWorkunits.WUUpdate>): Promise<Workunit> {
392
- return this.connection.WUUpdate({
393
- ...request,
394
- ...{
395
- Wuid: this.Wuid,
396
- StateOrig: this.StateID,
397
- JobnameOrig: this.Jobname,
398
- DescriptionOrig: this.Description,
399
- ProtectedOrig: this.Protected,
400
- ClusterOrig: this.Cluster
401
- }
402
- }).then((response) => {
403
- this.set(response.Workunit);
404
- return this;
405
- });
406
- }
407
-
408
- submit(_cluster?: string, action: WUUpdate.Action = WUUpdate.Action.Run, resultLimit?: number): Promise<Workunit> {
409
- let clusterPromise;
410
- if (_cluster !== void 0) {
411
- clusterPromise = Promise.resolve(_cluster);
412
- } else {
413
- clusterPromise = this.topologyConnection.DefaultTpLogicalClusterQuery().then((response) => {
414
- return response.Name;
415
- });
416
- }
417
-
418
- this._debugMode = false;
419
- if (action === WUUpdate.Action.Debug) {
420
- action = WUUpdate.Action.Run;
421
- this._debugMode = true;
422
- }
423
-
424
- return clusterPromise.then((cluster) => {
425
- return this.connection.WUUpdate({
426
- Wuid: this.Wuid,
427
- Action: action,
428
- ResultLimit: resultLimit,
429
- DebugValues: {
430
- DebugValue: [
431
- {
432
- Name: "Debug",
433
- Value: this._debugMode ? "1" : ""
434
- }
435
- ]
436
- }
437
- }).then((response) => {
438
- this.set(response.Workunit);
439
- this._submitAction = action;
440
- return this.connection.WUSubmit({ Wuid: this.Wuid, Cluster: cluster });
441
- });
442
- }).then(() => {
443
- return this;
444
- });
445
- }
446
-
447
- isComplete(): boolean {
448
- switch (this.StateID) {
449
- case WUStateID.Compiled:
450
- return this.ActionEx === "compile" || this._submitAction === WUUpdate.Action.Compile;
451
- case WUStateID.Completed:
452
- case WUStateID.Failed:
453
- case WUStateID.Aborted:
454
- case WUStateID.NotFound:
455
- return true;
456
- default:
457
- }
458
- return false;
459
- }
460
-
461
- isFailed() {
462
- switch (this.StateID) {
463
- case WUStateID.Aborted:
464
- case WUStateID.Failed:
465
- return true;
466
- default:
467
- }
468
- return false;
469
- }
470
-
471
- isDeleted() {
472
- switch (this.StateID) {
473
- case WUStateID.NotFound:
474
- return true;
475
- default:
476
- }
477
- return false;
478
- }
479
-
480
- isDebugging() {
481
- switch (this.StateID) {
482
- case WUStateID.DebugPaused:
483
- case WUStateID.DebugRunning:
484
- return true;
485
- default:
486
- }
487
- return this._debugMode;
488
- }
489
-
490
- isRunning(): boolean {
491
- switch (this.StateID) {
492
- case WUStateID.Compiled:
493
- case WUStateID.Running:
494
- case WUStateID.Aborting:
495
- case WUStateID.Blocked:
496
- case WUStateID.DebugPaused:
497
- case WUStateID.DebugRunning:
498
- return true;
499
- default:
500
- }
501
- return false;
502
- }
503
-
504
- setToFailed() {
505
- return this.WUAction(WsWorkunits.ECLWUActions.SetToFailed);
506
- }
507
-
508
- pause() {
509
- return this.WUAction(WsWorkunits.ECLWUActions.Pause);
510
- }
511
-
512
- pauseNow() {
513
- return this.WUAction(WsWorkunits.ECLWUActions.PauseNow);
514
- }
515
-
516
- resume() {
517
- return this.WUAction(WsWorkunits.ECLWUActions.Resume);
518
- }
519
-
520
- abort() {
521
- return this.WUAction(WsWorkunits.ECLWUActions.Abort);
522
- }
523
-
524
- protect() {
525
- return this.WUAction(WsWorkunits.ECLWUActions.Protect);
526
- }
527
-
528
- unprotect() {
529
- return this.WUAction(WsWorkunits.ECLWUActions.Unprotect);
530
- }
531
-
532
- delete() {
533
- return this.WUAction(WsWorkunits.ECLWUActions.Delete);
534
- }
535
-
536
- restore() {
537
- return this.WUAction(WsWorkunits.ECLWUActions.Restore);
538
- }
539
-
540
- deschedule() {
541
- return this.WUAction(WsWorkunits.ECLWUActions.Deschedule);
542
- }
543
-
544
- reschedule() {
545
- return this.WUAction(WsWorkunits.ECLWUActions.Reschedule);
546
- }
547
-
548
- resubmit(): Promise<Workunit> {
549
- return this.WUResubmit({
550
- CloneWorkunit: false,
551
- ResetWorkflow: false
552
- }).then(() => {
553
- this.clearState(this.Wuid);
554
- return this.refresh().then(() => {
555
- this._monitor();
556
- return this;
557
- });
558
- });
559
- }
560
-
561
- clone(): Promise<Workunit> {
562
- return this.WUResubmit({
563
- CloneWorkunit: true,
564
- ResetWorkflow: false
565
- }).then((response) => {
566
- return Workunit.attach(this.connection.opts(), response.WUs.WU[0].WUID)
567
- .refresh()
568
- ;
569
- });
570
- }
571
-
572
- async refreshState(): Promise<this> {
573
- await this.WUQuery();
574
- // Ensure "isComplete" is correct for WUs that are only "Compiled".
575
- if (this.StateID === WUStateID.Compiled && !this.ActionEx && !this._submitAction) {
576
- await this.refreshInfo();
577
- }
578
- return this;
579
- }
580
-
581
- async refreshInfo(request?: Partial<WsWorkunits.WUInfo>): Promise<this> {
582
- await this.WUInfo(request);
583
- return this;
584
- }
585
-
586
- async refreshDebug(): Promise<this> {
587
- await this.debugStatus();
588
- return this;
589
- }
590
-
591
- async refresh(full: boolean = false, request?: Partial<WsWorkunits.WUInfo>): Promise<this> {
592
- if (full) {
593
- await Promise.all([this.refreshInfo(request), this.refreshDebug()]);
594
- } else {
595
- await this.refreshState();
596
- }
597
- return this;
598
- }
599
-
600
- eclExceptions(): WsWorkunits.ECLException[] {
601
- return this.Exceptions.ECLException;
602
- }
603
-
604
- fetchArchive(): Promise<string> {
605
- return this.connection.WUFileEx({
606
- Wuid: this.Wuid,
607
- Type: "ArchiveQuery"
608
- });
609
- }
610
-
611
- fetchECLExceptions(): Promise<WsWorkunits.ECLException[]> {
612
- return this.WUInfo({ IncludeExceptions: true }).then(() => {
613
- return this.eclExceptions();
614
- });
615
- }
616
-
617
- fetchResults(): Promise<Result[]> {
618
- return this.WUInfo({ IncludeResults: true }).then(() => {
619
- return this.CResults;
620
- });
621
- }
622
-
623
- fetchGraphs(): Promise<ECLGraph[]> {
624
- return this.WUInfo({ IncludeGraphs: true }).then(() => {
625
- return this.CGraphs;
626
- });
627
- }
628
-
629
- fetchQuery(): Promise<WsWorkunits.Query> {
630
- return this.WUInfo({ IncludeECL: true, TruncateEclTo64k: false }).then(() => {
631
- return this.Query;
632
- });
633
- }
634
-
635
- fetchHelpers(): Promise<WsWorkunits.ECLHelpFile[]> {
636
- return this.WUInfo({ IncludeHelpers: true }).then(() => {
637
- return this.Helpers?.ECLHelpFile || [];
638
- });
639
- }
640
-
641
- fetchAllowedClusters(): Promise<string[]> {
642
- return this.WUInfo({ IncludeAllowedClusters: true }).then(() => {
643
- return this.AllowedClusters?.AllowedCluster || [];
644
- });
645
- }
646
-
647
- fetchTotalClusterTime(): Promise<string> {
648
- return this.WUInfo({ IncludeTotalClusterTime: true }).then(() => {
649
- return this.TotalClusterTime;
650
- });
651
- }
652
-
653
- fetchServiceNames(): Promise<string[]> {
654
- return this.WUInfo({ IncludeServiceNames: true }).then(() => {
655
- return this.ServiceNames?.Item;
656
- });
657
- }
658
-
659
- fetchDetailsMeta(request: RecursivePartial<WsWorkunits.WUDetailsMeta> = {}): Promise<WsWorkunits.WUDetailsMetaResponse> {
660
- return this.WUDetailsMeta(request);
661
- }
662
-
663
- fetchDetailsRaw(request: RecursivePartial<WsWorkunits.WUDetails> = {}): Promise<WsWorkunits.Scope[]> {
664
- return this.WUDetails(request).then(response => response.Scopes.Scope);
665
- }
666
-
667
- normalizeDetails(meta: WsWorkunits.WUDetailsMetaResponse, scopes: WsWorkunits.Scope[]): { meta: WsWorkunits.WUDetailsMetaResponse, columns: { [id: string]: any }, data: IScope[] } {
668
- const columns: { [id: string]: any } = {
669
- id: {
670
- Measure: "label"
671
- },
672
- name: {
673
- Measure: "label"
674
- },
675
- type: {
676
- Measure: "label"
677
- }
678
- };
679
- const activityMap = new Map<number, string>();
680
- for (const activity of meta.Activities?.Activity ?? []) {
681
- activityMap.set(activity.Kind, activity.Name);
682
- }
683
- const data: IScope[] = new Array(scopes.length);
684
- for (let i = 0; i < scopes.length; i++) {
685
- const scope = scopes[i];
686
- const props: { [key: string]: any } = {};
687
- const formattedProps: { [key: string]: any } = {};
688
- if (scope.Id && scope.Properties?.Property) {
689
- for (const scopeProperty of scope.Properties.Property) {
690
- const measure = scopeProperty.Measure;
691
- const name = scopeProperty.Name;
692
- const rawValue = scopeProperty.RawValue;
693
- if (measure === "ns") {
694
- scopeProperty.Measure = "s";
695
- }
696
- if (name === "Kind") {
697
- const rawValueInt = parseInt(rawValue, 10);
698
- scopeProperty.Formatted = activityMap.get(rawValueInt) ?? rawValue;
699
- }
700
- columns[name] = {
701
- Name: scopeProperty.Name,
702
- Measure: scopeProperty.Measure,
703
- Creator: scopeProperty.Creator,
704
- CreatorType: scopeProperty.CreatorType
705
- };
706
- switch (scopeProperty.Measure) {
707
- case "bool":
708
- props[name] = !!+rawValue;
709
- break;
710
- case "sz":
711
- props[name] = +rawValue;
712
- break;
713
- case "s":
714
- props[name] = +rawValue / 1000000000;
715
- break;
716
- case "ns":
717
- props[name] = +rawValue;
718
- break;
719
- case "ts":
720
- props[name] = new Date(+rawValue / 1000).toISOString();
721
- break;
722
- case "cnt":
723
- props[name] = +rawValue;
724
- break;
725
- case "cost":
726
- props[name] = +rawValue / 1000000;
727
- break;
728
- case "node":
729
- props[name] = +rawValue;
730
- break;
731
- case "skw":
732
- props[name] = +rawValue;
733
- break;
734
- case "cpu":
735
- case "ppm":
736
- case "ip":
737
- case "cy":
738
- case "en":
739
- case "txt":
740
- case "id":
741
- case "fname":
742
- default:
743
- props[name] = rawValue;
744
- }
745
- formattedProps[name] = formatNum(scopeProperty.Formatted ?? props[name]);
746
- }
747
- }
748
- const normalizedScope: IScope = {
749
- id: scope.Id,
750
- name: scope.ScopeName,
751
- type: scope.ScopeType,
752
- Kind: scope["Kind"],
753
- Label: scope["Label"],
754
- __formattedProps: formattedProps,
755
- __groupedProps: {},
756
- __groupedRawProps: {},
757
- __StdDevs: 0,
758
- __StdDevsSource: "",
759
- ...props
760
- };
761
- const definitionList = normalizedScope[DEFINITION_LIST];
762
- if (definitionList) {
763
- try {
764
- const parsedList = JSON.parse(definitionList.split("\\").join("\\\\"));
765
- const processedDefinitions: Array<{ filePath: string, line: number, col: number }> = [];
766
-
767
- for (let k = 0; k < parsedList.length; k++) {
768
- const matches = parsedList[k].match(definitionRegex);
769
- if (matches) {
770
- processedDefinitions.push({
771
- filePath: (matches[1] ?? "") + matches[2] + matches[3],
772
- line: parseInt(matches[5], 10),
773
- col: parseInt(matches[6], 10)
774
- });
775
- }
776
- }
777
- normalizedScope[DEFINITION_LIST] = processedDefinitions;
778
- } catch (e) {
779
- logger.error(`Unexpected "DefinitionList": ${definitionList}`);
780
- }
781
- }
782
-
783
- const dedup: DedupProperties = {};
784
- let maxStdDevs = 0;
785
- let maxStdDevsSource = "";
786
- for (const key in normalizedScope) {
787
- if (!key.startsWith("__")) {
788
- const row = formatValues(normalizedScope, key, dedup);
789
- if (row) {
790
- normalizedScope.__groupedProps[row.Key] = row;
791
- if (!isNaN(row.StdDevs) && row.StdDevs > maxStdDevs) {
792
- maxStdDevs = row.StdDevs;
793
- maxStdDevsSource = row.Key;
794
- }
795
- }
796
- }
797
- }
798
- normalizedScope.__StdDevs = maxStdDevs;
799
- normalizedScope.__StdDevsSource = maxStdDevsSource;
800
-
801
- data[i] = normalizedScope;
802
- }
803
- return {
804
- meta,
805
- columns,
806
- data
807
- };
808
- }
809
-
810
- fetchDetailsNormalized(request: RecursivePartial<WsWorkunits.WUDetails> = {}): Promise<{ meta: WsWorkunits.WUDetailsMetaResponse, columns: { [id: string]: any }, data: IScope[] }> {
811
- return Promise.all([this.fetchDetailsMeta(), this.fetchDetailsRaw(request)]).then(promises => {
812
- return this.normalizeDetails(promises[0], promises[1]);
813
- });
814
- }
815
-
816
- fetchInfo(request: Partial<WsWorkunits.WUInfo> = {}): Promise<WsWorkunits.WUInfoResponse> {
817
- return this.WUInfo(request);
818
- }
819
-
820
- fetchDetails(request: RecursivePartial<WsWorkunits.WUDetails> = {}): Promise<Scope[]> {
821
- return this.WUDetails(request).then((response) => {
822
- return response.Scopes.Scope.map((rawScope) => {
823
- return new Scope(this, rawScope);
824
- });
825
- });
826
- }
827
-
828
- fetchDetailsHierarchy(request: Partial<WsWorkunits.WUDetails> = {}): Promise<Scope[]> {
829
- return this.WUDetails(request).then((response) => {
830
- const retVal: Scope[] = [];
831
-
832
- // Recreate Scope Hierarchy and dedup ---
833
- const scopeMap: { [key: string]: Scope } = {};
834
- response.Scopes.Scope.forEach((rawScope) => {
835
- if (scopeMap[rawScope.ScopeName]) {
836
- scopeMap[rawScope.ScopeName].update(rawScope);
837
- return null;
838
- } else {
839
- const scope = new Scope(this, rawScope);
840
- scopeMap[scope.ScopeName] = scope;
841
- return scope;
842
- }
843
- });
844
- for (const key in scopeMap) {
845
- if (scopeMap.hasOwnProperty(key)) {
846
- const scope = scopeMap[key];
847
- const parentScopeID = scope.parentScope();
848
- if (parentScopeID && scopeMap[parentScopeID]) {
849
- scopeMap[parentScopeID].children().push(scope);
850
- } else {
851
- retVal.push(scope);
852
- }
853
- }
854
- }
855
-
856
- return retVal;
857
- });
858
- }
859
-
860
- fetchGraphDetails(graphIDs: string[] = [], rootTypes: string[]): Promise<BaseScope[]> {
861
- return this.fetchDetails({
862
- ScopeFilter: {
863
- MaxDepth: 999999,
864
- Ids: graphIDs,
865
- ScopeTypes: rootTypes,
866
- },
867
- NestedFilter: {
868
- Depth: 999999,
869
- ScopeTypes: ["graph", "subgraph", "activity", "edge", "function"]
870
- },
871
- PropertiesToReturn: {
872
- AllStatistics: true,
873
- AllAttributes: true,
874
- AllHints: true,
875
- AllProperties: true,
876
- AllScopes: true
877
- },
878
- ScopeOptions: {
879
- IncludeId: true,
880
- IncludeScope: true,
881
- IncludeScopeType: true
882
- },
883
- PropertyOptions: {
884
- IncludeName: true,
885
- IncludeRawValue: true,
886
- IncludeFormatted: true,
887
- IncludeMeasure: true,
888
- IncludeCreator: false,
889
- IncludeCreatorType: false
890
- }
891
- });
892
- }
893
-
894
- fetchScopeGraphs(graphIDs: string[] = []): Promise<ScopeGraph> {
895
- return this.fetchGraphDetails(graphIDs, ["graph"]).then((scopes) => {
896
- return createGraph(scopes);
897
- });
898
- }
899
-
900
- fetchTimeElapsed(): Promise<ITimeElapsed[]> {
901
- return this.fetchDetails({
902
- ScopeFilter: {
903
- PropertyFilters: {
904
- PropertyFilter: [{ Name: "TimeElapsed" }]
905
- }
906
- }
907
- }).then((scopes) => {
908
- const scopeInfo: { [key: string]: ITimeElapsed } = {};
909
- scopes.forEach((scope) => {
910
- scopeInfo[scope.ScopeName] = scopeInfo[scope.ScopeName] || {
911
- scope: scope.ScopeName,
912
- start: null,
913
- elapsed: null,
914
- finish: null
915
- };
916
- scope.CAttributes.forEach((attr) => {
917
- if (attr.Name === "TimeElapsed") {
918
- scopeInfo[scope.ScopeName].elapsed = +attr.RawValue;
919
- } else if (attr.Measure === "ts" && attr.Name.indexOf("Started") >= 0) {
920
- scopeInfo[scope.ScopeName].start = attr.Formatted;
921
- }
922
- });
923
- });
924
- // Workaround duplicate scope responses
925
- const retVal: ITimeElapsed[] = [];
926
- for (const key in scopeInfo) {
927
- const scope = scopeInfo[key];
928
- if (scope.start && scope.elapsed) {
929
- const endTime = parser(scope.start);
930
- endTime!.setMilliseconds(endTime!.getMilliseconds() + scope.elapsed / 1000000);
931
- scope.finish = formatter(endTime!);
932
- retVal.push(scope);
933
- }
934
- }
935
- retVal.sort((l, r) => {
936
- if (l.start < r.start) return -1;
937
- if (l.start > r.start) return 1;
938
- return 0;
939
- });
940
- return retVal;
941
- });
942
- }
943
-
944
- // Monitoring ---
945
- protected _monitor(): void {
946
- if (this.isComplete()) {
947
- this._monitorTickCount = 0;
948
- return;
949
- }
950
- super._monitor();
951
- }
952
-
953
- protected _monitorTimeoutDuration(): number {
954
- const retVal = super._monitorTimeoutDuration();
955
- if (this._monitorTickCount <= 1) { // Once
956
- return 1000;
957
- } else if (this._monitorTickCount <= 3) { // Twice
958
- return 3000;
959
- } else if (this._monitorTickCount <= 5) { // Twice
960
- return 5000;
961
- } else if (this._monitorTickCount <= 7) { // Twice
962
- return 10000;
963
- }
964
- return retVal;
965
- }
966
-
967
- // Events ---
968
- on(eventID: WorkunitEvents, propIDorCallback: StateCallback | keyof UWorkunitState, callback?: StatePropCallback): this {
969
- if (this.isCallback(propIDorCallback)) {
970
- switch (eventID) {
971
- case "completed":
972
- super.on("propChanged", "StateID", (changeInfo: IEvent) => {
973
- if (this.isComplete()) {
974
- propIDorCallback([changeInfo]);
975
- }
976
- });
977
- break;
978
- case "changed":
979
- super.on(eventID, propIDorCallback);
980
- break;
981
- default:
982
- }
983
- } else {
984
- switch (eventID) {
985
- case "changed":
986
- super.on(eventID, propIDorCallback, callback!);
987
- break;
988
- default:
989
- }
990
- }
991
- this._monitor();
992
- return this;
993
- }
994
-
995
- watchUntilComplete(callback?: StateCallback): Promise<this> {
996
- return new Promise((resolve, _) => {
997
- const watchHandle = this.watch((changes) => {
998
- if (callback) {
999
- callback(changes);
1000
- }
1001
- if (this.isComplete()) {
1002
- watchHandle.release();
1003
- resolve(this);
1004
- }
1005
- });
1006
- });
1007
- }
1008
-
1009
- watchUntilRunning(callback?: StateCallback): Promise<this> {
1010
- return new Promise((resolve, _) => {
1011
- const watchHandle = this.watch((changes) => {
1012
- if (callback) {
1013
- callback(changes);
1014
- }
1015
- if (this.isComplete() || this.isRunning()) {
1016
- watchHandle.release();
1017
- resolve(this);
1018
- }
1019
- });
1020
- });
1021
- }
1022
-
1023
- // WsWorkunits passthroughs ---
1024
- protected WUQuery(_request: Partial<WsWorkunits.WUQuery> = {}): Promise<WsWorkunits.WUQueryResponse> {
1025
- return this.connection.WUQuery({ ..._request, Wuid: this.Wuid }).then((response) => {
1026
- if (response.Workunits.ECLWorkunit.length === 0) {
1027
- // deleted ---
1028
- this.clearState(this.Wuid);
1029
- this.set("StateID", WUStateID.NotFound);
1030
- } else {
1031
- this.set(response.Workunits.ECLWorkunit[0]);
1032
- }
1033
- return response;
1034
- }).catch((e: ESPExceptions) => {
1035
- // deleted ---
1036
- const wuMissing = e.Exception.some((exception) => {
1037
- if (exception.Code === 20081) {
1038
- this.clearState(this.Wuid);
1039
- this.set("StateID", WUStateID.NotFound);
1040
- return true;
1041
- }
1042
- return false;
1043
- });
1044
- if (!wuMissing) {
1045
- logger.warning(`Unexpected ESP exception: ${e.message}`);
1046
- throw e;
1047
- }
1048
- return {} as WsWorkunits.WUQueryResponse;
1049
- });
1050
- }
1051
-
1052
- protected WUCreate() {
1053
- return this.connection.WUCreate().then((response) => {
1054
- this.set(response.Workunit);
1055
- _workunits.set(this);
1056
- return response;
1057
- });
1058
- }
1059
-
1060
- protected WUInfo(_request: Partial<WsWorkunits.WUInfo> = {}): Promise<WsWorkunits.WUInfoResponse> {
1061
- const includeResults = _request.IncludeResults || _request.IncludeResultsViewNames;
1062
- return this.connection.WUInfo({
1063
- ..._request,
1064
- Wuid: this.Wuid,
1065
- IncludeResults: includeResults,
1066
- IncludeResultsViewNames: includeResults,
1067
- SuppressResultSchemas: false
1068
- }).then((response) => {
1069
- this.set(response.Workunit);
1070
- if (includeResults) {
1071
- this.set({
1072
- ResultViews: response.ResultViews
1073
- } as IWorkunitState);
1074
- }
1075
- return response;
1076
- }).catch((e: ESPExceptions) => {
1077
- // deleted ---
1078
- const wuMissing = e.Exception.some((exception) => {
1079
- if (exception.Code === 20080) {
1080
- this.clearState(this.Wuid);
1081
- this.set("StateID", WUStateID.NotFound);
1082
- return true;
1083
- }
1084
- return false;
1085
- });
1086
- if (!wuMissing) {
1087
- logger.warning(`Unexpected ESP exception: ${e.message}`);
1088
- throw e;
1089
- }
1090
- return {} as WsWorkunits.WUInfoResponse;
1091
- });
1092
- }
1093
-
1094
- protected WUResubmit(request: Partial<WsWorkunits.WUResubmit>): Promise<WsWorkunits.WUResubmitResponse> {
1095
- return this.connection.WUResubmit(deepMixinT<WsWorkunits.WUResubmit>({}, request, {
1096
- Wuids: { Item: [this.Wuid] }
1097
- }));
1098
- }
1099
-
1100
- protected WUDetailsMeta(request: Partial<WsWorkunits.WUDetailsMeta>): Promise<WsWorkunits.WUDetailsMetaResponse> {
1101
- return this.connection.WUDetailsMeta(request);
1102
- }
1103
-
1104
- protected WUDetails(request: RecursivePartial<WsWorkunits.WUDetails>): Promise<WsWorkunits.WUDetailsResponse> {
1105
- return this.connection.WUDetails(deepMixinT<WsWorkunits.WUDetails>({
1106
- ScopeFilter: {
1107
- MaxDepth: 9999
1108
- },
1109
- ScopeOptions: {
1110
- IncludeMatchedScopesInResults: true,
1111
- IncludeScope: true,
1112
- IncludeId: false,
1113
- IncludeScopeType: false
1114
- },
1115
- PropertyOptions: {
1116
- IncludeName: true,
1117
- IncludeRawValue: false,
1118
- IncludeFormatted: true,
1119
- IncludeMeasure: true,
1120
- IncludeCreator: false,
1121
- IncludeCreatorType: false
1122
- }
1123
- }, request, { WUID: this.Wuid })).then((response) => {
1124
- return deepMixinT<WsWorkunits.WUDetailsResponse>({
1125
- Scopes: {
1126
- Scope: []
1127
- }
1128
- }, response);
1129
- });
1130
- }
1131
-
1132
- protected WUAction(actionType: WsWorkunits.ECLWUActions): Promise<WsWorkunits.WUActionResponse> {
1133
- return this.connection.WUAction({
1134
- Wuids: { Item: [this.Wuid] },
1135
- WUActionType: actionType
1136
- }).then((response) => {
1137
- return this.refresh().then(() => {
1138
- this._monitor();
1139
- return response;
1140
- });
1141
- });
1142
- }
1143
-
1144
- publish(name?: string) {
1145
- return this.connection.WUPublishWorkunit({
1146
- Wuid: this.Wuid,
1147
- Cluster: this.Cluster,
1148
- JobName: name || this.Jobname,
1149
- AllowForeignFiles: true,
1150
- Activate: WsWorkunits.WUQueryActivationMode.ActivateQuery,
1151
- Wait: 5000
1152
- });
1153
- }
1154
-
1155
- publishEx(request: Partial<WsWorkunits.WUPublishWorkunit>) {
1156
- const service = new WorkunitsServiceEx({ baseUrl: "" });
1157
- const publishRequest = {
1158
- Wuid: this.Wuid,
1159
- Cluster: this.Cluster,
1160
- JobName: this.Jobname,
1161
- AllowForeignFiles: true,
1162
- Activate: 1,
1163
- Wait: 5000,
1164
- ...request
1165
- };
1166
- return service.WUPublishWorkunitEx(publishRequest);
1167
- }
1168
-
1169
- protected WUCDebug(command: string, opts: any = {}): Promise<XMLNode | null> {
1170
- let optsStr = "";
1171
- for (const key in opts) {
1172
- if (opts.hasOwnProperty(key)) {
1173
- optsStr += ` ${key}='${opts[key]}'`;
1174
- }
1175
- }
1176
- return this.connection.WUCDebugEx({
1177
- Wuid: this.Wuid,
1178
- Command: `<debug:${command} uid='${this.Wuid}'${optsStr}/>`
1179
- }).then((response) => {
1180
- return response;
1181
- });
1182
- }
1183
-
1184
- debug(command: string, opts?: object): Promise<XMLNode> {
1185
- if (!this.isDebugging()) {
1186
- return Promise.resolve(new XMLNode(command));
1187
- }
1188
- return this.WUCDebug(command, opts).then((response: XMLNode) => {
1189
- const retVal: XMLNode[] = response.children(command);
1190
- if (retVal.length) {
1191
- return retVal[0];
1192
- }
1193
- return new XMLNode(command);
1194
- }).catch((_) => {
1195
- logger.error(_);
1196
- return Promise.resolve(new XMLNode(command));
1197
- });
1198
- }
1199
-
1200
- debugStatus(): Promise<XMLNode> {
1201
- if (!this.isDebugging()) {
1202
- return Promise.resolve<any>({
1203
- DebugState: { state: "unknown" }
1204
- });
1205
- }
1206
- return this.debug("status").then((response) => {
1207
- const debugState = { ...this.DebugState, ...response.$ };
1208
- this.set({
1209
- DebugState: debugState
1210
- });
1211
- return response;
1212
- });
1213
- }
1214
-
1215
- debugContinue(mode = ""): Promise<XMLNode> {
1216
- return this.debug("continue", {
1217
- mode
1218
- });
1219
- }
1220
-
1221
- debugStep(mode: string): Promise<XMLNode> {
1222
- return this.debug("step", {
1223
- mode
1224
- });
1225
- }
1226
-
1227
- debugPause(): Promise<XMLNode> {
1228
- return this.debug("interrupt");
1229
- }
1230
-
1231
- debugQuit(): Promise<XMLNode> {
1232
- return this.debug("quit");
1233
- }
1234
-
1235
- debugDeleteAllBreakpoints(): Promise<XMLNode> {
1236
- return this.debug("delete", {
1237
- idx: 0
1238
- });
1239
- }
1240
-
1241
- protected debugBreakpointResponseParser(rootNode: StringAnyMap) {
1242
- return rootNode.children().map((childNode: XMLNode) => {
1243
- if (childNode.name === "break") {
1244
- return childNode.$;
1245
- }
1246
- });
1247
- }
1248
-
1249
- debugBreakpointAdd(id: string, mode: string, action: string): Promise<XMLNode> {
1250
- return this.debug("breakpoint", {
1251
- id,
1252
- mode,
1253
- action
1254
- }).then((rootNode) => {
1255
- return this.debugBreakpointResponseParser(rootNode);
1256
- });
1257
- }
1258
-
1259
- debugBreakpointList(): Promise<any[]> {
1260
- return this.debug("list").then((rootNode) => {
1261
- return this.debugBreakpointResponseParser(rootNode);
1262
- });
1263
- }
1264
-
1265
- debugGraph(): Promise<XGMMLGraph> {
1266
- if (this._debugAllGraph && this.DebugState["_prevGraphSequenceNum"] === this.DebugState["graphSequenceNum"]) {
1267
- return Promise.resolve(this._debugAllGraph);
1268
- }
1269
- return this.debug("graph", { name: "all" }).then((response) => {
1270
- this.DebugState["_prevGraphSequenceNum"] = this.DebugState["graphSequenceNum"];
1271
- this._debugAllGraph = createXGMMLGraph(this.Wuid, response);
1272
- return this._debugAllGraph;
1273
- });
1274
- }
1275
-
1276
- debugBreakpointValid(path: string): Promise<IECLDefintion[]> {
1277
- return this.debugGraph().then((graph) => {
1278
- return breakpointLocations(graph, path);
1279
- });
1280
- }
1281
-
1282
- debugPrint(edgeID: string, startRow: number = 0, numRows: number = 10): Promise<StringAnyMap[]> {
1283
- return this.debug("print", {
1284
- edgeID,
1285
- startRow,
1286
- numRows
1287
- }).then((response: XMLNode) => {
1288
- return response.children().map((rowNode) => {
1289
- const retVal: StringAnyMap = {};
1290
- rowNode.children().forEach((cellNode) => {
1291
- retVal[cellNode.name] = cellNode.content;
1292
- });
1293
- return retVal;
1294
- });
1295
- });
1296
- }
1297
- }
1298
-
1299
- export interface IECLDefintion {
1300
- id: string;
1301
- file: string;
1302
- line: number;
1303
- column: number;
1304
- }
1305
-
1306
- const ATTR_DEFINITION = "definition";
1307
-
1308
- function hasECLDefinition(vertex: XGMMLVertex): boolean {
1309
- return vertex._![ATTR_DEFINITION] !== undefined;
1310
- }
1311
-
1312
- function getECLDefinition(vertex: XGMMLVertex): IECLDefintion {
1313
- const match = /([a-z]:\\(?:[-\w\.\d]+\\)*(?:[-\w\.\d]+)?|(?:\/[\w\.\-]+)+)\((\d*),(\d*)\)/.exec(vertex._![ATTR_DEFINITION]);
1314
- if (match) {
1315
- const [, _file, _row, _col] = match;
1316
- _file.replace(/\/\.\//g, "/");
1317
- return {
1318
- id: vertex._!["id"],
1319
- file: _file,
1320
- line: +_row,
1321
- column: +_col
1322
- };
1323
- }
1324
- throw new Error(`Bad definition: ${vertex._![ATTR_DEFINITION]}`);
1325
- }
1326
-
1327
- function breakpointLocations(graph: XGMMLGraph, path?: string): IECLDefintion[] {
1328
- const retVal: IECLDefintion[] = [];
1329
- for (const vertex of graph.vertices) {
1330
- if (hasECLDefinition(vertex)) {
1331
- const definition = getECLDefinition(vertex);
1332
- if (definition && !path || path === definition.file) {
1333
- retVal.push(definition);
1334
- }
1335
- }
1336
- }
1337
- return retVal.sort((l, r) => {
1338
- return l.line - r.line;
1339
- });
1340
- }
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.ts";
5
+ import { ESPExceptions } from "../espConnection.ts";
6
+ import { WsSMC } from "../services/wsSMC.ts";
7
+ import * as WsTopology from "../services/wsTopology.ts";
8
+ import { WsWorkunits, WUStateID, WorkunitsService, WorkunitsServiceEx, WUUpdate } from "../services/wsWorkunits.ts";
9
+ import { createGraph, createXGMMLGraph, ECLGraph, GraphCache, ScopeGraph, XGMMLGraph, XGMMLVertex } from "./graph.ts";
10
+ import { Resource } from "./resource.ts";
11
+ import { Result, ResultCache } from "./result.ts";
12
+ import { BaseScope, Scope } from "./scope.ts";
13
+ import { SourceFile } from "./sourceFile.ts";
14
+ import { Timer } from "./timer.ts";
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
+ get CostSavingPotential(): number { return this.get("CostSavingPotential"); }
330
+
331
+ // Factories ---
332
+ static create(optsConnection: IOptions | IConnection): Promise<Workunit> {
333
+ const retVal: Workunit = new Workunit(optsConnection);
334
+ return retVal.connection.WUCreate().then((response) => {
335
+ _workunits.set(retVal);
336
+ retVal.set(response.Workunit);
337
+ return retVal;
338
+ });
339
+ }
340
+
341
+ static attach(optsConnection: IOptions | IConnection, wuid: string, state?: IWorkunitState): Workunit {
342
+ const retVal: Workunit = _workunits.get({ BaseUrl: optsConnection.baseUrl, Wuid: wuid }, () => {
343
+ return new Workunit(optsConnection, wuid);
344
+ });
345
+ if (state) {
346
+ retVal.set(state);
347
+ }
348
+ return retVal;
349
+ }
350
+
351
+ static existsLocal(baseUrl: string, wuid: string): boolean {
352
+ return _workunits.has({ BaseUrl: baseUrl, Wuid: wuid });
353
+ }
354
+
355
+ static submit(server: IOptions | IConnection, target: string, ecl: string, compileOnly = false): Promise<Workunit> {
356
+ return Workunit.create(server).then((wu) => {
357
+ return wu.update({ QueryText: ecl });
358
+ }).then((wu) => {
359
+ return compileOnly ? wu.submit(target, WUUpdate.Action.Compile) : wu.submit(target);
360
+ });
361
+ }
362
+
363
+ static compile(server: IOptions | IConnection, target: string, ecl: string): Promise<Workunit> {
364
+ return Workunit.submit(server, target, ecl, true);
365
+ }
366
+
367
+ static query(server: IOptions | IConnection, opts: Partial<WsWorkunits.WUQuery>): Promise<Workunit[]> {
368
+ const wsWorkunits = new WorkunitsService(server);
369
+ return wsWorkunits.WUQuery(opts).then((response) => {
370
+ return response.Workunits.ECLWorkunit.map(function (wu) {
371
+ return Workunit.attach(server, wu.Wuid, wu);
372
+ });
373
+ });
374
+ }
375
+
376
+ // --- --- ---
377
+ protected constructor(optsConnection: IOptions | IConnection, wuid?: string) {
378
+ super();
379
+ this.connection = new WorkunitsService(optsConnection);
380
+ this.topologyConnection = new WsTopology.TopologyService(optsConnection);
381
+ this.clearState(wuid);
382
+ }
383
+
384
+ clearState(wuid?: string) {
385
+ this.clear({
386
+ Wuid: wuid,
387
+ StateID: WUStateID.Unknown
388
+ });
389
+ }
390
+
391
+ update(request: Partial<WsWorkunits.WUUpdate>): Promise<Workunit> {
392
+ return this.connection.WUUpdate({
393
+ ...request,
394
+ ...{
395
+ Wuid: this.Wuid,
396
+ StateOrig: this.StateID,
397
+ JobnameOrig: this.Jobname,
398
+ DescriptionOrig: this.Description,
399
+ ProtectedOrig: this.Protected,
400
+ ClusterOrig: this.Cluster
401
+ }
402
+ }).then((response) => {
403
+ this.set(response.Workunit);
404
+ return this;
405
+ });
406
+ }
407
+
408
+ submit(_cluster?: string, action: WUUpdate.Action = WUUpdate.Action.Run, resultLimit?: number): Promise<Workunit> {
409
+ let clusterPromise;
410
+ if (_cluster !== void 0) {
411
+ clusterPromise = Promise.resolve(_cluster);
412
+ } else {
413
+ clusterPromise = this.topologyConnection.DefaultTpLogicalClusterQuery().then((response) => {
414
+ return response.Name;
415
+ });
416
+ }
417
+
418
+ this._debugMode = false;
419
+ if (action === WUUpdate.Action.Debug) {
420
+ action = WUUpdate.Action.Run;
421
+ this._debugMode = true;
422
+ }
423
+
424
+ return clusterPromise.then((cluster) => {
425
+ return this.connection.WUUpdate({
426
+ Wuid: this.Wuid,
427
+ Action: action,
428
+ ResultLimit: resultLimit,
429
+ DebugValues: {
430
+ DebugValue: [
431
+ {
432
+ Name: "Debug",
433
+ Value: this._debugMode ? "1" : ""
434
+ }
435
+ ]
436
+ }
437
+ }).then((response) => {
438
+ this.set(response.Workunit);
439
+ this._submitAction = action;
440
+ return this.connection.WUSubmit({ Wuid: this.Wuid, Cluster: cluster });
441
+ });
442
+ }).then(() => {
443
+ return this;
444
+ });
445
+ }
446
+
447
+ isComplete(): boolean {
448
+ switch (this.StateID) {
449
+ case WUStateID.Compiled:
450
+ return this.ActionEx === "compile" || this._submitAction === WUUpdate.Action.Compile;
451
+ case WUStateID.Completed:
452
+ case WUStateID.Failed:
453
+ case WUStateID.Aborted:
454
+ case WUStateID.NotFound:
455
+ return true;
456
+ default:
457
+ }
458
+ return false;
459
+ }
460
+
461
+ isFailed() {
462
+ switch (this.StateID) {
463
+ case WUStateID.Aborted:
464
+ case WUStateID.Failed:
465
+ return true;
466
+ default:
467
+ }
468
+ return false;
469
+ }
470
+
471
+ isDeleted() {
472
+ switch (this.StateID) {
473
+ case WUStateID.NotFound:
474
+ return true;
475
+ default:
476
+ }
477
+ return false;
478
+ }
479
+
480
+ isDebugging() {
481
+ switch (this.StateID) {
482
+ case WUStateID.DebugPaused:
483
+ case WUStateID.DebugRunning:
484
+ return true;
485
+ default:
486
+ }
487
+ return this._debugMode;
488
+ }
489
+
490
+ isRunning(): boolean {
491
+ switch (this.StateID) {
492
+ case WUStateID.Compiled:
493
+ case WUStateID.Running:
494
+ case WUStateID.Aborting:
495
+ case WUStateID.Blocked:
496
+ case WUStateID.DebugPaused:
497
+ case WUStateID.DebugRunning:
498
+ return true;
499
+ default:
500
+ }
501
+ return false;
502
+ }
503
+
504
+ setToFailed() {
505
+ return this.WUAction(WsWorkunits.ECLWUActions.SetToFailed);
506
+ }
507
+
508
+ pause() {
509
+ return this.WUAction(WsWorkunits.ECLWUActions.Pause);
510
+ }
511
+
512
+ pauseNow() {
513
+ return this.WUAction(WsWorkunits.ECLWUActions.PauseNow);
514
+ }
515
+
516
+ resume() {
517
+ return this.WUAction(WsWorkunits.ECLWUActions.Resume);
518
+ }
519
+
520
+ abort() {
521
+ return this.WUAction(WsWorkunits.ECLWUActions.Abort);
522
+ }
523
+
524
+ protect() {
525
+ return this.WUAction(WsWorkunits.ECLWUActions.Protect);
526
+ }
527
+
528
+ unprotect() {
529
+ return this.WUAction(WsWorkunits.ECLWUActions.Unprotect);
530
+ }
531
+
532
+ delete() {
533
+ return this.WUAction(WsWorkunits.ECLWUActions.Delete);
534
+ }
535
+
536
+ restore() {
537
+ return this.WUAction(WsWorkunits.ECLWUActions.Restore);
538
+ }
539
+
540
+ deschedule() {
541
+ return this.WUAction(WsWorkunits.ECLWUActions.Deschedule);
542
+ }
543
+
544
+ reschedule() {
545
+ return this.WUAction(WsWorkunits.ECLWUActions.Reschedule);
546
+ }
547
+
548
+ resubmit(): Promise<Workunit> {
549
+ return this.WUResubmit({
550
+ CloneWorkunit: false,
551
+ ResetWorkflow: false
552
+ }).then(() => {
553
+ this.clearState(this.Wuid);
554
+ return this.refresh().then(() => {
555
+ this._monitor();
556
+ return this;
557
+ });
558
+ });
559
+ }
560
+
561
+ clone(): Promise<Workunit> {
562
+ return this.WUResubmit({
563
+ CloneWorkunit: true,
564
+ ResetWorkflow: false
565
+ }).then((response) => {
566
+ return Workunit.attach(this.connection.opts(), response.WUs.WU[0].WUID)
567
+ .refresh()
568
+ ;
569
+ });
570
+ }
571
+
572
+ async refreshState(): Promise<this> {
573
+ await this.WUQuery();
574
+ // Ensure "isComplete" is correct for WUs that are only "Compiled".
575
+ if (this.StateID === WUStateID.Compiled && !this.ActionEx && !this._submitAction) {
576
+ await this.refreshInfo();
577
+ }
578
+ return this;
579
+ }
580
+
581
+ async refreshInfo(request?: Partial<WsWorkunits.WUInfo>): Promise<this> {
582
+ await this.WUInfo(request);
583
+ return this;
584
+ }
585
+
586
+ async refreshDebug(): Promise<this> {
587
+ await this.debugStatus();
588
+ return this;
589
+ }
590
+
591
+ async refresh(full: boolean = false, request?: Partial<WsWorkunits.WUInfo>): Promise<this> {
592
+ if (full) {
593
+ await Promise.all([this.refreshInfo(request), this.refreshDebug()]);
594
+ } else {
595
+ await this.refreshState();
596
+ }
597
+ return this;
598
+ }
599
+
600
+ eclExceptions(): WsWorkunits.ECLException[] {
601
+ return this.Exceptions.ECLException;
602
+ }
603
+
604
+ fetchArchive(): Promise<string> {
605
+ return this.connection.WUFileEx({
606
+ Wuid: this.Wuid,
607
+ Type: "ArchiveQuery"
608
+ });
609
+ }
610
+
611
+ fetchECLExceptions(): Promise<WsWorkunits.ECLException[]> {
612
+ return this.WUInfo({ IncludeExceptions: true }).then(() => {
613
+ return this.eclExceptions();
614
+ });
615
+ }
616
+
617
+ fetchResults(): Promise<Result[]> {
618
+ return this.WUInfo({ IncludeResults: true }).then(() => {
619
+ return this.CResults;
620
+ });
621
+ }
622
+
623
+ fetchGraphs(): Promise<ECLGraph[]> {
624
+ return this.WUInfo({ IncludeGraphs: true }).then(() => {
625
+ return this.CGraphs;
626
+ });
627
+ }
628
+
629
+ fetchQuery(): Promise<WsWorkunits.Query> {
630
+ return this.WUInfo({ IncludeECL: true, TruncateEclTo64k: false }).then(() => {
631
+ return this.Query;
632
+ });
633
+ }
634
+
635
+ fetchHelpers(): Promise<WsWorkunits.ECLHelpFile[]> {
636
+ return this.WUInfo({ IncludeHelpers: true }).then(() => {
637
+ return this.Helpers?.ECLHelpFile || [];
638
+ });
639
+ }
640
+
641
+ fetchAllowedClusters(): Promise<string[]> {
642
+ return this.WUInfo({ IncludeAllowedClusters: true }).then(() => {
643
+ return this.AllowedClusters?.AllowedCluster || [];
644
+ });
645
+ }
646
+
647
+ fetchTotalClusterTime(): Promise<string> {
648
+ return this.WUInfo({ IncludeTotalClusterTime: true }).then(() => {
649
+ return this.TotalClusterTime;
650
+ });
651
+ }
652
+
653
+ fetchServiceNames(): Promise<string[]> {
654
+ return this.WUInfo({ IncludeServiceNames: true }).then(() => {
655
+ return this.ServiceNames?.Item;
656
+ });
657
+ }
658
+
659
+ fetchDetailsMeta(request: RecursivePartial<WsWorkunits.WUDetailsMeta> = {}): Promise<WsWorkunits.WUDetailsMetaResponse> {
660
+ return this.WUDetailsMeta(request);
661
+ }
662
+
663
+ fetchDetailsRaw(request: RecursivePartial<WsWorkunits.WUDetails> = {}): Promise<WsWorkunits.Scope[]> {
664
+ return this.WUDetails(request).then(response => response.Scopes.Scope);
665
+ }
666
+
667
+ normalizeDetails(meta: WsWorkunits.WUDetailsMetaResponse, scopes: WsWorkunits.Scope[]): { meta: WsWorkunits.WUDetailsMetaResponse, columns: { [id: string]: any }, data: IScope[] } {
668
+ const columns: { [id: string]: any } = {
669
+ id: {
670
+ Measure: "label"
671
+ },
672
+ name: {
673
+ Measure: "label"
674
+ },
675
+ type: {
676
+ Measure: "label"
677
+ }
678
+ };
679
+ const activityMap = new Map<number, string>();
680
+ for (const activity of meta.Activities?.Activity ?? []) {
681
+ activityMap.set(activity.Kind, activity.Name);
682
+ }
683
+ const data: IScope[] = new Array(scopes.length);
684
+ for (let i = 0; i < scopes.length; i++) {
685
+ const scope = scopes[i];
686
+ const props: { [key: string]: any } = {};
687
+ const formattedProps: { [key: string]: any } = {};
688
+ if (scope.Id && scope.Properties?.Property) {
689
+ for (const scopeProperty of scope.Properties.Property) {
690
+ const measure = scopeProperty.Measure;
691
+ const name = scopeProperty.Name;
692
+ const rawValue = scopeProperty.RawValue;
693
+ if (measure === "ns") {
694
+ scopeProperty.Measure = "s";
695
+ }
696
+ if (name === "Kind") {
697
+ const rawValueInt = parseInt(rawValue, 10);
698
+ scopeProperty.Formatted = activityMap.get(rawValueInt) ?? rawValue;
699
+ }
700
+ columns[name] = {
701
+ Name: scopeProperty.Name,
702
+ Measure: scopeProperty.Measure,
703
+ Creator: scopeProperty.Creator,
704
+ CreatorType: scopeProperty.CreatorType
705
+ };
706
+ switch (scopeProperty.Measure) {
707
+ case "bool":
708
+ props[name] = !!+rawValue;
709
+ break;
710
+ case "sz":
711
+ props[name] = +rawValue;
712
+ break;
713
+ case "s":
714
+ props[name] = +rawValue / 1000000000;
715
+ break;
716
+ case "ns":
717
+ props[name] = +rawValue;
718
+ break;
719
+ case "ts":
720
+ props[name] = new Date(+rawValue / 1000).toISOString();
721
+ break;
722
+ case "cnt":
723
+ props[name] = +rawValue;
724
+ break;
725
+ case "cost":
726
+ props[name] = +rawValue / 1000000;
727
+ break;
728
+ case "node":
729
+ props[name] = +rawValue;
730
+ break;
731
+ case "skw":
732
+ props[name] = +rawValue;
733
+ break;
734
+ case "cpu":
735
+ case "ppm":
736
+ case "ip":
737
+ case "cy":
738
+ case "en":
739
+ case "txt":
740
+ case "id":
741
+ case "fname":
742
+ default:
743
+ props[name] = rawValue;
744
+ }
745
+ formattedProps[name] = formatNum(scopeProperty.Formatted ?? props[name]);
746
+ }
747
+ }
748
+ const normalizedScope: IScope = {
749
+ id: scope.Id,
750
+ name: scope.ScopeName,
751
+ type: scope.ScopeType,
752
+ Kind: scope["Kind"],
753
+ Label: scope["Label"],
754
+ __formattedProps: formattedProps,
755
+ __groupedProps: {},
756
+ __groupedRawProps: {},
757
+ __StdDevs: 0,
758
+ __StdDevsSource: "",
759
+ ...props
760
+ };
761
+ const definitionList = normalizedScope[DEFINITION_LIST];
762
+ if (definitionList) {
763
+ try {
764
+ const parsedList = JSON.parse(definitionList.split("\\").join("\\\\"));
765
+ const processedDefinitions: Array<{ filePath: string, line: number, col: number }> = [];
766
+
767
+ for (let k = 0; k < parsedList.length; k++) {
768
+ const matches = parsedList[k].match(definitionRegex);
769
+ if (matches) {
770
+ processedDefinitions.push({
771
+ filePath: (matches[1] ?? "") + matches[2] + matches[3],
772
+ line: parseInt(matches[5], 10),
773
+ col: parseInt(matches[6], 10)
774
+ });
775
+ }
776
+ }
777
+ normalizedScope[DEFINITION_LIST] = processedDefinitions;
778
+ } catch (e) {
779
+ logger.error(`Unexpected "DefinitionList": ${definitionList}`);
780
+ }
781
+ }
782
+
783
+ const dedup: DedupProperties = {};
784
+ let maxStdDevs = 0;
785
+ let maxStdDevsSource = "";
786
+ for (const key in normalizedScope) {
787
+ if (!key.startsWith("__")) {
788
+ const row = formatValues(normalizedScope, key, dedup);
789
+ if (row) {
790
+ normalizedScope.__groupedProps[row.Key] = row;
791
+ if (!isNaN(row.StdDevs) && row.StdDevs > maxStdDevs) {
792
+ maxStdDevs = row.StdDevs;
793
+ maxStdDevsSource = row.Key;
794
+ }
795
+ }
796
+ }
797
+ }
798
+ normalizedScope.__StdDevs = maxStdDevs;
799
+ normalizedScope.__StdDevsSource = maxStdDevsSource;
800
+
801
+ data[i] = normalizedScope;
802
+ }
803
+ return {
804
+ meta,
805
+ columns,
806
+ data
807
+ };
808
+ }
809
+
810
+ fetchDetailsNormalized(request: RecursivePartial<WsWorkunits.WUDetails> = {}): Promise<{ meta: WsWorkunits.WUDetailsMetaResponse, columns: { [id: string]: any }, data: IScope[] }> {
811
+ return Promise.all([this.fetchDetailsMeta(), this.fetchDetailsRaw(request)]).then(promises => {
812
+ return this.normalizeDetails(promises[0], promises[1]);
813
+ });
814
+ }
815
+
816
+ fetchInfo(request: Partial<WsWorkunits.WUInfo> = {}): Promise<WsWorkunits.WUInfoResponse> {
817
+ return this.WUInfo(request);
818
+ }
819
+
820
+ fetchDetails(request: RecursivePartial<WsWorkunits.WUDetails> = {}): Promise<Scope[]> {
821
+ return this.WUDetails(request).then((response) => {
822
+ return response.Scopes.Scope.map((rawScope) => {
823
+ return new Scope(this, rawScope);
824
+ });
825
+ });
826
+ }
827
+
828
+ fetchDetailsHierarchy(request: Partial<WsWorkunits.WUDetails> = {}): Promise<Scope[]> {
829
+ return this.WUDetails(request).then((response) => {
830
+ const retVal: Scope[] = [];
831
+
832
+ // Recreate Scope Hierarchy and dedup ---
833
+ const scopeMap: { [key: string]: Scope } = {};
834
+ response.Scopes.Scope.forEach((rawScope) => {
835
+ if (scopeMap[rawScope.ScopeName]) {
836
+ scopeMap[rawScope.ScopeName].update(rawScope);
837
+ return null;
838
+ } else {
839
+ const scope = new Scope(this, rawScope);
840
+ scopeMap[scope.ScopeName] = scope;
841
+ return scope;
842
+ }
843
+ });
844
+ for (const key in scopeMap) {
845
+ if (scopeMap.hasOwnProperty(key)) {
846
+ const scope = scopeMap[key];
847
+ const parentScopeID = scope.parentScope();
848
+ if (parentScopeID && scopeMap[parentScopeID]) {
849
+ scopeMap[parentScopeID].children().push(scope);
850
+ } else {
851
+ retVal.push(scope);
852
+ }
853
+ }
854
+ }
855
+
856
+ return retVal;
857
+ });
858
+ }
859
+
860
+ fetchGraphDetails(graphIDs: string[] = [], rootTypes: string[]): Promise<BaseScope[]> {
861
+ return this.fetchDetails({
862
+ ScopeFilter: {
863
+ MaxDepth: 999999,
864
+ Ids: graphIDs,
865
+ ScopeTypes: rootTypes,
866
+ },
867
+ NestedFilter: {
868
+ Depth: 999999,
869
+ ScopeTypes: ["graph", "subgraph", "activity", "edge", "function"]
870
+ },
871
+ PropertiesToReturn: {
872
+ AllStatistics: true,
873
+ AllAttributes: true,
874
+ AllHints: true,
875
+ AllProperties: true,
876
+ AllScopes: true
877
+ },
878
+ ScopeOptions: {
879
+ IncludeId: true,
880
+ IncludeScope: true,
881
+ IncludeScopeType: true
882
+ },
883
+ PropertyOptions: {
884
+ IncludeName: true,
885
+ IncludeRawValue: true,
886
+ IncludeFormatted: true,
887
+ IncludeMeasure: true,
888
+ IncludeCreator: false,
889
+ IncludeCreatorType: false
890
+ }
891
+ });
892
+ }
893
+
894
+ fetchScopeGraphs(graphIDs: string[] = []): Promise<ScopeGraph> {
895
+ return this.fetchGraphDetails(graphIDs, ["graph"]).then((scopes) => {
896
+ return createGraph(scopes);
897
+ });
898
+ }
899
+
900
+ fetchTimeElapsed(): Promise<ITimeElapsed[]> {
901
+ return this.fetchDetails({
902
+ ScopeFilter: {
903
+ PropertyFilters: {
904
+ PropertyFilter: [{ Name: "TimeElapsed" }]
905
+ }
906
+ }
907
+ }).then((scopes) => {
908
+ const scopeInfo: { [key: string]: ITimeElapsed } = {};
909
+ scopes.forEach((scope) => {
910
+ scopeInfo[scope.ScopeName] = scopeInfo[scope.ScopeName] || {
911
+ scope: scope.ScopeName,
912
+ start: null,
913
+ elapsed: null,
914
+ finish: null
915
+ };
916
+ scope.CAttributes.forEach((attr) => {
917
+ if (attr.Name === "TimeElapsed") {
918
+ scopeInfo[scope.ScopeName].elapsed = +attr.RawValue;
919
+ } else if (attr.Measure === "ts" && attr.Name.indexOf("Started") >= 0) {
920
+ scopeInfo[scope.ScopeName].start = attr.Formatted;
921
+ }
922
+ });
923
+ });
924
+ // Workaround duplicate scope responses
925
+ const retVal: ITimeElapsed[] = [];
926
+ for (const key in scopeInfo) {
927
+ const scope = scopeInfo[key];
928
+ if (scope.start && scope.elapsed) {
929
+ const endTime = parser(scope.start);
930
+ endTime!.setMilliseconds(endTime!.getMilliseconds() + scope.elapsed / 1000000);
931
+ scope.finish = formatter(endTime!);
932
+ retVal.push(scope);
933
+ }
934
+ }
935
+ retVal.sort((l, r) => {
936
+ if (l.start < r.start) return -1;
937
+ if (l.start > r.start) return 1;
938
+ return 0;
939
+ });
940
+ return retVal;
941
+ });
942
+ }
943
+
944
+ // Monitoring ---
945
+ protected _monitor(): void {
946
+ if (this.isComplete()) {
947
+ this._monitorTickCount = 0;
948
+ return;
949
+ }
950
+ super._monitor();
951
+ }
952
+
953
+ protected _monitorTimeoutDuration(): number {
954
+ const retVal = super._monitorTimeoutDuration();
955
+ if (this._monitorTickCount <= 1) { // Once
956
+ return 1000;
957
+ } else if (this._monitorTickCount <= 3) { // Twice
958
+ return 3000;
959
+ } else if (this._monitorTickCount <= 5) { // Twice
960
+ return 5000;
961
+ } else if (this._monitorTickCount <= 7) { // Twice
962
+ return 10000;
963
+ }
964
+ return retVal;
965
+ }
966
+
967
+ // Events ---
968
+ on(eventID: WorkunitEvents, propIDorCallback: StateCallback | keyof UWorkunitState, callback?: StatePropCallback): this {
969
+ if (this.isCallback(propIDorCallback)) {
970
+ switch (eventID) {
971
+ case "completed":
972
+ super.on("propChanged", "StateID", (changeInfo: IEvent) => {
973
+ if (this.isComplete()) {
974
+ propIDorCallback([changeInfo]);
975
+ }
976
+ });
977
+ break;
978
+ case "changed":
979
+ super.on(eventID, propIDorCallback);
980
+ break;
981
+ default:
982
+ }
983
+ } else {
984
+ switch (eventID) {
985
+ case "changed":
986
+ super.on(eventID, propIDorCallback, callback!);
987
+ break;
988
+ default:
989
+ }
990
+ }
991
+ this._monitor();
992
+ return this;
993
+ }
994
+
995
+ watchUntilComplete(callback?: StateCallback): Promise<this> {
996
+ return new Promise((resolve, _) => {
997
+ const watchHandle = this.watch((changes) => {
998
+ if (callback) {
999
+ callback(changes);
1000
+ }
1001
+ if (this.isComplete()) {
1002
+ watchHandle.release();
1003
+ resolve(this);
1004
+ }
1005
+ });
1006
+ });
1007
+ }
1008
+
1009
+ watchUntilRunning(callback?: StateCallback): Promise<this> {
1010
+ return new Promise((resolve, _) => {
1011
+ const watchHandle = this.watch((changes) => {
1012
+ if (callback) {
1013
+ callback(changes);
1014
+ }
1015
+ if (this.isComplete() || this.isRunning()) {
1016
+ watchHandle.release();
1017
+ resolve(this);
1018
+ }
1019
+ });
1020
+ });
1021
+ }
1022
+
1023
+ // WsWorkunits passthroughs ---
1024
+ protected WUQuery(_request: Partial<WsWorkunits.WUQuery> = {}): Promise<WsWorkunits.WUQueryResponse> {
1025
+ return this.connection.WUQuery({ ..._request, Wuid: this.Wuid }).then((response) => {
1026
+ if (response.Workunits.ECLWorkunit.length === 0) {
1027
+ // deleted ---
1028
+ this.clearState(this.Wuid);
1029
+ this.set("StateID", WUStateID.NotFound);
1030
+ } else {
1031
+ this.set(response.Workunits.ECLWorkunit[0]);
1032
+ }
1033
+ return response;
1034
+ }).catch((e: ESPExceptions) => {
1035
+ // deleted ---
1036
+ const wuMissing = e.Exception.some((exception) => {
1037
+ if (exception.Code === 20081) {
1038
+ this.clearState(this.Wuid);
1039
+ this.set("StateID", WUStateID.NotFound);
1040
+ return true;
1041
+ }
1042
+ return false;
1043
+ });
1044
+ if (!wuMissing) {
1045
+ logger.warning(`Unexpected ESP exception: ${e.message}`);
1046
+ throw e;
1047
+ }
1048
+ return {} as WsWorkunits.WUQueryResponse;
1049
+ });
1050
+ }
1051
+
1052
+ protected WUCreate() {
1053
+ return this.connection.WUCreate().then((response) => {
1054
+ this.set(response.Workunit);
1055
+ _workunits.set(this);
1056
+ return response;
1057
+ });
1058
+ }
1059
+
1060
+ protected WUInfo(_request: Partial<WsWorkunits.WUInfo> = {}): Promise<WsWorkunits.WUInfoResponse> {
1061
+ const includeResults = _request.IncludeResults || _request.IncludeResultsViewNames;
1062
+ return this.connection.WUInfo({
1063
+ ..._request,
1064
+ Wuid: this.Wuid,
1065
+ IncludeResults: includeResults,
1066
+ IncludeResultsViewNames: includeResults,
1067
+ SuppressResultSchemas: false
1068
+ }).then((response) => {
1069
+ this.set(response.Workunit);
1070
+ if (includeResults) {
1071
+ this.set({
1072
+ ResultViews: response.ResultViews
1073
+ } as IWorkunitState);
1074
+ }
1075
+ return response;
1076
+ }).catch((e: ESPExceptions) => {
1077
+ // deleted ---
1078
+ const wuMissing = e.Exception.some((exception) => {
1079
+ if (exception.Code === 20080) {
1080
+ this.clearState(this.Wuid);
1081
+ this.set("StateID", WUStateID.NotFound);
1082
+ return true;
1083
+ }
1084
+ return false;
1085
+ });
1086
+ if (!wuMissing) {
1087
+ logger.warning(`Unexpected ESP exception: ${e.message}`);
1088
+ throw e;
1089
+ }
1090
+ return {} as WsWorkunits.WUInfoResponse;
1091
+ });
1092
+ }
1093
+
1094
+ protected WUResubmit(request: Partial<WsWorkunits.WUResubmit>): Promise<WsWorkunits.WUResubmitResponse> {
1095
+ return this.connection.WUResubmit(deepMixinT<WsWorkunits.WUResubmit>({}, request, {
1096
+ Wuids: { Item: [this.Wuid] }
1097
+ }));
1098
+ }
1099
+
1100
+ protected WUDetailsMeta(request: Partial<WsWorkunits.WUDetailsMeta>): Promise<WsWorkunits.WUDetailsMetaResponse> {
1101
+ return this.connection.WUDetailsMeta(request);
1102
+ }
1103
+
1104
+ protected WUDetails(request: RecursivePartial<WsWorkunits.WUDetails>): Promise<WsWorkunits.WUDetailsResponse> {
1105
+ return this.connection.WUDetails(deepMixinT<WsWorkunits.WUDetails>({
1106
+ ScopeFilter: {
1107
+ MaxDepth: 9999
1108
+ },
1109
+ ScopeOptions: {
1110
+ IncludeMatchedScopesInResults: true,
1111
+ IncludeScope: true,
1112
+ IncludeId: false,
1113
+ IncludeScopeType: false
1114
+ },
1115
+ PropertyOptions: {
1116
+ IncludeName: true,
1117
+ IncludeRawValue: false,
1118
+ IncludeFormatted: true,
1119
+ IncludeMeasure: true,
1120
+ IncludeCreator: false,
1121
+ IncludeCreatorType: false
1122
+ }
1123
+ }, request, { WUID: this.Wuid })).then((response) => {
1124
+ return deepMixinT<WsWorkunits.WUDetailsResponse>({
1125
+ Scopes: {
1126
+ Scope: []
1127
+ }
1128
+ }, response);
1129
+ });
1130
+ }
1131
+
1132
+ protected WUAction(actionType: WsWorkunits.ECLWUActions): Promise<WsWorkunits.WUActionResponse> {
1133
+ return this.connection.WUAction({
1134
+ Wuids: { Item: [this.Wuid] },
1135
+ WUActionType: actionType
1136
+ }).then((response) => {
1137
+ return this.refresh().then(() => {
1138
+ this._monitor();
1139
+ return response;
1140
+ });
1141
+ });
1142
+ }
1143
+
1144
+ publish(name?: string) {
1145
+ return this.connection.WUPublishWorkunit({
1146
+ Wuid: this.Wuid,
1147
+ Cluster: this.Cluster,
1148
+ JobName: name || this.Jobname,
1149
+ AllowForeignFiles: true,
1150
+ Activate: WsWorkunits.WUQueryActivationMode.ActivateQuery,
1151
+ Wait: 5000
1152
+ });
1153
+ }
1154
+
1155
+ publishEx(request: Partial<WsWorkunits.WUPublishWorkunit>) {
1156
+ const service = new WorkunitsServiceEx({ baseUrl: "" });
1157
+ const publishRequest = {
1158
+ Wuid: this.Wuid,
1159
+ Cluster: this.Cluster,
1160
+ JobName: this.Jobname,
1161
+ AllowForeignFiles: true,
1162
+ Activate: 1,
1163
+ Wait: 5000,
1164
+ ...request
1165
+ };
1166
+ return service.WUPublishWorkunitEx(publishRequest);
1167
+ }
1168
+
1169
+ protected WUCDebug(command: string, opts: any = {}): Promise<XMLNode | null> {
1170
+ let optsStr = "";
1171
+ for (const key in opts) {
1172
+ if (opts.hasOwnProperty(key)) {
1173
+ optsStr += ` ${key}='${opts[key]}'`;
1174
+ }
1175
+ }
1176
+ return this.connection.WUCDebugEx({
1177
+ Wuid: this.Wuid,
1178
+ Command: `<debug:${command} uid='${this.Wuid}'${optsStr}/>`
1179
+ }).then((response) => {
1180
+ return response;
1181
+ });
1182
+ }
1183
+
1184
+ debug(command: string, opts?: object): Promise<XMLNode> {
1185
+ if (!this.isDebugging()) {
1186
+ return Promise.resolve(new XMLNode(command));
1187
+ }
1188
+ return this.WUCDebug(command, opts).then((response: XMLNode) => {
1189
+ const retVal: XMLNode[] = response.children(command);
1190
+ if (retVal.length) {
1191
+ return retVal[0];
1192
+ }
1193
+ return new XMLNode(command);
1194
+ }).catch((_) => {
1195
+ logger.error(_);
1196
+ return Promise.resolve(new XMLNode(command));
1197
+ });
1198
+ }
1199
+
1200
+ debugStatus(): Promise<XMLNode> {
1201
+ if (!this.isDebugging()) {
1202
+ return Promise.resolve<any>({
1203
+ DebugState: { state: "unknown" }
1204
+ });
1205
+ }
1206
+ return this.debug("status").then((response) => {
1207
+ const debugState = { ...this.DebugState, ...response.$ };
1208
+ this.set({
1209
+ DebugState: debugState
1210
+ });
1211
+ return response;
1212
+ });
1213
+ }
1214
+
1215
+ debugContinue(mode = ""): Promise<XMLNode> {
1216
+ return this.debug("continue", {
1217
+ mode
1218
+ });
1219
+ }
1220
+
1221
+ debugStep(mode: string): Promise<XMLNode> {
1222
+ return this.debug("step", {
1223
+ mode
1224
+ });
1225
+ }
1226
+
1227
+ debugPause(): Promise<XMLNode> {
1228
+ return this.debug("interrupt");
1229
+ }
1230
+
1231
+ debugQuit(): Promise<XMLNode> {
1232
+ return this.debug("quit");
1233
+ }
1234
+
1235
+ debugDeleteAllBreakpoints(): Promise<XMLNode> {
1236
+ return this.debug("delete", {
1237
+ idx: 0
1238
+ });
1239
+ }
1240
+
1241
+ protected debugBreakpointResponseParser(rootNode: StringAnyMap) {
1242
+ return rootNode.children().map((childNode: XMLNode) => {
1243
+ if (childNode.name === "break") {
1244
+ return childNode.$;
1245
+ }
1246
+ });
1247
+ }
1248
+
1249
+ debugBreakpointAdd(id: string, mode: string, action: string): Promise<XMLNode> {
1250
+ return this.debug("breakpoint", {
1251
+ id,
1252
+ mode,
1253
+ action
1254
+ }).then((rootNode) => {
1255
+ return this.debugBreakpointResponseParser(rootNode);
1256
+ });
1257
+ }
1258
+
1259
+ debugBreakpointList(): Promise<any[]> {
1260
+ return this.debug("list").then((rootNode) => {
1261
+ return this.debugBreakpointResponseParser(rootNode);
1262
+ });
1263
+ }
1264
+
1265
+ debugGraph(): Promise<XGMMLGraph> {
1266
+ if (this._debugAllGraph && this.DebugState["_prevGraphSequenceNum"] === this.DebugState["graphSequenceNum"]) {
1267
+ return Promise.resolve(this._debugAllGraph);
1268
+ }
1269
+ return this.debug("graph", { name: "all" }).then((response) => {
1270
+ this.DebugState["_prevGraphSequenceNum"] = this.DebugState["graphSequenceNum"];
1271
+ this._debugAllGraph = createXGMMLGraph(this.Wuid, response);
1272
+ return this._debugAllGraph;
1273
+ });
1274
+ }
1275
+
1276
+ debugBreakpointValid(path: string): Promise<IECLDefintion[]> {
1277
+ return this.debugGraph().then((graph) => {
1278
+ return breakpointLocations(graph, path);
1279
+ });
1280
+ }
1281
+
1282
+ debugPrint(edgeID: string, startRow: number = 0, numRows: number = 10): Promise<StringAnyMap[]> {
1283
+ return this.debug("print", {
1284
+ edgeID,
1285
+ startRow,
1286
+ numRows
1287
+ }).then((response: XMLNode) => {
1288
+ return response.children().map((rowNode) => {
1289
+ const retVal: StringAnyMap = {};
1290
+ rowNode.children().forEach((cellNode) => {
1291
+ retVal[cellNode.name] = cellNode.content;
1292
+ });
1293
+ return retVal;
1294
+ });
1295
+ });
1296
+ }
1297
+ }
1298
+
1299
+ export interface IECLDefintion {
1300
+ id: string;
1301
+ file: string;
1302
+ line: number;
1303
+ column: number;
1304
+ }
1305
+
1306
+ const ATTR_DEFINITION = "definition";
1307
+
1308
+ function hasECLDefinition(vertex: XGMMLVertex): boolean {
1309
+ return vertex._![ATTR_DEFINITION] !== undefined;
1310
+ }
1311
+
1312
+ function getECLDefinition(vertex: XGMMLVertex): IECLDefintion {
1313
+ const match = /([a-z]:\\(?:[-\w\.\d]+\\)*(?:[-\w\.\d]+)?|(?:\/[\w\.\-]+)+)\((\d*),(\d*)\)/.exec(vertex._![ATTR_DEFINITION]);
1314
+ if (match) {
1315
+ const [, _file, _row, _col] = match;
1316
+ _file.replace(/\/\.\//g, "/");
1317
+ return {
1318
+ id: vertex._!["id"],
1319
+ file: _file,
1320
+ line: +_row,
1321
+ column: +_col
1322
+ };
1323
+ }
1324
+ throw new Error(`Bad definition: ${vertex._![ATTR_DEFINITION]}`);
1325
+ }
1326
+
1327
+ function breakpointLocations(graph: XGMMLGraph, path?: string): IECLDefintion[] {
1328
+ const retVal: IECLDefintion[] = [];
1329
+ for (const vertex of graph.vertices) {
1330
+ if (hasECLDefinition(vertex)) {
1331
+ const definition = getECLDefinition(vertex);
1332
+ if (definition && !path || path === definition.file) {
1333
+ retVal.push(definition);
1334
+ }
1335
+ }
1336
+ }
1337
+ return retVal.sort((l, r) => {
1338
+ return l.line - r.line;
1339
+ });
1340
+ }