feat(xugu): expose public synonyms in an isolated scope

This commit is contained in:
Elias authored and GitHub committed 2026-08-12 18:24:01 +08:00
1 parent ab46f3fc72
commit ea9a27ce11
11 files changed
+678 -39

No files matched your search

@@ -1226,7 +1226,7 @@ function findDatabaseNode(nodes: TreeNode[], connId: string, database: string):
function findSchemaNode(nodes: TreeNode[], connId: string, database: string, schema: string): TreeNode | null {
for (const node of nodes) {
if (node.type === "schema" && node.connectionId === connId && sameTreeName(node.database, database) && sameTreeName(node.label, schema)) {
if (node.type === "schema" && node.connectionId === connId && sameTreeName(node.database, database) && sameTreeName(node.schema || node.label, schema)) {
return node;
}
if (node.children) {
@@ -73,6 +73,7 @@ import { sidebarConnectionVisibleFilterMenu } from "@/lib/sidebar/sidebarVisible
import { objectTypesForGroupNode } from "@/lib/table/tableTree";
import { loadSidebarObjectGroup } from "@/lib/sidebar/sidebarObjectGroupRouting";
import { isXuguTypeMemberContainer } from "@/lib/sidebar/xuguTypeMembers";
import { isXuguPublicSynonymTreeNode } from "@/lib/sidebar/xuguPublicSynonyms";
import { mysqlObjectTemplateForGroup } from "@/lib/sidebar/mysqlObjectTemplates";
import { buildTableDeleteTemplate, buildTableInsertTemplate, buildTableSelectTemplate, buildTableUpdateTemplate } from "@/lib/table/tableSqlTemplates";
import { driverStoreFocusForInstallError } from "@/lib/connection/agentDriverInstallHint";
@@ -2578,7 +2579,7 @@ const canCreateSchema = computed(() => {
const canDropSchema = computed(() => {
const config = activeNode.value.connectionId ? connectionStore.getConfig(activeNode.value.connectionId) : undefined;
const dbType = effectiveDatabaseTypeForConnection(config);
return activeNode.value.type === "schema" && !isSqlServerLinkedNode(activeNode.value) && (usesTreeSchemaMode(dbType) || dbType === "dameng") && !connectionUsesDatabaseObjectTreeMode(config);
return activeNode.value.type === "schema" && !isXuguPublicSynonymTreeNode(config?.db_type, activeNode.value.type, activeNode.value.schema) && !isSqlServerLinkedNode(activeNode.value) && (usesTreeSchemaMode(dbType) || dbType === "dameng") && !connectionUsesDatabaseObjectTreeMode(config);
});
const canEditSchemaComment = computed(() => {
@@ -3228,6 +3229,9 @@ async function confirmCreateSchema() {
}
function dropSchema() {
const node = sidebarDangerTarget.value ?? activeNode.value;
const config = node.connectionId ? connectionStore.getConfig(node.connectionId) : undefined;
if (isXuguPublicSynonymTreeNode(config?.db_type, node.type, node.schema)) return;
void refreshDropSchemaPreviewSql();
showDropSchemaConfirm.value = true;
}
@@ -3236,8 +3240,9 @@ async function confirmDropSchema() {
const node = sidebarDangerTarget.value ?? activeNode.value;
if (!node.connectionId || !node.database) return;
try {
await connectionStore.ensureConnected(node.connectionId);
const config = connectionStore.getConfig(node.connectionId);
if (isXuguPublicSynonymTreeNode(config?.db_type, node.type, node.schema)) return;
await connectionStore.ensureConnected(node.connectionId);
const dbType = effectiveDatabaseTypeForConnection(config);
let dropExecutionSchema: string | undefined;
if (dbType === "dameng") {
@@ -4309,6 +4314,17 @@ function buildDatabaseSidebarMenu(context: SidebarMenuFactoryContext): boolean {
const { node, items } = context;
// 4. Database / Schema
if (node.type === "database" || node.type === "schema") {
if (isXuguPublicSynonymTreeNode(currentDatabaseType(), node.type, node.schema)) {
items.push({ label: t("contextMenu.copyName"), action: copyName, icon: Copy, shortcut: shortcutCopyName.value });
items.push({ label: "", separator: true });
items.push({
label: t("contextMenu.refreshChildren"),
action: refresh,
icon: RefreshCw,
shortcut: shortcutRefresh,
});
return true;
}
if (currentDatabaseType() === "hbase") {
items.push({ label: t("contextMenu.copyName"), action: copyName, icon: Copy, shortcut: shortcutCopyName.value });
if (canCreateTable.value) {
@@ -0,0 +1,28 @@
import { readFileSync } from "node:fs";
import { fileURLToPath } from "node:url";
import { describe, expect, it } from "vitest";
const runtimeSource = readFileSync(fileURLToPath(new URL("../SidebarTreeRuntimeHost.vue", import.meta.url)), "utf8");
function functionSource(name: string): string {
const start = runtimeSource.indexOf(`function ${name}(`);
expect(start, `${name} not found`).toBeGreaterThan(-1);
const next = runtimeSource.indexOf("\nfunction ", start + 1);
return runtimeSource.slice(start, next === -1 ? undefined : next);
}
describe("Xugu public synonym actions", () => {
it("returns a read-only schema menu before default and destructive actions", () => {
const menu = functionSource("buildDatabaseSidebarMenu");
const readOnlyGuard = menu.indexOf("isXuguPublicSynonymTreeNode");
expect(readOnlyGuard).toBeGreaterThan(-1);
expect(readOnlyGuard).toBeLessThan(menu.indexOf("contextMenu.setDefaultSchema"));
expect(readOnlyGuard).toBeLessThan(menu.indexOf("contextMenu.dropSchema"));
});
it("guards both the drop request and confirmed execution", () => {
expect(functionSource("dropSchema")).toContain("isXuguPublicSynonymTreeNode");
expect(functionSource("confirmDropSchema")).toContain("isXuguPublicSynonymTreeNode");
});
});