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.
Files changed (78) hide show
  1. package/CHANGELOG.md +89 -0
  2. package/lib/index.d.ts +1 -1
  3. package/lib/index.d.ts.map +1 -1
  4. package/lib/services/ApplicationService.d.ts +19 -3
  5. package/lib/services/ApplicationService.d.ts.map +1 -1
  6. package/lib/services/ApplicationService.js +232 -6
  7. package/lib/services/AsyncOperationService.d.ts +8 -1
  8. package/lib/services/AsyncOperationService.d.ts.map +1 -1
  9. package/lib/services/AsyncOperationService.js +69 -26
  10. package/lib/services/ElementService.d.ts +67 -1
  11. package/lib/services/ElementService.d.ts.map +1 -1
  12. package/lib/services/ElementService.js +214 -0
  13. package/lib/services/FileService.d.ts.map +1 -1
  14. package/lib/services/HierarchyService.d.ts +26 -0
  15. package/lib/services/HierarchyService.d.ts.map +1 -1
  16. package/lib/services/HierarchyService.js +306 -0
  17. package/lib/services/ProcessService.d.ts +40 -22
  18. package/lib/services/ProcessService.d.ts.map +1 -1
  19. package/lib/services/ProcessService.js +118 -111
  20. package/lib/services/RestService.d.ts +213 -25
  21. package/lib/services/RestService.d.ts.map +1 -1
  22. package/lib/services/RestService.js +841 -263
  23. package/lib/services/SubsetService.d.ts +2 -0
  24. package/lib/services/SubsetService.d.ts.map +1 -1
  25. package/lib/services/SubsetService.js +33 -0
  26. package/lib/services/TM1Service.d.ts +44 -1
  27. package/lib/services/TM1Service.d.ts.map +1 -1
  28. package/lib/services/TM1Service.js +96 -4
  29. package/lib/services/index.d.ts +1 -1
  30. package/lib/services/index.d.ts.map +1 -1
  31. package/lib/tests/100PercentParityCheck.test.js +23 -6
  32. package/lib/tests/applicationService.issue38.test.d.ts +5 -0
  33. package/lib/tests/applicationService.issue38.test.d.ts.map +1 -0
  34. package/lib/tests/applicationService.issue38.test.js +237 -0
  35. package/lib/tests/asyncOperationService.test.js +51 -45
  36. package/lib/tests/bugfix28.test.js +12 -4
  37. package/lib/tests/elementService.issue37.test.d.ts +5 -0
  38. package/lib/tests/elementService.issue37.test.d.ts.map +1 -0
  39. package/lib/tests/elementService.issue37.test.js +413 -0
  40. package/lib/tests/elementService.issue38.test.d.ts +5 -0
  41. package/lib/tests/elementService.issue38.test.d.ts.map +1 -0
  42. package/lib/tests/elementService.issue38.test.js +79 -0
  43. package/lib/tests/hierarchyService.issue38.test.d.ts +5 -0
  44. package/lib/tests/hierarchyService.issue38.test.d.ts.map +1 -0
  45. package/lib/tests/hierarchyService.issue38.test.js +460 -0
  46. package/lib/tests/processService.comprehensive.test.js +9 -9
  47. package/lib/tests/processService.test.js +234 -0
  48. package/lib/tests/restService.test.d.ts +0 -4
  49. package/lib/tests/restService.test.d.ts.map +1 -1
  50. package/lib/tests/restService.test.js +1558 -143
  51. package/lib/tests/subsetService.issue38.test.d.ts +5 -0
  52. package/lib/tests/subsetService.issue38.test.d.ts.map +1 -0
  53. package/lib/tests/subsetService.issue38.test.js +113 -0
  54. package/lib/tests/tm1Service.test.js +80 -8
  55. package/package.json +1 -1
  56. package/src/index.ts +1 -1
  57. package/src/services/ApplicationService.ts +282 -10
  58. package/src/services/AsyncOperationService.ts +76 -29
  59. package/src/services/ElementService.ts +322 -1
  60. package/src/services/FileService.ts +3 -3
  61. package/src/services/HierarchyService.ts +419 -1
  62. package/src/services/ProcessService.ts +185 -142
  63. package/src/services/RestService.ts +1021 -267
  64. package/src/services/SubsetService.ts +48 -0
  65. package/src/services/TM1Service.ts +127 -6
  66. package/src/services/index.ts +1 -1
  67. package/src/tests/100PercentParityCheck.test.ts +29 -8
  68. package/src/tests/applicationService.issue38.test.ts +293 -0
  69. package/src/tests/asyncOperationService.test.ts +52 -48
  70. package/src/tests/bugfix28.test.ts +12 -4
  71. package/src/tests/elementService.issue37.test.ts +571 -0
  72. package/src/tests/elementService.issue38.test.ts +103 -0
  73. package/src/tests/hierarchyService.issue38.test.ts +599 -0
  74. package/src/tests/processService.comprehensive.test.ts +10 -10
  75. package/src/tests/processService.test.ts +295 -3
  76. package/src/tests/restService.test.ts +1844 -139
  77. package/src/tests/subsetService.issue38.test.ts +182 -0
  78. package/src/tests/tm1Service.test.ts +95 -11
@@ -381,8 +381,11 @@ describe('AsyncOperationService', () => {
381
381
  });
382
382
  asyncService.updateOperationStatus(id3, OperationStatus.COMPLETED);
383
383
 
384
+ // HTTP 202 = still running on the /_async endpoint.
384
385
  mockRestService.get = jest.fn().mockResolvedValue({
385
- data: { Status: 'Running' }
386
+ status: 202,
387
+ headers: {},
388
+ data: {}
386
389
  });
387
390
 
388
391
  const activeOps = await asyncService.listActiveAsyncOperations();
@@ -641,91 +644,92 @@ describe('AsyncOperationService', () => {
641
644
  });
642
645
  });
643
646
 
647
+ // Status is inferred from the /_async('{id}') HTTP status code and the
648
+ // v12 `asyncresult` header; there is no `.Status` envelope on the new endpoint.
649
+ // These tests bypass createAsyncOperation (which tags operations as
650
+ // trackedLocally: true) to exercise the server-polling branch directly.
644
651
  describe('Server Status Mapping', () => {
645
- test('should map CompletedSuccessfully to COMPLETED', async () => {
646
- const operationId = await asyncService.createAsyncOperation({
652
+ const injectServerOperation = (id: string) => {
653
+ (asyncService as any).operations.set(id, {
654
+ id,
647
655
  type: OperationType.PROCESS_EXECUTION,
648
- name: 'TestProcess'
656
+ name: 'TestProcess',
657
+ status: OperationStatus.RUNNING,
658
+ startTime: new Date(),
659
+ trackedLocally: false
649
660
  });
661
+ };
650
662
 
651
- asyncService.updateOperationStatus(operationId, OperationStatus.RUNNING);
663
+ test('should map HTTP 200 without asyncresult header to COMPLETED', async () => {
664
+ injectServerOperation('srv-1');
652
665
 
653
666
  mockRestService.get = jest.fn().mockResolvedValue({
654
- data: { Status: 'CompletedSuccessfully', Result: { value: 42 } }
667
+ status: 200,
668
+ headers: {},
669
+ data: { value: 42 }
655
670
  });
656
671
 
657
- const status = await asyncService.getAsyncOperationStatus(operationId);
672
+ const status = await asyncService.getAsyncOperationStatus('srv-1');
658
673
  expect(status).toBe(OperationStatus.COMPLETED);
659
674
 
660
- const operation = asyncService.getOperation(operationId);
675
+ const operation = asyncService.getOperation('srv-1');
661
676
  expect(operation?.result).toEqual({ value: 42 });
662
677
  });
663
678
 
664
- test('should map CompletedWithErrors to FAILED', async () => {
665
- const operationId = await asyncService.createAsyncOperation({
666
- type: OperationType.PROCESS_EXECUTION,
667
- name: 'TestProcess'
668
- });
669
-
670
- asyncService.updateOperationStatus(operationId, OperationStatus.RUNNING);
679
+ test('should map HTTP 200 with non-2xx asyncresult header to FAILED', async () => {
680
+ injectServerOperation('srv-2');
671
681
 
672
682
  mockRestService.get = jest.fn().mockResolvedValue({
673
- data: { Status: 'CompletedWithErrors', Error: 'Process failed at line 10' }
683
+ status: 200,
684
+ headers: { asyncresult: '500 Internal Server Error' },
685
+ data: {}
674
686
  });
675
687
 
676
- const status = await asyncService.getAsyncOperationStatus(operationId);
688
+ const status = await asyncService.getAsyncOperationStatus('srv-2');
677
689
  expect(status).toBe(OperationStatus.FAILED);
678
690
 
679
- const operation = asyncService.getOperation(operationId);
680
- expect(operation?.error).toBe('Process failed at line 10');
691
+ const operation = asyncService.getOperation('srv-2');
692
+ expect(operation?.error).toBe('500 Internal Server Error');
681
693
  });
682
694
 
683
- test('should map Cancelled status correctly', async () => {
684
- const operationId = await asyncService.createAsyncOperation({
685
- type: OperationType.PROCESS_EXECUTION,
686
- name: 'TestProcess'
687
- });
695
+ test('should map thrown TM1RestException to FAILED', async () => {
696
+ injectServerOperation('srv-3');
688
697
 
689
- asyncService.updateOperationStatus(operationId, OperationStatus.RUNNING);
690
-
691
- mockRestService.get = jest.fn().mockResolvedValue({
692
- data: { Status: 'Cancelled' }
693
- });
698
+ const { TM1RestException } = require('../exceptions/TM1Exception');
699
+ mockRestService.get = jest.fn().mockRejectedValue(
700
+ new TM1RestException('Server error', 500)
701
+ );
694
702
 
695
- const status = await asyncService.getAsyncOperationStatus(operationId);
696
- expect(status).toBe(OperationStatus.CANCELLED);
703
+ const status = await asyncService.getAsyncOperationStatus('srv-3');
704
+ expect(status).toBe(OperationStatus.FAILED);
697
705
  });
698
706
 
699
- test('should map Timeout status correctly', async () => {
700
- const operationId = await asyncService.createAsyncOperation({
701
- type: OperationType.PROCESS_EXECUTION,
702
- name: 'TestProcess'
703
- });
704
-
705
- asyncService.updateOperationStatus(operationId, OperationStatus.RUNNING);
707
+ test('should map HTTP 202 to RUNNING', async () => {
708
+ injectServerOperation('srv-4');
706
709
 
707
710
  mockRestService.get = jest.fn().mockResolvedValue({
708
- data: { Status: 'Timeout' }
711
+ status: 202,
712
+ headers: {},
713
+ data: {}
709
714
  });
710
715
 
711
- const status = await asyncService.getAsyncOperationStatus(operationId);
712
- expect(status).toBe(OperationStatus.TIMEOUT);
716
+ const status = await asyncService.getAsyncOperationStatus('srv-4');
717
+ expect(status).toBe(OperationStatus.RUNNING);
713
718
  });
714
719
 
715
- test('should default to PENDING for unknown status', async () => {
720
+ test('locally-tracked operations skip server polling and return cached status', async () => {
716
721
  const operationId = await asyncService.createAsyncOperation({
717
722
  type: OperationType.PROCESS_EXECUTION,
718
- name: 'TestProcess'
723
+ name: 'LocalProcess'
719
724
  });
720
-
721
725
  asyncService.updateOperationStatus(operationId, OperationStatus.RUNNING);
722
726
 
723
- mockRestService.get = jest.fn().mockResolvedValue({
724
- data: { Status: 'UnknownStatus' }
725
- });
727
+ const getSpy = jest.fn();
728
+ mockRestService.get = getSpy;
726
729
 
727
730
  const status = await asyncService.getAsyncOperationStatus(operationId);
728
- expect(status).toBe(OperationStatus.PENDING);
731
+ expect(status).toBe(OperationStatus.RUNNING);
732
+ expect(getSpy).not.toHaveBeenCalled();
729
733
  });
730
734
  });
731
735
 
@@ -11,6 +11,7 @@ import { SubsetService } from '../services/SubsetService';
11
11
  import { ApplicationService } from '../services/ApplicationService';
12
12
  import { SessionService } from '../services/SessionService';
13
13
  import { RestService } from '../services/RestService';
14
+ import { TM1RestException } from '../exceptions/TM1Exception';
14
15
  import { Subset } from '../objects/Subset';
15
16
  import { CubeApplication, ApplicationTypes } from '../objects/Application';
16
17
 
@@ -166,13 +167,20 @@ describe('Bug #12 - ApplicationService getNames() should not inject /api/v1/ pre
166
167
  });
167
168
 
168
169
  test('getNames() with isPrivate should use PrivateContents', async () => {
169
- mockRest.get.mockResolvedValue(createMockResponse({ value: [{ Name: 'App1' }] }));
170
+ // _resolvePath probes public path first (returns 404), then tries private
171
+ const notFound = new TM1RestException('Not Found', 404, { status: 404 });
172
+ mockRest.get
173
+ .mockRejectedValueOnce(notFound) // public probe → 404
174
+ .mockRejectedValueOnce(notFound) // private probe → 404 (falls through to findBoundary)
175
+ .mockResolvedValueOnce(createMockResponse({ value: [] })) // boundary probe succeeds
176
+ .mockResolvedValueOnce(createMockResponse({ value: [{ Name: 'App1' }] })); // actual getNames
170
177
 
171
178
  await appService.getNames('Planning', true);
172
179
 
173
- const calledUrl = mockRest.get.mock.calls[0][0] as string;
174
- expect(calledUrl).not.toContain('/api/v1/');
175
- expect(calledUrl).toBe("/Contents('Applications')/Contents('Planning')/PrivateContents");
180
+ // The final GET should target PrivateContents
181
+ const lastCall = mockRest.get.mock.calls[mockRest.get.mock.calls.length - 1][0] as string;
182
+ expect(lastCall).not.toContain('/api/v1/');
183
+ expect(lastCall).toContain('PrivateContents');
176
184
  });
177
185
 
178
186
  test('getNames() with empty path', async () => {