tm1npm 1.5.3 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +89 -0
- package/lib/index.d.ts +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/services/ApplicationService.d.ts +19 -3
- package/lib/services/ApplicationService.d.ts.map +1 -1
- package/lib/services/ApplicationService.js +232 -6
- package/lib/services/AsyncOperationService.d.ts +8 -1
- package/lib/services/AsyncOperationService.d.ts.map +1 -1
- package/lib/services/AsyncOperationService.js +69 -26
- package/lib/services/ElementService.d.ts +67 -1
- package/lib/services/ElementService.d.ts.map +1 -1
- package/lib/services/ElementService.js +214 -0
- package/lib/services/FileService.d.ts.map +1 -1
- package/lib/services/HierarchyService.d.ts +26 -0
- package/lib/services/HierarchyService.d.ts.map +1 -1
- package/lib/services/HierarchyService.js +306 -0
- package/lib/services/ProcessService.d.ts +40 -22
- package/lib/services/ProcessService.d.ts.map +1 -1
- package/lib/services/ProcessService.js +118 -111
- package/lib/services/RestService.d.ts +213 -25
- package/lib/services/RestService.d.ts.map +1 -1
- package/lib/services/RestService.js +841 -263
- package/lib/services/SubsetService.d.ts +2 -0
- package/lib/services/SubsetService.d.ts.map +1 -1
- package/lib/services/SubsetService.js +33 -0
- package/lib/services/TM1Service.d.ts +44 -1
- package/lib/services/TM1Service.d.ts.map +1 -1
- package/lib/services/TM1Service.js +96 -4
- package/lib/services/index.d.ts +1 -1
- package/lib/services/index.d.ts.map +1 -1
- package/lib/tests/100PercentParityCheck.test.js +23 -6
- package/lib/tests/applicationService.issue38.test.d.ts +5 -0
- package/lib/tests/applicationService.issue38.test.d.ts.map +1 -0
- package/lib/tests/applicationService.issue38.test.js +237 -0
- package/lib/tests/asyncOperationService.test.js +51 -45
- package/lib/tests/bugfix28.test.js +12 -4
- package/lib/tests/elementService.issue37.test.d.ts +5 -0
- package/lib/tests/elementService.issue37.test.d.ts.map +1 -0
- package/lib/tests/elementService.issue37.test.js +413 -0
- package/lib/tests/elementService.issue38.test.d.ts +5 -0
- package/lib/tests/elementService.issue38.test.d.ts.map +1 -0
- package/lib/tests/elementService.issue38.test.js +79 -0
- package/lib/tests/hierarchyService.issue38.test.d.ts +5 -0
- package/lib/tests/hierarchyService.issue38.test.d.ts.map +1 -0
- package/lib/tests/hierarchyService.issue38.test.js +460 -0
- package/lib/tests/processService.comprehensive.test.js +9 -9
- package/lib/tests/processService.test.js +234 -0
- package/lib/tests/restService.test.d.ts +0 -4
- package/lib/tests/restService.test.d.ts.map +1 -1
- package/lib/tests/restService.test.js +1558 -143
- package/lib/tests/subsetService.issue38.test.d.ts +5 -0
- package/lib/tests/subsetService.issue38.test.d.ts.map +1 -0
- package/lib/tests/subsetService.issue38.test.js +113 -0
- package/lib/tests/tm1Service.test.js +80 -8
- package/package.json +1 -1
- package/src/index.ts +1 -1
- package/src/services/ApplicationService.ts +282 -10
- package/src/services/AsyncOperationService.ts +76 -29
- package/src/services/ElementService.ts +322 -1
- package/src/services/FileService.ts +3 -3
- package/src/services/HierarchyService.ts +419 -1
- package/src/services/ProcessService.ts +185 -142
- package/src/services/RestService.ts +1021 -267
- package/src/services/SubsetService.ts +48 -0
- package/src/services/TM1Service.ts +127 -6
- package/src/services/index.ts +1 -1
- package/src/tests/100PercentParityCheck.test.ts +29 -8
- package/src/tests/applicationService.issue38.test.ts +293 -0
- package/src/tests/asyncOperationService.test.ts +52 -48
- package/src/tests/bugfix28.test.ts +12 -4
- package/src/tests/elementService.issue37.test.ts +571 -0
- package/src/tests/elementService.issue38.test.ts +103 -0
- package/src/tests/hierarchyService.issue38.test.ts +599 -0
- package/src/tests/processService.comprehensive.test.ts +10 -10
- package/src/tests/processService.test.ts +295 -3
- package/src/tests/restService.test.ts +1844 -139
- package/src/tests/subsetService.issue38.test.ts +182 -0
- package/src/tests/tm1Service.test.ts +95 -11
|
@@ -1,13 +1,38 @@
|
|
|
1
1
|
import { AxiosResponse } from 'axios';
|
|
2
2
|
import { v4 as uuidv4 } from 'uuid';
|
|
3
|
-
import { RestService } from './RestService';
|
|
3
|
+
import { RequestOptions, RestService } from './RestService';
|
|
4
4
|
import { ObjectService } from './ObjectService';
|
|
5
5
|
import { Process } from '../objects/Process';
|
|
6
|
-
import { ProcessDebugBreakpoint } from '../objects/ProcessDebugBreakpoint';
|
|
6
|
+
import { ProcessDebugBreakpoint, BreakPointType, HitMode } from '../objects/ProcessDebugBreakpoint';
|
|
7
7
|
import { TM1RestException, TM1Exception } from '../exceptions/TM1Exception';
|
|
8
8
|
import { formatUrl, lowerAndDropSpaces } from '../utils/Utils';
|
|
9
9
|
import { OperationStatus, OperationType } from './AsyncOperationService';
|
|
10
10
|
|
|
11
|
+
export interface CompileSyntaxError {
|
|
12
|
+
LineNumber: number;
|
|
13
|
+
Message: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface ProcessExecuteBody {
|
|
17
|
+
Parameters?: Array<{ Name: string; Value: unknown }>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface ProcessExecuteSummary {
|
|
21
|
+
ProcessExecuteStatusCode: string;
|
|
22
|
+
ErrorLogFile?: { Filename?: string } | null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface ValidationError {
|
|
26
|
+
line: number;
|
|
27
|
+
message: string;
|
|
28
|
+
severity: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
interface ValidationResult {
|
|
32
|
+
isValid: boolean;
|
|
33
|
+
errors: ValidationError[];
|
|
34
|
+
}
|
|
35
|
+
|
|
11
36
|
export class ProcessService extends ObjectService {
|
|
12
37
|
/** Service to handle Object Updates for TI Processes
|
|
13
38
|
*
|
|
@@ -74,7 +99,7 @@ export class ProcessService extends ObjectService {
|
|
|
74
99
|
|
|
75
100
|
const response = await this.rest.get(url);
|
|
76
101
|
const responseAsDict = response.data;
|
|
77
|
-
return responseAsDict.value.map((p:
|
|
102
|
+
return responseAsDict.value.map((p: Record<string, unknown>) => Process.fromDict(p));
|
|
78
103
|
}
|
|
79
104
|
|
|
80
105
|
public async getAllNames(skipControlProcesses: boolean = false): Promise<string[]> {
|
|
@@ -88,7 +113,7 @@ export class ProcessService extends ObjectService {
|
|
|
88
113
|
const url = "/Processes?$select=Name" + (skipControlProcesses ? modelProcessFilter : "");
|
|
89
114
|
|
|
90
115
|
const response = await this.rest.get(url);
|
|
91
|
-
const processes = response.data.value.map((process:
|
|
116
|
+
const processes = response.data.value.map((process: { Name: string }) => process.Name);
|
|
92
117
|
return processes;
|
|
93
118
|
}
|
|
94
119
|
|
|
@@ -169,8 +194,8 @@ export class ProcessService extends ObjectService {
|
|
|
169
194
|
* :return: response
|
|
170
195
|
*/
|
|
171
196
|
const url = formatUrl("/Processes('{}')/tm1.Execute", processName);
|
|
172
|
-
|
|
173
|
-
const body:
|
|
197
|
+
|
|
198
|
+
const body: ProcessExecuteBody = {};
|
|
174
199
|
if (parameters && Object.keys(parameters).length > 0) {
|
|
175
200
|
body.Parameters = Object.entries(parameters).map(([name, value]) => ({
|
|
176
201
|
Name: name,
|
|
@@ -194,8 +219,8 @@ export class ProcessService extends ObjectService {
|
|
|
194
219
|
* :return: response including execution details
|
|
195
220
|
*/
|
|
196
221
|
const url = formatUrl("/Processes('{}')/tm1.ExecuteWithReturn?$expand=*", processName);
|
|
197
|
-
|
|
198
|
-
const body:
|
|
222
|
+
|
|
223
|
+
const body: ProcessExecuteBody = {};
|
|
199
224
|
if (parameters && Object.keys(parameters).length > 0) {
|
|
200
225
|
body.Parameters = Object.entries(parameters).map(([name, value]) => ({
|
|
201
226
|
Name: name,
|
|
@@ -203,9 +228,9 @@ export class ProcessService extends ObjectService {
|
|
|
203
228
|
}));
|
|
204
229
|
}
|
|
205
230
|
|
|
206
|
-
const config:
|
|
231
|
+
const config: RequestOptions = {};
|
|
207
232
|
if (timeout) {
|
|
208
|
-
config.timeout = timeout
|
|
233
|
+
config.timeout = timeout;
|
|
209
234
|
}
|
|
210
235
|
|
|
211
236
|
return await this.rest.post(url, JSON.stringify(body), config);
|
|
@@ -221,105 +246,53 @@ export class ProcessService extends ObjectService {
|
|
|
221
246
|
return await this.rest.post(url, '{}');
|
|
222
247
|
}
|
|
223
248
|
|
|
224
|
-
public async compileProcess(
|
|
225
|
-
/** Compile
|
|
249
|
+
public async compileProcess(process: Process): Promise<CompileSyntaxError[]> {
|
|
250
|
+
/** Compile an unbound process and return syntax errors
|
|
226
251
|
*
|
|
227
|
-
* :param
|
|
228
|
-
* :return:
|
|
252
|
+
* :param process: Instance of .Process class
|
|
253
|
+
* :return: list of syntax errors (empty if successful)
|
|
229
254
|
*/
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
if (response.status === 200) {
|
|
235
|
-
return {
|
|
236
|
-
success: true,
|
|
237
|
-
errors: []
|
|
238
|
-
};
|
|
239
|
-
} else {
|
|
240
|
-
return {
|
|
241
|
-
success: false,
|
|
242
|
-
errors: [response.statusText || 'Compilation failed']
|
|
243
|
-
};
|
|
244
|
-
}
|
|
245
|
-
} catch (error: any) {
|
|
246
|
-
const errorMessage = error.response?.data?.error?.message || error.message || 'Unknown compilation error';
|
|
247
|
-
return {
|
|
248
|
-
success: false,
|
|
249
|
-
errors: [errorMessage]
|
|
250
|
-
};
|
|
251
|
-
}
|
|
255
|
+
const url = '/CompileProcess';
|
|
256
|
+
const payload = { Process: process.bodyAsDict };
|
|
257
|
+
const response = await this.rest.post(url, JSON.stringify(payload));
|
|
258
|
+
return response.data.value;
|
|
252
259
|
}
|
|
253
260
|
|
|
254
261
|
/**
|
|
255
|
-
* Poll
|
|
262
|
+
* Poll for async execution result
|
|
263
|
+
*
|
|
264
|
+
* :param asyncId: async operation ID returned from executeWithReturn with returnAsyncId=true
|
|
265
|
+
* :return: tuple of [success, status, errorLogFile] or null if not ready
|
|
256
266
|
*/
|
|
257
|
-
public async pollExecuteWithReturn(
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
/** Execute process asynchronously and poll for completion
|
|
264
|
-
*
|
|
265
|
-
* :param process_name: name of the process
|
|
266
|
-
* :param parameters: dictionary of process parameters
|
|
267
|
-
* :param timeout: maximum time to wait for completion (seconds)
|
|
268
|
-
* :param poll_interval: time between status checks (seconds)
|
|
269
|
-
* :return: execution result when completed
|
|
270
|
-
*/
|
|
271
|
-
|
|
272
|
-
// Start async execution
|
|
273
|
-
const executeUrl = formatUrl("/Processes('{}')/tm1.ExecuteAsync", processName);
|
|
274
|
-
const body: any = {};
|
|
275
|
-
|
|
276
|
-
if (parameters && Object.keys(parameters).length > 0) {
|
|
277
|
-
body.Parameters = Object.entries(parameters).map(([name, value]) => ({
|
|
278
|
-
Name: name,
|
|
279
|
-
Value: value
|
|
280
|
-
}));
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
const executeResponse = await this.rest.post(executeUrl, JSON.stringify(body));
|
|
284
|
-
const executionId = executeResponse.data.ID || executeResponse.data.ExecutionId;
|
|
285
|
-
|
|
286
|
-
if (!executionId) {
|
|
287
|
-
throw new TM1Exception('Failed to start async process execution');
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
// Poll for completion
|
|
291
|
-
const startTime = Date.now();
|
|
292
|
-
const maxTime = timeout * 1000;
|
|
293
|
-
|
|
294
|
-
while (Date.now() - startTime < maxTime) {
|
|
295
|
-
try {
|
|
296
|
-
// Check execution status
|
|
297
|
-
const statusUrl = `/ExecutionStatus('${executionId}')`;
|
|
298
|
-
const statusResponse = await this.rest.get(statusUrl);
|
|
299
|
-
const status = statusResponse.data;
|
|
300
|
-
|
|
301
|
-
if (status.Status === 'Completed') {
|
|
302
|
-
// Get execution results
|
|
303
|
-
const resultUrl = `/ExecutionResults('${executionId}')`;
|
|
304
|
-
const resultResponse = await this.rest.get(resultUrl);
|
|
305
|
-
return resultResponse.data;
|
|
306
|
-
} else if (status.Status === 'Failed') {
|
|
307
|
-
throw new TM1Exception(`Process execution failed: ${status.ErrorMessage || 'Unknown error'}`);
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
// Wait before next poll
|
|
311
|
-
await new Promise(resolve => setTimeout(resolve, pollInterval * 1000));
|
|
312
|
-
|
|
313
|
-
} catch (error) {
|
|
314
|
-
if (error instanceof TM1Exception) {
|
|
315
|
-
throw error;
|
|
316
|
-
}
|
|
317
|
-
// If status check fails, wait and retry
|
|
318
|
-
await new Promise(resolve => setTimeout(resolve, pollInterval * 1000));
|
|
267
|
+
public async pollExecuteWithReturn(asyncId: string): Promise<[boolean, string, string | null] | null> {
|
|
268
|
+
try {
|
|
269
|
+
const response = await this.rest.retrieve_async_response(asyncId);
|
|
270
|
+
// tm1py returns None while the async op is still in-flight (status 202).
|
|
271
|
+
if (response.status !== 200 && response.status !== 201) {
|
|
272
|
+
return null;
|
|
319
273
|
}
|
|
274
|
+
// TODO: tm1py handles TM1 < v11 binary-wrapped responses via
|
|
275
|
+
// build_response_from_binary_response. Add support if needed.
|
|
276
|
+
return this._executeWithReturnParseResponse(response.data);
|
|
277
|
+
} catch (error) {
|
|
278
|
+
// 404 means the async resource hasn't materialized yet — return null
|
|
279
|
+
// so the caller can retry. 202 means still running. Both are
|
|
280
|
+
// retryable, unlike AsyncOperationService which treats 404 as
|
|
281
|
+
// terminal FAILED for locally-tracked operations.
|
|
282
|
+
const err = error as { status?: number; response?: { status?: number } };
|
|
283
|
+
const status = err?.status ?? err?.response?.status;
|
|
284
|
+
if (status === 202 || status === 404) {
|
|
285
|
+
return null;
|
|
286
|
+
}
|
|
287
|
+
throw error;
|
|
320
288
|
}
|
|
321
|
-
|
|
322
|
-
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
private _executeWithReturnParseResponse(executionSummary: ProcessExecuteSummary): [boolean, string, string | null] {
|
|
292
|
+
const success = executionSummary.ProcessExecuteStatusCode === 'CompletedSuccessfully';
|
|
293
|
+
const status = executionSummary.ProcessExecuteStatusCode;
|
|
294
|
+
const errorLogFile = executionSummary.ErrorLogFile?.Filename ?? null;
|
|
295
|
+
return [success, status, errorLogFile];
|
|
323
296
|
}
|
|
324
297
|
|
|
325
298
|
public async executeProcessWithReturn(
|
|
@@ -347,7 +320,7 @@ export class ProcessService extends ObjectService {
|
|
|
347
320
|
return '';
|
|
348
321
|
}
|
|
349
322
|
|
|
350
|
-
public async
|
|
323
|
+
public async debugGetBreakpoints(debugId: string): Promise<ProcessDebugBreakpoint[]> {
|
|
351
324
|
/** Get debug breakpoints for a debug context
|
|
352
325
|
*
|
|
353
326
|
* :param debugId: debug session ID
|
|
@@ -355,28 +328,27 @@ export class ProcessService extends ObjectService {
|
|
|
355
328
|
*/
|
|
356
329
|
const url = formatUrl("/ProcessDebugContexts('{}')/Breakpoints", debugId);
|
|
357
330
|
const response = await this.rest.get(url);
|
|
358
|
-
return response.data.value.map((bp:
|
|
331
|
+
return response.data.value.map((bp: Record<string, unknown>) => ProcessDebugBreakpoint.fromDict(bp));
|
|
359
332
|
}
|
|
360
333
|
|
|
361
|
-
public async
|
|
334
|
+
public async debugAddBreakpoint(
|
|
362
335
|
debugId: string,
|
|
363
336
|
breakpoint: ProcessDebugBreakpoint
|
|
364
337
|
): Promise<AxiosResponse> {
|
|
365
|
-
/**
|
|
338
|
+
/** Add a single breakpoint to a debug context (delegates to debugAddBreakpoints)
|
|
366
339
|
*
|
|
367
340
|
* :param debugId: debug session ID
|
|
368
341
|
* :param breakpoint: ProcessDebugBreakpoint object
|
|
369
342
|
* :return: response
|
|
370
343
|
*/
|
|
371
|
-
|
|
372
|
-
return await this.rest.post(url, breakpoint.body);
|
|
344
|
+
return this.debugAddBreakpoints(debugId, [breakpoint]);
|
|
373
345
|
}
|
|
374
346
|
|
|
375
|
-
public async
|
|
347
|
+
public async debugRemoveBreakpoint(
|
|
376
348
|
debugId: string,
|
|
377
349
|
breakpointId: number
|
|
378
350
|
): Promise<AxiosResponse> {
|
|
379
|
-
/**
|
|
351
|
+
/** Remove a breakpoint from a debug context
|
|
380
352
|
*
|
|
381
353
|
* :param debugId: debug session ID
|
|
382
354
|
* :param breakpointId: ID of the breakpoint
|
|
@@ -588,7 +560,7 @@ export class ProcessService extends ObjectService {
|
|
|
588
560
|
}
|
|
589
561
|
|
|
590
562
|
const response = await this.rest.get(url);
|
|
591
|
-
return response.data.value.map((log:
|
|
563
|
+
return response.data.value.map((log: { Filename: string }) => log.Filename);
|
|
592
564
|
}
|
|
593
565
|
|
|
594
566
|
public async getErrorLogFilenames(
|
|
@@ -675,7 +647,7 @@ export class ProcessService extends ObjectService {
|
|
|
675
647
|
}
|
|
676
648
|
|
|
677
649
|
const response = await this.rest.get(url);
|
|
678
|
-
return response.data.value.map((process:
|
|
650
|
+
return response.data.value.map((process: { Name: string }) => process.Name);
|
|
679
651
|
}
|
|
680
652
|
|
|
681
653
|
public async updateOrCreate(process: Process): Promise<AxiosResponse> {
|
|
@@ -729,7 +701,7 @@ export class ProcessService extends ObjectService {
|
|
|
729
701
|
processName
|
|
730
702
|
);
|
|
731
703
|
|
|
732
|
-
const body:
|
|
704
|
+
const body: ProcessExecuteBody = {};
|
|
733
705
|
if (parameters && Object.keys(parameters).length > 0) {
|
|
734
706
|
body.Parameters = Object.entries(parameters).map(([name, value]) => ({
|
|
735
707
|
Name: name,
|
|
@@ -737,12 +709,14 @@ export class ProcessService extends ObjectService {
|
|
|
737
709
|
}));
|
|
738
710
|
}
|
|
739
711
|
|
|
740
|
-
const config:
|
|
712
|
+
const config: RequestOptions = {};
|
|
741
713
|
if (timeout) {
|
|
742
|
-
config.timeout = timeout
|
|
714
|
+
config.timeout = timeout;
|
|
743
715
|
}
|
|
744
716
|
|
|
745
|
-
|
|
717
|
+
// rest.post returns AxiosResponse | string (string only when caller
|
|
718
|
+
// opts into returnAsyncId). debugProcess never does, so narrow.
|
|
719
|
+
const response = (await this.rest.post(url, JSON.stringify(body), config)) as AxiosResponse;
|
|
746
720
|
return response.data;
|
|
747
721
|
}
|
|
748
722
|
|
|
@@ -859,6 +833,79 @@ export class ProcessService extends ObjectService {
|
|
|
859
833
|
}
|
|
860
834
|
}
|
|
861
835
|
|
|
836
|
+
/**
|
|
837
|
+
* Evaluate a TI expression and return the string result.
|
|
838
|
+
*
|
|
839
|
+
* Creates a temporary process with sFunc = {formula}, compiles it,
|
|
840
|
+
* starts a debug session, adds a data breakpoint on sFunc, continues
|
|
841
|
+
* execution to evaluate, reads the result, and cleans up.
|
|
842
|
+
*/
|
|
843
|
+
public async evaluateTiExpression(formula: string): Promise<string> {
|
|
844
|
+
// tm1py uses formula[formula.find("=") + 1:] which greedily strips at the
|
|
845
|
+
// first "=" anywhere in the string. We use a regex to only strip a leading
|
|
846
|
+
// "=" prefix (e.g. "=NOW;" → "NOW;"), avoiding mangling formulas with
|
|
847
|
+
// embedded "=" (e.g. comparisons like "IF(1=1,...)").
|
|
848
|
+
formula = formula.replace(/^\s*=\s*/, '');
|
|
849
|
+
|
|
850
|
+
// Ensure semicolon at end
|
|
851
|
+
if (!formula.trim().endsWith(';')) {
|
|
852
|
+
formula += ';';
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
const prologList = [`sFunc = ${formula}`, "sDebug='Stop';"];
|
|
856
|
+
const processName = `}TM1py${uuidv4()}`;
|
|
857
|
+
const p = new Process(
|
|
858
|
+
processName,
|
|
859
|
+
false,
|
|
860
|
+
undefined,
|
|
861
|
+
undefined,
|
|
862
|
+
undefined,
|
|
863
|
+
undefined,
|
|
864
|
+
Process.AUTO_GENERATED_STATEMENTS + prologList.join('\r\n'),
|
|
865
|
+
'',
|
|
866
|
+
'',
|
|
867
|
+
''
|
|
868
|
+
);
|
|
869
|
+
|
|
870
|
+
const syntaxErrors = await this.compileProcess(p);
|
|
871
|
+
if (syntaxErrors && syntaxErrors.length > 0) {
|
|
872
|
+
throw new Error(syntaxErrors.map(e => `Line ${e.LineNumber}: ${e.Message}`).join('; '));
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
try {
|
|
876
|
+
await this.create(p);
|
|
877
|
+
const debugContext = await this.debugProcess(processName);
|
|
878
|
+
const debugId = debugContext.ID;
|
|
879
|
+
|
|
880
|
+
const breakpoint = new ProcessDebugBreakpoint(
|
|
881
|
+
1,
|
|
882
|
+
BreakPointType.PROCESS_DEBUG_CONTEXT_DATA_BREAK_POINT,
|
|
883
|
+
true,
|
|
884
|
+
HitMode.BREAK_ALWAYS,
|
|
885
|
+
0,
|
|
886
|
+
'',
|
|
887
|
+
'sFunc'
|
|
888
|
+
);
|
|
889
|
+
|
|
890
|
+
await this.debugAddBreakpoint(debugId, breakpoint);
|
|
891
|
+
await this.debugContinue(debugId);
|
|
892
|
+
const result = await this.debugGetVariableValues(debugId);
|
|
893
|
+
await this.debugContinue(debugId);
|
|
894
|
+
|
|
895
|
+
if (!result || !('sFunc' in result)) {
|
|
896
|
+
throw new Error('unknown error: no formula result found');
|
|
897
|
+
}
|
|
898
|
+
return result['sFunc'];
|
|
899
|
+
|
|
900
|
+
} finally {
|
|
901
|
+
try {
|
|
902
|
+
await this.delete(processName);
|
|
903
|
+
} catch (_) {
|
|
904
|
+
// Cleanup failure should not mask the original error
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
|
|
862
909
|
/**
|
|
863
910
|
* Analyze process dependencies (what cubes/dimensions/processes it uses)
|
|
864
911
|
*
|
|
@@ -868,8 +915,8 @@ export class ProcessService extends ObjectService {
|
|
|
868
915
|
* @example
|
|
869
916
|
* ```typescript
|
|
870
917
|
* const deps = await processService.analyzeProcessDependencies('ImportData');
|
|
871
|
-
*
|
|
872
|
-
*
|
|
918
|
+
* // deps.cubes => array of cube names referenced in the process
|
|
919
|
+
* // deps.dimensions => array of dimension names referenced in the process
|
|
873
920
|
* ```
|
|
874
921
|
*/
|
|
875
922
|
public async analyzeProcessDependencies(processName: string): Promise<any> {
|
|
@@ -937,35 +984,32 @@ export class ProcessService extends ObjectService {
|
|
|
937
984
|
* Validate process syntax without executing it
|
|
938
985
|
*
|
|
939
986
|
* @param processName - Name of the process
|
|
940
|
-
* @returns Promise<
|
|
987
|
+
* @returns Promise<ValidationResult> - Validation result
|
|
941
988
|
*
|
|
942
989
|
* @example
|
|
943
990
|
* ```typescript
|
|
944
991
|
* const result = await processService.validateProcessSyntax('MyProcess');
|
|
945
992
|
* if (!result.isValid) {
|
|
946
|
-
*
|
|
993
|
+
* // result.errors contains ValidationError[] entries
|
|
947
994
|
* }
|
|
948
995
|
* ```
|
|
949
996
|
*/
|
|
950
|
-
public async validateProcessSyntax(processName: string): Promise<
|
|
997
|
+
public async validateProcessSyntax(processName: string): Promise<ValidationResult> {
|
|
951
998
|
try {
|
|
952
|
-
const
|
|
999
|
+
const response = await this.compile(processName);
|
|
1000
|
+
if (response.status === 200) {
|
|
1001
|
+
return { isValid: true, errors: [] };
|
|
1002
|
+
}
|
|
953
1003
|
return {
|
|
954
|
-
isValid:
|
|
955
|
-
errors:
|
|
956
|
-
line: index + 1,
|
|
957
|
-
message: error,
|
|
958
|
-
severity: 'Error'
|
|
959
|
-
}))
|
|
1004
|
+
isValid: false,
|
|
1005
|
+
errors: [{ line: 0, message: response.statusText || 'Compilation failed', severity: 'Error' }]
|
|
960
1006
|
};
|
|
961
|
-
} catch (error
|
|
1007
|
+
} catch (error) {
|
|
1008
|
+
const err = error as { response?: { data?: { error?: { message?: string } } }; message?: string };
|
|
1009
|
+
const errorMessage = err.response?.data?.error?.message || err.message || 'Validation failed';
|
|
962
1010
|
return {
|
|
963
1011
|
isValid: false,
|
|
964
|
-
errors: [{
|
|
965
|
-
line: 0,
|
|
966
|
-
message: error.message || 'Validation failed',
|
|
967
|
-
severity: 'Error'
|
|
968
|
-
}]
|
|
1012
|
+
errors: [{ line: 0, message: errorMessage, severity: 'Error' }]
|
|
969
1013
|
};
|
|
970
1014
|
}
|
|
971
1015
|
}
|
|
@@ -979,7 +1023,7 @@ export class ProcessService extends ObjectService {
|
|
|
979
1023
|
* @example
|
|
980
1024
|
* ```typescript
|
|
981
1025
|
* const plan = await processService.getProcessExecutionPlan('ImportData');
|
|
982
|
-
*
|
|
1026
|
+
* // plan.estimatedTime => estimated execution time (ms)
|
|
983
1027
|
* ```
|
|
984
1028
|
*/
|
|
985
1029
|
public async getProcessExecutionPlan(processName: string): Promise<any> {
|
|
@@ -1059,19 +1103,20 @@ export class ProcessService extends ObjectService {
|
|
|
1059
1103
|
|
|
1060
1104
|
// Execute process asynchronously
|
|
1061
1105
|
this.executeWithReturn(processName, parameters)
|
|
1062
|
-
.then((result:
|
|
1106
|
+
.then((result: unknown) => {
|
|
1063
1107
|
asyncOps.updateOperationStatus(
|
|
1064
1108
|
operationId,
|
|
1065
1109
|
OperationStatus.COMPLETED,
|
|
1066
1110
|
result
|
|
1067
1111
|
);
|
|
1068
1112
|
})
|
|
1069
|
-
.catch((error:
|
|
1113
|
+
.catch((error: unknown) => {
|
|
1114
|
+
const message = (error as { message?: string })?.message ?? String(error);
|
|
1070
1115
|
asyncOps.updateOperationStatus(
|
|
1071
1116
|
operationId,
|
|
1072
1117
|
OperationStatus.FAILED,
|
|
1073
1118
|
undefined,
|
|
1074
|
-
|
|
1119
|
+
message
|
|
1075
1120
|
);
|
|
1076
1121
|
});
|
|
1077
1122
|
|
|
@@ -1087,9 +1132,7 @@ export class ProcessService extends ObjectService {
|
|
|
1087
1132
|
* @example
|
|
1088
1133
|
* ```typescript
|
|
1089
1134
|
* const status = await processService.pollProcessExecution(operationId);
|
|
1090
|
-
* if (status === OperationStatus.COMPLETED)
|
|
1091
|
-
* console.log('Process completed!');
|
|
1092
|
-
* }
|
|
1135
|
+
* // if (status === OperationStatus.COMPLETED) handle completion
|
|
1093
1136
|
* ```
|
|
1094
1137
|
*/
|
|
1095
1138
|
public async pollProcessExecution(operationId: string): Promise<OperationStatus> {
|
|
@@ -1110,7 +1153,7 @@ export class ProcessService extends ObjectService {
|
|
|
1110
1153
|
* @example
|
|
1111
1154
|
* ```typescript
|
|
1112
1155
|
* await processService.cancelProcessExecution(operationId);
|
|
1113
|
-
*
|
|
1156
|
+
* // cancellation is acknowledged once the promise resolves
|
|
1114
1157
|
* ```
|
|
1115
1158
|
*/
|
|
1116
1159
|
public async cancelProcessExecution(operationId: string): Promise<void> {
|
|
@@ -1121,4 +1164,4 @@ export class ProcessService extends ObjectService {
|
|
|
1121
1164
|
|
|
1122
1165
|
await asyncOps.cancelAsyncOperation(operationId);
|
|
1123
1166
|
}
|
|
1124
|
-
}
|
|
1167
|
+
}
|