lib/json_api/controllers/concerns/controller_helpers/parsing.rb
# frozen_string_literal: true module JSONAPI module ControllerHelpers module Parsing extend ActiveSupport::Concern included do before_action :parse_jsonapi_body, if: -> { modifying_request? } end protected def parse_jsonapi_body return unless jsonapi_content_type? return if params[:data].present? parse_and_apply_json_body rescue JSON::ParserError # Invalid JSON - will be handled by validation end def modifying_request? request.post? || request.patch? || request.put? || request.delete? end def jsonapi_content_type? request.content_type&.include?("application/vnd.api+json") end def parse_and_apply_json_body body = request.body.read request.body.rewind return if body.blank? parsed = JSON.parse(body) parsed.deep_transform_keys!(&:to_sym) request.env["action_dispatch.request.request_parameters"] = parsed end def jsonapi_params data = params.require(:data) return data if data.is_a?(Array) permitted = data.permit(:type, :id, attributes: {}) permitted[:relationships] = permit_relationships(data) if data[:relationships].present? permitted end def permit_relationships(data) result = {} data[:relationships].each do |key, value| result[key] = value.permit(data: %i[type id]) if value.is_a?(ActionController::Parameters) end result end def jsonapi_attributes (jsonapi_params[:attributes] || {}).to_h end def jsonapi_relationships jsonapi_params[:relationships] || {} end def jsonapi_type data = jsonapi_params return data.first[:type] if data.is_a?(Array) data[:type] end def jsonapi_id data = jsonapi_params return data.first[:id].to_s.presence if data.is_a?(Array) data[:id].to_s.presence end def parse_include_param return [] unless params[:include] params[:include].to_s.split(",").map(&:strip) end def parse_fields_param return {} unless params[:fields] params[:fields].permit!.to_h.each_with_object({}) do |(type, fields), hash| hash[type.to_sym] = fields.to_s.split(",").map(&:strip) end end def parse_filter_param return {} unless params[:filter] raw_filters = params[:filter].permit!.to_h JSONAPI::ParamHelpers.flatten_filter_hash(raw_filters) end def parse_sort_param return [] unless params[:sort] params[:sort].to_s.split(",").map(&:strip) end def parse_page_param return {} unless params[:page] params[:page].permit(:number, :size).to_h end def invalid_sort_fields_for_columns(sorts, available_columns) sorts.filter_map do |sort_field| field = JSONAPI::RelationshipHelpers.extract_sort_field_name(sort_field) field unless available_columns.include?(field.to_s) end end def valid_sort_fields_for_resource(resource_class, model_class) model_columns = model_class.column_names.map(&:to_sym) resource_sortable_fields = resource_class.permitted_sortable_fields.map(&:to_sym) (model_columns + resource_sortable_fields).uniq.map(&:to_s) end end end end