Fix server file workspace null handling

This commit is contained in:
npc0-hue
2026-08-24 14:06:14 +08:00
parent 33d3a13e74
commit 504387c657
6 changed files with 190 additions and 7 deletions
+19
View File
@@ -657,6 +657,25 @@ describe("PlatformApiClient AI providers", () => {
expect(fetchMock).toHaveBeenCalledTimes(44);
});
it("normalizes server file workspace null arrays from older platform responses", async () => {
const fetchMock = vi.fn(async (input: RequestInfo | URL) => {
const url = String(input);
if (url.endsWith("/api/v1/server-instances/server-1/files/workspace")) {
return jsonResponse({ ...serverFileWorkspace, defaultDirectoryKey: "", directories: null, files: null, configFields: null, transfer: { ...serverFileWorkspace.transfer, notes: null } });
}
if (url.endsWith("/api/v1/server-instances/server-1/files/list?directoryKey=scum-config")) {
return jsonResponse({ serverInstanceId: server.id, pluginId: plugin.id, directoryKey: "scum-config", state: "declared", entries: null });
}
throw new Error(`unexpected request: ${url}`);
});
vi.stubGlobal("fetch", fetchMock);
const client = new PlatformApiClient();
await expect(client.getServerFileWorkspace(server.id)).resolves.toMatchObject({ directories: [], files: [], configFields: [], transfer: { notes: [] } });
await expect(client.listServerFiles(server.id, { directoryKey: "scum-config" })).resolves.toMatchObject({ entries: [] });
});
it("calls plugin marketplace endpoints with filter and state contracts", async () => {
const fetchMock = vi.fn(async (input: RequestInfo | URL, init?: RequestInit) => {
const url = String(input);
+11 -3
View File
@@ -585,16 +585,16 @@ export class PlatformApiClient {
}
async getServerFileWorkspace(serverInstanceId: string): Promise<ServerFileWorkspaceResponse> {
return this.request<ServerFileWorkspaceResponse>(`/server-instances/${encodeURIComponent(serverInstanceId)}/files/workspace`);
return normalizeServerFileWorkspace(await this.request<ServerFileWorkspaceResponse>(`/server-instances/${encodeURIComponent(serverInstanceId)}/files/workspace`));
}
async listServerFiles(serverInstanceId: string, request: Partial<ServerFileListRequest> = {}): Promise<ServerFileListResponse> {
const params = serverFileListQuery(request);
return this.request<ServerFileListResponse>(`/server-instances/${encodeURIComponent(serverInstanceId)}/files/list${params}`);
return normalizeServerFileList(await this.request<ServerFileListResponse>(`/server-instances/${encodeURIComponent(serverInstanceId)}/files/list${params}`));
}
async refreshServerFiles(serverInstanceId: string, request: ServerFileListRequest): Promise<ServerFileListResponse> {
return this.request<ServerFileListResponse>(`/server-instances/${encodeURIComponent(serverInstanceId)}/files/refresh`, { method: "POST", body: request });
return normalizeServerFileList(await this.request<ServerFileListResponse>(`/server-instances/${encodeURIComponent(serverInstanceId)}/files/refresh`, { method: "POST", body: request }));
}
async readServerFile(serverInstanceId: string, request: ServerFileReadRequest): Promise<FileOperationDispatchResponse> {
@@ -964,4 +964,12 @@ function serverFileListQuery(request: Partial<ServerFileListRequest>): string {
return query ? `?${query}` : "";
}
function normalizeServerFileWorkspace(response: ServerFileWorkspaceResponse): ServerFileWorkspaceResponse {
return { ...response, directories: response.directories ?? [], files: response.files ?? [], configFields: response.configFields ?? [], transfer: { ...response.transfer, notes: response.transfer.notes ?? [] } };
}
function normalizeServerFileList(response: ServerFileListResponse): ServerFileListResponse {
return { ...response, entries: response.entries ?? [] };
}
export const platformApiClient = new PlatformApiClient(readWebRuntimeEnv().platformApiBaseUrl);