Skip to content

Commit c0f43bc

Browse files
committed
ESLint: fix rxjs/no-implicit-any-catch
In most cases we can deal with the untyped errors by introducing an explicit instanceof check DspaceRestService includes an unsafe catch/rethrow → made it explicitly typed as `any`, but it should be revisited at some point
1 parent 07259ca commit c0f43bc

17 files changed

Lines changed: 106 additions & 76 deletions

src/app/core/auth/auth.actions.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -427,16 +427,23 @@ export class UnsetUserAsIdleAction implements Action {
427427
public type: string = AuthActionTypes.UNSET_USER_AS_IDLE;
428428
}
429429

430+
/**
431+
* Authentication error actions that include Error payloads.
432+
*/
433+
export type AuthErrorActionsWithErrorPayload
434+
= AuthenticatedErrorAction
435+
| AuthenticationErrorAction
436+
| LogOutErrorAction
437+
| RetrieveAuthenticatedEpersonErrorAction;
438+
430439
/**
431440
* Actions type.
432441
* @type {AuthActions}
433442
*/
434443
export type AuthActions
435444
= AuthenticateAction
436445
| AuthenticatedAction
437-
| AuthenticatedErrorAction
438446
| AuthenticatedSuccessAction
439-
| AuthenticationErrorAction
440447
| AuthenticationSuccessAction
441448
| CheckAuthenticationTokenAction
442449
| CheckAuthenticationTokenCookieAction
@@ -453,10 +460,9 @@ export type AuthActions
453460
| RetrieveAuthMethodsErrorAction
454461
| RetrieveTokenAction
455462
| RetrieveAuthenticatedEpersonAction
456-
| RetrieveAuthenticatedEpersonErrorAction
457463
| RetrieveAuthenticatedEpersonSuccessAction
458464
| SetRedirectUrlAction
459465
| RedirectAfterLoginSuccessAction
460466
| SetUserAsIdleAction
461-
| UnsetUserAsIdleAction;
462-
467+
| UnsetUserAsIdleAction
468+
| AuthErrorActionsWithErrorPayload;

src/app/core/auth/auth.effects.ts

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {
22
Injectable,
33
NgZone,
4+
Type,
45
} from '@angular/core';
56
// import @ngrx
67
import {
@@ -50,6 +51,7 @@ import {
5051
AuthenticatedSuccessAction,
5152
AuthenticationErrorAction,
5253
AuthenticationSuccessAction,
54+
AuthErrorActionsWithErrorPayload,
5355
CheckAuthenticationTokenCookieAction,
5456
LogOutErrorAction,
5557
LogOutSuccessAction,
@@ -81,6 +83,16 @@ const IDLE_TIMER_IGNORE_TYPES: string[]
8183
= [...Object.values(AuthActionTypes).filter((t: string) => t !== AuthActionTypes.UNSET_USER_AS_IDLE),
8284
...Object.values(RequestActionTypes), ...Object.values(NotificationsActionTypes)];
8385

86+
export function errorToAuthAction$<T extends AuthErrorActionsWithErrorPayload>(actionType: Type<T>, error: unknown): Observable<T> {
87+
if (error instanceof Error) {
88+
return observableOf(new actionType(error));
89+
}
90+
91+
// If we caught something that's not an Error: complain & drop type safety
92+
console.warn('AuthEffects caught non-Error object:', error);
93+
return observableOf(new actionType(error as any));
94+
}
95+
8496
@Injectable()
8597
export class AuthEffects {
8698

@@ -94,7 +106,7 @@ export class AuthEffects {
94106
return this.authService.authenticate(action.payload.email, action.payload.password).pipe(
95107
take(1),
96108
map((response: AuthStatus) => new AuthenticationSuccessAction(response.token)),
97-
catchError((error) => observableOf(new AuthenticationErrorAction(error))),
109+
catchError((error: unknown) => errorToAuthAction$(AuthenticationErrorAction, error)),
98110
);
99111
}),
100112
));
@@ -109,7 +121,8 @@ export class AuthEffects {
109121
switchMap((action: AuthenticatedAction) => {
110122
return this.authService.authenticatedUser(action.payload).pipe(
111123
map((userHref: string) => new AuthenticatedSuccessAction((userHref !== null), action.payload, userHref)),
112-
catchError((error) => observableOf(new AuthenticatedErrorAction(error))));
124+
catchError((error: unknown) => errorToAuthAction$(AuthenticatedErrorAction, error)),
125+
);
113126
}),
114127
));
115128

@@ -155,15 +168,16 @@ export class AuthEffects {
155168
}
156169
return user$.pipe(
157170
map((user: EPerson) => new RetrieveAuthenticatedEpersonSuccessAction(user.id)),
158-
catchError((error) => observableOf(new RetrieveAuthenticatedEpersonErrorAction(error))));
171+
catchError((error: unknown) => errorToAuthAction$(RetrieveAuthenticatedEpersonErrorAction, error)),
172+
);
159173
}),
160174
));
161175

162176
public checkToken$: Observable<Action> = createEffect(() => this.actions$.pipe(ofType(AuthActionTypes.CHECK_AUTHENTICATION_TOKEN),
163177
switchMap(() => {
164178
return this.authService.hasValidAuthenticationToken().pipe(
165179
map((token: AuthTokenInfo) => new AuthenticatedAction(token)),
166-
catchError((error) => observableOf(new CheckAuthenticationTokenCookieAction())),
180+
catchError((error: unknown) => observableOf(new CheckAuthenticationTokenCookieAction())),
167181
);
168182
}),
169183
));
@@ -181,7 +195,7 @@ export class AuthEffects {
181195
return new RetrieveAuthMethodsAction(response);
182196
}
183197
}),
184-
catchError((error) => observableOf(new AuthenticatedErrorAction(error))),
198+
catchError((error: unknown) => errorToAuthAction$(AuthenticatedErrorAction, error)),
185199
);
186200
}),
187201
));
@@ -192,7 +206,7 @@ export class AuthEffects {
192206
return this.authService.refreshAuthenticationToken(null).pipe(
193207
take(1),
194208
map((token: AuthTokenInfo) => new AuthenticationSuccessAction(token)),
195-
catchError((error) => observableOf(new AuthenticationErrorAction(error))),
209+
catchError((error: unknown) => errorToAuthAction$(AuthenticationErrorAction, error)),
196210
);
197211
}),
198212
));
@@ -201,7 +215,7 @@ export class AuthEffects {
201215
switchMap((action: RefreshTokenAction) => {
202216
return this.authService.refreshAuthenticationToken(action.payload).pipe(
203217
map((token: AuthTokenInfo) => new RefreshTokenSuccessAction(token)),
204-
catchError((error) => observableOf(new RefreshTokenErrorAction())),
218+
catchError((error: unknown) => observableOf(new RefreshTokenErrorAction())),
205219
);
206220
}),
207221
));
@@ -245,8 +259,8 @@ export class AuthEffects {
245259
switchMap(() => {
246260
this.authService.stopImpersonating();
247261
return this.authService.logout().pipe(
248-
map((value) => new LogOutSuccessAction()),
249-
catchError((error) => observableOf(new LogOutErrorAction(error))),
262+
map(() => new LogOutSuccessAction()),
263+
catchError((error: unknown) => errorToAuthAction$(LogOutErrorAction, error)),
250264
);
251265
}),
252266
));
@@ -272,7 +286,7 @@ export class AuthEffects {
272286
return this.authService.retrieveAuthMethodsFromAuthStatus(action.payload)
273287
.pipe(
274288
map((authMethodModels: AuthMethod[]) => new RetrieveAuthMethodsSuccessAction(authMethodModels)),
275-
catchError((error) => observableOf(new RetrieveAuthMethodsErrorAction())),
289+
catchError(() => observableOf(new RetrieveAuthMethodsErrorAction())),
276290
);
277291
}),
278292
));

src/app/core/auth/auth.interceptor.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,7 @@ export class AuthInterceptor implements HttpInterceptor {
296296
return response;
297297
}
298298
}),
299-
catchError((error, caught) => {
299+
catchError((error: unknown, caught) => {
300300
// Intercept an error response
301301
if (error instanceof HttpErrorResponse) {
302302

src/app/core/data/request.effects.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,8 @@ export class RequestEffects {
5858
return this.restApi.request(request.method, request.href, body, request.options, request.isMultipart).pipe(
5959
map((data: RawRestResponse) => this.injector.get(request.getResponseParser()).parse(request, data)),
6060
map((response: ParsedResponse) => new RequestSuccessAction(request.uuid, response.statusCode, response.link, response.unCacheableObject)),
61-
catchError((error: RequestError) => {
62-
if (hasValue(error.statusCode)) {
61+
catchError((error: unknown) => {
62+
if (error instanceof RequestError) {
6363
// if it's an error returned by the server, complete the request
6464
return [new RequestErrorAction(request.uuid, error.statusCode, error.message)];
6565
} else {

src/app/core/data/root-data.service.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export class RootDataService extends BaseDataService<Root> {
4242
*/
4343
checkServerAvailability(): Observable<boolean> {
4444
return this.restService.get(this.halService.getRootHref()).pipe(
45-
catchError((err ) => {
45+
catchError((err: unknown) => {
4646
console.error(err);
4747
return observableOf(false);
4848
}),

src/app/core/data/signposting-data.service.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export class SignpostingDataService {
3939
const baseUrl = `${this.appConfig.rest.baseUrl}`;
4040

4141
return this.restService.get(`${baseUrl}/signposting/links/${uuid}`).pipe(
42-
catchError((err) => {
42+
catchError((err: unknown) => {
4343
return observableOf([]);
4444
}),
4545
map((res: RawRestResponse) => res.statusCode === 200 ? res.payload as SignpostingLink[] : []),

src/app/core/dspace-rest/dspace-rest.service.spec.ts

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
import { HttpHeaders } from '@angular/common/http';
1+
import {
2+
HttpErrorResponse,
3+
HttpHeaders,
4+
} from '@angular/common/http';
25
import {
36
HttpClientTestingModule,
47
HttpTestingController,
@@ -19,11 +22,14 @@ describe('DspaceRestService', () => {
1922
let dspaceRestService: DspaceRestService;
2023
let httpMock: HttpTestingController;
2124
const url = 'http://www.dspace.org/';
22-
const mockError: any = {
23-
statusCode: 0,
25+
26+
const mockError = new HttpErrorResponse({
27+
status: 0,
2428
statusText: 'Unknown Error',
25-
message: 'Http failure response for http://www.dspace.org/: 0 ',
26-
};
29+
error: {
30+
message: 'Http failure response for http://www.dspace.org/: 0 ',
31+
},
32+
});
2733

2834
beforeEach(() => {
2935
TestBed.configureTestingModule({
@@ -61,24 +67,28 @@ describe('DspaceRestService', () => {
6167
req.flush(mockPayload, { status: mockStatusCode, statusText: mockStatusText });
6268
});
6369
it('should throw an error', () => {
64-
dspaceRestService.get(url).subscribe(() => undefined, (err) => {
65-
expect(err).toEqual(mockError);
70+
dspaceRestService.get(url).subscribe(() => undefined, (err: unknown) => {
71+
expect(err).toEqual(jasmine.objectContaining({
72+
statusCode: 0,
73+
statusText: 'Unknown Error',
74+
message: 'Http failure response for http://www.dspace.org/: 0 ',
75+
}));
6676
});
6777
const req = httpMock.expectOne(url);
6878
expect(req.request.method).toBe('GET');
69-
req.error(mockError);
79+
req.error({ error: mockError } as ErrorEvent);
7080
});
7181

7282
it('should log an error', () => {
7383
spyOn(console, 'log');
7484

75-
dspaceRestService.get(url).subscribe(() => undefined, (err) => {
85+
dspaceRestService.get(url).subscribe(() => undefined, (err: unknown) => {
7686
expect(console.log).toHaveBeenCalled();
7787
});
7888

7989
const req = httpMock.expectOne(url);
8090
expect(req.request.method).toBe('GET');
81-
req.error(mockError);
91+
req.error({ error: mockError } as ErrorEvent);
8292
});
8393

8494
it('when no content-type header is provided, it should use application/json', () => {

src/app/core/dspace-rest/dspace-rest.service.ts

Lines changed: 18 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import {
22
HttpClient,
3+
HttpErrorResponse,
34
HttpHeaders,
45
HttpParams,
5-
HttpResponse,
66
} from '@angular/common/http';
77
import { Injectable } from '@angular/core';
88
import {
@@ -16,9 +16,9 @@ import {
1616

1717
import {
1818
hasNoValue,
19-
hasValue,
2019
isNotEmpty,
2120
} from '../../shared/empty.util';
21+
import { RequestError } from '../data/request-error.model';
2222
import { RestRequestMethod } from '../data/rest-request-method';
2323
import { DSpaceObject } from '../shared/dspace-object.model';
2424
import { RawRestResponse } from './raw-rest-response.model';
@@ -53,24 +53,7 @@ export class DspaceRestService {
5353
* An Observable<string> containing the response from the server
5454
*/
5555
get(absoluteURL: string): Observable<RawRestResponse> {
56-
const requestOptions = {
57-
observe: 'response' as any,
58-
headers: new HttpHeaders({ 'Content-Type': DEFAULT_CONTENT_TYPE }),
59-
};
60-
return this.http.get(absoluteURL, requestOptions).pipe(
61-
map((res: HttpResponse<any>) => ({
62-
payload: res.body,
63-
statusCode: res.status,
64-
statusText: res.statusText,
65-
})),
66-
catchError((err) => {
67-
console.log('Error: ', err);
68-
return observableThrowError({
69-
statusCode: err.status,
70-
statusText: err.statusText,
71-
message: (hasValue(err.error) && isNotEmpty(err.error.message)) ? err.error.message : err.message,
72-
});
73-
}));
56+
return this.request(RestRequestMethod.GET, absoluteURL);
7457
}
7558

7659
/**
@@ -126,17 +109,23 @@ export class DspaceRestService {
126109
statusCode: res.status,
127110
statusText: res.statusText,
128111
})),
129-
catchError((err) => {
130-
if (hasValue(err.status)) {
131-
return observableThrowError({
132-
statusCode: err.status,
133-
statusText: err.statusText,
134-
message: (hasValue(err.error) && isNotEmpty(err.error.message)) ? err.error.message : err.message,
135-
});
112+
catchError((err: unknown) => observableThrowError(() => {
113+
console.log('Error: ', err);
114+
if (err instanceof HttpErrorResponse) {
115+
const error = new RequestError(
116+
(isNotEmpty(err?.error?.message)) ? err.error.message : err.message,
117+
);
118+
119+
error.statusCode = err.status;
120+
error.statusText = err.statusText;
121+
122+
return error;
136123
} else {
137-
return observableThrowError(err);
124+
console.error('Cannot construct RequestError from', err);
125+
return err;
138126
}
139-
}));
127+
})),
128+
);
140129
}
141130

142131
/**

src/app/core/services/link-head.service.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export class LinkHeadService {
4747
renderer.appendChild(head, link);
4848
return renderer;
4949
} catch (e) {
50-
console.error('Error within linkService : ', e);
50+
console.error('Error within linkService: ', e);
5151
}
5252
}
5353

@@ -73,7 +73,9 @@ export class LinkHeadService {
7373
renderer.removeChild(head, link);
7474
}
7575
} catch (e) {
76-
console.log('Error while removing tag ' + e.message);
76+
if (e instanceof Error) {
77+
console.error('Error while removing tag: ' + e.message);
78+
}
7779
}
7880
}
7981
}

src/app/core/xsrf/xsrf.interceptor.spec.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { TestBed } from '@angular/core/testing';
1111

1212
import { CookieServiceMock } from '../../shared/mocks/cookie.service.mock';
1313
import { HttpXsrfTokenExtractorMock } from '../../shared/mocks/http-xsrf-token-extractor.mock';
14+
import { RequestError } from '../data/request-error.model';
1415
import { RestRequestMethod } from '../data/rest-request-method';
1516
import { DspaceRestService } from '../dspace-rest/dspace-rest.service';
1617
import { CookieService } from '../services/cookie.service';
@@ -153,12 +154,13 @@ describe(`XsrfInterceptor`, () => {
153154
const mockErrorMessage = 'CSRF token mismatch';
154155

155156
service.request(RestRequestMethod.GET, 'server/api/core/items').subscribe({
156-
error: (error) => {
157+
error: (error: unknown) => {
157158
expect(error).toBeTruthy();
159+
expect(error instanceof RequestError).toBeTrue();
158160

159161
// ensure mock error (added in below flush() call) is returned.
160-
expect(error.statusCode).toBe(mockErrorCode);
161-
expect(error.statusText).toBe(mockErrorText);
162+
expect((error as RequestError).statusCode).toBe(mockErrorCode);
163+
expect((error as RequestError).statusText).toBe(mockErrorText);
162164

163165
// ensure our XSRF-TOKEN cookie exists & has the same value as the new DSPACE-XSRF-TOKEN header
164166
expect(cookieService.get('XSRF-TOKEN')).not.toBeNull();

0 commit comments

Comments
 (0)