tm1npm 1.6.0 → 2.1.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/objects/Axis.d.ts +1 -0
- package/lib/objects/Axis.d.ts.map +1 -1
- package/lib/objects/Axis.js +3 -0
- package/lib/objects/Chore.d.ts +2 -2
- package/lib/objects/Chore.d.ts.map +1 -1
- package/lib/objects/Chore.js +7 -13
- package/lib/objects/Cube.d.ts.map +1 -1
- package/lib/objects/Cube.js +2 -1
- package/lib/objects/Hierarchy.js +10 -10
- package/lib/objects/MDXView.d.ts +2 -0
- package/lib/objects/MDXView.d.ts.map +1 -1
- package/lib/objects/MDXView.js +30 -9
- package/lib/objects/NativeView.d.ts +5 -5
- package/lib/objects/NativeView.d.ts.map +1 -1
- package/lib/objects/NativeView.js +17 -34
- package/lib/objects/Process.d.ts +8 -3
- package/lib/objects/Process.d.ts.map +1 -1
- package/lib/objects/Process.js +143 -33
- package/lib/objects/Subset.d.ts.map +1 -1
- package/lib/objects/Subset.js +10 -3
- package/lib/objects/User.d.ts +5 -5
- package/lib/objects/User.d.ts.map +1 -1
- package/lib/objects/User.js +14 -23
- 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/debuggerService.test.js +3 -1
- package/lib/tests/objectModelParity.test.js +362 -11
- package/lib/tests/objects.improved.test.js +28 -5
- 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/lib/tests/user.issue61.test.d.ts +2 -0
- package/lib/tests/user.issue61.test.d.ts.map +1 -0
- package/lib/tests/user.issue61.test.js +180 -0
- package/lib/utils/Utils.d.ts +6 -1
- package/lib/utils/Utils.d.ts.map +1 -1
- package/lib/utils/Utils.js +56 -7
- package/package.json +1 -1
- package/src/objects/Axis.ts +4 -0
- package/src/objects/Chore.ts +7 -14
- package/src/objects/Cube.ts +2 -1
- package/src/objects/Hierarchy.ts +11 -11
- package/src/objects/MDXView.ts +29 -9
- package/src/objects/NativeView.ts +26 -42
- package/src/objects/Process.ts +182 -66
- package/src/objects/Subset.ts +17 -3
- package/src/objects/User.ts +17 -23
- 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/debuggerService.test.ts +3 -1
- package/src/tests/objectModelParity.test.ts +456 -11
- package/src/tests/objects.improved.test.ts +41 -9
- 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
- package/src/tests/user.issue61.test.ts +206 -0
- package/src/utils/Utils.ts +60 -7
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
8
|
const TM1Service_1 = require("../services/TM1Service");
|
|
9
9
|
const RestService_1 = require("../services/RestService");
|
|
10
|
+
const User_1 = require("../objects/User");
|
|
10
11
|
// Mock all service dependencies
|
|
11
12
|
jest.mock('../services/RestService');
|
|
12
13
|
jest.mock('../services/DimensionService');
|
|
@@ -22,6 +23,19 @@ jest.mock('../services/FileService');
|
|
|
22
23
|
jest.mock('../services/SessionService');
|
|
23
24
|
jest.mock('../services/ServerService');
|
|
24
25
|
jest.mock('../services/MonitoringService');
|
|
26
|
+
jest.mock('../services/AnnotationService');
|
|
27
|
+
jest.mock('../services/ChoreService');
|
|
28
|
+
jest.mock('../services/GitService');
|
|
29
|
+
jest.mock('../services/ApplicationService');
|
|
30
|
+
jest.mock('../services/SandboxService');
|
|
31
|
+
jest.mock('../services/JobService');
|
|
32
|
+
jest.mock('../services/UserService');
|
|
33
|
+
jest.mock('../services/ThreadService');
|
|
34
|
+
jest.mock('../services/TransactionLogService');
|
|
35
|
+
jest.mock('../services/MessageLogService');
|
|
36
|
+
jest.mock('../services/ConfigurationService');
|
|
37
|
+
jest.mock('../services/AuditLogService');
|
|
38
|
+
jest.mock('../services/LoggerService');
|
|
25
39
|
describe('TM1Service', () => {
|
|
26
40
|
let tm1Service;
|
|
27
41
|
let mockRestService;
|
|
@@ -56,6 +70,8 @@ describe('TM1Service', () => {
|
|
|
56
70
|
setSandbox: jest.fn(),
|
|
57
71
|
getSandbox: jest.fn().mockReturnValue('test-sandbox'),
|
|
58
72
|
isLoggedIn: jest.fn().mockReturnValue(true),
|
|
73
|
+
getVersion: jest.fn().mockResolvedValue('12.0.0'),
|
|
74
|
+
version: undefined,
|
|
59
75
|
};
|
|
60
76
|
// Mock RestService constructor
|
|
61
77
|
RestService_1.RestService.mockImplementation(() => mockRestService);
|
|
@@ -75,6 +91,7 @@ describe('TM1Service', () => {
|
|
|
75
91
|
expect(tm1Service.security).toBeDefined();
|
|
76
92
|
expect(tm1Service.files).toBeDefined();
|
|
77
93
|
expect(tm1Service.sessions).toBeDefined();
|
|
94
|
+
expect(tm1Service.applications).toBeDefined();
|
|
78
95
|
});
|
|
79
96
|
test('should create RestService with provided config', () => {
|
|
80
97
|
expect(RestService_1.RestService).toHaveBeenCalledWith(mockConfig);
|
|
@@ -123,14 +140,16 @@ describe('TM1Service', () => {
|
|
|
123
140
|
});
|
|
124
141
|
});
|
|
125
142
|
describe('User and Authentication', () => {
|
|
126
|
-
test('should get current user with whoami', async () => {
|
|
127
|
-
|
|
143
|
+
test('should get current user as User object with whoami', async () => {
|
|
144
|
+
const expectedUser = new User_1.User('test-user', ['ADMIN'], 'Test User', undefined, User_1.UserType.Admin, true);
|
|
128
145
|
const mockSecurityService = {
|
|
129
|
-
getCurrentUser: jest.fn().mockResolvedValue(
|
|
146
|
+
getCurrentUser: jest.fn().mockResolvedValue(expectedUser)
|
|
130
147
|
};
|
|
131
148
|
tm1Service.security = mockSecurityService;
|
|
132
149
|
const result = await tm1Service.whoami();
|
|
133
|
-
expect(result).
|
|
150
|
+
expect(result).toBeInstanceOf(User_1.User);
|
|
151
|
+
expect(result.name).toBe('test-user');
|
|
152
|
+
expect(result).toBe(expectedUser);
|
|
134
153
|
expect(mockSecurityService.getCurrentUser).toHaveBeenCalledTimes(1);
|
|
135
154
|
});
|
|
136
155
|
test('should check if user is logged in', () => {
|
|
@@ -157,11 +176,11 @@ describe('TM1Service', () => {
|
|
|
157
176
|
expect(result).toEqual({ metadata: 'test-metadata' });
|
|
158
177
|
expect(mockRestService.get).toHaveBeenCalledWith('/$metadata');
|
|
159
178
|
});
|
|
160
|
-
test('should get TM1 version', async () => {
|
|
161
|
-
mockRestService.
|
|
179
|
+
test('should get TM1 version via cached RestService.getVersion', async () => {
|
|
180
|
+
mockRestService.getVersion.mockResolvedValueOnce('12.0.0');
|
|
162
181
|
const result = await tm1Service.getVersion();
|
|
163
182
|
expect(result).toBe('12.0.0');
|
|
164
|
-
expect(mockRestService.
|
|
183
|
+
expect(mockRestService.getVersion).toHaveBeenCalledTimes(1);
|
|
165
184
|
});
|
|
166
185
|
test('should handle metadata retrieval errors', async () => {
|
|
167
186
|
const metadataError = new Error('Metadata not available');
|
|
@@ -170,7 +189,7 @@ describe('TM1Service', () => {
|
|
|
170
189
|
});
|
|
171
190
|
test('should handle version retrieval errors', async () => {
|
|
172
191
|
const versionError = new Error('Version not available');
|
|
173
|
-
mockRestService.
|
|
192
|
+
mockRestService.getVersion.mockRejectedValueOnce(versionError);
|
|
174
193
|
await expect(tm1Service.getVersion()).rejects.toThrow('Version not available');
|
|
175
194
|
});
|
|
176
195
|
});
|
|
@@ -286,5 +305,58 @@ describe('TM1Service', () => {
|
|
|
286
305
|
expect(monitoring1).toBe(monitoring2);
|
|
287
306
|
});
|
|
288
307
|
});
|
|
308
|
+
describe('Lazy Services (Issue #82)', () => {
|
|
309
|
+
const lazyServiceNames = [
|
|
310
|
+
'annotations',
|
|
311
|
+
'chores',
|
|
312
|
+
'git',
|
|
313
|
+
'sandboxes',
|
|
314
|
+
'jobs',
|
|
315
|
+
'users',
|
|
316
|
+
'threads',
|
|
317
|
+
'transactionLogs',
|
|
318
|
+
'messageLogs',
|
|
319
|
+
'configuration',
|
|
320
|
+
'auditLogs',
|
|
321
|
+
'loggers',
|
|
322
|
+
];
|
|
323
|
+
test.each(lazyServiceNames)('should lazy-initialize %s service', (serviceName) => {
|
|
324
|
+
const instance = tm1Service[serviceName];
|
|
325
|
+
expect(instance).toBeDefined();
|
|
326
|
+
});
|
|
327
|
+
test.each(lazyServiceNames)('should cache %s service instance across accesses', (serviceName) => {
|
|
328
|
+
const first = tm1Service[serviceName];
|
|
329
|
+
const second = tm1Service[serviceName];
|
|
330
|
+
expect(second).toBe(first);
|
|
331
|
+
});
|
|
332
|
+
});
|
|
333
|
+
describe('Version Getter (Issue #82)', () => {
|
|
334
|
+
test('should expose cached version via sync getter', () => {
|
|
335
|
+
Object.defineProperty(mockRestService, 'version', {
|
|
336
|
+
get: () => '11.8.0',
|
|
337
|
+
configurable: true,
|
|
338
|
+
});
|
|
339
|
+
expect(tm1Service.version).toBe('11.8.0');
|
|
340
|
+
});
|
|
341
|
+
test('should return undefined when version has not been fetched yet', () => {
|
|
342
|
+
Object.defineProperty(mockRestService, 'version', {
|
|
343
|
+
get: () => undefined,
|
|
344
|
+
configurable: true,
|
|
345
|
+
});
|
|
346
|
+
expect(tm1Service.version).toBeUndefined();
|
|
347
|
+
});
|
|
348
|
+
});
|
|
349
|
+
describe('reConnect (Issue #82)', () => {
|
|
350
|
+
test('should call connect without disconnecting (tm1py parity)', async () => {
|
|
351
|
+
await tm1Service.reConnect();
|
|
352
|
+
expect(mockRestService.connect).toHaveBeenCalledTimes(1);
|
|
353
|
+
expect(mockRestService.disconnect).not.toHaveBeenCalled();
|
|
354
|
+
});
|
|
355
|
+
test('should propagate errors from connect', async () => {
|
|
356
|
+
mockRestService.connect.mockRejectedValueOnce(new Error('Connect failed'));
|
|
357
|
+
await expect(tm1Service.reConnect()).rejects.toThrow('Connect failed');
|
|
358
|
+
expect(mockRestService.connect).toHaveBeenCalledTimes(1);
|
|
359
|
+
});
|
|
360
|
+
});
|
|
289
361
|
});
|
|
290
362
|
//# sourceMappingURL=tm1Service.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user.issue61.test.d.ts","sourceRoot":"","sources":["../../src/tests/user.issue61.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const User_1 = require("../objects/User");
|
|
4
|
+
const Utils_1 = require("../utils/Utils");
|
|
5
|
+
describe('UserType — string enum (issue #61)', () => {
|
|
6
|
+
test('should produce correct string names from toString()', () => {
|
|
7
|
+
expect(User_1.UserType.User.toString()).toBe('User');
|
|
8
|
+
expect(User_1.UserType.SecurityAdmin.toString()).toBe('SecurityAdmin');
|
|
9
|
+
expect(User_1.UserType.DataAdmin.toString()).toBe('DataAdmin');
|
|
10
|
+
expect(User_1.UserType.Admin.toString()).toBe('Admin');
|
|
11
|
+
expect(User_1.UserType.OperationsAdmin.toString()).toBe('OperationsAdmin');
|
|
12
|
+
});
|
|
13
|
+
test('should serialize Type field as string name in body', () => {
|
|
14
|
+
const user = new User_1.User('Alice', [], undefined, undefined, User_1.UserType.Admin);
|
|
15
|
+
const body = JSON.parse(user.body);
|
|
16
|
+
expect(body.Type).toBe('Admin');
|
|
17
|
+
});
|
|
18
|
+
test('should serialize User type as "User" string in body', () => {
|
|
19
|
+
const user = new User_1.User('Alice', []);
|
|
20
|
+
const body = JSON.parse(user.body);
|
|
21
|
+
expect(body.Type).toBe('User');
|
|
22
|
+
});
|
|
23
|
+
test('should parse string value in userType setter', () => {
|
|
24
|
+
const user = new User_1.User('Alice', []);
|
|
25
|
+
user.userType = 'Admin';
|
|
26
|
+
expect(user.userType).toBe(User_1.UserType.Admin);
|
|
27
|
+
});
|
|
28
|
+
test('should parse case-insensitive string in userType setter', () => {
|
|
29
|
+
const user = new User_1.User('Alice', []);
|
|
30
|
+
user.userType = 'admin';
|
|
31
|
+
expect(user.userType).toBe(User_1.UserType.Admin);
|
|
32
|
+
});
|
|
33
|
+
test('should throw on invalid userType string', () => {
|
|
34
|
+
const user = new User_1.User('Alice', []);
|
|
35
|
+
expect(() => { user.userType = 'InvalidType'; }).toThrow();
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
describe('User — group auto-detection (issue #61)', () => {
|
|
39
|
+
test('should detect Admin type from groups', () => {
|
|
40
|
+
const user = new User_1.User('Alice', ['Admin']);
|
|
41
|
+
expect(user.userType).toBe(User_1.UserType.Admin);
|
|
42
|
+
});
|
|
43
|
+
test('should detect SecurityAdmin type from groups', () => {
|
|
44
|
+
const user = new User_1.User('Alice', ['SecurityAdmin']);
|
|
45
|
+
expect(user.userType).toBe(User_1.UserType.SecurityAdmin);
|
|
46
|
+
});
|
|
47
|
+
test('should detect DataAdmin type from groups', () => {
|
|
48
|
+
const user = new User_1.User('Alice', ['DataAdmin']);
|
|
49
|
+
expect(user.userType).toBe(User_1.UserType.DataAdmin);
|
|
50
|
+
});
|
|
51
|
+
test('should detect OperationsAdmin type from groups', () => {
|
|
52
|
+
const user = new User_1.User('Alice', ['OperationsAdmin']);
|
|
53
|
+
expect(user.userType).toBe(User_1.UserType.OperationsAdmin);
|
|
54
|
+
});
|
|
55
|
+
test('should default to User type when no special groups present', () => {
|
|
56
|
+
const user = new User_1.User('Alice', ['Everyone', 'SomeTeam']);
|
|
57
|
+
expect(user.userType).toBe(User_1.UserType.User);
|
|
58
|
+
});
|
|
59
|
+
test('should add correct string group when userType set to Admin', () => {
|
|
60
|
+
const user = new User_1.User('Alice', []);
|
|
61
|
+
user.userType = User_1.UserType.Admin;
|
|
62
|
+
expect(user.groups).toContain('Admin');
|
|
63
|
+
});
|
|
64
|
+
test('should not add a group when userType set to User', () => {
|
|
65
|
+
const user = new User_1.User('Alice', []);
|
|
66
|
+
user.userType = User_1.UserType.User;
|
|
67
|
+
expect(user.groups).toHaveLength(0);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
describe('User — isXxxAdmin getters (issue #61)', () => {
|
|
71
|
+
test('should return true for isAdmin when Admin group present', () => {
|
|
72
|
+
const user = new User_1.User('Alice', ['Admin']);
|
|
73
|
+
expect(user.isAdmin).toBe(true);
|
|
74
|
+
});
|
|
75
|
+
test('should return false for isAdmin when Admin group absent', () => {
|
|
76
|
+
const user = new User_1.User('Alice', ['Everyone']);
|
|
77
|
+
expect(user.isAdmin).toBe(false);
|
|
78
|
+
});
|
|
79
|
+
test('should return true for isDataAdmin when Admin group present', () => {
|
|
80
|
+
const user = new User_1.User('Alice', ['Admin']);
|
|
81
|
+
expect(user.isDataAdmin).toBe(true);
|
|
82
|
+
});
|
|
83
|
+
test('should return true for isDataAdmin when DataAdmin group present', () => {
|
|
84
|
+
const user = new User_1.User('Alice', ['DataAdmin']);
|
|
85
|
+
expect(user.isDataAdmin).toBe(true);
|
|
86
|
+
});
|
|
87
|
+
test('should return true for isSecurityAdmin when SecurityAdmin group present', () => {
|
|
88
|
+
const user = new User_1.User('Alice', ['SecurityAdmin']);
|
|
89
|
+
expect(user.isSecurityAdmin).toBe(true);
|
|
90
|
+
});
|
|
91
|
+
test('should return true for isOpsAdmin when OperationsAdmin group present', () => {
|
|
92
|
+
const user = new User_1.User('Alice', ['OperationsAdmin']);
|
|
93
|
+
expect(user.isOpsAdmin).toBe(true);
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
describe('User.fromDict — Type field parsing (issue #61)', () => {
|
|
97
|
+
test('should parse Type string field from API response dict', () => {
|
|
98
|
+
const dict = {
|
|
99
|
+
Name: 'Alice',
|
|
100
|
+
FriendlyName: 'Alice',
|
|
101
|
+
Groups: [{ Name: 'Admin' }],
|
|
102
|
+
Type: 'Admin',
|
|
103
|
+
Enabled: true,
|
|
104
|
+
};
|
|
105
|
+
const user = User_1.User.fromDict(dict);
|
|
106
|
+
expect(user.userType).toBe(User_1.UserType.Admin);
|
|
107
|
+
});
|
|
108
|
+
test('should parse SecurityAdmin Type field from API response dict', () => {
|
|
109
|
+
const dict = {
|
|
110
|
+
Name: 'Bob',
|
|
111
|
+
FriendlyName: 'Bob',
|
|
112
|
+
Groups: [{ Name: 'SecurityAdmin' }],
|
|
113
|
+
Type: 'SecurityAdmin',
|
|
114
|
+
Enabled: true,
|
|
115
|
+
};
|
|
116
|
+
const user = User_1.User.fromDict(dict);
|
|
117
|
+
expect(user.userType).toBe(User_1.UserType.SecurityAdmin);
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
describe('CaseAndSpaceInsensitiveSet — casing preservation (issue #61)', () => {
|
|
121
|
+
test('should preserve original casing of first insertion', () => {
|
|
122
|
+
const set = new Utils_1.CaseAndSpaceInsensitiveSet();
|
|
123
|
+
set.add('Admin');
|
|
124
|
+
set.add('ADMIN');
|
|
125
|
+
set.add('admin');
|
|
126
|
+
expect(set.size).toBe(1);
|
|
127
|
+
expect(Array.from(set)).toEqual(['Admin']);
|
|
128
|
+
});
|
|
129
|
+
test('should find by any casing variant', () => {
|
|
130
|
+
const set = new Utils_1.CaseAndSpaceInsensitiveSet();
|
|
131
|
+
set.add('HelloWorld');
|
|
132
|
+
expect(set.has('helloworld')).toBe(true);
|
|
133
|
+
expect(set.has('HELLOWORLD')).toBe(true);
|
|
134
|
+
expect(set.has('Hello World')).toBe(true);
|
|
135
|
+
expect(set.has('hello world')).toBe(true);
|
|
136
|
+
});
|
|
137
|
+
test('should return first-inserted casing during iteration', () => {
|
|
138
|
+
const set = new Utils_1.CaseAndSpaceInsensitiveSet();
|
|
139
|
+
set.add('MyGroup');
|
|
140
|
+
set.add('mygroup');
|
|
141
|
+
set.add('MYGROUP');
|
|
142
|
+
expect(Array.from(set)).toEqual(['MyGroup']);
|
|
143
|
+
});
|
|
144
|
+
test('should delete by any casing variant', () => {
|
|
145
|
+
const set = new Utils_1.CaseAndSpaceInsensitiveSet();
|
|
146
|
+
set.add('Admin');
|
|
147
|
+
expect(set.delete('ADMIN')).toBe(true);
|
|
148
|
+
expect(set.size).toBe(0);
|
|
149
|
+
expect(set.has('Admin')).toBe(false);
|
|
150
|
+
});
|
|
151
|
+
test('should return false when deleting non-existent entry', () => {
|
|
152
|
+
const set = new Utils_1.CaseAndSpaceInsensitiveSet();
|
|
153
|
+
expect(set.delete('NonExistent')).toBe(false);
|
|
154
|
+
});
|
|
155
|
+
test('should maintain correct size after operations', () => {
|
|
156
|
+
const set = new Utils_1.CaseAndSpaceInsensitiveSet();
|
|
157
|
+
set.add('A');
|
|
158
|
+
set.add('B');
|
|
159
|
+
set.add('a'); // duplicate — ignored
|
|
160
|
+
expect(set.size).toBe(2);
|
|
161
|
+
set.delete('A');
|
|
162
|
+
expect(set.size).toBe(1);
|
|
163
|
+
});
|
|
164
|
+
test('should clear all entries', () => {
|
|
165
|
+
const set = new Utils_1.CaseAndSpaceInsensitiveSet();
|
|
166
|
+
set.add('A');
|
|
167
|
+
set.add('B');
|
|
168
|
+
set.clear();
|
|
169
|
+
expect(set.size).toBe(0);
|
|
170
|
+
expect(set.has('A')).toBe(false);
|
|
171
|
+
});
|
|
172
|
+
test('should handle values with spaces', () => {
|
|
173
|
+
const set = new Utils_1.CaseAndSpaceInsensitiveSet();
|
|
174
|
+
set.add('Data Admin');
|
|
175
|
+
expect(set.has('dataadmin')).toBe(true);
|
|
176
|
+
expect(set.has('DataAdmin')).toBe(true);
|
|
177
|
+
expect(Array.from(set)).toEqual(['Data Admin']);
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
//# sourceMappingURL=user.issue61.test.js.map
|
package/lib/utils/Utils.d.ts
CHANGED
|
@@ -20,14 +20,17 @@ export declare class CaseAndSpaceInsensitiveMap<T> extends Map<string, T> {
|
|
|
20
20
|
delete(key: string): boolean;
|
|
21
21
|
}
|
|
22
22
|
export declare class CaseAndSpaceInsensitiveSet extends Set<string> {
|
|
23
|
-
private
|
|
23
|
+
private _normalizedMap;
|
|
24
|
+
constructor(values?: Iterable<string>);
|
|
24
25
|
add(value: string): this;
|
|
25
26
|
has(value: string): boolean;
|
|
26
27
|
delete(value: string): boolean;
|
|
28
|
+
clear(): void;
|
|
27
29
|
}
|
|
28
30
|
export declare function caseAndSpaceInsensitiveEquals(str1: string, str2: string): boolean;
|
|
29
31
|
export declare function lowerAndDropSpaces(str: string): string;
|
|
30
32
|
export declare function escapeODataValue(str: string): string;
|
|
33
|
+
export declare function buildUrlFriendlyObjectName(objectName: string): string;
|
|
31
34
|
export declare function formatUrl(template: string, ...args: string[]): string;
|
|
32
35
|
export declare function extractCellsetCells(cellset: any): any[];
|
|
33
36
|
export declare function buildMdxFromAxes(axes: any[]): string;
|
|
@@ -59,6 +62,7 @@ export declare function getMdxElementFromAttribute(attribute: string, cube: stri
|
|
|
59
62
|
export declare function buildMdxTuple(members: string[]): string;
|
|
60
63
|
export declare function verifyVersion(actualVersion: string, requiredVersion: string): boolean;
|
|
61
64
|
export declare function readObjectNameFromUrl(url: string): string;
|
|
65
|
+
export declare function readObjectNameFromUrl(url: string, pattern: string | RegExp): string | null;
|
|
62
66
|
export declare function parseODataBindUrl(url: string): string[];
|
|
63
67
|
export declare function integerizeVersion(version: string): number;
|
|
64
68
|
export declare function frameToSignificantValue(value: any): any;
|
|
@@ -74,6 +78,7 @@ export declare const Utils: {
|
|
|
74
78
|
CaseAndSpaceInsensitiveSet: typeof CaseAndSpaceInsensitiveSet;
|
|
75
79
|
caseAndSpaceInsensitiveEquals: typeof caseAndSpaceInsensitiveEquals;
|
|
76
80
|
lowerAndDropSpaces: typeof lowerAndDropSpaces;
|
|
81
|
+
buildUrlFriendlyObjectName: typeof buildUrlFriendlyObjectName;
|
|
77
82
|
formatUrl: typeof formatUrl;
|
|
78
83
|
extractCellsetCells: typeof extractCellsetCells;
|
|
79
84
|
buildMdxFromAxes: typeof buildMdxFromAxes;
|
package/lib/utils/Utils.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Utils.d.ts","sourceRoot":"","sources":["../../src/utils/Utils.ts"],"names":[],"mappings":"AAAA,qBAAa,2BAA2B,CAAC,CAAC,CAAE,SAAQ,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;IAC9D,OAAO,CAAC,YAAY;IAIpB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI;IAIhC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS;IAI/B,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAIzB,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;CAG/B;AAED,qBAAa,iCAAiC,CAAC,CAAC,CAAE,SAAQ,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;IACpE,OAAO,CAAC,YAAY;IAIpB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI;IAIhC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS;IAI/B,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAIzB,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;CAG/B;AAED,qBAAa,0BAA0B,CAAC,CAAC,CAAE,SAAQ,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;IAC7D,OAAO,CAAC,YAAY;IAIpB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI;IAIhC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS;IAI/B,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAIzB,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;CAG/B;AAED,qBAAa,0BAA2B,SAAQ,GAAG,CAAC,MAAM,CAAC;IACvD,OAAO,CAAC,cAAc;
|
|
1
|
+
{"version":3,"file":"Utils.d.ts","sourceRoot":"","sources":["../../src/utils/Utils.ts"],"names":[],"mappings":"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"}
|
package/lib/utils/Utils.js
CHANGED
|
@@ -4,6 +4,7 @@ exports.Utils = exports.HTTPAdapterWithSocketOptions = exports.CaseAndSpaceInsen
|
|
|
4
4
|
exports.caseAndSpaceInsensitiveEquals = caseAndSpaceInsensitiveEquals;
|
|
5
5
|
exports.lowerAndDropSpaces = lowerAndDropSpaces;
|
|
6
6
|
exports.escapeODataValue = escapeODataValue;
|
|
7
|
+
exports.buildUrlFriendlyObjectName = buildUrlFriendlyObjectName;
|
|
7
8
|
exports.formatUrl = formatUrl;
|
|
8
9
|
exports.extractCellsetCells = extractCellsetCells;
|
|
9
10
|
exports.buildMdxFromAxes = buildMdxFromAxes;
|
|
@@ -90,17 +91,37 @@ class CaseAndSpaceInsensitiveMap extends Map {
|
|
|
90
91
|
}
|
|
91
92
|
exports.CaseAndSpaceInsensitiveMap = CaseAndSpaceInsensitiveMap;
|
|
92
93
|
class CaseAndSpaceInsensitiveSet extends Set {
|
|
93
|
-
|
|
94
|
-
|
|
94
|
+
constructor(values) {
|
|
95
|
+
super();
|
|
96
|
+
this._normalizedMap = new Map();
|
|
97
|
+
if (values) {
|
|
98
|
+
for (const v of values) {
|
|
99
|
+
this.add(v);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
95
102
|
}
|
|
96
103
|
add(value) {
|
|
97
|
-
|
|
104
|
+
const key = lowerAndDropSpaces(value);
|
|
105
|
+
if (!this._normalizedMap.has(key)) {
|
|
106
|
+
this._normalizedMap.set(key, value);
|
|
107
|
+
super.add(value);
|
|
108
|
+
}
|
|
109
|
+
return this;
|
|
98
110
|
}
|
|
99
111
|
has(value) {
|
|
100
|
-
return
|
|
112
|
+
return this._normalizedMap.has(lowerAndDropSpaces(value));
|
|
101
113
|
}
|
|
102
114
|
delete(value) {
|
|
103
|
-
|
|
115
|
+
const key = lowerAndDropSpaces(value);
|
|
116
|
+
const original = this._normalizedMap.get(key);
|
|
117
|
+
if (original === undefined)
|
|
118
|
+
return false;
|
|
119
|
+
this._normalizedMap.delete(key);
|
|
120
|
+
return super.delete(original);
|
|
121
|
+
}
|
|
122
|
+
clear() {
|
|
123
|
+
this._normalizedMap.clear();
|
|
124
|
+
super.clear();
|
|
104
125
|
}
|
|
105
126
|
}
|
|
106
127
|
exports.CaseAndSpaceInsensitiveSet = CaseAndSpaceInsensitiveSet;
|
|
@@ -118,6 +139,14 @@ function lowerAndDropSpaces(str) {
|
|
|
118
139
|
function escapeODataValue(str) {
|
|
119
140
|
return str.replace(/'/g, "''");
|
|
120
141
|
}
|
|
142
|
+
function buildUrlFriendlyObjectName(objectName) {
|
|
143
|
+
return objectName
|
|
144
|
+
.replace(/'/g, "''")
|
|
145
|
+
.replace(/%/g, "%25")
|
|
146
|
+
.replace(/#/g, "%23")
|
|
147
|
+
.replace(/\?/g, "%3F")
|
|
148
|
+
.replace(/&/g, "%26");
|
|
149
|
+
}
|
|
121
150
|
function formatUrl(template, ...args) {
|
|
122
151
|
let url = template;
|
|
123
152
|
for (const arg of args) {
|
|
@@ -329,8 +358,27 @@ function verifyVersion(actualVersion, requiredVersion) {
|
|
|
329
358
|
}
|
|
330
359
|
return true; // Equal versions
|
|
331
360
|
}
|
|
332
|
-
function readObjectNameFromUrl(url) {
|
|
333
|
-
//
|
|
361
|
+
function readObjectNameFromUrl(url, pattern) {
|
|
362
|
+
// tm1py parity: when `pattern` is provided, behave like
|
|
363
|
+
// `re.match(pattern, url)` + `unquote(match.group(1))` — anchored at start,
|
|
364
|
+
// URL-decodes the first capture group, returns null on no match.
|
|
365
|
+
if (pattern !== undefined) {
|
|
366
|
+
const re = typeof pattern === 'string'
|
|
367
|
+
? new RegExp(pattern.startsWith('^') ? pattern : '^' + pattern)
|
|
368
|
+
: pattern;
|
|
369
|
+
const m = url.match(re);
|
|
370
|
+
if (!m || m[1] === undefined)
|
|
371
|
+
return null;
|
|
372
|
+
try {
|
|
373
|
+
return decodeURIComponent(m[1]);
|
|
374
|
+
}
|
|
375
|
+
catch (_a) {
|
|
376
|
+
// tm1py parity: urllib.parse.unquote is permissive on malformed %XX
|
|
377
|
+
// (returns the original string). decodeURIComponent throws — fall back.
|
|
378
|
+
return m[1];
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
// Backward-compat: extract first ('name') segment, return '' on no match
|
|
334
382
|
const match = url.match(/\('([^']+)'\)/);
|
|
335
383
|
return match ? match[1] : '';
|
|
336
384
|
}
|
|
@@ -381,6 +429,7 @@ exports.Utils = {
|
|
|
381
429
|
CaseAndSpaceInsensitiveSet,
|
|
382
430
|
caseAndSpaceInsensitiveEquals,
|
|
383
431
|
lowerAndDropSpaces,
|
|
432
|
+
buildUrlFriendlyObjectName,
|
|
384
433
|
formatUrl,
|
|
385
434
|
extractCellsetCells,
|
|
386
435
|
buildMdxFromAxes,
|
package/package.json
CHANGED
package/src/objects/Axis.ts
CHANGED
package/src/objects/Chore.ts
CHANGED
|
@@ -148,11 +148,11 @@ export class Chore extends TM1Object {
|
|
|
148
148
|
return body;
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
-
public
|
|
152
|
-
/**
|
|
151
|
+
public setFrequency(frequency: ChoreFrequency, startTime?: ChoreStartTime): void {
|
|
152
|
+
/** Replace the chore frequency (and optionally start time)
|
|
153
153
|
*
|
|
154
154
|
* :param frequency: new ChoreFrequency
|
|
155
|
-
* :param
|
|
155
|
+
* :param startTime: new ChoreStartTime (optional)
|
|
156
156
|
*/
|
|
157
157
|
this._frequency = frequency;
|
|
158
158
|
if (startTime) {
|
|
@@ -197,22 +197,15 @@ export class Chore extends TM1Object {
|
|
|
197
197
|
return { [this._name]: processNames };
|
|
198
198
|
}
|
|
199
199
|
|
|
200
|
-
public
|
|
201
|
-
/**
|
|
200
|
+
public reschedule(days: number = 0, hours: number = 0, minutes: number = 0, seconds: number = 0): void {
|
|
201
|
+
/** Reschedule the chore by adding time to the current start time.
|
|
202
|
+
*
|
|
202
203
|
* :param days: Days to add
|
|
203
204
|
* :param hours: Hours to add
|
|
204
205
|
* :param minutes: Minutes to add
|
|
205
206
|
* :param seconds: Seconds to add
|
|
206
207
|
*/
|
|
207
|
-
|
|
208
|
-
const newDateTime = new Date(this._startTime.datetime);
|
|
209
|
-
newDateTime.setDate(newDateTime.getDate() + days);
|
|
210
|
-
newDateTime.setHours(newDateTime.getHours() + hours);
|
|
211
|
-
newDateTime.setMinutes(newDateTime.getMinutes() + minutes);
|
|
212
|
-
newDateTime.setSeconds(newDateTime.getSeconds() + seconds);
|
|
213
|
-
|
|
214
|
-
this._startTime = new ChoreStartTime(newDateTime, this._startTime.tz);
|
|
215
|
-
}
|
|
208
|
+
this._startTime.add(days, hours, minutes, seconds);
|
|
216
209
|
}
|
|
217
210
|
|
|
218
211
|
public static fromJson(choreAsJson: string): Chore {
|
package/src/objects/Cube.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { TM1Object } from './TM1Object';
|
|
2
2
|
import { Rules } from './Rules';
|
|
3
|
+
import { buildUrlFriendlyObjectName } from '../utils/Utils';
|
|
3
4
|
|
|
4
5
|
export class Cube extends TM1Object {
|
|
5
6
|
/** Abstraction of a TM1 Cube
|
|
@@ -113,7 +114,7 @@ export class Cube extends TM1Object {
|
|
|
113
114
|
private constructBody(): any {
|
|
114
115
|
const body: any = {
|
|
115
116
|
Name: this._name,
|
|
116
|
-
Dimensions: this._dimensions.map(dim => ({
|
|
117
|
+
"Dimensions@odata.bind": this._dimensions.map(dim => `Dimensions('${buildUrlFriendlyObjectName(dim)}')`)
|
|
117
118
|
};
|
|
118
119
|
|
|
119
120
|
if (this._rules) {
|
package/src/objects/Hierarchy.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { TM1Object } from './TM1Object';
|
|
2
2
|
import { Element, ElementType } from './Element';
|
|
3
3
|
import { ElementAttribute } from './ElementAttribute';
|
|
4
|
-
import { lowerAndDropSpaces } from '../utils/Utils';
|
|
4
|
+
import { lowerAndDropSpaces, caseAndSpaceInsensitiveEquals } from '../utils/Utils';
|
|
5
5
|
|
|
6
6
|
export class Hierarchy extends TM1Object {
|
|
7
7
|
private _name: string;
|
|
@@ -29,7 +29,7 @@ export class Hierarchy extends TM1Object {
|
|
|
29
29
|
|
|
30
30
|
if (elements) {
|
|
31
31
|
for (const elem of elements) {
|
|
32
|
-
this._elements.set(elem.name
|
|
32
|
+
this._elements.set(lowerAndDropSpaces(elem.name), elem);
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
|
|
@@ -98,7 +98,7 @@ export class Hierarchy extends TM1Object {
|
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
public get elementNames(): string[] {
|
|
101
|
-
return Array.from(this._elements.
|
|
101
|
+
return Array.from(this._elements.values()).map(e => e.name);
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
public get elementAttributes(): ElementAttribute[] {
|
|
@@ -172,19 +172,19 @@ export class Hierarchy extends TM1Object {
|
|
|
172
172
|
}
|
|
173
173
|
|
|
174
174
|
public addElement(element: Element): void {
|
|
175
|
-
this._elements.set(element.name
|
|
175
|
+
this._elements.set(lowerAndDropSpaces(element.name), element);
|
|
176
176
|
}
|
|
177
177
|
|
|
178
178
|
public removeElement(elementName: string): boolean {
|
|
179
|
-
return this._elements.delete(elementName
|
|
179
|
+
return this._elements.delete(lowerAndDropSpaces(elementName));
|
|
180
180
|
}
|
|
181
181
|
|
|
182
182
|
public getElement(elementName: string): Element | undefined {
|
|
183
|
-
return this._elements.get(elementName
|
|
183
|
+
return this._elements.get(lowerAndDropSpaces(elementName));
|
|
184
184
|
}
|
|
185
185
|
|
|
186
186
|
public hasElement(elementName: string): boolean {
|
|
187
|
-
return this._elements.has(elementName
|
|
187
|
+
return this._elements.has(lowerAndDropSpaces(elementName));
|
|
188
188
|
}
|
|
189
189
|
|
|
190
190
|
public addEdge(parentName: string, componentName: string, weight: number = 1): void {
|
|
@@ -216,7 +216,7 @@ export class Hierarchy extends TM1Object {
|
|
|
216
216
|
|
|
217
217
|
public removeElementAttribute(attributeName: string): boolean {
|
|
218
218
|
const index = this._elementAttributes.findIndex(ea =>
|
|
219
|
-
ea.name
|
|
219
|
+
caseAndSpaceInsensitiveEquals(ea.name, attributeName));
|
|
220
220
|
|
|
221
221
|
if (index !== -1) {
|
|
222
222
|
this._elementAttributes.splice(index, 1);
|
|
@@ -227,12 +227,12 @@ export class Hierarchy extends TM1Object {
|
|
|
227
227
|
|
|
228
228
|
public getElementAttribute(attributeName: string): ElementAttribute | undefined {
|
|
229
229
|
return this._elementAttributes.find(ea =>
|
|
230
|
-
ea.name
|
|
230
|
+
caseAndSpaceInsensitiveEquals(ea.name, attributeName));
|
|
231
231
|
}
|
|
232
232
|
|
|
233
233
|
public hasElementAttribute(attributeName: string): boolean {
|
|
234
234
|
return this._elementAttributes.some(ea =>
|
|
235
|
-
ea.name
|
|
235
|
+
caseAndSpaceInsensitiveEquals(ea.name, attributeName));
|
|
236
236
|
}
|
|
237
237
|
|
|
238
238
|
public getAncestors(elementName: string, recursive: boolean = false): string[] {
|
|
@@ -337,7 +337,7 @@ export class Hierarchy extends TM1Object {
|
|
|
337
337
|
const element = this._elements.get(oldKey)!;
|
|
338
338
|
element.name = newName;
|
|
339
339
|
this._elements.delete(oldKey);
|
|
340
|
-
this._elements.set(newName
|
|
340
|
+
this._elements.set(lowerAndDropSpaces(newName), element);
|
|
341
341
|
}
|
|
342
342
|
|
|
343
343
|
// Rebuild edges replacing all occurrences of oldName
|