gaorlov / deserializer

Deserialization of complex API params into objects that can be consumed by an AR model
MIT License
22 stars 3 forks source link

JSONAPI Parameter Support / Validation? #8

Open chr0n1x opened 4 years ago

chr0n1x commented 4 years ago

Hey @gaorlov, first of all - thanks for the gem it's really nice to use! Appreciate all the work that you've done for it.

This is just a question, might be a bit dumb so sorry in advance.

I'm currently using this gem to wrap parameter requires for a JSON API. I'm doing some funky things per the JSONAPI spec. So an example for resource updates:

attr_params = params.require(:data).require(:attributes)
MyModelDeserializer.from_params(attr_params)

So here I've got params.require calls enforcing the schema for the request params, i.e.:

{
    "data": {
        "type": "my_model",
        "id": "123456",
        "attributes": {
            "model_name": "asdfasdfasdf"
        }
    }
}

So the question is: do you have a recommended way of setting up deserializers for JSON API parameters? I was kind of going to go in a direction w/ something like:

class MyBaseDeserializer < Deserializer::Base
    attribute :data
end

class JSONAPIDataDeserializer < MyBaseDeserializer
    has_one :attribute_hash, key: :attributes, nest_attr: :data
    has_many :relationships, ...
end

class MyModelDeserializer < JSONAPIDataDeserializer
    attribute :model_name
end

That's not an exact/functioning example, but I was hoping that the gem supports some kind of attribute nesting out-of-the-box via deserializer inheritance or something.

Thanks again!

gaorlov commented 4 years ago

Thanks for the question! I'm excited to see that this gem is being used! A couple things:

If you were so inclined, it would be great to have a PR with basically that code and a few tests. Of course I get that you may not have the time, so no worries. Hopefully this answers your question.

Let me know how I can help.

Greg

chr0n1x commented 4 years ago

@gaorlov thanks for replying!

Our app is a Rails app, but the rails-specific integrations & patterns for gems like JSONAPI::Resources skeeved us out a bit so we're going in a direction that attempts to lean towards pure ruby. As a result, the server layer is based on fotinakis/jsonapi-serializers. We've got what we think is a fairly nice pattern now partly due to the lack of Rails magic. That's the main reason why I started an attempt at integrating your gem into our deserialization layer 😄

I figured (i.e.: hacked) something out that works for what we need before you replied:

class JSONAPIDataDeserializer < ::Deserializer::Base
    def deserialize
        # other validations for params here

        @params = params.require(:data).require(:attributes)
        super
    end
end

class MyModelDeserializer < JSONAPIDataDeserializer
    attribute :model_name
end

I guess that this approach seemed "ok" to me since I explicitly reference super but...I could be smoking something here, and/or my sense of "aesthetics" is way off track as I now realize I'm mutating instance vars controlled by the parent class 😖

I guess that I could do the same if I overwrote self.from_params though. I thought about overriding it at some point but I couldn't see a "clean" way to handle the relationships attribute. That being said, I only thought about it and didn't actually try to implement anything that would've provided a basis for any proof-of-concept one way or another.

All of that being said then, is the encouraged method for digging and validating the "scaffolding" around JSONAPI :attributes to override self.from_params? Would you similarly have to overwrite self.permitted_params in that case too?

gaorlov commented 4 years ago

Thanks for the detailed reply! My thinking was that - if possible - it would be nice to have a JSON API V1.0 compliant base class. Whether you want to PR it back into the gem here, or if you want to keep it private is up to you.

In terms of what's encouraged, if you were to submit a PR, I would probably be the most comfortable with a subclass that implements the public Deserializer::Base API with JSON API V1.0 compliant behavior. But that's not to say that the approach you took is "wrong" in any way; just different from the style of this particular gem.

For a fully API V1.0 compliant implementation, you are right that permitted_params would probably get modified. As far as relationships go, the base class would need a pathing class that would get injected into the associations. Let me know if you want to pursue that option. It's not a difficult change, but is too long to sketch in an answer

All that said, let me know how I can help. If it's guidance with subclassing: great; if you have a PR you would like to collaborate on: excellent.

Thanks!

Greg

chr0n1x commented 4 years ago

hey @gaorlov, all of what you're saying makes sense. I was able to get something working for our app given the public APIs for the base class so thanks again for the help! 😄

9 contains our base class. Tests are very minimal and I added a docker-based dev environment (I dont have ruby on my host).

I can't contribute much more simply due to time constraints. But I hope that the PR is at least somewhat acceptable and helps.