sanger/sequencescape

View on GitHub
app/api/core/service/content_filtering.rb

Summary

Maintainability
A
35 mins
Test Coverage
A
97%
# frozen_string_literal: true
# Everything coming in and going out should be JSON.
module Core::Service::ContentFiltering
  class InvalidRequestedContentType < ::Core::Service::Error
    self.api_error_code = 406
    self.api_error_message =
      "the 'Accept' header can only be 'application/json' or a supported filetype eg.'sequencescape/qc_file'"
  end

  class InvalidRequestedContentTypeOnFile < ::Core::Service::Error
    self.api_error_code = 406
    self.api_error_message = "the 'Accept' header can only be 'application/json' when submitting a file"
  end

  class InvalidBodyContentType < ::Core::Service::Error
    self.api_error_code = 415
    self.api_error_message =
      "the 'Content-Type' can only be 'application/json' or a supported filetype eg.'sequencescape/qc_file'"
  end

  module Helpers
    def json
      @json
    end

    def process_request_body # rubocop:todo Metrics/AbcSize
      content = request.body.read
      if content.present? && acceptable_types.exclude?(request.content_type)
        raise Core::Service::ContentFiltering::InvalidBodyContentType
      end

      if request.content_type == 'application/json' || content.blank?
        @json = content.blank? ? {} : MultiJson.load(content)
      end
    ensure
      # It's important to ensure that the body IO object has been rewound to the start for other requests.
      request.body.rewind
    end

    def process_response_body
      headers('Content-Type' => request_accepted.first)
    end

    ACCEPTABLE_TYPES = Rails.env.development? ? %w[application/json */*].freeze : ['application/json'].freeze

    def acceptable_types
      ACCEPTABLE_TYPES + ::Api::EndpointHandler.registered_mimetypes
    end

    def check_acceptable_content_type_requested!
      accepts_json_or_star = request.acceptable_media_types.prioritize(*acceptable_types).present?
      raise Core::Service::ContentFiltering::InvalidRequestedContentType unless accepts_json_or_star
    end

    def request_accepted
      request.acceptable_media_types.prioritize(*acceptable_types).map(&:to_s)
    end
  end

  def self.registered(app)
    app.helpers Helpers

    app.before_all_actions do
      check_acceptable_content_type_requested!
      process_request_body
    end

    app.after_all_actions { process_response_body }
  end
end