codeant-ai-for-open-source[bot] commented on code in PR #40912:
URL: https://github.com/apache/superset/pull/40912#discussion_r3493574372
##########
superset/dashboards/api.py:
##########
@@ -528,6 +531,126 @@ def get(
)
return self.response(200, result=result)
+ @expose("/<id_or_slug>/lineage", methods=("GET",))
+ @protect()
+ @safe
+ @statsd_metrics
+ @with_dashboard
+ @event_logger.log_this_with_context(
+ action=lambda self, *args, **kwargs:
f"{self.__class__.__name__}.lineage",
+ log_to_statsd=False,
+ )
+ # pylint: disable=arguments-differ,arguments-renamed
+ def lineage(self, dash: Dashboard) -> Response:
+ """Get lineage information for a dashboard.
+ ---
+ get:
+ summary: Get lineage information for a dashboard
+ description: >-
+ Returns upstream (charts, datasets, databases) lineage information
+ for a dashboard
+ parameters:
+ - in: path
+ name: id_or_slug
+ schema:
+ type: string
+ description: Either the id of the dashboard, or its slug
+ responses:
+ 200:
+ description: Lineage information
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/DashboardLineageResponseSchema"
+ 401:
+ $ref: '#/components/responses/401'
+ 404:
+ $ref: '#/components/responses/404'
+ 500:
+ $ref: '#/components/responses/500'
+ """
+ dashboard_info = {
+ "id": dash.id,
+ "title": dash.dashboard_title,
+ "slug": dash.slug,
+ "published": dash.published,
+ }
+
+ # Get upstream (charts, datasets, databases) information
+ charts = []
+ dataset_map = {}
+ database_map = {}
+
+ for chart in dash.slices:
+ charts.append(
+ {
+ "id": chart.id,
+ "slice_name": chart.slice_name,
+ "viz_type": chart.viz_type,
+ "dataset_id": chart.datasource_id,
+ }
+ )
+
+ # Collect dataset information. Schema/table/database details are
+ # only exposed to users who can access the underlying datasource;
+ # otherwise they are redacted so lineage never leaks datasource
+ # internals (the dataset id/name are kept so the graph still
+ # renders).
+ dataset = chart.datasource
Review Comment:
**Suggestion:** This loop can trigger an N+1 query pattern because each
iteration dereferences `chart.datasource` (and then `dataset.database`) lazily,
causing one query per chart on large dashboards. Eager-load slices with their
datasource/database (for example via joinedload/selectinload in the dashboard
fetch) before iterating. [performance]
<details>
<summary><b>Severity Level:</b> Major ⚠️</summary>
```mdx
- ⚠️ Dashboard lineage endpoint issues extra database queries.
- ⚠️ Large dashboards may show slower lineage responses.
- ⚠️ Additional load on metadata database under heavy usage.
```
</details>
<details>
<summary><b>Steps of Reproduction ✅ </b></summary>
```mdx
1. Open a dashboard with many charts in the UI; this triggers
`useDashboardLineage()` in
`superset-frontend/src/hooks/apiResources/lineage.ts:147`, which calls `GET
/api/v1/dashboard/<id_or_slug>/lineage`.
2. The request is routed to `DashboardRestApi.lineage()` in
`superset/dashboards/api.py:35-45`, where the `@with_dashboard` decorator
resolves the
`Dashboard` instance and passes it as `dash` into the method.
3. Inside `DashboardRestApi.lineage()` at
`superset/dashboards/api.py:584-599`, the code
iterates `for chart in dash.slices:` and accesses `chart.datasource` (via the
`Slice.datasource` property in `superset/models/slice.py:145-147`) and then
`dataset.database` (SQLAlchemy relationship on `SqlaTable.database` in
`superset/connectors/sqla/models.py:40-50`).
4. While `Slice.datasource` uses `lazy="subquery"` (avoiding one query per
chart), each
distinct dataset still lazily loads its `database` via a separate `SELECT`,
so dashboards
with many datasets incur an extra query per dataset; fixing this by
eager-loading
`database` for all datasets used in the dashboard would reduce these
additional
round-trips.
```
</details>
[](https://app.codeant.ai/fix-in-ide?tool=cursor&prompt_id=e56e0fb932e24a3f90f52ca126eb22f9&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset)
[](https://app.codeant.ai/fix-in-ide?tool=vscode-claude&prompt_id=e56e0fb932e24a3f90f52ca126eb22f9&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset)
*(Use Cmd/Ctrl + Click for best experience)*
<details>
<summary><b>Prompt for AI Agent 🤖 </b></summary>
```mdx
This is a comment left during a code review.
**Path:** superset/dashboards/api.py
**Line:** 584:599
**Comment:**
*Performance: This loop can trigger an N+1 query pattern because each
iteration dereferences `chart.datasource` (and then `dataset.database`) lazily,
causing one query per chart on large dashboards. Eager-load slices with their
datasource/database (for example via joinedload/selectinload in the dashboard
fetch) before iterating.
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.
Once fix is implemented, also check other comments on the same PR, and ask
user if the user wants to fix the rest of the comments as well. if said yes,
then fetch all the comments validate the correctness and implement a minimal fix
```
</details>
<a
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F40912&comment_hash=0c7cd8ee8ee55a3531b0a052caf9c2a5ea6b19ca19d6891fe5a3820715334378&reaction=like'>👍</a>
| <a
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F40912&comment_hash=0c7cd8ee8ee55a3531b0a052caf9c2a5ea6b19ca19d6891fe5a3820715334378&reaction=dislike'>👎</a>
##########
superset/datasets/api.py:
##########
@@ -855,6 +858,130 @@ def related_objects(self, id_or_uuid: str) -> Response:
dashboards={"count": len(dashboards), "result": dashboards},
)
+ @expose("/<id_or_uuid>/lineage", methods=("GET",))
+ @protect()
+ @safe
+ @statsd_metrics
+ @event_logger.log_this_with_context(
+ action=lambda self, *args, **kwargs:
f"{self.__class__.__name__}.lineage",
+ log_to_statsd=False,
+ )
+ def lineage(self, id_or_uuid: str) -> Response:
+ """Get lineage information for a dataset.
+ ---
+ get:
+ summary: Get lineage information for a dataset
+ description: >-
+ Returns upstream (database) and downstream (charts, dashboards)
lineage
+ information for a dataset
+ parameters:
+ - in: path
+ name: id_or_uuid
+ schema:
+ type: string
+ description: Either the id of the dataset, or its uuid
+ responses:
+ 200:
+ description: Lineage information
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/DatasetLineageResponseSchema"
+ 401:
+ $ref: '#/components/responses/401'
+ 404:
+ $ref: '#/components/responses/404'
+ 500:
+ $ref: '#/components/responses/500'
+ """
+ dataset = DatasetDAO.find_by_id_or_uuid(id_or_uuid)
+ if not dataset:
+ return self.response_404()
+
+ dataset_info = {
+ "id": dataset.id,
+ "name": dataset.name,
+ "database_id": dataset.database_id,
+ "database_name": (
+ dataset.database.database_name if dataset.database else None
+ ),
+ "schema": dataset.schema,
+ "table_name": dataset.table_name,
+ }
+
+ # Get upstream (database) information
+ upstream: dict[str, Any] = {}
+ if dataset.database:
+ upstream["database"] = {
+ "id": dataset.database.id,
+ "database_name": dataset.database.database_name,
+ "backend": dataset.database.backend,
+ }
+ else:
+ upstream["database"] = None
+
+ # Get downstream (charts and dashboards) information
+ related_data = DatasetDAO.get_related_objects(dataset.id)
+
+ # Build chart information with dashboard IDs, filtering both the charts
+ # and their linked dashboards by the current user's permissions so
+ # lineage never exposes assets the user cannot access.
+ charts: list[dict[str, Any]] = []
+ for chart in related_data["charts"]:
+ if not security_manager.can_access_chart(chart):
+ continue
+ dashboard_ids = [
+ d.id
+ for d in chart.dashboards
+ if security_manager.can_access_dashboard(d)
+ ]
Review Comment:
**Suggestion:** This per-chart access to `chart.dashboards` can issue an
additional query for each chart, producing N+1 behavior when a dataset has many
related charts. Preload chart-dashboard relationships in the DAO query (or
bulk-build a chart→dashboard map) before this loop. [performance]
<details>
<summary><b>Severity Level:</b> Major ⚠️</summary>
```mdx
- ⚠️ Dataset lineage endpoint performs N+1 dashboard queries.
- ⚠️ Datasets feeding many charts incur noticeable latency.
- ⚠️ Extra database load when lineage is viewed frequently.
```
</details>
<details>
<summary><b>Steps of Reproduction ✅ </b></summary>
```mdx
1. Create or identify a dataset used by many charts; the dataset editor and
lineage tab
use `useDatasetLineage()` in
`superset-frontend/src/hooks/apiResources/lineage.ts:125`,
which calls `GET /api/v1/dataset/<id_or_uuid>/lineage`.
2. The request reaches `DatasetRestApi.lineage()` in
`superset/datasets/api.py:42-50`,
which looks up the dataset via `DatasetDAO.find_by_id_or_uuid()` and then
calls
`DatasetDAO.get_related_objects(dataset.id)` at
`superset/datasets/api.py:104-105`.
3. `DatasetDAO.get_related_objects()` in `superset/daos/dataset.py:124-144`
loads all
related `Slice` objects into `related_data["charts"]` and all dashboards via
a separate
query, but does not eager-load the `Slice.dashboards` relationship.
4. Back in `DatasetRestApi.lineage()` at `superset/datasets/api.py:930-937`,
the loop `for
chart in related_data["charts"]:` accesses `chart.dashboards`; this backref
relationship
is defined via `Dashboard.slices` in `superset/models/dashboard.py:145-147`
with default
lazy loading, so each chart triggers its own query to fetch dashboards,
producing an N+1
pattern proportional to the number of charts for the dataset.
```
</details>
[](https://app.codeant.ai/fix-in-ide?tool=cursor&prompt_id=cad836b1c07549dbb781905da0ec4b60&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset)
[](https://app.codeant.ai/fix-in-ide?tool=vscode-claude&prompt_id=cad836b1c07549dbb781905da0ec4b60&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset)
*(Use Cmd/Ctrl + Click for best experience)*
<details>
<summary><b>Prompt for AI Agent 🤖 </b></summary>
```mdx
This is a comment left during a code review.
**Path:** superset/datasets/api.py
**Line:** 930:937
**Comment:**
*Performance: This per-chart access to `chart.dashboards` can issue an
additional query for each chart, producing N+1 behavior when a dataset has many
related charts. Preload chart-dashboard relationships in the DAO query (or
bulk-build a chart→dashboard map) before this loop.
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.
Once fix is implemented, also check other comments on the same PR, and ask
user if the user wants to fix the rest of the comments as well. if said yes,
then fetch all the comments validate the correctness and implement a minimal fix
```
</details>
<a
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F40912&comment_hash=b8d22c4077a96c1ef73a9ea55fdfd8904a01754d13c977604f12e8021663adcd&reaction=like'>👍</a>
| <a
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F40912&comment_hash=b8d22c4077a96c1ef73a9ea55fdfd8904a01754d13c977604f12e8021663adcd&reaction=dislike'>👎</a>
##########
superset/datasets/schemas.py:
##########
@@ -250,6 +250,60 @@ class DatasetRelatedObjectsResponse(Schema):
dashboards = fields.Nested(DatasetRelatedDashboards)
+class DatasetLineageDatasetSchema(Schema):
+ id = fields.Integer()
+ name = fields.String()
+ database_id = fields.Integer()
+ database_name = fields.String()
Review Comment:
**Suggestion:** This field is declared as always present, but the lineage
endpoint explicitly returns null when the dataset has no attached database
object. The schema should allow null here so OpenAPI and any schema-based
consumers match real responses. [api mismatch]
<details>
<summary><b>Severity Level:</b> Major ⚠️</summary>
```mdx
⚠️ Dataset lineage spec misstates database_name nullability.
⚠️ Strict clients may treat missing database_name as error.
⚠️ Schema-based tooling reports inconsistent API response contract.
```
</details>
<details>
<summary><b>Steps of Reproduction ✅ </b></summary>
```mdx
1. Inspect the dataset lineage implementation in
`superset/datasets/api.py:22-45, 58-71`
(loaded via BulkRead). In `DatasetRestApi.lineage`, `dataset_info` is built
as `{"id":
dataset.id, "name": dataset.name, "database_id": dataset.database_id,
"database_name":
(dataset.database.database_name if dataset.database else None), "schema":
dataset.schema,
"table_name": dataset.table_name}`; the conditional clearly allows
`database_name` to be
`None` when `dataset.database` is missing.
2. Note that the upstream section is guarded by `if dataset.database:` at
`superset/datasets/api.py:73-80`, but `dataset_info["database_name"]` can
still be `None`
because it is always included in the `result["dataset"]` payload regardless
of whether
`dataset.database` exists.
3. Inspect the response schema in `superset/datasets/schemas.py:253-260`,
where `class
DatasetLineageDatasetSchema(Schema)` declares `database_name =
fields.String()` on line
257 in the PR hunk, without `allow_none=True`, thereby documenting
`database_name` as a
non-null string in the `DatasetLineageResponseSchema` used by OpenAPI at
`superset/datasets/api.py:45-50`.
4. For a dataset whose `database` relationship is missing (for example, an
orphaned
dataset after its database was removed or an invalid import), call `GET
/api/v1/dataset/{id_or_uuid}/lineage` on `DatasetRestApi.lineage`. The
actual JSON
response includes `"database_name": null` inside `result.dataset`, while the
schema
declares `database_name` as a non-null string, so any strict schema-based
client or
validator will detect and report this mismatch.
```
</details>
[](https://app.codeant.ai/fix-in-ide?tool=cursor&prompt_id=e08298d405f74b86a180883fba24456a&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset)
[](https://app.codeant.ai/fix-in-ide?tool=vscode-claude&prompt_id=e08298d405f74b86a180883fba24456a&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset)
*(Use Cmd/Ctrl + Click for best experience)*
<details>
<summary><b>Prompt for AI Agent 🤖 </b></summary>
```mdx
This is a comment left during a code review.
**Path:** superset/datasets/schemas.py
**Line:** 257:257
**Comment:**
*Api Mismatch: This field is declared as always present, but the
lineage endpoint explicitly returns null when the dataset has no attached
database object. The schema should allow null here so OpenAPI and any
schema-based consumers match real responses.
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.
Once fix is implemented, also check other comments on the same PR, and ask
user if the user wants to fix the rest of the comments as well. if said yes,
then fetch all the comments validate the correctness and implement a minimal fix
```
</details>
<a
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F40912&comment_hash=05b05f442c5cb52b4869aa6f75705bb517d4c3b1fbd17f31c9d1efe34caba5c5&reaction=like'>👍</a>
| <a
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F40912&comment_hash=05b05f442c5cb52b4869aa6f75705bb517d4c3b1fbd17f31c9d1efe34caba5c5&reaction=dislike'>👎</a>
##########
superset/datasets/api.py:
##########
@@ -855,6 +858,130 @@ def related_objects(self, id_or_uuid: str) -> Response:
dashboards={"count": len(dashboards), "result": dashboards},
)
+ @expose("/<id_or_uuid>/lineage", methods=("GET",))
+ @protect()
+ @safe
+ @statsd_metrics
+ @event_logger.log_this_with_context(
+ action=lambda self, *args, **kwargs:
f"{self.__class__.__name__}.lineage",
+ log_to_statsd=False,
+ )
+ def lineage(self, id_or_uuid: str) -> Response:
+ """Get lineage information for a dataset.
+ ---
+ get:
+ summary: Get lineage information for a dataset
+ description: >-
+ Returns upstream (database) and downstream (charts, dashboards)
lineage
+ information for a dataset
+ parameters:
+ - in: path
+ name: id_or_uuid
+ schema:
+ type: string
+ description: Either the id of the dataset, or its uuid
+ responses:
+ 200:
+ description: Lineage information
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/DatasetLineageResponseSchema"
+ 401:
+ $ref: '#/components/responses/401'
+ 404:
+ $ref: '#/components/responses/404'
+ 500:
+ $ref: '#/components/responses/500'
+ """
+ dataset = DatasetDAO.find_by_id_or_uuid(id_or_uuid)
+ if not dataset:
+ return self.response_404()
+
+ dataset_info = {
+ "id": dataset.id,
+ "name": dataset.name,
+ "database_id": dataset.database_id,
+ "database_name": (
+ dataset.database.database_name if dataset.database else None
+ ),
+ "schema": dataset.schema,
+ "table_name": dataset.table_name,
+ }
+
+ # Get upstream (database) information
+ upstream: dict[str, Any] = {}
+ if dataset.database:
+ upstream["database"] = {
+ "id": dataset.database.id,
+ "database_name": dataset.database.database_name,
+ "backend": dataset.database.backend,
+ }
+ else:
+ upstream["database"] = None
+
+ # Get downstream (charts and dashboards) information
+ related_data = DatasetDAO.get_related_objects(dataset.id)
+
+ # Build chart information with dashboard IDs, filtering both the charts
+ # and their linked dashboards by the current user's permissions so
+ # lineage never exposes assets the user cannot access.
+ charts: list[dict[str, Any]] = []
+ for chart in related_data["charts"]:
+ if not security_manager.can_access_chart(chart):
+ continue
+ dashboard_ids = [
+ d.id
+ for d in chart.dashboards
+ if security_manager.can_access_dashboard(d)
+ ]
+ charts.append(
+ {
+ "id": chart.id,
+ "slice_name": chart.slice_name,
+ "viz_type": chart.viz_type,
+ "dashboard_ids": dashboard_ids,
+ }
+ )
+
+ # Build dashboard information with chart IDs
+ dashboards: list[dict[str, Any]] = []
+ for dashboard in related_data["dashboards"]:
+ if not security_manager.can_access_dashboard(dashboard):
+ continue
+ chart_ids = [
+ chart.id
+ for chart in dashboard.slices
+ if chart.datasource_id == dataset.id
+ and security_manager.can_access_chart(chart)
+ ]
Review Comment:
**Suggestion:** This dashboard loop can also trigger N+1 queries because
`dashboard.slices` is lazily loaded per dashboard. For datasets used by many
dashboards, this causes many round-trips; preload slices for all dashboards in
one query or reuse already-fetched chart relationships. [performance]
<details>
<summary><b>Severity Level:</b> Major ⚠️</summary>
```mdx
- ⚠️ Dataset lineage endpoint performs N+1 chart-list queries.
- ⚠️ Datasets used by many dashboards suffer slower lineage.
- ⚠️ Increased database load when lineage view is popular.
```
</details>
<details>
<summary><b>Steps of Reproduction ✅ </b></summary>
```mdx
1. Using the same dataset-lineage flow as above, call `GET
/api/v1/dataset/<id_or_uuid>/lineage` via `useDatasetLineage()` (frontend
hook in
`superset-frontend/src/hooks/apiResources/lineage.ts:125`).
2. `DatasetRestApi.lineage()` in `superset/datasets/api.py:50-77` resolves
the dataset and
obtains `related_data = DatasetDAO.get_related_objects(dataset.id)` at
`superset/datasets/api.py:104-105`, which returns both charts and dashboards.
3. `DatasetDAO.get_related_objects()` in `superset/daos/dataset.py:124-144`
loads
dashboards with
`db.session.query(Dashboard).join(Dashboard.slices)...all()`, but does not
mark `Dashboard.slices` as eagerly loaded (no `joinedload` or
`contains_eager`), so the
`Dashboard.slices` relationship remains lazily loaded.
4. In `DatasetRestApi.lineage()` at `superset/datasets/api.py:949-957`, the
loop `for
dashboard in related_data["dashboards"]:` accesses `dashboard.slices` to
compute
`chart_ids`; because `Dashboard.slices` (relationship defined in
`superset/models/dashboard.py:145-147`) is lazy, each dashboard triggers a
separate query
to fetch its slices, resulting in an N+1 query pattern proportional to the
number of
dashboards linked to the dataset.
```
</details>
[](https://app.codeant.ai/fix-in-ide?tool=cursor&prompt_id=77933bd1a4094dc0826ed75ce908768d&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset)
[](https://app.codeant.ai/fix-in-ide?tool=vscode-claude&prompt_id=77933bd1a4094dc0826ed75ce908768d&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset)
*(Use Cmd/Ctrl + Click for best experience)*
<details>
<summary><b>Prompt for AI Agent 🤖 </b></summary>
```mdx
This is a comment left during a code review.
**Path:** superset/datasets/api.py
**Line:** 949:957
**Comment:**
*Performance: This dashboard loop can also trigger N+1 queries because
`dashboard.slices` is lazily loaded per dashboard. For datasets used by many
dashboards, this causes many round-trips; preload slices for all dashboards in
one query or reuse already-fetched chart relationships.
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.
Once fix is implemented, also check other comments on the same PR, and ask
user if the user wants to fix the rest of the comments as well. if said yes,
then fetch all the comments validate the correctness and implement a minimal fix
```
</details>
<a
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F40912&comment_hash=18074f08e7f53569669458c1a4e08780ab6dffa84b96c6c30a66eb0ef996ea48&reaction=like'>👍</a>
| <a
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F40912&comment_hash=18074f08e7f53569669458c1a4e08780ab6dffa84b96c6c30a66eb0ef996ea48&reaction=dislike'>👎</a>
##########
superset/datasets/schemas.py:
##########
@@ -250,6 +250,60 @@ class DatasetRelatedObjectsResponse(Schema):
dashboards = fields.Nested(DatasetRelatedDashboards)
+class DatasetLineageDatasetSchema(Schema):
+ id = fields.Integer()
+ name = fields.String()
+ database_id = fields.Integer()
+ database_name = fields.String()
+ schema = fields.String(allow_none=True)
+ table_name = fields.String()
+
+
+class DatasetLineageDatabaseSchema(Schema):
+ id = fields.Integer()
+ database_name = fields.String()
+ backend = fields.String()
+
+
+class DatasetLineageChartSchema(Schema):
+ id = fields.Integer()
+ slice_name = fields.String()
+ viz_type = fields.String()
+ dashboard_ids = fields.List(fields.Integer())
+
+
+class DatasetLineageDashboardSchema(Schema):
+ id = fields.Integer()
+ title = fields.String()
+ slug = fields.String()
Review Comment:
**Suggestion:** The dashboard slug is nullable in the model, but this
response schema declares it as a non-null string. When lineage includes
dashboards without a slug, the documented contract becomes incorrect and any
strict schema-based client/validation will fail. Mark this field as nullable to
match actual API output. [api mismatch]
<details>
<summary><b>Severity Level:</b> Major ⚠️</summary>
```mdx
⚠️ Dataset lineage spec misstates dashboard slug nullability.
⚠️ Strict OpenAPI clients may reject null slug.
⚠️ Downstream validation tools report schema-contract violations.
```
</details>
<details>
<summary><b>Steps of Reproduction ✅ </b></summary>
```mdx
1. Inspect the Dashboard model in `superset/models/dashboard.py:134-145`,
where `slug =
Column(String(255), unique=True)` is defined without `nullable=False`,
meaning the
database column and ORM attribute `Dashboard.slug` are allowed to be null
and slugs are
optional (also evidenced by `Dashboard.url` at
`superset/models/dashboard.py:199-205`
using `slug or id_`).
2. Inspect the dataset lineage implementation in
`superset/datasets/api.py:940-965`
(loaded via BulkRead), where the downstream dashboards payload is built: for
each
dashboard in `related_data["dashboards"]`, the code appends `{"id":
dashboard.id, "title":
dashboard.dashboard_title, "slug": dashboard.slug, "chart_ids": chart_ids}`.
If a
dashboard has no slug, `dashboard.slug` is `None`, and the response includes
`"slug":
null`.
3. Inspect the response schema in `superset/datasets/schemas.py:275-280`,
where `class
DatasetLineageDashboardSchema(Schema)` declares `slug = fields.String()`
(line 278 in the
PR hunk), without `allow_none=True`, documenting the field as a non-null
string in the
OpenAPI components via `DatasetLineageResponseSchema`.
4. Call the lineage endpoint for any dataset that is related to a dashboard
without a slug
(e.g., `GET /api/v1/dataset/{id_or_uuid}/lineage`, implemented by
`DatasetRestApi.lineage`
at `superset/datasets/api.py:22-45, 58-71, 940-965`); observe the JSON
payload contains
`null` for `downstream.dashboards[*].slug`, while the documented schema
`DatasetLineageDashboardSchema.slug` is non-null, causing schema-based
clients or
validators to flag a contract mismatch.
```
</details>
[](https://app.codeant.ai/fix-in-ide?tool=cursor&prompt_id=66cd9e83cb9a43669251da5bcfb5286a&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset)
[](https://app.codeant.ai/fix-in-ide?tool=vscode-claude&prompt_id=66cd9e83cb9a43669251da5bcfb5286a&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset)
*(Use Cmd/Ctrl + Click for best experience)*
<details>
<summary><b>Prompt for AI Agent 🤖 </b></summary>
```mdx
This is a comment left during a code review.
**Path:** superset/datasets/schemas.py
**Line:** 278:278
**Comment:**
*Api Mismatch: The dashboard slug is nullable in the model, but this
response schema declares it as a non-null string. When lineage includes
dashboards without a slug, the documented contract becomes incorrect and any
strict schema-based client/validation will fail. Mark this field as nullable to
match actual API output.
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.
Once fix is implemented, also check other comments on the same PR, and ask
user if the user wants to fix the rest of the comments as well. if said yes,
then fetch all the comments validate the correctness and implement a minimal fix
```
</details>
<a
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F40912&comment_hash=ee1abe435010f35bdcb00de1ea1c8cdd5e72735acf80681ce542ab38f78b6270&reaction=like'>👍</a>
| <a
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F40912&comment_hash=ee1abe435010f35bdcb00de1ea1c8cdd5e72735acf80681ce542ab38f78b6270&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]