vuex-orm / plugin-axios

Vuex ORM persistence plugin to sync the store against a RESTful API.
https://vuex-orm.github.io/plugin-axios/
MIT License
361 stars 52 forks source link

Missing related polymorphic model on store entities after data transformation #129

Open yoelpc4 opened 4 years ago

yoelpc4 commented 4 years ago

Describe the bug

I have User, Institute, and Activity model with morph many relationship as follow:

  1. User morph many actions
  2. User morph many activities
  3. Institute morph many activities

and inverse morph many relationship as follow:

  1. Activity morph to causer
  2. Activity morph to subject

The purpose of activity model is to logging activities (create, update, soft delete, restore, or force delete) of another model also the causer of the activity itself. In simple terms we can assume that:

  1. User causes actions as a causer of an Activity
  2. User activities are recorded as a subject of an Activity
  3. Institute activities are recorded as a subject of an Activity

I'm using JSON:API as API framework, in order to makes JSON:API response compatible with Vuex ORM data I'm using json-api-response-converter as response transformer package.

I don't know why at data transformation it works well, but when at data retrieval it gives id, type, and model morph to with null value.

Steps to reproduce the bug

  1. Visit the reproduction link

  2. When we check the console log of data transformation L:20, the subject is exists on activity 7672 also both causer and subject are exists on activity 7934.

  3. At the same time when we check the console log of data retrieval console log L:36, the subject is null from activity 7672 also both causer and subject are null from activity 7934.

Expected behavior

Persisted data with related polymorphic on vuex store entities is conformed to transformed data with related polymorphic.

Versions

cuebit commented 4 years ago

While this isn't an issue with the axios plugin per-se, it seems to point to either Vuex ORM or json-api-response-converter.

Currently a morphTo relation expects the id and type to exist in the given record since it doesn't need to attach foreign keys to the related model. Given your transformed data doesn't provide that, the necessary relations are not generated. For example, the following payload will generate the relations for a morphTo:

Activity.insert({
  data: {
    id: 'a1',
    causer_id: 'u1',
    causer_type: 'users',
    causer: { id: 'u1', name: 'Admin' }
  }
})

I'm not sure if this is Vuex ORM just not generating the missing foreign keys (something @kiaking could shed light on perhaps?)... But As a workaround, you could attach the would-be foreign keys to the Activity model in the transformer:

static apiConfig = {
  dataTransformer: (response) => {
    const data = new JsonApiResponseConverter(response.data).formattedResponse

    data.forEach((item, index) => {
      if (item.causer) {
        data[index].causer_id = item.causer.id
        data[index].causer_type = 'users'
      }
      if (item.subject) {
        data[index].subject_id = item.subject.id
        data[index].subject_type = 'institutes'
      }
    })

    return data
  }
}

Here's a workable fork.

yoelpc4 commented 4 years ago

Thanks for responding my issue @cuebit, the reason why I'm asked here instead of on vuex-orm package, is because of the dataTransformer timing so that I can provides data with expected morph to format.

This case is using User as causer and Institute as subject, anyway how can I guess the type of related data if I have registered 44 models on vuex-orm database?

Another point to mention is JSON:API prefers kebab-case on resource's type, but vuex-orm prefers snake-case on model's entity. Should I make a morph map to solve this case?