class Superintendent::Request::Validator

Constants

DEFAULT_OPTIONS
FORM_METHOD_ACTIONS
JSON_API_CONTENT_TYPE

Public Class Methods

new(app, opts={}) click to toggle source
# File lib/superintendent/request/validator.rb, line 20
def initialize(app, opts={})
  @app, @options = app, DEFAULT_OPTIONS.merge(opts)
end

Public Instance Methods

call(env) click to toggle source
# File lib/superintendent/request/validator.rb, line 24
def call(env)
  request = ActionDispatch::Request.new(env)

  # Only manage requests for the selected Mime Types and
  # FORM_METHOD_ACTIONS.
  if @options[:monitored_content_types].include?(request.content_type) &&
    FORM_METHOD_ACTIONS.has_key?(request.request_method)

    request_data = request.request_parameters
    resource = requested_resource(request.path_info)

    begin
      forms = "#{resource}Form".constantize
      form = form_for_method(forms, request.request_method)
    rescue NameError => e
      return respond_404 # Return a 404 if no form was found.
    end

    errors = JSON::Validator.fully_validate(
      form, request_data, { errors_as_objects: true })

    if ! errors.empty?
      return respond_400(serialize_errors(request.headers[Id::X_REQUEST_ID], errors))
    end
    drop_extra_params!(form, request_data) unless request_data.blank?
  end

  @app.call(env)
end

Private Instance Methods

adjust_errors(form_errors) click to toggle source

Adjust the errors returned from the schema validator so they can be reused in the serialized error response.

# File lib/superintendent/request/validator.rb, line 70
def adjust_errors(form_errors)
  form_errors.each do |e|
    case e[:failed_attribute]
    when 'Required'
      e[:message].gsub!("The property '#/'", 'The request')
    end
    e[:message].gsub!("The property '#/", "The property '")
    e[:message] = e[:message][/.+?(?= in schema)/]
  end
end
drop_extra_params!(form, data) click to toggle source

Parameters that are not in the form are removed from the request so they never reach the controller.

# File lib/superintendent/request/validator.rb, line 58
def drop_extra_params!(form, data)
  form_data = form['properties']['data']['properties']
  allowed_params = form_data['attributes']['properties'].keys rescue nil
  allowed_params.nil? ? nil : data['data']['attributes'].slice!(*allowed_params)
end
form_for_method(forms, request_method) click to toggle source
# File lib/superintendent/request/validator.rb, line 64
def form_for_method(forms, request_method)
  forms.send(FORM_METHOD_ACTIONS[request_method]).with_indifferent_access
end
requested_resource(request_path) click to toggle source

Determine the requested resource based on the requested endpoint

# File lib/superintendent/request/validator.rb, line 98
def requested_resource(request_path)
  parts = request_path.split('/')
  # if the last part is an ID, return the part before it, the resource.
  if (parts[-1]=~/(\d+|[A-Z]{2}[a-zA-Z0-9]{32})/)
    resource = parts[-2]
  else
    resource = parts[-1]
  end
  resource.singularize.classify
end
respond_400(errors) click to toggle source
# File lib/superintendent/request/validator.rb, line 109
def respond_400(errors)
  [400, {'Content-Type' => JSON_API_CONTENT_TYPE}, [errors]]
end
respond_404() click to toggle source
# File lib/superintendent/request/validator.rb, line 113
def respond_404
  [404, {'Content-Type' => JSON_API_CONTENT_TYPE}, ['']]
end
serialize_errors(request_id, form_errors) click to toggle source
# File lib/superintendent/request/validator.rb, line 81
def serialize_errors(request_id, form_errors)
  form_errors = adjust_errors(form_errors)
  errors = []
  form_errors.each do |e|
    error = {
      id: request_id,
      status: 400,
      code: e[:failed_attribute].underscore.dasherize,
      title: e[:failed_attribute],
      detail: e[:message]
    }
    errors << { attributes: error, type: 'errors' }
  end
  JSON.pretty_generate({errors: errors})
end