http-request-manager 18.13.0 → 18.13.2
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.
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
2
|
import { inject, Injectable, APP_ID, Inject, InjectionToken, isDevMode, signal, effect, computed, Injector, Optional, EventEmitter, Input, Output, ViewEncapsulation, Component, NgModule, ViewChild } from '@angular/core';
|
|
3
3
|
import { ComponentStore } from '@ngrx/component-store';
|
|
4
|
-
import { map, catchError, filter, tap, finalize, takeWhile, retry, startWith, mergeMap, takeUntil, concatMap, toArray, withLatestFrom, switchMap, delay,
|
|
4
|
+
import { map, catchError, take, filter, tap, finalize, takeWhile, retry, startWith, mergeMap, takeUntil, concatMap, toArray, withLatestFrom, switchMap, delay, scan, distinctUntilChanged } from 'rxjs/operators';
|
|
5
5
|
import { HttpClient, HttpHeaders, HttpEventType, HttpHeaderResponse, HttpErrorResponse, HTTP_INTERCEPTORS } from '@angular/common/http';
|
|
6
6
|
import * as CryptoJS from 'crypto-js';
|
|
7
7
|
import { from, BehaviorSubject, EMPTY, throwError, defer, interval, timer, Subject, of, merge, Subscription, take as take$1, catchError as catchError$1, map as map$1, tap as tap$1, switchMap as switchMap$1, startWith as startWith$1, distinctUntilChanged as distinctUntilChanged$1, combineLatest, filter as filter$1, takeUntil as takeUntil$1, ReplaySubject } from 'rxjs';
|
|
@@ -875,6 +875,382 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
875
875
|
}]
|
|
876
876
|
}], ctorParameters: () => [] });
|
|
877
877
|
|
|
878
|
+
class PathTrackerStateModel {
|
|
879
|
+
constructor(baselineQuery, consumedValuesByKey = {}, watchExpiresAt, trackedAt) {
|
|
880
|
+
this.baselineQuery = baselineQuery;
|
|
881
|
+
this.consumedValuesByKey = consumedValuesByKey;
|
|
882
|
+
this.watchExpiresAt = watchExpiresAt;
|
|
883
|
+
this.trackedAt = trackedAt;
|
|
884
|
+
}
|
|
885
|
+
static adapt(item) {
|
|
886
|
+
return new PathTrackerStateModel(item?.baselineQuery, item?.consumedValuesByKey, item?.watchExpiresAt, item?.trackedAt);
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
class QueryTrackerStateModel {
|
|
891
|
+
constructor(paths = {}) {
|
|
892
|
+
this.paths = paths;
|
|
893
|
+
}
|
|
894
|
+
static adapt(item) {
|
|
895
|
+
return new QueryTrackerStateModel(item?.paths
|
|
896
|
+
? Object.keys(item.paths).reduce((acc, key) => {
|
|
897
|
+
acc[key] = PathTrackerStateModel.adapt(item.paths[key]);
|
|
898
|
+
return acc;
|
|
899
|
+
}, {})
|
|
900
|
+
: {});
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
const TRACKER_STORE_NAME = 'query_params_tracker';
|
|
905
|
+
const TRACKER_SESSION_INIT_KEY = 'query_params_tracker_initialized';
|
|
906
|
+
const DEFAULT_TRACKER_OPTIONS = SettingOptions.adapt({
|
|
907
|
+
storage: StorageType.GLOBAL,
|
|
908
|
+
encrypted: false,
|
|
909
|
+
});
|
|
910
|
+
class QueryParamsTrackerService {
|
|
911
|
+
constructor() {
|
|
912
|
+
this.state = { paths: {} };
|
|
913
|
+
this.stateRestored = false;
|
|
914
|
+
this.localStorageManager = inject(LocalStorageManagerService);
|
|
915
|
+
}
|
|
916
|
+
clearTracking(resetSessionInit = false) {
|
|
917
|
+
this.state = { paths: {} };
|
|
918
|
+
this.localStorageManager.deleteStore({ name: TRACKER_STORE_NAME });
|
|
919
|
+
if (resetSessionInit && this.hasSessionStorage()) {
|
|
920
|
+
sessionStorage.removeItem(TRACKER_SESSION_INIT_KEY);
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
checkRequestOptions(requestOptions = [], options = {}) {
|
|
924
|
+
this.ensureStateRestored();
|
|
925
|
+
const normalized = this.normalizeRequestOptions(requestOptions);
|
|
926
|
+
if (!normalized.pathKey)
|
|
927
|
+
return false;
|
|
928
|
+
this.cleanupExpiredEntries();
|
|
929
|
+
if (!normalized.hasQuery) {
|
|
930
|
+
const pathExists = !!this.state.paths[normalized.pathKey];
|
|
931
|
+
if (pathExists) {
|
|
932
|
+
return false;
|
|
933
|
+
}
|
|
934
|
+
const pathState = this.ensurePathState(normalized.pathKey);
|
|
935
|
+
pathState.trackedAt = Math.floor(Date.now() / 1000);
|
|
936
|
+
this.persistState();
|
|
937
|
+
return true;
|
|
938
|
+
}
|
|
939
|
+
if (options.mode === 'exact') {
|
|
940
|
+
return this.checkExact(normalized, options);
|
|
941
|
+
}
|
|
942
|
+
return this.checkVariation(normalized, options);
|
|
943
|
+
}
|
|
944
|
+
matchesPath(requestOptions = [], expectedPathOptions = []) {
|
|
945
|
+
this.ensureStateRestored();
|
|
946
|
+
const requestPath = this.normalizeRequestOptions(requestOptions).pathKey;
|
|
947
|
+
const expectedPath = this.normalizeRequestOptions(expectedPathOptions).pathKey;
|
|
948
|
+
return Boolean(requestPath) && requestPath === expectedPath;
|
|
949
|
+
}
|
|
950
|
+
checkExact(normalized, options) {
|
|
951
|
+
const pathState = this.ensurePathState(normalized.pathKey);
|
|
952
|
+
const filteredQuery = normalized.query;
|
|
953
|
+
if (Object.keys(filteredQuery).length === 0) {
|
|
954
|
+
return false;
|
|
955
|
+
}
|
|
956
|
+
if (!pathState.baselineQuery) {
|
|
957
|
+
pathState.baselineQuery = filteredQuery;
|
|
958
|
+
this.persistState();
|
|
959
|
+
return true;
|
|
960
|
+
}
|
|
961
|
+
return this.stringifyQuery(pathState.baselineQuery) === this.stringifyQuery(filteredQuery);
|
|
962
|
+
}
|
|
963
|
+
checkVariation(normalized, options) {
|
|
964
|
+
const pathState = this.ensurePathState(normalized.pathKey);
|
|
965
|
+
const filteredQuery = normalized.query;
|
|
966
|
+
const keys = Object.keys(filteredQuery);
|
|
967
|
+
if (keys.length === 0) {
|
|
968
|
+
return false;
|
|
969
|
+
}
|
|
970
|
+
this.resetPathStateIfExpired(pathState);
|
|
971
|
+
let accepted = false;
|
|
972
|
+
keys.forEach((key) => {
|
|
973
|
+
const currentValue = filteredQuery[key];
|
|
974
|
+
const consumed = pathState.consumedValuesByKey[key] || [];
|
|
975
|
+
if (!consumed.includes(currentValue)) {
|
|
976
|
+
pathState.consumedValuesByKey[key] = [...consumed, currentValue];
|
|
977
|
+
accepted = true;
|
|
978
|
+
}
|
|
979
|
+
});
|
|
980
|
+
if (accepted) {
|
|
981
|
+
if (!pathState.baselineQuery) {
|
|
982
|
+
pathState.baselineQuery = filteredQuery;
|
|
983
|
+
}
|
|
984
|
+
pathState.watchExpiresAt = this.buildExpiryEpoch(typeof options.watchExpiresAt !== 'undefined' ? options.watchExpiresAt : options.watchParamsExpire);
|
|
985
|
+
this.persistState();
|
|
986
|
+
}
|
|
987
|
+
return accepted;
|
|
988
|
+
}
|
|
989
|
+
normalizeRequestOptions(requestOptions) {
|
|
990
|
+
const params = Array.isArray(requestOptions) ? requestOptions : [];
|
|
991
|
+
const pathSegments = [];
|
|
992
|
+
const queryObjects = [];
|
|
993
|
+
params.forEach((item) => {
|
|
994
|
+
if (this.isPlainObject(item)) {
|
|
995
|
+
queryObjects.push(item);
|
|
996
|
+
}
|
|
997
|
+
else if (typeof item !== 'undefined' && item !== null) {
|
|
998
|
+
const parsed = this.parsePathSegment(String(item));
|
|
999
|
+
if (parsed.pathSegment) {
|
|
1000
|
+
pathSegments.push(parsed.pathSegment);
|
|
1001
|
+
}
|
|
1002
|
+
if (Object.keys(parsed.queryObject).length > 0) {
|
|
1003
|
+
queryObjects.push(parsed.queryObject);
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
});
|
|
1007
|
+
const query = queryObjects.reduce((acc, current) => {
|
|
1008
|
+
Object.keys(current).forEach((key) => {
|
|
1009
|
+
const normalizedKey = this.normalizeParamKey(key);
|
|
1010
|
+
const value = current[key];
|
|
1011
|
+
if (!normalizedKey || typeof value === 'undefined' || value === null || value === '') {
|
|
1012
|
+
return;
|
|
1013
|
+
}
|
|
1014
|
+
acc[normalizedKey] = this.normalizeParamValue(value);
|
|
1015
|
+
});
|
|
1016
|
+
return acc;
|
|
1017
|
+
}, {});
|
|
1018
|
+
return {
|
|
1019
|
+
pathKey: this.normalizePath(pathSegments),
|
|
1020
|
+
query,
|
|
1021
|
+
hasQuery: Object.keys(query).length > 0,
|
|
1022
|
+
};
|
|
1023
|
+
}
|
|
1024
|
+
parsePathSegment(rawSegment) {
|
|
1025
|
+
const [pathPart, ...queryParts] = String(rawSegment).split('?');
|
|
1026
|
+
const queryString = queryParts.join('?');
|
|
1027
|
+
const queryObject = {};
|
|
1028
|
+
if (queryString) {
|
|
1029
|
+
queryString.split('&').forEach((pair) => {
|
|
1030
|
+
if (!pair)
|
|
1031
|
+
return;
|
|
1032
|
+
const [rawKey, ...rawValueParts] = pair.split('=');
|
|
1033
|
+
const key = this.safeDecode(rawKey).trim();
|
|
1034
|
+
const value = this.safeDecode(rawValueParts.join('=')).trim();
|
|
1035
|
+
if (!key)
|
|
1036
|
+
return;
|
|
1037
|
+
queryObject[key] = value;
|
|
1038
|
+
});
|
|
1039
|
+
}
|
|
1040
|
+
return {
|
|
1041
|
+
pathSegment: pathPart,
|
|
1042
|
+
queryObject,
|
|
1043
|
+
};
|
|
1044
|
+
}
|
|
1045
|
+
safeDecode(value) {
|
|
1046
|
+
if (typeof value === 'undefined') {
|
|
1047
|
+
return '';
|
|
1048
|
+
}
|
|
1049
|
+
try {
|
|
1050
|
+
return decodeURIComponent(value);
|
|
1051
|
+
}
|
|
1052
|
+
catch {
|
|
1053
|
+
return String(value);
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
normalizePath(pathSegments) {
|
|
1057
|
+
return pathSegments
|
|
1058
|
+
.map((segment) => String(segment).trim())
|
|
1059
|
+
.filter((segment) => segment.length > 0)
|
|
1060
|
+
.join('/')
|
|
1061
|
+
.replace(/([^:]\/+)\/+/g, '$1')
|
|
1062
|
+
.replace(/^\//, '')
|
|
1063
|
+
.replace(/\/$/, '');
|
|
1064
|
+
}
|
|
1065
|
+
normalizeParamKey(key) {
|
|
1066
|
+
return String(key).trim().toLowerCase();
|
|
1067
|
+
}
|
|
1068
|
+
normalizeParamValue(value) {
|
|
1069
|
+
if (Array.isArray(value)) {
|
|
1070
|
+
return value.map((item) => this.normalizeParamValue(item)).join(',');
|
|
1071
|
+
}
|
|
1072
|
+
if (this.isPlainObject(value)) {
|
|
1073
|
+
return this.stringifyQuery(Object.keys(value).reduce((acc, key) => {
|
|
1074
|
+
acc[this.normalizeParamKey(key)] = this.normalizeParamValue(value[key]);
|
|
1075
|
+
return acc;
|
|
1076
|
+
}, {}));
|
|
1077
|
+
}
|
|
1078
|
+
return String(value).trim().toLowerCase();
|
|
1079
|
+
}
|
|
1080
|
+
buildExpiryEpoch(expireIn) {
|
|
1081
|
+
if (typeof expireIn === 'undefined' || expireIn === null || expireIn === '') {
|
|
1082
|
+
return undefined;
|
|
1083
|
+
}
|
|
1084
|
+
if (typeof expireIn === 'number' && Number.isFinite(expireIn) && expireIn > 0) {
|
|
1085
|
+
return Math.floor(Date.now() / 1000) + Math.floor(expireIn);
|
|
1086
|
+
}
|
|
1087
|
+
const raw = String(expireIn).trim().toLowerCase().replace(/\s+/g, '');
|
|
1088
|
+
const parsed = raw.match(/^(\d+)(y|w|d|hr|h|mn|min|m|s)$/);
|
|
1089
|
+
if (!parsed) {
|
|
1090
|
+
return undefined;
|
|
1091
|
+
}
|
|
1092
|
+
const value = Number(parsed[1]);
|
|
1093
|
+
const unit = parsed[2];
|
|
1094
|
+
const toSeconds = {
|
|
1095
|
+
y: 31556926,
|
|
1096
|
+
w: 604800,
|
|
1097
|
+
d: 86400,
|
|
1098
|
+
hr: 3600,
|
|
1099
|
+
h: 3600,
|
|
1100
|
+
mn: 60,
|
|
1101
|
+
min: 60,
|
|
1102
|
+
m: 60,
|
|
1103
|
+
s: 1,
|
|
1104
|
+
};
|
|
1105
|
+
const seconds = toSeconds[unit];
|
|
1106
|
+
if (!seconds) {
|
|
1107
|
+
return undefined;
|
|
1108
|
+
}
|
|
1109
|
+
return Math.floor(Date.now() / 1000) + value * seconds;
|
|
1110
|
+
}
|
|
1111
|
+
resetPathStateIfExpired(pathState) {
|
|
1112
|
+
const now = Math.floor(Date.now() / 1000);
|
|
1113
|
+
if (pathState.watchExpiresAt && pathState.watchExpiresAt <= now) {
|
|
1114
|
+
pathState.consumedValuesByKey = {};
|
|
1115
|
+
pathState.watchExpiresAt = undefined;
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
cleanupExpiredEntries() {
|
|
1119
|
+
const now = Math.floor(Date.now() / 1000);
|
|
1120
|
+
Object.keys(this.state.paths).forEach((pathKey) => {
|
|
1121
|
+
const pathState = this.state.paths[pathKey];
|
|
1122
|
+
if (pathState.watchExpiresAt && pathState.watchExpiresAt <= now) {
|
|
1123
|
+
pathState.consumedValuesByKey = {};
|
|
1124
|
+
pathState.watchExpiresAt = undefined;
|
|
1125
|
+
}
|
|
1126
|
+
const hasBaseline = Boolean(pathState.baselineQuery && Object.keys(pathState.baselineQuery).length > 0);
|
|
1127
|
+
const hasTrackedValues = Object.keys(pathState.consumedValuesByKey).some((key) => (pathState.consumedValuesByKey[key] || []).length > 0);
|
|
1128
|
+
const isNoQueryTracked = Boolean(pathState.trackedAt);
|
|
1129
|
+
if (!hasBaseline && !hasTrackedValues && !isNoQueryTracked) {
|
|
1130
|
+
delete this.state.paths[pathKey];
|
|
1131
|
+
}
|
|
1132
|
+
});
|
|
1133
|
+
}
|
|
1134
|
+
ensurePathState(pathKey) {
|
|
1135
|
+
if (!this.state.paths[pathKey]) {
|
|
1136
|
+
this.state.paths[pathKey] = {
|
|
1137
|
+
consumedValuesByKey: {},
|
|
1138
|
+
};
|
|
1139
|
+
}
|
|
1140
|
+
return this.state.paths[pathKey];
|
|
1141
|
+
}
|
|
1142
|
+
ensureStateRestored() {
|
|
1143
|
+
if (this.stateRestored) {
|
|
1144
|
+
return;
|
|
1145
|
+
}
|
|
1146
|
+
this.stateRestored = true;
|
|
1147
|
+
this.initializeTrackingForSession();
|
|
1148
|
+
this.restoreState();
|
|
1149
|
+
}
|
|
1150
|
+
initializeTrackingForSession() {
|
|
1151
|
+
if (!this.hasSessionStorage()) {
|
|
1152
|
+
return;
|
|
1153
|
+
}
|
|
1154
|
+
const initialized = sessionStorage.getItem(TRACKER_SESSION_INIT_KEY);
|
|
1155
|
+
if (initialized === '1') {
|
|
1156
|
+
return;
|
|
1157
|
+
}
|
|
1158
|
+
this.clearTracking();
|
|
1159
|
+
sessionStorage.setItem(TRACKER_SESSION_INIT_KEY, '1');
|
|
1160
|
+
}
|
|
1161
|
+
restoreState() {
|
|
1162
|
+
this.localStorageManager.store$(TRACKER_STORE_NAME)
|
|
1163
|
+
.pipe(take(1))
|
|
1164
|
+
.subscribe({
|
|
1165
|
+
next: (storedState) => {
|
|
1166
|
+
// Don't overwrite in-memory state if we've already tracked paths
|
|
1167
|
+
if (Object.keys(this.state.paths).length > 0) {
|
|
1168
|
+
return;
|
|
1169
|
+
}
|
|
1170
|
+
if (this.isTrackerState(storedState)) {
|
|
1171
|
+
this.state = QueryTrackerStateModel.adapt(storedState);
|
|
1172
|
+
this.cleanupExpiredEntries();
|
|
1173
|
+
return;
|
|
1174
|
+
}
|
|
1175
|
+
this.state = { paths: {} };
|
|
1176
|
+
},
|
|
1177
|
+
error: () => {
|
|
1178
|
+
if (Object.keys(this.state.paths).length === 0) {
|
|
1179
|
+
this.state = { paths: {} };
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
});
|
|
1183
|
+
}
|
|
1184
|
+
persistState() {
|
|
1185
|
+
this.localStorageManager.storeExists$(TRACKER_STORE_NAME)
|
|
1186
|
+
.pipe(take(1))
|
|
1187
|
+
.subscribe((exists) => {
|
|
1188
|
+
if (exists) {
|
|
1189
|
+
this.localStorageManager.updateStore({ name: TRACKER_STORE_NAME, data: this.state });
|
|
1190
|
+
return;
|
|
1191
|
+
}
|
|
1192
|
+
this.localStorageManager.createStore({
|
|
1193
|
+
name: TRACKER_STORE_NAME,
|
|
1194
|
+
data: this.state,
|
|
1195
|
+
options: DEFAULT_TRACKER_OPTIONS,
|
|
1196
|
+
});
|
|
1197
|
+
});
|
|
1198
|
+
}
|
|
1199
|
+
stringifyQuery(query) {
|
|
1200
|
+
return JSON.stringify(Object.keys(query)
|
|
1201
|
+
.sort()
|
|
1202
|
+
.reduce((acc, key) => {
|
|
1203
|
+
acc[key] = query[key];
|
|
1204
|
+
return acc;
|
|
1205
|
+
}, {}));
|
|
1206
|
+
}
|
|
1207
|
+
isTrackerState(value) {
|
|
1208
|
+
return this.isPlainObject(value) && this.isPlainObject(value.paths);
|
|
1209
|
+
}
|
|
1210
|
+
isPlainObject(value) {
|
|
1211
|
+
return Object.prototype.toString.call(value) === '[object Object]';
|
|
1212
|
+
}
|
|
1213
|
+
hasSessionStorage() {
|
|
1214
|
+
try {
|
|
1215
|
+
return typeof sessionStorage !== 'undefined';
|
|
1216
|
+
}
|
|
1217
|
+
catch {
|
|
1218
|
+
return false;
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
1221
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: QueryParamsTrackerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1222
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: QueryParamsTrackerService, providedIn: 'root' }); }
|
|
1223
|
+
}
|
|
1224
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: QueryParamsTrackerService, decorators: [{
|
|
1225
|
+
type: Injectable,
|
|
1226
|
+
args: [{
|
|
1227
|
+
providedIn: 'root'
|
|
1228
|
+
}]
|
|
1229
|
+
}] });
|
|
1230
|
+
|
|
1231
|
+
class NormalizedRequestOptionsModel {
|
|
1232
|
+
constructor(pathKey = '', query = {}, hasQuery = false) {
|
|
1233
|
+
this.pathKey = pathKey;
|
|
1234
|
+
this.query = query;
|
|
1235
|
+
this.hasQuery = hasQuery;
|
|
1236
|
+
}
|
|
1237
|
+
static adapt(item) {
|
|
1238
|
+
return new NormalizedRequestOptionsModel(item?.pathKey, item?.query, item?.hasQuery);
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
class QueryParamsTrackerOptionsModel {
|
|
1243
|
+
constructor(mode, watchParams, watchExpiresAt, watchParamsExpire) {
|
|
1244
|
+
this.mode = mode;
|
|
1245
|
+
this.watchParams = watchParams;
|
|
1246
|
+
this.watchExpiresAt = watchExpiresAt;
|
|
1247
|
+
this.watchParamsExpire = watchParamsExpire;
|
|
1248
|
+
}
|
|
1249
|
+
static adapt(item) {
|
|
1250
|
+
return new QueryParamsTrackerOptionsModel(item?.mode, Array.isArray(item?.watchParams) ? item.watchParams : [], item?.watchExpiresAt, item?.watchParamsExpire);
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
|
|
878
1254
|
/**
|
|
879
1255
|
* Stateful processor for managing streaming data and events
|
|
880
1256
|
*/
|
|
@@ -883,28 +1259,66 @@ class StreamingProcessor {
|
|
|
883
1259
|
this.buffer = '';
|
|
884
1260
|
this.contentType = '';
|
|
885
1261
|
this.maxBufferSize = 10 * 1024 * 1024; // 10MB limit
|
|
1262
|
+
this.lastParsedLength = 0; // Track parsed position to avoid duplicates
|
|
886
1263
|
this.streamConfig = config || { streamType: StreamType.AI_STREAMING };
|
|
887
1264
|
}
|
|
888
1265
|
/**
|
|
889
|
-
* Process HTTP events and extract streaming data
|
|
1266
|
+
* Process HTTP events and extract streaming data with progress
|
|
890
1267
|
*/
|
|
891
1268
|
process(event) {
|
|
892
1269
|
switch (event.type) {
|
|
893
1270
|
case HttpEventType.ResponseHeader:
|
|
894
1271
|
this.contentType = event.headers?.get('content-type') || '';
|
|
1272
|
+
// Read total from response header if configured
|
|
1273
|
+
if (this.streamConfig.totalHeader && event.headers) {
|
|
1274
|
+
const totalVal = event.headers.get(this.streamConfig.totalHeader);
|
|
1275
|
+
if (totalVal !== undefined && totalVal !== null) {
|
|
1276
|
+
const parsed = parseInt(totalVal, 10);
|
|
1277
|
+
if (!isNaN(parsed)) {
|
|
1278
|
+
this.totalFromHeader = parsed;
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
895
1282
|
return null;
|
|
896
1283
|
case HttpEventType.DownloadProgress:
|
|
897
1284
|
if (event.partialText) {
|
|
898
1285
|
this.appendToBuffer(event.partialText);
|
|
899
1286
|
const parsedData = this.parseBuffer();
|
|
900
|
-
return
|
|
1287
|
+
// Only return NEW items since last parse (progressive updates)
|
|
1288
|
+
const newItems = parsedData.slice(this.lastParsedLength);
|
|
1289
|
+
this.lastParsedLength = parsedData.length;
|
|
1290
|
+
// Calculate progress
|
|
1291
|
+
const progress = {
|
|
1292
|
+
received: this.lastParsedLength,
|
|
1293
|
+
total: this.totalFromHeader,
|
|
1294
|
+
percent: this.totalFromHeader
|
|
1295
|
+
? Math.round((this.lastParsedLength / this.totalFromHeader) * 100)
|
|
1296
|
+
: 0,
|
|
1297
|
+
stage: 'streaming'
|
|
1298
|
+
};
|
|
1299
|
+
return {
|
|
1300
|
+
data: newItems.length > 0 ? newItems : [],
|
|
1301
|
+
progress
|
|
1302
|
+
};
|
|
901
1303
|
}
|
|
902
1304
|
return null;
|
|
903
1305
|
case HttpEventType.Response:
|
|
904
1306
|
if (event.body) {
|
|
905
1307
|
this.appendToBuffer(event.body);
|
|
906
1308
|
const parsedData = this.parseBuffer();
|
|
907
|
-
|
|
1309
|
+
// Calculate final progress
|
|
1310
|
+
const progress = {
|
|
1311
|
+
received: parsedData.length,
|
|
1312
|
+
total: this.totalFromHeader,
|
|
1313
|
+
percent: this.totalFromHeader
|
|
1314
|
+
? Math.round((parsedData.length / this.totalFromHeader) * 100)
|
|
1315
|
+
: 100,
|
|
1316
|
+
stage: 'complete'
|
|
1317
|
+
};
|
|
1318
|
+
return {
|
|
1319
|
+
data: parsedData.length > 0 ? parsedData : [],
|
|
1320
|
+
progress
|
|
1321
|
+
};
|
|
908
1322
|
}
|
|
909
1323
|
return null;
|
|
910
1324
|
default:
|
|
@@ -943,6 +1357,8 @@ class StreamingProcessor {
|
|
|
943
1357
|
reset() {
|
|
944
1358
|
this.buffer = '';
|
|
945
1359
|
this.contentType = '';
|
|
1360
|
+
this.lastParsedLength = 0;
|
|
1361
|
+
this.totalFromHeader = undefined;
|
|
946
1362
|
}
|
|
947
1363
|
/**
|
|
948
1364
|
* Update configuration
|
|
@@ -2793,19 +3209,6 @@ class UploadValidationErrorModel {
|
|
|
2793
3209
|
}
|
|
2794
3210
|
}
|
|
2795
3211
|
|
|
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
3212
|
class RequestService extends WebsocketService {
|
|
2810
3213
|
constructor() {
|
|
2811
3214
|
super(...arguments);
|
|
@@ -2816,6 +3219,13 @@ class RequestService extends WebsocketService {
|
|
|
2816
3219
|
this.isPending$ = this.isPending.asObservable();
|
|
2817
3220
|
this.progress = new BehaviorSubject(0);
|
|
2818
3221
|
this.progress$ = this.progress.asObservable();
|
|
3222
|
+
this.streamProgress = new BehaviorSubject({
|
|
3223
|
+
received: 0,
|
|
3224
|
+
total: undefined,
|
|
3225
|
+
percent: 0,
|
|
3226
|
+
stage: 'connecting'
|
|
3227
|
+
});
|
|
3228
|
+
this.streamProgress$ = this.streamProgress.asObservable();
|
|
2819
3229
|
}
|
|
2820
3230
|
// Implementation
|
|
2821
3231
|
getRecordRequest(options) {
|
|
@@ -2884,7 +3294,12 @@ class RequestService extends WebsocketService {
|
|
|
2884
3294
|
}
|
|
2885
3295
|
requestStreaming(options) {
|
|
2886
3296
|
return (source$) => {
|
|
2887
|
-
return source$.pipe(
|
|
3297
|
+
return source$.pipe(tap(output => {
|
|
3298
|
+
// Update progress from stream output
|
|
3299
|
+
this.progress.next(output.progress.received);
|
|
3300
|
+
this.streamProgress.next(output.progress);
|
|
3301
|
+
}), map(output => {
|
|
3302
|
+
const data = output.data;
|
|
2888
3303
|
if (!data || (Array.isArray(data) && data.length === 0)) {
|
|
2889
3304
|
return data;
|
|
2890
3305
|
}
|
|
@@ -3586,6 +4001,7 @@ class HTTPManagerService extends RequestService {
|
|
|
3586
4001
|
this.data$ = this.data.asObservable();
|
|
3587
4002
|
this.polling$ = new Subject();
|
|
3588
4003
|
this.config = ApiRequest.adapt();
|
|
4004
|
+
this.streamProgress$ = this.streamProgress.asObservable();
|
|
3589
4005
|
this.config = (configOptions) ? ApiRequest.adapt(configOptions.httpRequestOptions) : this.config;
|
|
3590
4006
|
}
|
|
3591
4007
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -3722,6 +4138,12 @@ class HTTPManagerService extends RequestService {
|
|
|
3722
4138
|
getRequest(options, params) {
|
|
3723
4139
|
this.isPending.next(true);
|
|
3724
4140
|
this.data.next(null);
|
|
4141
|
+
this.streamProgress.next({
|
|
4142
|
+
received: 0,
|
|
4143
|
+
total: undefined,
|
|
4144
|
+
percent: 0,
|
|
4145
|
+
stage: 'connecting'
|
|
4146
|
+
});
|
|
3725
4147
|
const updatedOptions = this.defineReqOptions(options, params);
|
|
3726
4148
|
const func = this.getRecordRequest;
|
|
3727
4149
|
const requests = this.createRequest(func, updatedOptions);
|
|
@@ -3730,9 +4152,19 @@ class HTTPManagerService extends RequestService {
|
|
|
3730
4152
|
this.data.next(data);
|
|
3731
4153
|
if (updatedOptions.displaySuccess)
|
|
3732
4154
|
this.handleSuccessWithSnackBar(updatedOptions.successMessage);
|
|
3733
|
-
})
|
|
4155
|
+
}), finalize(() => {
|
|
4156
|
+
this.streamProgress.next({
|
|
4157
|
+
...this.streamProgress.value,
|
|
4158
|
+
stage: 'complete'
|
|
4159
|
+
});
|
|
4160
|
+
this.isPending.next(false);
|
|
4161
|
+
}), catchError((err) => {
|
|
3734
4162
|
if (updatedOptions.displayError)
|
|
3735
4163
|
this.handleErrorWithSnackBar(err, updatedOptions.errorMessage || err?.message);
|
|
4164
|
+
this.streamProgress.next({
|
|
4165
|
+
...this.streamProgress.value,
|
|
4166
|
+
stage: 'error'
|
|
4167
|
+
});
|
|
3736
4168
|
this.isPending.next(false);
|
|
3737
4169
|
return this.handleError(err);
|
|
3738
4170
|
}));
|
|
@@ -4154,7 +4586,8 @@ class RequestSignalsService extends WebsocketService {
|
|
|
4154
4586
|
}
|
|
4155
4587
|
requestStreamingOperator(options) {
|
|
4156
4588
|
return (source$) => {
|
|
4157
|
-
return source$.pipe(map(
|
|
4589
|
+
return source$.pipe(map(output => {
|
|
4590
|
+
const data = output.data;
|
|
4158
4591
|
if (!data || (Array.isArray(data) && data.length === 0)) {
|
|
4159
4592
|
return data;
|
|
4160
4593
|
}
|
|
@@ -4891,12 +5324,15 @@ class ApiRequest {
|
|
|
4891
5324
|
}
|
|
4892
5325
|
|
|
4893
5326
|
class RequestOptions {
|
|
4894
|
-
constructor(path = [], headers = {}) {
|
|
5327
|
+
constructor(path = [], headers = {}, forceRefresh, watchParams, watchExpiresAt) {
|
|
4895
5328
|
this.path = path;
|
|
4896
5329
|
this.headers = headers;
|
|
5330
|
+
this.forceRefresh = forceRefresh;
|
|
5331
|
+
this.watchParams = watchParams;
|
|
5332
|
+
this.watchExpiresAt = watchExpiresAt;
|
|
4897
5333
|
}
|
|
4898
5334
|
static adapt(item) {
|
|
4899
|
-
return new RequestOptions(item?.path, item?.headers);
|
|
5335
|
+
return new RequestOptions(item?.path, item?.headers, item?.forceRefresh, Array.isArray(item?.watchParams) ? item.watchParams : [], item?.watchExpiresAt);
|
|
4900
5336
|
}
|
|
4901
5337
|
}
|
|
4902
5338
|
|
|
@@ -5691,8 +6127,8 @@ class DbService extends Dexie {
|
|
|
5691
6127
|
console.warn('Database upgrade blocked! Please close other tabs/windows of this app.');
|
|
5692
6128
|
});
|
|
5693
6129
|
this.on('versionchange', () => {
|
|
5694
|
-
console.warn('Database version changed in another tab.
|
|
5695
|
-
|
|
6130
|
+
console.warn('Database version changed in another tab. Closing current database connection to allow upgrade.');
|
|
6131
|
+
this.close();
|
|
5696
6132
|
});
|
|
5697
6133
|
this.dbReady = this.init();
|
|
5698
6134
|
}
|
|
@@ -5772,9 +6208,12 @@ class DbService extends Dexie {
|
|
|
5772
6208
|
await this.dbReady;
|
|
5773
6209
|
const safeTableName = this.cleanTableName(tableName);
|
|
5774
6210
|
const safeSchema = schema.trim();
|
|
5775
|
-
if (this.tableExists(safeTableName))
|
|
5776
|
-
return;
|
|
5777
6211
|
const currentSchema = this.getCurrentSchema();
|
|
6212
|
+
const existingSchema = currentSchema[safeTableName]?.trim();
|
|
6213
|
+
// No-op only when table already exists and schema is identical.
|
|
6214
|
+
if (this.tableExists(safeTableName) && existingSchema === safeSchema) {
|
|
6215
|
+
return;
|
|
6216
|
+
}
|
|
5778
6217
|
console.log('Current Schema before update:', currentSchema);
|
|
5779
6218
|
currentSchema[safeTableName] = safeSchema;
|
|
5780
6219
|
const nextVersion = this.verno + 1;
|
|
@@ -5788,6 +6227,7 @@ class DbService extends Dexie {
|
|
|
5788
6227
|
const created = this.tables.some(t => t.name === safeTableName);
|
|
5789
6228
|
if (!created) {
|
|
5790
6229
|
console.error(`CRITICAL: Table ${safeTableName} was NOT created after upgrade! Tables found:`, this.tables.map(t => t.name));
|
|
6230
|
+
throw new Error(`Table '${safeTableName}' was not created after schema upgrade`);
|
|
5791
6231
|
}
|
|
5792
6232
|
else {
|
|
5793
6233
|
console.log(`Database opened successfully version ${this.verno}. Table ${safeTableName} verified.`);
|
|
@@ -5795,15 +6235,24 @@ class DbService extends Dexie {
|
|
|
5795
6235
|
}
|
|
5796
6236
|
catch (err) {
|
|
5797
6237
|
console.error('Error opening database after schema update:', err);
|
|
6238
|
+
throw err;
|
|
5798
6239
|
}
|
|
5799
6240
|
}
|
|
5800
6241
|
async DBOpened() {
|
|
6242
|
+
try {
|
|
6243
|
+
await this.dbReady;
|
|
6244
|
+
}
|
|
6245
|
+
catch (err) {
|
|
6246
|
+
console.error('DBOpened: init failed', err);
|
|
6247
|
+
return false;
|
|
6248
|
+
}
|
|
5801
6249
|
if (!this.isOpen()) {
|
|
5802
6250
|
try {
|
|
5803
6251
|
await this.open();
|
|
5804
6252
|
return true;
|
|
5805
6253
|
}
|
|
5806
6254
|
catch (err) {
|
|
6255
|
+
console.error('DBOpened: open failed', err);
|
|
5807
6256
|
return false;
|
|
5808
6257
|
}
|
|
5809
6258
|
}
|
|
@@ -5898,7 +6347,11 @@ class DatabaseManagerService extends DbService {
|
|
|
5898
6347
|
}));
|
|
5899
6348
|
}
|
|
5900
6349
|
createDatabaseTable(tableDef) {
|
|
5901
|
-
|
|
6350
|
+
const tableName = this.cleanTableName(tableDef.table);
|
|
6351
|
+
return from(this.createTable(tableDef.table, tableDef.schema)).pipe(switchMap(() => from(this.DBOpened())), map((opened) => opened && this.tableExists(tableName)), catchError((error) => {
|
|
6352
|
+
console.error(`createDatabaseTable: failed for table '${tableName}'`, error);
|
|
6353
|
+
return of(false);
|
|
6354
|
+
}));
|
|
5902
6355
|
}
|
|
5903
6356
|
updateDatabaseTableSchema(tableDef) {
|
|
5904
6357
|
return from(this.createTable(tableDef.table, tableDef.schema)).pipe(switchMap(() => this.getDatabaseTable(tableDef.table)));
|
|
@@ -5945,26 +6398,23 @@ class DatabaseManagerService extends DbService {
|
|
|
5945
6398
|
}
|
|
5946
6399
|
createTableRecords(table, records) {
|
|
5947
6400
|
const tableName = this.cleanTableName(table);
|
|
5948
|
-
return from(this.DBOpened()).pipe(switchMap(() => {
|
|
6401
|
+
return from(this.DBOpened()).pipe(switchMap((opened) => {
|
|
6402
|
+
if (!opened) {
|
|
6403
|
+
console.error(`createTableRecords: DB not open. Cannot write to '${tableName}'.`);
|
|
6404
|
+
return EMPTY;
|
|
6405
|
+
}
|
|
5949
6406
|
if (!this.tables.some(t => t.name === tableName)) {
|
|
5950
6407
|
console.error(`createTableRecords: Table '${tableName}' does not exist in DB version ${this.verno}. Available:`, this.tables.map(t => t.name));
|
|
5951
6408
|
return EMPTY;
|
|
5952
6409
|
}
|
|
5953
6410
|
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
|
-
];
|
|
6411
|
+
// Keep full object payload; Dexie stores non-indexed properties as well.
|
|
5960
6412
|
const insertRecords = records.map((record) => {
|
|
5961
|
-
const
|
|
5962
|
-
|
|
5963
|
-
|
|
5964
|
-
|
|
5965
|
-
|
|
5966
|
-
});
|
|
5967
|
-
return objectFromSchema;
|
|
6413
|
+
const payload = { ...(record || {}) };
|
|
6414
|
+
if (payload.id === undefined || payload.id === null || payload.id === '') {
|
|
6415
|
+
delete payload.id;
|
|
6416
|
+
}
|
|
6417
|
+
return payload;
|
|
5968
6418
|
});
|
|
5969
6419
|
console.log(`createTableRecords: Bulk putting ${insertRecords.length} records into ${tableName}`);
|
|
5970
6420
|
return from(tableInstance.bulkPut(insertRecords)).pipe(map(() => insertRecords));
|
|
@@ -5978,17 +6428,14 @@ class DatabaseManagerService extends DbService {
|
|
|
5978
6428
|
updateTableRecords(table, records) {
|
|
5979
6429
|
const tableName = this.cleanTableName(table);
|
|
5980
6430
|
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
|
-
}));
|
|
6431
|
+
const insertRecords = records.map((record) => {
|
|
6432
|
+
const payload = { ...(record || {}) };
|
|
6433
|
+
if (payload.id === undefined || payload.id === null || payload.id === '') {
|
|
6434
|
+
delete payload.id;
|
|
6435
|
+
}
|
|
6436
|
+
return payload;
|
|
6437
|
+
});
|
|
6438
|
+
return from(tableData.bulkPut(insertRecords)).pipe(map(() => insertRecords));
|
|
5992
6439
|
}));
|
|
5993
6440
|
}
|
|
5994
6441
|
deleteTableRecord(table, id) {
|
|
@@ -6090,6 +6537,7 @@ class HTTPManagerStateService extends ComponentStore {
|
|
|
6090
6537
|
this.httpManagerService = inject(HTTPManagerService);
|
|
6091
6538
|
this.dbManagerService = inject(DatabaseManagerService);
|
|
6092
6539
|
this.localStorageManagerService = inject(LocalStorageManagerService);
|
|
6540
|
+
this.queryParamsTrackerService = inject(QueryParamsTrackerService);
|
|
6093
6541
|
this.utils = inject(UtilsService);
|
|
6094
6542
|
this.logger = inject(LoggerService);
|
|
6095
6543
|
this.error$ = this.httpManagerService.error$;
|
|
@@ -6106,6 +6554,16 @@ class HTTPManagerStateService extends ComponentStore {
|
|
|
6106
6554
|
this.hasDatabase = false;
|
|
6107
6555
|
this.streamedResponse = [];
|
|
6108
6556
|
this.shouldRetry = true;
|
|
6557
|
+
this.volatileHeaders = new Set([
|
|
6558
|
+
'authorization',
|
|
6559
|
+
'x-request-id',
|
|
6560
|
+
'x-correlation-id',
|
|
6561
|
+
'x-trace-id',
|
|
6562
|
+
'x-amzn-trace-id',
|
|
6563
|
+
'date',
|
|
6564
|
+
'if-none-match'
|
|
6565
|
+
]);
|
|
6566
|
+
this.requestSignatureCache = {};
|
|
6109
6567
|
this.wsRetryAttempts = new BehaviorSubject(0);
|
|
6110
6568
|
this.wsRetryAttempts$ = this.wsRetryAttempts.asObservable();
|
|
6111
6569
|
this.messages = new BehaviorSubject([]);
|
|
@@ -6375,22 +6833,25 @@ class HTTPManagerStateService extends ComponentStore {
|
|
|
6375
6833
|
}
|
|
6376
6834
|
}))))));
|
|
6377
6835
|
this.initDBStorage = this.effect((trigger$) => trigger$.pipe(tap(() => {
|
|
6836
|
+
console.log('[initDBStorage effect] Triggered, checking conditions:', {
|
|
6837
|
+
dataType: this.dataType,
|
|
6838
|
+
isARRAY: this.dataType === DataType.ARRAY,
|
|
6839
|
+
hasAdapter: !!this.apiOptions?.adapter,
|
|
6840
|
+
hasTable: !!this.databaseOptions?.table,
|
|
6841
|
+
tableValue: this.databaseOptions?.table
|
|
6842
|
+
});
|
|
6378
6843
|
if (this.dataType !== DataType.ARRAY)
|
|
6379
6844
|
console.warn('Database storage requires dataType to be ARRAY');
|
|
6380
6845
|
if (!this.apiOptions.adapter)
|
|
6381
|
-
console.warn('Database storage
|
|
6846
|
+
console.warn('Database storage adapter missing, using minimal or inferred schema');
|
|
6382
6847
|
if (this.databaseOptions && this.databaseOptions?.table === '')
|
|
6383
6848
|
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
|
-
}
|
|
6849
|
+
}), filter(() => {
|
|
6850
|
+
const shouldProceed = this.dataType === DataType.ARRAY && !!this.databaseOptions?.table;
|
|
6851
|
+
console.log('[initDBStorage effect] Filter result:', shouldProceed);
|
|
6852
|
+
return shouldProceed;
|
|
6853
|
+
}), switchMap(() => {
|
|
6854
|
+
const schema = this.buildSchemaFromAdapter();
|
|
6394
6855
|
const tableDef = TableSchemaDef.adapt({
|
|
6395
6856
|
table: this.databaseOptions?.table,
|
|
6396
6857
|
schema: schema
|
|
@@ -6479,6 +6940,7 @@ class HTTPManagerStateService extends ComponentStore {
|
|
|
6479
6940
|
}
|
|
6480
6941
|
}), concatMap(() => {
|
|
6481
6942
|
if (this.hasDatabase && this.databaseOptions?.table) {
|
|
6943
|
+
this.clearRequestCacheMetadata(this.databaseOptions.table);
|
|
6482
6944
|
const currentData = this.get()?.data;
|
|
6483
6945
|
const idsToDelete = Array.isArray(currentData) ? currentData.map((r) => r.id) : [];
|
|
6484
6946
|
if (idsToDelete.length > 0) {
|
|
@@ -6493,35 +6955,81 @@ class HTTPManagerStateService extends ComponentStore {
|
|
|
6493
6955
|
this.fetchRecords = (options) => this.effect(() => of(RequestOptions.adapt(options)).pipe(switchMap(() => {
|
|
6494
6956
|
this.streamedResponse = [];
|
|
6495
6957
|
const requestOptions = this.updateRequestOptions(options?.headers);
|
|
6958
|
+
const effectiveParams = this.getEffectiveParams(options?.path);
|
|
6959
|
+
const requestSignature = this.buildRequestSignature('GET', requestOptions, effectiveParams);
|
|
6496
6960
|
const fetchFromAPI = () => {
|
|
6497
|
-
|
|
6498
|
-
|
|
6961
|
+
if (this.hasDatabase && this.databaseOptions?.table) {
|
|
6962
|
+
this.setCachedRequestSignature(this.databaseOptions.table, 'GET', requestSignature);
|
|
6963
|
+
}
|
|
6964
|
+
return this.httpManagerService.getRequest(requestOptions, effectiveParams).pipe(tap((data) => {
|
|
6965
|
+
// Extract array from paginated response if needed
|
|
6966
|
+
const arrayData = (data?.results && Array.isArray(data.results)) ? data.results : data;
|
|
6967
|
+
data = (!arrayData) ? (this.dataType === DataType.ARRAY) ? [] : {} : arrayData;
|
|
6499
6968
|
this.setData$(data);
|
|
6500
6969
|
}), concatMap((data) => {
|
|
6501
|
-
|
|
6970
|
+
// Extract array from paginated response for database storage
|
|
6971
|
+
const dbData = (data?.results && Array.isArray(data.results)) ? data.results : data;
|
|
6972
|
+
if (this.hasDatabase && this.databaseOptions?.table && Array.isArray(dbData) && dbData.length > 0) {
|
|
6973
|
+
const tableName = this.databaseOptions.table;
|
|
6502
6974
|
this.localStorageManagerService.updateStore({
|
|
6503
|
-
name:
|
|
6975
|
+
name: tableName,
|
|
6504
6976
|
data: { ...this.databaseOptions, ...{ expires: this.utils.expires(this.databaseOptions.expiresIn) } }
|
|
6505
6977
|
});
|
|
6506
|
-
|
|
6978
|
+
const schema = this.buildSchemaFromSample(dbData[0]);
|
|
6979
|
+
const tableDef = TableSchemaDef.adapt({ table: tableName, schema });
|
|
6980
|
+
const schemaSignature = this.buildSchemaSignature(schema);
|
|
6981
|
+
// Always ensure table exists immediately before writing to avoid stale schema/store races.
|
|
6982
|
+
return this.localStorageManagerService.store$(tableName).pipe(take(1), switchMap((storeData) => {
|
|
6983
|
+
const storedSchemaSignature = this.getStoredSchemaSignature(storeData);
|
|
6984
|
+
const schemaChanged = !!storedSchemaSignature && storedSchemaSignature !== schemaSignature;
|
|
6985
|
+
const ensureTable$ = schemaChanged
|
|
6986
|
+
? this.dbManagerService.clearTable(tableName).pipe(switchMap(() => this.dbManagerService.createDatabaseTable(tableDef)))
|
|
6987
|
+
: this.dbManagerService.createDatabaseTable(tableDef);
|
|
6988
|
+
return ensureTable$.pipe(switchMap((created) => {
|
|
6989
|
+
if (!created) {
|
|
6990
|
+
console.warn('[DB STORAGE] Table create/open not ready, skipping DB write for this payload:', { table: tableName });
|
|
6991
|
+
return of(data);
|
|
6992
|
+
}
|
|
6993
|
+
return this.dbManagerService.createTableRecords(tableName, dbData).pipe(tap(() => this.saveRequestCacheMetadata(tableName, 'GET', requestSignature, schemaSignature, options)));
|
|
6994
|
+
}));
|
|
6995
|
+
}), catchError((error) => {
|
|
6996
|
+
console.error('[DB STORAGE] Failed to ensure table and write records:', { table: tableName, schema, error });
|
|
6997
|
+
return of(data);
|
|
6998
|
+
}));
|
|
6507
6999
|
}
|
|
6508
7000
|
return of(data);
|
|
6509
7001
|
}));
|
|
6510
7002
|
};
|
|
7003
|
+
console.log('[DB STORAGE] Checking database storage:', {
|
|
7004
|
+
hasDatabase: this.hasDatabase,
|
|
7005
|
+
table: this.databaseOptions?.table,
|
|
7006
|
+
databaseOptions: this.databaseOptions
|
|
7007
|
+
});
|
|
6511
7008
|
if (this.hasDatabase && this.databaseOptions?.table) {
|
|
6512
7009
|
return this.dbManagerService.databaseExists().pipe(switchMap((dbExists) => {
|
|
6513
7010
|
if (!dbExists) {
|
|
6514
7011
|
const initObs = this.initDBStorageAsync();
|
|
6515
|
-
return initObs.pipe(switchMap(() => fetchFromAPI()))
|
|
7012
|
+
return initObs.pipe(switchMap(() => fetchFromAPI()), catchError((error) => {
|
|
7013
|
+
console.warn('[DB STORAGE] initDBStorageAsync failed when DB did not exist, continuing with API:', error);
|
|
7014
|
+
return fetchFromAPI();
|
|
7015
|
+
}));
|
|
6516
7016
|
}
|
|
6517
7017
|
return this.dbManagerService.hasDatabaseTable(this.databaseOptions.table).pipe(switchMap((tableExists) => {
|
|
6518
7018
|
if (!tableExists) {
|
|
6519
7019
|
const initObs = this.initDBStorageAsync();
|
|
6520
|
-
return initObs.pipe(switchMap(() => fetchFromAPI()))
|
|
7020
|
+
return initObs.pipe(switchMap(() => fetchFromAPI()), catchError((error) => {
|
|
7021
|
+
console.warn('[DB STORAGE] initDBStorageAsync failed when table missing, continuing with API:', error);
|
|
7022
|
+
return fetchFromAPI();
|
|
7023
|
+
}));
|
|
6521
7024
|
}
|
|
6522
7025
|
return this.localStorageManagerService.store$(this.databaseOptions.table).pipe(take(1), switchMap((storeData) => {
|
|
7026
|
+
const forceRefresh = !!options?.forceRefresh;
|
|
7027
|
+
const storedSchemaSignature = this.getStoredSchemaSignature(storeData);
|
|
6523
7028
|
const expires = storeData?.expires || 0;
|
|
6524
7029
|
const hasExpired = expires > 0 && this.utils.hasExpired(expires);
|
|
7030
|
+
if (forceRefresh) {
|
|
7031
|
+
return fetchFromAPI();
|
|
7032
|
+
}
|
|
6525
7033
|
if (hasExpired) {
|
|
6526
7034
|
return this.dbManagerService.clearTable(this.databaseOptions.table).pipe(switchMap(() => fetchFromAPI()), tap(() => {
|
|
6527
7035
|
this.localStorageManagerService.updateStore({
|
|
@@ -6530,12 +7038,41 @@ class HTTPManagerStateService extends ComponentStore {
|
|
|
6530
7038
|
});
|
|
6531
7039
|
}));
|
|
6532
7040
|
}
|
|
7041
|
+
const expectedSchema = this.buildSchemaFromAdapter();
|
|
7042
|
+
const expectedSchemaSignature = this.buildSchemaSignature(expectedSchema);
|
|
7043
|
+
if (storedSchemaSignature && storedSchemaSignature !== expectedSchemaSignature) {
|
|
7044
|
+
const tableDef = TableSchemaDef.adapt({ table: this.databaseOptions.table, schema: expectedSchema });
|
|
7045
|
+
return this.dbManagerService.clearTable(this.databaseOptions.table).pipe(switchMap(() => this.dbManagerService.createDatabaseTable(tableDef)), switchMap(() => fetchFromAPI()));
|
|
7046
|
+
}
|
|
7047
|
+
const trackerAllowsRequest = this.queryParamsTrackerService.checkRequestOptions(this.resolvePath(effectiveParams), this.buildQueryTrackerOptions(options));
|
|
7048
|
+
const shouldMakeRequest = trackerAllowsRequest;
|
|
7049
|
+
if (shouldMakeRequest) {
|
|
7050
|
+
return fetchFromAPI();
|
|
7051
|
+
}
|
|
6533
7052
|
return this.dbManagerService.getTableRecords(this.databaseOptions.table).pipe(switchMap((dbData) => {
|
|
6534
7053
|
if (Array.isArray(dbData) && dbData.length > 0) {
|
|
6535
7054
|
this.setData$(dbData);
|
|
6536
7055
|
return of(dbData);
|
|
6537
7056
|
}
|
|
6538
|
-
|
|
7057
|
+
const currentStateData = this.get()?.data;
|
|
7058
|
+
if (Array.isArray(currentStateData) && currentStateData.length > 0) {
|
|
7059
|
+
return of(currentStateData);
|
|
7060
|
+
}
|
|
7061
|
+
if (currentStateData &&
|
|
7062
|
+
!Array.isArray(currentStateData) &&
|
|
7063
|
+
Object.keys(currentStateData).length > 0) {
|
|
7064
|
+
return of(currentStateData);
|
|
7065
|
+
}
|
|
7066
|
+
return of(this.dataType === DataType.ARRAY ? [] : {});
|
|
7067
|
+
}), catchError((error) => {
|
|
7068
|
+
const tableName = this.databaseOptions.table;
|
|
7069
|
+
console.warn('[DB STORAGE] getTableRecords failed, recreating table and falling back to API:', { table: tableName, error });
|
|
7070
|
+
const schema = this.buildSchemaFromAdapter();
|
|
7071
|
+
const tableDef = TableSchemaDef.adapt({ table: tableName, schema });
|
|
7072
|
+
return this.dbManagerService.createDatabaseTable(tableDef).pipe(switchMap(() => fetchFromAPI()), catchError((recreateError) => {
|
|
7073
|
+
console.error('[DB STORAGE] Failed to recreate table after read error, continuing with API only:', recreateError);
|
|
7074
|
+
return fetchFromAPI();
|
|
7075
|
+
}));
|
|
6539
7076
|
}));
|
|
6540
7077
|
}));
|
|
6541
7078
|
}));
|
|
@@ -6659,7 +7196,7 @@ class HTTPManagerStateService extends ComponentStore {
|
|
|
6659
7196
|
if (res.length > 0)
|
|
6660
7197
|
this.setData$(res);
|
|
6661
7198
|
this.streamedResponse = res;
|
|
6662
|
-
}), scan((acc, res) => {
|
|
7199
|
+
}), concatMap((res) => this.persistStreamDataToDb(res, options)), scan((acc, res) => {
|
|
6663
7200
|
const previous = acc.current;
|
|
6664
7201
|
const current = res;
|
|
6665
7202
|
return { previous, current };
|
|
@@ -6679,8 +7216,70 @@ class HTTPManagerStateService extends ComponentStore {
|
|
|
6679
7216
|
}), switchMap((options) => {
|
|
6680
7217
|
const requestOptions = this.updateRequestOptions(options?.headers);
|
|
6681
7218
|
requestOptions.stream = true;
|
|
7219
|
+
const effectiveParams = this.getEffectiveParams(options?.path);
|
|
6682
7220
|
console.log('[DEBUG] Making streaming request:', requestOptions);
|
|
6683
|
-
|
|
7221
|
+
if (this.hasDatabase && this.databaseOptions?.table) {
|
|
7222
|
+
const requestSignature = this.buildRequestSignature('STREAM', requestOptions, effectiveParams);
|
|
7223
|
+
return this.localStorageManagerService.store$(this.databaseOptions.table).pipe(take(1), switchMap((storeData) => {
|
|
7224
|
+
const forceRefresh = !!options?.forceRefresh;
|
|
7225
|
+
const expires = storeData?.expires || 0;
|
|
7226
|
+
const hasExpired = expires > 0 && this.utils.hasExpired(expires);
|
|
7227
|
+
if (forceRefresh) {
|
|
7228
|
+
this.setCachedRequestSignature(this.databaseOptions.table, 'STREAM', requestSignature);
|
|
7229
|
+
return this.httpManagerService.getRequest(requestOptions, effectiveParams).pipe(map((apiData) => ({ data: apiData, fromCache: false })));
|
|
7230
|
+
}
|
|
7231
|
+
if (hasExpired) {
|
|
7232
|
+
return this.dbManagerService.clearTable(this.databaseOptions.table).pipe(tap(() => this.setCachedRequestSignature(this.databaseOptions.table, 'STREAM', requestSignature)), switchMap(() => this.httpManagerService.getRequest(requestOptions, effectiveParams)), map((apiData) => ({ data: apiData, fromCache: false })));
|
|
7233
|
+
}
|
|
7234
|
+
const trackerAllowsRequest = this.queryParamsTrackerService.checkRequestOptions(this.resolvePath(effectiveParams), this.buildQueryTrackerOptions(options));
|
|
7235
|
+
const shouldMakeRequest = trackerAllowsRequest;
|
|
7236
|
+
if (!shouldMakeRequest) {
|
|
7237
|
+
return this.dbManagerService.getTableRecords(this.databaseOptions.table).pipe(switchMap((dbData) => {
|
|
7238
|
+
if (Array.isArray(dbData) && dbData.length > 0) {
|
|
7239
|
+
return of({ data: dbData, fromCache: true });
|
|
7240
|
+
}
|
|
7241
|
+
const currentStateData = this.get()?.data;
|
|
7242
|
+
if (Array.isArray(currentStateData) && currentStateData.length > 0) {
|
|
7243
|
+
return of({ data: currentStateData, fromCache: true });
|
|
7244
|
+
}
|
|
7245
|
+
if (currentStateData &&
|
|
7246
|
+
!Array.isArray(currentStateData) &&
|
|
7247
|
+
Object.keys(currentStateData).length > 0) {
|
|
7248
|
+
return of({ data: currentStateData, fromCache: true });
|
|
7249
|
+
}
|
|
7250
|
+
return of({ data: this.dataType === DataType.ARRAY ? [] : {}, fromCache: true });
|
|
7251
|
+
}));
|
|
7252
|
+
}
|
|
7253
|
+
this.setCachedRequestSignature(this.databaseOptions.table, 'STREAM', requestSignature);
|
|
7254
|
+
return this.httpManagerService.getRequest(requestOptions, effectiveParams).pipe(map((apiData) => ({ data: apiData, fromCache: false })));
|
|
7255
|
+
})).pipe(tap((packet) => {
|
|
7256
|
+
const res = packet?.data;
|
|
7257
|
+
console.log('[DEBUG] Streaming response received:', res);
|
|
7258
|
+
if (res && res.length > 0) {
|
|
7259
|
+
console.log('[DEBUG] Updating state with streaming data:', res);
|
|
7260
|
+
this.setData$(res);
|
|
7261
|
+
this.streamedResponse = res;
|
|
7262
|
+
}
|
|
7263
|
+
else {
|
|
7264
|
+
console.log('[DEBUG] No streaming data or empty array:', res);
|
|
7265
|
+
}
|
|
7266
|
+
// Reset pending once we have a response packet (cache or network)
|
|
7267
|
+
this.httpManagerService.isPending.next(false);
|
|
7268
|
+
}), concatMap((packet) => {
|
|
7269
|
+
if (packet?.fromCache) {
|
|
7270
|
+
return of(packet?.data);
|
|
7271
|
+
}
|
|
7272
|
+
return this.persistStreamDataToDb(packet?.data, options);
|
|
7273
|
+
}), map((res) => {
|
|
7274
|
+
console.log('[DEBUG] Returning data to subscribers:', res);
|
|
7275
|
+
return res;
|
|
7276
|
+
}), catchError((error) => {
|
|
7277
|
+
console.error('[DEBUG] Streaming error:', error);
|
|
7278
|
+
this.httpManagerService.isPending.next(false);
|
|
7279
|
+
return of([]);
|
|
7280
|
+
}));
|
|
7281
|
+
}
|
|
7282
|
+
return this.httpManagerService.getRequest(requestOptions, effectiveParams)
|
|
6684
7283
|
.pipe(tap((res) => {
|
|
6685
7284
|
console.log('[DEBUG] Streaming response received:', res);
|
|
6686
7285
|
// Always update state with streaming data
|
|
@@ -6692,7 +7291,9 @@ class HTTPManagerStateService extends ComponentStore {
|
|
|
6692
7291
|
else {
|
|
6693
7292
|
console.log('[DEBUG] No streaming data or empty array:', res);
|
|
6694
7293
|
}
|
|
6695
|
-
|
|
7294
|
+
// Reset pending once we have a response packet
|
|
7295
|
+
this.httpManagerService.isPending.next(false);
|
|
7296
|
+
}), concatMap((res) => this.persistStreamDataToDb(res, options)), map((res) => {
|
|
6696
7297
|
console.log('[DEBUG] Returning data to subscribers:', res);
|
|
6697
7298
|
return res; // Return the data so subscribers can receive it
|
|
6698
7299
|
}), catchError((error) => {
|
|
@@ -6719,7 +7320,11 @@ class HTTPManagerStateService extends ComponentStore {
|
|
|
6719
7320
|
encrypted: false,
|
|
6720
7321
|
})
|
|
6721
7322
|
});
|
|
6722
|
-
|
|
7323
|
+
// Use initDBStorageAsync directly - the effect initDBStorage requires subscription
|
|
7324
|
+
this.initDBStorageAsync().subscribe({
|
|
7325
|
+
next: () => console.log('[Constructor] Database storage initialized'),
|
|
7326
|
+
error: (err) => console.error('[Constructor] Database storage initialization failed:', err)
|
|
7327
|
+
});
|
|
6723
7328
|
}
|
|
6724
7329
|
}
|
|
6725
7330
|
catch (error) {
|
|
@@ -6773,8 +7378,24 @@ class HTTPManagerStateService extends ComponentStore {
|
|
|
6773
7378
|
this.dataType = (dataType) ? dataType : DataType.ARRAY;
|
|
6774
7379
|
// Only update database options if a database parameter is explicitly provided
|
|
6775
7380
|
if (database !== undefined) {
|
|
7381
|
+
console.log('[setApiRequestOptions] Database config:', {
|
|
7382
|
+
database,
|
|
7383
|
+
hasTable: !!database?.table,
|
|
7384
|
+
tableValue: database?.table
|
|
7385
|
+
});
|
|
6776
7386
|
this.hasDatabase = (database?.table) ? true : false;
|
|
6777
|
-
|
|
7387
|
+
const adapted = DatabaseStorage.adapt(database);
|
|
7388
|
+
console.log('[setApiRequestOptions] DatabaseStorage.adapt result:', adapted);
|
|
7389
|
+
this.databaseOptions = (this.hasDatabase) ? adapted : undefined;
|
|
7390
|
+
// Trigger database table creation if table is configured
|
|
7391
|
+
if (this.hasDatabase && this.databaseOptions?.table) {
|
|
7392
|
+
console.log('[setApiRequestOptions] Initializing database storage for table:', this.databaseOptions.table);
|
|
7393
|
+
// Use initDBStorageAsync directly instead of the effect - effects need subscription to run
|
|
7394
|
+
this.initDBStorageAsync().subscribe({
|
|
7395
|
+
next: () => console.log('[setApiRequestOptions] Database storage initialized successfully'),
|
|
7396
|
+
error: (err) => console.error('[setApiRequestOptions] Database storage initialization failed:', err)
|
|
7397
|
+
});
|
|
7398
|
+
}
|
|
6778
7399
|
}
|
|
6779
7400
|
if (this.apiOptions.ws && this.apiOptions.ws.id !== '') {
|
|
6780
7401
|
// Auto-prefix channel ID for private state manager channels
|
|
@@ -6896,11 +7517,20 @@ class HTTPManagerStateService extends ComponentStore {
|
|
|
6896
7517
|
this.setData$(data);
|
|
6897
7518
|
}
|
|
6898
7519
|
updateArrayState(currentData, newData) {
|
|
7520
|
+
// For non-streaming requests (GET), REPLACE data entirely
|
|
7521
|
+
// For streaming requests, MERGE with existing data incrementally
|
|
7522
|
+
if (this.streamedResponse.length === 0) {
|
|
7523
|
+
// GET request: return new data as-is (replace)
|
|
7524
|
+
return newData;
|
|
7525
|
+
}
|
|
7526
|
+
// Streaming: merge with existing data
|
|
6899
7527
|
const filterCurrentData = () => {
|
|
6900
7528
|
const ids = this.streamedResponse.map((obj) => obj.id);
|
|
6901
7529
|
return currentData.filter(obj => (obj.id) ? ids.includes(obj.id) : obj);
|
|
6902
7530
|
};
|
|
6903
|
-
const filteredCurrentData = (this.httpManagerService.isPending.value)
|
|
7531
|
+
const filteredCurrentData = (this.httpManagerService.isPending.value)
|
|
7532
|
+
? currentData
|
|
7533
|
+
: filterCurrentData();
|
|
6904
7534
|
const updatedData = filteredCurrentData.map(item => {
|
|
6905
7535
|
const newItem = newData.find(newItem => {
|
|
6906
7536
|
const hasId = (newItem?.id && item?.id) ? true : false;
|
|
@@ -6917,32 +7547,92 @@ class HTTPManagerStateService extends ComponentStore {
|
|
|
6917
7547
|
return [...updatedData, ...addedData];
|
|
6918
7548
|
}
|
|
6919
7549
|
initDBStorageAsync() {
|
|
7550
|
+
console.log('[initDBStorageAsync] Starting initialization:', {
|
|
7551
|
+
dataType: this.dataType,
|
|
7552
|
+
hasAdapter: !!this.apiOptions?.adapter,
|
|
7553
|
+
table: this.databaseOptions?.table,
|
|
7554
|
+
databaseOptions: this.databaseOptions
|
|
7555
|
+
});
|
|
6920
7556
|
if (this.dataType !== DataType.ARRAY) {
|
|
6921
7557
|
console.warn('Database storage requires dataType to be ARRAY');
|
|
6922
7558
|
return of(null);
|
|
6923
7559
|
}
|
|
6924
7560
|
if (!this.apiOptions.adapter) {
|
|
6925
|
-
console.warn('Database storage
|
|
6926
|
-
return of(null);
|
|
7561
|
+
console.warn('Database storage adapter missing, using minimal or inferred schema');
|
|
6927
7562
|
}
|
|
6928
7563
|
if (!this.databaseOptions?.table) {
|
|
6929
7564
|
console.warn('Database storage requires a table name');
|
|
6930
7565
|
return of(null);
|
|
6931
7566
|
}
|
|
7567
|
+
const schema = this.buildSchemaFromAdapter();
|
|
7568
|
+
const tableDef = TableSchemaDef.adapt({
|
|
7569
|
+
table: this.databaseOptions?.table,
|
|
7570
|
+
schema: schema
|
|
7571
|
+
});
|
|
7572
|
+
return this.dbManagerService.createDatabaseTable(tableDef).pipe(tap((created) => {
|
|
7573
|
+
if (created && this.databaseOptions?.table) {
|
|
7574
|
+
this.saveSchemaSignature(this.databaseOptions.table, this.buildSchemaSignature(schema));
|
|
7575
|
+
}
|
|
7576
|
+
}));
|
|
7577
|
+
}
|
|
7578
|
+
buildSchemaFromAdapter() {
|
|
6932
7579
|
const sampleData = this.apiOptions.adapter?.({}) || {};
|
|
6933
|
-
|
|
7580
|
+
return this.buildSchemaFromSample(sampleData);
|
|
7581
|
+
}
|
|
7582
|
+
buildSchemaFromSample(sampleData) {
|
|
7583
|
+
const schemaKeys = Object.keys(sampleData || {}).filter(key => sampleData[key] !== undefined);
|
|
6934
7584
|
let schema = '++id';
|
|
6935
7585
|
if (schemaKeys.length > 0) {
|
|
6936
|
-
const otherKeys = schemaKeys.filter(k => k !== 'id');
|
|
7586
|
+
const otherKeys = schemaKeys.filter((k) => k !== 'id');
|
|
6937
7587
|
if (otherKeys.length > 0) {
|
|
6938
7588
|
schema += ', ' + otherKeys.join(', ');
|
|
6939
7589
|
}
|
|
6940
7590
|
}
|
|
6941
|
-
|
|
6942
|
-
|
|
6943
|
-
|
|
6944
|
-
|
|
6945
|
-
|
|
7591
|
+
return schema;
|
|
7592
|
+
}
|
|
7593
|
+
persistStreamDataToDb(payload, streamOptions) {
|
|
7594
|
+
if (!this.hasDatabase || !this.databaseOptions?.table) {
|
|
7595
|
+
return of(payload);
|
|
7596
|
+
}
|
|
7597
|
+
const dbData = (payload?.results && Array.isArray(payload.results))
|
|
7598
|
+
? payload.results
|
|
7599
|
+
: Array.isArray(payload)
|
|
7600
|
+
? payload
|
|
7601
|
+
: payload
|
|
7602
|
+
? [payload]
|
|
7603
|
+
: [];
|
|
7604
|
+
if (dbData.length === 0) {
|
|
7605
|
+
return of(payload);
|
|
7606
|
+
}
|
|
7607
|
+
const tableName = this.databaseOptions.table;
|
|
7608
|
+
const requestOptions = this.updateRequestOptions(streamOptions?.headers);
|
|
7609
|
+
requestOptions.stream = true;
|
|
7610
|
+
const effectiveParams = this.getEffectiveParams(streamOptions?.path);
|
|
7611
|
+
const requestSignature = this.buildRequestSignature('STREAM', requestOptions, effectiveParams);
|
|
7612
|
+
this.localStorageManagerService.updateStore({
|
|
7613
|
+
name: tableName,
|
|
7614
|
+
data: { ...this.databaseOptions, ...{ expires: this.utils.expires(this.databaseOptions.expiresIn) } }
|
|
7615
|
+
});
|
|
7616
|
+
const schema = this.buildSchemaFromSample(dbData[0]);
|
|
7617
|
+
const schemaSignature = this.buildSchemaSignature(schema);
|
|
7618
|
+
const tableDef = TableSchemaDef.adapt({ table: tableName, schema });
|
|
7619
|
+
return this.localStorageManagerService.store$(tableName).pipe(take(1), switchMap((storeData) => {
|
|
7620
|
+
const storedSchemaSignature = this.getStoredSchemaSignature(storeData);
|
|
7621
|
+
const schemaChanged = !!storedSchemaSignature && storedSchemaSignature !== schemaSignature;
|
|
7622
|
+
const ensureTable$ = schemaChanged
|
|
7623
|
+
? this.dbManagerService.clearTable(tableName).pipe(switchMap(() => this.dbManagerService.createDatabaseTable(tableDef)))
|
|
7624
|
+
: this.dbManagerService.createDatabaseTable(tableDef);
|
|
7625
|
+
return ensureTable$.pipe(switchMap((created) => {
|
|
7626
|
+
if (!created) {
|
|
7627
|
+
console.warn('[DB STORAGE] Stream table create/open not ready, skipping DB write for this chunk:', { table: tableName });
|
|
7628
|
+
return of(payload);
|
|
7629
|
+
}
|
|
7630
|
+
return this.dbManagerService.createTableRecords(tableName, dbData).pipe(tap(() => this.saveRequestCacheMetadata(tableName, 'STREAM', requestSignature, schemaSignature, streamOptions)), map(() => payload));
|
|
7631
|
+
}));
|
|
7632
|
+
}), map(() => payload), catchError((error) => {
|
|
7633
|
+
console.error('[DB STORAGE] Failed to persist streaming payload:', { table: tableName, schema, error });
|
|
7634
|
+
return of(payload);
|
|
7635
|
+
}));
|
|
6946
7636
|
}
|
|
6947
7637
|
// WEBSOCKET COMMUNICATION (STATE MANAGER)
|
|
6948
7638
|
wsCommunication(method, path) {
|
|
@@ -7238,6 +7928,7 @@ class HTTPManagerStateService extends ComponentStore {
|
|
|
7238
7928
|
const tableName = this.databaseOptions.table;
|
|
7239
7929
|
this.dbManagerService.clearTable(tableName).subscribe({
|
|
7240
7930
|
next: () => {
|
|
7931
|
+
this.clearRequestCacheMetadata(tableName);
|
|
7241
7932
|
if (this.dataType === DataType.ARRAY) {
|
|
7242
7933
|
this.setData$([]);
|
|
7243
7934
|
}
|
|
@@ -7260,6 +7951,132 @@ class HTTPManagerStateService extends ComponentStore {
|
|
|
7260
7951
|
: { ...options.headers };
|
|
7261
7952
|
return options;
|
|
7262
7953
|
}
|
|
7954
|
+
buildQueryTrackerOptions(options) {
|
|
7955
|
+
return {
|
|
7956
|
+
watchParams: Array.isArray(options?.watchParams) ? options.watchParams : [],
|
|
7957
|
+
watchExpiresAt: options?.watchExpiresAt,
|
|
7958
|
+
};
|
|
7959
|
+
}
|
|
7960
|
+
normalizeObject(value) {
|
|
7961
|
+
if (Array.isArray(value)) {
|
|
7962
|
+
return value.map((item) => this.normalizeObject(item));
|
|
7963
|
+
}
|
|
7964
|
+
if (value && typeof value === 'object') {
|
|
7965
|
+
return Object.keys(value)
|
|
7966
|
+
.sort()
|
|
7967
|
+
.reduce((acc, key) => {
|
|
7968
|
+
acc[key] = this.normalizeObject(value[key]);
|
|
7969
|
+
return acc;
|
|
7970
|
+
}, {});
|
|
7971
|
+
}
|
|
7972
|
+
return value;
|
|
7973
|
+
}
|
|
7974
|
+
filterHeaders(headers) {
|
|
7975
|
+
const source = headers || {};
|
|
7976
|
+
return Object.keys(source).reduce((acc, key) => {
|
|
7977
|
+
if (!this.volatileHeaders.has(key.toLowerCase())) {
|
|
7978
|
+
acc[key] = source[key];
|
|
7979
|
+
}
|
|
7980
|
+
return acc;
|
|
7981
|
+
}, {});
|
|
7982
|
+
}
|
|
7983
|
+
resolvePath(params) {
|
|
7984
|
+
const basePath = Array.isArray(this.apiOptions.path) ? this.apiOptions.path : [];
|
|
7985
|
+
const effective = this.getEffectiveParams(params);
|
|
7986
|
+
return effective ? [...basePath, ...effective] : [...basePath];
|
|
7987
|
+
}
|
|
7988
|
+
getEffectiveParams(params) {
|
|
7989
|
+
if (!Array.isArray(params) || params.length === 0) {
|
|
7990
|
+
return undefined;
|
|
7991
|
+
}
|
|
7992
|
+
const basePath = Array.isArray(this.apiOptions.path) ? this.apiOptions.path : [];
|
|
7993
|
+
if (basePath.length !== params.length) {
|
|
7994
|
+
return params;
|
|
7995
|
+
}
|
|
7996
|
+
const normalizePart = (value) => {
|
|
7997
|
+
if (value && typeof value === 'object') {
|
|
7998
|
+
return JSON.stringify(this.normalizeObject(value));
|
|
7999
|
+
}
|
|
8000
|
+
return String(value);
|
|
8001
|
+
};
|
|
8002
|
+
const samePath = params.every((part, index) => normalizePart(part) === normalizePart(basePath[index]));
|
|
8003
|
+
return samePath ? undefined : params;
|
|
8004
|
+
}
|
|
8005
|
+
buildRequestSignature(method, requestOptions, params) {
|
|
8006
|
+
const signaturePayload = {
|
|
8007
|
+
method,
|
|
8008
|
+
server: requestOptions.server,
|
|
8009
|
+
path: this.resolvePath(params),
|
|
8010
|
+
headers: this.filterHeaders(requestOptions.headers),
|
|
8011
|
+
stream: !!requestOptions.stream,
|
|
8012
|
+
streamType: requestOptions.streamType || null
|
|
8013
|
+
};
|
|
8014
|
+
return JSON.stringify(this.normalizeObject(signaturePayload));
|
|
8015
|
+
}
|
|
8016
|
+
buildSchemaSignature(schema) {
|
|
8017
|
+
return JSON.stringify(this.normalizeObject(schema.split(',').map((part) => part.trim()).filter(Boolean)));
|
|
8018
|
+
}
|
|
8019
|
+
getCachedRequestSignature(tableName, type) {
|
|
8020
|
+
return this.requestSignatureCache[tableName]?.[type] || null;
|
|
8021
|
+
}
|
|
8022
|
+
setCachedRequestSignature(tableName, type, signature) {
|
|
8023
|
+
const existing = this.requestSignatureCache[tableName] || {};
|
|
8024
|
+
this.requestSignatureCache[tableName] = {
|
|
8025
|
+
...existing,
|
|
8026
|
+
[type]: signature,
|
|
8027
|
+
};
|
|
8028
|
+
}
|
|
8029
|
+
getRequestCacheMetadata(storeData, type) {
|
|
8030
|
+
return storeData?.requestCache?.[type] || null;
|
|
8031
|
+
}
|
|
8032
|
+
getStoredSchemaSignature(storeData) {
|
|
8033
|
+
return storeData?.schemaSignature || null;
|
|
8034
|
+
}
|
|
8035
|
+
saveSchemaSignature(tableName, schemaSignature) {
|
|
8036
|
+
this.localStorageManagerService.store$(tableName).pipe(take(1), tap((storeData) => {
|
|
8037
|
+
this.localStorageManagerService.updateStore({
|
|
8038
|
+
name: tableName,
|
|
8039
|
+
data: {
|
|
8040
|
+
...(storeData || {}),
|
|
8041
|
+
schemaSignature,
|
|
8042
|
+
}
|
|
8043
|
+
});
|
|
8044
|
+
})).subscribe();
|
|
8045
|
+
}
|
|
8046
|
+
saveRequestCacheMetadata(tableName, type, signature, schemaSignature, options) {
|
|
8047
|
+
this.setCachedRequestSignature(tableName, type, signature);
|
|
8048
|
+
this.localStorageManagerService.store$(tableName).pipe(take(1), tap((storeData) => {
|
|
8049
|
+
const currentCache = storeData?.requestCache || {};
|
|
8050
|
+
this.localStorageManagerService.updateStore({
|
|
8051
|
+
name: tableName,
|
|
8052
|
+
data: {
|
|
8053
|
+
...(storeData || {}),
|
|
8054
|
+
schemaSignature: schemaSignature || storeData?.schemaSignature || null,
|
|
8055
|
+
requestCache: {
|
|
8056
|
+
...currentCache,
|
|
8057
|
+
[type]: {
|
|
8058
|
+
signature,
|
|
8059
|
+
savedAt: Date.now(),
|
|
8060
|
+
path: this.resolvePath(options?.path),
|
|
8061
|
+
headers: this.filterHeaders(options?.headers)
|
|
8062
|
+
}
|
|
8063
|
+
}
|
|
8064
|
+
}
|
|
8065
|
+
});
|
|
8066
|
+
})).subscribe();
|
|
8067
|
+
}
|
|
8068
|
+
clearRequestCacheMetadata(tableName) {
|
|
8069
|
+
this.localStorageManagerService.store$(tableName).pipe(take(1), tap((storeData) => {
|
|
8070
|
+
if (!storeData)
|
|
8071
|
+
return;
|
|
8072
|
+
const updated = { ...(storeData || {}) };
|
|
8073
|
+
delete updated.requestCache;
|
|
8074
|
+
this.localStorageManagerService.updateStore({
|
|
8075
|
+
name: tableName,
|
|
8076
|
+
data: updated
|
|
8077
|
+
});
|
|
8078
|
+
})).subscribe();
|
|
8079
|
+
}
|
|
7263
8080
|
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
8081
|
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: HTTPManagerStateService }); }
|
|
7265
8082
|
}
|
|
@@ -8566,12 +9383,12 @@ class StateManagerDemoService extends HTTPManagerStateService {
|
|
|
8566
9383
|
setAPIOptions(apiOptions, dataType, database) {
|
|
8567
9384
|
this.setApiRequestOptions(apiOptions, dataType, database);
|
|
8568
9385
|
}
|
|
8569
|
-
getClients() {
|
|
9386
|
+
getClients(options) {
|
|
8570
9387
|
// const headers = {
|
|
8571
9388
|
// auth: "sample-auth-token"
|
|
8572
9389
|
// }
|
|
8573
9390
|
// const sampleOptions = RequestOptions.adapt({ path: ["id", 12], headers, sample: true })
|
|
8574
|
-
this.fetchRecords();
|
|
9391
|
+
this.fetchRecords(options);
|
|
8575
9392
|
}
|
|
8576
9393
|
createClient(data) {
|
|
8577
9394
|
// const headers = {
|
|
@@ -8596,13 +9413,13 @@ class StateManagerDemoService extends HTTPManagerStateService {
|
|
|
8596
9413
|
const sampleOptions = RequestOptions.adapt({ path: [data.id] });
|
|
8597
9414
|
this.deleteRecord(sampleOptions);
|
|
8598
9415
|
}
|
|
8599
|
-
streamRequest() {
|
|
9416
|
+
streamRequest(options) {
|
|
8600
9417
|
console.log('[DEMO SERVICE] streamRequest called');
|
|
8601
9418
|
const headers = {
|
|
8602
9419
|
auth: "sample-auth-token"
|
|
8603
9420
|
};
|
|
8604
9421
|
console.log('[DEMO SERVICE] Calling fetchStream with headers:', headers);
|
|
8605
|
-
this.fetchStream();
|
|
9422
|
+
this.fetchStream(options);
|
|
8606
9423
|
}
|
|
8607
9424
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: StateManagerDemoService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
8608
9425
|
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: StateManagerDemoService }); }
|
|
@@ -8750,6 +9567,8 @@ class RequestManagerStateDemoComponent {
|
|
|
8750
9567
|
database: this.fb.group({
|
|
8751
9568
|
table: [''],
|
|
8752
9569
|
expiresIn: ['1m'],
|
|
9570
|
+
watchParams: [''],
|
|
9571
|
+
watchExpiresAt: ['never'],
|
|
8753
9572
|
})
|
|
8754
9573
|
});
|
|
8755
9574
|
this.sampleAdaptors = [
|
|
@@ -8782,26 +9601,26 @@ class RequestManagerStateDemoComponent {
|
|
|
8782
9601
|
});
|
|
8783
9602
|
this.stateManagerDemoService.data$
|
|
8784
9603
|
.pipe(tap$1((data) => {
|
|
8785
|
-
console.log('[COMPONENT] State data received:', data)
|
|
9604
|
+
// console.log('[COMPONENT] State data received:', data)
|
|
8786
9605
|
switch (this.requestType) {
|
|
8787
9606
|
case 'GET':
|
|
8788
|
-
console.log('[COMPONENT] Updating GET$ with data:', data)
|
|
9607
|
+
// console.log('[COMPONENT] Updating GET$ with data:', data)
|
|
8789
9608
|
this.GET$.next(data);
|
|
8790
9609
|
break;
|
|
8791
9610
|
case 'PUT':
|
|
8792
|
-
console.log('[COMPONENT] Updating PUT$ with data:', data)
|
|
9611
|
+
// console.log('[COMPONENT] Updating PUT$ with data:', data)
|
|
8793
9612
|
this.PUT$.next(data);
|
|
8794
9613
|
break;
|
|
8795
9614
|
case 'POST':
|
|
8796
|
-
console.log('[COMPONENT] Updating POST$ with data:', data)
|
|
9615
|
+
// console.log('[COMPONENT] Updating POST$ with data:', data)
|
|
8797
9616
|
this.POST$.next(data);
|
|
8798
9617
|
break;
|
|
8799
9618
|
case 'DELETE':
|
|
8800
|
-
console.log('[COMPONENT] Updating DELETE$ with data:', data)
|
|
9619
|
+
// console.log('[COMPONENT] Updating DELETE$ with data:', data)
|
|
8801
9620
|
this.DELETE$.next(data);
|
|
8802
9621
|
break;
|
|
8803
9622
|
case 'STREAM':
|
|
8804
|
-
console.log('[COMPONENT] Updating STREAM$ with data:', data)
|
|
9623
|
+
// console.log('[COMPONENT] Updating STREAM$ with data:', data)
|
|
8805
9624
|
this.STREAM.next(data);
|
|
8806
9625
|
// Update table columns dynamically based on streaming data
|
|
8807
9626
|
if (data && Array.isArray(data) && data.length > 0) {
|
|
@@ -8809,11 +9628,11 @@ class RequestManagerStateDemoComponent {
|
|
|
8809
9628
|
}
|
|
8810
9629
|
break;
|
|
8811
9630
|
case 'STREAM_AI':
|
|
8812
|
-
console.log('[COMPONENT] Updating STREAM_AI$ with data:', data)
|
|
9631
|
+
// console.log('[COMPONENT] Updating STREAM_AI$ with data:', data)
|
|
8813
9632
|
this.STREAM_AI.next(data);
|
|
8814
9633
|
break;
|
|
8815
9634
|
default:
|
|
8816
|
-
console.log('[COMPONENT] No requestType set, ignoring data')
|
|
9635
|
+
// console.log('[COMPONENT] No requestType set, ignoring data')
|
|
8817
9636
|
break;
|
|
8818
9637
|
}
|
|
8819
9638
|
})).subscribe();
|
|
@@ -8831,10 +9650,59 @@ class RequestManagerStateDemoComponent {
|
|
|
8831
9650
|
removeHeader(index) {
|
|
8832
9651
|
this.headers.removeAt(index);
|
|
8833
9652
|
}
|
|
9653
|
+
parsePathInput(pathInput) {
|
|
9654
|
+
const raw = typeof pathInput === 'string' ? pathInput.trim() : '';
|
|
9655
|
+
if (!raw) {
|
|
9656
|
+
return [];
|
|
9657
|
+
}
|
|
9658
|
+
const [rawPath = '', rawQuery = ''] = raw.split('?', 2);
|
|
9659
|
+
const pathSegments = rawPath
|
|
9660
|
+
.split('/')
|
|
9661
|
+
.map((segment) => segment.trim())
|
|
9662
|
+
.filter((segment) => segment.length > 0)
|
|
9663
|
+
.map((segment) => decodeURIComponent(segment));
|
|
9664
|
+
if (!rawQuery) {
|
|
9665
|
+
return pathSegments;
|
|
9666
|
+
}
|
|
9667
|
+
const searchParams = new URLSearchParams(rawQuery);
|
|
9668
|
+
const queryObject = {};
|
|
9669
|
+
searchParams.forEach((value, key) => {
|
|
9670
|
+
const normalizedKey = key.trim();
|
|
9671
|
+
if (!normalizedKey) {
|
|
9672
|
+
return;
|
|
9673
|
+
}
|
|
9674
|
+
const normalizedValue = this.normalizeQueryValue(value);
|
|
9675
|
+
if (Object.prototype.hasOwnProperty.call(queryObject, normalizedKey)) {
|
|
9676
|
+
const currentValue = queryObject[normalizedKey];
|
|
9677
|
+
queryObject[normalizedKey] = Array.isArray(currentValue)
|
|
9678
|
+
? [...currentValue, normalizedValue]
|
|
9679
|
+
: [currentValue, normalizedValue];
|
|
9680
|
+
return;
|
|
9681
|
+
}
|
|
9682
|
+
queryObject[normalizedKey] = normalizedValue;
|
|
9683
|
+
});
|
|
9684
|
+
return Object.keys(queryObject).length > 0
|
|
9685
|
+
? [...pathSegments, queryObject]
|
|
9686
|
+
: pathSegments;
|
|
9687
|
+
}
|
|
9688
|
+
normalizeQueryValue(value) {
|
|
9689
|
+
const trimmedValue = value.trim();
|
|
9690
|
+
const lowerValue = trimmedValue.toLowerCase();
|
|
9691
|
+
if (lowerValue === 'true') {
|
|
9692
|
+
return true;
|
|
9693
|
+
}
|
|
9694
|
+
if (lowerValue === 'false') {
|
|
9695
|
+
return false;
|
|
9696
|
+
}
|
|
9697
|
+
if (trimmedValue !== '' && !Number.isNaN(Number(trimmedValue))) {
|
|
9698
|
+
return Number(trimmedValue);
|
|
9699
|
+
}
|
|
9700
|
+
return trimmedValue;
|
|
9701
|
+
}
|
|
8834
9702
|
compileRequest() {
|
|
8835
9703
|
const requestParams = this.requestForm.value;
|
|
8836
9704
|
requestParams.headers = this.arrayObjectsToObjects(requestParams.headers || []);
|
|
8837
|
-
const pathReq =
|
|
9705
|
+
const pathReq = this.parsePathInput(requestParams.path || '');
|
|
8838
9706
|
if (!this.pollingState.checked)
|
|
8839
9707
|
requestParams.polling = 0;
|
|
8840
9708
|
if (!this.failedState.checked) {
|
|
@@ -8848,12 +9716,53 @@ class RequestManagerStateDemoComponent {
|
|
|
8848
9716
|
const apiOptions = ApiRequest.adapt({ ...currentOptions, path: pathReq });
|
|
8849
9717
|
return { apiOptions: apiOptions, path: pathReq };
|
|
8850
9718
|
}
|
|
9719
|
+
parseWatchParams(value) {
|
|
9720
|
+
if (!value || typeof value !== 'string') {
|
|
9721
|
+
return [];
|
|
9722
|
+
}
|
|
9723
|
+
return value
|
|
9724
|
+
.split(',')
|
|
9725
|
+
.map((item) => item.trim())
|
|
9726
|
+
.filter((item) => item.length > 0);
|
|
9727
|
+
}
|
|
9728
|
+
normalizeWatchExpiry(value) {
|
|
9729
|
+
if (typeof value === 'undefined' || value === null || value === '') {
|
|
9730
|
+
return undefined;
|
|
9731
|
+
}
|
|
9732
|
+
if (typeof value === 'string' && value.trim().toLowerCase() === 'never') {
|
|
9733
|
+
return undefined;
|
|
9734
|
+
}
|
|
9735
|
+
return value;
|
|
9736
|
+
}
|
|
9737
|
+
buildDemoRequestOptions(path, headers) {
|
|
9738
|
+
const dbValue = this.database || {};
|
|
9739
|
+
const isDbEnabled = !!this.DBState?.checked;
|
|
9740
|
+
return RequestOptions.adapt({
|
|
9741
|
+
path,
|
|
9742
|
+
headers,
|
|
9743
|
+
forceRefresh: false,
|
|
9744
|
+
watchParams: isDbEnabled ? this.parseWatchParams(dbValue?.watchParams) : [],
|
|
9745
|
+
watchExpiresAt: isDbEnabled ? this.normalizeWatchExpiry(dbValue?.watchExpiresAt) : undefined,
|
|
9746
|
+
});
|
|
9747
|
+
}
|
|
8851
9748
|
onSetStateOptions() {
|
|
8852
|
-
|
|
9749
|
+
const dbValue = this.database;
|
|
9750
|
+
console.log('[onSetStateOptions] Called, checking form values:', {
|
|
9751
|
+
isValid: this.isValid,
|
|
9752
|
+
database: dbValue,
|
|
9753
|
+
hasTable: !!dbValue?.table,
|
|
9754
|
+
tableValue: dbValue?.table,
|
|
9755
|
+
dataType: this.dataType
|
|
9756
|
+
});
|
|
9757
|
+
if (!this.isValid) {
|
|
9758
|
+
console.log('[onSetStateOptions] Form invalid, aborting');
|
|
8853
9759
|
return;
|
|
9760
|
+
}
|
|
8854
9761
|
const reqParams = this.compileRequest();
|
|
8855
|
-
const db = DatabaseStorage.adapt(
|
|
9762
|
+
const db = DatabaseStorage.adapt(dbValue);
|
|
9763
|
+
console.log('[onSetStateOptions] DatabaseStorage.adapt result:', db);
|
|
8856
9764
|
const type = this.dataType === "ARRAY" ? DataType.ARRAY : DataType.OBJECT;
|
|
9765
|
+
console.log('[onSetStateOptions] Calling setAPIOptions with:', { db, type, hasTable: !!db?.table });
|
|
8857
9766
|
this.stateManagerDemoService.setAPIOptions(reqParams.apiOptions, type, db);
|
|
8858
9767
|
this.requestForm.markAsPristine();
|
|
8859
9768
|
}
|
|
@@ -8862,7 +9771,9 @@ class RequestManagerStateDemoComponent {
|
|
|
8862
9771
|
}
|
|
8863
9772
|
onGetRequest() {
|
|
8864
9773
|
this.requestType = 'GET';
|
|
8865
|
-
this.
|
|
9774
|
+
const reqParams = this.compileRequest();
|
|
9775
|
+
const requestOptions = this.buildDemoRequestOptions(reqParams.path, reqParams.apiOptions.headers);
|
|
9776
|
+
this.stateManagerDemoService.getClients(requestOptions);
|
|
8866
9777
|
}
|
|
8867
9778
|
onCreateRequest() {
|
|
8868
9779
|
this.requestType = 'POST';
|
|
@@ -8877,19 +9788,22 @@ class RequestManagerStateDemoComponent {
|
|
|
8877
9788
|
this.stateManagerDemoService.deleteClient(this.sampleClientData);
|
|
8878
9789
|
}
|
|
8879
9790
|
onStreamRequest() {
|
|
8880
|
-
console.log('[COMPONENT] onStreamRequest called')
|
|
9791
|
+
// console.log('[COMPONENT] onStreamRequest called')
|
|
8881
9792
|
if (!this.isValid) {
|
|
8882
|
-
console.log('[COMPONENT] Form invalid, aborting')
|
|
9793
|
+
// console.log('[COMPONENT] Form invalid, aborting')
|
|
8883
9794
|
return;
|
|
8884
9795
|
}
|
|
8885
|
-
console.log('[COMPONENT] Compiling request...')
|
|
9796
|
+
// console.log('[COMPONENT] Compiling request...')
|
|
8886
9797
|
const reqParams = this.compileRequest();
|
|
8887
|
-
console.log('[COMPONENT] Setting stream options:', reqParams.apiOptions)
|
|
9798
|
+
// console.log('[COMPONENT] Setting stream options:', reqParams.apiOptions)
|
|
8888
9799
|
reqParams.apiOptions.stream = true;
|
|
8889
9800
|
reqParams.apiOptions.streamType = this.streamType;
|
|
8890
9801
|
this.requestType = 'STREAM';
|
|
8891
|
-
|
|
8892
|
-
|
|
9802
|
+
const streamOptions = RequestOptions.adapt({
|
|
9803
|
+
...this.buildDemoRequestOptions(reqParams.path, reqParams.apiOptions.headers),
|
|
9804
|
+
});
|
|
9805
|
+
// console.log('[COMPONENT] Calling streamRequest...')
|
|
9806
|
+
this.stateManagerDemoService.streamRequest(streamOptions);
|
|
8893
9807
|
}
|
|
8894
9808
|
errorHandling(err, type) {
|
|
8895
9809
|
console.log(err, type);
|
|
@@ -8913,11 +9827,11 @@ class RequestManagerStateDemoComponent {
|
|
|
8913
9827
|
this.prompts = [];
|
|
8914
9828
|
}
|
|
8915
9829
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: RequestManagerStateDemoComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
8916
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: RequestManagerStateDemoComponent, selector: "app-request-manager-state-demo", inputs: { server: "server", adapter: "adapter", mapper: "mapper" }, providers: [StateManagerDemoService, HTTPManagerService], viewQueries: [{ propertyName: "failedState", first: true, predicate: ["failedState"], descendants: true, static: true }, { propertyName: "pollingState", first: true, predicate: ["pollingState"], descendants: true, static: true }], ngImport: i0, template: "<div style=\"margin: 2rem;\">\n\n <h2>\n HTTP Request State Manager\n </h2>\n\n <div [formGroup]=\"requestForm\" style=\"margin-top: 2rem;\">\n <div style=\"display: flex; gap: .5rem\">\n <mat-form-field appearance=\"outline\">\n <mat-label>State Data Type</mat-label>\n <mat-select formControlName=\"datatype\">\n <mat-option value=\"ARRAY\">Array</mat-option>\n <mat-option value=\"OBJECT\">Object</mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n <div style=\"display: flex; gap: .5rem\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Adapter (Model)</mat-label>\n <mat-select formControlName=\"adapter\" #adapterSelect>\n <mat-option>None</mat-option>\n @for (adapter of sampleAdaptors; track adapter) {\n <mat-option [value]=\"adapter.value\">\n {{adapter.label}}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>Mapper (Model)</mat-label>\n <mat-select formControlName=\"mapper\" #mapperSelect>\n <mat-option>None</mat-option>\n @for (mapper of sampleMappers; track mapper) {\n <mat-option [value]=\"mapper.value\">\n {{mapper.label}}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n\n @if (adapterSelect.value || mapperSelect.value) {\n <div style=\"display: flex; margin-bottom: 2rem;\">\n <div style=\"flex:1\" class=\"box\">\n <h3>Adapter (Incoming)</h3>\n @if (adapterSelect.value) {\n <div>\n {{ props(adapterSelect.value) | json }}\n </div>\n } @else {\n No Transformation\n }\n </div>\n <div style=\"flex:1\" class=\"box\">\n <h3>Mapper (Outgoing)</h3>\n @if (mapperSelect.value) {\n <div>\n {{ props(mapperSelect.value) | json }}\n </div>\n } @else {\n No Transformation\n }\n </div>\n </div>\n }\n\n <div>\n <mat-form-field appearance=\"outline\">\n <mat-label>RestPath (/ delimited)</mat-label>\n <input matInput placeholder=\"clients/list\" formControlName=\"path\">\n </mat-form-field>\n </div>\n <div>\n <div formArrayName=\"headers\">\n @for (task of headers.controls; track task; let i = $index) {\n <div [formGroupName]=\"i\">\n <div style=\"display: flex; gap: .5rem\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Key</mat-label>\n <input matInput placeholder=\"authentication\" formControlName=\"key\">\n </mat-form-field>\n <mat-form-field appearance=\"outline\" style=\"flex:1\">\n <mat-label>Value</mat-label>\n <input matInput placeholder=\"sample\" formControlName=\"value\">\n </mat-form-field>\n <div style=\"margin-top: .5rem;\">\n <button mat-icon-button (click)=\"removeHeader(i)\">\n <mat-icon>close</mat-icon>\n </button>\n </div>\n </div>\n </div>\n }\n </div>\n <button mat-stroked-button (click)=\"addHeader()\">Add Header</button>\n </div>\n <div style=\"margin-top: 2rem; display: flex; flex-direction:column; gap: 1rem;\">\n <div>\n <mat-slide-toggle #failedState>Retry on Failed</mat-slide-toggle>\n @if (failedState.checked) {\n <div style=\"display: flex; gap: .5rem; margin-top: 1rem;\" formGroupName=\"retry\">\n <mat-form-field appearance=\"outline\">\n <mat-label>#of Times</mat-label>\n <input matInput placeholder=\"3\" formControlName=\"times\" value=\"3\">\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>Delay Until Next</mat-label>\n <input matInput placeholder=\"3\" formControlName=\"delay\" value=\"3\">\n </mat-form-field>\n </div>\n }\n </div>\n <div>\n <mat-slide-toggle #pollingState>Polling</mat-slide-toggle>\n @if (pollingState.checked) {\n <div>\n <mat-form-field appearance=\"outline\" style=\"margin-top: 1rem\">\n <mat-label>#of Seconds</mat-label>\n <input matInput placeholder=\"3\" formControlName=\"polling\" value=\"3\">\n </mat-form-field>\n </div>\n }\n </div>\n <div>\n <mat-slide-toggle #DBState>Database Storage</mat-slide-toggle>\n @if (DBState.checked) {\n <div style=\"display: flex; gap: .5rem; margin-top: 1rem\" formGroupName=\"database\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Table Name</mat-label>\n <input matInput placeholder=\"table\" formControlName=\"table\" value=\"\">\n </mat-form-field>\n <div>\n <mat-form-field appearance=\"outline\">\n <mat-label>Expires In</mat-label>\n <mat-select formControlName=\"expiresIn\">\n <mat-option value=\"1m\">One Minute</mat-option>\n <mat-option value=\"1h\">One Hour</mat-option>\n <mat-option value=\"1d\">One Day</mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n </div>\n }\n </div>\n <div style=\"margin-top: 1rem; display: flex;\">\n <span style=\"flex:1\"></span>\n <button mat-stroked-button (click)=\"onSetStateOptions()\" [disabled]=\"!hasChanged\">\n Set API Request Options\n </button>\n </div>\n <div>\n @if ((error$ | async); as error) {\n <mat-error>\n {{ error }}\n </mat-error>\n }\n </div>\n </div>\n </div>\n\n <div style=\"margin-bottom: 1rem; margin-top: 2rem;\">\n @if ((isPending$ | async)) {\n <mat-progress-bar mode=\"indeterminate\"\n ></mat-progress-bar>\n }\n @if (pollingState.checked) {\n <div>\n @if (!(isPending$ | async) && (this.countdown$ | async) || -1 > 0) {\n <mat-progress-bar mode=\"determinate\"\n [value]=\"(this.countdown$ | async)\"\n ></mat-progress-bar>\n }\n </div>\n }\n\n </div>\n\n @if ((GET$ | async); as data) {\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1;\">CURRENT STATE ({{ dataType }})</h2>\n </div>\n @if (data.length > 1) {\n <div>\n <mat-form-field appearance=\"outline\">\n <mat-label>Records</mat-label>\n <mat-select [formControl]=\"selectedRecord\" [disabled]=\"data === null && data?.length === 0\">\n <mat-option [value]=\"\">None</mat-option>\n @for (item of data; track item) {\n <mat-option [value]=\"item\">\n {{item.name || (item.first_name) | titlecase}}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n }\n @if ((dataObservable$ | async); as dataRecord) {\n <div>\n @if ((selectedRecord$ | async); as record) {\n <div>\n {{ record | json }}\n </div>\n } @else {\n No Record Selected from State\n }\n </div>\n }\n <div style=\"margin-top: 1rem;\">\n @if (data !== null && data?.length > 0) {\n <span>\n State contains a Total of {{ data.length }} Records\n </span>\n } @else {\n No Records\n }\n <div style=\"display: flex; gap: .5rem; text-align: end; justify-content: flex-end;\">\n <button class=\"btn\" mat-stroked-button (click)=\"onClearRecords()\">Clear</button>\n </div>\n </div>\n </div>\n }\n\n\n\n\n <div style=\"margin-top: 1rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">GET Request ({{ dataType }})</h2>\n <div>\n <button mat-raised-button (click)=\"onGetRequest()\" [disabled]=\"hasChanged\" class=\"btn\">Request</button>\n </div>\n </div>\n\n @if ((GET_error$ | async); as get_error) {\n <div style=\"margin-top: .5rem;\">\n <mat-error>{{ get_error }}</mat-error>\n </div>\n }\n\n <div style=\"margin-top: 1rem;\">\n <!-- <div [innerHTML]=\"(GET$ | async) | jsonv\"></div> -->\n @if ((GET$ | async); as getData) {\n <div>{{ getData | json }}</div>\n }\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">POST Request</h2>\n <div>\n <button mat-raised-button (click)=\"onCreateRequest()\" [disabled]=\"hasChanged\" class=\"btn\">Request</button>\n </div>\n </div>\n\n @if ((POST_error$ | async); as post_error) {\n <div style=\"margin-top: .5rem;\">\n <mat-error>{{ post_error }}</mat-error>\n </div>\n }\n\n <div style=\"margin-top: 1rem;\">\n <!-- <div [innerHTML]=\"(POST$ | async) | jsonv\"></div> -->\n @if ((POST$ | async); as postData) {\n <div>{{ postData | json }}</div>\n }\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">PUT Request</h2>\n <div>\n <button mat-raised-button (click)=\"onUpdateRequest()\" [disabled]=\"hasChanged\" class=\"btn\">Request</button>\n </div>\n </div>\n\n @if ((PUT_error$ | async); as put_error) {\n <div style=\"margin-top: .5rem;\">\n <mat-error>{{ put_error }}</mat-error>\n </div>\n }\n\n <div style=\"margin-top: 1rem;\">\n <!-- <div [innerHTML]=\"(PUT$ | async) | jsonv\"></div> -->\n @if ((PUT$ | async); as putData) {\n <div>{{ putData | json }}</div>\n }\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">DELETE Request</h2>\n <div>\n <button mat-raised-button (click)=\"onDeleteRequest()\" [disabled]=\"hasChanged\" class=\"btn\">Request</button>\n </div>\n </div>\n\n @if ((DELETE_error$ | async); as delete_error) {\n <div style=\"margin-top: .5rem;\">\n <mat-error>{{ delete_error }}</mat-error>\n </div>\n }\n\n <div style=\"margin-top: 1rem;\">\n <!-- <div [innerHTML]=\"(DELETE$ | async) | jsonv\"></div> -->\n @if ((DELETE$ | async); as deleteData) {\n <div>{{ deleteData | json }}</div>\n }\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">Streaming GET Request</h2>\n <div style=\"display: flex; gap: 1rem; align-items: center;\">\n <div style=\"display: flex; gap: 1rem; align-items: center;\">\n {{ streamType }}\n <button mat-icon-button [matMenuTriggerFor]=\"menu\">\n <mat-icon>data_usage</mat-icon>\n </button>\n </div>\n <mat-menu #menu=\"matMenu\">\n <button mat-menu-item *ngFor=\"let item of streamTypes\" (click)=\"onStreamType(item.id)\">{{ item.value }}</button>\n </mat-menu>\n <button mat-raised-button (click)=\"onStreamRequest()\" class=\"btn\" [disabled]=\"hasChanged\">Request</button>\n </div>\n </div>\n\n @if ((STREAM_error$ | async); as stream_error) {\n <div style=\"margin-top: .5rem;\">\n <mat-error>{{ stream_error }}</mat-error>\n </div>\n }\n\n <div style=\"margin-top: 1rem;\">\n @if ((STREAM$ | async); as data) {\n <div class=\"container\">\n <table mat-table [dataSource]=\"data\" class=\"mat-elevation-z8\">\n\n <!-- Dynamic columns -->\n <ng-container *ngFor=\"let column of displayedColumns\" [matColumnDef]=\"column\">\n <th mat-header-cell *matHeaderCellDef> {{ column | titlecase }} </th>\n <td mat-cell *matCellDef=\"let element\">\n @if (isObject(element[column]); as objValue) {\n <pre style=\"margin: 0; font-size: 0.8em; white-space: pre-wrap;\">{{ objValue | json }}</pre>\n } @else {\n {{ element[column] }}\n }\n </td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: displayedColumns;\"></tr>\n </table>\n\n <!-- Debug info -->\n <div style=\"margin-top: 1rem; font-size: 0.8em; color: #666;\">\n Columns: {{ displayedColumns.join(', ') }} | Data received\n </div>\n </div>\n }\n </div>\n\n </div>\n\n</div>\n", styles: [".btn{min-width:120px}.mat-mdc-row .mat-mdc-cell{border-bottom:1px solid transparent;border-top:1px solid transparent;cursor:pointer}.mat-mdc-row:hover .mat-mdc-cell{border-color:currentColor;background-color:#f5f5f5}.container{height:400px;overflow:auto}.box{padding:10px;border:1px solid #ccc}\n"], dependencies: [{ kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i2$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i2$1.FormGroupName, selector: "[formGroupName]", inputs: ["formGroupName"] }, { kind: "directive", type: i2$1.FormArrayName, selector: "[formArrayName]", inputs: ["formArrayName"] }, { kind: "component", type: i3.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "component", type: i3.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "directive", type: i4.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "component", type: i5.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i6.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "component", type: i7.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i7.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i7.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "component", type: i8.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i9.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i9.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i9.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i9.MatColumnDef, selector: "[matColumnDef]", inputs: ["matColumnDef"] }, { kind: "directive", type: i9.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i9.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i9.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i9.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "component", type: i9.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i9.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "component", type: i8$1.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { kind: "component", type: i11.MatSlideToggle, selector: "mat-slide-toggle", inputs: ["name", "id", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "color", "disabled", "disableRipple", "tabIndex", "checked", "hideIcon", "disabledInteractive"], outputs: ["change", "toggleChange"], exportAs: ["matSlideToggle"] }, { kind: "component", type: i12.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "directive", type: i13.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }, { kind: "pipe", type: i1$1.JsonPipe, name: "json" }, { kind: "pipe", type: i1$1.TitleCasePipe, name: "titlecase" }] }); }
|
|
9830
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: RequestManagerStateDemoComponent, selector: "app-request-manager-state-demo", inputs: { server: "server", adapter: "adapter", mapper: "mapper" }, providers: [StateManagerDemoService, HTTPManagerService], viewQueries: [{ propertyName: "failedState", first: true, predicate: ["failedState"], descendants: true, static: true }, { propertyName: "pollingState", first: true, predicate: ["pollingState"], descendants: true, static: true }, { propertyName: "DBState", first: true, predicate: ["DBState"], descendants: true, static: true }], ngImport: i0, template: "<div style=\"margin: 2rem;\">\n\n <h2>\n HTTP Request State Manager\n </h2>\n\n <div [formGroup]=\"requestForm\" style=\"margin-top: 2rem;\">\n <div style=\"display: flex; gap: .5rem\">\n <mat-form-field appearance=\"outline\">\n <mat-label>State Data Type</mat-label>\n <mat-select formControlName=\"datatype\">\n <mat-option value=\"ARRAY\">Array</mat-option>\n <mat-option value=\"OBJECT\">Object</mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n <div style=\"display: flex; gap: .5rem\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Adapter (Model)</mat-label>\n <mat-select formControlName=\"adapter\" #adapterSelect>\n <mat-option>None</mat-option>\n @for (adapter of sampleAdaptors; track adapter) {\n <mat-option [value]=\"adapter.value\">\n {{adapter.label}}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>Mapper (Model)</mat-label>\n <mat-select formControlName=\"mapper\" #mapperSelect>\n <mat-option>None</mat-option>\n @for (mapper of sampleMappers; track mapper) {\n <mat-option [value]=\"mapper.value\">\n {{mapper.label}}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n\n @if (adapterSelect.value || mapperSelect.value) {\n <div style=\"display: flex; margin-bottom: 2rem;\">\n <div style=\"flex:1\" class=\"box\">\n <h3>Adapter (Incoming)</h3>\n @if (adapterSelect.value) {\n <div>\n {{ props(adapterSelect.value) | json }}\n </div>\n } @else {\n No Transformation\n }\n </div>\n <div style=\"flex:1\" class=\"box\">\n <h3>Mapper (Outgoing)</h3>\n @if (mapperSelect.value) {\n <div>\n {{ props(mapperSelect.value) | json }}\n </div>\n } @else {\n No Transformation\n }\n </div>\n </div>\n }\n\n <div style=\"display: flex; gap: .5rem\">\n <mat-form-field appearance=\"outline\" style=\"flex:1\">\n <mat-label>RestPath (/ delimited)</mat-label>\n <input matInput placeholder=\"clients/list\" formControlName=\"path\">\n </mat-form-field>\n </div>\n <div>\n <div formArrayName=\"headers\">\n @for (task of headers.controls; track task; let i = $index) {\n <div [formGroupName]=\"i\">\n <div style=\"display: flex; gap: .5rem\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Key</mat-label>\n <input matInput placeholder=\"authentication\" formControlName=\"key\">\n </mat-form-field>\n <mat-form-field appearance=\"outline\" style=\"flex:1\">\n <mat-label>Value</mat-label>\n <input matInput placeholder=\"sample\" formControlName=\"value\">\n </mat-form-field>\n <div style=\"margin-top: .5rem;\">\n <button mat-icon-button (click)=\"removeHeader(i)\">\n <mat-icon>close</mat-icon>\n </button>\n </div>\n </div>\n </div>\n }\n </div>\n <button mat-stroked-button (click)=\"addHeader()\">Add Header</button>\n </div>\n <div style=\"margin-top: 2rem; display: flex; flex-direction:column; gap: 1rem;\">\n <div>\n <mat-slide-toggle #failedState>Retry on Failed</mat-slide-toggle>\n @if (failedState.checked) {\n <div style=\"display: flex; gap: .5rem; margin-top: 1rem;\" formGroupName=\"retry\">\n <mat-form-field appearance=\"outline\">\n <mat-label>#of Times</mat-label>\n <input matInput placeholder=\"3\" formControlName=\"times\" value=\"3\">\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>Delay Until Next</mat-label>\n <input matInput placeholder=\"3\" formControlName=\"delay\" value=\"3\">\n </mat-form-field>\n </div>\n }\n </div>\n <div>\n <mat-slide-toggle #pollingState>Polling</mat-slide-toggle>\n @if (pollingState.checked) {\n <div>\n <mat-form-field appearance=\"outline\" style=\"margin-top: 1rem\">\n <mat-label>#of Seconds</mat-label>\n <input matInput placeholder=\"3\" formControlName=\"polling\" value=\"3\">\n </mat-form-field>\n </div>\n }\n </div>\n <div>\n <mat-slide-toggle #DBState>Database Storage</mat-slide-toggle>\n @if (DBState.checked) {\n <div style=\"margin-top: 1rem\" formGroupName=\"database\">\n <div style=\"display: flex; gap: .5rem;\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Table Name</mat-label>\n <input matInput placeholder=\"table\" formControlName=\"table\" value=\"\">\n </mat-form-field>\n <div>\n <mat-form-field appearance=\"outline\">\n <mat-label>Expires In</mat-label>\n <mat-select formControlName=\"expiresIn\">\n <mat-option value=\"1m\">One Minute</mat-option>\n <mat-option value=\"1h\">One Hour</mat-option>\n <mat-option value=\"1d\">One Day</mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n </div>\n <div style=\"display: flex; gap: .5rem;\">\n <mat-form-field appearance=\"outline\" style=\"flex:1\">\n <mat-label>Watch Params (comma delimited)</mat-label>\n <input matInput placeholder=\"sort,active\" formControlName=\"watchParams\" value=\"\">\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>Watch Param Expiry</mat-label>\n <mat-select formControlName=\"watchExpiresAt\">\n <mat-option value=\"never\">Never</mat-option>\n <mat-option value=\"10s\">10 Seconds</mat-option>\n <mat-option value=\"30s\">30 Seconds</mat-option>\n <mat-option value=\"1m\">1 Minute</mat-option>\n <mat-option value=\"5m\">5 Minutes</mat-option>\n <mat-option value=\"10m\">10 Minutes</mat-option>\n <mat-option value=\"1h\">1 Hour</mat-option>\n <mat-option value=\"1d\">1 Day</mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n </div>\n }\n </div>\n <div style=\"margin-top: 1rem; display: flex;\">\n <span style=\"flex:1\"></span>\n <button mat-stroked-button (click)=\"onSetStateOptions()\" [disabled]=\"!hasChanged\">\n Set API Request Options\n </button>\n </div>\n <div>\n @if ((error$ | async); as error) {\n <mat-error>\n {{ error }}\n </mat-error>\n }\n </div>\n </div>\n </div>\n\n <div style=\"margin-bottom: 1rem; margin-top: 2rem;\">\n @if ((isPending$ | async)) {\n <mat-progress-bar mode=\"indeterminate\"\n ></mat-progress-bar>\n }\n @if (pollingState.checked) {\n <div>\n @if (!(isPending$ | async) && (this.countdown$ | async) || -1 > 0) {\n <mat-progress-bar mode=\"determinate\"\n [value]=\"(this.countdown$ | async)\"\n ></mat-progress-bar>\n }\n </div>\n }\n\n </div>\n\n @if ((GET$ | async); as data) {\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1;\">CURRENT STATE ({{ dataType }})</h2>\n </div>\n @if (data.length > 1) {\n <div>\n <mat-form-field appearance=\"outline\">\n <mat-label>Records</mat-label>\n <mat-select [formControl]=\"selectedRecord\" [disabled]=\"data === null && data?.length === 0\">\n <mat-option [value]=\"\">None</mat-option>\n @for (item of data; track item) {\n <mat-option [value]=\"item\">\n {{item.name || (item.first_name) | titlecase}}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n }\n @if ((dataObservable$ | async); as dataRecord) {\n <div>\n @if ((selectedRecord$ | async); as record) {\n <div>\n {{ record | json }}\n </div>\n } @else {\n No Record Selected from State\n }\n </div>\n }\n <div style=\"margin-top: 1rem;\">\n @if (data !== null && data?.length > 0) {\n <span>\n State contains a Total of {{ data.length }} Records\n </span>\n } @else {\n No Records\n }\n <div style=\"display: flex; gap: .5rem; text-align: end; justify-content: flex-end;\">\n <button class=\"btn\" mat-stroked-button (click)=\"onClearRecords()\">Clear</button>\n </div>\n </div>\n </div>\n }\n\n\n\n\n <div style=\"margin-top: 1rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">GET Request ({{ dataType }})</h2>\n <div>\n <button mat-raised-button (click)=\"onGetRequest()\" [disabled]=\"hasChanged\" class=\"btn\">Request</button>\n </div>\n </div>\n\n @if ((GET_error$ | async); as get_error) {\n <div style=\"margin-top: .5rem;\">\n <mat-error>{{ get_error }}</mat-error>\n </div>\n }\n\n <div style=\"margin-top: 1rem;\">\n <!-- <div [innerHTML]=\"(GET$ | async) | jsonv\"></div> -->\n @if ((GET$ | async); as getData) {\n <div>{{ getData | json }}</div>\n }\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">POST Request</h2>\n <div>\n <button mat-raised-button (click)=\"onCreateRequest()\" [disabled]=\"hasChanged\" class=\"btn\">Request</button>\n </div>\n </div>\n\n @if ((POST_error$ | async); as post_error) {\n <div style=\"margin-top: .5rem;\">\n <mat-error>{{ post_error }}</mat-error>\n </div>\n }\n\n <div style=\"margin-top: 1rem;\">\n <!-- <div [innerHTML]=\"(POST$ | async) | jsonv\"></div> -->\n @if ((POST$ | async); as postData) {\n <div>{{ postData | json }}</div>\n }\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">PUT Request</h2>\n <div>\n <button mat-raised-button (click)=\"onUpdateRequest()\" [disabled]=\"hasChanged\" class=\"btn\">Request</button>\n </div>\n </div>\n\n @if ((PUT_error$ | async); as put_error) {\n <div style=\"margin-top: .5rem;\">\n <mat-error>{{ put_error }}</mat-error>\n </div>\n }\n\n <div style=\"margin-top: 1rem;\">\n <!-- <div [innerHTML]=\"(PUT$ | async) | jsonv\"></div> -->\n @if ((PUT$ | async); as putData) {\n <div>{{ putData | json }}</div>\n }\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">DELETE Request</h2>\n <div>\n <button mat-raised-button (click)=\"onDeleteRequest()\" [disabled]=\"hasChanged\" class=\"btn\">Request</button>\n </div>\n </div>\n\n @if ((DELETE_error$ | async); as delete_error) {\n <div style=\"margin-top: .5rem;\">\n <mat-error>{{ delete_error }}</mat-error>\n </div>\n }\n\n <div style=\"margin-top: 1rem;\">\n <!-- <div [innerHTML]=\"(DELETE$ | async) | jsonv\"></div> -->\n @if ((DELETE$ | async); as deleteData) {\n <div>{{ deleteData | json }}</div>\n }\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">Streaming GET Request</h2>\n <div style=\"display: flex; gap: 1rem; align-items: center;\">\n <div style=\"display: flex; gap: 1rem; align-items: center;\">\n {{ streamType }}\n <button mat-icon-button [matMenuTriggerFor]=\"menu\">\n <mat-icon>data_usage</mat-icon>\n </button>\n </div>\n <mat-menu #menu=\"matMenu\">\n <button mat-menu-item *ngFor=\"let item of streamTypes\" (click)=\"onStreamType(item.id)\">{{ item.value }}</button>\n </mat-menu>\n <button mat-raised-button (click)=\"onStreamRequest()\" class=\"btn\" [disabled]=\"hasChanged\">Request</button>\n </div>\n </div>\n\n @if ((STREAM_error$ | async); as stream_error) {\n <div style=\"margin-top: .5rem;\">\n <mat-error>{{ stream_error }}</mat-error>\n </div>\n }\n\n <div style=\"margin-top: 1rem;\">\n @if ((STREAM$ | async); as data) {\n <div class=\"container\">\n <table mat-table [dataSource]=\"data\" class=\"mat-elevation-z8\">\n\n <!-- Dynamic columns -->\n <ng-container *ngFor=\"let column of displayedColumns\" [matColumnDef]=\"column\">\n <th mat-header-cell *matHeaderCellDef> {{ column | titlecase }} </th>\n <td mat-cell *matCellDef=\"let element\">\n @if (isObject(element[column]); as objValue) {\n <pre style=\"margin: 0; font-size: 0.8em; white-space: pre-wrap;\">{{ objValue | json }}</pre>\n } @else {\n {{ element[column] }}\n }\n </td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: displayedColumns;\"></tr>\n </table>\n\n <!-- Debug info -->\n <div style=\"margin-top: 1rem; font-size: 0.8em; color: #666;\">\n Columns: {{ displayedColumns.join(', ') }} | Data received\n </div>\n </div>\n }\n </div>\n\n </div>\n\n</div>\n", styles: [".btn{min-width:120px}.mat-mdc-row .mat-mdc-cell{border-bottom:1px solid transparent;border-top:1px solid transparent;cursor:pointer}.mat-mdc-row:hover .mat-mdc-cell{border-color:currentColor;background-color:#f5f5f5}.container{height:400px;overflow:auto}.box{padding:10px;border:1px solid #ccc}\n"], dependencies: [{ kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i2$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i2$1.FormGroupName, selector: "[formGroupName]", inputs: ["formGroupName"] }, { kind: "directive", type: i2$1.FormArrayName, selector: "[formArrayName]", inputs: ["formArrayName"] }, { kind: "component", type: i3.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "component", type: i3.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "directive", type: i4.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "component", type: i5.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i6.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "component", type: i7.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i7.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i7.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "component", type: i8.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i9.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i9.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i9.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i9.MatColumnDef, selector: "[matColumnDef]", inputs: ["matColumnDef"] }, { kind: "directive", type: i9.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i9.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i9.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i9.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "component", type: i9.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i9.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "component", type: i8$1.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { kind: "component", type: i11.MatSlideToggle, selector: "mat-slide-toggle", inputs: ["name", "id", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "color", "disabled", "disableRipple", "tabIndex", "checked", "hideIcon", "disabledInteractive"], outputs: ["change", "toggleChange"], exportAs: ["matSlideToggle"] }, { kind: "component", type: i12.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "directive", type: i13.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }, { kind: "pipe", type: i1$1.JsonPipe, name: "json" }, { kind: "pipe", type: i1$1.TitleCasePipe, name: "titlecase" }] }); }
|
|
8917
9831
|
}
|
|
8918
9832
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: RequestManagerStateDemoComponent, decorators: [{
|
|
8919
9833
|
type: Component,
|
|
8920
|
-
args: [{ selector: 'app-request-manager-state-demo', providers: [StateManagerDemoService, HTTPManagerService], standalone: false, template: "<div style=\"margin: 2rem;\">\n\n <h2>\n HTTP Request State Manager\n </h2>\n\n <div [formGroup]=\"requestForm\" style=\"margin-top: 2rem;\">\n <div style=\"display: flex; gap: .5rem\">\n <mat-form-field appearance=\"outline\">\n <mat-label>State Data Type</mat-label>\n <mat-select formControlName=\"datatype\">\n <mat-option value=\"ARRAY\">Array</mat-option>\n <mat-option value=\"OBJECT\">Object</mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n <div style=\"display: flex; gap: .5rem\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Adapter (Model)</mat-label>\n <mat-select formControlName=\"adapter\" #adapterSelect>\n <mat-option>None</mat-option>\n @for (adapter of sampleAdaptors; track adapter) {\n <mat-option [value]=\"adapter.value\">\n {{adapter.label}}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>Mapper (Model)</mat-label>\n <mat-select formControlName=\"mapper\" #mapperSelect>\n <mat-option>None</mat-option>\n @for (mapper of sampleMappers; track mapper) {\n <mat-option [value]=\"mapper.value\">\n {{mapper.label}}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n\n @if (adapterSelect.value || mapperSelect.value) {\n <div style=\"display: flex; margin-bottom: 2rem;\">\n <div style=\"flex:1\" class=\"box\">\n <h3>Adapter (Incoming)</h3>\n @if (adapterSelect.value) {\n <div>\n {{ props(adapterSelect.value) | json }}\n </div>\n } @else {\n No Transformation\n }\n </div>\n <div style=\"flex:1\" class=\"box\">\n <h3>Mapper (Outgoing)</h3>\n @if (mapperSelect.value) {\n <div>\n {{ props(mapperSelect.value) | json }}\n </div>\n } @else {\n No Transformation\n }\n </div>\n </div>\n }\n\n <div>\n <mat-form-field appearance=\"outline\">\n <mat-label>RestPath (/ delimited)</mat-label>\n <input matInput placeholder=\"clients/list\" formControlName=\"path\">\n </mat-form-field>\n </div>\n <div>\n <div formArrayName=\"headers\">\n @for (task of headers.controls; track task; let i = $index) {\n <div [formGroupName]=\"i\">\n <div style=\"display: flex; gap: .5rem\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Key</mat-label>\n <input matInput placeholder=\"authentication\" formControlName=\"key\">\n </mat-form-field>\n <mat-form-field appearance=\"outline\" style=\"flex:1\">\n <mat-label>Value</mat-label>\n <input matInput placeholder=\"sample\" formControlName=\"value\">\n </mat-form-field>\n <div style=\"margin-top: .5rem;\">\n <button mat-icon-button (click)=\"removeHeader(i)\">\n <mat-icon>close</mat-icon>\n </button>\n </div>\n </div>\n </div>\n }\n </div>\n <button mat-stroked-button (click)=\"addHeader()\">Add Header</button>\n </div>\n <div style=\"margin-top: 2rem; display: flex; flex-direction:column; gap: 1rem;\">\n <div>\n <mat-slide-toggle #failedState>Retry on Failed</mat-slide-toggle>\n @if (failedState.checked) {\n <div style=\"display: flex; gap: .5rem; margin-top: 1rem;\" formGroupName=\"retry\">\n <mat-form-field appearance=\"outline\">\n <mat-label>#of Times</mat-label>\n <input matInput placeholder=\"3\" formControlName=\"times\" value=\"3\">\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>Delay Until Next</mat-label>\n <input matInput placeholder=\"3\" formControlName=\"delay\" value=\"3\">\n </mat-form-field>\n </div>\n }\n </div>\n <div>\n <mat-slide-toggle #pollingState>Polling</mat-slide-toggle>\n @if (pollingState.checked) {\n <div>\n <mat-form-field appearance=\"outline\" style=\"margin-top: 1rem\">\n <mat-label>#of Seconds</mat-label>\n <input matInput placeholder=\"3\" formControlName=\"polling\" value=\"3\">\n </mat-form-field>\n </div>\n }\n </div>\n <div>\n <mat-slide-toggle #DBState>Database Storage</mat-slide-toggle>\n @if (DBState.checked) {\n <div style=\"display: flex; gap: .5rem; margin-top: 1rem\" formGroupName=\"database\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Table Name</mat-label>\n <input matInput placeholder=\"table\" formControlName=\"table\" value=\"\">\n </mat-form-field>\n <div>\n <mat-form-field appearance=\"outline\">\n <mat-label>Expires In</mat-label>\n <mat-select formControlName=\"expiresIn\">\n <mat-option value=\"1m\">One Minute</mat-option>\n <mat-option value=\"1h\">One Hour</mat-option>\n <mat-option value=\"1d\">One Day</mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n </div>\n }\n </div>\n <div style=\"margin-top: 1rem; display: flex;\">\n <span style=\"flex:1\"></span>\n <button mat-stroked-button (click)=\"onSetStateOptions()\" [disabled]=\"!hasChanged\">\n Set API Request Options\n </button>\n </div>\n <div>\n @if ((error$ | async); as error) {\n <mat-error>\n {{ error }}\n </mat-error>\n }\n </div>\n </div>\n </div>\n\n <div style=\"margin-bottom: 1rem; margin-top: 2rem;\">\n @if ((isPending$ | async)) {\n <mat-progress-bar mode=\"indeterminate\"\n ></mat-progress-bar>\n }\n @if (pollingState.checked) {\n <div>\n @if (!(isPending$ | async) && (this.countdown$ | async) || -1 > 0) {\n <mat-progress-bar mode=\"determinate\"\n [value]=\"(this.countdown$ | async)\"\n ></mat-progress-bar>\n }\n </div>\n }\n\n </div>\n\n @if ((GET$ | async); as data) {\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1;\">CURRENT STATE ({{ dataType }})</h2>\n </div>\n @if (data.length > 1) {\n <div>\n <mat-form-field appearance=\"outline\">\n <mat-label>Records</mat-label>\n <mat-select [formControl]=\"selectedRecord\" [disabled]=\"data === null && data?.length === 0\">\n <mat-option [value]=\"\">None</mat-option>\n @for (item of data; track item) {\n <mat-option [value]=\"item\">\n {{item.name || (item.first_name) | titlecase}}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n }\n @if ((dataObservable$ | async); as dataRecord) {\n <div>\n @if ((selectedRecord$ | async); as record) {\n <div>\n {{ record | json }}\n </div>\n } @else {\n No Record Selected from State\n }\n </div>\n }\n <div style=\"margin-top: 1rem;\">\n @if (data !== null && data?.length > 0) {\n <span>\n State contains a Total of {{ data.length }} Records\n </span>\n } @else {\n No Records\n }\n <div style=\"display: flex; gap: .5rem; text-align: end; justify-content: flex-end;\">\n <button class=\"btn\" mat-stroked-button (click)=\"onClearRecords()\">Clear</button>\n </div>\n </div>\n </div>\n }\n\n\n\n\n <div style=\"margin-top: 1rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">GET Request ({{ dataType }})</h2>\n <div>\n <button mat-raised-button (click)=\"onGetRequest()\" [disabled]=\"hasChanged\" class=\"btn\">Request</button>\n </div>\n </div>\n\n @if ((GET_error$ | async); as get_error) {\n <div style=\"margin-top: .5rem;\">\n <mat-error>{{ get_error }}</mat-error>\n </div>\n }\n\n <div style=\"margin-top: 1rem;\">\n <!-- <div [innerHTML]=\"(GET$ | async) | jsonv\"></div> -->\n @if ((GET$ | async); as getData) {\n <div>{{ getData | json }}</div>\n }\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">POST Request</h2>\n <div>\n <button mat-raised-button (click)=\"onCreateRequest()\" [disabled]=\"hasChanged\" class=\"btn\">Request</button>\n </div>\n </div>\n\n @if ((POST_error$ | async); as post_error) {\n <div style=\"margin-top: .5rem;\">\n <mat-error>{{ post_error }}</mat-error>\n </div>\n }\n\n <div style=\"margin-top: 1rem;\">\n <!-- <div [innerHTML]=\"(POST$ | async) | jsonv\"></div> -->\n @if ((POST$ | async); as postData) {\n <div>{{ postData | json }}</div>\n }\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">PUT Request</h2>\n <div>\n <button mat-raised-button (click)=\"onUpdateRequest()\" [disabled]=\"hasChanged\" class=\"btn\">Request</button>\n </div>\n </div>\n\n @if ((PUT_error$ | async); as put_error) {\n <div style=\"margin-top: .5rem;\">\n <mat-error>{{ put_error }}</mat-error>\n </div>\n }\n\n <div style=\"margin-top: 1rem;\">\n <!-- <div [innerHTML]=\"(PUT$ | async) | jsonv\"></div> -->\n @if ((PUT$ | async); as putData) {\n <div>{{ putData | json }}</div>\n }\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">DELETE Request</h2>\n <div>\n <button mat-raised-button (click)=\"onDeleteRequest()\" [disabled]=\"hasChanged\" class=\"btn\">Request</button>\n </div>\n </div>\n\n @if ((DELETE_error$ | async); as delete_error) {\n <div style=\"margin-top: .5rem;\">\n <mat-error>{{ delete_error }}</mat-error>\n </div>\n }\n\n <div style=\"margin-top: 1rem;\">\n <!-- <div [innerHTML]=\"(DELETE$ | async) | jsonv\"></div> -->\n @if ((DELETE$ | async); as deleteData) {\n <div>{{ deleteData | json }}</div>\n }\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">Streaming GET Request</h2>\n <div style=\"display: flex; gap: 1rem; align-items: center;\">\n <div style=\"display: flex; gap: 1rem; align-items: center;\">\n {{ streamType }}\n <button mat-icon-button [matMenuTriggerFor]=\"menu\">\n <mat-icon>data_usage</mat-icon>\n </button>\n </div>\n <mat-menu #menu=\"matMenu\">\n <button mat-menu-item *ngFor=\"let item of streamTypes\" (click)=\"onStreamType(item.id)\">{{ item.value }}</button>\n </mat-menu>\n <button mat-raised-button (click)=\"onStreamRequest()\" class=\"btn\" [disabled]=\"hasChanged\">Request</button>\n </div>\n </div>\n\n @if ((STREAM_error$ | async); as stream_error) {\n <div style=\"margin-top: .5rem;\">\n <mat-error>{{ stream_error }}</mat-error>\n </div>\n }\n\n <div style=\"margin-top: 1rem;\">\n @if ((STREAM$ | async); as data) {\n <div class=\"container\">\n <table mat-table [dataSource]=\"data\" class=\"mat-elevation-z8\">\n\n <!-- Dynamic columns -->\n <ng-container *ngFor=\"let column of displayedColumns\" [matColumnDef]=\"column\">\n <th mat-header-cell *matHeaderCellDef> {{ column | titlecase }} </th>\n <td mat-cell *matCellDef=\"let element\">\n @if (isObject(element[column]); as objValue) {\n <pre style=\"margin: 0; font-size: 0.8em; white-space: pre-wrap;\">{{ objValue | json }}</pre>\n } @else {\n {{ element[column] }}\n }\n </td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: displayedColumns;\"></tr>\n </table>\n\n <!-- Debug info -->\n <div style=\"margin-top: 1rem; font-size: 0.8em; color: #666;\">\n Columns: {{ displayedColumns.join(', ') }} | Data received\n </div>\n </div>\n }\n </div>\n\n </div>\n\n</div>\n", styles: [".btn{min-width:120px}.mat-mdc-row .mat-mdc-cell{border-bottom:1px solid transparent;border-top:1px solid transparent;cursor:pointer}.mat-mdc-row:hover .mat-mdc-cell{border-color:currentColor;background-color:#f5f5f5}.container{height:400px;overflow:auto}.box{padding:10px;border:1px solid #ccc}\n"] }]
|
|
9834
|
+
args: [{ selector: 'app-request-manager-state-demo', providers: [StateManagerDemoService, HTTPManagerService], standalone: false, template: "<div style=\"margin: 2rem;\">\n\n <h2>\n HTTP Request State Manager\n </h2>\n\n <div [formGroup]=\"requestForm\" style=\"margin-top: 2rem;\">\n <div style=\"display: flex; gap: .5rem\">\n <mat-form-field appearance=\"outline\">\n <mat-label>State Data Type</mat-label>\n <mat-select formControlName=\"datatype\">\n <mat-option value=\"ARRAY\">Array</mat-option>\n <mat-option value=\"OBJECT\">Object</mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n <div style=\"display: flex; gap: .5rem\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Adapter (Model)</mat-label>\n <mat-select formControlName=\"adapter\" #adapterSelect>\n <mat-option>None</mat-option>\n @for (adapter of sampleAdaptors; track adapter) {\n <mat-option [value]=\"adapter.value\">\n {{adapter.label}}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>Mapper (Model)</mat-label>\n <mat-select formControlName=\"mapper\" #mapperSelect>\n <mat-option>None</mat-option>\n @for (mapper of sampleMappers; track mapper) {\n <mat-option [value]=\"mapper.value\">\n {{mapper.label}}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n\n @if (adapterSelect.value || mapperSelect.value) {\n <div style=\"display: flex; margin-bottom: 2rem;\">\n <div style=\"flex:1\" class=\"box\">\n <h3>Adapter (Incoming)</h3>\n @if (adapterSelect.value) {\n <div>\n {{ props(adapterSelect.value) | json }}\n </div>\n } @else {\n No Transformation\n }\n </div>\n <div style=\"flex:1\" class=\"box\">\n <h3>Mapper (Outgoing)</h3>\n @if (mapperSelect.value) {\n <div>\n {{ props(mapperSelect.value) | json }}\n </div>\n } @else {\n No Transformation\n }\n </div>\n </div>\n }\n\n <div style=\"display: flex; gap: .5rem\">\n <mat-form-field appearance=\"outline\" style=\"flex:1\">\n <mat-label>RestPath (/ delimited)</mat-label>\n <input matInput placeholder=\"clients/list\" formControlName=\"path\">\n </mat-form-field>\n </div>\n <div>\n <div formArrayName=\"headers\">\n @for (task of headers.controls; track task; let i = $index) {\n <div [formGroupName]=\"i\">\n <div style=\"display: flex; gap: .5rem\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Key</mat-label>\n <input matInput placeholder=\"authentication\" formControlName=\"key\">\n </mat-form-field>\n <mat-form-field appearance=\"outline\" style=\"flex:1\">\n <mat-label>Value</mat-label>\n <input matInput placeholder=\"sample\" formControlName=\"value\">\n </mat-form-field>\n <div style=\"margin-top: .5rem;\">\n <button mat-icon-button (click)=\"removeHeader(i)\">\n <mat-icon>close</mat-icon>\n </button>\n </div>\n </div>\n </div>\n }\n </div>\n <button mat-stroked-button (click)=\"addHeader()\">Add Header</button>\n </div>\n <div style=\"margin-top: 2rem; display: flex; flex-direction:column; gap: 1rem;\">\n <div>\n <mat-slide-toggle #failedState>Retry on Failed</mat-slide-toggle>\n @if (failedState.checked) {\n <div style=\"display: flex; gap: .5rem; margin-top: 1rem;\" formGroupName=\"retry\">\n <mat-form-field appearance=\"outline\">\n <mat-label>#of Times</mat-label>\n <input matInput placeholder=\"3\" formControlName=\"times\" value=\"3\">\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>Delay Until Next</mat-label>\n <input matInput placeholder=\"3\" formControlName=\"delay\" value=\"3\">\n </mat-form-field>\n </div>\n }\n </div>\n <div>\n <mat-slide-toggle #pollingState>Polling</mat-slide-toggle>\n @if (pollingState.checked) {\n <div>\n <mat-form-field appearance=\"outline\" style=\"margin-top: 1rem\">\n <mat-label>#of Seconds</mat-label>\n <input matInput placeholder=\"3\" formControlName=\"polling\" value=\"3\">\n </mat-form-field>\n </div>\n }\n </div>\n <div>\n <mat-slide-toggle #DBState>Database Storage</mat-slide-toggle>\n @if (DBState.checked) {\n <div style=\"margin-top: 1rem\" formGroupName=\"database\">\n <div style=\"display: flex; gap: .5rem;\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Table Name</mat-label>\n <input matInput placeholder=\"table\" formControlName=\"table\" value=\"\">\n </mat-form-field>\n <div>\n <mat-form-field appearance=\"outline\">\n <mat-label>Expires In</mat-label>\n <mat-select formControlName=\"expiresIn\">\n <mat-option value=\"1m\">One Minute</mat-option>\n <mat-option value=\"1h\">One Hour</mat-option>\n <mat-option value=\"1d\">One Day</mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n </div>\n <div style=\"display: flex; gap: .5rem;\">\n <mat-form-field appearance=\"outline\" style=\"flex:1\">\n <mat-label>Watch Params (comma delimited)</mat-label>\n <input matInput placeholder=\"sort,active\" formControlName=\"watchParams\" value=\"\">\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>Watch Param Expiry</mat-label>\n <mat-select formControlName=\"watchExpiresAt\">\n <mat-option value=\"never\">Never</mat-option>\n <mat-option value=\"10s\">10 Seconds</mat-option>\n <mat-option value=\"30s\">30 Seconds</mat-option>\n <mat-option value=\"1m\">1 Minute</mat-option>\n <mat-option value=\"5m\">5 Minutes</mat-option>\n <mat-option value=\"10m\">10 Minutes</mat-option>\n <mat-option value=\"1h\">1 Hour</mat-option>\n <mat-option value=\"1d\">1 Day</mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n </div>\n }\n </div>\n <div style=\"margin-top: 1rem; display: flex;\">\n <span style=\"flex:1\"></span>\n <button mat-stroked-button (click)=\"onSetStateOptions()\" [disabled]=\"!hasChanged\">\n Set API Request Options\n </button>\n </div>\n <div>\n @if ((error$ | async); as error) {\n <mat-error>\n {{ error }}\n </mat-error>\n }\n </div>\n </div>\n </div>\n\n <div style=\"margin-bottom: 1rem; margin-top: 2rem;\">\n @if ((isPending$ | async)) {\n <mat-progress-bar mode=\"indeterminate\"\n ></mat-progress-bar>\n }\n @if (pollingState.checked) {\n <div>\n @if (!(isPending$ | async) && (this.countdown$ | async) || -1 > 0) {\n <mat-progress-bar mode=\"determinate\"\n [value]=\"(this.countdown$ | async)\"\n ></mat-progress-bar>\n }\n </div>\n }\n\n </div>\n\n @if ((GET$ | async); as data) {\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1;\">CURRENT STATE ({{ dataType }})</h2>\n </div>\n @if (data.length > 1) {\n <div>\n <mat-form-field appearance=\"outline\">\n <mat-label>Records</mat-label>\n <mat-select [formControl]=\"selectedRecord\" [disabled]=\"data === null && data?.length === 0\">\n <mat-option [value]=\"\">None</mat-option>\n @for (item of data; track item) {\n <mat-option [value]=\"item\">\n {{item.name || (item.first_name) | titlecase}}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n }\n @if ((dataObservable$ | async); as dataRecord) {\n <div>\n @if ((selectedRecord$ | async); as record) {\n <div>\n {{ record | json }}\n </div>\n } @else {\n No Record Selected from State\n }\n </div>\n }\n <div style=\"margin-top: 1rem;\">\n @if (data !== null && data?.length > 0) {\n <span>\n State contains a Total of {{ data.length }} Records\n </span>\n } @else {\n No Records\n }\n <div style=\"display: flex; gap: .5rem; text-align: end; justify-content: flex-end;\">\n <button class=\"btn\" mat-stroked-button (click)=\"onClearRecords()\">Clear</button>\n </div>\n </div>\n </div>\n }\n\n\n\n\n <div style=\"margin-top: 1rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">GET Request ({{ dataType }})</h2>\n <div>\n <button mat-raised-button (click)=\"onGetRequest()\" [disabled]=\"hasChanged\" class=\"btn\">Request</button>\n </div>\n </div>\n\n @if ((GET_error$ | async); as get_error) {\n <div style=\"margin-top: .5rem;\">\n <mat-error>{{ get_error }}</mat-error>\n </div>\n }\n\n <div style=\"margin-top: 1rem;\">\n <!-- <div [innerHTML]=\"(GET$ | async) | jsonv\"></div> -->\n @if ((GET$ | async); as getData) {\n <div>{{ getData | json }}</div>\n }\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">POST Request</h2>\n <div>\n <button mat-raised-button (click)=\"onCreateRequest()\" [disabled]=\"hasChanged\" class=\"btn\">Request</button>\n </div>\n </div>\n\n @if ((POST_error$ | async); as post_error) {\n <div style=\"margin-top: .5rem;\">\n <mat-error>{{ post_error }}</mat-error>\n </div>\n }\n\n <div style=\"margin-top: 1rem;\">\n <!-- <div [innerHTML]=\"(POST$ | async) | jsonv\"></div> -->\n @if ((POST$ | async); as postData) {\n <div>{{ postData | json }}</div>\n }\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">PUT Request</h2>\n <div>\n <button mat-raised-button (click)=\"onUpdateRequest()\" [disabled]=\"hasChanged\" class=\"btn\">Request</button>\n </div>\n </div>\n\n @if ((PUT_error$ | async); as put_error) {\n <div style=\"margin-top: .5rem;\">\n <mat-error>{{ put_error }}</mat-error>\n </div>\n }\n\n <div style=\"margin-top: 1rem;\">\n <!-- <div [innerHTML]=\"(PUT$ | async) | jsonv\"></div> -->\n @if ((PUT$ | async); as putData) {\n <div>{{ putData | json }}</div>\n }\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">DELETE Request</h2>\n <div>\n <button mat-raised-button (click)=\"onDeleteRequest()\" [disabled]=\"hasChanged\" class=\"btn\">Request</button>\n </div>\n </div>\n\n @if ((DELETE_error$ | async); as delete_error) {\n <div style=\"margin-top: .5rem;\">\n <mat-error>{{ delete_error }}</mat-error>\n </div>\n }\n\n <div style=\"margin-top: 1rem;\">\n <!-- <div [innerHTML]=\"(DELETE$ | async) | jsonv\"></div> -->\n @if ((DELETE$ | async); as deleteData) {\n <div>{{ deleteData | json }}</div>\n }\n </div>\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">Streaming GET Request</h2>\n <div style=\"display: flex; gap: 1rem; align-items: center;\">\n <div style=\"display: flex; gap: 1rem; align-items: center;\">\n {{ streamType }}\n <button mat-icon-button [matMenuTriggerFor]=\"menu\">\n <mat-icon>data_usage</mat-icon>\n </button>\n </div>\n <mat-menu #menu=\"matMenu\">\n <button mat-menu-item *ngFor=\"let item of streamTypes\" (click)=\"onStreamType(item.id)\">{{ item.value }}</button>\n </mat-menu>\n <button mat-raised-button (click)=\"onStreamRequest()\" class=\"btn\" [disabled]=\"hasChanged\">Request</button>\n </div>\n </div>\n\n @if ((STREAM_error$ | async); as stream_error) {\n <div style=\"margin-top: .5rem;\">\n <mat-error>{{ stream_error }}</mat-error>\n </div>\n }\n\n <div style=\"margin-top: 1rem;\">\n @if ((STREAM$ | async); as data) {\n <div class=\"container\">\n <table mat-table [dataSource]=\"data\" class=\"mat-elevation-z8\">\n\n <!-- Dynamic columns -->\n <ng-container *ngFor=\"let column of displayedColumns\" [matColumnDef]=\"column\">\n <th mat-header-cell *matHeaderCellDef> {{ column | titlecase }} </th>\n <td mat-cell *matCellDef=\"let element\">\n @if (isObject(element[column]); as objValue) {\n <pre style=\"margin: 0; font-size: 0.8em; white-space: pre-wrap;\">{{ objValue | json }}</pre>\n } @else {\n {{ element[column] }}\n }\n </td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: displayedColumns;\"></tr>\n </table>\n\n <!-- Debug info -->\n <div style=\"margin-top: 1rem; font-size: 0.8em; color: #666;\">\n Columns: {{ displayedColumns.join(', ') }} | Data received\n </div>\n </div>\n }\n </div>\n\n </div>\n\n</div>\n", styles: [".btn{min-width:120px}.mat-mdc-row .mat-mdc-cell{border-bottom:1px solid transparent;border-top:1px solid transparent;cursor:pointer}.mat-mdc-row:hover .mat-mdc-cell{border-color:currentColor;background-color:#f5f5f5}.container{height:400px;overflow:auto}.box{padding:10px;border:1px solid #ccc}\n"] }]
|
|
8921
9835
|
}], ctorParameters: () => [], propDecorators: { server: [{
|
|
8922
9836
|
type: Input
|
|
8923
9837
|
}], adapter: [{
|
|
@@ -8930,6 +9844,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
8930
9844
|
}], pollingState: [{
|
|
8931
9845
|
type: ViewChild,
|
|
8932
9846
|
args: ["pollingState", { static: true }]
|
|
9847
|
+
}], DBState: [{
|
|
9848
|
+
type: ViewChild,
|
|
9849
|
+
args: ["DBState", { static: true }]
|
|
8933
9850
|
}] } });
|
|
8934
9851
|
|
|
8935
9852
|
class RequestManagerDemoComponent {
|
|
@@ -9934,6 +10851,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
9934
10851
|
}]
|
|
9935
10852
|
}] });
|
|
9936
10853
|
|
|
10854
|
+
class UserData {
|
|
10855
|
+
constructor(ldap = '', name = '', email = '', color = RandomPaletteColor()) {
|
|
10856
|
+
this.ldap = ldap;
|
|
10857
|
+
this.name = name;
|
|
10858
|
+
this.email = email;
|
|
10859
|
+
this.color = color;
|
|
10860
|
+
}
|
|
10861
|
+
static adapt(item) {
|
|
10862
|
+
const userName = `${item?.first_name} ${item?.last_name}`;
|
|
10863
|
+
return new UserData(item?.ldap, userName, item?.email, item?.color);
|
|
10864
|
+
}
|
|
10865
|
+
}
|
|
10866
|
+
|
|
9937
10867
|
class StateDataRequestService extends HTTPManagerStateService {
|
|
9938
10868
|
constructor() {
|
|
9939
10869
|
super(ApiRequest.adapt({
|
|
@@ -11763,5 +12693,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
11763
12693
|
* Generated bundle index. Do not edit.
|
|
11764
12694
|
*/
|
|
11765
12695
|
|
|
11766
|
-
export { ApiRequest, AppService, AsymmetricalEncryptionService, BatchOptions, BatchResult, CONFIG_SETTINGS_TOKEN, ChannelType, ConfigHTTPOptions, ConfigOptions, DataType, DatabaseDataDemoComponent, DatabaseManagerService, DatabaseStorage, DbService, ErrorDisplaySettings, GlobalStoreOptions, HTTPManagerService, HTTPManagerSignalsService, HTTPManagerStateService, HeadersService, HttpRequestManagerModule, HttpRequestServicesDemoComponent, InvalidFileInfoModel, LocalStorageDemoComponent, LocalStorageManagerService, LocalStorageOptions, LocalStorageSignalsDemoComponent, LocalStorageSignalsManagerService, LoggerService, NotificationMessage, OperationResultModel, PathQueryService, PublicMessage, Random, RandomHSLColor, RandomHexColor, RandomNumber, RandomNumbers, RandomNumbersUnique, RandomPaletteColor, RandomSignature, RandomStr, RandomVisibleColor, RequestErrorInterceptor, RequestHeadersInterceptor, RequestManagerDemoComponent, RequestManagerStateDemoComponent, RequestOptions, RequestService, RequestSignalsService, RetryOptions, SettingOptions, StateMessage, StateOperationResult, StateStorageOptions, StorageData, StorageOption, StorageType, StoreStateManagerService, StoreStateManagerSignalsService, StoreStateSignalsDemoComponent, StreamType, SymmetricalEncryptionService, TableSchemaDef, UUID, UUID_STR, UploadDemoComponent, UploadValidationErrorModel, UserData, UtilsService, WSOptions, WebSocketMessageService, WithCredentialsInterceptor, calculateBatchProgress, countdown, createChannelName, delayedRetry, isErrorState, isPendingState, isSuccessState, requestPolling, requestStreaming, streamAI, streamAuto, streamEvents, streamJSON, streamNDJSON };
|
|
12696
|
+
export { ApiRequest, AppService, AsymmetricalEncryptionService, BatchOptions, BatchResult, CONFIG_SETTINGS_TOKEN, ChannelType, ConfigHTTPOptions, ConfigOptions, DataType, DatabaseDataDemoComponent, DatabaseManagerService, DatabaseStorage, DbService, ErrorDisplaySettings, GlobalStoreOptions, HTTPManagerService, HTTPManagerSignalsService, HTTPManagerStateService, HeadersService, HttpRequestManagerModule, HttpRequestServicesDemoComponent, InvalidFileInfoModel, LocalStorageDemoComponent, LocalStorageManagerService, LocalStorageOptions, LocalStorageSignalsDemoComponent, LocalStorageSignalsManagerService, LoggerService, NormalizedRequestOptionsModel, NotificationMessage, OperationResultModel, PathQueryService, PathTrackerStateModel, PublicMessage, QueryParamsTrackerOptionsModel, QueryParamsTrackerService, QueryTrackerStateModel, Random, RandomHSLColor, RandomHexColor, RandomNumber, RandomNumbers, RandomNumbersUnique, RandomPaletteColor, RandomSignature, RandomStr, RandomVisibleColor, RequestErrorInterceptor, RequestHeadersInterceptor, RequestManagerDemoComponent, RequestManagerStateDemoComponent, RequestOptions, RequestService, RequestSignalsService, RetryOptions, SettingOptions, StateMessage, StateOperationResult, StateStorageOptions, StorageData, StorageOption, StorageType, StoreStateManagerService, StoreStateManagerSignalsService, StoreStateSignalsDemoComponent, StreamType, SymmetricalEncryptionService, TableSchemaDef, UUID, UUID_STR, UploadDemoComponent, UploadValidationErrorModel, UserData, UtilsService, WSOptions, WebSocketMessageService, WithCredentialsInterceptor, calculateBatchProgress, countdown, createChannelName, delayedRetry, isErrorState, isPendingState, isSuccessState, requestPolling, requestStreaming, streamAI, streamAuto, streamEvents, streamJSON, streamNDJSON };
|
|
11767
12697
|
//# sourceMappingURL=http-request-manager.mjs.map
|