Align frontend type contracts with normalized data

This commit is contained in:
rcourtman 2026-04-23 20:50:03 +01:00
parent 46ba8c6ef8
commit f919a24186
8 changed files with 54 additions and 15 deletions

View file

@ -93,6 +93,11 @@ export interface ConnectionSystem {
export interface ConnectionsListResponse {
connections: Connection[];
systems: ConnectionSystem[];
}
interface ConnectionsListWireResponse {
connections?: Connection[];
systems?: ConnectionSystem[];
}
@ -116,7 +121,7 @@ export class ConnectionsAPI {
private static readonly baseUrl = '/api/connections';
static async list(): Promise<ConnectionsListResponse> {
const response: ConnectionsListResponse = await apiFetchJSON(this.baseUrl);
const response: ConnectionsListWireResponse = await apiFetchJSON(this.baseUrl);
return {
connections: response.connections ?? [],
systems: response.systems ?? [],

View file

@ -17,11 +17,17 @@ export interface OrganizationInvitation {
invitedBy: string;
}
export interface OrganizationAccessMutationResult {
kind: 'member' | 'invitation';
member?: OrganizationMember;
invitation?: OrganizationInvitation;
}
export type OrganizationAccessMutationResult =
| {
kind: 'member';
member: OrganizationMember;
invitation?: never;
}
| {
kind: 'invitation';
invitation: OrganizationInvitation;
member?: never;
};
export interface UserOrganizationInvitation extends OrganizationInvitation {
orgId: string;

View file

@ -36,7 +36,8 @@ function buildParams(
savingHideLocalLogin: () => false,
handleHideLocalLoginChange: vi.fn(async () => undefined),
versionInfo: () => null,
getInfrastructurePanelProps: () => ({}),
getInfrastructurePanelProps: () =>
({} as ReturnType<UseSettingsPanelRegistryParams['getInfrastructurePanelProps']>),
systemPanels,
};
}

View file

@ -3,6 +3,10 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import type { DiagnosticsData } from '@/components/Settings/diagnosticsModel';
type UseDiagnosticsPanelStateModule = typeof import('../useDiagnosticsPanelState');
type URLStaticWithBlobMethods = {
createObjectURL?: typeof URL.createObjectURL;
revokeObjectURL?: typeof URL.revokeObjectURL;
};
const createDiagnosticsData = (): DiagnosticsData => ({
version: '6.0.0',
@ -135,7 +139,7 @@ describe('useDiagnosticsPanelState', () => {
value: originalCreateObjectURL,
});
} else {
delete (URL as URL & { createObjectURL?: typeof URL.createObjectURL }).createObjectURL;
delete (URL as unknown as URLStaticWithBlobMethods).createObjectURL;
}
if (originalRevokeObjectURL) {
Object.defineProperty(URL, 'revokeObjectURL', {
@ -143,7 +147,7 @@ describe('useDiagnosticsPanelState', () => {
value: originalRevokeObjectURL,
});
} else {
delete (URL as URL & { revokeObjectURL?: typeof URL.revokeObjectURL }).revokeObjectURL;
delete (URL as unknown as URLStaticWithBlobMethods).revokeObjectURL;
}
vi.restoreAllMocks();
vi.resetModules();

View file

@ -51,10 +51,14 @@ const mountHook = (
resources: () => resources,
discoverySettings: {
discoveryEnabled: () => false,
setDiscoveryEnabled: () => false,
discoverySubnet: () => 'auto',
discoveryMode: () => 'auto',
discoverySubnetDraft: () => '',
lastCustomSubnet: () => '',
discoverySubnetError: () => undefined,
savingDiscoverySettings: () => false,
setSavingDiscoverySettings: () => false,
setDiscoveryMode: () => 'auto',
setDiscoverySubnetDraft: () => '',
setDiscoverySubnetError: () => undefined,
@ -63,7 +67,9 @@ const mountHook = (
normalizeSubnetList: (value: string) => value,
isValidCIDR: () => true,
currentDraftSubnetValue: () => '',
applySavedDiscoverySubnet: () => undefined,
assignDiscoverySubnetInputRef: () => undefined,
getDiscoverySubnetInputRef: () => undefined,
} as Parameters<typeof useSettingsInfrastructurePanelProps>[0]['discoverySettings'],
systemSettings: {
envOverrides: () => ({}),

View file

@ -42,6 +42,20 @@ export interface DiscoveryDiagnostic {
lastResultTimestamp?: string;
lastResultServers?: number;
lastResultErrors?: number;
history?: DiscoveryHistoryDiagnostic[];
}
export interface DiscoveryHistoryDiagnostic {
startedAt?: string;
completedAt?: string;
duration?: string;
durationMs?: number;
subnet?: string;
serverCount?: number;
errorCount?: number;
blocklistLength?: number;
status?: string;
[key: string]: unknown;
}
export interface APITokenDiagnostic {
@ -246,11 +260,8 @@ export function sanitizeDiagnosticsData(raw: DiagnosticsData): DiagnosticsData {
subnetBlocklist: data.discovery.subnetBlocklist?.map(() => '[REDACTED_SUBNET]'),
};
const discovery = data.discovery as DiscoveryDiagnostic & {
history?: Array<Record<string, unknown>>;
};
if (Array.isArray(discovery.history)) {
discovery.history = discovery.history.map((historyEntry) => ({
if (Array.isArray(data.discovery.history)) {
data.discovery.history = data.discovery.history.map((historyEntry) => ({
...historyEntry,
subnet: '[REDACTED_SUBNET]',
}));

View file

@ -125,7 +125,9 @@ export function useOrganizationAccessPanelState(props: OrganizationAccessPanelPr
const result = await OrgsAPI.inviteMember(currentOrg.id, { userId, role });
if (result.kind === 'invitation') {
notificationStore.success(getOrganizationAccessInvitationSentMessage(userId, role));
notificationStore.success(
getOrganizationAccessInvitationSentMessage(userId, result.invitation.role),
);
} else {
notificationStore.success(getOrganizationAccessMemberAddedMessage(userId, role));
}

View file

@ -207,6 +207,10 @@ describe('shared storage override migration', () => {
displayName: 'ceph-pool',
type: 'storage',
platformId: 'Main',
platformType: 'proxmox-pve',
sourceType: 'api',
status: 'online',
lastSeen: Date.now(),
proxmox: {
instance: 'Main',
node: 'cluster',