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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0a2e54a665eba19186fb3b7ca15dfe8707cf38ba121ac7f0ebcc956715f9dd87
4
- data.tar.gz: ddea6b70d0f33c09a81c39025621b9075c836e15d1dda0b066485f4602f70af3
3
+ metadata.gz: 7c840922f71d5eb8be437c9a544b50288b807ba6a7b2b1c1a5aecad9c5ff3a37
4
+ data.tar.gz: 14f0d4fc6828f74125cebc2267362ed8ef6b641980af2bbc2e71241169c056f1
5
5
  SHA512:
6
- metadata.gz: 36f5c80aa8a9252c34581de7963df8e318a84c3b5de85910bf5843f7d446b806c428e485b77dedfe2fef82395c84aecb454905e9fa964337bc81b8ce60c890b3
7
- data.tar.gz: 212fba9b212eb55990d91ac0ae739d8cc0745427414eb41a144601124709df80f844b74d3fccdd6e2dd230bc426688542185d4c536ed2c0b175acf085aaacb23
6
+ metadata.gz: 47ae33c7b4d13fa6c5250b6b68a961142be882af3ee349f07b93df7421c6b18592c6fa830ea2cb4302c3409569dd0b054efbeff2026caca9111e8963ab103df4
7
+ data.tar.gz: 91b5141d6b3046e0108c514a1f6e42382d8c0189a00faeaebe68dd1dda5ff5099ebf0370097cab86ce2e5e4f6aa4b3eb40bab1bed516beb3078635182e300038
@@ -6,7 +6,15 @@ module OpenapiValidator
6
6
  class Error < StandardError; end
7
7
 
8
8
  # @see Validator#initialize
9
- def self.call(*attrs)
10
- Validator.new(*attrs)
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(api_doc, **params)
11
- new(api_doc, **params).call
12
+ def self.call(**params)
13
+ new(**params).call
12
14
  end
13
15
 
14
16
  def call
15
- validate_path_exists
17
+ validate_path
16
18
  self
17
19
  end
18
20
 
19
21
  def validate_response(body:, code:)
20
- response_body = body
21
- response_code = code.to_s
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, response_body, fragment: fragment)
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 :api_doc, :path, :method, :code, :media_type
32
+ attr_reader :request, :validator
32
33
 
33
- def initialize(api_doc, path:, method:, code:, media_type: "application/json")
34
- @api_doc = api_doc
35
- @path = path
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 path_key
43
- path[/(\/[-_\/\{\}\w]*)/]
44
- end
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(api_doc, **params)
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 :api_doc, :additional_schemas
29
+ attr_reader :additional_schemas
23
30
 
24
- # @param [String] path path to openapi documentation
31
+ # @param [Hash] doc parsed openapi documentation
25
32
  # @param [Array<String>] additional_schemas paths to custom schemas
26
- def initialize(path, additional_schemas: [], api_base_path: "")
27
- @api_doc = FileLoader.call(path)
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
@@ -1,3 +1,3 @@
1
1
  module OpenapiValidator
2
- VERSION = "0.2.0"
2
+ VERSION = "0.3.0"
3
3
  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.2.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-10 00:00:00.000000000 Z
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