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


##########
superset-frontend/src/core/views/index.ts:
##########
@@ -52,9 +73,12 @@ const registerView: typeof viewsApi.registerView = (
   ids.add(id);
   locationIndex.set(location, ids);
 
+  notifyListeners(location);
+
   return new Disposable(() => {
     viewRegistry.delete(id);
     locationIndex.get(location)?.delete(id);
+    notifyListeners(location);
   });

Review Comment:
   **Suggestion:** Listener notifications are emitted only for the incoming 
`location`, so when a view id is re-registered under a different location, 
subscribers of the previous location are never notified and can keep stale UI 
state. Track the previous location for an existing id and notify both old and 
new locations on overwrite/removal. [logic error]
   
   <details>
   <summary><b>Severity Level:</b> Major ⚠️</summary>
   
   ```mdx
   - ⚠️ Admin extensions list can show stale chatbot registrations.
   - ⚠️ Location-based listeners remain out-of-sync after id moves.
   ```
   </details>
   <details>
   <summary><b>Steps of Reproduction ✅ </b></summary>
   
   ```mdx
   1. Start the frontend so the host views registry in 
`src/core/views/index.ts` is
   initialized and components subscribe to locations: `ChatbotMount` subscribes 
to
   `CHATBOT_LOCATION` via `subscribeToLocation` at
   `src/components/ChatbotMount/index.tsx:56-60`, and the admin extensions UI 
subscribes
   similarly in `src/extensions/ExtensionsList.tsx:10-16`.
   
   2. Have an extension register a chatbot view at `CHATBOT_LOCATION` using
   `views.registerView` (the host implementation at 
`src/core/views/index.ts:63-76` and
   public API documented in `packages/superset-core/src/views/index.ts:16-23`), 
which adds
   the view id to `locationIndex.get(CHATBOT_LOCATION)` and calls
   `notifyListeners(CHATBOT_LOCATION)` at `src/core/views/index.ts:76`, causing 
subscribers
   like `ExtensionsList` (`chatbotRegistryVersion` in
   `src/extensions/ExtensionsList.tsx:10-16`) to observe the change.
   
   3. Later, the same extension (or a buggy update of it) re-registers the same 
view id under
   a different location (e.g. `'sqllab.panels'`) without disposing the original 
`Disposable`;
   `registerView` overwrites the `viewRegistry` entry with the new `location` 
and `provider`
   but only calls `notifyListeners(location)` for the new location at
   `src/core/views/index.ts:76`, leaving the id still present in the old 
location's
   `locationIndex` set and emitting no notification for the old location.
   
   4. Subscribers for the old location (e.g. `ExtensionsList` using
   `subscribeToLocation(CHATBOT_LOCATION, ...)` at 
`src/extensions/ExtensionsList.tsx:10-16`
   and recomputing `chatbotExtensions` from 
`getRegisteredViewIds(CHATBOT_LOCATION)` at
   `src/extensions/ExtensionsList.tsx:48-51`) never receive an event about the 
id moving, so
   they keep stale state: the admin UI can list a chatbot that no longer truly 
resides at
   `CHATBOT_LOCATION`, and any logic depending on registration-change 
notifications for that
   location stays out of sync with the actual `viewRegistry`.
   ```
   </details>
   
   [Fix in 
Cursor](https://app.codeant.ai/fix-in-ide?tool=cursor&prompt_id=9c0afd1bc7ac4ddba5b9407b4869ad3f&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=9c0afd1bc7ac4ddba5b9407b4869ad3f&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/core/views/index.ts
   **Line:** 76:82
   **Comment:**
        *Logic Error: Listener notifications are emitted only for the incoming 
`location`, so when a view id is re-registered under a different location, 
subscribers of the previous location are never notified and can keep stale UI 
state. Track the previous location for an existing id and notify both old and 
new locations on overwrite/removal.
   
   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%2F40443&comment_hash=d6424215b9522063644fd7ba7d315a314f39dd7f3bc8ec21cc0369cd4a112fd3&reaction=like'>👍</a>
 | <a 
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F40443&comment_hash=d6424215b9522063644fd7ba7d315a314f39dd7f3bc8ec21cc0369cd4a112fd3&reaction=dislike'>👎</a>



##########
superset-frontend/src/core/chatbot/index.ts:
##########
@@ -0,0 +1,87 @@
+/**
+ * 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.
+ */
+/**
+ * @fileoverview Host-internal resolver for the exclusive `superset.chatbot`
+ * contribution area.
+ *
+ * `superset.chatbot` is a singleton contribution area: multiple chatbot
+ * extensions may register a view there, but the host renders exactly one.
+ * This module owns the host-side selection policy.
+ *
+ * This is host-internal infrastructure — it is NOT part of the public
+ * `@apache-superset/core` API. Extensions register via the public
+ * `views.registerView()`; only the host resolves which one is active.
+ */
+
+import { ReactElement } from 'react';
+import { CHATBOT_LOCATION } from 'src/views/contributions';
+import { getRegisteredViewIds, getViewProvider } from 'src/core/views';
+
+/**
+ * The resolved active chatbot: a view id paired with its renderable provider.
+ */
+export interface ActiveChatbot {
+  /** The registered view id of the selected chatbot. */
+  id: string;
+  /** The provider that renders the chatbot's React element. */
+  provider: () => ReactElement;
+}
+
+/**
+ * Resolves which single chatbot extension is currently active.
+ *
+ * Selection policy:
+ *  - If no chatbot is registered, returns `undefined` — the corner stays 
empty.
+ *  - Disabled chatbots (per `enabledMap`) are excluded before selection.
+ *  - If `adminSelectedId` matches an enabled registered chatbot, that one 
wins.
+ *  - Otherwise the first enabled chatbot in registration order is used as a 
fallback.
+ *
+ * @param adminSelectedId The id stored in the admin "Default chatbot" 
setting, if any.
+ * @param enabledMap Per-extension enabled flags from the admin settings API.
+ * @returns The active chatbot's id and provider, or `undefined` if none.
+ */
+export const getActiveChatbot = (
+  adminSelectedId?: string | null,
+  enabledMap?: Record<string, boolean>,
+): ActiveChatbot | undefined => {
+  const registeredIds = getRegisteredViewIds(CHATBOT_LOCATION);
+  if (registeredIds.length === 0) {
+    return undefined;
+  }
+
+  const candidates = enabledMap
+    ? registeredIds.filter(id => enabledMap[id] !== false)
+    : registeredIds;
+
+  if (candidates.length === 0) {
+    return undefined;
+  }
+
+  const selectedId =
+    adminSelectedId && candidates.includes(adminSelectedId)
+      ? adminSelectedId
+      : candidates[0];
+
+  const provider = getViewProvider(CHATBOT_LOCATION, selectedId);
+  if (!provider) {
+    return undefined;
+  }

Review Comment:
   **Suggestion:** The resolver returns `undefined` immediately when the 
selected id has no provider, instead of trying the remaining candidates. With 
stale ids (for example after id collisions/re-registrations), this makes the 
chatbot disappear even when another valid chatbot is registered. Iterate 
through candidates and pick the first id that still resolves to a provider. 
[logic error]
   
   <details>
   <summary><b>Severity Level:</b> Major ⚠️</summary>
   
   ```mdx
   - ❌ Chatbot bubble can disappear despite enabled extensions.
   - ⚠️ Admin-selected chatbot id silently yields empty UI.
   ```
   </details>
   <details>
   <summary><b>Steps of Reproduction ✅ </b></summary>
   
   ```mdx
   1. Start the frontend with extensions enabled so `ExtensionsStartup` 
initializes the
   extension host and renders `ChatbotMount` (see
   `superset-frontend/src/extensions/ExtensionsStartup.tsx:18-25` and
   `superset-frontend/src/components/ChatbotMount/index.tsx:29-35`).
   
   2. Have two chatbot extensions register views at `CHATBOT_LOCATION` using 
the public API
   documented in `packages/superset-core/src/views/index.ts:16-23`, e.g. 
similar to the tests
   in `src/core/chatbot/index.test.ts:49-62` where `views.registerView` is 
called with ids
   `first.chatbot` and `second.chatbot` at `'superset.chatbot'`.
   
   3. Introduce a stale id by re-registering the `first.chatbot` id at a 
different location
   (e.g. `'sqllab.panels'`) without disposing the original registration; 
`registerView` at
   `src/core/views/index.ts:63-76` overwrites the `viewRegistry` entry to point 
to the new
   location but leaves `first.chatbot` in the `locationIndex` set for 
`CHATBOT_LOCATION`, so
   `getRegisteredViewIds(CHATBOT_LOCATION)` (`src/core/views/index.ts:121-123`) 
returns
   `['first.chatbot', 'second.chatbot']` even though `first.chatbot` is no 
longer actually at
   that location.
   
   4. Trigger chatbot resolution (for example via the `subscribeToLocation` 
callback in
   `ChatbotMount` at `src/components/ChatbotMount/index.tsx:56-60`, which calls
   `setActiveChatbot(getActiveChatbot(adminSelectedId, enabledMap))`): 
`getActiveChatbot`
   (`src/core/chatbot/index.ts:59-86`) selects `selectedId='first.chatbot'`, 
then
   `getViewProvider(CHATBOT_LOCATION, selectedId)` 
(`src/core/views/index.ts:109-117`)
   returns `undefined` because the `viewRegistry` entry's `location` no longer 
matches
   `CHATBOT_LOCATION`, so the function returns `undefined` at
   `src/core/chatbot/index.ts:81-84` without ever trying `second.chatbot`, 
causing the
   chatbot bubble to disappear even though a valid second chatbot is still 
registered.
   ```
   </details>
   
   [Fix in 
Cursor](https://app.codeant.ai/fix-in-ide?tool=cursor&prompt_id=2dc5f84a953c40638373fa17a1c1fffe&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=2dc5f84a953c40638373fa17a1c1fffe&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/core/chatbot/index.ts
   **Line:** 81:84
   **Comment:**
        *Logic Error: The resolver returns `undefined` immediately when the 
selected id has no provider, instead of trying the remaining candidates. With 
stale ids (for example after id collisions/re-registrations), this makes the 
chatbot disappear even when another valid chatbot is registered. Iterate 
through candidates and pick the first id that still resolves to a provider.
   
   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%2F40443&comment_hash=adc608cb072e1651d9255095420e00ce3bbb85ece33c1b0ae2e0e90b15b1be83&reaction=like'>👍</a>
 | <a 
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F40443&comment_hash=adc608cb072e1651d9255095420e00ce3bbb85ece33c1b0ae2e0e90b15b1be83&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