Skip to content

Conversation

@Rahien
Copy link

@Rahien Rahien commented Aug 19, 2015

The type parameter is necessary to choose the service build the right
resources. The type is not always the same as the relation name.

For instance say there is a Person that has a relationship called 'friends' to other Persons and a relationship called 'children' to other Persons as well. The specification in the resource would now be:

friends: hasMany('friends', 'persons')
offspring: hasMany('children', 'persons')

The previously you would get an exception when the name of the relation was not the same as the type.

The type is now the second argument in the specification of the relation in the resource, but it is optional. If the type is indeed the same as the relation name, it can be omitted.

Rahien added 2 commits August 19, 2015 16:31
The type parameter is necessary to choose the service build the right
resources. The type is not always the same as the relation name.
@pixelhandler
Copy link
Owner

I'll take a look tomorrow, thanks.

@pixelhandler
Copy link
Owner

@Rahien yeah I see how the type value helps when the reasource name is not the same as the type.

I'm wondering about how to set that up so it's clearly documented, for example:

friends: hasMany('friends', 'persons')

vs.

friends: hasMany({ resource: 'friends', type: 'persons' })

so instead of two parameters there would still be one parameter passed to hasMany or hasOne, and i the argument is a String then the resource name and the type value are equal, alternatively if the argument is an Object then the resource and type are set and used accordingly.

Any thoughts?

the relations now accept either a string or an object. the object holds
the specification of the relationship in the format:

hasMany( { resource: "friends",
           type: "persons" } )

If the type of the relationship is the same as the name of the
relationship, type can be omitted in the specification object.

Passing

hasMany( "persons" )

is the same as

hasMany( { resource: "persons",
           type: "persons" } )
@Rahien
Copy link
Author

Rahien commented Aug 21, 2015

Yes! That makes a lot of sense, I changed it in the latest commit.
Thanks for the feedback!

@pixelhandler
Copy link
Owner

@Rahien I'm trying to figure out a test case for this usage of a resource with a different type name. do you have any JSON examples you can share from your API? Are you using JSONAPI::Resources gem?

@pixelhandler
Copy link
Owner

@Rahien I think your use case is that a service may be named 'persons' which can return various resource types, e.g. Children and Friends. And, those types are relation names as well. But when used as a relation only the 'persons' service (endpoint) is used to find the resources.

This use case seems doable but it will require some extra setup when generating resources with the ember-cli generator tool. You'll need to generate a model for the Child and Friend types; but not services using those resource names (children and friends services/endpoints do not exist, only persons). And, you may need to generate a 'persons' resource - which may only be an abstract model used when extending to create Child or Friend resources (model prototypes).

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm curious if 'promise': promise, 'type': relation would also need to change to 'promise': promise, 'type': type ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From what I can tell, the type parameter on line 398 is not used at the moment, but I am probably missing something here...

@pixelhandler
Copy link
Owner

I'd like to create a test to accompany this pull request but am not familiar with how your app is using this optional resource/type value for using a common server for various resources. I'd need some JSON docs and a the resource (model) prototypes to setup a test.

@Rahien
Copy link
Author

Rahien commented Aug 22, 2015

Let me start by thanking you for all your help! Now, the actual use case that I am trying to solve is the front end of a taxonomy matching service. A user receives a list of topics and the topics have mappings to topics of another taxonomy. These mappings are directional and receive a match type selected by the user, e.g. exact, close, narrow, broad (the last two explain why the match is directional).

So, the mappings have two relationships to topics, a "from" and a "to" relation, both pointing to a resource of the kind "topic". There is no reason to use a different model or service to instantiate these resources (though I can see how polymorphic relations could be challenging). In my case both relations are of the type "topic", but they have different names and different semantics.

Here is an example json file that is returned by the back end that I am using (generated by https://github.com/mu-semtech/mu-cl-resources), this returns the list of mappings for a topic:

{"data": [
    { "attributes": { "matchtype": "exact" },
      "id": "MatchOne",
      "relationships": {
          "from": {
              "links": {"related": "/mappings/MatchOne/from", "self": "/mappings/MatchOne/links/from"}
          },
          "to": {
              "links": {"related": "/mappings/MatchOne/to", "self": "/mappings/MatchOne/links/to"}
          }
      },
      "type": "mappings"
    },
    { "attributes": { "matchtype": "broader"},
      "id": "MatchTwo",
      "relationships": {
          "from": {
              "links": {"related": "/mappings/MatchTwo/from", "self": "/mappings/MatchTwo/links/from"}
          },
          "to": {
              "links": {"related": "/mappings/MatchTwo/to", "self": "/mappings/MatchTwo/links/to"}
          },
      },
      "type": "mappings"
    }
]}

the topics themselves have the following resource file (coffeescript):

`import Ember from 'ember'`
`import Resource from './resource'`
`import { attr, hasOne, hasMany } from 'ember-jsonapi-resources/models/resource'`

Mapping = Resource.extend
  type: 'mappings'
  service: Ember.inject.service 'mappings'

  score: attr()
  matchtype: attr()

  from: hasOne { resource: 'from', type: 'topic' }
  to: hasOne { resource: 'to', type: 'topic' }


`export default Mapping`

So there are two hasOne relations with the same type "topic". This was previously impossible as the name of the resource was assumed to be the same as the topic.

Is there anything else that is still unclear in the use case?

@pixelhandler
Copy link
Owner

@Rahien I did some work to figure out what is needed to support polymorphic associations, see #37 however that is not directly related to your use case.

from: hasOne { resource: 'from', type: 'topic' }
  to: hasOne { resource: 'to', type: 'topic' }

These reference relations to and from both are always using a type of topic If I understand correctly the use case simply needs to have a resource name that is used in the relationship object of the resource's payload and use a specific service (endpoint) with a different name (type). Basically giving more configuration options.

I am still curious if just creating a service (which uses the application adapter and serializer) for the from and to relations would work since they are only used as relations and don't need a resource (model) prototype. See the example I added in the pull request #37 for polymorphic association support - I used an 'imageable' service that is only used since that is the name of the relation. I added the service, adapter and initializer for the imageable (polymorphic) association between a picture resource that has-one (belongs to) employee or product.

I'm wondering what would be best…

A) to have a simple service that is used only for the findRelated adapter method for every relation (no need to add extra configuration)

Or,

B) to provide the proposed options to provide a type that is used for the service when the relation is fetched via the service#findRelated call.

@oliverbarnes
Copy link
Contributor

hey guys, fwiw, I had a similar case where the same entity could be of a different type when in a relationship. In it a participant is also an author to a proposal, and a me (current user) resource.

I got bit by EJR not accepting a type and relation names being different, and almost posted a feature request here as well. But then decided to try and solve this on the backend, by having different endpoints and resources for these relationships. So, in rails, the Participant model now backs three different resources - ParticipantResource, AuthorResource and MeResource. Each of these are defined, using JSONAPI::Resources, with a model_name 'Participant'. And then three endpoint groups for both these resource and their relationship endpoints.

This clicked for me, seems intuitive to have the same resources defined on the front end and backend, instead of having to translate the types so a front-end resource maps to a different one on the backend. Makes it easy for the front-end to decide what resources it needs. Also gave me the freedom to return different payload for each of the resource representations on the backend.

This assumes one has control of the API, though, to keep the resource modeling in sync between the two. I'm not sure what the JSONAPI authors think about the common enough scenario of front-end devs having to adapt to APIs that don't map to their local modeling. My impression is the philosophy has been for the front-end to drive the API design from the beginning?

@pixelhandler
Copy link
Owner

@Rahien one more thing I am curious about… in your use case will the to and from relations sometimes use a different type? (I.e. sometimes the topic for a mapping but maybe another subject for a grouping) If the to and from resources do always use the topic service/endpoint then the to and from adapters can define how the url is used.

export default ApplicationAdapter.extend({
  type: 'from',
  url: '/topics',
  fetchUrl: function(url) {
    /* manipulate if needed */
    return url;
  }
});

@Rahien
Copy link
Author

Rahien commented Aug 23, 2015

Hmmm regarding the url, indeed at the moment the type of both relations is always 'topic'. I see what you mean, for every relation, you would create an adapter and a service. I guess that works, but isn't the overhead of defining an extra adapter and service every time a bit much? It does away with extra configuration options being passed in, but there is so much extra code involved when using it... Just referring to an existing service in the model seems less confusing to me.

Still: personal opinion, so it's your call of course!

@pixelhandler
Copy link
Owner

@Rahien I agree it's a nice option to specify another type as an alias for a relation. I just needed to better understand the use case so I could come up with a test for it. I'll work on adding a test.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants