Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to override type? #618

Open
bijanx opened this issue Feb 16, 2016 · 12 comments
Open

How to override type? #618

bijanx opened this issue Feb 16, 2016 · 12 comments

Comments

@bijanx
Copy link

bijanx commented Feb 16, 2016

We have a model in the backend that we refer to as a different name in our public API. We need to override the type returned but haven't found a way to do this yet.

I tried adding def self._type (as I noticed that's where it was grabbing type from) to my base model class as well as the resource serializer but that didn't change the response.

Any help would be appreciated.

@lgebhardt
Copy link
Member

@bijanx
Copy link
Author

bijanx commented Feb 16, 2016

Thanks. I tried that and it didn't work. Here's the setup:

project table
project.rb defining Project class
ProjectsController
ProjectResource

I need the type in the json responses for projects to be 'study'.

# still returns type as 'project'
class Api::V2::ProjectResource < JSONAPI::Resource
  model_name 'Study', add_model_hint: false
end

@bijanx
Copy link
Author

bijanx commented Feb 16, 2016

I was able to make it work by changing resource and controller name to the public API name, and using model name to point the public-named resource to the correct backend model.

Not sure if this is a good solution since it's not conventional Rails where the controller name be the same as the model name.

@lgebhardt
Copy link
Member

In JR the convention is for the controller to match the resource name. The resource acts as an intermediate layer mapping to the model so the naming disparity is expected.

@bijanx
Copy link
Author

bijanx commented Feb 16, 2016

Alright cool. Thanks @thibaudgg !

@thibaudgg
Copy link
Contributor

@bpourriahi not sure why, but you're welcome anyway! 😄

@bijanx
Copy link
Author

bijanx commented Feb 17, 2016

Heh. That may or may not been a 'typo'. Thank you @lgebhardt.

@gdlx
Copy link

gdlx commented Nov 23, 2016

@lgebhardt I'm using namespaced STI and I think JR type convention doesn't match this use case.

As explained in http://jsonapi.org/format/#document-resource-objects:

The type member is used to describe resource objects that share common attributes and relationships.

I'm working on an infrastructure management app that includes a lot of models that are namespaced to avoid any collision. For example, my model DNS::Zone will be typed as zones by JR. But I could have another kind of zone in the future.

IMHO, JR type should actually match the URL namespace, which in my case is dns/zones.

This is a little different with namespaced STI like my CDN::Purge which is declined to CDN::Purge::Custom, CDN::Purge::Full and CDN::Purge::Collection that are respectively typed customs, fulls and collections which have no sense.

My first thought was to rename my models like CDN::Purge::CustomPurge, which would be typed custom_purges.

This would be fine except that the cdn namespace is still missing, and that my controller and route are still purges.

That means that the type should actually not match the controller name, but the model name, given the resource attributes could vary in the same controller. In that case, I wouldn't have to rename my models as I don't hink JR should impose a model naming convention.

In any case, the type field should be customizable, even if convention is always better.

@adamdullenty
Copy link

Is this something which is being considered? I've tried the workarounds above and had no luck.

We are in a similar situation where we have multiple services which might share resource names (e.g. AssetFile) which will be different across the services. Ideally we want to namespace the types to match the URL namespace e.g. something like this:

model_name "ExampleServiceName::AssetFile"

def type
  "example_service_name/asset_files"
end

Even better would be if we can define the prefix globally across each app by defining this in a base class, or ideally have JR pick the namespacing up from the namespaced model_name in the above example.

@adambedford
Copy link

adambedford commented Jun 29, 2017

I'm also trying to figure this out.

I have STI models Events::Applied, Events::Interview which inherit from base model Event. I'd like to use a common controller/endpoint api/v1/events but have the type as events/applieds or events/interview (similar model names to @gauthier-delacroix which leads to weird pluralization ("applieds"). In hindsight I'd have named the models Events::AppliedEvent etc. but I don't think that actually affects this.

Namespaced type is important to me since my frontend Ember app has a similar folder structure with Event subclasses under an events directory. With a type of events/applied, the Ember resolver will look under models/events/applied.js rather than models/applied.js

@kgathi2
Copy link

kgathi2 commented Feb 19, 2018

Did this by using jsonapi-utils by explicitly defining the actions in the controllers so you know exactly whats going on.

# Specify a particular HTTP status code
jsonapi_render json: new_user, status: :created

# Forcing a different resource
jsonapi_render json: User.all, options: { resource: V2::UserResource }

# Using a specific count
jsonapi_render json: User.some_weird_scope, options: { count: User.some_weird_scope_count }

# Hash rendering
jsonapi_render json: { data: { id: 1, first_name: 'Tiago' } }, options: { model: User }

# Collection of Hashes rendering
jsonapi_render json: { data: [{ id: 1, first_name: 'Tiago' }, { id: 2, first_name: 'Doug' }] }, options: { model: User }

@andreasgebhard7
Copy link

andreasgebhard7 commented Aug 17, 2022

2022 and still the same problem: I have versioned models (Api::V1::ModelName, Api::V2::ModelName) and both get typed as 'model-name'. So, if the server gets such a serialiazed model instance and wants to deserialize it, how is it supposed to know the correct model version?

I have tracked this issue to the following code section (in 0.10.7):

# ~/.rvm/gems/ruby-3.0.0@climate/gems/jsonapi-resources-0.10.7/lib/jsonapi/basic_resource.rb, lines 445-446
type = subclass.name.demodulize.sub(/Resource$/, '').underscore
subclass._type = type.pluralize.to_sym

So this is where the class name gets demodulized and then, in

# ~/.rvm/gems/ruby-3.0.0@climate/gems/jsonapi-resources-0.10.7/lib/jsonapi/resource_serializer.rb, on line 172
obj_hash['type'] = format_key(source.class._type.to_s)

the type gets set to the value of _type.to_s which has been set previously. At the moment I cannot see any built-in option for customizing or overriding this hard-coded demodularization. I have therefore modified my local files as follows:

# ~/.rvm/gems/ruby-3.0.0@climate/gems/jsonapi-resources-0.10.7/lib/jsonapi/resource_serializer.rb, lines 172-173
# obj_hash['type'] = format_key(source.class._type.to_s)
obj_hash['type'] = source.class.to_s

With this, the _type attibute of a resource still gets set to a demodulized value but the serializer now uses the full modularized class name:

reload!; JSONAPI::ResourceSerializer.new(Api::V2::ClimateDatumResource).object_hash(Api::V2::ClimateDatumResource.new(Api::V2::ClimateDatum.first, nil), nil)

Now yields:

{"id"=>"1",                 
 "type"=>"Api::V2::ClimateDatumResource",
 "attributes"=>{"ambient-temperature"=>20.0, "ambient-humidity"=>50.0, "atmospheric-pressure"=>952.0, 
 "room-no"=>438, "position"=>"windows", "measured-at"=>"2022-07-22T14:31:20.884Z"},
 "meta"=>{:received_at=>Fri, 22 Jul 2022 14:38:11.107411000 UTC +00:00}} 

Et voilà! Again, this is not really configurable and just as hard coded as the previous solution. However, at the moment it suits my needs. In order to make this portable to other machines, I have forked the master branch, patched the code with a commit and am now installing directly from there.

Finally: Thanks to @lgebhardt for this great piece of software!

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

No branches or pull requests

9 participants