feat(nacos): enhance configuration management
This commit is contained in:
52 files changed
+7139
-263
No files matched your search
Vendored
+94
@@ -0,0 +1,94 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { shallowRef } from "vue";
|
||||
import type { TreeNode } from "@/types/database";
|
||||
import { createNacosNamespaceDesc, createNacosNamespaceId, createNacosNamespaceLoading, createNacosNamespaceName, showCreateNacosNamespaceDialog, sidebarFormTarget } from "@/components/sidebar/sidebarTreeDialogState";
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
toast: vi.fn(),
|
||||
nacosCreateNamespace: vi.fn(),
|
||||
notifyNacosNamespacesChanged: vi.fn(),
|
||||
loadNacosNamespaces: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("vue-i18n", () => ({
|
||||
useI18n: () => ({
|
||||
t: (key: string, params?: Record<string, unknown>) => (params ? `${key}:${JSON.stringify(params)}` : key),
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("@/composables/useToast", () => ({
|
||||
useToast: () => ({ toast: mocks.toast }),
|
||||
}));
|
||||
|
||||
vi.mock("@/lib/backend/api", () => ({
|
||||
nacosCreateNamespace: (...args: unknown[]) => mocks.nacosCreateNamespace(...args),
|
||||
}));
|
||||
|
||||
vi.mock("@/lib/nacos/nacosNamespaceCache", () => ({
|
||||
notifyNacosNamespacesChanged: (...args: unknown[]) => mocks.notifyNacosNamespacesChanged(...args),
|
||||
}));
|
||||
|
||||
import { useSidebarDatabaseSpecificMutationRuntime } from "@/composables/useSidebarDatabaseSpecificMutationRuntime";
|
||||
|
||||
function connectionNode(): TreeNode {
|
||||
return {
|
||||
id: "conn-1",
|
||||
label: "Nacos",
|
||||
type: "connection",
|
||||
connectionId: "conn-1",
|
||||
isExpanded: false,
|
||||
};
|
||||
}
|
||||
|
||||
describe("Nacos namespace creation cache invalidation", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
mocks.nacosCreateNamespace.mockResolvedValue(undefined);
|
||||
mocks.loadNacosNamespaces.mockResolvedValue(undefined);
|
||||
sidebarFormTarget.value = connectionNode();
|
||||
createNacosNamespaceId.value = "new-space";
|
||||
createNacosNamespaceName.value = "New Space";
|
||||
createNacosNamespaceDesc.value = "Created during sync";
|
||||
createNacosNamespaceLoading.value = false;
|
||||
showCreateNacosNamespaceDialog.value = true;
|
||||
});
|
||||
|
||||
it("notifies open views for the same connection immediately after creation succeeds", async () => {
|
||||
const { confirmCreateNacosNamespace } = useSidebarDatabaseSpecificMutationRuntime({
|
||||
activeNode: shallowRef(connectionNode()),
|
||||
connectionStore: {
|
||||
treeNodes: [],
|
||||
loadNacosNamespaces: mocks.loadNacosNamespaces,
|
||||
} as any,
|
||||
});
|
||||
|
||||
await confirmCreateNacosNamespace();
|
||||
|
||||
expect(mocks.nacosCreateNamespace).toHaveBeenCalledWith("conn-1", {
|
||||
namespaceId: "new-space",
|
||||
namespaceName: "New Space",
|
||||
namespaceDesc: "Created during sync",
|
||||
});
|
||||
expect(mocks.notifyNacosNamespacesChanged).toHaveBeenCalledWith("conn-1");
|
||||
expect(mocks.notifyNacosNamespacesChanged.mock.invocationCallOrder[0]).toBeLessThan(mocks.loadNacosNamespaces.mock.invocationCallOrder[0]);
|
||||
expect(showCreateNacosNamespaceDialog.value).toBe(false);
|
||||
});
|
||||
|
||||
it("does not invalidate namespace caches when creation fails", async () => {
|
||||
mocks.nacosCreateNamespace.mockRejectedValueOnce(new Error("create failed"));
|
||||
const { confirmCreateNacosNamespace } = useSidebarDatabaseSpecificMutationRuntime({
|
||||
activeNode: shallowRef(connectionNode()),
|
||||
connectionStore: {
|
||||
treeNodes: [],
|
||||
loadNacosNamespaces: mocks.loadNacosNamespaces,
|
||||
} as any,
|
||||
});
|
||||
|
||||
await confirmCreateNacosNamespace();
|
||||
|
||||
expect(mocks.notifyNacosNamespacesChanged).not.toHaveBeenCalled();
|
||||
expect(mocks.loadNacosNamespaces).not.toHaveBeenCalled();
|
||||
expect(showCreateNacosNamespaceDialog.value).toBe(true);
|
||||
expect(createNacosNamespaceLoading.value).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -1,9 +1,10 @@
|
||||
import { computed, ref } from "vue";
|
||||
import { computed, ref, type Ref } from "vue";
|
||||
import { safeLocalStorageGet, safeLocalStorageSet } from "@/lib/backend/safeStorage";
|
||||
|
||||
export const NACOS_CONFIG_LIST_COLUMN_WIDTHS_STORAGE_KEY = "dbx-nacos-config-list-column-widths";
|
||||
export const DEFAULT_NACOS_CONFIG_LIST_COLUMN_WIDTHS = [280, 180, 180, 96] as const;
|
||||
const MIN_NACOS_CONFIG_LIST_COLUMN_WIDTHS = [180, 120, 120, 72] as const;
|
||||
export const NACOS_CONFIG_LIST_HORIZONTAL_PADDING = 24;
|
||||
const MIN_NACOS_CONFIG_LIST_COLUMN_WIDTHS = [140, 96, 96, 72] as const;
|
||||
|
||||
function minWidthForColumn(index: number) {
|
||||
return MIN_NACOS_CONFIG_LIST_COLUMN_WIDTHS[index] ?? MIN_NACOS_CONFIG_LIST_COLUMN_WIDTHS[MIN_NACOS_CONFIG_LIST_COLUMN_WIDTHS.length - 1];
|
||||
@@ -37,29 +38,63 @@ function gridTemplateColumnsForWidths(widths: readonly number[]) {
|
||||
return widths.map((width) => `${width}px`).join(" ");
|
||||
}
|
||||
|
||||
export function useNacosConfigListColumnResize() {
|
||||
const columnWidths = ref(loadNacosConfigListColumnWidths());
|
||||
export function fitNacosConfigListColumnWidths(widths: readonly number[], availableWidth: number): number[] {
|
||||
const normalized = normalizeNacosConfigListColumnWidths([...widths]) ?? [...DEFAULT_NACOS_CONFIG_LIST_COLUMN_WIDTHS];
|
||||
const targetWidth = Math.floor(availableWidth) - NACOS_CONFIG_LIST_HORIZONTAL_PADDING;
|
||||
if (targetWidth <= 0) return normalized;
|
||||
|
||||
const minimums = normalized.map((_, index) => minWidthForColumn(index));
|
||||
const minimumTotal = minimums.reduce((sum, width) => sum + width, 0);
|
||||
if (targetWidth <= minimumTotal) return minimums;
|
||||
|
||||
const preferredExtras = normalized.map((width, index) => Math.max(0, width - minimums[index]!));
|
||||
const preferredExtraTotal = preferredExtras.reduce((sum, width) => sum + width, 0);
|
||||
const availableExtra = targetWidth - minimumTotal;
|
||||
const fitted = minimums.map((minimum, index) => {
|
||||
const weight = preferredExtraTotal > 0 ? preferredExtras[index]! / preferredExtraTotal : 1 / minimums.length;
|
||||
return minimum + Math.floor(availableExtra * weight);
|
||||
});
|
||||
fitted[fitted.length - 1]! += targetWidth - fitted.reduce((sum, width) => sum + width, 0);
|
||||
return fitted;
|
||||
}
|
||||
|
||||
export function useNacosConfigListColumnResize(availableWidth?: Readonly<Ref<number>>) {
|
||||
const preferredColumnWidths = ref(loadNacosConfigListColumnWidths());
|
||||
const resizingColumnIndex = ref<number | null>(null);
|
||||
const columnWidths = computed(() => {
|
||||
const width = availableWidth?.value ?? 0;
|
||||
return width > 0 ? fitNacosConfigListColumnWidths(preferredColumnWidths.value, width) : preferredColumnWidths.value;
|
||||
});
|
||||
const gridTemplateColumns = computed(() => gridTemplateColumnsForWidths(columnWidths.value));
|
||||
const totalWidth = computed(() => columnWidths.value.reduce((sum, width) => sum + width, 0));
|
||||
const minWidth = computed(() => `${totalWidth.value}px`);
|
||||
const minWidth = computed(() => `${totalWidth.value + NACOS_CONFIG_LIST_HORIZONTAL_PADDING}px`);
|
||||
|
||||
function onResizeStart(columnIndex: number, event: MouseEvent) {
|
||||
if (columnIndex < 0 || columnIndex >= columnWidths.value.length) return;
|
||||
if (columnIndex < 0 || columnIndex >= columnWidths.value.length - 1) return;
|
||||
event.preventDefault();
|
||||
const startX = event.clientX;
|
||||
const startWidth = columnWidths.value[columnIndex] ?? minWidthForColumn(columnIndex);
|
||||
const startWidths = [...columnWidths.value];
|
||||
const startWidth = startWidths[columnIndex] ?? minWidthForColumn(columnIndex);
|
||||
const nextStartWidth = startWidths[columnIndex + 1] ?? minWidthForColumn(columnIndex + 1);
|
||||
const pairWidth = startWidth + nextStartWidth;
|
||||
resizingColumnIndex.value = columnIndex;
|
||||
|
||||
const onMove = (moveEvent: MouseEvent) => {
|
||||
const delta = moveEvent.clientX - startX;
|
||||
columnWidths.value[columnIndex] = Math.max(minWidthForColumn(columnIndex), startWidth + delta);
|
||||
const minimum = minWidthForColumn(columnIndex);
|
||||
const nextMinimum = minWidthForColumn(columnIndex + 1);
|
||||
const width = Math.min(pairWidth - nextMinimum, Math.max(minimum, startWidth + delta));
|
||||
const nextWidths = [...startWidths];
|
||||
nextWidths[columnIndex] = width;
|
||||
nextWidths[columnIndex + 1] = pairWidth - width;
|
||||
preferredColumnWidths.value = nextWidths;
|
||||
};
|
||||
|
||||
const onUp = (moveEvent: MouseEvent) => {
|
||||
onMove(moveEvent);
|
||||
resizingColumnIndex.value = null;
|
||||
saveNacosConfigListColumnWidths(columnWidths.value);
|
||||
preferredColumnWidths.value = [...columnWidths.value];
|
||||
saveNacosConfigListColumnWidths(preferredColumnWidths.value);
|
||||
document.removeEventListener("mousemove", onMove);
|
||||
document.removeEventListener("mouseup", onUp);
|
||||
};
|
||||
|
||||
@@ -5,6 +5,7 @@ import { useConnectionStore } from "@/stores/connectionStore";
|
||||
import type { TreeNode } from "@/types/database";
|
||||
import * as api from "@/lib/backend/api";
|
||||
import { translateBackendError } from "@/i18n/backend-errors";
|
||||
import { notifyNacosNamespacesChanged } from "@/lib/nacos/nacosNamespaceCache";
|
||||
import { findSidebarActionTarget } from "@/lib/sidebar/sidebarActionTarget";
|
||||
import { isRenamableMongoCollection, mongoCollectionKindFromNode, mongoDropAllIndexesPreview, mongoDropCollectionPreview, mongoDropDatabasePreview, mongoDropIndexPreview, mongoRenameCollectionPreview } from "@/lib/sidebar/mongoCollectionMutation";
|
||||
import { runMongoSidebarMutation } from "@/lib/sidebar/runMongoSidebarMutation";
|
||||
@@ -170,6 +171,7 @@ export function useSidebarDatabaseSpecificMutationRuntime(options: SidebarDataba
|
||||
namespaceName,
|
||||
namespaceDesc: createNacosNamespaceDesc.value.trim() || namespaceName,
|
||||
});
|
||||
notifyNacosNamespacesChanged(node.connectionId);
|
||||
showCreateNacosNamespaceDialog.value = false;
|
||||
await connectionStore.loadNacosNamespaces(node.connectionId, { force: true });
|
||||
const liveNode = findSidebarActionTarget(connectionStore.treeNodes, node);
|
||||
|
||||
Reference in new issue
Block a user