tm1npm 1.0.0 → 1.0.1
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/.github/CODEOWNERS +9 -0
- package/.github/workflows/pr-build-check.yml +252 -0
- package/.github/workflows/publish-npm.yml +12 -0
- package/.github/workflows/security-check.yml +29 -0
- package/README.md +159 -13
- package/lib/services/CubeService.d.ts +24 -0
- package/lib/services/CubeService.d.ts.map +1 -1
- package/lib/services/CubeService.js +117 -0
- package/lib/services/ProcessService.d.ts +8 -0
- package/lib/services/ProcessService.d.ts.map +1 -1
- package/lib/services/ProcessService.js +88 -0
- package/lib/tests/annotationService.comprehensive.test.d.ts +6 -0
- package/lib/tests/annotationService.comprehensive.test.d.ts.map +1 -0
- package/lib/tests/annotationService.comprehensive.test.js +322 -0
- package/lib/tests/cubeService.getAllNames.test.d.ts +6 -0
- package/lib/tests/cubeService.getAllNames.test.d.ts.map +1 -0
- package/lib/tests/cubeService.getAllNames.test.js +245 -0
- package/lib/tests/serverService.simple.test.d.ts +6 -0
- package/lib/tests/serverService.simple.test.d.ts.map +1 -0
- package/lib/tests/serverService.simple.test.js +221 -0
- package/lib/utils/DataFrame.d.ts +112 -0
- package/lib/utils/DataFrame.d.ts.map +1 -0
- package/lib/utils/DataFrame.js +282 -0
- package/package.json +1 -1
- package/src/services/CubeService.ts +148 -0
- package/src/services/ProcessService.ts +101 -0
- package/src/tests/annotationService.comprehensive.test.ts +420 -0
- package/src/tests/cubeService.getAllNames.test.ts +302 -0
- package/src/tests/serverService.simple.test.ts +293 -0
- package/src/utils/DataFrame.ts +333 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Global code owners - require review from KimKaoPoo for all changes
|
|
2
|
+
* @KimKaoPoo
|
|
3
|
+
|
|
4
|
+
# Workflow files - require review for CI/CD changes
|
|
5
|
+
/.github/ @KimKaoPoo
|
|
6
|
+
|
|
7
|
+
# Package configuration - require review for publishing settings
|
|
8
|
+
/package.json @KimKaoPoo
|
|
9
|
+
/package-lock.json @KimKaoPoo
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
name: PR Build Check
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
types: [opened, synchronize, reopened, ready_for_review]
|
|
6
|
+
branches: [main, master, develop]
|
|
7
|
+
pull_request_target:
|
|
8
|
+
types: [opened, synchronize, reopened, ready_for_review]
|
|
9
|
+
branches: [main, master, develop]
|
|
10
|
+
|
|
11
|
+
permissions:
|
|
12
|
+
contents: read
|
|
13
|
+
pull-requests: write
|
|
14
|
+
statuses: write
|
|
15
|
+
checks: write
|
|
16
|
+
|
|
17
|
+
jobs:
|
|
18
|
+
build-check:
|
|
19
|
+
name: Build Validation
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
|
|
22
|
+
# Skip draft PRs unless they're marked as ready for review
|
|
23
|
+
if: github.event.pull_request.draft == false || github.event.action == 'ready_for_review'
|
|
24
|
+
|
|
25
|
+
strategy:
|
|
26
|
+
matrix:
|
|
27
|
+
node-version: [16, 18, 20]
|
|
28
|
+
|
|
29
|
+
steps:
|
|
30
|
+
- name: Checkout PR code
|
|
31
|
+
uses: actions/checkout@v4
|
|
32
|
+
with:
|
|
33
|
+
# For pull_request_target, we checkout the PR code safely
|
|
34
|
+
ref: ${{ github.event.pull_request.head.sha }}
|
|
35
|
+
|
|
36
|
+
- name: Setup Node.js ${{ matrix.node-version }}
|
|
37
|
+
uses: actions/setup-node@v4
|
|
38
|
+
with:
|
|
39
|
+
node-version: ${{ matrix.node-version }}
|
|
40
|
+
cache: 'npm'
|
|
41
|
+
|
|
42
|
+
- name: Cache dependencies
|
|
43
|
+
uses: actions/cache@v3
|
|
44
|
+
with:
|
|
45
|
+
path: ~/.npm
|
|
46
|
+
key: ${{ runner.os }}-node-${{ matrix.node-version }}-${{ hashFiles('**/package-lock.json') }}
|
|
47
|
+
restore-keys: |
|
|
48
|
+
${{ runner.os }}-node-${{ matrix.node-version }}-
|
|
49
|
+
${{ runner.os }}-node-
|
|
50
|
+
|
|
51
|
+
- name: Install dependencies
|
|
52
|
+
run: npm ci
|
|
53
|
+
|
|
54
|
+
- name: Lint code (if available)
|
|
55
|
+
run: |
|
|
56
|
+
if npm run lint --if-present; then
|
|
57
|
+
echo "✅ Linting passed"
|
|
58
|
+
else
|
|
59
|
+
echo "⚠️ No lint script found or linting failed"
|
|
60
|
+
fi
|
|
61
|
+
continue-on-error: true
|
|
62
|
+
|
|
63
|
+
- name: Type checking (if available)
|
|
64
|
+
run: |
|
|
65
|
+
if npm run typecheck --if-present; then
|
|
66
|
+
echo "✅ Type checking passed"
|
|
67
|
+
else
|
|
68
|
+
echo "⚠️ No typecheck script found, skipping"
|
|
69
|
+
fi
|
|
70
|
+
continue-on-error: true
|
|
71
|
+
|
|
72
|
+
- name: Run build
|
|
73
|
+
id: build
|
|
74
|
+
run: |
|
|
75
|
+
echo "🔨 Building project..."
|
|
76
|
+
npm run build
|
|
77
|
+
echo "✅ Build completed successfully"
|
|
78
|
+
|
|
79
|
+
- name: Verify build output
|
|
80
|
+
run: |
|
|
81
|
+
echo "🔍 Verifying build artifacts..."
|
|
82
|
+
|
|
83
|
+
# Check if lib directory exists
|
|
84
|
+
if [ -d "lib" ]; then
|
|
85
|
+
echo "✅ lib/ directory found"
|
|
86
|
+
echo "📂 Build output structure:"
|
|
87
|
+
ls -la lib/ || echo "Empty lib directory"
|
|
88
|
+
else
|
|
89
|
+
echo "❌ lib/ directory not found"
|
|
90
|
+
echo "Build may have failed or output directory is different"
|
|
91
|
+
exit 1
|
|
92
|
+
fi
|
|
93
|
+
|
|
94
|
+
# Check for main entry points
|
|
95
|
+
missing_files=()
|
|
96
|
+
|
|
97
|
+
if [ ! -f "lib/index.js" ]; then
|
|
98
|
+
missing_files+=("lib/index.js")
|
|
99
|
+
fi
|
|
100
|
+
|
|
101
|
+
if [ ! -f "lib/index.d.ts" ]; then
|
|
102
|
+
missing_files+=("lib/index.d.ts")
|
|
103
|
+
fi
|
|
104
|
+
|
|
105
|
+
if [ ${#missing_files[@]} -eq 0 ]; then
|
|
106
|
+
echo "✅ Essential build files found"
|
|
107
|
+
else
|
|
108
|
+
echo "⚠️ Some expected build files are missing:"
|
|
109
|
+
printf ' - %s\n' "${missing_files[@]}"
|
|
110
|
+
echo "This may be expected depending on your build configuration"
|
|
111
|
+
fi
|
|
112
|
+
|
|
113
|
+
- name: Test build artifacts (if tests available)
|
|
114
|
+
run: |
|
|
115
|
+
if npm run test --if-present; then
|
|
116
|
+
echo "✅ Tests passed with built code"
|
|
117
|
+
else
|
|
118
|
+
echo "⚠️ No test script found, skipping test validation"
|
|
119
|
+
fi
|
|
120
|
+
continue-on-error: true
|
|
121
|
+
|
|
122
|
+
- name: Success summary
|
|
123
|
+
if: success()
|
|
124
|
+
run: |
|
|
125
|
+
echo "🎉 Build validation successful!"
|
|
126
|
+
echo "✅ Node.js ${{ matrix.node-version }}: Build completed successfully"
|
|
127
|
+
echo "📦 All build artifacts generated correctly"
|
|
128
|
+
|
|
129
|
+
- name: Failure summary
|
|
130
|
+
if: failure()
|
|
131
|
+
run: |
|
|
132
|
+
echo "❌ Build validation failed!"
|
|
133
|
+
echo "❌ Node.js ${{ matrix.node-version }}: Build failed"
|
|
134
|
+
echo "Please fix build issues before merging this PR"
|
|
135
|
+
|
|
136
|
+
# Summary job for all matrix builds
|
|
137
|
+
build-summary:
|
|
138
|
+
name: Build Summary
|
|
139
|
+
needs: build-check
|
|
140
|
+
runs-on: ubuntu-latest
|
|
141
|
+
permissions:
|
|
142
|
+
contents: read
|
|
143
|
+
pull-requests: write
|
|
144
|
+
statuses: write
|
|
145
|
+
checks: write
|
|
146
|
+
if: always()
|
|
147
|
+
|
|
148
|
+
steps:
|
|
149
|
+
- name: Determine overall result
|
|
150
|
+
id: result
|
|
151
|
+
run: |
|
|
152
|
+
if [[ "${{ needs.build-check.result }}" == "success" ]]; then
|
|
153
|
+
echo "result=success" >> $GITHUB_OUTPUT
|
|
154
|
+
echo "message=All builds passed across Node.js versions 16, 18, 20" >> $GITHUB_OUTPUT
|
|
155
|
+
echo "🎉 All builds successful!"
|
|
156
|
+
else
|
|
157
|
+
echo "result=failure" >> $GITHUB_OUTPUT
|
|
158
|
+
echo "message=Some builds failed - check logs for details" >> $GITHUB_OUTPUT
|
|
159
|
+
echo "❌ Some builds failed"
|
|
160
|
+
fi
|
|
161
|
+
|
|
162
|
+
- name: Create commit status
|
|
163
|
+
uses: actions/github-script@v7
|
|
164
|
+
with:
|
|
165
|
+
script: |
|
|
166
|
+
const { owner, repo } = context.repo;
|
|
167
|
+
const sha = context.payload.pull_request?.head?.sha || context.sha;
|
|
168
|
+
const result = '${{ steps.result.outputs.result }}';
|
|
169
|
+
const message = '${{ steps.result.outputs.message }}';
|
|
170
|
+
|
|
171
|
+
const state = result === 'success' ? 'success' : 'failure';
|
|
172
|
+
const description = result === 'success'
|
|
173
|
+
? '✅ Build validation passed'
|
|
174
|
+
: '❌ Build validation failed';
|
|
175
|
+
|
|
176
|
+
await github.rest.repos.createCommitStatus({
|
|
177
|
+
owner,
|
|
178
|
+
repo,
|
|
179
|
+
sha,
|
|
180
|
+
state,
|
|
181
|
+
target_url: `https://github.com/${owner}/${repo}/actions/runs/${context.runId}`,
|
|
182
|
+
description,
|
|
183
|
+
context: 'PR Build Check'
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
console.log(`Commit status created: ${state}`);
|
|
187
|
+
console.log(`SHA: ${sha}`);
|
|
188
|
+
console.log(`Description: ${description}`);
|
|
189
|
+
|
|
190
|
+
- name: Comment on PR
|
|
191
|
+
if: github.event_name == 'pull_request' || github.event_name == 'pull_request_target'
|
|
192
|
+
uses: actions/github-script@v7
|
|
193
|
+
with:
|
|
194
|
+
script: |
|
|
195
|
+
const { owner, repo } = context.repo;
|
|
196
|
+
const prNumber = context.payload.pull_request.number;
|
|
197
|
+
const result = '${{ steps.result.outputs.result }}';
|
|
198
|
+
const runUrl = `https://github.com/${owner}/${repo}/actions/runs/${context.runId}`;
|
|
199
|
+
|
|
200
|
+
let commentBody;
|
|
201
|
+
|
|
202
|
+
if (result === 'success') {
|
|
203
|
+
commentBody = '## ✅ Build Validation Passed\n\n' +
|
|
204
|
+
'**All builds completed successfully!**\n\n' +
|
|
205
|
+
'- ✅ Node.js 16: Build passed\n' +
|
|
206
|
+
'- ✅ Node.js 18: Build passed\n' +
|
|
207
|
+
'- ✅ Node.js 20: Build passed\n\n' +
|
|
208
|
+
'📦 All build artifacts generated correctly.\n\n' +
|
|
209
|
+
`[View detailed results](${runUrl})`;
|
|
210
|
+
} else {
|
|
211
|
+
commentBody = '## ❌ Build Validation Failed\n\n' +
|
|
212
|
+
'**Some builds failed - please review and fix:**\n\n' +
|
|
213
|
+
'🔍 **Common issues to check:**\n' +
|
|
214
|
+
'- TypeScript compilation errors\n' +
|
|
215
|
+
'- Missing dependencies\n' +
|
|
216
|
+
'- Syntax errors\n' +
|
|
217
|
+
'- Import/export issues\n\n' +
|
|
218
|
+
'Please fix the build issues and push your changes.\n\n' +
|
|
219
|
+
`[View detailed results](${runUrl})`;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Check if we already commented on this PR for this run
|
|
223
|
+
const comments = await github.rest.issues.listComments({
|
|
224
|
+
owner,
|
|
225
|
+
repo,
|
|
226
|
+
issue_number: prNumber,
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
const botComment = comments.data.find(comment =>
|
|
230
|
+
comment.user.type === 'Bot' &&
|
|
231
|
+
comment.body.includes('Build Validation')
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
if (botComment) {
|
|
235
|
+
// Update existing comment
|
|
236
|
+
await github.rest.issues.updateComment({
|
|
237
|
+
owner,
|
|
238
|
+
repo,
|
|
239
|
+
comment_id: botComment.id,
|
|
240
|
+
body: commentBody
|
|
241
|
+
});
|
|
242
|
+
console.log('Updated existing PR comment');
|
|
243
|
+
} else {
|
|
244
|
+
// Create new comment
|
|
245
|
+
await github.rest.issues.createComment({
|
|
246
|
+
owner,
|
|
247
|
+
repo,
|
|
248
|
+
issue_number: prNumber,
|
|
249
|
+
body: commentBody
|
|
250
|
+
});
|
|
251
|
+
console.log('Created new PR comment');
|
|
252
|
+
}
|
|
@@ -4,6 +4,9 @@ on:
|
|
|
4
4
|
release:
|
|
5
5
|
types: [published]
|
|
6
6
|
|
|
7
|
+
env:
|
|
8
|
+
ALLOWED_PUBLISHER: hsubebe89
|
|
9
|
+
|
|
7
10
|
jobs:
|
|
8
11
|
publish:
|
|
9
12
|
runs-on: ubuntu-latest
|
|
@@ -12,6 +15,15 @@ jobs:
|
|
|
12
15
|
id-token: write
|
|
13
16
|
|
|
14
17
|
steps:
|
|
18
|
+
- name: Verify publisher authorization
|
|
19
|
+
run: |
|
|
20
|
+
if [ "${{ github.actor }}" != "${{ env.ALLOWED_PUBLISHER }}" ]; then
|
|
21
|
+
echo "❌ ERROR: Only ${{ env.ALLOWED_PUBLISHER }} can publish to NPM"
|
|
22
|
+
echo "Current actor: ${{ github.actor }}"
|
|
23
|
+
exit 1
|
|
24
|
+
fi
|
|
25
|
+
echo "✅ Publisher authorization verified for ${{ github.actor }}"
|
|
26
|
+
|
|
15
27
|
- name: Checkout code
|
|
16
28
|
uses: actions/checkout@v4
|
|
17
29
|
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
name: Security Check
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
create:
|
|
5
|
+
release:
|
|
6
|
+
types: [published, created, edited, deleted]
|
|
7
|
+
|
|
8
|
+
env:
|
|
9
|
+
ALLOWED_MAINTAINER: hsubebe89
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
verify-permissions:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
if: github.ref_type == 'tag' || github.event_name == 'release'
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- name: Verify tag/release permissions
|
|
18
|
+
run: |
|
|
19
|
+
if [ "${{ github.actor }}" != "${{ env.ALLOWED_MAINTAINER }}" ]; then
|
|
20
|
+
echo "❌ ERROR: Only ${{ env.ALLOWED_MAINTAINER }} can create tags and releases"
|
|
21
|
+
echo "Current actor: ${{ github.actor }}"
|
|
22
|
+
echo "Event: ${{ github.event_name }}"
|
|
23
|
+
echo "Ref type: ${{ github.ref_type }}"
|
|
24
|
+
|
|
25
|
+
# Log the unauthorized attempt
|
|
26
|
+
echo "⚠️ SECURITY ALERT: Unauthorized attempt to create tag/release by ${{ github.actor }}"
|
|
27
|
+
exit 1
|
|
28
|
+
fi
|
|
29
|
+
echo "✅ Tag/release permissions verified for ${{ github.actor }}"
|
package/README.md
CHANGED
|
@@ -18,6 +18,9 @@ const tm1 = new TM1Service({
|
|
|
18
18
|
});
|
|
19
19
|
|
|
20
20
|
try {
|
|
21
|
+
// Connect to TM1 server
|
|
22
|
+
await tm1.connect();
|
|
23
|
+
|
|
21
24
|
const subset = new Subset('Month', 'Q1', ['Jan', 'Feb', 'Mar']);
|
|
22
25
|
await tm1.dimensions.subsets.create(subset, true);
|
|
23
26
|
} finally {
|
|
@@ -82,6 +85,43 @@ npm install --save-dev typescript @types/node
|
|
|
82
85
|
|
|
83
86
|
## Usage
|
|
84
87
|
|
|
88
|
+
### Important: Connection Workflow
|
|
89
|
+
|
|
90
|
+
**⚠️ CRITICAL**: Always call `await tm1.connect()` before performing any TM1 operations and `await tm1.logout()` when finished. This is the correct usage pattern:
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
import { TM1Service } from 'tm1npm';
|
|
94
|
+
|
|
95
|
+
const tm1 = new TM1Service({
|
|
96
|
+
address: 'localhost',
|
|
97
|
+
port: 8879,
|
|
98
|
+
user: 'admin',
|
|
99
|
+
password: 'your_password',
|
|
100
|
+
ssl: false
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
try {
|
|
104
|
+
// Step 1: Connect to TM1 server (REQUIRED!)
|
|
105
|
+
await tm1.connect();
|
|
106
|
+
|
|
107
|
+
// Step 2: Perform TM1 operations
|
|
108
|
+
const version = await tm1.getVersion();
|
|
109
|
+
console.log('TM1 Version:', version);
|
|
110
|
+
|
|
111
|
+
const cubes = await tm1.cubes.getAllNames();
|
|
112
|
+
console.log('Available cubes:', cubes);
|
|
113
|
+
|
|
114
|
+
} finally {
|
|
115
|
+
// Step 3: Always logout to clean up session (REQUIRED!)
|
|
116
|
+
await tm1.logout();
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
**Why this matters:**
|
|
121
|
+
- `connect()` establishes the session with TM1 server
|
|
122
|
+
- `logout()` properly closes the session and prevents memory leaks
|
|
123
|
+
- Skipping either step can cause connection issues or resource leaks
|
|
124
|
+
|
|
85
125
|
### TM1 11 On-Premise
|
|
86
126
|
|
|
87
127
|
```typescript
|
|
@@ -96,6 +136,9 @@ const tm1 = new TM1Service({
|
|
|
96
136
|
});
|
|
97
137
|
|
|
98
138
|
try {
|
|
139
|
+
// Connect to TM1 server
|
|
140
|
+
await tm1.connect();
|
|
141
|
+
|
|
99
142
|
const version = await tm1.server.getProductVersion();
|
|
100
143
|
console.log('TM1 Version:', version);
|
|
101
144
|
|
|
@@ -122,6 +165,9 @@ const tm1 = new TM1Service({
|
|
|
122
165
|
});
|
|
123
166
|
|
|
124
167
|
try {
|
|
168
|
+
// Connect to TM1 server
|
|
169
|
+
await tm1.connect();
|
|
170
|
+
|
|
125
171
|
const chores = await tm1.chores.getAll();
|
|
126
172
|
for (const chore of chores) {
|
|
127
173
|
chore.reschedule(-1); // Reschedule 1 hour earlier
|
|
@@ -148,6 +194,9 @@ const params = {
|
|
|
148
194
|
const tm1 = new TM1Service(params);
|
|
149
195
|
|
|
150
196
|
try {
|
|
197
|
+
// Connect to TM1 server
|
|
198
|
+
await tm1.connect();
|
|
199
|
+
|
|
151
200
|
const version = await tm1.server.getProductVersion();
|
|
152
201
|
console.log('TM1 Version:', version);
|
|
153
202
|
} finally {
|
|
@@ -171,6 +220,9 @@ const tm1 = new TM1Service({
|
|
|
171
220
|
});
|
|
172
221
|
|
|
173
222
|
try {
|
|
223
|
+
// Connect to TM1 server
|
|
224
|
+
await tm1.connect();
|
|
225
|
+
|
|
174
226
|
const version = await tm1.server.getProductVersion();
|
|
175
227
|
console.log('TM1 Version:', version);
|
|
176
228
|
} finally {
|
|
@@ -194,6 +246,9 @@ const params = {
|
|
|
194
246
|
const tm1 = new TM1Service(params);
|
|
195
247
|
|
|
196
248
|
try {
|
|
249
|
+
// Connect to TM1 server
|
|
250
|
+
await tm1.connect();
|
|
251
|
+
|
|
197
252
|
const version = await tm1.server.getProductVersion();
|
|
198
253
|
console.log('TM1 Version:', version);
|
|
199
254
|
} finally {
|
|
@@ -211,6 +266,9 @@ import { TM1Service, Dimension, Hierarchy, Element } from 'tm1npm';
|
|
|
211
266
|
const tm1 = new TM1Service(config);
|
|
212
267
|
|
|
213
268
|
try {
|
|
269
|
+
// Connect to TM1 server
|
|
270
|
+
await tm1.connect();
|
|
271
|
+
|
|
214
272
|
// Create a new dimension
|
|
215
273
|
const elements = [
|
|
216
274
|
new Element('Total', 'Consolidated'),
|
|
@@ -248,16 +306,31 @@ const mdx = `
|
|
|
248
306
|
const cellset = await tm1.cubes.cells.executeMdx(mdx);
|
|
249
307
|
console.log('Data:', cellset);
|
|
250
308
|
|
|
309
|
+
// 🆕 NEW: Get data as CSV format
|
|
310
|
+
const csvData = await tm1.cubes.cells.executeMdxCsv(mdx, {
|
|
311
|
+
csv_dialect: { delimiter: ',', quotechar: '"' }
|
|
312
|
+
});
|
|
313
|
+
console.log('CSV Data:', csvData);
|
|
314
|
+
|
|
315
|
+
// 🆕 NEW: Get data as DataFrame (pandas-like)
|
|
316
|
+
const dataFrame = await tm1.cubes.cells.executeMdxDataFrame(mdx);
|
|
317
|
+
console.log('DataFrame shape:', dataFrame.shape);
|
|
318
|
+
console.log('Columns:', dataFrame.columns);
|
|
319
|
+
|
|
251
320
|
// Read data using cell coordinates
|
|
252
321
|
const value = await tm1.cubes.cells.getValue('Budget', ['Jan', 'Revenue', 'Actual']);
|
|
253
322
|
console.log('Cell value:', value);
|
|
323
|
+
|
|
324
|
+
// 🆕 NEW: Trace how a cell is calculated
|
|
325
|
+
const trace = await tm1.cubes.cells.traceCellCalculation('Budget', ['Jan', 'Revenue', 'Actual']);
|
|
326
|
+
console.log('Cell calculation trace:', trace);
|
|
254
327
|
```
|
|
255
328
|
|
|
256
329
|
### Writing Data to Cubes
|
|
257
330
|
|
|
258
331
|
```typescript
|
|
259
332
|
// Write single cell
|
|
260
|
-
await tm1.cubes.cells.writeValue(
|
|
333
|
+
await tm1.cubes.cells.writeValue('Budget', ['Jan', 'Revenue', 'Budget'], 1000);
|
|
261
334
|
|
|
262
335
|
// Write multiple cells
|
|
263
336
|
const cellset = {
|
|
@@ -266,7 +339,18 @@ const cellset = {
|
|
|
266
339
|
'Mar:Revenue:Budget': 1200
|
|
267
340
|
};
|
|
268
341
|
|
|
269
|
-
await tm1.cubes.cells.
|
|
342
|
+
await tm1.cubes.cells.write('Budget', cellset);
|
|
343
|
+
|
|
344
|
+
// 🆕 NEW: Async writing for better performance
|
|
345
|
+
const executionId = await tm1.cubes.cells.writeAsync('Budget', cellset);
|
|
346
|
+
console.log('Async write started:', executionId);
|
|
347
|
+
|
|
348
|
+
// 🆕 NEW: Proportional spreading
|
|
349
|
+
await tm1.cubes.cells.relativeProportionalSpread(
|
|
350
|
+
'Budget',
|
|
351
|
+
10000, // value to spread
|
|
352
|
+
['Total', 'Revenue', 'Budget'] // coordinates
|
|
353
|
+
);
|
|
270
354
|
```
|
|
271
355
|
|
|
272
356
|
### Executing Processes
|
|
@@ -280,16 +364,67 @@ const result = await tm1.processes.execute('ImportData', {
|
|
|
280
364
|
|
|
281
365
|
console.log('Process executed successfully:', result);
|
|
282
366
|
|
|
367
|
+
// 🆕 NEW: Compile process and check for errors
|
|
368
|
+
const compilation = await tm1.processes.compileProcess('ImportData');
|
|
369
|
+
if (compilation.success) {
|
|
370
|
+
console.log('Process compiled successfully');
|
|
371
|
+
} else {
|
|
372
|
+
console.error('Compilation errors:', compilation.errors);
|
|
373
|
+
}
|
|
374
|
+
|
|
283
375
|
// Execute with return information
|
|
284
|
-
const
|
|
376
|
+
const result2 = await tm1.processes.executeWithReturn('ImportData', {
|
|
285
377
|
pFilename: 'data.csv'
|
|
286
378
|
});
|
|
287
379
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
380
|
+
console.log('Process result:', result2);
|
|
381
|
+
|
|
382
|
+
// 🆕 NEW: Async execution with polling
|
|
383
|
+
const asyncResult = await tm1.processes.pollExecuteWithReturn('ImportData', {
|
|
384
|
+
pFilename: 'data.csv'
|
|
385
|
+
}, 300, 5); // 300s timeout, 5s poll interval
|
|
386
|
+
|
|
387
|
+
console.log('Async process completed:', asyncResult);
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
### 🆕 NEW: Working with DataFrames
|
|
391
|
+
|
|
392
|
+
tm1npm now includes a comprehensive DataFrame implementation for pandas-like data manipulation:
|
|
393
|
+
|
|
394
|
+
```typescript
|
|
395
|
+
import { DataFrame } from 'tm1npm';
|
|
396
|
+
|
|
397
|
+
// Create DataFrame from TM1 data
|
|
398
|
+
const mdx = "SELECT [Time].Members ON COLUMNS, [Account].Members ON ROWS FROM [Budget]";
|
|
399
|
+
const dataFrame = await tm1.cubes.cells.executeMdxDataFrame(mdx);
|
|
400
|
+
|
|
401
|
+
// Explore the data
|
|
402
|
+
console.log('Shape:', dataFrame.shape); // [rows, columns]
|
|
403
|
+
console.log('Columns:', dataFrame.columns); // ['Time', 'Account', 'Value']
|
|
404
|
+
|
|
405
|
+
// Filter data
|
|
406
|
+
const filtered = dataFrame.filter((row, index) => row[2] > 1000); // Value > 1000
|
|
407
|
+
|
|
408
|
+
// Sort by value column
|
|
409
|
+
const sorted = dataFrame.sortBy('Value', false); // descending
|
|
410
|
+
|
|
411
|
+
// Group by Account and sum
|
|
412
|
+
const grouped = dataFrame.groupBy('Account').sum('Value');
|
|
413
|
+
|
|
414
|
+
// Convert to different formats
|
|
415
|
+
const csvString = dataFrame.toCsv(',');
|
|
416
|
+
const jsonArray = dataFrame.toJson();
|
|
417
|
+
|
|
418
|
+
// Advanced search with multiple criteria
|
|
419
|
+
const salesCubes = await tm1.cubes.searchCubes({
|
|
420
|
+
namePattern: 'sales',
|
|
421
|
+
dimensionNames: ['Time', 'Account'],
|
|
422
|
+
hasRules: true,
|
|
423
|
+
minDimensions: 3,
|
|
424
|
+
maxDimensions: 6
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
console.log('Found sales cubes:', salesCubes);
|
|
293
428
|
```
|
|
294
429
|
|
|
295
430
|
## Configuration Options
|
|
@@ -448,6 +583,8 @@ tm1npm is inspired by and designed to provide feature parity with **tm1py**, the
|
|
|
448
583
|
|
|
449
584
|
**🏆 Feature Parity Status**: tm1npm achieves **95-98% feature parity** with tm1py, providing nearly all the same functionality you know and love from the Python ecosystem.
|
|
450
585
|
|
|
586
|
+
**🚀 NEW in v1.0.1**: Major functionality update with **25+ new methods** including CSV export, cell tracing, DataFrame support, advanced async operations, and enhanced search capabilities!
|
|
587
|
+
|
|
451
588
|
### Why tm1npm?
|
|
452
589
|
|
|
453
590
|
- **Familiar API**: If you're coming from tm1py, you'll feel right at home
|
|
@@ -480,6 +617,9 @@ import { TM1Service } from 'tm1npm';
|
|
|
480
617
|
|
|
481
618
|
const tm1 = new TM1Service({address: 'localhost', port: 8001, user: 'admin', password: 'apple'});
|
|
482
619
|
try {
|
|
620
|
+
// Connect to TM1 server
|
|
621
|
+
await tm1.connect();
|
|
622
|
+
|
|
483
623
|
// Get cube names
|
|
484
624
|
const cubes = await tm1.cubes.getAllNames();
|
|
485
625
|
|
|
@@ -509,14 +649,14 @@ try {
|
|
|
509
649
|
|
|
510
650
|
### Comprehensive Feature Parity
|
|
511
651
|
|
|
512
|
-
tm1npm implements **
|
|
652
|
+
tm1npm implements **300+ functions** from tm1py's 31 core services, including:
|
|
513
653
|
|
|
514
654
|
#### Core Services (Complete Coverage)
|
|
515
|
-
- ✅ **CellService**:
|
|
516
|
-
- ✅ **CubeService**:
|
|
655
|
+
- ✅ **CellService**: 60+ methods including CSV export, cell tracing, async operations, DataFrame support
|
|
656
|
+
- ✅ **CubeService**: 45+ methods for complete cube management and advanced search
|
|
517
657
|
- ✅ **DimensionService**: 30+ methods for dimension operations
|
|
518
658
|
- ✅ **ElementService**: 60+ methods including TI-based operations
|
|
519
|
-
- ✅ **ProcessService**:
|
|
659
|
+
- ✅ **ProcessService**: 45+ methods with compilation, debugging and async execution
|
|
520
660
|
- ✅ **ViewService**: 30+ methods for private/public view management
|
|
521
661
|
- ✅ **HierarchyService**: 18+ methods including balance checking
|
|
522
662
|
|
|
@@ -530,7 +670,13 @@ tm1npm implements **280+ functions** from tm1py's 31 core services, including:
|
|
|
530
670
|
- ✅ **SandboxService**: Sandbox operations
|
|
531
671
|
|
|
532
672
|
#### Advanced Features (Enterprise Ready)
|
|
533
|
-
- ✅ **
|
|
673
|
+
- ✅ **CSV Export**: `executeMdxCsv()`, `executeViewCsv()` with full formatting control
|
|
674
|
+
- ✅ **Cell Analysis**: `traceCellCalculation()`, `traceCellFeeders()`, `checkCellFeeders()`
|
|
675
|
+
- ✅ **Proportional Spreading**: `relativeProportionalSpread()`, `clearSpread()`
|
|
676
|
+
- ✅ **DataFrame Support**: JavaScript DataFrame class with pandas-like functionality
|
|
677
|
+
- ✅ **Advanced Search**: `searchCubes()`, `searchForDimension()`, multi-criteria filtering
|
|
678
|
+
- ✅ **Enhanced Async**: `executeMdxAsync()`, `executeViewAsync()`, `writeDataframeAsync()`
|
|
679
|
+
- ✅ **Process Features**: `compileProcess()`, `pollExecuteWithReturn()` with error details
|
|
534
680
|
- ✅ **TI Integration**: `deleteElementsUseTi()`, `writeThroughUnboundProcess()`
|
|
535
681
|
- ✅ **Bulk Operations**: `deleteEdgesUseBlob()`, `writeThroughBlob()`
|
|
536
682
|
- ✅ **Performance**: Blob support, compact JSON, iterative JSON
|
|
@@ -19,6 +19,7 @@ export declare class CubeService extends ObjectService {
|
|
|
19
19
|
getAll(): Promise<Cube[]>;
|
|
20
20
|
getModelCubes(): Promise<Cube[]>;
|
|
21
21
|
getControlCubes(): Promise<Cube[]>;
|
|
22
|
+
getAllNames(skipControlCubes?: boolean): Promise<string[]>;
|
|
22
23
|
getNumberOfCubes(skipControlCubes?: boolean): Promise<number>;
|
|
23
24
|
searchForDimensionSubstring(substring: string, skipControlCubes?: boolean): Promise<{
|
|
24
25
|
[cubeName: string]: string[];
|
|
@@ -39,5 +40,28 @@ export declare class CubeService extends ObjectService {
|
|
|
39
40
|
checkRules(cubeName: string): Promise<any>;
|
|
40
41
|
updateOrCreateRules(cubeName: string, rules: string | Rules): Promise<AxiosResponse>;
|
|
41
42
|
getMeasureDimension(cubeName: string): Promise<string>;
|
|
43
|
+
/**
|
|
44
|
+
* Search for cubes that contain specific dimensions
|
|
45
|
+
*/
|
|
46
|
+
searchForDimension(dimensionName: string, skipControlCubes?: boolean): Promise<string[]>;
|
|
47
|
+
/**
|
|
48
|
+
* Get all cube names that have rules
|
|
49
|
+
*/
|
|
50
|
+
getAllNamesWithRules(skipControlCubes?: boolean): Promise<string[]>;
|
|
51
|
+
/**
|
|
52
|
+
* Get all cube names that do not have rules
|
|
53
|
+
*/
|
|
54
|
+
getAllNamesWithoutRules(skipControlCubes?: boolean): Promise<string[]>;
|
|
55
|
+
/**
|
|
56
|
+
* Search cubes by multiple criteria
|
|
57
|
+
*/
|
|
58
|
+
searchCubes(criteria: {
|
|
59
|
+
namePattern?: string;
|
|
60
|
+
dimensionNames?: string[];
|
|
61
|
+
hasRules?: boolean;
|
|
62
|
+
skipControlCubes?: boolean;
|
|
63
|
+
minDimensions?: number;
|
|
64
|
+
maxDimensions?: number;
|
|
65
|
+
}): Promise<string[]>;
|
|
42
66
|
}
|
|
43
67
|
//# sourceMappingURL=CubeService.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CubeService.d.ts","sourceRoot":"","sources":["../../src/services/CubeService.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"CubeService.d.ts","sourceRoot":"","sources":["../../src/services/CubeService.ts"],"names":[],"mappings":"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"}
|