From 4a6422365b34a764a3d26e1ccccd710a7db0996e Mon Sep 17 00:00:00 2001 From: lewis <32348572+wang-zhengxin@users.noreply.github.com> Date: Wed, 29 Jul 2026 20:33:34 +0800 Subject: [PATCH] feat(redis): support database aliases --- .../connection/ConnectionDialog.vue | 1 + .../sidebar/SidebarTreeItemDialogs.vue | 26 ++++ .../sidebar/SidebarTreeRuntimeHost.vue | 22 ++- .../sidebar/sidebarTreeDialogState.ts | 6 + ...eSidebarDatabaseSpecificMutationRuntime.ts | 41 +++++ apps/desktop/src/i18n/locales/en.ts | 7 + apps/desktop/src/i18n/locales/es.ts | 7 + apps/desktop/src/i18n/locales/it.ts | 7 + apps/desktop/src/i18n/locales/ja.ts | 7 + apps/desktop/src/i18n/locales/pt-BR.ts | 7 + apps/desktop/src/i18n/locales/zh-CN.ts | 7 + apps/desktop/src/i18n/locales/zh-TW.ts | 7 + .../redis/redisDatabaseAlias.spec.ts | 35 +++++ .../src/lib/redis/redisDatabaseAlias.ts | 30 ++++ .../connectionStore.redisAlias.spec.ts | 145 ++++++++++++++++++ apps/desktop/src/stores/connectionStore.ts | 46 +++++- apps/desktop/src/types/database.ts | 1 + crates/dbx-core/src/agent_connection.rs | 1 + crates/dbx-core/src/cloud_sync.rs | 3 + crates/dbx-core/src/connection.rs | 1 + crates/dbx-core/src/connection_secrets.rs | 1 + crates/dbx-core/src/db/redis_driver.rs | 1 + crates/dbx-core/src/models/connection.rs | 24 +++ crates/dbx-core/src/mq/config.rs | 1 + crates/dbx-core/src/mq/service.rs | 1 + crates/dbx-core/src/nacos/config.rs | 1 + crates/dbx-core/src/nacos/service.rs | 2 + crates/dbx-core/src/production_safety.rs | 1 + crates/dbx-core/src/query.rs | 2 + crates/dbx-core/src/schema.rs | 1 + crates/dbx-core/src/storage.rs | 2 + crates/dbx-core/src/transfer.rs | 1 + .../tests/database_export_prefetch.rs | 1 + .../live_postgres_query_result_export.rs | 1 + .../dbx-core/tests/live_postgres_transfer.rs | 1 + .../tests/live_sqlserver_completion.rs | 1 + .../live_sqlserver_query_result_export.rs | 1 + crates/dbx-web/src/routes/connection.rs | 1 + src-tauri/src/commands/connection.rs | 1 + 39 files changed, 443 insertions(+), 9 deletions(-) create mode 100644 apps/desktop/src/lib/__tests__/redis/redisDatabaseAlias.spec.ts create mode 100644 apps/desktop/src/lib/redis/redisDatabaseAlias.ts create mode 100644 apps/desktop/src/stores/__tests__/connectionStore.redisAlias.spec.ts diff --git a/apps/desktop/src/components/connection/ConnectionDialog.vue b/apps/desktop/src/components/connection/ConnectionDialog.vue index 1e1f9af97..d856ca6fb 100644 --- a/apps/desktop/src/components/connection/ConnectionDialog.vue +++ b/apps/desktop/src/components/connection/ConnectionDialog.vue @@ -3254,6 +3254,7 @@ function connectionConfigForSubmit(id: string, generatedName = ""): ConnectionCo config.redis_cluster_nodes = undefined; config.redis_key_separator = undefined; config.redis_scan_page_size = undefined; + config.redis_database_aliases = undefined; } else if (config.redis_connection_mode === "sentinel") { config.redis_sentinel_master = config.redis_sentinel_master?.trim() || ""; config.redis_sentinel_nodes = normalizeRedisSentinelNodes(config.redis_sentinel_nodes || ""); diff --git a/apps/desktop/src/components/sidebar/SidebarTreeItemDialogs.vue b/apps/desktop/src/components/sidebar/SidebarTreeItemDialogs.vue index 27c0a110d..00938c471 100644 --- a/apps/desktop/src/components/sidebar/SidebarTreeItemDialogs.vue +++ b/apps/desktop/src/components/sidebar/SidebarTreeItemDialogs.vue @@ -94,6 +94,11 @@ const { renameMongoCollectionPreview, renameMongoCollectionLoading, confirmRenameMongoCollection, + showRedisDatabaseAliasDialog, + redisDatabaseAliasInput, + redisDatabaseAliasSaving, + confirmRedisDatabaseAlias, + clearRedisDatabaseAlias, showCreateSchemaDialog, createSchemaName, confirmCreateSchema, @@ -150,6 +155,7 @@ watch( showCreateNacosNamespaceDialog, showEditNacosNamespaceDialog, showRenameMongoCollectionDialog, + showRedisDatabaseAliasDialog, showCreateSchemaDialog, showEditSchemaCommentDialog, ], @@ -249,6 +255,26 @@ watch( + + + + {{ t("redis.databaseAliasTitle", { db: node.database }) }} + + + + {{ t("redis.databaseAliasHint") }} + + + {{ t("redis.clearDatabaseAlias") }} + {{ t("dangerDialog.cancel") }} + + + {{ t("common.save") }} + + + + + diff --git a/apps/desktop/src/components/sidebar/SidebarTreeRuntimeHost.vue b/apps/desktop/src/components/sidebar/SidebarTreeRuntimeHost.vue index a5c1329a7..444cc4345 100644 --- a/apps/desktop/src/components/sidebar/SidebarTreeRuntimeHost.vue +++ b/apps/desktop/src/components/sidebar/SidebarTreeRuntimeHost.vue @@ -397,6 +397,12 @@ const { dropMongoIndex, dropAllMongoIndexes, flushRedisDb, + prepareRedisDatabaseAliasDialog, + confirmRedisDatabaseAlias, + clearRedisDatabaseAlias, + showRedisDatabaseAliasDialog, + redisDatabaseAliasInput, + redisDatabaseAliasSaving, confirmFlushRedisDb, confirmDropMongoDatabase, confirmDropMongoCollection, @@ -575,7 +581,9 @@ async function toggle() { } } else if (node.type === "redis-db" && node.connectionId && node.database) { await connectionStore.ensureConnected(node.connectionId); - const tabTitle = `${connectionStore.getConfig(node.connectionId)?.name || "Redis"}:db${node.database}`; + const alias = connectionStore.getRedisDatabaseAlias(node.connectionId, node.database); + const databaseLabel = alias ? `db${node.database} · ${alias}` : `db${node.database}`; + const tabTitle = `${connectionStore.getConfig(node.connectionId)?.name || "Redis"}:${databaseLabel}`; queryStore.createTab(node.connectionId, node.database, tabTitle, "redis"); } else if (node.type === "mq-tenant" && node.connectionId) { await connectionStore.ensureConnected(node.connectionId); @@ -916,6 +924,12 @@ function openRenameMongoCollectionDialog() { prepareRenameMongoCollectionDialog(); } +function openRedisDatabaseAliasDialog() { + claimTreeItemDialogOwnership(); + routeTreeItemDialogController(); + prepareRedisDatabaseAliasDialog(); +} + function requestEditSelectedConnection(): boolean { const editTarget = selectedConnectionEditTarget(activeNode.value, selectedTreeNodesInVisibleOrder()); if (!editTarget) return false; @@ -3521,6 +3535,11 @@ function databaseSpecificDialogCapabilities() { renameMongoCollectionPreview, renameMongoCollectionLoading, confirmRenameMongoCollection, + showRedisDatabaseAliasDialog, + redisDatabaseAliasInput, + redisDatabaseAliasSaving, + confirmRedisDatabaseAlias, + clearRedisDatabaseAlias, showCreateSchemaDialog, createSchemaName, confirmCreateSchema, @@ -4017,6 +4036,7 @@ function buildSpecialSidebarMenu(context: SidebarMenuFactoryContext): boolean { } if (node.type === "redis-db") { items.push({ label: "", separator: true }); + items.push({ label: t("redis.setDatabaseAlias"), action: openRedisDatabaseAliasDialog, icon: Pencil }); items.push({ label: t("redis.flushDb"), action: flushRedisDb, icon: Eraser, variant: "destructive" as const }); } if (canDropMongoDatabase.value) { diff --git a/apps/desktop/src/components/sidebar/sidebarTreeDialogState.ts b/apps/desktop/src/components/sidebar/sidebarTreeDialogState.ts index 6f28c3a1f..80c2d01cc 100644 --- a/apps/desktop/src/components/sidebar/sidebarTreeDialogState.ts +++ b/apps/desktop/src/components/sidebar/sidebarTreeDialogState.ts @@ -97,6 +97,9 @@ export const dropMongoIndexLoading = ref(false); export const showDropAllMongoIndexesConfirm = ref(false); export const dropAllMongoIndexesLoading = ref(false); export const showFlushRedisDbConfirm = ref(false); +export const showRedisDatabaseAliasDialog = ref(false); +export const redisDatabaseAliasInput = ref(""); +export const redisDatabaseAliasSaving = ref(false); export const showCreateSchemaDialog = ref(false); export const createSchemaName = ref(""); export const showDropSchemaConfirm = ref(false); @@ -139,6 +142,7 @@ const openFlags = [ showDropMongoIndexConfirm, showDropAllMongoIndexesConfirm, showFlushRedisDbConfirm, + showRedisDatabaseAliasDialog, showCreateSchemaDialog, showDropSchemaConfirm, showEditDatabasePropertiesDialog, @@ -156,6 +160,8 @@ export function resetSidebarTreeDialogState() { createDatabasePreviewSql.value = ""; createDatabaseAuthorizationResults.value = []; createDatabaseAuthorizationApplying.value = false; + redisDatabaseAliasInput.value = ""; + redisDatabaseAliasSaving.value = false; sidebarTreeDialogOwner.value = null; sidebarDangerTarget.value = null; sidebarFormTarget.value = null; diff --git a/apps/desktop/src/composables/useSidebarDatabaseSpecificMutationRuntime.ts b/apps/desktop/src/composables/useSidebarDatabaseSpecificMutationRuntime.ts index 3652e25cf..9a0f1a9df 100644 --- a/apps/desktop/src/composables/useSidebarDatabaseSpecificMutationRuntime.ts +++ b/apps/desktop/src/composables/useSidebarDatabaseSpecificMutationRuntime.ts @@ -30,6 +30,9 @@ import { showDropDatabaseConfirm, dropDatabaseLoading, showFlushRedisDbConfirm, + showRedisDatabaseAliasDialog, + redisDatabaseAliasInput, + redisDatabaseAliasSaving, showRenameMongoCollectionDialog, renameMongoCollectionName, renameMongoCollectionError, @@ -231,6 +234,38 @@ export function useSidebarDatabaseSpecificMutationRuntime(options: SidebarDataba showFlushRedisDbConfirm.value = true; } + function prepareRedisDatabaseAliasDialog() { + const node = activeNode.value; + redisDatabaseAliasInput.value = node.connectionId && node.database != null ? connectionStore.getRedisDatabaseAlias(node.connectionId, node.database) || "" : ""; + redisDatabaseAliasSaving.value = false; + showRedisDatabaseAliasDialog.value = true; + } + + async function saveRedisDatabaseAlias(alias?: string) { + const node = sidebarFormTarget.value ?? activeNode.value; + if (node.type !== "redis-db" || !node.connectionId || node.database == null || redisDatabaseAliasSaving.value) return; + redisDatabaseAliasSaving.value = true; + try { + await connectionStore.setRedisDatabaseAlias(node.connectionId, node.database, alias); + showRedisDatabaseAliasDialog.value = false; + const normalizedAlias = alias?.trim(); + toast(normalizedAlias ? t("redis.databaseAliasSaved", { db: node.database, alias: normalizedAlias }) : t("redis.databaseAliasCleared", { db: node.database }), 3000); + } catch (error: any) { + toast(t("connection.saveFailed", { message: error?.message || String(error) }), 5000); + } finally { + redisDatabaseAliasSaving.value = false; + } + } + + async function confirmRedisDatabaseAlias() { + await saveRedisDatabaseAlias(redisDatabaseAliasInput.value); + } + + async function clearRedisDatabaseAlias() { + redisDatabaseAliasInput.value = ""; + await saveRedisDatabaseAlias(); + } + async function confirmFlushRedisDb() { const node = sidebarDangerTarget.value ?? activeNode.value; if (node.type !== "redis-db" || !node.connectionId || !node.database) return; @@ -387,6 +422,12 @@ export function useSidebarDatabaseSpecificMutationRuntime(options: SidebarDataba dropMongoIndex, dropAllMongoIndexes, flushRedisDb, + prepareRedisDatabaseAliasDialog, + confirmRedisDatabaseAlias, + clearRedisDatabaseAlias, + showRedisDatabaseAliasDialog, + redisDatabaseAliasInput, + redisDatabaseAliasSaving, confirmFlushRedisDb, confirmDropMongoDatabase, confirmDropMongoCollection, diff --git a/apps/desktop/src/i18n/locales/en.ts b/apps/desktop/src/i18n/locales/en.ts index 2a3960e87..5fcdbf258 100644 --- a/apps/desktop/src/i18n/locales/en.ts +++ b/apps/desktop/src/i18n/locales/en.ts @@ -2971,6 +2971,13 @@ export default { summarySize: "size", }, redis: { + setDatabaseAlias: "Set Database Alias", + databaseAliasTitle: "Alias for db{db}", + databaseAliasPlaceholder: "e.g. orders, cache, development", + databaseAliasHint: "The alias is for display and search only. Redis commands continue to use the numeric database index.", + clearDatabaseAlias: "Clear Alias", + databaseAliasSaved: "Alias for db{db} set to {alias}", + databaseAliasCleared: "Alias for db{db} cleared", selectKey: "Select a key to view its value", noKeys: "No keys found", noKeysInScanHint: "No keys found in current scan range", diff --git a/apps/desktop/src/i18n/locales/es.ts b/apps/desktop/src/i18n/locales/es.ts index a153ae30b..c1396364d 100644 --- a/apps/desktop/src/i18n/locales/es.ts +++ b/apps/desktop/src/i18n/locales/es.ts @@ -2823,6 +2823,13 @@ export default withEnglishFallback({ summarySize: "tamaño", }, redis: { + setDatabaseAlias: "Establecer alias de base de datos", + databaseAliasTitle: "Alias para db{db}", + databaseAliasPlaceholder: "p. ej. pedidos, caché, desarrollo", + databaseAliasHint: "El alias solo se usa para mostrar y buscar. Los comandos de Redis siguen usando el índice numérico.", + clearDatabaseAlias: "Borrar alias", + databaseAliasSaved: "Alias de db{db} establecido en {alias}", + databaseAliasCleared: "Alias de db{db} eliminado", createKeyTypeHelp: { string: "Valor único de hasta 512 MB; útil para caché y contadores.", hash: "Mapa plano campo-valor; leer un Hash grande completo es O(N).", diff --git a/apps/desktop/src/i18n/locales/it.ts b/apps/desktop/src/i18n/locales/it.ts index 65bb7980d..0fbf5da18 100644 --- a/apps/desktop/src/i18n/locales/it.ts +++ b/apps/desktop/src/i18n/locales/it.ts @@ -2821,6 +2821,13 @@ export default withEnglishFallback({ summarySize: "dimensione", }, redis: { + setDatabaseAlias: "Imposta alias database", + databaseAliasTitle: "Alias per db{db}", + databaseAliasPlaceholder: "es. ordini, cache, sviluppo", + databaseAliasHint: "L'alias viene usato solo per visualizzazione e ricerca. I comandi Redis continuano a usare l'indice numerico.", + clearDatabaseAlias: "Rimuovi alias", + databaseAliasSaved: "Alias di db{db} impostato su {alias}", + databaseAliasCleared: "Alias di db{db} rimosso", createKeyTypeHelp: { string: "Valore singolo fino a 512 MB; utile per cache e contatori.", hash: "Mappa campo-valore piatta; leggere un Hash grande intero è O(N).", diff --git a/apps/desktop/src/i18n/locales/ja.ts b/apps/desktop/src/i18n/locales/ja.ts index b47191a1b..bf3a8f40e 100644 --- a/apps/desktop/src/i18n/locales/ja.ts +++ b/apps/desktop/src/i18n/locales/ja.ts @@ -2822,6 +2822,13 @@ export default withEnglishFallback({ }, }, redis: { + setDatabaseAlias: "データベースの別名を設定", + databaseAliasTitle: "db{db} の別名", + databaseAliasPlaceholder: "例:注文、キャッシュ、開発", + databaseAliasHint: "別名は表示と検索にのみ使用されます。Redis コマンドは引き続き数値のデータベース番号を使用します。", + clearDatabaseAlias: "別名をクリア", + databaseAliasSaved: "db{db} の別名を {alias} に設定しました", + databaseAliasCleared: "db{db} の別名をクリアしました", createKeyTypeHelp: { string: "最大 512 MB の単一値。キャッシュやカウンター向けです。", hash: "フラットなフィールド値マップ。大きな Hash 全読込は O(N) です。", diff --git a/apps/desktop/src/i18n/locales/pt-BR.ts b/apps/desktop/src/i18n/locales/pt-BR.ts index 1c028d249..7d883cf0e 100644 --- a/apps/desktop/src/i18n/locales/pt-BR.ts +++ b/apps/desktop/src/i18n/locales/pt-BR.ts @@ -2823,6 +2823,13 @@ export default withEnglishFallback({ summarySize: "tamanho", }, redis: { + setDatabaseAlias: "Definir alias do banco", + databaseAliasTitle: "Alias para db{db}", + databaseAliasPlaceholder: "ex.: pedidos, cache, desenvolvimento", + databaseAliasHint: "O alias é usado apenas para exibição e pesquisa. Os comandos Redis continuam usando o índice numérico.", + clearDatabaseAlias: "Limpar alias", + databaseAliasSaved: "Alias de db{db} definido como {alias}", + databaseAliasCleared: "Alias de db{db} removido", createKeyTypeHelp: { string: "Valor único de até 512 MB; útil para cache e contadores.", hash: "Mapa plano campo-valor; ler um Hash grande inteiro é O(N).", diff --git a/apps/desktop/src/i18n/locales/zh-CN.ts b/apps/desktop/src/i18n/locales/zh-CN.ts index 97dce2574..65624bf13 100644 --- a/apps/desktop/src/i18n/locales/zh-CN.ts +++ b/apps/desktop/src/i18n/locales/zh-CN.ts @@ -2971,6 +2971,13 @@ export default withEnglishFallback({ summarySize: "大小", }, redis: { + setDatabaseAlias: "设置数据库别名", + databaseAliasTitle: "设置 db{db} 的别名", + databaseAliasPlaceholder: "例如:订单、缓存、开发环境", + databaseAliasHint: "别名仅用于显示和搜索,Redis 命令仍使用数字数据库编号。", + clearDatabaseAlias: "清除别名", + databaseAliasSaved: "已将 db{db} 的别名设置为「{alias}」", + databaseAliasCleared: "已清除 db{db} 的别名", selectKey: "选择一个 key 查看值", noKeys: "未找到 key", noKeysInScanHint: "当前扫描范围内未命中匹配的 key", diff --git a/apps/desktop/src/i18n/locales/zh-TW.ts b/apps/desktop/src/i18n/locales/zh-TW.ts index ad2821aef..b698cf00d 100644 --- a/apps/desktop/src/i18n/locales/zh-TW.ts +++ b/apps/desktop/src/i18n/locales/zh-TW.ts @@ -2494,6 +2494,13 @@ export default withEnglishFallback({ noJoinSql: "沒有可複製的關係 SQL", }, redis: { + setDatabaseAlias: "設定資料庫別名", + databaseAliasTitle: "設定 db{db} 的別名", + databaseAliasPlaceholder: "例如:訂單、快取、開發環境", + databaseAliasHint: "別名僅用於顯示和搜尋,Redis 命令仍使用數字資料庫編號。", + clearDatabaseAlias: "清除別名", + databaseAliasSaved: "已將 db{db} 的別名設定為「{alias}」", + databaseAliasCleared: "已清除 db{db} 的別名", createKeyTypeHelp: { string: "最大 512 MB 的單一值;適合快取與計數器。", hash: "扁平欄位值映射;完整讀取大型 Hash 為 O(N)。", diff --git a/apps/desktop/src/lib/__tests__/redis/redisDatabaseAlias.spec.ts b/apps/desktop/src/lib/__tests__/redis/redisDatabaseAlias.spec.ts new file mode 100644 index 000000000..8775abe96 --- /dev/null +++ b/apps/desktop/src/lib/__tests__/redis/redisDatabaseAlias.spec.ts @@ -0,0 +1,35 @@ +import { describe, expect, it } from "vitest"; +import { normalizeRedisDatabaseAliases, redisDatabaseAlias, redisDatabaseLabel } from "@/lib/redis/redisDatabaseAlias"; + +describe("redisDatabaseAlias", () => { + it("normalizes numeric database keys and trims aliases", () => { + expect( + normalizeRedisDatabaseAliases({ + "0": " default ", + "03": " orders ", + invalid: "ignored", + "4": "", + }), + ).toEqual({ + "0": "default", + "3": "orders", + }); + }); + + it("returns undefined for empty alias maps", () => { + expect(normalizeRedisDatabaseAliases({ "0": " " })).toBeUndefined(); + expect(normalizeRedisDatabaseAliases(null)).toBeUndefined(); + }); + + it("looks up aliases without changing the Redis database index", () => { + const aliases = { "3": "orders" }; + expect(redisDatabaseAlias(aliases, "03")).toBe("orders"); + expect(redisDatabaseAlias(aliases, "invalid")).toBeUndefined(); + }); + + it("formats labels with aliases and key counts", () => { + expect(redisDatabaseLabel(3, { "3": "orders" }, 128)).toBe("db3 · orders (128)"); + expect(redisDatabaseLabel(0, undefined, 0)).toBe("db0 (0)"); + expect(redisDatabaseLabel(2, { "2": "cache" })).toBe("db2 · cache"); + }); +}); diff --git a/apps/desktop/src/lib/redis/redisDatabaseAlias.ts b/apps/desktop/src/lib/redis/redisDatabaseAlias.ts new file mode 100644 index 000000000..757c062b5 --- /dev/null +++ b/apps/desktop/src/lib/redis/redisDatabaseAlias.ts @@ -0,0 +1,30 @@ +export type RedisDatabaseAliases = Record; + +function redisDatabaseKey(database: string | number): string | null { + const index = typeof database === "number" ? database : Number(database); + return Number.isInteger(index) && index >= 0 ? String(index) : null; +} + +export function normalizeRedisDatabaseAliases(value: unknown): RedisDatabaseAliases | undefined { + if (!value || typeof value !== "object" || Array.isArray(value)) return undefined; + + const aliases: RedisDatabaseAliases = {}; + for (const [database, alias] of Object.entries(value)) { + const key = redisDatabaseKey(database); + const normalizedAlias = typeof alias === "string" ? alias.trim() : ""; + if (key != null && normalizedAlias) aliases[key] = normalizedAlias; + } + return Object.keys(aliases).length > 0 ? aliases : undefined; +} + +export function redisDatabaseAlias(aliases: RedisDatabaseAliases | undefined, database: string | number): string | undefined { + const key = redisDatabaseKey(database); + return key == null ? undefined : aliases?.[key]?.trim() || undefined; +} + +export function redisDatabaseLabel(database: string | number, aliases?: RedisDatabaseAliases, totalKeyCount?: number): string { + const key = redisDatabaseKey(database) ?? String(database); + const alias = redisDatabaseAlias(aliases, key); + const name = alias ? `db${key} · ${alias}` : `db${key}`; + return totalKeyCount == null ? name : `${name} (${totalKeyCount})`; +} diff --git a/apps/desktop/src/stores/__tests__/connectionStore.redisAlias.spec.ts b/apps/desktop/src/stores/__tests__/connectionStore.redisAlias.spec.ts new file mode 100644 index 000000000..d79c74a08 --- /dev/null +++ b/apps/desktop/src/stores/__tests__/connectionStore.redisAlias.spec.ts @@ -0,0 +1,145 @@ +import { createPinia, setActivePinia } from "pinia"; +import { beforeEach, describe, expect, it, vi } from "vitest"; +import type { ConnectionConfig, TreeNode } from "@/types/database"; + +function installLocalStorage() { + const data = new Map(); + vi.stubGlobal("localStorage", { + getItem: vi.fn((key: string) => data.get(key) ?? null), + setItem: vi.fn((key: string, value: string) => data.set(key, value)), + removeItem: vi.fn((key: string) => data.delete(key)), + }); +} + +function redisConnection(): ConnectionConfig { + return { + id: "redis-1", + name: "Redis", + db_type: "redis", + host: "127.0.0.1", + port: 6379, + username: "", + password: "", + database: "0", + }; +} + +function seedRedisTree(store: { treeNodes: TreeNode[] }) { + store.treeNodes.push({ + id: "redis-1", + label: "Redis", + type: "connection", + connectionId: "redis-1", + children: [ + { + id: "redis-1:db3", + label: "db3 (12)", + type: "redis-db", + connectionId: "redis-1", + database: "3", + loadedKeyCount: 0, + totalKeyCount: 12, + children: [], + }, + ], + }); +} + +describe("connectionStore Redis database aliases", () => { + beforeEach(() => { + vi.resetModules(); + vi.unstubAllGlobals(); + installLocalStorage(); + setActivePinia(createPinia()); + }); + + it("persists an alias without disconnecting and keeps it during count refreshes", async () => { + const saveConnections = vi.fn().mockResolvedValue(undefined); + vi.doMock("@/lib/backend/tauriRuntime", () => ({ isTauriRuntime: () => false })); + vi.doMock("@/lib/backend/api", () => ({ + saveConnections, + saveSidebarLayout: vi.fn().mockResolvedValue(undefined), + deleteSchemaCachePrefix: vi.fn().mockResolvedValue(undefined), + loadSchemaCache: vi.fn().mockResolvedValue(null), + saveSchemaCache: vi.fn().mockResolvedValue(undefined), + })); + + const { useConnectionStore } = await import("@/stores/connectionStore"); + const store = useConnectionStore(); + store.addEphemeralConnection(redisConnection()); + seedRedisTree(store); + + await store.setRedisDatabaseAlias("redis-1", "3", " orders "); + + expect(store.getRedisDatabaseAlias("redis-1", 3)).toBe("orders"); + expect(store.connectedIds.has("redis-1")).toBe(true); + expect(store.treeNodes[0].children?.[0].label).toBe("db3 · orders (12)"); + expect(saveConnections).toHaveBeenLastCalledWith([ + expect.objectContaining({ + id: "redis-1", + redis_database_aliases: { "3": "orders" }, + }), + ]); + + store.updateRedisDbKeyStats("redis-1", 3, { total: 15 }); + expect(store.treeNodes[0].children?.[0].label).toBe("db3 · orders (15)"); + }); + + it("clears an alias and removes the empty map from persisted config", async () => { + const saveConnections = vi.fn().mockResolvedValue(undefined); + vi.doMock("@/lib/backend/tauriRuntime", () => ({ isTauriRuntime: () => false })); + vi.doMock("@/lib/backend/api", () => ({ + saveConnections, + saveSidebarLayout: vi.fn().mockResolvedValue(undefined), + deleteSchemaCachePrefix: vi.fn().mockResolvedValue(undefined), + loadSchemaCache: vi.fn().mockResolvedValue(null), + saveSchemaCache: vi.fn().mockResolvedValue(undefined), + })); + + const { useConnectionStore } = await import("@/stores/connectionStore"); + const store = useConnectionStore(); + store.addEphemeralConnection({ + ...redisConnection(), + redis_database_aliases: { "3": "orders" }, + }); + seedRedisTree(store); + + await store.setRedisDatabaseAlias("redis-1", 3); + + expect(store.getRedisDatabaseAlias("redis-1", 3)).toBeUndefined(); + expect(store.treeNodes[0].children?.[0].label).toBe("db3 (12)"); + expect(saveConnections).toHaveBeenLastCalledWith([ + expect.objectContaining({ + id: "redis-1", + redis_database_aliases: undefined, + }), + ]); + }); + + it("keeps aliases for one-time Redis connections in memory without persisting secrets", async () => { + const saveConnections = vi.fn().mockResolvedValue(undefined); + vi.doMock("@/lib/backend/tauriRuntime", () => ({ isTauriRuntime: () => false })); + vi.doMock("@/lib/backend/api", () => ({ + saveConnections, + saveSidebarLayout: vi.fn().mockResolvedValue(undefined), + deleteSchemaCachePrefix: vi.fn().mockResolvedValue(undefined), + loadSchemaCache: vi.fn().mockResolvedValue(null), + saveSchemaCache: vi.fn().mockResolvedValue(undefined), + })); + + const { useConnectionStore } = await import("@/stores/connectionStore"); + const store = useConnectionStore(); + store.addEphemeralConnection({ + ...redisConnection(), + one_time: true, + password: "one-time-secret", + }); + seedRedisTree(store); + + await store.setRedisDatabaseAlias("redis-1", 3, "orders"); + + expect(store.getRedisDatabaseAlias("redis-1", 3)).toBe("orders"); + expect(store.connections[0]).toEqual(expect.objectContaining({ password: "one-time-secret", redis_database_aliases: { "3": "orders" } })); + expect(saveConnections).toHaveBeenLastCalledWith([]); + }); +}); diff --git a/apps/desktop/src/stores/connectionStore.ts b/apps/desktop/src/stores/connectionStore.ts index fb7e8da05..71c0e698f 100644 --- a/apps/desktop/src/stores/connectionStore.ts +++ b/apps/desktop/src/stores/connectionStore.ts @@ -102,6 +102,7 @@ import { toMongoCollectionKind } from "@/lib/sidebar/mongoCollectionMutation"; import { completionSchemasFromTree, completionTablesFromTree } from "@/lib/metadata/completionTreeIndex"; import { kvRootNodeLabel } from "@/lib/kv/kvRootPresentation"; import { REDIS_SCAN_PAGE_SIZE_DEFAULT } from "@/lib/redis/redisKeyPattern"; +import { normalizeRedisDatabaseAliases, redisDatabaseAlias, redisDatabaseLabel } from "@/lib/redis/redisDatabaseAlias"; import { appendAgentDriverUpdateHint, hasAgentDriverUpdate, hasInstalledAgentVersion, type AgentDriverInstallState } from "@/lib/connection/agentDriverInstallHint"; import { appendConnectionErrorHints } from "@/lib/connection/connectionErrorHints"; import { appendVisibleDatabaseSelection } from "@/lib/connection/connectionVisibleDatabases"; @@ -286,11 +287,6 @@ type BeforeConnectHandler = (config: ConnectionConfig) => Promise; export const CONNECTION_ATTEMPT_CANCELLED_MESSAGE = "Connection attempt was cancelled"; -function redisDbLabel(db: number, _loadedKeyCount?: number, totalKeyCount?: number): string { - if (totalKeyCount == null) return `db${db}`; - return `db${db} (${totalKeyCount})`; -} - function metadataDriverProfile(config?: ConnectionConfig): string | undefined { return config?.driver_profile || config?.db_type; } @@ -1033,6 +1029,7 @@ export const useConnectionStore = defineStore("connection", () => { query_timeout_secs: config.query_timeout_secs ?? 30, idle_timeout_secs: config.idle_timeout_secs ?? 60, keepalive_interval_secs: config.keepalive_interval_secs ?? DEFAULT_KEEPALIVE_INTERVAL_SECS, + redis_database_aliases: normalizeRedisDatabaseAliases(config.redis_database_aliases), database_info: normalizeDatabaseConnectionInfo(config.database_info), }; } @@ -2389,6 +2386,37 @@ export const useConnectionStore = defineStore("connection", () => { return config?.database === database && database !== ""; } + function getRedisDatabaseAlias(connectionId: string, database: string | number): string | undefined { + return redisDatabaseAlias(getConfig(connectionId)?.redis_database_aliases, database); + } + + async function setRedisDatabaseAlias(connectionId: string, database: string | number, alias?: string) { + const index = typeof database === "number" ? database : Number(database); + const configIndex = connections.value.findIndex((connection) => connection.id === connectionId); + const config = connections.value[configIndex]; + if (!config || config.db_type !== "redis" || !Number.isInteger(index) || index < 0) return; + + const key = String(index); + const aliases = { ...(config.redis_database_aliases || {}) }; + const normalizedAlias = alias?.trim() || ""; + if (normalizedAlias) aliases[key] = normalizedAlias; + else delete aliases[key]; + + const redisDatabaseAliases = normalizeRedisDatabaseAliases(aliases); + const nextConnections = [...connections.value]; + nextConnections[configIndex] = { + ...config, + redis_database_aliases: redisDatabaseAliases, + }; + await persistConnections(nextConnections); + connections.value = nextConnections; + + const node = findNode(treeNodes.value, `${connectionId}:db${key}`); + if (node?.type === "redis-db") { + node.label = redisDatabaseLabel(index, redisDatabaseAliases, node.totalKeyCount); + } + } + async function setVisibleDatabases(connectionId: string, databaseNames: string[]) { const config = getConfig(connectionId); if (!config) return; @@ -3041,7 +3069,7 @@ export const useConnectionStore = defineStore("connection", () => { .filter((db) => visibleNameSet.has(String(db.db))) .map((db) => ({ id: `${connectionId}:db${db.db}`, - label: redisDbLabel(db.db, 0, db.keys), + label: redisDatabaseLabel(db.db, config?.redis_database_aliases, db.keys), type: "redis-db" as const, connectionId, database: String(db.db), @@ -3251,7 +3279,7 @@ export const useConnectionStore = defineStore("connection", () => { if (stats.totalDelta != null && node.totalKeyCount != null) { node.totalKeyCount = Math.max(0, node.totalKeyCount + stats.totalDelta); } - node.label = redisDbLabel(db, node.loadedKeyCount, node.totalKeyCount); + node.label = redisDatabaseLabel(db, getConfig(connectionId)?.redis_database_aliases, node.totalKeyCount); } // Re-fetch the authoritative per-db key counts (INFO keyspace, lightweight) and update @@ -5890,7 +5918,7 @@ export const useConnectionStore = defineStore("connection", () => { } async function persistConnections(nextConnections: ConnectionConfig[] = connections.value) { - await api.saveConnections(nextConnections); + await api.saveConnections(nextConnections.filter((connection) => connection.one_time !== true)); } function persistSidebarLayoutDebounced() { @@ -6462,6 +6490,8 @@ export const useConnectionStore = defineStore("connection", () => { setDefaultDatabase, clearDefaultDatabase, isDefaultDatabase, + getRedisDatabaseAlias, + setRedisDatabaseAlias, setVisibleDatabases, clearVisibleDatabases, ensureVisibleDatabase, diff --git a/apps/desktop/src/types/database.ts b/apps/desktop/src/types/database.ts index 6dc9910d8..aa8ec6c4b 100644 --- a/apps/desktop/src/types/database.ts +++ b/apps/desktop/src/types/database.ts @@ -158,6 +158,7 @@ export interface ConnectionConfig { redis_cluster_nodes?: string; redis_key_separator?: string; redis_scan_page_size?: number; + redis_database_aliases?: Record; etcd_endpoints?: string; gbase_server?: string; informix_server?: string; diff --git a/crates/dbx-core/src/agent_connection.rs b/crates/dbx-core/src/agent_connection.rs index 8987a10dc..f2b30f230 100644 --- a/crates/dbx-core/src/agent_connection.rs +++ b/crates/dbx-core/src/agent_connection.rs @@ -645,6 +645,7 @@ mod tests { redis_cluster_nodes: String::new(), redis_key_separator: default_redis_key_separator(), redis_scan_page_size: None, + redis_database_aliases: Default::default(), etcd_endpoints: String::new(), gbase_server: String::new(), informix_server: String::new(), diff --git a/crates/dbx-core/src/cloud_sync.rs b/crates/dbx-core/src/cloud_sync.rs index 5976d3760..1ddb5b8de 100644 --- a/crates/dbx-core/src/cloud_sync.rs +++ b/crates/dbx-core/src/cloud_sync.rs @@ -1159,6 +1159,7 @@ mod tests { redis_cluster_nodes: String::new(), redis_key_separator: default_redis_key_separator(), redis_scan_page_size: None, + redis_database_aliases: Default::default(), etcd_endpoints: String::new(), gbase_server: String::new(), informix_server: String::new(), @@ -1215,6 +1216,7 @@ mod tests { redis_cluster_nodes: String::new(), redis_key_separator: default_redis_key_separator(), redis_scan_page_size: None, + redis_database_aliases: Default::default(), etcd_endpoints: String::new(), gbase_server: String::new(), informix_server: String::new(), @@ -1335,6 +1337,7 @@ mod tests { redis_cluster_nodes: String::new(), redis_key_separator: default_redis_key_separator(), redis_scan_page_size: None, + redis_database_aliases: Default::default(), etcd_endpoints: String::new(), gbase_server: String::new(), informix_server: String::new(), diff --git a/crates/dbx-core/src/connection.rs b/crates/dbx-core/src/connection.rs index dece16e05..49cfc649d 100644 --- a/crates/dbx-core/src/connection.rs +++ b/crates/dbx-core/src/connection.rs @@ -3937,6 +3937,7 @@ mod tests { redis_cluster_nodes: String::new(), redis_key_separator: default_redis_key_separator(), redis_scan_page_size: None, + redis_database_aliases: Default::default(), etcd_endpoints: String::new(), gbase_server: String::new(), informix_server: String::new(), diff --git a/crates/dbx-core/src/connection_secrets.rs b/crates/dbx-core/src/connection_secrets.rs index 2898cffed..ea6f276a1 100644 --- a/crates/dbx-core/src/connection_secrets.rs +++ b/crates/dbx-core/src/connection_secrets.rs @@ -775,6 +775,7 @@ mod tests { redis_cluster_nodes: String::new(), redis_key_separator: crate::models::connection::default_redis_key_separator(), redis_scan_page_size: None, + redis_database_aliases: Default::default(), etcd_endpoints: String::new(), gbase_server: String::new(), informix_server: String::new(), diff --git a/crates/dbx-core/src/db/redis_driver.rs b/crates/dbx-core/src/db/redis_driver.rs index d3bd2b542..f80ae4a01 100644 --- a/crates/dbx-core/src/db/redis_driver.rs +++ b/crates/dbx-core/src/db/redis_driver.rs @@ -4668,6 +4668,7 @@ mod tests { redis_cluster_nodes: String::new(), redis_key_separator: crate::models::connection::default_redis_key_separator(), redis_scan_page_size: None, + redis_database_aliases: Default::default(), etcd_endpoints: String::new(), gbase_server: String::new(), informix_server: String::new(), diff --git a/crates/dbx-core/src/models/connection.rs b/crates/dbx-core/src/models/connection.rs index c5d10573a..cfb30f9e3 100644 --- a/crates/dbx-core/src/models/connection.rs +++ b/crates/dbx-core/src/models/connection.rs @@ -144,6 +144,8 @@ pub struct ConnectionConfig { pub redis_key_separator: String, #[serde(default, skip_serializing_if = "Option::is_none")] pub redis_scan_page_size: Option, + #[serde(default, skip_serializing_if = "HashMap::is_empty")] + pub redis_database_aliases: HashMap, #[serde(default, skip_serializing_if = "String::is_empty")] pub etcd_endpoints: String, #[serde(default, skip_serializing_if = "String::is_empty")] @@ -613,6 +615,8 @@ struct ConnectionConfigData { #[serde(default)] pub redis_scan_page_size: Option, #[serde(default)] + pub redis_database_aliases: HashMap, + #[serde(default)] pub etcd_endpoints: String, #[serde(default)] pub gbase_server: String, @@ -679,6 +683,7 @@ impl From for ConnectionConfig { redis_cluster_nodes: data.redis_cluster_nodes, redis_key_separator: data.redis_key_separator, redis_scan_page_size: data.redis_scan_page_size, + redis_database_aliases: data.redis_database_aliases, etcd_endpoints: data.etcd_endpoints, gbase_server: data.gbase_server, informix_server: data.informix_server, @@ -2227,6 +2232,7 @@ mod tests { redis_cluster_nodes: String::new(), redis_key_separator: default_redis_key_separator(), redis_scan_page_size: None, + redis_database_aliases: Default::default(), etcd_endpoints: String::new(), gbase_server: String::new(), informix_server: String::new(), @@ -2273,6 +2279,24 @@ mod tests { assert_eq!(serde_json::from_value::(value).unwrap().note, config.note); } + #[test] + fn redis_database_aliases_are_optional_and_round_trip() { + let config = mysql_config("default", "secret", None); + let value = serde_json::to_value(&config).unwrap(); + assert!(value.get("redis_database_aliases").is_none()); + assert!(serde_json::from_value::(value).unwrap().redis_database_aliases.is_empty()); + + let mut config = config; + config.db_type = DatabaseType::Redis; + config.redis_database_aliases.insert("3".to_string(), "orders".to_string()); + let value = serde_json::to_value(&config).unwrap(); + assert_eq!(value["redis_database_aliases"]["3"], "orders"); + assert_eq!( + serde_json::from_value::(value).unwrap().redis_database_aliases, + config.redis_database_aliases + ); + } + #[test] fn database_identifier_whitespace_is_preserved_and_percent_encoded() { let mut config = mysql_config("root", "secret", Some(" analytics ")); diff --git a/crates/dbx-core/src/mq/config.rs b/crates/dbx-core/src/mq/config.rs index 9781b661d..f697bc5b4 100644 --- a/crates/dbx-core/src/mq/config.rs +++ b/crates/dbx-core/src/mq/config.rs @@ -142,6 +142,7 @@ mod tests { redis_cluster_nodes: String::new(), redis_key_separator: String::new(), redis_scan_page_size: None, + redis_database_aliases: Default::default(), etcd_endpoints: String::new(), gbase_server: String::new(), informix_server: String::new(), diff --git a/crates/dbx-core/src/mq/service.rs b/crates/dbx-core/src/mq/service.rs index 57d338c9c..e095a0a97 100644 --- a/crates/dbx-core/src/mq/service.rs +++ b/crates/dbx-core/src/mq/service.rs @@ -895,6 +895,7 @@ mod tests { redis_cluster_nodes: String::new(), redis_key_separator: ":".to_string(), redis_scan_page_size: None, + redis_database_aliases: Default::default(), etcd_endpoints: String::new(), gbase_server: String::new(), informix_server: String::new(), diff --git a/crates/dbx-core/src/nacos/config.rs b/crates/dbx-core/src/nacos/config.rs index ff7ec423d..9a15f865b 100644 --- a/crates/dbx-core/src/nacos/config.rs +++ b/crates/dbx-core/src/nacos/config.rs @@ -324,6 +324,7 @@ mod tests { redis_cluster_nodes: String::new(), redis_key_separator: ":".to_string(), redis_scan_page_size: None, + redis_database_aliases: Default::default(), etcd_endpoints: String::new(), gbase_server: String::new(), informix_server: String::new(), diff --git a/crates/dbx-core/src/nacos/service.rs b/crates/dbx-core/src/nacos/service.rs index dd3b15c40..30adae14e 100644 --- a/crates/dbx-core/src/nacos/service.rs +++ b/crates/dbx-core/src/nacos/service.rs @@ -256,6 +256,7 @@ mod tests { redis_cluster_nodes: String::new(), redis_key_separator: ":".to_string(), redis_scan_page_size: None, + redis_database_aliases: Default::default(), etcd_endpoints: String::new(), gbase_server: String::new(), informix_server: String::new(), @@ -327,6 +328,7 @@ mod tests { redis_cluster_nodes: String::new(), redis_key_separator: ":".to_string(), redis_scan_page_size: None, + redis_database_aliases: Default::default(), etcd_endpoints: String::new(), gbase_server: String::new(), informix_server: String::new(), diff --git a/crates/dbx-core/src/production_safety.rs b/crates/dbx-core/src/production_safety.rs index 4d1891c0b..acb8b5036 100644 --- a/crates/dbx-core/src/production_safety.rs +++ b/crates/dbx-core/src/production_safety.rs @@ -680,6 +680,7 @@ mod tests { redis_cluster_nodes: String::new(), redis_key_separator: ":".to_string(), redis_scan_page_size: Some(1000), + redis_database_aliases: Default::default(), etcd_endpoints: String::new(), gbase_server: String::new(), informix_server: String::new(), diff --git a/crates/dbx-core/src/query.rs b/crates/dbx-core/src/query.rs index e3c59c856..95f5fc49d 100644 --- a/crates/dbx-core/src/query.rs +++ b/crates/dbx-core/src/query.rs @@ -4065,6 +4065,7 @@ mod tests { redis_cluster_nodes: String::new(), redis_key_separator: default_redis_key_separator(), redis_scan_page_size: None, + redis_database_aliases: Default::default(), etcd_endpoints: String::new(), gbase_server: String::new(), informix_server: String::new(), @@ -5190,6 +5191,7 @@ mod tests { redis_cluster_nodes: String::new(), redis_key_separator: default_redis_key_separator(), redis_scan_page_size: None, + redis_database_aliases: Default::default(), etcd_endpoints: String::new(), gbase_server: String::new(), informix_server: String::new(), diff --git a/crates/dbx-core/src/schema.rs b/crates/dbx-core/src/schema.rs index 097404e7a..12e2891c5 100644 --- a/crates/dbx-core/src/schema.rs +++ b/crates/dbx-core/src/schema.rs @@ -3063,6 +3063,7 @@ mod tests { redis_cluster_nodes: String::new(), redis_key_separator: crate::models::connection::default_redis_key_separator(), redis_scan_page_size: None, + redis_database_aliases: Default::default(), etcd_endpoints: String::new(), gbase_server: String::new(), informix_server: String::new(), diff --git a/crates/dbx-core/src/storage.rs b/crates/dbx-core/src/storage.rs index e869423db..58d82c4f0 100644 --- a/crates/dbx-core/src/storage.rs +++ b/crates/dbx-core/src/storage.rs @@ -3978,6 +3978,7 @@ mod tests { redis_cluster_nodes: String::new(), redis_key_separator: ":".to_string(), redis_scan_page_size: None, + redis_database_aliases: Default::default(), etcd_endpoints: String::new(), gbase_server: String::new(), informix_server: String::new(), @@ -4041,6 +4042,7 @@ mod tests { redis_cluster_nodes: String::new(), redis_key_separator: ":".to_string(), redis_scan_page_size: None, + redis_database_aliases: Default::default(), etcd_endpoints: String::new(), gbase_server: String::new(), informix_server: String::new(), diff --git a/crates/dbx-core/src/transfer.rs b/crates/dbx-core/src/transfer.rs index ad35d3011..e2873fb9c 100644 --- a/crates/dbx-core/src/transfer.rs +++ b/crates/dbx-core/src/transfer.rs @@ -4961,6 +4961,7 @@ mod tests { redis_cluster_nodes: String::new(), redis_key_separator: default_redis_key_separator(), redis_scan_page_size: None, + redis_database_aliases: Default::default(), etcd_endpoints: String::new(), gbase_server: String::new(), informix_server: String::new(), diff --git a/crates/dbx-core/tests/database_export_prefetch.rs b/crates/dbx-core/tests/database_export_prefetch.rs index 2dbe9034d..78bfa09f2 100644 --- a/crates/dbx-core/tests/database_export_prefetch.rs +++ b/crates/dbx-core/tests/database_export_prefetch.rs @@ -124,6 +124,7 @@ fn postgres_test_config(id: &str, port: u16) -> ConnectionConfig { redis_cluster_nodes: String::new(), redis_key_separator: dbx_core::models::connection::default_redis_key_separator(), redis_scan_page_size: None, + redis_database_aliases: Default::default(), etcd_endpoints: String::new(), gbase_server: String::new(), informix_server: String::new(), diff --git a/crates/dbx-core/tests/live_postgres_query_result_export.rs b/crates/dbx-core/tests/live_postgres_query_result_export.rs index 5132b24fa..af7d46e0c 100644 --- a/crates/dbx-core/tests/live_postgres_query_result_export.rs +++ b/crates/dbx-core/tests/live_postgres_query_result_export.rs @@ -57,6 +57,7 @@ fn live_postgres_config( redis_cluster_nodes: String::new(), redis_key_separator: dbx_core::models::connection::default_redis_key_separator(), redis_scan_page_size: None, + redis_database_aliases: Default::default(), etcd_endpoints: String::new(), gbase_server: String::new(), informix_server: String::new(), diff --git a/crates/dbx-core/tests/live_postgres_transfer.rs b/crates/dbx-core/tests/live_postgres_transfer.rs index a289accdb..b56547c15 100644 --- a/crates/dbx-core/tests/live_postgres_transfer.rs +++ b/crates/dbx-core/tests/live_postgres_transfer.rs @@ -49,6 +49,7 @@ fn postgres_test_config(id: &str, database: &str) -> ConnectionConfig { redis_cluster_nodes: String::new(), redis_key_separator: dbx_core::models::connection::default_redis_key_separator(), redis_scan_page_size: None, + redis_database_aliases: Default::default(), etcd_endpoints: String::new(), gbase_server: String::new(), informix_server: String::new(), diff --git a/crates/dbx-core/tests/live_sqlserver_completion.rs b/crates/dbx-core/tests/live_sqlserver_completion.rs index 778d96038..a0eddbb3a 100644 --- a/crates/dbx-core/tests/live_sqlserver_completion.rs +++ b/crates/dbx-core/tests/live_sqlserver_completion.rs @@ -52,6 +52,7 @@ fn live_sqlserver_config(id: &str, database: &str) -> dbx_core::models::connecti redis_cluster_nodes: String::new(), redis_key_separator: dbx_core::models::connection::default_redis_key_separator(), redis_scan_page_size: None, + redis_database_aliases: Default::default(), etcd_endpoints: String::new(), gbase_server: String::new(), informix_server: String::new(), diff --git a/crates/dbx-core/tests/live_sqlserver_query_result_export.rs b/crates/dbx-core/tests/live_sqlserver_query_result_export.rs index 9802fc9ef..e18d2a077 100644 --- a/crates/dbx-core/tests/live_sqlserver_query_result_export.rs +++ b/crates/dbx-core/tests/live_sqlserver_query_result_export.rs @@ -47,6 +47,7 @@ fn live_sqlserver_config(id: &str, database: &str) -> dbx_core::models::connecti redis_cluster_nodes: String::new(), redis_key_separator: dbx_core::models::connection::default_redis_key_separator(), redis_scan_page_size: None, + redis_database_aliases: Default::default(), etcd_endpoints: String::new(), gbase_server: String::new(), informix_server: String::new(), diff --git a/crates/dbx-web/src/routes/connection.rs b/crates/dbx-web/src/routes/connection.rs index ca83b219e..4e674395f 100644 --- a/crates/dbx-web/src/routes/connection.rs +++ b/crates/dbx-web/src/routes/connection.rs @@ -453,6 +453,7 @@ mod tests { redis_cluster_nodes: String::new(), redis_key_separator: dbx_core::models::connection::default_redis_key_separator(), redis_scan_page_size: None, + redis_database_aliases: Default::default(), etcd_endpoints: String::new(), gbase_server: String::new(), informix_server: String::new(), diff --git a/src-tauri/src/commands/connection.rs b/src-tauri/src/commands/connection.rs index 0ce2704f0..1979c890e 100644 --- a/src-tauri/src/commands/connection.rs +++ b/src-tauri/src/commands/connection.rs @@ -216,6 +216,7 @@ mod tests { redis_cluster_nodes: String::new(), redis_key_separator: dbx_core::models::connection::default_redis_key_separator(), redis_scan_page_size: None, + redis_database_aliases: Default::default(), etcd_endpoints: String::new(), gbase_server: String::new(), informix_server: String::new(),
{{ t("redis.databaseAliasHint") }}