@applica-software-guru/crud-client
Version:
Libreria per l'accesso ai servizi REST di Applica.
653 lines (509 loc) • 17 kB
Markdown
Libreria per l'accesso ai servizi REST di Applica.
| Info | Dettagli |
| --------------- | ------------------------------------------------------- |
| Ultima versione | 1.0.\* |
| Autore | Roberto Conte Rosito |
| Repository | https://bitbucket.org/applicaguru/crud-client |
| Pipeline | https://bitbucket.org/applicaguru/crud-client/pipelines |
# Prefazione
La libreria `@applica-software-guru/crud-client` è un client REST che consente l'accesso ai servizi REST di Applica.
La sua implementazione si basa sui principi e le linee guida definite all'interno del progetto [React-Admin](https://marmelab.com/react-admin/Admin.html#dataprovider)
# Installazione
Per installare la libreria:
```bash
npm install @applica-software-guru/crud-client
```
# Configurazione
## Configurazione Base
```javascript
import { ApplicaDataProvider, createAttachmentsParser } from '@applica-software-guru/crud-client';
import { createAuthProvider, MemoryStorage } from '@applica-software-guru/iam-client';
import { HttpError } from 'ra-core';
const apiUrl = 'https://api.applica.guru/api';
const storage = new MemoryStorage();
const authProvider = createAuthProvider({ apiUrl, storage });
const dataProvider = new ApplicaDataProvider({
apiUrl,
HttpErrorClass: HttpError,
getHeaders: async () => await authProvider.getHeaders(),
getToken: async () => await authProvider.getToken(),
attachmentsParser: createAttachmentsParser()
});
```
## Configurazione Avanzata
```javascript
const dataProvider = new ApplicaDataProvider({
apiUrl: 'https://api.applica.guru/api',
HttpErrorClass: HttpError,
getHeaders: async () => await authProvider.getHeaders(),
getToken: async () => await authProvider.getToken(),
attachmentsParser: createAttachmentsParser(),
// Opzionale: modalità mobile
mobile: false, // default: false
// Opzionale: timeout di default per tutte le richieste (in millisecondi)
timeout: 30000, // default: 30000 (30 secondi)
// Opzionale: funzione per preparare i dati prima dell'invio
prepareData: (data, resource, params) => {
// Personalizza i dati prima dell'invio
return data;
}
});
```
### Configurazione per Applicazioni Mobile
Se lavori su un'applicazione mobile devi necessariamente creare il data provider in questo modo:
```javascript
const dataProvider = new ApplicaDataProvider({
apiUrl,
// ... altre configurazioni
mobile: true
});
```
In modalità mobile, il dataProvider adotta comportamenti diversi per le chiamate di creazione e modifica dei record:
- Utilizza chiamate REST pure invece di FormData
- La gestione degli upload dei file viene gestita separatamente
# API Reference
Il dataProvider implementa tutte le operazioni CRUD standard di React-Admin, estese con funzionalità specifiche di Applica.
**🕒 Timeout Support:** Tutte le operazioni supportano un parametro `timeout` opzionale che sovrascrive il timeout globale configurato nel provider. Questo permette di personalizzare il timeout per ogni singola richiesta in base alle necessità specifiche dell'operazione.
## Operazioni CRUD Standard
### getList
Recupera una lista paginata di record con supporto per filtri e ordinamento.
```javascript
const result = await dataProvider.getList('entities/user', {
pagination: {
page: 1,
perPage: 10
},
sort: {
field: 'name',
order: 'ASC' // o 'DESC'
},
filter: {
name__like: 'Giovanni',
id__gt: 10,
active__is: true,
keyword: 'search term' // ricerca globale
},
// Opzionale: timeout specifico per questa richiesta
timeout: 5000 // 5 secondi
});
// Risultato:
// {
// data: [...], // array di record
// total: 150 // numero totale di record
// }
```
**Operatori di filtro supportati:**
- `__like`: ricerca LIKE
- `__eq`: uguaglianza esatta
- `__gt`: maggiore di
- `__gte`: maggiore o uguale
- `__lt`: minore di
- `__lte`: minore o uguale
- `__is`: valore booleano
- `__in`: valore in array
### getOne
Recupera un singolo record per ID.
```javascript
const result = await dataProvider.getOne('entities/user', {
id: 1,
timeout: 3000 // timeout opzionale
});
// Risultato:
// {
// data: { id: 1, name: 'Roberto', ... }
// }
```
### getMany
Recupera più record specificando i loro ID.
```javascript
const result = await dataProvider.getMany('entities/user', {
ids: [1, 2, 3],
timeout: 4000
});
// Risultato:
// {
// data: [
// { id: 1, name: 'Roberto' },
// { id: 2, name: 'Mario' },
// { id: 3, name: 'Luigi' }
// ]
// }
```
### getManyReference
Recupera record che referenziano un altro record.
```javascript
const result = await dataProvider.getManyReference('entities/post', {
target: 'author_id',
id: 123, // ID dell'autore
pagination: { page: 1, perPage: 10 },
sort: { field: 'title', order: 'ASC' },
filter: { published: true },
timeout: 6000
});
```
### create
Crea un nuovo record. Supporta upload di file e allegati.
```javascript
const result = await dataProvider.create('entities/user', {
data: {
name: 'Nuovo Utente',
email: 'nuovo@example.com',
avatar: fileObject, // File object per upload
documents: [file1, file2] // Array di file
},
timeout: 10000 // timeout più lungo per upload
});
// Risultato:
// {
// data: { id: 456, name: 'Nuovo Utente', ... }
// }
```
### update
Aggiorna un record esistente.
```javascript
const result = await dataProvider.update('entities/user', {
id: 1,
data: {
name: 'Nome Aggiornato',
email: 'aggiornato@example.com'
},
previousData: { id: 1, name: 'Vecchio Nome', ... }, // richiesto da React-Admin
timeout: 8000
});
```
### updateMany
Aggiorna più record contemporaneamente.
```javascript
const result = await dataProvider.updateMany('entities/user', {
ids: [1, 2, 3],
data: {
status: 'active'
},
rows: [
// dati specifici per ogni record (opzionale)
{ id: 1, name: 'Roberto' },
{ id: 2, name: 'Mario' },
{ id: 3, name: 'Luigi' }
],
timeout: 15000
});
```
### delete
Elimina un record.
```javascript
const result = await dataProvider.delete('entities/user', {
id: 1,
timeout: 5000
});
```
### deleteMany
Elimina più record contemporaneamente.
```javascript
const result = await dataProvider.deleteMany('entities/user', {
ids: [1, 2, 3],
timeout: 10000
});
```
## Operazioni Estese
### get
Esegue una chiamata GET generica. Supporta timeout configurabile per richiesta.
```javascript
// Chiamata GET di base
const result = await dataProvider.get('users', {
name: 'Roberto',
active: true
});
// Con timeout personalizzato
const result = await dataProvider.get('users', {
name: 'Roberto',
active: true,
timeout: 5000 // 5 secondi per questa richiesta
});
```
### post
Esegue una chiamata POST generica. Supporta timeout configurabile per richiesta.
```javascript
import { stringify } from 'query-string';
// Chiamata POST di base
const result = await dataProvider.post(`users?${stringify({ k: 'f' })}`, {
id: 1,
name: 'Roberto'
});
// Con timeout personalizzato
const result = await dataProvider.post('users', {
id: 1,
name: 'Roberto',
timeout: 10000 // 10 secondi per questa richiesta
});
```
### getFile
Scarica un file dal server mantenendo l'autenticazione.
```javascript
const fileUrl = await dataProvider.getFile('/attachments/post/1/picture/1');
// Utilizzo per download
const link = document.createElement('a');
link.href = fileUrl;
link.download = 'filename.jpg';
link.click();
```
### getApiUrl
Ottiene l'URL base dell'API configurato.
```javascript
const apiUrl = dataProvider.getApiUrl();
console.log(apiUrl); // 'https://api.applica.guru/api'
```
# Gestione Timeout
Ogni operazione supporta un timeout configurabile a livello di singola richiesta, che sovrascrive il timeout di default del provider.
## Timeout di Default
```javascript
const dataProvider = new ApplicaDataProvider({
// ... altre configurazioni
timeout: 30000 // 30 secondi per tutte le richieste
});
```
## Timeout per Richiesta Specifica
```javascript
// Timeout specifico sovrascrive quello di default
await dataProvider.getList('users', {
pagination: { page: 1, perPage: 10 },
timeout: 5000 // Solo questa richiesta avrà timeout di 5 secondi
});
// Timeout per operazioni di upload (solitamente più lunghi)
await dataProvider.create('documents', {
data: { file: largeFileObject },
timeout: 60000 // 1 minuto per upload di file grandi
});
```
## Gestione Errori di Timeout
```javascript
try {
const result = await dataProvider.getList('users', {
pagination: { page: 1, perPage: 10 },
timeout: 1000 // timeout molto breve
});
} catch (error) {
if (error.message === 'error.request_timeout') {
console.log('La richiesta è andata in timeout');
// Gestisci l'errore di timeout
}
}
```
## Gestione Timeout Globale vs Locale
Il sistema di timeout del dataProvider funziona su due livelli: **globale** e **locale**.
### Timeout Globale
Il timeout globale è configurato una sola volta durante l'istanziazione del dataProvider e si applica a tutte le richieste come valore di default. È particolarmente utile per:
- Definire un timeout standard per tutta l'applicazione
- Evitare di specificare il timeout per ogni singola chiamata
- Gestire connessioni lente o instabili con un valore appropriato
```javascript
// Configurazione globale - si applica a tutte le operazioni
const dataProvider = new ApplicaDataProvider({
apiUrl: 'https://api.applica.guru/api',
timeout: 15000 // 15 secondi per TUTTE le richieste
// ... altre configurazioni
});
// Tutte queste chiamate useranno il timeout di 15 secondi
await dataProvider.getList('users', { pagination: { page: 1, perPage: 10 } });
await dataProvider.getOne('users', { id: 1 });
await dataProvider.create('users', { data: { name: 'Roberto' } });
```
### Timeout Locale
Il timeout locale è specificato per ogni singola operazione e **sovrascrive** il timeout globale solo per quella chiamata specifica. È ideale per:
- Operazioni che richiedono più tempo (upload di file, export di dati)
- Operazioni critiche che necessitano di risposta rapida
- Adattare il timeout in base al tipo di operazione
```javascript
// Il dataProvider ha un timeout globale di 15 secondi
const dataProvider = new ApplicaDataProvider({
timeout: 15000 // timeout globale
// ...
});
// Timeout locale: questa chiamata aspetterà solo 3 secondi
await dataProvider.getList('users', {
pagination: { page: 1, perPage: 10 },
timeout: 3000 // SOVRASCRIVE il timeout globale per questa chiamata
});
// Upload con timeout esteso: questa chiamata aspetterà 2 minuti
await dataProvider.create('documents', {
data: { file: largeFile },
timeout: 120000 // SOVRASCRIVE il timeout globale per questa chiamata
});
// Questa chiamata userà il timeout globale di 15 secondi (nessun timeout locale specificato)
await dataProvider.getOne('users', { id: 1 });
```
### Strategia di Timeout Consigliata
```javascript
// Configurazione ottimale per la maggior parte delle applicazioni
const dataProvider = new ApplicaDataProvider({
timeout: 30000 // 30 secondi come default sicuro
// ...
});
// Operazioni veloci - timeout ridotto
await dataProvider.getOne('config', { id: 1, timeout: 5000 });
// Operazioni normali - usa il timeout globale
await dataProvider.getList('users', { pagination: { page: 1, perPage: 10 } });
// Upload/download - timeout esteso
await dataProvider.create('files', {
data: { file: bigFile },
timeout: 300000 // 5 minuti per file molto grandi
});
// Operazioni critiche real-time - timeout molto breve
await dataProvider.get('system/status', { timeout: 2000 });
// Chiamate POST personalizzate con timeout
await dataProvider.post('analytics/report', {
startDate: '2024-01-01',
endDate: '2024-12-31',
timeout: 45000 // 45 secondi per report complessi
});
```
La **precedenza** è sempre: **Timeout Locale > Timeout Globale > Default di Sistema (30 secondi)**
**Operazioni che supportano timeout personalizzato:**
- `getList`, `getOne`, `getMany`, `getManyReference` - Tutte le operazioni di lettura
- `create`, `update`, `updateMany` - Tutte le operazioni di scrittura
- `delete`, `deleteMany` - Tutte le operazioni di eliminazione
- `get`, `post` - Chiamate generiche GET e POST
# Gestione File e Allegati
Il dataProvider include supporto integrato per il caricamento di file e allegati.
## Parser per Allegati
```javascript
import { createAttachmentsParser } from '@applica-software-guru/crud-client';
// Configurazione di base
const attachmentsParser = createAttachmentsParser();
// Configurazione personalizzata
const customAttachmentsParser = createAttachmentsParser({
images: ['avatar', 'thumbnail', 'banner'],
files: ['document', 'pdf', 'contract'],
attachments: ['gallery'] // array di file
});
```
## Upload di File
```javascript
const fileInput = document.querySelector('input[type="file"]');
const file = fileInput.files[0];
const result = await dataProvider.create('entities/document', {
data: {
title: 'Documento Importante',
file: file, // File object
images: [image1, image2], // Array di immagini
metadata: {
category: 'important'
}
}
});
```
# Gestione Errori
Il dataProvider utilizza una classe di errore configurabile per gestire gli errori HTTP.
```javascript
import { HttpError } from 'ra-core';
const dataProvider = new ApplicaDataProvider({
// ... altre configurazioni
HttpErrorClass: HttpError
});
```
## Codici di Errore Standard
- `401`: `iam.error.unauthorized` - Non autorizzato
- `403`: `iam.error.forbidden` - Accesso negato
- `404`: `error.not_found` - Risorsa non trovata
- `408`: `error.request_timeout` - Timeout richiesta
- Errori di validazione: `error.validation`
- Errori generici: `error.generic`
## Gestione degli Errori
```javascript
try {
const result = await dataProvider.getOne('users', { id: 999 });
} catch (error) {
switch (error.message) {
case 'iam.error.unauthorized':
// Reindirizza al login
break;
case 'error.not_found':
// Mostra messaggio "utente non trovato"
break;
case 'error.request_timeout':
// Mostra opzione per ritentare
break;
default:
// Errore generico
console.error('Errore:', error);
}
}
```
# Utilizzo con React-Admin
Il dataProvider è completamente compatibile con React-Admin:
```javascript
import { Admin, Resource } from 'react-admin';
import { ApplicaDataProvider } from '@applica-software-guru/crud-client';
const dataProvider = new ApplicaDataProvider({
// configurazione...
});
function App() {
return (
<Admin dataProvider={dataProvider}>
<Resource name="users" list={UserList} edit={UserEdit} create={UserCreate} />
</Admin>
);
}
```
# Migrazione dalla Versione Precedente
Se stai utilizzando la funzione deprecata `createDataProvider`, migra alla nuova sintassi:
```javascript
// DEPRECATO ❌
import { createDataProvider } from '@applica-software-guru/crud-client';
const dataProvider = createDataProvider(config);
// NUOVO ✅
import { ApplicaDataProvider } from '@applica-software-guru/crud-client';
const dataProvider = new ApplicaDataProvider(config);
```
# Esempi Avanzati
## Configurazione Completa per Produzione
```javascript
import { ApplicaDataProvider, createAttachmentsParser } from '@applica-software-guru/crud-client';
import { createAuthProvider, MemoryStorage } from '@applica-software-guru/iam-client';
import { HttpError } from 'ra-core';
const apiUrl = process.env.REACT_APP_API_URL;
const storage = new MemoryStorage();
const authProvider = createAuthProvider({ apiUrl, storage });
const dataProvider = new ApplicaDataProvider({
apiUrl,
HttpErrorClass: HttpError,
getHeaders: async () => await authProvider.getHeaders(),
getToken: async () => await authProvider.getToken(),
attachmentsParser: createAttachmentsParser({
images: ['avatar', 'banner', 'thumbnail'],
files: ['cv', 'document', 'contract'],
attachments: ['gallery', 'portfolio']
}),
timeout: 15000, // 15 secondi timeout di default
prepareData: (data, resource, params) => {
// Aggiungi timestamp a tutti i record
return {
...data,
updatedAt: new Date().toISOString()
};
}
});
export { dataProvider, authProvider };
```
## Gestione Avanzata degli Upload
```javascript
// Upload con progress tracking
const uploadDocument = async (file, onProgress) => {
try {
const result = await dataProvider.create('documents', {
data: {
title: file.name,
file: file,
category: 'user-upload'
},
timeout: 120000 // 2 minuti per file grandi
});
onProgress(100);
return result;
} catch (error) {
if (error.message === 'error.request_timeout') {
throw new Error('Upload timeout - il file potrebbe essere troppo grande');
}
throw error;
}
};
```