ngrx / platform

Reactive State for Angular
https://ngrx.io
Other
8.03k stars 1.97k forks source link

ngrx/data: DataServiceError not returned correctly when using RxJs@6.6.2 #3401

Closed freikim closed 2 years ago

freikim commented 2 years ago

Minimal reproduction of the bug/regression with instructions

When an API returns an error the DataServiceError is not returned correctly when using RxJs@6.6.2 (most likely other 6.6.x versions as well). To reproduce open the stackblitz below, open the console and see that the content of the DataServiceError is just a function reference and null fields:

Returned Server Error: DataServiceError {error: ƒ, requestData: null, message: null} error: ƒ () message: null requestData: null proto: DataServiceError

https://stackblitz.com/edit/angular-ivy-itadgt?file=src/app/test.effects.ts

Minimal reproduction of the bug/regression with instructions

The expected behavior would be a response similar to this:

Returned Server Error: DataServiceError {error: {…}, requestData: {…}, message: "Http failure response for https://api.chucknorris.io/jokes/test/: 405 OK"} error: HttpErrorResponse error: null headers: HttpHeaders message: "Http failure response for https://api.chucknorris.io/jokes/test/: 405 OK" name: "HttpErrorResponse" ok: false status: 405 statusText: "OK" url: "https://api.chucknorris.io/jokes/test/" proto: HttpErrorResponse message: "Http failure response for https://api.chucknorris.io/jokes/test/: 405 OK" requestData: Object proto: DataServiceError

Versions of NgRx, Angular, Node, affected browser(s) and operating system(s)

NgRx: 13.2.0 RxJs: 6.6.2 Angular: 13.3.5 Browsers: Chrome / Firefox both latest OS: Linux

Other information

Changing RxJs version in stackblitz to 7.5.5. solves the issue.

I would be willing to submit a PR to fix this issue

freikim commented 2 years ago

BTW I traced the error to come from:

https://github.com/ngrx/platform/blob/35fad8cd4c32bb149acdd096147b3d3e10933485/modules/data/src/dataservices/default-data.service.ts#L177

Where the returned function never seems to be called .

brandonroberts commented 2 years ago

@freikim the returned function should be called here by catchError https://github.com/ngrx/platform/blob/35fad8cd4c32bb149acdd096147b3d3e10933485/modules/data/src/dataservices/default-data.service.ts#L174

fabioaccetta commented 2 years ago

It seems that this line is the source of the error:

https://github.com/ngrx/platform/blob/47afba598d13ec0cc50744d0bc6bd47e71afe2d0/modules/data/src/dataservices/persistence-result-handler.service.ts#L61

because, debugging my project, I see that the if part of this instruction is always false:

const error = err instanceof DataServiceError ? err : new DataServiceError(err, null);

It appens because err is an arrow function of kind () => error and not an error instance, so new DataServiceError(err, null) gives the error empty as said by @freikim.

Hope this helps, thanks.

eciuca commented 1 year ago

Why was this issue closed? Is it normal to receive a function on the error channel instead of a DataServiceError object?

Here's a simple example and the workaround I applied.

this.usersService.add(modifiedUser)
        .subscribe(() => console.log('do something on success'),
            (dataserviceerrorFn: () => DataServiceError) => {
                const dataserviceerror = dataserviceerrorFn();
                const message = dataserviceerror.message ? dataserviceerror.message : dataserviceerror.error().message
                console.log('Error creating user', message);
            }
      );

I check the message like this because normally the message field was expected to be filled from the containing error's message field.

And this is the userService class:

import {Injectable} from '@angular/core';
import {EntityCollectionServiceBase, EntityCollectionServiceElementsFactory} from '@ngrx/data';
import {User} from '../model/user';

@Injectable()
export class UserEntityService
    extends EntityCollectionServiceBase<User> {

    constructor(serviceElementsFactory: EntityCollectionServiceElementsFactory) {
        super('User', serviceElementsFactory);
    }

}