Skip to content

Object is empty inside client after upgrade to v3 #1200

@stefan-willems-beech

Description

@stefan-willems-beech

Used libraries

core, jsonapi, jsonapi-angular, utils

Library version(s)

 "@datx/core": "^3.0.0",     "@datx/jsonapi": "^3.0.0",     "@datx/jsonapi-angular": "^3.0.0",     "@datx/jsonapi-types": "^3.0.0",     "@datx/network": "^3.0.0",     "@datx/utils": "^3.0.0",

Issue description

When retrieving the model via a custom url the data is not being set inside the object.
Whereas I retrieve all the users after, the data is getting filled properly.

Response from api

{
   "jsonapi":{
      "version":"1.0"
   },
   "links":{
      "self":"http:\/\/localhost\/api\/v1\/users\/1"
   },
   "data":{
      "type":"users",
      "id":"1",
      "attributes":{
         "name":"Stefan Willems",
         "email":"",
         "given_name":"Stefan",
         "family_name":"Willems",
         "created_at":"2023-09-25T10:04:06.000000Z",
         "updated_at":"2023-09-25T10:04:06.000000Z"
      },
      "relationships":{
         "roles":{
            "links":{
               "related":"http:\/\/localhost\/api\/v1\/users\/1\/roles",
               "self":"http:\/\/localhost\/api\/v1\/users\/1\/relationships\/roles"
            },
            "data":[
               {
                  "type":"roles",
                  "id":"2"
               }
            ]
         },
         "revisions":{
            "links":{
               "related":"http:\/\/localhost\/api\/v1\/users\/1\/revisions",
               "self":"http:\/\/localhost\/api\/v1\/users\/1\/relationships\/revisions"
            },
            "data":[
               
            ]
         }
      },
      "links":{
         "self":"http:\/\/localhost\/api\/v1\/users\/1"
      }
   },
   "included":[
      {
         "type":"roles",
         "id":"2",
         "attributes":{
            "name":"user"
         },
         "relationships":{
            "permissions":{
               "links":{
                  "related":"http:\/\/localhost\/api\/v1\/roles\/2\/permissions",
                  "self":"http:\/\/localhost\/api\/v1\/roles\/2\/relationships\/permissions"
               },
               "data":[
                  {
                     "type":"permissions",
                     "id":"1"
                  },
                  {
                     "type":"permissions",
                     "id":"10"
                  },
                  {
                     "type":"permissions",
                     "id":"11"
                  },
                  {
                     "type":"permissions",
                     "id":"12"
                  },
                  {
                     "type":"permissions",
                     "id":"13"
                  },
                  {
                     "type":"permissions",
                     "id":"14"
                  }
               ]
            }
         },
         "links":{
            "self":"http:\/\/localhost\/api\/v1\/roles\/2"
         }
      },
      {
         "type":"permissions",
         "id":"1",
         "attributes":{
            "name":"login-permission"
         },
         "links":{
            "self":"http:\/\/localhost\/api\/v1\/permissions\/1"
         }
      },
      {
         "type":"permissions",
         "id":"10",
         "attributes":{
            "name":"file-read"
         },
         "links":{
            "self":"http:\/\/localhost\/api\/v1\/permissions\/10"
         }
      },
      {
         "type":"permissions",
         "id":"11",
         "attributes":{
            "name":"file-create"
         },
         "links":{
            "self":"http:\/\/localhost\/api\/v1\/permissions\/11"
         }
      },
      {
         "type":"permissions",
         "id":"12",
         "attributes":{
            "name":"file-delete"
         },
         "links":{
            "self":"http:\/\/localhost\/api\/v1\/permissions\/12"
         }
      },
      {
         "type":"permissions",
         "id":"13",
         "attributes":{
            "name":"file-upload"
         },
         "links":{
            "self":"http:\/\/localhost\/api\/v1\/permissions\/13"
         }
      },
      {
         "type":"permissions",
         "id":"14",
         "attributes":{
            "name":"file-update"
         },
         "links":{
            "self":"http:\/\/localhost\/api\/v1\/permissions\/14"
         }
      }
   ]
}

Object inside Angular

{
    "__META__": {
        "type": "users",
        "id": "1",
        "fields": {
            "name": {
                "referenceDef": false
            },
            "email": {
                "referenceDef": false
            },
            "given_name": {
                "referenceDef": false
            },
            "family_name": {
                "referenceDef": false
            },
            "company": {
                "referenceDef": {
                    "type": 0,
                    "model": "companies"
                }
            },
            "roles": {
                "referenceDef": {
                    "type": 1,
                    "model": "roles"
                }
            },
            "revisions": {
                "referenceDef": {
                    "type": 1,
                    "model": "revisions"
                }
            },
            "created_at": {
                "referenceDef": false
            },
            "updated_at": {
                "referenceDef": false
            }
        },
        "jsonapiLinks": {
            "self": "http://localhost/api/v1/users/1"
        },
        "networkPersisted": true,
        "jsonapiRefLinks": {
            "roles": {
                "related": "http://localhost/api/v1/users/1/roles",
                "self": "http://localhost/api/v1/users/1/relationships/roles"
            },
            "revisions": {
                "related": "http://localhost/api/v1/users/1/revisions",
                "self": "http://localhost/api/v1/users/1/relationships/revisions"
            }
        },
        "jsonapiRefMeta": {}
    },
    "company": null,
    "roles": [
        {
            "id": "2",
            "type": "roles"
        }
    ],
    "revisions": [],
    "created_at": "2023-09-25T10:04:06.000000Z",
    "updated_at": "2023-09-25T10:04:06.000000Z"
}

Request being send from client

public me(include?: string): Observable<User> {
        const options: IRequestOptions = {} as IRequestOptions;
        options.queryParams = {};

        if (include) {
            options.queryParams.include = include;
        }

        return this.collection.request('users/me', HttpMethod.Get, null, options).pipe(map((response: Response) => {
            console.log(response.data);
            return response.data;
        }));
    }

App module

import {BrowserModule} from '@angular/platform-browser';
import {APP_INITIALIZER, ErrorHandler, NgModule} from '@angular/core';
import {AppRoutingModule} from './app-routing.module';
import {AppComponent} from './app.component';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import {HTTP_INTERCEPTORS, HttpClient, HttpClientModule} from '@angular/common/http';
import {VersionMismatchInterceptor} from './interceptors/version-mismatch.interceptor';
import {NoConnectionInterceptor} from './interceptors/no-connection.interceptor';
import {DashboardModule} from './dashboard/dashboard.module';
import {MainModule} from './main/main.module';
import {MaterialModule} from './material/material.module';
import * as Sentry from '@sentry/angular-ivy';
import {APP_COLLECTION, DATX_CONFIG, setupDatx} from '@datx/jsonapi-angular';
import {AppCollection} from './collections/app.collection';
import {AuthenticationModule} from './authentication/authentication.module';
import {UnauthorizedInterceptor} from './interceptors/unauthorized.interceptor';
import {CustomFetchService} from './services/custom-fetch.service';
import {CachingStrategy} from '@datx/network';
import {config} from '@datx/jsonapi';

function initDatx(customFetch: CustomFetchService): () => Promise<void> {
    return async () => {
        config.baseFetch = customFetch.fetch.bind(customFetch);

        config.defaultFetchOptions = {
            credentials: 'same-origin',
            headers: {
                'Content-Type': 'application/vnd.api+json',
            },
        };

        // Use cache if not older than 10 seconds
        config.maxCacheAge = 10;
        config.cache = CachingStrategy.CacheFirst;
    };
}

@NgModule({
    declarations: [
        AppComponent,
    ],
    imports: [
        BrowserModule,
        AuthenticationModule,
        AppRoutingModule,
        BrowserAnimationsModule,
        DashboardModule,
        MainModule,
        MaterialModule,
        HttpClientModule,
    ],
    providers: [
        {provide: HTTP_INTERCEPTORS, useClass: NoConnectionInterceptor, multi: true},
        {provide: HTTP_INTERCEPTORS, useClass: VersionMismatchInterceptor, multi: true},
        {provide: HTTP_INTERCEPTORS, useClass: UnauthorizedInterceptor, multi: true},
        {
            provide: ErrorHandler,
            useValue: Sentry.createErrorHandler({
                showDialog: false,
            }),
        },
        {provide: APP_COLLECTION, useValue: new AppCollection()},
        {
            provide: DATX_CONFIG,
            useFactory: (httpClient: HttpClient) => {
                return setupDatx(httpClient, {
                    baseUrl: '/api/v1/',
                });
            },
            deps: [HttpClient],
        },
        {
            provide: APP_INITIALIZER,
            useFactory: initDatx,
            multi: true,
            deps: [CustomFetchService],
        },
    ],
    bootstrap: [AppComponent]
})

export class AppModule {
}

Custom Fetch Service

import {HttpClient} from '@angular/common/http';
import {Injectable} from '@angular/core';
import {config, IResponseObject} from '@datx/jsonapi';
import {lastValueFrom, Observable} from 'rxjs';
import {map, takeUntil} from 'rxjs/operators';
import {IResponseHeaders} from '@datx/utils';

@Injectable({providedIn: 'root'})
export class CustomFetchService {
    constructor(private httpClient: HttpClient) {
    }

    public async fetch(
        method: string,
        url: string,
        body?: unknown,
        headers: Record<string, string> = {},
        fetchOptions?: { takeUntil$?: Observable<void> },
    ): Promise<IResponseObject> {
        const takeUntil$: Observable<void> | undefined = fetchOptions?.takeUntil$;

        const requestHeaders = {
            ...config.defaultFetchOptions.headers,
            ...headers,
        };

        let request$ = this.httpClient
            .request(method, url, {
                observe: 'response',
                responseType: 'json',
                headers: requestHeaders,
                body,
            })
            .pipe(
                map((response) => {
                    return {
                        data: response.body,
                        headers: response.headers as unknown as IResponseHeaders, // The interface actually matches
                        requestHeaders,
                        status: response.status,
                    };
                }),
            );

        if (takeUntil$) {
            request$ = request$.pipe(takeUntil(takeUntil$));
        }

        try {
            const d = await lastValueFrom(request$);
            if (d === undefined) {
                return {status: -1}; // Signal to DatX that it shouldn't fail, but shouldn't cache either
            }

            return d;
        } catch (e) {
            console.error(e);
            throw e;
        }
    }
}

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions