Improve e2e flow for cloudzero

This commit is contained in:
yuneng-jiang 2025-12-19 13:04:42 -08:00
parent 026c2ad693
commit e92db2d718
6 changed files with 71 additions and 23 deletions

View file

@ -17,19 +17,35 @@ const getCloudZeroSettings = async (accessToken: string): Promise<CloudZeroSetti
},
});
if (response.status === 404) {
// 404 means no settings are configured - this is expected and not an error
return null;
}
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
const errorMessage =
errorData?.error?.message || errorData?.message || errorData?.detail || "Failed to fetch CloudZero settings";
let errorMessage = "Failed to fetch CloudZero settings";
try {
const errorData = await response.json();
// Handle different error response formats
if (typeof errorData === "object" && errorData !== null) {
errorMessage =
errorData?.error?.message ||
errorData?.error ||
errorData?.message ||
errorData?.detail ||
(typeof errorData?.error === "string" ? errorData.error : errorMessage);
} else if (typeof errorData === "string") {
errorMessage = errorData;
}
} catch {
// If JSON parsing fails, use the status text
errorMessage = response.statusText || errorMessage;
}
throw new Error(errorMessage);
}
const data = await response.json();
// Check if settings are actually configured (all required fields are present)
if (!data || (!data.api_key_masked && !data.connection_id)) {
return null;
}
return data;
};
@ -77,9 +93,22 @@ const updateCloudZeroSettings = async (accessToken: string, params: UpdateParams
});
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
const errorMessage =
errorData?.error?.message || errorData?.message || errorData?.detail || "Failed to update CloudZero settings";
let errorMessage = "Failed to update CloudZero settings";
try {
const errorData = await response.json();
if (typeof errorData === "object" && errorData !== null) {
errorMessage =
errorData?.error?.message ||
errorData?.error ||
errorData?.message ||
errorData?.detail ||
(typeof errorData?.error === "string" ? errorData.error : errorMessage);
} else if (typeof errorData === "string") {
errorMessage = errorData;
}
} catch {
errorMessage = response.statusText || errorMessage;
}
throw new Error(errorMessage);
}
@ -117,9 +146,22 @@ const deleteCloudZeroSettings = async (accessToken: string): Promise<DeleteRespo
});
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
const errorMessage =
errorData?.error?.message || errorData?.message || errorData?.detail || "Failed to delete CloudZero settings";
let errorMessage = "Failed to delete CloudZero settings";
try {
const errorData = await response.json();
if (typeof errorData === "object" && errorData !== null) {
errorMessage =
errorData?.error?.message ||
errorData?.error ||
errorData?.message ||
errorData?.detail ||
(typeof errorData?.error === "string" ? errorData.error : errorMessage);
} else if (typeof errorData === "string") {
errorMessage = errorData;
}
} catch {
errorMessage = response.statusText || errorMessage;
}
throw new Error(errorMessage);
}

View file

@ -36,7 +36,9 @@ export default function CloudZeroCostTracking() {
if (error) {
return (
<Card>
<Typography.Text className="text-red-600">Error loading CloudZero settings: {error.message}</Typography.Text>
<Typography.Text className="text-red-600">
Error loading CloudZero settings: {error instanceof Error ? error.message : String(error)}
</Typography.Text>
</Card>
);
}

View file

@ -9,6 +9,6 @@ describe("CloudZeroEmptyPlaceholder", () => {
expect(screen.getByText("No CloudZero Integration Found")).toBeInTheDocument();
expect(screen.getByText(/Connect your CloudZero account/)).toBeInTheDocument();
expect(screen.getByRole("button", { name: "Create Integration" })).toBeInTheDocument();
expect(screen.getByRole("button", { name: "Add CloudZero Integration" })).toBeInTheDocument();
});
});

View file

@ -21,7 +21,7 @@ export default function CloudZeroEmptyPlaceholder({ startCreation }: CloudZeroEm
}
>
<Button type="primary" size="large" onClick={startCreation} className="flex items-center gap-2 mx-auto mt-4">
Create Integration
Add CloudZero Integration
</Button>
</Empty>
</div>

View file

@ -134,10 +134,14 @@ export function CloudZeroIntegrationSettings({ settings, onSettingsUpdated }: Cl
}}
>
<Descriptions.Item label="API Key (Redacted)">
<span className="font-mono text-gray-600">{settings.api_key_masked}</span>
<span className="font-mono text-gray-600">
{settings.api_key_masked || <span className="text-gray-400 italic">Not configured</span>}
</span>
</Descriptions.Item>
<Descriptions.Item label="Connection ID">
<span className="font-mono text-gray-600">{settings.connection_id}</span>
<span className="font-mono text-gray-600">
{settings.connection_id || <span className="text-gray-400 italic">Not configured</span>}
</span>
</Descriptions.Item>
<Descriptions.Item label="Timezone">
{settings.timezone || <span className="text-gray-400 italic">Default (UTC)</span>}

View file

@ -1,6 +1,6 @@
export interface CloudZeroSettings {
api_key_masked: string;
connection_id: string;
timezone?: string;
status?: string;
api_key_masked: string | null;
connection_id: string | null;
timezone?: string | null;
status?: string | null;
}