tm1npm 1.6.0 → 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/services/ApplicationService.d.ts.map +1 -1
- 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/FileService.d.ts.map +1 -1
- package/lib/services/ProcessService.d.ts +18 -13
- package/lib/services/ProcessService.d.ts.map +1 -1
- package/lib/services/ProcessService.js +28 -17
- package/lib/services/RestService.d.ts +213 -25
- package/lib/services/RestService.d.ts.map +1 -1
- package/lib/services/RestService.js +840 -271
- package/lib/services/TM1Service.d.ts +42 -1
- package/lib/services/TM1Service.d.ts.map +1 -1
- package/lib/services/TM1Service.js +94 -4
- package/lib/tests/asyncOperationService.test.js +51 -45
- package/lib/tests/processService.comprehensive.test.js +2 -2
- package/lib/tests/processService.test.js +20 -6
- 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/tm1Service.test.js +80 -8
- package/package.json +1 -1
- package/src/services/ApplicationService.ts +4 -4
- package/src/services/AsyncOperationService.ts +76 -29
- package/src/services/FileService.ts +3 -3
- package/src/services/ProcessService.ts +67 -37
- package/src/services/RestService.ts +1020 -278
- package/src/services/TM1Service.ts +124 -6
- package/src/tests/asyncOperationService.test.ts +52 -48
- package/src/tests/processService.comprehensive.test.ts +3 -3
- package/src/tests/processService.test.ts +21 -9
- package/src/tests/restService.test.ts +1844 -139
- package/src/tests/tm1Service.test.ts +95 -11
|
@@ -8,6 +8,19 @@ import { BulkService } from './BulkService';
|
|
|
8
8
|
import { AsyncOperationService } from './AsyncOperationService';
|
|
9
9
|
import { PowerBiService } from './PowerBiService';
|
|
10
10
|
import { ApplicationService } from './ApplicationService';
|
|
11
|
+
import { AnnotationService } from './AnnotationService';
|
|
12
|
+
import { ChoreService } from './ChoreService';
|
|
13
|
+
import { GitService } from './GitService';
|
|
14
|
+
import { SandboxService } from './SandboxService';
|
|
15
|
+
import { JobService } from './JobService';
|
|
16
|
+
import { UserService } from './UserService';
|
|
17
|
+
import { ThreadService } from './ThreadService';
|
|
18
|
+
import { TransactionLogService } from './TransactionLogService';
|
|
19
|
+
import { MessageLogService } from './MessageLogService';
|
|
20
|
+
import { ConfigurationService } from './ConfigurationService';
|
|
21
|
+
import { AuditLogService } from './AuditLogService';
|
|
22
|
+
import { LoggerService } from './LoggerService';
|
|
23
|
+
import { User } from '../objects/User';
|
|
11
24
|
import {
|
|
12
25
|
CubeService,
|
|
13
26
|
ElementService,
|
|
@@ -26,6 +39,19 @@ export class TM1Service {
|
|
|
26
39
|
private _server?: ServerService;
|
|
27
40
|
private _monitoring?: MonitoringService;
|
|
28
41
|
|
|
42
|
+
private _annotations?: AnnotationService;
|
|
43
|
+
private _chores?: ChoreService;
|
|
44
|
+
private _git?: GitService;
|
|
45
|
+
private _sandboxes?: SandboxService;
|
|
46
|
+
private _jobs?: JobService;
|
|
47
|
+
private _users?: UserService;
|
|
48
|
+
private _threads?: ThreadService;
|
|
49
|
+
private _transactionLogs?: TransactionLogService;
|
|
50
|
+
private _messageLogs?: MessageLogService;
|
|
51
|
+
private _configuration?: ConfigurationService;
|
|
52
|
+
private _auditLogs?: AuditLogService;
|
|
53
|
+
private _loggers?: LoggerService;
|
|
54
|
+
|
|
29
55
|
public dimensions: DimensionService;
|
|
30
56
|
public hierarchies: HierarchyService;
|
|
31
57
|
public subsets: SubsetService;
|
|
@@ -98,9 +124,92 @@ export class TM1Service {
|
|
|
98
124
|
return this._monitoring;
|
|
99
125
|
}
|
|
100
126
|
|
|
101
|
-
public
|
|
102
|
-
|
|
103
|
-
|
|
127
|
+
public get annotations(): AnnotationService {
|
|
128
|
+
if (!this._annotations) {
|
|
129
|
+
this._annotations = new AnnotationService(this._tm1Rest);
|
|
130
|
+
}
|
|
131
|
+
return this._annotations;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
public get chores(): ChoreService {
|
|
135
|
+
if (!this._chores) {
|
|
136
|
+
this._chores = new ChoreService(this._tm1Rest);
|
|
137
|
+
}
|
|
138
|
+
return this._chores;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
public get git(): GitService {
|
|
142
|
+
if (!this._git) {
|
|
143
|
+
this._git = new GitService(this._tm1Rest);
|
|
144
|
+
}
|
|
145
|
+
return this._git;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
public get sandboxes(): SandboxService {
|
|
149
|
+
if (!this._sandboxes) {
|
|
150
|
+
this._sandboxes = new SandboxService(this._tm1Rest);
|
|
151
|
+
}
|
|
152
|
+
return this._sandboxes;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
public get jobs(): JobService {
|
|
156
|
+
if (!this._jobs) {
|
|
157
|
+
this._jobs = new JobService(this._tm1Rest);
|
|
158
|
+
}
|
|
159
|
+
return this._jobs;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
public get users(): UserService {
|
|
163
|
+
if (!this._users) {
|
|
164
|
+
this._users = new UserService(this._tm1Rest);
|
|
165
|
+
}
|
|
166
|
+
return this._users;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
public get threads(): ThreadService {
|
|
170
|
+
if (!this._threads) {
|
|
171
|
+
this._threads = new ThreadService(this._tm1Rest);
|
|
172
|
+
}
|
|
173
|
+
return this._threads;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
public get transactionLogs(): TransactionLogService {
|
|
177
|
+
if (!this._transactionLogs) {
|
|
178
|
+
this._transactionLogs = new TransactionLogService(this._tm1Rest);
|
|
179
|
+
}
|
|
180
|
+
return this._transactionLogs;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
public get messageLogs(): MessageLogService {
|
|
184
|
+
if (!this._messageLogs) {
|
|
185
|
+
this._messageLogs = new MessageLogService(this._tm1Rest);
|
|
186
|
+
}
|
|
187
|
+
return this._messageLogs;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
public get configuration(): ConfigurationService {
|
|
191
|
+
if (!this._configuration) {
|
|
192
|
+
this._configuration = new ConfigurationService(this._tm1Rest);
|
|
193
|
+
}
|
|
194
|
+
return this._configuration;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
public get auditLogs(): AuditLogService {
|
|
198
|
+
if (!this._auditLogs) {
|
|
199
|
+
this._auditLogs = new AuditLogService(this._tm1Rest);
|
|
200
|
+
}
|
|
201
|
+
return this._auditLogs;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
public get loggers(): LoggerService {
|
|
205
|
+
if (!this._loggers) {
|
|
206
|
+
this._loggers = new LoggerService(this._tm1Rest);
|
|
207
|
+
}
|
|
208
|
+
return this._loggers;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
public async whoami(): Promise<User> {
|
|
212
|
+
return await this.security.getCurrentUser();
|
|
104
213
|
}
|
|
105
214
|
|
|
106
215
|
public async getMetadata(): Promise<any> {
|
|
@@ -108,9 +217,12 @@ export class TM1Service {
|
|
|
108
217
|
return response.data;
|
|
109
218
|
}
|
|
110
219
|
|
|
220
|
+
public get version(): string | undefined {
|
|
221
|
+
return this._tm1Rest.version;
|
|
222
|
+
}
|
|
223
|
+
|
|
111
224
|
public async getVersion(): Promise<string> {
|
|
112
|
-
|
|
113
|
-
return response.data.value;
|
|
225
|
+
return await this._tm1Rest.getVersion();
|
|
114
226
|
}
|
|
115
227
|
|
|
116
228
|
public get connection(): RestService {
|
|
@@ -133,6 +245,12 @@ export class TM1Service {
|
|
|
133
245
|
return this._tm1Rest.isLoggedIn();
|
|
134
246
|
}
|
|
135
247
|
|
|
248
|
+
/** Reconnects without teardown. Use reAuthenticate() for full disconnect+reconnect. */
|
|
249
|
+
public async reConnect(): Promise<void> {
|
|
250
|
+
await this._tm1Rest.connect();
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/** Full teardown + reconnect. If disconnect() throws, connect() is not attempted. */
|
|
136
254
|
public async reAuthenticate(): Promise<void> {
|
|
137
255
|
await this._tm1Rest.disconnect();
|
|
138
256
|
await this._tm1Rest.connect();
|
|
@@ -153,4 +271,4 @@ export class TM1Service {
|
|
|
153
271
|
console.warn(`Logout failed due to exception: ${error}`);
|
|
154
272
|
}
|
|
155
273
|
}
|
|
156
|
-
}
|
|
274
|
+
}
|
|
@@ -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
|
-
|
|
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
|
-
|
|
646
|
-
|
|
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
|
-
|
|
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
|
-
|
|
667
|
+
status: 200,
|
|
668
|
+
headers: {},
|
|
669
|
+
data: { value: 42 }
|
|
655
670
|
});
|
|
656
671
|
|
|
657
|
-
const status = await asyncService.getAsyncOperationStatus(
|
|
672
|
+
const status = await asyncService.getAsyncOperationStatus('srv-1');
|
|
658
673
|
expect(status).toBe(OperationStatus.COMPLETED);
|
|
659
674
|
|
|
660
|
-
const operation = asyncService.getOperation(
|
|
675
|
+
const operation = asyncService.getOperation('srv-1');
|
|
661
676
|
expect(operation?.result).toEqual({ value: 42 });
|
|
662
677
|
});
|
|
663
678
|
|
|
664
|
-
test('should map
|
|
665
|
-
|
|
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
|
-
|
|
683
|
+
status: 200,
|
|
684
|
+
headers: { asyncresult: '500 Internal Server Error' },
|
|
685
|
+
data: {}
|
|
674
686
|
});
|
|
675
687
|
|
|
676
|
-
const status = await asyncService.getAsyncOperationStatus(
|
|
688
|
+
const status = await asyncService.getAsyncOperationStatus('srv-2');
|
|
677
689
|
expect(status).toBe(OperationStatus.FAILED);
|
|
678
690
|
|
|
679
|
-
const operation = asyncService.getOperation(
|
|
680
|
-
expect(operation?.error).toBe('
|
|
691
|
+
const operation = asyncService.getOperation('srv-2');
|
|
692
|
+
expect(operation?.error).toBe('500 Internal Server Error');
|
|
681
693
|
});
|
|
682
694
|
|
|
683
|
-
test('should map
|
|
684
|
-
|
|
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
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
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(
|
|
696
|
-
expect(status).toBe(OperationStatus.
|
|
703
|
+
const status = await asyncService.getAsyncOperationStatus('srv-3');
|
|
704
|
+
expect(status).toBe(OperationStatus.FAILED);
|
|
697
705
|
});
|
|
698
706
|
|
|
699
|
-
test('should map
|
|
700
|
-
|
|
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
|
-
|
|
711
|
+
status: 202,
|
|
712
|
+
headers: {},
|
|
713
|
+
data: {}
|
|
709
714
|
});
|
|
710
715
|
|
|
711
|
-
const status = await asyncService.getAsyncOperationStatus(
|
|
712
|
-
expect(status).toBe(OperationStatus.
|
|
716
|
+
const status = await asyncService.getAsyncOperationStatus('srv-4');
|
|
717
|
+
expect(status).toBe(OperationStatus.RUNNING);
|
|
713
718
|
});
|
|
714
719
|
|
|
715
|
-
test('
|
|
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: '
|
|
723
|
+
name: 'LocalProcess'
|
|
719
724
|
});
|
|
720
|
-
|
|
721
725
|
asyncService.updateOperationStatus(operationId, OperationStatus.RUNNING);
|
|
722
726
|
|
|
723
|
-
|
|
724
|
-
|
|
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.
|
|
731
|
+
expect(status).toBe(OperationStatus.RUNNING);
|
|
732
|
+
expect(getSpy).not.toHaveBeenCalled();
|
|
729
733
|
});
|
|
730
734
|
});
|
|
731
735
|
|
|
@@ -307,7 +307,7 @@ describe('ProcessService - Comprehensive Tests', () => {
|
|
|
307
307
|
expect(mockRestService.post).toHaveBeenCalledWith(
|
|
308
308
|
"/Processes('TestProcess')/tm1.ExecuteWithReturn?$expand=*",
|
|
309
309
|
"{}",
|
|
310
|
-
{ timeout:
|
|
310
|
+
{ timeout: 30 }
|
|
311
311
|
);
|
|
312
312
|
});
|
|
313
313
|
|
|
@@ -940,7 +940,7 @@ describe('ProcessService - Comprehensive Tests', () => {
|
|
|
940
940
|
expect(mockRestService.post).toHaveBeenCalledWith(
|
|
941
941
|
expect.any(String),
|
|
942
942
|
expect.any(String),
|
|
943
|
-
{ timeout:
|
|
943
|
+
{ timeout: 60 }
|
|
944
944
|
);
|
|
945
945
|
});
|
|
946
946
|
|
|
@@ -1244,4 +1244,4 @@ describe('ProcessService - Comprehensive Tests', () => {
|
|
|
1244
1244
|
expect(mockRestService.get).toHaveBeenCalledTimes(3);
|
|
1245
1245
|
});
|
|
1246
1246
|
});
|
|
1247
|
-
});
|
|
1247
|
+
});
|
|
@@ -459,9 +459,16 @@ describe('ProcessService Tests', () => {
|
|
|
459
459
|
|
|
460
460
|
describe('Process Async Polling', () => {
|
|
461
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.
|
|
462
466
|
(mockRestService as any).retrieve_async_response = jest.fn().mockResolvedValue({
|
|
463
|
-
|
|
464
|
-
|
|
467
|
+
status: 200,
|
|
468
|
+
data: {
|
|
469
|
+
ProcessExecuteStatusCode: 'CompletedSuccessfully',
|
|
470
|
+
ErrorLogFile: null
|
|
471
|
+
}
|
|
465
472
|
});
|
|
466
473
|
|
|
467
474
|
const result = await processService.pollExecuteWithReturn('async-001');
|
|
@@ -472,8 +479,11 @@ describe('ProcessService Tests', () => {
|
|
|
472
479
|
|
|
473
480
|
test('pollExecuteWithReturn should return error log file when present', async () => {
|
|
474
481
|
(mockRestService as any).retrieve_async_response = jest.fn().mockResolvedValue({
|
|
475
|
-
|
|
476
|
-
|
|
482
|
+
status: 200,
|
|
483
|
+
data: {
|
|
484
|
+
ProcessExecuteStatusCode: 'CompletedWithMessages',
|
|
485
|
+
ErrorLogFile: { Filename: 'TM1ProcessError_20240101.log' }
|
|
486
|
+
}
|
|
477
487
|
});
|
|
478
488
|
|
|
479
489
|
const result = await processService.pollExecuteWithReturn('async-002');
|
|
@@ -493,10 +503,12 @@ describe('ProcessService Tests', () => {
|
|
|
493
503
|
});
|
|
494
504
|
|
|
495
505
|
test('pollExecuteWithReturn should return null for 202 (accepted/pending)', async () => {
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
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
|
+
});
|
|
500
512
|
|
|
501
513
|
const result = await processService.pollExecuteWithReturn('async-004');
|
|
502
514
|
|
|
@@ -685,4 +697,4 @@ describe('ProcessService Tests', () => {
|
|
|
685
697
|
expect(mockRestService.delete).toHaveBeenCalled();
|
|
686
698
|
});
|
|
687
699
|
});
|
|
688
|
-
});
|
|
700
|
+
});
|