@applica-software-guru/crud-client
Version:
Libreria per l'accesso ai servizi REST di Applica.
378 lines (354 loc) • 11.8 kB
text/typescript
import {
ApplicaDataProviderConfig,
AttachmentParserResult,
CreateParamsExtended,
DeleteManyParamsExtended,
DeleteParamsExtended,
GetListParamsExtended,
GetManyParamsExtended,
GetManyReferenceParamsExtended,
GetOneParamsExtended,
IApplicaDataProvider,
UpdateManyParamsExtended,
UpdateParamsExtended
} from './types';
import { createFormData, createGetQuery, fetchJson } from './utils';
import { stringify } from 'query-string';
import {
CreateParams,
CreateResult,
DeleteManyResult,
DeleteResult,
GetListResult,
GetManyReferenceResult,
GetManyResult,
GetOneResult,
Identifier,
UpdateManyResult,
UpdateParams,
UpdateResult
} from 'ra-core';
class ApplicaDataProvider implements IApplicaDataProvider {
config: ApplicaDataProviderConfig;
prepareData: (data: any, resource?: string, params?: CreateParams | UpdateParams | any) => any;
prepareAttachments: (data: any) => Promise<AttachmentParserResult>;
private timeout: number;
constructor(config: ApplicaDataProviderConfig) {
this.config = config;
this.prepareData = config.prepareData || ((data: any) => data);
this.prepareAttachments = config.attachmentsParser;
this.timeout = config.timeout || 30000; // Default 30 seconds
}
/**
* @inheritdoc
*/
getApiUrl(): string {
return this.config.apiUrl;
}
/**
* @inheritdoc
*/
async getFile(resource: string): Promise<string> {
const headers = await this.config.getHeaders();
return fetch(`${this.config.apiUrl}${resource}`, { headers })
.then((response) => response.blob())
.then((blob) => URL.createObjectURL(blob));
}
/**
* @inheritdoc
*/
async getList(resource: string, params: GetListParamsExtended | any): Promise<GetListResult | any> {
const query = createGetQuery(params);
const url = `${this.config.apiUrl}/${resource}/find`;
const headers = await this.config.getHeaders();
const options = {
headers,
body: JSON.stringify(query),
method: 'POST'
};
const timeout = params?.timeout || this.timeout;
return fetchJson(url, options, this.config.HttpErrorClass, timeout).then(({ json }) => ({
data: json?.value?.rows,
total: parseInt(json?.value?.totalRows)
}));
}
/**
* @inheritdoc
*/
async getOne(resource: string, params: GetOneParamsExtended | any): Promise<GetOneResult | any> {
const url = `${this.config.apiUrl}/${resource}` + (params.id ? `/${params.id}` : '');
const headers = await this.config.getHeaders();
const options = { headers };
const timeout = params?.timeout || this.timeout;
return fetchJson(url, options, this.config.HttpErrorClass, timeout).then(({ json }) => ({
data: json.value
}));
}
/**
* @inheritdoc
*/
async getMany(resource: string, params: GetManyParamsExtended | any): Promise<GetManyResult | any> {
const url = `${this.config.apiUrl}/${resource}/find`;
const headers = await this.config.getHeaders();
const options = {
headers,
body: JSON.stringify({
filters: [
{
property: 'id',
value: params.ids,
type: 'in'
}
]
}),
method: 'POST'
};
const timeout = params?.timeout || this.timeout;
return fetchJson(url, options, this.config.HttpErrorClass, timeout).then(({ json }) => ({
data: json?.value?.rows,
total: parseInt(json?.value?.totalRows)
}));
}
/**
* @inheritdoc
*/
async getManyReference(resource: string, params: GetManyReferenceParamsExtended): Promise<GetManyReferenceResult> {
const query = createGetQuery(params);
const url = `${this.config.apiUrl}/${resource}/find`;
const headers = await this.config.getHeaders();
query.filters.push({
property: params.target,
value: params.id,
type: 'eq'
});
const options = {
headers,
body: JSON.stringify(query),
method: 'POST'
};
const timeout = params?.timeout || this.timeout;
return fetchJson(url, options, this.config.HttpErrorClass, timeout).then(({ json }) => ({
data: json?.value?.rows,
total: parseInt(json?.value?.totalRows)
}));
}
/**
* @inheritdoc
*/
async create(resource: string, params: CreateParamsExtended): Promise<CreateResult> {
return this.prepareAttachments(params.data).then(async ({ data, parts }) => {
const url = `${this.config.apiUrl}/${resource}`;
const token = await this.config.getToken();
let body: any = null;
let headers: any = {};
if (this.config.mobile === true) {
body = this.prepareData(data, resource, params);
body = JSON.stringify(body);
headers = {
'Content-Type': 'application/json'
};
} else {
body = new FormData();
const jsonData = JSON.stringify(this.prepareData(data, resource, params));
const entity = new Blob([jsonData], { type: 'application/json' });
body.append('entity', entity);
parts.forEach((p) => body.append(p.id, p.file));
}
const options = this.createOptions(
{
method: 'POST',
body,
headers
},
token
);
const timeout = params?.timeout || this.timeout;
return fetchJson(url, options, this.config.HttpErrorClass, timeout).then(({ json }) => ({
data: { ...(json?.value || params.data), id: json?.value.id }
}));
});
}
/**
* @inheritdoc
*/
async update(resource: string, params: UpdateParamsExtended): Promise<UpdateResult> {
return this.prepareAttachments(params.data).then(async ({ data, parts }) => {
const url = `${this.config.apiUrl}/${resource}`;
const token = await this.config.getToken();
let body: any = null;
let headers: any = {};
if (this.config.mobile === true) {
body = this.prepareData(data, resource, params);
body = JSON.stringify(body);
headers = {
'Content-Type': 'application/json'
};
} else {
body = new FormData();
const jsonData = JSON.stringify(this.prepareData(data, resource, params));
const entity = new Blob([jsonData], { type: 'application/json' });
body.append('entity', entity);
parts.forEach((p) => body.append(p.id, p.file));
}
const options = this.createOptions(
{
method: 'POST',
body,
headers
},
token
);
const timeout = params?.timeout || this.timeout;
return fetchJson(url, options, this.config.HttpErrorClass, timeout).then(({ json }) => ({
data: { ...(json?.value || params.data), id: json?.value.id }
}));
});
}
/**
* @inheritdoc
*/
async updateMany(
resource: string,
params: UpdateManyParamsExtended & {
rows?: any[];
}
): Promise<UpdateManyResult> {
const token = await this.config.getToken();
const timeout = params?.timeout || this.timeout;
return Promise.all(
params.ids.map((id: Identifier) =>
fetchJson(
`${this.config.apiUrl}/${resource}`,
{
method: 'POST',
body: (() => {
if (this.config.mobile === true) {
const row = params?.rows?.[id as number] || params?.rows?.find((r: any) => r.id === id);
const entity = JSON.stringify(this.prepareData({ ...row, ...params.data, id }));
return entity;
} else {
const row = params?.rows?.[id as number] || params?.rows?.find((r: any) => r.id === id);
const jsonData = JSON.stringify(this.prepareData({ ...row, ...params.data, id }));
const entity = new Blob([jsonData], { type: 'application/json' });
const formData = new FormData();
formData.append('entity', entity);
return formData;
}
})(),
headers: (() =>
this.config.mobile === true
? this.createHeaders(
{
'Content-Type': 'application/json'
},
token
)
: this.createHeaders({}, token))()
},
this.config.HttpErrorClass,
timeout
).then(({ json }) => ({
data: { ...(json?.value || params.data), id: json?.value.id }
}))
)
).then((responses) => ({
data: responses.map((response) => response.data)
}));
}
/**
* @inheritdoc
*/
async delete(resource: string, params: DeleteParamsExtended | any): Promise<DeleteResult | any> {
const url = `${this.config.apiUrl}/${resource}/delete`;
const token = await this.config.getToken();
const body = createFormData({ ids: params.id });
const options = this.createOptions(
{
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
Accept: 'application/json'
},
body
},
token
);
const timeout = params?.timeout || this.timeout;
return fetchJson(url, options, this.config.HttpErrorClass, timeout).then(({ json }) => ({
data: json
}));
}
/**
* @inheritdoc
*/
async deleteMany(resource: string, params: DeleteManyParamsExtended | any): Promise<DeleteManyResult | any> {
const token = await this.config.getToken();
const url = `${this.config.apiUrl}/${resource}/delete`;
const options = this.createOptions(
{
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
Accept: 'application/json'
},
body: createFormData({ ids: params.ids })
},
token
);
const timeout = params?.timeout || this.timeout;
return fetchJson(url, options, this.config.HttpErrorClass, timeout).then(() => ({
data: params.ids
}));
}
private async _call(method: string, resource: string, params: any, timeout?: number): Promise<any> {
let url = `${this.config.apiUrl}/${resource}`;
// Extract timeout from params if not explicitly provided
const requestTimeout = timeout || params?.timeout || this.timeout;
// Create a copy of params without timeout for the actual request
const requestParams = params ? { ...params } : {};
if (requestParams.timeout) {
delete requestParams.timeout;
}
if (method === 'GET' && requestParams && Object.keys(requestParams).length > 0) {
const queryString = stringify(requestParams);
url = `${this.config.apiUrl}/${resource}?${queryString}`;
}
const headers = await this.config.getHeaders();
const options = {
method,
headers,
body: method !== 'GET' ? JSON.stringify(requestParams) : undefined
};
return fetchJson(url, options, this.config.HttpErrorClass, requestTimeout).then(({ json }) => ({
data: json
}));
}
/**
* @inheritdoc
*/
async post(resource: string, params: any & { timeout?: number }): Promise<any> {
return this._call('POST', resource, params);
}
/**
* @inheritdoc
*/
async get(resource: string, params: any & { timeout?: number }): Promise<any> {
return this._call('GET', resource, params);
}
private createHeaders(headers: any, token?: string) {
const authHeaders =
typeof token === 'string' && token !== undefined && token !== null ? { Authorization: `Bearer ${token}` } : {};
return {
...headers,
...authHeaders
};
}
private createOptions(options: any, token?: string): any {
const headers = this.createHeaders(options?.headers, token);
return {
...options,
headers
};
}
}
export { ApplicaDataProvider };