angular2-jsonapi icon indicating copy to clipboard operation
angular2-jsonapi copied to clipboard

[BUG] Nested models are not correctly extracted

Open frans-beech-it opened this issue 6 years ago • 4 comments

When you have a response looking like:

data => [
      [
         id: 1,
         type: returnables,
         relationships: {
             parent: [
                id: 2,
                type: returnables,
             ]
         }
      ],
      [
         id: 2,
         type: returnables,
         relationships: {
             childs: [
                [
                  id: 1,
                  type: returnables,
                ]
             ]
         }
      ]
  ],
  included: []

The relations are not correctly extracted because in json-api-datastore.service::extractQueryData() it processes only the body.included but in above example some of the relations are already in body.data. The json-api specs write:

A compound document MUST NOT include more than one resource object for each type and id pair.

So in my opinion the response is ok, and extractQueryData() should also take body.data into account.

Our current workarround is to override extractQueryData in our own datastore:

  protected extractQueryData<T extends JsonApiModel>(
    body: any,
    modelType: ModelType<T>,
    withMeta = false
  ): T[] | JsonApiQueryData<T> {
    const models: T[] = [];
    const allRawResources = body.data;
    if (body.included) {
       allRawResources = allRawResources.concat(data.included);
    }

    body.data.forEach((data: any) => {
      const model: T = this.deserializeModel(modelType, data);
      model.syncRelationships(data, allRawResources);
      this.addToStore(model);
      models.push(model);
    });

    if (withMeta && withMeta === true) {
      return new JsonApiQueryData(models, this.parseMeta(body, modelType));
    }
    return models;
  }

frans-beech-it avatar Jul 03 '19 07:07 frans-beech-it

@frans-beech-it Hey, the code above didn't work for me, can you share how did you configure your DatastoreConfig[models]?

Alxzu avatar Jul 04 '19 16:07 Alxzu

Ah sorry. I patched an older release.

This should work, not tested to be honest

protected extractQueryData<T extends JsonApiModel>(
    response: HttpResponse<object>,
    modelType: ModelType<T>,
    withMeta = false
  ): Array<T> | JsonApiQueryData<T> {
    const body: any = response.body;
    const models: T[] = [];
    let allRawResources = body.data;
    if (body.included) {
       allRawResources = allRawResources.concat(data.included);
    }

    body.data.forEach((data: any) => {
      const model: T = this.deserializeModel(modelType, data);
      
      model.syncRelationships(data, allRawResources);
      this.addToStore(model);

      models.push(model);
    });

    if (withMeta && withMeta === true) {
      return new JsonApiQueryData(models, this.parseMeta(body, modelType));
    }

    return models;
  }

fsaris avatar Jul 04 '19 18:07 fsaris

This should be fixed in version v7.1.0 @frans-beech-it can you verify?

safo6m avatar Jul 18 '19 10:07 safo6m

@safo6m This is not fixed. Or can you link a pull request?

@fsaris, the JSON:API spec also states, that

In a compound document, all included resources MUST be represented as an array of resource objects in a top-level included member.

For me, this sounds like even if the related resource is already in the primary data, it should also be in the included member.

But they are not so sure about it, because in the next section they state, that

These resource identifier objects could either be primary data or represent resource linkage contained within primary or included resources.

and of course later the line you cited in your first post.

So I think this library should support this scenario (and it does already partly since #241 was merged) and I will provide a pull request for it.

hpawe01 avatar Jan 20 '21 14:01 hpawe01