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


##########
superset-frontend/src/extensions/ExtensionsList.tsx:
##########
@@ -50,6 +61,45 @@ const ExtensionsList: FunctionComponent<ExtensionsListProps> 
= ({
     addDangerToast,
   );
 
+  const [settings, setSettings] = useState<ExtensionSettings>({
+    active_chatbot_id: null,
+    enabled: {},
+  });
+
+  useEffect(() => {
+    SupersetClient.get({ endpoint: '/api/v1/extensions/settings' })
+      .then(({ json }) => setSettings(json.result))
+      .catch(() => addDangerToast(t('Failed to load extension settings.')));
+  }, [addDangerToast]);
+
+  const saveSettings = useCallback(
+    (patch: Partial<ExtensionSettings>) => {
+      const next = { ...settings, ...patch };
+      SupersetClient.put({
+        endpoint: '/api/v1/extensions/settings',
+        jsonPayload: next,

Review Comment:
   **Suggestion:** Saving always sends a full object built from a captured 
`settings` snapshot, so overlapping saves from different controls can overwrite 
each other (for example, a later toggle request can revert a recently selected 
default chatbot). Avoid closure-stale full-payload writes by basing updates on 
latest state and/or sending only the changed patch to the backend. [race 
condition]
   
   <details>
   <summary><b>Severity Level:</b> Major ⚠️</summary>
   
   ```mdx
   - ❌ Rapid edits can silently revert saved chatbot selection.
   - ⚠️ Enabled flags may not match latest admin intention.
   ```
   </details>
   <details>
   <summary><b>Steps of Reproduction ✅ </b></summary>
   
   ```mdx
   1. On the `/extensions/list/` admin page, `ExtensionsList` loads the current 
settings into
   local state via a GET to `/api/v1/extensions/settings`
   (superset-frontend/src/extensions/ExtensionsList.tsx:64-73) and defines 
`saveSettings`
   using the then-current `settings` snapshot (lines 75-89).
   
   2. The admin changes the default chatbot using the "Default chatbot" 
`<Select>`
   (superset-frontend/src/extensions/ExtensionsList.tsx:145-163); its 
`onChange` calls
   `saveSettings({ active_chatbot_id: (value as string) ?? null })` (lines 
153-159). Inside
   `saveSettings`, `const next = { ...settings, ...patch };` (line 77) merges 
this patch into
   the captured `settings` and sends a PUT to `/api/v1/extensions/settings` 
with the full
   `next` object (lines 78-80).
   
   3. Before the first PUT resolves and updates local state via 
`setSettings(json.result)`
   (superset-frontend/src/extensions/ExtensionsList.tsx:83), the admin toggles 
an extension's
   "Enabled" switch. The `toggleEnabled` handler (lines 91-95) calls 
`saveSettings({ enabled:
   { ...settings.enabled, [extensionId]: enabled } })`, but this `saveSettings` 
closure is
   still using the same stale `settings` snapshot from before the chatbot 
change, so `next`
   is recomputed from the original settings (with the old `active_chatbot_id`) 
plus the
   updated `enabled` map.
   
   4. Both PUTs are handled by `ExtensionsRestApi.put_settings`
   (superset/superset/extensions/api.py:190-215), which delegates to
   `update_extension_settings` 
(superset/superset/extensions/settings.py:37-54). Since
   `update_extension_settings` applies both `active_chatbot_id` and `enabled` 
whenever those
   keys are present in the body, the second request—built from the stale 
snapshot—reapplies
   the old `active_chatbot_id` while updating `enabled`, effectively 
overwriting the fresh
   chatbot selection from the first request and leaving backend settings that 
do not reflect
   the admin's most recent combination of changes.
   ```
   </details>
   
   [Fix in 
Cursor](https://app.codeant.ai/fix-in-ide?tool=cursor&prompt_id=8a6b26ac51394ff88d514e1f399504c4&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset)
 | [Fix in VSCode 
Claude](https://app.codeant.ai/fix-in-ide?tool=vscode-claude&prompt_id=8a6b26ac51394ff88d514e1f399504c4&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-frontend/src/extensions/ExtensionsList.tsx
   **Line:** 77:80
   **Comment:**
        *Race Condition: Saving always sends a full object built from a 
captured `settings` snapshot, so overlapping saves from different controls can 
overwrite each other (for example, a later toggle request can revert a recently 
selected default chatbot). Avoid closure-stale full-payload writes by basing 
updates on latest state and/or sending only the changed patch to the backend.
   
   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%2F40433&comment_hash=9d8709b4644a601308fff75747afb474fd549b65da6052a7bfcd07520572cd52&reaction=like'>👍</a>
 | <a 
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F40433&comment_hash=9d8709b4644a601308fff75747afb474fd549b65da6052a7bfcd07520572cd52&reaction=dislike'>👎</a>



##########
superset-frontend/src/extensions/ExtensionsList.tsx:
##########
@@ -58,15 +108,34 @@ const ExtensionsList: 
FunctionComponent<ExtensionsListProps> = ({
         size: 'lg',
         id: 'name',
         Cell: ({
-          row: {
-            original: { name },
-          },
+          row: { original: { name } },
         }: any) => name,
       },
+      {
+        Header: t('Enabled'),
+        accessor: 'enabled',
+        size: 'sm',
+        id: 'enabled',
+        Cell: ({
+          row: { original: { id, enabled } },
+        }: any) => (
+          <Switch
+            data-test="toggle-enabled"
+            checked={settings.enabled[id] ?? enabled}
+            onClick={(checked: boolean) => toggleEnabled(id, checked)}
+            size="small"
+          />
+        ),

Review Comment:
   **Suggestion:** The switch fallback uses `enabled` from the extensions list 
payload, but `/api/v1/extensions/` does not provide an `enabled` field (it only 
returns extension metadata), so untouched rows resolve to `undefined` and 
render with an incorrect checked state. Use an explicit default (for example 
`true`) or source enabled state solely from settings with a deterministic 
fallback. [api mismatch]
   
   <details>
   <summary><b>Severity Level:</b> Major ⚠️</summary>
   
   ```mdx
   - ❌ Extensions admin switches show incorrect enabled state initially.
   - ⚠️ Future enforcement of flags may start from wrong values.
   ```
   </details>
   <details>
   <summary><b>Steps of Reproduction ✅ </b></summary>
   
   ```mdx
   1. As an admin with `FeatureFlag.EnableExtensions` enabled, navigate to
   `/extensions/list/`, which routes to the lazy `Extensions` component
   (superset-frontend/src/views/routes.tsx:170-175) and mounts `ExtensionsList`
   (superset-frontend/src/extensions/ExtensionsList.tsx:50-53).
   
   2. `ExtensionsList` uses `useListViewResource<Extension>('extensions', ...)`
   (superset-frontend/src/extensions/ExtensionsList.tsx:54-62). The hook's 
`fetchData` issues
   `GET /api/v1/extensions/?q=...` for the `extensions` resource
   (superset-frontend/src/views/CRUD/hooks.ts:152-196).
   
   3. The backend handler `ExtensionsRestApi.get_list`
   (superset/superset/extensions/api.py:74-120) builds each entry via 
`build_extension_data`
   (superset/superset/extensions/utils.py:233-255), which returns objects 
containing `id`,
   `name`, `version`, `description`, `dependencies`, and optional frontend 
fields, but no
   `enabled` property.
   
   4. In `ExtensionsList` the "Enabled" column renders a `<Switch>` whose 
`checked` value is
   `settings.enabled[id] ?? enabled`
   (superset-frontend/src/extensions/ExtensionsList.tsx:115-125). For any 
extension without a
   row in the `extension_enabled` table, the settings payload from
   `/api/v1/extensions/settings` 
(superset-frontend/src/extensions/ExtensionsList.tsx:64-73
   and superset/superset/extensions/settings.py:28-35) does not include that 
`extension_id`,
   so `settings.enabled[id]` is `undefined`, and the API response does not 
define `enabled`
   at all. Thus `checked` evaluates to `undefined`, and the toggle renders 
unchecked even
   though `get_list` is documented to "List all enabled extensions"
   (superset/superset/extensions/api.py:79-83), causing the UI to misrepresent 
the enabled
   status for every extension without an explicit flag.
   ```
   </details>
   
   [Fix in 
Cursor](https://app.codeant.ai/fix-in-ide?tool=cursor&prompt_id=42833878ead44f21a490563e8d46a1f6&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset)
 | [Fix in VSCode 
Claude](https://app.codeant.ai/fix-in-ide?tool=vscode-claude&prompt_id=42833878ead44f21a490563e8d46a1f6&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-frontend/src/extensions/ExtensionsList.tsx
   **Line:** 115:128
   **Comment:**
        *Api Mismatch: The switch fallback uses `enabled` from the extensions 
list payload, but `/api/v1/extensions/` does not provide an `enabled` field (it 
only returns extension metadata), so untouched rows resolve to `undefined` and 
render with an incorrect checked state. Use an explicit default (for example 
`true`) or source enabled state solely from settings with a deterministic 
fallback.
   
   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%2F40433&comment_hash=5477cc6b3b80b41f1460840f57c5e5366703f04cde8c3a7dd89103b3d2e007a3&reaction=like'>👍</a>
 | <a 
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F40433&comment_hash=5477cc6b3b80b41f1460840f57c5e5366703f04cde8c3a7dd89103b3d2e007a3&reaction=dislike'>👎</a>



##########
superset-frontend/src/components/ChatbotMount/index.tsx:
##########
@@ -0,0 +1,76 @@
+/**
+ * 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.
+ */
+import { useState, useEffect } from 'react';
+import { SupersetClient } from '@superset-ui/core';
+import { css, useTheme } from '@apache-superset/core/theme';
+import { ErrorBoundary } from 'src/components/ErrorBoundary';
+import { getActiveChatbot } from 'src/core/chatbot';
+import { subscribeToLocation } from 'src/core/views';
+import { CHATBOT_LOCATION } from 'src/views/contributions';
+
+const CHATBOT_EDGE_MARGIN = 24;
+
+const ChatbotMount = () => {
+  const theme = useTheme();
+  const [adminSelectedId, setAdminSelectedId] = useState<string | null>(null);
+  const [activeChatbot, setActiveChatbot] = useState(() =>
+    getActiveChatbot(null),
+  );
+
+  useEffect(() => {
+    SupersetClient.get({ endpoint: '/api/v1/extensions/settings' })
+      .then(({ json }) => {
+        const id = json.result?.active_chatbot_id ?? null;
+        setAdminSelectedId(id);
+        setActiveChatbot(getActiveChatbot(id));
+      })
+      .catch(() => {
+        // Settings fetch failure is non-fatal — fall back to 
first-to-register.
+      });

Review Comment:
   **Suggestion:** The mount reads admin settings only once on initial render, 
so changing the default chatbot in the admin page during the same session does 
not update the active chatbot bubble until a full reload. Add a reactive 
refresh path (e.g., subscribe to settings changes or re-fetch after settings 
updates) so the mounted chatbot stays in sync. [stale reference]
   
   <details>
   <summary><b>Severity Level:</b> Major ⚠️</summary>
   
   ```mdx
   - ❌ Chatbot bubble ignores updated admin default selection.
   - ⚠️ Admin must reload page to see new chatbot.
   ```
   </details>
   <details>
   <summary><b>Steps of Reproduction ✅ </b></summary>
   
   ```mdx
   1. Start the Superset frontend using `App` 
(superset-frontend/src/views/App.tsx:48-93),
   which always renders `<ChatbotMount />` inside `<ExtensionsStartup>`
   (superset-frontend/src/views/App.tsx:57-89).
   
   2. On initial render, `ChatbotMount`
   (superset-frontend/src/components/ChatbotMount/index.tsx:29-55) runs its 
first
   `useEffect`, which GETs `/api/v1/extensions/settings` and sets 
`adminSelectedId` and
   `activeChatbot` from `json.result?.active_chatbot_id` (lines 36-41); this 
effect has an
   empty dependency array so it never runs again.
   
   3. As an admin, navigate to `/extensions/list/`, which is wired to the 
`Extensions` route
   when `FeatureFlag.EnableExtensions` is on
   (superset-frontend/src/views/routes.tsx:170-175); this lazy-loads 
`ExtensionsList`
   (superset-frontend/src/extensions/ExtensionsList.tsx:50-53), where changing 
the "Default
   chatbot" select (lines 145-163) calls `saveSettings` and sends a PUT to
   `/api/v1/extensions/settings` updating `active_chatbot_id`
   (superset/superset/extensions/api.py:190-215,
   superset/superset/extensions/settings.py:37-45).
   
   4. After the PUT succeeds, observe the chatbot bubble rendered by 
`ChatbotMount` in the
   bottom-right: it still uses the original `adminSelectedId` from its initial 
GET, because
   `adminSelectedId` is never updated again and 
`subscribeToLocation(CHATBOT_LOCATION, ...)`
   (superset-frontend/src/components/ChatbotMount/index.tsx:48-54) only re-runs
   `getActiveChatbot(adminSelectedId)` on view-registration changes, not on 
settings changes.
   The new default chosen in the admin page is not applied until the SPA is 
fully reloaded.
   ```
   </details>
   
   [Fix in 
Cursor](https://app.codeant.ai/fix-in-ide?tool=cursor&prompt_id=c2721a604fde4c178d4ccef3c1962d32&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset)
 | [Fix in VSCode 
Claude](https://app.codeant.ai/fix-in-ide?tool=vscode-claude&prompt_id=c2721a604fde4c178d4ccef3c1962d32&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-frontend/src/components/ChatbotMount/index.tsx
   **Line:** 36:45
   **Comment:**
        *Stale Reference: The mount reads admin settings only once on initial 
render, so changing the default chatbot in the admin page during the same 
session does not update the active chatbot bubble until a full reload. Add a 
reactive refresh path (e.g., subscribe to settings changes or re-fetch after 
settings updates) so the mounted chatbot stays in sync.
   
   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%2F40433&comment_hash=a7305f15a9ad4250b97b7afbc5ac7b84ddf9baf05437d613c2d9d03dd4a194e5&reaction=like'>👍</a>
 | <a 
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F40433&comment_hash=a7305f15a9ad4250b97b7afbc5ac7b84ddf9baf05437d613c2d9d03dd4a194e5&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