openapi_validator 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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,3 @@
1
+ module OpenapiValidator
2
+ VERSION = "0.1.0"
3
+ 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: []