http-request-manager 0.0.1 → 0.0.20
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/README.md +262 -13
- package/esm2022/http-request-manager.mjs +5 -0
- package/esm2022/lib/interceptors/credentials.interceptor.mjs +14 -0
- package/esm2022/lib/interceptors/index.mjs +5 -0
- package/esm2022/lib/interceptors/models/error-settings.model.mjs +10 -0
- package/esm2022/lib/interceptors/request-error.interceptor.mjs +49 -0
- package/esm2022/lib/interceptors/request-header.interceptor.mjs +41 -0
- package/esm2022/lib/models/data-type.enum.mjs +7 -0
- package/esm2022/lib/models/database-storage.model.mjs +10 -0
- package/esm2022/lib/models/index.mjs +4 -0
- package/esm2022/lib/models/retry-options.model.mjs +10 -0
- package/esm2022/lib/services/index.mjs +6 -0
- package/esm2022/lib/services/local-storage-manager-service/index.mjs +3 -0
- package/esm2022/lib/services/local-storage-manager-service/local-storage-manager.service.mjs +275 -0
- package/esm2022/lib/services/local-storage-manager-service/models/global-store-options.model.mjs +13 -0
- package/esm2022/lib/services/local-storage-manager-service/models/index.mjs +7 -0
- package/esm2022/lib/services/local-storage-manager-service/models/setting-options.model.mjs +13 -0
- package/esm2022/lib/services/local-storage-manager-service/models/storage-data.model.mjs +10 -0
- package/esm2022/lib/services/local-storage-manager-service/models/storage-option-settings.model.mjs +13 -0
- package/esm2022/lib/services/local-storage-manager-service/models/storage-option.model.mjs +12 -0
- package/esm2022/lib/services/local-storage-manager-service/models/storage-type.enum.mjs +7 -0
- package/esm2022/lib/services/request-manager-services/http-manager.service.mjs +172 -0
- package/esm2022/lib/services/request-manager-services/index.mjs +5 -0
- package/esm2022/lib/services/request-manager-services/request.service.mjs +159 -0
- package/esm2022/lib/services/request-manager-services/rxjs-operators/countdown.mjs +9 -0
- package/esm2022/lib/services/request-manager-services/rxjs-operators/delay-retry.mjs +8 -0
- package/esm2022/lib/services/request-manager-services/rxjs-operators/index.mjs +5 -0
- package/esm2022/lib/services/request-manager-services/rxjs-operators/request-polling.mjs +14 -0
- package/esm2022/lib/services/request-manager-services/rxjs-operators/request-streaming.mjs +19 -0
- package/esm2022/lib/services/request-manager-state-service/http-manager-state.store.mjs +299 -0
- package/esm2022/lib/services/request-manager-state-service/index.mjs +3 -0
- package/esm2022/lib/services/request-manager-state-service/models/api-request.model.mjs +20 -0
- package/esm2022/lib/services/request-manager-state-service/models/index.mjs +3 -0
- package/esm2022/lib/services/request-manager-state-service/models/request-options.model.mjs +10 -0
- package/esm2022/lib/services/utils/app.service.mjs +20 -0
- package/esm2022/lib/services/utils/encryption/asymmetrical-encryption.service.mjs +186 -0
- package/esm2022/lib/services/utils/encryption/encryption-test.service.mjs +35 -0
- package/esm2022/lib/services/utils/encryption/index.mjs +4 -0
- package/esm2022/lib/services/utils/encryption/random.mjs +52 -0
- package/esm2022/lib/services/utils/encryption/symmetrical-encryption.service.mjs +77 -0
- package/esm2022/lib/services/utils/headers.service.mjs +21 -0
- package/esm2022/lib/services/utils/index.mjs +6 -0
- package/esm2022/lib/services/utils/path-query.service.mjs +53 -0
- package/esm2022/lib/services/utils/utils.service.mjs +134 -0
- package/esm2022/public-api.mjs +7 -0
- package/fesm2022/http-request-manager.mjs +1715 -0
- package/fesm2022/http-request-manager.mjs.map +1 -0
- package/http-request-manager-0.0.20.tgz +0 -0
- package/index.d.ts +5 -0
- package/lib/interceptors/credentials.interceptor.d.ts +8 -0
- package/lib/interceptors/index.d.ts +4 -0
- package/lib/interceptors/models/error-settings.model.d.ts +10 -0
- package/lib/interceptors/request-error.interceptor.d.ts +10 -0
- package/lib/interceptors/request-header.interceptor.d.ts +15 -0
- package/lib/models/data-type.enum.d.ts +5 -0
- package/lib/models/database-storage.model.d.ts +10 -0
- package/lib/models/index.d.ts +3 -0
- package/lib/models/retry-options.model.d.ts +10 -0
- package/lib/services/index.d.ts +4 -0
- package/lib/services/local-storage-manager-service/index.d.ts +2 -0
- package/lib/services/local-storage-manager-service/local-storage-manager.service.d.ts +81 -0
- package/lib/services/local-storage-manager-service/models/global-store-options.model.d.ts +15 -0
- package/lib/services/local-storage-manager-service/models/index.d.ts +6 -0
- package/lib/services/local-storage-manager-service/models/setting-options.model.d.ts +15 -0
- package/lib/services/local-storage-manager-service/models/storage-data.model.d.ts +10 -0
- package/lib/services/local-storage-manager-service/models/storage-option-settings.model.d.ts +15 -0
- package/lib/services/local-storage-manager-service/models/storage-option.model.d.ts +13 -0
- package/lib/services/local-storage-manager-service/models/storage-type.enum.d.ts +5 -0
- package/lib/services/request-manager-services/http-manager.service.d.ts +35 -0
- package/lib/services/request-manager-services/index.d.ts +3 -0
- package/lib/services/request-manager-services/request.service.d.ts +27 -0
- package/lib/services/request-manager-services/rxjs-operators/countdown.d.ts +2 -0
- package/lib/services/request-manager-services/rxjs-operators/delay-retry.d.ts +2 -0
- package/lib/services/request-manager-services/rxjs-operators/index.d.ts +4 -0
- package/lib/services/request-manager-services/rxjs-operators/request-polling.d.ts +7 -0
- package/lib/services/request-manager-services/rxjs-operators/request-streaming.d.ts +2 -0
- package/lib/services/request-manager-state-service/http-manager-state.store.d.ts +55 -0
- package/lib/services/request-manager-state-service/index.d.ts +2 -0
- package/lib/services/request-manager-state-service/models/api-request.model.d.ts +25 -0
- package/lib/services/request-manager-state-service/models/index.d.ts +2 -0
- package/lib/services/request-manager-state-service/models/request-options.model.d.ts +10 -0
- package/lib/services/utils/app.service.d.ts +8 -0
- package/lib/services/utils/encryption/asymmetrical-encryption.service.d.ts +17 -0
- package/lib/services/utils/encryption/encryption-test.service.d.ts +10 -0
- package/lib/services/utils/encryption/index.d.ts +3 -0
- package/lib/services/utils/encryption/random.d.ts +7 -0
- package/lib/services/utils/encryption/symmetrical-encryption.service.d.ts +14 -0
- package/lib/services/utils/headers.service.d.ts +10 -0
- package/lib/services/utils/index.d.ts +5 -0
- package/lib/services/utils/path-query.service.d.ts +11 -0
- package/lib/services/utils/utils.service.d.ts +24 -0
- package/package.json +24 -10
- package/public-api.d.ts +3 -0
- package/ng-package.json +0 -7
- package/src/lib/http-request-manager.component.spec.ts +0 -21
- package/src/lib/http-request-manager.component.ts +0 -15
- package/src/lib/http-request-manager.module.ts +0 -16
- package/src/lib/http-request-manager.service.spec.ts +0 -16
- package/src/lib/http-request-manager.service.ts +0 -9
- package/src/public-api.ts +0 -7
- package/tsconfig.lib.json +0 -14
- package/tsconfig.lib.prod.json +0 -10
- package/tsconfig.spec.json +0 -14
|
@@ -0,0 +1,1715 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { inject, Injectable, APP_ID, Inject, Injector, InjectionToken } from '@angular/core';
|
|
3
|
+
import { ComponentStore } from '@ngrx/component-store';
|
|
4
|
+
import { withLatestFrom, map, tap, catchError, filter, finalize, takeWhile, retryWhen, delay, mergeMap, startWith, takeUntil, switchMap, concatMap, scan, expand } from 'rxjs/operators';
|
|
5
|
+
import { HttpClient, HttpHeaders, HttpEventType, HttpErrorResponse } from '@angular/common/http';
|
|
6
|
+
import * as CryptoJS from 'crypto-js';
|
|
7
|
+
import { interval, from, BehaviorSubject, EMPTY, defer, of, throwError, Subject, Subscription, catchError as catchError$1 } from 'rxjs';
|
|
8
|
+
import { ToastDisplay, ToastColors, ToastMessageService as ToastMessageService$1 } from 'toast-message';
|
|
9
|
+
import { ToastMessageService } from 'toast-message-display';
|
|
10
|
+
import * as i1 from '@ngx-translate/core';
|
|
11
|
+
|
|
12
|
+
var StorageType;
|
|
13
|
+
(function (StorageType) {
|
|
14
|
+
StorageType[StorageType["GLOBAL"] = 0] = "GLOBAL";
|
|
15
|
+
StorageType[StorageType["SESSION"] = 1] = "SESSION";
|
|
16
|
+
StorageType[StorageType["DB"] = 2] = "DB";
|
|
17
|
+
})(StorageType || (StorageType = {}));
|
|
18
|
+
|
|
19
|
+
class StorageOptionSettings {
|
|
20
|
+
constructor(storage = StorageType.GLOBAL, expires, expiresIn, encrypted) {
|
|
21
|
+
this.storage = storage;
|
|
22
|
+
this.expires = expires;
|
|
23
|
+
this.expiresIn = expiresIn;
|
|
24
|
+
this.encrypted = encrypted;
|
|
25
|
+
}
|
|
26
|
+
static adapt(item) {
|
|
27
|
+
return new StorageOptionSettings((item?.storage) ? item.storage : StorageType.GLOBAL, (item?.expires) ? item.expires : 0, (item?.expiresIn) ? item.expiresIn : '', (item?.encrypted) ? item.encrypted : false);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
class StorageOption {
|
|
32
|
+
constructor(id, name = 'untitled', options) {
|
|
33
|
+
this.id = id;
|
|
34
|
+
this.name = name;
|
|
35
|
+
this.options = options;
|
|
36
|
+
}
|
|
37
|
+
static adapt(item) {
|
|
38
|
+
return new StorageOption((item?.id) ? item.id : crypto.randomUUID(), item?.name, (item?.options) ? StorageOptionSettings.adapt(item.options) : StorageOptionSettings.adapt());
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
class GlobalStoreOptions {
|
|
43
|
+
constructor(encryption = false, expiresIn, expires, stores = []) {
|
|
44
|
+
this.encryption = encryption;
|
|
45
|
+
this.expiresIn = expiresIn;
|
|
46
|
+
this.expires = expires;
|
|
47
|
+
this.stores = stores;
|
|
48
|
+
}
|
|
49
|
+
static adapt(item) {
|
|
50
|
+
return new GlobalStoreOptions((item?.encryption) ? item.encryption : false, (item?.expiresIn) ? item.expiresIn : '', (item?.expires) ? item.expires : 0, (item?.stores) ? item.stores.map((item) => StorageOption.adapt(item)) : []);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
class SettingOptions {
|
|
55
|
+
constructor(storage = StorageType.GLOBAL, expires, expiresIn, encrypted) {
|
|
56
|
+
this.storage = storage;
|
|
57
|
+
this.expires = expires;
|
|
58
|
+
this.expiresIn = expiresIn;
|
|
59
|
+
this.encrypted = encrypted;
|
|
60
|
+
}
|
|
61
|
+
static adapt(item) {
|
|
62
|
+
return new SettingOptions((item?.storage) ? item.storage : StorageType.GLOBAL, (item?.expires) ? item.expires : 0, (item?.expiresIn) ? item.expiresIn : '', (item?.encrypted) ? item?.encrypted : false);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
class StorageData {
|
|
67
|
+
constructor(id, data) {
|
|
68
|
+
this.id = id;
|
|
69
|
+
this.data = data;
|
|
70
|
+
}
|
|
71
|
+
static adapt(item) {
|
|
72
|
+
return new StorageData((item?.id) ? item.id : crypto.randomUUID(), (item?.data) ? item.data : null);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
class UtilsService {
|
|
77
|
+
constructor() {
|
|
78
|
+
this.http = inject(HttpClient);
|
|
79
|
+
this.lc = (str) => str.toLowerCase().replace(/[^\w\s]/g, '').replace(/\s+/g, '_');
|
|
80
|
+
}
|
|
81
|
+
isString(x) {
|
|
82
|
+
return Object.prototype.toString.call(x) === '[object String]';
|
|
83
|
+
}
|
|
84
|
+
isObject(obj) {
|
|
85
|
+
return Object.prototype.toString.call(obj) === '[object Object]';
|
|
86
|
+
}
|
|
87
|
+
JSONToString(value) {
|
|
88
|
+
return (this.isObject(value)) ? JSON.stringify(value) : value;
|
|
89
|
+
}
|
|
90
|
+
stringToJSON(value) {
|
|
91
|
+
return (this.isJSON(value)) ? JSON.parse(value) : value;
|
|
92
|
+
}
|
|
93
|
+
isJSON(str) {
|
|
94
|
+
try {
|
|
95
|
+
JSON.parse(str);
|
|
96
|
+
}
|
|
97
|
+
catch (e) {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
getValueByProp(obj, prop) {
|
|
103
|
+
if (typeof obj === 'undefined')
|
|
104
|
+
return false;
|
|
105
|
+
return obj[prop];
|
|
106
|
+
}
|
|
107
|
+
objectsEqual(x, y) {
|
|
108
|
+
const ok = Object.keys, tx = typeof x, ty = typeof y;
|
|
109
|
+
return x && y && tx === 'object' && tx === ty ? (ok(x).length === ok(y).length &&
|
|
110
|
+
ok(x).every((key) => this.objectsEqual(x[key], y[key]))) : (x === y);
|
|
111
|
+
}
|
|
112
|
+
getJSON(file) {
|
|
113
|
+
return this.http.get(`assets/data/${file}`);
|
|
114
|
+
}
|
|
115
|
+
get today() {
|
|
116
|
+
return new Date().getMilliseconds() / 1000;
|
|
117
|
+
}
|
|
118
|
+
base32ToHex(base32) {
|
|
119
|
+
const base32chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
|
|
120
|
+
const charToValue = (char) => base32chars.indexOf(char.toUpperCase());
|
|
121
|
+
let bits = '';
|
|
122
|
+
let hex = '';
|
|
123
|
+
for (let i = 0; i < base32.length; i++) {
|
|
124
|
+
const val = charToValue(base32.charAt(i));
|
|
125
|
+
bits += val.toString(2).padStart(5, '0');
|
|
126
|
+
}
|
|
127
|
+
for (let i = 0; i + 4 <= bits.length; i += 4) {
|
|
128
|
+
const chunk = bits.substr(i, 4);
|
|
129
|
+
hex += parseInt(chunk, 2).toString(16);
|
|
130
|
+
}
|
|
131
|
+
return hex.toUpperCase();
|
|
132
|
+
}
|
|
133
|
+
binaryToHex(binary) {
|
|
134
|
+
let hex = '';
|
|
135
|
+
for (let i = 0; i < binary.length; i += 8) {
|
|
136
|
+
const byte = binary.substr(i, 8);
|
|
137
|
+
const decimal = parseInt(byte, 2);
|
|
138
|
+
hex += decimal.toString(16).padStart(2, '0');
|
|
139
|
+
}
|
|
140
|
+
return hex.padStart(32, '0').substr(0, 32);
|
|
141
|
+
}
|
|
142
|
+
// y = years, m = months, w = weeks, d = days, hr = hours, mn = minutes else seconds
|
|
143
|
+
expires(str) {
|
|
144
|
+
if (!str)
|
|
145
|
+
return;
|
|
146
|
+
const match = str.match(/[^0-9]+$/);
|
|
147
|
+
const type = (match) ? str.slice(-match[0].length) : 0;
|
|
148
|
+
const now = Math.floor(new Date().getTime() / 1000);
|
|
149
|
+
let value = 0;
|
|
150
|
+
switch (type) {
|
|
151
|
+
case 'y':
|
|
152
|
+
const years = parseInt(str.slice(0, -1));
|
|
153
|
+
value = now + (years * 31556926);
|
|
154
|
+
break;
|
|
155
|
+
case 'm':
|
|
156
|
+
const months = parseInt(str.slice(0, -1));
|
|
157
|
+
value = now + (months * 2629743);
|
|
158
|
+
break;
|
|
159
|
+
case 'w':
|
|
160
|
+
const weeks = parseInt(str.slice(0, -1));
|
|
161
|
+
value = now + (weeks * 604800);
|
|
162
|
+
break;
|
|
163
|
+
case 'd':
|
|
164
|
+
const days = parseInt(str.slice(0, -1));
|
|
165
|
+
value = now + (days * 86400);
|
|
166
|
+
break;
|
|
167
|
+
case 'hr':
|
|
168
|
+
const hrs = parseInt(str.slice(0, -2));
|
|
169
|
+
value = now + (hrs * 3600);
|
|
170
|
+
break;
|
|
171
|
+
case 'mn':
|
|
172
|
+
const min = parseInt(str.slice(0, -2));
|
|
173
|
+
value = now + (min * 60);
|
|
174
|
+
break;
|
|
175
|
+
default: //seconds
|
|
176
|
+
const sec = parseInt(str.slice(0, -1));
|
|
177
|
+
value = now + (sec);
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
180
|
+
return value;
|
|
181
|
+
}
|
|
182
|
+
hasExpired(expiryDate) {
|
|
183
|
+
if (!expiryDate || expiryDate === 0)
|
|
184
|
+
return false;
|
|
185
|
+
const currentTime = Math.floor(new Date().getTime() / 1000);
|
|
186
|
+
return expiryDate < currentTime;
|
|
187
|
+
}
|
|
188
|
+
hasExpiry(setting) {
|
|
189
|
+
return (setting?.expires) ? true : false;
|
|
190
|
+
}
|
|
191
|
+
expiresIn(expiryDate) {
|
|
192
|
+
if (!expiryDate)
|
|
193
|
+
return;
|
|
194
|
+
const now = Math.floor(new Date().getTime() / 1000);
|
|
195
|
+
return expiryDate - now;
|
|
196
|
+
}
|
|
197
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: UtilsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
198
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: UtilsService, providedIn: 'root' }); }
|
|
199
|
+
}
|
|
200
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: UtilsService, decorators: [{
|
|
201
|
+
type: Injectable,
|
|
202
|
+
args: [{
|
|
203
|
+
providedIn: 'root'
|
|
204
|
+
}]
|
|
205
|
+
}], ctorParameters: function () { return []; } });
|
|
206
|
+
|
|
207
|
+
const Random = () => {
|
|
208
|
+
const typedArray = new Uint8Array(1);
|
|
209
|
+
const randomValue = crypto.getRandomValues(typedArray)[0];
|
|
210
|
+
const randomFloat = randomValue / Math.pow(2, 8);
|
|
211
|
+
return randomFloat;
|
|
212
|
+
};
|
|
213
|
+
const RandomNumber = (min, max) => {
|
|
214
|
+
return Math.floor(Random() * max) + min;
|
|
215
|
+
};
|
|
216
|
+
const RandomNumbers = (min, max, length) => {
|
|
217
|
+
const numbers = [];
|
|
218
|
+
for (let i = 0; i < length; i++)
|
|
219
|
+
numbers.push(Math.floor(Random() * max) + min);
|
|
220
|
+
return numbers;
|
|
221
|
+
};
|
|
222
|
+
const RandomNumbersUnique = (min, max, length) => {
|
|
223
|
+
let count = 0;
|
|
224
|
+
// Adjust the length if it exceeds the number of unique numbers possible in the range
|
|
225
|
+
if (length > (max - min + 1)) {
|
|
226
|
+
console.error('error encountered');
|
|
227
|
+
length = max - min + 1;
|
|
228
|
+
}
|
|
229
|
+
console.log('length', length);
|
|
230
|
+
const numbers = [];
|
|
231
|
+
const maxAttempts = length * 100; // Safeguard to prevent infinite loops
|
|
232
|
+
while (numbers.length < length && count < maxAttempts) {
|
|
233
|
+
const num = Math.floor(Random() * max) + min;
|
|
234
|
+
if (!numbers.includes(num)) {
|
|
235
|
+
numbers.push(num);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
if (count >= maxAttempts) {
|
|
239
|
+
throw new Error('Maximum attempts exceeded while generating unique numbers');
|
|
240
|
+
}
|
|
241
|
+
return numbers;
|
|
242
|
+
};
|
|
243
|
+
const RandomStr = () => {
|
|
244
|
+
const typedArray = new Uint8Array(8);
|
|
245
|
+
const array = new Uint32Array(1);
|
|
246
|
+
crypto.getRandomValues(array);
|
|
247
|
+
return array[0].toString(36);
|
|
248
|
+
};
|
|
249
|
+
const RandomSignature = () => {
|
|
250
|
+
const typedArray = new Uint8Array(8);
|
|
251
|
+
const array = new Uint32Array(1);
|
|
252
|
+
crypto.getRandomValues(array);
|
|
253
|
+
return array[0];
|
|
254
|
+
};
|
|
255
|
+
const UUID = () => {
|
|
256
|
+
return self.crypto.randomUUID();
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
class AppService {
|
|
260
|
+
constructor(id) {
|
|
261
|
+
this.id = id;
|
|
262
|
+
this.appID = this.id;
|
|
263
|
+
}
|
|
264
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: AppService, deps: [{ token: APP_ID }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
265
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: AppService, providedIn: 'root' }); }
|
|
266
|
+
}
|
|
267
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: AppService, decorators: [{
|
|
268
|
+
type: Injectable,
|
|
269
|
+
args: [{
|
|
270
|
+
providedIn: 'root'
|
|
271
|
+
}]
|
|
272
|
+
}], ctorParameters: function () { return [{ type: undefined, decorators: [{
|
|
273
|
+
type: Inject,
|
|
274
|
+
args: [APP_ID]
|
|
275
|
+
}] }]; } });
|
|
276
|
+
|
|
277
|
+
class SymmetricalEncryptionService {
|
|
278
|
+
constructor() {
|
|
279
|
+
this.appService = inject(AppService);
|
|
280
|
+
// TODO: The APP ID is not a string, it is a function
|
|
281
|
+
this.appID = (this.appService.appID) ? this.appService.appID : '';
|
|
282
|
+
if (this.appID === '')
|
|
283
|
+
console.warn('No App Key has been define');
|
|
284
|
+
}
|
|
285
|
+
generateCipherKey() {
|
|
286
|
+
return (RandomSignature().toString() + RandomSignature().toString()).substring(0, 8);
|
|
287
|
+
}
|
|
288
|
+
encrypt(str, key = this.appID) {
|
|
289
|
+
if (!str || key === '')
|
|
290
|
+
return '';
|
|
291
|
+
let _key = CryptoJS.enc.Utf8.parse(key);
|
|
292
|
+
let _iv = CryptoJS.enc.Utf8.parse(key);
|
|
293
|
+
try {
|
|
294
|
+
const encrypted = CryptoJS.AES.encrypt(JSON.stringify(str), _key, {
|
|
295
|
+
keySize: 16,
|
|
296
|
+
iv: _iv,
|
|
297
|
+
mode: CryptoJS.mode.ECB,
|
|
298
|
+
padding: CryptoJS.pad.Pkcs7
|
|
299
|
+
});
|
|
300
|
+
return encrypted.toString();
|
|
301
|
+
}
|
|
302
|
+
catch (error) {
|
|
303
|
+
console.log(error);
|
|
304
|
+
}
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
decrypt(str, key = this.appID) {
|
|
308
|
+
if (!str || key === '')
|
|
309
|
+
return;
|
|
310
|
+
let _key = CryptoJS.enc.Utf8.parse(key);
|
|
311
|
+
let _iv = CryptoJS.enc.Utf8.parse(key);
|
|
312
|
+
try {
|
|
313
|
+
return CryptoJS.AES.decrypt(str, _key, {
|
|
314
|
+
keySize: 16,
|
|
315
|
+
iv: _iv,
|
|
316
|
+
mode: CryptoJS.mode.ECB,
|
|
317
|
+
padding: CryptoJS.pad.Pkcs7
|
|
318
|
+
}).toString(CryptoJS.enc.Utf8);
|
|
319
|
+
}
|
|
320
|
+
catch (error) {
|
|
321
|
+
console.log(error);
|
|
322
|
+
}
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
createSignature(url, len = 16) {
|
|
326
|
+
const sig = CryptoJS.SHA256(url).toString(CryptoJS.enc.Hex);
|
|
327
|
+
return sig.substring(0, len).toUpperCase();
|
|
328
|
+
}
|
|
329
|
+
normalizeURL(url) {
|
|
330
|
+
const normalizedURL = url.replace(/(https?:\/\/)?(www\.)?/, '').replace(/\/+$/, '');
|
|
331
|
+
return normalizedURL;
|
|
332
|
+
}
|
|
333
|
+
generateSignature(url) {
|
|
334
|
+
const normalizedURL = this.normalizeURL(url);
|
|
335
|
+
const hash = CryptoJS.SHA256(normalizedURL);
|
|
336
|
+
const signature = hash.toString(CryptoJS.enc.Hex);
|
|
337
|
+
return signature;
|
|
338
|
+
}
|
|
339
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: SymmetricalEncryptionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
340
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: SymmetricalEncryptionService, providedIn: 'root' }); }
|
|
341
|
+
}
|
|
342
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: SymmetricalEncryptionService, decorators: [{
|
|
343
|
+
type: Injectable,
|
|
344
|
+
args: [{
|
|
345
|
+
providedIn: 'root'
|
|
346
|
+
}]
|
|
347
|
+
}], ctorParameters: function () { return []; } });
|
|
348
|
+
|
|
349
|
+
class EncryptionTestService {
|
|
350
|
+
constructor() { }
|
|
351
|
+
isBase64(str) {
|
|
352
|
+
const base64Pattern = /^(?:[A-Z0-9+/]{4})*(?:[A-Z0-9+/]{2}==|[A-Z0-9+/]{3}=)?$/i;
|
|
353
|
+
return base64Pattern.test(str);
|
|
354
|
+
}
|
|
355
|
+
isHexadecimal(str) {
|
|
356
|
+
const hexPattern = /^[0-9a-fA-F]+$/;
|
|
357
|
+
return hexPattern.test(str) && (str.length % 2 === 0);
|
|
358
|
+
}
|
|
359
|
+
hasHighEntropy(str) {
|
|
360
|
+
const uniqueChars = new Set(str).size;
|
|
361
|
+
return uniqueChars > str.length * 0.6;
|
|
362
|
+
}
|
|
363
|
+
isEncrypted(str) {
|
|
364
|
+
if (typeof str !== 'string') {
|
|
365
|
+
return false;
|
|
366
|
+
}
|
|
367
|
+
if (this.isBase64(str) || this.isHexadecimal(str)) {
|
|
368
|
+
return true;
|
|
369
|
+
}
|
|
370
|
+
return this.hasHighEntropy(str);
|
|
371
|
+
}
|
|
372
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: EncryptionTestService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
373
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: EncryptionTestService, providedIn: 'root' }); }
|
|
374
|
+
}
|
|
375
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: EncryptionTestService, decorators: [{
|
|
376
|
+
type: Injectable,
|
|
377
|
+
args: [{
|
|
378
|
+
providedIn: 'root'
|
|
379
|
+
}]
|
|
380
|
+
}], ctorParameters: function () { return []; } });
|
|
381
|
+
|
|
382
|
+
const storage = {
|
|
383
|
+
localStores: [],
|
|
384
|
+
sessionStores: [],
|
|
385
|
+
settings: [],
|
|
386
|
+
};
|
|
387
|
+
class LocalStorageManagerService extends ComponentStore {
|
|
388
|
+
startTimer() {
|
|
389
|
+
const timer$ = interval(1000 * 3).pipe(withLatestFrom(this.data$), map(([_, state]) => state), tap((state) => {
|
|
390
|
+
const expired = this.expired(state) ? this.expired(state) : [];
|
|
391
|
+
if (expired.length > 0) {
|
|
392
|
+
const ids = expired.map((item) => item.id);
|
|
393
|
+
const updatedState = {
|
|
394
|
+
...state,
|
|
395
|
+
localStores: state.localStores.filter((item) => !ids.includes(item.id)),
|
|
396
|
+
sessionStores: state.sessionStores.filter((item) => !ids.includes(item.id)),
|
|
397
|
+
settings: state.settings.filter((item) => !ids.includes(item.id)),
|
|
398
|
+
};
|
|
399
|
+
this.persistState(updatedState);
|
|
400
|
+
this.updateState(updatedState);
|
|
401
|
+
}
|
|
402
|
+
}));
|
|
403
|
+
timer$.subscribe();
|
|
404
|
+
}
|
|
405
|
+
constructor() {
|
|
406
|
+
super(storage);
|
|
407
|
+
this.storageName = 'storage';
|
|
408
|
+
this.storageSettingsName = 'global-storage';
|
|
409
|
+
this.stateRetrieved = false;
|
|
410
|
+
this.encrypted = false;
|
|
411
|
+
this.app = inject(AppService);
|
|
412
|
+
this.utils = inject(UtilsService);
|
|
413
|
+
this.encryption = inject(SymmetricalEncryptionService);
|
|
414
|
+
this.encryptionTest = inject(EncryptionTestService);
|
|
415
|
+
// SELECTORS
|
|
416
|
+
this.data$ = this.select((state) => state);
|
|
417
|
+
this.stores$ = this.select((state) => state.settings);
|
|
418
|
+
this.storeExists$ = (store) => this.select(this.data$, (data) => data.settings.find(item => item.name === store) ? true : false);
|
|
419
|
+
this.store$ = (store) => this.select(this.data$, (data) => {
|
|
420
|
+
const foundStore = data.settings.find(item => item.name === store);
|
|
421
|
+
if (foundStore) {
|
|
422
|
+
const options = SettingOptions.adapt(foundStore.options);
|
|
423
|
+
const found = foundStore.options?.storage === StorageType.GLOBAL
|
|
424
|
+
? data.localStores.find(item => item.id === foundStore.id)
|
|
425
|
+
: data.sessionStores.find(item => item.id === foundStore.id);
|
|
426
|
+
if (!found) {
|
|
427
|
+
this.deleteStore({ name: store });
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
const storageData = (options.encrypted) ? this.encryption.decrypt(found.data, this.app.appID) : found.data;
|
|
431
|
+
return (this.isString(storageData)) ? JSON.parse(storageData) : storageData;
|
|
432
|
+
}
|
|
433
|
+
else {
|
|
434
|
+
return null;
|
|
435
|
+
}
|
|
436
|
+
});
|
|
437
|
+
this.settings$ = this.select((state) => state.settings);
|
|
438
|
+
this.setting$ = (store) => this.select(this.data$, (state) => {
|
|
439
|
+
const foundSetting = state.settings.find(item => item.name === store);
|
|
440
|
+
return (foundSetting) ? foundSetting : null;
|
|
441
|
+
});
|
|
442
|
+
this.persistence$ = this.data$
|
|
443
|
+
.subscribe(data => {
|
|
444
|
+
if (this.stateRetrieved)
|
|
445
|
+
this.persistState(data);
|
|
446
|
+
});
|
|
447
|
+
this.updateState = this.updater((state, updatedState) => ({
|
|
448
|
+
...updatedState,
|
|
449
|
+
}));
|
|
450
|
+
this.setStore = this.updater((state, store) => {
|
|
451
|
+
const settings = StorageOption.adapt(store);
|
|
452
|
+
const type = store.options.storage;
|
|
453
|
+
const hasStore = this.hasGlobalStorage(type, store.id);
|
|
454
|
+
store.name = this.validStoreName(store.name);
|
|
455
|
+
if (!hasStore) {
|
|
456
|
+
console.warn(`No such Store: ${store.name}`);
|
|
457
|
+
return state;
|
|
458
|
+
}
|
|
459
|
+
else {
|
|
460
|
+
const str = this.encryptionTest.isEncrypted(store.data) ? store.data : JSON.stringify(store.data);
|
|
461
|
+
const dataStr = (this.isObjectOrArray(str)) ? (store.options.encrypted) ? this.encryption.encrypt(str, this.app.appID) : store.data : store.data;
|
|
462
|
+
const localData = (dataStr && settings.options?.storage === StorageType.GLOBAL) ? [{ data: dataStr, id: settings.id }] : [];
|
|
463
|
+
const sessionData = (dataStr && settings.options?.storage === StorageType.SESSION) ? [{ data: dataStr, id: settings.id }] : [];
|
|
464
|
+
return {
|
|
465
|
+
...state,
|
|
466
|
+
localStores: [...state.localStores, ...localData],
|
|
467
|
+
sessionStores: [...state.sessionStores, ...sessionData],
|
|
468
|
+
settings: [...state.settings, ...[settings]],
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
});
|
|
472
|
+
this.createStore = this.updater((state, store) => {
|
|
473
|
+
const settings = StorageOption.adapt(store);
|
|
474
|
+
const foundStore = state?.settings.some(item => item.name === store.name);
|
|
475
|
+
const expires = (settings.options?.expiresIn) ? this.utils.expires(settings.options?.expiresIn) : 0;
|
|
476
|
+
if (settings.options)
|
|
477
|
+
settings.options.expires = expires;
|
|
478
|
+
store.name = this.validStoreName(store.name);
|
|
479
|
+
if (foundStore) {
|
|
480
|
+
return state;
|
|
481
|
+
}
|
|
482
|
+
else {
|
|
483
|
+
if (!this.isObjectOrArray(store.data)) {
|
|
484
|
+
console.warn('Data must ba an Object or Array');
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
487
|
+
store.data = (this.isString(store.data)) ? JSON.parse(store.data) : store.data;
|
|
488
|
+
const dataStr = (store.options.encrypted) ? this.encryption.encrypt(store.data, this.app.appID) : store.data;
|
|
489
|
+
const localData = (settings.options?.storage === StorageType.GLOBAL) ? [{ data: dataStr, id: settings.id }] : [];
|
|
490
|
+
const sessionData = (settings.options?.storage === StorageType.SESSION) ? [{ data: dataStr, id: settings.id }] : [];
|
|
491
|
+
return {
|
|
492
|
+
...state,
|
|
493
|
+
localStores: [...state.localStores, ...localData],
|
|
494
|
+
sessionStores: [...state.sessionStores, ...sessionData],
|
|
495
|
+
settings: [...state.settings, ...[settings]],
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
});
|
|
499
|
+
this.updateStore = this.updater((state, store) => {
|
|
500
|
+
store.name = this.validStoreName(store.name);
|
|
501
|
+
const settings = state.settings.find(item => item.name === store.name);
|
|
502
|
+
if (settings) {
|
|
503
|
+
const type = settings.options?.storage;
|
|
504
|
+
const hasStore = this.hasGlobalStorage(type, settings.id || '');
|
|
505
|
+
if (!hasStore) {
|
|
506
|
+
console.warn(`No such Store: ${store.name}`);
|
|
507
|
+
}
|
|
508
|
+
else {
|
|
509
|
+
const dataStr = (settings.options?.encrypted) ? this.encryption.encrypt(store.data, this.app.appID) : store.data;
|
|
510
|
+
const localData = (dataStr && settings.options?.storage === StorageType.GLOBAL) ? [{ data: dataStr, id: settings.id }] : [];
|
|
511
|
+
const sessionData = (dataStr && settings.options?.storage === StorageType.SESSION) ? [{ data: dataStr, id: settings.id }] : [];
|
|
512
|
+
state.localStores = state.localStores.filter(item => item.id !== settings.id);
|
|
513
|
+
state.sessionStores = state.sessionStores.filter(item => item.id !== settings.id);
|
|
514
|
+
return {
|
|
515
|
+
...state,
|
|
516
|
+
localStores: [...state.localStores, ...localData],
|
|
517
|
+
sessionStores: [...state.sessionStores, ...sessionData],
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
return state;
|
|
522
|
+
});
|
|
523
|
+
this.deleteStore = this.updater((state, store) => {
|
|
524
|
+
store.name = this.validStoreName(store.name);
|
|
525
|
+
const id = state.settings.find(item => item.name === store.name)?.id;
|
|
526
|
+
if (id) {
|
|
527
|
+
const settings = state.settings.filter(item => item.id !== id);
|
|
528
|
+
const localData = state.localStores.filter(item => item.id !== id);
|
|
529
|
+
const sessionData = state.sessionStores.filter(item => item.id !== id);
|
|
530
|
+
return {
|
|
531
|
+
...state,
|
|
532
|
+
localStores: [...localData],
|
|
533
|
+
sessionStores: [...sessionData],
|
|
534
|
+
settings: [...settings],
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
else {
|
|
538
|
+
console.warn(`No such Store: ${store.name}`);
|
|
539
|
+
return state;
|
|
540
|
+
}
|
|
541
|
+
});
|
|
542
|
+
this.isObjectOrArray = (obj) => {
|
|
543
|
+
try {
|
|
544
|
+
//obj = (Object.prototype.toString.call(obj) === '[object Object]') ? JSON.parse(obj) : obj
|
|
545
|
+
return typeof obj === 'object' || Array.isArray(obj);
|
|
546
|
+
}
|
|
547
|
+
catch (error) {
|
|
548
|
+
return false;
|
|
549
|
+
}
|
|
550
|
+
};
|
|
551
|
+
this.retrieveState();
|
|
552
|
+
this.startTimer();
|
|
553
|
+
}
|
|
554
|
+
resetStore() {
|
|
555
|
+
const newState = {
|
|
556
|
+
localStores: [],
|
|
557
|
+
sessionStores: [],
|
|
558
|
+
settings: [],
|
|
559
|
+
};
|
|
560
|
+
this.updateState(newState);
|
|
561
|
+
this.persistState(newState);
|
|
562
|
+
}
|
|
563
|
+
persistState(state) {
|
|
564
|
+
if (!state)
|
|
565
|
+
return;
|
|
566
|
+
const strLocal = JSON.stringify(state.localStores);
|
|
567
|
+
localStorage.setItem(this.storageName, strLocal);
|
|
568
|
+
const strSession = JSON.stringify(state.sessionStores);
|
|
569
|
+
sessionStorage.setItem(this.storageName, strSession);
|
|
570
|
+
const settingsStr = this.encryption.encrypt(state.settings, this.app.appID);
|
|
571
|
+
localStorage.setItem(this.storageSettingsName, settingsStr || '');
|
|
572
|
+
}
|
|
573
|
+
expired(state) {
|
|
574
|
+
if (!state)
|
|
575
|
+
return [];
|
|
576
|
+
return state.settings?.filter(item => (item.options?.expires || 0) > 0 && this.utils.hasExpired(item.options?.expires || 0));
|
|
577
|
+
}
|
|
578
|
+
retrieveState() {
|
|
579
|
+
const str = localStorage.getItem(this.storageSettingsName);
|
|
580
|
+
const localStr = localStorage.getItem(this.storageName);
|
|
581
|
+
const sessionStr = sessionStorage.getItem(this.storageName);
|
|
582
|
+
const localData = (localStr) ? JSON.parse(localStr) : null;
|
|
583
|
+
const sessionData = (sessionStr) ? JSON.parse(sessionStr) : null;
|
|
584
|
+
const decryptedStr = str ? this.encryption.decrypt(str, this.app.appID) : null;
|
|
585
|
+
const settings = (decryptedStr && decryptedStr !== null) ? JSON.parse(decryptedStr) : [];
|
|
586
|
+
settings.forEach(store => {
|
|
587
|
+
const expired = (store.options?.expires || 0) > 0 && this.utils.hasExpired(store.options?.expires || 0);
|
|
588
|
+
if (!expired) {
|
|
589
|
+
const hasStorage = this.hasGlobalStorage(store.options?.storage, store.id || '');
|
|
590
|
+
if (!hasStorage) {
|
|
591
|
+
this.createStore({ id: store.id, name: store.name, data: [], options: SettingOptions.adapt(store.options) });
|
|
592
|
+
}
|
|
593
|
+
else {
|
|
594
|
+
const storeData = (store.options?.storage === StorageType.GLOBAL) ? localData.find(item => item.id === store.id) : sessionData.find(item => item.id === store.id);
|
|
595
|
+
this.setStore({ id: store.id || '', name: store.name, data: storeData?.data, options: SettingOptions.adapt(store.options) });
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
});
|
|
599
|
+
this.stateRetrieved = true;
|
|
600
|
+
}
|
|
601
|
+
isString(obj) {
|
|
602
|
+
return (Object.prototype.toString.call(obj) === '[object String]');
|
|
603
|
+
}
|
|
604
|
+
fixAndParseJSON(jsonString) {
|
|
605
|
+
const fixedString = jsonString
|
|
606
|
+
.replace(/'/g, '"')
|
|
607
|
+
.replace(/([{,]\s*)(\w+)\s*:/g, '$1"$2":')
|
|
608
|
+
.replace(/,\s*(\}|\])/g, '$1');
|
|
609
|
+
try {
|
|
610
|
+
return JSON.parse(fixedString);
|
|
611
|
+
}
|
|
612
|
+
catch (error) {
|
|
613
|
+
throw new Error('Failed to parse JSON: ' + error.message);
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
validStoreName(str) {
|
|
617
|
+
return str
|
|
618
|
+
.toLowerCase() // Convert to lowercase
|
|
619
|
+
.replace(/\s+/g, '_') // Replace spaces with underscores
|
|
620
|
+
.replace(/[^ -~]/g, ''); // Remove non-ASCII characters
|
|
621
|
+
}
|
|
622
|
+
hasGlobalStorage(type = StorageType.GLOBAL, id) {
|
|
623
|
+
const strData = [];
|
|
624
|
+
if (type === StorageType.GLOBAL) {
|
|
625
|
+
const str = localStorage.getItem(this.storageName);
|
|
626
|
+
strData.push(...(str ? JSON.parse(str) : []));
|
|
627
|
+
}
|
|
628
|
+
else {
|
|
629
|
+
const str = sessionStorage.getItem(this.storageName);
|
|
630
|
+
strData.push(...(str ? JSON.parse(str) : []));
|
|
631
|
+
}
|
|
632
|
+
const found = strData.find(store => store.id === id);
|
|
633
|
+
return (found) ? true : false;
|
|
634
|
+
}
|
|
635
|
+
ngOnDestroy() {
|
|
636
|
+
this.persistence$.unsubscribe();
|
|
637
|
+
}
|
|
638
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: LocalStorageManagerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
639
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: LocalStorageManagerService, providedIn: 'root' }); }
|
|
640
|
+
}
|
|
641
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: LocalStorageManagerService, decorators: [{
|
|
642
|
+
type: Injectable,
|
|
643
|
+
args: [{ providedIn: 'root' }]
|
|
644
|
+
}], ctorParameters: function () { return []; } });
|
|
645
|
+
|
|
646
|
+
class HeadersService {
|
|
647
|
+
constructor() {
|
|
648
|
+
this.headers = {};
|
|
649
|
+
}
|
|
650
|
+
generateHeaders(headers) {
|
|
651
|
+
const allHeaders = headers ? { ...this.headers, ...headers } : {};
|
|
652
|
+
return { headers: new HttpHeaders(allHeaders) };
|
|
653
|
+
}
|
|
654
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: HeadersService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
655
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: HeadersService, providedIn: 'root' }); }
|
|
656
|
+
}
|
|
657
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: HeadersService, decorators: [{
|
|
658
|
+
type: Injectable,
|
|
659
|
+
args: [{
|
|
660
|
+
providedIn: 'root'
|
|
661
|
+
}]
|
|
662
|
+
}] });
|
|
663
|
+
|
|
664
|
+
class PathQueryService {
|
|
665
|
+
constructor() {
|
|
666
|
+
this.removeEmptyParams = (obj) => Object.fromEntries(Object.entries(obj)
|
|
667
|
+
.map(([key, value]) => [key, value && typeof value === 'object' ? this.removeEmptyParams(value) : value])
|
|
668
|
+
.filter(([_, value]) => value !== undefined && value !== ""));
|
|
669
|
+
}
|
|
670
|
+
buildAPIPath(server, params) {
|
|
671
|
+
server = (Array.isArray(server)) ? server.join('/') : server;
|
|
672
|
+
const pathObjects = (params) ? params.filter((path) => (!this.isObject(path))) : [];
|
|
673
|
+
const queryObjects = (params) ? params.filter((path) => (this.isObject(path))) : [];
|
|
674
|
+
const query = this.removeEmptyParams(queryObjects.reduce((r, c) => {
|
|
675
|
+
Object.entries(c).forEach(([key, value]) => {
|
|
676
|
+
r[key] = Array.isArray(value) ? value.join(',') : value;
|
|
677
|
+
});
|
|
678
|
+
return r;
|
|
679
|
+
}, {}));
|
|
680
|
+
let path = this.buildRestPath(server, pathObjects);
|
|
681
|
+
return (Object.keys(query).length > 0) ? path + '?' + this.buildQueryPath(query) : path;
|
|
682
|
+
}
|
|
683
|
+
buildRestPath(server, params) {
|
|
684
|
+
server = (server.charAt(0) === '/') ? server.substring(1) : server;
|
|
685
|
+
return this.cleanUrlPath(server + '/' + params.join('/'));
|
|
686
|
+
}
|
|
687
|
+
buildQueryPath(params) {
|
|
688
|
+
const searchParams = new URLSearchParams();
|
|
689
|
+
for (const key in params) {
|
|
690
|
+
if (Array.isArray(params[key])) {
|
|
691
|
+
params[key].forEach((value) => searchParams.append(key, value));
|
|
692
|
+
}
|
|
693
|
+
else {
|
|
694
|
+
searchParams.append(key, params[key]);
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
return searchParams.toString();
|
|
698
|
+
}
|
|
699
|
+
cleanUrlPath(str) {
|
|
700
|
+
return str.replace(/([^:]\/)\/+/g, "$1").replace(/\/$/, '');
|
|
701
|
+
}
|
|
702
|
+
isObject(val) {
|
|
703
|
+
return (val === null) ? false : ((typeof val === 'function') || (typeof val === 'object'));
|
|
704
|
+
}
|
|
705
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PathQueryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
706
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PathQueryService, providedIn: 'root' }); }
|
|
707
|
+
}
|
|
708
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PathQueryService, decorators: [{
|
|
709
|
+
type: Injectable,
|
|
710
|
+
args: [{
|
|
711
|
+
providedIn: 'root'
|
|
712
|
+
}]
|
|
713
|
+
}] });
|
|
714
|
+
|
|
715
|
+
class AsymmetricalEncryptionService {
|
|
716
|
+
constructor() { }
|
|
717
|
+
generateKeyPair(modulusLength = 2048) {
|
|
718
|
+
// modulusLength - 1024, 2048, 4096
|
|
719
|
+
// hash - SHA-256
|
|
720
|
+
return from(crypto.subtle.generateKey({
|
|
721
|
+
name: "RSA-OAEP",
|
|
722
|
+
modulusLength,
|
|
723
|
+
publicExponent: new Uint8Array([1, 0, 1]),
|
|
724
|
+
hash: { name: "SHA-256" }
|
|
725
|
+
}, true, // Extractable keys
|
|
726
|
+
["encrypt", "decrypt"]));
|
|
727
|
+
}
|
|
728
|
+
encryptData(publicKey, data) {
|
|
729
|
+
const encodedData = new TextEncoder().encode(data);
|
|
730
|
+
return from(crypto.subtle.encrypt({
|
|
731
|
+
name: "RSA-OAEP"
|
|
732
|
+
}, publicKey, encodedData)).pipe(map(enc => this.arrayBufferToBase64(enc)));
|
|
733
|
+
}
|
|
734
|
+
decryptData(privateKey, encryptedData) {
|
|
735
|
+
const encStr = this.base64ToArrayBuffer(encryptedData);
|
|
736
|
+
return from(crypto.subtle.decrypt({
|
|
737
|
+
name: "RSA-OAEP"
|
|
738
|
+
}, privateKey, encStr).then(decryptedData => {
|
|
739
|
+
return new TextDecoder().decode(decryptedData);
|
|
740
|
+
}));
|
|
741
|
+
}
|
|
742
|
+
pemToArrayBuffer(pem) {
|
|
743
|
+
const b64 = pem.replace(/(-----BEGIN (RSA )?(PRIVATE|PUBLIC) KEY-----|-----END (RSA )?(PRIVATE|PUBLIC) KEY-----|\n)/g, '');
|
|
744
|
+
const binaryString = atob(b64);
|
|
745
|
+
const len = binaryString.length;
|
|
746
|
+
const buffer = new ArrayBuffer(len);
|
|
747
|
+
const view = new Uint8Array(buffer);
|
|
748
|
+
for (let i = 0; i < len; i++) {
|
|
749
|
+
view[i] = binaryString.charCodeAt(i);
|
|
750
|
+
}
|
|
751
|
+
return buffer;
|
|
752
|
+
}
|
|
753
|
+
base64ToArrayBuffer(base64) {
|
|
754
|
+
const binaryString = atob(base64);
|
|
755
|
+
const len = binaryString.length;
|
|
756
|
+
const buffer = new ArrayBuffer(len);
|
|
757
|
+
const view = new Uint8Array(buffer);
|
|
758
|
+
for (let i = 0; i < len; i++) {
|
|
759
|
+
view[i] = binaryString.charCodeAt(i);
|
|
760
|
+
}
|
|
761
|
+
return buffer;
|
|
762
|
+
}
|
|
763
|
+
arrayBufferToBase64(buffer) {
|
|
764
|
+
let binary = '';
|
|
765
|
+
const bytes = new Uint8Array(buffer);
|
|
766
|
+
const len = bytes.byteLength;
|
|
767
|
+
for (let i = 0; i < len; i++) {
|
|
768
|
+
binary += String.fromCharCode(bytes[i]);
|
|
769
|
+
}
|
|
770
|
+
return window.btoa(binary);
|
|
771
|
+
}
|
|
772
|
+
base64ToPEM(base64Key, publicKey = true) {
|
|
773
|
+
const keyType = (publicKey) ? 'PUBLIC' : 'PRIVATE';
|
|
774
|
+
const header = `-----BEGIN ${keyType} KEY-----\n`;
|
|
775
|
+
const footer = `\n-----END ${keyType} KEY-----`;
|
|
776
|
+
const keyBody = base64Key.match(/.{1,64}/g)?.join("\n") || "";
|
|
777
|
+
return header + keyBody + footer;
|
|
778
|
+
}
|
|
779
|
+
pemToCryptoKey(pem, algorithm, extractable, keyUsages, format = "pkcs8" //"raw" | "pkcs8" | "spki"
|
|
780
|
+
) {
|
|
781
|
+
const buffer = this.pemToArrayBuffer(pem);
|
|
782
|
+
console.log('buffer found ', buffer);
|
|
783
|
+
return from(crypto.subtle.importKey(format, buffer, algorithm, extractable, keyUsages));
|
|
784
|
+
}
|
|
785
|
+
// TESTING
|
|
786
|
+
testGenerateKeys() {
|
|
787
|
+
const testSequence = new BehaviorSubject("");
|
|
788
|
+
const testSequence$ = testSequence.asObservable();
|
|
789
|
+
testSequence.next("Test Generating Keys");
|
|
790
|
+
// GENERATE KEYS
|
|
791
|
+
this.generateKeyPair().subscribe((data) => {
|
|
792
|
+
console.log("GEN-GENERATED KEYS: ", data);
|
|
793
|
+
crypto.subtle.exportKey('spki', data.publicKey)
|
|
794
|
+
.then(key => {
|
|
795
|
+
const publicKeyBase64 = this.arrayBufferToBase64(key);
|
|
796
|
+
console.log("GEN-EXPORT PUBLIC KEY: ", this.base64ToPEM(publicKeyBase64, true));
|
|
797
|
+
});
|
|
798
|
+
crypto.subtle.exportKey('pkcs8', data.privateKey)
|
|
799
|
+
.then(key => {
|
|
800
|
+
const privateKeyBase64 = this.arrayBufferToBase64(key);
|
|
801
|
+
console.log("GEN-EXPORT PRIVATE KEY: ", this.base64ToPEM(privateKeyBase64, false));
|
|
802
|
+
});
|
|
803
|
+
// TEST WITH GENERATED KEYS
|
|
804
|
+
this.encryptData(data.publicKey, "Sample Data here - Mike Bonifacio")
|
|
805
|
+
.subscribe((enc) => {
|
|
806
|
+
console.log("GEN-ENCRYPT==>: ", enc);
|
|
807
|
+
this.decryptData(data.privateKey, enc).subscribe((dec) => {
|
|
808
|
+
testSequence.next('Test Passed');
|
|
809
|
+
testSequence.complete();
|
|
810
|
+
});
|
|
811
|
+
});
|
|
812
|
+
});
|
|
813
|
+
return testSequence$;
|
|
814
|
+
}
|
|
815
|
+
testDecryptionWithKeys() {
|
|
816
|
+
const testSequence = new BehaviorSubject("");
|
|
817
|
+
const testSequence$ = testSequence.asObservable();
|
|
818
|
+
testSequence.next("Test Generating Keys");
|
|
819
|
+
const privateKey = `-----BEGIN PRIVATE KEY-----
|
|
820
|
+
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDKVBkCWNb9eHVF
|
|
821
|
+
twAA5mdAyvXFix3WavdrsoYRkvtCl8ogDBQf8f6N0bRkDF1zcVHRrNAEyozXDtuD
|
|
822
|
+
kFgSiivBFREI7fiS3sOvduq6FaGXzX0IY6oaVMu8oOPgDvC0yvoohR+0Qxq2d6lP
|
|
823
|
+
SPyHeKQaxP4KxzCJJzB1XAcbd2eFzUUDv25mNlwdVkmW8FI/sAxnJLgPTW78dzfX
|
|
824
|
+
Ddfk+nxdIqxVKSLATnL0kMN7aGJx24UozioAIpzvvvF5/+7HTd1gi6NeCqrAYr3g
|
|
825
|
+
iP364AE26CFwFvHnafO9uNQhlQ+uiNnOoInXByfFGVP+RidSpfLzaxDjs6RDwpol
|
|
826
|
+
h098btONAgMBAAECggEAAVhHM2OD5IkUtuowwcKNaCTKGmrClRE3gMuzJeBsSJEy
|
|
827
|
+
uAReUFILZylcnhegDytSti2vDA9T7xqtO6CU0p3V8LpU89JMOPRb/deirI4UXSpx
|
|
828
|
+
xbNyjirOO6bnrNlOWfKexdQQsikYPzF2iM1Scpr1u9ZDCHZOl1ZYSrkve5My4coP
|
|
829
|
+
3Gx6tTBqL0jQBfcpf4ES8BH3JSawR/MgqzDS74+bI2VH+kvuPAC1L4fBgaV3VUv0
|
|
830
|
+
nFzim5A3s5VaXPgJbEIghP9BkYGFkeT9ma3nliEZWc96AzsmowFn1ayCvrNVPo9R
|
|
831
|
+
ECD2NnttBiCJZt3H93sR9+OTeA8Bls+aZGlKd5mLAQKBgQDpf8nIYUm/Zmls7/lQ
|
|
832
|
+
QBUSGHEYrbjNsEejQsPgNDtg2EVSxdUkvTn7Wb+HExTk09Z5bNvhaO2532/FH9y8
|
|
833
|
+
ahSXafiMXpfR2olwXdOB9yXXl4F5C0LGnmzGim4OSeWzAX0JCL470KaS0QN3EmoG
|
|
834
|
+
9F0YCfMnZXNTweSSx4xvlO6ggQKBgQDd01uIolm7z1DIWCwNERAXP2GDwcvxyzYK
|
|
835
|
+
YqJfKDeHt2t6yIzZMMotnPDy+OlcdGspIIHkB5a+SZN5Nt7DUKg/MnyOZ3KbVq41
|
|
836
|
+
k7dR4Ka3LRnGkesy7lSr7ef1rfquyR75OXJQcPWvkFcLu0TMiQhvptatZ8VN+F4n
|
|
837
|
+
fDQELRotDQKBgQCgN+AUT7VT0QjcCBnRV/ddUEoiPenFsYSmYMSYzh5ESIHg1wB2
|
|
838
|
+
0iS79Iw4Of6nOTg8X1bM57vfQ5Kk90T2P+/bKYqzfqC8DTErWiYsUpKzyTC9Bt4N
|
|
839
|
+
/Vz0Kr5zrX8ggg/yp/4oevYhXav8AzWfigNq4EDpMnKc8TlPAf+5/L50gQKBgQC2
|
|
840
|
+
mLPhPhr1gUszD3l7bA89w7uGlLFHoQoj9FtKTzWervp3QLzIT+QtNeIVb5XQuDg5
|
|
841
|
+
y3uAdEq+6pvNjMBEMJG+K9Xh9v+dJPYUPjsJ2A4D/MkZ7qWX8B2cxSJK1uLim8W2
|
|
842
|
+
S3ZxBvsGgJ9WldmlMCvUlJZkeWYtr4P5psC+q02/xQKBgQCDII/jcV2+lRM2iNTe
|
|
843
|
+
vBijZSaNMEWOK/Y+/HFK8GYKXnB+xNWhrm8rKgWGzpDJAUPMh9Tt+leW9LKEBuzF
|
|
844
|
+
Y1bGblmGm1zsPWpIx05Fdtv9eOJmFnXbGzIsYyhadZnPcQOWhqxtpVxSuul0Jc7Q
|
|
845
|
+
XNq4qPaPXhbQAshgtyBt75DMkw==
|
|
846
|
+
-----END PRIVATE KEY-----`;
|
|
847
|
+
const pubKey = `-----BEGIN PUBLIC KEY-----
|
|
848
|
+
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAylQZAljW/Xh1RbcAAOZn
|
|
849
|
+
QMr1xYsd1mr3a7KGEZL7QpfKIAwUH/H+jdG0ZAxdc3FR0azQBMqM1w7bg5BYEoor
|
|
850
|
+
wRURCO34kt7Dr3bquhWhl819CGOqGlTLvKDj4A7wtMr6KIUftEMatnepT0j8h3ik
|
|
851
|
+
GsT+CscwiScwdVwHG3dnhc1FA79uZjZcHVZJlvBSP7AMZyS4D01u/Hc31w3X5Pp8
|
|
852
|
+
XSKsVSkiwE5y9JDDe2hicduFKM4qACKc777xef/ux03dYIujXgqqwGK94Ij9+uAB
|
|
853
|
+
NughcBbx52nzvbjUIZUProjZzqCJ1wcnxRlT/kYnUqXy82sQ47OkQ8KaJYdPfG7T
|
|
854
|
+
jQIDAQAB
|
|
855
|
+
-----END PUBLIC KEY-----`;
|
|
856
|
+
const algorithm = {
|
|
857
|
+
name: "RSA-OAEP",
|
|
858
|
+
modulusLength: 2048,
|
|
859
|
+
publicExponent: new Uint8Array([1, 0, 1]),
|
|
860
|
+
hash: "SHA-256"
|
|
861
|
+
};
|
|
862
|
+
// ENCRYPT
|
|
863
|
+
this.pemToCryptoKey(pubKey, algorithm, true, ["encrypt"], "spki").subscribe((keyPublic) => {
|
|
864
|
+
// console.log("PUBLIC KEY: ", keyPublic)
|
|
865
|
+
this.encryptData(keyPublic, "Sample Data here - Mike Bonifacio")
|
|
866
|
+
.subscribe((str) => {
|
|
867
|
+
console.log("ENCRYPTED STRING: ", str);
|
|
868
|
+
testSequence.next('encrypted string success');
|
|
869
|
+
});
|
|
870
|
+
});
|
|
871
|
+
const enc = "r7UOuEGBZ6jbVTOIBuv9jFOEtRiedmS27hfW40k7XKCTsQ5VhWup0qu2BA2ANZaGKii/uehJ/RSPiyfGbJ6leJrat0mA1hPqH/XodaBzLMDigvdYM1NSXFZMDa40OpLUIXbPlAvybjcGu7Bal+LMA8htHrFu/OmM/fHI66xOzzpuXSYx0OkWjtY81rUQ1FFkvrkx2jGFPZ0p+Nw/v3Q4cJnet6V63vCueFlD749VMcVXo7pvz4AIPpB+IOy3kbxaYTPakLVioJERU6GFMXWkECuRvohhS9CETcORZtRVRkvhcHk3lxgIhfqojUKYgD6K5dDELDPsWIS+iTG1XIQIlg==";
|
|
872
|
+
// DECRYPTION
|
|
873
|
+
this.pemToCryptoKey(privateKey, algorithm, false, ["decrypt"], "pkcs8").subscribe((key) => {
|
|
874
|
+
// console.log("DECRYPT ===", key)
|
|
875
|
+
this.decryptData(key, enc)
|
|
876
|
+
.pipe(catchError(err => {
|
|
877
|
+
console.log("ERROR: ", err);
|
|
878
|
+
return EMPTY;
|
|
879
|
+
})).subscribe((str) => {
|
|
880
|
+
console.log("DECRYPTED STRING: ", str);
|
|
881
|
+
testSequence.next('decrypted string success');
|
|
882
|
+
});
|
|
883
|
+
});
|
|
884
|
+
return testSequence$;
|
|
885
|
+
}
|
|
886
|
+
;
|
|
887
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: AsymmetricalEncryptionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
888
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: AsymmetricalEncryptionService, providedIn: 'root' }); }
|
|
889
|
+
}
|
|
890
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: AsymmetricalEncryptionService, decorators: [{
|
|
891
|
+
type: Injectable,
|
|
892
|
+
args: [{
|
|
893
|
+
providedIn: 'root'
|
|
894
|
+
}]
|
|
895
|
+
}], ctorParameters: function () { return []; } });
|
|
896
|
+
|
|
897
|
+
function requestStreaming() {
|
|
898
|
+
return input$ => input$.pipe(filter((event) => event.type === HttpEventType.DownloadProgress || event.type === HttpEventType.Response), map((event) => {
|
|
899
|
+
if (event.type === HttpEventType.DownloadProgress) {
|
|
900
|
+
return event.partialText || '';
|
|
901
|
+
}
|
|
902
|
+
else if (event.type === HttpEventType.Response) {
|
|
903
|
+
return event.body;
|
|
904
|
+
}
|
|
905
|
+
}), map((data) => {
|
|
906
|
+
if (typeof data === 'string') {
|
|
907
|
+
const matches = data.match(/{[^}]+}/g) || [];
|
|
908
|
+
return (matches.length > 0) ? matches.map(match => JSON.parse(match)) : data;
|
|
909
|
+
}
|
|
910
|
+
return data;
|
|
911
|
+
}));
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
class RequestService {
|
|
915
|
+
constructor() {
|
|
916
|
+
this.http = inject(HttpClient);
|
|
917
|
+
this.pathQueryService = inject(PathQueryService);
|
|
918
|
+
this.headersService = inject(HeadersService);
|
|
919
|
+
this.isPending = new BehaviorSubject(false);
|
|
920
|
+
this.isPending$ = this.isPending.asObservable();
|
|
921
|
+
this.progress = new BehaviorSubject(0);
|
|
922
|
+
this.progress$ = this.progress.asObservable();
|
|
923
|
+
}
|
|
924
|
+
getRecordRequest(options) {
|
|
925
|
+
this.isPending.next(true);
|
|
926
|
+
const urlPath = this.buildUrlPath(options);
|
|
927
|
+
const headers = this.buildCombinedHeaders(options);
|
|
928
|
+
return (options.stream)
|
|
929
|
+
? this.http.get(urlPath, headers).pipe(requestStreaming(), this.handleFinalize())
|
|
930
|
+
: this.http.get(urlPath, headers).pipe(this.request(options), this.handleFinalize());
|
|
931
|
+
}
|
|
932
|
+
createRecordRequest(options, data) {
|
|
933
|
+
this.isPending.next(true);
|
|
934
|
+
const urlPath = this.buildUrlPath(options);
|
|
935
|
+
const headers = this.buildCombinedHeaders(options);
|
|
936
|
+
return (options.stream)
|
|
937
|
+
? this.http.post(urlPath, data, headers).pipe(requestStreaming(), this.handleFinalize())
|
|
938
|
+
: this.http.post(urlPath, data, headers).pipe(this.request(options), this.handleFinalize());
|
|
939
|
+
}
|
|
940
|
+
updateRecordRequest(options, data) {
|
|
941
|
+
this.isPending.next(true);
|
|
942
|
+
const urlPath = this.buildUrlPath(options);
|
|
943
|
+
const headers = this.buildHeaders(options);
|
|
944
|
+
return this.http.put(urlPath, data, headers).pipe(this.request(options), this.handleFinalize());
|
|
945
|
+
}
|
|
946
|
+
deleteRecordRequest(options) {
|
|
947
|
+
this.isPending.next(true);
|
|
948
|
+
const urlPath = this.buildUrlPath(options);
|
|
949
|
+
const headers = this.buildHeaders(options);
|
|
950
|
+
return this.http.delete(urlPath, headers).pipe(this.handleFinalize());
|
|
951
|
+
}
|
|
952
|
+
// Helper functions
|
|
953
|
+
buildUrlPath(options) {
|
|
954
|
+
return this.pathQueryService.buildAPIPath(options.server, options.path);
|
|
955
|
+
}
|
|
956
|
+
buildHeaders(options) {
|
|
957
|
+
return this.headersService.generateHeaders(options.headers);
|
|
958
|
+
}
|
|
959
|
+
buildCombinedHeaders(options) {
|
|
960
|
+
const headers = this.headersService.generateHeaders(options.headers);
|
|
961
|
+
return this.combineHeaders(headers, options.stream);
|
|
962
|
+
}
|
|
963
|
+
request(options) {
|
|
964
|
+
return (source$) => {
|
|
965
|
+
return source$.pipe(map(data => {
|
|
966
|
+
if (options.adapter) {
|
|
967
|
+
return Array.isArray(data)
|
|
968
|
+
? data.map((item) => options.adapter(item))
|
|
969
|
+
: options.adapter(data);
|
|
970
|
+
}
|
|
971
|
+
return data;
|
|
972
|
+
}));
|
|
973
|
+
};
|
|
974
|
+
}
|
|
975
|
+
downloadFileRequest(options) {
|
|
976
|
+
this.isPending.next(true);
|
|
977
|
+
const urlPath = this.buildUrlPath(options);
|
|
978
|
+
return this.http.get(urlPath, { responseType: 'blob', observe: 'events', reportProgress: true })
|
|
979
|
+
.pipe(map((event) => {
|
|
980
|
+
this.isPending.next(true);
|
|
981
|
+
switch (event.type) {
|
|
982
|
+
case HttpEventType.DownloadProgress:
|
|
983
|
+
const status = (event.total) ? Math.round(event.loaded / (event.total || 1) * 100) : 100;
|
|
984
|
+
this.progress.next(status);
|
|
985
|
+
return status;
|
|
986
|
+
case HttpEventType.Response:
|
|
987
|
+
const header_content = event.headers.get('Content-Disposition') || '';
|
|
988
|
+
const file = header_content.split('=')[1].substring(0, header_content.split('=')[1].length);
|
|
989
|
+
this.downloadFile(file, event.body);
|
|
990
|
+
this.isPending.next(false);
|
|
991
|
+
return 100;
|
|
992
|
+
default:
|
|
993
|
+
this.isPending.next(false);
|
|
994
|
+
return 0;
|
|
995
|
+
}
|
|
996
|
+
}));
|
|
997
|
+
}
|
|
998
|
+
handleFinalize() {
|
|
999
|
+
return finalize(() => this.isPending.next(false));
|
|
1000
|
+
}
|
|
1001
|
+
downloadFile(file, fileData) {
|
|
1002
|
+
const navigatorAny = window.navigator;
|
|
1003
|
+
const extension = file.split('.')[1].toLowerCase();
|
|
1004
|
+
var newBlob = new Blob([fileData], { type: this.createFileType(extension) });
|
|
1005
|
+
if (navigatorAny.msSaveOrOpenBlob) {
|
|
1006
|
+
navigatorAny.msSaveOrOpenBlob(newBlob, file);
|
|
1007
|
+
}
|
|
1008
|
+
else {
|
|
1009
|
+
const link = document.createElement('a');
|
|
1010
|
+
const url = window.URL.createObjectURL(newBlob);
|
|
1011
|
+
link.href = url;
|
|
1012
|
+
link.download = file;
|
|
1013
|
+
link.click();
|
|
1014
|
+
window.URL.revokeObjectURL(url);
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
createFileType(ext) {
|
|
1018
|
+
let fileType = "";
|
|
1019
|
+
if (ext == 'pdf' || ext == 'csv') {
|
|
1020
|
+
fileType = `application/${ext}`;
|
|
1021
|
+
}
|
|
1022
|
+
else if (ext == 'jpeg' || ext == 'jpg' || ext == 'png') {
|
|
1023
|
+
fileType = `image/${ext}`;
|
|
1024
|
+
}
|
|
1025
|
+
else if (ext == 'txt') {
|
|
1026
|
+
fileType = 'text/plain';
|
|
1027
|
+
}
|
|
1028
|
+
else if (ext == 'ppt' || ext == 'pot' || ext == 'pps' || ext == 'ppa') {
|
|
1029
|
+
fileType = 'application/vnd.ms-powerpoint';
|
|
1030
|
+
}
|
|
1031
|
+
else if (ext == 'pptx') {
|
|
1032
|
+
fileType = 'application/vnd.openxmlformats-officedocument.presentationml.presentation';
|
|
1033
|
+
}
|
|
1034
|
+
else if (ext == 'doc' || ext == 'dot') {
|
|
1035
|
+
fileType = 'application/msword';
|
|
1036
|
+
}
|
|
1037
|
+
else if (ext == 'docx') {
|
|
1038
|
+
fileType = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
|
|
1039
|
+
}
|
|
1040
|
+
else if (ext == 'xls' || ext == 'xlt' || ext == 'xla') {
|
|
1041
|
+
fileType = 'application/vnd.ms-excel';
|
|
1042
|
+
}
|
|
1043
|
+
else if (ext == 'xlsx') {
|
|
1044
|
+
fileType = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
|
|
1045
|
+
}
|
|
1046
|
+
return fileType;
|
|
1047
|
+
}
|
|
1048
|
+
combineHeaders(headers, isStreaming) {
|
|
1049
|
+
const options = {
|
|
1050
|
+
observe: 'events',
|
|
1051
|
+
responseType: 'text',
|
|
1052
|
+
reportProgress: true,
|
|
1053
|
+
};
|
|
1054
|
+
return (isStreaming) ? { ...headers, ...options } : headers;
|
|
1055
|
+
}
|
|
1056
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: RequestService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1057
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: RequestService, providedIn: 'root' }); }
|
|
1058
|
+
}
|
|
1059
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: RequestService, decorators: [{
|
|
1060
|
+
type: Injectable,
|
|
1061
|
+
args: [{
|
|
1062
|
+
providedIn: 'root'
|
|
1063
|
+
}]
|
|
1064
|
+
}] });
|
|
1065
|
+
|
|
1066
|
+
function countdown(duration) {
|
|
1067
|
+
return defer(() => {
|
|
1068
|
+
const currentCount = { current: duration };
|
|
1069
|
+
return interval(1000).pipe(map(() => --currentCount.current), takeWhile(count => count >= 0));
|
|
1070
|
+
});
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
const DEFAULT_MAX_RETRIES = 3;
|
|
1074
|
+
function delayedRetry(delayMs, maxRetry = DEFAULT_MAX_RETRIES) {
|
|
1075
|
+
let retries = maxRetry;
|
|
1076
|
+
return (src) => src.pipe(retryWhen((errors) => errors.pipe(delay(delayMs), mergeMap(error => retries-- > 0 ? of(error) : throwError(error)))));
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
/**
|
|
1080
|
+
* @param pollInterval
|
|
1081
|
+
* @param stopCondition$
|
|
1082
|
+
* @param isPending$
|
|
1083
|
+
*/
|
|
1084
|
+
function requestPolling(pollInterval, stopCondition$, isPending$) {
|
|
1085
|
+
return (source) => {
|
|
1086
|
+
return interval(pollInterval * 1000)
|
|
1087
|
+
.pipe(startWith(0), tap(() => isPending$.next(true)), mergeMap(() => source), tap(() => isPending$.next(false)), takeUntil(stopCondition$));
|
|
1088
|
+
};
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
class HTTPManagerService extends RequestService {
|
|
1092
|
+
constructor() {
|
|
1093
|
+
super();
|
|
1094
|
+
this.toastMessage = inject(ToastMessageService);
|
|
1095
|
+
this.ng_injector = inject(Injector);
|
|
1096
|
+
this.countdown = new BehaviorSubject(0);
|
|
1097
|
+
this.countdown$ = this.countdown.asObservable();
|
|
1098
|
+
this.error = new BehaviorSubject(false);
|
|
1099
|
+
this.error$ = this.error.asObservable();
|
|
1100
|
+
this.data = new BehaviorSubject(null);
|
|
1101
|
+
this.data$ = this.data.asObservable();
|
|
1102
|
+
this.polling$ = new Subject();
|
|
1103
|
+
}
|
|
1104
|
+
// REQUESTS
|
|
1105
|
+
getRequest(options, params) {
|
|
1106
|
+
this.data.next(null);
|
|
1107
|
+
const updatedOptions = {
|
|
1108
|
+
...options,
|
|
1109
|
+
path: params ? [...options.path, ...params] : options.path,
|
|
1110
|
+
};
|
|
1111
|
+
const func = this.getRecordRequest;
|
|
1112
|
+
const requests = this.createRequest(func, updatedOptions);
|
|
1113
|
+
return this.createObservable(options, requests, func.name).pipe(tap(data => this.data.next(data)));
|
|
1114
|
+
}
|
|
1115
|
+
postRequest(data, options, params) {
|
|
1116
|
+
this.data.next(null);
|
|
1117
|
+
const updatedOptions = {
|
|
1118
|
+
...options,
|
|
1119
|
+
path: params ? [...options.path, ...params] : options.path,
|
|
1120
|
+
};
|
|
1121
|
+
const func = this.createRecordRequest;
|
|
1122
|
+
const requests = this.createRequest(func, updatedOptions, data);
|
|
1123
|
+
return this.createObservable(options, requests, func.name).pipe(tap(data => this.data.next(data)));
|
|
1124
|
+
}
|
|
1125
|
+
putRequest(data, options, params) {
|
|
1126
|
+
this.data.next(null);
|
|
1127
|
+
const updatedOptions = {
|
|
1128
|
+
...options,
|
|
1129
|
+
path: params ? [...options.path, ...params] : options.path,
|
|
1130
|
+
};
|
|
1131
|
+
const func = this.updateRecordRequest;
|
|
1132
|
+
const requests = this.createRequest(func, updatedOptions, data);
|
|
1133
|
+
return this.createObservable(options, requests, func.name).pipe(tap(data => this.data.next(data)));
|
|
1134
|
+
}
|
|
1135
|
+
deleteRequest(options, params) {
|
|
1136
|
+
this.data.next(null);
|
|
1137
|
+
const updatedOptions = {
|
|
1138
|
+
...options,
|
|
1139
|
+
path: params ? [...options.path, ...params] : options.path,
|
|
1140
|
+
};
|
|
1141
|
+
const func = this.deleteRecordRequest;
|
|
1142
|
+
const requests = this.createRequest(func, updatedOptions);
|
|
1143
|
+
return this.createObservable(options, requests, func.name).pipe(tap(data => this.data.next(data)));
|
|
1144
|
+
}
|
|
1145
|
+
downloadRequest(options, params) {
|
|
1146
|
+
const updatedOptions = {
|
|
1147
|
+
...options,
|
|
1148
|
+
path: params ? [...options.path, ...params] : options.path,
|
|
1149
|
+
};
|
|
1150
|
+
const func = this.downloadFileRequest;
|
|
1151
|
+
const requests = this.createRequest(func, updatedOptions);
|
|
1152
|
+
return this.createObservable(options, requests, func.name)
|
|
1153
|
+
.pipe(tap(() => this.error.next(false)), catchError((err) => {
|
|
1154
|
+
this.error.next(true);
|
|
1155
|
+
return EMPTY;
|
|
1156
|
+
}));
|
|
1157
|
+
}
|
|
1158
|
+
// UTILS
|
|
1159
|
+
createObservable(options, request$, funcName) {
|
|
1160
|
+
const polling = options.polling ? (options.polling > 0 ? true : false) : false;
|
|
1161
|
+
const isPolling = polling &&
|
|
1162
|
+
!(funcName === 'deleteRecordRequest' || funcName === 'updateRecordRequest' || funcName === 'createRecordRequest')
|
|
1163
|
+
? true
|
|
1164
|
+
: false;
|
|
1165
|
+
this.polling$.next();
|
|
1166
|
+
const polling$ = (isPolling && options.polling) || 0 >= 3
|
|
1167
|
+
? request$.pipe(requestPolling((options.polling || 0) + 1, this.polling$, this.isPending), tap(() => this.countdown.next(0)), tap(() => {
|
|
1168
|
+
if (!options.polling)
|
|
1169
|
+
return;
|
|
1170
|
+
const count = options.polling ? options.polling : 0;
|
|
1171
|
+
console.log('COUNT:', count);
|
|
1172
|
+
countdown(count)
|
|
1173
|
+
.pipe(map((x) => {
|
|
1174
|
+
console.log('XX:', count, x);
|
|
1175
|
+
const pollingInSec = options.polling || 0;
|
|
1176
|
+
const percentageCompleted = ((pollingInSec - x) / pollingInSec) * 100;
|
|
1177
|
+
return Math.round(percentageCompleted);
|
|
1178
|
+
}))
|
|
1179
|
+
.subscribe((countDownValue) => {
|
|
1180
|
+
console.log(this.countdown.value, countDownValue);
|
|
1181
|
+
this.countdown.next(countDownValue);
|
|
1182
|
+
});
|
|
1183
|
+
}))
|
|
1184
|
+
: request$.pipe(catchError((err) => {
|
|
1185
|
+
if (err instanceof HttpErrorResponse) {
|
|
1186
|
+
this.error.next(true);
|
|
1187
|
+
if (options.displayError)
|
|
1188
|
+
this.handleErrorWithSnackBar(err);
|
|
1189
|
+
return this.handleError(err);
|
|
1190
|
+
}
|
|
1191
|
+
return throwError(() => err);
|
|
1192
|
+
}), finalize(() => this.isPending.next(false)));
|
|
1193
|
+
return polling$.pipe(catchError((err, caught) => {
|
|
1194
|
+
if (err instanceof HttpErrorResponse) {
|
|
1195
|
+
this.error.next(true);
|
|
1196
|
+
this.stopPolling();
|
|
1197
|
+
return this.handleError(err);
|
|
1198
|
+
}
|
|
1199
|
+
return throwError(() => err);
|
|
1200
|
+
}), options.retry.times > 0
|
|
1201
|
+
? delayedRetry((options?.retry.delay || 3) * 1000, (options?.retry.times || 0) - 1)
|
|
1202
|
+
: (source) => source);
|
|
1203
|
+
}
|
|
1204
|
+
createRequest(func, options, data) {
|
|
1205
|
+
const dataItem = this.prepareRequestData(options, data, func.name);
|
|
1206
|
+
return func.bind(this)(dataItem.options, dataItem.data);
|
|
1207
|
+
}
|
|
1208
|
+
prepareRequestData(options, data, funcName) {
|
|
1209
|
+
if ((options.mapper && funcName === 'updateRecordRequest') || funcName === 'createRecordRequest') {
|
|
1210
|
+
data = options.mapper
|
|
1211
|
+
? Array.isArray(data)
|
|
1212
|
+
? map((item) => options.mapper(item))
|
|
1213
|
+
: options.mapper(data)
|
|
1214
|
+
: data;
|
|
1215
|
+
}
|
|
1216
|
+
else {
|
|
1217
|
+
data = options.adapter
|
|
1218
|
+
? Array.isArray(data)
|
|
1219
|
+
? map((item) => options.adapter(item))
|
|
1220
|
+
: options.adapter(data)
|
|
1221
|
+
: data;
|
|
1222
|
+
}
|
|
1223
|
+
return { options: options, data: data };
|
|
1224
|
+
}
|
|
1225
|
+
// MISC
|
|
1226
|
+
handleError(error) {
|
|
1227
|
+
this.isPending.next(false);
|
|
1228
|
+
return throwError(() => error);
|
|
1229
|
+
}
|
|
1230
|
+
handleErrorWithSnackBar(error) {
|
|
1231
|
+
const displayError = ToastDisplay.adapt({
|
|
1232
|
+
message: error.message || `${error.status} - ${error.statusText}`,
|
|
1233
|
+
action: 'OK',
|
|
1234
|
+
color: ToastColors.ERROR,
|
|
1235
|
+
icon: 'error',
|
|
1236
|
+
duration: 5 * 1000, //5 seconds
|
|
1237
|
+
});
|
|
1238
|
+
this.toastMessage.toastMessage(displayError);
|
|
1239
|
+
}
|
|
1240
|
+
stopPolling() {
|
|
1241
|
+
this.isPending.next(false);
|
|
1242
|
+
this.polling$.next();
|
|
1243
|
+
}
|
|
1244
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: HTTPManagerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1245
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: HTTPManagerService, providedIn: 'root' }); }
|
|
1246
|
+
}
|
|
1247
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: HTTPManagerService, decorators: [{
|
|
1248
|
+
type: Injectable,
|
|
1249
|
+
args: [{
|
|
1250
|
+
providedIn: 'root',
|
|
1251
|
+
}]
|
|
1252
|
+
}], ctorParameters: function () { return []; } });
|
|
1253
|
+
|
|
1254
|
+
class DatabaseStorage {
|
|
1255
|
+
constructor(table = '', expiresIn) {
|
|
1256
|
+
this.table = table;
|
|
1257
|
+
this.expiresIn = expiresIn;
|
|
1258
|
+
}
|
|
1259
|
+
static adapt(item) {
|
|
1260
|
+
return new DatabaseStorage(item?.table, item?.expiresIn);
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
class RetryOptions {
|
|
1265
|
+
constructor(times = 0, delay = 0) {
|
|
1266
|
+
this.times = times;
|
|
1267
|
+
this.delay = delay;
|
|
1268
|
+
}
|
|
1269
|
+
static adapt(item) {
|
|
1270
|
+
return new RetryOptions(item?.times ? Math.floor(item.times) : 0, item?.delay ? Math.floor(item.delay) : 0);
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
var DataType;
|
|
1275
|
+
(function (DataType) {
|
|
1276
|
+
DataType[DataType["ANY"] = 0] = "ANY";
|
|
1277
|
+
DataType[DataType["ARRAY"] = 1] = "ARRAY";
|
|
1278
|
+
DataType[DataType["OBJECT"] = 2] = "OBJECT";
|
|
1279
|
+
})(DataType || (DataType = {}));
|
|
1280
|
+
|
|
1281
|
+
class ApiRequest {
|
|
1282
|
+
constructor(server = '', path = [], headers = {}, adapter, mapper, polling, retry = RetryOptions.adapt(), stream = false, displayError = false) {
|
|
1283
|
+
this.server = server;
|
|
1284
|
+
this.path = path;
|
|
1285
|
+
this.headers = headers;
|
|
1286
|
+
this.adapter = adapter;
|
|
1287
|
+
this.mapper = mapper;
|
|
1288
|
+
this.polling = polling;
|
|
1289
|
+
this.retry = retry;
|
|
1290
|
+
this.stream = stream;
|
|
1291
|
+
this.displayError = displayError;
|
|
1292
|
+
}
|
|
1293
|
+
static adapt(item) {
|
|
1294
|
+
const server = Array.isArray(item?.server) ? item.server.join('/') : item?.server || '';
|
|
1295
|
+
const retryOptions = item?.retry ? RetryOptions.adapt(item.retry) : RetryOptions.adapt();
|
|
1296
|
+
return new ApiRequest(server, item?.path, (item?.headers) ? item.headers : {}, item?.adapter, item?.mapper, item?.polling ? Math.floor(item.polling) : 0, retryOptions, item?.stream, item?.displayError);
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
class RequestOptions {
|
|
1301
|
+
constructor(path = [], headers = {}) {
|
|
1302
|
+
this.path = path;
|
|
1303
|
+
this.headers = headers;
|
|
1304
|
+
}
|
|
1305
|
+
static adapt(item) {
|
|
1306
|
+
return new RequestOptions(item?.path, item?.headers);
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
const API_OPTS = new InjectionToken('API_OPTS');
|
|
1311
|
+
const defaultState = {
|
|
1312
|
+
data: [],
|
|
1313
|
+
dataObject: null,
|
|
1314
|
+
};
|
|
1315
|
+
class HTTPManagerStateService extends ComponentStore {
|
|
1316
|
+
constructor(apiOptions, dataType, database) {
|
|
1317
|
+
super(defaultState);
|
|
1318
|
+
this.apiOptions = apiOptions;
|
|
1319
|
+
this.dataType = dataType;
|
|
1320
|
+
this.database = database;
|
|
1321
|
+
this.httpManagerService = inject(HTTPManagerService);
|
|
1322
|
+
this.error$ = this.httpManagerService.error$;
|
|
1323
|
+
this.isPending$ = this.httpManagerService.isPending$;
|
|
1324
|
+
// PAGINATION
|
|
1325
|
+
this.page = new BehaviorSubject(0);
|
|
1326
|
+
this.page$ = this.page.asObservable();
|
|
1327
|
+
this.totalPages = new BehaviorSubject(0);
|
|
1328
|
+
this.totalPages$ = this.totalPages.asObservable();
|
|
1329
|
+
this.percentage = new BehaviorSubject(0);
|
|
1330
|
+
this.percentage$ = this.percentage.asObservable();
|
|
1331
|
+
// ----------
|
|
1332
|
+
this.hasDatabase = false;
|
|
1333
|
+
this.streamedResponse = [];
|
|
1334
|
+
// --------------------------------------------------------------------------------------------------
|
|
1335
|
+
// SELECTORS
|
|
1336
|
+
this.data$ = this.select(({ data, dataObject }) => {
|
|
1337
|
+
const isArray = (this.dataType === DataType.ARRAY) ? true : false;
|
|
1338
|
+
return (isArray) ? data : dataObject;
|
|
1339
|
+
});
|
|
1340
|
+
this.selectRecord$ = (id) => this.select(this.data$, (data) => {
|
|
1341
|
+
if (this.dataType === DataType.ARRAY && Array.isArray(data)) {
|
|
1342
|
+
return data.find(item => item.id === id);
|
|
1343
|
+
}
|
|
1344
|
+
else {
|
|
1345
|
+
return data.id === id ? data : null;
|
|
1346
|
+
}
|
|
1347
|
+
});
|
|
1348
|
+
// --------------------------------------------------------------------------------------------------
|
|
1349
|
+
// UPDATERS
|
|
1350
|
+
this.resetData$ = this.updater((state) => {
|
|
1351
|
+
return { ...state, data: [], dataObject: null };
|
|
1352
|
+
});
|
|
1353
|
+
this.setData$ = this.updater((state, data) => {
|
|
1354
|
+
if (!data)
|
|
1355
|
+
return state;
|
|
1356
|
+
if (this.dataType === DataType.ARRAY) {
|
|
1357
|
+
const dataArray = Array.isArray(data) ? data : [data];
|
|
1358
|
+
const stateDataSample = (state.data.length > 0) ? Object.keys(state.data[0]) : [];
|
|
1359
|
+
const newDataSample = (dataArray.length > 0) ? Object.keys(dataArray[0]) : [];
|
|
1360
|
+
const isSame = (state.data.length === 0) ? false : stateDataSample.every((value, index) => value === newDataSample[index]);
|
|
1361
|
+
const updatedData = (!isSame && dataArray.length !== 0) ? this.updateArrayState([], dataArray) : this.updateArrayState(state.data, dataArray);
|
|
1362
|
+
return { ...state, data: updatedData, dataObject: null };
|
|
1363
|
+
}
|
|
1364
|
+
else {
|
|
1365
|
+
const dataObject = this.isEmpty(data) ? null : data;
|
|
1366
|
+
return { ...state, data: [], dataObject: dataObject };
|
|
1367
|
+
}
|
|
1368
|
+
});
|
|
1369
|
+
this.addData$ = this.updater((state, data) => {
|
|
1370
|
+
if (this.dataType === DataType.ARRAY) {
|
|
1371
|
+
const newState = [...state.data, data];
|
|
1372
|
+
return { ...state, ...{ data: newState } };
|
|
1373
|
+
}
|
|
1374
|
+
else {
|
|
1375
|
+
return { ...state, ...{ dataObject: data } };
|
|
1376
|
+
}
|
|
1377
|
+
});
|
|
1378
|
+
this.deleteData$ = this.updater((state, data) => {
|
|
1379
|
+
if (this.dataType === DataType.ARRAY) {
|
|
1380
|
+
const newState = state.data.filter(item => item.id !== data.id);
|
|
1381
|
+
return { ...state, ...{ data: newState } };
|
|
1382
|
+
}
|
|
1383
|
+
else {
|
|
1384
|
+
return { ...state, ...{ dataObject: null } };
|
|
1385
|
+
}
|
|
1386
|
+
});
|
|
1387
|
+
this.updateData$ = this.updater((state, data) => {
|
|
1388
|
+
if (this.dataType === DataType.ARRAY) {
|
|
1389
|
+
const objIndex = state.data.findIndex(item => item.id === data.id);
|
|
1390
|
+
if (objIndex > -1) {
|
|
1391
|
+
const newState = [...state.data];
|
|
1392
|
+
newState[objIndex] = data;
|
|
1393
|
+
return { ...state, ...{ data: newState } };
|
|
1394
|
+
}
|
|
1395
|
+
return state;
|
|
1396
|
+
}
|
|
1397
|
+
else {
|
|
1398
|
+
return { ...state, ...{ dataObject: data } };
|
|
1399
|
+
}
|
|
1400
|
+
});
|
|
1401
|
+
// --------------------------------------------------------------------------------------------------
|
|
1402
|
+
// EFFECTS
|
|
1403
|
+
this.clearRecords = this.effect(data => data.pipe(tap(() => {
|
|
1404
|
+
if (this.dataType === DataType.ARRAY) {
|
|
1405
|
+
this.setData$([]);
|
|
1406
|
+
}
|
|
1407
|
+
else {
|
|
1408
|
+
this.setData$({});
|
|
1409
|
+
}
|
|
1410
|
+
})));
|
|
1411
|
+
this.refreshData = this.effect(data => data.pipe(tap(() => {
|
|
1412
|
+
// this.apiService.flushDatabase(this.otherOptions.database?.table)
|
|
1413
|
+
this.fetchRecords();
|
|
1414
|
+
})));
|
|
1415
|
+
// --------------------------------------------------------------------------------------------------
|
|
1416
|
+
// CRUD OPERATIONS
|
|
1417
|
+
// FETCH RECORDS
|
|
1418
|
+
this.fetchRecords = (options) => this.effect(() => of(RequestOptions.adapt(options)).pipe(switchMap(() => {
|
|
1419
|
+
this.streamedResponse = [];
|
|
1420
|
+
const requestOptions = this.updateRequestOptions(options?.headers);
|
|
1421
|
+
return this.httpManagerService.getRequest(requestOptions, options?.path)
|
|
1422
|
+
.pipe(tap((data) => {
|
|
1423
|
+
data = (!data) ? (this.dataType === DataType.ARRAY) ? [] : {} : data;
|
|
1424
|
+
this.setData$(data);
|
|
1425
|
+
}));
|
|
1426
|
+
})));
|
|
1427
|
+
// CREATE RECORD
|
|
1428
|
+
this.createRecord = (data, options) => this.effect(() => of(data).pipe(switchMap((data) => {
|
|
1429
|
+
this.streamedResponse = [];
|
|
1430
|
+
const requestOptions = this.updateRequestOptions(options?.headers);
|
|
1431
|
+
return this.httpManagerService.postRequest(data, requestOptions, options?.path)
|
|
1432
|
+
.pipe(tap((data) => {
|
|
1433
|
+
data = (!data) ? (this.dataType === DataType.ARRAY) ? [] : {} : data;
|
|
1434
|
+
this.addData$(data);
|
|
1435
|
+
}));
|
|
1436
|
+
})));
|
|
1437
|
+
// UPDATE RECORD
|
|
1438
|
+
this.updateRecord = (data, options) => this.effect(() => of(data).pipe(concatMap((data) => {
|
|
1439
|
+
this.streamedResponse = [];
|
|
1440
|
+
const requestOptions = this.updateRequestOptions(options?.headers);
|
|
1441
|
+
return this.httpManagerService.putRequest(data, requestOptions, options?.path)
|
|
1442
|
+
.pipe(tap((data) => {
|
|
1443
|
+
data = (!data) ? (this.dataType === DataType.ARRAY) ? [] : {} : data;
|
|
1444
|
+
this.updateData$(data);
|
|
1445
|
+
}));
|
|
1446
|
+
})));
|
|
1447
|
+
// DELETE RECORD
|
|
1448
|
+
this.deleteRecord = (options) => this.effect(() => of(options).pipe(concatMap((data) => {
|
|
1449
|
+
this.streamedResponse = [];
|
|
1450
|
+
const requestOptions = this.updateRequestOptions(options?.headers);
|
|
1451
|
+
return this.httpManagerService.deleteRequest(requestOptions, options?.path)
|
|
1452
|
+
.pipe(tap((data) => {
|
|
1453
|
+
data = (!data) ? (this.dataType === DataType.ARRAY) ? [] : {} : data;
|
|
1454
|
+
this.deleteData$(data);
|
|
1455
|
+
}));
|
|
1456
|
+
})));
|
|
1457
|
+
// --------------------------------------------------------------------------------------------------
|
|
1458
|
+
// FETCH STREAM
|
|
1459
|
+
this.createStream = (data, options) => this.effect(() => of(data).pipe(tap(() => this.httpManagerService.isPending.next(true)), switchMap((data) => {
|
|
1460
|
+
const requestOptions = this.updateRequestOptions(options?.headers);
|
|
1461
|
+
return this.httpManagerService.postRequest(data, requestOptions, options?.path)
|
|
1462
|
+
.pipe(tap((res) => {
|
|
1463
|
+
if (res.length > 0)
|
|
1464
|
+
this.setData$(res);
|
|
1465
|
+
this.streamedResponse = res;
|
|
1466
|
+
}), scan((acc, res) => {
|
|
1467
|
+
const previous = acc.current;
|
|
1468
|
+
const current = res;
|
|
1469
|
+
return { previous, current };
|
|
1470
|
+
}, { previous: null, current: null }), tap(({ previous, current }) => {
|
|
1471
|
+
if (previous && JSON.stringify(previous) === JSON.stringify(current)) {
|
|
1472
|
+
this.httpManagerService.isPending.next(false);
|
|
1473
|
+
this.setData$([]);
|
|
1474
|
+
}
|
|
1475
|
+
else {
|
|
1476
|
+
this.httpManagerService.isPending.next(true);
|
|
1477
|
+
}
|
|
1478
|
+
}));
|
|
1479
|
+
})));
|
|
1480
|
+
this.fetchStream = (options) => this.effect(() => of(options).pipe(tap(() => this.httpManagerService.isPending.next(true)), switchMap((options) => {
|
|
1481
|
+
const requestOptions = this.updateRequestOptions(options?.headers);
|
|
1482
|
+
return this.httpManagerService.getRequest(requestOptions, options?.path)
|
|
1483
|
+
.pipe(tap((res) => {
|
|
1484
|
+
if (res.length > 0)
|
|
1485
|
+
this.setData$(res);
|
|
1486
|
+
this.streamedResponse = res;
|
|
1487
|
+
}), scan((acc, res) => {
|
|
1488
|
+
const previous = acc.current;
|
|
1489
|
+
const current = res;
|
|
1490
|
+
return { previous, current };
|
|
1491
|
+
}, { previous: null, current: null }), tap(({ previous, current }) => {
|
|
1492
|
+
if (previous && JSON.stringify(previous) === JSON.stringify(current)) {
|
|
1493
|
+
this.httpManagerService.isPending.next(false);
|
|
1494
|
+
this.setData$([]);
|
|
1495
|
+
}
|
|
1496
|
+
else {
|
|
1497
|
+
this.httpManagerService.isPending.next(true);
|
|
1498
|
+
}
|
|
1499
|
+
}));
|
|
1500
|
+
})));
|
|
1501
|
+
// FETCH PAGINATION
|
|
1502
|
+
this.fetchPaginationCancel$ = new Subject();
|
|
1503
|
+
this.fetchPaginationRecords = (size = 10, options) => this.effect(() => {
|
|
1504
|
+
this.fetchPaginationCancel$.next();
|
|
1505
|
+
return of(RequestOptions.adapt(options)).pipe(switchMap(() => {
|
|
1506
|
+
this.resetData$();
|
|
1507
|
+
const requestOptions = this.updateRequestOptions(options?.headers);
|
|
1508
|
+
const updatedPath = [...(options?.path ?? []), { size, page: 1 }];
|
|
1509
|
+
const firstRequestObservable = this.httpManagerService.getRequest(requestOptions, updatedPath).pipe(takeUntil(this.fetchPaginationCancel$), tap(() => this.httpManagerService.isPending.next(true)), tap((data) => {
|
|
1510
|
+
data = !data ? (this.dataType === DataType.ARRAY ? [] : {}) : data;
|
|
1511
|
+
this.setData$(data.results);
|
|
1512
|
+
}));
|
|
1513
|
+
return firstRequestObservable.pipe(takeUntil(this.fetchPaginationCancel$), expand((data) => {
|
|
1514
|
+
this.httpManagerService.isPending.next(true);
|
|
1515
|
+
const totalPages = data?.totalPages || 1;
|
|
1516
|
+
const currentPage = data?.page || 1;
|
|
1517
|
+
if (currentPage < totalPages) {
|
|
1518
|
+
const nextPage = currentPage + 1;
|
|
1519
|
+
const updatedPathWithNextPage = [...(options?.path ?? []), { size, page: nextPage }];
|
|
1520
|
+
const nextRequestObservable = this.httpManagerService.getRequest(requestOptions, updatedPathWithNextPage).pipe(takeUntil(this.fetchPaginationCancel$), tap(() => this.httpManagerService.isPending.next(true)), tap((nextData) => {
|
|
1521
|
+
nextData = !nextData ? (this.dataType === DataType.ARRAY ? [] : {}) : nextData;
|
|
1522
|
+
this.setData$(nextData.results);
|
|
1523
|
+
}));
|
|
1524
|
+
return nextRequestObservable;
|
|
1525
|
+
}
|
|
1526
|
+
return EMPTY;
|
|
1527
|
+
}), takeUntil(this.fetchPaginationCancel$), tap(() => this.httpManagerService.isPending.next(false)));
|
|
1528
|
+
}));
|
|
1529
|
+
});
|
|
1530
|
+
this.setApiRequestOptions(apiOptions, dataType, database);
|
|
1531
|
+
}
|
|
1532
|
+
setApiRequestOptions(apiOptions, dataType, database) {
|
|
1533
|
+
this.apiOptions = ApiRequest.adapt(apiOptions);
|
|
1534
|
+
this.dataType = (dataType) ? dataType : DataType.ARRAY;
|
|
1535
|
+
this.hasDatabase = this.database?.table === "" ? false : true;
|
|
1536
|
+
this.database = (this.hasDatabase) ? DatabaseStorage.adapt(database) : undefined;
|
|
1537
|
+
}
|
|
1538
|
+
get apiRequestOptions() {
|
|
1539
|
+
return this.apiOptions;
|
|
1540
|
+
}
|
|
1541
|
+
initStorage() {
|
|
1542
|
+
// if(this.otherOptions.database) {
|
|
1543
|
+
// console.log('Has Database Option:', this.otherOptions.database.table)
|
|
1544
|
+
// const schema = (this.otherOptions.adapters?.incoming) ? Object.keys(this.otherOptions.adapters.incoming({})).join() : '++id'
|
|
1545
|
+
// this.apiService.initDB(this.otherOptions.database.expiresIn, this.otherOptions.database.table, schema)
|
|
1546
|
+
// }
|
|
1547
|
+
}
|
|
1548
|
+
initializeState(data) {
|
|
1549
|
+
this.setData$(data);
|
|
1550
|
+
}
|
|
1551
|
+
updateArrayState(currentData, newData) {
|
|
1552
|
+
const filterCurrentData = () => {
|
|
1553
|
+
const ids = this.streamedResponse.map((obj) => obj.id);
|
|
1554
|
+
return currentData.filter(obj => (obj.id) ? ids.includes(obj.id) : obj);
|
|
1555
|
+
};
|
|
1556
|
+
const filteredCurrentData = (this.httpManagerService.isPending.value) ? currentData : filterCurrentData();
|
|
1557
|
+
const updatedData = filteredCurrentData.map(item => {
|
|
1558
|
+
const newItem = newData.find(newItem => {
|
|
1559
|
+
const hasId = (newItem?.id && item?.id) ? true : false;
|
|
1560
|
+
return (hasId) ? newItem.id === item.id : JSON.stringify(newItem) === JSON.stringify(item);
|
|
1561
|
+
});
|
|
1562
|
+
return (newItem) ? { ...item, ...newItem } : item;
|
|
1563
|
+
});
|
|
1564
|
+
const addedData = newData.filter(newItem => {
|
|
1565
|
+
return !filteredCurrentData.some(item => {
|
|
1566
|
+
const hasId = (newItem?.id && item?.id) ? true : false;
|
|
1567
|
+
return (hasId) ? item.id === newItem.id : JSON.stringify(newItem) === JSON.stringify(item);
|
|
1568
|
+
});
|
|
1569
|
+
});
|
|
1570
|
+
return [...updatedData, ...addedData];
|
|
1571
|
+
}
|
|
1572
|
+
// --------------------------------------------------------------------------------------------------
|
|
1573
|
+
// MISC
|
|
1574
|
+
isEmpty(obj) {
|
|
1575
|
+
return Object.keys(obj).length === 0;
|
|
1576
|
+
}
|
|
1577
|
+
updateRequestOptions(headers) {
|
|
1578
|
+
const options = ApiRequest.adapt({ ...this.apiOptions });
|
|
1579
|
+
options.headers = (headers)
|
|
1580
|
+
? { ...options.headers, ...headers }
|
|
1581
|
+
: { ...options.headers };
|
|
1582
|
+
return options;
|
|
1583
|
+
}
|
|
1584
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: HTTPManagerStateService, deps: [{ token: API_OPTS }, { token: "dataType" }, { token: "database" }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1585
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: HTTPManagerStateService }); }
|
|
1586
|
+
}
|
|
1587
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: HTTPManagerStateService, decorators: [{
|
|
1588
|
+
type: Injectable
|
|
1589
|
+
}], ctorParameters: function () { return [{ type: ApiRequest, decorators: [{
|
|
1590
|
+
type: Inject,
|
|
1591
|
+
args: [API_OPTS]
|
|
1592
|
+
}] }, { type: undefined, decorators: [{
|
|
1593
|
+
type: Inject,
|
|
1594
|
+
args: ["dataType"]
|
|
1595
|
+
}] }, { type: undefined, decorators: [{
|
|
1596
|
+
type: Inject,
|
|
1597
|
+
args: ["database"]
|
|
1598
|
+
}] }]; } });
|
|
1599
|
+
|
|
1600
|
+
// export * from "./database-manager-services/index";
|
|
1601
|
+
|
|
1602
|
+
class ErrorDisplaySettings {
|
|
1603
|
+
constructor(displayTime = 3 * 1000, position = 'top') {
|
|
1604
|
+
this.displayTime = displayTime;
|
|
1605
|
+
this.position = position;
|
|
1606
|
+
}
|
|
1607
|
+
static adapt(item) {
|
|
1608
|
+
return new ErrorDisplaySettings((item?.displayTime) ? item.displayTime * 1000 : 3 * 1000, item?.position);
|
|
1609
|
+
}
|
|
1610
|
+
}
|
|
1611
|
+
|
|
1612
|
+
class WithCredentialsInterceptor {
|
|
1613
|
+
intercept(req, next) {
|
|
1614
|
+
req = req.clone({ withCredentials: true });
|
|
1615
|
+
return next.handle(req);
|
|
1616
|
+
}
|
|
1617
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: WithCredentialsInterceptor, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1618
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: WithCredentialsInterceptor }); }
|
|
1619
|
+
}
|
|
1620
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: WithCredentialsInterceptor, decorators: [{
|
|
1621
|
+
type: Injectable
|
|
1622
|
+
}] });
|
|
1623
|
+
|
|
1624
|
+
class RequestHeadersInterceptor {
|
|
1625
|
+
get currentDate() {
|
|
1626
|
+
const date = new Date();
|
|
1627
|
+
const year = date.getFullYear();
|
|
1628
|
+
const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
1629
|
+
const day = String(date.getDate()).padStart(2, '0');
|
|
1630
|
+
return `${year}-${month}-${day}`;
|
|
1631
|
+
}
|
|
1632
|
+
constructor(translate) {
|
|
1633
|
+
this.translate = translate;
|
|
1634
|
+
this.subscriptions = new Subscription();
|
|
1635
|
+
this.language = 'en-CA';
|
|
1636
|
+
this.subscriptions.add(this.translate.onLangChange
|
|
1637
|
+
.subscribe((params) => {
|
|
1638
|
+
this.language = `${params.lang}-CA`;
|
|
1639
|
+
}));
|
|
1640
|
+
}
|
|
1641
|
+
intercept(request, next) {
|
|
1642
|
+
request = request.clone({
|
|
1643
|
+
setHeaders: {
|
|
1644
|
+
'Content-Type': 'application/json',
|
|
1645
|
+
'Accept-Language': this.language || 'en-CA',
|
|
1646
|
+
'Current-Date': this.currentDate
|
|
1647
|
+
}
|
|
1648
|
+
});
|
|
1649
|
+
return next.handle(request);
|
|
1650
|
+
}
|
|
1651
|
+
ngOnDestroy() {
|
|
1652
|
+
this.subscriptions.unsubscribe();
|
|
1653
|
+
}
|
|
1654
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: RequestHeadersInterceptor, deps: [{ token: i1.TranslateService }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1655
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: RequestHeadersInterceptor }); }
|
|
1656
|
+
}
|
|
1657
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: RequestHeadersInterceptor, decorators: [{
|
|
1658
|
+
type: Injectable
|
|
1659
|
+
}], ctorParameters: function () { return [{ type: i1.TranslateService }]; } });
|
|
1660
|
+
|
|
1661
|
+
class RequestErrorInterceptor {
|
|
1662
|
+
constructor() {
|
|
1663
|
+
this.toastMessage = inject(ToastMessageService$1);
|
|
1664
|
+
}
|
|
1665
|
+
intercept(req, next) {
|
|
1666
|
+
return next.handle(req).pipe(catchError$1((error) => {
|
|
1667
|
+
const displayError = ToastDisplay.adapt({
|
|
1668
|
+
message: 'This is a toast message. This is an Error!!',
|
|
1669
|
+
action: 'OK',
|
|
1670
|
+
color: ToastColors.SUCCESS,
|
|
1671
|
+
icon: 'info',
|
|
1672
|
+
duration: 5 * 1000, //5 seconds
|
|
1673
|
+
});
|
|
1674
|
+
if (error.status >= 400 && error.status < 500) {
|
|
1675
|
+
displayError.color = ToastColors.ERROR;
|
|
1676
|
+
displayError.message = `${error.status}: ${error.statusText}`;
|
|
1677
|
+
console.error('Client Error:', {
|
|
1678
|
+
status: error.status,
|
|
1679
|
+
message: error.message,
|
|
1680
|
+
error: error.error,
|
|
1681
|
+
text: error.statusText,
|
|
1682
|
+
});
|
|
1683
|
+
this.toastMessage.toastMessage(displayError);
|
|
1684
|
+
}
|
|
1685
|
+
else if (error.status >= 500) {
|
|
1686
|
+
displayError.color = ToastColors.ERROR;
|
|
1687
|
+
displayError.message = `${error.status}: ${error.statusText}`;
|
|
1688
|
+
console.error('Server Error:', {
|
|
1689
|
+
status: error.status,
|
|
1690
|
+
message: error.message,
|
|
1691
|
+
error: error.error,
|
|
1692
|
+
text: error.statusText,
|
|
1693
|
+
});
|
|
1694
|
+
this.toastMessage.toastMessage(displayError);
|
|
1695
|
+
}
|
|
1696
|
+
return throwError(() => error);
|
|
1697
|
+
}));
|
|
1698
|
+
}
|
|
1699
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: RequestErrorInterceptor, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1700
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: RequestErrorInterceptor }); }
|
|
1701
|
+
}
|
|
1702
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: RequestErrorInterceptor, decorators: [{
|
|
1703
|
+
type: Injectable
|
|
1704
|
+
}] });
|
|
1705
|
+
|
|
1706
|
+
/*
|
|
1707
|
+
* Public API Surface of http-request-manager
|
|
1708
|
+
*/
|
|
1709
|
+
|
|
1710
|
+
/**
|
|
1711
|
+
* Generated bundle index. Do not edit.
|
|
1712
|
+
*/
|
|
1713
|
+
|
|
1714
|
+
export { ApiRequest, AppService, AsymmetricalEncryptionService, DataType, DatabaseStorage, ErrorDisplaySettings, GlobalStoreOptions, HTTPManagerService, HTTPManagerStateService, HeadersService, LocalStorageManagerService, PathQueryService, Random, RandomNumber, RandomNumbers, RandomNumbersUnique, RandomSignature, RandomStr, RequestErrorInterceptor, RequestHeadersInterceptor, RequestOptions, RetryOptions, SettingOptions, StorageData, StorageOption, StorageOptionSettings, StorageType, SymmetricalEncryptionService, UUID, UtilsService, WithCredentialsInterceptor, countdown, delayedRetry, requestPolling, requestStreaming };
|
|
1715
|
+
//# sourceMappingURL=http-request-manager.mjs.map
|