diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..96c2e0d3 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,15 @@ +# Needed for publishing of examples, build worker defaults to core.autocrlf=input. +* text eol=autocrlf + +*.mof text eol=crlf +*.sh text eol=lf +*.svg eol=lf + +# Ensure any exe files are treated as binary +*.exe binary +*.jpg binary +*.xl* binary +*.pfx binary +*.png binary +*.dll binary +*.so binary diff --git a/.github/ISSUE_TEMPLATE/General.md b/.github/ISSUE_TEMPLATE/General.md new file mode 100644 index 00000000..fbcdf240 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/General.md @@ -0,0 +1,7 @@ +--- +name: General question or documentation update +about: If you have a general question or documentation update suggestion around the resource module. +--- + diff --git a/.github/ISSUE_TEMPLATE/Problem_with_module.yml b/.github/ISSUE_TEMPLATE/Problem_with_module.yml new file mode 100644 index 00000000..8b8cce18 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/Problem_with_module.yml @@ -0,0 +1,102 @@ +name: Problem with the module +description: If you have a problem using this module, want to report a bug, or suggest an enhancement to this module. +labels: [] +assignees: [] +body: + - type: markdown + attributes: + value: | + TITLE: Please be descriptive not sensationalist. + + Your feedback and support is greatly appreciated, thanks for contributing! + + Please provide information regarding your issue under each section below. + **Write N/A in sections that do not apply, or if the information is not available.** + - type: textarea + id: description + attributes: + label: Problem description + description: Details of the scenario you tried and the problem that is occurring, or the enhancement you are suggesting. + validations: + required: true + - type: textarea + id: logs + attributes: + label: Verbose logs + description: | + Verbose logs showing the problem. **NOTE! Sensitive information should be obfuscated.** _Will be automatically formatted as plain text._ + placeholder: | + Paste verbose logs here + render: text + validations: + required: true + - type: textarea + id: reproducible + attributes: + label: How to reproduce + description: Provide the steps to reproduce the problem. + validations: + required: true + - type: textarea + id: expectedBehavior + attributes: + label: Expected behavior + description: Describe what you expected to happen. + validations: + required: true + - type: textarea + id: currentBehavior + attributes: + label: Current behavior + description: Describe what actually happens. + validations: + required: true + - type: textarea + id: suggestedSolution + attributes: + label: Suggested solution + description: Do you have any suggestions how to solve the issue? + validations: + required: true + - type: textarea + id: targetNodeOS + attributes: + label: Operating system you are running FabricTools on + description: | + Please provide as much as possible about the node running FabricTools. _Will be automatically formatted as plain text._ + + To help with this information: + - On a Linux distribution, please provide the distribution name, version, and release. The following command can help get this information: `cat /etc/*-release && cat /proc/version` + - On macOS, please provide the product version and build version. The following command can help get this information: `sw_vers` + - On a Windows OS please provide edition, version, build, and language. The following command can help get this information: `Get-ComputerInfo -Property @('OsName','OsOperatingSystemSKU','OSArchitecture','WindowsVersion','WindowsBuildLabEx','OsLanguage','OsMuiLanguages')` + placeholder: | + Add operating system information here + render: text + validations: + required: true + - type: textarea + id: targetNodePS + attributes: + label: PowerShell version and build the target node is running + description: | + Please provide the version and build of PowerShell the target node is running. _Will be automatically formatted as plain text._ + + To help with this information, please run this command: `$PSVersionTable` + placeholder: | + Add PowerShell information here + render: text + validations: + required: true + - type: textarea + id: moduleVersion + attributes: + label: Module version used + description: | + Please provide the version of the FabricTools module that was used. _Will be automatically formatted as plain text._ + + To help with this information, please run this command: `Get-Module -Name 'FabricTools' -ListAvailable | ft Name,Version,Path` + placeholder: | + Add module information here + render: text + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/Problem_with_resource.yml b/.github/ISSUE_TEMPLATE/Problem_with_resource.yml new file mode 100644 index 00000000..eaff08a5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/Problem_with_resource.yml @@ -0,0 +1,47 @@ +name: Problem with a function +description: If you have a problem, bug, or enhancement with a function in this module. +labels: [] +assignees: [] +body: + - type: markdown + attributes: + value: | + Please prefix the issue title (above) with the function name, e.g. 'FunctionName: Short description of my issue'! + + Your feedback and support is greatly appreciated, thanks for contributing! + - type: textarea + id: description + attributes: + label: Problem description + description: Details of the scenario you tried and the problem that is occurring. + validations: + required: true + - type: textarea + id: logs + attributes: + label: Verbose logs + description: | + Verbose logs showing the problem. **NOTE! Sensitive information should be obfuscated.** _Will be automatically formatted as plain text._ + placeholder: | + Paste verbose logs here + render: text + validations: + required: true + - type: textarea + id: version + attributes: + label: Module Version + description: | + The version of the FabricTools module you are using. _Will be automatically formatted as plain text._ + To help with this information, please run this command: `Get-Module -Name 'FabricTools' | ft Name,Version,Path` + placeholder: | + Paste output here + validations: + required: true + - type: textarea + id: suggestedSolution + attributes: + label: Suggested solution + description: Do you have any suggestions how to solve the issue? + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/Resource_proposal.yml b/.github/ISSUE_TEMPLATE/Resource_proposal.yml new file mode 100644 index 00000000..075099d9 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/Resource_proposal.yml @@ -0,0 +1,43 @@ +name: New function proposal +description: If you have a new function proposal that you think should be added to this module. +title: "NewFunctionName: New resource proposal" +labels: [] +assignees: [] +body: + - type: markdown + attributes: + value: | + Please replace `NewFunctionName` in the issue title (above) with your proposed function name. + + Thank you for contributing and making FabricTools better! + - type: textarea + id: description + attributes: + label: Function proposal + description: Provide information how this function will/should work and how it will help users. + validations: + required: true + - type: textarea + id: proposedProperties + attributes: + label: Proposed properties + description: | + List all the proposed properties and parameters that the function should have. + value: | + Parameter | Description | Data type | Default value | Allowed values + --- | --- | --- | --- | --- + ParameterName | Detailed description | String | None | Any + + Property | Description + --- | --- + PropertyName | Detailed description + validations: + required: true + - type: textarea + id: considerations + attributes: + label: Special considerations or limitations + description: | + Provide any considerations or limitations you can think of that a contributor should take in account when coding the proposed function, and or what limitations a user will encounter or should consider when using the proposed function. + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000..e0b172d8 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: "FabricTools Community" + url: https://github.com/dataplat/FabricTools/discussions + about: "To talk to the community and maintainers of FabricTools, please head to GitHub discussions." diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..4b839df3 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,63 @@ +# Pull Request + + + +## Pull Request (PR) description + + + +## Task list + + + +- [ ] The PR represents a single logical change. i.e. Cosmetic updates should go in different PRs. +- [ ] Added an entry under the Unreleased section of in the CHANGELOG.md as per [format](https://keepachangelog.com/en/1.0.0/). +- [ ] Local clean build passes without issue or fail tests (`build.ps1 -ResolveDependency`). +- [ ] Resource documentation added/updated in README.md. +- [ ] Resource parameter descriptions added/updated in README.md, schema.mof + and comment-based help. +- [ ] Comment-based help added/updated. +- [ ] Localization strings added/updated in all localization files as appropriate. +- [ ] Examples appropriately added/updated. +- [ ] Unit tests added/updated. See [DSC Resource Testing Guidelines](https://github.com/PowerShell/DscResources/blob/master/TestsGuidelines.md). +- [ ] Integration tests added/updated (where possible). See [DSC Resource Testing Guidelines](https://github.com/PowerShell/DscResources/blob/master/TestsGuidelines.md). +- [ ] New/changed code adheres to [DSC Resource Style Guidelines](https://github.com/PowerShell/DscResources/blob/master/StyleGuidelines.md) and [Best Practices](https://github.com/PowerShell/DscResources/blob/master/BestPractices.md). diff --git a/.github/copilot-commit-message-instructions.md b/.github/copilot-commit-message-instructions.md new file mode 100644 index 00000000..57e25aea --- /dev/null +++ b/.github/copilot-commit-message-instructions.md @@ -0,0 +1,8 @@ +# Limit the subject line to 50 characters +Capitalize the subject/description line +Do not end the subject line with a period +Separate the subject from the body with a blank line +Use the imperative mood in the subject line +The subject line should be a single sentence with an action word and target with some reasoning add "for Pester Help Tests" +Use the body to explain what and why in a friendly kind manner +Say thank you at the end of the message diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml new file mode 100644 index 00000000..e8a452a1 --- /dev/null +++ b/.github/workflows/pr.yml @@ -0,0 +1,174 @@ +on: + pull_request: + branches: + - main + - sampler # temporary branch for testing + - develop + paths-ignore: + - CHANGELOG.md +env: + buildFolderName: output + buildArtifactName: output + testResultFolderName: testResults +jobs: + Build_Stage_Package_Module: + name: Package Module + runs-on: ubuntu-latest + steps: + - name: Checkout Code + uses: actions/checkout@v3 + with: + ref: ${{github.event.pull_request.head.ref}} + repository: ${{github.event.pull_request.head.repo.full_name}} # checkout the correct branch name + fetch-depth: 0 + - name: Install GitVersion + uses: gittools/actions/gitversion/setup@v0.9.15 + with: + versionSpec: 5.x + - name: Evaluate Next Version + uses: gittools/actions/gitversion/execute@v0.9.15 + with: + configFilePath: GitVersion.yml + - name: Build & Package Module + shell: pwsh + run: ./build.ps1 -ResolveDependency -tasks pack -Verbose + env: + ModuleVersion: ${{ env.gitVersion.NuGetVersionV2 }} + - name: Publish Build Artifact + uses: actions/upload-artifact@v4 + with: + name: ${{ env.buildArtifactName }} + path: ${{ env.buildFolderName }}/ + Test_Stage_test_linux: + name: Linux + runs-on: ubuntu-latest + needs: + - Build_Stage_Package_Module + steps: + - name: Checkout Code + uses: actions/checkout@v3 + with: + ref: ${{github.event.pull_request.head.ref}} + repository: ${{github.event.pull_request.head.repo.full_name}} # checkout the correct branch name + fetch-depth: 0 + - name: Download Build Artifact + uses: actions/download-artifact@v4 + with: + name: ${{ env.buildArtifactName }} + path: ${{ env.buildFolderName }} + - name: Run Tests + shell: pwsh + run: ./build.ps1 -tasks noop ; ./build.ps1 -tasks test + - name: Publish Test Artifact + uses: actions/upload-artifact@v4 + with: + path: ${{ env.buildFolderName }}/${{ env.testResultFolderName }}/ + name: CodeCoverageLinux + if: success() || failure() + Test_Stage_test_windows_core: + name: Windows (PowerShell) + runs-on: windows-2019 + needs: + - Build_Stage_Package_Module + steps: + - name: Checkout Code + uses: actions/checkout@v3 + with: + ref: ${{github.event.pull_request.head.ref}} + repository: ${{github.event.pull_request.head.repo.full_name}} # checkout the correct branch name + fetch-depth: 0 + - name: Download Build Artifact + uses: actions/download-artifact@v4 + with: + name: ${{ env.buildArtifactName }} + path: ${{ env.buildFolderName }} + - name: Run Tests + shell: pwsh + run: ./build.ps1 -tasks noop; ./build.ps1 -tasks test + + - name: Publish Test Artifact + uses: actions/upload-artifact@v4 + with: + path: ${{ env.buildFolderName }}/${{ env.testResultFolderName }}/ + name: CodeCoverageWinPS7 + if: success() || failure() + Test_Stage_test_windows_ps: + name: Windows (Windows PowerShell) + runs-on: windows-2019 + needs: + - Build_Stage_Package_Module + steps: + - name: Checkout Code + uses: actions/checkout@v3 + with: + ref: ${{github.event.pull_request.head.ref}} + repository: ${{github.event.pull_request.head.repo.full_name}} # checkout the correct branch name + fetch-depth: 0 + - name: Download Build Artifact + uses: actions/download-artifact@v4 + with: + name: ${{ env.buildArtifactName }} + path: ${{ env.buildFolderName }} + - name: Run Tests + shell: pwsh + run: ./build.ps1 -ResolveDependency -tasks test + - name: Publish Test Artifact + uses: actions/upload-artifact@v4 + with: + path: ${{ env.buildFolderName }}/${{ env.testResultFolderName }}/ + name: CodeCoverageWinPS51 + if: success() || failure() + Test_Stage_Code_Coverage: + name: Publish Code Coverage + if: success() || failure() + runs-on: ubuntu-latest + needs: + - Build_Stage_Package_Module + - Test_Stage_test_linux + - Test_Stage_test_windows_core + - Test_Stage_test_windows_ps + steps: + - name: Checkout Code + uses: actions/checkout@v3 + with: + ref: ${{github.event.pull_request.head.ref}} # checkout the correct branch name + repository: ${{github.event.pull_request.head.repo.full_name}} # checkout the correct branch name + fetch-depth: 0 + + - name: Download Test Artifact Linux + uses: actions/download-artifact@v4 + with: + name: CodeCoverageLinux + path: ${{ env.buildFolderName }}/${{ env.testResultFolderName }}/CodeCoverageLinux/ + - name: Download Test Artifact Windows (PS 5.1) + uses: actions/download-artifact@v4 + with: + name: CodeCoverageWinPS51 + path: ${{ env.buildFolderName }}/${{ env.testResultFolderName }}/CodeCoverageWinPS51/ + - name: Download Test Artifact Windows (PS7) + uses: actions/download-artifact@v4 + with: + name: CodeCoverageWinPS7 + path: ${{ env.buildFolderName }}/${{ env.testResultFolderName }}/CodeCoverageWinPS7/ + + - name: Publish Linux Test Results + id: linux-test-results + uses: EnricoMi/publish-unit-test-result-action@v2 + if: always() + with: + nunit_files: ${{ env.buildFolderName }}/${{ env.testResultFolderName }}/CodeCoverageLinux/NUnit*.xml + check_name: Linux Test Results + - name: Publish WinPS51 Test Results + id: winps51-test-results + uses: EnricoMi/publish-unit-test-result-action@v2 + if: always() + with: + nunit_files: ${{ env.buildFolderName }}/${{ env.testResultFolderName }}/CodeCoverageWinPS51/NUnit*.xml + check_name: WinPS51 Test Results + - name: Publish WinPS71 Test Results + id: winps71-test-results + uses: EnricoMi/publish-unit-test-result-action@v2 + if: always() + with: + nunit_files: ${{ env.buildFolderName }}/${{ env.testResultFolderName }}/CodeCoverageWinPS7/NUnit*.xml + check_name: WinPS71 Test Results diff --git a/.gitignore b/.gitignore index 7747b6ed..a3eb64e0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,14 +1,17 @@ -**/TEST-Results.xml +**.bak +*.local.* +!**/README.md +.kitchen/ -# User-specific files +~*.* +*.nupkg *.suo *.user *.userosscache *.sln.docstates -~*.* # Build results -Output/ +[Oo]utput/ # MSTest test Results [Tt]est[Rr]esult*/ @@ -20,4 +23,11 @@ TestResult.xml # Visual Studio code coverage results *.coverage -*.coveragexml +.vs +.psproj +.sln +markdownissues.txt +node_modules +package-lock.json + +# just a comment diff --git a/.markdownlint.json b/.markdownlint.json new file mode 100644 index 00000000..87b7da56 --- /dev/null +++ b/.markdownlint.json @@ -0,0 +1,10 @@ +{ + "default": true, + "MD029": { + "style": "one" + }, + "MD013": true, + "MD024": false, + "MD034": false, + "no-hard-tabs": true +} diff --git a/.vscode/analyzersettings.psd1 b/.vscode/analyzersettings.psd1 new file mode 100644 index 00000000..78312d2c --- /dev/null +++ b/.vscode/analyzersettings.psd1 @@ -0,0 +1,44 @@ +@{ + CustomRulePath = '.\output\RequiredModules\DscResource.AnalyzerRules' + includeDefaultRules = $true + IncludeRules = @( + # DSC Resource Kit style guideline rules. + 'PSAvoidDefaultValueForMandatoryParameter', + 'PSAvoidDefaultValueSwitchParameter', + 'PSAvoidInvokingEmptyMembers', + 'PSAvoidNullOrEmptyHelpMessageAttribute', + 'PSAvoidUsingCmdletAliases', + 'PSAvoidUsingComputerNameHardcoded', + 'PSAvoidUsingDeprecatedManifestFields', + 'PSAvoidUsingEmptyCatchBlock', + 'PSAvoidUsingInvokeExpression', + 'PSAvoidUsingPositionalParameters', + 'PSAvoidShouldContinueWithoutForce', + 'PSAvoidUsingWMICmdlet', + 'PSAvoidUsingWriteHost', + 'PSDSCReturnCorrectTypesForDSCFunctions', + 'PSDSCStandardDSCFunctionsInResource', + 'PSDSCUseIdenticalMandatoryParametersForDSC', + 'PSDSCUseIdenticalParametersForDSC', + 'PSMisleadingBacktick', + 'PSMissingModuleManifestField', + 'PSPossibleIncorrectComparisonWithNull', + 'PSProvideCommentHelp', + 'PSReservedCmdletChar', + 'PSReservedParams', + 'PSUseApprovedVerbs', + 'PSUseCmdletCorrectly', + 'PSUseOutputTypeCorrectly', + 'PSAvoidGlobalVars', + 'PSAvoidUsingConvertToSecureStringWithPlainText', + 'PSAvoidUsingPlainTextForPassword', + 'PSAvoidUsingUsernameAndPasswordParams', + 'PSDSCUseVerboseMessageInDSCResource', + 'PSShouldProcess', + 'PSUseDeclaredVarsMoreThanAssignments', + 'PSUsePSCredentialType', + + 'Measure-*' + ) + +} diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 00000000..bbd4a82c --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,8 @@ +{ + "recommendations": [ + "davidanson.vscode-markdownlint", + "ms-vscode.powershell", + "streetsidesoftware.code-spell-checker", + "redhat.vscode-yaml" + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..7770c550 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,46 @@ +{ + "powershell.codeFormatting.openBraceOnSameLine": false, + "powershell.codeFormatting.newLineAfterOpenBrace": true, + "powershell.codeFormatting.newLineAfterCloseBrace": true, + "powershell.codeFormatting.whitespaceBeforeOpenBrace": true, + "powershell.codeFormatting.whitespaceBeforeOpenParen": true, + "powershell.codeFormatting.whitespaceAroundOperator": true, + "powershell.codeFormatting.whitespaceAfterSeparator": true, + "powershell.codeFormatting.ignoreOneLineBlock": false, + "powershell.codeFormatting.pipelineIndentationStyle": "IncreaseIndentationAfterEveryPipeline", + "powershell.codeFormatting.preset": "Custom", + "powershell.codeFormatting.alignPropertyValuePairs": true, + "powershell.developer.bundledModulesPath": "${cwd}/output/RequiredModules", + "powershell.scriptAnalysis.settingsPath": ".vscode\\analyzersettings.psd1", + "powershell.scriptAnalysis.enable": true, + "files.trimTrailingWhitespace": true, + "files.trimFinalNewlines": true, + "files.insertFinalNewline": true, + "files.associations": { + "*.ps1xml": "xml" + }, + "cSpell.words": [ + "COMPANYNAME", + "ICONURI", + "LICENSEURI", + "PROJECTURI", + "RELEASENOTES", + "buildhelpers", + "endregion", + "gitversion", + "icontains", + "keepachangelog", + "notin", + "pscmdlet", + "steppable" + ], + "[markdown]": { + "files.trimTrailingWhitespace": false, + "files.encoding": "utf8" + }, + "github.copilot.chat.commitMessageGeneration.instructions": [ + { + "file": ".github/copilot-commit-message-instructions.md" + } + ] +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 00000000..29911402 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,125 @@ +{ + "version": "2.0.0", + "_runner": "terminal", + "windows": { + "options": { + "shell": { + "executable": "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe", + "args": [ + "-NoProfile", + "-ExecutionPolicy", + "Bypass", + "-Command" + ] + } + } + }, + "linux": { + "options": { + "shell": { + "executable": "/usr/bin/pwsh", + "args": [ + "-NoProfile", + "-Command" + ] + } + } + }, + "osx": { + "options": { + "shell": { + "executable": "/usr/local/bin/pwsh", + "args": [ + "-NoProfile", + "-Command" + ] + } + } + }, + "tasks": [ + { + "label": "build", + "type": "shell", + "command": "&${cwd}/build.ps1", + "args": [], + "presentation": { + "echo": true, + "reveal": "always", + "focus": true, + "panel": "new", + "clear": false + }, + "runOptions": { + "runOn": "default" + }, + "problemMatcher": [ + { + "owner": "powershell", + "fileLocation": [ + "absolute" + ], + "severity": "error", + "pattern": [ + { + "regexp": "^\\s*(\\[-\\]\\s*.*?)(\\d+)ms\\s*$", + "message": 1 + }, + { + "regexp": "(.*)", + "code": 1 + }, + { + "regexp": "" + }, + { + "regexp": "^.*,\\s*(.*):\\s*line\\s*(\\d+).*", + "file": 1, + "line": 2 + } + ] + } + ] + }, + { + "label": "test", + "type": "shell", + "command": "&${cwd}/build.ps1", + "args": ["-AutoRestore","-Tasks","test"], + "presentation": { + "echo": true, + "reveal": "always", + "focus": true, + "panel": "dedicated", + "showReuseMessage": true, + "clear": false + }, + "problemMatcher": [ + { + "owner": "powershell", + "fileLocation": [ + "absolute" + ], + "severity": "error", + "pattern": [ + { + "regexp": "^\\s*(\\[-\\]\\s*.*?)(\\d+)ms\\s*$", + "message": 1 + }, + { + "regexp": "(.*)", + "code": 1 + }, + { + "regexp": "" + }, + { + "regexp": "^.*,\\s*(.*):\\s*line\\s*(\\d+).*", + "file": 1, + "line": 2 + } + ] + } + ] + } + ] +} diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..0590d24a --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,31 @@ +# Changelog for FabricTools + +The format is based on and uses the types of changes according to [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Added + +- For new features. + +### Changed + +- For changes in existing functionality. + +### Deprecated + +- For soon-to-be removed features. + +### Removed + +- For now removed features. + +### Fixed + +- For any bug fix. + +### Security + +- In case of vulnerabilities. + diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..7e77a3a2 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,46 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 856ac897..b524ed48 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,45 +1,123 @@ -# Code of Conduct +# Contributing -## Our Pledge +## Welcome -In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to make participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. +Before we go any further, thanks for being here. Thanks for using the module and especially thanks for being here and looking into how you can help! -## Our Standards +## Important resources -Examples of behavior that contributes to creating a positive environment include: +- docs TODO: link to docs +- bugs TODO: link to issues issue template +- communicate via issues, PRs, and discussions as well as the project -- Using welcoming and inclusive language -- Being respectful of differing viewpoints and experiences -- Gracefully accepting constructive criticism -- Focusing on what is best for the community -- Showing empathy towards other community members +## Develop & Build -Examples of unacceptable behavior by participants include: +We are using the [Sampler](https://github.com/gaelcolas/Sampler) Powershell Module to structure our module. This makes it easier to develop and test the module locally. -- The use of sexualized language or imagery and unwelcome sexual attention or advances -- Trolling, insulting/derogatory comments, and personal or political attacks -- Public or private harassment -- Publishing others' private information, such as a physical or electronic address, without explicit permission -- Other conduct which could reasonably be considered inappropriate in a professional setting +The workflow for using this and developing the code is shown below. -## Our Responsibilities +1. Download or clone the repo locally and create a new branch to develop on -Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. + ```PowerShell + git checkout -b newStuff # give it a proper name! + ``` -Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. +2. Make sure you have the latest Microsoft.PowerShell.PSResourceGet module -## Scope + ```PowerShell + -- Find the latest version on the gallery + Find-Module Microsoft.PowerShell.PSResourceGet -This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. + -- Install side-by-side + Install-Module Microsoft.PowerShell.PSResourceGet -Force + ``` -## Enforcement +3. Develop your updates in the source directory -Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. + You should also resolve all dependencies before you start developing. This will ensure that you have all the required modules installed and loaded into your session. -Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. + ```PowerShell + .\build.ps1 -ResolveDependency -Tasks noop -UsePSResourceGet + ``` -## Attribution + | :heavy_exclamation_mark: **Important** | + | :-------------------------------------------- | + | **YOU MUST DEVELOP IN THE SOURCE DIRECTORY.** | -This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org), version 1.4, available at [https://www.contributor-covenant.org/version/1/4/code-of-conduct.html](https://www.contributor-covenant.org/version/1/4/code-of-conduct.html) + This is important because the build process will create a new folder in the root of the repository called `output` and this is where the module will be built and loaded from. -For answers to common questions about this code of conduct, see [https://www.contributor-covenant.org/faq](https://www.contributor-covenant.org/faq) + If you change the code in the output folder and then build the module again, it will overwrite the changes you made. + + Ask Rob how he knows this! + +4. Use GitHub CoPilot to write your commit messages by clicking on the sparkles in the commit message box. This will generate a commit message based on the changes you made. You can then edit the message to make it more descriptive if you want. This uses the prompt in the `.github\copilot-commit-message-instructions.md` file. + + Add this to your VS Code settings to enable it: + + ```json + "github.copilot.chat.commitMessageGeneration.instructions": [ + { + "file": ".github/copilot-commit-message-instructions.md" + } + ], + ``` + +5. Build the module. From the root of the repository run the following command: + + ```PowerShell + ./build.ps1 -Tasks build + ``` + + This will build the module and create a new folder in the root of the repository called `output`. It will also load the new module into your current session. + +6. **AFTER** building, you can then run the Pester tests to ensure that everything is working as expected. The tests are located in the `tests` folder and can be run using the following command: + + ```PowerShell + Invoke-Pester ./tests/ + ``` + + This will run all the tests in the `tests` folder and output the results to the console. You can also run specific tags such as `FunctionalQuality`, `TestQuality`, `HelpQuality`. + +7. You can also simulate the deployment testing by running the following command: + + ```PowerShell + ./build.ps1 -Tasks build,test + ``` + +8. Once you are happy with your code, push your branch to GitHub and create a PR against the repo. + +## Thanks! + +We will review your PR and get back to you as soon as we can. We are all volunteers and do this in our spare time, so please be patient with us. We will try to get back to you within a week, but it may take longer if we are busy. + +If you have any questions or need help, please feel free to reach out to us on the [GitHub Discussions](https://github.com/dataplat/FabricTools/discussions) + +## How to submit changes + +TODO: +Pull Request protocol etc. You might also include what response they'll get back from the team on submission, or any caveats about the speed of response. + +## How to report a bug + +TODO: +Bugs are problems in code, in the functionality of an application or in its UI design; you can submit them through "bug trackers" and most projects invite you to do so, so that they may "debug" with more efficiency and the input of a contributor. Take a look at Atom's example for how to teach people to report bugs to your project. + +## Templates + +TODO: +in this section of your file, you might also want to link to a bug report "template" like this one here which contributors can copy and add context to; this will keep your bugs tidy and relevant. + +## Style Guide + +TODO: +include extensions and vscode settings we use to keep things neat + +## Code of Conduct + +This project follows a Code of Conduct to ensure a welcoming, respectful, and inclusive environment for all contributors, regardless of background or identity. Contributors are expected to use inclusive language, respect differing viewpoints, and respond to feedback constructively. Unacceptable behaviors, such as harassment, discrimination, or publishing others' private information, will not be tolerated. Project maintainers are responsible for enforcing these standards fairly and may remove or ban individuals who violate them. This Code applies in all project spaces and in any public context where someone represents the project. Reports of misconduct will be handled confidentially and seriously. You can read the full code of conduct [here](https://github.com/dataplat/FabricTools/blob/sampler/CODE_OF_CONDUCT.md). + +| :heavy_exclamation_mark: **Important** | +| :------------------------------------- | +| **BE EXCELLENT TO EACH OTHER** | + +Do I need to say more? If your behaviour or communication does not fit into this statement, we do not wish for you to help us. diff --git a/FabricTools/FabricTools.psd1 b/FabricTools/FabricTools.psd1 deleted file mode 100644 index 05b2962e..00000000 --- a/FabricTools/FabricTools.psd1 +++ /dev/null @@ -1,346 +0,0 @@ -# -# Module manifest for module 'FabricTools' -# -# Generated by: Ioana Bouariu -# -# Generated on: 05.11.2023 -# - -@{ - - # Script module or binary module file associated with this manifest. - RootModule = 'FabricTools.psm1' - - # Version number of this module. - ModuleVersion = '0.20.0' - - # Supported PSEditions - # CompatiblePSEditions = @() - - # ID used to uniquely identify this module - GUID = 'f2a0f9e6-fab6-41fc-9e1c-0c94ff38f794' - - # Author of this module - Author = 'The FabricTools Team' - - # Company or vendor of this module - CompanyName = 'fabrictools.io' - - # Copyright statement for this module - Copyright = 'Copyright (c) 2025 by FabricTools, licensed under MIT' - - # Description of the functionality provided by this module - Description = 'A module to be able to do more with Microsoft Fabric. - It lets you pause and resume Fabric capacities. - Adds functionallity previously only available with the REST API as PowerShell functions. - There are also functions to make it easier to monitor usage metrics and refreshes. - It also adds Fabric-friendly aliases for PowerBI functions to make it easier to use the module.' - - # Minimum version of the PowerShell engine required by this module - PowerShellVersion = '5.1' - - # Name of the PowerShell host required by this module - # PowerShellHostName = '' - - # Minimum version of the PowerShell host required by this module - # PowerShellHostVersion = '' - - # Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only. - # DotNetFrameworkVersion = '' - - # Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only. - # ClrVersion = '' - - # Processor architecture (None, X86, Amd64) required by this module - # ProcessorArchitecture = '' - - # Modules that must be imported into the global environment prior to importing this module - RequiredModules = @('Az.Accounts', 'Az.Resources','MicrosoftPowerBIMgmt') - - # Assemblies that must be loaded prior to importing this module - #RequiredAssemblies = @('Microsoft.Azure.PowerShell.Authentication') - - # Script files () that are run in the caller's environment prior to importing this module. - # ScriptsToProcess = @() - - # Type files (.ps1xml) to be loaded when importing this module - # TypesToProcess = @() - - # Format files (.ps1xml) to be loaded when importing this module - # FormatsToProcess = @() - - # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess - # NestedModules = '' - - # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. - FunctionsToExport = @( - "Add-FabricWorkspaceIdentity", - "Add-FabricWorkspaceRoleAssignment", - "Assign-FabricDomainWorkspaceByCapacity", - "Assign-FabricDomainWorkspaceById", - "Assign-FabricDomainWorkspaceByPrincipal", - "Assign-FabricDomainWorkspaceRoleAssignment", - "Assign-FabricWorkspaceCapacity", - "Confirm-FabricAuthToken", - "Connect-FabricAccount", - "Convert-FromBase64", - "Convert-ToBase64", - "Export-FabricItem", - "Get-AllFabricCapacities", - "Get-AllFabricDatasetRefreshes", - "Get-FabricAPIClusterURI", - "Get-FabricAuthToken", - "Get-FabricCapacity", - "Get-FabricCapacityRefreshables", - "Get-FabricCapacitySkus", - "Get-FabricCapacityState", - "Get-FabricCapacityTenantOverrides", - "Get-FabricCapacityTenantSettingOverrides", - "Get-FabricCapacityWorkload", - "Get-FabricConnection", - "Get-FabricCopyJob", - "Get-FabricCopyJobDefinition", - "Get-FabricDashboard", - "Get-FabricDatamart", - "Get-FabricDataPipeline", - "Get-FabricDatasetRefreshes", - "Get-FabricDebugInfo", - "Get-FabricDomain", - "Get-FabricDomainTenantSettingOverrides", - "Get-FabricDomainWorkspace", - "Get-FabricEnvironment", - "Get-FabricEnvironmentLibrary", - "Get-FabricEnvironmentSparkCompute", - "Get-FabricEnvironmentStagingLibrary", - "Get-FabricEnvironmentStagingSparkCompute", - "Get-FabricEventhouse", - "Get-FabricEventhouseDefinition", - "Get-FabricEventstream", - "Get-FabricEventstreamDefinition", - "Get-FabricExternalDataShares", - "Get-FabricItem", - "Get-FabricKQLDashboard", - "Get-FabricKQLDashboardDefinition", - "Get-FabricKQLDatabase", - "Get-FabricKQLDatabaseDefinition", - "Get-FabricKQLQueryset", - "Get-FabricKQLQuerysetDefinition", - "Get-FabricLakehouse", - "Get-FabricLakehouseTable", - "Get-FabricLongRunningOperation", - "Get-FabricLongRunningOperationResult", - "Get-FabricMirroredDatabase", - "Get-FabricMirroredDatabaseDefinition", - "Get-FabricMirroredDatabaseStatus", - "Get-FabricMirroredDatabaseTableStatus", - "Get-FabricMirroredWarehouse", - "Get-FabricMLExperiment", - "Get-FabricMLModel", - "Get-FabricNotebook", - "Get-FabricNotebookDefinition", - "Get-FabricPaginatedReport", - "Get-FabricReflex", - "Get-FabricReflexDefinition", - "Get-FabricReport", - "Get-FabricReportDefinition", - "Get-FabricSemanticModel", - "Get-FabricSemanticModelDefinition", - "Get-FabricSparkCustomPool", - "Get-FabricSparkJobDefinition", - "Get-FabricSparkJobDefinitionDefinition", - "Get-FabricSparkSettings", - "Get-FabricSQLDatabase", - "Get-FabricSQLEndpoint", - "Get-FabricTenantSetting", - "Get-FabricTenantSettings", - "Get-FabricUsageMetricsQuery", - "Get-FabricUserListAccessEntities", - "Get-FabricWarehouse", - "Get-FabricWorkspace", - "Get-FabricWorkspaceDatasetRefreshes", - "Get-FabricWorkspaceRoleAssignment", - "Get-FabricWorkspaceTenantSettingOverrides", - "Get-FabricWorkspaceUsageMetricsData", - "Get-FabricWorkspaceUsers", - "Get-SHA256", - "Import-FabricItem", - "Invoke-FabricAPIRequest", - "Invoke-FabricAPIRequest", - "Invoke-FabricDatasetRefresh", - "Invoke-FabricKQLCommand", - "Load-FabricLakehouseTable", - "New-FabricCopyJob", - "New-FabricDataPipeline", - "New-FabricDomain", - "New-FabricEnvironment", - "New-FabricEventhouse", - "New-FabricEventstream", - "New-FabricKQLDashboard", - "New-FabricKQLDatabase", - "New-FabricKQLQueryset", - "New-FabricLakehouse", - "New-FabricMirroredDatabase", - "New-FabricMLExperiment", - "New-FabricMLModel", - "New-FabricNotebook", - "New-FabricNotebookNEW", - "New-FabricReflex", - "New-FabricReport", - "New-FabricSemanticModel", - "New-FabricSparkCustomPool", - "New-FabricSparkJobDefinition", - "New-FabricSQLDatabase", - "New-FabricWarehouse", - "New-FabricWorkspace", - "New-FabricWorkspaceUsageMetricsReport", - "Publish-FabricEnvironment", - "Register-FabricWorkspaceToCapacity", - "Remove-FabricCopyJob", - "Remove-FabricDataPipeline", - "Remove-FabricDomain", - "Remove-FabricEnvironment", - "Remove-FabricEnvironmentStagingLibrary", - "Remove-FabricEventhouse", - "Remove-FabricEventstream", - "Remove-FabricItem", - "Remove-FabricKQLDashboard", - "Remove-FabricKQLDatabase", - "Remove-FabricKQLQueryset", - "Remove-FabricLakehouse", - "Remove-FabricMirroredDatabase", - "Remove-FabricMLExperiment", - "Remove-FabricMLModel", - "Remove-FabricNotebook", - "Remove-FabricReflex", - "Remove-FabricReport", - "Remove-FabricSemanticModel", - "Remove-FabricSparkCustomPool", - "Remove-FabricSparkJobDefinition", - "Remove-FabricSQLDatabase", - "Remove-FabricWarehouse", - "Remove-FabricWorkspace", - "Remove-FabricWorkspaceIdentity", - "Remove-FabricWorkspaceRoleAssignment", - "Resume-FabricCapacity", - "Revoke-FabricCapacityTenantSettingOverrides", - "Revoke-FabricExternalDataShares", - "Set-FabricApiHeaders", - "Set-FabricAuthToken", - "Start-FabricLakehouseTableMaintenance", - "Start-FabricMirroredDatabaseMirroring", - "Start-FabricSparkJobDefinitionOnDemand", - "Stop-FabricEnvironmentPublish", - "Stop-FabricMirroredDatabaseMirroring", - "Suspend-FabricCapacity", - "Test-FabricApiResponse", - "Unassign-FabricDomainWorkspace", - "Unassign-FabricDomainWorkspaceRoleAssignment", - "Unassign-FabricWorkspaceCapacity", - "Unregister-FabricWorkspaceToCapacity", - "Update-FabricCapacityTenantSettingOverrides", - "Update-FabricCopyJob", - "Update-FabricCopyJobDefinition", - "Update-FabricDataPipeline", - "Update-FabricDomain", - "Update-FabricEnvironment", - "Update-FabricEnvironmentStagingSparkCompute", - "Update-FabricEventhouse", - "Update-FabricEventhouseDefinition", - "Update-FabricEventstream", - "Update-FabricEventstreamDefinition", - "Update-FabricKQLDashboard", - "Update-FabricKQLDashboardDefinition", - "Update-FabricKQLDatabase", - "Update-FabricKQLDatabaseDefinition", - "Update-FabricKQLQueryset", - "Update-FabricKQLQuerysetDefinition", - "Update-FabricLakehouse", - "Update-FabricMirroredDatabase", - "Update-FabricMirroredDatabaseDefinition", - "Update-FabricMLExperiment", - "Update-FabricMLModel", - "Update-FabricNotebook", - "Update-FabricNotebookDefinition", - "Update-FabricPaginatedReport", - "Update-FabricReflex", - "Update-FabricReflexDefinition", - "Update-FabricReport", - "Update-FabricReportDefinition", - "Update-FabricSemanticModel", - "Update-FabricSemanticModelDefinition", - "Update-FabricSparkCustomPool", - "Update-FabricSparkJobDefinition", - "Update-FabricSparkJobDefinitionDefinition", - "Update-FabricSparkSettings", - "Update-FabricTenantSetting", - "Update-FabricWarehouse", - "Update-FabricWorkspace", - "Update-FabricWorkspaceRoleAssignment", - "Upload-FabricEnvironmentStagingLibrary" - ) - - # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. - CmdletsToExport = @() - - # Variables to export from this module - VariablesToExport = '*' - - # Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. - AliasesToExport = @( - "Export-FabItem", - "Get-FabricDataset", - "Get-FabricReport", - "Login-Fabric", - "Logout-Fabric" - ) - - # DSC resources to export from this module - # DscResourcesToExport = @() - - # List of all modules packaged with this module - # ModuleList = @() - - # List of all files packaged with this module - # FileList = @() - - # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. - PrivateData = @{ - - PSData = @{ - - # Tags applied to this module. These help with module discovery in online galleries. - Tags = @("microsoftfabric", "powerbi", "developermode", "pbip", "FabricTools", "Fabric") - - # A URL to the license for this module. - LicenseUri = 'https://www.github.com/dataplat/FabricTools/LICENSE.md' - - # A URL to the main website for this project. - ProjectUri = 'https://www.github.com/dataplat/FabricTools' - - # A URL to an icon representing this module. - IconUri = 'https://raw.githubusercontent.com/dataplat/FabricTools/main/images/FabricToolsLogo.ico' - - # ReleaseNotes of this module - ReleaseNotes = 'https://github.com/dataplat/FabricTools/blob/main/ReleaseNotes.md' - - # Prerelease string of this module - # Prerelease = '' - - # Flag to indicate whether the module requires explicit user acceptance for install/update/save - # RequireLicenseAcceptance = $false - - # External dependent modules of this module - # ExternalModuleDependencies = @() - - } # End of PSData hashtable - - } # End of PrivateData hashtable - - # HelpInfo URI of this module - HelpInfoURI = 'https://www.github.com/dataplat/FabricTools' - - # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. - # DefaultCommandPrefix = '' - -} - diff --git a/FabricTools/FabricTools.psm1 b/FabricTools/FabricTools.psm1 deleted file mode 100644 index ae776dfd..00000000 --- a/FabricTools/FabricTools.psm1 +++ /dev/null @@ -1,92 +0,0 @@ -<# -.SYNOPSIS - This script loads necessary modules, sources functions from .ps1 files, and sets aliases for PowerBI functions. - -.DESCRIPTION - The script first tries to load the Az.Accounts and Az.Resources modules. If these modules are not available, it installs and imports them. - It then gets all .ps1 files in the Functions folder, sources each function, and exports it as a module member. - Finally, it sets an alias for the PowerBI login function. - -.EXAMPLE - .\FabricTools.psm1 - - This command runs the script. - -.INPUTS - None. You cannot pipe inputs to this script. - -.OUTPUTS - None. This script does not return any output. - -.NOTES - This script is part of the FabricTools module. -#> - -$script:FabricSession = [ordered]@{ - BaseApiUrl = 'https://api.fabric.microsoft.com/v1' - ResourceUrl = 'https://api.fabric.microsoft.com' - FabricToken = $null - HeaderParams = $null - ContentType = @{'Content-Type' = "application/json"} - KustoURL = "https://api.kusto.windows.net" - AccessToken = $null -} - -$script:AzureSession = [ordered]@{ - BaseApiUrl = "https://management.azure.com" - AccessToken = $null - Token = $null - HeaderParams = $null -} - -$script:PowerBI = [ordered]@{ - BaseApiUrl = "https://api.powerbi.com/v1.0/myorg" -} - -$FabricTools = @{ - FeatureFlags = @{ - AutoRenewExpiredToken = $true - } -} - -$FabricConfig = @{ - BaseUrl = "https://api.fabric.microsoft.com/v1" - ResourceUrl = "https://api.fabric.microsoft.com" - FabricHeaders = @{} - TenantIdGlobal = "" - TokenExpiresOn = "" -} - -Export-ModuleMember -Variable FabricConfig -Export-ModuleMember -Variable FabricTools - -# Get all .ps1 files in the (public) Functions folder -$publicFunctions = Get-ChildItem -Path "$PSScriptRoot\public" -Filter '*.ps1' -Recurse - -# Loop over each function file -foreach ($function in $publicFunctions) { - # Dot-source the function - . $function.fullname - # Export the function as a module member - Export-ModuleMember -Function $function.basename -} - -$privateFunctions = Get-ChildItem -Path "$PSScriptRoot\private" -Filter '*.ps1' -Recurse -$privateFunctions | ForEach-Object { . $_.FullName } - -# Set aliases for PowerBI functions -Set-Alias -Name Login-Fabric -Value Login-PowerBI -Export-ModuleMember -Alias Login-Fabric -Set-Alias -Name Get-FabWorkspace -Value Get-FabricWorkspace -Export-ModuleMember -Alias Get-FabDataset -Set-Alias -Name Get-FabricDataset -Value Get-PowerBIDataset -Export-ModuleMember -Alias Get-FabricDataset -Set-Alias -Name Get-FabReport -Value Get-PowerBIReport -Export-ModuleMember -Alias Get-FabReport -Set-Alias -Name Get-FabricReport -Value Get-PowerBIReport -Export-ModuleMember -Alias Get-FabricReport -Set-Alias -Name Logout-Fabric -Value Logout-PowerBI -Export-ModuleMember -Alias Logout-Fabric - -$moduleName = 'FabricTools' -Write-Host "Module $moduleName imported." diff --git a/FabricTools/public/Confirm-FabricAuthToken.ps1 b/FabricTools/public/Confirm-FabricAuthToken.ps1 deleted file mode 100644 index 14c7933b..00000000 --- a/FabricTools/public/Confirm-FabricAuthToken.ps1 +++ /dev/null @@ -1,50 +0,0 @@ -<# -.SYNOPSIS - Check whether the Fabric API authentication token is set and not expired and reset it if necessary. - -.DESCRIPTION - The Confirm-FabricAuthToken function retrieves the Fabric API authentication token. If the token is not already set, it calls the Set-FabricAuthToken function to set it. It then outputs the token. - -.EXAMPLE - Confirm-FabricAuthToken - - This command retrieves the Fabric API authentication token. - -.INPUTS - None. You cannot pipe inputs to this function. - -.OUTPUTS - Returns object as Get-FabricDebugInfo function - -.NOTES - -#> - -function Confirm-FabricAuthToken { - [CmdletBinding()] - param ( ) - - Write-Verbose "Check if session is established and token not expired." - - # Check if the Fabric token is already set - if (!$FabricSession.FabricToken -or !$AzureSession.AccessToken) { - Write-Output "Confirm-FabricAuthToken::Set-FabricAuthToken" - Set-FabricAuthToken | Out-Null - } - - $now = (Get-Date) - $s = Get-FabricDebugInfo - if ($FabricSession.AccessToken.ExpiresOn -lt $now ) { - Write-Output "Confirm-FabricAuthToken::Set-FabricAuthToken#1" - Set-FabricAuthToken -reset | Out-Null - } - - if ($s.AzureSession.AccessToken.ExpiresOn -lt $now ) { - Write-Output "Confirm-FabricAuthToken::Set-FabricAuthToken#2" - Set-FabricAuthToken -reset | Out-Null - } - - $s = Get-FabricDebugInfo - return $s - -} \ No newline at end of file diff --git a/FabricTools/public/Item/Get-FabricItem.ps1 b/FabricTools/public/Item/Get-FabricItem.ps1 deleted file mode 100644 index e9261449..00000000 --- a/FabricTools/public/Item/Get-FabricItem.ps1 +++ /dev/null @@ -1,67 +0,0 @@ -<# -.SYNOPSIS -Retrieves fabric items from a workspace. - -.DESCRIPTION -The Get-FabricItem function retrieves fabric items from a specified workspace. It can retrieve all items or filter them by item type. - -.PARAMETER workspaceId -The ID of the workspace from which to retrieve the fabric items. - -.PARAMETER type -(Optional) The type of the fabric items to retrieve. If not specified, all items will be retrieved. - -.EXAMPLE -Get-FabricItem -workspaceId "12345" -type "file" - -This example retrieves all fabric items of type "file" from the workspace with ID "12345". - -.NOTES -This function was originally written by Rui Romano. -https://github.com/RuiRomano/fabricps-pbip - -#> - - -Function Get-FabricItem { - [CmdletBinding()] - param - ( - [Parameter(Mandatory = $true, ParameterSetName = 'WorkspaceId')] - [string]$workspaceId, - - [Parameter(Mandatory = $true, ParameterSetName = 'WorkspaceObject', ValueFromPipeline = $true )] - $Workspace, - - [Parameter(Mandatory = $false)] - [Alias("itemType")] - [string]$type, - - [Parameter(Mandatory = $false)] - [string]$itemID - ) - - begin { - Confirm-FabricAuthToken | Out-Null - } - - process { - if ($PSCmdlet.ParameterSetName -eq 'WorkspaceObject') { - $workspaceID = $Workspace.id - } - if ($itemID) { - $result = Invoke-FabricAPIRequest -Uri "workspaces/$($workspaceID)/items/$($itemID)" -Method Get - } - else { - if ($type) { - $result = Invoke-FabricAPIRequest -Uri "workspaces/$($workspaceID)/items?type=$type" -Method Get - } - else { - # Invoke the Fabric API to get the workspaces - $result = Invoke-FabricAPIRequest -Uri "workspaces/$($workspaceID)/items" -Method Get - } - # Output the result - return $result.value - } - } -} \ No newline at end of file diff --git a/FabricTools/public/Item/Remove-FabricItem.ps1 b/FabricTools/public/Item/Remove-FabricItem.ps1 deleted file mode 100644 index 8659760b..00000000 --- a/FabricTools/public/Item/Remove-FabricItem.ps1 +++ /dev/null @@ -1,67 +0,0 @@ -<# -.SYNOPSIS - Removes selected items from a Fabric workspace. - -.DESCRIPTION - The Remove-FabricItems function removes selected items from a specified Fabric workspace. It uses the workspace ID and an optional filter to select the items to remove. If a filter is provided, only items whose DisplayName matches the filter are removed. - -.PARAMETER WorkspaceID - The ID of the Fabric workspace. This is a mandatory parameter. - -.PARAMETER Filter - An optional filter to select items to remove. If provided, only items whose DisplayName matches the filter are removed. - -.EXAMPLE - Remove-FabricItems -WorkspaceID "12345678-90ab-cdef-1234-567890abcdef" -Filter "*test*" - - This command removes all items from the workspace with the specified ID whose DisplayName includes "test". - -.INPUTS - String. You can pipe two strings that contain the workspace ID and filter to Remove-FabricItems. - -.OUTPUTS - None. This function does not return any output. - -.NOTES - This function was written by Rui Romano. - https://github.com/RuiRomano/fabricps-pbip -#> - -Function Remove-FabricItem { - [CmdletBinding(SupportsShouldProcess)] - param - ( - [Parameter(Mandatory = $true)] - [string]$workspaceId, - [Parameter(Mandatory = $false)] - [string]$filter, - [Parameter(Mandatory = $false)] - [string]$itemID - ) - - Confirm-FabricAuthToken | Out-Null - - # Invoke the Fabric API to get the items in the workspace - if ($PSCmdlet.ShouldProcess("Remove items from workspace $workspaceId")) { - if ($itemID) { - Invoke-FabricAPIRequest -Uri "workspaces/$($workspaceID)/items/$($itemID)" -Method Delete - } else { - $items = Invoke-FabricAPIRequest -Uri "workspaces/$workspaceId/items" -Method Get - - # Display the count of existing items - Write-Output "Existing items: $($items.Count)" - - # If a filter is provided - if ($filter) { - # Filter the items whose DisplayName matches the filter - $items = $items | Where-Object { $_.DisplayName -like $filter } - } - - # For each item - foreach ($item in $items) { - # Remove the item - Invoke-FabricAPIRequest -Uri "workspaces/$workspaceId/items/$($item.ID)" -Method Delete - } - } - } -} \ No newline at end of file diff --git a/FabricTools/public/KQL Queryset/Invoke-FabricKQLCommand.ps1 b/FabricTools/public/KQL Queryset/Invoke-FabricKQLCommand.ps1 deleted file mode 100644 index 34720f2c..00000000 --- a/FabricTools/public/KQL Queryset/Invoke-FabricKQLCommand.ps1 +++ /dev/null @@ -1,230 +0,0 @@ -function Invoke-FabricKQLCommand { -#Requires -Version 7.1 - -<# -.SYNOPSIS - Executes a KQL command in a Kusto Database. - -.DESCRIPTION - Executes a KQL command in a Kusto Database. The KQL command is executed in the Kusto Database - that is specified by the KQLDatabaseName or KQLDatabaseId parameter. The KQL command is executed - in the context of the Fabric Real-Time Intelligence session that is established by the - Connect-RTISession cmdlet. The cmdlet distinguishes between management commands and query commands. - Management commands are executed in the management API, while query commands are executed in the query API. - The distinction is made by checking if the KQL command starts with a dot. If it does, it is a management command - else it is a query command. If the KQL command is a management command, it is crucial to have the - .execute database script <| in the beginning, otherwise the Kusto API will not execute the script. This cmdlet - will automatically add the .execute database script <| in the beginning of the KQL command if it is a management command - and if it is not already present. - If the KQL Command is a query command, the result is returned as an array of PowerShell objects by default. If the - parameter -ReturnRawResult is used, the raw result of the KQL query is returned which is a JSON object. - -.PARAMETER WorkspaceId - Id of the Fabric Workspace for which the KQL command should be executed. The value for WorkspaceId is a GUID. - An example of a GUID is '12345678-1234-1234-1234-123456789012'. - -.PARAMETER KQLDatabaseName - The name of the KQLDatabase in which the KQL command should be executed. This parameter cannot be used together with KQLDatabaseId. - -.PARAMETER KQLDatabaseId - The Id of the KQLDatabase in which the KQL command should be executed. This parameter cannot be used together with KQLDatabaseName. - The value for KQLDatabaseId is a GUID. An example of a GUID is '12345678-1234-1234-1234-123456789012'. - -.PARAMETER KQLCommand - The KQL command that should be executed in the Kusto Database. - The KQL command is a string. An example of a string is '.create table MyTable (MyColumn: string)'. - -.PARAMETER ReturnRawResult - When this switch is used, the raw result of the KQL command is returned. By default, the result is returned as - a PowerShell object. - -.EXAMPLE - Invoke-FabricKQLCommand ` - -WorkspaceId '12345678-1234-1234-1234-123456789012' ` - -KQLDatabaseName 'MyKQLDatabase' ` - -KQLCommand '.create table MyTable (MyColumn: string)' - - This example will create a table named 'MyTable' with a column named 'MyColumn' in - the KQLDatabase 'MyKQLDatabase'. - -.EXAMPLE - Invoke-FabricKQLCommand ` - -WorkspaceId '2c4ccbb5-9b13-4495-9ab3-ba41152733d9' ` - -KQLDatabaseName 'MyEventhouse2' ` - -KQLCommand 'productcategory - | take 100' - - This example will Execute the Query 'productcategory | take 100' in the KQLDatabase 'MyEventhouse2' - and it will return the result as an array of PowerShell objects. - -.EXAMPLE - Invoke-FabricKQLCommand ` - -WorkspaceId '2c4ccbb5-9b13-4495-9ab3-ba41152733d9' ` - -KQLDatabaseName 'MyEventhouse2' ` - -ReturnRawResult ` - -KQLCommand 'productcategory - | take 100' - - This example will Execute the Query 'productcategory | take 100' in the KQLDatabase 'MyEventhouse2' - and it will return the result as the raw result of the KQL command, which is a JSON object. - -.NOTES - - Revsion History: - - - 2024-12-22 - FGE: Added Verbose Output - - 2024-12-27 - FGE: Major Update to support KQL Queries and Management Commands - -#> - -[CmdletBinding()] - param ( - - [Parameter(Mandatory=$true)] - [string]$WorkspaceId, - - [string]$KQLDatabaseName, - - [string]$KQLDatabaseId, - - [Parameter(Mandatory=$true)] - [string]$KQLCommand, - - [switch]$ReturnRawResult - ) - -begin { - - Confirm-FabricAuthToken | Out-Null - - Write-Verbose "Check if KQLDatabaseName and KQLDatabaseId are used together" - if ($PSBoundParameters.ContainsKey("KQLDatabaseName") -and $PSBoundParameters.ContainsKey("KQLDatabaseId")) { - throw "Parameters KQLDatabaseName and KQLDatabaseId cannot be used together" - } - - Write-Verbose "Get Kusto Database" - if ($PSBoundParameters.ContainsKey("KQLDatabaseName")) { - Write-Verbose "Getting Kusto Database by Name: $KQLDatabaseName" - $kustDB = Get-FabricKQLDatabase ` - -WorkspaceId $WorkspaceId ` - -KQLDatabaseName $KQLDatabaseName - } - - if ($PSBoundParameters.ContainsKey("KQLDatabaseId")) { - Write-Verbose "Getting Kusto Database by Id: $KQLDatabaseId" - $kustDB = Get-FabricKQLDatabase ` - -WorkspaceId $WorkspaceId ` - -KQLDatabaseId $KQLDatabaseId - } - - Write-Verbose "Check if Kusto Database was found" - if ($null -eq $kustDB) { - throw "Kusto Database not found" - } - - Write-Verbose "Generate the Management API URL" - $mgmtAPI = "$($kustDB.queryServiceUri)/v1/rest/mgmt" - - Write-Verbose "Generate the query API URL" - $queryAPI = "$($kustDB.queryServiceUri)/v1/rest/query" - - - $KQLCommand = $KQLCommand | Out-String - - Write-Verbose "Check if the KQL command starts with a dot so it is a management command. Otherwise it is a query command" - if (-not ($KQLCommand -match "^\.")) { - $isManamgentCommand = $false - Write-Verbose "The command is a query command." - } - else { - $isManamgentCommand = $true - Write-Verbose "The command is a management command. It is crucial to have the .execute database script <| in the beginning, otherwise the Kusto API will not execute the script." - if (-not ($KQLCommand -match "\.execute database script <\|")) { - $KQLCommand = ".execute database script <| $KQLCommand" - } - } -} - -process { - - Write-Verbose "The KQL-Command is: $KQLCommand" - - Write-Verbose "Create body of the request" - $body = @{ - 'csl' = $KQLCommand; - 'db'= $kustDB.displayName - } | ConvertTo-Json -Depth 1 - - - if ($isManamgentCommand) { - Write-Verbose "Calling Management API" - Write-Verbose "----------------------" - Write-Verbose "Sending the following values to the Query API:" - Write-Verbose "Headers: $($FabricSession.headerParams | Format-List | Out-String)" - Write-Verbose "Method: POST" - Write-Verbose "URI: $mgmtAPI" - Write-Verbose "Body of request: $body" - Write-Verbose "ContentType: application/json" - - $result = Invoke-RestMethod ` - -Headers $headerParams ` - -Method POST ` - -Uri $mgmtAPI ` - -Body ($body) ` - -ContentType "application/json; charset=utf-8" - - Write-Verbose "Result of the Management API: $($result | ` - ConvertTo-Json ` - -Depth 10)" - $result - } - else { - Write-Verbose "Calling Query API" - Write-Verbose "-----------------" - Write-Verbose "Sending the following values to the Query API:" - Write-Verbose "Headers: $($FabricSession.headerParams | Format-List | Out-String)" - Write-Verbose "Method: POST" - Write-Verbose "URI: $queryAPI" - Write-Verbose "Body of request: $body" - Write-Verbose "ContentType: application/json" - - $result = Invoke-RestMethod ` - -Headers $headerParams ` - -Method POST ` - -Uri $queryAPI ` - -Body ($body) ` - -ContentType "application/json; charset=utf-8" - Write-Verbose "Result of the Query API: $($result | ` - ConvertTo-Json ` - -Depth 10)" - - - - if ($ReturnRawResult) { - $result - } - else { - $myRecords = @() - - for ($j = 0; $j -lt $Result.tables[0].rows.Count; $j++) { - $myTableRow = [PSCustomObject]@{} - - for ($i = 0; $i -lt $Result.tables[0].rows[0].Count; $i++) { - $myTableRow | ` - Add-Member ` - -MemberType NoteProperty ` - -Name $Result.Tables[0].Columns[$i].ColumnName ` - -Value $Result.Tables[0].rows[$j][$i] - } - $myRecords += $myTableRow - } - - $myRecords - } - - } -} - -end {} - -} \ No newline at end of file diff --git a/FabricTools/public/Mirrored Database/Start-FabricMirroredDatabaseMirroring.ps1 b/FabricTools/public/Mirrored Database/Start-FabricMirroredDatabaseMirroring.ps1 deleted file mode 100644 index 19f3bce3..00000000 --- a/FabricTools/public/Mirrored Database/Start-FabricMirroredDatabaseMirroring.ps1 +++ /dev/null @@ -1,51 +0,0 @@ -function Start-FabricMirroredDatabaseMirroring{ - [CmdletBinding()] - param ( - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [string]$WorkspaceId, - - [Parameter(Mandatory = $false)] - [ValidateNotNullOrEmpty()] - [string]$MirroredDatabaseId - ) - - try { - # Step 2: Ensure token validity - Write-Message -Message "Validating token..." -Level Debug - Test-TokenExpired - Write-Message -Message "Token validation completed." -Level Debug - - $apiEndpointUrl = "{0}/workspaces/{1}/mirroredDatabases/{2}/startMirroring" -f $FabricConfig.BaseUrl, $WorkspaceId, $MirroredDatabaseId - Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug - - # Step 6: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Post ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -ResponseHeadersVariable "responseHeader" ` - -StatusCodeVariable "statusCode" - - # Step 7: Validate the response code - if ($statusCode -ne 200) { - Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error - Write-Message -Message "Error: $($response.message)" -Level Error - Write-Message -Message "Error Details: $($response.moreDetails)" -Level Error - Write-Message "Error Code: $($response.errorCode)" -Level Error - return $null - } - - # Step 9: Handle results - Write-Message -Message "Database mirroring started successfully for MirroredDatabaseId: $MirroredDatabaseId" -Level Info - return - } - catch { - # Step 10: Capture and log error details - $errorDetails = $_.Exception.Message - Write-Message -Message "Failed to start MirroredDatabase. Error: $errorDetails" -Level Error - } - -} diff --git a/FabricTools/public/Mirrored Database/Stop-FabricMirroredDatabaseMirroring.ps1 b/FabricTools/public/Mirrored Database/Stop-FabricMirroredDatabaseMirroring.ps1 deleted file mode 100644 index 77d7404e..00000000 --- a/FabricTools/public/Mirrored Database/Stop-FabricMirroredDatabaseMirroring.ps1 +++ /dev/null @@ -1,53 +0,0 @@ - - -function Stop-FabricMirroredDatabaseMirroring{ - [CmdletBinding()] - param ( - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [string]$WorkspaceId, - - [Parameter(Mandatory = $false)] - [ValidateNotNullOrEmpty()] - [string]$MirroredDatabaseId - ) - - try { - # Step 2: Ensure token validity - Write-Message -Message "Validating token..." -Level Debug - Test-TokenExpired - Write-Message -Message "Token validation completed." -Level Debug - - $apiEndpointUrl = "{0}/workspaces/{1}/mirroredDatabases/{2}/stopMirroring" -f $FabricConfig.BaseUrl, $WorkspaceId, $MirroredDatabaseId - Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug - - # Step 6: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Post ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -ResponseHeadersVariable "responseHeader" ` - -StatusCodeVariable "statusCode" - - # Step 7: Validate the response code - if ($statusCode -ne 200) { - Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error - Write-Message -Message "Error: $($response.message)" -Level Error - Write-Message -Message "Error Details: $($response.moreDetails)" -Level Error - Write-Message "Error Code: $($response.errorCode)" -Level Error - return $null - } - - # Step 9: Handle results - Write-Message -Message "Database mirroring stopped successfully for MirroredDatabaseId: $MirroredDatabaseId" -Level Info - return - } - catch { - # Step 10: Capture and log error details - $errorDetails = $_.Exception.Message - Write-Message -Message "Failed to stop MirroredDatabase. Error: $errorDetails" -Level Error - } - -} diff --git a/FabricTools/public/SQL Database/New-FabricSQLDatabase.ps1 b/FabricTools/public/SQL Database/New-FabricSQLDatabase.ps1 deleted file mode 100644 index e980f04e..00000000 --- a/FabricTools/public/SQL Database/New-FabricSQLDatabase.ps1 +++ /dev/null @@ -1,89 +0,0 @@ -<# -.SYNOPSIS -Creates a new SQL Database in a specified Microsoft Fabric workspace. - -.DESCRIPTION -This function sends a POST request to the Microsoft Fabric API to create a new SQL Database -in the specified workspace. It supports optional parameters for SQL Database description -and path definitions for the SQL Database content. - -.PARAMETER WorkspaceId -The unique identifier of the workspace where the SQL Database will be created. - -.PARAMETER Name -The name of the SQL Database to be created. - -.PARAMETER Description -An optional description for the SQL Database. - - -.EXAMPLE - New-FabricSQLDatabase -WorkspaceId "workspace-12345" -Name "NewDatabase" - - .NOTES -- Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. -- Calls `Test-TokenExpired` to ensure token validity before making the API request. - -Author: Kamil Nowinski - -#> - -function New-FabricSQLDatabase { - [CmdletBinding()] - param ( - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [string]$WorkspaceId, - - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [ValidatePattern('^[a-zA-Z0-9_]*$')] - [string]$Name, - - [Parameter(Mandatory = $false)] - [ValidateNotNullOrEmpty()] - [string]$Description - ) - - try { - # Step 1: Ensure token validity - Test-TokenExpired - - # Step 2: Construct the API URL - $apiEndpointUrl = "{0}/workspaces/{1}/sqldatabases" -f $FabricConfig.BaseUrl, $WorkspaceId - Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug - - # Step 3: Construct the request body - $body = @{ - displayName = $Name - } - - if ($Description) { - $body.description = $Description - } - - $bodyJson = $body | ConvertTo-Json -Depth 10 - Write-Message -Message "Request Body: $bodyJson" -Level Debug - - # Step 4: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Post ` - -Body $bodyJson ` - -ContentType "application/json" ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -ResponseHeadersVariable "responseHeader" ` - -StatusCodeVariable "statusCode" - - # Step 5: Handle and log the response - Write-Message "RESPONSE: $response" -Level Debug - Test-FabricApiResponse -response $response -responseHeader $responseHeader -statusCode $statusCode -Name $Name -TypeName 'SQL Database' - } - catch { - # Step 6: Handle and log errors - $errorDetails = $_.Exception.Message - Write-Message -Message "Failed to create SQL Database. Error: $errorDetails" -Level Error - } -} diff --git a/FabricTools/public/Spark Job Definition/Start-FabricSparkJobDefinitionOnDemand.ps1 b/FabricTools/public/Spark Job Definition/Start-FabricSparkJobDefinitionOnDemand.ps1 deleted file mode 100644 index 43bb3dac..00000000 --- a/FabricTools/public/Spark Job Definition/Start-FabricSparkJobDefinitionOnDemand.ps1 +++ /dev/null @@ -1,118 +0,0 @@ -<# -.SYNOPSIS - Starts a Fabric Spark Job Definition on demand. - -.DESCRIPTION - This function initiates a Spark Job Definition on demand within a specified workspace. - It constructs the appropriate API endpoint URL and makes a POST request to start the job. - The function can optionally wait for the job to complete based on the 'waitForCompletion' parameter. - -.PARAMETER WorkspaceId - The ID of the workspace where the Spark Job Definition is located. This parameter is mandatory. - -.PARAMETER SparkJobDefinitionId - The ID of the Spark Job Definition to be started. This parameter is mandatory. - -.PARAMETER JobType - The type of job to be started. The default value is 'sparkjob'. This parameter is optional. - -.PARAMETER waitForCompletion - A boolean flag indicating whether to wait for the job to complete. The default value is $false. This parameter is optional. - -.EXAMPLE - Start-FabricSparkJobDefinitionOnDemand -WorkspaceId "workspace123" -SparkJobDefinitionId "jobdef456" -waitForCompletion $true - -.NOTES - Ensure that the necessary authentication tokens are valid before running this function. - The function logs detailed messages for debugging and informational purposes. -#> -function Start-FabricSparkJobDefinitionOnDemand { - [CmdletBinding()] - param ( - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [string]$WorkspaceId, - - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [string]$SparkJobDefinitionId, - - [Parameter(Mandatory = $false)] - [ValidateNotNullOrEmpty()] - [ValidateSet('sparkjob')] - [string]$JobType = "sparkjob", - - [Parameter(Mandatory = $false)] - [ValidateNotNullOrEmpty()] - [bool]$waitForCompletion = $false - ) - - try { - # Step 1: Ensure token validity - Write-Message -Message "Validating token..." -Level Debug - Test-TokenExpired - Write-Message -Message "Token validation completed." -Level Debug - - # Step 2: Construct the API URL - $apiEndpointUrl = "{0}/workspaces/{1}/SparkJobDefinitions/{2}/jobs/instances?jobType={3}" -f $FabricConfig.BaseUrl, $WorkspaceId , $SparkJobDefinitionId, $JobType - Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug - - # Step 4: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Post ` - -ContentType "application/json" ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -ResponseHeadersVariable "responseHeader" ` - -StatusCodeVariable "statusCode" - - Write-Message -Message "Response Code: $statusCode" -Level Debug - # Step 5: Handle and log the response - switch ($statusCode) { - 201 { - Write-Message -Message "Spark Job Definition on demand successfully initiated for SparkJobDefinition '$SparkJobDefinition.displayName'." -Level Info - return $response - } - 202 { - Write-Message -Message "Spark Job Definition on demand accepted and is now running in the background. Job execution is in progress." -Level Info - [string]$operationId = $responseHeader["x-ms-operation-id"] - [string]$location = $responseHeader["Location"] - [string]$retryAfter = $responseHeader["Retry-After"] - - Write-Message -Message "Operation ID: '$operationId'" -Level Debug - Write-Message -Message "Location: '$location'" -Level Debug - Write-Message -Message "Retry-After: '$retryAfter'" -Level Debug - - if ($waitForCompletion -eq $true) { - Write-Message -Message "Getting Long Running Operation status" -Level Debug - $operationStatus = Get-FabricLongRunningOperation -operationId $operationId -location $location -retryAfter $retryAfter - Write-Message -Message "Long Running Operation status: $operationStatus" -Level Debug - return $operationStatus - } - else { - Write-Message -Message "The operation is running asynchronously." -Level Info - Write-Message -Message "Use the returned details to check the operation status." -Level Info - Write-Message -Message "To wait for the operation to complete, set the 'waitForCompletion' parameter to true." -Level Info - $operationDetails = [PSCustomObject]@{ - OperationId = $operationId - Location = $location - RetryAfter = $retryAfter - } - return $operationDetails - } - } - default { - Write-Message -Message "Unexpected response code: $statusCode" -Level Error - Write-Message -Message "Error details: $($response.message)" -Level Error - throw "API request failed with status code $statusCode." - } - } - } - catch { - # Step 6: Handle and log errors - $errorDetails = $_.Exception.Message - Write-Message -Message "Failed to start Spark Job Definition on demand. Error: $errorDetails" -Level Error - } -} diff --git a/FabricTools/public/Tenant/Get-FabricTenantSettings.ps1 b/FabricTools/public/Tenant/Get-FabricTenantSettings.ps1 deleted file mode 100644 index 51f88e64..00000000 --- a/FabricTools/public/Tenant/Get-FabricTenantSettings.ps1 +++ /dev/null @@ -1,27 +0,0 @@ - -<# -.SYNOPSIS -Retrieves the tenant settings from the Fabric API. - -.DESCRIPTION -The Get-FabricTenantSettings function makes a GET request to the Fabric API to retrieve the tenant settings. It returns the 'tenantSettings' property of the first item in the response. - -.PARAMETER None -This function does not have any parameters. - -.EXAMPLE -Get-FabricTenantSettings -Retrieves the tenant settings from the Fabric API. - -#> - -function Get-FabricTenantSettings { - [Alias("Get-FabTenantSettings")] - Param () - - Confirm-FabricAuthToken | Out-Null - - $result = Invoke-FabricAPIRequest -uri "admin/tenantsettings" -Method GET - - return $result.tenantSettings -} \ No newline at end of file diff --git a/FabricTools/public/Utils/Test-FabricApiResponse.ps1 b/FabricTools/public/Utils/Test-FabricApiResponse.ps1 deleted file mode 100644 index f27f261b..00000000 --- a/FabricTools/public/Utils/Test-FabricApiResponse.ps1 +++ /dev/null @@ -1,53 +0,0 @@ -function Test-FabricApiResponse { - [CmdletBinding()] - param ( - [Parameter(Mandatory = $true)] - $statusCode, - [Parameter(Mandatory = $false)] - $response, - [Parameter(Mandatory = $false)] - $responseHeader, - [Parameter(Mandatory = $false)] - $Name, - [Parameter(Mandatory = $false)] - $typeName = 'Fabric Item' - ) - - switch ($statusCode) { - 201 { - Write-Message -Message "$typeName '$Name' created successfully!" -Level Info - return $response - } - 202 { - Write-Message -Message "$typeName '$Name' creation accepted. Provisioning in progress!" -Level Info - - [string]$operationId = $responseHeader["x-ms-operation-id"] - Write-Message -Message "Operation ID: '$operationId'" -Level Debug - Write-Message -Message "Getting Long Running Operation status" -Level Debug - - $operationStatus = Get-FabricLongRunningOperation -operationId $operationId - Write-Message -Message "Long Running Operation status: $operationStatus" -Level Debug - # Handle operation result - if ($operationStatus.status -eq "Succeeded") { - Write-Message -Message "Operation Succeeded" -Level Debug - Write-Message -Message "Getting Long Running Operation result" -Level Debug - - $operationResult = Get-FabricLongRunningOperationResult -operationId $operationId - Write-Message -Message "Long Running Operation status: $operationResult" -Level Debug - - return $operationResult - } - else { - Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Debug - Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Error - return $operationStatus - } - } - default { - Write-Message -Message "Unexpected response code: $statusCode" -Level Error - Write-Message -Message "Error details: $($response.message)" -Level Error - throw "API request failed with status code $statusCode." - } - } - -} diff --git a/GitVersion.yml b/GitVersion.yml new file mode 100644 index 00000000..21fc5dab --- /dev/null +++ b/GitVersion.yml @@ -0,0 +1,39 @@ +mode: ContinuousDelivery +next-version: 0.11.0 +major-version-bump-message: '(breaking\schange|breaking|major)\b' +minor-version-bump-message: '(adds?|features?|minor)\b' +patch-version-bump-message: '\s?(fix|patch)' +no-bump-message: '\+semver:\s?(none|skip)' +assembly-informational-format: '{NuGetVersionV2}+Sha.{Sha}.Date.{CommitDate}' +branches: + master: + tag: preview + regex: ^ It lets you pause and resume Fabric capacities.$ + pull-request: + tag: PR + feature: + tag: useBranchName + increment: Minor + regex: f(eature(s)?)?[\/-] + source-branches: ['master'] + hotfix: + tag: fix + increment: Patch + regex: (hot)?fix(es)?[\/-] + source-branches: ['master'] + +ignore: + sha: [] +merge-message-formats: {} + + +# feature: +# tag: useBranchName +# increment: Minor +# regex: f(eature(s)?)?[/-] +# source-branches: ['master'] +# hotfix: +# tag: fix +# increment: Patch +# regex: (hot)?fix(es)?[/-] +# source-branches: ['master'] diff --git a/README.md b/README.md index 5eb44acb..212ccef8 100644 --- a/README.md +++ b/README.md @@ -79,14 +79,14 @@ Set-FabricAuthToken -reset The entire history of changes to this module can be find here: [Release Notes](ReleaseNotes.md) +## Code of Conduct +This project and everyone participating in it is governed by the [Code of Conduct](CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. Please report unacceptable behavior to the project team. ## Contributing Contributions to FabricTools are welcome. -> Note: In this early stage of development, we are working hard to build strong foundations for this module and further contribution guidance to ensure the quality of this code. **Therefore, please get in touch with us before you commit any code and create a PR.** - -Please read [CONTRIBUTING.md](CONTRIBUTING.md) for details on our code of conduct, and the process for submitting pull requests to us. +Please read [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to develop, test and the process for submitting pull requests to us. ## Authors diff --git a/ReleaseNotes.md b/ReleaseNotes.md deleted file mode 100644 index cdd10135..00000000 --- a/ReleaseNotes.md +++ /dev/null @@ -1,54 +0,0 @@ -# Release Notes - -## Version 0.10.0: -* Added new functions: -- `New-FabricLakehouse` -- `Get-FabricConnection` - -## Version 0.9.0: -* Added few new functions: - - 'Get-FabricSQLDatabase', - - 'Remove-FabricSQLDatabase', - - 'Get-FabricCapacitySkus', - - 'Confirm-FabricAuthToken' -* Unified objects in module scope (`FabricSession`, `AzureSession`) -* Module scope holds all api urls, header params for requests -* Unified function to authenticate to Fabric API with bearer token -* Auto-refresh token when expires -* Removed `$authToken` as input param in several functions -* Removed usage of `$env:azToken` from several functions -* Get-FabricItem accepts Workspaces as input-pipeline -* Minor cosmetic changes - -## Version 0.8.0.0: -- Import original code from FabTools project authored by [Ioana Bouariu](https://github.com/Jojobit) -- Import functions from powerrti project authored by [Frank Geisler](https://github.com/Frank-Geisler) -- Rename project to FabricTools with new guid -- Refactoring of few functions -- Documentation generated & helper folder with scripts - -## Version 0.7.0.4: -- Fix a bug where the Fabric API didn't have a result URI - -## Version 0.7.0.3: -- Fixed the functions related to checking, pausing and activating Fabric capacities in Azure - -## Version 0.7.0.2: -- Fixed a bug that made the the module return an error on the first attempt to get data from the Rest API. - -## Version 0.7.0.1: -- Removed the parameter outfile in the function Invoke-FabricAPIRequest, as it led to an error in PowerShell version 7.4 - -## Version 0.7.0: -- The official Rest API for Microsoft Fabric is now Public. This means that there are a lot of new possibilities for this module. -- After a great talk with Rui Romano, he's graciously allowed us to integrate the functions from his project: fabricps-pbip ([GitHub Repository](https://github.com/RuiRomano/fabricps-pbip)) into Fabtools. -- Lots of new functions to make it easier to work with Microsoft Fabric. -- It is now possible to export and import items from a workspace. Currently that includes reports (pbip), semantic models (datasets), spark jobs, and notebooks (ipynb). -- It is now possible to register and unregister a workspace to/from a capacity. -- Several functions have been rewritten to use the new fabric API endpoint rather than the old PowerBI API endpoint. - -## Version 0.6.0: -- Added Get-AllFabricCapacities function to get all capacities in a tenant. -- Added Invoke-FabricDatasetRefresh function to refresh a dataset. -- Changed the main functions to be with the Fabric prefix instead of Fab, and added Fab as aliases. -- Added IconUri to the manifest. diff --git a/RequiredModules.psd1 b/RequiredModules.psd1 new file mode 100644 index 00000000..74581735 --- /dev/null +++ b/RequiredModules.psd1 @@ -0,0 +1,27 @@ +@{ + <# + This is only required if you need to use the method PowerShellGet & PSDepend + It is not required for PSResourceGet or ModuleFast (and will be ignored). + See Resolve-Dependency.psd1 on how to enable methods. + #> + #PSDependOptions = @{ + # AddToPath = $true + # Target = 'output\RequiredModules' + # Parameters = @{ + # Repository = 'PSGallery' + # } + #} + Assert = "0.9.6" + InvokeBuild = 'latest' + PSScriptAnalyzer = '1.19.1' + Pester = 'latest' + ModuleBuilder = 'latest' + ChangelogManagement = 'latest' + Sampler = 'latest' + 'Sampler.GitHubTasks' = 'latest' + MarkdownLinkCheck = 'latest' + PSFramework = 'latest' + 'Az.Accounts' = 'latest' + 'Az.Resources' = 'latest' + 'MicrosoftPowerBIMgmt' = 'latest' +} diff --git a/Resolve-Dependency.ps1 b/Resolve-Dependency.ps1 new file mode 100644 index 00000000..17cc98ec --- /dev/null +++ b/Resolve-Dependency.ps1 @@ -0,0 +1,1060 @@ +<# + .DESCRIPTION + Bootstrap script for PSDepend. + + .PARAMETER DependencyFile + Specifies the configuration file for the this script. The default value is + 'RequiredModules.psd1' relative to this script's path. + + .PARAMETER PSDependTarget + Path for PSDepend to be bootstrapped and save other dependencies. + Can also be CurrentUser or AllUsers if you wish to install the modules in + such scope. The default value is 'output/RequiredModules' relative to + this script's path. + + .PARAMETER Proxy + Specifies the URI to use for Proxy when attempting to bootstrap + PackageProvider and PowerShellGet. + + .PARAMETER ProxyCredential + Specifies the credential to contact the Proxy when provided. + + .PARAMETER Scope + Specifies the scope to bootstrap the PackageProvider and PSGet if not available. + THe default value is 'CurrentUser'. + + .PARAMETER Gallery + Specifies the gallery to use when bootstrapping PackageProvider, PSGet and + when calling PSDepend (can be overridden in Dependency files). The default + value is 'PSGallery'. + + .PARAMETER GalleryCredential + Specifies the credentials to use with the Gallery specified above. + + .PARAMETER AllowOldPowerShellGetModule + Allow you to use a locally installed version of PowerShellGet older than + 1.6.0 (not recommended). Default it will install the latest PowerShellGet + if an older version than 2.0 is detected. + + .PARAMETER MinimumPSDependVersion + Allow you to specify a minimum version fo PSDepend, if you're after specific + features. + + .PARAMETER AllowPrerelease + Not yet written. + + .PARAMETER WithYAML + Not yet written. + + .PARAMETER UseModuleFast + Specifies to use ModuleFast instead of PowerShellGet to resolve dependencies + faster. + + .PARAMETER ModuleFastBleedingEdge + Specifies to use ModuleFast code that is in the ModuleFast's main branch + in its GitHub repository. The parameter UseModuleFast must also be set to + true. + + .PARAMETER UsePSResourceGet + Specifies to use the new PSResourceGet module instead of the (now legacy) PowerShellGet module. + + .PARAMETER PSResourceGetVersion + String specifying the module version for PSResourceGet if the `UsePSResourceGet` switch is utilized. + + .NOTES + Load defaults for parameters values from Resolve-Dependency.psd1 if not + provided as parameter. +#> +[CmdletBinding()] +param +( + [Parameter()] + [System.String] + $DependencyFile = 'RequiredModules.psd1', + + [Parameter()] + [System.String] + $PSDependTarget = (Join-Path -Path $PSScriptRoot -ChildPath 'output/RequiredModules'), + + [Parameter()] + [System.Uri] + $Proxy, + + [Parameter()] + [System.Management.Automation.PSCredential] + $ProxyCredential, + + [Parameter()] + [ValidateSet('CurrentUser', 'AllUsers')] + [System.String] + $Scope = 'CurrentUser', + + [Parameter()] + [System.String] + $Gallery = 'PSGallery', + + [Parameter()] + [System.Management.Automation.PSCredential] + $GalleryCredential, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $AllowOldPowerShellGetModule, + + [Parameter()] + [System.String] + $MinimumPSDependVersion, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $AllowPrerelease, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $WithYAML, + + [Parameter()] + [System.Collections.Hashtable] + $RegisterGallery, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $UseModuleFast, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $ModuleFastBleedingEdge, + + [Parameter()] + [System.String] + $ModuleFastVersion, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $UsePSResourceGet, + + [Parameter()] + [System.String] + $PSResourceGetVersion, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $UsePowerShellGetCompatibilityModule, + + [Parameter()] + [System.String] + $UsePowerShellGetCompatibilityModuleVersion +) + +try +{ + if ($PSVersionTable.PSVersion.Major -le 5) + { + if (-not (Get-Command -Name 'Import-PowerShellDataFile' -ErrorAction 'SilentlyContinue')) + { + Import-Module -Name Microsoft.PowerShell.Utility -RequiredVersion '3.1.0.0' + } + } + + Write-Verbose -Message 'Importing Bootstrap default parameters from ''$PSScriptRoot/Resolve-Dependency.psd1''.' + + $resolveDependencyConfigPath = Join-Path -Path $PSScriptRoot -ChildPath '.\Resolve-Dependency.psd1' -Resolve -ErrorAction 'Stop' + + $resolveDependencyDefaults = Import-PowerShellDataFile -Path $resolveDependencyConfigPath + + $parameterToDefault = $MyInvocation.MyCommand.ParameterSets.Where{ $_.Name -eq $PSCmdlet.ParameterSetName }.Parameters.Keys + + if ($parameterToDefault.Count -eq 0) + { + $parameterToDefault = $MyInvocation.MyCommand.Parameters.Keys + } + + # Set the parameters available in the Parameter Set, or it's not possible to choose yet, so all parameters are an option. + foreach ($parameterName in $parameterToDefault) + { + if (-not $PSBoundParameters.Keys.Contains($parameterName) -and $resolveDependencyDefaults.ContainsKey($parameterName)) + { + Write-Verbose -Message "Setting parameter '$parameterName' to value '$($resolveDependencyDefaults[$parameterName])'." + + try + { + $variableValue = $resolveDependencyDefaults[$parameterName] + + if ($variableValue -is [System.String]) + { + $variableValue = $ExecutionContext.InvokeCommand.ExpandString($variableValue) + } + + $PSBoundParameters.Add($parameterName, $variableValue) + + Set-Variable -Name $parameterName -Value $variableValue -Force -ErrorAction 'SilentlyContinue' + } + catch + { + Write-Verbose -Message "Error adding default for $parameterName : $($_.Exception.Message)." + } + } + } +} +catch +{ + Write-Warning -Message "Error attempting to import Bootstrap's default parameters from '$resolveDependencyConfigPath': $($_.Exception.Message)." +} + +# Handle when both ModuleFast and PSResourceGet is configured or/and passed as parameter. +if ($UseModuleFast -and $UsePSResourceGet) +{ + Write-Information -MessageData 'Both ModuleFast and PSResourceGet is configured or/and passed as parameter.' -InformationAction 'Continue' + + if ($PSVersionTable.PSVersion -ge '7.2') + { + $UsePSResourceGet = $false + + Write-Information -MessageData 'PowerShell 7.2 or higher being used, prefer ModuleFast over PSResourceGet.' -InformationAction 'Continue' + } + else + { + $UseModuleFast = $false + + Write-Information -MessageData 'Windows PowerShell or PowerShell <=7.1 is being used, prefer PSResourceGet since ModuleFast is not supported on this version of PowerShell.' -InformationAction 'Continue' + } +} + +# Only bootstrap ModuleFast if it is not already imported. +if ($UseModuleFast -and -not (Get-Module -Name 'ModuleFast')) +{ + try + { + $moduleFastBootstrapScriptBlockParameters = @{} + + if ($ModuleFastBleedingEdge) + { + Write-Information -MessageData 'ModuleFast is configured to use Bleeding Edge (directly from ModuleFast''s main branch).' -InformationAction 'Continue' + + $moduleFastBootstrapScriptBlockParameters.UseMain = $true + } + elseif($ModuleFastVersion) + { + if ($ModuleFastVersion -notmatch 'v') + { + $ModuleFastVersion = 'v{0}' -f $ModuleFastVersion + } + + Write-Information -MessageData ('ModuleFast is configured to use version {0}.' -f $ModuleFastVersion) -InformationAction 'Continue' + + $moduleFastBootstrapScriptBlockParameters.Release = $ModuleFastVersion + } + else + { + Write-Information -MessageData 'ModuleFast is configured to use latest released version.' -InformationAction 'Continue' + } + + $moduleFastBootstrapUri = 'bit.ly/modulefast' # cSpell: disable-line + + Write-Debug -Message ('Using bootstrap script at {0}' -f $moduleFastBootstrapUri) + + $invokeWebRequestParameters = @{ + Uri = $moduleFastBootstrapUri + ErrorAction = 'Stop' + } + + $moduleFastBootstrapScript = Invoke-WebRequest @invokeWebRequestParameters + + $moduleFastBootstrapScriptBlock = [ScriptBlock]::Create($moduleFastBootstrapScript) + + & $moduleFastBootstrapScriptBlock @moduleFastBootstrapScriptBlockParameters + } + catch + { + Write-Warning -Message ('ModuleFast could not be bootstrapped. Reverting to PSResourceGet. Error: {0}' -f $_.Exception.Message) + + $UseModuleFast = $false + $UsePSResourceGet = $true + } +} + +if ($UsePSResourceGet) +{ + $psResourceGetModuleName = 'Microsoft.PowerShell.PSResourceGet' + + # If PSResourceGet was used prior it will be locked and we can't replace it. + if ((Test-Path -Path "$PSDependTarget/$psResourceGetModuleName" -PathType 'Container') -and (Get-Module -Name $psResourceGetModuleName)) + { + Write-Information -MessageData ('{0} is already bootstrapped and imported into the session. If there is a need to refresh the module, open a new session and resolve dependencies again.' -f $psResourceGetModuleName) -InformationAction 'Continue' + } + else + { + Write-Debug -Message ('{0} do not exist, saving the module to RequiredModules.' -f $psResourceGetModuleName) + + $psResourceGetDownloaded = $false + + try + { + if (-not $PSResourceGetVersion) + { + # Default to latest version if no version is passed in parameter or specified in configuration. + $psResourceGetUri = "https://www.powershellgallery.com/api/v2/package/$psResourceGetModuleName" + } + else + { + $psResourceGetUri = "https://www.powershellgallery.com/api/v2/package/$psResourceGetModuleName/$PSResourceGetVersion" + } + + $invokeWebRequestParameters = @{ + # TODO: Should support proxy parameters passed to the script. + Uri = $psResourceGetUri + OutFile = "$PSDependTarget/$psResourceGetModuleName.nupkg" # cSpell: ignore nupkg + ErrorAction = 'Stop' + } + + $previousProgressPreference = $ProgressPreference + $ProgressPreference = 'SilentlyContinue' + + # Bootstrapping Microsoft.PowerShell.PSResourceGet. + Invoke-WebRequest @invokeWebRequestParameters + + $ProgressPreference = $previousProgressPreference + + $psResourceGetDownloaded = $true + } + catch + { + Write-Warning -Message ('{0} could not be bootstrapped. Reverting to PowerShellGet. Error: {1}' -f $psResourceGetModuleName, $_.Exception.Message) + } + + $UsePSResourceGet = $false + + if ($psResourceGetDownloaded) + { + # On Windows PowerShell the command Expand-Archive do not like .nupkg as a zip archive extension. + $zipFileName = ((Split-Path -Path $invokeWebRequestParameters.OutFile -Leaf) -replace 'nupkg', 'zip') + + $renameItemParameters = @{ + Path = $invokeWebRequestParameters.OutFile + NewName = $zipFileName + Force = $true + } + + Rename-Item @renameItemParameters + + $psResourceGetZipArchivePath = Join-Path -Path (Split-Path -Path $invokeWebRequestParameters.OutFile -Parent) -ChildPath $zipFileName + + $expandArchiveParameters = @{ + Path = $psResourceGetZipArchivePath + DestinationPath = "$PSDependTarget/$psResourceGetModuleName" + Force = $true + } + + Expand-Archive @expandArchiveParameters + + Remove-Item -Path $psResourceGetZipArchivePath + + Import-Module -Name $expandArchiveParameters.DestinationPath -Force + + # Successfully bootstrapped PSResourceGet, so let's use it. + $UsePSResourceGet = $true + } + } + + if ($UsePSResourceGet) + { + $psResourceGetModule = Get-Module -Name $psResourceGetModuleName + + $psResourceGetModuleVersion = $psResourceGetModule.Version.ToString() + + if ($psResourceGetModule.PrivateData.PSData.Prerelease) + { + $psResourceGetModuleVersion += '-{0}' -f $psResourceGetModule.PrivateData.PSData.Prerelease + } + + Write-Information -MessageData ('Using {0} v{1}.' -f $psResourceGetModuleName, $psResourceGetModuleVersion) -InformationAction 'Continue' + + if ($UsePowerShellGetCompatibilityModule) + { + $savePowerShellGetParameters = @{ + Name = 'PowerShellGet' + Path = $PSDependTarget + Repository = 'PSGallery' + TrustRepository = $true + } + + if ($UsePowerShellGetCompatibilityModuleVersion) + { + $savePowerShellGetParameters.Version = $UsePowerShellGetCompatibilityModuleVersion + + # Check if the version is a prerelease. + if ($UsePowerShellGetCompatibilityModuleVersion -match '\d+\.\d+\.\d+-.*') + { + $savePowerShellGetParameters.Prerelease = $true + } + } + + Save-PSResource @savePowerShellGetParameters + + Import-Module -Name "$PSDependTarget/PowerShellGet" + } + } +} + +# Check if legacy PowerShellGet and PSDepend must be bootstrapped. +if (-not ($UseModuleFast -or $UsePSResourceGet)) +{ + if ($PSVersionTable.PSVersion.Major -le 5) + { + <# + Making sure the imported PackageManagement module is not from PS7 module + path. The VSCode PS extension is changing the $env:PSModulePath and + prioritize the PS7 path. This is an issue with PowerShellGet because + it loads an old version if available (or fail to load latest). + #> + Get-Module -ListAvailable PackageManagement | + Where-Object -Property 'ModuleBase' -NotMatch 'powershell.7' | + Select-Object -First 1 | + Import-Module -Force + } + + Write-Progress -Activity 'Bootstrap:' -PercentComplete 0 -CurrentOperation 'NuGet Bootstrap' + + $importModuleParameters = @{ + Name = 'PowerShellGet' + MinimumVersion = '2.0' + MaximumVersion = '2.8.999' + ErrorAction = 'SilentlyContinue' + PassThru = $true + } + + if ($AllowOldPowerShellGetModule) + { + $importModuleParameters.Remove('MinimumVersion') + } + + $powerShellGetModule = Import-Module @importModuleParameters + + # Install the package provider if it is not available. + $nuGetProvider = Get-PackageProvider -Name 'NuGet' -ListAvailable -ErrorAction 'SilentlyContinue' | + Select-Object -First 1 + + if (-not $powerShellGetModule -and -not $nuGetProvider) + { + $providerBootstrapParameters = @{ + Name = 'NuGet' + Force = $true + ForceBootstrap = $true + ErrorAction = 'Stop' + Scope = $Scope + } + + switch ($PSBoundParameters.Keys) + { + 'Proxy' + { + $providerBootstrapParameters.Add('Proxy', $Proxy) + } + + 'ProxyCredential' + { + $providerBootstrapParameters.Add('ProxyCredential', $ProxyCredential) + } + + 'AllowPrerelease' + { + $providerBootstrapParameters.Add('AllowPrerelease', $AllowPrerelease) + } + } + + Write-Information -MessageData 'Bootstrap: Installing NuGet Package Provider from the web (Make sure Microsoft addresses/ranges are allowed).' + + $null = Install-PackageProvider @providerBootstrapParameters + + $nuGetProvider = Get-PackageProvider -Name 'NuGet' -ListAvailable | Select-Object -First 1 + + $nuGetProviderVersion = $nuGetProvider.Version.ToString() + + Write-Information -MessageData "Bootstrap: Importing NuGet Package Provider version $nuGetProviderVersion to current session." + + $Null = Import-PackageProvider -Name 'NuGet' -RequiredVersion $nuGetProviderVersion -Force + } + + if ($RegisterGallery) + { + if ($RegisterGallery.ContainsKey('Name') -and -not [System.String]::IsNullOrEmpty($RegisterGallery.Name)) + { + $Gallery = $RegisterGallery.Name + } + else + { + $RegisterGallery.Name = $Gallery + } + + Write-Progress -Activity 'Bootstrap:' -PercentComplete 7 -CurrentOperation "Verifying private package repository '$Gallery'" -Completed + + $previousRegisteredRepository = Get-PSRepository -Name $Gallery -ErrorAction 'SilentlyContinue' + + if ($previousRegisteredRepository.SourceLocation -ne $RegisterGallery.SourceLocation) + { + if ($previousRegisteredRepository) + { + Write-Progress -Activity 'Bootstrap:' -PercentComplete 9 -CurrentOperation "Re-registrering private package repository '$Gallery'" -Completed + + Unregister-PSRepository -Name $Gallery + + $unregisteredPreviousRepository = $true + } + else + { + Write-Progress -Activity 'Bootstrap:' -PercentComplete 9 -CurrentOperation "Registering private package repository '$Gallery'" -Completed + } + + Register-PSRepository @RegisterGallery + } + } + + Write-Progress -Activity 'Bootstrap:' -PercentComplete 10 -CurrentOperation "Ensuring Gallery $Gallery is trusted" + + # Fail if the given PSGallery is not registered. + $previousGalleryInstallationPolicy = (Get-PSRepository -Name $Gallery -ErrorAction 'Stop').Trusted + + $updatedGalleryInstallationPolicy = $false + + if ($previousGalleryInstallationPolicy -ne $true) + { + $updatedGalleryInstallationPolicy = $true + + # Only change policy if the repository is not trusted + Set-PSRepository -Name $Gallery -InstallationPolicy 'Trusted' -ErrorAction 'Ignore' + } +} + +try +{ + # Check if legacy PowerShellGet and PSDepend must be used. + if (-not ($UseModuleFast -or $UsePSResourceGet)) + { + Write-Progress -Activity 'Bootstrap:' -PercentComplete 25 -CurrentOperation 'Checking PowerShellGet' + + # Ensure the module is loaded and retrieve the version you have. + $powerShellGetVersion = (Import-Module -Name 'PowerShellGet' -PassThru -ErrorAction 'SilentlyContinue').Version + + Write-Verbose -Message "Bootstrap: The PowerShellGet version is $powerShellGetVersion" + + # Versions below 2.0 are considered old, unreliable & not recommended + if (-not $powerShellGetVersion -or ($powerShellGetVersion -lt [System.Version] '2.0' -and -not $AllowOldPowerShellGetModule)) + { + Write-Progress -Activity 'Bootstrap:' -PercentComplete 40 -CurrentOperation 'Fetching newer version of PowerShellGet' + + # PowerShellGet module not found, installing or saving it. + if ($PSDependTarget -in 'CurrentUser', 'AllUsers') + { + Write-Debug -Message "PowerShellGet module not found. Attempting to install from Gallery $Gallery." + + Write-Warning -Message "Installing PowerShellGet in $PSDependTarget Scope." + + $installPowerShellGetParameters = @{ + Name = 'PowerShellGet' + Force = $true + SkipPublisherCheck = $true + AllowClobber = $true + Scope = $Scope + Repository = $Gallery + MaximumVersion = '2.8.999' + } + + switch ($PSBoundParameters.Keys) + { + 'Proxy' + { + $installPowerShellGetParameters.Add('Proxy', $Proxy) + } + + 'ProxyCredential' + { + $installPowerShellGetParameters.Add('ProxyCredential', $ProxyCredential) + } + + 'GalleryCredential' + { + $installPowerShellGetParameters.Add('Credential', $GalleryCredential) + } + } + + Write-Progress -Activity 'Bootstrap:' -PercentComplete 60 -CurrentOperation 'Installing newer version of PowerShellGet' + + Install-Module @installPowerShellGetParameters + } + else + { + Write-Debug -Message "PowerShellGet module not found. Attempting to Save from Gallery $Gallery to $PSDependTarget" + + $saveModuleParameters = @{ + Name = 'PowerShellGet' + Repository = $Gallery + Path = $PSDependTarget + Force = $true + MaximumVersion = '2.8.999' + } + + Write-Progress -Activity 'Bootstrap:' -PercentComplete 60 -CurrentOperation "Saving PowerShellGet from $Gallery to $Scope" + + Save-Module @saveModuleParameters + } + + Write-Debug -Message 'Removing previous versions of PowerShellGet and PackageManagement from session' + + Get-Module -Name 'PowerShellGet' -All | Remove-Module -Force -ErrorAction 'SilentlyContinue' + Get-Module -Name 'PackageManagement' -All | Remove-Module -Force + + Write-Progress -Activity 'Bootstrap:' -PercentComplete 65 -CurrentOperation 'Loading latest version of PowerShellGet' + + Write-Debug -Message 'Importing latest PowerShellGet and PackageManagement versions into session' + + if ($AllowOldPowerShellGetModule) + { + $powerShellGetModule = Import-Module -Name 'PowerShellGet' -Force -PassThru + } + else + { + Import-Module -Name 'PackageManagement' -MinimumVersion '1.4.8.1' -Force + + $powerShellGetModule = Import-Module -Name 'PowerShellGet' -MinimumVersion '2.2.5' -Force -PassThru + } + + $powerShellGetVersion = $powerShellGetModule.Version.ToString() + + Write-Information -MessageData "Bootstrap: PowerShellGet version loaded is $powerShellGetVersion" + } + + # Try to import the PSDepend module from the available modules. + $getModuleParameters = @{ + Name = 'PSDepend' + ListAvailable = $true + } + + $psDependModule = Get-Module @getModuleParameters + + if ($PSBoundParameters.ContainsKey('MinimumPSDependVersion')) + { + try + { + $psDependModule = $psDependModule | Where-Object -FilterScript { $_.Version -ge $MinimumPSDependVersion } + } + catch + { + throw ('There was a problem finding the minimum version of PSDepend. Error: {0}' -f $_) + } + } + + if (-not $psDependModule) + { + Write-Debug -Message 'PSDepend module not found.' + + # PSDepend module not found, installing or saving it. + if ($PSDependTarget -in 'CurrentUser', 'AllUsers') + { + Write-Debug -Message "Attempting to install from Gallery '$Gallery'." + + Write-Warning -Message "Installing PSDepend in $PSDependTarget Scope." + + $installPSDependParameters = @{ + Name = 'PSDepend' + Repository = $Gallery + Force = $true + Scope = $PSDependTarget + SkipPublisherCheck = $true + AllowClobber = $true + } + + if ($MinimumPSDependVersion) + { + $installPSDependParameters.Add('MinimumVersion', $MinimumPSDependVersion) + } + + Write-Progress -Activity 'Bootstrap:' -PercentComplete 75 -CurrentOperation "Installing PSDepend from $Gallery" + + Install-Module @installPSDependParameters + } + else + { + Write-Debug -Message "Attempting to Save from Gallery $Gallery to $PSDependTarget" + + $saveModuleParameters = @{ + Name = 'PSDepend' + Repository = $Gallery + Path = $PSDependTarget + Force = $true + } + + if ($MinimumPSDependVersion) + { + $saveModuleParameters.add('MinimumVersion', $MinimumPSDependVersion) + } + + Write-Progress -Activity 'Bootstrap:' -PercentComplete 75 -CurrentOperation "Saving PSDepend from $Gallery to $PSDependTarget" + + Save-Module @saveModuleParameters + } + } + + Write-Progress -Activity 'Bootstrap:' -PercentComplete 80 -CurrentOperation 'Importing PSDepend' + + $importModulePSDependParameters = @{ + Name = 'PSDepend' + ErrorAction = 'Stop' + Force = $true + } + + if ($PSBoundParameters.ContainsKey('MinimumPSDependVersion')) + { + $importModulePSDependParameters.Add('MinimumVersion', $MinimumPSDependVersion) + } + + # We should have successfully bootstrapped PSDepend. Fail if not available. + $null = Import-Module @importModulePSDependParameters + + Write-Progress -Activity 'Bootstrap:' -PercentComplete 81 -CurrentOperation 'Invoke PSDepend' + + if ($WithYAML) + { + Write-Progress -Activity 'Bootstrap:' -PercentComplete 82 -CurrentOperation 'Verifying PowerShell module PowerShell-Yaml' + + if (-not (Get-Module -ListAvailable -Name 'PowerShell-Yaml')) + { + Write-Progress -Activity 'Bootstrap:' -PercentComplete 85 -CurrentOperation 'Installing PowerShell module PowerShell-Yaml' + + Write-Verbose -Message "PowerShell-Yaml module not found. Attempting to Save from Gallery '$Gallery' to '$PSDependTarget'." + + $SaveModuleParam = @{ + Name = 'PowerShell-Yaml' + Repository = $Gallery + Path = $PSDependTarget + Force = $true + } + + Save-Module @SaveModuleParam + } + else + { + Write-Verbose -Message 'PowerShell-Yaml is already available' + } + + Write-Progress -Activity 'Bootstrap:' -PercentComplete 88 -CurrentOperation 'Importing PowerShell module PowerShell-Yaml' + } + } + + if (Test-Path -Path $DependencyFile) + { + if ($UseModuleFast -or $UsePSResourceGet) + { + $requiredModules = Import-PowerShellDataFile -Path $DependencyFile + + $requiredModules = $requiredModules.GetEnumerator() | + Where-Object -FilterScript { $_.Name -ne 'PSDependOptions' } + + if ($UseModuleFast) + { + Write-Progress -Activity 'Bootstrap:' -PercentComplete 90 -CurrentOperation 'Invoking ModuleFast' + + Write-Progress -Activity 'ModuleFast:' -PercentComplete 0 -CurrentOperation 'Restoring Build Dependencies' + + $modulesToSave = @( + 'PSDepend' # Always include PSDepend for backward compatibility. + ) + + if ($WithYAML) + { + $modulesToSave += 'PowerShell-Yaml' + } + + if ($UsePowerShellGetCompatibilityModule) + { + Write-Debug -Message 'PowerShellGet compatibility module is configured to be used.' + + # This is needed to ensure that the PowerShellGet compatibility module works. + $psResourceGetModuleName = 'Microsoft.PowerShell.PSResourceGet' + + if ($PSResourceGetVersion) + { + $modulesToSave += ('{0}:[{1}]' -f $psResourceGetModuleName, $PSResourceGetVersion) + } + else + { + $modulesToSave += $psResourceGetModuleName + } + + $powerShellGetCompatibilityModuleName = 'PowerShellGet' + + if ($UsePowerShellGetCompatibilityModuleVersion) + { + $modulesToSave += ('{0}:[{1}]' -f $powerShellGetCompatibilityModuleName, $UsePowerShellGetCompatibilityModuleVersion) + } + else + { + $modulesToSave += $powerShellGetCompatibilityModuleName + } + } + + foreach ($requiredModule in $requiredModules) + { + # If the RequiredModules.psd1 entry is an Hashtable then special handling is needed. + if ($requiredModule.Value -is [System.Collections.Hashtable]) + { + if (-not $requiredModule.Value.Version) + { + $requiredModuleVersion = 'latest' + } + else + { + $requiredModuleVersion = $requiredModule.Value.Version + } + + if ($requiredModuleVersion -eq 'latest') + { + $moduleNameSuffix = '' + + if ($requiredModule.Value.Parameters.AllowPrerelease -eq $true) + { + <# + Adding '!' to the module name indicate to ModuleFast + that is should also evaluate pre-releases. + #> + $moduleNameSuffix = '!' + } + + $modulesToSave += ('{0}{1}' -f $requiredModule.Name, $moduleNameSuffix) + } + else + { + $modulesToSave += ('{0}:[{1}]' -f $requiredModule.Name, $requiredModuleVersion) + } + } + else + { + if ($requiredModule.Value -eq 'latest') + { + $modulesToSave += $requiredModule.Name + } + else + { + # Handle different nuget version operators already present. + if ($requiredModule.Value -match '[!|:|[|(|,|>|<|=]') + { + $modulesToSave += ('{0}{1}' -f $requiredModule.Name, $requiredModule.Value) + } + else + { + # Assuming the version is a fixed version. + $modulesToSave += ('{0}:[{1}]' -f $requiredModule.Name, $requiredModule.Value) + } + } + } + } + + Write-Debug -Message ("Required modules to retrieve plan for:`n{0}" -f ($modulesToSave | Out-String)) + + $installModuleFastParameters = @{ + Destination = $PSDependTarget + DestinationOnly = $true + NoPSModulePathUpdate = $true + NoProfileUpdate = $true + Update = $true + Confirm = $false + } + + $moduleFastPlan = Install-ModuleFast -Specification $modulesToSave -Plan @installModuleFastParameters + + Write-Debug -Message ("Missing modules that need to be saved:`n{0}" -f ($moduleFastPlan | Out-String)) + + if ($moduleFastPlan) + { + # Clear all modules in plan from the current session so they can be fetched again. + $moduleFastPlan.Name | Get-Module | Remove-Module -Force + + $moduleFastPlan | Install-ModuleFast @installModuleFastParameters + } + else + { + Write-Verbose -Message 'All required modules were already up to date' + } + + Write-Progress -Activity 'ModuleFast:' -PercentComplete 100 -CurrentOperation 'Dependencies restored' -Completed + } + + if ($UsePSResourceGet) + { + Write-Progress -Activity 'Bootstrap:' -PercentComplete 90 -CurrentOperation 'Invoking PSResourceGet' + + $modulesToSave = @( + @{ + Name = 'PSDepend' # Always include PSDepend for backward compatibility. + } + ) + + if ($WithYAML) + { + $modulesToSave += @{ + Name = 'PowerShell-Yaml' + } + } + + # Prepare hashtable that can be concatenated to the Save-PSResource parameters. + foreach ($requiredModule in $requiredModules) + { + # If the RequiredModules.psd1 entry is an Hashtable then special handling is needed. + if ($requiredModule.Value -is [System.Collections.Hashtable]) + { + $saveModuleHashtable = @{ + Name = $requiredModule.Name + } + + if ($requiredModule.Value.Version -and $requiredModule.Value.Version -ne 'latest') + { + $saveModuleHashtable.Version = $requiredModule.Value.Version + } + + if ($requiredModule.Value.Parameters.AllowPrerelease -eq $true) + { + $saveModuleHashtable.Prerelease = $true + } + + $modulesToSave += $saveModuleHashtable + } + else + { + if ($requiredModule.Value -eq 'latest') + { + $modulesToSave += @{ + Name = $requiredModule.Name + } + } + else + { + $modulesToSave += @{ + Name = $requiredModule.Name + Version = $requiredModule.Value + } + } + } + } + + $percentagePerModule = [System.Math]::Floor(100 / $modulesToSave.Length) + + $progressPercentage = 0 + + Write-Progress -Activity 'PSResourceGet:' -PercentComplete $progressPercentage -CurrentOperation 'Restoring Build Dependencies' + + foreach ($currentModule in $modulesToSave) + { + Write-Progress -Activity 'PSResourceGet:' -PercentComplete $progressPercentage -CurrentOperation 'Restoring Build Dependencies' -Status ('Saving module {0}' -f $savePSResourceParameters.Name) + + $savePSResourceParameters = @{ + Path = $PSDependTarget + TrustRepository = $true + Confirm = $false + } + + # Concatenate the module parameters to the Save-PSResource parameters. + $savePSResourceParameters += $currentModule + + # Modules that Sampler depend on that cannot be refreshed without a new session. + $skipModule = @('PowerShell-Yaml') + + if ($savePSResourceParameters.Name -in $skipModule -and (Get-Module -Name $savePSResourceParameters.Name)) + { + Write-Progress -Activity 'PSResourceGet:' -PercentComplete $progressPercentage -CurrentOperation 'Restoring Build Dependencies' -Status ('Skipping module {0}' -f $savePSResourceParameters.Name) + + Write-Information -MessageData ('Skipping the module {0} since it cannot be refresh while loaded into the session. To refresh the module open a new session and resolve dependencies again.' -f $savePSResourceParameters.Name) -InformationAction 'Continue' + } + else + { + # Clear all module from the current session so any new version fetched will be re-imported. + Get-Module -Name $savePSResourceParameters.Name | Remove-Module -Force + + Save-PSResource @savePSResourceParameters -ErrorVariable 'savePSResourceError' + + if ($savePSResourceError) + { + Write-Warning -Message 'Save-PSResource could not save (replace) one or more dependencies. This can be due to the module is loaded into the session (and referencing assemblies). Close the current session and open a new session and try again.' + } + } + + $progressPercentage += $percentagePerModule + } + + Write-Progress -Activity 'PSResourceGet:' -PercentComplete 100 -CurrentOperation 'Dependencies restored' -Completed + } + } + else + { + Write-Progress -Activity 'Bootstrap:' -PercentComplete 90 -CurrentOperation 'Invoking PSDepend' + + Write-Progress -Activity 'PSDepend:' -PercentComplete 0 -CurrentOperation 'Restoring Build Dependencies' + + $psDependParameters = @{ + Force = $true + Path = $DependencyFile + } + + # TODO: Handle when the Dependency file is in YAML, and -WithYAML is specified. + Invoke-PSDepend @psDependParameters + + Write-Progress -Activity 'PSDepend:' -PercentComplete 100 -CurrentOperation 'Dependencies restored' -Completed + } + } + else + { + Write-Warning -Message "The dependency file '$DependencyFile' could not be found." + } + + Write-Progress -Activity 'Bootstrap:' -PercentComplete 100 -CurrentOperation 'Bootstrap complete' -Completed +} +finally +{ + if ($RegisterGallery) + { + Write-Verbose -Message "Removing private package repository '$Gallery'." + Unregister-PSRepository -Name $Gallery + } + + if ($unregisteredPreviousRepository) + { + Write-Verbose -Message "Reverting private package repository '$Gallery' to previous location URI:s." + + $registerPSRepositoryParameters = @{ + Name = $previousRegisteredRepository.Name + InstallationPolicy = $previousRegisteredRepository.InstallationPolicy + } + + if ($previousRegisteredRepository.SourceLocation) + { + $registerPSRepositoryParameters.SourceLocation = $previousRegisteredRepository.SourceLocation + } + + if ($previousRegisteredRepository.PublishLocation) + { + $registerPSRepositoryParameters.PublishLocation = $previousRegisteredRepository.PublishLocation + } + + if ($previousRegisteredRepository.ScriptSourceLocation) + { + $registerPSRepositoryParameters.ScriptSourceLocation = $previousRegisteredRepository.ScriptSourceLocation + } + + if ($previousRegisteredRepository.ScriptPublishLocation) + { + $registerPSRepositoryParameters.ScriptPublishLocation = $previousRegisteredRepository.ScriptPublishLocation + } + + Register-PSRepository @registerPSRepositoryParameters + } + + if ($updatedGalleryInstallationPolicy -eq $true -and $previousGalleryInstallationPolicy -ne $true) + { + # Only try to revert installation policy if the repository exist + if ((Get-PSRepository -Name $Gallery -ErrorAction 'SilentlyContinue')) + { + # Reverting the Installation Policy for the given gallery if it was not already trusted + Set-PSRepository -Name $Gallery -InstallationPolicy 'Untrusted' + } + } + + Write-Verbose -Message 'Project Bootstrapped, returning to Invoke-Build.' +} diff --git a/Resolve-Dependency.psd1 b/Resolve-Dependency.psd1 new file mode 100644 index 00000000..c72178de --- /dev/null +++ b/Resolve-Dependency.psd1 @@ -0,0 +1,76 @@ +@{ + <# + Default parameter values to be loaded by the Resolve-Dependency.ps1 script (unless set in bound parameters + when calling the script). + #> + + #PSDependTarget = './output/modules' + #Proxy = '' + #ProxyCredential = '$MyCredentialVariable' #TODO: find a way to support credentials in build (resolve variable) + + Gallery = 'PSGallery' + + # To use a private nuget repository change the following to your own feed. The locations must be a Nuget v2 feed due + # to limitation in PowerShellGet v2.x. Example below is for a Azure DevOps Server project-scoped feed. While resolving + # dependencies it will be registered as a trusted repository with the name specified in the property 'Gallery' above, + # unless property 'Name' is provided in the hashtable below, if so it will override the property 'Gallery' above. The + # registered repository will be removed when dependencies has been resolved, unless it was already registered to begin + # with. If repository is registered already but with different URL:s the repository will be re-registered and reverted + # after dependencies has been resolved. Currently only Windows integrated security works with private Nuget v2 feeds + # (or if it is a public feed with no security), it is not possible yet to securely provide other credentials for the feed. + # Private repositories will currently only work using PowerShellGet. + #RegisterGallery = @{ + # #Name = 'MyPrivateFeedName' + # GallerySourceLocation = 'https://azdoserver.company.local///_packaging//nuget/v2' + # GalleryPublishLocation = 'https://azdoserver.company.local///_packaging//nuget/v2' + # GalleryScriptSourceLocation = 'https://azdoserver.company.local///_packaging//nuget/v2' + # GalleryScriptPublishLocation = 'https://azdoserver.company.local///_packaging//nuget/v2' + # #InstallationPolicy = 'Trusted' + #} + + #AllowOldPowerShellGetModule = $true + #MinimumPSDependVersion = '0.3.0' + AllowPrerelease = $false + WithYAML = $true # Will also bootstrap PowerShell-Yaml to read other config files + + <# + Enable ModuleFast to be the default method of resolving dependencies by setting + UseModuleFast to the value $true. ModuleFast requires PowerShell 7.2 or higher. + If UseModuleFast is not configured or set to $false then PowerShellGet (or + PSResourceGet if enabled) will be used to as the default method of resolving + dependencies. You can always use the parameter `-UseModuleFast` of the + Resolve-Dependency.ps1 or build.ps1 script even when this is not configured + or set to $false. + + You can use ModuleFastVersion to specify a specific version of ModuleFast to use. + This will also affect the use of parameter `-UseModuleFast` of the Resolve-Dependency.ps1 + or build.ps1 script. If ModuleFastVersion is not configured then the latest + (non-preview) released version is used. + + ModuleFastBleedingEdge will override ModuleFastVersion and use the absolute latest + code from the ModuleFast repository. This is useful if you want to test the absolute + latest changes in ModuleFast repository. This is not recommended for production use. + By enabling ModuleFastBleedingEdge the pipeline can encounter breaking changes or + problems by code that is merged in the ModuleFast repository, this could affect the + pipeline negatively. Make sure to use a clean PowerShell session after changing + the value of ModuleFastBleedingEdge so that ModuleFast uses the correct bootstrap + script and correct parameter values. This will also affect the use of parameter + `-UseModuleFast` of the Resolve-Dependency.ps1 or build.ps1 script. + #> + #UseModuleFast = $true + #ModuleFastVersion = '0.1.2' + #ModuleFastBleedingEdge = $true + + <# + Enable PSResourceGet to be the default method of resolving dependencies by setting + UsePSResourceGet to the value $true. If UsePSResourceGet is not configured or + set to $false then PowerShellGet will be used to resolve dependencies. + #> + UsePSResourceGet = $true + PSResourceGetVersion = '1.0.1' + + # PowerShellGet compatibility module only works when using PSResourceGet or ModuleFast. + UsePowerShellGetCompatibilityModule = $true + UsePowerShellGetCompatibilityModuleVersion = '3.0.23-beta23' +} + diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..10e5bc57 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,43 @@ +## Security + +We take the security of our modules seriously, which includes all source +code repositories managed through our GitHub organization. + +If you believe you have found a security vulnerability in any of our +repository, please report it to us as described below. + +## Reporting Security Issues + +If the repository has enabled the ability to report a security vulnerability +through GitHub new issue (separate button called "Report a vulnerability") +then use that. See [Privately reporting a security vulnerability](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing-information-about-vulnerabilities/privately-reporting-a-security-vulnerability) +for more information. + +> [!CAUTION] +> Please do not report security vulnerabilities through a **public** GitHub issues +> or other public forum. + +If the repository does not have that option then please +report the security issue privately to one or several maintainers of the +repository. The easiest way to do so is to send us a direct message via +Twitter (X), Slack, Discord, or find us on some other social platform. + +You should receive a response within 48 hours. If for some reason you do not, +please follow up by other means or to other contributors. + +Please include the requested information listed below (as much as you can +provide) to help us better understand the nature and scope of the possible issue: + +* Type of issue +* Full paths of source file(s) related to the manifestation of the issue +* The location of the affected source code (tag/branch/commit or direct URL) +* Any special configuration required to reproduce the issue +* Step-by-step instructions to reproduce the issue +* Proof-of-concept or exploit code (if possible) +* Impact of the issue, including how an attacker might exploit the issue + +This information will help us triage your report more quickly. + +## Preferred Languages + +We prefer all communications to be in English. diff --git a/Test/SamplePBIP/Sales.Dataset/.pbi/editorSettings.json b/Test/SamplePBIP/Sales.Dataset/.pbi/editorSettings.json deleted file mode 100644 index 6b092b69..00000000 --- a/Test/SamplePBIP/Sales.Dataset/.pbi/editorSettings.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "version": "1.0", - "showHiddenFields": true, - "parallelQueryLoading": true, - "relationshipImportEnabled": true, - "shouldNotifyUserOfNameConflictResolution": true -} \ No newline at end of file diff --git a/Test/SamplePBIP/Sales.Dataset/definition.pbidataset b/Test/SamplePBIP/Sales.Dataset/definition.pbidataset deleted file mode 100644 index 8ca3cc6a..00000000 --- a/Test/SamplePBIP/Sales.Dataset/definition.pbidataset +++ /dev/null @@ -1,6 +0,0 @@ -{ - "version": "1.0", - "settings": { - "qnaEnabled": true - } -} \ No newline at end of file diff --git a/Test/SamplePBIP/Sales.Dataset/diagramLayout.json b/Test/SamplePBIP/Sales.Dataset/diagramLayout.json deleted file mode 100644 index fcb1ab4c..00000000 --- a/Test/SamplePBIP/Sales.Dataset/diagramLayout.json +++ /dev/null @@ -1,144 +0,0 @@ -{ - "version": "1.1.0", - "diagrams": [ - { - "ordinal": 0, - "scrollPosition": { - "x": 326, - "y": 0 - }, - "nodes": [ - { - "location": { - "x": 326.31359098587973, - "y": 3.6666666666666856 - }, - "nodeIndex": "Calendar", - "size": { - "height": 187, - "width": 254 - }, - "zIndex": 7 - }, - { - "location": { - "x": 664.04633538628775, - "y": 322.16666666666669 - }, - "nodeIndex": "Sales", - "size": { - "height": 481, - "width": 276 - }, - "zIndex": 3 - }, - { - "location": { - "x": 967.4, - "y": 50 - }, - "nodeIndex": "Customer", - "size": { - "height": 200, - "width": 234 - }, - "zIndex": 6 - }, - { - "location": { - "x": 1145.8, - "y": 368 - }, - "nodeIndex": "Product", - "size": { - "height": 497, - "width": 232 - }, - "zIndex": 5 - }, - { - "location": { - "x": 1774.1, - "y": 0 - }, - "nodeIndex": "Dynamic Measure", - "size": { - "height": 300, - "width": 234 - }, - "zIndex": 1 - }, - { - "location": { - "x": 1456.3000000000002, - "y": 55 - }, - "nodeIndex": "Smart Calcs", - "size": { - "height": 128, - "width": 234 - }, - "zIndex": 2 - }, - { - "location": { - "x": 232.86667175292951, - "y": 322 - }, - "nodeIndex": "Store", - "size": { - "height": 296, - "width": 234 - }, - "zIndex": 4 - }, - { - "location": { - "x": 2149.5, - "y": 0 - }, - "nodeIndex": "About", - "size": { - "height": 152, - "width": 234 - }, - "zIndex": 0 - }, - { - "location": { - "x": 2200.6333282470705, - "y": 356.5 - }, - "nodeIndex": "Parameter - Dimension", - "nodeLineageTag": "29294de5-e93a-47f4-bd78-26ec8efe7786", - "size": { - "height": 152, - "width": 234 - }, - "zIndex": 0 - }, - { - "location": { - "x": 2251.766656494141, - "y": 356.5 - }, - "nodeIndex": "Parameter - Measure", - "nodeLineageTag": "eee26640-bfec-44ed-b1e7-d56562bc25ed", - "size": { - "height": 152, - "width": 234 - }, - "zIndex": 0 - } - ], - "name": "All tables", - "zoomValue": 100, - "pinKeyFieldsToTop": false, - "showExtraHeaderInfo": false, - "hideKeyFieldsWhenCollapsed": false, - "tablesLocked": false - } - ], - "selectedDiagram": "All tables", - "defaultDiagram": "All tables" -} \ No newline at end of file diff --git a/Test/SamplePBIP/Sales.Dataset/item.config.json b/Test/SamplePBIP/Sales.Dataset/item.config.json deleted file mode 100644 index a525c9c1..00000000 --- a/Test/SamplePBIP/Sales.Dataset/item.config.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": "1.0", - "logicalId": "7b1d84cb-2da6-446a-9bc0-218da23c7fb0" -} \ No newline at end of file diff --git a/Test/SamplePBIP/Sales.Dataset/item.metadata.json b/Test/SamplePBIP/Sales.Dataset/item.metadata.json deleted file mode 100644 index 777a2ffd..00000000 --- a/Test/SamplePBIP/Sales.Dataset/item.metadata.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "type": "dataset", - "displayName": "Sales" -} \ No newline at end of file diff --git a/Test/SamplePBIP/Sales.Dataset/model.bim b/Test/SamplePBIP/Sales.Dataset/model.bim deleted file mode 100644 index 227c508d..00000000 --- a/Test/SamplePBIP/Sales.Dataset/model.bim +++ /dev/null @@ -1,15833 +0,0 @@ -{ - "compatibilityLevel": 1567, - "model": { - "annotations": [ - { - "name": "PBIDesktopVersion", - "value": "2.122.442.0 (23.10)" - }, - { - "name": "__PBI_TimeIntelligenceEnabled", - "value": "0" - }, - { - "name": "PBI_QueryOrder", - "value": "[\"RangeStart\",\"RangeEnd\",\"Environment\",\"Randomizer\",\"Calendar\",\"Sales\",\"Product\",\"Customer\",\"Store\",\"RAW-Product\",\"RAW-Store\",\"RAW-Customer\",\"RAW-Sales\",\"RAW-SalesDateAdjustedAndSalesRandomized\",\"RAW-CurrencyExchange\",\"About\"]" - }, - { - "name": "BestPracticeAnalyzer_IgnoreRules", - "value": "{\"RuleIDs\":[\"PARTITION_NAME_SHOULD_MATCH_TABLE_NAME_FOR_SINGLE_PARTITION_TABLES\"]}" - }, - { - "name": "__TEdtr", - "value": "1" - }, - { - "name": "PBI_ProTooling", - "value": "[\"DevMode\"]" - } - ], - "culture": "en-US", - "cultures": [ - { - "name": "en-US", - "linguisticMetadata": { - "content": { - "DynamicImprovement": "HighConfidence", - "Entities": { - "calendar": { - "Definition": { - "Binding": { - "ConceptualEntity": "Calendar" - } - }, - "State": "Generated", - "Terms": [ - { - "calendar": { - "State": "Generated" - } - }, - { - "almanac": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.491 - } - }, - { - "datebook": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.491 - } - }, - { - "agenda": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.491 - } - }, - { - "logbook": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.491 - } - }, - { - "diary": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.491 - } - }, - { - "schedule": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "timetable": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - } - ] - }, - "calendar.date": { - "Definition": { - "Binding": { - "ConceptualEntity": "Calendar", - "ConceptualProperty": "Date" - } - }, - "SemanticType": "Time", - "State": "Generated", - "Terms": [ - { - "date": { - "State": "Generated" - } - }, - { - "moment": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.736 - } - }, - { - "period": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.714 - } - } - ] - }, - "calendar.date_id": { - "Definition": { - "Binding": { - "ConceptualEntity": "Calendar", - "ConceptualProperty": "DateId" - } - }, - "State": "Generated", - "Terms": [ - { - "date id": { - "State": "Generated" - } - }, - { - "DateId": { - "State": "Generated", - "Type": "Noun", - "Weight": 0.99 - } - }, - { - "date identification": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.75 - } - }, - { - "date identity": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.75 - } - }, - { - "date identifier": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.75 - } - }, - { - "date credential": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.727 - } - }, - { - "moment id": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.6 - } - }, - { - "period id": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.582 - } - } - ], - "Visibility": { - "Value": "Hidden" - } - }, - "calendar.day": { - "Definition": { - "Binding": { - "ConceptualEntity": "Calendar", - "ConceptualProperty": "Day" - } - }, - "State": "Generated", - "Terms": [ - { - "day": { - "State": "Generated" - } - } - ] - }, - "calendar.day_relative": { - "Definition": { - "Binding": { - "ConceptualEntity": "Calendar", - "ConceptualProperty": "Day (Relative)" - } - }, - "State": "Generated", - "Terms": [ - { - "day (relative)": { - "State": "Generated" - } - }, - { - "day": { - "State": "Generated", - "Weight": 0.75 - } - } - ] - }, - "calendar.month": { - "Definition": { - "Binding": { - "ConceptualEntity": "Calendar", - "ConceptualProperty": "Month" - } - }, - "State": "Generated", - "Terms": [ - { - "month": { - "State": "Generated" - } - }, - { - "mth": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.736 - } - } - ] - }, - "calendar.month1": { - "Definition": { - "Binding": { - "ConceptualEntity": "Calendar", - "ConceptualProperty": "Month (Year)" - } - }, - "State": "Generated", - "Terms": [ - { - "month": { - "State": "Generated" - } - }, - { - "month (Year)": { - "State": "Generated" - } - }, - { - "mth": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.736 - } - }, - { - "mth (year)": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.617 - } - }, - { - "month ( yr )": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.617 - } - } - ], - "Units": [ - "year" - ] - }, - "calendar.month_": { - "Definition": { - "Binding": { - "ConceptualEntity": "Calendar", - "ConceptualProperty": "Month (#)" - } - }, - "State": "Generated", - "Terms": [ - { - "month (#)": { - "State": "Generated" - } - }, - { - "month": { - "State": "Generated", - "Weight": 0.75 - } - }, - { - "mth (#)": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.617 - } - }, - { - "month ( no )": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.599 - } - }, - { - "month ( num )": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.599 - } - }, - { - "month ( number )": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.599 - } - } - ] - }, - "calendar.month_long": { - "Definition": { - "Binding": { - "ConceptualEntity": "Calendar", - "ConceptualProperty": "Month (Long)" - } - }, - "State": "Generated", - "Terms": [ - { - "month (long)": { - "State": "Generated" - } - }, - { - "month": { - "State": "Generated", - "Weight": 0.75 - } - }, - { - "mth (long)": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.617 - } - } - ] - }, - "calendar.month_relative": { - "Definition": { - "Binding": { - "ConceptualEntity": "Calendar", - "ConceptualProperty": "Month (Relative)" - } - }, - "State": "Generated", - "Terms": [ - { - "month (relative)": { - "State": "Generated" - } - }, - { - "month": { - "State": "Generated", - "Weight": 0.75 - } - }, - { - "mth (relative)": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.617 - } - } - ] - }, - "calendar.month_start_date": { - "Definition": { - "Binding": { - "ConceptualEntity": "Calendar", - "ConceptualProperty": "Month Start Date" - } - }, - "SemanticType": "Time", - "State": "Generated", - "Terms": [ - { - "month start date": { - "State": "Generated" - } - }, - { - "start date": { - "State": "Generated", - "Weight": 0.97 - } - }, - { - "month start moment": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.762 - } - }, - { - "start moment": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.75 - } - }, - { - "month start period": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.739 - } - }, - { - "start period": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.727 - } - }, - { - "month commencement date": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.615 - } - }, - { - "month inception date": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.615 - } - }, - { - "month kickoff date": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.615 - } - }, - { - "mth start date": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.609 - } - }, - { - "commencement date": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.6 - } - }, - { - "inception date": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.6 - } - } - ] - }, - "calendar.month_year_id": { - "Definition": { - "Binding": { - "ConceptualEntity": "Calendar", - "ConceptualProperty": "MonthYearId" - } - }, - "State": "Generated", - "Terms": [ - { - "month year id": { - "State": "Generated" - } - }, - { - "MonthYearId": { - "State": "Generated", - "Type": "Noun", - "Weight": 0.99 - } - }, - { - "month year": { - "State": "Generated", - "Weight": 0.97 - } - }, - { - "year id": { - "State": "Generated", - "Weight": 0.97 - } - }, - { - "month year identification": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.762 - } - }, - { - "month year identity": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.762 - } - }, - { - "month year identifier": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.762 - } - }, - { - "month yr": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.75 - } - }, - { - "year identification": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.75 - } - }, - { - "year identity": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.75 - } - }, - { - "year identifier": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.75 - } - }, - { - "month year credential": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.739 - } - }, - { - "year credential": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.727 - } - }, - { - "mth year id": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.609 - } - } - ], - "Visibility": { - "Value": "Hidden" - } - }, - "calendar.quarter": { - "Definition": { - "Binding": { - "ConceptualEntity": "Calendar", - "ConceptualProperty": "Quarter" - } - }, - "State": "Generated", - "Terms": [ - { - "quarter": { - "State": "Generated" - } - }, - { - "qtr": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.818 - } - } - ] - }, - "calendar.quarter1": { - "Definition": { - "Binding": { - "ConceptualEntity": "Calendar", - "ConceptualProperty": "Quarter (Year)" - } - }, - "State": "Generated", - "Terms": [ - { - "quarter": { - "State": "Generated" - } - }, - { - "quarter (Year)": { - "State": "Generated" - } - }, - { - "qtr": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.818 - } - }, - { - "qtr (year)": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.686 - } - }, - { - "quarter ( yr )": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.617 - } - } - ], - "Units": [ - "year" - ] - }, - "calendar.quarter_year_id": { - "Definition": { - "Binding": { - "ConceptualEntity": "Calendar", - "ConceptualProperty": "QuarterYearId" - } - }, - "State": "Generated", - "Terms": [ - { - "quarter year id": { - "State": "Generated" - } - }, - { - "QuarterYearId": { - "State": "Generated", - "Type": "Noun", - "Weight": 0.99 - } - }, - { - "quarter year": { - "State": "Generated", - "Weight": 0.97 - } - }, - { - "year id": { - "State": "Generated", - "Weight": 0.97 - } - }, - { - "quarter year identification": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.762 - } - }, - { - "quarter year identity": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.762 - } - }, - { - "quarter year identifier": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.762 - } - }, - { - "quarter yr": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.75 - } - }, - { - "year identification": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.75 - } - }, - { - "year identity": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.75 - } - }, - { - "year identifier": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.75 - } - }, - { - "quarter year credential": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.739 - } - }, - { - "year credential": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.727 - } - }, - { - "qtr year id": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.677 - } - } - ], - "Visibility": { - "Value": "Hidden" - } - }, - "calendar.semester": { - "Definition": { - "Binding": { - "ConceptualEntity": "Calendar", - "ConceptualProperty": "Semester" - } - }, - "State": "Generated", - "Terms": [ - { - "semester": { - "State": "Generated" - } - } - ] - }, - "calendar.semester1": { - "Definition": { - "Binding": { - "ConceptualEntity": "Calendar", - "ConceptualProperty": "Semester (Year)" - } - }, - "State": "Generated", - "Terms": [ - { - "semester": { - "State": "Generated" - } - }, - { - "semester (Year)": { - "State": "Generated" - } - }, - { - "semester ( yr )": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.617 - } - } - ], - "Units": [ - "year" - ] - }, - "calendar.semester_year_id": { - "Definition": { - "Binding": { - "ConceptualEntity": "Calendar", - "ConceptualProperty": "SemesterYearId" - } - }, - "State": "Generated", - "Terms": [ - { - "semester year id": { - "State": "Generated" - } - }, - { - "SemesterYearId": { - "State": "Generated", - "Type": "Noun", - "Weight": 0.99 - } - }, - { - "semester year": { - "State": "Generated", - "Weight": 0.97 - } - }, - { - "year id": { - "State": "Generated", - "Weight": 0.97 - } - }, - { - "semester year identification": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.762 - } - }, - { - "semester year identity": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.762 - } - }, - { - "semester year identifier": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.762 - } - }, - { - "semester yr": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.75 - } - }, - { - "year identification": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.75 - } - }, - { - "year identity": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.75 - } - }, - { - "year identifier": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.75 - } - }, - { - "semester year credential": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.739 - } - }, - { - "year credential": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.727 - } - }, - { - "semester yr id": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.609 - } - } - ], - "Visibility": { - "Value": "Hidden" - } - }, - "calendar.week": { - "Definition": { - "Binding": { - "ConceptualEntity": "Calendar", - "ConceptualProperty": "Week" - } - }, - "State": "Generated", - "Terms": [ - { - "week": { - "State": "Generated" - } - } - ] - }, - "calendar.week1": { - "Definition": { - "Binding": { - "ConceptualEntity": "Calendar", - "ConceptualProperty": "Week (Year)" - } - }, - "State": "Generated", - "Terms": [ - { - "week": { - "State": "Generated" - } - }, - { - "week (Year)": { - "State": "Generated" - } - }, - { - "week ( yr )": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.617 - } - } - ], - "Units": [ - "year" - ] - }, - "calendar.week_day": { - "Definition": { - "Binding": { - "ConceptualEntity": "Calendar", - "ConceptualProperty": "Week Day" - } - }, - "State": "Generated", - "Terms": [ - { - "week day": { - "State": "Generated" - } - } - ] - }, - "calendar.week_day_": { - "Definition": { - "Binding": { - "ConceptualEntity": "Calendar", - "ConceptualProperty": "Week Day (#)" - } - }, - "State": "Generated", - "Terms": [ - { - "week day (#)": { - "State": "Generated" - } - }, - { - "week day": { - "State": "Generated", - "Weight": 0.75 - } - }, - { - "week day ( no )": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.605 - } - }, - { - "week day ( num )": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.605 - } - }, - { - "week day ( number )": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.605 - } - } - ] - }, - "calendar.week_end_date": { - "Definition": { - "Binding": { - "ConceptualEntity": "Calendar", - "ConceptualProperty": "Week End Date" - } - }, - "SemanticType": "Time", - "State": "Generated", - "Terms": [ - { - "week end date": { - "State": "Generated" - } - }, - { - "end date": { - "State": "Generated", - "Weight": 0.97 - } - }, - { - "week end moment": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.762 - } - }, - { - "end moment": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.75 - } - }, - { - "week end period": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.739 - } - }, - { - "end period": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.727 - } - }, - { - "week culmination date": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.615 - } - }, - { - "week completion date": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.609 - } - }, - { - "week conclusion date": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.609 - } - }, - { - "week expiration date": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.609 - } - }, - { - "culmination date": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.6 - } - }, - { - "completion date": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.6 - } - } - ] - }, - "calendar.week_relative": { - "Definition": { - "Binding": { - "ConceptualEntity": "Calendar", - "ConceptualProperty": "Week (Relative)" - } - }, - "State": "Generated", - "Terms": [ - { - "week (relative)": { - "State": "Generated" - } - }, - { - "week": { - "State": "Generated", - "Weight": 0.75 - } - } - ] - }, - "calendar.week_start_date": { - "Definition": { - "Binding": { - "ConceptualEntity": "Calendar", - "ConceptualProperty": "Week Start Date" - } - }, - "SemanticType": "Time", - "State": "Generated", - "Terms": [ - { - "week start date": { - "State": "Generated" - } - }, - { - "start date": { - "State": "Generated", - "Weight": 0.97 - } - }, - { - "week start moment": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.762 - } - }, - { - "start moment": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.75 - } - }, - { - "week start period": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.739 - } - }, - { - "start period": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.727 - } - }, - { - "week commencement date": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.615 - } - }, - { - "week inception date": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.615 - } - }, - { - "week kickoff date": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.615 - } - }, - { - "commencement date": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.6 - } - }, - { - "inception date": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.6 - } - }, - { - "kickoff date": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.6 - } - } - ] - }, - "calendar.week_year_id": { - "Definition": { - "Binding": { - "ConceptualEntity": "Calendar", - "ConceptualProperty": "WeekYearId" - } - }, - "State": "Generated", - "Terms": [ - { - "week year id": { - "State": "Generated" - } - }, - { - "WeekYearId": { - "State": "Generated", - "Type": "Noun", - "Weight": 0.99 - } - }, - { - "week year": { - "State": "Generated", - "Weight": 0.97 - } - }, - { - "year id": { - "State": "Generated", - "Weight": 0.97 - } - }, - { - "week year identification": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.762 - } - }, - { - "week year identity": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.762 - } - }, - { - "week year identifier": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.762 - } - }, - { - "week yr": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.75 - } - }, - { - "year identification": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.75 - } - }, - { - "year identity": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.75 - } - }, - { - "year identifier": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.75 - } - }, - { - "week year credential": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.739 - } - }, - { - "year credential": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.727 - } - }, - { - "week yr id": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.609 - } - } - ], - "Visibility": { - "Value": "Hidden" - } - }, - "calendar.work_day": { - "Definition": { - "Binding": { - "ConceptualEntity": "Calendar", - "ConceptualProperty": "Work Day" - } - }, - "State": "Generated", - "Terms": [ - { - "work day": { - "State": "Generated" - } - } - ] - }, - "calendar.year": { - "Definition": { - "Binding": { - "ConceptualEntity": "Calendar", - "ConceptualProperty": "Year" - } - }, - "SemanticType": "Time", - "State": "Generated", - "Terms": [ - { - "year": { - "State": "Generated" - } - }, - { - "yr": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.736 - } - } - ] - }, - "calendar.year_relative": { - "Definition": { - "Binding": { - "ConceptualEntity": "Calendar", - "ConceptualProperty": "Year (Relative)" - } - }, - "State": "Generated", - "Terms": [ - { - "year (relative)": { - "State": "Generated" - } - }, - { - "year": { - "State": "Generated", - "Weight": 0.75 - } - }, - { - "yr (relative)": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.617 - } - } - ] - }, - "customer": { - "Definition": { - "Binding": { - "ConceptualEntity": "Customer" - } - }, - "State": "Generated", - "Terms": [ - { - "customer": { - "State": "Generated" - } - }, - { - "client": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.736 - } - }, - { - "consumer": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.736 - } - }, - { - "user": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.736 - } - }, - { - "buyer": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.736 - } - }, - { - "patron": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.736 - } - }, - { - "purchaser": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.736 - } - }, - { - "shopper": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.736 - } - } - ] - }, - "customer.address": { - "Definition": { - "Binding": { - "ConceptualEntity": "Customer", - "ConceptualProperty": "Address" - } - }, - "State": "Generated", - "Terms": [ - { - "address": { - "State": "Generated" - } - }, - { - "location": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.736 - } - }, - { - "direction": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.736 - } - }, - { - "residence": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.736 - } - }, - { - "contact": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.714 - } - }, - { - "place": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.714 - } - } - ] - }, - "customer.age": { - "Definition": { - "Binding": { - "ConceptualEntity": "Customer", - "ConceptualProperty": "Age" - } - }, - "State": "Generated", - "Terms": [ - { - "age": { - "State": "Generated" - } - }, - { - "oldness": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.491 - } - }, - { - "stage": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "phase": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "era": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.466 - } - }, - { - "epoch": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.466 - } - }, - { - "period": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.452 - } - } - ] - }, - "customer.birthday": { - "Definition": { - "Binding": { - "ConceptualEntity": "Customer", - "ConceptualProperty": "Birthday" - } - }, - "SemanticType": "Time", - "State": "Generated", - "Terms": [ - { - "birthday": { - "State": "Generated" - } - }, - { - "date": { - "State": "Generated", - "Weight": 0.7 - } - }, - { - "birthdate": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.491 - } - }, - { - "anniversary": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.491 - } - }, - { - "centenary": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.452 - } - }, - { - "bicentenary": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.452 - } - }, - { - "centennial": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.452 - } - }, - { - "bicentennial": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.452 - } - } - ] - }, - "customer.city": { - "Definition": { - "Binding": { - "ConceptualEntity": "Customer", - "ConceptualProperty": "City" - } - }, - "SemanticType": "Location", - "State": "Generated", - "Terms": [ - { - "city": { - "State": "Generated" - } - }, - { - "location": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.736 - } - }, - { - "metropolis": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.736 - } - }, - { - "municipality": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.736 - } - }, - { - "town": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.714 - } - }, - { - "metropolitan": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.714 - } - } - ] - }, - "customer.continent": { - "Definition": { - "Binding": { - "ConceptualEntity": "Customer", - "ConceptualProperty": "Continent" - } - }, - "SemanticType": "Location", - "State": "Generated", - "Terms": [ - { - "continent": { - "State": "Generated" - } - }, - { - "landmass": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.491 - } - }, - { - "landform": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.491 - } - }, - { - "region": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.491 - } - }, - { - "land": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "island": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "mainland": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "zone": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - } - ] - }, - "customer.country": { - "Definition": { - "Binding": { - "ConceptualEntity": "Customer", - "ConceptualProperty": "Country" - } - }, - "SemanticType": "Location", - "State": "Generated", - "Terms": [ - { - "country": { - "State": "Generated" - } - }, - { - "nation": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.736 - } - }, - { - "location": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.736 - } - } - ] - }, - "customer.country_code": { - "Definition": { - "Binding": { - "ConceptualEntity": "Customer", - "ConceptualProperty": "Country Code" - } - }, - "SemanticType": "Location", - "State": "Generated", - "Terms": [ - { - "country code": { - "State": "Generated" - } - }, - { - "country id": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.75 - } - }, - { - "country key": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.727 - } - }, - { - "nation code": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.6 - } - }, - { - "location code": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.6 - } - } - ] - }, - "customer.customer": { - "Definition": { - "Binding": { - "ConceptualEntity": "Customer", - "ConceptualProperty": "Customer" - } - }, - "NameType": "Name", - "State": "Generated", - "Terms": [ - { - "customer": { - "State": "Generated" - } - }, - { - "customer name": { - "State": "Generated" - } - }, - { - "client": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.736 - } - }, - { - "consumer": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.736 - } - }, - { - "user": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.736 - } - }, - { - "buyer": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.736 - } - }, - { - "patron": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.736 - } - }, - { - "purchaser": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.736 - } - }, - { - "shopper": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.736 - } - } - ] - }, - "customer.customer_key": { - "Definition": { - "Binding": { - "ConceptualEntity": "Customer", - "ConceptualProperty": "CustomerKey" - } - }, - "State": "Generated", - "Terms": [ - { - "customer key": { - "State": "Generated" - } - }, - { - "CustomerKey": { - "State": "Generated", - "Type": "Noun", - "Weight": 0.99 - } - }, - { - "key": { - "State": "Generated", - "Weight": 0.97 - } - }, - { - "client key": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.6 - } - }, - { - "consumer key": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.6 - } - }, - { - "user key": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.6 - } - }, - { - "buyer key": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.6 - } - }, - { - "patron key": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.6 - } - }, - { - "purchaser key": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.6 - } - }, - { - "shopper key": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.6 - } - }, - { - "main": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "basic": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "fundamental": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - } - ] - }, - "customer.customers": { - "Definition": { - "Binding": { - "ConceptualEntity": "Customer", - "ConceptualProperty": "# Customers" - } - }, - "State": "Generated", - "Terms": [ - { - "# customers": { - "State": "Generated" - } - }, - { - "no customer": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.582 - } - }, - { - "num customer": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.582 - } - }, - { - "number customer": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.582 - } - }, - { - "# clientele": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.5 - } - }, - { - "# patron": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.5 - } - }, - { - "# client": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.5 - } - }, - { - "# consumer": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.5 - } - }, - { - "# punter": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.5 - } - }, - { - "# regular": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.485 - } - }, - { - "# custom": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.485 - } - } - ] - }, - "customer.gender": { - "Definition": { - "Binding": { - "ConceptualEntity": "Customer", - "ConceptualProperty": "Gender" - } - }, - "State": "Generated", - "Terms": [ - { - "gender": { - "State": "Generated" - } - }, - { - "sexuality": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.736 - } - }, - { - "sex": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.714 - } - } - ] - }, - "customer.state": { - "Definition": { - "Binding": { - "ConceptualEntity": "Customer", - "ConceptualProperty": "State" - } - }, - "SemanticType": "Location", - "State": "Generated", - "Terms": [ - {}, - { - "state or province": { - "State": "Generated", - "Weight": 0.7 - } - }, - { - "location": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.736 - } - }, - { - "province": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.736 - } - }, - { - "territory": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.736 - } - }, - { - "nation": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.736 - } - }, - { - "condition": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.714 - } - } - ] - }, - "customer.state_code": { - "Definition": { - "Binding": { - "ConceptualEntity": "Customer", - "ConceptualProperty": "State Code" - } - }, - "SemanticType": "Location", - "State": "Generated", - "Terms": [ - { - "state code": { - "State": "Generated" - } - }, - { - "state or province": { - "State": "Generated", - "Weight": 0.7 - } - }, - { - "state id": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.75 - } - }, - { - "state key": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.727 - } - }, - { - "location code": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.6 - } - }, - { - "province code": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.6 - } - }, - { - "territory code": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.6 - } - }, - { - "nation code": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.6 - } - }, - { - "condition code": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.582 - } - } - ] - }, - "customer.v___Customers_FormatString__customers_FormatString": { - "Definition": { - "Binding": { - "ConceptualEntity": "Customer", - "ConceptualProperty": "_# Customers FormatString" - } - }, - "State": "Generated", - "Terms": [ - { - "_# customers FormatString": { - "State": "Generated" - } - }, - { - "_ no customer formatstring": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.599 - } - }, - { - "_ num customer formatstring": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.599 - } - }, - { - "_ number customer formatstring": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.599 - } - } - ], - "Visibility": { - "Value": "Hidden" - } - }, - "customer.zip_code": { - "Definition": { - "Binding": { - "ConceptualEntity": "Customer", - "ConceptualProperty": "Zip Code" - } - }, - "SemanticType": "Location", - "State": "Generated", - "Terms": [ - { - "zip code": { - "State": "Generated" - } - }, - { - "postal code": { - "State": "Generated", - "Weight": 0.7 - } - }, - { - "zip id": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.75 - } - }, - { - "zip key": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.727 - } - }, - { - "location": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.54 - } - }, - { - "postcode": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.54 - } - }, - { - "post code": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.524 - } - } - ] - }, - "dynamic_measure": { - "Definition": { - "Binding": { - "ConceptualEntity": "Dynamic Measure" - } - }, - "State": "Generated", - "Terms": [ - { - "dynamic measure": { - "State": "Generated" - } - }, - { - "dynamic degree": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.5 - } - }, - { - "dynamic quantity": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.5 - } - }, - { - "dynamic quota": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.5 - } - }, - { - "dynamic extent": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.5 - } - }, - { - "dynamic amount": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.485 - } - }, - { - "dynamic portion": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.485 - } - }, - { - "dynamic ration": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.485 - } - }, - { - "dynamic size": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.485 - } - } - ] - }, - "dynamic_measure.area": { - "Definition": { - "Binding": { - "ConceptualEntity": "Dynamic Measure", - "ConceptualProperty": "Area" - } - }, - "State": "Generated", - "Terms": [ - { - "area": { - "State": "Generated" - } - }, - { - "region": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.736 - } - }, - { - "locale": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.736 - } - }, - { - "locality": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.736 - } - }, - { - "neighbourhood": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.736 - } - }, - { - "district": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.714 - } - }, - { - "field": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.714 - } - }, - { - "neighborhood": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.714 - } - }, - { - "section": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.714 - } - }, - { - "space": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.714 - } - } - ] - }, - "dynamic_measure.code": { - "Definition": { - "Binding": { - "ConceptualEntity": "Dynamic Measure", - "ConceptualProperty": "Code" - } - }, - "State": "Generated", - "Terms": [ - { - "code": { - "State": "Generated" - } - } - ] - }, - "dynamic_measure.format": { - "Definition": { - "Binding": { - "ConceptualEntity": "Dynamic Measure", - "ConceptualProperty": "Format" - } - }, - "State": "Generated", - "Terms": [ - { - "format": { - "State": "Generated" - } - }, - { - "arrangement": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.491 - } - }, - { - "setup": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.491 - } - }, - { - "presentation": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.491 - } - }, - { - "organization": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.491 - } - }, - { - "layout": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.491 - } - }, - { - "system": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.491 - } - }, - { - "set-up": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "plan": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "design": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "structure": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - } - ] - }, - "dynamic_measure.measure": { - "Definition": { - "Binding": { - "ConceptualEntity": "Dynamic Measure", - "ConceptualProperty": "Measure" - } - }, - "State": "Generated", - "Terms": [ - { - "measure": { - "State": "Generated" - } - }, - { - "degree": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.491 - } - }, - { - "quota": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.491 - } - }, - { - "extent": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.491 - } - }, - { - "portion": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "ration": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "size": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - } - ] - }, - "dynamic_measure.order": { - "Definition": { - "Binding": { - "ConceptualEntity": "Dynamic Measure", - "ConceptualProperty": "Order" - } - }, - "State": "Generated", - "Terms": [ - { - "order": { - "State": "Generated" - } - }, - { - "instruction": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.491 - } - }, - { - "direction": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.491 - } - }, - { - "edict": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.491 - } - }, - { - "command": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "directive": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "demand": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "mandate": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "imperative": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "stability": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.466 - } - }, - { - "harmony": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.466 - } - } - ] - }, - "dynamic_measure.v_Value_Avg_per_Month_FormatString_value_avg_per_month_FormatString": { - "Definition": { - "Binding": { - "ConceptualEntity": "Dynamic Measure", - "ConceptualProperty": "_Value Avg per Month FormatString" - } - }, - "State": "Generated", - "Terms": [ - { - "_value avg per month FormatString": { - "State": "Generated" - } - } - ], - "Visibility": { - "Value": "Hidden" - } - }, - "dynamic_measure.v_Value_Daily_Max_FormatString_value_daily_max_FormatString": { - "Definition": { - "Binding": { - "ConceptualEntity": "Dynamic Measure", - "ConceptualProperty": "_Value Daily Max FormatString" - } - }, - "State": "Generated", - "Terms": [ - { - "_value daily max FormatString": { - "State": "Generated" - } - }, - { - "_value daily maximum formatstring": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.605 - } - } - ], - "Visibility": { - "Value": "Hidden" - } - }, - "dynamic_measure.v_Value_FormatString_value_FormatString": { - "Definition": { - "Binding": { - "ConceptualEntity": "Dynamic Measure", - "ConceptualProperty": "_Value FormatString" - } - }, - "State": "Generated", - "Terms": [ - { - "_value FormatString": { - "State": "Generated" - } - } - ], - "Visibility": { - "Value": "Hidden" - } - }, - "dynamic_measure.v_Value_Normalized__by_date__FormatString_value_normalized_by_date_FormatString": { - "Definition": { - "Binding": { - "ConceptualEntity": "Dynamic Measure", - "ConceptualProperty": "_Value Normalized (by date) FormatString" - } - }, - "State": "Generated", - "Terms": [ - { - "_value normalized (by date) FormatString": { - "State": "Generated" - } - } - ], - "Visibility": { - "Value": "Hidden" - } - }, - "dynamic_measure.v_Value____Δ_ly__FormatString_value__δ_ly_FormatString": { - "Definition": { - "Binding": { - "ConceptualEntity": "Dynamic Measure", - "ConceptualProperty": "_Value % (Δ ly) FormatString" - } - }, - "State": "Generated", - "Terms": [ - { - "_value % (δ ly) FormatString": { - "State": "Generated" - } - } - ], - "Visibility": { - "Value": "Hidden" - } - }, - "dynamic_measure.v_Value__ly__FormatString_value_ly_FormatString": { - "Definition": { - "Binding": { - "ConceptualEntity": "Dynamic Measure", - "ConceptualProperty": "_Value (ly) FormatString" - } - }, - "State": "Generated", - "Terms": [ - { - "_value (ly) FormatString": { - "State": "Generated" - } - } - ], - "Visibility": { - "Value": "Hidden" - } - }, - "dynamic_measure.v_Value__ytd__FormatString_value_ytd_FormatString": { - "Definition": { - "Binding": { - "ConceptualEntity": "Dynamic Measure", - "ConceptualProperty": "_Value (ytd) FormatString" - } - }, - "State": "Generated", - "Terms": [ - { - "_value (ytd) FormatString": { - "State": "Generated" - } - } - ], - "Visibility": { - "Value": "Hidden" - } - }, - "dynamic_measure.value": { - "Definition": { - "Binding": { - "ConceptualEntity": "Dynamic Measure", - "ConceptualProperty": "Value" - } - }, - "State": "Generated", - "Terms": [ - { - "value": { - "State": "Generated" - } - }, - { - "assessment": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.491 - } - }, - { - "worth": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "charge": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "importance": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.466 - } - }, - { - "significance": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.466 - } - }, - { - "usefulness": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.466 - } - }, - { - "consequence": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.466 - } - }, - { - "use": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.452 - } - }, - { - "meaning": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.452 - } - }, - { - "merit": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.452 - } - } - ] - }, - "dynamic_measure.value__δ_ly": { - "Definition": { - "Binding": { - "ConceptualEntity": "Dynamic Measure", - "ConceptualProperty": "Value % (Δ ly)" - } - }, - "State": "Generated", - "Terms": [ - { - "value % (δ ly)": { - "State": "Generated" - } - }, - { - "value %": { - "State": "Generated", - "Weight": 0.75 - } - } - ] - }, - "dynamic_measure.value_avg_per_month": { - "Definition": { - "Binding": { - "ConceptualEntity": "Dynamic Measure", - "ConceptualProperty": "Value Avg per Month" - } - }, - "State": "Generated", - "Terms": [ - { - "value avg per month": { - "State": "Generated" - } - }, - { - "value average per month": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.748 - } - }, - { - "value avg per mth": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.617 - } - } - ] - }, - "dynamic_measure.value_daily_max": { - "Definition": { - "Binding": { - "ConceptualEntity": "Dynamic Measure", - "ConceptualProperty": "Value Daily Max" - } - }, - "State": "Generated", - "Terms": [ - { - "value daily max": { - "State": "Generated" - } - }, - { - "daily max": { - "State": "Generated", - "Weight": 0.97 - } - }, - { - "value daily maximum": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.739 - } - }, - { - "daily maximum": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.727 - } - } - ] - }, - "dynamic_measure.value_ly": { - "Definition": { - "Binding": { - "ConceptualEntity": "Dynamic Measure", - "ConceptualProperty": "Value (ly)" - } - }, - "State": "Generated", - "Terms": [ - { - "value (ly)": { - "State": "Generated" - } - }, - { - "value": { - "State": "Generated", - "Weight": 0.75 - } - } - ] - }, - "dynamic_measure.value_normalized_by_date": { - "Definition": { - "Binding": { - "ConceptualEntity": "Dynamic Measure", - "ConceptualProperty": "Value Normalized (by date)" - } - }, - "State": "Generated", - "Terms": [ - { - "value normalized (by date)": { - "State": "Generated" - } - }, - { - "value normalized": { - "State": "Generated", - "Weight": 0.75 - } - } - ] - }, - "dynamic_measure.value_ytd": { - "Definition": { - "Binding": { - "ConceptualEntity": "Dynamic Measure", - "ConceptualProperty": "Value (ytd)" - } - }, - "State": "Generated", - "Terms": [ - { - "value (ytd)": { - "State": "Generated" - } - }, - { - "value": { - "State": "Generated", - "Weight": 0.75 - } - }, - { - "value ( year to date )": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.514 - } - } - ] - }, - "product": { - "Definition": { - "Binding": { - "ConceptualEntity": "Product" - } - }, - "State": "Generated", - "Terms": [ - { - "product": { - "State": "Generated" - } - }, - { - "artifact": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.736 - } - }, - { - "item": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.714 - } - }, - { - "merchandise": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.714 - } - }, - { - "produce": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.714 - } - } - ] - }, - "product.brand": { - "Definition": { - "Binding": { - "ConceptualEntity": "Product", - "ConceptualProperty": "Brand" - } - }, - "State": "Generated", - "Terms": [ - { - "brand": { - "State": "Generated" - } - }, - { - "variety": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.491 - } - }, - { - "marque": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.491 - } - }, - { - "make": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "kind": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "trademark": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "type": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "style": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "class": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "strain": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "cast": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - } - ] - }, - "product.category": { - "Definition": { - "Binding": { - "ConceptualEntity": "Product", - "ConceptualProperty": "Category" - } - }, - "State": "Generated", - "Terms": [ - { - "category": { - "State": "Generated" - } - }, - { - "classification": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.736 - } - }, - { - "class": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.714 - } - }, - { - "type": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.714 - } - }, - { - "grouping": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.714 - } - }, - { - "kind": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.714 - } - } - ] - }, - "product.category_code": { - "Definition": { - "Binding": { - "ConceptualEntity": "Product", - "ConceptualProperty": "Category Code" - } - }, - "State": "Generated", - "Terms": [ - { - "category code": { - "State": "Generated" - } - }, - { - "category id": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.75 - } - }, - { - "category key": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.727 - } - }, - { - "classification code": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.6 - } - }, - { - "class code": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.582 - } - }, - { - "group code": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.582 - } - }, - { - "type code": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.582 - } - }, - { - "grouping code": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.582 - } - }, - { - "kind code": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.582 - } - } - ] - }, - "product.color": { - "Definition": { - "Binding": { - "ConceptualEntity": "Product", - "ConceptualProperty": "Color" - } - }, - "State": "Generated", - "Terms": [ - { - "color": { - "State": "Generated" - } - }, - { - "hue": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.491 - } - }, - { - "tint": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "shade": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "dye": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "paint": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "pigment": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - } - ] - }, - "product.manufacturer": { - "Definition": { - "Binding": { - "ConceptualEntity": "Product", - "ConceptualProperty": "Manufacturer" - } - }, - "State": "Generated", - "Terms": [ - { - "manufacturer": { - "State": "Generated" - } - }, - { - "builder": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.491 - } - }, - { - "producer": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.491 - } - }, - { - "constructer": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.491 - } - }, - { - "creator": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.491 - } - }, - { - "industrialist": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.491 - } - }, - { - "maker": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.491 - } - }, - { - "company": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "firm": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - } - ] - }, - "product.product": { - "Definition": { - "Binding": { - "ConceptualEntity": "Product", - "ConceptualProperty": "Product" - } - }, - "NameType": "Name", - "State": "Generated", - "Terms": [ - { - "product": { - "State": "Generated" - } - }, - { - "product name": { - "State": "Generated" - } - }, - { - "artifact": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.736 - } - }, - { - "item": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.714 - } - }, - { - "merchandise": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.714 - } - }, - { - "produce": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.714 - } - } - ] - }, - "product.product_code": { - "Definition": { - "Binding": { - "ConceptualEntity": "Product", - "ConceptualProperty": "Product Code" - } - }, - "NameType": "Identifier", - "State": "Generated", - "Terms": [ - { - "product code": { - "State": "Generated" - } - }, - { - "code": { - "State": "Generated", - "Weight": 0.97 - } - }, - { - "product id": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.75 - } - }, - { - "artifact code": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.6 - } - }, - { - "item code": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.582 - } - }, - { - "merchandise code": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.582 - } - }, - { - "produce code": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.582 - } - } - ] - }, - "product.product_key": { - "Definition": { - "Binding": { - "ConceptualEntity": "Product", - "ConceptualProperty": "ProductKey" - } - }, - "State": "Generated", - "Terms": [ - { - "product key": { - "State": "Generated" - } - }, - { - "ProductKey": { - "State": "Generated", - "Type": "Noun", - "Weight": 0.99 - } - }, - { - "key": { - "State": "Generated", - "Weight": 0.97 - } - }, - { - "artifact key": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.6 - } - }, - { - "item key": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.582 - } - }, - { - "merchandise key": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.582 - } - }, - { - "produce key": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.582 - } - }, - { - "main": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "basic": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "fundamental": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "central": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "major": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "keynote": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - } - ] - }, - "product.products": { - "Definition": { - "Binding": { - "ConceptualEntity": "Product", - "ConceptualProperty": "# Products" - } - }, - "State": "Generated", - "Terms": [ - { - "# products": { - "State": "Generated" - } - }, - { - "# artifact": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.75 - } - }, - { - "# item": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.727 - } - }, - { - "# merchandise": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.727 - } - }, - { - "# produce": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.727 - } - }, - { - "# goods": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.625 - } - }, - { - "no product": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.582 - } - }, - { - "num product": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.582 - } - }, - { - "number product": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.582 - } - } - ] - }, - "product.subcategory": { - "Definition": { - "Binding": { - "ConceptualEntity": "Product", - "ConceptualProperty": "Subcategory" - } - }, - "State": "Generated", - "Terms": [ - { - "subcategory": { - "State": "Generated" - } - } - ] - }, - "product.subcategory_code": { - "Definition": { - "Binding": { - "ConceptualEntity": "Product", - "ConceptualProperty": "Subcategory Code" - } - }, - "State": "Generated", - "Terms": [ - { - "subcategory code": { - "State": "Generated" - } - }, - { - "subcategory id": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.75 - } - }, - { - "subcategory key": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.727 - } - } - ] - }, - "product.unit_cost": { - "Definition": { - "Binding": { - "ConceptualEntity": "Product", - "ConceptualProperty": "Unit Cost" - } - }, - "State": "Generated", - "Terms": [ - { - "unit cost": { - "State": "Generated" - } - }, - { - "cost": { - "State": "Generated", - "Weight": 0.97 - } - }, - { - "module cost": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.6 - } - }, - { - "element cost": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.6 - } - }, - { - "entity cost": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.6 - } - }, - { - "group cost": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.582 - } - }, - { - "component cost": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.582 - } - }, - { - "constituent cost": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.582 - } - }, - { - "item cost": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.582 - } - }, - { - "part cost": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.582 - } - }, - { - "section cost": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.582 - } - }, - { - "unit charge": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.485 - } - } - ] - }, - "product.unit_price": { - "Definition": { - "Binding": { - "ConceptualEntity": "Product", - "ConceptualProperty": "Unit Price" - } - }, - "State": "Generated", - "Terms": [ - { - "unit price": { - "State": "Generated" - } - }, - { - "price": { - "State": "Generated", - "Weight": 0.97 - } - }, - { - "module price": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.6 - } - }, - { - "element price": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.6 - } - }, - { - "entity price": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.6 - } - }, - { - "group price": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.582 - } - }, - { - "component price": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.582 - } - }, - { - "constituent price": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.582 - } - }, - { - "item price": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.582 - } - }, - { - "part price": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.582 - } - }, - { - "section price": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.582 - } - }, - { - "unit value": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.485 - } - } - ] - }, - "product.v___Products_FormatString__products_FormatString": { - "Definition": { - "Binding": { - "ConceptualEntity": "Product", - "ConceptualProperty": "_# Products FormatString" - } - }, - "State": "Generated", - "Terms": [ - { - "_# products FormatString": { - "State": "Generated" - } - }, - { - "_# artifact formatstring": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.617 - } - }, - { - "_ no product formatstring": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.599 - } - }, - { - "_ num product formatstring": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.599 - } - }, - { - "_ number product formatstring": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.599 - } - }, - { - "_# item formatstring": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.599 - } - }, - { - "_# merchandise formatstring": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.599 - } - }, - { - "_# produce formatstring": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.599 - } - }, - { - "_# goods formatstring": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.514 - } - } - ], - "Visibility": { - "Value": "Hidden" - } - }, - "product.weight": { - "Definition": { - "Binding": { - "ConceptualEntity": "Product", - "ConceptualProperty": "Weight" - } - }, - "State": "Generated", - "Terms": [ - { - "weight": { - "State": "Generated" - } - }, - { - "heaviness": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.491 - } - }, - { - "weightiness": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.491 - } - }, - { - "mass": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "bulk": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "heft": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "encumbrance": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.466 - } - }, - { - "burden": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.452 - } - }, - { - "load": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.452 - } - } - ] - }, - "product.weight_unit_measure": { - "Definition": { - "Binding": { - "ConceptualEntity": "Product", - "ConceptualProperty": "Weight Unit Measure" - } - }, - "State": "Generated", - "Terms": [ - { - "weight unit measure": { - "State": "Generated" - } - }, - { - "unit measure": { - "State": "Generated", - "Weight": 0.97 - } - }, - { - "weight module measure": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.609 - } - }, - { - "weight element measure": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.609 - } - }, - { - "weight entity measure": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.609 - } - }, - { - "module measure": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.6 - } - }, - { - "element measure": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.6 - } - }, - { - "entity measure": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.6 - } - }, - { - "weight group measure": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.591 - } - }, - { - "weight component measure": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.591 - } - }, - { - "weight constituent measure": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.591 - } - }, - { - "weight item measure": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.591 - } - } - ] - }, - "sale": { - "Definition": { - "Binding": { - "ConceptualEntity": "Sales" - } - }, - "State": "Generated", - "Terms": [ - { - "sale": { - "State": "Generated" - } - }, - { - "auction": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "transaction": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.466 - } - }, - { - "deal": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.452 - } - }, - { - "trade": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.452 - } - }, - { - "vending": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.452 - } - }, - { - "retailing": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.452 - } - }, - { - "selling": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.452 - } - } - ] - }, - "sale.currency_code": { - "Definition": { - "Binding": { - "ConceptualEntity": "Sales", - "ConceptualProperty": "Currency Code" - } - }, - "State": "Generated", - "Terms": [ - { - "currency code": { - "State": "Generated" - } - }, - { - "currency id": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.75 - } - }, - { - "currency key": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.727 - } - } - ] - }, - "sale.customer_key": { - "Definition": { - "Binding": { - "ConceptualEntity": "Sales", - "ConceptualProperty": "CustomerKey" - } - }, - "State": "Generated", - "Terms": [ - { - "customer key": { - "State": "Generated" - } - }, - { - "CustomerKey": { - "State": "Generated", - "Type": "Noun", - "Weight": 0.99 - } - }, - { - "key": { - "State": "Generated", - "Weight": 0.97 - } - }, - { - "client key": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.6 - } - }, - { - "consumer key": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.6 - } - }, - { - "user key": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.6 - } - }, - { - "buyer key": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.6 - } - }, - { - "patron key": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.6 - } - }, - { - "purchaser key": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.6 - } - }, - { - "shopper key": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.6 - } - }, - { - "main": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "basic": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "fundamental": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - } - ], - "Visibility": { - "Value": "Hidden" - } - }, - "sale.customers_with_sales": { - "Definition": { - "Binding": { - "ConceptualEntity": "Sales", - "ConceptualProperty": "# Customers (with Sales)" - } - }, - "State": "Generated", - "Terms": [ - { - "# customers (with sales)": { - "State": "Generated" - } - }, - { - "# customers": { - "State": "Generated", - "Weight": 0.75 - } - } - ] - }, - "sale.delivery_date": { - "Definition": { - "Binding": { - "ConceptualEntity": "Sales", - "ConceptualProperty": "Delivery Date" - } - }, - "SemanticType": "Time", - "State": "Generated", - "Terms": [ - { - "delivery date": { - "State": "Generated" - } - }, - { - "date": { - "State": "Generated", - "Weight": 0.7 - } - }, - { - "delivery moment": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.75 - } - }, - { - "delivery period": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.727 - } - } - ] - }, - "sale.exchange_rate": { - "Definition": { - "Binding": { - "ConceptualEntity": "Sales", - "ConceptualProperty": "Exchange Rate" - } - }, - "State": "Generated", - "Terms": [ - { - "exchange rate": { - "State": "Generated" - } - }, - { - "rate": { - "State": "Generated", - "Weight": 0.97 - } - }, - { - "exchange degree": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.5 - } - }, - { - "exchange frequency": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.5 - } - }, - { - "exchange percentage": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.5 - } - }, - { - "exchange ratio": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.5 - } - }, - { - "exchange quotient": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.5 - } - }, - { - "degree": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.491 - } - }, - { - "frequency": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.491 - } - }, - { - "ratio": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.491 - } - }, - { - "quotient": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.491 - } - }, - { - "exchange amount": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.485 - } - } - ] - }, - "sale.line_number": { - "Definition": { - "Binding": { - "ConceptualEntity": "Sales", - "ConceptualProperty": "Line Number" - } - }, - "State": "Generated", - "Terms": [ - { - "line number": { - "State": "Generated" - } - }, - { - "line no": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.727 - } - } - ] - }, - "sale.margin": { - "Definition": { - "Binding": { - "ConceptualEntity": "Sales", - "ConceptualProperty": "Margin" - } - }, - "State": "Generated", - "Terms": [ - { - "margin": { - "State": "Generated" - } - }, - { - "boundary": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.491 - } - }, - { - "perimeter": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.491 - } - }, - { - "periphery": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.491 - } - }, - { - "border": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "brim": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "sideline": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "edge": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "verge": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "fringe": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "side": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - } - ] - }, - "sale.margin_ly": { - "Definition": { - "Binding": { - "ConceptualEntity": "Sales", - "ConceptualProperty": "Margin (ly)" - } - }, - "State": "Generated", - "Terms": [ - { - "margin (ly)": { - "State": "Generated" - } - }, - { - "margin": { - "State": "Generated", - "Weight": 0.75 - } - } - ] - }, - "sale.net_price": { - "Definition": { - "Binding": { - "ConceptualEntity": "Sales", - "ConceptualProperty": "Net Price" - } - }, - "State": "Generated", - "Terms": [ - { - "net price": { - "State": "Generated" - } - }, - { - "price": { - "State": "Generated", - "Weight": 0.97 - } - }, - { - "net value": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.485 - } - }, - { - "net worth": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.485 - } - }, - { - "net fee": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.485 - } - }, - { - "net charge": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.485 - } - }, - { - "net amount": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.485 - } - }, - { - "net bill": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.485 - } - }, - { - "net rate": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.485 - } - }, - { - "net expense": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.485 - } - }, - { - "net cost": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.485 - } - }, - { - "net outlay": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.485 - } - } - ] - }, - "sale.order_date": { - "Definition": { - "Binding": { - "ConceptualEntity": "Sales", - "ConceptualProperty": "Order Date" - } - }, - "SemanticType": "Time", - "State": "Generated", - "Terms": [ - { - "order date": { - "State": "Generated" - } - }, - { - "date": { - "State": "Generated", - "Weight": 0.7 - } - }, - { - "order moment": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.75 - } - }, - { - "order period": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.727 - } - } - ] - }, - "sale.order_number": { - "Definition": { - "Binding": { - "ConceptualEntity": "Sales", - "ConceptualProperty": "Order Number" - } - }, - "State": "Generated", - "Terms": [ - { - "order number": { - "State": "Generated" - } - }, - { - "order no": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.727 - } - } - ] - }, - "sale.product_key": { - "Definition": { - "Binding": { - "ConceptualEntity": "Sales", - "ConceptualProperty": "ProductKey" - } - }, - "State": "Generated", - "Terms": [ - { - "product key": { - "State": "Generated" - } - }, - { - "ProductKey": { - "State": "Generated", - "Type": "Noun", - "Weight": 0.99 - } - }, - { - "key": { - "State": "Generated", - "Weight": 0.97 - } - }, - { - "artifact key": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.6 - } - }, - { - "item key": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.582 - } - }, - { - "merchandise key": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.582 - } - }, - { - "produce key": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.582 - } - }, - { - "main": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "basic": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "fundamental": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "central": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "major": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "keynote": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - } - ], - "Visibility": { - "Value": "Hidden" - } - }, - "sale.products_with_sales": { - "Definition": { - "Binding": { - "ConceptualEntity": "Sales", - "ConceptualProperty": "# Products (with Sales)" - } - }, - "State": "Generated", - "Terms": [ - { - "# products (with sales)": { - "State": "Generated" - } - }, - { - "# products": { - "State": "Generated", - "Weight": 0.75 - } - } - ] - }, - "sale.quantity": { - "Definition": { - "Binding": { - "ConceptualEntity": "Sales", - "ConceptualProperty": "Quantity" - } - }, - "State": "Generated", - "Terms": [ - { - "quantity": { - "State": "Generated" - } - }, - { - "extent": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.491 - } - }, - { - "magnitude": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.491 - } - }, - { - "size": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "capacity": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "mass": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - } - ], - "Visibility": { - "Value": "Hidden" - } - }, - "sale.sales": { - "Definition": { - "Binding": { - "ConceptualEntity": "Sales", - "ConceptualProperty": "# Sales" - } - }, - "State": "Generated", - "Terms": [ - { - "# sales": { - "State": "Generated" - } - }, - { - "# sale": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.727 - } - }, - { - "no sale": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.582 - } - }, - { - "num sale": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.582 - } - }, - { - "number sale": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.582 - } - } - ] - }, - "sale.sales_amount": { - "Definition": { - "Binding": { - "ConceptualEntity": "Sales", - "ConceptualProperty": "Sales Amount" - } - }, - "State": "Generated", - "Terms": [ - { - "sales amount": { - "State": "Generated" - } - }, - { - "sales": { - "State": "Generated", - "Weight": 0.97 - } - }, - { - "sale avg. mth": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.771 - } - }, - { - "sale avg. month": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.599 - } - }, - { - "sale average . month": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.599 - } - }, - { - "sale amount": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.582 - } - }, - { - "sale quantity": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.5 - } - }, - { - "sale volume": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.5 - } - }, - { - "sale expanse": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.5 - } - }, - { - "sale extent": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.5 - } - }, - { - "sale sum": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.485 - } - }, - { - "sale total": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.485 - } - } - ] - }, - "sale.sales_amount_LY": { - "Definition": { - "Binding": { - "ConceptualEntity": "Sales", - "ConceptualProperty": "Sales Amount (LY)" - } - }, - "State": "Generated", - "Terms": [ - { - "sales amount (LY)": { - "State": "Generated" - } - }, - { - "sales amount": { - "State": "Generated", - "Weight": 0.75 - } - }, - { - "sale amount (ly)": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.605 - } - } - ] - }, - "sale.sales_amount_YTD": { - "Definition": { - "Binding": { - "ConceptualEntity": "Sales", - "ConceptualProperty": "Sales Amount (YTD)" - } - }, - "State": "Generated", - "Terms": [ - { - "sales amount (YTD)": { - "State": "Generated" - } - }, - { - "sales amount": { - "State": "Generated", - "Weight": 0.75 - } - }, - { - "sale amount (ytd)": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.605 - } - }, - { - "sale amount ( year to date )": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.528 - } - } - ] - }, - "sale.sales_amount_YTD_LY": { - "Definition": { - "Binding": { - "ConceptualEntity": "Sales", - "ConceptualProperty": "Sales Amount (YTD, LY)" - } - }, - "State": "Generated", - "Terms": [ - { - "sales amount (YTD, LY)": { - "State": "Generated" - } - }, - { - "sales amount": { - "State": "Generated", - "Weight": 0.75 - } - } - ] - }, - "sale.sales_amount___δ_LY": { - "Definition": { - "Binding": { - "ConceptualEntity": "Sales", - "ConceptualProperty": "Sales Amount (% Δ LY)" - } - }, - "State": "Generated", - "Terms": [ - { - "sales amount (% δ LY)": { - "State": "Generated" - } - }, - { - "sales amount": { - "State": "Generated", - "Weight": 0.75 - } - }, - { - "sales amount (% δ LY)": { - "State": "Generated", - "Weight": 0.97 - } - } - ] - }, - "sale.sales_amount__δ_LY": { - "Definition": { - "Binding": { - "ConceptualEntity": "Sales", - "ConceptualProperty": "Sales Amount (Δ LY)" - } - }, - "State": "Generated", - "Terms": [ - { - "sales amount (δ LY)": { - "State": "Generated" - } - }, - { - "sales amount": { - "State": "Generated", - "Weight": 0.75 - } - }, - { - "sales amount (δ LY)": { - "State": "Generated", - "Weight": 0.97 - } - } - ] - }, - "sale.sales_amount__δ_YTD_LY": { - "Definition": { - "Binding": { - "ConceptualEntity": "Sales", - "ConceptualProperty": "Sales Amount % (Δ YTD, LY)" - } - }, - "State": "Generated", - "Terms": [ - { - "sales amount % (δ YTD, LY)": { - "State": "Generated" - } - }, - { - "sales amount %": { - "State": "Generated", - "Weight": 0.75 - } - } - ], - "Visibility": { - "Value": "Hidden" - } - }, - "sale.sales_amount_avg_per_day": { - "Definition": { - "Binding": { - "ConceptualEntity": "Sales", - "ConceptualProperty": "Sales Amount Avg per Day" - } - }, - "State": "Generated", - "Terms": [ - { - "sales amount avg per day": { - "State": "Generated" - } - }, - { - "sale amount average per day": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.757 - } - }, - { - "sale amount avg per day": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.605 - } - } - ] - }, - "sale.sales_amount_δ_YTD_LY": { - "Definition": { - "Binding": { - "ConceptualEntity": "Sales", - "ConceptualProperty": "Sales Amount (Δ YTD, LY)" - } - }, - "State": "Generated", - "Terms": [ - { - "sales amount (δ YTD, LY)": { - "State": "Generated" - } - }, - { - "sales amount": { - "State": "Generated", - "Weight": 0.75 - } - } - ], - "Visibility": { - "Value": "Hidden" - } - }, - "sale.sales_qty": { - "Definition": { - "Binding": { - "ConceptualEntity": "Sales", - "ConceptualProperty": "Sales Qty" - } - }, - "State": "Generated", - "Terms": [ - { - "sales qty": { - "State": "Generated" - } - }, - { - "qty avg. mth": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.771 - } - }, - { - "qty average . month": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.599 - } - }, - { - "sale qty": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.582 - } - } - ] - }, - "sale.store_key": { - "Definition": { - "Binding": { - "ConceptualEntity": "Sales", - "ConceptualProperty": "StoreKey" - } - }, - "State": "Generated", - "Terms": [ - { - "store key": { - "State": "Generated" - } - }, - { - "StoreKey": { - "State": "Generated", - "Type": "Noun", - "Weight": 0.99 - } - }, - { - "key": { - "State": "Generated", - "Weight": 0.97 - } - }, - { - "main": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "basic": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "fundamental": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "central": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "major": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "keynote": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "essential": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "store solution": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.475 - } - }, - { - "store explanation": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.475 - } - }, - { - "store basis": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.475 - } - } - ], - "Visibility": { - "Value": "Hidden" - } - }, - "sale.unit_cost": { - "Definition": { - "Binding": { - "ConceptualEntity": "Sales", - "ConceptualProperty": "Unit Cost" - } - }, - "State": "Generated", - "Terms": [ - { - "unit cost": { - "State": "Generated" - } - }, - { - "cost": { - "State": "Generated", - "Weight": 0.97 - } - }, - { - "module cost": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.6 - } - }, - { - "element cost": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.6 - } - }, - { - "entity cost": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.6 - } - }, - { - "group cost": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.582 - } - }, - { - "component cost": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.582 - } - }, - { - "constituent cost": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.582 - } - }, - { - "item cost": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.582 - } - }, - { - "part cost": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.582 - } - }, - { - "section cost": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.582 - } - }, - { - "unit charge": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.485 - } - } - ], - "Visibility": { - "Value": "Hidden" - } - }, - "sale.v_Margin_FormatString_margin_FormatString": { - "Definition": { - "Binding": { - "ConceptualEntity": "Sales", - "ConceptualProperty": "_Margin FormatString" - } - }, - "State": "Generated", - "Terms": [ - { - "_margin FormatString": { - "State": "Generated" - } - } - ], - "Visibility": { - "Value": "Hidden" - } - }, - "sale.v_Margin__ly__FormatString_margin_ly_FormatString": { - "Definition": { - "Binding": { - "ConceptualEntity": "Sales", - "ConceptualProperty": "_Margin (ly) FormatString" - } - }, - "State": "Generated", - "Terms": [ - { - "_margin (ly) FormatString": { - "State": "Generated" - } - } - ], - "Visibility": { - "Value": "Hidden" - } - }, - "sale.v_Sales_Amount_Avg_per_Day_FormatString_sales_amount_avg_per_day_FormatString": { - "Definition": { - "Binding": { - "ConceptualEntity": "Sales", - "ConceptualProperty": "_Sales Amount Avg per Day FormatString" - } - }, - "State": "Generated", - "Terms": [ - { - "_sales amount avg per day FormatString": { - "State": "Generated" - } - } - ], - "Visibility": { - "Value": "Hidden" - } - }, - "sale.v_Sales_Amount_FormatString_sales_amount_FormatString": { - "Definition": { - "Binding": { - "ConceptualEntity": "Sales", - "ConceptualProperty": "_Sales Amount FormatString" - } - }, - "State": "Generated", - "Terms": [ - { - "_sales amount FormatString": { - "State": "Generated" - } - }, - { - "_ sale amount formatstring": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.599 - } - } - ], - "Visibility": { - "Value": "Hidden" - } - }, - "sale.v_Sales_Amount__LY__FormatString_sales_amount_LY_FormatString": { - "Definition": { - "Binding": { - "ConceptualEntity": "Sales", - "ConceptualProperty": "_Sales Amount (LY) FormatString" - } - }, - "State": "Generated", - "Terms": [ - { - "_sales amount (LY) FormatString": { - "State": "Generated" - } - } - ], - "Visibility": { - "Value": "Hidden" - } - }, - "sale.v_Sales_Amount__YTD__FormatString_sales_amount_YTD_FormatString": { - "Definition": { - "Binding": { - "ConceptualEntity": "Sales", - "ConceptualProperty": "_Sales Amount (YTD) FormatString" - } - }, - "State": "Generated", - "Terms": [ - { - "_sales amount (YTD) FormatString": { - "State": "Generated" - } - } - ], - "Visibility": { - "Value": "Hidden" - } - }, - "sale.v_Sales_Amount__YTD__LY__FormatString_sales_amount_YTD_LY_FormatString": { - "Definition": { - "Binding": { - "ConceptualEntity": "Sales", - "ConceptualProperty": "_Sales Amount (YTD, LY) FormatString" - } - }, - "State": "Generated", - "Terms": [ - { - "_sales amount (YTD, LY) FormatString": { - "State": "Generated" - } - } - ], - "Visibility": { - "Value": "Hidden" - } - }, - "sale.v_Sales_Amount_____Δ_LY__FormatString_sales_amount___δ_LY_FormatString": { - "Definition": { - "Binding": { - "ConceptualEntity": "Sales", - "ConceptualProperty": "_Sales Amount (% Δ LY) FormatString" - } - }, - "State": "Generated", - "Terms": [ - { - "_sales amount (% δ LY) FormatString": { - "State": "Generated" - } - }, - { - "_sales amount (% δ LY) FormatString": { - "State": "Generated", - "Weight": 0.97 - } - } - ], - "Visibility": { - "Value": "Hidden" - } - }, - "sale.v_Sales_Amount____Δ_YTD__LY__FormatString_sales_amount__δ_YTD_LY_FormatString": { - "Definition": { - "Binding": { - "ConceptualEntity": "Sales", - "ConceptualProperty": "_Sales Amount % (Δ YTD, LY) FormatString" - } - }, - "State": "Generated", - "Terms": [ - { - "_sales amount % (δ YTD, LY) FormatString": { - "State": "Generated" - } - } - ], - "Visibility": { - "Value": "Hidden" - } - }, - "sale.v_Sales_Amount___Δ_LY__FormatString_sales_amount__δ_LY_FormatString": { - "Definition": { - "Binding": { - "ConceptualEntity": "Sales", - "ConceptualProperty": "_Sales Amount (Δ LY) FormatString" - } - }, - "State": "Generated", - "Terms": [ - { - "_sales amount (δ LY) FormatString": { - "State": "Generated" - } - }, - { - "_sales amount (δ LY) FormatString": { - "State": "Generated", - "Weight": 0.97 - } - } - ], - "Visibility": { - "Value": "Hidden" - } - }, - "sale.v_Sales_Amount__Δ_YTD__LY__FormatString_sales_amount_δ_YTD_LY_FormatString": { - "Definition": { - "Binding": { - "ConceptualEntity": "Sales", - "ConceptualProperty": "_Sales Amount (Δ YTD, LY) FormatString" - } - }, - "State": "Generated", - "Terms": [ - { - "_sales amount (δ YTD, LY) FormatString": { - "State": "Generated" - } - } - ], - "Visibility": { - "Value": "Hidden" - } - }, - "sale.v_Sales_Qty_FormatString_sales_qty_FormatString": { - "Definition": { - "Binding": { - "ConceptualEntity": "Sales", - "ConceptualProperty": "_Sales Qty FormatString" - } - }, - "State": "Generated", - "Terms": [ - { - "_sales qty FormatString": { - "State": "Generated" - } - }, - { - "_ sale qty formatstring": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.599 - } - } - ], - "Visibility": { - "Value": "Hidden" - } - }, - "sale.v___Customers__with_Sales__FormatString__customers_with_sales_FormatString": { - "Definition": { - "Binding": { - "ConceptualEntity": "Sales", - "ConceptualProperty": "_# Customers (with Sales) FormatString" - } - }, - "State": "Generated", - "Terms": [ - { - "_# customers (with sales) FormatString": { - "State": "Generated" - } - } - ], - "Visibility": { - "Value": "Hidden" - } - }, - "sale.v___Products__with_Sales__FormatString__products_with_sales_FormatString": { - "Definition": { - "Binding": { - "ConceptualEntity": "Sales", - "ConceptualProperty": "_# Products (with Sales) FormatString" - } - }, - "State": "Generated", - "Terms": [ - { - "_# products (with sales) FormatString": { - "State": "Generated" - } - } - ], - "Visibility": { - "Value": "Hidden" - } - }, - "sale.v___Sales_FormatString__sales_FormatString": { - "Definition": { - "Binding": { - "ConceptualEntity": "Sales", - "ConceptualProperty": "_# Sales FormatString" - } - }, - "State": "Generated", - "Terms": [ - { - "_# sales FormatString": { - "State": "Generated" - } - }, - { - "_ no sale formatstring": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.599 - } - }, - { - "_ num sale formatstring": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.599 - } - }, - { - "_ number sale formatstring": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.599 - } - }, - { - "_# sale formatstring": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.599 - } - } - ], - "Visibility": { - "Value": "Hidden" - } - }, - "smart_calc": { - "Definition": { - "Binding": { - "ConceptualEntity": "Smart Calcs" - } - }, - "State": "Generated", - "Terms": [ - { - "smart calc": { - "State": "Generated" - } - } - ] - }, - "smart_calc.ordinal": { - "Definition": { - "Binding": { - "ConceptualEntity": "Smart Calcs", - "ConceptualProperty": "Ordinal" - } - }, - "State": "Generated", - "Terms": [ - { - "ordinal": { - "State": "Generated" - } - } - ], - "Visibility": { - "Value": "Hidden" - } - }, - "smart_calc.smart_calc": { - "Definition": { - "Binding": { - "ConceptualEntity": "Smart Calcs", - "ConceptualProperty": "Smart Calc" - } - }, - "NameType": "Name", - "State": "Generated", - "Terms": [ - { - "smart calc": { - "State": "Generated" - } - }, - { - "smart calc name": { - "State": "Generated" - } - } - ] - }, - "store": { - "Definition": { - "Binding": { - "ConceptualEntity": "Store" - } - }, - "State": "Generated", - "Terms": [ - { - "store": { - "State": "Generated" - } - }, - { - "accumulation": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.491 - } - }, - { - "collection": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.491 - } - }, - { - "stock": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "supply": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "stockpile": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "hoard": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "mass": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "pile": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "storehouse": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.466 - } - }, - { - "storeroom": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.466 - } - } - ] - }, - "store.close_date": { - "Definition": { - "Binding": { - "ConceptualEntity": "Store", - "ConceptualProperty": "Close Date" - } - }, - "SemanticType": "Time", - "State": "Generated", - "Terms": [ - { - "close date": { - "State": "Generated" - } - }, - { - "date": { - "State": "Generated", - "Weight": 0.7 - } - }, - { - "close moment": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.75 - } - }, - { - "close period": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.727 - } - } - ] - }, - "store.country": { - "Definition": { - "Binding": { - "ConceptualEntity": "Store", - "ConceptualProperty": "Country" - } - }, - "State": "Generated", - "Terms": [ - { - "country": { - "State": "Generated" - } - }, - { - "nation": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.736 - } - }, - { - "location": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.736 - } - } - ] - }, - "store.open_date": { - "Definition": { - "Binding": { - "ConceptualEntity": "Store", - "ConceptualProperty": "Open Date" - } - }, - "SemanticType": "Time", - "State": "Generated", - "Terms": [ - { - "open date": { - "State": "Generated" - } - }, - { - "date": { - "State": "Generated", - "Weight": 0.7 - } - }, - { - "open moment": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.75 - } - }, - { - "open period": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.727 - } - } - ] - }, - "store.square_meter": { - "Definition": { - "Binding": { - "ConceptualEntity": "Store", - "ConceptualProperty": "Square Meters" - } - }, - "State": "Generated", - "Terms": [ - { - "square meter": { - "State": "Generated" - } - }, - { - "meter": { - "State": "Generated", - "Weight": 0.97 - } - }, - { - "square rhythm": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.5 - } - }, - { - "square tempo": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.5 - } - }, - { - "square cadence": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.5 - } - }, - { - "rhythm": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.491 - } - }, - { - "tempo": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.491 - } - }, - { - "cadence": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.491 - } - }, - { - "square beat": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.485 - } - }, - { - "square pulse": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.485 - } - }, - { - "square pattern": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.485 - } - }, - { - "square stress": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.485 - } - } - ] - }, - "store.state": { - "Definition": { - "Binding": { - "ConceptualEntity": "Store", - "ConceptualProperty": "State" - } - }, - "State": "Generated", - "Terms": [ - {}, - { - "location": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.736 - } - }, - { - "province": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.736 - } - }, - { - "territory": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.736 - } - }, - { - "nation": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.736 - } - }, - { - "condition": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.714 - } - } - ] - }, - "store.status": { - "Definition": { - "Binding": { - "ConceptualEntity": "Store", - "ConceptualProperty": "Status" - } - }, - "State": "Generated", - "Terms": [ - { - "status": { - "State": "Generated" - } - }, - { - "importance": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.736 - } - }, - { - "prestige": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.736 - } - }, - { - "prominence": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.736 - } - }, - { - "condition": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.714 - } - }, - { - "class": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.714 - } - }, - { - "grade": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.714 - } - }, - { - "level": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.714 - } - }, - { - "position": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.714 - } - } - ] - }, - "store.store": { - "Definition": { - "Binding": { - "ConceptualEntity": "Store", - "ConceptualProperty": "Store" - } - }, - "NameType": "Name", - "State": "Generated", - "Terms": [ - { - "store": { - "State": "Generated" - } - }, - { - "store name": { - "State": "Generated" - } - }, - { - "accumulation": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.491 - } - }, - { - "collection": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.491 - } - }, - { - "stock": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "supply": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "stockpile": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "hoard": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "mass": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "pile": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "storehouse": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.466 - } - }, - { - "storeroom": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.466 - } - } - ] - }, - "store.store_code": { - "Definition": { - "Binding": { - "ConceptualEntity": "Store", - "ConceptualProperty": "Store Code" - } - }, - "NameType": "Identifier", - "State": "Generated", - "Terms": [ - { - "store code": { - "State": "Generated" - } - }, - { - "code": { - "State": "Generated", - "Weight": 0.97 - } - }, - { - "store id": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.75 - } - } - ] - }, - "store.store_key": { - "Definition": { - "Binding": { - "ConceptualEntity": "Store", - "ConceptualProperty": "StoreKey" - } - }, - "State": "Generated", - "Terms": [ - { - "store key": { - "State": "Generated" - } - }, - { - "StoreKey": { - "State": "Generated", - "Type": "Noun", - "Weight": 0.99 - } - }, - { - "key": { - "State": "Generated", - "Weight": 0.97 - } - }, - { - "main": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "basic": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "fundamental": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "central": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "major": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "keynote": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "essential": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.476 - } - }, - { - "store solution": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.475 - } - }, - { - "store explanation": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.475 - } - }, - { - "store basis": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.475 - } - } - ] - }, - "store.stores": { - "Definition": { - "Binding": { - "ConceptualEntity": "Store", - "ConceptualProperty": "# Stores" - } - }, - "State": "Generated", - "Terms": [ - { - "# stores": { - "State": "Generated" - } - }, - { - "no store": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.582 - } - }, - { - "num store": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.582 - } - }, - { - "number store": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.582 - } - }, - { - "# goods": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.5 - } - }, - { - "# foods": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.5 - } - }, - { - "# vittles": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.5 - } - }, - { - "# supply": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.485 - } - }, - { - "# provision": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.485 - } - }, - { - "# ration": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.485 - } - }, - { - "# accumulation": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.475 - } - } - ] - }, - "store.v___Stores_FormatString__stores_FormatString": { - "Definition": { - "Binding": { - "ConceptualEntity": "Store", - "ConceptualProperty": "_# Stores FormatString" - } - }, - "State": "Generated", - "Terms": [ - { - "_# stores FormatString": { - "State": "Generated" - } - }, - { - "_ no store formatstring": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.599 - } - }, - { - "_ num store formatstring": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.599 - } - }, - { - "_ number store formatstring": { - "Source": { - "Agent": "OfficeThesaurus" - }, - "State": "Suggested", - "Type": "Noun", - "Weight": 0.599 - } - } - ], - "Visibility": { - "Value": "Hidden" - } - } - }, - "Language": "en-US", - "Relationships": { - "calendar_has_date": { - "Binding": { - "ConceptualEntity": "Calendar" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "calendar.date" - }, - "Subject": { - "Role": "calendar" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "calendar": { - "Target": { - "Entity": "calendar" - } - }, - "calendar.date": { - "Target": { - "Entity": "calendar.date" - } - } - }, - "State": "Generated" - }, - "calendar_has_day": { - "Binding": { - "ConceptualEntity": "Calendar" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "calendar.day" - }, - "Subject": { - "Role": "calendar" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "calendar": { - "Target": { - "Entity": "calendar" - } - }, - "calendar.day": { - "Target": { - "Entity": "calendar.day" - } - } - }, - "State": "Generated" - }, - "calendar_has_day_relative": { - "Binding": { - "ConceptualEntity": "Calendar" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "calendar.day_relative" - }, - "Subject": { - "Role": "calendar" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "calendar": { - "Target": { - "Entity": "calendar" - } - }, - "calendar.day_relative": { - "Target": { - "Entity": "calendar.day_relative" - } - } - }, - "State": "Generated" - }, - "calendar_has_month": { - "Binding": { - "ConceptualEntity": "Calendar" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "calendar.month" - }, - "Subject": { - "Role": "calendar" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "calendar": { - "Target": { - "Entity": "calendar" - } - }, - "calendar.month": { - "Target": { - "Entity": "calendar.month" - } - } - }, - "State": "Generated" - }, - "calendar_has_month1": { - "Binding": { - "ConceptualEntity": "Calendar" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "calendar.month1" - }, - "Subject": { - "Role": "calendar" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "calendar": { - "Target": { - "Entity": "calendar" - } - }, - "calendar.month1": { - "Target": { - "Entity": "calendar.month1" - } - } - }, - "State": "Generated" - }, - "calendar_has_month_": { - "Binding": { - "ConceptualEntity": "Calendar" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "calendar.month_" - }, - "Subject": { - "Role": "calendar" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "calendar": { - "Target": { - "Entity": "calendar" - } - }, - "calendar.month_": { - "Target": { - "Entity": "calendar.month_" - } - } - }, - "State": "Generated" - }, - "calendar_has_month_long": { - "Binding": { - "ConceptualEntity": "Calendar" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "calendar.month_long" - }, - "Subject": { - "Role": "calendar" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "calendar": { - "Target": { - "Entity": "calendar" - } - }, - "calendar.month_long": { - "Target": { - "Entity": "calendar.month_long" - } - } - }, - "State": "Generated" - }, - "calendar_has_month_relative": { - "Binding": { - "ConceptualEntity": "Calendar" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "calendar.month_relative" - }, - "Subject": { - "Role": "calendar" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "calendar": { - "Target": { - "Entity": "calendar" - } - }, - "calendar.month_relative": { - "Target": { - "Entity": "calendar.month_relative" - } - } - }, - "State": "Generated" - }, - "calendar_has_month_start_date": { - "Binding": { - "ConceptualEntity": "Calendar" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "calendar.month_start_date" - }, - "Subject": { - "Role": "calendar" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "calendar": { - "Target": { - "Entity": "calendar" - } - }, - "calendar.month_start_date": { - "Target": { - "Entity": "calendar.month_start_date" - } - } - }, - "State": "Generated" - }, - "calendar_has_quarter": { - "Binding": { - "ConceptualEntity": "Calendar" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "calendar.quarter" - }, - "Subject": { - "Role": "calendar" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "calendar": { - "Target": { - "Entity": "calendar" - } - }, - "calendar.quarter": { - "Target": { - "Entity": "calendar.quarter" - } - } - }, - "State": "Generated" - }, - "calendar_has_quarter1": { - "Binding": { - "ConceptualEntity": "Calendar" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "calendar.quarter1" - }, - "Subject": { - "Role": "calendar" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "calendar": { - "Target": { - "Entity": "calendar" - } - }, - "calendar.quarter1": { - "Target": { - "Entity": "calendar.quarter1" - } - } - }, - "State": "Generated" - }, - "calendar_has_sale_sales_amount_avg_per_day": { - "Binding": { - "ConceptualEntity": "Calendar" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "sale.sales_amount_avg_per_day" - }, - "Subject": { - "Role": "calendar" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "calendar": { - "Target": { - "Entity": "calendar" - } - }, - "sale.sales_amount_avg_per_day": { - "Target": { - "Entity": "sale.sales_amount_avg_per_day" - } - } - }, - "State": "Generated" - }, - "calendar_has_semester": { - "Binding": { - "ConceptualEntity": "Calendar" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "calendar.semester" - }, - "Subject": { - "Role": "calendar" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "calendar": { - "Target": { - "Entity": "calendar" - } - }, - "calendar.semester": { - "Target": { - "Entity": "calendar.semester" - } - } - }, - "State": "Generated" - }, - "calendar_has_semester1": { - "Binding": { - "ConceptualEntity": "Calendar" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "calendar.semester1" - }, - "Subject": { - "Role": "calendar" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "calendar": { - "Target": { - "Entity": "calendar" - } - }, - "calendar.semester1": { - "Target": { - "Entity": "calendar.semester1" - } - } - }, - "State": "Generated" - }, - "calendar_has_week": { - "Binding": { - "ConceptualEntity": "Calendar" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "calendar.week" - }, - "Subject": { - "Role": "calendar" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "calendar": { - "Target": { - "Entity": "calendar" - } - }, - "calendar.week": { - "Target": { - "Entity": "calendar.week" - } - } - }, - "State": "Generated" - }, - "calendar_has_week1": { - "Binding": { - "ConceptualEntity": "Calendar" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "calendar.week1" - }, - "Subject": { - "Role": "calendar" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "calendar": { - "Target": { - "Entity": "calendar" - } - }, - "calendar.week1": { - "Target": { - "Entity": "calendar.week1" - } - } - }, - "State": "Generated" - }, - "calendar_has_week_day": { - "Binding": { - "ConceptualEntity": "Calendar" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "calendar.week_day" - }, - "Subject": { - "Role": "calendar" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "calendar": { - "Target": { - "Entity": "calendar" - } - }, - "calendar.week_day": { - "Target": { - "Entity": "calendar.week_day" - } - } - }, - "State": "Generated" - }, - "calendar_has_week_day_": { - "Binding": { - "ConceptualEntity": "Calendar" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "calendar.week_day_" - }, - "Subject": { - "Role": "calendar" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "calendar": { - "Target": { - "Entity": "calendar" - } - }, - "calendar.week_day_": { - "Target": { - "Entity": "calendar.week_day_" - } - } - }, - "State": "Generated" - }, - "calendar_has_week_end_date": { - "Binding": { - "ConceptualEntity": "Calendar" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "calendar.week_end_date" - }, - "Subject": { - "Role": "calendar" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "calendar": { - "Target": { - "Entity": "calendar" - } - }, - "calendar.week_end_date": { - "Target": { - "Entity": "calendar.week_end_date" - } - } - }, - "State": "Generated" - }, - "calendar_has_week_relative": { - "Binding": { - "ConceptualEntity": "Calendar" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "calendar.week_relative" - }, - "Subject": { - "Role": "calendar" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "calendar": { - "Target": { - "Entity": "calendar" - } - }, - "calendar.week_relative": { - "Target": { - "Entity": "calendar.week_relative" - } - } - }, - "State": "Generated" - }, - "calendar_has_week_start_date": { - "Binding": { - "ConceptualEntity": "Calendar" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "calendar.week_start_date" - }, - "Subject": { - "Role": "calendar" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "calendar": { - "Target": { - "Entity": "calendar" - } - }, - "calendar.week_start_date": { - "Target": { - "Entity": "calendar.week_start_date" - } - } - }, - "State": "Generated" - }, - "calendar_has_work_day": { - "Binding": { - "ConceptualEntity": "Calendar" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "calendar.work_day" - }, - "Subject": { - "Role": "calendar" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "calendar": { - "Target": { - "Entity": "calendar" - } - }, - "calendar.work_day": { - "Target": { - "Entity": "calendar.work_day" - } - } - }, - "State": "Generated" - }, - "calendar_has_year": { - "Binding": { - "ConceptualEntity": "Calendar" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "calendar.year" - }, - "Subject": { - "Role": "calendar" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "calendar": { - "Target": { - "Entity": "calendar" - } - }, - "calendar.year": { - "Target": { - "Entity": "calendar.year" - } - } - }, - "State": "Generated" - }, - "calendar_has_year_relative": { - "Binding": { - "ConceptualEntity": "Calendar" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "calendar.year_relative" - }, - "Subject": { - "Role": "calendar" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "calendar": { - "Target": { - "Entity": "calendar" - } - }, - "calendar.year_relative": { - "Target": { - "Entity": "calendar.year_relative" - } - } - }, - "State": "Generated" - }, - "calendar_is_ended_on_week_end_date": { - "Binding": { - "ConceptualEntity": "Calendar" - }, - "Phrasings": [ - { - "State": "Generated", - "Verb": { - "Object": { - "Role": "calendar" - }, - "Verbs": [ - { - "end": { - "State": "Generated" - } - } - ] - }, - "Weight": 0.9 - } - ], - "Roles": { - "calendar": { - "Target": { - "Entity": "calendar" - } - }, - "calendar.week_end_date": { - "Target": { - "Entity": "calendar.week_end_date" - } - } - }, - "SemanticSlots": { - "When": { - "Role": "calendar.week_end_date" - } - }, - "State": "Generated" - }, - "calendar_is_started_on_month_start_date": { - "Binding": { - "ConceptualEntity": "Calendar" - }, - "Phrasings": [ - { - "State": "Generated", - "Verb": { - "Object": { - "Role": "calendar" - }, - "Verbs": [ - { - "start": { - "State": "Generated" - } - } - ] - }, - "Weight": 0.9 - } - ], - "Roles": { - "calendar": { - "Target": { - "Entity": "calendar" - } - }, - "calendar.month_start_date": { - "Target": { - "Entity": "calendar.month_start_date" - } - } - }, - "SemanticSlots": { - "When": { - "Role": "calendar.month_start_date" - } - }, - "State": "Generated" - }, - "calendar_is_started_on_week_start_date": { - "Binding": { - "ConceptualEntity": "Calendar" - }, - "Phrasings": [ - { - "State": "Generated", - "Verb": { - "Object": { - "Role": "calendar" - }, - "Verbs": [ - { - "start": { - "State": "Generated" - } - } - ] - }, - "Weight": 0.9 - } - ], - "Roles": { - "calendar": { - "Target": { - "Entity": "calendar" - } - }, - "calendar.week_start_date": { - "Target": { - "Entity": "calendar.week_start_date" - } - } - }, - "SemanticSlots": { - "When": { - "Role": "calendar.week_start_date" - } - }, - "State": "Generated" - }, - "customer_has_address": { - "Binding": { - "ConceptualEntity": "Customer" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "customer.address" - }, - "Subject": { - "Role": "customer" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "customer": { - "Target": { - "Entity": "customer" - } - }, - "customer.address": { - "Target": { - "Entity": "customer.address" - } - } - }, - "State": "Generated" - }, - "customer_has_birthday": { - "Binding": { - "ConceptualEntity": "Customer" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "customer.birthday" - }, - "Subject": { - "Role": "customer" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "customer": { - "Target": { - "Entity": "customer" - } - }, - "customer.birthday": { - "Target": { - "Entity": "customer.birthday" - } - } - }, - "State": "Generated" - }, - "customer_has_city": { - "Binding": { - "ConceptualEntity": "Customer" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "customer.city" - }, - "Subject": { - "Role": "customer" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "customer": { - "Target": { - "Entity": "customer" - } - }, - "customer.city": { - "Target": { - "Entity": "customer.city" - } - } - }, - "State": "Generated" - }, - "customer_has_continent": { - "Binding": { - "ConceptualEntity": "Customer" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "customer.continent" - }, - "Subject": { - "Role": "customer" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "customer": { - "Target": { - "Entity": "customer" - } - }, - "customer.continent": { - "Target": { - "Entity": "customer.continent" - } - } - }, - "State": "Generated" - }, - "customer_has_country": { - "Binding": { - "ConceptualEntity": "Customer" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "customer.country" - }, - "Subject": { - "Role": "customer" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "customer": { - "Target": { - "Entity": "customer" - } - }, - "customer.country": { - "Target": { - "Entity": "customer.country" - } - } - }, - "State": "Generated" - }, - "customer_has_country_code": { - "Binding": { - "ConceptualEntity": "Customer" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "customer.country_code" - }, - "Subject": { - "Role": "customer" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "customer": { - "Target": { - "Entity": "customer" - } - }, - "customer.country_code": { - "Target": { - "Entity": "customer.country_code" - } - } - }, - "State": "Generated" - }, - "customer_has_customer_key": { - "Binding": { - "ConceptualEntity": "Customer" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "customer.customer_key" - }, - "Subject": { - "Role": "customer" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "customer": { - "Target": { - "Entity": "customer" - } - }, - "customer.customer_key": { - "Target": { - "Entity": "customer.customer_key" - } - } - }, - "State": "Generated" - }, - "customer_has_customers": { - "Binding": { - "ConceptualEntity": "Customer" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "customer.customers" - }, - "Subject": { - "Role": "customer" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "customer": { - "Target": { - "Entity": "customer" - } - }, - "customer.customers": { - "Target": { - "Entity": "customer.customers" - } - } - }, - "State": "Generated" - }, - "customer_has_gender": { - "Binding": { - "ConceptualEntity": "Customer" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "customer.gender" - }, - "Subject": { - "Role": "customer" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "customer": { - "Target": { - "Entity": "customer" - } - }, - "customer.gender": { - "Target": { - "Entity": "customer.gender" - } - } - }, - "State": "Generated" - }, - "customer_has_state": { - "Binding": { - "ConceptualEntity": "Customer" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "customer.state" - }, - "Subject": { - "Role": "customer" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "customer": { - "Target": { - "Entity": "customer" - } - }, - "customer.state": { - "Target": { - "Entity": "customer.state" - } - } - }, - "State": "Generated" - }, - "customer_has_state_code": { - "Binding": { - "ConceptualEntity": "Customer" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "customer.state_code" - }, - "Subject": { - "Role": "customer" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "customer": { - "Target": { - "Entity": "customer" - } - }, - "customer.state_code": { - "Target": { - "Entity": "customer.state_code" - } - } - }, - "State": "Generated" - }, - "customer_has_zip_code": { - "Binding": { - "ConceptualEntity": "Customer" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "customer.zip_code" - }, - "Subject": { - "Role": "customer" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "customer": { - "Target": { - "Entity": "customer" - } - }, - "customer.zip_code": { - "Target": { - "Entity": "customer.zip_code" - } - } - }, - "State": "Generated" - }, - "customer_in_address": { - "Binding": { - "ConceptualEntity": "Customer" - }, - "Phrasings": [ - { - "Preposition": { - "Object": { - "Role": "customer.address" - }, - "Prepositions": [ - { - "in": { - "State": "Generated" - } - } - ], - "Subject": { - "Role": "customer" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "customer": { - "Target": { - "Entity": "customer" - } - }, - "customer.address": { - "Target": { - "Entity": "customer.address" - } - } - }, - "SemanticSlots": { - "Where": { - "Role": "customer.address" - } - }, - "State": "Generated" - }, - "customer_in_city": { - "Binding": { - "ConceptualEntity": "Customer" - }, - "Phrasings": [ - { - "Preposition": { - "Object": { - "Role": "customer.city" - }, - "Prepositions": [ - { - "in": { - "State": "Generated" - } - } - ], - "Subject": { - "Role": "customer" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "customer": { - "Target": { - "Entity": "customer" - } - }, - "customer.city": { - "Target": { - "Entity": "customer.city" - } - } - }, - "SemanticSlots": { - "Where": { - "Role": "customer.city" - } - }, - "State": "Generated" - }, - "customer_in_continent": { - "Binding": { - "ConceptualEntity": "Customer" - }, - "Phrasings": [ - { - "Preposition": { - "Object": { - "Role": "customer.continent" - }, - "Prepositions": [ - { - "in": { - "State": "Generated" - } - } - ], - "Subject": { - "Role": "customer" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "customer": { - "Target": { - "Entity": "customer" - } - }, - "customer.continent": { - "Target": { - "Entity": "customer.continent" - } - } - }, - "SemanticSlots": { - "Where": { - "Role": "customer.continent" - } - }, - "State": "Generated" - }, - "customer_in_country": { - "Binding": { - "ConceptualEntity": "Customer" - }, - "Phrasings": [ - { - "Preposition": { - "Object": { - "Role": "customer.country" - }, - "Prepositions": [ - { - "in": { - "State": "Generated" - } - } - ], - "Subject": { - "Role": "customer" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "customer": { - "Target": { - "Entity": "customer" - } - }, - "customer.country": { - "Target": { - "Entity": "customer.country" - } - } - }, - "SemanticSlots": { - "Where": { - "Role": "customer.country" - } - }, - "State": "Generated" - }, - "customer_in_country_code": { - "Binding": { - "ConceptualEntity": "Customer" - }, - "Phrasings": [ - { - "Preposition": { - "Object": { - "Role": "customer.country_code" - }, - "Prepositions": [ - { - "in": { - "State": "Generated" - } - } - ], - "Subject": { - "Role": "customer" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "customer": { - "Target": { - "Entity": "customer" - } - }, - "customer.country_code": { - "Target": { - "Entity": "customer.country_code" - } - } - }, - "SemanticSlots": { - "Where": { - "Role": "customer.country_code" - } - }, - "State": "Generated" - }, - "customer_in_state": { - "Binding": { - "ConceptualEntity": "Customer" - }, - "Phrasings": [ - { - "Preposition": { - "Object": { - "Role": "customer.state" - }, - "Prepositions": [ - { - "in": { - "State": "Generated" - } - } - ], - "Subject": { - "Role": "customer" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "customer": { - "Target": { - "Entity": "customer" - } - }, - "customer.state": { - "Target": { - "Entity": "customer.state" - } - } - }, - "SemanticSlots": { - "Where": { - "Role": "customer.state" - } - }, - "State": "Generated" - }, - "customer_in_state_code": { - "Binding": { - "ConceptualEntity": "Customer" - }, - "Phrasings": [ - { - "Preposition": { - "Object": { - "Role": "customer.state_code" - }, - "Prepositions": [ - { - "in": { - "State": "Generated" - } - } - ], - "Subject": { - "Role": "customer" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "customer": { - "Target": { - "Entity": "customer" - } - }, - "customer.state_code": { - "Target": { - "Entity": "customer.state_code" - } - } - }, - "SemanticSlots": { - "Where": { - "Role": "customer.state_code" - } - }, - "State": "Generated" - }, - "customer_in_zip_code": { - "Binding": { - "ConceptualEntity": "Customer" - }, - "Phrasings": [ - { - "Preposition": { - "Object": { - "Role": "customer.zip_code" - }, - "Prepositions": [ - { - "in": { - "State": "Generated" - } - } - ], - "Subject": { - "Role": "customer" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "customer": { - "Target": { - "Entity": "customer" - } - }, - "customer.zip_code": { - "Target": { - "Entity": "customer.zip_code" - } - } - }, - "SemanticSlots": { - "Where": { - "Role": "customer.zip_code" - } - }, - "State": "Generated" - }, - "customer_is_born_on_birthday": { - "Binding": { - "ConceptualEntity": "Customer" - }, - "Phrasings": [ - { - "State": "Generated", - "Verb": { - "Object": { - "Role": "customer" - }, - "Verbs": [ - { - "bear": { - "State": "Generated" - } - } - ] - }, - "Weight": 0.9 - } - ], - "Roles": { - "customer": { - "Target": { - "Entity": "customer" - } - }, - "customer.birthday": { - "Target": { - "Entity": "customer.birthday" - } - } - }, - "SemanticSlots": { - "When": { - "Role": "customer.birthday" - } - }, - "State": "Generated" - }, - "customer_is_named_customer": { - "Binding": { - "ConceptualEntity": "Customer" - }, - "Phrasings": [ - { - "Name": { - "Name": { - "Role": "customer.customer" - }, - "Subject": { - "Role": "customer" - } - }, - "State": "Generated", - "Weight": 0.99 - }, - { - "Attribute": { - "Object": { - "Role": "customer.customer" - }, - "Subject": { - "Role": "customer" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "customer": { - "Target": { - "Entity": "customer" - } - }, - "customer.customer": { - "Target": { - "Entity": "customer.customer" - } - } - }, - "State": "Generated" - }, - "customer_is_old": { - "Binding": { - "ConceptualEntity": "Customer" - }, - "Phrasings": [ - { - "Adjective": { - "Adjectives": [ - { - "old": { - "State": "Generated" - } - } - ], - "Antonyms": [ - { - "young": { - "State": "Generated" - } - } - ], - "Measurement": { - "Role": "customer.age" - }, - "Subject": { - "Role": "customer" - } - }, - "State": "Generated", - "Weight": 0.99 - }, - { - "Attribute": { - "Object": { - "Role": "customer.age" - }, - "Subject": { - "Role": "customer" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "customer": { - "Target": { - "Entity": "customer" - } - }, - "customer.age": { - "Target": { - "Entity": "customer.age" - } - } - }, - "State": "Generated" - }, - "dynamic_measure_has_area": { - "Binding": { - "ConceptualEntity": "Dynamic Measure" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "dynamic_measure.area" - }, - "Subject": { - "Role": "dynamic_measure" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "dynamic_measure": { - "Target": { - "Entity": "dynamic_measure" - } - }, - "dynamic_measure.area": { - "Target": { - "Entity": "dynamic_measure.area" - } - } - }, - "State": "Generated" - }, - "dynamic_measure_has_code": { - "Binding": { - "ConceptualEntity": "Dynamic Measure" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "dynamic_measure.code" - }, - "Subject": { - "Role": "dynamic_measure" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "dynamic_measure": { - "Target": { - "Entity": "dynamic_measure" - } - }, - "dynamic_measure.code": { - "Target": { - "Entity": "dynamic_measure.code" - } - } - }, - "State": "Generated" - }, - "dynamic_measure_has_format": { - "Binding": { - "ConceptualEntity": "Dynamic Measure" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "dynamic_measure.format" - }, - "Subject": { - "Role": "dynamic_measure" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "dynamic_measure": { - "Target": { - "Entity": "dynamic_measure" - } - }, - "dynamic_measure.format": { - "Target": { - "Entity": "dynamic_measure.format" - } - } - }, - "State": "Generated" - }, - "dynamic_measure_has_measure": { - "Binding": { - "ConceptualEntity": "Dynamic Measure" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "dynamic_measure.measure" - }, - "Subject": { - "Role": "dynamic_measure" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "dynamic_measure": { - "Target": { - "Entity": "dynamic_measure" - } - }, - "dynamic_measure.measure": { - "Target": { - "Entity": "dynamic_measure.measure" - } - } - }, - "State": "Generated" - }, - "dynamic_measure_has_order": { - "Binding": { - "ConceptualEntity": "Dynamic Measure" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "dynamic_measure.order" - }, - "Subject": { - "Role": "dynamic_measure" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "dynamic_measure": { - "Target": { - "Entity": "dynamic_measure" - } - }, - "dynamic_measure.order": { - "Target": { - "Entity": "dynamic_measure.order" - } - } - }, - "State": "Generated" - }, - "dynamic_measure_has_value": { - "Binding": { - "ConceptualEntity": "Dynamic Measure" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "dynamic_measure.value" - }, - "Subject": { - "Role": "dynamic_measure" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "dynamic_measure": { - "Target": { - "Entity": "dynamic_measure" - } - }, - "dynamic_measure.value": { - "Target": { - "Entity": "dynamic_measure.value" - } - } - }, - "State": "Generated" - }, - "dynamic_measure_has_value__δ_ly": { - "Binding": { - "ConceptualEntity": "Dynamic Measure" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "dynamic_measure.value__δ_ly" - }, - "Subject": { - "Role": "dynamic_measure" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "dynamic_measure": { - "Target": { - "Entity": "dynamic_measure" - } - }, - "dynamic_measure.value__δ_ly": { - "Target": { - "Entity": "dynamic_measure.value__δ_ly" - } - } - }, - "State": "Generated" - }, - "dynamic_measure_has_value_avg_per_month": { - "Binding": { - "ConceptualEntity": "Dynamic Measure" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "dynamic_measure.value_avg_per_month" - }, - "Subject": { - "Role": "dynamic_measure" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "dynamic_measure": { - "Target": { - "Entity": "dynamic_measure" - } - }, - "dynamic_measure.value_avg_per_month": { - "Target": { - "Entity": "dynamic_measure.value_avg_per_month" - } - } - }, - "State": "Generated" - }, - "dynamic_measure_has_value_daily_max": { - "Binding": { - "ConceptualEntity": "Dynamic Measure" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "dynamic_measure.value_daily_max" - }, - "Subject": { - "Role": "dynamic_measure" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "dynamic_measure": { - "Target": { - "Entity": "dynamic_measure" - } - }, - "dynamic_measure.value_daily_max": { - "Target": { - "Entity": "dynamic_measure.value_daily_max" - } - } - }, - "State": "Generated" - }, - "dynamic_measure_has_value_ly": { - "Binding": { - "ConceptualEntity": "Dynamic Measure" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "dynamic_measure.value_ly" - }, - "Subject": { - "Role": "dynamic_measure" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "dynamic_measure": { - "Target": { - "Entity": "dynamic_measure" - } - }, - "dynamic_measure.value_ly": { - "Target": { - "Entity": "dynamic_measure.value_ly" - } - } - }, - "State": "Generated" - }, - "dynamic_measure_has_value_normalized_by_date": { - "Binding": { - "ConceptualEntity": "Dynamic Measure" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "dynamic_measure.value_normalized_by_date" - }, - "Subject": { - "Role": "dynamic_measure" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "dynamic_measure": { - "Target": { - "Entity": "dynamic_measure" - } - }, - "dynamic_measure.value_normalized_by_date": { - "Target": { - "Entity": "dynamic_measure.value_normalized_by_date" - } - } - }, - "State": "Generated" - }, - "dynamic_measure_has_value_ytd": { - "Binding": { - "ConceptualEntity": "Dynamic Measure" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "dynamic_measure.value_ytd" - }, - "Subject": { - "Role": "dynamic_measure" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "dynamic_measure": { - "Target": { - "Entity": "dynamic_measure" - } - }, - "dynamic_measure.value_ytd": { - "Target": { - "Entity": "dynamic_measure.value_ytd" - } - } - }, - "State": "Generated" - }, - "dynamic_measure_in_area": { - "Binding": { - "ConceptualEntity": "Dynamic Measure" - }, - "Phrasings": [ - { - "Preposition": { - "Object": { - "Role": "dynamic_measure.area" - }, - "Prepositions": [ - { - "in": { - "State": "Generated" - } - } - ], - "Subject": { - "Role": "dynamic_measure" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "dynamic_measure": { - "Target": { - "Entity": "dynamic_measure" - } - }, - "dynamic_measure.area": { - "Target": { - "Entity": "dynamic_measure.area" - } - } - }, - "SemanticSlots": { - "Where": { - "Role": "dynamic_measure.area" - } - }, - "State": "Generated" - }, - "product_has_brand": { - "Binding": { - "ConceptualEntity": "Product" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "product.brand" - }, - "Subject": { - "Role": "product" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "product": { - "Target": { - "Entity": "product" - } - }, - "product.brand": { - "Target": { - "Entity": "product.brand" - } - } - }, - "State": "Generated" - }, - "product_has_category": { - "Binding": { - "ConceptualEntity": "Product" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "product.category" - }, - "Subject": { - "Role": "product" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "product": { - "Target": { - "Entity": "product" - } - }, - "product.category": { - "Target": { - "Entity": "product.category" - } - } - }, - "State": "Generated" - }, - "product_has_category_code": { - "Binding": { - "ConceptualEntity": "Product" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "product.category_code" - }, - "Subject": { - "Role": "product" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "product": { - "Target": { - "Entity": "product" - } - }, - "product.category_code": { - "Target": { - "Entity": "product.category_code" - } - } - }, - "State": "Generated" - }, - "product_has_color": { - "Binding": { - "ConceptualEntity": "Product" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "product.color" - }, - "Subject": { - "Role": "product" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "product": { - "Target": { - "Entity": "product" - } - }, - "product.color": { - "Target": { - "Entity": "product.color" - } - } - }, - "State": "Generated" - }, - "product_has_product_key": { - "Binding": { - "ConceptualEntity": "Product" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "product.product_key" - }, - "Subject": { - "Role": "product" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "product": { - "Target": { - "Entity": "product" - } - }, - "product.product_key": { - "Target": { - "Entity": "product.product_key" - } - } - }, - "State": "Generated" - }, - "product_has_products": { - "Binding": { - "ConceptualEntity": "Product" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "product.products" - }, - "Subject": { - "Role": "product" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "product": { - "Target": { - "Entity": "product" - } - }, - "product.products": { - "Target": { - "Entity": "product.products" - } - } - }, - "State": "Generated" - }, - "product_has_subcategory": { - "Binding": { - "ConceptualEntity": "Product" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "product.subcategory" - }, - "Subject": { - "Role": "product" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "product": { - "Target": { - "Entity": "product" - } - }, - "product.subcategory": { - "Target": { - "Entity": "product.subcategory" - } - } - }, - "State": "Generated" - }, - "product_has_subcategory_code": { - "Binding": { - "ConceptualEntity": "Product" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "product.subcategory_code" - }, - "Subject": { - "Role": "product" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "product": { - "Target": { - "Entity": "product" - } - }, - "product.subcategory_code": { - "Target": { - "Entity": "product.subcategory_code" - } - } - }, - "State": "Generated" - }, - "product_has_weight_unit_measure": { - "Binding": { - "ConceptualEntity": "Product" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "product.weight_unit_measure" - }, - "Subject": { - "Role": "product" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "product": { - "Target": { - "Entity": "product" - } - }, - "product.weight_unit_measure": { - "Target": { - "Entity": "product.weight_unit_measure" - } - } - }, - "State": "Generated" - }, - "product_is_expensive": { - "Binding": { - "ConceptualEntity": "Product" - }, - "Phrasings": [ - { - "Adjective": { - "Adjectives": [ - { - "expensive": { - "State": "Generated" - } - } - ], - "Antonyms": [ - { - "cheap": { - "State": "Generated" - } - } - ], - "Measurement": { - "Role": "product.unit_cost" - }, - "Subject": { - "Role": "product" - } - }, - "State": "Generated", - "Weight": 0.99 - }, - { - "Attribute": { - "Object": { - "Role": "product.unit_cost" - }, - "Subject": { - "Role": "product" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "product": { - "Target": { - "Entity": "product" - } - }, - "product.unit_cost": { - "Target": { - "Entity": "product.unit_cost" - } - } - }, - "State": "Generated" - }, - "product_is_expensive1": { - "Binding": { - "ConceptualEntity": "Product" - }, - "Phrasings": [ - { - "Adjective": { - "Adjectives": [ - { - "expensive": { - "State": "Generated" - } - } - ], - "Antonyms": [ - { - "cheap": { - "State": "Generated" - } - } - ], - "Measurement": { - "Role": "product.unit_price" - }, - "Subject": { - "Role": "product" - } - }, - "State": "Generated", - "Weight": 0.99 - }, - { - "Attribute": { - "Object": { - "Role": "product.unit_price" - }, - "Subject": { - "Role": "product" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "product": { - "Target": { - "Entity": "product" - } - }, - "product.unit_price": { - "Target": { - "Entity": "product.unit_price" - } - } - }, - "State": "Generated" - }, - "product_is_heavy": { - "Binding": { - "ConceptualEntity": "Product" - }, - "Phrasings": [ - { - "Adjective": { - "Adjectives": [ - { - "heavy": { - "State": "Generated" - } - } - ], - "Antonyms": [ - { - "light": { - "State": "Generated" - } - } - ], - "Measurement": { - "Role": "product.weight" - }, - "Subject": { - "Role": "product" - } - }, - "State": "Generated", - "Weight": 0.99 - }, - { - "Attribute": { - "Object": { - "Role": "product.weight" - }, - "Subject": { - "Role": "product" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "product": { - "Target": { - "Entity": "product" - } - }, - "product.weight": { - "Target": { - "Entity": "product.weight" - } - } - }, - "State": "Generated" - }, - "product_is_named_product": { - "Binding": { - "ConceptualEntity": "Product" - }, - "Phrasings": [ - { - "Name": { - "Name": { - "Role": "product.product" - }, - "Subject": { - "Role": "product" - } - }, - "State": "Generated", - "Weight": 0.99 - }, - { - "Attribute": { - "Object": { - "Role": "product.product" - }, - "Subject": { - "Role": "product" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "product": { - "Target": { - "Entity": "product" - } - }, - "product.product": { - "Target": { - "Entity": "product.product" - } - } - }, - "State": "Generated" - }, - "product_is_named_product_code": { - "Binding": { - "ConceptualEntity": "Product" - }, - "Phrasings": [ - { - "Name": { - "Name": { - "Role": "product.product_code" - }, - "Subject": { - "Role": "product" - } - }, - "State": "Generated", - "Weight": 0.99 - }, - { - "Attribute": { - "Object": { - "Role": "product.product_code" - }, - "Subject": { - "Role": "product" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "product": { - "Target": { - "Entity": "product" - } - }, - "product.product_code": { - "Target": { - "Entity": "product.product_code" - } - } - }, - "State": "Generated" - }, - "product_manufacturer_manufacture_product": { - "Binding": { - "ConceptualEntity": "Product" - }, - "Phrasings": [ - { - "State": "Generated", - "Verb": { - "Object": { - "Role": "product" - }, - "Subject": { - "Role": "product.manufacturer" - }, - "Verbs": [ - { - "manufacture": { - "State": "Generated" - } - } - ] - }, - "Weight": 0.75 - }, - { - "Attribute": { - "Object": { - "Role": "product" - }, - "Subject": { - "Role": "product.manufacturer" - } - }, - "State": "Generated", - "Weight": 0.99 - }, - { - "Attribute": { - "Object": { - "Role": "product.manufacturer" - }, - "Subject": { - "Role": "product" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "product": { - "Target": { - "Entity": "product" - } - }, - "product.manufacturer": { - "Target": { - "Entity": "product.manufacturer" - } - } - }, - "State": "Generated" - }, - "sale_has_calendar": { - "Binding": { - "ConceptualEntity": "Sales" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "calendar" - }, - "Subject": { - "Role": "sale" - } - }, - "State": "Generated", - "Weight": 0.99 - }, - { - "Attribute": { - "Object": { - "Role": "sale" - }, - "Subject": { - "Role": "calendar" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "calendar": { - "Target": { - "Entity": "calendar" - } - }, - "sale": { - "Target": { - "Entity": "sale" - } - } - }, - "State": "Generated" - }, - "sale_has_currency_code": { - "Binding": { - "ConceptualEntity": "Sales" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "sale.currency_code" - }, - "Subject": { - "Role": "sale" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "sale": { - "Target": { - "Entity": "sale" - } - }, - "sale.currency_code": { - "Target": { - "Entity": "sale.currency_code" - } - } - }, - "State": "Generated" - }, - "sale_has_customer": { - "Binding": { - "ConceptualEntity": "Sales" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "customer" - }, - "Subject": { - "Role": "sale" - } - }, - "State": "Generated", - "Weight": 0.99 - }, - { - "Attribute": { - "Object": { - "Role": "sale" - }, - "Subject": { - "Role": "customer" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "customer": { - "Target": { - "Entity": "customer" - } - }, - "sale": { - "Target": { - "Entity": "sale" - } - } - }, - "State": "Generated" - }, - "sale_has_customers_with_sales": { - "Binding": { - "ConceptualEntity": "Sales" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "sale.customers_with_sales" - }, - "Subject": { - "Role": "sale" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "sale": { - "Target": { - "Entity": "sale" - } - }, - "sale.customers_with_sales": { - "Target": { - "Entity": "sale.customers_with_sales" - } - } - }, - "State": "Generated" - }, - "sale_has_delivery_date": { - "Binding": { - "ConceptualEntity": "Sales" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "sale.delivery_date" - }, - "Subject": { - "Role": "sale" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "sale": { - "Target": { - "Entity": "sale" - } - }, - "sale.delivery_date": { - "Target": { - "Entity": "sale.delivery_date" - } - } - }, - "State": "Generated" - }, - "sale_has_exchange_rate": { - "Binding": { - "ConceptualEntity": "Sales" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "sale.exchange_rate" - }, - "Subject": { - "Role": "sale" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "sale": { - "Target": { - "Entity": "sale" - } - }, - "sale.exchange_rate": { - "Target": { - "Entity": "sale.exchange_rate" - } - } - }, - "State": "Generated" - }, - "sale_has_line_number": { - "Binding": { - "ConceptualEntity": "Sales" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "sale.line_number" - }, - "Subject": { - "Role": "sale" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "sale": { - "Target": { - "Entity": "sale" - } - }, - "sale.line_number": { - "Target": { - "Entity": "sale.line_number" - } - } - }, - "State": "Generated" - }, - "sale_has_margin": { - "Binding": { - "ConceptualEntity": "Sales" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "sale.margin" - }, - "Subject": { - "Role": "sale" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "sale": { - "Target": { - "Entity": "sale" - } - }, - "sale.margin": { - "Target": { - "Entity": "sale.margin" - } - } - }, - "State": "Generated" - }, - "sale_has_margin_ly": { - "Binding": { - "ConceptualEntity": "Sales" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "sale.margin_ly" - }, - "Subject": { - "Role": "sale" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "sale": { - "Target": { - "Entity": "sale" - } - }, - "sale.margin_ly": { - "Target": { - "Entity": "sale.margin_ly" - } - } - }, - "State": "Generated" - }, - "sale_has_order_date": { - "Binding": { - "ConceptualEntity": "Sales" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "sale.order_date" - }, - "Subject": { - "Role": "sale" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "sale": { - "Target": { - "Entity": "sale" - } - }, - "sale.order_date": { - "Target": { - "Entity": "sale.order_date" - } - } - }, - "State": "Generated" - }, - "sale_has_order_number": { - "Binding": { - "ConceptualEntity": "Sales" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "sale.order_number" - }, - "Subject": { - "Role": "sale" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "sale": { - "Target": { - "Entity": "sale" - } - }, - "sale.order_number": { - "Target": { - "Entity": "sale.order_number" - } - } - }, - "State": "Generated" - }, - "sale_has_product": { - "Binding": { - "ConceptualEntity": "Sales" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "product" - }, - "Subject": { - "Role": "sale" - } - }, - "State": "Generated", - "Weight": 0.99 - }, - { - "Attribute": { - "Object": { - "Role": "sale" - }, - "Subject": { - "Role": "product" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "product": { - "Target": { - "Entity": "product" - } - }, - "sale": { - "Target": { - "Entity": "sale" - } - } - }, - "State": "Generated" - }, - "sale_has_products_with_sales": { - "Binding": { - "ConceptualEntity": "Sales" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "sale.products_with_sales" - }, - "Subject": { - "Role": "sale" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "sale": { - "Target": { - "Entity": "sale" - } - }, - "sale.products_with_sales": { - "Target": { - "Entity": "sale.products_with_sales" - } - } - }, - "State": "Generated" - }, - "sale_has_sales": { - "Binding": { - "ConceptualEntity": "Sales" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "sale.sales" - }, - "Subject": { - "Role": "sale" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "sale": { - "Target": { - "Entity": "sale" - } - }, - "sale.sales": { - "Target": { - "Entity": "sale.sales" - } - } - }, - "State": "Generated" - }, - "sale_has_sales_amount": { - "Binding": { - "ConceptualEntity": "Sales" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "sale.sales_amount" - }, - "Subject": { - "Role": "sale" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "sale": { - "Target": { - "Entity": "sale" - } - }, - "sale.sales_amount": { - "Target": { - "Entity": "sale.sales_amount" - } - } - }, - "State": "Generated" - }, - "sale_has_sales_amount_LY": { - "Binding": { - "ConceptualEntity": "Sales" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "sale.sales_amount_LY" - }, - "Subject": { - "Role": "sale" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "sale": { - "Target": { - "Entity": "sale" - } - }, - "sale.sales_amount_LY": { - "Target": { - "Entity": "sale.sales_amount_LY" - } - } - }, - "State": "Generated" - }, - "sale_has_sales_amount_YTD": { - "Binding": { - "ConceptualEntity": "Sales" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "sale.sales_amount_YTD" - }, - "Subject": { - "Role": "sale" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "sale": { - "Target": { - "Entity": "sale" - } - }, - "sale.sales_amount_YTD": { - "Target": { - "Entity": "sale.sales_amount_YTD" - } - } - }, - "State": "Generated" - }, - "sale_has_sales_amount_YTD_LY": { - "Binding": { - "ConceptualEntity": "Sales" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "sale.sales_amount_YTD_LY" - }, - "Subject": { - "Role": "sale" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "sale": { - "Target": { - "Entity": "sale" - } - }, - "sale.sales_amount_YTD_LY": { - "Target": { - "Entity": "sale.sales_amount_YTD_LY" - } - } - }, - "State": "Generated" - }, - "sale_has_sales_amount___δ_LY": { - "Binding": { - "ConceptualEntity": "Sales" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "sale.sales_amount___δ_LY" - }, - "Subject": { - "Role": "sale" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "sale": { - "Target": { - "Entity": "sale" - } - }, - "sale.sales_amount___δ_LY": { - "Target": { - "Entity": "sale.sales_amount___δ_LY" - } - } - }, - "State": "Generated" - }, - "sale_has_sales_amount__δ_LY": { - "Binding": { - "ConceptualEntity": "Sales" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "sale.sales_amount__δ_LY" - }, - "Subject": { - "Role": "sale" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "sale": { - "Target": { - "Entity": "sale" - } - }, - "sale.sales_amount__δ_LY": { - "Target": { - "Entity": "sale.sales_amount__δ_LY" - } - } - }, - "State": "Generated" - }, - "sale_has_sales_amount_avg_per_day": { - "Binding": { - "ConceptualEntity": "Sales" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "sale.sales_amount_avg_per_day" - }, - "Subject": { - "Role": "sale" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "sale": { - "Target": { - "Entity": "sale" - } - }, - "sale.sales_amount_avg_per_day": { - "Target": { - "Entity": "sale.sales_amount_avg_per_day" - } - } - }, - "State": "Generated" - }, - "sale_has_sales_qty": { - "Binding": { - "ConceptualEntity": "Sales" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "sale.sales_qty" - }, - "Subject": { - "Role": "sale" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "sale": { - "Target": { - "Entity": "sale" - } - }, - "sale.sales_qty": { - "Target": { - "Entity": "sale.sales_qty" - } - } - }, - "State": "Generated" - }, - "sale_has_store": { - "Binding": { - "ConceptualEntity": "Sales" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "store" - }, - "Subject": { - "Role": "sale" - } - }, - "State": "Generated", - "Weight": 0.99 - }, - { - "Attribute": { - "Object": { - "Role": "sale" - }, - "Subject": { - "Role": "store" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "sale": { - "Target": { - "Entity": "sale" - } - }, - "store": { - "Target": { - "Entity": "store" - } - } - }, - "State": "Generated" - }, - "sale_is_expensive": { - "Binding": { - "ConceptualEntity": "Sales" - }, - "Phrasings": [ - { - "Adjective": { - "Adjectives": [ - { - "expensive": { - "State": "Generated" - } - } - ], - "Antonyms": [ - { - "cheap": { - "State": "Generated" - } - } - ], - "Measurement": { - "Role": "sale.net_price" - }, - "Subject": { - "Role": "sale" - } - }, - "State": "Generated", - "Weight": 0.99 - }, - { - "Attribute": { - "Object": { - "Role": "sale.net_price" - }, - "Subject": { - "Role": "sale" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "sale": { - "Target": { - "Entity": "sale" - } - }, - "sale.net_price": { - "Target": { - "Entity": "sale.net_price" - } - } - }, - "State": "Generated" - }, - "sale_is_ordered_on_order_date": { - "Binding": { - "ConceptualEntity": "Sales" - }, - "Phrasings": [ - { - "State": "Generated", - "Verb": { - "Object": { - "Role": "sale" - }, - "Verbs": [ - { - "order": { - "State": "Generated" - } - } - ] - }, - "Weight": 0.9 - } - ], - "Roles": { - "sale": { - "Target": { - "Entity": "sale" - } - }, - "sale.order_date": { - "Target": { - "Entity": "sale.order_date" - } - } - }, - "SemanticSlots": { - "When": { - "Role": "sale.order_date" - } - }, - "State": "Generated" - }, - "smart_calc_is_named_smart_calc": { - "Binding": { - "ConceptualEntity": "Smart Calcs" - }, - "Phrasings": [ - { - "Name": { - "Name": { - "Role": "smart_calc.smart_calc" - }, - "Subject": { - "Role": "smart_calc" - } - }, - "State": "Generated", - "Weight": 0.99 - }, - { - "Attribute": { - "Object": { - "Role": "smart_calc.smart_calc" - }, - "Subject": { - "Role": "smart_calc" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "smart_calc": { - "Target": { - "Entity": "smart_calc" - } - }, - "smart_calc.smart_calc": { - "Target": { - "Entity": "smart_calc.smart_calc" - } - } - }, - "State": "Generated" - }, - "store_has_close_date": { - "Binding": { - "ConceptualEntity": "Store" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "store.close_date" - }, - "Subject": { - "Role": "store" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "store": { - "Target": { - "Entity": "store" - } - }, - "store.close_date": { - "Target": { - "Entity": "store.close_date" - } - } - }, - "State": "Generated" - }, - "store_has_country": { - "Binding": { - "ConceptualEntity": "Store" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "store.country" - }, - "Subject": { - "Role": "store" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "store": { - "Target": { - "Entity": "store" - } - }, - "store.country": { - "Target": { - "Entity": "store.country" - } - } - }, - "State": "Generated" - }, - "store_has_open_date": { - "Binding": { - "ConceptualEntity": "Store" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "store.open_date" - }, - "Subject": { - "Role": "store" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "store": { - "Target": { - "Entity": "store" - } - }, - "store.open_date": { - "Target": { - "Entity": "store.open_date" - } - } - }, - "State": "Generated" - }, - "store_has_square_meter": { - "Binding": { - "ConceptualEntity": "Store" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "store.square_meter" - }, - "Subject": { - "Role": "store" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "store": { - "Target": { - "Entity": "store" - } - }, - "store.square_meter": { - "Target": { - "Entity": "store.square_meter" - } - } - }, - "State": "Generated" - }, - "store_has_state": { - "Binding": { - "ConceptualEntity": "Store" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "store.state" - }, - "Subject": { - "Role": "store" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "store": { - "Target": { - "Entity": "store" - } - }, - "store.state": { - "Target": { - "Entity": "store.state" - } - } - }, - "State": "Generated" - }, - "store_has_store_key": { - "Binding": { - "ConceptualEntity": "Store" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "store.store_key" - }, - "Subject": { - "Role": "store" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "store": { - "Target": { - "Entity": "store" - } - }, - "store.store_key": { - "Target": { - "Entity": "store.store_key" - } - } - }, - "State": "Generated" - }, - "store_has_stores": { - "Binding": { - "ConceptualEntity": "Store" - }, - "Phrasings": [ - { - "Attribute": { - "Object": { - "Role": "store.stores" - }, - "Subject": { - "Role": "store" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "store": { - "Target": { - "Entity": "store" - } - }, - "store.stores": { - "Target": { - "Entity": "store.stores" - } - } - }, - "State": "Generated" - }, - "store_in_country": { - "Binding": { - "ConceptualEntity": "Store" - }, - "Phrasings": [ - { - "Preposition": { - "Object": { - "Role": "store.country" - }, - "Prepositions": [ - { - "in": { - "State": "Generated" - } - } - ], - "Subject": { - "Role": "store" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "store": { - "Target": { - "Entity": "store" - } - }, - "store.country": { - "Target": { - "Entity": "store.country" - } - } - }, - "SemanticSlots": { - "Where": { - "Role": "store.country" - } - }, - "State": "Generated" - }, - "store_in_state": { - "Binding": { - "ConceptualEntity": "Store" - }, - "Phrasings": [ - { - "Preposition": { - "Object": { - "Role": "store.state" - }, - "Prepositions": [ - { - "in": { - "State": "Generated" - } - } - ], - "Subject": { - "Role": "store" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "store": { - "Target": { - "Entity": "store" - } - }, - "store.state": { - "Target": { - "Entity": "store.state" - } - } - }, - "SemanticSlots": { - "Where": { - "Role": "store.state" - } - }, - "State": "Generated" - }, - "store_is_closed_on_close_date": { - "Binding": { - "ConceptualEntity": "Store" - }, - "Phrasings": [ - { - "State": "Generated", - "Verb": { - "Object": { - "Role": "store" - }, - "Verbs": [ - { - "close": { - "State": "Generated" - } - } - ] - }, - "Weight": 0.9 - } - ], - "Roles": { - "store": { - "Target": { - "Entity": "store" - } - }, - "store.close_date": { - "Target": { - "Entity": "store.close_date" - } - } - }, - "SemanticSlots": { - "When": { - "Role": "store.close_date" - } - }, - "State": "Generated" - }, - "store_is_named_store": { - "Binding": { - "ConceptualEntity": "Store" - }, - "Phrasings": [ - { - "Name": { - "Name": { - "Role": "store.store" - }, - "Subject": { - "Role": "store" - } - }, - "State": "Generated", - "Weight": 0.99 - }, - { - "Attribute": { - "Object": { - "Role": "store.store" - }, - "Subject": { - "Role": "store" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "store": { - "Target": { - "Entity": "store" - } - }, - "store.store": { - "Target": { - "Entity": "store.store" - } - } - }, - "State": "Generated" - }, - "store_is_named_store_code": { - "Binding": { - "ConceptualEntity": "Store" - }, - "Phrasings": [ - { - "Name": { - "Name": { - "Role": "store.store_code" - }, - "Subject": { - "Role": "store" - } - }, - "State": "Generated", - "Weight": 0.99 - }, - { - "Attribute": { - "Object": { - "Role": "store.store_code" - }, - "Subject": { - "Role": "store" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "store": { - "Target": { - "Entity": "store" - } - }, - "store.store_code": { - "Target": { - "Entity": "store.store_code" - } - } - }, - "State": "Generated" - }, - "store_is_opened_on_open_date": { - "Binding": { - "ConceptualEntity": "Store" - }, - "Phrasings": [ - { - "State": "Generated", - "Verb": { - "Object": { - "Role": "store" - }, - "Verbs": [ - { - "open": { - "State": "Generated" - } - } - ] - }, - "Weight": 0.9 - } - ], - "Roles": { - "store": { - "Target": { - "Entity": "store" - } - }, - "store.open_date": { - "Target": { - "Entity": "store.open_date" - } - } - }, - "SemanticSlots": { - "When": { - "Role": "store.open_date" - } - }, - "State": "Generated" - }, - "store_is_status": { - "Binding": { - "ConceptualEntity": "Store" - }, - "Phrasings": [ - { - "DynamicAdjective": { - "Adjective": { - "Role": "store.status" - }, - "Subject": { - "Role": "store" - } - }, - "State": "Generated", - "Weight": 0.99 - }, - { - "Attribute": { - "Object": { - "Role": "store.status" - }, - "Subject": { - "Role": "store" - } - }, - "State": "Generated", - "Weight": 0.99 - } - ], - "Roles": { - "store": { - "Target": { - "Entity": "store" - } - }, - "store.status": { - "Target": { - "Entity": "store.status" - } - } - }, - "State": "Generated" - } - }, - "Version": "3.1.0" - }, - "contentType": "json" - } - } - ], - "dataAccessOptions": { - "legacyRedirects": true, - "returnErrorValuesAsNull": true - }, - "defaultPowerBIDataSourceVersion": "powerBI_V3", - "discourageImplicitMeasures": true, - "expressions": [ - { - "name": "RAW-Sales", - "annotations": [ - { - "name": "PBI_NavigationStepName", - "value": "Navigation" - }, - { - "name": "PBI_ResultType", - "value": "Table" - } - ], - "expression": [ - "let", - " Source = Table.FromRows(Json.Document(Binary.Decompress(Binary.FromText(\"jL1Lr3XLbh32Vwy319mo96Pp2EYaCRzFhlpCGop0kAiIJeD6Koj+fTjI4qPmWnt/n2VgUVfnzjN3zSoWOTg4+Hd/9+//t7/8459/+Xf/5V//+//5519e/+s//fOfasv/4T/9/V//fP2nP/+ff/p///zLv8n/9h//9X/89V/++59/+V/+/LfXf/vrv/zlTxh/85d/+cd//Ye/wvzf//Xv//mv//TXf3v97T//01//3d/85Z/+4c/Xf/lTLf4P/+O//I+/0nP+8pc///kf/o3+t3/88/Wf/79/+L///p//rz//3X+lf8e//z9ef/fvyxortVd+lZTnHzn/kWY088qjtPTqI71yzeu1XqV+zUX/q/7m8pXxj/ztf/tPr8zPrDXn1F8VD9p/pPxHabc585791Rb+yyPTv7yOr71TetXytUum/2R90aPjM8usI2V9zzT+KMvNTf+Fnsuar83/75VHTq/xqpOeuyY9t42vVfjf1752ut82d/wrEx63/kjrj7zMLPirR1n+5NLyxlt0eWH9zfWr9OuFa9qdFlYfS29Z3OyvTKtZarIX7m29+quM8ZUSnksrMhseXMpXLWT8z//T37zS1+xlyHpsWp9hjy/nu7GZyyu3WcvcYT3o1V9zynLob+20zPNa5k1f3JY5lz/KNLPizyxrz2KPzfyVsaabFjzvTi9d8mu2r96u59LLLHqF87p4bnGzv8YYPawFPtSr5C1rkdf6qliLWb46dsV/+Ft67FdLa59n10xbo50H0r7t0Wx1z0mPo4+e+yj45+o6q5wzbQv8K9L+avh+//lv/yut8pq7y6N7r7rraPvuP0p1c7xmqiXbaow8X/PV+nltM2h/faVlD6eVqueY1Eo7pJwn0nfrbu7XammvbItSadkHvpgelMpLPr56eO+17LTQu8ly6xGJp2XmPtd+dX67XfAH1q+KLaG/GXstX59w5kSvi2UuCe/IW8PM3HNdu73wIfGu9TX7WWYzaltffd3nGv4n6Zu++59KD6WndrzSHLRYuST6kzvecH21vfur96/Wyv1QWrweHiqfTcxFi0auYtl3a2TS/wx6N94UatD5/GqPt91p0n4odibycJOeRLshmavAu2HnzK+KhTaDTkmF7zvfrM3jhngn1nOK9x95urmwhr0280K8wV67yW5Yg1Yg4S/5atV3Qx/r7OLcdTds9nAjmrRY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\", BinaryEncoding.Base64), Compression.Deflate)), let _t = ((type nullable text) meta [Serialized.Text = true]) in type table [Column1 = _t]),", - " #\"Split Column by Delimiter\" = Table.SplitColumn(Source, \"Column1\", Splitter.SplitTextByDelimiter(\",\", QuoteStyle.Csv), {\"Column1.1\", \"Column1.2\", \"Column1.3\", \"Column1.4\", \"Column1.5\", \"Column1.6\", \"Column1.7\", \"Column1.8\", \"Column1.9\", \"Column1.10\", \"Column1.11\", \"Column1.12\", \"Column1.13\"}),", - " #\"Promoted Headers1\" = Table.PromoteHeaders(#\"Split Column by Delimiter\", [PromoteAllScalars=true]),", - " #\"Replaced Value\" = Table.ReplaceValue(#\"Promoted Headers1\",\"NULL\",null,Replacer.ReplaceValue,{\"Order Number\", \"Line Number\", \"Order Date\", \"Delivery Date\", \"CustomerKey\", \"StoreKey\", \"ProductKey\", \"Quantity\", \"Unit Price\", \"Net Price\", \"Unit Cost\", \"Currency Code\", \"Exchange Rate\"})", - "in", - " #\"Replaced Value\"" - ], - "kind": "m", - "lineageTag": "a7c64317-d746-4e5a-813d-43bbf0439e23", - "queryGroup": "Raw Data" - }, - { - "name": "RAW-Product", - "annotations": [ - { - "name": "PBI_NavigationStepName", - "value": "Navigation" - }, - { - "name": "PBI_ResultType", - "value": "Table" - } - ], - "expression": [ - "let", - " Source = Table.FromRows(Json.Document(Binary.Decompress(Binary.FromText(\"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\", BinaryEncoding.Base64), Compression.Deflate)), let _t = ((type nullable text) meta [Serialized.Text = true]) in type table [Column1 = _t]),", - " #\"Split Column by Delimiter\" = Table.SplitColumn(Source, \"Column1\", Splitter.SplitTextByDelimiter(\",\", QuoteStyle.Csv), {\"Column1.1\", \"Column1.2\", \"Column1.3\", \"Column1.4\", \"Column1.5\", \"Column1.6\", \"Column1.7\", \"Column1.8\", \"Column1.9\", \"Column1.10\", \"Column1.11\", \"Column1.12\", \"Column1.13\", \"Column1.14\"}),", - " #\"Promoted Headers\" = Table.PromoteHeaders(#\"Split Column by Delimiter\", [PromoteAllScalars=true]),", - " #\"Replaced Value\" = Table.ReplaceValue(#\"Promoted Headers\",\"NULL\",null,Replacer.ReplaceValue,{\"ProductKey\", \"Product Code\", \"Product Name\", \"Manufacturer\", \"Brand\", \"Color\", \"Weight Unit Measure\", \"Weight\", \"Unit Cost\", \"Unit Price\", \"Subcategory Code\", \"Subcategory\", \"Category Code\", \"Category\"})", - "in", - " #\"Replaced Value\"" - ], - "kind": "m", - "lineageTag": "20f062e9-ff67-42bf-9c62-97df6dc33a35", - "queryGroup": "Raw Data" - }, - { - "name": "RAW-Customer", - "annotations": [ - { - "name": "PBI_NavigationStepName", - "value": "Navigation" - }, - { - "name": "PBI_ResultType", - "value": "Table" - } - ], - "expression": [ - "let", - " Source1 = Table.FromRows(Json.Document(Binary.Decompress(Binary.FromText(\"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\", BinaryEncoding.Base64), Compression.Deflate)), let _t = ((type nullable text) meta [Serialized.Text = true]) in type table [Column1 = _t]),", - " Source2 = Table.FromRows(Json.Document(Binary.Decompress(Binary.FromText(\"tL1rd9xIkiX4V3Dm0+45kXvwcjiw3/iSKImU1CQrtdWz+wGMgBgYIgJsRISYkb9+7ZrDDUDQIXmXamaqu6uyMkmTw92e9177n//zf0RxkeV5vLgtm2rxqdpuq/06eFd1XV11C5WGwdeu3j41VXDZ1T+qxcW6XpZP7eLDzeJD09Tbtt4tspD+/+If94t/bOt9tQru9+W+2i0+tx39qLNN1dE/soiKQv0R6j/iNAjD/5v/9X+F5v8tYrVI6P/+j/9vwfZolSbGnrt6+Vwdg4/tertrt4sozYvgvHyuuuDsR7U9VIuvTbltFw//z+Kh+qvcLbQKdfprS/LkjzCjf721JNGwJBJLiqQ/mfdV2z1VwVnzo+zKVbsosuCBDukY3JTbavGlIzvor767Wbxr2q5elYskzsPIw5Tijyj+I9IOUyKYEltT8jSLFu+qDYz5WC7/61DR6VfBp3a5pg8Vk6XB+67c/qgb+lZs1OfqNTjvDtvdK53i4vNH/gsfq25XHRdhXvhYp+M/wvSPyHFQaQ7rErEuj1Nr3dlh1dFH+7J7bDsyI9VaBw/lvu2Cr+V+uQ7u2nI1e498joyMyv+IC8eRsVGpNapI6S/0Rt3U21UpNqlc46dv9+02uG6bpn01Vn0rm5eW/u7bM/rqu125XB921X6/W4RxmHuYprI/ovCPyGFalsI0JaZlYSJf87Cqg+uye6HvGOUp3fCufd3aG/6lfA7u6tVTtXj4TLec3uduV1V0UfMk9HhzCRukHG+Ob3omBulcrtencr8+4mN1uDiRpid7U75uSrHpuurotm8Xf54t/qy7p3pbl4s4DgsPg+i+08dLorn7rsWgPC+sQe/ori/rMrir6BuleaKDs+6ppaOg39JV1X5xW69WTbWnY1tcXi0uq6Z8LbsKFzgsfm1Tqv8I1R9R/tYmncCmXGwqyLv0jrKmH8GuiAyPFZm2Cs4PK/Py7vAo8Q6/lt2bl6ezLPY4J3KWkdtFZbCpMDbBWenM2HSxLrum2gXvy6emLrtVTf6yiIOLsvlRb+1J3ZdNvXs8dMfF7SX9Y90RjmsRR3n4S78Zh7CI/JXj40UFGRWFYlSiQ2PUN/JHdbkJHtrVakHeOXjfNqu691B06bsajv3L9eLLum4XqQp9bnWWsAdwHI7CrY4isUOfRrYL+pX04BdplmXkzSlqbFdDOLlot8u2I+90tqBjq7+Ts6CrXaQq9rBKJ/hks84yisUqeuTy+Ft6Vt/WVdXQTUpjeqlfq7JbHq1F/+gey225+PB58WG7qvFv06zweWiwJv0jCR3W8BklgzVFbK25b+umDL7V2+/kwReRCsOA/GC7rZfBNX1I4yRv6v2e7jZd8ufF2d3irHsutzsEX3IBHj4yo6cWuT8e0oAotYZFSdrf7Lv2ser2ZEq3W5f08WKls+C8OdD7f+kOy/42XR7IxTfPJxc7jj1em8r50zncZAYPEClrU6yizB7WBQXeOvi6xg1/2SHO4UY1uND269Hv+d6SSRNHmajI546nf4QU6ByJgYIDiDJrUpqEYpI4nuvD05p+aJzoKPiTjoziscnf6NfQk2NP+fVs8ZVexe5IOQ0MI0dIQenXhmWcE8QOwxDjIi2GpUrC723dG/au7LbrckNXXWUBeawt+e1uEoO/0vWCh3pav7VQ0ff0cFMhji52WEjPmCzMxUJNmafNWjbH4FNT0iOMdBh8onsm0cXkfnxm9xf0Rg7Gj7SUttCvK9LUJ+7RqSVO55DwqYk/pxQ3Mrf+bLtft9tj8K6pNpSCL+KsyIOPZfej3pksj4/rPXn6ahtc1Pvj4vM/OdL8s6WoE0XKy5UqpMIuu/g1xuLSU0oGjV30uJ6DmyP9u7RQCSfn9r5TYvoMf377gb94/VRu6W/ycgtKIVNxJQYZGyI+nV6g+Cv6Chv6w18hUcHvpQusg6tyt8fjf2PUPRm12+FfLy/1IvGzC9lm8UfsStDh1WPx6nTcYtdHurX1d7rY79t6u0PKkgRfyRHYG/VwWMKis/8k71n/3dI1ypX2SsjN43N5dVyjWLw6ecjeeT6sq3bVduQTSi6iUkV+7oYylz4RLzd0f96UB9Gv8wE6Gw1X7nIFCdxmLK6c8vq+orsk5/jP9kDXmdxDGFy0zWHzSKndLYoZk4MjGOK+f/tz8a3a7YPBc2ahj4MqQpxR7Eox8a9YDValfTr3sd7Q8ydj2s0j1XZRMnUAfWZF1YLbAcQeZ6WQOFGUcdxvJL6x+PMsCnsH8LHdrQ9lcE91So1aKYrSmH48xV1Ew7HPvDlut69tS4d3RjXMbk3HB2OLPEx8CvMIvskVamKkdbEeTKPcYRyRv7av5NUXUUwndl53K8Rj+t19dkdfFvdPinOd0JP59RUP5x6c4s8n3jtLMokvnGZWyBD29XYRZVlCX/OFKqo1lQzmjCjZ3H/n5O4Bed62Wu7r5WG/wIH73KqCb5WjtIv5+4kDzzRFGVtJIdZS8v2NTomiMb1KZ9lJb/Cx4xrPUXhGqYdfKEwbwVXnwbpE3LimuCDW1Zvg8vBChZJGPbyhkuZ7XTXiOG8Oj49I8oYvSBmChzEpf8EweWtMji+YiCvX6eDK79fl67Ykh7mp/qYymGrOu5ZKzta+wfND1/TX+u0hUfnik7nAqD9iR52n+JDEles8i6blS/9/6RuqUKGs69OpW0qnWqoDj6clQ5KmPnknulB/xA53zv2CRNx5TjWmOIXqZR1whNk+7RZ5lAb3r3RCeP+9VZfV9geVExdfFnBp3K/KKTb8shQ2FR6q4ZkKLxGPXkRZaNOB/X5NoeN9S0kaugU5Pf/Vkarhp96cb2v6lfCe1UmGklC59usz4kLGVS9kKGQScedFkivbrENn7hIl35Hycqqizw5Ph92+NAX5edk9tofOZFGOMKOUT/AjP4U6xpUH8y0Xh17QlbEVOufeL1Q48I1CjZXoOLg97OESHtD2MTkw/S6uX078ObkW5ZdrZs5+BueaifXnURxmQzVjXFUZfOna3bJdJLFGzlJv9/ICq+2uMg3Gk7Zd5NX+LdgrzDnQJBer8rxPXT6i1xV8QjKgCkUJDJWgk2rmqnlGfuXIFGIfr5mm7BAcJmnUWIn16Zzd9SaVm2rH12hHGWQeB9/o2XXkyL+RD7ipf+BN4lONo57yMaWYq4o1TicNxZTY3qa79ogs5IW+AiW+RRZcV9uK7r1NWpAbN9POAfk4j4uNlM6ZqaTwAWkkpiSxuO8PbVNSpUL5QIdSmCIRLjidy3syZluJu9zun8jDd8fF2c3irCkfy025SLJQeTy4lJurrvmBxrVO48GuQq71Q7upn8vgnzWedUGvC7d2Z17ZfYnb/bU8NIvPV+SXHrty91wusjzXPtEk5KzJYU6G958mYk6qtTXn6kfVHLfB57r6QT8SbehzevtVtz3sTdJ0S6dGjmq1nz6xmHJfrxyTCl9XPs45ZpqKSSpV4/v8rt7/zX214BM9ObSA6DbfHjv0ec4rCrTOvFdpD/edmkamwyZ+Y6kSm9AS70McVZTXh4Yie1rQu3/XlU+/nkD52IKGirMnplHLpZnYoqPUVk+UhewC1JALSswTPH3ylGubI+FGV2iWncR/VXh1BQoeic11w1M9GJRL2nZx3O7X6IZzDo7XVuQBxgR8gc678kg/f3H7hatdinM1JROZT4lSJHM9Ch4WpOKhkzyWzPu+7Mq1TOioFEqDq2bDyYid0a3rplxVzQsZ7Wg6eWXfioOtK6XU/NgGT53nfep2tXotqSI5O7JHCukGUgCrVoeN7YTZPGSSk1AG5NOsSJGThI4Im+FSq8FbF3o056E/MeUgl1VJJZPShf2B296iBwpt5brk+c5kkpj4ZN1JBrftym5zuEcVjWwa+nJlRzfpz3K7rBYx/SKqmrqXCpllb9N507YbUxUgPaFU5GXa3cnR9vi1cQojFmdJgO6OEt+dRkNMuWoqKgA6imtd12dAdGyaspKXhjLwPs7h5p93bft84hFU7FPVxXxortIXHkGJE0/TXJz4/WFXYvj0rdzbkoTKgiK4X9NPt2adHZBOjfKAXPukJAi+obvBg+CrxIWnVDxJ77LvOiMXodwnRUbZPA7j6fN2Se6ihKHje5WkiU/jIuG8bS7/VuLAFRKd2z7obo7BVzR894ARpCn9ge73mCDYBljbVFSVyNyHnL9Pv4mbla5GRcK3KBtMydR4wrJF9+uVc0VKgkLO315Lm0V+qspue5x24+LEI57onB2mI3+k4g89OLEni2W6el3+XTWY1SMDoIqLQkpT0lURx/S12iPEOnwlXWqfmS9HOVf+Zu60uPEMh95PxzAilCI3whzKzlDGUIu96TNPLpHKPJL/jGf2rsir2Cbx35R8WewH/W5AP9CPjClxDS4aZN2YDPD3MzncPw+UQE6+HD697wTR1QrApc7EgetiqHO7ZzOapzukeeB7W75IWnLT7oKz7VPV0G87yQXCUPvElHhuuJrhiDLx33k0NJg4G6OE8urwRMGNvkaW0dWqv38fJr5f1y059L+mhxT6vPyMW/LOQ8Jzy+LBokQs+lwvn+sAJT/KtihXUfCPl/E9onN8KrtqT5Hw/7Ah5f8cPEGa+6QqiWbTXPEEnedM/HaeDi/vpjW/9rb9/p0OJinITy5f4bSpHufb9OFA+f/pnCAnb+BxWBqfz3nD2SJx3EU0DJ1u6+22roaHpyNEt74RfrZdddXrzp16+4wQ6ZBmAFfmkMRzU64q388AZS7bV+7BxwXaE/3cVRoBZbOvN3SYp6NWn0ygr+Ic15yruCwbjLI1N5KNknzmTftS/U3ZTBbcH7YrNEs4ujkirfLsBqbOlIS7gZkeDCnkdM5eurrpvQDd7eDhtUYr97CzmKuWSu5p6p371LZZjvzIeXsQSTLrs+OQzBk3k6iwD0yNgvAQcyK+PUot+Voenimb/PZh8a3eLdvtjg5KUdnu01JOOGWbqwayQkxKoz7u0wVtuLdNPoCuzwLZL2WUdGlXTzJMocMrV4fd4vaBiyc0JlRBjtarR0IWOds1+GI6FIt0LMnaQ1cCTQGUBwqUPFOm023NQePose3aw9Pa1VhWPrCvLGLDHHdaIWnT1nXHUVgUtrX1/Xvwvt3+Tf/hb+BvUrpGHbJdi7FCn2tP6e2nxZdnKgvoE9ML8fXbM6MK9tva+u2Y7oHkkJftdlsGBh6wSDKKQ18xknugQ+pvEwop0/2bPnvt1S3JOdF2zZlxwXUy2JQlo+qErvLhpaUrFwWfK/p3Xd8nOUN/dF8u3p+Z8TzFWYw5PCzpk0iHV+QkUqdiibLgz7uW4nrd8kN76efM+Snc8t8B+op5nurovvOsWSsxLY8FQcRJRxm8oy9HVxlY0DgpqNatntZolNprXi7HONlMezWSNY8nXI1kBBCdDfbk2s53D09NuQve1/1Bkbc+LztTQfI59d3IF/IKu2l3kl6B38hkBqrDn8+67Rj9rtM0yUIBKIHTMGuPJ2Zf3HndAWcBdMq0wvVqCRSmleNyl+ycxINT6Zabszrv4L0xl0TfRFNesmqXgBDRjfnamXnzh8vFhxW9fgrZeBi/9pERrpCzxcWuSLx2kumho0SZdUnpBca1SZEH54flc5/W3lOsN2gBzvz/8bD4x75cUxCOvA4lnpuRcCmSi8dOdKJtsKff9c/HsuvoSuSa6sZlBfwZOpL953OMIOLMpzJS/I1cxTX3R3Px06ka0LAX5Z4eUrXFz6Gso6EflNP1O6+6bdmtZD55Xx03FPEnN5rCkE9Vm/MhuV49XGMu7ppK1xM04/sDuo94ZRi6HQHRvSAj9vgdy67d7ab+0a8O0VzUuvraePS5eOo0T4oh7vdg2DTKYr7S45SfZwB8i04ykbTw7ImgoHWU/dwTydPBIiWx46JG+Xj1AmReRknsw6EDlYAKapNdz7X/vPrI0VxhxI8sFz+dUqE5GdveLinOls2KHGiug/840LnsjFu82lBwo680hQrmqU8bwjSxXD0jfmXiplWSypvHEBSv/q7lRB/8AUqVftQ7qRtNM4vi77KuTvpGPgWR4paf61qzo87FUatMi6N+jwnk7ZI844pcUZYnPJ7B4+cWztNT1e2P5gPeVvuy7si0mzOq7A71jt+cpl/gk7FpTpEcl5yrtVy8tSpG/cgVjGOICeVsKZ3YOd1ySWzNOIlNmZYASRR6BLY8nQVToaecF4NJef/ubkq0+N61KLQp8gVnuzXS77mqyAvuxo0R15SWqTGFeOwsKyTH/ohTeOAEBHHqBHJuMV4naSPCs8/0aKbO17hEhXhsukPS1Ga4VrvfV1yZIYomSqngolqVnW2I7txAkjiKfBLshOdsrmqND0k8dpZn/af6tuHATumEzsOA/ffq2Dvr82rFwCRH+aF9MJyaYXgud5TiMhfisXWcxVNcCyf1iyRNLUTCnM/9uqt+VMi2T15XFHldopQdkuOr8VUuxGFrNfpqGKxVwfmRTqIoshPKyZmgf6akEy8MZ8rQAxfJigvHQvy1zofi46qp/y4fEWXvyk3dofNQRHSJKOJu6Xc3gDJvjCv6sinX5ckMOfJphhQc2VxIDUbeFeK5hzbkNVJZiqxLniDz7I+nH4MbOjyZa74agxDo2nl8N578O5lVbI64bPrTFZMi7WvZYOhI3p6vdFc/rWUSwneN39vny/5XXpbPLRVvKvflyFHK77xOSJAKcdYU/sWs9/TYVkhqMZxNKdwGD/Xmsep4UtqjEejGv8FthYkfxHQm4jLEtCgGi7QatY8vS+DyKYShqxjDX+Ph9+b8xwFATyrTpjc88in5UanNHFG6oPgjbptOfBgb0fuiI7o9MCaC3iaVDcDn9mm2nSfjW55iuvMo8uj6I5V0F/1IJaNQvHdeqL4m+shhK7g+7OlX8UiZCiYXOJGM74AwGaMTvZyTcQbOCimCUfFglI5Hecn2SG674iFkGmYnoITP5UvVuaBRygcGkJiv5zinnL+eOPAiHKaQoJ8cGzqp79/JIsrXrw7LppbzAWq4DPos780t9zqpLJlr1SLMRaG48SIaTuqufTwC4r1bc64Y0cf70tAR/WTy55HiomlTOAfuaNpEoXjwIo7khC6B2uYeVs1IZCoSo55ceV4JhPOngxuftiQ6EvEs6TMKxYkXo2h3Vz0h1b0HFqCg2v8eJF1DsrKcxt/Jb5MMaZwrQzE3Sg822QrlkpL/FWPsKMMvNPmqUWr70j7DGZzAEf0YFKmxZQa3FYX5YIuWqvs/6anT9e3oFi8iNEdQo/XWXANcclcD4vrlbvGlq54YGxkmnrVk6uxIoJaMhFcZF9nAt+5b7VsgJXb/daBUoEiUoTGYBOCcHns17tVoH/dYcBHi7LDDPQqdErZY90jV436NrlFlO9rk0hX58N0TJsqCPvjX26Om5+eCJLFR4rOL3N6ch2PXojw7dOhcpVFGWUBTgahE36wHbUnadIJK9mojgXLmhiCAchYJsxKUk2R4YY/VcklPDLOzkpLKOE4yoTNaPGKznw4iQh9IMtkzw+Uw9iRiTzJQ4O7rJ6pqqeavu5aqR8qyeHhE6ciutwYNZbrY5Uu92p1QqJQnSAqdbddoDYAyYVYCnDhQTOjOPJPb2QC8TU6Mslpu3djkre2Wpq3sqE4yr7qWScNO0AZenHArAUeORrnSFXwyWQVMOd1wBgWST6IcwH68crtc97ZNym0/CJfpIzsyJrQnIuFXJhTti7c1we3y0xaJEYXeLA1u6kduTNjWyb+kSpFxYuLsSUQcSK1FcZb07uAbeetjcA7nk6QqNU5xV69sWmLOq5/aTHxB5pPmZsVc481YZD13kmSD/INpvLE4BtgAmpE3O/LfUlz+Xp8kYZCryxnkbFQxGGVHpdctQwHZjW/4PeUF97NHHuqXKEUP0zKun1zlHOaAkZApk0Tn4VtKAKYU5M81XSjzk8+rbR/oeppX9Vcfej/9Ez3g/WH5TA43VD4pFDCLyo1XzmCcdexJmmttGaj0BNvgDpUUxt1ZcH9clpj8Y9zUI3BuayqPp/1BSBL4dC3BUnA8QHQtIyFWJoocvDzAblshElcYKz/vFpGKMgo29XeuowZEIPeYTln8mY8YRJLPQXLzGGaJbx/IcCZzukcZxRQYpVPDVbD5isTqz38uPlc/Sjok6K/44JT0HEEIfcpIGJZJlub9Zb+suuo5uOjKDdXXcfBnPaRx1+337+ipXO3MbzthcRQ+5ICQXYILNRGyEoYYpK2Tos/DjLfLIzOGi+DLX/y1TPNrnp3rNWAysxOXphB/LfHiWqXRhLoI3P2OkqcioVxq07TLZ9GkQKepmrROlc90Cdxc5US6g5sbCY0y0ToaMZjpbS2rgHH/iySnIpCcOugAxi29o1tF5jqbJ6nP8KS/QS6aEt8g8eG6SMSH9zSF86Z6BQYhoVSVMieyVPhJjMA9b1B8Tn24l65Qbjiero4l36JiMMr6ouvyyKDyzy2lwFsGxwdfurIZUZm/dnXbmTHT7WfGVlU7PimVaB9kUjFHhefGgHAokxzfb8SeOG+PyC7zPLBXfTRCYU90Gk88iVxwkYlz1s0uUoiUSV4U8u1uqi0l10GvHoCHVEzqyrcFuI8jMuhEZyuHTRFvXUT5oBNgYWOlyS8hVYGZyfLZ/LW63f47ApvhBrimcbhMQqJMqL7M7fyUBxTX7eOKfk2aYmTxWJGpffPkzgzf/wV9I4Bv1ezEOxICJRmTyycz6hgVXaRXcPGBx3P0UW+qiuqCt24AMileDZTErTOGdFdYlGkYDaJZlMRVu9L0BFJNUe2BSsxuC8DykpwEZOGYdX5a0vl0vrlWcU67+KNlYk+aDg2d6rE1I8vGcF/TiJ77vhKtrB4oZbAKE+RU4tWPz2bpr5zACXMSsJYRiLNZdRXiWd9H1VRGfWp/lH3W9vMSygfImTK1Y7aDItTJFK7AjgZfu2pLgeUcQ1P4cGApLtb1y+awtVShcvvUklVX9I9QlrCgo/Zx3v0Vn8GXR8KaTCM9tOPfdeCY7IL75Zpy8f3flG8k9AsOe8i/mAE4el1vIe/Kq1M52+7iglzYk2kcZql4J/pamMStoB4ChhClt2sKGva9zSEEfCQVdMpFr8tzI9kWDmWKXtwU5QqexH7Lmn6U10xyJTNJBXbyNFnymu2a2+2ayPPtFgJlijTEGPW12rOKSQO/pNM0eFezYI8FB20RZ9ptPa52cy94Kepv7cZx8SezXjtN86I/oZsDoxMqXKBFnIQFWk4D4fUdPcKKlWDAEThx3n43myELLg0aKFRGwp9Ms5GTNDp5dUkZ5W4NzE8evEPv7YluO0BLXyt0eD7sOBEYvTWvihIMk9SdKCG/FfZkmmWDitcDlUPl9hjctiBsL6KICkrMxFeAeFrSy3kFX44od8oxD33Y3LhLbt1Kc5fEf+tIkiV0cIAGZlJ+BGHRTdda5PRlu3qqekb+BCPkCe0ySpWu6wSnJEzKVCextAj/rDo0A0sEXuivYWAfnNdPYDAij6L7PM1us9gHHqCTOYFKSK9FwqOkkFpIjxl4spr8NWOlFrlKIcNW9eAgi2MYDXS9qNw9fMI16+ILLa46j09VBC8oeQQ2oAhDFs2r6UL3bgjP/rKuntrTCjv26uYqnuU45yUwSsiTKZVd/fCUHv3LutoOANw4SZgPxCHOWPXbc5xEz3FMQZeIhECZkncfsu3y0CFD6jYMMwkhakbBHgWczbfLzUs57Y9kkY8qJSsCzIEWIqFMkjkDn/N+jR4SOaDXZR/03xQlP5Hv88lFzADFlYvwAEUYk4ouqO1SGnGea7rYJaiSUSCCNzaVbJ57/ZsT7T6fZjzP4p2kd2SSwphUoRqINxDmCW7Rl9myoiCFmTC4K+sG5ASZwJXdfx3oGKuTZjzdSz8sRejWDeNzUoNZuscIvQOkdLtqg4/tFtxApfo+oJ3CYUbRrfp27jS+FT4gfGXE3mZfXiY2obMv+PItndSKeUdhqhCAf2DYs9334JeXMrihy3Ryx70kHRTnbS7YAicBQqBEC3AEeAdn6rLcMmgqZRze6VD3Q/cDUfbUP2Ve7ptv+BzcNBL+pKIolwkeN7gtV6v2LwA78+DTYS215MwoziP4pywo6hS8QEwT0qSKsgHUYVr+9MA/VXVjiJM31esA66TQtj50u/1p8y/2KSLjOew932lhTSpUsH04aaEF/Q6Oeg1VBLpKzUb6I137o17Bhy/uPizu1u2qsolSGBc+HRIze3fVInyBhDSp4lj1iT8DSt93JfRNqU6/6HuiBnZ/3XaUkFyUYAWPOxGxTn16x6YD6HhfPHUXwqRi/obJaSuUaM3qFalOGOPBU/3aX53L8rifqOOmPqlZbuTcZlBckXAjVZIP09L7uvnBmhI7KmDjGBRp6JIABjS0+uekN3xaWcb1uERl4HqEHkl/RDWVXL+uyv2a/hYN7bSu3I8QU9/K+qVcHxbXHxbX5WtZ14si04UnZMOta26ujThn+vAj4jYAivu9iF3gpVvq5ljAsKrW5evzW1FFDfSOVxt5Bs3NtomTTpNBPY1eFSWpJdr+r8Yt5llELmndDIGfJarL7Vtukp/yZMo6Lq5KBEmt0CVVmlkqB/2CanOkULF6Ql8Ewrfko3GtZFJDJQhPm5kz9el+8cnkIki0fcRLf44LENakSrUVTeLX/wkOssO4Ow7OuzE58Xa5pFySKrQT3l3hA1Y2ksuu8Z85omIwpxgzuAF7NSE/iumAjRT8gJ7ujqvTxmjs5Qh0NqslgeMRvqRShZ2UPpRHiuC41bxvAeZQKVbBQfb2fNq2f5kE5ESPvvCp+jNzuV3jIzZJ/LUObeP/agPGLaVm0AGgS31GP/EgNEmKLAanPPFJkVd1pKLZ+IrMUXiSil6IbdL0YneXh235A9gkaBeAcb8Z4e7miiMfQZLMSDjNgQCEJ6nyyLb6mEp6U2JTBoa08cmzvy+PXV9Yn4q85j5jEYPfmE1khS+p8pHoxidLmjqHIhh5SjILQ4h9+zJgyiDqwphOV2PUTxZIs1SZK39EySaESZULv0yGxTBnAXm+4KEqQeeyailX5QG1Zd29YSt5Ocoi5caWq4mE1p+QJvHoJtC7T2XzSDlJHgZX9MNKwFwqRBYT61CvsWefXnQ/PD5nBC7Iq/mE1ndTGmgxXR92dblGS/sR6hthih7AyHEDz0Um0r2e0vC88NwFM11cSARzQtZtgxCajpkud1UFpEuoQt5QUw1DrZ55fjr30z6M0oJXUriACDE/uULMocvSV2odxVEMaqly1FSmPRybYYnAO0NSPqyqbsV/87gsSnwwCFk8lyxBLSUS2mTGvNvpzNaSFeKMHBNj3zCx6U37Ro8NpNKzxYf2tUTCmflUaWo2W0LuJrTJDNqS/SujKEo1x3l55LUGsQqM/M/Xcrc/dJW91EINcmlceKUAkJdzQ264wyXkySxJQ+svMX88pwdO8VZjw8Gp+uaX5rh5IaNOxZT9RhFMVnBdbhDdI6FPZqj8B9A0YHgV4iyKbHTcqCSo7MYFppbzBGkyikgzn7UUSTbHK+OiRNiTGN0PE8lepAWsTtA68yQCW8E+t0sole3GCLfYS78pmcMigFwWCW+STLG8qfOuXIHGfsfxn1XTvizLppQM0r156ZcVCW8JQN/Ihb5F9BfWZJaG8SnoXoQbkPQBprUb4u3Zy0vDNIFTqqsX0tUwlF0ZLXyRkCazdASKGuDJ16aDRIbR1UCcbzfVICjxrwJbimxWJI1vkPhr7C/pkzbWIwKYFF6axQODh/rxcScqQNxTb8op+jb3GtImHPRdL4xvkfhrzGL6tNYwW296gYSwUMFNu9tTuVRVfW1LhQllLOtq83YGSUHNB/xnsv85toQwJzHRjEaz46ZeUjANTLW7oKqcpVNXo8kI9MDq/QkBJ/bpIWP900ykZZvEc6t80Ch935Sr485qqdPtTy2YZJzjGmzgWFKi8IKXJ7OpCFsk/joLixP5n+uq3tJPWS+AngiQ9Zr+hB3X/uu7VjRrlLpa7TytFQJlRt9OGm039Q46qU88qoXsJT2w1cYipCiyWZ/95wMGTRu65ItQpT5gjWxWAp9b2sKfzLI8OmVzMVgjShnJXXffy79G0rtLZLVvuxI+SuWauUCuTJtrW6FQZhoXcySdQD8AFBxRTgEECPnubvTpPlWbFxa7mGgV+3Tc0lnlHTDxIyFQZprq79410T9KWZJZKFgUAStHSxq527Y/QFWeNPwTX2ESd/OPe0pCniRThvGRZeG/bzGdhe598mY4AiZjcH6gKvJ0PhJmPuO2YrZFipAi7MlMp3qQuqs5wpavGLUpquI2j9goBt3y8mhStb5ymzqlxGcyksyxJ4wLEP+d0987Xs/D1OA4p2TvuhoLSqGb4yxwdeSTbSte/+aasaOIjIU7meUQqDmJuVXw/gCNS6TcQN9giESO8nKYs/+Eau7T4grhmlz+El8vFgplVsTDhKSfAqI+Qe6mY9jy3WwI6JWvtwEjcpCfvMFJRz6oO83y185ODni5QqPMisJeq14D4w6yqd+/L2Jouvcq4cyC4WhntGbMEPqU+pbFPi4hmSNTYmoSC5lSh8WgqWylZm8xFcUOyQDa87XcsX5Dm0GWDP14MAy8el7aOXVDzysWGqWO0ni6XkVUy5I8satVfEWVfTzWT+VwYiFV6kjlkmz+8wcvXvy0PawALknzKDbNS8l/hak/yg1Cn7teMNx1DjsZC5FSx3oAu3w8rI50NlW1hGBIAi4OprpS8dZQyDH95UmNoHOfu8QwLid+IoNF1qdrrMYYaidAAs66bQudF0ouxhJGGLC4eMIQivECTbo5Swl/sFzMidOpWnFwBbQtEAHpW43nry0WDC1PVa98mHBGesKZObHvLAaLrJrjRVfWT+AvdseFykz45RFTD3j/effEBzOVzqKm8dGEVql5T8AATbrfU4JJ7+4J5UpwX3PCEtz/1wGrRP80ZPQpplT56ClpDi/OcTKOSAiVOhlto7uj6onSfzTWczJG5JyFyPEWD+QzEUhZ28kpxs+2WKetWYZ77IrII79wd4KM5PZDLVuG6Tqf6EylPojy1AQQF48LAURIlGSLFZeDJDAA5aKXmibIc+s9FUiCAqx3WIg8RbakPoGj77q5IG1wP0Kf1PRbU4lo2NAFDj4fjsqy4K4eT7ju11SncKY0XnhBf58/1GYGiBALc5ISt0zmSbJ6ToJHqmIoFYmOO9wQuYPdK37dGJHoNchNcyaZukQ4YxglTlrFcTgt4O6fIXcFyXhMcTZt1ys5vSuPmAr2KdxF/yuHTmCeeIkVJnMiL0grY6FPkmFJPMYBcRZnhHBSMGDhoh255ck+Dp/Wm2JVVyfHlM9K/LXKo2mORLXkD8y76M/e65Y8QuTFPLmPLSBuE8x97rPltDBMXFd85fskzjpL0pEuKP0Siu6fePyWmNpNRsrt5oRXGvmUJCkv4nJuc4gYfSCGyCZ4JipfHR4xv87oPn82X0VQLZT42Lg6FuDTnoDEGW1S9LdjYUbqTOuRpCRl89sV6AgHqmXplWkgbbr2KDwyxP/J2DbKfOojxK/c2bTh+CXESE15UCyC++XObuOLU2RB5JmMIRRvvyNWOJap+jwrSN2E7moNblr4kNjul0zFpf7B3ZokB9kWYoSvlJlZ5Hj1SN/tdKj9a1yd3VDm+ljYUBYLI1LrzHJs++7fBRVfLRMLgy/7/XCJGXJzGtrDzKe9ZjjkLvkmvjjioHOM6cRBc2Fxfaj/xrbZkHkQU0jdv0qpNeHdyWBDeBc2pM4pAgmspeIuDd0exC3EJm0C7KhBc1UiJzwZrkU+A38TLlw1BocLIUXqXBV9s/as4YwDP4mRvpSxXrZPXLzyhXYuT/URjU+58eAqxDQihFAhdaHiYbkdxv1YQbZj3DpkG1HiP7avgwzBzkT4QcO+8KmgE44OTg17PhrrjvMwHEr785bRj7ft7rl95a1NeeLejHBfNfUTLvb4oLLEZ6+FadLOhlJhQuahACMME1K4K3Rho+C6floHtxVFqFfz5Xi1s9MX5ZmPTlJqBO1dkGjcb+FD5lGUCJx94NdfG7XUKKKfcKq9AR9RMfYP1LFp+y/LfTYSZLPpLKp8IUjmWMw8ifW9mnwU5wHDD3frts+t/x1Fvk7mUv6Uv2UidsW2+9DbdcPAO7qH2DqNryslNZ0MeYARXEt7Sf8njLpzUuv5iFIxZbRKmbcglGRMT2PLAJDa1Kwzbz7dv1oMKR6MOrvsbI4Sc7Kh72e95VnX1dACxmbq4JzcQ7mxZFaZTIwcQuij46rM4pYZjm0snMg8GtJF+l0A2F7WWArM2X6eJn1DZqy+u4PGvSu51l7MqMzs23BRx/jl6cE0u7/l5vDMK5K4f5WoNPhKOcBLtdzbFY5m7HboOKc76UFGXis4i1n0b8xf0Dr0HKryffffbAX7iuWI5KYKcnLjUgR0DnLu6PI5CxGvTSAGATAjdR8LP5Ksyk4WcTFSexGlcfhmCvCV63+e7kyHAOQ3vLq1M1BA7tYKQTKP04FM8lBijVOvXrFu9/TLsjA4bw9UhQ+9iIko96TF5qfgEM61a7mnJUzJnGwbelprPo1P7csLpYQUko3arTRHQTLrJoVJ5Cd+xam3c3MqUm9hSJIxSkLyJ67PoMNTdszbyOid/Jth7tHcldL8+cSJp4kFur4v+bo8gZ+dploFVCIFXysodcsalz27rMPTG4253Kddm8/6BCiFxcKWzFNtF/MKboqlwpOELny9O3SVFQbaBszx/PJY717ezCUSL09leDcuACWwbrEwJilnHNoAlFVhMx+m8WaBQtJ7rsEwF8/Vi+iuZyVe+ZTEqasimiiUtJTkHRjUFmPbhDk6O4D/FXbSR1om5YNyCYPwXReXniXaSssYnGJLXvwZyhI59HCXtdwo9HLt1PuEXuLDBsrzOeV56LzGwpvMM2Uv1BX9Loofwaeurp64eknJQ5XkNlHQbG3F4KLg+eSb8awiCAKy0CfzLBtUwv+kUHy0O3nIk2tcq7E80dVfFUO5rnlWScnMy24NsCl9ZS9RLjW3RxnbXWKhT8IoPQErwFGZRTfQKkEztaXUUtzBbw4qTdRzmYWPJ+zJPNMDZhG4N2zkut+Sf6SvR4XyKbLzGyXA5JOmtWeW5j747nzu83H7TRiUeZbb1bNGnT/4z3pDPwXFE7rvF037QzjvtunWz+NOG3FeIBjFODjnigXk5MKhzDPg4OxZlX0XtYGIMOOYsPvdlsZGAce5683HTeXF3CpDDsPCosy1HnYsfAXxvWlKmS5hOHBOT7FbYsLbZ3hdu/3rREMh9eoSGh7TDLYjFgZlTrcmtvGFftwAzguppLrljUYC8LDS8GM/7qUX2PdW5mpP4U6SNdY53TTtcSU7OqA+eVe9vPA0pS17aOcslMJv09Ps/mDFX018eB7GE6z5eWeUjBWKTionv5d2bYCTregzPslncSbmBon3Jg9gx2591rbipZNUZo9lU961f83v5aKQ6AfxnAFPxGyS+O8CGChpeOO+vEfDEg4AApABhiqlOSCeV15SdXXbgp8/wVL7tOiKOcoSt76FQpkXsRUsYy6rLOEzu8IGJpwhv5tbPjtB0cpn3mSWdDqLPIQ7IVPmRWrhuRYYyB0o9KLyX8kW+OwMN8LTcwjPWHiUeaFCEZuivKjBhzJbXtMIxMEfWK+CedcMH1j78IFTznNd82R++cKnJGuGgEu3qN2vkYFvNpSHQLeZrvWrPZm7csUu8kTvKsp9spJwbqrEg1OhUkIUTOyxIkVn5Psou+Tsjdz2szTBq1derma2vb/hTftcnzSa24rB/lpIlWSXkjFKLzF1zguNqMJUdLkBeBtrB7fdy2EX9IirseNOfUjvmtHLTuIimgTCpywo0RkvoLdXKcEMjl+fmTj1HE/yAp+P2Ao/jWyFDyIoZY1JZyxBYiJcSjLJCvWyRFgpRLg01NGktTqreekV/Lkx55rHJXxE+WDPMDc4rw5NSdkjsycWEA0IjLIrehPSfLoqga48duWSXPf0rBIvhEnOItlOoTB+fYXYpoq+KfbZSGTfkUOgaxJrTVlAeZD8+1vdbHqE0uXV4pL+plcgPKBK6FNfMuXMyWGAPUKqLEItSSUontue4olgA4zSsm2kgYn/3kDL3jR5tZekSsoLV1zCRYxTElollZj5BCVYB9+oAqdDKqjk/W/0LHx6KVzNOSX7ccmFXVlEyaC0TnkJQMyipZqEp+tNXDHFBzuVxXPTFg5uQqwsomIEz4cqoIi+J1S6mMm8lTK2y0NPVr6mhacutnvlKzcqhFdZxPGgWPQRwE3OFikcZ32VaY8Gogb0iZ5PsWWhzxKhlLWLnYkJm6PEnHxY9mzhy2ZMhhUi6Ia3g3z4L66RD0gxmi3i2C5x3xDVkKiyNmHlouOuQFxkuZFSecWnO28PTfUDkvDXYM1ud+zGTxd2epHizYKTmc1LsRAr6XUVwzc8NHUZfC5/9DsOk5i8+m43kp+0E5YxiMlzxlIwTd8VhMH4iIVbWdBF78uU/hJ9Ycp3GmkmVz4dRuPf87ZxtwYi7TO269eaum46Pz1x4Wmcn+yh7peKRVTDvtm4/p6+IFoDp2JPsc+sJZlt0vPwVSiWRZooPWqIV5SEGwlT8tdozWUU9g6b7YD4+m0dqiye3S6A4xK6JdUSdgz0rWyAObuiHI9llGL6b/439FYTBsq4MuEcrSchW2J+bmV7qX4zmzIWqQpV8KleyYqaP8sGVLWzTwDGYWtWQe/Uhx7D384JqOZvlwxmWBmaq5WodlAdDtUXQzwd+QL0MJbtft9PgE921fnMfTL2VC71d2RRQrQsVBK6PCg0qDFtLoJ3VHzuexzRfb3akkOaZk5UZ3nZg681o7UQC9uyUNnAIrI6nZ+Aou6wgRUdneOwiM38FixmoTrhBBESKh95GoNBdXV54TOFdwmB3IFJdKRCs8YAA6I06NDHeHabelDHpTPkxo4U5LGXBBU2r7u5VuYyiQtXxeAIvq6PAMiQE9+tWczrzeaFm/KJjujpBB0X+7jLwiymd9Et0SIQuiVZlMusDoILHZZk9rSOBVle0O1+XJZ7UcsRLvPkdtNP9rlNZoH3DK85FtYlua+0mDRTPzUV74Ifdy0Yj/++3RnpjjFiL/Xpgedm04kL1ID0UriWRSaoojtuy98uP2PjUprSK4OqqUUxX1BCThe5pPpyYHfEPkqvOacALqVXvs5CsSw4kx+TFnjxM6gVTEbvrLjiHjI0k3Qy8+kKaANidKHe4ZuFWEl2DOnkWQOxqT3lZ/QHjtM0Ca6W69Ycye9uozEd5dl+oJAqC50M7QDKbyGCiTkl8/PA9MSQgFvc5oDMMmFKIt+qvHt5RdYwc25UQD4p3MpCF8VJT/C83w6e5PSP/29QMylm178z/0U4lgUwRCKM+QN6ZYfG/Gr6RRgJLPdtZ5FN1+CecovF0dkJYx8fiX6Fe7sgd8CEZVnkWaTH5SWlkBdlR6kkhfaEqt7NkCadUcWwPeENeOTc5K3iOYEFBoAKzxL7qQY+BbQlbpdYdViRL0pzqqLo4L629Za+z+yGWh8UEWsGOyVo+N2Jt871QBG6LF+p+DdSuHEGzwh2JQq50hBgvjE2f0SginyS2txQ0GeW98bCrKS4Eb6hoFKZbeST4YRV8K6p0CUxpCV6+tzFnHKrvSjo2qi9uojDCjKR4qopOx7U6I6PDQCxT+uKVQrGcwEBxjhHlV4rMeK5jiAS7EQ4lfQg82wKaX4oHx/JUUZp8I0qtLEu1ld6/0seqpyqLHmtgDTbeVxtJWznSYROWVBSa7uUDMr7Vj3yvD1JkohCyqGfK12WzdFksqcdQa+9Myqeg+1mfETJYI5I9LDUDBZjwSVD4hnyeDilfoURM4epUDEbO9i2fzws/rEv1xDy92pzxRxrXX6IrRLfXWTpIPP+HNwdfpTNsnwkH5NSVnTRYroLbXxJ/v/lDb4ptyfmVsEnlkOZUukXq+l3W3cNxIwSPYOMq5rgrO7e1pA+vHhDaXCS0fnVZWKWnNQVNCiN/DRkKPLwRMa87XeZ3I4lzP2Kxhll7pxN0WLKaF0A4HBVcN/uW6a5Bw+DPNfvMTozVsSeQ08klkJJ1hSxFEWfKtboE9pZolhytlwt0d3upxQyWXYBB2MfQGpS8KDJhY+NYFthbUN2YlvLvBAPzA+URVkRuebv7XNz3E5LyCj2ocYjz07dVBTkt5ZKSRalI9UHWb9+e+CKOw2xYKUpBTLYZ/svdDq7qegDEJFehXbq3tKRMvLcWhWnVvTVqpqfw3/rtKAASwnl5WH5LMXR/s2SY2auewGs3fsNAbBOLKeSrMmsBMVdywAl8kOPO/pDFzo4HyyZd5E+kFiQCty6/OhrJZZVCU0jC5qgRA11Y2u0cWKdGE6uDfQj7bBmN71ElAD41LM54wRdQktsUjqYlAjplCtH0aBlIChrYHCJbWGeVGAf6j1ltcMhaeWj06tnm398gdRg0TAuMUrv9ywEQFlbTj7gpDL58gpE5RuwEtS0/ch52hlIQM5LIvHYcW65DdcsigeYG0OZKak9r6qlALp+aySo1BweiANIJF4bOoYDuJMFJaRQgt8Lbqv93z2dAZOt8uBQxs39IGYGnzCXt0Xiu6l07y/SRYXtLiz4WnNBmaKgrCHRJcdEFq9OFF9y7aWLFc3RGM0nE38NMZv+rXGHsecoJyqM+I5bnOkd/S3109qJ4PACLGpe9uacdVOhlsTir1P6MkMT4HtXrUoMJZ/bAxwSzGrbtRFU75sBVdVDvF1SfV7DEpO9OcUU8PniaDBOjwfxBtvCLII8ojd37LW6f2v/VMr327WjA7qBSSxeOy3GMn3Y04VFXRjd8E1RlOSetLUuOpzYpKmlfOi5ejaVBN07iZPBpKFhcol8lb7WGkqUacpd7u3RUvNfoWtK3+hEiw7LLPwGlG4VyoTtEb+tJMwCU4ooX1UrjG514FZ9LDdYJ+S6SKEPrbqfcbloH4i4sRoZ1rvKc5jxhe4LyHkYKR+Ww3Lz+X20vx6cMiEVGaUjG4n4mMRxq2y0nLp9pGT7G+93o89LGdY1Vd7twKr4SaHkSQRV7oVh7AfEe6vC7sP6yBl2319HXfNvE1o2nF23OeaIxHHTCaWWBEp1WRtc0AnxBDdKVfCFUoJLcUlfAQ18JqN29QmwJPZyR5n5bjPLepNYvHeW67EuJmVEI/2JELO+w86k2nMbw3ymfjmjupyy7yErn4g5hRp3/UElLjtD/E4gZ8bkCjKIuUOo3G4q1p6bYgIoDvo9tJmcJOOGg7VJS3F70VQb7Hqn9/TaHelq03czkgEmRzqvtqz0NlUN9gIpcbR1HRC7o0Q8NpWRhTXGbgjqd/IAplT0GygpPQquW1afEk0sMB9P8yUV+WyhMasfXDAFTikTcd46tetebusnQBbNcJTy27cos9+94n2/xBXkEHET8eCUNUdjoeUHKpdejLbTv1FoWYVznVtgX5NE/LbOrdDyXcnLQ1gYN8EqquuJDDUYvT3qddhnmvhwwQHBV846CRD8JBGvnUfJCLnRS75B/vYJKVykQgySukc7reWZ5Nv9fJkPNjif1VlN+A6J18by6Ik6z/uKfi10H7XOZq43ZVGUsBjV89MbHnqRwsO5RrdxUOLE6cnLid1W/EuNMEWksVek7S/3/ZLyJFwlVx6A++iFA3BPARXfJ/Hg5DuGb8jkZl7WAVmaboXMUiU6+NY23yn+bYWX2jCr940/SD20zEzIc4uKc8izhEsq6unv7b1VyUtp78kqEIjph4DeUZfD/u7fhpYUs+LUXIpbsiVZleSTlO6y+tHV4Dammt5jXW1Xu3X9ItCSfckI8DdYHOU1gjcUUBd6EZPCxLIuySqKfYOMRrk1m5fJX2Q+NBgvZg5L0LmSOe57W6IlljfqSbOyhCje+65FUZDnKUjFjx0z4Swg4GffLvORWjN7BVzvD8HY0i0pQVQDXZ0HzUa6f6Eo1/n3soozxua6OjtghSeWa0km6URaX0fyVhQ3QOWPsAlurOH7m5sFYY+bvmvssU49iqNBau2y7ZbljtecoGbJ3hLjunb5PEON84JNqdkvx7Mdy7Qks/IB/wal8ZoKzb96yYocudxjadfUn7frakMnM23qap/igNzmjNwRu01LsyRzijwd1eG9vBD5U9ZYpL8+qg04xTz1lMpnII+lq9pZiUNEJ7EkS8qPouF0zg4rHhBQsF0vEqUi/MD9+rgbwC6Q0D5t6NIBezy0Yq6gS3E+Slw3aHEjH3C0Gt50bir4diz3Ak76eHip8R0nFFQw7b16TIUTdsM9JiUem36cGPOhg/j6XXWEBCW2KUVmr9gA6KQP+b3h1Synvkj7TCqKZO5GcwWu4sEqu1novH2kJOm8fUUqV+T//QGKz3DH7BeY4Q0lSrw3VQEWQLHmlRhYnAWyEPQwckqS0GTGg7N6TA19v/KN41YeLDTLY3SNdnlEqMRxJzqNx53dcxA9OfPl549R6r6sm14V3rGC4ZdRxKInXMZwXqLEZadhInClm+OKql5mDuKA6Ge3W1+JGh+GDnctXQ6Ju5YqG2zKwrGQgHn+Kfnsh0PTSO8EPYJH+kQnPV2vFYe9UMaMrHGixFcrivJyPtUOC04fqv9VrtCrBTzA7oWzjP22KTf1Wx8J3ozfFUpnqbCJEpedhUNg6znfVFzm5F2vKUMpJab13SbnU/PrnmRzFCaewSnx2pkawKWf62XbsLrzS71Fty8J7gEyGzYMfm2OG9QC06mg8iF4mK3LrmKX53CZuO0sU/korF0fqIKCBFURnvApuP32n4cOKnYnjAofNE7B2YhTzjhhBYLBINsSQHU91vLBbqGpvOnPtvf6tL3NxjwXggKfLRPHneWZFV09Gm7wE/POClRKI8X830UJZrOQcp7HZ+KydaTlZl9QXVvxytWOvgjQwAB1bMvXYcR8X+EiddM2PPku36rSKZWD8J+Ju4bHHUQf+mHp/SuWB0YYMTvqpJkGU+QDp+QdFa5iF9I0SSaOW2dDZWJZqDclXXLKA/LgpqWKl9uCt9WGnDZFkMkavzzy6eRo09B1pUjwR5l47BxoI8ue6GomTxi5szQPg4Ed1AeTcle6KB2R1+i7iOe2CwE+mWR6MCpT46Y37jN5mUy9UcZ+V3Y9VZAbJyc3PE98wr+K5m54xmclvjuHoOJoSngJJd/dItGUit6Ur5sBgz9IrE+XQMde29fM/Z5Zd5ZkxWCPBXJjdVB1tFshIeERfK23mIW1Q3MCAKGdcd5TmKD2EUNUxZzYA7ToEy3eO09tDsCrPHvJHorbMGlLrtP8b2vVp+rIU4s3O099Pp2RyHGVJpDISbR48Fwl2aQSOO/KAy55qANe8Fk+2TX1v1niarOszoU24XOKB5N0H+XOsRSqW5lmSRSqPGD/SRb9WVevi/t9w6OvSYRLvKBdilfUu6Av5qOJ86acQpreRsXEAJcMtIOqJezOqcB5GTRZ6Tfx1PnxsFufwk58trEaGpVTGwPuQIsfzynlH9MoKt5Yc94dwEivuZtjYIz96/sJH93nC+bcDXCSrflSiSMv4mFeyDrVJfMEmSAeXNiTokx408MtXPw8HwyjEfJyLoxGomIplim5l2SiInIFv0BhUQf3h+0K+/OMhgi3yccHo30ERH4xQrFkSjJDDeJr5x0lROX2CAT1U0X5daqokDjrlmtuB9rsGx91RT772z8X347txiTFfssqDXrJNWxGy9QSKiH0NPA7Hsg1bsrg/eEIRQMdfD6MNeGNambPM5swPSKv/d56dhDGn6sQi4ohZZKFfrcHCvTdIsqSqRs42wogbzKbS32SE817MuYkqxPLpkzjKBkkMs6rHyyb91BCxS9W8ODPo6n8b+3RLbLZDkXKQBKxaLTS62pDH+12+YHK15qXrkRvNi9Qmtm1Y/UgnXhN4oq5R8+jL8ucTCmGDwoLHw+Q+u27t1RwRsHl4ft3e6cpxKAUYPr5ZMWYl0GYxWnn9BuKgonlUJJBUi09HNGpfegob2TwUhEH9xVM6HpRk/tjS3drf9qT9BnlYAeTG0SZ4U5b7iTMsbkb94twPi3ljy/LGuiuiGq4b1BhlLfmktJOfOJsxNHD1ZNA1mbJk2SQHtQLbqkGwaaFFUvVYkoQPBy2sqsSIujcQp1mkakXOCiZo99xdZuLj8ZSOOEGvaypSmJtfPJDako0+00vpGflA82TF2+d5ENuJN76dom96xV5a0qMbw5Lu3p5Vsbfh2auOP93hTGUkrl46rQYipIzzG2wxoBeUax0FLwDKUfaJOQhjwwxm9yg0McDwSfOYKf5gIrBnGSA4CNRszKUWlHmUT5XZm2HtG7pszWuNZV+4CmDMpst3Qpx1SpMpwIB30CUwJelurtpj/01wpZBiPWM5aAz+qf9urVun8jd2kJctELpz5aYBnuAvTjkuBO7a6X3iIDjmstzIqXmM3zHGGJmCzRuTyEeWilbFvUD5OA96wPSNU3Q1a7Lbg20mSErtAZWPShBZ16TbY6nzjWwuDtFMhiTFyeraB5xdRIy5dNhLYoSblm31Gf/QzEHlORysRDPrDJbcPT3heVwIdmtKcz0NCXJ76tXQCXf7uvzcYVmPuNyzWyRuOYsV/LOv5abaiRUGGPzo9HdmU4ff7YVz6dLY/ZhuZJ7xLFC3DRlxNPZ0W1bUsYeRVmvTd8vxuNb5LEgx6Mg0oap6HJIfMPFYw9ooI8U0FhL6bY2F7wwPI4bVNjcsP0tEK4ZjLoG/zwYLfKRRf38mErCJ8Akz6vlcz8aidlhDg3JXyCVPZoRaTgHxIHCWlKI69aj/as3lC/TV2y7apGkdMr91pHfngBqJt47+SUxflM4GGNV+czskYLXfx1qFnlM6D/wiipZBn3Y7vsW0km2lnrc8pSV851aQSlMigaTikG/FF+C8Urc2k6panyzB7Z9bjfttPdfeGazMyuhkc2moTjvXFuNNwN0AzLJaJfdld/tvuWOotkpgivXPhipxEhiuy5OAjvEb+eDgr9hA5aN0ePMYiC3NtBYl7ko/Wwqg6YTCK97nBsQ0gxkMg3FdxcjkZt++SumehQ7yU2ixT9Mso3a+3jumP1axs1IcRbuMpq/kDhtypvEFCtGckuF0N+IIzFDogFRYUYgECwf3nA3Ih++LdaXuvtoiu3JBnsyNXSHOsqnP9PHiHMFEZn2mfvCxhUCmggWwOLiAWXitlru6+WBKrLMK3SgX+UOa+hXpcKTjCkkD5NZ8EV4scmCbFbBZdtAxnzbY+x+SijxmagXLOrqQm4j+qdCl0xCMP8tZBNccfPU9yzFqYL/IEe0lLT6V0NjrwLfkIFcQld8XNZHJxHWpEi2/4Qs4Aw62Du63rpIDQ5hNDnqqYJzyMTUC0am5wRMUc6mwpuE1vPJlnPmSjN4hivbkW74LTl1VllzZQBeku/KNEVcZsFvC3ESFY6eEt8/1RuKaxSxg347jNTXzSsoXdUpxcR7I4x7NW7OBsVikMrfSKu+wwgC0JCMpcHOD73au8kmTWF7mmB6bRJM81nBV0Rc4VDCTfSAjU+sX7oO3tfNBvm30klwUa0sV8Hcop8orPtAzHOuUJyCgQpmpWJWZDndqEa2DAOuIHxFmZYJe7/8eD5Qib5f6+gAmI+nxJ7EVgafqpp+hNkuRskGFThXq13V+8/fRrcm+Zyses7fLROD0mS8+PV92S1rZpBnsG3FnWN7RBU9y229pC/3Ol7ok/kwqBTrpriabMYNaDEoHxYu3lZ0ODuq5AC87zgFik5WjjEV9xT2XvhQp81GSmefjY9I/LkK80GtCBDkh676Xy35ieBdw9Jg5b62C48e6i16Jkx+O2GZ+Kg9az0nXpryKYknV5Q3jeGRR0r6sXq9ZLCBkREXEvXW3KXxwo7MQ7fUZiqu2pszFeFQJlk07A8ffbS/zQphBWHAmiLhbgLDp/v1zDx0+riONWjaC3tj+G8zMLdUiJRJpi1PgHldIFAf0XAsOGtZA/Hmg072oi/N6poDw50KnxLrjOMJQuka3MWI/jRnu7XMa+YpeV6aPAae6BJT4QMSB84TfNN623b1fx0obTqsGQENrKhRmxwpUc9CJnwgXMnc2g7NNon3ztP4JOH8hGk/6m+qyymqbPcABpRNAHRC7zor5g4N2ndR6NPkTlkvyClewjaJBy/C9CRNuV1+2O6PkE02YIC3k3chy351dXqyzEfMlDsWLk0z9ORSoVYmhWjO9dnJA1Te4FZP0hUWwzBSWSchL/TJDJJZ9ZCcDRKHPgjTfyuhynfTQgAz5aHbhl7W3ur2lyumNp8Yo32SzDSZm5earye+vBipcgEMAIU11nTco64NA9nPPK+w7KMgnLPQi5O+xIdTDPYkYs91Va9qq/QUxSpnMoIVwrHhruZVoN/+XEyUKRZxFv76GlmP7hRUhEcXamVCXiw62WtAdRxmAMiwtcEjBGdLUJkN4bPs+XrOCiv3kcPMuBB1Al/YOHHmRTEUotfl9ole/BKkLzTBVP7vUVczWFx3AxNq+amwLKHpMuA6SxR33+ot35skwTNvN9XgzWfWZ/kkB4YW66J08PEkYo+yIEoGLIKi15Jzohf3iVK6Sng49Au+t83zCUZJ+VQril+/i14CTYxU6JRUFiS9LTctOfC/W0EqU1X3DS8Q/vyn781nYmEWrrjwbgi5QqcErToeN5vOYdSCyuLI1Cg7hnP3e1Dbplq1o7Qy82oth3MCgYpNycQUbTFuxhS6yAZSplN3FPmdQUHK0q4unQCu44RPSYVvmk+a8Rf1bktXGILm9DKxJgPVpHnzvyle1Is8uVqEfFS5GJVmo62jVMAhacTC3wLra9cduR70mOtmgeDCI7hxU85rl0E+5xgTPqBCbMkHSuf9AcJO3csaOsQRpW9PYJW99OktyyoxB2W6/81n6lUY1QJXjxCFgHAmqSTQkkPer2sGJ34snw9UIe2eaz5FSlAuma8kF0lc9TR7036gyZiHuTPY0lSIk5QzjbfoBp+QBHX0aSGn8HjYH2yB4sAleO3pysya4RncfSpcSczj8jFWixUnzDguy4LPwJg3lmW+aR2YW+21zog7zC6YBIgJqfAlKfoPMAmjpvqNN1Oqoghu21W77EVdzlvw8D5coq+7bhd54oVgAxhhBq2RwQzxzAm5jjH09/yA8h4hNC9ixO4Vhe5+dntbHs3k/Ro91P1uVP1HPhRzgxxz8ciAHEuFHUkxPtfTcHEBGjkyRsrjg4fySHdEyC3t4+OOWfknouChl8hMwdqXzoYuX2Tx1EkcSdDA4qDbEiNAkNS5BOlKmS3/vN/mw8XnCZdTbJIPSg822TKEW0hUrVHFD1ZCrpN+M44dmnDvraclDIrgHnRNS912QW0ivtPioZNsECzEUgdW4njhXW95EeBhM7RdWEnb+mRy4qd2Y1yPC5jAX0ycdFIMw0iMALofRqlkh1cWB1fk7/6yKhy/hR3TrFXqbNjgtQlBkixKJpqXX7Ysvp2RH8WwhOc4FqCwW7+Wh8dq+6YYSr3uUBHNaifjowlPEl8tHsprFk23i+HTTJmIuguutiu7ha6DDs9hVXUrHkmNPbbXaSVmzfhcm1S4kikmV6JbcgTn4H1b7TH3ymBBR/4awBduSvDE/USDI/KR4/1FTiQUSbImGcuVo297Xh42CziAkwmllS4yFImTDqDPjqcimqsczccT982TtMnoC1iTF17roKgS/hP1UI9o/bXqvc/ExCxYdLkmZGtCl6SrE55iTS4riitxVhQCtO3v+iV/3ZHQfOojWpgaiKTryyE7EpYkJPTly/HS0Abs3w4d95Cu4TmLToHyWndLqzRjNNZOt6zGXroERilwRng+FcZkqtUgOg2olmzkoEw3C752PHOTvjvul5F5HgHLKO/3WaASzuk7c0ktbEnQ6yaMm/sX8DeTSGUoRqDzgg0zlnD/BL3+aUXtE3GNWqmTbs/vX/y3zqPRjIsXToJWj5VhSUF3pzYtd5Z0+lm7Vvt1/mZISdw7ErJkmlMMn1Yht9V+WTbf6auhZmtfGgq0F+tyZxfNrNvDyziBw67IX3uAdI68CaWpVLiSaR4XQrq7auq/y0eM3c4b3hMa6UIFX/4a7cU0yu6H3VtFHq9OexHONSAT3CShS6aAh42z7W8VE0ma4yLBKPCakn0APHqfNGDtp6IbXmoSOptTS+H3JozJtFCx+IGrH1Vz3Jo9XbAqTqIikCpoICn/1jLafK5GwoaQVIiTaZFZtbB+pA1ZAHQkKK+ma72qLKJCvt7Qjoh91MtNguLCLHKCIlxJsmQY4VDyv2FOKflqIAS1iukB/rA4V/gCyqlcJJvUa6uLWRXmSrzhvoUyiaRQ3CR0S48Gtb1QAX8eFsDll/ZTJ+DlmeI5MjAjToQwSSYNg7f7sivX9OBrKEvEOQUULOtYHoUd1bUbBrlPWiP61zKBJpJkzjTA3Gzrt1VUjNWmq2ZXAWPfq7TECdZMGWInucrL9hVq9G+XhHlW/TMMbj4g67rpoalxBQfIFOvfZnlMQWRfAV5iaRslyN3mkZ0u4Up8N3WnzgYST9qEKaniZExK/F42K6oHyidc4oSqe0ejnVUVWC70bac99wJOGgUzl8QrYp0QJlUsyzpN5L2tSt7aS2/2CzfdR63s5rHcHieBN/KilKbcaXMuxeSDss5bJdEQUyzk7J4iS7tI4rAwnROgFPuQUmLf41Qt2AewlBvR6Zklq6mQJVUSD3uvrk0ckVQJGwWwHkyqyhFa6e1H8yvnEtYpcJ0TgxaFKKmwWGmM4zgHKbFtsNocS2cOT025Cy7JV9kNBv1/P2m1p17AoJR1uJyzLbw8IUmqROe9TR92Zbk0UlbMAcT+hH5D7oAJ6LmwUA04pZX5Sjmhp+PCTyCyCFVS0U1Wk5byjdloxItWx7tVBGBimpPTsYQXJVgZPqCLYM7fT9w4/dtiPJTsZ95/LyLyqNzlvlsFRmBi8XBYwtyJgpr2auawDIcL6Mmzd+FLKsqZwlE5UHZ1a7UBgFA4QTKevVR/uTebe/UHszmNKZ5qCWVSqdDyKXhxyJemR+PlafBQbx6rDrO/fZ+drOsdott4f6HyEZhSLMvphCfgcgtbUqkkC6fTkiOWPMCYkuza9iJuIzC3wwf4QUqiOSY3xHBToUtC6XMywHnPINwE+ybJdT49yXZO0SiQ9umJhIpX+R3NwU8zNisezFJ9Z5A+E6MDrdc2u97u1+XqyEWdNOCpmqu3rleX+nEX8jmhIG5aCoFSqVwV49KAqe5bJuoGH8tl+ygcgVsI1fNoefm8ewsOUpHPVDBjBopTAythRIS1K4ssv+tPukr0nVgLEPVKalhMg57iZLnZGI6jcs/eHOA4rrkOf0Vx5hpCpCN0PtJfyt3QtZqq30FrIvhYQSbT0AewuO+EyKB9xhdml5gLlMOWiTvXyUDuNhoBR1TiDb8/paaLFxBo7koq3d8K4uU+4OFwjqfDZAZhVtKdD23RafZlXB+gz5eRn+NSAR7CygZVP8ivw7menJIPREEx3swZYODShVipKL5YXGXJCVOJrcaAJxdpcANa7EjcmBtNtgyedjJTT+zwjDgH91WFX6nyeICb3B/+RhZA58TLVkH8kO6lcVO9ezgvmz15kq46KdG9she0fHI3QA+mCceSroM9sAegTXChMPZJyJtBhG7F7ZZRCnNZQZziafH5sv+dl+UzH1muffBepp6ZHSUI45KS8zeiXWe7NZMboiwpAAbFptH2qRd82FMqDF87cQ7k4/2KvjliM4o+4V0qcgLair9VvKnOEmUhDDfZ5zf/8TyZ327sPgiyqXAvsygUxEkJudB+nTHVqZA13a6aqsGNsk1yB8rDR8YE+ZT7fPjxCf8yi0Sc4z3Apfu9TTmTXAU3FdqJjI7vCTxvGmKxl/wM44ed8CBEPeFeZtFIEcduY783quI6oXKG9XJNG6P6y/bnHcB47cO0zueWnRmjMjEqG0DNRrCzfsRZFCqzhfBocvATuL4P4NNMWp2MJ35sWqzKR90M0C1Zf8bkJ3SfKMEdVCgMqRgZ3tey2fQ7PaecQi+8TjG7agSlsRAvM0xATqiglL/8oKBMtypksorp1/VJH8giVD+PgfK+I1c9P3IVwmUWhzbP65XPzzqw0zBxjU4l4M6btu3Vu05WDPvsOy4Y0OxqswBdpYR2mcVQlZswn66wfhWgsii4KpERmwEC1Xyr/1Wfzlj8gDrxHKA5yXmNkBgz0lf50pVCcV5EYaHMvrMLOpznYeXBzwjPHsdkBgmupi8GCUqYl1lMNbl1T3UT3B3qv3tNkw7bBIXEf1mxeNBks5/yadbpYm7zWcofTPx2jJWHoiJO/m9Z0tX5C0o92HXets8jyDe9sAPPxkd+26uiSmcFVsAhVsK9zMB4FEhKS1ncRVe+Iu2FGoVtMsnKM8rido+H7mntqA+8FCDVrG4HOhhKiJhZEhXT9bl3wAzt9wbFeFs17ar3R3e/sR5WxXPT8YxvtvhuCE/IdAwPGXuhSvS+kzw50cm/rzd179Df9Fih/ecJZXbS+BWsEt9NBfqox3pEz+KcV7FESgtph9/a+6ZizexpS8WrQdfD9FzCJjGsEW+daLsX2tL2zIGAKK/D4Lx8YkScXejXU50mby3VuY8oBGsaOhvjBSwqBosGf3QJvEm5BpaBLmsaBffL2mxnqbaDTj57zPH8EODVXx9ROtfrhaiaEsJlxtsbbDHQQrGTO4ZRXui3Wit0u3f8zd5yVFMfHkrBlN45jqoSvmWG8HWCJMKejlRrCnBPaIef4GEvr+g4G4p2YGBgtbwfVC9xoqxQ6yrhWmZpEkvMX2+Dq92+4youKdIiOGvICYL/NQZ335Xfy+rNWpHUj7pvpoau/BaXW8iWZJY+WeaJZGmJoTw5UTAbV8DzWenO2V1MPsyKmCtwFy+Gv5v47gxSSaOGxZeufan+prAeYhfzD0guB2Ndih0jrlxtzNRLJJPXsbpSSnQGlDAuya5iSvzq6HDQGZjZ7Xm2gTjLqtycCC16rDqx8oGuZBKaJ0pol5AFEuiVKEHd1S3WiuRYFNmAlS1O/KV9hk/v/fi3szGUh56Wl0hVPLsPkT+k+PEssx/yvqQ3vmq73o/HRTKclsxcnIoMPsK5CQ/IXIEFTQsl/MuMKQD9WXHC9BW6uElOCVMvgmTvFbKnvo05SZq8Ekul5qiEnA0I9zLLw2EEDD7onqo5KrB3i4h+E2RPtuNit9/w92bLp9cGWz2n3IdxlBL+ZcZCD4J37BDubqpq1W7QP8HKL7xIswbN8ojMqvTJHU9V7nHHQfxyrxwE8UsJ5ZKMyqb0xjvsQqTcLKXwy1t/sa/VCkKD5WhW7iGnmxZyvk59pm/PTl1ol2RXYaU7qRh/FkQKebQMRMLtYfPzHXZes5/ZSWLGn058eZFY4vxdr6F1c9juuL9E5RQquUMDv/XT9NJrQ00+B04DJkUJ65JS+mjYqVm+ou/2N2YBFIHvyel4JuA+qAs1K1PNGa+QLnUYW60a9GwYXXzEg1Oa9XKZQrjd26aAm/HlIydqCGgzO76VkCzJHtsLMEHlgeoB+g9YSDpR7rwpg48tEobTCavXmrHULPVyIdNQxgnFkhxkLiii7plnBccFVkgH58euf2EXTfuDnPPnW77Kt9VfNdW50N7xaZams8JncI1Cr9Rhluan8/rzBvoiSZye7hTg3/KIIvN0HuZDiM2LuXiW8OEUYtNIeIn+8aZf37HDAuQo+ETu2i5lNr9iEDo8pYH4hZF8DrnDSZwwLDVMk4+2PGB+2ELSOC8yo3zC8Ka+mXR4OuxONesKr7AfzukegTSohFNJ5dK4FbCF3ConJuCj55yDj1oBF1DOPbEnyr3GmMzUdRVw6OEqIVXqKC8k9caYBFMdyLvlwQVrDFpLep21wAzmdifaa4Wv4PqMFgyukhArdRxpWTa42oLo0aACiejeM/SrGcmKfWvJNbpappGf1jKPe13Xm8tKIVhqSsv6WcCnY1MF71t0TVfwHir4WK7MUnbQ43dmCXJH0eWV3NWbjMSrRClY6MRJ5EHSJkRLqnXz3LYCUew3wXnb04sU1d8PFboD0sY1bOLz8viW8OBRoxj1TKeAFd8pcdtJNLSXP2MLVPAVWNmkoBB0g311vepZtYWI5fQmeU27ipwRHy5HyY9fD5aoEbAJWOI/ufKn9492wLCbzgCt7vDe8AKR2k53nXjdJpDC3DALoECVMC0pZNhBDsust9/poX3p9pAcizC4xBqYfjtNVe7K7b6nfJ/cpdgH45gbHTQXU41dk3hwSFhNEkmL4c/TLLjamMG36eKuu+qH2c18c7Zg0QxudFMC6QX9MEtiXcNK5CPCutTATI/6AreH1QrM8+y08j7v2u1fJ+ljqny6btGcXA6fjZAsdRrlsjAH1LOv8IJNxZhVivKb1j4xICk4q5wurfY6F23YqC5rkMwK01KDB3ay/AGD9kRDpr+lAn81VGvn9VMPJFrcPkAveo8vhXmrZwfHnV5zsi9kS51igD5igFQN62VS8UPXJSV/QG/tpd630gr8CafZl34Zu1fD4qUJ/VIrzJUnDUqWMqIiMqX6qOVQYh6/EYw70aL3lVqf04JEYBPapc7SAdMBGFPNi31/ANGZFhF6JtvtQEj5Kdrah+SUzCrSc+kvzEudKQuP53d2XzWPR5BS0KBstoe9kRzsMwE6JapOAH8Z6ERx5APKMXNll8wR4oeQLukWD52IMyqEHgwUCbIuWOgz9AHRyXpzNolXhE15A5wrmGl+a+KqqXKMxmCqL//vIQxjzTIdWJMJNXGzIcuodJjSCM4h+LBtgLGaTt+8imxtUqUZwWElDEytk4EQetZAZuVdc3hmsXxKRi/aFzoie17va4x099NxQJx4RP6fazIqoV9qnQ/m4IRqYDaqVYdEKIuVC7L726g4PUcv5A6A0DB1jh8ngwHUJ2afSrU1KFn4KCqSdmMIaPN4+K9D1dH/nFZ02mtWaRayOeEvbJx49DyN+8r7zxqzXAh5MRMroooO3GsqTEZUNcqBua/r2r+ifA6tmIPAcCounEydZ6OdjFjBGtwwAADtQorHe6AtH7CGxeQFdfOjZ62eNE+poPDpghvA+ozKrxJWpgbnTIhGO65//8S6Ea42U3ZX+/YFDXHRG/ttjUZIWbor4YyNEy9fxBZidb9GX+esA141yhN8vpIFW41CFQRl0WM57E+WD+Re2jAGYey6+GyPeHeMRUaPkgpcHoG1iHIA95KjeN92kjD8q2JMmcmlXJg95FLCytT0ZG1WZ3ZBH1dQRI5HK4YkMXdiYXzGc5ma465iy58SRia6zNlYuvLyAHhHRTc8Dc7q7qXBRMBGnH25cevtFl4gPZ6qOpU0ULYILxOCdbIGmbXEsLOaKU7Q4mUdi75uMXim+gfmiqeixD5rPjHo1c6YzINeYWXmoRZxBv5ol4c1Wrk67TcNGfF9W9hBlhxDgkEIga6QD+M44Z1srkwKKYKQMvMoTkdi9o9IN9G52ZhPl+uYywDyP3SvDet4LNPulM7yIRwWhm00V5cLOzOHFKI4qAPrn32tKzgCoEPJ8zBYbstl+3LdUmAEbgjMiDe0MR/FBkA/YqfjZOiHMDTJrnyyT8to+7WLBM/PSpDZEd1jTRXzePNQ4dWa6wU/5mbQwsqkGzpgPi+6I32/Jrhbtyv6mTF5ePJKzYrq3+8Wg1aRO31+O+TRYe7TfTKb9RxHxHMnYWiSVUMjExhZDOHKzQuFFpRX/zaumFkX5ew88VWy7juP46HYozf2WHYl1TBrSvHC6N8sCK7NbG6GxaqEnpkPZDpRZ8RkADus6L6ebmw4O3RtV540ekKfkgF5cORGDbJ7Ei+egO45oTyQs0w0oxksK0RWWT6LEvikxxMVPqQ6Fc1xjkEQUcLQzCGreYpHNeA4JCnoZAIax6Xnb2uhKoY0O2Ov4k6gNSqN8/6czrgYYJofXdmEqqu2EQ00JxfLB39iGNlO8Tx8MKFj5lQrWGwFlrBDIq5F3ZKG5P1v6+b7SIYcQlqHcvH5mp/9NR4jHPsihAa8h3NkTU9nqceHI06bp2n9F/uGdmrwn5SFV1hjj6SMPln7ChwKMqS+Qvg5MNYHy6ANJdolZpEzlV+sUyPJuKrhZPx+zxOxHYMe2A4yr5dFo1/TZ98Dlz30gVopo/I3sxhVCTszVyBFT/Ax1yyAxoXPWzYkPf79W4ZKnHgpW+m5U+JCStiZeZbpvk8Pyf+mfhk0P7C3BYJ2INv/Wn9A+8m2zyB2Nd928eE6Gwi21oe/b7d/GzQh2Sy9H8k0AzOMqrcnIIsw9ziu3IjIzE3thKCZaz1s/PxYlcwj3+0oH8joH74/bCkbAIzQzjarH7wKY6xL5qOOkBiv4Ph6OZ+TuHEN+SYBE7+sy+Ds6VBDuBorOOlmPyHTtEWCsxPlOz9wZ708PxByJuyRDOX+sGMs+u6ZE3F6uEBaSEPzavdSbtumfNMoUD5N+swsI56Rs1RC0Mx5vfUwg74H+X8Hqf0wDe1VtwdEpQw0AVzZrpfcry7mMKnchRKKZp5H+WjhxaqryuDmCE6vxup2o44C5vGHbtM6V1yEmU93IJpbr8vNaKFm5hRchh1JNetZGJYvFRD0zbaYy/U+6Vd7QXyaPDm3El12MRpNeJl5LjHvfdWBPXe7vIFiE5iRwX3JqlL22/3WlivkcbGz+OU8ThiZeREPspaAM9QV9O25S5hSjgeJpP2+p6jYjfL7MgB5+s1OeS82e8GWuUYc0NdWQswswiQedLeOkJGszF5rE9LiHJsTRlcLcxcDYXXdL+2lJl3MZb9cCws1s2DdQ+sVjlQlBTf0282uQqwIbIFaF9XNn3AOfVZe5Ay3mtkwoYSaSU9oWDoDmGqDUe9/HNB5TRKogBvIc++t3lE6+laRIPHbemkaYQ6TICuhhJxZhNpuTr1rj0wvOi6gfcHfydDa+9r8a3M0e9xPj8gr5zStfBf2gt9fIfbkA1m0X4GFlDNKioxBqt/LAap+cwDdvTrt3kdeAttqrkTg3pOQMQuwYUdzPAT7gB8+dG5U8J5iG72+EX0GVj4eTnU4I1RjXj2CZL5HIFTMAiK79rMxydeiGKNEoyb/zlXLzoJmbukDkhObtO5jP7yT6abMlcBCxCyifIDK/bPdPmEr+Z5yqOW6QlpXBB+2T51wDxxqd147ycPZ6Sufj3XjwAHLSztn//jpsGfR3VQrLLp4WtvE19CwwAozcMs3EUbnPvmTymZlwPmgUjEtGnoFlxDZ5Q0T/eQTaveQBpxqqf4b0Cp0ctFM24BPTpx6HCcnkF7IS9L1VfQb1+2Pcm2pqz2JjA9terO8gBjKwOldUgkoqoSfWSSh3ZpwzolCX8BQcMFSOvq4AsH+Uj5z6ntKOch8AjJKULcaCJegwswsksguLjqjEpiSOcoRLpoDJcBFElxX22pYpfQNxQo5pbFYodcWJZXOKfFxJ0yomGRMLs/u6/qIFUrY9vqISJdmGTSvjjsUnNyKNpyjfwPnIGUErROPiZAn1MwiGTVZwWEpWQNhtyiymIU3GJPVX3P6nPQUKIuY1nlF/muYiCGLZk6HDrJoJszMAnAam53/1UvwJwl5uNOW/WW179p6f1J0evHXUnYGTqpIyurxYkyWDcdj8LwP3YH5dCgWzrYbrA61wkkOBI2PFmcRzu0QhF5hJmzMAqsupHDZ87pZCO2tFxG0JidkOgzH3HJlqc9MQ5stzzOLyzOhZVJEnCgm1dKjT/IkDL7R66ogPGtBRoflM8vxTTaXA17iA3gCV8yVoeSwSPx3CiW+XnGg3D5DIAViSRB1+9q1u5dqKWsxZwkPPkIWOWfhDnuQVGbCyCywZVF6mMBeMAeSV27E9Mf57wjNeQHDQq6kXD16vkzitlMVncgD3rbbZXnEmswkOsE9PFSblxMSJLqgXh36yK3Ar2CNOG169H03/DP9tE+spxGpOOCFoUIuqlgA+81gPPJpYZqGhXNDGH8wcdlqtLXwgoHhPbOfPh26Fgk0570qgdgn8KezK7gymFUMZtnM+7KCoCwLAlOWh5WTw87Cby09Ps40HbiGzKs0MUwHVwmHhyakzEJpC+c1zIJePjHJVQTW8XosFO4aiHsllGbXlYvSCz8kXMyCNY0MGrRf6ihSUkmSh1idsG6bAdFvgE+TwO8nJafmVCswJciEj1lkUTJ2jE9t8GeJn5BGKRUD1TNkK0bKQ65NOz6ICh3NDQuge5IJEbPIUku86JlotyVDF0JwZV7JDI70VrDGhSr2kr3XcxxM9Jgz4WAWrKBgh7ubGonHbflEmSxluDy0XNJD+m5r256/2tGzurwYvf2A/hMlxD5NABXNLeTO+KDEX1MG+Garo2makkeij3ey35Xb9i2SIteCVy9yfxbPVU0c2oSKWeiRXLkdh32qTaKRk2FlY7HFlMM911tXraR9el8FN0xcxUgEg8Rra10k424A/TxuB9BbCu4Z3SsQ2mrFQ1UX2sqnsaRZ88uJ5uPPJ95b58Mmavv5rpbP3I5LKAe4rBr6n1/rzHtBe3lJgAvTx2mk8DCLPLPe6bLEPOD6wHvdF0kBkkivMy9LwBo0CKcEGi9yQc4rOZ2r3PD4hINJf2M+NAZ7RFoFcl4WXLY/qj2Yq3bD5IZLlZO9QKkPiY8TfmfChi8m5MuCaiTBVtHXIX9j5U3o79QghHAkkYUcK0QZB6Yp9CncMrNVbkZlPhPqZVEou46XSkZWJW4o0U5jaN2Wz+OyaJaz7kObZRl+V98bDNVMuJeUvQy7Zu8Oj7YdX5A/OiDcBz9nOfvy1GdEYfhoet+t6E827JX6E7u/yOMxkD+hhM2+Ickcy81LOZ3pZr6R3/3gOQ2xhEuyJh6atlcNFhEFD3hR33lgGWMNr6Gk+AHjEh8+uFE/cInYIZW05EuyLbGMfp40XddPTwi6SZHq4OuxKzf1ykpYseM2wNXR6psk9dnlkM8J16VsjrbmRCM1Ft7lcMdskIisuW5fhtqxNVteFl/uFl+66gk5tvbLRrK5g4Fia2bZl2RJOla5BvGjqndo0VDu+K5eNUKam8uNfD4ThvDKXQohjFneJazRdqHs6hW85j/bffkK9IGZl5QDu4mOxbQhT5N95UNszvVc0cge2jIuySI1lNWMaiGfeGied7w/NTAK++jVML9pPH+bSox4IZYMd9e5DQzHZHmXZJTWfbSnn8TLo2+XHyizqOGHGPwCeeRhv+09vasjpP4+X5FjfOxYbTfLM59qTTOP13mTUBZZ8iWMKmRNefOjBpS6g8IBfX3yxAcyqO/61y8vE4UapTxR+4lzChjz2SRiBmW0UxyHxUzocLJU/mf6dD5tbDUrLIiAkYiTjmXrVk9lhlwAImfEglnopxvVHJazrhvrEq8WKAyqRZhmykdJLJ5bJw/RlSwRN03mxFKgMctSlgBlYA1sv1e8n7ySqXJbbeuTflqCosqrpHavJeGSOhH3nCodjxYA/N3K+nZKJrB3e8vEZvP0WfJ+91YMKvYaZ3GV78R0sUniorHcwkYMCp/mlHZmWqsob1yN5tu/ieZMjU2O2lqzTeKslVylHkqN2TUGbORgTJP/CQpoZv3e2w2JkU8MM/vSnN0Zvkfiq1UyTI5uDsuu2oOFcuiWNcCcCfioQDKXFjhJX/Gx3D4vvnz6/1l71+22cWxr9FX4AD5j8AqAP+04iasSJ9m2Oznd4/yhJcbitiy6KSku5enPmgvEIimBCXa5xtdf76ruLhsBgYV1mZezz4/rCneBsnMVQtnR1rHdBwFA8yGXaA0ZGeGkvdDxbmHbwk8H/Gwhlgycy496EI2uN1R5VMcc4jKEeKK5w+cbFYEnr3KJ19Bd7Akx9G8bHo3SK4LwA5d5Olp71oQKUhUMTCDLWaEjlUvM1kkm4tqbhn3JUfIzxSnNWfhUHFx+ES/LgE6/hePOwThUng1ryo+EWN+zxgHMia6ajcvQ7M//SPH0aw2uzKevdPN+wIDKAG8R1lkvvEIi9lBJANc6PjGU+Df/yCSnGg8quu40jWD5PuRGUPaPfNufBXCnLS+GdQ0FtrNxfdPuGFGSQKqKe/7sxMUR4V/07nYealqa/F7r242NfGJsPDbK1bCswQX8AXGzpXKtyAvgO9fDxbvgZ84u51QKJggorNm5wTuqRRKXSyw3iePNsrn17Z4KMhBVKZRDjLlasDOYC+ZUUXb779+9XzALMSgwlqrqAywi+c4lnhs9SGtbbQG8/y00IKBkEzln4t8WuIG+V362B1cEucT1MpH2Nrrb6BUZunpvqMy1eUqf69qsYPICxyGY8zKe87zlIZvjXxb053IK+87QUT5cok0cXTRD8+/X5MaQKlfPmXJyQeCYl7QqKmSlysU/zPQ3yq0hswqL+0P0rv1rlHzfoXOzXo877lmQkk86hzPlhM7RLWlFWR5Plb0BpeTRP9582ggnVgN0+va+Ohlo0e0OiODZrCw7GNjK8SxpQflAFnhXHQanwjwuYtaDGLW4X+NQbvI5OTjOLx3FsoBXuxMZtrrH1wt6xNi01BjNehAVOm119bT9Jw04rLmjdyiAIsHRLLHAQcHyjx8Vblv7BJJONq6duGKio8U+oSOLwKAxaTnXk4RxunIMyyJJRL/yphcX44Sb8oFT19nn53W9O7WcLUM6bgXPkb22xXy+9bAgp8z+dr2k7/GpXvNhAmuXarwNNymgwCRasSxL7sPAFmGa8dYDxDda4lhghpUNJa+dddF6oE6BdEUlzJA59C4zvxlPhGr9x/5SnE9TKetCU8dGTsrcmm3v9dHQzzSlie7qzVPDA94XyJ7Qhr5sj9ThwjqDeq4m55ipJJJDv0+aKNsVEhU4h3PjHYCJwDFcUNS0VNkZfoxSEscHafbPVChAAwV8Rqpro7cUgiSju4RJyXbsOp2mIQq/3DSdU69XSoI3/aFSyXYP9OI/1z9hZVn0bEb6HOvRY/J5Wd+3693QmIQAYxiq1M9Fh6OGUhK602IwUeVaZUFFnfuLM8iMUVa5fcYk/qH3A3KoV4Z0dBN/bh2ytHKOhQqygFISw+ndHZjf4JxAQRNaY+B8/1/URkIMXk0+hy/ll0VJ4M4y1We7d139o+2i9zAOBy2OPuzsZr3ruLgb71UIYzdj+wjfheN2t5IYnuXOOfx2xZSv6F2z6VGdMWXii1XbrsfUr7vqsG67XrjixNsiUSHyEMkcvZHzOiXxPDNl7kjqlI7vIqbi0NfqKJeF6D8FpwOoDT8B4o8u2j3995h9Qe+n7k5FyYsyxEHUmDlDiYw3T6J6Hjup/T+2T6CD/tmC/Z2lReYKc5bVoDi16KonuqYnZXphwjVkfNguaMgoJfE8L4cK1OJwLtsDGCmUHrzv2h8jNVt2XLa6EFNUdZDqAWMqfL0fYL2VloBeZEMSzK/YAe3e7Q4AhZz+YNFbyq5eL9Vi4jkbQ2BxlZZgXuSDbOxl27GD2Rc6N027O4Mh1/lYaAeqsSzzNUW/ZUkQqd++wb5eHW+RBPYC2iV8ipxW04dmuaQXJtfRR7qM7XozwQfPQKqDsOeZBXv40PB4iLWEd5UMw0tH3buuVk+g5uR4iYELlIk8XTbG53+9g8cZ2zjEhQoxBrPyIt4MCu+NlqCuskFqi2rfZYef+UKPIGZzZurV+a5q7CI89qpFEeKwnM+hKbj3qyWoK10WYwL29d7Ki+RpocMK4ZAGlC2EvekTn26J5jp10by3nMTciTXlSy7vGJ0/ct55JXHeCtd4le7540kgpxS3GNALQOcwLC9Nyp7vaWtjNqPfQX/k4Pl0iQ56j9M5oWYeQmszrCk/kvboMECldE/zzx41eq7qbrH3ZAhw+gwr7QovqoKTcS3BW2sz0j+J/tgg7VVJguY9DOolTp5vqlXdPJ0U50EkAcX4Tt8MgUO3kdDNwCMXuusFA776B84coTzf/qjRfz1BeQb5gAHf4fdZ55mvkdhtTJlOQAw1vWlMVaciiorz2/1mMpr/Vm8qMFRO0adhbArLXvDBz3ijJIAb0UpDmyfiCm8J63cNXcLhebutflQPR1ZpsQ7hgPbYbl93AO+/kbBdxnky0Re4a2i/Npsz/DfgD40KlisgiOmETbUs4ySoNce3zHemuWoxErbL1DE7xNfqY/MDWroMpy4QltoO4qgOyVBvttUCjOKpM1GQuh07b/i+GKiyykjYplqiT9w+oQf2pUbDGzDY1IJ0dk7yc9SmP851TZA7UVnMsWQZTW0kdNM3Ng7QsKfkrI3u9h1biMe6dFYEjtXRQqPm7Pzj2fm6uq+eKq7MQjJvy5D1eW3xavSwGteatxKki8teIlmnChXwzvZ77Lzn80tFG0aH54jGGKsQwf9ZdSiuVBytEgz3Qm4/bO0o3e/sHcqzlKGxv49GISc745aODw7LV62U9UhL55x+wr6jTL/jyXh2RLbu+5anNGsd1Pxieyuv0zvefseqpPUoR5G/3aFKokKOUg94vWd2tNOn/FaM3HbGJoDFgIGhHe4Y7+vBwx1Hp6TlGAfQudo/rKI/WdQBOr/H453fOG2FDAxnB04MNHWESsh1mgG2yNJTFaQxOjtoRYaRUUVEJ5keWyF+extMIRhhU87p2kONQjlmZYGpWuJwi6yCeAXz9M3S9ndK6yTTJ2zOQ7Xhzu6kz5yHiKPmZq5jCZaZcpzKIsXLNEpGLrpm81h3q/b7d0pbqIBw1upuOk7pyUNLv+YwDUtBTiQ6m9UjxmvimJSFHUD2PEELHLqH9pLiQ2XxTY69MFh1Hxnx5gFBwIoMetU1kdg6IiUtKHcTlf7SwVPO2rdmoOr3FNSe6OGjLgZpHs6eJM6QHI8SDcd4Erb5lOSU9Ea3h0UFo9boHW1DD7MY2TYeE9BDiANgm2b+vI2/mkTuVI8kSJnxdtlSgauS4H5uqCKqn2zKvfhSAndajmQ7ng7w1OlqWIAVE/5yTxo4zflD9kbbKsQHGaC90bFE7Qx2MWNWdU17wsiBrQXHfGr+GvRxPmzav2w2cveJdgcfbEvpbabLkI6EBcf4chFkkTqW2A0j0L7vVm/+t6JDQoVt97w6WMHmyZLerCr4S7XtQ3W8qCDaYp7NiXZo3ikJ3sCfD87gFLPRnNyvdz+5AqY6Eg9MdFsDX9Sbl3On/l93Z//aVaszkwflkIarNW9zssB6JGpTAqjGrbYGWRJjYygjoWSbJf2iP7bjMdgoHTklxcRxkCQUT+d9k0yoB+hYAniuB5nBD7C6pWqtuYfDV5bFGU9aNweHAFu/VPtHFEqT8VwWFJ36QYqPmcs7JtE7pxpEugBA71XRv+/p+a1sWnDT0kPzo17vxtHgla6pmZ4z4US/S8cSyAtIi9tZZv1XhJQFgFRkTguRh/vY9jpQU5ZVETJtKjk6zSl56VhCOCYrsknVHvTcr/Rbt/+FSmamKWZ+aZ/XDprqw+6FtN0yNQcdMnyKJHZTFt+Hgo9UItI32q3oaVN5BBeX3un6X4DrMcpypHsa4tBkh01z9u06LodVZHr0rglKICngZrekn7aemlv6KAwhTMo8nav5ORol8bCivJxgdd9XwA0qStcgdzyoHV+h6b08+/BvTH53+8UjBdFEBWJiZ+ZdmCzpRMJ1oeQb8Rj5WwOMIB2sTPH9eRyxglm0701bbXdH+mohh7hH6c6uKB1W5FrafVr0odksWEqlMBF0DX/Dffk9uxzRkg/PTCWiEwnURZn2hRo03YbTk+osiz4CsbHhsRss7Z24i5sfTV80qEGGJSJ+biASEZ1IhKbPJonIJ7BdbhCcKb+I3jLwu1vuhNNFl88+/JODFOgbY0kVMw7bOpEArdIid4C8XhO327Rc9hcKHKqRlANyy1pweRMh2CB5SoAXS7/sFJKRRCKzyoZ07Y5fseeaD1Oe0fe7EHEs14zYLmp0j6Y+NhRMAvYpZxyVb5/4eEuMpnwjHh6yfQXb2I97OsaJ1ulEURS+TYsj9LkKgQUAMZH7jcj5EEmAptqxx7zQI78e3OyzUsNEdg09mSFXq7fVc109HuE6syyE9l4w+NwLw+HtkWgN6fV+oFWxuyBAJbhNoOpGn3fAmbhWDQ79pp1CvTNYcgaNsrRfzQE7lMbDchzp3aJsbC8N1gexLa25JBKwKZVG++3UNCYLwZj2+mC+A43HLJVwzbKB/eCB94XByiy3mtDDipjMbs39iqBhQt+wT/sHHIBWwdxJbyzCRUslYpt8ZGFR7ThdZJ+4ClJTCvn/oMHzi3MUMjXiN9Y7GMUbm2bDmka+o8C8fGg6hiZodtRarOhH9tHotQrQyZxsPjpIWniUaRkPM5FbljNdHzYbKnghEbbkjyns6Yp2jwW5RpS8ICyg4RakT6mIQ6PwKNMyVfkUFfyRggyQY1mZRIMQRq/k0myfKBo9nn267H/lZfXI9b4JqoyKbI5sqvi7ScSm+5ZPZbLfd83/wvGHLp2JKS/abqtm4XaKdZRY8/EY2hlUQBrL6PZFSr53eliWEZEpxj507fcdFdmUQ3J/NKgfGWQ8am3kfTJcKVbkYnfGRp8jGdo6+nxPCT8Emujr/+t5UjrS/Qe0ZdCgNSHyd1Yl0Evp5ihZyloKNzq+sLrq9yzSW6YJd/9fRmxcqsvorTsq9uMQZI22/CUf7RVPiJApaTVFPhSv7hz3PEFKVlMkuc13gABYYXXeVitk3G+bonOCxlrolFkCPzRh495TWg3iLRJbeksuaxEFez1gWZWzAhPI2YRMmUGWfhIkWYn2BfrKzsbKaTvWLw1P3caMylCDNr8DItgTWhiV9EwWrhsKILDzZytjZpuAWTX2Y/Yia/LAEX/qV5hBIBJCJb1GDg/skDWXzcMDRiLW2Hfg4GPn+gR7JBsecqStB4UvWENfUgubkhYzdProIYMDU996RGiNrICL+1Zi0Xoy4w8bhFrGkq/LjxdE6JRZmhfZhLt4y+ZGSET0GE9+AZL++ODoIBgy24x6aTd4VoVCmdF9H9BG9Kizc8hlhwSD/hAXHfTJWFzmdaptdlu8zFfeFgnMmRl5BzE5GPMzVI8UfBLK0SxQTZ6M38kph4wcDBt1zD0awqbMcu0OtTXyap9BM7tdrCjYoBmaQTPtgeLklkMjg7PWbTvqzQSpYFuhFC8zEM+qMCkphOSjAAQlZTz0GWW5vaG3szOY8zsP25wZ51PeHOFPYjGZdK43jR16YoqOP1B0SYXhyOfsl0LhYd4qqNF8ABG8ZkKehG29PrHpSLK0iC726IJ+bYCAch0aOkxUBu1Wk8QxqIC1ktyzRaMwJ2GqmR81+L8g6afkEJKFE9QKRWxWb5miVoIMz/lIexWT+avlw3IKNYK2XzBFYneWFhm6aa2jJQIq2noUkmAcFURro1fDN67K+UBLoC5GalL2iuHEfKgZjZGnqZli1r6tathSPPjAhkGH25oxe8XBESWFLwlNdS0qqUh9mO5+liZFMdKW5q36o/uBifbx4xGEODBWRXYGQaeFLAmJ23SC6gM/AhrOGKOvGFQ3TodeZzph1NyciOcyQpUEOnOSOq6t+TMsxXdnSR4bqtTa5cvQEr2q+Mk7QouUWQgBqEf/z/DKtHAl0Y+ULtZ7dF0rdoVc7Q5nVFPALrJetk99erRrF48ez5BAlwc2wfDqNyJzFNZkRvvuYiVbwFndVrrbBQcomXp4idNBaOOeHuVTc0E8EqokPbfDbI+T2Ju6WVpv+I9Mk9ptmbixWPXM9/2mQ0IyafAHNR9UOmcZBOE2LVxJWtLQEGHcNaaK6DCgy2FxkAJb/536b5iDUOI/3nAQ0kKZpHU52BEaQyNZMpVGvSLP2LOgx26faN8Efb88nXX45c2SAA5n3l5Xstqv3fgMLu3gtFH83D/1QeCmXays2KWHchfSXbc6wF6yK8K4ECUzquqcgJvVc++jE+0g5SgPghL9u7KSGKH55W54hCY0Sfoyues/siwZ12EgsGQeDRU6N7sVUKJTERUVJJFsDZl96S3ffAncFEz7M3RJ79iBxRv5aU20MtFFO0i38awE/83NH2fjgfYZyM0BSzJ6jgnMGYkQJHPOA/tChNLVTd/lP8Ophi38u7YbUFD2Q1nyyui75UmQtYSlkfn6xrxLpSypGKkm7OrnFYb70WfIDycG3kVWaI9qE060X/e8/Vq/UQtDklY1YpX/qMGned/Rr88VzKcqK5vmUjfr0AxnjqN5SBiArZhTJOcnVxiSFODc8Pyy/otFdg/Ar+RA937+weWHA0M6fFRfbE/8X8sgpg8n3d48AC+u8CVzylGd32oLI7UjC2t6kB2qVnyDKO1c09mbPCt5/nuGRkq/njtIPjw0ugBCm6QjOny+y3oLo06kI+y1Rmls9KFtN46E/5Ue5U/7wwntgJKYENmpdM7/jdu1QpeETb0aFZQuTtJBsylBXyxZD+vTcV+chBASdTaHPGbskdAk82Qk334N/AP611Q1UcRMQGKDLZfrQ+6B/acqk2rv6Vw0TYKGI8kcrxxyl1pIknmiRcS9oVgEzyL6a3Te8Lw1G0vycSj/Q1ct9tvjCJ6GxCadzMFVuGEizEgIpjvySgUVVwqP28MOviBpYf4PVjxhNhczBtA5H20J4Wme9lGANqgfRAJ8qEfIHsvL+Ns4SIsTnT3S5bCUQYvPCmDhGL1p1/esmELHjd+84SgxjobeHMySt0d4mpCjZJ3Ffe8cEJpa+JC0RcnEjOeirXYvVhcc4pOBsKwQGRdjbcx8xGReUjIsyZljOh2862a5tHIN6DRH5939frd3chcfaiyve2in2xQEIKEogPM9h0AUTmSeGtWvCe2Q5nmMgcpKuM0s8fXGbJ/XkyONmuO+83sn5EhancvB+9Tyqu5+tg9ndAOK6B1i+vligQYcL4zzvXkH5iApnDyeE37jLrPQJKGX3C/tpjo8IZP7UrFRNT5mDsnpdTVMu7nNvKohkjUBSQf532jm3PqGOQWvqRjWpLJJn+AKxkFMk0xUScnTbvs7vH3Q2NTKBfl0i3g9Es6LZCC3sDgei+EjJdcmuhrJBGM1l039cAI+TINIUnq2awmRVy30SJiDqbH/BU9uD2c4ZtzjATzpun4C8L+fdu+42jxtNYdN4Wxr1/fKoHIRimReFIlIFrRQ4thBGlQxrKxF4WDlMWl5q+rpBGqfBZ2igjFlc7Y8WtiReVHGrvtl+Ta3VQfJ+1TD3KFdA640MNtegwZAMPdHKQ7mwpDMVVweGda3LYhkaapiJ53gtMJ+jfwNpd5o7zSFAW/Ck8wVBZWBKk319tqmUEtGvVEG/Q9G9IILPN98mVs8QpLMdarzIfGtAB4Vg64sgY0vJSYgdW5GiFuRNT5qzoVJGxfFnOobCJxaWJO28nDFJ/N+Ucth2o6U7NikdgLImYAo05CpgU7noLdMDRDqJFzryyEqMAjaXcGM/jsq9ho8fn1dxXK1N9UzfeWRGHWYOyW7DPuuIGOThTaZM0hoSuZ8Uz0/Q+KYIidt01+7sdTpdud6PUda9CEz+X6W4cPh86LUsKhcDpYVBMFZX1suX3RR14vVSETsN2yzkP4qC0J7CYK8MInqRg3XkGqUjrbmokGkhDpAgZyBkmDnIDbnRxNUNTA72EswR1YlFEpYeGbjOdQl+z2gTk+j3u1BQujAnzoJonmIF3PJ6afv8eM2tPAo87J04n1uSE8JwSNrrmeZjt7R/UcjBfDTrXzEesn6DpOhlNIhcCFlWd2+BnDBRJp+XSAEDHOEBk1oOlpW/eYM0Sz6Dx19tPEtQrf+q9+uKUC3CAkJqpyzXOLmr5AqoZNUugkQD6BxyWA3zPo4U+ESdGKPC74g6EkxqwgP8psWOiWdmoHP4QZl0ICCEVQRm8kU8fN6uQMYZPLF6GcEhHEIFvhzX2YJCI+S1pOMoTANemM3dcVjzYzi+Mfm/l5yhI/tE2rQY80nHQT1tHKic11NIVFCc6s/25b/CqnjZ1i/Ka+S4GX9WK3vj6NliMZ43/r1YSoTFsSVFZWxG7V29cMDpbvLhxq9FfrTfDxslkP8ftfifA2PrifZDGn89BKnvmkifz0Xx8F8kaKKW02CZMo1xYG7/aYapwVURIBufdRqDWGc61kz5pwjgJYVlXkyNICrBT22IoCeqiLG33WyX1xYnX3799m3A9Mqz0yaBxG8S64SfBBd/nQuegN9NmkdXNLKWAhL/9PAhjyda0bz3EeolEWWDbgdkJWqvk9vMcPn2xXVLP3Zlqn4UUiieBImgJP7kQSYSgudsoAa88gug/5PDaYSrOIpy4sn8k67tVfaKWQuxr50Xs5iiuVIvM7TVI8iEsOIrgY77yzVWdT/cOfXuV6066cj+4Ug2U70e2daUAaLkrCd54OVkDXyQLuQQjZmP4C/C4S56Z5sbTfhdBdBOgrGzIKYeY8kaueF0tNqvDqAjJ/FRfRpz70LTDE3r+SaaTOnWJLzeiRm09su22N1qa/oLac09pnxv+xpysDcHtXQs1EGncUgz0dwTWZonArLKYbljPvh9wBT0wK2rNlbFn1CIm1M1k/6Wneb42I8LkKSo15sYkYO3ghdki6ao5y9r9CRpwrlZ8PwmCI5MjWmiLRpj5kCeZASdWYbcr4Ra4HlSKQuRPviY41OxQ1LdNJzeDWSXD+nGm0wxzYhdEDbKfH2vnMsQGKzoj/3KZaCEmUk1t2Y4n6+hq4/UqFTHFMYqMKKK89oExghTdKaBrWLt+vmJx0e+kFXdS/aD0nAS4i4jYzWL/f39E6cUt6DVB1twu+zg0hQsMbDuoYR2J8Vvepf6hbzlBw+izgtonMDKCEGPNyWP0KglUHCEsUc2gNPvREKZaHAgLJt017KeHjpjSon2cebrt2yZvY098iDqAHMDfRVH2iXGGFQ0oIG7ssnqv4P0fvqYQOZgtxEtw0vLbr97x6mQZZqcsShKkOE5TIzK/WMoCgcykLlTunSUTrYM4Cyyzx6u378MUjKMQSOWaa0rMVq2lLKgzi4VqvbB2NQ3PkYFpVLpB7ILo7emRjKqd5Uz82ulYH4L3gCQVq9fPW8Vpm4ekKkLDT6wycY3b5xdEa7EPOMjgtujtlQL+qFeV4QMXALj4Y+SRGoRjvPGTbCqiyoVp7SBpw2AFX3keUS9Xt2yYr6o8iZh8gU5LN8d80HXUK3VgO9E6DBwzp612zB8lJKidFiv5b/t0ZAGpaSmZD6sZxDDBUcmCSIa+NQTCx5c7n//p02JEkpKtHBoqjQzy+5MqJ0d1l3nIlMUWihFgv+BJLfFaFS0pKy2BX87KdKx8iyKkyZi8GxJGzVetdA5eWozA4brpq5pB8YayN8ysLgj3jcGOltBDB2jG4fm+etk+Cot6wJP2bAqqAMqWCCsG9KQfkirScZ1uO6Wk70sjdazmFu+LV5xPjEBqf3HXQddyOOhw4cmKAXOfe5hEUJeeFcdOWsefFFt7euXfRElBH4wCMe1StVgfM5vBfnkEKkpGUVo1PUS4cnivK8Y0XAJ4wMjxUBw1yWkzlWJ8x6jVAoCyp49cnxAaN0t9nvoOWaRHf8H/fgYb5yXFx7Xfwo9QvRKDFzw3Cu2IRSWZRK6+kwfJg8p7pU0Zd9t31YQ0nZ8s1Pw2MQMyab60LmfLglTpfG0fJuWpj2/AW35WLu9WCyByeXR48HBlVBnQh/lY1OhBEmJRblFBXwdFxRwdYAD5OfWgl9YzVxHgJMrYRUYKE9g9Wzn82FbhWnDsPQq0l+Xu6f4FLHauaVczecn+eGRO3S6ifNqHAb4VKqWOnE9dmB5ni/b5bd4axMNCVL62q4/xuEh9O2fx6HYE6sGZz3icX1FzKlQvE30eCOqBTisVuuKNlYR73sVd9cb58QpY8xZwF0czuizL1LwojSCJGSyrWBSHm7/8kKCoyxBPSM0iMNf4xuCVVJN+F6D4/DrWfqFnSSAFYovJUtwApG+JSUCDrdwg81wG9f+oeNcnO6fsvDNCo1G778k8w7aD1WY95rK4qTLZxKlWo92Io+0VliyxKqSkBb7uB12OdGlBa0j+vDsd5d0EwrYz9RXzcCmFgjjEqUlGrqY3K73ywpWD9AzT12Ijz9kl411GLlJq8hDhJI4VWqLHFQobcQJIXafRbTU3usfGMDdeUpS+j2/zY6MsRzRsY55ROkRgvq8xHGlUCFb8NyPEDDHxuufn6qVtVRuy9oSlPOqm5BytUIt1JlpdNMvO0lEumk7B9WFIPzkS4go6x5Sa8xOlTJnKFZwQFAAjZQpyN1Mshc0Y3/Qa8sVZqZRXyOYTiv9TmcHXFz3S3sSlrXcaF7sW6f+IDDX7P3xxk5etJv+t6uH4+EMIqg7lLOt86XBCCAC8WS6txhEPFldVhDPwW+PKwWpEBDG7zfn9vFyYXLQ/RUUDr6rZe5dBSOJa1G66FB2uygyIOGPq2G/tcRULnNjrlxg+wdtIL4iB01TbIQDQML+/TiONDZFp6lyouBufdnxSTi68WHDRcDlCAkw2nv3VU8nj1B2ZtVwpmRMTDCs6QFFWbcoYCEyfqJ9QJ0SftxgOGKzSPPAXTZVVNodRaicVckc0ZGnHkLzVLlamDGsZLSdcXtiDQvozdd07PPGElyRIWHzEAoSNEXkgBSNMKwVBifu6R2ubHoI37UkiKhJ2S73bixej+ExEBg0q4xobKpyr8xeNWEWKkoP47lVdtFf1JiDc9HlafR+/b794HnhbZ/j7GZnOOyCKJUW6nruSJEiJWKa5pj+jmX/TCegvhNg+m/iN06w9zjnK0IQkmxgIF32MdfTUJ2IW//W/pHwTpZHuhoUB4y1bv+dYc7IBKV7GvqdfBGni2kSlpRKQcaeoTN917Rodkxs/zIfHkkPH/ivpyF8KusqZKPDofvJ8xKpagU6otI+CaAzfidCjtK/2151OvLLlZ0oI6k00LuWca0Sl88hPKVEVqlovjjhHiq3W4Fq0sr45pm8JyjO75rn8XFkDnXQyik6xzo5eSHaYG8bIRPqZSaUDwZKgIcIIRsYETf7wrThapNL544IXcFPanJnNx+zsuRyKzlyt+0D2iq7VE20p9a9DV/jwEOaovMgqT5dgmFUmk9mJbJWe6LDyALIyf9PaZUz3Bzg8iCCbf7fMGId0qCtaEQ4HaKMUd3XQMzR3pSVVZEX+qRMNjrOHDK6in4+Dh4VoVRqeja50P1uK1YCrhra1a9TEv1T2e12ayDC88hhVhJCxvKWuea9A4C8/QRqTCMWMxg0Vbrfk10tu1uDapOZVCEjOfEORgPIbRKRWfBDSJsrUrP135L9Q/SJsCA2871sTqWxhnjV7KgkVYvnjT3qgmfUtE2pqNX9sKOsI2KLg/Cwburn56P5BJTEwLZjmcRGdgQoU/SIsr8aNzw3loTpEWaRW/X20YuVws41BLn++zTFb8XV9XT83bVdPVZDMGsMMUZPwoCSBojFEodJ0NX9M2q+e8eefVFe1iyFuYEb/+GwmPbHWHtg2TtM5bAmKMsG+FO6jiXmhpP/deqQ4xJszynmL2uXqquDizOQpZlWEjeO7TiTcpkWSobq+3za8rEFk2H+WgoCpgGcwCmzRnKgcMKjtx7vXj0INRJHWtVTKe05zgw1sE4p5hd/XCOG1/aza6pFsf1T4hyCdRv/HBDnswId1LHpVN0+BOOdtFVXdlhfw626ynZfJaqGFLpMyrTNxMp+Cy5YE3PWi41EKTl6H5BARj8kMjV2G7+0O45OxsZYMeh3XT/XJa76UKb1EliRuzbezawaheP/Mim2iQnPupzTbWgliPXZV7EEV5YIU7SqsqRs81hAYzY1iKgY51Gt1Nni4/1j2bH6fWRsUURbLfna/QxeEQYlDpJlQP4AKe6jP5n31g51NyA0UnHh076eu2yx8duPATJwyZ8KddDM77ORkiTOhlJ7X+stzhCIuJM8TzqDdwwlOl72M6hyNaxt29sZjICQtITGnLz4jkzOX5WhEOpAX+Zto2u6sMLGCQZO/S2YCrThx0sSr2Nv6CpH6e4vsSNM27hUOo0GRRMPrZdh/55dNV+f6obnsckcZH2BYnTB2675/026rHcYwJe0AhQzVaSindL4niaDivrax9xcwK+5UvbrtCn7UXuRyZcvi8Z1G8vOUR4tZ0RIoRAqdOsPM3DryoLKaGLSrX3ulr0PLxhIjGp4uj/hQIkfUGLJwBCn9QpoBoTSucHAF3gSJ/HFrH5Ug29gDUmgl6TEBWEbbdqVL7BBB8vieupctY3Dq/9dvPAn5BqNfz0ZSOqL//6656lw8+uz+kf2W6rxWoPPabtWYycLIzKZfza08jAhUqpU3otpxeR/obSOPabZWWz9f7pvhmjpftBL2tAnUh4FUGRzArleKs8jmQS6mFkN61cYGTIYhlUT0ZWv1846XP07zSckT5DYjTCq9RZ5iTzeieDCzrfj2g+nhD3MWxe2g855u4HdeCNnQz42ss4V0KqRIo2klim31dF3+p7jlj0NXR0WR24H+a8Z9EbnCZVSQg/qYe7+PYH10/YlDorhtfZ5ngXKBjW6xbMBIM27hBBr+nUUy5xlHPmIWIUs/LhnE8JkRLIvr55+qlu2Jr7AclUXLDJwqpet/25FvLkWEcwDXF31twb8DIk+FtJIM/pQE9QZO9rcP7g7aooXwXUvXXmxdBZ2rJL8BScFOTFidG735CLoS5CmtRF7qRo/qyap5otsHj4ptNjhW7//oTASs0s/p9DtxAmNSh3k94JgOz0A/MyKdFA3YiEqG8SEXCrslk8KbcChSWpCxOPHVTeQM79DNC2q/plUDJ7FYDs1+qTRoiRuigdzu5mb/WKrvebrq35mEdvq4d1LYSoXdc2u6P7HRT/eoC9j3/IJ0ZCsoqdWrlbzcf9oqk29F5QUtePketa4Jmv8XJk7TJf6sbZt3AitZKYfF2z0d0VhKY2PGQHyfxxO3q/7jokRJ/RyNls6//uwUeg333NxcF1/VdDdTnF+0CUxJw6HjI4IUfCOzlxlfgewMhvDXuWPbd0iyhGnabkztbAm8KFiRrnc2BlxrsIS5IWl+lJVLpevAe5HHLmJTgB+KCQ6wZQaOgQ/rFbVfSLJlVVboIk6K3Qw4zQqRHGpFbFJF4uIByKGeXtcwU9E0TNiG35rKIQZMWA9GJx/Ou3ZzA5rc/inJ6d4CDlu468KAniStRMGPTXqy2lgHMM7/+3Zi0iHTPioipoRjirwgr5PiPUSa3TkQrrYbNbUc7WIwQy2kaq1LerXjOkxvAJ0/BTRndQ/0lb6WUfI593SsK50QOd6yv9G1AL35qHBy7VIeM5hcIzTfF2x/HkCCWch7SdTTwnv5jzbVTDuhxo8bp52DNjibUe8pze8Hd197AfTcG+tXTuOJ3yJONhJlmMg/OlB5DRNkKg1GU2SENzj5BeGipAv7NhN/257iq4nX6BSbdLxRuvHVSIeY+ZC10pf0SJ92WRHmN0v9TLrv0JaluR0ALX68PwLL+I1MNkMl/8nvvqIEM+5jJDhoRHqUs94ASsEDOd9ltI6CKbhA4L3Cq225dGeGcX7QtDibg4PlY/C4r1jPfwYtAVNPsl1pdmqNbP2RaGWyvWQqtUPVaHr6IVsT/yxwtrJxZzymeg5ZdCqaRs0bWm3UzzCz8rlEdCt/apGeQPfzW6C+nds1DHnE9NKYRK+hMqc3zMv+GHHegpzG09NfbPOKc0nTaHqqjzD6BVovlTlkUIz9vEs1IPKKSEVWmYFGkh372VVx88qZSO7lYdPNjuuqpZn72pUfxOrIXyIHWjbM4MDk3XUgiVIGUnQx+KXhh6ew+PIJ0regfQpx/bitVPzyu69UfGa0lg62JmvIESsxROJfps6vS6VS8HNFXofw+aMKOr3Wh83RN2pxDmoC5wr6btqxX4FClZVDacoitrARud39/DNguueaGxMgTyUab86XwtRF6TC+AmHfXuP9TNdlX1nsKUsfUayP2ZZjgos2GGc6SSEJtD22P10r34SJthMa7Hekc/zfLPMkqooo+1dVqiBPhHn8tZymA/iT595oLuPxqtfkJMxgsrZWHSK3ecAQ7WZ0lCecpN9b0/2o7+dX3LhHP86/m5gcVOUPbLcsO+twSJUyn0SpMWjgvbC6nRyV5XPyu0TxLgvUctpvf1pq2OwrVJQmAfXHfO2ZmWQqw0FBonnq87GB3QupBAJjG3xt003MlBPdPv8kj46aA+gZU280H0eZckZqflUCNYJx+bDUD4lDb5zX47coe53bWHrR/RBKZmGHJIeSd4mJGXwrM0WaadRxWLX9C3Q0/nJ2DuWfS2N8pyiIa/HyptBPD2UxABhGNpskJNRq7oyi8pxchy5CdVx8uZiDMj690+ocLzTc2Duj0qmwM4FXhahGdpgGKQeV7btUBYCnE3o0TvWLuLfmH3eMQfgCJvELvZr/YNdnMpxEqTx6Ua2pcd8ObdpqIkjn7P/0FJLGSYZ8wcTi7jUyVhHKrDQ85EGe6znS4aCI5WPyoHSLOtcW+FHvbWMV/fCyFGEidMSwN9T8fXf6HkEapcVsU+KeiUY3AmL8to+uMTNDEhIjRKzzU0Cw5XEsoxkR+bR3SHjX3nzigpiq73UF3qDgH62iHPsNZz0oegOJTCuKRVFWPGPFJJev1rCIgUHsFRp7Xk264AAKbULTNUh1Kol7SwwQ7RztO/tO2ajovWkTXb/ELJb/8usjGhZ00mrEAwfLZ8rSneLInsRTwgfCw83frA05XKGEQHzPWv7YeDzG1mpQX4rAv50hRUbUz7i7cNst+sFN2u8Un/lY9EiLqgbQj7ega8TRLYiyKfaMFdMMXBqJg1n6CTHAmrmHFj7Z4KliNmaBBNtfeN9U1aUGgK99KgS+0wByzM3vv9JYadkroNDNF6uNp768B1BBULEZC3LD6vVDseF6FemqI0o4NErzHFyTUjsnKdWfHKoXFuteOPHpaQvoXK51w1C94dCeIK6e/xYPp8QY8ypW0YvltWH0fKXni46vDfjcb4QfKwSs+5pNmbJlFcjfTWQNCt+HqhFVbGI1LKP2BEYnWjvR06/mblsCTX3MFmWIBxBhz/n+A0yJtysf/+vVq3R73e0DiERoWnCw2tvlJYl4aKW8GGvd0+1/T0/6zow1BhAIutq5aPrWOi3FULDAuPdE5Z2zJIlmIGEYKkV1iXRlHae3KE3qPFlFLaEt3Qr7hsH0ZT8VeGbDWXnxS8LgnZCCQjguq6iuwoGjW25vQREciyVLdn31pWcfeUdWGjRDMXIVP+gBK4de6AzxcthAOveKOoIKVHbtGCPCPMVAtdGXVQgi5aae0Ifd8Np1pYl0YbN0b8E4wdqimf6M3/SQumlObjYUgof93bjQM5DtoPC+fdkWBNsWQ4Sy09Yld1s9n9pAtZlNHH1o5WKKs833dtd1xjBtEctM0mPRUvxHtKIVwak7kX9rZ6Qvv7ksU6qGakkokVYW/qzU8Kz80If4XmIGQ+TvQEkiwsIFE54DWT408nUdvkegBhNZtlhSndBtIU0IK0qBiHNwZmzQ5+mPwwedqyIJ0K64/gSyhR1Qn70tDXG/HSMBPH2kCFSRUF/gn78gJunM1J4M6DKI55OadzAF3KUniXtKLUdS3WuOxf6o5rlERlaUS/g+1QdwPRoK6Qep/EyyREUDTnQfWcNk0pvEtjKJyPqwFIHezYgTyHBfE7eH9LU/dHa4eJIxRkHKQnXMyqeOCYC++SYsBgIWeP9ZvKNuGLFCJe2x3Pnu6oQkHO/SflSctqM8XTolcdBmea8UPGORLCpSkTJwTxrl43zzW98rsDpxAT4pyVQl901fejUT4F00DbVj8vBPOmUuiWhpKpqbD/TXuo6beYbAq2uDsAEzIK10Futhau6lU44V1x4bpEf3Hid3BNAQfVWgJFqhU9FWtgCtv1un3poTq/VKUObJnMAGS4ZSLcSzpEKpuWbHa0y86OdprktgkwbS+QME1CCiSuub3zZ75lLnzTGXKKC3fNE7dwoPrE9hA6V9EtqwwMihmsyTjOalUZJOrP/VNfVgvsRSnkSxzpQXDBUi/fLpcHyASkABOyDAtKkL6cm0DhVBC9IJ2DptiDZGQp2vVtepefBtCcTBkDoHpNEeBz5ViX1+19gxWPhDqVCmHw5eVsbxIhWtiWZUJ52ng1NtlHoRT1dbS4V9f1EvP441xfhQq/+7GUfG6EZVkilRvqMzvkuqAA3MGBdBwRr6tN2/1K+y1E3tBi4ny3H5i4UkiXVGKPON9iH3tZP7HFCEgG72pmhAkOrWa13qkurip0yAyumLtjnCIJ+5Kq2nTqWHPbbBbrqunOAEWI3tFfQVB9gDFaYbGpOG5YEmKBV3Ps3VIomFRIuicfWIoNy2PvKTnbnkHbhK1pRsL9rxXLwjH3I+X4mAsRk4rJI2U8SoB2OM1UIJbRv7gUcek2nbB7METG4Jw4RNDYptleXhjSbGFfUpHkpjlUYe+XNbrua7ZqzBXFRmirDXjG/cPqsa6ft80RoydMIqeM54wNeN4l9EsqTFR2bEzRF2w6jQGCYWtG+7SdW926jr7UtHkUppOrOBr4MDDcjBDqJS1Ki4vVILl4wfkaS/bTJ+a8Glh5t2OQqXSmX8cYAboyYXuWe59du2cSyuFMPnI5qHsJpjShM/DnfkO509AueaAa++UI86iCdLvNnAuZPVUSyjPlrt5b4FEB775vmOJgqBz9/BcPv+0rZ6cC++3p2FKH9LhBnfXnbqDOlkLMpCUNAzlYVkEQpt1QvUg/JE0nAqwjhNxd+7Kh9P/5mFYX6HMwMyvkjEC4mZSCJ7nTzG8hSHu53ywgEGGoOKUjVS2BaeBq990ecGOPdzP98cI8ygEknBEaLIWgSTnvgI6DKtTF/qclG5XJsRj8Df1vGkosfS3lMBE2Kwzjg3uhQyn0TAqxyTDrratV5LSxIWtwjKkYwsIUVJEFyflpPlW+g46oIARNuvPOVGDAeTzXVYdhU0K1EOVRL5slJzP0ILNmDW/aBb2RsAjnQvxECi1kPpBzh8BLHcMzKKRNWqySDoHbh+h91aHmTksKLV86ivu0j5HIWGwW7bpf2xHzIYRpUMyWCopXpoaVucS8t4uB6Q/UYOLMpehSUVVPz9UU4qiCVKy4Ge9VseK1SHhXatily3qDC3e9eENPLqVU+bRsuejowWbI7FHZEqchSlFMtvXKjiKGCmuTVqRFmXEDZ+DrtrMeypBBdCLxrhqnB7Hbb9Eke1jtTh6bIgjiWDC+yWunjhRUyJsUmY2jZsGzeO3kyNWv+mF1hfHKSdwqQzoqADnNmHwYfilkZYlr1fXPYM/P0HkMoQTesl/P5kIa4ja7mpONLIW2WbI3+0Tbgm4SBa0zJAseIatX6lvYKYZvsILDLsxNWpZDgdghKppzyEBTOidc8gEY7iWQqpACK56j3TKGUGiapS4HA6vzNQfPi8qe8VyX0SC57RCpO5Q61f7s02X/Sy+rR65mTBYEKornSH6cTAlHszQjqNzXpl3XOzbWubdCjQUoEh19xUH3d/a7pSEINT2b5BW8LgnldLLMaQ5616BmoGzBeX1QQbG09Y0zCH2Fd1TGUtJeZ1CkDMLXpGJ5ENq+6JrdDsJbImdn9ZK+sL3x0Gt5hVhqns2quPNhl8hels7/w/l/8UQqh17bH9uqWmzHDgVvVvCde5pGBUrVA0YbyoZPn48Uwqcjaao4TpzMRJ8dXy8+VlRbQZQ9SczJ6G7/+AK/uZNedJBrgppTT+HZnaNpYlFGvPdWmLZstggJeUHncmrn+Fov49K2W31QJ3w5x9SkJZ3IS1F4gDka/bPTIvAXvZcgvp3mL+fDWmSsGC0rEn80m7EwAhIPHRiv3J0XbSlxJ7FeDhNwf1A0h+yeHxHGr7EjbFKKrZ0Gj/UGobxl3e6XcP9R0bt25A3ya2H5kKxT2eM045VWOuYmLcookeDjhtA5hSpWLYlu9vdVc6h6zu+rRe+shYovVecV5W5FlPqbYyzfR5QIBy5UIDxD//9FlK82S9RiR64gOsgQoODB4ozOVOnom7SkPHHHqaYP1zfKU0X5Kn2qp/26R879XfKmmTtBcN8oHXlTxXRvZT7+R4eO9Mca8NmsjC4oBkl7nFID7AOtcXrHQuQ/89lq2H4nLYsZgQj6JM4KyuclBOW7pqao2Hz/3hdSrxBQQSd4RiGBlyQBG6PNQST9cn+PVyErMkV37bIdUy8GKOoRajZXgSzkwi92xwdHgnUeO0od4AyAg7+wWmsGtdYanjtW83NTrerm6eSBDcMxWAi27+VAUCwlTOfZ8MHetPb4HDCDViaN3qxtR8OayPYT+qEnNpnalyFJnOKGj29ZTOQpk2FZg7HVBc/B6aGF3QeU5TCLGqxb++z7y5rqPZvFneg3hhj/9fwQ30QKEbuUiE0np3Rdg/1aDDdM9Kbu9ut126dt6GXa+dg0bwsCfypOKr3iNzHzVIfFiKiTHWp+q9YYclB1Pni7y/n+mwpKtjTxvht480uJ0hBLHb+wg/BeptLo3X49cgD+B4Q1LM5iTtmpLCVUF8ZIr2Jj++LW4ord5NyKrPBk/9GmhmRJSN/J2Ba5bwjEH00itspTUZQFf5u2ibbmJ0U+WP9UE6vdX5yjkCWVs6x7zDVKiduqcF1M+i5Lioz/s2/426RUnMGxCWWUm/y6SD31kIt/r2vjwMPelg7vkURtDf/QPn0Eo9hSU87gK0o10/29IFCu6nVND9n13dl1azWoijDRH5PP+epmfHrKYSnKjPTumOrA7fDc5IalrxbD0GBFyf6yXj+v6NycCgEkITNXzUrAXmZvygw0tzATJ2P6BR8kBppmBSsBTP0I3rH3xYkRWIiGNGfYM/rotCCJ2iYbMmxuLTmxZCpPeoRer5RQPXswQ0WI1o+Zq2gVb46EaVNmQmughP52jd+b0hOLFolIj0Mf5vi5gIL57w8y67f59AYoK6R1SIQuRyrf0sF1LFkKVDphN4tq0dew6AlC422/m6paaxOK8E69D2zOn8qFagpozpPQFdZOTrYEMabuHjkTcmXsh037lxftEfTs58yP8RWNOsGqClmV1uM5j+3Js5EuYpHOGIV6sV/aU3QHBwWLiD/6hEHYBs0uZL7gaPfKxWvKRwek161VbV1TiV+DypDTUuAAsNsyf8fxL6+broIB76RIi4Ns5Jg5600jNValZVVJkU4GKu/pRLHyVx69b9fLoef9uprfnnU/JMWedSNLytyY9RIAYqcWQYkt7Mie1/LUvkqTvDRz3OKUQ0Apy1GJyH1Z5SFOts9MFlM0WtJPWztPEjdd7SpGWIz3KA+qkCxR1de+1Xj/HQNTJVmSHokBvn2BeAto7NGnev9sy/wNqtgXFkXxCAykeUhnMuUHzg9KgceqLIk2yi3pEaIVjhybJ8aJANqOJFrLt9V6Z4VR0MI5+9fd2b921eoMPLKQ/lE6x9RJeVGpLEooKH/W271VskYNj6qk4n3p66R31YGqgnqWBGYCcklOTKC+4+tr8fdz8TzJk4HVBO0OECqsYFJOj9P/QR88BMHDjkkzIqG0KInmOd2wPprX6PyhA/hYr8/QQcWDB1iDddxyo6crZmof378syLyZcziv4R1vlUTzHHJbA1iGQnkvPaKRxPVMzP7q9d2Jk6QgyMANXg6ld8Kk8L4kEskpY5MhNK5fja/nZLeK1FAJsqkPlQyi36whcMMyYILbDdLhzK1ihU/JpsCKJIpTxJvKEV20DwxPT6FYQbswEI1Z1XRiJ8dm3YH8Yi+4CfEyMcNaxt4tD/Wa/vmRfHOq6SRSxV+DIzc64NAqak8hMnGIdKPmqY6XBcJnSWJ5oUdUcTjawvSihSi5KvKR3s+vwbIh2vdKzxJTMq67ZEnlMHXmLmMNpmW73XFDtvy/YAxD+twlHylfWZDiSKUSzqmay457pV/qGq1S+kXcfHcU1QqikQ9HY68yhCiHyVLuRTDQd6LVSBzHi3ekqHO9uKnqM5WfcGWuF5AphFnQmC0TeI4wPfV1bXGO0mxYjpt+86TEdm+SDCcbiEd3175ViwmKOEhfQCdzCCsO16mEayVOoD2R/wLCzrCHTFj5fopoZp2GMUw/LUJmRzYC+ZiMfFwkSivtSPw3oJsC9sZOCZSzHfs1vuvQzAGyl802p9icIEeQvuj2kcCR3aYSqXXi6J70uO4olbyqNnRXqJBM0Gpfdq1DVc3IAAZJg9uOja8dyadGorRJB3+Sc1pH9HlfczOyhu49PbEv8N/unOTYDP0kpIusba3tuVdclKQSrE0+KI++pUfqQOk+4CQ7CtQmL46kCX/XlwiBBmlW4vcuzWBpEquNUhKrP1W7ClMANh99sr2APLqpHE7+W9Vtqxd/3laGFHF2puUHxeVxJtG6TFwxYHuTTlCELj9dEqtQ4xZ1UVt3AA9PTodIpObMbvB6OeEjZhKq6XOX0sGBvyXQg6DvMeaFgxNwCvwJL+ivVtUTGxRNFPmDxBt7IL8P9ZIwYUmWZNw+sVVT9LFeUtKfZ2XWO3/bM15BnPAYtBvrEB6antOy5ROeSaguS9V3bS/qzf9SCbvB6aAKDbkIxkk3lvi1dBiT8/3DfnvskhYk5d6jXnydZPzLkS3poYZ+gKWjWD3kS/oqfKxTU0QTq9T3HcUBtAooz550J8Mck/myeRWIcdkc1RKpg0ncNInVQL90TU1BIMuKQg75CPF5Wx06VyxNuyYGeg0hQAkAAHw4JTxyjnhJC1OJNN07lqX4yNMbik5TGb3z9T3Fim6meotDNWH8VsAFnhXHuMSihnGXRbsNcSDNNJZU7UZI1CskbPUz/cUf9NcvVdOcUYIYwnDSPEf2DU0o1tKSjCzJJHroLrGk9VcASlijBqapMAn7NewtSErA6on4sfK0Hhe90yTNJX28azeHKvpP064f25ftY0Pfoyyiz7vdwJZ3zWTbDZh2l4O8SnsEsa85qNgoRRYGPWlb5FZr9k6tAQCHiN54zm0rAHQFJzk25TlBa5njNvJaElmLGbRaQf5c1xwq2w3QknTKom+1xcmjY/KFkpVn+hW0Pz71nCQEearNnDQqDwUc65JWVibD5LTewrHoS80u5AheFDN3I+2qN6v9uvqOlPuIHJaGYAWVlT3yqa7gTXHUS0Vlx5AQvF03P6t70Pbu9hC3p19G23XOWuCMRHU3r9206/16P7l5JsQ7wRoZ+XDyXJrkEsxTlM8TgXJANcDgpSKhPGJenG9XmNJPs0wT0vJCd2LGoZQ/nYRyulwSCT6vmx+AemPURV+uVLRd3WONSq7nzuNBeWifUNdNPl0SBJoq59RjM/50EsUz1KajHg7lvt/4jKdGD0DmvofjA52GsPosvMQ7H+AvJuE7ywZdgT+2dIzWQ/zOoOI+mnfZ9xdwygkJswhJ4FQ+p+FecByQ4F2kQxwA2LQCfKyjeFNCrLmWhvc3ev+4dzMFSSkTMvJi/4jZVCCXyM3j9yORyP/A6q6CSizj72wx0qe5v1CoCum66WJOwDYvGKAq69JlNhkL3AI28tOqqBurdS84jm8NN+SZvzARzNEhe5XNyswbvHKFBPBixFf90TDYlqc4Z8ZkAFFs9ruBsLqkvds8nkyZgpoCwHHl3i68we0vJHADpiC47nYNWN1Ti/IE9eWiWgvD6QNYVk9P9RQCCDp0WMVbeJ8RrngLCdkqi0eOnDuquCsM35YwDEsxtLxY72vMlPaLvsSctQcIkRy06a5vXXzGC4nZqhgImMNTAuT5T7j9lNzYXrDioJOoaO3FO6nl0iDT0JKF5WdgXbQyid5KldlJSvDt0LHFWpLr6DPKKVc2WH8SRIJd1RcJo3ZTEPwls47BsydLYrhChjkmMtwyPrJaQ/RTo3m6G6Oo++HhV4A+DkfM4yIPSVbMrHp0xnsm8VzHjiZjWeMfq/095Zhgy6Dw5eaps1b4m5gTqx7tt1ksOE5JMKeEddSLZ2Z/U58leT9QtcDFfiA+6uxcvhn9XUR/R4lWyMgws4RMT5fAfj+J62aYYwLK0LNpc+UMniSbW1tFj3E6UMS/HxXa6ZffyoynX0pCudFmuH7fv1Ny+b7d/KS/+2kHmLf7Dd0uJ/aJopPixPfv0+clCcLnaO4wewUQkRQoCeXGFMptzwYKP59wzymJ07GJ+jdO2rpdV08HKIHquqV/tssrkRBexm4MZ3tM79qXNdT8cqM4V4ou92KETZ/yv3vuIE5TAhNCqVLpnJl6gXCpJIyX+VANfGB1yi8V/56iyDC4aVhzd9Ru/oVbZ9DE0qJy59pxKh8W5vAn0DmlqP38jBKcSgHuy/f4Iad9tHjkMcVEb6AIcce0GA/vepAPKIneVB8Pqt/1w4M1cqIrRNkId4CmaowDK8fHnE1DWP4m46GlD8CMnoVSw9IGKYvPXUWHiPleOT2sSG8xQx1xFt7CUZwyq2klHhSQylmcB8/mlYTt0gyDQut6dUsJSbuhSqWgQ+n0NtwD3Dy4x2T0xAXBmK24j7c3wHskoZvWXkzZJm+hwIhyKKFXbbkZeBSorOy9Gx+nLAmhYANW7eftpnycXMjOYMMyQcIBewtUPoX79/T6jnymdttl9XTU0sVhDKoL/BLknDM5XiUW4yQZetwZvgzGgUUBx8KH1Y67zF9aSr1ddJqTjw5pXZZzd46n8Y5gSesqkmSKD6BKAP7zCRttCOCU8UtHuKU0BHam5jSZOG47SiUtRA2E+T/raoNnC4kttGJYq3Y3LixfocFQ8GDAB+hgJKVjV9JpTJKR47PViamcizHFybKIzruHdjOgl3vzqcu3Z86E+SzQeMqwZ6hX2gPX3jEraVGpG+XyuIR+SE96SUAexmOxaB2S6rl9ZLzJdAZWho3lMl6Qb0KAW+8oldDQTCTlfrukDbpq6SttIViZR+cIAIMcC44+/DGnU8ssCBIAyTP/6F3zTVOyIKUmw5Mb9GdaJKpFRO9cey9t01/jFEKs1a3vvDd9RFrrOJS0KGPUFNZBO9UrjBRwvoO9XdcDhF6NN5+znuer7+J1lppikM99OrCHKkTQqFLBplWbdqC9/cXNXE/9Fofwc9WstFfBQUBCdhZPpH0aTFTPD1R3n8Gjkc41JHy6qZOq1f2ftCnyNFBvyD9K5WmcsCizLHVueM494gaOKA97+qH4MbBpgac4Tju9r7bLZVvxk+ctDJxn8lkqJeo2oVJmWT6I2L3HH72KPqzrhjXapwKRX+pdtd4/naK9yxCTq4LfW19o4ngpNEpa0JFmze0aAF2qkZQrud20yfm1iNzCdJgSh3JO/VqaGa8rG9alxQrPDniBoQDDJc0ZmDdiMM3VuEEGqoZx356nLkMtKUTKLNNFMZHR7jUWAE6OJiycke2jbV1OVaPCVHRm+V4KMUrIlBkmmhPB8aq7b7e4epk61n5gYS0KCbA29VkGh5R0RT4r78MnXSJ6nk2kmaonOuiUYQK4X3Dj0kpqW3Dl36UT8EbhhfEhGHmjJJgXcTkklpuDNAPoc0TXVBg0jkfwqhe4sB5Avu3BgyfkyqwojOQpl9UL1UfPrF9f0HLGKRyfLFbUuqjpYx353QW1Ayyj0ddt5isncRzjpkm3qzcDynQGG5fHeu0YelsrF1mfYBfjOKRnqcs5lEeOIC4cy2zww7xqXzj9RnJAMYCC5bFfJyiXFIeODDpNkBB6kc41v/mrCbsyU8YFgbcsZHTR1S90opVmrNfIpPCmXax6yehpG7AMiZI5awl5WcPI44RSmVGaKmOd2/1PTsA/tByXEioSIheexYqeruD2EYXVxJ43DlIe5U3yNXMy3iQJ3fC+Pp6mWLgg5O7Q6eqrWvvh3uKbWlzcMfCrDLKkTecGvCmvSyK4zuN4GioRCPOMKxVrAY+JTm/38ZW7BqNNCiLsgAwb+xu4KAmEWUmvSZlO85PrxftVDSxjHE/n8r+AvwYJdORzRQGP5IRdSfswSJmATNWAsVcvbS13Sx9laAeKyLZ3OhDUX8rnsNWM8hCCZWZEtYf7W2x4C5OdNPs/IvV+j7JMY5bY9LaWmLAjLMvMFAND5rFm2icMI7OsKE9Neq8PHVqpNoB7jXqDdI54x3wvnf2OEscNXcARzxpVOGPOkgRx6p9kpM64/6D1nQjbEtLfZsDpdBVbJG6QCJQWIvuehZx7YXuYhx9D0bIs4Jxbq1IvQSbFepJhPanUK3cUnOptBWF0JqJkWYRztG6cedMLpeHTlmCeh9w6W/l6uQMGq0mH1Yy5A4jfG2TXZ3lu4jDv+SA59Gy2Q6mxHAnepfjcubQN0toL1rAvU+srN0xNuqb1uLepNMiIgFNcr2BWgSVJ3KZYMaELgDnctdszutLRXXWggOBW9Hb9tGX86fQ4FyHGm5DF8A8FUZkkQrWEIIW0KuxQkGXgMsC+WUNg2/e8gGD6tqrh83NUKiVB+uy2/vadoBwLcnEbhEvpBjrawhX+BmyhqZAmM8AgSXyqvvj7BqULj74UAOExEYol4rcs6QaD2k97oKd4H/mHN4Oewe+GJyG+e1aCwtdd5nW5sI0okE4bp3jimLtbIqdtXMieNeMOYX1zDuCtvXk1pawGbMQpbOmmPmza9XJLty0DFL3d7g7/BIq5MHMiHYBSJcKyzNN8oOxaUZy+UELD77g1SL/le7t+POoMBllYFFbPwNfIweshHMs8RX94PBpEZoJOZR6P0PAfq33XbDiJ9CLiQ3omFv7my90Af0uEYZmnMI6Rrvd2C7YXGn5nGR21qG8POM1vr4B8SEcw/X9mxFiRbifCq8wzIAsmzWWAhQ5n4BNEf4IfM4xM1mskuPOMzyB59KxkOV0ffCLBynJZWaGSUer2ptru6DcDYU0hpOd7vtYyzhFQffkt5yHCqswzo8rJS3uxpyoWpukY6dIru+yv2o1VV5mc6jgJ8bNU2ZwcOuDUifApc0xQJwoUGESiatPYmg5+kq4P6N78o+JIpyFzNwuh9Lpr8L2XiJ0XA1yRi1c0Z6DPxbYQaCahNhKEApe0fUlyDHQJ0vHN50DVwAsnQq3MVVyU04np+3Z5Twn0WQIlqvdUO45bgX+bd2pVjHxVJAq3RPiUOVrwo0N9vXgD7SJUknT7qbo9LAAcrCFl9ij6Ib/SnwuJ3uzZNNfkSoRZmauiSJ0oXrW0k/cd2OiGbh+EPGWfKKnDZTwccdGoegvDCpbejwesYCKEylynglKiNPvpEL3d/ndPx2d9liC7/Q/AeOA2OWrMhm2MT1reaZAmemH7XL6pLq9KwrfWrjOJljJ+nKUxwwsY7bbv1bBPlOLt/7uvgTg5bi3pJCit5H6pl7eHgy78ytyUA24QvYeOchGwPdgigarKqeKSNW2LPn8H4ONYcykNQeeVcxJQKYK4UC5zqmROZVjet0xJy4s0iSwAzWpACciLe1/2nZGLmMUhDWZTzLUrMj7rEs7pj+Dalf1wnirdZ8TQXOdWebzfrcv6e4MaZrSUIkgPPZ+t4fjbuWhexCN0NVsTsS/SWZHC2mbUHBnkc36JgQs57cbKjfvSA7w0wsCksO5kVxkDd0PXEMwBYHV3I4mzN9UTzJSOmU1BRBRVzCloF/zJjKylKHx0BrgNAG2uMk3fbTlgKG38hFpzYxuEp/7lQf3dkuOnDz2Q8gJLWaAaFni7sq7St9WP6qE+wyjztnkYVtU+1xN8PHBZIUPVmXEFhqqJsC4LIAMG/BlIX9dtV903TFADARznikW1xiCG4xFK0OcDekl7cwSunoR0WeDSTQpx+jeo92aJUZFFqI/Kuh3lCx7LhjAtgcK2B/3utrQmF8yLdESzoOK3iq733fMKPfnUNp3dnQNCpi8WfC04FSIMg53y0y0ypJrCwCxSyDHZJ4YeffoNm6VV06cSMI5u9w+U+UJbu5847WwQPTYrCoqXmjMpr1kR75QL5AUDtfpAQMUSlQV3zZodG0r6E/UqiAIcWr9U+8eaQtUfZ99QmEPv4KzIwmAoxRxWnjs7QsEsssy1UvhlWVNRwG2ClOIXv8uPEz83OmLQpp3KZwatKLcCAz5AEx9xieS5LqUk58EzZiUiUVsm2vqWC8ai46bFWHQh0Dh95saBUpgI67IoaGl2g1iZd0M3e8MGjnlOsfJNBU5IP413kM9fDed0SK5pVaF9uSavTYJ5kRWDE+96g7t3Tkt7wc9DJllSxNpvdtFddd92r8KhZ7OtOc43hYFJNyZ2eL09Gif9iJ6y9xQ9Z/qK0Vue8kBCt6Wq/Yabc9OZqgkRhGYWmK/FAwJfItTLQqmBKfcnN1QpR6qXXAZTVqeONZm+tE/taQKc6BCFcWAGSr/aIK6dcDCpyC9Ld6o6DuOUE6WZAcnxeSQ29gq8nlJzn4zfOWFdFiZ3UYnLqRuGMWYGa5Kn5FVS8GCoay/aCwz1RLiWhdFDpfmlhsAwANYsVxnTYzJB6X2rN2yBdapVEYTOVflcPcdtAiFawgq0L1UYXiaYE1i5Wav04cF9PfukZOKA18OZP5rE7TIxontmpdg+NbufZ0B2oKv6ABKWbVrOXrJApUi/hSJ3m4VqSbdepSOd0W/1PU/es5zOEf3sDWOWRu/IK4lNVq7Wy6/gE6VlXYmWCHld08eiyoCfDxiUxYDuNd+rX869QiZNaPH41XMVxyIjy0mHlPJ2xUPKq7pDbgLTa+BQnym9lNL3V6pLIZ0nNgT3Ib248yTkS1pWKQXK/7bd4DmSw5flghIVYAXtl5t7RUJgn2rW+JqrFGFdQmdcjRgWtwu6+fgaJos+NJCshPgBxalXa/jHc+qwBudIKJfgN5aCOkGhS4nbwwqpW6kT1AUPA7+CyiM60e16KsWYFqFSJ8ZP18FJEsalSuLSkQaAqFyPje6iu3rz1DAe5btnOJCH5EcA5fm1DnkWJ2RLReFbmibv665aLzFP/sCKtQjalJ9RYQkru3GLCW/uyRtLxWnI0NIihXypLbryQreklSm5+07t8KrhPlOemOg9vR+UoIzIgwzO23eHI0n/ogyRzcnmaIPACyVCtVRp5vqDH9Bzpqh3oDcLY1265ZhZum7AfmeZu1P9lZBHRLEijE8vR/GplqidqtHYErz3FY4yLamhY1IiPP6s1+Pe7iXaTC3v4R/nZ3+0L7SmOItfTW5OhFYJXqUaHjZYfOFxwGNCibjV7qQ7dVi6PtdNvWrvW/BoGdsx5RCUIem2toN5n/QZfzqJ3mxfcKzwe1NB8+gnVeA6o99ARe3Qz6HSfIv/7UnLJIz7yUQ5XwFe8EGX8A2VSNfj/f4dpvf/aaGGTK8fK+bdLHsqIaVxC4SI8/+cnbPTT3VmCh2kgmy4h+qzSsRTIiRLlQPB6JAdAOt/fqlR39Ir1GdHIzYKC3lyQ36Kzy2CHt14ll+FUy4cSywpG+W3eFHATKfEn8+VgKvXuxY7coqlCKJ8qmxOvZoTSiFaqrwY6pF+6B6dd5DQg5jIh7bdOTE2hqWhHPcOCsNSAMXXzpeZIJ8UsiWa364PQAkJZWsfWNhXU9n2nn4yRFh/gX8JaZdaxwOvqBBvkMTtAqmt8Cvod1Vw8+vg/pJT3n6kdyZeDC/+dDL0KGlvomSPkgRv9nYY43Mvm4cHNAOyhJE5O0pqOV72SvFzNvNBdFQrfeoD5/CVkyhejKVgnDDsVYWmF62r0NHtqqLtAs7LtSjOO3p+d0dDi7gIJMka/6oQx4VnSYWTm4nb6cRbHvQCKx/ZJtIwXX1FvZ0xjNk3cOIzJaGb6iWH9aR/u6dXv7rHtQeLyKKHrO2uExfd399DWWToKoUpn5liblzBkxPhWCpIZ03Lt3d197DnFhe9gLZviqKp705W3aM9SadmxSHOA7lVPJ1hEyZCt8TzKxW47Ucim7tuIY2Ro+S9xblC8636u+ZQmh1ZZpxPaC3JsJbSPWuW7UHr2bKPK043pgDswdQrCtWHurNvySknrQjBnih+4nxZU4H7JoxLpfTAabYiYz1ygPbO0AejZf6o1zv5dN8ZDzfR8dEhT+4s2Jvhi8K3hKRbNuAGIHLkZIXYhPv2udlsKG2SR+VvwjysObHxt3Bx94VsqXQ2TATuLLrTOX1SRakDK8qgw8T+rF7xQ96kYljSMDjhUgkVHDdvWPLM/lxpus2qxIdoHZs5WBWDYIVwqXQ+qOnDnfKZIvPBunv12jROiuK1stC5mUMLcPYmdEultTNB6Mvc2zUQ+owbTBMVoc9GKxkciSsbo/YPx3qasQmBfBR6zi6GO93CuaQrN4ifD1Um3buneg3EPuQymBzvVvavZ8ifQSxmv308jMbOWciMEPodfmfwnOOlxHETywS8emo30q/MUp0eGftd1LtVTRHqyNgvDtmnknMnb1sQMVM4l8qk2aCfbd2kB5FIQAVOqB/7rn486euWWR4QobKSZZp9Nn9oVgjlUpl8GOlQhKKEzgJ2YSAZXVYvg5T+r2S9Aku5GUoD75PEcDPifvUOwBQYqVAxuo/mTgm16mxm4BiXI1BTEI+/nAM1cUNH2JbKUAruTtL6eUXV0nZrAR65YTm/VjImf6MyqN+l50SjOYIL1VIZrSfSeSAUA88E6pcpY67l1nX1XRSO/ubo27lq+RJKxg4KzVIZM8Bi6Zr9dw8yeM2dpMczuOpVDU51HzP5G1rEwtCpoDwwLA/w+/tyHiD0SlWmamphddlUMItERonZdj3Eydd0lspZGZiUv5kE77IcfEYp5YcopSVSPANZlWXu3ttm9x6yDGNl9iBFuLzgFMBXKRUMcusXQz/Mpf/vG4vR/1pt6ZvRBqV5Frms9xfufjqwS5n7Ofv8rVycpjgzvB/0tShxjaD5hjmAoj/NtwPgnoLRfXoGid/D9gzS8y+ZNeBlweDxEG6lxnRJRKKhkQn3AM2TN/s8uAt/V9GKpisJBTAjQM/wBRMhVWoGHVpCBUT9o8v2hZ5KupTwx9jLrP0KsgG2DpnodJkgAIA1OpmLhMKo1JTSmEmRDWZQXe3XAJwqZuYJn/oTLK2+NtvqhHWaB2plzvAEQOhKhFCpAekbV7Po7fXm4iV/OVCHnfrra3YJtjkzwA1M3IRKqVNVTqPPpxaNrVxRljBpQ1529NTvPRwhnYagFHHjtRfTyTde6JSU8zt29yc4mbNy0N2+XR3o2FOFziL60hptNwsURSc2eiEr6jVFZ7SDEmFT6izNRwaj0ef/bx/Hqb6E8sx6TZ+OKpH362o5ttH92FrlvnFVG+bIGDOY02+gSYvSw6JMX9VeVHb+1/udxyo7cfWd7yAFZY1847yVCN84CdY8EhLd5R88XuPHjNPYi33Ha3IoFzvApQvJLKZRyA56QQo1xzznjqRwKHWmnfexdRe3PDMr+/im5SpkPFH+UkPKY1rWJkFRsseYzoh4p8KipBUp5Xgwlkz6g6t+U0afaUmbkd/R+/36O9zQTvs1RUhZm+dzTpF2SRK4M+O0Qz4xHhgVz65BfZ+DnYMGO1Pi5d6t90/3tE0+/F2QdbX1sJxjM6RCqtS5DCj/UwEbfMAg56lZUhCHPfMfm4euepIYVR0m5lBFHoRwsfwcX8vdYC0SvfMsGQn2odoQNxjA4OmV28uLMuEwT6dJQU4juZojwqHVlgqnUueFQwX3mrkX7ZZdLNFmOGpyA+U5oySU6JDmllUO8OVtGW+VRHGVDEqwb/CbWNSfc5MOOhRl9GfzRDduK2YaGwYunsxyk5ApM+r/fFYjJxWCpWYQl0NzAi/RDiCFBLr+35pN8x1cAsACfkXXD6ExMC3WR6sGiisVjqWmlLsPCG/3DyDqXbWwPgDwDtoYFgEvPa4rChebszd3ZxwqFrtmsd+dwb045HmxCBPf90uwJAnlOnZ6uU4jB2Q9OueZNoml7m8GOtrdAVim0VunQwJmZjulvlkuxwAJ4SYuxlxPCgHV+gGY0SLltgTFbobk1lL9b1BhTph6OqQTUVi0gu9RwTkSbiXtgROgoHcXry8oHVtuVkRDX+mXsr0hQBdtsXcz+q+pUCt1KfK4LBSA6TuUxTOjojvaGdBPewudX8hjhXS0rPqjL1amvEMSt8vSjbj+oDwxelttd1ZmIqWk9GMt2mZ/HwlcqDm7SsWbkw1LGUieX5vFjrW6KEXi3JJymCKBDBWvDjpLT07JaP9Yb1fVMW46CUKVZnOyi5jipkKsNHFsBk7HYgAFA1B6vqbbRCe5E0DwDwxTRwKiQMsFwTgK7wAAMI5UWJUmVoNI3sDp+LzcP2FaYqJbesJWlgtPiVELL5Svd5jzcgIXF/nv29oWCujv0wAKmAqv0sSlcz/vAe3fGs5vqXKzd+5d20ll+6Wjf5tqO8QqpCphnc45V+ZUSJW0mqHR1xf+vUscvbLaon/YhqlXEHytrtIv5R9TIVVSHDLaXXwgSR6jf9d7OiRUHWTRG6h1t0OVO2swGpRL6rnXTPPNL2VJaTl19rsD9gBDd/rz3K06qkUshPvvV0k9vHWmNZIKi9IkJjs2Frrgi76lOKRjRuHe8/TPKk/NDLUCCSV+WAKoN6kwKWlJxjEpLUz6Hr+RpbnG9vCvMIN1zuy+GSRfMiFQmnSEa+17fb0/rSoz2GixVFBvBPv3J35QdfBrBdrNcaHaZJlTD0d6+K1j/ZQsySDyeGAyrrUWrzaPcPk7ZtkEiqgB8D/jvZYKXdJkqnChGU2ZGyssdZbF3CTu2/n8rX6RJ4ZMaFXBSZmve4Q8UXiSBnpAdkW9niM8HxAUKd2aenfiueXjM3os8pAMyKpyemVeERCFKGmyMnGGJr0E18+aXsesNzl3yOP2VHqjUHkINJvvk1fEhY+MRGY0s1y4sZrboNfBZFH3+k13gPWJmXHVnZ1/QGIIcx7KZ4K0LuM5rXl0ZVKhRRrU1K7nyILb585vBrquaXQHqTm62lsxyJsHHodUiSVTbOcMcVOhQxq2RrG7tOQ2yLeW2+lnSVbE06jc17Z9v2iU04cgadBx8DNXuOMgrEhTpE417bLu6sfo+oDHMqHE4N3oxZovCwPZRjOWeACLpkKHNEWZjqRSnqhQbX/QOckNZXBOobTHiZ5vqlXdPJ0Q200Qds36Ofggoni3hAuJBfW3q/d5u2WnVSAg2dHwkTHazv2dfgvtX91tp739IDkZY7ugPuVrw+MetySWqLMVYS+WfN5B4ObMUJHxdr0VwZ3bpt3/1fu53F72/aHL6pFjdCCP1UJpfR1HpD/ChDRKZ8cYPzsSBtkdk0bL+3VN2ddmZWbOBANQulTYkEYZB/NnpvbVHgwjdn3PY3D9djWmeOJW8PelrkpekXeoxyuScK3VAIb8cw/t/aamnTiDjdGbrn2xr5n1vrGDtim6Nw0xxC5m4WFckQkZsowzM7UGeLtcHhhSHn3cb8Z+HG948rigCD6ORWVQ/6WM51SAwPFNhf9YxspZ0PbCRPR0skVIpvMkuq3gCWDnNf0r8kpEtmZpWV/VkfOdc1G7pNpCTWqgN6vq6RnK0cjNlvLMMoBtfTjSk0rSkCOUc8/DO5hBkBQCJC1msOaDudMh+tAu6KbllLl8WdXVls71GJX9P3ugxRhQe7JPpgxqNpRzercYPKRCgyyT3EgaS0/qgmLRp7pe1DzliT7SEpb08biNxjYhAeK3YTuHBoQPWYfLJ8zIMimy0kVzmO9E7+lBASgjo2f+SNftNTBWVc65YRS8X5ksqMwExfZ5/VABx/rCMSgr7Nx4rOrGAfxi70TDj4v+kJ3KOTDMeIqmwpIs08SxNr42m0VNB+orHW+ITN3uIfX8fu3UJf+2PFDJYpde8VtEBaFGlsxqGV++L1XzUMNOqqQjtVjRT+y/2WcUAoen+rQpm4UQR8pZjhT31IQdWWbpQBz5ALIxPhHV0NBpTPsI1X+0QbV4EhKo1A+UdvcDsjJekB4WNBBtodsGzOhma7NdA+mU7baGAqerSrquXZ99uurb2E/P2xV8vuOs0AF4CCs472s92lVJOAcpdbRNTdSDVHSRUIm9rVw7jQqor/UDvSefvtKKflSUNBlEuVB6nffm80WTAJ4Vgy6gbdBU6BWj/VAU5TF8xapJfJzWbToPQvkWXLf5EFkoBIQSSTlxLIQ/+8sdoIbe5HwqWPY6tnY2pz4AWZtUOJElsOdjTEQvwpnk0ATdtfDBpPTp1Q2agrMB7ywU914okSV0CmWKXXX3gIZ8rJ44J0pxhPagZ7DWTf+QAOKLkE7F3cleBflg/VrYORWWJHh7fbFrPbGR7IMUWubsC99UD7XIKNKirD/s+uQ5ydKQElzPJpec7gpDkvK+RKYhHUNtbtoHlCNoC/ZeGH2pUjW2cTL9ginadL8v52bPuOEzLpG7MNmQDrzUm2UL3OH7/YFxu9FNDVDvzirg9nIkFEeb3ZE8QhqiWW5sP9un3oQ+hZAkSyU2Jm/XdkNeHhqMHTGmpdeV/aeu6yexofmtCnbAw2uSuaeFGxdClyyVULed9KSVAQPDLbMfj5txMvFbtdaH6dNl/0tdhWeCel4ZoxF9+CQuzYUsWSrQQod2KZV0+yXdCogYPjET6VqGo7aZQhGs6ZpJ5yIOSTZNOScYnvFpl5hO50vyYCc6Lbbn6IB9qBEshvLukV5CSIh/pZVCf+rYbCWk+c5kF2/zHcFUeJNUWpT5aLve7Dcrig9U5F3uN4tKJJReAyTtLRZmjDFSIUyWulRmhLR9ZBv2HVC9/bH6W4l5iPpFPkfthl9tKhzK0kAkbNitj/ULfZKEJ9w9jUqQt2236F0gTqET6veC9HakM0Mx4U8oMd5o1+UFxKxhekLXbFfwE0hhQBxdtg/uOXwls9NprnoF1RAhhE5JyyoHea76vgXL+6pCYvWwhbEA2FSb9seAXZodfAdKBxp/rodQL0xKurVauYarI0xdtUxJjW6ocKdbWB2kbP+l1UEIOTed86tkpJBwKanIcLDl826379roXfsX3S9ghEZKStftBgj36f4EmUJljKb0TZv4KRT6ZFlmA8nsms3i33T7p2c65IU+Ygc6zskJ1CwI0IU2gl9KmIthoVDS5XQT78t2/7CmJBx3j8kuJo6jL/v7NZRfaDNcuwXuS9GXdXU4Ri9mJrALFHsxHVzvCZeypI2XrOH8/v6Aq/YEIYwULTK6/IyAg51GtbBOssctchOHUDngP+5/aLhKFxJlSYWhcuqm1ebRkV3oP44uatoeFE+uLf1h0/7lRXiWIQHTqDm5kAyNO8el1PSfpIPb4LqmC4/rzq5e0AmgwrhB/ie+p78Sgg3BdVkElU87EMfccSlpXcLLRYcAvmbVGlgB+kevR/KhjBKy2+RVDgzVw5ppsvCaMremRIDMPfD05cBCL8p5WLpNer0qZq5naxtkU45RqWMqBN0YiIe9zLQ7Swz9cVCPIxb0grRn7xqqunhRnhcvDwFY26rGy4DBM+xIlRpqRj166bZ62jPb7R5DHzZnvW6WohrAJB2HiZ/QukwQTJ/B1b6+BiNhHKWSFmRc6+cS3waC55QKQ9T0ol6OERWXVO8cTts+eYgWRslyj161c94ePazGERkua8oG4N8Fm2EYjn5pW1Ys6Um5vz3fId0o63vmkxPGG+e4lDoem0Xe7CleDnxhsPN5Djzy0Zq10w2pRIu5XkLOd64c1uQQupb9do5kkykfucrTgU8l4Qmt/V764cgMOUjOWzGe0vcOcyx3hEpamHIcAqqrtquKcfBOsStJszx6u37qd2qWyByoR1n4pbEQCYxEctr5eJT63i5WXQss85lSycQ/49XZuCnmSj3O5Uw6LMmMBWDZ/eRDAwzRkuoFznwtuOA3xh5hHU7UxZ6slx88I0EcFm3jNxjk4PbMUCLHMxeGfPVZStM9WWOPKV0nDWoo8hzfl4Vz+85IAIfe9YTUxFg4NFqibzX3EIYE3MeGC/FAz2fHnJzKGYnbuXHauD30/KqyMiY5PXJ3tcWjceD2Mc+CvEX0nKYSA3KNGpZSZqfYxZsKcEoq4E3ZSxdF5wtK5XpJw3WLHsLDiatPEpac2DxuhoiWGonfRa6HhiKWZTVxyqT4R7V6e9FQ3+vGZ0gCd2GcUOfHumvBDVjXrB4MlUWLahyEzP6m9ns5yz9jxJeRiK2UKY8v/cWatZXyBN6Q+7UIGd3M+FcHZd0J6yr5Zq5YUCmRWqfOpc6pKn3c7+j7rNdnlHAW0cfDZjD0O4fIKs7P9IPlZQgYH1pvMzoY6AiUEqt1nkhl4pzQa7rv9ROumsmidxXMxRxO2COoEgL9Km0DbMZ1LS3TYTmpPLKHpz1IFPCjOeBIQ5Rj88AaBnyE/qAH7rkGHH8s2g8p8rAEyT/wYRRIKUGakoh0aDQ9NT3gAoHIQLts6TT5rWiRX58zZIeyOSU1uyCJ0ibT04T2Paf4qHEL+OUsWrgJuAcWXacTtFWsQmoRnkB70w/+ZBKpjSAc3kKtBD/v6Qnwhiy0a5MFAhnVrC5nWkq0LmOTuoH4Eu49i0cOQFSBH0OpbQXZz2FO5hhhngGcPXrRRLwsCdQ8/LPTFfodODEWfbXFc1XasAQ82IQwyIPpH/wPHUF1YxPy8us5eXwu3UqJ2mXmlHptJvKufQHphiIodmekZE47xqP8iWqoDhQMn+lMMH7HcSo1xL6n/sL08FedBQ84AVGn0P/3ladzHkP7sn5sTeYIlYCUlTIXv607yvidvLKilOqyBeXD3TZ7nq7bDX7HcT6bByG91WycLLCsRJalC1kWKEkV1bUdHL3zXOvofEtxsd8ktoa+af/ilryvORliEGVtxnw5JJB8maNU6iRNEjX5ej1fCrbhFJf+GjOHOUeyE7uTKjcOInnxoMd3pgx/xGxYlTNkvulNYABR+941lP3DJsJ2KzEk47hwRZ+0XtXVcnuEcDBB9g8shOPtemusKh9WNXCHPtCPsqYUB4iYZfCvYplMl29DhpLplWNRwyBvoZyRTt78P8F6ClmPOB9Lyg00QR7nZeRmckF4x5B4ANjOzKoUVqVkVUUh1vWM57NKxmdlEb1bHzayouvqMK+CE+KmrWexajkWpGVB2tkHOO/T68WHmo2rKWGC6ZFwF71VUqAfm/JmJ4o/mRnW4jOisDbaCYSxbygHP2epd8SBJyBoThhxcUh/y7Bk2NwIM4slfmdZOh6qvqFaFeGSvoNSeV9k/1OgWbso3zfjRSXxsKiBq2M1uNiYx5giuq0p7FQipcTfa3LHQtTLtK35fRUADk8icTvLHUPnY/Viea69bUhC7xZocDt4roAId76+h2/uH5dnfyyrFX2oLAvJ/O34xgdsxPgmS9JhKaVou0GpiLVBwaCmuAr2tCBAEQA/d+jIb48V1JIQrWe49GT+TjuueiJhmhJYGShRodGsGRzzDO00SmK+9C7Hg7/ZJ7+xaFBBa9ULfMU/r0mCNNQQZGJaUXihDGnLbiEqY/fuXdWs+9ODTUOqfWTjq0PYuMx99U2SwBzKkmJYjztA19UGXw2kXEbyYTBBH2zLhwcO2sxcGmdqAG0HBMKEAZU+PECJpUhkhoDxlDqNIiSmHN2Sq/oEZL55FcJq4CaNlx3D+yJRGU0RtxjWBAdE8MBlUZbnU1+Ot5slYN+7E0m5JAT6razjsufoFHzXJTgXemg/fGy7rmIXc/Z3yvPySA3ssl0+1LZSEzGwFCyxoJwx9RPgeD0SmYvSTUfP11RX39AP3dAXAhwOTtCDwMwMeDE4+HgzsoLVmtxaVGHiaf561b4wysVQFOVZzAA33TziX3RopogSHYLs7CWdZ5TTs1QiMwS4pJOGh53i33Z1z6zg3OmSCWKjXVqIy5ERXlyGEauAEvZNQXiTJEJrUW48h+lVt7P6FgUPinsk17dmgx4WEC5HVVlIhZjxcNb3gMLMOEslNHOqKeUrj6k+L5xKkaH7eUv5csdkpnctBtqucw3M57rmzPUUnFsEDUKMHf/7pux45VOJ1Vo5V/peGvQ9ulWwNk1ztEYwkBE47NNTszuK1EH2Fz3PakY3IUslUlOxMFXeuN5v2p+sRA+icNWBftIr3T12Y2kZFAlhGXTir+75ZKthISPu/bLZrfi2QyzBAFPtWta+ZFWF4ABnDbEz3hAJ0VSZH6EzJAQldHWgKIsSzEUheu7b9VH3nCJiCFdBcRSasU/MUonQlP+VjhKAQh7m0xX6npQYwBTATT5f5+Ci0znVZlByslQCNG1lf2I+1WtbbEG1cndmVDG9VtcLpGNHrrzBJpfGL7iFvckkQpelG3cCtXkYTCQyk4e7PCeBqvuJ93Px/jgqpU7jRA292Jrxa3fOHJgSD+iA1fViNXYDua0OXf+sHsdqE5KX9dm0Z238oDlWJa2tHJxcLiixpxftpl12ML6hPSv+z9P0QCMu7Z+m86d0wRs+iWOHdbr9NSS+1dGr9hpz135g5Zs3oGJ1rEqsxdHzPvIEKHpvi2i6TEl6olH2pdoCk79fVx6VspAuds60Jd8w1q6rGNY1VNJ/dAjXlJ6BtFRm6PLXrsM/OycKVATxT9HsYlzEhpDw6O5hrPcFljcHy+uiVP+fEtvtZ9U+JjwfIRe7U/rL9Ji4YGnB+E0sf3kY8sfnGmDWSVc2D6GcGz3X5AcNJ3OsSloOJdDTHsxFX2LkMb1EMOZbCFBsfvQZ0j6zFYiXmMtrKmVNaoAkX4OaBCO3byv62XTX2GLuyKj0dtO+PFXb7bQdA3vRsLhUzoI0M8es1Gmej+jnTNnggRGdprMMEDbMbFph5dzWnK6dsnJCgAYFxB38YkBISHKJ47lyqsT09aCI1kYsIH+WUrT5h7UcbefaK7+LxyWXAJ5rh/a96yo6PJTQrne2fPR+vF1dPd231a5PW46+YhzsXWi8qRO0VLI8GxZXCot4g17/CmkBVZiJRf4L7ex7dQCZf6IUosugSnvWUT3nEyURPKcKbWJj7jC2FEFLmANtdj/7mXq9rZenfFOKuq/u7ecSuPNSTbvWPbmEUn6U/QfASJ3fPJTUTiTA6cOHyj3McUqyXEJ3kaRq0hS5aVpoT1B++z/7pt5NFK6/VIu2efLtULC6gi8E2JMtsbsQKA0dnsGEj5GZFKhE4fJL1/5o7DT95o+zm1W7BMuLebBwXwyIlYqzAN8ucd86l/itinIoSTo60RZqRBVcDJo3FwUDcoUyzym+Jwmp/Eu+XL4TlPLlksCtRPqGkg+6vsyEv2eOWUxZ+9uq22BmtJOZ+l99XPzwb7zPu/3iEdq4RbhKx5xrQ1ZI3NZxmg9ACEo4oo81yzykuPRHsIObXpZzkgDEOqQXYftrPpEXnOsiGdbjPAH7Pqh92TIgRUWpdBgTL7rqibamPRFXMSFvrmGcqJffiaNdSNDWxUhwf4/OMBWx9BMgThisoxSkt2nm5vzQlM0KidQmcyjot/SPCv4pAyWX1TV7FbcKb4aFQE/yJBOkJZnMUVgUjnaRD6sZvJKAXqeq5C3Q60mR5fTCUaUmdlLVrmvvfc5EQU1IY+YkuLlHU0i4NtplbpaRe1EvwHrA0LOgFdES7Aax8Azt3hqYv+nTWoSM8xSP0Oa8t7NCArYpHfbh5lDBIOEvxEZ6yxzt1u0Q9g+l5UlypIImRKzg7G0CYOBQSLQutaP3WCwGen242xT0kgiNR8av9C/av9s9HSPWwZg0//IgZ7JkTrDVniIXrSnDdyDxHlN40TzUa+jv//+0vUtz5Ea6NvZXsLQjeBy4ZCaAlYOXvkjdVPdpclpxZgcW0SyIxQIHVWCr9Gu++Xw2xzuHvfDG3ijmf/l93kS+AKoS6vzU8mZiRtMis4HEe30u1KdQVfTAQrdDodZ3nVXqOB6uhayKlL1GC/DQzBEl6UhmoploxeWuqXRd119RRerMTvtG3Ytlt4SQy80mMr4gCWXbzDEkczjZiR54z27t3X37gsudgBpMwbGSUnt5LRISIPUSFBu458zxJOlEVCC55/RTs6Jo5MaQWcJaHFNBnD8K20HIZ5bi8PKVcccdNxKnyoZB7YCPh5IsFfeJykoKUNAFtC+ufrrDQGQ+VSrSIMvUeBG/htbNcSLzLI3H1hbGkuAUUTrjEokCF/ZIdlLzDSOZQLV7vyoxYJCZI0TSmRIHg6R39NJARv7uDr22ji04/A8hBiGyAFat2Ruw+U5rOUrqFqBwbmU2OZpHlTEr8/n5MGuLZgJvPuKoDlEH0NmiWTk6SceFpMNlE7vb+uEBaslWtpBBNGBp3knHRtH9TXXXQZj3RAInD6gBCmbZegXK+VS5nKpMZrYA79tnDAFTFUdUYz909c4uaS+pO+oGNbOJA1CQ8W6+RL7nEbeR2J3lo4zKiBC5QeRxXJpRWJ7acQdan8vKF0GEFUaM+j5/zbdbgndWiOkF61kP5o2UdBPhr938o8evftOj+J3L72alCnOWTxbg/ThNHo+nMe40/aZp3SJSwRHkaGr7sWHa5t4/s81DArdNJb5dFj47YUNSvz1xTKU7va4YSkP1f0Yxkh+YyySXG0i+beuZenMQCNosSZLzIlIokFTkjwpvF3R76Qa9qWqKxQl1YpRQIOomC0lsKYD096VbFUrMWNC2R08iLEggPdzIhn//f7Toj8CppeQ6Aun/oPQPXd/4XxmvJ4QASc3SaOD0toaa1HXNHCxEqfEpuSlp/+Vx5m6lwzQVrWy7L27jwxfiI1W4akghLIwQXbWcqcAHgpfzbEvyrsYWpe7Ofv6Ps58PTFw5K8IA0Eov2RDmfIfMeJ4yHcdGvF+3APadrbcZ/Dw8G7vrYnOS2co/y0Kg4vli92hfmMRpXahxqF1TFx/dtlRsQ1vGRDdAiLgDIfVxSTAktpkVeJD8f14uDWwVf/gSsE3qELSDadIlVdQHgE9NBIkbxB95b8uC+0FmqWzf7BV24hApAdsolU/qNbw6K6QE8Y92A0j0dlDm+0R/qqHc4sn9eZBAmGbAhm+rxblfaI90KCdH4pYkb2su3NbtHnojsODp4KIKqU4ReeJ/NCeKqjhokmyyRfNdPpiEboMuYCJm2B22cCShR/QbhBMBK5mTRC1Tek4RTUJ0P6AZ4VdUhmZEJvRHmDGN1rL1Xb1aYT2J8JxQtRIx3fHBlUnMW4Xi8+6YnlWGjAKyRYU3HpUK/xECmEbKgPrJiitTUZIrbAG/1BYeeVkzwAX10jfI7CFdePlvC6R//gqFCwkbpXIcUDaoHLuVVZeMI/cEJ2ig6Ko6Bf6VIfGTZT+8HS+/QonnReJ6S6rYOoBEbyork5RQQ2R1boQ5/oUS3JQURbkpTCoMmc6HmzBs+CFnyY0wjtFzywogKelvcrGpRozmd95wzJX880meKwkRMitjB+WwHimD/zbgNgPo2KIRP7XQLJudRAf5pg/fmk9xgF+UBPBSjbD+822zo8oNHmln9Dkqek332CVP7W3BPX4PpO/pnc5C0C75ohQCT5aEDpmV1JnMvUqi69W/98AjUg4zdqLLcFYu5DxUvxC/JMU9rlePE29MyJAAsg4X+rLaAYXAfiQ7LLgzWLtNcL71V9Y0ZpDbya4kZPNexoutG4YBQodUMZjwk/GkFVWlyAXy0ROcbhCILCJo2I389IoOeNex5rspgjolaL77ByWKj5PKcXQ+U5tDokV3lkLOGKeQCuWieuAVwOR1qZBvvlRLRCgoRWRChKQkWbpv3ilLfm0eWLhQwc6xq/YMCxw+s+8tA0y+tLeF2EAmfEg4kcXzWdLH6v4BI4rceAQVFzfcJlhN1Yu44UNpOZSaSGLTTd7jZb3QT0wSlUTn/UO/21f2Pv+8ruES/HD282fbO42oEhMHzQLTpXaJlyXCjIQJk9QlVlH1M3Up1J6kdLv/Xj0/N26PfMluQCcrriQO8gFLF8XuMFYWSiR0MR0ry3LFhiobuLq/dntT2lmATw6FY5KL3aD5DIXubfMUvdsCJJ7A7f5dg43g5sCfPw/m6Z356HRBZNZSL7GzufEWMiTwCWMZ8tTQ77cqd9a3ZIAHhvS5IYV3wloanvoW6wAllEislo8OxQSoLXRQYH37AL2R7+1NCjYu8VXbmHArYUKC7106jw62BKKwtL0HQVNRyznXtLtut13rn0smIcFSmSWPe3DElNAgldLj9/8f7Yb95F/TS8IqMKUHYH+Be3HMSAJq46hACqGKqcVBoD2SxG9ldDLNbDwyoe6VEokzN3FwsrtmU8/GSWWQCm5uZ1sLLa4S4qPSWSKDklFQ4011xzq49PdOrLPcbfs8CHJ/BzLRrpS8CMAUp5KYzYaeUh7ZGExhu6GYtLXbt0/Ni5OvcOvIBfRPGSLSlLPm7JLykBIKJF2m9CjFXbZQRaNgcywXjgHGELpPOLV5iAJnxt633u6N75NEbx5XOalSGDg6aktR5tG0jp3SyC66frv7Cq/H43cZNPwC5N3PBgJpVAkvEuJs+XELftn1v53pWEfv6sOu7qZ2AnxyS6/1FSxB/YFdqHiNzPn2S0w30IMYEQLvsNgC9AXSe+3TXdXdN0O/+zN+xfA2pxopgf6g2ksKgmCLEmYkncaVT++tIuhVU683MAlMB21z9/4G0P4MZEK5MaBCKRelLSH7pYQdSfkjE1iQZNzLNUeqTBUZ3asRZMLF0vv6rtpCKvhU+D0vQroV9gn1elKlzByWo+mxq3NaMvTFrSom1NK3RR+iY3ANyeY7YfGlldtakO5XQptUeeIIyZddRdn3bfv8XGMOnoaiO0MADEg05aKXhxLGpKLGQ83Kups9l1EU1yMRKJPpwIYHOlQiXDFcjjuqNClC9nGKTTq9o2eEKuFMqrwYG98Le8sv4B8CEqcBF6fuVmA1jT64PpWSIJQHy4H4aigO6cKdpCMpuenMtbWc7RIc4H2FJNPv2ajPNgnzwVIWh+jalUzLWcrGwpxUcMOcGApdsBpRTOkOMu+8JXBSqQekk5PNd5BUg+LpidfAnEOARO4iUfFk73XgHUr7W8UOC7PG6XyzaQ+erkkHHcgKAPnGOVBpUMKaBHRDWnAmnXzhGuC+a9jZIEnpL/W+AjR/aH3/vAZQkS9OKfCJCXtSlWCCjw+Jcix92tQVJCrTPDDtqs1fmHYLCxJcYHgp4VBS3zbCOm+r7aGyTE7qsug6nTPU+xs6hCEwk9waq/oWcrjZwp9UJVZgDvzSMqljcAmH9MaReMvl4RmL+RMhAhOijmCLTK+GOtKI8ChVmWdHUxQ3Os2xbqa3uHkapdQvWsZ2eNrMNEjasvi3BdUWxadyIZu+mJHJjTdWublXUpok+rG6t3EABsY7q43ImeMEAxMyHjD5krWy5kuu5VCTGu7Npro/7FiVYEs55QE3qog+VTPBVB4y25XFHJmblgGh2xRLviuaH5YZz1XOEIxUk9/ztwVTn2HAO9EmPX+6QzN6arIQFKYKs+Rjbj+9XE41MVkAxfNdR3flLKHyjgrd54nohhUJ/7ixplrH6FMd4q/A358vHBT8/RVyJuMcK/GMoANaf/liO/MC2Ipnumri5vn0TD+fwpZFDRwrbwbJppSswbtkyqaEaKnjIp0rXVoACqjnP1p4g9Pg27PUmxTfOmhTqJIl9V0oKCjhWNI5Spmk/MgLnGbz0oCyUYDZSEfjrfiAO+/v7lq6ZpO2PKgxsXWkN9PhgxN6pY7LEQh3XVv+ErCVUHSPh/XABJxrdxjzdiBISkbZDa9vZIHUK5xKncS5WNStt7YXB7Oj0AYyHA+jf/B3reAAzU39S0F0lcKj1ImQ4C+gh1o7E+EUUmAAnqECoOQ7IGGGamVeCJSBxgl4Zb6hPC6ysCl1Ql/SFCVURW9aAKaNKSKEZha8s2UtvzPLgJnNBgvzzVLbEpWMF/6e8I2WoJ2m48CS53HV8G2pmI50Ve+eqzFg/3neorHtrW/xjTpbmJQ6BaVaNDh6WOFd9FjGxUbZIdNrLHX4ff3BxDIITcVLHd93xtFHuJSaakE1PqMtfWRWUQHznPRIcO/75B1y9sb2il/wa5NInalxmCOzr7fV/sm2SWUBk7zdrv7VabowynI/peMZHaRLWnAfsFQoCZtSQ6lovkF53z/W7F0KWkD/dNdMYSfgUu7Wbe3bogSpJGc8b14ygFJCqKTmdlQuGYjeHzZP4HjBvG90Cpm4DA9ir6sTWLUKRXkAULGA8lDCq9SKPj9HZOqs7hd8Rukv9erpSeAKfzaPFFbhfsGoWgmTEsdwO1QAbKLXzW5Nt0inZljKR86AUYyzPXaLIXMlvdRUpvzOJHDrLJ4QGNCU4DKzB2sZ3R6oJBm5wqdkMxPypZklA0HwupSwJ7VJnCOddUkAQehwVhoDPdBtv7dADie2e5iN/rQKNDhOFihdSK/CnNSG/mojKGhbgfNafcVfZmi0B5XUdUfvh8F4c/WvJAggqBn45ptygxOohDmpDaSsJ8QXx+gu6Oa/bph6M0hH/3ka5+Ak46vMEH6EM6mNFoPOatXesf5hBWOEImbaxEPrVpN/lp5oWADRd2MAtlHCldQ5er0Tc6QnFiEARf9YVtPKJO58G4A0C2MqIdf7vnM+mIToXI2uTRzigJcAQQpKdpfUG1ExX4lKe7/ZVXMct0pC5DRNuWRPBiKXEsakzrWbZNOXRGmLsgOUmjHNouBFT4lC9daxyTcPHXW1Rws3kwb6h/uxG/x5CWMS55HPC/UrCkYoSDOLE4i8OcjlDRVG9amZTZyqECmQeImezLWHECZ1XmRCT6QyY99SmQinQJVQRwYBwNZ+aJbqUu3WXuOfINw0lqV+h2UezwplUlP+mRaxD1VH2f5j390fznIqiCBrB0kQW3nYx2SLtCODucBBDSzJfKJJCADCnNRFrAV/L1ZodAYU9JCWPTK4glrA3eFEpKTI85CBVr50KEhMKCFPYg/oIFMs1d4+Q5n9fftMLVpBnT5Vi/eH6WLr4+bwBJ7CvOSnyB+WXLWfq4TkKgRKXYihzSBIbpnBKQWBD1+rh61T2L+gj6w+NTENM3wtl1z3eLkm7EldlM7E9GeAgTdQtXTjKhC9o5vDqnpqR3OrP4lyy7Ml8wjFUaAYDzTqRw9NDjI9piBFkVELcuDT2MQGducetdl8X2SSIgQxYQlvvlvEMUDidplM+nyqoLuXKjpfcQOSUHVt+Xce+Oastc7CZIGSRY0ynEmYk7pUsRj1drAwvQJXmosOjb6RWjOh3/kHx9/ffAhrkk7jbhFj8MUlMUvpjWOM1b90rdSvV/X25URUN/12W235gH6BdpjQKyFMQrkxdsp27OPcftn3zAfUkYXgUAH3G/thX7Q91dNgMF236B2pODomc5YhHLzBj3Cp6xD+pIkFeTswcKO37ZcvTHrP44KK7ZaK7XbqZ2xXWo7wlmQhGc7aYvu4Sjk/KiXHSZ1dGzWp1AXdUj9NveFjA53L5E8I7YcIzvGg3VvL8cXS4+GKEYIH7RQmnByYjftjvW5fqLZzkg7VtqWGdyYzpYKWkMY67i0IlCphT5q4cIvjq5qdWXhUYxXZIcYbva9bmdX0HdD5R8rRQXts9pf1yQJxwSS0SROXohxtgQcD5j5D5/hqtW6HPFJz2qVWkn0uZ3p8cUgFZ53/fE0AYyOEO2kS7Qya7KTfuVgqFYs++3evjDOreeUbPCD5C2uSkoBDAX2wvjn0HaERAgkWRRLcx6Kburbc0j9db+eL9TZv14Q3ifOMzghr18xuGgyMDEPdN5vVRLPwu7SlSha78aG4GEIivEm0OCIX/zSxHKTyMJ3LlH2PMbfdYfkgrnhnQpykLDlSAll4J3r1AhltqE4gDLrVet/dUct7gm3XQbhNs8TAg7OGErqkybRJp7U/kA8PDzueCVBGYQqQtbCaDIzxy6ZTviREvAnVkfFO+Lk6EsoktVzx6PL1Qp8TdMA4dRRmCiq3j+ltu203/aY/e/sD/fmvVdOcUegNmRTpRS0gxrYKa9JkZTkXt7peWb2bs5Rqb8whVuvqyeUyul27U/WmMsTuVy/KyNq3JkFaJe4R/b2CdwX49s+PGPJneTIXcOLB589Vx9vIUwkn2JgFLWf8DiQM1BDypEGRNIG4VhbiSrd4h5GrcfurSTPyCTS8VkQxjquSPITVnbPY7JLQvhIapTGpq5guqXmL3vcreBBkeRFjwj5gJIee8m9Pz4CMzVw/MTwPQ0Zk3h7XBoJycpx8UnNTlzYkNwXPiJvH5nmob19vqgfqMb98OTv/Ox2n+Y1u3xmUc8M8djD5843acBqhTtJp8plBo5O6yykQ0K+ganZH/bX0AEuBW4eAtLKlZMvLPmFNGjOB3/5IxU9k2X50mSgQPFMzhFXEoCtHLRIvTObOemHjyGxp7Gb4PBK6DQVvl0heGqt3ZUsjeq2wIru/b+o/9o4LSv9MKfFhNDj9C1vSGNiOzDA/1ysK4Pc1sMkaRgnYZO9mbFcO4e+r/r7u7nlKN51SBrlJFDyl9MonYhgohEljqJCZghDfgdNSb3fWV7pIonf1jgKR1b2wKGbcIp+QQtA2wqYYrww3v0eJ6FCbccTALf0SKE7xUosaBaroXqoBYj7slLwnCkLblNZSYoFJpYQ9aXL4yjtYUrWvwKd2lZOi/BP9NInpcq45ZTF4AqeWJ3BCnjRFPDI6RhNAC5iGmkLXVrK0/S4vUsOSU95hLvKwkCgpq4+L7fPnrtkgr2H0mMSi6jIgyQa09na2KqEaLLDH9U9xuMcV6qSZSJdbE6LRjzhJ8xnB9BpNgl28H72zPKSVVIv2LdBTUEKfpCOpdKYTMFi4p7lieeBn8Wz8fsSPZscCH7CGOyahT5oyzgQs+QpQlvFBKQhPsHbRBBz1tnlYuwJlomBkQkQCh+GJj0SFRkVIlKY0uRqX28D80bmoFQB6s1QlkxXkc2O2+dFeQIXYphQWZuNTdkEgFyIlhXGHt6X2GoJzYHSdFTqOfup/65/cnLve/+YRmwCMIijJLYjNcZIT8iQE/p1jAstMXbTV/qsFtSiTjsNSy3lZ10+wu566I+UhyAjrmOJrB3ggIbTJPEmN5LjX9aZZNXsrobzBUJQyQxFZ6u2wrf3Q4ea0c2RE8W2uEtWGTFJcUt5VQpqkI42Q1o8VbIeBtuVPLVX0V0JxsHN+hHRO36A0sB1YULziR5TLeTJVOAMpNLOUpdgWJMZZBsGmUV2i31F2OZVzU2XQdDJdYrRwzy2sSaqVR5G5SwBVt/cd4AbVFrttKF9SKoHfhk21H56qdXVEm06yELuvbImCkPG1LscDZcV4ILo+DUS4XlhG0UCMn37iuHk731DYoSPTk5kbKmR5iDigXrS/oNiohTYJ2evRoYjlgRiMfKBKxMR0vK/UeA9WbX+e92PNvn3vLOfTJHKaMotnPlJUaDCBXMcqAtx2vlI+df0NnNkab3rFzFYLXTLPUi0bidtDDcmEz3SXIfIfR9aqZTyK2wuMNklFEE3ado4+JQmFw0h8zrQaAf89+6X81IDnQ/0j/bWpf2w3I9r/Y/Xricd3TJ1YKBDaK1CY4UASpOkapkJKZuLRJRAIW7ja5uiOeqwERgnOm33E88rjLVKQuXbB9YdvmmSvsx5PNcKzTwwvkszkcP9tvlSD4O2fx/rZrY33Sqc4kYRqLN/nykm4tlSpsjlICXdtQdYvm2uHCAGy+L6PmWWfkQRrNZEntqZkbJPE8Zoe0Jt2g132ZERyvrmrtkeL7TQNMQOB6FXu3yEXOJMEazWxKbBh6DVaabrYDrw+zEyHEu0D9d30666G33hVPVoRThW02Y6XbPYgwqmFFAkoidMo/4qpdvQRpCxoAuZZZJeQdgZvD+XsSKF8G/1PKHepj/qfPQoqcRmiW2rhtgvINi1kSWaUjMVAD3/Cmr/kqXjZKwoVVDl11QoI/Ll+WRwSGPJsCbCN1lYLXZIR266SrCOwSe16AkUtkApSJm3uWSJ4zrHTIeObQrF/yoKkqxZ+ZK7FBZw/b4bZrtYUwVVMrT9mE4fvd0mEtJQfz4phiRZKJDUQI4vsE/YAlRt2p2Vi/BYTPiXVENZWZpYEb4Ac1UKMzI0ZHebOd2sAIZn8AyAi9erRp5qiliB/lyNUSLIrlspJyCZpoUai9ZfMe9Ou2q5rGcOF/TEMso5w9pfrBn48R+/OBAk6xIvUSP7IJIznarS2fnVPb+41chi61bTUydw18XzPk/CxFEiDdO+ypZkbZtxaqJF5oV3mtWqA1lZud5YAusGb5GmVdI7xNj0uOtY7DEZQ2ZZl0A7HMle8+BZ+OMV4Iu0++YHI2rHaBWwKC41C5ZGyDN9n5mmdLLRTE0Kxw2DE+OM2PyGJ22U2vqwfa+C09/WEHZmqLIVwEtS33fzo+zhtduLuVZdBCBCOZE65Z3hS7zru0KwQP7tZO/+5q6rphhUuZEs+dhV6qfnKPchxTlnla9/WBGFSGJIF/pZupQxQxGBmneSlAmFjPzpsM3oDfhztcR0XBC2xWm5e/Xs+USonUhOB4Hb7S8Xff9d/+QI9lyS6rdH0OhQnFZlzT1nqp0IyrFlSv+TiW/iRRSz2AG/Z1i3iWTGjtzRU3StYFE98yH1BOwgkvahVDEUgLdRIOpBxwCRAxz+tsXKnukJF593TXWc9zAYSEgsS/JGndYhWESbufm0gzrjCjywSgSg7/ZbhMsH32+KVBfLfYDdwGOxlZ7E7DhIvZf0krzsxPy0jZyqdpKNzLbmuVgcqNjJFuW7gRX1PYCrZnNiLmCpxEhe6i3T80LDioiZ38GcHrJyRAdNFyZJrQZKGFGxlsrR3s4cqxkONos4ODCiyd3Spog8vdbdpW0f5u+yqr1h0DazWualTiPKGYiiQj7eV83dXysnKzEFdrBYndBLpmyvNLKd8rHaVD6UctnuzRo9LSspaeJHFKMPl7N0cSzork/hYkR+KBX6PxzBR0NJaBPsGyai3hSBZZMlMtwjwrbpb93QELMbpxzebsX5jRzNola49nippoODskoUJn0tCOPS5p6vK6566cZYMLpyp6tAJfFw3G3qBm+c1HeOUkpwE9ePJUugEu1ULXbLItLMztccaZPDpskWfqrbfWBws87mjj1RMsVMf73VPiAsqyKZDL/E4uS0X1mSBHupUE/8nuPTuz4yJuSOeErgQ+k/wCkGkwKxcYguAK62FOFnQs1KTPHO9umRAbkoR622zfeS7/ugG8RVlHg5WnioqCRGgK7Il4eCMr5YEdJ2PC53bznKjhVeRZTlE6ESuRKQMj1JMiO4UpBW1H+CFFCPsyQID9QkxEEK4kDPvN9Cjp8tVRINc/itcLoc9wy/dIOtMLc1hIhmkNrGg0sVvUIK7KbJxIFa/sHYR1QhUINArg5jbXyUBP2wtfesUDqMS1HMqg8cxePc0LgmpgaHnM1qsMzDnZruvIkvAaPYnIT6jnxy06lmYF2DVo4VGWeTpaEo9nM25QBRxdFttqekcKV8OeXliApuGgBi0WlyuoKQSAmVBoUFsVyyUGVV4tWUNf4Dkqr6rB3ncz/THttiTT4mURYh3p2ZktdcnE+9PiJQF/bxsXFwOrNzb6kC19xmkDce5IVa89zYkvLsZiMxnJk9CJofKGpz6agR0VMKiLIp45C3/OLR6H6tD3UNPhXqfEc94g1FvNPjEnlykoFrB+nf59uCgfWkhVFInrie7nq7dHEalAFXmyTE9zsqEUTo8zTJBDGbNrbFPIM++PYnoI2YAuqYdez4+1me6jC7qbX3fPw0P6yM1o+vHun6m2Ho0+TVBvFy1qJTJt1tieVFk+TQf81sCviUtyuhvz5vJYvXG1QQTBfGQFGwXz17ZOeQV4VcWZRGXrlF4aro2um159ZUVLCZMeWRcz1nk+ZGbWMhUJWeZVe9cFwWBUCwLyj5mbiWAqhwKrylVahHrc4of5Xm3GXZzU7+VIGMT29r5ng6/KRe4S2TxEY9TdwcwKTuglrLUpLw32NTVl5ER8726iuxs5AuQAOlp4ViW4/gCmDdIOdD/uG+3oANgOTSDLlFdvh86l9lIJc6D1ETzJf4JSHFaeJZlrMZl74893do9IF3399SbZkl0CTG8bk05eJjyUnHgrzGDil+dLJlAAEishWhZxpMa8wp7bqoFti2IFYpC97t1NV7wb8HhQuwO4SvqZxEAF6uFbFnGE0O4c35WA7VZZZj2TsuTy36DRecc5hWHbA2s3PqSFIYWlmVJpWE8dlQVlvKvAfukBJeZhNoEfHeXXfvFkQgumPa4OSakmlD7df8z4nW00Cypm82yMdEdnuro1a8cnLL81DuHvs8DTjLzzkmD8LrfGPUIxbKkTDDEpx8oLEc3X4HIPUsM3aTXVJSv4ZY78BnRrpwDC0dBe4xPRRoC87KUTx++ApRPLSzLMsnysYGie7FFvf1D9xUBM8+y6GLchWEgdaR+CW+7bz+bdAm7CFVzLQTLMoEexySrwbWTfgYVuWVaiCmkdOW4ZBbGe5RQVGDl5tdU5NwvFMsy0eUI92YVQzuay2DexfNVrt2sgED9RO347uhKF0mQX6ZeDNx4Y8KvLBOTT0wp6DNj3NJZanQWDaaert9d13ft4QGyJWxMN18/haFQSitk6JvSIXYL0bLEnRsvUgcIwW5Hf3kKHSAJTpGCf3LQm9kJmCcUQc9YC8WyTEVAbTCmrX7FMiDRRQoB8bEX+bhu623z6wyfr+MkYEuQlUuakwV6XKFUlmnmhKbsWd5R/Xo4KyhA//WGdAVjqpY2hcKrpDMVqds89c2OaujNi3XJVGmqps3I236/WluP8WkzokNxcAsO4nyhJUaneiTq/AALKpHh46IfekB3TXf/B4izIKlJtdRqQwtDC5WypA9J0uoPdJoLriP3GODkVipAPLu27a9eUYUyCYR5aD//BRFIyJRlNgnRoAvs2g0j37GXSJXQhp3fwx+B34NE5uIlL2HO+MKpLDPtFjzXB1AGL/qOqSea0gal/mkTIrbi1vJpks1MUEkULwVr7iCFWFmqJJd0/x/9NrpevaEXQomssHrTFui1sLXIg7YWLNLvI52kikUF5Sj5ZCrihDjvMTAu5p6PC+S8kO23SZeCM4+PhEpZau2cqN/VAAe/rx9qSLqBvRhHA2pylE6yTT8F8FNqlwqZQwwZ3zeT5HNJpKa0NkSit9QYrqhk7LfUWKucchmrJ1mfdYcwpyTGyu6T52RCiLAFq0t5Ad0IRcKrLOn9jSX1BgPJd/3hEYg4CKvklPHtzEZ2lkxsgn7qjC2sQ8Amgy2OT6UUmVWYlaWWhc4NBepqj7tjkTnsbvge0qjRFf2nm0L84boihMmk4yXiMLQetHAsS5OMmnxuUHrVfGkw/csMCHp1PdTYTnl6s2+AQz9CMKUhBUBulgwyeAggREs6VlEKx4NqM4oBSZEZLkS+Vi5Ufqw2qPHpGs3yvwl16vGrYcAZRwu3kt6y83ob9Ek/tZv2yxc0OpnVmDxE70X7DnfoI+IBenJqU6breWV0CCO+RFfrtX5BqBRWZWkKJZ6LAMYOHL2MAk9kE4dDLzT3R6SOMkg6VZdLZb89iQTtPNHZuC+h8nUgwNFtTVALsAXMTOPhGzc8IELpdJEGgwgl/ErK0tOtSXuw9C7wvksdWv6HDLgsHs4rWohoIORKqmPmBeX76guSbhLnA4Fhxjbrj0wegmZI1vbRV5nYs0j4BqpKVs3dgbeA9GTymIdu2L0NYJP3LXUhO4v1nsoXld/GnlvFUv/iDQQPLbxK+ii1RCOnYvaK9Xmo9CmPVDne178Os785SEiFGGMW5ZLCNESMtHApy2Liag7pG/rQf2zpZmB2FE8wMOOj+g5cnjWB9U5F0AAIkZKObyayQZt7iLl/gEUHCgJTnERtthDd+3wNdRAQRsW8AfB1S4jdwqcsy9J1Jlf0N6dvvuuwnUyodQNUYLdjXRwns9ZgprX3W4akQT23rVN8mkYcBfLxXI7oZaUeB8WSzORp9KaCd5dYLt3Wa5D7n5uTZYkO8aUbNPJ81rR8s4b4XcQssD/pCu6YymjFN1MqvW7WQH4/ySLnGn6QXVMfKT/GQeOtnCFoi82TY1fSqbJibgT3Vhj6lPKoGRyv1vC84Op1AcWMY8BHUJLJ8qU3yKHKcSzpYGihr93Dwob0cA+uXlwOrjQT1sVbalSgusoF59GHqIL4IIumkAb33ZEsi5guttPx+NJVVJg7NLwFyP4PaKyGIEGZ1exDEDGO0LEs6VSFU+8Bu7SuAO8QsYCUmtvopnkQ09M32M2/2uBpTmcESoU0MHY1uKAIqR3TEkdyYNnhXl3ChBFpGKuKm37uw8oaUDsQ6o+FDsqQ2WCZLW2+GUXkOJeFRWg4EBj3Lza4qyxR0WX9BDqozOB9Y68yBJTOQqdeGy9EdUe5pNMIuWhgNn8EvCtjlGyIWncQMz5fMuzgcaAjW9JhypF69aprHivqUDoAv1Oqw4/MsYY9+JzNTD8jiKif+CVOUr7SuTtOlqWjnQl97zUE8Z/qzQ5zQQVXyP+RNU7I0ivTiwUdmpVSAnmmUtnlcrqLPnYMy9nvz1TJXUIlK2/weezI6SQVmyxEz0fzyMC3GODCtyzHY5lkdpt+rLeM0E8ptbK+aIcG3cL2uAhmLPiMbR2HfG05r058g2/kFhNLCM+MM/R15DkLQ4Nc5vFy4Kp+rDZ3x8SUEEYYMopfnQ4ZxcTJeJxRFBbNySH69x5DyyKNfqLaU7771/0/eioPPlPaodvjM6XTIUJVxiIufTOwAueS6K2gcj0iLuvoHV5RCl066/vgHGm6fttMR3FZyNvKrMua54tDNDKxRGxVxjPRlYv2cM/OHYPEghs1QSSTPQ6PBk1BE4GStTuXIrWJJVLrxAgo1iI4rPsqjM7p+5tTUS7rewDhqIa7nxJSgiS7M/bq8tVvQAmaWKK1FnFDSx94S9UZR5y0wGKprh9xoAly0bcKDCEQZWYJ9Ya1iYklZOtsdDp1XcH7BlpifC74fM/fHcuLnv1wdfbDfbVu6fswIbpPJa9xfHuBlM8jMZs10ied7k0F5jK0+Kgi2mycw7ENzxc1FUSz3jIIcQMBwcQP4OLgI1Fa56OI8G0LPyxWE+cLlGbW72l1mDl11UCXHC/dEh3SBMRLNgGoH01cjqdyVa0d4lBgfKLfBJWeYrCjFIRiu314YjaoB2qaBNHjTLZkMA4Us0ni8VjFzFfpbTXkM4aZDvW2XTPZd/iaLj9PB+YKGUG9eKkXRSk1TiUB26i4mGW0y65lnnxKbfrASQXgtNm6DbzMUEbsVBpiLqyZuu8dwvGRJFabcnSg+IRr/OppWMTFpY4+w/eqq2ZIPFbyOY6VcQigK2Oqs29ZUPChJHLnaSGQ/S0bvVnrt4xq0DcoUfZC4bcSHkOknOt36qAxKstA+MrJjG+UxO88G7Eu1gGj6ZDUlcntGG66bXpPqXV30uhmITtUzewmX9UG8Q6T6PFA2RzkDbhZhY1lPJ5neHFN2/9qZc5Hpdww5GS2JHRgPzgJ3bmKB1SJteZrh4uMb4gKyOoOZaXL+9+F5UJo8lfcHJoSid65cdvBgcAk8kFJSc/XNbpjePoDslXIRPdbwUkiOcMxxEeoawZzlaQoTPSq6rbAwrle+/t86AqmV3jXYCWOJGG8EIXDwfj8ff3SsNGqKSnXPYL24QSgOoZNfZ3KieQ6bKeDMsDHa4zZV14Ok492PQByrKOfbSuUlYW2upCbeuoeNmmwPdZ4oZzCdJFTaFIJ4UXpZHynqvkXFX1aqCzf1F0FM4//Bf9IFAaWaapJSMuL4aV/1cMVXSrBnP6uk7EJDCHRZ+/Yi+5TfQBk0MnCvIcl090poDo2QfZUzI7xymUhkqcSycukkCb8kqUq31W9FfROE2olpt4w56N0zpE3TBZUSBWLKQ99QSqRvFSj2Ohld9jtqbmlR7Bu+y2E/OJIdL0c9rSy1gz9w0lIL4KW5NnS/IQDQ6rHk5VTfih1Jvfthn4CpqmvKZ4jsUEdQu5VTS3n7r6arzLDjE9NsUj84MflInsSG7ccc2oadWfbcLYdaV8w1t1Er3b299z2z/WmPRKL1iGCDIX10/GtNPhELrAjcwlcx6qOWMPjLCvTox2LW48z6GMuyFQEjZ158+OVz0AAdWxMnKmQM902X75U20P0uoHJGBVS0YcVpb922ktdNfXDiZRWGmR6muVLfCueDzgeJkBJuZ4BB6mwZdq6yQtrMTiCCL4Hx1yoJZlBQE+No2LSgfTUv6Lbd/19PcRzB4+37Z2IIFTrunk6eUxBr86asHn7YMQox8SkU+VutfKKZVj5QJTUSsiOrx5BHJdAMDjVHU11Qh5SWSxRPcBJM46ASbXBpEkYxharGjk2jc6x8xkgRJ+qr7wFniGFgwKlYQK2D8XIX77jXOIko7bojdVhe9d0jxxlEq1yCFPu9oyrvKWGCqRLFzqnM+agjW/OqAHfVBDMOOP4lnQoMYSz2rBWUdsuLPDGrAzSt7+0IGdINvP0JV7NF1vLoQQgwxv6q/bujiNkkbLweNs+WzCR1WX60+pV1n3V15Vj9m0yidnZiGqyygcDZBgrWLenGPrev6Z+0ouOh9xvZhK7szIvp7f7Zj+AG4ssj1jtSNjFPmTst81GnByjrw7Htt5kErPhZTXIDFn5Q/qugGOmG4Ys+wQenJThvC84/vBD9A7+GFdhMgnXqlAu0WL1fNPTL3phvTE6z/kD/bh+Q924g3p4rOKCht3lEigW8gtGSbDW2ajmMygMfaqeqHRMSwpFWJq+rb4+OqkxKd2O9EWyEFPKki1PfI+IY6OSUK31aIGAEulLs604k9mWnzKeCPoO51qD3nwqMRCUQaz1slfwABdJScjW1LG4poD3NuMWlQoDHX06TKGN32NdoXgx6Mv9IFcZJbGbMu2oxEJNCT8p54ECA2boizDmw/kO1R1TGQbP7NnFUiEmBLm1rfdt6PklSgRnPS073WFjeFFCMmUJG/amZZicU2XyGx+HFABmSZaVCwAl0dtMVnOUTuhBXdLBKnisKrw+2DZMWZ+XbftY+2DXRRAsHe1c4RceQx+szHiuIpsT5AQniz9XHHH3rAYJV7rHxL0iaH7BgxWv4j+fS2K40S5ushzKh6+8Bod7JKZxsMsVpsV1s/la9Y/0cI6m0GkIb6e0MLCFXa9REslZms/FqjsKBtSQ3Dx3fcPCAiqFkdX2vhpX4QMH3L7EmeJvGigXjbmKb+KLakVJTM8nIi3XNeQjo+v6wNrsVFZgIs7l02Ro77P+CXVu8Q9XeW6oJa6zzPIIC2868HcwjT7LyriIBr1G69a6KFcR5KjLMxXvQ8L3pyWq52UhDflnilWQjqw2VLTR2zAR/dhJCf7dU7oiW8TNYoupJagXiSilglwAEO9XwFMhGmNFJAcrZkYQ3FBm3tcv0EQ/3reEeMlqtSS1y1sgLXG9yNy+5edqs2djUuv/Z4x3RHC7rui62Xt+LNkYMHrKsyUILeQjjZagXqjSoYzrXymKY1OPbf4JCoxJ0d0JBswEDAgMi7f6JjzcuGgJ6IVx5fgnK2w34DsyTaHgc73Zd43LLgsye2Hc/cQvdWD4IkkYL3Knjky9dVfdi0pTklIi/g+KTA+2WrmyqpKTdaYKsSCDD4J/8AUfBKMlbpd62s+h1b3q7+wOw5SsirQ9uOJyKWwH7cSTRUwMH0jCdjkPRf09y0CAxUPPhvLb+/rlMLS7P7fdivXRfLtDE+R8znAYr10EhyOJ2WU5Aq5v+t/oMb2tIK2dqZxDdr05sESrm4ffNcg6E14za3MGUWf8yqwMp3B0yyKlv59DnlUbBqff3dVA4+d/3V3WS/YH3L45jiWdJTNjWWl92i5qVh1NMxiiBkM+A8am6FT8YuhcUDq2JQ41StP8sMOqiaIi9oLI55rN0O/BvXJwmAW6U8inZk3ifeswfk4uTKeQy5IVD1DXVXTbbzFq3kCKI0Hf+3UygRtAA8/tBpL/UxBBGiROkTB116eHii/OES/pXEUp57qqN82moh+z31dfKYVplUV/r56fm+oEoD5fPekQBpZhnJ53doLazZEvCyoERwfZCxuuP9bQWaBWPsmiy655GrUp4L/V7OhPjj72QaAhlS0Fa+6bHPcSjn3JzNOa6iEK1ts1Xhw1xCmFzO2q2n6rYgtykM2WVDXtmVzcTjOTy0u7oJB3QAbpIIRKvyqnMNmgoRzs9uxWrO6oaxpI10cduQ5xR9AMkfFqr6CadBRMOls5TgkG0QXqQ6yIrEpjZhsLTrfffGGMxYnyUliQYlWvxQWP42AWGKDK87I2GzWA/O3XZv8b1Z6G6reZut5NDa4vjnzKLkhUQCgHit+vxQIUv8kllCvjqiQ0a/cUPUdLG6WPmcavu5r52HOucRDfIU8X1REREHIJ6OxtZIsT+JLvolsM5asemNR87nF5uaHvbQ6RVUFSGcUSVQWkEJNLHFdUWs9mg+1jC9JjbqLLngIDG0U5exRgrqz2wvxIcR5Cx0yXVBq5CcgljhuVnUoSX3T1V27BqWpiXtHg+gW7HzpmtTlytg3a6ijLMVoaNuUSwotcpFoZaRHdNHRb6b1muVOrc/oUXAvMnP9CzJqtyoEXf6oY7uJOUsYqHZPJXYtNPQ+XsQpPsFO1xh/WsLFdrW0BN0dYhdVuhmdLvtqNjyThmxopRzM+wNS24WFJEtO3BapTO7wqC2IYZD59eNggiUizqLPNh3LxO0tUqWbUgduu7Z+phNMaxq1UyzYr9874MVk/3u6UkWJC2iRrTuKLRvZchZyrdLhvR+Tb8hZQp6dLi6t6VVGLOx/DxVkgU80/VuK1rmNhFlSq6anKgFUOjF5tHzZsl56XWWSBV8MkACSUY15oFuKuZROJt3PDiRz1kk5UptIJfKrv6tWq4mkXpjdqMCW1rsR22vznC/CSW1uvWg4fycVr+vuNIifvW9Yif7rbnZUUy5gL2n7lcYRX4SSk5uY9jreQRIHriJd0jnzEnttKCan/TQepWE1l0jl9eO7u/FTt1l6ViiA1iJy/fd/IG2pUxpEv6UhlMgRG2H39Sql1a+HMKj7iXd80WFdGnxsMlufU6yAGQ8lruAVesXHcS2rfk9EK0UJ2zxk5gU4BTWU7UYJ7U3VYXDJIbxYhsYgJSvhgDC1oeRjHvATE3iVZdoz7eEC4NvQvXrTtaGNFTR1ScL2bw/PDfGNjv7y3Qd3vmJY4SDl3aP97hXxAX/l13z2vXeP/yTrI3p+qZQThO5WFufgIlvxhSZBGwB5KsxpzEKcSmlGc9JjGWGAJD9tPjWKCeklVLA1s7ckkTCskfCn/h3qW3hN1Y3Q7Enpz/Zcv1aa1PpFDqQ0AKKYj3u8uDoHjGGYYe6U9+JZL3FbULQhqad1XUMzcfYU8HX0p1D9trLz2qeLhfHcSJrVvlhSGGLovbMtMp2NXeVmBqnNDF3rbUvetY4YT19tVPS0m626LegG/9LQiKLIiZLbMGyffwhAqH0ZYl0j8Rx3dp9queIDfNRF7TkVvYbVpJVk9y94QaeayWILvcloRwmVGbeFU+LSigPmPHsXuWUqH5dSybkYELzMuL3r6EI+nynEIxbjQSzIaDBgS0iUc3fM5oPHdFi47mDfpPHdQBgsf5CgyjVd5yOjEgiu9ml4J6wO4s+SxoxNRFKrvBN9sSnMk6ulxuAs6ifVJ8blbI4QLzZJSbzr23Ky89AbezdSRKOr4TpACEy7hz5+tscQIg9NBkCE4JWReLBVPdIV0mcGXaGjhBlsUlJAMCY14fjOGKNzuITzNRCGLEPWzkiUqvQfiOCBhvQDQZ9JTvm+gfpaaNKGHVtMtHktcivt7txKc2beneQjlwmrDLAnEGqFbZux17kq4Bl537zegpmUGVluUYzpGm7KTBAf2o44pbAZvHWR9NhJIw8KypNMUU22f+7aDXjzqOIqgOrqpMVvqhrnSdbVdUxtVHcsNBSnWmXhxpoR1ktAss6IYQcODSrbVZYF/XR58j4KMpFh/wettVbCNnTtTWaqJuGjzW3UHZV87efulXaPKTMFKB/lpphRzXa2p0DvYwu60VChDIER6cYKChiUX5qWKE6ftOXSaYGCxPBT9DGoMHmuL8XADgm84OATVeyz3653K5ThbJmcrivnZLGiXCkZ4usFhetg5b/bWnoCf2N9uz/62r9ZnhQoSsbEDeq8PpsZpXDRXiR5VkYdFxuB+m+Q6Z2jTjm6aw6bbApBXGEcM4yRkfqGsZpuv1lM4lh6P5ZBEF92h2g7pDq6u6WQSbrW35hyMMCXUknl93macv0EjBzFO3/tyjQUl3Ziqvzsr6EU7gb/oWlSj8A0OVZ0nxQTQelI6Amc/H1qAT5aPJ3MIQsrAq36ITCnVOraaG7Gfi68sRBjCGoB4cQJ8kwo5TzmTZ20qBG9c8B39SarlhimdUx+zqhDzDi8L0mWxdveexgpr3lwomVDQlAMJyvodAMKqNGZunzxaS85mzEHSGQUr/Hq14vGEhIypqIQcWk6oMNLXjsCnlEV5iZaudZQ/5WFDciYMg176S2+DwyRyGK2Ol4VQ+Ho406rAD6cjTcP3T/1qU83nuUmQiHaplkQsUz6QxOy0yKcyOpjfvgfP4oyCQWrLE5EU+NJAU2oqpB9SmaBYUn5PIkQf4V0qthQV8cENEgedpUFXQj3EANObTCofoUXcY0PhmaEGQSwxLTD+UVyGo0m8hlz26H5LvTc4oOeUqrr6tzNVlLBQQxBnTxKR+d1SqNr0Z29/oH/na9U0Z6Wheieseir8gFQ+lsTrzGSO9tgNOaSrwZ7NqS+f+LjSL4Dk0JGDK9xzgwLRAoePPzMJ2VQ3SVvwZjMQmHjLTFfOKG7i7iZicQjT3LUdy56E7JeyJb9p4IJyIWEqlTkln9tmj5Vks//tmWcYjzYcXWDdNRhL8bF8o8sQ7/Scp3JeFY8ER5KArWM3J3RKPsOiAmlWsV7UYfCQovJ81W6swpdnKBCXIeN4SyHyRaaS78l4riyb8VXpJFiDmziRNeEk5/7MQeJ4Q6CC0GYMbPblNhgP5MLEhKmbcUOxbgsEKsIgqKpGmm+3Haye7qktP0aeBo3CdLpEkAOUKhf2JQxkHPnDTXAG1cikKHV023fb5+bRcfrpaj3ZpeG8M0hDBod5vCQ4BrfpXEiXQPDLZ/e5eQFsA2S4+gWBif5/yKMOI4qLTctMHTZ5n/rvqaAiMuHZl28+wG9NwrjR2XSRcoh+ruFT2O0BvMtz7236CYHidOEU9vryxRYA0VKYl9j1jugT/BBonu32XQu93WBl6xCqsVoy5eWmRBiXWPZMoAJP9aaSpJLFZRLdrNZtu4kQk+pvuUcFca/Uog0fqgJhXWI2F097YNgDVmyiCMO7L2600375Ug9Q9OMDFWUQeNCy5HykdWQXIV3iNc4EnM+7+xYQzxhCyf0EJnCktD0nXQZddGN5FwuAxlxIl4rSi5OR56BtEboJFd0XdT1JveyEu267+lgiIojYYLh88iVgLJ9z4VuqokikRnlXU1iSD43uiymgXdMIkBl4qhMOSJyF2c2iOPHJ12AaIHRLVWbj5b5qGIQ2oIXpXUY30D9DNVcNIhofO4vVm8wt4xCtGGV3lwuLjFyIlpTux0r3PRwv3zbb9hdofGcJ6su/VPogz5ZkxrhCEb6lKqk+HlEMu6bjq4NgWFDD/pb67xkmnpV/vUy5woSgKyyq0Zd7uUwR9iVVKS7XOfbHjxCMguk0FNmoNNiOqgzfB4fJ2GjPK/CHGy7kS8wY9Hij6vuuHuwkWFbr49r2cFP/hm+NmEJaKc0aW14xIn6VejzeSCTgTQmGqzdfWYUIULk8uoUJ0Hao7v6Cwfigk+SrXHDRhJGpwe+TqcXjMPtSCd1PKloeWQ1sOypweyGFIciGfNGcEJlYaJg6NWkxX6zQw3gGopAtdw4y8bXWLsf8DxNyGFUs0cJtXCjGw4wkmY/rAxpPuwFj2480y3SwM1GIgIzd+PgK8oK/QBfP6QaMgBQH/7hu0d1h9wR7qwkp7LbZYk55DLYsQpDgtgn2JZiMi0M5kLh0v607bl1ueLuSUXK6rp+gGOtEUTp7r+cG1EF4HWVJxj4wI58mkdNMwvlFB4uCwWAC3fhHlOF/jd5ByQAiH/iL853QMHWWj1Zb7/vf7JejEl3O/Ensj7/pnxBMfeKaSZD+NutG+bY90I3KhYipldRN/OHDCQDAOU4x0W1XOx7BLZaG64qr3BkhLSwO5ewavniFJILDj3esUdhWIvpYUS750u92vBCJ3lNouq/t5s7O6AYF9e9kXg1h3Bcr+ZFJGAdcZcZxet+DWdVSmaA1vII2cw77aMtxGsZVGeKiqhbtOWB9lQslU6si104nab9utwfoNvKdN5T/cCiZP7/aPO0YTnfszBEQEazTs1fjEiFKqJha5yabzTKsDDJF6MguEIZ2imPSQC84clBTIZqkOePnvctNfncSy01cTL3BxD3VZPQWAZ+txqjQVXyEmcVEGgLgyxjh6OuiuF4R+iXy1HzUM9S/1K3piJ053GGe20f8T3uvjuty6sUCpaS1X+YHL00YmJQVRr7KNeu2Cgo8S6kRvuza3Y6qFrc0kH0jb3xmtXAapFFurAyYD8qDhkqYmJo6YwGsvQK1vn96XnfA8lMWjs6bDsauor75B7IxRYhQKZty+ooVCCPkwsTUuRY7c0Zg/UD/QTFj3k+9P2y3PGI97akCY9MCJDRnMyA5S671vCrHRPOBZb/OEsAcbuvtUzOUKEtZLzANF96igNOwMC91XoxM/w9farpH0flm3z6dUeIz2Ehbkbt/9FWHQwNOu32Y6zNlSodgLtQSb503GkK/1EU86rdBlXTjVqsKVOxz6gUqd4WmPGyP8kCahnph+MGzGX94Er0LNR6MjQvfNKyfmpnUwGxKwgGSiIOrH0l+BKWTdImRCSRPLoxMzURVmdQ9UDaut8JWTXWCKF7Xv1nzcotVk49uInVvgqzCi3LJbtY+JonhRTHWvVeUVjGnu4Lgf3vGyon41GRvR79k94QSykPMzMI0tmKGqvgWm0gvQszURemWLW8w1XxTIVSnMVXyHzb3w4zOjQ5ArpnsyZKQ1nxYaC6tfIWRqUvZIFhQEfVOL/Tzijh6W6Pzdbfoz0/ocwvc8/F3UPMKIZOOMuKwLdcJ9L2mXm+wyrGg8B0Urh1J9DsUUZwRju9iQ1onF0qmLk8H9Vf9dlM9w41IRdas57aVVR0SYPt1hps3IWtfFG7KO03hwk0ImbrU46f/vl9xvPmZZYBhEx7Td9ZU3Vr05M8fu+n9UUEyNgMNbOmzFxamLvPRmuC842/eMlIwJ1TZKN/qZLWgTsys/tNOPEg2WS3pRvHUWbiYGmyFKd1pDEYJ9VnR64rpmTNJ8Aeo/U14tGWQkJWFXfnAHvykXMQ2bF5uv7QavZLoNJo8jn6supdhQjjapjhWzwxgERSHChZq8uoPIuMKH5MONYJ02BWFuhRMBSg4Uv39KL4yFJ0pDc8nzjBfDir/M79bESK1sC9NDDD/8dd/ubHmLJkqZqaT76uHnmq6n5qHvt6cFCUmJCyVljnje28clko5mM7EdO6hZkghxYEDJNzjyHoVfL9LGIZvC3NBJA6hXZokTWQQftPv6N6yiHVWquLIR8IhmSmQA3h1ihwKs51bEPwCPSQX7iUdy4mUu1ryogUdI0nYD4BJEX+tStvi5Cvnk7nwbVLttvaOp/oWgFBIDlxRn0LZVYgiYI14l+MmhCWuWCpiifWYCxHTZNk4t6BKjaIQpQzWKcfGngpu7HgcNYOJNcfA9DykxI2XFuMMjxMSpsmUg8edb+pfozdUkLE+Mf2rP7bUotiJyaHdWoAM9SnziJSWIVaKFvLlnerw09HjcfQQkSw00AmiFrAmekSzP7cvvF1D9GeDUc6HT2cfOupSqFXK4xBjd22XBb5WSTFZdzxUfqRG/JYrNjgv2wmNDE/pMlPdCK3bWfUfRJ0zammsxMlNaJgmE51PqUkwAWMoSkY9+QlA5qred22zP9qoBPmXAuvsd7qyoUkiuBKGw217T81k9dKBfZ3BEIBDp+Ct3C2fQD6DZAZyveQlBVx6LvxLA8fXaZttdVhig0T7Ug2TZSpEvlS/euemRYjhXW4fjA+giwcj3EtD/909GEhqvm+/1pQ/uINk1aqxyF7N6o88pLnWrMjs9SBCuy90S0Ove4Au2oWEGyBRwM0jK8cyKWD7h/XdYVI1QpUspNRf8HNWKDuEcmlU6d4PPBrQ/+whm5voPDttE68rKYVmvulB5nHZEpZS89ORoKyzWEDCVvfQ+m9m1PnN+/wPlEYeBlLz7BOPQzwRDJcdS4q1uRAujYndvG+AUF2vXlHDs91ij4Qmf7LZgs3lyWxGl9+upy1WWfs9yPgSS1w22cjzGhR0P7V31s4+owMdmX61W+iw+L6tIIMmxc5a3mSKqlqol8Zg5jsZi7bUimFjdEaFbR4NUC+Rqx5wAZZzMcctFSYEjKOXViUQF8qFgmkMGJ3Sfex29K9HN839ffOPvqEIzerQv7KiwOuqexpQcM3WDWrmSBMTglyCP0LqpVwxNk84mMYIHN7xmqiirZ6e7xg2aAyu1APGs0N+bTf1fTuZP5gQcU8rN+wd0yKTCePSGBmEXKFg5JXIGdWmOWROtiMRjbXFjrwaVBBtNl9M8iBd58KzpDasSGcT9k91hTxJVzaLblb1tllZfTxGeFqEwmyilubfrhPt4MF4Hw0PHoRZSeF/bK0vGSc81EJpVhjWOBvUDp3TVvWMMWAH9jXl3Xm5HzRch/aS9i4jFB9N4ncOWN5kanTzXFeA4sRZ9NGua2WXDD7MnJiehkzUgaMsvex9xlEKr9IUsRkCwEXVsUXTIEmXKkrNSPIjWZ6i0oqH/Mew/BDtFzua9X5g6DiEXQlYtgO/dmwifdmBCUf1RxJHF5vpqmaZ1xGyWjN66XUx4E1YlqaY9NbwcN5a87ozRbeRaqN6OwgIiBdZ/1BTWpnW0ypo7GBHfb6xLJKbcCvByhj37VV3B5qu43mf5Vr9pSAJQIT9wxCYteRCsDSFdpO+YYlFRTUbMQJJbrsfS48d0C1tt8J660hIJA95ULyDXLJqyoVgacrYFHMgNVof9hzRdKZAZ7tv90K2ECj8dHR+ShKxS+VWWIPMEu6SRSXp+MSKyGpBnHqzpN82K7dc3dwvmlsCECahm/5kMS4fqKB0y1GqIEBdQNj+8FStqyNyULCckT+BQOS4EGJlHqeCem+e6Ah71r850zxubDoIYIkkfN/dN0cOn1BGCpvs+x0+MdkvhECZx0jUR9/YTf2A3ZXVDWsRLSECuWufhit9s+oscekUERiUa7N8Uada4XCZHG6y6bOEWMC3YcLw3FJ9lCgq3TYOrOgWId8J2rC6Ij7wDXRFCiFU5mCxuYkIFwKvewhoo9eC2CI+OGeOdnAy1XPfex1C8MzKJSwgRqOFMCnzJHVBwDpEOGNkFGs3dMf6l64VR+uLtmfcxFy3KwuS8ciXVCpzfntGDpTnE0lIzF++chugjKaebTQ+uGi/HqsqxHlIrh2MdX1zR35VuZyEnqI7yU/UBKEDoau0ZRwRNDAm8qbW8+/8NBilQWZtalGAAhjAQliUOWZvIpXTVauGUuyeO22t7RrEBUebUj5TkwLc5tGyPw1pJ8vFiREW2YUwKfM006PdAMTfRs9WlVBufEsJfwS+/4wZ0b6a9dtBXP08XvSHwmcmREo6j/OQvdxUBxSMb/rW8vQ/tRQQXurN3rVFf/Isf6wbUAiPMs+yUUyYs4f97EH31dGP9Sj89kfQoyBZjJyxUD7aC59IYneWp3Kth03V22oNW6gY2KyOp/yD4AvlmLvDXGg1C9rLxvy1L7CCCqFS5lnhNN8+QA53GPCfUQyAjfW2Gil4sEanOomVZ8SdMQ7aE2ke1Hi1lnKcRoKzmqCybvaUONACuS8MJNJh/+iu8lXDMKhJORSULBTX1t5YiDQrtMlcUQiSD6u/Pwy/npJYWQzD9BFg8NSeRp8kD3k+RbxEblH8tiQ2q9SVZ6/uv1L8g+MDkgYkX4uIpzGgbduX9v1GkWqJXYo+vxDyJB2rHAM1O3o7q4xEx8Xg0m5ZebjVH77CqG577MESZs46oEMWOrVC6JM53B6Hmz2o3qxaGOuCrfzD9oHatkGCCvIqFpbpowcH9UY5M8t8iAzF375EaqWczAs8uigs7iHxVv8GQ78SlexdtXocZYT/tGQv15G+URZfcCFO0nnybJo5rAxOiqr2qt6+jG4wfyRvFqy25vUNyHhXIQcSM9QP1g0get1+ZSpQYiB30b2I1gVF8x30jqMf++1q37RHFTf1eyHXSS2ZiYCPXwh9MldFOZuKQHmDNfKgtj7w4W37bQuST/WWLtRnenUv1X11VoTpK2Z6aYJc8IuTuK0n4gmc1uxC7QyCj9PiXnqSZreqrH72ielKCCgLs/bMr2yKEC7cSUzuR0Gzdbt9iAbGLdy4LUzbGQxVK8xOqCU5mW4XIc6Qys5rfQQAflYSxXXuvFA/VQdeCvOEZN13uz39HKpK3rbs9flNteyA4WhpHcZ8LAA+lcRyXYyii4O/ycfuAPcHinHR+R3mowOBut9V/ZHDgQqKS6bksta3kECwFNZkbkauOdDYH6hEov+eJWlMF2tLV1iKRzw5zABPtEGCNjZcI3kZkxwGJHibwklx3Hb1S9vRxcH2U9O3dkkZD1O/Y1zGbOAfRHK1bgK+QY3hVyVRO4+d2Dp8BaOf++6JzYQjq+VGnfeqq4eodFFzGYXQeSQMFESQKOxuxJfe0BwJaTKngCSl0g/b+jcq8tn9Ic3yOHpFv/6BZcPsCokOEn34Ag1W+kWrkxgeB+ktF8micBiqFOFP5rke0aLsPVPzaIs+tlSVdjaxaitXdlPZb8ft4+i/DOpGSi4qfT5euNtCnMypBJMPjaVoX+OZZEVW8iY9uuoFdUiRdJATnKOhCh1i+K3+bUGji8slYUzSgRzggHr9ag+zJcTFNXOkch3//6JgkrNHlddPD5+ecCfhgznGpTX1jRsrVkJ/SGeAkAIi7Y5mmzZ706dA3zSErGFs7l3YIxXCl6S/YCb97Tu4WUPVkSImKFwlADaPvKocyksGIV1jgTpXogzCaql0iZEPoE8hRMm8mAB9zjeQOhUlVp2r0QFN9u7b+/26RcaZgACo+woD+yj/PJm/OwniKOcG/SII9UXA9UBxImEwe7Wq7KiUV9ynuLoQjRdlqSMLwKxCqJJ5Afmaawv36zc8saWuKaHnetE8RDZOc/b4QHmGLs2x8UoSoheU2QiwAD8shCBJp0md8isjRqztBIOyMhPd9LBd3gmdu16DS80uFUcXKITzU/JG0re3gYhRISRJ+sxM7KJAvYkuWkAhdZpHANjsRmOzPzfzt023n23ATbfwI/MyyY7ES4GoYUHqIp2g+caM2z339M2zr3wzPZcKwa4VLA/ixa6VbNUwOVfh5Liq7SO70J1lOX0IQ9p1g8iu3+7gjDGHZgbt/XS5hFwDBrIQemReZu4KDYsRiHHd28akKCKEaLAdBEYnkqbPwJMhsnv1+0PqXKsl7AtJ9ka5yF1M/YWoStr19aZlJ2SgHLOZsQAcGZd73iAlb5Mv2cJwLSc0ySLOnXz/gE36VDe46hobXHpYD7W1prumn8xxYWIsZtQ348C3YC6FsCKLuHSz9s/NvqXE8fCAFi5RWcoelSuKh1/cVvLcqgGcXV1ORrcR/a80zoPY0n9od1YIN7KgxqJ0XM37A1X+dzynwDTwnNq4/kkapn315OexBRVMmQVF+9QTMOMWbiQdKB/NRFrYhrB8qK3SFCThp8Do7zA+tY4LvvjNByrlQJgnz9VvRj+asqDIsKIjuAP9wAJnqEhm8SDXAbl2eEa+HReOJNxIGESns/770oUDTOJeV922H+ZLF13bPm4OR8DaJIiqWcZLsnOc+oUQWSQmcRBt+g/kr6um3v8G3xITfQYNs6tmmxIGlh+33UEDOEv48yEjec4tdEg6kxplCdpfYW/YA/UD6noGhkSzat2ZLK3Vw2cNaXIXWWwwqCuEEwljwaMx5aDecGawJMHKdNfDYMnGyM/0R9oeCO3j7Y0pQt6d5pmgL2yjuxRiJIzzkjkyAfAfLk/QY/7U/NpKrru0ssZt+1CdHQlOqZAdrs6XxPJ5DifcyCJNHYiTV0mU13a1Ncui73UOil5WBAgZVWa8yPFt3go+kQRv+nH5dAAnCVibwulfOajE9wVKzQW3l2XPX10+HknJhbqCntowhEsSKGTPtfu/exqfFUu0SBuaivFU2uGjYZVJ390P2y/00UOpM42jj10LxwyczK4JquhjD0uo091FkE+7ZUf79Iv4/UkQT3Upwlz0SuiHoCCBQj5FqGa1H4QgLVsDCwIKn/3DiSVjkFy2tqKmPignOkthSMKlauLsWVNgtOv3ROVxdAXFst1EeX0YH55qqgWxbQuGlHsBeLhWwpUs0nLEBlLgZCb7Q990XQ10ByLny8gnv6g2++ap7Y4NDoK2qIoN7b0aKrhTwpMsgPOf7AnQ7g4K9XGZjkrLgwUTFgdHGIWwpa4lSvngL/yEJJJTEDLTWGARb0CcgiBZd7aQc+l3++vRIFeZsO9toark701YktRJ5TKJe1tXcOiIblgtTCXROfYCIRiXELBrsrS2BIKzEHokVbzJKJ6CAVMHAA5UCEBsy4aJ/KAudX3o9kJq92K6QxbORb7ISUYYEKYklVDFDNMdgQwJgJlJCrC3eCo/kaG0QvC37dctJeDnI45LEPLFpEuiKVwfCF8SukVuxQN/vJ3bhlM4yGIdfWyfKaQj7+yG0HnRNrt62q7k3z6PIyf7LlbCr1ECucFMRmpMKEBfQLYXzYEuYzaBYSaFqw26A9XhG+emORtgliHNcK6XBPLAwCmENUmPbHT6GQrsySozy4sSUNjVI+NzpnyuZaP2EE6OLV68xgy4Y8KjpJOOLjH8xVXR9erfe1ADqbS/XNfb9mstnO7LrvrK1cKR0Y8OCeqa3ZF85QssDwohUdLpxuLzfFs9QYGdZZhbTH0pY/H7lKhuIQ8nSD0ThzZWmbdt4MGY8CeBhpPB6s26buhB2S25Agr933uE+BEB4vzZR7pAXIRMnoslm23FjygbT+PYnG/6XfThvt22D2u62KmmrPe3Z+a7OkkXiqIUnMaTqBBDNLRTmReHwvlFaJMFb/2nbrEV9ZjrDgIcWXTR3q0qNy1c4N6F0GAG4ypf38JvScJ5njubeEdfOmcUvBU4RkG3e6YS192dz3SPuAg4XvRmIe3dN4CUQpvkCfh8bAh5zj07pTK/oxXwANUBlEsGmtdM0YUSeZi1HkZNPuoZJmBCnaQzjcLwaMgPzv0opfaQLniHJa8T4qm+cn0+21vEISVvyVNwL5qaX5wE8CIuRpSuVZ677A88JUjzjK4UljwYfb1jS5/jAATEcQiMGppJSzBqoU1SWamOpS7sw6GQrQE23U8k/FmUa6rfH1J1Z/lS38StnHAmUXans4ngdQ+HTMu5heRcXW+mFn+7erA7OtK4CVK7svN431YOd0colFQ1jdNTB6keRLtVygY19QRCaadPTFk61ZNJ0pACU/GA3rfAhK92IYTKoozdMuWW9UgcaVofL+cXcQsh6G7DffiCm24hZMqiVE5Vwq4po5vnanMPrabMJOXApXppagftvm0P1dTBnvJBCCCXr7UvDPEeTqiUBdUOerrb4dU7MO1pLnseeW2UxHjo7XllOiSVlYv0Lv7YhFBJp0qHj+0nCo4tiHhgBtMfMznlVVaBHkTTqPPunjsUklPxd5OFzOC1xVMs2FIUwqQsSoysjiyQrWQqXQ8VDfwTKT6sT+Obrn05/ubikI2lrbi9lozIIcKkpL5nWuEyhmpi0VNSr/y5BnVpKNTW9OMf2qMTmSzcv2PB7bMQAmUZZ8Vwu8HsBDL36aldWfMboD8fxgTyPWIApV4C5vBwUCiUZZKm2sly7qm1/Eo9AH6g0lkSXYtW6Hd8+IOdn+9Lw8sSCiUdJZ8qhI5agCX9PezHNzWH9nloh3QhZcpf/oLmTiEUSuifJnKePdwD0PeA84W4vcfcbbt3UrOD9s1sSRHEVrDH8Toe83FclC5T4/Lrp8rqlQpJCOUpWv/G6X79QJX01rODD1kHZoueL5xlhTxJX49z0LWr2vaZJxO2CmI6+mBXNyb9tn20W5452CyLQzgUxioB+uj4fDAlB6PSdwiOrZUH/rm6p9gYKx0NP3Rg4/tYXSEyiUpxJ+vDKWD+J+TJMovL+TSCqcjAd6cRv6SHYUoyCUvzPU6uwpgcCyZdzOQQ7mSZJXoaEFly64KKI4Qg0Gsi5sHuZCPotB7qqdpGkPn6InqKYdTCmywzwGiHMt9e5+inmhVt0px6OtgVSOfhszYNKe8zxuEtmPQUQpgss8JdnBtGlaxeV/eAloJRMQUqzEUbPRYYIXFRx0sEXN4CCGeS6ri8cAXRQ9UxqOxM59EFzEye6DjQJlqtnVVm11nd+aM0pkMmIMosWhhQkVYKZ7JEHS00JXowEATf/aPnygioDpYtkBLb9gGMi+kmuiBZGdJUq3SpvUf+KIVBaQ3SZIvbbyittyxrmVLxiaK2uh9rfsq9oFXP/HDioCFWsYhSzHAcidfw75WBzN0dPY1rejfUjKmSMshrMIE/ts2WisPlEXtY5ZH4/dUMn0fCNRS3humHJZZQ40M/GPM0XUS28h9w7431w5qVHXmIBUZZLGGCoItWCksShynHFQS9i4ptS+AaClnwclC1EqokDE0GG5yJK0dIF2SSJVQCYBKlECWpok5H04R606yaPZCH7d0Gk/84js5361VXfdk77tSyrm3IzEGVS44JgLqVwpekcxVqOmyvedj+tqoerS+AwoaEpUg/PNw7RO5FXfWgDE/dw4Ji5SAyv+CUXQp5stRYl8obRG6rnGEuyF2Dk5lbmNaHJ3pos5AEU51AYoD2gjhADCiFOUlNgc6mjdqgiwZax2BfNhRHnFhPHWjDiDiss+kbEWOEXQplsuQR8eQ0F9XdHQaaOXZJu+eanw7brDs80Ge66VOzbBMXIUu/ZMm2G0u/UjiTZZ6OYraIjkAhR/gvVD5Sg3fSVn+4pxhZzfrqkLw/jEN8Q0dkEOFNUhyNxQaXJ69X9fY38MtTqDWvGrvaxt64E2AJBC8GvfnpVkSpEOV2vXQwfnNCn6SDJcWsZPvUb7dUkZzRZ00f115kv92U0WKB5kLSSUhBW6ilghbcoFJIlGVO3900BGyAvL+gOru/p/idUot6PHy8rg7QmJ/NHnWQ9k6y1B1xuBQqZYm1pFQA9MvrXcUeOKAqp3kZ3VQzKYfzvmu7IwNR6h8DDUS1H57IT0kCeCH9ml3aXjS7dfuM08TRRbvft4Nr8IKb+LetXOxqzQ9GSPjpSNCmllWS/3VLX1Y19NW5MtHlpu13O2dufl09b1goREivSZCIpbIy5L76kZ9LPp5lsq2CjO6mGUZ8iSn0SeqguHnPAvKT1GFMQEFbMCDJVzxm/IVJpC4pFc2+sCFUY+loJ6LjvUGWZY4HusUT7Z0kpDnS1rNhAahcOvIkVbeZ0yH9kUGJT7//nxiDUrV2cejuB0HkzV2POvcfoHFd8wW6rn+FgloRZhKYcd73qv0j7zvqJDgz2egg8YSWH5qjOxY4iG67AwpGe4O+j+9q7ZR9GQ0JxDEneZQ2Eu+oTtuDeBN9/P2/nhu7Ko/4Jsv647vZwenSt4YhX+mYk/TT6ILLtri66yBqRfUP1f0FHJPqiScfuuyhjpxV/kUQbOsP9dtKx52kjgTjDBmGbtnhrd3Uq8f25fd/Miqbym22frXv77sIJZaJ75s8an5/ajyVc966atmd+7Jh0U8YqeUTU3VcaWDQm92s689CNldGL5nNaX5EWg4Dg40B7t7//r/Bx62r6W7Alppe3j1j3QPQdmFySZip++SscxailjOZEQXMJghvqeDgvUOapRP30h8r1G9zPqAOMQrXPKXxGv8gYju2JPWQ8bge/lzfD6VaU4PMir6wEDuL0YGEoiZryZwWtkE6RUW+pCeToW5zzEk6WxY75mT79Ps/d5BM27fUtcW6FMykk5PpKm5R5rzpINO0slgSA4L+RplK0E7VqA1w+/t/3zJKbLVm5eWcKpKnu1p2IQsD/qA2u1xCa4HIUWYStqnMlqmoXaZ/hEl3y7tSsAC+cP8oFg3XdIF2KNDmso1h3ihMJvP2kChHMondaVkMNe35ftdGb3//z4cHDGk1RW24qgqr/OMGmEkbIY9mtEGG4XmyVAMoPpEEbeqExtU1xaPdBtq6Fw313WcplSyD6bNggDu7wZ6b/qQhOOnMypAvsLjKTMK2ih1t8qqr+18BsH2hX51k9Nc533zpaudD9O89FB2O5rQqRHRP66XFFaeQTIK1wnpuwNju6i/RZ8roLxVUK6nU5z4Ic6JxTnuguq7fHXGmszCMZr5UHgFvX2YSs+H84cqjx/6OTrBaY+kS02ffPD/120FY4mf6NZ1dg77CMIn+ET3bIkRVZnGWxbO1TEK1Th1i5R2lj/8dXsX047JMwwupe6i60X7MZ6MeSv/1W3wovjYSrHWWSrD+O1XRj7wu31KHg2zPsz6KA9UoFb8QhYJUEqw4iQ9swIeSKK21ySb8yMG8AtPSKfN/oCfYOeSJAEDIwtrkS7r1mt+YhGltRvQDe0gCnPZSbVaUPRo4U2RHKlJXdIdmnN88yMDKTiB9VSPPr5TEaV26CuQt/D12EdX8+35bA8Kq8+gVRehRlp09f6aTx5DJFfAX/tEMD66VRGejMmnQPlPSBnLvY3VPQRjTdLyyUxkAlLV0q0f2f5Cfni6WAGqGn46EZ1O6D+xTTb973+/b6FPT/lIBrJKbodQX3li7esQ3doy6DIo/gyahrz5DTFQSn/NUjfaozfYXSvRodlgpiK7wqqVT7RwoxJqSTt9ZEtINmXSJXctNh5IAnQPwMOzxK9hCrDcNjCEKEbYeSD7Ndq5VH8TtsZMhX/DhyZCSmJwXLk8MIkRvwGlg/FdxTDz+rjYD0Hk/bA/QxlJJaM7Lce3BHiPbmhr4vl7TmSitD2w+q4B+XR2GbuzUsNIUIeg0Y7X+Ft+XRGmwRKV7rYE8j36mV7NmuhjUiABWmcwdhtLeyrXN9NEAmAhCOfo1WhWfSwJ1mY1itn/rdthavW62j/XmLInLBKAeOxy1plXdZthAOluWI/BMiI6cxfR4KckI2UpCdpm7edpN/4SccVF1v1BJZEVJ3/ePA0rdAvov6u39/EEF1dWlHT8s3Ssdj6dJZ2iDhmpWqrDv2j31cYZbj+4JxcbAOurv7rDUnsyvVci0UduEtgDkKbUEbfpGpcmHhecaW4Z76jro2WAYaz0I7Gj/ffWF57EnZKMgLI+VRVzS2iu1BO1yAkt/RdX0u7a/rzd26qAyDQfw1aOlG00AfUsKCUFSxPGSFSIPjxw1skyooHWgvl0VvdoCdfVErWWpUbA1k3nfd/oRo2wzfqm7hGHw44ncguZ1ZdPrVdU8bGsqlyDemELK/hHqYF0tERRBiw43A/TmYXrkCzM/KJKWjhVJhxKMzy1lEcpmiAVnCRVzNkC51eN+w7Lfs68/SwKBYQvPB8Cw0tEhS0Qf5/hBRWLXUIbdvDRWZesK89jtQ3TVvgyfm1VqtpuQeV8UxPUvWPrXq0qE6YNjRJYJtcWOe9g8PTV19Kmu76nToUj7EZf7j7cNITfakmd8mRcNrGNB4iSJcP1BLow+UK8PtiE3RI6wdo2AxLX2MA1AMXCa7HSQdhMYY+kiMqt0VEg6m0pGxn9X/0LHWFebX6ivp+YaKjv2arOIyxCi3tB50bJdvTqDxeVXmKRCGiJMkWQB6gP9iNKRIelY1ASMIRP455/67qlBzY1+F/XK9kCXy41rLqiC+7KB4c3JBDkPYmmWSys/ntk4QiQdrBhpKh8bzPfpnWHCn5giZ9qBlQrkq35bu/b22NHShJgj2i2JryvgctdRIgHMHGl/FCMpnruitkiZEgnXbRc1ESup1juczkhViH0k5hILVm2oN43E8SxLx6UE+pTb6kt7oGyPkuBV9eAe0kX91Nz/0hxtJMJKAr24QjZ8nySEZ9qJuGGGRdmXOoB2u6/AOICbxCe6OoPQzU/Vbj24AE0mSBnVFIHEWu2nrCDLGQnfKh1FG36q9nRha4qR3Q5zktwkdJPqh9bBbDjZnp5JxRA7DbpGmb9D4GskcVxNvH5/BIf8X/+tvrM1QZbBHh3GdTKa+APpnZANoGKor+9u8zWSMK7pVw+jNspedKSKFxH0SVIQAMYNnjKDhUzXMJDl6CYlIVJAplgyRoRfUmkkmMOq2a0j6C/+1KDY/dc/ES7zv6y0LJIlb21OLUbCN6VyYT51LSaj0f/6mZLL5vf/B38uuu5ZaOdWbHZuHg+P7VOzW5/qgH/76+et9oItOm+1cwngRvLKDR2q3fzS38kWifq16MNXt0T+rl5zmHL54P2I23kynsdks6ESJMjoL7+m2pIubGF52xNYxCAVekxcC1rcFmqJfcSXO5fAbbQ8pYoKg/tmdLPLNdW79MFt0L1NFgBrFns9OVccVPEyFtlXXCo+l8RukzuHkmsox22otu1XXO4qo3Ko3DbPsrp5tdk1RyqFEO4Jk0bw2+zxtj1X43HG/Aa5JJZnsLukTIPZfqRwd97fdTMtbpMGkXr1EpmOR+65npzH7Uh2bNVa7ddUgqOk0/R4LiZmn/SSnunXnH364ezTur2vox92DJiMqfsLeURMN/TukpDgcond1CAOU7jzbr/vqaHbPdYYCiasHgEzy+pFdjcsNG0Tiq99Csq9plyKUfbTkwieqxEt+eoFrmg9lqJcXV7XT5LlllZuIUOwYmmZDEOgMpfonedOtfyWcsV+/ft/0XE2dG9RwHGtyzMU1gYWHc6G8VFz9x0TMoL/4wlhXo6nKmVA8OF5XW/ofl+0m/t+BeskjdaOZwQcwb9XPjnnqtKrvolbXkgIzws9oYxSr78FbPNd81LtqdeKbbLbVEJq2VRTTKJOQ9Y4pYW2+hBbuESFxG9Yi9gX97evYIse2OeC0WzwboFhwPC+6j177x0PKQLY6ha05R9ScHor0vE4DkF6vt3//n9tm5ZeDiW3hx6KLRCXtJ4bAk2i7q36ehQkg+x+9aJ9AoN/i2w8UiGX6B3FouYXzAc3zXPLvVxcQBes2bl54LfoPyEe6fGSDBAcW8tC4jfTh0cvhS+sBn5Rr+BRAq4NQEp1TVXK8xAJviP36mRJidPwK5QgXmSFmrQE9ER+7NcbBMwSYhH31bi1wPjXRdB5kVIEyRMmi7gbPpIEcerkHMGl6lZ0p95U9mJm9C87HIfjJUCRZCjeZhEzCQJvW4Vg7zQcpUCRj2fKJpCpA33vAyAwLWIeEK4mU6/bagVQ63FpqUKlrrTfKIBvk8TwUmkxSX6s6RnV3X7LcGCDO04PQlYqH1bYuHxu6q/H04o85L2ZJUorA5QdM7JM4zQdtSvaL5g1VbunfscVgYnj6H17/2ChEgEGLyFxitEcvkqF45QjStLJshHK/Q5u9/Q02oc9IDj42duJx9R3zuUUq494lcqQgh1Xko6UJ3KheOZUgfPb8bNKdY7PnSqC21ZEUd5Vm331cHb+Dig8MO/o7akQgAnLgXnx7hnr0MiJCmek+A7gKLrVlEOeGvqRdJ7UdgaUVAa/+8v6npIN66hOs50OIbYzUMBXEDC+xPElcSInUMYaKHwE6jRrLOvMBF9GHdWK7k5FDfh0nxK4TbWziqWJvCNJ0mno2zyyB7hg2bb2uaZKIMsmcYCP9Wm7WrfRDcQdLcYCvkpHKSZTQU+Mdyy+i85zTMeeLNNEZaNIoDUP2ne8gotu6i0wBMNcnn4BF+BT08k05IJbNeUl7dvSESfpKNp1B5/r5pc2ens4PD///p/UuucFcLlPk/bgfLceIFQe8dsiSE3dxIuYZX6NuZyryCYk5eZpUrEoXfz1k3rD+wOv2BUKTcelpIOV5kRTnZ4CmxhBTWdOfz3vtlQaH/mYxiH9ZmHpyr6pCl8miedUkgwB4aa5h9bGVX+H3MqTy7kgPmoCpsT63h/MB0JgKOgSfPDF9EzHscTyzCiRDm8g8d7eHSgNl1Q9NYAu2g+Q8wvgqPSrpiEzwGreyhcvOIYkOIsE8Swfvecu2n/9v1QNdY9V9LkHchnzOR1jUX5g1vAgO0+n4y9vXgQnIRIcplzCx2h+RhLKs1INgfM9lf4DCj7PonNAYKx5wHCcty1do37Tn739gR7X16ppzkpQuoNQcAtKuHmGw2TjYVyF+WO/wRL6TdO1/a8wVIytuKO7RTwD+xm/Zzq0CFKez60ft99LmU4jUVylTlLuU/tUt9Tj4gVkOUWOmfb1W/7yDlNEXvlt6WtLvvXfHQqHdBAJ1UrrsRt4aja/YNT8sd3Rl7QCu8b8JUxuiwr2flb8liRcg9jpuEDscs2TJfqwKHVYY9AJ1Y1+Wc8onbkPZxAhUNs2wOfngP18LIFax6Nm4qsNNgO3Xb9aA/17llEYx/YLKi32W39f7aLP9QP9f1OLpySIwbX4jDQ/I4nQOhFN57qxTvcDxAswwQsI7gpQ4DsacPacWZC4yPgSSYjWIkbyE2UASG3u6t//j5YieZ5Ht/WW7tXwfAaKgE9oJw9aOPNsacFsml5VPB6pmHiEYljy+/9t3YHp4xgp+HbFu4HzJNchYjWThBiVQBvJP72hWEOnkQitMwfLu4XVR80MZDBJgK5SKroU2xt6b21/v0AJCFNqK5nr6jVQ1jiVxGeTulX4ZdsOoO0eXtyZNcD5C+YldrHkGyobHEWisymUWN8AN4268fd/Pp7hjNHbmho2d5pP9Nsuqu3jyRY3TFIiXzIqUfjIEgnQwPgMdWP/SOH5ZWNTaVpkPMFdoTBz2HuQpOnXXNP5wDs5eW+lCqHgltkiYTLH2SRm54mrPWAJQvemptqaWodbWD8CNy0KzltOK3PD6SLEhNvKtnplQPg5mfEs5dhtNxRqondt90tPVawpYyaWOlTAe3gEswqXFzUYZF8Yc+Xhk7pGRZRI0M5VLqe6ppLwnsL25wbgfwrZcfSGfjqswpxKARXbFcsSHbdFOoTQCRxF4sdR8PcmgbtQ4wyXmjTu3W5//699x8ucTKM9YijFm43oAUB6x4IuJ7EppAIwegkyrPlU5Xgq4zSTrbrtzzVdlqfeRksKORBt3NW7dTtcq49rOrs1wag8dz0JQVPofMnsxeB+CZsSZCCZmVyBLBmdb56q7W+Yw0FmhjkVA9GUK6abfd/tKfGeAOOzJESv2O54FkAMdDAJ6UU+XvxzcDpB9npfr+ipUZgwBV1+bgTo/nMh125O6+0yiHxi7BzchyNEUSmEyrSEsL6jVDUgVFW/VE+//+djg3FcTnGrq90m5afqBZrBk1tlguSBFr1COP8KlzKFPpDY0HX1FgJu//rngF+IdU43neqUXwdgnGdhEZJe1KLBS4EiTjiUdJpyQhH8lcHNP1ZdRYXJpgURH5f8YYJe+DZiN6A8AG8wXs5+wqpMS126BQb1JlCWpx6bil6Ntm1EeXz4dal20iHrS5UsWQjmfI8kpkOSY8h91XbUc0vLOKbEPKHl/lVC3DHPBn1SOPwaXVjPYvqspGtqNowb6uCnSt04DAwYpifkDwpY2+bXs/O/A/H0G+YXhQ4yNMx5leIdx/N5CjlPmYwMfaDjwb/F7JROmkbn9/TUJnPmtxVfqnrWPukQqIB1o/KuUfirc7E8S7I0lakSFQP/+m/NExPz4jT6qf+tfxq+tyUphRCgNc/hfbQ8DpBCqKSzmNwRde6ZuPyh27NzMqa4FYbdA+Rs89D0T6fSciHrU7PEy1N8mEQOo8bQeNO/oErZPD5iI1+o6Hr1GSKzw5f/lwwBwYxZus9CpqRT6TFIAvi9aXccIXWRRJe4ztztXlfbtvteZgN6ldQLFipQDwidMoMF7QD5ZgtK9Cb3XfUIve2Y2rl1V+3W1ARXzYaquAr13CRuq2+rbvJkC4tmn1AyPyAXt7PUGBeJelDOWkr4/arGti+hf/2inqg24948HIWf8ttcFLtn9m8EE8RFIVJmWZxMvZUbkLupl6v6+xqtbhK9g37yt1CCIWbBoMdoPx8fnbcQKulILlSf00/oO6o1KKuz/ncB3bLn9WH45L/LqNsOI/1qbjyMFFolNbrOteh9//j7P//1T2wd0MolMehe67raUW1mpe4GDMW36smw1cTCwqtAPSkESzqekYUXQyZa1G1AVHSQbqOwfWwDMEJQj4yV0pCWpdRLWkHcHAjRMssEfXZNj2MbvaN7joW8KanK3Tb3I6f61fZhwwYPRzj5IL0AZZkfPlY1Pj3hWWaKouucJ/O6X0OqCAKPFNvkPjmYF+bJx8icLOTdmcVekwdfwrfMlBqRZ5+q/WMF7qcdcadUQ2HdfP+1dqY89E6pcDvW5aRoGQKIzZbcGzK04sK3zFSejaa4L80vcAoSfY60VEdl5RKqKqTWzbmf8w0s+SlJ/Ia3ktMMgFozGKdYyGuq3uhX8Fvbf0tZIQSlb22LPM+IJ6hCuQTaW4rv8w2Eg8AsbOg3pVQg4sFsBdxBxYATeplpKsZ5SMENwdAFJyV+bXo80tidOE33qxpqPGcqoebkbSP1wNJENwREkTFHzqfCjSgu3MtMF2nionjzbx9rqkygPPNIPwj+N5ryzOZJbN//vOOGLpesOQ2CuLAuMzMiqjDPrbuHTU+vQUX45V+gYTy8rz+WVQrIdFaWc0E6gI4kgdtQJjhSC4IyZ71drWE+x/YtaE1sYrG3e1FvOog9YHW6/CpvdDKJ3EZ0Xq1k4C1bFdFVos+Ikm/7OHEKg7KKXxNHBV1wtYT4VjiTsC6zPHaa3AyPgl0w9Ysg7BjKRW/rr6NW8PcZhVmRDr8aJh1IYneejrbY7+nr4mkJNKhWDeCnOtcj4BN8GSyX2WHQjsLnY5M0BDCUmSXHbi54hX6Z5aXImlHDhgfz0FeHFrstXP1Hamu72vGt3m3bX73VQBmijaXs+sKPy6EzSRAvsgn47PGxdyOB0ujoqv//Wnub3rixdE3wrxC9mgZUQJA8JA+XkiXbaVu22lLZqEL3goqgFHQwgipGUMrQrhe96D8wmOU1cBvoxCBnFtNo5KJvb4T8X/M+7yFfkqFD+1Q6URcXlVmZ0jF5+H4+H5t5JhJCkxLvLgywcIpSzBNnIV2GOhnI4gFfXS4yTG8reOAAK+SnAU8pMVUdKMBN6Ky6VAUJp2ArE5vPJrFc66idX+LjYg7I2+o+m7NCltbeJaUU+NJhCkfhqqhGRsJOXpQ6ngrkIV8lCeQ67VPdCYbbOywwVzue4uHBZCyQxfPczxmb36DPy1cHTYJ2Ye0kbEdplezE/wn5Mkzpt3e66lTHGZYQu6dAZTXw/no3EMdGD44V4m64cwaIxoXaNLUu5KckoZz+owcrVUgsYhE2CwKefA+9y15ltZkvdWO4wRTcpevVvAuboFvRmSSI09NM5YMzeByIUSnIMSAmvaTPyoTwt/kD0NfjFpx+gBs6YGa3xESjKYRLmCr25UmNy/uR7vMG+K4gPSBe06WHCTMTGz9dHX3Ka6ZdzdyoqTGzPmwbzAhfmVAtFXUqbUL5acuaItsyW60QkWbQEWrqudgF/IjpRMKTgcnRm7AsKVt2hOLX+abO92yZxnsBH76vH3BleBPW4108wM22vKob6nrEDseK1NQInie5wrNUs4EWHQoSqIeis/TjeMiM6QRYvi2S6dIWIDTF9o8OEzAhXSq23paPbl5QxQu28+YWkInA987KFYPLzCX/xrDJSe8onmSC8am64K3Y7lrEuxfUynpP/5nyK7R49ZjEfz6H3sGh3JqrVoWyj5v4u4vlNGHvZvoJSR5VyhXVHzklDayb3lW9Njav5tpeZQhDV25kWXYMsi7A+EyJnEl13gYY5oJtsSsMIzwOUF6WzJNrLxT6BGpkttRjPcu6gRN2YBLLbB6VlmNFvbQxpTNKJ0VGPyejV9hOvwe2Jsd3rVTugVRV6uprYodUMshC6JfKj3s9UTOeg/tz+YilfeS9b3aoJtkoo9XLryrW9+oFfCmTuzmHAbloG87hQEK7VH7SQ3U7zs7HapHzcEAp7b0oWRpSAmdVz+tquz1gN31f0MOM5ya4/IibQr1Uvu5nKC+Y6kyHepvtKbcECqSrxd47aW678UA7XWpl4bcHE6fUpZBTRujUZuTNR5OQDtSBYECWDbXAeYm1U0w5+J8xgnap6Iw6vY0aznlPyJcqwAirxcg0Bq3sHTd39R6CmFjXL8DmNRLDz7FxkUvZlHBPYHfIhWJZd5QwnA02zjmgaK/yAgLnaZJ4F6BffL/0dpoRsgGlrSjgpk6olyrUnTEnshhWy/l2wewP7L+vwKLFiLzzOJkaNrmsUmPgGW11HG+dhHoJb5KOSERBm8FxMMetAKtWvvc5v8Y8oHtQBjzUgqoP8EShq7dROqWjR+eSMK6CqJ87Y+9MV7koIQ0ZUOt7kt3y4FKqXiCGn6eWwFnyVFlHvDzlEf6lohvVeZo1ZdFs6c/OTYGii+YyAnOBU/hsLGJbgSOfCOtSqVjprv7+mfJs/pAf+XSQAw6RETne7oAGGXGI3Dy70ikPGEZ3C9sSLjky1r2cQwBmBweD2wYA5lRTg0k19mOrFEcf2/tm/0wt23ea6BiVY6uOFgKRcC5VpIfZrYGgCPYWzcaAhxIVPhvO9eOvA1pT6jL/SpPJqglXW+iXcG/rBU8KtoLKvTfVMms7zFiBwlfzXO5lVq9742CqiFFfHmx+nejhSk3ZdyaoyIWJqWJxXDzbriqK3Rs48wWWx/XDEvFopgK78jmfSfVn6l0X/3aPMaBnZl2AePrUEuRUCrfP6SUsWZe8Q3iu0+hicQgjWN9uKYhPUDiYFLP7rvO0+BlI52VxTfUKFBAoD1GxuckGAx7qPB+LcqynrV0Ac1E0lV9iNOVCwVSUP6XI/GDktNHmsfHJVYGi0nGD6FJm6qlOmDeIwsFUSZoOJpkZq4uwSY4fh2pAvuoEmqoSvJ1PdNEorRzyC12uesjin7YPUXPUkniuqR3qzb12T19R2mGNyJgCP6YoA/Ts6+yhHWUc7x6y5+GBvmeXYYYxQbHLV9CpJLJr1fONz3iPeXwHJi113z6AYBfAhYoiqdcCIw/PlDi1nWpqTsdVnTAx4b/ejldfFlSDP/1/VEs+/UpFgk9vxDutbnkGxd/fWXnDyXcEI4pdGE5pOKWKzNNVIWHCtVYEmutNRXdlm5X4mvyZ97KEzYfYn1GwKuqDQb2zG4td3sMcRqJ5CiNFPkyrTApd1LLk9Tjla++nzW2drbsTnWb7sUyqE7vYGGXbkh6DCYR6iQZRQtKLatsK6kNTHO4HMcTGgSP6VGzm6KO6WU/ZgvcG00J6+W7TQvt4nqeFQsIE9yHqpGLWDd4KC1nDAoly81BvBJPeg0rFCTYQ/GXCryrA6lCollhqJkPznLPrvF5SPw4hrYA+pkXuHWP03bILqFYBwP5ZJvFdHP2oaQJsxybLxHcolkOlXV9weU9NwF3ZrDCTf/paGwxR2513pL1NBRDRwaV20okKp1hO5kBJf6A+BjGpce+d3UM8ky5GBODgbrdnZnqrjPqduZxD14uP374YM99bF7Qj3++t6U/puXzJvDdNXbCIFcT1YBpblb0Za+dV3dwu/7BdtUnAVmAq3/MudkfBrJOO583B+6ff6tsMoYBik2oJWQOq8WkruHewzVCBS/HEkCerH7uGtdJMzuT35SZT+bxLSh4Lhl3C/PhzsSlY9J8XPhi3wspiNMN0Wo4ZRUmrblSKA/lyoAHZ4Ap60iBazzfZOquR4yOPBX6EXYhOucScYLw/dBKx0UYFdCJA+UK9jIKo35Z38EXvVbWZZyZQzSA5MF9WVS8pW9UPWbl6vpjWLgaScTTVn0d8sFAOFnfowvf53Y6KcYww4dek6TM2k0toWlQdUvVjA61nXpkfNugzly2wTllKzjZd8XEwCenBQPbnIituUdbRNdoUT/8dVH9f+95VjY1C7b3Lb9twylMx44xs+RadAgW+xcDercc4oET6MOzdiT5UANRn2V22qrargqJqhFkwfQcwtjZP7lVT3rDa1TObWzf14mCKjaT4e4z7c3Wr6ldUb/9Mt51iGCYboa8jJpKZoVm/rp5SLnZi2OopB2cV4FgS8sOoE3q/hBChZ/ikiBJapXzF+nmZzWvCZbsY8/LFGrMUDiNBPky6w0BLBijWDdTedTwzghYGTWf4K1m5MzqvPJXqjQKcVh2KA4TtrSX8HUpkD9Ou5vwPDaIQ4sBf3mTzfzQcHpT2eAItwKxzVIFj9604Dl2YPjNeUNkadYiTCWMzUnE4FClcP/1KXxrlQaC0o1QjTpR5zwM8y3ijBwOMMeTQxe8K4AK7+ADABb4QN6Mo7MVkYBiwgZrkcXONQUWaeO8q1kI4Rg4ceII9J487wcXSKRVOzO184W1S29j3eSdl9vv/Br2G7k12bSTDKT8Yuf5uz7Gpq8MxuZtJcRJMwWo5CAh/k7Jc0LmC0nthVyDvIl9RxZevjrROWWQ960WcKMXMGQZ1UFtFLmM8aoixJLZtFBADhMQZxcL5f1/MqxK4daD9qi0uSuy9BK7neD6nFGxeHifqrdEIfg7+T5QTbsUA2mxxM8ThJJ5TiNEdbgV5EGNq1pqP07Ff6TlbihwYlSoX4zKj8Gwd4SE+CYszop8na+vX+booATnghpiKJWgoNvT2et5Pa6BuGVAHTsdiEpm1ueH3JzE8SWMZwx6XuOUvllnZ3Jl5hqLW2iFIOXHvDa7ORpZEEhYKZ6TD3kzxHHYUi8z7G/9IHQdU9v3MY3yecsrK41kHEaWBw1MyPqFWrBifKR2cqa8/cxSfVOiyGxdALCtGHLEAXla0U/TLZX5d7YGlOyTduHQPOFg6fTDhbkY6CvSoCN1voC7Fymn0z2rvfV7KJKGTXj8wnQucaijN4dyGa8WFEtImlQS9uM3xZkX/tvcTnMuwhTuEahp81u0EWjN1tKKacFlAQBfiJtXqwcAqHDKOp42xodJpbFUK/KZLp4sDRMTwOts3GCMDCpEzgqxxOz+vixVdriXzgkEBeFVmBm8vYhPru2WxPehstO/q0BvYoYgoE4TKSefpYvq5STNXq6ycm2Y5QXy4W1Nt2Uq+ZxTBDkf5kcvjMZMFqxgHPx6J4mnQ7a1awZKXeb0ByIenQcqUAi2VVKz6/oAplcnEVmg7MrGQN+lAvWEOWwRiZnZJP4SusVYWcwxexLzfZ/NDwFaaOopjT7hAoUsW7maE8XQvFHBLZdRJtgJyzjcMhf4aHde3+/L5ANhVDSi0vjaQJHxhbkaUWDpyYl5sHgHLAIjlCKbd3sd8w44rreIdKrwPN95F3VAHZdsvKCe6hGa05gSH0xcOZ0yfgu4IVHM07eViO1+ui8UOgnMzOlWzHqqW/aCvQZJOde4cpITPGdOxkk6Yr8lYFGxRbTHEpz+UtHkddpuaF3peAts8zIBOA/TY9Hq2jxCPTNidMTVrbfX5rrqFajjsoajPjYE+2PEAaADg/AHOS+xPyYOBiu8LszOmNiTpv8Ka8hne5DqHAIUOveHCSmzp63w9aPdmTtgRFsq3RfGQn1Ao54l7iTC6VpgyntQrRE0Ak42Pt8hh5A8ddXEYpWIXI0he90/pX/lC7IwZpdNepoLaqJtds/0Lcu/2iGK9sROXiDnNUHBUxrar4UR8v7tIHvt+b7hwXpRgl1X3mE1zfRCDqMTyc4KxgfIEd1QH8ne+Sw3cAl1towOcTBieMZUJwgTY7Zb0ff8E/t9RENKRAHRvaUEvsvU179gtk6jASTEkneThYeHvC8UzpocWj1ZE75gcpCndXUGZ82yz2HbSquVD1qzyZ6a0buQSxWsiG7kEhaaQOulA8ayfod9XUL87Z0taqj0UrvfiYSBodJ4v0ND1yyGEj++fJuRoZDsNSgJhcuI0Izuv19kaAHsgQ0Nc7N2QJMHCQZBYo6x42v7labbinJcETtjkdIqHx2MDYXTGLIUpxe9ta6aXb3YP+Il/uVxijgm8HX0syCOydDwQ6HQSWtLBlGc2tMN8IXXGQ9QmxvkZ5tIPebF7ZFl6MCjv+kFds5tTNTVSCqGf5fCQgimcVszHkcAN4bBOYgJURe9lM19uYSYMo9N6C3QLuHll9SD+ULsdhQlKPIcjfRW4mEMlUwNN4A584XXG3Jx1dcGyYhgUy5dQM0In2gmMFPltlW2X2cHIYOYiI66N7cIE1cUXVmccBp28AxMmKOnSqWB0EkKJo6D3RPVBO4o+71TfxuiHmXIjCPp23QIe+AqnMw5h5nIuZS8F5wskEaqaYuVj/jSvyi6jcIX73HHdSTkwYeio7Xvj2aGwOuNQdwd6k90Xed2dR4X+DJan1aaYd0qGeW70MFb0OK+OjBsSPaJUxy4jVu4zJ/yy6ERJf6IehMh2p6Y5yK6RuZjFlXY0StaAN4f7xrTHadnPs1arsy8/L92frt9ZwaOKsi37QuWb/JaqW3q9STsnNx/ed7DJTjDgyUkUV5jC8YyV6vX832TG3Ae8pbLamGEUvGzfDaAaYhv3sLEm5MDh6oesRTUlL+gL2zMG7KIVhMO3vmUa1WZPL9TvLH97ZPCOKs7NMwXowIlgjYVHZBVg44WH0D3jKOyG+Z+KzdOvc6BLKbk9YvCaKrh8bAZSPZd5Vs/3R8cf6b2vjENjAhFpt8GdXcaTB3dC8gTiro3rbxGh6YeYopeeURhSpbfoEBLLQtAjzzs9pyI4Tabcq7iOEpIn9iddxEJDxW1wUdOPoPdG72i3hFNcB07EZHMzrMhTJ+C0NnI9tv0nvzGJ5/FgY2z6cziQMxwYpNMDcSWUUHN21oKY/sju2wltp5lnYl3LolEQdmfMWoei+XCT5UwXbGf49FF6J3lO9cKAMtiCbg6bYVcEJ6pyG4CMr5PE9kT1zoOmagEml0pJjfHB4rrprH95vYcnlx+odoeRC/SPKZU2QBs4zL5QPOMkijvhp4KKX2p2oQJbYgaln+1gZTM8Wgk57oWRbMJJdLkvLM840bpXo8tg+32eLTJICtJ1etvSPI2KOC/N3kFHp15kB0kmdOMxMjTJqiGCJCM8z1gH/mACjC16VVMA33EUD0A6o3KGaqd+HHVVPSBOHLaeLjM7PeUGzqWL8D1jeiK6k8paFyW+s4sip0cBK0v6nqC2BBdZWVdf54imdJLRk1KRi4RAxDshu+MQHUpCuE4C6Yc/lVRswsHm968dmpSeFX1cS2pAO6Ec3p6xLs2Yo5e6BPJET3Ux5lkFg2MJlOv4mu44QyFyKNPwct+HsHd2c5ObUb452h/UF2yJgzYlXUQpoX3GVGwkHU7q6f8uhfXpwzyS4vZw9YnhFCh7H7KVjTPg/qxsPGJ+VhLT07CrC7Bc3PeVno+R3Wjo87F6AMlhNPFxciBloKKtz+PGSpieySzuxU9NOLjI7rZs2TwWYDNGkq/omtkl2OLEuXSyxQMunYTymcySKBpYVbzL6/mSir8QBsX3vSb8u4w9UQ75iy5Sukk8ZYem+CiJHAXO5NLAwAOpt7KDgRM9nm3eA5N/RPhBp1OgDEZFCc2TztTzmT9CUvR1A8gDThTRS4eiwa6XNGCjW+911uzyA1qAkwUKE06mNId9IXomvq/T0c7FoG+TxP9zIYlt9rU5xCBUCtEzoWq+J8MWudnjQztkw7DpMNBYCXMKlqHKD+qgG76n7VJhfCh8z4RKqTa1fNjCl+0KE0O6uto7xtKgbdCvgE8s8/zu6PPfjj7v2cvqSAfKSWiYZ75WCQEESeF3JvTAeldEaNX1qzImVZ1Ra8cP7pipEi+/URI4iXYwQ8jG2Yd+jy8UTzpXr8L4ooQU1Ms6B0t3Zb68FP5xgNB8Cy7mdMH9KRlP89KUnCjtYyUktKFeDeueLZxIA/rx1AtIaLoo6N5AJGf5vGGJ3NQG9ZSvJXfBwvdMAvj+daL6Df79zPuUl09ft/9o8scjiLCeF33l9GcYWxpAm3Xqyt9g3J9NDVC4NeUXkHP/QWXTjgJtqFAJ3Ox650ZKv1zLHSKQYhcUomZ/LVuLx3sXoX0mQdRJtZ9SY35bffFeZZhLh0kc/VPv0QUxksymdvq8UxDiJ9BD/SS4RBv3otpCm1WnweE2Fkkna57TcVzenWEQ2kxIENCF/JlAQkvOQ88k34gd0iN1eZH+PuvEoVRp1aJtQgJ4PML+TJSeyef3OStXe9xzgbXGYepBE3I0wm8xwCPxUSotXDeKVuQRoqfwP+m799vo+RrS7Bs6wxcWiEzonZ0MVZlf5ZsqO5iGaZfZrzEjnWwNhPGZRGFPj71qmpKxyJu7p399+tejNPFO6n02KCy3rZ7uwfTXd1nop6Z4slXgKJ6E5klH6ix323ElNSt4JfQEI6/9qe2edZ0ts6P3Z1Q0XdfsxwSnIRfaooHzTKjn+ULvTCLt96PxVbP1Pjf1TV5SbUV/kNNmlYvX9mL73DbWZefTmiHavnR+LBKx45kI6Bb3BQR0qUZ7+krXk66TsSIeCua9oEDIkpmHUHunYSpLxNrelXk+EqpjvxuMU/L4sqq8t3VWfjlKZ8Bl5/mjKXJNZdJycy5qY2MzHDRFLvj/iCGQVkM2flISqWPqU+WlrSGwdJpv62pbGFpu7F2ucHnr0fLnlI61GS3HlAuRSkVTrHhOasLoTOKws0W5qu6oE8D68EtDVT6d5yy77ee6AEcXm3ZrPwI2BIHL3VaM0rbVkyY2SqiOAfQVQDRjMlltjRVp/BD5ozVjOIdg1Sv6wrYd+fUZDFK5zCsmaTgMKRBOZxID7tLLv+y9l2hpGX0tRiNSfhv987bLPJD21y79pSHiTIgL+sLtTJKgD5Xn2S1q2Q2k8xaMKwihnjVMJidN3X6RBz6ygeO4EKupCY1vX0ieSRIOOLnrdeG9AbapE/NTScKq/3TLMaTozGRusj3jkw9oG5GTPnM8ZYzG/bgwPqlTjHq7rUVD/VPmMcoAg/FQUWP+Dg/QOFvSP1SABndIcPGd3CyNRpVttspHknCu/d5KClYWOZPxMXo6osrkRT4vpFj6AWhRYgKVDTrDx5GQrqPedAD+9cYCGKehwKGfd8AQhrG0wL7TtDc18hQ2+WrMVYXymWgqd9pIlefrhhd3kNCMPbrSA3nBKYxo4LKbDqbmXowpELonHaYLm68rGER6V8u82VI3k3jvi/kqL4UNi6Fm3m8QD5C0sUuGSf0p9woeEgrTM4GRd0eUYv1a/MTNApHRh0LVcb2u6nYI9tJ8a9NGBKET5jCekhbi5Cc8zyQVJRgj4AGPTVT9KqWu8GWZYx4gqx96gJvH4gBmr5wgNGwga2s00RAEwvFMqA2ThuANJX7wW+6r68qAoilTQ0wTO/PuUO+zu04TefztOTmjhPEUtAehIBCqZ5LqvlH5VBoJ8qYG2B5Zxkwue7XvDw9QqaLXdYh7cFFig3LOhLIu+nKheurZLAg72SwoiQBhVCGOU3M88xgLUVLQzNvCydDKxpZgvstlUix7Zn15/JDC/kC9vKeZ9L7O65LuMr2OAEvysVQk9S6gwx6aNbk0mMmkjgCwq4GQO/Us6Dyu6DY9/eq9pRJ4u3r6N3qW3nmPxoKbzDKHxudoWBi6nCUy00vLSDXmux3JWaKwr1OKEh1/hiB5W20pwUF5iXr+m6wXPNpQNzmiuLnBwtTkni7GeWI5j3hsnGfFimrxp/8FbmIAtNppdWvWKMapJathZDuowWMHgw2DmLF/XkDMBMLZpLa7/+zPStZWOcnoR6g0Cl2oPqnLczHANNt70jhMF66pb9QjOUE6De+el1R/HMFPhiUzewu5P2Y/YpgY9ilJzE8nlQOloSxU6XuGbN9mhb6bOrp/PaII7nWrJVlaPEA8aNQ1xTMXfmTIxFarSwS+dSFt6mDWo/de1k+/LJ5+qYt/NNThoqDENcL4lPUf22okh4zfQXEbu0xPjZ63DcKH4jYQziadKepgFdV9nfcmERCH806KoVRtdV12QmtjhQAnsUx2+51aDwZC2KQTJWEvVlt4nzApoZe2KSizHgWRojeHqg0D+tZ05Adp0pFRrLbRe/hhSbAOcMvb7LF9+qU1aQZGx3Hb5Hib9GTxHwhfU1Ngaw+D3cmCUgPVGvURvC1f1cV6KJv7A8rimt20rNDUAMeJ+uP0crDvMD4ZbCxCCiReq+3UZY8io7c5WlQ4YZhCo0pn+9j4PBKpw9mga8vhF1XVS6iZKagWvcATandeh+PRyKGlTRnbZXtLAb8lidJh0NP6/s61GXPoUnDGWekC9ppdxvgB5H44mVGBjA2ElEkHGgZGSCu+aDZL+hE68VEzb3vc0rv85xbcNU7xkQtLtHWztaUOpFRhZOqQ6ub2o6J4+PQ/1qxa9PQLhrWQUauvi40HtfpeGXMaROwy2jZ+NbZxBFQEA6FkaiV0Na5bKZ8xf5yjI4X7z/nuMW+z/asa+8qP2V2x2B74L0ROFp/fHJcGQsqkM3UFiJEWz5pddp9BPCWEVEOH3O1a7GxN39yzsakTvMu0/Nb5CK64EDK1ohpdUAGs4p0tKBD5AWMX+4LxB2WyVcAXyqbWgFpESJgay5JBYfSYXQPBeJ4DKnQUBGiyzVgJKB2x0vkjuH0VTu28E75JEqujoEN4gpO945XN/jor+Xpg4r7OOrJxnd/nz/Fmie+kaGEymXWAi0wmTEwdhb2ycYtX4qXpEZVQbYcvK5uBbst4WOrmZm9emy1S8muTgB3FvekJO4ouMkPzx9023dnDQL37ZVbUN9nPB/CpmQseIDZQBdtCgJ+SBO84DsWVsa5ZWPWiohajLI9gQ8SGJ2XPcvozvNkAV4rtFj/Ib0LK1LHuBY6pvYczxCeU/z6oTiAe93H8NN/VVbE7qLedtqQq4CGEbfjOb0+ieJwqsYRgHA4zU6i7fqRY6UOQ5OaG3x4Pty+yDZSKxnmFEpPL6I/luGwFCfZugVAwdaI6n9gWXk63aU/tM7X6WrdKMh+yjrj6mquTg1bfyQDVWHvZFsk8exDipU6iuFcQpuMscjCwd3nWXJvxwxtDJqh2HUjwhAJXXh4dv0XfhuVbmkZO1QmvAWyhm2ttYV7qRHd7UlNDQpN6jhfDFnEoagHO7fTysfs+KZubmzH43HcTWmYopZXSy+9NwneShm3WpeD9hS1YwDamz48fduvIUhiPmpOm8x62oYO0U4XLTr+2ijLkh6X6g/XSZafZpvn9a458u2p1ZHqRMOOlIRZWCxzucOo+c0ELaX/q6zNHk3Cug64XgKtmScnuy2P59OvqCMpznyvWfRTh3h3gsbtsHDlV5GLJZLoBK+EJ0UmImBrtQK/m3/A04LjObiFeFqR+SC+Pfdk6EUHw1g7FB6jdcvn8jFan7V4hBwsRU2sV9lz/vNpAEb6sqGmCBjsVyInHeGJROD7Fc8ye2dwHzgqidu5HgOpX2JiaSoMODFusqD354p1m61Y20I9gIb1rsIvDZf+Ub/HuMs9wf4fjHHQ+TolvojyI+FQSzfVIXqr4/Svs0aGT4qMm/5jdtC3L37LHqmqHpM/2Si6KCBDGtQ/dIYwbCB9Ta530Q+6O+cg8/zAcqNC2Csc2M2uXSUDKc0Bb08KvTYiYWlMkMg/oc1XeUEK9pf6SxT6o90vbSH5a3XdYgfN9vYMEeQ7zJhu+K0rc/GsmrIDBTwmEmKkhWdZz16pthkXgii0Sg4jXznnekS7O83qeP4N2RaGTSzrzDW0fHyzaAuFjpjO/9/p6i/UkBreoQqgKczICdhIqi6fCpeLHo+Q06BPN9S7K+6JiXkO2o96JnUjhgzYqgUEmr6CLORrEpS6KbtowQGwhHI2dMDLTme7NfZ7+a1ktKD6dPP2yYFBsFFPXgh2XmDF8Khb0tg4cZJWLOErsT41QAaIKhJKZ+kEyZH9A1f8yy1mLz/epQn1dLHqxbmnN19edDeFzyqjTJCOdbM+xRQ2EoJnCmrLbw8EEmN7SHkUSlbZpdyqWqW3JRRWdaqJMcOn1GPdpbT/5qncRPaUAkPTfHn161AM3DI6NnuN2X1R1VspKdTQOAxjp+y8z4WmhDU+MsY9wMlO0Rv3LXCAf/3Sf8YUPozB8Vp8vmnm2PKjPZ07e16ZmsVwvGIQHwsNM2SxGBoZlSRVKOafoQ+8vjL1OA9rV/csF0mhkom3BCr2M8DHTAJ2oCQ8ZpRrvLQUlejbsFB7Ro4Lt0EOx6QIo/VbqtTgJDp9XqLHCdqqL7YxfrouFlJkG2u+fV55hLJ/TswHdyk9myuu0cvugBee78dIncVk8J8ZtawJEFAglMw2DHgt+QRmZvXgZFAN92pN8ky+adZdlKFjA1uphcyic9P08Yxyb7FgdCF4FQsyEGW+PuMo2XCXUeWFsdvzY+1hwEO3sG7PtsusexmhUl9olTacE9KHMFwgtkzrjXs3pbQHM/FvqtkBupyuClWol8zqkoM2GUtH404scyONGFsweOc2nJ5Ed1uUH9sDsDmFQxBDjSgLvTbYwKw0I9G3N9pkq+S3Unp5NFFMnMQA9pVrNsw0haEKReSB/RRn6CyDFt0jOlHQHLNbOlxuOihYjbH/mgpQJ9RTniFcKQtKkT1V4o9nPnV+a74cQ96YaSnIzRX36n3aHBCjl4lNq5Dqt6x8+jAT0KIn7OX6xwCPizYIOsYnOW2hT7+28shmFa5cOS0/uOrjqFGYm5Enig2sF6uV6zRtyKotfDJB7P9y2c9y0crURN4WamcaqzzOX2bXhYT/kENc30i0IRF1EOMNkZsh9jGcuZqUpK6ZYQwFSixAy6TC6JxOYMnhZFujfvMuCtR4l4c2XWX5zk9dGFuF5MeVEr4+MybtN7QLFlJAy01j3gtmXFKKWvHKh9gqszDfVvqvPv6Gk6DJASNIp6CyDUISNSUmo+9ZQETVbajr3EHI7SsLAIFEFwU8Vct7m3zFrPHIZBqcRcwpsLR/fo6g/Ua8VOFgrsJ6LmQEFszRsq+L7vu787lT4u4/N5L/YioHm/CcMzZSHnt3cJd/hd1A0WD/9umHSkQ/YOGhbKy7RmYj4rZ7ZybIwmTKlZ6iTUDYBEkn7V4qSCkT2fHv39AsKuxySqgc6+4MEffpi8Fce/RU9ldDNthvvdwKUGQh7E+VM56hItR3923i5u4Y1tcNEhxY1yCFIbAAbcYOIgYAU2t3w+J2m/cH6zfrlfQFbxQxGQNl9PjcCcO0uZACHuKAvpFgwnuWQnpC6NImpnjIL4dpG2Jz0+ci0toYM5EVuZDlUEEYHOkLvcqzfnwcyJ9Rh27VainfuWoXDmYIHMFL+x1IShn34TZqqmnogDtBC28dxQznN0rCHsHsE8eRKmJx0oM7E4VO+zctVRV/dfWmU8qIo9a4KtMzX0NjvdskscHRASopd7MsSTtW2FocnIC2NE5UigplBIBsRnBc1F+joOyO65IOt1rf1JrQLL8mf8qnmdURL5cSpwkTQmdv8bunRL5qv2KEnBOiXWRxbStryBV5m8zqDLexhTxhp34lvrqechbkkbdmcOFqixTpoU3zJyx4pRW2QD1x5tphncjDeOVVgANgAyU5aUFhMTKiL8WOL5Wyp3zMCF0+/lNnT/7sYSFVRAYTfQaF+SMc5NlaR42vm+04ybJPOKowuaxmddDL6c7YjGqgWlXTPLsqKvjmqvuLZzOONEotnGZfDQTs2KlNnTq6nDDixijTzB6nlUEE/HH2Vr9cgv6JGNStu6hkiqEjTx1BCjlX2g89pzE7kM14EWKUr+VSpnCoOO8oiYG8ren0rACm3GB0ladry8gBUGAp7TWN0XCQ79BRoiKN8S/LE4bTqmw2kn7dldd3Msxo1Dzv7bvbeabZ3Edx2KRMxqrHr2CW4+i3REwej0nvkLXbBU1Iet1F3DWdNiO9SC7mRD/MPs0600c2xtRwoJVrKJ+oSFfVVGMrogr489u2CRuNVvlkDjksZB0AP9CDb8TIudBIjTs1o2YaoRC2tJdiHcc9gpnb6S+6d7q8plmMg5A8oQi0Z9PkqJ3A1Qlb2ASkfR6K88v1w2GpgkEUF8+oonPkzcKlWBaZaLafxRXYHxnVNxx1lHidbVoii2vVsIYoa6GhwJmldwcqH7Nuiu0dUYV1uqofrom7xnfKmxsRY5aJCbLTkrZBqvkQS1VWiLCp67Gs/N/2GH1LieU3dB7XZp3l+yzD4Vbt5pr4b598dzHCdCGjKhFJL5jGPTeI7dRvSE51WdbVbPv2C9rpgGrwfUSj9SO2ZfHx/whS3BaTa0HscsyTMR1LPf9zz/rvYrTDCnfnasMCw4DGeL9mCM+EBqyJxooGHU1mH4fpa4ns0EEU0t/6umEN5O6TzQLdiP6wcWAXt6PMnIzjfR4bISeBLG/S3bU+I/JxKXI902gny73mFMn9r6EoBJfmDqdZZ1qB1hN3owWDLDQfmT3k3cxGYSkiPw640Pa1//9/Fkiv42tTLCtjm/BbMr+/sC508hPypYUTMR5J4Hsed7tHnslo8Fusirx+9t9maAtWqOIJ1AeQppG1tUYSCKQ5jp46CsVZW6BeGSKnE84Qicr/L2VE8x/3BWi5SgWdU3Q22meIq/e1xIxE4aVeqKSkBLltSCeaJ8ruSnf3QOeMaCT+6Q8p7nWf3IIKbYA7773OARI9+Oj76qXqgOn3mtCXR8ZTyA88gUwnkSdwZApgADoweQFTG8TBOzNbEe91sdp3q9nMgusvUOA14F29bSfDbkjieJAPsJ8rK2vuULfJrmDrlkEGMIb1Pn74Awa+MrsBwtQvchVts9KdjYypxOwGaokPr5ZX3odwiAIYp9ezVvOGQIwOs7wVtl53zjMcJtqSHWUcqQZsKXdnpmpRH92UzB5TJD5RxjObZ2qERjg1TEbj4JoRM+7SS0/lmpYOj9Xwr6NmCYWUqhBmDhx5WbWva2qJOSt5/fyxpJn6h3fYJu92ZhHA969io7ws2xsJKF/U6ZkDbu3y+G23oDY33HHLKz2RtZ8rpXZoCxhYaEpxMAjl3uaOR8uf9fJkblZE31LVvZQ+HzdsuGwlWBC6KJ3rGIdO2quSnJBFch77IxVFQeFnmhQE6htTMepfzgSL4BdxiV3l+ty0OZEgDJ6Bjuxa0zWoDnEnCuE7DoV/sy6q+BldGR50McYe7rOYZAL7Us5we/bTIlhTFQzcohcHG2XIuvyqJ4umsv9uv89Y8rHz6umgH25ThDw3tL/M9G7mPpOsCl9lixJHTqsGvcSqJ5WmQSKNA2WXDlKLbhiOVT9UNuxq2D+kqX9/l4+k1NWRuy+4JSSG+QRLG07Qvxz/m1/l8DkxcjkUMlLZZwX3bEy4XqOCeg6qciA7ctFgBxTGO1EVxn1JnMCRe/D3PrynrxkBzAOsyGvfYuYS+i6hIONUNKL5FWs4jH9kJ4zlOymptHG8OsABUOOb7QxxA5LLhZiVk26PBhjucdcGaOsxOVeikxs0Zlrb0Iv8Zkz4nichwiiuDyUDod9Hap+KrMwijLJtRCKL+++bpv1/fw+48oUKSAdb9/qNcVPXNwTjMKQzF4RQrlcO078uR/N4gk/U5itz7aZuxnl8QaN8zmaxTqgEQddqpxIVTEBvj+An6RegHcjLVDwpwo+hgZt2GqO1xZ3TJnu2deBbUGMrcyGs+83eK3UeutlU37pffxW4/DLpJ9adiV60LKnDN0giigzGKqCX90Laeoz4KuxiLMd4/AXCeoDyHvpIzRX7aIwLABz/P8B0ehXHkHdN1K/rlcnaf0VexHNeYTo4Oiveltro34bcXyXF0B5E9pSexMmtHSqJhANUFA87bzGE90a6t+Bd9KNdb+u0Dvxkn5+p2YGgzzuUXF8upUhHvAD6KblGxfcQ99y7Xxa5lGOJ/yne7A3Sl28I9nQJ+g8gX+hK81axHV54wfedVtsX2KfEuqnLVRYCrZo58N0psiRPWm2c4tjkTOrjQl6itgh6vaMq0HhaPtt07KeYM3DLPZpLb7ILAS6d2sQk/HAnf1DF3Y13ssLkPwOQLpW0ce6+q+of1Rb/dv4WBhGw1MBB+D9zk2/1mXhrSR6KpYWLI62474qjausrQJcEZoJslanPZH0jUVlHHTX2f7aivxGIF9DhghQPI1Ta7Zjv6smxHcmy9J/hNaL3DQMJ1pLrc1roBtaYRaeRT1ig3zW6oMj6AuvXca7exDTeTtvox4WcU9gdKOhuLek/hj54SnWP+mK3oz55QVZJxTHSEabgMA9XUlAI74TCQUB0l3db8I/3OFX3xd9U9NUdU/Yex2SUOSm0H8oCL9DAbBNqmXRCuDQMJ3PGsg79eYIZc7b3TZl1tdrC/Uxhb0ltbwcjMEFbpGGNkPNzkHcpKYKbsQgfmA5SQHQ9ktd9m4A1ibfgRrkkFFtZJ6p1Rkpfp2x/0DDUqtbbVOT8eCdwJ/l5XLy0hdELv5lVxjasOY5sxwsDgRVppqhHKYOZSkCCvhfZsyw9JoniSDNyf7ylie2/gWQRHhNDDXe5hwRSk6EgU3cdjCSfZSpVMYQEhShUGEsET3QsMnq25qLyk6vUWmgeUbuk5MYkJ5ZuwZ/8wayecvEmokEIJ5XqAK32bzTETyblC8rHFPCluPdPKUsW2MxKaB3IiM5etqjKEQtszQi0SSgzXQE90tQj4uudP/63e5RuexaVaU025XuPzGo5u7HZEjuazds08rm1DieNa+4e8j9c5iLz+LNKQsN7VRRs0P1F/kj+OCKr0tbq+Met0i9+YhHCwPA4GE8dUZ+dbk3/TNOW0u+lxk9/q5FwQNLMp7zueB4RdCKf/9MIer7ES/5Rtnr7uzMAkURjpZrtd71dqBgRHn4+HMAdQzF2kGEzotuEtEJvCSA4V9kiQT3nNrD0+EKvpvu44HuL9Y63knO64YWFPdQFh3J9IjRf0Z8iw7YZAMRyxaspdl06Eln2wENdOvoVqyq0FCJAw7EJ44M96KOdFRtd5XmRIr82CvnXMczHuKjPWg2o3Fx/o26N+4ZkHSeLy/pJ4yq9b8dPScrAo6ZgnS8r/2FJwFR63AKNFIyOmE4qmCEoHFB0XqwaU4TOr1Q6X4WEqx5EJJX902dZsdxDBKem+Nk2k4HAR2rfGW/k55ClxMmtpu6cJVeRQdZE8CGZRb39XFjuM45v6NgdeGfgdekM3nFxaIj19b8tszWrNw8seuvEW1CTUD9FK+XIqyjBt5YRbRT1vXtPbevo3SvfpzPu8z/ox80nFA0QcaexL9P02waAhYyvOD2jIUAVyorhnerzAxqmm/Ebp5EvF0/Nu2yPCkNuWRjGaC8QuOOoomfLfi1EZqLA/UmfNaViyT/9llXPGC6iB8CDAKiPmaekqRwXUiW4TCqihklAezvra6a/s3LRZ5SWdJ5l5nXZEJ4lU3Fc4wpgu5DvNcWZ/mfA057ZFSRCn3lwmzWc89XpbZ82WisvUO836vcBVNodX6WFGUU7dL4dK2wUC0TlUEr/pdg+AMUu2lHu7yVsZJEqqONOg3v1b1WxuPSOXMh6fRC7j+NSoRk2eS0I4lU69Ax+4JgAxFZ2HBcUH0NTn8wrFU7MB/HC3w/7+4GH5LlJWaTTVAYMiECoJ34qy55CR+rZu7lasqeNTD3Vz0++7NlUF5MCnK6Rpbq4gvurSphiLjwnbgVBJ7FYDJsAxOC979OTLIx8yUacZbNra4MjocWAfIZBy4CvpZGtlSKi2yhIk1DCSqK3iwB9s4d7WebN7XIMIji6FQQztmlJc955h0ZwATAZkZbvePDONJGKrJJT68idsvTK8DQD+6XVfQbC+ZwvSPc83fKhRtxs40ZXwjAJ7xMYUJZKIDa22TpARQI6P1QLgdcpUQWc324vmToAZA4dwlExKHfCsIpJ4HQ38wq/yFWQ9sdcJYbJl9MVaY0LKLw90gawDCpe9gGaX9ynHnzCSeA0z5M6mCZ0ZMB635sOPMD6hrzujtzgayDEwDvVU++mPF7vJ95dPwhSyxSZ8epEE8CgNVLeSz8stMxAMUMCnEH6RleuBLW+zvqbX20OFApeVXGvXZFNl5JcX90fpTYxfc7X2uXqkBLbCCgXquU0r7/2yzjkMHcI7Ilf6m11Ch0c5kUTteObPhhvL86wBM1fFmn50Az5QVx3VkPU3tchzgx8nDHHkT4mzAd0VRhK12WGjny99oUTS3ICcm4Te8XYpO5NdaQNzOO3hmWFqReqif4skaMcDWbaL4i4rm5X37um3++2ufvptDQZLMrIiA69T1ANGbmROhW2ryWSr2XCj41l/rqG452axpTj9uri9hbu8r6g7ec7MuMqWFa/iAK7Mn2/CXfx+lJpsVHDNY39wvqSvUrBMOZ+/KHPYXFI4U4l3RbG77gIVYgFbbx4A4wI3v+IJDjP8isNY4nicDgQXeCD3iaIzbD5Cqproy2M6o4c3uG1DVIVxdD6SIdROk1RjcDPh+xPGYX+mbiB+AgKed7XKSlaAiOENDBv4AavabAwHc1Q2E3UaM9mVhzmKxxLFE9UTtd5ni8cihw1Zdc3zeQUUzBmFgKFZMcWo7eZZMxAmLj2vZiKs1cQRFUEcDY4l3tdYCF6xeADTok7qZj5oTibxXeq7yc5w9e0qLIxkiCWCJ7rneZ9nNfwgMnz9O/plTCgIIZLKnZ2A4r7NPXKhrWhj/2VrefktSkTXvpJRCppYHpngE8TcYkYR9GpZw7IFtVRJV3xzC/jecDrvhGYwqsi216f5iksw1+HsQIeQUTFKRbDaYvem9ja9yDYrVAKHDi3ahR5p0Hk2vAc/nrQ/ju7N5mvv6f+kIvyWSnVtTGMKGeegKqD3mFENMxpazkKHoBRPKlHwaieROK4HNtMn+aIuwG71ThoMJ81EVSkeNVWrci8rg8/ZfpObyc6BWbF26XsNEMXGy0oYZd8dLvV7uBdv4L231Xx5pIJYGaLyYCzwLTyqy/iSAfJWuAASXyJRPB04pW2GWGIVBGo84r3cVEvKvtvlsyaTkrFrjWA1b+N3KEE8BUyjfUz/8d8db+jKZI8UOt9XD9nK+4//DpcmwFWShdhxCfUqy1Yzdvr2dDhFouHQKfRNlPmdgzKrSb6CeAF7JEV+q3bNC+Dnpi3jptNVe3fCVTLhU0lAT+MuIpzkm+rp/9kxiCgvjVLqVQOk2reFUl1sQc36cErbJBSyZjibdTw/I2RZ3VFT5715+q2+ZQA93Iu9l1j13OYGbdUZFZyz6t/5GawnqEyYqSRxUXA1mn+2bRRqKOFqhjPVW218Lig0wsjxc7HdULzGaiek8AFl+ZubXmPoB1QDFC8QbF8gH0vLsdJAAP1QleUtAs8LfaU01QgPDLzs0CA5o/7R4Flm0NH3BQM6rQUrUQWxXRiboe/35uqtKOnGO7tlfTQFL6eDguqkqTf50DScMrvzRbfCHXDRhaQZ+oFqMY/HJf0CPAhP/hv1DtQuQn2MV7B/qpzHlKdMKDzN0A97H65TxqZ1ispHAQR0LrEPakk9r4z782iD77T+maxZeMAq9MwQVJtxoqHWuDErxTClQHeV7SkBy1R8CvHkcM3DeIphy3WLcDTDgC5WJ0l4e5+DkYFYEPszD3aF+a0Unox6vqio+hzPV0MnicRk8oWhfRGSJqS9BjYBC96QwdDh6esGG98w+ucceZ1CAhNIbQgMXksJWTNEopGzNYs1TAw+NrtVZeJUMOMACnh/++WdZtQSDlV94KPjNmtV1iqBZ61C1QzDtJcuv1xmDxs6T75Y7CnzaU3/dQPNrD8RZ6T0lNEctAhD4WeGKund+N418713kpVbRtIeRb6pEYw1p3lAz46j3Qye2UlxSoopFEpmGA0VJddYYILVvgD3l3VMzvcDCRPrJ+eyrTNGM7Yrjs5KGJl0hXvgI0qCZQa0cL5uOfc+6Gu8j95R69Ia8vDsbniRfJfJYZxOGTpGuNnCxwyjpJsc0qV5+sX7lP3+9enXeb6CNZD3Lr/ft4Of0wzMvq1NPSt16tINcWaqSxc2Jp2o340BfMXYK6hz3PAaOKZndFH1NIzXxfU1WzuNTdwSp5aY579TIkeh0DGpmBLHEMTtR+/FI8y25quC0k6oIcFEXR5qJwHO8JGfDaHcXJUNr912LH55EsLpWxNvsJ+BLFrn6KeMBgFUQ/IO+/g5p0qu1WMbjQ5Cl+YzYm0e21Qsxtcv3MwQfJORGOLL7MuqzXNKUZPwMc9F7vqM2ghqqbJDFW4qxh3HGam1R+BxhvAzwzSOdD9JLOFbRvf6odgye8iMCbbe2WYxjaNNHMpxZYzLJrQ+Q2Fn0n+LR6rgVc3u5Vg6JxSR6G/JVIWqOQqORVYezA2dPKYACgvtVRw/oC5kU3MntgU4TTdLCSjhjg3m3ubsosbSH0OTORf/vTSdEk3nZlP4mGqmewfjk7q4hXwmPZ9mkz9k5YJuUeq3ZLVuQf6jJSU2wHasDEPDhI+JXUpb7r4o9/BSMOimo5D6Pa/fafI1+pjN0TIdsFWcICnm87etWvD5KyFiKsoEcrOhUA755t8oAoRwBc3qtTdSZOyhFgcWRsH3N9LSo9gOhYWicDAVagABhBXlGiwDVJPVFk5X8Xh3/y67rTN6dwcGik5C4My/sOLBUhwokAPFnYXa2eIebrOtzGAIyNzHZr5aZy3c8cOqzKBNZeTVPryVv3GUhG6B26xcJ8biSoiYiqoS+eQWe+91tT+KYv8w9//BIjJmpIVNFgU8DCUcTBUMBr1v6DbQv266SXpV1AV7b5pNUcky+hgycIcMldBld6gmLXjNq4rkQGmUdOJN15nBz8MgZMFquhQZKBBke0byse4IVSPZ/JC37sTBTuMpTxfzpuL+SJ3k99uCPqin3zg2hkAPfKZGG22jd5XXNbY8H/gu3/NrPcj7blwwNSkwyi9OIja9nLYcuap44AW4LCIRwIGc/TdtZzvNm3O5z0BY2a34gLBSwsNUyu/BcW+qReFdFps5lDuOEuaGVqvBHA65/jWQDc/paZEjInyiGkn4VBKv6YL3hnzU3Y6UGVJ6g6/p9fQXnBIsdhrjD1+FLpr7EacQW3ELHKgSNqZSoWrhlqfgzr9paqPoFkTUtl0YnoHMUHmQAgrteAYeuqB2MNy13yXkWyVkTKWSrobEvPkiu97uwKBt2Y7fVLBy+dASI+xj6434yUiYVno2UrBaIRqdZFgY4PlA8GBo7dZpj49t3XwXyUKVTjIKkF+FcklH6umNmI8C8XUFQEyRNSUlEMqyVJ3Ufb0GmXbqiQ7NikKXGMlKX1Y/6QSHUv2hOjohtdfwt6beR+k05qFNsZHUalwmvf/juF5v6Yyb239vmcYHTotMA+Of8ARSwr+k0qyHftC/nnNhmy2zNRjQ6p81UHdT+PEn9ND5ZBLFeZxnAmaxrnbLvff3fFMBHhPHdOf3/VfXatiPnYqcSPQpkzBsYckcRqJ35PcSVnBEaF3K+Z4ncLnI9sum7lQ9fsBKLTVKA7YXh5pNCJkK9rzm8byiAAhaSFZvlzCdo3caULtdPQ5Gkzaus5O/Y2J83G1kNSRdIWMqaks7c6n6cU9Xeg9cBTXbfgjTMhdBVZfzqNmUzIDG8xFCpqIeZyYA4mpDhcDn6su8yOdLDACLI1eVn9hlrRNMQT+hyaSEj6linbQv7en/opooLzbey/Lpt/2Whe1A7DeguK7UhrE7u6QcuHA5YkAn1AYwl1TCyFRx2pEvPpXZblV5n5rW3Dn1gf02y3hhFNDD/Plg4qZcxlvYptol7aAArYSQqSh/Swh/nSG2eL//y9Ov69ZBLQIshuoSg9L562ZOBUI2gqBQ5eaSUni7a61w+flI9E7SHrzwtswaYOPPCqgIYE2PKpd6uhEu9YRzXgf7eIZNdQFeRvHUPifmpyUBXOs4Ha5TqaqEmWIYye5kuBG3D7hCF2JYxBxDG8GXH1fcHyiRuvIq2+zZEHeVgz+XhqFIfX2zQ3FquRPcb2vsRkkpVEyYQcR9tSSGk0HkBxYDoJNqazccCJxQ4EpPzQExdFdCxaS827WTP9GT8E5hqZit77ZMgkEO3vRaQ6gwj+fUVI0u+fc3OWbipq1FLiZuSpiYCtZSPRA8895ndALwP+jjhhMCN03f1kZ1Q1tOCCkDbamEg0nHUSN2YVl475rNAsxnfyyDdlxe0yU7kEFzlT6xQwMgfaKEgBlB/KDdmDYQ7+BL8/QLiGn0phw85VxGIy2lwVYWoeYW3mU0oyzRsQeMFeDl4/wxvzPJNg4TyyT5jbGI6PyHRtPkyAmb2nrK2ewvEY6EiolYIsESDS6urpEq5g7O95F16W5XY92h6a/OaTQQx1PBKeLjKTmemLMco/leVN7nDJo6VJx55/k266SJs633Kb+l7+z9J0p09xnVAjp1ErFDp5La3Z7RqQgLk46SSOX9oSzg2IgrbhDqIWY5x9uhf81lWVEZKYqI/ixygYDrSStOBElhYKKaFFhAMy8qqG6znEnQicJc5hSBOtm6ncdfG+XfK5ChlkdaJU4KLCGXkxMwBSXky6gnyb3IinrTLtsRsmz67s02a7m8B7ktdpDRMzATu7dPwh+fllMppTpkOlXRFRXd86df6ZroOKLGsmRBC5kFlFCct7myaKdvLkymhKQ1v7tUTiWEmePNbllt9t7xHskiACzAkEFYPr0NBtPUZ6d1t/HinGCEKCFfwv5BC83JiDS+evp6XbFtcOx9BDIWpzqpmjLHkPeI6gTM3w+2gsqlOomjKe4z1KKUkC+hMN2WS2dlcVtgL/ih3D7m3BP4mpPdPTR/Od+2/Isyh1BaPjb8cVNBAqJ/AkCohIAJBeCekd2hqgyFnaKmxtgLrrM7YWOdNPR1DsRsnDbwivkg1tqEX50E8VAnXe+Ube8AkKgWeb2noJN6r4v5CiOB1zl07zvxmJOs3FGXXh9aa7n1dAzgt+Is+VwSvek9Smn56Uu+atWuT3NYWqkI2ACzoe8+wdumKLNxNg6daE9sAmh7cdzQCQszUnGf7s7z2yUbTM5P6VeVC6ColfeCTrJikrGJnE198wybrlxCecwAS2uOQ+gUMiYkpKVCoBC0wqyroPqguj8KY2oxD7Ze3xg3O0qP2Nn0CT8pCehKEHrUdd/wPpc5fVGQQtt+N+xTgJl/UVbN4iAWxG4TJs2NnW2MirgpREw6Uq94/76YV+xu1/xMvyfy/sp2jaJiOcULdXQpsLutmCckYTya9YClT0UF700I7LGQZax977xaVPPWevME4ulDpU8ntS81uaXk2kQomBFkrXvzi3pXN4vM+3v2JZsbZfRPFKf32MuZr/+s3GPkPVBjcxqf8iTQVvLyiknol1GUdIjhq+oaYH2KPSVfHszl6IMrqw7xclaW2wf8npFKrHKipyVTOt98bYR7GUVa9c4Eu/xuiVLbu7jhIvuIyrqUSsmS86sEIjGyG+9xtZMIGeB4dpVfxSeTuM3fR4c1pTqajtVCTiEbqUK72O9P9T3S7iF7PnYStjdzZhtCiN+h6o+mVf+tAWUi4roUGULveJGtDdiljUpznHBELIochQcnCgFOuUK/jGKZyb+i0Ei/dI+jJHHUW1p27S7rIOHljfYWTisdFU5VS1zvCgUzgv5Uz01brwvYpSyYB+JHTHtGGQeg9zvYAV7Xz3WZnbyf1GxqMcDzUyFhAtEl4np0x3Noo77PqR45CiNq4s7KlUltHXH2gUk9z3UhZoFyIacx9s26+uLIJDFb+x0y4Pd/oXM9fd14v/8LJbmnf8OnBx1C4PJKASy9KxC5mUB3wB9wsysxPsW24M33W4I39BY7U6qN0do7zer5/ggKpc+2qVX9kJWr57tU7bK0SAwo3hYOUC4JH5POlM76IQocAE6yZn1LJRLv5nWkvbP5smp3FtRV0gubj5cW6czFHytmQVSrViuekjAwI52k/ZSpZLgw5qT5gseoFOjMQ+pULSkjL6qhQYITQTye4ljwqEK4l5HWA4A3zxty7+m/7iq47oTQHPpYAVfRb5m2Q7cGt7FOHE2Z7XJfIqTLKI060uVLVLNfgG9pwcoFiAJha2fZKUV9C/biZBuWTu1R4DivhIBJB+tLgTfVfp5TtVaU9LIi/5mg9eWOPjMEysMJc+AUK9m9wfaseEEg5EscqY0Bf1/kGwpBl3v6/8a5j76nm5yaFcwH2363qhfZoaLPzImTZuiENp0hPpAE7zSeDTHCSG+tV0JEL26I5RYEbFXS36No3s9PfCfLKx56WycVKAWEcUlH6uYnrKuHFHdc5l8ATqDLO1OB9x8alt/mj/8PKwwYkWbbWJCvt4TtVHcz+E/YxQOdmN/yRj5IIiBPbvtiiQ62ocBpm8C7LON0MiWGyCMvYV1SC991JDwCgeTZgkGUOk7+TEBOPIV751sktMt4Fnc4k0/Fgg7wCM41tX0j6LTZD8LfbVyNxIEbUx6QlwnNeCUky9ifddoZ5/S7sxu6t1QkLgyhKowUxYBy7QnxU1qi0Zpy5mTIp82C2RaI+PEEcia/7wLW2RbOOr9/BXaajnNS9Lzmj9X2gQqAQ4SydvJCaGkBttyKTCb8SjpNHHZOj/Rk0ITAfiSg7gDrks0i35pc1pvn/jDoNTQEXhtNCLsloVjGfurH3aPaFMAotQtd9G5MCjrYxJvkcrDRmWmHry0yooM2DR0+Uxe048DXA4moVh5G0x/mY764brpiG9tmant522TxanbbVagpyAJD8oRkGQdBV7OdVNfXqK5L3in7AdzR8zJ7yOq8F0P/YVOyRE/6kPCzSuRgUS/w0XeYFIuoGUipP7lvrfh6W0ebnM7MRe83jacUhyBXo4RhGYN43SkeQO6TfkhZbR4rSrtUdbMsuhkqdYV3c32Nortv4PCUnBZhE9b2vAgTYiV1O1E8TLo5BYS7OzB2Ex147/abjffhdtGRGKHbyNI1g6Gp05rQdLhWvSFEBCFWxmHQ8yguC+r/v2SCpIjD1AakYP78wRV3ClPaIPQmlH2UMCqhFNOWSgy+8143+TWwsEGCCfxmkYnS9utqQ0VA2Ry9/unoNd38Ao5l2oXUHDGQwqrphyskjMo41H0IMFKRPYjKp4pEphMdNo91ow5xS9qpvo39KSwltyRCqaQ3M1xYlvlABA2mmBCvujaKEB0zz7bidRGF0WxLYrX/4CNJ+FYDiYMXOQNgzlYQ9/mfa4pNUcJXhXIuBacXRT1nJnY5xWbW6XdrFMMUUHYQPKYCQqqMlQr7yMR+uN5JU99Qk01lE93HkzyfD5eWrU8nY5gshE8XG+HEiBDacEx81yWcq7TnDb7LAdH7O0bMLWHXT9DPNdROdcbGAtUda3v4buaJwZRYE2QjlZArY/pDdqVUtltiWciu7Av6IYGvvMsl1767Tr75BOUxBa1OrmncjftOInKsfG8VuOWXKSE90p335VseErJ6wKrBMiUMobV1aAZtdwp0eYmtz8QUv0HolnSmTtv2alkYHTtqMXOoNcaKmuCsrvNeHhUwmQUFsjuqTQ8elZsCoBlc2Ehy+CSFchknA0La63xxX9wOnLM1xS5GXr+sev0Ro0N9dH4FI6MdbhbaREfWlb1/4VGYkC7pTEnad+ZUIwAl1Gx310+/oRf2+W6JjrORKK2rwqaU5gIpjnmPYbUKwY0X4mVMeTXt1tFUBUMJjGp5ulAXaOeKuaVvOH0x+CuP/gohyQVPpKfE9rj6FNZlnAwYaqDIeJdP/xMSEG1QRZv+4ecbqCO1fsLUEG4oBRyC97R22kYbLRRb8sHUV7iXsZ6pNj1T7ltVd97bp6+b7erp32CmpryuGBctV2yh65H0F0w+na6UfT5nrpSE99Tv9AW4XKjAK8x3KGB0Z87RmnPR372ttsv86P1p++tOsxUv6XT4/cWBAabZj8TANKFexqnqhj3vqwz0OHoxmE/4s1Ys5pBccMCUo7zg1sXYZXXMPZJong52P60meA5N985sOaTyxDujn5uhrsqzdbcgl7U5y7OMGy0VuVx1XknbBvcxf34S1dO0v+pXIKpe5K1HdRxhQb6Fq+N4NjYKUqGLllSYTJkpMDZF+JdUYgcDF1omiPzE23qfKtARZrYoq58LC1rWBcATxrxKtEFl1FEUCf0yAS181LYXq6yValIBDrTfQnWT6z0jMfs9vXI3TL9v15ECyyASHmaCVedAsuKBDgcXI+hV+FGiIa1R5/18/Ix+vimtDon0LurgSTIlm4itYiRkzARQclFlybcFQDvnHcYhDZT1vl8Ay8MN4cFlj1wESAwd0qoVHONsXVxPAth3d9jsVlznr7tlXqyhqqHaMkpG+HTJi7kxyRw5HOrIZXs+yagHtDcSjiYdSncCinW2Rwt6lUHF7UhFaXAgG3NaXV9v+cUeWh1GLkk5ZJF+WwLUPs4UyZnS2awHqtAffEGBIdtxLE1Zhsj4YzgRtB3eYMjWD7bKSkc4WBffkzCcddppDWB9L7PGWK7O6HP+uO94vj/V1T8avKzL03bS0eWbJIxcKr3JpVnMNyqR8whFo+Np/r1An2PwBj7VP/T3i869t9wxqdXMqoazfBfvNeMtZquJNY7URfSEze1MVi7K4q6tXXg3TME6/meULFx4kcGUt0nMx5LQrgaiCBjAUHEH3vh9/njkA6/SKRIBXA+HtmazO/CAit2Uw1tldRvZH3dJmJqJSiLZMmzzEvVLVjeGjAhXKv7MpECn87TyGsfvqB3MrrN1dhRG2undGRVq23XCak+omgnM34QimUGU1zjVP/3aVQz0pXtvCwgRlVXeNYEQosXTGpZ6M5dcY/hR1kzIB5OoHg2U31/l63UGJTL8yjCiYs8KFPkIuaTKO2X14IZe82FkCFywdYpFZaYQCJFQOeldDkwF6NcwKWKRwxQLcld3Ze9UOTGUcesAfTv9Hx1gJBzOJNKd4sbZ4jarUQSYbUisxyNsFgQwzjXjyV7oVObxxbJCRvnpSECP/Y73w/yRd0+/3KEYrWF9OIOIaVYvRQWEAlW+HuCzVOwIqrc76AFUHwlTM0n8bncNFN0Gdybf7J5+3RiH8T+5Z1eTn565PxLJk6CXSvhpc5t574uWRh4Envmp2KNRtXffKa2flaslXWYTzkeeOb5TOo650bKNQiHhFAl7M0noDG2Wycrratca02QNfX7UKb+A9qTUCIBIFBQ59gfB08m6unWFmlgaRULgpCP18+vzbEVVCoQdN4sChoOxHxnWXRsPWmgUzFpNBfr50xGPQPr1nxtYM5nsvLATiYTPmbA+Kj+xj1XJzI3LB9D+ApWkz7RozyEnfriPDBKXKW3CiC3rBjDBgSSy61B3GsK8YAchAYKvVLVQ7pVCivnUkEs5KINd9rWaZcSn2BuRUDgTpqbwWV43C/B8zqubm2y3o28qDrEdnQunFN4VeTleHs9c+gXjMGzrYmJ+MBK3UxEpBCMRKFEMPO9zuB7PABqD0yGFxU6C4zQv6RscejFq7YKtgeKsvenDCDYSAmdCkSwcQNl4kZcXdEOhmGAG1KBG8eDH/JL3ECvajpnA9CTdJi12TCsmLZHQNvUs6CSfAQ+hqu6O/SmoogkT76qpV/SRXeTfcdN1kblIjZuubZ/GJ4rlRMIiOWNTqrqhOi5UM9ZMYQB7u2y4oEvTIY9G/BEnZ+iIRS5sS9CYP/ikP4/uHUXoouy94zlYD9TyzbyzRTcrNNKN+TzbNfWBE7OTIZWha9mqXX48XcTWvq9k2vpiv4FqOCWxPYgh9JWFQUuI6iqS8+q6OKgsKS87lt/298UFiTA2tY/B+1hJqq5MUzBTCuO6FdBrZpYB4Cj/pmtEysNFv4vQDcTM7RMfCBNEQt2k8rnnshyXe3QFnyv60qFMwG1BVi4A0GhryqrzGR2LATg9K81If6v4DuKjUDi1T8WAeGjOK9n0BxG1vgM2OeIPEKJDbk3khDtGMLJrpXEwEv6mRt8nR2FeHYPXoiQYdLt4EGeQUDbL/Wctb+BCGGkbOdtwnI/UBWwd+J10w7FIAr+lH1bcIrsG4JL2hgZ31YotGMdUiNSN6MMnsgHZEz6RkhPF/dTwgpII48IhBJjv7nPWAdPgsWwAi/iGw7dTlWvakalOTviaOJOsF8/QL31smsWRToPnolKX2bzO1mD/PWvBtcu2LFI8zrSlWgwrhLWpWSdNdDfZziHzLjH9No0tfXAapeVmkfWN7zcIgN8X4DR6t4l1bAFdiUgonDr0xRdykZdr6plOMmoagzAN2+5JiiTBsYxq7lnkcqnCZBIVhTZFyJs6VF2Ce9+s0A2YwW8QRinMDTrp3c/0W2oK6/ux71rsIqNuwGy2bBLyWSR4K7+3ouHLdFIYGaIgjCPveEEdbLHtovYcGkUHuS1xIZDqKWAGhvSRUDU1qtZhb3uJ1SsUpSJ6UZtFmZfg+gnVb8+s1sGH5qRogdNE1sTGjYhQNLUaqIBT39P8/jX3XjarVpE0oY7yc252dXg+LyiNTSCgdOhiAGmCkhUUiaAkJE06VydIREXjEgBITAhZljgajLvEJeCPliQqnFRJ5ANJ3FaqW7S+4Zq2yZdZc4OaX4G+nd/3qhbnkCyicHTAp5u5LqTtKsABvziJ2py0244o3z6uKu9Nti1Yl0DNqJA8KatqbUBi7Fiwo7sFJ4zz3qxg5iJtG6ZTwtY89BZ2Jh3I7zgsDzkatGZR1TdHQUplzLtiM6/KPgRlTQ1z+BZ2cYg5mrmEbWg32eVkuUQShiadq2cevMCMfbffjF0UEvpOsO29g9GpeYWvssUWcKjRsNLZwMsOvgBAKxKOplZ61vtoV0CHnzQ/4+NSKoq9FzllESPk2FpCFhQb6EX2nAinvq3tSCakkiPhZ2oqdqXkpoKfUtv7fD3nMKCTBAQpCpkSlehX2WxC3Kq3YGpIwtWbcDQ1mu6h6+qmqr0Pd3f5bQYCu68S/1DgBiOU/CAMxC7sA+1PcZABF4uErKlRfPd2Pdmu3s8fc2G1Z8Dyp97xNfUrbcXZiiZk+1bKbTSlTJyGSsyMsGYW5Dmhbuoo9sNOMAE1nNG8NJxWfIXnxeJB1hb87S2yZ68wcVnhx5xebB8f3I0ioW/qSPesqHeIhRmFzuJ2u8sgLwO9a4pF1x3kCbuUV3COzcvnrt4u7nRpMLVmhethJORNHatAZIrqav30a+19qq8zXCqGalblnQxxqKGam67JIu3u1DrFPFqyHQturJEQN3UycKh7W+8ZJpYxXXP+uOcL41MLM89A/u8mBFTMULk3X1nAIU6fIw7n252q+HpJgNdULY2YQG8hWZwqKCtSEbDo0/Efh2PFHDwn9IEjYXBSIxCEHQOQwVhgjj39ZvSsvKt92ccEq165C5DAGJ9PzXKEvKnTsB9WQAkIWMhsTx+TUsEMkFZI4rW7pj+uxQcOWWIfCfBxJJCn8WDXVOc7Y8J8W8M/IfYV40D2UqkgRk0NlSOnGZORdrLCBzCqFNamTrWSMpxCU46JKBQwGwAt6gpwntCjC1NX3+wznUjc6RT3BiY4kbA201mgB3r883802NZDJmUDVU4VpxE4rn0c/zOsQiJjFWKDjCJ4CoEzhf9an/+ATD6+qxYZ/RDlR1ThFfMczUF7taYQ7i5dZjoFYjWPK5ATURHUFwn7HWAND2W2OkopPR1nuOdMTuikCu6WdbazPKjEbcEzm/oCeVogfE46VmcQCaAMVSkwBdgCxqOYyNE/pecsVyeduWhq0wSeeyT8TUqyYTisWLbex4YZ0kfgbXxeFhB4AUYGHcNWAOV7uL8MvZVSN8cZrOYmHGciYXCmQdohZN41ANOy8tU1XWN6QCEqu/VmsP6CQCC+QvySw5WzcuGYRUwGsMVwnqkKkTMNxS0AO3qYMOewVfFDHhPWC5Gd/9jsWOFmNCJw45PoKY9KXngJgzMNqYzqYLS3DUQvyusCrpBp6n3IVkPL06n9gAuEVhuvJ1vTgkJAKJxU88USlz7lW/r031ZlVkOP19C2d9Wd0QhvP7WPObziN8+FZx2eUzCbBVPrJp/fWSrnovjbjnXow8bqq7UMw7mAel5Um03WpZcP86zMRqlXKZdZSszSKbZVHG92hcVJx+ktsaiQY9up109fv7RoD6o+BySJ9v3xEBzl8YVNp9dJh97oXliHBogHQu2kf7D3ozverGCfmS9L1uoPAsrEGz6YGYe/pF9C0aqhBobL8lHF4rTeVdx6WiWn+FgSxiNxZP1YsYrax2q33M4pt1FM8EHsXmZ3efmjWTjk81gdYvk8Er/h2SGPqeaa93y/rHNIGCoqpu7uik4ssDO0PQAMzNR3z2OIN5F9tjJjRyQ5T9L3wkPRmezm5gjqYXgw2Y6nYt/kc7kovX7TNj4SSido+NIM/43yKDWZLB3O7Z1KqNJ8WTdCE572MnE61KTWOuxFIyF1UobWA3/D/At9fP/rms2ZAypQXlEXgKssZtENvCEskgVh7LITx6gusmoGJXwqCeZx6rfXuxXkgRLt02+1MfMMFLTglzxq7dzoTDdsELRjwU7tqP4MXIVNmIcvuoT1hOq2dlufPVLW78onxQySecFWzexq1J3spFmWQ+mp0AUwpxmGYlNbDZH2hNaZJnGv/Hp2nwPU0W3r6JsKDFqH19GdPE+2o7BfVbcHIsKJixtkrKc2LTw1EHZnykpNptOkLIw5nfcmewA2uziCfSz9gjwfGhytrzERGi9afeXWmU/oGnNnLtxOOAb7/aLVsNkyTFg2T1/hvkgRKHxGyPu2AaMrD8g++2HYqtA96ZPtK8+/33/JHxeIopj6gmAWpWCYVZv8rmrV1y+K7Zwy+AOWHgfjVyddHKWmLP2gbhAJ5TOl7li6vitKMPTWKBosRGqxpWowcu5HjU+iiOOWrfTEdyikTzpUByf6mO33RkdwlW8eqQyn96wO6Dd0o3aW6YpTBZpOUmOZjgCy53/6/wE=\", BinaryEncoding.Base64), Compression.Deflate)), let _t = ((type nullable text) meta [Serialized.Text = true]) in type table [Column1 = _t]),", - " Source = Table.Combine({Source1, Source2}),", - " #\"Split Column by Delimiter1\" = Table.SplitColumn(Source, \"Column1\", Splitter.SplitTextByDelimiter(\",\", QuoteStyle.Csv), {\"Column1.1\", \"Column1.2\", \"Column1.3\", \"Column1.4\", \"Column1.5\", \"Column1.6\", \"Column1.7\", \"Column1.8\", \"Column1.9\", \"Column1.10\", \"Column1.11\", \"Column1.12\", \"Column1.13\"}),", - " #\"Promoted Headers\" = Table.PromoteHeaders(#\"Split Column by Delimiter1\", [PromoteAllScalars=true]),", - " #\"Replaced Value\" = Table.ReplaceValue(#\"Promoted Headers\",\"NULL\",null,Replacer.ReplaceValue,{\"CustomerKey\", \"Gender\", \"Name\", \"Address\", \"City\", \"State Code\", \"State\", \"Zip Code\", \"Country Code\", \"Country\", \"Continent\", \"Birthday\", \"Age\"})", - "in", - " #\"Replaced Value\"" - ], - "kind": "m", - "lineageTag": "cd034b2b-6d5e-4420-9158-90f5ead53ec3", - "queryGroup": "Raw Data" - }, - { - "name": "RAW-Store", - "annotations": [ - { - "name": "PBI_NavigationStepName", - "value": "Navigation" - }, - { - "name": "PBI_ResultType", - "value": "Table" - } - ], - "expression": [ - "let", - " Source = Table.FromRows(Json.Document(Binary.Decompress(Binary.FromText(\"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\", BinaryEncoding.Base64), Compression.Deflate)), let _t = ((type nullable text) meta [Serialized.Text = true]) in type table [Column1 = _t]),", - " #\"Split Column by Delimiter\" = Table.SplitColumn(Source, \"Column1\", Splitter.SplitTextByDelimiter(\",\", QuoteStyle.Csv), {\"Column1.1\", \"Column1.2\", \"Column1.3\", \"Column1.4\", \"Column1.5\", \"Column1.6\", \"Column1.7\", \"Column1.8\", \"Column1.9\"}),", - " #\"Promoted Headers\" = Table.PromoteHeaders(#\"Split Column by Delimiter\", [PromoteAllScalars=true]),", - " #\"Replaced Value\" = Table.ReplaceValue(#\"Promoted Headers\",\"NULL\",null,Replacer.ReplaceValue,{\"StoreKey\", \"Store Code\", \"Country\", \"State\", \"Name\", \"Square Meters\", \"Open Date\", \"Close Date\", \"Status\"})", - "in", - " #\"Replaced Value\"" - ], - "kind": "m", - "lineageTag": "524dec4a-0544-44a7-8d45-cd6794082fe6", - "queryGroup": "Raw Data" - }, - { - "name": "RAW-CurrencyExchange", - "annotations": [ - { - "name": "PBI_NavigationStepName", - "value": "Navigation" - }, - { - "name": "PBI_ResultType", - "value": "Table" - } - ], - "expression": [ - "let", - " Source = Table.FromRows(Json.Document(Binary.Decompress(Binary.FromText(\"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\", BinaryEncoding.Base64), Compression.Deflate)), let _t = ((type nullable text) meta [Serialized.Text = true]) in type table [Column1 = _t]),", - " #\"Split Column by Delimiter1\" = Table.SplitColumn(Source, \"Column1\", Splitter.SplitTextByDelimiter(\",\", QuoteStyle.Csv), {\"Column1.1\", \"Column1.2\", \"Column1.3\", \"Column1.4\"}),", - " #\"Promoted Headers1\" = Table.PromoteHeaders(#\"Split Column by Delimiter1\", [PromoteAllScalars=true])", - "in", - " #\"Promoted Headers1\"" - ], - "kind": "m", - "lineageTag": "78c5038c-6adf-4b1f-85fa-879cb022b266", - "queryGroup": "Raw Data" - }, - { - "name": "RangeStart", - "annotations": [ - { - "name": "PBI_ResultType", - "value": "DateTime" - } - ], - "expression": "#datetime(2020, 1, 1, 0, 0, 0) meta [IsParameterQuery=true, List={#datetime(2020, 1, 1, 0, 0, 0)}, DefaultValue=#datetime(2020, 1, 1, 0, 0, 0), Type=\"DateTime\", IsParameterQueryRequired=true]", - "kind": "m", - "lineageTag": "a1a6bc94-b59e-4ec1-9b6d-cd637ebafff5" - }, - { - "name": "RangeEnd", - "annotations": [ - { - "name": "PBI_ResultType", - "value": "DateTime" - } - ], - "expression": "#datetime(2023, 2, 1, 0, 0, 0) meta [IsParameterQuery=true, List={#datetime(2023, 12, 31, 0, 0, 0)}, DefaultValue=#datetime(2023, 12, 31, 0, 0, 0), Type=\"DateTime\", IsParameterQueryRequired=true]", - "kind": "m", - "lineageTag": "3230a904-a045-4d7e-9424-64fc69c93735" - }, - { - "name": "RAW-SalesDateAdjustedAndSalesRandomized", - "annotations": [ - { - "name": "PBI_NavigationStepName", - "value": "Navigation" - }, - { - "name": "PBI_ResultType", - "value": "Table" - } - ], - "expression": [ - "let", - " Source = #\"RAW-Sales\",", - " #\"Changed Type\" = Table.TransformColumnTypes(Source,{{\"Order Date\", type datetime}, {\"Delivery Date\", type datetime}}),", - " minDate = List.Min(#\"Changed Type\"[Order Date]),", - " numYearsOnDummyData = 3,", - " yearsDiff = (Date.Year(DateTime.LocalNow()) - numYearsOnDummyData) - Date.Year(minDate),", - " #\"AdjustDates\" = Table.TransformColumns(#\"Changed Type\",{{\"Order Date\", each Date.AddYears(_, yearsDiff)}", - " , {\"Delivery Date\", each Date.AddYears(_, yearsDiff)}", - " }),", - " #\"Filtered Rows\" = Table.SelectRows(AdjustDates, each true),", - " #\"Changed Type2\" = Table.TransformColumnTypes(#\"Filtered Rows\",{{\"Quantity\", Int64.Type}, {\"Unit Price\", type number}, {\"Net Price\", type number}, {\"Unit Cost\", type number}}),", - " #\"Added Custom\" = Table.AddColumn(#\"Changed Type2\", \"NewQuantity\", each Number.RoundDown(Number.RandomBetween(", - "List.Max({1, [Quantity] - ([Quantity] * Randomizer)})", - ",", - "List.Min({[Quantity], [Quantity] + ([Quantity] * Randomizer)", - "})", - ")", - ")),", - " #\"Added Custom1\" = Table.AddColumn(#\"Added Custom\", \"NewNetPrice\", each Number.Round(", - "Number.RandomBetween(", - "List.Max({1, [Net Price] - ([Net Price] * Randomizer)})", - ",", - "List.Min({[Net Price], [Net Price] + ([Net Price] * Randomizer)", - "})", - "), 3)),", - " #\"Removed Columns\" = Table.RemoveColumns(#\"Added Custom1\",{\"Quantity\", \"Net Price\"}),", - " #\"Renamed Columns\" = Table.RenameColumns(#\"Removed Columns\",{{\"NewQuantity\", \"Quantity\"}, {\"NewNetPrice\", \"Net Price\"}})", - "in", - " #\"Renamed Columns\"" - ], - "kind": "m", - "lineageTag": "0bd410b3-1a0d-49c7-8952-7a7465778b08", - "queryGroup": "Raw Data" - }, - { - "name": "Environment", - "annotations": [ - { - "name": "PBI_ResultType", - "value": "Text" - } - ], - "description": "Dummy parameter to simulate data from different servers", - "expression": "\"DEV\" meta [IsParameterQuery=true, List={\"DEV\", \"QUAL\", \"PRD\"}, DefaultValue=\"DEV\", Type=\"Text\", IsParameterQueryRequired=true]", - "kind": "m", - "lineageTag": "64edd943-1a90-4438-b62f-bb95a9da1510" - }, - { - "name": "Randomizer", - "annotations": [ - { - "name": "PBI_ResultType", - "value": "Number" - } - ], - "expression": "0.7 meta [IsParameterQuery=true, Type=\"Number\", IsParameterQueryRequired=true]", - "kind": "m", - "lineageTag": "c92d2f56-fbbe-4162-b5ce-373932bf5cf2" - } - ], - "perspectives": [ - { - "name": "Sales", - "tables": [ - { - "name": "Sales", - "columns": [ - { - "name": "Quantity" - }, - { - "name": "Order Number" - }, - { - "name": "Line Number" - }, - { - "name": "Order Date" - }, - { - "name": "Delivery Date" - }, - { - "name": "CustomerKey" - }, - { - "name": "StoreKey" - }, - { - "name": "ProductKey" - }, - { - "name": "Net Price" - }, - { - "name": "Unit Cost" - }, - { - "name": "Currency Code" - }, - { - "name": "Exchange Rate" - }, - { - "name": "Environment" - } - ], - "measures": [ - { - "name": "# Customers (with Sales)" - }, - { - "name": "# Products (with Sales)" - }, - { - "name": "Sales Qty" - }, - { - "name": "Sales Amount" - }, - { - "name": "Sales Amount (Δ LY)" - }, - { - "name": "Sales Amount (LY)" - }, - { - "name": "Sales Amount (YTD, LY)" - }, - { - "name": "Sales Amount (YTD)" - }, - { - "name": "Sales Amount Avg per Day" - }, - { - "name": "Sales Amount (% Δ LY)" - }, - { - "name": "Margin" - }, - { - "name": "Margin (ly)" - }, - { - "name": "# Sales" - }, - { - "name": "Sales Qty by Delivery Date" - }, - { - "name": "Sales Amount (12M average)" - }, - { - "name": "Sales Amount (6M average)" - } - ] - }, - { - "name": "Product", - "columns": [ - { - "name": "Product" - }, - { - "name": "ProductKey" - }, - { - "name": "Product Code" - }, - { - "name": "Manufacturer" - }, - { - "name": "Brand" - }, - { - "name": "Color" - }, - { - "name": "Weight Unit Measure" - }, - { - "name": "Weight" - }, - { - "name": "Unit Cost" - }, - { - "name": "Unit Price" - }, - { - "name": "Subcategory Code" - }, - { - "name": "Subcategory" - }, - { - "name": "Category Code" - }, - { - "name": "Category" - } - ], - "measures": [ - { - "name": "# Products" - } - ] - } - ] - } - ], - "queryGroups": [ - { - "annotations": [ - { - "name": "PBI_QueryGroupOrder", - "value": "0" - } - ], - "folder": "Raw Data" - } - ], - "relationships": [ - { - "name": "d4e6dc5a-6f46-443d-ab94-4cc0e10323c6", - "fromColumn": "CustomerKey", - "fromTable": "Sales", - "toColumn": "CustomerKey", - "toTable": "Customer" - }, - { - "name": "434e79a9-f527-481f-accd-8bc60ed1370e", - "fromColumn": "Order Date", - "fromTable": "Sales", - "toColumn": "Date", - "toTable": "Calendar" - }, - { - "name": "21bd108e-527d-4566-be7d-9e474c858ee0", - "fromColumn": "Delivery Date", - "fromTable": "Sales", - "isActive": false, - "toColumn": "Date", - "toTable": "Calendar" - }, - { - "name": "bb5c5591-a0ff-4ce4-a62e-6c5f56006368", - "fromColumn": "ProductKey", - "fromTable": "Sales", - "toColumn": "ProductKey", - "toTable": "Product" - }, - { - "name": "55a6f513-c6f2-4d1c-b8aa-46edeaeb23f2", - "fromColumn": "StoreKey", - "fromTable": "Sales", - "toColumn": "StoreKey", - "toTable": "Store" - } - ], - "roles": [ - { - "name": "Stores Cluster 1", - "annotations": [ - { - "name": "PBI_Id", - "value": "3c40ccf098eb4253ad31f8c679e140d3" - } - ], - "modelPermission": "read", - "tablePermissions": [ - { - "name": "Store", - "filterExpression": "'Store'[Store Code] IN {\"1\",\"2\",\"4\"}" - } - ] - }, - { - "name": "Stores Cluster 2", - "annotations": [ - { - "name": "PBI_Id", - "value": "160d7a327dfd4ca2996841bfdde3567d" - } - ], - "modelPermission": "read", - "tablePermissions": [ - { - "name": "Store", - "filterExpression": "'Store'[Store Code] IN {\"10\",\"11\",\"15\",\"8\"}" - } - ] - } - ], - "sourceQueryCulture": "en-US", - "tables": [ - { - "name": "Calendar", - "annotations": [ - { - "name": "PBI_Id", - "value": "9eaa3654-c4d6-42e5-a057-348df1b3f460" - }, - { - "name": "LinkedQueryName", - "value": "Calendar" - }, - { - "name": "PBI_QueryRelationships", - "value": "{\"columnCount\":25,\"keyColumnNames\":[],\"queryRelationships\":[],\"columnIdentities\":[\"Section1/Calendar/ChangedType.{Column1,0}\",\"Section1/Calendar/Changed Type.{DateId,1}\",\"Section1/Calendar/Changed Type.{Day,2}\",\"Section1/Calendar/Changed Type.{Week Day (#),3}\",\"Section1/Calendar/Capitalized Each Word.{Week Day,15}\",\"Section1/Calendar/Changed Type.{Week,5}\",\"Section1/Calendar/Capitalized Each Word.{Month (Long),8}\",\"Section1/Calendar/Capitalized Each Word.{Month,9}\",\"Section1/Calendar/Changed Type.{Month (#),8}\",\"Section1/Calendar/Changed Type.{Quarter,9}\",\"Section1/Calendar/Changed Type.{Semester,10}\",\"Section1/Calendar/Changed Type.{Year,11}\",\"Section1/Calendar/Changed Type.{Week (Year),12}\",\"Section1/Calendar/Capitalized Each Word.{Month (Year),11}\",\"Section1/Calendar/Changed Type.{Quarter (Year),14}\",\"Section1/Calendar/Changed Type.{Semester (Year),15}\",\"Section1/Calendar/Changed Type.{WeekYearId,16}\",\"Section1/Calendar/Changed Type.{MonthYearId,17}\",\"Section1/Calendar/Changed Type.{QuarterYearId,18}\",\"Section1/Calendar/Changed Type.{SemesterYearId,19}\",\"Section1/Calendar/Changed Type.{Day (Relative),20}\",\"Section1/Calendar/Changed Type.{Month (Relative),21}\",\"Section1/Calendar/Changed Type.{Year (Relative),22}\",\"Section1/Calendar/Changed Type.{Holiday,23}\",\"Section1/Calendar/Changed Type.{Work Day,24}\"],\"ColumnCount\":25,\"KeyColumnNames\":[],\"ColumnIdentities\":[\"Section1/Calendar/ChangedType.{Column1,0}\",\"Section1/Calendar/Changed Type.{DateId,1}\",\"Section1/Calendar/Changed Type.{Day,2}\",\"Section1/Calendar/Changed Type.{Week Day (#),3}\",\"Section1/Calendar/Capitalized Each Word.{Week Day,15}\",\"Section1/Calendar/Changed Type.{Week,5}\",\"Section1/Calendar/Capitalized Each Word.{Month (Long),8}\",\"Section1/Calendar/Capitalized Each Word.{Month,9}\",\"Section1/Calendar/Changed Type.{Month (#),8}\",\"Section1/Calendar/Changed Type.{Quarter,9}\",\"Section1/Calendar/Changed Type.{Semester,10}\",\"Section1/Calendar/Changed Type.{Year,11}\",\"Section1/Calendar/Changed Type.{Week (Year),12}\",\"Section1/Calendar/Capitalized Each Word.{Month (Year),11}\",\"Section1/Calendar/Changed Type.{Quarter (Year),14}\",\"Section1/Calendar/Changed Type.{Semester (Year),15}\",\"Section1/Calendar/Changed Type.{WeekYearId,16}\",\"Section1/Calendar/Changed Type.{MonthYearId,17}\",\"Section1/Calendar/Changed Type.{QuarterYearId,18}\",\"Section1/Calendar/Changed Type.{SemesterYearId,19}\",\"Section1/Calendar/Changed Type.{Day (Relative),20}\",\"Section1/Calendar/Changed Type.{Month (Relative),21}\",\"Section1/Calendar/Changed Type.{Year (Relative),22}\",\"Section1/Calendar/Changed Type.{Holiday,23}\",\"Section1/Calendar/Changed Type.{Work Day,24}\"],\"RelationshipInfo\":[]}" - }, - { - "name": "PBI_ResultType", - "value": "Table" - } - ], - "columns": [ - { - "name": "Date", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - }, - { - "name": "UnderlyingDateTimeDataType", - "value": "Date" - }, - { - "name": "BestPracticeAnalyzer_IgnoreRules", - "value": "{\"RuleIDs\":[\"RELATIONSHIP_COLUMNS_SHOULD_BE_OF_INTEGER_DATA_TYPE\"]}" - }, - { - "name": "PBI_FormatHint", - "value": "{\"isDateTimeCustom\":true}" - } - ], - "changedProperties": [ - { - "property": "FormatString" - } - ], - "dataType": "dateTime", - "formatString": "yyyy-mm-dd", - "isKey": true, - "lineageTag": "ede68123-9903-412b-8747-d0cb8117ed41", - "sourceColumn": "Date", - "summarizeBy": "none" - }, - { - "name": "Day", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "User" - } - ], - "dataType": "int64", - "formatString": "0", - "lineageTag": "15ee1357-a55a-4206-b87f-4537950cefd1", - "sourceColumn": "Day", - "summarizeBy": "none" - }, - { - "name": "Week Day", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - } - ], - "changedProperties": [ - { - "property": "SortByColumn" - } - ], - "dataType": "string", - "lineageTag": "b249ac49-0964-4b34-ab62-786c1f6d7709", - "sortByColumn": "Week Day (#)", - "sourceColumn": "Week Day", - "summarizeBy": "none" - }, - { - "name": "Week", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "User" - } - ], - "dataType": "int64", - "formatString": "0", - "lineageTag": "0b47fd7c-b526-4a87-8304-50f9da4c79ed", - "sourceColumn": "Week", - "summarizeBy": "none" - }, - { - "name": "Month", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - } - ], - "changedProperties": [ - { - "property": "SortByColumn" - } - ], - "dataType": "string", - "lineageTag": "229b9d13-a91e-42b0-9d59-bf1aa3568da7", - "sortByColumn": "Month (#)", - "sourceColumn": "Month", - "summarizeBy": "none" - }, - { - "name": "Quarter", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "User" - } - ], - "dataType": "int64", - "formatString": "0", - "lineageTag": "0d2804c4-111e-4544-aaf6-c6a6988e49a1", - "sourceColumn": "Quarter", - "summarizeBy": "none" - }, - { - "name": "Semester", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "User" - } - ], - "dataType": "int64", - "formatString": "0", - "lineageTag": "1fa538c1-2ae2-4bbf-81ba-ef222e5944ed", - "sourceColumn": "Semester", - "summarizeBy": "none" - }, - { - "name": "Year", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "User" - } - ], - "dataType": "int64", - "formatString": "0", - "lineageTag": "e2e54129-d9ea-422d-a07a-747ce9981020", - "sourceColumn": "Year", - "summarizeBy": "none" - }, - { - "name": "Week (Year)", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - } - ], - "changedProperties": [ - { - "property": "SortByColumn" - } - ], - "dataType": "string", - "lineageTag": "6977be25-6a48-4eac-846f-85a3c3c271fe", - "sortByColumn": "WeekYearId", - "sourceColumn": "Week (Year)", - "summarizeBy": "none" - }, - { - "name": "Month (Year)", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - } - ], - "changedProperties": [ - { - "property": "SortByColumn" - } - ], - "dataType": "string", - "lineageTag": "557c5e05-696f-4418-85cc-b086c08c3d61", - "sortByColumn": "MonthYearId", - "sourceColumn": "Month (Year)", - "summarizeBy": "none" - }, - { - "name": "Quarter (Year)", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - } - ], - "changedProperties": [ - { - "property": "SortByColumn" - } - ], - "dataType": "string", - "lineageTag": "c0e2abff-7224-4166-b0a9-2fef49976c82", - "sortByColumn": "QuarterYearId", - "sourceColumn": "Quarter (Year)", - "summarizeBy": "none" - }, - { - "name": "Semester (Year)", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - } - ], - "changedProperties": [ - { - "property": "SortByColumn" - } - ], - "dataType": "string", - "lineageTag": "b2b0b830-1b3e-4e9e-b87d-585f4a23deeb", - "sortByColumn": "SemesterYearId", - "sourceColumn": "Semester (Year)", - "summarizeBy": "none" - }, - { - "name": "WeekYearId", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "User" - } - ], - "changedProperties": [ - { - "property": "IsHidden" - } - ], - "dataType": "int64", - "formatString": "0", - "isHidden": true, - "lineageTag": "90bba6d9-d308-40f6-826b-e4d5ca8fda78", - "sourceColumn": "WeekYearId", - "summarizeBy": "none" - }, - { - "name": "MonthYearId", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "User" - } - ], - "changedProperties": [ - { - "property": "IsHidden" - } - ], - "dataType": "int64", - "formatString": "0", - "isHidden": true, - "lineageTag": "81e0ba4a-f927-4152-843a-dfc0e3bd858c", - "sourceColumn": "MonthYearId", - "summarizeBy": "none" - }, - { - "name": "QuarterYearId", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "User" - } - ], - "changedProperties": [ - { - "property": "IsHidden" - } - ], - "dataType": "int64", - "formatString": "0", - "isHidden": true, - "lineageTag": "72f566a1-f253-4c96-ba97-47c76d5ce07c", - "sourceColumn": "QuarterYearId", - "summarizeBy": "none" - }, - { - "name": "SemesterYearId", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "User" - } - ], - "changedProperties": [ - { - "property": "IsHidden" - } - ], - "dataType": "int64", - "formatString": "0", - "isHidden": true, - "lineageTag": "9c42bf51-c759-439c-b02e-1861c41f56c4", - "sourceColumn": "SemesterYearId", - "summarizeBy": "none" - }, - { - "name": "Week Day (#)", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "User" - } - ], - "dataType": "int64", - "formatString": "0", - "lineageTag": "5559c18c-3ef4-4f17-bce2-e0a7cd5e6dea", - "sourceColumn": "Week Day (#)", - "summarizeBy": "none" - }, - { - "name": "Month (#)", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "User" - } - ], - "dataType": "int64", - "formatString": "0", - "lineageTag": "337dbbc4-139c-47a8-bb39-85c6deba31c8", - "sourceColumn": "Month (#)", - "summarizeBy": "none" - }, - { - "name": "Day (Relative)", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "User" - } - ], - "dataType": "int64", - "formatString": "0", - "lineageTag": "6eee588e-1d25-4994-a6f6-1f94ef196fef", - "sourceColumn": "Day (Relative)", - "summarizeBy": "none" - }, - { - "name": "Month (Relative)", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "User" - } - ], - "dataType": "int64", - "formatString": "0", - "lineageTag": "f884824e-2aa5-4a3c-9b59-89e31181a6bb", - "sourceColumn": "Month (Relative)", - "summarizeBy": "none" - }, - { - "name": "Year (Relative)", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "User" - } - ], - "dataType": "int64", - "formatString": "0", - "lineageTag": "f9ccaf5a-3f08-42f5-bce9-2f67e78a4173", - "sourceColumn": "Year (Relative)", - "summarizeBy": "none" - }, - { - "name": "Work Day", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - } - ], - "dataType": "string", - "lineageTag": "f58c6828-fdf1-4c2a-9e2f-59454b2f43b5", - "sourceColumn": "Work Day", - "summarizeBy": "none" - }, - { - "name": "DateId", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "User" - }, - { - "name": "BestPracticeAnalyzer_IgnoreRules", - "value": "{\"RuleIDs\":[\"ISAVAILABLEINMDX_FALSE_NONATTRIBUTE_COLUMNS\",\"UNNECESSARY_COLUMNS\"]}" - } - ], - "changedProperties": [ - { - "property": "IsHidden" - } - ], - "dataType": "int64", - "formatString": "0", - "isHidden": true, - "lineageTag": "5e5b06be-4f4f-4624-a922-74de16645764", - "sourceColumn": "DateId", - "summarizeBy": "none" - }, - { - "name": "Month (Long)", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - } - ], - "changedProperties": [ - { - "property": "SortByColumn" - } - ], - "dataType": "string", - "lineageTag": "b27d8a5e-c4bd-4a82-a4c8-bb402aefa873", - "sortByColumn": "Month (#)", - "sourceColumn": "Month (Long)", - "summarizeBy": "none" - }, - { - "name": "Week (Relative)", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - } - ], - "dataType": "int64", - "formatString": "0", - "lineageTag": "9e0d9793-9a33-451a-bc05-24d7878c8964", - "sourceColumn": "Week (Relative)", - "summarizeBy": "none" - }, - { - "name": "Week Start Date", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - }, - { - "name": "UnderlyingDateTimeDataType", - "value": "Date" - } - ], - "dataType": "dateTime", - "formatString": "yyyy-mm-dd", - "lineageTag": "138a3a63-bb64-469c-a94e-1a65757bd1d1", - "sourceColumn": "Week Start Date", - "summarizeBy": "none" - }, - { - "name": "Week End Date", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - }, - { - "name": "UnderlyingDateTimeDataType", - "value": "Date" - } - ], - "dataType": "dateTime", - "formatString": "yyyy-mm-dd", - "lineageTag": "c91844bb-d8e2-49b1-97e8-7ff79f35faf6", - "sourceColumn": "Week End Date", - "summarizeBy": "none" - }, - { - "name": "Month Start Date", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - }, - { - "name": "UnderlyingDateTimeDataType", - "value": "Date" - } - ], - "dataType": "dateTime", - "formatString": "yyyy-mmm", - "lineageTag": "9398fb3c-c0de-4357-afc0-69df70186f6d", - "sourceColumn": "Month Start Date", - "summarizeBy": "none" - }, - { - "name": "Date (Year-Month)", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - }, - { - "name": "PBI_FormatHint", - "value": "{\"isCustom\":true}" - } - ], - "dataType": "dateTime", - "expression": "DATE([Year], [Month (#)],1)", - "formatString": "mmm yyyy", - "isDataTypeInferred": true, - "lineageTag": "0e65a7a5-6c15-437d-8333-10653d8d673f", - "summarizeBy": "none", - "type": "calculated" - } - ], - "dataCategory": "Time", - "description": "Calendar table", - "hierarchies": [ - { - "name": "Year-Month-Day", - "levels": [ - { - "name": "Year", - "column": "Year", - "lineageTag": "5d3a5413-d08c-47e5-866e-894c288c29b0", - "ordinal": 0 - }, - { - "name": "Month", - "column": "Month", - "lineageTag": "4009bc63-dec6-46e9-84eb-679578b38105", - "ordinal": 1 - }, - { - "name": "Day", - "column": "Day", - "lineageTag": "6c697b75-14d3-4471-b6ad-80d79cd35f23", - "ordinal": 2 - } - ], - "lineageTag": "d00d0618-e29f-486f-a4d4-dea56417f06b" - } - ], - "lineageTag": "bfa0074c-0a44-4a9c-8bba-c87af778b3d7", - "partitions": [ - { - "name": "Calendar-c9bc757b-0dad-4b99-8287-18a451a3c5c3", - "annotations": [ - { - "name": "BestPracticeAnalyzer_IgnoreRules", - "value": "{\"RuleIDs\":[\"MINIMIZE_POWER_QUERY_TRANSFORMATIONS\"]}" - } - ], - "mode": "import", - "source": { - "expression": [ - "let ", - "", - " P_Today = DateTime.LocalNow(),", - " P_StartDate = #date(Date.Year(List.Min(#\"RAW-SalesDateAdjustedAndSalesRandomized\"[Order Date])), 1, 1),", - " P_EndDate = #date(Date.Year(List.Max(#\"RAW-SalesDateAdjustedAndSalesRandomized\"[Order Date])), 12, 31), ", - " P_FirstDayOfWeek = 1,", - " P_IsCarnivalHoliday = true,", - " P_UseIsoWeek = true,", - " P_Culture = \"en-US\",", - " DayCount = Duration.Days(Duration.From(P_EndDate - P_StartDate)) + 1,", - " Source = List.Dates(P_StartDate,DayCount,#duration(1,0,0,0)),", - " TableFromList = Table.FromList(Source, Splitter.SplitByNothing()), ", - " ChangedType = Table.TransformColumnTypes(TableFromList,{{\"Column1\", type date}}),", - " RenamedColumns = Table.RenameColumns(ChangedType,{{\"Column1\", \"Date\"}}),", - " InsertId = Table.AddColumn(RenamedColumns, \"DateId\", each Date.Year([Date])*10000 + Date.Month([Date])*100 +Date.Day([Date])),", - " InsertYear = Table.AddColumn(InsertId, \"Year\", each Date.Year([Date])),", - " InsertQuarter = Table.AddColumn(InsertYear, \"Quarter\", each Date.QuarterOfYear([Date])),", - " InsertSemester = Table.AddColumn(InsertQuarter, \"Semester\", each if [Quarter] < 3 then 1 else 2),", - " InsertMonth = Table.AddColumn(InsertSemester, \"Month (#)\", each Date.Month([Date])),", - " // Simple week", - " InsertWeekYear = Table.AddColumn(InsertMonth, \"WeekYear\", each [Year]),", - " InsertWeek = Table.AddColumn(InsertWeekYear, \"Week\", each Date.WeekOfYear([Date], P_FirstDayOfWeek )),", - " // ISO Week", - " InsertIsoYear = Table.AddColumn(InsertMonth, \"WeekYear\", each Date.Year(Date.AddDays([Date], 4-(Date.DayOfWeek([Date], Day.Monday) + 1)))),", - " InsertIsoWeek = Table.AddColumn(InsertIsoYear, \"Week\", each Duration.Days(Date.AddDays( [Date], 4-(Date.DayOfWeek([Date], Day.Monday) + 1)) - #date([WeekYear], 1 , 7 - Date.DayOfWeek( #date([WeekYear],1,4), Day.Monday)) ) / 7 + 1),", - " // Choose beetween simple Week and Iso Week according to parameter", - " ChosenWeek = if P_UseIsoWeek = true then InsertIsoWeek else InsertWeek,", - " ", - " InsertDay = Table.AddColumn(ChosenWeek, \"Day\", each Date.Day([Date])), ", - " InsertMonthName = Table.AddColumn(InsertDay, \"Month (Long)\", each Date.ToText([Date], \"MMMM\", P_Culture), type text),", - " InsertShortMonthName = Table.AddColumn(InsertMonthName, \"Month\", each try(Text.Range([#\"Month (Long)\"],0,3)) otherwise [#\"Month (Long)\"]),", - " InsertCalendarWeek = Table.AddColumn(InsertShortMonthName, \"Week (Year)\", each \"W\" & Number.ToText([Week]) & \" \" & Number.ToText([WeekYear])),", - " InsertCalendarMonth = Table.AddColumn(InsertCalendarWeek, \"Month (Year)\", each [#\"Month\"] & \" \" & Number.ToText([Year])),", - " InsertCalendarQtr = Table.AddColumn(InsertCalendarMonth, \"Quarter (Year)\", each \"Q\" & Number.ToText([Quarter]) & \" \" & Number.ToText([Year])), ", - " InsertCalendarSem = Table.AddColumn(InsertCalendarQtr, \"Semester (Year)\", each \"S\" & Number.ToText([Semester]) & \" \" & Number.ToText([Year])), ", - " InsertDayWeek = Table.AddColumn(InsertCalendarSem , \"Week Day (#)\", each Date.DayOfWeek([Date], P_FirstDayOfWeek ) + 1),", - " InsertDayName = Table.AddColumn(InsertDayWeek, \"Week Day\", each Date.ToText([Date], \"dddd\", P_Culture), type text),", - " InsertWeekYearId = Table.AddColumn(InsertDayName, \"WeekYearId\", each [WeekYear] * 100 + [Week]),", - " InsertMonthYear = Table.AddColumn(InsertWeekYearId, \"MonthYearId\", each [Year] *100 + [#\"Month (#)\"]),", - " InsertWeekStartDate = Table.AddColumn(InsertMonthYear , \"Week Start Date\", each Date.StartOfWeek([Date], P_FirstDayOfWeek), type date),", - " InsertWeekEndDate = Table.AddColumn(InsertWeekStartDate , \"Week End Date\", each Date.EndOfWeek([Date], P_FirstDayOfWeek), type date),", - " InsertQuarterYear = Table.AddColumn(InsertWeekEndDate, \"QuarterYearId\", each [Year] * 100 + [Quarter]),", - " InsertSemesterYear = Table.AddColumn(InsertQuarterYear, \"SemesterYearId\", each [Year] * 100 + [Semester]),", - " #\"Capitalized Each Word\" = Table.TransformColumns(InsertSemesterYear,{{\"Month (Long)\", Text.Proper}, {\"Month\", Text.Proper}, {\"Month (Year)\", Text.Proper}, {\"Week Day\", Text.Proper}}),", - " #\"Relative (Year)\" = Table.AddColumn(#\"Capitalized Each Word\", \"Year (Relative)\", each [Year] - Date.Year(P_Today)),", - " #\"Relative (Month)\" = Table.AddColumn(#\"Relative (Year)\", \"Month (Relative)\", each [#\"Year (Relative)\"] * 12 + ([#\"Month (#)\"] - Date.Month(P_Today))),", - " #\"Relative (Week)\" = Table.AddColumn(#\"Relative (Month)\", \"Week (Relative)\", each Duration.TotalDays(DateTime.Date(Date.StartOfWeek([Date])) - DateTime.Date(Date.StartOfWeek(P_Today))) / 7),", - " #\"Relative (Day)\" = Table.AddColumn(#\"Relative (Week)\", \"Day (Relative)\", each Duration.TotalDays([Date] - DateTime.Date(P_Today))),", - " AddedWorkDay =Table.AddColumn(#\"Relative (Day)\", \"Work Day\", each if [#\"Week Day (#)\"] > 5 then \"Weekend\" else \"WorkDay\"),", - " #\"Reordered Columns\" = Table.ReorderColumns(AddedWorkDay,{\"Date\", \"Day\", \"Week Day (#)\", \"Week Day\", \"Week\", \"Month (Long)\", \"Month\", \"Month (#)\", \"Quarter\", \"Semester\", \"Year\", \"Week (Year)\", \"Month (Year)\", \"Quarter (Year)\", \"Semester (Year)\", \"WeekYearId\", \"MonthYearId\", \"QuarterYearId\", \"SemesterYearId\", \"Day (Relative)\", \"Week (Relative)\", \"Month (Relative)\", \"Year (Relative)\", \"Work Day\"}),", - " #\"Removed Columns\" = Table.RemoveColumns(#\"Reordered Columns\",{\"WeekYear\"}),", - " #\"Changed Type\" = Table.TransformColumnTypes(#\"Removed Columns\",{{\"Day\", Int64.Type}, {\"Week Day (#)\", Int64.Type}, {\"Week\", Int64.Type}, {\"Month (#)\", Int64.Type}, {\"Quarter\", Int64.Type}, {\"Semester\", Int64.Type}, {\"Year\", Int64.Type}, {\"Week (Year)\", type text}, {\"Quarter (Year)\", type text}, {\"Semester (Year)\", type text}, {\"WeekYearId\", Int64.Type}, {\"SemesterYearId\", Int64.Type}, {\"MonthYearId\", Int64.Type}, {\"QuarterYearId\", Int64.Type}, {\"Day (Relative)\", Int64.Type}, {\"Month (Relative)\", Int64.Type}, {\"Year (Relative)\", Int64.Type}, {\"Work Day\", type text}, {\"DateId\", Int64.Type}, {\"Week (Relative)\", Int64.Type}}),", - " #\"Added Custom\" = Table.AddColumn(#\"Changed Type\", \"Month Start Date\", each #date([Year],[#\"Month (#)\"],1)),", - " #\"Changed Type1\" = Table.TransformColumnTypes(#\"Added Custom\",{{\"Month Start Date\", type date}})", - "in", - " #\"Changed Type1\"" - ], - "type": "m" - } - } - ] - }, - { - "name": "Sales", - "annotations": [ - { - "name": "PBI_Id", - "value": "975ddcc4-65e2-4eb0-9c9c-2c5ef0586f0e" - }, - { - "name": "LinkedQueryName", - "value": "Sales" - }, - { - "name": "PBI_QueryRelationships", - "value": "{\"columnCount\":7,\"keyColumnNames\":[],\"queryRelationships\":[],\"columnIdentities\":[\"Section1/Sales/Changed Type.{FK_Customer,0}\",\"Section1/Sales/Changed Type.{FK_Product,1}\",\"Section1/Sales/Changed Type.{FK_SalesDate,2}\",\"Section1/Sales/Changed Type.{Quantity,3}\",\"Section1/Sales/Changed Type.{UnitPrice,4}\",\"Section1/Sales/Changed Type.{Discount,5}\",\"Section1/Sales/Changed Type.{TotalAmount,6}\"],\"ColumnCount\":7,\"KeyColumnNames\":[],\"ColumnIdentities\":[\"Section1/Sales/Changed Type.{FK_Customer,0}\",\"Section1/Sales/Changed Type.{FK_Product,1}\",\"Section1/Sales/Changed Type.{FK_SalesDate,2}\",\"Section1/Sales/Changed Type.{Quantity,3}\",\"Section1/Sales/Changed Type.{UnitPrice,4}\",\"Section1/Sales/Changed Type.{Discount,5}\",\"Section1/Sales/Changed Type.{TotalAmount,6}\"],\"RelationshipInfo\":[]}" - }, - { - "name": "PBI_ResultType", - "value": "Table" - } - ], - "columns": [ - { - "name": "Quantity", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "User" - } - ], - "changedProperties": [ - { - "property": "IsHidden" - } - ], - "dataType": "int64", - "formatString": "0", - "isAvailableInMdx": false, - "isHidden": true, - "lineageTag": "0aaf711e-9b42-4cfb-9ab6-bee80e843a12", - "sourceColumn": "Quantity", - "summarizeBy": "none" - }, - { - "name": "Order Number", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - } - ], - "dataType": "int64", - "formatString": "0", - "lineageTag": "e2e629f1-f1fb-4444-a627-b121d77fdc06", - "sourceColumn": "Order Number", - "summarizeBy": "none" - }, - { - "name": "Line Number", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - } - ], - "dataType": "int64", - "formatString": "0", - "lineageTag": "d4046972-90a5-46b2-badb-709e834b99af", - "sourceColumn": "Line Number", - "summarizeBy": "none" - }, - { - "name": "Order Date", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - }, - { - "name": "UnderlyingDateTimeDataType", - "value": "Date" - }, - { - "name": "BestPracticeAnalyzer_IgnoreRules", - "value": "{\"RuleIDs\":[\"HIDE_FOREIGN_KEYS\",\"RELATIONSHIP_COLUMNS_SHOULD_BE_OF_INTEGER_DATA_TYPE\"]}" - } - ], - "dataType": "dateTime", - "formatString": "Long Date", - "lineageTag": "d2d074d6-be1f-4dfd-b826-cd99fe83bc3a", - "sourceColumn": "Order Date", - "summarizeBy": "none" - }, - { - "name": "Delivery Date", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - }, - { - "name": "UnderlyingDateTimeDataType", - "value": "Date" - }, - { - "name": "BestPracticeAnalyzer_IgnoreRules", - "value": "{\"RuleIDs\":[\"HIDE_FOREIGN_KEYS\",\"RELATIONSHIP_COLUMNS_SHOULD_BE_OF_INTEGER_DATA_TYPE\"]}" - } - ], - "dataType": "dateTime", - "formatString": "Long Date", - "lineageTag": "1056fc0f-d1e5-4872-a39b-25603dba5cf5", - "sourceColumn": "Delivery Date", - "summarizeBy": "none" - }, - { - "name": "CustomerKey", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - } - ], - "changedProperties": [ - { - "property": "IsHidden" - } - ], - "dataType": "int64", - "formatString": "0", - "isAvailableInMdx": false, - "isHidden": true, - "lineageTag": "4de77f33-318d-4006-85db-580cb119fc6a", - "sourceColumn": "CustomerKey", - "summarizeBy": "none" - }, - { - "name": "StoreKey", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - } - ], - "changedProperties": [ - { - "property": "IsHidden" - } - ], - "dataType": "int64", - "formatString": "0", - "isAvailableInMdx": false, - "isHidden": true, - "lineageTag": "cf26d73c-10d7-40ac-83a3-c91c04e948d8", - "sourceColumn": "StoreKey", - "summarizeBy": "none" - }, - { - "name": "ProductKey", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - } - ], - "changedProperties": [ - { - "property": "IsHidden" - } - ], - "dataType": "int64", - "formatString": "0", - "isAvailableInMdx": false, - "isHidden": true, - "lineageTag": "595525b1-f5ca-442e-a226-0cc478b823c0", - "sourceColumn": "ProductKey", - "summarizeBy": "none" - }, - { - "name": "Net Price", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - }, - { - "name": "PBI_FormatHint", - "value": "{\"isGeneralNumber\":true}" - } - ], - "changedProperties": [ - { - "property": "DataType" - }, - { - "property": "IsHidden" - } - ], - "dataType": "decimal", - "isAvailableInMdx": false, - "isHidden": true, - "lineageTag": "d4df8046-8db3-4910-8759-33b904a0cac6", - "sourceColumn": "Net Price", - "summarizeBy": "sum" - }, - { - "name": "Unit Cost", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - }, - { - "name": "BestPracticeAnalyzer_IgnoreRules", - "value": "{\"RuleIDs\":[\"REMOVE_REDUNDANT_COLUMNS_IN_RELATED_TABLES\"]}" - }, - { - "name": "PBI_FormatHint", - "value": "{\"isGeneralNumber\":true}" - } - ], - "changedProperties": [ - { - "property": "IsHidden" - }, - { - "property": "DataType" - } - ], - "dataType": "decimal", - "isAvailableInMdx": false, - "isHidden": true, - "lineageTag": "03a43676-2ce4-4678-a4a7-0ee8f4e00b17", - "sourceColumn": "Unit Cost", - "summarizeBy": "sum" - }, - { - "name": "Currency Code", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - } - ], - "dataType": "string", - "lineageTag": "a75ba79f-22a3-4bfd-9b52-80e5d73247a9", - "sourceColumn": "Currency Code", - "summarizeBy": "none" - }, - { - "name": "Exchange Rate", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - }, - { - "name": "PBI_FormatHint", - "value": "{\"isGeneralNumber\":true}" - } - ], - "changedProperties": [ - { - "property": "DataType" - } - ], - "dataType": "decimal", - "lineageTag": "0b3f913f-1587-4280-a8bb-21ab7d661086", - "sourceColumn": "Exchange Rate", - "summarizeBy": "none" - }, - { - "name": "Environment", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - } - ], - "dataType": "string", - "lineageTag": "05fb6ae8-19b4-40c5-81b2-e40a6edd8692", - "sourceColumn": "Environment", - "summarizeBy": "none" - } - ], - "description": "Sales table for year over year analysis", - "lineageTag": "97143e5b-7736-4fcb-8042-26b92b1f5684", - "measures": [ - { - "name": "# Customers (with Sales)", - "expression": "DISTINCTCOUNT('Sales'[CustomerKey])", - "formatString": "#,##0", - "lineageTag": "4ec872f0-a42d-4aa9-bf6b-1ca7c4916336" - }, - { - "name": "# Products (with Sales)", - "changedProperties": [ - { - "property": "FormatString" - } - ], - "expression": "DISTINCTCOUNT('Sales'[ProductKey])", - "formatString": "#,##0", - "lineageTag": "b11c3386-30b8-4303-8ddb-ac0d2fa7660d" - }, - { - "name": "Sales Qty", - "expression": "sum('Sales'[Quantity])", - "formatString": "#,##0", - "lineageTag": "c2ff8d96-2f03-4005-84df-91458625b73b" - }, - { - "name": "Sales Amount", - "annotations": [ - { - "name": "PBI_FormatHint", - "value": "{\"isCustom\":true}" - } - ], - "changedProperties": [ - { - "property": "FormatString" - } - ], - "expression": "SUMX('Sales', 'Sales'[Quantity] * 'Sales'[Net Price])", - "formatString": "$ #,##0", - "lineageTag": "a8e95485-02a2-4525-b02a-b2418fbdbe4c" - }, - { - "name": "Sales Amount (Δ LY)", - "expression": "[Sales Amount] - [Sales Amount (LY)]", - "formatString": "$ #,##0", - "lineageTag": "d2724187-f1de-4a90-84bc-fc96aab3194b" - }, - { - "name": "Sales Amount (LY)", - "annotations": [ - { - "name": "PBI_FormatHint", - "value": "{\"currencyCulture\":\"en-US\"}" - } - ], - "changedProperties": [ - { - "property": "FormatString" - } - ], - "description": [ - "Sales Amount Last Year considering a full month", - "" - ], - "expression": "CALCULATE([Sales Amount], SAMEPERIODLASTYEAR('Calendar'[Date]))", - "formatString": "\\$#,0;(\\$#,0);\\$#,0", - "lineageTag": "3fa889ae-64b9-4a6e-b0e6-c81c90fd32bf" - }, - { - "name": "Sales Amount (YTD, LY)", - "expression": "CALCULATE([Sales Amount (YTD)], SAMEPERIODLASTYEAR('Calendar'[Date]))", - "formatString": "$ #,##0", - "lineageTag": "6868b958-3e8b-4e0e-ae86-8a88e4651834" - }, - { - "name": "Sales Amount (YTD)", - "expression": "TOTALYTD([Sales Amount],'Calendar'[Date])", - "formatString": "$ #,##0", - "lineageTag": "aafaacd7-ff25-4a69-b8e7-f29fb02c5351" - }, - { - "name": "Sales Amount Avg per Day", - "expression": "AVERAGEX(VALUES('Calendar'[Date]), [Sales Amount])", - "formatString": "$ #,##0", - "lineageTag": "65842ce7-7176-4106-868e-2e83aaa62b4c" - }, - { - "name": "Sales Amount (% Δ LY)", - "expression": [ - "var ly =[Sales Amount (LY)]", - "return", - "DIVIDE(", - " [Sales Amount]- ly,", - " ly", - " )" - ], - "formatString": "#,##0.00 %", - "lineageTag": "f1a3a032-8cea-4b06-b85a-f6f4e03e1d9f" - }, - { - "name": "Margin", - "annotations": [ - { - "name": "PBI_FormatHint", - "value": "{\"isCustom\":true}" - } - ], - "changedProperties": [ - { - "property": "FormatString" - } - ], - "expression": [ - "SUMX ( ", - " Sales, ", - " Sales[Quantity] ", - " * ( Sales[Net Price] - Sales[Unit Cost] )", - ")" - ], - "formatString": "$ #,##0", - "kpi": { - "statusExpression": [ - "VAR MarginPercentage = [Margin %]", - "VAR MarginTolerance = 0.02", - "VAR MarginGoal = 0.3", - "RETURN", - " IF (", - " NOT ISBLANK ( MarginPercentage ),", - " SWITCH (", - " TRUE,", - " MarginPercentage < MarginGoal - MarginTolerance, -1, -- Negative", - " MarginPercentage > MarginGoal + MarginTolerance, 1, -- Positive", - " 0", - " )", - " )" - ], - "targetExpression": [ - "[Margin % Overall]", - "" - ], - "trendExpression": [ - "-- DAX code for Trend Expression", - "VAR MarginPerc = [Margin %]", - "VAR PrevMarginPerc =", - " CALCULATE (", - " [Margin %],", - " PREVIOUSYEAR( 'Calendar'[Date] )", - " )", - "RETURN", - " IF (", - " NOT ISBLANK ( MarginPerc ) && NOT ISBLANK ( PrevMarginPerc ),", - " SWITCH (", - " TRUE,", - " MarginPerc > PrevMarginPerc, 1, -- Positive", - " MarginPerc < PrevMarginPerc, -1, -- Negative", - " 0", - " )", - " )" - ] - }, - "lineageTag": "d22a262b-b776-4ccd-b9bd-7ef9c90eba51" - }, - { - "name": "Margin (ly)", - "expression": [ - "CALCULATE([Margin], SAMEPERIODLASTYEAR('Calendar'[Date]))", - "" - ], - "formatString": "$ #,##0", - "lineageTag": "bdf081eb-1952-439f-96b1-a27e1a13f1a3" - }, - { - "name": "# Sales", - "changedProperties": [ - { - "property": "FormatString" - } - ], - "expression": "COUNTROWS('Sales')", - "formatString": "#,##0", - "lineageTag": "67791bd1-5f33-4a29-a9ac-be0ad2453fcf" - }, - { - "name": "Sales Qty by Delivery Date", - "expression": [ - "CALCULATE([Sales Qty], USERELATIONSHIP('Sales'[Delivery Date],'Calendar'[Date]))", - "", - "" - ], - "formatString": "#,##0", - "lineageTag": "c85c43d0-a2e1-4d90-a8a0-4009499b6eea" - }, - { - "name": "Sales Amount (12M average)", - "annotations": [ - { - "name": "PBI_FormatHint", - "value": "{\"isCustom\":true}" - } - ], - "description": [ - "12 Month moving average sales calculation", - "" - ], - "expression": [ - "", - "VAR v_selDate =", - " MAX ( 'Calendar'[Date] )", - "VAR v_period =", - " DATESINPERIOD ( 'Calendar'[Date], v_selDate, -12, MONTH )", - "VAR v_result =", - " CALCULATE ( AVERAGEX ( VALUES ( 'Calendar'[Date] ), [Sales Amount] ), v_period )", - "VAR v_firstDate =", - " MINX ( v_period, 'Calendar'[Date] )", - "VAR v_lastDateSales =", - " MAX ( Sales[Order Date] )", - "RETURN", - " IF ( v_firstDate <= v_lastDateSales, v_result )" - ], - "formatString": "$ #,##0", - "lineageTag": "7fd60f7e-287e-46c3-a745-24c4507bc77b" - }, - { - "name": "Sales Amount (6M average)", - "expression": [ - "", - "VAR v_selDate =", - " MAX ( 'Calendar'[Date] )", - "VAR v_period =", - " DATESINPERIOD ( 'Calendar'[Date], v_selDate, -6, MONTH )", - "VAR v_result =", - " CALCULATE ( AVERAGEX ( VALUES ( 'Calendar'[Date] ), [Sales Amount] ), v_period )", - "VAR v_firstDate =", - " MINX ( v_period, 'Calendar'[Date] )", - "VAR v_lastDateSales =", - " MAX ( Sales[Order Date] )", - "RETURN", - " IF ( v_firstDate <= v_lastDateSales, v_result )" - ], - "formatString": "$ #,##0", - "lineageTag": "9a48bea0-e5fb-40fa-9e81-f61288e31a02" - }, - { - "name": "Margin %", - "expression": "DIVIDE ( [Margin], [Sales Amount] )", - "formatString": "#,##0.00 %", - "lineageTag": "91a7a901-c646-41fe-a1ed-6e82b421d140" - }, - { - "name": "Cost", - "expression": "SUMX ( Sales, Sales[Quantity] * Sales[Unit Cost] )", - "formatString": "$ #,##0", - "lineageTag": "3e5cb67e-a411-476f-ad34-7ec10dfd084d" - }, - { - "name": "Margin % Overall", - "expression": [ - "ROUND ( CALCULATE( [Margin %], REMOVEFILTERS () ), 2 )", - "" - ], - "formatString": "#,##0.00 %", - "lineageTag": "b2155351-e104-4bae-941b-3e651804cee5" - } - ], - "partitions": [ - { - "name": "Sales-ddb4c40b-46fd-49ea-9a19-16e7e640a21a", - "mode": "import", - "source": { - "expression": [ - "let", - " Source = #\"RAW-SalesDateAdjustedAndSalesRandomized\",", - " #\"Changed Type\" = Table.TransformColumnTypes(Source,{{\"Order Number\", Int64.Type}, {\"Line Number\", Int64.Type}, {\"Order Date\", type date}, {\"Delivery Date\", type date}, {\"CustomerKey\", Int64.Type}, {\"StoreKey\", Int64.Type}, {\"ProductKey\", Int64.Type}, {\"Quantity\", Int64.Type}, {\"Unit Price\", type number}, {\"Net Price\", type number}, {\"Unit Cost\", type number}, {\"Currency Code\", type text}, {\"Exchange Rate\", type number}}),", - " #\"Removed Columns\" = Table.RemoveColumns(#\"Changed Type\",{\"Unit Price\"}),", - " ", - " #\"Changed Type1\" = Table.TransformColumnTypes(#\"Removed Columns\",{{\"Delivery Date\", type datetime}, {\"Order Date\", type datetime}}),", - " #\"Filtered Rows\" = Table.SelectRows(#\"Changed Type1\", each [Order Date] >= RangeStart and [Order Date] <= RangeEnd),", - " #\"Changed Type2\" = Table.TransformColumnTypes(#\"Filtered Rows\",{{\"Delivery Date\", type date}, {\"Order Date\", type date}}),", - " #\"Added Custom\" = Table.AddColumn(#\"Changed Type2\", \"Environment\", each Environment),", - " #\"Changed Type3\" = Table.TransformColumnTypes(#\"Added Custom\",{{\"Environment\", type text}})", - "in", - " #\"Changed Type3\"" - ], - "type": "m" - } - } - ] - }, - { - "name": "Product", - "annotations": [ - { - "name": "PBI_ResultType", - "value": "Table" - } - ], - "columns": [ - { - "name": "Product", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - } - ], - "dataType": "string", - "isDefaultLabel": true, - "lineageTag": "da435585-1f9a-44bd-ba2c-34c98f298cfc", - "sourceColumn": "Product", - "summarizeBy": "none" - }, - { - "name": "ProductKey", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - } - ], - "dataType": "int64", - "formatString": "0", - "isAvailableInMdx": false, - "isKey": true, - "lineageTag": "4184d53e-cd2d-4cbe-b8cb-04c72a750bc4", - "sourceColumn": "ProductKey", - "summarizeBy": "none" - }, - { - "name": "Product Code", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - } - ], - "dataType": "string", - "lineageTag": "e9d204ad-76d8-4db9-9d1a-b9c07a4b50b2", - "sourceColumn": "Product Code", - "summarizeBy": "none" - }, - { - "name": "Manufacturer", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - } - ], - "dataType": "string", - "lineageTag": "59e45f50-f68d-44c3-becd-70ccd5a7eb7d", - "sourceColumn": "Manufacturer", - "summarizeBy": "none" - }, - { - "name": "Brand", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - } - ], - "dataType": "string", - "lineageTag": "a71b235d-8f7e-4678-85a3-96a78d64bf87", - "sourceColumn": "Brand", - "summarizeBy": "none" - }, - { - "name": "Color", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - } - ], - "dataType": "string", - "lineageTag": "7054b4d0-6d93-4c96-be74-800d02d96e43", - "sourceColumn": "Color", - "summarizeBy": "none" - }, - { - "name": "Weight Unit Measure", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - } - ], - "dataType": "string", - "lineageTag": "78fcf7c4-2b5d-45b0-abf9-6ee3b3aa255b", - "sourceColumn": "Weight Unit Measure", - "summarizeBy": "none" - }, - { - "name": "Weight", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - }, - { - "name": "PBI_FormatHint", - "value": "{\"isGeneralNumber\":true}" - } - ], - "changedProperties": [ - { - "property": "DataType" - } - ], - "dataType": "decimal", - "lineageTag": "a6299b36-bd05-4b41-8493-e45359af237b", - "sourceColumn": "Weight", - "summarizeBy": "none" - }, - { - "name": "Unit Cost", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - }, - { - "name": "PBI_FormatHint", - "value": "{\"isGeneralNumber\":true}" - } - ], - "changedProperties": [ - { - "property": "DataType" - } - ], - "dataType": "decimal", - "lineageTag": "f89fa3e3-061d-4269-8cd3-aa6ce2a464d2", - "sourceColumn": "Unit Cost", - "summarizeBy": "none" - }, - { - "name": "Unit Price", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - }, - { - "name": "PBI_FormatHint", - "value": "{\"isGeneralNumber\":true}" - } - ], - "changedProperties": [ - { - "property": "DataType" - } - ], - "dataType": "decimal", - "lineageTag": "ef300027-e4eb-4c7d-9770-ab8f6dab6b15", - "sourceColumn": "Unit Price", - "summarizeBy": "none" - }, - { - "name": "Subcategory Code", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - } - ], - "dataType": "string", - "lineageTag": "7cd08eb9-2cad-4263-ae88-8c5121a68b7e", - "sourceColumn": "Subcategory Code", - "summarizeBy": "none" - }, - { - "name": "Subcategory", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - } - ], - "dataType": "string", - "lineageTag": "0a208c62-4bdd-4873-af18-ebc286c5b3bb", - "sourceColumn": "Subcategory", - "summarizeBy": "none" - }, - { - "name": "Category Code", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - } - ], - "dataType": "string", - "lineageTag": "c0fc218a-5a06-4757-9172-2d303a67f3ff", - "sourceColumn": "Category Code", - "summarizeBy": "none" - }, - { - "name": "Category", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - } - ], - "dataType": "string", - "lineageTag": "0f4b99cc-fdb6-4f04-b7d9-bbdcf7b2c601", - "sourceColumn": "Category", - "summarizeBy": "none" - } - ], - "description": "Product Catalog", - "hierarchies": [ - { - "name": "Product Hierarchy", - "levels": [ - { - "name": "Category", - "column": "Category", - "lineageTag": "9ff3052d-e0de-44e8-85c3-85b8cc978936", - "ordinal": 0 - }, - { - "name": "Subcategory", - "column": "Subcategory", - "lineageTag": "647503e7-1d2b-4e0a-bc36-1ce6bc3d81ca", - "ordinal": 1 - }, - { - "name": "Product", - "column": "Product", - "lineageTag": "85ba527d-a9e2-4f3d-85d9-447600445bc3", - "ordinal": 2 - } - ], - "lineageTag": "89345cc9-e735-4d62-8caf-e494a6314e93" - } - ], - "lineageTag": "e9374b9a-faee-4f9e-b2e7-d9aafb9d6a91", - "measures": [ - { - "name": "# Products", - "expression": "COUNTROWS('Product')", - "formatString": "#,##0", - "lineageTag": "1f8f1a2a-06b6-4989-8af7-212719cf3617" - } - ], - "partitions": [ - { - "name": "Product-171f48b3-e0ea-4ea3-b9a0-c8c673eb0648", - "mode": "import", - "source": { - "expression": [ - "let", - " Source = #\"RAW-Product\",", - " #\"Renamed Columns\" = Table.RenameColumns(Source,{{\"Product Name\", \"Product\"}}),", - " #\"Changed Type\" = Table.TransformColumnTypes(#\"Renamed Columns\",{{\"ProductKey\", Int64.Type}, {\"Product Code\", type text}, {\"Product\", type text}, {\"Manufacturer\", type text}, {\"Brand\", type text}, {\"Color\", type text}, {\"Weight Unit Measure\", type text}, {\"Weight\", type number}, {\"Unit Cost\", type number}, {\"Unit Price\", type number}, {\"Subcategory Code\", type text}, {\"Subcategory\", type text}, {\"Category Code\", type text}, {\"Category\", type text}})", - "in", - " #\"Changed Type\"" - ], - "type": "m" - } - } - ] - }, - { - "name": "Customer", - "annotations": [ - { - "name": "PBI_ResultType", - "value": "Table" - } - ], - "columns": [ - { - "name": "CustomerKey", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - } - ], - "dataType": "int64", - "formatString": "0", - "isAvailableInMdx": false, - "isKey": true, - "lineageTag": "901662ed-f0ae-41f6-96b3-4aa68bad1c7a", - "sourceColumn": "CustomerKey", - "summarizeBy": "none" - }, - { - "name": "Gender", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - } - ], - "dataType": "string", - "lineageTag": "7e0ce5eb-3e63-4870-b30a-032f5186375d", - "sourceColumn": "Gender", - "summarizeBy": "none" - }, - { - "name": "Customer", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - } - ], - "dataType": "string", - "isDefaultLabel": true, - "lineageTag": "8845f8b5-b069-41a0-8af8-12e402b113e9", - "sourceColumn": "Customer", - "summarizeBy": "none" - }, - { - "name": "Address", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - } - ], - "dataType": "string", - "lineageTag": "adbeb074-7c67-40ee-be2d-d46775ce5e17", - "sourceColumn": "Address", - "summarizeBy": "none" - }, - { - "name": "City", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - } - ], - "dataCategory": "City", - "dataType": "string", - "lineageTag": "a791eb8c-7fab-418d-844a-f8b094453037", - "sourceColumn": "City", - "summarizeBy": "none" - }, - { - "name": "State Code", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - } - ], - "dataCategory": "StateOrProvince", - "dataType": "string", - "lineageTag": "29de3d16-fffa-4f61-8c39-c1a2a36d737a", - "sourceColumn": "State Code", - "summarizeBy": "none" - }, - { - "name": "State", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - } - ], - "dataCategory": "StateOrProvince", - "dataType": "string", - "lineageTag": "923b4cc1-6137-4bb4-bcb2-3a90c5f76c5e", - "sourceColumn": "State", - "summarizeBy": "none" - }, - { - "name": "Zip Code", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - } - ], - "dataCategory": "PostalCode", - "dataType": "string", - "lineageTag": "439ce0ee-437e-4770-90c4-c4d53ebfb35b", - "sourceColumn": "Zip Code", - "summarizeBy": "none" - }, - { - "name": "Country Code", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - } - ], - "dataCategory": "Country", - "dataType": "string", - "lineageTag": "f3eea279-4255-446b-9f83-f40f66abf6d1", - "sourceColumn": "Country Code", - "summarizeBy": "none" - }, - { - "name": "Country", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - } - ], - "dataCategory": "Country", - "dataType": "string", - "lineageTag": "99af44ae-86e9-4b75-98a3-e4b6e53fe806", - "sourceColumn": "Country", - "summarizeBy": "none" - }, - { - "name": "Continent", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - } - ], - "dataCategory": "Continent", - "dataType": "string", - "lineageTag": "53b840d2-962c-44c4-8733-6cc003fb9a83", - "sourceColumn": "Continent", - "summarizeBy": "none" - }, - { - "name": "Birthday", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - } - ], - "dataType": "dateTime", - "formatString": "General Date", - "lineageTag": "afa5e191-c690-45c1-a725-41c9d3ca9434", - "sourceColumn": "Birthday", - "summarizeBy": "none" - }, - { - "name": "Age", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - } - ], - "dataType": "int64", - "formatString": "0", - "lineageTag": "5c487309-c061-45ed-aa74-aba4d20bde3b", - "sourceColumn": "Age", - "summarizeBy": "none" - } - ], - "description": "Customer data", - "lineageTag": "04227f5a-0aeb-4448-b5c5-dbf3e276da85", - "measures": [ - { - "name": "# Customers", - "expression": "COUNTROWS('Customer')", - "formatString": "#,##0", - "lineageTag": "a8dc565a-aa9b-40dc-902c-1ba2596b0977" - } - ], - "partitions": [ - { - "name": "Customer-3757b886-e26c-4cec-a550-cdeea37b94d4", - "mode": "import", - "source": { - "expression": [ - "let", - " Source = #\"RAW-Customer\",", - " #\"Renamed Columns\" = Table.RenameColumns(Source,{{\"Name\", \"Customer\"}}),", - " #\"Changed Type\" = Table.TransformColumnTypes(#\"Renamed Columns\",{{\"CustomerKey\", Int64.Type}, {\"Gender\", type text}, {\"Customer\", type text}, {\"Address\", type text}, {\"City\", type text}, {\"State Code\", type text}, {\"State\", type text}, {\"Zip Code\", type text}, {\"Country Code\", type text}, {\"Country\", type text}, {\"Continent\", type text}, {\"Birthday\", type datetime}, {\"Age\", Int64.Type}})", - "in", - " #\"Changed Type\"" - ], - "type": "m" - } - } - ] - }, - { - "name": "Smart Calcs", - "calculationGroup": { - "calculationItems": [ - { - "name": "Normalize", - "expression": [ - "VAR DetailValue = SELECTEDMEASURE()", - "", - "return if (DetailValue, ", - "", - " VAR MinOfGroup = MINX(ALLSELECTED('Calendar'), SELECTEDMEASURE())", - " VAR MaxOfGroup = MAXX(ALLSELECTED('Calendar'), SELECTEDMEASURE())", - "", - " RETURN DIVIDE(DetailValue - MinOfGroup, MaxOfGroup - MinOfGroup)", - ")" - ], - "formatStringDefinition": { - "expression": "\"0.0\"" - } - }, - { - "name": "Randomize", - "expression": "IFERROR(SELECTEDMEASURE() * RAND(), SELECTEDMEASURE())" - }, - { - "name": "Label - ▲ LY", - "expression": [ - "var vValue = SELECTEDMEASURE()", - "var vValueLY = CALCULATE(SELECTEDMEASURE(), SAMEPERIODLASTYEAR('Calendar'[Date]))", - "var vGrowth = DIVIDE(vValue - vValueLY, vValueLY)", - "var vFormat = SELECTEDMEASUREFORMATSTRING()", - "", - "return", - " FORMAT(vValue, vFormat)", - " & IF (ISBLANK(vGrowth)", - " , BLANK()", - " , \" | \" ", - " & IF (vGrowth >= 0, \"▲\" , \"▼\") & FORMAT(vGrowth, \"0%\")", - " )", - "" - ], - "formatStringDefinition": { - "expression": "SELECTEDMEASUREFORMATSTRING()" - } - }, - { - "name": "Dynamic Measure - Apply Format", - "expression": "SELECTEDMEASURE()", - "formatStringDefinition": { - "expression": [ - "", - "IF (", - " // Only do this for the 'Dynamic Measure' Measures", - " ISSELECTEDMEASURE ( [Value], [Value (ly)], [Value (ytd)], [Value Avg per Month] ),", - " VAR measureCode =", - " SELECTEDVALUE ( 'Dynamic Measure'[Code] )", - " VAR measureFormat =", - " IF (", - " measureCode <> BLANK (),", - " LOOKUPVALUE ( 'Dynamic Measure'[Format], 'Dynamic Measure'[Code], measureCode )", - " )", - " RETURN", - " IF ( measureFormat <> BLANK (), measureFormat, SELECTEDMEASUREFORMATSTRING () )", - " ", - " , SELECTEDMEASUREFORMATSTRING ()", - ")" - ] - } - }, - { - "name": "Label - ▲ LM", - "expression": [ - "var vValue = SELECTEDMEASURE()", - "var vValueLM = CALCULATE(SELECTEDMEASURE(), PREVIOUSMONTH('Calendar'[Date]))", - "var vGrowth = DIVIDE(vValue - vValueLM, vValueLM)", - "var vFormat = SELECTEDMEASUREFORMATSTRING()", - "", - "return", - " FORMAT(vValue, vFormat)", - " & IF (ISBLANK(vGrowth)", - " , BLANK()", - " , \" | \" ", - " & IF (vGrowth >= 0, \"▲\" , \"▼\") & FORMAT(vGrowth, \"0%\")", - " )", - "" - ], - "formatStringDefinition": { - "expression": "SELECTEDMEASUREFORMATSTRING()" - } - } - ] - }, - "columns": [ - { - "name": "Smart Calc", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - } - ], - "dataType": "string", - "lineageTag": "005e8b9e-378a-421a-905c-f21dad946ceb", - "sortByColumn": "Ordinal", - "sourceColumn": "Name", - "summarizeBy": "none" - }, - { - "name": "Ordinal", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "User" - } - ], - "dataType": "int64", - "isHidden": true, - "lineageTag": "2532b358-06e7-449d-a944-810f51fff7a5", - "sourceColumn": "Ordinal", - "summarizeBy": "none" - } - ], - "lineageTag": "fa79b28a-6a9c-43b1-a775-099dabcd4428", - "partitions": [ - { - "name": "Partition", - "mode": "import", - "source": { - "type": "calculationGroup" - } - } - ] - }, - { - "name": "Dynamic Measure", - "annotations": [ - { - "name": "PBI_Id", - "value": "4d2f308d1af14ea3accf3c458621a9d7" - }, - { - "name": "BestPracticeAnalyzer_IgnoreRules", - "value": "{\"RuleIDs\":[\"REDUCE_USAGE_OF_CALCULATED_TABLES\",\"ENSURE_TABLES_HAVE_RELATIONSHIPS\"]}" - } - ], - "columns": [ - { - "name": "Code", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "User" - } - ], - "dataType": "int64", - "formatString": "0", - "isDataTypeInferred": true, - "isNameInferred": true, - "lineageTag": "a5db8d3b-70af-483d-bcab-5c0fcc7478c2", - "sourceColumn": "[Code]", - "summarizeBy": "none", - "type": "calculatedTableColumn" - }, - { - "name": "Order", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "User" - } - ], - "dataType": "int64", - "formatString": "0", - "isDataTypeInferred": true, - "isNameInferred": true, - "lineageTag": "c7824b9c-021c-4bff-b363-c04b3cb2c681", - "sourceColumn": "[Order]", - "summarizeBy": "none", - "type": "calculatedTableColumn" - }, - { - "name": "Measure", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - } - ], - "dataType": "string", - "isDataTypeInferred": true, - "isNameInferred": true, - "lineageTag": "d8e3ae78-fda2-49e6-b8cb-b6257e927261", - "sourceColumn": "[Measure]", - "summarizeBy": "none", - "type": "calculatedTableColumn" - }, - { - "name": "Area", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - } - ], - "dataType": "string", - "isDataTypeInferred": true, - "isNameInferred": true, - "lineageTag": "364ae39d-73bd-4527-91e4-0e649a23d8a7", - "sourceColumn": "[Area]", - "summarizeBy": "none", - "type": "calculatedTableColumn" - }, - { - "name": "Format", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - } - ], - "dataType": "string", - "isDataTypeInferred": true, - "isNameInferred": true, - "lineageTag": "ec17695c-dc59-4674-bdaa-bbdeb4131593", - "sourceColumn": "[Format]", - "summarizeBy": "none", - "type": "calculatedTableColumn" - } - ], - "description": [ - "Dynamic Measure table", - "Useful to explore measure \"as a dimension\"" - ], - "lineageTag": "208f785d-03c1-41ca-b33a-c56c36916caa", - "measures": [ - { - "name": "Value", - "annotations": [ - { - "name": "BestPracticeAnalyzer_IgnoreRules", - "value": "{\"RuleIDs\":[\"PROVIDE_FORMAT_STRING_FOR_MEASURES\",\"INTEGER_FORMATTING\"]}" - }, - { - "name": "PBI_FormatHint", - "value": "{\"isGeneralNumber\":true}" - } - ], - "expression": [ - "", - "IF (", - " HASONEVALUE ( 'Dynamic Measure'[Code] ),", - " var measureCode = SELECTEDVALUE('Dynamic Measure'[Code])", - " return SWITCH (", - " measureCode", - " ,1, [Sales Amount]", - " ,2, [Sales Amount (% Δ LY)]", - " ,3, [# Customers (with Sales)] ", - " ,4, [Sales Qty]", - " ,5, [Margin]", - " ,BLANK ()", - " )", - ")" - ], - "lineageTag": "1f748dd6-758e-4445-bf6c-dab17d61d2ee" - }, - { - "name": "Value (ly)", - "annotations": [ - { - "name": "BestPracticeAnalyzer_IgnoreRules", - "value": "{\"RuleIDs\":[\"PROVIDE_FORMAT_STRING_FOR_MEASURES\",\"INTEGER_FORMATTING\"]}" - }, - { - "name": "PBI_FormatHint", - "value": "{\"isGeneralNumber\":true}" - } - ], - "expression": "CALCULATE ( [Value], SAMEPERIODLASTYEAR('Calendar'[Date]) )", - "lineageTag": "59781612-6890-4665-a1b2-aa25324fe896" - }, - { - "name": "Value (ytd)", - "annotations": [ - { - "name": "BestPracticeAnalyzer_IgnoreRules", - "value": "{\"RuleIDs\":[\"PROVIDE_FORMAT_STRING_FOR_MEASURES\",\"INTEGER_FORMATTING\"]}" - } - ], - "expression": "CALCULATE([Value], DATESYTD('Calendar'[Date]))", - "lineageTag": "b01909c9-718c-48d2-98af-482b39bf2272" - }, - { - "name": "Value Avg per Month", - "annotations": [ - { - "name": "BestPracticeAnalyzer_IgnoreRules", - "value": "{\"RuleIDs\":[\"PROVIDE_FORMAT_STRING_FOR_MEASURES\",\"INTEGER_FORMATTING\"]}" - }, - { - "name": "PBI_FormatHint", - "value": "{\"isGeneralNumber\":true}" - } - ], - "expression": [ - "", - " AVERAGEX(VALUES('Calendar'[Month (Year)]), [Value])" - ], - "lineageTag": "ba87b819-2147-443b-aaeb-8ae1729c6550" - }, - { - "name": "Value Daily Max", - "annotations": [ - { - "name": "BestPracticeAnalyzer_IgnoreRules", - "value": "{\"RuleIDs\":[\"PROVIDE_FORMAT_STRING_FOR_MEASURES\",\"INTEGER_FORMATTING\"]}" - }, - { - "name": "PBI_FormatHint", - "value": "{\"isGeneralNumber\":true}" - } - ], - "expression": "MAXX(VALUES('Calendar'[Date]), [Value])", - "lineageTag": "d8fbae5f-a0c0-4b2e-85e9-1e045d7510e0" - }, - { - "name": "Value % (Δ ly)", - "changedProperties": [ - { - "property": "FormatString" - } - ], - "expression": [ - "", - "var ly =[Value (ly)]", - "return", - "DIVIDE(", - " [Value]- ly,", - " ly", - " )" - ], - "formatString": "0.00%;-0.00%;0.00%", - "lineageTag": "897fe824-eb94-4ec9-996e-26238f4c2b23" - }, - { - "name": "Value Normalized (by date)", - "annotations": [ - { - "name": "BestPracticeAnalyzer_IgnoreRules", - "value": "{\"RuleIDs\":[\"PROVIDE_FORMAT_STRING_FOR_MEASURES\",\"INTEGER_FORMATTING\"]}" - }, - { - "name": "PBI_FormatHint", - "value": "{\"isGeneralNumber\":true}" - } - ], - "expression": [ - "", - "VAR DetailValue = [Value]", - "return if (DetailValue, ", - "", - " //VAR MinOfGroup = MINX(ALLSELECTED('Calendar'[Month (Year)], 'Calendar'[MonthYearId]), [Value])", - " //VAR MaxOfGroup = MAXX(ALLSELECTED('Calendar'[Month (Year)], 'Calendar'[MonthYearId]), [Value])", - " VAR MinOfGroup = MINX(ALLSELECTED('Calendar'), [Value])", - " VAR MaxOfGroup = MAXX(ALLSELECTED('Calendar'), [Value])", - " RETURN DIVIDE(DetailValue - MinOfGroup, MaxOfGroup - MinOfGroup)", - ")" - ], - "lineageTag": "15549406-fb29-46e8-9094-25add2c74995" - } - ], - "partitions": [ - { - "name": "Dynamic Measure-e40351ed-a523-4ff4-9185-7834b3fbb8e8", - "mode": "import", - "source": { - "expression": [ - "", - "DATATABLE (", - " \"Code\", INTEGER,", - " \"Order\", INTEGER,", - " \"Measure\", STRING,", - " \"Area\", STRING,", - " \"Format\", STRING,", - " { ", - " { 1, 1, \"Sales Amount\", \"Sales\", \"\\$#,0.00;(\\$#,0.00);\\$#,0.00\" },", - " { 2, 2, \"Sales Growth vs LY\", \"Sales\", \"0.000%\"},", - " { 3, 4, \"# Customers\", \"Marketing\", \"#,#\" }, ", - " { 4, 2, \"Sales Qty\", \"Sales\", \"#,#\" },", - " { 5, 2, \"Sales Margin\", \"Sales\", \"\\$#,0.00;(\\$#,0.00);\\$#,0.00\" }", - " } ", - ")", - "" - ], - "type": "calculated" - } - } - ] - }, - { - "name": "Store", - "annotations": [ - { - "name": "PBI_ResultType", - "value": "Table" - } - ], - "columns": [ - { - "name": "StoreKey", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - } - ], - "dataType": "int64", - "formatString": "0", - "isAvailableInMdx": false, - "isKey": true, - "lineageTag": "b63bc7b8-266a-4424-9676-c3e68501b2ec", - "sourceColumn": "StoreKey", - "summarizeBy": "none" - }, - { - "name": "Store Code", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - } - ], - "dataType": "string", - "lineageTag": "307e3348-2132-4db3-b76a-771ac4561ef5", - "sourceColumn": "Store Code", - "summarizeBy": "none" - }, - { - "name": "Country", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - } - ], - "dataCategory": "Country", - "dataType": "string", - "lineageTag": "7564fe29-ad01-43e0-ba93-2e1634e1a9b3", - "sourceColumn": "Country", - "summarizeBy": "none" - }, - { - "name": "State", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - } - ], - "dataType": "string", - "lineageTag": "f471c9c0-1924-46e4-99d8-59ccf0f64cba", - "sourceColumn": "State", - "summarizeBy": "none" - }, - { - "name": "Store", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - } - ], - "dataType": "string", - "isDefaultLabel": true, - "lineageTag": "7bee915f-7eb7-4dbc-a16f-e796f410b3f5", - "sourceColumn": "Store", - "summarizeBy": "none" - }, - { - "name": "Square Meters", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - } - ], - "dataType": "int64", - "formatString": "0", - "lineageTag": "1596588a-acc4-41c4-a692-310fd28994bb", - "sourceColumn": "Square Meters", - "summarizeBy": "none" - }, - { - "name": "Open Date", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - }, - { - "name": "UnderlyingDateTimeDataType", - "value": "Date" - } - ], - "dataType": "dateTime", - "formatString": "Long Date", - "lineageTag": "66cf5168-4add-4a05-b2c8-0380a85b0539", - "sourceColumn": "Open Date", - "summarizeBy": "none" - }, - { - "name": "Close Date", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - }, - { - "name": "UnderlyingDateTimeDataType", - "value": "Date" - } - ], - "dataType": "dateTime", - "formatString": "Long Date", - "lineageTag": "e627ea17-3b62-46f2-a2f4-549dc98beb06", - "sourceColumn": "Close Date", - "summarizeBy": "none" - }, - { - "name": "Status", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - } - ], - "dataType": "string", - "lineageTag": "93122991-3d6a-413c-ad26-3610fa90013b", - "sourceColumn": "Status", - "summarizeBy": "none" - } - ], - "description": "Store metadata", - "lineageTag": "54d5f884-12db-4e03-a120-08cd725db2c4", - "measures": [ - { - "name": "# Stores", - "changedProperties": [ - { - "property": "FormatString" - } - ], - "expression": "COUNTROWS('Store')", - "formatString": "#,##0", - "lineageTag": "868df9c8-f579-47d1-a776-3d29121df7c7" - } - ], - "partitions": [ - { - "name": "Store-c0e5ba98-f95a-4712-91ec-71c7dc35e177", - "mode": "import", - "source": { - "expression": [ - "let", - " Source = #\"RAW-Store\",", - " #\"Renamed Columns\" = Table.RenameColumns(Source,{{\"Name\", \"Store\"}}),", - " #\"Changed Type\" = Table.TransformColumnTypes(#\"Renamed Columns\",{{\"StoreKey\", Int64.Type}, {\"Store Code\", type text}, {\"Country\", type text}, {\"State\", type text}, {\"Store\", type text}, {\"Square Meters\", Int64.Type}, {\"Open Date\", type date}, {\"Close Date\", type date}, {\"Status\", type text}})", - "in", - " #\"Changed Type\"" - ], - "type": "m" - } - } - ] - }, - { - "name": "About", - "annotations": [ - { - "name": "PBI_ResultType", - "value": "Table" - } - ], - "columns": [ - { - "name": "Key", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - } - ], - "dataType": "string", - "lineageTag": "64fdcf75-33e0-4134-a5b8-677f8fefa7ed", - "sourceColumn": "Key", - "summarizeBy": "none" - }, - { - "name": "Value", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - } - ], - "dataType": "string", - "lineageTag": "41af608f-f46f-4878-be03-184d0cad44cf", - "sourceColumn": "Value", - "summarizeBy": "none" - }, - { - "name": "Order", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "User" - } - ], - "dataType": "int64", - "formatString": "0", - "lineageTag": "93bae8d8-9794-480e-8753-d51693dfa9ad", - "sourceColumn": "Order", - "summarizeBy": "none" - } - ], - "description": [ - "Table with information about the model.", - "Key/Value representation, with properties like: last refresh; model creator;..." - ], - "lineageTag": "68907830-e8ac-4c12-98d9-f70711413080", - "partitions": [ - { - "name": "About-77c21240-7751-4575-bf40-8c068bfd01cd", - "mode": "import", - "source": { - "expression": [ - "let", - "", - "    Source = Table.FromRows(Json.Document(Binary.Decompress(Binary.FromText(\"NVA7bmMxDLzK4NXGQ3IHp9stk8ZwwUi0Q0ASFYk0ktsvlZftxCE1v8tlO/ODi3bOeP/eTttfSUOn3my7ni7bG48p2gJ/3p9+kDPPNKTbgf7RwRXSp1dkLTowxUCV7YSkbXIyNh+gLF1mknYHF4nlDL2sYPFZNcO49vgsLUmW7M3ghkLvQQ+2g5pR6d4IVOTTacergZvU4EaV9XjESPWET5eJptOGZ/AXjyRGyzC8FKpJD+Z1JFOW0g+l9DgGUxiv4UmPACFlO86LktwYMjycHFmlYXAf/MEt84jgATy0eA85DjuRFDwnI0kp/xuKQI6b34UMbRlCpxGDjx0vX4m7sa8aowNNiTjFXfIumWz9iBR9qGRuq8XVVIgmL51WbujtJkkImSePta1alg1aBUnUMX979bpv1+s/\", BinaryEncoding.Base64), Compression.Deflate)), let _t = ((type nullable text) meta [Serialized.Text = true]) in type table [Key = _t, Value = _t]),", - "", - "    DynamicTable = Table.FromRows({{\"Last Refresh\", DateTime.ToText(DateTime.LocalNow(), \"yyyy-MM-dd HH:mm:ss\")}}, {\"Key\", \"Value\"}),", - "", - "    FinalTable = Table.Combine({Source, DynamicTable}),", - "", - "    #\"Added Index\" = Table.AddIndexColumn(FinalTable, \"Order\", 1, 1),", - "", - "    #\"Changed Type\" = Table.TransformColumnTypes(#\"Added Index\",{{\"Key\", type text},  {\"Value\", type text},{\"Order\", Int64.Type}}),", - "", - "    #\"Reordered Columns\" = Table.ReorderColumns(#\"Changed Type\",{\"Key\", \"Value\", \"Order\"})", - "", - "in", - "", - "    #\"Reordered Columns\"" - ], - "type": "m" - } - } - ] - }, - { - "name": "Parameter - Dimension", - "annotations": [ - { - "name": "PBI_Id", - "value": "fb0b744d500c4fe187fc782efa6004bb" - } - ], - "columns": [ - { - "name": "Parameter - Dimension", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - } - ], - "dataType": "string", - "isDataTypeInferred": true, - "isNameInferred": false, - "lineageTag": "f29057e2-1184-48f8-b6c5-08f85cfd5ec1", - "relatedColumnDetails": { - "groupByColumns": [ - { - "groupingColumn": "Parameter - Dimension Fields" - } - ] - }, - "sortByColumn": "Parameter - Dimension Order", - "sourceColumn": "[Value1]", - "summarizeBy": "none", - "type": "calculatedTableColumn" - }, - { - "name": "Parameter - Dimension Fields", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - } - ], - "dataType": "string", - "extendedProperties": [ - { - "name": "ParameterMetadata", - "type": "json", - "value": { - "kind": 2, - "version": 3 - } - } - ], - "isDataTypeInferred": true, - "isHidden": true, - "isNameInferred": false, - "lineageTag": "dc604c44-bcb1-44cd-acb7-55201a651d63", - "sortByColumn": "Parameter - Dimension Order", - "sourceColumn": "[Value2]", - "summarizeBy": "none", - "type": "calculatedTableColumn" - }, - { - "name": "Parameter - Dimension Order", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - } - ], - "dataType": "int64", - "formatString": "0", - "isDataTypeInferred": true, - "isHidden": true, - "isNameInferred": false, - "lineageTag": "2102f8c8-0503-42f5-ac07-b1a606810a4a", - "sourceColumn": "[Value3]", - "summarizeBy": "sum", - "type": "calculatedTableColumn" - } - ], - "lineageTag": "29294de5-e93a-47f4-bd78-26ec8efe7786", - "partitions": [ - { - "name": "Parameter - Dimension", - "mode": "import", - "source": { - "expression": [ - "{", - " (\"Customer\", NAMEOF('Customer'[Customer]), 0),", - " (\"Product\", NAMEOF('Product'[Product]), 1),", - " (\"Store\", NAMEOF('Store'[Store]), 2)", - "}" - ], - "type": "calculated" - } - } - ] - }, - { - "name": "Parameter - Measure", - "annotations": [ - { - "name": "PBI_Id", - "value": "1a6e47ba0137472192b990cc0ff130aa" - } - ], - "columns": [ - { - "name": "Parameter - Measure", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - } - ], - "dataType": "string", - "isDataTypeInferred": true, - "isNameInferred": false, - "lineageTag": "f2f4b00e-fa13-46c5-9726-16717f325e26", - "relatedColumnDetails": { - "groupByColumns": [ - { - "groupingColumn": "Parameter - Measure Fields" - } - ] - }, - "sortByColumn": "Parameter - Measure Order", - "sourceColumn": "[Value1]", - "summarizeBy": "none", - "type": "calculatedTableColumn" - }, - { - "name": "Parameter - Measure Fields", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - } - ], - "dataType": "string", - "extendedProperties": [ - { - "name": "ParameterMetadata", - "type": "json", - "value": { - "kind": 2, - "version": 3 - } - } - ], - "isDataTypeInferred": true, - "isHidden": true, - "isNameInferred": false, - "lineageTag": "4787b049-3037-4007-9719-bfeed93a7cff", - "sortByColumn": "Parameter - Measure Order", - "sourceColumn": "[Value2]", - "summarizeBy": "none", - "type": "calculatedTableColumn" - }, - { - "name": "Parameter - Measure Order", - "annotations": [ - { - "name": "SummarizationSetBy", - "value": "Automatic" - } - ], - "dataType": "int64", - "formatString": "0", - "isDataTypeInferred": true, - "isHidden": true, - "isNameInferred": false, - "lineageTag": "5898d688-d0af-4285-9d33-3a41d2401e4b", - "sourceColumn": "[Value3]", - "summarizeBy": "sum", - "type": "calculatedTableColumn" - } - ], - "lineageTag": "eee26640-bfec-44ed-b1e7-d56562bc25ed", - "partitions": [ - { - "name": "Parameter - Measure", - "mode": "import", - "source": { - "expression": [ - "{", - " (\"# Sales\", NAMEOF('Sales'[# Sales]), 0),", - " (\"# Products (with Sales)\", NAMEOF('Sales'[# Products (with Sales)]), 1),", - " (\"# Customers (with Sales)\", NAMEOF('Sales'[# Customers (with Sales)]), 2),", - " (\"Margin\", NAMEOF('Sales'[Margin]), 3),", - " (\"Sales Amount\", NAMEOF('Sales'[Sales Amount]), 4),", - " (\"Sales Qty\", NAMEOF('Sales'[Sales Qty]), 5)", - "}" - ], - "type": "calculated" - } - } - ] - } - ] - } -} \ No newline at end of file diff --git a/Test/SamplePBIP/Sales.Report/StaticResources/RegisteredResources/Light4437032645752863.json b/Test/SamplePBIP/Sales.Report/StaticResources/RegisteredResources/Light4437032645752863.json deleted file mode 100644 index 55da67ed..00000000 --- a/Test/SamplePBIP/Sales.Report/StaticResources/RegisteredResources/Light4437032645752863.json +++ /dev/null @@ -1,499 +0,0 @@ -{ - "$schema": "https://raw.githubusercontent.com/microsoft/powerbi-desktop-samples/main/Report%20Theme%20JSON%20Schema/reportThemeSchema-2.116.json", - "name": "Light", - "dataColors": [ - "#364B59", - "#C480A7", - "#663466", - "#6E98B5", - "#67002E", - "#5D8099", - "#3F213F", - "#EC64A9", - "#3599B8", - "#DFBFBF", - "#4AC5BB", - "#5F6B6D", - "#FB8281", - "#F4D25A", - "#7F898A", - "#A4DDEE", - "#FDAB89", - "#B687AC", - "#28738A", - "#A78F8F", - "#168980", - "#293537", - "#BB4A4A", - "#B59525", - "#475052", - "#6A9FB0", - "#BD7150", - "#7B4F71", - "#1B4D5C", - "#706060", - "#0F5C55", - "#1C2325", - "#7D3231", - "#796419", - "#303637", - "#476A75", - "#7E4B36", - "#52354C", - "#0D262E", - "#544848", - "#016AB8", - "#373D49", - "#FDB15D", - "#AAF20F", - "#5F646D", - "#8AA3EB", - "#FEE266", - "#A6687A", - "#3557B8", - "#DFCFBF", - "#4A91C5", - "#5F646D", - "#FBBF81", - "#C9F459", - "#7F838A", - "#A4B8EE", - "#FDE489", - "#B68794", - "#28428A", - "#A79B8F", - "#165889", - "#292E37", - "#BB824A", - "#8DB525", - "#474A52", - "#6A7CB0", - "#BDA750", - "#7B4F5A", - "#1B2C5C", - "#706860", - "#0F3C5C", - "#1C1E25", - "#7D5731", - "#5D7918", - "#303237", - "#475375", - "#7E6F36", - "#52343D", - "#0D152E", - "#544E48", - "#010EB8", - "#393749", - "#F9FD5D", - "#38F20F", - "#615F6D", - "#A08AEB", - "#CEFE66", - "#A67668", - "#5435B8", - "#DFDFBF", - "#4A53C5", - "#615F6D", - "#FAFB81", - "#7CF459", - "#807F8A", - "#B5A4EE", - "#DBFD89", - "#B69087", - "#3F288A", - "#A7A78F", - "#161F89", - "#2A2937", - "#BBBB4A", - "#45B525", - "#494752", - "#7A6AB0", - "#9CBD50", - "#7B594F", - "#291B5C", - "#707060", - "#0F155C", - "#1E1C25", - "#7C7D31", - "#2D7918", - "#303037", - "#514775", - "#697E36", - "#523B34", - "#140D2E", - "#545448", - "#4E01B8", - "#423749", - "#A9FD5D", - "#0FF256", - "#675F6D", - "#D18AEB", - "#82FE66", - "#A69468", - "#9535B8", - "#CFDFBF", - "#7D4AC5", - "#675F6D", - "#BCFB81", - "#59F484", - "#857F8A", - "#DAA4EE", - "#A1FD89", - "#B6A887", - "#6F288A", - "#9BA78F", - "#461689", - "#322937", - "#82BB4A", - "#25B54C", - "#4E4752", - "#9E6AB0", - "#65BD50", - "#7B6E4F", - "#4A1B5C", - "#687060", - "#2E0F5C", - "#221C25", - "#567D31", - "#187934", - "#343037", - "#684775", - "#457E36", - "#524934", - "#250D2E", - "#4E5448", - "#AA01B8", - "#493746", - "#5DFD62", - "#0FF2C7", - "#6D5F6B", - "#EB8AD3", - "#66FE96", - "#99A668", - "#B83598", - "#BFDFBF", - "#BA4AC5", - "#6D5F6B", - "#81FB82", - "#59F4D1", - "#8A7F89", - "#EEA4DD", - "#89FDAA", - "#ACB687", - "#8A2873", - "#8FA78F", - "#801689", - "#372934", - "#4ABB4A", - "#25B594", - "#524750", - "#B06A9F", - "#50BD70", - "#717B4F", - "#5C1B4D", - "#607060", - "#540F5C", - "#251C22", - "#317D32", - "#187964", - "#373036", - "#75476A", - "#367E4A", - "#4C5234", - "#2E0D26", - "#485448", - "#B8016A", - "#49373D", - "#5DFDB1", - "#0FAAF2", - "#6D5F64", - "#EB8AA3", - "#66FEE2", - "#7AA668", - "#B83557", - "#BFDFCF", - "#C54A91", - "#6D5F64", - "#81FBBF", - "#59C9F4", - "#8A7F83", - "#EEA4B8", - "#89FDE5", - "#94B687", - "#8A2842", - "#8FA79B", - "#891658", - "#37292E", - "#4ABB82", - "#258DB5", - "#52474A", - "#B06A7C", - "#50BDA7", - "#5B7B4F", - "#5C1B2C", - "#607068", - "#5C0F3C", - "#251C1E", - "#317D58", - "#185D79", - "#373032", - "#754752", - "#367E6F", - "#3D5234", - "#2E0D15", - "#48544E", - "#B8010E", - "#493937", - "#5DF9FD", - "#0F38F2", - "#6D615F", - "#EBA08A", - "#66CEFE", - "#68A676", - "#B85435", - "#BFDFDF", - "#C54A53", - "#6D615F", - "#81FAFB", - "#597CF4", - "#8A807F", - "#EEB5A4", - "#89DBFD", - "#87B691", - "#8A3F28", - "#8FA7A7", - "#89161E", - "#372A29", - "#4ABBBB", - "#2545B5", - "#524947", - "#B07A6A", - "#509CBD", - "#4F7B58", - "#5C291B", - "#607070", - "#5C0F15", - "#251E1C", - "#317C7D", - "#182D79", - "#373030", - "#755147", - "#36687E", - "#34523B", - "#2E140D", - "#485454", - "#B84E01", - "#494337", - "#5DA9FD", - "#560FF2", - "#6D685F", - "#EBD18A", - "#6681FE", - "#68A694", - "#B89535", - "#BFCFDF", - "#C57D4A", - "#6D685F", - "#81BCFB", - "#8459F4", - "#8A857F", - "#EEDAA4", - "#89A1FD", - "#87B6A8", - "#8A7028", - "#8F9BA7", - "#894616", - "#373229", - "#4A82BB", - "#4D25B5", - "#524E47", - "#B09E6A", - "#5065BD", - "#4F7B6F", - "#5C4A1B", - "#606870", - "#5C2E0F", - "#25221C", - "#31567D", - "#341879", - "#373430", - "#756947", - "#36447E", - "#345249", - "#2E250D", - "#484E54", - "#B8A901", - "#464937", - "#615DFD", - "#C70FF2", - "#6B6D5F", - "#D4EB8A", - "#9666FE", - "#6898A6", - "#99B835", - "#BFBFDF", - "#C5BA4A", - "#6B6D5F", - "#8181FB", - "#D159F4", - "#898A7F", - "#DDEEA4", - "#AA89FD", - "#87ACB6", - "#738A28", - "#8F8FA7", - "#897F16", - "#353729", - "#4A4ABB", - "#9425B5", - "#505247", - "#9FB06A", - "#7050BD", - "#4F717B", - "#4D5C1B", - "#606070", - "#5C540F", - "#23251C", - "#31317D", - "#641879", - "#363730", - "#6A7547", - "#4B367E", - "#344C52", - "#262E0D", - "#484854", - "#6AB801", - "#3D4937", - "#B15DFD", - "#F20FAA", - "#646D5F", - "#A3EB8A", - "#E266FE", - "#687AA6", - "#57B835", - "#CFBFDF", - "#91C54A", - "#646D5F", - "#BF81FB", - "#F459C9", - "#838A7F", - "#B7EEA4", - "#E589FD", - "#8794B6", - "#428A28", - "#9B8FA7", - "#588916", - "#2E3729", - "#824ABB", - "#B5258D", - "#4A5247", - "#7CB06A", - "#A750BD", - "#4F5B7B", - "#2C5C1B", - "#686070", - "#3C5C0F", - "#1E251C", - "#57317D", - "#79185D", - "#323730", - "#537547", - "#6F367E", - "#343D52", - "#152E0D", - "#4E4854", - "#0FB801", - "#37493A", - "#FD5DF9", - "#F20F39", - "#5F6D61", - "#8AEBA1", - "#FE66CE", - "#7568A6", - "#35B854", - "#DFBFDF", - "#54C54A", - "#5F6D61", - "#FB81FA", - "#F4597C", - "#7F8A80", - "#A4EEB5", - "#FD89DB", - "#9187B6", - "#288A3F", - "#A78FA7", - "#1F8916", - "#29372B", - "#BB4ABB", - "#B52544", - "#475249", - "#6AB07B", - "#BD509B", - "#594F7B", - "#1B5C2A", - "#706070", - "#155C0F", - "#1C251E", - "#7D317B", - "#79182D", - "#303731", - "#477552", - "#7E3668", - "#3B3452", - "#0D2E14", - "#544854", - "#01B84E", - "#374942", - "#FD5DA9", - "#F2560F", - "#5F6D67", - "#8AEBD1", - "#FE6682", - "#9468A6", - "#35B895", - "#DFBFCF", - "#4AC57D", - "#5F6D67", - "#FB81BD", - "#F48459", - "#7F8A85", - "#A4EEDA", - "#FD89A1", - "#A887B6", - "#288A6F", - "#A78F9B", - "#168946", - "#293732", - "#BB4A82", - "#B54C25", - "#47524E", - "#6AB09D", - "#BD5065", - "#6E4F7B", - "#1B5C4A", - "#706068", - "#0F5C2E", - "#1C2522", - "#7D3156", - "#793418", - "#303734", - "#477568", - "#7E3644", - "#493452", - "#0D2E25", - "#54484E" - ], - "foreground": "#192229", - "background": "#FFFFFF", - "foregroundNeutralSecondary": "#716E76", - "backgroundLight": "#EBE8FA", - "foregroundNeutralTertiary": "#96939E", - "backgroundNeutral": "#DAD8E8", - "tableAccent": "#BD3978", - "maximum": "#3F213F", - "center": "#663466", - "minimum": "#6E98B5", - "bad": "#BD3978", - "neutral": "#C480A7", - "good": "#9C6584" -} \ No newline at end of file diff --git a/Test/SamplePBIP/Sales.Report/StaticResources/RegisteredResources/_7abfc6c7-1a23-4b5f-bd8b-8dc472366284171093267.jpg b/Test/SamplePBIP/Sales.Report/StaticResources/RegisteredResources/_7abfc6c7-1a23-4b5f-bd8b-8dc472366284171093267.jpg deleted file mode 100644 index 00e5de2a..00000000 Binary files a/Test/SamplePBIP/Sales.Report/StaticResources/RegisteredResources/_7abfc6c7-1a23-4b5f-bd8b-8dc472366284171093267.jpg and /dev/null differ diff --git a/Test/SamplePBIP/Sales.Report/StaticResources/SharedResources/BaseThemes/CY23SU04.json b/Test/SamplePBIP/Sales.Report/StaticResources/SharedResources/BaseThemes/CY23SU04.json deleted file mode 100644 index 7d092cbd..00000000 --- a/Test/SamplePBIP/Sales.Report/StaticResources/SharedResources/BaseThemes/CY23SU04.json +++ /dev/null @@ -1,674 +0,0 @@ -{ - "name": "CY23SU04", - "dataColors": [ - "#118DFF", - "#12239E", - "#E66C37", - "#6B007B", - "#E044A7", - "#744EC2", - "#D9B300", - "#D64550", - "#197278", - "#1AAB40", - "#15C6F4", - "#4092FF", - "#FFA058", - "#BE5DC9", - "#F472D0", - "#B5A1FF", - "#C4A200", - "#FF8080", - "#00DBBC", - "#5BD667", - "#0091D5", - "#4668C5", - "#FF6300", - "#99008A", - "#EC008C", - "#533285", - "#99700A", - "#FF4141", - "#1F9A85", - "#25891C", - "#0057A2", - "#002050", - "#C94F0F", - "#450F54", - "#B60064", - "#34124F", - "#6A5A29", - "#1AAB40", - "#BA141A", - "#0C3D37", - "#0B511F" - ], - "foreground": "#252423", - "foregroundNeutralSecondary": "#605E5C", - "foregroundNeutralTertiary": "#B3B0AD", - "background": "#FFFFFF", - "backgroundLight": "#F3F2F1", - "backgroundNeutral": "#C8C6C4", - "tableAccent": "#118DFF", - "good": "#1AAB40", - "neutral": "#D9B300", - "bad": "#D64554", - "maximum": "#118DFF", - "center": "#D9B300", - "minimum": "#DEEFFF", - "null": "#FF7F48", - "hyperlink": "#0078d4", - "visitedHyperlink": "#0078d4", - "textClasses": { - "callout": { - "fontSize": 45, - "fontFace": "DIN", - "color": "#252423" - }, - "title": { - "fontSize": 12, - "fontFace": "DIN", - "color": "#252423" - }, - "header": { - "fontSize": 12, - "fontFace": "Segoe UI Semibold", - "color": "#252423" - }, - "label": { - "fontSize": 10, - "fontFace": "Segoe UI", - "color": "#252423" - } - }, - "visualStyles": { - "*": { - "*": { - "*": [ - { - "wordWrap": true - } - ], - "line": [ - { - "transparency": 0 - } - ], - "outline": [ - { - "transparency": 0 - } - ], - "plotArea": [ - { - "transparency": 0 - } - ], - "categoryAxis": [ - { - "showAxisTitle": true, - "gridlineStyle": "dotted", - "concatenateLabels": false - } - ], - "valueAxis": [ - { - "showAxisTitle": true, - "gridlineStyle": "dotted" - } - ], - "y2Axis": [ - { - "show": true - } - ], - "title": [ - { - "titleWrap": true - } - ], - "lineStyles": [ - { - "strokeWidth": 3 - } - ], - "wordWrap": [ - { - "show": true - } - ], - "background": [ - { - "show": true, - "transparency": 0 - } - ], - "outspacePane": [ - { - "backgroundColor": { - "solid": { - "color": "#ffffff" - } - }, - "transparency": 0, - "border": true, - "borderColor": { - "solid": { - "color": "#B3B0AD" - } - } - } - ], - "filterCard": [ - { - "$id": "Applied", - "transparency": 0, - "foregroundColor": { - "solid": { - "color": "#252423" - } - }, - "border": true - }, - { - "$id": "Available", - "transparency": 0, - "foregroundColor": { - "solid": { - "color": "#252423" - } - }, - "border": true - } - ] - } - }, - "scatterChart": { - "*": { - "bubbles": [ - { - "bubbleSize": -10 - } - ], - "general": [ - { - "responsive": true - } - ], - "fillPoint": [ - { - "show": true - } - ], - "legend": [ - { - "showGradientLegend": true - } - ] - } - }, - "lineChart": { - "*": { - "general": [ - { - "responsive": true - } - ], - "smallMultiplesLayout": [ - { - "backgroundTransparency": 0, - "gridLineType": "inner" - } - ] - } - }, - "map": { - "*": { - "bubbles": [ - { - "bubbleSize": -10 - } - ] - } - }, - "azureMap": { - "*": { - "bubbleLayer": [ - { - "bubbleRadius": 8, - "minBubbleRadius": 8, - "maxRadius": 40 - } - ], - "barChart": [ - { - "barHeight": 3, - "thickness": 3 - } - ] - } - }, - "pieChart": { - "*": { - "legend": [ - { - "show": true, - "position": "RightCenter" - } - ], - "labels": [ - { - "labelStyle": "Data value, percent of total" - } - ] - } - }, - "donutChart": { - "*": { - "legend": [ - { - "show": true, - "position": "RightCenter" - } - ], - "labels": [ - { - "labelStyle": "Data value, percent of total" - } - ] - } - }, - "pivotTable": { - "*": { - "*": [ - { - "showExpandCollapseButtons": true - } - ] - } - }, - "multiRowCard": { - "*": { - "card": [ - { - "outlineWeight": 2, - "barShow": true, - "barWeight": 2 - } - ] - } - }, - "kpi": { - "*": { - "trendline": [ - { - "transparency": 20 - } - ] - } - }, - "slicer": { - "*": { - "general": [ - { - "responsive": true - } - ], - "date": [ - { - "hideDatePickerButton": false - } - ], - "items": [ - { - "padding": 4, - "accessibilityContrastProperties": true - } - ] - } - }, - "waterfallChart": { - "*": { - "general": [ - { - "responsive": true - } - ] - } - }, - "columnChart": { - "*": { - "general": [ - { - "responsive": true - } - ], - "legend": [ - { - "showGradientLegend": true - } - ], - "smallMultiplesLayout": [ - { - "backgroundTransparency": 0, - "gridLineType": "inner" - } - ] - } - }, - "clusteredColumnChart": { - "*": { - "general": [ - { - "responsive": true - } - ], - "legend": [ - { - "showGradientLegend": true - } - ], - "smallMultiplesLayout": [ - { - "backgroundTransparency": 0, - "gridLineType": "inner" - } - ] - } - }, - "hundredPercentStackedColumnChart": { - "*": { - "general": [ - { - "responsive": true - } - ], - "legend": [ - { - "showGradientLegend": true - } - ], - "smallMultiplesLayout": [ - { - "backgroundTransparency": 0, - "gridLineType": "inner" - } - ] - } - }, - "barChart": { - "*": { - "general": [ - { - "responsive": true - } - ], - "legend": [ - { - "showGradientLegend": true - } - ], - "smallMultiplesLayout": [ - { - "backgroundTransparency": 0, - "gridLineType": "inner" - } - ] - } - }, - "clusteredBarChart": { - "*": { - "general": [ - { - "responsive": true - } - ], - "legend": [ - { - "showGradientLegend": true - } - ], - "smallMultiplesLayout": [ - { - "backgroundTransparency": 0, - "gridLineType": "inner" - } - ] - } - }, - "hundredPercentStackedBarChart": { - "*": { - "general": [ - { - "responsive": true - } - ], - "legend": [ - { - "showGradientLegend": true - } - ], - "smallMultiplesLayout": [ - { - "backgroundTransparency": 0, - "gridLineType": "inner" - } - ] - } - }, - "areaChart": { - "*": { - "general": [ - { - "responsive": true - } - ], - "smallMultiplesLayout": [ - { - "backgroundTransparency": 0, - "gridLineType": "inner" - } - ] - } - }, - "stackedAreaChart": { - "*": { - "general": [ - { - "responsive": true - } - ], - "smallMultiplesLayout": [ - { - "backgroundTransparency": 0, - "gridLineType": "inner" - } - ] - } - }, - "lineClusteredColumnComboChart": { - "*": { - "general": [ - { - "responsive": true - } - ], - "smallMultiplesLayout": [ - { - "backgroundTransparency": 0, - "gridLineType": "inner" - } - ] - } - }, - "lineStackedColumnComboChart": { - "*": { - "general": [ - { - "responsive": true - } - ], - "smallMultiplesLayout": [ - { - "backgroundTransparency": 0, - "gridLineType": "inner" - } - ] - } - }, - "ribbonChart": { - "*": { - "general": [ - { - "responsive": true - } - ] - } - }, - "group": { - "*": { - "background": [ - { - "show": false - } - ] - } - }, - "basicShape": { - "*": { - "background": [ - { - "show": false - } - ], - "general": [ - { - "keepLayerOrder": true - } - ], - "visualHeader": [ - { - "show": false - } - ] - } - }, - "shape": { - "*": { - "background": [ - { - "show": false - } - ], - "general": [ - { - "keepLayerOrder": true - } - ], - "visualHeader": [ - { - "show": false - } - ] - } - }, - "image": { - "*": { - "background": [ - { - "show": false - } - ], - "general": [ - { - "keepLayerOrder": true - } - ], - "visualHeader": [ - { - "show": false - } - ], - "lockAspect": [ - { - "show": true - } - ] - } - }, - "actionButton": { - "*": { - "background": [ - { - "show": false - } - ], - "visualHeader": [ - { - "show": false - } - ] - } - }, - "pageNavigator": { - "*": { - "background": [ - { - "show": false - } - ], - "visualHeader": [ - { - "show": false - } - ] - } - }, - "bookmarkNavigator": { - "*": { - "background": [ - { - "show": false - } - ], - "visualHeader": [ - { - "show": false - } - ] - } - }, - "textbox": { - "*": { - "general": [ - { - "keepLayerOrder": true - } - ], - "visualHeader": [ - { - "show": false - } - ] - } - }, - "page": { - "*": { - "outspace": [ - { - "color": { - "solid": { - "color": "#FFFFFF" - } - } - } - ], - "background": [ - { - "transparency": 100 - } - ] - } - } - } -} \ No newline at end of file diff --git a/Test/SamplePBIP/Sales.Report/definition.pbir b/Test/SamplePBIP/Sales.Report/definition.pbir deleted file mode 100644 index 8889dcf4..00000000 --- a/Test/SamplePBIP/Sales.Report/definition.pbir +++ /dev/null @@ -1,9 +0,0 @@ -{ - "version": "1.0", - "datasetReference": { - "byPath": { - "path": "../Sales.Dataset" - }, - "byConnection": null - } -} \ No newline at end of file diff --git a/Test/SamplePBIP/Sales.Report/item.config.json b/Test/SamplePBIP/Sales.Report/item.config.json deleted file mode 100644 index 172a9ff4..00000000 --- a/Test/SamplePBIP/Sales.Report/item.config.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": "1.0", - "logicalId": "e5e0eb61-bd59-44f6-8408-35e72b54012c" -} \ No newline at end of file diff --git a/Test/SamplePBIP/Sales.Report/item.metadata.json b/Test/SamplePBIP/Sales.Report/item.metadata.json deleted file mode 100644 index eef95209..00000000 --- a/Test/SamplePBIP/Sales.Report/item.metadata.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "type": "report", - "displayName": "Sales" -} \ No newline at end of file diff --git a/Test/SamplePBIP/Sales.Report/report.json b/Test/SamplePBIP/Sales.Report/report.json deleted file mode 100644 index 1c883cee..00000000 --- a/Test/SamplePBIP/Sales.Report/report.json +++ /dev/null @@ -1,517 +0,0 @@ -{ - "config": "{\"version\":\"5.43\",\"themeCollection\":{\"baseTheme\":{\"name\":\"CY23SU04\",\"version\":\"5.43\",\"type\":2},\"customTheme\":{\"name\":\"Light4437032645752863.json\",\"version\":\"5.48\",\"type\":1}},\"activeSectionIndex\":0,\"bookmarks\":[{\"displayName\":\"Bookmark 1\",\"name\":\"Bookmark7c19b7211ada7de10c30\",\"explorationState\":{\"version\":\"1.3\",\"activeSection\":\"ReportSection89a9619c7025093ade1c\",\"sections\":{\"ReportSection89a9619c7025093ade1c\":{\"filters\":{\"byExpr\":[{\"name\":\"Filterf99428c5489cc4e6747a\",\"type\":\"Categorical\",\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Calendar\"}},\"Property\":\"Year\"}},\"howCreated\":1},{\"name\":\"Filter78bda1908a0bd749d86a\",\"type\":\"Categorical\",\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Product\"}},\"Property\":\"Category\"}},\"howCreated\":1},{\"name\":\"Filter9ce4dd28c25b24ab1603\",\"type\":\"Categorical\",\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Product\"}},\"Property\":\"Subcategory\"}},\"howCreated\":1},{\"name\":\"Filterfa59dace34a0d2b060c1\",\"type\":\"Categorical\",\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Store\"}},\"Property\":\"Store\"}},\"howCreated\":1}]},\"visualContainers\":{\"c258740674a8a53d2c4c\":{\"filters\":{\"byExpr\":[{\"name\":\"Filter7a63ba4725d907434991\",\"type\":\"Categorical\",\"filter\":{\"Version\":2,\"From\":[{\"Name\":\"s\",\"Entity\":\"Smart Calcs\",\"Type\":0}],\"Where\":[{\"Condition\":{\"In\":{\"Expressions\":[{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"s\"}},\"Property\":\"Smart Calc\"}}],\"Values\":[[{\"Literal\":{\"Value\":\"'Label - ▲ LY'\"}}]]}}}]},\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Smart Calcs\"}},\"Property\":\"Smart Calc\"}},\"howCreated\":1},{\"type\":\"Advanced\",\"expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Sales\"}},\"Property\":\"Sales Amount\"}},\"howCreated\":0}]},\"singleVisual\":{\"visualType\":\"card\",\"objects\":{},\"orderBy\":[{\"Direction\":2,\"Expression\":{\"Aggregation\":{\"Expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Calendar\"}},\"Property\":\"Year\"}},\"Function\":2}}}]}},\"0f9bcdc145fc9e6fdc33\":{\"filters\":{\"byExpr\":[{\"name\":\"Filter2a1ae8311dd5bd80e06c\",\"type\":\"Categorical\",\"filter\":{\"Version\":2,\"From\":[{\"Name\":\"s\",\"Entity\":\"Smart Calcs\",\"Type\":0}],\"Where\":[{\"Condition\":{\"In\":{\"Expressions\":[{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"s\"}},\"Property\":\"Smart Calc\"}}],\"Values\":[[{\"Literal\":{\"Value\":\"'Label - ▲ LY'\"}}]]}}}]},\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Smart Calcs\"}},\"Property\":\"Smart Calc\"}},\"howCreated\":1},{\"type\":\"Advanced\",\"expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Sales\"}},\"Property\":\"Margin\"}},\"howCreated\":0}]},\"singleVisual\":{\"visualType\":\"card\",\"objects\":{},\"orderBy\":[{\"Direction\":2,\"Expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Sales\"}},\"Property\":\"Margin\"}}}]}},\"9472f90b5973fdfeb1a9\":{\"filters\":{\"byExpr\":[{\"name\":\"Filterac4e7a5cd0c9731a470e\",\"type\":\"Categorical\",\"filter\":{\"Version\":2,\"From\":[{\"Name\":\"s\",\"Entity\":\"Smart Calcs\",\"Type\":0}],\"Where\":[{\"Condition\":{\"In\":{\"Expressions\":[{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"s\"}},\"Property\":\"Smart Calc\"}}],\"Values\":[[{\"Literal\":{\"Value\":\"'Label - ▲ LY'\"}}]]}}}]},\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Smart Calcs\"}},\"Property\":\"Smart Calc\"}},\"howCreated\":1},{\"type\":\"Advanced\",\"expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Sales\"}},\"Property\":\"Sales Amount Avg per Day\"}},\"howCreated\":0}]},\"singleVisual\":{\"visualType\":\"card\",\"objects\":{},\"orderBy\":[{\"Direction\":2,\"Expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Sales\"}},\"Property\":\"Sales Amount Avg per Day\"}}}]}},\"bf50a0a6082015e4bcfe\":{\"filters\":{\"byExpr\":[{\"name\":\"Filter2bc8e5e0b6ab2126005e\",\"type\":\"Advanced\",\"expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Sales\"}},\"Property\":\"Sales Qty\"}},\"howCreated\":1},{\"name\":\"Filter0f21badd084a049e0663\",\"type\":\"Advanced\",\"expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Sales\"}},\"Property\":\"Sales Amount Avg per Day\"}},\"howCreated\":1},{\"name\":\"Filterf321968e3dc8aa5d2791\",\"type\":\"Categorical\",\"filter\":{\"Version\":2,\"From\":[{\"Name\":\"s\",\"Entity\":\"Smart Calcs\",\"Type\":0}],\"Where\":[{\"Condition\":{\"In\":{\"Expressions\":[{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"s\"}},\"Property\":\"Smart Calc\"}}],\"Values\":[[{\"Literal\":{\"Value\":\"'Label - ▲ LY'\"}}]]}}}]},\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Smart Calcs\"}},\"Property\":\"Smart Calc\"}},\"howCreated\":1},{\"type\":\"Advanced\",\"expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Sales\"}},\"Property\":\"# Customers (with Sales)\"}},\"howCreated\":0}]},\"singleVisual\":{\"visualType\":\"card\",\"objects\":{},\"orderBy\":[{\"Direction\":2,\"Expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Sales\"}},\"Property\":\"# Customers (with Sales)\"}}}]}},\"8b8727ff328bdc49692c\":{\"filters\":{\"byExpr\":[{\"type\":\"Categorical\",\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Calendar\"}},\"Property\":\"Date\"}},\"howCreated\":0}]},\"singleVisual\":{\"visualType\":\"slicer\",\"objects\":{},\"orderBy\":[{\"Direction\":1,\"Expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Calendar\"}},\"Property\":\"Date\"}}}],\"activeProjections\":{\"Values\":[{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Calendar\"}},\"Property\":\"Date\"}}]}}},\"32b08d7f32cb5f9816cc\":{\"filters\":{\"byExpr\":[{\"name\":\"Filter\",\"type\":\"Categorical\",\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Calendar\"}},\"Property\":\"Year\"}},\"howCreated\":1},{\"type\":\"Advanced\",\"expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Sales\"}},\"Property\":\"Sales Amount\"}},\"howCreated\":0},{\"type\":\"Categorical\",\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Product\"}},\"Property\":\"Product\"}},\"howCreated\":0},{\"type\":\"Categorical\",\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Product\"}},\"Property\":\"Brand\"}},\"howCreated\":0},{\"type\":\"Categorical\",\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Customer\"}},\"Property\":\"Gender\"}},\"howCreated\":0}]},\"singleVisual\":{\"visualType\":\"barChart\",\"objects\":{},\"orderBy\":[{\"Direction\":2,\"Expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Sales\"}},\"Property\":\"Sales Amount\"}}}],\"activeProjections\":{\"Category\":[{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Product\"}},\"Property\":\"Brand\"}}]}}},\"484fbdd73143c5bf71fa\":{\"filters\":{\"byExpr\":[{\"type\":\"Advanced\",\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Calendar\"}},\"Property\":\"Year\"}},\"howCreated\":0},{\"type\":\"Categorical\",\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Calendar\"}},\"Property\":\"Month\"}},\"howCreated\":0},{\"type\":\"Advanced\",\"expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Sales\"}},\"Property\":\"Sales Amount\"}},\"howCreated\":0},{\"type\":\"Advanced\",\"expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Sales\"}},\"Property\":\"Sales Amount (LY)\"}},\"howCreated\":0},{\"type\":\"Advanced\",\"expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Sales\"}},\"Property\":\"Sales Amount (12M average)\"}},\"howCreated\":0},{\"type\":\"Advanced\",\"expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Sales\"}},\"Property\":\"Margin\"}},\"howCreated\":0}]},\"singleVisual\":{\"visualType\":\"lineClusteredColumnComboChart\",\"objects\":{},\"orderBy\":[{\"Direction\":1,\"Expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Calendar\"}},\"Property\":\"Year\"}}},{\"Direction\":1,\"Expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Calendar\"}},\"Property\":\"Month\"}}}],\"activeProjections\":{\"Category\":[{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Calendar\"}},\"Property\":\"Year\"}},{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Calendar\"}},\"Property\":\"Month\"}}]}}},\"4b18758374093c2e23fb\":{\"filters\":{\"byExpr\":[{\"type\":\"Categorical\",\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Calendar\"}},\"Property\":\"Month\"}},\"howCreated\":0},{\"type\":\"Categorical\",\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Store\"}},\"Property\":\"Store\"}},\"howCreated\":0},{\"type\":\"Advanced\",\"expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Sales\"}},\"Property\":\"Sales Amount\"}},\"howCreated\":0},{\"type\":\"Advanced\",\"expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Sales\"}},\"Property\":\"# Customers (with Sales)\"}},\"howCreated\":0}]},\"singleVisual\":{\"visualType\":\"scatterChart\",\"objects\":{},\"orderBy\":[{\"Direction\":2,\"Expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Store\"}},\"Property\":\"Store\"}}}],\"activeProjections\":{\"X\":[{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Calendar\"}},\"Property\":\"Month\"}}]}}},\"5acb1caf298449a8acb4\":{\"filters\":{\"byExpr\":[{\"type\":\"Advanced\",\"expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Sales\"}},\"Property\":\"Sales Amount\"}},\"howCreated\":0},{\"type\":\"Advanced\",\"expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Sales\"}},\"Property\":\"Sales Amount (LY)\"}},\"howCreated\":0},{\"type\":\"Categorical\",\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Product\"}},\"Property\":\"Category\"}},\"howCreated\":0},{\"type\":\"Categorical\",\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Product\"}},\"Property\":\"Subcategory\"}},\"howCreated\":0},{\"type\":\"Categorical\",\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Product\"}},\"Property\":\"Product\"}},\"howCreated\":0}]},\"singleVisual\":{\"visualType\":\"clusteredBarChart\",\"objects\":{},\"orderBy\":[{\"Direction\":2,\"Expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Sales\"}},\"Property\":\"Sales Amount\"}}}],\"activeProjections\":{\"Category\":[{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Product\"}},\"Property\":\"Category\"}}]}},\"highlight\":{\"selection\":[{\"dataMap\":{\"Product.Category\":[{\"scopeId\":{\"Comparison\":{\"ComparisonKind\":0,\"Left\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Product\"}},\"Property\":\"Category\"}},\"Right\":{\"Literal\":{\"Value\":\"'Computers'\"}}}}}]},\"metadata\":[\"Measure Table.Sales Amount\"]}],\"filterExpressionMetadata\":{\"expressions\":[{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Product\"}},\"Property\":\"Category\"}}],\"cachedValueItems\":[{\"identities\":[{\"scopeId\":{\"Comparison\":{\"ComparisonKind\":0,\"Left\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Product\"}},\"Property\":\"Category\"}},\"Right\":{\"Literal\":{\"Value\":\"'Computers'\"}}}}}],\"valueMap\":{\"0\":\"Computers\"}}]}}},\"eb5c360e357e8b54eb88\":{\"singleVisual\":{\"visualType\":\"textbox\",\"objects\":{}}},\"3641d5ce63678e76a370\":{\"filters\":{\"byExpr\":[{\"name\":\"Filterf1ec25bb722d30be4755\",\"type\":\"Categorical\",\"filter\":{\"Version\":2,\"From\":[{\"Name\":\"a\",\"Entity\":\"About\",\"Type\":0}],\"Where\":[{\"Condition\":{\"In\":{\"Expressions\":[{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"a\"}},\"Property\":\"Key\"}}],\"Values\":[[{\"Literal\":{\"Value\":\"'Last Refresh'\"}}]]}}}]},\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"About\"}},\"Property\":\"Key\"}},\"howCreated\":1},{\"type\":\"Advanced\",\"expression\":{\"Aggregation\":{\"Expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"About\"}},\"Property\":\"Value\"}},\"Function\":3}},\"howCreated\":0}]},\"singleVisual\":{\"visualType\":\"card\",\"objects\":{}}},\"624827fbb3e3bd2b52a0\":{\"filters\":{\"byExpr\":[{\"type\":\"Advanced\",\"expression\":{\"Aggregation\":{\"Expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Sales\"}},\"Property\":\"Environment\"}},\"Function\":3}},\"howCreated\":0}]},\"singleVisual\":{\"visualType\":\"card\",\"objects\":{}}}}}},\"objects\":{\"merge\":{\"outspacePane\":[{\"properties\":{\"expanded\":{\"expr\":{\"Literal\":{\"Value\":\"false\"}}},\"visible\":{\"expr\":{\"Literal\":{\"Value\":\"true\"}}}}}]}}},\"options\":{\"targetVisualNames\":[\"5acb1caf298449a8acb4\"]}},{\"displayName\":\"Bookmark 2\",\"name\":\"Bookmarkd2402dab4df672d2c479\",\"explorationState\":{\"version\":\"1.3\",\"activeSection\":\"ReportSection89a9619c7025093ade1c\",\"sections\":{\"ReportSection89a9619c7025093ade1c\":{\"filters\":{\"byExpr\":[{\"name\":\"Filterf99428c5489cc4e6747a\",\"type\":\"Categorical\",\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Calendar\"}},\"Property\":\"Year\"}},\"howCreated\":1},{\"name\":\"Filter78bda1908a0bd749d86a\",\"type\":\"Categorical\",\"filter\":{\"Version\":2,\"From\":[{\"Name\":\"p\",\"Entity\":\"Product\",\"Type\":0}],\"Where\":[{\"Condition\":{\"In\":{\"Expressions\":[{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"p\"}},\"Property\":\"Category\"}}],\"Values\":[[{\"Literal\":{\"Value\":\"'Audio'\"}}]]}}}]},\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Product\"}},\"Property\":\"Category\"}},\"howCreated\":1},{\"name\":\"Filter9ce4dd28c25b24ab1603\",\"type\":\"Categorical\",\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Product\"}},\"Property\":\"Subcategory\"}},\"howCreated\":1},{\"name\":\"Filterfa59dace34a0d2b060c1\",\"type\":\"Categorical\",\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Store\"}},\"Property\":\"Store\"}},\"howCreated\":1}]},\"visualContainers\":{\"c258740674a8a53d2c4c\":{\"filters\":{\"byExpr\":[{\"name\":\"Filter7a63ba4725d907434991\",\"type\":\"Categorical\",\"filter\":{\"Version\":2,\"From\":[{\"Name\":\"s\",\"Entity\":\"Smart Calcs\",\"Type\":0}],\"Where\":[{\"Condition\":{\"In\":{\"Expressions\":[{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"s\"}},\"Property\":\"Smart Calc\"}}],\"Values\":[[{\"Literal\":{\"Value\":\"'Label - ▲ LY'\"}}]]}}}]},\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Smart Calcs\"}},\"Property\":\"Smart Calc\"}},\"howCreated\":1},{\"type\":\"Advanced\",\"expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Sales\"}},\"Property\":\"Sales Amount\"}},\"howCreated\":0}]},\"singleVisual\":{\"visualType\":\"card\",\"objects\":{},\"orderBy\":[{\"Direction\":2,\"Expression\":{\"Aggregation\":{\"Expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Calendar\"}},\"Property\":\"Year\"}},\"Function\":2}}}]}},\"0f9bcdc145fc9e6fdc33\":{\"filters\":{\"byExpr\":[{\"name\":\"Filter2a1ae8311dd5bd80e06c\",\"type\":\"Categorical\",\"filter\":{\"Version\":2,\"From\":[{\"Name\":\"s\",\"Entity\":\"Smart Calcs\",\"Type\":0}],\"Where\":[{\"Condition\":{\"In\":{\"Expressions\":[{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"s\"}},\"Property\":\"Smart Calc\"}}],\"Values\":[[{\"Literal\":{\"Value\":\"'Label - ▲ LY'\"}}]]}}}]},\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Smart Calcs\"}},\"Property\":\"Smart Calc\"}},\"howCreated\":1},{\"type\":\"Advanced\",\"expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Sales\"}},\"Property\":\"Margin\"}},\"howCreated\":0}]},\"singleVisual\":{\"visualType\":\"card\",\"objects\":{},\"orderBy\":[{\"Direction\":2,\"Expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Sales\"}},\"Property\":\"Margin\"}}}]}},\"9472f90b5973fdfeb1a9\":{\"filters\":{\"byExpr\":[{\"name\":\"Filterac4e7a5cd0c9731a470e\",\"type\":\"Categorical\",\"filter\":{\"Version\":2,\"From\":[{\"Name\":\"s\",\"Entity\":\"Smart Calcs\",\"Type\":0}],\"Where\":[{\"Condition\":{\"In\":{\"Expressions\":[{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"s\"}},\"Property\":\"Smart Calc\"}}],\"Values\":[[{\"Literal\":{\"Value\":\"'Label - ▲ LY'\"}}]]}}}]},\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Smart Calcs\"}},\"Property\":\"Smart Calc\"}},\"howCreated\":1},{\"type\":\"Advanced\",\"expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Sales\"}},\"Property\":\"Sales Amount Avg per Day\"}},\"howCreated\":0}]},\"singleVisual\":{\"visualType\":\"card\",\"objects\":{},\"orderBy\":[{\"Direction\":2,\"Expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Sales\"}},\"Property\":\"Sales Amount Avg per Day\"}}}]}},\"bf50a0a6082015e4bcfe\":{\"filters\":{\"byExpr\":[{\"name\":\"Filter2bc8e5e0b6ab2126005e\",\"type\":\"Advanced\",\"expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Sales\"}},\"Property\":\"Sales Qty\"}},\"howCreated\":1},{\"name\":\"Filter0f21badd084a049e0663\",\"type\":\"Advanced\",\"expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Sales\"}},\"Property\":\"Sales Amount Avg per Day\"}},\"howCreated\":1},{\"name\":\"Filterf321968e3dc8aa5d2791\",\"type\":\"Categorical\",\"filter\":{\"Version\":2,\"From\":[{\"Name\":\"s\",\"Entity\":\"Smart Calcs\",\"Type\":0}],\"Where\":[{\"Condition\":{\"In\":{\"Expressions\":[{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"s\"}},\"Property\":\"Smart Calc\"}}],\"Values\":[[{\"Literal\":{\"Value\":\"'Label - ▲ LY'\"}}]]}}}]},\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Smart Calcs\"}},\"Property\":\"Smart Calc\"}},\"howCreated\":1},{\"type\":\"Advanced\",\"expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Sales\"}},\"Property\":\"# Customers (with Sales)\"}},\"howCreated\":0}]},\"singleVisual\":{\"visualType\":\"card\",\"objects\":{},\"orderBy\":[{\"Direction\":2,\"Expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Sales\"}},\"Property\":\"# Customers (with Sales)\"}}}]}},\"8b8727ff328bdc49692c\":{\"filters\":{\"byExpr\":[{\"type\":\"Categorical\",\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Calendar\"}},\"Property\":\"Date\"}},\"howCreated\":0}]},\"singleVisual\":{\"visualType\":\"slicer\",\"objects\":{},\"orderBy\":[{\"Direction\":1,\"Expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Calendar\"}},\"Property\":\"Date\"}}}],\"activeProjections\":{\"Values\":[{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Calendar\"}},\"Property\":\"Date\"}}]}}},\"32b08d7f32cb5f9816cc\":{\"filters\":{\"byExpr\":[{\"name\":\"Filter\",\"type\":\"Categorical\",\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Calendar\"}},\"Property\":\"Year\"}},\"howCreated\":1},{\"type\":\"Advanced\",\"expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Sales\"}},\"Property\":\"Sales Amount\"}},\"howCreated\":0},{\"type\":\"Categorical\",\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Product\"}},\"Property\":\"Product\"}},\"howCreated\":0},{\"type\":\"Categorical\",\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Product\"}},\"Property\":\"Brand\"}},\"howCreated\":0},{\"type\":\"Categorical\",\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Customer\"}},\"Property\":\"Gender\"}},\"howCreated\":0}]},\"singleVisual\":{\"visualType\":\"barChart\",\"objects\":{},\"orderBy\":[{\"Direction\":2,\"Expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Sales\"}},\"Property\":\"Sales Amount\"}}}],\"activeProjections\":{\"Category\":[{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Product\"}},\"Property\":\"Brand\"}}]}}},\"484fbdd73143c5bf71fa\":{\"filters\":{\"byExpr\":[{\"type\":\"Advanced\",\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Calendar\"}},\"Property\":\"Year\"}},\"howCreated\":0},{\"type\":\"Categorical\",\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Calendar\"}},\"Property\":\"Month\"}},\"howCreated\":0},{\"type\":\"Advanced\",\"expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Sales\"}},\"Property\":\"Sales Amount\"}},\"howCreated\":0},{\"type\":\"Advanced\",\"expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Sales\"}},\"Property\":\"Sales Amount (LY)\"}},\"howCreated\":0},{\"type\":\"Advanced\",\"expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Sales\"}},\"Property\":\"Sales Amount (12M average)\"}},\"howCreated\":0},{\"type\":\"Advanced\",\"expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Sales\"}},\"Property\":\"Margin\"}},\"howCreated\":0}]},\"singleVisual\":{\"visualType\":\"lineClusteredColumnComboChart\",\"objects\":{},\"orderBy\":[{\"Direction\":1,\"Expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Calendar\"}},\"Property\":\"Year\"}}},{\"Direction\":1,\"Expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Calendar\"}},\"Property\":\"Month\"}}}],\"activeProjections\":{\"Category\":[{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Calendar\"}},\"Property\":\"Year\"}},{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Calendar\"}},\"Property\":\"Month\"}}]}}},\"4b18758374093c2e23fb\":{\"filters\":{\"byExpr\":[{\"type\":\"Categorical\",\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Calendar\"}},\"Property\":\"Month\"}},\"howCreated\":0},{\"type\":\"Categorical\",\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Store\"}},\"Property\":\"Store\"}},\"howCreated\":0},{\"type\":\"Advanced\",\"expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Sales\"}},\"Property\":\"Sales Amount\"}},\"howCreated\":0},{\"type\":\"Advanced\",\"expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Sales\"}},\"Property\":\"# Customers (with Sales)\"}},\"howCreated\":0}]},\"singleVisual\":{\"visualType\":\"scatterChart\",\"objects\":{},\"orderBy\":[{\"Direction\":2,\"Expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Store\"}},\"Property\":\"Store\"}}}],\"activeProjections\":{\"X\":[{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Calendar\"}},\"Property\":\"Month\"}}]}}},\"5acb1caf298449a8acb4\":{\"filters\":{\"byExpr\":[{\"type\":\"Advanced\",\"expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Sales\"}},\"Property\":\"Sales Amount\"}},\"howCreated\":0},{\"type\":\"Advanced\",\"expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Sales\"}},\"Property\":\"Sales Amount (LY)\"}},\"howCreated\":0},{\"type\":\"Categorical\",\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Product\"}},\"Property\":\"Category\"}},\"howCreated\":0},{\"type\":\"Categorical\",\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Product\"}},\"Property\":\"Subcategory\"}},\"howCreated\":0},{\"type\":\"Categorical\",\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Product\"}},\"Property\":\"Product\"}},\"howCreated\":0}]},\"singleVisual\":{\"visualType\":\"clusteredBarChart\",\"objects\":{},\"orderBy\":[{\"Direction\":2,\"Expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Sales\"}},\"Property\":\"Sales Amount\"}}}],\"activeProjections\":{\"Category\":[{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Product\"}},\"Property\":\"Category\"}}]}}},\"eb5c360e357e8b54eb88\":{\"singleVisual\":{\"visualType\":\"textbox\",\"objects\":{}}},\"3641d5ce63678e76a370\":{\"filters\":{\"byExpr\":[{\"name\":\"Filterf1ec25bb722d30be4755\",\"type\":\"Categorical\",\"filter\":{\"Version\":2,\"From\":[{\"Name\":\"a\",\"Entity\":\"About\",\"Type\":0}],\"Where\":[{\"Condition\":{\"In\":{\"Expressions\":[{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"a\"}},\"Property\":\"Key\"}}],\"Values\":[[{\"Literal\":{\"Value\":\"'Last Refresh'\"}}]]}}}]},\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"About\"}},\"Property\":\"Key\"}},\"howCreated\":1},{\"type\":\"Advanced\",\"expression\":{\"Aggregation\":{\"Expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"About\"}},\"Property\":\"Value\"}},\"Function\":3}},\"howCreated\":0}]},\"singleVisual\":{\"visualType\":\"card\",\"objects\":{}}},\"624827fbb3e3bd2b52a0\":{\"filters\":{\"byExpr\":[{\"type\":\"Advanced\",\"expression\":{\"Aggregation\":{\"Expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Sales\"}},\"Property\":\"Environment\"}},\"Function\":3}},\"howCreated\":0}]},\"singleVisual\":{\"visualType\":\"card\",\"objects\":{}}}}}},\"objects\":{\"merge\":{\"outspacePane\":[{\"properties\":{\"expanded\":{\"expr\":{\"Literal\":{\"Value\":\"true\"}}},\"visible\":{\"expr\":{\"Literal\":{\"Value\":\"true\"}}}}}]}}},\"options\":{\"targetVisualNames\":[\"5acb1caf298449a8acb4\"]}}],\"defaultDrillFilterOtherVisuals\":true,\"slowDataSourceSettings\":{\"isCrossHighlightingDisabled\":false,\"isSlicerSelectionsButtonEnabled\":false,\"isFilterSelectionsButtonEnabled\":false,\"isFieldWellButtonEnabled\":false,\"isApplyAllButtonEnabled\":false},\"linguisticSchemaSyncVersion\":2,\"settings\":{\"isPersistentUserStateDisabled\":false,\"hideVisualContainerHeader\":false,\"useStylableVisualContainerHeader\":true,\"exportDataMode\":0,\"useNewFilterPaneExperience\":true,\"optOutNewFilterPaneExperience\":false,\"defaultFilterActionIsDataFilter\":true,\"useCrossReportDrillthrough\":false,\"allowChangeFilterTypes\":true,\"allowInlineExploration\":true,\"disableFilterPaneSearch\":false,\"allowDataPointLassoSelect\":true},\"objects\":{\"outspacePane\":[{\"properties\":{\"expanded\":{\"expr\":{\"Literal\":{\"Value\":\"true\"}}},\"visible\":{\"expr\":{\"Literal\":{\"Value\":\"true\"}}}}}]}}", - "filters": "[]", - "layoutOptimization": 1, - "pods": [ - { - "boundSection": "ReportSectiond90903559771e58905a1", - "config": "{}", - "name": "Pod", - "parameters": "[{\"name\":\"Param_Filterf99428c5489cc4e6747a\",\"boundFilter\":\"Filterf99428c5489cc4e6747a\",\"fieldExpr\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Calendar\"}},\"Property\":\"Year\"}},\"isLegacySingleSelection\":true}]", - "type": 1 - }, - { - "boundSection": "ReportSection89a9619c7025093ade1c", - "config": "{}", - "name": "Pod6", - "parameters": "[{\"name\":\"Param_Filterf99428c5489cc4e6747a\",\"boundFilter\":\"Filterf99428c5489cc4e6747a\",\"fieldExpr\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Calendar\"}},\"Property\":\"Year\"}},\"isLegacySingleSelection\":true}]", - "type": 1 - } - ], - "publicCustomVisuals": [ - "FilterByList507A2DBEC31244C8AAABDE9BA541F723" - ], - "resourcePackages": [ - { - "resourcePackage": { - "disabled": false, - "items": [ - { - "name": "CY23SU04", - "path": "BaseThemes/CY23SU04.json", - "type": 202 - } - ], - "name": "SharedResources", - "type": 2 - } - }, - { - "resourcePackage": { - "disabled": false, - "items": [ - { - "name": "Light4437032645752863.json", - "path": "Light4437032645752863.json", - "type": 201 - }, - { - "name": "_7abfc6c7-1a23-4b5f-bd8b-8dc472366284171093267.jpg", - "path": "_7abfc6c7-1a23-4b5f-bd8b-8dc472366284171093267.jpg", - "type": 100 - } - ], - "name": "RegisteredResources", - "type": 1 - } - } - ], - "sections": [ - { - "config": "{\"visibility\":1}", - "displayName": "Sales Detail", - "displayOption": 1, - "filters": "[]", - "height": 720.00, - "name": "ReportSection61481e08c8c340011ce0", - "ordinal": 3, - "visualContainers": [ - { - "config": "{\"name\":\"3852e5607b224b8ebd1a\",\"layouts\":[{\"id\":0,\"position\":{\"x\":292.52156694070004,\"y\":90.4157570543982,\"z\":4000,\"width\":958.0081317307926,\"height\":611.6360036032819,\"tabOrder\":4000}}],\"singleVisual\":{\"visualType\":\"tableEx\",\"projections\":{\"Values\":[{\"queryRef\":\"Calendar.Year\"},{\"queryRef\":\"Product.Product\"},{\"queryRef\":\"Customer.Customer\"},{\"queryRef\":\"Sales.Margin\"},{\"queryRef\":\"Sales.Sales Qty\"},{\"queryRef\":\"Product.Product Code\"}]},\"prototypeQuery\":{\"Version\":2,\"From\":[{\"Name\":\"c\",\"Entity\":\"Calendar\",\"Type\":0},{\"Name\":\"p\",\"Entity\":\"Parameter - Dimension\",\"Type\":0},{\"Name\":\"p1\",\"Entity\":\"Product\",\"Type\":0},{\"Name\":\"c1\",\"Entity\":\"Customer\",\"Type\":0},{\"Name\":\"s\",\"Entity\":\"Sales\",\"Type\":0}],\"Select\":[{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"c\"}},\"Property\":\"Year\"},\"Name\":\"Calendar.Year\",\"NativeReferenceName\":\"Year\"},{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"p1\"}},\"Property\":\"Product\"},\"Name\":\"Product.Product\",\"NativeReferenceName\":\"Product\"},{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"c1\"}},\"Property\":\"Customer\"},\"Name\":\"Customer.Customer\",\"NativeReferenceName\":\"Customer\"},{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Source\":\"s\"}},\"Property\":\"Margin\"},\"Name\":\"Sales.Margin\",\"NativeReferenceName\":\"Margin\"},{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Source\":\"s\"}},\"Property\":\"Sales Qty\"},\"Name\":\"Sales.Sales Qty\",\"NativeReferenceName\":\"Sales Qty\"},{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"p1\"}},\"Property\":\"Product Code\"},\"Name\":\"Product.Product Code\",\"NativeReferenceName\":\"Product Code\"}],\"OrderBy\":[{\"Direction\":1,\"Expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"p\"}},\"Property\":\"Parameter - Dimension\"}}}]},\"queryFieldParametersByRole\":{\"Values\":[{\"index\":1,\"length\":2,\"expr\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Parameter - Dimension\"}},\"Property\":\"Parameter - Dimension\"}}},{\"index\":2,\"length\":2,\"expr\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Parameter - Measure\"}},\"Property\":\"Parameter - Measure\"}}}]},\"columnProperties\":{\"Product.Product\":{\"displayName\":\"Product\"},\"Customer.Customer\":{\"displayName\":\"Customer\"},\"Sales.Margin\":{\"displayName\":\"Margin\"},\"Sales.Sales Qty\":{\"displayName\":\"Sales Qty\"}},\"drillFilterOtherVisuals\":true}}", - "filters": "[]", - "height": 611.64, - "width": 958.01, - "x": 292.52, - "y": 90.42, - "z": 4000.00 - }, - { - "config": "{\"name\":\"5019b5b630a0b1096507\",\"layouts\":[{\"id\":0,\"position\":{\"x\":32.137339055793994,\"y\":6.180257510729613,\"z\":1000,\"width\":65.5107296137339,\"height\":65.5107296137339,\"tabOrder\":1000}}],\"singleVisual\":{\"visualType\":\"image\",\"drillFilterOtherVisuals\":true,\"objects\":{\"general\":[{\"properties\":{\"imageUrl\":{\"expr\":{\"ResourcePackageItem\":{\"PackageName\":\"RegisteredResources\",\"PackageType\":1,\"ItemName\":\"_7abfc6c7-1a23-4b5f-bd8b-8dc472366284171093267.jpg\"}}}}}]}}}", - "filters": "[]", - "height": 65.51, - "width": 65.51, - "x": 32.14, - "y": 6.18, - "z": 1000.00 - }, - { - "config": "{\"name\":\"7df3763f63115a096029\",\"layouts\":[{\"id\":0,\"position\":{\"x\":106.30042918454936,\"y\":8.034334763948499,\"z\":0,\"width\":585.2703862660944,\"height\":62.4206008583691,\"tabOrder\":0}}],\"singleVisual\":{\"visualType\":\"textbox\",\"drillFilterOtherVisuals\":true,\"objects\":{\"general\":[{\"properties\":{\"paragraphs\":[{\"textRuns\":[{\"value\":\"MyCompany - \",\"textStyle\":{\"fontFamily\":\"Segoe (Bold)\",\"fontSize\":\"24pt\",\"color\":\"#000000\"}},{\"value\":\"Sales Detail\",\"textStyle\":{\"fontWeight\":\"bold\",\"fontFamily\":\"Segoe (Bold)\",\"fontSize\":\"24pt\",\"color\":\"#000000\"}}]}]}}]},\"vcObjects\":{}}}", - "filters": "[]", - "height": 62.42, - "width": 585.27, - "x": 106.30, - "y": 8.03, - "z": 0.00 - }, - { - "config": "{\"name\":\"8c05ec5365e4a086b40d\",\"layouts\":[{\"id\":0,\"position\":{\"x\":19.776824034334762,\"y\":254.0085836909871,\"z\":3000,\"width\":216.30901287553647,\"height\":235.46781115879827,\"tabOrder\":2000}}],\"singleVisual\":{\"visualType\":\"slicer\",\"projections\":{\"Values\":[{\"queryRef\":\"Parameter - Measure.Parameter - Measure\",\"active\":true}]},\"prototypeQuery\":{\"Version\":2,\"From\":[{\"Name\":\"p\",\"Entity\":\"Parameter - Measure\",\"Type\":0}],\"Select\":[{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"p\"}},\"Property\":\"Parameter - Measure\"},\"Name\":\"Parameter - Measure.Parameter - Measure\",\"NativeReferenceName\":\"Measure\"}]},\"columnProperties\":{\"Parameter - Measure.Parameter - Measure\":{\"displayName\":\"Measure\"}},\"drillFilterOtherVisuals\":true,\"objects\":{\"general\":[{\"properties\":{\"filter\":{\"filter\":{\"Version\":2,\"From\":[{\"Name\":\"p\",\"Entity\":\"Parameter - Measure\",\"Type\":0}],\"Where\":[{\"Condition\":{\"In\":{\"Expressions\":[{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"p\"}},\"Property\":\"Parameter - Measure Fields\"}}],\"Values\":[[{\"Literal\":{\"Value\":\"'''Sales''[Margin]'\"}}],[{\"Literal\":{\"Value\":\"'''Sales''[Sales Qty]'\"}}]]}}}]}}}}],\"selection\":[{\"properties\":{\"singleSelect\":{\"expr\":{\"Literal\":{\"Value\":\"false\"}}}}}]},\"cachedFilterDisplayItems\":[{\"id\":{\"scopeId\":{\"Comparison\":{\"ComparisonKind\":0,\"Left\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Parameter - Measure\"}},\"Property\":\"Parameter - Measure Fields\"}},\"Right\":{\"Literal\":{\"Value\":\"'''Sales''[Margin]'\"}}}}},\"displayName\":\"Margin\"},{\"id\":{\"scopeId\":{\"Comparison\":{\"ComparisonKind\":0,\"Left\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Parameter - Measure\"}},\"Property\":\"Parameter - Measure Fields\"}},\"Right\":{\"Literal\":{\"Value\":\"'''Sales''[Sales Qty]'\"}}}}},\"displayName\":\"Sales Qty\"}]}}", - "filters": "[]", - "height": 235.47, - "width": 216.31, - "x": 19.78, - "y": 254.01, - "z": 3000.00 - }, - { - "config": "{\"name\":\"e04b35ccb498a1d0a1ba\",\"layouts\":[{\"id\":0,\"position\":{\"x\":20,\"y\":489,\"width\":216,\"height\":213,\"z\":5000,\"tabOrder\":5000}}],\"singleVisual\":{\"visualType\":\"FilterByList507A2DBEC31244C8AAABDE9BA541F723\",\"projections\":{\"category\":[{\"queryRef\":\"Product.Product Code\"}]},\"prototypeQuery\":{\"Version\":2,\"From\":[{\"Name\":\"p\",\"Entity\":\"Product\",\"Type\":0}],\"Select\":[{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"p\"}},\"Property\":\"Product Code\"},\"Name\":\"Product.Product Code\",\"NativeReferenceName\":\"Product Code\"}]},\"drillFilterOtherVisuals\":true,\"objects\":{\"general\":[{\"properties\":{}}]}}}", - "filters": "[]", - "height": 213.00, - "width": 216.00, - "x": 20.00, - "y": 489.00, - "z": 5000.00 - }, - { - "config": "{\"name\":\"e1795a417ee6a1b4bbb6\",\"layouts\":[{\"id\":0,\"position\":{\"x\":20.394849785407725,\"y\":90.23175965665236,\"z\":2000,\"width\":216.30901287553647,\"height\":147.09012875536482,\"tabOrder\":3000}}],\"singleVisual\":{\"visualType\":\"slicer\",\"projections\":{\"Values\":[{\"queryRef\":\"Parameter - Dimension.Parameter - Dimension\",\"active\":true}]},\"prototypeQuery\":{\"Version\":2,\"From\":[{\"Name\":\"p\",\"Entity\":\"Parameter - Dimension\",\"Type\":0}],\"Select\":[{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"p\"}},\"Property\":\"Parameter - Dimension\"},\"Name\":\"Parameter - Dimension.Parameter - Dimension\",\"NativeReferenceName\":\"Dimension\"}],\"OrderBy\":[{\"Direction\":1,\"Expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"p\"}},\"Property\":\"Parameter - Dimension\"}}}]},\"columnProperties\":{\"Parameter - Dimension.Parameter - Dimension\":{\"displayName\":\"Dimension\"}},\"drillFilterOtherVisuals\":true,\"objects\":{\"data\":[{\"properties\":{\"mode\":{\"expr\":{\"Literal\":{\"Value\":\"'Basic'\"}}}}}],\"general\":[{\"properties\":{\"orientation\":{\"expr\":{\"Literal\":{\"Value\":\"0D\"}}},\"filter\":{\"filter\":{\"Version\":2,\"From\":[{\"Name\":\"p\",\"Entity\":\"Parameter - Dimension\",\"Type\":0}],\"Where\":[{\"Condition\":{\"In\":{\"Expressions\":[{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"p\"}},\"Property\":\"Parameter - Dimension Fields\"}}],\"Values\":[[{\"Literal\":{\"Value\":\"'''Product''[Product]'\"}}],[{\"Literal\":{\"Value\":\"'''Customer''[Customer]'\"}}]]}}}]}}}}],\"selection\":[{\"properties\":{\"singleSelect\":{\"expr\":{\"Literal\":{\"Value\":\"false\"}}}}}]},\"cachedFilterDisplayItems\":[{\"id\":{\"scopeId\":{\"Comparison\":{\"ComparisonKind\":0,\"Left\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Parameter - Dimension\"}},\"Property\":\"Parameter - Dimension Fields\"}},\"Right\":{\"Literal\":{\"Value\":\"'''Product''[Product]'\"}}}}},\"displayName\":\"Product\"},{\"id\":{\"scopeId\":{\"Comparison\":{\"ComparisonKind\":0,\"Left\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Parameter - Dimension\"}},\"Property\":\"Parameter - Dimension Fields\"}},\"Right\":{\"Literal\":{\"Value\":\"'''Customer''[Customer]'\"}}}}},\"displayName\":\"Customer\"}]}}", - "filters": "[]", - "height": 147.09, - "width": 216.31, - "x": 20.39, - "y": 90.23, - "z": 2000.00 - } - ], - "width": 1280.00 - }, - { - "config": "{}", - "displayName": "KPI", - "displayOption": 1, - "filters": "[]", - "height": 720.00, - "name": "ReportSection6687c48ea8a9b7740002", - "ordinal": 4, - "visualContainers": [ - { - "config": "{\"name\":\"4ebf2d5595cb5ae43396\",\"layouts\":[{\"id\":0,\"position\":{\"x\":38.80781089414183,\"y\":98.6639260020555,\"z\":0,\"width\":1205.6731757451182,\"height\":279.54779033915725,\"tabOrder\":0}}],\"singleVisual\":{\"visualType\":\"pivotTable\",\"projections\":{\"Rows\":[{\"queryRef\":\"Product.Category\",\"active\":true}],\"Values\":[{\"queryRef\":\"Sales.Margin %\"},{\"queryRef\":\"Sales._Margin Status\"}],\"Columns\":[{\"queryRef\":\"Calendar.Year\",\"active\":true}]},\"prototypeQuery\":{\"Version\":2,\"From\":[{\"Name\":\"p\",\"Entity\":\"Product\",\"Type\":0},{\"Name\":\"s\",\"Entity\":\"Sales\",\"Type\":0},{\"Name\":\"c\",\"Entity\":\"Calendar\",\"Type\":0}],\"Select\":[{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"p\"}},\"Property\":\"Category\"},\"Name\":\"Product.Category\",\"NativeReferenceName\":\"Category\"},{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Source\":\"s\"}},\"Property\":\"Margin %\"},\"Name\":\"Sales.Margin %\",\"NativeReferenceName\":\"Margin %\"},{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"c\"}},\"Property\":\"Year\"},\"Name\":\"Calendar.Year\",\"NativeReferenceName\":\"Year\"},{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Source\":\"s\"}},\"Property\":\"_Margin Status\"},\"Name\":\"Sales._Margin Status\",\"NativeReferenceName\":\"Margin Status\"}]},\"autoSelectVisualType\":false,\"drillFilterOtherVisuals\":true,\"objects\":{\"values\":[{\"properties\":{\"icon\":{\"kind\":\"Icon\",\"layout\":{\"expr\":{\"Literal\":{\"Value\":\"'IconOnly'\"}}},\"verticalAlignment\":{\"expr\":{\"Literal\":{\"Value\":\"'Top'\"}}},\"value\":{\"expr\":{\"Conditional\":{\"Cases\":[{\"Condition\":{\"Comparison\":{\"ComparisonKind\":2,\"Left\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Sales\"}},\"Property\":\"_Margin Status\"}},\"Right\":{\"RangePercent\":{\"Min\":{\"ScopedEval\":{\"Expression\":{\"Aggregation\":{\"Expression\":{\"ScopedEval\":{\"Expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Sales\"}},\"Property\":\"_Margin Status\"}},\"Scope\":[{\"AllRolesRef\":{}}]}},\"Function\":3}},\"Scope\":[]}},\"Max\":{\"ScopedEval\":{\"Expression\":{\"Aggregation\":{\"Expression\":{\"ScopedEval\":{\"Expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Sales\"}},\"Property\":\"_Margin Status\"}},\"Scope\":[{\"AllRolesRef\":{}}]}},\"Function\":4}},\"Scope\":[]}},\"Percent\":0.67}}}},\"Value\":{\"Literal\":{\"Value\":\"'CircleHigh'\"}}},{\"Condition\":{\"And\":{\"Left\":{\"Comparison\":{\"ComparisonKind\":2,\"Left\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Sales\"}},\"Property\":\"_Margin Status\"}},\"Right\":{\"RangePercent\":{\"Min\":{\"ScopedEval\":{\"Expression\":{\"Aggregation\":{\"Expression\":{\"ScopedEval\":{\"Expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Sales\"}},\"Property\":\"_Margin Status\"}},\"Scope\":[{\"AllRolesRef\":{}}]}},\"Function\":3}},\"Scope\":[]}},\"Max\":{\"ScopedEval\":{\"Expression\":{\"Aggregation\":{\"Expression\":{\"ScopedEval\":{\"Expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Sales\"}},\"Property\":\"_Margin Status\"}},\"Scope\":[{\"AllRolesRef\":{}}]}},\"Function\":4}},\"Scope\":[]}},\"Percent\":0.33}}}},\"Right\":{\"Comparison\":{\"ComparisonKind\":3,\"Left\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Sales\"}},\"Property\":\"_Margin Status\"}},\"Right\":{\"RangePercent\":{\"Min\":{\"ScopedEval\":{\"Expression\":{\"Aggregation\":{\"Expression\":{\"ScopedEval\":{\"Expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Sales\"}},\"Property\":\"_Margin Status\"}},\"Scope\":[{\"AllRolesRef\":{}}]}},\"Function\":3}},\"Scope\":[]}},\"Max\":{\"ScopedEval\":{\"Expression\":{\"Aggregation\":{\"Expression\":{\"ScopedEval\":{\"Expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Sales\"}},\"Property\":\"_Margin Status\"}},\"Scope\":[{\"AllRolesRef\":{}}]}},\"Function\":4}},\"Scope\":[]}},\"Percent\":0.67}}}}}},\"Value\":{\"Literal\":{\"Value\":\"'SignMedium'\"}}},{\"Condition\":{\"Comparison\":{\"ComparisonKind\":3,\"Left\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Sales\"}},\"Property\":\"_Margin Status\"}},\"Right\":{\"RangePercent\":{\"Min\":{\"ScopedEval\":{\"Expression\":{\"Aggregation\":{\"Expression\":{\"ScopedEval\":{\"Expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Sales\"}},\"Property\":\"_Margin Status\"}},\"Scope\":[{\"AllRolesRef\":{}}]}},\"Function\":3}},\"Scope\":[]}},\"Max\":{\"ScopedEval\":{\"Expression\":{\"Aggregation\":{\"Expression\":{\"ScopedEval\":{\"Expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Sales\"}},\"Property\":\"_Margin Status\"}},\"Scope\":[{\"AllRolesRef\":{}}]}},\"Function\":4}},\"Scope\":[]}},\"Percent\":0.33}}}},\"Value\":{\"Literal\":{\"Value\":\"'SignLow'\"}}}]}}}}},\"selector\":{\"data\":[{\"dataViewWildcard\":{\"matchingOption\":1}}],\"metadata\":\"Sales._Margin Status\"}}]}}}", - "filters": "[]", - "height": 279.55, - "width": 1205.67, - "x": 38.81, - "y": 98.66, - "z": 0.00 - }, - { - "config": "{\"name\":\"54d5a6fb015310d05ab1\",\"layouts\":[{\"id\":0,\"position\":{\"x\":32.137339055793994,\"y\":6.180257510729613,\"z\":2000,\"width\":65.5107296137339,\"height\":65.5107296137339,\"tabOrder\":2000}}],\"singleVisual\":{\"visualType\":\"image\",\"drillFilterOtherVisuals\":true,\"objects\":{\"general\":[{\"properties\":{\"imageUrl\":{\"expr\":{\"ResourcePackageItem\":{\"PackageName\":\"RegisteredResources\",\"PackageType\":1,\"ItemName\":\"_7abfc6c7-1a23-4b5f-bd8b-8dc472366284171093267.jpg\"}}}}}]}}}", - "filters": "[]", - "height": 65.51, - "width": 65.51, - "x": 32.14, - "y": 6.18, - "z": 2000.00 - }, - { - "config": "{\"name\":\"92658440a6c902ba3d82\",\"layouts\":[{\"id\":0,\"position\":{\"x\":106.30042918454936,\"y\":8.034334763948499,\"z\":1000,\"width\":585.2703862660944,\"height\":62.4206008583691,\"tabOrder\":1000}}],\"singleVisual\":{\"visualType\":\"textbox\",\"drillFilterOtherVisuals\":true,\"objects\":{\"general\":[{\"properties\":{\"paragraphs\":[{\"textRuns\":[{\"value\":\"MyCompany - \",\"textStyle\":{\"fontFamily\":\"Segoe (Bold)\",\"fontSize\":\"24pt\",\"color\":\"#000000\"}},{\"value\":\"Margin KPI\",\"textStyle\":{\"fontWeight\":\"bold\",\"fontFamily\":\"Segoe (Bold)\",\"fontSize\":\"24pt\",\"color\":\"#000000\"}}]}]}}]},\"vcObjects\":{}}}", - "filters": "[]", - "height": 62.42, - "width": 585.27, - "x": 106.30, - "y": 8.03, - "z": 1000.00 - } - ], - "width": 1280.00 - }, - { - "config": "{\"objects\":{\"outspacePane\":[{\"properties\":{\"width\":{\"expr\":{\"Literal\":{\"Value\":\"303L\"}}}}}]},\"filterSortOrder\":3}", - "displayName": "Sales", - "displayOption": 1, - "filters": "[{\"name\":\"Filterf99428c5489cc4e6747a\",\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Calendar\"}},\"Property\":\"Year\"}},\"type\":\"Categorical\",\"howCreated\":1,\"objects\":{\"general\":[{\"properties\":{\"requireSingleSelect\":{\"expr\":{\"Literal\":{\"Value\":\"true\"}}}}}]},\"ordinal\":0},{\"name\":\"Filter78bda1908a0bd749d86a\",\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Product\"}},\"Property\":\"Category\"}},\"type\":\"Categorical\",\"howCreated\":1,\"objects\":{},\"ordinal\":1},{\"name\":\"Filter9ce4dd28c25b24ab1603\",\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Product\"}},\"Property\":\"Subcategory\"}},\"type\":\"Categorical\",\"howCreated\":1,\"ordinal\":2},{\"name\":\"Filterfa59dace34a0d2b060c1\",\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Store\"}},\"Property\":\"Store\"}},\"type\":\"Categorical\",\"howCreated\":1,\"ordinal\":3}]", - "height": 720.00, - "name": "ReportSection89a9619c7025093ade1c", - "visualContainers": [ - { - "config": "{\"name\":\"0f9bcdc145fc9e6fdc33\",\"layouts\":[{\"id\":0,\"position\":{\"x\":256.1310133060389,\"y\":80.57318321392016,\"z\":1000,\"width\":224.03275332650972,\"height\":111.36131013306039,\"tabOrder\":1000}},{\"id\":1,\"position\":{\"x\":0,\"y\":240,\"width\":324,\"height\":120}}],\"singleVisual\":{\"visualType\":\"card\",\"projections\":{\"Values\":[{\"queryRef\":\"Sales.Margin\"}]},\"prototypeQuery\":{\"Version\":2,\"From\":[{\"Name\":\"s\",\"Entity\":\"Sales\",\"Type\":0}],\"Select\":[{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Source\":\"s\"}},\"Property\":\"Margin\"},\"Name\":\"Sales.Margin\"}],\"OrderBy\":[{\"Direction\":2,\"Expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Source\":\"s\"}},\"Property\":\"Margin\"}}}]},\"drillFilterOtherVisuals\":true,\"hasDefaultSort\":true,\"objects\":{\"labels\":[{\"properties\":{\"color\":{\"solid\":{\"color\":{\"expr\":{\"ThemeDataColor\":{\"ColorId\":0,\"Percent\":0}}}}},\"fontSize\":{\"expr\":{\"Literal\":{\"Value\":\"16D\"}}}}}],\"categoryLabels\":[{\"properties\":{\"color\":{\"solid\":{\"color\":{\"expr\":{\"ThemeDataColor\":{\"ColorId\":0,\"Percent\":0}}}}}}}]},\"vcObjects\":{\"background\":[{\"properties\":{\"show\":{\"expr\":{\"Literal\":{\"Value\":\"true\"}}},\"color\":{\"solid\":{\"color\":{\"expr\":{\"ThemeDataColor\":{\"ColorId\":3,\"Percent\":0}}}}},\"transparency\":{\"expr\":{\"Literal\":{\"Value\":\"0D\"}}}}}],\"border\":[{\"properties\":{\"show\":{\"expr\":{\"Literal\":{\"Value\":\"false\"}}}}}]}}}", - "filters": "[{\"name\":\"Filter2a1ae8311dd5bd80e06c\",\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Smart Calcs\"}},\"Property\":\"Smart Calc\"}},\"filter\":{\"Version\":2,\"From\":[{\"Name\":\"s\",\"Entity\":\"Smart Calcs\",\"Type\":0}],\"Where\":[{\"Condition\":{\"In\":{\"Expressions\":[{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"s\"}},\"Property\":\"Smart Calc\"}}],\"Values\":[[{\"Literal\":{\"Value\":\"'Label - ▲ LY'\"}}]]}}}]},\"type\":\"Categorical\",\"howCreated\":1,\"objects\":{}}]", - "height": 111.36, - "width": 224.03, - "x": 256.13, - "y": 80.57, - "z": 1000.00 - }, - { - "config": "{\"name\":\"223d9300700274da67c8\",\"layouts\":[{\"id\":0,\"position\":{\"x\":32.137339055793994,\"y\":6.180257510729613,\"z\":13000,\"width\":65.5107296137339,\"height\":65.5107296137339,\"tabOrder\":13000}}],\"singleVisual\":{\"visualType\":\"image\",\"drillFilterOtherVisuals\":true,\"objects\":{\"general\":[{\"properties\":{\"imageUrl\":{\"expr\":{\"ResourcePackageItem\":{\"PackageName\":\"RegisteredResources\",\"PackageType\":1,\"ItemName\":\"_7abfc6c7-1a23-4b5f-bd8b-8dc472366284171093267.jpg\"}}}}}]}}}", - "filters": "[]", - "height": 65.51, - "width": 65.51, - "x": 32.14, - "y": 6.18, - "z": 13000.00 - }, - { - "config": "{\"name\":\"32b08d7f32cb5f9816cc\",\"layouts\":[{\"id\":0,\"position\":{\"x\":490.7838242636046,\"y\":207.6884672990514,\"z\":5000,\"width\":313.1303045431852,\"height\":272.23165252121817,\"tabOrder\":5000}},{\"id\":1,\"position\":{\"x\":0,\"y\":1860,\"width\":324,\"height\":420}}],\"singleVisual\":{\"visualType\":\"barChart\",\"projections\":{\"Y\":[{\"queryRef\":\"Measure Table.Sales Amount\"}],\"Category\":[{\"queryRef\":\"Product.Brand\",\"active\":true},{\"queryRef\":\"Product.Product\"}],\"Series\":[{\"queryRef\":\"Customer.Gender\"}]},\"prototypeQuery\":{\"Version\":2,\"From\":[{\"Name\":\"s\",\"Entity\":\"Sales\",\"Type\":0},{\"Name\":\"p\",\"Entity\":\"Product\",\"Type\":0},{\"Name\":\"c\",\"Entity\":\"Customer\",\"Type\":0}],\"Select\":[{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Source\":\"s\"}},\"Property\":\"Sales Amount\"},\"Name\":\"Measure Table.Sales Amount\"},{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"p\"}},\"Property\":\"Product\"},\"Name\":\"Product.Product\"},{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"p\"}},\"Property\":\"Brand\"},\"Name\":\"Product.Brand\"},{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"c\"}},\"Property\":\"Gender\"},\"Name\":\"Customer.Gender\"}],\"OrderBy\":[{\"Direction\":2,\"Expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Source\":\"s\"}},\"Property\":\"Sales Amount\"}}}]},\"drillFilterOtherVisuals\":true,\"objects\":{},\"vcObjects\":{}}}", - "filters": "[{\"name\":\"Filter\",\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Calendar\"}},\"Property\":\"Year\"}},\"type\":\"Categorical\",\"howCreated\":1}]", - "height": 272.23, - "width": 313.13, - "x": 490.78, - "y": 207.69, - "z": 5000.00 - }, - { - "config": "{\"name\":\"3641d5ce63678e76a370\",\"layouts\":[{\"id\":0,\"position\":{\"x\":980.6184987787653,\"y\":3.8355351973093823,\"z\":11000,\"width\":299.17174539013183,\"height\":70.31814528400534,\"tabOrder\":11000}}],\"singleVisual\":{\"visualType\":\"card\",\"projections\":{\"Values\":[{\"queryRef\":\"Min(About.Value)\"}]},\"prototypeQuery\":{\"Version\":2,\"From\":[{\"Name\":\"a\",\"Entity\":\"About\",\"Type\":0}],\"Select\":[{\"Aggregation\":{\"Expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"a\"}},\"Property\":\"Value\"}},\"Function\":3},\"Name\":\"Min(About.Value)\"}]},\"drillFilterOtherVisuals\":true,\"objects\":{\"labels\":[{\"properties\":{\"fontSize\":{\"expr\":{\"Literal\":{\"Value\":\"20D\"}}}}}],\"categoryLabels\":[{\"properties\":{\"show\":{\"expr\":{\"Literal\":{\"Value\":\"false\"}}}}}]},\"vcObjects\":{}}}", - "filters": "[{\"name\":\"Filterf1ec25bb722d30be4755\",\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"About\"}},\"Property\":\"Key\"}},\"filter\":{\"Version\":2,\"From\":[{\"Name\":\"a\",\"Entity\":\"About\",\"Type\":0}],\"Where\":[{\"Condition\":{\"In\":{\"Expressions\":[{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"a\"}},\"Property\":\"Key\"}}],\"Values\":[[{\"Literal\":{\"Value\":\"'Last Refresh'\"}}]]}}}]},\"type\":\"Categorical\",\"howCreated\":1,\"objects\":{}}]", - "height": 70.32, - "width": 299.17, - "x": 980.62, - "y": 3.84, - "z": 11000.00 - }, - { - "config": "{\"name\":\"484fbdd73143c5bf71fa\",\"layouts\":[{\"id\":0,\"position\":{\"x\":32.31031733102402,\"y\":495.1907330080855,\"z\":6000,\"width\":1230.601651390306,\"height\":213.52905366589786,\"tabOrder\":6000}},{\"id\":1,\"position\":{\"x\":0,\"y\":1200,\"width\":324,\"height\":240}}],\"singleVisual\":{\"visualType\":\"lineClusteredColumnComboChart\",\"projections\":{\"Y\":[{\"queryRef\":\"Sales.Sales Amount\"},{\"queryRef\":\"Sales.Sales Amount (LY)\"},{\"queryRef\":\"Sales.Margin\"}],\"Y2\":[{\"queryRef\":\"Sales.Sales Amount (12M average)\"}],\"Category\":[{\"queryRef\":\"Calendar.Date (Year-Month)\",\"active\":true}]},\"prototypeQuery\":{\"Version\":2,\"From\":[{\"Name\":\"s\",\"Entity\":\"Sales\",\"Type\":0},{\"Name\":\"c\",\"Entity\":\"Calendar\",\"Type\":0}],\"Select\":[{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Source\":\"s\"}},\"Property\":\"Sales Amount\"},\"Name\":\"Sales.Sales Amount\"},{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Source\":\"s\"}},\"Property\":\"Sales Amount (LY)\"},\"Name\":\"Sales.Sales Amount (LY)\"},{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Source\":\"s\"}},\"Property\":\"Sales Amount (12M average)\"},\"Name\":\"Sales.Sales Amount (12M average)\",\"NativeReferenceName\":\"Sales Amount (12M average)\"},{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Source\":\"s\"}},\"Property\":\"Margin\"},\"Name\":\"Sales.Margin\",\"NativeReferenceName\":\"Margin\"},{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"c\"}},\"Property\":\"Date (Year-Month)\"},\"Name\":\"Calendar.Date (Year-Month)\",\"NativeReferenceName\":\"Date (Year-Month)\"}],\"OrderBy\":[{\"Direction\":1,\"Expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"c\"}},\"Property\":\"Date (Year-Month)\"}}}]},\"queryOptions\":{\"keepProjectionOrder\":true},\"drillFilterOtherVisuals\":true,\"hasDefaultSort\":true,\"objects\":{},\"vcObjects\":{}}}", - "filters": "[]", - "height": 213.53, - "width": 1230.60, - "x": 32.31, - "y": 495.19, - "z": 6000.00 - }, - { - "config": "{\"name\":\"4b18758374093c2e23fb\",\"layouts\":[{\"id\":0,\"position\":{\"x\":834.4490649838376,\"y\":207.90986804311106,\"z\":7000,\"width\":428.4629037374924,\"height\":272.5305027051591,\"tabOrder\":7000}}],\"singleVisual\":{\"visualType\":\"scatterChart\",\"projections\":{\"X\":[{\"queryRef\":\"Calendar.Month\",\"active\":true}],\"Series\":[{\"queryRef\":\"Store.Store\"}],\"Y\":[{\"queryRef\":\"Sales.Sales Amount\"}],\"Size\":[{\"queryRef\":\"Sales.# Customers (with Sales)\"}]},\"prototypeQuery\":{\"Version\":2,\"From\":[{\"Name\":\"c\",\"Entity\":\"Calendar\",\"Type\":0},{\"Name\":\"s1\",\"Entity\":\"Store\",\"Type\":0},{\"Name\":\"s\",\"Entity\":\"Sales\",\"Type\":0}],\"Select\":[{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"c\"}},\"Property\":\"Month\"},\"Name\":\"Calendar.Month\"},{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"s1\"}},\"Property\":\"Store\"},\"Name\":\"Store.Store\"},{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Source\":\"s\"}},\"Property\":\"Sales Amount\"},\"Name\":\"Sales.Sales Amount\"},{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Source\":\"s\"}},\"Property\":\"# Customers (with Sales)\"},\"Name\":\"Sales.# Customers (with Sales)\"}],\"OrderBy\":[{\"Direction\":2,\"Expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"s1\"}},\"Property\":\"Store\"}}}]},\"queryOptions\":{\"keepProjectionOrder\":true},\"drillFilterOtherVisuals\":true,\"objects\":{},\"vcObjects\":{\"title\":[{\"properties\":{\"text\":{\"expr\":{\"Literal\":{\"Value\":\"'Sales Amount vs # Customers'\"}}}}}]}}}", - "filters": "[]", - "height": 272.53, - "width": 428.46, - "x": 834.45, - "y": 207.91, - "z": 7000.00 - }, - { - "config": "{\"name\":\"5acb1caf298449a8acb4\",\"layouts\":[{\"id\":0,\"position\":{\"x\":31.952071892161754,\"y\":207.6884672990514,\"z\":8000,\"width\":428.1577633549675,\"height\":272.23165252121817,\"tabOrder\":8000}},{\"id\":1,\"position\":{\"x\":0,\"y\":1500,\"width\":324,\"height\":300}}],\"singleVisual\":{\"visualType\":\"clusteredBarChart\",\"projections\":{\"Y\":[{\"queryRef\":\"Measure Table.Sales Amount\"},{\"queryRef\":\"Measure Table.Sales Amount (ly)\"}],\"Category\":[{\"queryRef\":\"Product.Category\",\"active\":true},{\"queryRef\":\"Product.Subcategory\"},{\"queryRef\":\"Product.Product\"}]},\"prototypeQuery\":{\"Version\":2,\"From\":[{\"Name\":\"s\",\"Entity\":\"Sales\",\"Type\":0},{\"Name\":\"p\",\"Entity\":\"Product\",\"Type\":0}],\"Select\":[{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Source\":\"s\"}},\"Property\":\"Sales Amount\"},\"Name\":\"Measure Table.Sales Amount\"},{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Source\":\"s\"}},\"Property\":\"Sales Amount (LY)\"},\"Name\":\"Measure Table.Sales Amount (ly)\"},{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"p\"}},\"Property\":\"Category\"},\"Name\":\"Product.Category\"},{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"p\"}},\"Property\":\"Subcategory\"},\"Name\":\"Product.Subcategory\"},{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"p\"}},\"Property\":\"Product\"},\"Name\":\"Product.Product\"}],\"OrderBy\":[{\"Direction\":2,\"Expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Source\":\"s\"}},\"Property\":\"Sales Amount\"}}}]},\"drillFilterOtherVisuals\":true,\"objects\":{},\"vcObjects\":{}}}", - "filters": "[]", - "height": 272.23, - "width": 428.16, - "x": 31.95, - "y": 207.69, - "z": 8000.00 - }, - { - "config": "{\"name\":\"624827fbb3e3bd2b52a0\",\"layouts\":[{\"id\":0,\"position\":{\"x\":765.803224723949,\"y\":5.2362613656338395,\"z\":12000,\"width\":223.85017338084663,\"height\":68.0713977532399,\"tabOrder\":12000}}],\"singleVisual\":{\"visualType\":\"card\",\"projections\":{\"Values\":[{\"queryRef\":\"Min(Sales.Environment)\"}]},\"prototypeQuery\":{\"Version\":2,\"From\":[{\"Name\":\"s\",\"Entity\":\"Sales\",\"Type\":0}],\"Select\":[{\"Aggregation\":{\"Expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"s\"}},\"Property\":\"Environment\"}},\"Function\":3},\"Name\":\"Min(Sales.Environment)\"}]},\"drillFilterOtherVisuals\":true,\"objects\":{\"labels\":[{\"properties\":{\"fontSize\":{\"expr\":{\"Literal\":{\"Value\":\"20D\"}}}}}],\"categoryLabels\":[{\"properties\":{\"show\":{\"expr\":{\"Literal\":{\"Value\":\"false\"}}}}}]}}}", - "filters": "[]", - "height": 68.07, - "width": 223.85, - "x": 765.80, - "y": 5.24, - "z": 12000.00 - }, - { - "config": "{\"name\":\"8b8727ff328bdc49692c\",\"layouts\":[{\"id\":0,\"position\":{\"x\":942.7956989247311,\"y\":79.82795698924731,\"z\":4000,\"width\":304.1720430107527,\"height\":112.86021505376344,\"tabOrder\":4000}},{\"id\":1,\"position\":{\"x\":0,\"y\":900,\"width\":324,\"height\":120}}],\"singleVisual\":{\"visualType\":\"slicer\",\"projections\":{\"Values\":[{\"queryRef\":\"Calendar.Date\",\"active\":true}]},\"prototypeQuery\":{\"Version\":2,\"From\":[{\"Name\":\"c\",\"Entity\":\"Calendar\",\"Type\":0}],\"Select\":[{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"c\"}},\"Property\":\"Date\"},\"Name\":\"Calendar.Date\"}],\"OrderBy\":[{\"Direction\":1,\"Expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"c\"}},\"Property\":\"Date\"}}}]},\"drillFilterOtherVisuals\":true,\"hasDefaultSort\":true,\"objects\":{},\"vcObjects\":{}}}", - "filters": "[]", - "height": 112.86, - "width": 304.17, - "x": 942.80, - "y": 79.83, - "z": 4000.00 - }, - { - "config": "{\"name\":\"9472f90b5973fdfeb1a9\",\"layouts\":[{\"id\":0,\"position\":{\"x\":480.16376663254863,\"y\":80.57318321392016,\"z\":2000,\"width\":224.03275332650972,\"height\":111.36131013306039,\"tabOrder\":2000}},{\"id\":1,\"position\":{\"x\":0,\"y\":360,\"width\":324,\"height\":120}}],\"singleVisual\":{\"visualType\":\"card\",\"projections\":{\"Values\":[{\"queryRef\":\"Sales.Sales Amount Avg per Day\"}]},\"prototypeQuery\":{\"Version\":2,\"From\":[{\"Name\":\"s\",\"Entity\":\"Sales\",\"Type\":0}],\"Select\":[{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Source\":\"s\"}},\"Property\":\"Sales Amount Avg per Day\"},\"Name\":\"Sales.Sales Amount Avg per Day\"}],\"OrderBy\":[{\"Direction\":2,\"Expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Source\":\"s\"}},\"Property\":\"Sales Amount Avg per Day\"}}}]},\"drillFilterOtherVisuals\":true,\"hasDefaultSort\":true,\"objects\":{\"labels\":[{\"properties\":{\"color\":{\"solid\":{\"color\":{\"expr\":{\"ThemeDataColor\":{\"ColorId\":0,\"Percent\":0}}}}},\"fontSize\":{\"expr\":{\"Literal\":{\"Value\":\"16D\"}}}}}],\"categoryLabels\":[{\"properties\":{\"color\":{\"solid\":{\"color\":{\"expr\":{\"ThemeDataColor\":{\"ColorId\":0,\"Percent\":0}}}}}}}]},\"vcObjects\":{\"background\":[{\"properties\":{\"show\":{\"expr\":{\"Literal\":{\"Value\":\"true\"}}},\"color\":{\"solid\":{\"color\":{\"expr\":{\"ThemeDataColor\":{\"ColorId\":4,\"Percent\":0}}}}},\"transparency\":{\"expr\":{\"Literal\":{\"Value\":\"0D\"}}}}}],\"border\":[{\"properties\":{\"show\":{\"expr\":{\"Literal\":{\"Value\":\"false\"}}}}}]}}}", - "filters": "[{\"name\":\"Filterac4e7a5cd0c9731a470e\",\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Smart Calcs\"}},\"Property\":\"Smart Calc\"}},\"filter\":{\"Version\":2,\"From\":[{\"Name\":\"s\",\"Entity\":\"Smart Calcs\",\"Type\":0}],\"Where\":[{\"Condition\":{\"In\":{\"Expressions\":[{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"s\"}},\"Property\":\"Smart Calc\"}}],\"Values\":[[{\"Literal\":{\"Value\":\"'Label - ▲ LY'\"}}]]}}}]},\"type\":\"Categorical\",\"howCreated\":1,\"objects\":{}}]", - "height": 111.36, - "width": 224.03, - "x": 480.16, - "y": 80.57, - "z": 2000.00 - }, - { - "config": "{\"name\":\"bf50a0a6082015e4bcfe\",\"layouts\":[{\"id\":0,\"position\":{\"x\":702.8863868986693,\"y\":80.57318321392016,\"z\":3000,\"width\":224.03275332650972,\"height\":111.36131013306039,\"tabOrder\":3000}},{\"id\":1,\"position\":{\"x\":0,\"y\":480,\"width\":324,\"height\":120}}],\"singleVisual\":{\"visualType\":\"card\",\"projections\":{\"Values\":[{\"queryRef\":\"Sales.# Customers (with Sales)\"}]},\"prototypeQuery\":{\"Version\":2,\"From\":[{\"Name\":\"s\",\"Entity\":\"Sales\",\"Type\":0}],\"Select\":[{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Source\":\"s\"}},\"Property\":\"# Customers (with Sales)\"},\"Name\":\"Sales.# Customers (with Sales)\"}],\"OrderBy\":[{\"Direction\":2,\"Expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Source\":\"s\"}},\"Property\":\"# Customers (with Sales)\"}}}]},\"columnProperties\":{\"Sales.# Customers (with Sales)\":{\"displayName\":\"# Customers\"}},\"drillFilterOtherVisuals\":true,\"filterSortOrder\":3,\"hasDefaultSort\":true,\"objects\":{\"labels\":[{\"properties\":{\"color\":{\"solid\":{\"color\":{\"expr\":{\"ThemeDataColor\":{\"ColorId\":0,\"Percent\":0}}}}},\"fontSize\":{\"expr\":{\"Literal\":{\"Value\":\"16D\"}}}}}],\"categoryLabels\":[{\"properties\":{\"color\":{\"solid\":{\"color\":{\"expr\":{\"ThemeDataColor\":{\"ColorId\":0,\"Percent\":0}}}}}}}]},\"vcObjects\":{\"background\":[{\"properties\":{\"show\":{\"expr\":{\"Literal\":{\"Value\":\"true\"}}},\"color\":{\"solid\":{\"color\":{\"expr\":{\"ThemeDataColor\":{\"ColorId\":5,\"Percent\":0}}}}},\"transparency\":{\"expr\":{\"Literal\":{\"Value\":\"0D\"}}}}}],\"border\":[{\"properties\":{\"show\":{\"expr\":{\"Literal\":{\"Value\":\"false\"}}}}}]}}}", - "filters": "[{\"name\":\"Filter2bc8e5e0b6ab2126005e\",\"expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Sales\"}},\"Property\":\"Sales Qty\"}},\"type\":\"Advanced\",\"howCreated\":1,\"isHiddenInViewMode\":false,\"ordinal\":0},{\"name\":\"Filter0f21badd084a049e0663\",\"expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Sales\"}},\"Property\":\"Sales Amount Avg per Day\"}},\"type\":\"Advanced\",\"howCreated\":1,\"isHiddenInViewMode\":false,\"ordinal\":1},{\"name\":\"Filterf321968e3dc8aa5d2791\",\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Smart Calcs\"}},\"Property\":\"Smart Calc\"}},\"filter\":{\"Version\":2,\"From\":[{\"Name\":\"s\",\"Entity\":\"Smart Calcs\",\"Type\":0}],\"Where\":[{\"Condition\":{\"In\":{\"Expressions\":[{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"s\"}},\"Property\":\"Smart Calc\"}}],\"Values\":[[{\"Literal\":{\"Value\":\"'Label - ▲ LY'\"}}]]}}}]},\"type\":\"Categorical\",\"howCreated\":1,\"objects\":{\"general\":[{\"properties\":{}}]},\"ordinal\":2}]", - "height": 111.36, - "width": 224.03, - "x": 702.89, - "y": 80.57, - "z": 3000.00 - }, - { - "config": "{\"name\":\"c258740674a8a53d2c4c\",\"layouts\":[{\"id\":0,\"position\":{\"x\":32.09825997952917,\"y\":80.57318321392016,\"z\":0,\"width\":224.03275332650972,\"height\":111.36131013306039,\"tabOrder\":0}},{\"id\":1,\"position\":{\"x\":0,\"y\":120,\"width\":324,\"height\":120}}],\"singleVisual\":{\"visualType\":\"card\",\"projections\":{\"Values\":[{\"queryRef\":\"Measure Table.Sales Amount\"}]},\"prototypeQuery\":{\"Version\":2,\"From\":[{\"Name\":\"c\",\"Entity\":\"Calendar\",\"Type\":0},{\"Name\":\"s\",\"Entity\":\"Sales\",\"Type\":0}],\"Select\":[{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Source\":\"s\"}},\"Property\":\"Sales Amount\"},\"Name\":\"Measure Table.Sales Amount\"}],\"OrderBy\":[{\"Direction\":2,\"Expression\":{\"Aggregation\":{\"Expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"c\"}},\"Property\":\"Year\"}},\"Function\":2}}}]},\"drillFilterOtherVisuals\":true,\"hasDefaultSort\":true,\"objects\":{\"categoryLabels\":[{\"properties\":{\"color\":{\"solid\":{\"color\":{\"expr\":{\"ThemeDataColor\":{\"ColorId\":0,\"Percent\":0}}}}}}}],\"labels\":[{\"properties\":{\"color\":{\"solid\":{\"color\":{\"expr\":{\"ThemeDataColor\":{\"ColorId\":0,\"Percent\":0}}}}},\"fontSize\":{\"expr\":{\"Literal\":{\"Value\":\"16D\"}}}}}]},\"vcObjects\":{\"background\":[{\"properties\":{\"show\":{\"expr\":{\"Literal\":{\"Value\":\"true\"}}},\"color\":{\"solid\":{\"color\":{\"expr\":{\"ThemeDataColor\":{\"ColorId\":2,\"Percent\":0}}}}},\"transparency\":{\"expr\":{\"Literal\":{\"Value\":\"0D\"}}}}}],\"border\":[{\"properties\":{\"show\":{\"expr\":{\"Literal\":{\"Value\":\"false\"}}}}}]}}}", - "filters": "[{\"name\":\"Filter7a63ba4725d907434991\",\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Smart Calcs\"}},\"Property\":\"Smart Calc\"}},\"filter\":{\"Version\":2,\"From\":[{\"Name\":\"s\",\"Entity\":\"Smart Calcs\",\"Type\":0}],\"Where\":[{\"Condition\":{\"In\":{\"Expressions\":[{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"s\"}},\"Property\":\"Smart Calc\"}}],\"Values\":[[{\"Literal\":{\"Value\":\"'Label - ▲ LY'\"}}]]}}}]},\"type\":\"Categorical\",\"howCreated\":1,\"objects\":{}}]", - "height": 111.36, - "width": 224.03, - "x": 32.10, - "y": 80.57, - "z": 0.00 - }, - { - "config": "{\"name\":\"eb5c360e357e8b54eb88\",\"layouts\":[{\"id\":0,\"position\":{\"x\":106.30042918454936,\"y\":8.034334763948499,\"z\":10000,\"width\":585.2703862660944,\"height\":62.4206008583691,\"tabOrder\":10000}},{\"id\":1,\"position\":{\"x\":0,\"y\":0,\"width\":324,\"height\":60}}],\"singleVisual\":{\"visualType\":\"textbox\",\"drillFilterOtherVisuals\":true,\"objects\":{\"general\":[{\"properties\":{\"paragraphs\":[{\"textRuns\":[{\"value\":\"MyCompany - \",\"textStyle\":{\"fontFamily\":\"Segoe (Bold)\",\"fontSize\":\"24pt\",\"color\":\"#000000\"}},{\"value\":\"Sales Analysis\",\"textStyle\":{\"fontWeight\":\"bold\",\"fontFamily\":\"Segoe (Bold)\",\"fontSize\":\"24pt\",\"color\":\"#000000\"}}]}]}}]},\"vcObjects\":{}}}", - "filters": "[]", - "height": 62.42, - "width": 585.27, - "x": 106.30, - "y": 8.03, - "z": 10000.00 - } - ], - "width": 1280.00 - }, - { - "config": "{\"filterSortOrder\":3}", - "displayName": "Dynamic Measure", - "displayOption": 1, - "filters": "[{\"name\":\"Filter5c809dc57d8789cbe6b2\",\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Smart Calcs\"}},\"Property\":\"Smart Calc\"}},\"filter\":{\"Version\":2,\"From\":[{\"Name\":\"s\",\"Entity\":\"Smart Calcs\",\"Type\":0}],\"Where\":[{\"Condition\":{\"In\":{\"Expressions\":[{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"s\"}},\"Property\":\"Smart Calc\"}}],\"Values\":[[{\"Literal\":{\"Value\":\"'Dynamic Measure - Apply Format'\"}}]]}}}]},\"type\":\"Categorical\",\"howCreated\":1,\"objects\":{},\"isHiddenInViewMode\":true,\"isLockedInViewMode\":true,\"ordinal\":0}]", - "height": 720.00, - "name": "ReportSection8a2d1e93c8442763827c", - "ordinal": 2, - "visualContainers": [ - { - "config": "{\"name\":\"0d3e7e800d157447d967\",\"layouts\":[{\"id\":0,\"position\":{\"x\":128.54935622317598,\"y\":8.034334763948499,\"z\":10000,\"width\":534.5922746781116,\"height\":62.4206008583691,\"tabOrder\":10000}}],\"singleVisual\":{\"visualType\":\"textbox\",\"drillFilterOtherVisuals\":true,\"objects\":{\"general\":[{\"properties\":{\"paragraphs\":[{\"textRuns\":[{\"value\":\"MyCompany - Dynamic Measure\",\"textStyle\":{\"fontFamily\":\"Segoe (Bold)\",\"fontSize\":\"24pt\",\"color\":\"#000000\"}}]}]}}]},\"vcObjects\":{}}}", - "filters": "[]", - "height": 62.42, - "width": 534.59, - "x": 128.55, - "y": 8.03, - "z": 10000.00 - }, - { - "config": "{\"name\":\"469bc55e70ada4022984\",\"layouts\":[{\"id\":0,\"position\":{\"x\":702.3728813559321,\"y\":80,\"z\":3000,\"width\":225.08474576271186,\"height\":112.54237288135593,\"tabOrder\":3000}}],\"singleVisual\":{\"visualType\":\"card\",\"projections\":{\"Values\":[{\"queryRef\":\"Dynamic Measure.Value % (Δ ly)\"}]},\"prototypeQuery\":{\"Version\":2,\"From\":[{\"Name\":\"d\",\"Entity\":\"Dynamic Measure\",\"Type\":0}],\"Select\":[{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Source\":\"d\"}},\"Property\":\"Value % (Δ ly)\"},\"Name\":\"Dynamic Measure.Value % (Δ ly)\"}],\"OrderBy\":[{\"Direction\":2,\"Expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Source\":\"d\"}},\"Property\":\"Value % (Δ ly)\"}}}]},\"drillFilterOtherVisuals\":true,\"hasDefaultSort\":true,\"objects\":{\"labels\":[{\"properties\":{\"color\":{\"solid\":{\"color\":{\"expr\":{\"ThemeDataColor\":{\"ColorId\":0,\"Percent\":0}}}}},\"fontSize\":{\"expr\":{\"Literal\":{\"Value\":\"20D\"}}}}}],\"categoryLabels\":[{\"properties\":{\"color\":{\"solid\":{\"color\":{\"expr\":{\"ThemeDataColor\":{\"ColorId\":0,\"Percent\":0}}}}}}}]},\"vcObjects\":{\"background\":[{\"properties\":{\"show\":{\"expr\":{\"Literal\":{\"Value\":\"true\"}}},\"color\":{\"solid\":{\"color\":{\"expr\":{\"ThemeDataColor\":{\"ColorId\":5,\"Percent\":0}}}}},\"transparency\":{\"expr\":{\"Literal\":{\"Value\":\"0D\"}}}}}],\"border\":[{\"properties\":{\"show\":{\"expr\":{\"Literal\":{\"Value\":\"false\"}}}}}],\"title\":[{\"properties\":{\"titleWrap\":{\"expr\":{\"Literal\":{\"Value\":\"true\"}}}}}]}}}", - "filters": "[]", - "height": 112.54, - "width": 225.08, - "x": 702.37, - "y": 80.00, - "z": 3000.00 - }, - { - "config": "{\"name\":\"6f9803040a3195450ea1\",\"layouts\":[{\"id\":0,\"position\":{\"x\":32.2508398656215,\"y\":416.39417693169094,\"z\":6000,\"width\":672.2508398656215,\"height\":289.54087346024636,\"tabOrder\":6000}}],\"singleVisual\":{\"visualType\":\"barChart\",\"projections\":{\"Category\":[{\"queryRef\":\"Product.Category\"},{\"queryRef\":\"Product.Product\",\"active\":true}],\"Y\":[{\"queryRef\":\"Dynamic Measure.Value\"}]},\"prototypeQuery\":{\"Version\":2,\"From\":[{\"Name\":\"p\",\"Entity\":\"Product\",\"Type\":0},{\"Name\":\"d\",\"Entity\":\"Dynamic Measure\",\"Type\":0}],\"Select\":[{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"p\"}},\"Property\":\"Product\"},\"Name\":\"Product.Product\"},{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Source\":\"d\"}},\"Property\":\"Value\"},\"Name\":\"Dynamic Measure.Value\"},{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"p\"}},\"Property\":\"Category\"},\"Name\":\"Product.Category\"}],\"OrderBy\":[{\"Direction\":2,\"Expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Source\":\"d\"}},\"Property\":\"Value\"}}}]},\"drillFilterOtherVisuals\":true,\"hasDefaultSort\":true,\"objects\":{},\"vcObjects\":{}}}", - "filters": "[]", - "height": 289.54, - "width": 672.25, - "x": 32.25, - "y": 416.39, - "z": 6000.00 - }, - { - "config": "{\"name\":\"836c673550db49514e6b\",\"layouts\":[{\"id\":0,\"position\":{\"x\":479.99999999999994,\"y\":80,\"z\":2000,\"width\":223.72881355932202,\"height\":112.54237288135593,\"tabOrder\":2000}}],\"singleVisual\":{\"visualType\":\"card\",\"projections\":{\"Values\":[{\"queryRef\":\"Dynamic Measure.Value Avg per Month\"}]},\"prototypeQuery\":{\"Version\":2,\"From\":[{\"Name\":\"d\",\"Entity\":\"Dynamic Measure\",\"Type\":0}],\"Select\":[{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Source\":\"d\"}},\"Property\":\"Value Avg per Month\"},\"Name\":\"Dynamic Measure.Value Avg per Month\"}],\"OrderBy\":[{\"Direction\":2,\"Expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Source\":\"d\"}},\"Property\":\"Value Avg per Month\"}}}]},\"drillFilterOtherVisuals\":true,\"hasDefaultSort\":true,\"objects\":{\"categoryLabels\":[{\"properties\":{\"color\":{\"solid\":{\"color\":{\"expr\":{\"ThemeDataColor\":{\"ColorId\":0,\"Percent\":0}}}}}}}],\"labels\":[{\"properties\":{\"color\":{\"solid\":{\"color\":{\"expr\":{\"ThemeDataColor\":{\"ColorId\":0,\"Percent\":0}}}}},\"fontSize\":{\"expr\":{\"Literal\":{\"Value\":\"20D\"}}}}}]},\"vcObjects\":{\"background\":[{\"properties\":{\"show\":{\"expr\":{\"Literal\":{\"Value\":\"true\"}}},\"color\":{\"solid\":{\"color\":{\"expr\":{\"ThemeDataColor\":{\"ColorId\":4,\"Percent\":0}}}}},\"transparency\":{\"expr\":{\"Literal\":{\"Value\":\"0D\"}}}}}],\"border\":[{\"properties\":{\"show\":{\"expr\":{\"Literal\":{\"Value\":\"false\"}}}}}],\"title\":[{\"properties\":{\"titleWrap\":{\"expr\":{\"Literal\":{\"Value\":\"true\"}}}}}]}}}", - "filters": "[]", - "height": 112.54, - "width": 223.73, - "x": 480.00, - "y": 80.00, - "z": 2000.00 - }, - { - "config": "{\"name\":\"8e729be52dab6a0bd956\",\"layouts\":[{\"id\":0,\"position\":{\"x\":256.2711864406779,\"y\":80,\"z\":1000,\"width\":223.72881355932202,\"height\":112.54237288135593,\"tabOrder\":1000}}],\"singleVisual\":{\"visualType\":\"card\",\"projections\":{\"Values\":[{\"queryRef\":\"Dynamic Measure.Value (ly)\"}]},\"prototypeQuery\":{\"Version\":2,\"From\":[{\"Name\":\"d\",\"Entity\":\"Dynamic Measure\",\"Type\":0}],\"Select\":[{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Source\":\"d\"}},\"Property\":\"Value (ly)\"},\"Name\":\"Dynamic Measure.Value (ly)\"}],\"OrderBy\":[{\"Direction\":2,\"Expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Source\":\"d\"}},\"Property\":\"Value (ly)\"}}}]},\"drillFilterOtherVisuals\":true,\"hasDefaultSort\":true,\"objects\":{\"labels\":[{\"properties\":{\"color\":{\"solid\":{\"color\":{\"expr\":{\"ThemeDataColor\":{\"ColorId\":0,\"Percent\":0}}}}},\"fontSize\":{\"expr\":{\"Literal\":{\"Value\":\"20D\"}}}}}],\"categoryLabels\":[{\"properties\":{\"color\":{\"solid\":{\"color\":{\"expr\":{\"ThemeDataColor\":{\"ColorId\":0,\"Percent\":0}}}}}}}]},\"vcObjects\":{\"background\":[{\"properties\":{\"show\":{\"expr\":{\"Literal\":{\"Value\":\"true\"}}},\"color\":{\"solid\":{\"color\":{\"expr\":{\"ThemeDataColor\":{\"ColorId\":3,\"Percent\":0}}}}},\"transparency\":{\"expr\":{\"Literal\":{\"Value\":\"0D\"}}}}}],\"border\":[{\"properties\":{\"show\":{\"expr\":{\"Literal\":{\"Value\":\"false\"}}}}}],\"title\":[{\"properties\":{\"titleWrap\":{\"expr\":{\"Literal\":{\"Value\":\"true\"}}}}}]}}}", - "filters": "[]", - "height": 112.54, - "width": 223.73, - "x": 256.27, - "y": 80.00, - "z": 1000.00 - }, - { - "config": "{\"name\":\"b1acdc3d6014876e1960\",\"layouts\":[{\"id\":0,\"position\":{\"x\":32.54237288135593,\"y\":80,\"z\":0,\"width\":223.72881355932202,\"height\":112.54237288135593,\"tabOrder\":0}}],\"singleVisual\":{\"visualType\":\"card\",\"projections\":{\"Values\":[{\"queryRef\":\"Dynamic Measure.Value\"}]},\"prototypeQuery\":{\"Version\":2,\"From\":[{\"Name\":\"d\",\"Entity\":\"Dynamic Measure\",\"Type\":0}],\"Select\":[{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Source\":\"d\"}},\"Property\":\"Value\"},\"Name\":\"Dynamic Measure.Value\"}],\"OrderBy\":[{\"Direction\":2,\"Expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Source\":\"d\"}},\"Property\":\"Value\"}}}]},\"drillFilterOtherVisuals\":true,\"hasDefaultSort\":true,\"objects\":{\"categoryLabels\":[{\"properties\":{\"color\":{\"solid\":{\"color\":{\"expr\":{\"ThemeDataColor\":{\"ColorId\":0,\"Percent\":0}}}}}}}],\"labels\":[{\"properties\":{\"color\":{\"solid\":{\"color\":{\"expr\":{\"ThemeDataColor\":{\"ColorId\":0,\"Percent\":0}}}}},\"fontSize\":{\"expr\":{\"Literal\":{\"Value\":\"20D\"}}}}}]},\"vcObjects\":{\"background\":[{\"properties\":{\"show\":{\"expr\":{\"Literal\":{\"Value\":\"true\"}}},\"color\":{\"solid\":{\"color\":{\"expr\":{\"ThemeDataColor\":{\"ColorId\":2,\"Percent\":0}}}}},\"transparency\":{\"expr\":{\"Literal\":{\"Value\":\"0D\"}}}}}],\"border\":[{\"properties\":{\"show\":{\"expr\":{\"Literal\":{\"Value\":\"false\"}}}}}]}}}", - "filters": "[]", - "height": 112.54, - "width": 223.73, - "x": 32.54, - "y": 80.00, - "z": 0.00 - }, - { - "config": "{\"name\":\"c199c1700c0b520ba80a\",\"layouts\":[{\"id\":0,\"position\":{\"x\":32.137339055793994,\"y\":6.180257510729613,\"z\":11000,\"width\":65.5107296137339,\"height\":65.5107296137339,\"tabOrder\":11000}}],\"singleVisual\":{\"visualType\":\"image\",\"drillFilterOtherVisuals\":true,\"objects\":{\"general\":[{\"properties\":{\"imageUrl\":{\"expr\":{\"ResourcePackageItem\":{\"PackageName\":\"RegisteredResources\",\"PackageType\":1,\"ItemName\":\"_7abfc6c7-1a23-4b5f-bd8b-8dc472366284171093267.jpg\"}}}}}]}}}", - "filters": "[]", - "height": 65.51, - "width": 65.51, - "x": 32.14, - "y": 6.18, - "z": 11000.00 - }, - { - "config": "{\"name\":\"cedd897ce35c9089630c\",\"layouts\":[{\"id\":0,\"position\":{\"x\":942.441209406495,\"y\":80.26875699888018,\"z\":4000,\"width\":312.474804031355,\"height\":112.51959686450168,\"tabOrder\":4000}}],\"singleVisual\":{\"visualType\":\"slicer\",\"projections\":{\"Values\":[{\"queryRef\":\"Calendar.Date\",\"active\":true}]},\"prototypeQuery\":{\"Version\":2,\"From\":[{\"Name\":\"c\",\"Entity\":\"Calendar\",\"Type\":0}],\"Select\":[{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"c\"}},\"Property\":\"Date\"},\"Name\":\"Calendar.Date\"}],\"OrderBy\":[{\"Direction\":1,\"Expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"c\"}},\"Property\":\"Date\"}}}]},\"drillFilterOtherVisuals\":true,\"hasDefaultSort\":true,\"objects\":{},\"vcObjects\":{}}}", - "filters": "[]", - "height": 112.52, - "width": 312.47, - "x": 942.44, - "y": 80.27, - "z": 4000.00 - }, - { - "config": "{\"name\":\"e50725a866c432900b35\",\"layouts\":[{\"id\":0,\"position\":{\"x\":728.7591240875912,\"y\":416.4337851929093,\"z\":8000,\"width\":525.881126173097,\"height\":289.6350364963503,\"tabOrder\":8000}}],\"singleVisual\":{\"visualType\":\"lineChart\",\"projections\":{\"Category\":[{\"queryRef\":\"Calendar.Date\",\"active\":true}],\"Y\":[{\"queryRef\":\"Dynamic Measure.Value\"}]},\"prototypeQuery\":{\"Version\":2,\"From\":[{\"Name\":\"c1\",\"Entity\":\"Calendar\",\"Type\":0},{\"Name\":\"d\",\"Entity\":\"Dynamic Measure\",\"Type\":0}],\"Select\":[{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Source\":\"d\"}},\"Property\":\"Value\"},\"Name\":\"Dynamic Measure.Value\"},{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"c1\"}},\"Property\":\"Date\"},\"Name\":\"Calendar.Date\"}],\"OrderBy\":[{\"Direction\":1,\"Expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"c1\"}},\"Property\":\"Date\"}}}]},\"queryOptions\":{\"keepProjectionOrder\":true},\"drillFilterOtherVisuals\":true,\"hasDefaultSort\":true,\"objects\":{},\"vcObjects\":{}}}", - "filters": "[]", - "height": 289.64, - "width": 525.88, - "x": 728.76, - "y": 416.43, - "z": 8000.00 - }, - { - "config": "{\"name\":\"e859ee4d7c5b10e836d6\",\"layouts\":[{\"id\":0,\"position\":{\"x\":31.419423692636073,\"y\":207.64140875133404,\"z\":5000,\"width\":672.102454642476,\"height\":191.24866595517608,\"tabOrder\":5000}}],\"singleVisual\":{\"visualType\":\"lineChart\",\"projections\":{\"Category\":[{\"queryRef\":\"Calendar.Month Start Date\",\"active\":true}],\"Y\":[{\"queryRef\":\"Dynamic Measure.Value\"},{\"queryRef\":\"Dynamic Measure.Value (ly)\"}],\"Y2\":[{\"queryRef\":\"Dynamic Measure.Value Daily Max\"}]},\"prototypeQuery\":{\"Version\":2,\"From\":[{\"Name\":\"c1\",\"Entity\":\"Calendar\",\"Type\":0},{\"Name\":\"d\",\"Entity\":\"Dynamic Measure\",\"Type\":0}],\"Select\":[{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Source\":\"d\"}},\"Property\":\"Value\"},\"Name\":\"Dynamic Measure.Value\"},{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Source\":\"d\"}},\"Property\":\"Value (ly)\"},\"Name\":\"Dynamic Measure.Value (ly)\"},{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Source\":\"d\"}},\"Property\":\"Value Daily Max\"},\"Name\":\"Dynamic Measure.Value Daily Max\"},{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"c1\"}},\"Property\":\"Month Start Date\"},\"Name\":\"Calendar.Month Start Date\"}],\"OrderBy\":[{\"Direction\":1,\"Expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"c1\"}},\"Property\":\"Month Start Date\"}}}]},\"queryOptions\":{\"keepProjectionOrder\":true},\"drillFilterOtherVisuals\":true,\"hasDefaultSort\":true,\"objects\":{},\"vcObjects\":{}}}", - "filters": "[]", - "height": 191.25, - "width": 672.10, - "x": 31.42, - "y": 207.64, - "z": 5000.00 - }, - { - "config": "{\"name\":\"f0143b84500069035233\",\"layouts\":[{\"id\":0,\"position\":{\"x\":728.1522956326988,\"y\":207.8387458006719,\"z\":9000,\"width\":526.7637178051511,\"height\":191.35498320268758,\"tabOrder\":9000}}],\"singleVisual\":{\"visualType\":\"areaChart\",\"projections\":{\"Category\":[{\"queryRef\":\"Calendar.Month Start Date\",\"active\":true}],\"Y\":[{\"queryRef\":\"Dynamic Measure.Value (ytd)\"}]},\"prototypeQuery\":{\"Version\":2,\"From\":[{\"Name\":\"c1\",\"Entity\":\"Calendar\",\"Type\":0},{\"Name\":\"d\",\"Entity\":\"Dynamic Measure\",\"Type\":0}],\"Select\":[{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Source\":\"d\"}},\"Property\":\"Value (ytd)\"},\"Name\":\"Dynamic Measure.Value (ytd)\"},{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"c1\"}},\"Property\":\"Month Start Date\"},\"Name\":\"Calendar.Month Start Date\"}],\"OrderBy\":[{\"Direction\":1,\"Expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"c1\"}},\"Property\":\"Month Start Date\"}}}]},\"queryOptions\":{\"keepProjectionOrder\":true},\"drillFilterOtherVisuals\":true,\"hasDefaultSort\":true,\"objects\":{},\"vcObjects\":{}}}", - "filters": "[]", - "height": 191.35, - "width": 526.76, - "x": 728.15, - "y": 207.84, - "z": 9000.00 - }, - { - "config": "{\"name\":\"faf65e012ba31569ae03\",\"layouts\":[{\"id\":0,\"position\":{\"x\":800.3433476394849,\"y\":8.65236051502146,\"z\":7000,\"width\":454.86695278969955,\"height\":61.18454935622317,\"tabOrder\":7000}}],\"singleVisual\":{\"visualType\":\"slicer\",\"projections\":{\"Values\":[{\"queryRef\":\"Dynamic Measure.Measure\",\"active\":true}]},\"prototypeQuery\":{\"Version\":2,\"From\":[{\"Name\":\"d\",\"Entity\":\"Dynamic Measure\",\"Type\":0}],\"Select\":[{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"d\"}},\"Property\":\"Measure\"},\"Name\":\"Dynamic Measure.Measure\"}]},\"drillFilterOtherVisuals\":true,\"objects\":{\"header\":[{\"properties\":{\"show\":{\"expr\":{\"Literal\":{\"Value\":\"false\"}}}}}],\"data\":[{\"properties\":{\"mode\":{\"expr\":{\"Literal\":{\"Value\":\"'Dropdown'\"}}}}}]},\"vcObjects\":{}}}", - "filters": "[]", - "height": 61.18, - "width": 454.87, - "x": 800.34, - "y": 8.65, - "z": 7000.00 - } - ], - "width": 1280.00 - }, - { - "config": "{\"objects\":{\"outspacePane\":[{\"properties\":{\"width\":{\"expr\":{\"Literal\":{\"Value\":\"303L\"}}}}}]},\"filterSortOrder\":3}", - "displayName": "Sales Geo", - "displayOption": 1, - "filters": "[{\"name\":\"Filterf99428c5489cc4e6747a\",\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Calendar\"}},\"Property\":\"Year\"}},\"type\":\"Categorical\",\"howCreated\":1,\"objects\":{\"general\":[{\"properties\":{\"requireSingleSelect\":{\"expr\":{\"Literal\":{\"Value\":\"true\"}}}}}]},\"ordinal\":0},{\"name\":\"Filter78bda1908a0bd749d86a\",\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Product\"}},\"Property\":\"Category\"}},\"type\":\"Categorical\",\"howCreated\":1,\"objects\":{},\"ordinal\":1},{\"name\":\"Filter9ce4dd28c25b24ab1603\",\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Product\"}},\"Property\":\"Subcategory\"}},\"type\":\"Categorical\",\"howCreated\":1,\"ordinal\":2},{\"name\":\"Filterfa59dace34a0d2b060c1\",\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Store\"}},\"Property\":\"Store\"}},\"type\":\"Categorical\",\"howCreated\":1,\"ordinal\":3}]", - "height": 720.00, - "name": "ReportSectiond90903559771e58905a1", - "ordinal": 1, - "visualContainers": [ - { - "config": "{\"name\":\"07ec3a800d9302761545\",\"layouts\":[{\"id\":0,\"position\":{\"x\":480.16376663254863,\"y\":80.57318321392016,\"z\":2000,\"width\":224.03275332650972,\"height\":111.36131013306039,\"tabOrder\":2000}},{\"id\":1,\"position\":{\"x\":0,\"y\":360,\"width\":324,\"height\":120}}],\"singleVisual\":{\"visualType\":\"card\",\"projections\":{\"Values\":[{\"queryRef\":\"Sales.Sales Amount Avg per Day\"}]},\"prototypeQuery\":{\"Version\":2,\"From\":[{\"Name\":\"s\",\"Entity\":\"Sales\",\"Type\":0}],\"Select\":[{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Source\":\"s\"}},\"Property\":\"Sales Amount Avg per Day\"},\"Name\":\"Sales.Sales Amount Avg per Day\"}],\"OrderBy\":[{\"Direction\":2,\"Expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Source\":\"s\"}},\"Property\":\"Sales Amount Avg per Day\"}}}]},\"drillFilterOtherVisuals\":true,\"hasDefaultSort\":true,\"objects\":{\"labels\":[{\"properties\":{\"color\":{\"solid\":{\"color\":{\"expr\":{\"ThemeDataColor\":{\"ColorId\":0,\"Percent\":0}}}}},\"fontSize\":{\"expr\":{\"Literal\":{\"Value\":\"16D\"}}}}}],\"categoryLabels\":[{\"properties\":{\"color\":{\"solid\":{\"color\":{\"expr\":{\"ThemeDataColor\":{\"ColorId\":0,\"Percent\":0}}}}}}}]},\"vcObjects\":{\"background\":[{\"properties\":{\"show\":{\"expr\":{\"Literal\":{\"Value\":\"true\"}}},\"color\":{\"solid\":{\"color\":{\"expr\":{\"ThemeDataColor\":{\"ColorId\":4,\"Percent\":0}}}}},\"transparency\":{\"expr\":{\"Literal\":{\"Value\":\"0D\"}}}}}],\"border\":[{\"properties\":{\"show\":{\"expr\":{\"Literal\":{\"Value\":\"false\"}}}}}]}}}", - "filters": "[{\"name\":\"Filterac4e7a5cd0c9731a470e\",\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Smart Calcs\"}},\"Property\":\"Smart Calc\"}},\"filter\":{\"Version\":2,\"From\":[{\"Name\":\"s\",\"Entity\":\"Smart Calcs\",\"Type\":0}],\"Where\":[{\"Condition\":{\"In\":{\"Expressions\":[{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"s\"}},\"Property\":\"Smart Calc\"}}],\"Values\":[[{\"Literal\":{\"Value\":\"'Label - ▲ LY'\"}}]]}}}]},\"type\":\"Categorical\",\"howCreated\":1,\"objects\":{}}]", - "height": 111.36, - "width": 224.03, - "x": 480.16, - "y": 80.57, - "z": 2000.00 - }, - { - "config": "{\"name\":\"3580cccd5431ed37d799\",\"layouts\":[{\"id\":0,\"position\":{\"x\":32.137339055793994,\"y\":6.180257510729613,\"z\":13000,\"width\":65.5107296137339,\"height\":65.5107296137339,\"tabOrder\":13000}}],\"singleVisual\":{\"visualType\":\"image\",\"drillFilterOtherVisuals\":true,\"objects\":{\"general\":[{\"properties\":{\"imageUrl\":{\"expr\":{\"ResourcePackageItem\":{\"PackageName\":\"RegisteredResources\",\"PackageType\":1,\"ItemName\":\"_7abfc6c7-1a23-4b5f-bd8b-8dc472366284171093267.jpg\"}}}}}]}}}", - "filters": "[]", - "height": 65.51, - "width": 65.51, - "x": 32.14, - "y": 6.18, - "z": 13000.00 - }, - { - "config": "{\"name\":\"40792541a9e9a0e80ce0\",\"layouts\":[{\"id\":0,\"position\":{\"x\":980.6184987787653,\"y\":3.8355351973093823,\"z\":11000,\"width\":299.17174539013183,\"height\":70.31814528400534,\"tabOrder\":11000}}],\"singleVisual\":{\"visualType\":\"card\",\"projections\":{\"Values\":[{\"queryRef\":\"Min(About.Value)\"}]},\"prototypeQuery\":{\"Version\":2,\"From\":[{\"Name\":\"a\",\"Entity\":\"About\",\"Type\":0}],\"Select\":[{\"Aggregation\":{\"Expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"a\"}},\"Property\":\"Value\"}},\"Function\":3},\"Name\":\"Min(About.Value)\"}]},\"drillFilterOtherVisuals\":true,\"objects\":{\"labels\":[{\"properties\":{\"fontSize\":{\"expr\":{\"Literal\":{\"Value\":\"20D\"}}}}}],\"categoryLabels\":[{\"properties\":{\"show\":{\"expr\":{\"Literal\":{\"Value\":\"false\"}}}}}]},\"vcObjects\":{}}}", - "filters": "[{\"name\":\"Filterf1ec25bb722d30be4755\",\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"About\"}},\"Property\":\"Key\"}},\"filter\":{\"Version\":2,\"From\":[{\"Name\":\"a\",\"Entity\":\"About\",\"Type\":0}],\"Where\":[{\"Condition\":{\"In\":{\"Expressions\":[{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"a\"}},\"Property\":\"Key\"}}],\"Values\":[[{\"Literal\":{\"Value\":\"'Last Refresh'\"}}]]}}}]},\"type\":\"Categorical\",\"howCreated\":1,\"objects\":{}}]", - "height": 70.32, - "width": 299.17, - "x": 980.62, - "y": 3.84, - "z": 11000.00 - }, - { - "config": "{\"name\":\"477e4ffc7bd2ba217c85\",\"layouts\":[{\"id\":0,\"position\":{\"x\":106.30042918454936,\"y\":8.034334763948499,\"z\":10000,\"width\":585.2703862660944,\"height\":62.4206008583691,\"tabOrder\":10000}},{\"id\":1,\"position\":{\"x\":0,\"y\":0,\"width\":324,\"height\":60}}],\"singleVisual\":{\"visualType\":\"textbox\",\"drillFilterOtherVisuals\":true,\"objects\":{\"general\":[{\"properties\":{\"paragraphs\":[{\"textRuns\":[{\"value\":\"MyCompany - \",\"textStyle\":{\"fontFamily\":\"Segoe (Bold)\",\"fontSize\":\"24pt\",\"color\":\"#000000\"}},{\"value\":\"Geo Analysis\",\"textStyle\":{\"fontWeight\":\"bold\",\"fontFamily\":\"Segoe (Bold)\",\"fontSize\":\"24pt\",\"color\":\"#000000\"}}]}]}}]},\"vcObjects\":{}}}", - "filters": "[]", - "height": 62.42, - "width": 585.27, - "x": 106.30, - "y": 8.03, - "z": 10000.00 - }, - { - "config": "{\"name\":\"4a9f9e8013c4b3d2ed84\",\"layouts\":[{\"id\":0,\"position\":{\"x\":32.09825997952917,\"y\":80.57318321392016,\"z\":0,\"width\":224.03275332650972,\"height\":111.36131013306039,\"tabOrder\":0}},{\"id\":1,\"position\":{\"x\":0,\"y\":120,\"width\":324,\"height\":120}}],\"singleVisual\":{\"visualType\":\"card\",\"projections\":{\"Values\":[{\"queryRef\":\"Measure Table.Sales Amount\"}]},\"prototypeQuery\":{\"Version\":2,\"From\":[{\"Name\":\"c\",\"Entity\":\"Calendar\",\"Type\":0},{\"Name\":\"s\",\"Entity\":\"Sales\",\"Type\":0}],\"Select\":[{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Source\":\"s\"}},\"Property\":\"Sales Amount\"},\"Name\":\"Measure Table.Sales Amount\"}],\"OrderBy\":[{\"Direction\":2,\"Expression\":{\"Aggregation\":{\"Expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"c\"}},\"Property\":\"Year\"}},\"Function\":2}}}]},\"drillFilterOtherVisuals\":true,\"hasDefaultSort\":true,\"objects\":{\"categoryLabels\":[{\"properties\":{\"color\":{\"solid\":{\"color\":{\"expr\":{\"ThemeDataColor\":{\"ColorId\":0,\"Percent\":0}}}}}}}],\"labels\":[{\"properties\":{\"color\":{\"solid\":{\"color\":{\"expr\":{\"ThemeDataColor\":{\"ColorId\":0,\"Percent\":0}}}}},\"fontSize\":{\"expr\":{\"Literal\":{\"Value\":\"16D\"}}}}}]},\"vcObjects\":{\"background\":[{\"properties\":{\"show\":{\"expr\":{\"Literal\":{\"Value\":\"true\"}}},\"color\":{\"solid\":{\"color\":{\"expr\":{\"ThemeDataColor\":{\"ColorId\":2,\"Percent\":0}}}}},\"transparency\":{\"expr\":{\"Literal\":{\"Value\":\"0D\"}}}}}],\"border\":[{\"properties\":{\"show\":{\"expr\":{\"Literal\":{\"Value\":\"false\"}}}}}]}}}", - "filters": "[{\"name\":\"Filter7a63ba4725d907434991\",\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Smart Calcs\"}},\"Property\":\"Smart Calc\"}},\"filter\":{\"Version\":2,\"From\":[{\"Name\":\"s\",\"Entity\":\"Smart Calcs\",\"Type\":0}],\"Where\":[{\"Condition\":{\"In\":{\"Expressions\":[{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"s\"}},\"Property\":\"Smart Calc\"}}],\"Values\":[[{\"Literal\":{\"Value\":\"'Label - ▲ LY'\"}}]]}}}]},\"type\":\"Categorical\",\"howCreated\":1,\"objects\":{}}]", - "height": 111.36, - "width": 224.03, - "x": 32.10, - "y": 80.57, - "z": 0.00 - }, - { - "config": "{\"name\":\"836caf85daad17452d48\",\"layouts\":[{\"id\":0,\"position\":{\"x\":942.7956989247311,\"y\":79.82795698924731,\"z\":4000,\"width\":304.1720430107527,\"height\":112.86021505376344,\"tabOrder\":4000}},{\"id\":1,\"position\":{\"x\":0,\"y\":900,\"width\":324,\"height\":120}}],\"singleVisual\":{\"visualType\":\"slicer\",\"projections\":{\"Values\":[{\"queryRef\":\"Calendar.Date\",\"active\":true}]},\"prototypeQuery\":{\"Version\":2,\"From\":[{\"Name\":\"c\",\"Entity\":\"Calendar\",\"Type\":0}],\"Select\":[{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"c\"}},\"Property\":\"Date\"},\"Name\":\"Calendar.Date\"}],\"OrderBy\":[{\"Direction\":1,\"Expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"c\"}},\"Property\":\"Date\"}}}]},\"drillFilterOtherVisuals\":true,\"hasDefaultSort\":true,\"objects\":{},\"vcObjects\":{}}}", - "filters": "[]", - "height": 112.86, - "width": 304.17, - "x": 942.80, - "y": 79.83, - "z": 4000.00 - }, - { - "config": "{\"name\":\"a02ce779cd248c240002\",\"layouts\":[{\"id\":0,\"position\":{\"x\":765.803224723949,\"y\":5.2362613656338395,\"z\":12000,\"width\":223.85017338084663,\"height\":68.0713977532399,\"tabOrder\":12000}}],\"singleVisual\":{\"visualType\":\"card\",\"projections\":{\"Values\":[{\"queryRef\":\"Min(Sales.Environment)\"}]},\"prototypeQuery\":{\"Version\":2,\"From\":[{\"Name\":\"s\",\"Entity\":\"Sales\",\"Type\":0}],\"Select\":[{\"Aggregation\":{\"Expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"s\"}},\"Property\":\"Environment\"}},\"Function\":3},\"Name\":\"Min(Sales.Environment)\"}]},\"drillFilterOtherVisuals\":true,\"objects\":{\"labels\":[{\"properties\":{\"fontSize\":{\"expr\":{\"Literal\":{\"Value\":\"20D\"}}}}}],\"categoryLabels\":[{\"properties\":{\"show\":{\"expr\":{\"Literal\":{\"Value\":\"false\"}}}}}]}}}", - "filters": "[]", - "height": 68.07, - "width": 223.85, - "x": 765.80, - "y": 5.24, - "z": 12000.00 - }, - { - "config": "{\"name\":\"a64038428c095bd71739\",\"layouts\":[{\"id\":0,\"position\":{\"x\":32.137339055793994,\"y\":207.656652360515,\"z\":9000,\"width\":1215.038626609442,\"height\":494.4206008583691,\"tabOrder\":9000}},{\"id\":1,\"position\":{\"x\":0,\"y\":660,\"width\":324,\"height\":180}}],\"singleVisual\":{\"visualType\":\"azureMap\",\"projections\":{\"Size\":[{\"queryRef\":\"Sales.Sales Amount\"}],\"Category\":[{\"queryRef\":\"Customer.Country\",\"active\":true},{\"queryRef\":\"Customer.State\"},{\"queryRef\":\"Customer.City\"}],\"Series\":[{\"queryRef\":\"Customer.Gender\"}]},\"prototypeQuery\":{\"Version\":2,\"From\":[{\"Name\":\"s\",\"Entity\":\"Sales\",\"Type\":0},{\"Name\":\"c\",\"Entity\":\"Customer\",\"Type\":0}],\"Select\":[{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Source\":\"s\"}},\"Property\":\"Sales Amount\"},\"Name\":\"Sales.Sales Amount\"},{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"c\"}},\"Property\":\"Country\"},\"Name\":\"Customer.Country\"},{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"c\"}},\"Property\":\"State\"},\"Name\":\"Customer.State\"},{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"c\"}},\"Property\":\"City\"},\"Name\":\"Customer.City\"},{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"c\"}},\"Property\":\"Gender\"},\"Name\":\"Customer.Gender\"}],\"OrderBy\":[{\"Direction\":2,\"Expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Source\":\"s\"}},\"Property\":\"Sales Amount\"}}}]},\"drillFilterOtherVisuals\":true,\"hasDefaultSort\":true,\"objects\":{\"mapControls\":[{\"properties\":{\"defaultStyle\":{\"expr\":{\"Literal\":{\"Value\":\"'road'\"}}},\"showStylePicker\":{\"expr\":{\"Literal\":{\"Value\":\"false\"}}},\"showNavigationControls\":{\"expr\":{\"Literal\":{\"Value\":\"false\"}}},\"showSelectionControl\":{\"expr\":{\"Literal\":{\"Value\":\"false\"}}}}}],\"bubbleLayer\":[{\"properties\":{\"show\":{\"expr\":{\"Literal\":{\"Value\":\"true\"}}},\"bubbleRadius\":{\"expr\":{\"Literal\":{\"Value\":\"6L\"}}},\"minBubbleRadius\":{\"expr\":{\"Literal\":{\"Value\":\"6L\"}}},\"maxRadius\":{\"expr\":{\"Literal\":{\"Value\":\"21L\"}}},\"bubbleStrokeWidth\":{\"expr\":{\"Literal\":{\"Value\":\"1L\"}}},\"autoStrokeColor\":{\"expr\":{\"Literal\":{\"Value\":\"true\"}}},\"layerPosition\":{\"expr\":{\"Literal\":{\"Value\":\"''\"}}},\"markerRangeType\":{\"expr\":{\"Literal\":{\"Value\":\"'scatterDeprecated'\"}}}}}]},\"vcObjects\":{}}}", - "filters": "[]", - "height": 494.42, - "width": 1215.04, - "x": 32.14, - "y": 207.66, - "z": 9000.00 - }, - { - "config": "{\"name\":\"b5f2ce8ee65990353917\",\"layouts\":[{\"id\":0,\"position\":{\"x\":702.8863868986693,\"y\":80.57318321392016,\"z\":3000,\"width\":224.03275332650972,\"height\":111.36131013306039,\"tabOrder\":3000}},{\"id\":1,\"position\":{\"x\":0,\"y\":480,\"width\":324,\"height\":120}}],\"singleVisual\":{\"visualType\":\"card\",\"projections\":{\"Values\":[{\"queryRef\":\"Sales.# Customers (with Sales)\"}]},\"prototypeQuery\":{\"Version\":2,\"From\":[{\"Name\":\"s\",\"Entity\":\"Sales\",\"Type\":0}],\"Select\":[{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Source\":\"s\"}},\"Property\":\"# Customers (with Sales)\"},\"Name\":\"Sales.# Customers (with Sales)\"}],\"OrderBy\":[{\"Direction\":2,\"Expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Source\":\"s\"}},\"Property\":\"# Customers (with Sales)\"}}}]},\"columnProperties\":{\"Sales.# Customers (with Sales)\":{\"displayName\":\"# Customers\"}},\"drillFilterOtherVisuals\":true,\"filterSortOrder\":3,\"hasDefaultSort\":true,\"objects\":{\"labels\":[{\"properties\":{\"color\":{\"solid\":{\"color\":{\"expr\":{\"ThemeDataColor\":{\"ColorId\":0,\"Percent\":0}}}}},\"fontSize\":{\"expr\":{\"Literal\":{\"Value\":\"16D\"}}}}}],\"categoryLabels\":[{\"properties\":{\"color\":{\"solid\":{\"color\":{\"expr\":{\"ThemeDataColor\":{\"ColorId\":0,\"Percent\":0}}}}}}}]},\"vcObjects\":{\"background\":[{\"properties\":{\"show\":{\"expr\":{\"Literal\":{\"Value\":\"true\"}}},\"color\":{\"solid\":{\"color\":{\"expr\":{\"ThemeDataColor\":{\"ColorId\":5,\"Percent\":0}}}}},\"transparency\":{\"expr\":{\"Literal\":{\"Value\":\"0D\"}}}}}],\"border\":[{\"properties\":{\"show\":{\"expr\":{\"Literal\":{\"Value\":\"false\"}}}}}]}}}", - "filters": "[{\"name\":\"Filter2bc8e5e0b6ab2126005e\",\"expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Sales\"}},\"Property\":\"Sales Qty\"}},\"type\":\"Advanced\",\"howCreated\":1,\"isHiddenInViewMode\":false,\"ordinal\":0},{\"name\":\"Filter0f21badd084a049e0663\",\"expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Sales\"}},\"Property\":\"Sales Amount Avg per Day\"}},\"type\":\"Advanced\",\"howCreated\":1,\"isHiddenInViewMode\":false,\"ordinal\":1},{\"name\":\"Filterf321968e3dc8aa5d2791\",\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Smart Calcs\"}},\"Property\":\"Smart Calc\"}},\"filter\":{\"Version\":2,\"From\":[{\"Name\":\"s\",\"Entity\":\"Smart Calcs\",\"Type\":0}],\"Where\":[{\"Condition\":{\"In\":{\"Expressions\":[{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"s\"}},\"Property\":\"Smart Calc\"}}],\"Values\":[[{\"Literal\":{\"Value\":\"'Label - ▲ LY'\"}}]]}}}]},\"type\":\"Categorical\",\"howCreated\":1,\"objects\":{\"general\":[{\"properties\":{}}]},\"ordinal\":2}]", - "height": 111.36, - "width": 224.03, - "x": 702.89, - "y": 80.57, - "z": 3000.00 - }, - { - "config": "{\"name\":\"c541b1fe7656a8301322\",\"layouts\":[{\"id\":0,\"position\":{\"x\":256.1310133060389,\"y\":80.57318321392016,\"z\":1000,\"width\":224.03275332650972,\"height\":111.36131013306039,\"tabOrder\":1000}},{\"id\":1,\"position\":{\"x\":0,\"y\":240,\"width\":324,\"height\":120}}],\"singleVisual\":{\"visualType\":\"card\",\"projections\":{\"Values\":[{\"queryRef\":\"Sales.Margin\"}]},\"prototypeQuery\":{\"Version\":2,\"From\":[{\"Name\":\"s\",\"Entity\":\"Sales\",\"Type\":0}],\"Select\":[{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Source\":\"s\"}},\"Property\":\"Margin\"},\"Name\":\"Sales.Margin\"}],\"OrderBy\":[{\"Direction\":2,\"Expression\":{\"Measure\":{\"Expression\":{\"SourceRef\":{\"Source\":\"s\"}},\"Property\":\"Margin\"}}}]},\"drillFilterOtherVisuals\":true,\"hasDefaultSort\":true,\"objects\":{\"labels\":[{\"properties\":{\"color\":{\"solid\":{\"color\":{\"expr\":{\"ThemeDataColor\":{\"ColorId\":0,\"Percent\":0}}}}},\"fontSize\":{\"expr\":{\"Literal\":{\"Value\":\"16D\"}}}}}],\"categoryLabels\":[{\"properties\":{\"color\":{\"solid\":{\"color\":{\"expr\":{\"ThemeDataColor\":{\"ColorId\":0,\"Percent\":0}}}}}}}]},\"vcObjects\":{\"background\":[{\"properties\":{\"show\":{\"expr\":{\"Literal\":{\"Value\":\"true\"}}},\"color\":{\"solid\":{\"color\":{\"expr\":{\"ThemeDataColor\":{\"ColorId\":3,\"Percent\":0}}}}},\"transparency\":{\"expr\":{\"Literal\":{\"Value\":\"0D\"}}}}}],\"border\":[{\"properties\":{\"show\":{\"expr\":{\"Literal\":{\"Value\":\"false\"}}}}}]}}}", - "filters": "[{\"name\":\"Filter2a1ae8311dd5bd80e06c\",\"expression\":{\"Column\":{\"Expression\":{\"SourceRef\":{\"Entity\":\"Smart Calcs\"}},\"Property\":\"Smart Calc\"}},\"filter\":{\"Version\":2,\"From\":[{\"Name\":\"s\",\"Entity\":\"Smart Calcs\",\"Type\":0}],\"Where\":[{\"Condition\":{\"In\":{\"Expressions\":[{\"Column\":{\"Expression\":{\"SourceRef\":{\"Source\":\"s\"}},\"Property\":\"Smart Calc\"}}],\"Values\":[[{\"Literal\":{\"Value\":\"'Label - ▲ LY'\"}}]]}}}]},\"type\":\"Categorical\",\"howCreated\":1,\"objects\":{}}]", - "height": 111.36, - "width": 224.03, - "x": 256.13, - "y": 80.57, - "z": 1000.00 - } - ], - "width": 1280.00 - } - ], - "theme": "Light4437032645752863.json" -} \ No newline at end of file diff --git a/Test/SamplePBIP/Sales.pbip b/Test/SamplePBIP/Sales.pbip deleted file mode 100644 index 285e545a..00000000 --- a/Test/SamplePBIP/Sales.pbip +++ /dev/null @@ -1,13 +0,0 @@ -{ - "version": "1.0", - "artifacts": [ - { - "report": { - "path": "Sales.Report" - } - } - ], - "settings": { - "enableAutoRecovery": true - } -} \ No newline at end of file diff --git a/Test/Test-DeployPBIP-Overrides.ps1 b/Test/Test-DeployPBIP-Overrides.ps1 deleted file mode 100644 index 1790acb6..00000000 --- a/Test/Test-DeployPBIP-Overrides.ps1 +++ /dev/null @@ -1,72 +0,0 @@ -$currentPath = (Split-Path $MyInvocation.MyCommand.Definition -Parent) - -Set-Location $currentPath - -Import-Module ".\FabricPS-PBIP" -Force - -$workspaceName = "RR - FabricAPIs - Deploy 2" -$datasetName = "Dataset A" -$reportName = "Report A" -$pbipDatasetPath = "$currentPath\SamplePBIP\Sales.Dataset" -$pbipReportPath = "$currentPath\SamplePBIP\Sales.Report" - -# Ensure workspace exists - -$workspaceId = New-FabricWorkspace -name $workspaceName -skipErrorIfExists - -if (!$workspaceId) { throw "WorkspaceId cannot be null"} - -# Deploy Dataset - -$fileDatasetOverrides = @{ - "*item.metadata.json" = @{ - "type" = "dataset" - "displayName" = $datasetName - } | ConvertTo-Json -} - -$datasetId = Import-FabricItems -workspaceId $workspaceId -path $pbipDatasetPath -fileOverrides $fileDatasetOverrides - -# Deploy Report - -$fileReportOverrides = @{ - - # Change the connected dataset - - "*definition.pbir" = @{ - "version" = "1.0" - "datasetReference" = @{ - "byConnection" = @{ - "connectionString" = $null - "pbiServiceModelId" = $null - "pbiModelVirtualServerName" = "sobe_wowvirtualserver" - "pbiModelDatabaseName" = "$datasetId" - "name" = "EntityDataSource" - "connectionType" = "pbiServiceXmlaStyleLive" - } - } - } | ConvertTo-Json - - # Change logo - - "*_7abfc6c7-1a23-4b5f-bd8b-8dc472366284171093267.jpg" = [System.IO.File]::ReadAllBytes("$currentPath\sample-resources\logo2.jpg") - - # Change theme - - "*Light4437032645752863.json" = [System.IO.File]::ReadAllBytes("$currentPath\sample-resources\theme_dark.json") - - # Report Name - - "*item.metadata.json" = @{ - "type" = "report" - "displayName" = $reportName - } | ConvertTo-Json -} - -$reportId = Import-FabricItems -workspaceId $workspaceId -path $pbipReportPath -fileOverrides $fileReportOverrides - - - - - - diff --git a/Test/Test-DeployPBIP.ps1 b/Test/Test-DeployPBIP.ps1 deleted file mode 100644 index 8e8e8156..00000000 --- a/Test/Test-DeployPBIP.ps1 +++ /dev/null @@ -1,45 +0,0 @@ -$currentPath = (Split-Path $MyInvocation.MyCommand.Definition -Parent) - -Set-Location $currentPath - -Import-Module ".\FabricPS-PBIP" -Force - -$workspaceName = "RR - FabricAPIs - Deploy 6" -$datasetName = "Dataset A" -$reportName = "Report A" -$pbipPath = "$currentPath\SamplePBIP" - -# Ensure workspace exists - -$workspaceId = New-FabricWorkspace -name $workspaceName -skipErrorIfExists - -$fileOverrides = @{ - - "*.Dataset\item.metadata.json" = @{ - "type" = "dataset" - "displayName" = $datasetName - } | ConvertTo-Json - - "*.Report\item.metadata.json" = @{ - "type" = "report" - "displayName" = $reportName - } | ConvertTo-Json - - # Change logo - - "*_7abfc6c7-1a23-4b5f-bd8b-8dc472366284171093267.jpg" = [System.IO.File]::ReadAllBytes("$currentPath\sample-resources\logo2.jpg") - - # # Change theme - "*Light4437032645752863.json" = [System.IO.File]::ReadAllBytes("$currentPath\sample-resources\theme_dark.json") - -} - -# Import the PBIP to service - -Import-FabricItems -workspaceId $workspaceId -path $pbipPath -fileOverrides $fileOverrides - - - - - - diff --git a/Test/Test-Export.ps1 b/Test/Test-Export.ps1 deleted file mode 100644 index f871d922..00000000 --- a/Test/Test-Export.ps1 +++ /dev/null @@ -1,11 +0,0 @@ -$currentPath = (Split-Path $MyInvocation.MyCommand.Definition -Parent) - -Set-Location $currentPath - -Import-Module ".\FabricPS-PBIP" -Force - -# Set-FabricAuthToken -reset - -$workspaceId = "9e34f19a-dec9-4405-88a9-f09b8c99310f" - -Export-FabricItems -workspaceId $workspaceId -path '.\export' -filter {$_.id -eq "74ab67cc-1cfc-4275-8a96-1d03ef4e186b"} diff --git a/Test/Test-ExportForBPA.ps1 b/Test/Test-ExportForBPA.ps1 deleted file mode 100644 index 43b04f74..00000000 --- a/Test/Test-ExportForBPA.ps1 +++ /dev/null @@ -1,99 +0,0 @@ -$currentPath = (Split-Path $MyInvocation.MyCommand.Definition -Parent) - -Set-Location $currentPath - -Import-Module ".\FabricPS-PBIP" -Force - -#$workspaces = @("d036b3f6-049e-4757-b1cd-80ea88dfbac5", "409a6698-e92d-432c-9901-179107aecf03") -$workspaces = @("9e34f19a-dec9-4405-88a9-f09b8c99310f") -$exportLocation = "$currentPath\export\rules" - -# Download Tabular Editor - -$tabularEditorPath = "$currentPath\_tools\TE\TabularEditor.exe" -$tabularEditorRulesPath = "$currentPath\_tools\TE\rules.json" -if (!(Test-Path $tabularEditorPath)) -{ - $toolPath = "$currentPath\_tools\TE" - New-Item -ItemType Directory -Path $toolPath -ErrorAction SilentlyContinue | Out-Null - - Write-Host "Downloading Tabular Editor binaries" - $downloadUrl = "https://github.com/TabularEditor/TabularEditor/releases/latest/download/TabularEditor.Portable.zip" - $zipFile = "$toolPath\TabularEditor.zip" - Invoke-WebRequest -Uri $downloadUrl -OutFile $zipFile - Expand-Archive -Path $zipFile -DestinationPath $toolPath -Force - - Write-Host "Downloading Dataset default rules" - $downloadUrl = "https://raw.githubusercontent.com/microsoft/Analysis-Services/master/BestPracticeRules/BPARules.json" - Invoke-WebRequest -Uri $downloadUrl -OutFile $tabularEditorRulesPath -} - -# Download PBI Inspector - -$pbiInspectorPath = "$currentPath\_tools\PBIInspector\win-x64\CLI\PBIXInspectorCLI.exe" -$pbiInspectorRulesPath = "$currentPath\_tools\PBIInspector\rules.json" - -if (!(Test-Path $pbiInspectorPath)) -{ - $toolPath = "$currentPath\_Tools\PBIInspector" - New-Item -ItemType Directory -Path $toolPath -ErrorAction SilentlyContinue | Out-Null - - Write-Host "##[debug]Downloading PBI Inspector" - $downloadUrl = "https://github.com/NatVanG/PBI-Inspector/releases/latest/download/win-x64-CLI.zip" - $zipFile = "$toolPath\PBIXInspector.zip" - Invoke-WebRequest -Uri $downloadUrl -OutFile $zipFile - Expand-Archive -Path $zipFile -DestinationPath $toolPath -Force - - Write-Host "##[debug]Downloading Report default rules" - $downloadUrl = "https://raw.githubusercontent.com/NatVanG/PBI-Inspector/main/Rules/Base-rules.json" - Invoke-WebRequest -Uri $downloadUrl -OutFile $pbiInspectorRulesPath -} - -# Export Fabric content - -foreach($workspaceId in $workspaces) -{ - Export-FabricItems -workspaceId $workspaceId -path $exportLocation -} - -# Run Rules for each dataset and report and persist output in file - -$itemsFolders = Get-ChildItem -Path $exportLocation -recurse -include *.pbir, *.pbidataset - -$itemsFolders = $itemsFolders | Select-Object @{n="Order";e={ if ($_.Name -like "*.pbidataset") {1} else {2} }}, * | sort-object Order - -foreach ($itemFolder in $itemsFolders) { - - # Get the parent folder - - $itemFolderPath = $itemFolder.Directory.FullName - $workspaceId = $itemFolder.Directory.Parent.Name - - $itemMetadata = Get-Content "$itemFolderPath\item.metadata.json" | ConvertFrom-Json - - $itemName = $itemMetadata.displayName - $itemType = $itemMetadata.type - - Write-Host "Running rules for '$itemFolderPath' ($itemName - $itemType)" - - $toolOutputPath = "$exportLocation\rulesOutput\$($itemType)_$($workspaceId)_$($itemName).txt" - - New-Item -ItemType Directory -Path (Split-Path $toolOutputPath) -ErrorAction SilentlyContinue | Out-Null - - if ($itemType -in @("dataset", "SemanticModel")) - { - $modelPath = "$itemFolderPath\model.bim" - - Start-Process -FilePath "$tabularEditorPath" -ArgumentList """$modelPath"" -A ""$tabularEditorRulesPath"" -V" -NoNewWindow -Wait -RedirectStandardOutput $toolOutputPath - } - elseif ($itemType -ieq "report") - { - $reportPath = "$itemFolderPath\report.json" - - Start-Process -FilePath "$pbiInspectorPath" -ArgumentList "-pbipreport ""$reportPath"" -rules ""$pbiInspectorRulesPath"" -formats ""ADO""" -NoNewWindow -Wait -RedirectStandardOutput $toolOutputPath - } - else { - throw "Invalid item type: $itemType" - } - -} diff --git a/Test/Test-ExportImport.ps1 b/Test/Test-ExportImport.ps1 deleted file mode 100644 index 24614787..00000000 --- a/Test/Test-ExportImport.ps1 +++ /dev/null @@ -1,16 +0,0 @@ -$currentPath = (Split-Path $MyInvocation.MyCommand.Definition -Parent) - -Set-Location $currentPath - -Import-Module ".\FabricPS-PBIP" -Force - -$exportWorkspaceId = "d020f53d-eb41-421d-af50-8279882524f3" - -Export-FabricItems -workspaceId $exportWorkspaceId -path '.\export' - -$importWorkspaceId = "5bff05e0-355e-41d3-a776-08659726f396" - -#Remove-FabricItems -workspaceId "5bff05e0-355e-41d3-a776-08659726f396" - -Import-FabricItems -workspaceId $importWorkspaceId -path '.\export' - diff --git a/Test/Test-Import.ps1 b/Test/Test-Import.ps1 deleted file mode 100644 index ba07eed7..00000000 --- a/Test/Test-Import.ps1 +++ /dev/null @@ -1,9 +0,0 @@ -$currentPath = (Split-Path $MyInvocation.MyCommand.Definition -Parent) - -Set-Location $currentPath - -Import-Module ".\FabricPS-PBIP" -Force - -$workspaceId = "cdfc383c-5eaa-4f39-91de-0eb26fdd2401" - -Import-FabricItems -workspaceId $workspaceId -path '.\SamplePBIP' -filter "*Sales.Report*" \ No newline at end of file diff --git a/Test/Test-InvokeFabricRequest.ps1 b/Test/Test-InvokeFabricRequest.ps1 deleted file mode 100644 index 323d6db5..00000000 --- a/Test/Test-InvokeFabricRequest.ps1 +++ /dev/null @@ -1,11 +0,0 @@ -$currentPath = (Split-Path $MyInvocation.MyCommand.Definition -Parent) - -Set-Location $currentPath - -Import-Module ".\FabricPS-PBIP" -Force - -Invoke-FabricAPIRequest -uri "workspaces" - -Set-FabricAuthToken -reset - -Invoke-FabricAPIRequest -uri "workspaces" \ No newline at end of file diff --git a/Test/Test-ThemeSwap.ps1 b/Test/Test-ThemeSwap.ps1 deleted file mode 100644 index df14a612..00000000 --- a/Test/Test-ThemeSwap.ps1 +++ /dev/null @@ -1,44 +0,0 @@ -$currentPath = (Split-Path $MyInvocation.MyCommand.Definition -Parent) - -Set-Location $currentPath - -Import-Module ".\FabricPS-PBIP" -Force - -$workspaceId = "d020f53d-eb41-421d-af50-8279882524f3" -$newTheme = "$currentPath\sample-resources\Theme_dark.json" -$exportFolder = "$currentPath\exportThemeSwap" - -# Exports all reports from workspace - -Export-FabricItems -workspaceId $workspaceId -path $exportFolder -itemTypes @("report") - -# Only change reports with theme files - -$themeFiles = Get-ChildItem -Path $exportFolder -recurse |? { - - if ($_.FullName -like "*StaticResources\RegisteredResources\*.json") - { - $jsonContent = Get-Content $_.FullName | ConvertFrom-Json - - if ($jsonContent.'$schema' -ilike "*reportThemeSchema*") - { - return $true - } - } -} - -#Swap theme file and import reports - -foreach($themeFile in $themeFiles) -{ - Write-Host "Changing theme: '$themeFile'" - - $newThemeContent = Get-Content $newTheme - - $newThemeContent | Out-File $themeFile -Force - - $reportFolder = "$($themeFile.DirectoryName)\..\.." - - Import-FabricItems -workspaceId $workspaceId -path $reportFolder -} - diff --git a/Test/sample-resources/logo1.jpg b/Test/sample-resources/logo1.jpg deleted file mode 100644 index 00e5de2a..00000000 Binary files a/Test/sample-resources/logo1.jpg and /dev/null differ diff --git a/Test/sample-resources/logo2.jpg b/Test/sample-resources/logo2.jpg deleted file mode 100644 index 9962c151..00000000 Binary files a/Test/sample-resources/logo2.jpg and /dev/null differ diff --git a/azure-pipelines.yml b/azure-pipelines.yml new file mode 100644 index 00000000..897de014 --- /dev/null +++ b/azure-pipelines.yml @@ -0,0 +1,324 @@ +trigger: + branches: + include: + - It lets you pause and resume Fabric capacities. + paths: + exclude: + - CHANGELOG.md + tags: + include: + - "v*" + exclude: + - "*-*" + +variables: + buildFolderName: output + buildArtifactName: output + testResultFolderName: testResults + defaultBranch: It lets you pause and resume Fabric capacities. + Agent.Source.Git.ShallowFetchDepth: 0 # override ShallowFetchDepth + +stages: + - stage: Build + jobs: + - job: Package_Module + displayName: 'Package Module' + pool: + vmImage: 'ubuntu-latest' + steps: + - pwsh: | + dotnet tool install --global GitVersion.Tool --version 5.* + $gitVersionObject = dotnet-gitversion | ConvertFrom-Json + $gitVersionObject.PSObject.Properties.ForEach{ + Write-Host -Object "Setting Task Variable '$($_.Name)' with value '$($_.Value)'." + Write-Host -Object "##vso[task.setvariable variable=$($_.Name);]$($_.Value)" + } + Write-Host -Object "##vso[build.updatebuildnumber]$($gitVersionObject.FullSemVer)" + displayName: Calculate ModuleVersion (GitVersion) + + - task: PowerShell@2 + name: package + displayName: 'Build & Package Module' + inputs: + filePath: './build.ps1' + arguments: '-ResolveDependency -tasks pack' + pwsh: true + env: + ModuleVersion: $(NuGetVersionV2) + + - task: PublishPipelineArtifact@1 + displayName: 'Publish Build Artifact' + inputs: + targetPath: '$(buildFolderName)/' + artifact: $(buildArtifactName) + publishLocation: 'pipeline' + parallel: true + + - stage: Test + dependsOn: Build + jobs: + - job: test_linux + displayName: 'Linux' + timeoutInMinutes: 0 + pool: + vmImage: 'ubuntu-latest' + steps: + - task: DownloadPipelineArtifact@2 + displayName: 'Download Build Artifact' + inputs: + buildType: 'current' + artifactName: $(buildArtifactName) + targetPath: '$(Build.SourcesDirectory)/$(buildFolderName)' + + - task: PowerShell@2 + name: test + displayName: 'Run Tests' + inputs: + filePath: './build.ps1' + arguments: '-tasks test' + + - task: PublishTestResults@2 + displayName: 'Publish Test Results' + condition: succeededOrFailed() + inputs: + testResultsFormat: 'NUnit' + testResultsFiles: '$(buildFolderName)/$(testResultFolderName)/NUnit*.xml' + testRunTitle: 'Linux' + + - task: PublishPipelineArtifact@1 + displayName: 'Publish Test Artifact' + inputs: + targetPath: '$(buildFolderName)/$(testResultFolderName)/' + artifactName: 'CodeCoverageLinux' + parallel: true + + - job: test_windows_core + displayName: 'Windows (PowerShell)' + timeoutInMinutes: 0 + pool: + vmImage: 'windows-latest' + steps: + - task: DownloadPipelineArtifact@2 + displayName: 'Download Build Artifact' + inputs: + buildType: 'current' + artifactName: $(buildArtifactName) + targetPath: '$(Build.SourcesDirectory)/$(buildFolderName)' + + - task: PowerShell@2 + name: test + displayName: 'Run Tests' + inputs: + filePath: './build.ps1' + arguments: '-tasks test' + pwsh: true + + - task: PublishTestResults@2 + displayName: 'Publish Test Results' + condition: succeededOrFailed() + inputs: + testResultsFormat: 'NUnit' + testResultsFiles: '$(buildFolderName)/$(testResultFolderName)/NUnit*.xml' + testRunTitle: 'Windows (PowerShell)' + + - task: PublishPipelineArtifact@1 + displayName: 'Publish Test Artifact' + inputs: + targetPath: '$(buildFolderName)/$(testResultFolderName)/' + artifactName: 'CodeCoverageWinPS7' + parallel: true + + - job: test_windows_ps + displayName: 'Windows (Windows PowerShell)' + timeoutInMinutes: 0 + pool: + vmImage: 'windows-latest' + steps: + - task: DownloadPipelineArtifact@2 + displayName: 'Download Build Artifact' + inputs: + buildType: 'current' + artifactName: $(buildArtifactName) + targetPath: '$(Build.SourcesDirectory)/$(buildFolderName)' + + - task: PowerShell@2 + name: test + displayName: 'Run Tests' + inputs: + filePath: './build.ps1' + arguments: '-tasks test' + pwsh: false + + - task: PublishTestResults@2 + displayName: 'Publish Test Results' + condition: succeededOrFailed() + inputs: + testResultsFormat: 'NUnit' + testResultsFiles: '$(buildFolderName)/$(testResultFolderName)/NUnit*.xml' + testRunTitle: 'Windows (Windows PowerShell)' + + - task: PublishPipelineArtifact@1 + displayName: 'Publish Test Artifact' + inputs: + targetPath: '$(buildFolderName)/$(testResultFolderName)/' + artifactName: 'CodeCoverageWinPS51' + parallel: true + + - job: test_macos + displayName: 'macOS' + timeoutInMinutes: 0 + pool: + vmImage: 'macos-latest' + steps: + - task: DownloadPipelineArtifact@2 + displayName: 'Download Build Artifact' + inputs: + buildType: 'current' + artifactName: $(buildArtifactName) + targetPath: '$(Build.SourcesDirectory)/$(buildFolderName)' + + - task: PowerShell@2 + name: test + displayName: 'Run Tests' + inputs: + filePath: './build.ps1' + arguments: '-tasks test' + pwsh: true + + - task: PublishTestResults@2 + displayName: 'Publish Test Results' + condition: succeededOrFailed() + inputs: + testResultsFormat: 'NUnit' + testResultsFiles: '$(buildFolderName)/$(testResultFolderName)/NUnit*.xml' + testRunTitle: 'MacOS' + + - task: PublishPipelineArtifact@1 + displayName: 'Publish Test Artifact' + inputs: + targetPath: '$(buildFolderName)/$(testResultFolderName)/' + artifactName: 'CodeCoverageMacOS' + parallel: true + + # If no code coverage should be reported, then this entire removed: + - job: Code_Coverage + displayName: 'Publish Code Coverage' + dependsOn: + - test_macos + - test_linux + - test_windows_core + - test_windows_ps + pool: + vmImage: 'ubuntu-latest' + timeoutInMinutes: 0 + steps: + - pwsh: | + $repositoryOwner,$repositoryName = $env:BUILD_REPOSITORY_NAME -split '/' + echo "##vso[task.setvariable variable=RepositoryOwner;isOutput=true]$repositoryOwner" + echo "##vso[task.setvariable variable=RepositoryName;isOutput=true]$repositoryName" + name: dscBuildVariable + displayName: 'Set Environment Variables' + + - task: DownloadPipelineArtifact@2 + displayName: 'Download Pipeline Artifact' + inputs: + buildType: 'current' + artifactName: $(buildArtifactName) + targetPath: '$(Build.SourcesDirectory)/$(buildArtifactName)' + + - task: DownloadPipelineArtifact@2 + displayName: 'Download Test Artifact macOS' + inputs: + buildType: 'current' + artifactName: 'CodeCoverageMacOS' + targetPath: '$(Build.SourcesDirectory)/$(buildFolderName)/$(testResultFolderName)' + + - task: DownloadPipelineArtifact@2 + displayName: 'Download Test Artifact Linux' + inputs: + buildType: 'current' + artifactName: 'CodeCoverageLinux' + targetPath: '$(Build.SourcesDirectory)/$(buildFolderName)/$(testResultFolderName)' + + - task: DownloadPipelineArtifact@2 + displayName: 'Download Test Artifact Windows (PS 5.1)' + inputs: + buildType: 'current' + artifactName: 'CodeCoverageWinPS51' + targetPath: '$(Build.SourcesDirectory)/$(buildFolderName)/$(testResultFolderName)' + + - task: DownloadPipelineArtifact@2 + displayName: 'Download Test Artifact Windows (PS7)' + inputs: + buildType: 'current' + artifactName: 'CodeCoverageWinPS7' + targetPath: '$(Build.SourcesDirectory)/$(buildFolderName)/$(testResultFolderName)' + + # Make sure to update build.yaml to support these tasks, then uncomment these tasks: + #- task: PowerShell@2 + # name: merge + # displayName: 'Merge Code Coverage files' + # inputs: + # filePath: './build.ps1' + # arguments: '-tasks merge' + # pwsh: true + #- task: PublishCodeCoverageResults@1 + # displayName: 'Publish Azure Code Coverage' + # inputs: + # codeCoverageTool: 'JaCoCo' + # summaryFileLocation: '$(buildFolderName)/$(testResultFolderName)/JaCoCo_coverage.xml' + # pathToSources: '$(Build.SourcesDirectory)/$(dscBuildVariable.RepositoryName)/' + + # Uncomment if Codecov.io should be used (see docs at Codecov.io how to use and the required repository configuration). + #- script: | + # bash <(curl -s https://codecov.io/bash) -f "./$(buildFolderName)/$(testResultFolderName)/JaCoCo_coverage.xml" + # displayName: 'Publish Code Coverage to Codecov.io' + + - stage: Deploy + dependsOn: Test + # Only execute deploy stage if we're on master and previous stage succeeded + condition: | + and( + succeeded(), + or( + eq(variables['Build.SourceBranch'], 'refs/heads/ It lets you pause and resume Fabric capacities.'), + startsWith(variables['Build.SourceBranch'], 'refs/tags/') + ), + contains(variables['System.TeamFoundationCollectionUri'], ' Adds functionallity previously only available with the REST API as PowerShell functions.') + ) + jobs: + - job: Deploy_Module + displayName: 'Deploy Module' + pool: + vmImage: 'ubuntu-latest' + steps: + - task: DownloadPipelineArtifact@2 + displayName: 'Download Build Artifact' + inputs: + buildType: 'current' + artifactName: $(buildArtifactName) + targetPath: '$(Build.SourcesDirectory)/$(buildFolderName)' + - task: PowerShell@2 + name: publishRelease + displayName: 'Publish Release' + inputs: + filePath: './build.ps1' + arguments: '-tasks publish' + pwsh: true + env: + GitHubToken: $(GitHubToken) + GalleryApiToken: $(GalleryApiToken) + ReleaseBranch: $(defaultBranch) + MainGitBranch: $(defaultBranch) + - task: PowerShell@2 + name: sendChangelogPR + displayName: 'Send Changelog PR' + inputs: + filePath: './build.ps1' + arguments: '-tasks Create_ChangeLog_GitHub_PR' + pwsh: true + env: + GitHubToken: $(GitHubToken) + ReleaseBranch: $(defaultBranch) + MainGitBranch: $(defaultBranch) + diff --git a/build.ps1 b/build.ps1 new file mode 100644 index 00000000..f4a0faec --- /dev/null +++ b/build.ps1 @@ -0,0 +1,538 @@ +<# + .DESCRIPTION + Bootstrap and build script for PowerShell module CI/CD pipeline. + + .PARAMETER Tasks + The task or tasks to run. The default value is '.' (runs the default task). + + .PARAMETER CodeCoverageThreshold + The code coverage target threshold to uphold. Set to 0 to disable. + The default value is '' (empty string). + + .PARAMETER BuildConfig + Not yet written. + + .PARAMETER OutputDirectory + Specifies the folder to build the artefact into. The default value is 'output'. + + .PARAMETER BuiltModuleSubdirectory + Subdirectory name to build the module (under $OutputDirectory). The default + value is '' (empty string). + + .PARAMETER RequiredModulesDirectory + Can be a path (relative to $PSScriptRoot or absolute) to tell Resolve-Dependency + and PSDepend where to save the required modules. It is also possible to use + 'CurrentUser' och 'AllUsers' to install missing dependencies. You can override + the value for PSDepend in the Build.psd1 build manifest. The default value is + 'output/RequiredModules'. + + .PARAMETER PesterScript + One or more paths that will override the Pester configuration in build + configuration file when running the build task Invoke_Pester_Tests. + + If running Pester 5 test, use the alias PesterPath to be future-proof. + + .PARAMETER PesterTag + Filter which tags to run when invoking Pester tests. This is used in the + Invoke-Pester.pester.build.ps1 tasks. + + .PARAMETER PesterExcludeTag + Filter which tags to exclude when invoking Pester tests. This is used in + the Invoke-Pester.pester.build.ps1 tasks. + + .PARAMETER DscTestTag + Filter which tags to run when invoking DSC Resource tests. This is used + in the DscResource.Test.build.ps1 tasks. + + .PARAMETER DscTestExcludeTag + Filter which tags to exclude when invoking DSC Resource tests. This is + used in the DscResource.Test.build.ps1 tasks. + + .PARAMETER ResolveDependency + Not yet written. + + .PARAMETER BuildInfo + The build info object from ModuleBuilder. Defaults to an empty hashtable. + + .PARAMETER AutoRestore + Not yet written. + + .PARAMETER UseModuleFast + Specifies to use ModuleFast instead of PowerShellGet to resolve dependencies + faster. + + .PARAMETER UsePSResourceGet + Specifies to use PSResourceGet instead of PowerShellGet to resolve dependencies + faster. This can also be configured in Resolve-Dependency.psd1. + + .PARAMETER UsePowerShellGetCompatibilityModule + Specifies to use the compatibility module PowerShellGet. This parameter + only works then the method of downloading dependencies is PSResourceGet. + This can also be configured in Resolve-Dependency.psd1. +#> +[CmdletBinding()] +param +( + [Parameter(Position = 0)] + [System.String[]] + $Tasks = '.', + + [Parameter()] + [System.String] + $CodeCoverageThreshold = '', + + [Parameter()] + [System.String] + [ValidateScript( + { Test-Path -Path $_ } + )] + $BuildConfig, + + [Parameter()] + [System.String] + $OutputDirectory = 'output', + + [Parameter()] + [System.String] + $BuiltModuleSubdirectory = '', + + [Parameter()] + [System.String] + $RequiredModulesDirectory = $(Join-Path 'output' 'RequiredModules'), + + [Parameter()] + # This alias is to prepare for the rename of this parameter to PesterPath when Pester 4 support is removed + [Alias('PesterPath')] + [System.Object[]] + $PesterScript, + + [Parameter()] + [System.String[]] + $PesterTag, + + [Parameter()] + [System.String[]] + $PesterExcludeTag, + + [Parameter()] + [System.String[]] + $DscTestTag, + + [Parameter()] + [System.String[]] + $DscTestExcludeTag, + + [Parameter()] + [Alias('bootstrap')] + [System.Management.Automation.SwitchParameter] + $ResolveDependency, + + [Parameter(DontShow)] + [AllowNull()] + [System.Collections.Hashtable] + $BuildInfo, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $AutoRestore, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $UseModuleFast, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $UsePSResourceGet, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $UsePowerShellGetCompatibilityModule +) + +<# + The BEGIN block (at the end of this file) handles the Bootstrap of the Environment + before Invoke-Build can run the tasks if the parameter ResolveDependency (or + parameter alias Bootstrap) is specified. +#> + +process +{ + if ($MyInvocation.ScriptName -notLike '*Invoke-Build.ps1') + { + # Only run the process block through InvokeBuild (look at the Begin block at the bottom of this script). + return + } + + # Execute the Build process from the .build.ps1 path. + Push-Location -Path $PSScriptRoot -StackName 'BeforeBuild' + + try + { + Write-Host -Object "[build] Parsing defined tasks" -ForeGroundColor Magenta + + # Load the default BuildInfo if the parameter BuildInfo is not set. + if (-not $PSBoundParameters.ContainsKey('BuildInfo')) + { + try + { + if (Test-Path -Path $BuildConfig) + { + $configFile = Get-Item -Path $BuildConfig + + Write-Host -Object "[build] Loading Configuration from $configFile" + + $BuildInfo = switch -Regex ($configFile.Extension) + { + # Native Support for PSD1 + '\.psd1' + { + if (-not (Get-Command -Name Import-PowerShellDataFile -ErrorAction SilentlyContinue)) + { + Import-Module -Name Microsoft.PowerShell.Utility -RequiredVersion 3.1.0.0 + } + + Import-PowerShellDataFile -Path $BuildConfig + } + + # Support for yaml when module PowerShell-Yaml is available + '\.[yaml|yml]' + { + Import-Module -Name 'powershell-yaml' -ErrorAction Stop + + ConvertFrom-Yaml -Yaml (Get-Content -Raw $configFile) + } + + # Support for JSON and JSONC (by Removing comments) when module PowerShell-Yaml is available + '\.[json|jsonc]' + { + $jsonFile = Get-Content -Raw -Path $configFile + + $jsonContent = $jsonFile -replace '(?m)\s*//.*?$' -replace '(?ms)/\*.*?\*/' + + # Yaml is superset of JSON. + ConvertFrom-Yaml -Yaml $jsonContent + } + + # Unknown extension, return empty hashtable. + default + { + Write-Error -Message "Extension '$_' not supported. using @{}" + + @{ } + } + } + } + else + { + Write-Host -Object "Configuration file '$($BuildConfig.FullName)' not found" -ForegroundColor Red + + # No config file was found, return empty hashtable. + $BuildInfo = @{ } + } + } + catch + { + $logMessage = "Error loading Config '$($BuildConfig.FullName)'.`r`nAre you missing dependencies?`r`nMake sure you run './build.ps1 -ResolveDependency -tasks noop' before running build to restore the required modules." + + Write-Host -Object $logMessage -ForegroundColor Yellow + + $BuildInfo = @{ } + + Write-Error -Message $_.Exception.Message + } + } + + # If the Invoke-Build Task Header is specified in the Build Info, set it. + if ($BuildInfo.TaskHeader) + { + Set-BuildHeader -Script ([scriptblock]::Create($BuildInfo.TaskHeader)) + } + + <# + Add BuildModuleOutput to PSModule Path environment variable. + Moved here (not in begin block) because build file can contains BuiltSubModuleDirectory value. + #> + if ($BuiltModuleSubdirectory) + { + if (-not (Split-Path -IsAbsolute -Path $BuiltModuleSubdirectory)) + { + $BuildModuleOutput = Join-Path -Path $OutputDirectory -ChildPath $BuiltModuleSubdirectory + } + else + { + $BuildModuleOutput = $BuiltModuleSubdirectory + } + } # test if BuiltModuleSubDirectory set in build config file + elseif ($BuildInfo.ContainsKey('BuiltModuleSubDirectory')) + { + $BuildModuleOutput = Join-Path -Path $OutputDirectory -ChildPath $BuildInfo['BuiltModuleSubdirectory'] + } + else + { + $BuildModuleOutput = $OutputDirectory + } + + # Pre-pending $BuildModuleOutput folder to PSModulePath to resolve built module from this folder. + if ($powerShellModulePaths -notcontains $BuildModuleOutput) + { + Write-Host -Object "[build] Pre-pending '$BuildModuleOutput' folder to PSModulePath" -ForegroundColor Green + + $env:PSModulePath = $BuildModuleOutput + [System.IO.Path]::PathSeparator + $env:PSModulePath + } + + <# + Import Tasks from modules via their exported aliases when defined in Build Manifest. + https://github.com/nightroman/Invoke-Build/tree/master/Tasks/Import#example-2-import-from-a-module-with-tasks + #> + if ($BuildInfo.ContainsKey('ModuleBuildTasks')) + { + foreach ($module in $BuildInfo['ModuleBuildTasks'].Keys) + { + try + { + Write-Host -Object "Importing tasks from module $module" -ForegroundColor DarkGray + + $loadedModule = Import-Module -Name $module -PassThru -ErrorAction Stop + + foreach ($TaskToExport in $BuildInfo['ModuleBuildTasks'].($module)) + { + $loadedModule.ExportedAliases.GetEnumerator().Where{ + Write-Host -Object "`t Loading $($_.Key)..." -ForegroundColor DarkGray + + # Using -like to support wildcard. + $_.Key -like $TaskToExport + }.ForEach{ + # Dot-sourcing the Tasks via their exported aliases. + . (Get-Alias $_.Key) + } + } + } + catch + { + Write-Host -Object "Could not load tasks for module $module." -ForegroundColor Red + + Write-Error -Message $_ + } + } + } + + # Loading Build Tasks defined in the .build/ folder (will override the ones imported above if same task name). + Get-ChildItem -Path '.build/' -Recurse -Include '*.ps1' -ErrorAction Ignore | + ForEach-Object { + "Importing file $($_.BaseName)" | Write-Verbose + + . $_.FullName + } + + # Synopsis: Empty task, useful to test the bootstrap process. + task noop { } + + # Define default task sequence ("."), can be overridden in the $BuildInfo. + task . { + Write-Build -Object 'No sequence currently defined for the default task' -ForegroundColor Yellow + } + + Write-Host -Object 'Adding Workflow from configuration:' -ForegroundColor DarkGray + + # Load Invoke-Build task sequences/workflows from $BuildInfo. + foreach ($workflow in $BuildInfo.BuildWorkflow.keys) + { + Write-Verbose -Message "Creating Build Workflow '$Workflow' with tasks $($BuildInfo.BuildWorkflow.($Workflow) -join ', ')." + + $workflowItem = $BuildInfo.BuildWorkflow.($workflow) + + if ($workflowItem.Trim() -match '^\{(?[\w\W]*)\}$') + { + $workflowItem = [ScriptBlock]::Create($Matches['sb']) + } + + Write-Host -Object " +-> $workflow" -ForegroundColor DarkGray + + task $workflow $workflowItem + } + + Write-Host -Object "[build] Executing requested workflow: $($Tasks -join ', ')" -ForeGroundColor Magenta + + } + finally + { + Pop-Location -StackName 'BeforeBuild' + } +} + +begin +{ + # Find build config if not specified. + if (-not $BuildConfig) + { + $config = Get-ChildItem -Path "$PSScriptRoot\*" -Include 'build.y*ml', 'build.psd1', 'build.json*' -ErrorAction Ignore + + if (-not $config -or ($config -is [System.Array] -and $config.Length -le 0)) + { + throw 'No build configuration found. Specify path via parameter BuildConfig.' + } + elseif ($config -is [System.Array]) + { + if ($config.Length -gt 1) + { + throw 'More than one build configuration found. Specify which path to use via parameter BuildConfig.' + } + + $BuildConfig = $config[0] + } + else + { + $BuildConfig = $config + } + } + + # Bootstrapping the environment before using Invoke-Build as task runner + + if ($MyInvocation.ScriptName -notlike '*Invoke-Build.ps1') + { + Write-Host -Object "[pre-build] Starting Build Init" -ForegroundColor Green + + Push-Location $PSScriptRoot -StackName 'BuildModule' + } + + if ($RequiredModulesDirectory -in @('CurrentUser', 'AllUsers')) + { + # Installing modules instead of saving them. + Write-Host -Object "[pre-build] Required Modules will be installed to the PowerShell module path that is used for $RequiredModulesDirectory." -ForegroundColor Green + + <# + The variable $PSDependTarget will be used below when building the splatting + variable before calling Resolve-Dependency.ps1, unless overridden in the + file Resolve-Dependency.psd1. + #> + $PSDependTarget = $RequiredModulesDirectory + } + else + { + if (-not (Split-Path -IsAbsolute -Path $OutputDirectory)) + { + $OutputDirectory = Join-Path -Path $PSScriptRoot -ChildPath $OutputDirectory + } + + # Resolving the absolute path to save the required modules to. + if (-not (Split-Path -IsAbsolute -Path $RequiredModulesDirectory)) + { + $RequiredModulesDirectory = Join-Path -Path $PSScriptRoot -ChildPath $RequiredModulesDirectory + } + + # Create the output/modules folder if not exists, or resolve the Absolute path otherwise. + if (Resolve-Path -Path $RequiredModulesDirectory -ErrorAction SilentlyContinue) + { + Write-Debug -Message "[pre-build] Required Modules path already exist at $RequiredModulesDirectory" + + $requiredModulesPath = Convert-Path -Path $RequiredModulesDirectory + } + else + { + Write-Host -Object "[pre-build] Creating required modules directory $RequiredModulesDirectory." -ForegroundColor Green + + $requiredModulesPath = (New-Item -ItemType Directory -Force -Path $RequiredModulesDirectory).FullName + } + + $powerShellModulePaths = $env:PSModulePath -split [System.IO.Path]::PathSeparator + + # Pre-pending $requiredModulesPath folder to PSModulePath to resolve from this folder FIRST. + if ($RequiredModulesDirectory -notin @('CurrentUser', 'AllUsers') -and + ($powerShellModulePaths -notcontains $RequiredModulesDirectory)) + { + Write-Host -Object "[pre-build] Pre-pending '$RequiredModulesDirectory' folder to PSModulePath" -ForegroundColor Green + + $env:PSModulePath = $RequiredModulesDirectory + [System.IO.Path]::PathSeparator + $env:PSModulePath + } + + $powerShellYamlModule = Get-Module -Name 'powershell-yaml' -ListAvailable + $invokeBuildModule = Get-Module -Name 'InvokeBuild' -ListAvailable + $psDependModule = Get-Module -Name 'PSDepend' -ListAvailable + + # Checking if the user should -ResolveDependency. + if (-not ($powerShellYamlModule -and $invokeBuildModule -and $psDependModule) -and -not $ResolveDependency) + { + if ($AutoRestore -or -not $PSBoundParameters.ContainsKey('Tasks') -or $Tasks -contains 'build') + { + Write-Host -Object "[pre-build] Dependency missing, running './build.ps1 -ResolveDependency -Tasks noop' for you `r`n" -ForegroundColor Yellow + + $ResolveDependency = $true + } + else + { + Write-Warning -Message "Some required Modules are missing, make sure you first run with the '-ResolveDependency' parameter. Running 'build.ps1 -ResolveDependency -Tasks noop' will pull required modules without running the build task." + } + } + + <# + The variable $PSDependTarget will be used below when building the splatting + variable before calling Resolve-Dependency.ps1, unless overridden in the + file Resolve-Dependency.psd1. + #> + $PSDependTarget = $requiredModulesPath + } + + if ($ResolveDependency) + { + Write-Host -Object "[pre-build] Resolving dependencies using preferred method." -ForegroundColor Green + + $resolveDependencyParams = @{ } + + # If BuildConfig is a Yaml file, bootstrap powershell-yaml via ResolveDependency. + if ($BuildConfig -match '\.[yaml|yml]$') + { + $resolveDependencyParams.Add('WithYaml', $true) + } + + $resolveDependencyAvailableParams = (Get-Command -Name '.\Resolve-Dependency.ps1').Parameters.Keys + + foreach ($cmdParameter in $resolveDependencyAvailableParams) + { + # The parameter has been explicitly used for calling the .build.ps1 + if ($MyInvocation.BoundParameters.ContainsKey($cmdParameter)) + { + $paramValue = $MyInvocation.BoundParameters.Item($cmdParameter) + + Write-Debug " adding $cmdParameter :: $paramValue [from user-provided parameters to Build.ps1]" + + $resolveDependencyParams.Add($cmdParameter, $paramValue) + } + # Use defaults parameter value from Build.ps1, if any + else + { + $paramValue = Get-Variable -Name $cmdParameter -ValueOnly -ErrorAction Ignore + + if ($paramValue) + { + Write-Debug " adding $cmdParameter :: $paramValue [from default Build.ps1 variable]" + + $resolveDependencyParams.Add($cmdParameter, $paramValue) + } + } + } + + Write-Host -Object "[pre-build] Starting bootstrap process." -ForegroundColor Green + + .\Resolve-Dependency.ps1 @resolveDependencyParams + } + + if ($MyInvocation.ScriptName -notlike '*Invoke-Build.ps1') + { + Write-Verbose -Message "Bootstrap completed. Handing back to InvokeBuild." + + if ($PSBoundParameters.ContainsKey('ResolveDependency')) + { + Write-Verbose -Message "Dependency already resolved. Removing task." + + $null = $PSBoundParameters.Remove('ResolveDependency') + } + + Write-Host -Object "[build] Starting build with InvokeBuild." -ForegroundColor Green + + Invoke-Build @PSBoundParameters -Task $Tasks -File $MyInvocation.MyCommand.Path + + Pop-Location -StackName 'BuildModule' + + return + } +} diff --git a/build.yaml b/build.yaml new file mode 100644 index 00000000..64d9797d --- /dev/null +++ b/build.yaml @@ -0,0 +1,154 @@ +--- +#################################################### +# ModuleBuilder Configuration # +#################################################### +# Path to the Module Manifest to build (where path will be resolved from) +# SourcePath: ./Sampler/Sampler.psd1 +# Output Directory where ModuleBuilder will build the Module, relative to module manifest +# OutputDirectory: ../output/Sampler +BuiltModuleSubdirectory: module +CopyPaths: + - en-US + +Encoding: UTF8 +# Can be used to manually specify module's semantic version if the preferred method of +# using GitVersion is not available, and it is not possible to set the session environment +# variable `$env:ModuleVersion`, nor setting the variable `$ModuleVersion`, in the +# PowerShell session (parent scope) before running the task `build`. +#SemVer: '99.0.0-preview1' + +# Suffix to add to Root module PSM1 after merge (here, the Set-Alias exporting IB tasks) +# suffix: suffix.ps1 +# prefix: prefix.ps1 +VersionedOutputDirectory: true + +#################################################### +# ModuleBuilder Submodules Configuration # +#################################################### + +NestedModule: +# HelperSubmodule: # This is the first submodule to build into the output +# Path: ./*/Modules/HelperSubmodule/HelperSubmodule.psd1 +# # is trimmed (remove metadata & Prerelease tag) and OutputDirectory expanded (the only one) +# OutputDirectory: ///Modules/HelperSubmodule +# VersionedOutputDirectory: false +# AddToManifest: false +# SemVer: +# # suffix: +# # prefix: + +#################################################### +# Sampler Pipeline Configuration # +#################################################### +# Defining 'Workflows' (suite of InvokeBuild tasks) to be run using their alias +BuildWorkflow: + '.': # "." is the default Invoke-Build workflow. It is called when no -Tasks is specified to the build.ps1 + - build + - test + + build: + - Clean + - Build_Module_ModuleBuilder + - Build_NestedModules_ModuleBuilder + - Create_changelog_release_output + + + pack: + - build + - package_module_nupkg + + + + # Defining test task to be run when invoking `./build.ps1 -Tasks test` + test: + # Uncomment to modify the PSModulePath in the test pipeline (also requires the build configuration section SetPSModulePath). + #- Set_PSModulePath + - Pester_Tests_Stop_On_Fail + # Use this task if pipeline uses code coverage and the module is using the + # pattern of Public, Private, Enum, Classes. + #- Convert_Pester_Coverage + - Pester_if_Code_Coverage_Under_Threshold + + # Use this task when you have multiple parallel tests, which produce multiple + # code coverage files and needs to get merged into one file. + #merge: + #- Merge_CodeCoverage_Files + + publish: + - Publish_Release_To_GitHub # Runs first, if token is expired it will fail early + - Publish_GitHub_Wiki_Content + + - publish_module_to_gallery + +#################################################### +# PESTER Configuration # +#################################################### + +Pester: + OutputFormat: NUnitXML + # Excludes one or more paths from being used to calculate code coverage. + ExcludeFromCodeCoverage: + + # If no scripts are defined the default is to use all the tests under the project's + # tests folder or source folder (if present). Test script paths can be defined to + # only run tests in certain folders, or run specific test files, or can be use to + # specify the order tests are run. + Script: + # - tests/QA/module.tests.ps1 + # - tests/QA + # - tests/Unit + # - tests/Integration + ExcludeTag: + # - helpQuality + # - FunctionalQuality + # - TestQuality + Tag: + CodeCoverageThreshold: 0.35 # 85 # Set to 0 to bypass + #CodeCoverageOutputFile: JaCoCo_$OsShortName.xml + #CodeCoverageOutputFileEncoding: ascii + # Use this if code coverage should be merged from several pipeline test jobs. + # Any existing keys above should be replaced. See also CodeCoverage below. + # CodeCoverageOutputFile is the file that is created for each pipeline test job. + #CodeCoverageOutputFile: JaCoCo_Merge.xml + +# Use this to merged code coverage from several pipeline test jobs. +# CodeCoverageFilePattern - the pattern used to search all pipeline test job artifacts +# after the file specified in CodeCoverageOutputFile. +# CodeCoverageMergedOutputFile - the file that is created by the merge build task and +# is the file that should be uploaded to code coverage services. +#CodeCoverage: + #CodeCoverageFilePattern: JaCoCo_Merge.xml # the pattern used to search all pipeline test job artifacts + #CodeCoverageMergedOutputFile: JaCoCo_coverage.xml # the file that is created for the merged code coverage + +DscTest: + ExcludeTag: + - "Common Tests - New Error-Level Script Analyzer Rules" + Tag: + ExcludeSourceFile: + - output + ExcludeModuleFile: + - Modules/DscResource.Common + # - Templates + +# Import ModuleBuilder tasks from a specific PowerShell module using the build +# task's alias. Wildcard * can be used to specify all tasks that has a similar +# prefix and or suffix. The module contain the task must be added as a required +# module in the file RequiredModules.psd1. +ModuleBuildTasks: + Sampler: + - '*.build.Sampler.ib.tasks' + Sampler.GitHubTasks: + - '*.ib.tasks' + + +# Invoke-Build Header to be used to 'decorate' the terminal output of the tasks. +TaskHeader: | + param($Path) + "" + "=" * 79 + Write-Build Cyan "`t`t`t$($Task.Name.replace("_"," ").ToUpper())" + Write-Build DarkGray "$(Get-BuildSynopsis $Task)" + "-" * 79 + Write-Build DarkGray " $Path" + Write-Build DarkGray " $($Task.InvocationInfo.ScriptName):$($Task.InvocationInfo.ScriptLineNumber)" + "" diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 00000000..847b3d13 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,31 @@ +codecov: + require_ci_to_pass: no + # master should be the baseline for reporting + branch: It lets you pause and resume Fabric capacities. + +comment: + layout: "reach, diff, flags, files" + behavior: default + +coverage: + range: 50..80 + round: down + precision: 0 + + status: + project: + default: + # Set the overall project code coverage requirement to 70% + target: 70 + patch: + default: + # Set the pull request requirement to not regress overall coverage by more than 5% + # and let codecov.io set the goal for the code changed in the patch. + target: auto + threshold: 5 + +# Use this if there are paths that should not be part of the code coverage, for +# example a deprecated function where tests has been removed. +#ignore: +# - 'source/Public/Get-Deprecated.ps1' + diff --git a/documentation/Add-FabricWorkspaceRoleAssignment.md b/documentation/Add-FabricWorkspaceRoleAssignment.md deleted file mode 100644 index 0e79c1f8..00000000 --- a/documentation/Add-FabricWorkspaceRoleAssignment.md +++ /dev/null @@ -1,147 +0,0 @@ -# Add-FabricWorkspaceRoleAssignment - -## SYNOPSIS -Adds a role assignment to a user in a workspace. - -## SYNTAX - -``` -Add-FabricWorkspaceRoleAssignment [-WorkspaceId] [-principalId] [-Role] - [-ProgressAction ] [-WhatIf] [-Confirm] [] -``` - -## DESCRIPTION -Adds a role assignment to a user in a workspace. -The User is identified by the principalId and the role is -identified by the Role parameter. -The Workspace is identified by the WorkspaceId. - -## EXAMPLES - -### EXAMPLE 1 -``` -Add-RtiWorkspaceRoleAssignment ` - -WorkspaceId '12345678-1234-1234-1234-123456789012' ` - -PrincipalId '12345678-1234-1234-1234-123456789012' ` - -Role 'Admin' -``` - -## PARAMETERS - -### -principalId -Id of the principal for which the role assignment should be added. -The value for PrincipalId is a GUID. -An example of a GUID is '12345678-1234-1234-1234-123456789012'. -This parameter is mandatory. -At the -moment only principal type 'User' is supported. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: True -Position: 2 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -ProgressAction -{{ Fill ProgressAction Description }} - -```yaml -Type: System.Management.Automation.ActionPreference -Parameter Sets: (All) -Aliases: proga - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -Role -The role to assign to the principal. -The value for Role is a string. -An example of a string is 'Admin'. -The values that can be used are 'Admin', 'Contributor', 'Member' and 'Viewer'. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: True -Position: 3 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -WorkspaceId -Id of the Fabric Workspace for which the role assignment should be added. -The value for WorkspaceId is a GUID. -An example of a GUID is '12345678-1234-1234-1234-123456789012'. -This parameter is mandatory. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: Id - -Required: True -Position: 1 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -Confirm -Prompts you for confirmation before running the cmdlet. - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: cf - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -WhatIf -Shows what would happen if the cmdlet runs. -The cmdlet is not run. - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: wi - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -## OUTPUTS - -## NOTES -TODO: Add functionallity to add role assignments to groups. -TODO: Add functionallity to add a user by SPN. - -## RELATED LINKS - -[https://learn.microsoft.com/en-us/rest/api/fabric/core/workspaces/add-workspace-role-assignment?tabs=HTTP](https://learn.microsoft.com/en-us/rest/api/fabric/core/workspaces/add-workspace-role-assignment?tabs=HTTP) - diff --git a/documentation/Confirm-FabricAuthToken.md b/documentation/Confirm-FabricAuthToken.md deleted file mode 100644 index 7e118174..00000000 --- a/documentation/Confirm-FabricAuthToken.md +++ /dev/null @@ -1,54 +0,0 @@ -# Confirm-FabricAuthToken - -## SYNOPSIS -Check whether the Fabric API authentication token is set and not expired and reset it if necessary. - -## SYNTAX - -``` -Confirm-FabricAuthToken [-ProgressAction ] [] -``` - -## DESCRIPTION -The Confirm-FabricAuthToken function retrieves the Fabric API authentication token. -If the token is not already set, it calls the Set-FabricAuthToken function to set it. -It then outputs the token. - -## EXAMPLES - -### EXAMPLE 1 -``` -Confirm-FabricAuthToken -``` - -This command retrieves the Fabric API authentication token. - -## PARAMETERS - -### -ProgressAction -{{ Fill ProgressAction Description }} - -```yaml -Type: System.Management.Automation.ActionPreference -Parameter Sets: (All) -Aliases: proga - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -### None. You cannot pipe inputs to this function. -## OUTPUTS - -### Returns object as Get-FabricDebugInfo function -## NOTES - -## RELATED LINKS diff --git a/documentation/Connect-FabricAccount.md b/documentation/Connect-FabricAccount.md deleted file mode 100644 index ea2e9576..00000000 --- a/documentation/Connect-FabricAccount.md +++ /dev/null @@ -1,72 +0,0 @@ -# Connect-FabricAccount - -## SYNOPSIS -Connects to the Fabric WebAPI. - -## SYNTAX - -``` -Connect-FabricAccount [-TenantId] [-ProgressAction ] [] -``` - -## DESCRIPTION -Connects to the Fabric WebAPI by using the cmdlet Connect-AzAccount. -This function retrieves the authentication token for the Fabric API and sets up the headers for API calls. - -## EXAMPLES - -### EXAMPLE 1 -``` -Connect-FabricAccount ` - -TenantID '12345678-1234-1234-1234-123456789012' -``` - -## PARAMETERS - -### -ProgressAction -{{ Fill ProgressAction Description }} - -```yaml -Type: System.Management.Automation.ActionPreference -Parameter Sets: (All) -Aliases: proga - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -TenantId -The TenantId of the Azure Active Directory tenant you want to connect to -and in which your Fabric Capacity is. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: True -Position: 1 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -## OUTPUTS - -## NOTES -Revsion History: - -- 2024-12-22 - FGE: Added Verbose Output - -## RELATED LINKS - -[Connect-AzAccount https://learn.microsoft.com/de-de/powershell/module/az.accounts/connect-azaccount?view=azps-12.4.0]() - diff --git a/documentation/Export-FabricItem.md b/documentation/Export-FabricItem.md deleted file mode 100644 index 8b687676..00000000 --- a/documentation/Export-FabricItem.md +++ /dev/null @@ -1,128 +0,0 @@ -# Export-FabricItem - -## SYNOPSIS -Exports items from a Fabric workspace. -Either all items in a workspace or a specific item. - -## SYNTAX - -``` -Export-FabricItem [[-path] ] [[-workspaceId] ] [[-filter] ] [[-itemID] ] - [-ProgressAction ] [] -``` - -## DESCRIPTION -The Export-FabricItem function exports items from a Fabric workspace to a specified directory. -It can export items of type "Report", "SemanticModel", "SparkJobDefinitionV1" or "Notebook". -If a specific item ID is provided, only that item will be exported. -Otherwise, all items in the workspace will be exported. - -## EXAMPLES - -### EXAMPLE 1 -``` -Export-FabricItem -workspaceId "12345678-1234-1234-1234-1234567890AB" -path "C:\ExportedItems" -``` - -This example exports all items from the Fabric workspace with the specified ID to the "C:\ExportedItems" directory. - -### EXAMPLE 2 -``` -Export-FabricItem -workspaceId "12345678-1234-1234-1234-1234567890AB" -itemID "98765432-4321-4321-4321-9876543210BA" -path "C:\ExportedItems" -``` - -This example exports the item with the specified ID from the Fabric workspace with the specified ID to the "C:\ExportedItems" directory. - -## PARAMETERS - -### -filter -A script block used to filter the items to be exported. -Only items that match the filter will be exported. -The default filter includes items of type "Report", "SemanticModel", "SparkJobDefinitionV1" or "Notebook". - -```yaml -Type: System.Management.Automation.ScriptBlock -Parameter Sets: (All) -Aliases: - -Required: False -Position: 3 -Default value: { $_.type -in @("Report", "SemanticModel", "Notebook", "SparkJobDefinitionV1") } -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -itemID -The ID of the specific item to export. -If provided, only that item will be exported. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: False -Position: 4 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -path -The path to the directory where the items will be exported. -The default value is '.\pbipOutput'. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: False -Position: 1 -Default value: .\pbipOutput -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -ProgressAction -{{ Fill ProgressAction Description }} - -```yaml -Type: System.Management.Automation.ActionPreference -Parameter Sets: (All) -Aliases: proga - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -workspaceId -The ID of the Fabric workspace. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: False -Position: 2 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -## OUTPUTS - -## NOTES -This function is based on the Export-FabricItems function written by Rui Romano. -https://github.com/RuiRomano/fabricps-pbip - -## RELATED LINKS diff --git a/documentation/FabricTools.md b/documentation/FabricTools.md deleted file mode 100644 index 58d04acc..00000000 --- a/documentation/FabricTools.md +++ /dev/null @@ -1,193 +0,0 @@ ---- -Module Name: FabricTools -Module Guid: f2a0f9e6-fab6-41fc-9e1c-0c94ff38f794 -Download Help Link: {{ Update Download Link }} -Help Version: {{ Please enter version of help manually (X.X.X.X) format }} -Locale: en-US ---- - -# FabricTools Module -## Description -{{ Fill in the Description }} - -## FabricTools Cmdlets -### [Add-FabricWorkspaceRoleAssignment](Add-FabricWorkspaceRoleAssignment.md) -{{ Fill in the Description }} - -### [Confirm-FabricAuthToken](Confirm-FabricAuthToken.md) -{{ Fill in the Description }} - -### [Connect-FabricAccount](Connect-FabricAccount.md) -{{ Fill in the Description }} - -### [Export-FabricItem](Export-FabricItem.md) -{{ Fill in the Description }} - -### [Get-AllFabricCapacities](Get-AllFabricCapacities.md) -{{ Fill in the Description }} - -### [Get-AllFabricDatasetRefreshes](Get-AllFabricDatasetRefreshes.md) -{{ Fill in the Description }} - -### [Get-FabricAPIclusterURI](Get-FabricAPIclusterURI.md) -{{ Fill in the Description }} - -### [Get-FabricAuthToken](Get-FabricAuthToken.md) -{{ Fill in the Description }} - -### [Get-FabricCapacity](Get-FabricCapacity.md) -{{ Fill in the Description }} - -### [Get-FabricCapacityRefreshables](Get-FabricCapacityRefreshables.md) -{{ Fill in the Description }} - -### [Get-FabricCapacitySkus](Get-FabricCapacitySkus.md) -{{ Fill in the Description }} - -### [Get-FabricCapacityState](Get-FabricCapacityState.md) -{{ Fill in the Description }} - -### [Get-FabricCapacityTenantOverrides](Get-FabricCapacityTenantOverrides.md) -{{ Fill in the Description }} - -### [Get-FabricCapacityWorkload](Get-FabricCapacityWorkload.md) -{{ Fill in the Description }} - -### [Get-FabricDatasetRefreshes](Get-FabricDatasetRefreshes.md) -{{ Fill in the Description }} - -### [Get-FabricDebugInfo](Get-FabricDebugInfo.md) -{{ Fill in the Description }} - -### [Get-FabricEventhouse](Get-FabricEventhouse.md) -{{ Fill in the Description }} - -### [Get-FabricEventstream](Get-FabricEventstream.md) -{{ Fill in the Description }} - -### [Get-FabricItem](Get-FabricItem.md) -{{ Fill in the Description }} - -### [Get-FabricKQLDashboard](Get-FabricKQLDashboard.md) -{{ Fill in the Description }} - -### [Get-FabricKQLDashboardDefinition](Get-FabricKQLDashboardDefinition.md) -{{ Fill in the Description }} - -### [Get-FabricKQLDatabase](Get-FabricKQLDatabase.md) -{{ Fill in the Description }} - -### [Get-FabricKQLQueryset](Get-FabricKQLQueryset.md) -{{ Fill in the Description }} - -### [Get-FabricSQLDatabase](Get-FabricSQLDatabase.md) -{{ Fill in the Description }} - -### [Get-FabricTenantSettings](Get-FabricTenantSettings.md) -{{ Fill in the Description }} - -### [Get-FabricUsageMetricsQuery](Get-FabricUsageMetricsQuery.md) -{{ Fill in the Description }} - -### [Get-FabricWorkspace](Get-FabricWorkspace.md) -{{ Fill in the Description }} - -### [Get-FabricWorkspace2](Get-FabricWorkspace2.md) -{{ Fill in the Description }} - -### [Get-FabricWorkspaceDatasetRefreshes](Get-FabricWorkspaceDatasetRefreshes.md) -{{ Fill in the Description }} - -### [Get-FabricWorkspaceRoleAssignment](Get-FabricWorkspaceRoleAssignment.md) -{{ Fill in the Description }} - -### [Get-FabricWorkspaceUsageMetricsData](Get-FabricWorkspaceUsageMetricsData.md) -{{ Fill in the Description }} - -### [Get-FabricWorkspaceUsers](Get-FabricWorkspaceUsers.md) -{{ Fill in the Description }} - -### [Get-Sha256](Get-Sha256.md) -{{ Fill in the Description }} - -### [Import-FabricItem](Import-FabricItem.md) -{{ Fill in the Description }} - -### [Invoke-FabricAPIRequest](Invoke-FabricAPIRequest.md) -{{ Fill in the Description }} - -### [Invoke-FabricDatasetRefresh](Invoke-FabricDatasetRefresh.md) -{{ Fill in the Description }} - -### [Invoke-FabricKQLCommand](Invoke-FabricKQLCommand.md) -{{ Fill in the Description }} - -### [New-FabricEventhouse](New-FabricEventhouse.md) -{{ Fill in the Description }} - -### [New-FabricEventstream](New-FabricEventstream.md) -{{ Fill in the Description }} - -### [New-FabricKQLDashboard](New-FabricKQLDashboard.md) -{{ Fill in the Description }} - -### [New-FabricKQLDatabase](New-FabricKQLDatabase.md) -{{ Fill in the Description }} - -### [New-FabricWorkspace](New-FabricWorkspace.md) -{{ Fill in the Description }} - -### [New-FabricWorkspace2](New-FabricWorkspace2.md) -{{ Fill in the Description }} - -### [New-FabricWorkspaceUsageMetricsReport](New-FabricWorkspaceUsageMetricsReport.md) -{{ Fill in the Description }} - -### [Register-FabricWorkspaceToCapacity](Register-FabricWorkspaceToCapacity.md) -{{ Fill in the Description }} - -### [Remove-FabricEventhouse](Remove-FabricEventhouse.md) -{{ Fill in the Description }} - -### [Remove-FabricEventstream](Remove-FabricEventstream.md) -{{ Fill in the Description }} - -### [Remove-FabricItem](Remove-FabricItem.md) -{{ Fill in the Description }} - -### [Remove-FabricKQLDatabase](Remove-FabricKQLDatabase.md) -{{ Fill in the Description }} - -### [Remove-FabricKQLQueryset](Remove-FabricKQLQueryset.md) -{{ Fill in the Description }} - -### [Remove-FabricSQLDatabase](Remove-FabricSQLDatabase.md) -{{ Fill in the Description }} - -### [Remove-FabricWorkspace](Remove-FabricWorkspace.md) -{{ Fill in the Description }} - -### [Resume-FabricCapacity](Resume-FabricCapacity.md) -{{ Fill in the Description }} - -### [Set-FabricAuthToken](Set-FabricAuthToken.md) -{{ Fill in the Description }} - -### [Set-FabricEventhouse](Set-FabricEventhouse.md) -{{ Fill in the Description }} - -### [Set-FabricEventstream](Set-FabricEventstream.md) -{{ Fill in the Description }} - -### [Set-FabricKQLDatabase](Set-FabricKQLDatabase.md) -{{ Fill in the Description }} - -### [Set-FabricKQLQueryset](Set-FabricKQLQueryset.md) -{{ Fill in the Description }} - -### [Suspend-FabricCapacity](Suspend-FabricCapacity.md) -{{ Fill in the Description }} - -### [Unregister-FabricWorkspaceToCapacity](Unregister-FabricWorkspaceToCapacity.md) -{{ Fill in the Description }} - diff --git a/documentation/Get-AllFabricCapacities.md b/documentation/Get-AllFabricCapacities.md deleted file mode 100644 index dee563bf..00000000 --- a/documentation/Get-AllFabricCapacities.md +++ /dev/null @@ -1,75 +0,0 @@ -# Get-AllFabricCapacities - -## SYNOPSIS -This function retrieves all resources of type "Microsoft.Fabric/capacities" from all resource groups in a given subscription or all subscriptions if no subscription ID is provided. - -## SYNTAX - -``` -Get-AllFabricCapacities [[-subscriptionID] ] [-ProgressAction ] [] -``` - -## DESCRIPTION -The Get-AllFabricCapacities function is used to retrieve all resources of type "Microsoft.Fabric/capacities" from all resource groups in a given subscription or all subscriptions if no subscription ID is provided. -It uses the Az module to interact with Azure. - -## EXAMPLES - -### EXAMPLE 1 -``` -Get-AllFabricCapacities -subscriptionID "12345678-1234-1234-1234-123456789012" -``` - -This command retrieves all resources of type "Microsoft.Fabric/capacities" from all resource groups in the subscription with the ID "12345678-1234-1234-1234-123456789012". - -### EXAMPLE 2 -``` -Get-AllFabricCapacities -``` - -This command retrieves all resources of type "Microsoft.Fabric/capacities" from all resource groups in all subscriptions. - -## PARAMETERS - -### -ProgressAction -{{ Fill ProgressAction Description }} - -```yaml -Type: System.Management.Automation.ActionPreference -Parameter Sets: (All) -Aliases: proga - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -subscriptionID -An optional parameter that specifies the subscription ID. -If this parameter is not provided, the function will retrieve resources from all subscriptions. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: False -Position: 1 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -## OUTPUTS - -## NOTES -Alias: Get-AllFabCapacities - -## RELATED LINKS diff --git a/documentation/Get-AllFabricDatasetRefreshes.md b/documentation/Get-AllFabricDatasetRefreshes.md deleted file mode 100644 index 5d8be806..00000000 --- a/documentation/Get-AllFabricDatasetRefreshes.md +++ /dev/null @@ -1,35 +0,0 @@ -# Get-AllFabricDatasetRefreshes - -## SYNOPSIS -{{ Fill in the Synopsis }} - -## SYNTAX - -``` -Get-AllFabricDatasetRefreshes -``` - -## DESCRIPTION -{{ Fill in the Description }} - -## EXAMPLES - -### Example 1 -```powershell -PS C:\> {{ Add example code here }} -``` - -{{ Add example description here }} - -## PARAMETERS - -## INPUTS - -### None - -## OUTPUTS - -### System.Object -## NOTES - -## RELATED LINKS diff --git a/documentation/Get-FabricAPIclusterURI.md b/documentation/Get-FabricAPIclusterURI.md deleted file mode 100644 index 73e87835..00000000 --- a/documentation/Get-FabricAPIclusterURI.md +++ /dev/null @@ -1,35 +0,0 @@ -# Get-FabricAPIclusterURI - -## SYNOPSIS -{{ Fill in the Synopsis }} - -## SYNTAX - -``` -Get-FabricAPIclusterURI -``` - -## DESCRIPTION -{{ Fill in the Description }} - -## EXAMPLES - -### Example 1 -```powershell -PS C:\> {{ Add example code here }} -``` - -{{ Add example description here }} - -## PARAMETERS - -## INPUTS - -### None - -## OUTPUTS - -### System.Object -## NOTES - -## RELATED LINKS diff --git a/documentation/Get-FabricAuthToken.md b/documentation/Get-FabricAuthToken.md deleted file mode 100644 index 585352ea..00000000 --- a/documentation/Get-FabricAuthToken.md +++ /dev/null @@ -1,56 +0,0 @@ -# Get-FabricAuthToken - -## SYNOPSIS -Retrieves the Fabric API authentication token. - -## SYNTAX - -``` -Get-FabricAuthToken [-ProgressAction ] [] -``` - -## DESCRIPTION -The Get-FabricAuthToken function retrieves the Fabric API authentication token. -If the token is not already set, it calls the Set-FabricAuthToken function to set it. -It then outputs the token. - -## EXAMPLES - -### EXAMPLE 1 -``` -Get-FabricAuthToken -``` - -This command retrieves the Fabric API authentication token. - -## PARAMETERS - -### -ProgressAction -{{ Fill ProgressAction Description }} - -```yaml -Type: System.Management.Automation.ActionPreference -Parameter Sets: (All) -Aliases: proga - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -### None. You cannot pipe inputs to this function. -## OUTPUTS - -### String. This function returns the Fabric API authentication token. -## NOTES -This function was originally written by Rui Romano. -https://github.com/RuiRomano/fabricps-pbip - -## RELATED LINKS diff --git a/documentation/Get-FabricCapacity.md b/documentation/Get-FabricCapacity.md deleted file mode 100644 index bd4f32b8..00000000 --- a/documentation/Get-FabricCapacity.md +++ /dev/null @@ -1,68 +0,0 @@ -# Get-FabricCapacity - -## SYNOPSIS -Retrieves the fabric capacity information. - -## SYNTAX - -``` -Get-FabricCapacity [[-capacity] ] [-ProgressAction ] [] -``` - -## DESCRIPTION -This function makes a GET request to the Fabric API to retrieve the tenant settings. - -## EXAMPLES - -### EXAMPLE 1 -``` -Get-FabricCapacity -capacity "exampleCapacity" -Retrieves the fabric capacity information for the specified capacity. -``` - -Get-FabricCapacity -Retrieves the fabric capacity information for all capacities. - -## PARAMETERS - -### -capacity -Specifies the capacity to retrieve information for. -If not provided, all capacities will be retrieved. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: False -Position: 1 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -ProgressAction -{{ Fill ProgressAction Description }} - -```yaml -Type: System.Management.Automation.ActionPreference -Parameter Sets: (All) -Aliases: proga - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -## OUTPUTS - -## NOTES - -## RELATED LINKS diff --git a/documentation/Get-FabricCapacityRefreshables.md b/documentation/Get-FabricCapacityRefreshables.md deleted file mode 100644 index c61eefba..00000000 --- a/documentation/Get-FabricCapacityRefreshables.md +++ /dev/null @@ -1,68 +0,0 @@ -# Get-FabricCapacityRefreshables - -## SYNOPSIS -{{ Fill in the Synopsis }} - -## SYNTAX - -``` -Get-FabricCapacityRefreshables [[-top] ] [-ProgressAction ] [] -``` - -## DESCRIPTION -{{ Fill in the Description }} - -## EXAMPLES - -### Example 1 -```powershell -PS C:\> {{ Add example code here }} -``` - -{{ Add example description here }} - -## PARAMETERS - -### -ProgressAction -{{ Fill ProgressAction Description }} - -```yaml -Type: System.Management.Automation.ActionPreference -Parameter Sets: (All) -Aliases: proga - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -top -{{ Fill top Description }} - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: False -Position: 0 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -### None - -## OUTPUTS - -### System.Object -## NOTES - -## RELATED LINKS diff --git a/documentation/Get-FabricCapacitySkus.md b/documentation/Get-FabricCapacitySkus.md deleted file mode 100644 index bbf9de59..00000000 --- a/documentation/Get-FabricCapacitySkus.md +++ /dev/null @@ -1,96 +0,0 @@ -# Get-FabricCapacitySkus - -## SYNOPSIS -Retrieves the fabric capacity information. - -## SYNTAX - -``` -Get-FabricCapacitySkus [-subscriptionID] [-ResourceGroupName] [-capacity] - [-ProgressAction ] [] -``` - -## DESCRIPTION -This function makes a GET request to the Fabric API to retrieve the tenant settings. - -## EXAMPLES - -### EXAMPLE 1 -``` -Get-FabricCapacitySkus -capacity "exampleCapacity" -Retrieves the fabric capacity information for the specified capacity. -``` - -## PARAMETERS - -### -capacity -Specifies the capacity to retrieve information for. -If not provided, all capacities will be retrieved. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: True -Position: 3 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -ProgressAction -{{ Fill ProgressAction Description }} - -```yaml -Type: System.Management.Automation.ActionPreference -Parameter Sets: (All) -Aliases: proga - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -ResourceGroupName -{{ Fill ResourceGroupName Description }} - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: True -Position: 2 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -subscriptionID -{{ Fill subscriptionID Description }} - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: True -Position: 1 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -## OUTPUTS - -## NOTES - -## RELATED LINKS diff --git a/documentation/Get-FabricCapacityState.md b/documentation/Get-FabricCapacityState.md deleted file mode 100644 index 4c51e75b..00000000 --- a/documentation/Get-FabricCapacityState.md +++ /dev/null @@ -1,99 +0,0 @@ -# Get-FabricCapacityState - -## SYNOPSIS -{{ Fill in the Synopsis }} - -## SYNTAX - -``` -Get-FabricCapacityState [-subscriptionID] [-resourcegroup] [-capacity] - [-ProgressAction ] [] -``` - -## DESCRIPTION -{{ Fill in the Description }} - -## EXAMPLES - -### Example 1 -```powershell -PS C:\> {{ Add example code here }} -``` - -{{ Add example description here }} - -## PARAMETERS - -### -capacity -{{ Fill capacity Description }} - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: True -Position: 2 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -ProgressAction -{{ Fill ProgressAction Description }} - -```yaml -Type: System.Management.Automation.ActionPreference -Parameter Sets: (All) -Aliases: proga - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -resourcegroup -{{ Fill resourcegroup Description }} - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: True -Position: 1 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -subscriptionID -{{ Fill subscriptionID Description }} - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: True -Position: 0 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -### None - -## OUTPUTS - -### System.Object -## NOTES - -## RELATED LINKS diff --git a/documentation/Get-FabricCapacityTenantOverrides.md b/documentation/Get-FabricCapacityTenantOverrides.md deleted file mode 100644 index 01cd9e3b..00000000 --- a/documentation/Get-FabricCapacityTenantOverrides.md +++ /dev/null @@ -1,35 +0,0 @@ -# Get-FabricCapacityTenantOverrides - -## SYNOPSIS -{{ Fill in the Synopsis }} - -## SYNTAX - -``` -Get-FabricCapacityTenantOverrides -``` - -## DESCRIPTION -{{ Fill in the Description }} - -## EXAMPLES - -### Example 1 -```powershell -PS C:\> {{ Add example code here }} -``` - -{{ Add example description here }} - -## PARAMETERS - -## INPUTS - -### None - -## OUTPUTS - -### System.Object -## NOTES - -## RELATED LINKS diff --git a/documentation/Get-FabricCapacityWorkload.md b/documentation/Get-FabricCapacityWorkload.md deleted file mode 100644 index 125abf5a..00000000 --- a/documentation/Get-FabricCapacityWorkload.md +++ /dev/null @@ -1,68 +0,0 @@ -# Get-FabricCapacityWorkload - -## SYNOPSIS -{{ Fill in the Synopsis }} - -## SYNTAX - -``` -Get-FabricCapacityWorkload [-capacityID] [-ProgressAction ] [] -``` - -## DESCRIPTION -{{ Fill in the Description }} - -## EXAMPLES - -### Example 1 -```powershell -PS C:\> {{ Add example code here }} -``` - -{{ Add example description here }} - -## PARAMETERS - -### -capacityID -{{ Fill capacityID Description }} - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: True -Position: 0 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -ProgressAction -{{ Fill ProgressAction Description }} - -```yaml -Type: System.Management.Automation.ActionPreference -Parameter Sets: (All) -Aliases: proga - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -### None - -## OUTPUTS - -### System.Object -## NOTES - -## RELATED LINKS diff --git a/documentation/Get-FabricDatasetRefreshes.md b/documentation/Get-FabricDatasetRefreshes.md deleted file mode 100644 index bb1fe167..00000000 --- a/documentation/Get-FabricDatasetRefreshes.md +++ /dev/null @@ -1,88 +0,0 @@ -# Get-FabricDatasetRefreshes - -## SYNOPSIS -Retrieves the refresh history of a specified dataset in a PowerBI workspace. - -## SYNTAX - -``` -Get-FabricDatasetRefreshes [-DatasetID] [-workspaceId] [-ProgressAction ] - [] -``` - -## DESCRIPTION -The Get-FabricDatasetRefreshes function uses the PowerBI cmdlets to retrieve the refresh history of a specified dataset in a workspace. -It uses the dataset ID and workspace ID to get the dataset and checks if it is refreshable. -If it is, the function retrieves the refresh history. - -## EXAMPLES - -### EXAMPLE 1 -``` -Get-FabricDatasetRefreshes -DatasetID "12345678-90ab-cdef-1234-567890abcdef" -workspaceId "abcdef12-3456-7890-abcd-ef1234567890" -``` - -This command retrieves the refresh history of the specified dataset in the specified workspace. - -## PARAMETERS - -### -DatasetID -The ID of the dataset. -This is a mandatory parameter. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: True -Position: 1 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -ProgressAction -{{ Fill ProgressAction Description }} - -```yaml -Type: System.Management.Automation.ActionPreference -Parameter Sets: (All) -Aliases: proga - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -workspaceId -The ID of the workspace. -This is a mandatory parameter. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: True -Position: 2 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -### String. You can pipe two strings that contain the dataset ID and workspace ID to Get-FabricDatasetRefreshes. -## OUTPUTS - -### Object. Get-FabricDatasetRefreshes returns an object that contains the refresh history. -## NOTES -Alias: Get-PowerBIDatasetRefreshes, Get-FabricDatasetRefreshes - -## RELATED LINKS diff --git a/documentation/Get-FabricDebugInfo.md b/documentation/Get-FabricDebugInfo.md deleted file mode 100644 index a12d9afe..00000000 --- a/documentation/Get-FabricDebugInfo.md +++ /dev/null @@ -1,58 +0,0 @@ -# Get-FabricDebugInfo - -## SYNOPSIS -Shows internal debug information about the current session. - -## SYNTAX - -``` -Get-FabricDebugInfo [-ProgressAction ] [] -``` - -## DESCRIPTION -Shows internal debug information about the current session. -It is useful for troubleshooting purposes. -It will show you the current session object. -This includes the bearer token. -This can be useful -for connecting to the REST API directly via Postman. - -## EXAMPLES - -### EXAMPLE 1 -``` -Get-FabricDebugInfo -``` - -This example shows the current session object. - -## PARAMETERS - -### -ProgressAction -{{ Fill ProgressAction Description }} - -```yaml -Type: System.Management.Automation.ActionPreference -Parameter Sets: (All) -Aliases: proga - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -## OUTPUTS - -## NOTES -Revsion History: - -- 2024-12-22 - FGE: Added Verbose Output - -## RELATED LINKS diff --git a/documentation/Get-FabricEventhouse.md b/documentation/Get-FabricEventhouse.md deleted file mode 100644 index 1ee60c03..00000000 --- a/documentation/Get-FabricEventhouse.md +++ /dev/null @@ -1,148 +0,0 @@ -# Get-FabricEventhouse - -## SYNOPSIS -Retrieves Fabric Eventhouses - -## SYNTAX - -``` -Get-FabricEventhouse [-WorkspaceId] [[-EventhouseName] ] [[-EventhouseId] ] - [-ProgressAction ] [] -``` - -## DESCRIPTION -Retrieves Fabric Eventhouses. -Without the EventhouseName or EventhouseID parameter, all Eventhouses are returned. -If you want to retrieve a specific Eventhouse, you can use the EventhouseName or EventhouseID parameter. -These -parameters cannot be used together. - -## EXAMPLES - -### EXAMPLE 1 -``` -Get-FabricEventhouse ` - -WorkspaceId '12345678-1234-1234-1234-123456789012' -``` - -This example will give you all Eventhouses in the Workspace. - -### EXAMPLE 2 -``` -Get-FabricEventhouse ` - -WorkspaceId '12345678-1234-1234-1234-123456789012' ` - -EventhouseName 'MyEventhouse' -``` - -This example will give you all Information about the Eventhouse with the name 'MyEventhouse'. - -### EXAMPLE 3 -``` -Get-FabricEventhouse ` - -WorkspaceId '12345678-1234-1234-1234-123456789012' ` - -EventhouseId '12345678-1234-1234-1234-123456789012' -``` - -This example will give you all Information about the Eventhouse with the Id '12345678-1234-1234-1234-123456789012'. - -### EXAMPLE 4 -``` -Get-FabricEventhouse ` - -WorkspaceId '12345678-1234-1234-1234-123456789012' ` - -EventhouseId '12345678-1234-1234-1234-123456789012' ` - -Verbose -``` - -This example will give you all Information about the Eventhouse with the Id '12345678-1234-1234-1234-123456789012'. -It will also give you verbose output which is useful for debugging. - -## PARAMETERS - -### -EventhouseId -The Id of the Eventhouse to retrieve. -This parameter cannot be used together with EventhouseName. -The value for WorkspaceId is a GUID. -An example of a GUID is '12345678-1234-1234-1234-123456789012'. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: Id - -Required: False -Position: 3 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -EventhouseName -The name of the Eventhouse to retrieve. -This parameter cannot be used together with EventhouseID. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: Name, DisplayName - -Required: False -Position: 2 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -ProgressAction -{{ Fill ProgressAction Description }} - -```yaml -Type: System.Management.Automation.ActionPreference -Parameter Sets: (All) -Aliases: proga - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -WorkspaceId -Id of the Fabric Workspace for which the Eventhouses should be retrieved. -The value for WorkspaceId is a GUID. -An example of a GUID is '12345678-1234-1234-1234-123456789012'. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: True -Position: 1 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -## OUTPUTS - -## NOTES -TODO: Add functionality to list all Eventhouses in the subscription. -To do so fetch all workspaces -and then all eventhouses in each workspace. - -Revsion History: - -- 2024-11-09 - FGE: Added DisplaName as Alias for EventhouseName -- 2024-11-16 - FGE: Added Verbose Output -- 2024-11-27 - FGE: Added more Verbose Output - -## RELATED LINKS - -[https://learn.microsoft.com/en-us/rest/api/fabric/eventhouse/items/list-eventhouses?tabs=HTTP](https://learn.microsoft.com/en-us/rest/api/fabric/eventhouse/items/list-eventhouses?tabs=HTTP) - diff --git a/documentation/Get-FabricEventstream.md b/documentation/Get-FabricEventstream.md deleted file mode 100644 index 3568167f..00000000 --- a/documentation/Get-FabricEventstream.md +++ /dev/null @@ -1,135 +0,0 @@ -# Get-FabricEventstream - -## SYNOPSIS -Retrieves Fabric Eventstreams - -## SYNTAX - -``` -Get-FabricEventstream [-WorkspaceId] [[-EventstreamName] ] [[-EventstreamId] ] - [-ProgressAction ] [] -``` - -## DESCRIPTION -Retrieves Fabric Eventstreams. -Without the EventstreamName or EventstreamID parameter, all Eventstreams are returned. -If you want to retrieve a specific Eventstream, you can use the EventstreamName or EventstreamID parameter. -These -parameters cannot be used together. - -## EXAMPLES - -### EXAMPLE 1 -``` -Get-FabricEventstream ` - -WorkspaceId '12345678-1234-1234-1234-123456789012' -``` - -This example will give you all Eventstreams in the Workspace. - -### EXAMPLE 2 -``` -Get-FabricEventstream ` - -WorkspaceId '12345678-1234-1234-1234-123456789012' ` - -EventstreamName 'MyEventstream' -``` - -This example will give you all Information about the Eventstream with the name 'MyEventstream'. - -### EXAMPLE 3 -``` -Get-FabricEventstream ` - -WorkspaceId '12345678-1234-1234-1234-123456789012' ` - -EventstreamId '12345678-1234-1234-1234-123456789012' -``` - -This example will give you all Information about the Eventstream with the Id '12345678-1234-1234-1234-123456789012'. - -## PARAMETERS - -### -EventstreamId -The Id of the Eventstream to retrieve. -This parameter cannot be used together with EventstreamName. -The value for EventstreamId is a GUID. -An example of a GUID is '12345678-1234-1234-1234-123456789012'. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: Id - -Required: False -Position: 3 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -EventstreamName -The name of the Eventstream to retrieve. -This parameter cannot be used together with EventstreamID. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: Name, DisplayName - -Required: False -Position: 2 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -ProgressAction -{{ Fill ProgressAction Description }} - -```yaml -Type: System.Management.Automation.ActionPreference -Parameter Sets: (All) -Aliases: proga - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -WorkspaceId -Id of the Fabric Workspace for which the Eventstreams should be retrieved. -The value for WorkspaceId is a GUID. -An example of a GUID is '12345678-1234-1234-1234-123456789012'. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: True -Position: 1 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -## OUTPUTS - -## NOTES -TODO: Add functionality to list all Eventhouses. -To do so fetch all workspaces and - then all eventhouses in each workspace. - -Revision History: - - 2024-11-09 - FGE: Added DisplaName as Alias for EventStreamName - - 2024-11-27 - FGE: Added Verbose Output - -## RELATED LINKS - -[https://learn.microsoft.com/en-us/rest/api/fabric/eventstream/items/get-eventstream?tabs=HTTP](https://learn.microsoft.com/en-us/rest/api/fabric/eventstream/items/get-eventstream?tabs=HTTP) - diff --git a/documentation/Get-FabricItem.md b/documentation/Get-FabricItem.md deleted file mode 100644 index 322b5bd2..00000000 --- a/documentation/Get-FabricItem.md +++ /dev/null @@ -1,121 +0,0 @@ -# Get-FabricItem - -## SYNOPSIS -{{ Fill in the Synopsis }} - -## SYNTAX - -### WorkspaceId -``` -Get-FabricItem -workspaceId [-type ] [-itemID ] [-ProgressAction ] - [] -``` - -### WorkspaceObject -``` -Get-FabricItem -Workspace [-type ] [-itemID ] [-ProgressAction ] - [] -``` - -## DESCRIPTION -{{ Fill in the Description }} - -## EXAMPLES - -### Example 1 -```powershell -PS C:\> {{ Add example code here }} -``` - -{{ Add example description here }} - -## PARAMETERS - -### -itemID -{{ Fill itemID Description }} - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -ProgressAction -{{ Fill ProgressAction Description }} - -```yaml -Type: System.Management.Automation.ActionPreference -Parameter Sets: (All) -Aliases: proga - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -type -{{ Fill type Description }} - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: itemType - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -Workspace -{{ Fill Workspace Description }} - -```yaml -Type: System.Object -Parameter Sets: WorkspaceObject -Aliases: - -Required: True -Position: Named -Default value: None -Accept pipeline input: True (ByValue) -Accept wildcard characters: False -``` - -### -workspaceId -{{ Fill workspaceId Description }} - -```yaml -Type: System.String -Parameter Sets: WorkspaceId -Aliases: - -Required: True -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -### System.Object - -## OUTPUTS - -### System.Object -## NOTES - -## RELATED LINKS diff --git a/documentation/Get-FabricKQLDashboard.md b/documentation/Get-FabricKQLDashboard.md deleted file mode 100644 index 63cc86bf..00000000 --- a/documentation/Get-FabricKQLDashboard.md +++ /dev/null @@ -1,111 +0,0 @@ -# Get-FabricKQLDashboard - -## SYNOPSIS -Retrieves Fabric KQLDashboards - -## SYNTAX - -``` -Get-FabricKQLDashboard [-WorkspaceId] [[-KQLDashboardName] ] [[-KQLDashboardId] ] - [-ProgressAction ] [] -``` - -## DESCRIPTION -Retrieves Fabric KQLDashboards. -Without the KQLDashboardName or KQLDashboardID parameter, all KQLDashboards are returned. -If you want to retrieve a specific KQLDashboard, you can use the KQLDashboardName or KQLDashboardID parameter. -These -parameters cannot be used together. - -## EXAMPLES - -### EXAMPLE 1 -``` -Get-FabricKQLDashboard -``` - -## PARAMETERS - -### -KQLDashboardId -The Id of the KQLDashboard to retrieve. -This parameter cannot be used together with KQLDashboardName. -The value for KQLDashboardID is a GUID. -An example of a GUID is '12345678-1234-1234-1234-123456789012'. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: Id - -Required: False -Position: 3 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -KQLDashboardName -The name of the KQLDashboard to retrieve. -This parameter cannot be used together with KQLDashboardID. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: Name, DisplayName - -Required: False -Position: 2 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -ProgressAction -{{ Fill ProgressAction Description }} - -```yaml -Type: System.Management.Automation.ActionPreference -Parameter Sets: (All) -Aliases: proga - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -WorkspaceId -Id of the Fabric Workspace for which the KQLDashboards should be retrieved. -The value for WorkspaceId is a GUID. -An example of a GUID is '12345678-1234-1234-1234-123456789012'. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: True -Position: 1 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -## OUTPUTS - -## NOTES -TODO: Add functionality to list all KQLDashboards. -To do so fetch all workspaces and - then all KQLDashboards in each workspace. - -Revision History: - - 2024-11-09 - FGE: Added DisplaName as Alias for KQLDashboardName - - 2024-12-08 - FGE: Added Verbose Output - -## RELATED LINKS diff --git a/documentation/Get-FabricKQLDashboardDefinition.md b/documentation/Get-FabricKQLDashboardDefinition.md deleted file mode 100644 index 18caa1a3..00000000 --- a/documentation/Get-FabricKQLDashboardDefinition.md +++ /dev/null @@ -1,140 +0,0 @@ -# Get-FabricKQLDashboardDefinition - -## SYNOPSIS -Retrieves Fabric KQLDashboard Definitions for a given KQLDashboard. - -## SYNTAX - -``` -Get-FabricKQLDashboardDefinition [-WorkspaceId] [[-KQLDashboardName] ] - [[-KQLDashboardId] ] [[-Format] ] [-ProgressAction ] [] -``` - -## DESCRIPTION -Retrieves the Definition of the Fabric KQLDashboard that is specified by the KQLDashboardName or KQLDashboardID. -The KQLDashboard Definition contains the parts of the KQLDashboard, which are the visualizations and their configuration. -This is provided as a JSON object. - -## EXAMPLES - -### EXAMPLE 1 -``` -Get-FabricKQLDashboardDefinition ` - -WorkspaceId "12345678-1234-1234-1234-123456789012" ` - -KQLDashboardName "MyKQLDashboard" -``` - -This example retrieves the KQLDashboard Definition for the KQLDashboard named "MyKQLDashboard" in the -Workspace with the ID "12345678-1234-1234-1234-123456789012". - -### EXAMPLE 2 -``` -$db = Get-FabricKQLDashboardDefinition ` - -WorkspaceId "12345678-1234-1234-1234-123456789012" ` - -KQLDashboardName "MyKQLDashboard" -``` - -$db\[0\].payload | \` - Set-Content \` - -Path "C:\temp\mydashboard.json" - -This example retrieves the KQLDashboard Definition for the KQLDashboard named "MyKQLDashboard" in the -Workspace with the ID "12345678-1234-1234-1234-123456789012". -The definition is saved to a file named "mydashboard.json". - -## PARAMETERS - -### -Format -{{ Fill Format Description }} - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: False -Position: 4 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -KQLDashboardId -The Id of the KQLDashboard to retrieve. -This parameter cannot be used together with KQLDashboardName. -The value for KQLDashboardID is a GUID. -An example of a GUID is '12345678-1234-1234-1234-123456789012'. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: Id - -Required: False -Position: 3 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -KQLDashboardName -The name of the KQLDashboard to retrieve. -This parameter cannot be used together with KQLDashboardID. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: Name, DisplayName - -Required: False -Position: 2 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -ProgressAction -{{ Fill ProgressAction Description }} - -```yaml -Type: System.Management.Automation.ActionPreference -Parameter Sets: (All) -Aliases: proga - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -WorkspaceId -Id of the Fabric Workspace in which the KQLDashboard exists. -The value for WorkspaceId is a GUID. -An example of a GUID is '12345678-1234-1234-1234-123456789012'. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: True -Position: 1 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -## OUTPUTS - -## NOTES -Revision History: - - 2024-11-16 - FGE: First version - - 2024-12-08 - FGE: Added Verbose Output - -## RELATED LINKS diff --git a/documentation/Get-FabricKQLDatabase.md b/documentation/Get-FabricKQLDatabase.md deleted file mode 100644 index 16b7d1c6..00000000 --- a/documentation/Get-FabricKQLDatabase.md +++ /dev/null @@ -1,133 +0,0 @@ -# Get-FabricKQLDatabase - -## SYNOPSIS -Retrieves Fabric KQLDatabases - -## SYNTAX - -``` -Get-FabricKQLDatabase [-WorkspaceId] [[-KQLDatabaseName] ] [[-KQLDatabaseId] ] - [-ProgressAction ] [] -``` - -## DESCRIPTION -Retrieves Fabric KQLDatabases. -Without the KQLDatabaseName or KQLDatabaseID parameter, -all KQLDatabases are returned. -If you want to retrieve a specific KQLDatabase, you can -use the KQLDatabaseName or KQLDatabaseID parameter. -These parameters cannot be used together. - -## EXAMPLES - -### EXAMPLE 1 -``` -Get-FabricKQLDatabase ` - -WorkspaceId '12345678-1234-1234-1234-123456789012' ` - -KQLDatabaseName 'MyKQLDatabase' -``` - -This example will retrieve the KQLDatabase with the name 'MyKQLDatabase'. - -### EXAMPLE 2 -``` -Get-FabricKQLDatabase -``` - -This example will retrieve all KQLDatabases in the workspace that is specified -by the WorkspaceId. - -### EXAMPLE 3 -``` -Get-FabricKQLDatabase ` - -WorkspaceId '12345678-1234-1234-1234-123456789012' ` - -KQLDatabaseId '12345678-1234-1234-1234-123456789012' -``` - -This example will retrieve the KQLDatabase with the ID '12345678-1234-1234-1234-123456789012'. - -## PARAMETERS - -### -KQLDatabaseId -The Id of the KQLDatabase to retrieve. -This parameter cannot be used together with KQLDatabaseName. -The value for KQLDatabaseID is a GUID. -An example of a GUID is '12345678-1234-1234-1234-123456789012'. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: Id - -Required: False -Position: 3 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -KQLDatabaseName -The name of the KQLDatabase to retrieve. -This parameter cannot be used together with KQLDatabaseID. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: Name, DisplayName - -Required: False -Position: 2 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -ProgressAction -{{ Fill ProgressAction Description }} - -```yaml -Type: System.Management.Automation.ActionPreference -Parameter Sets: (All) -Aliases: proga - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -WorkspaceId -Id of the Fabric Workspace for which the KQLDatabases should be retrieved. -The value for WorkspaceId is a GUID. -An example of a GUID is '12345678-1234-1234-1234-123456789012'. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: True -Position: 1 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -## OUTPUTS - -## NOTES -TODO: Add functionality to list all KQLDatabases. -To do so fetch all workspaces and - then all KQLDatabases in each workspace. - -Revision History: - - 2024-11-09 - FGE: Added DisplaName as Alias for KQLDatabaseName - - 2024-12-08 - FGE: Added Verbose Output - -## RELATED LINKS diff --git a/documentation/Get-FabricKQLQueryset.md b/documentation/Get-FabricKQLQueryset.md deleted file mode 100644 index 699d6ef8..00000000 --- a/documentation/Get-FabricKQLQueryset.md +++ /dev/null @@ -1,136 +0,0 @@ -# Get-FabricKQLQueryset - -## SYNOPSIS -Retrieves Fabric KQLQuerysets - -## SYNTAX - -``` -Get-FabricKQLQueryset [-WorkspaceId] [[-KQLQuerysetName] ] [[-KQLQuerysetId] ] - [-ProgressAction ] [] -``` - -## DESCRIPTION -Retrieves Fabric KQLQuerysets. -Without the KQLQuerysetName or KQLQuerysetId parameter, -all KQLQuerysets are returned in the given Workspace. -If you want to retrieve a specific -KQLQueryset, you can use the KQLQuerysetName or KQLQuerysetId parameter. -These parameters -cannot be used together. - -## EXAMPLES - -### EXAMPLE 1 -``` -Get-FabricKQLQueryset ` - -WorkspaceId '12345678-1234-1234-1234-123456789012' ` - -KQLQuerysetName 'MyKQLQueryset' -``` - -This example will retrieve the KQLQueryset with the name 'MyKQLQueryset'. - -### EXAMPLE 2 -``` -Get-FabricKQLQueryset ` - -WorkspaceId '12345678-1234-1234-1234-123456789012' -``` - -This example will retrieve all KQLQuerysets in the workspace that is specified -by the WorkspaceId. - -### EXAMPLE 3 -``` -Get-FabricKQLQueryset ` - -WorkspaceId '12345678-1234-1234-1234-123456789012' ` - -KQLQuerysetId '12345678-1234-1234-1234-123456789012' -``` - -This example will retrieve the KQLQueryset with the ID '12345678-1234-1234-1234-123456789012'. - -## PARAMETERS - -### -KQLQuerysetId -The Id of the KQLQueryset to retrieve. -This parameter cannot be used together with KQLQuerysetName. -The value for KQLQuerysetId is a GUID. -An example of a GUID is '12345678-1234-1234-1234-123456789012'. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: Id - -Required: False -Position: 3 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -KQLQuerysetName -The name of the KQLQueryset to retrieve. -This parameter cannot be used together with KQLQuerysetId. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: Name, DisplayName - -Required: False -Position: 2 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -ProgressAction -{{ Fill ProgressAction Description }} - -```yaml -Type: System.Management.Automation.ActionPreference -Parameter Sets: (All) -Aliases: proga - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -WorkspaceId -Id of the Fabric Workspace for which the KQLQuerysets should be retrieved. -The value for WorkspaceId is a GUID. -An example of a GUID is '12345678-1234-1234-1234-123456789012'. -This parameter is mandatory. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: True -Position: 1 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -## OUTPUTS - -## NOTES -TODO: Add functionality to list all KQLQuerysets. -To do so fetch all workspaces and - then all KQLQuerysets in each workspace. - -Revision History: - - 2024-11-09 - FGE: Added DisplaName as Alias for KQLQuerysetName - - 2024-12-22 - FGE: Added Verbose Output - -## RELATED LINKS diff --git a/documentation/Get-FabricSQLDatabase.md b/documentation/Get-FabricSQLDatabase.md deleted file mode 100644 index dcea1818..00000000 --- a/documentation/Get-FabricSQLDatabase.md +++ /dev/null @@ -1,128 +0,0 @@ -# Get-FabricSQLDatabase - -## SYNOPSIS -Retrieves Fabric SQLDatabases - -## SYNTAX - -``` -Get-FabricSQLDatabase [-WorkspaceId] [[-SQLDatabaseName] ] [[-SQLDatabaseId] ] - [-ProgressAction ] [] -``` - -## DESCRIPTION -Retrieves Fabric SQLDatabases. -Without the SQLDatabaseName or SQLDatabaseID parameter, -all SQLDatabases are returned. -If you want to retrieve a specific SQLDatabase, you can -use the SQLDatabaseName or SQLDatabaseID parameter. -These parameters cannot be used together. - -## EXAMPLES - -### EXAMPLE 1 -``` -Get-FabricSQLDatabase ` - -WorkspaceId '12345678-1234-1234-1234-123456789012' ` - -SQLDatabaseName 'MySQLDatabase' -``` - -This example will retrieve the SQLDatabase with the name 'MySQLDatabase'. - -### EXAMPLE 2 -``` -Get-FabricSQLDatabase -``` - -This example will retrieve all SQLDatabases in the workspace that is specified -by the WorkspaceId. - -### EXAMPLE 3 -``` -Get-FabricSQLDatabase ` - -WorkspaceId '12345678-1234-1234-1234-123456789012' ` - -SQLDatabaseId '12345678-1234-1234-1234-123456789012' -``` - -This example will retrieve the SQLDatabase with the ID '12345678-1234-1234-1234-123456789012'. - -## PARAMETERS - -### -ProgressAction -{{ Fill ProgressAction Description }} - -```yaml -Type: System.Management.Automation.ActionPreference -Parameter Sets: (All) -Aliases: proga - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -SQLDatabaseId -The Id of the SQLDatabase to retrieve. -This parameter cannot be used together with SQLDatabaseName. -The value for SQLDatabaseID is a GUID. -An example of a GUID is '12345678-1234-1234-1234-123456789012'. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: Id - -Required: False -Position: 3 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -SQLDatabaseName -The name of the KQLDatabase to retrieve. -This parameter cannot be used together with SQLDatabaseID. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: Name, DisplayName - -Required: False -Position: 2 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -WorkspaceId -Id of the Fabric Workspace for which the SQLDatabases should be retrieved. -The value for WorkspaceId is a GUID. -An example of a GUID is '12345678-1234-1234-1234-123456789012'. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: True -Position: 1 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -## OUTPUTS - -## NOTES -Revision History: - - 2025-03-06 - KNO: Init version of the function - -## RELATED LINKS diff --git a/documentation/Get-FabricTenantSettings.md b/documentation/Get-FabricTenantSettings.md deleted file mode 100644 index 68ba030e..00000000 --- a/documentation/Get-FabricTenantSettings.md +++ /dev/null @@ -1,32 +0,0 @@ -# Get-FabricTenantSettings - -## SYNOPSIS -Retrieves the tenant settings from the Fabric API. - -## SYNTAX - -``` -Get-FabricTenantSettings -``` - -## DESCRIPTION -The Get-FabricTenantSettings function makes a GET request to the Fabric API to retrieve the tenant settings. -It returns the 'tenantSettings' property of the first item in the response. - -## EXAMPLES - -### EXAMPLE 1 -``` -Get-FabricTenantSettings -Retrieves the tenant settings from the Fabric API. -``` - -## PARAMETERS - -## INPUTS - -## OUTPUTS - -## NOTES - -## RELATED LINKS diff --git a/documentation/Get-FabricUsageMetricsQuery.md b/documentation/Get-FabricUsageMetricsQuery.md deleted file mode 100644 index d39f9840..00000000 --- a/documentation/Get-FabricUsageMetricsQuery.md +++ /dev/null @@ -1,114 +0,0 @@ -# Get-FabricUsageMetricsQuery - -## SYNOPSIS -{{ Fill in the Synopsis }} - -## SYNTAX - -``` -Get-FabricUsageMetricsQuery [-DatasetID] [-groupId] [-reportname] - [[-ImpersonatedUser] ] [-ProgressAction ] [] -``` - -## DESCRIPTION -{{ Fill in the Description }} - -## EXAMPLES - -### Example 1 -```powershell -PS C:\> {{ Add example code here }} -``` - -{{ Add example description here }} - -## PARAMETERS - -### -DatasetID -{{ Fill DatasetID Description }} - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: True -Position: 0 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -groupId -{{ Fill groupId Description }} - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: True -Position: 1 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -ImpersonatedUser -{{ Fill ImpersonatedUser Description }} - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: False -Position: 3 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -ProgressAction -{{ Fill ProgressAction Description }} - -```yaml -Type: System.Management.Automation.ActionPreference -Parameter Sets: (All) -Aliases: proga - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -reportname -{{ Fill reportname Description }} - -```yaml -Type: System.Object -Parameter Sets: (All) -Aliases: - -Required: True -Position: 2 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -### None - -## OUTPUTS - -### System.Object -## NOTES - -## RELATED LINKS diff --git a/documentation/Get-FabricWorkspace.md b/documentation/Get-FabricWorkspace.md deleted file mode 100644 index a1acc54a..00000000 --- a/documentation/Get-FabricWorkspace.md +++ /dev/null @@ -1,70 +0,0 @@ -# Get-FabricWorkspace - -## SYNOPSIS -Retrieves all Fabric workspaces. - -## SYNTAX - -``` -Get-FabricWorkspace [[-workspaceId] ] [-ProgressAction ] [] -``` - -## DESCRIPTION -The Get-FabricWorkspace function retrieves all Fabric workspaces. -It invokes the Fabric API to get the workspaces and outputs the result. - -## EXAMPLES - -### EXAMPLE 1 -``` -Get-FabricWorkspace -``` - -This command retrieves all Fabric workspaces. - -## PARAMETERS - -### -ProgressAction -{{ Fill ProgressAction Description }} - -```yaml -Type: System.Management.Automation.ActionPreference -Parameter Sets: (All) -Aliases: proga - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -workspaceId -{{ Fill workspaceId Description }} - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: False -Position: 1 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -### None. You cannot pipe inputs to this function. -## OUTPUTS - -### Object. This function returns the Fabric workspaces. -## NOTES -This function was originally written by Rui Romano. -https://github.com/RuiRomano/fabricps-pbip - -## RELATED LINKS diff --git a/documentation/Get-FabricWorkspace2.md b/documentation/Get-FabricWorkspace2.md deleted file mode 100644 index dd232248..00000000 --- a/documentation/Get-FabricWorkspace2.md +++ /dev/null @@ -1,198 +0,0 @@ -# Get-FabricWorkspace2 - -## SYNOPSIS -Retrieves Fabric Workspaces - -## SYNTAX - -``` -Get-FabricWorkspace2 [[-WorkspaceId] ] [[-WorkspaceName] ] [[-WorkspaceCapacityId] ] - [[-WorkspaceType] ] [[-WorkspaceState] ] [-ProgressAction ] - [] -``` - -## DESCRIPTION -Retrieves Fabric Workspaces. -Without the WorkspaceName or WorkspaceID parameter, -all Workspaces are returned. -If you want to retrieve a specific Workspace, you can -use the WorkspaceName, an CapacityID, a WorkspaceType, a WorkspaceState or the WorkspaceID -parameter. -The WorkspaceId parameter has precedence over all other parameters because it -is most specific. - -## EXAMPLES - -### EXAMPLE 1 -``` -Get-FabricWorkspace -``` - -This example will retrieve all Workspaces. - -### EXAMPLE 2 -``` -Get-FabricWorkspace ` - -WorkspaceId '12345678-1234-1234-1234-123456789012' -``` - -This example will retrieve the Workspace with the ID '12345678-1234-1234-1234-123456789012'. - -### EXAMPLE 3 -``` -Get-FabricWorkspace ` - -WorkspaceName 'MyWorkspace' -``` - -This example will retrieve the Workspace with the name 'MyWorkspace'. - -### EXAMPLE 4 -``` -Get-FabricWorkspace ` - -WorkspaceCapacityId '12345678-1234-1234-1234-123456789012' -``` - -This example will retrieve the Workspaces with the Capacity ID '12345678-1234-1234-1234-123456789012'. - -### EXAMPLE 5 -``` -Get-FabricWorkspace ` - -WorkspaceType 'Personal' -``` - -This example will retrieve the Workspaces with the type 'Personal'. - -### EXAMPLE 6 -``` -Get-FabricWorkspace ` - -WorkspaceState 'active' -``` - -This example will retrieve the Workspaces with the state 'active'. - -## PARAMETERS - -### -ProgressAction -{{ Fill ProgressAction Description }} - -```yaml -Type: System.Management.Automation.ActionPreference -Parameter Sets: (All) -Aliases: proga - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -WorkspaceCapacityId -The Id of the Capacity to retrieve. -This parameter cannot be used together with WorkspaceID. -The value for WorkspaceCapacityId is a GUID. -An example of a GUID is '12345678-1234-1234-1234-123456789012'. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: CapacityId - -Required: False -Position: 3 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -WorkspaceId -Id of the Fabric Workspace to retrieve. -The value for WorkspaceId is a GUID. -An example of a GUID is '12345678-1234-1234-1234-123456789012'. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: Id - -Required: False -Position: 1 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -WorkspaceName -The name of the Workspace to retrieve. -This parameter cannot be used together with WorkspaceID. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: Name - -Required: False -Position: 2 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -WorkspaceState -The state of the Workspace to retrieve. -This parameter cannot be used together with WorkspaceID. -The value for WorkspaceState is a string. -An example of a string is 'active'. -The values that -can be used are 'active' and 'deleted'. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: State - -Required: False -Position: 5 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -WorkspaceType -The type of the Workspace to retrieve. -This parameter cannot be used together with WorkspaceID. -The value for WorkspaceType is a string. -An example of a string is 'Personal'. -The values that -can be used are 'Personal', 'Workspace' and 'Adminworkspace'. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: Type - -Required: False -Position: 4 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -## OUTPUTS - -## NOTES -Revsion History: - -- 2024-12-22 - FGE: Added Verbose Output - -## RELATED LINKS - -[https://learn.microsoft.com/en-us/rest/api/fabric/admin/workspaces/get-workspace?tabs=HTTP](https://learn.microsoft.com/en-us/rest/api/fabric/admin/workspaces/get-workspace?tabs=HTTP) - -[https://learn.microsoft.com/en-us/rest/api/fabric/admin/workspaces/list-workspaces?tabs=HTTP](https://learn.microsoft.com/en-us/rest/api/fabric/admin/workspaces/list-workspaces?tabs=HTTP) - diff --git a/documentation/Get-FabricWorkspaceDatasetRefreshes.md b/documentation/Get-FabricWorkspaceDatasetRefreshes.md deleted file mode 100644 index e78fe968..00000000 --- a/documentation/Get-FabricWorkspaceDatasetRefreshes.md +++ /dev/null @@ -1,69 +0,0 @@ -# Get-FabricWorkspaceDatasetRefreshes - -## SYNOPSIS -{{ Fill in the Synopsis }} - -## SYNTAX - -``` -Get-FabricWorkspaceDatasetRefreshes [-WorkspaceID] [-ProgressAction ] - [] -``` - -## DESCRIPTION -{{ Fill in the Description }} - -## EXAMPLES - -### Example 1 -```powershell -PS C:\> {{ Add example code here }} -``` - -{{ Add example description here }} - -## PARAMETERS - -### -ProgressAction -{{ Fill ProgressAction Description }} - -```yaml -Type: System.Management.Automation.ActionPreference -Parameter Sets: (All) -Aliases: proga - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -WorkspaceID -{{ Fill WorkspaceID Description }} - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: True -Position: 0 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -### None - -## OUTPUTS - -### System.Object -## NOTES - -## RELATED LINKS diff --git a/documentation/Get-FabricWorkspaceRoleAssignment.md b/documentation/Get-FabricWorkspaceRoleAssignment.md deleted file mode 100644 index 063fd8c0..00000000 --- a/documentation/Get-FabricWorkspaceRoleAssignment.md +++ /dev/null @@ -1,75 +0,0 @@ -# Get-FabricWorkspaceRoleAssignment - -## SYNOPSIS -Retrieves Fabric Workspace Role Assignments - -## SYNTAX - -``` -Get-FabricWorkspaceRoleAssignment [[-WorkspaceId] ] [-ProgressAction ] - [] -``` - -## DESCRIPTION -Retrieves Fabric Workspace Role Assignments. -Without the WorkspaceName or WorkspaceID parameter, - -## EXAMPLES - -### EXAMPLE 1 -``` -Get-FabricWorkspaceRoleAssignment ` - -WorkspaceId '12345678-1234-1234-1234-123456789012' -``` - -This example will retrieve all Role Assignments for the Workspace with the ID '12345678-1234-1234-1234-123456789012'. - -## PARAMETERS - -### -ProgressAction -{{ Fill ProgressAction Description }} - -```yaml -Type: System.Management.Automation.ActionPreference -Parameter Sets: (All) -Aliases: proga - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -WorkspaceId -Id of the Fabric Workspace for which the Role Assignments should be retrieved. -The value for WorkspaceId is a GUID. -An example of a GUID is '12345678-1234-1234-1234-123456789012'. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: Id - -Required: False -Position: 1 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -## OUTPUTS - -## NOTES - -## RELATED LINKS - -[https://learn.microsoft.com/en-us/rest/api/fabric/core/workspaces/get-workspace-role-assignment?tabs=HTTP](https://learn.microsoft.com/en-us/rest/api/fabric/core/workspaces/get-workspace-role-assignment?tabs=HTTP) - -[https://learn.microsoft.com/en-us/rest/api/fabric/core/workspaces/list-workspace-role-assignments?tabs=HTTP](https://learn.microsoft.com/en-us/rest/api/fabric/core/workspaces/list-workspace-role-assignments?tabs=HTTP) - diff --git a/documentation/Get-FabricWorkspaceUsageMetricsData.md b/documentation/Get-FabricWorkspaceUsageMetricsData.md deleted file mode 100644 index 3620f978..00000000 --- a/documentation/Get-FabricWorkspaceUsageMetricsData.md +++ /dev/null @@ -1,84 +0,0 @@ -# Get-FabricWorkspaceUsageMetricsData - -## SYNOPSIS -{{ Fill in the Synopsis }} - -## SYNTAX - -``` -Get-FabricWorkspaceUsageMetricsData [-workspaceId] [[-username] ] - [-ProgressAction ] [] -``` - -## DESCRIPTION -{{ Fill in the Description }} - -## EXAMPLES - -### Example 1 -```powershell -PS C:\> {{ Add example code here }} -``` - -{{ Add example description here }} - -## PARAMETERS - -### -ProgressAction -{{ Fill ProgressAction Description }} - -```yaml -Type: System.Management.Automation.ActionPreference -Parameter Sets: (All) -Aliases: proga - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -username -{{ Fill username Description }} - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: False -Position: 1 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -workspaceId -{{ Fill workspaceId Description }} - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: True -Position: 0 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -### None - -## OUTPUTS - -### System.Object -## NOTES - -## RELATED LINKS diff --git a/documentation/Get-FabricWorkspaceUsers.md b/documentation/Get-FabricWorkspaceUsers.md deleted file mode 100644 index 611f94a6..00000000 --- a/documentation/Get-FabricWorkspaceUsers.md +++ /dev/null @@ -1,89 +0,0 @@ -# Get-FabricWorkspaceUsers - -## SYNOPSIS -{{ Fill in the Synopsis }} - -## SYNTAX - -### WorkspaceId -``` -Get-FabricWorkspaceUsers -WorkspaceId [-ProgressAction ] [] -``` - -### WorkspaceObject -``` -Get-FabricWorkspaceUsers -Workspace [-ProgressAction ] [] -``` - -## DESCRIPTION -{{ Fill in the Description }} - -## EXAMPLES - -### Example 1 -```powershell -PS C:\> {{ Add example code here }} -``` - -{{ Add example description here }} - -## PARAMETERS - -### -ProgressAction -{{ Fill ProgressAction Description }} - -```yaml -Type: System.Management.Automation.ActionPreference -Parameter Sets: (All) -Aliases: proga - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -Workspace -{{ Fill Workspace Description }} - -```yaml -Type: System.Object -Parameter Sets: WorkspaceObject -Aliases: - -Required: True -Position: Named -Default value: None -Accept pipeline input: True (ByValue) -Accept wildcard characters: False -``` - -### -WorkspaceId -{{ Fill WorkspaceId Description }} - -```yaml -Type: System.String -Parameter Sets: WorkspaceId -Aliases: - -Required: True -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -### System.Object - -## OUTPUTS - -### System.Object -## NOTES - -## RELATED LINKS diff --git a/documentation/Get-Sha256.md b/documentation/Get-Sha256.md deleted file mode 100644 index 664fb93f..00000000 --- a/documentation/Get-Sha256.md +++ /dev/null @@ -1,50 +0,0 @@ -# Get-Sha256 - -## SYNOPSIS -{{ Fill in the Synopsis }} - -## SYNTAX - -``` -Get-Sha256 [[-string] ] -``` - -## DESCRIPTION -{{ Fill in the Description }} - -## EXAMPLES - -### Example 1 -```powershell -PS C:\> {{ Add example code here }} -``` - -{{ Add example description here }} - -## PARAMETERS - -### -string -{{ Fill string Description }} - -```yaml -Type: System.Object -Parameter Sets: (All) -Aliases: - -Required: False -Position: 0 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -## INPUTS - -### None - -## OUTPUTS - -### System.Object -## NOTES - -## RELATED LINKS diff --git a/documentation/Import-FabricItem.md b/documentation/Import-FabricItem.md deleted file mode 100644 index 05e9dcba..00000000 --- a/documentation/Import-FabricItem.md +++ /dev/null @@ -1,116 +0,0 @@ -# Import-FabricItem - -## SYNOPSIS -Imports items using the Power BI Project format (PBIP) into a Fabric workspace from a specified file system source. - -## SYNTAX - -``` -Import-FabricItem [[-path] ] [[-workspaceId] ] [[-filter] ] - [[-fileOverrides] ] [-ProgressAction ] [] -``` - -## DESCRIPTION -{{ Fill in the Description }} - -## EXAMPLES - -### EXAMPLE 1 -``` -Import-FabricItems -path 'C:\PBIPFiles' -workspaceId '12345' -filter 'C:\PBIPFiles\Reports' -``` - -This example imports PBIP files from the 'C:\PBIPFiles' folder into the Fabric workspace with ID '12345'. -It only searches for PBIP files in the 'C:\PBIPFiles\Reports' folder. - -## PARAMETERS - -### -fileOverrides -This parameter let's you override a PBIP file without altering the local file. - -```yaml -Type: System.Collections.Hashtable -Parameter Sets: (All) -Aliases: - -Required: False -Position: 4 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -filter -A filter to limit the search for PBIP files to specific folders. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: False -Position: 3 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -path -The path to the PBIP files. -Default value is '.\pbipOutput'. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: False -Position: 1 -Default value: .\pbipOutput -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -ProgressAction -{{ Fill ProgressAction Description }} - -```yaml -Type: System.Management.Automation.ActionPreference -Parameter Sets: (All) -Aliases: proga - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -workspaceId -The ID of the Fabric workspace. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: False -Position: 2 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -## OUTPUTS - -## NOTES -This function requires the Invoke-FabricAPIRequest function to be available in the current session. -This function was originally written by Rui Romano. -https://github.com/RuiRomano/fabricps-pbip - -## RELATED LINKS diff --git a/documentation/Invoke-FabricAPIRequest.md b/documentation/Invoke-FabricAPIRequest.md deleted file mode 100644 index 7abb350d..00000000 --- a/documentation/Invoke-FabricAPIRequest.md +++ /dev/null @@ -1,158 +0,0 @@ -# Invoke-FabricAPIRequest - -## SYNOPSIS -Sends an HTTP request to a Fabric API endpoint and retrieves the response. -Takes care of: authentication, 429 throttling, Long-Running-Operation (LRO) response - -## SYNTAX - -``` -Invoke-FabricAPIRequest [[-authToken] ] [-uri] [[-method] ] [[-body] ] - [[-contentType] ] [[-timeoutSec] ] [[-retryCount] ] [-ProgressAction ] - [] -``` - -## DESCRIPTION -{{ Fill in the Description }} - -## EXAMPLES - -### Example 1 -```powershell -PS C:\> {{ Add example code here }} -``` - -{{ Add example description here }} - -## PARAMETERS - -### -authToken -{{ Fill authToken Description }} - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: False -Position: 1 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -body -{{ Fill body Description }} - -```yaml -Type: System.Object -Parameter Sets: (All) -Aliases: - -Required: False -Position: 4 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -contentType -{{ Fill contentType Description }} - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: False -Position: 5 -Default value: Application/json; charset=utf-8 -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -method -{{ Fill method Description }} - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: False -Position: 3 -Default value: Get -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -ProgressAction -{{ Fill ProgressAction Description }} - -```yaml -Type: System.Management.Automation.ActionPreference -Parameter Sets: (All) -Aliases: proga - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -retryCount -{{ Fill retryCount Description }} - -```yaml -Type: System.Int32 -Parameter Sets: (All) -Aliases: - -Required: False -Position: 7 -Default value: 0 -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -timeoutSec -{{ Fill timeoutSec Description }} - -```yaml -Type: System.Int32 -Parameter Sets: (All) -Aliases: - -Required: False -Position: 6 -Default value: 240 -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -uri -{{ Fill uri Description }} - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: True -Position: 2 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -## OUTPUTS - -## NOTES - -## RELATED LINKS diff --git a/documentation/Invoke-FabricDatasetRefresh.md b/documentation/Invoke-FabricDatasetRefresh.md deleted file mode 100644 index d25d448b..00000000 --- a/documentation/Invoke-FabricDatasetRefresh.md +++ /dev/null @@ -1,70 +0,0 @@ -# Invoke-FabricDatasetRefresh - -## SYNOPSIS -This function invokes a refresh of a PowerBI dataset - -## SYNTAX - -``` -Invoke-FabricDatasetRefresh -DatasetID [-ProgressAction ] [] -``` - -## DESCRIPTION -The Invoke-FabricDatasetRefresh function is used to refresh a PowerBI dataset. -It first checks if the dataset is refreshable. -If it is not, it writes an error. -If it is, it invokes a PowerBI REST method to refresh the dataset. -The URL for the request is constructed using the provided dataset ID. - -## EXAMPLES - -### EXAMPLE 1 -``` -Invoke-FabricDatasetRefresh -DatasetID "12345678-1234-1234-1234-123456789012" -``` - -This command invokes a refresh of the dataset with the ID "12345678-1234-1234-1234-123456789012" - -## PARAMETERS - -### -DatasetID -A mandatory parameter that specifies the dataset ID. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: True -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -ProgressAction -{{ Fill ProgressAction Description }} - -```yaml -Type: System.Management.Automation.ActionPreference -Parameter Sets: (All) -Aliases: proga - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -## OUTPUTS - -## NOTES -Alias: Invoke-FabDatasetRetresh - -## RELATED LINKS diff --git a/documentation/Invoke-FabricKQLCommand.md b/documentation/Invoke-FabricKQLCommand.md deleted file mode 100644 index c5a48ed6..00000000 --- a/documentation/Invoke-FabricKQLCommand.md +++ /dev/null @@ -1,129 +0,0 @@ -# Invoke-FabricKQLCommand - -## SYNOPSIS -{{ Fill in the Synopsis }} - -## SYNTAX - -``` -Invoke-FabricKQLCommand [-WorkspaceId] [[-KQLDatabaseName] ] [[-KQLDatabaseId] ] - [-KQLCommand] [-ReturnRawResult] [-ProgressAction ] [] -``` - -## DESCRIPTION -{{ Fill in the Description }} - -## EXAMPLES - -### Example 1 -```powershell -PS C:\> {{ Add example code here }} -``` - -{{ Add example description here }} - -## PARAMETERS - -### -KQLCommand -{{ Fill KQLCommand Description }} - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: True -Position: 3 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -KQLDatabaseId -{{ Fill KQLDatabaseId Description }} - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: False -Position: 2 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -KQLDatabaseName -{{ Fill KQLDatabaseName Description }} - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: False -Position: 1 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -ProgressAction -{{ Fill ProgressAction Description }} - -```yaml -Type: System.Management.Automation.ActionPreference -Parameter Sets: (All) -Aliases: proga - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -ReturnRawResult -{{ Fill ReturnRawResult Description }} - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -WorkspaceId -{{ Fill WorkspaceId Description }} - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: True -Position: 0 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -### None - -## OUTPUTS - -### System.Object -## NOTES - -## RELATED LINKS diff --git a/documentation/New-FabricEventhouse.md b/documentation/New-FabricEventhouse.md deleted file mode 100644 index c7588752..00000000 --- a/documentation/New-FabricEventhouse.md +++ /dev/null @@ -1,152 +0,0 @@ -# New-FabricEventhouse - -## SYNOPSIS -Creates a new Fabric Eventhouse - -## SYNTAX - -``` -New-FabricEventhouse [-WorkspaceID] [-EventhouseName] [[-EventhouseDescription] ] - [-ProgressAction ] [-WhatIf] [-Confirm] [] -``` - -## DESCRIPTION -Creates a new Fabric Eventhouse - -## EXAMPLES - -### EXAMPLE 1 -``` -New-FabricEventhouse ` - -WorkspaceID '12345678-1234-1234-1234-123456789012' ` - -EventhouseName 'MyEventhouse' ` - -EventhouseDescription 'This is my Eventhouse' -``` - -This example will create a new Eventhouse with the name 'MyEventhouse' and the description 'This is my Eventhouse'. - -### EXAMPLE 2 -``` -New-FabricEventhouse ` - -WorkspaceID '12345678-1234-1234-1234-123456789012' ` - -EventhouseName 'MyEventhouse' ` - -EventhouseDescription 'This is my Eventhouse' ` - -Verbose -``` - -This example will create a new Eventhouse with the name 'MyEventhouse' and the description 'This is my Eventhouse'. -It will also give you verbose output which is useful for debugging. - -## PARAMETERS - -### -EventhouseDescription -The description of the Eventhouse to create. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: Description - -Required: False -Position: 3 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -EventhouseName -The name of the Eventhouse to create. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: Name, DisplayName - -Required: True -Position: 2 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -ProgressAction -{{ Fill ProgressAction Description }} - -```yaml -Type: System.Management.Automation.ActionPreference -Parameter Sets: (All) -Aliases: proga - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -WorkspaceID -Id of the Fabric Workspace for which the Eventhouse should be created. -The value for WorkspaceID is a GUID. -An example of a GUID is '12345678-1234-1234-1234-123456789012'. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: True -Position: 1 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -Confirm -Prompts you for confirmation before running the cmdlet. - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: cf - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -WhatIf -Shows what would happen if the cmdlet runs. -The cmdlet is not run. - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: wi - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -## OUTPUTS - -## NOTES -Revsion History: - -- 2024-11-07 - FGE: Implemented SupportShouldProcess -- 2024-11-09 - FGE: Added DisplaName as Alias for EventhouseName -- 2024-11-27 - FGE: Added Verbose Output - -## RELATED LINKS - -[https://learn.microsoft.com/en-us/rest/api/fabric/eventhouse/items/create-eventhouse?tabs=HTTP](https://learn.microsoft.com/en-us/rest/api/fabric/eventhouse/items/create-eventhouse?tabs=HTTP) - diff --git a/documentation/New-FabricEventstream.md b/documentation/New-FabricEventstream.md deleted file mode 100644 index fa7129d8..00000000 --- a/documentation/New-FabricEventstream.md +++ /dev/null @@ -1,140 +0,0 @@ -# New-FabricEventstream - -## SYNOPSIS -Creates a new Fabric Eventstream - -## SYNTAX - -``` -New-FabricEventstream [-WorkspaceID] [-EventstreamName] [[-EventstreamDescription] ] - [-ProgressAction ] [-WhatIf] [-Confirm] [] -``` - -## DESCRIPTION -Creates a new Fabric Eventstream - -## EXAMPLES - -### EXAMPLE 1 -``` -New-FabricEventstream - -WorkspaceID '12345678-1234-1234-1234-123456789012' - -EventstreamName 'MyEventstream' - -EventstreamDescription 'This is my Eventstream' -``` - -This example will create a new Eventstream with the name 'MyEventstream' and the description 'This is my Eventstream'. - -## PARAMETERS - -### -EventstreamDescription -The description of the Eventstream to create. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: Description - -Required: False -Position: 3 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -EventstreamName -The name of the Eventstream to create. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: Name, DisplayName - -Required: True -Position: 2 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -ProgressAction -{{ Fill ProgressAction Description }} - -```yaml -Type: System.Management.Automation.ActionPreference -Parameter Sets: (All) -Aliases: proga - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -WorkspaceID -Id of the Fabric Workspace for which the Eventstream should be created. -The value for WorkspaceID is a GUID. -An example of a GUID is '12345678-1234-1234-1234-123456789012'. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: True -Position: 1 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -Confirm -Prompts you for confirmation before running the cmdlet. - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: cf - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -WhatIf -Shows what would happen if the cmdlet runs. -The cmdlet is not run. - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: wi - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -## OUTPUTS - -## NOTES -Revsion History: - -- 2024-11-07 - FGE: Implemented SupportShouldProcess -- 2024-11-09 - FGE: Added DisplaName as Alias for EventStreamName -- 2024-11-30 - FGE: Added Verbose Output - -## RELATED LINKS - -[https://learn.microsoft.com/en-us/rest/api/fabric/eventstream/items/create-eventstream?tabs=HTTP](https://learn.microsoft.com/en-us/rest/api/fabric/eventstream/items/create-eventstream?tabs=HTTP) - diff --git a/documentation/New-FabricKQLDashboard.md b/documentation/New-FabricKQLDashboard.md deleted file mode 100644 index 91e66ac5..00000000 --- a/documentation/New-FabricKQLDashboard.md +++ /dev/null @@ -1,138 +0,0 @@ -# New-FabricKQLDashboard - -## SYNOPSIS -Creates a new Fabric KQLDashboard - -## SYNTAX - -``` -New-FabricKQLDashboard [-WorkspaceID] [-KQLDashboardName] - [[-KQLDashboardDescription] ] [-ProgressAction ] [-WhatIf] [-Confirm] - [] -``` - -## DESCRIPTION -Creates a new Fabric KQLDashboard - -## EXAMPLES - -### EXAMPLE 1 -``` -New-FabricDashboard ` - -WorkspaceID '12345678-1234-1234-1234-123456789012' ` - -KQLDashboardName 'MyKQLDashboard' ` - -KQLDashboardDescription 'This is my KQLDashboard' -``` - -This example will create a new KQLDashboard with the name 'MyKQLDashboard' and the description 'This is my KQLDashboard'. - -## PARAMETERS - -### -KQLDashboardDescription -The description of the KQLDashboard to create. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: Description - -Required: False -Position: 3 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -KQLDashboardName -The name of the KQLDashboard to create. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: Name, DisplayName - -Required: True -Position: 2 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -ProgressAction -{{ Fill ProgressAction Description }} - -```yaml -Type: System.Management.Automation.ActionPreference -Parameter Sets: (All) -Aliases: proga - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -WorkspaceID -Id of the Fabric Workspace for which the KQLDashboard should be created. -The value for WorkspaceID is a GUID. -An example of a GUID is '12345678-1234-1234-1234-123456789012'. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: True -Position: 1 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -Confirm -Prompts you for confirmation before running the cmdlet. - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: cf - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -WhatIf -Shows what would happen if the cmdlet runs. -The cmdlet is not run. - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: wi - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -## OUTPUTS - -## NOTES -Revsion History: - -- 2024-11-07 - FGE: Implemented SupportShouldProcess -- 2024-11-09 - FGE: Added DisplaName as Alias for KQLDashboardName -- 2024-12-08 - FGE: Added Verbose Output - -## RELATED LINKS diff --git a/documentation/New-FabricKQLDatabase.md b/documentation/New-FabricKQLDatabase.md deleted file mode 100644 index a5b16581..00000000 --- a/documentation/New-FabricKQLDatabase.md +++ /dev/null @@ -1,160 +0,0 @@ -# New-FabricKQLDatabase - -## SYNOPSIS -Creates a new Fabric KQLDatabase - -## SYNTAX - -``` -New-FabricKQLDatabase [-WorkspaceID] [-EventhouseID] [-KQLDatabaseName] - [[-KQLDatabaseDescription] ] [-ProgressAction ] [-WhatIf] [-Confirm] - [] -``` - -## DESCRIPTION -Creates a new Fabric KQLDatabase. -The KQLDatabase is created in the specified Workspace and Eventhouse. -It will be created with the specified name and description. - -## EXAMPLES - -### EXAMPLE 1 -``` -New-FabricKQLDatabase ` - -WorkspaceID '12345678-1234-1234-1234-123456789012' ` - -EventhouseID '12345678-1234-1234-1234-123456789012' ` - -KQLDatabaseName 'MyKQLDatabase' ` - -KQLDatabaseDescription 'This is my KQLDatabase' -``` - -This example will create a new KQLDatabase with the name 'MyKQLDatabase' and the description 'This is my KQLDatabase'. - -## PARAMETERS - -### -EventhouseID -Id of the Fabric Eventhouse for which the KQLDatabase should be created. -The value for EventhouseID is a GUID. -An example of a GUID is '12345678-1234-1234-1234-123456789012'. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: True -Position: 2 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -KQLDatabaseDescription -The description of the KQLDatabase to create. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: Description - -Required: False -Position: 4 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -KQLDatabaseName -The name of the KQLDatabase to create. -The name must be unique within the eventhouse and is a -mandatory parameter. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: Name, DisplayName - -Required: True -Position: 3 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -ProgressAction -{{ Fill ProgressAction Description }} - -```yaml -Type: System.Management.Automation.ActionPreference -Parameter Sets: (All) -Aliases: proga - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -WorkspaceID -Id of the Fabric Workspace for which the KQLDatabase should be created. -The value for WorkspaceID is a GUID. -An example of a GUID is '12345678-1234-1234-1234-123456789012'. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: True -Position: 1 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -Confirm -Prompts you for confirmation before running the cmdlet. - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: cf - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -WhatIf -Shows what would happen if the cmdlet runs. -The cmdlet is not run. - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: wi - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -## OUTPUTS - -## NOTES -Revsion History: - -- 2024-11-07 - FGE: Implemented SupportShouldProcess -- 2024-11-09 - FGE: Added DisplaName as Alias for KQLDatabaseName -- 2024-12-08 - FGE: Added Verbose Output - -## RELATED LINKS diff --git a/documentation/New-FabricLakehouse.md b/documentation/New-FabricLakehouse.md deleted file mode 100644 index c6527acd..00000000 --- a/documentation/New-FabricLakehouse.md +++ /dev/null @@ -1,149 +0,0 @@ -# New-FabricLakehouse - -## SYNOPSIS -Creates a new Fabric Lakehouse - -## SYNTAX - -``` -New-FabricLakehouse [-WorkspaceID] [-LakehouseName] [-LakehouseSchemaEnabled] [[-LakehouseDescription] ] - [-ProgressAction ] [-WhatIf] [-Confirm] [] -``` - -## DESCRIPTION -Creates a new Fabric Lakehouse - -## EXAMPLES - -### EXAMPLE 1 -``` -New-FabricLakehouse ` - -WorkspaceID '12345678-1234-1234-1234-123456789012' ` - -LakehouseName 'MyLakehouse' ` - -LakehouseSchemaEnabled $true ` - -LakehouseDescription 'This is my Lakehouse' -``` - -This example will create a new Lakehouse with the name 'MyLakehouse' and the description 'This is my Lakehouse'. - -### EXAMPLE 2 -``` -New-FabricLakehouse ` - -WorkspaceID '12345678-1234-1234-1234-123456789012' ` - -LakehouseName 'MyLakehouse' ` - -LakehouseSchemaEnabled $true ` - -LakehouseDescription 'This is my Lakehouse' ` - -Verbose -``` - -This example will create a new Lakehouse with the name 'MyLakehouse' and the description 'This is my MyLakehouse'. -It will also give you verbose output which is useful for debugging. - -## PARAMETERS - -### -EventhouseDescription -The description of the Eventhouse to create. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: Description - -Required: False -Position: 3 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -EventhouseName -The name of the Eventhouse to create. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: Name, DisplayName - -Required: True -Position: 2 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -ProgressAction -{{ Fill ProgressAction Description }} - -```yaml -Type: System.Management.Automation.ActionPreference -Parameter Sets: (All) -Aliases: proga - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -WorkspaceID -Id of the Fabric Workspace for which the Eventhouse should be created. -The value for WorkspaceID is a GUID. -An example of a GUID is '12345678-1234-1234-1234-123456789012'. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: True -Position: 1 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -Confirm -Prompts you for confirmation before running the cmdlet. - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: cf - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -WhatIf -Shows what would happen if the cmdlet runs. -The cmdlet is not run. - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: wi - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -## OUTPUTS - -## NOTES - -## RELATED LINKS - -[https://learn.microsoft.com/en-us/rest/api/fabric/eventhouse/items/create-eventhouse?tabs=HTTP](https://learn.microsoft.com/en-us/rest/api/fabric/eventhouse/items/create-eventhouse?tabs=HTTP) - diff --git a/documentation/New-FabricWorkspace.md b/documentation/New-FabricWorkspace.md deleted file mode 100644 index e604ceb9..00000000 --- a/documentation/New-FabricWorkspace.md +++ /dev/null @@ -1,121 +0,0 @@ -# New-FabricWorkspace - -## SYNOPSIS -Creates a new Fabric workspace. - -## SYNTAX - -``` -New-FabricWorkspace [[-name] ] [-skipErrorIfExists] [-ProgressAction ] [-WhatIf] - [-Confirm] [] -``` - -## DESCRIPTION -The New-FabricWorkspace function creates a new Fabric workspace. -It uses the provided name to create the workspace. -If the workspace already exists and the skipErrorIfExists switch is provided, it does not throw an error. - -## EXAMPLES - -### EXAMPLE 1 -``` -New-FabricWorkspace -Name "NewWorkspace" -SkipErrorIfExists -``` - -This command creates a new Fabric workspace named "NewWorkspace". -If the workspace already exists, it does not throw an error. - -## PARAMETERS - -### -name -The name of the new Fabric workspace. -This is a mandatory parameter. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: False -Position: 1 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -ProgressAction -{{ Fill ProgressAction Description }} - -```yaml -Type: System.Management.Automation.ActionPreference -Parameter Sets: (All) -Aliases: proga - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -skipErrorIfExists -A switch that indicates whether to skip the error if the workspace already exists. -If provided, the function does not throw an error if the workspace already exists. - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: False -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -Confirm -Prompts you for confirmation before running the cmdlet. - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: cf - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -WhatIf -Shows what would happen if the cmdlet runs. -The cmdlet is not run. - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: wi - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -### String, Switch. You can pipe a string that contains the name and a switch that indicates whether to skip the error if the workspace already exists to New-FabricWorkspace. -## OUTPUTS - -### String. This function returns the ID of the new Fabric workspace. -## NOTES -This function was originally written by Rui Romano. -https://github.com/RuiRomano/fabricps-pbip - -## RELATED LINKS diff --git a/documentation/New-FabricWorkspace2.md b/documentation/New-FabricWorkspace2.md deleted file mode 100644 index 88e3d6a8..00000000 --- a/documentation/New-FabricWorkspace2.md +++ /dev/null @@ -1,136 +0,0 @@ -# New-FabricWorkspace2 - -## SYNOPSIS -Creates a new Fabric Workspace - -## SYNTAX - -``` -New-FabricWorkspace2 [-CapacityId] [-WorkspaceName] [[-WorkspaceDescription] ] - [-ProgressAction ] [-WhatIf] [-Confirm] [] -``` - -## DESCRIPTION -Creates a new Fabric Workspace - -## EXAMPLES - -### EXAMPLE 1 -``` -New-FabricWorkspace ` - -CapacityID '12345678-1234-1234-1234-123456789012' ` - -WorkspaceName 'TestWorkspace' ` - -WorkspaceDescription 'This is a Test Workspace' -``` - -This example will create a new Workspace with the name 'TestWorkspace' and the description 'This is a test workspace'. - -## PARAMETERS - -### -CapacityId -Id of the Fabric Capacity for which the Workspace should be created. -The value for CapacityID is a GUID. -An example of a GUID is '12345678-1234-1234-1234-123456789012'. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: True -Position: 1 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -ProgressAction -{{ Fill ProgressAction Description }} - -```yaml -Type: System.Management.Automation.ActionPreference -Parameter Sets: (All) -Aliases: proga - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -WorkspaceDescription -The description of the Workspace to create. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: Description - -Required: False -Position: 3 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -WorkspaceName -The name of the Workspace to create. -This parameter is mandatory. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: Name, DisplayName - -Required: True -Position: 2 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -Confirm -Prompts you for confirmation before running the cmdlet. - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: cf - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -WhatIf -Shows what would happen if the cmdlet runs. -The cmdlet is not run. - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: wi - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -## OUTPUTS - -## NOTES -Revsion History: - -- 2024-12-22 - FGE: Added Verbose Output - -## RELATED LINKS diff --git a/documentation/New-FabricWorkspaceUsageMetricsReport.md b/documentation/New-FabricWorkspaceUsageMetricsReport.md deleted file mode 100644 index 6bf0c332..00000000 --- a/documentation/New-FabricWorkspaceUsageMetricsReport.md +++ /dev/null @@ -1,100 +0,0 @@ -# New-FabricWorkspaceUsageMetricsReport - -## SYNOPSIS -{{ Fill in the Synopsis }} - -## SYNTAX - -``` -New-FabricWorkspaceUsageMetricsReport [-workspaceId] [-ProgressAction ] [-WhatIf] - [-Confirm] [] -``` - -## DESCRIPTION -{{ Fill in the Description }} - -## EXAMPLES - -### Example 1 -```powershell -PS C:\> {{ Add example code here }} -``` - -{{ Add example description here }} - -## PARAMETERS - -### -ProgressAction -{{ Fill ProgressAction Description }} - -```yaml -Type: System.Management.Automation.ActionPreference -Parameter Sets: (All) -Aliases: proga - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -workspaceId -{{ Fill workspaceId Description }} - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: True -Position: 0 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -Confirm -Prompts you for confirmation before running the cmdlet. - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: cf - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -WhatIf -Shows what would happen if the cmdlet runs. -The cmdlet is not run. - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: wi - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -### None - -## OUTPUTS - -### System.Object -## NOTES - -## RELATED LINKS diff --git a/documentation/Register-FabricWorkspaceToCapacity.md b/documentation/Register-FabricWorkspaceToCapacity.md deleted file mode 100644 index 4ec77577..00000000 --- a/documentation/Register-FabricWorkspaceToCapacity.md +++ /dev/null @@ -1,137 +0,0 @@ -# Register-FabricWorkspaceToCapacity - -## SYNOPSIS -{{ Fill in the Synopsis }} - -## SYNTAX - -### WorkspaceId -``` -Register-FabricWorkspaceToCapacity [-WorkspaceId ] -CapacityId - [-ProgressAction ] [-WhatIf] [-Confirm] [] -``` - -### WorkspaceObject -``` -Register-FabricWorkspaceToCapacity [-Workspace ] -CapacityId - [-ProgressAction ] [-WhatIf] [-Confirm] [] -``` - -## DESCRIPTION -{{ Fill in the Description }} - -## EXAMPLES - -### Example 1 -```powershell -PS C:\> {{ Add example code here }} -``` - -{{ Add example description here }} - -## PARAMETERS - -### -CapacityId -{{ Fill CapacityId Description }} - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: True -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -ProgressAction -{{ Fill ProgressAction Description }} - -```yaml -Type: System.Management.Automation.ActionPreference -Parameter Sets: (All) -Aliases: proga - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -Workspace -{{ Fill Workspace Description }} - -```yaml -Type: System.Object -Parameter Sets: WorkspaceObject -Aliases: - -Required: False -Position: Named -Default value: None -Accept pipeline input: True (ByValue) -Accept wildcard characters: False -``` - -### -WorkspaceId -{{ Fill WorkspaceId Description }} - -```yaml -Type: System.String -Parameter Sets: WorkspaceId -Aliases: - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -Confirm -Prompts you for confirmation before running the cmdlet. - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: cf - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -WhatIf -Shows what would happen if the cmdlet runs. -The cmdlet is not run. - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: wi - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -### System.Object - -## OUTPUTS - -### System.Object -## NOTES - -## RELATED LINKS diff --git a/documentation/Remove-FabricEventhouse.md b/documentation/Remove-FabricEventhouse.md deleted file mode 100644 index 12c12006..00000000 --- a/documentation/Remove-FabricEventhouse.md +++ /dev/null @@ -1,154 +0,0 @@ -# Remove-FabricEventhouse - -## SYNOPSIS -Removes an existing Fabric Eventhouse - -## SYNTAX - -``` -Remove-FabricEventhouse [-WorkspaceId] [[-EventhouseId] ] [[-EventhouseName] ] - [-ProgressAction ] [-WhatIf] [-Confirm] [] -``` - -## DESCRIPTION -Removes an existing Fabric Eventhouse - -## EXAMPLES - -### EXAMPLE 1 -``` -Remove-FabricEventhouse ` - -WorkspaceId '12345678-1234-1234-1234-123456789012' ` - -EventhouseId '12345678-1234-1234-1234-123456789012' -``` - -This example will delete the Eventhouse with the Id '12345678-1234-1234-1234-123456789012' from -the Workspace with the Id '12345678-1234-1234-1234-123456789012'. - -### EXAMPLE 2 -``` -Remove-FabricEventhouse ` - -WorkspaceId '12345678-1234-1234-1234-123456789012' ` - -EventhouseName 'MyEventhouse' -``` - -This example will delete the Eventhouse with the name 'MyEventhouse' from the Workspace with the -Id '12345678-1234-1234-1234-123456789012'. - -## PARAMETERS - -### -EventhouseId -The Id of the Eventhouse to delete. -The value for EventhouseId is a GUID. -An example of a GUID is '12345678-1234-1234-1234-123456789012'. -EventhouseId and EventhouseName cannot be used together. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: Id - -Required: False -Position: 2 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -EventhouseName -The name of the Eventhouse to delete. -EventhouseId and EventhouseName cannot be used together. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: Name, DisplayName - -Required: False -Position: 3 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -ProgressAction -{{ Fill ProgressAction Description }} - -```yaml -Type: System.Management.Automation.ActionPreference -Parameter Sets: (All) -Aliases: proga - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -WorkspaceId -Id of the Fabric Workspace for which the Eventhouse should be deleted. -The value for WorkspaceId is a GUID. -An example of a GUID is '12345678-1234-1234-1234-123456789012'. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: True -Position: 1 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -Confirm -Prompts you for confirmation before running the cmdlet. - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: cf - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -WhatIf -Shows what would happen if the cmdlet runs. -The cmdlet is not run. - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: wi - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -## OUTPUTS - -## NOTES -Revsion History: - -- 2024-11-07 - FGE: Implemented SupportShouldProcess -- 2024-11-09 - FGE: Added DisplaName as Alias for EventhouseName -- 2024-11-27 - FGE: Added Verbose Output - -## RELATED LINKS - -[https://learn.microsoft.com/en-us/rest/api/fabric/eventhouse/items/delete-eventhouse?tabs=HTTP](https://learn.microsoft.com/en-us/rest/api/fabric/eventhouse/items/delete-eventhouse?tabs=HTTP) - diff --git a/documentation/Remove-FabricEventstream.md b/documentation/Remove-FabricEventstream.md deleted file mode 100644 index df261e17..00000000 --- a/documentation/Remove-FabricEventstream.md +++ /dev/null @@ -1,148 +0,0 @@ -# Remove-FabricEventstream - -## SYNOPSIS -Removes an existing Fabric Eventstream - -## SYNTAX - -``` -Remove-FabricEventstream [-WorkspaceId] [[-EventstreamId] ] [[-EventstreamName] ] - [-ProgressAction ] [-WhatIf] [-Confirm] [] -``` - -## DESCRIPTION -Removes an existing Fabric Eventstream - -## EXAMPLES - -### EXAMPLE 1 -``` -Remove-FabricEventstream ` - -WorkspaceId '12345678-1234-1234-1234-123456789012' ` - -EventstreamId '12345678-1234-1234-1234-123456789012' -``` - -This example will delete the Eventstream with the Id '12345678-1234-1234-1234-123456789012' from -the Workspace. - -### EXAMPLE 2 -``` -Remove-FabricEventstream ` - -WorkspaceId '12345678-1234-1234-1234-123456789012' ` - -EventstreamName 'MyEventstream' -``` - -This example will delete the Eventstream with the name 'MyEventstream' from the Workspace. - -## PARAMETERS - -### -EventstreamId -The Id of the Eventstream to delete. -The value for Eventstream is a GUID. -An example of a GUID is '12345678-1234-1234-1234-123456789012'. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: Id - -Required: False -Position: 2 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -EventstreamName -{{ Fill EventstreamName Description }} - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: Name, DisplayName - -Required: False -Position: 3 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -ProgressAction -{{ Fill ProgressAction Description }} - -```yaml -Type: System.Management.Automation.ActionPreference -Parameter Sets: (All) -Aliases: proga - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -WorkspaceId -Id of the Fabric Workspace for which the Eventstream should be deleted. -The value for WorkspaceId is a GUID. -An example of a GUID is '12345678-1234-1234-1234-123456789012'. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: True -Position: 1 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -Confirm -Prompts you for confirmation before running the cmdlet. - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: cf - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -WhatIf -Shows what would happen if the cmdlet runs. -The cmdlet is not run. - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: wi - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -## OUTPUTS - -## NOTES -Revsion History: - -- 2024-11-07 - FGE: Implemented SupportShouldProcess -- 2024-11-09 - FGE: Added DisplaName as Alias for EventStreamName -- 2024-12-08 - FGE: Added Verbose Output - -## RELATED LINKS diff --git a/documentation/Remove-FabricItem.md b/documentation/Remove-FabricItem.md deleted file mode 100644 index 29ada66c..00000000 --- a/documentation/Remove-FabricItem.md +++ /dev/null @@ -1,135 +0,0 @@ -# Remove-FabricItem - -## SYNOPSIS -Removes selected items from a Fabric workspace. - -## SYNTAX - -``` -Remove-FabricItem [-workspaceId] [[-filter] ] [[-itemID] ] - [-ProgressAction ] [-WhatIf] [-Confirm] [] -``` - -## DESCRIPTION -The Remove-FabricItems function removes selected items from a specified Fabric workspace. -It uses the workspace ID and an optional filter to select the items to remove. -If a filter is provided, only items whose DisplayName matches the filter are removed. - -## EXAMPLES - -### EXAMPLE 1 -``` -Remove-FabricItems -WorkspaceID "12345678-90ab-cdef-1234-567890abcdef" -Filter "*test*" -``` - -This command removes all items from the workspace with the specified ID whose DisplayName includes "test". - -## PARAMETERS - -### -filter -An optional filter to select items to remove. -If provided, only items whose DisplayName matches the filter are removed. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: False -Position: 2 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -itemID -{{ Fill itemID Description }} - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: False -Position: 3 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -ProgressAction -{{ Fill ProgressAction Description }} - -```yaml -Type: System.Management.Automation.ActionPreference -Parameter Sets: (All) -Aliases: proga - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -workspaceId -The ID of the Fabric workspace. -This is a mandatory parameter. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: True -Position: 1 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -Confirm -Prompts you for confirmation before running the cmdlet. - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: cf - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -WhatIf -Shows what would happen if the cmdlet runs. -The cmdlet is not run. - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: wi - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -### String. You can pipe two strings that contain the workspace ID and filter to Remove-FabricItems. -## OUTPUTS - -### None. This function does not return any output. -## NOTES -This function was written by Rui Romano. -https://github.com/RuiRomano/fabricps-pbip - -## RELATED LINKS diff --git a/documentation/Remove-FabricKQLDatabase.md b/documentation/Remove-FabricKQLDatabase.md deleted file mode 100644 index 9bb358c5..00000000 --- a/documentation/Remove-FabricKQLDatabase.md +++ /dev/null @@ -1,122 +0,0 @@ -# Remove-FabricKQLDatabase - -## SYNOPSIS -Removes an existing Fabric Eventhouse - -## SYNTAX - -``` -Remove-FabricKQLDatabase [-WorkspaceId] [-KQLDatabaseId] [-ProgressAction ] - [-WhatIf] [-Confirm] [] -``` - -## DESCRIPTION -Removes an existing Fabric Eventhouse. -The Eventhouse is removed from the specified Workspace. - -## EXAMPLES - -### EXAMPLE 1 -``` -Remove-FabricEventhouse ` - -WorkspaceId '12345678-1234-1234-1234-123456789012' ` - -EventhouseId '12345678-1234-1234-1234-123456789012' -``` - -This example will remove the Eventhouse with the Id '12345678-1234-1234-1234-123456789012'. - -## PARAMETERS - -### -KQLDatabaseId -{{ Fill KQLDatabaseId Description }} - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: Id - -Required: True -Position: 2 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -ProgressAction -{{ Fill ProgressAction Description }} - -```yaml -Type: System.Management.Automation.ActionPreference -Parameter Sets: (All) -Aliases: proga - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -WorkspaceId -Id of the Fabric Workspace from which the Eventhouse should be removed. -The value for WorkspaceId is a GUID. -An example of a GUID is '12345678-1234-1234-1234-123456789012'. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: True -Position: 1 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -Confirm -Prompts you for confirmation before running the cmdlet. - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: cf - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -WhatIf -Shows what would happen if the cmdlet runs. -The cmdlet is not run. - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: wi - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -## OUTPUTS - -## NOTES -TODO: Add functionality to remove Eventhouse by name. - -Revsion History: - -- 2024-12-08 - FGE: Added Verbose Output - -## RELATED LINKS diff --git a/documentation/Remove-FabricKQLQueryset.md b/documentation/Remove-FabricKQLQueryset.md deleted file mode 100644 index 41fb343a..00000000 --- a/documentation/Remove-FabricKQLQueryset.md +++ /dev/null @@ -1,125 +0,0 @@ -# Remove-FabricKQLQueryset - -## SYNOPSIS -Removes an existing Fabric KQLQueryset. - -## SYNTAX - -``` -Remove-FabricKQLQueryset [-WorkspaceId] [-KQLQuerysetId] [-ProgressAction ] - [-WhatIf] [-Confirm] [] -``` - -## DESCRIPTION -Removes an existing Fabric KQLQueryset. -The Eventhouse is identified by the WorkspaceId and KQLQuerysetId. - -## EXAMPLES - -### EXAMPLE 1 -``` -Remove-FabricKQLQueryset ` - -WorkspaceId '12345678-1234-1234-1234-123456789012' ` - -KQLQuerysetId '12345678-1234-1234-1234-123456789012' -``` - -## PARAMETERS - -### -KQLQuerysetId -The Id of the KQLQueryset to remove. -The value for KQLQuerysetId is a GUID. -An example of a GUID is '12345678-1234-1234-1234-123456789012'. -This parameter is mandatory. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: Id - -Required: True -Position: 2 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -ProgressAction -{{ Fill ProgressAction Description }} - -```yaml -Type: System.Management.Automation.ActionPreference -Parameter Sets: (All) -Aliases: proga - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -WorkspaceId -Id of the Fabric Workspace for which the KQLQueryset should be removed. -The value for WorkspaceId is a GUID. -An example of a GUID is '12345678-1234-1234-1234-123456789012'. -This parameter is mandatory. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: True -Position: 1 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -Confirm -Prompts you for confirmation before running the cmdlet. - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: cf - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -WhatIf -Shows what would happen if the cmdlet runs. -The cmdlet is not run. - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: wi - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -## OUTPUTS - -## NOTES -TODO: Add functionality to remove KQLQueryset by name. - -Revsion History: - -- 2024-11-07 - FGE: Implemented SupportShouldProcess -- 2024-12-22 - FGE: Added Verbose Output - -## RELATED LINKS diff --git a/documentation/Remove-FabricSQLDatabase.md b/documentation/Remove-FabricSQLDatabase.md deleted file mode 100644 index 29491122..00000000 --- a/documentation/Remove-FabricSQLDatabase.md +++ /dev/null @@ -1,92 +0,0 @@ -# Remove-FabricSQLDatabase - -## SYNOPSIS -Removes an existing Fabric SQL Database - -## SYNTAX - -``` -Remove-FabricSQLDatabase [-WorkspaceId] [-SQLDatabaseId] [-ProgressAction ] - [] -``` - -## DESCRIPTION -Removes an existing Fabric SQL Database. -The SQL Database is removed from the specified Workspace. - -## EXAMPLES - -### EXAMPLE 1 -``` -Remove-FabricSQLDatabase ` - -WorkspaceId '12345678-1234-1234-1234-123456789012' ` - -SQLDatabaseId '12345678-1234-1234-1234-123456789012' -``` - -This example will remove the SQL Database with the Id '12345678-1234-1234-1234-123456789012'. - -## PARAMETERS - -### -ProgressAction -{{ Fill ProgressAction Description }} - -```yaml -Type: System.Management.Automation.ActionPreference -Parameter Sets: (All) -Aliases: proga - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -SQLDatabaseId -The Id of the SQL Database to remove. -The value for EventhouseId is a GUID. -An example of a GUID is '12345678-1234-1234-1234-123456789012'. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: Id - -Required: True -Position: 2 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -WorkspaceId -Id of the Fabric Workspace from which the SQL Database should be removed. -The value for WorkspaceId is a GUID. -An example of a GUID is '12345678-1234-1234-1234-123456789012'. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: True -Position: 1 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -## OUTPUTS - -## NOTES -TODO: Add functionality to remove SQLDatabase by name. - -Revsion History: -- 2025-03-06 - KNO: Init version - -## RELATED LINKS diff --git a/documentation/Remove-FabricWorkspace.md b/documentation/Remove-FabricWorkspace.md deleted file mode 100644 index 1ca54e14..00000000 --- a/documentation/Remove-FabricWorkspace.md +++ /dev/null @@ -1,100 +0,0 @@ -# Remove-FabricWorkspace - -## SYNOPSIS -{{ Fill in the Synopsis }} - -## SYNTAX - -``` -Remove-FabricWorkspace [-workspaceID] [-ProgressAction ] [-WhatIf] [-Confirm] - [] -``` - -## DESCRIPTION -{{ Fill in the Description }} - -## EXAMPLES - -### Example 1 -```powershell -PS C:\> {{ Add example code here }} -``` - -{{ Add example description here }} - -## PARAMETERS - -### -ProgressAction -{{ Fill ProgressAction Description }} - -```yaml -Type: System.Management.Automation.ActionPreference -Parameter Sets: (All) -Aliases: proga - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -workspaceID -{{ Fill workspaceID Description }} - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: True -Position: 0 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -Confirm -Prompts you for confirmation before running the cmdlet. - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: cf - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -WhatIf -Shows what would happen if the cmdlet runs. -The cmdlet is not run. - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: wi - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -### None - -## OUTPUTS - -### System.Object -## NOTES - -## RELATED LINKS diff --git a/documentation/Resume-FabricCapacity.md b/documentation/Resume-FabricCapacity.md deleted file mode 100644 index c6128a02..00000000 --- a/documentation/Resume-FabricCapacity.md +++ /dev/null @@ -1,130 +0,0 @@ -# Resume-FabricCapacity - -## SYNOPSIS -{{ Fill in the Synopsis }} - -## SYNTAX - -``` -Resume-FabricCapacity [-subscriptionID] [-resourcegroup] [-capacity] - [-ProgressAction ] [-WhatIf] [-Confirm] [] -``` - -## DESCRIPTION -{{ Fill in the Description }} - -## EXAMPLES - -### Example 1 -```powershell -PS C:\> {{ Add example code here }} -``` - -{{ Add example description here }} - -## PARAMETERS - -### -capacity -{{ Fill capacity Description }} - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: True -Position: 2 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -ProgressAction -{{ Fill ProgressAction Description }} - -```yaml -Type: System.Management.Automation.ActionPreference -Parameter Sets: (All) -Aliases: proga - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -resourcegroup -{{ Fill resourcegroup Description }} - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: True -Position: 1 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -subscriptionID -{{ Fill subscriptionID Description }} - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: True -Position: 0 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -Confirm -Prompts you for confirmation before running the cmdlet. - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: cf - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -WhatIf -Shows what would happen if the cmdlet runs. -The cmdlet is not run. - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: wi - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -### None - -## OUTPUTS - -### System.Object -## NOTES - -## RELATED LINKS diff --git a/documentation/Set-FabricAuthToken.md b/documentation/Set-FabricAuthToken.md deleted file mode 100644 index 771edf1e..00000000 --- a/documentation/Set-FabricAuthToken.md +++ /dev/null @@ -1,184 +0,0 @@ -# Set-FabricAuthToken - -## SYNOPSIS -Sets the Fabric authentication token. - -## SYNTAX - -``` -Set-FabricAuthToken [[-servicePrincipalId] ] [[-servicePrincipalSecret] ] - [[-credential] ] [[-tenantId] ] [-reset] [[-apiUrl] ] - [-ProgressAction ] [-WhatIf] [-Confirm] [] -``` - -## DESCRIPTION -The Set-FabricAuthToken function sets the Fabric authentication token. -It checks if an Azure context is already available. -If not, it connects to the Azure account using either a service principal ID and secret, a provided credential, or interactive login. -It then gets the Azure context and sets the Fabric authentication token. - -## EXAMPLES - -### EXAMPLE 1 -``` -Set-FabricAuthToken -servicePrincipalId "12345678-90ab-cdef-1234-567890abcdef" -servicePrincipalSecret "secret" -tenantId "12345678-90ab-cdef-1234-567890abcdef" -``` - -This command sets the Fabric authentication token using the provided service principal ID, secret, and tenant ID. - -## PARAMETERS - -### -apiUrl -{{ Fill apiUrl Description }} - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: False -Position: 5 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -credential -The credential. -If provided, the function uses this credential to connect to the Azure account. - -```yaml -Type: System.Management.Automation.PSCredential -Parameter Sets: (All) -Aliases: - -Required: False -Position: 3 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -ProgressAction -{{ Fill ProgressAction Description }} - -```yaml -Type: System.Management.Automation.ActionPreference -Parameter Sets: (All) -Aliases: proga - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -reset -{{ Fill reset Description }} - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: False -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -servicePrincipalId -The service principal ID. -If provided, the function uses this ID and the service principal secret to connect to the Azure account. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: False -Position: 1 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -servicePrincipalSecret -The service principal secret. -Used with the service principal ID to connect to the Azure account. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: False -Position: 2 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -tenantId -The tenant ID. -Used with the service principal ID and secret or the credential to connect to the Azure account. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: False -Position: 4 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -Confirm -Prompts you for confirmation before running the cmdlet. - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: cf - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -WhatIf -Shows what would happen if the cmdlet runs. -The cmdlet is not run. - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: wi - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -### String, SecureString, PSCredential. You can pipe a string that contains the service principal ID, a secure string that contains the service principal secret, and a PSCredential object that contains the credential to Set-FabricAuthToken. -## OUTPUTS - -### None. This function does not return any output. -## NOTES -This function was originally written by Rui Romano. -https://github.com/RuiRomano/fabricps-pbip - -## RELATED LINKS diff --git a/documentation/Set-FabricEventhouse.md b/documentation/Set-FabricEventhouse.md deleted file mode 100644 index ba764814..00000000 --- a/documentation/Set-FabricEventhouse.md +++ /dev/null @@ -1,179 +0,0 @@ -# Set-FabricEventhouse - -## SYNOPSIS -Updates Properties of an existing Fabric Eventhouse - -## SYNTAX - -``` -Set-FabricEventhouse [-WorkspaceId] [-EventhouseId] [[-EventhouseNewName] ] - [[-EventhouseDescription] ] [-ProgressAction ] [-WhatIf] [-Confirm] - [] -``` - -## DESCRIPTION -Updates Properties of an existing Fabric Eventhouse - -## EXAMPLES - -### EXAMPLE 1 -``` -Set-FabricEventhouse ` - -WorkspaceId '12345678-1234-1234-1234-123456789012' ` - -EventhouseId '12345678-1234-1234-1234-123456789012' ` - -EventhouseNewName 'MyNewEventhouse' ` - -EventhouseDescription 'This is my new Eventhouse' -``` - -This example will update the Eventhouse with the Id '12345678-1234-1234-1234-123456789012' -in the Workspace with the Id '12345678-1234-1234-1234-123456789012' to -have the name 'MyNewEventhouse' and the description -'This is my new Eventhouse'. - -### EXAMPLE 2 -``` -Set-FabricEventhouse ` - -WorkspaceId '12345678-1234-1234-1234-123456789012' ` - -EventhouseId '12345678-1234-1234-1234-123456789012' ` - -EventhouseNewName 'MyNewEventhouse' ` - -EventhouseDescription 'This is my new Eventhouse' ` - -Verbose -``` - -This example will update the Eventhouse with the Id '12345678-1234-1234-1234-123456789012' -in the Workspace with the Id '12345678-1234-1234-1234-123456789012' to -have the name 'MyNewEventhouse' and the description 'This is my new Eventhouse'. -It will also give you verbose output which is useful for debugging. - -## PARAMETERS - -### -EventhouseDescription -The new description of the Eventhouse. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: Description - -Required: False -Position: 4 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -EventhouseId -The Id of the Eventhouse to update. -The value for EventhouseId is a GUID. -An example of a GUID is '12345678-1234-1234-1234-123456789012'. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: Id - -Required: True -Position: 2 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -EventhouseNewName -The new name of the Eventhouse. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: NewName, NewDisplayName - -Required: False -Position: 3 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -ProgressAction -{{ Fill ProgressAction Description }} - -```yaml -Type: System.Management.Automation.ActionPreference -Parameter Sets: (All) -Aliases: proga - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -WorkspaceId -Id of the Fabric Workspace for which the Eventhouse should be updated. -The value for WorkspaceId is a GUID. -An example of a GUID is '12345678-1234-1234-1234-123456789012'. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: True -Position: 1 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -Confirm -Prompts you for confirmation before running the cmdlet. - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: cf - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -WhatIf -Shows what would happen if the cmdlet runs. -The cmdlet is not run. - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: wi - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -## OUTPUTS - -## NOTES -TODO: Add functionality to update Eventhouse properties using EventhouseName instead of EventhouseId - -Revsion History: - -- 2024-11-07 - FGE: Implemented SupportShouldProcess -- 2024-11-09 - FGE: Added NewDisplaName as Alias for EventhouseName -- 2024-11-27 - FGE: Added Verbose Output - -## RELATED LINKS - -[https://learn.microsoft.com/en-us/rest/api/fabric/eventhouse/items/create-eventhouse?tabs=HTTP](https://learn.microsoft.com/en-us/rest/api/fabric/eventhouse/items/create-eventhouse?tabs=HTTP) - diff --git a/documentation/Set-FabricEventstream.md b/documentation/Set-FabricEventstream.md deleted file mode 100644 index a0190c79..00000000 --- a/documentation/Set-FabricEventstream.md +++ /dev/null @@ -1,160 +0,0 @@ -# Set-FabricEventstream - -## SYNOPSIS -Updates Properties of an existing Fabric Eventstream - -## SYNTAX - -``` -Set-FabricEventstream [-WorkspaceId] [-EventstreamId] [[-EventstreamNewName] ] - [[-EventstreamDescription] ] [-ProgressAction ] [-WhatIf] [-Confirm] - [] -``` - -## DESCRIPTION -Updates Properties of an existing Fabric Eventstream - -## EXAMPLES - -### EXAMPLE 1 -``` -Set-FabricEventstream ` - -WorkspaceId '12345678-1234-1234-1234-123456789012' ` - -EventstreamId '12345678-1234-1234-1234-123456789012' ` - -EventstreamNewName 'MyNewEventstream' ` - -EventstreamDescription 'This is my new Eventstream' -``` - -This example will update the Eventstream with the Id '12345678-1234-1234-1234-123456789012'. - -## PARAMETERS - -### -EventstreamDescription -The new description of the Eventstream. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: Description, NewDescription, EventstreamNewDescription - -Required: False -Position: 4 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -EventstreamId -The Id of the Eventstream to update. -The value for EventstreamId is a GUID. -An example of a GUID is '12345678-1234-1234-1234-123456789012'. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: Id - -Required: True -Position: 2 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -EventstreamNewName -The new name of the Eventstream. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: NewName, NewDisplayName - -Required: False -Position: 3 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -ProgressAction -{{ Fill ProgressAction Description }} - -```yaml -Type: System.Management.Automation.ActionPreference -Parameter Sets: (All) -Aliases: proga - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -WorkspaceId -Id of the Fabric Workspace for which the Eventstream should be updated. -The value for WorkspaceId is a GUID. -An example of a GUID is '12345678-1234-1234-1234-123456789012'. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: True -Position: 1 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -Confirm -Prompts you for confirmation before running the cmdlet. - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: cf - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -WhatIf -Shows what would happen if the cmdlet runs. -The cmdlet is not run. - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: wi - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -## OUTPUTS - -## NOTES -TODO: Add functionality to update Eventstream properties using EventstreamName instead of EventstreamId - -Revsion History: - -- 2024-11-07 - FGE: Implemented SupportShouldProcess -- 2024-11-09 - FGE: Added DisplaName as Alias for EventStreamNewName -- 2024-12-08 - FGE: Added Verbose Output - Added Aliases for EventstreamNewName and EventstreamDescription - Corrected typo in EventstreamNewName Variable - -## RELATED LINKS diff --git a/documentation/Set-FabricKQLDatabase.md b/documentation/Set-FabricKQLDatabase.md deleted file mode 100644 index 705625f0..00000000 --- a/documentation/Set-FabricKQLDatabase.md +++ /dev/null @@ -1,165 +0,0 @@ -# Set-FabricKQLDatabase - -## SYNOPSIS -Updates Properties of an existing Fabric KQLDatabase - -## SYNTAX - -``` -Set-FabricKQLDatabase [-WorkspaceId] [-KQLDatabaseId] [[-NewKQLDatabaseName] ] - [[-KQLDatabaseDescription] ] [-ProgressAction ] [-WhatIf] [-Confirm] - [] -``` - -## DESCRIPTION -Updates Properties of an existing Fabric KQLDatabase. -The KQLDatabase is updated -in the specified Workspace. -The KQLDatabaseId is used to identify the KQLDatabase -that should be updated. -The KQLDatabaseNewName and KQLDatabaseDescription are the -properties that can be updated. - -## EXAMPLES - -### EXAMPLE 1 -``` -Set-FabricKQLDatabase ` - -WorkspaceId '12345678-1234-1234-1234-123456789012' ` - -KQLDatabaseId '12345678-1234-1234-1234-123456789012' ` - -NewKQLDatabaseNewName 'MyNewKQLDatabase' ` - -KQLDatabaseDescription 'This is my new KQLDatabase' -``` - -This example will update the KQLDatabase with the Id '12345678-1234-1234-1234-123456789012'. -It will update the name to 'MyNewKQLDatabase' and the description to 'This is my new KQLDatabase'. - -## PARAMETERS - -### -KQLDatabaseDescription -The new description of the KQLDatabase. -The description can be up to 256 characters long. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: Description - -Required: False -Position: 4 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -KQLDatabaseId -The Id of the KQLDatabase to update. -The value for KQLDatabaseId is a GUID. -An example of a GUID is '12345678-1234-123-1234-123456789012'. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: Id - -Required: True -Position: 2 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -NewKQLDatabaseName -The new name of the KQLDatabase. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: NewName, NewDisplayName - -Required: False -Position: 3 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -ProgressAction -{{ Fill ProgressAction Description }} - -```yaml -Type: System.Management.Automation.ActionPreference -Parameter Sets: (All) -Aliases: proga - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -WorkspaceId -Id of the Fabric Workspace for which the KQLDatabase should be updated. -The value for WorkspaceId is a GUID. -An example of a GUID is '12345678-1234-1234-1234-123456789012'. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: True -Position: 1 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -Confirm -Prompts you for confirmation before running the cmdlet. - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: cf - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -WhatIf -Shows what would happen if the cmdlet runs. -The cmdlet is not run. - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: wi - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -## OUTPUTS - -## NOTES -Revsion History: - -- 2024-11-07 - FGE: Implemented SupportShouldProcess -- 2024-11-09 - FGE: Added DisplaName as Alias for KQLDatabaseName -- 2024-12-08 - FGE: Added Verbose Output - Renamed Parameter KQLDatabaseName to NewKQLDatabaseNewName - -## RELATED LINKS diff --git a/documentation/Set-FabricKQLQueryset.md b/documentation/Set-FabricKQLQueryset.md deleted file mode 100644 index 14d7edf7..00000000 --- a/documentation/Set-FabricKQLQueryset.md +++ /dev/null @@ -1,166 +0,0 @@ -# Set-FabricKQLQueryset - -## SYNOPSIS -Updates Properties of an existing Fabric KQLQueryset - -## SYNTAX - -``` -Set-FabricKQLQueryset [-WorkspaceId] [-KQLQuerysetId] [[-KQLQuerysetNewName] ] - [[-KQLQuerysetDescription] ] [-ProgressAction ] [-WhatIf] [-Confirm] - [] -``` - -## DESCRIPTION -Updates Properties of an existing Fabric KQLQueryset. -The KQLQueryset is identified by -the WorkspaceId and KQLQuerysetId. - -## EXAMPLES - -### EXAMPLE 1 -``` -Set-FabricKQLQueryset ` - -WorkspaceId '12345678-1234-1234-1234-123456789012' ` - -KQLQuerysetId '12345678-1234-1234-1234-123456789012' ` - -KQLQuerysetNewName 'MyKQLQueryset' ` - -KQLQuerysetDescription 'This is my KQLQueryset' -``` - -This example will update the KQLQueryset. -The KQLQueryset will have the name 'MyKQLQueryset' -and the description 'This is my KQLQueryset'. - -## PARAMETERS - -### -KQLQuerysetDescription -The new description of the KQLQueryset. -This parameter is optional. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: Description - -Required: False -Position: 4 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -KQLQuerysetId -The Id of the KQLQueryset to update. -The value for KQLQuerysetId is a GUID. -An example of a GUID is '12345678-1234-1234-1234-123456789012'. -This parameter is mandatory. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: Id - -Required: True -Position: 2 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -KQLQuerysetNewName -{{ Fill KQLQuerysetNewName Description }} - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: NewName, NewDisplayName - -Required: False -Position: 3 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -ProgressAction -{{ Fill ProgressAction Description }} - -```yaml -Type: System.Management.Automation.ActionPreference -Parameter Sets: (All) -Aliases: proga - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -WorkspaceId -Id of the Fabric Workspace for which the KQLQueryset should be updated. -The value for WorkspaceId is a GUID. -An example of a GUID is '12345678-1234-1234-1234-123456789012'. -This parameter is mandatory. - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: True -Position: 1 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -Confirm -Prompts you for confirmation before running the cmdlet. - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: cf - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -WhatIf -Shows what would happen if the cmdlet runs. -The cmdlet is not run. - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: wi - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -## OUTPUTS - -## NOTES -Revsion History: - -- 2024-11-07 - FGE: Implemented SupportShouldProcess -- 2024-11-09 - FGE: Added NewDisplaName as Alias for KQLQuerysetNewName -- 2024-12-22 - FGE: Added Verbose Output - -## RELATED LINKS - -[https://learn.microsoft.com/en-us/rest/api/fabric/KQLQueryset/items/create-KQLQueryset?tabs=HTTP](https://learn.microsoft.com/en-us/rest/api/fabric/KQLQueryset/items/create-KQLQueryset?tabs=HTTP) - diff --git a/documentation/Suspend-FabricCapacity.md b/documentation/Suspend-FabricCapacity.md deleted file mode 100644 index 112ad86b..00000000 --- a/documentation/Suspend-FabricCapacity.md +++ /dev/null @@ -1,130 +0,0 @@ -# Suspend-FabricCapacity - -## SYNOPSIS -{{ Fill in the Synopsis }} - -## SYNTAX - -``` -Suspend-FabricCapacity [-subscriptionID] [-resourcegroup] [-capacity] - [-ProgressAction ] [-WhatIf] [-Confirm] [] -``` - -## DESCRIPTION -{{ Fill in the Description }} - -## EXAMPLES - -### Example 1 -```powershell -PS C:\> {{ Add example code here }} -``` - -{{ Add example description here }} - -## PARAMETERS - -### -capacity -{{ Fill capacity Description }} - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: True -Position: 2 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -ProgressAction -{{ Fill ProgressAction Description }} - -```yaml -Type: System.Management.Automation.ActionPreference -Parameter Sets: (All) -Aliases: proga - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -resourcegroup -{{ Fill resourcegroup Description }} - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: True -Position: 1 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -subscriptionID -{{ Fill subscriptionID Description }} - -```yaml -Type: System.String -Parameter Sets: (All) -Aliases: - -Required: True -Position: 0 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -Confirm -Prompts you for confirmation before running the cmdlet. - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: cf - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -WhatIf -Shows what would happen if the cmdlet runs. -The cmdlet is not run. - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: wi - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -### None - -## OUTPUTS - -### System.Object -## NOTES - -## RELATED LINKS diff --git a/documentation/Unregister-FabricWorkspaceToCapacity.md b/documentation/Unregister-FabricWorkspaceToCapacity.md deleted file mode 100644 index 7f84c740..00000000 --- a/documentation/Unregister-FabricWorkspaceToCapacity.md +++ /dev/null @@ -1,132 +0,0 @@ -# Unregister-FabricWorkspaceToCapacity - -## SYNOPSIS -Unregisters a workspace from a capacity. - -## SYNTAX - -### WorkspaceId -``` -Unregister-FabricWorkspaceToCapacity -WorkspaceId [-ProgressAction ] [-WhatIf] - [-Confirm] [] -``` - -### WorkspaceObject -``` -Unregister-FabricWorkspaceToCapacity -Workspace [-ProgressAction ] [-WhatIf] - [-Confirm] [] -``` - -## DESCRIPTION -The Unregister-FabricWorkspaceToCapacity function unregisters a workspace from a capacity in PowerBI. -It can be used to remove a workspace from a capacity, allowing it to be assigned to a different capacity or remain unassigned. - -## EXAMPLES - -### EXAMPLE 1 -``` -Unregister-FabricWorkspaceToCapacity -WorkspaceId "12345678" -Unregisters the workspace with ID "12345678" from the capacity. -``` - -### EXAMPLE 2 -``` -Get-FabricWorkspace | Unregister-FabricWorkspaceToCapacity -Unregisters the workspace objects piped from Get-FabricWorkspace from the capacity. -``` - -## PARAMETERS - -### -ProgressAction -{{ Fill ProgressAction Description }} - -```yaml -Type: System.Management.Automation.ActionPreference -Parameter Sets: (All) -Aliases: proga - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -Workspace -Specifies the workspace object to be unregistered from the capacity. -This parameter is mandatory when using the 'WorkspaceObject' parameter set. -The workspace object can be piped into the function. - -```yaml -Type: System.Object -Parameter Sets: WorkspaceObject -Aliases: - -Required: True -Position: Named -Default value: None -Accept pipeline input: True (ByValue) -Accept wildcard characters: False -``` - -### -WorkspaceId -Specifies the ID of the workspace to be unregistered from the capacity. -This parameter is mandatory when using the 'WorkspaceId' parameter set. - -```yaml -Type: System.String -Parameter Sets: WorkspaceId -Aliases: - -Required: True -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -Confirm -Prompts you for confirmation before running the cmdlet. - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: cf - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -WhatIf -Shows what would happen if the cmdlet runs. -The cmdlet is not run. - -```yaml -Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) -Aliases: wi - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -### System.Management.Automation.PSCustomObject -## OUTPUTS - -### System.Object -## NOTES -Author: Your Name -Date: Today's Date - -## RELATED LINKS diff --git a/helper/Get-RootPath.ps1 b/helper/Get-RootPath.ps1 deleted file mode 100644 index dc88bf23..00000000 --- a/helper/Get-RootPath.ps1 +++ /dev/null @@ -1,12 +0,0 @@ -#$Host.name - -$rootPath = Switch ($Host.name) { - 'Visual Studio Code Host' { split-path $psEditor.GetEditorContext().CurrentFile.Path } - 'Windows PowerShell ISE Host' { Split-Path -Path $psISE.CurrentFile.FullPath } - 'ConsoleHost' { $PSScriptRoot } -} -#$parentPath = $rootPath | Split-Path -Parent -$rootPath -#$parentPath - - diff --git a/helper/createdocumentation.ps1 b/helper/createdocumentation.ps1 deleted file mode 100644 index 20b90558..00000000 --- a/helper/createdocumentation.ps1 +++ /dev/null @@ -1,69 +0,0 @@ -#============================================================================ -# Datei: createdocumentation.ps1 -# -# Summary: This script will help to create the documentation for -# the PowerShell module. Markdown Code will be created -# with platyPS -# -# Datum: 2024-11-05 -# -# Revisionen: yyyy-dd-mm -# - ... -# -# PowerShell Version: 7.1 -#------------------------------------------------------------------------------ -# Geschrieben von -# Frank Geisler, GDS Business Intelligence GmbH -# -# Dieses Script ist nur zu Lehr- bzw. Lernzwecken gedacht -# -# DIESER CODE UND DIE ENTHALTENEN INFORMATIONEN WERDEN OHNE GEWÄHR JEGLICHER -# ART ZUR VERFÜGUNG GESTELLT, WEDER AUSDRÜCKLICH NOCH IMPLIZIT, EINSCHLIESSLICH, -# ABER NICHT BESCHRÄNKT AUF FUNKTIONALITÄT ODER EIGNUNG FÜR EINEN BESTIMMTEN -# ZWECK. SIE VERWENDEN DEN CODE AUF EIGENE GEFAHR. -#============================================================================*/ - -#---------------------------------------------------------------------------- -# 01. Define the variables we will need further down the -# road. -#---------------------------------------------------------------------------- - -Import-Module platyPS - -$rootPath = Switch ($Host.name) { - 'Visual Studio Code Host' { split-path $psEditor.GetEditorContext().CurrentFile.Path } - 'Windows PowerShell ISE Host' { Split-Path -Path $psISE.CurrentFile.FullPath } - 'ConsoleHost' { $PSScriptRoot } -} - -$basepath = Split-Path $rootPath -Parent -$moduleName = 'FabricTools' -$modulePathName = "$basepath\$moduleName\$moduleName.psd1" -$markdownPath = "$basepath\documentation" -$mamlPath = "$basepath\en-US" - -#---------------------------------------------------------------------------- -# 02. Import the module to be documented -# ATTENTION!!!!!! The module must be loaded in the PowerShell session -#---------------------------------------------------------------------------- - -# FGE: Remove the module from PowerShell -Remove-Module ` - -Name FabricTools ` - -ErrorAction SilentlyContinue - -Import-Module ` - -Name $modulePathName - -#---------------------------------------------------------------------------- -# 03. Create the markdown documentation -#---------------------------------------------------------------------------- -New-MarkdownHelp ` - -Module $moduleName ` - -OutputFolder $markdownPath ` - -AlphabeticParamsOrder ` - -UseFullTypeName ` - -WithModulePage ` - -ExcludeDontShow ` - -NoMetadata ` - -Force diff --git a/helper/createmodule.ps1 b/helper/createmodule.ps1 deleted file mode 100644 index 2ec8d95e..00000000 --- a/helper/createmodule.ps1 +++ /dev/null @@ -1,49 +0,0 @@ -#============================================================================ -# Datei: createmodule.ps1 -# -# Summary: This script helps creating the module. It has the -# New-ModuleManifest function in it. -# -# Datum: 2024-11-04 -# -# Revisionen: yyyy-dd-mm -# - ... -# Kunde: Kunde -# -# Projekt: PowerRTI -# -# PowerShell Version: 7.1 -#------------------------------------------------------------------------------ -# Geschrieben von -# Frank Geisler, GDS Business Intelligence GmbH -# -# Dieses Script ist nur zu Lehr- bzw. Lernzwecken gedacht -# -# DIESER CODE UND DIE ENTHALTENEN INFORMATIONEN WERDEN OHNE GEWÄHR JEGLICHER -# ART ZUR VERFÜGUNG GESTELLT, WEDER AUSDRÜCKLICH NOCH IMPLIZIT, EINSCHLIESSLICH, -# ABER NICHT BESCHRÄNKT AUF FUNKTIONALITÄT ODER EIGNUNG FÜR EINEN BESTIMMTEN -# ZWECK. SIE VERWENDEN DEN CODE AUF EIGENE GEFAHR. -#============================================================================*/ - -New-ModuleManifest ` - -Path '.\powerrti.psd1' ` - -RootModule '.\powerrti.psm1' ` - -Author 'Frank Geisler' ` - -CompanyName 'GDS Business Intelligence GmbH' ` - -Description 'PowerRTI is a PowerShell module to automate Fabric Real-Time Intelligence in PowerShell' ` - -ModuleVersion '0.1.0' ` - -PowerShellVersion '7.1' ` - -FunctionsToExport '*' ` - -VariablesToExport '*' ` - -AliasesToExport '*' ` - -CmdletsToExport '*' ` - -TypesToProcess '*' ` - -FormatsToProcess '*' - -# FGE: Import the module for testing -Import-Module ` - -Name 'C:\Users\fgeisler\repos\github\powerrit\powerrti\powerrti.psd1' - -Remove-Module ` - -Name 'PowerRTI' ` - -Force \ No newline at end of file diff --git a/helper/startscriptanalyzer.ps1 b/helper/startscriptanalyzer.ps1 deleted file mode 100644 index 2e0420eb..00000000 --- a/helper/startscriptanalyzer.ps1 +++ /dev/null @@ -1,17 +0,0 @@ -$rootPath = Switch ($Host.name) { - 'Visual Studio Code Host' { split-path $psEditor.GetEditorContext().CurrentFile.Path } - 'Windows PowerShell ISE Host' { Split-Path -Path $psISE.CurrentFile.FullPath } - 'ConsoleHost' { $PSScriptRoot } -} - -$basepath = Split-Path $rootPath -Parent -$basepath = Join-Path (Split-Path $rootPath -Parent) (Split-Path $basePath -Leaf) - - -Invoke-ScriptAnalyzer ` - -Path $basepath ` - -Recurse ` - -Severity All - -#Invoke-ScriptAnalyzer -Path $basepath -Severity Error -Recurse -Invoke-ScriptAnalyzer -Path $basepath -Severity @('Error', 'Warning') -Recurse diff --git a/images/FabricToolsLogo.ico b/images/FabricToolsLogo.ico index 572524ca..91f0fc5f 100644 Binary files a/images/FabricToolsLogo.ico and b/images/FabricToolsLogo.ico differ diff --git a/images/Fabtools.ico b/images/Fabtools.ico index dc4f2f13..52f46acc 100644 Binary files a/images/Fabtools.ico and b/images/Fabtools.ico differ diff --git a/source/FabricTools.psd1 b/source/FabricTools.psd1 new file mode 100644 index 00000000..e8095fde --- /dev/null +++ b/source/FabricTools.psd1 @@ -0,0 +1,143 @@ +# +# Module manifest for module 'FabricTools' +# +# Generated by: mrrob +# +# Generated on: 08/05/2025 +# + +@{ + +# Script module or binary module file associated with this manifest. +RootModule = 'FabricTools.psm1' + +# Version number of this module. +ModuleVersion = '0.0.1' + +# Supported PSEditions +# CompatiblePSEditions = @() + +# ID used to uniquely identify this module +GUID = '0ba3e49a-b47e-4beb-8434-5a34ad41ae72' + +# Author of this module +Author = 'mrrob' + +# Company or vendor of this module +CompanyName = 'mrrob' + +# Copyright statement for this module +Copyright = '(c) mrrob. All rights reserved.' + +# Description of the functionality provided by this module +Description = 'A module to be able to do more with Microsoft Fabric.' + +# Minimum version of the PowerShell engine required by this module +PowerShellVersion = '5.0' + +# Name of the PowerShell host required by this module +# PowerShellHostName = '' + +# Minimum version of the PowerShell host required by this module +# PowerShellHostVersion = '' + +# Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only. +# DotNetFrameworkVersion = '' + +# Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only. +# ClrVersion = '' + +# Processor architecture (None, X86, Amd64) required by this module +# ProcessorArchitecture = '' + +# Modules that must be imported into the global environment prior to importing this module +RequiredModules = @() + +# Assemblies that must be loaded prior to importing this module +# RequiredAssemblies = @() + +# Script files (.ps1) that are run in the caller's environment prior to importing this module. +# ScriptsToProcess = @() + +# Type files (.ps1xml) to be loaded when importing this module +# TypesToProcess = @() + +# Format files (.ps1xml) to be loaded when importing this module +# FormatsToProcess = @() + +# Modules to import as nested modules of the module specified in RootModule/ModuleToProcess +# NestedModules = @() + +# Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. +FunctionsToExport = @() + +# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. +CmdletsToExport = @() + +# Variables to export from this module +VariablesToExport = @() + +# Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. +AliasesToExport = @() + +# DSC resources to export from this module +DscResourcesToExport = @() + +# List of all modules packaged with this module +# ModuleList = @() + +# List of all files packaged with this module +# FileList = @() + +# Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. +PrivateData = @{ + + PSData = @{ + + # Tags applied to this module. These help with module discovery in online galleries. + # Tags = @() + + # A URL to the license for this module. + # LicenseUri = '' + + # A URL to the main website for this project. + # ProjectUri = '' + + # A URL to an icon representing this module. + # IconUri = '' + + # ReleaseNotes of this module + ReleaseNotes = '' + + # Prerelease string of this module + Prerelease = '' + + # Flag to indicate whether the module requires explicit user acceptance for install/update/save + # RequireLicenseAcceptance = $false + + # External dependent modules of this module + # ExternalModuleDependencies = @() + + } # End of PSData hashtable + +} # End of PrivateData hashtable + +# HelpInfo URI of this module +# HelpInfoURI = '' + +# Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. +# DefaultCommandPrefix = '' + +} + + + + + + + + + + + + diff --git a/source/FabricTools.psm1 b/source/FabricTools.psm1 new file mode 100644 index 00000000..92d7cfb9 --- /dev/null +++ b/source/FabricTools.psm1 @@ -0,0 +1,5 @@ +<# + This file is intentionally left empty. It is must be left here for the module + manifest to refer to. It is recreated during the build process. +#> + diff --git a/source/Private/Get-FabricUri.ps1 b/source/Private/Get-FabricUri.ps1 new file mode 100644 index 00000000..c6e72a21 --- /dev/null +++ b/source/Private/Get-FabricUri.ps1 @@ -0,0 +1,75 @@ +<# +.SYNOPSIS +Internal function to connect to Fabric and setup the uri and headers for commands. + +.DESCRIPTION +Internal function to connect to Fabric and setup the uri and headers for commands. + +Requires the Workspace and DataWarehouse GUIDs to connect to. + +.PARAMETER BaseUrl +Defaults to api.powerbi.com + +.PARAMETER WorkspaceGUID +This is the workspace GUID in which the data warehouse resides. + +.PARAMETER DataWarehouseGUID +The GUID for the data warehouse which we want to retrieve restore points for. + +.PARAMETER BatchId +The BatchId to use for the request. If this is set then the batches endpoint will be used. + +.EXAMPLE +Get-FabricUri -WorkspaceGUID 'GUID-GUID-GUID-GUID' -DataWarehouseGUID 'GUID-GUID-GUID-GUID' + +Connects to the specified Fabric Data Warehouse and sets up the headers and uri for future commands. + +.EXAMPLE +Get-FabricUri -WorkspaceGUID 'GUID-GUID-GUID-GUID' -DataWarehouseGUID 'GUID-GUID-GUID-GUID' -BatchId 'GUID-GUID-GUID-GUID' + +Connects to the specified Fabric Data Warehouse and sets up the headers and uri for checking the progress of an operation with a specific batchId. + +#> +function Get-FabricUri { + param ( + $BaseUrl = 'api.powerbi.com', + [parameter(Mandatory)] + [String]$WorkspaceGUID, + [parameter(Mandatory)] + [String]$DataWarehouseGUID, + + [String]$BatchId + ) + + try { + $headers = Get-PowerBIAccessToken + } catch { + try { + Stop-PSFFunction -Message ('Not able to get a token - execute Connect-PowerBIServiceAccount manually first') -EnableException + # Write-PSFMessage -Level Warning -Message ('Not able to get a token - will execute Connect-PowerBIServiceAccount') + #TODO: This doesn't work the first time - is it waiting for response? + # $conn = Connect-PowerBIServiceAccount + # if($conn) { + # Write-PSFMessage -Level Info -Message ('Successfully connected to PowerBI') + # } + } catch { + throw 'Not able to get a token - manually try and run Connect-PowerBIServiceAccount' + } + } + + if($BatchId) { + $Uri = ('https://{0}/v1.0/myorg/groups/{1}/datawarehouses/{2}/batches/{3}' -f $baseurl, $workspaceGUID, $dataWarehouseGUID, $BatchId) + $method = 'Get' + } else { + $Uri = ('https://{0}/v1.0/myorg/groups/{1}/datawarehouses/{2}/' -f $baseurl, $workspaceGUID, $dataWarehouseGUID) + $method = 'Post' + } + + return @{ + Uri = $Uri + Headers = $headers + Method = $method + ContentType = 'application/json' + } + #TODO: Change this to be a saved config property? +} diff --git a/FabricTools/private/Get-FileDefinitionParts.ps1 b/source/Private/Get-FileDefinitionParts.ps1 similarity index 100% rename from FabricTools/private/Get-FileDefinitionParts.ps1 rename to source/Private/Get-FileDefinitionParts.ps1 diff --git a/source/Private/Set-FabConfig.ps1 b/source/Private/Set-FabConfig.ps1 new file mode 100644 index 00000000..56429327 --- /dev/null +++ b/source/Private/Set-FabConfig.ps1 @@ -0,0 +1,37 @@ +# this is a workaround to get the variables set for now +# TODO: change to use PSFConfig? + +$script:FabricSession = [ordered]@{ + BaseApiUrl = 'https://api.fabric.microsoft.com/v1' + ResourceUrl = 'https://api.fabric.microsoft.com' + FabricToken = $null + HeaderParams = $null + ContentType = @{'Content-Type' = "application/json" } + KustoURL = "https://api.kusto.windows.net" + AccessToken = $null +} + +$script:AzureSession = [ordered]@{ + BaseApiUrl = "https://management.azure.com" + AccessToken = $null + Token = $null + HeaderParams = $null +} + +$script:PowerBI = [ordered]@{ + BaseApiUrl = "https://api.powerbi.com/v1.0/myorg" +} + +$FabricTools = @{ + FeatureFlags = @{ + AutoRenewExpiredToken = $true + } +} + +$FabricConfig = @{ + BaseUrl = "https://api.fabric.microsoft.com/v1" + ResourceUrl = "https://api.fabric.microsoft.com" + FabricHeaders = @{} + TenantIdGlobal = "" + TokenExpiresOn = "" +} diff --git a/FabricTools/private/Test-TokenExpired.ps1 b/source/Private/Test-TokenExpired.ps1 similarity index 97% rename from FabricTools/private/Test-TokenExpired.ps1 rename to source/Private/Test-TokenExpired.ps1 index 1f3da68f..f1cfa57d 100644 --- a/FabricTools/private/Test-TokenExpired.ps1 +++ b/source/Private/Test-TokenExpired.ps1 @@ -3,8 +3,8 @@ Checks if the Fabric token is expired and logs appropriate messages. .DESCRIPTION -The `Test-TokenExpired` function checks the expiration status of the Fabric token stored in the `$FabricConfig.TokenExpiresOn` variable. -If the token is expired, it logs an error message and provides guidance for refreshing the token. +The `Test-TokenExpired` function checks the expiration status of the Fabric token stored in the `$FabricConfig.TokenExpiresOn` variable. +If the token is expired, it logs an error message and provides guidance for refreshing the token. Otherwise, it logs that the token is still valid. .PARAMETER FabricConfig @@ -32,7 +32,7 @@ function Test-TokenExpired { try { # Ensure required properties have valid values - if ([string]::IsNullOrWhiteSpace($FabricConfig.TenantIdGlobal) -or + if ([string]::IsNullOrWhiteSpace($FabricConfig.TenantIdGlobal) -or [string]::IsNullOrWhiteSpace($FabricConfig.TokenExpiresOn)) { Write-Message -Message "Token details are missing. Please run 'Set-FabricApiHeaders' to configure them." -Level Error throw "MissingTokenDetailsException: Token details are missing." @@ -44,7 +44,7 @@ function Test-TokenExpired { } else { $tokenExpiryDate = [datetimeoffset]::Parse($FabricConfig.TokenExpiresOn) } - + # Check if the token is expired if ($tokenExpiryDate -le [datetimeoffset]::Now) { Write-Message -Message "Your authentication token has expired. Please sign in again to refresh your session." -Level Warning diff --git a/FabricTools/private/Write-Message.ps1 b/source/Private/Write-Message.ps1 similarity index 100% rename from FabricTools/private/Write-Message.ps1 rename to source/Private/Write-Message.ps1 diff --git a/FabricTools/public/Capacity/Get-AllFabricCapacities.ps1 b/source/Public/Capacity/Get-FabricCapacities.ps1 similarity index 98% rename from FabricTools/public/Capacity/Get-AllFabricCapacities.ps1 rename to source/Public/Capacity/Get-FabricCapacities.ps1 index 1e5434db..b2280eae 100644 --- a/FabricTools/public/Capacity/Get-AllFabricCapacities.ps1 +++ b/source/Public/Capacity/Get-FabricCapacities.ps1 @@ -21,7 +21,7 @@ .NOTES Alias: Get-AllFabCapacities #> -function Get-AllFabricCapacities { +function Get-FabricCapacities { # Define aliases for the function for flexibility. # Define parameters for the function @@ -49,8 +49,7 @@ function Get-AllFabricCapacities { # Get all resources of type "Microsoft.Fabric/capacities" and add them to the results array $res += Get-AzResource -ResourceGroupName $r.ResourceGroupName -resourcetype "Microsoft.Fabric/capacities" -ErrorAction SilentlyContinue } - } - else { + } else { # If no subscription ID is provided, get all subscriptions $subscriptions = Get-AzSubscription diff --git a/FabricTools/public/Capacity/Get-FabricCapacity.ps1 b/source/Public/Capacity/Get-FabricCapacity.ps1 similarity index 85% rename from FabricTools/public/Capacity/Get-FabricCapacity.ps1 rename to source/Public/Capacity/Get-FabricCapacity.ps1 index a36c88ca..8f563083 100644 --- a/FabricTools/public/Capacity/Get-FabricCapacity.ps1 +++ b/source/Public/Capacity/Get-FabricCapacity.ps1 @@ -14,11 +14,11 @@ The name of the capacity to retrieve. This parameter is optional. .EXAMPLE - Get-FabricCapacity -capacityId "capacity-12345" + Get-FabricCapacity -capacityId "capacity-12345" This example retrieves the capacity details for the capacity with ID "capacity-12345". .EXAMPLE - Get-FabricCapacity -capacityName "MyCapacity" + Get-FabricCapacity -capacityName "MyCapacity" This example retrieves the capacity details for the capacity named "MyCapacity". .NOTES @@ -49,44 +49,40 @@ function Get-FabricCapacity { Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired Write-Message -Message "Token validation completed." -Level Debug - + # Construct the API endpoint URL - $apiEndpointURI = "{0}/capacities" -f $FabricConfig.BaseUrl - + $apiEndpointURI = "capacities" + # Invoke the Fabric API to retrieve capacity details - $capacities = Invoke-FabricAPIRequest ` - -BaseURI $apiEndpointURI ` - -Headers $FabricConfig.FabricHeaders ` - -Method Get + $apiParams = @{ + Uri = $apiEndpointURI + Method = 'Get' + } + $capacities = (Invoke-FabricAPIRequest @apiParams).Value - # Filter results based on provided parameters $response = if ($capacityId) { $capacities | Where-Object { $_.Id -eq $capacityId } - } - elseif ($capacityName) { + } elseif ($capacityName) { $capacities | Where-Object { $_.DisplayName -eq $capacityName } - } - else { + } else { # No filter, return all capacities Write-Message -Message "No filter specified. Returning all capacities." -Level Debug return $capacities } - + # Handle results if ($response) { Write-Message -Message "Capacity found matching the specified criteria." -Level Debug return $response - } - else { + } else { Write-Message -Message "No capacity found matching the specified criteria." -Level Warning return $null } - } - catch { + } catch { # Capture and log error details $errorDetails = $_.Exception.Message Write-Message -Message "Failed to retrieve capacity. Error: $errorDetails" -Level Error return $null } -} \ No newline at end of file +} diff --git a/FabricTools/public/Capacity/Get-FabricCapacityRefreshables.ps1 b/source/Public/Capacity/Get-FabricCapacityRefreshables.ps1 similarity index 87% rename from FabricTools/public/Capacity/Get-FabricCapacityRefreshables.ps1 rename to source/Public/Capacity/Get-FabricCapacityRefreshables.ps1 index fa21ff63..49888dea 100644 --- a/FabricTools/public/Capacity/Get-FabricCapacityRefreshables.ps1 +++ b/source/Public/Capacity/Get-FabricCapacityRefreshables.ps1 @@ -1,4 +1,5 @@ -<# +function Get-FabricCapacityRefreshables { + <# .SYNOPSIS Retrieves the top refreshable capacities for the tenant. @@ -15,16 +16,15 @@ This example retrieves the top 5 refreshable capacities for the tenant. .NOTES The function retrieves the PowerBI access token and makes a GET request to the PowerBI API to retrieve the top refreshable capacities. It then returns the 'value' property of the response, which contains the capacities. -#> + #> -# This function retrieves the top refreshable capacities for the tenant. -function Get-FabricCapacityRefreshables { + # This function retrieves the top refreshable capacities for the tenant. # Define aliases for the function for flexibility. [Alias("Get-FabCapacityRefreshables")] # Define a mandatory parameter for the number of top refreshable capacities to retrieve. Param ( - [Parameter(Mandatory=$false)] + [Parameter(Mandatory = $false)] [string]$top = 5 ) diff --git a/FabricTools/public/Capacity/Get-FabricCapacitySkus.ps1 b/source/Public/Capacity/Get-FabricCapacitySkus.ps1 similarity index 80% rename from FabricTools/public/Capacity/Get-FabricCapacitySkus.ps1 rename to source/Public/Capacity/Get-FabricCapacitySkus.ps1 index 06e27a5f..713f4ae7 100644 --- a/FabricTools/public/Capacity/Get-FabricCapacitySkus.ps1 +++ b/source/Public/Capacity/Get-FabricCapacitySkus.ps1 @@ -1,20 +1,25 @@ -<# + +function Get-FabricCapacitySkus { + <# .SYNOPSIS Retrieves the fabric capacity information. .DESCRIPTION This function makes a GET request to the Fabric API to retrieve the tenant settings. +.PARAMETER subscriptionID +Specifies the subscription ID for the Azure subscription. + +.PARAMETER ResourceGroupName +Specifies the name of the resource group in which the Fabric capacity is located. + .PARAMETER capacity Specifies the capacity to retrieve information for. If not provided, all capacities will be retrieved. .EXAMPLE Get-FabricCapacitySkus -capacity "exampleCapacity" Retrieves the fabric capacity information for the specified capacity. - -#> - -function Get-FabricCapacitySkus { + #> # Define aliases for the function for flexibility. Param( @@ -22,7 +27,7 @@ function Get-FabricCapacitySkus { [string]$subscriptionID, [Parameter(Mandatory = $true)] [string]$ResourceGroupName, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string]$capacity ) diff --git a/FabricTools/public/Capacity/Get-FabricCapacityState.ps1 b/source/Public/Capacity/Get-FabricCapacityState.ps1 similarity index 90% rename from FabricTools/public/Capacity/Get-FabricCapacityState.ps1 rename to source/Public/Capacity/Get-FabricCapacityState.ps1 index 7b8f5307..4f779b34 100644 --- a/FabricTools/public/Capacity/Get-FabricCapacityState.ps1 +++ b/source/Public/Capacity/Get-FabricCapacityState.ps1 @@ -1,4 +1,5 @@ -<# +function Get-FabricCapacityState { + <# .SYNOPSIS Retrieves the state of a specific capacity. @@ -21,20 +22,19 @@ This example retrieves the state of a specific capacity given the subscription I .NOTES The function checks if the Azure token is null. If it is, it connects to the Azure account and retrieves the token. It then defines the headers for the GET request and the URL for the GET request. Finally, it makes the GET request and returns the response. -#> + #> -# This function retrieves the state of a specific capacity. -function Get-FabricCapacityState { + # This function retrieves the state of a specific capacity. # Define aliases for the function for flexibility. [Alias("Get-FabCapacityState")] # Define mandatory parameters for the subscription ID, resource group, and capacity. Param ( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string]$subscriptionID, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string]$resourcegroup, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string]$capacity ) diff --git a/FabricTools/public/Capacity/Get-FabricCapacityTenantOverrides.ps1 b/source/Public/Capacity/Get-FabricCapacityTenantOverrides.ps1 similarity index 90% rename from FabricTools/public/Capacity/Get-FabricCapacityTenantOverrides.ps1 rename to source/Public/Capacity/Get-FabricCapacityTenantOverrides.ps1 index ac6862bd..311ada92 100644 --- a/FabricTools/public/Capacity/Get-FabricCapacityTenantOverrides.ps1 +++ b/source/Public/Capacity/Get-FabricCapacityTenantOverrides.ps1 @@ -1,4 +1,5 @@ -<# +function Get-FabricCapacityTenantOverrides { + <# .SYNOPSIS Retrieves the tenant overrides for all capacities. @@ -15,10 +16,9 @@ This example retrieves the tenant overrides for all capacities. .NOTES The function retrieves the PowerBI access token and makes a GET request to the Fabric API to retrieve the tenant overrides for all capacities. It then returns the response of the GET request. -#> + #> -# This function retrieves the tenant overrides for all capacities. -function Get-FabricCapacityTenantOverrides { + # This function retrieves the tenant overrides for all capacities. # Define aliases for the function for flexibility. [Alias("Get-FabCapacityTenantOverrides")] diff --git a/FabricTools/public/Capacity/Get-FabricCapacityWorkload.ps1 b/source/Public/Capacity/Get-FabricCapacityWorkload.ps1 similarity index 90% rename from FabricTools/public/Capacity/Get-FabricCapacityWorkload.ps1 rename to source/Public/Capacity/Get-FabricCapacityWorkload.ps1 index f7036ae3..92cacceb 100644 --- a/FabricTools/public/Capacity/Get-FabricCapacityWorkload.ps1 +++ b/source/Public/Capacity/Get-FabricCapacityWorkload.ps1 @@ -1,4 +1,5 @@ -<# +function Get-FabricCapacityWorkload { + <# .SYNOPSIS Retrieves the workloads for a specific capacity. @@ -18,16 +19,15 @@ This example retrieves the workloads for a specific capacity given the capacity .NOTES The function retrieves the PowerBI access token and makes a GET request to the PowerBI API to retrieve the workloads for the specified capacity. It then returns the 'value' property of the response, which contains the workloads. -#> + #> -# This function retrieves the workloads for a specific capacity. -function Get-FabricCapacityWorkload { + # This function retrieves the workloads for a specific capacity. # Define aliases for the function for flexibility. [Alias("Get-FabCapacityWorkload")] # Define a mandatory parameter for the capacity ID. Param ( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string]$capacityID ) diff --git a/FabricTools/public/Capacity/Resume-FabricCapacity.ps1 b/source/Public/Capacity/Resume-FabricCapacity.ps1 similarity index 97% rename from FabricTools/public/Capacity/Resume-FabricCapacity.ps1 rename to source/Public/Capacity/Resume-FabricCapacity.ps1 index edf0d2f0..a1ee73ab 100644 --- a/FabricTools/public/Capacity/Resume-FabricCapacity.ps1 +++ b/source/Public/Capacity/Resume-FabricCapacity.ps1 @@ -1,4 +1,5 @@ -<# +function Resume-FabricCapacity { + <# .SYNOPSIS Resumes a capacity. @@ -21,10 +22,10 @@ This example resumes a capacity given the subscription ID, resource group, and c .NOTES The function defines parameters for the subscription ID, resource group, and capacity. If the 'azToken' environment variable is null, it connects to the Azure account and sets the 'azToken' environment variable. It then defines the headers for the request, defines the URI for the request, and makes a GET request to the URI. -#> + #> + + # This function resumes a capacity. -# This function resumes a capacity. -function Resume-FabricCapacity { # Define aliases for the function for flexibility. [Alias("Resume-FabCapacity")] [CmdletBinding(SupportsShouldProcess)] diff --git a/FabricTools/public/Capacity/Suspend-FabricCapacity.ps1 b/source/Public/Capacity/Suspend-FabricCapacity.ps1 similarity index 99% rename from FabricTools/public/Capacity/Suspend-FabricCapacity.ps1 rename to source/Public/Capacity/Suspend-FabricCapacity.ps1 index d7c3fe94..7461bf5a 100644 --- a/FabricTools/public/Capacity/Suspend-FabricCapacity.ps1 +++ b/source/Public/Capacity/Suspend-FabricCapacity.ps1 @@ -1,4 +1,6 @@ -<# +# This function suspends a capacity. +function Suspend-FabricCapacity { + <# .SYNOPSIS Suspends a capacity. @@ -21,10 +23,8 @@ This example suspends a capacity given the subscription ID, resource group, and .NOTES The function defines parameters for the subscription ID, resource group, and capacity. If the 'azToken' environment variable is null, it connects to the Azure account and sets the 'azToken' environment variable. It then defines the headers for the request, defines the URI for the request, and makes a GET request to the URI. -#> + #> -# This function suspends a capacity. -function Suspend-FabricCapacity { # Define aliases for the function for flexibility. [Alias("Suspend-PowerBICapacity", "Suspend-FabCapacity")] [CmdletBinding(SupportsShouldProcess)] diff --git a/source/Public/Config/Get-FabricConfig.ps1 b/source/Public/Config/Get-FabricConfig.ps1 new file mode 100644 index 00000000..608e5748 --- /dev/null +++ b/source/Public/Config/Get-FabricConfig.ps1 @@ -0,0 +1,32 @@ +<# +.SYNOPSIS +Gets the configuration for use with all functions in the PSFabricTools module. + +.DESCRIPTION +Gets the configuration for use with all functions in the PSFabricTools module. + +.PARAMETER ConfigName +The name of the configuration to retrieve. + +.EXAMPLE +PS> Get-FabricConfig + +Gets all the configuration values for the PSFabricTools module and outputs them + +.EXAMPLE +PS> Get-FabricConfig -ConfigName BaseUrl + +Gets the BaseUrl configuration value for the PSFabricTools module. +#> + +function Get-FabricConfig { + param ( + [String]$ConfigName + ) + + if ($ConfigName) { + Get-PSFConfig -Module PSFabricTools -Name $ConfigName + } else { + Get-PSFConfig -Module PSFabricTools + } +} diff --git a/source/Public/Config/Set-FabricConfig.ps1 b/source/Public/Config/Set-FabricConfig.ps1 new file mode 100644 index 00000000..b94211f7 --- /dev/null +++ b/source/Public/Config/Set-FabricConfig.ps1 @@ -0,0 +1,61 @@ +<# +.SYNOPSIS +Register the configuration for use with all functions in the PSFabricTools module. + +.DESCRIPTION +Register the configuration for use with all functions in the PSFabricTools module. + +.PARAMETER WorkspaceGUID +This is the workspace GUID in which the Data Warehouse resides. + +.PARAMETER DataWarehouseGUID +The GUID for the Data Warehouse which we want to retrieve restore points for. + +.PARAMETER BaseUrl +Defaults to api.powerbi.com + +.PARAMETER SkipPersist +If set, the configuration will not be persisted to the registry. + +.EXAMPLE +PS> Set-FabricConfig -WorkspaceGUID 'GUID-GUID-GUID-GUID' -DataWarehouseGUID 'GUID-GUID-GUID-GUID' + +Registers the specified Fabric Data Warehouse configuration for use with all functions in the PSFabricTools module. + +.EXAMPLE +PS> Set-FabricConfig -WorkspaceGUID 'GUID-GUID-GUID-GUID' -DataWarehouseGUID 'GUID-GUID-GUID-GUID' -SkipPersist + +Registers the specified Fabric Data Warehouse configuration for use with all functions in the PSFabricTools module - but does not persist the values, only uses them for the current session. + +#> + +function Set-FabricConfig { + [CmdletBinding(SupportsShouldProcess)] + param ( + [String]$WorkspaceGUID, + + [String]$DataWarehouseGUID, + + $BaseUrl = 'api.powerbi.com', + + [switch]$SkipPersist + ) + + if ($PSCmdlet.ShouldProcess("Setting Fabric Configuration")) { + + if ($BaseUrl) { + Set-PSFConfig -Module PSFabricTools -Name BaseUrl -Value $BaseUrl + } + if ($WorkspaceGUID) { + Set-PSFConfig -Module PSFabricTools -Name WorkspaceGUID -Value $WorkspaceGUID + } + if ($DataWarehouseGUID) { + Set-PSFConfig -Module PSFabricTools -Name DataWarehouseGUID -Value $DataWarehouseGUID + } + + # Register the config values in the registry if skip persist is not set + if (-not $SkipPersist) { + Register-PSFConfig -Module PSFabricTools -Scope SystemMandatory + } + } +} diff --git a/source/Public/Confirm-FabricAuthToken.ps1 b/source/Public/Confirm-FabricAuthToken.ps1 new file mode 100644 index 00000000..7400251c --- /dev/null +++ b/source/Public/Confirm-FabricAuthToken.ps1 @@ -0,0 +1,50 @@ +<# +.SYNOPSIS + Check whether the Fabric API authentication token is set and not expired and reset it if necessary. + +.DESCRIPTION + The Confirm-FabricAuthToken function retrieves the Fabric API authentication token. If the token is not already set, it calls the Set-FabricAuthToken function to set it. It then outputs the token. + +.EXAMPLE + Confirm-FabricAuthToken + + This command retrieves the Fabric API authentication token. + +.INPUTS + None. You cannot pipe inputs to this function. + +.OUTPUTS + Returns object as Get-FabricDebugInfo function + +.NOTES + +#> + +function Confirm-FabricAuthToken { + [CmdletBinding()] + param ( ) + + Write-Verbose "Check if session is established and token not expired." + + # Check if the Fabric token is already set + if (!$FabricSession.FabricToken -or !$AzureSession.AccessToken) { + Write-Output "Confirm-FabricAuthToken::Set-FabricAuthToken" + Set-FabricAuthToken | Out-Null + } + + $now = (Get-Date) + $s = Get-FabricDebugInfo + if ($FabricSession.AccessToken.ExpiresOn -lt $now ) { + Write-Output "Confirm-FabricAuthToken::Set-FabricAuthToken#1" + Set-FabricAuthToken -reset | Out-Null + } + + if ($s.AzureSession.AccessToken.ExpiresOn -lt $now ) { + Write-Output "Confirm-FabricAuthToken::Set-FabricAuthToken#2" + Set-FabricAuthToken -reset | Out-Null + } + + $s = Get-FabricDebugInfo + return $s + +} diff --git a/FabricTools/public/Connect-FabricAccount.ps1 b/source/Public/Connect-FabricAccount.ps1 similarity index 53% rename from FabricTools/public/Connect-FabricAccount.ps1 rename to source/Public/Connect-FabricAccount.ps1 index a96a1cec..253150d3 100644 --- a/FabricTools/public/Connect-FabricAccount.ps1 +++ b/source/Public/Connect-FabricAccount.ps1 @@ -1,7 +1,6 @@ function Connect-FabricAccount { -#Requires -Version 7.1 -<# + <# .SYNOPSIS Connects to the Fabric WebAPI. @@ -26,31 +25,31 @@ function Connect-FabricAccount { .LINK Connect-AzAccount https://learn.microsoft.com/de-de/powershell/module/az.accounts/connect-azaccount?view=azps-12.4.0 -#> + #> -[CmdletBinding()] + [CmdletBinding()] param ( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string]$TenantId ) -begin { -} + begin { + } -process { - Write-Verbose "Connect to Azure Account" - Connect-AzAccount -TenantId $TenantId | Out-Null + process { + Write-Verbose "Connect to Azure Account" + Connect-AzAccount -TenantId $TenantId | Out-Null - Write-Verbose "Get authentication token" - $FabricSession.FabricToken = (Get-AzAccessToken -ResourceUrl $FabricSession.BaseApiUrl).Token - Write-Verbose "Token: $($FabricSession.FabricToken)" + Write-Verbose "Get authentication token" + $FabricSession.FabricToken = (Get-AzAccessToken -ResourceUrl $FabricSession.BaseApiUrl).Token + Write-Verbose "Token: $($FabricSession.FabricToken)" - Write-Verbose "Setup headers for API calls" - $FabricSession.HeaderParams = @{'Authorization'="Bearer {0}" -f $FabricSession.FabricToken} - Write-Verbose "HeaderParams: $($FabricSession.HeaderParams)" -} + Write-Verbose "Setup headers for API calls" + $FabricSession.HeaderParams = @{'Authorization' = "Bearer {0}" -f $FabricSession.FabricToken } + Write-Verbose "HeaderParams: $($FabricSession.HeaderParams)" + } -end { -} + end { + } -} \ No newline at end of file +} diff --git a/FabricTools/public/Copy Job/Get-FabricCopyJob.ps1 b/source/Public/Copy Job/Get-FabricCopyJob.ps1 similarity index 74% rename from FabricTools/public/Copy Job/Get-FabricCopyJob.ps1 rename to source/Public/Copy Job/Get-FabricCopyJob.ps1 index 043ff8de..8f616ae7 100644 --- a/FabricTools/public/Copy Job/Get-FabricCopyJob.ps1 +++ b/source/Public/Copy Job/Get-FabricCopyJob.ps1 @@ -1,9 +1,10 @@ -<# +function Get-FabricCopyJob { + <# .SYNOPSIS Retrieves CopyJob details from a specified Microsoft Fabric workspace. .DESCRIPTION - This function retrieves CopyJob details from a specified workspace using either the provided CopyJobId or CopyJobName. + This function retrieves CopyJob details from a specified workspace using either the provided CopyJobId or CopyJob. It handles token validation, constructs the API URL, makes the API request, and processes the response. .PARAMETER WorkspaceId @@ -12,24 +13,23 @@ .PARAMETER CopyJobId The unique identifier of the CopyJob to retrieve. This parameter is optional. -.PARAMETER CopyJobName +.PARAMETER CopyJob The name of the CopyJob to retrieve. This parameter is optional. .EXAMPLE - FabricCopyJob -WorkspaceId "workspace-12345" -CopyJobId "CopyJob-67890" + FabricCopyJob -WorkspaceId "workspace-12345" -CopyJobId "CopyJob-67890" This example retrieves the CopyJob details for the CopyJob with ID "CopyJob-67890" in the workspace with ID "workspace-12345". .EXAMPLE - FabricCopyJob -WorkspaceId "workspace-12345" -CopyJobName "My CopyJob" + FabricCopyJob -WorkspaceId "workspace-12345" -CopyJob "My CopyJob" This example retrieves the CopyJob details for the CopyJob named "My CopyJob" in the workspace with ID "workspace-12345". .NOTES - - Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. - - Calls `Test-TokenExpired` to ensure token validity before making the API request. + Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. + Calls `Test-TokenExpired` to ensure token validity before making the API request. Author: Tiago Balabuch -#> -function FabricCopyJob { + #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] @@ -48,8 +48,8 @@ function FabricCopyJob { try { # Handle ambiguous input - if ($CopyJobId -and $CopyJobName) { - Write-Message -Message "Both 'CopyJobId' and 'CopyJobName' were provided. Please specify only one." -Level Error + if ($CopyJobId -and $CopyJob) { + Write-Message -Message "Both 'CopyJobId' and 'CopyJob' were provided. Please specify only one." -Level Error return $null } @@ -57,25 +57,24 @@ function FabricCopyJob { Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired Write-Message -Message "Token validation completed." -Level Debug - + # Construct the API endpoint URL - $apiEndpointURI = "{0}/workspaces/{1}/copyJobs" -f $FabricConfig.BaseUrl, $WorkspaceId - + $apiEndpointURI = "workspaces/{0}/copyJobs" -f $WorkspaceId + # Invoke the Fabric API to retrieve capacity details - $copyJobs = Invoke-FabricAPIRequest ` - -BaseURI $apiEndpointURI ` - -Headers $FabricConfig.FabricHeaders ` - -Method Get + $apiParams = @{ + Uri = $apiEndpointURI + Method = 'Get' + } + $copyJobs = Invoke-FabricAPIRequest @apiParams # Filter results based on provided parameters $response = if ($CopyJobId) { $copyJobs | Where-Object { $_.Id -eq $CopyJobId } - } - elseif ($CopyJobName) { - $copyJobs | Where-Object { $_.DisplayName -eq $CopyJobName } - } - else { + } elseif ($CopyJob) { + $copyJobs | Where-Object { $_.DisplayName -eq $CopyJob } + } else { # Return all CopyJobs if no filter is provided Write-Message -Message "No filter provided. Returning all CopyJobs." -Level Debug $copyJobs @@ -85,16 +84,14 @@ function FabricCopyJob { if ($response) { Write-Message -Message "CopyJob found matching the specified criteria." -Level Debug return $response - } - else { + } else { Write-Message -Message "No CopyJob found matching the provided criteria." -Level Warning return $null } - } - catch { + } catch { # Step 10: Capture and log error details $errorDetails = $_.Exception.Message Write-Message -Message "Failed to retrieve CopyJob. Error: $errorDetails" -Level Error - } - + } + } diff --git a/FabricTools/public/Copy Job/Get-FabricCopyJobDefinition.ps1 b/source/Public/Copy Job/Get-FabricCopyJobDefinition.ps1 similarity index 88% rename from FabricTools/public/Copy Job/Get-FabricCopyJobDefinition.ps1 rename to source/Public/Copy Job/Get-FabricCopyJobDefinition.ps1 index bfc2fcff..96bdbb56 100644 --- a/FabricTools/public/Copy Job/Get-FabricCopyJobDefinition.ps1 +++ b/source/Public/Copy Job/Get-FabricCopyJobDefinition.ps1 @@ -3,7 +3,7 @@ Retrieves the definition of a Copy Job from a specific workspace in Microsoft Fabric. .DESCRIPTION -This function fetches the Copy Job's content or metadata from a workspace. +This function fetches the Copy Job's content or metadata from a workspace. It supports both synchronous and asynchronous operations, with detailed logging and error handling. .PARAMETER WorkspaceId @@ -50,7 +50,7 @@ function Get-FabricCopyJobDefinition { Write-Message -Message "Authentication token is valid." -Level Debug # Step 2: Construct the API endpoint URL for retrieving the Copy Job definition. - $apiEndpointUrl = "{0}/workspaces/{1}/copyJobs/{2}/getDefinition" -f $FabricConfig.BaseUrl, $WorkspaceId, $CopyJobId + $apiEndpointUrl = "workspaces/{0}/copyJobs/{1}/getDefinition" -f $WorkspaceId, $CopyJobId # Step 3: Append the format query parameter if specified by the user. if ($CopyJobFormat) { @@ -59,17 +59,17 @@ function Get-FabricCopyJobDefinition { Write-Message -Message "Constructed API Endpoint URL: $apiEndpointUrl" -Level Debug # Step 4: Execute the API request to retrieve the Copy Job definition. - $response = Invoke-FabricAPIRequest ` - -BaseURI $apiEndpointUrl ` - -Headers $FabricConfig.FabricHeaders ` - -Method Post + $apiParams = @{ + Uri = $apiEndpointUrl + Method = 'POST' + } + $response = Invoke-FabricAPIRequest @apiParams # Step 5: Return the API response containing the Copy Job definition. return $response - } - catch { + } catch { # Step 6: Capture and log detailed error information for troubleshooting. $errorDetails = $_.Exception.Message Write-Message -Message "Failed to retrieve Copy Job definition. Error: $errorDetails" -Level Error - } + } } diff --git a/FabricTools/public/Copy Job/New-FabricCopyJob.ps1 b/source/Public/Copy Job/New-FabricCopyJob.ps1 similarity index 89% rename from FabricTools/public/Copy Job/New-FabricCopyJob.ps1 rename to source/Public/Copy Job/New-FabricCopyJob.ps1 index 7ac76e1c..6675f6fb 100644 --- a/FabricTools/public/Copy Job/New-FabricCopyJob.ps1 +++ b/source/Public/Copy Job/New-FabricCopyJob.ps1 @@ -31,7 +31,7 @@ Author: Tiago Balabuch #> function New-FabricCopyJob { - [CmdletBinding()] + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -49,7 +49,7 @@ function New-FabricCopyJob { [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$CopyJobPathDefinition, - + [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$CopyJobPathPlatformDefinition @@ -62,7 +62,7 @@ function New-FabricCopyJob { Write-Message -Message "Token validation completed." -Level Debug # Step 2: Construct the API URL - $apiEndpointURI = "{0}/workspaces/{1}/warehouses" -f $FabricConfig.BaseUrl, $WorkspaceId + $apiEndpointURI = "workspaces/{0}/copyJobs" -f $WorkspaceId Write-Message -Message "API Endpoint: $apiEndpointURI" -Level Debug # Step 3: Construct the request body @@ -92,8 +92,7 @@ function New-FabricCopyJob { payload = $CopyJobEncodedContent payloadType = "InlineBase64" } - } - else { + } else { Write-Message -Message "Invalid or empty content in Copy Job definition." -Level Error return $null } @@ -116,8 +115,7 @@ function New-FabricCopyJob { payload = $CopyJobEncodedPlatformContent payloadType = "InlineBase64" } - } - else { + } else { Write-Message -Message "Invalid or empty content in platform definition." -Level Error return $null } @@ -126,18 +124,21 @@ function New-FabricCopyJob { $bodyJson = $body | ConvertTo-Json -Depth 10 Write-Message -Message "Request Body: $bodyJson" -Level Debug + if($PSCmdlet.ShouldProcess($apiEndpointURI, "Create Copy Job")) { + # Step 6: Make the API request - $response = Invoke-FabricAPIRequest ` - -BaseURI $apiEndpointURI ` - -Headers $FabricConfig.FabricHeaders ` - -Method Post ` - -Body $bodyJson - - Write-Message -Message "Copy Job created successfully!" -Level Info - return $response - + $apiParams = @{ + Uri = $apiEndpointURI + Method = 'Post' + Body = $bodyJson + } + $response = Invoke-FabricAPIRequest @apiParams } - catch { + + Write-Message -Message "Copy Job created successfully!" -Level Info + return $response + + } catch { # Step 7: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to create Copy Job. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Copy Job/Remove-FabricCopyJob.ps1 b/source/Public/Copy Job/Remove-FabricCopyJob.ps1 similarity index 74% rename from FabricTools/public/Copy Job/Remove-FabricCopyJob.ps1 rename to source/Public/Copy Job/Remove-FabricCopyJob.ps1 index d1613966..f9a30175 100644 --- a/FabricTools/public/Copy Job/Remove-FabricCopyJob.ps1 +++ b/source/Public/Copy Job/Remove-FabricCopyJob.ps1 @@ -3,7 +3,7 @@ Deletes a Copy Job from a specified Microsoft Fabric workspace. .DESCRIPTION - This function performs a DELETE operation on the Microsoft Fabric API to remove a Copy Job + This function performs a DELETE operation on the Microsoft Fabric API to remove a Copy Job from the specified workspace using the provided WorkspaceId and CopyJobId parameters. .PARAMETER WorkspaceId @@ -23,7 +23,7 @@ Author: Tiago Balabuch #> function Remove-FabricCopyJob { - [CmdletBinding()] + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -40,21 +40,23 @@ function Remove-FabricCopyJob { Write-Message -Message "Token validation completed." -Level Debug # Construct the API endpoint URI - $apiEndpointURI = "{0}/workspaces/{1}/copyJobs/{2}" -f $FabricConfig.BaseUrl, $WorkspaceId, $CopyJobId + $apiEndpointURI = "workspaces/{0}/copyJobs/{1}" -f $WorkspaceId, $CopyJobId Write-Message -Message "API Endpoint: $apiEndpointURI" -Level Debug - # Make the API request - $response = Invoke-FabricAPIRequest ` - -Headers $FabricConfig.FabricHeaders ` - -BaseURI $apiEndpointURI ` - -Method Delete - - Write-Message -Message "Copy Job '$CopyJobId' deleted successfully from workspace '$WorkspaceId'." -Level Info - return $response + if($PSCmdlet.ShouldProcess($apiEndpointURI, "Delete Copy Job")) { + # Make the API request + $apiParams = @{ + Uri = $apiEndpointURI + Method = 'DELETE' + } + $response = Invoke-FabricAPIRequest @apiParams } - catch { - # Log and handle errors + Write-Message -Message "Copy Job '$CopyJobId' deleted successfully from workspace '$WorkspaceId'." -Level Info + return $response + +} catch { + # Log and handle errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to delete Copy Job '$CopyJobId' from workspace '$WorkspaceId'. Error: $errorDetails" -Level Error } diff --git a/FabricTools/public/Copy Job/Update-FabricCopyJob.ps1 b/source/Public/Copy Job/Update-FabricCopyJob.ps1 similarity index 83% rename from FabricTools/public/Copy Job/Update-FabricCopyJob.ps1 rename to source/Public/Copy Job/Update-FabricCopyJob.ps1 index 0db436dc..7ae97d93 100644 --- a/FabricTools/public/Copy Job/Update-FabricCopyJob.ps1 +++ b/source/Public/Copy Job/Update-FabricCopyJob.ps1 @@ -3,7 +3,7 @@ Updates an existing Copy Job in a specified Microsoft Fabric workspace. .DESCRIPTION - Sends a PATCH request to the Microsoft Fabric API to update an existing Copy Job + Sends a PATCH request to the Microsoft Fabric API to update an existing Copy Job in the specified workspace. Allows updating the Copy Job's name and optionally its description. .PARAMETER WorkspaceId @@ -28,13 +28,14 @@ Author: Tiago Balabuch #> -function Update-FabricCopyJob { - [CmdletBinding()] +function Update-FabricCopyJob +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [string]$WorkspaceId, - + [string]$WorkspaceId, + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$CopyJobId, @@ -49,7 +50,8 @@ function Update-FabricCopyJob { [string]$CopyJobDescription ) - try { + try + { # Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -64,25 +66,30 @@ function Update-FabricCopyJob { displayName = $CopyJobName } - if ($CopyJobDescription) { + if ($CopyJobDescription) + { $body.description = $CopyJobDescription } # Convert the body to JSON $bodyJson = $body | ConvertTo-Json Write-Message -Message "Request Body: $bodyJson" -Level Debug - - # Make the API request - $response = Invoke-FabricAPIRequest ` - -Headers $FabricConfig.FabricHeaders ` - -BaseURI $apiEndpointURI ` - -Method Patch ` - -Body $bodyJson + + if ($PSCmdlet.ShouldProcess($apiEndpointURI, "Update Copy Job")) + { + # Step 4: Make the API request + $response = Invoke-FabricAPIRequest ` + -Headers $FabricConfig.FabricHeaders ` + -BaseURI $apiEndpointURI ` + -method Patch ` + -body $bodyJson + } Write-Message -Message "Copy Job '$CopyJobName' updated successfully!" -Level Info return $response } - catch { + catch + { # Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to update Copy Job. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Copy Job/Update-FabricCopyJobDefinition.ps1 b/source/Public/Copy Job/Update-FabricCopyJobDefinition.ps1 similarity index 90% rename from FabricTools/public/Copy Job/Update-FabricCopyJobDefinition.ps1 rename to source/Public/Copy Job/Update-FabricCopyJobDefinition.ps1 index 4fd57f0f..6a1de71a 100644 --- a/FabricTools/public/Copy Job/Update-FabricCopyJobDefinition.ps1 +++ b/source/Public/Copy Job/Update-FabricCopyJobDefinition.ps1 @@ -3,7 +3,7 @@ Updates the definition of a Copy Job in a Microsoft Fabric workspace. .DESCRIPTION -This function updates the content or metadata of a Copy Job within a Microsoft Fabric workspace. +This function updates the content or metadata of a Copy Job within a Microsoft Fabric workspace. The Copy Job content and platform-specific definitions can be provided as file paths, which will be encoded as Base64 and sent in the request. .PARAMETER WorkspaceId @@ -38,7 +38,7 @@ Author: Tiago Balabuch #> function Update-FabricCopyJobDefinition { - [CmdletBinding()] + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -51,7 +51,7 @@ function Update-FabricCopyJobDefinition { [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$CopyJobPathDefinition, - + [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$CopyJobPathPlatformDefinition @@ -67,7 +67,7 @@ function Update-FabricCopyJobDefinition { $apiEndpointUrl = "{0}/workspaces/{1}/copyJobs/{2}/updateDefinition" -f $FabricConfig.BaseUrl, $WorkspaceId, $CopyJobId if ($CopyJobPathPlatformDefinition) { - $apiEndpointUrl = "?updateMetadata=true" -f $apiEndpointUrl + $apiEndpointUrl = "?updateMetadata=true" -f $apiEndpointUrl } Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug @@ -75,12 +75,12 @@ function Update-FabricCopyJobDefinition { $body = @{ definition = @{ parts = @() - } + } } - + if ($CopyJobPathDefinition) { $CopyJobEncodedContent = Convert-ToBase64 -filePath $CopyJobPathDefinition - + if (-not [string]::IsNullOrEmpty($CopyJobEncodedContent)) { # Add new part to the parts array $body.definition.parts += @{ @@ -88,8 +88,7 @@ function Update-FabricCopyJobDefinition { payload = $CopyJobEncodedContent payloadType = "InlineBase64" } - } - else { + } else { Write-Message -Message "Invalid or empty content in Copy Job definition." -Level Error return $null } @@ -104,8 +103,7 @@ function Update-FabricCopyJobDefinition { payload = $CopyJobEncodedPlatformContent payloadType = "InlineBase64" } - } - else { + } else { Write-Message -Message "Invalid or empty content in platform definition." -Level Error return $null } @@ -114,19 +112,20 @@ function Update-FabricCopyJobDefinition { $bodyJson = $body | ConvertTo-Json -Depth 10 Write-Message -Message "Request Body: $bodyJson" -Level Debug - # Step 4: Make the API request - $response = Invoke-FabricAPIRequest ` - -BaseURI $apiEndpointURI ` - -Headers $FabricConfig.FabricHeaders ` - -Method Post ` - -Body $bodyJson + if($PSCmdlet.ShouldProcess($apiEndpointUrl, "Update Copy Job Definition")) { + # Step 4: Make the API request + $response = Invoke-FabricAPIRequest ` + -BaseURI $apiEndpointUrl ` + -Headers $FabricConfig.FabricHeaders ` + -Method Post ` + -Body $bodyJson + } - Write-Message -Message "Copy Job updated successfully!" -Level Info + Write-Message -Message "Copy Job updated successfully!" -Level Info return $response - - - } - catch { + + + } catch { # Step 6: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to update Copy Job. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Dashboard/Get-FabricDashboard.ps1 b/source/Public/Dashboard/Get-FabricDashboard.ps1 similarity index 82% rename from FabricTools/public/Dashboard/Get-FabricDashboard.ps1 rename to source/Public/Dashboard/Get-FabricDashboard.ps1 index ad267a30..b938d89d 100644 --- a/FabricTools/public/Dashboard/Get-FabricDashboard.ps1 +++ b/source/Public/Dashboard/Get-FabricDashboard.ps1 @@ -17,7 +17,7 @@ - Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. - Calls `Test-TokenExpired` to ensure token validity before making the API request. - Author: Tiago Balabuch + Author: Tiago Balabuch #> function Get-FabricDashboard { @@ -35,20 +35,20 @@ function Get-FabricDashboard { Write-Message -Message "Token validation completed." -Level Debug # Construct the API endpoint URL - $apiEndpointURI = "{0}/workspaces/{1}/dashboards" -f $FabricConfig.BaseUrl, $WorkspaceId + $apiEndpointURI = "workspaces/{0}/dashboards" -f $WorkspaceId # Invoke the Fabric API to retrieve capacity details - $Dashboards = Invoke-FabricAPIRequest ` - -BaseURI $apiEndpointURI ` - -Headers $FabricConfig.FabricHeaders ` - -Method Get - + $apiParams = @{ + Uri = $apiEndpointURI + Method = 'Get' + } + $Dashboards = Invoke-FabricAPIRequest @apiParams + return $Dashboards - } - catch { + } catch { # Capture and log error details $errorDetails = $_.Exception.Message Write-Message -Message "Failed to retrieve Dashboard. Error: $errorDetails" -Level Error - } + } } diff --git a/FabricTools/public/Data Pipeline/Get-FabricDataPipeline.ps1 b/source/Public/Data Pipeline/Get-FabricDataPipeline.ps1 similarity index 81% rename from FabricTools/public/Data Pipeline/Get-FabricDataPipeline.ps1 rename to source/Public/Data Pipeline/Get-FabricDataPipeline.ps1 index 6f944615..060e2e0d 100644 --- a/FabricTools/public/Data Pipeline/Get-FabricDataPipeline.ps1 +++ b/source/Public/Data Pipeline/Get-FabricDataPipeline.ps1 @@ -1,4 +1,5 @@ -<# +function Get-FabricDataPipeline { + <# .SYNOPSIS Retrieves data pipelines from a specified Microsoft Fabric workspace. @@ -9,18 +10,18 @@ .PARAMETER WorkspaceId The unique identifier of the workspace where the Data Pipeline exists. This parameter is mandatory. -.PARAMETER Data PipelineId +.PARAMETER DataPipelineId The unique identifier of the Data Pipeline to retrieve. This parameter is optional. -.PARAMETER Data PipelineName +.PARAMETER DataPipelineName The name of the Data Pipeline to retrieve. This parameter is optional. .EXAMPLE - Get-FabricData Pipeline -WorkspaceId "workspace-12345" -Data PipelineId "Data Pipeline-67890" + Get-FabricData Pipeline -WorkspaceId "workspace-12345" -Data PipelineId "Data Pipeline-67890" This example retrieves the Data Pipeline details for the Data Pipeline with ID "Data Pipeline-67890" in the workspace with ID "workspace-12345". .EXAMPLE - Get-FabricData Pipeline -WorkspaceId "workspace-12345" -Data PipelineName "My Data Pipeline" + Get-FabricData Pipeline -WorkspaceId "workspace-12345" -Data PipelineName "My Data Pipeline" This example retrieves the Data Pipeline details for the Data Pipeline named "My Data Pipeline" in the workspace with ID "workspace-12345". .NOTES @@ -28,8 +29,7 @@ - Calls `Test-TokenExpired` to ensure token validity before making the API request. Author: Tiago Balabuch -#> -function Get-FabricDataPipeline { + #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] @@ -59,22 +59,17 @@ function Get-FabricDataPipeline { Write-Message -Message "Token validation completed." -Level Debug # Construct the API endpoint URL - $apiEndpointURI = "{0}/workspaces/{1}/dataPipelines" -f $FabricConfig.BaseUrl, $WorkspaceId - - # Invoke the Fabric API to retrieve capacity details - $DataPipelines = Invoke-FabricAPIRequest ` - -BaseURI $apiEndpointURI ` - -Headers $FabricConfig.FabricHeaders ` - -Method Get - + $apiEndpointURI = ("workspaces/{0}/dataPipelines" -f $WorkspaceId) + + # Invoke the Fabric API to retrieve data pipeline details + $DataPipelines = (Invoke-FabricAPIRequest -uri $apiEndpointURI -Method Get).Value + # Filter results based on provided parameters $response = if ($DataPipelineId) { $DataPipelines | Where-Object { $_.Id -eq $DataPipelineId } - } - elseif ($DataPipelineName) { + } elseif ($DataPipelineName) { $DataPipelines | Where-Object { $_.DisplayName -eq $DataPipelineName } - } - else { + } else { # Return all DataPipelines if no filter is provided Write-Message -Message "No filter provided. Returning all DataPipelines." -Level Debug $DataPipelines @@ -84,16 +79,14 @@ function Get-FabricDataPipeline { if ($response) { Write-Message -Message "DataPipeline found matching the specified criteria." -Level Debug return $response - } - else { + } else { Write-Message -Message "No DataPipeline found matching the provided criteria." -Level Warning return $null } - } - catch { + } catch { # Capture and log error details $errorDetails = $_.Exception.Message Write-Message -Message "Failed to retrieve DataPipeline. Error: $errorDetails" -Level Error - } - + } + } diff --git a/FabricTools/public/Data Pipeline/New-FabricDataPipeline.ps1 b/source/Public/Data Pipeline/New-FabricDataPipeline.ps1 similarity index 75% rename from FabricTools/public/Data Pipeline/New-FabricDataPipeline.ps1 rename to source/Public/Data Pipeline/New-FabricDataPipeline.ps1 index 5daa2ff3..221de75b 100644 --- a/FabricTools/public/Data Pipeline/New-FabricDataPipeline.ps1 +++ b/source/Public/Data Pipeline/New-FabricDataPipeline.ps1 @@ -3,8 +3,8 @@ Creates a new DataPipeline in a specified Microsoft Fabric workspace. .DESCRIPTION - This function sends a POST request to the Microsoft Fabric API to create a new DataPipeline - in the specified workspace. It supports optional parameters for DataPipeline description + This function sends a POST request to the Microsoft Fabric API to create a new DataPipeline + in the specified workspace. It supports optional parameters for DataPipeline description and path definitions for the DataPipeline content. .PARAMETER WorkspaceId @@ -17,7 +17,7 @@ An optional description for the DataPipeline. .EXAMPLE - New-FabricDataPipeline -WorkspaceId "workspace-12345" -DataPipelineName "New DataPipeline" + New-FabricDataPipeline -WorkspaceId "workspace-12345" -DataPipelineName "New DataPipeline" This example creates a new DataPipeline named "New DataPipeline" in the workspace with ID "workspace-12345" and uploads the definition file from the specified path. .NOTES @@ -27,8 +27,9 @@ Author: Tiago Balabuch #> -function New-FabricDataPipeline { - [CmdletBinding()] +function New-FabricDataPipeline +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -44,14 +45,15 @@ function New-FabricDataPipeline { [string]$DataPipelineDescription ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired Write-Message -Message "Token validation completed." -Level Debug # Step 2: Construct the API URL - $apiEndpointURI = "{0}/workspaces/{1}/dataPipelines" -f $FabricConfig.BaseUrl, $WorkspaceId + $apiEndpointURI = ("workspaces/{0}/dataPipelines" -f $WorkspaceId) Write-Message -Message "API Endpoint: $apiEndpointURI" -Level Debug # Step 3: Construct the request body @@ -59,24 +61,30 @@ function New-FabricDataPipeline { displayName = $DataPipelineName } - if ($DataPipelineDescription) { + if ($DataPipelineDescription) + { $body.description = $DataPipelineDescription } $bodyJson = $body | ConvertTo-Json -Depth 10 + Write-Message -Message "Request Body: $bodyJson" -Level Debug - # Step 4: Make the API request - $response = Invoke-FabricAPIRequest ` - -BaseURI $apiEndpointURI ` - -Headers $FabricConfig.FabricHeaders ` - -Method Post ` - -Body $bodyJson - - Write-Message -Message "Data Pipeline created successfully!" -Level Info + if ($PSCmdlet.ShouldProcess($apiEndpointURI, "Create DataPipeline")) + { + # Step 4: Make the API request + $apiParams = @{ + Uri = $apiEndpointURI + method = 'Post' + body = $bodyJson + } + $response = Invoke-FabricAPIRequest @apiParams + } + Write-Message -Message "Data Pipeline created successfully!" -Level Info return $response } - catch { + catch + { # Step 6: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to create DataPipeline. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Data Pipeline/Remove-FabricDataPipeline.ps1 b/source/Public/Data Pipeline/Remove-FabricDataPipeline.ps1 similarity index 81% rename from FabricTools/public/Data Pipeline/Remove-FabricDataPipeline.ps1 rename to source/Public/Data Pipeline/Remove-FabricDataPipeline.ps1 index f0ef3f1d..c1b17244 100644 --- a/FabricTools/public/Data Pipeline/Remove-FabricDataPipeline.ps1 +++ b/source/Public/Data Pipeline/Remove-FabricDataPipeline.ps1 @@ -3,7 +3,7 @@ Removes a DataPipeline from a specified Microsoft Fabric workspace. .DESCRIPTION - This function sends a DELETE request to the Microsoft Fabric API to remove a DataPipeline + This function sends a DELETE request to the Microsoft Fabric API to remove a DataPipeline from the specified workspace using the provided WorkspaceId and DataPipelineId. .PARAMETER WorkspaceId @@ -23,8 +23,9 @@ Author: Tiago Balabuch #> -function Remove-FabricDataPipeline { - [CmdletBinding()] +function Remove-FabricDataPipeline +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -34,7 +35,8 @@ function Remove-FabricDataPipeline { [ValidateNotNullOrEmpty()] [string]$DataPipelineId ) - try { + try + { # Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -44,15 +46,20 @@ function Remove-FabricDataPipeline { $apiEndpointURI = "{0}/workspaces/{1}/dataPipelines/{2}" -f $FabricConfig.BaseUrl, $WorkspaceId, $DataPipelineId Write-Message -Message "API Endpoint: $apiEndpointURI" -Level Debug - # Make the API request - $response = Invoke-FabricAPIRequest ` - -Headers $FabricConfig.FabricHeaders ` - -BaseURI $apiEndpointURI ` - -Method Delete + if ($PSCmdlet.ShouldProcess($apiEndpointURI, "Delete DataPipeline")) + { + + # Make the API request + $response = Invoke-FabricAPIRequest ` + -Headers $FabricConfig.FabricHeaders ` + -BaseURI $apiEndpointURI ` + -method Delete + } Write-Message -Message "DataPipeline '$DataPipelineId' deleted successfully from workspace '$WorkspaceId'." -Level Info return $response } - catch { + catch + { # Log and handle errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to delete DataPipeline '$DataPipelineId' from workspace '$WorkspaceId'. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Data Pipeline/Update-FabricDataPipeline.ps1 b/source/Public/Data Pipeline/Update-FabricDataPipeline.ps1 similarity index 82% rename from FabricTools/public/Data Pipeline/Update-FabricDataPipeline.ps1 rename to source/Public/Data Pipeline/Update-FabricDataPipeline.ps1 index 557fcf6a..c286653f 100644 --- a/FabricTools/public/Data Pipeline/Update-FabricDataPipeline.ps1 +++ b/source/Public/Data Pipeline/Update-FabricDataPipeline.ps1 @@ -3,7 +3,7 @@ Updates an existing DataPipeline in a specified Microsoft Fabric workspace. .DESCRIPTION - This function sends a PATCH request to the Microsoft Fabric API to update an existing DataPipeline + This function sends a PATCH request to the Microsoft Fabric API to update an existing DataPipeline in the specified workspace. It supports optional parameters for DataPipeline description. .PARAMETER WorkspaceId @@ -28,13 +28,14 @@ Author: Tiago Balabuch #> -function Update-FabricDataPipeline { - [CmdletBinding()] +function Update-FabricDataPipeline +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [string]$WorkspaceId, - + [string]$WorkspaceId, + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$DataPipelineId, @@ -49,7 +50,8 @@ function Update-FabricDataPipeline { [string]$DataPipelineDescription ) - try { + try + { # Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -64,7 +66,8 @@ function Update-FabricDataPipeline { displayName = $DataPipelineName } - if ($DataPipelineDescription) { + if ($DataPipelineDescription) + { $body.description = $DataPipelineDescription } @@ -72,17 +75,22 @@ function Update-FabricDataPipeline { $bodyJson = $body | ConvertTo-Json Write-Message -Message "Request Body: $bodyJson" -Level Debug - # Make the API request - $response = Invoke-FabricAPIRequest ` - -Headers $FabricConfig.FabricHeaders ` - -BaseURI $apiEndpointURI ` - -Method Patch ` - -Body $bodyJson - + if ($PSCmdlet.ShouldProcess($apiEndpointURI, "Update DataPipeline")) + { + + # Make the API request + $response = Invoke-FabricAPIRequest ` + -Headers $FabricConfig.FabricHeaders ` + -BaseURI $apiEndpointURI ` + -method Patch ` + -body $bodyJson + } + Write-Message -Message "DataPipeline '$DataPipelineName' updated successfully!" -Level Info return $response } - catch { + catch + { # Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to update DataPipeline. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Datamart/Get-FabricDatamart.ps1 b/source/Public/Datamart/Get-FabricDatamart.ps1 similarity index 80% rename from FabricTools/public/Datamart/Get-FabricDatamart.ps1 rename to source/Public/Datamart/Get-FabricDatamart.ps1 index 55de12a3..4dde6c90 100644 --- a/FabricTools/public/Datamart/Get-FabricDatamart.ps1 +++ b/source/Public/Datamart/Get-FabricDatamart.ps1 @@ -1,4 +1,5 @@ -<# +function Get-FabricDatamart { + <# .SYNOPSIS Retrieves datamarts from a specified workspace. @@ -9,8 +10,14 @@ .PARAMETER WorkspaceId The ID of the workspace from which to retrieve datamarts. This parameter is mandatory. +.PARAMETER datamartId + The ID of the specific datamart to retrieve. This parameter is optional. + +.PARAMETER datamartName + The name of the specific datamart to retrieve. This parameter is optional. + .EXAMPLE - Get-FabricDatamart -WorkspaceId "12345" + Get-FabricDatamart -WorkspaceId "12345" This example retrieves all datamarts from the workspace with ID "12345". .NOTES @@ -18,8 +25,7 @@ - Calls `Test-TokenExpired` to ensure token validity before making the API request. Author: Tiago Balabuch -#> -function Get-FabricDatamart { + #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] @@ -41,40 +47,38 @@ function Get-FabricDatamart { Test-TokenExpired Write-Message -Message "Token validation completed." -Level Debug # Step 3: Initialize variables - - $apiEndpointURI = "{0}/workspaces/{1}/Datamarts" -f $FabricConfig.BaseUrl, $WorkspaceId - $Datamarts = = Invoke-FabricAPIRequest ` - -BaseURI $apiEndpointURI ` - -Headers $FabricConfig.FabricHeaders ` - -Method Get + $apiEndpointURI = "workspaces/{0}/Datamarts" -f $WorkspaceId + + $apiParams = @{ + Uri = $apiEndpointURI + method = 'Get' + } + $Datamarts = Invoke-FabricAPIRequest @apiParams + # Step 9: Filter results based on provided parameters - + $response = if ($datamartId) { $Datamarts | Where-Object { $_.Id -eq $datamartId } - } - elseif ($datamartName) { + } elseif ($datamartName) { $Datamarts | Where-Object { $_.DisplayName -eq $datamartName } - } - else { + } else { # No filter, return all datamarts Write-Message -Message "No filter specified. Returning all datamarts." -Level Debug return $Datamarts } - + # Step 10: Handle results if ($response) { Write-Message -Message "Datamart found matching the specified criteria." -Level Debug return $response - } - else { + } else { Write-Message -Message "No Datamart found matching the specified criteria." -Level Warning return $null } - } - catch { + } catch { # Step 10: Capture and log error details $errorDetails = $_.Exception.Message Write-Message -Message "Failed to retrieve Datamart. Error: $errorDetails" -Level Error - } -} \ No newline at end of file + } +} diff --git a/FabricTools/public/Domain/Assign-FabricDomainWorkspaceByCapacity.ps1 b/source/Public/Domain/Add-FabricDomainWorkspaceAssignmentByCapacity.ps1 similarity index 90% rename from FabricTools/public/Domain/Assign-FabricDomainWorkspaceByCapacity.ps1 rename to source/Public/Domain/Add-FabricDomainWorkspaceAssignmentByCapacity.ps1 index 5669c142..84bf2dff 100644 --- a/FabricTools/public/Domain/Assign-FabricDomainWorkspaceByCapacity.ps1 +++ b/source/Public/Domain/Add-FabricDomainWorkspaceAssignmentByCapacity.ps1 @@ -3,7 +3,7 @@ Assigns workspaces to a Fabric domain based on specified capacities. .DESCRIPTION -The `Assign-FabricDomainWorkspaceByCapacity` function assigns workspaces to a Fabric domain using a list of capacity IDs by making a POST request to the relevant API endpoint. +The `Add-FabricDomainWorkspaceAssignmentByCapacity` function assigns workspaces to a Fabric domain using a list of capacity IDs by making a POST request to the relevant API endpoint. .PARAMETER DomainId The unique identifier of the Fabric domain to which the workspaces will be assigned. @@ -12,7 +12,7 @@ The unique identifier of the Fabric domain to which the workspaces will be assig An array of capacity IDs used to assign workspaces to the domain. .EXAMPLE -Assign-FabricDomainWorkspaceByCapacity -DomainId "12345" -CapacitiesIds @("capacity1", "capacity2") +Add-FabricDomainWorkspaceAssignmentByCapacity -DomainId "12345" -CapacitiesIds @("capacity1", "capacity2") Assigns workspaces to the domain with ID "12345" based on the specified capacities. @@ -20,11 +20,12 @@ Assigns workspaces to the domain with ID "12345" based on the specified capaciti - Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. - Calls `Test-TokenExpired` to ensure token validity before making the API request. -Author: Tiago Balabuch +Author: Tiago Balabuch #> -function Assign-FabricDomainWorkspaceByCapacity { +function Add-FabricDomainWorkspaceAssignmentByCapacity { [CmdletBinding()] + [Alias("Assign-FabricDomainWorkspaceByCapacity")] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -44,12 +45,12 @@ function Assign-FabricDomainWorkspaceByCapacity { # Step 2: Construct the API URL $apiEndpointUrl = "{0}/admin/domains/{1}/assignWorkspacesByCapacities" -f $FabricConfig.BaseUrl, $DomainId Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug - + # Step 3: Construct the request body $body = @{ capacitiesIds = $CapacitiesIds } - + # Convert the body to JSON $bodyJson = $body | ConvertTo-Json -Depth 2 Write-Message -Message "Request Body: $bodyJson" -Level Debug @@ -78,25 +79,24 @@ function Assign-FabricDomainWorkspaceByCapacity { [string]$operationId = $responseHeader["x-ms-operation-id"] [string]$location = $responseHeader["Location"] - [string]$retryAfter = $responseHeader["Retry-After"] + [string]$retryAfter = $responseHeader["Retry-After"] Write-Message -Message "Operation ID: '$operationId'" -Level Debug Write-Message -Message "Location: '$location'" -Level Debug Write-Message -Message "Retry-After: '$retryAfter'" -Level Debug Write-Message -Message "Getting Long Running Operation status" -Level Debug - + $operationStatus = Get-FabricLongRunningOperation -operationId $operationId Write-Message -Message "Long Running Operation status: $operationStatus" -Level Debug # Handle operation result if ($operationStatus.status -eq "Succeeded") { Write-Message -Message "Operation Succeeded" -Level Debug return $operationStatus - } - else { + } else { Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Debug Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Error return $operationStatus - } + } } default { Write-Message -Message "Unexpected response code: $statusCode" -Level Error @@ -104,8 +104,7 @@ function Assign-FabricDomainWorkspaceByCapacity { throw "API request failed with status code $statusCode." } } - } - catch { + } catch { # Step 6: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Error occurred while assigning workspaces by capacity for domain '$DomainId'. Details: $errorDetails" -Level Error diff --git a/FabricTools/public/Domain/Assign-FabricDomainWorkspaceById.ps1 b/source/Public/Domain/Add-FabricDomainWorkspaceAssignmentById.ps1 similarity index 87% rename from FabricTools/public/Domain/Assign-FabricDomainWorkspaceById.ps1 rename to source/Public/Domain/Add-FabricDomainWorkspaceAssignmentById.ps1 index 7c20edb6..118a3f19 100644 --- a/FabricTools/public/Domain/Assign-FabricDomainWorkspaceById.ps1 +++ b/source/Public/Domain/Add-FabricDomainWorkspaceAssignmentById.ps1 @@ -3,7 +3,7 @@ Assigns workspaces to a specified domain in Microsoft Fabric by their IDs. .DESCRIPTION -The `Assign-FabricDomainWorkspaceById` function sends a request to assign multiple workspaces to a specified domain using the provided domain ID and an array of workspace IDs. +The `Add-FabricDomainWorkspaceAssignmentById` function sends a request to assign multiple workspaces to a specified domain using the provided domain ID and an array of workspace IDs. .PARAMETER DomainId The ID of the domain to which workspaces will be assigned. This parameter is mandatory. @@ -12,7 +12,7 @@ The ID of the domain to which workspaces will be assigned. This parameter is man An array of workspace IDs to be assigned to the domain. This parameter is mandatory. .EXAMPLE -Assign-FabricDomainWorkspaceById -DomainId "12345" -WorkspaceIds @("ws1", "ws2", "ws3") +Add-FabricDomainWorkspaceAssignmentById -DomainId "12345" -WorkspaceIds @("ws1", "ws2", "ws3") Assigns the workspaces with IDs "ws1", "ws2", and "ws3" to the domain with ID "12345". @@ -23,8 +23,9 @@ Assigns the workspaces with IDs "ws1", "ws2", and "ws3" to the domain with ID "1 Author: Tiago Balabuch #> -function Assign-FabricDomainWorkspaceById { +function Add-FabricDomainWorkspaceAssignmentById { [CmdletBinding()] + [Alias("Assign-FabricDomainWorkspaceById")] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -44,12 +45,12 @@ function Assign-FabricDomainWorkspaceById { # Step 2: Construct the API URL $apiEndpointUrl = "{0}/admin/domains/{1}/assignWorkspaces" -f $FabricConfig.BaseUrl, $DomainId Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug - + # Step 3: Construct the request body $body = @{ workspacesIds = $WorkspaceIds } - + # Convert the body to JSON $bodyJson = $body | ConvertTo-Json -Depth 2 Write-Message -Message "Request Body: $bodyJson" -Level Debug @@ -74,8 +75,7 @@ function Assign-FabricDomainWorkspaceById { return $null } Write-Message -Message "Successfully assigned workspaces to the domain with ID '$DomainId'." -Level Info - } - catch { + } catch { # Step 6: Capture and log error details $errorDetails = $_.Exception.Message Write-Message -Message "Failed to assign workspaces to the domain with ID '$DomainId'. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Domain/Assign-FabricDomainWorkspaceByPrincipal.ps1 b/source/Public/Domain/Add-FabricDomainWorkspaceAssignmentByPrincipal.ps1 similarity index 91% rename from FabricTools/public/Domain/Assign-FabricDomainWorkspaceByPrincipal.ps1 rename to source/Public/Domain/Add-FabricDomainWorkspaceAssignmentByPrincipal.ps1 index 52f868e8..58a40535 100644 --- a/FabricTools/public/Domain/Assign-FabricDomainWorkspaceByPrincipal.ps1 +++ b/source/Public/Domain/Add-FabricDomainWorkspaceAssignmentByPrincipal.ps1 @@ -3,7 +3,7 @@ Assigns workspaces to a domain based on principal IDs in Microsoft Fabric. .DESCRIPTION -The `Assign-FabricDomainWorkspaceByPrincipal` function sends a request to assign workspaces to a specified domain using a JSON object of principal IDs and types. +The `Add-FabricDomainWorkspaceAssignmentByPrincipal` function sends a request to assign workspaces to a specified domain using a JSON object of principal IDs and types. .PARAMETER DomainId The ID of the domain to which workspaces will be assigned. This parameter is mandatory. @@ -12,12 +12,12 @@ The ID of the domain to which workspaces will be assigned. This parameter is man An array representing the principals with their `id` and `type` properties. Must contain a `principals` key with an array of objects. .EXAMPLE -$PrincipalIds = @( +$PrincipalIds = @( @{id = "813abb4a-414c-4ac0-9c2c-bd17036fd58c"; type = "User"}, @{id = "b5b9495c-685a-447a-b4d3-2d8e963e6288"; type = "User"} ) -Assign-FabricDomainWorkspaceByPrincipal -DomainId "12345" -PrincipalIds $principals +Add-FabricDomainWorkspaceAssignmentByPrincipal -DomainId "12345" -PrincipalIds $principals Assigns the workspaces based on the provided principal IDs and types. @@ -28,8 +28,9 @@ Assigns the workspaces based on the provided principal IDs and types. Author: Tiago Balabuch #> -function Assign-FabricDomainWorkspaceByPrincipal { +function Add-FabricDomainWorkspaceAssignmentByPrincipal { [CmdletBinding()] + [Alias("Assign-FabricDomainWorkspaceByPrincipal")] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -90,15 +91,14 @@ function Assign-FabricDomainWorkspaceByPrincipal { [string]$operationId = $responseHeader["x-ms-operation-id"] Write-Message -Message "Operation ID: '$operationId'" -Level Debug Write-Message -Message "Getting Long Running Operation status" -Level Debug - + $operationStatus = Get-FabricLongRunningOperation -operationId $operationId Write-Message -Message "Long Running Operation status: $operationStatus" -Level Debug # Handle operation result if ($operationStatus.status -eq "Succeeded") { Write-Message -Message "Operation Succeeded" -Level Debug return $operationStatus - } - else { + } else { Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Debug Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Error return operationStatus @@ -110,8 +110,7 @@ function Assign-FabricDomainWorkspaceByPrincipal { throw "API request failed with status code $statusCode." } } - } - catch { + } catch { # Step 7: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to assign domain workspaces by principals. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Domain/Assign-FabricDomainWorkspaceRoleAssignment.ps1 b/source/Public/Domain/Add-FabricDomainWorkspaceRoleAssignment.ps1 similarity index 96% rename from FabricTools/public/Domain/Assign-FabricDomainWorkspaceRoleAssignment.ps1 rename to source/Public/Domain/Add-FabricDomainWorkspaceRoleAssignment.ps1 index da306f7d..d39a642a 100644 --- a/FabricTools/public/Domain/Assign-FabricDomainWorkspaceRoleAssignment.ps1 +++ b/source/Public/Domain/Add-FabricDomainWorkspaceRoleAssignment.ps1 @@ -27,11 +27,12 @@ Assigns the `Admins` role to the specified principals in the domain with ID "123 - Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. - Calls `Test-TokenExpired` to ensure token validity before making the API request. -Author: Tiago Balabuch +Author: Tiago Balabuch #> -function Assign-FabricDomainWorkspaceRoleAssignment { +function Add-FabricDomainWorkspaceRoleAssignment { [CmdletBinding()] + [Alias("Assign-FabricDomainWorkspaceRoleAssignment")] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -91,12 +92,11 @@ function Assign-FabricDomainWorkspaceRoleAssignment { Write-Message "Error Code: $($response.errorCode)" -Level Error return $null } - + Write-Message -Message "Bulk role assignment for domain '$DomainId' completed successfully!" -Level Info - } - catch { + } catch { # Step 7: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to bulk assign roles in domain '$DomainId'. Error: $errorDetails" -Level Error } -} \ No newline at end of file +} diff --git a/FabricTools/public/Domain/Get-FabricDomain.ps1 b/source/Public/Domain/Get-FabricDomain.ps1 similarity index 94% rename from FabricTools/public/Domain/Get-FabricDomain.ps1 rename to source/Public/Domain/Get-FabricDomain.ps1 index 31c8c1c7..a3122983 100644 --- a/FabricTools/public/Domain/Get-FabricDomain.ps1 +++ b/source/Public/Domain/Get-FabricDomain.ps1 @@ -28,11 +28,12 @@ Fetches the domain with the display name "Finance". - Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. - Calls `Test-TokenExpired` to ensure token validity before making the API request. -Author: Tiago Balabuch +Author: Tiago Balabuch #> function Get-FabricDomain { [CmdletBinding()] + [OutputType([System.Object[]])] param ( [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] @@ -59,7 +60,7 @@ function Get-FabricDomain { Test-TokenExpired Write-Message -Message "Token validation completed." -Level Debug - # Step 3: Construct the API URL with filtering logic + # Step 3: Construct the API URL with filtering logic $apiEndpointUrl = "{0}/admin/domains" -f $FabricConfig.BaseUrl if ($NonEmptyDomainsOnly) { $apiEndpointUrl = "{0}?nonEmptyOnly=true" -f $apiEndpointUrl @@ -83,7 +84,7 @@ function Get-FabricDomain { Write-Message "Error Code: $($response.errorCode)" -Level Error return $null } - + # Step 6: Handle empty response if (-not $response) { Write-Message -Message "No data returned from the API." -Level Warning @@ -94,11 +95,9 @@ function Get-FabricDomain { # Step 7: Filter results based on provided parameters $domains = if ($DomainId) { $response.domains | Where-Object { $_.Id -eq $DomainId } - } - elseif ($DomainName) { + } elseif ($DomainName) { $response.domains | Where-Object { $_.DisplayName -eq $DomainName } - } - else { + } else { # Return all domains if no filter is provided Write-Message -Message "No filter provided. Returning all domains." -Level Debug return $response.domains @@ -107,13 +106,11 @@ function Get-FabricDomain { # Step 8: Handle results if ($domains) { return $domains - } - else { + } else { Write-Message -Message "No domain found matching the provided criteria." -Level Warning return $null } - } - catch { + } catch { # Step 9: Capture and log error details $errorDetails = $_.Exception.Message Write-Message -Message "Failed to retrieve environment. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Domain/Get-FabricDomainWorkspace.ps1 b/source/Public/Domain/Get-FabricDomainWorkspace.ps1 similarity index 96% rename from FabricTools/public/Domain/Get-FabricDomainWorkspace.ps1 rename to source/Public/Domain/Get-FabricDomainWorkspace.ps1 index 4094ab30..337ac15e 100644 --- a/FabricTools/public/Domain/Get-FabricDomainWorkspace.ps1 +++ b/source/Public/Domain/Get-FabricDomainWorkspace.ps1 @@ -17,7 +17,7 @@ Fetches workspaces for the domain with ID "12345". - Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. - Calls `Test-TokenExpired` to ensure token validity before making the API request. -Author: Tiago Balabuch +Author: Tiago Balabuch #> @@ -56,7 +56,7 @@ function Get-FabricDomainWorkspace { Write-Message "Error Code: $($response.errorCode)" -Level Error return $null } - + # Step 5: Handle empty response if (-not $response) { Write-Message -Message "No data returned from the API." -Level Warning @@ -65,16 +65,14 @@ function Get-FabricDomainWorkspace { # Step 6: Handle results if ($response) { return $response.value - } - else { + } else { Write-Message -Message "No workspace found for the '$DomainId'." -Level Warning return $null } - } - catch { + } catch { # Step 7: Capture and log error details $errorDetails = Get-ErrorResponse($_.Exception) Write-Message -Message "Failed to retrieve domain workspaces. Error: $errorDetails" -Level Error } -} +} \ No newline at end of file diff --git a/FabricTools/public/Domain/New-FabricDomain.ps1 b/source/Public/Domain/New-FabricDomain.ps1 similarity index 79% rename from FabricTools/public/Domain/New-FabricDomain.ps1 rename to source/Public/Domain/New-FabricDomain.ps1 index e60c9048..f07affc8 100644 --- a/FabricTools/public/Domain/New-FabricDomain.ps1 +++ b/source/Public/Domain/New-FabricDomain.ps1 @@ -23,12 +23,13 @@ Creates a "Finance" domain under the parent domain with ID "12345". - Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. - Calls `Test-TokenExpired` to ensure token validity before making the API request. -Author: Tiago Balabuch +Author: Tiago Balabuch #> -function New-FabricDomain { - [CmdletBinding()] +function New-FabricDomain +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -44,7 +45,8 @@ function New-FabricDomain { [string]$ParentDomainId ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -59,11 +61,13 @@ function New-FabricDomain { displayName = $DomainName } - if ($DomainDescription) { + if ($DomainDescription) + { $body.description = $DomainDescription } - if ($ParentDomainId) { + if ($ParentDomainId) + { $body.parentDomainId = $ParentDomainId } @@ -71,56 +75,67 @@ function New-FabricDomain { $bodyJson = $body | ConvertTo-Json -Depth 2 Write-Message -Message "Request Body: $bodyJson" -Level Debug - # Step 4: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Post ` - -Body $bodyJson ` - -ContentType "application/json" ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -ResponseHeadersVariable "responseHeader" ` - -StatusCodeVariable "statusCode" - + if ($PSCmdlet.ShouldProcess($DomainName, "Create Domain")) + { + + # Step 4: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Post ` + -Body $bodyJson ` + -ContentType "application/json" ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -ResponseHeadersVariable "responseHeader" ` + -StatusCodeVariable "statusCode" + } + # Step 5: Handle and log the response - switch ($statusCode) { - 201 { + switch ($statusCode) + { + 201 + { Write-Message -Message "Domain '$DomainName' created successfully!" -Level Info return $response } - 202 { + 202 + { Write-Message -Message "Domain '$DomainName' creation accepted. Provisioning in progress!" -Level Info [string]$operationId = $responseHeader["x-ms-operation-id"] Write-Message -Message "Operation ID: '$operationId'" -Level Debug Write-Message -Message "Getting Long Running Operation status" -Level Debug - + $operationStatus = Get-FabricLongRunningOperation -operationId $operationId Write-Message -Message "Long Running Operation status: $operationStatus" -Level Debug # Handle operation result - if ($operationStatus.status -eq "Succeeded") { + if ($operationStatus.status -eq "Succeeded") + { Write-Message -Message "Operation Succeeded" -Level Debug Write-Message -Message "Getting Long Running Operation result" -Level Debug - + $operationResult = Get-FabricLongRunningOperationResult -operationId $operationId Write-Message -Message "Long Running Operation status: $operationResult" -Level Debug - + return $operationResult } - else { + else + { Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Debug Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Error return $operationStatus - } + } } - default { + default + { Write-Message -Message "Unexpected response code: $statusCode" -Level Error Write-Message -Message "Error details: $($response.message)" -Level Error throw "API request failed with status code $statusCode." } } } - catch { + catch + { # Step 6: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to create domain. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Domain/Remove-FabricDomain.ps1 b/source/Public/Domain/Remove-FabricDomain.ps1 similarity index 72% rename from FabricTools/public/Domain/Remove-FabricDomain.ps1 rename to source/Public/Domain/Remove-FabricDomain.ps1 index c7103158..27fcd905 100644 --- a/FabricTools/public/Domain/Remove-FabricDomain.ps1 +++ b/source/Public/Domain/Remove-FabricDomain.ps1 @@ -17,19 +17,21 @@ Deletes the domain with ID "12345". - Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. - Calls `Test-TokenExpired` to ensure token validity before making the API request. -Author: Tiago Balabuch +Author: Tiago Balabuch #> -function Remove-FabricDomain { - [CmdletBinding()] +function Remove-FabricDomain +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$DomainId ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -38,19 +40,22 @@ function Remove-FabricDomain { # Step 2: Construct the API URL $apiEndpointUrl = "{0}/admin/domains/{1}" -f $FabricConfig.BaseUrl, $DomainId Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug - - # Step 3: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Delete ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -ResponseHeadersVariable "responseHeader" ` - -StatusCodeVariable "statusCode" + if ($PSCmdlet.ShouldProcess($apiEndpointUrl, "Remove Domain")) + { + # Step 3: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Delete ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -ResponseHeadersVariable "responseHeader" ` + -StatusCodeVariable "statusCode" + } # Step 4: Validate the response code - if ($statusCode -ne 200) { + if ($statusCode -ne 200) + { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error Write-Message -Message "Error: $($response.message)" -Level Error Write-Message "Error Code: $($response.errorCode)" -Level Error @@ -58,9 +63,10 @@ function Remove-FabricDomain { } Write-Message -Message "Domain '$DomainId' deleted successfully!" -Level Info - + } - catch { + catch + { # Step 5: Log and handle errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to delete domain '$DomainId'. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Domain/Unassign-FabricDomainWorkspace.ps1 b/source/Public/Domain/Remove-FabricDomainWorkspaceAssignment.ps1 similarity index 65% rename from FabricTools/public/Domain/Unassign-FabricDomainWorkspace.ps1 rename to source/Public/Domain/Remove-FabricDomainWorkspaceAssignment.ps1 index d86afbdc..2dd1c856 100644 --- a/FabricTools/public/Domain/Unassign-FabricDomainWorkspace.ps1 +++ b/source/Public/Domain/Remove-FabricDomainWorkspaceAssignment.ps1 @@ -4,7 +4,7 @@ Unassign workspaces from a specified Fabric domain. .DESCRIPTION -The `Unassign -FabricDomainWorkspace` function allows you to Unassign specific workspaces from a given Fabric domain or unassign all workspaces if no workspace IDs are specified. +The `Unassign -FabricDomainWorkspace` function allows you to Unassign specific workspaces from a given Fabric domain or unassign all workspaces if no workspace IDs are specified. It makes a POST request to the relevant API endpoint for this operation. .PARAMETER DomainId @@ -14,12 +14,12 @@ The unique identifier of the Fabric domain. (Optional) An array of workspace IDs to unassign. If not provided, all workspaces will be unassigned. .EXAMPLE -Unassign-FabricDomainWorkspace -DomainId "12345" +Remove-FabricDomainWorkspaceAssignment -DomainId "12345" Unassigns all workspaces from the domain with ID "12345". .EXAMPLE -Unassign-FabricDomainWorkspace -DomainId "12345" -WorkspaceIds @("workspace1", "workspace2") +Remove-FabricDomainWorkspaceAssignment -DomainId "12345" -WorkspaceIds @("workspace1", "workspace2") Unassigns the specified workspaces from the domain with ID "12345". @@ -28,22 +28,25 @@ Unassigns the specified workspaces from the domain with ID "12345". - Calls `Test-TokenExpired` to ensure token validity before making the API request. -Author: Tiago Balabuch +Author: Tiago Balabuch #> -function Unassign-FabricDomainWorkspace { - [CmdletBinding()] +function Remove-FabricDomainWorkspaceAssignment +{ + [CmdletBinding(SupportsShouldProcess)] + [Alias("Unassign-FabricDomainWorkspace")] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$DomainId, - + [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [array]$WorkspaceIds ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -51,36 +54,49 @@ function Unassign-FabricDomainWorkspace { # Step 2: Construct the API URL # Determine the API endpoint URL based on the presence of WorkspaceIds - $endpointSuffix = if ($WorkspaceIds) { "unassignWorkspaces" } else { "unassignAllWorkspaces" } + $endpointSuffix = if ($WorkspaceIds) + { + "unassignWorkspaces" + } + else + { + "unassignAllWorkspaces" + } $apiEndpointUrl = "{0}/admin/domains/{1}/{2}" -f $FabricConfig.BaseUrl, $DomainId, $endpointSuffix Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug - + # Step 3: Construct the request body (if needed) - $bodyJson = if ($WorkspaceIds) { + $bodyJson = if ($WorkspaceIds) + { $body = @{ workspacesIds = $WorkspaceIds } $body | ConvertTo-Json -Depth 2 } - else { + else + { $null } - + Write-Message -Message "Request Body: $bodyJson" -Level Debug - # Step 4: Make the API request to unassign specific workspaces - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Post ` - -Body $bodyJson ` - -ContentType "application/json" ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -ResponseHeadersVariable "responseHeader" ` - -StatusCodeVariable "statusCode" + if ($PSCmdlet.ShouldProcess($DomainId, "Unassign Workspaces")) + { + # Step 4: Make the API request to unassign specific workspaces + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Post ` + -Body $bodyJson ` + -ContentType "application/json" ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -ResponseHeadersVariable "responseHeader" ` + -StatusCodeVariable "statusCode" + } # Step 5: Validate the response code - if ($statusCode -ne 200) { + if ($statusCode -ne 200) + { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error Write-Message -Message "Error: $($response.message)" -Level Error Write-Message "Error Code: $($response.errorCode)" -Level Error @@ -88,9 +104,10 @@ function Unassign-FabricDomainWorkspace { } Write-Message -Message "Successfully unassigned workspaces to the domain with ID '$DomainId'." -Level Info } - catch { + catch + { # Step 6: Capture and log error details $errorDetails = $_.Exception.Message Write-Message -Message "Failed to unassign workspaces to the domain with ID '$DomainId'. Error: $errorDetails" -Level Error } -} \ No newline at end of file +} diff --git a/FabricTools/public/Domain/Unassign-FabricDomainWorkspaceRoleAssignment.ps1 b/source/Public/Domain/Remove-FabricDomainWorkspaceRoleAssignment.ps1 similarity index 92% rename from FabricTools/public/Domain/Unassign-FabricDomainWorkspaceRoleAssignment.ps1 rename to source/Public/Domain/Remove-FabricDomainWorkspaceRoleAssignment.ps1 index c818fbb7..9d61e3c5 100644 --- a/FabricTools/public/Domain/Unassign-FabricDomainWorkspaceRoleAssignment.ps1 +++ b/source/Public/Domain/Remove-FabricDomainWorkspaceRoleAssignment.ps1 @@ -27,12 +27,13 @@ Unassign the `Admins` role to the specified principals in the domain with ID "12 - Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. - Calls `Test-TokenExpired` to ensure token validity before making the API request. -Author: Tiago Balabuch +Author: Tiago Balabuch #> -function Unassign-FabricDomainWorkspaceRoleAssignment { - [CmdletBinding()] +function Remove-FabricDomainWorkspaceRoleAssignment { + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] + [Alias("Unassign-FabricDomainWorkspaceRoleAssignment")] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -73,6 +74,7 @@ function Unassign-FabricDomainWorkspaceRoleAssignment { $bodyJson = $body | ConvertTo-Json -Depth 2 Write-Message -Message "Request Body: $bodyJson" -Level Debug + if($PSCmdlet.ShouldProcess($DomainId, "Unassign Roles")) { # Step 5: Make the API request $response = Invoke-RestMethod ` -Headers $FabricConfig.FabricHeaders ` @@ -84,6 +86,7 @@ function Unassign-FabricDomainWorkspaceRoleAssignment { -SkipHttpErrorCheck ` -ResponseHeadersVariable "responseHeader" ` -StatusCodeVariable "statusCode" + } # Step 6: Validate the response code if ($statusCode -ne 200) { @@ -93,11 +96,10 @@ function Unassign-FabricDomainWorkspaceRoleAssignment { return $null } Write-Message -Message "Bulk role unassignment for domain '$DomainId' completed successfully!" -Level Info - - } - catch { + + } catch { # Step 7: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to bulk assign roles in domain '$DomainId'. Error: $errorDetails" -Level Error } -} \ No newline at end of file +} diff --git a/FabricTools/public/Domain/Update-FabricDomain.ps1 b/source/Public/Domain/Update-FabricDomain.ps1 similarity index 77% rename from FabricTools/public/Domain/Update-FabricDomain.ps1 rename to source/Public/Domain/Update-FabricDomain.ps1 index 0ffad315..e3ed65d5 100644 --- a/FabricTools/public/Domain/Update-FabricDomain.ps1 +++ b/source/Public/Domain/Update-FabricDomain.ps1 @@ -3,7 +3,7 @@ Updates a Fabric domain by its ID. .DESCRIPTION -The `Update-FabricDomain` function modifies a specified domain in Microsoft Fabric using the provided parameters. +The `Update-FabricDomain` function modifies a specified domain in Microsoft Fabric using the provided parameters. .PARAMETER DomainId The unique identifier of the domain to be updated. @@ -26,12 +26,13 @@ Updates the domain with ID "12345" with a new name, description, and contributor - Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. - Calls `Test-TokenExpired` to ensure token validity before making the API request. -Author: Tiago Balabuch +Author: Tiago Balabuch #> -function Update-FabricDomain { - [CmdletBinding()] +function Update-FabricDomain +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -52,7 +53,8 @@ function Update-FabricDomain { [string]$DomainContributorsScope ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -67,11 +69,13 @@ function Update-FabricDomain { displayName = $DomainName } - if ($DomainDescription) { + if ($DomainDescription) + { $body.description = $DomainDescription } - if ($DomainContributorsScope) { + if ($DomainContributorsScope) + { $body.contributorsScope = $DomainContributorsScope } @@ -79,34 +83,39 @@ function Update-FabricDomain { $bodyJson = $body | ConvertTo-Json -Depth 10 Write-Message -Message "Request Body: $bodyJson" -Level Debug - # Step 4: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Patch ` - -Body $bodyJson ` - -ContentType "application/json" ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -ResponseHeadersVariable "responseHeader" ` - -StatusCodeVariable "statusCode" + if ($PSCmdlet.ShouldProcess($DomainName, "Update Domain")) + { + + # Step 4: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Patch ` + -Body $bodyJson ` + -ContentType "application/json" ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -ResponseHeadersVariable "responseHeader" ` + -StatusCodeVariable "statusCode" + } # Step 5: Validate the response code - if ($statusCode -ne 200) { + if ($statusCode -ne 200) + { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error Write-Message -Message "Error: $($response.message)" -Level Error Write-Message "Error Code: $($response.errorCode)" -Level Error return $null } - + # Step 6: Handle results Write-Message -Message "Domain '$DomainName' updated successfully!" -Level Info return $response } - catch { + catch + { # Step 7: Log and handle errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to update domain '$DomainId'. Error: $errorDetails" -Level Error } } - \ No newline at end of file diff --git a/FabricTools/public/Environment/Get-FabricEnvironment.ps1 b/source/Public/Environment/Get-FabricEnvironment.ps1 similarity index 95% rename from FabricTools/public/Environment/Get-FabricEnvironment.ps1 rename to source/Public/Environment/Get-FabricEnvironment.ps1 index 48e497c8..05c16161 100644 --- a/FabricTools/public/Environment/Get-FabricEnvironment.ps1 +++ b/source/Public/Environment/Get-FabricEnvironment.ps1 @@ -1,4 +1,5 @@ -<# +function Get-FabricEnvironment { + <# .SYNOPSIS Retrieves an environment or a list of environments from a specified workspace in Microsoft Fabric. @@ -8,6 +9,9 @@ The `Get-FabricEnvironment` function sends a GET request to the Fabric API to re .PARAMETER WorkspaceId (Mandatory) The ID of the workspace to query environments. +.PARAMETER EnvironmentId +(Optional) The ID of a specific environment to retrieve. + .PARAMETER EnvironmentName (Optional) The name of the specific environment to retrieve. @@ -26,11 +30,10 @@ Retrieves all environments in workspace "12345". - Calls `Test-TokenExpired` to ensure token validity before making the API request. - Returns the matching environment details or all environments if no filter is provided. -Author: Tiago Balabuch +Author: Tiago Balabuch -#> + #> -function Get-FabricEnvironment { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] @@ -68,14 +71,14 @@ function Get-FabricEnvironment { } $baseApiEndpointUrl = "{0}/workspaces/{1}/environments" -f $FabricConfig.BaseUrl, $WorkspaceId - + # Step 4: Loop to retrieve data with continuation token Write-Message -Message "Loop started to get continuation token" -Level Debug do { # Step 5: Construct the API URL $apiEndpointUrl = $baseApiEndpointUrl - + if ($null -ne $continuationToken) { # URL-encode the continuation token $encodedToken = [System.Web.HttpUtility]::UrlEncode($continuationToken) @@ -101,7 +104,7 @@ function Get-FabricEnvironment { Write-Message "Error Code: $($response.errorCode)" -Level Error return $null } - + # Step 8: Add data to the list if ($null -ne $response) { Write-Message -Message "Adding data to the list" -Level Debug @@ -112,27 +115,23 @@ function Get-FabricEnvironment { Write-Message -Message "Updating the continuation token" -Level Debug $continuationToken = $response.continuationToken Write-Message -Message "Continuation token: $continuationToken" -Level Debug - } - else { + } else { Write-Message -Message "Updating the continuation token to null" -Level Debug $continuationToken = $null } - } - else { + } else { Write-Message -Message "No data received from the API." -Level Warning break } } while ($null -ne $continuationToken) Write-Message -Message "Loop finished and all data added to the list" -Level Debug - + # Step 8: Filter results based on provided parameters $environment = if ($EnvironmentId) { $environments | Where-Object { $_.Id -eq $EnvironmentId } - } - elseif ($EnvironmentName) { + } elseif ($EnvironmentName) { $environments | Where-Object { $_.DisplayName -eq $EnvironmentName } - } - else { + } else { # Return all workspaces if no filter is provided Write-Message -Message "No filter provided. Returning all environments." -Level Debug $environments @@ -142,16 +141,14 @@ function Get-FabricEnvironment { if ($environment) { Write-Message -Message "Environment found in the Workspace '$WorkspaceId'." -Level Debug return $environment - } - else { + } else { Write-Message -Message "No environment found matching the provided criteria." -Level Warning return $null } - } - catch { + } catch { # Step 10: Capture and log error details $errorDetails = $_.Exception.Message Write-Message -Message "Failed to retrieve environment. Error: $errorDetails" -Level Error - } - -} + } + +} \ No newline at end of file diff --git a/FabricTools/public/Environment/Get-FabricEnvironmentLibrary.ps1 b/source/Public/Environment/Get-FabricEnvironmentLibrary.ps1 similarity index 94% rename from FabricTools/public/Environment/Get-FabricEnvironmentLibrary.ps1 rename to source/Public/Environment/Get-FabricEnvironmentLibrary.ps1 index 75a650ab..41f4330d 100644 --- a/FabricTools/public/Environment/Get-FabricEnvironmentLibrary.ps1 +++ b/source/Public/Environment/Get-FabricEnvironmentLibrary.ps1 @@ -3,8 +3,8 @@ Retrieves the list of libraries associated with a specific environment in a Microsoft Fabric workspace. .DESCRIPTION -The Get-FabricEnvironmentLibrary function fetches library information for a given workspace and environment -using the Microsoft Fabric API. It ensures the authentication token is valid and validates the response +The Get-FabricEnvironmentLibrary function fetches library information for a given workspace and environment +using the Microsoft Fabric API. It ensures the authentication token is valid and validates the response to handle errors gracefully. .PARAMETER WorkspaceId @@ -22,7 +22,7 @@ Retrieves the libraries associated with the specified environment in the given w - Requires the `$FabricConfig` global object, including `BaseUrl` and `FabricHeaders`. - Uses `Test-TokenExpired` to validate the token before making API calls. -Author: Tiago Balabuch +Author: Tiago Balabuch #> function Get-FabricEnvironmentLibrary { [CmdletBinding()] @@ -63,14 +63,13 @@ function Get-FabricEnvironmentLibrary { Write-Message "Error Code: $($response.errorCode)" -Level Error return $null } - + # Step 5: Handle results return $response - } - catch { + } catch { # Step 6: Capture and log error details $errorDetails = $_.Exception.Message Write-Message -Message "Failed to retrieve environment libraries. Error: $errorDetails" -Level Error - } - -} + } + +} \ No newline at end of file diff --git a/FabricTools/public/Environment/Get-FabricEnvironmentSparkCompute.ps1 b/source/Public/Environment/Get-FabricEnvironmentSparkCompute.ps1 similarity index 94% rename from FabricTools/public/Environment/Get-FabricEnvironmentSparkCompute.ps1 rename to source/Public/Environment/Get-FabricEnvironmentSparkCompute.ps1 index 65cf74de..cf60c915 100644 --- a/FabricTools/public/Environment/Get-FabricEnvironmentSparkCompute.ps1 +++ b/source/Public/Environment/Get-FabricEnvironmentSparkCompute.ps1 @@ -3,8 +3,8 @@ Retrieves the Spark compute details for a specific environment in a Microsoft Fabric workspace. .DESCRIPTION -The Get-FabricEnvironmentSparkCompute function communicates with the Microsoft Fabric API to fetch information -about Spark compute resources associated with a specified environment. It ensures that the API token is valid +The Get-FabricEnvironmentSparkCompute function communicates with the Microsoft Fabric API to fetch information +about Spark compute resources associated with a specified environment. It ensures that the API token is valid and gracefully handles errors during the API call. .PARAMETER WorkspaceId @@ -22,7 +22,7 @@ Retrieves Spark compute details for the specified environment in the given works - Requires the `$FabricConfig` global object, including `BaseUrl` and `FabricHeaders`. - Uses `Test-TokenExpired` to validate the token before making API calls. -Author: Tiago Balabuch +Author: Tiago Balabuch #> function Get-FabricEnvironmentSparkCompute { [CmdletBinding()] @@ -63,14 +63,13 @@ function Get-FabricEnvironmentSparkCompute { Write-Message "Error Code: $($response.errorCode)" -Level Error return $null } - + # Step 5: Handle results return $response - } - catch { + } catch { # Step 6: Capture and log error details $errorDetails = $_.Exception.Message Write-Message -Message "Failed to retrieve environment Spark compute. Error: $errorDetails" -Level Error - } - -} + } + +} \ No newline at end of file diff --git a/FabricTools/public/Environment/Get-FabricEnvironmentStagingLibrary.ps1 b/source/Public/Environment/Get-FabricEnvironmentStagingLibrary.ps1 similarity index 95% rename from FabricTools/public/Environment/Get-FabricEnvironmentStagingLibrary.ps1 rename to source/Public/Environment/Get-FabricEnvironmentStagingLibrary.ps1 index 0a8a5505..1e87be69 100644 --- a/FabricTools/public/Environment/Get-FabricEnvironmentStagingLibrary.ps1 +++ b/source/Public/Environment/Get-FabricEnvironmentStagingLibrary.ps1 @@ -3,7 +3,7 @@ Retrieves the staging library details for a specific environment in a Microsoft Fabric workspace. .DESCRIPTION -The Get-FabricEnvironmentStagingLibrary function interacts with the Microsoft Fabric API to fetch information +The Get-FabricEnvironmentStagingLibrary function interacts with the Microsoft Fabric API to fetch information about staging libraries associated with a specified environment. It ensures token validity and handles API errors gracefully. .PARAMETER WorkspaceId @@ -21,7 +21,7 @@ Retrieves the staging libraries for the specified environment in the given works - Requires the `$FabricConfig` global object, including `BaseUrl` and `FabricHeaders`. - Uses `Test-TokenExpired` to validate the token before making API calls. -Author: Tiago Balabuch +Author: Tiago Balabuch #> function Get-FabricEnvironmentStagingLibrary { [CmdletBinding()] @@ -55,7 +55,7 @@ function Get-FabricEnvironmentStagingLibrary { -SkipHttpErrorCheck ` -ResponseHeadersVariable "responseHeader" ` -StatusCodeVariable "statusCode" - + # Step 4: Validate the response code if ($statusCode -ne 200) { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error @@ -63,14 +63,13 @@ function Get-FabricEnvironmentStagingLibrary { Write-Message "Error Code: $($response.errorCode)" -Level Error return $null } - + # Step 5: Handle results return $response.customLibraries - } - catch { + } catch { # Step 6: Capture and log error details $errorDetails = $_.Exception.Message Write-Message -Message "Failed to retrieve environment spark compute. Error: $errorDetails" -Level Error - } - -} + } + +} \ No newline at end of file diff --git a/FabricTools/public/Environment/Get-FabricEnvironmentStagingSparkCompute.ps1 b/source/Public/Environment/Get-FabricEnvironmentStagingSparkCompute.ps1 similarity index 95% rename from FabricTools/public/Environment/Get-FabricEnvironmentStagingSparkCompute.ps1 rename to source/Public/Environment/Get-FabricEnvironmentStagingSparkCompute.ps1 index cbc94d55..49075388 100644 --- a/FabricTools/public/Environment/Get-FabricEnvironmentStagingSparkCompute.ps1 +++ b/source/Public/Environment/Get-FabricEnvironmentStagingSparkCompute.ps1 @@ -1,9 +1,10 @@ -<# +function Get-FabricEnvironmentStagingSparkCompute { + <# .SYNOPSIS Retrieves staging Spark compute details for a specific environment in a Microsoft Fabric workspace. .DESCRIPTION -The Get-FabricEnvironmentStagingSparkCompute function interacts with the Microsoft Fabric API to fetch information +The Get-FabricEnvironmentStagingSparkCompute function interacts with the Microsoft Fabric API to fetch information about staging Spark compute configurations for a specified environment. It ensures token validity and handles API errors gracefully. .PARAMETER WorkspaceId @@ -21,11 +22,8 @@ Retrieves the staging Spark compute configurations for the specified environment - Requires the `$FabricConfig` global object, including `BaseUrl` and `FabricHeaders`. - Uses `Test-TokenExpired` to validate the token before making API calls. -Author: Tiago Balabuch -#> - - -function Get-FabricEnvironmentStagingSparkCompute { +Author: Tiago Balabuch + #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] @@ -65,14 +63,13 @@ function Get-FabricEnvironmentStagingSparkCompute { Write-Message "Error Code: $($response.errorCode)" -Level Error return $null } - + # Step 5: Handle results return $response - } - catch { + } catch { # Step 6: Capture and log error details $errorDetails = $_.Exception.Message Write-Message -Message "Failed to retrieve environment spark compute. Error: $errorDetails" -Level Error - } - -} + } + +} \ No newline at end of file diff --git a/FabricTools/public/Environment/Upload-FabricEnvironmentStagingLibrary.ps1 b/source/Public/Environment/Import-FabricEnvironmentStagingLibrary.ps1 similarity index 91% rename from FabricTools/public/Environment/Upload-FabricEnvironmentStagingLibrary.ps1 rename to source/Public/Environment/Import-FabricEnvironmentStagingLibrary.ps1 index d8b09960..1c33e533 100644 --- a/FabricTools/public/Environment/Upload-FabricEnvironmentStagingLibrary.ps1 +++ b/source/Public/Environment/Import-FabricEnvironmentStagingLibrary.ps1 @@ -13,23 +13,24 @@ The unique identifier of the workspace where the environment exists. The unique identifier of the environment where the library will be uploaded. .EXAMPLE -Upload-FabricEnvironmentStagingLibrary -WorkspaceId "workspace-12345" -EnvironmentId "env-67890" +Import-FabricEnvironmentStagingLibrary -WorkspaceId "workspace-12345" -EnvironmentId "env-67890" .NOTES - This is not working code. It is a placeholder for future development. Fabric documentation is missing some important details on how to upload libraries. - Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. - Calls `Test-TokenExpired` to ensure token validity before making the API request. -Author: Tiago Balabuch +Author: Tiago Balabuch #> -function Upload-FabricEnvironmentStagingLibrary { +function Import-FabricEnvironmentStagingLibrary { [CmdletBinding()] + [Alias("Upload-FabricEnvironmentStagingLibrary")] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$WorkspaceId, - + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$EnvironmentId @@ -46,9 +47,9 @@ function Upload-FabricEnvironmentStagingLibrary { Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug # Step 3: Construct the request body - + # Step 4: Make the API request - $response = Invoke-RestMethod ` + $response = Invoke-RestMethod ` -Headers $FabricConfig.FabricHeaders ` -Uri $apiEndpointUrl ` -Method Post ` @@ -70,8 +71,7 @@ function Upload-FabricEnvironmentStagingLibrary { # Step 6: Handle results Write-Message -Message "Environment staging library uploaded successfully!" -Level Info return $response - } - catch { + } catch { # Step 7: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to upload environment staging library. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Environment/New-FabricEnvironment.ps1 b/source/Public/Environment/New-FabricEnvironment.ps1 similarity index 80% rename from FabricTools/public/Environment/New-FabricEnvironment.ps1 rename to source/Public/Environment/New-FabricEnvironment.ps1 index 009a5aeb..e91fdc22 100644 --- a/FabricTools/public/Environment/New-FabricEnvironment.ps1 +++ b/source/Public/Environment/New-FabricEnvironment.ps1 @@ -23,12 +23,13 @@ Creates an environment named "DevEnv" in workspace "12345" with the specified de - Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. - Calls `Test-TokenExpired` to ensure token validity before making the API request. -Author: Tiago Balabuch +Author: Tiago Balabuch #> -function New-FabricEnvironment { - [CmdletBinding()] +function New-FabricEnvironment +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -44,7 +45,8 @@ function New-FabricEnvironment { [string]$EnvironmentDescription ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -59,63 +61,73 @@ function New-FabricEnvironment { displayName = $EnvironmentName } - if ($EnvironmentDescription) { + if ($EnvironmentDescription) + { $body.description = $EnvironmentDescription } $bodyJson = $body | ConvertTo-Json -Depth 2 Write-Message -Message "Request Body: $bodyJson" -Level Debug - - # Step 4: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Post ` - -Body $bodyJson ` - -ContentType "application/json" ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -ResponseHeadersVariable "responseHeader" ` - -StatusCodeVariable "statusCode" + if ($PSCmdlet.ShouldProcess($EnvironmentName, "Create Environment")) + { + # Step 4: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Post ` + -Body $bodyJson ` + -ContentType "application/json" ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -ResponseHeadersVariable "responseHeader" ` + -StatusCodeVariable "statusCode" + } # Step 5: Handle and log the response - switch ($statusCode) { - 201 { + switch ($statusCode) + { + 201 + { Write-Message -Message "Environment '$EnvironmentName' created successfully!" -Level Info return $response } - 202 { + 202 + { Write-Message -Message "Environment '$EnvironmentName' creation accepted. Provisioning in progress!" -Level Info [string]$operationId = $responseHeader["x-ms-operation-id"] Write-Message -Message "Operation ID: '$operationId'" -Level Debug Write-Message -Message "Getting Long Running Operation status" -Level Debug - + $operationStatus = Get-FabricLongRunningOperation -operationId $operationId Write-Message -Message "Long Running Operation status: $operationStatus" -Level Debug # Handle operation result - if ($operationStatus.status -eq "Succeeded") { + if ($operationStatus.status -eq "Succeeded") + { Write-Message -Message "Operation Succeeded" -Level Debug Write-Message -Message "Getting Long Running Operation result" -Level Debug - + $operationResult = Get-FabricLongRunningOperationResult -operationId $operationId Write-Message -Message "Long Running Operation status: $operationResult" -Level Debug - + return $operationResult } - else { + else + { Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Debug Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Error return $operationStatus - } + } } - default { + default + { Write-Message -Message "Unexpected response code: $statusCode" -Level Error Write-Message -Message "Error details: $($response.message)" -Level Error throw "API request failed with status code $statusCode." } } } - catch { + catch + { # Step 6: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to create environment. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Environment/Publish-FabricEnvironment.ps1 b/source/Public/Environment/Publish-FabricEnvironment.ps1 similarity index 95% rename from FabricTools/public/Environment/Publish-FabricEnvironment.ps1 rename to source/Public/Environment/Publish-FabricEnvironment.ps1 index bf9fffbc..03ed87fc 100644 --- a/FabricTools/public/Environment/Publish-FabricEnvironment.ps1 +++ b/source/Public/Environment/Publish-FabricEnvironment.ps1 @@ -3,7 +3,7 @@ Publishes a staging environment in a specified Microsoft Fabric workspace. .DESCRIPTION -This function interacts with the Microsoft Fabric API to initiate the publishing process for a staging environment. +This function interacts with the Microsoft Fabric API to initiate the publishing process for a staging environment. It validates the authentication token, constructs the API request, and handles both immediate and long-running operations. @@ -22,7 +22,7 @@ Initiates the publishing process for the specified staging environment. - Requires the `$FabricConfig` global object, including `BaseUrl` and `FabricHeaders`. - Uses `Test-TokenExpired` to validate the token before making API calls. -Author: Tiago Balabuch +Author: Tiago Balabuch #> function Publish-FabricEnvironment { @@ -70,24 +70,23 @@ function Publish-FabricEnvironment { [string]$operationId = $responseHeader["x-ms-operation-id"] Write-Message -Message "Operation ID: '$operationId'" -Level Debug Write-Message -Message "Getting Long Running Operation status" -Level Debug - + $operationStatus = Get-FabricLongRunningOperation -operationId $operationId Write-Message -Message "Long Running Operation status: $operationStatus" -Level Debug # Handle operation result if ($operationStatus.status -eq "Succeeded") { Write-Message -Message "Operation Succeeded" -Level Debug Write-Message -Message "Getting Long Running Operation result" -Level Debug - + $operationResult = Get-FabricLongRunningOperationResult -operationId $operationId Write-Message -Message "Long Running Operation status: $operationResult" -Level Debug - + return $operationResult - } - else { + } else { Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Debug Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Error return $operationStatus - } + } } default { Write-Message -Message "Unexpected response code: $statusCode" -Level Error @@ -95,10 +94,9 @@ function Publish-FabricEnvironment { throw "API request failed with status code $statusCode." } } - } - catch { + } catch { # Step 6: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to create environment. Error: $errorDetails" -Level Error } -} +} \ No newline at end of file diff --git a/FabricTools/public/Environment/Remove-FabricEnvironment.ps1 b/source/Public/Environment/Remove-FabricEnvironment.ps1 similarity index 75% rename from FabricTools/public/Environment/Remove-FabricEnvironment.ps1 rename to source/Public/Environment/Remove-FabricEnvironment.ps1 index 4e1f734e..01719b20 100644 --- a/FabricTools/public/Environment/Remove-FabricEnvironment.ps1 +++ b/source/Public/Environment/Remove-FabricEnvironment.ps1 @@ -20,12 +20,13 @@ Deletes the environment with ID "67890" from workspace "12345". - Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. - Validates token expiration before making the API request. -Author: Tiago Balabuch +Author: Tiago Balabuch #> -function Remove-FabricEnvironment { - [CmdletBinding()] +function Remove-FabricEnvironment +{ + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -36,7 +37,8 @@ function Remove-FabricEnvironment { [string]$EnvironmentId ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -45,28 +47,32 @@ function Remove-FabricEnvironment { # Step 2: Construct the API URL $apiEndpointUrl = "{0}/workspaces/{1}/environments/{2}" -f $FabricConfig.BaseUrl, $WorkspaceId, $EnvironmentId Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug - - # Step 3: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Delete ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -ResponseHeadersVariable "responseHeader" ` - -StatusCodeVariable "statusCode" + if ($PSCmdlet.ShouldProcess($apiEndpointUrl, "Remove Environment")) + { + # Step 3: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Delete ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -ResponseHeadersVariable "responseHeader" ` + -StatusCodeVariable "statusCode" + } # Step 4: Validate the response code - if ($statusCode -ne 200) { + if ($statusCode -ne 200) + { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error Write-Message -Message "Error: $($response.message)" -Level Error Write-Message "Error Code: $($response.errorCode)" -Level Error return $null } Write-Message -Message "Environment '$EnvironmentId' deleted successfully from workspace '$WorkspaceId'." -Level Info - + } - catch { + catch + { # Step 5: Log and handle errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to delete environment '$EnvironmentId' from workspace '$WorkspaceId'. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Environment/Remove-FabricEnvironmentStagingLibrary.ps1 b/source/Public/Environment/Remove-FabricEnvironmentStagingLibrary.ps1 similarity index 77% rename from FabricTools/public/Environment/Remove-FabricEnvironmentStagingLibrary.ps1 rename to source/Public/Environment/Remove-FabricEnvironmentStagingLibrary.ps1 index ebfd16f5..d814aff9 100644 --- a/FabricTools/public/Environment/Remove-FabricEnvironmentStagingLibrary.ps1 +++ b/source/Public/Environment/Remove-FabricEnvironmentStagingLibrary.ps1 @@ -1,10 +1,11 @@ - -<# +function Remove-FabricEnvironmentStagingLibrary +{ + <# .SYNOPSIS Deletes a specified library from the staging environment in a Microsoft Fabric workspace. .DESCRIPTION -This function allows for the deletion of a library from the staging environment, one file at a time. +This function allows for the deletion of a library from the staging environment, one file at a time. It ensures token validity, constructs the appropriate API request, and handles both success and failure responses. .PARAMETER WorkspaceId @@ -25,13 +26,10 @@ Deletes the specified library from the staging environment in the specified work - Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. - Validates token expiration before making the API request. - This function currently supports deleting one library at a time. -Author: Tiago Balabuch - -#> - +Author: Tiago Balabuch -function Remove-FabricEnvironmentStagingLibrary { - [CmdletBinding()] + #> + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -40,36 +38,40 @@ function Remove-FabricEnvironmentStagingLibrary { [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$EnvironmentId, - + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$LibraryName ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired Write-Message -Message "Token validation completed." -Level Debug - + Write-Message -Message "Token validation completed." -Level Debug # Step 2: Construct the API URL $apiEndpointUrl = "{0}/workspaces/{1}/environments/{2}/staging/libraries?libraryToDelete={3}" -f $FabricConfig.BaseUrl, $WorkspaceId, $EnvironmentId, $LibraryName Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug + if ($PSCmdlet.ShouldProcess($apiEndpointUrl, "Remove Staging Library")) + { + # Step 3: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Delete ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -ResponseHeadersVariable "responseHeader" ` + -StatusCodeVariable "statusCode" + } - # Step 3: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Delete ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -ResponseHeadersVariable "responseHeader" ` - -StatusCodeVariable "statusCode" - - # Step 4: Validate the response code - if ($statusCode -ne 200) { + # Step 4: Validate the response code + if ($statusCode -ne 200) + { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error Write-Message -Message "Error: $($response.message)" -Level Error Write-Message "Error Code: $($response.errorCode)" -Level Error @@ -77,7 +79,8 @@ function Remove-FabricEnvironmentStagingLibrary { } Write-Message -Message "Staging library $LibraryName for the Environment '$EnvironmentId' deleted successfully from workspace '$WorkspaceId'." -Level Info } - catch { + catch + { # Step 5: Log and handle errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to delete environment '$EnvironmentId' from workspace '$WorkspaceId'. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Environment/Stop-FabricEnvironmentPublish.ps1 b/source/Public/Environment/Stop-FabricEnvironmentPublish.ps1 similarity index 75% rename from FabricTools/public/Environment/Stop-FabricEnvironmentPublish.ps1 rename to source/Public/Environment/Stop-FabricEnvironmentPublish.ps1 index 9276ea0a..eb198683 100644 --- a/FabricTools/public/Environment/Stop-FabricEnvironmentPublish.ps1 +++ b/source/Public/Environment/Stop-FabricEnvironmentPublish.ps1 @@ -21,11 +21,12 @@ Cancels the publish operation for the specified environment. - Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. - Validates token expiration before making the API request. -Author: Tiago Balabuch +Author: Tiago Balabuch #> -function Stop-FabricEnvironmentPublish{ - [CmdletBinding()] +function Stop-FabricEnvironmentPublish +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -36,7 +37,8 @@ function Stop-FabricEnvironmentPublish{ [string]$EnvironmentId ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -45,30 +47,34 @@ function Stop-FabricEnvironmentPublish{ # Step 2: Construct the API URL $apiEndpointUrl = "{0}/workspaces/{1}/environments/{2}/staging/cancelPublish" -f $FabricConfig.BaseUrl, $WorkspaceId, $EnvironmentId Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug - - # Step 3: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Post ` - -Body $bodyJson ` - -ContentType "application/json" ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -ResponseHeadersVariable "responseHeader" ` - -StatusCodeVariable "statusCode" + if ($PSCmdlet.ShouldProcess($apiEndpointUrl, "Cancel Publish")) + { + # Step 3: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Post ` + -Body $bodyJson ` + -ContentType "application/json" ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -ResponseHeadersVariable "responseHeader" ` + -StatusCodeVariable "statusCode" + } # Step 4: Validate the response code - if ($statusCode -ne 200) { + if ($statusCode -ne 200) + { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error Write-Message -Message "Error: $($response.message)" -Level Error Write-Message "Error Code: $($response.errorCode)" -Level Error return $null } Write-Message -Message "Publication for environment '$EnvironmentId' has been successfully canceled." -Level Info - + } - catch { + catch + { # Step 5: Log and handle errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to cancel publication for environment '$EnvironmentId' from workspace '$WorkspaceId'. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Environment/Update-FabricEnvironment.ps1 b/source/Public/Environment/Update-FabricEnvironment.ps1 similarity index 76% rename from FabricTools/public/Environment/Update-FabricEnvironment.ps1 rename to source/Public/Environment/Update-FabricEnvironment.ps1 index 2c9364b0..a69f8962 100644 --- a/FabricTools/public/Environment/Update-FabricEnvironment.ps1 +++ b/source/Public/Environment/Update-FabricEnvironment.ps1 @@ -14,6 +14,9 @@ The new name for the Environment. .PARAMETER EnvironmentDescription (Optional) The new description for the Environment. +.PARAMETER WorkspaceId +The unique identifier of the workspace where the Environment resides. + .EXAMPLE Update-FabricEnvironment -EnvironmentId "Environment123" -EnvironmentName "NewEnvironmentName" @@ -28,17 +31,18 @@ Updates both the name and description of the Environment "Environment123". - Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. - Calls `Test-TokenExpired` to ensure token validity before making the API request. -Author: Tiago Balabuch +Author: Tiago Balabuch #> -function Update-FabricEnvironment { - [CmdletBinding()] +function Update-FabricEnvironment +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [string]$WorkspaceId, - + [string]$WorkspaceId, + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$EnvironmentId, @@ -53,7 +57,8 @@ function Update-FabricEnvironment { [string]$EnvironmentDescription ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -68,7 +73,8 @@ function Update-FabricEnvironment { displayName = $EnvironmentName } - if ($EnvironmentDescription) { + if ($EnvironmentDescription) + { $body.description = $EnvironmentDescription } @@ -76,20 +82,24 @@ function Update-FabricEnvironment { $bodyJson = $body | ConvertTo-Json Write-Message -Message "Request Body: $bodyJson" -Level Debug - # Step 4: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Patch ` - -Body $bodyJson ` - -ContentType "application/json" ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -ResponseHeadersVariable "responseHeader" ` - -StatusCodeVariable "statusCode" + if ($PSCmdlet.ShouldProcess($EnvironmentId, "Update Environment")) + { + # Step 4: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Patch ` + -Body $bodyJson ` + -ContentType "application/json" ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -ResponseHeadersVariable "responseHeader" ` + -StatusCodeVariable "statusCode" + } # Step 5: Validate the response code - if ($statusCode -ne 200) { + if ($statusCode -ne 200) + { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error Write-Message -Message "Error: $($response.message)" -Level Error Write-Message "Error Code: $($response.errorCode)" -Level Error @@ -100,7 +110,8 @@ function Update-FabricEnvironment { Write-Message -Message "Environment '$EnvironmentName' updated successfully!" -Level Info return $response } - catch { + catch + { # Step 7: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to update Environment. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Environment/Update-FabricEnvironmentStagingSparkCompute.ps1 b/source/Public/Environment/Update-FabricEnvironmentStagingSparkCompute.ps1 similarity index 88% rename from FabricTools/public/Environment/Update-FabricEnvironmentStagingSparkCompute.ps1 rename to source/Public/Environment/Update-FabricEnvironmentStagingSparkCompute.ps1 index bd542623..f2e0cc02 100644 --- a/FabricTools/public/Environment/Update-FabricEnvironmentStagingSparkCompute.ps1 +++ b/source/Public/Environment/Update-FabricEnvironmentStagingSparkCompute.ps1 @@ -52,16 +52,17 @@ Update-FabricEnvironmentStagingSparkCompute -WorkspaceId "workspace-12345" -Envi - Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. - Calls `Test-TokenExpired` to ensure token validity before making the API request. -Author: Tiago Balabuch +Author: Tiago Balabuch #> -function Update-FabricEnvironmentStagingSparkCompute { - [CmdletBinding()] +function Update-FabricEnvironmentStagingSparkCompute +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$WorkspaceId, - + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$EnvironmentId, @@ -112,7 +113,8 @@ function Update-FabricEnvironmentStagingSparkCompute { [System.Object]$SparkProperties ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -145,6 +147,20 @@ function Update-FabricEnvironmentStagingSparkCompute { $bodyJson = $body | ConvertTo-Json -Depth 4 Write-Message -Message "Request Body: $bodyJson" -Level Debug + if ($PSCmdlet.ShouldProcess($EnvironmentId, "Update Environment Staging Spark Compute")) + { + # Step 4: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Patch ` + -Body $bodyJson ` + -ContentType "application/json" ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -ResponseHeadersVariable "responseHeader" ` + -StatusCodeVariable "statusCode" + } # Step 4: Make the API request $response = Invoke-RestMethod ` -Headers $FabricConfig.FabricHeaders ` @@ -156,9 +172,10 @@ function Update-FabricEnvironmentStagingSparkCompute { -SkipHttpErrorCheck ` -ResponseHeadersVariable "responseHeader" ` -StatusCodeVariable "statusCode" - - # Step 5: Validate the response code - if ($statusCode -ne 200) { + + # Step 5: Validate the response code + if ($statusCode -ne 200) + { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error Write-Message -Message "Error: $($response.message)" -Level Error Write-Message "Error Code: $($response.errorCode)" -Level Error @@ -169,7 +186,8 @@ function Update-FabricEnvironmentStagingSparkCompute { Write-Message -Message "Environment staging Spark compute updated successfully!" -Level Info return $response } - catch { + catch + { # Step 7: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to update environment staging Spark compute. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Eventhouse/Get-FabricEventhouse.ps1 b/source/Public/Eventhouse/Get-FabricEventhouse.ps1 similarity index 64% rename from FabricTools/public/Eventhouse/Get-FabricEventhouse.ps1 rename to source/Public/Eventhouse/Get-FabricEventhouse.ps1 index dabafa46..e1a501f6 100644 --- a/FabricTools/public/Eventhouse/Get-FabricEventhouse.ps1 +++ b/source/Public/Eventhouse/Get-FabricEventhouse.ps1 @@ -1,36 +1,66 @@ -<# -.SYNOPSIS - Retrieves Eventhouse details from a specified Microsoft Fabric workspace. +function Get-FabricEventhouse { + <# + .SYNOPSIS + Retrieves Fabric Eventhouses -.DESCRIPTION - This function retrieves Eventhouse details from a specified workspace using either the provided EventhouseId or EventhouseName. - It handles token validation, constructs the API URL, makes the API request, and processes the response. + .DESCRIPTION + Retrieves Fabric Eventhouses. Without the EventhouseName or EventhouseID parameter, all Eventhouses are returned. + If you want to retrieve a specific Eventhouse, you can use the EventhouseName or EventhouseID parameter. These + parameters cannot be used together. -.PARAMETER WorkspaceId - The unique identifier of the workspace where the Eventhouse exists. This parameter is mandatory. + .PARAMETER WorkspaceId + Id of the Fabric Workspace for which the Eventhouses should be retrieved. The value for WorkspaceId is a GUID. + An example of a GUID is '12345678-1234-1234-1234-123456789012'. -.PARAMETER EventhouseId - The unique identifier of the Eventhouse to retrieve. This parameter is optional. + .PARAMETER EventhouseName + The name of the Eventhouse to retrieve. This parameter cannot be used together with EventhouseID. -.PARAMETER EventhouseName - The name of the Eventhouse to retrieve. This parameter is optional. + .PARAMETER EventhouseId + The Id of the Eventhouse to retrieve. This parameter cannot be used together with EventhouseName. The value for WorkspaceId is a GUID. + An example of a GUID is '12345678-1234-1234-1234-123456789012'. -.EXAMPLE - Get-FabricEventhouse -WorkspaceId "workspace-12345" -EventhouseId "eventhouse-67890" - This example retrieves the Eventhouse details for the Eventhouse with ID "eventhouse-67890" in the workspace with ID "workspace-12345". + .EXAMPLE + Get-FabricEventhouse ` + -WorkspaceId '12345678-1234-1234-1234-123456789012' -.EXAMPLE - Get-FabricEventhouse -WorkspaceId "workspace-12345" -EventhouseName "My Eventhouse" - This example retrieves the Eventhouse details for the Eventhouse named "My Eventhouse" in the workspace with ID "workspace-12345". + This example will give you all Eventhouses in the Workspace. -.NOTES - - Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. - - Calls `Test-TokenExpired` to ensure token validity before making the API request. + .EXAMPLE + Get-FabricEventhouse ` + -WorkspaceId '12345678-1234-1234-1234-123456789012' ` + -EventhouseName 'MyEventhouse' - Author: Tiago Balabuch - -#> -function Get-FabricEventhouse { + This example will give you all Information about the Eventhouse with the name 'MyEventhouse'. + + .EXAMPLE + Get-FabricEventhouse ` + -WorkspaceId '12345678-1234-1234-1234-123456789012' ` + -EventhouseId '12345678-1234-1234-1234-123456789012' + + This example will give you all Information about the Eventhouse with the Id '12345678-1234-1234-1234-123456789012'. + + .EXAMPLE + Get-FabricEventhouse ` + -WorkspaceId '12345678-1234-1234-1234-123456789012' ` + -EventhouseId '12345678-1234-1234-1234-123456789012' ` + -Verbose + + This example will give you all Information about the Eventhouse with the Id '12345678-1234-1234-1234-123456789012'. + It will also give you verbose output which is useful for debugging. + + .LINK + https://learn.microsoft.com/en-us/rest/api/fabric/eventhouse/items/list-eventhouses?tabs=HTTP + + .NOTES + TODO: Add functionality to list all Eventhouses in the subscription. To do so fetch all workspaces + and then all eventhouses in each workspace. + + Revsion History: + + - 2024-11-09 - FGE: Added DisplaName as Alias for EventhouseName + - 2024-11-16 - FGE: Added Verbose Output + - 2024-11-27 - FGE: Added more Verbose Output + #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] @@ -58,15 +88,15 @@ function Get-FabricEventhouse { Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired Write-Message -Message "Token validation completed." -Level Debug - + # Step 3: Initialize variables $continuationToken = $null $eventhouses = @() - + if (-not ([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.GetName().Name -eq "System.Web" })) { Add-Type -AssemblyName System.Web } - + # Step 4: Loop to retrieve all capacities with continuation token Write-Message -Message "Loop started to get continuation token" -Level Debug $baseApiEndpointUrl = "{0}/workspaces/{1}/eventhouses" -f $FabricConfig.BaseUrl, $WorkspaceId @@ -81,7 +111,7 @@ function Get-FabricEventhouse { $apiEndpointUrl = "{0}?continuationToken={1}" -f $apiEndpointUrl, $encodedToken } Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug - + # Step 6: Make the API request $response = Invoke-RestMethod ` -Headers $FabricConfig.FabricHeaders ` @@ -91,7 +121,7 @@ function Get-FabricEventhouse { -SkipHttpErrorCheck ` -ResponseHeadersVariable "responseHeader" ` -StatusCodeVariable "statusCode" - + # Step 7: Validate the response code if ($statusCode -ne 200) { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error @@ -100,38 +130,34 @@ function Get-FabricEventhouse { Write-Message "Error Code: $($response.errorCode)" -Level Error return $null } - + # Step 8: Add data to the list if ($null -ne $response) { Write-Message -Message "Adding data to the list" -Level Debug $eventhouses += $response.value - + # Update the continuation token if present if ($response.PSObject.Properties.Match("continuationToken")) { Write-Message -Message "Updating the continuation token" -Level Debug $continuationToken = $response.continuationToken Write-Message -Message "Continuation token: $continuationToken" -Level Debug - } - else { + } else { Write-Message -Message "Updating the continuation token to null" -Level Debug $continuationToken = $null } - } - else { + } else { Write-Message -Message "No data received from the API." -Level Warning break } } while ($null -ne $continuationToken) Write-Message -Message "Loop finished and all data added to the list" -Level Debug - + # Step 8: Filter results based on provided parameters $eventhouse = if ($EventhouseId) { $eventhouses | Where-Object { $_.Id -eq $EventhouseId } - } - elseif ($EventhouseName) { + } elseif ($EventhouseName) { $eventhouses | Where-Object { $_.DisplayName -eq $EventhouseName } - } - else { + } else { # Return all eventhouses if no filter is provided Write-Message -Message "No filter provided. Returning all Eventhouses." -Level Debug $eventhouses @@ -141,16 +167,14 @@ function Get-FabricEventhouse { if ($eventhouse) { Write-Message -Message "Eventhouse found in the Workspace '$WorkspaceId'." -Level Debug return $eventhouse - } - else { + } else { Write-Message -Message "No Eventhouse found matching the provided criteria." -Level Warning return $null } - } - catch { + } catch { # Step 10: Capture and log error details $errorDetails = $_.Exception.Message Write-Message -Message "Failed to retrieve Eventhouse. Error: $errorDetails" -Level Error - } - -} + } + +} \ No newline at end of file diff --git a/FabricTools/public/Eventhouse/Get-FabricEventhouseDefinition.ps1 b/source/Public/Eventhouse/Get-FabricEventhouseDefinition.ps1 similarity index 96% rename from FabricTools/public/Eventhouse/Get-FabricEventhouseDefinition.ps1 rename to source/Public/Eventhouse/Get-FabricEventhouseDefinition.ps1 index e42a8a17..1f8bc72d 100644 --- a/FabricTools/public/Eventhouse/Get-FabricEventhouseDefinition.ps1 +++ b/source/Public/Eventhouse/Get-FabricEventhouseDefinition.ps1 @@ -28,7 +28,7 @@ - Calls `Test-TokenExpired` to ensure token validity before making the API request. Author: Tiago Balabuch - + #> function Get-FabricEventhouseDefinition { [CmdletBinding()] @@ -53,11 +53,11 @@ function Get-FabricEventhouseDefinition { # Step 3: Construct the API URL $apiEndpointUrl = "{0}/workspaces/{1}/eventhouses/{2}/getDefinition" -f $FabricConfig.BaseUrl, $WorkspaceId, $EventhouseId - + if ($EventhouseFormat) { $apiEndpointUrl = "{0}?format={1}" -f $apiEndpointUrl, $EventhouseFormat } - + Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug # Step 4: Make the API request @@ -81,30 +81,29 @@ function Get-FabricEventhouseDefinition { [string]$operationId = $responseHeader["x-ms-operation-id"] [string]$location = $responseHeader["Location"] - [string]$retryAfter = $responseHeader["Retry-After"] + [string]$retryAfter = $responseHeader["Retry-After"] Write-Message -Message "Operation ID: '$operationId'" -Level Debug Write-Message -Message "Location: '$location'" -Level Debug Write-Message -Message "Retry-After: '$retryAfter'" -Level Debug Write-Message -Message "Getting Long Running Operation status" -Level Debug - + $operationStatus = Get-FabricLongRunningOperation -operationId $operationId -location $location Write-Message -Message "Long Running Operation status: $operationStatus" -Level Debug # Handle operation result if ($operationStatus.status -eq "Succeeded") { Write-Message -Message "Operation Succeeded" -Level Debug Write-Message -Message "Getting Long Running Operation result" -Level Debug - - $operationResult = Get-FabricLongRunningOperationResult -operationId $operationId + + $operationResult = Get-FabricLongRunningOperationResult -operationId $operationId Write-Message -Message "Long Running Operation status: $operationResult" -Level Debug - + return $operationResult.definition.parts - } - else { + } else { Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Debug Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Error return $operationStatus - } + } } default { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error @@ -114,11 +113,10 @@ function Get-FabricEventhouseDefinition { throw "API request failed with status code $statusCode." } } - } - catch { + } catch { # Step 9: Capture and log error details $errorDetails = $_.Exception.Message Write-Message -Message "Failed to retrieve Eventhouse. Error: $errorDetails" -Level Error - } - -} + } + +} \ No newline at end of file diff --git a/FabricTools/public/Eventhouse/New-FabricEventhouse.ps1 b/source/Public/Eventhouse/New-FabricEventhouse.ps1 similarity index 67% rename from FabricTools/public/Eventhouse/New-FabricEventhouse.ps1 rename to source/Public/Eventhouse/New-FabricEventhouse.ps1 index de5979b9..d2db2d3c 100644 --- a/FabricTools/public/Eventhouse/New-FabricEventhouse.ps1 +++ b/source/Public/Eventhouse/New-FabricEventhouse.ps1 @@ -1,39 +1,41 @@ -<# -.SYNOPSIS - Creates a new Eventhouse in a specified Microsoft Fabric workspace. +function New-FabricEventhouse +{ + <# + .SYNOPSIS + Creates a new Eventhouse in a specified Microsoft Fabric workspace. -.DESCRIPTION - This function sends a POST request to the Microsoft Fabric API to create a new Eventhouse - in the specified workspace. It supports optional parameters for Eventhouse description and path definitions. + .DESCRIPTION + This function sends a POST request to the Microsoft Fabric API to create a new Eventhouse + in the specified workspace. It supports optional parameters for Eventhouse description and path definitions. -.PARAMETER WorkspaceId - The unique identifier of the workspace where the Eventhouse will be created. This parameter is mandatory. + .PARAMETER WorkspaceId + The unique identifier of the workspace where the Eventhouse will be created. This parameter is mandatory. -.PARAMETER EventhouseName - The name of the Eventhouse to be created. This parameter is mandatory. + .PARAMETER EventhouseName + The name of the Eventhouse to be created. This parameter is mandatory. -.PARAMETER EventhouseDescription - An optional description for the Eventhouse. + .PARAMETER EventhouseDescription + An optional description for the Eventhouse. -.PARAMETER EventhousePathDefinition - An optional path to the Eventhouse definition file to upload. + .PARAMETER EventhousePathDefinition + An optional path to the Eventhouse definition file to upload. -.PARAMETER EventhousePathPlatformDefinition - An optional path to the platform-specific definition file to upload. + .PARAMETER EventhousePathPlatformDefinition + An optional path to the platform-specific definition file to upload. -.EXAMPLE - New-FabricEventhouse -WorkspaceId "workspace-12345" -EventhouseName "New Eventhouse" -EventhouseDescription "Description of the new Eventhouse" - This example creates a new Eventhouse named "New Eventhouse" in the workspace with ID "workspace-12345" with the provided description. + .EXAMPLE + New-FabricEventhouse -WorkspaceId "workspace-12345" -EventhouseName "New Eventhouse" -EventhouseDescription "Description of the new Eventhouse" + This example creates a new Eventhouse named "New Eventhouse" in the workspace with ID "workspace-12345" with the provided description. -.NOTES - - Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. - - Calls `Test-TokenExpired` to ensure token validity before making the API request. + .NOTES + - Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. + - Calls `Test-TokenExpired` to ensure token validity before making the API request. - Author: Tiago Balabuch - -#> -function New-FabricEventhouse { - [CmdletBinding()] + Author: Tiago Balabuch + + #> + + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -51,12 +53,13 @@ function New-FabricEventhouse { [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$EventhousePathDefinition, - + [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$EventhousePathPlatformDefinition ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -71,17 +74,21 @@ function New-FabricEventhouse { displayName = $EventhouseName } - if ($EventhouseDescription) { + if ($EventhouseDescription) + { $body.description = $EventhouseDescription } - if ($EventhousePathDefinition) { + if ($EventhousePathDefinition) + { $eventhouseEncodedContent = Convert-ToBase64 -filePath $EventhousePathDefinition - if (-not [string]::IsNullOrEmpty($eventhouseEncodedContent)) { + if (-not [string]::IsNullOrEmpty($eventhouseEncodedContent)) + { # Initialize definition if it doesn't exist - if (-not $body.definition) { + if (-not $body.definition) + { $body.definition = @{ - parts = @() + parts = @() } } @@ -92,20 +99,24 @@ function New-FabricEventhouse { payloadType = "InlineBase64" } } - else { + else + { Write-Message -Message "Invalid or empty content in Eventhouse definition." -Level Error return $null } } - if ($EventhousePathPlatformDefinition) { + if ($EventhousePathPlatformDefinition) + { $eventhouseEncodedPlatformContent = Convert-ToBase64 -filePath $EventhousePathPlatformDefinition - if (-not [string]::IsNullOrEmpty($eventhouseEncodedPlatformContent)) { + if (-not [string]::IsNullOrEmpty($eventhouseEncodedPlatformContent)) + { # Initialize definition if it doesn't exist - if (-not $body.definition) { + if (-not $body.definition) + { $body.definition = @{ - parts = @() + parts = @() } } @@ -116,7 +127,8 @@ function New-FabricEventhouse { payloadType = "InlineBase64" } } - else { + else + { Write-Message -Message "Invalid or empty content in platform definition." -Level Error return $null } @@ -126,6 +138,20 @@ function New-FabricEventhouse { $bodyJson = $body | ConvertTo-Json -Depth 10 Write-Message -Message "Request Body: $bodyJson" -Level Debug + if ($PSCmdlet.ShouldProcess($EventhouseName, "Create Eventhouse")) + { + # Step 4: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Post ` + -Body $bodyJson ` + -ContentType "application/json" ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -ResponseHeadersVariable "responseHeader" ` + -StatusCodeVariable "statusCode" + } # Step 4: Make the API request $response = Invoke-RestMethod ` -Headers $FabricConfig.FabricHeaders ` @@ -137,46 +163,52 @@ function New-FabricEventhouse { -SkipHttpErrorCheck ` -ResponseHeadersVariable "responseHeader" ` -StatusCodeVariable "statusCode" - + Write-Message -Message "Response Code: $statusCode" -Level Debug - + # Step 5: Handle and log the response - switch ($statusCode) { - 201 { + switch ($statusCode) + { + 201 + { Write-Message -Message "Eventhouse '$EventhouseName' created successfully!" -Level Info return $response } - 202 { + 202 + { Write-Message -Message "Eventhouse '$EventhouseName' creation accepted. Provisioning in progress!" -Level Info - + [string]$operationId = $responseHeader["x-ms-operation-id"] [string]$location = $responseHeader["Location"] - [string]$retryAfter = $responseHeader["Retry-After"] + [string]$retryAfter = $responseHeader["Retry-After"] Write-Message -Message "Operation ID: '$operationId'" -Level Debug Write-Message -Message "Location: '$location'" -Level Debug Write-Message -Message "Retry-After: '$retryAfter'" -Level Debug Write-Message -Message "Getting Long Running Operation status" -Level Debug - + $operationStatus = Get-FabricLongRunningOperation -operationId $operationId -location $location Write-Message -Message "Long Running Operation status: $operationStatus" -Level Debug # Handle operation result - if ($operationStatus.status -eq "Succeeded") { + if ($operationStatus.status -eq "Succeeded") + { Write-Message -Message "Operation Succeeded" -Level Debug Write-Message -Message "Getting Long Running Operation result" -Level Debug - + $operationResult = Get-FabricLongRunningOperationResult -operationId $operationId Write-Message -Message "Long Running Operation status: $operationResult" -Level Debug - + return $operationResult - } - else { + } + else + { Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Debug Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Error return $operationStatus - } + } } - default { + default + { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error Write-Message -Message "Error: $($response.message)" -Level Error Write-Message -Message "Error Details: $($response.moreDetails)" -Level Error @@ -185,7 +217,8 @@ function New-FabricEventhouse { } } } - catch { + catch + { # Step 6: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to create Eventhouse. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Eventhouse/Remove-FabricEventhouse.ps1 b/source/Public/Eventhouse/Remove-FabricEventhouse.ps1 similarity index 64% rename from FabricTools/public/Eventhouse/Remove-FabricEventhouse.ps1 rename to source/Public/Eventhouse/Remove-FabricEventhouse.ps1 index 0bdfd275..2a2756e4 100644 --- a/FabricTools/public/Eventhouse/Remove-FabricEventhouse.ps1 +++ b/source/Public/Eventhouse/Remove-FabricEventhouse.ps1 @@ -1,9 +1,11 @@ -<# +function Remove-FabricEventhouse +{ + <# .SYNOPSIS Removes an Eventhouse from a specified Microsoft Fabric workspace. .DESCRIPTION - This function sends a DELETE request to the Microsoft Fabric API to remove an Eventhouse + This function sends a DELETE request to the Microsoft Fabric API to remove an Eventhouse from the specified workspace using the provided WorkspaceId and EventhouseId. .PARAMETER WorkspaceId @@ -12,19 +14,31 @@ .PARAMETER EventhouseId The unique identifier of the Eventhouse to be removed. +.PARAMETER EventhouseName + The name of the Eventhouse to delete. EventhouseId and EventhouseName cannot be used together. + .EXAMPLE Remove-FabricEventhouse -WorkspaceId "workspace-12345" -EventhouseId "eventhouse-67890" This example removes the Eventhouse with ID "eventhouse-67890" from the workspace with ID "workspace-12345". .NOTES + Revsion History: + + - 2024-11-07 - FGE: Implemented SupportShouldProcess + - 2024-11-09 - FGE: Added DisplaName as Alias for EventhouseName + - 2024-11-27 - FGE: Added Verbose Output + +.LINK + https://learn.microsoft.com/en-us/rest/api/fabric/eventhouse/items/delete-eventhouse?tabs=HTTP + - Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. - Calls `Test-TokenExpired` to ensure token validity before making the API request. Author: Tiago Balabuch - -#> -function Remove-FabricEventhouse { - [CmdletBinding()] + + #> + + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -34,7 +48,8 @@ function Remove-FabricEventhouse { [ValidateNotNullOrEmpty()] [string]$EventhouseId ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -43,19 +58,22 @@ function Remove-FabricEventhouse { # Step 2: Construct the API URL $apiEndpointUrl = "{0}/workspaces/{1}/eventhouses/{2}" -f $FabricConfig.BaseUrl, $WorkspaceId, $EventhouseId Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug + if ($PSCmdlet.ShouldProcess($apiEndpointUrl, "Remove Eventhouse")) + { + # Step 3: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Delete ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -ResponseHeadersVariable "responseHeader" ` + -StatusCodeVariable "statusCode" + } - # Step 3: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Delete ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -ResponseHeadersVariable "responseHeader" ` - -StatusCodeVariable "statusCode" - - # Step 4: Handle response - if ($statusCode -ne 200) { + # Step 4: Handle response + if ($statusCode -ne 200) + { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error Write-Message -Message "Error: $($response.message)" -Level Error Write-Message -Message "Error Details: $($response.moreDetails)" -Level Error @@ -63,9 +81,10 @@ function Remove-FabricEventhouse { return $null } - Write-Message -Message "Eventhouse '$EventhouseId' deleted successfully from workspace '$WorkspaceId'." -Level Info + Write-Message -Message "Eventhouse '$EventhouseId' deleted successfully from workspace '$WorkspaceId'." -Level Info } - catch { + catch + { # Step 5: Log and handle errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to delete Eventhouse '$EventhouseId'. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Eventhouse/Update-FabricEventhouse.ps1 b/source/Public/Eventhouse/Update-FabricEventhouse.ps1 similarity index 79% rename from FabricTools/public/Eventhouse/Update-FabricEventhouse.ps1 rename to source/Public/Eventhouse/Update-FabricEventhouse.ps1 index 6968c89c..fa23a2a1 100644 --- a/FabricTools/public/Eventhouse/Update-FabricEventhouse.ps1 +++ b/source/Public/Eventhouse/Update-FabricEventhouse.ps1 @@ -3,7 +3,7 @@ Updates an existing Eventhouse in a specified Microsoft Fabric workspace. .DESCRIPTION - This function sends a PATCH request to the Microsoft Fabric API to update an existing Eventhouse + This function sends a PATCH request to the Microsoft Fabric API to update an existing Eventhouse in the specified workspace. It supports optional parameters for Eventhouse description. .PARAMETER WorkspaceId @@ -27,15 +27,16 @@ - Calls `Test-TokenExpired` to ensure token validity before making the API request. Author: Tiago Balabuch - + #> -function Update-FabricEventhouse { - [CmdletBinding()] +function Update-FabricEventhouse +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [string]$WorkspaceId, - + [string]$WorkspaceId, + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$EventhouseId, @@ -49,7 +50,8 @@ function Update-FabricEventhouse { [ValidateNotNullOrEmpty()] [string]$EventhouseDescription ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -64,7 +66,8 @@ function Update-FabricEventhouse { displayName = $EventhouseName } - if ($EventhouseDescription) { + if ($EventhouseDescription) + { $body.description = $EventhouseDescription } @@ -72,20 +75,24 @@ function Update-FabricEventhouse { $bodyJson = $body | ConvertTo-Json Write-Message -Message "Request Body: $bodyJson" -Level Debug - # Step 4: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Patch ` - -Body $bodyJson ` - -ContentType "application/json" ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -ResponseHeadersVariable "responseHeader" ` - -StatusCodeVariable "statusCode" + if ($PSCmdlet.ShouldProcess("Eventhouse", "Update")) + { + # Step 4: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Patch ` + -Body $bodyJson ` + -ContentType "application/json" ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -ResponseHeadersVariable "responseHeader" ` + -StatusCodeVariable "statusCode" + } # Step 5: Validate the response code - if ($statusCode -ne 200) { + if ($statusCode -ne 200) + { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error Write-Message -Message "Error: $($response.message)" -Level Error Write-Message -Message "Error Details: $($response.moreDetails)" -Level Error @@ -97,7 +104,8 @@ function Update-FabricEventhouse { Write-Message -Message "Eventhouse '$EventhouseName' updated successfully!" -Level Info return $response } - catch { + catch + { # Step 7: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to update Eventhouse. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Eventhouse/Update-FabricEventhouseDefinition.ps1 b/source/Public/Eventhouse/Update-FabricEventhouseDefinition.ps1 similarity index 82% rename from FabricTools/public/Eventhouse/Update-FabricEventhouseDefinition.ps1 rename to source/Public/Eventhouse/Update-FabricEventhouseDefinition.ps1 index aa1bf713..d5486e14 100644 --- a/FabricTools/public/Eventhouse/Update-FabricEventhouseDefinition.ps1 +++ b/source/Public/Eventhouse/Update-FabricEventhouseDefinition.ps1 @@ -3,7 +3,7 @@ Updates the definition of an existing Eventhouse in a specified Microsoft Fabric workspace. .DESCRIPTION - This function sends a PATCH request to the Microsoft Fabric API to update the definition of an existing Eventhouse + This function sends a PATCH request to the Microsoft Fabric API to update the definition of an existing Eventhouse in the specified workspace. It supports optional parameters for Eventhouse definition and platform-specific definition. .PARAMETER WorkspaceId @@ -27,10 +27,11 @@ - Calls `Test-TokenExpired` to ensure token validity before making the API request. Author: Tiago Balabuch - + #> -function Update-FabricEventhouseDefinition { - [CmdletBinding()] +function Update-FabricEventhouseDefinition +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -43,12 +44,13 @@ function Update-FabricEventhouseDefinition { [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$EventhousePathDefinition, - + [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$EventhousePathPlatformDefinition ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -58,22 +60,25 @@ function Update-FabricEventhouseDefinition { $apiEndpointUrl = "{0}/workspaces/{1}/eventhouses/{2}/updateDefinition" -f $FabricConfig.BaseUrl, $WorkspaceId, $EventhouseId #if ($UpdateMetadata -eq $true) { - if($EventhousePathPlatformDefinition){ - $apiEndpointUrl = "?updateMetadata=true" -f $apiEndpointUrl + if ($EventhousePathPlatformDefinition) + { + $apiEndpointUrl = "?updateMetadata=true" -f $apiEndpointUrl } Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug # Step 3: Construct the request body $body = @{ definition = @{ - parts = @() - } + parts = @() + } } - - if ($EventhousePathDefinition) { + + if ($EventhousePathDefinition) + { $EventhouseEncodedContent = Convert-ToBase64 -filePath $EventhousePathDefinition - - if (-not [string]::IsNullOrEmpty($EventhouseEncodedContent)) { + + if (-not [string]::IsNullOrEmpty($EventhouseEncodedContent)) + { # Add new part to the parts array $body.definition.parts += @{ path = "EventhouseProperties.json" @@ -81,15 +86,18 @@ function Update-FabricEventhouseDefinition { payloadType = "InlineBase64" } } - else { + else + { Write-Message -Message "Invalid or empty content in Eventhouse definition." -Level Error return $null } } - if ($EventhousePathPlatformDefinition) { + if ($EventhousePathPlatformDefinition) + { $EventhouseEncodedPlatformContent = Convert-ToBase64 -filePath $EventhousePathPlatformDefinition - if (-not [string]::IsNullOrEmpty($EventhouseEncodedPlatformContent)) { + if (-not [string]::IsNullOrEmpty($EventhouseEncodedPlatformContent)) + { # Add new part to the parts array $body.definition.parts += @{ path = ".platform" @@ -97,7 +105,8 @@ function Update-FabricEventhouseDefinition { payloadType = "InlineBase64" } } - else { + else + { Write-Message -Message "Invalid or empty content in platform definition." -Level Error return $null } @@ -105,62 +114,71 @@ function Update-FabricEventhouseDefinition { $bodyJson = $body | ConvertTo-Json -Depth 10 Write-Message -Message "Request Body: $bodyJson" -Level Debug + if ($PSCmdlet.ShouldProcess("Eventhouse", "Update")) + { + # Step 4: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Post ` + -Body $bodyJson ` + -ContentType "application/json" ` + -ErrorAction Stop ` + -ResponseHeadersVariable "responseHeader" ` + -StatusCodeVariable "statusCode" + } - # Step 4: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Post ` - -Body $bodyJson ` - -ContentType "application/json" ` - -ErrorAction Stop ` - -ResponseHeadersVariable "responseHeader" ` - -StatusCodeVariable "statusCode" - # Step 5: Handle and log the response - switch ($statusCode) { - 200 { + switch ($statusCode) + { + 200 + { Write-Message -Message "Update definition for Eventhouse '$EventhouseId' created successfully!" -Level Info return $response } - 202 { + 202 + { Write-Message -Message "Update definition for Eventhouse '$EventhouseId' accepted. Operation in progress!" -Level Info - + [string]$operationId = $responseHeader["x-ms-operation-id"] [string]$location = $responseHeader["Location"] - [string]$retryAfter = $responseHeader["Retry-After"] + [string]$retryAfter = $responseHeader["Retry-After"] Write-Message -Message "Operation ID: '$operationId'" -Level Debug Write-Message -Message "Location: '$location'" -Level Debug Write-Message -Message "Retry-After: '$retryAfter'" -Level Debug Write-Message -Message "Getting Long Running Operation status" -Level Debug - + $operationStatus = Get-FabricLongRunningOperation -operationId $operationId -location $location Write-Message -Message "Long Running Operation status: $operationStatus" -Level Debug # Handle operation result - if ($operationStatus.status -eq "Succeeded") { + if ($operationStatus.status -eq "Succeeded") + { Write-Message -Message "Operation Succeeded" -Level Debug Write-Message -Message "Getting Long Running Operation result" -Level Debug - + $operationResult = Get-FabricLongRunningOperationResult -operationId $operationId Write-Message -Message "Long Running Operation status: $operationResult" -Level Debug - + return $operationResult - } - else { + } + else + { Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Debug Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Error return $operationStatus - } - } - default { + } + } + default + { Write-Message -Message "Unexpected response code: $statusCode" -Level Error Write-Message -Message "Error details: $($response.message)" -Level Error throw "API request failed with status code $statusCode." } } } - catch { + catch + { # Step 6: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to update Eventhouse. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Eventstream/Get-FabricEventstream.ps1 b/source/Public/Eventstream/Get-FabricEventstream.ps1 similarity index 88% rename from FabricTools/public/Eventstream/Get-FabricEventstream.ps1 rename to source/Public/Eventstream/Get-FabricEventstream.ps1 index 28f6448a..74319aed 100644 --- a/FabricTools/public/Eventstream/Get-FabricEventstream.ps1 +++ b/source/Public/Eventstream/Get-FabricEventstream.ps1 @@ -1,15 +1,21 @@ -<# +function Get-FabricEventstream { + <# .SYNOPSIS Retrieves an Eventstream or a list of Eventstreams from a specified workspace in Microsoft Fabric. .DESCRIPTION -The `Get-FabricEventstream` function sends a GET request to the Fabric API to retrieve Eventstream details for a given workspace. It can filter the results by `EventstreamName`. + Retrieves Fabric Eventstreams. Without the EventstreamName or EventstreamID parameter, all Eventstreams are returned. + If you want to retrieve a specific Eventstream, you can use the EventstreamName or EventstreamID parameter. These + parameters cannot be used together. .PARAMETER WorkspaceId (Mandatory) The ID of the workspace to query Eventstreams. .PARAMETER EventstreamName (Optional) The name of the specific Eventstream to retrieve. +.PARAMETER EventstreamId + The Id of the Eventstream to retrieve. This parameter cannot be used together with EventstreamName. The value for EventstreamId is a GUID. + An example of a GUID is '12345678-1234-1234-1234-123456789012'. .EXAMPLE Get-FabricEventstream -WorkspaceId "12345" -EventstreamName "Development" @@ -25,11 +31,11 @@ Retrieves all Eventstreams in workspace "12345". - Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. - Calls `Test-TokenExpired` to ensure token validity before making the API request. -Author: Tiago Balabuch +Author: Tiago Balabuch + + #> -#> -function Get-FabricEventstream { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] @@ -64,26 +70,26 @@ function Get-FabricEventstream { if (-not ([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.GetName().Name -eq "System.Web" })) { Add-Type -AssemblyName System.Web } - + # Step 4: Loop to retrieve all capacities with continuation token Write-Message -Message "Loop started to get continuation token" -Level Debug $baseApiEndpointUrl = "{0}/workspaces/{1}/eventstreams" -f $FabricConfig.BaseUrl, $WorkspaceId - + # Step 3: Loop to retrieve data with continuation token - - - + + + do { # Step 5: Construct the API URL $apiEndpointUrl = $baseApiEndpointUrl - + if ($null -ne $continuationToken) { # URL-encode the continuation token $encodedToken = [System.Web.HttpUtility]::UrlEncode($continuationToken) $apiEndpointUrl = "{0}?continuationToken={1}" -f $apiEndpointUrl, $encodedToken } Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug - + # Step 6: Make the API request $response = Invoke-RestMethod ` -Headers $FabricConfig.FabricHeaders ` @@ -93,7 +99,7 @@ function Get-FabricEventstream { -SkipHttpErrorCheck ` -ResponseHeadersVariable "responseHeader" ` -StatusCodeVariable "statusCode" - + # Step 7: Validate the response code if ($statusCode -ne 200) { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error @@ -102,39 +108,35 @@ function Get-FabricEventstream { Write-Message "Error Code: $($response.errorCode)" -Level Error return $null } - + # Step 8: Add data to the list if ($null -ne $response) { Write-Message -Message "Adding data to the list" -Level Debug $eventstreams += $response.value - + # Update the continuation token if present if ($response.PSObject.Properties.Match("continuationToken")) { Write-Message -Message "Updating the continuation token" -Level Debug $continuationToken = $response.continuationToken Write-Message -Message "Continuation token: $continuationToken" -Level Debug - } - else { + } else { Write-Message -Message "Updating the continuation token to null" -Level Debug $continuationToken = $null } - } - else { + } else { Write-Message -Message "No data received from the API." -Level Warning break } } while ($null -ne $continuationToken) Write-Message -Message "Loop finished and all data added to the list" -Level Debug - + # Step 8: Filter results based on provided parameters $eventstream = if ($EventstreamId) { $eventstreams | Where-Object { $_.Id -eq $EventstreamId } - } - elseif ($EventstreamName) { + } elseif ($EventstreamName) { $eventstreams | Where-Object { $_.DisplayName -eq $EventstreamName } - } - else { + } else { # Return all eventstreams if no filter is provided Write-Message -Message "No filter provided. Returning all Eventstreams." -Level Debug $eventstreams @@ -144,16 +146,14 @@ function Get-FabricEventstream { if ($eventstream) { Write-Message -Message "Eventstream found matching the specified criteria." -Level Debug return $eventstream - } - else { + } else { Write-Message -Message "No Eventstream found matching the provided criteria." -Level Warning return $null } - } - catch { + } catch { # Step 10: Capture and log error details $errorDetails = $_.Exception.Message Write-Message -Message "Failed to retrieve Eventstream. Error: $errorDetails" -Level Error - } - -} + } + +} \ No newline at end of file diff --git a/FabricTools/public/Eventstream/Get-FabricEventstreamDefinition.ps1 b/source/Public/Eventstream/Get-FabricEventstreamDefinition.ps1 similarity index 97% rename from FabricTools/public/Eventstream/Get-FabricEventstreamDefinition.ps1 rename to source/Public/Eventstream/Get-FabricEventstreamDefinition.ps1 index eb070a7b..411deb41 100644 --- a/FabricTools/public/Eventstream/Get-FabricEventstreamDefinition.ps1 +++ b/source/Public/Eventstream/Get-FabricEventstreamDefinition.ps1 @@ -4,7 +4,7 @@ Retrieves the definition of a Eventstream from a specific workspace in Microsoft Fabric. .DESCRIPTION -This function fetches the Eventstream's content or metadata from a workspace. +This function fetches the Eventstream's content or metadata from a workspace. Handles both synchronous and asynchronous operations, with detailed logging and error handling. .PARAMETER WorkspaceId @@ -85,37 +85,35 @@ function Get-FabricEventstreamDefinition { [string]$operationId = $responseHeader["x-ms-operation-id"] Write-Message -Message "Operation ID: '$operationId'" -Level Debug Write-Message -Message "Getting Long Running Operation status" -Level Debug - + $operationStatus = Get-FabricLongRunningOperation -operationId $operationId Write-Message -Message "Long Running Operation status: $operationStatus" -Level Debug # Handle operation result if ($operationStatus.status -eq "Succeeded") { Write-Message -Message "Operation Succeeded" -Level Debug Write-Message -Message "Getting Long Running Operation result" -Level Debug - + $operationResult = Get-FabricLongRunningOperationResult -operationId $operationId Write-Message -Message "Long Running Operation status: $operationResult" -Level Debug - + return $operationResult.definition.parts - } - else { + } else { Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Debug Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Error return $operationStatus - } + } } default { Write-Message -Message "Unexpected response code: $statusCode" -Level Error Write-Message -Message "Error details: $($response.message)" -Level Error throw "API request failed with status code $statusCode." } - + } - } - catch { + } catch { # Step 9: Capture and log error details $errorDetails = $_.Exception.Message Write-Message -Message "Failed to retrieve Eventstream. Error: $errorDetails" -Level Error - } - -} + } + +} \ No newline at end of file diff --git a/FabricTools/public/Eventstream/New-FabricEventstream.ps1 b/source/Public/Eventstream/New-FabricEventstream.ps1 similarity index 80% rename from FabricTools/public/Eventstream/New-FabricEventstream.ps1 rename to source/Public/Eventstream/New-FabricEventstream.ps1 index f5def5b5..16b7d189 100644 --- a/FabricTools/public/Eventstream/New-FabricEventstream.ps1 +++ b/source/Public/Eventstream/New-FabricEventstream.ps1 @@ -1,10 +1,12 @@ -<# +function New-FabricEventstream +{ + <# .SYNOPSIS Creates a new Eventstream in a specified Microsoft Fabric workspace. .DESCRIPTION -This function sends a POST request to the Microsoft Fabric API to create a new Eventstream -in the specified workspace. It supports optional parameters for Eventstream description +This function sends a POST request to the Microsoft Fabric API to create a new Eventstream +in the specified workspace. It supports optional parameters for Eventstream description and path definitions for the Eventstream content. .PARAMETER WorkspaceId @@ -23,18 +25,18 @@ An optional path to the Eventstream definition file (e.g., .ipynb file) to uploa An optional path to the platform-specific definition (e.g., .platform file) to upload. .EXAMPLE +#TODO Fix example name Add-FabricEventstream -WorkspaceId "workspace-12345" -EventstreamName "New Eventstream" -EventstreamPathDefinition "C:\Eventstreams\example.ipynb" .NOTES - Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. - Calls `Test-TokenExpired` to ensure token validity before making the API request. -Author: Tiago Balabuch +Author: Tiago Balabuch #> - -function New-FabricEventstream { - [CmdletBinding()] + #TODO SupportsShouldProcess + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -52,13 +54,14 @@ function New-FabricEventstream { [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$EventstreamPathDefinition, - + [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$EventstreamPathPlatformDefinition ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -73,16 +76,20 @@ function New-FabricEventstream { displayName = $EventstreamName } - if ($EventstreamDescription) { + if ($EventstreamDescription) + { $body.description = $EventstreamDescription } - if ($EventstreamPathDefinition) { + if ($EventstreamPathDefinition) + { $EventstreamEncodedContent = Convert-ToBase64 -filePath $EventstreamPathDefinition - if (-not [string]::IsNullOrEmpty($EventstreamEncodedContent)) { + if (-not [string]::IsNullOrEmpty($EventstreamEncodedContent)) + { # Initialize definition if it doesn't exist - if (-not $body.definition) { + if (-not $body.definition) + { $body.definition = @{ format = "eventstream" parts = @() @@ -96,18 +103,22 @@ function New-FabricEventstream { payloadType = "InlineBase64" } } - else { + else + { Write-Message -Message "Invalid or empty content in Eventstream definition." -Level Error return $null } } - if ($EventstreamPathPlatformDefinition) { + if ($EventstreamPathPlatformDefinition) + { $EventstreamEncodedPlatformContent = Convert-ToBase64 -filePath $EventstreamPathPlatformDefinition - if (-not [string]::IsNullOrEmpty($EventstreamEncodedPlatformContent)) { + if (-not [string]::IsNullOrEmpty($EventstreamEncodedPlatformContent)) + { # Initialize definition if it doesn't exist - if (-not $body.definition) { + if (-not $body.definition) + { $body.definition = @{ format = "eventstream" parts = @() @@ -121,7 +132,8 @@ function New-FabricEventstream { payloadType = "InlineBase64" } } - else { + else + { Write-Message -Message "Invalid or empty content in platform definition." -Level Error return $null } @@ -130,57 +142,67 @@ function New-FabricEventstream { $bodyJson = $body | ConvertTo-Json -Depth 10 Write-Message -Message "Request Body: $bodyJson" -Level Debug - # Step 4: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Post ` - -Body $bodyJson ` - -ContentType "application/json" ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -ResponseHeadersVariable "responseHeader" ` - -StatusCodeVariable "statusCode" + if ($PSCmdlet.ShouldProcess($EventstreamName, "Create Eventstream")) + { + # Step 4: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Post ` + -Body $bodyJson ` + -ContentType "application/json" ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -ResponseHeadersVariable "responseHeader" ` + -StatusCodeVariable "statusCode" + } # Step 5: Handle and log the response - switch ($statusCode) { - 201 { + switch ($statusCode) + { + 201 + { Write-Message -Message "Eventstream '$EventstreamName' created successfully!" -Level Info return $response } - 202 { + 202 + { Write-Message -Message "Eventstream '$EventstreamName' creation accepted. Provisioning in progress!" -Level Info - + [string]$operationId = $responseHeader["x-ms-operation-id"] Write-Message -Message "Operation ID: '$operationId'" -Level Debug Write-Message -Message "Getting Long Running Operation status" -Level Debug - + $operationStatus = Get-FabricLongRunningOperation -operationId $operationId Write-Message -Message "Long Running Operation status: $operationStatus" -Level Debug # Handle operation result - if ($operationStatus.status -eq "Succeeded") { + if ($operationStatus.status -eq "Succeeded") + { Write-Message -Message "Operation Succeeded" -Level Debug Write-Message -Message "Getting Long Running Operation result" -Level Debug - + $operationResult = Get-FabricLongRunningOperationResult -operationId $operationId Write-Message -Message "Long Running Operation status: $operationResult" -Level Debug - + return $operationResult } - else { + else + { Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Debug Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Error return $operationStatus - } + } } - default { + default + { Write-Message -Message "Unexpected response code: $statusCode" -Level Error Write-Message -Message "Error details: $($response.message)" -Level Error throw "API request failed with status code $statusCode." } } } - catch { + catch + { # Step 6: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to create Eventstream. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Eventstream/Remove-FabricEventstream.ps1 b/source/Public/Eventstream/Remove-FabricEventstream.ps1 similarity index 68% rename from FabricTools/public/Eventstream/Remove-FabricEventstream.ps1 rename to source/Public/Eventstream/Remove-FabricEventstream.ps1 index ef59265f..0b9da7e3 100644 --- a/FabricTools/public/Eventstream/Remove-FabricEventstream.ps1 +++ b/source/Public/Eventstream/Remove-FabricEventstream.ps1 @@ -1,4 +1,6 @@ -<# +function Remove-FabricEventstream +{ + <# .SYNOPSIS Deletes an Eventstream from a specified workspace in Microsoft Fabric. @@ -11,6 +13,11 @@ The `Remove-FabricEventstream` function sends a DELETE request to the Fabric API .PARAMETER EventstreamId (Mandatory) The ID of the Eventstream to be deleted. +.PARAMETER EventstreamName + The name of the Eventstream to delete. The value for Eventstream is a string. + An example of a string is 'MyEventstream'. + The name of the Eventstream to delete. The value for Eventstream is a string. + .EXAMPLE Remove-FabricEventstream -WorkspaceId "12345" -EventstreamId "67890" @@ -20,12 +27,10 @@ Deletes the Eventstream with ID "67890" from workspace "12345". - Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. - Validates token expiration before making the API request. -Author: Tiago Balabuch - -#> +Author: Tiago Balabuch -function Remove-FabricEventstream { - [CmdletBinding()] + #> + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -34,9 +39,11 @@ function Remove-FabricEventstream { [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$EventstreamId + #TODO Add EventstreamName parameter to validate the name of the Eventstream to delete. ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -46,26 +53,31 @@ function Remove-FabricEventstream { $apiEndpointUrl = "{0}/workspaces/{1}/eventstreams/{2}" -f $FabricConfig.BaseUrl, $WorkspaceId, $EventstreamId Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug - # Step 3: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Delete ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -StatusCodeVariable "statusCode" + if ($PSCmdlet.ShouldProcess($apiEndpointUrl, "Remove Eventstream")) + { + # Step 3: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Delete ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -StatusCodeVariable "statusCode" + } # Step 4: Validate the response code - if ($statusCode -ne 200) { + if ($statusCode -ne 200) + { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error Write-Message -Message "Error: $($response.message)" -Level Error Write-Message "Error Code: $($response.errorCode)" -Level Error return $null } Write-Message -Message "Eventstream '$EventstreamId' deleted successfully from workspace '$WorkspaceId'." -Level Info - + } - catch { + catch + { # Step 5: Log and handle errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to delete Eventstream '$EventstreamId' from workspace '$WorkspaceId'. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Eventstream/Update-FabricEventstream.ps1 b/source/Public/Eventstream/Update-FabricEventstream.ps1 similarity index 77% rename from FabricTools/public/Eventstream/Update-FabricEventstream.ps1 rename to source/Public/Eventstream/Update-FabricEventstream.ps1 index 28103bf3..9d2883d6 100644 --- a/FabricTools/public/Eventstream/Update-FabricEventstream.ps1 +++ b/source/Public/Eventstream/Update-FabricEventstream.ps1 @@ -14,6 +14,9 @@ The new name for the Eventstream. .PARAMETER EventstreamDescription (Optional) The new description for the Eventstream. +.PARAMETER WorkspaceId +The unique identifier of the workspace where the Eventstream resides. + .EXAMPLE Update-FabricEventstream -EventstreamId "Eventstream123" -EventstreamName "NewEventstreamName" @@ -28,17 +31,18 @@ Updates both the name and description of the Eventstream "Eventstream123". - Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. - Calls `Test-TokenExpired` to ensure token validity before making the API request. -Author: Tiago Balabuch +Author: Tiago Balabuch #> -function Update-FabricEventstream { - [CmdletBinding()] +function Update-FabricEventstream +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [string]$WorkspaceId, - + [string]$WorkspaceId, + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$EventstreamId, @@ -53,7 +57,8 @@ function Update-FabricEventstream { [string]$EventstreamDescription ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -68,27 +73,30 @@ function Update-FabricEventstream { displayName = $EventstreamName } - if ($EventstreamDescription) { + if ($EventstreamDescription) + { $body.description = $EventstreamDescription } # Convert the body to JSON $bodyJson = $body | ConvertTo-Json Write-Message -Message "Request Body: $bodyJson" -Level Debug - - # Step 4: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Patch ` - -Body $bodyJson ` - -ContentType "application/json" ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -StatusCodeVariable "statusCode" - + if ($PSCmdlet.ShouldProcess($EventstreamId, "Update Eventstream")) + { + # Step 4: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Patch ` + -Body $bodyJson ` + -ContentType "application/json" ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -StatusCodeVariable "statusCode" + } # Step 5: Validate the response code - if ($statusCode -ne 200) { + if ($statusCode -ne 200) + { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error Write-Message -Message "Error: $($response.message)" -Level Error Write-Message "Error Code: $($response.errorCode)" -Level Error @@ -99,7 +107,8 @@ function Update-FabricEventstream { Write-Message -Message "Eventstream '$EventstreamName' updated successfully!" -Level Info return $response } - catch { + catch + { # Step 7: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to update Eventstream. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Eventstream/Update-FabricEventstreamDefinition.ps1 b/source/Public/Eventstream/Update-FabricEventstreamDefinition.ps1 similarity index 82% rename from FabricTools/public/Eventstream/Update-FabricEventstreamDefinition.ps1 rename to source/Public/Eventstream/Update-FabricEventstreamDefinition.ps1 index 0929734e..9ae03b65 100644 --- a/FabricTools/public/Eventstream/Update-FabricEventstreamDefinition.ps1 +++ b/source/Public/Eventstream/Update-FabricEventstreamDefinition.ps1 @@ -3,7 +3,7 @@ Updates the definition of a Eventstream in a Microsoft Fabric workspace. .DESCRIPTION -This function allows updating the content or metadata of a Eventstream in a Microsoft Fabric workspace. +This function allows updating the content or metadata of a Eventstream in a Microsoft Fabric workspace. The Eventstream content can be provided as file paths, and metadata updates can optionally be enabled. .PARAMETER WorkspaceId @@ -19,7 +19,7 @@ The Eventstream content can be provided as file paths, and metadata updates can (Optional) The file path to the Eventstream's platform-specific definition file. The content will be encoded as Base64 and sent in the request. .PARAMETER UpdateMetadata -(Optional)A boolean flag indicating whether to update the Eventstream's metadata. +(Optional)A boolean flag indicating whether to update the Eventstream's metadata. Default: `$false`. .EXAMPLE @@ -38,12 +38,13 @@ Updates both the content and metadata of the Eventstream with ID `67890` in the - The Eventstream content is encoded as Base64 before being sent to the Fabric API. - This function handles asynchronous operations and retrieves operation results if required. -Author: Tiago Balabuch +Author: Tiago Balabuch #> -function Update-FabricEventstreamDefinition { - [CmdletBinding()] +function Update-FabricEventstreamDefinition +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -56,13 +57,14 @@ function Update-FabricEventstreamDefinition { [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$EventstreamPathDefinition, - + [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$EventstreamPathPlatformDefinition ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -71,22 +73,25 @@ function Update-FabricEventstreamDefinition { # Step 2: Construct the API URL $apiEndpointUrl = "{0}/workspaces/{1}/eventstreams/{2}/updateDefinition" -f $FabricConfig.BaseUrl, $WorkspaceId, $EventstreamId - if($EventstreamPathPlatformDefinition){ - $apiEndpointUrl = "?updateMetadata=true" -f $apiEndpointUrl + if ($EventstreamPathPlatformDefinition) + { + $apiEndpointUrl = "?updateMetadata=true" -f $apiEndpointUrl } Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug # Step 3: Construct the request body $body = @{ definition = @{ - parts = @() - } + parts = @() + } } - - if ($EventstreamPathDefinition) { + + if ($EventstreamPathDefinition) + { $EventstreamEncodedContent = Convert-ToBase64 -filePath $EventstreamPathDefinition - - if (-not [string]::IsNullOrEmpty($EventstreamEncodedContent)) { + + if (-not [string]::IsNullOrEmpty($EventstreamEncodedContent)) + { # Add new part to the parts array $body.definition.parts += @{ path = "eventstream.json" @@ -94,15 +99,18 @@ function Update-FabricEventstreamDefinition { payloadType = "InlineBase64" } } - else { + else + { Write-Message -Message "Invalid or empty content in Eventstream definition." -Level Error return $null } } - if ($EventstreamPathPlatformDefinition) { + if ($EventstreamPathPlatformDefinition) + { $EventstreamEncodedPlatformContent = Convert-ToBase64 -filePath $EventstreamPathPlatformDefinition - if (-not [string]::IsNullOrEmpty($EventstreamEncodedPlatformContent)) { + if (-not [string]::IsNullOrEmpty($EventstreamEncodedPlatformContent)) + { # Add new part to the parts array $body.definition.parts += @{ path = ".platform" @@ -110,7 +118,8 @@ function Update-FabricEventstreamDefinition { payloadType = "InlineBase64" } } - else { + else + { Write-Message -Message "Invalid or empty content in platform definition." -Level Error return $null } @@ -118,61 +127,70 @@ function Update-FabricEventstreamDefinition { $bodyJson = $body | ConvertTo-Json -Depth 10 Write-Message -Message "Request Body: $bodyJson" -Level Debug + if ($PSCmdlet.ShouldProcess($EventstreamId, "Update Eventstream")) + { + # Step 4: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Post ` + -Body $bodyJson ` + -ContentType "application/json" ` + -ErrorAction Stop ` + -ResponseHeadersVariable "responseHeader" ` + -StatusCodeVariable "statusCode" + } - # Step 4: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Post ` - -Body $bodyJson ` - -ContentType "application/json" ` - -ErrorAction Stop ` - -ResponseHeadersVariable "responseHeader" ` - -StatusCodeVariable "statusCode" - # Step 5: Handle and log the response - switch ($statusCode) { - 200 { + switch ($statusCode) + { + 200 + { Write-Message -Message "Update definition for Eventstream '$EventstreamId' created successfully!" -Level Info return $response } - 202 { + 202 + { Write-Message -Message "Update definition for Eventstream '$EventstreamId' accepted. Operation in progress!" -Level Info [string]$operationId = $responseHeader["x-ms-operation-id"] [string]$location = $responseHeader["Location"] - [string]$retryAfter = $responseHeader["Retry-After"] + [string]$retryAfter = $responseHeader["Retry-After"] Write-Message -Message "Operation ID: '$operationId'" -Level Debug Write-Message -Message "Location: '$location'" -Level Debug Write-Message -Message "Retry-After: '$retryAfter'" -Level Debug Write-Message -Message "Getting Long Running Operation status" -Level Debug - + $operationStatus = Get-FabricLongRunningOperation -operationId $operationId -location $location Write-Message -Message "Long Running Operation status: $operationStatus" -Level Debug # Handle operation result - if ($operationStatus.status -eq "Succeeded") { + if ($operationStatus.status -eq "Succeeded") + { Write-Message -Message "Operation Succeeded" -Level Debug Write-Message -Message "Getting Long Running Operation result" -Level Debug - + $operationResult = Get-FabricLongRunningOperationResult -operationId $operationId Write-Message -Message "Long Running Operation status: $operationResult" -Level Debug - + return $operationResult - } - else { + } + else + { Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Debug Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Error return $operationStatus - } - } - default { + } + } + default + { Write-Message -Message "Unexpected response code: $statusCode" -Level Error Write-Message -Message "Error details: $($response.message)" -Level Error throw "API request failed with status code $statusCode." } } } - catch { + catch + { # Step 6: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to update Eventstream. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/External Data Share/Get-FabricExternalDataShares.ps1 b/source/Public/External Data Share/Get-FabricExternalDataShares.ps1 similarity index 87% rename from FabricTools/public/External Data Share/Get-FabricExternalDataShares.ps1 rename to source/Public/External Data Share/Get-FabricExternalDataShares.ps1 index d9434787..c79cf336 100644 --- a/FabricTools/public/External Data Share/Get-FabricExternalDataShares.ps1 +++ b/source/Public/External Data Share/Get-FabricExternalDataShares.ps1 @@ -7,7 +7,7 @@ It handles token validation, constructs the API URL, makes the API request, and processes the response. .EXAMPLE - Get-FabricExternalDataShares + Get-FabricExternalDataShares This example retrieves the External Data Shares details .NOTES @@ -21,30 +21,29 @@ function Get-FabricExternalDataShares { param ( ) try { - - # Validate authentication token before proceeding + + # Validate authentication token before proceeding Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired Write-Message -Message "Token validation completed." -Level Debug - # Construct the API endpoint URI for retrieving external data shares + # Construct the API endpoint URI for retrieving external data shares Write-Message -Message "Constructing API endpoint URI..." -Level Debug $apiEndpointURI = "{0}/admin/items/externalDataShares" -f $FabricConfig.BaseUrl, $WorkspaceId - + # Invoke the API request to retrieve external data shares $externalDataShares = Invoke-FabricAPIRequest ` -BaseURI $apiEndpointURI ` -Headers $FabricConfig.FabricHeaders ` - -Method Get + -Method Get # Return the retrieved external data shares Write-Message -Message "Successfully retrieved external data shares." -Level Debug return $externalDataShares - } - catch { + } catch { # Capture and log detailed error information if the API request fails $errorDetails = $_.Exception.Message Write-Message -Message "Failed to retrieve External Data Shares. Error: $errorDetails" -Level Error - } - -} + } + +} \ No newline at end of file diff --git a/FabricTools/public/External Data Share/Revoke-FabricExternalDataShares.ps1 b/source/Public/External Data Share/Revoke-FabricExternalDataShares.ps1 similarity index 77% rename from FabricTools/public/External Data Share/Revoke-FabricExternalDataShares.ps1 rename to source/Public/External Data Share/Revoke-FabricExternalDataShares.ps1 index 0d7f4cb2..373000df 100644 --- a/FabricTools/public/External Data Share/Revoke-FabricExternalDataShares.ps1 +++ b/source/Public/External Data Share/Revoke-FabricExternalDataShares.ps1 @@ -1,4 +1,5 @@ -<# +function Revoke-FabricExternalDataShares { + <# .SYNOPSIS Retrieves External Data Shares details from a specified Microsoft Fabric. @@ -6,8 +7,17 @@ This function retrieves External Data Shares details. It handles token validation, constructs the API URL, makes the API request, and processes the response. -.EXAMPLE - Get-FabricExternalDataShares +.PARAMETER WorkspaceId + The unique identifier of the workspace where the External Data Shares resides. + +.PARAMETER ItemId + The unique identifier of the item associated with the External Data Shares. + +.PARAMETER ExternalDataShareId + The unique identifier of the External Data Share to be retrieved. + + .EXAMPLE + Get-FabricExternalDataShares This example retrieves the External Data Shares details .NOTES @@ -15,9 +25,9 @@ - Calls `Test-TokenExpired` to ensure token validity before making the API request. Author: Tiago Balabuch -#> -function Revoke-FabricExternalDataShares { - [CmdletBinding()] + #> + + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -26,14 +36,14 @@ function Revoke-FabricExternalDataShares { [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$ItemId, - + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$ExternalDataShareId - ) + ) try { - + # Step 2: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -42,20 +52,22 @@ function Revoke-FabricExternalDataShares { # Step 4: Loop to retrieve all capacities with continuation token Write-Message -Message "Constructing API endpoint URI..." -Level Debug $apiEndpointURI = "{0}/admin/workspaces/{1}/items/{2}/externalDataShares/{3}/revoke" -f $FabricConfig.BaseUrl, $WorkspaceId, $ItemId, $ExternalDataShareId - + + if ($PSCmdlet.ShouldProcess("$ExternalDataShareId", "revoke")) { + $externalDataShares = Invoke-FabricAPIRequest ` -BaseURI $apiEndpointURI ` -Headers $FabricConfig.FabricHeaders ` - -Method Post + -Method Post + } # Step 4: Return retrieved data Write-Message -Message "Successfully revoked external data shares." -Level Info return $externalDataShares - } - catch { + } catch { # Step 10: Capture and log error details $errorDetails = $_.Exception.Message Write-Message -Message "Failed to retrieve External Data Shares. Error: $errorDetails" -Level Error - } - + } + } diff --git a/FabricTools/public/Get-AllFabricDatasetRefreshes.ps1 b/source/Public/Get-AllFabricDatasetRefreshes.ps1 similarity index 88% rename from FabricTools/public/Get-AllFabricDatasetRefreshes.ps1 rename to source/Public/Get-AllFabricDatasetRefreshes.ps1 index 94dab947..12a6c81f 100644 --- a/FabricTools/public/Get-AllFabricDatasetRefreshes.ps1 +++ b/source/Public/Get-AllFabricDatasetRefreshes.ps1 @@ -1,21 +1,21 @@ -<# +function Get-FabricDatasetRefreshes { + <# .SYNOPSIS Retrieves all refreshes for all datasets in all PowerBI workspaces. .DESCRIPTION -The Get-AllFabricDatasetRefreshes function retrieves all refreshes for all datasets in all PowerBI workspaces. It supports multiple aliases for flexibility. +The Get-FabricDatasetRefreshes function retrieves all refreshes for all datasets in all PowerBI workspaces. It supports multiple aliases for flexibility. .EXAMPLE -Get-AllFabricDatasetRefreshes +Get-FabricDatasetRefreshes This example retrieves all refreshes for all datasets in all PowerBI workspaces. .NOTES The function makes a GET request to the PowerBI API to retrieve the refreshes. It loops through each workspace and each dataset in each workspace. If a dataset is refreshable, it retrieves the refreshes for the dataset and selects the most recent one. It then creates a PSCustomObject with information about the refresh and adds it to an array of refreshes. Finally, it returns the array of refreshes. -#> + #> -# This function retrieves all refreshes for all datasets in all PowerBI workspaces. -function Get-AllFabricDatasetRefreshes { + # This function retrieves all refreshes for all datasets in all PowerBI workspaces. # Define aliases for the function for flexibility. Confirm-FabricAuthToken | Out-Null diff --git a/FabricTools/public/Get-FabricAPIClusterURI.ps1 b/source/Public/Get-FabricAPIClusterURI.ps1 similarity index 50% rename from FabricTools/public/Get-FabricAPIClusterURI.ps1 rename to source/Public/Get-FabricAPIClusterURI.ps1 index 73cdf5f2..ced8ce60 100644 --- a/FabricTools/public/Get-FabricAPIClusterURI.ps1 +++ b/source/Public/Get-FabricAPIClusterURI.ps1 @@ -1,26 +1,26 @@ -<# +function Get-FabricAPIclusterURI { + <# .SYNOPSIS -Retrieves the cluster URI for the tenant. + Retrieves the cluster URI for the tenant. .DESCRIPTION -The Get-FabricAPIclusterURI function retrieves the cluster URI for the tenant. It supports multiple aliases for flexibility. + The Get-FabricAPIclusterURI function retrieves the cluster URI for the tenant. It supports multiple aliases for flexibility. .EXAMPLE -Get-FabricAPIclusterURI + Get-FabricAPIclusterURI -This example retrieves the cluster URI for the tenant. + This example retrieves the cluster URI for the tenant. .NOTES -The function retrieves the PowerBI access token and makes a GET request to the PowerBI API to retrieve the datasets. It then extracts the '@odata.context' property from the response, splits it on the '/' character, and selects the third element. This element is used to construct the cluster URI, which is then returned by the function. + The function retrieves the PowerBI access token and makes a GET request to the PowerBI API to retrieve the datasets. It then extracts the '@odata.context' property from the response, splits it on the '/' character, and selects the third element. This element is used to construct the cluster URI, which is then returned by the function. -#> + #> -#This function retrieves the cluster URI for the tenant. - - -function Get-FabricAPIclusterURI { + #This function retrieves the cluster URI for the tenant. # Define aliases for the function for flexibility. [Alias("Get-FabAPIClusterURI")] + [CmdletBinding()] + [OutputType([string])] Param ( ) @@ -40,4 +40,4 @@ function Get-FabricAPIclusterURI { # Return the cluster URI. return $clusterURI -} \ No newline at end of file +} diff --git a/FabricTools/public/Get-FabricAuthToken.ps1 b/source/Public/Get-FabricAuthToken.ps1 similarity index 68% rename from FabricTools/public/Get-FabricAuthToken.ps1 rename to source/Public/Get-FabricAuthToken.ps1 index 55707a69..3bbd3fcb 100644 --- a/FabricTools/public/Get-FabricAuthToken.ps1 +++ b/source/Public/Get-FabricAuthToken.ps1 @@ -22,18 +22,18 @@ #> function Get-FabricAuthToken { - [Alias("Get-FabAuthToken")] - [CmdletBinding()] - param - ( - ) - - # Check if the Fabric token is already set - if (!$FabricSession.FabricToken) { - # If not, set the Fabric token - Set-FabricAuthToken - } - - # Output the Fabric token - return $FabricSession.FabricToken + [Alias("Get-FabAuthToken")] + [CmdletBinding()] + param + ( + ) + + # Check if the Fabric token is already set + if (!$FabricSession.FabricToken) { + # If not, set the Fabric token + Set-FabricAuthToken + } + + # Output the Fabric token + return $FabricSession.FabricToken } \ No newline at end of file diff --git a/FabricTools/public/Get-FabricConnection.ps1 b/source/Public/Get-FabricConnection.ps1 similarity index 57% rename from FabricTools/public/Get-FabricConnection.ps1 rename to source/Public/Get-FabricConnection.ps1 index b621a862..a1056914 100644 --- a/FabricTools/public/Get-FabricConnection.ps1 +++ b/source/Public/Get-FabricConnection.ps1 @@ -1,4 +1,5 @@ -<# +function Get-FabricConnection { + <# .SYNOPSIS Retrieves Fabric connections. @@ -21,29 +22,25 @@ This example retrieves specific connection from Fabric with ID "12345". .NOTES https://learn.microsoft.com/en-us/rest/api/fabric/core/connections/get-connection?tabs=HTTP https://learn.microsoft.com/en-us/rest/api/fabric/core/connections/list-connections?tabs=HTTP -#> - - -Function Get-FabricConnection { - [CmdletBinding()] - param - ( - [Parameter(Mandatory = $false)] - [string]$connectionId - ) + #> + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $false)] + [string]$connectionId + ) + + begin { + Confirm-FabricAuthToken | Out-Null + } - begin { - Confirm-FabricAuthToken | Out-Null - } + process { + if ($connectionId) { + $result = Invoke-FabricAPIRequest -Uri "/connections/$($connectionId)" -Method GET + } else { + $result = Invoke-FabricAPIRequest -Uri "/connections" -Method GET + } - process { - if ($connectionId) { - $result = Invoke-FabricAPIRequest -Uri "/connections/$($connectionId)" -Method GET - } - else { - $result = Invoke-FabricAPIRequest -Uri "/connections" -Method GET + return $result.value } - - return $result.value - } -} +} \ No newline at end of file diff --git a/FabricTools/public/Get-FabricDatasetRefreshes.ps1 b/source/Public/Get-FabricDatasetRefreshes.ps1 similarity index 94% rename from FabricTools/public/Get-FabricDatasetRefreshes.ps1 rename to source/Public/Get-FabricDatasetRefreshes.ps1 index 7f979aae..dde942ba 100644 --- a/FabricTools/public/Get-FabricDatasetRefreshes.ps1 +++ b/source/Public/Get-FabricDatasetRefreshes.ps1 @@ -31,14 +31,14 @@ function Get-FabricDatasetRefreshes { # Define a mandatory parameter for the dataset ID. Param ( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string]$DatasetID, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string]$workspaceId ) # Get the dataset information. - $di = get-powerbidataset -id $DatasetID -WorkspaceId $workspaceId + $di = Get-PowerBIDataset -id $DatasetID -WorkspaceId $workspaceId # Check if the dataset is refreshable. if ($di.isrefreshable -eq "True") { diff --git a/FabricTools/public/Get-FabricDebugInfo.ps1 b/source/Public/Get-FabricDebugInfo.ps1 similarity index 62% rename from FabricTools/public/Get-FabricDebugInfo.ps1 rename to source/Public/Get-FabricDebugInfo.ps1 index 1c7704a0..f51809ef 100644 --- a/FabricTools/public/Get-FabricDebugInfo.ps1 +++ b/source/Public/Get-FabricDebugInfo.ps1 @@ -1,7 +1,6 @@ function Get-FabricDebugInfo { - #Requires -Version 7.1 -<# + <# .SYNOPSIS Shows internal debug information about the current session. @@ -21,26 +20,27 @@ function Get-FabricDebugInfo { - 2024-12-22 - FGE: Added Verbose Output -#> + #> -[CmdletBinding()] + [CmdletBinding()] + [OutputType([System.Collections.Hashtable])] param ( ) -begin {} + begin { } -process { - Write-Verbose "Show current session object" + process { + Write-Verbose "Show current session object" - return @{ - FabricSession = $script:FabricSession - AzureSession = $script:AzureSession - FabricConfig = $script:FabricConfig - } + return @{ + FabricSession = $script:FabricSession + AzureSession = $script:AzureSession + FabricConfig = $script:FabricConfig + } -} + } -end {} + end { } -} \ No newline at end of file +} diff --git a/FabricTools/public/Get-FabricUsageMetricsQuery.ps1 b/source/Public/Get-FabricUsageMetricsQuery.ps1 similarity index 56% rename from FabricTools/public/Get-FabricUsageMetricsQuery.ps1 rename to source/Public/Get-FabricUsageMetricsQuery.ps1 index 14a57128..13b0357d 100644 --- a/FabricTools/public/Get-FabricUsageMetricsQuery.ps1 +++ b/source/Public/Get-FabricUsageMetricsQuery.ps1 @@ -1,4 +1,5 @@ -<# +function Get-FabricUsageMetricsQuery { + <# .SYNOPSIS Retrieves usage metrics for a specific dataset. @@ -30,31 +31,30 @@ This example retrieves the usage metrics for a specific dataset given the datase .NOTES The function defines the headers and body for a POST request to the PowerBI API to retrieve the usage metrics for the specified dataset. It then makes the POST request and returns the response. -#> + #> -# This function retrieves usage metrics for a specific dataset. -function Get-FabricUsageMetricsQuery { - # Define aliases for the function for flexibility. - [Alias("Get-FabUsageMetricsQuery")] + # This function retrieves usage metrics for a specific dataset. + # Define aliases for the function for flexibility. + [Alias("Get-FabUsageMetricsQuery")] - # Define parameters for the dataset ID, group ID, report name, token, and impersonated user. - param ( - [Parameter(Mandatory = $true)] - [string]$DatasetID, - [Parameter(Mandatory = $true)] - [string]$groupId, - [Parameter(Mandatory = $true)] - $reportname, - [Parameter(Mandatory = $false)] - [string]$ImpersonatedUser = "" - ) + # Define parameters for the dataset ID, group ID, report name, token, and impersonated user. + param ( + [Parameter(Mandatory = $true)] + [string]$DatasetID, + [Parameter(Mandatory = $true)] + [string]$groupId, + [Parameter(Mandatory = $true)] + $reportname, + [Parameter(Mandatory = $false)] + [string]$ImpersonatedUser = "" + ) - # Confirm the authentication token. - Confirm-FabricAuthToken | Out-Null + # Confirm the authentication token. + Confirm-FabricAuthToken | Out-Null - # Define the body of the POST request. - if ($ImpersonatedUser -ne "") { - $reportbody = '{ + # Define the body of the POST request. + if ($ImpersonatedUser -ne "") { + $reportbody = '{ "queries": [ { "query": "EVALUATE VALUES(' + $reportname + ')" @@ -63,11 +63,10 @@ function Get-FabricUsageMetricsQuery { "serializerSettings": { "includeNulls": true }, - "impersonatedUserName": "'+ $ImpersonatedUser + '" + "impersonatedUserName": "' + $ImpersonatedUser + '" }' - } - else { - $reportbody = '{ + } else { + $reportbody = '{ "queries": [ { "query": "EVALUATE VALUES(' + $reportname + ')" @@ -77,8 +76,8 @@ function Get-FabricUsageMetricsQuery { "includeNulls": true } }' - } - # Make a POST request to the PowerBI API to retrieve the usage metrics for the specified dataset. - # The function returns the response of the POST request. - return Invoke-RestMethod -uri "$($PowerBI.BaseApiUrl)/groups/$groupId/datasets/$DatasetID/executeQueries" -Headers $FabricSession.HeaderParams -Body $reportbody -Method Post + } + # Make a POST request to the PowerBI API to retrieve the usage metrics for the specified dataset. + # The function returns the response of the POST request. + return Invoke-RestMethod -uri "$($PowerBI.BaseApiUrl)/groups/$groupId/datasets/$DatasetID/executeQueries" -Headers $FabricSession.HeaderParams -Body $reportbody -Method Post } \ No newline at end of file diff --git a/FabricTools/public/Get-SHA256.ps1 b/source/Public/Get-SHA256.ps1 similarity index 93% rename from FabricTools/public/Get-SHA256.ps1 rename to source/Public/Get-SHA256.ps1 index 905b291d..87e66724 100644 --- a/FabricTools/public/Get-SHA256.ps1 +++ b/source/Public/Get-SHA256.ps1 @@ -1,4 +1,6 @@ -<# + +function Get-Sha256 ($string) { + <# .SYNOPSIS Calculates the SHA256 hash of a string. @@ -15,10 +17,9 @@ This example calculates the SHA256 hash of a string. .NOTES The function creates a new SHA256CryptoServiceProvider object, converts the string to a byte array using UTF8 encoding, computes the SHA256 hash of the byte array, converts the hash to a string and removes any hyphens, and returns the resulting hash. -#> + #> -# This function calculates the SHA256 hash of a string. -function Get-Sha256 ($string) { + # This function calculates the SHA256 hash of a string. # Create a new SHA256CryptoServiceProvider object. $sha256 = New-Object System.Security.Cryptography.SHA256CryptoServiceProvider diff --git a/FabricTools/public/Invoke-FabricAPIRequest.ps1 b/source/Public/Invoke-FabricAPIRequest.ps1 similarity index 80% rename from FabricTools/public/Invoke-FabricAPIRequest.ps1 rename to source/Public/Invoke-FabricAPIRequest.ps1 index d54b46a9..5ff3ad80 100644 --- a/FabricTools/public/Invoke-FabricAPIRequest.ps1 +++ b/source/Public/Invoke-FabricAPIRequest.ps1 @@ -1,5 +1,6 @@ +Function Invoke-FabricAPIRequest { -<# + <# .SYNOPSIS Sends an HTTP request to a Fabric API endpoint and retrieves the response. Takes care of: authentication, 429 throttling, Long-Running-Operation (LRO) response @@ -46,23 +47,29 @@ This function was originally written by Rui Romano. https://github.com/RuiRomano/fabricps-pbip #> - - -Function Invoke-FabricAPIRequest { - <# - .SYNOPSIS - Sends an HTTP request to a Fabric API endpoint and retrieves the response. - Takes care of: authentication, 429 throttling, Long-Running-Operation (LRO) response - #> [CmdletBinding()] param( - [Parameter(Mandatory = $false)] [string] $authToken, - [Parameter(Mandatory = $true)] [string] $uri, - [Parameter(Mandatory = $false)] [ValidateSet('Get', 'Post', 'Delete', 'Put', 'Patch')] [string] $method = "Get", - [Parameter(Mandatory = $false)] $body, - [Parameter(Mandatory = $false)] [string] $contentType = "application/json; charset=utf-8", - [Parameter(Mandatory = $false)] [int] $timeoutSec = 240, - [Parameter(Mandatory = $false)] [int] $retryCount = 0 + [Parameter(Mandatory = $false)] + [string] $authToken, + + [Parameter(Mandatory = $true)] + [string] $uri, + + [Parameter(Mandatory = $false)] + [ValidateSet('Get', 'Post', 'Delete', 'Put', 'Patch')] + [string] $method = "Get", + + [Parameter(Mandatory = $false)] + $body, + + [Parameter(Mandatory = $false)] + [string] $contentType = "application/json; charset=utf-8", + + [Parameter(Mandatory = $false)] + [int] $timeoutSec = 240, + + [Parameter(Mandatory = $false)] + [int] $retryCount = 0 ) Confirm-FabricAuthToken | Out-Null @@ -72,28 +79,26 @@ Function Invoke-FabricAPIRequest { $requestUrl = "$($FabricSession.BaseApiUrl)/$uri" Write-Verbose "Calling $requestUrl" - $response = Invoke-WebRequest -Headers $fabricHeaders -Method $method -Uri $requestUrl -Body $body -TimeoutSec $timeoutSec + $response = Invoke-WebRequest -Headers $fabricHeaders -ContentType $contentType -Method $method -Uri $requestUrl -Body $body -TimeoutSec $timeoutSec if ($response.StatusCode -eq 202) { if ($uri -match "jobType=Pipeline") { - write-output "Waiting for pipeline to complete. Please check the status in the Power BI Service." - } - else { + Write-Output "Waiting for pipeline to complete. Please check the status in the Power BI Service." + } else { do { $asyncUrl = [string]$response.Headers.Location - + Write-Output "Waiting for request to complete. Sleeping..." Start-Sleep -Seconds 5 $response2 = Invoke-WebRequest -Headers $fabricHeaders -Method Get -Uri $asyncUrl $lroStatusContent = $response2.Content | ConvertFrom-Json } while ($lroStatusContent.status -ine "succeeded" -and $lroStatusContent.status -ine "failed") - + try { $response = Invoke-WebRequest -Headers $fabricHeaders -Method Get -Uri "$asyncUrl/result" - } - catch { - write-output "No result URL" + } catch { + Write-Output "No result URL" } } } @@ -104,21 +109,18 @@ Function Invoke-FabricAPIRequest { if ($contentBytes[0] -eq 0xef -and $contentBytes[1] -eq 0xbb -and $contentBytes[2] -eq 0xbf) { $contentText = [System.Text.Encoding]::UTF8.GetString($contentBytes[3..$contentBytes.Length]) - } - else { + } else { $contentText = $response.Content } $jsonResult = $contentText | ConvertFrom-Json Write-Output $jsonResult -NoEnumerate - } - else { + } else { Write-Output $response -NoEnumerate } - } - catch { + } catch { $ex = $_.Exception $message = $null - + if ($null -ne $ex.Response) { $responseStatusCode = [int]$ex.Response.StatusCode @@ -140,14 +142,12 @@ Function Invoke-FabricAPIRequest { if ($retryCount -le $maxRetries) { Invoke-FabricAPIRequest -authToken $authToken -uri $uri -method $method -body $body -contentType $contentType -timeoutSec $timeoutSec -retryCount ($retryCount + 1) - } - else { + } else { throw "Exceeded the amount of retries ($maxRetries) after 429 error." } - } - else { - $apiErrorObj = $ex.Response.Headers | Where-Object { $_.key -ieq "x-ms-public-api-error-code" } | Select-object -First 1 + } else { + $apiErrorObj = $ex.Response.Headers | Where-Object { $_.key -ieq "x-ms-public-api-error-code" } | Select-Object -First 1 if ($apiErrorObj) { $apiError = $apiErrorObj.Value[0] @@ -155,8 +155,7 @@ Function Invoke-FabricAPIRequest { if ($apiError -ieq "ItemHasProtectedLabel") { Write-Warning "Item has a protected label." - } - else { + } else { throw } @@ -164,12 +163,11 @@ Function Invoke-FabricAPIRequest { #$errorContent = $ex.Response.Content.ReadAsStringAsync().Result; #$message = "$($ex.Message) - StatusCode: '$($ex.Response.StatusCode)'; Content: '$errorContent'" } - } - else { + } else { $message = "$($ex.Message)" } if ($message) { - throw $message + throw $message } } -} \ No newline at end of file +} diff --git a/FabricTools/public/Invoke-FabricDatasetRefresh.ps1 b/source/Public/Invoke-FabricDatasetRefresh.ps1 similarity index 99% rename from FabricTools/public/Invoke-FabricDatasetRefresh.ps1 rename to source/Public/Invoke-FabricDatasetRefresh.ps1 index be979475..b34b79b6 100644 --- a/FabricTools/public/Invoke-FabricDatasetRefresh.ps1 +++ b/source/Public/Invoke-FabricDatasetRefresh.ps1 @@ -33,8 +33,7 @@ function Invoke-FabricDatasetRefresh { if ((Get-FabricDataset -DatasetId $DatasetID).isrefreshable -eq $false) { # If the dataset is not refreshable, write an error Write-Error "Dataset is not refreshable" - } - else { + } else { # If the dataset is refreshable, invoke a PowerBI REST method to refresh the dataset # The URL for the request is constructed using the provided workspace ID and dataset ID. diff --git a/FabricTools/public/Item/Export-FabricItem.ps1 b/source/Public/Item/Export-FabricItem.ps1 similarity index 91% rename from FabricTools/public/Item/Export-FabricItem.ps1 rename to source/Public/Item/Export-FabricItem.ps1 index a760653c..a1ceafe0 100644 --- a/FabricTools/public/Item/Export-FabricItem.ps1 +++ b/source/Public/Item/Export-FabricItem.ps1 @@ -51,7 +51,7 @@ Function Export-FabricItem { $parts = $item.definition.parts - if (!(test-path $path)) { + if (!(Test-Path $path)) { New-Item -ItemType Directory -Force -Path $path } @@ -63,14 +63,13 @@ Function Export-FabricItem { } # Display a message indicating the items were exported - write-output "Items exported to $path" + Write-Output "Items exported to $path" } else { $items = Invoke-FabricAPIRequest -Uri "workspaces/$workspaceId/items" -Method Get if ($filter) { $items = $items.value | Where-Object $filter - } - else { + } else { $items = $items.value } @@ -83,22 +82,22 @@ Function Export-FabricItem { $itemOutputPath = "$path\$workspaceId\$($itemName).$($itemType)" if ($itemType -in @("report", "semanticmodel", "notebook", "SparkJobDefinitionV1", "DataPipeline")) { - write-output "Getting definition of: $itemId / $itemName / $itemType" + Write-Output "Getting definition of: $itemId / $itemName / $itemType" $response = Invoke-FabricAPIRequest -Uri "workspaces/$workspaceId/items/$itemId/getDefinition" -Method Post $partCount = $response.definition.parts.Count - write-output "Parts: $partCount" + Write-Output "Parts: $partCount" if ($partCount -gt 0) { foreach ($part in $response.definition.parts) { - write-output "Saving part: $($part.path)" + Write-Output "Saving part: $($part.path)" $outputFilePath = "$itemOutputPath\$($part.path.Replace("/", "\"))" New-Item -ItemType Directory -Path (Split-Path $outputFilePath -Parent) -ErrorAction SilentlyContinue | Out-Null $payload = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($part.payload)) $payload | Out-File -FilePath "$outputFilePath" - + } @{ @@ -107,9 +106,8 @@ Function Export-FabricItem { } | ConvertTo-Json | Out-File "$itemOutputPath\item.metadata.json" } - } - else { - write-output "Type '$itemType' not available for export." + } else { + Write-Output "Type '$itemType' not available for export." } } } diff --git a/source/Public/Item/Get-FabricItem.ps1 b/source/Public/Item/Get-FabricItem.ps1 new file mode 100644 index 00000000..9fbf0c93 --- /dev/null +++ b/source/Public/Item/Get-FabricItem.ps1 @@ -0,0 +1,69 @@ +function Get-FabricItem { + <# +.SYNOPSIS +Retrieves fabric items from a workspace. + +.DESCRIPTION +The Get-FabricItem function retrieves fabric items from a specified workspace. It can retrieve all items or filter them by item type. + +.PARAMETER workspaceId +The ID of the workspace from which to retrieve the fabric items. + +.PARAMETER Workspace +The workspace object from which to retrieve the fabric items. This parameter can be piped into the function. + +.PARAMETER itemID +The ID of the specific item to retrieve. If not specified, all items will be retrieved. + +.PARAMETER type +(Optional) The type of the fabric items to retrieve. If not specified, all items will be retrieved. + +.EXAMPLE +Get-FabricItem -workspaceId "12345" -type "file" + +This example retrieves all fabric items of type "file" from the workspace with ID "12345". + +.NOTES +This function was originally written by Rui Romano. +https://github.com/RuiRomano/fabricps-pbip + + #> + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true, ParameterSetName = 'WorkspaceId')] + [string]$workspaceId, + + [Parameter(Mandatory = $true, ParameterSetName = 'WorkspaceObject', ValueFromPipeline = $true )] + $Workspace, + + [Parameter(Mandatory = $false)] + [Alias("itemType")] + [string]$type, + + [Parameter(Mandatory = $false)] + [string]$itemID + ) + + begin { + Confirm-FabricAuthToken | Out-Null + } + + process { + if ($PSCmdlet.ParameterSetName -eq 'WorkspaceObject') { + $workspaceID = $Workspace.id + } + if ($itemID) { + $result = Invoke-FabricAPIRequest -Uri "workspaces/$($workspaceID)/items/$($itemID)" -Method Get + } else { + if ($type) { + $result = Invoke-FabricAPIRequest -Uri "workspaces/$($workspaceID)/items?type=$type" -Method Get + } else { + # Invoke the Fabric API to get the workspaces + $result = Invoke-FabricAPIRequest -Uri "workspaces/$($workspaceID)/items" -Method Get + } + # Output the result + return $result.value + } + } +} \ No newline at end of file diff --git a/FabricTools/public/Item/Import-FabricItem.ps1 b/source/Public/Item/Import-FabricItem.ps1 similarity index 79% rename from FabricTools/public/Item/Import-FabricItem.ps1 rename to source/Public/Item/Import-FabricItem.ps1 index b885fb21..1052201f 100644 --- a/FabricTools/public/Item/Import-FabricItem.ps1 +++ b/source/Public/Item/Import-FabricItem.ps1 @@ -2,6 +2,9 @@ <# .SYNOPSIS Imports items using the Power BI Project format (PBIP) into a Fabric workspace from a specified file system source. + .DESCRIPTION + The Import-FabricItem function imports items using the Power BI Project format (PBIP) into a Fabric workspace from a specified file system source. It supports multiple aliases for flexibility. + The function handles the import of datasets and reports, ensuring that the correct item type is used and that the items are created or updated as necessary. .PARAMETER fileOverrides This parameter let's you override a PBIP file without altering the local file. @@ -24,7 +27,7 @@ This function requires the Invoke-FabricAPIRequest function to be available in the current session. This function was originally written by Rui Romano. https://github.com/RuiRomano/fabricps-pbip - #> +#> Function Import-FabricItem { <# @@ -34,10 +37,10 @@ Function Import-FabricItem { .PARAMETER fileOverrides This parameter let's you override a PBIP file without altering the local file. #> - [CmdletBinding()] + [CmdletBinding(SupportsShouldProcess)] param ( - [string] $path = '.\pbipOutput' + [string] $path = '.\pbipOutput' , [string] $workspaceId , [string] $filter = $null , [hashtable] $fileOverrides @@ -50,27 +53,27 @@ Function Import-FabricItem { $itemsFolders = Get-ChildItem -Path $path -recurse -include *.pbir, *.pbidataset if ($filter) { - $itemsFolders = $itemsFolders | where-object { $_.Directory.FullName -like $filter } + $itemsFolders = $itemsFolders | Where-Object { $_.Directory.FullName -like $filter } } # Get existing items of the workspace $items = Invoke-FabricAPIRequest -Uri "workspaces/$workspaceId/items" -Method Get - write-output "Existing items: $($items.Count)" + Write-Output "Existing items: $($items.Count)" # Datasets first - $itemsFolders = $itemsFolders | Select-Object @{n = "Order"; e = { if ($_.Name -like "*.pbidataset") { 1 } else { 2 } } }, * | sort-object Order + $itemsFolders = $itemsFolders | Select-Object @{n = "Order"; e = { if ($_.Name -like "*.pbidataset") { 1 } else { 2 } } }, * | Sort-Object Order - $datasetReferences = @{} + $datasetReferences = @{ } foreach ($itemFolder in $itemsFolders) { # Get the parent folder $itemPath = $itemFolder.Directory.FullName - write-output "Processing item: '$itemPath'" + Write-Output "Processing item: '$itemPath'" $files = Get-ChildItem -Path $itemPath -Recurse -Attributes !Directory @@ -82,19 +85,19 @@ Function Import-FabricItem { $itemMetadataStr = Get-Content "$itemPath\item.metadata.json" if ($fileOverrides) { - $fileOverrideMatch = $fileOverrides.GetEnumerator() | where-object { "$itemPath\item.metadata.json" -ilike $_.Name } | select-object -First 1 + $fileOverrideMatch = $fileOverrides.GetEnumerator() | Where-Object { "$itemPath\item.metadata.json" -ilike $_.Name } | Select-Object -First 1 - if ($fileOverrideMatch) { - $itemMetadataStr = $fileOverrideMatch.Value + if ($fileOverrideMatch) { + $itemMetadataStr = $fileOverrideMatch.Value + } } - } $itemMetadata = $itemMetadataStr | ConvertFrom-Json $itemType = $itemMetadata.type if ($itemType -ieq "dataset") { $itemType = "SemanticModel" } - + $displayName = $itemMetadata.displayName @@ -105,7 +108,7 @@ Function Import-FabricItem { #$fileName = $_.Name $filePath = $_.FullName if ($fileOverrides) { - $fileOverrideMatch = $fileOverrides.GetEnumerator() | Where-Object { $filePath -ilike $_.Name } | select-object -First 1 + $fileOverrideMatch = $fileOverrides.GetEnumerator() | Where-Object { $filePath -ilike $_.Name } | Select-Object -First 1 } if ($fileOverrideMatch) { $fileContent = $fileOverrideMatch.Value @@ -114,12 +117,10 @@ Function Import-FabricItem { if ($fileContent -is [string]) { $fileContent = [system.Text.Encoding]::UTF8.GetBytes($fileContent) - } - elseif (!($fileContent -is [byte[]])) { + } elseif (!($fileContent -is [byte[]])) { throw "FileOverrides value type must be string or byte[]" } - } - else { + } else { if ($filePath -like "*.pbir") { $pbirJson = Get-Content -Path $filePath | ConvertFrom-Json @@ -128,7 +129,7 @@ Function Import-FabricItem { # try to swap byPath to byConnection - $reportDatasetPath = (Resolve-path (Join-Path $itemPath $pbirJson.datasetReference.byPath.path.Replace("/", "\"))).Path + $reportDatasetPath = (Resolve-Path (Join-Path $itemPath $pbirJson.datasetReference.byPath.path.Replace("/", "\"))).Path $datasetReference = $datasetReferences[$reportDatasetPath] @@ -152,22 +153,20 @@ Function Import-FabricItem { $fileContent = [system.Text.Encoding]::UTF8.GetBytes($newPBIR) - } - else { + } else { throw "Item API dont support byPath connection, switch the connection in the *.pbir file to 'byConnection'." } } else { $fileContent = Get-Content -Path $filePath -AsByteStream -Raw } - } - else { - + } else { + $fileContent = Get-Content -Path $filePath -AsByteStream -Raw } } - + $partPath = $filePath.Replace($itemPathAbs, "").TrimStart("\").Replace("\", "/") - write-host "Processing part: '$partPath'" + Write-Output "Processing part: '$partPath'" $fileEncodedContent = [Convert]::ToBase64String($fileContent) Write-Output @{ @@ -188,13 +187,13 @@ Function Import-FabricItem { throw "Found more than one item for displayName '$displayName'" } - Write-output "Item '$displayName' of type '$itemType' already exists." -ForegroundColor Yellow + Write-Output "Item '$displayName' of type '$itemType' already exists." -ForegroundColor Yellow $itemId = $foundItem.id } if ($null -eq $itemId) { - write-output "Creating a new item" + Write-Output "Creating a new item" # Prepare the request @@ -206,25 +205,29 @@ Function Import-FabricItem { } } | ConvertTo-Json -Depth 3 - $createItemResult = Invoke-FabricAPIRequest -uri "workspaces/$workspaceId/items" -method Post -body $itemRequest + if($PSCmdlet.ShouldProcess($itemPath, "Create Item")) { + $createItemResult = Invoke-FabricAPIRequest -uri "workspaces/$workspaceId/items" -method Post -body $itemRequest + } $itemId = $createItemResult.id - write-output "Created a new item with ID '$itemId' $([datetime]::Now.ToString("s"))" -ForegroundColor Green + Write-Output "Created a new item with ID '$itemId' $([datetime]::Now.ToString("s"))" -ForegroundColor Green Write-Output $itemId - } - else { - write-output "Updating item definition" + } else { + Write-Output "Updating item definition" $itemRequest = @{ definition = @{ Parts = $parts } } | ConvertTo-Json -Depth 3 + if($PSCmdlet.ShouldProcess($itemPath, "Update Item")) { + Invoke-FabricAPIRequest -Uri "workspaces/$workspaceId/items/$itemId/updateDefinition" -Method Post -Body $itemRequest + } - write-output "Updated new item with ID '$itemId' $([datetime]::Now.ToString("s"))" -ForegroundColor Green + Write-Output "Updated new item with ID '$itemId' $([datetime]::Now.ToString("s"))" -ForegroundColor Green Write-Output $itemId } diff --git a/source/Public/Item/Remove-FabricItem.ps1 b/source/Public/Item/Remove-FabricItem.ps1 new file mode 100644 index 00000000..219d358d --- /dev/null +++ b/source/Public/Item/Remove-FabricItem.ps1 @@ -0,0 +1,70 @@ +<# +.SYNOPSIS + Removes selected items from a Fabric workspace. + +.DESCRIPTION + The Remove-FabricItems function removes selected items from a specified Fabric workspace. It uses the workspace ID and an optional filter to select the items to remove. If a filter is provided, only items whose DisplayName matches the filter are removed. + +.PARAMETER WorkspaceID + The ID of the Fabric workspace. This is a mandatory parameter. + +.PARAMETER Filter + An optional filter to select items to remove. If provided, only items whose DisplayName matches the filter are removed. + +.PARAMETER ItemID + The ID of a specific item to remove. If provided, this item is removed regardless of the filter + +.EXAMPLE + Remove-FabricItems -WorkspaceID "12345678-90ab-cdef-1234-567890abcdef" -Filter "*test*" + + This command removes all items from the workspace with the specified ID whose DisplayName includes "test". + +.INPUTS + String. You can pipe two strings that contain the workspace ID and filter to Remove-FabricItems. + +.OUTPUTS + None. This function does not return any output. + +.NOTES + This function was written by Rui Romano. + https://github.com/RuiRomano/fabricps-pbip +#> + +Function Remove-FabricItem { + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] + param + ( + [Parameter(Mandatory = $true)] + [string]$workspaceId, + [Parameter(Mandatory = $false)] + [string]$filter, + [Parameter(Mandatory = $false)] + [string]$itemID + ) + + Confirm-FabricAuthToken | Out-Null + + # Invoke the Fabric API to get the items in the workspace + if ($PSCmdlet.ShouldProcess("Remove items from workspace $workspaceId")) { + if ($itemID) { + Invoke-FabricAPIRequest -Uri "workspaces/$($workspaceID)/items/$($itemID)" -Method Delete + } else { + $items = Invoke-FabricAPIRequest -Uri "workspaces/$workspaceId/items" -Method Get + + # Display the count of existing items + Write-Output "Existing items: $($items.Count)" + + # If a filter is provided + if ($filter) { + # Filter the items whose DisplayName matches the filter + $items = $items | Where-Object { $_.DisplayName -like $filter } + } + + # For each item + foreach ($item in $items) { + # Remove the item + Invoke-FabricAPIRequest -Uri "workspaces/$workspaceId/items/$($item.ID)" -Method Delete + } + } + } +} diff --git a/FabricTools/public/KQL Dashboard/Get-FabricKQLDashboard.ps1 b/source/Public/KQL Dashboard/Get-FabricKQLDashboard.ps1 similarity index 93% rename from FabricTools/public/KQL Dashboard/Get-FabricKQLDashboard.ps1 rename to source/Public/KQL Dashboard/Get-FabricKQLDashboard.ps1 index 66087294..754fdd8e 100644 --- a/FabricTools/public/KQL Dashboard/Get-FabricKQLDashboard.ps1 +++ b/source/Public/KQL Dashboard/Get-FabricKQLDashboard.ps1 @@ -16,6 +16,10 @@ Get-FabricKQLDashboard -WorkspaceId "12345" -KQLDashboardName "Development" Retrieves the "Development" KQLDashboard from workspace "12345". +.PARAMETER KQLDashboardID + The Id of the KQLDashboard to retrieve. This parameter cannot be used together with KQLDashboardName. The value for KQLDashboardID is a GUID. + An example of a GUID is '12345678-1234-1234-1234-123456789012'. + .EXAMPLE Get-FabricKQLDashboard -WorkspaceId "12345" @@ -25,7 +29,7 @@ Retrieves all KQLDashboards in workspace "12345". - Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. - Calls `Test-TokenExpired` to ensure token validity before making the API request. -Author: Tiago Balabuch +Author: Tiago Balabuch #> @@ -65,22 +69,22 @@ function Get-FabricKQLDashboard { if (-not ([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.GetName().Name -eq "System.Web" })) { Add-Type -AssemblyName System.Web } - + # Step 4: Loop to retrieve all capacities with continuation token Write-Message -Message "Loop started to get continuation token" -Level Debug $baseApiEndpointUrl = "{0}/workspaces/{1}/kqlDashboards" -f $FabricConfig.BaseUrl, $WorkspaceId - + do { # Step 5: Construct the API URL $apiEndpointUrl = $baseApiEndpointUrl - + if ($null -ne $continuationToken) { # URL-encode the continuation token $encodedToken = [System.Web.HttpUtility]::UrlEncode($continuationToken) $apiEndpointUrl = "{0}?continuationToken={1}" -f $apiEndpointUrl, $encodedToken } Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug - + # Step 6: Make the API request $response = Invoke-RestMethod ` -Headers $FabricConfig.FabricHeaders ` @@ -90,7 +94,7 @@ function Get-FabricKQLDashboard { -SkipHttpErrorCheck ` -ResponseHeadersVariable "responseHeader" ` -StatusCodeVariable "statusCode" - + # Step 7: Validate the response code if ($statusCode -ne 200) { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error @@ -99,24 +103,22 @@ function Get-FabricKQLDashboard { Write-Message "Error Code: $($response.errorCode)" -Level Error return $null } - + # Step 8: Add data to the list if ($null -ne $response) { Write-Message -Message "Adding data to the list" -Level Debug $KQLDashboards += $response.value - + # Update the continuation token if present if ($response.PSObject.Properties.Match("continuationToken")) { Write-Message -Message "Updating the continuation token" -Level Debug $continuationToken = $response.continuationToken Write-Message -Message "Continuation token: $continuationToken" -Level Debug - } - else { + } else { Write-Message -Message "Updating the continuation token to null" -Level Debug $continuationToken = $null } - } - else { + } else { Write-Message -Message "No data received from the API." -Level Warning break } @@ -126,11 +128,9 @@ function Get-FabricKQLDashboard { # Step 8: Filter results based on provided parameters $KQLDashboard = if ($KQLDashboardId) { $KQLDashboards | Where-Object { $_.Id -eq $KQLDashboardId } - } - elseif ($KQLDashboardName) { + } elseif ($KQLDashboardName) { $KQLDashboards | Where-Object { $_.DisplayName -eq $KQLDashboardName } - } - else { + } else { # Return all KQLDashboards if no filter is provided Write-Message -Message "No filter provided. Returning all KQLDashboards." -Level Debug $KQLDashboards @@ -140,16 +140,14 @@ function Get-FabricKQLDashboard { if ($KQLDashboard) { Write-Message -Message "KQLDashboard found matching the specified criteria." -Level Debug return $KQLDashboard - } - else { + } else { Write-Message -Message "No KQLDashboard found matching the provided criteria." -Level Warning return $null } - } - catch { + } catch { # Step 10: Capture and log error details $errorDetails = $_.Exception.Message Write-Message -Message "Failed to retrieve KQLDashboard. Error: $errorDetails" -Level Error - } - -} + } + +} \ No newline at end of file diff --git a/FabricTools/public/KQL Dashboard/Get-FabricKQLDashboardDefinition.ps1 b/source/Public/KQL Dashboard/Get-FabricKQLDashboardDefinition.ps1 similarity index 97% rename from FabricTools/public/KQL Dashboard/Get-FabricKQLDashboardDefinition.ps1 rename to source/Public/KQL Dashboard/Get-FabricKQLDashboardDefinition.ps1 index 4f0cf5d1..48f81469 100644 --- a/FabricTools/public/KQL Dashboard/Get-FabricKQLDashboardDefinition.ps1 +++ b/source/Public/KQL Dashboard/Get-FabricKQLDashboardDefinition.ps1 @@ -4,7 +4,7 @@ Retrieves the definition of a KQLDashboard from a specific workspace in Microsoft Fabric. .DESCRIPTION -This function fetches the KQLDashboard's content or metadata from a workspace. +This function fetches the KQLDashboard's content or metadata from a workspace. Handles both synchronous and asynchronous operations, with detailed logging and error handling. .PARAMETER WorkspaceId @@ -84,37 +84,35 @@ function Get-FabricKQLDashboardDefinition { [string]$operationId = $responseHeader["x-ms-operation-id"] Write-Message -Message "Operation ID: '$operationId'" -Level Debug Write-Message -Message "Getting Long Running Operation status" -Level Debug - + $operationStatus = Get-FabricLongRunningOperation -operationId $operationId Write-Message -Message "Long Running Operation status: $operationStatus" -Level Debug # Handle operation result if ($operationStatus.status -eq "Succeeded") { Write-Message -Message "Operation Succeeded" -Level Debug Write-Message -Message "Getting Long Running Operation result" -Level Debug - + $operationResult = Get-FabricLongRunningOperationResult -operationId $operationId Write-Message -Message "Long Running Operation status: $operationResult" -Level Debug - + return $operationResult.definition.parts - } - else { + } else { Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Debug Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Error return $operationStatus - } + } } default { Write-Message -Message "Unexpected response code: $statusCode" -Level Error Write-Message -Message "Error details: $($response.message)" -Level Error throw "API request failed with status code $statusCode." } - + } - } - catch { + } catch { # Step 9: Capture and log error details $errorDetails = $_.Exception.Message Write-Message -Message "Failed to retrieve KQLDashboard. Error: $errorDetails" -Level Error - } - -} + } + +} \ No newline at end of file diff --git a/FabricTools/public/KQL Dashboard/New-FabricKQLDashboard.ps1 b/source/Public/KQL Dashboard/New-FabricKQLDashboard.ps1 similarity index 81% rename from FabricTools/public/KQL Dashboard/New-FabricKQLDashboard.ps1 rename to source/Public/KQL Dashboard/New-FabricKQLDashboard.ps1 index 50694d70..1def48a3 100644 --- a/FabricTools/public/KQL Dashboard/New-FabricKQLDashboard.ps1 +++ b/source/Public/KQL Dashboard/New-FabricKQLDashboard.ps1 @@ -3,8 +3,8 @@ Creates a new KQLDashboard in a specified Microsoft Fabric workspace. .DESCRIPTION -This function sends a POST request to the Microsoft Fabric API to create a new KQLDashboard -in the specified workspace. It supports optional parameters for KQLDashboard description +This function sends a POST request to the Microsoft Fabric API to create a new KQLDashboard +in the specified workspace. It supports optional parameters for KQLDashboard description and path definitions for the KQLDashboard content. .PARAMETER WorkspaceId @@ -29,12 +29,13 @@ An optional path to the platform-specific definition (e.g., .platform file) to u - Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. - Calls `Test-TokenExpired` to ensure token validity before making the API request. -Author: Tiago Balabuch +Author: Tiago Balabuch #> -function New-FabricKQLDashboard { - [CmdletBinding()] +function New-FabricKQLDashboard +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -52,13 +53,14 @@ function New-FabricKQLDashboard { [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$KQLDashboardPathDefinition, - + [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$KQLDashboardPathPlatformDefinition ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -73,16 +75,20 @@ function New-FabricKQLDashboard { displayName = $KQLDashboardName } - if ($KQLDashboardDescription) { + if ($KQLDashboardDescription) + { $body.description = $KQLDashboardDescription } - if ($KQLDashboardPathDefinition) { + if ($KQLDashboardPathDefinition) + { $KQLDashboardEncodedContent = Convert-ToBase64 -filePath $KQLDashboardPathDefinition - if (-not [string]::IsNullOrEmpty($KQLDashboardEncodedContent)) { + if (-not [string]::IsNullOrEmpty($KQLDashboardEncodedContent)) + { # Initialize definition if it doesn't exist - if (-not $body.definition) { + if (-not $body.definition) + { $body.definition = @{ format = "KQLDashboard" parts = @() @@ -96,18 +102,22 @@ function New-FabricKQLDashboard { payloadType = "InlineBase64" } } - else { + else + { Write-Message -Message "Invalid or empty content in KQLDashboard definition." -Level Error return $null } } - if ($KQLDashboardPathPlatformDefinition) { + if ($KQLDashboardPathPlatformDefinition) + { $KQLDashboardEncodedPlatformContent = Convert-ToBase64 -filePath $KQLDashboardPathPlatformDefinition - if (-not [string]::IsNullOrEmpty($KQLDashboardEncodedPlatformContent)) { + if (-not [string]::IsNullOrEmpty($KQLDashboardEncodedPlatformContent)) + { # Initialize definition if it doesn't exist - if (-not $body.definition) { + if (-not $body.definition) + { $body.definition = @{ format = $null parts = @() @@ -121,7 +131,8 @@ function New-FabricKQLDashboard { payloadType = "InlineBase64" } } - else { + else + { Write-Message -Message "Invalid or empty content in platform definition." -Level Error return $null } @@ -129,58 +140,67 @@ function New-FabricKQLDashboard { $bodyJson = $body | ConvertTo-Json -Depth 10 Write-Message -Message "Request Body: $bodyJson" -Level Debug - - # Step 4: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Post ` - -Body $bodyJson ` - -ContentType "application/json" ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -ResponseHeadersVariable "responseHeader" ` - -StatusCodeVariable "statusCode" + if ($PSCmdlet.ShouldProcess($KQLDashboardName, "Create KQLDashboard")) + { + # Step 4: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Post ` + -Body $bodyJson ` + -ContentType "application/json" ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -ResponseHeadersVariable "responseHeader" ` + -StatusCodeVariable "statusCode" + } # Step 5: Handle and log the response - switch ($statusCode) { - 201 { + switch ($statusCode) + { + 201 + { Write-Message -Message "KQLDashboard '$KQLDashboardName' created successfully!" -Level Info return $response } - 202 { + 202 + { Write-Message -Message "KQLDashboard '$KQLDashboardName' creation accepted. Provisioning in progress!" -Level Info - + [string]$operationId = $responseHeader["x-ms-operation-id"] Write-Message -Message "Operation ID: '$operationId'" -Level Debug Write-Message -Message "Getting Long Running Operation status" -Level Debug - + $operationStatus = Get-FabricLongRunningOperation -operationId $operationId Write-Message -Message "Long Running Operation status: $operationStatus" -Level Debug # Handle operation result - if ($operationStatus.status -eq "Succeeded") { + if ($operationStatus.status -eq "Succeeded") + { Write-Message -Message "Operation Succeeded" -Level Debug Write-Message -Message "Getting Long Running Operation result" -Level Debug - + $operationResult = Get-FabricLongRunningOperationResult -operationId $operationId Write-Message -Message "Long Running Operation status: $operationResult" -Level Debug - + return $operationResult } - else { + else + { Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Debug Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Error return $operationStatus - } + } } - default { + default + { Write-Message -Message "Unexpected response code: $statusCode" -Level Error Write-Message -Message "Error details: $($response.message)" -Level Error throw "API request failed with status code $statusCode." } } } - catch { + catch + { # Step 6: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to create KQLDashboard. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/KQL Dashboard/Remove-FabricKQLDashboard.ps1 b/source/Public/KQL Dashboard/Remove-FabricKQLDashboard.ps1 similarity index 77% rename from FabricTools/public/KQL Dashboard/Remove-FabricKQLDashboard.ps1 rename to source/Public/KQL Dashboard/Remove-FabricKQLDashboard.ps1 index 166e7635..f76af2fb 100644 --- a/FabricTools/public/KQL Dashboard/Remove-FabricKQLDashboard.ps1 +++ b/source/Public/KQL Dashboard/Remove-FabricKQLDashboard.ps1 @@ -20,12 +20,13 @@ Deletes the KQLDashboard with ID "67890" from workspace "12345". - Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. - Validates token expiration before making the API request. -Author: Tiago Balabuch +Author: Tiago Balabuch #> -function Remove-FabricKQLDashboard { - [CmdletBinding()] +function Remove-FabricKQLDashboard +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -36,7 +37,8 @@ function Remove-FabricKQLDashboard { [string]$KQLDashboardId ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -46,26 +48,31 @@ function Remove-FabricKQLDashboard { $apiEndpointUrl = "{0}/workspaces/{1}/kqlDashboards/{2}" -f $FabricConfig.BaseUrl, $WorkspaceId, $KQLDashboardId Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug - # Step 3: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Delete ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -StatusCodeVariable "statusCode" + if ($PSCmdlet.ShouldProcess($apiEndpointUrl, "Remove KQLDashboard")) + { + # Step 3: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Delete ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -StatusCodeVariable "statusCode" + } # Step 4: Validate the response code - if ($statusCode -ne 200) { + if ($statusCode -ne 200) + { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error Write-Message -Message "Error: $($response.message)" -Level Error Write-Message "Error Code: $($response.errorCode)" -Level Error return $null } Write-Message -Message "KQLDashboard '$KQLDashboardId' deleted successfully from workspace '$WorkspaceId'." -Level Info - + } - catch { + catch + { # Step 5: Log and handle errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to delete KQLDashboard '$KQLDashboardId' from workspace '$WorkspaceId'. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/KQL Dashboard/Update-FabricKQLDashboard.ps1 b/source/Public/KQL Dashboard/Update-FabricKQLDashboard.ps1 similarity index 77% rename from FabricTools/public/KQL Dashboard/Update-FabricKQLDashboard.ps1 rename to source/Public/KQL Dashboard/Update-FabricKQLDashboard.ps1 index 874a614b..85c6013d 100644 --- a/FabricTools/public/KQL Dashboard/Update-FabricKQLDashboard.ps1 +++ b/source/Public/KQL Dashboard/Update-FabricKQLDashboard.ps1 @@ -1,4 +1,6 @@ -<# +function Update-FabricKQLDashboard +{ + <# .SYNOPSIS Updates the properties of a Fabric KQLDashboard. @@ -14,6 +16,9 @@ The new name for the KQLDashboard. .PARAMETER KQLDashboardDescription (Optional) The new description for the KQLDashboard. +.PARAMETER WorkspaceId +The unique identifier of the workspace where the KQLDashboard exists. + .EXAMPLE Update-FabricKQLDashboard -KQLDashboardId "KQLDashboard123" -KQLDashboardName "NewKQLDashboardName" @@ -28,17 +33,15 @@ Updates both the name and description of the KQLDashboard "KQLDashboard123". - Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. - Calls `Test-TokenExpired` to ensure token validity before making the API request. -Author: Tiago Balabuch - -#> +Author: Tiago Balabuch -function Update-FabricKQLDashboard { - [CmdletBinding()] + #> + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [string]$WorkspaceId, - + [string]$WorkspaceId, + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$KQLDashboardId, @@ -53,7 +56,8 @@ function Update-FabricKQLDashboard { [string]$KQLDashboardDescription ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -68,7 +72,8 @@ function Update-FabricKQLDashboard { displayName = $KQLDashboardName } - if ($KQLDashboardDescription) { + if ($KQLDashboardDescription) + { $body.description = $KQLDashboardDescription } @@ -76,19 +81,23 @@ function Update-FabricKQLDashboard { $bodyJson = $body | ConvertTo-Json Write-Message -Message "Request Body: $bodyJson" -Level Debug - # Step 4: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Patch ` - -Body $bodyJson ` - -ContentType "application/json" ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -StatusCodeVariable "statusCode" + if ($PSCmdlet.ShouldProcess($KQLDashboardId, "Update KQLDashboard")) + { + # Step 4: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Patch ` + -Body $bodyJson ` + -ContentType "application/json" ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -StatusCodeVariable "statusCode" + } # Step 5: Validate the response code - if ($statusCode -ne 200) { + if ($statusCode -ne 200) + { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error Write-Message -Message "Error: $($response.message)" -Level Error Write-Message "Error Code: $($response.errorCode)" -Level Error @@ -99,7 +108,8 @@ function Update-FabricKQLDashboard { Write-Message -Message "KQLDashboard '$KQLDashboardName' updated successfully!" -Level Info return $response } - catch { + catch + { # Step 7: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to update KQLDashboard. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/KQL Dashboard/Update-FabricKQLDashboardDefinition.ps1 b/source/Public/KQL Dashboard/Update-FabricKQLDashboardDefinition.ps1 similarity index 80% rename from FabricTools/public/KQL Dashboard/Update-FabricKQLDashboardDefinition.ps1 rename to source/Public/KQL Dashboard/Update-FabricKQLDashboardDefinition.ps1 index 14b423fc..3c8326f2 100644 --- a/FabricTools/public/KQL Dashboard/Update-FabricKQLDashboardDefinition.ps1 +++ b/source/Public/KQL Dashboard/Update-FabricKQLDashboardDefinition.ps1 @@ -3,7 +3,7 @@ Updates the definition of a KQLDashboard in a Microsoft Fabric workspace. .DESCRIPTION -This function allows updating the content or metadata of a KQLDashboard in a Microsoft Fabric workspace. +This function allows updating the content or metadata of a KQLDashboard in a Microsoft Fabric workspace. The KQLDashboard content can be provided as file paths, and metadata updates can optionally be enabled. .PARAMETER WorkspaceId @@ -25,7 +25,7 @@ Update-FabricKQLDashboardDefinition -WorkspaceId "12345" -KQLDashboardId "67890" Updates the content of the KQLDashboard with ID `67890` in the workspace `12345` using the specified KQLDashboard file. .EXAMPLE -Update-FabricKQLDashboardDefinition -WorkspaceId "12345" -KQLDashboardId "67890" -KQLDashboardPathDefinition "C:\KQLDashboards\KQLDashboard.ipynb" +Update-FabricKQLDashboardDefinition -WorkspaceId "12345" -KQLDashboardId "67890" -KQLDashboardPathDefinition "C:\KQLDashboards\KQLDashboard.ipynb" Updates both the content and metadata of the KQLDashboard with ID `67890` in the workspace `12345`. @@ -35,12 +35,13 @@ Updates both the content and metadata of the KQLDashboard with ID `67890` in the - The KQLDashboard content is encoded as Base64 before being sent to the Fabric API. - This function handles asynchronous operations and retrieves operation results if required. -Author: Tiago Balabuch +Author: Tiago Balabuch #> -function Update-FabricKQLDashboardDefinition { - [CmdletBinding()] +function Update-FabricKQLDashboardDefinition +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -53,13 +54,14 @@ function Update-FabricKQLDashboardDefinition { [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$KQLDashboardPathDefinition, - + [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$KQLDashboardPathPlatformDefinition ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -68,8 +70,9 @@ function Update-FabricKQLDashboardDefinition { # Step 2: Construct the API URL $apiEndpointUrl = "{0}/workspaces/{1}/KQLDashboards/{2}/updateDefinition" -f $FabricConfig.BaseUrl, $WorkspaceId, $KQLDashboardId - if($KQLDashboardPathPlatformDefinition){ - $apiEndpointUrl = "?updateMetadata=true" -f $apiEndpointUrl + if ($KQLDashboardPathPlatformDefinition) + { + $apiEndpointUrl = "?updateMetadata=true" -f $apiEndpointUrl } Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug @@ -78,13 +81,15 @@ function Update-FabricKQLDashboardDefinition { definition = @{ format = $null parts = @() - } + } } - - if ($KQLDashboardPathDefinition) { + + if ($KQLDashboardPathDefinition) + { $KQLDashboardEncodedContent = Convert-ToBase64 -filePath $KQLDashboardPathDefinition - - if (-not [string]::IsNullOrEmpty($KQLDashboardEncodedContent)) { + + if (-not [string]::IsNullOrEmpty($KQLDashboardEncodedContent)) + { # Add new part to the parts array $body.definition.parts += @{ path = "RealTimeDashboard.json" @@ -92,15 +97,18 @@ function Update-FabricKQLDashboardDefinition { payloadType = "InlineBase64" } } - else { + else + { Write-Message -Message "Invalid or empty content in KQLDashboard definition." -Level Error return $null } } - if ($KQLDashboardPathPlatformDefinition) { + if ($KQLDashboardPathPlatformDefinition) + { $KQLDashboardEncodedPlatformContent = Convert-ToBase64 -filePath $KQLDashboardPathPlatformDefinition - if (-not [string]::IsNullOrEmpty($KQLDashboardEncodedPlatformContent)) { + if (-not [string]::IsNullOrEmpty($KQLDashboardEncodedPlatformContent)) + { # Add new part to the parts array $body.definition.parts += @{ path = ".platform" @@ -108,7 +116,8 @@ function Update-FabricKQLDashboardDefinition { payloadType = "InlineBase64" } } - else { + else + { Write-Message -Message "Invalid or empty content in platform definition." -Level Error return $null } @@ -117,48 +126,58 @@ function Update-FabricKQLDashboardDefinition { $bodyJson = $body | ConvertTo-Json -Depth 10 Write-Message -Message "Request Body: $bodyJson" -Level Debug - # Step 4: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Post ` - -Body $bodyJson ` - -ContentType "application/json" ` - -ErrorAction Stop ` - -ResponseHeadersVariable "responseHeader" ` - -StatusCodeVariable "statusCode" - + if ($PSCmdlet.ShouldProcess($KQLDashboardId, "Update KQLDashboard")) + { + # Step 4: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Post ` + -Body $bodyJson ` + -ContentType "application/json" ` + -ErrorAction Stop ` + -ResponseHeadersVariable "responseHeader" ` + -StatusCodeVariable "statusCode" + } + # Step 5: Handle and log the response - switch ($statusCode) { - 200 { + switch ($statusCode) + { + 200 + { Write-Message -Message "Update definition for KQLDashboard '$KQLDashboardId' created successfully!" -Level Info return $response } - 202 { + 202 + { Write-Message -Message "Update definition for KQLDashboard '$KQLDashboardId' accepted. Operation in progress!" -Level Info [string]$operationId = $responseHeader["x-ms-operation-id"] $operationResult = Get-FabricLongRunningOperation -operationId $operationId # Handle operation result - if ($operationResult.status -eq "Succeeded") { + if ($operationResult.status -eq "Succeeded") + { Write-Message -Message "Operation Succeeded" -Level Debug - + $result = Get-FabricLongRunningOperationResult -operationId $operationId return $result.definition.parts } - else { + else + { Write-Message -Message "Operation Failed" -Level Debug return $operationResult.definition.parts - } - } - default { + } + } + default + { Write-Message -Message "Unexpected response code: $statusCode" -Level Error Write-Message -Message "Error details: $($response.message)" -Level Error throw "API request failed with status code $statusCode." } } } - catch { + catch + { # Step 6: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to update KQLDashboard. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/KQL Database/Get-FabricKQLDatabase.ps1 b/source/Public/KQL Database/Get-FabricKQLDatabase.ps1 similarity index 95% rename from FabricTools/public/KQL Database/Get-FabricKQLDatabase.ps1 rename to source/Public/KQL Database/Get-FabricKQLDatabase.ps1 index 94677fe6..144aa890 100644 --- a/FabricTools/public/KQL Database/Get-FabricKQLDatabase.ps1 +++ b/source/Public/KQL Database/Get-FabricKQLDatabase.ps1 @@ -1,4 +1,5 @@ -<# +function Get-FabricKQLDatabase { + <# .SYNOPSIS Retrieves an KQLDatabase or a list of KQLDatabases from a specified workspace in Microsoft Fabric. @@ -8,6 +9,9 @@ The `Get-FabricKQLDatabase` function sends a GET request to the Fabric API to re .PARAMETER WorkspaceId (Mandatory) The ID of the workspace to query KQLDatabases. +.PARAMETER KQLDatabaseId +(Optional) The ID of a specific KQLDatabase to retrieve. + .PARAMETER KQLDatabaseName (Optional) The name of the specific KQLDatabase to retrieve. @@ -25,10 +29,9 @@ Retrieves all KQLDatabases in workspace "12345". - Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. - Calls `Test-TokenExpired` to ensure token validity before making the API request. -Author: Tiago Balabuch +Author: Tiago Balabuch -#> -function Get-FabricKQLDatabase { + #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] @@ -59,11 +62,11 @@ function Get-FabricKQLDatabase { # Step 3: Initialize variables $continuationToken = $null $KQLDatabases = @() - + if (-not ([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.GetName().Name -eq "System.Web" })) { Add-Type -AssemblyName System.Web } - + # Step 4: Loop to retrieve all capacities with continuation token Write-Message -Message "Loop started to get continuation token" -Level Debug $baseApiEndpointUrl = "{0}/workspaces/{1}/kqlDatabases" -f $FabricConfig.BaseUrl, $WorkspaceId @@ -71,14 +74,14 @@ function Get-FabricKQLDatabase { do { # Step 5: Construct the API URL $apiEndpointUrl = $baseApiEndpointUrl - + if ($null -ne $continuationToken) { # URL-encode the continuation token $encodedToken = [System.Web.HttpUtility]::UrlEncode($continuationToken) $apiEndpointUrl = "{0}?continuationToken={1}" -f $apiEndpointUrl, $encodedToken } Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug - + # Step 6: Make the API request $response = Invoke-RestMethod ` -Headers $FabricConfig.FabricHeaders ` @@ -88,7 +91,7 @@ function Get-FabricKQLDatabase { -SkipHttpErrorCheck ` -ResponseHeadersVariable "responseHeader" ` -StatusCodeVariable "statusCode" - + # Step 7: Validate the response code if ($statusCode -ne 200) { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error @@ -97,38 +100,34 @@ function Get-FabricKQLDatabase { Write-Message "Error Code: $($response.errorCode)" -Level Error return $null } - + # Step 8: Add data to the list if ($null -ne $response) { Write-Message -Message "Adding data to the list" -Level Debug $KQLDatabases += $response.value - + # Update the continuation token if present if ($response.PSObject.Properties.Match("continuationToken")) { Write-Message -Message "Updating the continuation token" -Level Debug $continuationToken = $response.continuationToken Write-Message -Message "Continuation token: $continuationToken" -Level Debug - } - else { + } else { Write-Message -Message "Updating the continuation token to null" -Level Debug $continuationToken = $null } - } - else { + } else { Write-Message -Message "No data received from the API." -Level Warning break } } while ($null -ne $continuationToken) Write-Message -Message "Loop finished and all data added to the list" -Level Debug - + # Step 8: Filter results based on provided parameters $KQLDatabase = if ($KQLDatabaseId) { $KQLDatabases | Where-Object { $_.Id -eq $KQLDatabaseId } - } - elseif ($KQLDatabaseName) { + } elseif ($KQLDatabaseName) { $KQLDatabases | Where-Object { $_.DisplayName -eq $KQLDatabaseName } - } - else { + } else { # Return all KQLDatabases if no filter is provided Write-Message -Message "No filter provided. Returning all KQLDatabases." -Level Debug $KQLDatabases @@ -138,16 +137,14 @@ function Get-FabricKQLDatabase { if ($KQLDatabase) { Write-Message -Message "KQLDatabase found matching the specified criteria." -Level Debug return $KQLDatabase - } - else { + } else { Write-Message -Message "No KQLDatabase found matching the provided criteria." -Level Warning return $null } - } - catch { + } catch { # Step 10: Capture and log error details $errorDetails = $_.Exception.Message Write-Message -Message "Failed to retrieve KQLDatabase. Error: $errorDetails" -Level Error - } - -} + } + +} \ No newline at end of file diff --git a/FabricTools/public/KQL Database/Get-FabricKQLDatabaseDefinition.ps1 b/source/Public/KQL Database/Get-FabricKQLDatabaseDefinition.ps1 similarity index 97% rename from FabricTools/public/KQL Database/Get-FabricKQLDatabaseDefinition.ps1 rename to source/Public/KQL Database/Get-FabricKQLDatabaseDefinition.ps1 index a3d56115..95c016ea 100644 --- a/FabricTools/public/KQL Database/Get-FabricKQLDatabaseDefinition.ps1 +++ b/source/Public/KQL Database/Get-FabricKQLDatabaseDefinition.ps1 @@ -4,7 +4,7 @@ Retrieves the definition of a KQLDatabase from a specific workspace in Microsoft Fabric. .DESCRIPTION -This function fetches the KQLDatabase's content or metadata from a workspace. +This function fetches the KQLDatabase's content or metadata from a workspace. It supports retrieving KQLDatabase definitions in the Jupyter KQLDatabase (`ipynb`) format. Handles both synchronous and asynchronous operations, with detailed logging and error handling. @@ -92,24 +92,23 @@ function Get-FabricKQLDatabaseDefinition { Write-Message -Message "Location: '$location'" -Level Debug Write-Message -Message "Retry-After: '$retryAfter'" -Level Debug Write-Message -Message "Getting Long Running Operation status" -Level Debug - + $operationStatus = Get-FabricLongRunningOperation -operationId $operationId Write-Message -Message "Long Running Operation status: $operationStatus" -Level Debug # Handle operation result if ($operationStatus.status -eq "Succeeded") { Write-Message -Message "Operation Succeeded" -Level Debug Write-Message -Message "Getting Long Running Operation result" -Level Debug - + $operationResult = Get-FabricLongRunningOperationResult -operationId $operationId Write-Message -Message "Long Running Operation status: $operationResult" -Level Debug - + return $operationResult.definition.parts - } - else { + } else { Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Debug Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Error return $operationStatus - } + } } default { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error @@ -118,12 +117,11 @@ function Get-FabricKQLDatabaseDefinition { Write-Message "Error Code: $($response.errorCode)" -Level Error throw "API request failed with status code $statusCode." } - + } - } - catch { + } catch { # Step 9: Capture and log error details $errorDetails = $_.Exception.Message Write-Message -Message "Failed to retrieve KQLDatabase. Error: $errorDetails" -Level Error - } -} + } +} \ No newline at end of file diff --git a/FabricTools/public/KQL Database/New-FabricKQLDatabase.ps1 b/source/Public/KQL Database/New-FabricKQLDatabase.ps1 similarity index 78% rename from FabricTools/public/KQL Database/New-FabricKQLDatabase.ps1 rename to source/Public/KQL Database/New-FabricKQLDatabase.ps1 index 21098872..4b686982 100644 --- a/FabricTools/public/KQL Database/New-FabricKQLDatabase.ps1 +++ b/source/Public/KQL Database/New-FabricKQLDatabase.ps1 @@ -1,15 +1,35 @@ -<# +function New-FabricKQLDatabase +{ + <# .SYNOPSIS Creates a new KQLDatabase in a specified Microsoft Fabric workspace. .DESCRIPTION -This function sends a POST request to the Microsoft Fabric API to create a new KQLDatabase -in the specified workspace. It supports optional parameters for KQLDatabase description +This function sends a POST request to the Microsoft Fabric API to create a new KQLDatabase +in the specified workspace. It supports optional parameters for KQLDatabase description and path definitions for the KQLDatabase content. .PARAMETER WorkspaceId The unique identifier of the workspace where the KQLDatabase will be created. +.PARAMETER KQLInvitationToken +An optional invitation token for the KQLDatabase. + +.PARAMETER KQLSourceClusterUri +An optional source cluster URI for the KQLDatabase. + +.PARAMETER KQLDatabasePathSchemaDefinition +the path to the KQLDatabase schema definition file (e.g., .kql file) to upload. + +.PARAMETER KQLSourceDatabaseName +An optional source database name for the KQLDatabase. + +.PARAMETER parentEventhouseId +The ID of the parent Eventhouse item for the KQLDatabase. This is mandatory for ReadWrite type databases. + +.PARAMETER KQLDatabaseType +The type of KQLDatabase to create. Valid values are "ReadWrite" and "Shortcut". + .PARAMETER KQLDatabaseName The name of the KQLDatabase to be created. @@ -33,12 +53,10 @@ An optional path to the platform-specific definition (e.g., .platform file) to u - CreationPayload is evaluate only if Definition file is not provided. - invitationToken has priority over all other payload fields. -Author: Tiago Balabuch - -#> +Author: Tiago Balabuch -function New-FabricKQLDatabase { - [CmdletBinding()] + #> + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -77,7 +95,7 @@ function New-FabricKQLDatabase { [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$KQLDatabasePathDefinition, - + [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$KQLDatabasePathPlatformDefinition, @@ -87,7 +105,8 @@ function New-FabricKQLDatabase { [string]$KQLDatabasePathSchemaDefinition ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -102,19 +121,22 @@ function New-FabricKQLDatabase { displayName = $KQLDatabaseName } - if ($KQLDatabaseDescription) { + if ($KQLDatabaseDescription) + { $body.description = $KQLDatabaseDescription } - if ($KQLDatabasePathDefinition) { + if ($KQLDatabasePathDefinition) + { $KQLDatabaseEncodedContent = Convert-ToBase64 -filePath $KQLDatabasePathDefinition $body.definition = @{ parts = @() } - - if (-not [string]::IsNullOrEmpty($KQLDatabaseEncodedContent)) { - + + if (-not [string]::IsNullOrEmpty($KQLDatabaseEncodedContent)) + { + # Add new part to the parts array $body.definition.parts += @{ @@ -123,15 +145,18 @@ function New-FabricKQLDatabase { payloadType = "InlineBase64" } } - else { + else + { Write-Message -Message "Invalid or empty content in KQLDatabase definition." -Level Error return $null } - - if ($KQLDatabasePathPlatformDefinition) { + + if ($KQLDatabasePathPlatformDefinition) + { $KQLDatabaseEncodedPlatformContent = Convert-ToBase64 -filePath $KQLDatabasePathPlatformDefinition - if (-not [string]::IsNullOrEmpty($KQLDatabaseEncodedPlatformContent)) { + if (-not [string]::IsNullOrEmpty($KQLDatabaseEncodedPlatformContent)) + { # Add new part to the parts array $body.definition.parts += @{ @@ -140,16 +165,19 @@ function New-FabricKQLDatabase { payloadType = "InlineBase64" } } - else { + else + { Write-Message -Message "Invalid or empty content in platform definition." -Level Error return $null } - + } - if ($KQLDatabasePathSchemaDefinition) { + if ($KQLDatabasePathSchemaDefinition) + { $KQLDatabaseEncodedSchemaContent = Convert-ToBase64 -filePath $KQLDatabasePathSchemaDefinition - if (-not [string]::IsNullOrEmpty($KQLDatabaseEncodedSchemaContent)) { + if (-not [string]::IsNullOrEmpty($KQLDatabaseEncodedSchemaContent)) + { # Add new part to the parts array $body.definition.parts += @{ @@ -158,43 +186,53 @@ function New-FabricKQLDatabase { payloadType = "InlineBase64" } } - else { + else + { Write-Message -Message "Invalid or empty content in schema definition." -Level Error return $null } } } - else { - if ($KQLDatabaseType -eq "Shortcut") { - if (-not $parentEventhouseId) { + else + { + if ($KQLDatabaseType -eq "Shortcut") + { + if (-not $parentEventhouseId) + { Write-Message -Message "Error: 'parentEventhouseId' is required for Shortcut type." -Level Error return $null } - if (-not ($KQLInvitationToken -or $KQLSourceClusterUri -or $KQLSourceDatabaseName)) { + if (-not ($KQLInvitationToken -or $KQLSourceClusterUri -or $KQLSourceDatabaseName)) + { Write-Message -Message "Error: Provide either 'KQLInvitationToken', 'KQLSourceClusterUri', or 'KQLSourceDatabaseName'." -Level Error return $null } - if ($KQLInvitationToken) { + if ($KQLInvitationToken) + { Write-Message -Message "Info: 'KQLInvitationToken' is provided." -Level Warning - if ($KQLSourceClusterUri) { + if ($KQLSourceClusterUri) + { Write-Message -Message "Warning: 'KQLSourceClusterUri' is ignored when 'KQLInvitationToken' is provided." -Level Warning #$KQLSourceClusterUri = $null } - if ($KQLSourceDatabaseName) { + if ($KQLSourceDatabaseName) + { Write-Message -Message "Warning: 'KQLSourceDatabaseName' is ignored when 'KQLInvitationToken' is provided." -Level Warning #$KQLSourceDatabaseName = $null } } - if ($KQLSourceClusterUri -and -not $KQLSourceDatabaseName) { + if ($KQLSourceClusterUri -and -not $KQLSourceDatabaseName) + { Write-Message -Message "Error: 'KQLSourceDatabaseName' is required when 'KQLSourceClusterUri' is provided." -Level Error return $null } } # Validate ReadWrite type database - if ($KQLDatabaseType -eq "ReadWrite" -and -not $parentEventhouseId) { + if ($KQLDatabaseType -eq "ReadWrite" -and -not $parentEventhouseId) + { Write-Message -Message "Error: 'parentEventhouseId' is required for ReadWrite type." -Level Error return $null } @@ -204,15 +242,19 @@ function New-FabricKQLDatabase { parentEventhouseItemId = $parentEventhouseId } - if ($KQLDatabaseType -eq "Shortcut") { - if ($KQLInvitationToken) { + if ($KQLDatabaseType -eq "Shortcut") + { + if ($KQLInvitationToken) + { $body.creationPayload.invitationToken = $KQLInvitationToken } - if ($KQLSourceClusterUri -and -not $KQLInvitationToken) { + if ($KQLSourceClusterUri -and -not $KQLInvitationToken) + { $body.creationPayload.sourceClusterUri = $KQLSourceClusterUri } - if ($KQLSourceDatabaseName -and -not $KQLInvitationToken) { + if ($KQLSourceDatabaseName -and -not $KQLInvitationToken) + { $body.creationPayload.sourceDatabaseName = $KQLSourceDatabaseName } } @@ -222,28 +264,33 @@ function New-FabricKQLDatabase { $bodyJson = $body | ConvertTo-Json -Depth 10 Write-Message -Message "Request Body: $bodyJson" -Level Debug - - # Step 4: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Post ` - -Body $bodyJson ` - -ContentType "application/json" ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -ResponseHeadersVariable "responseHeader" ` - -StatusCodeVariable "statusCode" + if ($PSCmdlet.ShouldProcess($KQLDatabaseName, "Create KQLDatabase")) + { + # Step 4: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Post ` + -Body $bodyJson ` + -ContentType "application/json" ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -ResponseHeadersVariable "responseHeader" ` + -StatusCodeVariable "statusCode" + } # Step 5: Handle and log the response - switch ($statusCode) { - 201 { + switch ($statusCode) + { + 201 + { Write-Message -Message "KQLDatabase '$KQLDatabaseName' created successfully!" -Level Info return $response } - 202 { + 202 + { Write-Message -Message "KQLDatabase '$KQLDatabaseName' creation accepted. Provisioning in progress!" -Level Info - + [string]$operationId = $responseHeader["x-ms-operation-id"] [string]$location = $responseHeader["Location"] [string]$retryAfter = $responseHeader["Retry-After"] @@ -252,26 +299,29 @@ function New-FabricKQLDatabase { Write-Message -Message "Location: '$location'" -Level Debug Write-Message -Message "Retry-After: '$retryAfter'" -Level Debug Write-Message -Message "Getting Long Running Operation status" -Level Debug - + $operationStatus = Get-FabricLongRunningOperation -operationId $operationId Write-Message -Message "Long Running Operation status: $operationStatus" -Level Debug # Handle operation result - if ($operationStatus.status -eq "Succeeded") { + if ($operationStatus.status -eq "Succeeded") + { Write-Message -Message "Operation Succeeded" -Level Debug Write-Message -Message "Getting Long Running Operation result" -Level Debug - + $operationResult = Get-FabricLongRunningOperationResult -operationId $operationId Write-Message -Message "Long Running Operation result: $operationResult" -Level Debug - + return $operationResult } - else { + else + { Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Debug Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Error return $operationStatus - } + } } - default { + default + { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error Write-Message -Message "Error: $($response.message)" -Level Error Write-Message -Message "Error Details: $($response.moreDetails)" -Level Error @@ -280,7 +330,8 @@ function New-FabricKQLDatabase { } } } - catch { + catch + { # Step 6: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to create KQLDatabase. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/KQL Database/Remove-FabricKQLDatabase.ps1 b/source/Public/KQL Database/Remove-FabricKQLDatabase.ps1 similarity index 76% rename from FabricTools/public/KQL Database/Remove-FabricKQLDatabase.ps1 rename to source/Public/KQL Database/Remove-FabricKQLDatabase.ps1 index 2b5d69f2..568e7f48 100644 --- a/FabricTools/public/KQL Database/Remove-FabricKQLDatabase.ps1 +++ b/source/Public/KQL Database/Remove-FabricKQLDatabase.ps1 @@ -20,12 +20,13 @@ Deletes the KQLDatabase with ID "67890" from workspace "12345". - Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. - Validates token expiration before making the API request. -Author: Tiago Balabuch +Author: Tiago Balabuch #> -function Remove-FabricKQLDatabase { - [CmdletBinding()] +function Remove-FabricKQLDatabase +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -36,7 +37,8 @@ function Remove-FabricKQLDatabase { [string]$KQLDatabaseId ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -45,18 +47,22 @@ function Remove-FabricKQLDatabase { # Step 2: Construct the API URL $apiEndpointUrl = "{0}/workspaces/{1}/kqlDatabases/{2}" -f $FabricConfig.BaseUrl, $WorkspaceId, $KQLDatabaseId Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug - - # Step 3: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Delete ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -StatusCodeVariable "statusCode" + if ($PSCmdlet.ShouldProcess($apiEndpointUrl, "Remove KQLDatabase")) + { + # Step 3: Check if the API endpoint is valid + # Step 3: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Delete ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -StatusCodeVariable "statusCode" + } # Step 4: Validate the response code - if ($statusCode -ne 200) { + if ($statusCode -ne 200) + { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error Write-Message -Message "Error: $($response.message)" -Level Error Write-Message -Message "Error Details: $($response.moreDetails)" -Level Error @@ -64,9 +70,10 @@ function Remove-FabricKQLDatabase { return $null } Write-Message -Message "KQLDatabase '$KQLDatabaseId' deleted successfully from workspace '$WorkspaceId'." -Level Info - + } - catch { + catch + { # Step 5: Log and handle errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to delete KQLDatabase '$KQLDatabaseId' from workspace '$WorkspaceId'. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/KQL Database/Update-FabricKQLDatabase.ps1 b/source/Public/KQL Database/Update-FabricKQLDatabase.ps1 similarity index 78% rename from FabricTools/public/KQL Database/Update-FabricKQLDatabase.ps1 rename to source/Public/KQL Database/Update-FabricKQLDatabase.ps1 index 4c15f3a2..fe6b1548 100644 --- a/FabricTools/public/KQL Database/Update-FabricKQLDatabase.ps1 +++ b/source/Public/KQL Database/Update-FabricKQLDatabase.ps1 @@ -5,6 +5,9 @@ Updates the properties of a Fabric KQLDatabase. .DESCRIPTION The `Update-FabricKQLDatabase` function updates the name and/or description of a specified Fabric KQLDatabase by making a PATCH request to the API. +.PARAMETER WorkspaceId +The unique identifier of the workspace where the KQLDatabase resides. + .PARAMETER KQLDatabaseId The unique identifier of the KQLDatabase to be updated. @@ -28,17 +31,18 @@ Updates both the name and description of the KQLDatabase "KQLDatabase123". - Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. - Calls `Test-TokenExpired` to ensure token validity before making the API request. -Author: Tiago Balabuch +Author: Tiago Balabuch #> -function Update-FabricKQLDatabase { - [CmdletBinding()] +function Update-FabricKQLDatabase +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [string]$WorkspaceId, - + [string]$WorkspaceId, + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$KQLDatabaseId, @@ -53,7 +57,8 @@ function Update-FabricKQLDatabase { [string]$KQLDatabaseDescription ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -68,27 +73,31 @@ function Update-FabricKQLDatabase { displayName = $KQLDatabaseName } - if ($KQLDatabaseDescription) { + if ($KQLDatabaseDescription) + { $body.description = $KQLDatabaseDescription } # Convert the body to JSON $bodyJson = $body | ConvertTo-Json Write-Message -Message "Request Body: $bodyJson" -Level Debug - - # Step 4: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Patch ` - -Body $bodyJson ` - -ContentType "application/json" ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -StatusCodeVariable "statusCode" + if ($PSCmdlet.ShouldProcess($KQLDatabaseId, "Update KQLDatabase")) + { + # Step 4: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Patch ` + -Body $bodyJson ` + -ContentType "application/json" ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -StatusCodeVariable "statusCode" + } # Step 5: Validate the response code - if ($statusCode -ne 200) { + if ($statusCode -ne 200) + { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error Write-Message -Message "Error: $($response.message)" -Level Error Write-Message -Message "Error Details: $($response.moreDetails)" -Level Error @@ -100,7 +109,8 @@ function Update-FabricKQLDatabase { Write-Message -Message "KQLDatabase '$KQLDatabaseName' updated successfully!" -Level Info return $response } - catch { + catch + { # Step 7: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to update KQLDatabase. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/KQL Database/Update-FabricKQLDatabaseDefinition.ps1 b/source/Public/KQL Database/Update-FabricKQLDatabaseDefinition.ps1 similarity index 82% rename from FabricTools/public/KQL Database/Update-FabricKQLDatabaseDefinition.ps1 rename to source/Public/KQL Database/Update-FabricKQLDatabaseDefinition.ps1 index 35df15fe..4570ae4d 100644 --- a/FabricTools/public/KQL Database/Update-FabricKQLDatabaseDefinition.ps1 +++ b/source/Public/KQL Database/Update-FabricKQLDatabaseDefinition.ps1 @@ -3,7 +3,7 @@ Updates the definition of a KQLDatabase in a Microsoft Fabric workspace. .DESCRIPTION -This function allows updating the content or metadata of a KQLDatabase in a Microsoft Fabric workspace. +This function allows updating the content or metadata of a KQLDatabase in a Microsoft Fabric workspace. The KQLDatabase content can be provided as file paths, and metadata updates can optionally be enabled. .PARAMETER WorkspaceId @@ -19,9 +19,12 @@ The KQLDatabase content can be provided as file paths, and metadata updates can (Optional) The file path to the KQLDatabase's platform-specific definition file. The content will be encoded as Base64 and sent in the request. .PARAMETER UpdateMetadata -(Optional)A boolean flag indicating whether to update the KQLDatabase's metadata. +(Optional)A boolean flag indicating whether to update the KQLDatabase's metadata. Default: `$false`. +.PARAMETER KQLDatabasePathSchemaDefinition +(Optional) The file path to the KQLDatabase's schema definition file. The content will be encoded as Base64 and sent in the request. + .EXAMPLE Update-FabricKQLDatabaseDefinition -WorkspaceId "12345" -KQLDatabaseId "67890" -KQLDatabasePathDefinition "C:\KQLDatabases\KQLDatabase.ipynb" @@ -38,12 +41,13 @@ Updates both the content and metadata of the KQLDatabase with ID `67890` in the - The KQLDatabase content is encoded as Base64 before being sent to the Fabric API. - This function handles asynchronous operations and retrieves operation results if required. -Author: Tiago Balabuch +Author: Tiago Balabuch #> -function Update-FabricKQLDatabaseDefinition { - [CmdletBinding()] +function Update-FabricKQLDatabaseDefinition +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -56,7 +60,7 @@ function Update-FabricKQLDatabaseDefinition { [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$KQLDatabasePathDefinition, - + [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$KQLDatabasePathPlatformDefinition, @@ -65,7 +69,8 @@ function Update-FabricKQLDatabaseDefinition { [ValidateNotNullOrEmpty()] [string]$KQLDatabasePathSchemaDefinition ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -74,22 +79,25 @@ function Update-FabricKQLDatabaseDefinition { # Step 2: Construct the API URL $apiEndpointUrl = "{0}/workspaces/{1}/kqlDatabases/{2}/updateDefinition" -f $FabricConfig.BaseUrl, $WorkspaceId, $KQLDatabaseId - if($KQLDatabasePathPlatformDefinition){ - $apiEndpointUrl = "?updateMetadata=true" -f $apiEndpointUrl + if ($KQLDatabasePathPlatformDefinition) + { + $apiEndpointUrl = "?updateMetadata=true" -f $apiEndpointUrl } Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug # Step 3: Construct the request body $body = @{ definition = @{ - parts = @() - } + parts = @() + } } - - if ($KQLDatabasePathDefinition) { + + if ($KQLDatabasePathDefinition) + { $KQLDatabaseEncodedContent = Convert-ToBase64 -filePath $KQLDatabasePathDefinition - - if (-not [string]::IsNullOrEmpty($KQLDatabaseEncodedContent)) { + + if (-not [string]::IsNullOrEmpty($KQLDatabaseEncodedContent)) + { # Add new part to the parts array $body.definition.parts += @{ path = "DatabaseProperties.json" @@ -97,15 +105,18 @@ function Update-FabricKQLDatabaseDefinition { payloadType = "InlineBase64" } } - else { + else + { Write-Message -Message "Invalid or empty content in KQLDatabase definition." -Level Error return $null } } - if ($KQLDatabasePathPlatformDefinition) { + if ($KQLDatabasePathPlatformDefinition) + { $KQLDatabaseEncodedPlatformContent = Convert-ToBase64 -filePath $KQLDatabasePathPlatformDefinition - if (-not [string]::IsNullOrEmpty($KQLDatabaseEncodedPlatformContent)) { + if (-not [string]::IsNullOrEmpty($KQLDatabaseEncodedPlatformContent)) + { # Add new part to the parts array $body.definition.parts += @{ path = ".platform" @@ -113,16 +124,19 @@ function Update-FabricKQLDatabaseDefinition { payloadType = "InlineBase64" } } - else { + else + { Write-Message -Message "Invalid or empty content in platform definition." -Level Error return $null } } - if ($KQLDatabasePathSchemaDefinition) { + if ($KQLDatabasePathSchemaDefinition) + { $KQLDatabaseEncodedSchemaContent = Convert-ToBase64 -filePath $KQLDatabasePathSchemaDefinition - if (-not [string]::IsNullOrEmpty($KQLDatabaseEncodedSchemaContent)) { + if (-not [string]::IsNullOrEmpty($KQLDatabaseEncodedSchemaContent)) + { # Add new part to the parts array $body.definition.parts += @{ @@ -131,7 +145,8 @@ function Update-FabricKQLDatabaseDefinition { payloadType = "InlineBase64" } } - else { + else + { Write-Message -Message "Invalid or empty content in schema definition." -Level Error return $null } @@ -140,52 +155,61 @@ function Update-FabricKQLDatabaseDefinition { $bodyJson = $body | ConvertTo-Json -Depth 10 Write-Message -Message "Request Body: $bodyJson" -Level Debug - # Step 4: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Post ` - -Body $bodyJson ` - -ContentType "application/json" ` - -ErrorAction Stop ` - -ResponseHeadersVariable "responseHeader" ` - -StatusCodeVariable "statusCode" - + if ($PSCmdlet.ShouldProcess($KQLDatabaseId, "Update KQLDatabase")) + { + # Step 4: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Post ` + -Body $bodyJson ` + -ContentType "application/json" ` + -ErrorAction Stop ` + -ResponseHeadersVariable "responseHeader" ` + -StatusCodeVariable "statusCode" + } + # Step 5: Handle and log the response - switch ($statusCode) { - 200 { + switch ($statusCode) + { + 200 + { Write-Message -Message "Update definition for KQLDatabase '$KQLDatabaseId' created successfully!" -Level Info return $response } - 202 { + 202 + { Write-Message -Message "Update definition for KQLDatabase '$KQLDatabaseId' accepted. Operation in progress!" -Level Info [string]$operationId = $responseHeader["x-ms-operation-id"] [string]$location = $responseHeader["Location"] - [string]$retryAfter = $responseHeader["Retry-After"] + [string]$retryAfter = $responseHeader["Retry-After"] Write-Message -Message "Operation ID: '$operationId'" -Level Debug Write-Message -Message "Location: '$location'" -Level Debug Write-Message -Message "Retry-After: '$retryAfter'" -Level Debug Write-Message -Message "Getting Long Running Operation status" -Level Debug - + $operationStatus = Get-FabricLongRunningOperation -operationId $operationId -location $location Write-Message -Message "Long Running Operation status: $operationStatus" -Level Debug # Handle operation result - if ($operationStatus.status -eq "Succeeded") { + if ($operationStatus.status -eq "Succeeded") + { Write-Message -Message "Operation Succeeded" -Level Debug Write-Message -Message "Getting Long Running Operation result" -Level Debug Write-Message -Message "Operation completed successfully." -Level Info $operationResult = Get-FabricLongRunningOperationResult -operationId $operationId Write-Message -Message "Long Running Operation status: $operationResult" -Level Debug return $operationResult - } - else { + } + else + { Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Debug Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Error return $operationStatus - } - } - default { + } + } + default + { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error Write-Message -Message "Error: $($response.message)" -Level Error Write-Message -Message "Error Details: $($response.moreDetails)" -Level Error @@ -194,7 +218,8 @@ function Update-FabricKQLDatabaseDefinition { } } } - catch { + catch + { # Step 6: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to update KQLDatabase. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/KQL Queryset/Get-FabricKQLQueryset.ps1 b/source/Public/KQL Queryset/Get-FabricKQLQueryset.ps1 similarity index 95% rename from FabricTools/public/KQL Queryset/Get-FabricKQLQueryset.ps1 rename to source/Public/KQL Queryset/Get-FabricKQLQueryset.ps1 index b1c06d8b..49591b5f 100644 --- a/FabricTools/public/KQL Queryset/Get-FabricKQLQueryset.ps1 +++ b/source/Public/KQL Queryset/Get-FabricKQLQueryset.ps1 @@ -1,4 +1,5 @@ -<# +function Get-FabricKQLQueryset { + <# .SYNOPSIS Retrieves an KQLQueryset or a list of KQLQuerysets from a specified workspace in Microsoft Fabric. @@ -8,6 +9,9 @@ The `Get-FabricKQLQueryset` function sends a GET request to the Fabric API to re .PARAMETER WorkspaceId (Mandatory) The ID of the workspace to query KQLQuerysets. +.PARAMETER KQLQuerysetId +(Optional) The ID of a specific KQLQueryset to retrieve. + .PARAMETER KQLQuerysetName (Optional) The name of the specific KQLQueryset to retrieve. @@ -25,11 +29,9 @@ Retrieves all KQLQuerysets in workspace "12345". - Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. - Calls `Test-TokenExpired` to ensure token validity before making the API request. -Author: Tiago Balabuch - -#> +Author: Tiago Balabuch -function Get-FabricKQLQueryset { + #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] @@ -57,7 +59,7 @@ function Get-FabricKQLQueryset { Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired Write-Message -Message "Token validation completed." -Level Debug - + # Step 3: Initialize variables $continuationToken = $null $KQLQuerysets = @() @@ -65,7 +67,7 @@ function Get-FabricKQLQueryset { if (-not ([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.GetName().Name -eq "System.Web" })) { Add-Type -AssemblyName System.Web } - + # Step 4: Loop to retrieve all capacities with continuation token Write-Message -Message "Loop started to get continuation token" -Level Debug $baseApiEndpointUrl = "{0}/workspaces/{1}/kqlQuerysets" -f $FabricConfig.BaseUrl, $WorkspaceId @@ -79,7 +81,7 @@ function Get-FabricKQLQueryset { $apiEndpointUrl = "{0}?continuationToken={1}" -f $apiEndpointUrl, $encodedToken } Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug - + # Step 6: Make the API request $response = Invoke-RestMethod ` -Headers $FabricConfig.FabricHeaders ` @@ -89,7 +91,7 @@ function Get-FabricKQLQueryset { -SkipHttpErrorCheck ` -ResponseHeadersVariable "responseHeader" ` -StatusCodeVariable "statusCode" - + # Step 7: Validate the response code if ($statusCode -ne 200) { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error @@ -98,38 +100,34 @@ function Get-FabricKQLQueryset { Write-Message "Error Code: $($response.errorCode)" -Level Error return $null } - + # Step 8: Add data to the list if ($null -ne $response) { Write-Message -Message "Adding data to the list" -Level Debug $KQLQuerysets += $response.value - + # Update the continuation token if present if ($response.PSObject.Properties.Match("continuationToken")) { Write-Message -Message "Updating the continuation token" -Level Debug $continuationToken = $response.continuationToken Write-Message -Message "Continuation token: $continuationToken" -Level Debug - } - else { + } else { Write-Message -Message "Updating the continuation token to null" -Level Debug $continuationToken = $null } - } - else { + } else { Write-Message -Message "No data received from the API." -Level Warning break } } while ($null -ne $continuationToken) Write-Message -Message "Loop finished and all data added to the list" -Level Debug - + # Step 8: Filter results based on provided parameters $KQLQueryset = if ($KQLQuerysetId) { $KQLQuerysets | Where-Object { $_.Id -eq $KQLQuerysetId } - } - elseif ($KQLQuerysetName) { + } elseif ($KQLQuerysetName) { $KQLQuerysets | Where-Object { $_.DisplayName -eq $KQLQuerysetName } - } - else { + } else { # Return all KQLQuerysets if no filter is provided Write-Message -Message "No filter provided. Returning all KQLQuerysets." -Level Debug $KQLQuerysets @@ -139,16 +137,14 @@ function Get-FabricKQLQueryset { if ($KQLQueryset) { Write-Message -Message "KQLQueryset found matching the specified criteria." -Level Debug return $KQLQueryset - } - else { + } else { Write-Message -Message "No KQLQueryset found matching the provided criteria." -Level Warning return $null } - } - catch { + } catch { # Step 10: Capture and log error details $errorDetails = $_.Exception.Message Write-Message -Message "Failed to retrieve KQLQueryset. Error: $errorDetails" -Level Error - } - -} + } + +} \ No newline at end of file diff --git a/FabricTools/public/KQL Queryset/Get-FabricKQLQuerysetDefinition.ps1 b/source/Public/KQL Queryset/Get-FabricKQLQuerysetDefinition.ps1 similarity index 97% rename from FabricTools/public/KQL Queryset/Get-FabricKQLQuerysetDefinition.ps1 rename to source/Public/KQL Queryset/Get-FabricKQLQuerysetDefinition.ps1 index 79e88196..d1340167 100644 --- a/FabricTools/public/KQL Queryset/Get-FabricKQLQuerysetDefinition.ps1 +++ b/source/Public/KQL Queryset/Get-FabricKQLQuerysetDefinition.ps1 @@ -4,7 +4,7 @@ Retrieves the definition of a KQLQueryset from a specific workspace in Microsoft Fabric. .DESCRIPTION -This function fetches the KQLQueryset's content or metadata from a workspace. +This function fetches the KQLQueryset's content or metadata from a workspace. Handles both synchronous and asynchronous operations, with detailed logging and error handling. .PARAMETER WorkspaceId @@ -84,37 +84,35 @@ function Get-FabricKQLQuerysetDefinition { [string]$operationId = $responseHeader["x-ms-operation-id"] Write-Message -Message "Operation ID: '$operationId'" -Level Debug Write-Message -Message "Getting Long Running Operation status" -Level Debug - + $operationStatus = Get-FabricLongRunningOperation -operationId $operationId Write-Message -Message "Long Running Operation status: $operationStatus" -Level Debug # Handle operation result if ($operationStatus.status -eq "Succeeded") { Write-Message -Message "Operation Succeeded" -Level Debug Write-Message -Message "Getting Long Running Operation result" -Level Debug - + $operationResult = Get-FabricLongRunningOperationResult -operationId $operationId Write-Message -Message "Long Running Operation status: $operationResult" -Level Debug - + return $operationResult.definition.parts - } - else { + } else { Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Debug Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Error return $operationStatus - } + } } default { Write-Message -Message "Unexpected response code: $statusCode" -Level Error Write-Message -Message "Error details: $($response.message)" -Level Error throw "API request failed with status code $statusCode." } - + } - } - catch { + } catch { # Step 9: Capture and log error details $errorDetails = $_.Exception.Message Write-Message -Message "Failed to retrieve KQLQueryset. Error: $errorDetails" -Level Error - } - -} + } + +} \ No newline at end of file diff --git a/source/Public/KQL Queryset/Invoke-FabricKQLCommand.ps1 b/source/Public/KQL Queryset/Invoke-FabricKQLCommand.ps1 new file mode 100644 index 00000000..00ab9f5c --- /dev/null +++ b/source/Public/KQL Queryset/Invoke-FabricKQLCommand.ps1 @@ -0,0 +1,211 @@ +function Invoke-FabricKQLCommand { + <# +.SYNOPSIS + Executes a KQL command in a Kusto Database. + +.DESCRIPTION + Executes a KQL command in a Kusto Database. The KQL command is executed in the Kusto Database that is specified by the KQLDatabaseName or KQLDatabaseId parameter. The KQL command is executed in the context of the Fabric Real-Time Intelligence session that is established by the Connect-RTISession cmdlet. The cmdlet distinguishes between management commands and query commands. Management commands are executed in the management API, while query commands are executed in the query API. The distinction is made by checking if the KQL command starts with a dot. If it does, it is a management command else it is a query command. If the KQL command is a management command, it is crucial to have the execute database script <| in the beginning, otherwise the Kusto API will not execute the script. This cmdlet will automatically add the .execute database script <| in the beginning of the KQL command if it is a management command and if it is not already present. If the KQL Command is a query command, the result is returned as an array of PowerShell objects by default. If the parameter -ReturnRawResult is used, the raw result of the KQL query is returned which is a JSON object. + + +.PARAMETER WorkspaceId + Id of the Fabric Workspace for which the KQL command should be executed. The value for WorkspaceId is a GUID. + An example of a GUID is '12345678-1234-1234-1234-123456789012'. + +.PARAMETER KQLDatabaseName + The name of the KQLDatabase in which the KQL command should be executed. This parameter cannot be used together with KQLDatabaseId. + +.PARAMETER KQLDatabaseId + The Id of the KQLDatabase in which the KQL command should be executed. This parameter cannot be used together with KQLDatabaseName. + The value for KQLDatabaseId is a GUID. An example of a GUID is '12345678-1234-1234-1234-123456789012'. + +.PARAMETER KQLCommand + The KQL command that should be executed in the Kusto Database. + The KQL command is a string. An example of a string is '.create table MyTable (MyColumn: string)'. + +.PARAMETER ReturnRawResult + When this switch is used, the raw result of the KQL command is returned. By default, the result is returned as + a PowerShell object. + +.EXAMPLE + Invoke-FabricKQLCommand -WorkspaceId '12345678-1234-1234-1234-123456789012' -KQLDatabaseName 'MyKQLDatabase'-KQLCommand '.create table MyTable (MyColumn: string) + + This example will create a table named 'MyTable' with a column named 'MyColumn' in + the KQLDatabase 'MyKQLDatabase'. + +.EXAMPLE + Invoke-FabricKQLCommand ` + -WorkspaceId '2c4ccbb5-9b13-4495-9ab3-ba41152733d9' ` + -KQLDatabaseName 'MyEventhouse2' ` + -KQLCommand 'productcategory + | take 100' + + This example will Execute the Query 'productcategory | take 100' in the KQLDatabase 'MyEventhouse2' + and it will return the result as an array of PowerShell objects. + +.EXAMPLE + Invoke-FabricKQLCommand ` + -WorkspaceId '2c4ccbb5-9b13-4495-9ab3-ba41152733d9' ` + -KQLDatabaseName 'MyEventhouse2' ` + -ReturnRawResult ` + -KQLCommand 'productcategory + | take 100' + + This example will Execute the Query 'productcategory | take 100' in the KQLDatabase 'MyEventhouse2' + and it will return the result as the raw result of the KQL command, which is a JSON object. + +.NOTES + Revision History: + + 2024-12-22 - FGE: Added Verbose Output + 2024-12-27 - FGE: Major Update to support KQL Queries and Management Commands + + #> + + [CmdletBinding()] + param ( + + [Parameter(Mandatory = $true)] + [string]$WorkspaceId, + + [string]$KQLDatabaseName, + + [string]$KQLDatabaseId, + + [Parameter(Mandatory = $true)] + [string]$KQLCommand, + + [switch]$ReturnRawResult + ) + + begin { + + Confirm-FabricAuthToken | Out-Null + + Write-Verbose "Check if KQLDatabaseName and KQLDatabaseId are used together" + if ($PSBoundParameters.ContainsKey("KQLDatabaseName") -and $PSBoundParameters.ContainsKey("KQLDatabaseId")) { + throw "Parameters KQLDatabaseName and KQLDatabaseId cannot be used together" + } + + Write-Verbose "Get Kusto Database" + if ($PSBoundParameters.ContainsKey("KQLDatabaseName")) { + Write-Verbose "Getting Kusto Database by Name: $KQLDatabaseName" + $kustDB = Get-FabricKQLDatabase ` + -WorkspaceId $WorkspaceId ` + -KQLDatabaseName $KQLDatabaseName + } + + if ($PSBoundParameters.ContainsKey("KQLDatabaseId")) { + Write-Verbose "Getting Kusto Database by Id: $KQLDatabaseId" + $kustDB = Get-FabricKQLDatabase ` + -WorkspaceId $WorkspaceId ` + -KQLDatabaseId $KQLDatabaseId + } + + Write-Verbose "Check if Kusto Database was found" + if ($null -eq $kustDB) { + throw "Kusto Database not found" + } + + Write-Verbose "Generate the Management API URL" + $mgmtAPI = "$($kustDB.queryServiceUri)/v1/rest/mgmt" + + Write-Verbose "Generate the query API URL" + $queryAPI = "$($kustDB.queryServiceUri)/v1/rest/query" + + + $KQLCommand = $KQLCommand | Out-String + + Write-Verbose "Check if the KQL command starts with a dot so it is a management command. Otherwise it is a query command" + if (-not ($KQLCommand -match "^\.")) { + $isManamgentCommand = $false + Write-Verbose "The command is a query command." + } else { + $isManamgentCommand = $true + Write-Verbose "The command is a management command. It is crucial to have the .execute database script <| in the beginning, otherwise the Kusto API will not execute the script." + if (-not ($KQLCommand -match "\.execute database script <\|")) { + $KQLCommand = ".execute database script <| $KQLCommand" + } + } + } + + process { + + Write-Verbose "The KQL-Command is: $KQLCommand" + + Write-Verbose "Create body of the request" + $body = @{ + 'csl' = $KQLCommand; + 'db' = $kustDB.displayName + } | ConvertTo-Json -Depth 1 + + + if ($isManamgentCommand) { + Write-Verbose "Calling Management API" + Write-Verbose "----------------------" + Write-Verbose "Sending the following values to the Query API:" + Write-Verbose "Headers: $($FabricSession.headerParams | Format-List | Out-String)" + Write-Verbose "Method: POST" + Write-Verbose "URI: $mgmtAPI" + Write-Verbose "Body of request: $body" + Write-Verbose "ContentType: application/json" + + $result = Invoke-RestMethod ` + -Headers $headerParams ` + -Method POST ` + -Uri $mgmtAPI ` + -Body ($body) ` + -ContentType "application/json; charset=utf-8" + + Write-Verbose "Result of the Management API: $($result | ` + ConvertTo-Json ` + -Depth 10)" + $result + } else { + Write-Verbose "Calling Query API" + Write-Verbose "-----------------" + Write-Verbose "Sending the following values to the Query API:" + Write-Verbose "Headers: $($FabricSession.headerParams | Format-List | Out-String)" + Write-Verbose "Method: POST" + Write-Verbose "URI: $queryAPI" + Write-Verbose "Body of request: $body" + Write-Verbose "ContentType: application/json" + + $result = Invoke-RestMethod ` + -Headers $headerParams ` + -Method POST ` + -Uri $queryAPI ` + -Body ($body) ` + -ContentType "application/json; charset=utf-8" + Write-Verbose "Result of the Query API: $($result | ` + ConvertTo-Json ` + -Depth 10)" + + + + if ($ReturnRawResult) { + $result + } else { + $myRecords = @() + + for ($j = 0; $j -lt $Result.tables[0].rows.Count; $j++) { + $myTableRow = [PSCustomObject]@{ } + + for ($i = 0; $i -lt $Result.tables[0].rows[0].Count; $i++) { + $myTableRow | ` + Add-Member ` + -MemberType NoteProperty ` + -Name $Result.Tables[0].Columns[$i].ColumnName ` + -Value $Result.Tables[0].rows[$j][$i] + } + $myRecords += $myTableRow + } + + $myRecords + } + + } + } + + end { } + +} \ No newline at end of file diff --git a/FabricTools/public/KQL Queryset/New-FabricKQLQueryset.ps1 b/source/Public/KQL Queryset/New-FabricKQLQueryset.ps1 similarity index 95% rename from FabricTools/public/KQL Queryset/New-FabricKQLQueryset.ps1 rename to source/Public/KQL Queryset/New-FabricKQLQueryset.ps1 index fea1665c..d465e65d 100644 --- a/FabricTools/public/KQL Queryset/New-FabricKQLQueryset.ps1 +++ b/source/Public/KQL Queryset/New-FabricKQLQueryset.ps1 @@ -3,8 +3,8 @@ Creates a new KQLQueryset in a specified Microsoft Fabric workspace. .DESCRIPTION -This function sends a POST request to the Microsoft Fabric API to create a new KQLQueryset -in the specified workspace. It supports optional parameters for KQLQueryset description +This function sends a POST request to the Microsoft Fabric API to create a new KQLQueryset +in the specified workspace. It supports optional parameters for KQLQueryset description and path definitions for the KQLQueryset content. .PARAMETER WorkspaceId @@ -29,12 +29,12 @@ An optional path to the platform-specific definition (e.g., .platform file) to u - Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. - Calls `Test-TokenExpired` to ensure token validity before making the API request. -Author: Tiago Balabuch +Author: Tiago Balabuch #> function New-FabricKQLQueryset { - [CmdletBinding()] + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -52,7 +52,7 @@ function New-FabricKQLQueryset { [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$KQLQuerysetPathDefinition, - + [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$KQLQuerysetPathPlatformDefinition @@ -95,8 +95,7 @@ function New-FabricKQLQueryset { payload = $KQLQuerysetEncodedContent payloadType = "InlineBase64" } - } - else { + } else { Write-Message -Message "Invalid or empty content in KQLQueryset definition." -Level Error return $null } @@ -120,8 +119,7 @@ function New-FabricKQLQueryset { payload = $KQLQuerysetEncodedPlatformContent payloadType = "InlineBase64" } - } - else { + } else { Write-Message -Message "Invalid or empty content in platform definition." -Level Error return $null } @@ -130,6 +128,7 @@ function New-FabricKQLQueryset { $bodyJson = $body | ConvertTo-Json -Depth 10 Write-Message -Message "Request Body: $bodyJson" -Level Debug + if ($PSCmdlet.ShouldProcess($KQLQuerysetName, "Create KQLQueryset")) { # Step 4: Make the API request $response = Invoke-RestMethod ` -Headers $FabricConfig.FabricHeaders ` @@ -141,6 +140,7 @@ function New-FabricKQLQueryset { -SkipHttpErrorCheck ` -ResponseHeadersVariable "responseHeader" ` -StatusCodeVariable "statusCode" + } # Step 5: Handle and log the response switch ($statusCode) { @@ -150,28 +150,27 @@ function New-FabricKQLQueryset { } 202 { Write-Message -Message "KQLQueryset '$KQLQuerysetName' creation accepted. Provisioning in progress!" -Level Info - + [string]$operationId = $responseHeader["x-ms-operation-id"] Write-Message -Message "Operation ID: '$operationId'" -Level Debug Write-Message -Message "Getting Long Running Operation status" -Level Debug - + $operationStatus = Get-FabricLongRunningOperation -operationId $operationId Write-Message -Message "Long Running Operation status: $operationStatus" -Level Debug # Handle operation result if ($operationStatus.status -eq "Succeeded") { Write-Message -Message "Operation Succeeded" -Level Debug Write-Message -Message "Getting Long Running Operation result" -Level Debug - + $operationResult = Get-FabricLongRunningOperationResult -operationId $operationId Write-Message -Message "Long Running Operation status: $operationResult" -Level Debug - + return $operationResult - } - else { + } else { Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Debug Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Error return $operationStatus - } + } } default { Write-Message -Message "Unexpected response code: $statusCode" -Level Error @@ -179,8 +178,7 @@ function New-FabricKQLQueryset { throw "API request failed with status code $statusCode." } } - } - catch { + } catch { # Step 6: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to create KQLQueryset. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/KQL Queryset/Remove-FabricKQLQueryset.ps1 b/source/Public/KQL Queryset/Remove-FabricKQLQueryset.ps1 similarity index 77% rename from FabricTools/public/KQL Queryset/Remove-FabricKQLQueryset.ps1 rename to source/Public/KQL Queryset/Remove-FabricKQLQueryset.ps1 index d9a48c5d..c826735b 100644 --- a/FabricTools/public/KQL Queryset/Remove-FabricKQLQueryset.ps1 +++ b/source/Public/KQL Queryset/Remove-FabricKQLQueryset.ps1 @@ -20,12 +20,13 @@ Deletes the KQLQueryset with ID "67890" from workspace "12345". - Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. - Validates token expiration before making the API request. -Author: Tiago Balabuch +Author: Tiago Balabuch #> -function Remove-FabricKQLQueryset { - [CmdletBinding()] +function Remove-FabricKQLQueryset +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -36,7 +37,8 @@ function Remove-FabricKQLQueryset { [string]$KQLQuerysetId ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -45,27 +47,30 @@ function Remove-FabricKQLQueryset { # Step 2: Construct the API URL $apiEndpointUrl = "{0}/workspaces/{1}/kqlQuerysets/{2}" -f $FabricConfig.BaseUrl, $WorkspaceId, $KQLQuerysetId Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug - - # Step 3: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Delete ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -StatusCodeVariable "statusCode" - + if ($PSCmdlet.ShouldProcess($apiEndpointUrl, "Remove KQLQueryset")) + { + # Step 3: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Delete ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -StatusCodeVariable "statusCode" + } # Step 4: Validate the response code - if ($statusCode -ne 200) { + if ($statusCode -ne 200) + { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error Write-Message -Message "Error: $($response.message)" -Level Error Write-Message "Error Code: $($response.errorCode)" -Level Error return $null } Write-Message -Message "KQLQueryset '$KQLQuerysetId' deleted successfully from workspace '$WorkspaceId'." -Level Info - + } - catch { + catch + { # Step 5: Log and handle errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to delete KQLQueryset '$KQLQuerysetId' from workspace '$WorkspaceId'. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/KQL Queryset/Update-FabricKQLQueryset.ps1 b/source/Public/KQL Queryset/Update-FabricKQLQueryset.ps1 similarity index 77% rename from FabricTools/public/KQL Queryset/Update-FabricKQLQueryset.ps1 rename to source/Public/KQL Queryset/Update-FabricKQLQueryset.ps1 index 4377448c..f7438a60 100644 --- a/FabricTools/public/KQL Queryset/Update-FabricKQLQueryset.ps1 +++ b/source/Public/KQL Queryset/Update-FabricKQLQueryset.ps1 @@ -14,6 +14,9 @@ The new name for the KQLQueryset. .PARAMETER KQLQuerysetDescription (Optional) The new description for the KQLQueryset. +.PARAMETER WorkspaceId +The unique identifier of the workspace where the KQLQueryset exists. + .EXAMPLE Update-FabricKQLQueryset -KQLQuerysetId "KQLQueryset123" -KQLQuerysetName "NewKQLQuerysetName" @@ -28,17 +31,18 @@ Updates both the name and description of the KQLQueryset "KQLQueryset123". - Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. - Calls `Test-TokenExpired` to ensure token validity before making the API request. -Author: Tiago Balabuch +Author: Tiago Balabuch #> -function Update-FabricKQLQueryset { - [CmdletBinding()] +function Update-FabricKQLQueryset +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [string]$WorkspaceId, - + [string]$WorkspaceId, + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$KQLQuerysetId, @@ -53,7 +57,8 @@ function Update-FabricKQLQueryset { [string]$KQLQuerysetDescription ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -68,27 +73,31 @@ function Update-FabricKQLQueryset { displayName = $KQLQuerysetName } - if ($KQLQuerysetDescription) { + if ($KQLQuerysetDescription) + { $body.description = $KQLQuerysetDescription } # Convert the body to JSON $bodyJson = $body | ConvertTo-Json Write-Message -Message "Request Body: $bodyJson" -Level Debug - - # Step 4: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Patch ` - -Body $bodyJson ` - -ContentType "application/json" ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -StatusCodeVariable "statusCode" + if ($PSCmdlet.ShouldProcess($KQLQuerysetId, "Update KQLQueryset")) + { + # Step 4: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Patch ` + -Body $bodyJson ` + -ContentType "application/json" ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -StatusCodeVariable "statusCode" + } # Step 5: Validate the response code - if ($statusCode -ne 200) { + if ($statusCode -ne 200) + { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error Write-Message -Message "Error: $($response.message)" -Level Error Write-Message "Error Code: $($response.errorCode)" -Level Error @@ -99,7 +108,8 @@ function Update-FabricKQLQueryset { Write-Message -Message "KQLQueryset '$KQLQuerysetName' updated successfully!" -Level Info return $response } - catch { + catch + { # Step 7: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to update KQLQueryset. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/KQL Queryset/Update-FabricKQLQuerysetDefinition.ps1 b/source/Public/KQL Queryset/Update-FabricKQLQuerysetDefinition.ps1 similarity index 94% rename from FabricTools/public/KQL Queryset/Update-FabricKQLQuerysetDefinition.ps1 rename to source/Public/KQL Queryset/Update-FabricKQLQuerysetDefinition.ps1 index 6189f659..1e77c90b 100644 --- a/FabricTools/public/KQL Queryset/Update-FabricKQLQuerysetDefinition.ps1 +++ b/source/Public/KQL Queryset/Update-FabricKQLQuerysetDefinition.ps1 @@ -3,7 +3,7 @@ Updates the definition of a KQLQueryset in a Microsoft Fabric workspace. .DESCRIPTION -This function allows updating the content or metadata of a KQLQueryset in a Microsoft Fabric workspace. +This function allows updating the content or metadata of a KQLQueryset in a Microsoft Fabric workspace. The KQLQueryset content can be provided as file paths, and metadata updates can optionally be enabled. .PARAMETER WorkspaceId @@ -25,7 +25,7 @@ Update-FabricKQLQuerysetDefinition -WorkspaceId "12345" -KQLQuerysetId "67890" - Updates the content of the KQLQueryset with ID `67890` in the workspace `12345` using the specified KQLQueryset file. .EXAMPLE -Update-FabricKQLQuerysetDefinition -WorkspaceId "12345" -KQLQuerysetId "67890" -KQLQuerysetPathDefinition "C:\KQLQuerysets\KQLQueryset.ipynb" +Update-FabricKQLQuerysetDefinition -WorkspaceId "12345" -KQLQuerysetId "67890" -KQLQuerysetPathDefinition "C:\KQLQuerysets\KQLQueryset.ipynb" Updates both the content and metadata of the KQLQueryset with ID `67890` in the workspace `12345`. @@ -35,12 +35,12 @@ Updates both the content and metadata of the KQLQueryset with ID `67890` in the - The KQLQueryset content is encoded as Base64 before being sent to the Fabric API. - This function handles asynchronous operations and retrieves operation results if required. -Author: Tiago Balabuch +Author: Tiago Balabuch #> function Update-FabricKQLQuerysetDefinition { - [CmdletBinding()] + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -53,7 +53,7 @@ function Update-FabricKQLQuerysetDefinition { [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$KQLQuerysetPathDefinition, - + [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$KQLQuerysetPathPlatformDefinition @@ -68,8 +68,8 @@ function Update-FabricKQLQuerysetDefinition { # Step 2: Construct the API URL $apiEndpointUrl = "{0}/workspaces/{1}/kqlQuerysets/{2}/updateDefinition" -f $FabricConfig.BaseUrl, $WorkspaceId, $KQLQuerysetId - if($KQLQuerysetPathPlatformDefinition){ - $apiEndpointUrl = "?updateMetadata=true" -f $apiEndpointUrl + if ($KQLQuerysetPathPlatformDefinition) { + $apiEndpointUrl = "?updateMetadata=true" -f $apiEndpointUrl } Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug @@ -78,12 +78,12 @@ function Update-FabricKQLQuerysetDefinition { definition = @{ format = $null parts = @() - } + } } - + if ($KQLQuerysetPathDefinition) { $KQLQuerysetEncodedContent = Convert-ToBase64 -filePath $KQLQuerysetPathDefinition - + if (-not [string]::IsNullOrEmpty($KQLQuerysetEncodedContent)) { # Add new part to the parts array $body.definition.parts += @{ @@ -91,8 +91,7 @@ function Update-FabricKQLQuerysetDefinition { payload = $KQLQuerysetEncodedContent payloadType = "InlineBase64" } - } - else { + } else { Write-Message -Message "Invalid or empty content in KQLQueryset definition." -Level Error return $null } @@ -107,8 +106,7 @@ function Update-FabricKQLQuerysetDefinition { payload = $KQLQuerysetEncodedPlatformContent payloadType = "InlineBase64" } - } - else { + } else { Write-Message -Message "Invalid or empty content in platform definition." -Level Error return $null } @@ -117,6 +115,7 @@ function Update-FabricKQLQuerysetDefinition { $bodyJson = $body | ConvertTo-Json -Depth 10 Write-Message -Message "Request Body: $bodyJson" -Level Debug + if ($PSCmdlet.ShouldProcess($KQLQuerysetId, "Update KQLQueryset")) { # Step 4: Make the API request $response = Invoke-RestMethod ` -Headers $FabricConfig.FabricHeaders ` @@ -127,7 +126,8 @@ function Update-FabricKQLQuerysetDefinition { -ErrorAction Stop ` -ResponseHeadersVariable "responseHeader" ` -StatusCodeVariable "statusCode" - + } + # Step 5: Handle and log the response switch ($statusCode) { 200 { @@ -142,23 +142,21 @@ function Update-FabricKQLQuerysetDefinition { # Handle operation result if ($operationResult.status -eq "Succeeded") { Write-Message -Message "Operation Succeeded" -Level Debug - + $result = Get-FabricLongRunningOperationResult -operationId $operationId return $result.definition.parts - } - else { + } else { Write-Message -Message "Operation Failed" -Level Debug return $operationResult.definition.parts - } - } + } + } default { Write-Message -Message "Unexpected response code: $statusCode" -Level Error Write-Message -Message "Error details: $($response.message)" -Level Error throw "API request failed with status code $statusCode." } } - } - catch { + } catch { # Step 6: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to update KQLQueryset. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Lakehouse/Get-FabricLakehouse.ps1 b/source/Public/Lakehouse/Get-FabricLakehouse.ps1 similarity index 95% rename from FabricTools/public/Lakehouse/Get-FabricLakehouse.ps1 rename to source/Public/Lakehouse/Get-FabricLakehouse.ps1 index ecdeb17f..ad33e1b0 100644 --- a/FabricTools/public/Lakehouse/Get-FabricLakehouse.ps1 +++ b/source/Public/Lakehouse/Get-FabricLakehouse.ps1 @@ -1,4 +1,5 @@ -<# +function Get-FabricLakehouse { + <# .SYNOPSIS Retrieves an Lakehouse or a list of Lakehouses from a specified workspace in Microsoft Fabric. @@ -8,6 +9,9 @@ The `Get-FabricLakehouse` function sends a GET request to the Fabric API to retr .PARAMETER WorkspaceId (Mandatory) The ID of the workspace to query Lakehouses. +.PARAMETER LakehouseId +(Optional) The ID of a specific Lakehouse to retrieve. + .PARAMETER LakehouseName (Optional) The name of the specific Lakehouse to retrieve. @@ -25,11 +29,9 @@ Retrieves all Lakehouses in workspace "12345". - Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. - Calls `Test-TokenExpired` to ensure token validity before making the API request. -Author: Tiago Balabuch - -#> +Author: Tiago Balabuch -function Get-FabricLakehouse { + #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] @@ -64,7 +66,7 @@ function Get-FabricLakehouse { if (-not ([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.GetName().Name -eq "System.Web" })) { Add-Type -AssemblyName System.Web } - + # Step 4: Loop to retrieve all capacities with continuation token Write-Message -Message "Loop started to get continuation token" -Level Debug $baseApiEndpointUrl = "{0}/workspaces/{1}/lakehouses" -f $FabricConfig.BaseUrl, $WorkspaceId @@ -72,14 +74,14 @@ function Get-FabricLakehouse { do { # Step 5: Construct the API URL $apiEndpointUrl = $baseApiEndpointUrl - + if ($null -ne $continuationToken) { # URL-encode the continuation token $encodedToken = [System.Web.HttpUtility]::UrlEncode($continuationToken) $apiEndpointUrl = "{0}?continuationToken={1}" -f $apiEndpointUrl, $encodedToken } Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug - + # Step 6: Make the API request $response = Invoke-RestMethod ` -Headers $FabricConfig.FabricHeaders ` @@ -89,7 +91,7 @@ function Get-FabricLakehouse { -SkipHttpErrorCheck ` -ResponseHeadersVariable "responseHeader" ` -StatusCodeVariable "statusCode" - + # Step 7: Validate the response code if ($statusCode -ne 200) { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error @@ -98,38 +100,34 @@ function Get-FabricLakehouse { Write-Message "Error Code: $($response.errorCode)" -Level Error return $null } - + # Step 8: Add data to the list if ($null -ne $response) { Write-Message -Message "Adding data to the list" -Level Debug $lakehouses += $response.value - + # Update the continuation token if present if ($response.PSObject.Properties.Match("continuationToken")) { Write-Message -Message "Updating the continuation token" -Level Debug $continuationToken = $response.continuationToken Write-Message -Message "Continuation token: $continuationToken" -Level Debug - } - else { + } else { Write-Message -Message "Updating the continuation token to null" -Level Debug $continuationToken = $null } - } - else { + } else { Write-Message -Message "No data received from the API." -Level Warning break } } while ($null -ne $continuationToken) Write-Message -Message "Loop finished and all data added to the list" -Level Debug - + # Step 8: Filter results based on provided parameters $lakehouse = if ($LakehouseId) { $lakehouses | Where-Object { $_.Id -eq $LakehouseId } - } - elseif ($LakehouseName) { + } elseif ($LakehouseName) { $lakehouses | Where-Object { $_.DisplayName -eq $LakehouseName } - } - else { + } else { # Return all lakehouses if no filter is provided Write-Message -Message "No filter provided. Returning all Lakehouses." -Level Debug $lakehouses @@ -139,16 +137,14 @@ function Get-FabricLakehouse { if ($Lakehouse) { Write-Message -Message "Lakehouse found matching the specified criteria." -Level Debug return $Lakehouse - } - else { + } else { Write-Message -Message "No Lakehouse found matching the provided criteria." -Level Warning return $null } - } - catch { + } catch { # Step 10: Capture and log error details $errorDetails = $_.Exception.Message Write-Message -Message "Failed to retrieve Lakehouse. Error: $errorDetails" -Level Error - } - -} + } + +} \ No newline at end of file diff --git a/FabricTools/public/Lakehouse/Get-FabricLakehouseTable.ps1 b/source/Public/Lakehouse/Get-FabricLakehouseTable.ps1 similarity index 84% rename from FabricTools/public/Lakehouse/Get-FabricLakehouseTable.ps1 rename to source/Public/Lakehouse/Get-FabricLakehouseTable.ps1 index e35ce0dd..f4f4816d 100644 --- a/FabricTools/public/Lakehouse/Get-FabricLakehouseTable.ps1 +++ b/source/Public/Lakehouse/Get-FabricLakehouseTable.ps1 @@ -1,7 +1,21 @@ +function Get-FabricLakehouseTable { + <# +.SYNOPSIS +Retrieves tables from a specified Lakehouse in a Fabric workspace. +.DESCRIPTION +This function retrieves tables from a specified Lakehouse in a Fabric workspace. It handles pagination using a continuation token to ensure all data is retrieved. +.PARAMETER WorkspaceId +The ID of the workspace containing the Lakehouse. +.PARAMETER LakehouseId +The ID of the Lakehouse from which to retrieve tables. +.EXAMPLE +Get-FabricLakehouseTable -WorkspaceId "your-workspace-id" -LakehouseId "your-lakehouse-id" +This example retrieves all tables from the specified Lakehouse in the specified workspace. -function Get-FabricLakehouseTable { + #> [CmdletBinding()] + [OutputType([System.Object[]])] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -11,7 +25,7 @@ function Get-FabricLakehouseTable { [ValidateNotNullOrEmpty()] [string]$LakehouseId ) - + try { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug @@ -24,19 +38,19 @@ function Get-FabricLakehouseTable { $continuationToken = $null $tables = @() $maxResults = 100 - + if (-not ([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.GetName().Name -eq "System.Web" })) { Add-Type -AssemblyName System.Web } $baseApiEndpointUrl = "{0}/workspaces/{1}/lakehouses/{2}/tables?maxResults={3}" -f $FabricConfig.BaseUrl, $WorkspaceId, $LakehouseId, $maxResults - + # Step 3: Loop to retrieve data with continuation token Write-Message -Message "Loop started to get continuation token" -Level Debug do { # Step 4: Construct the API URL $apiEndpointUrl = $baseApiEndpointUrl - + if ($null -ne $continuationToken) { # URL-encode the continuation token $encodedToken = [System.Web.HttpUtility]::UrlEncode($continuationToken) @@ -62,24 +76,22 @@ function Get-FabricLakehouseTable { Write-Message "Error Code: $($response.errorCode)" -Level Error return $null } - + # Step 7: Add data to the list if ($null -ne $response) { Write-Message -Message "Adding data to the list" -Level Debug $tables += $response.data - + # Update the continuation token if present if ($response.PSObject.Properties.Match("continuationToken")) { Write-Message -Message "Updating the continuation token" -Level Debug $continuationToken = $response.continuationToken Write-Message -Message "Continuation token: $continuationToken" -Level Debug - } - else { + } else { Write-Message -Message "Updating the continuation token to null" -Level Debug $continuationToken = $null } - } - else { + } else { Write-Message -Message "No data received from the API." -Level Warning break } @@ -89,16 +101,14 @@ function Get-FabricLakehouseTable { if ($tables) { Write-Message -Message "Tables found in the Lakehouse '$LakehouseId'." -Level Debug return $tables - } - else { + } else { Write-Message -Message "No tables found matching in the Lakehouse '$LakehouseId'." -Level Warning return $null } - } - catch { + } catch { # Step 10: Capture and log error details $errorDetails = $_.Exception.Message Write-Message -Message "Failed to retrieve Lakehouse. Error: $errorDetails" -Level Error - } - + } + } diff --git a/FabricTools/public/Lakehouse/New-FabricLakehouse.ps1 b/source/Public/Lakehouse/New-FabricLakehouse.ps1 similarity index 75% rename from FabricTools/public/Lakehouse/New-FabricLakehouse.ps1 rename to source/Public/Lakehouse/New-FabricLakehouse.ps1 index f24c8feb..70393c3a 100644 --- a/FabricTools/public/Lakehouse/New-FabricLakehouse.ps1 +++ b/source/Public/Lakehouse/New-FabricLakehouse.ps1 @@ -3,8 +3,8 @@ Creates a new Lakehouse in a specified Microsoft Fabric workspace. .DESCRIPTION -This function sends a POST request to the Microsoft Fabric API to create a new Lakehouse -in the specified workspace. It supports optional parameters for Lakehouse description +This function sends a POST request to the Microsoft Fabric API to create a new Lakehouse +in the specified workspace. It supports optional parameters for Lakehouse description and path definitions for the Lakehouse content. .PARAMETER WorkspaceId @@ -17,21 +17,22 @@ The name of the Lakehouse to be created. An optional description for the Lakehouse. .PARAMETER LakehouseEnableSchemas -An optional path to enable schemas in the Lakehouse +An optional path to enable schemas in the Lakehouse .EXAMPLE - Add-FabricLakehouse -WorkspaceId "workspace-12345" -LakehouseName "New Lakehouse" -LakehouseEnableSchemas $true +Add-FabricLakehouse -WorkspaceId "workspace-12345" -LakehouseName "New Lakehouse" -LakehouseEnableSchemas $true - .NOTES +.NOTES - Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. - Calls `Test-TokenExpired` to ensure token validity before making the API request. -Author: Tiago Balabuch +Author: Tiago Balabuch #> -function New-FabricLakehouse { - [CmdletBinding()] +function New-FabricLakehouse +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -51,7 +52,8 @@ function New-FabricLakehouse { [bool]$LakehouseEnableSchemas = $false ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -63,72 +65,82 @@ function New-FabricLakehouse { # Step 3: Construct the request body $body = @{ - displayName = $LakehouseName - } + displayName = $LakehouseName + } - if ($LakehouseDescription) { + if ($LakehouseDescription) + { $body.description = $LakehouseDescription } - if ($true -eq $LakehouseEnableSchemas) { + if ($true -eq $LakehouseEnableSchemas) + { $body.creationPayload = @{ enableSchemas = $LakehouseEnableSchemas } } $bodyJson = $body | ConvertTo-Json -Depth 10 Write-Message -Message "Request Body: $bodyJson" -Level Debug - - # Step 4: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Post ` - -Body $bodyJson ` - -ContentType "application/json" ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -ResponseHeadersVariable "responseHeader" ` - -StatusCodeVariable "statusCode" - + if ($PSCmdlet.ShouldProcess($LakehouseName, "Create Lakehouse")) + { + # Step 4: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Post ` + -Body $bodyJson ` + -ContentType "application/json" ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -ResponseHeadersVariable "responseHeader" ` + -StatusCodeVariable "statusCode" + } # Step 5: Handle and log the response - switch ($statusCode) { - 201 { + switch ($statusCode) + { + 201 + { Write-Message -Message "Lakehouse '$LakehouseName' created successfully!" -Level Info return $response } - 202 { + 202 + { Write-Message -Message "Lakehouse '$LakehouseName' creation accepted. Provisioning in progress!" -Level Info - + [string]$operationId = $responseHeader["x-ms-operation-id"] Write-Message -Message "Operation ID: '$operationId'" -Level Debug Write-Message -Message "Getting Long Running Operation status" -Level Debug - + $operationStatus = Get-FabricLongRunningOperation -operationId $operationId Write-Message -Message "Long Running Operation status: $operationStatus" -Level Debug # Handle operation result - if ($operationStatus.status -eq "Succeeded") { + if ($operationStatus.status -eq "Succeeded") + { Write-Message -Message "Operation Succeeded" -Level Debug Write-Message -Message "Getting Long Running Operation result" -Level Debug - + $operationResult = Get-FabricLongRunningOperationResult -operationId $operationId Write-Message -Message "Long Running Operation status: $operationResult" -Level Debug - + return $operationResult } - else { + else + { Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Debug Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Error return $operationStatus - } + } } - default { + default + { Write-Message -Message "Unexpected response code: $statusCode" -Level Error Write-Message -Message "Error details: $($response.message)" -Level Error throw "API request failed with status code $statusCode." } } } - catch { + catch + { # Step 6: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to create Lakehouse. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Lakehouse/Remove-FabricLakehouse.ps1 b/source/Public/Lakehouse/Remove-FabricLakehouse.ps1 similarity index 77% rename from FabricTools/public/Lakehouse/Remove-FabricLakehouse.ps1 rename to source/Public/Lakehouse/Remove-FabricLakehouse.ps1 index 254bdde6..4a2b8a8d 100644 --- a/FabricTools/public/Lakehouse/Remove-FabricLakehouse.ps1 +++ b/source/Public/Lakehouse/Remove-FabricLakehouse.ps1 @@ -20,12 +20,13 @@ Deletes the Lakehouse with ID "67890" from workspace "12345". - Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. - Validates token expiration before making the API request. -Author: Tiago Balabuch +Author: Tiago Balabuch #> -function Remove-FabricLakehouse { - [CmdletBinding()] +function Remove-FabricLakehouse +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -36,7 +37,8 @@ function Remove-FabricLakehouse { [string]$LakehouseId ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -45,27 +47,31 @@ function Remove-FabricLakehouse { # Step 2: Construct the API URL $apiEndpointUrl = "{0}/workspaces/{1}/lakehouses/{2}" -f $FabricConfig.BaseUrl, $WorkspaceId, $LakehouseId Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug - - # Step 3: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Delete ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -StatusCodeVariable "statusCode" + if ($PSCmdlet.ShouldProcess($apiEndpointUrl, "Remove Lakehouse")) + { + # Step 3: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Delete ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -StatusCodeVariable "statusCode" + } # Step 4: Validate the response code - if ($statusCode -ne 200) { + if ($statusCode -ne 200) + { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error Write-Message -Message "Error: $($response.message)" -Level Error Write-Message "Error Code: $($response.errorCode)" -Level Error return $null } Write-Message -Message "Lakehouse '$LakehouseId' deleted successfully from workspace '$WorkspaceId'." -Level Info - + } - catch { + catch + { # Step 5: Log and handle errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to delete Lakehouse '$LakehouseId' from workspace '$WorkspaceId'. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Lakehouse/Start-FabricLakehouseTableMaintenance.ps1 b/source/Public/Lakehouse/Start-FabricLakehouseTableMaintenance.ps1 similarity index 53% rename from FabricTools/public/Lakehouse/Start-FabricLakehouseTableMaintenance.ps1 rename to source/Public/Lakehouse/Start-FabricLakehouseTableMaintenance.ps1 index c680beba..a168801f 100644 --- a/FabricTools/public/Lakehouse/Start-FabricLakehouseTableMaintenance.ps1 +++ b/source/Public/Lakehouse/Start-FabricLakehouseTableMaintenance.ps1 @@ -1,5 +1,50 @@ -function Start-FabricLakehouseTableMaintenance { - [CmdletBinding()] +function Start-FabricLakehouseTableMaintenance +{ + <# +.SYNOPSIS + Initiates a table maintenance job for a specified Lakehouse in a Fabric workspace. +.DESCRIPTION + This function sends a POST request to the Fabric API to start a table maintenance job for a specified Lakehouse. + It allows for optional parameters such as schema name, table name, and Z-ordering columns. + The function also handles asynchronous operations and can wait for completion if specified. +.PARAMETER WorkspaceId + The unique identifier of the workspace where the Lakehouse resides. This parameter is mandatory. +.PARAMETER LakehouseId + The unique identifier of the Lakehouse for which the table maintenance job is to be initiated. This parameter is mandatory. +.PARAMETER JobType + The type of job to be initiated. Default is "TableMaintenance". This parameter is optional. +.PARAMETER SchemaName + The name of the schema in the Lakehouse. This parameter is optional. +.PARAMETER TableName + The name of the table in the Lakehouse. This parameter is optional. +.PARAMETER IsVOrder + A boolean flag indicating whether to apply V-ordering. This parameter is optional. +.PARAMETER ColumnsZOrderBy + An array of columns to be used for Z-ordering. This parameter is optional. +.PARAMETER retentionPeriod + The retention period for the table maintenance job. This parameter is optional. +.PARAMETER waitForCompletion + A boolean flag indicating whether to wait for the job to complete. Default is false. This parameter is optional. +.EXAMPLE + Start-FabricLakehouseTableMaintenance -WorkspaceId "12345" -LakehouseId "67890" -JobType "TableMaintenance" -SchemaName "dbo" -TableName "MyTable" -IsVOrder $true -ColumnsZOrderBy @("Column1", "Column2") -retentionPeriod "7:00:00" -waitForCompletion $true + Initiates a table maintenance job for the specified Lakehouse and waits for its completion. +.EXAMPLE + Start-FabricLakehouseTableMaintenance -WorkspaceId "12345" -LakehouseId "67890" -JobType "TableMaintenance" -SchemaName "dbo" -TableName "MyTable" -IsVOrder $false -ColumnsZOrderBy @("Column1", "Column2") -retentionPeriod "7:00:00" + Initiates a table maintenance job for the specified Lakehouse without waiting for its completion. +.NOTES + - Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. + - Calls `Test-TokenExpired` to ensure token validity before making the API request. + - This function handles asynchronous operations and retrieves operation results if required. + - The function uses the `Write-Message` function for logging and debugging purposes. + - The function uses the `Get-FabricLakehouse` function to retrieve Lakehouse details. + - The function uses the `Get-FabricLongRunningOperation` function to check the status of long-running operations. + - The function uses the `Invoke-RestMethod` cmdlet to make API requests. + +.NOTES + + #> + + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -17,11 +62,11 @@ function Start-FabricLakehouseTableMaintenance { [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$SchemaName, - + [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$TableName, - + [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [bool]$IsVOrder, @@ -38,22 +83,24 @@ function Start-FabricLakehouseTableMaintenance { [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [bool]$waitForCompletion = $false - + ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired Write-Message -Message "Token validation completed." -Level Debug - - - $lakehouse = Get-FabricLakehouse -WorkspaceId $WorkspaceId -LakehouseId $LakehouseId - if ($lakehouse.properties.PSObject.Properties['defaultSchema'] -and -not $SchemaName) { + + + $lakehouse = Get-FabricLakehouse -WorkspaceId $WorkspaceId -LakehouseId $LakehouseId + if ($lakehouse.properties.PSObject.Properties['defaultSchema'] -and -not $SchemaName) + { Write-Error "The Lakehouse '$lakehouse.displayName' has schema enabled, but no schema name was provided. Please specify the 'SchemaName' parameter to proceed." return } - + # Step 2: Construct the API URL $apiEndpointUrl = "{0}/workspaces/{1}/lakehouses/{2}/jobs/instances?jobType={3}" -f $FabricConfig.BaseUrl, $WorkspaceId , $LakehouseId, $JobType Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug @@ -62,81 +109,94 @@ function Start-FabricLakehouseTableMaintenance { $body = @{ executionData = @{ tableName = $TableName - optimizeSettings = @{} + optimizeSettings = @{ } } } - if ($lakehouse.properties.PSObject.Properties['defaultSchema'] -and $SchemaName) { + if ($lakehouse.properties.PSObject.Properties['defaultSchema'] -and $SchemaName) + { $body.executionData.schemaName = $SchemaName } - if ($IsVOrder) { + if ($IsVOrder) + { $body.executionData.optimizeSettings.vOrder = $IsVOrder } - if ($ColumnsZOrderBy) { - # Ensure $ColumnsZOrderBy is an array - if (-not ($ColumnsZOrderBy -is [array])) { - $ColumnsZOrderBy = $ColumnsZOrderBy -split "," + if ($ColumnsZOrderBy) + { + # Ensure $ColumnsZOrderBy is an array + if (-not ($ColumnsZOrderBy -is [array])) + { + $ColumnsZOrderBy = $ColumnsZOrderBy -split "," + } + # Add it to the optimizeSettings in the request body + $body.executionData.optimizeSettings.zOrderBy = $ColumnsZOrderBy } - # Add it to the optimizeSettings in the request body - $body.executionData.optimizeSettings.zOrderBy = $ColumnsZOrderBy - } - - if ($retentionPeriod) { - if (-not $body.executionData.PSObject.Properties['vacuumSettings']) { + if ($retentionPeriod) + { + + if (-not $body.executionData.PSObject.Properties['vacuumSettings']) + { $body.executionData.vacuumSettings = @{ retentionPeriod = @() } } $body.executionData.vacuumSettings.retentionPeriod = $retentionPeriod - + } - + $bodyJson = $body | ConvertTo-Json -Depth 10 Write-Message -Message "Request Body: $bodyJson" -Level Debug + if ($PSCmdlet.ShouldProcess($apiEndpointUrl, "Start Table Maintenance Job")) + { + # Step 4: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Post ` + -Body $bodyJson ` + -ContentType "application/json" ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -ResponseHeadersVariable "responseHeader" ` + -StatusCodeVariable "statusCode" + } - # Step 4: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Post ` - -Body $bodyJson ` - -ContentType "application/json" ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -ResponseHeadersVariable "responseHeader" ` - -StatusCodeVariable "statusCode" - - Write-Message -Message "Response Code: $statusCode" -Level Debug + Write-Message -Message "Response Code: $statusCode" -Level Debug # Step 5: Handle and log the response - switch ($statusCode) { - 201 { + switch ($statusCode) + { + 201 + { Write-Message -Message "Table maintenance job successfully initiated for Lakehouse '$lakehouse.displayName'." -Level Info return $response } - 202 { + 202 + { Write-Message -Message "Table maintenance job accepted and is now running in the background. Job execution is in progress." -Level Info [string]$operationId = $responseHeader["x-ms-operation-id"] [string]$location = $responseHeader["Location"] - [string]$retryAfter = $responseHeader["Retry-After"] + [string]$retryAfter = $responseHeader["Retry-After"] Write-Message -Message "Operation ID: '$operationId'" -Level Debug Write-Message -Message "Location: '$location'" -Level Debug Write-Message -Message "Retry-After: '$retryAfter'" -Level Debug - - if ($waitForCompletion -eq $true) { - Write-Message -Message "Getting Long Running Operation status" -Level Debug + + if ($waitForCompletion -eq $true) + { + Write-Message -Message "Getting Long Running Operation status" -Level Debug $operationStatus = Get-FabricLongRunningOperation -operationId $operationId -location $location -retryAfter $retryAfter Write-Message -Message "Long Running Operation status: $operationStatus" -Level Debug return $operationStatus } - else { + else + { Write-Message -Message "The operation is running asynchronously." -Level Info Write-Message -Message "Use the returned details to check the operation status." -Level Info - Write-Message -Message "To wait for the operation to complete, set the 'waitForCompletion' parameter to true." -Level Info + Write-Message -Message "To wait for the operation to complete, set the 'waitForCompletion' parameter to true." -Level Info $operationDetails = [PSCustomObject]@{ OperationId = $operationId Location = $location @@ -145,14 +205,16 @@ function Start-FabricLakehouseTableMaintenance { return $operationDetails } } - default { + default + { Write-Message -Message "Unexpected response code: $statusCode" -Level Error Write-Message -Message "Error details: $($response.message)" -Level Error throw "API request failed with status code $statusCode." } } } - catch { + catch + { # Step 6: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to start table maintenance job. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Lakehouse/Update-FabricLakehouse.ps1 b/source/Public/Lakehouse/Update-FabricLakehouse.ps1 similarity index 77% rename from FabricTools/public/Lakehouse/Update-FabricLakehouse.ps1 rename to source/Public/Lakehouse/Update-FabricLakehouse.ps1 index c4b9879b..37a439ea 100644 --- a/FabricTools/public/Lakehouse/Update-FabricLakehouse.ps1 +++ b/source/Public/Lakehouse/Update-FabricLakehouse.ps1 @@ -5,6 +5,9 @@ Updates the properties of a Fabric Lakehouse. .DESCRIPTION The `Update-FabricLakehouse` function updates the name and/or description of a specified Fabric Lakehouse by making a PATCH request to the API. +.PARAMETER WorkspaceId +The unique identifier of the workspace where the Lakehouse exists. + .PARAMETER LakehouseId The unique identifier of the Lakehouse to be updated. @@ -28,17 +31,18 @@ Updates both the name and description of the Lakehouse "Lakehouse123". - Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. - Calls `Test-TokenExpired` to ensure token validity before making the API request. -Author: Tiago Balabuch +Author: Tiago Balabuch #> -function Update-FabricLakehouse { - [CmdletBinding()] +function Update-FabricLakehouse +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [string]$WorkspaceId, - + [string]$WorkspaceId, + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$LakehouseId, @@ -53,7 +57,8 @@ function Update-FabricLakehouse { [string]$LakehouseDescription ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -68,27 +73,31 @@ function Update-FabricLakehouse { displayName = $LakehouseName } - if ($LakehouseDescription) { + if ($LakehouseDescription) + { $body.description = $LakehouseDescription } # Convert the body to JSON $bodyJson = $body | ConvertTo-Json Write-Message -Message "Request Body: $bodyJson" -Level Debug - - # Step 4: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Patch ` - -Body $bodyJson ` - -ContentType "application/json" ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -StatusCodeVariable "statusCode" + if ($PSCmdlet.ShouldProcess($LakehouseId, "Update Lakehouse")) + { + # Step 4: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Patch ` + -Body $bodyJson ` + -ContentType "application/json" ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -StatusCodeVariable "statusCode" + } # Step 5: Validate the response code - if ($statusCode -ne 200) { + if ($statusCode -ne 200) + { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error Write-Message -Message "Error: $($response.message)" -Level Error Write-Message -Message "Error Details: $($response.moreDetails)" -Level Error @@ -100,7 +109,8 @@ function Update-FabricLakehouse { Write-Message -Message "Lakehouse '$LakehouseName' updated successfully!" -Level Info return $response } - catch { + catch + { # Step 7: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to update Lakehouse. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Lakehouse/Load-FabricLakehouseTable.ps1 b/source/Public/Lakehouse/Write-FabricLakehouseTableData.ps1 similarity index 60% rename from FabricTools/public/Lakehouse/Load-FabricLakehouseTable.ps1 rename to source/Public/Lakehouse/Write-FabricLakehouseTableData.ps1 index b1ca0822..124f7e3e 100644 --- a/FabricTools/public/Lakehouse/Load-FabricLakehouseTable.ps1 +++ b/source/Public/Lakehouse/Write-FabricLakehouseTableData.ps1 @@ -1,10 +1,44 @@ -function Load-FabricLakehouseTable { - [CmdletBinding()] +function Write-FabricLakehouseTableData +{ + <# +.SYNOPSIS +Loads data into a specified table in a Lakehouse within a Fabric workspace. +.DESCRIPTION +Loads data into a specified table in a Lakehouse within a Fabric workspace. The function supports loading data from files or folders, with options for file format and CSV settings. +.PARAMETER WorkspaceId +The ID of the workspace containing the Lakehouse. +.PARAMETER LakehouseId +The ID of the Lakehouse where the table resides. +.PARAMETER TableName +The name of the table to load data into. +.PARAMETER PathType +The type of path to load data from (File or Folder). +.PARAMETER RelativePath +The relative path to the file or folder to load data from. +.PARAMETER FileFormat +The format of the file to load data from (CSV or Parquet). +.PARAMETER CsvDelimiter +The delimiter used in the CSV file (default is comma). +.PARAMETER CsvHeader +Indicates whether the CSV file has a header row (default is false). +.PARAMETER Mode +The mode for loading data (append or overwrite). +.PARAMETER Recursive +Indicates whether to load data recursively from subfolders (default is false). +.EXAMPLE +Import-FabricLakehouseTableData -WorkspaceId "your-workspace-id" -LakehouseId "your-lakehouse-id" -TableName "your-table-name" -PathType "File" -RelativePath "path/to/your/file.csv" -FileFormat "CSV" -CsvDelimiter "," -CsvHeader $true -Mode "append" -Recursive $false +This example loads data from a CSV file into the specified table in the Lakehouse. +.EXAMPLE +Import-FabricLakehouseTableData -WorkspaceId "your-workspace-id" -LakehouseId "your-lakehouse-id" -TableName "your-table-name" -PathType "Folder" -RelativePath "path/to/your/folder" -FileFormat "Parquet" -Mode "overwrite" -Recursive $true +This example loads data from a folder into the specified table in the Lakehouse, overwriting any existing data. + #> + [CmdletBinding(SupportsShouldProcess)] + [Alias("Import-FabricLakehouseTableData")] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [string]$WorkspaceId, - + [string]$WorkspaceId, + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$LakehouseId, @@ -31,7 +65,7 @@ function Load-FabricLakehouseTable { [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$CsvDelimiter = ",", - + [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [bool]$CsvHeader = $false, @@ -40,13 +74,14 @@ function Load-FabricLakehouseTable { [ValidateNotNullOrEmpty()] [ValidateSet('append', 'overwrite')] [string]$Mode = "append", - + [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [bool]$Recursive = $false ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -66,8 +101,9 @@ function Load-FabricLakehouseTable { format = $FileFormat } } - - if ($FileFormat -eq "CSV") { + + if ($FileFormat -eq "CSV") + { $body.formatOptions.delimiter = $CsvDelimiter $body.formatOptions.hasHeader = $CsvHeader } @@ -76,20 +112,24 @@ function Load-FabricLakehouseTable { $bodyJson = $body | ConvertTo-Json Write-Message -Message "Request Body: $bodyJson" -Level Debug - # Step 4: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Post ` - -Body $bodyJson ` - -ContentType "application/json" ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -ResponseHeadersVariable "responseHeader" ` - -StatusCodeVariable "statusCode" + if ($PSCmdlet.ShouldProcess($apiEndpointUrl, "Load Lakehouse Table Data")) + { + # Step 4: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Post ` + -Body $bodyJson ` + -ContentType "application/json" ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -ResponseHeadersVariable "responseHeader" ` + -StatusCodeVariable "statusCode" + } # Step 5: Validate the response code - if ($statusCode -ne 202) { + if ($statusCode -ne 202) + { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error Write-Message -Message "Error: $($response.message)" -Level Error Write-Message -Message "Error Details: $($response.moreDetails)" -Level Error @@ -98,29 +138,34 @@ function Load-FabricLakehouseTable { } # Step 5: Handle and log the response - switch ($statusCode) { - 202 { + switch ($statusCode) + { + 202 + { Write-Message -Message "Load table '$TableName' request accepted. Load table operation in progress!" -Level Info - + [string]$operationId = $responseHeader["x-ms-operation-id"] Write-Message -Message "Operation ID: '$operationId'" -Level Debug Write-Message -Message "Getting Long Running Operation status" -Level Debug - + $operationStatus = Get-FabricLongRunningOperation -operationId $operationId Write-Message -Message "Long Running Operation status: $operationStatus" -Level Debug # Handle operation result - if ($operationStatus.status -eq "Succeeded") { + if ($operationStatus.status -eq "Succeeded") + { Write-Message -Message "Operation Succeeded" -Level Debug Write-Message -Message "Load table '$TableName' operation complete successfully!" -Level Info return $operationStatus } - else { + else + { Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Debug Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Error return $operationStatus - } + } } - default { + default + { Write-Message -Message "Unexpected response code: $statusCode" -Level Error Write-Message -Message "Error: $($response.message)" -Level Error Write-Message -Message "Error Details: $($response.moreDetails)" -Level Error @@ -131,7 +176,8 @@ function Load-FabricLakehouseTable { # Step 6: Handle results } - catch { + catch + { # Step 7: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to update Lakehouse. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/ML Experiment/Get-FabricMLExperiment.ps1 b/source/Public/ML Experiment/Get-FabricMLExperiment.ps1 similarity index 95% rename from FabricTools/public/ML Experiment/Get-FabricMLExperiment.ps1 rename to source/Public/ML Experiment/Get-FabricMLExperiment.ps1 index 52e57f8e..4e607c4f 100644 --- a/FabricTools/public/ML Experiment/Get-FabricMLExperiment.ps1 +++ b/source/Public/ML Experiment/Get-FabricMLExperiment.ps1 @@ -28,7 +28,7 @@ - Calls `Test-TokenExpired` to ensure token validity before making the API request. Author: Tiago Balabuch - + #> function Get-FabricMLExperiment { [CmdletBinding()] @@ -61,27 +61,27 @@ function Get-FabricMLExperiment { # Step 3: Initialize variables $continuationToken = $null $MLExperiments = @() - + if (-not ([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.GetName().Name -eq "System.Web" })) { Add-Type -AssemblyName System.Web } - + # Step 4: Loop to retrieve all capacities with continuation token Write-Message -Message "Loop started to get continuation token" -Level Debug $baseApiEndpointUrl = "{0}/workspaces/{1}/mlExperiments" -f $FabricConfig.BaseUrl, $WorkspaceId - + do { # Step 5: Construct the API URL $apiEndpointUrl = $baseApiEndpointUrl - + if ($null -ne $continuationToken) { # URL-encode the continuation token $encodedToken = [System.Web.HttpUtility]::UrlEncode($continuationToken) $apiEndpointUrl = "{0}?continuationToken={1}" -f $apiEndpointUrl, $encodedToken } Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug - + # Step 6: Make the API request $response = Invoke-RestMethod ` -Headers $FabricConfig.FabricHeaders ` @@ -91,7 +91,7 @@ function Get-FabricMLExperiment { -SkipHttpErrorCheck ` -ResponseHeadersVariable "responseHeader" ` -StatusCodeVariable "statusCode" - + # Step 7: Validate the response code if ($statusCode -ne 200) { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error @@ -100,38 +100,34 @@ function Get-FabricMLExperiment { Write-Message "Error Code: $($response.errorCode)" -Level Error return $null } - + # Step 8: Add data to the list if ($null -ne $response) { Write-Message -Message "Adding data to the list" -Level Debug $MLExperiments += $response.value - + # Update the continuation token if present if ($response.PSObject.Properties.Match("continuationToken")) { Write-Message -Message "Updating the continuation token" -Level Debug $continuationToken = $response.continuationToken Write-Message -Message "Continuation token: $continuationToken" -Level Debug - } - else { + } else { Write-Message -Message "Updating the continuation token to null" -Level Debug $continuationToken = $null } - } - else { + } else { Write-Message -Message "No data received from the API." -Level Warning break } } while ($null -ne $continuationToken) Write-Message -Message "Loop finished and all data added to the list" -Level Debug - + # Step 8: Filter results based on provided parameters $MLExperiment = if ($MLExperimentId) { $MLExperiments | Where-Object { $_.Id -eq $MLExperimentId } - } - elseif ($MLExperimentName) { + } elseif ($MLExperimentName) { $MLExperiments | Where-Object { $_.DisplayName -eq $MLExperimentName } - } - else { + } else { # Return all MLExperiments if no filter is provided Write-Message -Message "No filter provided. Returning all MLExperiments." -Level Debug $MLExperiments @@ -141,16 +137,14 @@ function Get-FabricMLExperiment { if ($MLExperiment) { Write-Message -Message "ML Experiment found matching the specified criteria." -Level Debug return $MLExperiment - } - else { + } else { Write-Message -Message "No ML Experiment found matching the provided criteria." -Level Warning return $null } - } - catch { + } catch { # Step 10: Capture and log error details $errorDetails = $_.Exception.Message Write-Message -Message "Failed to retrieve ML Experiment. Error: $errorDetails" -Level Error - } - -} + } + +} \ No newline at end of file diff --git a/FabricTools/public/ML Experiment/New-FabricMLExperiment.ps1 b/source/Public/ML Experiment/New-FabricMLExperiment.ps1 similarity index 81% rename from FabricTools/public/ML Experiment/New-FabricMLExperiment.ps1 rename to source/Public/ML Experiment/New-FabricMLExperiment.ps1 index 0a4d28be..2e561b7b 100644 --- a/FabricTools/public/ML Experiment/New-FabricMLExperiment.ps1 +++ b/source/Public/ML Experiment/New-FabricMLExperiment.ps1 @@ -3,7 +3,7 @@ Creates a new ML Experiment in a specified Microsoft Fabric workspace. .DESCRIPTION - This function sends a POST request to the Microsoft Fabric API to create a new ML Experiment + This function sends a POST request to the Microsoft Fabric API to create a new ML Experiment in the specified workspace. It supports optional parameters for ML Experiment description. .PARAMETER WorkspaceId @@ -24,10 +24,11 @@ - Calls `Test-TokenExpired` to ensure token validity before making the API request. Author: Tiago Balabuch - + #> -function New-FabricMLExperiment { - [CmdletBinding()] +function New-FabricMLExperiment +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -43,7 +44,8 @@ function New-FabricMLExperiment { [string]$MLExperimentDescription ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -58,62 +60,71 @@ function New-FabricMLExperiment { displayName = $MLExperimentName } - if ($MLExperimentDescription) { + if ($MLExperimentDescription) + { $body.description = $MLExperimentDescription } $bodyJson = $body | ConvertTo-Json -Depth 10 Write-Message -Message "Request Body: $bodyJson" -Level Debug - - # Step 4: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Post ` - -Body $bodyJson ` - -ContentType "application/json" ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -ResponseHeadersVariable "responseHeader" ` - -StatusCodeVariable "statusCode" + if ($PSCmdlet.ShouldProcess($MLExperimentName, "Create ML Experiment")) + { + # Step 4: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Post ` + -Body $bodyJson ` + -ContentType "application/json" ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -ResponseHeadersVariable "responseHeader" ` + -StatusCodeVariable "statusCode" + } # Step 5: Handle and log the response - switch ($statusCode) { - 201 { + switch ($statusCode) + { + 201 + { Write-Message -Message "ML Experiment '$MLExperimentName' created successfully!" -Level Info return $response } - 202 { + 202 + { Write-Message -Message "ML Experiment '$MLExperimentName' creation accepted. Provisioning in progress!" -Level Info - + [string]$operationId = $responseHeader["x-ms-operation-id"] [string]$location = $responseHeader["Location"] - [string]$retryAfter = $responseHeader["Retry-After"] + [string]$retryAfter = $responseHeader["Retry-After"] Write-Message -Message "Operation ID: '$operationId'" -Level Debug Write-Message -Message "Location: '$location'" -Level Debug Write-Message -Message "Retry-After: '$retryAfter'" -Level Debug Write-Message -Message "Getting Long Running Operation status" -Level Debug - + $operationStatus = Get-FabricLongRunningOperation -operationId $operationId Write-Message -Message "Long Running Operation status: $operationStatus" -Level Debug # Handle operation result - if ($operationStatus.status -eq "Succeeded") { + if ($operationStatus.status -eq "Succeeded") + { Write-Message -Message "Operation Succeeded" -Level Debug Write-Message -Message "Getting Long Running Operation result" -Level Debug - + $operationResult = Get-FabricLongRunningOperationResult -operationId $operationId Write-Message -Message "Long Running Operation status: $operationResult" -Level Debug - + return $operationResult } - else { + else + { Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Debug Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Error return $operationStatus - } + } } - default { + default + { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error Write-Message -Message "Error: $($response.message)" -Level Error Write-Message -Message "Error Details: $($response.moreDetails)" -Level Error @@ -122,7 +133,8 @@ function New-FabricMLExperiment { } } } - catch { + catch + { # Step 6: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to create ML Experiment. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/ML Experiment/Remove-FabricMLExperiment.ps1 b/source/Public/ML Experiment/Remove-FabricMLExperiment.ps1 similarity index 79% rename from FabricTools/public/ML Experiment/Remove-FabricMLExperiment.ps1 rename to source/Public/ML Experiment/Remove-FabricMLExperiment.ps1 index 7c8cbb20..5a1e4652 100644 --- a/FabricTools/public/ML Experiment/Remove-FabricMLExperiment.ps1 +++ b/source/Public/ML Experiment/Remove-FabricMLExperiment.ps1 @@ -3,7 +3,7 @@ Removes an ML Experiment from a specified Microsoft Fabric workspace. .DESCRIPTION - This function sends a DELETE request to the Microsoft Fabric API to remove an ML Experiment + This function sends a DELETE request to the Microsoft Fabric API to remove an ML Experiment from the specified workspace using the provided WorkspaceId and MLExperimentId. .PARAMETER WorkspaceId @@ -21,10 +21,11 @@ - Calls `Test-TokenExpired` to ensure token validity before making the API request. Author: Tiago Balabuch - + #> -function Remove-FabricMLExperiment { - [CmdletBinding()] +function Remove-FabricMLExperiment +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -34,7 +35,8 @@ function Remove-FabricMLExperiment { [ValidateNotNullOrEmpty()] [string]$MLExperimentId ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -43,18 +45,21 @@ function Remove-FabricMLExperiment { # Step 2: Construct the API URL $apiEndpointUrl = "{0}/workspaces/{1}/mlExperiments/{2}" -f $FabricConfig.BaseUrl, $WorkspaceId, $MLExperimentId Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug - - # Step 3: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Delete ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -StatusCodeVariable "statusCode" + if ($PSCmdlet.ShouldProcess($apiEndpointUrl, "Remove ML Experiment")) + { + # Step 3: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Delete ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -StatusCodeVariable "statusCode" + } # Step 4: Validate the response code - if ($statusCode -ne 200) { + if ($statusCode -ne 200) + { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error Write-Message -Message "Error: $($response.message)" -Level Error Write-Message -Message "Error Details: $($response.moreDetails)" -Level Error @@ -62,9 +67,10 @@ function Remove-FabricMLExperiment { return $null } Write-Message -Message "ML Experiment '$MLExperimentId' deleted successfully from workspace '$WorkspaceId'." -Level Info - + } - catch { + catch + { # Step 5: Log and handle errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to delete ML Experiment '$MLExperimentId' from workspace '$WorkspaceId'. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/ML Experiment/Update-FabricMLExperiment.ps1 b/source/Public/ML Experiment/Update-FabricMLExperiment.ps1 similarity index 80% rename from FabricTools/public/ML Experiment/Update-FabricMLExperiment.ps1 rename to source/Public/ML Experiment/Update-FabricMLExperiment.ps1 index f9c0a9e5..2d26ae9e 100644 --- a/FabricTools/public/ML Experiment/Update-FabricMLExperiment.ps1 +++ b/source/Public/ML Experiment/Update-FabricMLExperiment.ps1 @@ -3,7 +3,7 @@ Updates an existing ML Experiment in a specified Microsoft Fabric workspace. .DESCRIPTION - This function sends a PATCH request to the Microsoft Fabric API to update an existing ML Experiment + This function sends a PATCH request to the Microsoft Fabric API to update an existing ML Experiment in the specified workspace. It supports optional parameters for ML Experiment description. .PARAMETER WorkspaceId @@ -27,15 +27,16 @@ - Calls `Test-TokenExpired` to ensure token validity before making the API request. Author: Tiago Balabuch - + #> -function Update-FabricMLExperiment { - [CmdletBinding()] +function Update-FabricMLExperiment +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [string]$WorkspaceId, - + [string]$WorkspaceId, + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$MLExperimentId, @@ -50,7 +51,8 @@ function Update-FabricMLExperiment { [string]$MLExperimentDescription ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -65,27 +67,30 @@ function Update-FabricMLExperiment { displayName = $MLExperimentName } - if ($MLExperimentDescription) { + if ($MLExperimentDescription) + { $body.description = $MLExperimentDescription } # Convert the body to JSON $bodyJson = $body | ConvertTo-Json Write-Message -Message "Request Body: $bodyJson" -Level Debug - - # Step 4: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Patch ` - -Body $bodyJson ` - -ContentType "application/json" ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -StatusCodeVariable "statusCode" - + if ($PSCmdlet.ShouldProcess($MLExperimentName, "Update ML Experiment")) + { + # Step 4: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Patch ` + -Body $bodyJson ` + -ContentType "application/json" ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -StatusCodeVariable "statusCode" + } # Step 5: Validate the response code - if ($statusCode -ne 200) { + if ($statusCode -ne 200) + { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error Write-Message -Message "Error: $($response.message)" -Level Error Write-Message -Message "Error Details: $($response.moreDetails)" -Level Error @@ -97,7 +102,8 @@ function Update-FabricMLExperiment { Write-Message -Message "ML Experiment '$MLExperimentName' updated successfully!" -Level Info return $response } - catch { + catch + { # Step 7: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to update ML Experiment. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/ML Model/Get-FabricMLModel.ps1 b/source/Public/ML Model/Get-FabricMLModel.ps1 similarity index 95% rename from FabricTools/public/ML Model/Get-FabricMLModel.ps1 rename to source/Public/ML Model/Get-FabricMLModel.ps1 index 7d15c5e8..e7b728cc 100644 --- a/FabricTools/public/ML Model/Get-FabricMLModel.ps1 +++ b/source/Public/ML Model/Get-FabricMLModel.ps1 @@ -28,7 +28,7 @@ - Calls `Test-TokenExpired` to ensure token validity before making the API request. Author: Tiago Balabuch - + #> function Get-FabricMLModel { [CmdletBinding()] @@ -61,27 +61,27 @@ function Get-FabricMLModel { # Step 3: Initialize variables $continuationToken = $null $MLModels = @() - + if (-not ([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.GetName().Name -eq "System.Web" })) { Add-Type -AssemblyName System.Web } - + # Step 4: Loop to retrieve all capacities with continuation token Write-Message -Message "Loop started to get continuation token" -Level Debug $baseApiEndpointUrl = "{0}/workspaces/{1}/mlModels" -f $FabricConfig.BaseUrl, $WorkspaceId - + do { # Step 5: Construct the API URL $apiEndpointUrl = $baseApiEndpointUrl - + if ($null -ne $continuationToken) { # URL-encode the continuation token $encodedToken = [System.Web.HttpUtility]::UrlEncode($continuationToken) $apiEndpointUrl = "{0}?continuationToken={1}" -f $apiEndpointUrl, $encodedToken } Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug - + # Step 6: Make the API request $response = Invoke-RestMethod ` -Headers $FabricConfig.FabricHeaders ` @@ -91,7 +91,7 @@ function Get-FabricMLModel { -SkipHttpErrorCheck ` -ResponseHeadersVariable "responseHeader" ` -StatusCodeVariable "statusCode" - + # Step 7: Validate the response code if ($statusCode -ne 200) { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error @@ -100,38 +100,34 @@ function Get-FabricMLModel { Write-Message "Error Code: $($response.errorCode)" -Level Error return $null } - + # Step 8: Add data to the list if ($null -ne $response) { Write-Message -Message "Adding data to the list" -Level Debug $MLModels += $response.value - + # Update the continuation token if present if ($response.PSObject.Properties.Match("continuationToken")) { Write-Message -Message "Updating the continuation token" -Level Debug $continuationToken = $response.continuationToken Write-Message -Message "Continuation token: $continuationToken" -Level Debug - } - else { + } else { Write-Message -Message "Updating the continuation token to null" -Level Debug $continuationToken = $null } - } - else { + } else { Write-Message -Message "No data received from the API." -Level Warning break } } while ($null -ne $continuationToken) Write-Message -Message "Loop finished and all data added to the list" -Level Debug - + # Step 8: Filter results based on provided parameters $MLModel = if ($MLModelId) { $MLModels | Where-Object { $_.Id -eq $MLModelId } - } - elseif ($MLModelName) { + } elseif ($MLModelName) { $MLModels | Where-Object { $_.DisplayName -eq $MLModelName } - } - else { + } else { # Return all MLModels if no filter is provided Write-Message -Message "No filter provided. Returning all MLModels." -Level Debug $MLModels @@ -141,16 +137,14 @@ function Get-FabricMLModel { if ($MLModel) { Write-Message -Message "ML Model found matching the specified criteria." -Level Debug return $MLModel - } - else { + } else { Write-Message -Message "No ML Model found matching the provided criteria." -Level Warning return $null } - } - catch { + } catch { # Step 10: Capture and log error details $errorDetails = $_.Exception.Message Write-Message -Message "Failed to retrieve ML Model. Error: $errorDetails" -Level Error - } - -} + } + +} \ No newline at end of file diff --git a/FabricTools/public/ML Model/New-FabricMLModel.ps1 b/source/Public/ML Model/New-FabricMLModel.ps1 similarity index 81% rename from FabricTools/public/ML Model/New-FabricMLModel.ps1 rename to source/Public/ML Model/New-FabricMLModel.ps1 index cf4ffac0..4281dba5 100644 --- a/FabricTools/public/ML Model/New-FabricMLModel.ps1 +++ b/source/Public/ML Model/New-FabricMLModel.ps1 @@ -3,7 +3,7 @@ Creates a new ML Model in a specified Microsoft Fabric workspace. .DESCRIPTION - This function sends a POST request to the Microsoft Fabric API to create a new ML Model + This function sends a POST request to the Microsoft Fabric API to create a new ML Model in the specified workspace. It supports optional parameters for ML Model description. .PARAMETER WorkspaceId @@ -24,10 +24,11 @@ - Calls `Test-TokenExpired` to ensure token validity before making the API request. Author: Tiago Balabuch - + #> -function New-FabricMLModel { - [CmdletBinding()] +function New-FabricMLModel +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -43,7 +44,8 @@ function New-FabricMLModel { [string]$MLModelDescription ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -58,62 +60,71 @@ function New-FabricMLModel { displayName = $MLModelName } - if ($MLModelDescription) { + if ($MLModelDescription) + { $body.description = $MLModelDescription } $bodyJson = $body | ConvertTo-Json -Depth 10 Write-Message -Message "Request Body: $bodyJson" -Level Debug - - # Step 4: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Post ` - -Body $bodyJson ` - -ContentType "application/json" ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -ResponseHeadersVariable "responseHeader" ` - -StatusCodeVariable "statusCode" + if ($PSCmdlet.ShouldProcess($MLModelName, "Create ML Model")) + { + # Step 4: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Post ` + -Body $bodyJson ` + -ContentType "application/json" ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -ResponseHeadersVariable "responseHeader" ` + -StatusCodeVariable "statusCode" + } # Step 5: Handle and log the response - switch ($statusCode) { - 201 { + switch ($statusCode) + { + 201 + { Write-Message -Message "ML Model '$MLModelName' created successfully!" -Level Info return $response } - 202 { + 202 + { Write-Message -Message "ML Model '$MLModelName' creation accepted. Provisioning in progress!" -Level Info - + [string]$operationId = $responseHeader["x-ms-operation-id"] [string]$location = $responseHeader["Location"] - [string]$retryAfter = $responseHeader["Retry-After"] + [string]$retryAfter = $responseHeader["Retry-After"] Write-Message -Message "Operation ID: '$operationId'" -Level Debug Write-Message -Message "Location: '$location'" -Level Debug Write-Message -Message "Retry-After: '$retryAfter'" -Level Debug Write-Message -Message "Getting Long Running Operation status" -Level Debug - + $operationStatus = Get-FabricLongRunningOperation -operationId $operationId Write-Message -Message "Long Running Operation status: $operationStatus" -Level Debug # Handle operation result - if ($operationStatus.status -eq "Succeeded") { + if ($operationStatus.status -eq "Succeeded") + { Write-Message -Message "Operation Succeeded" -Level Debug Write-Message -Message "Getting Long Running Operation result" -Level Debug - + $operationResult = Get-FabricLongRunningOperationResult -operationId $operationId Write-Message -Message "Long Running Operation status: $operationResult" -Level Debug - + return $operationResult } - else { + else + { Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Debug Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Error return $operationStatus - } + } } - default { + default + { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error Write-Message -Message "Error: $($response.message)" -Level Error Write-Message -Message "Error Details: $($response.moreDetails)" -Level Error @@ -122,7 +133,8 @@ function New-FabricMLModel { } } } - catch { + catch + { # Step 6: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to create ML Model. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/ML Model/Remove-FabricMLModel.ps1 b/source/Public/ML Model/Remove-FabricMLModel.ps1 similarity index 78% rename from FabricTools/public/ML Model/Remove-FabricMLModel.ps1 rename to source/Public/ML Model/Remove-FabricMLModel.ps1 index f1491d73..6542378a 100644 --- a/FabricTools/public/ML Model/Remove-FabricMLModel.ps1 +++ b/source/Public/ML Model/Remove-FabricMLModel.ps1 @@ -3,7 +3,7 @@ Removes an ML Model from a specified Microsoft Fabric workspace. .DESCRIPTION - This function sends a DELETE request to the Microsoft Fabric API to remove an ML Model + This function sends a DELETE request to the Microsoft Fabric API to remove an ML Model from the specified workspace using the provided WorkspaceId and MLModelId. .PARAMETER WorkspaceId @@ -21,10 +21,11 @@ - Calls `Test-TokenExpired` to ensure token validity before making the API request. Author: Tiago Balabuch - + #> -function Remove-FabricMLModel { - [CmdletBinding()] +function Remove-FabricMLModel +{ + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -34,7 +35,8 @@ function Remove-FabricMLModel { [ValidateNotNullOrEmpty()] [string]$MLModelId ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -43,18 +45,20 @@ function Remove-FabricMLModel { # Step 2: Construct the API URL $apiEndpointUrl = "{0}/workspaces/{1}/mlModels/{2}" -f $FabricConfig.BaseUrl, $WorkspaceId, $MLModelId Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug - - # Step 3: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Delete ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -StatusCodeVariable "statusCode" - + if ($PSCmdlet.ShouldProcess($apiEndpointUrl, "Remove ML Model")) + { + # Step 3: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Delete ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -StatusCodeVariable "statusCode" + } # Step 4: Validate the response code - if ($statusCode -ne 200) { + if ($statusCode -ne 200) + { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error Write-Message -Message "Error: $($response.message)" -Level Error Write-Message -Message "Error Details: $($response.moreDetails)" -Level Error @@ -62,9 +66,10 @@ function Remove-FabricMLModel { return $null } Write-Message -Message "ML Model '$MLModelId' deleted successfully from workspace '$WorkspaceId'." -Level Info - + } - catch { + catch + { # Step 5: Log and handle errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to delete ML Model '$MLModelId' from workspace '$WorkspaceId'. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/ML Model/Update-FabricMLModel.ps1 b/source/Public/ML Model/Update-FabricMLModel.ps1 similarity index 79% rename from FabricTools/public/ML Model/Update-FabricMLModel.ps1 rename to source/Public/ML Model/Update-FabricMLModel.ps1 index 50e2d6a0..2c78a81e 100644 --- a/FabricTools/public/ML Model/Update-FabricMLModel.ps1 +++ b/source/Public/ML Model/Update-FabricMLModel.ps1 @@ -3,7 +3,7 @@ Updates an existing ML Model in a specified Microsoft Fabric workspace. .DESCRIPTION - This function sends a PATCH request to the Microsoft Fabric API to update an existing ML Model + This function sends a PATCH request to the Microsoft Fabric API to update an existing ML Model in the specified workspace. It supports optional parameters for ML Model description. .PARAMETER WorkspaceId @@ -24,15 +24,16 @@ - Calls `Test-TokenExpired` to ensure token validity before making the API request. Author: Tiago Balabuch - + #> -function Update-FabricMLModel { - [CmdletBinding()] +function Update-FabricMLModel +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [string]$WorkspaceId, - + [string]$WorkspaceId, + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$MLModelId, @@ -42,7 +43,8 @@ function Update-FabricMLModel { [string]$MLModelDescription ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -60,20 +62,22 @@ function Update-FabricMLModel { # Convert the body to JSON $bodyJson = $body | ConvertTo-Json Write-Message -Message "Request Body: $bodyJson" -Level Debug - - # Step 4: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Patch ` - -Body $bodyJson ` - -ContentType "application/json" ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -StatusCodeVariable "statusCode" - + if ($PSCmdlet.ShouldProcess($apiEndpointUrl, "Update ML Model")) + { + # Step 4: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Patch ` + -Body $bodyJson ` + -ContentType "application/json" ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -StatusCodeVariable "statusCode" + } # Step 5: Validate the response code - if ($statusCode -ne 200) { + if ($statusCode -ne 200) + { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error Write-Message -Message "Error: $($response.message)" -Level Error Write-Message -Message "Error Details: $($response.moreDetails)" -Level Error @@ -85,7 +89,8 @@ function Update-FabricMLModel { Write-Message -Message "ML Model '$MLModelId' updated successfully!" -Level Info return $response } - catch { + catch + { # Step 7: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to update ML Model. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Mirrored Database/Get-FabricMirroredDatabase.ps1 b/source/Public/Mirrored Database/Get-FabricMirroredDatabase.ps1 similarity index 95% rename from FabricTools/public/Mirrored Database/Get-FabricMirroredDatabase.ps1 rename to source/Public/Mirrored Database/Get-FabricMirroredDatabase.ps1 index e5d93177..0129b89d 100644 --- a/FabricTools/public/Mirrored Database/Get-FabricMirroredDatabase.ps1 +++ b/source/Public/Mirrored Database/Get-FabricMirroredDatabase.ps1 @@ -1,4 +1,5 @@ -<# +function Get-FabricMirroredDatabase { + <# .SYNOPSIS Retrieves an MirroredDatabase or a list of MirroredDatabases from a specified workspace in Microsoft Fabric. @@ -8,6 +9,9 @@ The `Get-FabricMirroredDatabase` function sends a GET request to the Fabric API .PARAMETER WorkspaceId (Mandatory) The ID of the workspace to query MirroredDatabases. +.PARAMETER MirroredDatabaseId +(Optional) The ID of a specific MirroredDatabase to retrieve. + .PARAMETER MirroredDatabaseName (Optional) The name of the specific MirroredDatabase to retrieve. @@ -25,11 +29,9 @@ Retrieves all MirroredDatabases in workspace "12345". - Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. - Calls `Test-TokenExpired` to ensure token validity before making the API request. -Author: Tiago Balabuch - -#> +Author: Tiago Balabuch -function Get-FabricMirroredDatabase { + #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] @@ -64,24 +66,24 @@ function Get-FabricMirroredDatabase { if (-not ([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.GetName().Name -eq "System.Web" })) { Add-Type -AssemblyName System.Web } - + # Step 4: Loop to retrieve all capacities with continuation token Write-Message -Message "Loop started to get continuation token" -Level Debug $baseApiEndpointUrl = "{0}/workspaces/{1}/mirroredDatabases" -f $FabricConfig.BaseUrl, $WorkspaceId - + # Step 3: Loop to retrieve data with continuation token - + do { # Step 5: Construct the API URL $apiEndpointUrl = $baseApiEndpointUrl - + if ($null -ne $continuationToken) { # URL-encode the continuation token $encodedToken = [System.Web.HttpUtility]::UrlEncode($continuationToken) $apiEndpointUrl = "{0}?continuationToken={1}" -f $apiEndpointUrl, $encodedToken } Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug - + # Step 6: Make the API request $response = Invoke-RestMethod ` -Headers $FabricConfig.FabricHeaders ` @@ -91,7 +93,7 @@ function Get-FabricMirroredDatabase { -SkipHttpErrorCheck ` -ResponseHeadersVariable "responseHeader" ` -StatusCodeVariable "statusCode" - + # Step 7: Validate the response code if ($statusCode -ne 200) { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error @@ -100,39 +102,35 @@ function Get-FabricMirroredDatabase { Write-Message "Error Code: $($response.errorCode)" -Level Error return $null } - + # Step 8: Add data to the list if ($null -ne $response) { Write-Message -Message "Adding data to the list" -Level Debug $MirroredDatabases += $response.value - + # Update the continuation token if present if ($response.PSObject.Properties.Match("continuationToken")) { Write-Message -Message "Updating the continuation token" -Level Debug $continuationToken = $response.continuationToken Write-Message -Message "Continuation token: $continuationToken" -Level Debug - } - else { + } else { Write-Message -Message "Updating the continuation token to null" -Level Debug $continuationToken = $null } - } - else { + } else { Write-Message -Message "No data received from the API." -Level Warning break } } while ($null -ne $continuationToken) Write-Message -Message "Loop finished and all data added to the list" -Level Debug - + # Step 8: Filter results based on provided parameters $MirroredDatabase = if ($MirroredDatabaseId) { $MirroredDatabases | Where-Object { $_.Id -eq $MirroredDatabaseId } - } - elseif ($MirroredDatabaseName) { + } elseif ($MirroredDatabaseName) { $MirroredDatabases | Where-Object { $_.DisplayName -eq $MirroredDatabaseName } - } - else { + } else { # Return all MirroredDatabases if no filter is provided Write-Message -Message "No filter provided. Returning all MirroredDatabases." -Level Debug $MirroredDatabases @@ -142,16 +140,14 @@ function Get-FabricMirroredDatabase { if ($MirroredDatabase) { Write-Message -Message "MirroredDatabase found matching the specified criteria." -Level Debug return $MirroredDatabase - } - else { + } else { Write-Message -Message "No MirroredDatabase found matching the provided criteria." -Level Warning return $null } - } - catch { + } catch { # Step 10: Capture and log error details $errorDetails = $_.Exception.Message Write-Message -Message "Failed to retrieve MirroredDatabase. Error: $errorDetails" -Level Error - } - -} + } + +} \ No newline at end of file diff --git a/FabricTools/public/Mirrored Database/Get-FabricMirroredDatabaseDefinition.ps1 b/source/Public/Mirrored Database/Get-FabricMirroredDatabaseDefinition.ps1 similarity index 96% rename from FabricTools/public/Mirrored Database/Get-FabricMirroredDatabaseDefinition.ps1 rename to source/Public/Mirrored Database/Get-FabricMirroredDatabaseDefinition.ps1 index 2a7700cf..c1df17c8 100644 --- a/FabricTools/public/Mirrored Database/Get-FabricMirroredDatabaseDefinition.ps1 +++ b/source/Public/Mirrored Database/Get-FabricMirroredDatabaseDefinition.ps1 @@ -4,7 +4,7 @@ Retrieves the definition of a MirroredDatabase from a specific workspace in Microsoft Fabric. .DESCRIPTION -This function fetches the MirroredDatabase's content or metadata from a workspace. +This function fetches the MirroredDatabase's content or metadata from a workspace. Handles both synchronous and asynchronous operations, with detailed logging and error handling. .PARAMETER WorkspaceId @@ -73,37 +73,35 @@ function Get-FabricMirroredDatabaseDefinition { [string]$operationId = $responseHeader["x-ms-operation-id"] Write-Message -Message "Operation ID: '$operationId'" -Level Debug Write-Message -Message "Getting Long Running Operation status" -Level Debug - + $operationStatus = Get-FabricLongRunningOperation -operationId $operationId Write-Message -Message "Long Running Operation status: $operationStatus" -Level Debug # Handle operation result if ($operationStatus.status -eq "Succeeded") { Write-Message -Message "Operation Succeeded" -Level Debug Write-Message -Message "Getting Long Running Operation result" -Level Debug - + $operationResult = Get-FabricLongRunningOperationResult -operationId $operationId Write-Message -Message "Long Running Operation status: $operationResult" -Level Debug - + return $operationResult.definition.parts - } - else { + } else { Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Debug Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Error return $operationStatus - } + } } default { Write-Message -Message "Unexpected response code: $statusCode" -Level Error Write-Message -Message "Error details: $($response.message)" -Level Error throw "API request failed with status code $statusCode." } - + } - } - catch { + } catch { # Step 9: Capture and log error details $errorDetails = $_.Exception.Message Write-Message -Message "Failed to retrieve MirroredDatabase. Error: $errorDetails" -Level Error - } - -} + } + +} \ No newline at end of file diff --git a/FabricTools/public/Mirrored Database/Get-FabricMirroredDatabaseStatus.ps1 b/source/Public/Mirrored Database/Get-FabricMirroredDatabaseStatus.ps1 similarity index 70% rename from FabricTools/public/Mirrored Database/Get-FabricMirroredDatabaseStatus.ps1 rename to source/Public/Mirrored Database/Get-FabricMirroredDatabaseStatus.ps1 index 200788c3..c2583190 100644 --- a/FabricTools/public/Mirrored Database/Get-FabricMirroredDatabaseStatus.ps1 +++ b/source/Public/Mirrored Database/Get-FabricMirroredDatabaseStatus.ps1 @@ -1,4 +1,18 @@ function Get-FabricMirroredDatabaseStatus { + <# +.SYNOPSIS +Retrieves the status of a mirrored database in a specified workspace. +.DESCRIPTION +Retrieves the status of a mirrored database in a specified workspace. The function validates the authentication token, constructs the API endpoint URL, and makes a POST request to retrieve the mirroring status. +It handles errors and logs messages at various levels (Debug, Error). +.PARAMETER WorkspaceId +The ID of the workspace containing the mirrored database. +.PARAMETER MirroredDatabaseId +the ID of the mirrored database whose status is to be retrieved. +.EXAMPLE +Get-FabricMirroredDatabaseStatus -WorkspaceId "your-workspace-id" -MirroredDatabaseId "your-mirrored-database-id" +This example retrieves the status of a mirrored database with the specified ID in the specified workspace. + #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] @@ -15,10 +29,10 @@ function Get-FabricMirroredDatabaseStatus { Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired Write-Message -Message "Token validation completed." -Level Debug - + $apiEndpointUrl = "{0}/workspaces/{1}/mirroredDatabases/{2}/getMirroringStatus" -f $FabricConfig.BaseUrl, $WorkspaceId, $MirroredDatabaseId Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug - + # Step 6: Make the API request $response = Invoke-RestMethod ` -Headers $FabricConfig.FabricHeaders ` @@ -28,7 +42,7 @@ function Get-FabricMirroredDatabaseStatus { -SkipHttpErrorCheck ` -ResponseHeadersVariable "responseHeader" ` -StatusCodeVariable "statusCode" - + # Step 7: Validate the response code if ($statusCode -ne 200) { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error @@ -37,16 +51,15 @@ function Get-FabricMirroredDatabaseStatus { Write-Message "Error Code: $($response.errorCode)" -Level Error return $null } - + # Step 9: Handle results - + Write-Message -Message "Returning status of MirroredDatabases." -Level Debug return $response - } - catch { + } catch { # Step 10: Capture and log error details $errorDetails = $_.Exception.Message Write-Message -Message "Failed to retrieve MirroredDatabase. Error: $errorDetails" -Level Error - } - -} + } + +} \ No newline at end of file diff --git a/FabricTools/public/Mirrored Database/Get-FabricMirroredDatabaseTableStatus.ps1 b/source/Public/Mirrored Database/Get-FabricMirroredDatabaseTableStatus.ps1 similarity index 76% rename from FabricTools/public/Mirrored Database/Get-FabricMirroredDatabaseTableStatus.ps1 rename to source/Public/Mirrored Database/Get-FabricMirroredDatabaseTableStatus.ps1 index f37c4cfe..a8a8790f 100644 --- a/FabricTools/public/Mirrored Database/Get-FabricMirroredDatabaseTableStatus.ps1 +++ b/source/Public/Mirrored Database/Get-FabricMirroredDatabaseTableStatus.ps1 @@ -1,5 +1,22 @@ function Get-FabricMirroredDatabaseTableStatus { + <# +.SYNOPSIS +Retrieves the status of tables in a mirrored database. +.DESCRIPTION +Retrieves the status of tables in a mirrored database. The function validates the authentication token, constructs the API endpoint URL, and makes a POST request to retrieve the mirroring status of tables. It handles errors and logs messages at various levels (Debug, Error). +.PARAMETER WorkspaceId +The ID of the workspace containing the mirrored database. +.PARAMETER MirroredDatabaseId +The ID of the mirrored database whose table status is to be retrieved. +.EXAMPLE +Get-FabricMirroredDatabaseTableStatus -WorkspaceId "your-workspace-id" -MirroredDatabaseId "your-mirrored-database-id" +This example retrieves the status of tables in a mirrored database with the specified ID in the specified workspace. +.NOTES +The function retrieves the PowerBI access token and makes a POST request to the PowerBI API to retrieve the status of tables in the specified mirrored database. It then returns the 'value' property of the response, which contains the table statuses. + + #> [CmdletBinding()] + [OutputType([System.Object[]])] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -23,24 +40,24 @@ function Get-FabricMirroredDatabaseTableStatus { if (-not ([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.GetName().Name -eq "System.Web" })) { Add-Type -AssemblyName System.Web } - + # Step 4: Loop to retrieve all capacities with continuation token Write-Message -Message "Loop started to get continuation token" -Level Debug $baseApiEndpointUrl = "{0}/workspaces/{1}/mirroredDatabases/{2}/getTablesMirroringStatus" -f $FabricConfig.BaseUrl, $WorkspaceId, $MirroredDatabaseId - + # Step 3: Loop to retrieve data with continuation token - + do { # Step 5: Construct the API URL $apiEndpointUrl = $baseApiEndpointUrl - + if ($null -ne $continuationToken) { # URL-encode the continuation token $encodedToken = [System.Web.HttpUtility]::UrlEncode($continuationToken) $apiEndpointUrl = "{0}?continuationToken={1}" -f $apiEndpointUrl, $encodedToken } Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug - + # Step 6: Make the API request $response = Invoke-RestMethod ` -Headers $FabricConfig.FabricHeaders ` @@ -50,7 +67,7 @@ function Get-FabricMirroredDatabaseTableStatus { -SkipHttpErrorCheck ` -ResponseHeadersVariable "responseHeader" ` -StatusCodeVariable "statusCode" - + # Step 7: Validate the response code if ($statusCode -ne 200) { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error @@ -59,24 +76,22 @@ function Get-FabricMirroredDatabaseTableStatus { Write-Message "Error Code: $($response.errorCode)" -Level Error return $null } - + # Step 8: Add data to the list if ($null -ne $response) { Write-Message -Message "Adding data to the list" -Level Debug $MirroredDatabaseTableStatus += $response.data - + # Update the continuation token if present if ($response.PSObject.Properties.Match("continuationToken")) { Write-Message -Message "Updating the continuation token" -Level Debug $continuationToken = $response.continuationToken Write-Message -Message "Continuation token: $continuationToken" -Level Debug - } - else { + } else { Write-Message -Message "Updating the continuation token to null" -Level Debug $continuationToken = $null } - } - else { + } else { Write-Message -Message "No data received from the API." -Level Warning break } @@ -84,14 +99,13 @@ function Get-FabricMirroredDatabaseTableStatus { Write-Message -Message "Loop finished and all data added to the list" -Level Debug # Step 9: Handle results - # Return all Mirrored Database Table Status + # Return all Mirrored Database Table Status Write-Message -Message "No filter provided. Returning all MirroredDatabases." -Level Debug $MirroredDatabaseTableStatus - } - catch { + } catch { # Step 10: Capture and log error details $errorDetails = $_.Exception.Message Write-Message -Message "Failed to retrieve MirroredDatabase. Error: $errorDetails" -Level Error - } - + } + } diff --git a/FabricTools/public/Mirrored Database/New-FabricMirroredDatabase.ps1 b/source/Public/Mirrored Database/New-FabricMirroredDatabase.ps1 similarity index 80% rename from FabricTools/public/Mirrored Database/New-FabricMirroredDatabase.ps1 rename to source/Public/Mirrored Database/New-FabricMirroredDatabase.ps1 index 70b4710d..98957dad 100644 --- a/FabricTools/public/Mirrored Database/New-FabricMirroredDatabase.ps1 +++ b/source/Public/Mirrored Database/New-FabricMirroredDatabase.ps1 @@ -3,8 +3,8 @@ Creates a new MirroredDatabase in a specified Microsoft Fabric workspace. .DESCRIPTION -This function sends a POST request to the Microsoft Fabric API to create a new MirroredDatabase -in the specified workspace. It supports optional parameters for MirroredDatabase description +This function sends a POST request to the Microsoft Fabric API to create a new MirroredDatabase +in the specified workspace. It supports optional parameters for MirroredDatabase description and path definitions for the MirroredDatabase content. .PARAMETER WorkspaceId @@ -29,12 +29,13 @@ An optional path to the platform-specific definition (e.g., .platform file) to u - Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. - Calls `Test-TokenExpired` to ensure token validity before making the API request. -Author: Tiago Balabuch +Author: Tiago Balabuch #> -function New-FabricMirroredDatabase { - [CmdletBinding()] +function New-FabricMirroredDatabase +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -52,13 +53,14 @@ function New-FabricMirroredDatabase { [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$MirroredDatabasePathDefinition, - + [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$MirroredDatabasePathPlatformDefinition ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -73,18 +75,22 @@ function New-FabricMirroredDatabase { displayName = $MirroredDatabaseName } - if ($MirroredDatabaseDescription) { + if ($MirroredDatabaseDescription) + { $body.description = $MirroredDatabaseDescription } - if ($MirroredDatabasePathDefinition) { + if ($MirroredDatabasePathDefinition) + { $MirroredDatabaseEncodedContent = Convert-ToBase64 -filePath $MirroredDatabasePathDefinition - if (-not [string]::IsNullOrEmpty($MirroredDatabaseEncodedContent)) { + if (-not [string]::IsNullOrEmpty($MirroredDatabaseEncodedContent)) + { # Initialize definition if it doesn't exist - if (-not $body.definition) { + if (-not $body.definition) + { $body.definition = @{ - parts = @() + parts = @() } } @@ -95,18 +101,22 @@ function New-FabricMirroredDatabase { payloadType = "InlineBase64" } } - else { + else + { Write-Message -Message "Invalid or empty content in MirroredDatabase definition." -Level Error return $null } } - if ($MirroredDatabasePathPlatformDefinition) { + if ($MirroredDatabasePathPlatformDefinition) + { $MirroredDatabaseEncodedPlatformContent = Convert-ToBase64 -filePath $MirroredDatabasePathPlatformDefinition - if (-not [string]::IsNullOrEmpty($MirroredDatabaseEncodedPlatformContent)) { + if (-not [string]::IsNullOrEmpty($MirroredDatabaseEncodedPlatformContent)) + { # Initialize definition if it doesn't exist - if (-not $body.definition) { + if (-not $body.definition) + { $body.definition = @{ format = "MirroredDatabase" parts = @() @@ -120,7 +130,8 @@ function New-FabricMirroredDatabase { payloadType = "InlineBase64" } } - else { + else + { Write-Message -Message "Invalid or empty content in platform definition." -Level Error return $null } @@ -128,57 +139,66 @@ function New-FabricMirroredDatabase { $bodyJson = $body | ConvertTo-Json -Depth 10 Write-Message -Message "Request Body: $bodyJson" -Level Debug - - # Step 4: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Post ` - -Body $bodyJson ` - -ContentType "application/json" ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -ResponseHeadersVariable "responseHeader" ` - -StatusCodeVariable "statusCode" + if ($PSCmdlet.ShouldProcess($MirroredDatabaseName, "Create MirroredDatabase")) + { + # Step 4: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Post ` + -Body $bodyJson ` + -ContentType "application/json" ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -ResponseHeadersVariable "responseHeader" ` + -StatusCodeVariable "statusCode" + } # Step 5: Handle and log the response - switch ($statusCode) { - 201 { + switch ($statusCode) + { + 201 + { Write-Message -Message "MirroredDatabase '$MirroredDatabaseName' created successfully!" -Level Info return $response } - 202 { + 202 + { Write-Message -Message "MirroredDatabase '$MirroredDatabaseName' creation accepted. Provisioning in progress!" -Level Info - + [string]$operationId = $responseHeader["x-ms-operation-id"] Write-Message -Message "Operation ID: '$operationId'" -Level Debug Write-Message -Message "Getting Long Running Operation status" -Level Debug - + $operationStatus = Get-FabricLongRunningOperation -operationId $operationId Write-Message -Message "Long Running Operation status: $operationStatus" -Level Debug # Handle operation result - if ($operationStatus.status -eq "Succeeded") { + if ($operationStatus.status -eq "Succeeded") + { Write-Message -Message "Operation Succeeded" -Level Debug Write-Message -Message "Getting Long Running Operation result" -Level Debug - + $operationResult = Get-FabricLongRunningOperationResult -operationId $operationId Write-Message -Message "Long Running Operation status: $operationResult" -Level Debug - + return $operationResult } - else { + else + { Write-Message -Message "Operation Failed" -Level Debug return $operationStatus - } + } } - default { + default + { Write-Message -Message "Unexpected response code: $statusCode" -Level Error Write-Message -Message "Error details: $($response.message)" -Level Error throw "API request failed with status code $statusCode." } } } - catch { + catch + { # Step 6: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to create MirroredDatabase. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Mirrored Database/Remove-FabricMirroredDatabase.ps1 b/source/Public/Mirrored Database/Remove-FabricMirroredDatabase.ps1 similarity index 78% rename from FabricTools/public/Mirrored Database/Remove-FabricMirroredDatabase.ps1 rename to source/Public/Mirrored Database/Remove-FabricMirroredDatabase.ps1 index 701e513d..440cc1c0 100644 --- a/FabricTools/public/Mirrored Database/Remove-FabricMirroredDatabase.ps1 +++ b/source/Public/Mirrored Database/Remove-FabricMirroredDatabase.ps1 @@ -23,8 +23,9 @@ Deletes the MirroredDatabase with ID "67890" from workspace "12345". Author: Tiago Balabuch #> -function Remove-FabricMirroredDatabase { - [CmdletBinding()] +function Remove-FabricMirroredDatabase +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -35,7 +36,8 @@ function Remove-FabricMirroredDatabase { [string]$MirroredDatabaseId ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -44,27 +46,31 @@ function Remove-FabricMirroredDatabase { # Step 2: Construct the API URL $apiEndpointUrl = "{0}/workspaces/{1}/mirroredDatabases/{2}" -f $FabricConfig.BaseUrl, $WorkspaceId, $MirroredDatabaseId Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug - - # Step 3: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Delete ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -StatusCodeVariable "statusCode" + if ($PSCmdlet.ShouldProcess($apiEndpointUrl, "Remove MirroredDatabase")) + { + # Step 3: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Delete ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -StatusCodeVariable "statusCode" + } # Step 4: Validate the response code - if ($statusCode -ne 200) { + if ($statusCode -ne 200) + { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error Write-Message -Message "Error: $($response.message)" -Level Error Write-Message "Error Code: $($response.errorCode)" -Level Error return $null } Write-Message -Message "MirroredDatabase '$MirroredDatabaseId' deleted successfully from workspace '$WorkspaceId'." -Level Info - + } - catch { + catch + { # Step 5: Log and handle errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to delete MirroredDatabase '$MirroredDatabaseId' from workspace '$WorkspaceId'. Error: $errorDetails" -Level Error diff --git a/source/Public/Mirrored Database/Start-FabricMirroredDatabaseMirroring.ps1 b/source/Public/Mirrored Database/Start-FabricMirroredDatabaseMirroring.ps1 new file mode 100644 index 00000000..5525eb20 --- /dev/null +++ b/source/Public/Mirrored Database/Start-FabricMirroredDatabaseMirroring.ps1 @@ -0,0 +1,76 @@ +function Start-FabricMirroredDatabaseMirroring +{ + <# +.SYNOPSIS + Starts the mirroring of a specified mirrored database in a given workspace. +.DESCRIPTION + This function sends a POST request to the Microsoft Fabric API to start the mirroring of a specified mirrored database. + It requires the workspace ID and the mirrored database ID as parameters. +.PARAMETER WorkspaceId + The unique identifier of the workspace where the mirrored database resides. This parameter is mandatory. +.PARAMETER MirroredDatabaseId + The unique identifier of the mirrored database to be started. This parameter is mandatory. +.EXAMPLE + Start-FabricMirroredDatabaseMirroring -WorkspaceId "12345" -MirroredDatabaseId "67890" + Starts the mirroring of the mirrored database with ID `67890` in the workspace `12345`. +.NOTES + - Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. + - Calls `Test-TokenExpired` to ensure token validity before making the API request. + - This function handles asynchronous operations and retrieves operation results if required. + + #> + [CmdletBinding(SupportsShouldProcess)] + param ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [string]$WorkspaceId, + + [Parameter(Mandatory = $false)] + [ValidateNotNullOrEmpty()] + [string]$MirroredDatabaseId + ) + + try + { + # Step 2: Ensure token validity + Write-Message -Message "Validating token..." -Level Debug + Test-TokenExpired + Write-Message -Message "Token validation completed." -Level Debug + + $apiEndpointUrl = "{0}/workspaces/{1}/mirroredDatabases/{2}/startMirroring" -f $FabricConfig.BaseUrl, $WorkspaceId, $MirroredDatabaseId + Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug + if ($PSCmdlet.ShouldProcess($apiEndpointUrl, "Start MirroredDatabase Mirroring")) + { + # Step 6: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Post ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -ResponseHeadersVariable "responseHeader" ` + -StatusCodeVariable "statusCode" + } + + # Step 7: Validate the response code + if ($statusCode -ne 200) + { + Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error + Write-Message -Message "Error: $($response.message)" -Level Error + Write-Message -Message "Error Details: $($response.moreDetails)" -Level Error + Write-Message "Error Code: $($response.errorCode)" -Level Error + return $null + } + + # Step 9: Handle results + Write-Message -Message "Database mirroring started successfully for MirroredDatabaseId: $MirroredDatabaseId" -Level Info + return + } + catch + { + # Step 10: Capture and log error details + $errorDetails = $_.Exception.Message + Write-Message -Message "Failed to start MirroredDatabase. Error: $errorDetails" -Level Error + } + +} diff --git a/source/Public/Mirrored Database/Stop-FabricMirroredDatabaseMirroring.ps1 b/source/Public/Mirrored Database/Stop-FabricMirroredDatabaseMirroring.ps1 new file mode 100644 index 00000000..eb2b686a --- /dev/null +++ b/source/Public/Mirrored Database/Stop-FabricMirroredDatabaseMirroring.ps1 @@ -0,0 +1,80 @@ +function Stop-FabricMirroredDatabaseMirroring +{ + <# +.SYNOPSIS + Stops the mirroring of a specified mirrored database in a given workspace. +.DESCRIPTION + This function sends a POST request to the Microsoft Fabric API to stop the mirroring of a specified mirrored database. + It requires the workspace ID and the mirrored database ID as parameters. + +.PARAMETER WorkspaceId + The unique identifier of the workspace where the mirrored database resides. This parameter is mandatory. + +.PARAMETER MirroredDatabaseId + The unique identifier of the mirrored database to be stopped. This parameter is mandatory. + +.EXAMPLE + Stop-FabricMirroredDatabaseMirroring -WorkspaceId "12345" -MirroredDatabaseId "67890" + Stops the mirroring of the mirrored database with ID `67890` in the workspace `12345`. + +.NOTES + - Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. + - Calls `Test-TokenExpired` to ensure token validity before making the API request. + - This function handles asynchronous operations and retrieves operation results if required. + + #> + [CmdletBinding(SupportsShouldProcess)] + param ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [string]$WorkspaceId, + + [Parameter(Mandatory = $false)] + [ValidateNotNullOrEmpty()] + [string]$MirroredDatabaseId + ) + + try + { + # Step 2: Ensure token validity + Write-Message -Message "Validating token..." -Level Debug + Test-TokenExpired + Write-Message -Message "Token validation completed." -Level Debug + + $apiEndpointUrl = "{0}/workspaces/{1}/mirroredDatabases/{2}/stopMirroring" -f $FabricConfig.BaseUrl, $WorkspaceId, $MirroredDatabaseId + Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug + if ($PSCmdlet.ShouldProcess($apiEndpointUrl, "Stop MirroredDatabase Mirroring")) + { + # Step 6: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Post ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -ResponseHeadersVariable "responseHeader" ` + -StatusCodeVariable "statusCode" + } + + # Step 7: Validate the response code + if ($statusCode -ne 200) + { + Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error + Write-Message -Message "Error: $($response.message)" -Level Error + Write-Message -Message "Error Details: $($response.moreDetails)" -Level Error + Write-Message "Error Code: $($response.errorCode)" -Level Error + return $null + } + + # Step 9: Handle results + Write-Message -Message "Database mirroring stopped successfully for MirroredDatabaseId: $MirroredDatabaseId" -Level Info + return + } + catch + { + # Step 10: Capture and log error details + $errorDetails = $_.Exception.Message + Write-Message -Message "Failed to stop MirroredDatabase. Error: $errorDetails" -Level Error + } + +} diff --git a/FabricTools/public/Mirrored Database/Update-FabricMirroredDatabase.ps1 b/source/Public/Mirrored Database/Update-FabricMirroredDatabase.ps1 similarity index 77% rename from FabricTools/public/Mirrored Database/Update-FabricMirroredDatabase.ps1 rename to source/Public/Mirrored Database/Update-FabricMirroredDatabase.ps1 index 09691385..734bae10 100644 --- a/FabricTools/public/Mirrored Database/Update-FabricMirroredDatabase.ps1 +++ b/source/Public/Mirrored Database/Update-FabricMirroredDatabase.ps1 @@ -5,6 +5,9 @@ Updates the properties of a Fabric MirroredDatabase. .DESCRIPTION The `Update-FabricMirroredDatabase` function updates the name and/or description of a specified Fabric MirroredDatabase by making a PATCH request to the API. +.PARAMETER WorkspaceId +(Mandatory) The unique identifier of the workspace where the MirroredDatabase resides. + .PARAMETER MirroredDatabaseId The unique identifier of the MirroredDatabase to be updated. @@ -28,17 +31,18 @@ Updates both the name and description of the MirroredDatabase "MirroredDatabase1 - Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. - Calls `Test-TokenExpired` to ensure token validity before making the API request. -Author: Tiago Balabuch +Author: Tiago Balabuch #> -function Update-FabricMirroredDatabase { - [CmdletBinding()] +function Update-FabricMirroredDatabase +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [string]$WorkspaceId, - + [string]$WorkspaceId, + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$MirroredDatabaseId, @@ -53,7 +57,8 @@ function Update-FabricMirroredDatabase { [string]$MirroredDatabaseDescription ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -68,27 +73,31 @@ function Update-FabricMirroredDatabase { displayName = $MirroredDatabaseName } - if ($MirroredDatabaseDescription) { + if ($MirroredDatabaseDescription) + { $body.description = $MirroredDatabaseDescription } # Convert the body to JSON $bodyJson = $body | ConvertTo-Json Write-Message -Message "Request Body: $bodyJson" -Level Debug - - # Step 4: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Patch ` - -Body $bodyJson ` - -ContentType "application/json" ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -StatusCodeVariable "statusCode" + if ($PSCmdlet.ShouldProcess($MirroredDatabaseId, "Update MirroredDatabase")) + { + # Step 4: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Patch ` + -Body $bodyJson ` + -ContentType "application/json" ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -StatusCodeVariable "statusCode" + } # Step 5: Validate the response code - if ($statusCode -ne 200) { + if ($statusCode -ne 200) + { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error Write-Message -Message "Error: $($response.message)" -Level Error Write-Message "Error Code: $($response.errorCode)" -Level Error @@ -99,7 +108,8 @@ function Update-FabricMirroredDatabase { Write-Message -Message "MirroredDatabase '$MirroredDatabaseName' updated successfully!" -Level Info return $response } - catch { + catch + { # Step 7: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to update MirroredDatabase. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Mirrored Database/Update-FabricMirroredDatabaseDefinition.ps1 b/source/Public/Mirrored Database/Update-FabricMirroredDatabaseDefinition.ps1 similarity index 80% rename from FabricTools/public/Mirrored Database/Update-FabricMirroredDatabaseDefinition.ps1 rename to source/Public/Mirrored Database/Update-FabricMirroredDatabaseDefinition.ps1 index 5fc7fb71..5e76ec63 100644 --- a/FabricTools/public/Mirrored Database/Update-FabricMirroredDatabaseDefinition.ps1 +++ b/source/Public/Mirrored Database/Update-FabricMirroredDatabaseDefinition.ps1 @@ -3,7 +3,7 @@ Updates the definition of a MirroredDatabase in a Microsoft Fabric workspace. .DESCRIPTION -This function allows updating the content or metadata of a MirroredDatabase in a Microsoft Fabric workspace. +This function allows updating the content or metadata of a MirroredDatabase in a Microsoft Fabric workspace. The MirroredDatabase content can be provided as file paths, and metadata updates can optionally be enabled. .PARAMETER WorkspaceId @@ -19,7 +19,7 @@ The MirroredDatabase content can be provided as file paths, and metadata updates (Optional) The file path to the MirroredDatabase's platform-specific definition file. The content will be encoded as Base64 and sent in the request. .PARAMETER UpdateMetadata -(Optional)A boolean flag indicating whether to update the MirroredDatabase's metadata. +(Optional)A boolean flag indicating whether to update the MirroredDatabase's metadata. Default: `$false`. .EXAMPLE @@ -38,12 +38,13 @@ Updates both the content and metadata of the MirroredDatabase with ID `67890` in - The MirroredDatabase content is encoded as Base64 before being sent to the Fabric API. - This function handles asynchronous operations and retrieves operation results if required. -Author: Tiago Balabuch +Author: Tiago Balabuch #> -function Update-FabricMirroredDatabaseDefinition { - [CmdletBinding()] +function Update-FabricMirroredDatabaseDefinition +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -56,13 +57,14 @@ function Update-FabricMirroredDatabaseDefinition { [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$MirroredDatabasePathDefinition, - + [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$MirroredDatabasePathPlatformDefinition ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -71,22 +73,25 @@ function Update-FabricMirroredDatabaseDefinition { # Step 2: Construct the API URL $apiEndpointUrl = "{0}/workspaces/{1}/mirroredDatabases/{2}/updateDefinition" -f $FabricConfig.BaseUrl, $WorkspaceId, $MirroredDatabaseId - if($MirroredDatabasePathPlatformDefinition){ - $apiEndpointUrl = "?updateMetadata=true" -f $apiEndpointUrl + if ($MirroredDatabasePathPlatformDefinition) + { + $apiEndpointUrl = "?updateMetadata=true" -f $apiEndpointUrl } Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug # Step 3: Construct the request body $body = @{ definition = @{ - parts = @() - } + parts = @() + } } - - if ($MirroredDatabasePathDefinition) { + + if ($MirroredDatabasePathDefinition) + { $MirroredDatabaseEncodedContent = Convert-ToBase64 -filePath $MirroredDatabasePathDefinition - - if (-not [string]::IsNullOrEmpty($MirroredDatabaseEncodedContent)) { + + if (-not [string]::IsNullOrEmpty($MirroredDatabaseEncodedContent)) + { # Add new part to the parts array $body.definition.parts += @{ path = "MirroredDatabase.json" @@ -94,15 +99,18 @@ function Update-FabricMirroredDatabaseDefinition { payloadType = "InlineBase64" } } - else { + else + { Write-Message -Message "Invalid or empty content in MirroredDatabase definition." -Level Error return $null } } - if ($MirroredDatabasePathPlatformDefinition) { + if ($MirroredDatabasePathPlatformDefinition) + { $MirroredDatabaseEncodedPlatformContent = Convert-ToBase64 -filePath $MirroredDatabasePathPlatformDefinition - if (-not [string]::IsNullOrEmpty($MirroredDatabaseEncodedPlatformContent)) { + if (-not [string]::IsNullOrEmpty($MirroredDatabaseEncodedPlatformContent)) + { # Add new part to the parts array $body.definition.parts += @{ path = ".platform" @@ -110,7 +118,8 @@ function Update-FabricMirroredDatabaseDefinition { payloadType = "InlineBase64" } } - else { + else + { Write-Message -Message "Invalid or empty content in platform definition." -Level Error return $null } @@ -118,49 +127,58 @@ function Update-FabricMirroredDatabaseDefinition { $bodyJson = $body | ConvertTo-Json -Depth 10 Write-Message -Message "Request Body: $bodyJson" -Level Debug + if ($PSCmdlet.ShouldProcess($MirroredDatabaseId, "Update MirroredDatabase")) + { + # Step 4: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Post ` + -Body $bodyJson ` + -ContentType "application/json" ` + -ErrorAction Stop ` + -ResponseHeadersVariable "responseHeader" ` + -StatusCodeVariable "statusCode" + } - # Step 4: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Post ` - -Body $bodyJson ` - -ContentType "application/json" ` - -ErrorAction Stop ` - -ResponseHeadersVariable "responseHeader" ` - -StatusCodeVariable "statusCode" - # Step 5: Handle and log the response - switch ($statusCode) { - 200 { + switch ($statusCode) + { + 200 + { Write-Message -Message "Update definition for MirroredDatabase '$MirroredDatabaseId' created successfully!" -Level Info return $response } - 202 { + 202 + { Write-Message -Message "Update definition for MirroredDatabase '$MirroredDatabaseId' accepted. Operation in progress!" -Level Info [string]$operationId = $responseHeader["x-ms-operation-id"] $operationResult = Get-FabricLongRunningOperation -operationId $operationId # Handle operation result - if ($operationResult.status -eq "Succeeded") { + if ($operationResult.status -eq "Succeeded") + { Write-Message -Message "Operation Succeeded" -Level Debug - + $result = Get-FabricLongRunningOperationResult -operationId $operationId return $result.definition.parts } - else { + else + { Write-Message -Message "Operation Failed" -Level Debug return $operationResult.definition.parts - } - } - default { + } + } + default + { Write-Message -Message "Unexpected response code: $statusCode" -Level Error Write-Message -Message "Error details: $($response.message)" -Level Error throw "API request failed with status code $statusCode." } } } - catch { + catch + { # Step 6: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to update MirroredDatabase. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Mirrored Warehouse/Get-FabricMirroredWarehouse.ps1 b/source/Public/Mirrored Warehouse/Get-FabricMirroredWarehouse.ps1 similarity index 95% rename from FabricTools/public/Mirrored Warehouse/Get-FabricMirroredWarehouse.ps1 rename to source/Public/Mirrored Warehouse/Get-FabricMirroredWarehouse.ps1 index a5775d3f..8e32b5dc 100644 --- a/FabricTools/public/Mirrored Warehouse/Get-FabricMirroredWarehouse.ps1 +++ b/source/Public/Mirrored Warehouse/Get-FabricMirroredWarehouse.ps1 @@ -1,4 +1,5 @@ -<# +function Get-FabricMirroredWarehouse { + <# .SYNOPSIS Retrieves an MirroredWarehouse or a list of MirroredWarehouses from a specified workspace in Microsoft Fabric. @@ -8,6 +9,9 @@ The `Get-FabricMirroredWarehouse` function sends a GET request to the Fabric API .PARAMETER WorkspaceId (Mandatory) The ID of the workspace to query MirroredWarehouses. +.PARAMETER MirroredWarehouseId +(Optional) The ID of a specific MirroredWarehouse to retrieve. + .PARAMETER MirroredWarehouseName (Optional) The name of the specific MirroredWarehouse to retrieve. @@ -25,11 +29,9 @@ Retrieves all MirroredWarehouses in workspace "12345". - Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. - Calls `Test-TokenExpired` to ensure token validity before making the API request. -Author: Tiago Balabuch - -#> +Author: Tiago Balabuch -function Get-FabricMirroredWarehouse { + #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] @@ -64,24 +66,24 @@ function Get-FabricMirroredWarehouse { if (-not ([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.GetName().Name -eq "System.Web" })) { Add-Type -AssemblyName System.Web } - + # Step 4: Loop to retrieve all capacities with continuation token Write-Message -Message "Loop started to get continuation token" -Level Debug $baseApiEndpointUrl = "{0}/workspaces/{1}/MirroredWarehouses" -f $FabricConfig.BaseUrl, $WorkspaceId - + # Step 3: Loop to retrieve data with continuation token - + do { # Step 5: Construct the API URL $apiEndpointUrl = $baseApiEndpointUrl - + if ($null -ne $continuationToken) { # URL-encode the continuation token $encodedToken = [System.Web.HttpUtility]::UrlEncode($continuationToken) $apiEndpointUrl = "{0}?continuationToken={1}" -f $apiEndpointUrl, $encodedToken } Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug - + # Step 6: Make the API request $response = Invoke-RestMethod ` -Headers $FabricConfig.FabricHeaders ` @@ -91,7 +93,7 @@ function Get-FabricMirroredWarehouse { -SkipHttpErrorCheck ` -ResponseHeadersVariable "responseHeader" ` -StatusCodeVariable "statusCode" - + # Step 7: Validate the response code if ($statusCode -ne 200) { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error @@ -100,39 +102,35 @@ function Get-FabricMirroredWarehouse { Write-Message "Error Code: $($response.errorCode)" -Level Error return $null } - + # Step 8: Add data to the list if ($null -ne $response) { Write-Message -Message "Adding data to the list" -Level Debug $MirroredWarehouses += $response.value - + # Update the continuation token if present if ($response.PSObject.Properties.Match("continuationToken")) { Write-Message -Message "Updating the continuation token" -Level Debug $continuationToken = $response.continuationToken Write-Message -Message "Continuation token: $continuationToken" -Level Debug - } - else { + } else { Write-Message -Message "Updating the continuation token to null" -Level Debug $continuationToken = $null } - } - else { + } else { Write-Message -Message "No data received from the API." -Level Warning break } } while ($null -ne $continuationToken) Write-Message -Message "Loop finished and all data added to the list" -Level Debug - + # Step 8: Filter results based on provided parameters $MirroredWarehouse = if ($MirroredWarehouseId) { $MirroredWarehouses | Where-Object { $_.Id -eq $MirroredWarehouseId } - } - elseif ($MirroredWarehouseName) { + } elseif ($MirroredWarehouseName) { $MirroredWarehouses | Where-Object { $_.DisplayName -eq $MirroredWarehouseName } - } - else { + } else { # Return all MirroredWarehouses if no filter is provided Write-Message -Message "No filter provided. Returning all MirroredWarehouses." -Level Debug $MirroredWarehouses @@ -142,16 +140,14 @@ function Get-FabricMirroredWarehouse { if ($MirroredWarehouse) { Write-Message -Message "MirroredWarehouse found matching the specified criteria." -Level Debug return $MirroredWarehouse - } - else { + } else { Write-Message -Message "No MirroredWarehouse found matching the provided criteria." -Level Warning return $null } - } - catch { + } catch { # Step 10: Capture and log error details $errorDetails = $_.Exception.Message Write-Message -Message "Failed to retrieve MirroredWarehouse. Error: $errorDetails" -Level Error - } - -} + } + +} \ No newline at end of file diff --git a/FabricTools/public/Notebook/Get-FabricNotebook.ps1 b/source/Public/Notebook/Get-FabricNotebook.ps1 similarity index 94% rename from FabricTools/public/Notebook/Get-FabricNotebook.ps1 rename to source/Public/Notebook/Get-FabricNotebook.ps1 index bd759f9a..8f9785ad 100644 --- a/FabricTools/public/Notebook/Get-FabricNotebook.ps1 +++ b/source/Public/Notebook/Get-FabricNotebook.ps1 @@ -1,4 +1,5 @@ -<# +function Get-FabricNotebook { + <# .SYNOPSIS Retrieves an Notebook or a list of Notebooks from a specified workspace in Microsoft Fabric. @@ -8,6 +9,9 @@ The `Get-FabricNotebook` function sends a GET request to the Fabric API to retri .PARAMETER WorkspaceId (Mandatory) The ID of the workspace to query Notebooks. +.PARAMETER NotebookId +(Optional) The ID of a specific Notebook to retrieve. + .PARAMETER NotebookName (Optional) The name of the specific Notebook to retrieve. @@ -25,11 +29,9 @@ Retrieves all Notebooks in workspace "12345". - Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. - Calls `Test-TokenExpired` to ensure token validity before making the API request. -Author: Tiago Balabuch - -#> +Author: Tiago Balabuch -function Get-FabricNotebook { + #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] @@ -60,27 +62,27 @@ function Get-FabricNotebook { # Step 3: Initialize variables $continuationToken = $null $notebooks = @() - + if (-not ([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.GetName().Name -eq "System.Web" })) { Add-Type -AssemblyName System.Web } - + # Step 4: Loop to retrieve all capacities with continuation token Write-Message -Message "Loop started to get continuation token" -Level Debug $baseApiEndpointUrl = "{0}/workspaces/{1}/notebooks" -f $FabricConfig.BaseUrl, $WorkspaceId - + do { # Step 5: Construct the API URL $apiEndpointUrl = $baseApiEndpointUrl - + if ($null -ne $continuationToken) { # URL-encode the continuation token $encodedToken = [System.Web.HttpUtility]::UrlEncode($continuationToken) $apiEndpointUrl = "{0}?continuationToken={1}" -f $apiEndpointUrl, $encodedToken } Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug - + # Step 6: Make the API request $response = Invoke-RestMethod ` -Headers $FabricConfig.FabricHeaders ` @@ -90,7 +92,7 @@ function Get-FabricNotebook { -SkipHttpErrorCheck ` -ResponseHeadersVariable "responseHeader" ` -StatusCodeVariable "statusCode" - + # Step 7: Validate the response code if ($statusCode -ne 200) { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error @@ -99,38 +101,34 @@ function Get-FabricNotebook { Write-Message "Error Code: $($response.errorCode)" -Level Error return $null } - + # Step 8: Add data to the list if ($null -ne $response) { Write-Message -Message "Adding data to the list" -Level Debug $notebooks += $response.value - + # Update the continuation token if present if ($response.PSObject.Properties.Match("continuationToken")) { Write-Message -Message "Updating the continuation token" -Level Debug $continuationToken = $response.continuationToken Write-Message -Message "Continuation token: $continuationToken" -Level Debug - } - else { + } else { Write-Message -Message "Updating the continuation token to null" -Level Debug $continuationToken = $null } - } - else { + } else { Write-Message -Message "No data received from the API." -Level Warning break } } while ($null -ne $continuationToken) Write-Message -Message "Loop finished and all data added to the list" -Level Debug - + # Step 8: Filter results based on provided parameters $notebook = if ($NotebookId) { $notebooks | Where-Object { $_.Id -eq $NotebookId } - } - elseif ($NotebookName) { + } elseif ($NotebookName) { $notebooks | Where-Object { $_.DisplayName -eq $NotebookName } - } - else { + } else { # Return all notebooks if no filter is provided Write-Message -Message "No filter provided. Returning all Notebooks." -Level Debug $notebooks @@ -140,16 +138,14 @@ function Get-FabricNotebook { if ($notebook) { Write-Message -Message "Notebook found matching the specified criteria." -Level Debug return $notebook - } - else { + } else { Write-Message -Message "No notebook found matching the provided criteria." -Level Warning return $null } - } - catch { + } catch { # Step 10: Capture and log error details $errorDetails = $_.Exception.Message Write-Message -Message "Failed to retrieve Notebook. Error: $errorDetails" -Level Error - } - -} + } + +} \ No newline at end of file diff --git a/FabricTools/public/Notebook/Get-FabricNotebookDefinition.ps1 b/source/Public/Notebook/Get-FabricNotebookDefinition.ps1 similarity index 96% rename from FabricTools/public/Notebook/Get-FabricNotebookDefinition.ps1 rename to source/Public/Notebook/Get-FabricNotebookDefinition.ps1 index 009eb88d..b7235bbb 100644 --- a/FabricTools/public/Notebook/Get-FabricNotebookDefinition.ps1 +++ b/source/Public/Notebook/Get-FabricNotebookDefinition.ps1 @@ -4,7 +4,7 @@ Retrieves the definition of a notebook from a specific workspace in Microsoft Fabric. .DESCRIPTION -This function fetches the notebook's content or metadata from a workspace. +This function fetches the notebook's content or metadata from a workspace. It supports retrieving notebook definitions in the Jupyter Notebook (`ipynb`) format. Handles both synchronous and asynchronous operations, with detailed logging and error handling. @@ -88,13 +88,13 @@ function Get-FabricNotebookDefinition { [string]$operationId = $responseHeader["x-ms-operation-id"] #[string]$location = $responseHeader["Location"] - [string]$retryAfter = $responseHeader["Retry-After"] + [string]$retryAfter = $responseHeader["Retry-After"] Write-Message -Message "Operation ID: '$operationId'" -Level Debug Write-Message -Message "Location: '$location'" -Level Debug Write-Message -Message "Retry-After: '$retryAfter'" -Level Debug Write-Message -Message "Getting Long Running Operation status" -Level Debug - + $operationStatus = Get-FabricLongRunningOperation -operationId $operationId -location $location Write-Message -Message "Long Running Operation status: $operationStatus" -Level Debug @@ -102,30 +102,28 @@ function Get-FabricNotebookDefinition { if ($operationStatus.status -eq "Succeeded") { Write-Message -Message "Operation Succeeded" -Level Debug Write-Message -Message "Getting Long Running Operation result" -Level Debug - - $operationResult = Get-FabricLongRunningOperationResult -operationId $operationId + + $operationResult = Get-FabricLongRunningOperationResult -operationId $operationId Write-Message -Message "Long Running Operation status: $operationResult" -Level Debug - + return $operationResult.definition.parts - } - else { + } else { Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Debug Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Error return $operationStatus - } + } } default { Write-Message -Message "Unexpected response code: $statusCode" -Level Error Write-Message -Message "Error details: $($response.message)" -Level Error throw "API request failed with status code $statusCode." } - + } - } - catch { + } catch { # Step 9: Capture and log error details $errorDetails = $_.Exception.Message Write-Message -Message "Failed to retrieve Notebook. Error: $errorDetails" -Level Error - } - -} + } + +} \ No newline at end of file diff --git a/FabricTools/public/Notebook/New-FabricNotebook.ps1 b/source/Public/Notebook/New-FabricNotebook.ps1 similarity index 81% rename from FabricTools/public/Notebook/New-FabricNotebook.ps1 rename to source/Public/Notebook/New-FabricNotebook.ps1 index eec94a4e..96128aea 100644 --- a/FabricTools/public/Notebook/New-FabricNotebook.ps1 +++ b/source/Public/Notebook/New-FabricNotebook.ps1 @@ -3,8 +3,8 @@ Creates a new notebook in a specified Microsoft Fabric workspace. .DESCRIPTION -This function sends a POST request to the Microsoft Fabric API to create a new notebook -in the specified workspace. It supports optional parameters for notebook description +This function sends a POST request to the Microsoft Fabric API to create a new notebook +in the specified workspace. It supports optional parameters for notebook description and path definitions for the notebook content. .PARAMETER WorkspaceId @@ -29,12 +29,13 @@ An optional path to the platform-specific definition (e.g., .platform file) to u - Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. - Calls `Test-TokenExpired` to ensure token validity before making the API request. -Author: Tiago Balabuch +Author: Tiago Balabuch #> -function New-FabricNotebook { - [CmdletBinding()] +function New-FabricNotebook +{ + [CmdletBinding(supportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -52,13 +53,14 @@ function New-FabricNotebook { [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$NotebookPathDefinition, - + [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$NotebookPathPlatformDefinition ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -73,16 +75,20 @@ function New-FabricNotebook { displayName = $NotebookName } - if ($NotebookDescription) { + if ($NotebookDescription) + { $body.description = $NotebookDescription } - if ($NotebookPathDefinition) { + if ($NotebookPathDefinition) + { $notebookEncodedContent = Convert-ToBase64 -filePath $NotebookPathDefinition - if (-not [string]::IsNullOrEmpty($notebookEncodedContent)) { + if (-not [string]::IsNullOrEmpty($notebookEncodedContent)) + { # Initialize definition if it doesn't exist - if (-not $body.definition) { + if (-not $body.definition) + { $body.definition = @{ format = "ipynb" parts = @() @@ -96,18 +102,22 @@ function New-FabricNotebook { payloadType = "InlineBase64" } } - else { + else + { Write-Message -Message "Invalid or empty content in notebook definition." -Level Error return $null } } - if ($NotebookPathPlatformDefinition) { + if ($NotebookPathPlatformDefinition) + { $notebookEncodedPlatformContent = Convert-ToBase64 -filePath $NotebookPathPlatformDefinition - if (-not [string]::IsNullOrEmpty($notebookEncodedPlatformContent)) { + if (-not [string]::IsNullOrEmpty($notebookEncodedPlatformContent)) + { # Initialize definition if it doesn't exist - if (-not $body.definition) { + if (-not $body.definition) + { $body.definition = @{ format = "ipynb" parts = @() @@ -121,7 +131,8 @@ function New-FabricNotebook { payloadType = "InlineBase64" } } - else { + else + { Write-Message -Message "Invalid or empty content in platform definition." -Level Error return $null } @@ -129,63 +140,71 @@ function New-FabricNotebook { $bodyJson = $body | ConvertTo-Json -Depth 10 Write-Message -Message "Request Body: $bodyJson" -Level Debug - - # Step 4: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Post ` - -Body $bodyJson ` - -ContentType "application/json" ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -ResponseHeadersVariable "responseHeader" ` - -StatusCodeVariable "statusCode" - + if ($PSCmdlet.ShouldProcess($NotebookName, "Create Notebook")) + { + # Step 4: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Post ` + -Body $bodyJson ` + -ContentType "application/json" ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -ResponseHeadersVariable "responseHeader" ` + -StatusCodeVariable "statusCode" + } # Step 5: Handle and log the response - switch ($statusCode) { - 201 { + switch ($statusCode) + { + 201 + { Write-Message -Message "Notebook '$NotebookName' created successfully!" -Level Info return $response } - 202 { + 202 + { Write-Message -Message "Notebook '$NotebookName' creation accepted. Provisioning in progress!" -Level Info - + [string]$operationId = $responseHeader["x-ms-operation-id"] [string]$location = $responseHeader["Location"] - [string]$retryAfter = $responseHeader["Retry-After"] + [string]$retryAfter = $responseHeader["Retry-After"] Write-Message -Message "Operation ID: '$operationId'" -Level Debug Write-Message -Message "Location: '$location'" -Level Debug Write-Message -Message "Retry-After: '$retryAfter'" -Level Debug Write-Message -Message "Getting Long Running Operation status" -Level Debug - + $operationStatus = Get-FabricLongRunningOperation -operationId $operationId -location $location Write-Message -Message "Long Running Operation status: $operationStatus" -Level Debug # Handle operation result - if ($operationStatus.status -eq "Succeeded") { + if ($operationStatus.status -eq "Succeeded") + { Write-Message -Message "Operation Succeeded" -Level Debug Write-Message -Message "Getting Long Running Operation result" -Level Debug - + $operationResult = Get-FabricLongRunningOperationResult -operationId $operationId Write-Message -Message "Long Running Operation status: $operationResult" -Level Debug - + return $operationResult - } - else { + } + else + { Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Debug Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Error return $operationStatus - } + } } - default { + default + { Write-Message -Message "Unexpected response code: $statusCode" -Level Error Write-Message -Message "Error details: $($response.message)" -Level Error throw "API request failed with status code $statusCode." } } } - catch { + catch + { # Step 6: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to create notebook. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Notebook/New-FabricNotebookNEW.ps1 b/source/Public/Notebook/New-FabricNotebookNEW.ps1 similarity index 79% rename from FabricTools/public/Notebook/New-FabricNotebookNEW.ps1 rename to source/Public/Notebook/New-FabricNotebookNEW.ps1 index c29b71f7..007ff7e6 100644 --- a/FabricTools/public/Notebook/New-FabricNotebookNEW.ps1 +++ b/source/Public/Notebook/New-FabricNotebookNEW.ps1 @@ -3,8 +3,8 @@ Creates a new notebook in a specified Microsoft Fabric workspace. .DESCRIPTION -This function sends a POST request to the Microsoft Fabric API to create a new notebook -in the specified workspace. It supports optional parameters for notebook description +This function sends a POST request to the Microsoft Fabric API to create a new notebook +in the specified workspace. It supports optional parameters for notebook description and path definitions for the notebook content. .PARAMETER WorkspaceId @@ -29,12 +29,13 @@ An optional path to the platform-specific definition (e.g., .platform file) to u - Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. - Calls `Test-TokenExpired` to ensure token validity before making the API request. -Author: Tiago Balabuch +Author: Tiago Balabuch #> -function New-FabricNotebookNEW { - [CmdletBinding()] +function New-FabricNotebookNEW +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -54,7 +55,8 @@ function New-FabricNotebookNEW { [string]$NotebookPathDefinition ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -69,12 +71,15 @@ function New-FabricNotebookNEW { displayName = $NotebookName } - if ($NotebookDescription) { + if ($NotebookDescription) + { $body.description = $NotebookDescription } - if ($NotebookPathDefinition) { - if (-not $body.definition) { + if ($NotebookPathDefinition) + { + if (-not $body.definition) + { $body.definition = @{ format = "ipynb" parts = @() @@ -85,8 +90,10 @@ function New-FabricNotebookNEW { $body.definition.parts = $jsonObjectParts.parts } # Check if any path is .platform - foreach ($part in $jsonObjectParts.parts) { - if ($part.path -eq ".platform") { + foreach ($part in $jsonObjectParts.parts) + { + if ($part.path -eq ".platform") + { $hasPlatformFile = $true Write-Message -Message "Platform File: $hasPlatformFile" -Level Debug } @@ -94,63 +101,72 @@ function New-FabricNotebookNEW { $bodyJson = $body | ConvertTo-Json -Depth 10 Write-Message -Message "Request Body: $bodyJson" -Level Debug - - # Step 4: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Post ` - -Body $bodyJson ` - -ContentType "application/json" ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -ResponseHeadersVariable "responseHeader" ` - -StatusCodeVariable "statusCode" + if ($PSCmdlet.ShouldProcess($NotebookName, "Create Notebook")) + { + # Step 4: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Post ` + -Body $bodyJson ` + -ContentType "application/json" ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -ResponseHeadersVariable "responseHeader" ` + -StatusCodeVariable "statusCode" + } # Step 5: Handle and log the response - switch ($statusCode) { - 201 { + switch ($statusCode) + { + 201 + { Write-Message -Message "Notebook '$NotebookName' created successfully!" -Level Info return $response } - 202 { + 202 + { Write-Message -Message "Notebook '$NotebookName' creation accepted. Provisioning in progress!" -Level Info - + [string]$operationId = $responseHeader["x-ms-operation-id"] [string]$location = $responseHeader["Location"] - [string]$retryAfter = $responseHeader["Retry-After"] + [string]$retryAfter = $responseHeader["Retry-After"] Write-Message -Message "Operation ID: '$operationId'" -Level Debug Write-Message -Message "Location: '$location'" -Level Debug Write-Message -Message "Retry-After: '$retryAfter'" -Level Debug Write-Message -Message "Getting Long Running Operation status" -Level Debug - - $operationStatus = Get-FabricLongRunningOperation -operationId $operationId + + $operationStatus = Get-FabricLongRunningOperation -operationId $operationId Write-Message -Message "Long Running Operation status: $operationStatus" -Level Debug # Handle operation result - if ($operationStatus.status -eq "Succeeded") { + if ($operationStatus.status -eq "Succeeded") + { Write-Message -Message "Operation Succeeded" -Level Debug Write-Message -Message "Getting Long Running Operation result" -Level Debug - + $operationResult = Get-FabricLongRunningOperationResult -operationId $operationId Write-Message -Message "Long Running Operation status: $operationResult" -Level Debug - + return $operationResult - } - else { + } + else + { Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Debug Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Error return $operationStatus - } + } } - default { + default + { Write-Message -Message "Unexpected response code: $statusCode" -Level Error Write-Message -Message "Error details: $($response.message)" -Level Error throw "API request failed with status code $statusCode." } } } - catch { + catch + { # Step 6: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to create notebook. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Notebook/Remove-FabricNotebook.ps1 b/source/Public/Notebook/Remove-FabricNotebook.ps1 similarity index 77% rename from FabricTools/public/Notebook/Remove-FabricNotebook.ps1 rename to source/Public/Notebook/Remove-FabricNotebook.ps1 index ec22fad3..78f03b98 100644 --- a/FabricTools/public/Notebook/Remove-FabricNotebook.ps1 +++ b/source/Public/Notebook/Remove-FabricNotebook.ps1 @@ -20,12 +20,13 @@ Deletes the Notebook with ID "67890" from workspace "12345". - Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. - Validates token expiration before making the API request. -Author: Tiago Balabuch +Author: Tiago Balabuch #> -function Remove-FabricNotebook { - [CmdletBinding()] +function Remove-FabricNotebook +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -36,7 +37,8 @@ function Remove-FabricNotebook { [string]$NotebookId ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -45,27 +47,31 @@ function Remove-FabricNotebook { # Step 2: Construct the API URL $apiEndpointUrl = "{0}/workspaces/{1}/notebooks/{2}" -f $FabricConfig.BaseUrl, $WorkspaceId, $NotebookId Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug - - # Step 3: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Delete ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -StatusCodeVariable "statusCode" + if ($PSCmdlet.ShouldProcess($apiEndpointUrl, "Remove Notebook")) + { + # Step 3: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Delete ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -StatusCodeVariable "statusCode" + } # Step 4: Validate the response code - if ($statusCode -ne 200) { + if ($statusCode -ne 200) + { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error Write-Message -Message "Error: $($response.message)" -Level Error Write-Message "Error Code: $($response.errorCode)" -Level Error return $null } Write-Message -Message "Notebook '$NotebookId' deleted successfully from workspace '$WorkspaceId'." -Level Info - + } - catch { + catch + { # Step 5: Log and handle errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to delete notebook '$NotebookId' from workspace '$WorkspaceId'. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Notebook/Update-FabricNotebook.ps1 b/source/Public/Notebook/Update-FabricNotebook.ps1 similarity index 77% rename from FabricTools/public/Notebook/Update-FabricNotebook.ps1 rename to source/Public/Notebook/Update-FabricNotebook.ps1 index c5cc8203..bdf39e5e 100644 --- a/FabricTools/public/Notebook/Update-FabricNotebook.ps1 +++ b/source/Public/Notebook/Update-FabricNotebook.ps1 @@ -5,6 +5,9 @@ Updates the properties of a Fabric Notebook. .DESCRIPTION The `Update-FabricNotebook` function updates the name and/or description of a specified Fabric Notebook by making a PATCH request to the API. +.PARAMETER WorkspaceId +The unique identifier of the workspace where the Notebook exists. + .PARAMETER NotebookId The unique identifier of the Notebook to be updated. @@ -28,17 +31,18 @@ Updates both the name and description of the Notebook "Notebook123". - Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. - Calls `Test-TokenExpired` to ensure token validity before making the API request. -Author: Tiago Balabuch +Author: Tiago Balabuch #> -function Update-FabricNotebook { - [CmdletBinding()] +function Update-FabricNotebook +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [string]$WorkspaceId, - + [string]$WorkspaceId, + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$NotebookId, @@ -53,7 +57,8 @@ function Update-FabricNotebook { [string]$NotebookDescription ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -68,27 +73,30 @@ function Update-FabricNotebook { displayName = $NotebookName } - if ($NotebookDescription) { + if ($NotebookDescription) + { $body.description = $NotebookDescription } # Convert the body to JSON $bodyJson = $body | ConvertTo-Json Write-Message -Message "Request Body: $bodyJson" -Level Debug - - # Step 4: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Patch ` - -Body $bodyJson ` - -ContentType "application/json" ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -StatusCodeVariable "statusCode" - + if ($PSCmdlet.ShouldProcess($apiEndpointUrl, "Update Notebook")) + { + # Step 4: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Patch ` + -Body $bodyJson ` + -ContentType "application/json" ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -StatusCodeVariable "statusCode" + } # Step 5: Validate the response code - if ($statusCode -ne 200) { + if ($statusCode -ne 200) + { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error Write-Message -Message "Error: $($response.message)" -Level Error Write-Message "Error Code: $($response.errorCode)" -Level Error @@ -99,7 +107,8 @@ function Update-FabricNotebook { Write-Message -Message "Notebook '$NotebookName' updated successfully!" -Level Info return $response } - catch { + catch + { # Step 7: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to update notebook. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Notebook/Update-FabricNotebookDefinition.ps1 b/source/Public/Notebook/Update-FabricNotebookDefinition.ps1 similarity index 83% rename from FabricTools/public/Notebook/Update-FabricNotebookDefinition.ps1 rename to source/Public/Notebook/Update-FabricNotebookDefinition.ps1 index e6272644..d867e626 100644 --- a/FabricTools/public/Notebook/Update-FabricNotebookDefinition.ps1 +++ b/source/Public/Notebook/Update-FabricNotebookDefinition.ps1 @@ -3,7 +3,7 @@ Updates the definition of a notebook in a Microsoft Fabric workspace. .DESCRIPTION -This function allows updating the content or metadata of a notebook in a Microsoft Fabric workspace. +This function allows updating the content or metadata of a notebook in a Microsoft Fabric workspace. The notebook content can be provided as file paths, and metadata updates can optionally be enabled. .PARAMETER WorkspaceId @@ -34,12 +34,13 @@ Updates both the content and metadata of the notebook with ID `67890` in the wor - The notebook content is encoded as Base64 before being sent to the Fabric API. - This function handles asynchronous operations and retrieves operation results if required. -Author: Tiago Balabuch +Author: Tiago Balabuch #> -function Update-FabricNotebookDefinition { - [CmdletBinding()] +function Update-FabricNotebookDefinition +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -52,13 +53,14 @@ function Update-FabricNotebookDefinition { [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$NotebookPathDefinition, - + [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$NotebookPathPlatformDefinition ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -67,8 +69,9 @@ function Update-FabricNotebookDefinition { # Step 2: Construct the API URL $apiEndpointUrl = "{0}/workspaces/{1}/notebooks/{2}/updateDefinition" -f $FabricConfig.BaseUrl, $WorkspaceId, $NotebookId - if ($NotebookPathPlatformDefinition) { - $apiEndpointUrl += "?updateMetadata=true" -f $apiEndpointUrl + if ($NotebookPathPlatformDefinition) + { + $apiEndpointUrl += "?updateMetadata=true" -f $apiEndpointUrl } Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug @@ -77,13 +80,15 @@ function Update-FabricNotebookDefinition { definition = @{ format = "ipynb" parts = @() - } + } } - - if ($NotebookPathDefinition) { + + if ($NotebookPathDefinition) + { $notebookEncodedContent = Convert-ToBase64 -filePath $NotebookPathDefinition - - if (-not [string]::IsNullOrEmpty($notebookEncodedContent)) { + + if (-not [string]::IsNullOrEmpty($notebookEncodedContent)) + { # Add new part to the parts array $body.definition.parts += @{ path = "notebook-content.py" @@ -91,15 +96,18 @@ function Update-FabricNotebookDefinition { payloadType = "InlineBase64" } } - else { + else + { Write-Message -Message "Invalid or empty content in notebook definition." -Level Error return $null } } - if ($NotebookPathPlatformDefinition) { + if ($NotebookPathPlatformDefinition) + { $notebookEncodedPlatformContent = Convert-ToBase64 -filePath $NotebookPathPlatformDefinition - if (-not [string]::IsNullOrEmpty($notebookEncodedPlatformContent)) { + if (-not [string]::IsNullOrEmpty($notebookEncodedPlatformContent)) + { # Add new part to the parts array $body.definition.parts += @{ path = ".platform" @@ -107,7 +115,8 @@ function Update-FabricNotebookDefinition { payloadType = "InlineBase64" } } - else { + else + { Write-Message -Message "Invalid or empty content in platform definition." -Level Error return $null } @@ -115,25 +124,30 @@ function Update-FabricNotebookDefinition { $bodyJson = $body | ConvertTo-Json -Depth 10 Write-Message -Message "Request Body: $bodyJson" -Level Debug + if ($PSCmdlet.ShouldProcess($NotebookId, "Update Notebook Definition")) + { + # Step 4: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Post ` + -Body $bodyJson ` + -ContentType "application/json" ` + -ErrorAction Stop ` + -ResponseHeadersVariable "responseHeader" ` + -StatusCodeVariable "statusCode" + } - # Step 4: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Post ` - -Body $bodyJson ` - -ContentType "application/json" ` - -ErrorAction Stop ` - -ResponseHeadersVariable "responseHeader" ` - -StatusCodeVariable "statusCode" - # Step 5: Handle and log the response - switch ($statusCode) { - 200 { + switch ($statusCode) + { + 200 + { Write-Message -Message "Update definition for notebook '$NotebookId' created successfully!" -Level Info return $response } - 202 { + 202 + { Write-Message -Message "Update definition for notebook '$NotebookId' accepted. Operation in progress!" -Level Info [string]$operationId = $responseHeader["x-ms-operation-id"] [string]$location = $responseHeader["Location"] @@ -147,22 +161,25 @@ function Update-FabricNotebookDefinition { $operationStatus = Get-FabricLongRunningOperation -operationId $operationId -location $location Write-Message -Message "Long Running Operation status: $operationStatus" -Level Debug # Handle operation result - if ($operationStatus.status -eq "Succeeded") { + if ($operationStatus.status -eq "Succeeded") + { Write-Message -Message "Operation Succeeded" -Level Debug Write-Message -Message "Getting Long Running Operation result" -Level Debug - + $operationResult = Get-FabricLongRunningOperationResult -operationId $operationId Write-Message -Message "Long Running Operation status: $operationResult" -Level Debug - + return $operationResult - } - else { + } + else + { Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Debug Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Error return $operationStatus - } - } - default { + } + } + default + { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error Write-Message -Message "Error: $($response.message)" -Level Error Write-Message -Message "Error Details: $($response.moreDetails)" -Level Error @@ -171,7 +188,8 @@ function Update-FabricNotebookDefinition { } } } - catch { + catch + { # Step 6: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to update notebook. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Paginated Reports/Get-FabricPaginatedReport.ps1 b/source/Public/Paginated Reports/Get-FabricPaginatedReport.ps1 similarity index 95% rename from FabricTools/public/Paginated Reports/Get-FabricPaginatedReport.ps1 rename to source/Public/Paginated Reports/Get-FabricPaginatedReport.ps1 index fc52257d..4ffe89b8 100644 --- a/FabricTools/public/Paginated Reports/Get-FabricPaginatedReport.ps1 +++ b/source/Public/Paginated Reports/Get-FabricPaginatedReport.ps1 @@ -28,7 +28,7 @@ - Calls `Test-TokenExpired` to ensure token validity before making the API request. Author: Tiago Balabuch - + #> function Get-FabricPaginatedReport { [CmdletBinding()] @@ -61,27 +61,27 @@ function Get-FabricPaginatedReport { # Step 3: Initialize variables $continuationToken = $null $PaginatedReports = @() - + if (-not ([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.GetName().Name -eq "System.Web" })) { Add-Type -AssemblyName System.Web } - + # Step 4: Loop to retrieve all capacities with continuation token Write-Message -Message "Loop started to get continuation token" -Level Debug $baseApiEndpointUrl = "{0}/workspaces/{1}/paginatedReports" -f $FabricConfig.BaseUrl, $WorkspaceId - + do { # Step 5: Construct the API URL $apiEndpointUrl = $baseApiEndpointUrl - + if ($null -ne $continuationToken) { # URL-encode the continuation token $encodedToken = [System.Web.HttpUtility]::UrlEncode($continuationToken) $apiEndpointUrl = "{0}?continuationToken={1}" -f $apiEndpointUrl, $encodedToken } Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug - + # Step 6: Make the API request $response = Invoke-RestMethod ` -Headers $FabricConfig.FabricHeaders ` @@ -91,7 +91,7 @@ function Get-FabricPaginatedReport { -SkipHttpErrorCheck ` -ResponseHeadersVariable "responseHeader" ` -StatusCodeVariable "statusCode" - + # Step 7: Validate the response code if ($statusCode -ne 200) { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error @@ -100,38 +100,34 @@ function Get-FabricPaginatedReport { Write-Message "Error Code: $($response.errorCode)" -Level Error return $null } - + # Step 8: Add data to the list if ($null -ne $response) { Write-Message -Message "Adding data to the list" -Level Debug $PaginatedReports += $response.value - + # Update the continuation token if present if ($response.PSObject.Properties.Match("continuationToken")) { Write-Message -Message "Updating the continuation token" -Level Debug $continuationToken = $response.continuationToken Write-Message -Message "Continuation token: $continuationToken" -Level Debug - } - else { + } else { Write-Message -Message "Updating the continuation token to null" -Level Debug $continuationToken = $null } - } - else { + } else { Write-Message -Message "No data received from the API." -Level Warning break } } while ($null -ne $continuationToken) Write-Message -Message "Loop finished and all data added to the list" -Level Debug - + # Step 8: Filter results based on provided parameters $PaginatedReport = if ($PaginatedReportId) { $PaginatedReports | Where-Object { $_.Id -eq $PaginatedReportId } - } - elseif ($PaginatedReportName) { + } elseif ($PaginatedReportName) { $PaginatedReports | Where-Object { $_.DisplayName -eq $PaginatedReportName } - } - else { + } else { # Return all PaginatedReports if no filter is provided Write-Message -Message "No filter provided. Returning all Paginated Reports." -Level Debug $PaginatedReports @@ -141,15 +137,13 @@ function Get-FabricPaginatedReport { if ($PaginatedReport) { Write-Message -Message "Paginated Report found matching the specified criteria." -Level Debug return $PaginatedReport - } - else { + } else { Write-Message -Message "No Paginated Report found matching the provided criteria." -Level Warning return $null } - } - catch { + } catch { # Step 10: Capture and log error details $errorDetails = $_.Exception.Message Write-Message -Message "Failed to retrieve Paginated Report. Error: $errorDetails" -Level Error - } -} + } +} \ No newline at end of file diff --git a/FabricTools/public/Paginated Reports/Update-FabricPaginatedReport.ps1 b/source/Public/Paginated Reports/Update-FabricPaginatedReport.ps1 similarity index 80% rename from FabricTools/public/Paginated Reports/Update-FabricPaginatedReport.ps1 rename to source/Public/Paginated Reports/Update-FabricPaginatedReport.ps1 index 00d0c59d..e8d17474 100644 --- a/FabricTools/public/Paginated Reports/Update-FabricPaginatedReport.ps1 +++ b/source/Public/Paginated Reports/Update-FabricPaginatedReport.ps1 @@ -3,7 +3,7 @@ Updates an existing paginated report in a specified Microsoft Fabric workspace. .DESCRIPTION - This function sends a PATCH request to the Microsoft Fabric API to update an existing paginated report + This function sends a PATCH request to the Microsoft Fabric API to update an existing paginated report in the specified workspace. It supports optional parameters for paginated report description. .PARAMETER WorkspaceId @@ -27,15 +27,16 @@ - Calls `Test-TokenExpired` to ensure token validity before making the API request. Author: Tiago Balabuch - + #> -function Update-FabricPaginatedReport { - [CmdletBinding()] +function Update-FabricPaginatedReport +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [string]$WorkspaceId, - + [string]$WorkspaceId, + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$PaginatedReportId, @@ -50,7 +51,8 @@ function Update-FabricPaginatedReport { [string]$PaginatedReportDescription ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -65,7 +67,8 @@ function Update-FabricPaginatedReport { displayName = $PaginatedReportName } - if ($PaginatedReportDescription) { + if ($PaginatedReportDescription) + { $body.description = $PaginatedReportDescription } @@ -73,19 +76,23 @@ function Update-FabricPaginatedReport { $bodyJson = $body | ConvertTo-Json Write-Message -Message "Request Body: $bodyJson" -Level Debug - # Step 4: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Patch ` - -Body $bodyJson ` - -ContentType "application/json" ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -StatusCodeVariable "statusCode" - + if ($PSCmdlet.ShouldProcess($PaginatedReportName, "Update Paginated Report")) + { + + # Step 4: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Patch ` + -Body $bodyJson ` + -ContentType "application/json" ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -StatusCodeVariable "statusCode" + } # Step 5: Validate the response code - if ($statusCode -ne 200) { + if ($statusCode -ne 200) + { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error Write-Message -Message "Error: $($response.message)" -Level Error Write-Message -Message "Error Details: $($response.moreDetails)" -Level Error @@ -97,7 +104,8 @@ function Update-FabricPaginatedReport { Write-Message -Message "Paginated Report '$PaginatedReportName' updated successfully!" -Level Info return $response } - catch { + catch + { # Step 7: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to update Paginated Report. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Reflex/Get-FabricReflex.ps1 b/source/Public/Reflex/Get-FabricReflex.ps1 similarity index 96% rename from FabricTools/public/Reflex/Get-FabricReflex.ps1 rename to source/Public/Reflex/Get-FabricReflex.ps1 index 9e274d3c..b7d0a707 100644 --- a/FabricTools/public/Reflex/Get-FabricReflex.ps1 +++ b/source/Public/Reflex/Get-FabricReflex.ps1 @@ -28,7 +28,7 @@ - Calls `Test-TokenExpired` to ensure token validity before making the API request. Author: Tiago Balabuch - + #> function Get-FabricReflex { [CmdletBinding()] @@ -58,15 +58,15 @@ function Get-FabricReflex { Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired Write-Message -Message "Token validation completed." -Level Debug - + # Step 3: Initialize variables $continuationToken = $null $Reflexes = @() - + if (-not ([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.GetName().Name -eq "System.Web" })) { Add-Type -AssemblyName System.Web } - + # Step 4: Loop to retrieve all capacities with continuation token Write-Message -Message "Loop started to get continuation token" -Level Debug $baseApiEndpointUrl = "{0}/workspaces/{1}/reflexes" -f $FabricConfig.BaseUrl, $WorkspaceId @@ -81,7 +81,7 @@ function Get-FabricReflex { $apiEndpointUrl = "{0}?continuationToken={1}" -f $apiEndpointUrl, $encodedToken } Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug - + # Step 6: Make the API request $response = Invoke-RestMethod ` -Headers $FabricConfig.FabricHeaders ` @@ -91,7 +91,7 @@ function Get-FabricReflex { -SkipHttpErrorCheck ` -ResponseHeadersVariable "responseHeader" ` -StatusCodeVariable "statusCode" - + # Step 7: Validate the response code if ($statusCode -ne 200) { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error @@ -100,38 +100,34 @@ function Get-FabricReflex { Write-Message "Error Code: $($response.errorCode)" -Level Error return $null } - + # Step 8: Add data to the list if ($null -ne $response) { Write-Message -Message "Adding data to the list" -Level Debug $Reflexes += $response.value - + # Update the continuation token if present if ($response.PSObject.Properties.Match("continuationToken")) { Write-Message -Message "Updating the continuation token" -Level Debug $continuationToken = $response.continuationToken Write-Message -Message "Continuation token: $continuationToken" -Level Debug - } - else { + } else { Write-Message -Message "Updating the continuation token to null" -Level Debug $continuationToken = $null } - } - else { + } else { Write-Message -Message "No data received from the API." -Level Warning break } } while ($null -ne $continuationToken) Write-Message -Message "Loop finished and all data added to the list" -Level Debug - + # Step 8: Filter results based on provided parameters $Reflex = if ($ReflexId) { $Reflexes | Where-Object { $_.Id -eq $ReflexId } - } - elseif ($ReflexName) { + } elseif ($ReflexName) { $Reflexes | Where-Object { $_.DisplayName -eq $ReflexName } - } - else { + } else { # Return all Reflexes if no filter is provided Write-Message -Message "No filter provided. Returning all Reflexes." -Level Debug $Reflexes @@ -141,16 +137,14 @@ function Get-FabricReflex { if ($Reflex) { Write-Message -Message "Reflex found in the Workspace '$WorkspaceId'." -Level Debug return $Reflex - } - else { + } else { Write-Message -Message "No Reflex found matching the provided criteria." -Level Warning return $null } - } - catch { + } catch { # Step 10: Capture and log error details $errorDetails = $_.Exception.Message Write-Message -Message "Failed to retrieve Reflex. Error: $errorDetails" -Level Error - } - -} + } + +} \ No newline at end of file diff --git a/FabricTools/public/Reflex/Get-FabricReflexDefinition.ps1 b/source/Public/Reflex/Get-FabricReflexDefinition.ps1 similarity index 97% rename from FabricTools/public/Reflex/Get-FabricReflexDefinition.ps1 rename to source/Public/Reflex/Get-FabricReflexDefinition.ps1 index 121677e4..4dde23c7 100644 --- a/FabricTools/public/Reflex/Get-FabricReflexDefinition.ps1 +++ b/source/Public/Reflex/Get-FabricReflexDefinition.ps1 @@ -28,7 +28,7 @@ - Calls `Test-TokenExpired` to ensure token validity before making the API request. Author: Tiago Balabuch - + #> function Get-FabricReflexDefinition { [CmdletBinding()] @@ -53,11 +53,11 @@ function Get-FabricReflexDefinition { # Step 3: Construct the API URL $apiEndpointUrl = "{0}/workspaces/{1}/reflexes/{2}/getDefinition" -f $FabricConfig.BaseUrl, $WorkspaceId, $ReflexId - + if ($ReflexFormat) { $apiEndpointUrl = "{0}?format={1}" -f $apiEndpointUrl, $ReflexFormat } - + Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug # Step 4: Make the API request @@ -81,30 +81,29 @@ function Get-FabricReflexDefinition { [string]$operationId = $responseHeader["x-ms-operation-id"] [string]$location = $responseHeader["Location"] - [string]$retryAfter = $responseHeader["Retry-After"] + [string]$retryAfter = $responseHeader["Retry-After"] Write-Message -Message "Operation ID: '$operationId'" -Level Debug Write-Message -Message "Location: '$location'" -Level Debug Write-Message -Message "Retry-After: '$retryAfter'" -Level Debug Write-Message -Message "Getting Long Running Operation status" -Level Debug - + $operationStatus = Get-FabricLongRunningOperation -operationId $operationId -location $location Write-Message -Message "Long Running Operation status: $operationStatus" -Level Debug # Handle operation result if ($operationStatus.status -eq "Succeeded") { Write-Message -Message "Operation Succeeded" -Level Debug Write-Message -Message "Getting Long Running Operation result" -Level Debug - + $operationResult = Get-FabricLongRunningOperationResult -operationId $operationId, -location $location Write-Message -Message "Long Running Operation status: $operationResult" -Level Debug - + return $operationResult.definition.parts - } - else { + } else { Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Debug Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Error return $operationStatus - } + } } default { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error @@ -114,11 +113,10 @@ function Get-FabricReflexDefinition { throw "API request failed with status code $statusCode." } } - } - catch { + } catch { # Step 9: Capture and log error details $errorDetails = $_.Exception.Message Write-Message -Message "Failed to retrieve Reflex. Error: $errorDetails" -Level Error - } - -} + } + +} \ No newline at end of file diff --git a/FabricTools/public/Reflex/New-FabricReflex.ps1 b/source/Public/Reflex/New-FabricReflex.ps1 similarity index 82% rename from FabricTools/public/Reflex/New-FabricReflex.ps1 rename to source/Public/Reflex/New-FabricReflex.ps1 index e71b6502..f19cc0ad 100644 --- a/FabricTools/public/Reflex/New-FabricReflex.ps1 +++ b/source/Public/Reflex/New-FabricReflex.ps1 @@ -3,7 +3,7 @@ Creates a new Reflex in a specified Microsoft Fabric workspace. .DESCRIPTION - This function sends a POST request to the Microsoft Fabric API to create a new Reflex + This function sends a POST request to the Microsoft Fabric API to create a new Reflex in the specified workspace. It supports optional parameters for Reflex description and path definitions. .PARAMETER WorkspaceId @@ -30,10 +30,11 @@ - Calls `Test-TokenExpired` to ensure token validity before making the API request. Author: Tiago Balabuch - + #> -function New-FabricReflex { - [CmdletBinding()] +function New-FabricReflex +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -51,12 +52,13 @@ function New-FabricReflex { [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$ReflexPathDefinition, - + [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$ReflexPathPlatformDefinition ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -71,17 +73,21 @@ function New-FabricReflex { displayName = $ReflexName } - if ($ReflexDescription) { + if ($ReflexDescription) + { $body.description = $ReflexDescription } - if ($ReflexPathDefinition) { + if ($ReflexPathDefinition) + { $ReflexEncodedContent = Convert-ToBase64 -filePath $ReflexPathDefinition - if (-not [string]::IsNullOrEmpty($ReflexEncodedContent)) { + if (-not [string]::IsNullOrEmpty($ReflexEncodedContent)) + { # Initialize definition if it doesn't exist - if (-not $body.definition) { + if (-not $body.definition) + { $body.definition = @{ - parts = @() + parts = @() } } @@ -92,20 +98,24 @@ function New-FabricReflex { payloadType = "InlineBase64" } } - else { + else + { Write-Message -Message "Invalid or empty content in Reflex definition." -Level Error return $null } } - if ($ReflexPathPlatformDefinition) { + if ($ReflexPathPlatformDefinition) + { $ReflexEncodedPlatformContent = Convert-ToBase64 -filePath $ReflexPathPlatformDefinition - if (-not [string]::IsNullOrEmpty($ReflexEncodedPlatformContent)) { + if (-not [string]::IsNullOrEmpty($ReflexEncodedPlatformContent)) + { # Initialize definition if it doesn't exist - if (-not $body.definition) { + if (-not $body.definition) + { $body.definition = @{ - parts = @() + parts = @() } } @@ -116,7 +126,8 @@ function New-FabricReflex { payloadType = "InlineBase64" } } - else { + else + { Write-Message -Message "Invalid or empty content in platform definition." -Level Error return $null } @@ -126,57 +137,66 @@ function New-FabricReflex { $bodyJson = $body | ConvertTo-Json -Depth 10 Write-Message -Message "Request Body: $bodyJson" -Level Debug - # Step 4: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Post ` - -Body $bodyJson ` - -ContentType "application/json" ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -ResponseHeadersVariable "responseHeader" ` - -StatusCodeVariable "statusCode" - + if ($PSCmdlet.ShouldProcess($ReflexName, "Create Reflex")) + { + # Step 4: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Post ` + -Body $bodyJson ` + -ContentType "application/json" ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -ResponseHeadersVariable "responseHeader" ` + -StatusCodeVariable "statusCode" + } + Write-Message -Message "Response Code: $statusCode" -Level Debug - + # Step 5: Handle and log the response - switch ($statusCode) { - 201 { + switch ($statusCode) + { + 201 + { Write-Message -Message "Reflex '$ReflexName' created successfully!" -Level Info return $response } - 202 { + 202 + { Write-Message -Message "Reflex '$ReflexName' creation accepted. Provisioning in progress!" -Level Info - + [string]$operationId = $responseHeader["x-ms-operation-id"] [string]$location = $responseHeader["Location"] - [string]$retryAfter = $responseHeader["Retry-After"] + [string]$retryAfter = $responseHeader["Retry-After"] Write-Message -Message "Operation ID: '$operationId'" -Level Debug Write-Message -Message "Location: '$location'" -Level Debug Write-Message -Message "Retry-After: '$retryAfter'" -Level Debug Write-Message -Message "Getting Long Running Operation status" -Level Debug - + $operationStatus = Get-FabricLongRunningOperation -operationId $operationId -location $location Write-Message -Message "Long Running Operation status: $operationStatus" -Level Debug # Handle operation result - if ($operationStatus.status -eq "Succeeded") { + if ($operationStatus.status -eq "Succeeded") + { Write-Message -Message "Operation Succeeded" -Level Debug Write-Message -Message "Getting Long Running Operation result" -Level Debug - + $operationResult = Get-FabricLongRunningOperationResult -operationId $operationId Write-Message -Message "Long Running Operation status: $operationResult" -Level Debug - + return $operationResult - } - else { + } + else + { Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Debug Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Error return $operationStatus - } + } } - default { + default + { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error Write-Message -Message "Error: $($response.message)" -Level Error Write-Message -Message "Error Details: $($response.moreDetails)" -Level Error @@ -185,7 +205,8 @@ function New-FabricReflex { } } } - catch { + catch + { # Step 6: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to create Reflex. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Reflex/Remove-FabricReflex.ps1 b/source/Public/Reflex/Remove-FabricReflex.ps1 similarity index 73% rename from FabricTools/public/Reflex/Remove-FabricReflex.ps1 rename to source/Public/Reflex/Remove-FabricReflex.ps1 index 884f4ab5..d57a3188 100644 --- a/FabricTools/public/Reflex/Remove-FabricReflex.ps1 +++ b/source/Public/Reflex/Remove-FabricReflex.ps1 @@ -3,7 +3,7 @@ Removes an Reflex from a specified Microsoft Fabric workspace. .DESCRIPTION - This function sends a DELETE request to the Microsoft Fabric API to remove an Reflex + This function sends a DELETE request to the Microsoft Fabric API to remove an Reflex from the specified workspace using the provided WorkspaceId and ReflexId. .PARAMETER WorkspaceId @@ -21,10 +21,11 @@ - Calls `Test-TokenExpired` to ensure token validity before making the API request. Author: Tiago Balabuch - + #> -function Remove-FabricReflex { - [CmdletBinding()] +function Remove-FabricReflex +{ + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -34,7 +35,8 @@ function Remove-FabricReflex { [ValidateNotNullOrEmpty()] [string]$ReflexId ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -44,18 +46,22 @@ function Remove-FabricReflex { $apiEndpointUrl = "{0}/workspaces/{1}/reflexes/{2}" -f $FabricConfig.BaseUrl, $WorkspaceId, $ReflexId Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug - # Step 3: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Delete ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -ResponseHeadersVariable "responseHeader" ` - -StatusCodeVariable "statusCode" - - # Step 4: Handle response - if ($statusCode -ne 200) { + if ($PSCmdlet.ShouldProcess($apiEndpointUrl, "Remove Reflex")) + { + # Step 3: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Delete ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -ResponseHeadersVariable "responseHeader" ` + -StatusCodeVariable "statusCode" + } + + # Step 4: Handle response + if ($statusCode -ne 200) + { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error Write-Message -Message "Error: $($response.message)" -Level Error Write-Message -Message "Error Details: $($response.moreDetails)" -Level Error @@ -63,9 +69,10 @@ function Remove-FabricReflex { return $null } - Write-Message -Message "Reflex '$ReflexId' deleted successfully from workspace '$WorkspaceId'." -Level Info + Write-Message -Message "Reflex '$ReflexId' deleted successfully from workspace '$WorkspaceId'." -Level Info } - catch { + catch + { # Step 5: Log and handle errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to delete Reflex '$ReflexId'. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Reflex/Update-FabricReflex.ps1 b/source/Public/Reflex/Update-FabricReflex.ps1 similarity index 79% rename from FabricTools/public/Reflex/Update-FabricReflex.ps1 rename to source/Public/Reflex/Update-FabricReflex.ps1 index a600d2fc..da7b2252 100644 --- a/FabricTools/public/Reflex/Update-FabricReflex.ps1 +++ b/source/Public/Reflex/Update-FabricReflex.ps1 @@ -3,7 +3,7 @@ Updates an existing Reflex in a specified Microsoft Fabric workspace. .DESCRIPTION - This function sends a PATCH request to the Microsoft Fabric API to update an existing Reflex + This function sends a PATCH request to the Microsoft Fabric API to update an existing Reflex in the specified workspace. It supports optional parameters for Reflex description. .PARAMETER WorkspaceId @@ -27,15 +27,16 @@ - Calls `Test-TokenExpired` to ensure token validity before making the API request. Author: Tiago Balabuch - + #> -function Update-FabricReflex { - [CmdletBinding()] +function Update-FabricReflex +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [string]$WorkspaceId, - + [string]$WorkspaceId, + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$ReflexId, @@ -49,7 +50,8 @@ function Update-FabricReflex { [ValidateNotNullOrEmpty()] [string]$ReflexDescription ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -64,7 +66,8 @@ function Update-FabricReflex { displayName = $ReflexName } - if ($ReflexDescription) { + if ($ReflexDescription) + { $body.description = $ReflexDescription } @@ -72,20 +75,24 @@ function Update-FabricReflex { $bodyJson = $body | ConvertTo-Json Write-Message -Message "Request Body: $bodyJson" -Level Debug - # Step 4: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Patch ` - -Body $bodyJson ` - -ContentType "application/json" ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -ResponseHeadersVariable "responseHeader" ` - -StatusCodeVariable "statusCode" + if ($PSCmdlet.ShouldProcess("Reflex", "Update")) + { + # Step 4: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Patch ` + -Body $bodyJson ` + -ContentType "application/json" ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -ResponseHeadersVariable "responseHeader" ` + -StatusCodeVariable "statusCode" + } # Step 5: Validate the response code - if ($statusCode -ne 200) { + if ($statusCode -ne 200) + { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error Write-Message -Message "Error: $($response.message)" -Level Error Write-Message -Message "Error Details: $($response.moreDetails)" -Level Error @@ -97,7 +104,8 @@ function Update-FabricReflex { Write-Message -Message "Reflex '$ReflexName' updated successfully!" -Level Info return $response } - catch { + catch + { # Step 7: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to update Reflex. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Reflex/Update-FabricReflexDefinition.ps1 b/source/Public/Reflex/Update-FabricReflexDefinition.ps1 similarity index 81% rename from FabricTools/public/Reflex/Update-FabricReflexDefinition.ps1 rename to source/Public/Reflex/Update-FabricReflexDefinition.ps1 index 45582d9a..8eb8862d 100644 --- a/FabricTools/public/Reflex/Update-FabricReflexDefinition.ps1 +++ b/source/Public/Reflex/Update-FabricReflexDefinition.ps1 @@ -3,7 +3,7 @@ Updates the definition of an existing Reflex in a specified Microsoft Fabric workspace. .DESCRIPTION - This function sends a PATCH request to the Microsoft Fabric API to update the definition of an existing Reflex + This function sends a PATCH request to the Microsoft Fabric API to update the definition of an existing Reflex in the specified workspace. It supports optional parameters for Reflex definition and platform-specific definition. .PARAMETER WorkspaceId @@ -27,10 +27,11 @@ - Calls `Test-TokenExpired` to ensure token validity before making the API request. Author: Tiago Balabuch - + #> -function Update-FabricReflexDefinition { - [CmdletBinding()] +function Update-FabricReflexDefinition +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -43,12 +44,13 @@ function Update-FabricReflexDefinition { [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$ReflexPathDefinition, - + [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$ReflexPathPlatformDefinition ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -58,22 +60,25 @@ function Update-FabricReflexDefinition { $apiEndpointUrl = "{0}/workspaces/{1}/reflexes/{2}/updateDefinition" -f $FabricConfig.BaseUrl, $WorkspaceId, $ReflexId #if ($UpdateMetadata -eq $true) { - if($ReflexPathPlatformDefinition){ - $apiEndpointUrl = "?updateMetadata=true" -f $apiEndpointUrl + if ($ReflexPathPlatformDefinition) + { + $apiEndpointUrl = "?updateMetadata=true" -f $apiEndpointUrl } Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug # Step 3: Construct the request body $body = @{ definition = @{ - parts = @() - } + parts = @() + } } - - if ($ReflexPathDefinition) { + + if ($ReflexPathDefinition) + { $ReflexEncodedContent = Convert-ToBase64 -filePath $ReflexPathDefinition - - if (-not [string]::IsNullOrEmpty($ReflexEncodedContent)) { + + if (-not [string]::IsNullOrEmpty($ReflexEncodedContent)) + { # Add new part to the parts array $body.definition.parts += @{ path = "ReflexEntities.json" @@ -81,15 +86,18 @@ function Update-FabricReflexDefinition { payloadType = "InlineBase64" } } - else { + else + { Write-Message -Message "Invalid or empty content in Reflex definition." -Level Error return $null } } - if ($ReflexPathPlatformDefinition) { + if ($ReflexPathPlatformDefinition) + { $ReflexEncodedPlatformContent = Convert-ToBase64 -filePath $ReflexPathPlatformDefinition - if (-not [string]::IsNullOrEmpty($ReflexEncodedPlatformContent)) { + if (-not [string]::IsNullOrEmpty($ReflexEncodedPlatformContent)) + { # Add new part to the parts array $body.definition.parts += @{ path = ".platform" @@ -97,7 +105,8 @@ function Update-FabricReflexDefinition { payloadType = "InlineBase64" } } - else { + else + { Write-Message -Message "Invalid or empty content in platform definition." -Level Error return $null } @@ -106,61 +115,71 @@ function Update-FabricReflexDefinition { $bodyJson = $body | ConvertTo-Json -Depth 10 Write-Message -Message "Request Body: $bodyJson" -Level Debug - # Step 4: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Post ` - -Body $bodyJson ` - -ContentType "application/json" ` - -ErrorAction Stop ` - -ResponseHeadersVariable "responseHeader" ` - -StatusCodeVariable "statusCode" - + if ($PSCmdlet.ShouldProcess($apiEndpointUrl, "Update Reflex Definition")) + { + + # Step 4: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Post ` + -Body $bodyJson ` + -ContentType "application/json" ` + -ErrorAction Stop ` + -ResponseHeadersVariable "responseHeader" ` + -StatusCodeVariable "statusCode" + } # Step 5: Handle and log the response - switch ($statusCode) { - 200 { + switch ($statusCode) + { + 200 + { Write-Message -Message "Update definition for Reflex '$ReflexId' created successfully!" -Level Info return $response } - 202 { + 202 + { Write-Message -Message "Update definition for Reflex '$ReflexId' accepted. Operation in progress!" -Level Info - + [string]$operationId = $responseHeader["x-ms-operation-id"] [string]$location = $responseHeader["Location"] - [string]$retryAfter = $responseHeader["Retry-After"] + [string]$retryAfter = $responseHeader["Retry-After"] Write-Message -Message "Operation ID: '$operationId'" -Level Debug Write-Message -Message "Location: '$location'" -Level Debug Write-Message -Message "Retry-After: '$retryAfter'" -Level Debug Write-Message -Message "Getting Long Running Operation status" -Level Debug - + $operationStatus = Get-FabricLongRunningOperation -operationId $operationId -location $location Write-Message -Message "Long Running Operation status: $operationStatus" -Level Debug # Handle operation result - if ($operationStatus.status -eq "Succeeded") { + if ($operationStatus.status -eq "Succeeded") + { Write-Message -Message "Operation Succeeded" -Level Debug Write-Message -Message "Getting Long Running Operation result" -Level Debug - + $operationResult = Get-FabricLongRunningOperationResult -operationId $operationId Write-Message -Message "Long Running Operation status: $operationResult" -Level Debug - + return $operationResult - } - else { + } + else + { Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Debug Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Error return $operationStatus - } - } - default { + } + } + default + { Write-Message -Message "Unexpected response code: $statusCode" -Level Error Write-Message -Message "Error details: $($response.message)" -Level Error throw "API request failed with status code $statusCode." } } } - catch { + catch + { # Step 6: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to update Reflex. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Report/Get-FabricReport.ps1 b/source/Public/Report/Get-FabricReport.ps1 similarity index 96% rename from FabricTools/public/Report/Get-FabricReport.ps1 rename to source/Public/Report/Get-FabricReport.ps1 index 0f655be2..ee09cba1 100644 --- a/FabricTools/public/Report/Get-FabricReport.ps1 +++ b/source/Public/Report/Get-FabricReport.ps1 @@ -28,7 +28,7 @@ - Calls `Test-TokenExpired` to ensure token validity before making the API request. Author: Tiago Balabuch - + #> function Get-FabricReport { [CmdletBinding()] @@ -58,15 +58,15 @@ function Get-FabricReport { Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired Write-Message -Message "Token validation completed." -Level Debug - + # Step 3: Initialize variables $continuationToken = $null $Reports = @() - + if (-not ([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.GetName().Name -eq "System.Web" })) { Add-Type -AssemblyName System.Web } - + # Step 4: Loop to retrieve all capacities with continuation token Write-Message -Message "Loop started to get continuation token" -Level Debug $baseApiEndpointUrl = "{0}/workspaces/{1}/reports" -f $FabricConfig.BaseUrl, $WorkspaceId @@ -81,7 +81,7 @@ function Get-FabricReport { $apiEndpointUrl = "{0}?continuationToken={1}" -f $apiEndpointUrl, $encodedToken } Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug - + # Step 6: Make the API request $response = Invoke-RestMethod ` -Headers $FabricConfig.FabricHeaders ` @@ -91,7 +91,7 @@ function Get-FabricReport { -SkipHttpErrorCheck ` -ResponseHeadersVariable "responseHeader" ` -StatusCodeVariable "statusCode" - + # Step 7: Validate the response code if ($statusCode -ne 200) { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error @@ -100,38 +100,34 @@ function Get-FabricReport { Write-Message "Error Code: $($response.errorCode)" -Level Error return $null } - + # Step 8: Add data to the list if ($null -ne $response) { Write-Message -Message "Adding data to the list" -Level Debug $Reports += $response.value - + # Update the continuation token if present if ($response.PSObject.Properties.Match("continuationToken")) { Write-Message -Message "Updating the continuation token" -Level Debug $continuationToken = $response.continuationToken Write-Message -Message "Continuation token: $continuationToken" -Level Debug - } - else { + } else { Write-Message -Message "Updating the continuation token to null" -Level Debug $continuationToken = $null } - } - else { + } else { Write-Message -Message "No data received from the API." -Level Warning break } } while ($null -ne $continuationToken) Write-Message -Message "Loop finished and all data added to the list" -Level Debug - + # Step 8: Filter results based on provided parameters $Report = if ($ReportId) { $Reports | Where-Object { $_.Id -eq $ReportId } - } - elseif ($ReportName) { + } elseif ($ReportName) { $Reports | Where-Object { $_.DisplayName -eq $ReportName } - } - else { + } else { # Return all Reports if no filter is provided Write-Message -Message "No filter provided. Returning all Reports." -Level Debug $Reports @@ -141,16 +137,14 @@ function Get-FabricReport { if ($Report) { Write-Message -Message "Report found in the Workspace '$WorkspaceId'." -Level Debug return $Report - } - else { + } else { Write-Message -Message "No Report found matching the provided criteria." -Level Warning return $null } - } - catch { + } catch { # Step 10: Capture and log error details $errorDetails = $_.Exception.Message Write-Message -Message "Failed to retrieve Report. Error: $errorDetails" -Level Error - } - -} + } + +} \ No newline at end of file diff --git a/FabricTools/public/Report/Get-FabricReportDefinition.ps1 b/source/Public/Report/Get-FabricReportDefinition.ps1 similarity index 97% rename from FabricTools/public/Report/Get-FabricReportDefinition.ps1 rename to source/Public/Report/Get-FabricReportDefinition.ps1 index ef06367d..4e3e8cf9 100644 --- a/FabricTools/public/Report/Get-FabricReportDefinition.ps1 +++ b/source/Public/Report/Get-FabricReportDefinition.ps1 @@ -28,7 +28,7 @@ - Calls `Test-TokenExpired` to ensure token validity before making the API request. Author: Tiago Balabuch - + #> function Get-FabricReportDefinition { [CmdletBinding()] @@ -53,11 +53,11 @@ function Get-FabricReportDefinition { # Step 3: Construct the API URL $apiEndpointUrl = "{0}/workspaces/{1}/reports/{2}/getDefinition" -f $FabricConfig.BaseUrl, $WorkspaceId, $ReportId - + if ($ReportFormat) { $apiEndpointUrl = "{0}?format={1}" -f $apiEndpointUrl, $ReportFormat } - + Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug # Step 4: Make the API request @@ -81,30 +81,29 @@ function Get-FabricReportDefinition { [string]$operationId = $responseHeader["x-ms-operation-id"] [string]$location = $responseHeader["Location"] - [string]$retryAfter = $responseHeader["Retry-After"] + [string]$retryAfter = $responseHeader["Retry-After"] Write-Message -Message "Operation ID: '$operationId'" -Level Debug Write-Message -Message "Location: '$location'" -Level Debug Write-Message -Message "Retry-After: '$retryAfter'" -Level Debug Write-Message -Message "Getting Long Running Operation status" -Level Debug - + $operationStatus = Get-FabricLongRunningOperation -operationId $operationId -location $location Write-Message -Message "Long Running Operation status: $operationStatus" -Level Debug # Handle operation result if ($operationStatus.status -eq "Succeeded") { Write-Message -Message "Operation Succeeded" -Level Debug Write-Message -Message "Getting Long Running Operation result" -Level Debug - + $operationResult = Get-FabricLongRunningOperationResult -operationId $operationId Write-Message -Message "Long Running Operation status: $operationResult" -Level Debug - + return $operationResult.definition.parts - } - else { + } else { Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Debug Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Error return $operationStatus - } + } } default { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error @@ -114,11 +113,10 @@ function Get-FabricReportDefinition { throw "API request failed with status code $statusCode." } } - } - catch { + } catch { # Step 9: Capture and log error details $errorDetails = $_.Exception.Message Write-Message -Message "Failed to retrieve Report. Error: $errorDetails" -Level Error - } - -} + } + +} \ No newline at end of file diff --git a/FabricTools/public/Report/New-FabricReport.ps1 b/source/Public/Report/New-FabricReport.ps1 similarity index 81% rename from FabricTools/public/Report/New-FabricReport.ps1 rename to source/Public/Report/New-FabricReport.ps1 index 9e61167c..c80e4a60 100644 --- a/FabricTools/public/Report/New-FabricReport.ps1 +++ b/source/Public/Report/New-FabricReport.ps1 @@ -3,7 +3,7 @@ Creates a new Report in a specified Microsoft Fabric workspace. .DESCRIPTION - This function sends a POST request to the Microsoft Fabric API to create a new Report + This function sends a POST request to the Microsoft Fabric API to create a new Report in the specified workspace. It supports optional parameters for Report description and path definitions. .PARAMETER WorkspaceId @@ -28,10 +28,11 @@ - Calls `Test-TokenExpired` to ensure token validity before making the API request. Author: Tiago Balabuch - + #> -function New-FabricReport { - [CmdletBinding()] +function New-FabricReport +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -49,8 +50,9 @@ function New-FabricReport { [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$ReportPathDefinition - ) - try { + ) + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -65,75 +67,86 @@ function New-FabricReport { displayName = $ReportName } - if ($ReportDescription) { + if ($ReportDescription) + { $body.description = $ReportDescription } - if ($ReportPathDefinition) { - if (-not $body.definition) { + if ($ReportPathDefinition) + { + if (-not $body.definition) + { $body.definition = @{ - parts = @() + parts = @() } } $jsonObjectParts = Get-FileDefinitionParts -sourceDirectory $ReportPathDefinition # Add new part to the parts array $body.definition.parts = $jsonObjectParts.parts } - + # Convert the body to JSON $bodyJson = $body | ConvertTo-Json -Depth 10 Write-Message -Message "Request Body: $bodyJson" -Level Debug - # Step 4: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Post ` - -Body $bodyJson ` - -ContentType "application/json" ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -ResponseHeadersVariable "responseHeader" ` - -StatusCodeVariable "statusCode" - + if ($PSCmdlet.ShouldProcess($ReportName, "Create Report")){ + # Step 4: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Post ` + -Body $bodyJson ` + -ContentType "application/json" ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -ResponseHeadersVariable "responseHeader" ` + -StatusCodeVariable "statusCode" + } + Write-Message -Message "Response Code: $statusCode" -Level Debug - + # Step 5: Handle and log the response - switch ($statusCode) { - 201 { + switch ($statusCode) + { + 201 + { Write-Message -Message "Report '$ReportName' created successfully!" -Level Info return $response } - 202 { + 202 + { Write-Message -Message "Report '$ReportName' creation accepted. Provisioning in progress!" -Level Info - + [string]$operationId = $responseHeader["x-ms-operation-id"] [string]$location = $responseHeader["Location"] - [string]$retryAfter = $responseHeader["Retry-After"] + [string]$retryAfter = $responseHeader["Retry-After"] Write-Message -Message "Operation ID: '$operationId'" -Level Debug Write-Message -Message "Location: '$location'" -Level Debug Write-Message -Message "Retry-After: '$retryAfter'" -Level Debug Write-Message -Message "Getting Long Running Operation status" -Level Debug - + $operationStatus = Get-FabricLongRunningOperation -operationId $operationId -location $location Write-Message -Message "Long Running Operation status: $operationStatus" -Level Debug # Handle operation result - if ($operationStatus.status -eq "Succeeded") { + if ($operationStatus.status -eq "Succeeded") + { Write-Message -Message "Operation Succeeded" -Level Debug Write-Message -Message "Getting Long Running Operation result" -Level Debug - + $operationResult = Get-FabricLongRunningOperationResult -operationId $operationId Write-Message -Message "Long Running Operation status: $operationResult" -Level Debug - + return $operationResult - } - else { + } + else + { Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Debug Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Error return $operationStatus - } + } } - default { + default + { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error Write-Message -Message "Error: $($response.message)" -Level Error Write-Message -Message "Error Details: $($response.moreDetails)" -Level Error @@ -142,7 +155,8 @@ function New-FabricReport { } } } - catch { + catch + { # Step 6: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to create Report. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Report/Remove-FabricReport.ps1 b/source/Public/Report/Remove-FabricReport.ps1 similarity index 74% rename from FabricTools/public/Report/Remove-FabricReport.ps1 rename to source/Public/Report/Remove-FabricReport.ps1 index 3ab69774..130a1762 100644 --- a/FabricTools/public/Report/Remove-FabricReport.ps1 +++ b/source/Public/Report/Remove-FabricReport.ps1 @@ -3,7 +3,7 @@ Removes an Report from a specified Microsoft Fabric workspace. .DESCRIPTION - This function sends a DELETE request to the Microsoft Fabric API to remove an Report + This function sends a DELETE request to the Microsoft Fabric API to remove an Report from the specified workspace using the provided WorkspaceId and ReportId. .PARAMETER WorkspaceId @@ -21,10 +21,11 @@ - Calls `Test-TokenExpired` to ensure token validity before making the API request. Author: Tiago Balabuch - + #> -function Remove-FabricReport { - [CmdletBinding()] +function Remove-FabricReport +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -34,7 +35,8 @@ function Remove-FabricReport { [ValidateNotNullOrEmpty()] [string]$ReportId ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -44,18 +46,22 @@ function Remove-FabricReport { $apiEndpointUrl = "{0}/workspaces/{1}/reports/{2}" -f $FabricConfig.BaseUrl, $WorkspaceId, $ReportId Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug - # Step 3: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Delete ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -ResponseHeadersVariable "responseHeader" ` - -StatusCodeVariable "statusCode" - - # Step 4: Handle response - if ($statusCode -ne 200) { + if ($PSCmdlet.ShouldProcess($apiEndpointUrl, "Remove Report")) + { + # Step 3: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Delete ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -ResponseHeadersVariable "responseHeader" ` + -StatusCodeVariable "statusCode" + } + + # Step 4: Handle response + if ($statusCode -ne 200) + { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error Write-Message -Message "Error: $($response.message)" -Level Error Write-Message -Message "Error Details: $($response.moreDetails)" -Level Error @@ -63,9 +69,10 @@ function Remove-FabricReport { return $null } - Write-Message -Message "Report '$ReportId' deleted successfully from workspace '$WorkspaceId'." -Level Info + Write-Message -Message "Report '$ReportId' deleted successfully from workspace '$WorkspaceId'." -Level Info } - catch { + catch + { # Step 5: Log and handle errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to delete Report '$ReportId'. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Report/Update-FabricReport.ps1 b/source/Public/Report/Update-FabricReport.ps1 similarity index 79% rename from FabricTools/public/Report/Update-FabricReport.ps1 rename to source/Public/Report/Update-FabricReport.ps1 index 08b852ea..c28725b0 100644 --- a/FabricTools/public/Report/Update-FabricReport.ps1 +++ b/source/Public/Report/Update-FabricReport.ps1 @@ -3,7 +3,7 @@ Updates an existing Report in a specified Microsoft Fabric workspace. .DESCRIPTION - This function sends a PATCH request to the Microsoft Fabric API to update an existing Report + This function sends a PATCH request to the Microsoft Fabric API to update an existing Report in the specified workspace. It supports optional parameters for Report description. .PARAMETER WorkspaceId @@ -27,15 +27,16 @@ - Calls `Test-TokenExpired` to ensure token validity before making the API request. Author: Tiago Balabuch - + #> -function Update-FabricReport { - [CmdletBinding()] +function Update-FabricReport +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [string]$WorkspaceId, - + [string]$WorkspaceId, + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$ReportId, @@ -49,7 +50,8 @@ function Update-FabricReport { [ValidateNotNullOrEmpty()] [string]$ReportDescription ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -64,7 +66,8 @@ function Update-FabricReport { displayName = $ReportName } - if ($ReportDescription) { + if ($ReportDescription) + { $body.description = $ReportDescription } @@ -72,20 +75,24 @@ function Update-FabricReport { $bodyJson = $body | ConvertTo-Json Write-Message -Message "Request Body: $bodyJson" -Level Debug - # Step 4: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Patch ` - -Body $bodyJson ` - -ContentType "application/json" ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -ResponseHeadersVariable "responseHeader" ` - -StatusCodeVariable "statusCode" + if ($PSCmdlet.ShouldProcess($ReportName, "Update Report")) + { + # Step 4: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Patch ` + -Body $bodyJson ` + -ContentType "application/json" ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -ResponseHeadersVariable "responseHeader" ` + -StatusCodeVariable "statusCode" + } # Step 5: Validate the response code - if ($statusCode -ne 200) { + if ($statusCode -ne 200) + { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error Write-Message -Message "Error: $($response.message)" -Level Error Write-Message -Message "Error Details: $($response.moreDetails)" -Level Error @@ -97,7 +104,8 @@ function Update-FabricReport { Write-Message -Message "Report '$ReportName' updated successfully!" -Level Info return $response } - catch { + catch + { # Step 7: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to update Report. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Report/Update-FabricReportDefinition.ps1 b/source/Public/Report/Update-FabricReportDefinition.ps1 similarity index 78% rename from FabricTools/public/Report/Update-FabricReportDefinition.ps1 rename to source/Public/Report/Update-FabricReportDefinition.ps1 index 1a7bb73f..2cb998c1 100644 --- a/FabricTools/public/Report/Update-FabricReportDefinition.ps1 +++ b/source/Public/Report/Update-FabricReportDefinition.ps1 @@ -3,7 +3,7 @@ Updates the definition of an existing Report in a specified Microsoft Fabric workspace. .DESCRIPTION - This function sends a PATCH request to the Microsoft Fabric API to update the definition of an existing Report + This function sends a PATCH request to the Microsoft Fabric API to update the definition of an existing Report in the specified workspace. It supports optional parameters for Report definition and platform-specific definition. .PARAMETER WorkspaceId @@ -24,10 +24,11 @@ - Calls `Test-TokenExpired` to ensure token validity before making the API request. Author: Tiago Balabuch - + #> -function Update-FabricReportDefinition { - [CmdletBinding()] +function Update-FabricReportDefinition +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -41,7 +42,8 @@ function Update-FabricReportDefinition { [ValidateNotNullOrEmpty()] [string]$ReportPathDefinition ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -56,14 +58,16 @@ function Update-FabricReportDefinition { # Step 3: Construct the request body $body = @{ definition = @{ - parts = @() - } + parts = @() + } } - - if ($ReportPathDefinition) { - if (-not $body.definition) { + + if ($ReportPathDefinition) + { + if (-not $body.definition) + { $body.definition = @{ - parts = @() + parts = @() } } $jsonObjectParts = Get-FileDefinitionParts -sourceDirectory $ReportPathDefinition @@ -71,15 +75,18 @@ function Update-FabricReportDefinition { $body.definition.parts = $jsonObjectParts.parts } # Check if any path is .platform - foreach ($part in $jsonObjectParts.parts) { - if ($part.path -eq ".platform") { + foreach ($part in $jsonObjectParts.parts) + { + if ($part.path -eq ".platform") + { $hasPlatformFile = $true Write-Message -Message "Platform File: $hasPlatformFile" -Level Debug } } - if($hasPlatformFile -eq $true) { - $apiEndpointUrl += "?updateMetadata=true" -f $apiEndpointUrl + if ($hasPlatformFile -eq $true) + { + $apiEndpointUrl += "?updateMetadata=true" -f $apiEndpointUrl } Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug @@ -87,57 +94,67 @@ function Update-FabricReportDefinition { $bodyJson = $body | ConvertTo-Json -Depth 10 Write-Message -Message "Request Body: $bodyJson" -Level Debug - # Step 4: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Post ` - -Body $bodyJson ` - -ContentType "application/json" ` - -ErrorAction Stop ` - -ResponseHeadersVariable "responseHeader" ` - -StatusCodeVariable "statusCode" - + if ($PSCmdlet.ShouldProcess($apiEndpointUrl, "Update Report Definition")) + { + # Step 4: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Post ` + -Body $bodyJson ` + -ContentType "application/json" ` + -ErrorAction Stop ` + -ResponseHeadersVariable "responseHeader" ` + -StatusCodeVariable "statusCode" + } + # Step 5: Handle and log the response - switch ($statusCode) { - 200 { + switch ($statusCode) + { + 200 + { Write-Message -Message "Update definition for Report '$ReportId' created successfully!" -Level Info return $response } - 202 { + 202 + { Write-Message -Message "Update definition for Report '$ReportId' accepted. Operation in progress!" -Level Info - + [string]$operationId = $responseHeader["x-ms-operation-id"] [string]$location = $responseHeader["Location"] - [string]$retryAfter = $responseHeader["Retry-After"] + [string]$retryAfter = $responseHeader["Retry-After"] Write-Message -Message "Operation ID: '$operationId'" -Level Debug Write-Message -Message "Location: '$location'" -Level Debug Write-Message -Message "Retry-After: '$retryAfter'" -Level Debug Write-Message -Message "Getting Long Running Operation status" -Level Debug - + $operationStatus = Get-FabricLongRunningOperation -operationId $operationId -location $location Write-Message -Message "Long Running Operation status: $operationStatus" -Level Debug # Handle operation result - if ($operationStatus.status -eq "Succeeded") { + if ($operationStatus.status -eq "Succeeded") + { Write-Message -Message "Operation Succeeded" -Level Debug Write-Message -Message "Update definition operation for Report '$ReportId' succeeded!" -Level Info return $operationStatus - } - else { + } + else + { Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Debug Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Error return $operationStatus - } - } - default { + } + } + default + { Write-Message -Message "Unexpected response code: $statusCode" -Level Error Write-Message -Message "Error details: $($response.message)" -Level Error throw "API request failed with status code $statusCode." } } } - catch { + catch + { # Step 6: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to update Report. Error: $errorDetails" -Level Error diff --git a/source/Public/Restore Points/Get-FabricRecoveryPoint.ps1 b/source/Public/Restore Points/Get-FabricRecoveryPoint.ps1 new file mode 100644 index 00000000..1f17a76f --- /dev/null +++ b/source/Public/Restore Points/Get-FabricRecoveryPoint.ps1 @@ -0,0 +1,130 @@ +<# +.SYNOPSIS +Get a list of Fabric recovery points. + +.DESCRIPTION +Get a list of Fabric recovery points. Results can be filter by date or type. + +.PARAMETER BaseUrl +Defaults to api.powerbi.com + +.PARAMETER WorkspaceGUID +This is the workspace GUID in which the data warehouse resides. + +.PARAMETER DataWarehouseGUID +The GUID for the data warehouse which we want to retrieve restore points for. + +.PARAMETER Since +Filter the results to only include restore points created after this date. + +.PARAMETER Type +Filter the results to only include restore points of this type. + +.PARAMETER CreateTime +The specific unique time of the restore point to remove. Get this from Get-FabricRecoveryPoint. + +.EXAMPLE +PS> Get-FabricRecoveryPoint -WorkspaceGUID 'GUID-GUID-GUID-GUID' -DataWarehouseGUID 'GUID-GUID-GUID-GUID' + +Gets all the available recovery points for the specified data warehouse, in the specified workspace. + +.NOTES +Based on API calls from this blog post: https://blog.fabric.microsoft.com/en-US/blog/the-art-of-data-warehouse-recovery-within-microsoft-fabric/ + +#> + +function Get-FabricRecoveryPoint { + param ( + [String]$WorkspaceGUID, + + [String]$DataWarehouseGUID, + + [String]$BaseUrl = 'api.powerbi.com', + + [DateTime]$Since, + + [ValidateSet("automatic", "userDefined")] + [string]$Type, + #TODO: accept a list of times + [string]$CreateTime + + ) + + #region handle the config parameters + if(-not $WorkspaceGUID) { + $WorkspaceGUID = Get-PSFConfigValue -FullName PSFabricTools.WorkspaceGUID + } + + if(-not $DataWarehouseGUID) { + $DataWarehouseGUID = Get-PSFConfigValue -FullName PSFabricTools.DataWarehouseGUID + } + + if(-not $BaseUrl) { + $BaseUrl = Get-PSFConfigValue -FullName PSFabricTools.BaseUrl + } + + if (-not $WorkspaceGUID -or -not $DataWarehouseGUID -or -not $BaseUrl) { + Stop-PSFFunction -Message 'WorkspaceGUID, DataWarehouseGUID, and BaseUrl are required parameters. Either set them with Set-FabricConfig or pass them in as parameter values' -EnableException $true + } else { + Write-PSFMessage -Level Verbose -Message ('WorkspaceGUID: {0}; DataWarehouseGUID: {1}; BaseUrl: {2}' -f $WorkspaceGUID, $DataWarehouseGUID, $BaseUrl) + } + #endregion + + #region setting up the API call + try { + # Get token and setup the uri + $getUriParam = @{ + BaseUrl = $BaseUrl + WorkspaceGUID = $WorkspaceGUID + DataWarehouseGUID = $DataWarehouseGUID + } + $iwr = Get-FabricUri @getUriParam + } catch { + Stop-PSFFunction -Message 'Failed to get Fabric URI - check authentication and parameters.' -ErrorRecord $_ -EnableException $true + } + #endregion + + #region call the API + if (-not $iwr) { + Stop-PSFFunction -Message 'No URI received from API - check authentication and parameters.' -ErrorRecord $_ -EnableException $true + } else { + + # set the body to list restore points + $command = [PSCustomObject]@{ + Commands = @(@{ + '$type' = 'WarehouseListRestorePointsCommand' + }) + } + + try { + # add the body and invoke + $iwr.Add('Body', ($command | ConvertTo-Json -Compress)) + $content = Invoke-WebRequest @iwr + + if($content) { + # change output to be a PowerShell object and view restore points + $restorePoints = ($content.Content | ConvertFrom-Json).operationInformation.progressDetail.restorePoints + + if($CreateTime) { + $restorePoints = $restorePoints | Select-Object @{l='createTimeWhere';e={get-date($_.createTime) -Format 'yyyy-MM-ddTHH:mm:ssZ'}}, * | Where-Object createTimeWhere -eq $createTime + } + + if($Since) { + $restorePoints = $restorePoints | Where-Object { $_.createTime -gt $Since } + } + + if($Type) { + $restorePoints = $restorePoints | Where-Object { $_.createMode -eq $Type } + } + + $restorePoints | Select-Object @{l='createTime';e={get-date($_.createTime) -Format 'yyyy-MM-ddTHH:mm:ssZ'}}, @{l='friendlyCreateTime';e={$_.createTime}}, label, createMode, type, createdByUserObjectId | Sort-Object createTime + #TODO: default view rather than select\sort? + } else { + Stop-PSFFunction -Message 'No Content received from API - check authentication and parameters.' -ErrorRecord $_ -EnableException $true + } + } catch { + Stop-PSFFunction -Message 'Issue calling Invoke-WebRequest' -ErrorRecord $_ -EnableException $true + } + } + #endregion +} diff --git a/source/Public/Restore Points/New-FabricRecoveryPoint.ps1 b/source/Public/Restore Points/New-FabricRecoveryPoint.ps1 new file mode 100644 index 00000000..7c718a2a --- /dev/null +++ b/source/Public/Restore Points/New-FabricRecoveryPoint.ps1 @@ -0,0 +1,101 @@ +<# +.SYNOPSIS +Create a recovery point for a Fabric data warehouse + +.DESCRIPTION +Create a recovery point for a Fabric data warehouse + +.PARAMETER BaseUrl +Defaults to api.powerbi.com + +.PARAMETER WorkspaceGUID +This is the workspace GUID in which the data warehouse resides. + +.PARAMETER DataWarehouseGUID +The GUID for the data warehouse which we want to retrieve restore points for. + +.EXAMPLE +PS> New-FabricRecoveryPoint + +Create a new recovery point for the data warehouse specified in the configuration. + +.EXAMPLE +PS> New-FabricRecoveryPoint -WorkspaceGUID 'GUID-GUID-GUID-GUID' -DataWarehouseGUID 'GUID-GUID-GUID-GUID' + +Create a new recovery point for the specified data warehouse, in the specified workspace. + +#> +function New-FabricRecoveryPoint { + [CmdletBinding(SupportsShouldProcess)] + param ( + [String]$WorkspaceGUID, + + [String]$DataWarehouseGUID, + + [String]$BaseUrl = 'api.powerbi.com' + ) + + #region handle the config parameters + if(-not $WorkspaceGUID) { + $WorkspaceGUID = Get-PSFConfigValue -FullName PSFabricTools.WorkspaceGUID + } + + if(-not $DataWarehouseGUID) { + $DataWarehouseGUID = Get-PSFConfigValue -FullName PSFabricTools.DataWarehouseGUID + } + + if(-not $BaseUrl) { + $BaseUrl = Get-PSFConfigValue -FullName PSFabricTools.BaseUrl + } + + if (-not $WorkspaceGUID -or -not $DataWarehouseGUID -or -not $BaseUrl) { + Stop-PSFFunction -Message 'WorkspaceGUID, DataWarehouseGUID, and BaseUrl are required parameters. Either set them with Set-FabricConfig or pass them in as parameter values' -EnableException $true + } else { + Write-PSFMessage -Level Verbose -Message ('WorkspaceGUID: {0}; DataWarehouseGUID: {1}; BaseUrl: {2}' -f $WorkspaceGUID, $DataWarehouseGUID, $BaseUrl) + } + #endregion + + if ($PSCmdlet.ShouldProcess("Create a recovery point for a Fabric Data Warehouse")) { + #region setting up the API call + try { + # Get token and setup the uri + $getUriParam = @{ + BaseUrl = $BaseUrl + WorkspaceGUID = $WorkspaceGUID + DataWarehouseGUID = $DataWarehouseGUID + } + $iwr = Get-FabricUri @getUriParam + } catch { + Stop-PSFFunction -Message 'Failed to get Fabric URI - check authentication and parameters.' -ErrorRecord $_ -EnableException $true + } + #endregion + + #region call the API + if (-not $iwr) { + Stop-PSFFunction -Message 'No URI received from API - check authentication and parameters.' -ErrorRecord $_ -EnableException $true + } else { + $command = [PSCustomObject]@{ + Commands = @(@{ + '$type' = 'WarehouseCreateRestorePointCommand' + }) + } + + try { + # add the body and invoke + $iwr.Add('Body', ($command | ConvertTo-Json -Compress)) + $content = Invoke-WebRequest @iwr + + if($content) { + # change output to be a PowerShell object and view new restore point + #TODO: output - select default view but return more? + ($content.Content | ConvertFrom-Json) | Select-Object progressState,@{l='type';e={$_.operationInformation.progressDetail.restorePoint.type}},@{l='createTime';e={get-date($_.operationInformation.progressDetail.restorePoint.createTime) -format 'yyyy-MM-ddTHH:mm:ssZ'}},@{l='friendlyCreateTime';e={$_.operationInformation.progressDetail.restorePoint.createTime}}, @{l='label';e={$_.operationInformation.progressDetail.restorePoint.label}}, @{l='createMode';e={$_.operationInformation.progressDetail.restorePoint.createMode}}, @{l='description';e={$_.operationInformation.progressDetail.restorePoint.description}}, @{l='createdByUserObjectId';e={$_.operationInformation.progressDetail.restorePoint.createdByUserObjectId}}, @{l='lastModifiedByUserObjectId';e={$_.operationInformation.progressDetail.restorePoint.lastModifiedByUserObjectId}}, @{l='lastModifiedTime';e={$_.operationInformation.progressDetail.restorePoint.lastModifiedTime}} + } else { + Stop-PSFFunction -Message 'No Content received from API - check authentication and parameters.' -ErrorRecord $_ -EnableException $true + } + } catch { + Stop-PSFFunction -Message 'Issue calling Invoke-WebRequest' -ErrorRecord $_ -EnableException $true + } + } + #endregion + } +} diff --git a/source/Public/Restore Points/Remove-FabricRecoveryPoint.ps1 b/source/Public/Restore Points/Remove-FabricRecoveryPoint.ps1 new file mode 100644 index 00000000..3b5a5dcb --- /dev/null +++ b/source/Public/Restore Points/Remove-FabricRecoveryPoint.ps1 @@ -0,0 +1,151 @@ +<# +.SYNOPSIS +Remove a selected Fabric Recovery Point. + +.DESCRIPTION +Remove a selected Fabric Recovery Point. + +.PARAMETER CreateTime +The specific unique time of the restore point to remove. Get this from Get-FabricRecoveryPoint. + +.PARAMETER BaseUrl +Defaults to api.powerbi.com + +.PARAMETER WorkspaceGUID +This is the workspace GUID in which the data warehouse resides. + +.PARAMETER DataWarehouseGUID +The GUID for the data warehouse which we want to retrieve restore points for. + +.EXAMPLE +PS> Remove-FabricRecoveryPoint -CreateTime '2024-07-23T11:20:26Z' + +Remove a specific restore point from a Fabric Data Warehouse that has been set using Set-FabricConfig. + +.EXAMPLE +PS> Remove-FabricRecoveryPoint -CreateTime '2024-07-23T11:20:26Z' -WorkspaceGUID 'GUID-GUID-GUID-GUID' -DataWarehouseGUID 'GUID-GUID-GUID-GUID' + +Remove a specific restore point from a Fabric Data Warehouse, specifying the workspace and data warehouse GUIDs. + +.NOTES +General notes +#> +function Remove-FabricRecoveryPoint { + [CmdletBinding(SupportsShouldProcess)] + param ( + [string]$CreateTime, + + [String]$WorkspaceGUID, + + [String]$DataWarehouseGUID, + + [String]$BaseUrl = 'api.powerbi.com' + + #TODO - implement piping from get? or a way of interactively choosing points to remove + ) + + #region handle the config parameters + if(-not $WorkspaceGUID) { + $WorkspaceGUID = Get-PSFConfigValue -FullName PSFabricTools.WorkspaceGUID + } + + if(-not $DataWarehouseGUID) { + $DataWarehouseGUID = Get-PSFConfigValue -FullName PSFabricTools.DataWarehouseGUID + } + + if(-not $BaseUrl) { + $BaseUrl = Get-PSFConfigValue -FullName PSFabricTools.BaseUrl + } + + if (-not $WorkspaceGUID -or -not $DataWarehouseGUID -or -not $BaseUrl) { + Stop-PSFFunction -Message 'WorkspaceGUID, DataWarehouseGUID, and BaseUrl are required parameters. Either set them with Set-FabricConfig or pass them in as parameter values' -EnableException $true + } else { + Write-PSFMessage -Level Verbose -Message ('WorkspaceGUID: {0}; DataWarehouseGUID: {1}; BaseUrl: {2}' -f $WorkspaceGUID, $DataWarehouseGUID, $BaseUrl) + } + #endregion + + if ($PSCmdlet.ShouldProcess("Remove recovery point for a Fabric Data Warehouse")) { + #region setting up the API call + try { + # Get token and setup the uri + $getUriParam = @{ + BaseUrl = $BaseUrl + WorkspaceGUID = $WorkspaceGUID + DataWarehouseGUID = $DataWarehouseGUID + } + $iwr = Get-FabricUri @getUriParam + } catch { + Stop-PSFFunction -Message 'Failed to get Fabric URI - check authentication and parameters.' -ErrorRecord $_ -EnableException $true + } + #endregion + + #region call the API + if (-not $iwr) { + Stop-PSFFunction -Message 'No URI received from API - check authentication and parameters.' -ErrorRecord $_ -EnableException $true + } else { + + # for the API this needs to be an array, even if it's just one item + [string[]]$CreateTimeObj = $CreateTime + + #region check restore point exists + #Get the restore point to make sure it exists - the fabric API doesn't really confirm we deleted anything so we will manually check + $getSplat = @{ + WorkspaceGUID = $WorkspaceGUID + DataWarehouseGUID = $DataWarehouseGUID + BaseUrl = $BaseUrl + CreateTime = $CreateTimeObj + } + + try { + if(Get-FabricRecoveryPoint @getSplat) { + Write-PSFMessage -Level Verbose -Message ('WorkspaceGUID: {0}; DataWarehouseGUID: {1}; BaseUrl: {2}; CreateTime: {3} - restore point exists' -f $WorkspaceGUID, $DataWarehouseGUID, $BaseUrl, $CreateTime) + } else { + Stop-PSFFunction -Message ('WorkspaceGUID: {0}; DataWarehouseGUID: {1}; BaseUrl: {2}; CreateTime: {3} - restore point not found!' -f $WorkspaceGUID, $DataWarehouseGUID, $BaseUrl, $CreateTime) -ErrorRecord $_ -EnableException $true + } + } catch { + Stop-PSFFunction -Message 'Issue calling Get-FabricRecoveryPoint to check restore point exists before removal' -ErrorRecord $_ -EnableException $true + } + #endregion + + #region remove the restore point + $command = [PSCustomObject]@{ + commands = @([ordered]@{ + '$type' = 'WarehouseDeleteRestorePointsCommand' + 'RestorePointsToDelete' = $CreateTimeObj + }) + } + + try { + # add the body and invoke + $iwr.Add('Body', ($command | ConvertTo-Json -Compress -Depth 3)) + $content = Invoke-WebRequest @iwr + + if($content) { + # change output to be a PowerShell object and view new restore point + #TODO: output - select default view but return more? + $results = ($content.Content | ConvertFrom-Json) + } else { + Stop-PSFFunction -Message 'No Content received from API - check authentication and parameters.' -ErrorRecord $_ -EnableException $true + } + } catch { + Stop-PSFFunction -Message 'Issue calling Invoke-WebRequest' -ErrorRecord $_ -EnableException $true + } + #endregion + + #region check restore point exists + try { + #Get the restore point to make sure it exists - the fabric API doesn't really confirm we deleted anything so we will manually check + if(Get-FabricRecoveryPoint @getSplat) { + Stop-PSFFunction -Message ('WorkspaceGUID: {0}; DataWarehouseGUID: {1}; BaseUrl: {2}; CreateTime: {3} - restore point not was not successfully removed!' -f $WorkspaceGUID, $DataWarehouseGUID, $BaseUrl, $CreateTime) -ErrorRecord $_ -EnableException $true + } else { + Write-PSFMessage -Level Output -Message ('WorkspaceGUID: {0}; DataWarehouseGUID: {1}; BaseUrl: {2}; CreateTime: {3} - restore point successfully removed' -f $WorkspaceGUID, $DataWarehouseGUID, $BaseUrl, $CreateTime) + $results + } + } catch { + Stop-PSFFunction -Message 'Issue calling Get-FabricRecoveryPoint to check restore point exists before removal' -ErrorRecord $_ -EnableException $true + } + #endregion + } + #endregion + } +} diff --git a/source/Public/Restore Points/Restore-FabricRecoveryPoint.ps1 b/source/Public/Restore Points/Restore-FabricRecoveryPoint.ps1 new file mode 100644 index 00000000..b0388fff --- /dev/null +++ b/source/Public/Restore Points/Restore-FabricRecoveryPoint.ps1 @@ -0,0 +1,179 @@ +<# +.SYNOPSIS +Restore a Fabric data warehouse to a specified restore pont. + +.DESCRIPTION +Restore a Fabric data warehouse to a specified restore pont. + +.PARAMETER CreateTime +The specific unique time of the restore point to remove. Get this from Get-FabricRecoveryPoint. + +.PARAMETER BaseUrl +Defaults to api.powerbi.com + +.PARAMETER WorkspaceGUID +This is the workspace GUID in which the data warehouse resides. + +.PARAMETER DataWarehouseGUID +The GUID for the data warehouse which we want to retrieve restore points for. + +.PARAMETER Wait +Wait for the restore to complete before returning. + +.EXAMPLE +PS> Restore-FabricRecoveryPoint -CreateTime '2024-07-23T11:20:26Z' + +Restore a Fabric Data Warehouse to a specific restore point that has been set using Set-FabricConfig. + +.EXAMPLE +PS> Restore-FabricRecoveryPoint -CreateTime '2024-07-23T11:20:26Z' -WorkspaceGUID 'GUID-GUID-GUID-GUID' -DataWarehouseGUID 'GUID-GUID-GUID-GUID' + +Restore a Fabric Data Warehouse to a specific restore point, specifying the workspace and data warehouse GUIDs. + +#> +function Restore-FabricRecoveryPoint { + [CmdletBinding(SupportsShouldProcess)] + param ( + [string]$CreateTime, + + [String]$WorkspaceGUID, + + [String]$DataWarehouseGUID, + + [String]$BaseUrl = 'api.powerbi.com', + + [switch]$Wait + + ) + + #region handle the config parameters + if(-not $WorkspaceGUID) { + $WorkspaceGUID = Get-PSFConfigValue -FullName PSFabricTools.WorkspaceGUID + } + + if(-not $DataWarehouseGUID) { + $DataWarehouseGUID = Get-PSFConfigValue -FullName PSFabricTools.DataWarehouseGUID + } + + if(-not $BaseUrl) { + $BaseUrl = Get-PSFConfigValue -FullName PSFabricTools.BaseUrl + } + + if (-not $WorkspaceGUID -or -not $DataWarehouseGUID -or -not $BaseUrl) { + Stop-PSFFunction -Message 'WorkspaceGUID, DataWarehouseGUID, and BaseUrl are required parameters. Either set them with Set-FabricConfig or pass them in as parameter values' -EnableException $true + } else { + Write-PSFMessage -Level Verbose -Message ('WorkspaceGUID: {0}; DataWarehouseGUID: {1}; BaseUrl: {2}' -f $WorkspaceGUID, $DataWarehouseGUID, $BaseUrl) + } + #endregion + + if ($PSCmdlet.ShouldProcess("Recover a Fabric Data Warehouse to a restore point")) { + #region setting up the API call + try { + # Get token and setup the uri + $getUriParam = @{ + BaseUrl = $BaseUrl + WorkspaceGUID = $WorkspaceGUID + DataWarehouseGUID = $DataWarehouseGUID + } + $iwr = Get-FabricUri @getUriParam + } catch { + Stop-PSFFunction -Message 'Failed to get Fabric URI - check authentication and parameters.' -ErrorRecord $_ -EnableException $true + } + #endregion + + #region call the API + if (-not $iwr) { + Stop-PSFFunction -Message 'No URI received from API - check authentication and parameters.' -ErrorRecord $_ -EnableException $true + } else { + + #region check restore point exists + #Get the restore point to make sure it exists before we try and restore to it + $getSplat = @{ + WorkspaceGUID = $WorkspaceGUID + DataWarehouseGUID = $DataWarehouseGUID + BaseUrl = $BaseUrl + CreateTime = $CreateTime + } + + try { + if(Get-FabricRecoveryPoint @getSplat) { + Write-PSFMessage -Level Verbose -Message ('WorkspaceGUID: {0}; DataWarehouseGUID: {1}; BaseUrl: {2}; CreateTime: {3} - restore point exists' -f $WorkspaceGUID, $DataWarehouseGUID, $BaseUrl, $CreateTime) + } else { + Stop-PSFFunction -Message ('WorkspaceGUID: {0}; DataWarehouseGUID: {1}; BaseUrl: {2}; CreateTime: {3} - restore point not found!' -f $WorkspaceGUID, $DataWarehouseGUID, $BaseUrl, $CreateTime) -ErrorRecord $_ -EnableException $true + } + } catch { + Stop-PSFFunction -Message 'Issue calling Get-FabricRecoveryPoint to check restore point exists before attempting recovery' -ErrorRecord $_ -EnableException $true + } + #endregion + + #region recover to the restore point + # command is now WarehouseRestoreInPlaceCommand and the RestorePoint is the create time of the specific restore point to use + $command = [PSCustomObject]@{ + commands = @([ordered]@{ + '$type' = 'WarehouseRestoreInPlaceCommand' + 'RestorePoint' = $CreateTime + }) + } + + try { + # add the body and invoke + $iwr.Add('Body', ($command | ConvertTo-Json -Compress)) + $content = Invoke-WebRequest @iwr + + if($content) { + #TODO: output - select default view but return more? + $content = ($content.Content | ConvertFrom-Json) + } else { + Stop-PSFFunction -Message 'No Content received from API - check authentication and parameters.' -ErrorRecord $_ -EnableException $true + } + } catch { + Stop-PSFFunction -Message 'Issue calling Invoke-WebRequest' -ErrorRecord $_ -EnableException $true + } + #endregion + + #region check the progress of the restore + if ($Wait) { + # we need to append batches to the uri + try { + + while($Wait) { + # Get token and setup the uri + $getUriParam = @{ + BaseUrl = $BaseUrl + WorkspaceGUID = $WorkspaceGUID + DataWarehouseGUID = $DataWarehouseGUID + BatchId = $content.batchId + } + $iwr = Get-FabricUri @getUriParam + + $restoreProgress = ((Invoke-WebRequest @iwr).Content | ConvertFrom-Json) + + if($restoreProgress.progressState -eq 'inProgress') { + Write-PSFMessage -Level Output -Message 'Restore in progress' + } elseif ($restoreProgress.progressState -eq 'success') { + Write-PSFMessage -Level Output -Message 'Restore completed successfully' + $restoreProgress | Select-Object progressState, @{l='startDateTimeUtc';e={$_.startTimeStamp }}, @{l='RestorePointCreateTime';e={$CreateTime }} + $wait = $false + break + } else { + Write-PSFMessage -Level Output -Message 'Restore failed' + $restoreProgress | Select-Object progressState, @{l='startDateTimeUtc';e={$_.startTimeStamp }} + $wait = $false + break + } + + # wait a few seconds + Start-Sleep -Seconds 3 + } + } catch { + Stop-PSFFunction -Message 'Failed to get Fabric URI for the batchId - check authentication and parameters.' -ErrorRecord $_ -EnableException $true + } + + } else { + Write-PSFMessage -Level Output -Message 'Restore in progress - use the -Wait parameter to wait for restore to complete' + $content + } + } + #endregion + } +} diff --git a/FabricTools/public/SQL Database/Get-FabricSQLDatabase.ps1 b/source/Public/SQL Database/Get-FabricSQLDatabase.ps1 similarity index 95% rename from FabricTools/public/SQL Database/Get-FabricSQLDatabase.ps1 rename to source/Public/SQL Database/Get-FabricSQLDatabase.ps1 index 6ba60a10..ddbfdbe8 100644 --- a/FabricTools/public/SQL Database/Get-FabricSQLDatabase.ps1 +++ b/source/Public/SQL Database/Get-FabricSQLDatabase.ps1 @@ -1,6 +1,6 @@ function Get-FabricSQLDatabase { -<# + <# .SYNOPSIS Retrieves Fabric SQLDatabases @@ -43,15 +43,15 @@ function Get-FabricSQLDatabase { .NOTES Revision History: - 2025-03-06 - KNO: Init version of the function -#> + #> -[CmdletBinding()] + [CmdletBinding()] param ( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string]$WorkspaceId, - [Alias("Name","DisplayName")] + [Alias("Name", "DisplayName")] [string]$SQLDatabaseName, [Alias("Id")] diff --git a/source/Public/SQL Database/New-FabricSQLDatabase.ps1 b/source/Public/SQL Database/New-FabricSQLDatabase.ps1 new file mode 100644 index 00000000..f6fb22db --- /dev/null +++ b/source/Public/SQL Database/New-FabricSQLDatabase.ps1 @@ -0,0 +1,96 @@ +<# +.SYNOPSIS +Creates a new SQL Database in a specified Microsoft Fabric workspace. + +.DESCRIPTION +This function sends a POST request to the Microsoft Fabric API to create a new SQL Database +in the specified workspace. It supports optional parameters for SQL Database description +and path definitions for the SQL Database content. + +.PARAMETER WorkspaceId +The unique identifier of the workspace where the SQL Database will be created. + +.PARAMETER Name +The name of the SQL Database to be created. + +.PARAMETER Description +An optional description for the SQL Database. + + +.EXAMPLE + New-FabricSQLDatabase -WorkspaceId "workspace-12345" -Name "NewDatabase" + + .NOTES +- Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. +- Calls `Test-TokenExpired` to ensure token validity before making the API request. + +Author: Kamil Nowinski + +#> + +function New-FabricSQLDatabase +{ + [CmdletBinding(SupportsShouldProcess)] + param ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [string]$WorkspaceId, + + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [ValidatePattern('^[a-zA-Z0-9_]*$')] + [string]$Name, + + [Parameter(Mandatory = $false)] + [ValidateNotNullOrEmpty()] + [string]$Description + ) + + try + { + # Step 1: Ensure token validity + Test-TokenExpired + + # Step 2: Construct the API URL + $apiEndpointUrl = "{0}/workspaces/{1}/sqldatabases" -f $FabricConfig.BaseUrl, $WorkspaceId + Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug + + # Step 3: Construct the request body + $body = @{ + displayName = $Name + } + + if ($Description) + { + $body.description = $Description + } + + $bodyJson = $body | ConvertTo-Json -Depth 10 + Write-Message -Message "Request Body: $bodyJson" -Level Debug + + if ($PSCmdlet.ShouldProcess($apiEndpointUrl, "Create SQL Database")) + { + + # Step 4: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Post ` + -Body $bodyJson ` + -ContentType "application/json" ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -ResponseHeadersVariable "responseHeader" ` + -StatusCodeVariable "statusCode" + } + # Step 5: Handle and log the response + Write-Message "RESPONSE: $response" -Level Debug + Test-FabricApiResponse -response $response -responseHeader $responseHeader -statusCode $statusCode -Name $Name -TypeName 'SQL Database' + } + catch + { + # Step 6: Handle and log errors + $errorDetails = $_.Exception.Message + Write-Message -Message "Failed to create SQL Database. Error: $errorDetails" -Level Error + } +} diff --git a/FabricTools/public/SQL Database/Remove-FabricSQLDatabase.ps1 b/source/Public/SQL Database/Remove-FabricSQLDatabase.ps1 similarity index 76% rename from FabricTools/public/SQL Database/Remove-FabricSQLDatabase.ps1 rename to source/Public/SQL Database/Remove-FabricSQLDatabase.ps1 index 799f07d4..382d4c98 100644 --- a/FabricTools/public/SQL Database/Remove-FabricSQLDatabase.ps1 +++ b/source/Public/SQL Database/Remove-FabricSQLDatabase.ps1 @@ -24,8 +24,9 @@ Author: Kamil Nowinski #> -function Remove-FabricSQLDatabase { - [CmdletBinding()] +function Remove-FabricSQLDatabase +{ + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -36,35 +37,42 @@ function Remove-FabricSQLDatabase { [string]$SQLDatabaseId ) - try { + try + { # Step 1: Ensure token validity Confirm-FabricAuthToken | Out-Null Test-TokenExpired - + # Step 2: Construct the API URL $apiEndpointUrl = "{0}/workspaces/{1}/sqldatabases/{2}" -f $FabricConfig.BaseUrl, $WorkspaceId, $SQLDatabaseId Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug - # Step 3: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Delete ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -StatusCodeVariable "statusCode" + if ($PSCmdlet.ShouldProcess($apiEndpointUrl, "Delete SQL Database")) + { + + # Step 3: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Delete ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -StatusCodeVariable "statusCode" + } # Step 4: Validate the response code - if ($statusCode -ne 200) { + if ($statusCode -ne 200) + { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error Write-Message -Message "Error: $($response.message)" -Level Error Write-Message "Error Code: $($response.errorCode)" -Level Error return $null } Write-Message -Message "SQL Database '$SQLDatabaseId' deleted successfully from workspace '$WorkspaceId'." -Level Info - + } - catch { + catch + { # Step 5: Log and handle errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to delete SQL Database '$SQLDatabaseId' from workspace '$WorkspaceId'. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/SQL Endpoints/Get-FabricSQLEndpoint.ps1 b/source/Public/SQL Endpoints/Get-FabricSQLEndpoint.ps1 similarity index 94% rename from FabricTools/public/SQL Endpoints/Get-FabricSQLEndpoint.ps1 rename to source/Public/SQL Endpoints/Get-FabricSQLEndpoint.ps1 index 6b760c91..8639f525 100644 --- a/FabricTools/public/SQL Endpoints/Get-FabricSQLEndpoint.ps1 +++ b/source/Public/SQL Endpoints/Get-FabricSQLEndpoint.ps1 @@ -3,9 +3,9 @@ Retrieves SQL Endpoints from a specified workspace in Fabric. .DESCRIPTION -The Get-FabricSQLEndpoint function retrieves SQL Endpoints from a specified workspace in Fabric. -It supports filtering by SQL Endpoint ID or SQL Endpoint Name. If both filters are provided, -an error message is returned. The function handles token validation, API requests with continuation +The Get-FabricSQLEndpoint function retrieves SQL Endpoints from a specified workspace in Fabric. +It supports filtering by SQL Endpoint ID or SQL Endpoint Name. If both filters are provided, +an error message is returned. The function handles token validation, API requests with continuation tokens, and processes the response to return the desired SQL Endpoint(s). .PARAMETER WorkspaceId @@ -60,27 +60,27 @@ function Get-FabricSQLEndpoint { # Step 3: Initialize variables $continuationToken = $null $SQLEndpoints = @() - + if (-not ([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.GetName().Name -eq "System.Web" })) { Add-Type -AssemblyName System.Web } - + # Step 4: Loop to retrieve all capacities with continuation token Write-Message -Message "Loop started to get continuation token" -Level Debug $baseApiEndpointUrl = "{0}/workspaces/{1}/SQLEndpoints" -f $FabricConfig.BaseUrl, $WorkspaceId - + do { # Step 5: Construct the API URL $apiEndpointUrl = $baseApiEndpointUrl - + if ($null -ne $continuationToken) { # URL-encode the continuation token $encodedToken = [System.Web.HttpUtility]::UrlEncode($continuationToken) $apiEndpointUrl = "{0}?continuationToken={1}" -f $apiEndpointUrl, $encodedToken } Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug - + # Step 6: Make the API request $response = Invoke-RestMethod ` -Headers $FabricConfig.FabricHeaders ` @@ -90,7 +90,7 @@ function Get-FabricSQLEndpoint { -SkipHttpErrorCheck ` -ResponseHeadersVariable "responseHeader" ` -StatusCodeVariable "statusCode" - + # Step 7: Validate the response code if ($statusCode -ne 200) { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error @@ -99,38 +99,34 @@ function Get-FabricSQLEndpoint { Write-Message "Error Code: $($response.errorCode)" -Level Error return $null } - + # Step 8: Add data to the list if ($null -ne $response) { Write-Message -Message "Adding data to the list" -Level Debug $SQLEndpoints += $response.value - + # Update the continuation token if present if ($response.PSObject.Properties.Match("continuationToken")) { Write-Message -Message "Updating the continuation token" -Level Debug $continuationToken = $response.continuationToken Write-Message -Message "Continuation token: $continuationToken" -Level Debug - } - else { + } else { Write-Message -Message "Updating the continuation token to null" -Level Debug $continuationToken = $null } - } - else { + } else { Write-Message -Message "No data received from the API." -Level Warning break } } while ($null -ne $continuationToken) Write-Message -Message "Loop finished and all data added to the list" -Level Debug - + # Step 8: Filter results based on provided parameters $SQLEndpoint = if ($SQLEndpointId) { $SQLEndpoints | Where-Object { $_.Id -eq $SQLEndpointId } - } - elseif ($SQLEndpointName) { + } elseif ($SQLEndpointName) { $SQLEndpoints | Where-Object { $_.DisplayName -eq $SQLEndpointName } - } - else { + } else { # Return all SQLEndpoints if no filter is provided Write-Message -Message "No filter provided. Returning all Paginated Reports." -Level Debug $SQLEndpoints @@ -140,15 +136,13 @@ function Get-FabricSQLEndpoint { if ($SQLEndpoint) { Write-Message -Message "Paginated Report found matching the specified criteria." -Level Debug return $SQLEndpoint - } - else { + } else { Write-Message -Message "No Paginated Report found matching the provided criteria." -Level Warning return $null } - } - catch { + } catch { # Step 10: Capture and log error details $errorDetails = $_.Exception.Message Write-Message -Message "Failed to retrieve Paginated Report. Error: $errorDetails" -Level Error - } -} + } +} \ No newline at end of file diff --git a/FabricTools/public/Semantic Model/Get-FabricSemanticModel.ps1 b/source/Public/Semantic Model/Get-FabricSemanticModel.ps1 similarity index 96% rename from FabricTools/public/Semantic Model/Get-FabricSemanticModel.ps1 rename to source/Public/Semantic Model/Get-FabricSemanticModel.ps1 index f0611bdf..653dcc22 100644 --- a/FabricTools/public/Semantic Model/Get-FabricSemanticModel.ps1 +++ b/source/Public/Semantic Model/Get-FabricSemanticModel.ps1 @@ -28,7 +28,7 @@ - Calls `Test-TokenExpired` to ensure token validity before making the API request. Author: Tiago Balabuch - + #> function Get-FabricSemanticModel { [CmdletBinding()] @@ -58,15 +58,15 @@ function Get-FabricSemanticModel { Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired Write-Message -Message "Token validation completed." -Level Debug - + # Step 3: Initialize variables $continuationToken = $null $SemanticModels = @() - + if (-not ([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.GetName().Name -eq "System.Web" })) { Add-Type -AssemblyName System.Web } - + # Step 4: Loop to retrieve all capacities with continuation token Write-Message -Message "Loop started to get continuation token" -Level Debug $baseApiEndpointUrl = "{0}/workspaces/{1}/semanticModels" -f $FabricConfig.BaseUrl, $WorkspaceId @@ -81,7 +81,7 @@ function Get-FabricSemanticModel { $apiEndpointUrl = "{0}?continuationToken={1}" -f $apiEndpointUrl, $encodedToken } Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug - + # Step 6: Make the API request $response = Invoke-RestMethod ` -Headers $FabricConfig.FabricHeaders ` @@ -91,7 +91,7 @@ function Get-FabricSemanticModel { -SkipHttpErrorCheck ` -ResponseHeadersVariable "responseHeader" ` -StatusCodeVariable "statusCode" - + # Step 7: Validate the response code if ($statusCode -ne 200) { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error @@ -100,38 +100,34 @@ function Get-FabricSemanticModel { Write-Message "Error Code: $($response.errorCode)" -Level Error return $null } - + # Step 8: Add data to the list if ($null -ne $response) { Write-Message -Message "Adding data to the list" -Level Debug $SemanticModels += $response.value - + # Update the continuation token if present if ($response.PSObject.Properties.Match("continuationToken")) { Write-Message -Message "Updating the continuation token" -Level Debug $continuationToken = $response.continuationToken Write-Message -Message "Continuation token: $continuationToken" -Level Debug - } - else { + } else { Write-Message -Message "Updating the continuation token to null" -Level Debug $continuationToken = $null } - } - else { + } else { Write-Message -Message "No data received from the API." -Level Warning break } } while ($null -ne $continuationToken) Write-Message -Message "Loop finished and all data added to the list" -Level Debug - + # Step 8: Filter results based on provided parameters $SemanticModel = if ($SemanticModelId) { $SemanticModels | Where-Object { $_.Id -eq $SemanticModelId } - } - elseif ($SemanticModelName) { + } elseif ($SemanticModelName) { $SemanticModels | Where-Object { $_.DisplayName -eq $SemanticModelName } - } - else { + } else { # Return all SemanticModels if no filter is provided Write-Message -Message "No filter provided. Returning all SemanticModels." -Level Debug $SemanticModels @@ -141,16 +137,14 @@ function Get-FabricSemanticModel { if ($SemanticModel) { Write-Message -Message "SemanticModel found in the Workspace '$WorkspaceId'." -Level Debug return $SemanticModel - } - else { + } else { Write-Message -Message "No SemanticModel found matching the provided criteria." -Level Warning return $null } - } - catch { + } catch { # Step 10: Capture and log error details $errorDetails = $_.Exception.Message Write-Message -Message "Failed to retrieve SemanticModel. Error: $errorDetails" -Level Error - } - -} + } + +} \ No newline at end of file diff --git a/FabricTools/public/Semantic Model/Get-FabricSemanticModelDefinition.ps1 b/source/Public/Semantic Model/Get-FabricSemanticModelDefinition.ps1 similarity index 97% rename from FabricTools/public/Semantic Model/Get-FabricSemanticModelDefinition.ps1 rename to source/Public/Semantic Model/Get-FabricSemanticModelDefinition.ps1 index fa38d546..d0083936 100644 --- a/FabricTools/public/Semantic Model/Get-FabricSemanticModelDefinition.ps1 +++ b/source/Public/Semantic Model/Get-FabricSemanticModelDefinition.ps1 @@ -28,7 +28,7 @@ - Calls `Test-TokenExpired` to ensure token validity before making the API request. Author: Tiago Balabuch - + #> function Get-FabricSemanticModelDefinition { [CmdletBinding()] @@ -54,11 +54,11 @@ function Get-FabricSemanticModelDefinition { # Step 3: Construct the API URL $apiEndpointUrl = "{0}/workspaces/{1}/semanticModels/{2}/getDefinition" -f $FabricConfig.BaseUrl, $WorkspaceId, $SemanticModelId - + if ($SemanticModelFormat) { $apiEndpointUrl = "{0}?format={1}" -f $apiEndpointUrl, $SemanticModelFormat } - + Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug # Step 4: Make the API request @@ -82,30 +82,29 @@ function Get-FabricSemanticModelDefinition { [string]$operationId = $responseHeader["x-ms-operation-id"] [string]$location = $responseHeader["Location"] - [string]$retryAfter = $responseHeader["Retry-After"] + [string]$retryAfter = $responseHeader["Retry-After"] Write-Message -Message "Operation ID: '$operationId'" -Level Debug Write-Message -Message "Location: '$location'" -Level Debug Write-Message -Message "Retry-After: '$retryAfter'" -Level Debug Write-Message -Message "Getting Long Running Operation status" -Level Debug - + $operationStatus = Get-FabricLongRunningOperation -operationId $operationId -location $location Write-Message -Message "Long Running Operation status: $operationStatus" -Level Debug # Handle operation result if ($operationStatus.status -eq "Succeeded") { Write-Message -Message "Operation Succeeded" -Level Debug Write-Message -Message "Getting Long Running Operation result" -Level Debug - + $operationResult = Get-FabricLongRunningOperationResult -operationId $operationId Write-Message -Message "Long Running Operation status: $operationResult" -Level Debug - + return $operationResult.definition.parts - } - else { + } else { Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Debug Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Error return $operationStatus - } + } } default { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error @@ -115,11 +114,10 @@ function Get-FabricSemanticModelDefinition { throw "API request failed with status code $statusCode." } } - } - catch { + } catch { # Step 9: Capture and log error details $errorDetails = $_.Exception.Message Write-Message -Message "Failed to retrieve SemanticModel. Error: $errorDetails" -Level Error - } - -} + } + +} \ No newline at end of file diff --git a/FabricTools/public/Semantic Model/New-FabricSemanticModel.ps1 b/source/Public/Semantic Model/New-FabricSemanticModel.ps1 similarity index 81% rename from FabricTools/public/Semantic Model/New-FabricSemanticModel.ps1 rename to source/Public/Semantic Model/New-FabricSemanticModel.ps1 index 4ff05a02..fd7fb974 100644 --- a/FabricTools/public/Semantic Model/New-FabricSemanticModel.ps1 +++ b/source/Public/Semantic Model/New-FabricSemanticModel.ps1 @@ -3,7 +3,7 @@ Creates a new SemanticModel in a specified Microsoft Fabric workspace. .DESCRIPTION - This function sends a POST request to the Microsoft Fabric API to create a new SemanticModel + This function sends a POST request to the Microsoft Fabric API to create a new SemanticModel in the specified workspace. It supports optional parameters for SemanticModel description and path definitions. .PARAMETER WorkspaceId @@ -27,10 +27,11 @@ - Calls `Test-TokenExpired` to ensure token validity before making the API request. Author: Tiago Balabuch - + #> -function New-FabricSemanticModel { - [CmdletBinding()] +function New-FabricSemanticModel +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -48,8 +49,9 @@ function New-FabricSemanticModel { [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$SemanticModelPathDefinition - ) - try { + ) + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -62,73 +64,84 @@ function New-FabricSemanticModel { # Step 3: Construct the request body $body = @{ displayName = $SemanticModelName - definition = @{ - parts = @() - }} + definition = @{ + parts = @() + } + } $jsonObjectParts = Get-FileDefinitionParts -sourceDirectory $SemanticModelPathDefinition # Add new part to the parts array $body.definition.parts = $jsonObjectParts.parts - if ($SemanticModelDescription) { + if ($SemanticModelDescription) + { $body.description = $SemanticModelDescription } - + # Convert the body to JSON $bodyJson = $body | ConvertTo-Json -Depth 10 Write-Message -Message "Request Body: $bodyJson" -Level Debug - # Step 4: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Post ` - -Body $bodyJson ` - -ContentType "application/json" ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -ResponseHeadersVariable "responseHeader" ` - -StatusCodeVariable "statusCode" - + if ($PSCmdlet.ShouldProcess("Create SemanticModel", "Creating the SemanticModel '$SemanticModelName' in workspace '$WorkspaceId'.")) + { + # Step 4: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Post ` + -Body $bodyJson ` + -ContentType "application/json" ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -ResponseHeadersVariable "responseHeader" ` + -StatusCodeVariable "statusCode" + } + Write-Message -Message "Response Code: $statusCode" -Level Debug - + # Step 5: Handle and log the response - switch ($statusCode) { - 201 { + switch ($statusCode) + { + 201 + { Write-Message -Message "SemanticModel '$SemanticModelName' created successfully!" -Level Info return $response } - 202 { + 202 + { Write-Message -Message "SemanticModel '$SemanticModelName' creation accepted. Provisioning in progress!" -Level Info - + [string]$operationId = $responseHeader["x-ms-operation-id"] [string]$location = $responseHeader["Location"] - [string]$retryAfter = $responseHeader["Retry-After"] + [string]$retryAfter = $responseHeader["Retry-After"] Write-Message -Message "Operation ID: '$operationId'" -Level Debug Write-Message -Message "Location: '$location'" -Level Debug Write-Message -Message "Retry-After: '$retryAfter'" -Level Debug Write-Message -Message "Getting Long Running Operation status" -Level Debug - + $operationStatus = Get-FabricLongRunningOperation -operationId $operationId -location $location Write-Message -Message "Long Running Operation status: $operationStatus" -Level Debug # Handle operation result - if ($operationStatus.status -eq "Succeeded") { + if ($operationStatus.status -eq "Succeeded") + { Write-Message -Message "Operation Succeeded" -Level Debug Write-Message -Message "Getting Long Running Operation result" -Level Debug - + $operationResult = Get-FabricLongRunningOperationResult -operationId $operationId Write-Message -Message "Long Running Operation status: $operationResult" -Level Debug - + return $operationResult - } - else { + } + else + { Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Debug Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Error return $operationStatus - } + } } - default { + default + { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error Write-Message -Message "Error: $($response.message)" -Level Error Write-Message -Message "Error Details: $($response.moreDetails)" -Level Error @@ -137,7 +150,8 @@ function New-FabricSemanticModel { } } } - catch { + catch + { # Step 6: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to create SemanticModel. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Semantic Model/Remove-FabricSemanticModel.ps1 b/source/Public/Semantic Model/Remove-FabricSemanticModel.ps1 similarity index 76% rename from FabricTools/public/Semantic Model/Remove-FabricSemanticModel.ps1 rename to source/Public/Semantic Model/Remove-FabricSemanticModel.ps1 index 4e12b229..2f165eee 100644 --- a/FabricTools/public/Semantic Model/Remove-FabricSemanticModel.ps1 +++ b/source/Public/Semantic Model/Remove-FabricSemanticModel.ps1 @@ -3,7 +3,7 @@ Removes an SemanticModel from a specified Microsoft Fabric workspace. .DESCRIPTION - This function sends a DELETE request to the Microsoft Fabric API to remove an SemanticModel + This function sends a DELETE request to the Microsoft Fabric API to remove an SemanticModel from the specified workspace using the provided WorkspaceId and SemanticModelId. .PARAMETER WorkspaceId @@ -21,10 +21,11 @@ - Calls `Test-TokenExpired` to ensure token validity before making the API request. Author: Tiago Balabuch - + #> -function Remove-FabricSemanticModel { - [CmdletBinding()] +function Remove-FabricSemanticModel +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -34,7 +35,8 @@ function Remove-FabricSemanticModel { [ValidateNotNullOrEmpty()] [string]$SemanticModelId ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -44,18 +46,22 @@ function Remove-FabricSemanticModel { $apiEndpointUrl = "{0}/workspaces/{1}/semanticModels/{2}" -f $FabricConfig.BaseUrl, $WorkspaceId, $SemanticModelId Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug - # Step 3: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Delete ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -ResponseHeadersVariable "responseHeader" ` - -StatusCodeVariable "statusCode" - - # Step 4: Handle response - if ($statusCode -ne 200) { + if ($PSCmdlet.ShouldProcess($apiEndpointUrl, "Remove SemanticModel")) + { + # Step 3: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Delete ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -ResponseHeadersVariable "responseHeader" ` + -StatusCodeVariable "statusCode" + } + + # Step 4: Handle response + if ($statusCode -ne 200) + { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error Write-Message -Message "Error: $($response.message)" -Level Error Write-Message -Message "Error Details: $($response.moreDetails)" -Level Error @@ -63,9 +69,10 @@ function Remove-FabricSemanticModel { return $null } - Write-Message -Message "SemanticModel '$SemanticModelId' deleted successfully from workspace '$WorkspaceId'." -Level Info + Write-Message -Message "SemanticModel '$SemanticModelId' deleted successfully from workspace '$WorkspaceId'." -Level Info } - catch { + catch + { # Step 5: Log and handle errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to delete SemanticModel '$SemanticModelId'. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Semantic Model/Update-FabricSemanticModel.ps1 b/source/Public/Semantic Model/Update-FabricSemanticModel.ps1 similarity index 78% rename from FabricTools/public/Semantic Model/Update-FabricSemanticModel.ps1 rename to source/Public/Semantic Model/Update-FabricSemanticModel.ps1 index 12974b58..5fa9ceaa 100644 --- a/FabricTools/public/Semantic Model/Update-FabricSemanticModel.ps1 +++ b/source/Public/Semantic Model/Update-FabricSemanticModel.ps1 @@ -3,7 +3,7 @@ Updates an existing SemanticModel in a specified Microsoft Fabric workspace. .DESCRIPTION - This function sends a PATCH request to the Microsoft Fabric API to update an existing SemanticModel + This function sends a PATCH request to the Microsoft Fabric API to update an existing SemanticModel in the specified workspace. It supports optional parameters for SemanticModel description. .PARAMETER WorkspaceId @@ -27,15 +27,16 @@ - Calls `Test-TokenExpired` to ensure token validity before making the API request. Author: Tiago Balabuch - + #> -function Update-FabricSemanticModel { - [CmdletBinding()] +function Update-FabricSemanticModel +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [string]$WorkspaceId, - + [string]$WorkspaceId, + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$SemanticModelId, @@ -49,7 +50,8 @@ function Update-FabricSemanticModel { [ValidateNotNullOrEmpty()] [string]$SemanticModelDescription ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -64,7 +66,8 @@ function Update-FabricSemanticModel { displayName = $SemanticModelName } - if ($SemanticModelDescription) { + if ($SemanticModelDescription) + { $body.description = $SemanticModelDescription } @@ -72,20 +75,24 @@ function Update-FabricSemanticModel { $bodyJson = $body | ConvertTo-Json Write-Message -Message "Request Body: $bodyJson" -Level Debug - # Step 4: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Patch ` - -Body $bodyJson ` - -ContentType "application/json" ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -ResponseHeadersVariable "responseHeader" ` - -StatusCodeVariable "statusCode" + if ($PSCmdlet.ShouldProcess("Update SemanticModel", "Updating the SemanticModel with ID '$SemanticModelId' in workspace '$WorkspaceId'.")) + { + # Step 4: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Patch ` + -Body $bodyJson ` + -ContentType "application/json" ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -ResponseHeadersVariable "responseHeader" ` + -StatusCodeVariable "statusCode" + } # Step 5: Validate the response code - if ($statusCode -ne 200) { + if ($statusCode -ne 200) + { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error Write-Message -Message "Error: $($response.message)" -Level Error Write-Message -Message "Error Details: $($response.moreDetails)" -Level Error @@ -97,7 +104,8 @@ function Update-FabricSemanticModel { Write-Message -Message "SemanticModel '$SemanticModelName' updated successfully!" -Level Info return $response } - catch { + catch + { # Step 7: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to update SemanticModel. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Semantic Model/Update-FabricSemanticModelDefinition.ps1 b/source/Public/Semantic Model/Update-FabricSemanticModelDefinition.ps1 similarity index 80% rename from FabricTools/public/Semantic Model/Update-FabricSemanticModelDefinition.ps1 rename to source/Public/Semantic Model/Update-FabricSemanticModelDefinition.ps1 index 67e146ea..08c0b59b 100644 --- a/FabricTools/public/Semantic Model/Update-FabricSemanticModelDefinition.ps1 +++ b/source/Public/Semantic Model/Update-FabricSemanticModelDefinition.ps1 @@ -3,7 +3,7 @@ Updates the definition of an existing SemanticModel in a specified Microsoft Fabric workspace. .DESCRIPTION - This function sends a PATCH request to the Microsoft Fabric API to update the definition of an existing SemanticModel + This function sends a PATCH request to the Microsoft Fabric API to update the definition of an existing SemanticModel in the specified workspace. It supports optional parameters for SemanticModel definition and platform-specific definition. .PARAMETER WorkspaceId @@ -24,10 +24,11 @@ - Calls `Test-TokenExpired` to ensure token validity before making the API request. Author: Tiago Balabuch - + #> -function Update-FabricSemanticModelDefinition { - [CmdletBinding()] +function Update-FabricSemanticModelDefinition +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -41,7 +42,8 @@ function Update-FabricSemanticModelDefinition { [ValidateNotNullOrEmpty()] [string]$SemanticModelPathDefinition ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -54,22 +56,25 @@ function Update-FabricSemanticModelDefinition { $body = @{ definition = @{ parts = @() - } + } } - + $jsonObjectParts = Get-FileDefinitionParts -sourceDirectory $SemanticModelPathDefinition # Add new part to the parts array $body.definition.parts = $jsonObjectParts.parts # Check if any path is .platform - foreach ($part in $jsonObjectParts.parts) { - if ($part.path -eq ".platform") { + foreach ($part in $jsonObjectParts.parts) + { + if ($part.path -eq ".platform") + { $hasPlatformFile = $true Write-Message -Message "Platform File: $hasPlatformFile" -Level Debug } } - if ($hasPlatformFile -eq $true) { - $apiEndpointUrl = "?updateMetadata=true" -f $apiEndpointUrl + if ($hasPlatformFile -eq $true) + { + $apiEndpointUrl = "?updateMetadata=true" -f $apiEndpointUrl } Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug @@ -77,57 +82,67 @@ function Update-FabricSemanticModelDefinition { $bodyJson = $body | ConvertTo-Json -Depth 10 Write-Message -Message "Request Body: $bodyJson" -Level Debug - # Step 4: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Post ` - -Body $bodyJson ` - -ContentType "application/json" ` - -ErrorAction Stop ` - -ResponseHeadersVariable "responseHeader" ` - -StatusCodeVariable "statusCode" - + if ($PSCmdlet.ShouldProcess($apiEndpointUrl, "Update SemanticModel Definition")) + { + # Step 4: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Post ` + -Body $bodyJson ` + -ContentType "application/json" ` + -ErrorAction Stop ` + -ResponseHeadersVariable "responseHeader" ` + -StatusCodeVariable "statusCode" + } + # Step 5: Handle and log the response - switch ($statusCode) { - 200 { + switch ($statusCode) + { + 200 + { Write-Message -Message "Update definition for SemanticModel '$SemanticModelId' created successfully!" -Level Info return $response } - 202 { + 202 + { Write-Message -Message "Update definition for SemanticModel '$SemanticModelId' accepted. Operation in progress!" -Level Info - + [string]$operationId = $responseHeader["x-ms-operation-id"] [string]$location = $responseHeader["Location"] - [string]$retryAfter = $responseHeader["Retry-After"] + [string]$retryAfter = $responseHeader["Retry-After"] Write-Message -Message "Operation ID: '$operationId'" -Level Debug Write-Message -Message "Location: '$location'" -Level Debug Write-Message -Message "Retry-After: '$retryAfter'" -Level Debug Write-Message -Message "Getting Long Running Operation status" -Level Debug - + $operationStatus = Get-FabricLongRunningOperation -operationId $operationId -location $location Write-Message -Message "Long Running Operation status: $operationStatus" -Level Debug # Handle operation result - if ($operationStatus.status -eq "Succeeded") { + if ($operationStatus.status -eq "Succeeded") + { Write-Message -Message "Operation Succeeded" -Level Debug Write-Message -Message "Update definition operation for Semantic Model '$SemanticModelId' succeeded!" -Level Info return $operationStatus - } - else { + } + else + { Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Debug Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Error return $operationStatus - } - } - default { + } + } + default + { Write-Message -Message "Unexpected response code: $statusCode" -Level Error Write-Message -Message "Error details: $($response.message)" -Level Error throw "API request failed with status code $statusCode." } } } - catch { + catch + { # Step 6: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to update SemanticModel. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Set-FabricAuthToken.ps1 b/source/Public/Set-FabricAuthToken.ps1 similarity index 54% rename from FabricTools/public/Set-FabricAuthToken.ps1 rename to source/Public/Set-FabricAuthToken.ps1 index 591f8ec9..0781c372 100644 --- a/FabricTools/public/Set-FabricAuthToken.ps1 +++ b/source/Public/Set-FabricAuthToken.ps1 @@ -1,50 +1,53 @@ <# .SYNOPSIS - Sets the Fabric authentication token. +Sets the Fabric authentication token. .DESCRIPTION - The Set-FabricAuthToken function sets the Fabric authentication token. It checks if an Azure context is already available. If not, it connects to the Azure account using either a service principal ID and secret, a provided credential, or interactive login. It then gets the Azure context and sets the Fabric authentication token. +The Set-FabricAuthToken function sets the Fabric authentication token. It checks if an Azure context is already available. If not, it connects to the Azure account using either a service principal ID and secret, a provided credential, or interactive login. It then gets the Azure context and sets the Fabric authentication token. .PARAMETER azContext - The Azure context. If not provided, the function connects to the Azure account and gets the context. +The Azure context. If not provided, the function connects to the Azure account and gets the context. .PARAMETER servicePrincipalId - The service principal ID. If provided, the function uses this ID and the service principal secret to connect to the Azure account. +The service principal ID. If provided, the function uses this ID and the service principal secret to connect to the Azure account. .PARAMETER servicePrincipalSecret - The service principal secret. Used with the service principal ID to connect to the Azure account. +The service principal secret. Used with the service principal ID to connect to the Azure account. .PARAMETER tenantId - The tenant ID. Used with the service principal ID and secret or the credential to connect to the Azure account. +The tenant ID. Used with the service principal ID and secret or the credential to connect to the Azure account. .PARAMETER credential - The credential. If provided, the function uses this credential to connect to the Azure account. +The credential. If provided, the function uses this credential to connect to the Azure account. + +.PARAMETER reset +A switch parameter. If provided, the function resets the Fabric authentication token. + +.PARAMETER apiUrl +The API URL. If provided, the function sets the Fabric API URL to this value. .EXAMPLE - Set-FabricAuthToken -servicePrincipalId "12345678-90ab-cdef-1234-567890abcdef" -servicePrincipalSecret "secret" -tenantId "12345678-90ab-cdef-1234-567890abcdef" +Set-FabricAuthToken -servicePrincipalId "12345678-90ab-cdef-1234-567890abcdef" -servicePrincipalSecret "secret" -tenantId "12345678-90ab-cdef-1234-567890abcdef" - This command sets the Fabric authentication token using the provided service principal ID, secret, and tenant ID. +This command sets the Fabric authentication token using the provided service principal ID, secret, and tenant ID. .INPUTS - String, SecureString, PSCredential. You can pipe a string that contains the service principal ID, a secure string that contains the service principal secret, and a PSCredential object that contains the credential to Set-FabricAuthToken. +String, SecureString, PSCredential. You can pipe a string that contains the service principal ID, a secure string that contains the service principal secret, and a PSCredential object that contains the credential to Set-FabricAuthToken. .OUTPUTS - None. This function does not return any output. +None. This function does not return any output. .NOTES - This function was originally written by Rui Romano. - https://github.com/RuiRomano/fabricps-pbip +This function was originally written by Rui Romano. +https://github.com/RuiRomano/fabricps-pbip #> - function Set-FabricAuthToken { - <# - .SYNOPSIS - Set authentication token for the Fabric service - #> + [OutputType([System.Collections.Hashtable])] + [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingConvertToSecureStringWithPlainText", "", Justification = "To pass current unit tests")] [CmdletBinding(SupportsShouldProcess)] param ( - [string] $servicePrincipalId + [string] $servicePrincipalId , [string] $servicePrincipalSecret , [PSCredential] $credential , [string] $tenantId @@ -52,7 +55,9 @@ function Set-FabricAuthToken { , [string] $apiUrl ) - if (!$reset) { + + if (!$reset) + { $azContext = Get-AzContext } @@ -77,8 +82,9 @@ function Set-FabricAuthToken { } $azContext = Get-AzContext } - if ($PSCmdlet.ShouldProcess("Setting Fabric authentication token for $($azContext.Account)")) { - Write-Output "Connnected: $($azContext.Account)" + if ($PSCmdlet.ShouldProcess("Setting Fabric authentication token for $($azContext.Account)")) + { + Write-Output "Connected: $($azContext.Account)" Write-Output "Fabric ResourceUrl: $($FabricSession.ResourceUrl)" $FabricSession.AccessToken = (Get-AzAccessToken -AsSecureString -ResourceUrl $FabricSession.ResourceUrl) @@ -96,9 +102,9 @@ function Set-FabricAuthToken { # Copy session values to exposed $FabricConfig $FabricConfig.TenantIdGlobal = $FabricSession.AccessToken.TenantId $FabricConfig.TokenExpiresOn = $FabricSession.AccessToken.ExpiresOn - $FabricConfig.FabricHeaders = $FabricSession.HeaderParams + $FabricConfig.FabricHeaders = $FabricSession.HeaderParams return $($FabricSession.FabricToken) } -} \ No newline at end of file +} diff --git a/FabricTools/public/Spark Job Definition/Get-FabricSparkJobDefinition.ps1 b/source/Public/Spark Job Definition/Get-FabricSparkJobDefinition.ps1 similarity index 96% rename from FabricTools/public/Spark Job Definition/Get-FabricSparkJobDefinition.ps1 rename to source/Public/Spark Job Definition/Get-FabricSparkJobDefinition.ps1 index 09524fb4..13000c7b 100644 --- a/FabricTools/public/Spark Job Definition/Get-FabricSparkJobDefinition.ps1 +++ b/source/Public/Spark Job Definition/Get-FabricSparkJobDefinition.ps1 @@ -28,7 +28,7 @@ - Calls `Test-TokenExpired` to ensure token validity before making the API request. Author: Tiago Balabuch - + #> function Get-FabricSparkJobDefinition { [CmdletBinding()] @@ -58,15 +58,15 @@ function Get-FabricSparkJobDefinition { Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired Write-Message -Message "Token validation completed." -Level Debug - + # Step 3: Initialize variables $continuationToken = $null $SparkJobDefinitions = @() - + if (-not ([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.GetName().Name -eq "System.Web" })) { Add-Type -AssemblyName System.Web } - + # Step 4: Loop to retrieve all capacities with continuation token Write-Message -Message "Loop started to get continuation token" -Level Debug $baseApiEndpointUrl = "{0}/workspaces/{1}/sparkJobDefinitions" -f $FabricConfig.BaseUrl, $WorkspaceId @@ -81,7 +81,7 @@ function Get-FabricSparkJobDefinition { $apiEndpointUrl = "{0}?continuationToken={1}" -f $apiEndpointUrl, $encodedToken } Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug - + # Step 6: Make the API request $response = Invoke-RestMethod ` -Headers $FabricConfig.FabricHeaders ` @@ -91,7 +91,7 @@ function Get-FabricSparkJobDefinition { -SkipHttpErrorCheck ` -ResponseHeadersVariable "responseHeader" ` -StatusCodeVariable "statusCode" - + # Step 7: Validate the response code if ($statusCode -ne 200) { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error @@ -100,38 +100,34 @@ function Get-FabricSparkJobDefinition { Write-Message "Error Code: $($response.errorCode)" -Level Error return $null } - + # Step 8: Add data to the list if ($null -ne $response) { Write-Message -Message "Adding data to the list" -Level Debug $SparkJobDefinitions += $response.value - + # Update the continuation token if present if ($response.PSObject.Properties.Match("continuationToken")) { Write-Message -Message "Updating the continuation token" -Level Debug $continuationToken = $response.continuationToken Write-Message -Message "Continuation token: $continuationToken" -Level Debug - } - else { + } else { Write-Message -Message "Updating the continuation token to null" -Level Debug $continuationToken = $null } - } - else { + } else { Write-Message -Message "No data received from the API." -Level Warning break } } while ($null -ne $continuationToken) Write-Message -Message "Loop finished and all data added to the list" -Level Debug - + # Step 8: Filter results based on provided parameters $SparkJobDefinition = if ($SparkJobDefinitionId) { $SparkJobDefinitions | Where-Object { $_.Id -eq $SparkJobDefinitionId } - } - elseif ($SparkJobDefinitionName) { + } elseif ($SparkJobDefinitionName) { $SparkJobDefinitions | Where-Object { $_.DisplayName -eq $SparkJobDefinitionName } - } - else { + } else { # Return all SparkJobDefinitions if no filter is provided Write-Message -Message "No filter provided. Returning all SparkJobDefinitions." -Level Debug $SparkJobDefinitions @@ -141,16 +137,14 @@ function Get-FabricSparkJobDefinition { if ($SparkJobDefinition) { Write-Message -Message "Spark Job Definition found in the Workspace '$WorkspaceId'." -Level Debug return $SparkJobDefinition - } - else { + } else { Write-Message -Message "No Spark Job Definition found matching the provided criteria." -Level Warning return $null } - } - catch { + } catch { # Step 10: Capture and log error details $errorDetails = $_.Exception.Message Write-Message -Message "Failed to retrieve SparkJobDefinition. Error: $errorDetails" -Level Error - } - -} + } + +} \ No newline at end of file diff --git a/FabricTools/public/Spark Job Definition/Get-FabricSparkJobDefinitionDefinition.ps1 b/source/Public/Spark Job Definition/Get-FabricSparkJobDefinitionDefinition.ps1 similarity index 97% rename from FabricTools/public/Spark Job Definition/Get-FabricSparkJobDefinitionDefinition.ps1 rename to source/Public/Spark Job Definition/Get-FabricSparkJobDefinitionDefinition.ps1 index b1db8a91..11b2ea17 100644 --- a/FabricTools/public/Spark Job Definition/Get-FabricSparkJobDefinitionDefinition.ps1 +++ b/source/Public/Spark Job Definition/Get-FabricSparkJobDefinitionDefinition.ps1 @@ -53,11 +53,11 @@ function Get-FabricSparkJobDefinitionDefinition { # Step 3: Construct the API URL $apiEndpointUrl = "{0}/workspaces/{1}/sparkJobDefinitions/{2}/getDefinition" -f $FabricConfig.BaseUrl, $WorkspaceId, $SparkJobDefinitionId - + if ($SparkJobDefinitionFormat) { $apiEndpointUrl = "{0}?format={1}" -f $apiEndpointUrl, $SparkJobDefinitionFormat } - + Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug # Step 4: Make the API request @@ -81,30 +81,29 @@ function Get-FabricSparkJobDefinitionDefinition { [string]$operationId = $responseHeader["x-ms-operation-id"] [string]$location = $responseHeader["Location"] - [string]$retryAfter = $responseHeader["Retry-After"] + [string]$retryAfter = $responseHeader["Retry-After"] Write-Message -Message "Operation ID: '$operationId'" -Level Debug Write-Message -Message "Location: '$location'" -Level Debug Write-Message -Message "Retry-After: '$retryAfter'" -Level Debug Write-Message -Message "Getting Long Running Operation status" -Level Debug - + $operationStatus = Get-FabricLongRunningOperation -operationId $operationId -location $location Write-Message -Message "Long Running Operation status: $operationStatus" -Level Debug # Handle operation result if ($operationStatus.status -eq "Succeeded") { Write-Message -Message "Operation Succeeded" -Level Debug Write-Message -Message "Getting Long Running Operation result" -Level Debug - + $operationResult = Get-FabricLongRunningOperationResult -operationId $operationId, -location $location Write-Message -Message "Long Running Operation status: $operationResult" -Level Debug - + return $operationResult.definition.parts - } - else { + } else { Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Debug Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Error return $operationStatus - } + } } default { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error @@ -114,11 +113,10 @@ function Get-FabricSparkJobDefinitionDefinition { throw "API request failed with status code $statusCode." } } - } - catch { + } catch { # Step 9: Capture and log error details $errorDetails = $_.Exception.Message Write-Message -Message "Failed to retrieve Spark Job Definition. Error: $errorDetails" -Level Error - } - -} + } + +} \ No newline at end of file diff --git a/FabricTools/public/Spark Job Definition/New-FabricSparkJobDefinition.ps1 b/source/Public/Spark Job Definition/New-FabricSparkJobDefinition.ps1 similarity index 82% rename from FabricTools/public/Spark Job Definition/New-FabricSparkJobDefinition.ps1 rename to source/Public/Spark Job Definition/New-FabricSparkJobDefinition.ps1 index 99d992ef..deb64c00 100644 --- a/FabricTools/public/Spark Job Definition/New-FabricSparkJobDefinition.ps1 +++ b/source/Public/Spark Job Definition/New-FabricSparkJobDefinition.ps1 @@ -3,7 +3,7 @@ Creates a new SparkJobDefinition in a specified Microsoft Fabric workspace. .DESCRIPTION - This function sends a POST request to the Microsoft Fabric API to create a new SparkJobDefinition + This function sends a POST request to the Microsoft Fabric API to create a new SparkJobDefinition in the specified workspace. It supports optional parameters for SparkJobDefinition description and path definitions. .PARAMETER WorkspaceId @@ -31,8 +31,9 @@ Author: Tiago Balabuch #> -function New-FabricSparkJobDefinition { - [CmdletBinding()] +function New-FabricSparkJobDefinition +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -50,12 +51,13 @@ function New-FabricSparkJobDefinition { [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$SparkJobDefinitionPathDefinition, - + [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$SparkJobDefinitionPathPlatformDefinition ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -70,15 +72,19 @@ function New-FabricSparkJobDefinition { displayName = $SparkJobDefinitionName } - if ($SparkJobDefinitionDescription) { + if ($SparkJobDefinitionDescription) + { $body.description = $SparkJobDefinitionDescription } - if ($SparkJobDefinitionPathDefinition) { + if ($SparkJobDefinitionPathDefinition) + { $SparkJobDefinitionEncodedContent = Convert-ToBase64 -filePath $SparkJobDefinitionPathDefinition - if (-not [string]::IsNullOrEmpty($SparkJobDefinitionEncodedContent)) { + if (-not [string]::IsNullOrEmpty($SparkJobDefinitionEncodedContent)) + { # Initialize definition if it doesn't exist - if (-not $body.definition) { + if (-not $body.definition) + { $body.definition = @{ format = "SparkJobDefinitionV1" parts = @() @@ -92,18 +98,22 @@ function New-FabricSparkJobDefinition { payloadType = "InlineBase64" } } - else { + else + { Write-Message -Message "Invalid or empty content in SparkJobDefinition definition." -Level Error return $null } } - if ($SparkJobDefinitionPathPlatformDefinition) { + if ($SparkJobDefinitionPathPlatformDefinition) + { $SparkJobDefinitionEncodedPlatformContent = Convert-ToBase64 -filePath $SparkJobDefinitionPathPlatformDefinition - if (-not [string]::IsNullOrEmpty($SparkJobDefinitionEncodedPlatformContent)) { + if (-not [string]::IsNullOrEmpty($SparkJobDefinitionEncodedPlatformContent)) + { # Initialize definition if it doesn't exist - if (-not $body.definition) { + if (-not $body.definition) + { $body.definition = @{ format = "SparkJobDefinitionV1" parts = @() @@ -117,7 +127,8 @@ function New-FabricSparkJobDefinition { payloadType = "InlineBase64" } } - else { + else + { Write-Message -Message "Invalid or empty content in platform definition." -Level Error return $null } @@ -127,57 +138,65 @@ function New-FabricSparkJobDefinition { $bodyJson = $body | ConvertTo-Json -Depth 10 Write-Message -Message "Request Body: $bodyJson" -Level Debug - # Step 4: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Post ` - -Body $bodyJson ` - -ContentType "application/json" ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -ResponseHeadersVariable "responseHeader" ` - -StatusCodeVariable "statusCode" - - Write-Message -Message "Response Code: $statusCode" -Level Debug - + if ($PSCmdlet.ShouldProcess($apiEndpointUrl, "Create Spark Job Definition")) + { + # Step 4: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Post ` + -Body $bodyJson ` + -ContentType "application/json" ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -ResponseHeadersVariable "responseHeader" ` + -StatusCodeVariable "statusCode" + + Write-Message -Message "Response Code: $statusCode" -Level Debug + } # Step 5: Handle and log the response - switch ($statusCode) { - 201 { + switch ($statusCode) + { + 201 + { Write-Message -Message "Spark Job Definition '$SparkJobDefinitionName' created successfully!" -Level Info return $response } - 202 { + 202 + { Write-Message -Message "Spark Job Definition '$SparkJobDefinitionName' creation accepted. Provisioning in progress!" -Level Info - + [string]$operationId = $responseHeader["x-ms-operation-id"] [string]$location = $responseHeader["Location"] - [string]$retryAfter = $responseHeader["Retry-After"] + [string]$retryAfter = $responseHeader["Retry-After"] Write-Message -Message "Operation ID: '$operationId'" -Level Debug Write-Message -Message "Location: '$location'" -Level Debug Write-Message -Message "Retry-After: '$retryAfter'" -Level Debug Write-Message -Message "Getting Long Running Operation status" -Level Debug - + $operationStatus = Get-FabricLongRunningOperation -operationId $operationId -location $location Write-Message -Message "Long Running Operation status: $operationStatus" -Level Debug # Handle operation result - if ($operationStatus.status -eq "Succeeded") { + if ($operationStatus.status -eq "Succeeded") + { Write-Message -Message "Operation Succeeded" -Level Debug Write-Message -Message "Getting Long Running Operation result" -Level Debug - + $operationResult = Get-FabricLongRunningOperationResult -operationId $operationId Write-Message -Message "Long Running Operation status: $operationResult" -Level Debug - + return $operationResult - } - else { + } + else + { Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Debug Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Error return $operationStatus - } + } } - default { + default + { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error Write-Message -Message "Error: $($response.message)" -Level Error Write-Message -Message "Error Details: $($response.moreDetails)" -Level Error @@ -186,7 +205,8 @@ function New-FabricSparkJobDefinition { } } } - catch { + catch + { # Step 6: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to create Spark Job Definition. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Spark Job Definition/Remove-FabricSparkJobDefinition.ps1 b/source/Public/Spark Job Definition/Remove-FabricSparkJobDefinition.ps1 similarity index 75% rename from FabricTools/public/Spark Job Definition/Remove-FabricSparkJobDefinition.ps1 rename to source/Public/Spark Job Definition/Remove-FabricSparkJobDefinition.ps1 index 68f8e184..ea9146db 100644 --- a/FabricTools/public/Spark Job Definition/Remove-FabricSparkJobDefinition.ps1 +++ b/source/Public/Spark Job Definition/Remove-FabricSparkJobDefinition.ps1 @@ -3,7 +3,7 @@ Removes an SparkJobDefinition from a specified Microsoft Fabric workspace. .DESCRIPTION - This function sends a DELETE request to the Microsoft Fabric API to remove an SparkJobDefinition + This function sends a DELETE request to the Microsoft Fabric API to remove an SparkJobDefinition from the specified workspace using the provided WorkspaceId and SparkJobDefinitionId. .PARAMETER WorkspaceId @@ -22,8 +22,9 @@ Author: Tiago Balabuch #> -function Remove-FabricSparkJobDefinition { - [CmdletBinding()] +function Remove-FabricSparkJobDefinition +{ + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -33,7 +34,8 @@ function Remove-FabricSparkJobDefinition { [ValidateNotNullOrEmpty()] [string]$SparkJobDefinitionId ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -42,19 +44,21 @@ function Remove-FabricSparkJobDefinition { # Step 2: Construct the API URL $apiEndpointUrl = "{0}/workspaces/{1}/sparkJobDefinitions/{2}" -f $FabricConfig.BaseUrl, $WorkspaceId, $SparkJobDefinitionId Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug - - # Step 3: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Delete ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -ResponseHeadersVariable "responseHeader" ` - -StatusCodeVariable "statusCode" - - # Step 4: Handle response - if ($statusCode -ne 200) { + if ($PSCmdlet.ShouldProcess($apiEndpointUrl, "Remove SparkJobDefinition")) + { + # Step 3: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Delete ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -ResponseHeadersVariable "responseHeader" ` + -StatusCodeVariable "statusCode" + } + # Step 4: Handle response + if ($statusCode -ne 200) + { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error Write-Message -Message "Error: $($response.message)" -Level Error Write-Message -Message "Error Details: $($response.moreDetails)" -Level Error @@ -62,9 +66,10 @@ function Remove-FabricSparkJobDefinition { return $null } - Write-Message -Message "Spark Job Definition '$SparkJobDefinitionId' deleted successfully from workspace '$WorkspaceId'." -Level Info + Write-Message -Message "Spark Job Definition '$SparkJobDefinitionId' deleted successfully from workspace '$WorkspaceId'." -Level Info } - catch { + catch + { # Step 5: Log and handle errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to delete SparkJobDefinition '$SparkJobDefinitionId'. Error: $errorDetails" -Level Error diff --git a/source/Public/Spark Job Definition/Start-FabricSparkJobDefinitionOnDemand.ps1 b/source/Public/Spark Job Definition/Start-FabricSparkJobDefinitionOnDemand.ps1 new file mode 100644 index 00000000..5cdd5f61 --- /dev/null +++ b/source/Public/Spark Job Definition/Start-FabricSparkJobDefinitionOnDemand.ps1 @@ -0,0 +1,127 @@ +<# +.SYNOPSIS + Starts a Fabric Spark Job Definition on demand. + +.DESCRIPTION + This function initiates a Spark Job Definition on demand within a specified workspace. + It constructs the appropriate API endpoint URL and makes a POST request to start the job. + The function can optionally wait for the job to complete based on the 'waitForCompletion' parameter. + +.PARAMETER WorkspaceId + The ID of the workspace where the Spark Job Definition is located. This parameter is mandatory. + +.PARAMETER SparkJobDefinitionId + The ID of the Spark Job Definition to be started. This parameter is mandatory. + +.PARAMETER JobType + The type of job to be started. The default value is 'sparkjob'. This parameter is optional. + +.PARAMETER waitForCompletion + A boolean flag indicating whether to wait for the job to complete. The default value is $false. This parameter is optional. + +.EXAMPLE + Start-FabricSparkJobDefinitionOnDemand -WorkspaceId "workspace123" -SparkJobDefinitionId "jobdef456" -waitForCompletion $true + +.NOTES + Ensure that the necessary authentication tokens are valid before running this function. + The function logs detailed messages for debugging and informational purposes. +#> +function Start-FabricSparkJobDefinitionOnDemand +{ + [CmdletBinding(SupportsShouldProcess)] + param ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [string]$WorkspaceId, + + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [string]$SparkJobDefinitionId, + + [Parameter(Mandatory = $false)] + [ValidateNotNullOrEmpty()] + [ValidateSet('sparkjob')] + [string]$JobType = "sparkjob", + + [Parameter(Mandatory = $false)] + [ValidateNotNullOrEmpty()] + [bool]$waitForCompletion = $false + ) + + try + { + # Step 1: Ensure token validity + Write-Message -Message "Validating token..." -Level Debug + Test-TokenExpired + Write-Message -Message "Token validation completed." -Level Debug + + # Step 2: Construct the API URL + $apiEndpointUrl = "{0}/workspaces/{1}/SparkJobDefinitions/{2}/jobs/instances?jobType={3}" -f $FabricConfig.BaseUrl, $WorkspaceId , $SparkJobDefinitionId, $JobType + Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug + + if ($PSCmdlet.ShouldProcess($apiEndpointUrl, "Start Spark Job Definition on demand")){ + # Step 4: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Post ` + -ContentType "application/json" ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -ResponseHeadersVariable "responseHeader" ` + -StatusCodeVariable "statusCode" + } + Write-Message -Message "Response Code: $statusCode" -Level Debug + # Step 5: Handle and log the response + switch ($statusCode) + { + 201 + { + Write-Message -Message "Spark Job Definition on demand successfully initiated for SparkJobDefinition '$SparkJobDefinition.displayName'." -Level Info + return $response + } + 202 + { + Write-Message -Message "Spark Job Definition on demand accepted and is now running in the background. Job execution is in progress." -Level Info + [string]$operationId = $responseHeader["x-ms-operation-id"] + [string]$location = $responseHeader["Location"] + [string]$retryAfter = $responseHeader["Retry-After"] + + Write-Message -Message "Operation ID: '$operationId'" -Level Debug + Write-Message -Message "Location: '$location'" -Level Debug + Write-Message -Message "Retry-After: '$retryAfter'" -Level Debug + + if ($waitForCompletion -eq $true) + { + Write-Message -Message "Getting Long Running Operation status" -Level Debug + $operationStatus = Get-FabricLongRunningOperation -operationId $operationId -location $location -retryAfter $retryAfter + Write-Message -Message "Long Running Operation status: $operationStatus" -Level Debug + return $operationStatus + } + else + { + Write-Message -Message "The operation is running asynchronously." -Level Info + Write-Message -Message "Use the returned details to check the operation status." -Level Info + Write-Message -Message "To wait for the operation to complete, set the 'waitForCompletion' parameter to true." -Level Info + $operationDetails = [PSCustomObject]@{ + OperationId = $operationId + Location = $location + RetryAfter = $retryAfter + } + return $operationDetails + } + } + default + { + Write-Message -Message "Unexpected response code: $statusCode" -Level Error + Write-Message -Message "Error details: $($response.message)" -Level Error + throw "API request failed with status code $statusCode." + } + } + } + catch { + # Step 6: Handle and log errors + $errorDetails = $_.Exception.Message + Write-Message -Message "Failed to start Spark Job Definition on demand. Error: $errorDetails" -Level Error + } + } diff --git a/FabricTools/public/Spark Job Definition/Update-FabricSparkJobDefinition.ps1 b/source/Public/Spark Job Definition/Update-FabricSparkJobDefinition.ps1 similarity index 79% rename from FabricTools/public/Spark Job Definition/Update-FabricSparkJobDefinition.ps1 rename to source/Public/Spark Job Definition/Update-FabricSparkJobDefinition.ps1 index 9b2c641a..f841d006 100644 --- a/FabricTools/public/Spark Job Definition/Update-FabricSparkJobDefinition.ps1 +++ b/source/Public/Spark Job Definition/Update-FabricSparkJobDefinition.ps1 @@ -3,7 +3,7 @@ Updates an existing SparkJobDefinition in a specified Microsoft Fabric workspace. .DESCRIPTION - This function sends a PATCH request to the Microsoft Fabric API to update an existing SparkJobDefinition + This function sends a PATCH request to the Microsoft Fabric API to update an existing SparkJobDefinition in the specified workspace. It supports optional parameters for SparkJobDefinition description. .PARAMETER WorkspaceId @@ -28,13 +28,14 @@ Author: Tiago Balabuch #> -function Update-FabricSparkJobDefinition { - [CmdletBinding()] +function Update-FabricSparkJobDefinition +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [string]$WorkspaceId, - + [string]$WorkspaceId, + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$SparkJobDefinitionId, @@ -48,7 +49,8 @@ function Update-FabricSparkJobDefinition { [ValidateNotNullOrEmpty()] [string]$SparkJobDefinitionDescription ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -63,7 +65,8 @@ function Update-FabricSparkJobDefinition { displayName = $SparkJobDefinitionName } - if ($SparkJobDefinitionDescription) { + if ($SparkJobDefinitionDescription) + { $body.description = $SparkJobDefinitionDescription } @@ -71,20 +74,24 @@ function Update-FabricSparkJobDefinition { $bodyJson = $body | ConvertTo-Json Write-Message -Message "Request Body: $bodyJson" -Level Debug - # Step 4: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Patch ` - -Body $bodyJson ` - -ContentType "application/json" ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -ResponseHeadersVariable "responseHeader" ` - -StatusCodeVariable "statusCode" - + if ($PSCmdlet.ShouldProcess($apiEndpointUrl, "Update SparkJobDefinition")) + { + + # Step 4: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Patch ` + -Body $bodyJson ` + -ContentType "application/json" ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -ResponseHeadersVariable "responseHeader" ` + -StatusCodeVariable "statusCode" + } # Step 5: Validate the response code - if ($statusCode -ne 200) { + if ($statusCode -ne 200) + { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error Write-Message -Message "Error: $($response.message)" -Level Error Write-Message -Message "Error Details: $($response.moreDetails)" -Level Error @@ -96,7 +103,8 @@ function Update-FabricSparkJobDefinition { Write-Message -Message "Spark Job Definition '$SparkJobDefinitionName' updated successfully!" -Level Info return $response } - catch { + catch + { # Step 7: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to update SparkJobDefinition. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Spark Job Definition/Update-FabricSparkJobDefinitionDefinition.ps1 b/source/Public/Spark Job Definition/Update-FabricSparkJobDefinitionDefinition.ps1 similarity index 82% rename from FabricTools/public/Spark Job Definition/Update-FabricSparkJobDefinitionDefinition.ps1 rename to source/Public/Spark Job Definition/Update-FabricSparkJobDefinitionDefinition.ps1 index cca084d7..1c680b99 100644 --- a/FabricTools/public/Spark Job Definition/Update-FabricSparkJobDefinitionDefinition.ps1 +++ b/source/Public/Spark Job Definition/Update-FabricSparkJobDefinitionDefinition.ps1 @@ -3,7 +3,7 @@ Updates the definition of an existing SparkJobDefinition in a specified Microsoft Fabric workspace. .DESCRIPTION - This function sends a PATCH request to the Microsoft Fabric API to update the definition of an existing SparkJobDefinition + This function sends a PATCH request to the Microsoft Fabric API to update the definition of an existing SparkJobDefinition in the specified workspace. It supports optional parameters for SparkJobDefinition definition and platform-specific definition. .PARAMETER WorkspaceId @@ -28,8 +28,9 @@ Author: Tiago Balabuch #> -function Update-FabricSparkJobDefinitionDefinition { - [CmdletBinding()] +function Update-FabricSparkJobDefinitionDefinition +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -42,12 +43,13 @@ function Update-FabricSparkJobDefinitionDefinition { [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$SparkJobDefinitionPathDefinition, - + [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$SparkJobDefinitionPathPlatformDefinition ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -57,8 +59,9 @@ function Update-FabricSparkJobDefinitionDefinition { $apiEndpointUrl = "{0}/workspaces/{1}/SparkJobDefinitions/{2}/updateDefinition" -f $FabricConfig.BaseUrl, $WorkspaceId, $SparkJobDefinitionId #if ($UpdateMetadata -eq $true) { - if($SparkJobDefinitionPathPlatformDefinition){ - $apiEndpointUrl = "?updateMetadata=true" -f $apiEndpointUrl + if ($SparkJobDefinitionPathPlatformDefinition) + { + $apiEndpointUrl = "?updateMetadata=true" -f $apiEndpointUrl } Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug @@ -67,13 +70,15 @@ function Update-FabricSparkJobDefinitionDefinition { definition = @{ format = "SparkJobDefinitionV1" parts = @() - } + } } - - if ($SparkJobDefinitionPathDefinition) { + + if ($SparkJobDefinitionPathDefinition) + { $SparkJobDefinitionEncodedContent = Convert-ToBase64 -filePath $SparkJobDefinitionPathDefinition - - if (-not [string]::IsNullOrEmpty($SparkJobDefinitionEncodedContent)) { + + if (-not [string]::IsNullOrEmpty($SparkJobDefinitionEncodedContent)) + { # Add new part to the parts array $body.definition.parts += @{ path = "SparkJobDefinitionV1.json" @@ -81,15 +86,18 @@ function Update-FabricSparkJobDefinitionDefinition { payloadType = "InlineBase64" } } - else { + else + { Write-Message -Message "Invalid or empty content in SparkJobDefinition definition." -Level Error return $null } } - if ($SparkJobDefinitionPathPlatformDefinition) { + if ($SparkJobDefinitionPathPlatformDefinition) + { $SparkJobDefinitionEncodedPlatformContent = Convert-ToBase64 -filePath $SparkJobDefinitionPathPlatformDefinition - if (-not [string]::IsNullOrEmpty($SparkJobDefinitionEncodedPlatformContent)) { + if (-not [string]::IsNullOrEmpty($SparkJobDefinitionEncodedPlatformContent)) + { # Add new part to the parts array $body.definition.parts += @{ path = ".platform" @@ -97,7 +105,8 @@ function Update-FabricSparkJobDefinitionDefinition { payloadType = "InlineBase64" } } - else { + else + { Write-Message -Message "Invalid or empty content in platform definition." -Level Error return $null } @@ -106,61 +115,72 @@ function Update-FabricSparkJobDefinitionDefinition { $bodyJson = $body | ConvertTo-Json -Depth 10 Write-Message -Message "Request Body: $bodyJson" -Level Debug - # Step 4: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Post ` - -Body $bodyJson ` - -ContentType "application/json" ` - -ErrorAction Stop ` - -ResponseHeadersVariable "responseHeader" ` - -StatusCodeVariable "statusCode" - + if ($PSCmdlet.ShouldProcess($apiEndpointUrl, "Update Spark Job Definition")) + { + + # Step 4: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Post ` + -Body $bodyJson ` + -ContentType "application/json" ` + -ErrorAction Stop ` + -ResponseHeadersVariable "responseHeader" ` + -StatusCodeVariable "statusCode" + } + # Step 5: Handle and log the response - switch ($statusCode) { - 200 { + switch ($statusCode) + { + 200 + { Write-Message -Message "Update definition for Spark Job Definition '$SparkJobDefinitionId' created successfully!" -Level Info return $response } - 202 { + 202 + { Write-Message -Message "Update definition for Spark Job Definition '$SparkJobDefinitionId' accepted. Operation in progress!" -Level Info - + [string]$operationId = $responseHeader["x-ms-operation-id"] [string]$location = $responseHeader["Location"] - [string]$retryAfter = $responseHeader["Retry-After"] + [string]$retryAfter = $responseHeader["Retry-After"] Write-Message -Message "Operation ID: '$operationId'" -Level Debug Write-Message -Message "Location: '$location'" -Level Debug Write-Message -Message "Retry-After: '$retryAfter'" -Level Debug Write-Message -Message "Getting Long Running Operation status" -Level Debug - + $operationStatus = Get-FabricLongRunningOperation -operationId $operationId -location $location Write-Message -Message "Long Running Operation status: $operationStatus" -Level Debug # Handle operation result - if ($operationStatus.status -eq "Succeeded") { + if ($operationStatus.status -eq "Succeeded") + { Write-Message -Message "Operation Succeeded" -Level Debug Write-Message -Message "Getting Long Running Operation result" -Level Debug - + $operationResult = Get-FabricLongRunningOperationResult -operationId $operationId Write-Message -Message "Long Running Operation status: $operationResult" -Level Debug - + return $operationResult - } - else { + } + else + { Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Debug Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Error return $operationStatus - } - } - default { + } + } + default + { Write-Message -Message "Unexpected response code: $statusCode" -Level Error Write-Message -Message "Error details: $($response.message)" -Level Error throw "API request failed with status code $statusCode." } } } - catch { + catch + { # Step 6: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to update Spark Job Definition. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Spark/Get-FabricSparkCustomPool.ps1 b/source/Public/Spark/Get-FabricSparkCustomPool.ps1 similarity index 95% rename from FabricTools/public/Spark/Get-FabricSparkCustomPool.ps1 rename to source/Public/Spark/Get-FabricSparkCustomPool.ps1 index 6bf0441e..b2e1d221 100644 --- a/FabricTools/public/Spark/Get-FabricSparkCustomPool.ps1 +++ b/source/Public/Spark/Get-FabricSparkCustomPool.ps1 @@ -66,27 +66,27 @@ function Get-FabricSparkCustomPool { # Step 3: Initialize variables $continuationToken = $null $SparkCustomPools = @() - + if (-not ([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.GetName().Name -eq "System.Web" })) { Add-Type -AssemblyName System.Web } - + # Step 4: Loop to retrieve all capacities with continuation token Write-Message -Message "Loop started to get continuation token" -Level Debug $baseApiEndpointUrl = "{0}/workspaces/{1}/spark/pools" -f $FabricConfig.BaseUrl, $WorkspaceId - + do { # Step 5: Construct the API URL $apiEndpointUrl = $baseApiEndpointUrl - + if ($null -ne $continuationToken) { # URL-encode the continuation token $encodedToken = [System.Web.HttpUtility]::UrlEncode($continuationToken) $apiEndpointUrl = "{0}?continuationToken={1}" -f $apiEndpointUrl, $encodedToken } Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug - + # Step 6: Make the API request $response = Invoke-RestMethod ` -Headers $FabricConfig.FabricHeaders ` @@ -96,7 +96,7 @@ function Get-FabricSparkCustomPool { -SkipHttpErrorCheck ` -ResponseHeadersVariable "responseHeader" ` -StatusCodeVariable "statusCode" - + # Step 7: Validate the response code if ($statusCode -ne 200) { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error @@ -105,38 +105,34 @@ function Get-FabricSparkCustomPool { Write-Message "Error Code: $($response.errorCode)" -Level Error return $null } - + # Step 8: Add data to the list if ($null -ne $response) { Write-Message -Message "Adding data to the list" -Level Debug $SparkCustomPools += $response.value - + # Update the continuation token if present if ($response.PSObject.Properties.Match("continuationToken")) { Write-Message -Message "Updating the continuation token" -Level Debug $continuationToken = $response.continuationToken Write-Message -Message "Continuation token: $continuationToken" -Level Debug - } - else { + } else { Write-Message -Message "Updating the continuation token to null" -Level Debug $continuationToken = $null } - } - else { + } else { Write-Message -Message "No data received from the API." -Level Warning break } } while ($null -ne $continuationToken) Write-Message -Message "Loop finished and all data added to the list" -Level Debug - + # Step 8: Filter results based on provided parameters $SparkCustomPool = if ($SparkCustomPoolId) { $SparkCustomPools | Where-Object { $_.id -eq $SparkCustomPoolId } - } - elseif ($SparkCustomPoolName) { + } elseif ($SparkCustomPoolName) { $SparkCustomPools | Where-Object { $_.name -eq $SparkCustomPoolName } - } - else { + } else { # Return all SparkCustomPools if no filter is provided Write-Message -Message "No filter provided. Returning all SparkCustomPools." -Level Debug $SparkCustomPools @@ -146,16 +142,14 @@ function Get-FabricSparkCustomPool { if ($SparkCustomPool) { Write-Message -Message "SparkCustomPool found matching the specified criteria." -Level Debug return $SparkCustomPool - } - else { + } else { Write-Message -Message "No SparkCustomPool found matching the provided criteria." -Level Warning return $null } - } - catch { + } catch { # Step 10: Capture and log error details $errorDetails = $_.Exception.Message Write-Message -Message "Failed to retrieve SparkCustomPool. Error: $errorDetails" -Level Error - } - -} + } + +} \ No newline at end of file diff --git a/FabricTools/public/Spark/Get-FabricSparkSettings.ps1 b/source/Public/Spark/Get-FabricSparkSettings.ps1 similarity index 94% rename from FabricTools/public/Spark/Get-FabricSparkSettings.ps1 rename to source/Public/Spark/Get-FabricSparkSettings.ps1 index 11c5c51f..e111847c 100644 --- a/FabricTools/public/Spark/Get-FabricSparkSettings.ps1 +++ b/source/Public/Spark/Get-FabricSparkSettings.ps1 @@ -18,10 +18,11 @@ - Calls `Test-TokenExpired` to ensure token validity before making the API request. Author: Tiago Balabuch - + #> function Get-FabricSparkSettings { [CmdletBinding()] + [OutputType([System.Object[]])] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -37,26 +38,26 @@ function Get-FabricSparkSettings { # Step 3: Initialize variables $continuationToken = $null $SparkSettings = @() - + if (-not ([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.GetName().Name -eq "System.Web" })) { Add-Type -AssemblyName System.Web } - + # Step 4: Loop to retrieve all capacities with continuation token Write-Message -Message "Loop started to get continuation token" -Level Debug $baseApiEndpointUrl = "{0}/workspaces/{1}/spark/settings" -f $FabricConfig.BaseUrl, $WorkspaceId - + do { # Step 5: Construct the API URL $apiEndpointUrl = $baseApiEndpointUrl - + if ($null -ne $continuationToken) { # URL-encode the continuation token $encodedToken = [System.Web.HttpUtility]::UrlEncode($continuationToken) $apiEndpointUrl = "{0}?continuationToken={1}" -f $apiEndpointUrl, $encodedToken } Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug - + # Step 6: Make the API request $response = Invoke-RestMethod ` -Headers $FabricConfig.FabricHeaders ` @@ -66,7 +67,7 @@ function Get-FabricSparkSettings { -SkipHttpErrorCheck ` -ResponseHeadersVariable "responseHeader" ` -StatusCodeVariable "statusCode" - + # Step 7: Validate the response code if ($statusCode -ne 200) { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error @@ -75,45 +76,41 @@ function Get-FabricSparkSettings { Write-Message "Error Code: $($response.errorCode)" -Level Error return $null } - + # Step 8: Add data to the list if ($null -ne $response) { Write-Message -Message "Adding data to the list" -Level Debug $SparkSettings += $response - + # Update the continuation token if present if ($response.PSObject.Properties.Match("continuationToken")) { Write-Message -Message "Updating the continuation token" -Level Debug $continuationToken = $response.continuationToken Write-Message -Message "Continuation token: $continuationToken" -Level Debug - } - else { + } else { Write-Message -Message "Updating the continuation token to null" -Level Debug $continuationToken = $null } - } - else { + } else { Write-Message -Message "No data received from the API." -Level Warning break } } while ($null -ne $continuationToken) Write-Message -Message "Loop finished and all data added to the list" -Level Debug - + # Step 9: Handle results if ($SparkSettings) { Write-Message -Message " Returning all Spark Settings." -Level Debug - # Return all Spark Settings + # Return all Spark Settings return $SparkSettings - } - else { + } else { Write-Message -Message "No SparkSettings found matching the provided criteria." -Level Warning return $null } - } - catch { + } catch { # Step 10: Capture and log error details $errorDetails = $_.Exception.Message Write-Message -Message "Failed to retrieve SparkSettings. Error: $errorDetails" -Level Error - } - + } + } diff --git a/FabricTools/public/Spark/New-FabricSparkCustomPool.ps1 b/source/Public/Spark/New-FabricSparkCustomPool.ps1 similarity index 88% rename from FabricTools/public/Spark/New-FabricSparkCustomPool.ps1 rename to source/Public/Spark/New-FabricSparkCustomPool.ps1 index 848677ce..750c977f 100644 --- a/FabricTools/public/Spark/New-FabricSparkCustomPool.ps1 +++ b/source/Public/Spark/New-FabricSparkCustomPool.ps1 @@ -3,7 +3,7 @@ Creates a new Spark custom pool in a specified Microsoft Fabric workspace. .DESCRIPTION - This function sends a POST request to the Microsoft Fabric API to create a new Spark custom pool + This function sends a POST request to the Microsoft Fabric API to create a new Spark custom pool in the specified workspace. It supports various parameters for Spark custom pool configuration. .PARAMETER WorkspaceId @@ -45,11 +45,12 @@ - Calls `Test-TokenExpired` to ensure token validity before making the API request. Author: Tiago Balabuch - + #> -function New-FabricSparkCustomPool { - [CmdletBinding()] +function New-FabricSparkCustomPool +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -64,7 +65,7 @@ function New-FabricSparkCustomPool { [ValidateNotNullOrEmpty()] [ValidateSet('MemoryOptimized')] [string]$NodeFamily, - + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [ValidateSet('Large', 'Medium', 'Small', 'XLarge', 'XXLarge')] @@ -95,7 +96,8 @@ function New-FabricSparkCustomPool { [int]$DynamicExecutorAllocationMaxExecutors ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -125,55 +127,64 @@ function New-FabricSparkCustomPool { $bodyJson = $body | ConvertTo-Json -Depth 10 Write-Message -Message "Request Body: $bodyJson" -Level Debug - # Step 4: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Post ` - -Body $bodyJson ` - -ContentType "application/json" ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -ResponseHeadersVariable "responseHeader" ` - -StatusCodeVariable "statusCode" + if ($PSCmdlet.ShouldProcess($apiEndpointUrl, "Create Spark Custom Pool")) + { + # Step 4: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Post ` + -Body $bodyJson ` + -ContentType "application/json" ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -ResponseHeadersVariable "responseHeader" ` + -StatusCodeVariable "statusCode" + } # Step 5: Handle and log the response - switch ($statusCode) { - 201 { + switch ($statusCode) + { + 201 + { Write-Message -Message "SparkCustomPool '$SparkCustomPoolName' created successfully!" -Level Info return $response } - 202 { + 202 + { Write-Message -Message "SparkCustomPool '$SparkCustomPoolName' creation accepted. Provisioning in progress!" -Level Info - + [string]$operationId = $responseHeader["x-ms-operation-id"] [string]$location = $responseHeader["Location"] - [string]$retryAfter = $responseHeader["Retry-After"] + [string]$retryAfter = $responseHeader["Retry-After"] Write-Message -Message "Operation ID: '$operationId'" -Level Debug Write-Message -Message "Location: '$location'" -Level Debug Write-Message -Message "Retry-After: '$retryAfter'" -Level Debug Write-Message -Message "Getting Long Running Operation status" -Level Debug - + $operationStatus = Get-FabricLongRunningOperation -operationId $operationId Write-Message -Message "Long Running Operation status: $operationStatus" -Level Debug # Handle operation result - if ($operationStatus.status -eq "Succeeded") { + if ($operationStatus.status -eq "Succeeded") + { Write-Message -Message "Operation Succeeded" -Level Debug Write-Message -Message "Getting Long Running Operation result" -Level Debug - + $operationResult = Get-FabricLongRunningOperationResult -operationId $operationId, -location $location Write-Message -Message "Long Running Operation status: $operationResult" -Level Debug - + return $operationResult } - else { + else + { Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Debug Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Error return $operationStatus - } + } } - default { + default + { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error Write-Message -Message "Error: $($response.message)" -Level Error Write-Message -Message "Error Details: $($response.moreDetails)" -Level Error @@ -182,7 +193,8 @@ function New-FabricSparkCustomPool { } } } - catch { + catch + { # Step 6: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to create SparkCustomPool. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Spark/Remove-FabricSparkCustomPool.ps1 b/source/Public/Spark/Remove-FabricSparkCustomPool.ps1 similarity index 78% rename from FabricTools/public/Spark/Remove-FabricSparkCustomPool.ps1 rename to source/Public/Spark/Remove-FabricSparkCustomPool.ps1 index 2b748e65..cf44ffc1 100644 --- a/FabricTools/public/Spark/Remove-FabricSparkCustomPool.ps1 +++ b/source/Public/Spark/Remove-FabricSparkCustomPool.ps1 @@ -3,7 +3,7 @@ Removes a Spark custom pool from a specified Microsoft Fabric workspace. .DESCRIPTION - This function sends a DELETE request to the Microsoft Fabric API to remove a Spark custom pool + This function sends a DELETE request to the Microsoft Fabric API to remove a Spark custom pool from the specified workspace using the provided WorkspaceId and SparkCustomPoolId. .PARAMETER WorkspaceId @@ -21,10 +21,11 @@ - Calls `Test-TokenExpired` to ensure token validity before making the API request. Author: Tiago Balabuch - + #> -function Remove-FabricSparkCustomPool { - [CmdletBinding()] +function Remove-FabricSparkCustomPool +{ + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -34,7 +35,8 @@ function Remove-FabricSparkCustomPool { [ValidateNotNullOrEmpty()] [string]$SparkCustomPoolId ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -44,17 +46,21 @@ function Remove-FabricSparkCustomPool { $apiEndpointUrl = "{0}/workspaces/{1}/spark/pools/{2}" -f $FabricConfig.BaseUrl, $WorkspaceId, $SparkCustomPoolId Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug - # Step 3: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Delete ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -StatusCodeVariable "statusCode" + if ($PSCmdlet.ShouldProcess($apiEndpointUrl, "Remove Spark Custom Pool")) + { + # Step 3: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Delete ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -StatusCodeVariable "statusCode" + } # Step 4: Validate the response code - if ($statusCode -ne 200) { + if ($statusCode -ne 200) + { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error Write-Message -Message "Error: $($response.message)" -Level Error Write-Message -Message "Error Details: $($response.moreDetails)" -Level Error @@ -62,9 +68,10 @@ function Remove-FabricSparkCustomPool { return $null } Write-Message -Message "Spark Custom Pool '$SparkCustomPoolId' deleted successfully from workspace '$WorkspaceId'." -Level Info - + } - catch { + catch + { # Step 5: Log and handle errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to delete SparkCustomPool '$SparkCustomPoolId' from workspace '$WorkspaceId'. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Spark/Update-FabricSparkCustomPool.ps1 b/source/Public/Spark/Update-FabricSparkCustomPool.ps1 similarity index 83% rename from FabricTools/public/Spark/Update-FabricSparkCustomPool.ps1 rename to source/Public/Spark/Update-FabricSparkCustomPool.ps1 index 9e4bcfd5..f67013ad 100644 --- a/FabricTools/public/Spark/Update-FabricSparkCustomPool.ps1 +++ b/source/Public/Spark/Update-FabricSparkCustomPool.ps1 @@ -3,7 +3,7 @@ Updates an existing Spark custom pool in a specified Microsoft Fabric workspace. .DESCRIPTION - This function sends a PATCH request to the Microsoft Fabric API to update an existing Spark custom pool + This function sends a PATCH request to the Microsoft Fabric API to update an existing Spark custom pool in the specified workspace. It supports various parameters for Spark custom pool configuration. .PARAMETER WorkspaceId @@ -48,15 +48,16 @@ - Calls `Test-TokenExpired` to ensure token validity before making the API request. Author: Tiago Balabuch - + #> -function Update-FabricSparkCustomPool { - [CmdletBinding()] +function Update-FabricSparkCustomPool +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [string]$WorkspaceId, - + [string]$WorkspaceId, + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$SparkCustomPoolId, @@ -70,7 +71,7 @@ function Update-FabricSparkCustomPool { [ValidateNotNullOrEmpty()] [ValidateSet('MemoryOptimized')] [string]$NodeFamily, - + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [ValidateSet('Large', 'Medium', 'Small', 'XLarge', 'XXLarge')] @@ -101,7 +102,8 @@ function Update-FabricSparkCustomPool { [int]$DynamicExecutorAllocationMaxExecutors ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -113,14 +115,14 @@ function Update-FabricSparkCustomPool { # Step 3: Construct the request body $body = @{ - name = $InstancePoolName - nodeFamily = $NodeFamily - nodeSize = $NodeSize - autoScale = @{ - enabled = $AutoScaleEnabled - minNodeCount = $AutoScaleMinNodeCount - maxNodeCount = $AutoScaleMaxNodeCount - } + name = $InstancePoolName + nodeFamily = $NodeFamily + nodeSize = $NodeSize + autoScale = @{ + enabled = $AutoScaleEnabled + minNodeCount = $AutoScaleMinNodeCount + maxNodeCount = $AutoScaleMaxNodeCount + } dynamicExecutorAllocation = @{ enabled = $DynamicExecutorAllocationEnabled minExecutors = $DynamicExecutorAllocationMinExecutors @@ -132,19 +134,24 @@ function Update-FabricSparkCustomPool { $bodyJson = $body | ConvertTo-Json Write-Message -Message "Request Body: $bodyJson" -Level Debug - # Step 4: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Patch ` - -Body $bodyJson ` - -ContentType "application/json" ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -StatusCodeVariable "statusCode" + if ($PSCmdlet.ShouldProcess($apiEndpointUrl, "Update SparkCustomPool")) + { + + # Step 4: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Patch ` + -Body $bodyJson ` + -ContentType "application/json" ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -StatusCodeVariable "statusCode" + } # Step 5: Validate the response code - if ($statusCode -ne 200) { + if ($statusCode -ne 200) + { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error Write-Message -Message "Error: $($response.message)" -Level Error Write-Message -Message "Error Details: $($response.moreDetails)" -Level Error @@ -156,7 +163,8 @@ function Update-FabricSparkCustomPool { Write-Message -Message "Spark Custom Pool '$SparkCustomPoolName' updated successfully!" -Level Info return $response } - catch { + catch + { # Step 7: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to update SparkCustomPool. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Spark/Update-FabricSparkSettings.ps1 b/source/Public/Spark/Update-FabricSparkSettings.ps1 similarity index 71% rename from FabricTools/public/Spark/Update-FabricSparkSettings.ps1 rename to source/Public/Spark/Update-FabricSparkSettings.ps1 index 9320af81..749a2e2b 100644 --- a/FabricTools/public/Spark/Update-FabricSparkSettings.ps1 +++ b/source/Public/Spark/Update-FabricSparkSettings.ps1 @@ -3,7 +3,7 @@ Updates an existing Spark custom pool in a specified Microsoft Fabric workspace. .DESCRIPTION - This function sends a PATCH request to the Microsoft Fabric API to update an existing Spark custom pool + This function sends a PATCH request to the Microsoft Fabric API to update an existing Spark custom pool in the specified workspace. It supports various parameters for Spark custom pool configuration. .PARAMETER WorkspaceId @@ -39,6 +39,33 @@ .PARAMETER DynamicExecutorAllocationMaxExecutors The maximum number of executors for dynamic executor allocation in the Spark custom pool. This parameter is mandatory. +.PARAMETER automaticLogEnabled + Specifies whether automatic logging is enabled for the Spark custom pool. This parameter is optional. + +.PARAMETER notebookInteractiveRunEnabled + Specifies whether notebook interactive run is enabled for the Spark custom pool. This parameter is optional. + +.PARAMETER customizeComputeEnabled + Specifies whether compute customization is enabled for the Spark custom pool. This parameter is optional. + +.PARAMETER defaultPoolName + The name of the default pool for the Spark custom pool. This parameter is optional. + +.PARAMETER defaultPoolType + The type of the default pool for the Spark custom pool. This parameter is optional and must be either 'Workspace' or 'Capacity'. + +.PARAMETER starterPoolMaxNode + The maximum number of nodes for the starter pool in the Spark custom pool. This parameter is optional. + +.PARAMETER starterPoolMaxExecutors + The maximum number of executors for the starter pool in the Spark custom pool. This parameter is optional. + +.PARAMETER EnvironmentName + The name of the environment for the Spark custom pool. This parameter is optional. + +.PARAMETER EnvironmentRuntimeVersion + The runtime version of the environment for the Spark custom pool. This parameter is optional. + .EXAMPLE Update-FabricSparkSettings -WorkspaceId "workspace-12345" -SparkSettingsId "pool-67890" -InstancePoolName "Updated Spark Pool" -NodeFamily "MemoryOptimized" -NodeSize "Large" -AutoScaleEnabled $true -AutoScaleMinNodeCount 1 -AutoScaleMaxNodeCount 10 -DynamicExecutorAllocationEnabled $true -DynamicExecutorAllocationMinExecutors 1 -DynamicExecutorAllocationMaxExecutors 10 This example updates the Spark custom pool with ID "pool-67890" in the workspace with ID "workspace-12345" with a new name and configuration. @@ -48,15 +75,16 @@ - Calls `Test-TokenExpired` to ensure token validity before making the API request. Author: Tiago Balabuch - + #> -function Update-FabricSparkSettings { - [CmdletBinding()] +function Update-FabricSparkSettings +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [string]$WorkspaceId, - + [string]$WorkspaceId, + [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] @@ -65,7 +93,7 @@ function Update-FabricSparkSettings { [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [bool]$notebookInteractiveRunEnabled, - + [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [bool]$customizeComputeEnabled, @@ -96,7 +124,8 @@ function Update-FabricSparkSettings { [string]$EnvironmentRuntimeVersion ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -109,45 +138,54 @@ function Update-FabricSparkSettings { # Step 3: Construct the request body # Construct the request body with optional properties - $body = @{} - - if ($PSBoundParameters.ContainsKey('automaticLogEnabled')) { + $body = @{ } + + if ($PSBoundParameters.ContainsKey('automaticLogEnabled')) + { $body.automaticLog = @{ enabled = $automaticLogEnabled } } - if ($PSBoundParameters.ContainsKey('notebookInteractiveRunEnabled')) { + if ($PSBoundParameters.ContainsKey('notebookInteractiveRunEnabled')) + { $body.highConcurrency = @{ notebookInteractiveRunEnabled = $notebookInteractiveRunEnabled } } - if ($PSBoundParameters.ContainsKey('customizeComputeEnabled') ) { + if ($PSBoundParameters.ContainsKey('customizeComputeEnabled') ) + { $body.pool = @{ customizeComputeEnabled = $customizeComputeEnabled } } - if ($PSBoundParameters.ContainsKey('defaultPoolName') -or $PSBoundParameters.ContainsKey('defaultPoolType')) { - if ($PSBoundParameters.ContainsKey('defaultPoolName') -and $PSBoundParameters.ContainsKey('defaultPoolType')) { - $body.pool = @{ - defaultPool = @{ - name = $defaultPoolName - type = $defaultPoolType + if ($PSBoundParameters.ContainsKey('defaultPoolName') -or $PSBoundParameters.ContainsKey('defaultPoolType')) + { + if ($PSBoundParameters.ContainsKey('defaultPoolName') -and $PSBoundParameters.ContainsKey('defaultPoolType')) + { + $body.pool = @{ + defaultPool = @{ + name = $defaultPoolName + type = $defaultPoolType + } } } - } else { + else + { Write-Message -Message "Both 'defaultPoolName' and 'defaultPoolType' must be provided together." -Level Error - throw + throw } } - - if ($PSBoundParameters.ContainsKey('EnvironmentName') -or $PSBoundParameters.ContainsKey('EnvironmentRuntimeVersion')) { + + if ($PSBoundParameters.ContainsKey('EnvironmentName') -or $PSBoundParameters.ContainsKey('EnvironmentRuntimeVersion')) + { $body.environment = @{ name = $EnvironmentName } } - if ($PSBoundParameters.ContainsKey('EnvironmentRuntimeVersion')) { + if ($PSBoundParameters.ContainsKey('EnvironmentRuntimeVersion')) + { $body.environment = @{ runtimeVersion = $EnvironmentRuntimeVersion } @@ -157,19 +195,24 @@ function Update-FabricSparkSettings { $bodyJson = $body | ConvertTo-Json Write-Message -Message "Request Body: $bodyJson" -Level Debug - # Step 4: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Patch ` - -Body $bodyJson ` - -ContentType "application/json" ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -StatusCodeVariable "statusCode" + if ($PSCmdlet.ShouldProcess($apiEndpointUrl, "Update SparkSettings")) + { + + # Step 4: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Patch ` + -Body $bodyJson ` + -ContentType "application/json" ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -StatusCodeVariable "statusCode" + } # Step 5: Validate the response code - if ($statusCode -ne 200) { + if ($statusCode -ne 200) + { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error Write-Message -Message "Error: $($response.message)" -Level Error Write-Message -Message "Error Details: $($response.moreDetails)" -Level Error @@ -181,7 +224,8 @@ function Update-FabricSparkSettings { Write-Message -Message "Spark Custom Pool '$SparkSettingsName' updated successfully!" -Level Info return $response } - catch { + catch + { # Step 7: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to update SparkSettings. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Tenant/Get-FabricCapacityTenantSettingOverrides.ps1 b/source/Public/Tenant/Get-FabricCapacityTenantSettingOverrides.ps1 similarity index 97% rename from FabricTools/public/Tenant/Get-FabricCapacityTenantSettingOverrides.ps1 rename to source/Public/Tenant/Get-FabricCapacityTenantSettingOverrides.ps1 index e36f1f3a..d4d50c61 100644 --- a/FabricTools/public/Tenant/Get-FabricCapacityTenantSettingOverrides.ps1 +++ b/source/Public/Tenant/Get-FabricCapacityTenantSettingOverrides.ps1 @@ -52,19 +52,17 @@ function Get-FabricCapacityTenantSettingOverrides { $response = Invoke-FabricAPIRequest ` -BaseURI $apiEndpointURI ` -Headers $FabricConfig.FabricHeaders ` - -Method Get + -Method Get # Step 4: Check if any capacity tenant setting overrides were retrieved and handle results accordingly if ($response) { Write-Message -Message $message -Level Debug return $response - } - else { + } else { Write-Message -Message "No capacity tenant setting overrides found." -Level Warning return $null } - } - catch { + } catch { # Step 5: Log detailed error information if the API request fails $errorDetails = $_.Exception.Message Write-Message -Message "Error retrieving capacity tenant setting overrides: $errorDetails" -Level Error diff --git a/FabricTools/public/Tenant/Get-FabricDomainTenantSettingOverrides.ps1 b/source/Public/Tenant/Get-FabricDomainTenantSettingOverrides.ps1 similarity index 97% rename from FabricTools/public/Tenant/Get-FabricDomainTenantSettingOverrides.ps1 rename to source/Public/Tenant/Get-FabricDomainTenantSettingOverrides.ps1 index 22ce3d3a..c5fb76b1 100644 --- a/FabricTools/public/Tenant/Get-FabricDomainTenantSettingOverrides.ps1 +++ b/source/Public/Tenant/Get-FabricDomainTenantSettingOverrides.ps1 @@ -35,19 +35,17 @@ function Get-FabricDomainTenantSettingOverrides { $response = Invoke-FabricAPIRequest ` -BaseURI $apiEndpointURI ` -Headers $FabricConfig.FabricHeaders ` - -Method Get + -Method Get # Step 4: Check if any domain tenant setting overrides were retrieved and handle results accordingly if ($response) { Write-Message -Message "Successfully retrieved domain tenant setting overrides." -Level Debug return $response - } - else { + } else { Write-Message -Message "No domain tenant setting overrides found." -Level Warning return $null } - } - catch { + } catch { # Step 5: Log detailed error information if the API request fails $errorDetails = $_.Exception.Message Write-Message -Message "Error retrieving domain tenant setting overrides: $errorDetails" -Level Error diff --git a/FabricTools/public/Tenant/Get-FabricTenantSetting.ps1 b/source/Public/Tenant/Get-FabricTenantSetting.ps1 similarity index 95% rename from FabricTools/public/Tenant/Get-FabricTenantSetting.ps1 rename to source/Public/Tenant/Get-FabricTenantSetting.ps1 index 655e1c9c..7a83a33f 100644 --- a/FabricTools/public/Tenant/Get-FabricTenantSetting.ps1 +++ b/source/Public/Tenant/Get-FabricTenantSetting.ps1 @@ -22,7 +22,7 @@ Returns the tenant setting with the title "SomeSetting". - Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. - Calls `Is-TokenExpired` to ensure token validity before making the API request. -Author: Tiago Balabuch +Author: Tiago Balabuch #> @@ -48,14 +48,13 @@ function Get-FabricTenantSetting { $response = Invoke-FabricAPIRequest ` -BaseURI $apiEndpointURI ` -Headers $FabricConfig.FabricHeaders ` - -Method Get + -Method Get # Step 4: Filter tenant settings based on the provided SettingTitle parameter (if specified) $settings = if ($SettingTitle) { Write-Message -Message "Filtering tenant settings by title: '$SettingTitle'" -Level Debug $response.tenantSettings | Where-Object { $_.title -eq $SettingTitle } - } - else { + } else { Write-Message -Message "No filter specified. Retrieving all tenant settings." -Level Debug $response.tenantSettings } @@ -64,13 +63,11 @@ function Get-FabricTenantSetting { if ($settings) { Write-Message -Message "Tenant settings successfully retrieved." -Level Debug return $settings - } - else { + } else { Write-Message -Message "No tenant settings found matching the specified criteria." -Level Warning return $null } - } - catch { + } catch { # Step 6: Log detailed error information if the API request fails $errorDetails = $_.Exception.Message Write-Message -Message "Error retrieving tenant settings: $errorDetails" -Level Error diff --git a/FabricTools/public/Tenant/Get-FabricWorkspaceTenantSettingOverrides.ps1 b/source/Public/Tenant/Get-FabricWorkspaceTenantSettingOverrides.ps1 similarity index 97% rename from FabricTools/public/Tenant/Get-FabricWorkspaceTenantSettingOverrides.ps1 rename to source/Public/Tenant/Get-FabricWorkspaceTenantSettingOverrides.ps1 index c04e8ed1..9ff733b1 100644 --- a/FabricTools/public/Tenant/Get-FabricWorkspaceTenantSettingOverrides.ps1 +++ b/source/Public/Tenant/Get-FabricWorkspaceTenantSettingOverrides.ps1 @@ -34,19 +34,17 @@ function Get-FabricWorkspaceTenantSettingOverrides { $response = Invoke-FabricAPIRequest ` -BaseURI $apiEndpointURI ` -Headers $FabricConfig.FabricHeaders ` - -Method Get + -Method Get # Step 4: Check if any workspaces tenant setting overrides were retrieved and handle results accordingly if ($response) { Write-Message -Message "Successfully retrieved workspaces tenant setting overrides." -Level Debug return $response - } - else { + } else { Write-Message -Message "No workspaces tenant setting overrides found." -Level Warning return $null } - } - catch { + } catch { # Step 5: Log detailed error information if the API request fails $errorDetails = $_.Exception.Message Write-Message -Message "Error retrieving workspaces tenant setting overrides: $errorDetails" -Level Error diff --git a/FabricTools/public/Tenant/Revoke-FabricCapacityTenantSettingOverrides.ps1 b/source/Public/Tenant/Revoke-FabricCapacityTenantSettingOverrides.ps1 similarity index 92% rename from FabricTools/public/Tenant/Revoke-FabricCapacityTenantSettingOverrides.ps1 rename to source/Public/Tenant/Revoke-FabricCapacityTenantSettingOverrides.ps1 index 7665ddea..e47c7d14 100644 --- a/FabricTools/public/Tenant/Revoke-FabricCapacityTenantSettingOverrides.ps1 +++ b/source/Public/Tenant/Revoke-FabricCapacityTenantSettingOverrides.ps1 @@ -23,7 +23,7 @@ Removes the tenant setting override named "ExampleSetting" from the capacity wit Author: Tiago Balabuch #> function Revoke-FabricCapacityTenantSettingOverrides { - [CmdletBinding()] + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -43,18 +43,18 @@ function Revoke-FabricCapacityTenantSettingOverrides { $apiEndpointURI = "{0}/admin/capacities/{1}/delegatedTenantSettingOverrides/{2}" -f $FabricConfig.BaseUrl, $capacityId, $tenantSettingName Write-Message -Message "Constructed API Endpoint: $apiEndpointURI" -Level Debug + if ($PSCmdlet.ShouldProcess("$tenantSettingName" , "Revoke")) { # Step 3: Invoke the Fabric API to retrieve capacity tenant setting overrides $response = Invoke-FabricAPIRequest ` -BaseURI $apiEndpointURI ` -Headers $FabricConfig.FabricHeaders ` - -Method Delete - + -Method Delete + } Write-Message -Message "Successfully removed the tenant setting override '$tenantSettingName' from the capacity with ID '$capacityId'." -Level Info return $response - } - catch { + } catch { # Step 5: Log detailed error information if the API request fails $errorDetails = $_.Exception.Message Write-Message -Message "Error retrieving capacity tenant setting overrides: $errorDetails" -Level Error } -} \ No newline at end of file +} diff --git a/FabricTools/public/Tenant/Update-FabricCapacityTenantSettingOverrides.ps1 b/source/Public/Tenant/Update-FabricCapacityTenantSettingOverrides.ps1 similarity index 82% rename from FabricTools/public/Tenant/Update-FabricCapacityTenantSettingOverrides.ps1 rename to source/Public/Tenant/Update-FabricCapacityTenantSettingOverrides.ps1 index f352b771..5c6f6fe4 100644 --- a/FabricTools/public/Tenant/Update-FabricCapacityTenantSettingOverrides.ps1 +++ b/source/Public/Tenant/Update-FabricCapacityTenantSettingOverrides.ps1 @@ -41,8 +41,9 @@ Author: Tiago Balabuch #> -function Update-FabricCapacityTenantSettingOverrides { - [CmdletBinding()] +function Update-FabricCapacityTenantSettingOverrides +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -59,7 +60,7 @@ function Update-FabricCapacityTenantSettingOverrides { [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [bool]$DelegateToWorkspace, - + [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [System.Object]$EnabledSecurityGroups, @@ -69,24 +70,31 @@ function Update-FabricCapacityTenantSettingOverrides { [System.Object]$ExcludedSecurityGroups ) - try { + try + { # Validate authentication token Write-Message -Message "Validating authentication token..." -Level Debug Test-TokenExpired Write-Message -Message "Authentication token is valid." -Level Debug # Validate Security Groups if provided - if ($EnabledSecurityGroups) { - foreach ($enabledGroup in $EnabledSecurityGroups) { - if (-not ($enabledGroup.PSObject.Properties.Name -contains 'graphId' -and $enabledGroup.PSObject.Properties.Name -contains 'name')) { + if ($EnabledSecurityGroups) + { + foreach ($enabledGroup in $EnabledSecurityGroups) + { + if (-not ($enabledGroup.PSObject.Properties.Name -contains 'graphId' -and $enabledGroup.PSObject.Properties.Name -contains 'name')) + { throw "Each enabled security group must contain 'graphId' and 'name' properties." } } } - if ($ExcludedSecurityGroups) { - foreach ($excludedGroup in $ExcludedSecurityGroups) { - if (-not ($excludedGroup.PSObject.Properties.Name -contains 'graphId' -and $excludedGroup.PSObject.Properties.Name -contains 'name')) { + if ($ExcludedSecurityGroups) + { + foreach ($excludedGroup in $ExcludedSecurityGroups) + { + if (-not ($excludedGroup.PSObject.Properties.Name -contains 'graphId' -and $excludedGroup.PSObject.Properties.Name -contains 'name')) + { throw "Each excluded security group must contain 'graphId' and 'name' properties." } } @@ -102,35 +110,39 @@ function Update-FabricCapacityTenantSettingOverrides { SettingTitle = $SettingTitle } - if ($DelegateToWorkspace) { + if ($DelegateToWorkspace) + { $body.delegateToWorkspace = $DelegateToWorkspace } - if ($EnabledSecurityGroups) { + if ($EnabledSecurityGroups) + { $body.enabledSecurityGroups = $EnabledSecurityGroups } - if ($ExcludedSecurityGroups) { + if ($ExcludedSecurityGroups) + { $body.excludedSecurityGroups = $ExcludedSecurityGroups } # Convert body to JSON $bodyJson = $body | ConvertTo-Json -Depth 4 Write-Message -Message "Request Body: $bodyJson" -Level Debug - - # Invoke Fabric API request - $response = Invoke-FabricAPIRequest ` - -BaseURI $apiEndpointURI ` - -Headers $FabricConfig.FabricHeaders ` - -Method Post ` - -Body $bodyJson + if ($PSCmdlet.ShouldProcess($apiEndpointURI, "Update Tenant Setting Overrides")){ + # Invoke Fabric API request + $response = Invoke-FabricAPIRequest ` + -BaseURI $apiEndpointURI ` + -Headers $FabricConfig.FabricHeaders ` + -method Post ` + -body $bodyJson + } Write-Message -Message "Successfully updated capacity tenant setting overrides for CapacityId: $CapacityId and SettingTitle: $SettingTitle." -Level Info return $response } - catch { + catch + { $errorDetails = $_.Exception.Message Write-Message -Message "Error updating tenant settings: $errorDetails" -Level Error } } - diff --git a/FabricTools/public/Tenant/Update-FabricTenantSetting.ps1 b/source/Public/Tenant/Update-FabricTenantSetting.ps1 similarity index 81% rename from FabricTools/public/Tenant/Update-FabricTenantSetting.ps1 rename to source/Public/Tenant/Update-FabricTenantSetting.ps1 index 0a701151..88fd6845 100644 --- a/FabricTools/public/Tenant/Update-FabricTenantSetting.ps1 +++ b/source/Public/Tenant/Update-FabricTenantSetting.ps1 @@ -41,8 +41,9 @@ Author: Tiago Balabuch #> -function Update-FabricCapacityTenantSettingOverrides { - [CmdletBinding()] +function Update-FabricCapacityTenantSettingOverrides +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -63,7 +64,7 @@ function Update-FabricCapacityTenantSettingOverrides { [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [bool]$DelegateToWorkspace, - + [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [System.Object]$EnabledSecurityGroups, @@ -77,33 +78,43 @@ function Update-FabricCapacityTenantSettingOverrides { [System.Object]$Properties ) - try { + try + { # Validate authentication token Write-Message -Message "Validating authentication token..." -Level Debug Test-TokenExpired Write-Message -Message "Authentication token is valid." -Level Debug # Validate Security Groups if provided - if ($EnabledSecurityGroups) { - foreach ($enabledGroup in $EnabledSecurityGroups) { - if (-not ($enabledGroup.PSObject.Properties.Name -contains 'graphId' -and $enabledGroup.PSObject.Properties.Name -contains 'name')) { + if ($EnabledSecurityGroups) + { + foreach ($enabledGroup in $EnabledSecurityGroups) + { + if (-not ($enabledGroup.PSObject.Properties.Name -contains 'graphId' -and $enabledGroup.PSObject.Properties.Name -contains 'name')) + { throw "Each enabled security group must contain 'graphId' and 'name' properties." } } } - if ($ExcludedSecurityGroups) { - foreach ($excludedGroup in $ExcludedSecurityGroups) { - if (-not ($excludedGroup.PSObject.Properties.Name -contains 'graphId' -and $excludedGroup.PSObject.Properties.Name -contains 'name')) { + if ($ExcludedSecurityGroups) + { + foreach ($excludedGroup in $ExcludedSecurityGroups) + { + if (-not ($excludedGroup.PSObject.Properties.Name -contains 'graphId' -and $excludedGroup.PSObject.Properties.Name -contains 'name')) + { throw "Each excluded security group must contain 'graphId' and 'name' properties." } } } # Validate Security Groups if provided - if ($Properties) { - foreach ($property in $Properties) { - if (-not ($property.PSObject.Properties.Name -contains 'name' -and $property.PSObject.Properties.Name -contains 'type' -and $property.PSObject.Properties.Name -contains 'value')) { + if ($Properties) + { + foreach ($property in $Properties) + { + if (-not ($property.PSObject.Properties.Name -contains 'name' -and $property.PSObject.Properties.Name -contains 'type' -and $property.PSObject.Properties.Name -contains 'value')) + { throw "Each property object must include 'name', 'type', and 'value' properties to be valid." } } @@ -118,27 +129,33 @@ function Update-FabricCapacityTenantSettingOverrides { EnableTenantSetting = $EnableTenantSetting } - if ($DelegateToCapacity) { + if ($DelegateToCapacity) + { $body.delegateToCapacity = $DelegateToCapacity } - if ($DelegateToDomain) { + if ($DelegateToDomain) + { $body.delegateToDomain = $DelegateToDomain } - if ($DelegateToWorkspace) { + if ($DelegateToWorkspace) + { $body.delegateToWorkspace = $DelegateToWorkspace } - if ($EnabledSecurityGroups) { + if ($EnabledSecurityGroups) + { $body.enabledSecurityGroups = $EnabledSecurityGroups } - if ($ExcludedSecurityGroups) { + if ($ExcludedSecurityGroups) + { $body.excludedSecurityGroups = $ExcludedSecurityGroups } - if ($Properties) { + if ($Properties) + { $body.properties = $Properties } @@ -146,19 +163,23 @@ function Update-FabricCapacityTenantSettingOverrides { $bodyJson = $body | ConvertTo-Json -Depth 5 Write-Message -Message "Request Body: $bodyJson" -Level Debug - # Invoke Fabric API request - $response = Invoke-FabricAPIRequest ` - -BaseURI $apiEndpointURI ` - -Headers $FabricConfig.FabricHeaders ` - -Method Post ` - -Body $bodyJson + if ($PSCmdlet.ShouldProcess($apiEndpointURI, "Update Tenant Setting")) + { + + # Invoke Fabric API request + $response = Invoke-FabricAPIRequest ` + -BaseURI $apiEndpointURI ` + -Headers $FabricConfig.FabricHeaders ` + -method Post ` + -body $bodyJson + } Write-Message -Message "Successfully updated tenant setting." -Level Info return $response } - catch { + catch + { $errorDetails = $_.Exception.Message Write-Message -Message "Error updating tenant settings: $errorDetails" -Level Error } } - diff --git a/FabricTools/public/Users/Get-FabricUserListAccessEntities.ps1 b/source/Public/Users/Get-FabricUserListAccessEntities.ps1 similarity index 98% rename from FabricTools/public/Users/Get-FabricUserListAccessEntities.ps1 rename to source/Public/Users/Get-FabricUserListAccessEntities.ps1 index 8d15cdaa..c39956ec 100644 --- a/FabricTools/public/Users/Get-FabricUserListAccessEntities.ps1 +++ b/source/Public/Users/Get-FabricUserListAccessEntities.ps1 @@ -40,11 +40,11 @@ function Get-FabricUserListAccessEntities { ) try { - + Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired Write-Message -Message "Token validation completed." -Level Debug - + # Step 4: Loop to retrieve all capacities with continuation token $apiEndpointURI = "{0}admin/users/{1}/access" -f $FabricConfig.BaseUrl, $UserId @@ -58,11 +58,10 @@ function Get-FabricUserListAccessEntities { -Method Get return $response - } - catch { + } catch { # Step 10: Capture and log error details $errorDetails = $_.Exception.Message Write-Message -Message "Failed to retrieve Warehouse. Error: $errorDetails" -Level Error - } - -} + } + +} \ No newline at end of file diff --git a/FabricTools/public/Utils/Convert-FromBase64.ps1 b/source/Public/Utils/Convert-FromBase64.ps1 similarity index 82% rename from FabricTools/public/Utils/Convert-FromBase64.ps1 rename to source/Public/Utils/Convert-FromBase64.ps1 index 5aa99e13..7d2de3c9 100644 --- a/FabricTools/public/Utils/Convert-FromBase64.ps1 +++ b/source/Public/Utils/Convert-FromBase64.ps1 @@ -1,10 +1,11 @@ -<# +function Convert-FromBase64 { + <# .SYNOPSIS Decodes a Base64-encoded string into its original text representation. .DESCRIPTION - The Convert-FromBase64 function takes a Base64-encoded string as input, decodes it into a byte array, - and converts it back into a UTF-8 encoded string. It is useful for reversing Base64 encoding applied + The Convert-FromBase64 function takes a Base64-encoded string as input, decodes it into a byte array, + and converts it back into a UTF-8 encoded string. It is useful for reversing Base64 encoding applied to text or other data. .PARAMETER Base64String @@ -22,15 +23,10 @@ Output: Some encoded text - .NOTES - - This function assumes the Base64 input is a valid UTF-8 encoded string. - - Any decoding errors will throw a descriptive error message. - -.AUTHOR -Tiago Balabuch -#> -function Convert-FromBase64 { +This function assumes the Base64 input is a valid UTF-8 encoded string. +Any decoding errors will throw a descriptive error message. + #> param ( [Parameter(Mandatory = $true)] [string]$Base64String @@ -45,8 +41,7 @@ function Convert-FromBase64 { # Step 3: Return the decoded string return $decodedString - } - catch { + } catch { # Step 4: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "An error occurred while decoding from Base64: $errorDetails" -Level Error diff --git a/FabricTools/public/Utils/Convert-ToBase64.ps1 b/source/Public/Utils/Convert-ToBase64.ps1 similarity index 92% rename from FabricTools/public/Utils/Convert-ToBase64.ps1 rename to source/Public/Utils/Convert-ToBase64.ps1 index 8b152567..78a17453 100644 --- a/FabricTools/public/Utils/Convert-ToBase64.ps1 +++ b/source/Public/Utils/Convert-ToBase64.ps1 @@ -1,10 +1,11 @@ -<# +function Convert-ToBase64 { + <# .SYNOPSIS Encodes the content of a file into a Base64-encoded string. .DESCRIPTION - The Convert-ToBase64 function takes a file path as input, reads the file's content as a byte array, - and converts it into a Base64-encoded string. This is useful for embedding binary data (e.g., images, + The Convert-ToBase64 function takes a file path as input, reads the file's content as a byte array, + and converts it into a Base64-encoded string. This is useful for embedding binary data (e.g., images, documents) in text-based formats such as JSON or XML. .PARAMETER filePath @@ -26,18 +27,18 @@ - Ensure the file exists at the specified path before running this function. - Large files may cause memory constraints due to full loading into memory. -.AUTHOR -Tiago Balabuch -#> -function Convert-ToBase64 { + + Tiago Balabuch + #> [CmdletBinding()] + [OutputType([System.String])] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$filePath ) try { - + # Step 1: Reading all the bytes from the file #$bytes = [System.Text.Encoding]::UTF8.GetBytes($InputString) Write-Message -Message "Reading all the bytes from the file specified: $filePath" -Level Debug @@ -50,11 +51,10 @@ function Convert-ToBase64 { # Step 3: Return the encoded string Write-Message -Message "Return the encoded string for the file: $filePath" -Level Debug return $base64String - } - catch { + } catch { # Step 4: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "An error occurred while encoding to Base64: $errorDetails" -Level Error throw "An error occurred while encoding to Base64: $_" } -} \ No newline at end of file +} diff --git a/FabricTools/public/Utils/Get-FabricLongRunningOperation.ps1 b/source/Public/Utils/Get-FabricLongRunningOperation.ps1 similarity index 89% rename from FabricTools/public/Utils/Get-FabricLongRunningOperation.ps1 rename to source/Public/Utils/Get-FabricLongRunningOperation.ps1 index 4fa2cc07..1238955f 100644 --- a/FabricTools/public/Utils/Get-FabricLongRunningOperation.ps1 +++ b/source/Public/Utils/Get-FabricLongRunningOperation.ps1 @@ -1,14 +1,18 @@ -<# +function Get-FabricLongRunningOperation { + <# .SYNOPSIS Monitors the status of a long-running operation in Microsoft Fabric. .DESCRIPTION -The Get-FabricLongRunningOperation function queries the Microsoft Fabric API to check the status of a +The Get-FabricLongRunningOperation function queries the Microsoft Fabric API to check the status of a long-running operation. It periodically polls the operation until it reaches a terminal state (Succeeded or Failed). .PARAMETER operationId The unique identifier of the long-running operation to be monitored. +.PARAMETER location +The URL provided in the Location header of the initial request. This is used to check the status of the operation. + .PARAMETER retryAfter The interval (in seconds) to wait between polling the operation status. The default is 5 seconds. @@ -20,15 +24,13 @@ This command polls the status of the operation with the given operationId every .NOTES - Requires the `$FabricConfig` global object, including `BaseUrl` and `FabricHeaders`. -.AUTHOR -Tiago Balabuch - -#> -function Get-FabricLongRunningOperation { + AUTHOR + Tiago Balabuch + #> param ( [Parameter(Mandatory = $false)] [string]$operationId, - + [Parameter(Mandatory = $false)] [string]$location, @@ -40,20 +42,18 @@ function Get-FabricLongRunningOperation { if ($location) { # Use the Location header to define the operationUrl $apiEndpointUrl = $location - } - else { + } else { $apiEndpointUrl = "https://api.fabric.microsoft.com/v1/operations/{0}" -f $operationId } Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug - + try { do { # Step 2: Wait before the next request if ($retryAfter) { Start-Sleep -Seconds $retryAfter - } - else { + } else { Start-Sleep -Seconds 5 # Default retry interval if no Retry-After header } @@ -73,13 +73,12 @@ function Get-FabricLongRunningOperation { # Log status for debugging Write-Message -Message "Operation Status: $($operation.status)" -Level Debug - + } while ($operation.status -notin @("Succeeded", "Completed", "Failed")) # Step 5: Return the operation result return $operation - } - catch { + } catch { # Step 6: Capture and log error details $errorDetails = $_.Exception.Message Write-Message -Message "An error occurred while checking the operation: $errorDetails" -Level Error diff --git a/FabricTools/public/Utils/Get-FabricLongRunningOperationResult.ps1 b/source/Public/Utils/Get-FabricLongRunningOperationResult.ps1 similarity index 83% rename from FabricTools/public/Utils/Get-FabricLongRunningOperationResult.ps1 rename to source/Public/Utils/Get-FabricLongRunningOperationResult.ps1 index 00000c6c..7e9e81c8 100644 --- a/FabricTools/public/Utils/Get-FabricLongRunningOperationResult.ps1 +++ b/source/Public/Utils/Get-FabricLongRunningOperationResult.ps1 @@ -1,9 +1,10 @@ -<# +function Get-FabricLongRunningOperationResult { + <# .SYNOPSIS Retrieves the result of a completed long-running operation from the Microsoft Fabric API. .DESCRIPTION -The Get-FabricLongRunningOperationResult function queries the Microsoft Fabric API to fetch the result +The Get-FabricLongRunningOperationResult function queries the Microsoft Fabric API to fetch the result of a specific long-running operation. This is typically used after confirming the operation has completed successfully. .PARAMETER operationId @@ -18,11 +19,9 @@ This command fetches the result of the operation with the specified operationId. - Ensure the Fabric API headers (e.g., authorization tokens) are defined in $FabricConfig.FabricHeaders. - This function does not handle polling. Ensure the operation is in a terminal state before calling this function. -.AUTHOR -Tiago Balabuch - -#> -function Get-FabricLongRunningOperationResult { + AUTHOR + Tiago Balabuch + #> param ( [Parameter(Mandatory = $true)] [string]$operationId @@ -35,14 +34,14 @@ function Get-FabricLongRunningOperationResult { try { # Step 2: Make the API request $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Get ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -ResponseHeadersVariable "responseHeader" ` - -StatusCodeVariable "statusCode" - + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Get ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -ResponseHeadersVariable "responseHeader" ` + -StatusCodeVariable "statusCode" + # Step 3: Return the result Write-Message -Message "Result response code: $statusCode" -Level Debug @@ -56,9 +55,8 @@ function Get-FabricLongRunningOperationResult { Write-Message "Error Code: $($response.errorCode)" -Level Debug } - return $response - } - catch { + return $response + } catch { # Step 3: Capture and log error details $errorDetails = $_.Exception.Message Write-Message -Message "An error occurred while returning the operation result: $errorDetails" -Level Error diff --git a/FabricTools/public/Utils/Invoke-FabricAPIRequest.ps1 b/source/Public/Utils/Invoke-FabricAPIRequest_duplicate.ps1 similarity index 62% rename from FabricTools/public/Utils/Invoke-FabricAPIRequest.ps1 rename to source/Public/Utils/Invoke-FabricAPIRequest_duplicate.ps1 index c2d4f894..25632b9d 100644 --- a/FabricTools/public/Utils/Invoke-FabricAPIRequest.ps1 +++ b/source/Public/Utils/Invoke-FabricAPIRequest_duplicate.ps1 @@ -1,21 +1,69 @@ -function Invoke-FabricAPIRequest { +function Invoke-FabricAPIRequest_duplicate { + + <# + .SYNOPSIS + Sends an HTTP request to a Fabric API endpoint and retrieves the response. + Takes care of: authentication, 429 throttling, Long-Running-Operation (LRO) response + + .DESCRIPTION + The Invoke-FabricAPIRequest function is used to send an HTTP request to a Fabric API endpoint and retrieve the response. It handles various aspects such as authentication, 429 throttling, and Long-Running-Operation (LRO) response. + + .PARAMETER authToken + The authentication token to be used for the request. If not provided, it will be obtained using the Get-FabricAuthToken function. + + .PARAMETER uri + The URI of the Fabric API endpoint to send the request to. + + .PARAMETER method + The HTTP method to be used for the request. Valid values are 'Get', 'Post', 'Delete', 'Put', and 'Patch'. The default value is 'Get'. + + .PARAMETER body + The body of the request, if applicable. + + .PARAMETER contentType + The content type of the request. The default value is 'application/json; charset=utf-8'. + + .PARAMETER timeoutSec + The timeout duration for the request in seconds. The default value is 240 seconds. + + .PARAMETER outFile + The file path to save the response content to, if applicable. + + .PARAMETER retryCount + The number of times to retry the request in case of a 429 (Too Many Requests) error. The default value is 0. + + .EXAMPLE + Invoke-FabricAPIRequest -uri "/api/resource" -method "Get" + + This example sends a GET request to the "/api/resource" endpoint of the Fabric API. + + .EXAMPLE + Invoke-FabricAPIRequest -authToken "abc123" -uri "/api/resource" -method "Post" -body $requestBody + + This example sends a POST request to the "/api/resource" endpoint of the Fabric API with a request body. + + .NOTES + This function requires the Get-FabricAuthToken function to be defined in the same script or module. + This function was originally written by Rui Romano. + https://github.com/RuiRomano/fabricps-pbip + #> param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [hashtable]$Headers, - + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$BaseURI, - [Parameter(Mandatory = $true)] - [ValidateSet('Get', 'Post', 'Delete', 'Put', 'Patch')] + [Parameter(Mandatory = $true)] + [ValidateSet('Get', 'Post', 'Delete', 'Put', 'Patch')] [string] $Method, - - [Parameter(Mandatory = $false)] + + [Parameter(Mandatory = $false)] [string] $Body, - [Parameter(Mandatory = $false)] + [Parameter(Mandatory = $false)] [string] $ContentType = "application/json; charset=utf-8" ) @@ -34,8 +82,7 @@ function Invoke-FabricAPIRequest { if ($BaseURI -like "*`?*") { # URI already has parameters, append with & $apiEndpointURI = "$BaseURI&continuationToken=$encodedToken" - } - else { + } else { # No existing parameters, append with ? $apiEndpointURI = "$BaseURI?continuationToken=$encodedToken" } @@ -62,54 +109,53 @@ function Invoke-FabricAPIRequest { switch ($statusCode) { 200 { - Write-Message -Message "API call succeeded." -Level Debug + Write-Message -Message "API call succeeded." -Level Debug # Step 5: Handle and log the response if ($response) { if ($response.PSObject.Properties.Name -contains 'value') { $results += $response.value - } - elseif ($response.PSObject.Properties.Name -contains 'accessEntities') { + } elseif ($response.PSObject.Properties.Name -contains 'accessEntities') { $results += $response.accessEntities - } - else { + } else { $results += $response } - $continuationToken = $response.PSObject.Properties.Match("continuationToken") ? $response.continuationToken : $null - } - else { + $continuationToken = $null + if ($response.PSObject.Properties.Match("continuationToken")) { + $continuationToken = $response.continuationToken + } + } else { Write-Message -Message "No data in response" -Level Debug $continuationToken = $null } } 201 { - Write-Message -Message "Resource created successfully." -Level Info + Write-Message -Message "Resource created successfully." -Level Info return $response - } + } 202 { - # Step 6: Handle long-running operations + # Step 6: Handle long-running operations Write-Message -Message "Request accepted. Provisioning in progress." -Level Info [string]$operationId = $responseHeader["x-ms-operation-id"] [string]$location = $responseHeader["Location"] - # Need to implement a retry mechanism for long running operations - # [string]$retryAfter = $responseHeader["Retry-After"] + # Need to implement a retry mechanism for long running operations + # [string]$retryAfter = $responseHeader["Retry-After"] Write-Message -Message "Operation ID: '$operationId', Location: '$location'" -Level Debug Write-Message -Message "Getting Long Running Operation status" -Level Debug - + $operationStatus = Get-FabricLongRunningOperation -operationId $operationId -location $location Write-Message -Message "Long Running Operation status: $operationStatus" -Level Debug - + # Handle operation result if ($operationStatus.status -eq "Succeeded") { Write-Message -Message "Operation succeeded. Fetching result." -Level Debug - + $operationResult = Get-FabricLongRunningOperationResult -operationId $operationId - Write-Message -Message "Long Running Operation result: $operationResult" -Level Debug + Write-Message -Message "Long Running Operation result: $operationResult" -Level Debug return $operationResult - } - else { + } else { Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Error return $operationStatus - } + } } 400 { $errorMsg = "Bad Request" } 401 { $errorMsg = "Unauthorized" } @@ -120,7 +166,7 @@ function Invoke-FabricAPIRequest { 500 { $errorMsg = "Internal Server Error" } default { $errorMsg = "Unexpected response code: $statusCode" } } - + if ($statusCode -notin 200, 201, 202) { Write-Message -Message "$errorMsg : $($response.message)" -Level Error Write-Message -Message "Error Details: $($response.moreDetails)" -Level Error diff --git a/FabricTools/public/Utils/Set-FabricApiHeaders.ps1 b/source/Public/Utils/Set-FabricApiHeaders.ps1 similarity index 80% rename from FabricTools/public/Utils/Set-FabricApiHeaders.ps1 rename to source/Public/Utils/Set-FabricApiHeaders.ps1 index e1c7a43d..ef46e676 100644 --- a/FabricTools/public/Utils/Set-FabricApiHeaders.ps1 +++ b/source/Public/Utils/Set-FabricApiHeaders.ps1 @@ -1,9 +1,11 @@ -<# +function Set-FabricApiHeaders +{ + <# .SYNOPSIS Sets the Fabric API headers with a valid token for the specified Azure tenant. .DESCRIPTION -The `Set-FabricApiHeaders` function logs into the specified Azure tenant, retrieves an access token for the Fabric API, and sets the necessary headers for subsequent API requests. +The `Set-FabricApiHeaders` function logs into the specified Azure tenant, retrieves an access token for the Fabric API, and sets the necessary headers for subsequent API requests. It also updates the token expiration time and global tenant ID. .PARAMETER TenantId @@ -33,12 +35,11 @@ Logs in to Azure with the specified tenant ID, retrieves an access token for the - Ensure the `Connect-AzAccount` and `Get-AzAccessToken` commands are available (Azure PowerShell module required). - Relies on a global `$FabricConfig` object for storing headers and token metadata. -.AUTHOR -Tiago Balabuch -#> + AUTHOR + Tiago Balabuch + #> -function Set-FabricApiHeaders { - [CmdletBinding()] + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -51,39 +52,44 @@ function Set-FabricApiHeaders { [System.Security.SecureString]$AppSecret ) - try { + try + { # Step 1: Connect to the Azure account Write-Message -Message "Logging in to Azure tenant: $TenantId" -Level Info # Step 2: Performing validation checks on the parameters passed to a function or script. # Checks if 'AppId' is provided without 'AppSecret' and vice versa. - if ($PSBoundParameters.ContainsKey('AppId') -and -not $PSBoundParameters.ContainsKey('AppSecret')) { + if ($PSBoundParameters.ContainsKey('AppId') -and -not $PSBoundParameters.ContainsKey('AppSecret')) + { Write-Message -Message "AppSecret is required when using AppId: $AppId" -Level Error throw "AppSecret is required when using AppId." - } - if ($PSBoundParameters.ContainsKey('AppSecret') -and -not $PSBoundParameters.ContainsKey('AppId')) { + } + if ($PSBoundParameters.ContainsKey('AppSecret') -and -not $PSBoundParameters.ContainsKey('AppId')) + { Write-Message -Message "AppId is required when using AppSecret." -Level Error throw "AppId is required when using AppId." } # Step 3: Connect to the Azure account # Using AppId and AppSecret - if ($PSBoundParameters.ContainsKey('AppId') -and $PSBoundParameters.ContainsKey('AppSecret')) { - + if ($PSBoundParameters.ContainsKey('AppId') -and $PSBoundParameters.ContainsKey('AppSecret')) + { + Write-Message -Message "Logging in using the AppId: $AppId" -Level Debug Write-Message -Message "Logging in using the AppId: $AppId" -Level Info $psCredential = [pscredential]::new($AppId, $AppSecret) Connect-AzAccount -ServicePrincipal -Credential $psCredential -Tenant $tenantId - } + } # Using the current user - else { - + else + { + Write-Message -Message "Logging in using the current user" -Level Debug Write-Message -Message "Logging in using the current user" -Level Info Connect-AzAccount -Tenant $TenantId -ErrorAction Stop | Out-Null - } + } - ## Step 4: Retrieve the access token for the Fabric API + ## Step 4: Retrieve the access token for the Fabric API Write-Message -Message "Retrieve the access token for the Fabric API: $TenantId" -Level Debug $fabricToken = Get-AzAccessToken -AsSecureString -ResourceUrl $FabricConfig.ResourceUrl -ErrorAction Stop -WarningAction SilentlyContinue @@ -92,22 +98,26 @@ function Set-FabricApiHeaders { $plainToken = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto( [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($fabricToken.Token) ) - - ## Step 6: Set the headers in the global configuration - Write-Message -Message "Set the headers in the global configuration" -Level Debug - $FabricConfig.FabricHeaders = @{ - 'Content-Type' = 'application/json' - 'Authorization' = "Bearer $plainToken" + if ($PSCmdlet.ShouldProcess("Set the headers in the global configuration $($TenantId)")) + { + ## Step 6: Set the headers in the global configuration + $FabricConfig.FabricHeaders = @{ + 'Content-Type' = 'application/json' + 'Authorization' = "Bearer $plainToken" + } } - ## Step 7: Update token metadata in the global configuration Write-Message -Message "Update token metadata in the global configuration" -Level Debug - $FabricConfig.TokenExpiresOn = $fabricToken.ExpiresOn - $FabricConfig.TenantIdGlobal = $TenantId + if ($PSCmdlet.ShouldProcess("Update token metadata in the global configuration")) + { + $FabricConfig.TokenExpiresOn = $fabricToken.ExpiresOn + $FabricConfig.TenantIdGlobal = $TenantId + } Write-Message -Message "Fabric token successfully configured." -Level Info } - catch { + catch + { # Step 8: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to set Fabric token: $errorDetails" -Level Error diff --git a/source/Public/Utils/Test-FabricApiResponse.ps1 b/source/Public/Utils/Test-FabricApiResponse.ps1 new file mode 100644 index 00000000..34878a11 --- /dev/null +++ b/source/Public/Utils/Test-FabricApiResponse.ps1 @@ -0,0 +1,84 @@ +function Test-FabricApiResponse { + <# +.SYNOPSIS +Tests the response from a Fabric API call and handles long-running operations. +.DESCRIPTION +Tests the response from a Fabric API call and handles long-running operations. It checks the status code and processes the response accordingly. +.PARAMETER statusCode +The HTTP status code returned from the API call. +.PARAMETER response +The response body from the API call. +.PARAMETER responseHeader +The response headers from the API call. +.PARAMETER Name +The name of the resource being created or updated. +.PARAMETER typeName +The type of resource being created or updated (default: 'Fabric Item'). + +.EXAMPLE +Test-FabricApiResponse -statusCode 201 -response $response -responseHeader $header -Name "MyResource" -typeName "Fabric Item" + +Handles the response from a Fabric API call with a 201 status code, indicating successful creation of a resource. + +.EXAMPLE +Test-FabricApiResponse -statusCode 202 -response $response -responseHeader $header -Name "MyResource" -typeName "Fabric Item" + +Handles the response from a Fabric API call with a 202 status code, indicating that the request has been accepted for processing. + +.NOTES +- This function is designed to be used within the context of a Fabric API client. +- It requires the `Write-Message` function to log messages at different levels (Info, Debug, Error). +- The function handles long-running operations by checking the status of the operation and retrieving the result if it has succeeded. + #> + + [CmdletBinding()] + param ( + [Parameter(Mandatory = $true)] + $statusCode, + [Parameter(Mandatory = $false)] + $response, + [Parameter(Mandatory = $false)] + $responseHeader, + [Parameter(Mandatory = $false)] + $Name, + [Parameter(Mandatory = $false)] + $typeName = 'Fabric Item' + ) + + switch ($statusCode) { + 201 { + Write-Message -Message "$typeName '$Name' created successfully!" -Level Info + return $response + } + 202 { + Write-Message -Message "$typeName '$Name' creation accepted. Provisioning in progress!" -Level Info + + [string]$operationId = $responseHeader["x-ms-operation-id"] + Write-Message -Message "Operation ID: '$operationId'" -Level Debug + Write-Message -Message "Getting Long Running Operation status" -Level Debug + + $operationStatus = Get-FabricLongRunningOperation -operationId $operationId + Write-Message -Message "Long Running Operation status: $operationStatus" -Level Debug + # Handle operation result + if ($operationStatus.status -eq "Succeeded") { + Write-Message -Message "Operation Succeeded" -Level Debug + Write-Message -Message "Getting Long Running Operation result" -Level Debug + + $operationResult = Get-FabricLongRunningOperationResult -operationId $operationId + Write-Message -Message "Long Running Operation status: $operationResult" -Level Debug + + return $operationResult + } else { + Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Debug + Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Error + return $operationStatus + } + } + default { + Write-Message -Message "Unexpected response code: $statusCode" -Level Error + Write-Message -Message "Error details: $($response.message)" -Level Error + throw "API request failed with status code $statusCode." + } + } + +} \ No newline at end of file diff --git a/FabricTools/public/Warehouse/Get-FabricWarehouse.ps1 b/source/Public/Warehouse/Get-FabricWarehouse.ps1 similarity index 89% rename from FabricTools/public/Warehouse/Get-FabricWarehouse.ps1 rename to source/Public/Warehouse/Get-FabricWarehouse.ps1 index 304ede6f..f3f08178 100644 --- a/FabricTools/public/Warehouse/Get-FabricWarehouse.ps1 +++ b/source/Public/Warehouse/Get-FabricWarehouse.ps1 @@ -58,25 +58,23 @@ function Get-FabricWarehouse { Test-TokenExpired Write-Message -Message "Token validation completed." -Level Debug # Step 3: Initialize variables - + # Step 4: Loop to retrieve all capacities with continuation token - $apiEndpointURI = "{0}/workspaces/{1}/warehouses" -f $FabricConfig.BaseUrl, $WorkspaceId - - $Warehouses = Invoke-FabricAPIRequest ` - -BaseURI $apiEndpointURI ` - -Headers $FabricConfig.FabricHeaders ` - -Method Get ` - -Body $null + $apiEndpointURI = "workspaces/{0}/warehouses" -f $WorkspaceId + + $apiParams = @{ + Uri = $apiEndpointURI + Method = 'Get' + } + $Warehouses = (Invoke-FabricAPIRequest @apiParams).Value # Step 8: Filter results based on provided parameters $Warehouse = if ($WarehouseId) { $Warehouses | Where-Object { $_.Id -eq $WarehouseId } - } - elseif ($WarehouseName) { + } elseif ($WarehouseName) { $Warehouses | Where-Object { $_.DisplayName -eq $WarehouseName } - } - else { + } else { # Return all Warehouses if no filter is provided Write-Message -Message "No filter provided. Returning all Warehouses." -Level Debug $Warehouses @@ -86,16 +84,14 @@ function Get-FabricWarehouse { if ($Warehouse) { Write-Message -Message "Warehouse found matching the specified criteria." -Level Debug return $Warehouse - } - else { + } else { Write-Message -Message "No Warehouse found matching the provided criteria." -Level Warning return $null } - } - catch { + } catch { # Step 10: Capture and log error details $errorDetails = $_.Exception.Message Write-Message -Message "Failed to retrieve Warehouse. Error: $errorDetails" -Level Error - } - + } + } diff --git a/FabricTools/public/Warehouse/New-FabricWarehouse.ps1 b/source/Public/Warehouse/New-FabricWarehouse.ps1 similarity index 79% rename from FabricTools/public/Warehouse/New-FabricWarehouse.ps1 rename to source/Public/Warehouse/New-FabricWarehouse.ps1 index 2b8a49af..0b9c464c 100644 --- a/FabricTools/public/Warehouse/New-FabricWarehouse.ps1 +++ b/source/Public/Warehouse/New-FabricWarehouse.ps1 @@ -3,7 +3,7 @@ Creates a new warehouse in a specified Microsoft Fabric workspace. .DESCRIPTION - This function sends a POST request to the Microsoft Fabric API to create a new warehouse + This function sends a POST request to the Microsoft Fabric API to create a new warehouse in the specified workspace. It supports optional parameters for warehouse description. .PARAMETER WorkspaceId @@ -25,8 +25,9 @@ Author: Tiago Balabuch #> -function New-FabricWarehouse { - [CmdletBinding()] +function New-FabricWarehouse +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -42,14 +43,15 @@ function New-FabricWarehouse { [string]$WarehouseDescription ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired Write-Message -Message "Token validation completed." -Level Debug # Step 2: Construct the API URL - $apiEndpointURI = "{0}/workspaces/{1}/warehouses" -f $FabricConfig.BaseUrl, $WorkspaceId + $apiEndpointURI = "workspaces/{0}/warehouses" -f $WorkspaceId Write-Message -Message "API Endpoint: $apiEndpointURI" -Level Debug # Step 3: Construct the request body @@ -57,25 +59,31 @@ function New-FabricWarehouse { displayName = $WarehouseName } - if ($WarehouseDescription) { + if ($WarehouseDescription) + { $body.description = $WarehouseDescription } $bodyJson = $body | ConvertTo-Json -Depth 10 Write-Message -Message "Request Body: $bodyJson" -Level Debug - # Step 4: Make the API request - $response = Invoke-FabricAPIRequest ` - -BaseURI $apiEndpointURI ` - -Headers $FabricConfig.FabricHeaders ` - -Method Post ` - -Body $bodyJson + if ($PSCmdlet.ShouldProcess($apiEndpointURI, "Create Warehouse")) + { + # Step 4: Make the API request + $apiParams = @{ + Uri = $apiEndpointURI + Method = 'Post' + Body = $bodyJson + } + $response = Invoke-FabricAPIRequest @apiParams + } - Write-Message -Message "Data Warehouse created successfully!" -Level Info + Write-Message -Message "Data Warehouse created successfully!" -Level Info return $response - + } - catch { + catch + { # Step 6: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to create Warehouse. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Warehouse/Remove-FabricWarehouse.ps1 b/source/Public/Warehouse/Remove-FabricWarehouse.ps1 similarity index 80% rename from FabricTools/public/Warehouse/Remove-FabricWarehouse.ps1 rename to source/Public/Warehouse/Remove-FabricWarehouse.ps1 index 42b245f0..67e7cd97 100644 --- a/FabricTools/public/Warehouse/Remove-FabricWarehouse.ps1 +++ b/source/Public/Warehouse/Remove-FabricWarehouse.ps1 @@ -3,7 +3,7 @@ Removes a warehouse from a specified Microsoft Fabric workspace. .DESCRIPTION - This function sends a DELETE request to the Microsoft Fabric API to remove a warehouse + This function sends a DELETE request to the Microsoft Fabric API to remove a warehouse from the specified workspace using the provided WorkspaceId and WarehouseId. .PARAMETER WorkspaceId @@ -22,8 +22,9 @@ Author: Tiago Balabuch #> -function Remove-FabricWarehouse { - [CmdletBinding()] +function Remove-FabricWarehouse +{ + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -33,7 +34,8 @@ function Remove-FabricWarehouse { [ValidateNotNullOrEmpty()] [string]$WarehouseId ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -43,17 +45,22 @@ function Remove-FabricWarehouse { $apiEndpointURI = "{0}/workspaces/{1}/warehouses/{2}" -f $FabricConfig.BaseUrl, $WorkspaceId, $WarehouseId Write-Message -Message "API Endpoint: $apiEndpointURI" -Level Debug - # Step 3: Make the API request - $response = Invoke-FabricAPIRequest ` - -Headers $FabricConfig.FabricHeaders ` - -BaseURI $apiEndpointURI ` - -Method Delete ` - + if ($PSCmdlet.ShouldProcess($apiEndpointURI, "Delete Warehouse")) + { + # Step 3: Make the API request + $response = Invoke-FabricAPIRequest ` + -Headers $FabricConfig.FabricHeaders ` + -BaseURI $apiEndpointURI ` + -method Delete ` + + } + Write-Message -Message "Warehouse '$WarehouseId' deleted successfully from workspace '$WorkspaceId'." -Level Info return $response } - catch { + catch + { # Step 5: Log and handle errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to delete Warehouse '$WarehouseId' from workspace '$WorkspaceId'. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Warehouse/Update-FabricWarehouse.ps1 b/source/Public/Warehouse/Update-FabricWarehouse.ps1 similarity index 84% rename from FabricTools/public/Warehouse/Update-FabricWarehouse.ps1 rename to source/Public/Warehouse/Update-FabricWarehouse.ps1 index fd6e2856..02c10f3c 100644 --- a/FabricTools/public/Warehouse/Update-FabricWarehouse.ps1 +++ b/source/Public/Warehouse/Update-FabricWarehouse.ps1 @@ -3,7 +3,7 @@ Updates an existing warehouse in a specified Microsoft Fabric workspace. .DESCRIPTION - This function sends a PATCH request to the Microsoft Fabric API to update an existing warehouse + This function sends a PATCH request to the Microsoft Fabric API to update an existing warehouse in the specified workspace. It supports optional parameters for warehouse description. .PARAMETER WorkspaceId @@ -27,15 +27,16 @@ - Calls `Test-TokenExpired` to ensure token validity before making the API request. Author: Tiago Balabuch - + #> -function Update-FabricWarehouse { - [CmdletBinding()] +function Update-FabricWarehouse +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [string]$WorkspaceId, - + [string]$WorkspaceId, + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$WarehouseId, @@ -50,7 +51,8 @@ function Update-FabricWarehouse { [string]$WarehouseDescription ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -65,25 +67,31 @@ function Update-FabricWarehouse { displayName = $WarehouseName } - if ($WarehouseDescription) { + if ($WarehouseDescription) + { $body.description = $WarehouseDescription } # Convert the body to JSON $bodyJson = $body | ConvertTo-Json Write-Message -Message "Request Body: $bodyJson" -Level Debug - - $response = Invoke-FabricAPIRequest ` - -Headers $FabricConfig.FabricHeaders ` - -BaseURI $apiEndpointURI ` - -Method Patch ` - -Body $bodyJson + + if ($PSCmdlet.ShouldProcess($apiEndpointURI, "Update Warehouse")) + { + + $response = Invoke-FabricAPIRequest ` + -Headers $FabricConfig.FabricHeaders ` + -BaseURI $apiEndpointURI ` + -method Patch ` + -body $bodyJson + } # Step 6: Handle results Write-Message -Message "Warehouse '$WarehouseName' updated successfully!" -Level Info return $response } - catch { + catch + { # Step 7: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to update Warehouse. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Workspace/Assign-FabricWorkspaceCapacity.ps1 b/source/Public/Workspace/Add-FabricWorkspaceCapacityAssignment.ps1 similarity index 88% rename from FabricTools/public/Workspace/Assign-FabricWorkspaceCapacity.ps1 rename to source/Public/Workspace/Add-FabricWorkspaceCapacityAssignment.ps1 index 0754a94b..11e52bf6 100644 --- a/FabricTools/public/Workspace/Assign-FabricWorkspaceCapacity.ps1 +++ b/source/Public/Workspace/Add-FabricWorkspaceCapacityAssignment.ps1 @@ -3,7 +3,7 @@ Assigns a Fabric workspace to a specified capacity. .DESCRIPTION -The `Assign-FabricWorkspaceCapacity` function sends a POST request to assign a workspace to a specific capacity. +The `Add-FabricWorkspaceCapacityAssignment` function sends a POST request to assign a workspace to a specific capacity. .PARAMETER WorkspaceId The unique identifier of the workspace to be assigned. @@ -12,7 +12,7 @@ The unique identifier of the workspace to be assigned. The unique identifier of the capacity to which the workspace should be assigned. .EXAMPLE -Assign-FabricWorkspaceCapacity -WorkspaceId "workspace123" -CapacityId "capacity456" +Add-FabricWorkspaceCapacityAssignment -WorkspaceId "workspace123" -CapacityId "capacity456" Assigns the workspace with ID "workspace123" to the capacity "capacity456". @@ -20,11 +20,12 @@ Assigns the workspace with ID "workspace123" to the capacity "capacity456". - Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. - Calls `Test-TokenExpired` to ensure token validity before making the API request. -Author: Tiago Balabuch +Author: Tiago Balabuch #> -function Assign-FabricWorkspaceCapacity { +function Add-FabricWorkspaceCapacityAssignment { [CmdletBinding()] + [Alias("Assign-FabricWorkspaceCapacity")] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -74,8 +75,7 @@ function Assign-FabricWorkspaceCapacity { return $null } Write-Message -Message "Successfully assigned workspace with ID '$WorkspaceId' to capacity with ID '$CapacityId'." -Level Info - } - catch { + } catch { # Step 6: Capture and log error details $errorDetails = $_.Exception.Message Write-Message -Message "Failed to assign workspace with ID '$WorkspaceId' to capacity with ID '$CapacityId'. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Workspace/Add-FabricWorkspaceIdentity.ps1 b/source/Public/Workspace/Add-FabricWorkspaceIdentity.ps1 similarity index 95% rename from FabricTools/public/Workspace/Add-FabricWorkspaceIdentity.ps1 rename to source/Public/Workspace/Add-FabricWorkspaceIdentity.ps1 index cc042432..1dba6e27 100644 --- a/FabricTools/public/Workspace/Add-FabricWorkspaceIdentity.ps1 +++ b/source/Public/Workspace/Add-FabricWorkspaceIdentity.ps1 @@ -17,7 +17,7 @@ Provisions a Managed Identity for the workspace with ID "workspace123". - Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. - Calls `Test-TokenExpired` to ensure token validity before making the API request. -Author: Tiago Balabuch +Author: Tiago Balabuch #> function Add-FabricWorkspaceIdentity { @@ -39,7 +39,7 @@ function Add-FabricWorkspaceIdentity { Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug # Step 3: Make the API request - $response = Invoke-RestMethod ` + $response = Invoke-RestMethod ` -Headers $FabricConfig.FabricHeaders ` -Uri $apiEndpointUrl ` -Method Post ` @@ -59,14 +59,14 @@ function Add-FabricWorkspaceIdentity { Write-Message -Message "Workspace identity provisioning accepted for workspace '$WorkspaceId'. Provisioning in progress!" -Level Info [string]$operationId = $responseHeader["x-ms-operation-id"] [string]$location = $responseHeader["Location"] - [string]$retryAfter = $responseHeader["Retry-After"] + [string]$retryAfter = $responseHeader["Retry-After"] Write-Message -Message "Operation ID: '$operationId'" -Level Debug Write-Message -Message "Location: '$location'" -Level Debug Write-Message -Message "Retry-After: '$retryAfter'" -Level Debug Write-Message -Message "Getting Long Running Operation status" -Level Debug - + $operationStatus = Get-FabricLongRunningOperation -operationId $operationId Write-Message -Message "Long Running Operation status: $operationStatus" -Level Debug @@ -74,17 +74,16 @@ function Add-FabricWorkspaceIdentity { if ($operationStatus.status -eq "Succeeded") { Write-Message -Message "Operation Succeeded" -Level Debug Write-Message -Message "Getting Long Running Operation result" -Level Debug - + $operationResult = Get-FabricLongRunningOperationResult -operationId $operationId Write-Message -Message "Long Running Operation status: $operationResult" -Level Debug - + return $operationResult - } - else { + } else { Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Debug Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Error return $operationStatus - } + } } default { Write-Message -Message "Unexpected response code: $statusCode" -Level Error @@ -92,10 +91,9 @@ function Add-FabricWorkspaceIdentity { throw "API request failed with status code $statusCode." } } - } - catch { + } catch { # Step 5: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to provision workspace identity. Error: $errorDetails" -Level Error } -} +} \ No newline at end of file diff --git a/FabricTools/public/Workspace/Add-FabricWorkspaceRoleAssignment.ps1 b/source/Public/Workspace/Add-FabricWorkspaceRoleAssignment.ps1 similarity index 97% rename from FabricTools/public/Workspace/Add-FabricWorkspaceRoleAssignment.ps1 rename to source/Public/Workspace/Add-FabricWorkspaceRoleAssignment.ps1 index 4d29053b..82464a4f 100644 --- a/FabricTools/public/Workspace/Add-FabricWorkspaceRoleAssignment.ps1 +++ b/source/Public/Workspace/Add-FabricWorkspaceRoleAssignment.ps1 @@ -26,11 +26,12 @@ Assigns the Admin role to the user with ID "principal123" in the workspace "work - Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. - Calls `Test-TokenExpired` to ensure token validity before making the API request. -Author: Tiago Balabuch +Author: Tiago Balabuch #> function Add-FabricWorkspaceRoleAssignment { [CmdletBinding()] + [OutputType([System.Collections.Hashtable])] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -93,7 +94,7 @@ function Add-FabricWorkspaceRoleAssignment { Write-Message "Error Code: $($response.errorCode)" -Level Error return $null } - + # Step 6: Handle empty response if (-not $response) { Write-Message -Message "No data returned from the API." -Level Warning @@ -102,9 +103,8 @@ function Add-FabricWorkspaceRoleAssignment { Write-Message -Message "Role '$WorkspaceRole' assigned to principal '$PrincipalId' successfully in workspace '$WorkspaceId'." -Level Info return $response - - } - catch { + + } catch { # Step 7: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to assign role. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Workspace/Get-FabricWorkspace.ps1 b/source/Public/Workspace/Get-FabricWorkspace.ps1 similarity index 95% rename from FabricTools/public/Workspace/Get-FabricWorkspace.ps1 rename to source/Public/Workspace/Get-FabricWorkspace.ps1 index 77797006..c64078e2 100644 --- a/FabricTools/public/Workspace/Get-FabricWorkspace.ps1 +++ b/source/Public/Workspace/Get-FabricWorkspace.ps1 @@ -26,7 +26,7 @@ Fetches details of the workspace with the name "MyWorkspace". - Calls `Test-TokenExpired` to ensure token validity before making the API request. - Returns the matching workspace details or all workspaces if no filter is provided. -Author: Tiago Balabuch +Author: Tiago Balabuch #> function Get-FabricWorkspace { @@ -38,7 +38,7 @@ function Get-FabricWorkspace { [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] - [ValidatePattern('^[a-zA-Z0-9_ ]*$')] + [ValidatePattern('^[a-zA-Z0-9_\- ]*$')] [string]$WorkspaceName ) @@ -61,7 +61,7 @@ function Get-FabricWorkspace { if (-not ([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.GetName().Name -eq "System.Web" })) { Add-Type -AssemblyName System.Web } - + # Step 4: Loop to retrieve all capacities with continuation token Write-Message -Message "Loop started to get continuation token" -Level Debug $baseApiEndpointUrl = "{0}/workspaces" -f $FabricConfig.BaseUrl @@ -75,7 +75,7 @@ function Get-FabricWorkspace { $apiEndpointUrl = "{0}?continuationToken={1}" -f $apiEndpointUrl, $encodedToken } Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug - + # Step 6: Make the API request $response = Invoke-RestMethod ` -Headers $FabricConfig.FabricHeaders ` @@ -85,7 +85,7 @@ function Get-FabricWorkspace { -SkipHttpErrorCheck ` -ResponseHeadersVariable "responseHeader" ` -StatusCodeVariable "statusCode" - + # Step 7: Validate the response code if ($statusCode -ne 200) { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error @@ -94,24 +94,22 @@ function Get-FabricWorkspace { Write-Message "Error Code: $($response.errorCode)" -Level Error return $null } - + # Step 8: Add data to the list if ($null -ne $response) { Write-Message -Message "Adding data to the list" -Level Debug $workspaces += $response.value - + # Update the continuation token if present if ($response.PSObject.Properties.Match("continuationToken")) { Write-Message -Message "Updating the continuation token" -Level Debug $continuationToken = $response.continuationToken Write-Message -Message "Continuation token: $continuationToken" -Level Debug - } - else { + } else { Write-Message -Message "Updating the continuation token to null" -Level Debug $continuationToken = $null } - } - else { + } else { Write-Message -Message "No data received from the API." -Level Warning break } @@ -121,29 +119,25 @@ function Get-FabricWorkspace { # Step 8: Filter results based on provided parameters $workspace = if ($WorkspaceId) { $workspaces | Where-Object { $_.Id -eq $WorkspaceId } - } - elseif ($WorkspaceName) { + } elseif ($WorkspaceName) { $workspaces | Where-Object { $_.DisplayName -eq $WorkspaceName } - } - else { + } else { # Return all workspaces if no filter is provided Write-Message -Message "No filter provided. Returning all workspaces." -Level Debug $workspaces } - + # Step 9: Handle results if ($workspace) { Write-Message -Message "Workspace found matching the specified criteria." -Level Debug return $workspace - } - else { + } else { Write-Message -Message "No workspace found matching the provided criteria." -Level Warning return $null } - } - catch { + } catch { # Step 10: Capture and log error details $errorDetails = $_.Exception.Message Write-Message -Message "Failed to retrieve workspace. Error: $errorDetails" -Level Error } -} \ No newline at end of file +} diff --git a/FabricTools/public/Workspace/Get-FabricWorkspaceDatasetRefreshes.ps1 b/source/Public/Workspace/Get-FabricWorkspaceDatasetRefreshes.ps1 similarity index 92% rename from FabricTools/public/Workspace/Get-FabricWorkspaceDatasetRefreshes.ps1 rename to source/Public/Workspace/Get-FabricWorkspaceDatasetRefreshes.ps1 index 81188a66..76c040d8 100644 --- a/FabricTools/public/Workspace/Get-FabricWorkspaceDatasetRefreshes.ps1 +++ b/source/Public/Workspace/Get-FabricWorkspaceDatasetRefreshes.ps1 @@ -1,4 +1,5 @@ -<# +function Get-FabricWorkspaceDatasetRefreshes { + <# .SYNOPSIS Retrieves the refresh history of all datasets in a specified PowerBI workspace. @@ -22,15 +23,14 @@ .NOTES Alias: Get-PowerBIWorkspaceDatasetRefreshes, Get-FabWorkspaceDatasetRefreshes -#> + #> -# Define a function to get the refresh history of all datasets in a PowerBI workspace -function Get-FabricWorkspaceDatasetRefreshes { + # Define a function to get the refresh history of all datasets in a PowerBI workspace # Set aliases for the function [Alias("Get-FabWorkspaceDatasetRefreshes")] param( # Define a mandatory parameter for the workspace ID - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [string]$WorkspaceID ) diff --git a/FabricTools/public/Workspace/Get-FabricWorkspaceRoleAssignment.ps1 b/source/Public/Workspace/Get-FabricWorkspaceRoleAssignment.ps1 similarity index 95% rename from FabricTools/public/Workspace/Get-FabricWorkspaceRoleAssignment.ps1 rename to source/Public/Workspace/Get-FabricWorkspaceRoleAssignment.ps1 index 02b2f585..dd393868 100644 --- a/FabricTools/public/Workspace/Get-FabricWorkspaceRoleAssignment.ps1 +++ b/source/Public/Workspace/Get-FabricWorkspaceRoleAssignment.ps1 @@ -25,11 +25,12 @@ Fetches the role assignment with the ID "role123" for the workspace "workspace12 - Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. - Calls `Test-TokenExpired` to ensure token validity before making the API request. -Author: Tiago Balabuch +Author: Tiago Balabuch #> function Get-FabricWorkspaceRoleAssignment { [CmdletBinding()] + [OutputType([System.Object[]])] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -45,7 +46,7 @@ function Get-FabricWorkspaceRoleAssignment { Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired Write-Message -Message "Token validation completed." -Level Debug - + # Step 3: Initialize variables $continuationToken = $null $workspaceRoles = @() @@ -53,22 +54,22 @@ function Get-FabricWorkspaceRoleAssignment { if (-not ([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.GetName().Name -eq "System.Web" })) { Add-Type -AssemblyName System.Web } - + # Step 4: Loop to retrieve all capacities with continuation token Write-Message -Message "Loop started to get continuation token" -Level Debug $baseApiEndpointUrl = "{0}/workspaces/{1}/roleAssignments" -f $FabricConfig.BaseUrl, $WorkspaceId - + do { # Step 5: Construct the API URL $apiEndpointUrl = $baseApiEndpointUrl - + if ($null -ne $continuationToken) { # URL-encode the continuation token $encodedToken = [System.Web.HttpUtility]::UrlEncode($continuationToken) $apiEndpointUrl = "{0}?continuationToken={1}" -f $apiEndpointUrl, $encodedToken } Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug - + # Step 6: Make the API request $response = Invoke-RestMethod ` -Headers $FabricConfig.FabricHeaders ` @@ -78,7 +79,7 @@ function Get-FabricWorkspaceRoleAssignment { -SkipHttpErrorCheck ` -ResponseHeadersVariable "responseHeader" ` -StatusCodeVariable "statusCode" - + # Step 7: Validate the response code if ($statusCode -ne 200) { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error @@ -87,24 +88,22 @@ function Get-FabricWorkspaceRoleAssignment { Write-Message "Error Code: $($response.errorCode)" -Level Error return $null } - + # Step 8: Add data to the list if ($null -ne $response) { Write-Message -Message "Adding data to the list" -Level Debug $workspaceRoles += $response.value - + # Update the continuation token if present if ($response.PSObject.Properties.Match("continuationToken")) { Write-Message -Message "Updating the continuation token" -Level Debug $continuationToken = $response.continuationToken Write-Message -Message "Continuation token: $continuationToken" -Level Debug - } - else { + } else { Write-Message -Message "Updating the continuation token to null" -Level Debug $continuationToken = $null } - } - else { + } else { Write-Message -Message "No data received from the API." -Level Warning break } @@ -113,8 +112,7 @@ function Get-FabricWorkspaceRoleAssignment { # Step 8: Filter results based on provided parameters $roleAssignments = if ($WorkspaceRoleAssignmentId) { $workspaceRoles | Where-Object { $_.Id -eq $WorkspaceRoleAssignmentId } - } - else { + } else { $workspaceRoles } @@ -134,18 +132,15 @@ function Get-FabricWorkspaceRoleAssignment { } } return $results - } - else { + } else { if ($WorkspaceRoleAssignmentId) { Write-Message -Message "No role assignment found with ID '$WorkspaceRoleAssignmentId' for WorkspaceId '$WorkspaceId'." -Level Warning - } - else { + } else { Write-Message -Message "No role assignments found for WorkspaceId '$WorkspaceId'." -Level Warning } return @() } - } - catch { + } catch { # Step 10: Capture and log error details $errorDetails = $_.Exception.Message Write-Message -Message "Failed to retrieve role assignments for WorkspaceId '$WorkspaceId'. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Workspace/Get-FabricWorkspaceUsageMetricsData.ps1 b/source/Public/Workspace/Get-FabricWorkspaceUsageMetricsData.ps1 similarity index 90% rename from FabricTools/public/Workspace/Get-FabricWorkspaceUsageMetricsData.ps1 rename to source/Public/Workspace/Get-FabricWorkspaceUsageMetricsData.ps1 index f94d101a..422eeee4 100644 --- a/FabricTools/public/Workspace/Get-FabricWorkspaceUsageMetricsData.ps1 +++ b/source/Public/Workspace/Get-FabricWorkspaceUsageMetricsData.ps1 @@ -1,4 +1,5 @@ -<# +function Get-FabricWorkspaceUsageMetricsData { + <# .SYNOPSIS Retrieves workspace usage metrics data. @@ -18,10 +19,9 @@ This example retrieves the workspace usage metrics for a specific workspace give .NOTES The function retrieves the PowerBI access token and creates a new usage metrics report. It then defines the names of the reports to retrieve, initializes an empty hashtable to store the reports, and for each report name, retrieves the report and adds it to the hashtable. It then returns the hashtable of reports. -#> + #> -# This function retrieves workspace usage metrics. -function Get-FabricWorkspaceUsageMetricsData { + # This function retrieves workspace usage metrics. # Define aliases for the function for flexibility. [Alias("Get-FabWorkspaceUsageMetricsData")] @@ -40,18 +40,17 @@ function Get-FabricWorkspaceUsageMetricsData { $reportnames = @("'Workspace views'", "'Report pages'", "Users", "Reports", "'Report views'", "'Report page views'", "'Report load times'") # Initialize an empty hashtable to store the reports. - $reports = @{} + $reports = @{ } # For each report name, retrieve the report and add it to the hashtable. if ($username -eq "") { foreach ($reportname in $reportnames) { - $report = Get-FabricUsagemetricsQuery -DatasetID $datasetId -groupId $workspaceId -reportname $reportname + $report = Get-FabricUsageMetricsQuery -DatasetID $datasetId -groupId $workspaceId -reportname $reportname $reports += @{ $reportname.replace("'", "") = $report } } - } - else { + } else { foreach ($reportname in $reportnames) { - $report = Get-FabricUsagemetricsQuery -DatasetID $datasetId -groupId $workspaceId -reportname $reportname -ImpersonatedUser $username + $report = Get-FabricUsageMetricsQuery -DatasetID $datasetId -groupId $workspaceId -reportname $reportname -ImpersonatedUser $username $reports += @{ $reportname.replace("'", "") = $report } } } diff --git a/FabricTools/public/Workspace/Get-FabricWorkspaceUsers.ps1 b/source/Public/Workspace/Get-FabricWorkspaceUsers.ps1 similarity index 96% rename from FabricTools/public/Workspace/Get-FabricWorkspaceUsers.ps1 rename to source/Public/Workspace/Get-FabricWorkspaceUsers.ps1 index 38426fcd..8f5e90ce 100644 --- a/FabricTools/public/Workspace/Get-FabricWorkspaceUsers.ps1 +++ b/source/Public/Workspace/Get-FabricWorkspaceUsers.ps1 @@ -1,4 +1,5 @@ -<# +function Get-FabricWorkspaceUsers { + <# .SYNOPSIS Retrieves the users of a workspace. @@ -23,10 +24,9 @@ This example retrieves the users of a workspace given a workspace object. .NOTES The function defines parameters for the workspace ID and workspace object. If the parameter set name is 'WorkspaceId', it retrieves the workspace object. It then makes a GET request to the PowerBI API to retrieve the users of the workspace and returns the 'value' property of the response, which contains the users. -#> + #> -# This function retrieves the users of a workspace. -function Get-FabricWorkspaceUsers { + # This function retrieves the users of a workspace. # Define aliases for the function for flexibility. [Alias("Get-FabWorkspaceUsers")] diff --git a/FabricTools/public/Workspace/New-FabricWorkspace.ps1 b/source/Public/Workspace/New-FabricWorkspace.ps1 similarity index 74% rename from FabricTools/public/Workspace/New-FabricWorkspace.ps1 rename to source/Public/Workspace/New-FabricWorkspace.ps1 index 4da24893..a6c020ed 100644 --- a/FabricTools/public/Workspace/New-FabricWorkspace.ps1 +++ b/source/Public/Workspace/New-FabricWorkspace.ps1 @@ -1,4 +1,6 @@ -<# +function New-FabricWorkspace +{ + <# .SYNOPSIS Creates a new Fabric workspace with the specified display name. @@ -8,6 +10,12 @@ The `Add-FabricWorkspace` function creates a new workspace in the Fabric platfor .PARAMETER WorkspaceName The display name of the workspace to be created. Must only contain alphanumeric characters, spaces, and underscores. +.PARAMETER WorkspaceDescription +(Optional) A description for the workspace. This parameter is optional. + +.PARAMETER CapacityId +(Optional) The ID of the capacity to be associated with the workspace. This parameter is optional. + .EXAMPLE Add-FabricWorkspace -WorkspaceName "NewWorkspace" @@ -18,14 +26,12 @@ Creates a workspace named "NewWorkspace". - Calls `Test-TokenExpired` to ensure token validity before making the API request. Author: Tiago Balabuch -#> - -function New-FabricWorkspace { - [CmdletBinding()] + #> + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [ValidatePattern('^[a-zA-Z0-9_ ]*$')] + [ValidatePattern('^[a-zA-Z0-9_\- ]*$')] [string]$WorkspaceName, [Parameter(Mandatory = $false)] @@ -37,7 +43,8 @@ function New-FabricWorkspace { [string]$CapacityId ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -52,11 +59,13 @@ function New-FabricWorkspace { displayName = $WorkspaceName } - if ($WorkspaceDescription) { + if ($WorkspaceDescription) + { $body.description = $WorkspaceDescription } - if ($CapacityId) { + if ($CapacityId) + { $body.capacityId = $CapacityId } @@ -64,59 +73,70 @@ function New-FabricWorkspace { $bodyJson = $body | ConvertTo-Json -Depth 2 Write-Message -Message "Request Body: $bodyJson" -Level Debug - # Step 4: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Post ` - -Body $bodyJson ` - -ContentType "application/json" ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -ResponseHeadersVariable "responseHeader" ` - -StatusCodeVariable "statusCode" + if ($PSCmdlet.ShouldProcess($apiEndpointUrl, "Create Workspace")) + { + + # Step 4: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Post ` + -Body $bodyJson ` + -ContentType "application/json" ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -ResponseHeadersVariable "responseHeader" ` + -StatusCodeVariable "statusCode" + } # Step 5: Handle and log the response - switch ($statusCode) { - 201 { + switch ($statusCode) + { + 201 + { Write-Message -Message "Workspace '$WorkspaceName' created successfully!" -Level Info return $response } - 202 { + 202 + { Write-Message -Message "Workspace '$WorkspaceName' creation accepted. Provisioning in progress!" -Level Info [string]$operationId = $responseHeader["x-ms-operation-id"] Write-Message -Message "Operation ID: '$operationId'" -Level Debug Write-Message -Message "Getting Long Running Operation status" -Level Debug - + $operationStatus = Get-FabricLongRunningOperation -operationId $operationId Write-Message -Message "Long Running Operation status: $operationStatus" -Level Debug # Handle operation result - if ($operationStatus.status -eq "Succeeded") { + if ($operationStatus.status -eq "Succeeded") + { Write-Message -Message "Operation Succeeded" -Level Debug Write-Message -Message "Getting Long Running Operation result" -Level Debug - + $operationResult = Get-FabricLongRunningOperationResult -operationId $operationId Write-Message -Message "Long Running Operation status: $operationResult" -Level Debug - + return $operationResult } - else { + else + { Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Debug Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Error return $operationStatus - } + } } - default { + default + { Write-Message -Message "Unexpected response code: $statusCode" -Level Error Write-Message -Message "Error details: $($response.message)" -Level Error throw "API request failed with status code $statusCode." } } } - catch { + catch + { # Step 6: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to create workspace. Error: $errorDetails" -Level Error - + } } diff --git a/FabricTools/public/Workspace/New-FabricWorkspaceUsageMetricsReport.ps1 b/source/Public/Workspace/New-FabricWorkspaceUsageMetricsReport.ps1 similarity index 93% rename from FabricTools/public/Workspace/New-FabricWorkspaceUsageMetricsReport.ps1 rename to source/Public/Workspace/New-FabricWorkspaceUsageMetricsReport.ps1 index 4f2a0084..893f8387 100644 --- a/FabricTools/public/Workspace/New-FabricWorkspaceUsageMetricsReport.ps1 +++ b/source/Public/Workspace/New-FabricWorkspaceUsageMetricsReport.ps1 @@ -1,4 +1,5 @@ -<# +function New-FabricWorkspaceUsageMetricsReport { + <# .SYNOPSIS Retrieves the workspace usage metrics dataset ID. @@ -15,10 +16,10 @@ This example retrieves the workspace usage metrics dataset ID for a specific wor .NOTES The function retrieves the PowerBI access token and the Fabric API cluster URI. It then makes a GET request to the Fabric API to retrieve the workspace usage metrics dataset ID, parses the response and replaces certain keys to match the expected format, and returns the 'dbName' property of the first model in the response, which is the dataset ID. -#> + #> + + # This function retrieves the workspace usage metrics dataset ID. -# This function retrieves the workspace usage metrics dataset ID. -function New-FabricWorkspaceUsageMetricsReport { # Define aliases for the function for flexibility. [Alias("New-FabWorkspaceUsageMetricsReport")] [CmdletBinding(SupportsShouldProcess)] @@ -31,7 +32,7 @@ function New-FabricWorkspaceUsageMetricsReport { Confirm-FabricAuthToken | Out-Null # Retrieve the Fabric API cluster URI. - $url = get-FabricAPIclusterURI + $url = Get-FabricAPIclusterURI # Make a GET request to the Fabric API to retrieve the workspace usage metrics dataset ID. if ($PSCmdlet.ShouldProcess("Workspace Usage Metrics Report", "Retrieve")) { @@ -41,8 +42,7 @@ function New-FabricWorkspaceUsageMetricsReport { # Return the 'dbName' property of the first model in the response, which is the dataset ID. return $response.models[0].dbName - } - else { + } else { return $null } } \ No newline at end of file diff --git a/FabricTools/public/Workspace/Register-FabricWorkspaceToCapacity.ps1 b/source/Public/Workspace/Register-FabricWorkspaceToCapacity.ps1 similarity index 95% rename from FabricTools/public/Workspace/Register-FabricWorkspaceToCapacity.ps1 rename to source/Public/Workspace/Register-FabricWorkspaceToCapacity.ps1 index e54d8fd4..e7d6d6f1 100644 --- a/FabricTools/public/Workspace/Register-FabricWorkspaceToCapacity.ps1 +++ b/source/Public/Workspace/Register-FabricWorkspaceToCapacity.ps1 @@ -1,4 +1,5 @@ -<# +function Register-FabricWorkspaceToCapacity { + <# .SYNOPSIS Sets a PowerBI workspace to a capacity. @@ -26,12 +27,11 @@ This example Sets the workspace object stored in the $workspace variable to the .NOTES The function makes a POST request to the PowerBI API to Set the workspace to the capacity. The PowerBI access token is retrieved using the Get-PowerBIAccessToken function. -#> + #> -# This function Sets a PowerBI workspace to a capacity. -# It supports multiple aliases for flexibility. -function Register-FabricWorkspaceToCapacity { + # This function Sets a PowerBI workspace to a capacity. + # It supports multiple aliases for flexibility. [Alias("Register-FabWorkspaceToCapacity")] [CmdletBinding(SupportsShouldProcess)] param( @@ -57,8 +57,8 @@ function Register-FabricWorkspaceToCapacity { # The body of the request is created. It contains the capacity ID. $body = @{ capacityId = $CapacityId - } - + } + Confirm-FabricAuthToken | Out-Null # The workspace is Seted to the capacity by making a POST request to the PowerBI API. @@ -67,5 +67,5 @@ function Register-FabricWorkspaceToCapacity { #return (Invoke-FabricAPIRequest -Uri "workspaces/$($workspaceID)/assignToCapacity" -Method POST -Body $body).value return Invoke-WebRequest -Headers $FabricSession.HeaderParams -Method POST -Uri "$($FabricSession.BaseApiUrl)/workspaces/$($workspaceID)/assignToCapacity" -Body $body } - } + } } \ No newline at end of file diff --git a/FabricTools/public/Workspace/Remove-FabricWorkspace.ps1 b/source/Public/Workspace/Remove-FabricWorkspace.ps1 similarity index 75% rename from FabricTools/public/Workspace/Remove-FabricWorkspace.ps1 rename to source/Public/Workspace/Remove-FabricWorkspace.ps1 index d65a2be7..5cbfa4d4 100644 --- a/FabricTools/public/Workspace/Remove-FabricWorkspace.ps1 +++ b/source/Public/Workspace/Remove-FabricWorkspace.ps1 @@ -17,17 +17,18 @@ Deletes the workspace with the ID "workspace123". - Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. - Calls `Test-TokenExpired` to ensure token validity before making the API request. -Author: Tiago Balabuch +Author: Tiago Balabuch #> function Remove-FabricWorkspace { - [CmdletBinding()] + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$WorkspaceId ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -37,18 +38,22 @@ function Remove-FabricWorkspace { $apiEndpointUrl = "{0}/workspaces/{1}" -f $FabricConfig.BaseUrl, $WorkspaceId Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug - # Step 3: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Delete ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -ResponseHeadersVariable "responseHeader" ` - -StatusCodeVariable "statusCode" + if ($PSCmdlet.ShouldProcess($apiEndpointUrl, "Delete Workspace")) + { + # Step 3: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Delete ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -ResponseHeadersVariable "responseHeader" ` + -StatusCodeVariable "statusCode" + } # Step 4: Validate the response code - if ($statusCode -ne 200) { + if ($statusCode -ne 200) + { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error Write-Message -Message "Error: $($response.message)" -Level Error Write-Message "Error Code: $($response.errorCode)" -Level Error @@ -59,7 +64,8 @@ function Remove-FabricWorkspace { return $null } - catch { + catch + { # Step 5: Capture and log error details $errorDetails = $_.Exception.Message Write-Message -Message "Failed to retrieve capacity. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Workspace/Unassign-FabricWorkspaceCapacity.ps1 b/source/Public/Workspace/Remove-FabricWorkspaceCapacityAssignment.ps1 similarity index 61% rename from FabricTools/public/Workspace/Unassign-FabricWorkspaceCapacity.ps1 rename to source/Public/Workspace/Remove-FabricWorkspaceCapacityAssignment.ps1 index 8c8ce8bf..85e89633 100644 --- a/FabricTools/public/Workspace/Unassign-FabricWorkspaceCapacity.ps1 +++ b/source/Public/Workspace/Remove-FabricWorkspaceCapacityAssignment.ps1 @@ -3,13 +3,13 @@ Unassigns a Fabric workspace from its capacity. .DESCRIPTION -The `Unassign-FabricWorkspaceCapacity` function sends a POST request to unassign a workspace from its assigned capacity. +The `Remove-FabricWorkspaceCapacityAssignment` function sends a POST request to unassign a workspace from its assigned capacity. .PARAMETER WorkspaceId The unique identifier of the workspace to be unassigned from its capacity. .EXAMPLE -Unassign-FabricWorkspaceCapacity -WorkspaceId "workspace123" +Remove-FabricWorkspaceCapacityAssignment -WorkspaceId "workspace123" Unassigns the workspace with ID "workspace123" from its capacity. @@ -20,14 +20,17 @@ Unassigns the workspace with ID "workspace123" from its capacity. Author: Tiago Balabuch #> -function Unassign-FabricWorkspaceCapacity { - [CmdletBinding()] +function Remove-FabricWorkspaceCapacityAssignment +{ + [CmdletBinding(SupportsShouldProcess)] + [Alias("Unassign-FabricWorkspaceCapacity")] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$WorkspaceId ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -37,29 +40,32 @@ function Unassign-FabricWorkspaceCapacity { $apiEndpointUrl = "{0}/workspaces/{1}/unassignFromCapacity" -f $FabricConfig.BaseUrl, $WorkspaceId Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Message - # Step 3: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Post ` - -ContentType "application/json" ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -ResponseHeadersVariable "responseHeader" ` - -StatusCodeVariable "statusCode" - - + if ($PSCmdlet.ShouldProcess($apiEndpointUrl, "Unassign Workspace from Capacity")) + { + # Step 3: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Post ` + -ContentType "application/json" ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -ResponseHeadersVariable "responseHeader" ` + -StatusCodeVariable "statusCode" + } # Step 4: Validate the response code - if ($statusCode -ne 202) { + if ($statusCode -ne 202) + { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error Write-Message -Message "Error: $($response.message)" -Level Error Write-Message "Error Code: $($response.errorCode)" -Level Error return $null } - Write-Message -Message "Workspace capacity has been successfully unassigned from workspace '$WorkspaceId'." -Level Info + Write-Message -Message "Workspace capacity has been successfully unassigned from workspace '$WorkspaceId'." -Level Info } - catch { + catch + { # Step 5: Capture and log error details $errorDetails = $_.Exception.Message Write-Message -Message "Failed to unassign workspace from capacity. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Workspace/Remove-FabricWorkspaceIdentity.ps1 b/source/Public/Workspace/Remove-FabricWorkspaceIdentity.ps1 similarity index 77% rename from FabricTools/public/Workspace/Remove-FabricWorkspaceIdentity.ps1 rename to source/Public/Workspace/Remove-FabricWorkspaceIdentity.ps1 index 2a55d5d4..9fe5914c 100644 --- a/FabricTools/public/Workspace/Remove-FabricWorkspaceIdentity.ps1 +++ b/source/Public/Workspace/Remove-FabricWorkspaceIdentity.ps1 @@ -17,18 +17,20 @@ Deprovisions the Managed Identity for the workspace with ID "workspace123". - Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. - Calls `Test-TokenExpired` to ensure token validity before making the API request. -Author: Tiago Balabuch +Author: Tiago Balabuch #> -function Remove-FabricWorkspaceIdentity { - [CmdletBinding()] +function Remove-FabricWorkspaceIdentity +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$WorkspaceId ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -38,55 +40,66 @@ function Remove-FabricWorkspaceIdentity { $apiEndpointUrl = "{0}/workspaces/{1}/deprovisionIdentity" -f $FabricConfig.BaseUrl, $WorkspaceId Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug - # Step 3: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Post ` - -ContentType "application/json" ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -ResponseHeadersVariable "responseHeader" ` - -StatusCodeVariable "statusCode" + if ($PSCmdlet.ShouldProcess($apiEndpointUrl, "Deprovision Identity")) + { + + # Step 3: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Post ` + -ContentType "application/json" ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -ResponseHeadersVariable "responseHeader" ` + -StatusCodeVariable "statusCode" + } # Step 4: Handle and log the response - switch ($statusCode) { - 200 { + switch ($statusCode) + { + 200 + { Write-Message -Message "Workspace identity was successfully deprovisioned for workspace '$WorkspaceId'." -Level Info return $response.value } - 202 { + 202 + { Write-Message -Message "Workspace identity deprovisioning accepted for workspace '$WorkspaceId'. Deprovisioning in progress!" -Level Info [string]$operationId = $responseHeader["x-ms-operation-id"] Write-Message -Message "Operation ID: '$operationId'" -Level Debug Write-Message -Message "Getting Long Running Operation status" -Level Debug - + $operationStatus = Get-FabricLongRunningOperation -operationId $operationId Write-Message -Message "Long Running Operation status: $operationStatus" -Level Debug # Handle operation result - if ($operationStatus.status -eq "Succeeded") { + if ($operationStatus.status -eq "Succeeded") + { Write-Message -Message "Operation Succeeded" -Level Debug Write-Message -Message "Getting Long Running Operation result" -Level Debug - + $operationResult = Get-FabricLongRunningOperationResult -operationId $operationId Write-Message -Message "Long Running Operation status: $operationResult" -Level Debug - + return $operationResult } - else { + else + { Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Debug Write-Message -Message "Operation failed. Status: $($operationStatus)" -Level Error return $operationStatus - } + } } - default { + default + { Write-Message -Message "Unexpected response code: $statusCode" -Level Error Write-Message -Message "Error details: $($response.message)" -Level Error throw "API request failed with status code $statusCode." } } } - catch { + catch + { # Step 5: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to deprovision workspace identity. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Workspace/Remove-FabricWorkspaceRoleAssignment.ps1 b/source/Public/Workspace/Remove-FabricWorkspaceRoleAssignment.ps1 similarity index 76% rename from FabricTools/public/Workspace/Remove-FabricWorkspaceRoleAssignment.ps1 rename to source/Public/Workspace/Remove-FabricWorkspaceRoleAssignment.ps1 index 36be3a18..dd96b1a2 100644 --- a/FabricTools/public/Workspace/Remove-FabricWorkspaceRoleAssignment.ps1 +++ b/source/Public/Workspace/Remove-FabricWorkspaceRoleAssignment.ps1 @@ -20,12 +20,13 @@ Removes the role assignment with the ID "role123" from the workspace "workspace1 - Requires `$FabricConfig` global configuration, including `BaseUrl` and `FabricHeaders`. - Calls `Test-TokenExpired` to ensure token validity before making the API request. -Author: Tiago Balabuch +Author: Tiago Balabuch #> -function Remove-FabricWorkspaceRoleAssignment { - [CmdletBinding()] +function Remove-FabricWorkspaceRoleAssignment +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -36,7 +37,8 @@ function Remove-FabricWorkspaceRoleAssignment { [string]$WorkspaceRoleAssignmentId ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -46,27 +48,31 @@ function Remove-FabricWorkspaceRoleAssignment { $apiEndpointUrl = "{0}/workspaces/{1}/roleAssignments/{2}" -f $FabricConfig.BaseUrl, $WorkspaceId, $WorkspaceRoleAssignmentId Write-Message -Message "API Endpoint: $apiEndpointUrl" -Level Debug - # Step 3: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Delete ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -ResponseHeadersVariable "responseHeader" ` - -StatusCodeVariable "statusCode" - + if ($PSCmdlet.ShouldProcess($apiEndpointUrl, "Delete Role Assignment")) + { + # Step 3: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Delete ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -ResponseHeadersVariable "responseHeader" ` + -StatusCodeVariable "statusCode" + } # Step 4: Validate the response code - if ($statusCode -ne 200) { + if ($statusCode -ne 200) + { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error Write-Message -Message "Error: $($response.message)" -Level Error Write-Message "Error Code: $($response.errorCode)" -Level Error return $null } - + Write-Message -Message "Role assignment '$WorkspaceRoleAssignmentId' successfully removed from workspace '$WorkspaceId'." -Level Info } - catch { + catch + { # Step 5: Capture and log error details $errorDetails = $_.Exception.Message Write-Message -Message "Failed to remove role assignments for WorkspaceId '$WorkspaceId'. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Workspace/Unregister-FabricWorkspaceToCapacity.ps1 b/source/Public/Workspace/Unregister-FabricWorkspaceToCapacity.ps1 similarity index 99% rename from FabricTools/public/Workspace/Unregister-FabricWorkspaceToCapacity.ps1 rename to source/Public/Workspace/Unregister-FabricWorkspaceToCapacity.ps1 index 9b4f0ae4..f9fad83d 100644 --- a/FabricTools/public/Workspace/Unregister-FabricWorkspaceToCapacity.ps1 +++ b/source/Public/Workspace/Unregister-FabricWorkspaceToCapacity.ps1 @@ -40,7 +40,7 @@ function Unregister-FabricWorkspaceToCapacity { [Parameter(Mandatory = $true, ParameterSetName = 'WorkspaceObject', ValueFromPipeline = $true)] $Workspace ) - + begin { Confirm-FabricAuthToken | Out-Null } @@ -49,10 +49,10 @@ function Unregister-FabricWorkspaceToCapacity { if ($PSCmdlet.ParameterSetName -eq 'WorkspaceObject') { $workspaceid = $workspace.id } - + if ($PSCmdlet.ShouldProcess("Unassigns workspace $workspaceid from a capacity")) { return Invoke-WebRequest -Headers $FabricSession.HeaderParams -Method POST -Uri "$($FabricSession.BaseApiUrl)/workspaces/$($workspaceID)/unassignFromCapacity" #return (Invoke-FabricAPIRequest -Uri "workspaces/$workspaceid/unassignFromCapacity" -Method POST).value } - } + } } \ No newline at end of file diff --git a/FabricTools/public/Workspace/Update-FabricWorkspace.ps1 b/source/Public/Workspace/Update-FabricWorkspace.ps1 similarity index 78% rename from FabricTools/public/Workspace/Update-FabricWorkspace.ps1 rename to source/Public/Workspace/Update-FabricWorkspace.ps1 index 77b1056a..3dae3ed0 100644 --- a/FabricTools/public/Workspace/Update-FabricWorkspace.ps1 +++ b/source/Public/Workspace/Update-FabricWorkspace.ps1 @@ -31,8 +31,9 @@ Updates both the name and description of the workspace "workspace123". Author: Tiago Balabuch #> -function Update-FabricWorkspace { - [CmdletBinding()] +function Update-FabricWorkspace +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -48,7 +49,8 @@ function Update-FabricWorkspace { [string]$WorkspaceDescription ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -63,7 +65,8 @@ function Update-FabricWorkspace { displayName = $WorkspaceName } - if ($WorkspaceDescription) { + if ($WorkspaceDescription) + { $body.description = $WorkspaceDescription } @@ -71,20 +74,24 @@ function Update-FabricWorkspace { $bodyJson = $body | ConvertTo-Json Write-Message -Message "Request Body: $bodyJson" -Level Debug - # Step 4: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Patch ` - -Body $bodyJson ` - -ContentType "application/json" ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -ResponseHeadersVariable "responseHeader" ` - -StatusCodeVariable "statusCode" - + if ($PSCmdlet.ShouldProcess($apiEndpointUrl, "Update Workspace")) + { + + # Step 4: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Patch ` + -Body $bodyJson ` + -ContentType "application/json" ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -ResponseHeadersVariable "responseHeader" ` + -StatusCodeVariable "statusCode" + } # Step 5: Validate the response code - if ($statusCode -ne 200) { + if ($statusCode -ne 200) + { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error Write-Message -Message "Error: $($response.message)" -Level Error Write-Message "Error Code: $($response.errorCode)" -Level Error @@ -95,7 +102,8 @@ function Update-FabricWorkspace { Write-Message -Message "Workspace '$WorkspaceName' updated successfully!" -Level Info return $response } - catch { + catch + { # Step 7: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to update workspace. Error: $errorDetails" -Level Error diff --git a/FabricTools/public/Workspace/Update-FabricWorkspaceRoleAssignment.ps1 b/source/Public/Workspace/Update-FabricWorkspaceRoleAssignment.ps1 similarity index 79% rename from FabricTools/public/Workspace/Update-FabricWorkspaceRoleAssignment.ps1 rename to source/Public/Workspace/Update-FabricWorkspaceRoleAssignment.ps1 index f619e461..3c2994fa 100644 --- a/FabricTools/public/Workspace/Update-FabricWorkspaceRoleAssignment.ps1 +++ b/source/Public/Workspace/Update-FabricWorkspaceRoleAssignment.ps1 @@ -30,8 +30,9 @@ Updates the role assignment to "Admin" for the specified workspace and role assi Author: Tiago Balabuch #> -function Update-FabricWorkspaceRoleAssignment { - [CmdletBinding()] +function Update-FabricWorkspaceRoleAssignment +{ + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -47,7 +48,8 @@ function Update-FabricWorkspaceRoleAssignment { [string]$WorkspaceRole ) - try { + try + { # Step 1: Ensure token validity Write-Message -Message "Validating token..." -Level Debug Test-TokenExpired @@ -66,37 +68,42 @@ function Update-FabricWorkspaceRoleAssignment { $bodyJson = $body | ConvertTo-Json -Depth 4 -Compress Write-Message -Message "Request Body: $bodyJson" -Level Debug - # Step 4: Make the API request - $response = Invoke-RestMethod ` - -Headers $FabricConfig.FabricHeaders ` - -Uri $apiEndpointUrl ` - -Method Patch ` - -Body $bodyJson ` - -ContentType "application/json" ` - -ErrorAction Stop ` - -SkipHttpErrorCheck ` - -ResponseHeadersVariable "responseHeader" ` - -StatusCodeVariable "statusCode" - + if ($PSCmdlet.ShouldProcess($apiEndpointUrl, "Update Role Assignment")) + { + # Step 4: Make the API request + $response = Invoke-RestMethod ` + -Headers $FabricConfig.FabricHeaders ` + -Uri $apiEndpointUrl ` + -Method Patch ` + -Body $bodyJson ` + -ContentType "application/json" ` + -ErrorAction Stop ` + -SkipHttpErrorCheck ` + -ResponseHeadersVariable "responseHeader" ` + -StatusCodeVariable "statusCode" + } # Step 5: Validate the response code - if ($statusCode -ne 200) { + if ($statusCode -ne 200) + { Write-Message -Message "Unexpected response code: $statusCode from the API." -Level Error Write-Message -Message "Error: $($response.message)" -Level Error Write-Message "Error Code: $($response.errorCode)" -Level Error return $null } - + # Step 6: Handle empty response - if (-not $response) { + if (-not $response) + { Write-Message -Message "No data returned from the API." -Level Warning return $null } - + Write-Message -Message "Role assignment $WorkspaceRoleAssignmentId updated successfully in workspace '$WorkspaceId'." -Level Info return $response - + } - catch { + catch + { # Step 7: Handle and log errors $errorDetails = $_.Exception.Message Write-Message -Message "Failed to update role assignment. Error: $errorDetails" -Level Error diff --git a/source/en-US/about_FabricTools.help.txt b/source/en-US/about_FabricTools.help.txt new file mode 100644 index 00000000..057961b8 --- /dev/null +++ b/source/en-US/about_FabricTools.help.txt @@ -0,0 +1,24 @@ +TOPIC + about_FabricTools + +SHORT DESCRIPTION + A module to be able to do more with Microsoft Fabric. + +LONG DESCRIPTION + A module to be able to do more with Microsoft Fabric. + +EXAMPLES + PS C:\> {{ add examples here }} + +NOTE: + Thank you to all those who contributed to this module, by writing code, sharing opinions, and provided feedback. + +TROUBLESHOOTING NOTE: + Look out on the Github repository for issues and new releases. + +SEE ALSO + - {{ Please add Project URI such as github }}} + +KEYWORDS + {{ Add comma separated keywords here }} + diff --git a/tests/QA/module.tests.ps1 b/tests/QA/module.tests.ps1 new file mode 100644 index 00000000..e1f3847c --- /dev/null +++ b/tests/QA/module.tests.ps1 @@ -0,0 +1,229 @@ +BeforeDiscovery { + $projectPath = "$($PSScriptRoot)\..\.." | Convert-Path + + <# + If the QA tests are run outside of the build script (e.g with Invoke-Pester) + the parent scope has not set the variable $ProjectName. + #> + if (-not $ProjectName) + { + # Assuming project folder name is project name. + $ProjectName = Get-SamplerProjectName -BuildRoot $projectPath + } + + $script:moduleName = $ProjectName + + Remove-Module -Name $script:moduleName -Force -ErrorAction SilentlyContinue + + $mut = Get-Module -Name $script:moduleName -ListAvailable | + Select-Object -First 1 | + Import-Module -Force -ErrorAction Stop -PassThru +} + +BeforeAll { + # Convert-Path required for PS7 or Join-Path fails + $projectPath = "$($PSScriptRoot)\..\.." | Convert-Path + + <# + If the QA tests are run outside of the build script (e.g with Invoke-Pester) + the parent scope has not set the variable $ProjectName. + #> + if (-not $ProjectName) + { + # Assuming project folder name is project name. + $ProjectName = Get-SamplerProjectName -BuildRoot $projectPath + } + + $script:moduleName = $ProjectName + + $sourcePath = ( + Get-ChildItem -Path $projectPath\*\*.psd1 | + Where-Object -FilterScript { + ($_.Directory.Name -match 'source|src' -or $_.Directory.Name -eq $_.BaseName) ` + -and $( + try + { + Test-ModuleManifest -Path $_.FullName -ErrorAction Stop + } + catch + { + $false + } + ) + } + ).Directory.FullName +} + +Describe 'Changelog Management' -Tag 'Changelog' { + + It 'Changelog format compliant with keepachangelog format' -Skip:(![bool](Get-Command git -EA SilentlyContinue)) { + { Get-ChangelogData -Path (Join-Path $ProjectPath 'CHANGELOG.md') -ErrorAction Stop } | Should -Not -Throw + } + + It 'Changelog should have an Unreleased header' -Skip:$skipTest { + (Get-ChangelogData -Path (Join-Path -Path $ProjectPath -ChildPath 'CHANGELOG.md') -ErrorAction Stop).Unreleased | Should -Not -BeNullOrEmpty + } +} + +Describe 'General module control' -Tags 'FunctionalQuality' { + It 'Should import without errors' { + { Import-Module -Name $script:moduleName -Force -ErrorAction Stop } | Should -Not -Throw + + Get-Module -Name $script:moduleName | Should -Not -BeNullOrEmpty + } + + It 'Should remove without error' { + { Remove-Module -Name $script:moduleName -ErrorAction Stop } | Should -Not -Throw + + Get-Module $script:moduleName | Should -BeNullOrEmpty + } +} + +BeforeDiscovery { + # Must use the imported module to build test cases. + $allModuleFunctions = & $mut { Get-Command -Module $args[0] -CommandType Function } $script:moduleName + + # Build test cases. + $testCases = @() + + foreach ($function in $allModuleFunctions | Where-Object -FilterScript { + $_.Name -notin ( + 'Test-TokenExpired', + 'Get-FabricUri', + 'Get-FileDefinitionParts', + 'Set-FabConfig', + 'Write-Message', + 'Invoke-FabricAPIRequest_duplicate' + ) + }) + { + $testCases += @{ + Name = $function.Name + } + } + { + $testCases += @{ + Name = $function.Name + } + } +} + +Describe 'Quality for module' -Tags 'TestQuality' { + BeforeDiscovery { + if (Get-Command -Name Invoke-ScriptAnalyzer -ErrorAction SilentlyContinue) + { + $scriptAnalyzerRules = Get-ScriptAnalyzerRule + } + else + { + if ($ErrorActionPreference -ne 'Stop') + { + Write-Warning -Message 'ScriptAnalyzer not found!' + } + else + { + throw 'ScriptAnalyzer not found!' + } + } + } + + It 'Should have a unit test for ' -ForEach $testCases { + Get-ChildItem -Path 'tests\' -Recurse -Include "$Name.Tests.ps1" | Should -Not -BeNullOrEmpty + } + + It 'Should pass Script Analyzer for ' -ForEach ($testCases | Where-Object { $_.Name -in $mut.ExportedCommands.Values.Name }) -Skip:(-not $scriptAnalyzerRules) { + $functionFile = Get-ChildItem -Path $sourcePath -Recurse -Include "$Name.ps1" + + $pssaResult = (Invoke-ScriptAnalyzer -Path $functionFile.FullName) + $report = $pssaResult | Format-Table -AutoSize | Out-String -Width 110 + $pssaResult | Should -BeNullOrEmpty -Because ` + "some rule triggered.`r`n`r`n $report" + } +} + +Describe 'Help for module' -Tags 'helpQuality' { + It 'Should have .SYNOPSIS for ' -ForEach ($testCases | Where-Object { $_.Name -in $mut.ExportedCommands.Values.Name }) { + $functionFile = Get-ChildItem -Path $sourcePath -Recurse -Include "$Name.ps1" + + $scriptFileRawContent = Get-Content -Raw -Path $functionFile.FullName + + $abstractSyntaxTree = [System.Management.Automation.Language.Parser]::ParseInput($scriptFileRawContent, [ref] $null, [ref] $null) + + $astSearchDelegate = { $args[0] -is [System.Management.Automation.Language.FunctionDefinitionAst] } + + $parsedFunction = $abstractSyntaxTree.FindAll( $astSearchDelegate, $true ) | + Where-Object -FilterScript { + $_.Name -eq $Name + } + + $functionHelp = $parsedFunction.GetHelpContent() + + $functionHelp.Synopsis | Should -Not -BeNullOrEmpty + } + + It 'Should have a .DESCRIPTION with length greater than 40 characters for ' -ForEach ($testCases | Where-Object { $_.Name -in $mut.ExportedCommands.Values.Name }) { + $functionFile = Get-ChildItem -Path $sourcePath -Recurse -Include "$Name.ps1" + + $scriptFileRawContent = Get-Content -Raw -Path $functionFile.FullName + + $abstractSyntaxTree = [System.Management.Automation.Language.Parser]::ParseInput($scriptFileRawContent, [ref] $null, [ref] $null) + + $astSearchDelegate = { $args[0] -is [System.Management.Automation.Language.FunctionDefinitionAst] } + + $parsedFunction = $abstractSyntaxTree.FindAll($astSearchDelegate, $true) | + Where-Object -FilterScript { + $_.Name -eq $Name + } + + $functionHelp = $parsedFunction.GetHelpContent() + + $functionHelp.Description.Length | Should -BeGreaterThan 40 + } + + It 'Should have at least one (1) example for ' -ForEach ($testCases | Where-Object { $_.Name -in $mut.ExportedCommands.Values.Name }) { + $functionFile = Get-ChildItem -Path $sourcePath -Recurse -Include "$Name.ps1" + + $scriptFileRawContent = Get-Content -Raw -Path $functionFile.FullName + + $abstractSyntaxTree = [System.Management.Automation.Language.Parser]::ParseInput($scriptFileRawContent, [ref] $null, [ref] $null) + + $astSearchDelegate = { $args[0] -is [System.Management.Automation.Language.FunctionDefinitionAst] } + + $parsedFunction = $abstractSyntaxTree.FindAll( $astSearchDelegate, $true ) | + Where-Object -FilterScript { + $_.Name -eq $Name + } + + $functionHelp = $parsedFunction.GetHelpContent() + + $functionHelp.Examples.Count | Should -BeGreaterThan 0 + $functionHelp.Examples[0] | Should -Match ([regex]::Escape($function.Name)) + $functionHelp.Examples[0].Length | Should -BeGreaterThan ($function.Name.Length + 10) + + } + + It 'Should have described all parameters for ' -ForEach ($testCases | Where-Object { $_.Name -in $mut.ExportedCommands.Values.Name }) { + $functionFile = Get-ChildItem -Path $sourcePath -Recurse -Include "$Name.ps1" + + $scriptFileRawContent = Get-Content -Raw -Path $functionFile.FullName + + $abstractSyntaxTree = [System.Management.Automation.Language.Parser]::ParseInput($scriptFileRawContent, [ref] $null, [ref] $null) + + $astSearchDelegate = { $args[0] -is [System.Management.Automation.Language.FunctionDefinitionAst] } + + $parsedFunction = $abstractSyntaxTree.FindAll( $astSearchDelegate, $true ) | + Where-Object -FilterScript { + $_.Name -eq $Name + } + + $functionHelp = $parsedFunction.GetHelpContent() + + $parameters = $parsedFunction.Body.ParamBlock.Parameters.Name.VariablePath.ForEach({ $_.ToString() }) + + foreach ($parameter in $parameters) + { + $functionHelp.Parameters.($parameter.ToUpper()) | Should -Not -BeNullOrEmpty -Because ('the parameter {0} must have a description' -f $parameter) + $functionHelp.Parameters.($parameter.ToUpper()).Length | Should -BeGreaterThan 25 -Because ('the parameter {0} must have descriptive description' -f $parameter) + } + } +} diff --git a/tests/Unit/Add-FabricDomainWorkspaceAssignmentByCapacity.Tests.ps1 b/tests/Unit/Add-FabricDomainWorkspaceAssignmentByCapacity.Tests.ps1 new file mode 100644 index 00000000..c630e23c --- /dev/null +++ b/tests/Unit/Add-FabricDomainWorkspaceAssignmentByCapacity.Tests.ps1 @@ -0,0 +1,46 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "DomainId" + "CapacitiesIds" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Add-FabricDomainWorkspaceAssignmentByCapacity" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Add-FabricDomainWorkspaceAssignmentByCapacity + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Add-FabricDomainWorkspaceAssignmentByCapacity + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Add-FabricDomainWorkspaceAssignmentById.Tests.ps1 b/tests/Unit/Add-FabricDomainWorkspaceAssignmentById.Tests.ps1 new file mode 100644 index 00000000..519c954f --- /dev/null +++ b/tests/Unit/Add-FabricDomainWorkspaceAssignmentById.Tests.ps1 @@ -0,0 +1,46 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "DomainId" + "WorkspaceIds" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Add-FabricDomainWorkspaceAssignmentById" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Add-FabricDomainWorkspaceAssignmentById + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Add-FabricDomainWorkspaceAssignmentById + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Add-FabricDomainWorkspaceAssignmentByPrincipal.Tests.ps1 b/tests/Unit/Add-FabricDomainWorkspaceAssignmentByPrincipal.Tests.ps1 new file mode 100644 index 00000000..93c481c9 --- /dev/null +++ b/tests/Unit/Add-FabricDomainWorkspaceAssignmentByPrincipal.Tests.ps1 @@ -0,0 +1,46 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "DomainId" + "PrincipalIds" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Add-FabricDomainWorkspaceAssignmentByPrincipal" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Add-FabricDomainWorkspaceAssignmentByPrincipal + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Add-FabricDomainWorkspaceAssignmentByPrincipal + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Add-FabricDomainWorkspaceRoleAssignment.Tests.ps1 b/tests/Unit/Add-FabricDomainWorkspaceRoleAssignment.Tests.ps1 new file mode 100644 index 00000000..1ff6107a --- /dev/null +++ b/tests/Unit/Add-FabricDomainWorkspaceRoleAssignment.Tests.ps1 @@ -0,0 +1,47 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "DomainId" + "DomainRole" + "PrincipalIds" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Assign-FabricDomainWorkspaceRoleAssignment" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Assign-FabricDomainWorkspaceRoleAssignment + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Assign-FabricDomainWorkspaceRoleAssignment + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Add-FabricWorkspaceCapacityAssignment.Tests.ps1 b/tests/Unit/Add-FabricWorkspaceCapacityAssignment.Tests.ps1 new file mode 100644 index 00000000..1780283a --- /dev/null +++ b/tests/Unit/Add-FabricWorkspaceCapacityAssignment.Tests.ps1 @@ -0,0 +1,46 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "CapacityId" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Add-FabricWorkspaceCapacityAssignment" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Add-FabricWorkspaceCapacityAssignment + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Add-FabricWorkspaceCapacityAssignment + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Add-FabricWorkspaceIdentity.Tests.ps1 b/tests/Unit/Add-FabricWorkspaceIdentity.Tests.ps1 new file mode 100644 index 00000000..2563787f --- /dev/null +++ b/tests/Unit/Add-FabricWorkspaceIdentity.Tests.ps1 @@ -0,0 +1,46 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Add-FabricWorkspaceIdentity" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Add-FabricWorkspaceIdentity + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Add-FabricWorkspaceIdentity + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Add-FabricWorkspaceRoleAssignment.Tests.ps1 b/tests/Unit/Add-FabricWorkspaceRoleAssignment.Tests.ps1 new file mode 100644 index 00000000..bd4a541b --- /dev/null +++ b/tests/Unit/Add-FabricWorkspaceRoleAssignment.Tests.ps1 @@ -0,0 +1,49 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "PrincipalId" + "PrincipalType" + "WorkspaceRole" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Add-FabricWorkspaceRoleAssignment" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Add-FabricWorkspaceRoleAssignment + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Add-FabricWorkspaceRoleAssignment + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Confirm-FabricAuthToken.Tests.ps1 b/tests/Unit/Confirm-FabricAuthToken.Tests.ps1 new file mode 100644 index 00000000..ac94caf3 --- /dev/null +++ b/tests/Unit/Confirm-FabricAuthToken.Tests.ps1 @@ -0,0 +1,45 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Confirm-FabricAuthToken" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Confirm-FabricAuthToken + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Confirm-FabricAuthToken + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Connect-FabricAccount.Tests.ps1 b/tests/Unit/Connect-FabricAccount.Tests.ps1 new file mode 100644 index 00000000..8167126a --- /dev/null +++ b/tests/Unit/Connect-FabricAccount.Tests.ps1 @@ -0,0 +1,46 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "TenantId" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Connect-FabricAccount" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Connect-FabricAccount + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Connect-FabricAccount + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Convert-FromBase64.Tests.ps1 b/tests/Unit/Convert-FromBase64.Tests.ps1 new file mode 100644 index 00000000..adf4f0d1 --- /dev/null +++ b/tests/Unit/Convert-FromBase64.Tests.ps1 @@ -0,0 +1,46 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "Base64String" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Convert-FromBase64" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Convert-FromBase64 + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Convert-FromBase64 + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Convert-ToBase64.Tests.ps1 b/tests/Unit/Convert-ToBase64.Tests.ps1 new file mode 100644 index 00000000..3180dece --- /dev/null +++ b/tests/Unit/Convert-ToBase64.Tests.ps1 @@ -0,0 +1,46 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "filePath" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Convert-ToBase64" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Convert-ToBase64 + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Convert-ToBase64 + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Export-FabricItem.Tests.ps1 b/tests/Unit/Export-FabricItem.Tests.ps1 new file mode 100644 index 00000000..85b76288 --- /dev/null +++ b/tests/Unit/Export-FabricItem.Tests.ps1 @@ -0,0 +1,49 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "path" + "workspaceId" + "filter" + "itemID" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Export-FabricItem" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Export-FabricItem + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Export-FabricItem + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricAPIclusterURI.Tests.ps1 b/tests/Unit/Get-FabricAPIclusterURI.Tests.ps1 new file mode 100644 index 00000000..adcdcfa1 --- /dev/null +++ b/tests/Unit/Get-FabricAPIclusterURI.Tests.ps1 @@ -0,0 +1,31 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + ) +) + +Describe "Get-FabricAPIclusterURI" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricAPIclusterURI + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricAPIclusterURI + $expected = $expectedParams + } + + # It "Has parameter: <_>" -ForEach $expected { + # $command | Should -HaveParameter $PSItem + # } +# + # It "Should have exactly the number of expected parameters $($expected.Count)# " { + # $hasparms = $command.Parameters.Values.Name + # #$hasparms.Count | Should -BeExactly $expected.Count + # Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | # Should -BeNullOrEmpty + # } + } +} diff --git a/tests/Unit/Get-FabricAuthToken.Tests.ps1 b/tests/Unit/Get-FabricAuthToken.Tests.ps1 new file mode 100644 index 00000000..5aeecc03 --- /dev/null +++ b/tests/Unit/Get-FabricAuthToken.Tests.ps1 @@ -0,0 +1,45 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricAuthToken" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricAuthToken + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricAuthToken + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricCapacities.Tests.ps1 b/tests/Unit/Get-FabricCapacities.Tests.ps1 new file mode 100644 index 00000000..4e4334d9 --- /dev/null +++ b/tests/Unit/Get-FabricCapacities.Tests.ps1 @@ -0,0 +1,46 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "subscriptionID" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricCapacities" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricCapacities + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricCapacities + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricCapacity.Tests.ps1 b/tests/Unit/Get-FabricCapacity.Tests.ps1 new file mode 100644 index 00000000..668ec459 --- /dev/null +++ b/tests/Unit/Get-FabricCapacity.Tests.ps1 @@ -0,0 +1,47 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "capacityId" + "capacityName" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricCapacity" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricCapacity + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricCapacity + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricCapacityRefreshables.Tests.ps1 b/tests/Unit/Get-FabricCapacityRefreshables.Tests.ps1 new file mode 100644 index 00000000..9e045f74 --- /dev/null +++ b/tests/Unit/Get-FabricCapacityRefreshables.Tests.ps1 @@ -0,0 +1,46 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "top" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricCapacityRefreshables" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricCapacityRefreshables + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricCapacityRefreshables + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricCapacitySkus.Tests.ps1 b/tests/Unit/Get-FabricCapacitySkus.Tests.ps1 new file mode 100644 index 00000000..8547af11 --- /dev/null +++ b/tests/Unit/Get-FabricCapacitySkus.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "subscriptionID" + "ResourceGroupName" + "capacity" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricCapacitySkus" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricCapacitySkus + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricCapacitySkus + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricCapacityState.Tests.ps1 b/tests/Unit/Get-FabricCapacityState.Tests.ps1 new file mode 100644 index 00000000..a6880ca6 --- /dev/null +++ b/tests/Unit/Get-FabricCapacityState.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "subscriptionID" + "resourcegroup" + "capacity" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricCapacityState" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricCapacityState + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricCapacityState + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricCapacityTenantOverrides.Tests.ps1 b/tests/Unit/Get-FabricCapacityTenantOverrides.Tests.ps1 new file mode 100644 index 00000000..5b0bd23d --- /dev/null +++ b/tests/Unit/Get-FabricCapacityTenantOverrides.Tests.ps1 @@ -0,0 +1,33 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + + + ) +) + +Describe "Get-FabricCapacityTenantOverrides" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricCapacityTenantOverrides + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricCapacityTenantOverrides + $expected = $expectedParams + } + + # It "Has parameter: <_>" -ForEach $expected { + # $command | Should -HaveParameter $PSItem + # } +# + # It "Should have exactly the number of expected parameters $($expected.Count)#" { + # $hasparms = $command.Parameters.Values.Name + # #$hasparms.Count | Should -BeExactly $expected.Count + # Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | #Should -BeNullOrEmpty + # } + } +} diff --git a/tests/Unit/Get-FabricCapacityTenantSettingOverrides.Tests.ps1 b/tests/Unit/Get-FabricCapacityTenantSettingOverrides.Tests.ps1 new file mode 100644 index 00000000..b2d2b2c2 --- /dev/null +++ b/tests/Unit/Get-FabricCapacityTenantSettingOverrides.Tests.ps1 @@ -0,0 +1,46 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "capacityId" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricCapacityTenantSettingOverrides" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricCapacityTenantSettingOverrides + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricCapacityTenantSettingOverrides + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricCapacityWorkload.Tests.ps1 b/tests/Unit/Get-FabricCapacityWorkload.Tests.ps1 new file mode 100644 index 00000000..6f612312 --- /dev/null +++ b/tests/Unit/Get-FabricCapacityWorkload.Tests.ps1 @@ -0,0 +1,46 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "capacityID" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricCapacityWorkload" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricCapacityWorkload + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricCapacityWorkload + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricConfig.tests.ps1 b/tests/Unit/Get-FabricConfig.tests.ps1 new file mode 100644 index 00000000..6b2025d9 --- /dev/null +++ b/tests/Unit/Get-FabricConfig.tests.ps1 @@ -0,0 +1,10 @@ +Describe "Get-FabricConfig Unit Tests" -Tag 'UnitTests' { + Context "Validate parameters" { + It "Should only contain our specific parameters" { + $CommandName = 'Get-FabricConfig' + [array]$params = ([Management.Automation.CommandMetaData]$ExecutionContext.SessionState.InvokeCommand.GetCommand($CommandName, 'Function')).Parameters.Keys + [object[]]$knownParameters = 'ConfigName' + Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Get-FabricConnection.Tests.ps1 b/tests/Unit/Get-FabricConnection.Tests.ps1 new file mode 100644 index 00000000..e6ed6ba4 --- /dev/null +++ b/tests/Unit/Get-FabricConnection.Tests.ps1 @@ -0,0 +1,46 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "connectionId" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricConnection" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricConnection + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricConnection + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricCopyJob.Tests.ps1 b/tests/Unit/Get-FabricCopyJob.Tests.ps1 new file mode 100644 index 00000000..0e49492a --- /dev/null +++ b/tests/Unit/Get-FabricCopyJob.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "CopyJobId" + "CopyJob" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricCopyJob" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricCopyJob + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricCopyJob + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricCopyJobDefinition.Tests.ps1 b/tests/Unit/Get-FabricCopyJobDefinition.Tests.ps1 new file mode 100644 index 00000000..fcdd430e --- /dev/null +++ b/tests/Unit/Get-FabricCopyJobDefinition.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "CopyJobId" + "CopyJobFormat" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricCopyJobDefinition" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricCopyJobDefinition + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricCopyJobDefinition + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricDashboard.Tests.ps1 b/tests/Unit/Get-FabricDashboard.Tests.ps1 new file mode 100644 index 00000000..e6062a4c --- /dev/null +++ b/tests/Unit/Get-FabricDashboard.Tests.ps1 @@ -0,0 +1,46 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricDashboard" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricDashboard + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricDashboard + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricDataPipeline.Tests.ps1 b/tests/Unit/Get-FabricDataPipeline.Tests.ps1 new file mode 100644 index 00000000..e2475f30 --- /dev/null +++ b/tests/Unit/Get-FabricDataPipeline.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "DataPipelineId" + "DataPipelineName" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricDataPipeline" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricDataPipeline + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricDataPipeline + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricDatamart.Tests.ps1 b/tests/Unit/Get-FabricDatamart.Tests.ps1 new file mode 100644 index 00000000..e2192416 --- /dev/null +++ b/tests/Unit/Get-FabricDatamart.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "datamartId" + "datamartName" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricDatamart" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricDatamart + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricDatamart + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricDatasetRefreshes.Tests.ps1 b/tests/Unit/Get-FabricDatasetRefreshes.Tests.ps1 new file mode 100644 index 00000000..9f94eb23 --- /dev/null +++ b/tests/Unit/Get-FabricDatasetRefreshes.Tests.ps1 @@ -0,0 +1,47 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "DatasetID" + "workspaceId" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricDatasetRefreshes" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricDatasetRefreshes + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricDatasetRefreshes + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricDebugInfo.Tests.ps1 b/tests/Unit/Get-FabricDebugInfo.Tests.ps1 new file mode 100644 index 00000000..4894a880 --- /dev/null +++ b/tests/Unit/Get-FabricDebugInfo.Tests.ps1 @@ -0,0 +1,45 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricDebugInfo" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricDebugInfo + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricDebugInfo + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricDomain.Tests.ps1 b/tests/Unit/Get-FabricDomain.Tests.ps1 new file mode 100644 index 00000000..24fce27b --- /dev/null +++ b/tests/Unit/Get-FabricDomain.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "DomainId" + "DomainName" + "NonEmptyDomainsOnly" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricDomain" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricDomain + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricDomain + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricDomainTenantSettingOverrides.Tests.ps1 b/tests/Unit/Get-FabricDomainTenantSettingOverrides.Tests.ps1 new file mode 100644 index 00000000..916f1dd8 --- /dev/null +++ b/tests/Unit/Get-FabricDomainTenantSettingOverrides.Tests.ps1 @@ -0,0 +1,45 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricDomainTenantSettingOverrides" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricDomainTenantSettingOverrides + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricDomainTenantSettingOverrides + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricDomainWorkspace.Tests.ps1 b/tests/Unit/Get-FabricDomainWorkspace.Tests.ps1 new file mode 100644 index 00000000..646c0a4c --- /dev/null +++ b/tests/Unit/Get-FabricDomainWorkspace.Tests.ps1 @@ -0,0 +1,46 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "DomainId" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricDomainWorkspace" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricDomainWorkspace + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricDomainWorkspace + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricEnvironment.Tests.ps1 b/tests/Unit/Get-FabricEnvironment.Tests.ps1 new file mode 100644 index 00000000..c43f2f95 --- /dev/null +++ b/tests/Unit/Get-FabricEnvironment.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "EnvironmentId" + "EnvironmentName" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricEnvironment" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricEnvironment + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricEnvironment + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricEnvironmentLibrary.Tests.ps1 b/tests/Unit/Get-FabricEnvironmentLibrary.Tests.ps1 new file mode 100644 index 00000000..e31bb133 --- /dev/null +++ b/tests/Unit/Get-FabricEnvironmentLibrary.Tests.ps1 @@ -0,0 +1,47 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "EnvironmentId" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricEnvironmentLibrary" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricEnvironmentLibrary + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricEnvironmentLibrary + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricEnvironmentSparkCompute.Tests.ps1 b/tests/Unit/Get-FabricEnvironmentSparkCompute.Tests.ps1 new file mode 100644 index 00000000..ae3e24a5 --- /dev/null +++ b/tests/Unit/Get-FabricEnvironmentSparkCompute.Tests.ps1 @@ -0,0 +1,47 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "EnvironmentId" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricEnvironmentSparkCompute" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricEnvironmentSparkCompute + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricEnvironmentSparkCompute + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricEnvironmentStagingLibrary.Tests.ps1 b/tests/Unit/Get-FabricEnvironmentStagingLibrary.Tests.ps1 new file mode 100644 index 00000000..601f5485 --- /dev/null +++ b/tests/Unit/Get-FabricEnvironmentStagingLibrary.Tests.ps1 @@ -0,0 +1,47 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "EnvironmentId" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricEnvironmentStagingLibrary" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricEnvironmentStagingLibrary + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricEnvironmentStagingLibrary + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricEnvironmentStagingSparkCompute.Tests.ps1 b/tests/Unit/Get-FabricEnvironmentStagingSparkCompute.Tests.ps1 new file mode 100644 index 00000000..43d27225 --- /dev/null +++ b/tests/Unit/Get-FabricEnvironmentStagingSparkCompute.Tests.ps1 @@ -0,0 +1,47 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "EnvironmentId" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricEnvironmentStagingSparkCompute" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricEnvironmentStagingSparkCompute + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricEnvironmentStagingSparkCompute + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricEventhouse.Tests.ps1 b/tests/Unit/Get-FabricEventhouse.Tests.ps1 new file mode 100644 index 00000000..07c5635b --- /dev/null +++ b/tests/Unit/Get-FabricEventhouse.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "EventhouseId" + "EventhouseName" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricEventhouse" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricEventhouse + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricEventhouse + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricEventhouseDefinition.Tests.ps1 b/tests/Unit/Get-FabricEventhouseDefinition.Tests.ps1 new file mode 100644 index 00000000..b9848cf0 --- /dev/null +++ b/tests/Unit/Get-FabricEventhouseDefinition.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "EventhouseId" + "EventhouseFormat" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricEventhouseDefinition" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricEventhouseDefinition + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricEventhouseDefinition + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricEventstream.Tests.ps1 b/tests/Unit/Get-FabricEventstream.Tests.ps1 new file mode 100644 index 00000000..c79a2ca4 --- /dev/null +++ b/tests/Unit/Get-FabricEventstream.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "EventstreamId" + "EventstreamName" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricEventstream" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricEventstream + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricEventstream + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricEventstreamDefinition.Tests.ps1 b/tests/Unit/Get-FabricEventstreamDefinition.Tests.ps1 new file mode 100644 index 00000000..553a7731 --- /dev/null +++ b/tests/Unit/Get-FabricEventstreamDefinition.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "EventstreamId" + "EventstreamFormat" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricEventstreamDefinition" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricEventstreamDefinition + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricEventstreamDefinition + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricExternalDataShares.Tests.ps1 b/tests/Unit/Get-FabricExternalDataShares.Tests.ps1 new file mode 100644 index 00000000..b7a2d106 --- /dev/null +++ b/tests/Unit/Get-FabricExternalDataShares.Tests.ps1 @@ -0,0 +1,45 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricExternalDataShares" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricExternalDataShares + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricExternalDataShares + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricItem.Tests.ps1 b/tests/Unit/Get-FabricItem.Tests.ps1 new file mode 100644 index 00000000..a633e704 --- /dev/null +++ b/tests/Unit/Get-FabricItem.Tests.ps1 @@ -0,0 +1,49 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "workspaceId" + "Workspace" + "type" + "itemID" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricItem" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricItem + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricItem + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricKQLDashboard.Tests.ps1 b/tests/Unit/Get-FabricKQLDashboard.Tests.ps1 new file mode 100644 index 00000000..535f1f39 --- /dev/null +++ b/tests/Unit/Get-FabricKQLDashboard.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "KQLDashboardId" + "KQLDashboardName" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricKQLDashboard" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricKQLDashboard + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricKQLDashboard + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricKQLDashboardDefinition.Tests.ps1 b/tests/Unit/Get-FabricKQLDashboardDefinition.Tests.ps1 new file mode 100644 index 00000000..794f8cdd --- /dev/null +++ b/tests/Unit/Get-FabricKQLDashboardDefinition.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "KQLDashboardId" + "KQLDashboardFormat" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricKQLDashboardDefinition" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricKQLDashboardDefinition + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricKQLDashboardDefinition + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricKQLDatabase.Tests.ps1 b/tests/Unit/Get-FabricKQLDatabase.Tests.ps1 new file mode 100644 index 00000000..7ad7f295 --- /dev/null +++ b/tests/Unit/Get-FabricKQLDatabase.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "KQLDatabaseId" + "KQLDatabaseName" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricKQLDatabase" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricKQLDatabase + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricKQLDatabase + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricKQLDatabaseDefinition.Tests.ps1 b/tests/Unit/Get-FabricKQLDatabaseDefinition.Tests.ps1 new file mode 100644 index 00000000..d877a181 --- /dev/null +++ b/tests/Unit/Get-FabricKQLDatabaseDefinition.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "KQLDatabaseId" + "KQLDatabaseFormat" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricKQLDatabaseDefinition" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricKQLDatabaseDefinition + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricKQLDatabaseDefinition + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricKQLQueryset.Tests.ps1 b/tests/Unit/Get-FabricKQLQueryset.Tests.ps1 new file mode 100644 index 00000000..71fd2d8c --- /dev/null +++ b/tests/Unit/Get-FabricKQLQueryset.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "KQLQuerysetId" + "KQLQuerysetName" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricKQLQueryset" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricKQLQueryset + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricKQLQueryset + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricKQLQuerysetDefinition.Tests.ps1 b/tests/Unit/Get-FabricKQLQuerysetDefinition.Tests.ps1 new file mode 100644 index 00000000..bde67925 --- /dev/null +++ b/tests/Unit/Get-FabricKQLQuerysetDefinition.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "KQLQuerysetId" + "KQLQuerysetFormat" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricKQLQuerysetDefinition" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricKQLQuerysetDefinition + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricKQLQuerysetDefinition + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricLakehouse.Tests.ps1 b/tests/Unit/Get-FabricLakehouse.Tests.ps1 new file mode 100644 index 00000000..9a2714b0 --- /dev/null +++ b/tests/Unit/Get-FabricLakehouse.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "LakehouseId" + "LakehouseName" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricLakehouse" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricLakehouse + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricLakehouse + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricLakehouseTable.Tests.ps1 b/tests/Unit/Get-FabricLakehouseTable.Tests.ps1 new file mode 100644 index 00000000..c8656337 --- /dev/null +++ b/tests/Unit/Get-FabricLakehouseTable.Tests.ps1 @@ -0,0 +1,47 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "LakehouseId" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricLakehouseTable" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricLakehouseTable + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricLakehouseTable + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricLongRunningOperation.Tests.ps1 b/tests/Unit/Get-FabricLongRunningOperation.Tests.ps1 new file mode 100644 index 00000000..a6fa16ea --- /dev/null +++ b/tests/Unit/Get-FabricLongRunningOperation.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "operationId" + "location" + "retryAfter" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricLongRunningOperation" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricLongRunningOperation + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricLongRunningOperation + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricLongRunningOperationResult.Tests.ps1 b/tests/Unit/Get-FabricLongRunningOperationResult.Tests.ps1 new file mode 100644 index 00000000..c1a231d9 --- /dev/null +++ b/tests/Unit/Get-FabricLongRunningOperationResult.Tests.ps1 @@ -0,0 +1,46 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "operationId" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricLongRunningOperationResult" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricLongRunningOperationResult + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricLongRunningOperationResult + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricMLExperiment.Tests.ps1 b/tests/Unit/Get-FabricMLExperiment.Tests.ps1 new file mode 100644 index 00000000..cf09ee38 --- /dev/null +++ b/tests/Unit/Get-FabricMLExperiment.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "MLExperimentId" + "MLExperimentName" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricMLExperiment" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricMLExperiment + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricMLExperiment + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricMLModel.Tests.ps1 b/tests/Unit/Get-FabricMLModel.Tests.ps1 new file mode 100644 index 00000000..a41397da --- /dev/null +++ b/tests/Unit/Get-FabricMLModel.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "MLModelId" + "MLModelName" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricMLModel" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricMLModel + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricMLModel + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricMirroredDatabase.Tests.ps1 b/tests/Unit/Get-FabricMirroredDatabase.Tests.ps1 new file mode 100644 index 00000000..cd394f2d --- /dev/null +++ b/tests/Unit/Get-FabricMirroredDatabase.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "MirroredDatabaseId" + "MirroredDatabaseName" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricMirroredDatabase" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricMirroredDatabase + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricMirroredDatabase + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricMirroredDatabaseDefinition.Tests.ps1 b/tests/Unit/Get-FabricMirroredDatabaseDefinition.Tests.ps1 new file mode 100644 index 00000000..e3d0390e --- /dev/null +++ b/tests/Unit/Get-FabricMirroredDatabaseDefinition.Tests.ps1 @@ -0,0 +1,47 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "MirroredDatabaseId" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricMirroredDatabaseDefinition" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricMirroredDatabaseDefinition + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricMirroredDatabaseDefinition + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricMirroredDatabaseStatus.Tests.ps1 b/tests/Unit/Get-FabricMirroredDatabaseStatus.Tests.ps1 new file mode 100644 index 00000000..90625c2c --- /dev/null +++ b/tests/Unit/Get-FabricMirroredDatabaseStatus.Tests.ps1 @@ -0,0 +1,47 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "MirroredDatabaseId" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricMirroredDatabaseStatus" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricMirroredDatabaseStatus + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricMirroredDatabaseStatus + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricMirroredDatabaseTableStatus.Tests.ps1 b/tests/Unit/Get-FabricMirroredDatabaseTableStatus.Tests.ps1 new file mode 100644 index 00000000..e7c9ba05 --- /dev/null +++ b/tests/Unit/Get-FabricMirroredDatabaseTableStatus.Tests.ps1 @@ -0,0 +1,47 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "MirroredDatabaseId" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricMirroredDatabaseTableStatus" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricMirroredDatabaseTableStatus + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricMirroredDatabaseTableStatus + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricMirroredWarehouse.Tests.ps1 b/tests/Unit/Get-FabricMirroredWarehouse.Tests.ps1 new file mode 100644 index 00000000..dd62ac74 --- /dev/null +++ b/tests/Unit/Get-FabricMirroredWarehouse.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "MirroredWarehouseId" + "MirroredWarehouseName" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricMirroredWarehouse" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricMirroredWarehouse + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricMirroredWarehouse + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricNotebook.Tests.ps1 b/tests/Unit/Get-FabricNotebook.Tests.ps1 new file mode 100644 index 00000000..8cc0a2fc --- /dev/null +++ b/tests/Unit/Get-FabricNotebook.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "NotebookId" + "NotebookName" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricNotebook" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricNotebook + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricNotebook + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricNotebookDefinition.Tests.ps1 b/tests/Unit/Get-FabricNotebookDefinition.Tests.ps1 new file mode 100644 index 00000000..035f2ce7 --- /dev/null +++ b/tests/Unit/Get-FabricNotebookDefinition.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "NotebookId" + "NotebookFormat" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricNotebookDefinition" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricNotebookDefinition + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricNotebookDefinition + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricPaginatedReport.Tests.ps1 b/tests/Unit/Get-FabricPaginatedReport.Tests.ps1 new file mode 100644 index 00000000..f95868a2 --- /dev/null +++ b/tests/Unit/Get-FabricPaginatedReport.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "PaginatedReportId" + "PaginatedReportName" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricPaginatedReport" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricPaginatedReport + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricPaginatedReport + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricRecoveryPoint.tests.ps1 b/tests/Unit/Get-FabricRecoveryPoint.tests.ps1 new file mode 100644 index 00000000..c2227e3b --- /dev/null +++ b/tests/Unit/Get-FabricRecoveryPoint.tests.ps1 @@ -0,0 +1,10 @@ +Describe "Get-FabricRecoveryPoint Unit Tests" -Tag 'UnitTests' { + Context "Validate parameters" { + It "Should only contain our specific parameters" { + $CommandName = 'Get-FabricRecoveryPoint' + [array]$params = ([Management.Automation.CommandMetaData]$ExecutionContext.SessionState.InvokeCommand.GetCommand($CommandName, 'Function')).Parameters.Keys + [object[]]$knownParameters = 'WorkspaceGUID','DataWarehouseGUID','BaseUrl','Since','Type','CreateTime' + Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Get-FabricReflex.Tests.ps1 b/tests/Unit/Get-FabricReflex.Tests.ps1 new file mode 100644 index 00000000..4c82f7b1 --- /dev/null +++ b/tests/Unit/Get-FabricReflex.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "ReflexId" + "ReflexName" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricReflex" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricReflex + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricReflex + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricReflexDefinition.Tests.ps1 b/tests/Unit/Get-FabricReflexDefinition.Tests.ps1 new file mode 100644 index 00000000..fce6662d --- /dev/null +++ b/tests/Unit/Get-FabricReflexDefinition.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "ReflexId" + "ReflexFormat" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricReflexDefinition" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricReflexDefinition + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricReflexDefinition + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricReport.Tests.ps1 b/tests/Unit/Get-FabricReport.Tests.ps1 new file mode 100644 index 00000000..ed8538ee --- /dev/null +++ b/tests/Unit/Get-FabricReport.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "ReportId" + "ReportName" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricReport" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricReport + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricReport + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricReportDefinition.Tests.ps1 b/tests/Unit/Get-FabricReportDefinition.Tests.ps1 new file mode 100644 index 00000000..3418fbf7 --- /dev/null +++ b/tests/Unit/Get-FabricReportDefinition.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "ReportId" + "ReportFormat" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricReportDefinition" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricReportDefinition + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricReportDefinition + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricSQLDatabase.Tests.ps1 b/tests/Unit/Get-FabricSQLDatabase.Tests.ps1 new file mode 100644 index 00000000..d363b3e2 --- /dev/null +++ b/tests/Unit/Get-FabricSQLDatabase.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "SQLDatabaseName" + "SQLDatabaseId" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricSQLDatabase" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricSQLDatabase + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricSQLDatabase + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricSQLEndpoint.Tests.ps1 b/tests/Unit/Get-FabricSQLEndpoint.Tests.ps1 new file mode 100644 index 00000000..05c03b16 --- /dev/null +++ b/tests/Unit/Get-FabricSQLEndpoint.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "SQLEndpointId" + "SQLEndpointName" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricSQLEndpoint" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricSQLEndpoint + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricSQLEndpoint + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricSemanticModel.Tests.ps1 b/tests/Unit/Get-FabricSemanticModel.Tests.ps1 new file mode 100644 index 00000000..89049c2d --- /dev/null +++ b/tests/Unit/Get-FabricSemanticModel.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "SemanticModelId" + "SemanticModelName" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricSemanticModel" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricSemanticModel + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricSemanticModel + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricSemanticModelDefinition.Tests.ps1 b/tests/Unit/Get-FabricSemanticModelDefinition.Tests.ps1 new file mode 100644 index 00000000..020d281f --- /dev/null +++ b/tests/Unit/Get-FabricSemanticModelDefinition.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "SemanticModelId" + "SemanticModelFormat" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricSemanticModelDefinition" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricSemanticModelDefinition + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricSemanticModelDefinition + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricSparkCustomPool.Tests.ps1 b/tests/Unit/Get-FabricSparkCustomPool.Tests.ps1 new file mode 100644 index 00000000..ecba6b7d --- /dev/null +++ b/tests/Unit/Get-FabricSparkCustomPool.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "SparkCustomPoolId" + "SparkCustomPoolName" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricSparkCustomPool" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricSparkCustomPool + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricSparkCustomPool + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricSparkJobDefinition.Tests.ps1 b/tests/Unit/Get-FabricSparkJobDefinition.Tests.ps1 new file mode 100644 index 00000000..fa2f7d75 --- /dev/null +++ b/tests/Unit/Get-FabricSparkJobDefinition.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "SparkJobDefinitionId" + "SparkJobDefinitionName" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricSparkJobDefinition" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricSparkJobDefinition + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricSparkJobDefinition + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricSparkJobDefinitionDefinition.Tests.ps1 b/tests/Unit/Get-FabricSparkJobDefinitionDefinition.Tests.ps1 new file mode 100644 index 00000000..e946f4aa --- /dev/null +++ b/tests/Unit/Get-FabricSparkJobDefinitionDefinition.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "SparkJobDefinitionId" + "SparkJobDefinitionFormat" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricSparkJobDefinitionDefinition" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricSparkJobDefinitionDefinition + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricSparkJobDefinitionDefinition + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricSparkSettings.Tests.ps1 b/tests/Unit/Get-FabricSparkSettings.Tests.ps1 new file mode 100644 index 00000000..5daa621d --- /dev/null +++ b/tests/Unit/Get-FabricSparkSettings.Tests.ps1 @@ -0,0 +1,46 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricSparkSettings" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricSparkSettings + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricSparkSettings + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricTenantSetting.Tests.ps1 b/tests/Unit/Get-FabricTenantSetting.Tests.ps1 new file mode 100644 index 00000000..d742a66f --- /dev/null +++ b/tests/Unit/Get-FabricTenantSetting.Tests.ps1 @@ -0,0 +1,45 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "SettingTitle" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricTenantSetting" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricTenantSetting + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricTenantSetting + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Get-FabricUri.tests.ps1 b/tests/Unit/Get-FabricUri.tests.ps1 new file mode 100644 index 00000000..d4e8abca --- /dev/null +++ b/tests/Unit/Get-FabricUri.tests.ps1 @@ -0,0 +1,11 @@ +Describe "Get-FabricUri Unit Tests" -Tag 'UnitTests' -Skip { + Context "Validate parameters" { + It "Should only contain our specific parameters" { + $CommandName = 'Get-FabricUri' + [array]$params = ([Management.Automation.CommandMetaData]$ExecutionContext.SessionState.InvokeCommand.GetCommand($CommandName, 'Function')).Parameters.Keys + [object[]]$knownParameters = 'SqlInstance', 'SqlCredential', 'Database', 'Name', 'InputObject', 'EnableException', 'Value' + Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params | Should -BeNullOrEmpty + } + } +} +#TODO: Fix test for internal function diff --git a/tests/Unit/Get-FabricUsageMetricsQuery.Tests.ps1 b/tests/Unit/Get-FabricUsageMetricsQuery.Tests.ps1 new file mode 100644 index 00000000..9f3f055c --- /dev/null +++ b/tests/Unit/Get-FabricUsageMetricsQuery.Tests.ps1 @@ -0,0 +1,49 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "DatasetID" + "groupId" + "reportname" + "ImpersonatedUser" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricUsageMetricsQuery" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricUsageMetricsQuery + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricUsageMetricsQuery + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricUserListAccessEntities.Tests.ps1 b/tests/Unit/Get-FabricUserListAccessEntities.Tests.ps1 new file mode 100644 index 00000000..64c85b2d --- /dev/null +++ b/tests/Unit/Get-FabricUserListAccessEntities.Tests.ps1 @@ -0,0 +1,47 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "UserId" + "Type" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricUserListAccessEntities" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricUserListAccessEntities + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricUserListAccessEntities + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricWarehouse.Tests.ps1 b/tests/Unit/Get-FabricWarehouse.Tests.ps1 new file mode 100644 index 00000000..5de72f9f --- /dev/null +++ b/tests/Unit/Get-FabricWarehouse.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "WarehouseId" + "WarehouseName" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricWarehouse" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricWarehouse + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricWarehouse + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricWorkspace.Tests.ps1 b/tests/Unit/Get-FabricWorkspace.Tests.ps1 new file mode 100644 index 00000000..19ecc9bc --- /dev/null +++ b/tests/Unit/Get-FabricWorkspace.Tests.ps1 @@ -0,0 +1,59 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "WorkspaceName" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricWorkspace" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricWorkspace + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricWorkspace + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } + + Context "WorkspaceName parameter validation" { + It "Throws error when WorkspaceName does not match ValidatePattern" { + # Assuming the ValidatePattern allows only alphanumeric, underscore, space and hyphen + { Get-FabricWorkspace -WorkspaceName "InvalidName!" } | Should -Throw + { Get-FabricWorkspace -WorkspaceName "Another@Invalid" } | Should -Throw + } + + It "Does not throw when WorkspaceName matches ValidatePattern" { + { Get-FabricWorkspace -WorkspaceName "Valid_Name-123" } | Should -Not -Throw + { Get-FabricWorkspace -WorkspaceName "Another Valid Name" } | Should -Not -Throw + } + } +} diff --git a/tests/Unit/Get-FabricWorkspaceDatasetRefreshes.Tests.ps1 b/tests/Unit/Get-FabricWorkspaceDatasetRefreshes.Tests.ps1 new file mode 100644 index 00000000..24ea5c10 --- /dev/null +++ b/tests/Unit/Get-FabricWorkspaceDatasetRefreshes.Tests.ps1 @@ -0,0 +1,46 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceID" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricWorkspaceDatasetRefreshes" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricWorkspaceDatasetRefreshes + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricWorkspaceDatasetRefreshes + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricWorkspaceRoleAssignment.Tests.ps1 b/tests/Unit/Get-FabricWorkspaceRoleAssignment.Tests.ps1 new file mode 100644 index 00000000..9f62e7ef --- /dev/null +++ b/tests/Unit/Get-FabricWorkspaceRoleAssignment.Tests.ps1 @@ -0,0 +1,47 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "WorkspaceRoleAssignmentId" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricWorkspaceRoleAssignment" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricWorkspaceRoleAssignment + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricWorkspaceRoleAssignment + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricWorkspaceTenantSettingOverrides.Tests.ps1 b/tests/Unit/Get-FabricWorkspaceTenantSettingOverrides.Tests.ps1 new file mode 100644 index 00000000..41b1ea99 --- /dev/null +++ b/tests/Unit/Get-FabricWorkspaceTenantSettingOverrides.Tests.ps1 @@ -0,0 +1,45 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricWorkspaceTenantSettingOverrides" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricWorkspaceTenantSettingOverrides + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricWorkspaceTenantSettingOverrides + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricWorkspaceUsageMetricsData.Tests.ps1 b/tests/Unit/Get-FabricWorkspaceUsageMetricsData.Tests.ps1 new file mode 100644 index 00000000..896262be --- /dev/null +++ b/tests/Unit/Get-FabricWorkspaceUsageMetricsData.Tests.ps1 @@ -0,0 +1,47 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "workspaceId" + "username" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricWorkspaceUsageMetricsData" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricWorkspaceUsageMetricsData + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricWorkspaceUsageMetricsData + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-FabricWorkspaceUsers.Tests.ps1 b/tests/Unit/Get-FabricWorkspaceUsers.Tests.ps1 new file mode 100644 index 00000000..93e0cec2 --- /dev/null +++ b/tests/Unit/Get-FabricWorkspaceUsers.Tests.ps1 @@ -0,0 +1,47 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "Workspace" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Get-FabricWorkspaceUsers" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-FabricWorkspaceUsers + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-FabricWorkspaceUsers + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Get-Sha256.Tests.ps1 b/tests/Unit/Get-Sha256.Tests.ps1 new file mode 100644 index 00000000..50003a1e --- /dev/null +++ b/tests/Unit/Get-Sha256.Tests.ps1 @@ -0,0 +1,34 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "string" + + ) +) + +Describe "Get-Sha256" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Get-Sha256 + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Get-Sha256 + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Import-FabricEnvironmentStagingLibrary.Tests.ps1 b/tests/Unit/Import-FabricEnvironmentStagingLibrary.Tests.ps1 new file mode 100644 index 00000000..bc07327a --- /dev/null +++ b/tests/Unit/Import-FabricEnvironmentStagingLibrary.Tests.ps1 @@ -0,0 +1,45 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "EnvironmentId" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + ) +) + +Describe "Import-FabricEnvironmentStagingLibrary" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Import-FabricEnvironmentStagingLibrary + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Import-FabricEnvironmentStagingLibrary + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Import-FabricItem.Tests.ps1 b/tests/Unit/Import-FabricItem.Tests.ps1 new file mode 100644 index 00000000..0a068db8 --- /dev/null +++ b/tests/Unit/Import-FabricItem.Tests.ps1 @@ -0,0 +1,49 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "path" + "workspaceId" + "filter" + "fileOverrides" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + ) +) + +Describe "Import-FabricItem" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Import-FabricItem + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Import-FabricItem + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Invoke-FabricAPIRequest.Tests.ps1 b/tests/Unit/Invoke-FabricAPIRequest.Tests.ps1 new file mode 100644 index 00000000..96baaede --- /dev/null +++ b/tests/Unit/Invoke-FabricAPIRequest.Tests.ps1 @@ -0,0 +1,52 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "authToken" + "uri" + "method" + "body" + "contentType" + "timeoutSec" + "retryCount" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Invoke-FabricAPIRequest" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Invoke-FabricAPIRequest + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Invoke-FabricAPIRequest + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Invoke-FabricDatasetRefresh.Tests.ps1 b/tests/Unit/Invoke-FabricDatasetRefresh.Tests.ps1 new file mode 100644 index 00000000..fdced642 --- /dev/null +++ b/tests/Unit/Invoke-FabricDatasetRefresh.Tests.ps1 @@ -0,0 +1,46 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "DatasetID" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Invoke-FabricDatasetRefresh" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Invoke-FabricDatasetRefresh + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Invoke-FabricDatasetRefresh + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Invoke-FabricKQLCommand.Tests.ps1 b/tests/Unit/Invoke-FabricKQLCommand.Tests.ps1 new file mode 100644 index 00000000..07cba83f --- /dev/null +++ b/tests/Unit/Invoke-FabricKQLCommand.Tests.ps1 @@ -0,0 +1,50 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "KQLDatabaseName" + "KQLDatabaseId" + "KQLCommand" + "ReturnRawResult" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Invoke-FabricKQLCommand" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Invoke-FabricKQLCommand + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Invoke-FabricKQLCommand + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/New-FabricCopyJob.Tests.ps1 b/tests/Unit/New-FabricCopyJob.Tests.ps1 new file mode 100644 index 00000000..1cd7681c --- /dev/null +++ b/tests/Unit/New-FabricCopyJob.Tests.ps1 @@ -0,0 +1,50 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "CopyJobName" + "CopyJobDescription" + "CopyJobPathDefinition" + "CopyJobPathPlatformDefinition" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + ) +) + +Describe "New-FabricCopyJob" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name New-FabricCopyJob + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name New-FabricCopyJob + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/New-FabricDataPipeline.Tests.ps1 b/tests/Unit/New-FabricDataPipeline.Tests.ps1 new file mode 100644 index 00000000..fffa4021 --- /dev/null +++ b/tests/Unit/New-FabricDataPipeline.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "DataPipelineName" + "DataPipelineDescription" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + ) +) + +Describe "New-FabricDataPipeline" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name New-FabricDataPipeline + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name New-FabricDataPipeline + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/New-FabricDomain.Tests.ps1 b/tests/Unit/New-FabricDomain.Tests.ps1 new file mode 100644 index 00000000..18b89db9 --- /dev/null +++ b/tests/Unit/New-FabricDomain.Tests.ps1 @@ -0,0 +1,49 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "DomainName" + "DomainDescription" + "ParentDomainId" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + + ) +) + +Describe "New-FabricDomain" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name New-FabricDomain + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name New-FabricDomain + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/New-FabricEnvironment.Tests.ps1 b/tests/Unit/New-FabricEnvironment.Tests.ps1 new file mode 100644 index 00000000..cb663b4c --- /dev/null +++ b/tests/Unit/New-FabricEnvironment.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "EnvironmentName" + "EnvironmentDescription" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "Confirm" + "WhatIf" + ) +) + +Describe "New-FabricEnvironment" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name New-FabricEnvironment + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name New-FabricEnvironment + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/New-FabricEventhouse.Tests.ps1 b/tests/Unit/New-FabricEventhouse.Tests.ps1 new file mode 100644 index 00000000..5e93e30c --- /dev/null +++ b/tests/Unit/New-FabricEventhouse.Tests.ps1 @@ -0,0 +1,50 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "EventhouseName" + "EventhouseDescription" + "EventhousePathDefinition" + "EventhousePathPlatformDefinition" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "Confirm" + "WhatIf" + ) +) + +Describe "New-FabricEventhouse" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name New-FabricEventhouse + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name New-FabricEventhouse + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/New-FabricEventstream.Tests.ps1 b/tests/Unit/New-FabricEventstream.Tests.ps1 new file mode 100644 index 00000000..4a841489 --- /dev/null +++ b/tests/Unit/New-FabricEventstream.Tests.ps1 @@ -0,0 +1,50 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "EventstreamName" + "EventstreamDescription" + "EventstreamPathDefinition" + "EventstreamPathPlatformDefinition" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "Confirm" + "WhatIf" + ) +) + +Describe "New-FabricEventstream" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name New-FabricEventstream + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name New-FabricEventstream + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/New-FabricKQLDashboard.Tests.ps1 b/tests/Unit/New-FabricKQLDashboard.Tests.ps1 new file mode 100644 index 00000000..f60dac55 --- /dev/null +++ b/tests/Unit/New-FabricKQLDashboard.Tests.ps1 @@ -0,0 +1,50 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "KQLDashboardName" + "KQLDashboardDescription" + "KQLDashboardPathDefinition" + "KQLDashboardPathPlatformDefinition" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "Confirm" + "WhatIf" + ) +) + +Describe "New-FabricKQLDashboard" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name New-FabricKQLDashboard + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name New-FabricKQLDashboard + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/New-FabricKQLDatabase.Tests.ps1 b/tests/Unit/New-FabricKQLDatabase.Tests.ps1 new file mode 100644 index 00000000..de31137f --- /dev/null +++ b/tests/Unit/New-FabricKQLDatabase.Tests.ps1 @@ -0,0 +1,56 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "KQLDatabaseName" + "KQLDatabaseDescription" + "parentEventhouseId" + "KQLDatabaseType" + "KQLInvitationToken" + "KQLSourceClusterUri" + "KQLSourceDatabaseName" + "KQLDatabasePathDefinition" + "KQLDatabasePathPlatformDefinition" + "KQLDatabasePathSchemaDefinition" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "Confirm" + "WhatIf" + ) +) + +Describe "New-FabricKQLDatabase" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name New-FabricKQLDatabase + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name New-FabricKQLDatabase + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/New-FabricKQLQueryset.Tests.ps1 b/tests/Unit/New-FabricKQLQueryset.Tests.ps1 new file mode 100644 index 00000000..48fe0a8c --- /dev/null +++ b/tests/Unit/New-FabricKQLQueryset.Tests.ps1 @@ -0,0 +1,50 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "KQLQuerysetName" + "KQLQuerysetDescription" + "KQLQuerysetPathDefinition" + "KQLQuerysetPathPlatformDefinition" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "Confirm" + "WhatIf" + ) +) + +Describe "New-FabricKQLQueryset" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name New-FabricKQLQueryset + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name New-FabricKQLQueryset + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/New-FabricLakehouse.Tests.ps1 b/tests/Unit/New-FabricLakehouse.Tests.ps1 new file mode 100644 index 00000000..168fc1c8 --- /dev/null +++ b/tests/Unit/New-FabricLakehouse.Tests.ps1 @@ -0,0 +1,51 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "LakehouseName" + "LakehouseDescription" + "LakehouseEnableSchemas" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + + ) +) + +Describe "New-FabricLakehouse" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name New-FabricLakehouse + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name New-FabricLakehouse + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/New-FabricMLExperiment.Tests.ps1 b/tests/Unit/New-FabricMLExperiment.Tests.ps1 new file mode 100644 index 00000000..88f2f199 --- /dev/null +++ b/tests/Unit/New-FabricMLExperiment.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "MLExperimentName" + "MLExperimentDescription" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "Confirm" + "WhatIf" + ) +) + +Describe "New-FabricMLExperiment" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name New-FabricMLExperiment + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name New-FabricMLExperiment + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/New-FabricMLModel.Tests.ps1 b/tests/Unit/New-FabricMLModel.Tests.ps1 new file mode 100644 index 00000000..375775ba --- /dev/null +++ b/tests/Unit/New-FabricMLModel.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "MLModelName" + "MLModelDescription" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "Confirm" + "WhatIf" + ) +) + +Describe "New-FabricMLModel" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name New-FabricMLModel + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name New-FabricMLModel + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/New-FabricMirroredDatabase.Tests.ps1 b/tests/Unit/New-FabricMirroredDatabase.Tests.ps1 new file mode 100644 index 00000000..65bca204 --- /dev/null +++ b/tests/Unit/New-FabricMirroredDatabase.Tests.ps1 @@ -0,0 +1,50 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "MirroredDatabaseName" + "MirroredDatabaseDescription" + "MirroredDatabasePathDefinition" + "MirroredDatabasePathPlatformDefinition" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "Confirm" + "WhatIf" + ) +) + +Describe "New-FabricMirroredDatabase" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name New-FabricMirroredDatabase + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name New-FabricMirroredDatabase + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/New-FabricNotebook.Tests.ps1 b/tests/Unit/New-FabricNotebook.Tests.ps1 new file mode 100644 index 00000000..bc1d57e0 --- /dev/null +++ b/tests/Unit/New-FabricNotebook.Tests.ps1 @@ -0,0 +1,50 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "NotebookName" + "NotebookDescription" + "NotebookPathDefinition" + "NotebookPathPlatformDefinition" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "Confirm" + "WhatIf" + ) +) + +Describe "New-FabricNotebook" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name New-FabricNotebook + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name New-FabricNotebook + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/New-FabricNotebookNEW.Tests.ps1 b/tests/Unit/New-FabricNotebookNEW.Tests.ps1 new file mode 100644 index 00000000..e25dfc60 --- /dev/null +++ b/tests/Unit/New-FabricNotebookNEW.Tests.ps1 @@ -0,0 +1,49 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "NotebookName" + "NotebookDescription" + "NotebookPathDefinition" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "Confirm" + "WhatIf" + ) +) + +Describe "New-FabricNotebookNEW" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name New-FabricNotebookNEW + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name New-FabricNotebookNEW + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/New-FabricRecoveryPoint.tests.ps1 b/tests/Unit/New-FabricRecoveryPoint.tests.ps1 new file mode 100644 index 00000000..d91a1ad6 --- /dev/null +++ b/tests/Unit/New-FabricRecoveryPoint.tests.ps1 @@ -0,0 +1,10 @@ +Describe "New-FabricRecoveryPoint Unit Tests" -Tag 'UnitTests' { + Context "Validate parameters" { + It "Should only contain our specific parameters" { + $CommandName = 'New-FabricRecoveryPoint' + [array]$params = ([Management.Automation.CommandMetaData]$ExecutionContext.SessionState.InvokeCommand.GetCommand($CommandName, 'Function')).Parameters.Keys + [object[]]$knownParameters = 'WorkspaceGUID','DataWarehouseGUID','BaseUrl' + Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/New-FabricReflex.Tests.ps1 b/tests/Unit/New-FabricReflex.Tests.ps1 new file mode 100644 index 00000000..fc59c976 --- /dev/null +++ b/tests/Unit/New-FabricReflex.Tests.ps1 @@ -0,0 +1,50 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "ReflexName" + "ReflexDescription" + "ReflexPathDefinition" + "ReflexPathPlatformDefinition" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "Confirm" + "WhatIf" + ) +) + +Describe "New-FabricReflex" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name New-FabricReflex + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name New-FabricReflex + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/New-FabricReport.Tests.ps1 b/tests/Unit/New-FabricReport.Tests.ps1 new file mode 100644 index 00000000..411926f1 --- /dev/null +++ b/tests/Unit/New-FabricReport.Tests.ps1 @@ -0,0 +1,49 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "ReportName" + "ReportDescription" + "ReportPathDefinition" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "Confirm" + "WhatIf" + ) +) + +Describe "New-FabricReport" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name New-FabricReport + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name New-FabricReport + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/New-FabricSQLDatabase.Tests.ps1 b/tests/Unit/New-FabricSQLDatabase.Tests.ps1 new file mode 100644 index 00000000..a445ae29 --- /dev/null +++ b/tests/Unit/New-FabricSQLDatabase.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "Name" + "Description" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "Confirm" + "WhatIf" + ) +) + +Describe "New-FabricSQLDatabase" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name New-FabricSQLDatabase + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name New-FabricSQLDatabase + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/New-FabricSemanticModel.Tests.ps1 b/tests/Unit/New-FabricSemanticModel.Tests.ps1 new file mode 100644 index 00000000..315cef1f --- /dev/null +++ b/tests/Unit/New-FabricSemanticModel.Tests.ps1 @@ -0,0 +1,49 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "SemanticModelName" + "SemanticModelDescription" + "SemanticModelPathDefinition" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "Confirm" + "WhatIf" + ) +) + +Describe "New-FabricSemanticModel" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name New-FabricSemanticModel + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name New-FabricSemanticModel + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/New-FabricSparkCustomPool.Tests.ps1 b/tests/Unit/New-FabricSparkCustomPool.Tests.ps1 new file mode 100644 index 00000000..a8b0da00 --- /dev/null +++ b/tests/Unit/New-FabricSparkCustomPool.Tests.ps1 @@ -0,0 +1,55 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "SparkCustomPoolName" + "NodeFamily" + "NodeSize" + "AutoScaleEnabled" + "AutoScaleMinNodeCount" + "AutoScaleMaxNodeCount" + "DynamicExecutorAllocationEnabled" + "DynamicExecutorAllocationMinExecutors" + "DynamicExecutorAllocationMaxExecutors" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "Confirm" + "WhatIf" + ) +) + +Describe "New-FabricSparkCustomPool" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name New-FabricSparkCustomPool + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name New-FabricSparkCustomPool + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/New-FabricSparkJobDefinition.Tests.ps1 b/tests/Unit/New-FabricSparkJobDefinition.Tests.ps1 new file mode 100644 index 00000000..45e63800 --- /dev/null +++ b/tests/Unit/New-FabricSparkJobDefinition.Tests.ps1 @@ -0,0 +1,50 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "SparkJobDefinitionName" + "SparkJobDefinitionDescription" + "SparkJobDefinitionPathDefinition" + "SparkJobDefinitionPathPlatformDefinition" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "Confirm" + "WhatIf" + ) +) + +Describe "New-FabricSparkJobDefinition" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name New-FabricSparkJobDefinition + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name New-FabricSparkJobDefinition + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/New-FabricWarehouse.Tests.ps1 b/tests/Unit/New-FabricWarehouse.Tests.ps1 new file mode 100644 index 00000000..7f5faf03 --- /dev/null +++ b/tests/Unit/New-FabricWarehouse.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "WarehouseName" + "WarehouseDescription" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "Confirm" + "WhatIf" + ) +) + +Describe "New-FabricWarehouse" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name New-FabricWarehouse + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name New-FabricWarehouse + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/New-FabricWorkspace.Tests.ps1 b/tests/Unit/New-FabricWorkspace.Tests.ps1 new file mode 100644 index 00000000..d2f77bba --- /dev/null +++ b/tests/Unit/New-FabricWorkspace.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceName" + "WorkspaceDescription" + "CapacityId" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "Confirm" + "WhatIf" + ) +) + +Describe "New-FabricWorkspace" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name New-FabricWorkspace + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name New-FabricWorkspace + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/New-FabricWorkspaceUsageMetricsReport.Tests.ps1 b/tests/Unit/New-FabricWorkspaceUsageMetricsReport.Tests.ps1 new file mode 100644 index 00000000..ee9419f4 --- /dev/null +++ b/tests/Unit/New-FabricWorkspaceUsageMetricsReport.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "workspaceId" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + + ) +) + +Describe "New-FabricWorkspaceUsageMetricsReport" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name New-FabricWorkspaceUsageMetricsReport + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name New-FabricWorkspaceUsageMetricsReport + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Publish-FabricEnvironment.Tests.ps1 b/tests/Unit/Publish-FabricEnvironment.Tests.ps1 new file mode 100644 index 00000000..1ed304fd --- /dev/null +++ b/tests/Unit/Publish-FabricEnvironment.Tests.ps1 @@ -0,0 +1,47 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "EnvironmentId" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Publish-FabricEnvironment" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Publish-FabricEnvironment + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Publish-FabricEnvironment + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Register-FabricWorkspaceToCapacity.Tests.ps1 b/tests/Unit/Register-FabricWorkspaceToCapacity.Tests.ps1 new file mode 100644 index 00000000..6e37cd47 --- /dev/null +++ b/tests/Unit/Register-FabricWorkspaceToCapacity.Tests.ps1 @@ -0,0 +1,50 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "Workspace" + "CapacityId" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + + ) +) + +Describe "Register-FabricWorkspaceToCapacity" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Register-FabricWorkspaceToCapacity + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Register-FabricWorkspaceToCapacity + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Remove-FabricCopyJob.Tests.ps1 b/tests/Unit/Remove-FabricCopyJob.Tests.ps1 new file mode 100644 index 00000000..abe5fb86 --- /dev/null +++ b/tests/Unit/Remove-FabricCopyJob.Tests.ps1 @@ -0,0 +1,47 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "CopyJobId" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "Confirm" + "WhatIf" + ) +) + +Describe "Remove-FabricCopyJob" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Remove-FabricCopyJob + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Remove-FabricCopyJob + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Remove-FabricDataPipeline.Tests.ps1 b/tests/Unit/Remove-FabricDataPipeline.Tests.ps1 new file mode 100644 index 00000000..1c611b9e --- /dev/null +++ b/tests/Unit/Remove-FabricDataPipeline.Tests.ps1 @@ -0,0 +1,47 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "DataPipelineId" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "Confirm" + "WhatIf" + ) +) + +Describe "Remove-FabricDataPipeline" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Remove-FabricDataPipeline + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Remove-FabricDataPipeline + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Remove-FabricDomain.Tests.ps1 b/tests/Unit/Remove-FabricDomain.Tests.ps1 new file mode 100644 index 00000000..b6851f5b --- /dev/null +++ b/tests/Unit/Remove-FabricDomain.Tests.ps1 @@ -0,0 +1,46 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "DomainId" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "Confirm" + "WhatIf" + ) +) + +Describe "Remove-FabricDomain" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Remove-FabricDomain + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Remove-FabricDomain + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Remove-FabricDomainWorkspaceAssignment.Tests.ps1 b/tests/Unit/Remove-FabricDomainWorkspaceAssignment.Tests.ps1 new file mode 100644 index 00000000..c3fd918d --- /dev/null +++ b/tests/Unit/Remove-FabricDomainWorkspaceAssignment.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "DomainId" + "WorkspaceIds" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "Confirm" + "WhatIf" + + ) +) + +Describe "Remove-FabricDomainWorkspaceAssignment" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Remove-FabricDomainWorkspaceAssignment + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Remove-FabricDomainWorkspaceAssignment + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Remove-FabricDomainWorkspaceRoleAssignment.Tests.ps1 b/tests/Unit/Remove-FabricDomainWorkspaceRoleAssignment.Tests.ps1 new file mode 100644 index 00000000..8ed251b0 --- /dev/null +++ b/tests/Unit/Remove-FabricDomainWorkspaceRoleAssignment.Tests.ps1 @@ -0,0 +1,49 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "DomainId" + "DomainRole" + "PrincipalIds" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "Confirm" + "WhatIf" + + ) +) + +Describe "Remove-FabricDomainWorkspaceRoleAssignment " -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Remove-FabricDomainWorkspaceRoleAssignment + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Remove-FabricDomainWorkspaceRoleAssignment + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Remove-FabricEnvironment.Tests.ps1 b/tests/Unit/Remove-FabricEnvironment.Tests.ps1 new file mode 100644 index 00000000..66ede1ac --- /dev/null +++ b/tests/Unit/Remove-FabricEnvironment.Tests.ps1 @@ -0,0 +1,47 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "EnvironmentId" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "Confirm" + "WhatIf" + ) +) + +Describe "Remove-FabricEnvironment" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Remove-FabricEnvironment + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Remove-FabricEnvironment + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Remove-FabricEnvironmentStagingLibrary.Tests.ps1 b/tests/Unit/Remove-FabricEnvironmentStagingLibrary.Tests.ps1 new file mode 100644 index 00000000..585efb87 --- /dev/null +++ b/tests/Unit/Remove-FabricEnvironmentStagingLibrary.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "EnvironmentId" + "LibraryName" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "Confirm" + "WhatIf" + ) +) + +Describe "Remove-FabricEnvironmentStagingLibrary" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Remove-FabricEnvironmentStagingLibrary + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Remove-FabricEnvironmentStagingLibrary + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Remove-FabricEventhouse.Tests.ps1 b/tests/Unit/Remove-FabricEventhouse.Tests.ps1 new file mode 100644 index 00000000..73315278 --- /dev/null +++ b/tests/Unit/Remove-FabricEventhouse.Tests.ps1 @@ -0,0 +1,47 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "EventhouseId" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "Confirm" + "WhatIf" + ) +) + +Describe "Remove-FabricEventhouse" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Remove-FabricEventhouse + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Remove-FabricEventhouse + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Remove-FabricEventstream.Tests.ps1 b/tests/Unit/Remove-FabricEventstream.Tests.ps1 new file mode 100644 index 00000000..9308b0ee --- /dev/null +++ b/tests/Unit/Remove-FabricEventstream.Tests.ps1 @@ -0,0 +1,47 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "EventstreamId" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "Confirm" + "WhatIf" + ) +) + +Describe "Remove-FabricEventstream" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Remove-FabricEventstream + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Remove-FabricEventstream + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Remove-FabricItem.Tests.ps1 b/tests/Unit/Remove-FabricItem.Tests.ps1 new file mode 100644 index 00000000..df850903 --- /dev/null +++ b/tests/Unit/Remove-FabricItem.Tests.ps1 @@ -0,0 +1,50 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "workspaceId" + "filter" + "itemID" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + + ) +) + +Describe "Remove-FabricItem" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Remove-FabricItem + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Remove-FabricItem + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Remove-FabricKQLDashboard.Tests.ps1 b/tests/Unit/Remove-FabricKQLDashboard.Tests.ps1 new file mode 100644 index 00000000..47bc34b0 --- /dev/null +++ b/tests/Unit/Remove-FabricKQLDashboard.Tests.ps1 @@ -0,0 +1,47 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "KQLDashboardId" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "Confirm" + "WhatIf" + ) +) + +Describe "Remove-FabricKQLDashboard" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Remove-FabricKQLDashboard + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Remove-FabricKQLDashboard + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Remove-FabricKQLDatabase.Tests.ps1 b/tests/Unit/Remove-FabricKQLDatabase.Tests.ps1 new file mode 100644 index 00000000..18f27cda --- /dev/null +++ b/tests/Unit/Remove-FabricKQLDatabase.Tests.ps1 @@ -0,0 +1,47 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "KQLDatabaseId" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "Confirm" + "WhatIf" + ) +) + +Describe "Remove-FabricKQLDatabase" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Remove-FabricKQLDatabase + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Remove-FabricKQLDatabase + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Remove-FabricKQLQueryset.Tests.ps1 b/tests/Unit/Remove-FabricKQLQueryset.Tests.ps1 new file mode 100644 index 00000000..99377eba --- /dev/null +++ b/tests/Unit/Remove-FabricKQLQueryset.Tests.ps1 @@ -0,0 +1,49 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "KQLQuerysetId" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + + ) +) + +Describe "Remove-FabricKQLQueryset" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Remove-FabricKQLQueryset + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Remove-FabricKQLQueryset + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Remove-FabricLakehouse.Tests.ps1 b/tests/Unit/Remove-FabricLakehouse.Tests.ps1 new file mode 100644 index 00000000..75bab867 --- /dev/null +++ b/tests/Unit/Remove-FabricLakehouse.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "LakehouseId" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "Confirm" + "WhatIf" + + ) +) + +Describe "Remove-FabricLakehouse" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Remove-FabricLakehouse + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Remove-FabricLakehouse + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Remove-FabricMLExperiment.Tests.ps1 b/tests/Unit/Remove-FabricMLExperiment.Tests.ps1 new file mode 100644 index 00000000..9b6ddbde --- /dev/null +++ b/tests/Unit/Remove-FabricMLExperiment.Tests.ps1 @@ -0,0 +1,47 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "MLExperimentId" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "Confirm" + "WhatIf" + ) +) + +Describe "Remove-FabricMLExperiment" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Remove-FabricMLExperiment + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Remove-FabricMLExperiment + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Remove-FabricMLModel.Tests.ps1 b/tests/Unit/Remove-FabricMLModel.Tests.ps1 new file mode 100644 index 00000000..748144a7 --- /dev/null +++ b/tests/Unit/Remove-FabricMLModel.Tests.ps1 @@ -0,0 +1,47 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "MLModelId" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + ) +) + +Describe "Remove-FabricMLModel" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Remove-FabricMLModel + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Remove-FabricMLModel + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Remove-FabricMirroredDatabase.Tests.ps1 b/tests/Unit/Remove-FabricMirroredDatabase.Tests.ps1 new file mode 100644 index 00000000..205e8b15 --- /dev/null +++ b/tests/Unit/Remove-FabricMirroredDatabase.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "MirroredDatabaseId" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "Confirm" + "WhatIf" + + ) +) + +Describe "Remove-FabricMirroredDatabase" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Remove-FabricMirroredDatabase + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Remove-FabricMirroredDatabase + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Remove-FabricNotebook.Tests.ps1 b/tests/Unit/Remove-FabricNotebook.Tests.ps1 new file mode 100644 index 00000000..a0c69463 --- /dev/null +++ b/tests/Unit/Remove-FabricNotebook.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "NotebookId" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + + ) +) + +Describe "Remove-FabricNotebook" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Remove-FabricNotebook + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Remove-FabricNotebook + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Remove-FabricRecoveryPoint.tests.ps1 b/tests/Unit/Remove-FabricRecoveryPoint.tests.ps1 new file mode 100644 index 00000000..e4b868c9 --- /dev/null +++ b/tests/Unit/Remove-FabricRecoveryPoint.tests.ps1 @@ -0,0 +1,10 @@ +Describe "Remove-FabricRecoveryPoint Unit Tests" -Tag 'UnitTests' { + Context "Validate parameters" { + It "Should only contain our specific parameters" { + $CommandName = 'Remove-FabricRecoveryPoint' + [array]$params = ([Management.Automation.CommandMetaData]$ExecutionContext.SessionState.InvokeCommand.GetCommand($CommandName, 'Function')).Parameters.Keys + [object[]]$knownParameters = 'CreateTime','WorkspaceGUID','DataWarehouseGUID','BaseUrl' + Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Remove-FabricReflex.Tests.ps1 b/tests/Unit/Remove-FabricReflex.Tests.ps1 new file mode 100644 index 00000000..848fa2e2 --- /dev/null +++ b/tests/Unit/Remove-FabricReflex.Tests.ps1 @@ -0,0 +1,47 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "ReflexId" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + ) +) + +Describe "Remove-FabricReflex" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Remove-FabricReflex + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Remove-FabricReflex + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Remove-FabricReport.Tests.ps1 b/tests/Unit/Remove-FabricReport.Tests.ps1 new file mode 100644 index 00000000..d09a5a73 --- /dev/null +++ b/tests/Unit/Remove-FabricReport.Tests.ps1 @@ -0,0 +1,47 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "ReportId" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + ) +) + +Describe "Remove-FabricReport" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Remove-FabricReport + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Remove-FabricReport + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Remove-FabricSQLDatabase.Tests.ps1 b/tests/Unit/Remove-FabricSQLDatabase.Tests.ps1 new file mode 100644 index 00000000..930c21d1 --- /dev/null +++ b/tests/Unit/Remove-FabricSQLDatabase.Tests.ps1 @@ -0,0 +1,47 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "SQLDatabaseId" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + ) +) + +Describe "Remove-FabricSQLDatabase" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Remove-FabricSQLDatabase + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Remove-FabricSQLDatabase + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Remove-FabricSemanticModel.Tests.ps1 b/tests/Unit/Remove-FabricSemanticModel.Tests.ps1 new file mode 100644 index 00000000..daae5f47 --- /dev/null +++ b/tests/Unit/Remove-FabricSemanticModel.Tests.ps1 @@ -0,0 +1,47 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "SemanticModelId" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + ) +) + +Describe "Remove-FabricSemanticModel" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Remove-FabricSemanticModel + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Remove-FabricSemanticModel + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Remove-FabricSparkCustomPool.Tests.ps1 b/tests/Unit/Remove-FabricSparkCustomPool.Tests.ps1 new file mode 100644 index 00000000..b5747b3c --- /dev/null +++ b/tests/Unit/Remove-FabricSparkCustomPool.Tests.ps1 @@ -0,0 +1,47 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "SparkCustomPoolId" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + ) +) + +Describe "Remove-FabricSparkCustomPool" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Remove-FabricSparkCustomPool + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Remove-FabricSparkCustomPool + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Remove-FabricSparkJobDefinition.Tests.ps1 b/tests/Unit/Remove-FabricSparkJobDefinition.Tests.ps1 new file mode 100644 index 00000000..fb7620f8 --- /dev/null +++ b/tests/Unit/Remove-FabricSparkJobDefinition.Tests.ps1 @@ -0,0 +1,47 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "SparkJobDefinitionId" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + ) +) + +Describe "Remove-FabricSparkJobDefinition" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Remove-FabricSparkJobDefinition + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Remove-FabricSparkJobDefinition + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Remove-FabricWarehouse.Tests.ps1 b/tests/Unit/Remove-FabricWarehouse.Tests.ps1 new file mode 100644 index 00000000..b4eb9b62 --- /dev/null +++ b/tests/Unit/Remove-FabricWarehouse.Tests.ps1 @@ -0,0 +1,47 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "WarehouseId" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + ) +) + +Describe "Remove-FabricWarehouse" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Remove-FabricWarehouse + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Remove-FabricWarehouse + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Remove-FabricWorkspace.Tests.ps1 b/tests/Unit/Remove-FabricWorkspace.Tests.ps1 new file mode 100644 index 00000000..2ab7580f --- /dev/null +++ b/tests/Unit/Remove-FabricWorkspace.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + + ) +) + +Describe "Remove-FabricWorkspace" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Remove-FabricWorkspace + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Remove-FabricWorkspace + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Remove-FabricWorkspaceCapacityAssignment.Tests.ps1 b/tests/Unit/Remove-FabricWorkspaceCapacityAssignment.Tests.ps1 new file mode 100644 index 00000000..0e3b413c --- /dev/null +++ b/tests/Unit/Remove-FabricWorkspaceCapacityAssignment.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + + ) +) + +Describe "Remove-FabricWorkspaceCapacityAssignment" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Remove-FabricWorkspaceCapacityAssignment + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Remove-FabricWorkspaceCapacityAssignment + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } + +} diff --git a/tests/Unit/Remove-FabricWorkspaceIdentity.Tests.ps1 b/tests/Unit/Remove-FabricWorkspaceIdentity.Tests.ps1 new file mode 100644 index 00000000..324256f3 --- /dev/null +++ b/tests/Unit/Remove-FabricWorkspaceIdentity.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + + ) +) + +Describe "Remove-FabricWorkspaceIdentity" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Remove-FabricWorkspaceIdentity + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Remove-FabricWorkspaceIdentity + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Remove-FabricWorkspaceRoleAssignment.Tests.ps1 b/tests/Unit/Remove-FabricWorkspaceRoleAssignment.Tests.ps1 new file mode 100644 index 00000000..afbb7566 --- /dev/null +++ b/tests/Unit/Remove-FabricWorkspaceRoleAssignment.Tests.ps1 @@ -0,0 +1,49 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "WorkspaceRoleAssignmentId" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + + ) +) + +Describe "Remove-FabricWorkspaceRoleAssignment" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Remove-FabricWorkspaceRoleAssignment + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Remove-FabricWorkspaceRoleAssignment + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Restore-FabricRecoveryPoint.tests.ps1 b/tests/Unit/Restore-FabricRecoveryPoint.tests.ps1 new file mode 100644 index 00000000..f515187f --- /dev/null +++ b/tests/Unit/Restore-FabricRecoveryPoint.tests.ps1 @@ -0,0 +1,10 @@ +Describe "Restore-FabricRecoveryPoint Unit Tests" -Tag 'UnitTests' { + Context "Validate parameters" { + It "Should only contain our specific parameters" { + $CommandName = 'Restore-FabricRecoveryPoint' + [array]$params = ([Management.Automation.CommandMetaData]$ExecutionContext.SessionState.InvokeCommand.GetCommand($CommandName, 'Function')).Parameters.Keys + [object[]]$knownParameters = 'CreateTime','WorkspaceGUID','DataWarehouseGUID','BaseUrl','Wait' + Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Resume-FabricCapacity.Tests.ps1 b/tests/Unit/Resume-FabricCapacity.Tests.ps1 new file mode 100644 index 00000000..7c6156fa --- /dev/null +++ b/tests/Unit/Resume-FabricCapacity.Tests.ps1 @@ -0,0 +1,50 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "subscriptionID" + "resourcegroup" + "capacity" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + + ) +) + +Describe "Resume-FabricCapacity" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Resume-FabricCapacity + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Resume-FabricCapacity + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Revoke-FabricCapacityTenantSettingOverrides.Tests.ps1 b/tests/Unit/Revoke-FabricCapacityTenantSettingOverrides.Tests.ps1 new file mode 100644 index 00000000..1e6822d4 --- /dev/null +++ b/tests/Unit/Revoke-FabricCapacityTenantSettingOverrides.Tests.ps1 @@ -0,0 +1,47 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "capacityId" + "tenantSettingName" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "Confirm" + "WhatIf" + ) +) + +Describe "Revoke-FabricCapacityTenantSettingOverrides" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Revoke-FabricCapacityTenantSettingOverrides + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Revoke-FabricCapacityTenantSettingOverrides + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Revoke-FabricExternalDataShares.Tests.ps1 b/tests/Unit/Revoke-FabricExternalDataShares.Tests.ps1 new file mode 100644 index 00000000..ab5122be --- /dev/null +++ b/tests/Unit/Revoke-FabricExternalDataShares.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "ItemId" + "ExternalDataShareId" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "Confirm" + "WhatIf" + ) +) + +Describe "Revoke-FabricExternalDataShares" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Revoke-FabricExternalDataShares + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Revoke-FabricExternalDataShares + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Set-FabricApiHeaders.Tests.ps1 b/tests/Unit/Set-FabricApiHeaders.Tests.ps1 new file mode 100644 index 00000000..310ecbc8 --- /dev/null +++ b/tests/Unit/Set-FabricApiHeaders.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "TenantId" + "AppId" + "AppSecret" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + ) +) + +Describe "Set-FabricApiHeaders" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Set-FabricApiHeaders + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Set-FabricApiHeaders + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Set-FabricAuthToken.Tests.ps1 b/tests/Unit/Set-FabricAuthToken.Tests.ps1 new file mode 100644 index 00000000..f185f65a --- /dev/null +++ b/tests/Unit/Set-FabricAuthToken.Tests.ps1 @@ -0,0 +1,53 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "servicePrincipalId" + "servicePrincipalSecret" + "credential" + "tenantId" + "reset" + "apiUrl" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + + ) +) + +Describe "Set-FabricAuthToken" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Set-FabricAuthToken + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Set-FabricAuthToken + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Set-FabricConfig.tests.ps1 b/tests/Unit/Set-FabricConfig.tests.ps1 new file mode 100644 index 00000000..67f5a346 --- /dev/null +++ b/tests/Unit/Set-FabricConfig.tests.ps1 @@ -0,0 +1,10 @@ +Describe "Set-FabricConfig Unit Tests" -Tag 'UnitTests' { + Context "Validate parameters" { + It "Should only contain our specific parameters" { + $CommandName = 'Set-FabricConfig' + [array]$params = ([Management.Automation.CommandMetaData]$ExecutionContext.SessionState.InvokeCommand.GetCommand($CommandName, 'Function')).Parameters.Keys + [object[]]$knownParameters = 'WorkspaceGUID','DataWarehouseGUID','BaseUrl','SkipPersist' + Compare-Object -ReferenceObject $knownParameters -DifferenceObject $params | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Start-FabricLakehouseTableMaintenance.Tests.ps1 b/tests/Unit/Start-FabricLakehouseTableMaintenance.Tests.ps1 new file mode 100644 index 00000000..8e47c628 --- /dev/null +++ b/tests/Unit/Start-FabricLakehouseTableMaintenance.Tests.ps1 @@ -0,0 +1,54 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "LakehouseId" + "JobType" + "SchemaName" + "TableName" + "IsVOrder" + "ColumnsZOrderBy" + "retentionPeriod" + "waitForCompletion" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + ) +) + +Describe "Start-FabricLakehouseTableMaintenance" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Start-FabricLakehouseTableMaintenance + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Start-FabricLakehouseTableMaintenance + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Start-FabricMirroredDatabaseMirroring.Tests.ps1 b/tests/Unit/Start-FabricMirroredDatabaseMirroring.Tests.ps1 new file mode 100644 index 00000000..7b95ca42 --- /dev/null +++ b/tests/Unit/Start-FabricMirroredDatabaseMirroring.Tests.ps1 @@ -0,0 +1,47 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "MirroredDatabaseId" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + ) +) + +Describe "Start-FabricMirroredDatabaseMirroring" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Start-FabricMirroredDatabaseMirroring + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Start-FabricMirroredDatabaseMirroring + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Start-FabricSparkJobDefinitionOnDemand.Tests.ps1 b/tests/Unit/Start-FabricSparkJobDefinitionOnDemand.Tests.ps1 new file mode 100644 index 00000000..48bef72a --- /dev/null +++ b/tests/Unit/Start-FabricSparkJobDefinitionOnDemand.Tests.ps1 @@ -0,0 +1,49 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "SparkJobDefinitionId" + "JobType" + "waitForCompletion" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + ) +) + +Describe "Start-FabricSparkJobDefinitionOnDemand" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Start-FabricSparkJobDefinitionOnDemand + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Start-FabricSparkJobDefinitionOnDemand + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Stop-FabricEnvironmentPublish.Tests.ps1 b/tests/Unit/Stop-FabricEnvironmentPublish.Tests.ps1 new file mode 100644 index 00000000..2c5f2b30 --- /dev/null +++ b/tests/Unit/Stop-FabricEnvironmentPublish.Tests.ps1 @@ -0,0 +1,47 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "EnvironmentId" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + ) +) + +Describe "Stop-FabricEnvironmentPublish" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Stop-FabricEnvironmentPublish + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Stop-FabricEnvironmentPublish + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Stop-FabricMirroredDatabaseMirroring.Tests.ps1 b/tests/Unit/Stop-FabricMirroredDatabaseMirroring.Tests.ps1 new file mode 100644 index 00000000..c3617ce2 --- /dev/null +++ b/tests/Unit/Stop-FabricMirroredDatabaseMirroring.Tests.ps1 @@ -0,0 +1,47 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "MirroredDatabaseId" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + ) +) + +Describe "Stop-FabricMirroredDatabaseMirroring" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Stop-FabricMirroredDatabaseMirroring + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Stop-FabricMirroredDatabaseMirroring + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Suspend-FabricCapacity.Tests.ps1 b/tests/Unit/Suspend-FabricCapacity.Tests.ps1 new file mode 100644 index 00000000..68beff0f --- /dev/null +++ b/tests/Unit/Suspend-FabricCapacity.Tests.ps1 @@ -0,0 +1,50 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "subscriptionID" + "resourcegroup" + "capacity" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + + ) +) + +Describe "Suspend-FabricCapacity" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Suspend-FabricCapacity + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Suspend-FabricCapacity + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Test-FabricApiResponse.Tests.ps1 b/tests/Unit/Test-FabricApiResponse.Tests.ps1 new file mode 100644 index 00000000..ea83fd26 --- /dev/null +++ b/tests/Unit/Test-FabricApiResponse.Tests.ps1 @@ -0,0 +1,50 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "statusCode" + "response" + "responseHeader" + "Name" + "typeName" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + + ) +) + +Describe "Test-FabricApiResponse" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Test-FabricApiResponse + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Test-FabricApiResponse + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Unregister-FabricWorkspaceToCapacity.Tests.ps1 b/tests/Unit/Unregister-FabricWorkspaceToCapacity.Tests.ps1 new file mode 100644 index 00000000..d9c0a0cd --- /dev/null +++ b/tests/Unit/Unregister-FabricWorkspaceToCapacity.Tests.ps1 @@ -0,0 +1,49 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "Workspace" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + + ) +) + +Describe "Unregister-FabricWorkspaceToCapacity" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Unregister-FabricWorkspaceToCapacity + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Unregister-FabricWorkspaceToCapacity + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} + diff --git a/tests/Unit/Update-FabricCapacityTenantSettingOverrides.Tests.ps1 b/tests/Unit/Update-FabricCapacityTenantSettingOverrides.Tests.ps1 new file mode 100644 index 00000000..c4ab4e7a --- /dev/null +++ b/tests/Unit/Update-FabricCapacityTenantSettingOverrides.Tests.ps1 @@ -0,0 +1,53 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "TenantSettingName" + "EnableTenantSetting" + "DelegateToCapacity" + "DelegateToDomain" + "DelegateToWorkspace" + "EnabledSecurityGroups" + "ExcludedSecurityGroups" + "Properties" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + ) +) + +Describe "Update-FabricCapacityTenantSettingOverrides" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Update-FabricCapacityTenantSettingOverrides + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Update-FabricCapacityTenantSettingOverrides + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Update-FabricCopyJob.Tests.ps1 b/tests/Unit/Update-FabricCopyJob.Tests.ps1 new file mode 100644 index 00000000..f58b2a01 --- /dev/null +++ b/tests/Unit/Update-FabricCopyJob.Tests.ps1 @@ -0,0 +1,49 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "CopyJobId" + "CopyJobName" + "CopyJobDescription" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + ) +) + +Describe "Update-FabricCopyJob" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Update-FabricCopyJob + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Update-FabricCopyJob + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Update-FabricCopyJobDefinition.Tests.ps1 b/tests/Unit/Update-FabricCopyJobDefinition.Tests.ps1 new file mode 100644 index 00000000..9caa670f --- /dev/null +++ b/tests/Unit/Update-FabricCopyJobDefinition.Tests.ps1 @@ -0,0 +1,49 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "CopyJobId" + "CopyJobPathDefinition" + "CopyJobPathPlatformDefinition" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + ) +) + +Describe "Update-FabricCopyJobDefinition" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Update-FabricCopyJobDefinition + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Update-FabricCopyJobDefinition + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Update-FabricDataPipeline.Tests.ps1 b/tests/Unit/Update-FabricDataPipeline.Tests.ps1 new file mode 100644 index 00000000..db2de7f7 --- /dev/null +++ b/tests/Unit/Update-FabricDataPipeline.Tests.ps1 @@ -0,0 +1,49 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "DataPipelineId" + "DataPipelineName" + "DataPipelineDescription" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + ) +) + +Describe "Update-FabricDataPipeline" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Update-FabricDataPipeline + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Update-FabricDataPipeline + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Update-FabricDomain.Tests.ps1 b/tests/Unit/Update-FabricDomain.Tests.ps1 new file mode 100644 index 00000000..dc040311 --- /dev/null +++ b/tests/Unit/Update-FabricDomain.Tests.ps1 @@ -0,0 +1,49 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "DomainId" + "DomainName" + "DomainDescription" + "DomainContributorsScope" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + ) +) + +Describe "Update-FabricDomain" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Update-FabricDomain + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Update-FabricDomain + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Update-FabricEnvironment.Tests.ps1 b/tests/Unit/Update-FabricEnvironment.Tests.ps1 new file mode 100644 index 00000000..d500788e --- /dev/null +++ b/tests/Unit/Update-FabricEnvironment.Tests.ps1 @@ -0,0 +1,49 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "EnvironmentId" + "EnvironmentName" + "EnvironmentDescription" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + ) +) + +Describe "Update-FabricEnvironment" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Update-FabricEnvironment + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Update-FabricEnvironment + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Update-FabricEnvironmentStagingSparkCompute.Tests.ps1 b/tests/Unit/Update-FabricEnvironmentStagingSparkCompute.Tests.ps1 new file mode 100644 index 00000000..d2e9b335 --- /dev/null +++ b/tests/Unit/Update-FabricEnvironmentStagingSparkCompute.Tests.ps1 @@ -0,0 +1,58 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "EnvironmentId" + "InstancePoolName" + "InstancePoolType" + "DriverCores" + "DriverMemory" + "ExecutorCores" + "ExecutorMemory" + "DynamicExecutorAllocationEnabled" + "DynamicExecutorAllocationMinExecutors" + "DynamicExecutorAllocationMaxExecutors" + "RuntimeVersion" + "SparkProperties" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + ) +) + +Describe "Update-FabricEnvironmentStagingSparkCompute" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Update-FabricEnvironmentStagingSparkCompute + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Update-FabricEnvironmentStagingSparkCompute + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Update-FabricEventhouse.Tests.ps1 b/tests/Unit/Update-FabricEventhouse.Tests.ps1 new file mode 100644 index 00000000..5673634b --- /dev/null +++ b/tests/Unit/Update-FabricEventhouse.Tests.ps1 @@ -0,0 +1,49 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "EventhouseId" + "EventhouseName" + "EventhouseDescription" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + ) +) + +Describe "Update-FabricEventhouse" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Update-FabricEventhouse + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Update-FabricEventhouse + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Update-FabricEventhouseDefinition.Tests.ps1 b/tests/Unit/Update-FabricEventhouseDefinition.Tests.ps1 new file mode 100644 index 00000000..bd880261 --- /dev/null +++ b/tests/Unit/Update-FabricEventhouseDefinition.Tests.ps1 @@ -0,0 +1,49 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "EventhouseId" + "EventhousePathDefinition" + "EventhousePathPlatformDefinition" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + ) +) + +Describe "Update-FabricEventhouseDefinition" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Update-FabricEventhouseDefinition + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Update-FabricEventhouseDefinition + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Update-FabricEventstream.Tests.ps1 b/tests/Unit/Update-FabricEventstream.Tests.ps1 new file mode 100644 index 00000000..69ba7fa2 --- /dev/null +++ b/tests/Unit/Update-FabricEventstream.Tests.ps1 @@ -0,0 +1,49 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "EventstreamId" + "EventstreamName" + "EventstreamDescription" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + ) +) + +Describe "Update-FabricEventstream" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Update-FabricEventstream + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Update-FabricEventstream + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Update-FabricEventstreamDefinition.Tests.ps1 b/tests/Unit/Update-FabricEventstreamDefinition.Tests.ps1 new file mode 100644 index 00000000..fd441624 --- /dev/null +++ b/tests/Unit/Update-FabricEventstreamDefinition.Tests.ps1 @@ -0,0 +1,49 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "EventstreamId" + "EventstreamPathDefinition" + "EventstreamPathPlatformDefinition" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + ) +) + +Describe "Update-FabricEventstreamDefinition" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Update-FabricEventstreamDefinition + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Update-FabricEventstreamDefinition + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Update-FabricKQLDashboard.Tests.ps1 b/tests/Unit/Update-FabricKQLDashboard.Tests.ps1 new file mode 100644 index 00000000..d5158500 --- /dev/null +++ b/tests/Unit/Update-FabricKQLDashboard.Tests.ps1 @@ -0,0 +1,49 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "KQLDashboardId" + "KQLDashboardName" + "KQLDashboardDescription" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + ) +) + +Describe "Update-FabricKQLDashboard" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Update-FabricKQLDashboard + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Update-FabricKQLDashboard + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Update-FabricKQLDashboardDefinition.Tests.ps1 b/tests/Unit/Update-FabricKQLDashboardDefinition.Tests.ps1 new file mode 100644 index 00000000..06830127 --- /dev/null +++ b/tests/Unit/Update-FabricKQLDashboardDefinition.Tests.ps1 @@ -0,0 +1,50 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "KQLDashboardId" + "KQLDashboardPathDefinition" + "KQLDashboardPathPlatformDefinition" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "Confirm" + "WhatIf" + + ) +) + +Describe "Update-FabricKQLDashboardDefinition" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Update-FabricKQLDashboardDefinition + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Update-FabricKQLDashboardDefinition + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Update-FabricKQLDatabase.Tests.ps1 b/tests/Unit/Update-FabricKQLDatabase.Tests.ps1 new file mode 100644 index 00000000..6b446cb2 --- /dev/null +++ b/tests/Unit/Update-FabricKQLDatabase.Tests.ps1 @@ -0,0 +1,50 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "KQLDatabaseId" + "KQLDatabaseName" + "KQLDatabaseDescription" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + + ) +) + +Describe "Update-FabricKQLDatabase" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Update-FabricKQLDatabase + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Update-FabricKQLDatabase + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Update-FabricKQLDatabaseDefinition.Tests.ps1 b/tests/Unit/Update-FabricKQLDatabaseDefinition.Tests.ps1 new file mode 100644 index 00000000..ffc56b6e --- /dev/null +++ b/tests/Unit/Update-FabricKQLDatabaseDefinition.Tests.ps1 @@ -0,0 +1,50 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "KQLDatabaseId" + "KQLDatabasePathDefinition" + "KQLDatabasePathPlatformDefinition" + "KQLDatabasePathSchemaDefinition" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + ) +) + +Describe "Update-FabricKQLDatabaseDefinition" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Update-FabricKQLDatabaseDefinition + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Update-FabricKQLDatabaseDefinition + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Update-FabricKQLQueryset.Tests.ps1 b/tests/Unit/Update-FabricKQLQueryset.Tests.ps1 new file mode 100644 index 00000000..eaf5ae6e --- /dev/null +++ b/tests/Unit/Update-FabricKQLQueryset.Tests.ps1 @@ -0,0 +1,49 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "KQLQuerysetId" + "KQLQuerysetName" + "KQLQuerysetDescription" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + ) +) + +Describe "Update-FabricKQLQueryset" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Update-FabricKQLQueryset + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Update-FabricKQLQueryset + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Update-FabricKQLQuerysetDefinition.Tests.ps1 b/tests/Unit/Update-FabricKQLQuerysetDefinition.Tests.ps1 new file mode 100644 index 00000000..b966774b --- /dev/null +++ b/tests/Unit/Update-FabricKQLQuerysetDefinition.Tests.ps1 @@ -0,0 +1,49 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "KQLQuerysetId" + "KQLQuerysetPathDefinition" + "KQLQuerysetPathPlatformDefinition" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + ) +) + +Describe "Update-FabricKQLQuerysetDefinition" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Update-FabricKQLQuerysetDefinition + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Update-FabricKQLQuerysetDefinition + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Update-FabricLakehouse.Tests.ps1 b/tests/Unit/Update-FabricLakehouse.Tests.ps1 new file mode 100644 index 00000000..81faa4e4 --- /dev/null +++ b/tests/Unit/Update-FabricLakehouse.Tests.ps1 @@ -0,0 +1,49 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "LakehouseId" + "LakehouseName" + "LakehouseDescription" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + ) +) + +Describe "Update-FabricLakehouse" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Update-FabricLakehouse + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Update-FabricLakehouse + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Update-FabricMLExperiment.Tests.ps1 b/tests/Unit/Update-FabricMLExperiment.Tests.ps1 new file mode 100644 index 00000000..42cad877 --- /dev/null +++ b/tests/Unit/Update-FabricMLExperiment.Tests.ps1 @@ -0,0 +1,49 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "MLExperimentId" + "MLExperimentName" + "MLExperimentDescription" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + ) +) + +Describe "Update-FabricMLExperiment" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Update-FabricMLExperiment + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Update-FabricMLExperiment + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Update-FabricMLModel.Tests.ps1 b/tests/Unit/Update-FabricMLModel.Tests.ps1 new file mode 100644 index 00000000..8763ff35 --- /dev/null +++ b/tests/Unit/Update-FabricMLModel.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "MLModelId" + "MLModelDescription" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + ) +) + +Describe "Update-FabricMLModel" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Update-FabricMLModel + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Update-FabricMLModel + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Update-FabricMirroredDatabase.Tests.ps1 b/tests/Unit/Update-FabricMirroredDatabase.Tests.ps1 new file mode 100644 index 00000000..58389317 --- /dev/null +++ b/tests/Unit/Update-FabricMirroredDatabase.Tests.ps1 @@ -0,0 +1,49 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "MirroredDatabaseId" + "MirroredDatabaseName" + "MirroredDatabaseDescription" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + ) +) + +Describe "Update-FabricMirroredDatabase" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Update-FabricMirroredDatabase + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Update-FabricMirroredDatabase + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Update-FabricMirroredDatabaseDefinition.Tests.ps1 b/tests/Unit/Update-FabricMirroredDatabaseDefinition.Tests.ps1 new file mode 100644 index 00000000..4234a76c --- /dev/null +++ b/tests/Unit/Update-FabricMirroredDatabaseDefinition.Tests.ps1 @@ -0,0 +1,49 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "MirroredDatabaseId" + "MirroredDatabasePathDefinition" + "MirroredDatabasePathPlatformDefinition" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + ) +) + +Describe "Update-FabricMirroredDatabaseDefinition" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Update-FabricMirroredDatabaseDefinition + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Update-FabricMirroredDatabaseDefinition + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Update-FabricNotebook.Tests.ps1 b/tests/Unit/Update-FabricNotebook.Tests.ps1 new file mode 100644 index 00000000..fd929b4a --- /dev/null +++ b/tests/Unit/Update-FabricNotebook.Tests.ps1 @@ -0,0 +1,49 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "NotebookId" + "NotebookName" + "NotebookDescription" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + ) +) + +Describe "Update-FabricNotebook" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Update-FabricNotebook + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Update-FabricNotebook + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Update-FabricNotebookDefinition.Tests.ps1 b/tests/Unit/Update-FabricNotebookDefinition.Tests.ps1 new file mode 100644 index 00000000..481c8c02 --- /dev/null +++ b/tests/Unit/Update-FabricNotebookDefinition.Tests.ps1 @@ -0,0 +1,49 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "NotebookId" + "NotebookPathDefinition" + "NotebookPathPlatformDefinition" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + ) +) + +Describe "Update-FabricNotebookDefinition" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Update-FabricNotebookDefinition + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Update-FabricNotebookDefinition + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Update-FabricPaginatedReport.Tests.ps1 b/tests/Unit/Update-FabricPaginatedReport.Tests.ps1 new file mode 100644 index 00000000..863cfbbf --- /dev/null +++ b/tests/Unit/Update-FabricPaginatedReport.Tests.ps1 @@ -0,0 +1,49 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "PaginatedReportId" + "PaginatedReportName" + "PaginatedReportDescription" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "Confirm" + "WhatIf" + ) +) + +Describe "Update-FabricPaginatedReport" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Update-FabricPaginatedReport + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Update-FabricPaginatedReport + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Update-FabricReflex.Tests.ps1 b/tests/Unit/Update-FabricReflex.Tests.ps1 new file mode 100644 index 00000000..1bc5d514 --- /dev/null +++ b/tests/Unit/Update-FabricReflex.Tests.ps1 @@ -0,0 +1,50 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "ReflexId" + "ReflexName" + "ReflexDescription" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + + ) +) + +Describe "Update-FabricReflex" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Update-FabricReflex + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Update-FabricReflex + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Update-FabricReflexDefinition.Tests.ps1 b/tests/Unit/Update-FabricReflexDefinition.Tests.ps1 new file mode 100644 index 00000000..99fd3083 --- /dev/null +++ b/tests/Unit/Update-FabricReflexDefinition.Tests.ps1 @@ -0,0 +1,49 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "ReflexId" + "ReflexPathDefinition" + "ReflexPathPlatformDefinition" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + ) +) + +Describe "Update-FabricReflexDefinition" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Update-FabricReflexDefinition + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Update-FabricReflexDefinition + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Update-FabricReport.Tests.ps1 b/tests/Unit/Update-FabricReport.Tests.ps1 new file mode 100644 index 00000000..342ebff8 --- /dev/null +++ b/tests/Unit/Update-FabricReport.Tests.ps1 @@ -0,0 +1,49 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "ReportId" + "ReportName" + "ReportDescription" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + ) +) + +Describe "Update-FabricReport" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Update-FabricReport + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Update-FabricReport + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Update-FabricReportDefinition.Tests.ps1 b/tests/Unit/Update-FabricReportDefinition.Tests.ps1 new file mode 100644 index 00000000..52862848 --- /dev/null +++ b/tests/Unit/Update-FabricReportDefinition.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "ReportId" + "ReportPathDefinition" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + ) +) + +Describe "Update-FabricReportDefinition" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Update-FabricReportDefinition + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Update-FabricReportDefinition + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Update-FabricSemanticModel.Tests.ps1 b/tests/Unit/Update-FabricSemanticModel.Tests.ps1 new file mode 100644 index 00000000..d9084fe5 --- /dev/null +++ b/tests/Unit/Update-FabricSemanticModel.Tests.ps1 @@ -0,0 +1,49 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "SemanticModelId" + "SemanticModelName" + "SemanticModelDescription" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + ) +) + +Describe "Update-FabricSemanticModel" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Update-FabricSemanticModel + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Update-FabricSemanticModel + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Update-FabricSemanticModelDefinition.Tests.ps1 b/tests/Unit/Update-FabricSemanticModelDefinition.Tests.ps1 new file mode 100644 index 00000000..6186124b --- /dev/null +++ b/tests/Unit/Update-FabricSemanticModelDefinition.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "SemanticModelId" + "SemanticModelPathDefinition" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + ) +) + +Describe "Update-FabricSemanticModelDefinition" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Update-FabricSemanticModelDefinition + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Update-FabricSemanticModelDefinition + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Update-FabricSparkCustomPool.Tests.ps1 b/tests/Unit/Update-FabricSparkCustomPool.Tests.ps1 new file mode 100644 index 00000000..bd8815ab --- /dev/null +++ b/tests/Unit/Update-FabricSparkCustomPool.Tests.ps1 @@ -0,0 +1,56 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "SparkCustomPoolId" + "InstancePoolName" + "NodeFamily" + "NodeSize" + "AutoScaleEnabled" + "AutoScaleMinNodeCount" + "AutoScaleMaxNodeCount" + "DynamicExecutorAllocationEnabled" + "DynamicExecutorAllocationMinExecutors" + "DynamicExecutorAllocationMaxExecutors" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + ) +) + +Describe "Update-FabricSparkCustomPool" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Update-FabricSparkCustomPool + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Update-FabricSparkCustomPool + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Update-FabricSparkJobDefinition.Tests.ps1 b/tests/Unit/Update-FabricSparkJobDefinition.Tests.ps1 new file mode 100644 index 00000000..68ff79ab --- /dev/null +++ b/tests/Unit/Update-FabricSparkJobDefinition.Tests.ps1 @@ -0,0 +1,49 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "SparkJobDefinitionId" + "SparkJobDefinitionName" + "SparkJobDefinitionDescription" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + ) +) + +Describe "Update-FabricSparkJobDefinition" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Update-FabricSparkJobDefinition + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Update-FabricSparkJobDefinition + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Update-FabricSparkJobDefinitionDefinition.Tests.ps1 b/tests/Unit/Update-FabricSparkJobDefinitionDefinition.Tests.ps1 new file mode 100644 index 00000000..1269fa0d --- /dev/null +++ b/tests/Unit/Update-FabricSparkJobDefinitionDefinition.Tests.ps1 @@ -0,0 +1,49 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "SparkJobDefinitionId" + "SparkJobDefinitionPathDefinition" + "SparkJobDefinitionPathPlatformDefinition" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + ) +) + +Describe "Update-FabricSparkJobDefinitionDefinition" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Update-FabricSparkJobDefinitionDefinition + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Update-FabricSparkJobDefinitionDefinition + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Update-FabricSparkSettings.Tests.ps1 b/tests/Unit/Update-FabricSparkSettings.Tests.ps1 new file mode 100644 index 00000000..eadda2cd --- /dev/null +++ b/tests/Unit/Update-FabricSparkSettings.Tests.ps1 @@ -0,0 +1,55 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "automaticLogEnabled" + "notebookInteractiveRunEnabled" + "customizeComputeEnabled" + "defaultPoolName" + "defaultPoolType" + "starterPoolMaxNode" + "starterPoolMaxExecutors" + "EnvironmentName" + "EnvironmentRuntimeVersion" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + ) +) + +Describe "Update-FabricSparkSettings" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Update-FabricSparkSettings + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Update-FabricSparkSettings + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Update-FabricWarehouse.Tests.ps1 b/tests/Unit/Update-FabricWarehouse.Tests.ps1 new file mode 100644 index 00000000..14eddb62 --- /dev/null +++ b/tests/Unit/Update-FabricWarehouse.Tests.ps1 @@ -0,0 +1,49 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "WarehouseId" + "WarehouseName" + "WarehouseDescription" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + ) +) + +Describe "Update-FabricWarehouse" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Update-FabricWarehouse + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Update-FabricWarehouse + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Update-FabricWorkspace.Tests.ps1 b/tests/Unit/Update-FabricWorkspace.Tests.ps1 new file mode 100644 index 00000000..14cd0ac6 --- /dev/null +++ b/tests/Unit/Update-FabricWorkspace.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "WorkspaceName" + "WorkspaceDescription" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + ) +) + +Describe "Update-FabricWorkspace" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Update-FabricWorkspace + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Update-FabricWorkspace + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Update-FabricWorkspaceRoleAssignment.Tests.ps1 b/tests/Unit/Update-FabricWorkspaceRoleAssignment.Tests.ps1 new file mode 100644 index 00000000..eb37ecb9 --- /dev/null +++ b/tests/Unit/Update-FabricWorkspaceRoleAssignment.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "WorkspaceRoleAssignmentId" + "WorkspaceRole" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + ) +) + +Describe "Update-FabricWorkspaceRoleAssignment" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Update-FabricWorkspaceRoleAssignment + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Update-FabricWorkspaceRoleAssignment + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Write-FabricLakehouseTableData.Tests.ps1 b/tests/Unit/Write-FabricLakehouseTableData.Tests.ps1 new file mode 100644 index 00000000..cd915691 --- /dev/null +++ b/tests/Unit/Write-FabricLakehouseTableData.Tests.ps1 @@ -0,0 +1,55 @@ +#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0"} +param( + $ModuleName = "FabricTools", + $expectedParams = @( + "WorkspaceId" + "LakehouseId" + "TableName" + "PathType" + "RelativePath" + "FileFormat" + "CsvDelimiter" + "CsvHeader" + "Mode" + "Recursive" + "Verbose" + "Debug" + "ErrorAction" + "WarningAction" + "InformationAction" + "ProgressAction" + "ErrorVariable" + "WarningVariable" + "InformationVariable" + "OutVariable" + "OutBuffer" + "PipelineVariable" + "WhatIf" + "Confirm" + ) +) + +Describe "Write-FabricLakehouseTableData" -Tag "UnitTests" { + + BeforeDiscovery { + $command = Get-Command -Name Write-FabricLakehouseTableData + $expected = $expectedParams + } + + Context "Parameter validation" { + BeforeAll { + $command = Get-Command -Name Write-FabricLakehouseTableData + $expected = $expectedParams + } + + It "Has parameter: <_>" -ForEach $expected { + $command | Should -HaveParameter $PSItem + } + + It "Should have exactly the number of expected parameters $($expected.Count)" { + $hasparms = $command.Parameters.Values.Name + #$hasparms.Count | Should -BeExactly $expected.Count + Compare-Object -ReferenceObject $expected -DifferenceObject $hasparms | Should -BeNullOrEmpty + } + } +}