ruby-grape / grape

An opinionated framework for creating REST-like APIs in Ruby.
http://www.ruby-grape.org
MIT License
9.89k stars 1.22k forks source link

Provide a generic interface for requires: complex type. #827

Open dblock opened 10 years ago

dblock commented 10 years ago

http://pastebin.com/QQV0NJ5K

  # Entity
  module API
  module Entities
    class Article < Grape::Entity
      expose :title, documentation: { type: 'string', desc: 'Title' }
      expose :body, documentation: { type: 'string', desc: 'Body' }
    end
  end
end

  # API
  desc "Create an article"
  params do
    requires :article, type: API::Entities::Article, documentation: { example: "aklsdfj" }
  end
  post '/articles' do
    puts params
    article = Article.create(params(:title, :body))
    represent(article, env)
  end

Currently:

# curl -X POST -H "Content-Type:application/json" 0:9292/v1/articles -d '{"title":"hello","body":"world"}'  
# {"error":"article is missing"}
# curl -X POST -H "Content-Type:application/json" 0:9292/v1/articles -d '{"article":{"title":"hello","body":"world"}}'
# {"error":"article is invalid"}
antonzhyliuk commented 9 years ago

:+1:

dim commented 9 years ago

+1

0x5d commented 9 years ago

Acording to the documentation , it is supported (in a way, I guess).

birbird commented 9 years ago

Hi, what's status of this issue? I wrote something like

    params do
      requires :user, type: Entities::User
    end

But when I pass a user json as parameter, it throws {"error":"user is invalid"}

It the result expected or I made some mistake?

dblock commented 9 years ago

I believe https://github.com/intridea/grape/pull/1039 has implemented this, @rnubel would you confirm and I'll close this issue? Is this any different from #993?

dblock commented 9 years ago

@birbird this is on HEAD FYI, not in any released version of Grape yet.

dblock commented 9 years ago

Maybe we need to do something built-in for Grape entities though ...

birbird commented 9 years ago

@dblock Thanks a lot for you help.

Actually, before I asked, I have tried exactly the same code with https://github.com/intridea/grape#custom-types, which is also the sample code in #1039. But got the {"error":"color is invalid"} sadly.

All the things can be explained with your reminder.

A little suggestion, if this feature has not been released, it should not be in the document.

birbird commented 9 years ago

But even with custom-types, Grape Entity still could not be processd. custom-types require the input type is String, this is not the case for Entity.

rnubel commented 9 years ago

Yeah, like @birbird noted, #1039 added support for custom primitive types (i.e., something that can be parsed from a single string parameter) whereas this story requires support for complex types. And actually, grape does have support for that already, but only when done through Virtus. It would certainly be possible to also support coercion into Grape::Entitys, but that's not existing functionality.

guizmaii commented 9 years ago

Is it possible to express required/optional parameters in entities ?

With a required: parameters ? Does it fail if the title in not passed in the following example ? Or is it just documentation ?

module API
  module Entities
    class Article < Grape::Entity
      expose :title, documentation: { type: 'string', desc: 'Title', required: true }
      expose :body, documentation: { type: 'string', desc: 'Body', required: false }
    end
  end
end

Can we express complex validations as with "normal" params ? ( https://github.com/intridea/grape/blob/v0.12.0/README.md#parameter-validation-and-coercion)

atljeremy commented 9 years ago

I'm also wondering the exact same thing as @guizmaii. I have a Grape Entity with some required attributes but if I only use the desc block with params set to my Entity's documentation there isn't any validation being done on the params when i make requests to the API for those resources. The only way I can get validation of the params is to also use the separate params block like below...

This will validate the params:

desc 'Create a User.', {
    detail: 'Used to create a new User.',
    named: 'Create User',
    success: Entities::User,
    failure: [
        [400, 'Bad request', Entities::Error],
        [422, 'Bad parameter entry', Entities::Error]
    ],
    notes: <<-NOTE
      Creates a new User

      ## Request properties

      * _Safe:_ Yes
      * _Idempotent:_ No
      * _Can be retried:_ Yes
    NOTE
}
params do
  requires :email, type: String, desc: 'Email address.'
  requires :password, type: String, desc: 'Password.'
  requires :password_confirmation, type: String, desc: 'Password Confirmation.'
  optional :first_name, type: String, desc: 'Users first name.'
  optional :last_name, type: String, desc: 'Users last name.'
  optional :phone, type: Integer, desc: 'Users phone number.'
  optional :address, type: String, desc: 'Users address.'
  optional :address_two, type: String, desc: 'Users address line two.'
  optional :city, type: String, desc: 'Users city.'
  optional :state, type: String, desc: 'Users state.'
  optional :zip, type: Integer, desc: 'Users zip code.'
end
post do
  # stuff ...
end

This will not validate params:

desc 'Create a User.', {
    detail: 'Used to create a new User.',
    named: 'Create User',
    params: Entities::CreateUserParams.documentation,
    success: Entities::User,
    failure: [
        [400, 'Bad request', Entities::Error],
        [422, 'Bad parameter entry', Entities::Error]
    ],
    notes: <<-NOTE
      Creates a new User

      ## Request properties

      * _Safe:_ Yes
      * _Idempotent:_ No
      * _Can be retried:_ Yes
    NOTE
}
post do
  # stuff ...
end

# here is what CreateUserParams looks like...
module API
  module V1
    module Entities
      require 'grape-entity'
      class User < Grape::Entity
        expose :id, documentation: { type: Integer, desc: 'User unique identifier.' }
        expose :first_name, documentation: { type: String, desc: 'User first name.' }
        expose :last_name, documentation: { type: String, desc: 'User last name.' }
        expose :email, documentation: { type: String, desc: 'User unique email address.' }
        expose :username, documentation: { type: String, desc: 'User unique username.' }
        expose :errors, documentation: { type: Hash, desc: 'A hash containing any validation errors that may have occurred.' }
        expose :phone, documentation: { type: Integer, desc: 'User phone number.' }
        expose :address, documentation: { type: String, desc: 'User address line 1.' }
        expose :address_two, documentation: { type: String, desc: 'User address line 2.' }
        expose :city, documentation: { type: String, desc: 'User city.' }
        expose :state, documentation: { type: String, desc: 'User state.' }
        expose :zip, documentation: { type: Integer, desc: 'User zip code.' }
        expose :created_at, documentation: { type: DateTime, desc: 'The date the user was created.' }
        expose :updated_at, documentation: { type: DateTime, desc: 'The date the user was last updated.' }
      end

      class CreateUserParams < User
        unexpose :id
        unexpose :email

        expose :email, documentation: { type: String, desc: 'User unique email address.', required: true }
        expose :password, documentation: { type: String, desc: 'Password.', required: true }
        expose :password_confirmation, documentation: { type: String, desc: 'Password Confirmation.', required: true }
      end

      class UpdateUserParams < User
        unexpose :id

        expose :id, documentation: { type: Integer, desc: 'User unique identifier.', required: true }
        expose :password, documentation: { type: String, desc: 'Password.' }
        expose :password_confirmation, documentation: { type: String, desc: 'Password Confirmation.' }
      end
    end
  end
end
dpad commented 8 years ago

Same request as per @guizmaii and @atljeremy above. Did either of you manage to get some sort of simple/elegant workaround?

atljeremy commented 8 years ago

@dpad Unfortunately no. I'm having to use the explicit params do block to define required and optional params separately from my Grape::Entity's. I'm really hoping that this gets updated so that I don't have to keep maintaining two separate sets of params documentation.

emanoelxavier2 commented 6 years ago

+1

nanderss commented 6 years ago

I was wondering if there's a solution to this?

maxgurewitz commented 4 years ago

+1