fix(mongodb): move sidebar index actions to indexes group
This commit is contained in:
1 parent
e82c1aedf0
commit
a2e1128670
5 files changed
+93
-47
No files matched your search
@@ -3577,7 +3577,7 @@ routeDangerDialog(showDropMongoIndexConfirm, () =>
|
||||
routeDangerDialog(showDropAllMongoIndexesConfirm, () =>
|
||||
dangerRequest({
|
||||
title: t("contextMenu.dropAllIndexes"),
|
||||
message: t("contextMenu.confirmDropMongoAllIndexesMessage", { name: activeNode.value.label }),
|
||||
message: t("contextMenu.confirmDropMongoAllIndexesMessage", { name: activeNode.value.tableName || activeNode.value.label }),
|
||||
detailsText: t("contextMenu.confirmDropMongoAllIndexesDetails"),
|
||||
sql: mongoDropAllIndexesPreview(activeNode.value),
|
||||
confirmLabel: t("contextMenu.dropAllIndexes"),
|
||||
@@ -4248,17 +4248,13 @@ function buildSpecialSidebarMenu(context: SidebarMenuFactoryContext): boolean {
|
||||
}
|
||||
if (canDropMongoDatabase.value) {
|
||||
items.push({ label: "", separator: true });
|
||||
items.push(
|
||||
moreActionsSubmenu([
|
||||
{
|
||||
label: t("contextMenu.dropDatabase"),
|
||||
action: dropDatabase,
|
||||
icon: Trash2,
|
||||
shortcut: shortcutDelete,
|
||||
variant: "destructive" as const,
|
||||
},
|
||||
]),
|
||||
);
|
||||
items.push({
|
||||
label: t("contextMenu.dropDatabase"),
|
||||
action: dropDatabase,
|
||||
icon: Trash2,
|
||||
shortcut: shortcutDelete,
|
||||
variant: "destructive" as const,
|
||||
});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -4292,17 +4288,9 @@ function buildSpecialSidebarMenu(context: SidebarMenuFactoryContext): boolean {
|
||||
shortcut: shortcutRename,
|
||||
});
|
||||
}
|
||||
if (canCreateMongoIndex.value || canDropAllMongoIndexes.value || canDropMongoCollection.value) {
|
||||
if (canDropMongoCollection.value) {
|
||||
items.push({ label: "", separator: true });
|
||||
if (canCreateMongoIndex.value) {
|
||||
items.push({ label: t("contextMenu.createMongoIndex"), action: openCreateMongoIndexDialog, icon: Plus });
|
||||
}
|
||||
if (canDropAllMongoIndexes.value) {
|
||||
items.push({ label: t("contextMenu.dropAllIndexes"), action: dropAllMongoIndexes, icon: Trash2, variant: "destructive" as const });
|
||||
}
|
||||
if (canDropMongoCollection.value) {
|
||||
items.push({ label: t("contextMenu.dropCollection"), action: dropMongoCollection, icon: Trash2, shortcut: shortcutDelete, variant: "destructive" as const });
|
||||
}
|
||||
items.push({ label: t("contextMenu.dropCollection"), action: dropMongoCollection, icon: Trash2, shortcut: shortcutDelete, variant: "destructive" as const });
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -4569,7 +4557,8 @@ function buildObjectGroupSidebarMenu(context: SidebarMenuFactoryContext): boolea
|
||||
if (isGroupLabel(node)) {
|
||||
const mysqlObjectTemplate = node.connectionId ? mysqlObjectTemplateForGroup(connectionStore.getConfig(node.connectionId), node) : null;
|
||||
const hasMongoCreateIndexAction = node.type === "group-indexes" && canCreateMongoIndex.value;
|
||||
const hasGroupCreateAction = (node.type === "group-tables" && canCreateTable.value) || (node.type === "group-views" && !!node.connectionId && !!node.database) || !!mysqlObjectTemplate || hasMongoCreateIndexAction;
|
||||
const hasMongoDropAllIndexesAction = node.type === "group-indexes" && canDropAllMongoIndexes.value;
|
||||
const hasGroupAction = (node.type === "group-tables" && canCreateTable.value) || (node.type === "group-views" && !!node.connectionId && !!node.database) || !!mysqlObjectTemplate || hasMongoCreateIndexAction || hasMongoDropAllIndexesAction;
|
||||
const canLoadAllObjectGroup = node.type === "group-tables" || node.type === "group-views" || node.type === "group-materialized-views";
|
||||
if (node.type === "group-tables" && canCreateTable.value) {
|
||||
items.push({ label: t("contextMenu.createTable"), action: createTable, icon: Plus });
|
||||
@@ -4589,7 +4578,11 @@ function buildObjectGroupSidebarMenu(context: SidebarMenuFactoryContext): boolea
|
||||
if (hasMongoCreateIndexAction) {
|
||||
items.push({ label: t("contextMenu.createMongoIndex"), action: openCreateMongoIndexDialog, icon: Plus });
|
||||
}
|
||||
if (hasGroupCreateAction) {
|
||||
if (hasMongoDropAllIndexesAction) {
|
||||
if (hasMongoCreateIndexAction) items.push({ label: "", separator: true });
|
||||
items.push({ label: t("contextMenu.dropAllIndexes"), action: dropAllMongoIndexes, icon: Trash2, variant: "destructive" as const });
|
||||
}
|
||||
if (hasGroupAction) {
|
||||
items.push({ label: "", separator: true });
|
||||
}
|
||||
if (node.type === "group-extensions") {
|
||||
|
||||
Vendored
+50
-8
@@ -153,7 +153,7 @@ describe("MongoDB sidebar mutation runtime", () => {
|
||||
resetMongoCreateIndexForm();
|
||||
});
|
||||
|
||||
it("allows Legacy connections to create and delete MongoDB tree objects while keeping rename native-only", () => {
|
||||
it("keeps Legacy MongoDB mutations available while limiting index actions to the Indexes group", () => {
|
||||
mocks.getConfig.mockReturnValue(mongoConfig("mongodb-legacy"));
|
||||
const activeNode = shallowRef(mongoDatabaseNode());
|
||||
const feature = useSidebarDatabaseSpecificMutationRuntime({
|
||||
@@ -172,10 +172,14 @@ describe("MongoDB sidebar mutation runtime", () => {
|
||||
expect(feature.canDropMongoDatabase.value).toBe(true);
|
||||
activeNode.value = mongoCollectionNode();
|
||||
expect(feature.canDropMongoCollection.value).toBe(true);
|
||||
expect(feature.canDropAllMongoIndexes.value).toBe(true);
|
||||
expect(feature.canDropAllMongoIndexes.value).toBe(false);
|
||||
expect(feature.canRenameMongoCollection.value).toBe(false);
|
||||
expect(feature.canCreateMongoIndex.value).toBe(true);
|
||||
expect(feature.canCreateMongoIndex.value).toBe(false);
|
||||
activeNode.value = mongoIndexesGroupNode();
|
||||
expect(feature.canDropAllMongoIndexes.value).toBe(true);
|
||||
expect(feature.canCreateMongoIndex.value).toBe(true);
|
||||
activeNode.value = mongoIndexesGroupNode("timeseries");
|
||||
expect(feature.canDropAllMongoIndexes.value).toBe(true);
|
||||
expect(feature.canCreateMongoIndex.value).toBe(true);
|
||||
activeNode.value = mongoIndexNode("email_1");
|
||||
expect(feature.canDropMongoIndex.value).toBe(true);
|
||||
@@ -201,11 +205,20 @@ describe("MongoDB sidebar mutation runtime", () => {
|
||||
expect(feature.canDropMongoCollection.value).toBe(true);
|
||||
expect(feature.canDropAllMongoIndexes.value).toBe(false);
|
||||
activeNode.value = mongoIndexesGroupNode("view");
|
||||
expect(feature.canDropAllMongoIndexes.value).toBe(false);
|
||||
expect(feature.canCreateMongoIndex.value).toBe(false);
|
||||
activeNode.value = mongoIndexNode("email_1", "view");
|
||||
expect(feature.canDropMongoIndex.value).toBe(false);
|
||||
});
|
||||
|
||||
it("does not expose Indexes group mutations for read-only MongoDB connections", () => {
|
||||
mocks.getConfig.mockReturnValue({ ...mongoConfig(), read_only: true });
|
||||
const feature = runtime(mongoIndexesGroupNode());
|
||||
|
||||
expect(feature.canCreateMongoIndex.value).toBe(false);
|
||||
expect(feature.canDropAllMongoIndexes.value).toBe(false);
|
||||
});
|
||||
|
||||
it("creates an index from the shared sidebar dialog state", async () => {
|
||||
mocks.getConfig.mockReturnValue(mongoConfig("mongodb-legacy"));
|
||||
const node = mongoIndexesGroupNode();
|
||||
@@ -253,16 +266,26 @@ describe("MongoDB sidebar mutation runtime", () => {
|
||||
expect(showCreateMongoIndexDialog.value).toBe(true);
|
||||
});
|
||||
|
||||
it("allows a collection menu to create an index before its Indexes group is expanded", async () => {
|
||||
it("does not expose index creation through a collection node", async () => {
|
||||
const node = mongoCollectionNode();
|
||||
const feature = runtime(node);
|
||||
sidebarFormTarget.value = node;
|
||||
|
||||
feature.prepareCreateMongoIndexDialog();
|
||||
mongoCreateIndexForm.value.fields[0]!.path = "email";
|
||||
expect(showCreateMongoIndexDialog.value).toBe(false);
|
||||
await feature.confirmCreateMongoIndex();
|
||||
|
||||
expect(mocks.mongoCreateIndex).toHaveBeenCalledWith("conn-1", "app", "users", '{"email":1}', undefined);
|
||||
expect(mocks.mongoCreateIndex).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("does not clear indexes through a collection node", async () => {
|
||||
const node = mongoCollectionNode();
|
||||
const feature = runtime(node);
|
||||
sidebarDangerTarget.value = node;
|
||||
|
||||
await feature.confirmDropAllMongoIndexes();
|
||||
|
||||
expect(mocks.mongoDropIndexes).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("keeps the sidebar form target when the active node changes", async () => {
|
||||
@@ -296,12 +319,13 @@ describe("MongoDB sidebar mutation runtime", () => {
|
||||
});
|
||||
|
||||
it("drops every removable index through the shared mutation and refreshes metadata", async () => {
|
||||
const node = mongoCollectionNode();
|
||||
const node = mongoIndexesGroupNode();
|
||||
const feature = runtime(node);
|
||||
sidebarDangerTarget.value = node;
|
||||
showDropAllMongoIndexesConfirm.value = true;
|
||||
mocks.mongoDropIndexes.mockResolvedValueOnce({ dropped_names: ["email_1", "created_at_-1"], affected_rows: 2 });
|
||||
|
||||
expect(feature.mongoDropAllIndexesPreview(node)).toBe('db.getSiblingDB("app").getCollection("users").dropIndexes()');
|
||||
await feature.confirmDropAllMongoIndexes();
|
||||
|
||||
expect(mocks.mongoDropIndexes).toHaveBeenCalledWith("conn-1", "app", "users", undefined, false);
|
||||
@@ -323,7 +347,7 @@ describe("MongoDB sidebar mutation runtime", () => {
|
||||
});
|
||||
|
||||
it("reports partial index deletion after forcing a metadata refresh", async () => {
|
||||
const node = mongoCollectionNode();
|
||||
const node = mongoIndexesGroupNode();
|
||||
const feature = runtime(node);
|
||||
sidebarDangerTarget.value = node;
|
||||
mocks.mongoDropIndexes.mockResolvedValueOnce({
|
||||
@@ -432,4 +456,22 @@ describe("MongoDB sidebar mutation runtime", () => {
|
||||
expect(mocks.mongoCreateIndex).not.toHaveBeenCalled();
|
||||
expect(showCreateMongoIndexDialog.value).toBe(true);
|
||||
});
|
||||
|
||||
it("does not clear indexes when production confirmation is cancelled", async () => {
|
||||
mocks.getConfig.mockReturnValue(mongoConfig(undefined, true));
|
||||
const node = mongoIndexesGroupNode();
|
||||
const feature = runtime(node);
|
||||
sidebarDangerTarget.value = node;
|
||||
showDropAllMongoIndexesConfirm.value = true;
|
||||
const pending = feature.confirmDropAllMongoIndexes();
|
||||
await Promise.resolve();
|
||||
|
||||
const { useProductionSafetyStore } = await import("@/stores/productionSafetyStore");
|
||||
useProductionSafetyStore().cancel();
|
||||
await pending;
|
||||
|
||||
expect(mocks.ensureConnected).not.toHaveBeenCalled();
|
||||
expect(mocks.mongoDropIndexes).not.toHaveBeenCalled();
|
||||
expect(showDropAllMongoIndexesConfirm.value).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -175,19 +175,26 @@ export function useSidebarDatabaseSpecificMutationRuntime(options: SidebarDataba
|
||||
|
||||
const canDropMongoIndex = computed(() => canDropMongoIndexNode(activeNode.value));
|
||||
|
||||
const canDropAllMongoIndexes = computed(() => activeNode.value.type === "mongo-collection" && canMutateMongoIndexes(activeNode.value));
|
||||
function mongoIndexCollectionName(node: TreeNode): string {
|
||||
return node.type === "group-indexes" ? node.tableName || "" : "";
|
||||
}
|
||||
|
||||
function canManageMongoIndexesNode(node: TreeNode): boolean {
|
||||
return !!mongoIndexCollectionName(node) && !!node.database && canMutateMongoIndexes(node);
|
||||
}
|
||||
|
||||
const canDropAllMongoIndexes = computed(() => canManageMongoIndexesNode(activeNode.value));
|
||||
|
||||
function mongoIndexDropPreview(node: Pick<TreeNode, "database" | "tableName">, indexName: string): string {
|
||||
return mongoDropIndexPreview(node.database || "", node.tableName || "", indexName);
|
||||
}
|
||||
|
||||
function mongoDropAllIndexesPreviewForNode(node: Pick<TreeNode, "database" | "label">): string {
|
||||
return mongoDropAllIndexesPreview(node.database || "", node.label);
|
||||
function mongoDropAllIndexesPreviewForNode(node: TreeNode): string {
|
||||
return mongoDropAllIndexesPreview(node.database || "", mongoIndexCollectionName(node));
|
||||
}
|
||||
|
||||
function canCreateMongoIndexNode(node: TreeNode): boolean {
|
||||
const collectionName = mongoIndexCollectionName(node);
|
||||
return !!collectionName && !!node.database && canMutateMongoIndexes(node);
|
||||
return canManageMongoIndexesNode(node);
|
||||
}
|
||||
|
||||
const canCreateMongoIndex = computed(() => canCreateMongoIndexNode(activeNode.value));
|
||||
@@ -202,11 +209,6 @@ export function useSidebarDatabaseSpecificMutationRuntime(options: SidebarDataba
|
||||
{ deep: true },
|
||||
);
|
||||
|
||||
function mongoIndexCollectionName(node: TreeNode): string {
|
||||
if (node.type === "mongo-collection") return node.label;
|
||||
return node.type === "group-indexes" ? node.tableName || "" : "";
|
||||
}
|
||||
|
||||
function prepareCreateMongoIndexDialog() {
|
||||
const node = activeNode.value;
|
||||
if (!canCreateMongoIndexNode(node) || !node.connectionId || !node.database) return;
|
||||
@@ -505,8 +507,8 @@ export function useSidebarDatabaseSpecificMutationRuntime(options: SidebarDataba
|
||||
const node = sidebarDangerTarget.value ?? activeNode.value;
|
||||
const connectionId = node.connectionId;
|
||||
const database = node.database;
|
||||
if (node.type !== "mongo-collection" || !canMutateMongoIndexes(node) || !connectionId || !database) return;
|
||||
const collectionName = node.label;
|
||||
if (!canManageMongoIndexesNode(node) || !connectionId || !database) return;
|
||||
const collectionName = mongoIndexCollectionName(node);
|
||||
await runMongoSidebarMutation({
|
||||
connection: connectionStore.getConfig(connectionId),
|
||||
database,
|
||||
|
||||
Reference in new issue
Block a user