mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-07-12 16:02:48 +08:00
✨ feat(jvm): 落地 JVM 连接契约与配置归一化
- 新增 JVM 连接配置与共享 DTO,补齐 JMX 和 Endpoint 契约 - 实现后端归一化规则,支持默认只读、模式回退和 JMX 端口兜底 - 新增前端 JVM 默认值与配置构建工具,统一模式环境和端口收敛 - 补充 Go 与 Vitest 用例并更新需求追踪,记录 Task 1 验证证据
This commit is contained in:
61
frontend/src/utils/jvmConnectionConfig.test.ts
Normal file
61
frontend/src/utils/jvmConnectionConfig.test.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { buildDefaultJVMConnectionValues, buildJVMConnectionConfig } from './jvmConnectionConfig';
|
||||
|
||||
describe('jvmConnectionConfig', () => {
|
||||
it('defaults to readonly jmx mode', () => {
|
||||
const values = buildDefaultJVMConnectionValues();
|
||||
expect(values.type).toBe('jvm');
|
||||
expect(values.jvmReadOnly).toBe(true);
|
||||
expect(values.jvmAllowedModes).toEqual(['jmx']);
|
||||
expect(values.jvmPreferredMode).toBe('jmx');
|
||||
});
|
||||
|
||||
it('builds nested jvm config payload', () => {
|
||||
const config = buildJVMConnectionConfig({
|
||||
name: 'Orders JVM',
|
||||
type: 'jvm',
|
||||
host: 'orders.internal',
|
||||
port: 9010,
|
||||
jvmReadOnly: true,
|
||||
jvmAllowedModes: ['jmx', 'endpoint'],
|
||||
jvmPreferredMode: 'endpoint',
|
||||
jvmEnvironment: 'prod',
|
||||
jvmEndpointEnabled: true,
|
||||
jvmEndpointBaseUrl: 'https://orders.internal/manage/jvm',
|
||||
jvmEndpointApiKey: 'token-1',
|
||||
});
|
||||
expect(config.jvm?.preferredMode).toBe('endpoint');
|
||||
expect(config.jvm?.endpoint?.baseUrl).toBe('https://orders.internal/manage/jvm');
|
||||
});
|
||||
|
||||
it('normalizes allowed modes and falls back preferred mode to first allowed mode', () => {
|
||||
const config = buildJVMConnectionConfig({
|
||||
host: 'cache.internal',
|
||||
port: 9010,
|
||||
jvmAllowedModes: [' Endpoint ', 'invalid', 'JMX', 'endpoint'],
|
||||
jvmPreferredMode: 'AGENT',
|
||||
});
|
||||
|
||||
expect(config.jvm?.allowedModes).toEqual(['endpoint', 'jmx']);
|
||||
expect(config.jvm?.preferredMode).toBe('endpoint');
|
||||
expect(config.jvm?.jmx?.enabled).toBe(true);
|
||||
});
|
||||
|
||||
it('normalizes environment and port defaults when input is invalid', () => {
|
||||
const config = buildJVMConnectionConfig({
|
||||
host: 'orders.internal',
|
||||
port: 0,
|
||||
jvmJmxPort: '',
|
||||
jvmEnvironment: ' PROD ',
|
||||
jvmReadOnly: false,
|
||||
jvmAllowedModes: ['JMX'],
|
||||
jvmPreferredMode: 'jmx',
|
||||
});
|
||||
|
||||
expect(config.port).toBe(9010);
|
||||
expect(config.jvm?.jmx?.port).toBe(9010);
|
||||
expect(config.jvm?.environment).toBe('prod');
|
||||
expect(config.jvm?.readOnly).toBe(false);
|
||||
});
|
||||
});
|
||||
123
frontend/src/utils/jvmConnectionConfig.ts
Normal file
123
frontend/src/utils/jvmConnectionConfig.ts
Normal file
@@ -0,0 +1,123 @@
|
||||
import type { ConnectionConfig } from '../types';
|
||||
|
||||
const DEFAULT_JMX_PORT = 9010;
|
||||
const DEFAULT_TIMEOUT_SECONDS = 30;
|
||||
const DEFAULT_ENVIRONMENT = 'dev';
|
||||
const JVM_MODES = ['jmx', 'endpoint', 'agent'] as const;
|
||||
|
||||
type JVMMode = typeof JVM_MODES[number];
|
||||
type JVMEnvironment = 'dev' | 'uat' | 'prod';
|
||||
type JVMConnectionFormValues = Record<string, unknown>;
|
||||
|
||||
const isJVMMode = (value: string): value is JVMMode => JVM_MODES.includes(value as JVMMode);
|
||||
|
||||
const toStringValue = (value: unknown): string => {
|
||||
if (typeof value === 'string') {
|
||||
return value.trim();
|
||||
}
|
||||
if (typeof value === 'number' || typeof value === 'boolean') {
|
||||
return String(value).trim();
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
const toInteger = (value: unknown, fallback: number): number => {
|
||||
if (value === undefined || value === null || value === '') {
|
||||
return fallback;
|
||||
}
|
||||
const parsed = Number(value);
|
||||
if (!Number.isFinite(parsed)) {
|
||||
return fallback;
|
||||
}
|
||||
const intValue = Math.trunc(parsed);
|
||||
return intValue > 0 ? intValue : fallback;
|
||||
};
|
||||
|
||||
const normalizeModes = (value: unknown): JVMMode[] => {
|
||||
if (!Array.isArray(value)) {
|
||||
return ['jmx'];
|
||||
}
|
||||
|
||||
const result: JVMMode[] = [];
|
||||
const seen = new Set<JVMMode>();
|
||||
for (const item of value) {
|
||||
const mode = toStringValue(item).toLowerCase();
|
||||
if (!isJVMMode(mode) || seen.has(mode)) {
|
||||
continue;
|
||||
}
|
||||
seen.add(mode);
|
||||
result.push(mode);
|
||||
}
|
||||
return result.length > 0 ? result : ['jmx'];
|
||||
};
|
||||
|
||||
const normalizePreferredMode = (value: unknown, allowedModes: JVMMode[]): JVMMode => {
|
||||
const preferred = toStringValue(value).toLowerCase();
|
||||
if (isJVMMode(preferred) && allowedModes.includes(preferred)) {
|
||||
return preferred;
|
||||
}
|
||||
return allowedModes[0];
|
||||
};
|
||||
|
||||
const normalizeEnvironment = (value: unknown): JVMEnvironment => {
|
||||
const env = toStringValue(value).toLowerCase();
|
||||
if (env === 'uat' || env === 'prod') {
|
||||
return env;
|
||||
}
|
||||
return DEFAULT_ENVIRONMENT;
|
||||
};
|
||||
|
||||
const normalizeReadOnly = (value: unknown): boolean => {
|
||||
if (typeof value === 'boolean') {
|
||||
return value;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
export const buildDefaultJVMConnectionValues = () => ({
|
||||
type: 'jvm',
|
||||
host: 'localhost',
|
||||
port: DEFAULT_JMX_PORT,
|
||||
jvmReadOnly: true,
|
||||
jvmAllowedModes: ['jmx'],
|
||||
jvmPreferredMode: 'jmx',
|
||||
jvmEnvironment: DEFAULT_ENVIRONMENT,
|
||||
jvmEndpointEnabled: false,
|
||||
jvmEndpointBaseUrl: '',
|
||||
jvmEndpointApiKey: '',
|
||||
});
|
||||
|
||||
export const buildJVMConnectionConfig = (values: JVMConnectionFormValues): ConnectionConfig => {
|
||||
const allowedModes = normalizeModes(values.jvmAllowedModes);
|
||||
const preferredMode = normalizePreferredMode(values.jvmPreferredMode, allowedModes);
|
||||
const port = toInteger(values.port, DEFAULT_JMX_PORT);
|
||||
const timeout = toInteger(values.timeout, DEFAULT_TIMEOUT_SECONDS);
|
||||
|
||||
return {
|
||||
type: 'jvm',
|
||||
host: toStringValue(values.host),
|
||||
port,
|
||||
user: '',
|
||||
password: '',
|
||||
timeout,
|
||||
jvm: {
|
||||
environment: normalizeEnvironment(values.jvmEnvironment),
|
||||
readOnly: normalizeReadOnly(values.jvmReadOnly),
|
||||
allowedModes,
|
||||
preferredMode,
|
||||
jmx: {
|
||||
enabled: allowedModes.includes('jmx'),
|
||||
host: toStringValue(values.jvmJmxHost) || toStringValue(values.host),
|
||||
port: toInteger(values.jvmJmxPort, port),
|
||||
username: toStringValue(values.jvmJmxUsername),
|
||||
password: toStringValue(values.jvmJmxPassword),
|
||||
},
|
||||
endpoint: {
|
||||
enabled: values.jvmEndpointEnabled === true,
|
||||
baseUrl: toStringValue(values.jvmEndpointBaseUrl),
|
||||
apiKey: toStringValue(values.jvmEndpointApiKey),
|
||||
timeoutSeconds: toInteger(values.jvmEndpointTimeoutSeconds, timeout),
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user