@applica-software-guru/crud-client
Version:
Libreria per l'accesso ai servizi REST di Applica.
329 lines (303 loc) • 9.84 kB
text/typescript
import { AttachmentParserResult, CreateHeaderOptions, ErrorMapperResult } from './types';
import _ from 'lodash';
import { stringify } from 'query-string';
import { GetListParams, GetManyParams } from 'ra-core';
function createHeadersFromOptions(options: CreateHeaderOptions): Headers | any {
const requestHeaders =
options.headers ||
new Headers({
Accept: 'application/json'
});
if (
!requestHeaders.has('Content-Type') &&
!(options && (!options.method || options.method === 'GET')) &&
!(options && options.body && options.body instanceof FormData)
) {
requestHeaders.set('Content-Type', 'application/json');
}
if (options.user && options.user && options.user.token) {
requestHeaders.set('Authorization', options.user.token);
}
return requestHeaders;
}
function createFormData(body: any): string {
return Object.keys(body)
.map((key) => encodeURIComponent(key) + '=' + encodeURIComponent(body[key]))
.join('&');
}
function fetchJson(url: string, options: any = {}, HttpErrorClass: any = Error, timeout?: number): Promise<any> {
const controller = new AbortController();
const timeoutId = timeout ? setTimeout(() => controller.abort(), timeout) : null;
const fetchOptions = {
...options,
signal: controller.signal
};
return fetch(url, fetchOptions)
.then((response) =>
response.text().then((text) => ({
status: response.status,
statusText: response.statusText,
headers: response.headers,
body: text
}))
)
.then(({ status, headers, body }) => {
if (timeoutId) clearTimeout(timeoutId);
if (status === 401) {
return Promise.reject(new HttpErrorClass('iam.error.unauthorized', status, {}));
} else if (status === 403) {
return Promise.reject(new HttpErrorClass('iam.error.forbidden', status, {}));
} else if (status === 404) {
return Promise.reject(new HttpErrorClass('error.not_found', 404, {}));
}
let json;
try {
json = JSON.parse(body);
} catch (e) {
return Promise.reject(new HttpErrorClass('error.invalid_json_response', status, {}));
}
if (json?.responseCode === 'error.validation' || json?.responseCode === 'error.generic') {
const body = {
errors: createErrorMapper(json?.result)
};
return Promise.reject(new HttpErrorClass(json?.responseCode, status, body));
} else if (json?.responseCode !== undefined && json?.responseCode !== 'ok') {
return Promise.reject(new HttpErrorClass(json?.responseCode, status, body));
}
return Promise.resolve({ status, headers, body, json });
})
.catch((error) => {
if (timeoutId) clearTimeout(timeoutId);
if (error.name === 'AbortError') {
return Promise.reject(new HttpErrorClass('error.request_timeout', 408, {}));
}
throw error;
});
}
function isValidObject(value: any): boolean {
if (!value) {
return false;
}
const isArray = Array.isArray(value);
// eslint-disable-next-line no-undef
const isBuffer = typeof Buffer !== 'undefined' && Buffer.isBuffer(value);
const isObject = Object.prototype.toString.call(value) === '[object Object]';
const hasKeys = !!Object.keys(value).length;
return !isArray && !isBuffer && isObject && hasKeys;
}
function flattenObject(value: any, path: string[] = []): any {
if (isValidObject(value)) {
return Object.assign({}, ...Object.keys(value).map((key: string) => flattenObject(value[key], path.concat([key]))));
} else {
return path.length ? { [path.join('.')]: value } : value;
}
}
const queryParameters = stringify;
function createGetQuery(params: GetListParams | GetManyParams | any) {
const filter = params?.filter || {};
const page = params?.pagination?.page || 1;
const perPage = params?.pagination?.perPage || 10;
const field = params?.sort?.field || 'id';
const order = params?.sort?.order || 'DESC';
return {
filters: Object.keys(filter).reduce((acc: any[], key) => {
let property = key;
if (property === 'keyword') {
return acc;
}
let value = filter[key];
let type = 'eq';
if (property.includes('__')) {
const args = property.split('__');
property = args[0];
type = args[1];
} else if (typeof value === 'object') {
type = Object.keys(value)[0];
value = value[type];
}
return acc.concat([
{
property,
value,
type
}
]);
}, []),
page: page,
rowsPerPage: perPage,
keyword: filter?.keyword,
sorts: [
{
property: field,
descending: 'DESC' === order.toUpperCase()
}
]
};
}
type ConvertFileToBase64Result = null | { name: string; data: string | ArrayBuffer | null };
function convertFileToBase64(file: any): Promise<ConvertFileToBase64Result> {
if (!file.rawFile) {
return Promise.resolve(file);
}
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file.rawFile);
reader.onload = () => {
resolve({
name: file.rawFile.name,
data: reader.result
});
};
reader.onerror = reject;
});
}
async function convertFile(file: any) {
return file.rawFile
? convertFileToBase64(file).then((convertedFile) => ({
data: convertedFile,
name: file.rawFile.name,
size: file.rawFile.size,
type: file.rawFile.type
}))
: Promise.resolve(file);
}
function uuid(): string {
let d = new Date().getTime();
if (window.performance && typeof window.performance.now === 'function') {
d += performance.now(); //use high-precision timer if available
}
const uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
const r = (d + Math.random() * 16) % 16 | 0;
d = Math.floor(d / 16);
return (c == 'x' ? r : (r & 0x3) | 0x8).toString(16);
});
return uuid;
}
function convertFileToPart(item: any, parts: any[]): string {
let data = item;
const file = item?.rawFile || item;
if (file instanceof File) {
const id = uuid();
const { name, size, type } = file;
parts.push({ id, file });
data = {
id,
name,
size,
contentType: type,
action: 'Add'
};
}
return data;
}
function springErrorMapper(errors: any[]): any[] {
const validationErrors = errors.reduce(
(errorMap, error) => ({
...errorMap,
[error.property]: error.message
}),
{}
);
return validationErrors;
}
function createErrorMapper(result: ErrorMapperResult): any | any[] | boolean {
const errors = result?.errors || [];
if (errors) {
const mappedErrors = springErrorMapper(errors);
return mappedErrors;
} else {
return false;
}
}
function flattenKeys(obj: any, prefix: string = '.'): any {
const keys = [];
if (typeof obj === 'object' && obj !== null) {
for (const key in obj) {
let newPrefix = prefix + key + '.';
if (newPrefix.startsWith('.')) {
newPrefix = newPrefix.substring(1);
}
keys.push(...flattenKeys(obj[key], newPrefix));
}
} else {
if (prefix.endsWith('.') && prefix.length > 1) {
prefix = prefix.substring(0, prefix.length - 1);
}
keys.push(prefix);
}
return keys;
}
/**
* Configure a parser capable of working with attachments managed within forms.
*
* @param {Array<String>} fields
* @returns {Function} Returns a parser function that can be used to parse attachments.
* @example
* import { createAttachmentsParser } from '@applica-software-guru/crud-client';
* const parser = createAttachmentsParser();
*/
function createAttachmentsParser() {
return async (data: any): Promise<AttachmentParserResult> => {
const parts: any[] = [];
const regex = new RegExp(/__attachment_*|__file_*|__image_*/);
const fields = flattenKeys(data).filter((f: string) => regex.test(f));
for (let i = 0; i < fields.length; i++) {
const attachment = fields[i];
const type = regex.exec(attachment);
if (type && type[0]) {
const theKey = attachment.replace(type[0], '');
const value = _.get(data, theKey);
const isAttachment = type[0] === '__attachment__';
if (!value) {
if (!isAttachment) {
data = _.set(data, `_${theKey}`, null);
}
} else {
const _value = _.get(data, theKey);
if (Array.isArray(_value)) {
if (!isAttachment) {
throw new Error('Array of file/image is not supported, please use Attachment instead.');
}
for (let j = 0; j < _value.length; j++) {
const item = _value[j];
const file = convertFileToPart(item, parts);
_value[j] = file;
}
_.set(data, theKey, _value);
} else {
const file = (
isAttachment ? await convertFileToPart(_value, parts) : await convertFileToBase64(_value)
) as any;
const alreadyStoredBase64Value = _.get(data, `_${theKey}`);
_.set(
data,
isAttachment ? theKey : `_${theKey}`,
isAttachment ? file : file.data || alreadyStoredBase64Value
);
if (!isAttachment && file?.name) {
_.set(data, theKey, file.name);
}
}
}
}
data = _.omit(data, attachment);
}
return { data, parts };
};
}
export {
convertFile,
convertFileToBase64,
convertFileToPart,
createAttachmentsParser,
createErrorMapper,
createFormData,
createGetQuery,
createHeadersFromOptions,
fetchJson,
flattenKeys,
flattenObject,
queryParameters,
springErrorMapper,
uuid
};