Fix http error handling in the client service

This commit is contained in:
Andras Schmelczer 2025-12-06 22:00:54 +00:00
commit d979963f86

View file

@ -40,6 +40,21 @@ export class SyncService {
this.pingClient = unboundFetch; this.pingClient = unboundFetch;
} }
private static async errorFromResponse(
response: Response
): Promise<string> {
if (
response.headers
.get("Content-Type")
?.includes("application/json") == true
) {
const result: SerializedError =
(await response.json()) as SerializedError; // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion
return SyncService.formatError(result);
}
return `HTTP ${response.status}: ${response.statusText}`;
}
private static formatError(error: SerializedError): string { private static formatError(error: SerializedError): string {
let result = error.message; let result = error.message;
if (error.causes.length > 0) { if (error.causes.length > 0) {
@ -80,17 +95,17 @@ export class SyncService {
headers: this.getDefaultHeaders() headers: this.getDefaultHeaders()
}); });
const result: SerializedError | DocumentVersionWithoutContent = if (!response.ok) {
(await response.json()) as // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion
| SerializedError
| DocumentVersionWithoutContent;
if ("errorType" in result) {
throw new Error( throw new Error(
`Failed to create document: ${SyncService.formatError(result)}` `Failed to create document: ${await SyncService.errorFromResponse(
response
)}`
); );
} }
const result: DocumentVersionWithoutContent =
(await response.json()) as DocumentVersionWithoutContent; // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion
this.logger.debug(`Created document ${JSON.stringify(result)}`); this.logger.debug(`Created document ${JSON.stringify(result)}`);
return result; return result;
@ -128,17 +143,17 @@ export class SyncService {
} }
); );
const result: SerializedError | DocumentUpdateResponse = if (!response.ok) {
(await response.json()) as // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion
| SerializedError
| DocumentUpdateResponse;
if ("errorType" in result) {
throw new Error( throw new Error(
`Failed to update document: ${SyncService.formatError(result)}` `Failed to update document: ${await SyncService.errorFromResponse(
response
)}`
); );
} }
const result: DocumentUpdateResponse =
(await response.json()) as DocumentUpdateResponse; // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion
this.logger.debug( this.logger.debug(
`Updated document ${JSON.stringify(result)} with id ${ `Updated document ${JSON.stringify(result)} with id ${
result.documentId result.documentId
@ -181,17 +196,17 @@ export class SyncService {
} }
); );
const result: SerializedError | DocumentUpdateResponse = if (!response.ok) {
(await response.json()) as // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion
| SerializedError
| DocumentUpdateResponse;
if ("errorType" in result) {
throw new Error( throw new Error(
`Failed to update document: ${SyncService.formatError(result)}` `Failed to update document: ${await SyncService.errorFromResponse(
response
)}`
); );
} }
const result: DocumentUpdateResponse =
(await response.json()) as DocumentUpdateResponse; // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion
this.logger.debug( this.logger.debug(
`Updated document ${JSON.stringify(result)} with id ${ `Updated document ${JSON.stringify(result)} with id ${
result.documentId result.documentId
@ -227,17 +242,17 @@ export class SyncService {
} }
); );
const result: SerializedError | DocumentVersionWithoutContent = if (!response.ok) {
(await response.json()) as // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion
| SerializedError
| DocumentVersionWithoutContent;
if ("errorType" in result) {
throw new Error( throw new Error(
`Failed to delete document: ${SyncService.formatError(result)}` `Failed to delete document: ${await SyncService.errorFromResponse(
response
)}`
); );
} }
const result: DocumentVersionWithoutContent =
(await response.json()) as DocumentVersionWithoutContent; // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion
this.logger.debug( this.logger.debug(
`Deleted document ${relativePath} with id ${documentId}` `Deleted document ${relativePath} with id ${documentId}`
); );
@ -261,15 +276,17 @@ export class SyncService {
} }
); );
const result: SerializedError | DocumentVersion = if (!response.ok) {
(await response.json()) as SerializedError | DocumentVersion; // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion
if ("errorType" in result) {
throw new Error( throw new Error(
`Failed to get document: ${SyncService.formatError(result)}` `Failed to get document: ${await SyncService.errorFromResponse(
response
)}`
); );
} }
const result: DocumentVersion =
(await response.json()) as DocumentVersion; // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion
this.logger.debug(`Got document ${JSON.stringify(result)}`); this.logger.debug(`Got document ${JSON.stringify(result)}`);
return result; return result;
@ -297,20 +314,19 @@ export class SyncService {
} }
); );
if (response.ok) { if (!response.ok) {
const result = await response.bytes(); throw new Error(
this.logger.debug( `Failed to get document: ${await SyncService.errorFromResponse(
`Got document version content for document ${documentId} version ${vaultUpdateId}` response
)}`
); );
return result;
} }
const result: SerializedError = const result = await response.bytes();
(await response.json()) as SerializedError; // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion this.logger.debug(
`Got document version content for document ${documentId} version ${vaultUpdateId}`
throw new Error(
`Failed to get document: ${SyncService.formatError(result)}`
); );
return result;
}); });
} }
@ -331,17 +347,17 @@ export class SyncService {
headers: this.getDefaultHeaders() headers: this.getDefaultHeaders()
}); });
const result: SerializedError | FetchLatestDocumentsResponse = if (!response.ok) {
(await response.json()) as // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion
| SerializedError
| FetchLatestDocumentsResponse;
if ("errorType" in result) {
throw new Error( throw new Error(
`Failed to get documents: ${SyncService.formatError(result)}` `Failed to get documents: ${await SyncService.errorFromResponse(
response
)}`
); );
} }
const result: FetchLatestDocumentsResponse =
(await response.json()) as FetchLatestDocumentsResponse; // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion
this.logger.debug( this.logger.debug(
`Got ${result.latestDocuments.length} document metadata` `Got ${result.latestDocuments.length} document metadata`
); );
@ -355,15 +371,17 @@ export class SyncService {
const response = await this.pingClient(this.getUrl("/ping"), { const response = await this.pingClient(this.getUrl("/ping"), {
headers: this.getDefaultHeaders() headers: this.getDefaultHeaders()
}); });
const result: PingResponse | SerializedError =
(await response.json()) as PingResponse | SerializedError; // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion
if ("errorType" in result) { if (!response.ok) {
throw new Error( throw new Error(
`Failed to ping server: ${SyncService.formatError(result)}` `Failed to ping server: ${await SyncService.errorFromResponse(
response
)}`
); );
} }
const result: PingResponse = (await response.json()) as PingResponse; // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion
this.logger.debug( this.logger.debug(
`Pinged server, got response: ${JSON.stringify(result)}` `Pinged server, got response: ${JSON.stringify(result)}`
); );