tm1npm 1.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/.env.example +16 -0
- package/.eslintrc.js +28 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +36 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
- package/.github/ISSUE_TEMPLATE/question.md +24 -0
- package/.github/workflows/publish-npm.yml +53 -0
- package/.github/workflows/test-on-tag.yml +172 -0
- package/DEVELOPMENT_GUIDE.md +587 -0
- package/LICENSE +21 -0
- package/README.md +580 -0
- package/jest.ci.config.js +83 -0
- package/jest.config.js +68 -0
- package/lib/exceptions/TM1Exception.d.ts +17 -0
- package/lib/exceptions/TM1Exception.d.ts.map +1 -0
- package/lib/exceptions/TM1Exception.js +36 -0
- package/lib/exceptions/TM1RestException.d.ts +10 -0
- package/lib/exceptions/TM1RestException.d.ts.map +1 -0
- package/lib/exceptions/TM1RestException.js +17 -0
- package/lib/exceptions/TM1TimeoutException.d.ts +9 -0
- package/lib/exceptions/TM1TimeoutException.d.ts.map +1 -0
- package/lib/exceptions/TM1TimeoutException.js +16 -0
- package/lib/index.d.ts +41 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +132 -0
- package/lib/objects/Annotation.d.ts +36 -0
- package/lib/objects/Annotation.d.ts.map +1 -0
- package/lib/objects/Annotation.js +134 -0
- package/lib/objects/Application.d.ts +67 -0
- package/lib/objects/Application.d.ts.map +1 -0
- package/lib/objects/Application.js +125 -0
- package/lib/objects/Axis.d.ts +36 -0
- package/lib/objects/Axis.d.ts.map +1 -0
- package/lib/objects/Axis.js +103 -0
- package/lib/objects/Chore.d.ts +43 -0
- package/lib/objects/Chore.d.ts.map +1 -0
- package/lib/objects/Chore.js +129 -0
- package/lib/objects/ChoreFrequency.d.ts +23 -0
- package/lib/objects/ChoreFrequency.d.ts.map +1 -0
- package/lib/objects/ChoreFrequency.js +61 -0
- package/lib/objects/ChoreStartTime.d.ts +15 -0
- package/lib/objects/ChoreStartTime.d.ts.map +1 -0
- package/lib/objects/ChoreStartTime.js +85 -0
- package/lib/objects/ChoreTask.d.ts +28 -0
- package/lib/objects/ChoreTask.d.ts.map +1 -0
- package/lib/objects/ChoreTask.js +66 -0
- package/lib/objects/Cube.d.ts +26 -0
- package/lib/objects/Cube.d.ts.map +1 -0
- package/lib/objects/Cube.js +109 -0
- package/lib/objects/Dimension.d.ts +26 -0
- package/lib/objects/Dimension.d.ts.map +1 -0
- package/lib/objects/Dimension.js +86 -0
- package/lib/objects/Element.d.ts +35 -0
- package/lib/objects/Element.d.ts.map +1 -0
- package/lib/objects/Element.js +115 -0
- package/lib/objects/ElementAttribute.d.ts +28 -0
- package/lib/objects/ElementAttribute.d.ts.map +1 -0
- package/lib/objects/ElementAttribute.js +97 -0
- package/lib/objects/Git.d.ts +21 -0
- package/lib/objects/Git.d.ts.map +1 -0
- package/lib/objects/Git.js +49 -0
- package/lib/objects/GitCommit.d.ts +12 -0
- package/lib/objects/GitCommit.d.ts.map +1 -0
- package/lib/objects/GitCommit.js +26 -0
- package/lib/objects/GitPlan.d.ts +35 -0
- package/lib/objects/GitPlan.d.ts.map +1 -0
- package/lib/objects/GitPlan.js +78 -0
- package/lib/objects/GitRemote.d.ts +12 -0
- package/lib/objects/GitRemote.d.ts.map +1 -0
- package/lib/objects/GitRemote.js +26 -0
- package/lib/objects/Hierarchy.d.ts +47 -0
- package/lib/objects/Hierarchy.d.ts.map +1 -0
- package/lib/objects/Hierarchy.js +165 -0
- package/lib/objects/MDXView.d.ts +19 -0
- package/lib/objects/MDXView.d.ts.map +1 -0
- package/lib/objects/MDXView.js +67 -0
- package/lib/objects/NativeView.d.ts +39 -0
- package/lib/objects/NativeView.d.ts.map +1 -0
- package/lib/objects/NativeView.js +209 -0
- package/lib/objects/Process.d.ts +70 -0
- package/lib/objects/Process.d.ts.map +1 -0
- package/lib/objects/Process.js +210 -0
- package/lib/objects/ProcessDebugBreakpoint.d.ts +60 -0
- package/lib/objects/ProcessDebugBreakpoint.d.ts.map +1 -0
- package/lib/objects/ProcessDebugBreakpoint.js +168 -0
- package/lib/objects/ProcessParameter.d.ts +23 -0
- package/lib/objects/ProcessParameter.d.ts.map +1 -0
- package/lib/objects/ProcessParameter.js +55 -0
- package/lib/objects/ProcessVariable.d.ts +26 -0
- package/lib/objects/ProcessVariable.d.ts.map +1 -0
- package/lib/objects/ProcessVariable.js +63 -0
- package/lib/objects/Rules.d.ts +30 -0
- package/lib/objects/Rules.d.ts.map +1 -0
- package/lib/objects/Rules.js +103 -0
- package/lib/objects/Sandbox.d.ts +21 -0
- package/lib/objects/Sandbox.d.ts.map +1 -0
- package/lib/objects/Sandbox.js +64 -0
- package/lib/objects/Server.d.ts +27 -0
- package/lib/objects/Server.d.ts.map +1 -0
- package/lib/objects/Server.js +26 -0
- package/lib/objects/Subset.d.ts +44 -0
- package/lib/objects/Subset.d.ts.map +1 -0
- package/lib/objects/Subset.js +222 -0
- package/lib/objects/TM1Object.d.ts +8 -0
- package/lib/objects/TM1Object.d.ts.map +1 -0
- package/lib/objects/TM1Object.js +17 -0
- package/lib/objects/TM1Project.d.ts +74 -0
- package/lib/objects/TM1Project.d.ts.map +1 -0
- package/lib/objects/TM1Project.js +409 -0
- package/lib/objects/User.d.ts +42 -0
- package/lib/objects/User.d.ts.map +1 -0
- package/lib/objects/User.js +157 -0
- package/lib/objects/View.d.ts +19 -0
- package/lib/objects/View.d.ts.map +1 -0
- package/lib/objects/View.js +33 -0
- package/lib/objects/index.d.ts +30 -0
- package/lib/objects/index.d.ts.map +1 -0
- package/lib/objects/index.js +68 -0
- package/lib/services/AnnotationService.d.ts +17 -0
- package/lib/services/AnnotationService.d.ts.map +1 -0
- package/lib/services/AnnotationService.js +91 -0
- package/lib/services/ApplicationService.d.ts +21 -0
- package/lib/services/ApplicationService.d.ts.map +1 -0
- package/lib/services/ApplicationService.js +227 -0
- package/lib/services/AuditLogService.d.ts +15 -0
- package/lib/services/AuditLogService.d.ts.map +1 -0
- package/lib/services/AuditLogService.js +153 -0
- package/lib/services/CellService.d.ts +191 -0
- package/lib/services/CellService.d.ts.map +1 -0
- package/lib/services/CellService.js +597 -0
- package/lib/services/ChoreService.d.ts +24 -0
- package/lib/services/ChoreService.d.ts.map +1 -0
- package/lib/services/ChoreService.js +219 -0
- package/lib/services/ConfigurationService.d.ts +18 -0
- package/lib/services/ConfigurationService.d.ts.map +1 -0
- package/lib/services/ConfigurationService.js +60 -0
- package/lib/services/CubeService.d.ts +43 -0
- package/lib/services/CubeService.d.ts.map +1 -0
- package/lib/services/CubeService.js +296 -0
- package/lib/services/DimensionService.d.ts +21 -0
- package/lib/services/DimensionService.d.ts.map +1 -0
- package/lib/services/DimensionService.js +146 -0
- package/lib/services/ElementService.d.ts +132 -0
- package/lib/services/ElementService.d.ts.map +1 -0
- package/lib/services/ElementService.js +579 -0
- package/lib/services/FileService.d.ts +14 -0
- package/lib/services/FileService.d.ts.map +1 -0
- package/lib/services/FileService.js +65 -0
- package/lib/services/GitService.d.ts +23 -0
- package/lib/services/GitService.d.ts.map +1 -0
- package/lib/services/GitService.js +225 -0
- package/lib/services/HierarchyService.d.ts +32 -0
- package/lib/services/HierarchyService.d.ts.map +1 -0
- package/lib/services/HierarchyService.js +235 -0
- package/lib/services/JobService.d.ts +13 -0
- package/lib/services/JobService.d.ts.map +1 -0
- package/lib/services/JobService.js +83 -0
- package/lib/services/LoggerService.d.ts +16 -0
- package/lib/services/LoggerService.d.ts.map +1 -0
- package/lib/services/LoggerService.js +101 -0
- package/lib/services/ManageService.d.ts +47 -0
- package/lib/services/ManageService.d.ts.map +1 -0
- package/lib/services/ManageService.js +221 -0
- package/lib/services/MessageLogService.d.ts +13 -0
- package/lib/services/MessageLogService.d.ts.map +1 -0
- package/lib/services/MessageLogService.js +175 -0
- package/lib/services/MonitoringService.d.ts +29 -0
- package/lib/services/MonitoringService.d.ts.map +1 -0
- package/lib/services/MonitoringService.js +86 -0
- package/lib/services/ObjectService.d.ts +8 -0
- package/lib/services/ObjectService.d.ts.map +1 -0
- package/lib/services/ObjectService.js +20 -0
- package/lib/services/PowerBiService.d.ts +13 -0
- package/lib/services/PowerBiService.d.ts.map +1 -0
- package/lib/services/PowerBiService.js +52 -0
- package/lib/services/ProcessService.d.ts +57 -0
- package/lib/services/ProcessService.d.ts.map +1 -0
- package/lib/services/ProcessService.js +499 -0
- package/lib/services/RestService.d.ts +68 -0
- package/lib/services/RestService.d.ts.map +1 -0
- package/lib/services/RestService.js +166 -0
- package/lib/services/SandboxService.d.ts +28 -0
- package/lib/services/SandboxService.d.ts.map +1 -0
- package/lib/services/SandboxService.js +197 -0
- package/lib/services/SecurityService.d.ts +33 -0
- package/lib/services/SecurityService.d.ts.map +1 -0
- package/lib/services/SecurityService.js +249 -0
- package/lib/services/ServerService.d.ts +55 -0
- package/lib/services/ServerService.d.ts.map +1 -0
- package/lib/services/ServerService.js +247 -0
- package/lib/services/SessionService.d.ts +15 -0
- package/lib/services/SessionService.d.ts.map +1 -0
- package/lib/services/SessionService.js +68 -0
- package/lib/services/SubsetService.d.ts +13 -0
- package/lib/services/SubsetService.d.ts.map +1 -0
- package/lib/services/SubsetService.js +46 -0
- package/lib/services/TM1Service.d.ts +39 -0
- package/lib/services/TM1Service.d.ts.map +1 -0
- package/lib/services/TM1Service.js +94 -0
- package/lib/services/ThreadService.d.ts +15 -0
- package/lib/services/ThreadService.d.ts.map +1 -0
- package/lib/services/ThreadService.js +104 -0
- package/lib/services/TransactionLogService.d.ts +11 -0
- package/lib/services/TransactionLogService.d.ts.map +1 -0
- package/lib/services/TransactionLogService.js +124 -0
- package/lib/services/UserService.d.ts +14 -0
- package/lib/services/UserService.d.ts.map +1 -0
- package/lib/services/UserService.js +103 -0
- package/lib/services/ViewService.d.ts +30 -0
- package/lib/services/ViewService.d.ts.map +1 -0
- package/lib/services/ViewService.js +331 -0
- package/lib/services/index.d.ts +26 -0
- package/lib/services/index.d.ts.map +1 -0
- package/lib/services/index.js +56 -0
- package/lib/tests/100PercentParityCheck.test.d.ts +6 -0
- package/lib/tests/100PercentParityCheck.test.d.ts.map +1 -0
- package/lib/tests/100PercentParityCheck.test.js +143 -0
- package/lib/tests/basic.test.d.ts +6 -0
- package/lib/tests/basic.test.d.ts.map +1 -0
- package/lib/tests/basic.test.js +52 -0
- package/lib/tests/cellService.test.d.ts +6 -0
- package/lib/tests/cellService.test.d.ts.map +1 -0
- package/lib/tests/cellService.test.js +311 -0
- package/lib/tests/ciSetup.d.ts +6 -0
- package/lib/tests/ciSetup.d.ts.map +1 -0
- package/lib/tests/ciSetup.js +23 -0
- package/lib/tests/comprehensive.service.test.d.ts +6 -0
- package/lib/tests/comprehensive.service.test.d.ts.map +1 -0
- package/lib/tests/comprehensive.service.test.js +507 -0
- package/lib/tests/connection.test.d.ts +6 -0
- package/lib/tests/connection.test.d.ts.map +1 -0
- package/lib/tests/connection.test.js +89 -0
- package/lib/tests/cubeService.test.d.ts +6 -0
- package/lib/tests/cubeService.test.d.ts.map +1 -0
- package/lib/tests/cubeService.test.js +368 -0
- package/lib/tests/dimensionService.comprehensive.test.d.ts +7 -0
- package/lib/tests/dimensionService.comprehensive.test.d.ts.map +1 -0
- package/lib/tests/dimensionService.comprehensive.test.js +614 -0
- package/lib/tests/dimensionService.test.d.ts +6 -0
- package/lib/tests/dimensionService.test.d.ts.map +1 -0
- package/lib/tests/dimensionService.test.js +293 -0
- package/lib/tests/edgeCases.test.d.ts +6 -0
- package/lib/tests/edgeCases.test.d.ts.map +1 -0
- package/lib/tests/edgeCases.test.js +301 -0
- package/lib/tests/elementService.comprehensive.test.d.ts +7 -0
- package/lib/tests/elementService.comprehensive.test.d.ts.map +1 -0
- package/lib/tests/elementService.comprehensive.test.js +846 -0
- package/lib/tests/elementService.test.d.ts +6 -0
- package/lib/tests/elementService.test.d.ts.map +1 -0
- package/lib/tests/elementService.test.js +350 -0
- package/lib/tests/enhancedCellService.test.d.ts +2 -0
- package/lib/tests/enhancedCellService.test.d.ts.map +1 -0
- package/lib/tests/enhancedCellService.test.js +152 -0
- package/lib/tests/enhancedCubeService.test.d.ts +2 -0
- package/lib/tests/enhancedCubeService.test.d.ts.map +1 -0
- package/lib/tests/enhancedCubeService.test.js +246 -0
- package/lib/tests/enhancedElementService.test.d.ts +2 -0
- package/lib/tests/enhancedElementService.test.d.ts.map +1 -0
- package/lib/tests/enhancedElementService.test.js +199 -0
- package/lib/tests/enhancedViewService.test.d.ts +2 -0
- package/lib/tests/enhancedViewService.test.d.ts.map +1 -0
- package/lib/tests/enhancedViewService.test.js +260 -0
- package/lib/tests/errorHandling.test.d.ts +6 -0
- package/lib/tests/errorHandling.test.d.ts.map +1 -0
- package/lib/tests/errorHandling.test.js +227 -0
- package/lib/tests/exceptions.test.d.ts +7 -0
- package/lib/tests/exceptions.test.d.ts.map +1 -0
- package/lib/tests/exceptions.test.js +257 -0
- package/lib/tests/hierarchyService.test.d.ts +6 -0
- package/lib/tests/hierarchyService.test.d.ts.map +1 -0
- package/lib/tests/hierarchyService.test.js +294 -0
- package/lib/tests/index.test.d.ts +6 -0
- package/lib/tests/index.test.d.ts.map +1 -0
- package/lib/tests/index.test.js +346 -0
- package/lib/tests/integration.test.d.ts +6 -0
- package/lib/tests/integration.test.d.ts.map +1 -0
- package/lib/tests/integration.test.js +302 -0
- package/lib/tests/integrationTests.test.d.ts +2 -0
- package/lib/tests/integrationTests.test.d.ts.map +1 -0
- package/lib/tests/integrationTests.test.js +252 -0
- package/lib/tests/mdx.advanced.test.d.ts +6 -0
- package/lib/tests/mdx.advanced.test.d.ts.map +1 -0
- package/lib/tests/mdx.advanced.test.js +437 -0
- package/lib/tests/objects.improved.test.d.ts +7 -0
- package/lib/tests/objects.improved.test.d.ts.map +1 -0
- package/lib/tests/objects.improved.test.js +302 -0
- package/lib/tests/performance.test.d.ts +6 -0
- package/lib/tests/performance.test.d.ts.map +1 -0
- package/lib/tests/performance.test.js +264 -0
- package/lib/tests/processService.comprehensive.test.d.ts +7 -0
- package/lib/tests/processService.comprehensive.test.d.ts.map +1 -0
- package/lib/tests/processService.comprehensive.test.js +656 -0
- package/lib/tests/processService.test.d.ts +6 -0
- package/lib/tests/processService.test.d.ts.map +1 -0
- package/lib/tests/processService.test.js +322 -0
- package/lib/tests/restService.test.d.ts +6 -0
- package/lib/tests/restService.test.d.ts.map +1 -0
- package/lib/tests/restService.test.js +177 -0
- package/lib/tests/security.advanced.test.d.ts +6 -0
- package/lib/tests/security.advanced.test.d.ts.map +1 -0
- package/lib/tests/security.advanced.test.js +407 -0
- package/lib/tests/security.test.d.ts +6 -0
- package/lib/tests/security.test.d.ts.map +1 -0
- package/lib/tests/security.test.js +204 -0
- package/lib/tests/securityService.comprehensive.test.d.ts +7 -0
- package/lib/tests/securityService.comprehensive.test.d.ts.map +1 -0
- package/lib/tests/securityService.comprehensive.test.js +457 -0
- package/lib/tests/setup.d.ts +4 -0
- package/lib/tests/setup.d.ts.map +1 -0
- package/lib/tests/setup.js +40 -0
- package/lib/tests/simpleCoverage.test.d.ts +6 -0
- package/lib/tests/simpleCoverage.test.d.ts.map +1 -0
- package/lib/tests/simpleCoverage.test.js +236 -0
- package/lib/tests/stress.performance.test.d.ts +6 -0
- package/lib/tests/stress.performance.test.d.ts.map +1 -0
- package/lib/tests/stress.performance.test.js +423 -0
- package/lib/tests/subsetService.test.d.ts +6 -0
- package/lib/tests/subsetService.test.d.ts.map +1 -0
- package/lib/tests/subsetService.test.js +271 -0
- package/lib/tests/testConfig.d.ts +18 -0
- package/lib/tests/testConfig.d.ts.map +1 -0
- package/lib/tests/testConfig.js +38 -0
- package/lib/tests/testUtils.d.ts +9 -0
- package/lib/tests/testUtils.d.ts.map +1 -0
- package/lib/tests/testUtils.js +100 -0
- package/lib/tests/tm1Service.test.d.ts +7 -0
- package/lib/tests/tm1Service.test.d.ts.map +1 -0
- package/lib/tests/tm1Service.test.js +290 -0
- package/lib/tests/viewService.test.d.ts +6 -0
- package/lib/tests/viewService.test.d.ts.map +1 -0
- package/lib/tests/viewService.test.js +240 -0
- package/lib/utils/Utils.d.ts +90 -0
- package/lib/utils/Utils.d.ts.map +1 -0
- package/lib/utils/Utils.js +379 -0
- package/package.json +81 -0
- package/run-all-tests.js +296 -0
- package/src/exceptions/TM1Exception.ts +38 -0
- package/src/exceptions/TM1RestException.ts +17 -0
- package/src/exceptions/TM1TimeoutException.ts +15 -0
- package/src/index.ts +94 -0
- package/src/objects/Annotation.ts +194 -0
- package/src/objects/Application.ts +146 -0
- package/src/objects/Axis.ts +149 -0
- package/src/objects/Chore.ts +174 -0
- package/src/objects/ChoreFrequency.ts +83 -0
- package/src/objects/ChoreStartTime.ts +111 -0
- package/src/objects/ChoreTask.ts +92 -0
- package/src/objects/Cube.ts +125 -0
- package/src/objects/Dimension.ts +107 -0
- package/src/objects/Element.ts +153 -0
- package/src/objects/ElementAttribute.ts +115 -0
- package/src/objects/Git.ts +86 -0
- package/src/objects/GitCommit.ts +31 -0
- package/src/objects/GitPlan.ts +121 -0
- package/src/objects/GitRemote.ts +31 -0
- package/src/objects/Hierarchy.ts +229 -0
- package/src/objects/MDXView.ts +91 -0
- package/src/objects/NativeView.ts +268 -0
- package/src/objects/Process.ts +320 -0
- package/src/objects/ProcessDebugBreakpoint.ts +239 -0
- package/src/objects/ProcessParameter.ts +76 -0
- package/src/objects/ProcessVariable.ts +89 -0
- package/src/objects/Rules.ts +117 -0
- package/src/objects/Sandbox.ts +90 -0
- package/src/objects/Server.ts +45 -0
- package/src/objects/Subset.ts +323 -0
- package/src/objects/TM1Object.ts +17 -0
- package/src/objects/TM1Project.ts +587 -0
- package/src/objects/User.ts +198 -0
- package/src/objects/View.ts +43 -0
- package/src/objects/index.ts +36 -0
- package/src/services/AnnotationService.ts +107 -0
- package/src/services/ApplicationService.ts +279 -0
- package/src/services/AuditLogService.ts +172 -0
- package/src/services/CellService.ts +814 -0
- package/src/services/ChoreService.ts +219 -0
- package/src/services/ConfigurationService.ts +69 -0
- package/src/services/CubeService.ts +338 -0
- package/src/services/DimensionService.ts +168 -0
- package/src/services/ElementService.ts +966 -0
- package/src/services/FileService.ts +67 -0
- package/src/services/GitService.ts +324 -0
- package/src/services/HierarchyService.ts +284 -0
- package/src/services/JobService.ts +59 -0
- package/src/services/LoggerService.ts +118 -0
- package/src/services/ManageService.ts +322 -0
- package/src/services/MessageLogService.ts +211 -0
- package/src/services/MonitoringService.ts +105 -0
- package/src/services/ObjectService.ts +21 -0
- package/src/services/PowerBiService.ts +85 -0
- package/src/services/ProcessService.ts +589 -0
- package/src/services/RestService.ts +224 -0
- package/src/services/SandboxService.ts +217 -0
- package/src/services/SecurityService.ts +284 -0
- package/src/services/ServerService.ts +313 -0
- package/src/services/SessionService.ts +81 -0
- package/src/services/SubsetService.ts +52 -0
- package/src/services/TM1Service.ts +133 -0
- package/src/services/ThreadService.ts +83 -0
- package/src/services/TransactionLogService.ts +148 -0
- package/src/services/UserService.ts +77 -0
- package/src/services/ViewService.ts +398 -0
- package/src/services/index.ts +28 -0
- package/src/tests/100PercentParityCheck.test.ts +166 -0
- package/src/tests/basic.test.ts +59 -0
- package/src/tests/cellService.test.ts +405 -0
- package/src/tests/ciSetup.ts +26 -0
- package/src/tests/comprehensive.service.test.ts +653 -0
- package/src/tests/config.ini.template +23 -0
- package/src/tests/connection.test.ts +90 -0
- package/src/tests/cubeService.test.ts +458 -0
- package/src/tests/dimensionService.comprehensive.test.ts +786 -0
- package/src/tests/dimensionService.test.ts +373 -0
- package/src/tests/edgeCases.test.ts +358 -0
- package/src/tests/elementService.comprehensive.test.ts +1190 -0
- package/src/tests/elementService.test.ts +472 -0
- package/src/tests/enhancedCellService.test.ts +237 -0
- package/src/tests/enhancedCubeService.test.ts +384 -0
- package/src/tests/enhancedElementService.test.ts +301 -0
- package/src/tests/enhancedViewService.test.ts +373 -0
- package/src/tests/errorHandling.test.ts +264 -0
- package/src/tests/exceptions.test.ts +313 -0
- package/src/tests/hierarchyService.test.ts +386 -0
- package/src/tests/index.test.ts +376 -0
- package/src/tests/integration.test.ts +333 -0
- package/src/tests/integrationTests.test.ts +302 -0
- package/src/tests/mdx.advanced.test.ts +513 -0
- package/src/tests/objects.improved.test.ts +385 -0
- package/src/tests/performance.test.ts +314 -0
- package/src/tests/processService.comprehensive.test.ts +933 -0
- package/src/tests/processService.test.ts +409 -0
- package/src/tests/restService.test.ts +218 -0
- package/src/tests/security.advanced.test.ts +464 -0
- package/src/tests/security.test.ts +233 -0
- package/src/tests/securityService.comprehensive.test.ts +582 -0
- package/src/tests/setup.ts +42 -0
- package/src/tests/simpleCoverage.test.ts +287 -0
- package/src/tests/stress.performance.test.ts +531 -0
- package/src/tests/subsetService.test.ts +350 -0
- package/src/tests/testConfig.ts +53 -0
- package/src/tests/testUtils.ts +94 -0
- package/src/tests/tm1Service.test.ts +361 -0
- package/src/tests/viewService.test.ts +324 -0
- package/src/utils/Utils.ts +395 -0
- package/tests/README.md +57 -0
- package/tests/connection/test-connection.ts +86 -0
- package/tests/edge-cases/edge-cases-test.ts +244 -0
- package/tests/integration/working-test.ts +193 -0
- package/tests/performance/performance-test.ts +133 -0
- package/tests/run-all-tests.sh +106 -0
- package/tests/security/security-test.ts +103 -0
- package/tsconfig.json +20 -0
|
@@ -0,0 +1,1190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Comprehensive ElementService Tests
|
|
3
|
+
* Target: Achieve 80%+ coverage for ElementService (currently 48%)
|
|
4
|
+
* Testing all element operations including CRUD, hierarchy management, attributes, and advanced features
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { ElementService, MDXDrillMethod } from '../services/ElementService';
|
|
8
|
+
import { RestService } from '../services/RestService';
|
|
9
|
+
import { Element, ElementType } from '../objects/Element';
|
|
10
|
+
import { ElementAttribute } from '../objects/ElementAttribute';
|
|
11
|
+
import { CaseAndSpaceInsensitiveDict } from '../utils/Utils';
|
|
12
|
+
|
|
13
|
+
// Mock dependencies
|
|
14
|
+
jest.mock('../objects/Element');
|
|
15
|
+
jest.mock('../objects/ElementAttribute');
|
|
16
|
+
|
|
17
|
+
describe('ElementService - Comprehensive Tests', () => {
|
|
18
|
+
let elementService: ElementService;
|
|
19
|
+
let mockRestService: jest.Mocked<RestService>;
|
|
20
|
+
|
|
21
|
+
const mockResponse = (data: any) => ({
|
|
22
|
+
data: data,
|
|
23
|
+
status: 200,
|
|
24
|
+
statusText: 'OK',
|
|
25
|
+
headers: {},
|
|
26
|
+
config: { headers: {} }
|
|
27
|
+
} as any);
|
|
28
|
+
|
|
29
|
+
const mockElement = {
|
|
30
|
+
name: 'TestElement',
|
|
31
|
+
elementType: ElementType.NUMERIC,
|
|
32
|
+
body: {
|
|
33
|
+
Name: 'TestElement',
|
|
34
|
+
Type: 'Numeric'
|
|
35
|
+
}
|
|
36
|
+
} as any;
|
|
37
|
+
|
|
38
|
+
const mockElementAttribute = {
|
|
39
|
+
name: 'TestAttribute',
|
|
40
|
+
attributeType: 'String',
|
|
41
|
+
body: {
|
|
42
|
+
Name: 'TestAttribute',
|
|
43
|
+
Type: 'String'
|
|
44
|
+
}
|
|
45
|
+
} as any;
|
|
46
|
+
|
|
47
|
+
beforeEach(() => {
|
|
48
|
+
mockRestService = {
|
|
49
|
+
get: jest.fn(),
|
|
50
|
+
post: jest.fn(),
|
|
51
|
+
patch: jest.fn(),
|
|
52
|
+
put: jest.fn(),
|
|
53
|
+
delete: jest.fn()
|
|
54
|
+
} as any;
|
|
55
|
+
|
|
56
|
+
elementService = new ElementService(mockRestService);
|
|
57
|
+
|
|
58
|
+
// Mock Element.fromDict
|
|
59
|
+
(Element as any).fromDict = jest.fn().mockReturnValue(mockElement);
|
|
60
|
+
|
|
61
|
+
// Mock ElementAttribute.fromDict
|
|
62
|
+
(ElementAttribute as any).fromDict = jest.fn().mockReturnValue(mockElementAttribute);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
afterEach(() => {
|
|
66
|
+
jest.clearAllMocks();
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
describe('Constructor and Initialization', () => {
|
|
70
|
+
test('should initialize ElementService properly', () => {
|
|
71
|
+
expect(elementService).toBeDefined();
|
|
72
|
+
expect(elementService).toBeInstanceOf(ElementService);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test('should extend ObjectService', () => {
|
|
76
|
+
expect(elementService).toBeInstanceOf(ElementService);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
describe('Element CRUD Operations', () => {
|
|
81
|
+
test('should get element by dimension, hierarchy, and name', async () => {
|
|
82
|
+
const elementData = {
|
|
83
|
+
Name: 'TestElement',
|
|
84
|
+
Type: 'Numeric'
|
|
85
|
+
};
|
|
86
|
+
mockRestService.get.mockResolvedValue(mockResponse(elementData));
|
|
87
|
+
|
|
88
|
+
const result = await elementService.get('TestDim', 'TestHierarchy', 'TestElement');
|
|
89
|
+
|
|
90
|
+
expect(Element.fromDict).toHaveBeenCalledWith(elementData);
|
|
91
|
+
expect(mockRestService.get).toHaveBeenCalledWith(
|
|
92
|
+
"/Dimensions('TestDim')/Hierarchies('TestHierarchy')/Elements('TestElement')?$expand=*"
|
|
93
|
+
);
|
|
94
|
+
expect(result).toEqual(mockElement);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test('should create element', async () => {
|
|
98
|
+
mockRestService.post.mockResolvedValue(mockResponse({}));
|
|
99
|
+
|
|
100
|
+
const result = await elementService.create('TestDim', 'TestHierarchy', mockElement);
|
|
101
|
+
|
|
102
|
+
expect(result).toBeDefined();
|
|
103
|
+
expect(mockRestService.post).toHaveBeenCalledWith(
|
|
104
|
+
"/Dimensions('TestDim')/Hierarchies('TestHierarchy')/Elements",
|
|
105
|
+
mockElement.body
|
|
106
|
+
);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test('should update element', async () => {
|
|
110
|
+
mockRestService.patch.mockResolvedValue(mockResponse({}));
|
|
111
|
+
|
|
112
|
+
const result = await elementService.update('TestDim', 'TestHierarchy', mockElement);
|
|
113
|
+
|
|
114
|
+
expect(result).toBeDefined();
|
|
115
|
+
expect(mockRestService.patch).toHaveBeenCalledWith(
|
|
116
|
+
"/Dimensions('TestDim')/Hierarchies('TestHierarchy')/Elements('TestElement')",
|
|
117
|
+
mockElement.body
|
|
118
|
+
);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test('should delete element', async () => {
|
|
122
|
+
mockRestService.delete.mockResolvedValue(mockResponse({}));
|
|
123
|
+
|
|
124
|
+
const result = await elementService.delete('TestDim', 'TestHierarchy', 'TestElement');
|
|
125
|
+
|
|
126
|
+
expect(result).toBeDefined();
|
|
127
|
+
expect(mockRestService.delete).toHaveBeenCalledWith(
|
|
128
|
+
"/Dimensions('TestDim')/Hierarchies('TestHierarchy')/Elements('TestElement')"
|
|
129
|
+
);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
test('should check if element exists', async () => {
|
|
133
|
+
mockRestService.get.mockResolvedValue(mockResponse({}));
|
|
134
|
+
|
|
135
|
+
const result = await elementService.exists('TestDim', 'TestHierarchy', 'TestElement');
|
|
136
|
+
|
|
137
|
+
expect(result).toBe(true);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test('should return false when element does not exist', async () => {
|
|
141
|
+
mockRestService.get.mockRejectedValue(new Error('Element not found'));
|
|
142
|
+
|
|
143
|
+
const result = await elementService.exists('TestDim', 'TestHierarchy', 'NonExistent');
|
|
144
|
+
|
|
145
|
+
expect(result).toBe(false);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
test('should update or create element - update existing', async () => {
|
|
149
|
+
jest.spyOn(elementService, 'exists').mockResolvedValue(true);
|
|
150
|
+
jest.spyOn(elementService, 'update').mockResolvedValue(mockResponse({}));
|
|
151
|
+
|
|
152
|
+
const result = await elementService.updateOrCreate('TestDim', 'TestHierarchy', mockElement);
|
|
153
|
+
|
|
154
|
+
expect(elementService.exists).toHaveBeenCalledWith('TestDim', 'TestHierarchy', 'TestElement');
|
|
155
|
+
expect(elementService.update).toHaveBeenCalledWith('TestDim', 'TestHierarchy', mockElement);
|
|
156
|
+
expect(result).toBeDefined();
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
test('should update or create element - create new', async () => {
|
|
160
|
+
jest.spyOn(elementService, 'exists').mockResolvedValue(false);
|
|
161
|
+
jest.spyOn(elementService, 'create').mockResolvedValue(mockResponse({}));
|
|
162
|
+
|
|
163
|
+
const result = await elementService.updateOrCreate('TestDim', 'TestHierarchy', mockElement);
|
|
164
|
+
|
|
165
|
+
expect(elementService.exists).toHaveBeenCalledWith('TestDim', 'TestHierarchy', 'TestElement');
|
|
166
|
+
expect(elementService.create).toHaveBeenCalledWith('TestDim', 'TestHierarchy', mockElement);
|
|
167
|
+
expect(result).toBeDefined();
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
describe('Element Retrieval Operations', () => {
|
|
172
|
+
test('should get element names', async () => {
|
|
173
|
+
const elementsData = {
|
|
174
|
+
value: [
|
|
175
|
+
{ Name: 'Element1' },
|
|
176
|
+
{ Name: 'Element2' },
|
|
177
|
+
{ Name: 'ConsolElement' }
|
|
178
|
+
]
|
|
179
|
+
};
|
|
180
|
+
mockRestService.get.mockResolvedValue(mockResponse(elementsData));
|
|
181
|
+
|
|
182
|
+
const result = await elementService.getNames('TestDim', 'TestHierarchy');
|
|
183
|
+
|
|
184
|
+
expect(result).toEqual(['Element1', 'Element2', 'ConsolElement']);
|
|
185
|
+
expect(mockRestService.get).toHaveBeenCalledWith(
|
|
186
|
+
"/Dimensions('TestDim')/Hierarchies('TestHierarchy')/Elements?$select=Name"
|
|
187
|
+
);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
test('should get element names with default hierarchy', async () => {
|
|
191
|
+
const elementsData = {
|
|
192
|
+
value: [
|
|
193
|
+
{ Name: 'Element1' },
|
|
194
|
+
{ Name: 'Element2' }
|
|
195
|
+
]
|
|
196
|
+
};
|
|
197
|
+
mockRestService.get.mockResolvedValue(mockResponse(elementsData));
|
|
198
|
+
|
|
199
|
+
const result = await elementService.getNames('TestDim');
|
|
200
|
+
|
|
201
|
+
expect(result).toEqual(['Element1', 'Element2']);
|
|
202
|
+
expect(mockRestService.get).toHaveBeenCalledWith(
|
|
203
|
+
"/Dimensions('TestDim')/Hierarchies('TestDim')/Elements?$select=Name"
|
|
204
|
+
);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
test('should get element names excluding consolidated elements', async () => {
|
|
208
|
+
const elementsData = {
|
|
209
|
+
value: [
|
|
210
|
+
{ Name: 'Element1' },
|
|
211
|
+
{ Name: 'Element2' }
|
|
212
|
+
]
|
|
213
|
+
};
|
|
214
|
+
mockRestService.get.mockResolvedValue(mockResponse(elementsData));
|
|
215
|
+
|
|
216
|
+
const result = await elementService.getNames('TestDim', 'TestHierarchy', true);
|
|
217
|
+
|
|
218
|
+
expect(result).toEqual(['Element1', 'Element2']);
|
|
219
|
+
expect(mockRestService.get).toHaveBeenCalledWith(
|
|
220
|
+
"/Dimensions('TestDim')/Hierarchies('TestHierarchy')/Elements?$select=Name&$filter=Type ne 'Consolidated'"
|
|
221
|
+
);
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
test('should get all elements', async () => {
|
|
225
|
+
const elementsData = {
|
|
226
|
+
value: [
|
|
227
|
+
{ Name: 'Element1', Type: 'Numeric' },
|
|
228
|
+
{ Name: 'Element2', Type: 'String' }
|
|
229
|
+
]
|
|
230
|
+
};
|
|
231
|
+
mockRestService.get.mockResolvedValue(mockResponse(elementsData));
|
|
232
|
+
|
|
233
|
+
const result = await elementService.getElements('TestDim', 'TestHierarchy');
|
|
234
|
+
|
|
235
|
+
expect(result).toHaveLength(2);
|
|
236
|
+
expect(Element.fromDict).toHaveBeenCalledTimes(2);
|
|
237
|
+
expect(mockRestService.get).toHaveBeenCalledWith(
|
|
238
|
+
"/Dimensions('TestDim')/Hierarchies('TestHierarchy')/Elements?$expand=*"
|
|
239
|
+
);
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
test('should get leaf element names', async () => {
|
|
243
|
+
const leafElementsData = {
|
|
244
|
+
value: [
|
|
245
|
+
{ Name: 'NumericLeaf' },
|
|
246
|
+
{ Name: 'StringLeaf' }
|
|
247
|
+
]
|
|
248
|
+
};
|
|
249
|
+
mockRestService.get.mockResolvedValue(mockResponse(leafElementsData));
|
|
250
|
+
|
|
251
|
+
const result = await elementService.getLeafElementNames('TestDim', 'TestHierarchy');
|
|
252
|
+
|
|
253
|
+
expect(result).toEqual(['NumericLeaf', 'StringLeaf']);
|
|
254
|
+
expect(mockRestService.get).toHaveBeenCalledWith(
|
|
255
|
+
"/Dimensions('TestDim')/Hierarchies('TestHierarchy')/Elements?$select=Name&$filter=Type eq 'Numeric' or Type eq 'String'"
|
|
256
|
+
);
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
test('should get consolidated element names', async () => {
|
|
260
|
+
const consolidatedData = {
|
|
261
|
+
value: [
|
|
262
|
+
{ Name: 'ConsolElement1' },
|
|
263
|
+
{ Name: 'ConsolElement2' }
|
|
264
|
+
]
|
|
265
|
+
};
|
|
266
|
+
mockRestService.get.mockResolvedValue(mockResponse(consolidatedData));
|
|
267
|
+
|
|
268
|
+
const result = await elementService.getConsolidatedElementNames('TestDim', 'TestHierarchy');
|
|
269
|
+
|
|
270
|
+
expect(result).toEqual(['ConsolElement1', 'ConsolElement2']);
|
|
271
|
+
expect(mockRestService.get).toHaveBeenCalledWith(
|
|
272
|
+
"/Dimensions('TestDim')/Hierarchies('TestHierarchy')/Elements?$select=Name&$filter=Type eq 'Consolidated'"
|
|
273
|
+
);
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
test('should get numeric element names', async () => {
|
|
277
|
+
const numericData = {
|
|
278
|
+
value: [
|
|
279
|
+
{ Name: 'NumericElement1' },
|
|
280
|
+
{ Name: 'NumericElement2' }
|
|
281
|
+
]
|
|
282
|
+
};
|
|
283
|
+
mockRestService.get.mockResolvedValue(mockResponse(numericData));
|
|
284
|
+
|
|
285
|
+
const result = await elementService.getNumericElementNames('TestDim', 'TestHierarchy');
|
|
286
|
+
|
|
287
|
+
expect(result).toEqual(['NumericElement1', 'NumericElement2']);
|
|
288
|
+
expect(mockRestService.get).toHaveBeenCalledWith(
|
|
289
|
+
"/Dimensions('TestDim')/Hierarchies('TestHierarchy')/Elements?$select=Name&$filter=Type eq 'Numeric'"
|
|
290
|
+
);
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
test('should get string element names', async () => {
|
|
294
|
+
const stringData = {
|
|
295
|
+
value: [
|
|
296
|
+
{ Name: 'StringElement1' },
|
|
297
|
+
{ Name: 'StringElement2' }
|
|
298
|
+
]
|
|
299
|
+
};
|
|
300
|
+
mockRestService.get.mockResolvedValue(mockResponse(stringData));
|
|
301
|
+
|
|
302
|
+
const result = await elementService.getStringElementNames('TestDim', 'TestHierarchy');
|
|
303
|
+
|
|
304
|
+
expect(result).toEqual(['StringElement1', 'StringElement2']);
|
|
305
|
+
expect(mockRestService.get).toHaveBeenCalledWith(
|
|
306
|
+
"/Dimensions('TestDim')/Hierarchies('TestHierarchy')/Elements?$select=Name&$filter=Type eq 'String'"
|
|
307
|
+
);
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
test('should get element types', async () => {
|
|
311
|
+
const typesData = {
|
|
312
|
+
value: [
|
|
313
|
+
{ Name: 'Element1', Type: 'Numeric' },
|
|
314
|
+
{ Name: 'Element2', Type: 'String' },
|
|
315
|
+
{ Name: 'Element3', Type: 'Consolidated' }
|
|
316
|
+
]
|
|
317
|
+
};
|
|
318
|
+
mockRestService.get.mockResolvedValue(mockResponse(typesData));
|
|
319
|
+
|
|
320
|
+
const result = await elementService.getElementTypes('TestDim', 'TestHierarchy');
|
|
321
|
+
|
|
322
|
+
expect(result).toBeInstanceOf(CaseAndSpaceInsensitiveDict);
|
|
323
|
+
expect(mockRestService.get).toHaveBeenCalledWith(
|
|
324
|
+
"/Dimensions('TestDim')/Hierarchies('TestHierarchy')/Elements?$select=Name,Type"
|
|
325
|
+
);
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
test('should get elements count', async () => {
|
|
329
|
+
mockRestService.get.mockResolvedValue(mockResponse('15'));
|
|
330
|
+
|
|
331
|
+
const result = await elementService.getElementsCount('TestDim', 'TestHierarchy');
|
|
332
|
+
|
|
333
|
+
expect(result).toBe(15);
|
|
334
|
+
expect(mockRestService.get).toHaveBeenCalledWith(
|
|
335
|
+
"/Dimensions('TestDim')/Hierarchies('TestHierarchy')/Elements/$count"
|
|
336
|
+
);
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
test('should get elements count excluding consolidated elements', async () => {
|
|
340
|
+
mockRestService.get.mockResolvedValue(mockResponse('10'));
|
|
341
|
+
|
|
342
|
+
const result = await elementService.getElementsCount('TestDim', 'TestHierarchy', true);
|
|
343
|
+
|
|
344
|
+
expect(result).toBe(10);
|
|
345
|
+
expect(mockRestService.get).toHaveBeenCalledWith(
|
|
346
|
+
"/Dimensions('TestDim')/Hierarchies('TestHierarchy')/Elements/$count?$filter=Type ne 'Consolidated'"
|
|
347
|
+
);
|
|
348
|
+
});
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
describe('Hierarchy Relationship Operations', () => {
|
|
352
|
+
test('should get element parents', async () => {
|
|
353
|
+
const parentsData = {
|
|
354
|
+
value: [
|
|
355
|
+
{ Name: 'Parent1' },
|
|
356
|
+
{ Name: 'Parent2' }
|
|
357
|
+
]
|
|
358
|
+
};
|
|
359
|
+
mockRestService.get.mockResolvedValue(mockResponse(parentsData));
|
|
360
|
+
|
|
361
|
+
const result = await elementService.getParents('TestDim', 'TestHierarchy', 'ChildElement');
|
|
362
|
+
|
|
363
|
+
expect(result).toEqual(['Parent1', 'Parent2']);
|
|
364
|
+
expect(mockRestService.get).toHaveBeenCalledWith(
|
|
365
|
+
"/Dimensions('TestDim')/Hierarchies('TestHierarchy')/Elements('ChildElement')/Parents?$select=Name"
|
|
366
|
+
);
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
test('should get element children', async () => {
|
|
370
|
+
const childrenData = {
|
|
371
|
+
value: [
|
|
372
|
+
{ Name: 'Child1' },
|
|
373
|
+
{ Name: 'Child2' }
|
|
374
|
+
]
|
|
375
|
+
};
|
|
376
|
+
mockRestService.get.mockResolvedValue(mockResponse(childrenData));
|
|
377
|
+
|
|
378
|
+
const result = await elementService.getChildren('TestDim', 'TestHierarchy', 'ParentElement');
|
|
379
|
+
|
|
380
|
+
expect(result).toEqual(['Child1', 'Child2']);
|
|
381
|
+
expect(mockRestService.get).toHaveBeenCalledWith(
|
|
382
|
+
"/Dimensions('TestDim')/Hierarchies('TestHierarchy')/Elements('ParentElement')/Components?$select=Name"
|
|
383
|
+
);
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
test('should get element ancestors', async () => {
|
|
387
|
+
// Mock parent chain: Element -> Parent1 -> GrandParent1
|
|
388
|
+
mockRestService.get
|
|
389
|
+
.mockResolvedValueOnce(mockResponse({ value: [{ Name: 'Parent1' }] })) // First call - direct parents
|
|
390
|
+
.mockResolvedValueOnce(mockResponse({ value: [{ Name: 'GrandParent1' }] })) // Second call - grandparents
|
|
391
|
+
.mockResolvedValueOnce(mockResponse({ value: [] })); // Third call - no more ancestors
|
|
392
|
+
|
|
393
|
+
const result = await elementService.getAncestors('TestDim', 'TestHierarchy', 'Element');
|
|
394
|
+
|
|
395
|
+
expect(result).toEqual(['Parent1', 'GrandParent1']);
|
|
396
|
+
expect(mockRestService.get).toHaveBeenCalledTimes(3);
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
test('should get element descendants', async () => {
|
|
400
|
+
// Mock child chain: Element -> Child1 -> GrandChild1
|
|
401
|
+
mockRestService.get
|
|
402
|
+
.mockResolvedValueOnce(mockResponse({ value: [{ Name: 'Child1' }] })) // First call - direct children
|
|
403
|
+
.mockResolvedValueOnce(mockResponse({ value: [{ Name: 'GrandChild1' }] })) // Second call - grandchildren
|
|
404
|
+
.mockResolvedValueOnce(mockResponse({ value: [] })); // Third call - no more descendants
|
|
405
|
+
|
|
406
|
+
const result = await elementService.getDescendants('TestDim', 'TestHierarchy', 'Element');
|
|
407
|
+
|
|
408
|
+
expect(result).toEqual(['Child1', 'GrandChild1']);
|
|
409
|
+
expect(mockRestService.get).toHaveBeenCalledTimes(3);
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
test('should handle circular references in ancestors', async () => {
|
|
413
|
+
// Mock circular reference: Element -> Parent1 -> Element
|
|
414
|
+
mockRestService.get
|
|
415
|
+
.mockResolvedValueOnce(mockResponse({ value: [{ Name: 'Parent1' }] }))
|
|
416
|
+
.mockResolvedValueOnce(mockResponse({ value: [{ Name: 'Element' }] }));
|
|
417
|
+
|
|
418
|
+
const result = await elementService.getAncestors('TestDim', 'TestHierarchy', 'Element');
|
|
419
|
+
|
|
420
|
+
// Should include both Parent1 and Element (the circular reference is handled by processed set)
|
|
421
|
+
expect(result).toEqual(['Parent1', 'Element']);
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
test('should handle circular references in descendants', async () => {
|
|
425
|
+
// Mock circular reference: Element -> Child1 -> Element
|
|
426
|
+
mockRestService.get
|
|
427
|
+
.mockResolvedValueOnce(mockResponse({ value: [{ Name: 'Child1' }] }))
|
|
428
|
+
.mockResolvedValueOnce(mockResponse({ value: [{ Name: 'Element' }] }));
|
|
429
|
+
|
|
430
|
+
const result = await elementService.getDescendants('TestDim', 'TestHierarchy', 'Element');
|
|
431
|
+
|
|
432
|
+
// Should include both Child1 and Element (the circular reference is handled by processed set)
|
|
433
|
+
expect(result).toEqual(['Child1', 'Element']);
|
|
434
|
+
});
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
describe('Edge Management Operations', () => {
|
|
438
|
+
test('should add edges', async () => {
|
|
439
|
+
mockRestService.post.mockResolvedValue(mockResponse({}));
|
|
440
|
+
|
|
441
|
+
const edges = [
|
|
442
|
+
{ parent: 'Parent1', child: 'Child1', weight: 1.5 },
|
|
443
|
+
{ parent: 'Parent1', child: 'Child2', weight: 2.0 },
|
|
444
|
+
{ parent: 'Parent2', child: 'Child1' } // No weight specified
|
|
445
|
+
];
|
|
446
|
+
|
|
447
|
+
await elementService.addEdges('TestDim', 'TestHierarchy', edges);
|
|
448
|
+
|
|
449
|
+
expect(mockRestService.post).toHaveBeenCalledTimes(3);
|
|
450
|
+
expect(mockRestService.post).toHaveBeenNthCalledWith(1,
|
|
451
|
+
"/Dimensions('TestDim')/Hierarchies('TestHierarchy')/Elements('Parent1')/Components",
|
|
452
|
+
{ Name: 'Child1', Weight: 1.5 }
|
|
453
|
+
);
|
|
454
|
+
expect(mockRestService.post).toHaveBeenNthCalledWith(2,
|
|
455
|
+
"/Dimensions('TestDim')/Hierarchies('TestHierarchy')/Elements('Parent1')/Components",
|
|
456
|
+
{ Name: 'Child2', Weight: 2.0 }
|
|
457
|
+
);
|
|
458
|
+
expect(mockRestService.post).toHaveBeenNthCalledWith(3,
|
|
459
|
+
"/Dimensions('TestDim')/Hierarchies('TestHierarchy')/Elements('Parent2')/Components",
|
|
460
|
+
{ Name: 'Child1', Weight: 1 }
|
|
461
|
+
);
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
test('should delete edges using REST API', async () => {
|
|
465
|
+
mockRestService.delete.mockResolvedValue(mockResponse({}));
|
|
466
|
+
|
|
467
|
+
const edges = [
|
|
468
|
+
{ parent: 'Parent1', child: 'Child1' },
|
|
469
|
+
{ parent: 'Parent2', child: 'Child2' }
|
|
470
|
+
];
|
|
471
|
+
|
|
472
|
+
await elementService.deleteEdges('TestDim', 'TestHierarchy', edges, false);
|
|
473
|
+
|
|
474
|
+
expect(mockRestService.delete).toHaveBeenCalledTimes(2);
|
|
475
|
+
expect(mockRestService.delete).toHaveBeenNthCalledWith(1,
|
|
476
|
+
"/Dimensions('TestDim')/Hierarchies('TestHierarchy')/Elements('Parent1')/Components('Child1')"
|
|
477
|
+
);
|
|
478
|
+
expect(mockRestService.delete).toHaveBeenNthCalledWith(2,
|
|
479
|
+
"/Dimensions('TestDim')/Hierarchies('TestHierarchy')/Elements('Parent2')/Components('Child2')"
|
|
480
|
+
);
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
test('should delete edges using TI', async () => {
|
|
484
|
+
mockRestService.post.mockResolvedValue(mockResponse({}));
|
|
485
|
+
|
|
486
|
+
const edges = [
|
|
487
|
+
{ parent: 'Parent1', child: 'Child1' },
|
|
488
|
+
{ parent: 'Parent2', child: 'Child2' }
|
|
489
|
+
];
|
|
490
|
+
|
|
491
|
+
await elementService.deleteEdges('TestDim', 'TestHierarchy', edges, true);
|
|
492
|
+
|
|
493
|
+
expect(mockRestService.post).toHaveBeenCalledWith(
|
|
494
|
+
"/ExecuteProcessWithReturn",
|
|
495
|
+
expect.objectContaining({
|
|
496
|
+
Name: expect.stringContaining('tm1npm_delete_edges_'),
|
|
497
|
+
PrologProcedure: expect.stringContaining("HierarchyElementComponentDelete('TestDim','TestHierarchy','Parent1','Child1');")
|
|
498
|
+
})
|
|
499
|
+
);
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
test('should get edges under consolidation', async () => {
|
|
503
|
+
const componentsData = {
|
|
504
|
+
value: [
|
|
505
|
+
{ Name: 'Child1', Weight: 1.5 },
|
|
506
|
+
{ Name: 'Child2', Weight: 2.0 },
|
|
507
|
+
{ Name: 'Child3' } // No weight
|
|
508
|
+
]
|
|
509
|
+
};
|
|
510
|
+
mockRestService.get.mockResolvedValue(mockResponse(componentsData));
|
|
511
|
+
|
|
512
|
+
const result = await elementService.getEdgesUnderConsolidation('TestDim', 'TestHierarchy', 'Parent1');
|
|
513
|
+
|
|
514
|
+
expect(result).toEqual([
|
|
515
|
+
{ parent: 'Parent1', child: 'Child1', weight: 1.5 },
|
|
516
|
+
{ parent: 'Parent1', child: 'Child2', weight: 2.0 },
|
|
517
|
+
{ parent: 'Parent1', child: 'Child3', weight: 1 }
|
|
518
|
+
]);
|
|
519
|
+
});
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
describe('Element Bulk Operations', () => {
|
|
523
|
+
test('should add multiple elements', async () => {
|
|
524
|
+
mockRestService.post.mockResolvedValue(mockResponse({}));
|
|
525
|
+
|
|
526
|
+
const elements = [mockElement, mockElement, mockElement];
|
|
527
|
+
|
|
528
|
+
await elementService.addElements('TestDim', 'TestHierarchy', elements);
|
|
529
|
+
|
|
530
|
+
expect(mockRestService.post).toHaveBeenCalledTimes(3);
|
|
531
|
+
});
|
|
532
|
+
|
|
533
|
+
test('should delete multiple elements using REST API', async () => {
|
|
534
|
+
mockRestService.delete.mockResolvedValue(mockResponse({}));
|
|
535
|
+
|
|
536
|
+
const elementNames = ['Element1', 'Element2', 'Element3'];
|
|
537
|
+
|
|
538
|
+
await elementService.deleteElements('TestDim', 'TestHierarchy', elementNames, false);
|
|
539
|
+
|
|
540
|
+
expect(mockRestService.delete).toHaveBeenCalledTimes(3);
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
test('should delete multiple elements using TI', async () => {
|
|
544
|
+
mockRestService.post.mockResolvedValue(mockResponse({}));
|
|
545
|
+
|
|
546
|
+
const elementNames = ['Element1', 'Element2', 'Element3'];
|
|
547
|
+
|
|
548
|
+
await elementService.deleteElements('TestDim', 'TestHierarchy', elementNames, true);
|
|
549
|
+
|
|
550
|
+
expect(mockRestService.post).toHaveBeenCalledWith(
|
|
551
|
+
"/ExecuteProcessWithReturn",
|
|
552
|
+
expect.objectContaining({
|
|
553
|
+
Name: expect.stringContaining('tm1npm_delete_elements_'),
|
|
554
|
+
PrologProcedure: expect.stringContaining("HierarchyElementDelete('TestDim','TestHierarchy','Element1');")
|
|
555
|
+
})
|
|
556
|
+
);
|
|
557
|
+
});
|
|
558
|
+
|
|
559
|
+
test('should handle empty element list for deletion', async () => {
|
|
560
|
+
await elementService.deleteElements('TestDim', 'TestHierarchy', [], true);
|
|
561
|
+
|
|
562
|
+
expect(mockRestService.post).not.toHaveBeenCalled();
|
|
563
|
+
});
|
|
564
|
+
});
|
|
565
|
+
|
|
566
|
+
describe('Element Attribute Operations', () => {
|
|
567
|
+
test('should get element attributes', async () => {
|
|
568
|
+
const attributesData = {
|
|
569
|
+
value: [
|
|
570
|
+
{ Name: 'Attribute1', Type: 'String' },
|
|
571
|
+
{ Name: 'Attribute2', Type: 'Numeric' }
|
|
572
|
+
]
|
|
573
|
+
};
|
|
574
|
+
mockRestService.get.mockResolvedValue(mockResponse(attributesData));
|
|
575
|
+
|
|
576
|
+
const result = await elementService.getElementAttributes('TestDim', 'TestHierarchy');
|
|
577
|
+
|
|
578
|
+
expect(result).toHaveLength(2);
|
|
579
|
+
expect(ElementAttribute.fromDict).toHaveBeenCalledTimes(2);
|
|
580
|
+
expect(mockRestService.get).toHaveBeenCalledWith(
|
|
581
|
+
"/Dimensions('TestDim')/Hierarchies('TestHierarchy')/ElementAttributes"
|
|
582
|
+
);
|
|
583
|
+
});
|
|
584
|
+
|
|
585
|
+
test('should get element attribute names', async () => {
|
|
586
|
+
const attributeNamesData = {
|
|
587
|
+
value: [
|
|
588
|
+
{ Name: 'Attribute1' },
|
|
589
|
+
{ Name: 'Attribute2' }
|
|
590
|
+
]
|
|
591
|
+
};
|
|
592
|
+
mockRestService.get.mockResolvedValue(mockResponse(attributeNamesData));
|
|
593
|
+
|
|
594
|
+
const result = await elementService.getElementAttributeNames('TestDim', 'TestHierarchy');
|
|
595
|
+
|
|
596
|
+
expect(result).toEqual(['Attribute1', 'Attribute2']);
|
|
597
|
+
expect(mockRestService.get).toHaveBeenCalledWith(
|
|
598
|
+
"/Dimensions('TestDim')/Hierarchies('TestHierarchy')/ElementAttributes?$select=Name"
|
|
599
|
+
);
|
|
600
|
+
});
|
|
601
|
+
|
|
602
|
+
test('should create element attribute', async () => {
|
|
603
|
+
mockRestService.post.mockResolvedValue(mockResponse({}));
|
|
604
|
+
|
|
605
|
+
const result = await elementService.createElementAttribute('TestDim', 'TestHierarchy', mockElementAttribute);
|
|
606
|
+
|
|
607
|
+
expect(result).toBeDefined();
|
|
608
|
+
expect(mockRestService.post).toHaveBeenCalledWith(
|
|
609
|
+
"/Dimensions('TestDim')/Hierarchies('TestHierarchy')/ElementAttributes",
|
|
610
|
+
mockElementAttribute.body
|
|
611
|
+
);
|
|
612
|
+
});
|
|
613
|
+
|
|
614
|
+
test('should delete element attribute', async () => {
|
|
615
|
+
mockRestService.delete.mockResolvedValue(mockResponse({}));
|
|
616
|
+
|
|
617
|
+
const result = await elementService.deleteElementAttribute('TestDim', 'TestHierarchy', 'TestAttribute');
|
|
618
|
+
|
|
619
|
+
expect(result).toBeDefined();
|
|
620
|
+
expect(mockRestService.delete).toHaveBeenCalledWith(
|
|
621
|
+
"/Dimensions('TestDim')/Hierarchies('TestHierarchy')/ElementAttributes('TestAttribute')"
|
|
622
|
+
);
|
|
623
|
+
});
|
|
624
|
+
|
|
625
|
+
test('should update element attribute value', async () => {
|
|
626
|
+
mockRestService.patch.mockResolvedValue(mockResponse({}));
|
|
627
|
+
|
|
628
|
+
const result = await elementService.updateElementAttribute(
|
|
629
|
+
'TestDim', 'TestHierarchy', 'TestElement', 'TestAttribute', 'NewValue'
|
|
630
|
+
);
|
|
631
|
+
|
|
632
|
+
expect(result).toBeDefined();
|
|
633
|
+
expect(mockRestService.patch).toHaveBeenCalledWith(
|
|
634
|
+
"/Dimensions('TestDim')/Hierarchies('TestHierarchy')/Elements('TestElement')/Attributes('TestAttribute')",
|
|
635
|
+
{ Value: 'NewValue' }
|
|
636
|
+
);
|
|
637
|
+
});
|
|
638
|
+
|
|
639
|
+
test('should get attribute values for multiple elements', async () => {
|
|
640
|
+
mockRestService.get
|
|
641
|
+
.mockResolvedValueOnce(mockResponse({ Value: 'Value1' })) // Element1 attribute
|
|
642
|
+
.mockResolvedValueOnce(mockResponse({ Value: 'Value2' })) // Element2 attribute
|
|
643
|
+
.mockRejectedValueOnce(new Error('Not found')); // Element3 attribute missing
|
|
644
|
+
|
|
645
|
+
const result = await elementService.getAttributeOfElements(
|
|
646
|
+
'TestDim', 'TestHierarchy', ['Element1', 'Element2', 'Element3'], 'TestAttribute'
|
|
647
|
+
);
|
|
648
|
+
|
|
649
|
+
expect(result).toEqual({
|
|
650
|
+
Element1: 'Value1',
|
|
651
|
+
Element2: 'Value2',
|
|
652
|
+
Element3: null
|
|
653
|
+
});
|
|
654
|
+
expect(mockRestService.get).toHaveBeenCalledTimes(3);
|
|
655
|
+
});
|
|
656
|
+
|
|
657
|
+
test('should get alias element attributes', async () => {
|
|
658
|
+
const aliasAttributesData = {
|
|
659
|
+
value: [
|
|
660
|
+
{ Name: 'Caption', Type: 'Alias' },
|
|
661
|
+
{ Name: 'DisplayName', Type: 'Alias' }
|
|
662
|
+
]
|
|
663
|
+
};
|
|
664
|
+
mockRestService.get.mockResolvedValue(mockResponse(aliasAttributesData));
|
|
665
|
+
|
|
666
|
+
const result = await elementService.getAliasElementAttributes('TestDim', 'TestHierarchy');
|
|
667
|
+
|
|
668
|
+
expect(result).toHaveLength(2);
|
|
669
|
+
expect(mockRestService.get).toHaveBeenCalledWith(
|
|
670
|
+
"/Dimensions('TestDim')/Hierarchies('TestHierarchy')/ElementAttributes?$filter=Type eq 'Alias'"
|
|
671
|
+
);
|
|
672
|
+
});
|
|
673
|
+
});
|
|
674
|
+
|
|
675
|
+
describe('MDX and Query Operations', () => {
|
|
676
|
+
test('should execute set MDX', async () => {
|
|
677
|
+
const mdxResult = {
|
|
678
|
+
value: ['Element1', 'Element2', 'Element3']
|
|
679
|
+
};
|
|
680
|
+
mockRestService.post.mockResolvedValue(mockResponse(mdxResult));
|
|
681
|
+
|
|
682
|
+
const mdx = "{[TestDim].[TestHierarchy].[All]}";
|
|
683
|
+
const result = await elementService.executeSetMdx('TestDim', 'TestHierarchy', mdx);
|
|
684
|
+
|
|
685
|
+
expect(result).toEqual(['Element1', 'Element2', 'Element3']);
|
|
686
|
+
expect(mockRestService.post).toHaveBeenCalledWith(
|
|
687
|
+
'/ExecuteMDXSetExpression',
|
|
688
|
+
{
|
|
689
|
+
MDX: mdx,
|
|
690
|
+
Dimension: 'TestDim',
|
|
691
|
+
Hierarchy: 'TestHierarchy'
|
|
692
|
+
}
|
|
693
|
+
);
|
|
694
|
+
});
|
|
695
|
+
|
|
696
|
+
test('should execute set MDX for element names', async () => {
|
|
697
|
+
const mdxResult = {
|
|
698
|
+
value: ['Element1', 'Element2']
|
|
699
|
+
};
|
|
700
|
+
mockRestService.post.mockResolvedValue(mockResponse(mdxResult));
|
|
701
|
+
|
|
702
|
+
const mdx = "DESCENDANTS({[TestDim].[TestHierarchy].[Total]}, 1, LEAVES)";
|
|
703
|
+
const result = await elementService.executeSetMdxElementNames('TestDim', 'TestHierarchy', mdx);
|
|
704
|
+
|
|
705
|
+
expect(result).toEqual(['Element1', 'Element2']);
|
|
706
|
+
});
|
|
707
|
+
|
|
708
|
+
test('should handle empty MDX results', async () => {
|
|
709
|
+
mockRestService.post.mockResolvedValue(mockResponse({}));
|
|
710
|
+
|
|
711
|
+
const result = await elementService.executeSetMdx('TestDim', 'TestHierarchy', '{[Empty]}');
|
|
712
|
+
|
|
713
|
+
expect(result).toEqual([]);
|
|
714
|
+
});
|
|
715
|
+
});
|
|
716
|
+
|
|
717
|
+
describe('Advanced Element Operations', () => {
|
|
718
|
+
test('should get leaf elements', async () => {
|
|
719
|
+
jest.spyOn(elementService, 'getElements').mockResolvedValue([mockElement]);
|
|
720
|
+
|
|
721
|
+
const result = await elementService.getLeafElements('TestDim', 'TestHierarchy');
|
|
722
|
+
|
|
723
|
+
expect(result).toEqual([mockElement]);
|
|
724
|
+
expect(elementService.getElements).toHaveBeenCalledWith('TestDim', 'TestHierarchy', true);
|
|
725
|
+
});
|
|
726
|
+
|
|
727
|
+
test('should get consolidated elements', async () => {
|
|
728
|
+
const consolidatedData = {
|
|
729
|
+
value: [
|
|
730
|
+
{ Name: 'Consol1', Type: 'Consolidated' },
|
|
731
|
+
{ Name: 'Consol2', Type: 'Consolidated' }
|
|
732
|
+
]
|
|
733
|
+
};
|
|
734
|
+
mockRestService.get.mockResolvedValue(mockResponse(consolidatedData));
|
|
735
|
+
|
|
736
|
+
const result = await elementService.getConsolidatedElements('TestDim', 'TestHierarchy');
|
|
737
|
+
|
|
738
|
+
expect(result).toHaveLength(2);
|
|
739
|
+
expect(mockRestService.get).toHaveBeenCalledWith(
|
|
740
|
+
"/Dimensions('TestDim')/Hierarchies('TestHierarchy')/Elements?$expand=*&$filter=Type eq 'Consolidated'"
|
|
741
|
+
);
|
|
742
|
+
});
|
|
743
|
+
|
|
744
|
+
test('should get elements dataframe', async () => {
|
|
745
|
+
const elementsData = {
|
|
746
|
+
value: [
|
|
747
|
+
{ Name: 'Element1', Type: 'Numeric', Attributes: { Caption: 'Elem1' } },
|
|
748
|
+
{ Name: 'Element2', Type: 'String', Attributes: { Caption: 'Elem2' } }
|
|
749
|
+
]
|
|
750
|
+
};
|
|
751
|
+
mockRestService.get.mockResolvedValue(mockResponse(elementsData));
|
|
752
|
+
|
|
753
|
+
const result = await elementService.getElementsDataframe('TestDim', 'TestHierarchy', ['Caption']);
|
|
754
|
+
|
|
755
|
+
expect(result).toEqual([
|
|
756
|
+
['Name', 'Type', 'Caption'],
|
|
757
|
+
['Element1', 'Numeric', 'Elem1'],
|
|
758
|
+
['Element2', 'String', 'Elem2']
|
|
759
|
+
]);
|
|
760
|
+
});
|
|
761
|
+
|
|
762
|
+
test('should get elements dataframe without attributes', async () => {
|
|
763
|
+
const elementsData = {
|
|
764
|
+
value: [
|
|
765
|
+
{ Name: 'Element1', Type: 'Numeric' },
|
|
766
|
+
{ Name: 'Element2', Type: 'String' }
|
|
767
|
+
]
|
|
768
|
+
};
|
|
769
|
+
mockRestService.get.mockResolvedValue(mockResponse(elementsData));
|
|
770
|
+
|
|
771
|
+
const result = await elementService.getElementsDataframe('TestDim', 'TestHierarchy');
|
|
772
|
+
|
|
773
|
+
expect(result).toEqual([
|
|
774
|
+
['Name', 'Type'],
|
|
775
|
+
['Element1', 'Numeric'],
|
|
776
|
+
['Element2', 'String']
|
|
777
|
+
]);
|
|
778
|
+
});
|
|
779
|
+
|
|
780
|
+
test('should create hierarchy from dataframe', async () => {
|
|
781
|
+
const dataFrame = [
|
|
782
|
+
['Name', 'Type', 'Parent1', 'Weight1'],
|
|
783
|
+
['Child1', 'Numeric', 'Parent1', '1.0'],
|
|
784
|
+
['Child2', 'String', 'Parent1', '2.0'],
|
|
785
|
+
['Parent1', 'Consolidated', '', '']
|
|
786
|
+
];
|
|
787
|
+
|
|
788
|
+
jest.spyOn(elementService, 'create').mockResolvedValue(mockResponse({}));
|
|
789
|
+
jest.spyOn(elementService, 'addEdges').mockResolvedValue();
|
|
790
|
+
|
|
791
|
+
await elementService.createHierarchyFromDataframe('TestDim', 'TestHierarchy', dataFrame);
|
|
792
|
+
|
|
793
|
+
expect(elementService.create).toHaveBeenCalledTimes(3);
|
|
794
|
+
expect(elementService.addEdges).toHaveBeenCalledWith('TestDim', 'TestHierarchy', [
|
|
795
|
+
{ parent: 'Parent1', child: 'Child1', weight: 1.0 },
|
|
796
|
+
{ parent: 'Parent1', child: 'Child2', weight: 2.0 }
|
|
797
|
+
]);
|
|
798
|
+
});
|
|
799
|
+
|
|
800
|
+
test('should throw error for invalid dataframe structure', async () => {
|
|
801
|
+
const invalidDataFrame = [
|
|
802
|
+
['InvalidHeader1', 'InvalidHeader2'],
|
|
803
|
+
['Data1', 'Data2']
|
|
804
|
+
];
|
|
805
|
+
|
|
806
|
+
await expect(elementService.createHierarchyFromDataframe('TestDim', 'TestHierarchy', invalidDataFrame))
|
|
807
|
+
.rejects.toThrow('DataFrame must contain Name and Type columns');
|
|
808
|
+
});
|
|
809
|
+
});
|
|
810
|
+
|
|
811
|
+
describe('Advanced Features and Parity Functions', () => {
|
|
812
|
+
test('should delete elements using TI method', async () => {
|
|
813
|
+
mockRestService.post
|
|
814
|
+
.mockResolvedValueOnce(mockResponse({})) // Create process
|
|
815
|
+
.mockResolvedValueOnce(mockResponse({})) // Execute process
|
|
816
|
+
.mockResolvedValueOnce(mockResponse({})); // Delete process
|
|
817
|
+
|
|
818
|
+
mockRestService.delete.mockResolvedValue(mockResponse({}));
|
|
819
|
+
|
|
820
|
+
await elementService.deleteElementsUseTi('TestDim', 'TestHierarchy', ['Element1', 'Element2']);
|
|
821
|
+
|
|
822
|
+
expect(mockRestService.post).toHaveBeenCalledTimes(2); // Create and execute
|
|
823
|
+
expect(mockRestService.delete).toHaveBeenCalledTimes(1); // Delete process
|
|
824
|
+
});
|
|
825
|
+
|
|
826
|
+
test('should handle empty element list in TI deletion', async () => {
|
|
827
|
+
await elementService.deleteElementsUseTi('TestDim', 'TestHierarchy', []);
|
|
828
|
+
|
|
829
|
+
expect(mockRestService.post).not.toHaveBeenCalled();
|
|
830
|
+
});
|
|
831
|
+
|
|
832
|
+
test('should delete edges using blob method', async () => {
|
|
833
|
+
mockRestService.post
|
|
834
|
+
.mockResolvedValueOnce(mockResponse({})) // Create process
|
|
835
|
+
.mockResolvedValueOnce(mockResponse({})); // Execute process
|
|
836
|
+
|
|
837
|
+
mockRestService.delete.mockResolvedValue(mockResponse({}));
|
|
838
|
+
|
|
839
|
+
const edges = [
|
|
840
|
+
{ parent: 'Parent1', child: 'Child1' },
|
|
841
|
+
{ parent: 'Parent2', child: 'Child2' }
|
|
842
|
+
];
|
|
843
|
+
|
|
844
|
+
await elementService.deleteEdgesUseBlob('TestDim', 'TestHierarchy', edges);
|
|
845
|
+
|
|
846
|
+
expect(mockRestService.post).toHaveBeenCalledTimes(2);
|
|
847
|
+
expect(mockRestService.delete).toHaveBeenCalledTimes(1);
|
|
848
|
+
});
|
|
849
|
+
|
|
850
|
+
test('should get elements by level', async () => {
|
|
851
|
+
const allElements = [
|
|
852
|
+
{ name: 'Leaf1', elementType: ElementType.NUMERIC },
|
|
853
|
+
{ name: 'Consol1', elementType: ElementType.CONSOLIDATED }
|
|
854
|
+
];
|
|
855
|
+
jest.spyOn(elementService, 'getElements').mockResolvedValue(allElements as any);
|
|
856
|
+
|
|
857
|
+
const result = await elementService.getElementsByLevel('TestDim', 'TestHierarchy', 1);
|
|
858
|
+
|
|
859
|
+
expect(result).toHaveLength(1);
|
|
860
|
+
expect(result[0].name).toBe('Consol1');
|
|
861
|
+
});
|
|
862
|
+
|
|
863
|
+
test('should get elements filtered by wildcard', async () => {
|
|
864
|
+
const filteredData = {
|
|
865
|
+
value: [
|
|
866
|
+
{ Name: 'TestElement1', Type: 'Numeric' },
|
|
867
|
+
{ Name: 'TestElement2', Type: 'String' }
|
|
868
|
+
]
|
|
869
|
+
};
|
|
870
|
+
mockRestService.get.mockResolvedValue(mockResponse(filteredData));
|
|
871
|
+
|
|
872
|
+
const result = await elementService.getElementsFilteredByWildcard('TestDim', 'TestHierarchy', 'Test*');
|
|
873
|
+
|
|
874
|
+
expect(result).toHaveLength(2);
|
|
875
|
+
expect(mockRestService.get).toHaveBeenCalledWith(
|
|
876
|
+
expect.stringContaining("substringof('Test%',Name)")
|
|
877
|
+
);
|
|
878
|
+
});
|
|
879
|
+
|
|
880
|
+
test('should get elements filtered by attribute', async () => {
|
|
881
|
+
const allElements = [
|
|
882
|
+
{ name: 'Element1', elementType: ElementType.NUMERIC },
|
|
883
|
+
{ name: 'Element2', elementType: ElementType.STRING }
|
|
884
|
+
];
|
|
885
|
+
jest.spyOn(elementService, 'getElements').mockResolvedValue(allElements as any);
|
|
886
|
+
|
|
887
|
+
mockRestService.get
|
|
888
|
+
.mockResolvedValueOnce(mockResponse({ Value: 'MatchingValue' })) // Element1 matches
|
|
889
|
+
.mockRejectedValueOnce(new Error('Not found')); // Element2 has no attribute
|
|
890
|
+
|
|
891
|
+
const result = await elementService.getElementsFilteredByAttribute(
|
|
892
|
+
'TestDim', 'TestHierarchy', 'TestAttribute', 'MatchingValue'
|
|
893
|
+
);
|
|
894
|
+
|
|
895
|
+
expect(result).toHaveLength(1);
|
|
896
|
+
expect(result[0].name).toBe('Element1');
|
|
897
|
+
});
|
|
898
|
+
|
|
899
|
+
test('should lock element', async () => {
|
|
900
|
+
mockRestService.post.mockResolvedValue(mockResponse({}));
|
|
901
|
+
|
|
902
|
+
await elementService.elementLock('TestDim', 'TestHierarchy', 'TestElement');
|
|
903
|
+
|
|
904
|
+
expect(mockRestService.post).toHaveBeenCalledWith(
|
|
905
|
+
"/Dimensions('TestDim')/Hierarchies('TestHierarchy')/Elements('TestElement')/tm1.Lock",
|
|
906
|
+
{}
|
|
907
|
+
);
|
|
908
|
+
});
|
|
909
|
+
|
|
910
|
+
test('should unlock element', async () => {
|
|
911
|
+
mockRestService.post.mockResolvedValue(mockResponse({}));
|
|
912
|
+
|
|
913
|
+
await elementService.elementUnlock('TestDim', 'TestHierarchy', 'TestElement');
|
|
914
|
+
|
|
915
|
+
expect(mockRestService.post).toHaveBeenCalledWith(
|
|
916
|
+
"/Dimensions('TestDim')/Hierarchies('TestHierarchy')/Elements('TestElement')/tm1.Unlock",
|
|
917
|
+
{}
|
|
918
|
+
);
|
|
919
|
+
});
|
|
920
|
+
|
|
921
|
+
test('should get levels count', async () => {
|
|
922
|
+
const elements = [
|
|
923
|
+
{ name: 'Leaf1', elementType: ElementType.NUMERIC },
|
|
924
|
+
{ name: 'Consol1', elementType: ElementType.CONSOLIDATED },
|
|
925
|
+
{ name: 'Leaf2', elementType: ElementType.STRING }
|
|
926
|
+
];
|
|
927
|
+
jest.spyOn(elementService, 'getElements').mockResolvedValue(elements as any);
|
|
928
|
+
|
|
929
|
+
const result = await elementService.getLevelsCount('TestDim', 'TestHierarchy');
|
|
930
|
+
|
|
931
|
+
expect(result).toBe(2); // 1 consolidated level + 1 leaf level
|
|
932
|
+
});
|
|
933
|
+
|
|
934
|
+
test('should get level names', async () => {
|
|
935
|
+
jest.spyOn(elementService, 'getLevelsCount').mockResolvedValue(3);
|
|
936
|
+
|
|
937
|
+
const result = await elementService.getLevelNames('TestDim', 'TestHierarchy');
|
|
938
|
+
|
|
939
|
+
expect(result).toEqual(['Level 0', 'Level 1', 'Level 2']);
|
|
940
|
+
});
|
|
941
|
+
|
|
942
|
+
test('should get leaves under consolidation using MDX', async () => {
|
|
943
|
+
jest.spyOn(elementService, 'executeSetMdxElementNames').mockResolvedValue(['Leaf1', 'Leaf2']);
|
|
944
|
+
jest.spyOn(elementService, 'get')
|
|
945
|
+
.mockResolvedValueOnce(mockElement)
|
|
946
|
+
.mockResolvedValueOnce(mockElement);
|
|
947
|
+
|
|
948
|
+
const result = await elementService.getLeavesUnderConsolidation('TestDim', 'TestHierarchy', 'Consol1');
|
|
949
|
+
|
|
950
|
+
expect(result).toHaveLength(2);
|
|
951
|
+
expect(elementService.executeSetMdxElementNames).toHaveBeenCalledWith(
|
|
952
|
+
'TestDim', 'TestHierarchy',
|
|
953
|
+
expect.stringContaining('FILTER(DESCENDANTS')
|
|
954
|
+
);
|
|
955
|
+
});
|
|
956
|
+
|
|
957
|
+
test('should get members under consolidation using MDX', async () => {
|
|
958
|
+
jest.spyOn(elementService, 'executeSetMdxElementNames').mockResolvedValue(['Member1', 'Member2']);
|
|
959
|
+
jest.spyOn(elementService, 'get')
|
|
960
|
+
.mockResolvedValueOnce(mockElement)
|
|
961
|
+
.mockResolvedValueOnce(mockElement);
|
|
962
|
+
|
|
963
|
+
const result = await elementService.getMembersUnderConsolidation('TestDim', 'TestHierarchy', 'Consol1');
|
|
964
|
+
|
|
965
|
+
expect(result).toHaveLength(2);
|
|
966
|
+
expect(elementService.executeSetMdxElementNames).toHaveBeenCalledWith(
|
|
967
|
+
'TestDim', 'TestHierarchy',
|
|
968
|
+
expect.stringContaining('DESCENDANTS')
|
|
969
|
+
);
|
|
970
|
+
});
|
|
971
|
+
|
|
972
|
+
test('should get all element identifiers', async () => {
|
|
973
|
+
jest.spyOn(elementService, 'getNames').mockResolvedValue(['Element1', 'Element2', 'Element3']);
|
|
974
|
+
|
|
975
|
+
const result = await elementService.getAllElementIdentifiers('TestDim', 'TestHierarchy');
|
|
976
|
+
|
|
977
|
+
expect(result).toEqual([
|
|
978
|
+
'[TestDim].[TestHierarchy].[Element1]',
|
|
979
|
+
'[TestDim].[TestHierarchy].[Element2]',
|
|
980
|
+
'[TestDim].[TestHierarchy].[Element3]'
|
|
981
|
+
]);
|
|
982
|
+
});
|
|
983
|
+
|
|
984
|
+
test('should get element identifiers with filter pattern', async () => {
|
|
985
|
+
const filteredElements = [
|
|
986
|
+
{ name: 'FilteredElement1', elementType: ElementType.NUMERIC },
|
|
987
|
+
{ name: 'FilteredElement2', elementType: ElementType.STRING }
|
|
988
|
+
];
|
|
989
|
+
jest.spyOn(elementService, 'getElementsFilteredByWildcard').mockResolvedValue(filteredElements as any);
|
|
990
|
+
|
|
991
|
+
const result = await elementService.getElementIdentifiers('TestDim', 'TestHierarchy', 'Filtered*');
|
|
992
|
+
|
|
993
|
+
expect(result).toEqual([
|
|
994
|
+
'[TestDim].[TestHierarchy].[FilteredElement1]',
|
|
995
|
+
'[TestDim].[TestHierarchy].[FilteredElement2]'
|
|
996
|
+
]);
|
|
997
|
+
});
|
|
998
|
+
|
|
999
|
+
test('should get element identifiers without filter', async () => {
|
|
1000
|
+
jest.spyOn(elementService, 'getNames').mockResolvedValue(['Element1', 'Element2']);
|
|
1001
|
+
|
|
1002
|
+
const result = await elementService.getElementIdentifiers('TestDim', 'TestHierarchy');
|
|
1003
|
+
|
|
1004
|
+
expect(result).toEqual([
|
|
1005
|
+
'[TestDim].[TestHierarchy].[Element1]',
|
|
1006
|
+
'[TestDim].[TestHierarchy].[Element2]'
|
|
1007
|
+
]);
|
|
1008
|
+
});
|
|
1009
|
+
});
|
|
1010
|
+
|
|
1011
|
+
describe('MDXDrillMethod Enum', () => {
|
|
1012
|
+
test('should export all drill methods correctly', () => {
|
|
1013
|
+
expect(MDXDrillMethod.TM1DRILLDOWNMEMBER).toBe(1);
|
|
1014
|
+
expect(MDXDrillMethod.DESCENDANTS).toBe(2);
|
|
1015
|
+
});
|
|
1016
|
+
});
|
|
1017
|
+
|
|
1018
|
+
describe('Error Handling', () => {
|
|
1019
|
+
test('should handle element retrieval errors', async () => {
|
|
1020
|
+
const error = new Error('Element not found');
|
|
1021
|
+
mockRestService.get.mockRejectedValue(error);
|
|
1022
|
+
|
|
1023
|
+
await expect(elementService.get('TestDim', 'TestHierarchy', 'NonExistent'))
|
|
1024
|
+
.rejects.toThrow('Element not found');
|
|
1025
|
+
});
|
|
1026
|
+
|
|
1027
|
+
test('should handle element creation errors', async () => {
|
|
1028
|
+
const error = new Error('Element creation failed');
|
|
1029
|
+
mockRestService.post.mockRejectedValue(error);
|
|
1030
|
+
|
|
1031
|
+
await expect(elementService.create('TestDim', 'TestHierarchy', mockElement))
|
|
1032
|
+
.rejects.toThrow('Element creation failed');
|
|
1033
|
+
});
|
|
1034
|
+
|
|
1035
|
+
test('should handle MDX execution errors', async () => {
|
|
1036
|
+
const error = new Error('MDX execution failed');
|
|
1037
|
+
mockRestService.post.mockRejectedValue(error);
|
|
1038
|
+
|
|
1039
|
+
await expect(elementService.executeSetMdx('TestDim', 'TestHierarchy', 'INVALID MDX'))
|
|
1040
|
+
.rejects.toThrow('MDX execution failed');
|
|
1041
|
+
});
|
|
1042
|
+
|
|
1043
|
+
test('should handle attribute operation errors', async () => {
|
|
1044
|
+
const error = new Error('Attribute operation failed');
|
|
1045
|
+
mockRestService.get.mockRejectedValue(error);
|
|
1046
|
+
|
|
1047
|
+
await expect(elementService.getElementAttributes('TestDim', 'TestHierarchy'))
|
|
1048
|
+
.rejects.toThrow('Attribute operation failed');
|
|
1049
|
+
});
|
|
1050
|
+
|
|
1051
|
+
test('should handle hierarchy traversal errors', async () => {
|
|
1052
|
+
const error = new Error('Hierarchy access failed');
|
|
1053
|
+
mockRestService.get.mockRejectedValue(error);
|
|
1054
|
+
|
|
1055
|
+
await expect(elementService.getParents('TestDim', 'TestHierarchy', 'Element'))
|
|
1056
|
+
.rejects.toThrow('Hierarchy access failed');
|
|
1057
|
+
});
|
|
1058
|
+
});
|
|
1059
|
+
|
|
1060
|
+
describe('Edge Cases and Special Scenarios', () => {
|
|
1061
|
+
test('should handle empty results gracefully', async () => {
|
|
1062
|
+
mockRestService.get.mockResolvedValue(mockResponse({ value: [] }));
|
|
1063
|
+
|
|
1064
|
+
const names = await elementService.getNames('TestDim', 'TestHierarchy');
|
|
1065
|
+
const elements = await elementService.getElements('TestDim', 'TestHierarchy');
|
|
1066
|
+
const parents = await elementService.getParents('TestDim', 'TestHierarchy', 'Element');
|
|
1067
|
+
const children = await elementService.getChildren('TestDim', 'TestHierarchy', 'Element');
|
|
1068
|
+
|
|
1069
|
+
expect(names).toEqual([]);
|
|
1070
|
+
expect(elements).toEqual([]);
|
|
1071
|
+
expect(parents).toEqual([]);
|
|
1072
|
+
expect(children).toEqual([]);
|
|
1073
|
+
});
|
|
1074
|
+
|
|
1075
|
+
test('should handle special characters in element names', async () => {
|
|
1076
|
+
const specialName = "Element's & \"Special\" Name";
|
|
1077
|
+
mockRestService.get.mockResolvedValue(mockResponse({ Name: specialName }));
|
|
1078
|
+
|
|
1079
|
+
await elementService.get('TestDim', 'TestHierarchy', specialName);
|
|
1080
|
+
|
|
1081
|
+
// The formatUrl method encodes special characters, so we expect the encoded version
|
|
1082
|
+
expect(mockRestService.get).toHaveBeenCalledWith(
|
|
1083
|
+
expect.stringContaining("/Elements('Element's%20%26%20%22Special%22%20Name')")
|
|
1084
|
+
);
|
|
1085
|
+
});
|
|
1086
|
+
|
|
1087
|
+
test('should handle null/undefined values in element types', async () => {
|
|
1088
|
+
const typesData = {
|
|
1089
|
+
value: [
|
|
1090
|
+
{ Name: 'Element1', Type: null },
|
|
1091
|
+
{ Name: 'Element2', Type: undefined },
|
|
1092
|
+
{ Name: 'Element3', Type: 'Numeric' }
|
|
1093
|
+
]
|
|
1094
|
+
};
|
|
1095
|
+
mockRestService.get.mockResolvedValue(mockResponse(typesData));
|
|
1096
|
+
|
|
1097
|
+
const result = await elementService.getElementTypes('TestDim', 'TestHierarchy');
|
|
1098
|
+
|
|
1099
|
+
expect(result).toBeInstanceOf(CaseAndSpaceInsensitiveDict);
|
|
1100
|
+
});
|
|
1101
|
+
|
|
1102
|
+
test('should handle large datasets efficiently', async () => {
|
|
1103
|
+
const largeDataset = Array.from({ length: 10000 }, (_, i) => ({
|
|
1104
|
+
Name: `Element${i}`,
|
|
1105
|
+
Type: i % 2 === 0 ? 'Numeric' : 'String'
|
|
1106
|
+
}));
|
|
1107
|
+
|
|
1108
|
+
mockRestService.get.mockResolvedValue(mockResponse({ value: largeDataset }));
|
|
1109
|
+
|
|
1110
|
+
const result = await elementService.getNames('TestDim', 'TestHierarchy');
|
|
1111
|
+
|
|
1112
|
+
expect(result).toHaveLength(10000);
|
|
1113
|
+
expect(result[0]).toBe('Element0');
|
|
1114
|
+
expect(result[9999]).toBe('Element9999');
|
|
1115
|
+
});
|
|
1116
|
+
|
|
1117
|
+
test('should handle missing attribute values gracefully', async () => {
|
|
1118
|
+
const elementsData = {
|
|
1119
|
+
value: [
|
|
1120
|
+
{ Name: 'Element1', Type: 'Numeric', Attributes: {} },
|
|
1121
|
+
{ Name: 'Element2', Type: 'String' } // No Attributes property
|
|
1122
|
+
]
|
|
1123
|
+
};
|
|
1124
|
+
mockRestService.get.mockResolvedValue(mockResponse(elementsData));
|
|
1125
|
+
|
|
1126
|
+
const result = await elementService.getElementsDataframe('TestDim', 'TestHierarchy', ['MissingAttribute']);
|
|
1127
|
+
|
|
1128
|
+
expect(result).toEqual([
|
|
1129
|
+
['Name', 'Type', 'MissingAttribute'],
|
|
1130
|
+
['Element1', 'Numeric', ''],
|
|
1131
|
+
['Element2', 'String'] // No attribute value added when no Attributes property exists
|
|
1132
|
+
]);
|
|
1133
|
+
});
|
|
1134
|
+
});
|
|
1135
|
+
|
|
1136
|
+
describe('Integration Patterns', () => {
|
|
1137
|
+
test('should support element lifecycle management', async () => {
|
|
1138
|
+
mockRestService.post.mockResolvedValue(mockResponse({}));
|
|
1139
|
+
mockRestService.patch.mockResolvedValue(mockResponse({}));
|
|
1140
|
+
mockRestService.delete.mockResolvedValue(mockResponse({}));
|
|
1141
|
+
jest.spyOn(elementService, 'exists').mockResolvedValue(true);
|
|
1142
|
+
|
|
1143
|
+
// Element lifecycle: create, add relationships, update, delete
|
|
1144
|
+
await elementService.create('TestDim', 'TestHierarchy', mockElement);
|
|
1145
|
+
await elementService.addEdges('TestDim', 'TestHierarchy', [
|
|
1146
|
+
{ parent: 'Parent1', child: 'TestElement', weight: 1.5 }
|
|
1147
|
+
]);
|
|
1148
|
+
await elementService.update('TestDim', 'TestHierarchy', mockElement);
|
|
1149
|
+
await elementService.delete('TestDim', 'TestHierarchy', 'TestElement');
|
|
1150
|
+
|
|
1151
|
+
expect(mockRestService.post).toHaveBeenCalledTimes(2); // create + add edge
|
|
1152
|
+
expect(mockRestService.patch).toHaveBeenCalledTimes(1);
|
|
1153
|
+
expect(mockRestService.delete).toHaveBeenCalledTimes(1);
|
|
1154
|
+
});
|
|
1155
|
+
|
|
1156
|
+
test('should support hierarchy building workflow', async () => {
|
|
1157
|
+
mockRestService.post.mockResolvedValue(mockResponse({}));
|
|
1158
|
+
jest.spyOn(elementService, 'create').mockResolvedValue(mockResponse({}));
|
|
1159
|
+
jest.spyOn(elementService, 'addEdges').mockResolvedValue();
|
|
1160
|
+
|
|
1161
|
+
const elements = [mockElement, mockElement, mockElement];
|
|
1162
|
+
const edges = [
|
|
1163
|
+
{ parent: 'Parent1', child: 'Child1', weight: 1 },
|
|
1164
|
+
{ parent: 'Parent1', child: 'Child2', weight: 2 }
|
|
1165
|
+
];
|
|
1166
|
+
|
|
1167
|
+
// Hierarchy building workflow
|
|
1168
|
+
await elementService.addElements('TestDim', 'TestHierarchy', elements);
|
|
1169
|
+
await elementService.addEdges('TestDim', 'TestHierarchy', edges);
|
|
1170
|
+
|
|
1171
|
+
expect(elementService.create).toHaveBeenCalledTimes(3);
|
|
1172
|
+
expect(elementService.addEdges).toHaveBeenCalledWith('TestDim', 'TestHierarchy', edges);
|
|
1173
|
+
});
|
|
1174
|
+
|
|
1175
|
+
test('should support attribute management workflow', async () => {
|
|
1176
|
+
mockRestService.post.mockResolvedValue(mockResponse({}));
|
|
1177
|
+
mockRestService.patch.mockResolvedValue(mockResponse({}));
|
|
1178
|
+
mockRestService.delete.mockResolvedValue(mockResponse({}));
|
|
1179
|
+
|
|
1180
|
+
// Attribute management workflow
|
|
1181
|
+
await elementService.createElementAttribute('TestDim', 'TestHierarchy', mockElementAttribute);
|
|
1182
|
+
await elementService.updateElementAttribute('TestDim', 'TestHierarchy', 'Element1', 'TestAttribute', 'Value1');
|
|
1183
|
+
await elementService.deleteElementAttribute('TestDim', 'TestHierarchy', 'TestAttribute');
|
|
1184
|
+
|
|
1185
|
+
expect(mockRestService.post).toHaveBeenCalledTimes(1);
|
|
1186
|
+
expect(mockRestService.patch).toHaveBeenCalledTimes(1);
|
|
1187
|
+
expect(mockRestService.delete).toHaveBeenCalledTimes(1);
|
|
1188
|
+
});
|
|
1189
|
+
});
|
|
1190
|
+
});
|