codeant-ai-for-open-source[bot] commented on code in PR #38376:
URL: https://github.com/apache/superset/pull/38376#discussion_r2881412736
##########
superset-frontend/src/pages/DatabaseList/index.tsx:
##########
@@ -108,20 +122,106 @@ function DatabaseList({
addSuccessToast,
user,
}: DatabaseListProps) {
+ const theme = useTheme();
+ const showSemanticLayers = isFeatureEnabled(FeatureFlag.SemanticLayers);
+
+ // Standard database list view resource (used when SL flag is OFF)
const {
state: {
- loading,
- resourceCount: databaseCount,
- resourceCollection: databases,
+ loading: dbLoading,
+ resourceCount: dbCount,
+ resourceCollection: dbCollection,
},
hasPerm,
- fetchData,
- refreshData,
+ fetchData: dbFetchData,
+ refreshData: dbRefreshData,
} = useListViewResource<DatabaseObject>(
'database',
t('database'),
addDangerToast,
);
+
+ // Combined endpoint state (used when SL flag is ON)
+ const [combinedItems, setCombinedItems] = useState<ConnectionItem[]>([user]);
Review Comment:
**Suggestion:** The combined connections list is initialized with the `user`
object instead of an empty array, so before the first fetch the table will
render a bogus row whose shape doesn't match `ConnectionItem` (missing fields
like `database_name`, `backend`, etc.), which can lead to incorrect UI state or
runtime errors when columns try to access these properties; initialize it as an
empty array instead. [logic error]
<details>
<summary><b>Severity Level:</b> Major ⚠️</summary>
```mdx
- ⚠️ Databases list briefly shows bogus row from logged-in user.
- ⚠️ Clicking actions on bogus row triggers failing database API call.
```
</details>
```suggestion
const [combinedItems, setCombinedItems] = useState<ConnectionItem[]>([]);
```
<details>
<summary><b>Steps of Reproduction ✅ </b></summary>
```mdx
1. Enable the `SEMANTIC_LAYERS` feature flag in the backend configuration so
that
`isFeatureEnabled(FeatureFlag.SemanticLayers)` returns true (see semantic
layers wiring
under `superset/superset/config.py` and usage in
`superset-frontend/src/pages/DatabaseList/index.tsx`).
2. Log in to Superset as a user who has `can_write` on databases so that the
Databases
page is accessible and actions are visible.
3. Navigate to the Databases list UI, which renders the `DatabaseList`
component defined
in `superset-frontend/src/pages/DatabaseList/index.tsx`. When
`showSemanticLayers` is
true, it passes `databases = combinedItems` into `ListView<DatabaseObject>`
(see
`ListView` usage near the bottom of this file).
4. On first render before any network response, `combinedItems` is
initialized to `[user]`
(the `user` prop has shape `{ userId, firstName, lastName }` at
`DatabaseList`'s props).
`ListView` (implemented in
`superset-frontend/src/components/ListView/ListView.tsx`, lines
~268–323) immediately uses `data` to build table rows, so a single row with
`original ===
user` is rendered even though it does not have `DatabaseObject` fields like
`database_name`, `backend`, `allow_run_async`, or `id`.
5. Observe that this bogus row is displayed in the Databases table (mostly
blank columns).
If the user quickly clicks the Actions column on this row before
`/api/v1/semantic_layer/connections/` returns, the handlers in the Actions
column (defined
in `DatabaseList`'s `columns` useMemo) treat `original` as a
`DatabaseObject` and call
helpers like `openDatabaseDeleteModal(original)`, which triggers a request to
`/api/v1/database/${original.id}/related_objects/` with `original.id ===
undefined`,
producing an invalid API call and resulting toast error.
```
</details>
<details>
<summary><b>Prompt for AI Agent 🤖 </b></summary>
```mdx
This is a comment left during a code review.
**Path:** superset-frontend/src/pages/DatabaseList/index.tsx
**Line:** 145:145
**Comment:**
*Logic Error: The combined connections list is initialized with the
`user` object instead of an empty array, so before the first fetch the table
will render a bogus row whose shape doesn't match `ConnectionItem` (missing
fields like `database_name`, `backend`, etc.), which can lead to incorrect UI
state or runtime errors when columns try to access these properties; initialize
it as an empty array instead.
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%2F38376&comment_hash=db5364a258ddf06b37456e04def959e5a041e064215ba74c582ffb27c5ab1ddc&reaction=like'>👍</a>
| <a
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F38376&comment_hash=db5364a258ddf06b37456e04def959e5a041e064215ba74c582ffb27c5ab1ddc&reaction=dislike'>👎</a>
##########
superset/semantic_layers/api.py:
##########
@@ -436,6 +523,164 @@ def delete(self, uuid: str) -> FlaskResponse:
)
return self.response_422(message=str(ex))
+ @expose("/connections/", methods=("GET",))
+ @protect()
+ @safe
+ @statsd_metrics
+ @rison(get_list_schema)
+ @event_logger.log_this_with_context(
+ action=lambda self, *args, **kwargs: f"{self.__class__.__name__}"
+ ".connections",
+ log_to_statsd=False,
+ )
+ def connections(self, **kwargs: Any) -> FlaskResponse:
+ """List databases and semantic layers combined.
+ ---
+ get:
+ summary: List databases and semantic layers combined
+ parameters:
+ - in: query
+ name: q
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/get_list_schema'
+ responses:
+ 200:
+ description: Combined list of databases and semantic layers
+ 401:
+ $ref: '#/components/responses/401'
+ 500:
+ $ref: '#/components/responses/500'
+ """
+ args = kwargs.get("rison", {})
+ page = args.get("page", 0)
+ page_size = args.get("page_size", 25)
+ order_column = args.get("order_column", "changed_on")
+ order_direction = args.get("order_direction", "desc")
+ filters = args.get("filters", [])
+
+ source_type = "all"
+ name_filter = None
+ for f in filters:
+ if f.get("col") == "source_type":
+ source_type = f.get("value", "all")
+ elif f.get("col") == "database_name" and f.get("opr") == "ct":
+ name_filter = f.get("value")
+
+ if not is_feature_enabled("SEMANTIC_LAYERS"):
+ source_type = "database"
+
+ reverse = order_direction == "desc"
+
+ def _sort_key_changed_on(
+ item: tuple[str, Database | SemanticLayer],
+ ) -> Any:
+ return item[1].changed_on or ""
Review Comment:
**Suggestion:** The `_sort_key_changed_on` helper currently returns either a
`datetime` (when `changed_on` is set) or the empty string `""` (when it is
`None`), so when there is at least one object with `changed_on=None` the
in-place sort over `all_items` will attempt to compare `datetime` and `str`
keys and raise a `TypeError` at runtime; returning a single numeric type (e.g.
a timestamp or 0.0) for all cases avoids this mixed-type comparison issue.
[type error]
<details>
<summary><b>Severity Level:</b> Critical 🚨</summary>
```mdx
- ❌ GET /api/v1/semantic_layer/connections/ can crash sorting.
- ⚠️ Semantic connections UI breaks when some rows lack changed_on.
```
</details>
```suggestion
def _sort_key_changed_on(
item: tuple[str, Database | SemanticLayer],
) -> float:
changed_on = item[1].changed_on
return changed_on.timestamp() if changed_on else 0.0
```
<details>
<summary><b>Steps of Reproduction ✅ </b></summary>
```mdx
1. Start a Superset instance with semantic layers enabled, as in tests using
`SEMANTIC_LAYERS_APP`
(`superset/tests/unit_tests/semantic_layers/api_test.py:37-41`),
which configure `FEATURE_FLAGS: {"SEMANTIC_LAYERS": True}`.
2. Ensure there are at least two connection-like objects: (a) a `Database`
row with a
non-null `changed_on` timestamp and (b) a `SemanticLayer` or `Database` row
with
`changed_on` set to NULL in the database. The `changed_on` column is
nullable by design in
`AuditMixinNullable` (`superset/models/helpers.py:563-566`), and both
`Database`
(`superset/models/core.py:144-151` with `AuditMixinNullable` mixin) and
`SemanticLayer`
(`superset/semantic_layers/models.py:110-118` with `AuditMixinNullable`
mixin) inherit
this behavior, so rows with `changed_on` = NULL are valid.
3. Call the combined connections endpoint, which is registered by
Flask-AppBuilder as
`/api/v1/semantic_layer/connections/` for `SemanticLayerRestApi`
(`superset/semantic_layers/api.py:242-247` and `@expose("/connections/",
methods=("GET",))` at `superset/semantic_layers/api.py:526-536`), e.g. via
HTTP GET:
`GET
/api/v1/semantic_layer/connections/?q={"order_column":"changed_on_delta_humanized","order_direction":"desc"}`.
4. Inside `SemanticLayerRestApi.connections`
(`superset/semantic_layers/api.py:556-634`),
the code builds `all_items` combining `("database", Database)` and
`("semantic_layer",
SemanticLayer)` tuples and then sorts them in-place with:
- sort key selection at `superset/semantic_layers/api.py:592-596`, mapping
`"changed_on_delta_humanized"` to `_sort_key_changed_on`.
- `_sort_key_changed_on` definition at
`superset/semantic_layers/api.py:576-579`, which
currently returns `item[1].changed_on` (a `datetime`) or the empty string
`""` when
`changed_on` is `None`.
When both non-null and null `changed_on` values exist, Python's list sort
evaluates
keys that are a mix of `datetime` and `str`, and comparison between these
types raises
`TypeError: '<' not supported between instances of 'datetime.datetime'
and 'str'`,
causing the `/api/v1/semantic_layer/connections/` request to fail with
HTTP 500.
```
</details>
<details>
<summary><b>Prompt for AI Agent 🤖 </b></summary>
```mdx
This is a comment left during a code review.
**Path:** superset/semantic_layers/api.py
**Line:** 576:579
**Comment:**
*Type Error: The `_sort_key_changed_on` helper currently returns either
a `datetime` (when `changed_on` is set) or the empty string `""` (when it is
`None`), so when there is at least one object with `changed_on=None` the
in-place sort over `all_items` will attempt to compare `datetime` and `str`
keys and raise a `TypeError` at runtime; returning a single numeric type (e.g.
a timestamp or 0.0) for all cases avoids this mixed-type comparison issue.
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%2F38376&comment_hash=d7702e32b8aafb7140cbdc1ec6c8bf9809a99acc0f88edafef35d90b3a3c3d79&reaction=like'>👍</a>
| <a
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F38376&comment_hash=d7702e32b8aafb7140cbdc1ec6c8bf9809a99acc0f88edafef35d90b3a3c3d79&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]