Contract-first API with DTOs, validation, and server-side OpenAPI post-processing
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:
@@ -1,12 +1,6 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { api } from '../client';
|
||||
import type {
|
||||
SearchRequest,
|
||||
ExecutionStats,
|
||||
ExecutionSummary,
|
||||
StatsTimeseries,
|
||||
ExecutionDetail,
|
||||
} from '../schema-types';
|
||||
import type { SearchRequest } from '../types';
|
||||
|
||||
export function useExecutionStats(timeFrom: string | undefined, timeTo: string | undefined) {
|
||||
return useQuery({
|
||||
@@ -21,7 +15,7 @@ export function useExecutionStats(timeFrom: string | undefined, timeTo: string |
|
||||
},
|
||||
});
|
||||
if (error) throw new Error('Failed to load stats');
|
||||
return data as unknown as ExecutionStats;
|
||||
return data!;
|
||||
},
|
||||
enabled: !!timeFrom,
|
||||
placeholderData: (prev) => prev,
|
||||
@@ -37,7 +31,7 @@ export function useSearchExecutions(filters: SearchRequest, live = false) {
|
||||
body: filters,
|
||||
});
|
||||
if (error) throw new Error('Search failed');
|
||||
return data as unknown as { data: ExecutionSummary[]; total: number; offset: number; limit: number };
|
||||
return data!;
|
||||
},
|
||||
placeholderData: (prev) => prev,
|
||||
refetchInterval: live ? 5_000 : false,
|
||||
@@ -58,7 +52,7 @@ export function useStatsTimeseries(timeFrom: string | undefined, timeTo: string
|
||||
},
|
||||
});
|
||||
if (error) throw new Error('Failed to load timeseries');
|
||||
return data as unknown as StatsTimeseries;
|
||||
return data!;
|
||||
},
|
||||
enabled: !!timeFrom,
|
||||
placeholderData: (prev) => prev,
|
||||
@@ -74,7 +68,7 @@ export function useExecutionDetail(executionId: string | null) {
|
||||
params: { path: { executionId: executionId! } },
|
||||
});
|
||||
if (error) throw new Error('Failed to load execution detail');
|
||||
return data as unknown as ExecutionDetail;
|
||||
return data!;
|
||||
},
|
||||
enabled: !!executionId,
|
||||
});
|
||||
@@ -96,7 +90,7 @@ export function useProcessorSnapshot(
|
||||
},
|
||||
);
|
||||
if (error) throw new Error('Failed to load snapshot');
|
||||
return data as unknown as Record<string, string>;
|
||||
return data!;
|
||||
},
|
||||
enabled: !!executionId && index !== null,
|
||||
});
|
||||
|
||||
@@ -1,80 +1,47 @@
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import { config } from '../../config';
|
||||
import { useAuthStore } from '../../auth/auth-store';
|
||||
|
||||
export interface OidcConfigResponse {
|
||||
configured: boolean;
|
||||
enabled?: boolean;
|
||||
issuerUri?: string;
|
||||
clientId?: string;
|
||||
clientSecretSet?: boolean;
|
||||
rolesClaim?: string;
|
||||
defaultRoles?: string[];
|
||||
autoSignup?: boolean;
|
||||
}
|
||||
|
||||
export interface OidcConfigRequest {
|
||||
enabled: boolean;
|
||||
issuerUri: string;
|
||||
clientId: string;
|
||||
clientSecret: string;
|
||||
rolesClaim: string;
|
||||
defaultRoles: string[];
|
||||
autoSignup: boolean;
|
||||
}
|
||||
|
||||
interface TestResult {
|
||||
status: string;
|
||||
authorizationEndpoint: string;
|
||||
}
|
||||
|
||||
async function adminFetch<T>(path: string, options?: RequestInit): Promise<T> {
|
||||
const token = useAuthStore.getState().accessToken;
|
||||
const res = await fetch(`${config.apiBaseUrl}${path}`, {
|
||||
...options,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...(token ? { Authorization: `Bearer ${token}` } : {}),
|
||||
...options?.headers,
|
||||
},
|
||||
});
|
||||
if (res.status === 204) return undefined as T;
|
||||
const body = await res.json();
|
||||
if (!res.ok) throw new Error(body.message || `Request failed (${res.status})`);
|
||||
return body as T;
|
||||
}
|
||||
import { api } from '../client';
|
||||
import type { OidcAdminConfigRequest } from '../types';
|
||||
|
||||
export function useOidcConfig() {
|
||||
return useQuery<OidcConfigResponse>({
|
||||
return useQuery({
|
||||
queryKey: ['admin', 'oidc'],
|
||||
queryFn: () => adminFetch('/admin/oidc'),
|
||||
queryFn: async () => {
|
||||
const { data, error } = await api.GET('/admin/oidc');
|
||||
if (error) throw new Error('Failed to load OIDC config');
|
||||
return data!;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useSaveOidcConfig() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (data: OidcConfigRequest) =>
|
||||
adminFetch<OidcConfigResponse>('/admin/oidc', {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify(data),
|
||||
}),
|
||||
mutationFn: async (body: OidcAdminConfigRequest) => {
|
||||
const { data, error } = await api.PUT('/admin/oidc', { body });
|
||||
if (error) throw new Error('Failed to save OIDC config');
|
||||
return data!;
|
||||
},
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: ['admin', 'oidc'] }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useTestOidcConnection() {
|
||||
return useMutation({
|
||||
mutationFn: () =>
|
||||
adminFetch<TestResult>('/admin/oidc/test', { method: 'POST' }),
|
||||
mutationFn: async () => {
|
||||
const { data, error } = await api.POST('/admin/oidc/test');
|
||||
if (error) throw new Error('OIDC test failed');
|
||||
return data!;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useDeleteOidcConfig() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: () =>
|
||||
adminFetch<void>('/admin/oidc', { method: 'DELETE' }),
|
||||
mutationFn: async () => {
|
||||
const { error } = await api.DELETE('/admin/oidc');
|
||||
if (error) throw new Error('Failed to delete OIDC config');
|
||||
},
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: ['admin', 'oidc'] }),
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user