ciyidogan commited on
Commit
4e8cf3c
·
verified ·
1 Parent(s): 1eff071

Update flare-ui/src/app/services/error-handler.service.ts

Browse files
flare-ui/src/app/services/error-handler.service.ts CHANGED
@@ -1,4 +1,4 @@
1
- // error-handler.service.ts (YENİ DOSYA)
2
  // Path: /flare-ui/src/app/services/error-handler.service.ts
3
 
4
  import { ErrorHandler, Injectable, Injector } from '@angular/core';
@@ -22,172 +22,277 @@ export class GlobalErrorHandler implements ErrorHandler {
22
  constructor(private injector: Injector) {}
23
 
24
  handleError(error: Error | HttpErrorResponse): void {
25
- // Get services lazily to avoid circular dependency
26
- const snackBar = this.injector.get(MatSnackBar);
27
- const router = this.injector.get(Router);
28
-
29
- console.error('Global error caught:', error);
30
-
31
- // Handle HTTP errors
32
- if (error instanceof HttpErrorResponse) {
33
- this.handleHttpError(error, snackBar, router);
34
- } else {
35
- // Handle client-side errors
36
- this.handleClientError(error, snackBar);
 
 
 
 
 
 
37
  }
38
  }
39
 
40
  private handleHttpError(error: HttpErrorResponse, snackBar: MatSnackBar, router: Router): void {
41
- const flareError = error.error as FlareError;
42
-
43
- // Race condition error (409)
44
- if (error.status === 409 && flareError.error === 'RaceConditionError') {
45
- const snackBarRef = snackBar.open(
46
- flareError.message || 'The data was modified by another user. Please refresh and try again.',
47
- 'Refresh',
48
- {
49
- duration: 0,
50
- panelClass: ['error-snackbar', 'race-condition-snackbar']
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
  }
52
- );
53
 
54
- snackBarRef.onAction().subscribe(() => {
55
- window.location.reload();
56
- });
 
 
 
 
 
 
 
 
 
 
 
57
 
58
- // Show additional info if available
59
- if (flareError.details?.last_update_user) {
60
- console.info(`Last updated by: ${flareError.details.last_update_user} at ${flareError.details.last_update_date}`);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
  }
62
- return;
63
- }
64
-
65
- // Authentication error (401)
66
- if (error.status === 401) {
67
- snackBar.open(
68
- 'Your session has expired. Please login again.',
69
- 'Login',
70
- {
71
- duration: 5000,
72
- panelClass: ['error-snackbar']
73
- }
74
- ).onAction().subscribe(() => {
75
- router.navigate(['/login']);
76
- });
77
- return;
78
- }
79
-
80
- // Validation error (422)
81
- if (error.status === 422 && flareError.details) {
82
- const fieldErrors = flareError.details
83
- .map((d: any) => `${d.field}: ${d.message}`)
84
- .join('\n');
85
 
86
- snackBar.open(
87
- flareError.message || 'Validation failed. Please check your input.',
88
- 'Close',
89
- {
90
- duration: 8000,
91
- panelClass: ['error-snackbar', 'validation-snackbar']
92
- }
93
- );
 
 
 
 
94
 
95
- console.error('Validation errors:', flareError.details);
96
- return;
97
- }
98
-
99
- // Not found error (404)
100
- if (error.status === 404) {
101
- snackBar.open(
102
- flareError.message || 'The requested resource was not found.',
103
- 'Close',
104
- {
105
- duration: 5000,
106
- panelClass: ['error-snackbar']
107
- }
108
- );
109
- return;
110
- }
111
-
112
- // Server errors (5xx)
113
- if (error.status >= 500) {
114
- const message = flareError.message || 'A server error occurred. Please try again later.';
115
- const requestId = flareError.request_id;
 
 
 
 
 
 
 
 
 
116
 
 
 
117
  snackBar.open(
118
- requestId ? `${message} (Request ID: ${requestId})` : message,
119
  'Close',
120
  {
121
- duration: 8000,
122
- panelClass: ['error-snackbar', 'server-error-snackbar']
123
  }
124
  );
125
- return;
 
 
126
  }
127
-
128
- // Generic HTTP error
129
- snackBar.open(
130
- flareError.message || `HTTP Error ${error.status}: ${error.statusText}`,
131
- 'Close',
132
- {
133
- duration: 6000,
134
- panelClass: ['error-snackbar']
135
- }
136
- );
137
  }
138
 
139
  private handleClientError(error: Error, snackBar: MatSnackBar): void {
140
- // Check if it's a network error
141
- if (error.message.includes('NetworkError') || error.message.includes('Failed to fetch')) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
142
  snackBar.open(
143
- 'Network connection error. Please check your internet connection.',
144
- 'Retry',
145
  {
146
- duration: 0,
147
- panelClass: ['error-snackbar', 'network-error-snackbar']
148
  }
149
  ).onAction().subscribe(() => {
150
  window.location.reload();
151
  });
152
- return;
 
 
153
  }
154
-
155
- // Generic client error
 
156
  snackBar.open(
157
- 'An unexpected error occurred. Please refresh the page.',
158
- 'Refresh',
159
  {
160
- duration: 6000,
161
  panelClass: ['error-snackbar']
162
  }
163
- ).onAction().subscribe(() => {
164
- window.location.reload();
165
- });
166
  }
167
  }
168
 
169
  // Error interceptor for consistent error format
170
- import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http';
171
  import { Observable, throwError } from 'rxjs';
172
- import { catchError } from 'rxjs/operators';
173
 
174
  @Injectable()
175
  export class ErrorInterceptor implements HttpInterceptor {
 
 
176
  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
177
- return next.handle(req).pipe(
 
 
 
 
 
 
 
 
 
 
 
 
178
  catchError((error: HttpErrorResponse) => {
179
  // Log request details for debugging
180
  console.error('HTTP Error:', {
181
  url: req.url,
182
  method: req.method,
183
  status: error.status,
 
184
  error: error.error,
185
- requestId: error.headers.get('X-Request-ID')
 
186
  });
187
 
 
 
 
 
 
 
 
 
 
188
  // Re-throw to be handled by global error handler
189
- return throwError(() => error);
 
 
 
 
190
  })
191
  );
192
  }
193
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // error-handler.service.ts
2
  // Path: /flare-ui/src/app/services/error-handler.service.ts
3
 
4
  import { ErrorHandler, Injectable, Injector } from '@angular/core';
 
22
  constructor(private injector: Injector) {}
23
 
24
  handleError(error: Error | HttpErrorResponse): void {
25
+ try {
26
+ // Get services lazily to avoid circular dependency
27
+ const snackBar = this.injector.get(MatSnackBar);
28
+ const router = this.injector.get(Router);
29
+
30
+ console.error('Global error caught:', error);
31
+
32
+ // Handle HTTP errors
33
+ if (error instanceof HttpErrorResponse) {
34
+ this.handleHttpError(error, snackBar, router);
35
+ } else {
36
+ // Handle client-side errors
37
+ this.handleClientError(error, snackBar);
38
+ }
39
+ } catch (handlerError) {
40
+ // Fallback if error handler itself fails
41
+ console.error('Error in error handler:', handlerError);
42
+ console.error('Original error:', error);
43
  }
44
  }
45
 
46
  private handleHttpError(error: HttpErrorResponse, snackBar: MatSnackBar, router: Router): void {
47
+ try {
48
+ const flareError = error.error as FlareError;
49
+
50
+ // Race condition error (409)
51
+ if (error.status === 409) {
52
+ const isRaceCondition = flareError?.error === 'RaceConditionError' ||
53
+ error.error?.type === 'race_condition';
54
+
55
+ if (isRaceCondition) {
56
+ const snackBarRef = snackBar.open(
57
+ flareError?.message || 'The data was modified by another user. Please refresh and try again.',
58
+ 'Refresh',
59
+ {
60
+ duration: 0,
61
+ panelClass: ['error-snackbar', 'race-condition-snackbar']
62
+ }
63
+ );
64
+
65
+ snackBarRef.onAction().subscribe(() => {
66
+ window.location.reload();
67
+ });
68
+
69
+ // Show additional info if available
70
+ if (flareError?.details?.last_update_user) {
71
+ console.info(`Last updated by: ${flareError.details.last_update_user} at ${flareError.details.last_update_date}`);
72
+ }
73
+ return;
74
  }
75
+ }
76
 
77
+ // Authentication error (401)
78
+ if (error.status === 401) {
79
+ snackBar.open(
80
+ 'Your session has expired. Please login again.',
81
+ 'Login',
82
+ {
83
+ duration: 5000,
84
+ panelClass: ['error-snackbar']
85
+ }
86
+ ).onAction().subscribe(() => {
87
+ router.navigate(['/login']);
88
+ });
89
+ return;
90
+ }
91
 
92
+ // Validation error (422)
93
+ if (error.status === 422 && flareError?.details) {
94
+ const fieldErrors = Array.isArray(flareError.details)
95
+ ? flareError.details.map((d: any) => `${d.field}: ${d.message}`).join('\n')
96
+ : 'Validation error occurred';
97
+
98
+ snackBar.open(
99
+ flareError.message || 'Validation failed. Please check your input.',
100
+ 'Close',
101
+ {
102
+ duration: 8000,
103
+ panelClass: ['error-snackbar', 'validation-snackbar']
104
+ }
105
+ );
106
+
107
+ console.error('Validation errors:', flareError.details);
108
+ return;
109
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
 
111
+ // Not found error (404)
112
+ if (error.status === 404) {
113
+ snackBar.open(
114
+ flareError?.message || 'The requested resource was not found.',
115
+ 'Close',
116
+ {
117
+ duration: 5000,
118
+ panelClass: ['error-snackbar']
119
+ }
120
+ );
121
+ return;
122
+ }
123
 
124
+ // Server errors (5xx)
125
+ if (error.status >= 500) {
126
+ const message = flareError?.message || 'A server error occurred. Please try again later.';
127
+ const requestId = flareError?.request_id || error.headers?.get('X-Request-ID');
128
+
129
+ snackBar.open(
130
+ requestId ? `${message} (Request ID: ${requestId})` : message,
131
+ 'Close',
132
+ {
133
+ duration: 8000,
134
+ panelClass: ['error-snackbar', 'server-error-snackbar']
135
+ }
136
+ );
137
+ return;
138
+ }
139
+
140
+ // Network error (0 status usually indicates network issues)
141
+ if (error.status === 0) {
142
+ snackBar.open(
143
+ 'Network connection error. Please check your internet connection.',
144
+ 'Retry',
145
+ {
146
+ duration: 0,
147
+ panelClass: ['error-snackbar', 'network-error-snackbar']
148
+ }
149
+ ).onAction().subscribe(() => {
150
+ window.location.reload();
151
+ });
152
+ return;
153
+ }
154
 
155
+ // Generic HTTP error
156
+ const errorMessage = flareError?.message || error.message || `HTTP Error ${error.status}: ${error.statusText}`;
157
  snackBar.open(
158
+ errorMessage,
159
  'Close',
160
  {
161
+ duration: 6000,
162
+ panelClass: ['error-snackbar']
163
  }
164
  );
165
+ } catch (err) {
166
+ console.error('Error in handleHttpError:', err);
167
+ this.showGenericError(snackBar);
168
  }
 
 
 
 
 
 
 
 
 
 
169
  }
170
 
171
  private handleClientError(error: Error, snackBar: MatSnackBar): void {
172
+ try {
173
+ // Check if it's a network error
174
+ if (error.message?.includes('NetworkError') || error.message?.includes('Failed to fetch')) {
175
+ snackBar.open(
176
+ 'Network connection error. Please check your internet connection.',
177
+ 'Retry',
178
+ {
179
+ duration: 0,
180
+ panelClass: ['error-snackbar', 'network-error-snackbar']
181
+ }
182
+ ).onAction().subscribe(() => {
183
+ window.location.reload();
184
+ });
185
+ return;
186
+ }
187
+
188
+ // Check for specific Angular errors
189
+ if (error.name === 'HttpErrorResponse') {
190
+ // This might be an HTTP error that wasn't caught properly
191
+ this.handleHttpError(error as any, snackBar, this.injector.get(Router));
192
+ return;
193
+ }
194
+
195
+ // Generic client error
196
  snackBar.open(
197
+ 'An unexpected error occurred. Please refresh the page.',
198
+ 'Refresh',
199
  {
200
+ duration: 6000,
201
+ panelClass: ['error-snackbar']
202
  }
203
  ).onAction().subscribe(() => {
204
  window.location.reload();
205
  });
206
+ } catch (err) {
207
+ console.error('Error in handleClientError:', err);
208
+ this.showGenericError(snackBar);
209
  }
210
+ }
211
+
212
+ private showGenericError(snackBar: MatSnackBar): void {
213
  snackBar.open(
214
+ 'An error occurred. Please try again.',
215
+ 'Close',
216
  {
217
+ duration: 5000,
218
  panelClass: ['error-snackbar']
219
  }
220
+ );
 
 
221
  }
222
  }
223
 
224
  // Error interceptor for consistent error format
225
+ import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpErrorResponse } from '@angular/common/http';
226
  import { Observable, throwError } from 'rxjs';
227
+ import { catchError, finalize } from 'rxjs/operators';
228
 
229
  @Injectable()
230
  export class ErrorInterceptor implements HttpInterceptor {
231
+ private activeRequests = new Map<string, AbortController>();
232
+
233
  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
234
+ // Create abort controller for request cancellation
235
+ const requestId = this.generateRequestId();
236
+ const abortController = new AbortController();
237
+ this.activeRequests.set(requestId, abortController);
238
+
239
+ // Clone request with additional headers
240
+ const clonedReq = req.clone({
241
+ setHeaders: {
242
+ 'X-Request-ID': requestId
243
+ }
244
+ });
245
+
246
+ return next.handle(clonedReq).pipe(
247
  catchError((error: HttpErrorResponse) => {
248
  // Log request details for debugging
249
  console.error('HTTP Error:', {
250
  url: req.url,
251
  method: req.method,
252
  status: error.status,
253
+ statusText: error.statusText,
254
  error: error.error,
255
+ requestId: requestId,
256
+ headers: error.headers?.keys()
257
  });
258
 
259
+ // Enhanced error object
260
+ const enhancedError = {
261
+ ...error,
262
+ requestId: requestId,
263
+ timestamp: new Date().toISOString(),
264
+ url: req.url,
265
+ method: req.method
266
+ };
267
+
268
  // Re-throw to be handled by global error handler
269
+ return throwError(() => enhancedError);
270
+ }),
271
+ finalize(() => {
272
+ // Clean up abort controller
273
+ this.activeRequests.delete(requestId);
274
  })
275
  );
276
  }
277
+
278
+ private generateRequestId(): string {
279
+ return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
280
+ }
281
+
282
+ // Method to cancel a specific request
283
+ cancelRequest(requestId: string): void {
284
+ const controller = this.activeRequests.get(requestId);
285
+ if (controller) {
286
+ controller.abort();
287
+ this.activeRequests.delete(requestId);
288
+ }
289
+ }
290
+
291
+ // Method to cancel all active requests
292
+ cancelAllRequests(): void {
293
+ this.activeRequests.forEach((controller) => {
294
+ controller.abort();
295
+ });
296
+ this.activeRequests.clear();
297
+ }
298
+ }