refactor: merge all api calls args in one object

This commit is contained in:
axolotle 2024-08-05 15:36:53 +02:00
parent c34e05d5eb
commit cb344f28fc
31 changed files with 316 additions and 326 deletions

View file

@ -6,39 +6,29 @@
import { useSettings } from '@/composables/useSettings'
import store from '@/store'
import type { Obj } from '@/types/commons'
import { APIUnauthorizedError, type APIError } from './errors'
import { getError, getResponseData, openWebSocket } from './handlers'
export type RequestMethod = 'GET' | 'POST' | 'PUT' | 'DELETE'
type StoreUri = {
uri: string
storeKey?: string
param?: string
}
type HumanKey = {
export type HumanKey = {
key: string
[propName: string]: any
}
type APIQueryOptions = {
// Display the waiting modal
wait?: boolean
// Open a websocket connection
export type APIQuery = {
method?: RequestMethod
uri: string
cachePath?: string
cacheParams?: Obj
data?: Obj
humanKey?: string | HumanKey
showModal?: boolean
websocket?: boolean
// If an error occurs, the dismiss button will trigger a go back in history
initial?: boolean
// Send the data with a body encoded as `"multipart/form-data"` instead of `"x-www-form-urlencoded"`)
asFormData?: boolean
}
export type APIQuery = [
method: RequestMethod,
uri: string | StoreUri,
data?: Obj | null,
humanKey?: string | HumanKey | null,
options?: APIQueryOptions,
]
export type APIErrorData = {
error: string
error_key?: string
@ -54,7 +44,7 @@ export type APIRequest = {
uri: string
humanRouteKey: HumanKey['key']
humanRoute: string
initial: APIQueryOptions['initial']
initial: boolean
status: RequestStatus
}
@ -114,39 +104,42 @@ export default {
/**
* Generic method to fetch the api.
*
* @param method - a method in `'GET' | 'POST' | 'PUT' | 'DELETE'`
* @param uri - URI to fetch
* @param cachePath - Cache path to get or store data
* @param cacheParams - Cache params to get or update data
* @param method - An HTTP method in `'GET' | 'POST' | 'PUT' | 'DELETE'`
* @param data - Data to send as body
* @param options - {@link APIQueryOptions}
* @param humanKey - Key and eventually some data to build the query's description
* @param showModal - Lock view and display the waiting modal
* @param websocket - Open a websocket connection to receive server messages
* @param initial - If an error occurs, the dismiss button will trigger a go back in history
* @param asFormData - Send the data with a body encoded as `"multipart/form-data"` instead of `"x-www-form-urlencoded"`)
*
* @returns Promise that resolve the api response data
* @throws Throw an `APIError` or subclass depending on server response
*/
async fetch(
method: RequestMethod,
uri: string,
data: Obj | null | undefined = {},
humanKey: string | HumanKey | null = null,
{
wait = true,
websocket = true,
initial = false,
asFormData = true,
}: APIQueryOptions = {},
): Promise<Obj | string> {
async fetch<T extends any = Obj | string>({
uri,
method = 'GET',
cachePath = undefined,
cacheParams = undefined,
data = undefined,
humanKey = undefined,
showModal = method !== 'GET',
websocket = method !== 'GET',
initial = false,
asFormData = true,
}: APIQuery): Promise<T> {
const { locale } = useSettings()
// `await` because Vuex actions returns promises by default.
const request: APIRequest | APIRequestAction = await store.dispatch(
'INIT_REQUEST',
{
method,
uri,
humanKey,
initial,
wait,
websocket,
},
)
const request: APIRequest = await store.dispatch('INIT_REQUEST', {
method,
uri,
humanKey,
initial,
wait: showModal,
websocket,
})
if (websocket) {
await openWebSocket(request as APIRequestAction)
@ -173,7 +166,7 @@ export default {
throw getError(request, response, responseData as string | APIErrorData)
}
return responseData
return responseData as T
},
/**
@ -182,30 +175,21 @@ export default {
* Calls are synchronous since the API can't handle multiple calls.
*
* @param queries - Array of {@link APIQuery}
* @param wait - Show the waiting modal until every queries have been resolved
* @param showModal - Show the waiting modal until every queries have been resolved
* @param initial - Inform that thoses queries are required for a view to be displayed
*
* @returns Promise that resolves an array of server responses
* @throws Throw an `APIError` or subclass depending on server response
*/
async fetchAll(queries: APIQuery[], { wait = false, initial = false } = {}) {
async fetchAll(
queries: APIQuery[],
{ showModal = false, initial = false } = {},
) {
const results: Array<Obj | string> = []
if (wait) store.commit('SET_WAITING', true)
try {
for (const [method, uri, data, humanKey, options = {}] of queries) {
if (wait) options.wait = false
if (initial) options.initial = true
results.push(
await this[method.toLowerCase() as 'get' | 'post' | 'put' | 'delete'](
uri,
data,
humanKey,
options,
),
)
}
} finally {
// Stop waiting even if there is an error.
if (wait) store.commit('SET_WAITING', false)
for (const query of queries) {
if (showModal) query.showModal = true
if (initial) query.initial = true
results.push(await this.fetch(query))
}
return results
@ -214,86 +198,57 @@ export default {
/**
* Api get helper function.
*
* @param uri - uri to fetch
* @param data - for convenience in muliple calls, just pass null
* @param humanKey - key and eventually some data to build the query's description
* @param options - {@link APIQueryOptions}
* @param query - a simple string for uri or complete APIQuery object {@link APIQuery}
*
* @returns Promise that resolve the api response data or an error
* @throws Throw an `APIError` or subclass depending on server response
*/
get(
uri: string | StoreUri,
data: Obj | null = null,
humanKey: string | HumanKey | null = null,
options: APIQueryOptions = {},
): Promise<Obj | string> {
options = { websocket: false, wait: false, ...options }
if (typeof uri === 'string')
return this.fetch('GET', uri, data, humanKey, options)
return store.dispatch('GET', { ...uri, humanKey, options })
get<T extends any = Obj | string>(
query: string | Omit<APIQuery, 'method' | 'data'>,
): Promise<T> {
return this.fetch(typeof query === 'string' ? { uri: query } : query)
},
/**
* Api post helper function.
*
* @param uri - uri to fetch
* @param data - data to send as body
* @param humanKey - key and eventually some data to build the query's description
* @param options - {@link APIQueryOptions}
* @param query - {@link APIQuery}
*
* @returns Promise that resolve the api response data or an error
* @throws Throw an `APIError` or subclass depending on server response
*/
post(
uri: string | StoreUri,
data: Obj | null | undefined = {},
humanKey: string | HumanKey | null = null,
options: APIQueryOptions = {},
): Promise<Obj | string> {
if (typeof uri === 'string')
return this.fetch('POST', uri, data, humanKey, options)
return store.dispatch('POST', { ...uri, data, humanKey, options })
post<T extends any = Obj | string>(
query: Omit<APIQuery, 'method'>,
): Promise<T> {
return this.fetch({ ...query, method: 'POST' })
},
/**
* Api put helper function.
*
* @param uri - uri to fetch
* @param data - data to send as body
* @param humanKey - key and eventually some data to build the query's description
* @param options - {@link APIQueryOptions}
* @param query - {@link APIQuery}
*
* @returns Promise that resolve the api response data or an error
* @throws Throw an `APIError` or subclass depending on server response
*/
put(
uri: string | StoreUri,
data: Obj | null | undefined = {},
humanKey: string | HumanKey | null = null,
options: APIQueryOptions = {},
): Promise<Obj | string> {
if (typeof uri === 'string')
return this.fetch('PUT', uri, data, humanKey, options)
return store.dispatch('PUT', { ...uri, data, humanKey, options })
put<T extends any = Obj | string>(
query: Omit<APIQuery, 'method'>,
): Promise<T> {
return this.fetch({ ...query, method: 'PUT' })
},
/**
* Api delete helper function.
*
* @param uri - uri to fetch
* @param data - data to send as body
* @param humanKey - key and eventually some data to build the query's description
* @param options - {@link APIQueryOptions}
* @param query - {@link APIQuery}
*
* @returns Promise that resolve the api response data or an error
* @throws Throw an `APIError` or subclass depending on server response
*/
delete(
uri: string | StoreUri,
data: Obj | null | undefined = {},
humanKey: string | HumanKey | null = null,
options: APIQueryOptions = {},
): Promise<Obj | string> {
if (typeof uri === 'string')
return this.fetch('DELETE', uri, data, humanKey, options)
return store.dispatch('DELETE', { ...uri, data, humanKey, options })
delete<T extends any = Obj | string>(
query: Omit<APIQuery, 'method'>,
): Promise<T> {
return this.fetch({ ...query, method: 'DELETE' })
},
/**
@ -302,6 +257,7 @@ export default {
* @param attemps - Number of attemps before rejecting
* @param delay - Delay between calls to the API in ms
* @param initialDelay - Delay before calling the API for the first time in ms
*
* @returns Promise that resolve yunohost version infos
* @throws Throw an `APIError` or subclass depending on server response
*/
@ -311,8 +267,8 @@ export default {
store
.dispatch('GET_YUNOHOST_INFOS')
.then(resolve)
.catch((err) => {
if (err.name === 'APIUnauthorizedError') {
.catch((err: APIError) => {
if (err instanceof APIUnauthorizedError) {
reject(err)
} else if (n < 1) {
reject(err)
@ -321,7 +277,6 @@ export default {
}
})
}
if (initialDelay > 0) setTimeout(() => reconnect(attemps), initialDelay)
else reconnect(attemps)
})

View file

@ -11,10 +11,10 @@ export function useInitialQueries<
queries: MaybeRefOrGetter<APIQuery[]> | ComputedRef<APIQuery[]>,
{
onQueriesResponse,
wait = false,
showModal = false,
}: {
onQueriesResponse?: (...responses: ResponsesType) => Promise<void> | void
wait?: boolean
showModal?: boolean
} = {},
) {
const loading = ref(true)
@ -24,7 +24,7 @@ export function useInitialQueries<
function call(triggerLoading = true) {
if (triggerLoading) loading.value = true
return api
.fetchAll(toValue(queries), { wait, initial: true })
.fetchAll(toValue(queries), { showModal, initial: true })
.then(async (responses_) => {
responses.value = responses_ as ResponsesType
if (onQueriesResponse) {

View file

@ -114,8 +114,10 @@ async function performPostInstall(force = false) {
// FIXME does the api will throw an error for bad passwords ?
api
.post('postinstall' + (force ? '?force_diskspace' : ''), data, {
key: 'postinstall',
.post({
uri: 'postinstall' + (force ? '?force_diskspace' : ''),
data,
humanKey: { key: 'postinstall' },
})
.then(() => {
// Display success message and allow the user to login

View file

@ -33,7 +33,7 @@ const router = useRouter()
const route = useRoute()
const modalConfirm = useAutoModal()
const { loading } = useInitialQueries(
[['GET', 'apps/catalog?full&with_categories&with_antifeatures']],
[{ uri: 'apps/catalog?full&with_categories&with_antifeatures' }],
{ onQueriesResponse },
)

View file

@ -54,9 +54,9 @@ const externalResults = reactive({})
const v$ = useVuelidate(rules, form, { $externalResults: externalResults })
const { loading, refetch } = useInitialQueries(
[
['GET', `apps/${props.id}?full`],
['GET', { uri: 'users/permissions?full', storeKey: 'permissions' }],
['GET', { uri: 'domains' }],
{ uri: `apps/${props.id}?full` },
{ uri: 'users/permissions?full', cachePath: 'permissions' },
{ uri: 'domains', cachePath: 'domains' },
],
{ onQueriesResponse },
)
@ -228,17 +228,17 @@ async function onQueriesResponse(app_: any) {
const onPanelApply: OnPanelApply = ({ panelId, data, action }, onError) => {
api
.put(
action
.put({
uri: action
? `apps/${props.id}/actions/${action}`
: `apps/${props.id}/config/${panelId}`,
isEmptyValue(data) ? {} : { args: objectToParams(data) },
{
data: isEmptyValue(data) ? {} : { args: objectToParams(data) },
humanKey: {
key: `apps.${action ? 'action' : 'update'}_config`,
id: panelId,
name: props.id,
},
)
})
.then(() => refetch())
.catch(onError)
}
@ -246,10 +246,14 @@ const onPanelApply: OnPanelApply = ({ panelId, data, action }, onError) => {
function changeLabel(permName, data) {
data.show_tile = data.show_tile ? 'True' : 'False'
api
.put('users/permissions/' + permName, data, {
key: 'apps.change_label',
prevName: app.value.label,
nextName: data.label,
.put({
uri: 'users/permissions/' + permName,
data,
humanKey: {
key: 'apps.change_label',
prevName: app.value.label,
nextName: data.label,
},
})
.then(() => refetch(false))
}
@ -260,11 +264,11 @@ async function changeUrl() {
const { domain, path } = form.url
api
.put(
`apps/${props.id}/changeurl`,
{ domain, path: '/' + path },
{ key: 'apps.change_url', name: app.value.label },
)
.put({
uri: `apps/${props.id}/changeurl`,
data: { domain, path: '/' + path },
humanKey: { key: 'apps.change_url', name: app.value.label },
})
.then(() => refetch(false))
}
@ -273,34 +277,33 @@ async function setAsDefaultDomain(undo = false) {
if (!confirmed) return
api
.put(
`apps/${props.id}/default${undo ? '?undo' : ''}`,
{},
{
.put({
uri: `apps/${props.id}/default${undo ? '?undo' : ''}`,
humanKey: {
key: 'apps.set_default',
name: app.value.label,
domain: app.value.domain,
},
)
})
.then(() => refetch(false))
}
async function dismissNotification(name: string) {
api
.put(
`apps/${props.id}/dismiss_notification/${name}`,
{},
{ key: 'apps.dismiss_notification', name: app.value.label },
)
.put({
uri: `apps/${props.id}/dismiss_notification/${name}`,
humanKey: { key: 'apps.dismiss_notification', name: app.value.label },
})
.then(() => refetch(false))
}
async function uninstall() {
const data = purge.value === true ? { purge: 1 } : {}
api
.delete('apps/' + props.id, data, {
key: 'apps.uninstall',
name: app.value.label,
.delete({
uri: 'apps/' + props.id,
data,
humanKey: { key: 'apps.uninstall', name: app.value.label },
})
.then(() => {
router.push({ name: 'app-list' })

View file

@ -37,8 +37,8 @@ const formData = shallowRef<
const { loading } = useInitialQueries(
[
['GET', 'apps/catalog?full&with_categories&with_antifeatures'],
['GET', `apps/manifest?app=${props.id}&with_screenshot`],
{ uri: 'apps/catalog?full&with_categories&with_antifeatures' },
{ uri: `apps/manifest?app=${props.id}&with_screenshot` },
],
{ onQueriesResponse },
)
@ -186,7 +186,11 @@ async function performInstall(onError: (err: APIError) => void) {
}
api
.post('apps', data, { key: 'apps.install', name: app.value.name })
.post({
uri: 'apps',
data,
humanKey: { key: 'apps.install', name: app.value.name },
})
.then(async ({ notifications }) => {
const postInstall = formatAppNotifs(notifications)
if (postInstall) {

View file

@ -5,7 +5,7 @@ import { useInitialQueries } from '@/composables/useInitialQueries'
import { useSearch } from '@/composables/useSearch'
import type { Obj } from '@/types/commons'
const { loading } = useInitialQueries([['GET', 'apps?full']], {
const { loading } = useInitialQueries([{ uri: 'apps?full' }], {
onQueriesResponse,
})

View file

@ -13,10 +13,7 @@ const props = defineProps<{
const { t } = useI18n()
const router = useRouter()
const { loading } = useInitialQueries(
[
['GET', 'hooks/backup'],
['GET', 'apps?with_backup'],
],
[{ uri: 'hooks/backup' }, { uri: 'apps?with_backup' }],
{ onQueriesResponse },
)
@ -76,7 +73,7 @@ function createBackup() {
}
}
api.post('backups', data, 'backups.create').then(() => {
api.post({ uri: 'backups', data, humanKey: 'backups.create' }).then(() => {
router.push({ name: 'backup-list', params: { id: props.id } })
})
}

View file

@ -22,7 +22,7 @@ const router = useRouter()
const store = useStore()
const modalConfirm = useAutoModal()
const { loading } = useInitialQueries(
[['GET', `backups/${props.name}?with_details`]],
[{ uri: `backups/${props.name}?with_details` }],
{ onQueriesResponse },
)
@ -96,9 +96,10 @@ async function restoreBackup() {
}
api
.put(`backups/${props.name}/restore`, data, {
key: 'backups.restore',
name: props.name,
.put({
uri: `backups/${props.name}/restore`,
data,
humanKey: { key: 'backups.restore', name: props.name },
})
.then(() => {
isValid.value = null
@ -117,11 +118,10 @@ async function deleteBackup() {
if (!confirmed) return
api
.delete(
'backups/' + props.name,
{},
{ key: 'backups.delete', name: props.name },
)
.delete({
uri: 'backups/' + props.name,
humanKey: { key: 'backups.delete', name: props.name },
})
.then(() => {
router.push({ name: 'backup-list', params: { id: props.id } })
})

View file

@ -9,7 +9,7 @@ const props = defineProps<{
id: string
}>()
const { loading } = useInitialQueries([['GET', 'backups?with_info']], {
const { loading } = useInitialQueries([{ uri: 'backups?with_info' }], {
onQueriesResponse,
})
const archives = ref<any[] | null>(null)

View file

@ -8,10 +8,14 @@ import { DEFAULT_STATUS_ICON } from '@/helpers/yunohostArguments'
const { loading, refetch } = useInitialQueries(
[
['PUT', 'diagnosis/run?except_if_never_ran_yet', {}, 'diagnosis.run'],
['GET', 'diagnosis?full'],
{
method: 'PUT',
uri: 'diagnosis/run?except_if_never_ran_yet',
humanKey: 'diagnosis.run',
},
{ uri: 'diagnosis?full' },
],
{ wait: true, onQueriesResponse },
{ showModal: true, onQueriesResponse },
)
const reports = ref()
@ -62,9 +66,13 @@ function runDiagnosis({ id = null, description } = {}) {
const data = id !== null ? { categories: [id] } : {}
api
.put('diagnosis/run' + param, data, {
key: 'diagnosis.run' + (id !== null ? '_specific' : ''),
description,
.put({
uri: 'diagnosis/run' + param,
data,
humanKey: {
key: 'diagnosis.run' + (id !== null ? '_specific' : ''),
description,
},
})
.then(() => refetch(false))
}
@ -75,11 +83,11 @@ function toggleIgnoreIssue(action, report, item) {
)
api
.put(
'diagnosis/' + action,
{ filter: filterArgs },
`diagnosis.${action}.${item.status.toLowerCase()}`,
)
.put({
uri: 'diagnosis/' + action,
data: { filter: filterArgs },
humanKey: `diagnosis.${action}.${item.status.toLowerCase()}`,
})
.then(() => {
item.ignored = action === 'ignore'
if (item.ignored) {

View file

@ -9,14 +9,20 @@ import { useInitialQueries } from '@/composables/useInitialQueries'
import { DomainForm } from '@/views/_partials'
const router = useRouter()
const store = useStore()
const { loading } = useInitialQueries([['GET', { uri: 'domains' }]])
const { loading } = useInitialQueries([
{ uri: 'domains', cachePath: 'domains' },
])
const serverError = ref('')
function onSubmit(data) {
api
.post('domains', data, { key: 'domains.add', name: data.domain })
.post({
uri: 'domains',
cachePath: 'domains',
data,
humanKey: { key: 'domains.add', name: data.domain },
})
.then(() => {
store.dispatch('RESET_CACHE_DATA', ['domains'])
router.push({ name: 'domain-list' })

View file

@ -14,7 +14,7 @@ const props = defineProps<{
const { t } = useI18n()
const modalConfirm = useAutoModal()
const { loading } = useInitialQueries(
[['GET', `domains/${props.name}/dns/suggest`]],
[{ uri: `domains/${props.name}/dns/suggest` }],
{ onQueriesResponse },
)
@ -36,8 +36,9 @@ function getDnsChanges() {
loading.value = true
return api
.post(`domains/${props.name}/dns/push?dry_run`, {}, null, {
wait: false,
.post({
uri: `domains/${props.name}/dns/push?dry_run`,
showModal: false,
websocket: false,
})
.then((dnsChanges) => {
@ -110,11 +111,10 @@ async function pushDnsChanges() {
}
api
.post(
`domains/${props.name}/dns/push${force.value ? '?force' : ''}`,
{},
{ key: 'domains.push_dns_changes', name: props.name },
)
.post({
uri: `domains/${props.name}/dns/push${force.value ? '?force' : ''}`,
humanKey: { key: 'domains.push_dns_changes', name: props.name },
})
.then(async (responseData) => {
await getDnsChanges()
if (!isEmptyValue(responseData)) {

View file

@ -29,9 +29,13 @@ const store = useStore()
const modalConfirm = useAutoModal()
const { loading, refetch } = useInitialQueries(
[
['GET', { uri: 'domains', storeKey: 'domains' }],
['GET', { uri: 'domains', storeKey: 'domains_details', param: props.name }],
['GET', `domains/${props.name}/config?full`],
{ uri: 'domains', cachePath: 'domains' },
{
uri: `domains/${props.name}`,
cachePath: 'domains_details',
cacheParams: { domain: props.name },
},
{ uri: `domains/${props.name}/config?full` },
],
{ onQueriesResponse },
)
@ -92,17 +96,17 @@ function onQueriesResponse(
const onPanelApply: OnPanelApply = ({ panelId, data, action }, onError) => {
api
.put(
action
.put({
uri: action
? `domain/${props.name}/actions/${action}`
: `domains/${props.name}/config/${panelId}`,
{ args: objectToParams(data) },
{
data: { args: objectToParams(data) },
humanKey: {
key: `domains.${action ? 'action' : 'update'}_config`,
id: panelId,
name: props.name,
},
)
})
.then(() => refetch())
.catch(onError)
}
@ -114,9 +118,15 @@ async function deleteDomain() {
: {}
api
.delete({ uri: 'domains', param: props.name }, data, {
key: 'domains.delete',
name: props.name,
.delete({
uri: 'domains',
cachePath: 'domains',
cacheParams: { domain: props.name },
data,
humanKey: {
key: 'domains.delete',
name: props.name,
},
})
.then(() => {
router.push({ name: 'domain-list' })
@ -128,11 +138,12 @@ async function setAsDefaultDomain() {
if (!confirmed) return
api
.put(
{ uri: `domains/${props.name}/main`, storeKey: 'main_domain' },
{},
{ key: 'domains.set_default', name: props.name },
)
.put({
uri: `domains/${props.name}/main`,
cachePath: 'main_domain',
data: {},
humanKey: { key: 'domains.set_default', name: props.name },
})
.then(() => {
// FIXME Have to commit by hand here since the response is empty (should return the given name)
store.commit('UPDATE_MAIN_DOMAIN', props.name)

View file

@ -9,7 +9,7 @@ import type { ComputedRef } from 'vue'
const { mainDomain, domainsTree } = useStoreGetters()
const { loading } = useInitialQueries([
['GET', { uri: 'domains', storeKey: 'domains' }],
{ uri: 'domains', cachePath: 'domains' },
])
const [search, filteredTree] = useSearch(

View file

@ -29,9 +29,11 @@ const { v, onSubmit } = useForm(form, fields)
const onAddGroup = onSubmit((onError) => {
api
.post({ uri: 'users/groups', storeKey: 'groups' }, form, {
key: 'groups.create',
name: form.value.groupname,
.post({
uri: 'users/groups',
cachePath: 'groups',
data: form,
humanKey: { key: 'groups.create', name: form.value.groupname },
})
.then(() => {
router.push({ name: 'group-list' })

View file

@ -17,15 +17,12 @@ const { t } = useI18n()
const modalConfirm = useAutoModal()
const { loading } = useInitialQueries(
[
['GET', { uri: 'users' }],
[
'GET',
{
uri: 'users/groups?full&include_primary_groups',
storeKey: 'groups',
},
],
['GET', { uri: 'users/permissions?full', storeKey: 'permissions' }],
{ uri: 'users', cachePath: 'users' },
{
uri: 'users/groups?full&include_primary_groups',
cachePath: 'groups',
},
{ uri: 'users/permissions?full', cachePath: 'permissions' },
],
{ onQueriesResponse },
)
@ -123,33 +120,25 @@ async function onPermissionChanged({ option, groupName, action, applyMethod }) {
)
if (!confirmed) return
}
// FIXME hacky way to update the store
api
.put(
// FIXME hacky way to update the store
{
uri: `users/permissions/${permId}/${action}/${groupName}`,
storeKey: 'permissions',
groupName,
action,
permId,
},
{},
{ key: 'permissions.' + action, perm: option, name: groupName },
)
.put({
uri: `users/permissions/${permId}/${action}/${groupName}`,
cachePath: 'permissions',
cacheParams: { groupName, action, permId },
humanKey: { key: 'permissions.' + action, perm: option, name: groupName },
})
.then(() => applyMethod(option))
}
function onUserChanged({ option, groupName, action, applyMethod }) {
api
.put(
{
uri: `users/groups/${groupName}/${action}/${option}`,
storeKey: 'groups',
groupName,
},
{},
{ key: 'groups.' + action, user: option, name: groupName },
)
.put({
uri: `users/groups/${groupName}/${action}/${option}`,
cachePath: 'groups',
cacheParams: { groupName },
humanKey: { key: 'groups.' + action, user: option, name: groupName },
})
.then(() => applyMethod(option))
}
@ -165,11 +154,12 @@ async function deleteGroup(groupName) {
if (!confirmed) return
api
.delete(
{ uri: 'users/groups', param: groupName, storeKey: 'groups' },
{},
{ key: 'groups.delete', name: groupName },
)
.delete({
uri: `users/groups/${groupName}`,
cachePath: 'groups',
cacheParams: { groupName },
humanKey: { key: 'groups.delete', name: groupName },
})
.then(() => {
primaryGroups.value = primaryGroups.value?.filter(
(group) => group.name !== groupName,

View file

@ -15,8 +15,8 @@ const { t } = useI18n()
const modalConfirm = useAutoModal()
const { loading, refetch } = useInitialQueries(
[
['GET', 'services/' + props.name],
['GET', `services/${props.name}/log?number=50`],
{ uri: 'services/' + props.name },
{ uri: `services/${props.name}/log?number=50` },
],
{ onQueriesResponse },
)
@ -56,11 +56,10 @@ async function updateService(action) {
if (!confirmed) return
api
.put(
`services/${props.name}/${action}`,
{},
{ key: 'services.' + action, name: props.name },
)
.put({
uri: `services/${props.name}/${action}`,
humanKey: { key: 'services.' + action, name: props.name },
})
.then(() => refetch(false))
}

View file

@ -6,7 +6,7 @@ import { useSearch } from '@/composables/useSearch'
import { distanceToNow } from '@/helpers/filters/date'
import type { Obj } from '@/types/commons'
const { loading } = useInitialQueries([['GET', 'services']], {
const { loading } = useInitialQueries([{ uri: 'services' }], {
onQueriesResponse,
})

View file

@ -13,7 +13,7 @@ import type { FieldProps, FormFieldDict } from '@/types/form'
const { t } = useI18n()
const modalConfirm = useAutoModal()
const { loading, refetch } = useInitialQueries([['GET', '/firewall?raw']], {
const { loading, refetch } = useInitialQueries([{ uri: '/firewall?raw' }], {
onQueriesResponse,
})
@ -141,18 +141,17 @@ async function togglePort({ action, port, protocol, connection }) {
const actionTrad = t({ allow: 'open', disallow: 'close' }[action])
return api
.put(
`firewall/${protocol}/${action}/${port}?${connection}_only`,
{},
{
.put({
uri: `firewall/${protocol}/${action}/${port}?${connection}_only`,
humanKey: {
key: 'firewall.ports',
protocol,
action: actionTrad,
port,
connection,
},
{ wait: false },
)
showModal: false,
})
.then(() => confirmed)
}
@ -162,11 +161,10 @@ async function toggleUpnp(value) {
if (!confirmed) return
api
.put(
'firewall/upnp/' + action,
{},
{ key: 'firewall.upnp', action: t(action) },
)
.put({
uri: 'firewall/upnp/' + action,
humanKey: { key: 'firewall.upnp', action: t(action) },
})
.then(() => {
// FIXME Couldn't test when it works.
refetch(false)

View file

@ -18,7 +18,7 @@ const queries = computed<APIQuery[]>(() => {
with_suboperations: '',
number: numberOfLines.value,
})
return [['GET', `logs/${props.name}?${queryString}`]]
return [{ uri: `logs/${props.name}?${queryString}` }]
})
const { loading, refetch } = useInitialQueries(queries, {
onQueriesResponse,
@ -66,12 +66,11 @@ function onQueriesResponse(log: any) {
function shareLogs() {
api
.get(
`logs/${props.name}/share`,
null,
{ key: 'share_logs', name: props.name },
{ websocket: true },
)
.get({
uri: `logs/${props.name}/share`,
humanKey: { key: 'share_logs', name: props.name },
websocket: true,
})
.then(({ url }) => {
window.open(url, '_blank')
})

View file

@ -7,7 +7,7 @@ import { distanceToNow, readableDate } from '@/helpers/filters/date'
import type { Obj } from '@/types/commons'
const { loading } = useInitialQueries(
[['GET', `logs?limit=${25}&with_details`]],
[{ uri: `logs?limit=${25}&with_details` }],
{ onQueriesResponse },
)

View file

@ -10,10 +10,7 @@ import { useInitialQueries } from '@/composables/useInitialQueries'
const { t } = useI18n()
const modalConfirm = useAutoModal()
const { loading, refetch } = useInitialQueries(
[
['GET', 'migrations?pending'],
['GET', 'migrations?done'],
],
[{ uri: 'migrations?pending' }, { uri: 'migrations?done' }],
{ onQueriesResponse },
)
@ -46,7 +43,7 @@ function runMigrations() {
// Check that every migration's disclaimer has been checked.
if (Object.values(checked).every((value) => value === true)) {
api
.put('migrations?accept_disclaimer', {}, 'migrations.run')
.put({ uri: 'migrations?accept_disclaimer', humanKey: 'migrations.run' })
.then(() => refetch(false))
}
}
@ -55,7 +52,11 @@ async function skipMigration(id) {
const confirmed = await modalConfirm(t('confirm_migrations_skip'))
if (!confirmed) return
api
.put('/migrations/' + id, { skip: '', targets: id }, 'migration.skip')
.put({
uri: '/migrations/' + id,
data: { skip: '', targets: id },
humanKey: 'migration.skip',
})
.then(() => refetch(false))
}
</script>

View file

@ -13,7 +13,7 @@ async function triggerAction(action) {
const confirmed = await modalConfirm(t('confirm_reboot_action_' + action))
if (!confirmed) return
api.put(action + '?force', {}, action).then(() => {
api.put({ uri: action + '?force', humanKey: action }).then(() => {
const delay = action === 'reboot' ? 4000 : 10000
store.dispatch('TRY_TO_RECONNECT', {
attemps: Infinity,

View file

@ -13,7 +13,7 @@ import type { CoreConfigPanels } from '@/types/core/options'
const props = defineProps<{ tabId?: string }>()
const { loading, refetch } = useInitialQueries([['GET', 'settings?full']], {
const { loading, refetch } = useInitialQueries([{ uri: 'settings?full' }], {
onQueriesResponse,
})
const config = shallowRef<ConfigPanelsProps | undefined>()
@ -29,11 +29,11 @@ function onQueriesResponse(config_: CoreConfigPanels) {
const onPanelApply: OnPanelApply = ({ panelId, data }, onError) => {
// FIXME no route for potential action
api
.put(
`settings/${panelId}`,
{ args: objectToParams(data) },
{ key: 'settings.update', panel: panelId },
)
.put({
uri: `settings/${panelId}`,
data: { args: objectToParams(data) },
humanKey: { key: 'settings.update', panel: panelId },
})
.then(() => refetch())
.catch(onError)
}

View file

@ -11,10 +11,10 @@ import { useInitialQueries } from '@/composables/useInitialQueries'
const { t } = useI18n()
const store = useStore()
const modalConfirm = useAutoModal()
const { loading } = useInitialQueries([['PUT', 'update/all', {}, 'update']], {
wait: true,
onQueriesResponse,
})
const { loading } = useInitialQueries(
[{ method: 'PUT', uri: 'update/all', humanKey: 'update' }],
{ showModal: true, onQueriesResponse },
)
const system = ref()
const apps = ref()
@ -64,7 +64,10 @@ async function performAppsUpgrade(ids) {
for (const app of apps_) {
const continue_ = await api
.put(`apps/${app.id}/upgrade`, {}, { key: 'upgrade.app', app: app.name })
.put({
uri: `apps/${app.id}/upgrade`,
humanKey: { key: 'upgrade.app', app: app.name },
})
.then((response) => {
const postMessage = formatAppNotifs(response.notifications.POST_UPGRADE)
const isLast = app.id === lastAppId
@ -100,7 +103,7 @@ async function performSystemUpgrade() {
const confirmed = await modalConfirm(t('confirm_update_system'))
if (!confirmed) return
api.put('upgrade/system', {}, { key: 'upgrade.system' }).then(() => {
api.put({ uri: 'upgrade/system', humanKey: 'upgrade.system' }).then(() => {
if (system.value.some(({ name }) => name.includes('yunohost'))) {
store.dispatch('TRY_TO_RECONNECT', {
attemps: 1,

View file

@ -23,8 +23,8 @@ const { t } = useI18n()
const router = useRouter()
const { loading } = useInitialQueries(
[
['GET', { uri: 'users' }],
['GET', { uri: 'domains' }],
{ uri: 'users', cachePath: 'users' },
{ uri: 'domains', cachePath: 'domains' },
],
{ onQueriesResponse },
)
@ -117,9 +117,11 @@ function onQueriesResponse() {
const onUserCreate = onSubmit(async (onError) => {
const data = await formatForm(form)
api
.post({ uri: 'users' }, data, {
key: 'users.create',
name: form.value.username,
.post({
uri: 'users',
cachePath: 'users',
data,
humanKey: { key: 'users.create', name: form.value.username },
})
.then(() => {
router.push({ name: 'user-list' })

View file

@ -30,8 +30,12 @@ const { t } = useI18n()
const router = useRouter()
const { loading } = useInitialQueries(
[
['GET', { uri: 'users', param: props.name, storeKey: 'users_details' }],
['GET', { uri: 'domains' }],
{
uri: `users/${props.name}`,
cachePath: 'users_details',
cacheParams: { username: props.name },
},
{ uri: 'domains', cachePath: 'domains' },
],
{ onQueriesResponse },
)
@ -207,12 +211,15 @@ const onUserEdit = onSubmit(async (onError, serverErrors) => {
}
api
.put({ uri: 'users', param: props.name, storeKey: 'users_details' }, data, {
key: 'users.update',
name: props.name,
.put({
uri: `users/${props.name}`,
cachePath: 'users_details',
cacheParams: { username: props.name },
data,
humanKey: { key: 'users.update', name: props.name },
})
.then(() => {
router.push({ name: 'user-info', param: { name: props.name } })
router.push({ name: 'user-info', params: { name: props.name } })
})
.catch(onError)
})

View file

@ -70,7 +70,7 @@ const onUserImport = onSubmit(async (onError) => {
if (!requestArgs.update) delete requestArgs.update
const data = await formatForm(requestArgs)
api
.post('users/import', data, null, { asFormData: true })
.post({ uri: 'users/import', data })
.then(() => {
// Reset all cached data related to users.
store.dispatch('RESET_CACHE_DATA', [

View file

@ -10,7 +10,11 @@ const props = defineProps<{ name: string }>()
const router = useRouter()
const { loading } = useInitialQueries([
['GET', { uri: 'users', param: props.name, storeKey: 'users_details' }],
{
uri: `users/${props.name}`,
cachePath: 'users_details',
cacheParams: { username: props.name },
},
])
const { user: userGetter } = useStoreGetters()
@ -20,11 +24,13 @@ const user = computed(() => userGetter.value(props.name))
function deleteUser() {
const data = purge.value ? { purge: '' } : {}
api
.delete(
{ uri: 'users', param: props.name, storeKey: 'users_details' },
.delete({
uri: `users/${props.name}`,
cachePath: 'user_details',
cacheParams: { username: props.name },
data,
{ key: 'users.delete', name: props.name },
)
humanKey: { key: 'users.delete', name: props.name },
})
.then(() => {
router.push({ name: 'user-list' })
})

View file

@ -9,13 +9,10 @@ import type { Obj } from '@/types/commons'
const store = useStore()
const { loading } = useInitialQueries([
[
'GET',
{
uri: 'users?fields=username&fields=fullname&fields=mail&fields=mailbox-quota&fields=groups',
storeKey: 'users',
},
],
{
uri: 'users?fields=username&fields=fullname&fields=mail&fields=mailbox-quota&fields=groups',
cachePath: 'users',
},
])
const { users } = useStoreGetters()