@hpcc-js/comms 3.15.4 → 3.15.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) 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 +10 -10
  8. package/dist/node/index.cjs.map +4 -4
  9. package/dist/node/index.js +9 -9
  10. package/dist/node/index.js.map +4 -4
  11. package/package.json +7 -7
  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 +73 -73
  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 +270 -270
  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 +228 -227
  65. package/src/services/wsdl/WsDali/v1.04/WsDali.ts +216 -216
  66. package/src/services/wsdl/WsDali/v1.07/WsDali.ts +72 -72
  67. package/src/services/wsdl/WsDfu/v1.62/WsDfu.ts +1455 -1455
  68. package/src/services/wsdl/WsDfu/v1.63/WsDfu.ts +1465 -1465
  69. package/src/services/wsdl/WsDfu/v1.65/WsDfu.ts +1244 -1244
  70. package/src/services/wsdl/WsDfu/v1.66/WsDfu.ts +1267 -1267
  71. package/src/services/wsdl/WsDfu/v1.67/WsDfu.ts +1268 -1268
  72. package/src/services/wsdl/WsDfu/v1.68/WsDfu.ts +1301 -0
  73. package/src/services/wsdl/WsESDLConfig/v1.5/WsESDLConfig.ts +366 -0
  74. package/src/services/wsdl/WsFileIO/v1.01/WsFileIO.ts +104 -104
  75. package/src/services/wsdl/WsPackageProcess/v1.04/WsPackageProcess.ts +519 -519
  76. package/src/services/wsdl/WsPackageProcess/v1.07/WsPackageProcess.ts +500 -500
  77. package/src/services/wsdl/WsPackageProcess/v1.08/WsPackageProcess.ts +503 -0
  78. package/src/services/wsdl/WsResources/v1.01/WsResources.ts +119 -119
  79. package/src/services/wsdl/WsSMC/v1.24/WsSMC.ts +665 -665
  80. package/src/services/wsdl/WsSMC/v1.27/WsSMC.ts +591 -591
  81. package/src/services/wsdl/WsSMC/v1.28/WsSMC.ts +645 -645
  82. package/src/services/wsdl/WsSMC/v1.29/WsSMC.ts +660 -660
  83. package/src/services/wsdl/WsSasha/v1.01/WsSasha.ts +18 -18
  84. package/src/services/wsdl/WsTopology/v1.31/WsTopology.ts +856 -856
  85. package/src/services/wsdl/WsTopology/v1.32/WsTopology.ts +786 -786
  86. package/src/services/wsdl/WsTopology/v1.33/WsTopology.ts +835 -835
  87. package/src/services/wsdl/WsWorkunits/v1.88/WsWorkunits.ts +2944 -2944
  88. package/src/services/wsdl/WsWorkunits/v1.94/WsWorkunits.ts +3072 -3072
  89. package/src/services/wsdl/WsWorkunits/v1.95/WsWorkunits.ts +3073 -3073
  90. package/src/services/wsdl/WsWorkunits/v1.97/WsWorkunits.ts +3134 -3134
  91. package/src/services/wsdl/WsWorkunits/v1.98/WsWorkunits.ts +3182 -3182
  92. package/src/services/wsdl/WsWorkunits/v1.99/WsWorkunits.ts +3162 -3162
  93. package/src/services/wsdl/WsWorkunits/v2/WsWorkunits.ts +3153 -3153
  94. package/src/services/wsdl/WsWorkunits/v2.02/WsWorkunits.ts +3162 -3162
  95. package/src/services/wsdl/WsWorkunits/v2.03/WsWorkunits.ts +3164 -3164
  96. package/src/services/wsdl/WsWorkunits/v2.04/WsWorkunits.ts +3171 -3171
  97. package/src/services/wsdl/WsWorkunits/v2.05/WsWorkunits.ts +3177 -0
  98. package/src/services/wsdl/ws_access/v1.16/ws_access.ts +1086 -1086
  99. package/src/services/wsdl/ws_access/v1.17/ws_access.ts +1023 -1023
  100. package/src/services/wsdl/ws_account/v1.05/ws_account.ts +111 -111
  101. package/src/services/wsdl/ws_account/v1.06/ws_account.ts +109 -109
  102. package/src/services/wsdl/ws_account/v1.07/ws_account.ts +114 -114
  103. package/src/services/wsdl/ws_codesign/v1.1/ws_codesign.ts +95 -95
  104. package/src/services/wsdl/ws_elk/v1/ws_elk.ts +47 -47
  105. package/src/services/wsdl/ws_logaccess/v1/ws_logaccess.ts +83 -83
  106. package/src/services/wsdl/ws_logaccess/v1.02/ws_logaccess.ts +161 -161
  107. package/src/services/wsdl/ws_logaccess/v1.03/ws_logaccess.ts +190 -190
  108. package/src/services/wsdl/ws_logaccess/v1.04/ws_logaccess.ts +215 -215
  109. package/src/services/wsdl/ws_logaccess/v1.05/ws_logaccess.ts +219 -219
  110. package/src/services/wsdl/ws_logaccess/v1.08/ws_logaccess.ts +267 -267
  111. package/src/services/wsdl/ws_machine/v1.17/ws_machine.ts +567 -567
  112. package/src/services/wsdl/ws_machine/v1.18/ws_machine.ts +497 -497
  113. package/src/services/wsdl/ws_machine/v1.19/ws_machine.ts +497 -497
  114. package/src/services/wsdl/wsstore/v1.02/wsstore.ts +239 -239
  115. package/types/services/wsPackageProcess.d.ts +1 -1
  116. package/types/services/wsWorkunits.d.ts +1 -1
  117. package/types/services/wsdl/FileSpray/v1.27/FileSpray.d.ts +506 -506
  118. package/types/services/wsdl/WsCloud/v1.02/WsCloud.d.ts +18 -18
  119. package/types/services/wsdl/WsDFUXRef/v1.04/WsDFUXRef.d.ts +58 -57
  120. package/types/services/wsdl/WsDali/v1.07/WsDali.d.ts +42 -42
  121. package/types/services/wsdl/WsPackageProcess/{v1.07 → v1.08}/WsPackageProcess.d.ts +121 -118
  122. package/types/services/wsdl/WsSasha/v1.01/WsSasha.d.ts +13 -13
  123. package/types/services/wsdl/WsTopology/v1.33/WsTopology.d.ts +360 -360
  124. package/types/services/wsdl/WsWorkunits/v2.05/WsWorkunits.d.ts +2571 -0
  125. package/types/services/wsdl/ws_access/v1.17/ws_access.d.ts +268 -268
  126. package/types/services/wsdl/ws_account/v1.07/ws_account.d.ts +34 -34
  127. package/types/services/wsdl/ws_codesign/v1.1/ws_codesign.d.ts +22 -22
  128. package/types/services/wsdl/ws_elk/v1/ws_elk.d.ts +12 -12
  129. package/types/services/wsdl/wsstore/v1.02/wsstore.d.ts +61 -61
  130. package/types/services/wsdl/WsWorkunits/v2.04/WsWorkunits.d.ts +0 -2565
@@ -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.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
+ }