openapi_validator 0.1.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 +7 -0
- data/.gitignore +11 -0
- data/.rspec +3 -0
- data/.travis.yml +7 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +41 -0
- data/LICENSE.txt +21 -0
- data/README.md +37 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/data/openapi-3.0.json +1245 -0
- data/lib/openapi_validator.rb +12 -0
- data/lib/openapi_validator/documentation_validator.rb +57 -0
- data/lib/openapi_validator/extended_schema.rb +16 -0
- data/lib/openapi_validator/extended_type_attribute.rb +10 -0
- data/lib/openapi_validator/file_loader.rb +34 -0
- data/lib/openapi_validator/request_validator.rb +77 -0
- data/lib/openapi_validator/validator.rb +32 -0
- data/lib/openapi_validator/version.rb +3 -0
- data/openapi_validator.gemspec +29 -0
- metadata +119 -0
@@ -0,0 +1,12 @@
|
|
1
|
+
require "openapi_validator/extended_schema"
|
2
|
+
require "openapi_validator/validator"
|
3
|
+
require "openapi_validator/version"
|
4
|
+
|
5
|
+
module OpenapiValidator
|
6
|
+
class Error < StandardError; end
|
7
|
+
|
8
|
+
# @see Validator#initialize
|
9
|
+
def self.call(*attrs)
|
10
|
+
Validator.new(*attrs)
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require "json-schema"
|
2
|
+
require "openapi_validator/file_loader"
|
3
|
+
|
4
|
+
module OpenapiValidator
|
5
|
+
class DocumentationValidator
|
6
|
+
|
7
|
+
attr_reader :errors
|
8
|
+
|
9
|
+
# @param [Hash] api_doc parsed openapi documentation
|
10
|
+
# @param [Array<String>] additional_schemas paths to custom schemas
|
11
|
+
# @return [DocumentationValidator] validation result
|
12
|
+
def self.call(api_doc, additional_schemas: [])
|
13
|
+
new(api_doc, additional_schemas: additional_schemas).call
|
14
|
+
end
|
15
|
+
|
16
|
+
# @return [DocumentationValidator]
|
17
|
+
def call
|
18
|
+
validate
|
19
|
+
end
|
20
|
+
|
21
|
+
# @return [true, false]
|
22
|
+
def valid?
|
23
|
+
errors.empty?
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
attr_reader :api_doc, :schemas
|
29
|
+
|
30
|
+
# @return [DocumentationValidator]
|
31
|
+
def validate
|
32
|
+
parsed_schemas.each do |schema|
|
33
|
+
errors.concat JSON::Validator.fully_validate(schema, api_doc)
|
34
|
+
end
|
35
|
+
|
36
|
+
self
|
37
|
+
end
|
38
|
+
|
39
|
+
# @return [Array<Hash>] parsed custom schemas
|
40
|
+
def parsed_schemas
|
41
|
+
schemas.map { |schema| FileLoader.call(schema) }
|
42
|
+
end
|
43
|
+
|
44
|
+
# @param [Hash] api_doc parsed openapi documentation
|
45
|
+
# @param [Array<String>] additional_schemas paths to custom schemas
|
46
|
+
def initialize(api_doc, additional_schemas:)
|
47
|
+
@api_doc = api_doc
|
48
|
+
@schemas = additional_schemas.unshift openapi_schema
|
49
|
+
@errors = []
|
50
|
+
end
|
51
|
+
|
52
|
+
# @return [String] path to openapi v3 schema
|
53
|
+
def openapi_schema
|
54
|
+
File.expand_path("../../../data/openapi-3.0.json", __FILE__)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require "openapi_validator/extended_type_attribute"
|
2
|
+
require "json-schema"
|
3
|
+
|
4
|
+
module OpenapiValidator
|
5
|
+
class ExtendedSchema < JSON::Schema::Draft4
|
6
|
+
def initialize
|
7
|
+
super
|
8
|
+
@attributes['type'] = ExtendedTypeAttribute
|
9
|
+
@uri = URI.parse('http://tempuri.org/apivore/extended_schema')
|
10
|
+
@names = ['http://tempuri.org/apivore/extended_schema']
|
11
|
+
end
|
12
|
+
|
13
|
+
JSON::Validator.register_validator(self.new)
|
14
|
+
JSON::Validator.register_default_validator(self.new)
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require "json-schema/attributes/type_v4"
|
2
|
+
|
3
|
+
module OpenapiValidator
|
4
|
+
class ExtendedTypeAttribute < JSON::Schema::TypeV4Attribute
|
5
|
+
def self.validate(current_schema, data, fragments, processor, validator, options = {})
|
6
|
+
return if data.nil? && current_schema.schema["nullable"] == true
|
7
|
+
super
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require "json"
|
2
|
+
require "yaml"
|
3
|
+
|
4
|
+
module OpenapiValidator
|
5
|
+
class FileLoader
|
6
|
+
|
7
|
+
# @param [String] path path to file
|
8
|
+
# @return [Hash] parsed file
|
9
|
+
def self.call(path)
|
10
|
+
new(path).call
|
11
|
+
end
|
12
|
+
|
13
|
+
# @return [Hash] parsed file
|
14
|
+
def call
|
15
|
+
case File.extname(path)
|
16
|
+
when ".yml", ".yaml"
|
17
|
+
YAML.load_file(path)
|
18
|
+
when ".json"
|
19
|
+
JSON.parse(File.read(path))
|
20
|
+
else
|
21
|
+
raise "Can't parse #{path}. It should be json or yaml file.", Error
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
attr_reader :path
|
28
|
+
|
29
|
+
# @param [String] path path to file
|
30
|
+
def initialize(path)
|
31
|
+
@path = path
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module OpenapiValidator
|
2
|
+
class RequestValidator
|
3
|
+
|
4
|
+
attr_reader :errors
|
5
|
+
|
6
|
+
def valid?
|
7
|
+
errors.empty?
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.call(api_doc, **params)
|
11
|
+
new(api_doc, **params).call
|
12
|
+
end
|
13
|
+
|
14
|
+
def call
|
15
|
+
validate_path_exists
|
16
|
+
self
|
17
|
+
end
|
18
|
+
|
19
|
+
def validate_response(response_body)
|
20
|
+
@response_body = response_body
|
21
|
+
@errors += JSON::Validator.fully_validate(api_doc, response_body, fragment: fragment)
|
22
|
+
self
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
attr_reader :api_doc, :path, :method, :code, :media_type
|
28
|
+
|
29
|
+
def initialize(api_doc, path:, method:, code:, media_type: "application/json")
|
30
|
+
@api_doc = api_doc
|
31
|
+
@path = path
|
32
|
+
@method = method.to_s
|
33
|
+
@code = code.to_s
|
34
|
+
@media_type = media_type.to_s
|
35
|
+
@errors = []
|
36
|
+
end
|
37
|
+
|
38
|
+
def path_key
|
39
|
+
path[/(\/[-_\/\{\}\w]*)/]
|
40
|
+
end
|
41
|
+
|
42
|
+
def fragment
|
43
|
+
["#", "paths", path_key, method, "responses", code, "content", media_type, "schema"].tap do |array|
|
44
|
+
array.define_singleton_method(:split) do |_|
|
45
|
+
self
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def validate_path_exists
|
51
|
+
path_schema = api_doc.dig("paths", path_key)
|
52
|
+
unless path_schema
|
53
|
+
errors << "OpenAPI documentation does not have a documented path for #{path_key}"
|
54
|
+
return
|
55
|
+
end
|
56
|
+
|
57
|
+
responses_schema = path_schema.dig(method, "responses")
|
58
|
+
unless responses_schema
|
59
|
+
errors << "OpenAPI documentation does not have a documented path for #{method.upcase} #{path_key}"
|
60
|
+
return
|
61
|
+
end
|
62
|
+
|
63
|
+
content_schema = responses_schema.dig(code, "content") || responses_schema.dig("default", "content")
|
64
|
+
unless content_schema
|
65
|
+
errors << "OpenAPI documentation does not have a documented response for code #{code}"\
|
66
|
+
" at path #{method.upcase} #{path_key}"
|
67
|
+
return
|
68
|
+
end
|
69
|
+
|
70
|
+
response_schema = content_schema.dig(media_type)
|
71
|
+
unless response_schema
|
72
|
+
errors << "OpenAPI documentation does not have a documented response for #{media_type}"\
|
73
|
+
" media-type at path #{method.upcase} #{path_key}"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require "json-schema"
|
2
|
+
require "openapi_validator/file_loader"
|
3
|
+
require "openapi_validator/documentation_validator"
|
4
|
+
require "openapi_validator/request_validator"
|
5
|
+
|
6
|
+
module OpenapiValidator
|
7
|
+
class Validator
|
8
|
+
|
9
|
+
attr_reader :api_base_path
|
10
|
+
|
11
|
+
# @return [DocumentationValidator] validation result
|
12
|
+
def validate_documentation
|
13
|
+
DocumentationValidator.call(api_doc, additional_schemas: additional_schemas)
|
14
|
+
end
|
15
|
+
|
16
|
+
def validate_request(**params)
|
17
|
+
RequestValidator.call(api_doc, **params)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
attr_reader :api_doc, :additional_schemas
|
23
|
+
|
24
|
+
# @param [String] path path to openapi documentation
|
25
|
+
# @param [Array<String>] additional_schemas paths to custom schemas
|
26
|
+
def initialize(path, additional_schemas: [], api_base_path: "")
|
27
|
+
@api_doc = FileLoader.call(path)
|
28
|
+
@api_base_path = api_base_path
|
29
|
+
@additional_schemas = additional_schemas
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
lib = File.expand_path("../lib", __FILE__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require "openapi_validator/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "openapi_validator"
|
7
|
+
spec.version = OpenapiValidator::VERSION
|
8
|
+
spec.authors = ["Svyatoslav Kryukov"]
|
9
|
+
spec.email = ["s.g.kryukov@yandex.ru"]
|
10
|
+
|
11
|
+
spec.summary = "Validator used for openapi_rspec"
|
12
|
+
spec.homepage = "https://github.com/llcmedsolutions/openapi_validator"
|
13
|
+
spec.license = "MIT"
|
14
|
+
|
15
|
+
# Specify which files should be added to the gem when it is released.
|
16
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
17
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
18
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
19
|
+
end
|
20
|
+
spec.bindir = "exe"
|
21
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
22
|
+
spec.require_paths = ["lib"]
|
23
|
+
|
24
|
+
spec.add_runtime_dependency "json-schema", "~> 2.8"
|
25
|
+
|
26
|
+
spec.add_development_dependency "bundler", "~> 2.0"
|
27
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
28
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
29
|
+
end
|
metadata
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: openapi_validator
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Svyatoslav Kryukov
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-04-09 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: json-schema
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.8'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.8'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '10.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '10.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '3.0'
|
69
|
+
description:
|
70
|
+
email:
|
71
|
+
- s.g.kryukov@yandex.ru
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- ".gitignore"
|
77
|
+
- ".rspec"
|
78
|
+
- ".travis.yml"
|
79
|
+
- Gemfile
|
80
|
+
- Gemfile.lock
|
81
|
+
- LICENSE.txt
|
82
|
+
- README.md
|
83
|
+
- Rakefile
|
84
|
+
- bin/console
|
85
|
+
- bin/setup
|
86
|
+
- data/openapi-3.0.json
|
87
|
+
- lib/openapi_validator.rb
|
88
|
+
- lib/openapi_validator/documentation_validator.rb
|
89
|
+
- lib/openapi_validator/extended_schema.rb
|
90
|
+
- lib/openapi_validator/extended_type_attribute.rb
|
91
|
+
- lib/openapi_validator/file_loader.rb
|
92
|
+
- lib/openapi_validator/request_validator.rb
|
93
|
+
- lib/openapi_validator/validator.rb
|
94
|
+
- lib/openapi_validator/version.rb
|
95
|
+
- openapi_validator.gemspec
|
96
|
+
homepage: https://github.com/llcmedsolutions/openapi_validator
|
97
|
+
licenses:
|
98
|
+
- MIT
|
99
|
+
metadata: {}
|
100
|
+
post_install_message:
|
101
|
+
rdoc_options: []
|
102
|
+
require_paths:
|
103
|
+
- lib
|
104
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
105
|
+
requirements:
|
106
|
+
- - ">="
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: '0'
|
109
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
110
|
+
requirements:
|
111
|
+
- - ">="
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
version: '0'
|
114
|
+
requirements: []
|
115
|
+
rubygems_version: 3.0.1
|
116
|
+
signing_key:
|
117
|
+
specification_version: 4
|
118
|
+
summary: Validator used for openapi_rspec
|
119
|
+
test_files: []
|