Proper API error handling

This commit is contained in:
Christian Beutel
2025-01-03 18:05:25 +01:00
parent 9887874ad7
commit 46b5b94219
25 changed files with 366 additions and 251 deletions

View File

@@ -6,10 +6,11 @@ import { deepEqual } from "$lib/util/deep_util";
import { getFileURL } from "$lib/util/file_util";
import * as M from "maplibre-gl";
import type { Hits } from "meilisearch";
import { ClientResponseError, type ListResult } from "pocketbase";
import { type ListResult } from "pocketbase";
import { writable, type Writable } from "svelte/store";
import { summit_logs_create, summit_logs_delete, summit_logs_update } from "./summit_log_store";
import { waypoints_create, waypoints_delete, waypoints_update } from "./waypoint_store";
import { APIError } from "$lib/util/api_util";
let trails: Trail[] = []
export const trail: Writable<Trail> = writable(new Trail(""));
@@ -26,12 +27,14 @@ export async function trails_index(perPage: number = 21, random: boolean = false
})
const response: ListResult<Trail> = await r.json()
if (r.ok) {
trails = response.items
return response.items;
} else {
throw new ClientResponseError(response)
if (!r.ok) {
const response = await r.json();
throw new APIError(r.status, response.message, response.detail)
}
trails = response.items
return response.items;
}
export async function trails_search_filter(filter: TrailFilter, page: number = 1, f: (url: RequestInfo | URL, config?: RequestInit) => Promise<Response> = fetch) {
@@ -42,11 +45,13 @@ export async function trails_search_filter(filter: TrailFilter, page: number = 1
body: JSON.stringify({ q: filter.q, options: { filter: filterText, sort: [`${filter.sort}:${filter.sortOrder == "+" ? "asc" : "desc"}`], hitsPerPage: 12, page: page } }),
});
if (!r.ok) {
const response = await r.json();
throw new APIError(r.status, response.message, response.detail)
}
const result: { page: number, totalPages: number, hits: Hits<Record<string, any>> } = await r.json();
if (!r.ok) {
throw new ClientResponseError(result)
}
const trailIds = result.hits.map((h: Record<string, any>) => h.id);
@@ -61,13 +66,16 @@ export async function trails_search_filter(filter: TrailFilter, page: number = 1
}), {
method: 'GET',
})
if (!r.ok) {
const response = await r.json();
throw new APIError(r.status, response.message, response.detail)
}
const response: ListResult<Trail> = await r.json()
if (r.ok) {
return { items: response.items, ...result };
} else {
throw new ClientResponseError(response)
}
return { items: response.items, ...result };
}
export async function trails_search_bounding_box(northEast: M.LngLat, southWest: M.LngLat, filter?: TrailFilter, loadGPX: boolean = true) {
@@ -109,27 +117,31 @@ export async function trails_search_bounding_box(northEast: M.LngLat, southWest:
}), {
method: 'GET',
})
if (!r.ok) {
const response = await r.json();
throw new APIError(r.status, response.message, response.detail)
}
const response = await r.json()
if (r.ok) {
if (loadGPX) {
for (const trail of response.items) {
const gpxData: string = await fetchGPX(trail);
if (!trail.expand) {
trail.expand = {};
}
trail.expand.gpx_data = gpxData;
if (loadGPX) {
for (const trail of response.items) {
const gpxData: string = await fetchGPX(trail);
if (!trail.expand) {
trail.expand = {};
}
trail.expand.gpx_data = gpxData;
}
const comparison = compareObjectArrays<Trail>(trails, response.items)
trails = response.items;
return { trails: response.items, ...comparison };
} else {
throw new ClientResponseError(response)
}
const comparison = compareObjectArrays<Trail>(trails, response.items)
trails = response.items;
return { trails: response.items, ...comparison };
}
export async function trails_show(id: string, loadGPX?: boolean, f: (url: RequestInfo | URL, config?: RequestInit) => Promise<Response> = fetch) {
@@ -138,12 +150,14 @@ export async function trails_show(id: string, loadGPX?: boolean, f: (url: Reques
}), {
method: 'GET',
})
const response = await r.json()
if (!r.ok) {
throw new ClientResponseError(response)
const response = await r.json();
throw new APIError(r.status, response.message, response.detail)
}
const response = await r.json()
if (loadGPX) {
if (!response.expand) {
response.expand = {}
@@ -175,10 +189,6 @@ export async function trails_show(id: string, loadGPX?: boolean, f: (url: Reques
export async function trails_create(trail: Trail, photos: File[], gpx: File | Blob | null, f: (url: RequestInfo | URL, config?: RequestInit) => Promise<Response> = fetch) {
if (!pb.authStore.model) {
throw new ClientResponseError({ status: 401, response: { message: "Forbidden" } });
}
for (const waypoint of trail.expand?.waypoints ?? []) {
const model = await waypoints_create({
...waypoint,
@@ -199,9 +209,11 @@ export async function trails_create(trail: Trail, photos: File[], gpx: File | Bl
})
if (!r.ok) {
throw new ClientResponseError(await r.json())
const response = await r.json();
throw new APIError(r.status, response.message, response.detail)
}
let model: Trail = await r.json();
const formData = new FormData()
@@ -218,11 +230,13 @@ export async function trails_create(trail: Trail, photos: File[], gpx: File | Bl
body: formData,
})
if (r.ok) {
return await r.json();
} else {
throw new ClientResponseError(await r.json())
if (!r.ok) {
const response = await r.json();
throw new APIError(r.status, response.message, response.detail)
}
return await r.json();
}
export async function trails_update(oldTrail: Trail, newTrail: Trail, photos?: File[], gpx?: File | Blob | null) {
@@ -274,9 +288,11 @@ export async function trails_update(oldTrail: Trail, newTrail: Trail, photos?: F
})
if (!r.ok) {
throw new ClientResponseError(await r.json())
const response = await r.json();
throw new APIError(r.status, response.message, response.detail)
}
let model: Trail = await r.json();
const formData = new FormData()
@@ -303,10 +319,12 @@ export async function trails_update(oldTrail: Trail, newTrail: Trail, photos?: F
})
if (!r.ok) {
throw new ClientResponseError(await r.json())
const response = await r.json();
throw new APIError(r.status, response.message, response.detail)
}
trail.set(model);
return model;
@@ -329,11 +347,14 @@ export async function trails_delete(trail: Trail) {
method: 'DELETE',
})
if (r.ok) {
return await r.json();
} else {
throw new ClientResponseError(await r.json())
if (!r.ok) {
const response = await r.json();
throw new APIError(r.status, response.message, response.detail)
}
return await r.json();
}
export async function trails_get_filter_values(f: (url: RequestInfo | URL, config?: RequestInit) => Promise<Response> = fetch): Promise<TrailFilterValues> {
@@ -341,23 +362,26 @@ export async function trails_get_filter_values(f: (url: RequestInfo | URL, confi
method: 'GET',
})
if (r.ok) {
return await r.json();
} else {
throw new ClientResponseError(await r.json())
if (!r.ok) {
const response = await r.json();
throw new APIError(r.status, response.message, response.detail)
}
return await r.json();
}
export async function trails_get_bounding_box(f: (url: RequestInfo | URL, config?: RequestInit) => Promise<Response> = fetch): Promise<TrailFilterValues> {
const r = await f('/api/v1/trail/bounding-box', {
method: 'GET',
})
if (r.ok) {
return await r.json();
} else {
throw new ClientResponseError(await r.json())
if (!r.ok) {
const response = await r.json();
throw new APIError(r.status, response.message, response.detail)
}
return await r.json();
}
export async function trails_upload(file: File, f: (url: RequestInfo | URL, config?: RequestInit) => Promise<Response> = fetch): Promise<TrailFilterValues> {
@@ -370,12 +394,13 @@ export async function trails_upload(file: File, f: (url: RequestInfo | URL, conf
method: 'PUT',
body: fd
})
if (r.ok) {
return await r.json();
} else {
throw new ClientResponseError(await r.json())
if (!r.ok) {
const response = await r.json();
throw new APIError(r.status, response.message, response.detail)
}
return await r.json();
}
export async function fetchGPX(trail: { gpx?: string } & Record<string, any>, f: (url: RequestInfo | URL, config?: RequestInit) => Promise<Response> = fetch) {