tm1npm 2.0.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/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/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/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/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/user.issue61.test.ts +206 -0
- package/src/utils/Utils.ts +60 -7
|
@@ -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
|
package/src/objects/MDXView.ts
CHANGED
|
@@ -7,6 +7,27 @@ export class MDXView extends View {
|
|
|
7
7
|
* IMPORTANT. MDXViews can't be seen through the old TM1 clients (Archict, Perspectives). They do exist though!
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
+
private static readonly _DYNAMIC_PROPERTIES_EXCLUDED_KEYS: ReadonlySet<string> = new Set([
|
|
11
|
+
'@odata.type',
|
|
12
|
+
'@odata.context',
|
|
13
|
+
'@odata.etag',
|
|
14
|
+
'Name',
|
|
15
|
+
'MDX',
|
|
16
|
+
'Cube',
|
|
17
|
+
'Attributes',
|
|
18
|
+
'LocalizedAttributes',
|
|
19
|
+
]);
|
|
20
|
+
|
|
21
|
+
private static _filterDynamicProperties(properties: Record<string, any>): Record<string, any> {
|
|
22
|
+
const out: Record<string, any> = {};
|
|
23
|
+
for (const key of Object.keys(properties)) {
|
|
24
|
+
if (!MDXView._DYNAMIC_PROPERTIES_EXCLUDED_KEYS.has(key)) {
|
|
25
|
+
out[key] = properties[key];
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return out;
|
|
29
|
+
}
|
|
30
|
+
|
|
10
31
|
private _mdx: string;
|
|
11
32
|
private _dynamicProperties: Record<string, any>;
|
|
12
33
|
|
|
@@ -85,21 +106,20 @@ export class MDXView extends View {
|
|
|
85
106
|
|
|
86
107
|
public static fromDict(viewAsDict: any, cubeName?: string): MDXView {
|
|
87
108
|
return new MDXView(
|
|
88
|
-
viewAsDict.Cube
|
|
109
|
+
cubeName ? cubeName : viewAsDict.Cube.Name,
|
|
89
110
|
viewAsDict.Name,
|
|
90
111
|
viewAsDict.MDX,
|
|
91
|
-
viewAsDict
|
|
112
|
+
MDXView._filterDynamicProperties(viewAsDict),
|
|
92
113
|
);
|
|
93
114
|
}
|
|
94
115
|
|
|
95
116
|
private constructBody(): string {
|
|
96
|
-
const mdxViewAsDict: any = {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
}
|
|
117
|
+
const mdxViewAsDict: Record<string, any> = {
|
|
118
|
+
'@odata.type': 'ibm.tm1.api.v1.MDXView',
|
|
119
|
+
Name: this._name,
|
|
120
|
+
MDX: this._mdx,
|
|
121
|
+
...MDXView._filterDynamicProperties(this._dynamicProperties),
|
|
122
|
+
};
|
|
103
123
|
return JSON.stringify(mdxViewAsDict);
|
|
104
124
|
}
|
|
105
125
|
}
|