lib/restforce/middleware/json_request.rb



# frozen_string_literal: true

#
# Adapted from `lib/faraday/request/json.rb` in the `faraday`
# gem (<https://github.com/lostisland/faraday/blob/5366029282968d59980a182258f8c2b0212721c8/lib/faraday/request/json.rb>).
#
# We use this because we want to support Faraday 1.x and Faraday 2.x.
# Faraday 2.x has the JSON middlewares included, but Faraday 1.x doesn't,
# forcing you to use the `faraday_middleware` gem. This approach allows us
# to support both.
#
# Copyright (c) 2009-2022 Rick Olson, Zack Hobson
# Licensed under the MIT License.
#
require 'json'

module Restforce
  # Request middleware that encodes the body as JSON.
  #
  # Processes only requests with matching Content-type or those without a type.
  # If a request doesn't have a type but has a body, it sets the Content-type
  # to JSON MIME-type.
  #
  # Doesn't try to encode bodies that already are in string form.
  # rubocop:disable Style/ClassAndModuleChildren
  class Middleware::JsonRequest < Faraday::Middleware
    # rubocop:enable Style/ClassAndModuleChildren

    # This is the only line that differs substantively from the version in
    # Faraday. In Faraday, this refers to a Faraday constant.
    CONTENT_TYPE = 'Content-Type'

    MIME_TYPE = 'application/json'
    MIME_TYPE_REGEX = %r{^application/(vnd\..+\+)?json$}

    #
    # Taken from `lib/faraday/middleware.rb` in the `faraday`
    # gem (<https://github.com/lostisland/faraday/blob/08b7d4d/lib/faraday/middleware.rb>),
    # with a tiny adaptation to refer to the `@app` instance
    # variable rather than expecting an `attr_reader` to exist.
    #
    # In Faraday versions before v1.2.0, `#call`  is missing.
    #
    # Copyright (c) 2009-2022 Rick Olson, Zack Hobson
    # Licensed under the MIT License.
    #
    def call(env)
      on_request(env) if respond_to?(:on_request)
      @app.call(env).on_complete do |environment|
        on_complete(environment) if respond_to?(:on_complete)
      end
    end

    def on_request(env)
      match_content_type(env) do |data|
        env[:body] = encode(data)
      end
    end

    private

    def encode(data)
      ::JSON.generate(data)
    end

    def match_content_type(env)
      return unless process_request?(env)

      env[:request_headers][CONTENT_TYPE] ||= MIME_TYPE
      yield env[:body] unless env[:body].respond_to?(:to_str)
    end

    def process_request?(env)
      type = request_type(env)
      body?(env) && (type.empty? || type.match?(MIME_TYPE_REGEX))
    end

    def body?(env)
      (body = env[:body]) && !(body.respond_to?(:to_str) && body.empty?)
    end

    def request_type(env)
      type = env[:request_headers][CONTENT_TYPE].to_s
      type = type.split(';', 2).first if type.index(';')
      type
    end
  end
end

Faraday::Request.register_middleware(restforce_json: Restforce::Middleware::JsonRequest)