Contract-first API with DTOs, validation, and server-side OpenAPI post-processing
All checks were successful
CI / build (push) Successful in 1m27s
CI / docker (push) Successful in 2m6s
CI / deploy (push) Successful in 30s

Add dedicated request/response DTOs for all controllers, replacing raw
JsonNode parameters with validated types. Move OpenAPI path-prefix stripping
and ProcessorNode children injection into OpenApiCustomizer beans so the
spec served at /api/v1/api-docs is already clean — eliminating the need for
the ui/scripts/process-openapi.mjs post-processing script.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-03-14 15:33:37 +01:00
parent 50bb22d6f6
commit 465f210aee
43 changed files with 1561 additions and 509 deletions

404
ui/src/api/schema.d.ts vendored
View File

@@ -127,6 +127,7 @@ export interface paths {
};
get?: never;
put?: never;
/** Refresh access token */
post: operations["refresh"];
delete?: never;
options?: never;
@@ -143,6 +144,7 @@ export interface paths {
};
get?: never;
put?: never;
/** Exchange OIDC authorization code for JWTs */
post: operations["callback"];
delete?: never;
options?: never;
@@ -159,6 +161,7 @@ export interface paths {
};
get?: never;
put?: never;
/** Login with local credentials */
post: operations["login"];
delete?: never;
options?: never;
@@ -418,6 +421,7 @@ export interface paths {
path?: never;
cookie?: never;
};
/** Get OIDC config for SPA login flow */
get: operations["getConfig_1"];
put?: never;
post?: never;
@@ -509,7 +513,8 @@ export interface components {
RolesRequest: {
roles?: string[];
};
OidcConfigRequest: {
/** @description OIDC configuration update request */
OidcAdminConfigRequest: {
enabled?: boolean;
issuerUri?: string;
clientId?: string;
@@ -518,6 +523,21 @@ export interface components {
defaultRoles?: string[];
autoSignup?: boolean;
};
/** @description Error response */
ErrorResponse: {
message: string;
};
/** @description OIDC configuration for admin management */
OidcAdminConfigResponse: {
configured?: boolean;
enabled?: boolean;
issuerUri?: string;
clientId?: string;
clientSecretSet?: boolean;
rolesClaim?: string;
defaultRoles?: string[];
autoSignup?: boolean;
};
SearchRequest: {
status?: string;
/** Format: date-time */
@@ -542,32 +562,37 @@ export interface components {
limit?: number;
};
ExecutionSummary: {
executionId?: string;
routeId?: string;
agentId?: string;
status?: string;
executionId: string;
routeId: string;
agentId: string;
status: string;
/** Format: date-time */
startTime?: string;
startTime: string;
/** Format: date-time */
endTime?: string;
endTime: string;
/** Format: int64 */
durationMs?: number;
correlationId?: string;
errorMessage?: string;
diagramContentHash?: string;
durationMs: number;
correlationId: string;
errorMessage: string;
diagramContentHash: string;
};
SearchResultExecutionSummary: {
data?: components["schemas"]["ExecutionSummary"][];
data: components["schemas"]["ExecutionSummary"][];
/** Format: int64 */
total?: number;
total: number;
/** Format: int32 */
offset?: number;
offset: number;
/** Format: int32 */
limit?: number;
limit: number;
};
RefreshRequest: {
refreshToken?: string;
};
/** @description JWT token pair */
AuthTokenResponse: {
accessToken: string;
refreshToken: string;
};
CallbackRequest: {
code?: string;
redirectUri?: string;
@@ -576,89 +601,190 @@ export interface components {
username?: string;
password?: string;
};
/** @description Agent token refresh request */
AgentRefreshRequest: {
refreshToken: string;
};
/** @description Refreshed access token */
AgentRefreshResponse: {
accessToken: string;
};
/** @description Command to send to agent(s) */
CommandRequest: {
/** @description Command type: config-update, deep-trace, or replay */
type: string;
/** @description Command payload JSON */
payload?: Record<string, never>;
};
/** @description Result of sending a command to a single agent */
CommandSingleResponse: {
commandId: string;
status: string;
};
/** @description Agent registration payload */
AgentRegistrationRequest: {
agentId: string;
name: string;
/** @default default */
group: string;
version?: string;
routeIds?: string[];
capabilities?: {
[key: string]: Record<string, never>;
};
};
/** @description Agent registration result with JWT tokens and SSE endpoint */
AgentRegistrationResponse: {
agentId: string;
sseEndpoint: string;
/** Format: int64 */
heartbeatIntervalMs?: number;
serverPublicKey: string;
accessToken: string;
refreshToken: string;
};
/** @description Result of broadcasting a command to multiple agents */
CommandBroadcastResponse: {
commandIds: string[];
/** Format: int32 */
targetCount?: number;
};
/** @description OIDC provider connectivity test result */
OidcTestResult: {
status: string;
authorizationEndpoint: string;
};
ExecutionStats: {
/** Format: int64 */
totalCount?: number;
totalCount: number;
/** Format: int64 */
failedCount?: number;
failedCount: number;
/** Format: int64 */
avgDurationMs?: number;
avgDurationMs: number;
/** Format: int64 */
p99LatencyMs?: number;
p99LatencyMs: number;
/** Format: int64 */
activeCount?: number;
activeCount: number;
/** Format: int64 */
totalToday?: number;
totalToday: number;
/** Format: int64 */
prevTotalCount?: number;
prevTotalCount: number;
/** Format: int64 */
prevFailedCount?: number;
prevFailedCount: number;
/** Format: int64 */
prevAvgDurationMs?: number;
prevAvgDurationMs: number;
/** Format: int64 */
prevP99LatencyMs?: number;
prevP99LatencyMs: number;
};
StatsTimeseries: {
buckets?: components["schemas"]["TimeseriesBucket"][];
buckets: components["schemas"]["TimeseriesBucket"][];
};
TimeseriesBucket: {
/** Format: date-time */
time?: string;
time: string;
/** Format: int64 */
totalCount?: number;
totalCount: number;
/** Format: int64 */
failedCount?: number;
failedCount: number;
/** Format: int64 */
avgDurationMs?: number;
avgDurationMs: number;
/** Format: int64 */
p99DurationMs?: number;
p99DurationMs: number;
/** Format: int64 */
activeCount?: number;
activeCount: number;
};
ExecutionDetail: {
executionId?: string;
routeId?: string;
agentId?: string;
status?: string;
executionId: string;
routeId: string;
agentId: string;
status: string;
/** Format: date-time */
startTime?: string;
startTime: string;
/** Format: date-time */
endTime?: string;
endTime: string;
/** Format: int64 */
durationMs?: number;
correlationId?: string;
exchangeId?: string;
errorMessage?: string;
errorStackTrace?: string;
diagramContentHash?: string;
processors?: components["schemas"]["ProcessorNode"][];
durationMs: number;
correlationId: string;
exchangeId: string;
errorMessage: string;
errorStackTrace: string;
diagramContentHash: string;
processors: components["schemas"]["ProcessorNode"][];
};
ProcessorNode: {
processorId?: string;
processorType?: string;
status?: string;
processorId: string;
processorType: string;
status: string;
/** Format: date-time */
startTime?: string;
startTime: string;
/** Format: date-time */
endTime?: string;
endTime: string;
/** Format: int64 */
durationMs?: number;
diagramNodeId?: string;
errorMessage?: string;
errorStackTrace?: string;
durationMs: number;
diagramNodeId: string;
errorMessage: string;
errorStackTrace: string;
children: components["schemas"]["ProcessorNode"][];
};
DiagramLayout: {
/** Format: double */
width?: number;
/** Format: double */
height?: number;
nodes?: components["schemas"]["PositionedNode"][];
edges?: components["schemas"]["PositionedEdge"][];
};
PositionedEdge: {
sourceId?: string;
targetId?: string;
label?: string;
points?: number[][];
};
PositionedNode: {
id?: string;
label?: string;
type?: string;
/** Format: double */
x?: number;
/** Format: double */
y?: number;
/** Format: double */
width?: number;
/** Format: double */
height?: number;
};
/** @description OIDC configuration for SPA login flow */
OidcPublicConfigResponse: {
issuer: string;
clientId: string;
authorizationEndpoint: string;
/** @description Present if the provider supports RP-initiated logout */
endSessionEndpoint?: string;
};
/** @description Agent instance summary */
AgentInstanceResponse: {
id: string;
name: string;
group: string;
status: string;
routeIds: string[];
/** Format: date-time */
registeredAt: string;
/** Format: date-time */
lastHeartbeat: string;
};
SseEmitter: {
/** Format: int64 */
timeout?: number;
};
UserInfo: {
userId?: string;
provider?: string;
email?: string;
displayName?: string;
roles?: string[];
userId: string;
provider: string;
email: string;
displayName: string;
roles: string[];
/** Format: date-time */
createdAt?: string;
createdAt: string;
};
};
responses: never;
@@ -715,7 +841,7 @@ export interface operations {
[name: string]: unknown;
};
content: {
"*/*": Record<string, never>;
"*/*": components["schemas"]["OidcAdminConfigResponse"];
};
};
};
@@ -729,7 +855,7 @@ export interface operations {
};
requestBody: {
content: {
"application/json": components["schemas"]["OidcConfigRequest"];
"application/json": components["schemas"]["OidcAdminConfigRequest"];
};
};
responses: {
@@ -739,7 +865,7 @@ export interface operations {
[name: string]: unknown;
};
content: {
"*/*": Record<string, never>;
"*/*": components["schemas"]["OidcAdminConfigResponse"];
};
};
/** @description Invalid configuration */
@@ -748,7 +874,7 @@ export interface operations {
[name: string]: unknown;
};
content: {
"*/*": Record<string, never>;
"*/*": components["schemas"]["ErrorResponse"];
};
};
};
@@ -926,13 +1052,22 @@ export interface operations {
};
};
responses: {
/** @description OK */
/** @description Token refreshed */
200: {
headers: {
[name: string]: unknown;
};
content: {
"*/*": Record<string, never>;
"*/*": components["schemas"]["AuthTokenResponse"];
};
};
/** @description Invalid refresh token */
401: {
headers: {
[name: string]: unknown;
};
content: {
"*/*": components["schemas"]["ErrorResponse"];
};
};
};
@@ -950,13 +1085,40 @@ export interface operations {
};
};
responses: {
/** @description OK */
/** @description Authentication successful */
200: {
headers: {
[name: string]: unknown;
};
content: {
"*/*": Record<string, never>;
"*/*": components["schemas"]["AuthTokenResponse"];
};
};
/** @description OIDC authentication failed */
401: {
headers: {
[name: string]: unknown;
};
content: {
"*/*": components["schemas"]["ErrorResponse"];
};
};
/** @description Account not provisioned */
403: {
headers: {
[name: string]: unknown;
};
content: {
"*/*": components["schemas"]["ErrorResponse"];
};
};
/** @description OIDC not configured or disabled */
404: {
headers: {
[name: string]: unknown;
};
content: {
"*/*": components["schemas"]["AuthTokenResponse"];
};
};
};
@@ -974,13 +1136,22 @@ export interface operations {
};
};
responses: {
/** @description OK */
/** @description Login successful */
200: {
headers: {
[name: string]: unknown;
};
content: {
"*/*": Record<string, never>;
"*/*": components["schemas"]["AuthTokenResponse"];
};
};
/** @description Invalid credentials */
401: {
headers: {
[name: string]: unknown;
};
content: {
"*/*": components["schemas"]["ErrorResponse"];
};
};
};
@@ -996,7 +1167,7 @@ export interface operations {
};
requestBody: {
content: {
"application/json": string;
"application/json": components["schemas"]["AgentRefreshRequest"];
};
};
responses: {
@@ -1006,7 +1177,7 @@ export interface operations {
[name: string]: unknown;
};
content: {
"*/*": string;
"*/*": components["schemas"]["AgentRefreshResponse"];
};
};
/** @description Invalid or expired refresh token */
@@ -1015,7 +1186,7 @@ export interface operations {
[name: string]: unknown;
};
content: {
"*/*": string;
"*/*": components["schemas"]["AgentRefreshResponse"];
};
};
/** @description Agent not found */
@@ -1024,7 +1195,7 @@ export interface operations {
[name: string]: unknown;
};
content: {
"*/*": string;
"*/*": components["schemas"]["AgentRefreshResponse"];
};
};
};
@@ -1067,7 +1238,7 @@ export interface operations {
};
requestBody: {
content: {
"application/json": string;
"application/json": components["schemas"]["CommandRequest"];
};
};
responses: {
@@ -1077,7 +1248,7 @@ export interface operations {
[name: string]: unknown;
};
content: {
"*/*": string;
"*/*": components["schemas"]["CommandSingleResponse"];
};
};
/** @description Invalid command payload */
@@ -1086,7 +1257,7 @@ export interface operations {
[name: string]: unknown;
};
content: {
"*/*": string;
"*/*": components["schemas"]["CommandSingleResponse"];
};
};
/** @description Agent not registered */
@@ -1095,7 +1266,7 @@ export interface operations {
[name: string]: unknown;
};
content: {
"*/*": string;
"*/*": components["schemas"]["CommandSingleResponse"];
};
};
};
@@ -1137,7 +1308,7 @@ export interface operations {
};
requestBody: {
content: {
"application/json": string;
"application/json": components["schemas"]["AgentRegistrationRequest"];
};
};
responses: {
@@ -1147,7 +1318,7 @@ export interface operations {
[name: string]: unknown;
};
content: {
"*/*": string;
"*/*": components["schemas"]["AgentRegistrationResponse"];
};
};
/** @description Invalid registration payload */
@@ -1156,7 +1327,7 @@ export interface operations {
[name: string]: unknown;
};
content: {
"*/*": string;
"*/*": components["schemas"]["ErrorResponse"];
};
};
/** @description Missing or invalid bootstrap token */
@@ -1165,7 +1336,7 @@ export interface operations {
[name: string]: unknown;
};
content: {
"*/*": string;
"*/*": components["schemas"]["AgentRegistrationResponse"];
};
};
};
@@ -1181,7 +1352,7 @@ export interface operations {
};
requestBody: {
content: {
"application/json": string;
"application/json": components["schemas"]["CommandRequest"];
};
};
responses: {
@@ -1191,7 +1362,7 @@ export interface operations {
[name: string]: unknown;
};
content: {
"*/*": string;
"*/*": components["schemas"]["CommandBroadcastResponse"];
};
};
/** @description Invalid command payload */
@@ -1200,7 +1371,7 @@ export interface operations {
[name: string]: unknown;
};
content: {
"*/*": string;
"*/*": components["schemas"]["CommandBroadcastResponse"];
};
};
};
@@ -1214,7 +1385,7 @@ export interface operations {
};
requestBody: {
content: {
"application/json": string;
"application/json": components["schemas"]["CommandRequest"];
};
};
responses: {
@@ -1224,7 +1395,7 @@ export interface operations {
[name: string]: unknown;
};
content: {
"*/*": string;
"*/*": components["schemas"]["CommandBroadcastResponse"];
};
};
/** @description Invalid command payload */
@@ -1233,7 +1404,7 @@ export interface operations {
[name: string]: unknown;
};
content: {
"*/*": string;
"*/*": components["schemas"]["CommandBroadcastResponse"];
};
};
};
@@ -1253,7 +1424,7 @@ export interface operations {
[name: string]: unknown;
};
content: {
"*/*": Record<string, never>;
"*/*": components["schemas"]["OidcTestResult"];
};
};
/** @description Provider unreachable or misconfigured */
@@ -1262,7 +1433,7 @@ export interface operations {
[name: string]: unknown;
};
content: {
"*/*": Record<string, never>;
"*/*": components["schemas"]["ErrorResponse"];
};
};
};
@@ -1325,7 +1496,7 @@ export interface operations {
};
requestBody?: never;
responses: {
/** @description OK */
/** @description Execution detail found */
200: {
headers: {
[name: string]: unknown;
@@ -1334,6 +1505,15 @@ export interface operations {
"*/*": components["schemas"]["ExecutionDetail"];
};
};
/** @description Execution not found */
404: {
headers: {
[name: string]: unknown;
};
content: {
"*/*": components["schemas"]["ExecutionDetail"];
};
};
};
};
getProcessorSnapshot: {
@@ -1348,7 +1528,7 @@ export interface operations {
};
requestBody?: never;
responses: {
/** @description OK */
/** @description Snapshot data */
200: {
headers: {
[name: string]: unknown;
@@ -1359,6 +1539,17 @@ export interface operations {
};
};
};
/** @description Snapshot not found */
404: {
headers: {
[name: string]: unknown;
};
content: {
"*/*": {
[key: string]: string;
};
};
};
};
};
renderDiagram: {
@@ -1378,7 +1569,8 @@ export interface operations {
[name: string]: unknown;
};
content: {
"*/*": Record<string, never>;
"image/svg+xml": string;
"application/json": components["schemas"]["DiagramLayout"];
};
};
/** @description Diagram not found */
@@ -1401,13 +1593,31 @@ export interface operations {
};
requestBody?: never;
responses: {
/** @description OK */
/** @description OIDC configuration */
200: {
headers: {
[name: string]: unknown;
};
content: {
"*/*": Record<string, never>;
"*/*": components["schemas"]["OidcPublicConfigResponse"];
};
};
/** @description OIDC not configured or disabled */
404: {
headers: {
[name: string]: unknown;
};
content: {
"*/*": components["schemas"]["OidcPublicConfigResponse"];
};
};
/** @description Failed to retrieve OIDC provider metadata */
500: {
headers: {
[name: string]: unknown;
};
content: {
"*/*": components["schemas"]["ErrorResponse"];
};
};
};
@@ -1429,7 +1639,7 @@ export interface operations {
[name: string]: unknown;
};
content: {
"*/*": string;
"*/*": components["schemas"]["AgentInstanceResponse"][];
};
};
/** @description Invalid status filter */
@@ -1438,7 +1648,7 @@ export interface operations {
[name: string]: unknown;
};
content: {
"*/*": string;
"*/*": components["schemas"]["ErrorResponse"];
};
};
};