Skip to content

Fixes #29764: refactor(ingestion): migrate powerbi test-connection to declarative framework#29767

Open
IceS2 wants to merge 9 commits into
mainfrom
migrate-powerbi-testconnection
Open

Fixes #29764: refactor(ingestion): migrate powerbi test-connection to declarative framework#29767
IceS2 wants to merge 9 commits into
mainfrom
migrate-powerbi-testconnection

Conversation

@IceS2

@IceS2 IceS2 commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Migrates the PowerBI dashboard connector's test-connection off the legacy test_connection_steps helper onto the declarative @check / ChecksProvider / ErrorPack framework. Closes #29764.

PowerBI is the first Dashboard-vertical connector to migrate, so this also seeds the shared Dashboard scaffolding that Tableau and Looker will reuse.

What changed

  • New core/connections/test_connection/checks/dashboard.pyDashboardStep enum (CheckAccess, GetDashboards) + REST-generic helpers verify_access (auth probe) and fetch_list (count summary), modeled on checks/database.py.
  • powerbi/connection.py — dropped the test_connection override; added PowerBIChecks (checks() provider) with a CheckAccess OAuth gate that acquires a token so a bad service principal fails fast, and GetDashboards for list access. No network call happens while building the provider.
  • powerbi.json — added the CheckAccess gate step (category: ConnectionGate, shortCircuit, mandatory, ordered first); kept GetDashboards.
  • Error pack — HTTP-status matchers (401 auth / 403 permissions / 404 not found) plus InvalidSourceException → auth, folding in the shared NETWORK_ERRORS for DNS/refused/timeout. Matches on the REST client's APIError.status_code.
  • Colocated unit test asserting each step resolves, the gate stays offline at build time, and the error pack classifies each known scenario.

Notes

  • The exact exception shape PowerBI's REST client raises per HTTP status (APIError.status_code vs message tokens) is confirmed live against a real tenant before merge.
  • Duplication with future dashboard connectors is intentionally left for the later shared-consolidation phase.

Greptile Summary

Migrates PowerBI's test-connection from the legacy test_connection_steps helper to the declarative @check / ChecksProvider / ErrorPack framework, and seeds the shared checks/dashboard.py scaffolding that Tableau and Looker will reuse.

  • checks/dashboard.py — adds DashboardStep enum, verify_access (auth probe wrapping failures as CheckError), and fetch_list (count summary with a 100+ cap for large tenants), mirroring the existing checks/database.py pattern.
  • powerbi/connection.py — replaces the test_connection override with PowerBIChecks, a lazy-client provider whose CheckAccess gate acquires an OAuth token before any REST call is made; POWERBI_ERRORS covers InvalidSourceException, HTTP 401/403/404, MSAL authority errors, and network errors in priority order.
  • powerbi.json — adds CheckAccess as a mandatory / shortCircuit ConnectionGate step ordered before GetDashboards; the unit tests validate step discovery, evidence content, cap behavior, client-build failures, and the full error-pack classification matrix against both APIError and raw HTTPError shapes.

Confidence Score: 5/5

Safe to merge — the migration is a clean drop-in replacement with no logic changes to the ingestion path and comprehensive unit-test coverage of every code branch.

The change is well-scoped: the get_connection function and _get_client path are untouched, so live ingestion is unaffected. The declarative checks are thoroughly tested including APIError vs raw HTTPError shapes, cap boundary behavior, lazy client construction failures, and the complete error-pack classification matrix. The gate ordering in POWERBI_ERRORS is carefully reasoned and verified by a dedicated test case. No unguarded code paths or missing error classifications were found.

No files require special attention.

Important Files Changed

Filename Overview
ingestion/src/metadata/core/connections/test_connection/checks/dashboard.py New shared helpers for dashboard connectors: DashboardStep enum (CheckAccess, GetDashboards), verify_access (auth probe), and fetch_list (count summary with cap). Well-structured and modeled cleanly after database.py.
ingestion/src/metadata/ingestion/source/dashboard/powerbi/connection.py Drops the legacy test_connection_steps override; adds PowerBIChecks with lazy client construction, a CheckAccess gate, GetDashboards check, and a well-ordered POWERBI_ERRORS pack covering 401/403/404/authority failures and NETWORK_ERRORS.
ingestion/tests/unit/source/dashboard/powerbi/test_connection.py Comprehensive unit tests covering both APIError and raw HTTPError shapes, cap boundary, empty/None results, client-build failures, and the full error-pack classification matrix; all gating behavior is exercised.
openmetadata-service/src/main/resources/json/data/testConnections/dashboard/powerbi.json Adds the CheckAccess step as a mandatory, short-circuit ConnectionGate ordered before GetDashboards; trailing-whitespace cleanup only other change.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant Runner
    participant PowerBIChecks
    participant PowerBiApiClient
    participant MSAL
    participant PowerBIRestAPI

    Runner->>PowerBIChecks: checks()
    Note over PowerBIChecks: No network I/O at construction

    Runner->>PowerBIChecks: check_access() [CheckAccess gate]
    PowerBIChecks->>PowerBIChecks: _client() - build PowerBiApiClient lazily
    PowerBiApiClient->>MSAL: ConfidentialClientApplication(authority)
    PowerBIChecks->>PowerBiApiClient: get_auth_token()
    PowerBiApiClient->>MSAL: acquire_token_for_client(scope)
    MSAL-->>PowerBiApiClient: token or error dict
    alt token missing
        PowerBiApiClient-->>PowerBIChecks: raise InvalidSourceException
        PowerBIChecks-->>Runner: raise CheckError
        Note over Runner: shortCircuit - skip GetDashboards
    else token OK
        PowerBiApiClient-->>PowerBIChecks: access_token
        PowerBIChecks-->>Runner: Evidence authenticated
    end

    Runner->>PowerBIChecks: get_dashboards() [GetDashboards]
    PowerBIChecks->>PowerBiApiClient: fetch_dashboards()
    PowerBiApiClient->>PowerBIRestAPI: GET /myorg/admin/dashboards
    PowerBIRestAPI-->>PowerBiApiClient: dashboards list or HTTP error
    alt HTTP error 401/403/404
        PowerBiApiClient-->>PowerBIChecks: raise APIError or HTTPError
        PowerBIChecks-->>Runner: raise CheckError
    else success
        PowerBiApiClient-->>PowerBIChecks: list of PowerBIDashboard
        PowerBIChecks-->>Runner: Evidence N dashboards enumerated
    end
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant Runner
    participant PowerBIChecks
    participant PowerBiApiClient
    participant MSAL
    participant PowerBIRestAPI

    Runner->>PowerBIChecks: checks()
    Note over PowerBIChecks: No network I/O at construction

    Runner->>PowerBIChecks: check_access() [CheckAccess gate]
    PowerBIChecks->>PowerBIChecks: _client() - build PowerBiApiClient lazily
    PowerBiApiClient->>MSAL: ConfidentialClientApplication(authority)
    PowerBIChecks->>PowerBiApiClient: get_auth_token()
    PowerBiApiClient->>MSAL: acquire_token_for_client(scope)
    MSAL-->>PowerBiApiClient: token or error dict
    alt token missing
        PowerBiApiClient-->>PowerBIChecks: raise InvalidSourceException
        PowerBIChecks-->>Runner: raise CheckError
        Note over Runner: shortCircuit - skip GetDashboards
    else token OK
        PowerBiApiClient-->>PowerBIChecks: access_token
        PowerBIChecks-->>Runner: Evidence authenticated
    end

    Runner->>PowerBIChecks: get_dashboards() [GetDashboards]
    PowerBIChecks->>PowerBiApiClient: fetch_dashboards()
    PowerBiApiClient->>PowerBIRestAPI: GET /myorg/admin/dashboards
    PowerBIRestAPI-->>PowerBiApiClient: dashboards list or HTTP error
    alt HTTP error 401/403/404
        PowerBiApiClient-->>PowerBIChecks: raise APIError or HTTPError
        PowerBIChecks-->>Runner: raise CheckError
    else success
        PowerBiApiClient-->>PowerBIChecks: list of PowerBIDashboard
        PowerBIChecks-->>Runner: Evidence N dashboards enumerated
    end
Loading

Reviews (6): Last reviewed commit: "fix(powerbi): build client inside fetch_..." | Re-trigger Greptile

… declarative framework

Migrate the PowerBI dashboard connector's test-connection onto the
declarative @check / ChecksProvider / ErrorPack framework. PowerBI is the
first Dashboard-vertical connector to migrate, so this also seeds the shared
Dashboard scaffolding (checks/dashboard.py: DashboardStep enum + REST-generic
verify_access/fetch_list helpers) that Tableau and Looker will reuse.

- Add a CheckAccess OAuth gate step (ConnectionGate) that acquires a token so
  a bad service principal fails fast before any list endpoint is dialled.
- Keep GetDashboards as the list-access step.
- HTTP-status-aware error pack (401/403/404) folding in the shared network pack.
@github-actions github-actions Bot added Ingestion safe to test Add this label to run secure Github workflows on PRs labels Jul 6, 2026
Comment thread ingestion/src/metadata/ingestion/source/dashboard/powerbi/connection.py Outdated
… count

The MSAL client that PowerBiApiClient builds in its constructor performs
authority/instance discovery over the network. Building it while constructing
the checks provider (via self.client) ran that network call before the runner's
gate, so a bad authority/tenant surfaced as a raw automation-workflow error
instead of a classified CheckAccess failure. Build the client lazily inside the
first check instead, and classify MSAL authority errors.

Also cap the GetDashboards count summary (100+) so a huge tenant does not
produce an unbounded figure.
IceS2 added 2 commits July 6, 2026 16:44
An empty Tenant ID makes MSAL raise a differently-worded ValueError ("...should
consist of an https url") that the "authority" token missed, leaving CheckAccess
unclassified. Match both message shapes.
…last

Address review on the error pack:
- _http_status now falls back to .response.status_code, so a raw
  requests.HTTPError (which the REST client re-raises when the error body has no
  "code" field) is still classified as 401/403/404 instead of falling through.
- Move the broad "authority" substring matcher below the HTTP-status matchers so
  a status-coded 401 whose message echoes the authority URL is not misclassified
  as an authority error. MSAL authority ValueErrors carry no status, so they
  still reach the rule.
@IceS2

IceS2 commented Jul 6, 2026

Copy link
Copy Markdown
Contributor Author

Both review findings addressed in b798dca:

  1. Raw HTTPError status_http_status now falls back to .response.status_code, so a bare requests.HTTPError (re-raised by the REST client when the error body lacks a code field) is still classified as 401/403/404. Added tests covering the raw-HTTPError path.
  2. Matcher ordering — moved the broad authority substring matcher below the HTTP-status matchers. MSAL authority failures are ValueErrors with no status, so they still reach the rule, while a status-coded 401 that happens to echo the authority URL now classifies as "Authentication failed". Added a test locking this.

@gitar-bot

gitar-bot Bot commented Jul 6, 2026

Copy link
Copy Markdown
Code Review ✅ Approved 2 resolved / 2 findings

Migrates PowerBI test-connection to the declarative framework and adds a shared dashboard scaffolding, resolving the HTTPError classification and authority matcher shadowing issues.

✅ 2 resolved
Bug: HTTP-status matcher misses raw requests.HTTPError (only APIError)

📄 ingestion/src/metadata/ingestion/source/dashboard/powerbi/connection.py:50-64 📄 ingestion/src/metadata/ingestion/source/dashboard/powerbi/connection.py:69-83 📄 ingestion/tests/unit/source/dashboard/powerbi/test_connection.py:32-35
_http_status classifies errors by reading .status_code off each exception in the cause chain. Only metadata.ingestion.ometa.client.APIError exposes a top-level .status_code property. The PowerBI REST client only wraps an error in APIError when the error response body contains a "code" field (client.py:323-326); when it does not, it re-raises the raw requests.exceptions.HTTPError unchanged (client.py:327-328). A raw HTTPError has no .status_code attribute (the status lives at err.response.status_code), so getattr(current, "status_code", None) returns None and none of the 401/403/404 matchers fire — the failure falls through to "unknown" and the user gets a generic message instead of the tailored auth/permission/not-found guidance this PR adds.

Azure AD / PowerBI 401 and 403 responses frequently return bodies without a "code" key, so this is a realistic path, not an edge case. The colocated tests only build APIError instances (via _api_error, which sets http_error.response.status_code), so the raw-HTTPError path is never exercised and the gap is invisible in CI. This is also the exact exception-shape uncertainty called out in the PR description.

Suggested fix: have the matcher also consult .response.status_code (and/or handle requests.HTTPError) in addition to .status_code, and add a test covering a raw HTTPError with no "code" body.

Edge Case: 'authority' matcher ordered first may shadow 401 auth errors

📄 ingestion/src/metadata/ingestion/source/dashboard/powerbi/connection.py:69-83
Matchers.contains("authority") is placed first in POWERBI_ERRORS, and ErrorPack.classify is first-match-wins over a case-insensitive substring search across the whole exception chain (classifier.py). Azure AD / MSAL 401 error responses frequently echo the authority URL (e.g. https://login.microsoftonline.com/<tenant>) in their message. If such a 401 APIError message contains the substring "authority", it will be classified as "Invalid tenant or authority" instead of the more accurate "Authentication failed" (_http_status(401)), giving the user a misleading fix hint (check tenant/authority URI) for what is actually a bad client secret. The genuine authority-discovery errors are MSAL ValueErrors that carry no status_code and are not InvalidSourceException, so moving this rule below the 401/InvalidSourceException/403/404 matchers preserves its detection while letting HTTP-status signals win. The PR notes the exact exception shapes are to be confirmed live before merge, so this is a pre-merge caution.

Options

Display: compact → Showing less information.

Comment with these commands to change the behavior for this request:

Compact
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

@IceS2 IceS2 marked this pull request as ready for review July 6, 2026 15:12
@IceS2 IceS2 requested a review from a team as a code owner July 6, 2026 15:12
Copilot AI review requested due to automatic review settings July 6, 2026 15:12
@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

✅ PR checks passed

The linked issue has a description and all required Shipping project fields set. Thanks!

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR migrates the PowerBI dashboard connector’s test-connection implementation from the legacy test_connection_steps helper to the declarative @check / ChecksProvider / ErrorPack framework, while also introducing shared “dashboard-vertical” scaffolding intended for reuse by other dashboard connectors.

Changes:

  • Added a new dashboard test-connection checks module with DashboardStep and shared REST-oriented helpers (verify_access, fetch_list).
  • Refactored the PowerBI connector to expose checks() via a new PowerBIChecks provider, including an HTTP-status-aware ErrorPack and lazy REST client construction.
  • Updated the PowerBI test-connection definition JSON to add a CheckAccess ConnectionGate step, and added/updated unit tests for the new checks + classification behavior.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

File Description
openmetadata-service/src/main/resources/json/data/testConnections/dashboard/powerbi.json Adds the CheckAccess gate step ahead of GetDashboards in the PowerBI test-connection definition.
ingestion/tests/unit/source/dashboard/powerbi/test_connection.py Updates unit tests to validate the new checks provider behavior and error classification.
ingestion/src/metadata/ingestion/source/dashboard/powerbi/connection.py Replaces the legacy test_connection override with a checks() provider, lazy client creation, and a PowerBI-specific ErrorPack.
ingestion/src/metadata/core/connections/test_connection/checks/dashboard.py Introduces shared dashboard-step identity (DashboardStep) and generic REST check helpers for dashboard connectors.

Comment thread ingestion/src/metadata/ingestion/source/dashboard/powerbi/connection.py Outdated
…in matcher

Address review:
- The checks provider builds PowerBiApiClient directly instead of via
  get_connection, so the file client (which the checks never use, and which does
  I/O for pbit sources) is not constructed. get_connection stays the ingestion
  path.
- _http_status returns on the first matching status instead of scanning the rest
  of the cause chain.
@IceS2

IceS2 commented Jul 6, 2026

Copy link
Copy Markdown
Contributor Author

Thanks @greptile-apps — both applied in the latest commit:

  • _client() now constructs PowerBiApiClient(self._connection) directly, so the unused PowerBiFileClient is never built for a test connection (get_connection remains the ingestion path).
  • _http_status early-returns on the first matching status in the cause chain.

@greptile-apps

greptile-apps Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Both fixes look correct:

  1. _client() direct constructionPowerBiApiClient(self._connection) is called directly, with a clear docstring explaining the rationale. No get_connection() indirection, no unused PowerBiFileClient.

  2. _http_status early-return — the match function iterates exception_chain, checks both .status_code and .response.status_code, and returns True on the first hit.

The parametrized test for _api_error(401, "AADSTS700016 ... authority ...") is a nice regression guard — it confirms that a 401 whose message happens to contain the word "authority" still classifies as "Authentication failed" and isn't shadowed by the broad _contains_any("authority", ...) matcher. Both issues are resolved.

Tip: You can customize Greptile's behavior for this repo with .greptile/rules.md and .greptile/config.json.

@IceS2

IceS2 commented Jul 7, 2026

Copy link
Copy Markdown
Contributor Author

Good catch on the 401-vs-403 question — I checked the Microsoft docs and the original split was wrong. Fixed in 5456918.

Per Troubleshoot Power BI REST APIs (Microsoft Learn):

  • A service principal that isn't enabled for the (admin) APIs — not in the security group, 'Allow service principals to use Power BI APIs' off, missing app permission, or wrong token resource — returns 401, which the doc explicitly calls "a tenant or app registration configuration gap rather than an authentication failure."
  • 403 is documented for workspace-membership / capacity / expired-token cases.

So the docs do not back up "no-access SP → 403"; the enablement case is 401. Also, since CheckAccess acquires the OAuth token first, any 401/403 at GetDashboards happens after a valid token — so it's never a bad-secret problem, and the old 401 → "Authentication failed" (check your secret) text was actively misleading.

Reframed accordingly:

  • 401 → "Power BI did not authorize the service principal" — enable the SP for Power BI/admin APIs, add it to the security group, grant the app permission.
  • 403 → "Insufficient permissions" — add the SP to the target workspace and grant resource access.
  • Credential failures stay on the CheckAccess InvalidSourceException path ("Authentication failed").

Which exact code a no-access SP returns can still vary by endpoint (admin vs group APIs), but both now map to authorization guidance, so the hint is correct either way. Will confirm the precise code live against the tenant.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

Comment on lines +34 to +36
# A check only needs to prove the list endpoint is reachable and returns items,
# not enumerate every one, so ``fetch_list`` reports the count up to this cap and
# marks it ``+`` beyond, keeping the summary bounded on huge tenants.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 777f978 — wording is now "meets or exceeds", and the count is capped so <cap>+ only ever renders at the boundary.

Comment on lines +80 to +83
Reports the command it attempted and a count summary, capped at ``cap`` (shown
as ``<cap>+`` beyond) so a huge tenant does not produce an unbounded figure. On
failure, re-raise as ``CheckError`` carrying the command so the failed step
still reports what it ran.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 777f978 — docstring now says "meets or exceeds" and matches the >= behavior.

…caveat

Review follow-ups on the shared dashboard helpers:
- fetch_list caps the counted number at cap (min), so the "<cap>+" boundary is
  exact and a bare number never exceeds the cap; docs say "meets or exceeds".
- fetch_list takes an optional empty_caveat: when the endpoint returns nothing
  the step still passes but carries a non-blocking Warning, so an empty (or
  silently-filtered/failed) listing is visible rather than a silent green.
  PowerBI's GetDashboards opts in with a "No dashboards visible" caveat.
Khairajani
Khairajani previously approved these changes Jul 7, 2026
Copilot AI review requested due to automatic review settings July 7, 2026 13:01

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

…shboards

Pass a zero-arg lambda so the lazy _client() construction (MSAL authority
discovery) runs inside fetch_list's try/except. A build failure is then wrapped
as a CheckError carrying the step command instead of escaping raw, matching the
CheckAccess path.
@gitar-bot

gitar-bot Bot commented Jul 7, 2026

Copy link
Copy Markdown
Code Review ✅ Approved 2 resolved / 2 findings

Migrates PowerBI test-connection to the declarative framework, establishing a reusable scaffold for dashboard connectors. This change resolves previous issues with raw HTTP error handling and matcher ordering through improved status-code classification.

✅ 2 resolved
Bug: HTTP-status matcher misses raw requests.HTTPError (only APIError)

📄 ingestion/src/metadata/ingestion/source/dashboard/powerbi/connection.py:50-64 📄 ingestion/src/metadata/ingestion/source/dashboard/powerbi/connection.py:69-83 📄 ingestion/tests/unit/source/dashboard/powerbi/test_connection.py:32-35
_http_status classifies errors by reading .status_code off each exception in the cause chain. Only metadata.ingestion.ometa.client.APIError exposes a top-level .status_code property. The PowerBI REST client only wraps an error in APIError when the error response body contains a "code" field (client.py:323-326); when it does not, it re-raises the raw requests.exceptions.HTTPError unchanged (client.py:327-328). A raw HTTPError has no .status_code attribute (the status lives at err.response.status_code), so getattr(current, "status_code", None) returns None and none of the 401/403/404 matchers fire — the failure falls through to "unknown" and the user gets a generic message instead of the tailored auth/permission/not-found guidance this PR adds.

Azure AD / PowerBI 401 and 403 responses frequently return bodies without a "code" key, so this is a realistic path, not an edge case. The colocated tests only build APIError instances (via _api_error, which sets http_error.response.status_code), so the raw-HTTPError path is never exercised and the gap is invisible in CI. This is also the exact exception-shape uncertainty called out in the PR description.

Suggested fix: have the matcher also consult .response.status_code (and/or handle requests.HTTPError) in addition to .status_code, and add a test covering a raw HTTPError with no "code" body.

Edge Case: 'authority' matcher ordered first may shadow 401 auth errors

📄 ingestion/src/metadata/ingestion/source/dashboard/powerbi/connection.py:69-83
Matchers.contains("authority") is placed first in POWERBI_ERRORS, and ErrorPack.classify is first-match-wins over a case-insensitive substring search across the whole exception chain (classifier.py). Azure AD / MSAL 401 error responses frequently echo the authority URL (e.g. https://login.microsoftonline.com/<tenant>) in their message. If such a 401 APIError message contains the substring "authority", it will be classified as "Invalid tenant or authority" instead of the more accurate "Authentication failed" (_http_status(401)), giving the user a misleading fix hint (check tenant/authority URI) for what is actually a bad client secret. The genuine authority-discovery errors are MSAL ValueErrors that carry no status_code and are not InvalidSourceException, so moving this rule below the 401/InvalidSourceException/403/404 matchers preserves its detection while letting HTTP-status signals win. The PR notes the exact exception shapes are to be confirmed live before merge, so this is a pre-merge caution.

Options

Display: compact → Showing less information.

Comment with these commands to change the behavior for this request:

Compact
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

@sonarqubecloud

sonarqubecloud Bot commented Jul 7, 2026

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Ingestion safe to test Add this label to run secure Github workflows on PRs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

migrate PowerBI test-connection to the @check framework

3 participants