yunohost-admin/app/src/helpers/yunohostArguments.ts
2024-08-13 11:52:06 +02:00

170 lines
5.3 KiB
TypeScript

import {
flattenObjectLiteral,
getFileContent,
isEmptyValue,
isObjectLiteral,
} from '@/helpers/commons'
import store from '@/store'
import type { Translation } from '@/types/commons'
import type { AdressModelValue } from '@/types/form'
export const DEFAULT_STATUS_ICON = {
[null]: null,
danger: 'times',
error: 'times',
info: 'info',
success: 'check',
warning: 'warning',
}
// FORMAT FROM CORE
/**
* Tries to find a translation corresponding to the user's locale/fallback locale in a
* Yunohost argument or simply return the string if it's not an object literal.
*
* @param field - A field value containing a translation object or string
* @return translated field or empty string
*/
export function formatI18nField(field?: Translation): string {
if (!field) return ''
if (typeof field === 'string') return field
const { locale, fallbackLocale } = store.state as {
locale: string
fallbackLocale: string
}
return field[locale] || field[fallbackLocale] || field.en || ''
}
/**
* Returns a string size declaration to a M value.
*
* @param size - A size declared like '500M' or '56k'
* @return a number in M
*/
export function sizeToM(size: string) {
const unit = size.slice(-1)
const value = parseInt(size.slice(0, -1))
if (unit === 'M') return value
if (unit === 'b') return Math.ceil(value / (1024 * 1024))
if (unit === 'k') return Math.ceil(value / 1024)
if (unit === 'G') return Math.ceil(value * 1024)
if (unit === 'T') return Math.ceil(value * 1024 * 1024)
}
/**
* Returns an address as AdressModelValue to be used by AdressItem component.
*
* @param address - A string representing an adress (subdomain or email)
* @return Parsed address as `AdressModelValue`
*/
export function formatAdress(address: string): AdressModelValue {
const separator = address.includes('@') ? '@' : '.'
const [localPart, domain] = address.split(separator)
return { localPart, separator, domain }
}
// FORMAT TO CORE
/**
* Parse a front-end value to its API equivalent. This function returns a Promise or an
* Object `{ key: Promise }` if `key` is supplied. When parsing a form, all those
* objects must be merged to define the final sent form.
*
* Convert Boolean to '1' (true) or '0' (false),
* Concatenate two parts adresses (subdomain or email for example) into a single string,
* Convert File to its Base64 representation or set its value to '' to ask for a removal.
*
* @param {*} value
* @return {*}
*/
export function formatFormDataValue(value, key = null) {
if (Array.isArray(value)) {
return Promise.all(value.map((value_) => formatFormDataValue(value_))).then(
(resolvedValues) => ({ [key]: resolvedValues }),
)
}
let result = value
if (typeof value === 'boolean') result = value ? 1 : 0
if (isObjectLiteral(value) && 'file' in value) {
// File has to be deleted
if (value.removed) result = ''
// File has not changed (will not be sent)
else if (value.current || value.file === null) result = null
else {
return getFileContent(value.file, { base64: true }).then((content) => {
return {
[key]: content.replace(/data:[^;]*;base64,/, ''),
[key + '[name]']: value.file.name,
}
})
}
} else if (isObjectLiteral(value) && 'separator' in value) {
result = Object.values(value).join('')
}
// Returns a resolved Promise for non async values
return Promise.resolve(key ? { [key]: result } : result)
}
/**
* Convinient helper to properly parse a front-end form to its API equivalent.
* This parse each values asynchronously, allow to inject keys into the final form and
* make sure every async values resolves before resolving itself.
*
* @param {Object} formData
* @return {Object}
*/
function formatFormDataValues(formData) {
const promisedValues = Object.entries(formData).map(([key, value]) => {
return formatFormDataValue(value, key)
})
return Promise.all(promisedValues).then((resolvedValues) => {
return resolvedValues.reduce((form, obj) => ({ ...form, ...obj }), {})
})
}
/**
* Format a form produced by a vue view to be sent to the server.
*
* @param {Object} formData - An object literal containing form values.
* @param {Object} [extraParams] - Optionnal params
* @param {Array} [extraParams.extract] - An array of keys that should be extracted from the form.
* @param {Boolean} [extraParams.flatten=false] - Flattens or not the passed formData.
* @param {Boolean} [extraParams.removeEmpty=true] - Removes "empty" values from the object.
* @return {Object} the parsed data to be sent to the server, with extracted values if specified.
*/
export async function formatFormData(
formData,
{
extract = null,
flatten = false,
removeEmpty = true,
removeNull = false,
} = {},
) {
const output = {
data: {},
extracted: {},
}
const values = await formatFormDataValues(formData)
for (const key in values) {
const type = extract && extract.includes(key) ? 'extracted' : 'data'
const value = values[key]
if (removeEmpty && isEmptyValue(value)) {
continue
} else if (removeNull && [null, undefined].includes(value)) {
continue
} else if (flatten && isObjectLiteral(value)) {
flattenObjectLiteral(value, output[type])
} else {
output[type][key] = value
}
}
const { data, extracted } = output
return extract ? { data, ...extracted } : data
}