@esfaenza/httpservice 13.1.3 → 13.1.7

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.
@@ -3,7 +3,8 @@ import { HttpEventType, HttpHeaders, HttpErrorResponse } from '@angular/common/h
3
3
  import * as i0 from '@angular/core';
4
4
  import { InjectionToken, Injectable, Inject, Optional, NgModule } from '@angular/core';
5
5
  import { delay, finalize, map, catchError, scan } from 'rxjs/operators';
6
- import { of, throwError, Observable } from 'rxjs';
6
+ import { forkJoin, of, throwError, Observable } from 'rxjs';
7
+ import * as i3 from '@esfaenza/preferences';
7
8
 
8
9
  /**
9
10
  * Dizionario di invalidazioni delle chiamate HTTP
@@ -133,9 +134,24 @@ const Loc = {
133
134
  "Redirecting to Login, ignoring HTTP error on call": { "en-US": "Redirecting to Login, ignoring HTTP error on call", "it-IT": "Redireziono al login ignorando errori sulla chiamata", },
134
135
  "Already Handling an error, ignoring HTTP error on call": { "en-US": "Already Handling an error, ignoring HTTP error on call", "it-IT": "sto già gestendo un errore, ignoro quello corrente per la chiamata HTTP", },
135
136
  "Error 444 on call": { "en-US": "Error 444 on call", "it-IT": "Errore 444 su chiamata" },
136
- "Resource not found": { "en-US": "Resource not found", "it-IT": "Risorsa non trovata" }
137
+ "Resource not found": { "en-US": "Resource not found", "it-IT": "Risorsa non trovata" },
138
+ "Converted old storage system to new one": { "en-US": "Converted old storage system to new one", "it-IT": "Convertito il vecchio sistema di storicizzazione al nuovo" },
139
+ "Marking cache to be persisted as soon as possible": { "en-US": "Marking cache to be persisted as soon as possible", "it-IT": "Marco cache da persistere il prima possibile" },
140
+ "No more HTTP calls to be executed, entering idle state and persisting modified caches": { "en-US": "No more HTTP calls to be executed, entering idle state and persisting modified caches", "it-IT": "Nessun'altra chiamata HTTP dev'essere eseguita, entro in stato IDLE e persisto le cache modificate" },
141
+ "Caches succesfully persisted": { "en-US": "Caches succesfully persisted", "it-IT": "Le cache sono state persistite con successo" },
142
+ "Errors persisting modified caches": { "en-US": "Errors persisting modified caches", "it-IT": "Errori nella persistizione delle cache (esiste persistizione? boh...)" },
137
143
  };
138
144
 
145
+ class CacheInfos {
146
+ constructor(finalUrl, cache, cacheKey, cacheTime, cacheItem) {
147
+ this.finalUrl = finalUrl;
148
+ this.cache = cache;
149
+ this.cacheKey = cacheKey;
150
+ this.cacheTime = cacheTime;
151
+ this.cacheItem = cacheItem;
152
+ }
153
+ }
154
+
139
155
  // Angular
140
156
  /**
141
157
  * Servizio utilizzato per effettuare qualsiasi tipo di chiamata HTTP dall'applicazione, gestisce errori e cache delle risposte nativamente e ne può essere
@@ -145,7 +161,7 @@ class HTTPService {
145
161
  /**
146
162
  * @ignore
147
163
  */
148
- constructor(BACKEND_URL, EXTENSION_CLASS, HTTP_INVALIDATION, LOCALE, APP_NAME, CLIENT_CACHE_DURATION_S, CACHE_BY_USER, DEBUG_MODE, http) {
164
+ constructor(BACKEND_URL, EXTENSION_CLASS, HTTP_INVALIDATION, LOCALE, APP_NAME, CLIENT_CACHE_DURATION_S, CACHE_BY_USER, DEBUG_MODE, http, localStorage) {
149
165
  this.BACKEND_URL = BACKEND_URL;
150
166
  this.EXTENSION_CLASS = EXTENSION_CLASS;
151
167
  this.HTTP_INVALIDATION = HTTP_INVALIDATION;
@@ -155,34 +171,49 @@ class HTTPService {
155
171
  this.CACHE_BY_USER = CACHE_BY_USER;
156
172
  this.DEBUG_MODE = DEBUG_MODE;
157
173
  this.http = http;
158
- /**
159
- * Cache dello stato delle chiamate HTTP attualmente effettuate. Ogni chiamata è rappresentata da un GUID e lo status assume valore **false** solo sugli errori
160
- */
174
+ this.localStorage = localStorage;
175
+ /** Cache dello stato delle chiamate HTTP attualmente effettuate. Ogni chiamata è rappresentata da un GUID e lo status assume valore **false** solo sugli errori */
161
176
  this.callStatus = {};
162
- /**
163
- * Nome della cache in cui vengono storicizzate le informazioni sulle risposte delle chiamate sotto forma di dictionary
164
- */
177
+ /** Nome della cache in cui vengono storicizzate le informazioni sulle risposte delle chiamate sotto forma di dictionary */
165
178
  this.localStorageHttpCacheKey = null;
166
- /**
167
- * Nome della cache in cui vengono storicizzate le informazioni sulle risposte delle chiamate sotto forma di dictionary, solo per le chiamate con un tempo definito di caching
168
- */
179
+ /** Nome della cache in cui vengono storicizzate le informazioni sulle risposte delle chiamate sotto forma di dictionary, solo per le chiamate con un tempo definito di caching */
169
180
  this.localStorageHttpTimedCacheKey = null;
170
- /**
171
- * Nome della cache in cui vengono storicizzate le informazioni sullo scadere della cache stessa
172
- */
181
+ /** Nome della cache in cui vengono storicizzate le informazioni sullo scadere della cache stessa */
173
182
  this.localStorageHttpCacheExpireKey = null;
174
- /**
175
- * Identità collegata al token di autenticazione da usare, richiesta al programma che utilizza la libreria attraverso l'hook **retrieveIdentity** della **HttpServiceExtensions**
176
- */
183
+ /** Identità collegata al token di autenticazione da usare, richiesta al programma che utilizza la libreria attraverso l'hook **retrieveIdentity** della **HttpServiceExtensions** */
177
184
  this.me = null;
185
+ /** Indica che la cache a tempo dev'essere persistita il prima possibile */
186
+ this.PersistTimedCacheASAP = false;
187
+ /** Indica che la cache senza limiti di tempo dev'essere persistita il prima possibile */
188
+ this.PersistUntimedCacheASAP = false;
178
189
  this.ExtensionsImpl = this.EXTENSION_CLASS || new HttpServiceExtensions();
179
190
  this.me = this.ExtensionsImpl.retrieveIdentity();
180
191
  this.localStorageHttpCacheKey = (this.APP_NAME ?? "APP").replace(/\s/g, '_') + "_HttpCache";
181
192
  this.localStorageHttpTimedCacheKey = (this.APP_NAME ?? "APP").replace(/\s/g, '_') + "_HttpTimedCache";
182
193
  this.localStorageHttpCacheExpireKey = this.localStorageHttpCacheKey + "_Expire";
194
+ this.adjustOldStorageSystem();
183
195
  if (this.untimedCacheExpired())
184
196
  this.resetHttpUntimedCacheStorage();
185
197
  }
198
+ /**
199
+ * Se vedo che ci sono elementi salvati nel localStorage provo a toglierli e a risalvarli via preference.
200
+ * Nel caso migliore li sposto dal localStorage al DB di preference, nel caso peggiore li tolgo e li risalvo nel localStorage
201
+ */
202
+ async adjustOldStorageSystem() {
203
+ var lsTimed = localStorage.getItem(this.localStorageHttpTimedCacheKey);
204
+ var lsUntimed = localStorage.getItem(this.localStorageHttpCacheKey);
205
+ if (!lsTimed && !lsUntimed)
206
+ return;
207
+ localStorage.removeItem(this.localStorageHttpTimedCacheKey);
208
+ localStorage.removeItem(this.localStorageHttpCacheKey);
209
+ lsTimed = lsTimed ? JSON.parse(lsTimed) : {};
210
+ lsUntimed = lsUntimed ? JSON.parse(lsUntimed) : {};
211
+ await forkJoin([
212
+ this.localStorage.setItem(this.localStorageHttpTimedCacheKey, lsTimed),
213
+ this.localStorage.setItem(this.localStorageHttpCacheKey, lsUntimed)
214
+ ]).toPromise();
215
+ this.log(this.loc("Converted old storage system to new one"));
216
+ }
186
217
  /**
187
218
  * Esecuzione di una GET HTTP con eventualmente la gestione della cache qualora la chiamata termini con la convenzione "#cache"
188
219
  *
@@ -214,7 +245,7 @@ class HTTPService {
214
245
  if (errorCallback)
215
246
  errorCallback();
216
247
  return !succesfulResultMandatory ? throwError(err) : this.registerErrorAndReturnEmptyObservable(err, myGuid);
217
- }), finalize(() => { this.tryFinalizeByGuid(myGuid); }));
248
+ }), finalize(() => { this.tryFinalizeByGuid(myGuid, cachedInfos); }));
218
249
  }
219
250
  /**
220
251
  * Esecuzione di una GET HTTP con eventualmente la gestione della cache qualora la chiamata termini con la convenzione "#cache"
@@ -249,7 +280,7 @@ class HTTPService {
249
280
  if (errorCallback)
250
281
  errorCallback();
251
282
  return !succesfulResultMandatory ? throwError(err) : this.registerErrorAndReturnEmptyObservable(err, myGuid);
252
- }), finalize(() => { this.tryFinalizeByGuid(myGuid); }));
283
+ }), finalize(() => { this.tryFinalizeByGuid(myGuid, cachedInfos); }));
253
284
  }
254
285
  /**
255
286
  * Esecuzione di una POST HTTP
@@ -462,7 +493,7 @@ class HTTPService {
462
493
  * @param {string} url URL per cui controllare se ci sono chiamate in cache da invalidare
463
494
  */
464
495
  checkCacheInvalidation(url) {
465
- let cache = JSON.parse(localStorage.getItem(this.localStorageHttpCacheKey));
496
+ let cache = this.localStorage.getLocalItem(this.localStorageHttpCacheKey);
466
497
  let tmp = /#cache(:?-(\d+))/gi.exec(url);
467
498
  let cacheTime = tmp ? tmp[2] : null;
468
499
  let normalizedUrl = url.replace("#cache" + (cacheTime ? "-" + cacheTime : ""), "").replace(this.BACKEND_URL, "");
@@ -483,14 +514,16 @@ class HTTPService {
483
514
  this.log(this.loc("Invlalidating cache element") + " " + k);
484
515
  delete cache[k];
485
516
  }
486
- localStorage.setItem(this.localStorageHttpCacheKey, JSON.stringify(cache));
517
+ this.localStorage.setItem(this.localStorageHttpCacheKey, cache);
487
518
  }
488
519
  /**
489
520
  * Finalizzazione di una chiamata
490
521
  *
491
522
  * @param {string} guid GUID collegato alla chiamata appena terminata
523
+ * @param {boolean} persistCacheIfIdle Effettua il persist della cache HTTP solo se non ci sono altre chiamate in corso, per evitare
524
+ * che mi cambi sotto l'oggetto di cache mentre sto salvando le informazioni
492
525
  */
493
- tryFinalizeByGuid(guid) {
526
+ async tryFinalizeByGuid(guid, cacheInfos) {
494
527
  this.log(this.loc("Finalizing call") + " " + guid + " " + this.loc("with status") + ": " + (this.callStatus[guid] ? this.loc('Success') : this.loc('Failure')));
495
528
  //Alla prima chiamata buona, significa che almeno il login ce l'ho. Posso mettermi in modalità redirectInProgress false
496
529
  //e riiniziare a valutare errori di javashit
@@ -498,6 +531,34 @@ class HTTPService {
498
531
  this.ExtensionsImpl.writeRedirectionStatus(false);
499
532
  //In ogni caso questa posso rimuoverla
500
533
  delete this.callStatus[guid];
534
+ // Se vengo da una chiamata che ha aggiornato la cache a timer o globale marco il fatto che quella cache dev'essere aggiornata
535
+ if (cacheInfos && cacheInfos.cache) {
536
+ this.PersistTimedCacheASAP = this.PersistTimedCacheASAP || !!cacheInfos.cacheTime;
537
+ this.PersistUntimedCacheASAP = this.PersistTimedCacheASAP || !cacheInfos.cacheTime;
538
+ this.log(this.loc("Marking cache to be persisted as soon as possible"));
539
+ }
540
+ // Se una delle due cache devono essere aggiornate e non ho nessuna chiamata in corso effettuo l'aggiornamento in base a quale delle due va aggiornata
541
+ // Alla fine di tutto marco come fatto
542
+ if ((this.PersistTimedCacheASAP || this.PersistUntimedCacheASAP) && Object.keys(this.callStatus).length == 0) {
543
+ var toCall = [];
544
+ let tmpKey = "";
545
+ if (this.PersistTimedCacheASAP) {
546
+ tmpKey = this.localStorageHttpTimedCacheKey;
547
+ toCall.push(this.localStorage.setItem(tmpKey, this.localStorage.getLocalItem(tmpKey)));
548
+ }
549
+ if (this.PersistUntimedCacheASAP) {
550
+ tmpKey = this.localStorageHttpCacheKey;
551
+ toCall.push(this.localStorage.setItem(tmpKey, this.localStorage.getLocalItem(tmpKey)));
552
+ }
553
+ this.log(this.loc("No more HTTP calls to be executed, entering idle state and persisting modified caches..."));
554
+ var ret = await forkJoin(toCall).toPromise();
555
+ if (ret)
556
+ this.log(this.loc("Caches succesfully persisted"));
557
+ else
558
+ this.log(this.loc("Errors persisting modified caches"));
559
+ this.PersistTimedCacheASAP = false;
560
+ this.PersistUntimedCacheASAP = false;
561
+ }
501
562
  }
502
563
  /**
503
564
  * Helper che dato un unico File o una lista di File crea un oggetto FormData che può essere inviato comodamente
@@ -680,7 +741,7 @@ class HTTPService {
680
741
  * @param {string} url URL su cui effettuare la chiamata
681
742
  * @param {HttpParams} params Parametri della chiamata (nulli in caso di POST)
682
743
  *
683
- * @returns {{ finalUrl: string, cache: boolean, cacheKey: string, cacheTime: string, cacheItem: any }} Oggetto contenente le informazioni sulla cache per l'oggetto specificato
744
+ * @returns {CacheInfos} Oggetto contenente le informazioni sulla cache per l'oggetto specificato
684
745
  */
685
746
  tryGetCacheInformations(url, params) {
686
747
  let cachingKey = null, cacheTime = null, cache = null, item = null;
@@ -693,8 +754,8 @@ class HTTPService {
693
754
  this.resetHttpUntimedCacheStorage();
694
755
  cachingKey = this.makeCacheKey(url, params);
695
756
  let cacheKey = cacheTime ? this.localStorageHttpTimedCacheKey : this.localStorageHttpCacheKey;
696
- let cacheLs = localStorage.getItem(cacheKey);
697
- let cache = cacheLs ? JSON.parse(cacheLs) : {};
757
+ let cacheLs = this.localStorage.getLocalItem(cacheKey);
758
+ let cache = cacheLs ? cacheLs : {};
698
759
  if (cache && cache[cachingKey] && !this.timedCacheItemExpired(cache[cachingKey])) {
699
760
  // Se gestisco il tempo esatto della cache prima di restituire l'oggetto cachato lo ripulisco
700
761
  // dalle proprietà interne che gli ho aggiunto per controllarne la scadenza
@@ -703,22 +764,22 @@ class HTTPService {
703
764
  item = cache[cachingKey];
704
765
  }
705
766
  }
706
- return { finalUrl: url, cache: useCaching, cacheKey: cachingKey, cacheTime: cacheTime, cacheItem: item };
767
+ return new CacheInfos(url, useCaching, cachingKey, cacheTime, item);
707
768
  }
708
769
  /**
709
770
  * Inserisce un oggetto nella cache appropriata in base alle informazioni contenute nel parametro **cacheInfos**
710
771
  *
711
772
  * @param {T} item Oggetto da inserire nella cache
712
- * @param {{ finalUrl: string, cache: boolean, cacheKey: string, cacheTime: string, cacheItem: any }} cacheInfos Informazioni sulla cache recuperate da una chiamata precedente a **tryGetCacheInformations**
773
+ * @param {CacheInfos} cacheInfos Informazioni sulla cache recuperate da una chiamata precedente a **tryGetCacheInformations**
713
774
  */
714
775
  cacheResponse(item, cacheInfos) {
715
776
  let cacheKey = cacheInfos.cacheTime ? this.localStorageHttpTimedCacheKey : this.localStorageHttpCacheKey;
716
- let cacheLs = localStorage.getItem(cacheKey);
717
- let cache = cacheLs ? JSON.parse(cacheLs) : {};
777
+ let cacheLs = this.localStorage.getLocalItem(cacheKey);
778
+ let cache = cacheLs ? cacheLs : {};
718
779
  cache[cacheInfos.cacheKey] = item;
719
780
  if (cacheInfos.cacheTime)
720
781
  cache[cacheInfos.cacheKey].__expire = dayjs().add(cacheInfos.cacheTime, "seconds");
721
- localStorage.setItem(cacheKey, JSON.stringify(cache));
782
+ this.localStorage.setLocalItem(cacheKey, cache);
722
783
  }
723
784
  /**
724
785
  * Controlla se la cache per le chiamate senza expire time è scaduta
@@ -742,15 +803,15 @@ class HTTPService {
742
803
  /**
743
804
  * Ripulisce la cache e imposta la prossima scadenza ad adesso + **CLIENT_CACHE_DURATION_S** (token valorizzato in fase di configurazione dalla proprietà **clientCacheDurationS** della classe **HttpServiceModuleConfig**)
744
805
  */
745
- resetHttpUntimedCacheStorage() {
746
- localStorage.setItem(this.localStorageHttpCacheKey, JSON.stringify({}));
806
+ async resetHttpUntimedCacheStorage() {
807
+ await this.localStorage.setItem(this.localStorageHttpCacheKey, {}).toPromise();
747
808
  localStorage.setItem(this.localStorageHttpCacheExpireKey, JSON.stringify(dayjs().add(this.CLIENT_CACHE_DURATION_S ?? 3600, "second")));
748
809
  }
749
810
  /**
750
811
  * Ripulisce la cache delle chiamate con un tempo di expire definito
751
812
  */
752
- resetHttpTimedCacheStorage() {
753
- localStorage.setItem(this.localStorageHttpTimedCacheKey, JSON.stringify({}));
813
+ async resetHttpTimedCacheStorage() {
814
+ await this.localStorage.setItem(this.localStorageHttpTimedCacheKey, {}).toPromise();
754
815
  }
755
816
  /**
756
817
  * Genera una chiave per la cache delle chiamate in base ad URL e Parametri HTTP
@@ -762,8 +823,8 @@ class HTTPService {
762
823
  */
763
824
  makeCacheKey(url, params) {
764
825
  let authKeyPart = (this.me ? this.me.Idtenant : "") + (this.me && this.CACHE_BY_USER ? this.me.IdUser : "");
765
- let paramsKeyPart = params && params.keys().length > 0 ? params.keys().reduce((prev, next) => prev + next + '=' + params.get(next), "") : "";
766
- return authKeyPart + url + paramsKeyPart;
826
+ let paramsKeyPart = params && params.keys().length > 0 ? params.keys().reduce((prev, next) => prev + "," + next + '=' + params.get(next), "") : "";
827
+ return authKeyPart + "_" + url.substring(url.indexOf("api") + 4) + "?" + paramsKeyPart;
767
828
  }
768
829
  /**
769
830
  * Generazione GUID
@@ -800,7 +861,7 @@ class HTTPService {
800
861
  return Loc[message][this.LOCALE];
801
862
  }
802
863
  }
803
- HTTPService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.1.1", ngImport: i0, type: HTTPService, deps: [{ token: BACKEND_URL }, { token: HttpServiceExtensions }, { token: HTTP_INVALIDATION, optional: true }, { token: HTTP_LOCALE, optional: true }, { token: APP_NAME, optional: true }, { token: CLIENT_CACHE_DURATION_S, optional: true }, { token: CACHE_BY_USER, optional: true }, { token: DEBUG_MODE, optional: true }, { token: i2.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable });
864
+ HTTPService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.1.1", ngImport: i0, type: HTTPService, deps: [{ token: BACKEND_URL }, { token: HttpServiceExtensions }, { token: HTTP_INVALIDATION, optional: true }, { token: HTTP_LOCALE, optional: true }, { token: APP_NAME, optional: true }, { token: CLIENT_CACHE_DURATION_S, optional: true }, { token: CACHE_BY_USER, optional: true }, { token: DEBUG_MODE, optional: true }, { token: i2.HttpClient }, { token: i3.PreferencesService }], target: i0.ɵɵFactoryTarget.Injectable });
804
865
  HTTPService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.1.1", ngImport: i0, type: HTTPService });
805
866
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.1.1", ngImport: i0, type: HTTPService, decorators: [{
806
867
  type: Injectable
@@ -837,7 +898,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.1.1", ngImpor
837
898
  }, {
838
899
  type: Inject,
839
900
  args: [DEBUG_MODE]
840
- }] }, { type: i2.HttpClient }]; } });
901
+ }] }, { type: i2.HttpClient }, { type: i3.PreferencesService }]; } });
841
902
 
842
903
  class HttpserviceModule {
843
904
  static forRoot(config) {