Skip to content

Commit 48f3f0c

Browse files
authored
Merge pull request #409 from ahx/register-global
Register OADs globally
2 parents b1eb037 + 7647290 commit 48f3f0c

16 files changed

Lines changed: 102 additions & 61 deletions

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@
55
- Breaking: Trailing slashes are no longer ignored in dynamic paths. See [#403](https://github.com/ahx/openapi_first/issues/403).
66
- Remove superflous `Test::Coverage.current_run, .plans, .install, .uninstall`
77
- Update dependency `openapi_paramters` to >= 0.7.0, because that version fixes unpacking exploded deepObject paramters
8+
- Add support to register OADs globally via:
9+
```ruby
10+
OpenapiFirst.configure { |config| config.register('openapi.yaml') }
11+
```
12+
This removes the necessity to load the OAD in the same place where you use the middlewares and adds a cache for parsed OADs.
813
- Add `OpenapiFirst::Test::Configuration#ignore_unknown_response_status=`. When this is set to true, it won't complain about undefined response statuses it sees during a test run. Use with caution.
914
- Failure type `:response_not_found` was split into two more specific types `:response_content_type_not_found` and `:response_status_not_found`. This should be mostly internal stuff.
1015

README.md

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,24 @@ openapi_first is a Ruby gem for request / response validation and contract-testi
44

55
## Usage
66

7+
Configure
8+
```ruby
9+
OpenapiFirst.configure do |config|
10+
config.register('openapi/openapi.yaml')
11+
end
12+
```
13+
714
Use an OAD to validate incoming requests:
815
```ruby
9-
use OpenapiFirst::Middlewares::RequestValidation, 'openapi/openapi.yaml'
16+
use OpenapiFirst::Middlewares::RequestValidation
1017
```
1118

1219
Turn your request tests into [contract tests](#contract-testing) against an OAD:
1320
```ruby
1421
# spec_helper.rb
1522
require 'openapi_first'
16-
OpenapiFirst::Test.setup do |config|
17-
config.register('openapi/openapi.yaml')
23+
OpenapiFirst::Test.setup do |test|
24+
test.register('openapi/openapi.yaml')
1825
end
1926

2027
require 'my_app'

lib/openapi_first.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
require_relative 'openapi_first/json'
44
require_relative 'openapi_first/file_loader'
55
require_relative 'openapi_first/errors'
6+
require_relative 'openapi_first/registry'
67
require_relative 'openapi_first/configuration'
78
require_relative 'openapi_first/definition'
89
require_relative 'openapi_first/version'
@@ -11,6 +12,8 @@
1112

1213
# OpenapiFirst is a toolchain to build HTTP APIS based on OpenAPI API descriptions.
1314
module OpenapiFirst
15+
extend Registry
16+
1417
autoload :Test, 'openapi_first/test'
1518

1619
# Key in rack to find instance of Request
@@ -54,6 +57,7 @@ def self.find_error_response(name)
5457
# @return [Definition]
5558
def self.load(filepath_or_definition, only: nil, &)
5659
return filepath_or_definition if filepath_or_definition.is_a?(Definition)
60+
return self[filepath_or_definition] if filepath_or_definition.is_a?(Symbol)
5761

5862
filepath = filepath_or_definition
5963
raise FileNotFoundError, "File not found: #{filepath}" unless File.exist?(filepath)

lib/openapi_first/configuration.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ def initialize
1818
@path = nil
1919
end
2020

21+
def register(path_or_definition, as: :default)
22+
OpenapiFirst.register(path_or_definition, as:)
23+
end
24+
2125
attr_reader :request_validation_error_response, :hooks
2226
attr_accessor :request_validation_raise_error, :response_validation_raise_error, :path
2327

lib/openapi_first/definition.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ def key
5858
"#{title} @ #{version}"
5959
end
6060

61+
def inspect
62+
"#<#{self.class.name} @key=#{key}>"
63+
end
64+
6165
# Validates the request against the API description.
6266
# @param [Rack::Request] request The Rack request object.
6367
# @param [Boolean] raise_error Whether to raise an error if validation fails.

lib/openapi_first/middlewares/request_validation.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ def initialize(app, spec = nil, options = {})
2525
@raise = options.fetch(:raise_error, OpenapiFirst.configuration.request_validation_raise_error)
2626
@error_response_class = error_response_option(options[:error_response])
2727

28-
spec ||= options.fetch(:spec)
29-
raise "You have to pass spec: when initializing #{self.class}" unless spec
28+
spec ||= :default
29+
spec = OpenapiFirst[spec] if spec.is_a?(Symbol)
3030

3131
@definition = spec.is_a?(Definition) ? spec : OpenapiFirst.load(spec)
3232
end

lib/openapi_first/middlewares/response_validation.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ def initialize(app, spec = nil, options = {})
1919
end
2020
@raise = options.fetch(:raise_error, OpenapiFirst.configuration.response_validation_raise_error)
2121

22-
raise "You have to pass spec: when initializing #{self.class}" unless spec
22+
spec ||= :default
23+
spec = OpenapiFirst[spec] if spec.is_a?(Symbol)
2324

2425
@definition = spec.is_a?(Definition) ? spec : OpenapiFirst.load(spec)
2526
end

lib/openapi_first/registry.rb

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# frozen_string_literal: true
2+
3+
module OpenapiFirst
4+
class NotRegisteredError < Error; end
5+
class AlreadyRegisteredError < Error; end
6+
7+
# @visibility private
8+
module Registry
9+
def definitions
10+
@definitions ||= {}
11+
end
12+
13+
# Register an OpenAPI definition for testing
14+
# @param path_or_definition [String, Definition] Path to the OpenAPI file or a Definition object
15+
# @param as [Symbol] Name to register the API definition as
16+
def register(path_or_definition, as: :default)
17+
if definitions.key?(as) && as == :default
18+
raise(
19+
AlreadyRegisteredError,
20+
"#{definitions[as].filepath.inspect} is already registered " \
21+
"as ':default' so you cannot register #{path_or_definition.inspect} without " \
22+
'giving it a custom name. Please call register with a custom key like: ' \
23+
"#{name}.register(#{path_or_definition.inspect}, as: :my_other_api)"
24+
)
25+
end
26+
27+
definition = OpenapiFirst.load(path_or_definition)
28+
definitions[as] = definition
29+
definition
30+
end
31+
32+
def [](api)
33+
definitions.fetch(api) do
34+
option = api == :default ? '' : ", as: #{api.inspect}"
35+
raise(NotRegisteredError,
36+
"API description '#{api.inspect}' not found." \
37+
"Please call #{name}.register('myopenapi.yaml'#{option}) " \
38+
'once before running your app.')
39+
end
40+
end
41+
end
42+
end

lib/openapi_first/test.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# frozen_string_literal: true
22

33
require_relative 'test/configuration'
4-
require_relative 'test/registry'
4+
require_relative 'registry'
55

66
module OpenapiFirst
77
# Test integration

lib/openapi_first/test/registry.rb

Lines changed: 0 additions & 44 deletions
This file was deleted.

0 commit comments

Comments
 (0)