Class: Reynard::Specification

Inherits:
Object
  • Object
show all
Defined in:
lib/reynard/specification.rb,
lib/reynard/specification/query.rb,
lib/reynard/specification/finder.rb

Overview

Wraps the YAML representation of an OpenAPI specification.

Defined Under Namespace

Classes: Finder, Query

Constant Summary collapse

VERBS =
%w[get put post delete options head patch trace].freeze

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(filename:) ⇒ Specification

Returns a new instance of Specification.



13
14
15
16
# File 'lib/reynard/specification.rb', line 13

def initialize(filename:)
  @filename = filename
  @data = read
end

Class Method Details

.media_type_matches?(media_type, expression) ⇒ Boolean

Returns:

  • (Boolean)


100
101
102
103
104
105
# File 'lib/reynard/specification.rb', line 100

def self.media_type_matches?(media_type, expression)
  return true unless media_type
  return true if expression == media_type

  false
end

Instance Method Details

#build_body(operation_node, data) ⇒ Object

Returns a serialized body instance to serialize a request body and figure out the request headers.



62
63
64
# File 'lib/reynard/specification.rb', line 62

def build_body(operation_node, data)
  SerializedBody.new(dig(*operation_node, 'requestBody', 'content'), data)
end

#build_grouped_params(operation_node, params) ⇒ Object

The specification tells us where a parameter should be included, they can be placed in path, query, header, or cookie. In order to get them in the correct place, we group them by their location.

build_grouped_params(operation_node, { 'q' => 'face' }) #=>
  { 'query' => { 'q' => 'face' } }


46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/reynard/specification.rb', line 46

def build_grouped_params(operation_node, params)
  return {} unless params

  GroupedParameters.new(
    [
      # Parameters can be shared between methods on a path or be specific to an operation. The
      # parameters on the operation level override those at the path level.
      dig(*operation_node, 'parameters'),
      dig(*operation_node[..-2], 'parameters')
    ].compact.flatten,
    params
  ).to_h
end

#default_base_urlObject



36
37
38
# File 'lib/reynard/specification.rb', line 36

def default_base_url
  servers.first&.url
end

#dig(*path) ⇒ Object

Digs a value out of the specification, taking $ref into account.



19
20
21
# File 'lib/reynard/specification.rb', line 19

def dig(*path)
  dig_into(@data, @data, path.dup, File.dirname(@filename))
end

#find_each(type:, &block) ⇒ Object

Yields all nodes in the specification matching the specified type.

specification.find_each(type: 'object') {}

Please don’t use this in a hot paths in production, primarily meant for testing and tooling.



28
29
30
# File 'lib/reynard/specification.rb', line 28

def find_each(type:, &block)
  Finder.new(specification: self, query: Query.new(type:)).find_each(&block)
end

#media_type(operation_node, response_code, media_type) ⇒ Object



75
76
77
78
79
80
81
82
# File 'lib/reynard/specification.rb', line 75

def media_type(operation_node, response_code, media_type)
  responses = dig(*operation_node, 'responses')
  response_code = 'default' unless responses.key?(response_code)
  response, media_type = media_type_response(responses, response_code, media_type)
  return unless response

  MediaType.new(node: [*operation_node, 'responses', response_code, 'content', media_type])
end

#media_type_response(responses, response_code, media_type) ⇒ Object



84
85
86
87
88
89
90
91
92
# File 'lib/reynard/specification.rb', line 84

def media_type_response(responses, response_code, media_type)
  defined_responses = responses.dig(response_code, 'content')
  return unless defined_responses&.any?

  defined_responses.each do |expression, response|
    return response, expression if self.class.media_type_matches?(media_type, expression)
  end
  nil
end

#operation(operation_name) ⇒ Object



66
67
68
69
70
71
72
73
# File 'lib/reynard/specification.rb', line 66

def operation(operation_name)
  dig('paths').each do |path, operations|
    operations.slice(*VERBS).each do |verb, operation|
      return Operation.new(node: ['paths', path, verb]) if operation_name == operation['operationId']
    end
  end
  nil
end

#schema(media_type_node) ⇒ Object



94
95
96
97
98
# File 'lib/reynard/specification.rb', line 94

def schema(media_type_node)
  return unless dig(*media_type_node, 'schema')

  Schema.new(specification: self, node: [*media_type_node, 'schema'])
end

#serversObject



32
33
34
# File 'lib/reynard/specification.rb', line 32

def servers
  dig('servers').map { |attributes| Server.new(attributes) }
end