<commit_before>require 'json-schema'
require 'interpol/errors'

module JSON
  # The JSON-schema namespace
  class Schema
    # Monkey patch json-schema to reject unrecognized types.
    # It allows them because the spec says they should be allowed,
    # but we don't want to allow them.
    # For more info, see:
    # - https://github.com/hoxworth/json-schema/pull/37
    # - https://github.com/hoxworth/json-schema/pull/38
    class TypeAttribute
      (class << self; self; end).class_eval do
        alias original_data_valid_for_type? data_valid_for_type?
        def data_valid_for_type?(data, type)
          return false unless TYPE_CLASS_MAPPINGS.has_key?(type)
          original_data_valid_for_type?(data, type)
        end
      end
    end
  end
end

module Interpol
  module HashFetcher
    # Unfortunately, on JRuby 1.9, the error raised from Hash#fetch when
    # the key is not found does not include the key itself :(. So we work
    # around it here.
    def fetch_from(hash, key)
      hash.fetch(key) do
        raise ArgumentError.new("key not found: #{key.inspect}")
      end
    end
  end

  # Represents an endpoint. Instances of this class are constructed
  # based on the endpoint definitions in the YAML files.
  class Endpoint
    include HashFetcher
    attr_reader :name, :route, :method

    def initialize(endpoint_hash)
      @name        = fetch_from(endpoint_hash, 'name')
      @route       = fetch_from(endpoint_hash, 'route')
      @method      = fetch_from(endpoint_hash, 'method').downcase.to_sym
      @definitions = extract_definitions_from(endpoint_hash)
      validate_name!
    end

    def find_definition!(version, message_type)
      @definitions.fetch([message_type, version]) do
        message = "No definition found for #{name} endpoint for version #{version}"
        message << " and message_type #{message_type}"
        raise ArgumentError.new(message)
      end
    end

    def find_example_for!(version, message_type)
      find_definition!(version, message_type).first.examples.first
    end

    def find_example_status_code_for!(version)
      find_definition!(version, 'response').first.example_status_code
    end

    def available_versions
      definitions.map { |d| d.first.version }
    end

    def definitions
      # sort all requests before all responses
      # sort higher version numbers before lower version numbers
      @definitions.values.sort do |x,y|
        if x.first.message_type == y.first.message_type
          y.first.version <=> x.first.version
        else
          x.first.message_type <=> y.first.message_type
        end
      end
    end

    def route_matches?(path)
      path =~ route_regex
    end

  private

    def route_regex
      @route_regex ||= begin
        regex_string = route.split('/').map do |path_part|
          if path_part.start_with?(':')
            '[^\/]+' # it's a parameter; match anything
          else
            Regexp.escape(path_part)
          end
        end.join('\/')

        /\A#{regex_string}\z/
      end
    end

    DEFAULT_MESSAGE_TYPE = 'response'

    def extract_definitions_from(endpoint_hash)
      definitions = Hash.new { |h, k| h[k] = [] }

      fetch_from(endpoint_hash, 'definitions').each do |definition|
        fetch_from(definition, 'versions').each do |version|
          message_type = definition.fetch('message_type', DEFAULT_MESSAGE_TYPE)
          key = [message_type, version]
          definitions[key] << EndpointDefinition.new(name, version, message_type, definition)
        end
      end

      definitions
    end

    def validate_name!
      unless name =~ /\A[\w\-]+\z/
        raise ArgumentError, "Invalid endpoint name (#{name.inspect}). "+
                             "Only letters, numbers, underscores and dashes are allowed."
      end
    end
  end

  # Wraps a single versioned definition for an endpoint.
  # Provides the means to validate data against that version of the schema.
  class EndpointDefinition
    include HashFetcher
    attr_reader :endpoint_name, :message_type, :version, :schema, :path_params, :query_params, :examples

    def initialize(endpoint_name, version, message_type, definition)
      @endpoint_name  = endpoint_name
      @message_type   = message_type
      @status_codes   = StatusCodeMatcher.new(definition['status_codes'])
      @version        = version<commit_msg>Fixed line length cane check (over 100 char)<commit_after>require 'json-schema'
require 'interpol/errors'

module JSON
  # The JSON-schema namespace
  class Schema
    # Monkey patch json-schema to reject unrecognized types.
    # It allows them because the spec says they should be allowed,
    # but we don't want to allow them.
    # For more info, see:
    # - https://github.com/hoxworth/json-schema/pull/37
    # - https://github.com/hoxworth/json-schema/pull/38
    class TypeAttribute
      (class << self; self; end).class_eval do
        alias original_data_valid_for_type? data_valid_for_type?
        def data_valid_for_type?(data, type)
          return false unless TYPE_CLASS_MAPPINGS.has_key?(type)
          original_data_valid_for_type?(data, type)
        end
      end
    end
  end
end

module Interpol
  module HashFetcher
    # Unfortunately, on JRuby 1.9, the error raised from Hash#fetch when
    # the key is not found does not include the key itself :(. So we work
    # around it here.
    def fetch_from(hash, key)
      hash.fetch(key) do
        raise ArgumentError.new("key not found: #{key.inspect}")
      end
    end
  end

  # Represents an endpoint. Instances of this class are constructed
  # based on the endpoint definitions in the YAML files.
  class Endpoint
    include HashFetcher
    attr_reader :name, :route, :method

    def initialize(endpoint_hash)
      @name        = fetch_from(endpoint_hash, 'name')
      @route       = fetch_from(endpoint_hash, 'route')
      @method      = fetch_from(endpoint_hash, 'method').downcase.to_sym
      @definitions = extract_definitions_from(endpoint_hash)
      validate_name!
    end

    def find_definition!(version, message_type)
      @definitions.fetch([message_type, version]) do
        message = "No definition found for #{name} endpoint for version #{version}"
        message << " and message_type #{message_type}"
        raise ArgumentError.new(message)
      end
    end

    def find_example_for!(version, message_type)
      find_definition!(version, message_type).first.examples.first
    end

    def find_example_status_code_for!(version)
      find_definition!(version, 'response').first.example_status_code
    end

    def available_versions
      definitions.map { |d| d.first.version }
    end

    def definitions
      # sort all requests before all responses
      # sort higher version numbers before lower version numbers
      @definitions.values.sort do |x,y|
        if x.first.message_type == y.first.message_type
          y.first.version <=> x.first.version
        else
          x.first.message_type <=> y.first.message_type
        end
      end
    end

    def route_matches?(path)
      path =~ route_regex
    end

  private

    def route_regex
      @route_regex ||= begin
        regex_string = route.split('/').map do |path_part|
          if path_part.start_with?(':')
            '[^\/]+' # it's a parameter; match anything
          else
            Regexp.escape(path_part)
          end
        end.join('\/')

        /\A#{regex_string}\z/
      end
    end

    DEFAULT_MESSAGE_TYPE = 'response'

    def extract_definitions_from(endpoint_hash)
      definitions = Hash.new { |h, k| h[k] = [] }

      fetch_from(endpoint_hash, 'definitions').each do |definition|
        fetch_from(definition, 'versions').each do |version|
          message_type = definition.fetch('message_type', DEFAULT_MESSAGE_TYPE)
          key = [message_type, version]
          definitions[key] << EndpointDefinition.new(name, version, message_type, definition)
        end
      end

      definitions
    end

    def validate_name!
      unless name =~ /\A[\w\-]+\z/
        raise ArgumentError, "Invalid endpoint name (#{name.inspect}). "+
                             "Only letters, numbers, underscores and dashes are allowed."
      end
    end
  end

  # Wraps a single versioned definition for an endpoint.
  # Provides the means to validate data against that version of the schema.
  class EndpointDefinition
    include HashFetcher
    attr_reader :endpoint_name, :message_type, :version, :schema,
                :path_params, :query_params, :examples

    def initialize(endpoint_name, version, message_type, definition)
      @endpoint_name  = endpoint_name
      @message_type   = message_type
      @status_codes   = StatusCodeMatcher.new(definition['status_codes'])
      @version        = version
{'fraction_non_alphanumeric': 0.08914596801633208, 'fraction_numerical': 0.001701258931609391, 'mean_word_length': 2.6650041562759768, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 7, 'https://': 4, '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': '4789522', 'n_tokens_mistral': 2563, 'n_tokens_neox': 2516, 'n_words': 704}