codeant-ai-for-open-source[bot] commented on code in PR #38638:
URL: https://github.com/apache/superset/pull/38638#discussion_r2938043612
##########
tests/integration_tests/charts/data/api_tests.py:
##########
@@ -1810,3 +1810,200 @@ def test_chart_data_subquery_allowed(
rv = test_client.post(CHART_DATA_URI, json=physical_query_context)
assert rv.status_code == status_code
+
+
+class TestGetChartDataWithDashboardFilter(BaseTestChartDataApi):
Review Comment:
**Suggestion:** This new test class uses the same birth-names fixture/query
flow that is explicitly skipped elsewhere in this file due to DuckDB
incompatibility, but it is missing the same class-level skip marker. In CI
environments using DuckDB, these tests can fail even when the feature code is
correct. Add the same skip guard used by adjacent chart data flow test classes.
[logic error]
<details>
<summary><b>Severity Level:</b> Major ⚠️</summary>
```mdx
- ❌ DuckDB chart-data CI lane fails on new tests.
- ⚠️ Dashboard-filter feature validation blocked by fixture incompatibility.
```
</details>
```suggestion
@pytest.mark.chart_data_flow
@pytest.mark.skip(
reason=(
"TODO: Fix test class to work with DuckDB example data format. "
"Birth names fixture conflicts with new example data structure."
)
)
```
<details>
<summary><b>Steps of Reproduction ✅ </b></summary>
```mdx
1. Run integration tests in the DuckDB chart-data environment (the same
environment
already acknowledged as broken in this file via class-level skips at
`tests/integration_tests/charts/data/api_tests.py:168-173` and `:1165-1169`
with reason
`"Birth names fixture conflicts with new example data structure."`).
2. Execute any test from `TestGetChartDataWithDashboardFilter` (class
declaration at
`tests/integration_tests/charts/data/api_tests.py:1815`), e.g.
`test_get_data_with_dashboard_filter_context` at `:1851`.
3. The test path uses the same birth-names fixture chain
(`@pytest.mark.usefixtures("load_birth_names_dashboard_with_slices")` at
`:1849`, `:1892`,
`:1937`, etc.) and the same `"Genders"` slice setup in
`_setup_chart_with_query_context`
(`:1818-1820`).
4. Because this class lacks the existing DuckDB skip guard while adjacent
chart-data
classes have it, pytest executes this known-incompatible fixture path and
the test fails
in DuckDB runs before validating the new dashboard-filter behavior.
```
</details>
<details>
<summary><b>Prompt for AI Agent 🤖 </b></summary>
```mdx
This is a comment left during a code review.
**Path:** tests/integration_tests/charts/data/api_tests.py
**Line:** 1815:1815
**Comment:**
*Logic Error: This new test class uses the same birth-names
fixture/query flow that is explicitly skipped elsewhere in this file due to
DuckDB incompatibility, but it is missing the same class-level skip marker. In
CI environments using DuckDB, these tests can fail even when the feature code
is correct. Add the same skip guard used by adjacent chart data flow test
classes.
Validate the correctness of the flagged issue. If correct, How can I resolve
this? If you propose a fix, implement it and please make it concise.
```
</details>
<a
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F38638&comment_hash=1ab822b99f62fc57e4b8f47e9a5681f2c6510c2721d672c2c605e59debf296b4&reaction=like'>👍</a>
| <a
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F38638&comment_hash=1ab822b99f62fc57e4b8f47e9a5681f2c6510c2721d672c2c605e59debf296b4&reaction=dislike'>👎</a>
##########
superset/charts/data/api.py:
##########
@@ -156,6 +180,53 @@ def get_data( # noqa: C901
json_body["result_type"] = request.args.get("type",
ChartDataResultType.FULL)
json_body["force"] = request.args.get("force")
+ # Apply dashboard filter context when filters_dashboard_id is provided
+ dashboard_filter_context: DashboardFilterContext | None = None
+ filters_dashboard_id = request.args.get("filters_dashboard_id",
type=int)
+ if filters_dashboard_id is not None:
Review Comment:
**Suggestion:** Invalid `filters_dashboard_id` values are silently treated
as if the parameter was not provided because `request.args.get(..., type=int)`
returns `None` on conversion failure. This causes incorrect 200 responses
without applying filters instead of a client error; explicitly validate parsing
and return 400 when the parameter is present but non-integer. [logic error]
<details>
<summary><b>Severity Level:</b> Major ⚠️</summary>
```mdx
- ⚠️ GET chart data silently ignores malformed dashboard filter ID.
- ⚠️ Clients receive unfiltered results without explicit error.
```
</details>
```suggestion
filters_dashboard_id: int | None = None
if "filters_dashboard_id" in request.args:
try:
filters_dashboard_id =
int(request.args["filters_dashboard_id"])
except (TypeError, ValueError):
return self.response_400(
message=_("`filters_dashboard_id` must be an integer")
)
if filters_dashboard_id is not None:
```
<details>
<summary><b>Steps of Reproduction ✅ </b></summary>
```mdx
1. Hit GET chart data endpoint `/<int:pk>/data/`
(`superset/charts/data/api.py:81`,
`get_data` at `:89`) with malformed query param:
`/api/v1/chart/<id>/data/?filters_dashboard_id=abc`.
2. In `get_data`, parsing uses `request.args.get("filters_dashboard_id",
type=int)`
(`superset/charts/data/api.py:185`), which yields `None` for bad integer
input; branch at
`:186` is skipped.
3. Because the dashboard filter branch is skipped,
`get_dashboard_filter_context(...)`
(`:188-191`) is never called, so no validation/security/default-filter
application occurs.
4. Endpoint still returns normal 200 data path; `dashboard_filters` metadata
is only
attached when context exists (`superset/charts/data/api.py:534-535`), so
client gets
silent unfiltered response instead of input error.
```
</details>
<details>
<summary><b>Prompt for AI Agent 🤖 </b></summary>
```mdx
This is a comment left during a code review.
**Path:** superset/charts/data/api.py
**Line:** 185:186
**Comment:**
*Logic Error: Invalid `filters_dashboard_id` values are silently
treated as if the parameter was not provided because `request.args.get(...,
type=int)` returns `None` on conversion failure. This causes incorrect 200
responses without applying filters instead of a client error; explicitly
validate parsing and return 400 when the parameter is present but non-integer.
Validate the correctness of the flagged issue. If correct, How can I resolve
this? If you propose a fix, implement it and please make it concise.
```
</details>
<a
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F38638&comment_hash=61085592bfe9ca44b74574045a51a76f287c7985f2abd6f6b8b38bab55a3be76&reaction=like'>👍</a>
| <a
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F38638&comment_hash=61085592bfe9ca44b74574045a51a76f287c7985f2abd6f6b8b38bab55a3be76&reaction=dislike'>👎</a>
##########
superset/charts/data/api.py:
##########
@@ -156,6 +180,53 @@ def get_data( # noqa: C901
json_body["result_type"] = request.args.get("type",
ChartDataResultType.FULL)
json_body["force"] = request.args.get("force")
+ # Apply dashboard filter context when filters_dashboard_id is provided
+ dashboard_filter_context: DashboardFilterContext | None = None
+ filters_dashboard_id = request.args.get("filters_dashboard_id",
type=int)
+ if filters_dashboard_id is not None:
+ try:
+ dashboard_filter_context = get_dashboard_filter_context(
+ dashboard_id=filters_dashboard_id,
+ chart_id=pk,
+ )
+ except ValueError as error:
+ return self.response_400(message=str(error))
+ except SupersetSecurityException:
+ return self.response_403()
+
+ if dashboard_filter_context.extra_form_data:
+ efd = dashboard_filter_context.extra_form_data
+ extra_filters = efd.get("filters", [])
+
+ for query in json_body.get("queries", []):
+ if extra_filters:
+ existing = query.get("filters", [])
+ query["filters"] = existing + [
+ {**f, "isExtra": True} for f in extra_filters
+ ]
Review Comment:
**Suggestion:** `filters` can be `None` in query objects (schema allows
null), so concatenating it with a list raises a `TypeError` at runtime when
dashboard filters are applied. Normalize missing/null filters to an empty list
before concatenation. [null pointer]
<details>
<summary><b>Severity Level:</b> Critical 🚨</summary>
```mdx
- ❌ GET chart data crashes when saved query filters null.
- ⚠️ Dashboard default filters cannot be applied reliably.
```
</details>
```suggestion
existing = query.get("filters") or []
query["filters"] = existing + [
{**f, "isExtra": True} for f in extra_filters
]
```
<details>
<summary><b>Steps of Reproduction ✅ </b></summary>
```mdx
1. Use chart save/update flow where `query_context` is accepted as raw JSON
string
(`ChartPostSchema.query_context` in `superset/charts/schemas.py:213`; update
persists
properties via `ChartDAO.update` in `superset/commands/chart/update.py:73`)
and store a
query object containing `"filters": null`.
2. Call GET chart data endpoint with dashboard filter application enabled
(`superset/charts/data/api.py:81`, `:185-186`) and a dashboard whose
defaults produce
`extra_form_data.filters` (`:199-203`).
3. Loop over saved query context (`superset/charts/data/api.py:201`)
executes `existing =
query.get("filters", [])` (`:203`), returning `None` because key exists with
null.
4. Next line performs `None + list` (`:204-206`), raising `TypeError` before
request
validation/response handling, causing server error on this endpoint path.
```
</details>
<details>
<summary><b>Prompt for AI Agent 🤖 </b></summary>
```mdx
This is a comment left during a code review.
**Path:** superset/charts/data/api.py
**Line:** 203:206
**Comment:**
*Null Pointer: `filters` can be `None` in query objects (schema allows
null), so concatenating it with a list raises a `TypeError` at runtime when
dashboard filters are applied. Normalize missing/null filters to an empty list
before concatenation.
Validate the correctness of the flagged issue. If correct, How can I resolve
this? If you propose a fix, implement it and please make it concise.
```
</details>
<a
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F38638&comment_hash=f726c98f1d17cb82eae8dd233d4323d39bc82e3c3f92a39bee253b522a1848c0&reaction=like'>👍</a>
| <a
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F38638&comment_hash=f726c98f1d17cb82eae8dd233d4323d39bc82e3c3f92a39bee253b522a1848c0&reaction=dislike'>👎</a>
##########
superset/charts/data/api.py:
##########
@@ -156,6 +180,53 @@ def get_data( # noqa: C901
json_body["result_type"] = request.args.get("type",
ChartDataResultType.FULL)
json_body["force"] = request.args.get("force")
+ # Apply dashboard filter context when filters_dashboard_id is provided
+ dashboard_filter_context: DashboardFilterContext | None = None
+ filters_dashboard_id = request.args.get("filters_dashboard_id",
type=int)
+ if filters_dashboard_id is not None:
+ try:
+ dashboard_filter_context = get_dashboard_filter_context(
+ dashboard_id=filters_dashboard_id,
+ chart_id=pk,
+ )
+ except ValueError as error:
+ return self.response_400(message=str(error))
+ except SupersetSecurityException:
+ return self.response_403()
+
+ if dashboard_filter_context.extra_form_data:
+ efd = dashboard_filter_context.extra_form_data
+ extra_filters = efd.get("filters", [])
+
+ for query in json_body.get("queries", []):
+ if extra_filters:
+ existing = query.get("filters", [])
+ query["filters"] = existing + [
+ {**f, "isExtra": True} for f in extra_filters
+ ]
+
+ extras = query.get("extras", {})
+ for key in EXTRA_FORM_DATA_OVERRIDE_EXTRA_KEYS:
+ if key in efd:
+ extras[key] = efd[key]
Review Comment:
**Suggestion:** `extras` can be `None` in query objects (schema allows
null), and assigning keys to `None` raises a runtime exception when extra
form-data overrides are present. Coerce null `extras` to a dict before writing
override keys. [null pointer]
<details>
<summary><b>Severity Level:</b> Critical 🚨</summary>
```mdx
- ❌ GET chart data fails when query extras is null.
- ⚠️ Time/extras overrides from dashboard defaults are lost.
```
</details>
```suggestion
extras = query.get("extras") or {}
for key in EXTRA_FORM_DATA_OVERRIDE_EXTRA_KEYS:
if key in efd:
extras[key] = efd[key]
```
<details>
<summary><b>Steps of Reproduction ✅ </b></summary>
```mdx
1. Persist a chart `query_context` containing `"extras": null` (allowed
nullable query
object fields: `extras` in `superset/charts/schemas.py:1337-1340`; chart
query_context
accepted as JSON string at `superset/charts/schemas.py:213`).
2. Invoke GET `/api/v1/chart/<id>/data/?filters_dashboard_id=<dashboard_id>`
so dashboard
context is loaded (`superset/charts/data/api.py:185-191`) and
`extra_form_data` is present
(`:197-199`).
3. Ensure `efd` includes any override key iterated by
`EXTRA_FORM_DATA_OVERRIDE_EXTRA_KEYS`
(`superset/charts/data/api.py:209-211`), then
mutation path runs.
4. `extras = query.get("extras", {})` returns `None` when key exists as null
(`:208`), and
assignment `extras[key] = ...` at `:211` raises `TypeError`, breaking
request execution.
```
</details>
<details>
<summary><b>Prompt for AI Agent 🤖 </b></summary>
```mdx
This is a comment left during a code review.
**Path:** superset/charts/data/api.py
**Line:** 208:211
**Comment:**
*Null Pointer: `extras` can be `None` in query objects (schema allows
null), and assigning keys to `None` raises a runtime exception when extra
form-data overrides are present. Coerce null `extras` to a dict before writing
override keys.
Validate the correctness of the flagged issue. If correct, How can I resolve
this? If you propose a fix, implement it and please make it concise.
```
</details>
<a
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F38638&comment_hash=b271a2b1e8d4b1f3efdae349d6633d05afc89b76860a268b5396ee7ed9315780&reaction=like'>👍</a>
| <a
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F38638&comment_hash=b271a2b1e8d4b1f3efdae349d6633d05afc89b76860a268b5396ee7ed9315780&reaction=dislike'>👎</a>
##########
superset/charts/data/dashboard_filter_context.py:
##########
@@ -0,0 +1,288 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+import logging
+from dataclasses import dataclass, field
+from enum import Enum
+from typing import Any
+
+from flask_babel import gettext as _
+
+from superset import db, security_manager
+from superset.models.dashboard import Dashboard
+from superset.utils import json
+
+logger = logging.getLogger(__name__)
+
+CHART_TYPE = "CHART"
+
+
+class DashboardFilterStatus(str, Enum):
+ APPLIED = "applied"
+ NOT_APPLIED = "not_applied"
+ NOT_APPLIED_USES_DEFAULT_TO_FIRST_ITEM_PREQUERY = (
+ "not_applied_uses_default_to_first_item_prequery"
+ )
+
+
+@dataclass
+class DashboardFilterInfo:
+ id: str
+ name: str
+ status: DashboardFilterStatus
+ column: str | None = None
+
+
+@dataclass
+class DashboardFilterContext:
+ extra_form_data: dict[str, Any] = field(default_factory=dict)
+ filters: list[DashboardFilterInfo] = field(default_factory=list)
+
+ def to_dict(self) -> dict[str, Any]:
+ return {
+ "filters": [
+ {
+ "id": f.id,
+ "name": f.name,
+ "status": f.status.value,
+ **({"column": f.column} if f.column else {}),
+ }
+ for f in self.filters
+ ],
+ }
+
+
+def _is_filter_in_scope_for_chart(
+ filter_config: dict[str, Any],
+ chart_id: int,
+ position_json: dict[str, Any],
+) -> bool:
+ """
+ Determines whether a native filter applies to a given chart. When
+ chartsInScope is present on the filter config, uses that directly.
+ Otherwise falls back to scope.rootPath and scope.excluded with
+ the dashboard layout.
+ """
+ if charts_in_scope := filter_config.get("chartsInScope"):
+ return chart_id in charts_in_scope
Review Comment:
**Suggestion:** The scope check treats an empty `chartsInScope` list as if
the field were missing and falls back to `rootPath`, which can incorrectly
apply a filter to charts that should receive no filter. Handle the presence of
`chartsInScope` explicitly so `[]` means "in scope for no charts". [logic error]
<details>
<summary><b>Severity Level:</b> Critical 🚨</summary>
```mdx
- ❌ Scoped-out filters can still affect chart SQL.
- ⚠️ Dashboard filter metadata reports misleading applied status.
- ⚠️ GET chart data differs from frontend scope semantics.
```
</details>
```suggestion
if "chartsInScope" in filter_config:
charts_in_scope = filter_config.get("chartsInScope")
if charts_in_scope is not None:
return chart_id in charts_in_scope
```
<details>
<summary><b>Steps of Reproduction ✅ </b></summary>
```mdx
1. Call GET
`api/v1/chart/<chart_id>/data/?filters_dashboard_id=<dashboard_id>`
(entrypoint in `superset/charts/data/api.py:185-190`), which invokes
`get_dashboard_filter_context(...)`.
2. Ensure dashboard metadata contains a native filter with explicit
`"chartsInScope": []`
and default `scope.rootPath` (this field is persisted when present; see
helper behavior in
`tests/unit_tests/charts/test_dashboard_filter_context.py:101-103`).
3. Execution reaches `_is_filter_in_scope_for_chart`
(`superset/charts/data/dashboard_filter_context.py:70-97`); line 81 treats
`[]` as falsy,
so it skips explicit scope and falls back to `rootPath` logic.
4. For charts under `ROOT_ID`, line 96 returns True and filter is treated
in-scope, so
defaults are merged (`dashboard_filter_context.py:274-277`) and applied to
chart query
unexpectedly.
```
</details>
<details>
<summary><b>Prompt for AI Agent 🤖 </b></summary>
```mdx
This is a comment left during a code review.
**Path:** superset/charts/data/dashboard_filter_context.py
**Line:** 81:82
**Comment:**
*Logic Error: The scope check treats an empty `chartsInScope` list as
if the field were missing and falls back to `rootPath`, which can incorrectly
apply a filter to charts that should receive no filter. Handle the presence of
`chartsInScope` explicitly so `[]` means "in scope for no charts".
Validate the correctness of the flagged issue. If correct, How can I resolve
this? If you propose a fix, implement it and please make it concise.
```
</details>
<a
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F38638&comment_hash=26265dd75eeb82280bd39234d819b5b54778e4e2b1580e341c50f257e6c7ddba&reaction=like'>👍</a>
| <a
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F38638&comment_hash=26265dd75eeb82280bd39234d819b5b54778e4e2b1580e341c50f257e6c7ddba&reaction=dislike'>👎</a>
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]