skooma 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +22 -0
- data/LICENSE.txt +21 -0
- data/README.md +125 -0
- data/data/oas-3.1/dialect/base.json +25 -0
- data/data/oas-3.1/meta/base.json +96 -0
- data/data/oas-3.1/schema/2022-10-07.json +1441 -0
- data/data/oas-3.1/schema-base/2022-10-07.json +23 -0
- data/lib/skooma/body_parsers.rb +31 -0
- data/lib/skooma/dialects/oas_3_1.rb +41 -0
- data/lib/skooma/inflector.rb +19 -0
- data/lib/skooma/instance.rb +112 -0
- data/lib/skooma/keywords/oas_3_1/dialect/discriminator.rb +13 -0
- data/lib/skooma/keywords/oas_3_1/dialect/example.rb +13 -0
- data/lib/skooma/keywords/oas_3_1/dialect/external_docs.rb +13 -0
- data/lib/skooma/keywords/oas_3_1/dialect/xml.rb +13 -0
- data/lib/skooma/keywords/oas_3_1/schema.rb +32 -0
- data/lib/skooma/keywords/oas_3_1.rb +22 -0
- data/lib/skooma/objects/base/keywords/deprecated.rb +13 -0
- data/lib/skooma/objects/base/keywords/description.rb +13 -0
- data/lib/skooma/objects/base/keywords/security.rb +13 -0
- data/lib/skooma/objects/base/keywords/servers.rb +13 -0
- data/lib/skooma/objects/base/keywords/summary.rb +13 -0
- data/lib/skooma/objects/base/keywords/tags.rb +13 -0
- data/lib/skooma/objects/base.rb +33 -0
- data/lib/skooma/objects/callback.rb +12 -0
- data/lib/skooma/objects/components.rb +30 -0
- data/lib/skooma/objects/header/keywords/content.rb +50 -0
- data/lib/skooma/objects/header/keywords/example.rb +13 -0
- data/lib/skooma/objects/header/keywords/examples.rb +13 -0
- data/lib/skooma/objects/header/keywords/explode.rb +13 -0
- data/lib/skooma/objects/header/keywords/required.rb +19 -0
- data/lib/skooma/objects/header/keywords/schema.rb +19 -0
- data/lib/skooma/objects/header/keywords/style.rb +13 -0
- data/lib/skooma/objects/header.rb +23 -0
- data/lib/skooma/objects/media_type.rb +14 -0
- data/lib/skooma/objects/openapi/keywords/components.rb +23 -0
- data/lib/skooma/objects/openapi/keywords/info.rb +13 -0
- data/lib/skooma/objects/openapi/keywords/json_schema_dialect.rb +21 -0
- data/lib/skooma/objects/openapi/keywords/openapi.rb +24 -0
- data/lib/skooma/objects/openapi/keywords/paths.rb +62 -0
- data/lib/skooma/objects/openapi/keywords/security.rb +13 -0
- data/lib/skooma/objects/openapi/keywords/webhooks.rb +15 -0
- data/lib/skooma/objects/openapi.rb +39 -0
- data/lib/skooma/objects/operation/keywords/callbacks.rb +13 -0
- data/lib/skooma/objects/operation/keywords/operation_id.rb +13 -0
- data/lib/skooma/objects/operation/keywords/parameters.rb +53 -0
- data/lib/skooma/objects/operation/keywords/request_body.rb +21 -0
- data/lib/skooma/objects/operation/keywords/responses.rb +48 -0
- data/lib/skooma/objects/operation.rb +31 -0
- data/lib/skooma/objects/parameter/keywords/allow_empty_value.rb +13 -0
- data/lib/skooma/objects/parameter/keywords/allow_reserved.rb +13 -0
- data/lib/skooma/objects/parameter/keywords/content.rb +21 -0
- data/lib/skooma/objects/parameter/keywords/in.rb +13 -0
- data/lib/skooma/objects/parameter/keywords/name.rb +13 -0
- data/lib/skooma/objects/parameter/keywords/required.rb +19 -0
- data/lib/skooma/objects/parameter/keywords/schema.rb +21 -0
- data/lib/skooma/objects/parameter/keywords/value_parser.rb +80 -0
- data/lib/skooma/objects/parameter.rb +27 -0
- data/lib/skooma/objects/path_item/keywords/base_operation.rb +25 -0
- data/lib/skooma/objects/path_item/keywords/delete.rb +16 -0
- data/lib/skooma/objects/path_item/keywords/get.rb +16 -0
- data/lib/skooma/objects/path_item/keywords/head.rb +16 -0
- data/lib/skooma/objects/path_item/keywords/options.rb +16 -0
- data/lib/skooma/objects/path_item/keywords/parameters.rb +19 -0
- data/lib/skooma/objects/path_item/keywords/patch.rb +16 -0
- data/lib/skooma/objects/path_item/keywords/post.rb +16 -0
- data/lib/skooma/objects/path_item/keywords/put.rb +16 -0
- data/lib/skooma/objects/path_item/keywords/trace.rb +16 -0
- data/lib/skooma/objects/path_item.rb +28 -0
- data/lib/skooma/objects/ref_base.rb +33 -0
- data/lib/skooma/objects/request_body/keywords/required.rb +19 -0
- data/lib/skooma/objects/request_body.rb +17 -0
- data/lib/skooma/objects/response/keywords/content.rb +51 -0
- data/lib/skooma/objects/response/keywords/headers.rb +37 -0
- data/lib/skooma/objects/response/keywords/links.rb +13 -0
- data/lib/skooma/objects/response.rb +18 -0
- data/lib/skooma/output_format.rb +35 -0
- data/lib/skooma/rspec.rb +166 -0
- data/lib/skooma/validators/double.rb +13 -0
- data/lib/skooma/validators/float.rb +13 -0
- data/lib/skooma/validators/int_32.rb +15 -0
- data/lib/skooma/validators/int_64.rb +15 -0
- data/lib/skooma/version.rb +5 -0
- data/lib/skooma.rb +26 -0
- metadata +161 -0
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Skooma
|
4
|
+
module Objects
|
5
|
+
class PathItem
|
6
|
+
module Keywords
|
7
|
+
class Delete < BaseOperation
|
8
|
+
self.key = "options"
|
9
|
+
self.depends_on = %w[parameters]
|
10
|
+
self.value_schema = :schema
|
11
|
+
self.schema_value_class = Objects::Operation
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Skooma
|
4
|
+
module Objects
|
5
|
+
class PathItem
|
6
|
+
module Keywords
|
7
|
+
class Get < BaseOperation
|
8
|
+
self.key = "get"
|
9
|
+
self.depends_on = %w[parameters]
|
10
|
+
self.value_schema = :schema
|
11
|
+
self.schema_value_class = Objects::Operation
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Skooma
|
4
|
+
module Objects
|
5
|
+
class PathItem
|
6
|
+
module Keywords
|
7
|
+
class Head < BaseOperation
|
8
|
+
self.key = "head"
|
9
|
+
self.depends_on = %w[parameters]
|
10
|
+
self.value_schema = :schema
|
11
|
+
self.schema_value_class = Objects::Operation
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Skooma
|
4
|
+
module Objects
|
5
|
+
class PathItem
|
6
|
+
module Keywords
|
7
|
+
class Options < BaseOperation
|
8
|
+
self.key = "options"
|
9
|
+
self.depends_on = %w[parameters]
|
10
|
+
self.value_schema = :schema
|
11
|
+
self.schema_value_class = Objects::Operation
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Skooma
|
4
|
+
module Objects
|
5
|
+
class PathItem
|
6
|
+
module Keywords
|
7
|
+
class Parameters < JSONSkooma::Keywords::Base
|
8
|
+
self.key = "parameters"
|
9
|
+
self.value_schema = :array_of_schemas
|
10
|
+
self.schema_value_class = Objects::Parameter
|
11
|
+
|
12
|
+
def evaluate(instance, result)
|
13
|
+
result.skip_assertion
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Skooma
|
4
|
+
module Objects
|
5
|
+
class PathItem
|
6
|
+
module Keywords
|
7
|
+
class Patch < BaseOperation
|
8
|
+
self.key = "patch"
|
9
|
+
self.depends_on = %w[parameters]
|
10
|
+
self.value_schema = :schema
|
11
|
+
self.schema_value_class = Objects::Operation
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Skooma
|
4
|
+
module Objects
|
5
|
+
class PathItem
|
6
|
+
module Keywords
|
7
|
+
class Post < BaseOperation
|
8
|
+
self.key = "post"
|
9
|
+
self.depends_on = %w[parameters]
|
10
|
+
self.value_schema = :schema
|
11
|
+
self.schema_value_class = Objects::Operation
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Skooma
|
4
|
+
module Objects
|
5
|
+
class PathItem
|
6
|
+
module Keywords
|
7
|
+
class Put < BaseOperation
|
8
|
+
self.key = "put"
|
9
|
+
self.depends_on = %w[parameters]
|
10
|
+
self.value_schema = :schema
|
11
|
+
self.schema_value_class = Objects::Operation
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Skooma
|
4
|
+
module Objects
|
5
|
+
class PathItem
|
6
|
+
module Keywords
|
7
|
+
class Trace < BaseOperation
|
8
|
+
self.key = "trace"
|
9
|
+
self.depends_on = %w[parameters]
|
10
|
+
self.value_schema = :schema
|
11
|
+
self.schema_value_class = Objects::Operation
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Skooma
|
4
|
+
module Objects
|
5
|
+
# Describes the operations available on a single path.
|
6
|
+
# https://spec.openapis.org/oas/v3.1.0#path-item-object
|
7
|
+
class PathItem < RefBase
|
8
|
+
def kw_classes
|
9
|
+
[
|
10
|
+
Base::Keywords::Summary,
|
11
|
+
Base::Keywords::Description,
|
12
|
+
|
13
|
+
Keywords::Get,
|
14
|
+
Keywords::Put,
|
15
|
+
Keywords::Post,
|
16
|
+
Keywords::Delete,
|
17
|
+
Keywords::Options,
|
18
|
+
Keywords::Head,
|
19
|
+
Keywords::Patch,
|
20
|
+
Keywords::Trace,
|
21
|
+
|
22
|
+
Base::Keywords::Servers,
|
23
|
+
Keywords::Parameters
|
24
|
+
]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Skooma
|
4
|
+
module Objects
|
5
|
+
# A simple object to allow referencing other components in the OpenAPI document.
|
6
|
+
# https://spec.openapis.org/oas/v3.1.0#referenceObject
|
7
|
+
class RefBase < Base
|
8
|
+
# This object cannot be extended with additional properties
|
9
|
+
# and any properties added SHALL be ignored.
|
10
|
+
def resolve_keywords(value)
|
11
|
+
return super unless value.key?("$ref")
|
12
|
+
|
13
|
+
resolve_ref_keywords(value)
|
14
|
+
end
|
15
|
+
|
16
|
+
def ref_kw_classes
|
17
|
+
[
|
18
|
+
JSONSkooma::Keywords::Core::Ref,
|
19
|
+
Base::Keywords::Summary,
|
20
|
+
Base::Keywords::Description
|
21
|
+
]
|
22
|
+
end
|
23
|
+
|
24
|
+
def resolve_ref_keywords(value)
|
25
|
+
ref_kw_classes.each do |kw_class|
|
26
|
+
next unless value.key?(kw_class.key)
|
27
|
+
|
28
|
+
add_keyword(kw_class.new(self, value[kw_class.key]))
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Skooma
|
4
|
+
module Objects
|
5
|
+
class RequestBody
|
6
|
+
module Keywords
|
7
|
+
class Required < JSONSkooma::Keywords::Base
|
8
|
+
self.key = "required"
|
9
|
+
|
10
|
+
def evaluate(instance, result)
|
11
|
+
if json.value && instance["body"].value.nil?
|
12
|
+
result.failure("Body is required")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Skooma
|
4
|
+
module Objects
|
5
|
+
# Describes a single request body.
|
6
|
+
# https://spec.openapis.org/oas/v3.1.0#request-body-object
|
7
|
+
class RequestBody < RefBase
|
8
|
+
def kw_classes
|
9
|
+
[
|
10
|
+
Base::Keywords::Description,
|
11
|
+
Skooma::Objects::Response::Keywords::Content,
|
12
|
+
Keywords::Required
|
13
|
+
]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Skooma
|
4
|
+
module Objects
|
5
|
+
class Response
|
6
|
+
module Keywords
|
7
|
+
class Content < JSONSkooma::Keywords::Base
|
8
|
+
self.key = "content"
|
9
|
+
self.value_schema = :object_of_schemas
|
10
|
+
self.schema_value_class = Objects::MediaType
|
11
|
+
|
12
|
+
def evaluate(instance, result)
|
13
|
+
return result.discard unless instance["body"].value
|
14
|
+
|
15
|
+
media_type = instance["headers"]&.[]("Content-Type")&.split(";")&.first&.strip&.downcase
|
16
|
+
media_type_object, matched_media_type = find_media_type(media_type)
|
17
|
+
|
18
|
+
return result.failure("Media type #{media_type} not found") unless media_type_object
|
19
|
+
|
20
|
+
result.annotate(matched_media_type)
|
21
|
+
result.call(instance, matched_media_type) do |media_type_result|
|
22
|
+
media_type_object.evaluate(instance["body"], media_type_result)
|
23
|
+
|
24
|
+
result.failure("Invalid content") unless media_type_result.passed?
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
# The key is a media type or media type range and the value describes it.
|
31
|
+
# For requests that match multiple keys, only the most specific key is applicable.
|
32
|
+
# e.g. text/plain overrides text/*
|
33
|
+
def find_media_type(media_type)
|
34
|
+
matched_media_type =
|
35
|
+
if json.key?(media_type)
|
36
|
+
media_type
|
37
|
+
elsif media_type &&
|
38
|
+
(key = "#{media_type.split("/").first}/*") &&
|
39
|
+
json.key?(key)
|
40
|
+
key
|
41
|
+
elsif json.key?("*/*")
|
42
|
+
"*/*"
|
43
|
+
end
|
44
|
+
|
45
|
+
[json[matched_media_type], matched_media_type]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Skooma
|
4
|
+
module Objects
|
5
|
+
class Response
|
6
|
+
module Keywords
|
7
|
+
class Headers < JSONSkooma::Keywords::Base
|
8
|
+
self.key = "headers"
|
9
|
+
self.value_schema = :object_of_schemas
|
10
|
+
self.schema_value_class = Objects::Header
|
11
|
+
|
12
|
+
def evaluate(instance, result)
|
13
|
+
errors = []
|
14
|
+
json.each do |key, schema|
|
15
|
+
next if ignored_key?(key)
|
16
|
+
|
17
|
+
result.call(instance["headers"], key) do |subresult|
|
18
|
+
schema.evaluate(instance["headers"][key], subresult)
|
19
|
+
|
20
|
+
errors << key unless subresult.passed?
|
21
|
+
end
|
22
|
+
end
|
23
|
+
return if errors.empty?
|
24
|
+
|
25
|
+
result.failure("The following headers are invalid: #{errors}")
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def ignored_key?(key)
|
31
|
+
%w[accept content-type authorization].include?(key.downcase)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Skooma
|
4
|
+
module Objects
|
5
|
+
# Describes a single response from an API Operation.
|
6
|
+
# https://spec.openapis.org/oas/v3.1.0#responseObject
|
7
|
+
class Response < RefBase
|
8
|
+
def kw_classes
|
9
|
+
[
|
10
|
+
Base::Keywords::Description,
|
11
|
+
Keywords::Headers,
|
12
|
+
Keywords::Content,
|
13
|
+
Keywords::Links
|
14
|
+
]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Skooma
|
2
|
+
module OutputFormat
|
3
|
+
class << self
|
4
|
+
def call(result, **_options)
|
5
|
+
return {"valid" => true} if result.valid?
|
6
|
+
|
7
|
+
node_data(result, true)
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def node_data(node, first = false)
|
13
|
+
data = {
|
14
|
+
"instanceLocation" => node.instance.path.to_s,
|
15
|
+
"relativeKeywordLocation" => node.relative_path.to_s,
|
16
|
+
"keywordLocation" => node.path.to_s
|
17
|
+
}
|
18
|
+
|
19
|
+
child_data = node.children.filter_map do |_, child|
|
20
|
+
node_data(child) unless child.valid?
|
21
|
+
end
|
22
|
+
|
23
|
+
if first || child_data.length > 1
|
24
|
+
data["errors"] = child_data
|
25
|
+
elsif child_data.length == 1
|
26
|
+
data = child_data[0]
|
27
|
+
elsif node.error
|
28
|
+
data["error"] = node.error
|
29
|
+
end
|
30
|
+
|
31
|
+
data
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/skooma/rspec.rb
ADDED
@@ -0,0 +1,166 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "yaml"
|
4
|
+
|
5
|
+
module Skooma
|
6
|
+
# RSpec matchers for OpenAPI schema validation
|
7
|
+
# @example
|
8
|
+
# require "skooma/rspec"
|
9
|
+
# # ...
|
10
|
+
# RSpec.configure do |config|
|
11
|
+
# # ...
|
12
|
+
# config.include Skooma::RSpec[Rails.root.join("docs", "openapi.yml")], type: :request
|
13
|
+
# end
|
14
|
+
class RSpec < Module
|
15
|
+
class << self
|
16
|
+
alias_method :[], :new
|
17
|
+
end
|
18
|
+
|
19
|
+
module Mapper
|
20
|
+
PLAIN_HEADERS = %w[CONTENT_LENGTH CONTENT_TYPE].freeze
|
21
|
+
REGEXP_HTTP = /^HTTP_/.freeze
|
22
|
+
|
23
|
+
def mapped_response(response: true, request: true)
|
24
|
+
result = {
|
25
|
+
"method" => env["REQUEST_METHOD"].downcase,
|
26
|
+
"path" => env["PATH_INFO"]
|
27
|
+
}
|
28
|
+
|
29
|
+
if response
|
30
|
+
result["response"] = {
|
31
|
+
"status" => response_data[0],
|
32
|
+
"headers" => response_data[1],
|
33
|
+
"body" => response_data[2]
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
if request
|
38
|
+
result["request"] = {
|
39
|
+
"query" => env["rack.request.query_string"] || env["QUERY_STRING"],
|
40
|
+
"headers" => env.select { |k, _| k.start_with?("HTTP_") || PLAIN_HEADERS.include?(k) }.transform_keys { |k| k.sub(REGEXP_HTTP, "").split("_").map(&:capitalize).join("-") },
|
41
|
+
"body" => env["RAW_POST_DATA"]
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
result
|
46
|
+
end
|
47
|
+
|
48
|
+
def env
|
49
|
+
request.env
|
50
|
+
end
|
51
|
+
|
52
|
+
def response_data
|
53
|
+
[response.status, response.headers.to_h, response.body]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def initialize(openapi_path, base_uri: "https://skoomarb.dev/")
|
58
|
+
super()
|
59
|
+
|
60
|
+
pathname = Pathname.new(openapi_path)
|
61
|
+
|
62
|
+
registry = Skooma.create_registry
|
63
|
+
registry.add_source(
|
64
|
+
base_uri,
|
65
|
+
JSONSkooma::Sources::Local.new(pathname.dirname.to_s)
|
66
|
+
)
|
67
|
+
schema = registry.schema(URI.parse("#{base_uri}#{pathname.basename}"), schema_class: Skooma::Objects::OpenAPI)
|
68
|
+
|
69
|
+
include Mapper
|
70
|
+
|
71
|
+
define_method :skooma_openapi_schema do
|
72
|
+
schema
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
::RSpec::Matchers.define(:conform_schema) do |expected_status|
|
78
|
+
match do
|
79
|
+
next false unless response.status == expected_status
|
80
|
+
|
81
|
+
@result = skooma_openapi_schema.evaluate(mapped_response)
|
82
|
+
@result.valid?
|
83
|
+
end
|
84
|
+
|
85
|
+
description do
|
86
|
+
"conform schema with #{expected_status} response code"
|
87
|
+
end
|
88
|
+
|
89
|
+
failure_message do
|
90
|
+
<<~MSG
|
91
|
+
ENV:
|
92
|
+
#{PP.pp(mapped_response, +"")}
|
93
|
+
|
94
|
+
Validation Result:
|
95
|
+
#{@result ? PP.pp(@result.output(:skooma), +"") : "Expected #{expected_status} status code"}
|
96
|
+
MSG
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
::RSpec::Matchers.define(:conform_request_schema) do
|
101
|
+
match do
|
102
|
+
@result = skooma_openapi_schema.evaluate(mapped_response(response: false))
|
103
|
+
@result.valid?
|
104
|
+
end
|
105
|
+
|
106
|
+
description do
|
107
|
+
"conform request schema"
|
108
|
+
end
|
109
|
+
|
110
|
+
failure_message do
|
111
|
+
<<~MSG
|
112
|
+
ENV:
|
113
|
+
#{PP.pp(mapped_response, +"")}
|
114
|
+
|
115
|
+
Validation Result:
|
116
|
+
#{@result ? PP.pp(@result.output(:skooma), +"") : "Expected #{expected_status} status code"}
|
117
|
+
MSG
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
::RSpec::Matchers.define(:conform_response_schema) do |expected_status|
|
122
|
+
match do
|
123
|
+
next false unless response.status == expected_status
|
124
|
+
|
125
|
+
@result = skooma_openapi_schema.evaluate(mapped_response(request: false))
|
126
|
+
@result.valid?
|
127
|
+
end
|
128
|
+
|
129
|
+
description do
|
130
|
+
"conform response schema with #{expected_status} response code"
|
131
|
+
end
|
132
|
+
|
133
|
+
failure_message do
|
134
|
+
<<~MSG
|
135
|
+
ENV:
|
136
|
+
#{PP.pp(mapped_response, +"")}
|
137
|
+
|
138
|
+
Validation Result:
|
139
|
+
#{@result ? PP.pp(@result.output(:skooma), +"") : "Expected #{expected_status} status code"}
|
140
|
+
MSG
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
::RSpec::Matchers.define(:be_valid_document) do
|
145
|
+
match do |actual|
|
146
|
+
@actual = actual
|
147
|
+
next false unless comparable?
|
148
|
+
|
149
|
+
@result = actual.validate
|
150
|
+
@result.valid?
|
151
|
+
end
|
152
|
+
|
153
|
+
description { "be a valid OpenAPI document" }
|
154
|
+
|
155
|
+
failure_message do
|
156
|
+
next "expected value to be an OpenAPI object" unless comparable?
|
157
|
+
|
158
|
+
pretty_output = PP.pp(@result.output(:detailed), +"")
|
159
|
+
"must valid against OpenAPI specification:\n#{pretty_output}"
|
160
|
+
end
|
161
|
+
|
162
|
+
def comparable?
|
163
|
+
actual.is_a?(Skooma::Objects::OpenAPI)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Skooma
|
2
|
+
module Validators
|
3
|
+
class Double < JSONSkooma::Validators::Base
|
4
|
+
self.instance_types = "number"
|
5
|
+
|
6
|
+
def call(instance)
|
7
|
+
return if instance.value.is_a?(::Float)
|
8
|
+
|
9
|
+
raise JSONSkooma::Validators::FormatError, "must be a valid double"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Skooma
|
2
|
+
module Validators
|
3
|
+
class Float < JSONSkooma::Validators::Base
|
4
|
+
self.instance_types = "number"
|
5
|
+
|
6
|
+
def call(instance)
|
7
|
+
return if instance.value.is_a?(::Float)
|
8
|
+
|
9
|
+
raise JSONSkooma::Validators::FormatError, "must be a valid float"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Skooma
|
2
|
+
module Validators
|
3
|
+
class Int32 < JSONSkooma::Validators::Base
|
4
|
+
def self.assert?(instance)
|
5
|
+
instance.type == "number" && instance == instance.to_i
|
6
|
+
end
|
7
|
+
|
8
|
+
def call(instance)
|
9
|
+
return if instance.value.bit_length <= 32
|
10
|
+
|
11
|
+
raise JSONSkooma::Validators::FormatError, "must be a valid int32"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Skooma
|
2
|
+
module Validators
|
3
|
+
class Int64 < JSONSkooma::Validators::Base
|
4
|
+
def self.assert?(instance)
|
5
|
+
instance.type == "number" && instance == instance.to_i
|
6
|
+
end
|
7
|
+
|
8
|
+
def call(instance)
|
9
|
+
return if instance.value.bit_length <= 64
|
10
|
+
|
11
|
+
raise JSONSkooma::Validators::FormatError, "must be a valid int64"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/lib/skooma.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "json_skooma"
|
4
|
+
require "zeitwerk"
|
5
|
+
|
6
|
+
require_relative "skooma/inflector"
|
7
|
+
|
8
|
+
loader = Zeitwerk::Loader.for_gem
|
9
|
+
loader.inflector = Skooma::Inflector.new
|
10
|
+
loader.setup
|
11
|
+
|
12
|
+
module Skooma
|
13
|
+
DATA_DIR = File.join(__dir__, "..", "data")
|
14
|
+
REGISTRY_NAME = "skooma_registry"
|
15
|
+
|
16
|
+
class Error < StandardError; end
|
17
|
+
|
18
|
+
JSONSkooma.register_dialect("oas-3.1", Dialects::OAS31)
|
19
|
+
JSONSkooma::Formatters.register :skooma, OutputFormat
|
20
|
+
|
21
|
+
class << self
|
22
|
+
def create_registry
|
23
|
+
JSONSkooma.create_registry("2020-12", "oas-3.1", name: REGISTRY_NAME, assert_formats: true)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|