class OpenAPIParser::PathItemFinder

Public Class Methods

new(paths) click to toggle source

@param [OpenAPIParser::Schemas::Paths] paths

# File lib/openapi_parser/path_item_finder.rb, line 3
def initialize(paths)
  @paths = paths
end

Public Instance Methods

operation_object(http_method, request_path) click to toggle source

find operation object and if not exist return nil @param [String, Symbol] http_method like (get, post .… allow symbol) @param [String] request_path @return [Result, nil]

# File lib/openapi_parser/path_item_finder.rb, line 11
def operation_object(http_method, request_path)
  if (path_item_object = @paths.path[request_path])
    if (op = path_item_object.operation(http_method))
      return Result.new(path_item_object, op, request_path, {}) # find no path_params path
    end
  end

  # check with path_params
  parse_request_path(http_method, request_path)
end
parse_path_parameters(schema_path, request_path) click to toggle source
# File lib/openapi_parser/path_item_finder.rb, line 41
def parse_path_parameters(schema_path, request_path)
  parameters = path_parameters(schema_path)
  return nil if parameters.empty?

  # If there are regex special characters in the path, the regex will
  # be too permissive, so escape the non-parameter parts.
  components = []
  unprocessed = schema_path.dup
  parameters.each do |parameter|
    parts = unprocessed.partition(parameter)
    components << Regexp.escape(parts[0]) unless parts[0] == ''
    components << "(?<#{param_name(parameter)}>.+)"
    unprocessed = parts[2]
  end
  components << Regexp.escape(unprocessed) unless unprocessed == ''

  regex = components.join('')
  matches = request_path.match(regex)
  return nil unless matches

  # Match up the captured names with the captured values as a hash
  matches.names.zip(matches.captures).to_h
end

Private Instance Methods

different_depth_or_method?(splitted_schema_path, splitted_request_path, path_item, http_method) click to toggle source

used to filter paths with different depth or without given http method

# File lib/openapi_parser/path_item_finder.rb, line 82
def different_depth_or_method?(splitted_schema_path, splitted_request_path, path_item, http_method)
  splitted_schema_path.size != splitted_request_path.size || !path_item.operation(http_method)
end
extract_params(splitted_request_path, splitted_schema_path) click to toggle source

extract params by comparing the request path and the path from schema EXAMPLE: extract_params(['org', '1', 'user', '2', 'edit'], ['org', '{org_id}', 'user', '{user_id}'])

> { 'org_id' => 1, 'user_id' => 2 }

return nil if the schema does not match

# File lib/openapi_parser/path_item_finder.rb, line 103
def extract_params(splitted_request_path, splitted_schema_path)
  splitted_request_path.zip(splitted_schema_path).reduce({}) do |result, zip_item|
    request_path_item, schema_path_item = zip_item

    params = parse_path_parameters(schema_path_item, request_path_item)
    if params
      result.merge!(params)
    else
      return if schema_path_item != request_path_item
    end

    result
  end
end
find_path_and_params(http_method, request_path) click to toggle source

find matching path and extract params EXAMPLE: find_path_and_params('get', '/user/1') => ['/user/{id}', { 'id' => 1 }]

# File lib/openapi_parser/path_item_finder.rb, line 141
def find_path_and_params(http_method, request_path)
  return [request_path, {}] if matches_directly?(request_path, http_method)

  matching = matching_paths_with_params(request_path, http_method)

  # if there are many matching paths, return the one with the smallest number of params
  # (prefer /user/{id}/action over /user/{param_1}/{param_2} )
  matching.min_by { |match| match[1].size }
end
matches_directly?(request_path, http_method) click to toggle source

check if there is a identical path in the schema (without any param)

# File lib/openapi_parser/path_item_finder.rb, line 77
def matches_directly?(request_path, http_method)
  @paths.path[request_path]&.operation(http_method)
end
matching_paths_with_params(request_path, http_method) click to toggle source

find all matching paths with parameters extracted EXAMPLE: [

  ['/user/{id}/edit', { 'id' => 1 }],
  ['/user/{id}/{action}', { 'id' => 1, 'action' => 'edit' }],
]
# File lib/openapi_parser/path_item_finder.rb, line 124
def matching_paths_with_params(request_path, http_method)
  splitted_request_path = request_path.split('/')

  @paths.path.reduce([]) do |result, item|
    path, path_item = item
    splitted_schema_path = path.split('/')

    next result if different_depth_or_method?(splitted_schema_path, splitted_request_path, path_item, http_method)

    extracted_params = extract_params(splitted_request_path, splitted_schema_path)
    result << [path, extracted_params] if extracted_params
    result
  end
end
param_name(schema_path_item) click to toggle source

get the parameter name from the schema path item EXAMPLE: param_name('{id}') => 'id'

# File lib/openapi_parser/path_item_finder.rb, line 94
def param_name(schema_path_item)
  schema_path_item[1..(schema_path_item.length - 2)]
end
parse_request_path(http_method, request_path) click to toggle source
# File lib/openapi_parser/path_item_finder.rb, line 151
def parse_request_path(http_method, request_path)
  original_path, path_params = find_path_and_params(http_method, request_path)
  return nil unless original_path # # can't find

  path_item_object = @paths.path[original_path]
  obj = path_item_object.operation(http_method.to_s)
  return nil unless obj

  Result.new(path_item_object, obj, original_path, path_params)
end
path_parameters(schema_path) click to toggle source
# File lib/openapi_parser/path_item_finder.rb, line 66
def path_parameters(schema_path)
  # OAS3 follows a RFC6570 subset for URL templates
  # https://swagger.io/docs/specification/serialization/#uri-templates
  # A URL template param can be preceded optionally by a "." or ";", and can be succeeded optionally by a "*";
  # this regex returns a match of the full parameter name with all of these modifiers. Ex: {;id*}
  parameters = schema_path.scan(/(\{[\.;]*[^\{\*\}]+\**\})/)
  # The `String#scan` method returns an array of arrays; we want an array of strings
  parameters.collect { |param| param[0] }
end
path_template?(schema_path_item) click to toggle source

check if the path item is a template EXAMPLE: path_template?('{id}') => true

# File lib/openapi_parser/path_item_finder.rb, line 88
def path_template?(schema_path_item)
  schema_path_item.start_with?('{') && schema_path_item.end_with?('}')
end