openapi_first 3.3.0 → 3.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7d164451de439181b88fc45c5735f564cc802f5f53e13e245d224ca474a12c03
4
- data.tar.gz: c5adc0092bccf3de1248799ddde58bf77dce9c58858e588dd72e8c8ed4a5c6cd
3
+ metadata.gz: 1786c083ff520504c716e9bdd1e8b92754be71e70dd774644b6a0c655681369a
4
+ data.tar.gz: bc86929efbbe041515cfb12e6d453133cb4cf0a849c40ae5c526eda1b910a4cd
5
5
  SHA512:
6
- metadata.gz: ca4c5185152f49fe83977856d585f31ab1490f6b813a3f9878fc42e266f84dddc445baaf6865868e4a2b9c24f9ab7dccc2732ccc74f15aea43363a2f2b519e80
7
- data.tar.gz: 43e0abb51e9d78fe86d0fb0903e372dbdfe480e73906148dd9a1da15261feb41d3614c3a6d337e11796baceff375c7ea887cbb8e7bbbb7098e6f00ec3825c5c8
6
+ metadata.gz: 4b745143e975173192680152d5b42243c4eb58e8158b4f71aa67730780ad9ec49e788767f8bd5816f0b5dc6ffb711bbba362968a2c075db11dc5c7f11d29733d
7
+ data.tar.gz: 32b7bbab75bd6fbb1620808f389d4bfc051a20da124823ada6ea154635306b8b6f6ad378c4bc55a1733211b6438092b307d36d83cc3d23494462729c7b80c817
data/CHANGELOG.md CHANGED
@@ -2,9 +2,14 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 3.3.1
6
+
7
+ - Optimized caching towards reducing retained memory after calling `OpenapiFirst.load` without using a global cache. (Removed `OpenapiFirst.clear_cache!`.)
8
+ - Require ruby >= 3.3.0
9
+
5
10
  ## 3.3.0
6
11
 
7
- - OpenapiFirst will now cache the contents of files that have been loaded. If you need to reload your OpenAPI definition for tests or server hot reloading, you can call `OpenapiFirst.clear_cache!`.
12
+ - ~OpenapiFirst will now cache the contents of files that have been loaded. If you need to reload your OpenAPI definition for tests or server hot reloading, you can call `OpenapiFirst.clear_cache!`.~
8
13
  - Optimized `OpenapiFirst::Router#match` for faster path matching and reduced memory allocation.
9
14
 
10
15
  ## 3.2.1
data/README.md CHANGED
@@ -82,9 +82,6 @@ The request validation middleware returns a 4xx if the request is invalid or not
82
82
 
83
83
  ```ruby
84
84
  use OpenapiFirst::Middlewares::RequestValidation
85
-
86
- # Pass `raise_error: true` to raise an error if request is invalid:
87
- use OpenapiFirst::Middlewares::RequestValidation, raise_error: true
88
85
  ```
89
86
 
90
87
  #### Error responses
@@ -27,18 +27,20 @@ module OpenapiFirst
27
27
  meta_schema = detect_meta_schema(contents, filepath)
28
28
  @schemer_configuration = build_schemer_config(filepath:, meta_schema:)
29
29
  @config = config
30
- @contents = RefResolver.for(contents, filepath:)
30
+ @file_loader = FileLoader.new
31
+ ref_resolver = RefResolver.new(file_loader:)
32
+ @contents = ref_resolver.for(contents, filepath:)
31
33
  end
32
34
 
33
35
  attr_reader :config
34
- private attr_reader :schemer_configuration
36
+ private attr_reader :schemer_configuration, :file_loader
35
37
 
36
38
  def build_schemer_config(filepath:, meta_schema:)
37
39
  result = JSONSchemer.configuration.clone
38
40
  dir = (filepath && File.absolute_path(File.dirname(filepath))) || Dir.pwd
39
41
  result.base_uri = URI::File.build({ path: "#{dir}/" })
40
42
  result.ref_resolver = JSONSchemer::CachedResolver.new do |uri|
41
- FileLoader.load(uri.path)
43
+ file_loader.load(uri.path)
42
44
  end
43
45
  result.meta_schema = meta_schema
44
46
  result.insert_property_defaults = true
@@ -5,11 +5,15 @@ require 'yaml'
5
5
 
6
6
  module OpenapiFirst
7
7
  # @!visibility private
8
- module FileLoader
9
- @cache = {}
10
- @mutex = Mutex.new
8
+ class FileLoader
9
+ def self.load(file_path)
10
+ new.load(file_path)
11
+ end
11
12
 
12
- module_function
13
+ def initialize
14
+ @cache = {}
15
+ @mutex = Mutex.new
16
+ end
13
17
 
14
18
  def load(file_path)
15
19
  @cache[file_path] || @mutex.synchronize do
@@ -29,9 +33,5 @@ module OpenapiFirst
29
33
  end
30
34
  end
31
35
  end
32
-
33
- def clear_cache!
34
- @mutex.synchronize { @cache.clear }
35
- end
36
36
  end
37
37
  end
@@ -5,30 +5,42 @@ require 'json_schemer'
5
5
  module OpenapiFirst
6
6
  # This is here to give traverse an OAD while keeping $refs intact
7
7
  # @visibility private
8
- module RefResolver
9
- def self.load(filepath)
10
- contents = OpenapiFirst::FileLoader.load(filepath)
8
+ class RefResolver
9
+ def initialize(file_loader:)
10
+ @file_loader = file_loader
11
+ end
12
+
13
+ private attr_reader :file_loader
14
+
15
+ def load(filepath)
16
+ contents = file_loader.load(filepath)
11
17
  self.for(contents, filepath:)
12
18
  end
13
19
 
14
- def self.for(value, filepath: nil, context: value)
20
+ def for(value, filepath: nil, context: value)
15
21
  case value
16
22
  when ::Hash
17
- resolver = Hash.new(value, context:, filepath:)
23
+ resolver = Hash.new(value, context:, filepath:, ref_resolver: self)
18
24
  if value.key?('$ref')
19
25
  probe = resolver.resolve_ref(value['$ref'])
20
26
  return probe if probe.is_a?(Array)
21
27
  end
22
28
  resolver
23
29
  when ::Array
24
- Array.new(value, context:, filepath:)
30
+ Array.new(value, context:, filepath:, ref_resolver: self)
25
31
  when ::NilClass
26
32
  nil
27
33
  else
28
- Simple.new(value)
34
+ Simple.new(value, ref_resolver: self)
29
35
  end
30
36
  end
31
37
 
38
+ def file_at(filepath, file_pointer)
39
+ file_contents = file_loader.load(filepath)
40
+ value = Hana::Pointer.new(file_pointer).eval(file_contents)
41
+ self.for(value, filepath: filepath, context: file_contents)
42
+ end
43
+
32
44
  # @visibility private
33
45
  module Diggable
34
46
  def dig(*keys)
@@ -42,10 +54,11 @@ module OpenapiFirst
42
54
 
43
55
  # @visibility private
44
56
  module Resolvable
45
- def initialize(value, context: value, filepath: nil)
57
+ def initialize(value, ref_resolver:, context: value, filepath: nil)
46
58
  @value = value
47
59
  @context = context
48
60
  @filepath = filepath
61
+ @ref_resolver = ref_resolver
49
62
  @dir = if filepath
50
63
  File.dirname(File.absolute_path(filepath))
51
64
  else
@@ -62,6 +75,8 @@ module OpenapiFirst
62
75
 
63
76
  attr_reader :filepath
64
77
 
78
+ private attr_reader :ref_resolver
79
+
65
80
  def ==(_other)
66
81
  raise "Don't call == on an unresolved value. Use .value == other instead."
67
82
  end
@@ -71,16 +86,14 @@ module OpenapiFirst
71
86
  value = Hana::Pointer.new(pointer[1..]).eval(context)
72
87
  raise "Unknown reference #{pointer} in #{context}" unless value
73
88
 
74
- return RefResolver.for(value, filepath:, context:)
89
+ return ref_resolver.for(value, filepath:, context:)
75
90
  end
76
91
 
77
92
  relative_path, file_pointer = pointer.split('#')
78
93
  full_path = File.expand_path(relative_path, dir)
79
- return RefResolver.load(full_path) unless file_pointer
94
+ return ref_resolver.load(full_path) unless file_pointer
80
95
 
81
- file_contents = FileLoader.load(full_path)
82
- value = Hana::Pointer.new(file_pointer).eval(file_contents)
83
- RefResolver.for(value, filepath: full_path, context: file_contents)
96
+ ref_resolver.file_at(full_path, file_pointer)
84
97
  rescue OpenapiFirst::FileNotFoundError => e
85
98
  message = "Problem with reference resolving #{pointer.inspect} in " \
86
99
  "file #{File.absolute_path(filepath).inspect}: #{e.message}"
@@ -114,13 +127,13 @@ module OpenapiFirst
114
127
  def [](key)
115
128
  return resolve_ref(@value['$ref'])[key] if !@value.key?(key) && @value.key?('$ref')
116
129
 
117
- RefResolver.for(@value[key], filepath:, context:)
130
+ ref_resolver.for(@value[key], filepath:, context:)
118
131
  end
119
132
 
120
133
  def fetch(key)
121
134
  return resolve_ref(@value['$ref']).fetch(key) if !@value.key?(key) && @value.key?('$ref')
122
135
 
123
- RefResolver.for(@value.fetch(key), filepath:, context:)
136
+ ref_resolver.for(@value.fetch(key), filepath:, context:)
124
137
  end
125
138
 
126
139
  def each
@@ -170,7 +183,7 @@ module OpenapiFirst
170
183
  item = @value[index]
171
184
  return resolve_ref(item['$ref']) if item.is_a?(::Hash) && item.key?('$ref')
172
185
 
173
- RefResolver.for(item, filepath:, context:)
186
+ ref_resolver.for(item, filepath:, context:)
174
187
  end
175
188
 
176
189
  def each
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OpenapiFirst
4
- VERSION = '3.3.0'
4
+ VERSION = '3.3.1'
5
5
  end
data/lib/openapi_first.rb CHANGED
@@ -22,11 +22,6 @@ module OpenapiFirst
22
22
 
23
23
  FAILURE = :openapi_first_validation_failure
24
24
 
25
- # Clears cached files
26
- def self.clear_cache!
27
- FileLoader.clear_cache!
28
- end
29
-
30
25
  # @return [Configuration]
31
26
  def self.configuration
32
27
  @configuration ||= Configuration.new
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: openapi_first
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.3.0
4
+ version: 3.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andreas Haller
@@ -179,14 +179,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
179
179
  requirements:
180
180
  - - ">="
181
181
  - !ruby/object:Gem::Version
182
- version: 3.2.0
182
+ version: 3.3.0
183
183
  required_rubygems_version: !ruby/object:Gem::Requirement
184
184
  requirements:
185
185
  - - ">="
186
186
  - !ruby/object:Gem::Version
187
187
  version: '0'
188
188
  requirements: []
189
- rubygems_version: 4.0.6
189
+ rubygems_version: 4.0.10
190
190
  specification_version: 4
191
191
  summary: OpenAPI based request validation, response validation, contract-testing and
192
192
  coverage