CardTapp/yext-api

View on GitHub
lib/yext/api/utils/middleware/default_parameters.rb

Summary

Maintainability
A
0 mins
Test Coverage
# frozen_string_literal: true

module Yext
  module Api
    module Utils
      module Middleware
        # Faraday Middleware for adding Configuration default values to API calls as needed.
        #
        # Most options can be set via scopes and the soped values will be kept if they are used.
        # i.e. the existing query string values will be kept if they exist.
        #
        # Because Spyke puts `where` options into the body OR the query depending on the type of
        # request being made, we have to move the Yext query options out of the body into the query
        # if there is a body.
        #
        # The following configuration options are defaulted:
        #   validation_level: Added to query parameters if not already there and is not nil
        #   api_key:          Added to query parameters if not already there
        #   api_version:      Added to qeury parameters if not already there
        #   account_id:       Inserted into the path if the path includes the Account default URI and no
        #                     account_id was specified.
        #
        # The following values will be moved out of the body into the query string if they are found:
        #   account_id      * Not used if in body.  If set it will already be in the path.
        #   v
        #   api_key
        #   validation
        #   yext_username
        #   yext_user_id
        class DefaultParameters < Faraday::Response::Middleware
          extend Memoist

          attr_reader :configuration,
                      :url,
                      :params,
                      :body_json,
                      :request_env

          def call(call_env)
            get_attribute_values(call_env)

            body_json.delete(:account_id)
            add_username_headers
            add_default_query_params
            save_query_params
            save_body

            @app.call(request_env)
          end

          private

          def get_attribute_values(call_env)
            @request_env   = call_env
            @configuration = Yext::Api.configuration
            @url           = request_env[:url]
            @params        = Rack::Utils.parse_nested_query(url.query).with_indifferent_access
            @body_json     = parse_body
          end

          def parse_body
            if request_env.body.present?
              JSON.parse(request_env.body, symbolize_names: true)
            else
              {}
            end
          end

          def add_default_query_params
            params.reverse_merge!(api_key: api_key, v: version)

            params[:validation] ||= validation if validation.present?
          end

          def validation
            return body_json.delete(:validation) if body_json.key?(:validation)

            configuration.validation_level if configuration.validation_level.present?
          end

          def version
            body_json.delete(:v) || configuration.api_version || "20161012"
          end

          def api_key
            body_json.delete(:api_key) || configuration.api_key
          end

          def save_query_params
            url.query = params.to_query
          end

          def save_body
            extract_rooted_body

            return if request_env.body.blank?

            body_json[:id] = body_json.delete(:create_id) if body_json.key?(:create_id)

            request_env.body = body_json.to_json
          end

          def extract_rooted_body
            return unless body_json.keys.length == 1

            sub_json   = body_json[body_json.keys.first]
            @body_json = sub_json if sub_json.is_a?(Hash)
          end

          def add_username_headers
            user_id, username = extract_header_values

            return unless %w[put post patch delete].include?(request_env[:method].to_s)

            request_headers                  = request_env[:request_headers]
            request_headers["Yext-Username"] = username if username.present?
            request_headers["Yext-User-Id"]  = user_id if user_id.present?
          end

          def extract_header_values
            username = extract_defaulted_value(:yext_username, configuration.yext_username)
            user_id  = extract_defaulted_value(:yext_user_id, configuration.yext_user_id)

            [user_id, username]
          end

          def extract_defaulted_value(param_name, default_value)
            params.delete(param_name) || body_json.delete(param_name) || default_value
          end

          memoize :validation,
                  :version,
                  :api_key
        end
      end
    end
  end
end