@ffschrattenecker/tm1-mcp-server 7.0.2 → 8.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 +83 -1
- package/README.md +5 -5
- package/dist/config.js +34 -17
- package/dist/connections.d.ts +18 -1
- package/dist/connections.js +34 -18
- package/dist/http-transport.js +51 -4
- package/dist/lib/callgraph/referenceIndex.js +2 -1
- package/dist/lib/callgraph/rulesLinter.d.ts +0 -19
- package/dist/lib/callgraph/rulesLinter.js +0 -590
- package/dist/lib/callgraph/tiParser.js +10 -5
- package/dist/lib/callgraph/tm1-adapter.d.ts +5 -1
- package/dist/lib/callgraph/tm1-adapter.js +41 -9
- package/dist/lib/callgraph/variableEnv.js +3 -2
- package/dist/lib/cell-address.d.ts +1 -1
- package/dist/lib/cell-address.js +2 -2
- package/dist/lib/complexity/antipatterns.js +3 -1
- package/dist/lib/complexity/comment-classifier.js +2 -1
- package/dist/lib/feeders/element-type-cache.js +4 -6
- package/dist/lib/naming/odata-filter.js +2 -1
- package/dist/lib/pro-parser.js +13 -4
- package/dist/lib/safe-regex.d.ts +0 -13
- package/dist/lib/safe-regex.js +40 -13
- package/dist/lib/ti-identifier.d.ts +11 -0
- package/dist/lib/ti-identifier.js +11 -0
- package/dist/lib/tm1-name.d.ts +9 -0
- package/dist/lib/tm1-name.js +9 -0
- package/dist/lib/v12-compat/deprecated-ti.js +2 -2
- package/dist/session-manager.d.ts +1 -0
- package/dist/session-manager.js +24 -0
- package/dist/tm1-client/connection/profile.js +2 -4
- package/dist/tm1-client/dispatcher.d.ts +1 -1
- package/dist/tm1-client/dispatcher.js +18 -6
- package/dist/tm1-client/http.d.ts +21 -0
- package/dist/tm1-client/http.js +39 -30
- package/dist/tm1-client/services/cell-service.d.ts +9 -7
- package/dist/tm1-client/services/cell-service.js +51 -25
- package/dist/tm1-client/services/chore-service.js +8 -9
- package/dist/tm1-client/services/cube-service.d.ts +14 -18
- package/dist/tm1-client/services/cube-service.js +51 -45
- package/dist/tm1-client/services/dimension-order.d.ts +13 -0
- package/dist/tm1-client/services/dimension-order.js +42 -0
- package/dist/tm1-client/services/dimension-service.js +4 -6
- package/dist/tm1-client/services/element-service.d.ts +35 -11
- package/dist/tm1-client/services/element-service.js +124 -60
- package/dist/tm1-client/services/file-service.d.ts +42 -6
- package/dist/tm1-client/services/file-service.js +225 -28
- package/dist/tm1-client/services/hierarchy-service.d.ts +19 -9
- package/dist/tm1-client/services/hierarchy-service.js +118 -12
- package/dist/tm1-client/services/monitoring-service.js +2 -5
- package/dist/tm1-client/services/odata-page.d.ts +6 -0
- package/dist/tm1-client/services/odata-page.js +8 -0
- package/dist/tm1-client/services/process-service.d.ts +1 -1
- package/dist/tm1-client/services/process-service.js +26 -26
- package/dist/tm1-client/services/security-service.js +8 -9
- package/dist/tm1-client/services/server-service.js +14 -15
- package/dist/tm1-client/services/subset-service.d.ts +23 -10
- package/dist/tm1-client/services/subset-service.js +82 -26
- package/dist/tm1-client/services/view-service.js +12 -13
- package/dist/tm1-client.js +5 -2
- package/dist/tools/analysis/check-v12-readiness.js +3 -3
- package/dist/tools/celldata/check-feeders.js +6 -6
- package/dist/tools/celldata/check-writable-coords.js +33 -22
- package/dist/tools/celldata/execute-mdx.js +10 -9
- package/dist/tools/celldata/get-view.js +10 -9
- package/dist/tools/celldata/member-ref.d.ts +15 -0
- package/dist/tools/celldata/member-ref.js +67 -0
- package/dist/tools/celldata/trace-cell-calculation.js +6 -6
- package/dist/tools/celldata/trace-feeders.js +6 -6
- package/dist/tools/celldata/write-cells.js +62 -12
- package/dist/tools/dimension-management/create-element-attribute.js +1 -1
- package/dist/tools/dimension-management/delete-hierarchy.js +3 -4
- package/dist/tools/dimension-management/get-element-attribute-values.js +7 -2
- package/dist/tools/dimension-management/list-element-attributes.js +1 -1
- package/dist/tools/dimension-management/update-element-attribute-value.js +8 -3
- package/dist/tools/dimension-management/update-element.js +14 -7
- package/dist/tools/fileops/container.d.ts +8 -0
- package/dist/tools/fileops/container.js +11 -0
- package/dist/tools/fileops/delete-file.js +4 -2
- package/dist/tools/fileops/get-file-content.js +45 -7
- package/dist/tools/fileops/list-files.js +5 -2
- package/dist/tools/fileops/search-files.js +4 -1
- package/dist/tools/fileops/upload-file.js +5 -2
- package/dist/tools/index.js +0 -2
- package/dist/tools/model-building/check-cube-rule.js +3 -2
- package/dist/tools/model-building/clear-cube.js +35 -26
- package/dist/tools/model-building/set-cube-rules.js +13 -20
- package/dist/tools/model-building/unload-cube.js +1 -1
- package/dist/tools/operations/get-cube-stats.js +1 -1
- package/dist/tools/operations/get-transaction-log.js +1 -1
- package/dist/tools/operations/list-error-logs.js +27 -2
- package/dist/tools/schemas/items-fileops.d.ts +4 -0
- package/dist/tools/schemas/items-fileops.js +1 -0
- package/dist/tools/security/list-clients.js +5 -5
- package/dist/tools/security/list-groups.js +1 -1
- package/dist/tools/subsets/create-subset.js +9 -5
- package/dist/tools/subsets/delete-subset.js +8 -3
- package/dist/tools/subsets/update-subset.js +9 -9
- package/dist/tools/ti-development/diff-process-with-file.js +4 -29
- package/dist/tools/ti-development/diff-processes.js +9 -0
- package/dist/tools/ti-development/export-process-to-git.js +9 -7
- package/dist/tools/ti-development/export-process-to-pro.js +15 -8
- package/dist/tools/ti-development/import-pro-file.js +10 -21
- package/dist/tools/ti-development/import-process-from-git.js +8 -18
- package/dist/tools/ti-development/install-pro-bundle.js +2 -2
- package/dist/tools/ti-development/upsert-process.js +26 -5
- package/npm-shrinkwrap.json +10 -9
- package/package.json +9 -5
- package/dist/tools/dimension-management/move-element.d.ts +0 -2
- package/dist/tools/dimension-management/move-element.js +0 -28
|
@@ -5,14 +5,15 @@
|
|
|
5
5
|
// as deprecated wrappers during the migration period (Phase 1 of the
|
|
6
6
|
// god-class split — see docs/ARCHITECTURE.md).
|
|
7
7
|
import { TM1Error, TM1ErrorCode } from "../../types.js";
|
|
8
|
-
import { filterClause, nameFilterPredicates, pageClauses, readCount, } from "./odata-page.js";
|
|
8
|
+
import { filterClause, nameFilterPredicates, pageClauses, readCount, odataKey, } from "./odata-page.js";
|
|
9
9
|
import { classifyExecution } from "./process-status.js";
|
|
10
|
-
|
|
11
|
-
const enc = (s) => encodeURIComponent(String(s).replace(/'/g, "''"));
|
|
10
|
+
import { DimensionOrderCache } from "./dimension-order.js";
|
|
12
11
|
export class CubeService {
|
|
13
12
|
http;
|
|
14
|
-
|
|
13
|
+
dimOrder;
|
|
14
|
+
constructor(http, dimOrder = new DimensionOrderCache(http)) {
|
|
15
15
|
this.http = http;
|
|
16
|
+
this.dimOrder = dimOrder;
|
|
16
17
|
}
|
|
17
18
|
async list(opts = {}) {
|
|
18
19
|
// Without a top-level $select TM1 returns every cube property — including
|
|
@@ -41,13 +42,12 @@ export class CubeService {
|
|
|
41
42
|
: { items, total: readCount(response) };
|
|
42
43
|
}
|
|
43
44
|
/**
|
|
44
|
-
* Return ordered dimension-name list of a cube.
|
|
45
|
+
* Return ordered dimension-name list of a cube. Cached per connection —
|
|
46
|
+
* see dimension-order.ts.
|
|
45
47
|
* GET /api/v1/Cubes('{name}')?$expand=Dimensions($select=Name)
|
|
46
48
|
*/
|
|
47
49
|
async getDimensionNames(cubeName) {
|
|
48
|
-
|
|
49
|
-
const response = await this.http.request("GET", path);
|
|
50
|
-
return response.Dimensions.map((d) => d.Name);
|
|
50
|
+
return this.dimOrder.get(cubeName);
|
|
51
51
|
}
|
|
52
52
|
/**
|
|
53
53
|
* Cheap existence probe: a single GET with $select=Name (404 → false)
|
|
@@ -62,7 +62,7 @@ export class CubeService {
|
|
|
62
62
|
*/
|
|
63
63
|
async exists(cubeName) {
|
|
64
64
|
try {
|
|
65
|
-
await this.http.request("GET", `/api/v1/Cubes('${
|
|
65
|
+
await this.http.request("GET", `/api/v1/Cubes('${odataKey(cubeName)}')?$select=Name`);
|
|
66
66
|
return true;
|
|
67
67
|
}
|
|
68
68
|
catch (e) {
|
|
@@ -79,7 +79,7 @@ export class CubeService {
|
|
|
79
79
|
await this.http.request("POST", "/api/v1/Cubes", {
|
|
80
80
|
Name: name,
|
|
81
81
|
Dimensions: dimensionNames.map((d) => ({
|
|
82
|
-
"@odata.id": `Dimensions('${
|
|
82
|
+
"@odata.id": `Dimensions('${odataKey(d)}')`,
|
|
83
83
|
})),
|
|
84
84
|
});
|
|
85
85
|
}
|
|
@@ -88,21 +88,21 @@ export class CubeService {
|
|
|
88
88
|
* DELETE /api/v1/Cubes('{name}')
|
|
89
89
|
*/
|
|
90
90
|
async delete(name) {
|
|
91
|
-
await this.http.request("DELETE", `/api/v1/Cubes('${
|
|
91
|
+
await this.http.request("DELETE", `/api/v1/Cubes('${odataKey(name)}')`);
|
|
92
92
|
}
|
|
93
93
|
/**
|
|
94
94
|
* Get the rules text for a cube.
|
|
95
95
|
* GET /api/v1/Cubes('{name}')/Rules
|
|
96
96
|
*/
|
|
97
97
|
async getRules(cubeName) {
|
|
98
|
-
const path = `/api/v1/Cubes('${
|
|
98
|
+
const path = `/api/v1/Cubes('${odataKey(cubeName)}')/Rules`;
|
|
99
99
|
// TM1 returns 404/204 both for "cube missing" and "cube has no rules";
|
|
100
100
|
// an empty 200 body (response undefined) also means "no rules". For any
|
|
101
101
|
// of those, probe `/Cubes('X')?$select=Name` to disambiguate so callers
|
|
102
102
|
// get a clear NOT_FOUND for typos instead of a silently-empty rulesText.
|
|
103
103
|
const empty = async () => {
|
|
104
104
|
try {
|
|
105
|
-
await this.http.request("GET", `/api/v1/Cubes('${
|
|
105
|
+
await this.http.request("GET", `/api/v1/Cubes('${odataKey(cubeName)}')?$select=Name`);
|
|
106
106
|
}
|
|
107
107
|
catch (probeErr) {
|
|
108
108
|
if (probeErr instanceof TM1Error && probeErr.httpStatus === 404) {
|
|
@@ -173,7 +173,7 @@ export class CubeService {
|
|
|
173
173
|
* text, so it travels in `rulesText` like every other statement.
|
|
174
174
|
*/
|
|
175
175
|
async updateRules(cubeName, rulesText) {
|
|
176
|
-
const cubePath = `/api/v1/Cubes('${
|
|
176
|
+
const cubePath = `/api/v1/Cubes('${odataKey(cubeName)}')`;
|
|
177
177
|
await this.http.request("PATCH", cubePath, { Rules: rulesText });
|
|
178
178
|
}
|
|
179
179
|
/**
|
|
@@ -181,7 +181,7 @@ export class CubeService {
|
|
|
181
181
|
* POST /api/v1/Cubes('{name}')/tm1.CheckRules with { Rules: "..." }
|
|
182
182
|
*/
|
|
183
183
|
async checkRule(cubeName, ruleText) {
|
|
184
|
-
const path = `/api/v1/Cubes('${
|
|
184
|
+
const path = `/api/v1/Cubes('${odataKey(cubeName)}')/tm1.CheckRules`;
|
|
185
185
|
const response = await this.http.request("POST", path, { Rules: ruleText });
|
|
186
186
|
return (response.value ?? []).map((e) => ({
|
|
187
187
|
message: e.Message,
|
|
@@ -189,33 +189,17 @@ export class CubeService {
|
|
|
189
189
|
}));
|
|
190
190
|
}
|
|
191
191
|
/**
|
|
192
|
-
* Clear cube
|
|
193
|
-
*
|
|
194
|
-
* A **full** clear runs an ephemeral TI with CubeClearData(). tm1.Clear is
|
|
195
|
-
* not an alternative on either build: 11.8 and 12.5 both answer
|
|
196
|
-
* "'tm1.Clear' resource can not be resolved on type 'Cube'" for a cube that
|
|
197
|
-
* exists, and neither declares a clear action anywhere in $metadata — Cube
|
|
198
|
-
* carries only Lock and Unlock. CubeClearData() is the only route measured
|
|
199
|
-
* to work.
|
|
192
|
+
* Clear every cell in a cube.
|
|
200
193
|
*
|
|
201
|
-
*
|
|
202
|
-
*
|
|
203
|
-
*
|
|
204
|
-
*
|
|
194
|
+
* The clear runs an ephemeral TI with CubeClearData(). tm1.Clear is not an
|
|
195
|
+
* alternative on either build: 11.8 and 12.5 both answer "'tm1.Clear'
|
|
196
|
+
* resource can not be resolved on type 'Cube'" for a cube that exists, and
|
|
197
|
+
* neither declares a clear action anywhere in $metadata — Cube carries only
|
|
198
|
+
* Lock and Unlock. CubeClearData() is the only route measured to work, and
|
|
199
|
+
* it takes a cube name and nothing else, so there is no region to scope.
|
|
205
200
|
*/
|
|
206
|
-
async clear(cubeName,
|
|
207
|
-
|
|
208
|
-
if (!isFullClear)
|
|
209
|
-
throw this.partialClearUnsupported(cubeName);
|
|
210
|
-
await this.clearViaTI(cubeName);
|
|
211
|
-
}
|
|
212
|
-
/** Same verdict for both versions: no tuple-selective clear on this server. */
|
|
213
|
-
partialClearUnsupported(cubeName) {
|
|
214
|
-
return new TM1Error({
|
|
215
|
-
code: TM1ErrorCode.UNSUPPORTED_OPERATION,
|
|
216
|
-
message: `Partial clearCube is not supported on TM1 ${this.http.tm1Version} (no tm1.Clear endpoint). Implement a TI process with bedrock '}bedrock.cube.data.clear' or custom CellPutN loop and call via tm1_execute_process.`,
|
|
217
|
-
endpoint: `/api/v1/Cubes('${cubeName}')/tm1.Clear`,
|
|
218
|
-
});
|
|
201
|
+
async clear(cubeName, opts) {
|
|
202
|
+
await this.clearViaTI(cubeName, opts);
|
|
219
203
|
}
|
|
220
204
|
/**
|
|
221
205
|
* Unload a cube from memory. Forces TM1 to discard the in-memory fed-cell
|
|
@@ -225,10 +209,10 @@ export class CubeService {
|
|
|
225
209
|
* POST /api/v1/Cubes('{cube}')/tm1.Unload
|
|
226
210
|
*/
|
|
227
211
|
async unload(cubeName) {
|
|
228
|
-
await this.http.request("POST", `/api/v1/Cubes('${
|
|
212
|
+
await this.http.request("POST", `/api/v1/Cubes('${odataKey(cubeName)}')/tm1.Unload`);
|
|
229
213
|
}
|
|
230
214
|
// 11.x fallback: deploy ephemeral TI with CubeClearData(), execute, delete.
|
|
231
|
-
async clearViaTI(cubeName) {
|
|
215
|
+
async clearViaTI(cubeName, opts) {
|
|
232
216
|
// Cap the sanitized cube name so the temp process name stays under TM1's
|
|
233
217
|
// ~256-char process-name limit (prefix + timestamp suffix add ~25 chars).
|
|
234
218
|
const safeName = cubeName.replace(/[^A-Za-z0-9_]/g, "_").slice(0, 200);
|
|
@@ -244,8 +228,29 @@ export class CubeService {
|
|
|
244
228
|
EpilogProcedure: "",
|
|
245
229
|
DataSource: { Type: "None" },
|
|
246
230
|
});
|
|
231
|
+
let timedOut = false;
|
|
247
232
|
try {
|
|
248
|
-
const result = await this.http
|
|
233
|
+
const result = await this.http
|
|
234
|
+
.request("POST", `/api/v1/Processes('${odataKey(procName)}')/tm1.ExecuteWithReturn`, {}, opts)
|
|
235
|
+
.catch((err) => {
|
|
236
|
+
// A client-side timeout does not stop the TI — but deleting the
|
|
237
|
+
// process does, when it is still waiting on a lock (measured on
|
|
238
|
+
// 12.5: the cube kept its data). So on a timeout the process is left
|
|
239
|
+
// in place to finish; 11.8 held the DELETE until the TI was done
|
|
240
|
+
// anyway. Reporting a bare timeout would tell the model the cube
|
|
241
|
+
// still holds its data — the one reading this must not give.
|
|
242
|
+
if (err instanceof TM1Error &&
|
|
243
|
+
err.code === TM1ErrorCode.LOCK_TIMEOUT) {
|
|
244
|
+
timedOut = true;
|
|
245
|
+
throw new TM1Error({
|
|
246
|
+
code: TM1ErrorCode.LOCK_TIMEOUT,
|
|
247
|
+
message: `Cube clear for '${cubeName}' did not answer within the request timeout, but the clear keeps running on the server — expect the cube to end up empty. Do not retry; check it with tm1_get_cube_stats once the server is idle. The temporary process '${procName}' was left in place so the clear can finish; delete it afterwards with tm1_delete_process.`,
|
|
248
|
+
endpoint: err.endpoint,
|
|
249
|
+
hint: "Pass a larger timeoutMs for big cubes or a busy server so the call waits for the clear to finish.",
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
throw err;
|
|
253
|
+
});
|
|
249
254
|
// ExecuteWithReturn returns HTTP 200 even when the process aborts; the real
|
|
250
255
|
// outcome is in ProcessExecuteStatusCode. Without this check an aborted
|
|
251
256
|
// clear (e.g. lock, security) would be reported as a successful clear.
|
|
@@ -277,13 +282,14 @@ export class CubeService {
|
|
|
277
282
|
message: verdict.outcome === "rolled_back"
|
|
278
283
|
? `Cube clear via TI was rolled back for cube '${cubeName}' (status: ${verdict.processErrorStatus}). Nothing was cleared — the cube is unchanged.`
|
|
279
284
|
: `Cube clear via TI could not be confirmed for cube '${cubeName}': ${verdict.processErrorStatus} The clear may or may not have run — check the cube before retrying.`,
|
|
280
|
-
endpoint: `/api/v1/Processes('${
|
|
285
|
+
endpoint: `/api/v1/Processes('${odataKey(procName)}')/tm1.ExecuteWithReturn`,
|
|
281
286
|
});
|
|
282
287
|
}
|
|
283
288
|
}
|
|
284
289
|
finally {
|
|
285
290
|
try {
|
|
286
|
-
|
|
291
|
+
if (!timedOut)
|
|
292
|
+
await this.http.request("DELETE", `/api/v1/Processes('${odataKey(procName)}')`);
|
|
287
293
|
}
|
|
288
294
|
catch (cleanupErr) {
|
|
289
295
|
this.http.logger.warn({ proc: procName, err: String(cleanupErr) }, "Failed to delete ephemeral clearCube TI process — manual cleanup needed");
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { TM1HttpClient } from "../http.js";
|
|
2
|
+
export declare class DimensionOrderCache {
|
|
3
|
+
private readonly http;
|
|
4
|
+
private readonly now;
|
|
5
|
+
private readonly entries;
|
|
6
|
+
constructor(http: TM1HttpClient, now?: () => number);
|
|
7
|
+
/**
|
|
8
|
+
* Ordered dimension names of `cubeName`.
|
|
9
|
+
* GET /api/v1/Cubes('{name}')?$expand=Dimensions($select=Name)
|
|
10
|
+
*/
|
|
11
|
+
get(cubeName: string): Promise<string[]>;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=dimension-order.d.ts.map
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { tm1NameKey } from "../../lib/tm1-name.js";
|
|
2
|
+
import { odataKey as enc } from "./odata-page.js";
|
|
3
|
+
const TTL_MS = 60_000;
|
|
4
|
+
const INERT_PATHS = [/\/ExecuteMDX/i, /\/Cellsets/i];
|
|
5
|
+
export class DimensionOrderCache {
|
|
6
|
+
http;
|
|
7
|
+
now;
|
|
8
|
+
entries = new Map();
|
|
9
|
+
constructor(http, now = Date.now) {
|
|
10
|
+
this.http = http;
|
|
11
|
+
this.now = now;
|
|
12
|
+
// Test doubles stub only request(); a real client always has the hook.
|
|
13
|
+
if (typeof http.onMutation === "function") {
|
|
14
|
+
http.onMutation((e) => {
|
|
15
|
+
if (!INERT_PATHS.some((re) => re.test(e.path)))
|
|
16
|
+
this.entries.clear();
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Ordered dimension names of `cubeName`.
|
|
22
|
+
* GET /api/v1/Cubes('{name}')?$expand=Dimensions($select=Name)
|
|
23
|
+
*/
|
|
24
|
+
get(cubeName) {
|
|
25
|
+
const key = tm1NameKey(cubeName);
|
|
26
|
+
const hit = this.entries.get(key);
|
|
27
|
+
if (hit && this.now() - hit.at < TTL_MS)
|
|
28
|
+
return hit.dims;
|
|
29
|
+
// The promise is cached, so concurrent callers share one request; a
|
|
30
|
+
// failure is evicted so the next call retries instead of replaying it.
|
|
31
|
+
const dims = this.http
|
|
32
|
+
.request("GET", `/api/v1/Cubes('${enc(cubeName)}')?$expand=Dimensions($select=Name)`)
|
|
33
|
+
.then((r) => r.Dimensions.map((d) => d.Name));
|
|
34
|
+
this.entries.set(key, { dims, at: this.now() });
|
|
35
|
+
dims.catch(() => {
|
|
36
|
+
if (this.entries.get(key)?.dims === dims)
|
|
37
|
+
this.entries.delete(key);
|
|
38
|
+
});
|
|
39
|
+
return dims;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=dimension-order.js.map
|
|
@@ -3,9 +3,7 @@
|
|
|
3
3
|
// sibling services. See docs/ARCHITECTURE.md for the layering.
|
|
4
4
|
import { TM1Error, TM1ErrorCode } from "../../types.js";
|
|
5
5
|
import { freeCellset } from "./cellset-transform.js";
|
|
6
|
-
import { filterClause, nameFilterPredicates, pageClauses, readCount, } from "./odata-page.js";
|
|
7
|
-
// OData key encoder: double ' per OData literal rules, then percent-encode.
|
|
8
|
-
const enc = (s) => encodeURIComponent(String(s).replace(/'/g, "''"));
|
|
6
|
+
import { filterClause, nameFilterPredicates, pageClauses, readCount, odataKey, } from "./odata-page.js";
|
|
9
7
|
/**
|
|
10
8
|
* Decode a TM1 `}DimensionProperties.LAST_TIME_UPDATED` cell — a 14-digit
|
|
11
9
|
* `YYYYMMDDHHMMSS` stamp in server-local time — to a naive-local ISO string
|
|
@@ -112,7 +110,7 @@ export class DimensionService {
|
|
|
112
110
|
async create(name) {
|
|
113
111
|
await this.http.request("POST", "/api/v1/Dimensions", { Name: name });
|
|
114
112
|
try {
|
|
115
|
-
await this.http.request("POST", `/api/v1/Dimensions('${
|
|
113
|
+
await this.http.request("POST", `/api/v1/Dimensions('${odataKey(name)}')/Hierarchies`, { Name: name });
|
|
116
114
|
}
|
|
117
115
|
catch (err) {
|
|
118
116
|
if (err instanceof TM1Error && err.httpStatus === 409) {
|
|
@@ -126,7 +124,7 @@ export class DimensionService {
|
|
|
126
124
|
* DELETE /api/v1/Dimensions('{name}')
|
|
127
125
|
*/
|
|
128
126
|
async delete(name) {
|
|
129
|
-
await this.http.request("DELETE", `/api/v1/Dimensions('${
|
|
127
|
+
await this.http.request("DELETE", `/api/v1/Dimensions('${odataKey(name)}')`);
|
|
130
128
|
}
|
|
131
129
|
/**
|
|
132
130
|
* Read per-dimension last-modified timestamps from the `}DimensionProperties`
|
|
@@ -173,7 +171,7 @@ export class DimensionService {
|
|
|
173
171
|
*/
|
|
174
172
|
async resolveDefaultMember(dimensionName, hierarchyName) {
|
|
175
173
|
const hier = hierarchyName ?? dimensionName;
|
|
176
|
-
const base = `/api/v1/Dimensions('${
|
|
174
|
+
const base = `/api/v1/Dimensions('${odataKey(dimensionName)}')/Hierarchies('${odataKey(hier)}')`;
|
|
177
175
|
// Tier 1: explicit DefaultMember attribute.
|
|
178
176
|
try {
|
|
179
177
|
const dm = await this.http.request("GET", `${base}/DefaultMember?$select=Name,Level`);
|
|
@@ -66,7 +66,12 @@ export declare class ElementService {
|
|
|
66
66
|
* Update an existing element (rename, type change, replace components).
|
|
67
67
|
* PATCH /api/v1/Dimensions('{d}')/Hierarchies('{h}')/Elements('{name}')
|
|
68
68
|
*/
|
|
69
|
-
update(dimensionName: string, hierarchyName: string, elementName: string, update: ElementUpdate): Promise<
|
|
69
|
+
update(dimensionName: string, hierarchyName: string, elementName: string, update: ElementUpdate): Promise<{
|
|
70
|
+
typeChange: {
|
|
71
|
+
from: string;
|
|
72
|
+
to: string;
|
|
73
|
+
} | null;
|
|
74
|
+
}>;
|
|
70
75
|
/**
|
|
71
76
|
* Delete an element. May fail if the element is referenced in rules.
|
|
72
77
|
* DELETE /api/v1/Dimensions('{d}')/Hierarchies('{h}')/Elements('{name}')
|
|
@@ -141,10 +146,26 @@ export declare class ElementService {
|
|
|
141
146
|
truncated: boolean;
|
|
142
147
|
}>;
|
|
143
148
|
/**
|
|
144
|
-
*
|
|
145
|
-
*
|
|
149
|
+
* Type of one element, or null when the hierarchy has no such element.
|
|
150
|
+
* Keyed lookup, so TM1 applies its own case- and space-insensitive name
|
|
151
|
+
* matching and answers with the stored spelling.
|
|
152
|
+
* GET /api/v1/Dimensions('{d}')/Hierarchies('{h}')/Elements('{name}')?$select=Name,Type
|
|
153
|
+
*/
|
|
154
|
+
getType(dimensionName: string, hierarchyName: string, elementName: string): Promise<{
|
|
155
|
+
name: string;
|
|
156
|
+
type: string;
|
|
157
|
+
} | null>;
|
|
158
|
+
/**
|
|
159
|
+
* Which of `names` are consolidated (Type 3) in this hierarchy.
|
|
160
|
+
*
|
|
161
|
+
* One filtered request per chunk of names rather than a GET per element:
|
|
162
|
+
* the server evaluates `Type eq 3 and (Name eq ...)` and answers with the
|
|
163
|
+
* hits only, so the response is bounded by the consolidations among the
|
|
164
|
+
* names asked about, never by the size of the hierarchy.
|
|
165
|
+
*
|
|
166
|
+
* GET /api/v1/Dimensions('{d}')/Hierarchies('{h}')/Elements?$select=Name&$filter=...
|
|
146
167
|
*/
|
|
147
|
-
|
|
168
|
+
consolidatedAmong(dimensionName: string, hierarchyName: string, names: string[]): Promise<string[]>;
|
|
148
169
|
/**
|
|
149
170
|
* Bulk upsert elements into a hierarchy. Two-pass to ensure leaves exist
|
|
150
171
|
* before consolidations reference them: pass 1 creates/upserts every element
|
|
@@ -197,7 +218,7 @@ export declare class ElementService {
|
|
|
197
218
|
* `}ElementAttributes_{Dim}`. Routes through CellService.executeMdx for the
|
|
198
219
|
* cellset round-trip.
|
|
199
220
|
*/
|
|
200
|
-
getAttributeValues(dimensionName: string, elementName: string): Promise<ElementAttributeValue[]>;
|
|
221
|
+
getAttributeValues(dimensionName: string, elementName: string, hierarchyName?: string): Promise<ElementAttributeValue[]>;
|
|
201
222
|
/**
|
|
202
223
|
* Attribute values for a window of elements — every element of the
|
|
203
224
|
* dimension's default hierarchy, name-sorted, `limit` at a time. One MDX
|
|
@@ -222,16 +243,19 @@ export declare class ElementService {
|
|
|
222
243
|
/**
|
|
223
244
|
* Set a single attribute value. See {@link updateAttributeValues}.
|
|
224
245
|
*/
|
|
225
|
-
updateAttributeValue(dimensionName: string, elementName: string, attributeName: string, value: number | string): Promise<void>;
|
|
246
|
+
updateAttributeValue(dimensionName: string, elementName: string, attributeName: string, value: number | string, hierarchyName?: string): Promise<void>;
|
|
226
247
|
/**
|
|
227
248
|
* Set attribute values by writing to the `}ElementAttributes_{Dim}` control
|
|
228
249
|
* cube — every update in ONE cellset write (CellService.writeCells).
|
|
229
250
|
*
|
|
230
|
-
*
|
|
251
|
+
* The attributes are looked up once. TM1 matches attribute names ignoring
|
|
252
|
+
* case and spaces, so the lookup compares the same way; an unknown attribute
|
|
253
|
+
* is NOT_FOUND, raised before anything is written — without the check TM1
|
|
254
|
+
* answers with a raw MDX syntax error (no attribute at all means no
|
|
255
|
+
* attribute cube). Values are coerced to the attribute's type: a Numeric
|
|
231
256
|
* attribute takes a number or a numeric string (`"12.5"`), String and Alias
|
|
232
|
-
* attributes take the value as text
|
|
233
|
-
*
|
|
234
|
-
* before anything is written.
|
|
257
|
+
* attributes take the value as text; a non-numeric value for a Numeric
|
|
258
|
+
* attribute is a VALIDATION_ERROR that names it.
|
|
235
259
|
*
|
|
236
260
|
* Prefer TI processes (CellPutS / AttrPutS) for reproducible deployments;
|
|
237
261
|
* this REST-direct path is for ad-hoc / debugging use.
|
|
@@ -240,6 +264,6 @@ export declare class ElementService {
|
|
|
240
264
|
elementName: string;
|
|
241
265
|
attributeName: string;
|
|
242
266
|
value: number | string;
|
|
243
|
-
}
|
|
267
|
+
}>, hierarchyName?: string): Promise<void>;
|
|
244
268
|
}
|
|
245
269
|
//# sourceMappingURL=element-service.d.ts.map
|