Adds Jsonapi v1.0 sugar on Grape-Entity.
Add the grape
, grape-entity
and grape-jsonapi_entity
gems to Gemfile.
gem 'grape'
gem 'grape-entity'
gem 'grape-jsonapi_entity'
Run bundle install
.
class API < Grape::API
format :jsonapi
formatter :jsonapi, Grape::Jsonapi::Formatter
end
Following Json Api v 1.0 spec. Resource Objects may expose the following fields
field | description |
---|---|
id |
automatically exposed by Grape::Jsonapi::Entity::Resource |
type |
automatically exposed by Grape::Jsonapi::Entity::Resource , additionally it will make an attempt to automatically determine a type using either the plural word passed via the root method, or based on the class name of the resource entity. To specify a type , you can overload the json_api_type method in your resource as shown here. You can also use the json_api_type keyword as in this example |
attributes |
can be exposed using the attribute method, instead of expose . All expose options and block syntax should work with attribute |
relationships |
relationships of compound-documents can be represented using the nest method, and expects the :using option to be passed with another Resource Entity |
module API
module Entities
class Status < Grape::Jsonapi::Entity::Resource
root 'statuses'
format_with(:iso_timestamp) { |dt| dt.iso8601 }
attribute :user_name
attribute :text, documentation: { type: "String", desc: "Status update text." }
attribute :ip, if: { type: :full }
attribute :user_type, :user_id, if: lambda { |status, options| status.user.public? }
attribute :location, merge: true
attribute :contact_info do
expose :phone
expose :address, merge: true, using: API::Entities::Address
end
attribute :digest do |status, options|
Digest::MD5.hexdigest status.txt
end
nest :replies, using: API::Entities::Status, as: :responses
nest :last_reply, using: API::Entities::Status do |status, options|
status.replies.last
end
with_options(format_with: :iso_timestamp) do
attribute :created_at
attribute :updated_at
end
end
end
end
module API
module Entities
class StatusDetailed < Grape::Jsonapi::Entity::Resource
attribute :internal_id
end
end
end
Overloading the json_api_type
method is one way of specifying the type
field in your Json Api output. The following code will output 'Weather' in the type
field.
module API
module Entities
class Status < Grape::Jsonapi::Entity::Resource
attribute :user_name
attribute :ip
def json_api_type
'Weather'
end
end
end
end
This follows the grape-entity
use of present.
but instead of passing your entity directly to :with, we wrap it in a factory call to nest the data inside Jsonapi's
top level document structure. It also makes the output of the type
field the result of current_user.admin? ? :full : :default
class Statuses < Grape::API
version 'v1'
desc 'Statuses index' do
params: API::Entities::Status.documentation
end
get '/statuses' do
statuses = Status.all
type = current_user.admin? ? :full : :default
present({ data: statuses },
with: Grape::Jsonapi::Document.top(API::Entities::Status),
json_api_type: type
)
end
end
This fulfills server responsibilites for JSON API Content Negotiation v1.0
and makes one addition. JSON API specifies that the client must send Content-Type: application/vnd.api+json
with all
request documents. This gem specifies that the client must send Content-Type: application/vnd.api+json
with every request,
regardless of whether sending a document or not.
Two new exceptions have also been added.
- UnsupportedMediaTypeError is raised when the Content-Type header is invalid.
- NotAcceptableError is raised when the Accept header is invalid.
Usage:
- Run the service and pass it the Accept header and Content-Type header.
- It returns
true
or raises exception if not JSON API-compliant.
Grape::Jsonapi::Services::ContentNegotiator.run(accept_header, content_type)
This follows the JSON APi specifications for filtering.
- Accepts a hash with the key as search field:
{ foo: value }
- When value is scalar type, operation 'equals(==)' is implied:
{ foo: 'scalar', bar: 123 } => where(foo: 'scalar', bar: 123)
- if value is an array, 'IN' operation is applied:
{ foo: [1,2,3] } => where(:foo.in([1,2,3]))
- if value is a Hash, there must be one entry, of which the value_key is the operation, and the value must be scalar. Valid operations include
<
,<=
,>
,>=
,==
:
{ foo: {'<': 100 } } => where(foo < 100)
In your API, accept an optional filter
parameter with with a custom type. It should call the Filter module's allow
method and pass it your search term:
params do
optional :filter, type:
Grape::Jsonapi::Parameters::Filter.allow([:something_id])
end
Pass the params like this in your tests:
context 'with a something' do
let(:params) do
{ filter: JSON.unparse(something_id: [something.id.to_s]) }
end
end
##Copyright and License
MIT License, see LICENSE for details.