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

Expose collection of items of different types without keys #137

Open
ghost opened this issue Jul 8, 2015 · 6 comments
Open

Expose collection of items of different types without keys #137

ghost opened this issue Jul 8, 2015 · 6 comments
Labels

Comments

@ghost
Copy link

ghost commented Jul 8, 2015

Hi.

I've recently tried to expose collection of objects of different types, but it seems that it's not possible with a current version.

Here's the result I'm trying to achieve:

{
  included: [
    {
      id: 1,
      type: 'posts',
      attributes: {
        title: 'Title 1',
        date: '2015-05-01'
      }
    },
    {
      id: 2,
      type: 'posts',
      attributes: {
        title: 'Title 2',
        date: '2015-05-02'
      }
    },
    {
      id: 1,
      type: 'users',
      attributes: {
        first_name: 'John',
        last_name: 'Doe',
        email: 'john.doe@example.com'
      }
    }
  ]
}

And classes:

class Result < Grape::Entity
  expose :included, documentation: { is_array: true } do
    expose :posts, using: Post
    expose :user, using: User
  end
end

class Post < Grape::Entity
  expose :id
  expose :type
  expose :attributes do
    expose :title
    expose :date
  end
end

class User < Grape::Entity
  expose :id
  expose :type
  expose :attributes do
    expose :first_name
    expose :last_name
    expose :email
  end
end

Is there something I am missing?

@ghost
Copy link
Author

ghost commented Jul 8, 2015

Oh yeah, and the response I'm getting is:

{
  included: [
   posts: [
   {
      id: 1,
      type: 'posts',
      attributes: {
        title: 'Title 1',
        date: '2015-05-01'
      }
    },
    {
      id: 2,
      type: 'posts',
      attributes: {
        title: 'Title 2',
        date: '2015-05-02'
      }
   }
   ],
   user:  {
      id: 1,
      type: 'users',
      attributes: {
        first_name: 'John',
        last_name: 'Doe',
        email: 'john.doe@example.com'
      }
    }
  ]
}

@dblock dblock added the bug? label Jul 8, 2015
@marshall-lee
Copy link
Member

It will be possible to achieve this when #56 will be fixed. I made an attempt here: #151.
When #56 will be fixed, try this example (you can already try this on my exposures branch)

require 'grape_entity'
require 'date'
require 'json'

Post = Struct.new(:id, :title, :date) do
  def type
    'posts'
  end
end

User = Struct.new(:id, :first_name, :last_name) do
  def type
    'users'
  end
end

class PostAttributes < Grape::Entity
  expose :title
  expose :date
end

class UserAttributes < Grape::Entity
  expose :first_name
  expose :last_name
end

class ResultEntity < Grape::Entity
  class Included < Grape::Entity
    expose :id
    expose :type
    expose :attributes,
      using: PostAttributes,
      if: ->(obj, opts) { obj.is_a? Post },
      proc: ->(obj, opts) { obj }
    expose :attributes,
      using: UserAttributes,
      if: ->(obj, opts) { obj.is_a? User },
      proc: ->(obj, opts) { obj }
  end

  def included
    object[:posts] + [object[:user]]
  end

  expose :included, using: Included
end

model = {
  posts: [
    Post.new(11, 'post #1', Date.parse('2015-01-01')),
    Post.new(12, 'post #2', Date.parse('2015-02-01'))
  ],
  user: User.new(1, 'Vladimir', 'Kochnev')
}

puts JSON.pretty_generate ResultEntity.represent(model, serializable: true)

Output:

{
  "included": [
    {
      "id": 11,
      "type": "posts",
      "attributes": {
        "title": "post #1",
        "date": "2015-01-01"
      }
    },
    {
      "id": 12,
      "type": "posts",
      "attributes": {
        "title": "post #2",
        "date": "2015-02-01"
      }
    },
    {
      "id": 1,
      "type": "users",
      "attributes": {
        "first_name": "Vladimir",
        "last_name": "Kochnev"
      }
    }
  ]
}

Main caveat of this approach: as more included entities will be added, more :if conditions you would need. It may be even good in some cases but to fully bypass I'd like to implement lambda passing to using option.

@marshall-lee
Copy link
Member

Also it looks like you're trying to implement JSON API compliant entities.
I think it deserves its own implementation. Something like Grape::Entity::JsonApi.

@ghost
Copy link
Author

ghost commented Jul 22, 2015

Thanks for the heads up. And yes, I was trying to implement JSON API v1.0, though unsuccessfully with Grape::Entity, so I switched over to Roar. Another problem is that Grape Swagger does not really support deeply nested models/parameters, but that's another story.

@dblock
Copy link
Member

dblock commented Jul 23, 2015

I have been using Roar in production as well, and cannot encourage everyone to stick to a Hypermedia standard enough. So, I would welcome Grape::Entity support for HAL and JSON API as well.

@francois-belle
Copy link

francois-belle commented Apr 10, 2019

Hey guys, I was also trying to implement a JSON API using grape / grape-entity / grape-sagger.
So far, I spent half a day trying to figure out which solution might fit all my needs...

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

No branches or pull requests

3 participants