uni-manager 0.0.79 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/uni-manager-error.mjs +69 -0
- package/fesm2022/uni-manager-error.mjs.map +1 -0
- package/fesm2022/uni-manager-file.mjs +82 -0
- package/fesm2022/uni-manager-file.mjs.map +1 -0
- package/fesm2022/uni-manager-http.mjs +678 -0
- package/fesm2022/uni-manager-http.mjs.map +1 -0
- package/fesm2022/uni-manager-locale.mjs +168 -0
- package/fesm2022/uni-manager-locale.mjs.map +1 -0
- package/fesm2022/uni-manager-toast.mjs +57 -0
- package/fesm2022/uni-manager-toast.mjs.map +1 -0
- package/fesm2022/uni-manager-type.mjs +149 -0
- package/fesm2022/uni-manager-type.mjs.map +1 -0
- package/fesm2022/uni-manager.mjs +629 -619
- package/fesm2022/uni-manager.mjs.map +1 -1
- package/package.json +26 -2
- package/types/uni-manager-error.d.ts +27 -0
- package/types/uni-manager-file.d.ts +25 -0
- package/types/uni-manager-http.d.ts +131 -0
- package/types/uni-manager-locale.d.ts +63 -0
- package/types/uni-manager-toast.d.ts +41 -0
- package/types/uni-manager-type.d.ts +40 -0
- package/types/uni-manager.d.ts +25 -26
|
@@ -0,0 +1,678 @@
|
|
|
1
|
+
import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject';
|
|
2
|
+
import { map } from 'rxjs/internal/operators/map';
|
|
3
|
+
import { tap } from 'rxjs/internal/operators/tap';
|
|
4
|
+
import { UniToastManager } from 'uni-manager/toast';
|
|
5
|
+
import isEqual from 'lodash-es/isEqual';
|
|
6
|
+
import { defer } from 'rxjs/internal/observable/defer';
|
|
7
|
+
import { from } from 'rxjs/internal/observable/from';
|
|
8
|
+
import { timer } from 'rxjs/internal/observable/timer';
|
|
9
|
+
import { catchError } from 'rxjs/internal/operators/catchError';
|
|
10
|
+
import { distinctUntilChanged } from 'rxjs/internal/operators/distinctUntilChanged';
|
|
11
|
+
import { finalize } from 'rxjs/internal/operators/finalize';
|
|
12
|
+
import { UniErrorManager } from 'uni-manager/error';
|
|
13
|
+
import { EMPTY } from 'rxjs/internal/observable/empty';
|
|
14
|
+
import { of } from 'rxjs/internal/observable/of';
|
|
15
|
+
import { throwError } from 'rxjs/internal/observable/throwError';
|
|
16
|
+
import { concatMap } from 'rxjs/internal/operators/concatMap';
|
|
17
|
+
import { exhaustMap } from 'rxjs/internal/operators/exhaustMap';
|
|
18
|
+
import { mergeMap } from 'rxjs/internal/operators/mergeMap';
|
|
19
|
+
import { switchMap } from 'rxjs/internal/operators/switchMap';
|
|
20
|
+
import { UniHttpError, isHttpErrorBody } from 'uni-error/http';
|
|
21
|
+
import { UniTypeDateManager } from 'uni-manager/type';
|
|
22
|
+
|
|
23
|
+
var MapOperator;
|
|
24
|
+
(function (MapOperator) {
|
|
25
|
+
/** Cancella la precedente richiesta, mantiene solo l’ultima */
|
|
26
|
+
MapOperator[MapOperator["SWITCH_MAP"] = 0] = "SWITCH_MAP";
|
|
27
|
+
/** Ignora nuovi valori finché la corrente non finisce */
|
|
28
|
+
MapOperator[MapOperator["EXHAUST_MAP"] = 1] = "EXHAUST_MAP";
|
|
29
|
+
/** Mette le richieste in coda, le esegue una alla volta */
|
|
30
|
+
MapOperator[MapOperator["CONCAT_MAP"] = 2] = "CONCAT_MAP";
|
|
31
|
+
/** Esegue tutte le richieste in parallelo, ordine non garantito */
|
|
32
|
+
MapOperator[MapOperator["MERGE_MAP"] = 3] = "MERGE_MAP";
|
|
33
|
+
})(MapOperator || (MapOperator = {}));
|
|
34
|
+
var PollingErrorMode;
|
|
35
|
+
(function (PollingErrorMode) {
|
|
36
|
+
/** Salta questa iterazione emettendo un undefined. Il polling continua al tick successivo. */
|
|
37
|
+
PollingErrorMode[PollingErrorMode["IGNORE"] = 0] = "IGNORE";
|
|
38
|
+
/** Salta questa iterazione senza emettere valori. Il polling continua al tick successivo. */
|
|
39
|
+
PollingErrorMode[PollingErrorMode["SKIP"] = 1] = "SKIP";
|
|
40
|
+
/** Interrompe il polling propagando l'errore. */
|
|
41
|
+
PollingErrorMode[PollingErrorMode["STOP"] = 2] = "STOP";
|
|
42
|
+
/** Ignora l'errore, lo salva (es. per UI), e continua il polling. Emette `undefined`. */
|
|
43
|
+
PollingErrorMode[PollingErrorMode["IGNORE_WITH_ERROR"] = 3] = "IGNORE_WITH_ERROR";
|
|
44
|
+
})(PollingErrorMode || (PollingErrorMode = {}));
|
|
45
|
+
var EmitValueMode;
|
|
46
|
+
(function (EmitValueMode) {
|
|
47
|
+
/** Aggiorna lo stato solo se arrivano dati nuovi (distinct) */
|
|
48
|
+
EmitValueMode[EmitValueMode["ON_NEW_DATA"] = 0] = "ON_NEW_DATA";
|
|
49
|
+
/** Aggiorna lo stato ad ogni chiamata, anche se i dati sono uguali */
|
|
50
|
+
EmitValueMode[EmitValueMode["EVERY_TIME"] = 1] = "EVERY_TIME";
|
|
51
|
+
})(EmitValueMode || (EmitValueMode = {}));
|
|
52
|
+
|
|
53
|
+
function operatorHandler(operator) {
|
|
54
|
+
// Gestione della concorrenza in base al parametro
|
|
55
|
+
switch (operator) {
|
|
56
|
+
case MapOperator.EXHAUST_MAP: {
|
|
57
|
+
return (project) => exhaustMap(project);
|
|
58
|
+
}
|
|
59
|
+
case MapOperator.CONCAT_MAP: {
|
|
60
|
+
return (project) => concatMap(project);
|
|
61
|
+
}
|
|
62
|
+
case MapOperator.MERGE_MAP: {
|
|
63
|
+
return (project) => mergeMap(project);
|
|
64
|
+
}
|
|
65
|
+
case MapOperator.SWITCH_MAP: {
|
|
66
|
+
return (project) => switchMap(project);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function errorHandler(err, errorMode, ref) {
|
|
71
|
+
// Controllo: sia effettivamente un errore di tipo 'UniHttpError'
|
|
72
|
+
if (!(err instanceof UniHttpError)) {
|
|
73
|
+
return throwError(() => err);
|
|
74
|
+
}
|
|
75
|
+
// Aggiorna la ref nella map
|
|
76
|
+
updateHasError(ref, true);
|
|
77
|
+
// Gestione dell'errore in base al parametro
|
|
78
|
+
switch (errorMode) {
|
|
79
|
+
case PollingErrorMode.IGNORE: {
|
|
80
|
+
return of();
|
|
81
|
+
}
|
|
82
|
+
case PollingErrorMode.SKIP: {
|
|
83
|
+
return EMPTY;
|
|
84
|
+
}
|
|
85
|
+
case PollingErrorMode.IGNORE_WITH_ERROR: {
|
|
86
|
+
UniErrorManager.add(ref, err);
|
|
87
|
+
return of();
|
|
88
|
+
}
|
|
89
|
+
case PollingErrorMode.STOP: {
|
|
90
|
+
UniErrorManager.add(ref, err);
|
|
91
|
+
return throwError(() => err);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* ------------------------------------------------------------------------------- */
|
|
97
|
+
/* -------------------------------- Funzioni Core RxJS -------------------------- */
|
|
98
|
+
/* ------------------------------------------------------------------------------- */
|
|
99
|
+
/**
|
|
100
|
+
* Gestisce l'esecuzione e il ciclo di vita di una singola richiesta HTTP.
|
|
101
|
+
* Si occupa della registrazione della reference, della gestione dei loader, dei toast di successo e dell'intercettazione degli errori.
|
|
102
|
+
*/
|
|
103
|
+
function http$(url, refType, config, promiseFactory) {
|
|
104
|
+
/* Recupero configurazione */
|
|
105
|
+
const { ref, toast, hasLoader } = config;
|
|
106
|
+
return defer(() => {
|
|
107
|
+
const http$ = from(promiseFactory());
|
|
108
|
+
return http$.pipe(tap({
|
|
109
|
+
subscribe: () => {
|
|
110
|
+
/* Creazione reference */
|
|
111
|
+
add(ref, url, refType);
|
|
112
|
+
/* Incrementa per il loader */
|
|
113
|
+
if (hasLoader !== false) {
|
|
114
|
+
updateIsLoading(ref, 1);
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
unsubscribe: () => {
|
|
118
|
+
/* Rimozione reference */
|
|
119
|
+
remove(ref);
|
|
120
|
+
},
|
|
121
|
+
next: (res) => {
|
|
122
|
+
/* Mostra toast (se presente) */
|
|
123
|
+
if (toast) {
|
|
124
|
+
UniToastManager.showHttp(toast, res);
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
}), catchError((err) => {
|
|
128
|
+
/* Gestione errore */
|
|
129
|
+
return errorHandler(err, PollingErrorMode.STOP, ref);
|
|
130
|
+
}), finalize(() => {
|
|
131
|
+
/* Decrementa per il loader */
|
|
132
|
+
if (hasLoader !== false) {
|
|
133
|
+
updateIsLoading(ref, -1);
|
|
134
|
+
}
|
|
135
|
+
}));
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Avvia e coordina un ciclo di polling a intervalli regolari.
|
|
140
|
+
* Gestisce la concorrenza tramite operatori RxJS configurabili, la rimozione dei popup, di errore nelle iterazioni successive e i comportamenti custom al primo avvio.
|
|
141
|
+
*/
|
|
142
|
+
function httpPolling$(url, config, promiseFactory) {
|
|
143
|
+
/* Recupero configurazione */
|
|
144
|
+
const { interval, ref, operator = MapOperator.SWITCH_MAP, errorMode = PollingErrorMode.STOP, emitValueMode = EmitValueMode.ON_NEW_DATA, firstIteration, } = config;
|
|
145
|
+
const timer$ = timer(0, interval);
|
|
146
|
+
const source$ = timer$.pipe(tap({
|
|
147
|
+
subscribe: () => {
|
|
148
|
+
/* Creazione reference */
|
|
149
|
+
add(ref, url, 'polling');
|
|
150
|
+
},
|
|
151
|
+
unsubscribe: () => {
|
|
152
|
+
/* Rimozione reference */
|
|
153
|
+
remove(ref);
|
|
154
|
+
},
|
|
155
|
+
}), operatorHandler(operator)((index) => {
|
|
156
|
+
/* Incrementa per il loader */
|
|
157
|
+
if (index === 0 && firstIteration?.hasLoader !== false) {
|
|
158
|
+
updateIsLoading(ref, 1);
|
|
159
|
+
}
|
|
160
|
+
return defer(() => {
|
|
161
|
+
const http$ = from(promiseFactory());
|
|
162
|
+
return http$.pipe(tap((res) => {
|
|
163
|
+
/* Meccanismo di ripristino automatico: se il polling torna in salute, cancella l'errore dallo store e nasconde i relativi messaggi a schermo */
|
|
164
|
+
if (errorMode === PollingErrorMode.IGNORE_WITH_ERROR) {
|
|
165
|
+
updateHasError(ref, false);
|
|
166
|
+
UniErrorManager.remove(ref);
|
|
167
|
+
}
|
|
168
|
+
/* Prima risposta */
|
|
169
|
+
if (index === 0 && firstIteration?.toast) {
|
|
170
|
+
UniToastManager.showHttp(firstIteration.toast, res);
|
|
171
|
+
}
|
|
172
|
+
}), catchError((err) => {
|
|
173
|
+
/* Gestione errore */
|
|
174
|
+
return errorHandler(err, errorMode, ref);
|
|
175
|
+
}), finalize(() => {
|
|
176
|
+
/* Decrementa per il loader */
|
|
177
|
+
if (index === 0 && firstIteration?.hasLoader !== false) {
|
|
178
|
+
updateIsLoading(ref, -1);
|
|
179
|
+
}
|
|
180
|
+
}));
|
|
181
|
+
});
|
|
182
|
+
}));
|
|
183
|
+
return emitValueMode === EmitValueMode.ON_NEW_DATA
|
|
184
|
+
? source$.pipe(distinctUntilChanged((prev, cur) => isEqual(prev, cur)))
|
|
185
|
+
: source$;
|
|
186
|
+
}
|
|
187
|
+
/* ------------------------------------------------------------------------------- */
|
|
188
|
+
/* ------------------------------------ Utils ------------------------------------ */
|
|
189
|
+
/* ------------------------------------------------------------------------------- */
|
|
190
|
+
/**
|
|
191
|
+
* Aggiunge una nuova ref nello store solo se non è già presente.
|
|
192
|
+
* Se l'ID esiste già, l'operazione viene interrotta per preservare il dato originale.
|
|
193
|
+
*/
|
|
194
|
+
function add(id, url, type) {
|
|
195
|
+
// Recupera l'ultimo stato (Map)
|
|
196
|
+
const oldMap = UniHttpManager.currentValue;
|
|
197
|
+
// Controllo: se è presente l'item allora termina
|
|
198
|
+
if (oldMap.get(id))
|
|
199
|
+
return;
|
|
200
|
+
// Crea il nuovo oggetto
|
|
201
|
+
const newItemMap = {
|
|
202
|
+
type,
|
|
203
|
+
lineId: undefined,
|
|
204
|
+
url,
|
|
205
|
+
hasError: false,
|
|
206
|
+
pendingCount: 0,
|
|
207
|
+
};
|
|
208
|
+
// Crea una nuova istanza della Map
|
|
209
|
+
const newMap = new Map(oldMap);
|
|
210
|
+
newMap.set(id, newItemMap);
|
|
211
|
+
// Aggiorna il nuovo stato notificando l'observer
|
|
212
|
+
UniHttpManager.store.next(newMap);
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Rimuove un http ref tramite ID
|
|
216
|
+
*/
|
|
217
|
+
function remove(id) {
|
|
218
|
+
// Recupera l'ultimo stato (Map)
|
|
219
|
+
const oldMap = UniHttpManager.currentValue;
|
|
220
|
+
// Controllo: se non è presente l'item allora termina
|
|
221
|
+
if (!oldMap.has(id))
|
|
222
|
+
return;
|
|
223
|
+
// Aggiorna store
|
|
224
|
+
const newMap = new Map(oldMap);
|
|
225
|
+
newMap.delete(id);
|
|
226
|
+
UniHttpManager.store.next(newMap);
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Aggiorna il contatore delle chiamate pendenti per una specifica ref.
|
|
230
|
+
* Incrementa o decrementa 'pendingCount' garantendo che non scenda mai sotto lo zero.
|
|
231
|
+
* Se la ref non esiste, l'operazione viene ignorata.
|
|
232
|
+
*/
|
|
233
|
+
function updateIsLoading(id, delta) {
|
|
234
|
+
// Recupera l'ultimo stato (Map)
|
|
235
|
+
const oldMap = UniHttpManager.currentValue;
|
|
236
|
+
// Controllo: se non è presente l'item allora termina
|
|
237
|
+
const oldItem = oldMap.get(id);
|
|
238
|
+
if (!oldItem)
|
|
239
|
+
return;
|
|
240
|
+
// Aggiorna l'oggetto
|
|
241
|
+
const newItemMap = {
|
|
242
|
+
...oldItem,
|
|
243
|
+
pendingCount: Math.max(0, oldItem.pendingCount + delta),
|
|
244
|
+
};
|
|
245
|
+
// Crea una nuova istanza della Map
|
|
246
|
+
const newMap = new Map(oldMap);
|
|
247
|
+
newMap.set(id, newItemMap);
|
|
248
|
+
// Aggiorna il nuovo stato notificando l'observer
|
|
249
|
+
UniHttpManager.store.next(newMap);
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Aggiorna lo stato di errore per una specifica ref.
|
|
253
|
+
* Se il valore di 'hasError' è identico a quello attuale o se la ref non esiste,
|
|
254
|
+
* l'operazione viene interrotta per evitare aggiornamenti inutili.
|
|
255
|
+
*/
|
|
256
|
+
function updateHasError(id, hasError) {
|
|
257
|
+
// Recupera l'ultimo stato (Map)
|
|
258
|
+
const oldMap = UniHttpManager.currentValue;
|
|
259
|
+
// Controllo: se non è presente l'item allora termina
|
|
260
|
+
const oldItem = oldMap.get(id);
|
|
261
|
+
if (!oldItem)
|
|
262
|
+
return;
|
|
263
|
+
// Controllo: se con lo stesso valore, salta
|
|
264
|
+
if (oldItem.hasError === hasError)
|
|
265
|
+
return;
|
|
266
|
+
// Aggiorna l'oggetto
|
|
267
|
+
const newItemMap = { ...oldItem, hasError };
|
|
268
|
+
// Crea una nuova istanza della Map
|
|
269
|
+
const newMap = new Map(oldMap);
|
|
270
|
+
newMap.set(id, newItemMap);
|
|
271
|
+
// Aggiorna il nuovo stato notificando l'observer
|
|
272
|
+
UniHttpManager.store.next(newMap);
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Svuota completamente la lista delle refs
|
|
276
|
+
*/
|
|
277
|
+
function removeAll() {
|
|
278
|
+
// Crea una nuova istanza della Map
|
|
279
|
+
const newMap = new Map();
|
|
280
|
+
// Aggiorna il nuovo stato notificando l'observer
|
|
281
|
+
UniHttpManager.store.next(newMap);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
async function execute(url, init) {
|
|
285
|
+
try {
|
|
286
|
+
/* Esegue chiamata http */
|
|
287
|
+
const res = await fetch(url, init);
|
|
288
|
+
/* Nessun contenuto cons status 204 */
|
|
289
|
+
if (res.status === 204) {
|
|
290
|
+
return undefined;
|
|
291
|
+
}
|
|
292
|
+
/* Recupero se è un json */
|
|
293
|
+
const contentType = res.headers.get('content-type') ?? '';
|
|
294
|
+
const isJson = contentType.startsWith('application/json');
|
|
295
|
+
/* Errore HTTP (quindi risposta ricevuta ma non ok) */
|
|
296
|
+
if (!res.ok) {
|
|
297
|
+
let errorBody;
|
|
298
|
+
try {
|
|
299
|
+
/* Clona risposta solo se c'è errore, per leggerla senza consumare la originale */
|
|
300
|
+
const resClone = res.clone();
|
|
301
|
+
/* Aggiorna body con struttura errore */
|
|
302
|
+
errorBody = isJson ? await resClone.json() : await resClone.text();
|
|
303
|
+
}
|
|
304
|
+
catch {
|
|
305
|
+
errorBody = await res.text();
|
|
306
|
+
}
|
|
307
|
+
if (isHttpErrorBody(errorBody)) {
|
|
308
|
+
const type = errorBody.exceptionType.includes('HttpRequestException') ? 'be' : 'ice';
|
|
309
|
+
throw new UniHttpError(type, res.status, url, errorBody);
|
|
310
|
+
}
|
|
311
|
+
else {
|
|
312
|
+
const error = new Error(`HTTP ${res.statusText}`);
|
|
313
|
+
throw new UniHttpError('base', res.status, url, {
|
|
314
|
+
exceptionMessage: error.message,
|
|
315
|
+
exceptionType: 'ErrorBase',
|
|
316
|
+
message: error.message,
|
|
317
|
+
stackTrace: error.stack ?? '',
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
/* Risposta ok */
|
|
322
|
+
return res;
|
|
323
|
+
}
|
|
324
|
+
catch (error) {
|
|
325
|
+
if (error instanceof TypeError) {
|
|
326
|
+
throw new UniHttpError('network', -1, url, {
|
|
327
|
+
exceptionMessage: `${error.name}: ${error.message}\n${error.stack}`,
|
|
328
|
+
exceptionType: error.name,
|
|
329
|
+
message: error.message,
|
|
330
|
+
stackTrace: error.stack ?? '',
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
// Fallback
|
|
334
|
+
throw error;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
async function executeHttp(url, init) {
|
|
338
|
+
/* Esegue la chiamata HTTP e restituisce il corpo decodificato come JSON */
|
|
339
|
+
const res = await execute(url, init);
|
|
340
|
+
if (!res)
|
|
341
|
+
return undefined;
|
|
342
|
+
/* Estrae il contenuto come testo */
|
|
343
|
+
const text = await res.text();
|
|
344
|
+
/* Controllo: se il testo è vuoto (''), ritorna undefined */
|
|
345
|
+
if (!text || text.trim().length === 0) {
|
|
346
|
+
return undefined;
|
|
347
|
+
}
|
|
348
|
+
/* Parsa manualmente il testo, dato che lo stream è stato già letto */
|
|
349
|
+
return JSON.parse(text);
|
|
350
|
+
}
|
|
351
|
+
async function executeBlob(url, init) {
|
|
352
|
+
/* Esegue la chiamata HTTP */
|
|
353
|
+
const res = await execute(url, init);
|
|
354
|
+
if (!res)
|
|
355
|
+
return undefined;
|
|
356
|
+
/* Estrae il contenuto come Blob direttamente */
|
|
357
|
+
const blob = await res.blob();
|
|
358
|
+
/* Controllo: se il blob è vuoto (0 byte), ritorna undefined */
|
|
359
|
+
if (blob.size === 0) {
|
|
360
|
+
return undefined;
|
|
361
|
+
}
|
|
362
|
+
/* Estrae il nome del file dall'header */
|
|
363
|
+
const disposition = res.headers.get('Content-Disposition');
|
|
364
|
+
let fileName = 'download.pdf'; // Fallback di default
|
|
365
|
+
if (disposition) {
|
|
366
|
+
const matches = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/.exec(disposition);
|
|
367
|
+
if (!!matches && matches[1]) {
|
|
368
|
+
fileName = matches[1].replaceAll(/['"]/g, '');
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
/* Genera l'URL temporaneo dal blob */
|
|
372
|
+
const fileUrl = URL.createObjectURL(blob);
|
|
373
|
+
return { url: fileUrl, name: fileName };
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Costruisce un URL completo per l'API partendo dai parametri di configurazione.
|
|
378
|
+
*/
|
|
379
|
+
function getUrl(hostname, port, config) {
|
|
380
|
+
const { params, path, hasApiPrefix = true } = config;
|
|
381
|
+
// Costruzione url
|
|
382
|
+
const prefix = hasApiPrefix ? 'api' : '';
|
|
383
|
+
const cleanPath = path.startsWith('/') ? path.slice(1) : path;
|
|
384
|
+
const pathFixed = prefix ? `${prefix}/${cleanPath}` : cleanPath;
|
|
385
|
+
const url = new URL(pathFixed, `http://${hostname}:${port}`);
|
|
386
|
+
if (params) {
|
|
387
|
+
// Regex per intercettare stringhe in formato ISO string
|
|
388
|
+
const isoDateRegex = /^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})?)?$/;
|
|
389
|
+
for (const [key, rawValue] of Object.entries(params)) {
|
|
390
|
+
if (rawValue === undefined || rawValue === null) {
|
|
391
|
+
continue;
|
|
392
|
+
}
|
|
393
|
+
// Conversione in array per usare un solo ciclo
|
|
394
|
+
const valuesArray = Array.isArray(rawValue) ? rawValue : [rawValue];
|
|
395
|
+
for (const item of valuesArray) {
|
|
396
|
+
if (item === undefined || item === null) {
|
|
397
|
+
continue;
|
|
398
|
+
}
|
|
399
|
+
let formattedValue;
|
|
400
|
+
// Caso: Date nativo
|
|
401
|
+
if (item instanceof Date) {
|
|
402
|
+
formattedValue = UniTypeDateManager.toYYYYMMDD(item);
|
|
403
|
+
}
|
|
404
|
+
// Caso: Date formato ISO string
|
|
405
|
+
else if (typeof item === 'string' && isoDateRegex.test(item)) {
|
|
406
|
+
const parsedDate = new Date(item);
|
|
407
|
+
formattedValue = Number.isNaN(parsedDate.getTime())
|
|
408
|
+
? item
|
|
409
|
+
: UniTypeDateManager.toYYYYMMDD(parsedDate);
|
|
410
|
+
}
|
|
411
|
+
// Default
|
|
412
|
+
else {
|
|
413
|
+
formattedValue = String(item);
|
|
414
|
+
}
|
|
415
|
+
url.searchParams.append(key, formattedValue);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
return url;
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* Normalizza i valori del body cosi da sistemare le incongruenze tra ui e db
|
|
423
|
+
*/
|
|
424
|
+
function normalizeHttpBody(body, isRoot = true) {
|
|
425
|
+
// PRIMITIVI GENERICI
|
|
426
|
+
// Tipi gestiti: null, undefined, number, boolean, symbol, bigint, string
|
|
427
|
+
if (body === null || typeof body !== 'object') {
|
|
428
|
+
// Sotto-gestione specifica per il tipo: string
|
|
429
|
+
if (typeof body === 'string') {
|
|
430
|
+
return body.trim();
|
|
431
|
+
}
|
|
432
|
+
return body;
|
|
433
|
+
}
|
|
434
|
+
// DATE
|
|
435
|
+
// Tipi gestiti: Date
|
|
436
|
+
// Formattazione esplicita manuale in YYYY-MM-DD
|
|
437
|
+
if (body instanceof Date) {
|
|
438
|
+
const year = body.getFullYear();
|
|
439
|
+
const month = String(body.getMonth() + 1).padStart(2, '0');
|
|
440
|
+
const day = String(body.getDate()).padStart(2, '0');
|
|
441
|
+
return `${year}-${month}-${day}`;
|
|
442
|
+
}
|
|
443
|
+
// STRUTTURE DATI ITERABILI
|
|
444
|
+
// Tipi gestiti: Array (qualsiasi array, es. string[], number[], Object[])
|
|
445
|
+
// Se è un array, viene mappato ricorsivamente ogni singolo elemento al suo interno (passando isRoot = false perché gli elementi dell'array non sono l'oggetto root principale)
|
|
446
|
+
if (Array.isArray(body)) {
|
|
447
|
+
return body.map((item) => normalizeHttpBody(item, false));
|
|
448
|
+
}
|
|
449
|
+
// OGGETTI
|
|
450
|
+
// Tipi gestiti: Record<string, any>, generici oggetti JavaScript ({ })
|
|
451
|
+
// Rimuove chiave 'id' al primo livello e prosegue ricorsivamente
|
|
452
|
+
const entries = Object.entries(body)
|
|
453
|
+
.filter(([key]) => !(isRoot && key.toLowerCase() === 'id'))
|
|
454
|
+
.map(([key, value]) => [key, normalizeHttpBody(value, false)]);
|
|
455
|
+
// Ricostruisce l'oggetto normalizzato
|
|
456
|
+
return Object.fromEntries(entries);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
const CONFIG_TOAST_DEFAULT = {
|
|
460
|
+
type: 'success',
|
|
461
|
+
label: 'OperationCompleted',
|
|
462
|
+
};
|
|
463
|
+
class UniHttpManager {
|
|
464
|
+
/* ------------------------------------------------------------------------------- */
|
|
465
|
+
/* --------------------------------- Metodi: get --------------------------------- */
|
|
466
|
+
/* ------------------------------------------------------------------------------- */
|
|
467
|
+
/** Restituisce se lo store ha chiamate in attesa di risposta o meno */
|
|
468
|
+
static get hasWaitingRequests$() {
|
|
469
|
+
return this.store$.pipe(map((x) => [...x.values()].some((y) => y.pendingCount > 0)));
|
|
470
|
+
}
|
|
471
|
+
/* ------------------------------------------------------------------------------- */
|
|
472
|
+
/* ------------------------------------ Store ------------------------------------ */
|
|
473
|
+
/* ------------------------------------------------------------------------------- */
|
|
474
|
+
/** Store privato (Subject) */
|
|
475
|
+
static { this.store = new BehaviorSubject(new Map()); }
|
|
476
|
+
/** Store pubblico (Observable) */
|
|
477
|
+
static { this.store$ = this.store.asObservable(); }
|
|
478
|
+
/** Ottiene lo stato attuale della Map senza dover sottoscrivere l'observable */
|
|
479
|
+
static get currentValue() {
|
|
480
|
+
return this.store.getValue();
|
|
481
|
+
}
|
|
482
|
+
/* ------------------------------------------------------------------------------- */
|
|
483
|
+
/* -------------------------------- Metodi: setup -------------------------------- */
|
|
484
|
+
/* ------------------------------------------------------------------------------- */
|
|
485
|
+
/**
|
|
486
|
+
* Inizializza la configurazione di rete del manager.
|
|
487
|
+
* Deve essere chiamato prima di effettuare qualsiasi richiesta HTTP.
|
|
488
|
+
*/
|
|
489
|
+
static setup(hostname, port) {
|
|
490
|
+
this.hostname = hostname;
|
|
491
|
+
this.port = port;
|
|
492
|
+
}
|
|
493
|
+
/* ------------------------------------------------------------------------------- */
|
|
494
|
+
/* -------------------------------- Metodi: CRUD --------------------------------- */
|
|
495
|
+
/* ------------------------------------------------------------------------------- */
|
|
496
|
+
/*
|
|
497
|
+
* Esegue una singola richiesta HTTP GET.
|
|
498
|
+
* Utilizza il path configurato per costruire l'URL completo e restituisce un Observable del risultato.
|
|
499
|
+
*/
|
|
500
|
+
static read$(config) {
|
|
501
|
+
/* Config */
|
|
502
|
+
const initCustom = {
|
|
503
|
+
...config.init,
|
|
504
|
+
method: 'GET',
|
|
505
|
+
};
|
|
506
|
+
/* Chiama API */
|
|
507
|
+
const url = getUrl(this.hostname, this.port, config);
|
|
508
|
+
return http$(url, 'one', config, () => executeHttp(url, initCustom)).pipe(tap((res) => {
|
|
509
|
+
if (Array.isArray(res) && config.toast === undefined) {
|
|
510
|
+
UniToastManager.show({
|
|
511
|
+
label: 'ItemsFound',
|
|
512
|
+
params: { count: res.length },
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
}));
|
|
516
|
+
}
|
|
517
|
+
/**
|
|
518
|
+
* Esegue una richiesta HTTP POST inviando un payload JSON nel corpo della richiesta.
|
|
519
|
+
* Imposta automaticamente l'header 'Content-Type' come 'application/json'.
|
|
520
|
+
*/
|
|
521
|
+
static create$(config, body) {
|
|
522
|
+
/* Config */
|
|
523
|
+
const initCustom = {
|
|
524
|
+
...config.init,
|
|
525
|
+
method: 'POST',
|
|
526
|
+
headers: {
|
|
527
|
+
'Content-Type': 'application/json',
|
|
528
|
+
...config.init?.headers,
|
|
529
|
+
},
|
|
530
|
+
body: JSON.stringify(normalizeHttpBody(body)),
|
|
531
|
+
};
|
|
532
|
+
/* Toast di default */
|
|
533
|
+
const configCustom = {
|
|
534
|
+
...config,
|
|
535
|
+
toast: config.toast === null ? undefined : (config.toast ?? CONFIG_TOAST_DEFAULT),
|
|
536
|
+
};
|
|
537
|
+
/* Chiama API */
|
|
538
|
+
const url = getUrl(this.hostname, this.port, config);
|
|
539
|
+
return http$(url, 'one', configCustom, () => executeHttp(url, initCustom));
|
|
540
|
+
}
|
|
541
|
+
/**
|
|
542
|
+
* Esegue una singola richiesta HTTP PUT per aggiornare una risorsa esistente.
|
|
543
|
+
*/
|
|
544
|
+
static update$(config, body) {
|
|
545
|
+
/* Config */
|
|
546
|
+
const initCustom = { ...config.init, method: 'PUT' };
|
|
547
|
+
if (body !== undefined) {
|
|
548
|
+
initCustom.headers = {
|
|
549
|
+
'Content-Type': 'application/json',
|
|
550
|
+
...config.init?.headers,
|
|
551
|
+
};
|
|
552
|
+
initCustom.body = JSON.stringify(normalizeHttpBody(body));
|
|
553
|
+
}
|
|
554
|
+
/* Toast di default */
|
|
555
|
+
const configCustom = {
|
|
556
|
+
...config,
|
|
557
|
+
toast: config.toast === null ? undefined : (config.toast ?? CONFIG_TOAST_DEFAULT),
|
|
558
|
+
};
|
|
559
|
+
/* Chiama API */
|
|
560
|
+
const url = getUrl(this.hostname, this.port, config);
|
|
561
|
+
return http$(url, 'one', configCustom, () => executeHttp(url, initCustom));
|
|
562
|
+
}
|
|
563
|
+
/**
|
|
564
|
+
* Esegue una richiesta HTTP DELETE per rimuovere una risorsa.
|
|
565
|
+
* Utilizza il path configurato per costruire l'URL completo e restituisce un Observable del risultato.
|
|
566
|
+
*/
|
|
567
|
+
static delete$(config) {
|
|
568
|
+
/* Config */
|
|
569
|
+
const initCustom = {
|
|
570
|
+
...config.init,
|
|
571
|
+
method: 'DELETE',
|
|
572
|
+
};
|
|
573
|
+
/* Toast di default */
|
|
574
|
+
const configCustom = {
|
|
575
|
+
...config,
|
|
576
|
+
toast: config.toast === null ? undefined : (config.toast ?? CONFIG_TOAST_DEFAULT),
|
|
577
|
+
};
|
|
578
|
+
/* Chiama API */
|
|
579
|
+
const url = getUrl(this.hostname, this.port, config);
|
|
580
|
+
return http$(url, 'one', configCustom, () => executeHttp(url, initCustom));
|
|
581
|
+
}
|
|
582
|
+
/* ------------------------------------------------------------------------------- */
|
|
583
|
+
/* -------------------------- Metodi: CRUD file/image ---------------------------- */
|
|
584
|
+
/* ------------------------------------------------------------------------------- */
|
|
585
|
+
/**
|
|
586
|
+
* Recupera un'immagine tramite una richiesta GET e la trasforma in un formato gestibile (es. Blob o Base64).
|
|
587
|
+
* Delega la logica di conversione alla funzione executeImage.
|
|
588
|
+
*/
|
|
589
|
+
static readImage$(config) {
|
|
590
|
+
/* Config */
|
|
591
|
+
const initCustom = {
|
|
592
|
+
...config.init,
|
|
593
|
+
method: 'GET',
|
|
594
|
+
};
|
|
595
|
+
/* Chiama API */
|
|
596
|
+
const url = getUrl(this.hostname, this.port, config);
|
|
597
|
+
return http$(url, 'one', config, () => executeBlob(url, initCustom));
|
|
598
|
+
}
|
|
599
|
+
/**
|
|
600
|
+
* Recupera un file (es. PDF) tramite una richiesta GET e restituisce un Object URL temporaneo.
|
|
601
|
+
* Delega la logica di conversione alla funzione executeFile.
|
|
602
|
+
*/
|
|
603
|
+
static readFile$(config) {
|
|
604
|
+
/* Config */
|
|
605
|
+
const initCustom = {
|
|
606
|
+
...config.init,
|
|
607
|
+
method: 'GET',
|
|
608
|
+
};
|
|
609
|
+
/* Toast di default */
|
|
610
|
+
const configCustom = {
|
|
611
|
+
...config,
|
|
612
|
+
toast: config.toast === null ? undefined : (config.toast ?? CONFIG_TOAST_DEFAULT),
|
|
613
|
+
};
|
|
614
|
+
/* Chiama API */
|
|
615
|
+
const url = getUrl(this.hostname, this.port, config);
|
|
616
|
+
return http$(url, 'file', configCustom, () => executeBlob(url, initCustom));
|
|
617
|
+
}
|
|
618
|
+
/* ------------------------------------------------------------------------------- */
|
|
619
|
+
/* ---------------------------- Metodi: CRUD polling ----------------------------- */
|
|
620
|
+
/* ------------------------------------------------------------------------------- */
|
|
621
|
+
/**
|
|
622
|
+
* Avvia un ciclo di polling basato su richieste GET.
|
|
623
|
+
* Continua a emettere valori in base alla configurazione di intervallo definita in HttpConfigPolling.
|
|
624
|
+
*/
|
|
625
|
+
static readPolling$(config) {
|
|
626
|
+
/* Config */
|
|
627
|
+
const initCustom = {
|
|
628
|
+
...config.init,
|
|
629
|
+
method: 'GET',
|
|
630
|
+
};
|
|
631
|
+
/* Chiama API */
|
|
632
|
+
const url = getUrl(this.hostname, this.port, config);
|
|
633
|
+
return httpPolling$(url, config, () => executeHttp(url, initCustom));
|
|
634
|
+
}
|
|
635
|
+
/**
|
|
636
|
+
* Avvia un ciclo di polling basato su richieste POST.
|
|
637
|
+
* Invia il body specificato a ogni iterazione del ciclo.
|
|
638
|
+
*/
|
|
639
|
+
static createPolling$(config, body) {
|
|
640
|
+
/* Config */
|
|
641
|
+
const initCustom = {
|
|
642
|
+
...config.init,
|
|
643
|
+
method: 'POST',
|
|
644
|
+
headers: { 'Content-Type': 'application/json' },
|
|
645
|
+
body: JSON.stringify(body),
|
|
646
|
+
};
|
|
647
|
+
/* Chiama API */
|
|
648
|
+
const url = getUrl(this.hostname, this.port, config);
|
|
649
|
+
return httpPolling$(url, config, () => executeHttp(url, initCustom));
|
|
650
|
+
}
|
|
651
|
+
/**
|
|
652
|
+
* Avvia un ciclo di polling basato su richieste PUT.
|
|
653
|
+
*/
|
|
654
|
+
static updatePolling$(config, body) {
|
|
655
|
+
/* Config */
|
|
656
|
+
const initCustom = {
|
|
657
|
+
...config.init,
|
|
658
|
+
method: 'PUT',
|
|
659
|
+
};
|
|
660
|
+
if (body !== undefined) {
|
|
661
|
+
initCustom.headers = {
|
|
662
|
+
...initCustom.headers,
|
|
663
|
+
'Content-Type': 'application/json',
|
|
664
|
+
};
|
|
665
|
+
initCustom.body = JSON.stringify(body);
|
|
666
|
+
}
|
|
667
|
+
/* Chiama API */
|
|
668
|
+
const url = getUrl(this.hostname, this.port, config);
|
|
669
|
+
return httpPolling$(url, config, () => executeHttp(url, initCustom));
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
/**
|
|
674
|
+
* Generated bundle index. Do not edit.
|
|
675
|
+
*/
|
|
676
|
+
|
|
677
|
+
export { EmitValueMode, MapOperator, PollingErrorMode, UniHttpManager };
|
|
678
|
+
//# sourceMappingURL=uni-manager-http.mjs.map
|