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
|
@@ -397,12 +397,304 @@ describe('ProcessService Tests', () => {
|
|
|
397
397
|
|
|
398
398
|
const processNames = await processService.getAllNames();
|
|
399
399
|
expect(processNames).toContain('DebugProcess');
|
|
400
|
-
|
|
400
|
+
|
|
401
401
|
// In a real implementation, this would set debug breakpoints
|
|
402
402
|
// For now, we just verify the mock interaction
|
|
403
403
|
expect(mockRestService.get).toHaveBeenCalled();
|
|
404
|
-
|
|
404
|
+
|
|
405
405
|
console.log('✅ Debug operations handled for existing processes');
|
|
406
406
|
});
|
|
407
|
+
|
|
408
|
+
test('debugGetBreakpoints should return ProcessDebugBreakpoint array', async () => {
|
|
409
|
+
mockRestService.get.mockResolvedValue(createMockResponse({
|
|
410
|
+
value: [
|
|
411
|
+
{
|
|
412
|
+
'@odata.type': '#ibm.tm1.api.v1.ProcessDebugContextLineBreakpoint',
|
|
413
|
+
ID: 1,
|
|
414
|
+
Enabled: true,
|
|
415
|
+
HitMode: 'BreakAlways',
|
|
416
|
+
HitCount: 0,
|
|
417
|
+
Expression: '',
|
|
418
|
+
ProcessName: 'TestProcess',
|
|
419
|
+
Procedure: 'Prolog',
|
|
420
|
+
LineNumber: 5
|
|
421
|
+
}
|
|
422
|
+
]
|
|
423
|
+
}));
|
|
424
|
+
|
|
425
|
+
const result = await processService.debugGetBreakpoints('debug-123');
|
|
426
|
+
|
|
427
|
+
expect(result).toHaveLength(1);
|
|
428
|
+
expect(result[0].breakpointId).toBe(1);
|
|
429
|
+
expect(mockRestService.get).toHaveBeenCalledWith("/ProcessDebugContexts('debug-123')/Breakpoints");
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
test('debugAddBreakpoint should delegate to debugAddBreakpoints', async () => {
|
|
433
|
+
mockRestService.post.mockResolvedValue(createMockResponse({}));
|
|
434
|
+
|
|
435
|
+
const { ProcessDebugBreakpoint: BP, BreakPointType: BPType, HitMode: HM } =
|
|
436
|
+
require('../objects/ProcessDebugBreakpoint');
|
|
437
|
+
const bp = new BP(1, BPType.PROCESS_DEBUG_CONTEXT_LINE_BREAK_POINT, true, HM.BREAK_ALWAYS);
|
|
438
|
+
|
|
439
|
+
await processService.debugAddBreakpoint('debug-456', bp);
|
|
440
|
+
|
|
441
|
+
// Should POST a JSON array (delegated to debugAddBreakpoints)
|
|
442
|
+
const postCall = mockRestService.post.mock.calls[0];
|
|
443
|
+
expect(postCall[0]).toBe("/ProcessDebugContexts('debug-456')/Breakpoints");
|
|
444
|
+
const postedBody = JSON.parse(postCall[1]);
|
|
445
|
+
expect(Array.isArray(postedBody)).toBe(true);
|
|
446
|
+
expect(postedBody).toHaveLength(1);
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
test('debugRemoveBreakpoint should DELETE correct URL', async () => {
|
|
450
|
+
mockRestService.delete.mockResolvedValue(createMockResponse({}));
|
|
451
|
+
|
|
452
|
+
await processService.debugRemoveBreakpoint('debug-789', 3);
|
|
453
|
+
|
|
454
|
+
expect(mockRestService.delete).toHaveBeenCalledWith(
|
|
455
|
+
"/ProcessDebugContexts('debug-789')/Breakpoints('3')"
|
|
456
|
+
);
|
|
457
|
+
});
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
describe('Process Async Polling', () => {
|
|
461
|
+
test('pollExecuteWithReturn should return parsed result on success', async () => {
|
|
462
|
+
// retrieve_async_response returns an AxiosResponse; the execute summary
|
|
463
|
+
// lives in .data. Tests that mocked the raw body directly did not
|
|
464
|
+
// reflect production shape and silently passed against the untyped
|
|
465
|
+
// parser signature.
|
|
466
|
+
(mockRestService as any).retrieve_async_response = jest.fn().mockResolvedValue({
|
|
467
|
+
status: 200,
|
|
468
|
+
data: {
|
|
469
|
+
ProcessExecuteStatusCode: 'CompletedSuccessfully',
|
|
470
|
+
ErrorLogFile: null
|
|
471
|
+
}
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
const result = await processService.pollExecuteWithReturn('async-001');
|
|
475
|
+
|
|
476
|
+
expect(result).toEqual([true, 'CompletedSuccessfully', null]);
|
|
477
|
+
expect((mockRestService as any).retrieve_async_response).toHaveBeenCalledWith('async-001');
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
test('pollExecuteWithReturn should return error log file when present', async () => {
|
|
481
|
+
(mockRestService as any).retrieve_async_response = jest.fn().mockResolvedValue({
|
|
482
|
+
status: 200,
|
|
483
|
+
data: {
|
|
484
|
+
ProcessExecuteStatusCode: 'CompletedWithMessages',
|
|
485
|
+
ErrorLogFile: { Filename: 'TM1ProcessError_20240101.log' }
|
|
486
|
+
}
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
const result = await processService.pollExecuteWithReturn('async-002');
|
|
490
|
+
|
|
491
|
+
expect(result).toEqual([false, 'CompletedWithMessages', 'TM1ProcessError_20240101.log']);
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
test('pollExecuteWithReturn should return null for 404 (not ready)', async () => {
|
|
495
|
+
const { TM1RestException } = require('../exceptions/TM1Exception');
|
|
496
|
+
(mockRestService as any).retrieve_async_response = jest.fn().mockRejectedValue(
|
|
497
|
+
new TM1RestException('Not found', 404)
|
|
498
|
+
);
|
|
499
|
+
|
|
500
|
+
const result = await processService.pollExecuteWithReturn('async-003');
|
|
501
|
+
|
|
502
|
+
expect(result).toBeNull();
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
test('pollExecuteWithReturn should return null for 202 (accepted/pending)', async () => {
|
|
506
|
+
// After the #80 refactor retrieve_async_response returns the raw AxiosResponse
|
|
507
|
+
// instead of throwing on 202; the pending path is now signalled by status === 202.
|
|
508
|
+
(mockRestService as any).retrieve_async_response = jest.fn().mockResolvedValue({
|
|
509
|
+
status: 202,
|
|
510
|
+
data: {}
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
const result = await processService.pollExecuteWithReturn('async-004');
|
|
514
|
+
|
|
515
|
+
expect(result).toBeNull();
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
test('pollExecuteWithReturn should throw on unexpected errors', async () => {
|
|
519
|
+
const { TM1RestException } = require('../exceptions/TM1Exception');
|
|
520
|
+
const serverError = new TM1RestException('Internal Server Error', 500);
|
|
521
|
+
(mockRestService as any).retrieve_async_response = jest.fn().mockRejectedValue(serverError);
|
|
522
|
+
|
|
523
|
+
await expect(processService.pollExecuteWithReturn('async-005'))
|
|
524
|
+
.rejects.toThrow('Internal Server Error');
|
|
525
|
+
});
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
describe('Process Compile (unbound)', () => {
|
|
529
|
+
test('compileProcess should POST Process body to /CompileProcess', async () => {
|
|
530
|
+
mockRestService.post.mockResolvedValue(createMockResponse({ value: [] }));
|
|
531
|
+
|
|
532
|
+
const testProcess = new Process('TestProcess', false);
|
|
533
|
+
const result = await processService.compileProcess(testProcess);
|
|
534
|
+
|
|
535
|
+
expect(result).toEqual([]);
|
|
536
|
+
const postCall = mockRestService.post.mock.calls[0];
|
|
537
|
+
expect(postCall[0]).toBe('/CompileProcess');
|
|
538
|
+
const payload = JSON.parse(postCall[1]);
|
|
539
|
+
expect(payload.Process).toBeDefined();
|
|
540
|
+
expect(payload.Process.Name).toBe('TestProcess');
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
test('compileProcess should return syntax errors when present', async () => {
|
|
544
|
+
const errors = [
|
|
545
|
+
{ LineNumber: 1, Message: 'Syntax error on line 1' }
|
|
546
|
+
];
|
|
547
|
+
mockRestService.post.mockResolvedValue(createMockResponse({ value: errors }));
|
|
548
|
+
|
|
549
|
+
const testProcess = new Process('BadProcess', false);
|
|
550
|
+
const result = await processService.compileProcess(testProcess);
|
|
551
|
+
|
|
552
|
+
expect(result).toHaveLength(1);
|
|
553
|
+
expect(result[0].Message).toBe('Syntax error on line 1');
|
|
554
|
+
});
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
describe('evaluateTiExpression', () => {
|
|
558
|
+
test('should evaluate a TI expression and return the result', async () => {
|
|
559
|
+
// Mock compileProcess (no errors)
|
|
560
|
+
mockRestService.post.mockResolvedValueOnce(createMockResponse({ value: [] }));
|
|
561
|
+
|
|
562
|
+
// Mock create (process creation)
|
|
563
|
+
mockRestService.post.mockResolvedValueOnce(createMockResponse({}, 201));
|
|
564
|
+
|
|
565
|
+
// Mock debugProcess
|
|
566
|
+
mockRestService.post.mockResolvedValueOnce(createMockResponse({
|
|
567
|
+
ID: 'debug-eval-123',
|
|
568
|
+
CallStack: [],
|
|
569
|
+
Breakpoints: []
|
|
570
|
+
}));
|
|
571
|
+
|
|
572
|
+
// Mock debugAddBreakpoint (via debugAddBreakpoints)
|
|
573
|
+
mockRestService.post.mockResolvedValueOnce(createMockResponse({}));
|
|
574
|
+
|
|
575
|
+
// Mock debugContinue (first call - run to breakpoint)
|
|
576
|
+
mockRestService.post.mockResolvedValueOnce(createMockResponse({}));
|
|
577
|
+
mockRestService.get.mockResolvedValueOnce(createMockResponse({
|
|
578
|
+
CallStack: [{ Variables: [{ Name: 'sFunc', Value: '2024-01-01' }] }]
|
|
579
|
+
}));
|
|
580
|
+
|
|
581
|
+
// Mock debugGetVariableValues
|
|
582
|
+
mockRestService.get.mockResolvedValueOnce(createMockResponse({
|
|
583
|
+
CallStack: [{ Variables: [{ Name: 'sFunc', Value: '2024-01-01' }] }]
|
|
584
|
+
}));
|
|
585
|
+
|
|
586
|
+
// Mock debugContinue (second call - finish)
|
|
587
|
+
mockRestService.post.mockResolvedValueOnce(createMockResponse({}));
|
|
588
|
+
mockRestService.get.mockResolvedValueOnce(createMockResponse({ CallStack: [] }));
|
|
589
|
+
|
|
590
|
+
// Mock delete (cleanup)
|
|
591
|
+
mockRestService.delete.mockResolvedValueOnce(createMockResponse({}, 204));
|
|
592
|
+
|
|
593
|
+
const result = await processService.evaluateTiExpression('NOW;');
|
|
594
|
+
|
|
595
|
+
expect(result).toBe('2024-01-01');
|
|
596
|
+
});
|
|
597
|
+
|
|
598
|
+
test('should strip leading "=" prefix from formula', async () => {
|
|
599
|
+
// Mock compileProcess
|
|
600
|
+
mockRestService.post.mockResolvedValueOnce(createMockResponse({ value: [] }));
|
|
601
|
+
// Mock create
|
|
602
|
+
mockRestService.post.mockResolvedValueOnce(createMockResponse({}, 201));
|
|
603
|
+
// Mock debugProcess
|
|
604
|
+
mockRestService.post.mockResolvedValueOnce(createMockResponse({
|
|
605
|
+
ID: 'debug-eq-123',
|
|
606
|
+
CallStack: []
|
|
607
|
+
}));
|
|
608
|
+
// Mock debugAddBreakpoint
|
|
609
|
+
mockRestService.post.mockResolvedValueOnce(createMockResponse({}));
|
|
610
|
+
// Mock debugContinue
|
|
611
|
+
mockRestService.post.mockResolvedValueOnce(createMockResponse({}));
|
|
612
|
+
mockRestService.get.mockResolvedValueOnce(createMockResponse({
|
|
613
|
+
CallStack: [{ Variables: [{ Name: 'sFunc', Value: '42' }] }]
|
|
614
|
+
}));
|
|
615
|
+
// Mock debugGetVariableValues
|
|
616
|
+
mockRestService.get.mockResolvedValueOnce(createMockResponse({
|
|
617
|
+
CallStack: [{ Variables: [{ Name: 'sFunc', Value: '42' }] }]
|
|
618
|
+
}));
|
|
619
|
+
// Mock debugContinue
|
|
620
|
+
mockRestService.post.mockResolvedValueOnce(createMockResponse({}));
|
|
621
|
+
mockRestService.get.mockResolvedValueOnce(createMockResponse({ CallStack: [] }));
|
|
622
|
+
// Mock delete
|
|
623
|
+
mockRestService.delete.mockResolvedValueOnce(createMockResponse({}, 204));
|
|
624
|
+
|
|
625
|
+
// Leading "=" is stripped (Excel-style formula prefix)
|
|
626
|
+
const result = await processService.evaluateTiExpression('= NumberToString(42);');
|
|
627
|
+
|
|
628
|
+
expect(result).toBe('42');
|
|
629
|
+
|
|
630
|
+
// Verify the formula was stripped correctly
|
|
631
|
+
const compileCall = mockRestService.post.mock.calls[0];
|
|
632
|
+
const compilePayload = JSON.parse(compileCall[1]);
|
|
633
|
+
expect(compilePayload.Process.PrologProcedure).toContain('sFunc = NumberToString(42);');
|
|
634
|
+
});
|
|
635
|
+
|
|
636
|
+
test('should not mangle formulas with embedded "=" characters', async () => {
|
|
637
|
+
// Mock compileProcess
|
|
638
|
+
mockRestService.post.mockResolvedValueOnce(createMockResponse({ value: [] }));
|
|
639
|
+
// Mock create
|
|
640
|
+
mockRestService.post.mockResolvedValueOnce(createMockResponse({}, 201));
|
|
641
|
+
// Mock debugProcess
|
|
642
|
+
mockRestService.post.mockResolvedValueOnce(createMockResponse({
|
|
643
|
+
ID: 'debug-embed-123',
|
|
644
|
+
CallStack: []
|
|
645
|
+
}));
|
|
646
|
+
// Mock debugAddBreakpoint
|
|
647
|
+
mockRestService.post.mockResolvedValueOnce(createMockResponse({}));
|
|
648
|
+
// Mock debugContinue
|
|
649
|
+
mockRestService.post.mockResolvedValueOnce(createMockResponse({}));
|
|
650
|
+
mockRestService.get.mockResolvedValueOnce(createMockResponse({
|
|
651
|
+
CallStack: [{ Variables: [{ Name: 'sFunc', Value: '1' }] }]
|
|
652
|
+
}));
|
|
653
|
+
// Mock debugGetVariableValues
|
|
654
|
+
mockRestService.get.mockResolvedValueOnce(createMockResponse({
|
|
655
|
+
CallStack: [{ Variables: [{ Name: 'sFunc', Value: '1' }] }]
|
|
656
|
+
}));
|
|
657
|
+
// Mock debugContinue
|
|
658
|
+
mockRestService.post.mockResolvedValueOnce(createMockResponse({}));
|
|
659
|
+
mockRestService.get.mockResolvedValueOnce(createMockResponse({ CallStack: [] }));
|
|
660
|
+
// Mock delete
|
|
661
|
+
mockRestService.delete.mockResolvedValueOnce(createMockResponse({}, 204));
|
|
662
|
+
|
|
663
|
+
// Formula with embedded "=" should NOT be mangled
|
|
664
|
+
const result = await processService.evaluateTiExpression('IF(1=1, "yes", "no");');
|
|
665
|
+
|
|
666
|
+
expect(result).toBe('1');
|
|
667
|
+
|
|
668
|
+
// Verify the full formula is preserved
|
|
669
|
+
const compileCall = mockRestService.post.mock.calls[0];
|
|
670
|
+
const compilePayload = JSON.parse(compileCall[1]);
|
|
671
|
+
expect(compilePayload.Process.PrologProcedure).toContain('sFunc = IF(1=1, "yes", "no");');
|
|
672
|
+
});
|
|
673
|
+
|
|
674
|
+
test('should throw readable error on syntax errors from compileProcess', async () => {
|
|
675
|
+
mockRestService.post.mockResolvedValueOnce(createMockResponse({
|
|
676
|
+
value: [{ LineNumber: 1, Message: 'Unexpected token' }]
|
|
677
|
+
}));
|
|
678
|
+
|
|
679
|
+
await expect(processService.evaluateTiExpression('INVALID_FUNC();'))
|
|
680
|
+
.rejects.toThrow('Line 1: Unexpected token');
|
|
681
|
+
});
|
|
682
|
+
|
|
683
|
+
test('should clean up temp process even on debug failure', async () => {
|
|
684
|
+
// Mock compileProcess (no errors)
|
|
685
|
+
mockRestService.post.mockResolvedValueOnce(createMockResponse({ value: [] }));
|
|
686
|
+
// Mock create
|
|
687
|
+
mockRestService.post.mockResolvedValueOnce(createMockResponse({}, 201));
|
|
688
|
+
// Mock debugProcess - fails
|
|
689
|
+
mockRestService.post.mockRejectedValueOnce(new Error('Debug session failed'));
|
|
690
|
+
// Mock delete (cleanup should still happen)
|
|
691
|
+
mockRestService.delete.mockResolvedValueOnce(createMockResponse({}, 204));
|
|
692
|
+
|
|
693
|
+
await expect(processService.evaluateTiExpression('NOW;'))
|
|
694
|
+
.rejects.toThrow('Debug session failed');
|
|
695
|
+
|
|
696
|
+
// Verify delete was called for cleanup
|
|
697
|
+
expect(mockRestService.delete).toHaveBeenCalled();
|
|
698
|
+
});
|
|
407
699
|
});
|
|
408
|
-
});
|
|
700
|
+
});
|