feat(dx): [WIP] auto-generate correctly typed client for any API route 🎉
This commit is contained in:
parent
01ed8cfb12
commit
30493ade84
|
@ -9,6 +9,7 @@ import { humanSize, truncate } from '~/utils/filters'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import { sortBy } from 'lodash-es'
|
import { sortBy } from 'lodash-es'
|
||||||
import { useStore } from '~/store'
|
import { useStore } from '~/store'
|
||||||
|
import { useClient } from '~/ui/composables/useClient'
|
||||||
|
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
|
|
||||||
|
@ -41,6 +42,7 @@ const props = withDefaults(defineProps<Props>(), {
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const store = useStore()
|
const store = useStore()
|
||||||
|
const { get, post } = useClient('libraries')
|
||||||
|
|
||||||
const upload = ref()
|
const upload = ref()
|
||||||
const currentTab = ref('uploads')
|
const currentTab = ref('uploads')
|
||||||
|
@ -80,6 +82,23 @@ const options = {
|
||||||
|
|
||||||
const privacyLevel = defineModel<keyof typeof options>({ required: true })
|
const privacyLevel = defineModel<keyof typeof options>({ required: true })
|
||||||
|
|
||||||
|
// New implementation with `useClient`:
|
||||||
|
|
||||||
|
watch(privacyLevel, (newValue) =>
|
||||||
|
get({
|
||||||
|
query: {
|
||||||
|
privacy_level: newValue,
|
||||||
|
scope: 'me'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then((data) =>
|
||||||
|
library.value = data?.results.find(({name}) => name === privacyLevel.value)
|
||||||
|
),
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
// Old implementation:
|
||||||
|
/*
|
||||||
const library = ref<Library>()
|
const library = ref<Library>()
|
||||||
watch(privacyLevel, async(newValue) => { try {
|
watch(privacyLevel, async(newValue) => { try {
|
||||||
const response = await axios.get<paths['/api/v2/libraries/']['get']['responses']['200']['content']['application/json']>('libraries/', {
|
const response = await axios.get<paths['/api/v2/libraries/']['get']['responses']['200']['content']['application/json']>('libraries/', {
|
||||||
|
@ -93,6 +112,7 @@ watch(privacyLevel, async(newValue) => { try {
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
useErrorHandler(error as Error)
|
useErrorHandler(error as Error)
|
||||||
}}, { immediate: true })
|
}}, { immediate: true })
|
||||||
|
*/
|
||||||
|
|
||||||
//
|
//
|
||||||
// File counts
|
// File counts
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
import { type paths } from '~/generated/types.ts'
|
||||||
|
import { type Simplify } from 'type-fest'
|
||||||
|
|
||||||
|
import axios from 'axios'
|
||||||
|
import useErrorHandler from '~/composables/useErrorHandler'
|
||||||
|
|
||||||
|
const prefix = "/api/v2/" as const;
|
||||||
|
type Prefix = typeof prefix;
|
||||||
|
|
||||||
|
type RemovePrefixAndSuffix<T extends string> =
|
||||||
|
T extends `${Prefix}${infer Infix}/` ? Infix : never
|
||||||
|
|
||||||
|
type Path = RemovePrefixAndSuffix<Simplify<keyof paths>>
|
||||||
|
|
||||||
|
type Get<TPath extends Path> = paths[`${Prefix}${TPath}/`]['get']
|
||||||
|
type Post<TPath extends Path> = paths[`${Prefix}${TPath}/`]['post']
|
||||||
|
|
||||||
|
type GetRequestParameters<TPath extends Path> =
|
||||||
|
Get<TPath> extends { parameters: any }
|
||||||
|
? Get<TPath>['parameters']
|
||||||
|
: never
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 200
|
||||||
|
*/
|
||||||
|
type OK<TPath extends Path> =
|
||||||
|
Get<TPath> extends { responses: { ['200'] : { content: { ['application/json']: any } } } }
|
||||||
|
? Get<TPath>['responses']['200']['content']['application/json']
|
||||||
|
: never
|
||||||
|
|
||||||
|
type PostRequestJson<TPath extends Path> =
|
||||||
|
Post<TPath> extends { requestBody: { content: { ['application/json']: any } } }
|
||||||
|
? Post<TPath>['requestBody']['content']['application/json']
|
||||||
|
: never
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 201
|
||||||
|
*/
|
||||||
|
type Created<TPath extends Path> =
|
||||||
|
Post<TPath> extends { responses: { ['201']: { content: { ['application/json']: any } } } }
|
||||||
|
? Post<TPath>['responses']['201']['content']['application/json']
|
||||||
|
: never
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Returns `get` and `post` clients for the chosen path (endpoint).
|
||||||
|
```ts
|
||||||
|
const { get, post } = useClient('manage/tags');
|
||||||
|
|
||||||
|
const result0 = post({ name: 'test' })
|
||||||
|
const result1 = get({ query: { q: 'test' } })
|
||||||
|
```
|
||||||
|
*
|
||||||
|
* @param path The path to create a client for. Check the `paths` type in '~/generated/types.ts' to find all available paths.
|
||||||
|
*/
|
||||||
|
export const useClient = <TPath extends Path>( path: TPath ) => ({
|
||||||
|
|
||||||
|
get: async (parameters: GetRequestParameters<TPath>) =>
|
||||||
|
await axios.get<OK<TPath>>(`${prefix}${path}/`, {
|
||||||
|
params: parameters.query || parameters
|
||||||
|
})
|
||||||
|
.then(({ data }) => { return data })
|
||||||
|
.catch (useErrorHandler),
|
||||||
|
|
||||||
|
post: async (requestBody: PostRequestJson<TPath> ) =>
|
||||||
|
await axios.post<Created<TPath>>(`${prefix}${path}/`, {
|
||||||
|
params: requestBody
|
||||||
|
})
|
||||||
|
.then(({ data }) => { return data })
|
||||||
|
.catch (useErrorHandler)
|
||||||
|
|
||||||
|
// TODO: Add put, patch, delete, head, options, trace
|
||||||
|
|
||||||
|
})
|
Loading…
Reference in New Issue