openapi_validator 0.2.0 → 0.3.0
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 +4 -4
- data/lib/openapi_validator.rb +10 -2
- data/lib/openapi_validator/path_validator.rb +96 -0
- data/lib/openapi_validator/request.rb +23 -0
- data/lib/openapi_validator/request_validator.rb +18 -53
- data/lib/openapi_validator/validator.rb +27 -6
- data/lib/openapi_validator/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7c840922f71d5eb8be437c9a544b50288b807ba6a7b2b1c1a5aecad9c5ff3a37
|
4
|
+
data.tar.gz: 14f0d4fc6828f74125cebc2267362ed8ef6b641980af2bbc2e71241169c056f1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 47ae33c7b4d13fa6c5250b6b68a961142be882af3ee349f07b93df7421c6b18592c6fa830ea2cb4302c3409569dd0b054efbeff2026caca9111e8963ab103df4
|
7
|
+
data.tar.gz: 91b5141d6b3046e0108c514a1f6e42382d8c0189a00faeaebe68dd1dda5ff5099ebf0370097cab86ce2e5e4f6aa4b3eb40bab1bed516beb3078635182e300038
|
data/lib/openapi_validator.rb
CHANGED
@@ -6,7 +6,15 @@ module OpenapiValidator
|
|
6
6
|
class Error < StandardError; end
|
7
7
|
|
8
8
|
# @see Validator#initialize
|
9
|
-
def self.call(
|
10
|
-
|
9
|
+
def self.call(doc, **params)
|
10
|
+
if doc.is_a? String
|
11
|
+
parsed_doc = FileLoader.call(doc)
|
12
|
+
elsif doc.is_a? Hash
|
13
|
+
parsed_doc = doc
|
14
|
+
else
|
15
|
+
raise ArgumentError, "Please provide parsed OpenAPI doc as Hash or path to file as String. Passed: #{doc.class}"
|
16
|
+
end
|
17
|
+
|
18
|
+
Validator.new(parsed_doc, **params)
|
11
19
|
end
|
12
20
|
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module OpenapiValidator
|
4
|
+
class PathValidator
|
5
|
+
class Error < StandardError; end
|
6
|
+
|
7
|
+
extend Forwardable
|
8
|
+
|
9
|
+
def path
|
10
|
+
[path_key, method, @schema_code]
|
11
|
+
end
|
12
|
+
|
13
|
+
def fragment
|
14
|
+
build_fragment.tap do |array|
|
15
|
+
array.define_singleton_method(:split) do |_|
|
16
|
+
self
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.call(**params)
|
22
|
+
new(**params).call
|
23
|
+
end
|
24
|
+
|
25
|
+
def call
|
26
|
+
validate_path_exists
|
27
|
+
self
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
attr_reader :api_doc
|
33
|
+
|
34
|
+
def_delegators :@request, :media_type, :method, :path_key, :code
|
35
|
+
|
36
|
+
def initialize(request:, api_doc:)
|
37
|
+
@request = request
|
38
|
+
@api_doc = api_doc
|
39
|
+
end
|
40
|
+
|
41
|
+
def validate_path_exists
|
42
|
+
code_schema.dig(media_type) ||
|
43
|
+
raise(Error, "OpenAPI documentation does not have a documented response"\
|
44
|
+
" for #{media_type} media-type at path #{method.upcase} #{path_key}")
|
45
|
+
end
|
46
|
+
|
47
|
+
def code_schema
|
48
|
+
schema = schema_code
|
49
|
+
ref_schema(schema) || content_schema(schema)
|
50
|
+
end
|
51
|
+
|
52
|
+
def content_schema(responses)
|
53
|
+
responses.dig("content")
|
54
|
+
end
|
55
|
+
|
56
|
+
def ref_schema(responses)
|
57
|
+
schema = responses.dig("$ref")
|
58
|
+
return unless schema
|
59
|
+
|
60
|
+
@fragment_path = "#{schema}/#{media_type}/schema"
|
61
|
+
api_doc.dig(*schema[2..-1].split("/"), "content")
|
62
|
+
end
|
63
|
+
|
64
|
+
def schema_code
|
65
|
+
responses = responses_schema
|
66
|
+
if responses.dig(code)
|
67
|
+
@schema_code = code
|
68
|
+
elsif responses.dig("default")
|
69
|
+
@schema_code = "default"
|
70
|
+
else
|
71
|
+
raise(Error, "OpenAPI documentation does not have a documented response for code #{code}"\
|
72
|
+
" at path #{method.upcase} #{path_key}")
|
73
|
+
end
|
74
|
+
|
75
|
+
responses.dig(@schema_code)
|
76
|
+
end
|
77
|
+
|
78
|
+
def responses_schema
|
79
|
+
path_schema.dig(method, "responses") ||
|
80
|
+
raise(Error, "OpenAPI documentation does not have a documented path for #{method.upcase} #{path_key}")
|
81
|
+
end
|
82
|
+
|
83
|
+
def path_schema
|
84
|
+
api_doc.dig("paths", path_key) ||
|
85
|
+
raise(Error, "OpenAPI documentation does not have a documented path for #{path_key}")
|
86
|
+
end
|
87
|
+
|
88
|
+
def build_fragment
|
89
|
+
if @fragment_path
|
90
|
+
@fragment_path.split("/")
|
91
|
+
else
|
92
|
+
["#", "paths", path_key, method, "responses", @schema_code, "content", media_type, "schema"]
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module OpenapiValidator
|
2
|
+
class Request
|
3
|
+
|
4
|
+
attr_reader :path, :method, :code, :media_type
|
5
|
+
|
6
|
+
def self.call(**params)
|
7
|
+
new(**params)
|
8
|
+
end
|
9
|
+
|
10
|
+
def path_key
|
11
|
+
path[/(\/[-_\/\{\}\w]*)/]
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def initialize(path:, method:, code:, media_type: "application/json")
|
17
|
+
@path = path
|
18
|
+
@method = method.to_s
|
19
|
+
@code = code.to_s
|
20
|
+
@media_type = media_type.to_s
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -1,81 +1,46 @@
|
|
1
|
+
require 'openapi_validator/path_validator'
|
2
|
+
|
1
3
|
module OpenapiValidator
|
2
4
|
class RequestValidator
|
3
5
|
|
4
|
-
attr_reader :errors
|
6
|
+
attr_reader :errors, :api_doc, :path_validator
|
5
7
|
|
6
8
|
def valid?
|
7
9
|
errors.empty?
|
8
10
|
end
|
9
11
|
|
10
|
-
def self.call(
|
11
|
-
new(
|
12
|
+
def self.call(**params)
|
13
|
+
new(**params).call
|
12
14
|
end
|
13
15
|
|
14
16
|
def call
|
15
|
-
|
17
|
+
validate_path
|
16
18
|
self
|
17
19
|
end
|
18
20
|
|
19
21
|
def validate_response(body:, code:)
|
20
|
-
|
21
|
-
|
22
|
-
if @code != response_code
|
23
|
-
@errors << "Path #{path} did not respond with expected status code. Expected #{@code} got #{response_code}"
|
22
|
+
if request.code != code.to_s
|
23
|
+
@errors << "Path #{request.path} did not respond with expected status code. Expected #{request.code} got #{code}"
|
24
24
|
end
|
25
|
-
@errors += JSON::Validator.fully_validate(api_doc,
|
25
|
+
@errors += JSON::Validator.fully_validate(validator.api_doc, body, fragment: path_validator.fragment)
|
26
|
+
validator.remove_validated_path(path_validator.path) if @errors.empty?
|
26
27
|
self
|
27
28
|
end
|
28
29
|
|
29
30
|
private
|
30
31
|
|
31
|
-
attr_reader :
|
32
|
+
attr_reader :request, :validator
|
32
33
|
|
33
|
-
def initialize(
|
34
|
-
@
|
35
|
-
@
|
36
|
-
@method = method.to_s
|
37
|
-
@code = code.to_s
|
38
|
-
@media_type = media_type.to_s
|
34
|
+
def initialize(request:, validator:)
|
35
|
+
@validator = validator
|
36
|
+
@request = request
|
39
37
|
@errors = []
|
40
38
|
end
|
41
39
|
|
42
|
-
def
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
def fragment
|
47
|
-
["#", "paths", path_key, method, "responses", code, "content", media_type, "schema"].tap do |array|
|
48
|
-
array.define_singleton_method(:split) do |_|
|
49
|
-
self
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
def validate_path_exists
|
55
|
-
path_schema = api_doc.dig("paths", path_key)
|
56
|
-
unless path_schema
|
57
|
-
errors << "OpenAPI documentation does not have a documented path for #{path_key}"
|
58
|
-
return
|
59
|
-
end
|
60
|
-
|
61
|
-
responses_schema = path_schema.dig(method, "responses")
|
62
|
-
unless responses_schema
|
63
|
-
errors << "OpenAPI documentation does not have a documented path for #{method.upcase} #{path_key}"
|
64
|
-
return
|
65
|
-
end
|
66
|
-
|
67
|
-
content_schema = responses_schema.dig(code, "content") || responses_schema.dig("default", "content")
|
68
|
-
unless content_schema
|
69
|
-
errors << "OpenAPI documentation does not have a documented response for code #{code}"\
|
70
|
-
" at path #{method.upcase} #{path_key}"
|
71
|
-
return
|
72
|
-
end
|
73
|
-
|
74
|
-
response_schema = content_schema.dig(media_type)
|
75
|
-
unless response_schema
|
76
|
-
errors << "OpenAPI documentation does not have a documented response for #{media_type}"\
|
77
|
-
" media-type at path #{method.upcase} #{path_key}"
|
78
|
-
end
|
40
|
+
def validate_path
|
41
|
+
@path_validator = PathValidator.call(request: request, api_doc: validator.api_doc)
|
42
|
+
rescue PathValidator::Error => e
|
43
|
+
@errors << e.message
|
79
44
|
end
|
80
45
|
end
|
81
46
|
end
|
@@ -1,32 +1,53 @@
|
|
1
1
|
require "json-schema"
|
2
2
|
require "openapi_validator/file_loader"
|
3
3
|
require "openapi_validator/documentation_validator"
|
4
|
+
require "openapi_validator/request"
|
4
5
|
require "openapi_validator/request_validator"
|
5
6
|
|
6
7
|
module OpenapiValidator
|
7
8
|
class Validator
|
8
9
|
|
9
|
-
attr_reader :api_base_path
|
10
|
+
attr_reader :api_base_path, :unvalidated_requests, :api_doc
|
10
11
|
|
11
12
|
# @return [DocumentationValidator] validation result
|
12
13
|
def validate_documentation
|
13
14
|
DocumentationValidator.call(api_doc, additional_schemas: additional_schemas)
|
14
15
|
end
|
15
16
|
|
17
|
+
# @return [Object] RequestValidator
|
16
18
|
def validate_request(**params)
|
17
|
-
RequestValidator.call(
|
19
|
+
RequestValidator.call(request: Request.call(**params), validator: self)
|
20
|
+
end
|
21
|
+
|
22
|
+
# @param [Array] request
|
23
|
+
def remove_validated_path(request)
|
24
|
+
@unvalidated_requests.delete(request)
|
18
25
|
end
|
19
26
|
|
20
27
|
private
|
21
28
|
|
22
|
-
attr_reader :
|
29
|
+
attr_reader :additional_schemas
|
23
30
|
|
24
|
-
# @param [
|
31
|
+
# @param [Hash] doc parsed openapi documentation
|
25
32
|
# @param [Array<String>] additional_schemas paths to custom schemas
|
26
|
-
def initialize(
|
27
|
-
@api_doc =
|
33
|
+
def initialize(doc, additional_schemas: [], api_base_path: "")
|
34
|
+
@api_doc = doc
|
28
35
|
@api_base_path = api_base_path
|
29
36
|
@additional_schemas = additional_schemas
|
37
|
+
@unvalidated_requests = build_unvalidated_requests
|
38
|
+
end
|
39
|
+
|
40
|
+
# @return [Array]
|
41
|
+
def build_unvalidated_requests
|
42
|
+
requests = []
|
43
|
+
api_doc["paths"] && api_doc["paths"].each do |path, methods|
|
44
|
+
methods.each do |method, values|
|
45
|
+
values["responses"] && values["responses"].each_key do |code|
|
46
|
+
requests << [path, method, code]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
requests
|
30
51
|
end
|
31
52
|
end
|
32
53
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: openapi_validator
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Svyatoslav Kryukov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-04-
|
11
|
+
date: 2019-04-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: json-schema
|
@@ -81,6 +81,8 @@ files:
|
|
81
81
|
- lib/openapi_validator/extended_schema.rb
|
82
82
|
- lib/openapi_validator/extended_type_attribute.rb
|
83
83
|
- lib/openapi_validator/file_loader.rb
|
84
|
+
- lib/openapi_validator/path_validator.rb
|
85
|
+
- lib/openapi_validator/request.rb
|
84
86
|
- lib/openapi_validator/request_validator.rb
|
85
87
|
- lib/openapi_validator/validator.rb
|
86
88
|
- lib/openapi_validator/version.rb
|