@acontplus/ng-infrastructure 2.0.10 → 2.0.12
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,225 +1,178 @@
|
|
|
1
1
|
import { HttpContextToken, HttpResponse, HttpContext, HttpClient } from '@angular/common/http';
|
|
2
2
|
import * as i0 from '@angular/core';
|
|
3
3
|
import { inject, Injectable, signal, computed, InjectionToken } from '@angular/core';
|
|
4
|
-
import { retry,
|
|
5
|
-
import { throwError, catchError as catchError$1, switchMap, lastValueFrom } from 'rxjs';
|
|
4
|
+
import { retry, timer, switchMap, throwError, of, catchError, lastValueFrom } from 'rxjs';
|
|
6
5
|
import { NotificationService } from '@acontplus/ng-notifications';
|
|
7
6
|
import { AUTH_TOKEN, ENVIRONMENT, CORE_CONFIG, DEFAULT_CONFIG as DEFAULT_CONFIG$1 } from '@acontplus/ng-config';
|
|
8
7
|
import { Router } from '@angular/router';
|
|
8
|
+
import { finalize, catchError as catchError$1 } from 'rxjs/operators';
|
|
9
9
|
import { OverlayService } from '@acontplus/ng-components';
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
const RETRY_COUNT = 2;
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
// HTTP Context Tokens
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
/**
|
|
16
|
+
* Skip all toast notifications for a specific request.
|
|
17
|
+
* Usage: new HttpContext().set(SKIP_NOTIFICATION, true)
|
|
18
|
+
*/
|
|
12
19
|
const SKIP_NOTIFICATION = new HttpContextToken(() => false);
|
|
13
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Force-show or force-hide notifications, overriding URL/method exclusion logic.
|
|
22
|
+
* Usage: new HttpContext().set(SHOW_NOTIFICATIONS, true)
|
|
23
|
+
*/
|
|
14
24
|
const SHOW_NOTIFICATIONS = new HttpContextToken(() => undefined);
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
// Interceptor
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
15
28
|
const apiInterceptor = (req, next) => {
|
|
16
29
|
const toastr = inject(NotificationService);
|
|
17
30
|
const tokenProvider = inject(AUTH_TOKEN, { optional: true });
|
|
18
|
-
//
|
|
19
|
-
let modifiedReq = req;
|
|
31
|
+
// Attach Bearer token when available
|
|
20
32
|
const token = tokenProvider?.getToken();
|
|
21
|
-
|
|
22
|
-
modifiedReq = req.clone({
|
|
23
|
-
setHeaders: { Authorization: `Bearer ${token}` },
|
|
24
|
-
});
|
|
25
|
-
}
|
|
33
|
+
const modifiedReq = token ? req.clone({ setHeaders: { Authorization: `Bearer ${token}` } }) : req;
|
|
26
34
|
return next(modifiedReq).pipe(
|
|
27
|
-
//
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
35
|
+
// Retry only on network errors or 5xx — never on 4xx client errors.
|
|
36
|
+
// Shows toast + updates spinner message so the user knows what is happening.
|
|
37
|
+
retry({
|
|
38
|
+
count: RETRY_COUNT,
|
|
39
|
+
delay: (error, attempt) => {
|
|
40
|
+
const isRetryable = error.status === 0 || (error.status >= 500 && error.status < 600);
|
|
41
|
+
if (!isRetryable)
|
|
42
|
+
throw error; // 4xx — bubble up immediately, no retry
|
|
43
|
+
// Toast informa al usuario
|
|
44
|
+
toastr.warning({
|
|
45
|
+
message: `Reintentando solicitud (${attempt} de ${RETRY_COUNT})...`,
|
|
46
|
+
title: 'Conexión inestable',
|
|
47
|
+
config: { duration: 2500 },
|
|
48
|
+
});
|
|
49
|
+
return timer(1000 * attempt); // attempt 1 → 1 s, attempt 2 → 2 s
|
|
50
|
+
},
|
|
51
|
+
}),
|
|
52
|
+
// Handle successful responses via switchMap so we can return an Observable
|
|
53
|
+
// (needed to properly throw ApiResponse errors into the RxJS stream).
|
|
54
|
+
switchMap((event) => {
|
|
31
55
|
if (event instanceof HttpResponse) {
|
|
32
|
-
|
|
56
|
+
const standardized = standardizeApiResponse(event.body);
|
|
57
|
+
handleToastNotifications(standardized, toastr, req);
|
|
58
|
+
if (standardized.status === 'error') {
|
|
59
|
+
handleApiResponseError(standardized, toastr, req);
|
|
60
|
+
return throwError(() => standardized);
|
|
61
|
+
}
|
|
62
|
+
return of(transformResponseForConsumers(standardized, event));
|
|
33
63
|
}
|
|
34
|
-
return event;
|
|
64
|
+
return of(event);
|
|
35
65
|
}),
|
|
36
|
-
//
|
|
37
|
-
catchError((error) =>
|
|
66
|
+
// Handle HTTP-level errors — show notification for critical errors, always re-throw.
|
|
67
|
+
catchError((error) => {
|
|
68
|
+
return handleHttpError(error, toastr);
|
|
69
|
+
}));
|
|
38
70
|
};
|
|
39
|
-
//
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
*/
|
|
43
|
-
function handleSuccessResponse(event, notificationService, req) {
|
|
44
|
-
const body = event.body;
|
|
45
|
-
// Standardize the response to always have ApiResponse structure
|
|
46
|
-
const standardizedResponse = standardizeApiResponse(body);
|
|
47
|
-
// Handle toast notifications based on standardized response
|
|
48
|
-
handleToastNotifications(standardizedResponse, notificationService, req);
|
|
49
|
-
// Return the appropriate data based on response type
|
|
50
|
-
return transformResponseForConsumers(standardizedResponse, event);
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* Handles HTTP errors (from the interceptor chain, not backend ApiResponse).
|
|
54
|
-
*/
|
|
55
|
-
function handleErrorResponse(error, notificationService) {
|
|
56
|
-
const status = error.status;
|
|
57
|
-
// Only show notifications for critical HTTP-level errors.
|
|
58
|
-
// We avoid showing toasts for 4xx errors, which are handled by the component.
|
|
59
|
-
if (status !== null && shouldShowCriticalErrorNotification(status)) {
|
|
60
|
-
const message = getCriticalErrorMessage(error);
|
|
61
|
-
const title = getErrorTitle(status);
|
|
62
|
-
notificationService.error({
|
|
63
|
-
message: message,
|
|
64
|
-
title: title,
|
|
65
|
-
config: { duration: 5000 },
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
// Always re-throw the error so components/services can handle it.
|
|
69
|
-
return throwError(() => error);
|
|
70
|
-
}
|
|
71
|
-
/**
|
|
72
|
-
* Standardizes any response to follow the ApiResponse structure
|
|
73
|
-
*/
|
|
71
|
+
// ---------------------------------------------------------------------------
|
|
72
|
+
// Standardisation
|
|
73
|
+
// ---------------------------------------------------------------------------
|
|
74
74
|
function standardizeApiResponse(body) {
|
|
75
|
-
|
|
76
|
-
if (isValidApiResponse(body)) {
|
|
75
|
+
if (isValidApiResponse(body))
|
|
77
76
|
return body;
|
|
77
|
+
// Raw object without ApiResponse wrapper
|
|
78
|
+
if (body !== null && body !== undefined && typeof body === 'object' && !('status' in body)) {
|
|
79
|
+
return wrapSuccess(body);
|
|
78
80
|
}
|
|
79
|
-
//
|
|
80
|
-
if (body && typeof body === 'object' && !('status' in body)) {
|
|
81
|
-
return {
|
|
82
|
-
status: 'success',
|
|
83
|
-
code: '200',
|
|
84
|
-
message: 'Operation completed successfully',
|
|
85
|
-
data: body,
|
|
86
|
-
timestamp: new Date().toISOString(),
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
// If it's a primitive value, wrap it
|
|
81
|
+
// Primitive value
|
|
90
82
|
if (body !== null && body !== undefined && typeof body !== 'object') {
|
|
91
|
-
return
|
|
92
|
-
status: 'success',
|
|
93
|
-
code: '200',
|
|
94
|
-
message: 'Operation completed successfully',
|
|
95
|
-
data: body,
|
|
96
|
-
timestamp: new Date().toISOString(),
|
|
97
|
-
};
|
|
83
|
+
return wrapSuccess(body);
|
|
98
84
|
}
|
|
99
|
-
//
|
|
85
|
+
// null / undefined
|
|
86
|
+
return wrapSuccess(undefined);
|
|
87
|
+
}
|
|
88
|
+
function wrapSuccess(data) {
|
|
100
89
|
return {
|
|
101
90
|
status: 'success',
|
|
102
91
|
code: '200',
|
|
103
92
|
message: 'Operation completed successfully',
|
|
93
|
+
data,
|
|
104
94
|
timestamp: new Date().toISOString(),
|
|
105
95
|
};
|
|
106
96
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
97
|
+
function isValidApiResponse(response) {
|
|
98
|
+
if (response === null || typeof response !== 'object')
|
|
99
|
+
return false;
|
|
100
|
+
const r = response;
|
|
101
|
+
return ('status' in r &&
|
|
102
|
+
'code' in r &&
|
|
103
|
+
typeof r['status'] === 'string' &&
|
|
104
|
+
['success', 'error', 'warning'].includes(r['status']));
|
|
105
|
+
}
|
|
106
|
+
// ---------------------------------------------------------------------------
|
|
107
|
+
// Notifications
|
|
108
|
+
// ---------------------------------------------------------------------------
|
|
110
109
|
function handleToastNotifications(response, notificationService, req) {
|
|
111
|
-
|
|
110
|
+
// SKIP_NOTIFICATION always wins — even over SHOW_NOTIFICATIONS
|
|
111
|
+
if (req.context.get(SKIP_NOTIFICATION))
|
|
112
|
+
return;
|
|
112
113
|
const forceShow = req.context.get(SHOW_NOTIFICATIONS);
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
if (skipNotification)
|
|
114
|
+
const autoShow = shouldShowSuccessToast(req);
|
|
115
|
+
const showNotifications = forceShow !== undefined ? forceShow : autoShow;
|
|
116
|
+
if (!showNotifications)
|
|
117
117
|
return;
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
}
|
|
127
|
-
//
|
|
128
|
-
if (response.status === 'warning' && response.warnings && response.
|
|
129
|
-
response.warnings.forEach((
|
|
130
|
-
notificationService.show({
|
|
131
|
-
type: 'warning',
|
|
132
|
-
message: warning.message,
|
|
133
|
-
});
|
|
134
|
-
});
|
|
118
|
+
if (!response.message)
|
|
119
|
+
return;
|
|
120
|
+
if (!['success', 'warning', 'error'].includes(response.status))
|
|
121
|
+
return;
|
|
122
|
+
// Primary notification — show main message once
|
|
123
|
+
notificationService.show({
|
|
124
|
+
type: response.status,
|
|
125
|
+
message: response.message,
|
|
126
|
+
});
|
|
127
|
+
// Secondary: show individual warnings only when no primary message covered them
|
|
128
|
+
if (response.status === 'warning' && response.warnings?.length && !response.message) {
|
|
129
|
+
response.warnings.forEach((w) => notificationService.show({ type: 'warning', message: w.message }));
|
|
135
130
|
}
|
|
136
|
-
//
|
|
137
|
-
if (response.status === 'error' && response.errors && response.
|
|
138
|
-
response.errors.forEach((
|
|
139
|
-
notificationService.show({
|
|
140
|
-
type: 'error',
|
|
141
|
-
message: error.message,
|
|
142
|
-
});
|
|
143
|
-
});
|
|
131
|
+
// Secondary: show individual errors only when no primary message covered them
|
|
132
|
+
if (response.status === 'error' && response.errors?.length && !response.message) {
|
|
133
|
+
response.errors.forEach((e) => notificationService.show({ type: 'error', message: e.message }));
|
|
144
134
|
}
|
|
145
135
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
136
|
+
function handleApiResponseError(response, notificationService, req) {
|
|
137
|
+
if (req.context.get(SKIP_NOTIFICATION))
|
|
138
|
+
return;
|
|
139
|
+
const message = response.message || response.errors?.[0]?.message || 'An unexpected error occurred';
|
|
140
|
+
notificationService.error({ message, config: { duration: 5000 } });
|
|
141
|
+
}
|
|
142
|
+
// ---------------------------------------------------------------------------
|
|
143
|
+
// Response transformation
|
|
144
|
+
// ---------------------------------------------------------------------------
|
|
149
145
|
function transformResponseForConsumers(response, originalEvent) {
|
|
150
146
|
switch (response.status) {
|
|
151
147
|
case 'success':
|
|
152
|
-
// For success responses, return the data if it exists, otherwise the full response
|
|
153
|
-
if (response.data !== undefined && response.data !== null) {
|
|
154
|
-
return originalEvent.clone({ body: response.data });
|
|
155
|
-
}
|
|
156
|
-
// For message-only success responses, return the full response
|
|
157
|
-
return originalEvent.clone({ body: response });
|
|
158
148
|
case 'warning':
|
|
159
|
-
//
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
return originalEvent.clone({ body: response });
|
|
149
|
+
// Unwrap data for consumers; fall back to full envelope when no data
|
|
150
|
+
return response.data !== undefined && response.data !== null
|
|
151
|
+
? originalEvent.clone({ body: response.data })
|
|
152
|
+
: originalEvent.clone({ body: response });
|
|
164
153
|
case 'error':
|
|
165
|
-
//
|
|
166
|
-
|
|
154
|
+
// Already handled in switchMap — this is a safety fallback
|
|
155
|
+
return originalEvent.clone({ body: response });
|
|
167
156
|
default:
|
|
168
157
|
return originalEvent.clone({ body: response });
|
|
169
158
|
}
|
|
170
159
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
function
|
|
175
|
-
if (
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
['success', 'error', 'warning'].includes(r['status']));
|
|
182
|
-
}
|
|
183
|
-
/**
|
|
184
|
-
* Determines whether to show success toast notifications based on the request type.
|
|
185
|
-
*/
|
|
186
|
-
function shouldShowSuccessToast(req) {
|
|
187
|
-
const url = req.url?.toLowerCase() || '';
|
|
188
|
-
const method = req.method?.toLowerCase() || '';
|
|
189
|
-
// Never show for these cases
|
|
190
|
-
const excludedPatterns = [
|
|
191
|
-
'get',
|
|
192
|
-
'/list',
|
|
193
|
-
'/search',
|
|
194
|
-
'/query',
|
|
195
|
-
'/page',
|
|
196
|
-
'/paginated',
|
|
197
|
-
'/health',
|
|
198
|
-
'/status',
|
|
199
|
-
'/ping',
|
|
200
|
-
];
|
|
201
|
-
if (excludedPatterns.some((pattern) => method === pattern || url.includes(pattern))) {
|
|
202
|
-
return false;
|
|
203
|
-
}
|
|
204
|
-
// Always show for these methods
|
|
205
|
-
if (['post', 'put', 'patch', 'delete'].includes(method)) {
|
|
206
|
-
return true;
|
|
160
|
+
// ---------------------------------------------------------------------------
|
|
161
|
+
// Error handling
|
|
162
|
+
// ---------------------------------------------------------------------------
|
|
163
|
+
function handleHttpError(error, notificationService) {
|
|
164
|
+
if (shouldShowCriticalErrorNotification(error.status)) {
|
|
165
|
+
notificationService.error({
|
|
166
|
+
message: getCriticalErrorMessage(error),
|
|
167
|
+
title: getErrorTitle(error.status),
|
|
168
|
+
config: { duration: 5000 },
|
|
169
|
+
});
|
|
207
170
|
}
|
|
208
|
-
|
|
209
|
-
return method !== 'get';
|
|
171
|
+
return throwError(() => error);
|
|
210
172
|
}
|
|
211
|
-
/**
|
|
212
|
-
* Determines if we should show a notification for this HTTP error.
|
|
213
|
-
*/
|
|
214
173
|
function shouldShowCriticalErrorNotification(status) {
|
|
215
|
-
// Show notifications for:
|
|
216
|
-
// - Network errors (status 0)
|
|
217
|
-
// - Server errors (5xx)
|
|
218
174
|
return status === 0 || (status >= 500 && status < 600);
|
|
219
175
|
}
|
|
220
|
-
/**
|
|
221
|
-
* Gets the appropriate title for error notifications.
|
|
222
|
-
*/
|
|
223
176
|
function getErrorTitle(status) {
|
|
224
177
|
if (status === 0)
|
|
225
178
|
return 'Connection Error';
|
|
@@ -227,17 +180,37 @@ function getErrorTitle(status) {
|
|
|
227
180
|
return 'Server Error';
|
|
228
181
|
return 'Error';
|
|
229
182
|
}
|
|
230
|
-
/**
|
|
231
|
-
* Gets the appropriate message for critical errors.
|
|
232
|
-
*/
|
|
233
183
|
function getCriticalErrorMessage(error) {
|
|
234
184
|
if (error.status === 0) {
|
|
235
185
|
return 'Unable to connect to the server. Please check your network connection.';
|
|
236
186
|
}
|
|
237
187
|
if (error.status >= 500) {
|
|
238
|
-
return (error.error?.message
|
|
239
|
-
}
|
|
240
|
-
return error.error?.message
|
|
188
|
+
return (error.error?.message ?? error.message ?? 'A server error occurred. Please try again later.');
|
|
189
|
+
}
|
|
190
|
+
return error.error?.message ?? error.message ?? 'An unexpected error occurred';
|
|
191
|
+
}
|
|
192
|
+
// ---------------------------------------------------------------------------
|
|
193
|
+
// Utility: decide whether to auto-show toast
|
|
194
|
+
// ---------------------------------------------------------------------------
|
|
195
|
+
const MUTABLE_METHODS = new Set(['post', 'put', 'patch', 'delete']);
|
|
196
|
+
const EXCLUDED_URL_PATTERNS = [
|
|
197
|
+
'/list',
|
|
198
|
+
'/search',
|
|
199
|
+
'/query',
|
|
200
|
+
'/page',
|
|
201
|
+
'/paginated',
|
|
202
|
+
'/health',
|
|
203
|
+
'/status',
|
|
204
|
+
'/ping',
|
|
205
|
+
];
|
|
206
|
+
function shouldShowSuccessToast(req) {
|
|
207
|
+
const method = req.method.toLowerCase();
|
|
208
|
+
const url = req.url.toLowerCase();
|
|
209
|
+
if (!MUTABLE_METHODS.has(method))
|
|
210
|
+
return false;
|
|
211
|
+
if (EXCLUDED_URL_PATTERNS.some((p) => url.includes(p)))
|
|
212
|
+
return false;
|
|
213
|
+
return true;
|
|
241
214
|
}
|
|
242
215
|
|
|
243
216
|
class LoggingService {
|
|
@@ -309,10 +282,10 @@ class LoggingService {
|
|
|
309
282
|
// e.g., Sentry, LogRocket, etc.
|
|
310
283
|
// This is a placeholder for production logging implementation
|
|
311
284
|
}
|
|
312
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.
|
|
313
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.
|
|
285
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: LoggingService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
286
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: LoggingService, providedIn: 'root' });
|
|
314
287
|
}
|
|
315
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.
|
|
288
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: LoggingService, decorators: [{
|
|
316
289
|
type: Injectable,
|
|
317
290
|
args: [{
|
|
318
291
|
providedIn: 'root',
|
|
@@ -358,10 +331,10 @@ class TenantInfo {
|
|
|
358
331
|
localStorage.removeItem('tenantId');
|
|
359
332
|
sessionStorage.removeItem('tenantId');
|
|
360
333
|
}
|
|
361
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.
|
|
362
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.
|
|
334
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TenantInfo, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
335
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TenantInfo, providedIn: 'root' });
|
|
363
336
|
}
|
|
364
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.
|
|
337
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TenantInfo, decorators: [{
|
|
365
338
|
type: Injectable,
|
|
366
339
|
args: [{
|
|
367
340
|
providedIn: 'root',
|
|
@@ -393,21 +366,25 @@ class CorrelationInfo {
|
|
|
393
366
|
getId() {
|
|
394
367
|
return this.getOrCreateCorrelationId();
|
|
395
368
|
}
|
|
396
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.
|
|
397
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.
|
|
369
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: CorrelationInfo, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
370
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: CorrelationInfo, providedIn: 'root' });
|
|
398
371
|
}
|
|
399
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.
|
|
372
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: CorrelationInfo, decorators: [{
|
|
400
373
|
type: Injectable,
|
|
401
374
|
args: [{
|
|
402
375
|
providedIn: 'root',
|
|
403
376
|
}]
|
|
404
377
|
}] });
|
|
405
378
|
|
|
406
|
-
//
|
|
379
|
+
// ---------------------------------------------------------------------------
|
|
380
|
+
// HTTP Context Tokens & helpers
|
|
381
|
+
// ---------------------------------------------------------------------------
|
|
382
|
+
/** Mark a request as using an absolute URL (skip base-URL injection). */
|
|
407
383
|
const CUSTOM_URL = new HttpContextToken(() => false);
|
|
384
|
+
/** Skip all context headers (correlation, tenant, etc.) for this request. */
|
|
408
385
|
const SKIP_CONTEXT_HEADERS = new HttpContextToken(() => false);
|
|
386
|
+
/** Merge additional headers into the request at call-site. */
|
|
409
387
|
const CUSTOM_HEADERS = new HttpContextToken(() => ({}));
|
|
410
|
-
// Helper functions for HTTP context
|
|
411
388
|
function customUrl() {
|
|
412
389
|
return new HttpContext().set(CUSTOM_URL, true);
|
|
413
390
|
}
|
|
@@ -417,10 +394,9 @@ function skipContextHeaders() {
|
|
|
417
394
|
function withCustomHeaders(headers) {
|
|
418
395
|
return new HttpContext().set(CUSTOM_HEADERS, headers);
|
|
419
396
|
}
|
|
420
|
-
// Default configuration
|
|
421
397
|
const DEFAULT_CONFIG = {
|
|
422
398
|
enableCorrelationTracking: true,
|
|
423
|
-
enableRequestLogging: false,
|
|
399
|
+
enableRequestLogging: false,
|
|
424
400
|
enableErrorLogging: true,
|
|
425
401
|
customHeaders: {},
|
|
426
402
|
clientVersion: '1.0.0',
|
|
@@ -429,100 +405,94 @@ const DEFAULT_CONFIG = {
|
|
|
429
405
|
requestIdHeader: 'Request-Id',
|
|
430
406
|
timestampHeader: 'Timestamp',
|
|
431
407
|
excludeUrls: [],
|
|
408
|
+
skipAuthUrls: ['/login', '/register', '/refresh'],
|
|
432
409
|
includeAuthToken: true,
|
|
433
410
|
baseUrlInjection: true,
|
|
434
411
|
refreshTokenCallback: undefined,
|
|
435
412
|
logoutCallback: undefined,
|
|
436
413
|
};
|
|
437
|
-
// Injection token for configuration - FIXED
|
|
438
414
|
const HTTP_CONTEXT_CONFIG = new InjectionToken('HTTP_CONTEXT_CONFIG', {
|
|
439
|
-
factory: () => DEFAULT_CONFIG,
|
|
415
|
+
factory: () => DEFAULT_CONFIG,
|
|
440
416
|
});
|
|
417
|
+
// ---------------------------------------------------------------------------
|
|
418
|
+
// Interceptor
|
|
419
|
+
// ---------------------------------------------------------------------------
|
|
441
420
|
const httpContextInterceptor = (req, next) => {
|
|
442
421
|
const tokenProvider = inject(AUTH_TOKEN, { optional: true });
|
|
443
|
-
console.log(tokenProvider);
|
|
444
422
|
const router = inject(Router);
|
|
445
423
|
const tenantService = inject(TenantInfo);
|
|
446
424
|
const correlationService = inject(CorrelationInfo);
|
|
447
425
|
const loggingService = inject(LoggingService);
|
|
448
426
|
const environment = inject(ENVIRONMENT);
|
|
449
|
-
// Get configuration (with fallback to default) - FIXED
|
|
450
427
|
const config = {
|
|
451
428
|
...DEFAULT_CONFIG,
|
|
452
|
-
// The default enableRequestLogging needs to be set based on environment here,
|
|
453
|
-
// as it's dynamic, and DEFAULT_CONFIG is static.
|
|
454
429
|
enableRequestLogging: !environment.isProduction,
|
|
455
430
|
...inject(HTTP_CONTEXT_CONFIG, { optional: true }),
|
|
456
431
|
};
|
|
457
|
-
//
|
|
432
|
+
// ── Context token flags ──────────────────────────────────────────────────
|
|
458
433
|
const isCustomUrl = req.context.get(CUSTOM_URL);
|
|
459
434
|
const skipHeaders = req.context.get(SKIP_CONTEXT_HEADERS);
|
|
460
435
|
const contextHeaders = req.context.get(CUSTOM_HEADERS);
|
|
461
|
-
|
|
462
|
-
if (skipHeaders) {
|
|
436
|
+
if (skipHeaders)
|
|
463
437
|
return next(req);
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
438
|
+
// ── URL exclusion (with ReDoS protection) ────────────────────────────────
|
|
439
|
+
const shouldExclude = config.excludeUrls.some((pattern) => {
|
|
440
|
+
try {
|
|
441
|
+
return req.url.includes(pattern) || new RegExp(pattern).test(req.url);
|
|
442
|
+
}
|
|
443
|
+
catch {
|
|
444
|
+
return false; // malformed regex — skip safely
|
|
445
|
+
}
|
|
446
|
+
});
|
|
447
|
+
if (shouldExclude)
|
|
468
448
|
return next(req);
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
// Handle URL transformation
|
|
449
|
+
// ── Auth skip check ──────────────────────────────────────────────────────
|
|
450
|
+
const skipAuth = config.skipAuthUrls.some((u) => req.url.includes(u));
|
|
451
|
+
// ── URL resolution ───────────────────────────────────────────────────────
|
|
473
452
|
const baseUrl = environment.apiBaseUrl;
|
|
474
453
|
const finalUrl = isCustomUrl || !config.baseUrlInjection ? req.url : `${baseUrl}${req.url}`;
|
|
475
|
-
//
|
|
454
|
+
// ── Header construction ──────────────────────────────────────────────────
|
|
476
455
|
const correlationId = config.enableCorrelationTracking
|
|
477
456
|
? correlationService.getOrCreateCorrelationId()
|
|
478
457
|
: crypto.randomUUID();
|
|
479
458
|
const tenantId = tenantService.getTenantId();
|
|
480
459
|
const requestId = crypto.randomUUID();
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
460
|
+
const headers = {
|
|
461
|
+
[config.requestIdHeader]: requestId,
|
|
462
|
+
[config.timestampHeader]: new Date().toISOString(),
|
|
463
|
+
};
|
|
485
464
|
if (config.enableCorrelationTracking) {
|
|
486
465
|
headers[config.correlationIdHeader] = correlationId;
|
|
487
466
|
}
|
|
488
467
|
if (tenantId) {
|
|
489
468
|
headers[config.tenantIdHeader] = tenantId;
|
|
490
469
|
}
|
|
491
|
-
headers[config.timestampHeader] = new Date().toISOString();
|
|
492
|
-
// Client information
|
|
493
470
|
if (config.clientVersion) {
|
|
494
471
|
headers['Client-Version'] = config.clientVersion;
|
|
495
472
|
}
|
|
496
|
-
// Environment client ID
|
|
497
473
|
if (environment.clientId) {
|
|
498
474
|
headers['Client-Id'] = environment.clientId;
|
|
499
475
|
}
|
|
500
|
-
// Add authorization header if configured and available
|
|
501
476
|
if (config.includeAuthToken && !skipAuth) {
|
|
502
477
|
const authToken = tokenProvider?.getToken();
|
|
503
|
-
if (authToken)
|
|
478
|
+
if (authToken)
|
|
504
479
|
headers['Authorization'] = `Bearer ${authToken}`;
|
|
505
|
-
}
|
|
506
480
|
}
|
|
507
|
-
//
|
|
481
|
+
// Static and dynamic custom headers from config
|
|
508
482
|
Object.entries(config.customHeaders).forEach(([key, value]) => {
|
|
509
483
|
headers[key] = typeof value === 'function' ? value() : value;
|
|
510
484
|
});
|
|
511
|
-
//
|
|
485
|
+
// Per-request headers from HttpContext
|
|
512
486
|
Object.entries(contextHeaders).forEach(([key, value]) => {
|
|
513
487
|
headers[key] = value;
|
|
514
488
|
});
|
|
515
|
-
//
|
|
516
|
-
if (['POST', 'PUT', 'PATCH'].includes(req.method.
|
|
517
|
-
!req.headers.has('Content-Type')) {
|
|
489
|
+
// Content-Type for mutation requests (do not override if already set)
|
|
490
|
+
if (['POST', 'PUT', 'PATCH'].includes(req.method) && !req.headers.has('Content-Type')) {
|
|
518
491
|
headers['Content-Type'] = 'application/json';
|
|
519
492
|
}
|
|
520
|
-
// Clone request
|
|
521
|
-
const enhancedReq = req.clone({
|
|
522
|
-
|
|
523
|
-
setHeaders: headers,
|
|
524
|
-
});
|
|
525
|
-
// Log outgoing request if enabled
|
|
493
|
+
// ── Clone request ────────────────────────────────────────────────────────
|
|
494
|
+
const enhancedReq = req.clone({ url: finalUrl, setHeaders: headers });
|
|
495
|
+
// ── Request logging ──────────────────────────────────────────────────────
|
|
526
496
|
if (config.enableRequestLogging) {
|
|
527
497
|
loggingService.logHttpRequest({
|
|
528
498
|
method: req.method,
|
|
@@ -536,113 +506,107 @@ const httpContextInterceptor = (req, next) => {
|
|
|
536
506
|
isCustomUrl,
|
|
537
507
|
});
|
|
538
508
|
}
|
|
539
|
-
//
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
correlationId,
|
|
568
|
-
tenantId,
|
|
569
|
-
status: error.status,
|
|
570
|
-
statusText: error.statusText,
|
|
571
|
-
message: error.message,
|
|
572
|
-
timestamp: new Date().toISOString(),
|
|
573
|
-
errorDetails: error.error,
|
|
574
|
-
environment: environment.clientId,
|
|
575
|
-
isCustomUrl,
|
|
576
|
-
headers: [], // Headers are not included in HttpErrorResponse by default, adjust if needed
|
|
577
|
-
});
|
|
578
|
-
}
|
|
579
|
-
// Handle other error scenarios
|
|
580
|
-
switch (error.status) {
|
|
581
|
-
case 403:
|
|
582
|
-
tenantService.handleForbidden();
|
|
583
|
-
break;
|
|
584
|
-
case 0:
|
|
585
|
-
loggingService.logNetworkError(correlationId);
|
|
586
|
-
break;
|
|
587
|
-
case 429:
|
|
588
|
-
loggingService.logRateLimitError(correlationId, finalUrl);
|
|
589
|
-
break;
|
|
590
|
-
}
|
|
509
|
+
// ── Execute request with unified error handling ──────────────────────────
|
|
510
|
+
const hasAuth = !!headers['Authorization'];
|
|
511
|
+
// Shared error context for both handlers below
|
|
512
|
+
const errorCtx = {
|
|
513
|
+
req,
|
|
514
|
+
finalUrl,
|
|
515
|
+
requestId,
|
|
516
|
+
correlationId,
|
|
517
|
+
tenantId,
|
|
518
|
+
isCustomUrl,
|
|
519
|
+
hasAuth,
|
|
520
|
+
config,
|
|
521
|
+
router,
|
|
522
|
+
loggingService,
|
|
523
|
+
tenantService,
|
|
524
|
+
tokenProvider,
|
|
525
|
+
environment,
|
|
526
|
+
};
|
|
527
|
+
// Base pipeline — always present
|
|
528
|
+
const base$ = next(enhancedReq).pipe(catchError((error) => handleContextError(error, errorCtx)));
|
|
529
|
+
// When auth + refresh callback are available, prepend a 401-refresh layer
|
|
530
|
+
if (!hasAuth || !config.refreshTokenCallback) {
|
|
531
|
+
return base$;
|
|
532
|
+
}
|
|
533
|
+
return next(enhancedReq).pipe(
|
|
534
|
+
// Intercept 401 first — attempt token refresh before general error handling
|
|
535
|
+
catchError((error) => {
|
|
536
|
+
if (error.status !== 401)
|
|
591
537
|
return throwError(() => error);
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
else {
|
|
595
|
-
// No auth header, use standard error handling
|
|
596
|
-
return next(enhancedReq).pipe(catchError$1((error) => {
|
|
597
|
-
// Enhanced error logging with context
|
|
598
|
-
if (config.enableErrorLogging) {
|
|
599
|
-
loggingService.logHttpError({
|
|
600
|
-
method: req.method,
|
|
601
|
-
url: finalUrl,
|
|
602
|
-
originalUrl: req.url,
|
|
603
|
-
requestId,
|
|
604
|
-
correlationId,
|
|
605
|
-
tenantId,
|
|
606
|
-
status: error.status,
|
|
607
|
-
statusText: error.statusText,
|
|
608
|
-
message: error.message,
|
|
609
|
-
timestamp: new Date().toISOString(),
|
|
610
|
-
errorDetails: error.error,
|
|
611
|
-
environment: environment.clientId,
|
|
612
|
-
isCustomUrl,
|
|
613
|
-
headers: [], // Headers are not included in HttpErrorResponse by default, adjust if needed
|
|
614
|
-
});
|
|
615
|
-
}
|
|
616
|
-
// Handle specific error scenarios
|
|
617
|
-
switch (error.status) {
|
|
618
|
-
case 401: {
|
|
619
|
-
loggingService.error('Unauthorized access - token expired or invalid');
|
|
620
|
-
// Note: Token clearing should be handled by the auth service, not infrastructure
|
|
621
|
-
router.navigate([`/${environment.loginRoute}`]);
|
|
622
|
-
break;
|
|
623
|
-
}
|
|
624
|
-
case 403:
|
|
625
|
-
tenantService.handleForbidden();
|
|
626
|
-
break;
|
|
627
|
-
case 0:
|
|
628
|
-
loggingService.logNetworkError(correlationId);
|
|
629
|
-
break;
|
|
630
|
-
case 429:
|
|
631
|
-
loggingService.logRateLimitError(correlationId, finalUrl);
|
|
632
|
-
break;
|
|
633
|
-
}
|
|
538
|
+
const refreshToken = tokenProvider?.getRefreshToken?.();
|
|
539
|
+
if (!refreshToken?.trim())
|
|
634
540
|
return throwError(() => error);
|
|
541
|
+
return config.refreshTokenCallback().pipe(switchMap(({ token }) => next(req.clone({
|
|
542
|
+
url: finalUrl,
|
|
543
|
+
setHeaders: { ...headers, Authorization: `Bearer ${token}` },
|
|
544
|
+
}))), catchError((refreshError) => {
|
|
545
|
+
config.logoutCallback?.();
|
|
546
|
+
return throwError(() => refreshError);
|
|
635
547
|
}));
|
|
636
|
-
}
|
|
548
|
+
}),
|
|
549
|
+
// General error handling (logs, redirects, tenant, rate-limit, etc.)
|
|
550
|
+
catchError((error) => handleContextError(error, errorCtx)));
|
|
637
551
|
};
|
|
638
|
-
|
|
552
|
+
function handleContextError(error, ctx) {
|
|
553
|
+
const { req, finalUrl, requestId, correlationId, tenantId, isCustomUrl, config, router, loggingService, tenantService, environment, } = ctx;
|
|
554
|
+
if (config.enableErrorLogging) {
|
|
555
|
+
loggingService.logHttpError({
|
|
556
|
+
method: req.method,
|
|
557
|
+
url: finalUrl,
|
|
558
|
+
originalUrl: req.url,
|
|
559
|
+
requestId,
|
|
560
|
+
correlationId,
|
|
561
|
+
tenantId,
|
|
562
|
+
status: error.status,
|
|
563
|
+
statusText: error.statusText,
|
|
564
|
+
message: error.message,
|
|
565
|
+
timestamp: new Date().toISOString(),
|
|
566
|
+
errorDetails: error.error,
|
|
567
|
+
environment: environment.clientId,
|
|
568
|
+
isCustomUrl,
|
|
569
|
+
headers: [],
|
|
570
|
+
});
|
|
571
|
+
}
|
|
572
|
+
switch (error.status) {
|
|
573
|
+
case 401:
|
|
574
|
+
loggingService.error('Unauthorized — redirecting to login');
|
|
575
|
+
// Let auth service clear tokens before navigation
|
|
576
|
+
config.logoutCallback?.();
|
|
577
|
+
router.navigate([`/${environment.loginRoute}`]);
|
|
578
|
+
break;
|
|
579
|
+
case 403:
|
|
580
|
+
tenantService.handleForbidden();
|
|
581
|
+
break;
|
|
582
|
+
case 0:
|
|
583
|
+
loggingService.logNetworkError(correlationId);
|
|
584
|
+
break;
|
|
585
|
+
case 429:
|
|
586
|
+
loggingService.logRateLimitError(correlationId, finalUrl);
|
|
587
|
+
break;
|
|
588
|
+
}
|
|
589
|
+
return throwError(() => error);
|
|
590
|
+
}
|
|
591
|
+
// ---------------------------------------------------------------------------
|
|
592
|
+
// Public helpers for library consumers
|
|
593
|
+
// ---------------------------------------------------------------------------
|
|
639
594
|
function createHttpContextConfig(overrides = {}) {
|
|
640
|
-
return {
|
|
641
|
-
...DEFAULT_CONFIG,
|
|
642
|
-
...overrides,
|
|
643
|
-
};
|
|
595
|
+
return { ...DEFAULT_CONFIG, ...overrides };
|
|
644
596
|
}
|
|
645
|
-
|
|
597
|
+
/**
|
|
598
|
+
* Register the interceptor configuration in your app providers.
|
|
599
|
+
*
|
|
600
|
+
* @example
|
|
601
|
+
* // app.config.ts
|
|
602
|
+
* provideHttpClient(
|
|
603
|
+
* withInterceptors([
|
|
604
|
+
* httpContextInterceptor, // 1st — adds headers, handles 401 refresh
|
|
605
|
+
* apiInterceptor, // 2nd — normalises response, shows toasts
|
|
606
|
+
* ])
|
|
607
|
+
* ),
|
|
608
|
+
* ...provideHttpContext({ clientVersion: '2.0.0' }),
|
|
609
|
+
*/
|
|
646
610
|
function provideHttpContext(config = {}) {
|
|
647
611
|
return [
|
|
648
612
|
{
|
|
@@ -652,65 +616,50 @@ function provideHttpContext(config = {}) {
|
|
|
652
616
|
];
|
|
653
617
|
}
|
|
654
618
|
|
|
655
|
-
/**
|
|
656
|
-
* Token to determine if a request should show spinner
|
|
657
|
-
* Default is true (show spinner for all requests)
|
|
658
|
-
*/
|
|
659
619
|
const SHOW_SPINNER = new HttpContextToken(() => true);
|
|
660
|
-
const requests = [];
|
|
661
|
-
/**
|
|
662
|
-
* Helper function to disable spinner for specific requests
|
|
663
|
-
* @returns HttpContext with spinner disabled
|
|
664
|
-
*/
|
|
665
620
|
function withoutSpinner() {
|
|
666
621
|
return new HttpContext().set(SHOW_SPINNER, false);
|
|
667
622
|
}
|
|
668
|
-
/**
|
|
669
|
-
* Service to track active HTTP requests
|
|
670
|
-
*/
|
|
671
623
|
class ActiveRequestsTracker {
|
|
624
|
+
requests = [];
|
|
672
625
|
get count() {
|
|
673
|
-
return requests.length;
|
|
626
|
+
return this.requests.length;
|
|
674
627
|
}
|
|
628
|
+
/** Retorna true si se pasa de 0 → 1 (primera request activa). */
|
|
675
629
|
add(request) {
|
|
676
|
-
requests.push(request);
|
|
630
|
+
this.requests.push(request);
|
|
631
|
+
return this.requests.length === 1;
|
|
677
632
|
}
|
|
633
|
+
/** Retorna true si se pasa de 1 → 0 (última request completada). */
|
|
678
634
|
remove(request) {
|
|
679
|
-
const index = requests.indexOf(request);
|
|
635
|
+
const index = this.requests.indexOf(request);
|
|
680
636
|
if (index >= 0) {
|
|
681
|
-
requests.splice(index, 1);
|
|
637
|
+
this.requests.splice(index, 1);
|
|
682
638
|
}
|
|
639
|
+
return this.requests.length === 0;
|
|
683
640
|
}
|
|
684
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.
|
|
685
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.
|
|
641
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ActiveRequestsTracker, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
642
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ActiveRequestsTracker, providedIn: 'root' });
|
|
686
643
|
}
|
|
687
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.
|
|
644
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ActiveRequestsTracker, decorators: [{
|
|
688
645
|
type: Injectable,
|
|
689
|
-
args: [{
|
|
690
|
-
providedIn: 'root',
|
|
691
|
-
}]
|
|
646
|
+
args: [{ providedIn: 'root' }]
|
|
692
647
|
}] });
|
|
693
|
-
/**
|
|
694
|
-
* Interceptor that shows/hides a loading spinner based on active HTTP requests
|
|
695
|
-
*/
|
|
696
648
|
const spinnerInterceptor = (req, next) => {
|
|
697
|
-
// Track active requests requiring spinner
|
|
698
649
|
const activeRequests = inject(ActiveRequestsTracker);
|
|
699
650
|
const overlayService = inject(OverlayService);
|
|
700
|
-
// Skip spinner if disabled for this request
|
|
701
651
|
if (!req.context.get(SHOW_SPINNER)) {
|
|
702
652
|
return next(req);
|
|
703
653
|
}
|
|
704
|
-
//
|
|
705
|
-
activeRequests.add(req);
|
|
706
|
-
|
|
707
|
-
if (activeRequests.count === 1) {
|
|
654
|
+
// showSpinner solo cuando pasamos de 0 → 1 requests
|
|
655
|
+
const isFirst = activeRequests.add(req);
|
|
656
|
+
if (isFirst) {
|
|
708
657
|
overlayService.showSpinner();
|
|
709
658
|
}
|
|
710
659
|
return next(req).pipe(finalize(() => {
|
|
711
|
-
//
|
|
712
|
-
activeRequests.remove(req);
|
|
713
|
-
if (
|
|
660
|
+
// hideSpinner solo cuando pasamos de 1 → 0 requests
|
|
661
|
+
const isEmpty = activeRequests.remove(req);
|
|
662
|
+
if (isEmpty) {
|
|
714
663
|
overlayService.hideSpinner();
|
|
715
664
|
}
|
|
716
665
|
}));
|
|
@@ -739,10 +688,10 @@ class BaseHttpRepository {
|
|
|
739
688
|
delete(path = '') {
|
|
740
689
|
return this.http.delete(this.buildUrl(path));
|
|
741
690
|
}
|
|
742
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.
|
|
743
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.
|
|
691
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: BaseHttpRepository, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
692
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: BaseHttpRepository });
|
|
744
693
|
}
|
|
745
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.
|
|
694
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: BaseHttpRepository, decorators: [{
|
|
746
695
|
type: Injectable
|
|
747
696
|
}] });
|
|
748
697
|
|
|
@@ -786,10 +735,10 @@ class GenericRepository extends BaseHttpRepository {
|
|
|
786
735
|
}
|
|
787
736
|
return params;
|
|
788
737
|
}
|
|
789
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.
|
|
790
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.
|
|
738
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: GenericRepository, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
739
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: GenericRepository });
|
|
791
740
|
}
|
|
792
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.
|
|
741
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: GenericRepository, decorators: [{
|
|
793
742
|
type: Injectable
|
|
794
743
|
}], ctorParameters: () => [] });
|
|
795
744
|
class SearchableGenericRepository extends GenericRepository {
|
|
@@ -798,10 +747,10 @@ class SearchableGenericRepository extends GenericRepository {
|
|
|
798
747
|
const params = this.buildParams(pagination, searchFilters);
|
|
799
748
|
return this.get('search', params);
|
|
800
749
|
}
|
|
801
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.
|
|
802
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.
|
|
750
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SearchableGenericRepository, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
|
|
751
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SearchableGenericRepository });
|
|
803
752
|
}
|
|
804
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.
|
|
753
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SearchableGenericRepository, decorators: [{
|
|
805
754
|
type: Injectable
|
|
806
755
|
}] });
|
|
807
756
|
|
|
@@ -844,10 +793,10 @@ class RepositoryFactory {
|
|
|
844
793
|
remove: (id) => this.http.delete(buildUrl(idToString(id))),
|
|
845
794
|
};
|
|
846
795
|
}
|
|
847
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.
|
|
848
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.
|
|
796
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: RepositoryFactory, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
797
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: RepositoryFactory, providedIn: 'root' });
|
|
849
798
|
}
|
|
850
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.
|
|
799
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: RepositoryFactory, decorators: [{
|
|
851
800
|
type: Injectable,
|
|
852
801
|
args: [{ providedIn: 'root' }]
|
|
853
802
|
}] });
|
|
@@ -979,10 +928,10 @@ class CoreConfigService {
|
|
|
979
928
|
resetConfig() {
|
|
980
929
|
this.config = this.initializeConfig();
|
|
981
930
|
}
|
|
982
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.
|
|
983
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.
|
|
931
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: CoreConfigService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
932
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: CoreConfigService, providedIn: 'root' });
|
|
984
933
|
}
|
|
985
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.
|
|
934
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: CoreConfigService, decorators: [{
|
|
986
935
|
type: Injectable,
|
|
987
936
|
args: [{
|
|
988
937
|
providedIn: 'root',
|
|
@@ -1025,7 +974,7 @@ class Command extends BaseUseCase {
|
|
|
1025
974
|
timestamp: new Date().toISOString(),
|
|
1026
975
|
}));
|
|
1027
976
|
}
|
|
1028
|
-
return this.executeInternal(request).pipe(catchError((error) => {
|
|
977
|
+
return this.executeInternal(request).pipe(catchError$1((error) => {
|
|
1029
978
|
// Log the error for debugging
|
|
1030
979
|
const logger = inject(LoggingService);
|
|
1031
980
|
logger.error('An error occurred during command execution:', error);
|
|
@@ -1037,7 +986,7 @@ class Command extends BaseUseCase {
|
|
|
1037
986
|
|
|
1038
987
|
class Query extends BaseUseCase {
|
|
1039
988
|
execute(request) {
|
|
1040
|
-
return this.executeInternal(request).pipe(catchError((error) => {
|
|
989
|
+
return this.executeInternal(request).pipe(catchError$1((error) => {
|
|
1041
990
|
const logger = inject(LoggingService);
|
|
1042
991
|
logger.error('An error occurred during query execution:', error);
|
|
1043
992
|
return throwError(() => error);
|
|
@@ -1049,5 +998,5 @@ class Query extends BaseUseCase {
|
|
|
1049
998
|
* Generated bundle index. Do not edit.
|
|
1050
999
|
*/
|
|
1051
1000
|
|
|
1052
|
-
export { ActiveRequestsTracker, AngularHttpAdapter, BaseHttpRepository, BaseUseCase, Command, CoreConfigService, CorrelationInfo, GenericRepository, HTTP_CONTEXT_CONFIG, LoggingService, Query, REPOSITORY_CONFIG, RepositoryFactory, SHOW_NOTIFICATIONS, SKIP_NOTIFICATION, SearchableGenericRepository, apiInterceptor, createCoreConfig, createHttpContextConfig, customUrl, httpContextInterceptor, provideCoreConfig, provideHttpContext, skipContextHeaders, spinnerInterceptor, withCustomHeaders, withoutSpinner };
|
|
1001
|
+
export { ActiveRequestsTracker, AngularHttpAdapter, BaseHttpRepository, BaseUseCase, Command, CoreConfigService, CorrelationInfo, GenericRepository, HTTP_CONTEXT_CONFIG, LoggingService, Query, REPOSITORY_CONFIG, RepositoryFactory, SHOW_NOTIFICATIONS, SHOW_SPINNER, SKIP_NOTIFICATION, SearchableGenericRepository, apiInterceptor, createCoreConfig, createHttpContextConfig, customUrl, httpContextInterceptor, provideCoreConfig, provideHttpContext, skipContextHeaders, spinnerInterceptor, withCustomHeaders, withoutSpinner };
|
|
1053
1002
|
//# sourceMappingURL=acontplus-ng-infrastructure.mjs.map
|