From 536bb4d9681785c184c983826483c410997b2374 Mon Sep 17 00:00:00 2001 From: marcusgraca Date: Tue, 17 Jun 2025 19:55:41 +0100 Subject: [PATCH 1/4] Implemented PBI Inspector for integrity check --- .github/workflows/validate-powerbi.yml | 38 ++ rules/pbi-inspector-rules.json | 497 +++++++++++++++++++++++++ 2 files changed, 535 insertions(+) create mode 100644 .github/workflows/validate-powerbi.yml create mode 100644 rules/pbi-inspector-rules.json diff --git a/.github/workflows/validate-powerbi.yml b/.github/workflows/validate-powerbi.yml new file mode 100644 index 0000000..d815c98 --- /dev/null +++ b/.github/workflows/validate-powerbi.yml @@ -0,0 +1,38 @@ +name: Validate Power BI report + +on: + pull_request: + branches: [ main ] + paths: + - '**.pbip' + - '**.tmdl' + - 'rules/**' + +jobs: + validate: + runs-on: windows-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: 8.0.x + + - name: Clone PBIRInspectorV2 + run: git clone https://github.com/NatVanG/PBI-InspectorV2.git + + - name: Build PBIRInspectorCLI + run: | + cd PBI-InspectorV2/PBIRInspectorCLI + dotnet build -c Release + + - name: Run validation + run: | + .\PBI-InspectorV2\PBIRInspectorCLI\bin\Release\net8.0\PBIRInspectorCLI.exe ` + -fabricitem "${{ github.workspace }}\CareTogether.Report" ` + -rules "${{ github.workspace }}\rules\pbi-inspector-rules.json" ` + -formats "Console,GitHub" ` + -failOnIssue true \ No newline at end of file diff --git a/rules/pbi-inspector-rules.json b/rules/pbi-inspector-rules.json new file mode 100644 index 0000000..c2354fd --- /dev/null +++ b/rules/pbi-inspector-rules.json @@ -0,0 +1,497 @@ +{ + "rules": [ + { + "id": "REMOVE_UNUSED_CUSTOM_VISUALS", + "name": "Remove custom visuals which are not used in the report.", + "description": "Returns an array of custom visual names to be removed if any. To disable this rule, mark it as disabled in the base rules file.", + "disabled": false, + "part": "Report", + "test": [ + { + "diff": [ + { + "var": "customvis" + }, + { + "map": [ + { "part": "Visuals" }, + { "var": "visual.visualType" } + ] + } + ] + }, + { + "customvis": "/publicCustomVisuals" + }, + [] + ] + }, + { + "id": "REDUCE_VISUALS_ON_PAGE", + "name": "Reduce the number of visible visuals on the page", + "description": "Reports a test fail if the rule's maximum number of visible visuals on the page is exceeded. By default the base rules file specifies 20 as the maximum, set the paramMaxVisualsPerPage parameter to change this. To disable this rule, mark it as disabled in the base rules file.", + "disabled": false, + "part": "Pages", + "test": [ + { + "<=": [ + { + "count": [ + { + "filter": [ + { + "part": "Visuals" + }, + { + "and": [ + { + "!": [ + { + "var": "isHidden" + } + ] + }, + { + "!": [ + { + "in": [ + { + "var": "visual.visualType" + }, + [ + "shape", + "slicer", + "actionButton", + "textbox" + ] + ] + } + ] + } + ] + } + ] + } + ] + }, + { + "var": "paramMaxVisualsPerPage" + } + ] + }, + { + "paramMaxVisualsPerPage": 20 + }, + true + ] + }, + { + "id": "REDUCE_OBJECTS_WITHIN_VISUALS", + "name": "Reduce the number of objects within visuals", + "description": "Reports a test fail if the rule's maximum number of objects within visuals on a page is exceeded. An object is a data field that is assigned to a visual. By default, the base rules file specifies 6 as the maximum objects within a visual. To disable this rule, mark it as disabled in the base rules file.", + "disabled": false, + "part": "Pages", + "test": [ + { + "map": [ + { + "filter": [ + { + "part": "Visuals" + }, + { + ">": [ + { + "count": [ + { + "path": "$..projections[*]" + } + ] + }, + 6 + ] + } + ] + }, + { + "var": "name" + } + ] + }, + {}, + [] + ] + }, + { + "id": "REDUCE_TOPN_FILTERS", + "name": "Reduce usage of TopN filtering visuals by page", + "description": "Reports a test fail if the rule's maximum number of visuals using TopN filtering on a the page is exceeded. By default the base rules file specifies 4 as the maximum objects within a visual, set the paramMaxTopNFilteringPerPage to change this. To disable this rule, mark it as disabled in the base rules file.", + "part": "Pages", + "disabled": false, + "test": [ + { + "<=": [ + { + "count": [ + { + "filter": [ + { + "part": "Visuals" + }, + { + "some": [ + { + "var": "filterConfig.filters" + }, + { + "==": [ + { + "var": "type" + }, + "TopN" + ] + } + ] + } + ] + } + ] + }, + { + "var": "paramMaxTopNFilteringPerPage" + } + ] + }, + { + "paramMaxTopNFilteringPerPage": 4 + }, + true + ] + }, + { + "id": "REDUCE_ADVANCED_FILTERS", + "name": "Reduce usage of Advanced filtering visuals by page", + "description": "Reports a test fail if the rule's maximum number of visuals using Advanced filtering on a the page is exceeded. By default, the base rules file specifies 4 as the maximum objects within a visual, set the paramMaxAdvancedFilteringVisualsPerPage parameter value to change this. To disable this rule, mark it as disabled in the base rules file", + "part": "Pages", + "disabled": false, + "test": [ + { + "<=": [ + { + "count": [ + { + "filter": [ + { + "part": "Visuals" + }, + { + "some": [ + { + "var": "filterConfig.filters" + }, + { + "==": [ + { + "var": "type" + }, + "Advanced" + ] + } + ] + } + ] + } + ] + }, + { + "var": "paramMaxAdvancedFilteringPerPage" + } + ] + }, + { + "paramMaxAdvancedFilteringPerPage": 4 + }, + true + ] + }, + { + "id": "REDUCE_PAGES", + "name": "Reduce number of pages per report", + "description": "Reports a test fail if the rule's maximum number of pages per report is exceeded. By default, the base rules file specifies 10 as the maximum number of pages. Set the paramMaxNumberOfPagesPerReport parameter to change this. To disable this rule, mark it as disabled in the base rules file.", + "disabled": false, + "test": [ + { + "<=": [ + { + "count": [ + { + "part": "Pages" + } + ] + }, + 10 + ] + }, + {}, + true + ] + }, + { + "id": "AVOID_SHOW_ITEMS_WITH_NO_DATA", + "name": "Avoid setting ‘Show items with no data’ on columns", + "description": "Returns an array of visual names which have the option ‘Show items with no data’ enabled on one or more columns. To disable this rule, mark it as disabled in the base rules file.", + "disabled": false, + "part": "Pages", + "test": [ + { + "map": [ + { + "filter": [ + { + "part": "Visuals" + }, + { + "==": [ + { + "var": "visual.query.queryState.Category.showAll" + }, + true + ] + } + ] + + }, + { + "var": "name" + } + ] + }, + { + }, + [] + ] + }, + { + "id": "HIDE_TOOLTIP_DRILLTROUGH_PAGES", + "name": "Tooltip and Drillthrough pages should be hidden", + "description": "Reports a test fail if a page of type Tooltip or Drillthrough is visible. To disable this rule, mark it as disabled in the base rules file.", + "disabled": false, + "test": [ + { + "map": [ + { + "filter": [ + { + "part": "Pages" + }, + { + "and": [ + { + "in": [ + { + "var": "pageBinding.type" + }, + [ + "Tooltip", + "Drillthrough" + ] + ] + }, + { + "!=": [ + { + "var": "visibility" + }, + "HiddenInViewMode" + ] + } + ] + } + ] + }, + { "var": "displayName" } + ] + }, + { + }, + [] + ] + }, + { + "id": "ENSURE_THEME_COLOURS", + "name": "Ensure charts use theme colours", + "description": "Check that charts (excluding textboxes) avoid custom colours and use theme colours instead. To disable this rule, mark it as disabled in the base rules file.", + "disabled": false, + "part": "Pages", + "test": [ + { + "map": [ + { + "filter": [ + { + "part": "Visuals" + }, + { + "and": [ + { + "!": [ + { + "in": [ + { + "var": "visual.visualType" + }, + [ + "textbox" + ] + ] + } + ] + }, + { + "strcontains": [ + { + "tostring": [ + { + "var": "" + } + ] + }, + "#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})" + ] + } + ] + } + ] + }, + { + "var": "name" + } + ] + }, + { + }, + [] + ] + }, + { + "id": "ENSURE_PAGES_DO_NOT_SCROLL_VERTICALLY", + "name": "Ensure pages do not scroll vertically", + "description": "Returns an array of visible page names with a height great than 720px. Modify the rule parameter value if a different maximum height value is required. To disable this rule, mark it as disabled in the base rules file.", + "disabled": false, + "test": [ + { + "map": [ + { + "filter": [ + { + "part": "Pages" + }, + { + "and": [ + { + ">": [ + { + "var": "height" + }, + 720 + ] + }, + { + "!=": [ + { + "var": "visibility" + }, + "HiddenInViewMode" + ] + } + ] + } + ] + }, + { + "var": "displayName" + } + ] + }, + { + }, + [] + ] + }, + { + "id": "ENSURE_ALTTEXT", + "name": "Ensure alternativeText has been defined for all visuals", + "description": "Alt-text is required for screen readers", + "disabled": true, + "part": "Pages", + "test": [ + { + "map": [ + { + "filter": [ + { + "part": "Visuals" + }, + { + "and": [ + { + "!": [ + { + "in": [ + { + "var": "visual.visualType" + }, + [ + "shape" + ] + ] + } + ] + }, + { + "none": [ + { + "var": "visual.visualContainerObjects.general" + }, + { + "or": [ + { + "!!": [ { "var": "properties.altText.expr.Aggregation" } ] + }, + { + "!=": [ + { "var": "properties.altText.expr.Literal.Value" }, + "''" + ] + } + ] + } + ] + } + ] + } + ] + }, + { + "var": "name" + } + ] + }, + {}, + [] + ] + }, + { + "id": "template", + "name": "Rule Template", + "description": "Rule template", + "disabled": true, + "logType": "warning", + "test": [ + true, + {}, + true + ] + } + ] +} \ No newline at end of file From 2e79e023a6a405dffacb1a17394a10d613354e4d Mon Sep 17 00:00:00 2001 From: marcusgraca Date: Tue, 17 Jun 2025 20:03:49 +0100 Subject: [PATCH 2/4] Fixing the YML file --- .github/workflows/validate-powerbi.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/validate-powerbi.yml b/.github/workflows/validate-powerbi.yml index d815c98..e02e654 100644 --- a/.github/workflows/validate-powerbi.yml +++ b/.github/workflows/validate-powerbi.yml @@ -32,7 +32,7 @@ jobs: - name: Run validation run: | .\PBI-InspectorV2\PBIRInspectorCLI\bin\Release\net8.0\PBIRInspectorCLI.exe ` - -fabricitem "${{ github.workspace }}\CareTogether.Report" ` - -rules "${{ github.workspace }}\rules\pbi-inspector-rules.json" ` + -fabricitem "${{ github.workspace }}/CareTogether.Report" ` + -rules "${{ github.workspace }}/rules/pbi-inspector-rules.json" ` -formats "Console,GitHub" ` - -failOnIssue true \ No newline at end of file + -failOnIssue true From c6b4aafbe745b6a88cf949a0ebe8c9dc2b1d0455 Mon Sep 17 00:00:00 2001 From: marcusgraca Date: Tue, 17 Jun 2025 20:14:32 +0100 Subject: [PATCH 3/4] Adapting the logic to block the PR if the validation fails --- .github/workflows/validate-powerbi.yml | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/.github/workflows/validate-powerbi.yml b/.github/workflows/validate-powerbi.yml index e02e654..8a0de8e 100644 --- a/.github/workflows/validate-powerbi.yml +++ b/.github/workflows/validate-powerbi.yml @@ -29,10 +29,17 @@ jobs: cd PBI-InspectorV2/PBIRInspectorCLI dotnet build -c Release - - name: Run validation + - name: Run PBIRInspector validation + shell: pwsh run: | - .\PBI-InspectorV2\PBIRInspectorCLI\bin\Release\net8.0\PBIRInspectorCLI.exe ` - -fabricitem "${{ github.workspace }}/CareTogether.Report" ` - -rules "${{ github.workspace }}/rules/pbi-inspector-rules.json" ` - -formats "Console,GitHub" ` - -failOnIssue true + $output = & .\PBI-InspectorV2\PBIRInspectorCLI\bin\Release\net8.0\PBIRInspectorCLI.exe ` + -fabricitem "${{ github.workspace }}\CareTogether.Report" ` + -rules "${{ github.workspace }}\rules\pbi-inspector-rules.json" ` + -formats "Console,GitHub" + + Write-Output $output + + if ($output -match "❌") { + Write-Error "PBIRInspector found issues in the report." + exit 1 + } From 76b363e5611afc17d8fb85090447e789b3f7e3ad Mon Sep 17 00:00:00 2001 From: marcusgraca Date: Tue, 17 Jun 2025 20:23:12 +0100 Subject: [PATCH 4/4] Adjusted the YML file to block the PR in Warnings --- .github/workflows/validate-powerbi.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/validate-powerbi.yml b/.github/workflows/validate-powerbi.yml index 8a0de8e..5be8166 100644 --- a/.github/workflows/validate-powerbi.yml +++ b/.github/workflows/validate-powerbi.yml @@ -39,7 +39,7 @@ jobs: Write-Output $output - if ($output -match "❌") { - Write-Error "PBIRInspector found issues in the report." + if ($output -match "Warning:") { + Write-Error "Validation failed due to rule violations." exit 1 }