forked from ahx/openapi_first
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathopenapi_first.rb
More file actions
85 lines (70 loc) · 3.25 KB
/
openapi_first.rb
File metadata and controls
85 lines (70 loc) · 3.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# frozen_string_literal: true
require_relative 'openapi_first/json'
require_relative 'openapi_first/file_loader'
require_relative 'openapi_first/errors'
require_relative 'openapi_first/registry'
require_relative 'openapi_first/configuration'
require_relative 'openapi_first/child_configuration'
require_relative 'openapi_first/definition'
require_relative 'openapi_first/version'
require_relative 'openapi_first/middlewares/response_validation'
require_relative 'openapi_first/middlewares/request_validation'
# OpenapiFirst is a toolchain to build HTTP APIS based on OpenAPI API descriptions.
module OpenapiFirst
extend Registry
autoload :Test, 'openapi_first/test'
# Key in rack to find instance of Request
REQUEST = 'openapi.request'
FAILURE = :openapi_first_validation_failure
# @return [Configuration]
def self.configuration
@configuration ||= Configuration.new
end
# @return [Configuration]
# @yield [Configuration]
def self.configure
yield configuration if block_given?
end
ERROR_RESPONSES = {} # rubocop:disable Style/MutableConstant
private_constant :ERROR_RESPONSES
# Register an error response class
# @param name [Symbol]
# @param klass [Class] A class that includes / implements OpenapiFirst::ErrorResponse
def self.register_error_response(name, klass)
ERROR_RESPONSES[name.to_sym] = klass
end
# @param name [Symbol]
# @return [Class] The error response class
def self.find_error_response(name)
ERROR_RESPONSES.fetch(name) do
raise "Unknown error response: #{name}. " \
'Register your error response class via `OpenapiFirst.register_error_response(name, klass)`. ' \
"Registered error responses are: #{ERROR_RESPONSES.keys.join(', ')}."
end
end
# Load and dereference an OpenAPI spec file or return the Definition if it's already loaded
# @param filepath_or_definition [String, Definition] The path to the file or a Definition object
# @param only [Proc, nil] An optional proc to filter paths. It is called with the path string and should return
# true/false
# @param path_prefix [String, nil] An optional path prefix, that is not documented, that all requests begin with.
# @return [Definition]
def self.load(filepath_or_definition, only: nil, path_prefix: nil, &)
return filepath_or_definition if filepath_or_definition.is_a?(Definition)
return self[filepath_or_definition] if filepath_or_definition.is_a?(Symbol)
filepath = filepath_or_definition
raise FileNotFoundError, "File not found: #{filepath}" unless File.exist?(filepath)
contents = FileLoader.load(filepath)
parse(contents, only:, filepath:, path_prefix:, &)
end
# Parse a dereferenced Hash
# @return [Definition]
# TODO: This needs to work with unresolved contents as well
def self.parse(contents, only: nil, filepath: nil, path_prefix: nil, &)
contents = ::JSON.parse(::JSON.generate(contents)) # Deeply stringify keys, because of YAML. See https://github.com/ahx/openapi_first/issues/367
contents['paths'].filter!(&->(key, _) { only.call(key) }) if only
Definition.new(contents, filepath, path_prefix, &)
end
end
require_relative 'openapi_first/error_response'
require_relative 'openapi_first/error_responses/default'
require_relative 'openapi_first/error_responses/jsonapi'