http-request-manager 18.12.3 → 18.12.5
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.
|
@@ -883,28 +883,66 @@ class StreamingProcessor {
|
|
|
883
883
|
this.buffer = '';
|
|
884
884
|
this.contentType = '';
|
|
885
885
|
this.maxBufferSize = 10 * 1024 * 1024; // 10MB limit
|
|
886
|
+
this.lastParsedLength = 0; // Track parsed position to avoid duplicates
|
|
886
887
|
this.streamConfig = config || { streamType: StreamType.AI_STREAMING };
|
|
887
888
|
}
|
|
888
889
|
/**
|
|
889
|
-
* Process HTTP events and extract streaming data
|
|
890
|
+
* Process HTTP events and extract streaming data with progress
|
|
890
891
|
*/
|
|
891
892
|
process(event) {
|
|
892
893
|
switch (event.type) {
|
|
893
894
|
case HttpEventType.ResponseHeader:
|
|
894
895
|
this.contentType = event.headers?.get('content-type') || '';
|
|
896
|
+
// Read total from response header if configured
|
|
897
|
+
if (this.streamConfig.totalHeader && event.headers) {
|
|
898
|
+
const totalVal = event.headers.get(this.streamConfig.totalHeader);
|
|
899
|
+
if (totalVal !== undefined && totalVal !== null) {
|
|
900
|
+
const parsed = parseInt(totalVal, 10);
|
|
901
|
+
if (!isNaN(parsed)) {
|
|
902
|
+
this.totalFromHeader = parsed;
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
}
|
|
895
906
|
return null;
|
|
896
907
|
case HttpEventType.DownloadProgress:
|
|
897
908
|
if (event.partialText) {
|
|
898
909
|
this.appendToBuffer(event.partialText);
|
|
899
910
|
const parsedData = this.parseBuffer();
|
|
900
|
-
return
|
|
911
|
+
// Only return NEW items since last parse (progressive updates)
|
|
912
|
+
const newItems = parsedData.slice(this.lastParsedLength);
|
|
913
|
+
this.lastParsedLength = parsedData.length;
|
|
914
|
+
// Calculate progress
|
|
915
|
+
const progress = {
|
|
916
|
+
received: this.lastParsedLength,
|
|
917
|
+
total: this.totalFromHeader,
|
|
918
|
+
percent: this.totalFromHeader
|
|
919
|
+
? Math.round((this.lastParsedLength / this.totalFromHeader) * 100)
|
|
920
|
+
: 0,
|
|
921
|
+
stage: 'streaming'
|
|
922
|
+
};
|
|
923
|
+
return {
|
|
924
|
+
data: newItems.length > 0 ? newItems : [],
|
|
925
|
+
progress
|
|
926
|
+
};
|
|
901
927
|
}
|
|
902
928
|
return null;
|
|
903
929
|
case HttpEventType.Response:
|
|
904
930
|
if (event.body) {
|
|
905
931
|
this.appendToBuffer(event.body);
|
|
906
932
|
const parsedData = this.parseBuffer();
|
|
907
|
-
|
|
933
|
+
// Calculate final progress
|
|
934
|
+
const progress = {
|
|
935
|
+
received: parsedData.length,
|
|
936
|
+
total: this.totalFromHeader,
|
|
937
|
+
percent: this.totalFromHeader
|
|
938
|
+
? Math.round((parsedData.length / this.totalFromHeader) * 100)
|
|
939
|
+
: 100,
|
|
940
|
+
stage: 'complete'
|
|
941
|
+
};
|
|
942
|
+
return {
|
|
943
|
+
data: parsedData.length > 0 ? parsedData : [],
|
|
944
|
+
progress
|
|
945
|
+
};
|
|
908
946
|
}
|
|
909
947
|
return null;
|
|
910
948
|
default:
|
|
@@ -943,6 +981,8 @@ class StreamingProcessor {
|
|
|
943
981
|
reset() {
|
|
944
982
|
this.buffer = '';
|
|
945
983
|
this.contentType = '';
|
|
984
|
+
this.lastParsedLength = 0;
|
|
985
|
+
this.totalFromHeader = undefined;
|
|
946
986
|
}
|
|
947
987
|
/**
|
|
948
988
|
* Update configuration
|
|
@@ -2793,19 +2833,6 @@ class UploadValidationErrorModel {
|
|
|
2793
2833
|
}
|
|
2794
2834
|
}
|
|
2795
2835
|
|
|
2796
|
-
class UserData {
|
|
2797
|
-
constructor(ldap = '', name = '', email = '', color = RandomPaletteColor()) {
|
|
2798
|
-
this.ldap = ldap;
|
|
2799
|
-
this.name = name;
|
|
2800
|
-
this.email = email;
|
|
2801
|
-
this.color = color;
|
|
2802
|
-
}
|
|
2803
|
-
static adapt(item) {
|
|
2804
|
-
const userName = `${item?.first_name} ${item?.last_name}`;
|
|
2805
|
-
return new UserData(item?.ldap, userName, item?.email, item?.color);
|
|
2806
|
-
}
|
|
2807
|
-
}
|
|
2808
|
-
|
|
2809
2836
|
class RequestService extends WebsocketService {
|
|
2810
2837
|
constructor() {
|
|
2811
2838
|
super(...arguments);
|
|
@@ -2816,6 +2843,13 @@ class RequestService extends WebsocketService {
|
|
|
2816
2843
|
this.isPending$ = this.isPending.asObservable();
|
|
2817
2844
|
this.progress = new BehaviorSubject(0);
|
|
2818
2845
|
this.progress$ = this.progress.asObservable();
|
|
2846
|
+
this.streamProgress = new BehaviorSubject({
|
|
2847
|
+
received: 0,
|
|
2848
|
+
total: undefined,
|
|
2849
|
+
percent: 0,
|
|
2850
|
+
stage: 'connecting'
|
|
2851
|
+
});
|
|
2852
|
+
this.streamProgress$ = this.streamProgress.asObservable();
|
|
2819
2853
|
}
|
|
2820
2854
|
// Implementation
|
|
2821
2855
|
getRecordRequest(options) {
|
|
@@ -2884,7 +2918,12 @@ class RequestService extends WebsocketService {
|
|
|
2884
2918
|
}
|
|
2885
2919
|
requestStreaming(options) {
|
|
2886
2920
|
return (source$) => {
|
|
2887
|
-
return source$.pipe(
|
|
2921
|
+
return source$.pipe(tap(output => {
|
|
2922
|
+
// Update progress from stream output
|
|
2923
|
+
this.progress.next(output.progress.received);
|
|
2924
|
+
this.streamProgress.next(output.progress);
|
|
2925
|
+
}), map(output => {
|
|
2926
|
+
const data = output.data;
|
|
2888
2927
|
if (!data || (Array.isArray(data) && data.length === 0)) {
|
|
2889
2928
|
return data;
|
|
2890
2929
|
}
|
|
@@ -3586,6 +3625,7 @@ class HTTPManagerService extends RequestService {
|
|
|
3586
3625
|
this.data$ = this.data.asObservable();
|
|
3587
3626
|
this.polling$ = new Subject();
|
|
3588
3627
|
this.config = ApiRequest.adapt();
|
|
3628
|
+
this.streamProgress$ = this.streamProgress.asObservable();
|
|
3589
3629
|
this.config = (configOptions) ? ApiRequest.adapt(configOptions.httpRequestOptions) : this.config;
|
|
3590
3630
|
}
|
|
3591
3631
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -3722,6 +3762,12 @@ class HTTPManagerService extends RequestService {
|
|
|
3722
3762
|
getRequest(options, params) {
|
|
3723
3763
|
this.isPending.next(true);
|
|
3724
3764
|
this.data.next(null);
|
|
3765
|
+
this.streamProgress.next({
|
|
3766
|
+
received: 0,
|
|
3767
|
+
total: undefined,
|
|
3768
|
+
percent: 0,
|
|
3769
|
+
stage: 'connecting'
|
|
3770
|
+
});
|
|
3725
3771
|
const updatedOptions = this.defineReqOptions(options, params);
|
|
3726
3772
|
const func = this.getRecordRequest;
|
|
3727
3773
|
const requests = this.createRequest(func, updatedOptions);
|
|
@@ -3730,9 +3776,19 @@ class HTTPManagerService extends RequestService {
|
|
|
3730
3776
|
this.data.next(data);
|
|
3731
3777
|
if (updatedOptions.displaySuccess)
|
|
3732
3778
|
this.handleSuccessWithSnackBar(updatedOptions.successMessage);
|
|
3733
|
-
})
|
|
3779
|
+
}), finalize(() => {
|
|
3780
|
+
this.streamProgress.next({
|
|
3781
|
+
...this.streamProgress.value,
|
|
3782
|
+
stage: 'complete'
|
|
3783
|
+
});
|
|
3784
|
+
this.isPending.next(false);
|
|
3785
|
+
}), catchError((err) => {
|
|
3734
3786
|
if (updatedOptions.displayError)
|
|
3735
3787
|
this.handleErrorWithSnackBar(err, updatedOptions.errorMessage || err?.message);
|
|
3788
|
+
this.streamProgress.next({
|
|
3789
|
+
...this.streamProgress.value,
|
|
3790
|
+
stage: 'error'
|
|
3791
|
+
});
|
|
3736
3792
|
this.isPending.next(false);
|
|
3737
3793
|
return this.handleError(err);
|
|
3738
3794
|
}));
|
|
@@ -4154,7 +4210,8 @@ class RequestSignalsService extends WebsocketService {
|
|
|
4154
4210
|
}
|
|
4155
4211
|
requestStreamingOperator(options) {
|
|
4156
4212
|
return (source$) => {
|
|
4157
|
-
return source$.pipe(map(
|
|
4213
|
+
return source$.pipe(map(output => {
|
|
4214
|
+
const data = output.data;
|
|
4158
4215
|
if (!data || (Array.isArray(data) && data.length === 0)) {
|
|
4159
4216
|
return data;
|
|
4160
4217
|
}
|
|
@@ -4891,12 +4948,13 @@ class ApiRequest {
|
|
|
4891
4948
|
}
|
|
4892
4949
|
|
|
4893
4950
|
class RequestOptions {
|
|
4894
|
-
constructor(path = [], headers = {}) {
|
|
4951
|
+
constructor(path = [], headers = {}, forceRefresh) {
|
|
4895
4952
|
this.path = path;
|
|
4896
4953
|
this.headers = headers;
|
|
4954
|
+
this.forceRefresh = forceRefresh;
|
|
4897
4955
|
}
|
|
4898
4956
|
static adapt(item) {
|
|
4899
|
-
return new RequestOptions(item?.path, item?.headers);
|
|
4957
|
+
return new RequestOptions(item?.path, item?.headers, item?.forceRefresh);
|
|
4900
4958
|
}
|
|
4901
4959
|
}
|
|
4902
4960
|
|
|
@@ -5772,9 +5830,12 @@ class DbService extends Dexie {
|
|
|
5772
5830
|
await this.dbReady;
|
|
5773
5831
|
const safeTableName = this.cleanTableName(tableName);
|
|
5774
5832
|
const safeSchema = schema.trim();
|
|
5775
|
-
if (this.tableExists(safeTableName))
|
|
5776
|
-
return;
|
|
5777
5833
|
const currentSchema = this.getCurrentSchema();
|
|
5834
|
+
const existingSchema = currentSchema[safeTableName]?.trim();
|
|
5835
|
+
// No-op only when table already exists and schema is identical.
|
|
5836
|
+
if (this.tableExists(safeTableName) && existingSchema === safeSchema) {
|
|
5837
|
+
return;
|
|
5838
|
+
}
|
|
5778
5839
|
console.log('Current Schema before update:', currentSchema);
|
|
5779
5840
|
currentSchema[safeTableName] = safeSchema;
|
|
5780
5841
|
const nextVersion = this.verno + 1;
|
|
@@ -5788,6 +5849,7 @@ class DbService extends Dexie {
|
|
|
5788
5849
|
const created = this.tables.some(t => t.name === safeTableName);
|
|
5789
5850
|
if (!created) {
|
|
5790
5851
|
console.error(`CRITICAL: Table ${safeTableName} was NOT created after upgrade! Tables found:`, this.tables.map(t => t.name));
|
|
5852
|
+
throw new Error(`Table '${safeTableName}' was not created after schema upgrade`);
|
|
5791
5853
|
}
|
|
5792
5854
|
else {
|
|
5793
5855
|
console.log(`Database opened successfully version ${this.verno}. Table ${safeTableName} verified.`);
|
|
@@ -5795,15 +5857,24 @@ class DbService extends Dexie {
|
|
|
5795
5857
|
}
|
|
5796
5858
|
catch (err) {
|
|
5797
5859
|
console.error('Error opening database after schema update:', err);
|
|
5860
|
+
throw err;
|
|
5798
5861
|
}
|
|
5799
5862
|
}
|
|
5800
5863
|
async DBOpened() {
|
|
5864
|
+
try {
|
|
5865
|
+
await this.dbReady;
|
|
5866
|
+
}
|
|
5867
|
+
catch (err) {
|
|
5868
|
+
console.error('DBOpened: init failed', err);
|
|
5869
|
+
return false;
|
|
5870
|
+
}
|
|
5801
5871
|
if (!this.isOpen()) {
|
|
5802
5872
|
try {
|
|
5803
5873
|
await this.open();
|
|
5804
5874
|
return true;
|
|
5805
5875
|
}
|
|
5806
5876
|
catch (err) {
|
|
5877
|
+
console.error('DBOpened: open failed', err);
|
|
5807
5878
|
return false;
|
|
5808
5879
|
}
|
|
5809
5880
|
}
|
|
@@ -5898,7 +5969,11 @@ class DatabaseManagerService extends DbService {
|
|
|
5898
5969
|
}));
|
|
5899
5970
|
}
|
|
5900
5971
|
createDatabaseTable(tableDef) {
|
|
5901
|
-
|
|
5972
|
+
const tableName = this.cleanTableName(tableDef.table);
|
|
5973
|
+
return from(this.createTable(tableDef.table, tableDef.schema)).pipe(switchMap(() => from(this.DBOpened())), map((opened) => opened && this.tableExists(tableName)), catchError((error) => {
|
|
5974
|
+
console.error(`createDatabaseTable: failed for table '${tableName}'`, error);
|
|
5975
|
+
return of(false);
|
|
5976
|
+
}));
|
|
5902
5977
|
}
|
|
5903
5978
|
updateDatabaseTableSchema(tableDef) {
|
|
5904
5979
|
return from(this.createTable(tableDef.table, tableDef.schema)).pipe(switchMap(() => this.getDatabaseTable(tableDef.table)));
|
|
@@ -5945,26 +6020,23 @@ class DatabaseManagerService extends DbService {
|
|
|
5945
6020
|
}
|
|
5946
6021
|
createTableRecords(table, records) {
|
|
5947
6022
|
const tableName = this.cleanTableName(table);
|
|
5948
|
-
return from(this.DBOpened()).pipe(switchMap(() => {
|
|
6023
|
+
return from(this.DBOpened()).pipe(switchMap((opened) => {
|
|
6024
|
+
if (!opened) {
|
|
6025
|
+
console.error(`createTableRecords: DB not open. Cannot write to '${tableName}'.`);
|
|
6026
|
+
return EMPTY;
|
|
6027
|
+
}
|
|
5949
6028
|
if (!this.tables.some(t => t.name === tableName)) {
|
|
5950
6029
|
console.error(`createTableRecords: Table '${tableName}' does not exist in DB version ${this.verno}. Available:`, this.tables.map(t => t.name));
|
|
5951
6030
|
return EMPTY;
|
|
5952
6031
|
}
|
|
5953
6032
|
const tableInstance = this.table(tableName);
|
|
5954
|
-
//
|
|
5955
|
-
const schema = tableInstance.schema;
|
|
5956
|
-
const validFields = [
|
|
5957
|
-
schema.primKey.name,
|
|
5958
|
-
...schema.indexes.map(idx => idx.name)
|
|
5959
|
-
];
|
|
6033
|
+
// Keep full object payload; Dexie stores non-indexed properties as well.
|
|
5960
6034
|
const insertRecords = records.map((record) => {
|
|
5961
|
-
const
|
|
5962
|
-
|
|
5963
|
-
|
|
5964
|
-
|
|
5965
|
-
|
|
5966
|
-
});
|
|
5967
|
-
return objectFromSchema;
|
|
6035
|
+
const payload = { ...(record || {}) };
|
|
6036
|
+
if (payload.id === undefined || payload.id === null || payload.id === '') {
|
|
6037
|
+
delete payload.id;
|
|
6038
|
+
}
|
|
6039
|
+
return payload;
|
|
5968
6040
|
});
|
|
5969
6041
|
console.log(`createTableRecords: Bulk putting ${insertRecords.length} records into ${tableName}`);
|
|
5970
6042
|
return from(tableInstance.bulkPut(insertRecords)).pipe(map(() => insertRecords));
|
|
@@ -5978,17 +6050,14 @@ class DatabaseManagerService extends DbService {
|
|
|
5978
6050
|
updateTableRecords(table, records) {
|
|
5979
6051
|
const tableName = this.cleanTableName(table);
|
|
5980
6052
|
return this.getDatabaseTable(tableName).pipe(switchMap((tableData) => {
|
|
5981
|
-
|
|
5982
|
-
const
|
|
5983
|
-
|
|
5984
|
-
|
|
5985
|
-
|
|
5986
|
-
|
|
5987
|
-
|
|
5988
|
-
|
|
5989
|
-
});
|
|
5990
|
-
return from(tableData.bulkPut(insertRecords)).pipe(map(() => insertRecords));
|
|
5991
|
-
}));
|
|
6053
|
+
const insertRecords = records.map((record) => {
|
|
6054
|
+
const payload = { ...(record || {}) };
|
|
6055
|
+
if (payload.id === undefined || payload.id === null || payload.id === '') {
|
|
6056
|
+
delete payload.id;
|
|
6057
|
+
}
|
|
6058
|
+
return payload;
|
|
6059
|
+
});
|
|
6060
|
+
return from(tableData.bulkPut(insertRecords)).pipe(map(() => insertRecords));
|
|
5992
6061
|
}));
|
|
5993
6062
|
}
|
|
5994
6063
|
deleteTableRecord(table, id) {
|
|
@@ -6106,6 +6175,15 @@ class HTTPManagerStateService extends ComponentStore {
|
|
|
6106
6175
|
this.hasDatabase = false;
|
|
6107
6176
|
this.streamedResponse = [];
|
|
6108
6177
|
this.shouldRetry = true;
|
|
6178
|
+
this.volatileHeaders = new Set([
|
|
6179
|
+
'authorization',
|
|
6180
|
+
'x-request-id',
|
|
6181
|
+
'x-correlation-id',
|
|
6182
|
+
'x-trace-id',
|
|
6183
|
+
'x-amzn-trace-id',
|
|
6184
|
+
'date',
|
|
6185
|
+
'if-none-match'
|
|
6186
|
+
]);
|
|
6109
6187
|
this.wsRetryAttempts = new BehaviorSubject(0);
|
|
6110
6188
|
this.wsRetryAttempts$ = this.wsRetryAttempts.asObservable();
|
|
6111
6189
|
this.messages = new BehaviorSubject([]);
|
|
@@ -6375,22 +6453,25 @@ class HTTPManagerStateService extends ComponentStore {
|
|
|
6375
6453
|
}
|
|
6376
6454
|
}))))));
|
|
6377
6455
|
this.initDBStorage = this.effect((trigger$) => trigger$.pipe(tap(() => {
|
|
6456
|
+
console.log('[initDBStorage effect] Triggered, checking conditions:', {
|
|
6457
|
+
dataType: this.dataType,
|
|
6458
|
+
isARRAY: this.dataType === DataType.ARRAY,
|
|
6459
|
+
hasAdapter: !!this.apiOptions?.adapter,
|
|
6460
|
+
hasTable: !!this.databaseOptions?.table,
|
|
6461
|
+
tableValue: this.databaseOptions?.table
|
|
6462
|
+
});
|
|
6378
6463
|
if (this.dataType !== DataType.ARRAY)
|
|
6379
6464
|
console.warn('Database storage requires dataType to be ARRAY');
|
|
6380
6465
|
if (!this.apiOptions.adapter)
|
|
6381
|
-
console.warn('Database storage
|
|
6466
|
+
console.warn('Database storage adapter missing, using minimal or inferred schema');
|
|
6382
6467
|
if (this.databaseOptions && this.databaseOptions?.table === '')
|
|
6383
6468
|
console.warn('Database storage requires a table name');
|
|
6384
|
-
}), filter(() =>
|
|
6385
|
-
const
|
|
6386
|
-
|
|
6387
|
-
|
|
6388
|
-
|
|
6389
|
-
|
|
6390
|
-
if (otherKeys.length > 0) {
|
|
6391
|
-
schema += ', ' + otherKeys.join(', ');
|
|
6392
|
-
}
|
|
6393
|
-
}
|
|
6469
|
+
}), filter(() => {
|
|
6470
|
+
const shouldProceed = this.dataType === DataType.ARRAY && !!this.databaseOptions?.table;
|
|
6471
|
+
console.log('[initDBStorage effect] Filter result:', shouldProceed);
|
|
6472
|
+
return shouldProceed;
|
|
6473
|
+
}), switchMap(() => {
|
|
6474
|
+
const schema = this.buildSchemaFromAdapter();
|
|
6394
6475
|
const tableDef = TableSchemaDef.adapt({
|
|
6395
6476
|
table: this.databaseOptions?.table,
|
|
6396
6477
|
schema: schema
|
|
@@ -6479,6 +6560,7 @@ class HTTPManagerStateService extends ComponentStore {
|
|
|
6479
6560
|
}
|
|
6480
6561
|
}), concatMap(() => {
|
|
6481
6562
|
if (this.hasDatabase && this.databaseOptions?.table) {
|
|
6563
|
+
this.clearRequestCacheMetadata(this.databaseOptions.table);
|
|
6482
6564
|
const currentData = this.get()?.data;
|
|
6483
6565
|
const idsToDelete = Array.isArray(currentData) ? currentData.map((r) => r.id) : [];
|
|
6484
6566
|
if (idsToDelete.length > 0) {
|
|
@@ -6493,35 +6575,86 @@ class HTTPManagerStateService extends ComponentStore {
|
|
|
6493
6575
|
this.fetchRecords = (options) => this.effect(() => of(RequestOptions.adapt(options)).pipe(switchMap(() => {
|
|
6494
6576
|
this.streamedResponse = [];
|
|
6495
6577
|
const requestOptions = this.updateRequestOptions(options?.headers);
|
|
6578
|
+
const effectiveParams = this.getEffectiveParams(options?.path);
|
|
6579
|
+
const requestSignature = this.buildRequestSignature('GET', requestOptions, effectiveParams);
|
|
6496
6580
|
const fetchFromAPI = () => {
|
|
6497
|
-
return this.httpManagerService.getRequest(requestOptions,
|
|
6498
|
-
|
|
6581
|
+
return this.httpManagerService.getRequest(requestOptions, effectiveParams).pipe(tap((data) => {
|
|
6582
|
+
// Extract array from paginated response if needed
|
|
6583
|
+
const arrayData = (data?.results && Array.isArray(data.results)) ? data.results : data;
|
|
6584
|
+
data = (!arrayData) ? (this.dataType === DataType.ARRAY) ? [] : {} : arrayData;
|
|
6499
6585
|
this.setData$(data);
|
|
6500
6586
|
}), concatMap((data) => {
|
|
6501
|
-
|
|
6587
|
+
// Extract array from paginated response for database storage
|
|
6588
|
+
const dbData = (data?.results && Array.isArray(data.results)) ? data.results : data;
|
|
6589
|
+
if (this.hasDatabase && this.databaseOptions?.table && Array.isArray(dbData) && dbData.length > 0) {
|
|
6590
|
+
const tableName = this.databaseOptions.table;
|
|
6502
6591
|
this.localStorageManagerService.updateStore({
|
|
6503
|
-
name:
|
|
6592
|
+
name: tableName,
|
|
6504
6593
|
data: { ...this.databaseOptions, ...{ expires: this.utils.expires(this.databaseOptions.expiresIn) } }
|
|
6505
6594
|
});
|
|
6506
|
-
|
|
6595
|
+
const schema = this.buildSchemaFromSample(dbData[0]);
|
|
6596
|
+
const tableDef = TableSchemaDef.adapt({ table: tableName, schema });
|
|
6597
|
+
const schemaSignature = this.buildSchemaSignature(schema);
|
|
6598
|
+
// Always ensure table exists immediately before writing to avoid stale schema/store races.
|
|
6599
|
+
return this.localStorageManagerService.store$(tableName).pipe(take(1), switchMap((storeData) => {
|
|
6600
|
+
const storedSchemaSignature = this.getStoredSchemaSignature(storeData);
|
|
6601
|
+
const schemaChanged = !!storedSchemaSignature && storedSchemaSignature !== schemaSignature;
|
|
6602
|
+
const ensureTable$ = schemaChanged
|
|
6603
|
+
? this.dbManagerService.clearTable(tableName).pipe(switchMap(() => this.dbManagerService.createDatabaseTable(tableDef)))
|
|
6604
|
+
: this.dbManagerService.createDatabaseTable(tableDef);
|
|
6605
|
+
return ensureTable$.pipe(switchMap((created) => {
|
|
6606
|
+
if (!created) {
|
|
6607
|
+
console.warn('[DB STORAGE] Table create/open not ready, skipping DB write for this payload:', { table: tableName });
|
|
6608
|
+
return of(data);
|
|
6609
|
+
}
|
|
6610
|
+
return this.dbManagerService.createTableRecords(tableName, dbData).pipe(tap(() => this.saveRequestCacheMetadata(tableName, 'GET', requestSignature, schemaSignature, options)));
|
|
6611
|
+
}));
|
|
6612
|
+
}), catchError((error) => {
|
|
6613
|
+
console.error('[DB STORAGE] Failed to ensure table and write records:', { table: tableName, schema, error });
|
|
6614
|
+
return of(data);
|
|
6615
|
+
}));
|
|
6507
6616
|
}
|
|
6508
6617
|
return of(data);
|
|
6509
6618
|
}));
|
|
6510
6619
|
};
|
|
6620
|
+
console.log('[DB STORAGE] Checking database storage:', {
|
|
6621
|
+
hasDatabase: this.hasDatabase,
|
|
6622
|
+
table: this.databaseOptions?.table,
|
|
6623
|
+
databaseOptions: this.databaseOptions
|
|
6624
|
+
});
|
|
6511
6625
|
if (this.hasDatabase && this.databaseOptions?.table) {
|
|
6512
6626
|
return this.dbManagerService.databaseExists().pipe(switchMap((dbExists) => {
|
|
6513
6627
|
if (!dbExists) {
|
|
6514
6628
|
const initObs = this.initDBStorageAsync();
|
|
6515
|
-
return initObs.pipe(switchMap(() => fetchFromAPI()))
|
|
6629
|
+
return initObs.pipe(switchMap(() => fetchFromAPI()), catchError((error) => {
|
|
6630
|
+
console.warn('[DB STORAGE] initDBStorageAsync failed when DB did not exist, continuing with API:', error);
|
|
6631
|
+
return fetchFromAPI();
|
|
6632
|
+
}));
|
|
6516
6633
|
}
|
|
6517
6634
|
return this.dbManagerService.hasDatabaseTable(this.databaseOptions.table).pipe(switchMap((tableExists) => {
|
|
6518
6635
|
if (!tableExists) {
|
|
6519
6636
|
const initObs = this.initDBStorageAsync();
|
|
6520
|
-
return initObs.pipe(switchMap(() => fetchFromAPI()))
|
|
6637
|
+
return initObs.pipe(switchMap(() => fetchFromAPI()), catchError((error) => {
|
|
6638
|
+
console.warn('[DB STORAGE] initDBStorageAsync failed when table missing, continuing with API:', error);
|
|
6639
|
+
return fetchFromAPI();
|
|
6640
|
+
}));
|
|
6521
6641
|
}
|
|
6522
6642
|
return this.localStorageManagerService.store$(this.databaseOptions.table).pipe(take(1), switchMap((storeData) => {
|
|
6643
|
+
const cached = this.getRequestCacheMetadata(storeData, 'GET');
|
|
6644
|
+
const sameSignature = !!cached?.signature && cached.signature === requestSignature;
|
|
6645
|
+
const forceRefresh = !!options?.forceRefresh;
|
|
6646
|
+
const storedSchemaSignature = this.getStoredSchemaSignature(storeData);
|
|
6523
6647
|
const expires = storeData?.expires || 0;
|
|
6524
6648
|
const hasExpired = expires > 0 && this.utils.hasExpired(expires);
|
|
6649
|
+
if (!forceRefresh && sameSignature && !hasExpired) {
|
|
6650
|
+
return this.dbManagerService.getTableRecords(this.databaseOptions.table).pipe(switchMap((dbData) => {
|
|
6651
|
+
if (Array.isArray(dbData) && dbData.length > 0) {
|
|
6652
|
+
this.setData$(dbData);
|
|
6653
|
+
return of(dbData);
|
|
6654
|
+
}
|
|
6655
|
+
return fetchFromAPI();
|
|
6656
|
+
}));
|
|
6657
|
+
}
|
|
6525
6658
|
if (hasExpired) {
|
|
6526
6659
|
return this.dbManagerService.clearTable(this.databaseOptions.table).pipe(switchMap(() => fetchFromAPI()), tap(() => {
|
|
6527
6660
|
this.localStorageManagerService.updateStore({
|
|
@@ -6530,12 +6663,27 @@ class HTTPManagerStateService extends ComponentStore {
|
|
|
6530
6663
|
});
|
|
6531
6664
|
}));
|
|
6532
6665
|
}
|
|
6666
|
+
const expectedSchema = this.buildSchemaFromAdapter();
|
|
6667
|
+
const expectedSchemaSignature = this.buildSchemaSignature(expectedSchema);
|
|
6668
|
+
if (storedSchemaSignature && storedSchemaSignature !== expectedSchemaSignature) {
|
|
6669
|
+
const tableDef = TableSchemaDef.adapt({ table: this.databaseOptions.table, schema: expectedSchema });
|
|
6670
|
+
return this.dbManagerService.clearTable(this.databaseOptions.table).pipe(switchMap(() => this.dbManagerService.createDatabaseTable(tableDef)), switchMap(() => fetchFromAPI()));
|
|
6671
|
+
}
|
|
6533
6672
|
return this.dbManagerService.getTableRecords(this.databaseOptions.table).pipe(switchMap((dbData) => {
|
|
6534
6673
|
if (Array.isArray(dbData) && dbData.length > 0) {
|
|
6535
6674
|
this.setData$(dbData);
|
|
6536
6675
|
return of(dbData);
|
|
6537
6676
|
}
|
|
6538
6677
|
return fetchFromAPI();
|
|
6678
|
+
}), catchError((error) => {
|
|
6679
|
+
const tableName = this.databaseOptions.table;
|
|
6680
|
+
console.warn('[DB STORAGE] getTableRecords failed, recreating table and falling back to API:', { table: tableName, error });
|
|
6681
|
+
const schema = this.buildSchemaFromAdapter();
|
|
6682
|
+
const tableDef = TableSchemaDef.adapt({ table: tableName, schema });
|
|
6683
|
+
return this.dbManagerService.createDatabaseTable(tableDef).pipe(switchMap(() => fetchFromAPI()), catchError((recreateError) => {
|
|
6684
|
+
console.error('[DB STORAGE] Failed to recreate table after read error, continuing with API only:', recreateError);
|
|
6685
|
+
return fetchFromAPI();
|
|
6686
|
+
}));
|
|
6539
6687
|
}));
|
|
6540
6688
|
}));
|
|
6541
6689
|
}));
|
|
@@ -6659,7 +6807,7 @@ class HTTPManagerStateService extends ComponentStore {
|
|
|
6659
6807
|
if (res.length > 0)
|
|
6660
6808
|
this.setData$(res);
|
|
6661
6809
|
this.streamedResponse = res;
|
|
6662
|
-
}), scan((acc, res) => {
|
|
6810
|
+
}), concatMap((res) => this.persistStreamDataToDb(res, options)), scan((acc, res) => {
|
|
6663
6811
|
const previous = acc.current;
|
|
6664
6812
|
const current = res;
|
|
6665
6813
|
return { previous, current };
|
|
@@ -6679,8 +6827,52 @@ class HTTPManagerStateService extends ComponentStore {
|
|
|
6679
6827
|
}), switchMap((options) => {
|
|
6680
6828
|
const requestOptions = this.updateRequestOptions(options?.headers);
|
|
6681
6829
|
requestOptions.stream = true;
|
|
6830
|
+
const effectiveParams = this.getEffectiveParams(options?.path);
|
|
6831
|
+
const requestSignature = this.buildRequestSignature('STREAM', requestOptions, effectiveParams);
|
|
6682
6832
|
console.log('[DEBUG] Making streaming request:', requestOptions);
|
|
6683
|
-
|
|
6833
|
+
if (this.hasDatabase && this.databaseOptions?.table && !options?.forceRefresh) {
|
|
6834
|
+
return this.localStorageManagerService.store$(this.databaseOptions.table).pipe(take(1), switchMap((storeData) => {
|
|
6835
|
+
const cached = this.getRequestCacheMetadata(storeData, 'STREAM');
|
|
6836
|
+
const sameSignature = !!cached?.signature && cached.signature === requestSignature;
|
|
6837
|
+
const expires = storeData?.expires || 0;
|
|
6838
|
+
const hasExpired = expires > 0 && this.utils.hasExpired(expires);
|
|
6839
|
+
if (sameSignature && !hasExpired) {
|
|
6840
|
+
return this.dbManagerService.getTableRecords(this.databaseOptions.table).pipe(switchMap((dbData) => {
|
|
6841
|
+
if (Array.isArray(dbData) && dbData.length > 0) {
|
|
6842
|
+
return of({ data: dbData, fromCache: true });
|
|
6843
|
+
}
|
|
6844
|
+
return this.httpManagerService.getRequest(requestOptions, effectiveParams).pipe(map((apiData) => ({ data: apiData, fromCache: false })));
|
|
6845
|
+
}));
|
|
6846
|
+
}
|
|
6847
|
+
return this.httpManagerService.getRequest(requestOptions, effectiveParams).pipe(map((apiData) => ({ data: apiData, fromCache: false })));
|
|
6848
|
+
})).pipe(tap((packet) => {
|
|
6849
|
+
const res = packet?.data;
|
|
6850
|
+
console.log('[DEBUG] Streaming response received:', res);
|
|
6851
|
+
if (res && res.length > 0) {
|
|
6852
|
+
console.log('[DEBUG] Updating state with streaming data:', res);
|
|
6853
|
+
this.setData$(res);
|
|
6854
|
+
this.streamedResponse = res;
|
|
6855
|
+
}
|
|
6856
|
+
else {
|
|
6857
|
+
console.log('[DEBUG] No streaming data or empty array:', res);
|
|
6858
|
+
}
|
|
6859
|
+
// Reset pending once we have a response packet (cache or network)
|
|
6860
|
+
this.httpManagerService.isPending.next(false);
|
|
6861
|
+
}), concatMap((packet) => {
|
|
6862
|
+
if (packet?.fromCache) {
|
|
6863
|
+
return of(packet?.data);
|
|
6864
|
+
}
|
|
6865
|
+
return this.persistStreamDataToDb(packet?.data, options);
|
|
6866
|
+
}), map((res) => {
|
|
6867
|
+
console.log('[DEBUG] Returning data to subscribers:', res);
|
|
6868
|
+
return res;
|
|
6869
|
+
}), catchError((error) => {
|
|
6870
|
+
console.error('[DEBUG] Streaming error:', error);
|
|
6871
|
+
this.httpManagerService.isPending.next(false);
|
|
6872
|
+
return of([]);
|
|
6873
|
+
}));
|
|
6874
|
+
}
|
|
6875
|
+
return this.httpManagerService.getRequest(requestOptions, effectiveParams)
|
|
6684
6876
|
.pipe(tap((res) => {
|
|
6685
6877
|
console.log('[DEBUG] Streaming response received:', res);
|
|
6686
6878
|
// Always update state with streaming data
|
|
@@ -6692,7 +6884,9 @@ class HTTPManagerStateService extends ComponentStore {
|
|
|
6692
6884
|
else {
|
|
6693
6885
|
console.log('[DEBUG] No streaming data or empty array:', res);
|
|
6694
6886
|
}
|
|
6695
|
-
|
|
6887
|
+
// Reset pending once we have a response packet
|
|
6888
|
+
this.httpManagerService.isPending.next(false);
|
|
6889
|
+
}), concatMap((res) => this.persistStreamDataToDb(res, options)), map((res) => {
|
|
6696
6890
|
console.log('[DEBUG] Returning data to subscribers:', res);
|
|
6697
6891
|
return res; // Return the data so subscribers can receive it
|
|
6698
6892
|
}), catchError((error) => {
|
|
@@ -6719,7 +6913,11 @@ class HTTPManagerStateService extends ComponentStore {
|
|
|
6719
6913
|
encrypted: false,
|
|
6720
6914
|
})
|
|
6721
6915
|
});
|
|
6722
|
-
|
|
6916
|
+
// Use initDBStorageAsync directly - the effect initDBStorage requires subscription
|
|
6917
|
+
this.initDBStorageAsync().subscribe({
|
|
6918
|
+
next: () => console.log('[Constructor] Database storage initialized'),
|
|
6919
|
+
error: (err) => console.error('[Constructor] Database storage initialization failed:', err)
|
|
6920
|
+
});
|
|
6723
6921
|
}
|
|
6724
6922
|
}
|
|
6725
6923
|
catch (error) {
|
|
@@ -6773,8 +6971,24 @@ class HTTPManagerStateService extends ComponentStore {
|
|
|
6773
6971
|
this.dataType = (dataType) ? dataType : DataType.ARRAY;
|
|
6774
6972
|
// Only update database options if a database parameter is explicitly provided
|
|
6775
6973
|
if (database !== undefined) {
|
|
6974
|
+
console.log('[setApiRequestOptions] Database config:', {
|
|
6975
|
+
database,
|
|
6976
|
+
hasTable: !!database?.table,
|
|
6977
|
+
tableValue: database?.table
|
|
6978
|
+
});
|
|
6776
6979
|
this.hasDatabase = (database?.table) ? true : false;
|
|
6777
|
-
|
|
6980
|
+
const adapted = DatabaseStorage.adapt(database);
|
|
6981
|
+
console.log('[setApiRequestOptions] DatabaseStorage.adapt result:', adapted);
|
|
6982
|
+
this.databaseOptions = (this.hasDatabase) ? adapted : undefined;
|
|
6983
|
+
// Trigger database table creation if table is configured
|
|
6984
|
+
if (this.hasDatabase && this.databaseOptions?.table) {
|
|
6985
|
+
console.log('[setApiRequestOptions] Initializing database storage for table:', this.databaseOptions.table);
|
|
6986
|
+
// Use initDBStorageAsync directly instead of the effect - effects need subscription to run
|
|
6987
|
+
this.initDBStorageAsync().subscribe({
|
|
6988
|
+
next: () => console.log('[setApiRequestOptions] Database storage initialized successfully'),
|
|
6989
|
+
error: (err) => console.error('[setApiRequestOptions] Database storage initialization failed:', err)
|
|
6990
|
+
});
|
|
6991
|
+
}
|
|
6778
6992
|
}
|
|
6779
6993
|
if (this.apiOptions.ws && this.apiOptions.ws.id !== '') {
|
|
6780
6994
|
// Auto-prefix channel ID for private state manager channels
|
|
@@ -6896,11 +7110,20 @@ class HTTPManagerStateService extends ComponentStore {
|
|
|
6896
7110
|
this.setData$(data);
|
|
6897
7111
|
}
|
|
6898
7112
|
updateArrayState(currentData, newData) {
|
|
7113
|
+
// For non-streaming requests (GET), REPLACE data entirely
|
|
7114
|
+
// For streaming requests, MERGE with existing data incrementally
|
|
7115
|
+
if (this.streamedResponse.length === 0) {
|
|
7116
|
+
// GET request: return new data as-is (replace)
|
|
7117
|
+
return newData;
|
|
7118
|
+
}
|
|
7119
|
+
// Streaming: merge with existing data
|
|
6899
7120
|
const filterCurrentData = () => {
|
|
6900
7121
|
const ids = this.streamedResponse.map((obj) => obj.id);
|
|
6901
7122
|
return currentData.filter(obj => (obj.id) ? ids.includes(obj.id) : obj);
|
|
6902
7123
|
};
|
|
6903
|
-
const filteredCurrentData = (this.httpManagerService.isPending.value)
|
|
7124
|
+
const filteredCurrentData = (this.httpManagerService.isPending.value)
|
|
7125
|
+
? currentData
|
|
7126
|
+
: filterCurrentData();
|
|
6904
7127
|
const updatedData = filteredCurrentData.map(item => {
|
|
6905
7128
|
const newItem = newData.find(newItem => {
|
|
6906
7129
|
const hasId = (newItem?.id && item?.id) ? true : false;
|
|
@@ -6917,32 +7140,92 @@ class HTTPManagerStateService extends ComponentStore {
|
|
|
6917
7140
|
return [...updatedData, ...addedData];
|
|
6918
7141
|
}
|
|
6919
7142
|
initDBStorageAsync() {
|
|
7143
|
+
console.log('[initDBStorageAsync] Starting initialization:', {
|
|
7144
|
+
dataType: this.dataType,
|
|
7145
|
+
hasAdapter: !!this.apiOptions?.adapter,
|
|
7146
|
+
table: this.databaseOptions?.table,
|
|
7147
|
+
databaseOptions: this.databaseOptions
|
|
7148
|
+
});
|
|
6920
7149
|
if (this.dataType !== DataType.ARRAY) {
|
|
6921
7150
|
console.warn('Database storage requires dataType to be ARRAY');
|
|
6922
7151
|
return of(null);
|
|
6923
7152
|
}
|
|
6924
7153
|
if (!this.apiOptions.adapter) {
|
|
6925
|
-
console.warn('Database storage
|
|
6926
|
-
return of(null);
|
|
7154
|
+
console.warn('Database storage adapter missing, using minimal or inferred schema');
|
|
6927
7155
|
}
|
|
6928
7156
|
if (!this.databaseOptions?.table) {
|
|
6929
7157
|
console.warn('Database storage requires a table name');
|
|
6930
7158
|
return of(null);
|
|
6931
7159
|
}
|
|
7160
|
+
const schema = this.buildSchemaFromAdapter();
|
|
7161
|
+
const tableDef = TableSchemaDef.adapt({
|
|
7162
|
+
table: this.databaseOptions?.table,
|
|
7163
|
+
schema: schema
|
|
7164
|
+
});
|
|
7165
|
+
return this.dbManagerService.createDatabaseTable(tableDef).pipe(tap((created) => {
|
|
7166
|
+
if (created && this.databaseOptions?.table) {
|
|
7167
|
+
this.saveSchemaSignature(this.databaseOptions.table, this.buildSchemaSignature(schema));
|
|
7168
|
+
}
|
|
7169
|
+
}));
|
|
7170
|
+
}
|
|
7171
|
+
buildSchemaFromAdapter() {
|
|
6932
7172
|
const sampleData = this.apiOptions.adapter?.({}) || {};
|
|
6933
|
-
|
|
7173
|
+
return this.buildSchemaFromSample(sampleData);
|
|
7174
|
+
}
|
|
7175
|
+
buildSchemaFromSample(sampleData) {
|
|
7176
|
+
const schemaKeys = Object.keys(sampleData || {}).filter(key => sampleData[key] !== undefined);
|
|
6934
7177
|
let schema = '++id';
|
|
6935
7178
|
if (schemaKeys.length > 0) {
|
|
6936
|
-
const otherKeys = schemaKeys.filter(k => k !== 'id');
|
|
7179
|
+
const otherKeys = schemaKeys.filter((k) => k !== 'id');
|
|
6937
7180
|
if (otherKeys.length > 0) {
|
|
6938
7181
|
schema += ', ' + otherKeys.join(', ');
|
|
6939
7182
|
}
|
|
6940
7183
|
}
|
|
6941
|
-
|
|
6942
|
-
|
|
6943
|
-
|
|
6944
|
-
|
|
6945
|
-
|
|
7184
|
+
return schema;
|
|
7185
|
+
}
|
|
7186
|
+
persistStreamDataToDb(payload, streamOptions) {
|
|
7187
|
+
if (!this.hasDatabase || !this.databaseOptions?.table) {
|
|
7188
|
+
return of(payload);
|
|
7189
|
+
}
|
|
7190
|
+
const dbData = (payload?.results && Array.isArray(payload.results))
|
|
7191
|
+
? payload.results
|
|
7192
|
+
: Array.isArray(payload)
|
|
7193
|
+
? payload
|
|
7194
|
+
: payload
|
|
7195
|
+
? [payload]
|
|
7196
|
+
: [];
|
|
7197
|
+
if (dbData.length === 0) {
|
|
7198
|
+
return of(payload);
|
|
7199
|
+
}
|
|
7200
|
+
const tableName = this.databaseOptions.table;
|
|
7201
|
+
const requestOptions = this.updateRequestOptions(streamOptions?.headers);
|
|
7202
|
+
requestOptions.stream = true;
|
|
7203
|
+
const effectiveParams = this.getEffectiveParams(streamOptions?.path);
|
|
7204
|
+
const requestSignature = this.buildRequestSignature('STREAM', requestOptions, effectiveParams);
|
|
7205
|
+
this.localStorageManagerService.updateStore({
|
|
7206
|
+
name: tableName,
|
|
7207
|
+
data: { ...this.databaseOptions, ...{ expires: this.utils.expires(this.databaseOptions.expiresIn) } }
|
|
7208
|
+
});
|
|
7209
|
+
const schema = this.buildSchemaFromSample(dbData[0]);
|
|
7210
|
+
const schemaSignature = this.buildSchemaSignature(schema);
|
|
7211
|
+
const tableDef = TableSchemaDef.adapt({ table: tableName, schema });
|
|
7212
|
+
return this.localStorageManagerService.store$(tableName).pipe(take(1), switchMap((storeData) => {
|
|
7213
|
+
const storedSchemaSignature = this.getStoredSchemaSignature(storeData);
|
|
7214
|
+
const schemaChanged = !!storedSchemaSignature && storedSchemaSignature !== schemaSignature;
|
|
7215
|
+
const ensureTable$ = schemaChanged
|
|
7216
|
+
? this.dbManagerService.clearTable(tableName).pipe(switchMap(() => this.dbManagerService.createDatabaseTable(tableDef)))
|
|
7217
|
+
: this.dbManagerService.createDatabaseTable(tableDef);
|
|
7218
|
+
return ensureTable$.pipe(switchMap((created) => {
|
|
7219
|
+
if (!created) {
|
|
7220
|
+
console.warn('[DB STORAGE] Stream table create/open not ready, skipping DB write for this chunk:', { table: tableName });
|
|
7221
|
+
return of(payload);
|
|
7222
|
+
}
|
|
7223
|
+
return this.dbManagerService.createTableRecords(tableName, dbData).pipe(tap(() => this.saveRequestCacheMetadata(tableName, 'STREAM', requestSignature, schemaSignature, streamOptions)), map(() => payload));
|
|
7224
|
+
}));
|
|
7225
|
+
}), map(() => payload), catchError((error) => {
|
|
7226
|
+
console.error('[DB STORAGE] Failed to persist streaming payload:', { table: tableName, schema, error });
|
|
7227
|
+
return of(payload);
|
|
7228
|
+
}));
|
|
6946
7229
|
}
|
|
6947
7230
|
// WEBSOCKET COMMUNICATION (STATE MANAGER)
|
|
6948
7231
|
wsCommunication(method, path) {
|
|
@@ -7238,6 +7521,7 @@ class HTTPManagerStateService extends ComponentStore {
|
|
|
7238
7521
|
const tableName = this.databaseOptions.table;
|
|
7239
7522
|
this.dbManagerService.clearTable(tableName).subscribe({
|
|
7240
7523
|
next: () => {
|
|
7524
|
+
this.clearRequestCacheMetadata(tableName);
|
|
7241
7525
|
if (this.dataType === DataType.ARRAY) {
|
|
7242
7526
|
this.setData$([]);
|
|
7243
7527
|
}
|
|
@@ -7260,6 +7544,115 @@ class HTTPManagerStateService extends ComponentStore {
|
|
|
7260
7544
|
: { ...options.headers };
|
|
7261
7545
|
return options;
|
|
7262
7546
|
}
|
|
7547
|
+
normalizeObject(value) {
|
|
7548
|
+
if (Array.isArray(value)) {
|
|
7549
|
+
return value.map((item) => this.normalizeObject(item));
|
|
7550
|
+
}
|
|
7551
|
+
if (value && typeof value === 'object') {
|
|
7552
|
+
return Object.keys(value)
|
|
7553
|
+
.sort()
|
|
7554
|
+
.reduce((acc, key) => {
|
|
7555
|
+
acc[key] = this.normalizeObject(value[key]);
|
|
7556
|
+
return acc;
|
|
7557
|
+
}, {});
|
|
7558
|
+
}
|
|
7559
|
+
return value;
|
|
7560
|
+
}
|
|
7561
|
+
filterHeaders(headers) {
|
|
7562
|
+
const source = headers || {};
|
|
7563
|
+
return Object.keys(source).reduce((acc, key) => {
|
|
7564
|
+
if (!this.volatileHeaders.has(key.toLowerCase())) {
|
|
7565
|
+
acc[key] = source[key];
|
|
7566
|
+
}
|
|
7567
|
+
return acc;
|
|
7568
|
+
}, {});
|
|
7569
|
+
}
|
|
7570
|
+
resolvePath(params) {
|
|
7571
|
+
const basePath = Array.isArray(this.apiOptions.path) ? this.apiOptions.path : [];
|
|
7572
|
+
const effective = this.getEffectiveParams(params);
|
|
7573
|
+
return effective ? [...basePath, ...effective] : [...basePath];
|
|
7574
|
+
}
|
|
7575
|
+
getEffectiveParams(params) {
|
|
7576
|
+
if (!Array.isArray(params) || params.length === 0) {
|
|
7577
|
+
return undefined;
|
|
7578
|
+
}
|
|
7579
|
+
const basePath = Array.isArray(this.apiOptions.path) ? this.apiOptions.path : [];
|
|
7580
|
+
if (basePath.length !== params.length) {
|
|
7581
|
+
return params;
|
|
7582
|
+
}
|
|
7583
|
+
const normalizePart = (value) => {
|
|
7584
|
+
if (value && typeof value === 'object') {
|
|
7585
|
+
return JSON.stringify(this.normalizeObject(value));
|
|
7586
|
+
}
|
|
7587
|
+
return String(value);
|
|
7588
|
+
};
|
|
7589
|
+
const samePath = params.every((part, index) => normalizePart(part) === normalizePart(basePath[index]));
|
|
7590
|
+
return samePath ? undefined : params;
|
|
7591
|
+
}
|
|
7592
|
+
buildRequestSignature(method, requestOptions, params) {
|
|
7593
|
+
const signaturePayload = {
|
|
7594
|
+
method,
|
|
7595
|
+
server: requestOptions.server,
|
|
7596
|
+
path: this.resolvePath(params),
|
|
7597
|
+
headers: this.filterHeaders(requestOptions.headers),
|
|
7598
|
+
stream: !!requestOptions.stream,
|
|
7599
|
+
streamType: requestOptions.streamType || null
|
|
7600
|
+
};
|
|
7601
|
+
return JSON.stringify(this.normalizeObject(signaturePayload));
|
|
7602
|
+
}
|
|
7603
|
+
buildSchemaSignature(schema) {
|
|
7604
|
+
return JSON.stringify(this.normalizeObject(schema.split(',').map((part) => part.trim()).filter(Boolean)));
|
|
7605
|
+
}
|
|
7606
|
+
getRequestCacheMetadata(storeData, type) {
|
|
7607
|
+
return storeData?.requestCache?.[type] || null;
|
|
7608
|
+
}
|
|
7609
|
+
getStoredSchemaSignature(storeData) {
|
|
7610
|
+
return storeData?.schemaSignature || null;
|
|
7611
|
+
}
|
|
7612
|
+
saveSchemaSignature(tableName, schemaSignature) {
|
|
7613
|
+
this.localStorageManagerService.store$(tableName).pipe(take(1), tap((storeData) => {
|
|
7614
|
+
this.localStorageManagerService.updateStore({
|
|
7615
|
+
name: tableName,
|
|
7616
|
+
data: {
|
|
7617
|
+
...(storeData || {}),
|
|
7618
|
+
schemaSignature,
|
|
7619
|
+
}
|
|
7620
|
+
});
|
|
7621
|
+
})).subscribe();
|
|
7622
|
+
}
|
|
7623
|
+
saveRequestCacheMetadata(tableName, type, signature, schemaSignature, options) {
|
|
7624
|
+
this.localStorageManagerService.store$(tableName).pipe(take(1), tap((storeData) => {
|
|
7625
|
+
const currentCache = storeData?.requestCache || {};
|
|
7626
|
+
this.localStorageManagerService.updateStore({
|
|
7627
|
+
name: tableName,
|
|
7628
|
+
data: {
|
|
7629
|
+
...(storeData || {}),
|
|
7630
|
+
schemaSignature: schemaSignature || storeData?.schemaSignature || null,
|
|
7631
|
+
requestCache: {
|
|
7632
|
+
...currentCache,
|
|
7633
|
+
[type]: {
|
|
7634
|
+
signature,
|
|
7635
|
+
savedAt: Date.now(),
|
|
7636
|
+
path: this.resolvePath(options?.path),
|
|
7637
|
+
headers: this.filterHeaders(options?.headers)
|
|
7638
|
+
}
|
|
7639
|
+
}
|
|
7640
|
+
}
|
|
7641
|
+
});
|
|
7642
|
+
})).subscribe();
|
|
7643
|
+
}
|
|
7644
|
+
clearRequestCacheMetadata(tableName) {
|
|
7645
|
+
this.localStorageManagerService.store$(tableName).pipe(take(1), tap((storeData) => {
|
|
7646
|
+
if (!storeData)
|
|
7647
|
+
return;
|
|
7648
|
+
const updated = { ...(storeData || {}) };
|
|
7649
|
+
delete updated.requestCache;
|
|
7650
|
+
this.localStorageManagerService.updateStore({
|
|
7651
|
+
name: tableName,
|
|
7652
|
+
data: updated
|
|
7653
|
+
});
|
|
7654
|
+
})).subscribe();
|
|
7655
|
+
}
|
|
7263
7656
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: HTTPManagerStateService, deps: [{ token: API_OPTS }, { token: "dataType" }, { token: DatabaseStorage }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
7264
7657
|
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: HTTPManagerStateService }); }
|
|
7265
7658
|
}
|
|
@@ -8596,13 +8989,13 @@ class StateManagerDemoService extends HTTPManagerStateService {
|
|
|
8596
8989
|
const sampleOptions = RequestOptions.adapt({ path: [data.id] });
|
|
8597
8990
|
this.deleteRecord(sampleOptions);
|
|
8598
8991
|
}
|
|
8599
|
-
streamRequest() {
|
|
8992
|
+
streamRequest(options) {
|
|
8600
8993
|
console.log('[DEMO SERVICE] streamRequest called');
|
|
8601
8994
|
const headers = {
|
|
8602
8995
|
auth: "sample-auth-token"
|
|
8603
8996
|
};
|
|
8604
8997
|
console.log('[DEMO SERVICE] Calling fetchStream with headers:', headers);
|
|
8605
|
-
this.fetchStream();
|
|
8998
|
+
this.fetchStream(options);
|
|
8606
8999
|
}
|
|
8607
9000
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: StateManagerDemoService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
8608
9001
|
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: StateManagerDemoService }); }
|
|
@@ -8849,11 +9242,23 @@ class RequestManagerStateDemoComponent {
|
|
|
8849
9242
|
return { apiOptions: apiOptions, path: pathReq };
|
|
8850
9243
|
}
|
|
8851
9244
|
onSetStateOptions() {
|
|
8852
|
-
|
|
9245
|
+
const dbValue = this.database;
|
|
9246
|
+
console.log('[onSetStateOptions] Called, checking form values:', {
|
|
9247
|
+
isValid: this.isValid,
|
|
9248
|
+
database: dbValue,
|
|
9249
|
+
hasTable: !!dbValue?.table,
|
|
9250
|
+
tableValue: dbValue?.table,
|
|
9251
|
+
dataType: this.dataType
|
|
9252
|
+
});
|
|
9253
|
+
if (!this.isValid) {
|
|
9254
|
+
console.log('[onSetStateOptions] Form invalid, aborting');
|
|
8853
9255
|
return;
|
|
9256
|
+
}
|
|
8854
9257
|
const reqParams = this.compileRequest();
|
|
8855
|
-
const db = DatabaseStorage.adapt(
|
|
9258
|
+
const db = DatabaseStorage.adapt(dbValue);
|
|
9259
|
+
console.log('[onSetStateOptions] DatabaseStorage.adapt result:', db);
|
|
8856
9260
|
const type = this.dataType === "ARRAY" ? DataType.ARRAY : DataType.OBJECT;
|
|
9261
|
+
console.log('[onSetStateOptions] Calling setAPIOptions with:', { db, type, hasTable: !!db?.table });
|
|
8857
9262
|
this.stateManagerDemoService.setAPIOptions(reqParams.apiOptions, type, db);
|
|
8858
9263
|
this.requestForm.markAsPristine();
|
|
8859
9264
|
}
|
|
@@ -8888,8 +9293,13 @@ class RequestManagerStateDemoComponent {
|
|
|
8888
9293
|
reqParams.apiOptions.stream = true;
|
|
8889
9294
|
reqParams.apiOptions.streamType = this.streamType;
|
|
8890
9295
|
this.requestType = 'STREAM';
|
|
9296
|
+
const streamOptions = RequestOptions.adapt({
|
|
9297
|
+
path: reqParams.path,
|
|
9298
|
+
headers: reqParams.apiOptions.headers,
|
|
9299
|
+
forceRefresh: false,
|
|
9300
|
+
});
|
|
8891
9301
|
console.log('[COMPONENT] Calling streamRequest...');
|
|
8892
|
-
this.stateManagerDemoService.streamRequest();
|
|
9302
|
+
this.stateManagerDemoService.streamRequest(streamOptions);
|
|
8893
9303
|
}
|
|
8894
9304
|
errorHandling(err, type) {
|
|
8895
9305
|
console.log(err, type);
|
|
@@ -9934,6 +10344,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
9934
10344
|
}]
|
|
9935
10345
|
}] });
|
|
9936
10346
|
|
|
10347
|
+
class UserData {
|
|
10348
|
+
constructor(ldap = '', name = '', email = '', color = RandomPaletteColor()) {
|
|
10349
|
+
this.ldap = ldap;
|
|
10350
|
+
this.name = name;
|
|
10351
|
+
this.email = email;
|
|
10352
|
+
this.color = color;
|
|
10353
|
+
}
|
|
10354
|
+
static adapt(item) {
|
|
10355
|
+
const userName = `${item?.first_name} ${item?.last_name}`;
|
|
10356
|
+
return new UserData(item?.ldap, userName, item?.email, item?.color);
|
|
10357
|
+
}
|
|
10358
|
+
}
|
|
10359
|
+
|
|
9937
10360
|
class StateDataRequestService extends HTTPManagerStateService {
|
|
9938
10361
|
constructor() {
|
|
9939
10362
|
super(ApiRequest.adapt({
|