♻️ refactor(connection): 拆分连接类型目录配置

- 抽出连接类型分组、默认端口和提示文案到独立 catalog

- ConnectionModal 仅负责渲染数据源图标和选择流程

- 补充 catalog 单元测试并更新连接弹窗源码快照测试
This commit is contained in:
Syngnat
2026-06-12 04:07:02 +08:00
parent d5d4d4fabc
commit c64b1fbb72
4 changed files with 216 additions and 228 deletions

View File

@@ -4,7 +4,8 @@ import { readFileSync } from 'node:fs';
const connectionModalSource = readFileSync(new URL('./ConnectionModal.tsx', import.meta.url), 'utf8');
const redisSectionsSource = readFileSync(new URL('./ConnectionModalRedisSections.tsx', import.meta.url), 'utf8');
const mongoSectionsSource = readFileSync(new URL('./ConnectionModalMongoSections.tsx', import.meta.url), 'utf8');
const source = `${connectionModalSource}\n${redisSectionsSource}\n${mongoSectionsSource}`;
const connectionTypeCatalogSource = readFileSync(new URL('../utils/connectionTypeCatalog.ts', import.meta.url), 'utf8');
const source = `${connectionModalSource}\n${redisSectionsSource}\n${mongoSectionsSource}\n${connectionTypeCatalogSource}`;
describe('ConnectionModal edit password behavior', () => {
it('keeps the prefilled primary password masked by default', () => {
@@ -22,14 +23,14 @@ describe('ConnectionModal edit password behavior', () => {
describe('ConnectionModal data source registry', () => {
it('exposes Elasticsearch in the create-connection picker with HTTP defaults', () => {
expect(source).toContain('case "elasticsearch":');
expect(source).toContain("case 'elasticsearch':");
expect(source).toContain('return 9200;');
expect(source).toContain('elasticsearch: ["http", "https"]');
expect(source).toContain('key: "elasticsearch"');
expect(source).toContain('name: "Elasticsearch"');
expect(source).toContain('getDbIcon("elasticsearch", undefined, 36)');
expect(source).toContain("key: 'elasticsearch'");
expect(source).toContain("name: 'Elasticsearch'");
expect(source).toContain('icon: getDbIcon(item.key, undefined, 36)');
expect(source).toContain('type === "elasticsearch"');
expect(source).toContain('return "支持索引浏览、Mapping 检查、JSON DSL 和 query_string 查询";');
expect(source).toContain("return '支持索引浏览、Mapping 检查、JSON DSL 和 query_string 查询';");
expect(source).toContain(
'type === "clickhouse" ? "default" : (type === "redis" || type === "elasticsearch") ? "" : "root";',
);

View File

@@ -63,6 +63,12 @@ import { resolveConnectionSecretDraft } from "../utils/connectionSecretDraft";
import { getCustomConnectionDsnValidationMessage } from "../utils/customConnectionDsn";
import { mergeParsedUriValuesForForm } from "../utils/connectionUriMerge";
import { buildRpcConnectionConfig } from "../utils/connectionRpcConfig";
import {
CONNECTION_TYPE_GROUPS,
getAllConnectionTypeCatalogItems,
getConnectionTypeDefaultPort as getDefaultPortByType,
getConnectionTypeHint,
} from "../utils/connectionTypeCatalog";
import { CUSTOM_CONNECTION_DRIVER_HELP } from "../utils/driverImportGuidance";
import {
describeUnsupportedOceanBaseProtocol,
@@ -230,58 +236,6 @@ const resolveInitialSecretFieldValue = (
}
};
const getDefaultPortByType = (type: string) => {
switch (type) {
case "jvm":
return 9010;
case "mysql":
return 3306;
case "oceanbase":
return 2881;
case "doris":
case "diros":
case "starrocks":
return 9030;
case "sphinx":
return 9306;
case "clickhouse":
return 9000;
case "postgres":
case "opengauss":
return 5432;
case "redis":
return 6379;
case "tdengine":
return 6041;
case "oracle":
return 1521;
case "dameng":
return 5236;
case "kingbase":
return 54321;
case "sqlserver":
return 1433;
case "iris":
return 1972;
case "mongodb":
return 27017;
case "elasticsearch":
return 9200;
case "highgo":
return 5866;
case "mariadb":
return 3306;
case "vastbase":
return 5432;
case "sqlite":
return 0;
case "duckdb":
return 0;
default:
return 3306;
}
};
const singleHostUriSchemesByType: Record<string, string[]> = {
postgres: ["postgresql", "postgres"],
opengauss: ["opengauss", "jdbc:opengauss", "postgresql", "postgres"],
@@ -4018,176 +3972,19 @@ const ConnectionModal: React.FC<{
const driverStatusChecking =
hasCurrentDriverType && !driverStatusLoaded && step === 2;
const dbTypeGroups = [
{
label: "关系型数据库",
items: [
{
key: "mysql",
name: "MySQL",
icon: getDbIcon("mysql", undefined, 36),
},
{
key: "mariadb",
name: "MariaDB",
icon: getDbIcon("mariadb", undefined, 36),
},
{
key: "diros",
name: "Doris",
icon: getDbIcon("diros", undefined, 36),
},
{
key: "starrocks",
name: "StarRocks",
icon: getDbIcon("starrocks", undefined, 36),
},
{
key: "sphinx",
name: "Sphinx",
icon: getDbIcon("sphinx", undefined, 36),
},
{
key: "clickhouse",
name: "ClickHouse",
icon: getDbIcon("clickhouse", undefined, 36),
},
{
key: "postgres",
name: "PostgreSQL",
icon: getDbIcon("postgres", undefined, 36),
},
{
key: "sqlserver",
name: "SQL Server",
icon: getDbIcon("sqlserver", undefined, 36),
},
{
key: "iris",
name: "InterSystems IRIS",
icon: getDbIcon("iris", undefined, 36),
},
{
key: "sqlite",
name: "SQLite",
icon: getDbIcon("sqlite", undefined, 36),
},
{
key: "duckdb",
name: "DuckDB",
icon: getDbIcon("duckdb", undefined, 36),
},
{
key: "oracle",
name: "Oracle",
icon: getDbIcon("oracle", undefined, 36),
},
],
},
{
label: "国产数据库",
items: [
{
key: "oceanbase",
name: "OceanBase",
icon: getDbIcon("oceanbase", undefined, 36),
},
{
key: "dameng",
name: "Dameng (达梦)",
icon: getDbIcon("dameng", undefined, 36),
},
{
key: "kingbase",
name: "Kingbase (人大金仓)",
icon: getDbIcon("kingbase", undefined, 36),
},
{
key: "highgo",
name: "HighGo (瀚高)",
icon: getDbIcon("highgo", undefined, 36),
},
{
key: "vastbase",
name: "Vastbase (海量)",
icon: getDbIcon("vastbase", undefined, 36),
},
{
key: "opengauss",
name: "OpenGauss",
icon: getDbIcon("opengauss", undefined, 36),
},
],
},
{
label: "NoSQL",
items: [
{
key: "mongodb",
name: "MongoDB",
icon: getDbIcon("mongodb", undefined, 36),
},
{
key: "redis",
name: "Redis",
icon: getDbIcon("redis", undefined, 36),
},
{
key: "elasticsearch",
name: "Elasticsearch",
icon: getDbIcon("elasticsearch", undefined, 36),
},
],
},
{
label: "时序数据库",
items: [
{
key: "tdengine",
name: "TDengine",
icon: getDbIcon("tdengine", undefined, 36),
},
],
},
{
label: "其他",
items: [
{
key: "jvm",
name: "JVM Runtime",
icon: getDbIcon("jvm", undefined, 36),
},
{
key: "custom",
name: "Custom (自定义)",
icon: getDbIcon("custom", undefined, 36),
},
],
},
];
const dbTypeGroups = useMemo(
() =>
CONNECTION_TYPE_GROUPS.map((group) => ({
...group,
items: group.items.map((item) => ({
...item,
icon: getDbIcon(item.key, undefined, 36),
})),
})),
[],
);
const dbTypes = dbTypeGroups.flatMap((g) => g.items);
const getDbTypeHint = (type: string) => {
switch (type) {
case "jvm":
return "JMX / Endpoint / Agent";
case "custom":
return "自定义驱动与 DSN";
case "redis":
return "单机 / 集群";
case "mongodb":
return "单机 / 副本集";
case "elasticsearch":
return "支持索引浏览、Mapping 检查、JSON DSL 和 query_string 查询";
case "oceanbase":
return "MySQL / Oracle 租户";
case "sqlite":
case "duckdb":
return "本地文件连接";
default:
return "标准连接配置";
}
};
const dbTypes = getAllConnectionTypeCatalogItems();
const renderStep1 = () => (
<div
@@ -4344,7 +4141,7 @@ const ConnectionModal: React.FC<{
{item.name}
</Text>
<Text type="secondary" style={{ fontSize: 12 }}>
{getDbTypeHint(item.key)}
{getConnectionTypeHint(item.key)}
</Text>
</div>
</Card>

View File

@@ -0,0 +1,53 @@
import { describe, expect, it } from 'vitest';
import {
CONNECTION_TYPE_GROUPS,
getAllConnectionTypeCatalogItems,
getConnectionTypeDefaultPort,
getConnectionTypeHint,
} from './connectionTypeCatalog';
describe('connectionTypeCatalog', () => {
it('keeps supported connection types grouped for the creation modal', () => {
expect(CONNECTION_TYPE_GROUPS.map((group) => group.label)).toEqual([
'关系型数据库',
'国产数据库',
'NoSQL',
'时序数据库',
'其他',
]);
const keys = getAllConnectionTypeCatalogItems().map((item) => item.key);
expect(keys).toContain('mysql');
expect(keys).toContain('oceanbase');
expect(keys).toContain('mongodb');
expect(keys).toContain('redis');
expect(keys).toContain('elasticsearch');
expect(keys).toContain('jvm');
expect(keys).toContain('custom');
expect(new Set(keys).size).toBe(keys.length);
});
it('returns the existing default port mapping for supported connection types', () => {
expect(getConnectionTypeDefaultPort('mysql')).toBe(3306);
expect(getConnectionTypeDefaultPort('oceanbase')).toBe(2881);
expect(getConnectionTypeDefaultPort('diros')).toBe(9030);
expect(getConnectionTypeDefaultPort('postgres')).toBe(5432);
expect(getConnectionTypeDefaultPort('redis')).toBe(6379);
expect(getConnectionTypeDefaultPort('oracle')).toBe(1521);
expect(getConnectionTypeDefaultPort('mongodb')).toBe(27017);
expect(getConnectionTypeDefaultPort('elasticsearch')).toBe(9200);
expect(getConnectionTypeDefaultPort('sqlite')).toBe(0);
expect(getConnectionTypeDefaultPort('duckdb')).toBe(0);
expect(getConnectionTypeDefaultPort('unknown')).toBe(3306);
});
it('keeps concise localized hints for special connection types', () => {
expect(getConnectionTypeHint('redis')).toBe('单机 / 集群');
expect(getConnectionTypeHint('mongodb')).toBe('单机 / 副本集');
expect(getConnectionTypeHint('elasticsearch')).toContain('Mapping');
expect(getConnectionTypeHint('oceanbase')).toBe('MySQL / Oracle 租户');
expect(getConnectionTypeHint('duckdb')).toBe('本地文件连接');
expect(getConnectionTypeHint('mysql')).toBe('标准连接配置');
});
});

View File

@@ -0,0 +1,137 @@
export type ConnectionTypeCatalogItem = {
key: string;
name: string;
};
export type ConnectionTypeCatalogGroup = {
label: string;
items: ConnectionTypeCatalogItem[];
};
export const CONNECTION_TYPE_GROUPS: ConnectionTypeCatalogGroup[] = [
{
label: '关系型数据库',
items: [
{ key: 'mysql', name: 'MySQL' },
{ key: 'mariadb', name: 'MariaDB' },
{ key: 'diros', name: 'Doris' },
{ key: 'starrocks', name: 'StarRocks' },
{ key: 'sphinx', name: 'Sphinx' },
{ key: 'clickhouse', name: 'ClickHouse' },
{ key: 'postgres', name: 'PostgreSQL' },
{ key: 'sqlserver', name: 'SQL Server' },
{ key: 'iris', name: 'InterSystems IRIS' },
{ key: 'sqlite', name: 'SQLite' },
{ key: 'duckdb', name: 'DuckDB' },
{ key: 'oracle', name: 'Oracle' },
],
},
{
label: '国产数据库',
items: [
{ key: 'oceanbase', name: 'OceanBase' },
{ key: 'dameng', name: 'Dameng (达梦)' },
{ key: 'kingbase', name: 'Kingbase (人大金仓)' },
{ key: 'highgo', name: 'HighGo (瀚高)' },
{ key: 'vastbase', name: 'Vastbase (海量)' },
{ key: 'opengauss', name: 'OpenGauss' },
],
},
{
label: 'NoSQL',
items: [
{ key: 'mongodb', name: 'MongoDB' },
{ key: 'redis', name: 'Redis' },
{ key: 'elasticsearch', name: 'Elasticsearch' },
],
},
{
label: '时序数据库',
items: [
{ key: 'tdengine', name: 'TDengine' },
],
},
{
label: '其他',
items: [
{ key: 'jvm', name: 'JVM Runtime' },
{ key: 'custom', name: 'Custom (自定义)' },
],
},
];
export const getConnectionTypeDefaultPort = (type: string): number => {
switch (String(type || '').trim().toLowerCase()) {
case 'jvm':
return 9010;
case 'mysql':
return 3306;
case 'oceanbase':
return 2881;
case 'doris':
case 'diros':
case 'starrocks':
return 9030;
case 'sphinx':
return 9306;
case 'clickhouse':
return 9000;
case 'postgres':
case 'opengauss':
return 5432;
case 'redis':
return 6379;
case 'tdengine':
return 6041;
case 'oracle':
return 1521;
case 'dameng':
return 5236;
case 'kingbase':
return 54321;
case 'sqlserver':
return 1433;
case 'iris':
return 1972;
case 'mongodb':
return 27017;
case 'elasticsearch':
return 9200;
case 'highgo':
return 5866;
case 'mariadb':
return 3306;
case 'vastbase':
return 5432;
case 'sqlite':
case 'duckdb':
return 0;
default:
return 3306;
}
};
export const getConnectionTypeHint = (type: string): string => {
switch (String(type || '').trim().toLowerCase()) {
case 'jvm':
return 'JMX / Endpoint / Agent';
case 'custom':
return '自定义驱动与 DSN';
case 'redis':
return '单机 / 集群';
case 'mongodb':
return '单机 / 副本集';
case 'elasticsearch':
return '支持索引浏览、Mapping 检查、JSON DSL 和 query_string 查询';
case 'oceanbase':
return 'MySQL / Oracle 租户';
case 'sqlite':
case 'duckdb':
return '本地文件连接';
default:
return '标准连接配置';
}
};
export const getAllConnectionTypeCatalogItems = (): ConnectionTypeCatalogItem[] =>
CONNECTION_TYPE_GROUPS.flatMap((group) => group.items);