Skip to content
Snippets Groups Projects
Commit 5e04f0e6 authored by Konrad Mohrfeldt's avatar Konrad Mohrfeldt :koala:
Browse files

feat: add REST API pinia store abstration

parent 563df0e3
No related branches found
No related tags found
No related merge requests found
import { defineStore } from 'pinia'
import { computed, ref } from 'vue'
type ID = number | string
class APIError extends Error {
constructor(message: string) {
super(message)
}
}
class APIResponseError extends APIError {
response: Response
constructor(message: string, response: Response) {
super(message)
this.response = response
}
}
type APIStoreOptions = {
getRequestDefaults: () => RequestInit
}
function createURLBuilder(basepath: string, useTrailingSlash = true) { function createURLBuilder(basepath: string, useTrailingSlash = true) {
// Strip all trailing slashes from the basepath. // Strip all trailing slashes from the basepath.
// We handle slashes when building URLs. // We handle slashes when building URLs.
...@@ -19,3 +43,113 @@ function createURLBuilder(basepath: string, useTrailingSlash = true) { ...@@ -19,3 +43,113 @@ function createURLBuilder(basepath: string, useTrailingSlash = true) {
export const createTankURL = createURLBuilder(import.meta.env.VUE_APP_API_TANK, false) export const createTankURL = createURLBuilder(import.meta.env.VUE_APP_API_TANK, false)
export const createSteeringURL = createURLBuilder(import.meta.env.VUE_APP_API_STEERING) export const createSteeringURL = createURLBuilder(import.meta.env.VUE_APP_API_STEERING)
export function createAPIStore<T extends { id: ID }>(
storeId: string,
endpoint: string,
options?: APIStoreOptions,
) {
return defineStore(storeId, () => {
const itemMap = ref<Map<ID, T>>(new Map())
const items = computed<T[]>(() => Array.from(itemMap.value.values()))
const currentItemId = ref<ID>()
const currentItem = computed(() => items.value.find((item) => item.id === currentItemId.value))
const error = ref<Error>()
function maybeRaiseResponse(response: Response) {
if (!response.ok) {
const _error = new APIResponseError(
`Failure response when executing when interacting with ${response.url}`,
response,
)
error.value = _error
throw _error
}
}
function createRequest(
url: string,
customRequestData: RequestInit | undefined,
defaultRequestData?: RequestInit | undefined,
) {
return new Request(url, {
...(defaultRequestData ?? {}),
...(options?.getRequestDefaults?.() ?? {}),
...(customRequestData ?? {}),
})
}
async function list(requestInit?: RequestInit): Promise<T[]> {
const res = await fetch(createRequest(endpoint, requestInit))
maybeRaiseResponse(res)
const _items: T[] = await res.json()
for (const item of _items) {
itemMap.value.set(item.id, item)
}
return _items
}
async function retrieve(id: ID, requestInit?: RequestInit): Promise<T | null> {
const res = await fetch(createRequest(`${endpoint}/${id}`, requestInit))
if (res.status === 404) {
itemMap.value.delete(id)
return null
} else {
maybeRaiseResponse(res)
const obj: T = await res.json()
itemMap.value.set(obj.id, obj)
return obj
}
}
async function update(
id: ID,
data: Partial<T> | FormData,
requestInit?: RequestInit,
): Promise<T> {
const res = await fetch(
createRequest(`${endpoint}/${id}`, requestInit, {
method: 'PUT',
headers: data instanceof FormData ? undefined : { 'Content-Type': 'application/json' },
body: data instanceof FormData ? data : JSON.stringify(data),
}),
)
maybeRaiseResponse(res)
const obj: T = await res.json()
itemMap.value.set(obj.id, obj)
return obj
}
async function create(data: Partial<T> | FormData, requestInit?: RequestInit): Promise<T> {
const res = await fetch(
createRequest(endpoint, requestInit, {
method: 'POST',
headers: data instanceof FormData ? undefined : { 'Content-Type': 'application/json' },
body: data instanceof FormData ? data : JSON.stringify(data),
}),
)
maybeRaiseResponse(res)
const obj = await res.json()
itemMap.value.set(obj.id, obj)
return obj
}
async function remove(id: ID, requestInit?: RequestInit): Promise<void> {
const res = await fetch(createRequest(`${endpoint}/${id}`, requestInit, { method: 'DELETE' }))
maybeRaiseResponse(res)
itemMap.value.delete(id)
}
return {
items,
currentItemId,
currentItem,
error,
list,
retrieve,
update,
create,
remove,
}
})
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment