<commit_before>require 'active_support/core_ext/hash/except'
require 'active_support/core_ext/hash/compact'
require 'active_support/inflector'
require 'brainstem/api_docs/formatters/abstract_formatter'
require 'brainstem/api_docs/formatters/open_api_specification/helper'
require 'brainstem/api_docs/formatters/markdown/helper'

#
# Responsible for formatting each endpoint.
#
module Brainstem
  module ApiDocs
    module Formatters
      module OpenApiSpecification
        module Version2
          module Endpoint
            class ParamDefinitionsFormatter < AbstractFormatter
              include Helper
              include ::Brainstem::ApiDocs::Formatters::Markdown::Helper

              attr_reader :output

              def initialize(endpoint)
                @endpoint    = endpoint
                @http_method = format_http_method(endpoint)
                @presenter   = endpoint.presenter
                @output      = []
              end

              def call
                format_path_params!

                if endpoint.action == 'index'
                  format_pagination_params!
                  format_search_param!
                  format_only_param!
                  format_sort_order_params!
                  format_filter_params!
                end

                if http_method != 'delete'
                  format_optional_params!
                  format_include_params!
                end

                format_query_params!
                format_body_params!

                output
              end

              ################################################################################
              private
              ################################################################################

              attr_reader :endpoint, :presenter, :http_method

              def format_path_params!
                path_params.each do |param|
                  model_name = param.match(/_id/) ? param.split('_id').first : 'model'

                  output << {
                    'in'          => 'path',
                    'name'        => param,
                    'required'    => true,
                    'type'        => 'integer',
                    'description' => "The ID of the #{model_name.humanize}."
                  }
                end
              end

              def path_params
                endpoint.path
                  .gsub('(.:format)', '')
                  .scan(/(:(?<param>\w+))/)
                  .flatten
              end

              def nested_properties(param_config)
                param_config.except(:_config)
              end

              def format_pagination_params!
                output << format_query_param(:page, type: 'integer', default: 1)
                output << format_query_param(:per_page, type: 'integer', default: 20, maximum: 200)
              end

              def format_search_param!
                return if presenter.nil? || !presenter.searchable?

                output << format_query_param(:search, type: 'string')
              end

              def format_only_param!
                output << format_query_param(:only,
                  type: 'array',
                  item_type: 'integer',
                  info: 'Allows you to request one or more resources directly by IDs in a comma separated list'
                )
              end

              def format_sort_order_params!
                return if presenter.nil? || (valid_sort_orders = presenter.valid_sort_orders).empty?

                sort_orders = valid_sort_orders.map { |sort_name, _|
                  [md_inline_code("#{sort_name}:asc"), md_inline_code("#{sort_name}:desc")]
                }.flatten.sort

                description = <<~DESC
                  Supply `order` with the name of a valid sort field for the endpoint and a direction.

                  Valid values: #{sort_orders.to_sentence}.
                DESC

                output << format_query_param('order',
                  info:    description,
                  type:    'string',
                  default: presenter.default_sort_order
                )
              end

              def format_optional_params!
                return if presenter.nil? || (optional_field_names = presenter.optional_field_names).empty?

                output << format_query_param('optional_fields',
                  info:      'Allows you to request one or more optional fields as an array',
                  type:      'array',
                  item_type: 'string',
                  items:     optional_field_names
                )
              end

              def format_include_params!<commit_msg>Add strip_heredoc instead of unsupported heredoc syntax<commit_after>require 'active_support/core_ext/hash/except'
require 'active_support/core_ext/hash/compact'
require 'active_support/inflector'
require 'brainstem/api_docs/formatters/abstract_formatter'
require 'brainstem/api_docs/formatters/open_api_specification/helper'
require 'brainstem/api_docs/formatters/markdown/helper'

#
# Responsible for formatting each endpoint.
#
module Brainstem
  module ApiDocs
    module Formatters
      module OpenApiSpecification
        module Version2
          module Endpoint
            class ParamDefinitionsFormatter < AbstractFormatter
              include Helper
              include ::Brainstem::ApiDocs::Formatters::Markdown::Helper

              attr_reader :output

              def initialize(endpoint)
                @endpoint    = endpoint
                @http_method = format_http_method(endpoint)
                @presenter   = endpoint.presenter
                @output      = []
              end

              def call
                format_path_params!

                if endpoint.action == 'index'
                  format_pagination_params!
                  format_search_param!
                  format_only_param!
                  format_sort_order_params!
                  format_filter_params!
                end

                if http_method != 'delete'
                  format_optional_params!
                  format_include_params!
                end

                format_query_params!
                format_body_params!

                output
              end

              ################################################################################
              private
              ################################################################################

              attr_reader :endpoint, :presenter, :http_method

              def format_path_params!
                path_params.each do |param|
                  model_name = param.match(/_id/) ? param.split('_id').first : 'model'

                  output << {
                    'in'          => 'path',
                    'name'        => param,
                    'required'    => true,
                    'type'        => 'integer',
                    'description' => "The ID of the #{model_name.humanize}."
                  }
                end
              end

              def path_params
                endpoint.path
                  .gsub('(.:format)', '')
                  .scan(/(:(?<param>\w+))/)
                  .flatten
              end

              def nested_properties(param_config)
                param_config.except(:_config)
              end

              def format_pagination_params!
                output << format_query_param(:page, type: 'integer', default: 1)
                output << format_query_param(:per_page, type: 'integer', default: 20, maximum: 200)
              end

              def format_search_param!
                return if presenter.nil? || !presenter.searchable?

                output << format_query_param(:search, type: 'string')
              end

              def format_only_param!
                output << format_query_param(:only,
                  type: 'array',
                  item_type: 'integer',
                  info: 'Allows you to request one or more resources directly by IDs in a comma separated list'
                )
              end

              def format_sort_order_params!
                return if presenter.nil? || (valid_sort_orders = presenter.valid_sort_orders).empty?

                sort_orders = valid_sort_orders.map { |sort_name, _|
                  [md_inline_code("#{sort_name}:asc"), md_inline_code("#{sort_name}:desc")]
                }.flatten.sort

                description = <<-DESC.strip_heredoc
                  Supply `order` with the name of a valid sort field for the endpoint and a direction.

                  Valid values: #{sort_orders.to_sentence}.
                DESC

                output << format_query_param('order',
                  info:    description,
                  type:    'string',
                  default: presenter.default_sort_order
                )
              end

              def format_optional_params!
                return if presenter.nil? || (optional_field_names = presenter.optional_field_names).empty?

                output << format_query_param('optional_fields',
                  info:      'Allows you to request one or more optional fields as an array',
                  type:      'array',
                  item_type: 'string',
                  items:     optional_field_names
                )
              end

              def format_include_params!
{'fraction_non_alphanumeric': 0.105712478920742, 'fraction_numerical': 0.001475548060708263, 'mean_word_length': 1.4500387296669248, 'pattern_counts': {'":': 0, '<': 39, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5634088', 'n_tokens_mistral': 2364, 'n_tokens_neox': 2121, 'n_words': 487}