feat(object-browser): add table Add to AI context menu entry
This commit is contained in:
1 parent
182b6f3d8d
commit
464f2f33c5
8 files changed
+221
-1
No files matched your search
@@ -0,0 +1,92 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { objectBrowserTablesToAiTreeNodes } from "@/lib/ai/objectBrowserToAiTargets";
|
||||
|
||||
function tab(overrides: Partial<Parameters<typeof objectBrowserTablesToAiTreeNodes>[0]> = {}) {
|
||||
return {
|
||||
id: "tab-1",
|
||||
connectionId: "conn-1",
|
||||
database: "db-1",
|
||||
schema: undefined,
|
||||
catalog: undefined,
|
||||
...overrides,
|
||||
} as any;
|
||||
}
|
||||
|
||||
describe("objectBrowserTablesToAiTreeNodes", () => {
|
||||
it("maps table names and row-level schemas into AI tree nodes", () => {
|
||||
const nodes = objectBrowserTablesToAiTreeNodes(tab(), [
|
||||
{ name: "users", schema: "public" },
|
||||
{ name: "orders", schema: "sales" },
|
||||
]);
|
||||
|
||||
expect(nodes).toEqual([
|
||||
{
|
||||
id: "conn-1:db-1:public:users",
|
||||
label: "users",
|
||||
type: "table",
|
||||
connectionId: "conn-1",
|
||||
database: "db-1",
|
||||
catalog: undefined,
|
||||
schema: "public",
|
||||
},
|
||||
{
|
||||
id: "conn-1:db-1:sales:orders",
|
||||
label: "orders",
|
||||
type: "table",
|
||||
connectionId: "conn-1",
|
||||
database: "db-1",
|
||||
catalog: undefined,
|
||||
schema: "sales",
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("falls back to the tab schema when a row has no schema", () => {
|
||||
const nodes = objectBrowserTablesToAiTreeNodes(tab({ schema: "app" }), [{ name: "users" }]);
|
||||
|
||||
expect(nodes[0].schema).toBe("app");
|
||||
expect(nodes[0].id).toBe("conn-1:db-1:app:users");
|
||||
});
|
||||
|
||||
it("keeps the schema undefined instead of falling back to the database name", () => {
|
||||
const nodes = objectBrowserTablesToAiTreeNodes(tab(), [{ name: "users" }, { name: "orders", schema: undefined }]);
|
||||
|
||||
expect(nodes.map((node) => node.schema)).toEqual([undefined, undefined]);
|
||||
expect(nodes[0].id).toBe("conn-1:db-1::users");
|
||||
});
|
||||
|
||||
it("keeps the schema undefined for schema-less engines so addToAi reuses the current tab", () => {
|
||||
// MySQL/MariaDB-style connection: object-browser rows and the tab both
|
||||
// carry schema: undefined (rows via connectionObjectTreeNodeSchema, which
|
||||
// returns undefined for schema-less engines). The synthesized node must
|
||||
// keep schema undefined so App.vue's addToAi() tab matching
|
||||
// ((tab.schema || "") === (target.schema || "")) reuses the current tab
|
||||
// instead of spawning a stray query tab and clearing accumulated AI
|
||||
// context via clearContextReferences().
|
||||
const nodes = objectBrowserTablesToAiTreeNodes(tab(), [{ name: "users" }]);
|
||||
|
||||
expect(nodes[0]).toEqual({
|
||||
id: "conn-1:db-1::users",
|
||||
label: "users",
|
||||
type: "table",
|
||||
connectionId: "conn-1",
|
||||
database: "db-1",
|
||||
catalog: undefined,
|
||||
schema: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
it("carries the tab catalog through for external-catalog contexts (Doris/StarRocks)", () => {
|
||||
const nodes = objectBrowserTablesToAiTreeNodes(tab({ catalog: "hive_catalog" }), [{ name: "orders", schema: "tpch" }]);
|
||||
|
||||
expect(nodes[0]).toEqual({
|
||||
id: "conn-1:db-1:tpch:orders",
|
||||
label: "orders",
|
||||
type: "table",
|
||||
connectionId: "conn-1",
|
||||
database: "db-1",
|
||||
catalog: "hive_catalog",
|
||||
schema: "tpch",
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,38 @@
|
||||
import type { QueryTab, TreeNode } from "@/types/database";
|
||||
|
||||
/** A table reference emitted by the object browser's "Add to AI" action. */
|
||||
export interface ObjectBrowserAiTableTarget {
|
||||
name: string;
|
||||
schema?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts object-browser table selections into the TreeNode[] shape that
|
||||
* App.vue's addToAi() consumes. Schema resolution mirrors the sidebar path,
|
||||
* which passes real tree nodes through as-is: the row's own schema, then the
|
||||
* tab's schema. The database name is deliberately NOT a schema fallback —
|
||||
* rows are already built with the engine-aware connectionObjectTreeNodeSchema()
|
||||
* (ObjectBrowser.vue), which applies the database-as-schema fallback for the
|
||||
* engines where that is correct (Oracle/Dameng/Hive family, sqlite) and keeps
|
||||
* schema undefined for schema-less engines (MySQL/MariaDB etc., whose tabs
|
||||
* also carry schema: undefined). Substituting the database name here would
|
||||
* break addToAi()'s tab matching for those engines, spawning a stray query
|
||||
* tab and clearing the accumulated AI context. The tab's catalog
|
||||
* (Doris/StarRocks external catalogs, etc.) is carried through so addToAi()
|
||||
* can locate the correct query context instead of falling back to a
|
||||
* catalog-less tab.
|
||||
*/
|
||||
export function objectBrowserTablesToAiTreeNodes(tab: QueryTab, tables: ObjectBrowserAiTableTarget[]): TreeNode[] {
|
||||
return tables.map((table) => {
|
||||
const schema = table.schema || tab.schema;
|
||||
return {
|
||||
id: `${tab.connectionId}:${tab.database}:${schema || ""}:${table.name}`,
|
||||
label: table.name,
|
||||
type: "table" as const,
|
||||
connectionId: tab.connectionId,
|
||||
database: tab.database,
|
||||
catalog: tab.catalog,
|
||||
schema,
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -35,6 +35,7 @@ export const contentSurfaceEventNames = [
|
||||
"objectSchemaChange",
|
||||
"objectBrowserViewportChange",
|
||||
"objectBrowserSearchChange",
|
||||
"addObjectTableToAi",
|
||||
"structureEditorSaved",
|
||||
"structureEditorClose",
|
||||
"previewStatement",
|
||||
|
||||
Reference in new issue
Block a user