class JsonApiServer::Include

Description:

Handles include parameters per JSON API spec jsonapi.org/format/#fetching-includes.

An endpoint may support an include request parameter to allow the client to customize which related resources should be returned.

ie., GET /articles/1?include=comments,comment.author,tags HTTP/1.1

This class (1) whitelists include params, (2) maintains an array of permitted inclusions, and (3) generates a sub-query if eagerloading is configured for inclusions.

Usage:

An inclusion request looks like:

/topics?include=author,comment.author,comments

It is converted to an array of relationships:

['author', 'comment.author', 'comments']

Includes are whitelisted with a configuration that looks like:

{
  {'author': -> { includes(:author) }},
  {'comments': -> { includes(:comments) }},
  'comment.author'
}

In this example, author, comments, and comment.author are allowed includes. If an unsupported include is requested, a JsonApiServer::BadRequest exception is raised which renders a 400 error.

A proc/lambda can be specified to eagerload relationships. Be careful, to date, there is no way to apply limits to :includes.

Example:

permitted = {
  {'author': -> { includes(:author) }},
  {'comments': -> { includes(:comments) }},
  'comment.author'
}

# create instance
include = JsonApiServer::Include.new(request, Topic, permitted)

# merge into master query
recent_topics = Topic.recent.merge(include.query)

# use in serializers
class CommentSerializer < JsonApiServer::ResourceSerializer
  def relationships
    if relationship?('comment.author') # relationship? is a helper methods in serializers.
      #...
    end
  end
end

Note:

JsonApiServer::Builder class provides an easier way to use this class.

Attributes

model[R]

ActiveRecord::Base model passed in constructor.

params[R]

Query parameters from request.

permitted[R]

Include configs passed in constructor.

request[R]

ActionDispatch::Request passed in constructor.

Public Class Methods

new(request, model, permitted = []) click to toggle source

Arguments:

  • request - ActionDispatch::Request

  • model (ActiveRecord::Base) - Model to append queries to.

  • permitted (Array) - Permitted inclusions. To eagerload the relationship, pass a proc:

Example:

Eagerloads author, comments, comments -> authors.

[
  {'author': -> { includes(:author) }},
  {'comments': -> { includes(:comments) }},
  {'comments.author': -> {includes(comments: :author) }},
  'publisher.addresses'
]
# File lib/json_api_server/include.rb, line 92
def initialize(request, model, permitted = [])
  @request = request
  @model = model
  @permitted = permitted.is_a?(Array) ? permitted : []
  @params = request.query_parameters
end

Public Instance Methods

include_params() click to toggle source

Array of include params from the request.

Examples
include=comments becomes ['comments']
include=comments.author,tags becomes ['comments.author', 'tags']
# File lib/json_api_server/include.rb, line 116
def include_params
  @include_params ||= begin
    params[:include].present? ? params[:include].split(',').map!(&:strip) : []
  end
end
includes() click to toggle source

Array of whitelisted include params. Raises JsonApiServer::BadRequest if any include_params is not whitelisted.

Examples

include=comments becomes ['comments']
include=comments.author,tags becomes ['comments.author', 'tags']
# File lib/json_api_server/include.rb, line 106
def includes
  include_params.select { |i| config_for(i).present? }
end
query()
Alias for: relation
relation() click to toggle source

Returns an ActiveRecord::Relation object (a query fragment). Returns nil if no eagerloading is configured.

# File lib/json_api_server/include.rb, line 124
def relation
  @relation ||= begin
    additions = false
    # TODO: merge! has unexpected results.
    frag = include_params.reduce(model.all) do |result, inclusion|
      config = config_for(inclusion)
      query = config.respond_to?(:keys) ? config.values.first : nil
      unless query.nil?
        additions = true
        result = result.merge(query)
      end
      result
    end
    additions ? frag : nil
  end
end
Also aliased as: query

Protected Instance Methods

config_for(inclusion) click to toggle source

Returns config. Raises JsonApiServer::BadRequest if inclusion is not whitelisted.

# File lib/json_api_server/include.rb, line 146
def config_for(inclusion)
  config = permitted.find do |v|
    inc = inclusion.to_s
    v.respond_to?(:keys) ? v.keys.first.to_s == inc : v.to_s == inc
  end
  if config.nil?
    msg = I18n.t('json_api_server.render_400.inclusion', param: inclusion)
    raise JsonApiServer::BadRequest, msg
  end
  config
end