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
This commit is contained in:
Yury Bondarenko
2023-06-28 16:30:38 +02:00
parent 07259ca342
commit c0f43bc585
17 changed files with 106 additions and 76 deletions

View File

@@ -427,6 +427,15 @@ export class UnsetUserAsIdleAction implements Action {
public type: string = AuthActionTypes.UNSET_USER_AS_IDLE; public type: string = AuthActionTypes.UNSET_USER_AS_IDLE;
} }
/**
* Authentication error actions that include Error payloads.
*/
export type AuthErrorActionsWithErrorPayload
= AuthenticatedErrorAction
| AuthenticationErrorAction
| LogOutErrorAction
| RetrieveAuthenticatedEpersonErrorAction;
/** /**
* Actions type. * Actions type.
* @type {AuthActions} * @type {AuthActions}
@@ -434,9 +443,7 @@ export class UnsetUserAsIdleAction implements Action {
export type AuthActions export type AuthActions
= AuthenticateAction = AuthenticateAction
| AuthenticatedAction | AuthenticatedAction
| AuthenticatedErrorAction
| AuthenticatedSuccessAction | AuthenticatedSuccessAction
| AuthenticationErrorAction
| AuthenticationSuccessAction | AuthenticationSuccessAction
| CheckAuthenticationTokenAction | CheckAuthenticationTokenAction
| CheckAuthenticationTokenCookieAction | CheckAuthenticationTokenCookieAction
@@ -453,10 +460,9 @@ export type AuthActions
| RetrieveAuthMethodsErrorAction | RetrieveAuthMethodsErrorAction
| RetrieveTokenAction | RetrieveTokenAction
| RetrieveAuthenticatedEpersonAction | RetrieveAuthenticatedEpersonAction
| RetrieveAuthenticatedEpersonErrorAction
| RetrieveAuthenticatedEpersonSuccessAction | RetrieveAuthenticatedEpersonSuccessAction
| SetRedirectUrlAction | SetRedirectUrlAction
| RedirectAfterLoginSuccessAction | RedirectAfterLoginSuccessAction
| SetUserAsIdleAction | SetUserAsIdleAction
| UnsetUserAsIdleAction; | UnsetUserAsIdleAction
| AuthErrorActionsWithErrorPayload;

View File

@@ -1,6 +1,7 @@
import { import {
Injectable, Injectable,
NgZone, NgZone,
Type,
} from '@angular/core'; } from '@angular/core';
// import @ngrx // import @ngrx
import { import {
@@ -50,6 +51,7 @@ import {
AuthenticatedSuccessAction, AuthenticatedSuccessAction,
AuthenticationErrorAction, AuthenticationErrorAction,
AuthenticationSuccessAction, AuthenticationSuccessAction,
AuthErrorActionsWithErrorPayload,
CheckAuthenticationTokenCookieAction, CheckAuthenticationTokenCookieAction,
LogOutErrorAction, LogOutErrorAction,
LogOutSuccessAction, LogOutSuccessAction,
@@ -81,6 +83,16 @@ const IDLE_TIMER_IGNORE_TYPES: string[]
= [...Object.values(AuthActionTypes).filter((t: string) => t !== AuthActionTypes.UNSET_USER_AS_IDLE), = [...Object.values(AuthActionTypes).filter((t: string) => t !== AuthActionTypes.UNSET_USER_AS_IDLE),
...Object.values(RequestActionTypes), ...Object.values(NotificationsActionTypes)]; ...Object.values(RequestActionTypes), ...Object.values(NotificationsActionTypes)];
export function errorToAuthAction$<T extends AuthErrorActionsWithErrorPayload>(actionType: Type<T>, error: unknown): Observable<T> {
if (error instanceof Error) {
return observableOf(new actionType(error));
}
// If we caught something that's not an Error: complain & drop type safety
console.warn('AuthEffects caught non-Error object:', error);
return observableOf(new actionType(error as any));
}
@Injectable() @Injectable()
export class AuthEffects { export class AuthEffects {
@@ -94,7 +106,7 @@ export class AuthEffects {
return this.authService.authenticate(action.payload.email, action.payload.password).pipe( return this.authService.authenticate(action.payload.email, action.payload.password).pipe(
take(1), take(1),
map((response: AuthStatus) => new AuthenticationSuccessAction(response.token)), map((response: AuthStatus) => new AuthenticationSuccessAction(response.token)),
catchError((error) => observableOf(new AuthenticationErrorAction(error))), catchError((error: unknown) => errorToAuthAction$(AuthenticationErrorAction, error)),
); );
}), }),
)); ));
@@ -109,7 +121,8 @@ export class AuthEffects {
switchMap((action: AuthenticatedAction) => { switchMap((action: AuthenticatedAction) => {
return this.authService.authenticatedUser(action.payload).pipe( return this.authService.authenticatedUser(action.payload).pipe(
map((userHref: string) => new AuthenticatedSuccessAction((userHref !== null), action.payload, userHref)), map((userHref: string) => new AuthenticatedSuccessAction((userHref !== null), action.payload, userHref)),
catchError((error) => observableOf(new AuthenticatedErrorAction(error)))); catchError((error: unknown) => errorToAuthAction$(AuthenticatedErrorAction, error)),
);
}), }),
)); ));
@@ -155,7 +168,8 @@ export class AuthEffects {
} }
return user$.pipe( return user$.pipe(
map((user: EPerson) => new RetrieveAuthenticatedEpersonSuccessAction(user.id)), map((user: EPerson) => new RetrieveAuthenticatedEpersonSuccessAction(user.id)),
catchError((error) => observableOf(new RetrieveAuthenticatedEpersonErrorAction(error)))); catchError((error: unknown) => errorToAuthAction$(RetrieveAuthenticatedEpersonErrorAction, error)),
);
}), }),
)); ));
@@ -163,7 +177,7 @@ export class AuthEffects {
switchMap(() => { switchMap(() => {
return this.authService.hasValidAuthenticationToken().pipe( return this.authService.hasValidAuthenticationToken().pipe(
map((token: AuthTokenInfo) => new AuthenticatedAction(token)), map((token: AuthTokenInfo) => new AuthenticatedAction(token)),
catchError((error) => observableOf(new CheckAuthenticationTokenCookieAction())), catchError((error: unknown) => observableOf(new CheckAuthenticationTokenCookieAction())),
); );
}), }),
)); ));
@@ -181,7 +195,7 @@ export class AuthEffects {
return new RetrieveAuthMethodsAction(response); return new RetrieveAuthMethodsAction(response);
} }
}), }),
catchError((error) => observableOf(new AuthenticatedErrorAction(error))), catchError((error: unknown) => errorToAuthAction$(AuthenticatedErrorAction, error)),
); );
}), }),
)); ));
@@ -192,7 +206,7 @@ export class AuthEffects {
return this.authService.refreshAuthenticationToken(null).pipe( return this.authService.refreshAuthenticationToken(null).pipe(
take(1), take(1),
map((token: AuthTokenInfo) => new AuthenticationSuccessAction(token)), map((token: AuthTokenInfo) => new AuthenticationSuccessAction(token)),
catchError((error) => observableOf(new AuthenticationErrorAction(error))), catchError((error: unknown) => errorToAuthAction$(AuthenticationErrorAction, error)),
); );
}), }),
)); ));
@@ -201,7 +215,7 @@ export class AuthEffects {
switchMap((action: RefreshTokenAction) => { switchMap((action: RefreshTokenAction) => {
return this.authService.refreshAuthenticationToken(action.payload).pipe( return this.authService.refreshAuthenticationToken(action.payload).pipe(
map((token: AuthTokenInfo) => new RefreshTokenSuccessAction(token)), map((token: AuthTokenInfo) => new RefreshTokenSuccessAction(token)),
catchError((error) => observableOf(new RefreshTokenErrorAction())), catchError((error: unknown) => observableOf(new RefreshTokenErrorAction())),
); );
}), }),
)); ));
@@ -245,8 +259,8 @@ export class AuthEffects {
switchMap(() => { switchMap(() => {
this.authService.stopImpersonating(); this.authService.stopImpersonating();
return this.authService.logout().pipe( return this.authService.logout().pipe(
map((value) => new LogOutSuccessAction()), map(() => new LogOutSuccessAction()),
catchError((error) => observableOf(new LogOutErrorAction(error))), catchError((error: unknown) => errorToAuthAction$(LogOutErrorAction, error)),
); );
}), }),
)); ));
@@ -272,7 +286,7 @@ export class AuthEffects {
return this.authService.retrieveAuthMethodsFromAuthStatus(action.payload) return this.authService.retrieveAuthMethodsFromAuthStatus(action.payload)
.pipe( .pipe(
map((authMethodModels: AuthMethod[]) => new RetrieveAuthMethodsSuccessAction(authMethodModels)), map((authMethodModels: AuthMethod[]) => new RetrieveAuthMethodsSuccessAction(authMethodModels)),
catchError((error) => observableOf(new RetrieveAuthMethodsErrorAction())), catchError(() => observableOf(new RetrieveAuthMethodsErrorAction())),
); );
}), }),
)); ));

View File

@@ -296,7 +296,7 @@ export class AuthInterceptor implements HttpInterceptor {
return response; return response;
} }
}), }),
catchError((error, caught) => { catchError((error: unknown, caught) => {
// Intercept an error response // Intercept an error response
if (error instanceof HttpErrorResponse) { if (error instanceof HttpErrorResponse) {

View File

@@ -58,8 +58,8 @@ export class RequestEffects {
return this.restApi.request(request.method, request.href, body, request.options, request.isMultipart).pipe( return this.restApi.request(request.method, request.href, body, request.options, request.isMultipart).pipe(
map((data: RawRestResponse) => this.injector.get(request.getResponseParser()).parse(request, data)), map((data: RawRestResponse) => this.injector.get(request.getResponseParser()).parse(request, data)),
map((response: ParsedResponse) => new RequestSuccessAction(request.uuid, response.statusCode, response.link, response.unCacheableObject)), map((response: ParsedResponse) => new RequestSuccessAction(request.uuid, response.statusCode, response.link, response.unCacheableObject)),
catchError((error: RequestError) => { catchError((error: unknown) => {
if (hasValue(error.statusCode)) { if (error instanceof RequestError) {
// if it's an error returned by the server, complete the request // if it's an error returned by the server, complete the request
return [new RequestErrorAction(request.uuid, error.statusCode, error.message)]; return [new RequestErrorAction(request.uuid, error.statusCode, error.message)];
} else { } else {

View File

@@ -42,7 +42,7 @@ export class RootDataService extends BaseDataService<Root> {
*/ */
checkServerAvailability(): Observable<boolean> { checkServerAvailability(): Observable<boolean> {
return this.restService.get(this.halService.getRootHref()).pipe( return this.restService.get(this.halService.getRootHref()).pipe(
catchError((err ) => { catchError((err: unknown) => {
console.error(err); console.error(err);
return observableOf(false); return observableOf(false);
}), }),

View File

@@ -39,7 +39,7 @@ export class SignpostingDataService {
const baseUrl = `${this.appConfig.rest.baseUrl}`; const baseUrl = `${this.appConfig.rest.baseUrl}`;
return this.restService.get(`${baseUrl}/signposting/links/${uuid}`).pipe( return this.restService.get(`${baseUrl}/signposting/links/${uuid}`).pipe(
catchError((err) => { catchError((err: unknown) => {
return observableOf([]); return observableOf([]);
}), }),
map((res: RawRestResponse) => res.statusCode === 200 ? res.payload as SignpostingLink[] : []), map((res: RawRestResponse) => res.statusCode === 200 ? res.payload as SignpostingLink[] : []),

View File

@@ -1,4 +1,7 @@
import { HttpHeaders } from '@angular/common/http'; import {
HttpErrorResponse,
HttpHeaders,
} from '@angular/common/http';
import { import {
HttpClientTestingModule, HttpClientTestingModule,
HttpTestingController, HttpTestingController,
@@ -19,11 +22,14 @@ describe('DspaceRestService', () => {
let dspaceRestService: DspaceRestService; let dspaceRestService: DspaceRestService;
let httpMock: HttpTestingController; let httpMock: HttpTestingController;
const url = 'http://www.dspace.org/'; const url = 'http://www.dspace.org/';
const mockError: any = {
statusCode: 0, const mockError = new HttpErrorResponse({
status: 0,
statusText: 'Unknown Error', statusText: 'Unknown Error',
message: 'Http failure response for http://www.dspace.org/: 0 ', error: {
}; message: 'Http failure response for http://www.dspace.org/: 0 ',
},
});
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
@@ -61,24 +67,28 @@ describe('DspaceRestService', () => {
req.flush(mockPayload, { status: mockStatusCode, statusText: mockStatusText }); req.flush(mockPayload, { status: mockStatusCode, statusText: mockStatusText });
}); });
it('should throw an error', () => { it('should throw an error', () => {
dspaceRestService.get(url).subscribe(() => undefined, (err) => { dspaceRestService.get(url).subscribe(() => undefined, (err: unknown) => {
expect(err).toEqual(mockError); expect(err).toEqual(jasmine.objectContaining({
statusCode: 0,
statusText: 'Unknown Error',
message: 'Http failure response for http://www.dspace.org/: 0 ',
}));
}); });
const req = httpMock.expectOne(url); const req = httpMock.expectOne(url);
expect(req.request.method).toBe('GET'); expect(req.request.method).toBe('GET');
req.error(mockError); req.error({ error: mockError } as ErrorEvent);
}); });
it('should log an error', () => { it('should log an error', () => {
spyOn(console, 'log'); spyOn(console, 'log');
dspaceRestService.get(url).subscribe(() => undefined, (err) => { dspaceRestService.get(url).subscribe(() => undefined, (err: unknown) => {
expect(console.log).toHaveBeenCalled(); expect(console.log).toHaveBeenCalled();
}); });
const req = httpMock.expectOne(url); const req = httpMock.expectOne(url);
expect(req.request.method).toBe('GET'); expect(req.request.method).toBe('GET');
req.error(mockError); req.error({ error: mockError } as ErrorEvent);
}); });
it('when no content-type header is provided, it should use application/json', () => { it('when no content-type header is provided, it should use application/json', () => {

View File

@@ -1,8 +1,8 @@
import { import {
HttpClient, HttpClient,
HttpErrorResponse,
HttpHeaders, HttpHeaders,
HttpParams, HttpParams,
HttpResponse,
} from '@angular/common/http'; } from '@angular/common/http';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { import {
@@ -16,9 +16,9 @@ import {
import { import {
hasNoValue, hasNoValue,
hasValue,
isNotEmpty, isNotEmpty,
} from '../../shared/empty.util'; } from '../../shared/empty.util';
import { RequestError } from '../data/request-error.model';
import { RestRequestMethod } from '../data/rest-request-method'; import { RestRequestMethod } from '../data/rest-request-method';
import { DSpaceObject } from '../shared/dspace-object.model'; import { DSpaceObject } from '../shared/dspace-object.model';
import { RawRestResponse } from './raw-rest-response.model'; import { RawRestResponse } from './raw-rest-response.model';
@@ -53,24 +53,7 @@ export class DspaceRestService {
* An Observable<string> containing the response from the server * An Observable<string> containing the response from the server
*/ */
get(absoluteURL: string): Observable<RawRestResponse> { get(absoluteURL: string): Observable<RawRestResponse> {
const requestOptions = { return this.request(RestRequestMethod.GET, absoluteURL);
observe: 'response' as any,
headers: new HttpHeaders({ 'Content-Type': DEFAULT_CONTENT_TYPE }),
};
return this.http.get(absoluteURL, requestOptions).pipe(
map((res: HttpResponse<any>) => ({
payload: res.body,
statusCode: res.status,
statusText: res.statusText,
})),
catchError((err) => {
console.log('Error: ', err);
return observableThrowError({
statusCode: err.status,
statusText: err.statusText,
message: (hasValue(err.error) && isNotEmpty(err.error.message)) ? err.error.message : err.message,
});
}));
} }
/** /**
@@ -126,17 +109,23 @@ export class DspaceRestService {
statusCode: res.status, statusCode: res.status,
statusText: res.statusText, statusText: res.statusText,
})), })),
catchError((err) => { catchError((err: unknown) => observableThrowError(() => {
if (hasValue(err.status)) { console.log('Error: ', err);
return observableThrowError({ if (err instanceof HttpErrorResponse) {
statusCode: err.status, const error = new RequestError(
statusText: err.statusText, (isNotEmpty(err?.error?.message)) ? err.error.message : err.message,
message: (hasValue(err.error) && isNotEmpty(err.error.message)) ? err.error.message : err.message, );
});
error.statusCode = err.status;
error.statusText = err.statusText;
return error;
} else { } else {
return observableThrowError(err); console.error('Cannot construct RequestError from', err);
return err;
} }
})); })),
);
} }
/** /**

View File

@@ -47,7 +47,7 @@ export class LinkHeadService {
renderer.appendChild(head, link); renderer.appendChild(head, link);
return renderer; return renderer;
} catch (e) { } catch (e) {
console.error('Error within linkService : ', e); console.error('Error within linkService: ', e);
} }
} }
@@ -73,7 +73,9 @@ export class LinkHeadService {
renderer.removeChild(head, link); renderer.removeChild(head, link);
} }
} catch (e) { } catch (e) {
console.log('Error while removing tag ' + e.message); if (e instanceof Error) {
console.error('Error while removing tag: ' + e.message);
}
} }
} }
} }

View File

@@ -11,6 +11,7 @@ import { TestBed } from '@angular/core/testing';
import { CookieServiceMock } from '../../shared/mocks/cookie.service.mock'; import { CookieServiceMock } from '../../shared/mocks/cookie.service.mock';
import { HttpXsrfTokenExtractorMock } from '../../shared/mocks/http-xsrf-token-extractor.mock'; import { HttpXsrfTokenExtractorMock } from '../../shared/mocks/http-xsrf-token-extractor.mock';
import { RequestError } from '../data/request-error.model';
import { RestRequestMethod } from '../data/rest-request-method'; import { RestRequestMethod } from '../data/rest-request-method';
import { DspaceRestService } from '../dspace-rest/dspace-rest.service'; import { DspaceRestService } from '../dspace-rest/dspace-rest.service';
import { CookieService } from '../services/cookie.service'; import { CookieService } from '../services/cookie.service';
@@ -153,12 +154,13 @@ describe(`XsrfInterceptor`, () => {
const mockErrorMessage = 'CSRF token mismatch'; const mockErrorMessage = 'CSRF token mismatch';
service.request(RestRequestMethod.GET, 'server/api/core/items').subscribe({ service.request(RestRequestMethod.GET, 'server/api/core/items').subscribe({
error: (error) => { error: (error: unknown) => {
expect(error).toBeTruthy(); expect(error).toBeTruthy();
expect(error instanceof RequestError).toBeTrue();
// ensure mock error (added in below flush() call) is returned. // ensure mock error (added in below flush() call) is returned.
expect(error.statusCode).toBe(mockErrorCode); expect((error as RequestError).statusCode).toBe(mockErrorCode);
expect(error.statusText).toBe(mockErrorText); expect((error as RequestError).statusText).toBe(mockErrorText);
// ensure our XSRF-TOKEN cookie exists & has the same value as the new DSPACE-XSRF-TOKEN header // ensure our XSRF-TOKEN cookie exists & has the same value as the new DSPACE-XSRF-TOKEN header
expect(cookieService.get('XSRF-TOKEN')).not.toBeNull(); expect(cookieService.get('XSRF-TOKEN')).not.toBeNull();

View File

@@ -102,7 +102,7 @@ export class XsrfInterceptor implements HttpInterceptor {
} }
} }
}), }),
catchError((error) => { catchError((error: unknown) => {
if (error instanceof HttpErrorResponse) { if (error instanceof HttpErrorResponse) {
// For every error that comes back, also check for the custom // For every error that comes back, also check for the custom
// DSPACE-XSRF-TOKEN header sent from the backend. // DSPACE-XSRF-TOKEN header sent from the backend.

View File

@@ -180,7 +180,7 @@ export class ItemAuthorizationsComponent implements OnInit, OnDestroy {
filter((item: Item) => isNotEmpty(item.bundles)), filter((item: Item) => isNotEmpty(item.bundles)),
mergeMap((item: Item) => item.bundles), mergeMap((item: Item) => item.bundles),
getFirstSucceededRemoteDataWithNotEmptyPayload(), getFirstSucceededRemoteDataWithNotEmptyPayload(),
catchError((error) => { catchError((error: unknown) => {
console.error(error); console.error(error);
return observableOf(buildPaginatedList(null, [])); return observableOf(buildPaginatedList(null, []));
}), }),
@@ -229,7 +229,7 @@ export class ItemAuthorizationsComponent implements OnInit, OnDestroy {
private getBundleBitstreams(bundle: Bundle): Observable<PaginatedList<Bitstream>> { private getBundleBitstreams(bundle: Bundle): Observable<PaginatedList<Bitstream>> {
return bundle.bitstreams.pipe( return bundle.bitstreams.pipe(
getFirstSucceededRemoteDataPayload(), getFirstSucceededRemoteDataPayload(),
catchError((error) => { catchError((error: unknown) => {
console.error(error); console.error(error);
return observableOf(buildPaginatedList(null, [])); return observableOf(buildPaginatedList(null, []));
}), }),

View File

@@ -204,7 +204,7 @@ export class SubscriptionModalComponent implements OnInit {
} }
this.processing$.next(false); this.processing$.next(false);
}, },
error: err => { error: (err: unknown) => {
this.processing$.next(false); this.processing$.next(false);
}, },
}); });

View File

@@ -262,7 +262,7 @@ export class SubmissionObjectEffects {
switchMap(([action, state]: [DepositSubmissionAction, any]) => { switchMap(([action, state]: [DepositSubmissionAction, any]) => {
return this.submissionService.depositSubmission(state.submission.objects[action.payload.submissionId].selfUrl).pipe( return this.submissionService.depositSubmission(state.submission.objects[action.payload.submissionId].selfUrl).pipe(
map(() => new DepositSubmissionSuccessAction(action.payload.submissionId)), map(() => new DepositSubmissionSuccessAction(action.payload.submissionId)),
catchError((error) => observableOf(new DepositSubmissionErrorAction(action.payload.submissionId)))); catchError((error: unknown) => observableOf(new DepositSubmissionErrorAction(action.payload.submissionId))));
}))); })));
/** /**

View File

@@ -341,7 +341,9 @@ export class SubmissionSectionFormComponent extends SectionModelComponent {
message: msg, message: msg,
path: '/sections/' + this.sectionData.id, path: '/sections/' + this.sectionData.id,
}; };
console.error(e.stack); if (e instanceof Error) {
console.error(e.stack);
}
this.sectionService.setSectionError(this.submissionId, this.sectionData.id, sectionError); this.sectionService.setSectionError(this.submissionId, this.sectionData.id, sectionError);
} }
} }

View File

@@ -29,7 +29,9 @@ import { TestScheduler } from 'rxjs/testing';
import { environment } from '../../environments/environment'; import { environment } from '../../environments/environment';
import { storeModuleConfig } from '../app.reducer'; import { storeModuleConfig } from '../app.reducer';
import { ErrorResponse } from '../core/cache/response.models';
import { RequestService } from '../core/data/request.service'; import { RequestService } from '../core/data/request.service';
import { RequestError } from '../core/data/request-error.model';
import { HttpOptions } from '../core/dspace-rest/dspace-rest.service'; import { HttpOptions } from '../core/dspace-rest/dspace-rest.service';
import { RouteService } from '../core/services/route.service'; import { RouteService } from '../core/services/route.service';
import { Item } from '../core/shared/item.model'; import { Item } from '../core/shared/item.model';
@@ -959,11 +961,12 @@ describe('SubmissionService test suite', () => {
}); });
it('should catch error from REST endpoint', () => { it('should catch error from REST endpoint', () => {
const requestError = new RequestError('Internal Server Error');
requestError.statusCode = 500;
const errorResponse = new ErrorResponse(requestError);
(service as any).restService.getDataById.and.callFake( (service as any).restService.getDataById.and.callFake(
() => observableThrowError({ () => observableThrowError(errorResponse),
statusCode: 500,
errorMessage: 'Internal Server Error',
}),
); );
service.retrieveSubmission('826').subscribe((r) => { service.retrieveSubmission('826').subscribe((r) => {

View File

@@ -576,8 +576,10 @@ export class SubmissionService {
find((submissionObjects: SubmissionObject[]) => isNotUndefined(submissionObjects)), find((submissionObjects: SubmissionObject[]) => isNotUndefined(submissionObjects)),
map((submissionObjects: SubmissionObject[]) => createSuccessfulRemoteDataObject( map((submissionObjects: SubmissionObject[]) => createSuccessfulRemoteDataObject(
submissionObjects[0])), submissionObjects[0])),
catchError((errorResponse: ErrorResponse) => { catchError((errorResponse: unknown) => {
return createFailedRemoteDataObject$<SubmissionObject>(errorResponse.errorMessage, errorResponse.statusCode); if (errorResponse instanceof ErrorResponse) {
return createFailedRemoteDataObject$<SubmissionObject>(errorResponse.errorMessage, errorResponse.statusCode);
}
}), }),
); );
} }