codeant-ai-for-open-source[bot] commented on code in PR #41580:
URL: https://github.com/apache/superset/pull/41580#discussion_r3498736381


##########
superset/commands/datasource/list.py:
##########
@@ -193,53 +199,59 @@ def _resolve_source_type(
     @staticmethod
     def _parse_filters(
         filters: list[dict[str, Any]],
-    ) -> tuple[str, str | None, bool | None, str | None, int | None, str | 
None]:
+    ) -> _Filters:
         """
         Translate raw rison filter dicts into typed query parameters.
 
-        Returns:
-            source_type:        "all" | "database" | "semantic_layer"
+        Returns a ``_Filters`` dataclass with the following fields:
+
+            source_type:        ``"all"`` | ``"database"`` | 
``"semantic_layer"``
             name_filter:        substring to match against name/table_name
-            sql_filter:         True → physical only, False → virtual only, 
None → both
-            type_filter:        "semantic_view" when caller wants only
-                                semantic views
+            sql_filter:         ``True`` → physical only, ``False`` → virtual 
only, ``None`` → both
+            type_filter:        ``"semantic_view"`` when caller wants only 
semantic views
             database_id:        filter datasets to a specific database ID
             semantic_layer_uuid: filter semantic views to a specific semantic 
layer UUID
+            schema_filter:      filter datasets by schema name
+            owners_filter:      filter datasets by owner user IDs
+            changed_by_filter:  filter datasets by last-modified user ID
+            certified_filter:   ``True`` → certified only, ``False`` → 
uncertified only, ``None`` → both
         """
-        source_type = "all"
-        name_filter: str | None = None
-        sql_filter: bool | None = None
-        type_filter: str | None = None
-        database_id: int | None = None
-        semantic_layer_uuid: str | None = None
+        result = _Filters()
 
         for f in filters:
             col = f.get("col")
             opr = f.get("opr")
             value = f.get("value")
 
             if col == "source_type":
-                source_type = value or "all"
+                result.source_type = value or "all"
             elif col == "table_name" and f.get("opr") == "ct":
-                name_filter = value
+                result.name_filter = value
             elif col == "sql":
                 if opr == "dataset_is_null_or_empty" and value == 
"semantic_view":
-                    type_filter = "semantic_view"
+                    result.type_filter = "semantic_view"
                 elif opr == "dataset_is_null_or_empty" and isinstance(value, 
bool):
-                    sql_filter = value
+                    result.sql_filter = value
             elif col == "database" and value is not None:
                 try:
-                    database_id = int(value)
+                    result.database_id = int(value)
                 except (TypeError, ValueError):
                     pass
             elif col == "semantic_layer_uuid" and value is not None:
-                semantic_layer_uuid = str(value)
-
-        return (
-            source_type,
-            name_filter,
-            sql_filter,
-            type_filter,
-            database_id,
-            semantic_layer_uuid,
-        )
+                result.semantic_layer_uuid = str(value)
+            elif col == "schema" and opr == "eq":
+                result.schema_filter = value
+            elif col == "owners" and opr == "rel_m_m":
+                try:
+                    result.owners_filter = [int(value)]
+                except (TypeError, ValueError):
+                    pass
+            elif col == "changed_by" and opr == "rel_o_m":
+                try:
+                    result.changed_by_filter = int(value)
+                except (TypeError, ValueError):
+                    pass
+            elif col == "id" and opr == "dataset_is_certified":
+                result.certified_filter = bool(value)

Review Comment:
   **Suggestion:** Coercing the certified filter with `bool(value)` is unsafe 
because non-empty strings like `"false"` become `True`, which inverts filter 
behavior and returns certified datasets instead of uncertified ones. Preserve 
the tri-state contract by only accepting real booleans (or explicitly parsing 
known string forms) before assigning this field. [incorrect condition logic]
   
   <details>
   <summary><b>Severity Level:</b> Major ⚠️</summary>
   
   ```mdx
   - ❌ Certified filter may return opposite dataset set.
   - ⚠️ Tri-state certified filtering contract is broken.
   - ⚠️ External callers using string values get misfiltered results.
   ```
   </details>
   <details>
   <summary><b>Steps of Reproduction ✅ </b></summary>
   
   ```mdx
   1. The combined datasource list endpoint `combined_list` in
   `superset/datasource/api.py:2-11` exposes `GET /api/v1/datasource/` and 
accepts rison
   filters using the same custom `dataset_is_certified` operator that the 
dataset API uses
   (`superset/datasets/filters.py:38-42` and `superset/datasets/api.py:15-18`).
   
   2. A client issues `GET /api/v1/datasource/?q=<rison>` with a certified 
filter whose
   `value` is not a native boolean, for example `{"filters": [{"col": "id", 
"opr":
   "dataset_is_certified", "value": "false"}]}`, which is a realistic payload 
when the value
   is serialized as a string from the UI or an external integration.
   
   3. `combined_list` constructs `GetCombinedDatasourceListCommand` and calls 
`.run()` at
   `superset/datasource/api.py:41-46`; `_parse_filters` is invoked at
   `superset/commands/datasource/list.py:199-256`. When it reaches the 
certified branch at
   lines 254-255 (`elif col == "id" and opr == "dataset_is_certified":`), the 
code assigns
   `result.certified_filter = bool(value)`. For a non-empty string such as 
`"false"` or
   `"true"`, `bool(value)` evaluates to `True`, so user intent `value="false"` 
(meaning
   uncertified datasets) is misinterpreted as `certified_filter=True`.
   
   4. `_build_combined_query` at `superset/commands/datasource/list.py:119-138` 
forwards this
   mis-coerced `certified_filter=True` into `DatasourceDAO.build_dataset_query` 
at
   `superset/daos/datasource.py:95-105`. The certified filtering logic at
   `superset/daos/datasource.py:150-160` then applies the `"certification"` 
ilike condition
   (`SqlaTable.extra.isnot(None) & 
SqlaTable.extra.ilike('%"certification":%')`), returning
   only certified datasets even though the caller requested uncertified ones; 
similarly, any
   other truthy non-boolean value is treated as `True`, and falsey non-None 
values are
   treated as `False`, collapsing the intended tri-state contract 
(True/False/None) into a
   two-state interpretation and inverting filters when string values are used.
   ```
   </details>
   
   [![Fix in 
Cursor](https://new-codeant-butcket.s3.us-west-1.amazonaws.com/badges/fix-in-cursor-flat.svg)](https://app.codeant.ai/fix-in-ide?tool=cursor&prompt_id=835cea5a7ab24529ae58578bceb189d3&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset)
 [![Fix in VSCode 
Claude](https://new-codeant-butcket.s3.us-west-1.amazonaws.com/badges/fix-in-vscode-claude-flat.svg)](https://app.codeant.ai/fix-in-ide?tool=vscode-claude&prompt_id=835cea5a7ab24529ae58578bceb189d3&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/commands/datasource/list.py
   **Line:** 254:255
   **Comment:**
        *Incorrect Condition Logic: Coercing the certified filter with 
`bool(value)` is unsafe because non-empty strings like `"false"` become `True`, 
which inverts filter behavior and returns certified datasets instead of 
uncertified ones. Preserve the tri-state contract by only accepting real 
booleans (or explicitly parsing known string forms) before assigning this field.
   
   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%2F41580&comment_hash=0d3b4ce207819884107ca7a4af3cb0670ba2bd97f58583611daecf473a1ce845&reaction=like'>👍</a>
 | <a 
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F41580&comment_hash=0d3b4ce207819884107ca7a4af3cb0670ba2bd97f58583611daecf473a1ce845&reaction=dislike'>👎</a>



##########
superset/commands/datasource/list.py:
##########
@@ -193,53 +199,59 @@ def _resolve_source_type(
     @staticmethod
     def _parse_filters(
         filters: list[dict[str, Any]],
-    ) -> tuple[str, str | None, bool | None, str | None, int | None, str | 
None]:
+    ) -> _Filters:
         """
         Translate raw rison filter dicts into typed query parameters.
 
-        Returns:
-            source_type:        "all" | "database" | "semantic_layer"
+        Returns a ``_Filters`` dataclass with the following fields:
+
+            source_type:        ``"all"`` | ``"database"`` | 
``"semantic_layer"``
             name_filter:        substring to match against name/table_name
-            sql_filter:         True → physical only, False → virtual only, 
None → both
-            type_filter:        "semantic_view" when caller wants only
-                                semantic views
+            sql_filter:         ``True`` → physical only, ``False`` → virtual 
only, ``None`` → both
+            type_filter:        ``"semantic_view"`` when caller wants only 
semantic views
             database_id:        filter datasets to a specific database ID
             semantic_layer_uuid: filter semantic views to a specific semantic 
layer UUID
+            schema_filter:      filter datasets by schema name
+            owners_filter:      filter datasets by owner user IDs
+            changed_by_filter:  filter datasets by last-modified user ID
+            certified_filter:   ``True`` → certified only, ``False`` → 
uncertified only, ``None`` → both
         """
-        source_type = "all"
-        name_filter: str | None = None
-        sql_filter: bool | None = None
-        type_filter: str | None = None
-        database_id: int | None = None
-        semantic_layer_uuid: str | None = None
+        result = _Filters()
 
         for f in filters:
             col = f.get("col")
             opr = f.get("opr")
             value = f.get("value")
 
             if col == "source_type":
-                source_type = value or "all"
+                result.source_type = value or "all"
             elif col == "table_name" and f.get("opr") == "ct":
-                name_filter = value
+                result.name_filter = value
             elif col == "sql":
                 if opr == "dataset_is_null_or_empty" and value == 
"semantic_view":
-                    type_filter = "semantic_view"
+                    result.type_filter = "semantic_view"
                 elif opr == "dataset_is_null_or_empty" and isinstance(value, 
bool):
-                    sql_filter = value
+                    result.sql_filter = value
             elif col == "database" and value is not None:
                 try:
-                    database_id = int(value)
+                    result.database_id = int(value)
                 except (TypeError, ValueError):
                     pass
             elif col == "semantic_layer_uuid" and value is not None:
-                semantic_layer_uuid = str(value)
-
-        return (
-            source_type,
-            name_filter,
-            sql_filter,
-            type_filter,
-            database_id,
-            semantic_layer_uuid,
-        )
+                result.semantic_layer_uuid = str(value)
+            elif col == "schema" and opr == "eq":
+                result.schema_filter = value
+            elif col == "owners" and opr == "rel_m_m":
+                try:
+                    result.owners_filter = [int(value)]
+                except (TypeError, ValueError):
+                    pass

Review Comment:
   **Suggestion:** The `rel_m_m` owners filter currently assumes a scalar value 
and silently drops the filter when the API sends the standard multi-value 
payload (e.g. a list of owner IDs). This causes owner filtering to be ignored 
for valid requests. Parse both scalar and list inputs into a list of ints so 
many-to-many filtering works reliably. [api mismatch]
   
   <details>
   <summary><b>Severity Level:</b> Major ⚠️</summary>
   
   ```mdx
   - ❌ Combined datasource list ignores multi-owner filters.
   - ⚠️ Users see datasets beyond selected owners.
   - ⚠️ Owner-based auditing of datasets becomes unreliable.
   ```
   </details>
   <details>
   <summary><b>Steps of Reproduction ✅ </b></summary>
   
   ```mdx
   1. The combined datasource list endpoint `combined_list` in
   `superset/datasource/api.py:2-11` is exposed as `GET /api/v1/datasource/` 
with
   rison-parsed query arguments via `@rison(get_list_schema)` at
   `superset/datasource/api.py:6`.
   
   2. A client (or the Superset UI) calls `GET /api/v1/datasource/?q=<rison>` 
with a filters
   payload containing an owners many-to-many filter, e.g. `{"filters": [{"col": 
"owners",
   "opr": "rel_m_m", "value": [admin.id, gamma.id]}]}`, matching the 
established pattern in
   `tests/integration_tests/dashboards/api_tests.py:861` for `rel_m_m` owner 
filters.
   
   3. `combined_list` constructs `GetCombinedDatasourceListCommand` and calls 
`.run()` at
   `superset/datasource/api.py:41-46`, which in turn calls
   `_parse_filters(self._args.get("filters", []))` at
   `superset/commands/datasource/list.py:71-79` to translate the raw rison 
filters.
   
   4. Inside `_parse_filters` at 
`superset/commands/datasource/list.py:221-256`, when it
   reaches the owners branch at lines 244-248 (`elif col == "owners" and opr == 
"rel_m_m":`),
   `value` is a list of user IDs (e.g. `[admin.id, gamma.id]`), so `int(value)` 
raises a
   `TypeError`. The exception is caught, the `pass` at line 248 executes, and
   `result.owners_filter` remains `None` instead of being populated with the 
list of owner
   IDs.
   
   5. `_build_combined_query` at `superset/commands/datasource/list.py:119-138` 
passes
   `filters.owners_filter` (still `None`) into 
`DatasourceDAO.build_dataset_query` at
   `superset/daos/datasource.py:95-105`. Because `owners_filter is not None` 
check at
   `superset/daos/datasource.py:141-145` fails, the join on 
`sqla_models.sqlatable_user` and
   the `user_id.in_(owners_filter)` predicate are never applied, so the 
resulting combined
   list ignores the owners many-to-many filter and returns datasets owned by 
any user.
   ```
   </details>
   
   [![Fix in 
Cursor](https://new-codeant-butcket.s3.us-west-1.amazonaws.com/badges/fix-in-cursor-flat.svg)](https://app.codeant.ai/fix-in-ide?tool=cursor&prompt_id=413fab80e8894160993ea12afea99f3e&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset)
 [![Fix in VSCode 
Claude](https://new-codeant-butcket.s3.us-west-1.amazonaws.com/badges/fix-in-vscode-claude-flat.svg)](https://app.codeant.ai/fix-in-ide?tool=vscode-claude&prompt_id=413fab80e8894160993ea12afea99f3e&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/commands/datasource/list.py
   **Line:** 244:248
   **Comment:**
        *Api Mismatch: The `rel_m_m` owners filter currently assumes a scalar 
value and silently drops the filter when the API sends the standard multi-value 
payload (e.g. a list of owner IDs). This causes owner filtering to be ignored 
for valid requests. Parse both scalar and list inputs into a list of ints so 
many-to-many filtering works reliably.
   
   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%2F41580&comment_hash=81424dcf36bed3cc68a5357f2e55ac635a85bd51a00e7a8a0b49126e8fdc5ef0&reaction=like'>👍</a>
 | <a 
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F41580&comment_hash=81424dcf36bed3cc68a5357f2e55ac635a85bd51a00e7a8a0b49126e8fdc5ef0&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]

Reply via email to