sober_swag 0.21.0 → 0.22.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/.rubocop.yml +1 -0
- data/CHANGELOG.md +5 -0
- data/bin/console +30 -10
- data/docs/reporting.md +190 -0
- data/example/Gemfile +2 -2
- data/example/Gemfile.lock +92 -101
- data/example/app/controllers/application_controller.rb +4 -0
- data/example/app/controllers/people_controller.rb +44 -28
- data/example/app/output_objects/identified_output.rb +7 -0
- data/example/app/output_objects/person_output_object.rb +37 -11
- data/example/app/output_objects/post_output_object.rb +0 -4
- data/example/app/output_objects/reporting_post_output.rb +18 -0
- data/example/bin/rspec +29 -0
- data/example/spec/requests/people/create_spec.rb +3 -2
- data/example/spec/requests/people/index_spec.rb +1 -1
- data/lib/sober_swag/compiler/path.rb +3 -1
- data/lib/sober_swag/compiler.rb +58 -12
- data/lib/sober_swag/controller/route.rb +44 -8
- data/lib/sober_swag/controller.rb +18 -5
- data/lib/sober_swag/reporting/compiler.rb +39 -0
- data/lib/sober_swag/reporting/input/base.rb +11 -0
- data/lib/sober_swag/reporting/input/bool.rb +19 -0
- data/lib/sober_swag/reporting/input/converting/bool.rb +24 -0
- data/lib/sober_swag/reporting/input/converting/date.rb +30 -0
- data/lib/sober_swag/reporting/input/converting/date_time.rb +28 -0
- data/lib/sober_swag/reporting/input/converting/decimal.rb +24 -0
- data/lib/sober_swag/reporting/input/converting/integer.rb +19 -0
- data/lib/sober_swag/reporting/input/converting.rb +16 -0
- data/lib/sober_swag/reporting/input/defer.rb +29 -0
- data/lib/sober_swag/reporting/input/described.rb +38 -0
- data/lib/sober_swag/reporting/input/dictionary.rb +37 -0
- data/lib/sober_swag/reporting/input/either.rb +51 -0
- data/lib/sober_swag/reporting/input/enum.rb +44 -0
- data/lib/sober_swag/reporting/input/format.rb +39 -0
- data/lib/sober_swag/reporting/input/interface.rb +87 -0
- data/lib/sober_swag/reporting/input/list.rb +44 -0
- data/lib/sober_swag/reporting/input/mapped.rb +36 -0
- data/lib/sober_swag/reporting/input/merge_objects.rb +72 -0
- data/lib/sober_swag/reporting/input/null.rb +34 -0
- data/lib/sober_swag/reporting/input/number.rb +19 -0
- data/lib/sober_swag/reporting/input/object/property.rb +53 -0
- data/lib/sober_swag/reporting/input/object.rb +100 -0
- data/lib/sober_swag/reporting/input/pattern.rb +46 -0
- data/lib/sober_swag/reporting/input/referenced.rb +38 -0
- data/lib/sober_swag/reporting/input/struct.rb +271 -0
- data/lib/sober_swag/reporting/input/text.rb +42 -0
- data/lib/sober_swag/reporting/input.rb +54 -0
- data/lib/sober_swag/reporting/invalid_schema_error.rb +21 -0
- data/lib/sober_swag/reporting/output/base.rb +25 -0
- data/lib/sober_swag/reporting/output/bool.rb +25 -0
- data/lib/sober_swag/reporting/output/defer.rb +69 -0
- data/lib/sober_swag/reporting/output/described.rb +42 -0
- data/lib/sober_swag/reporting/output/dictionary.rb +46 -0
- data/lib/sober_swag/reporting/output/interface.rb +83 -0
- data/lib/sober_swag/reporting/output/list.rb +54 -0
- data/lib/sober_swag/reporting/output/merge_objects.rb +97 -0
- data/lib/sober_swag/reporting/output/null.rb +25 -0
- data/lib/sober_swag/reporting/output/number.rb +25 -0
- data/lib/sober_swag/reporting/output/object/property.rb +45 -0
- data/lib/sober_swag/reporting/output/object.rb +54 -0
- data/lib/sober_swag/reporting/output/partitioned.rb +77 -0
- data/lib/sober_swag/reporting/output/pattern.rb +50 -0
- data/lib/sober_swag/reporting/output/referenced.rb +42 -0
- data/lib/sober_swag/reporting/output/struct.rb +262 -0
- data/lib/sober_swag/reporting/output/text.rb +25 -0
- data/lib/sober_swag/reporting/output/via_map.rb +67 -0
- data/lib/sober_swag/reporting/output/viewed.rb +72 -0
- data/lib/sober_swag/reporting/output.rb +54 -0
- data/lib/sober_swag/reporting/report/base.rb +57 -0
- data/lib/sober_swag/reporting/report/either.rb +36 -0
- data/lib/sober_swag/reporting/report/error.rb +15 -0
- data/lib/sober_swag/reporting/report/list.rb +28 -0
- data/lib/sober_swag/reporting/report/merged_object.rb +25 -0
- data/lib/sober_swag/reporting/report/object.rb +29 -0
- data/lib/sober_swag/reporting/report/output.rb +14 -0
- data/lib/sober_swag/reporting/report/value.rb +28 -0
- data/lib/sober_swag/reporting/report.rb +16 -0
- data/lib/sober_swag/reporting.rb +11 -0
- data/lib/sober_swag/version.rb +1 -1
- data/lib/sober_swag.rb +1 -0
- metadata +65 -2
@@ -0,0 +1,30 @@
|
|
1
|
+
module SoberSwag
|
2
|
+
module Reporting
|
3
|
+
module Input
|
4
|
+
module Converting
|
5
|
+
##
|
6
|
+
# Convert via a date.
|
7
|
+
#
|
8
|
+
# Note: unlike the swagger spec, we first try to convert
|
9
|
+
# rfc8601, then try rfc3339.
|
10
|
+
Date = (
|
11
|
+
SoberSwag::Reporting::Input::Text
|
12
|
+
.new
|
13
|
+
.mapped { |str|
|
14
|
+
begin
|
15
|
+
::Date.rfc3339(str)
|
16
|
+
rescue ArgumentError
|
17
|
+
Report::Value.new(['was not an RFC 3339 date string'])
|
18
|
+
end
|
19
|
+
} |
|
20
|
+
SoberSwag::Reporting::Input::Text
|
21
|
+
.new
|
22
|
+
.mapped do |str|
|
23
|
+
::Date.iso8601(str)
|
24
|
+
rescue ArgumentError
|
25
|
+
Report::Value.new(['was not an ISO8601 date string'])
|
26
|
+
end).format('date')
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module SoberSwag
|
2
|
+
module Reporting
|
3
|
+
module Input
|
4
|
+
module Converting
|
5
|
+
##
|
6
|
+
# Convert via a date.
|
7
|
+
DateTime =
|
8
|
+
SoberSwag::Reporting::Input::Text
|
9
|
+
.new
|
10
|
+
.mapped { |str|
|
11
|
+
begin
|
12
|
+
::DateTime.rfc3339(str)
|
13
|
+
rescue ArgumentError
|
14
|
+
Report::Value.new(['was not an RFC 3339 date-time string'])
|
15
|
+
end
|
16
|
+
}.or(
|
17
|
+
SoberSwag::Reporting::Input::Text
|
18
|
+
.new
|
19
|
+
.mapped do |str|
|
20
|
+
::DateTime.iso8601(str)
|
21
|
+
rescue ArgumentError
|
22
|
+
Report::Value.new(['was not an ISO8601 date-time string'])
|
23
|
+
end
|
24
|
+
).format('date-time')
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module SoberSwag
|
2
|
+
module Reporting
|
3
|
+
module Input
|
4
|
+
module Converting
|
5
|
+
##
|
6
|
+
# Parse a decimal.
|
7
|
+
Decimal =
|
8
|
+
(SoberSwag::Reporting::Input::Number.new.mapped(&:to_d).format(:decimal) |
|
9
|
+
SoberSwag::Reporting::Input::Text
|
10
|
+
.new
|
11
|
+
.format('decimal')
|
12
|
+
.mapped do |v|
|
13
|
+
BigDecimal(v)
|
14
|
+
rescue ArgumentError
|
15
|
+
Report::Value.new('was not a decimal')
|
16
|
+
end).described(<<~MARKDOWN).referenced('SoberSwag.Converting.Decimal')
|
17
|
+
Decimal formatted input.
|
18
|
+
Will either convert a JSON number to a decimal, or accept a string representation.
|
19
|
+
The string representation allows for greater precision.
|
20
|
+
MARKDOWN
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module SoberSwag
|
2
|
+
module Reporting
|
3
|
+
module Input
|
4
|
+
module Converting
|
5
|
+
Integer =
|
6
|
+
(SoberSwag::Reporting::Input.number.format('integer').mapped(&:to_i)) |
|
7
|
+
(SoberSwag::Reporting::Input.text.format('integer').mapped do |v|
|
8
|
+
Integer(v)
|
9
|
+
rescue ArgumentError
|
10
|
+
Report::Value.new(['was not an integer string'])
|
11
|
+
end).described(<<~MARKDOWN).referenced('SoberSwag.Converting.Integer')
|
12
|
+
Integer formatted input.
|
13
|
+
|
14
|
+
With either convert a JSON number to an integer, or accept a string representation of an integer.
|
15
|
+
MARKDOWN
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module SoberSwag
|
2
|
+
module Reporting
|
3
|
+
module Input
|
4
|
+
##
|
5
|
+
# Namespace for things that can do conversion.
|
6
|
+
# These are really just compound types that kinda look nice.
|
7
|
+
module Converting
|
8
|
+
autoload(:Decimal, 'sober_swag/reporting/input/converting/decimal')
|
9
|
+
autoload(:Date, 'sober_swag/reporting/input/converting/date')
|
10
|
+
autoload(:DateTime, 'sober_swag/reporting/input/converting/date_time')
|
11
|
+
autoload(:Bool, 'sober_swag/reporting/input/converting/bool')
|
12
|
+
autoload(:Integer, 'sober_swag/reporting/input/converting/integer')
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module SoberSwag
|
2
|
+
module Reporting
|
3
|
+
module Input
|
4
|
+
##
|
5
|
+
# Resolve circular references by deferring the loading of an input.
|
6
|
+
class Defer < Base
|
7
|
+
def initialize(other_lazy)
|
8
|
+
@other_lazy = other_lazy
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_reader :other_lazy
|
12
|
+
|
13
|
+
def other
|
14
|
+
return @other if defined?(@other)
|
15
|
+
|
16
|
+
@other = other_lazy.call
|
17
|
+
end
|
18
|
+
|
19
|
+
def call(input)
|
20
|
+
other.call(input)
|
21
|
+
end
|
22
|
+
|
23
|
+
def swagger_schema
|
24
|
+
other.swagger_schema
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module SoberSwag
|
2
|
+
module Reporting
|
3
|
+
module Input
|
4
|
+
##
|
5
|
+
# Node for things with descriptions.
|
6
|
+
# This describes the *type*, not the *object key*.
|
7
|
+
class Described < Base
|
8
|
+
def initialize(input, description)
|
9
|
+
@input = input
|
10
|
+
@description = description
|
11
|
+
end
|
12
|
+
|
13
|
+
##
|
14
|
+
# @return [Interface] base input
|
15
|
+
attr_reader :input
|
16
|
+
|
17
|
+
##
|
18
|
+
# @return [String] description of input
|
19
|
+
attr_reader :description
|
20
|
+
|
21
|
+
def call(value)
|
22
|
+
input.call(value)
|
23
|
+
end
|
24
|
+
|
25
|
+
def swagger_schema
|
26
|
+
val, other = input.swagger_schema
|
27
|
+
merged =
|
28
|
+
if val.key?(:$ref)
|
29
|
+
{ allOf: [val] }
|
30
|
+
else
|
31
|
+
val
|
32
|
+
end.merge(description: description)
|
33
|
+
[merged, other]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module SoberSwag
|
2
|
+
module Reporting
|
3
|
+
module Input
|
4
|
+
##
|
5
|
+
# Dictionary types: string keys, something else as a value.
|
6
|
+
class Dictionary < Base
|
7
|
+
def self.of(input_type)
|
8
|
+
new(input_type)
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(value_input)
|
12
|
+
@value_input = value_input
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_reader :value_input
|
16
|
+
|
17
|
+
def call(value)
|
18
|
+
return Report::Base.new(['was not an object']) unless value.is_a?(Hash)
|
19
|
+
|
20
|
+
bad, good = value.map { |k, v|
|
21
|
+
[k, value_input.call(v)]
|
22
|
+
}.compact.partition { |(_, v)| v.is_a?(Report::Base) }
|
23
|
+
|
24
|
+
return Report::Object.new(bad.to_h) if bad.any?
|
25
|
+
|
26
|
+
good.to_h
|
27
|
+
end
|
28
|
+
|
29
|
+
def swagger_schema
|
30
|
+
schema, found = value_input.swagger_schema
|
31
|
+
|
32
|
+
[{ type: :object, additionalProperties: schema }, found]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module SoberSwag
|
2
|
+
module Reporting
|
3
|
+
module Input
|
4
|
+
##
|
5
|
+
# Parses either one input, or another.
|
6
|
+
# Left-biased.
|
7
|
+
class Either < Base
|
8
|
+
##
|
9
|
+
# @param lhs [Base] an input we will try first
|
10
|
+
# @param rhs [Base] an input we will try second
|
11
|
+
def initialize(lhs, rhs)
|
12
|
+
@lhs = lhs
|
13
|
+
@rhs = rhs
|
14
|
+
end
|
15
|
+
|
16
|
+
##
|
17
|
+
# @return [Base] parser for LHS
|
18
|
+
attr_reader :lhs
|
19
|
+
##
|
20
|
+
# @return [Base] parser for RHS
|
21
|
+
attr_reader :rhs
|
22
|
+
|
23
|
+
def call(value)
|
24
|
+
maybe_lhs = lhs.call(value)
|
25
|
+
|
26
|
+
return maybe_lhs unless maybe_lhs.is_a?(Report::Base)
|
27
|
+
|
28
|
+
maybe_rhs = rhs.call(value)
|
29
|
+
|
30
|
+
return maybe_rhs unless maybe_rhs.is_a?(Report::Base)
|
31
|
+
|
32
|
+
Report::Either.new(maybe_lhs, maybe_rhs)
|
33
|
+
end
|
34
|
+
|
35
|
+
def swagger_schema
|
36
|
+
lhs_val, lhs_set = lhs.swagger_schema
|
37
|
+
rhs_val, rhs_set = rhs.swagger_schema
|
38
|
+
|
39
|
+
val = { oneOf: defs_for(lhs_val) + defs_for(rhs_val) }
|
40
|
+
[val, lhs_set.merge(rhs_set)]
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def defs_for(schema)
|
46
|
+
schema[:oneOf] || [schema]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module SoberSwag
|
2
|
+
module Reporting
|
3
|
+
module Input
|
4
|
+
##
|
5
|
+
# Specify that a value must be included in a list of possible values.
|
6
|
+
class Enum < Base
|
7
|
+
def initialize(input, values)
|
8
|
+
@input = input
|
9
|
+
@values = values
|
10
|
+
end
|
11
|
+
|
12
|
+
##
|
13
|
+
# @return [Interface] base type
|
14
|
+
attr_reader :input
|
15
|
+
|
16
|
+
##
|
17
|
+
# @return [Array<String>] acceptable types
|
18
|
+
attr_reader :values
|
19
|
+
|
20
|
+
def call(value)
|
21
|
+
inner = input.call(value)
|
22
|
+
|
23
|
+
return inner if inner.is_a?(Report::Base)
|
24
|
+
|
25
|
+
return Report::Value.new(['was not an acceptable enum member']) unless values.include?(inner)
|
26
|
+
|
27
|
+
inner
|
28
|
+
end
|
29
|
+
|
30
|
+
def swagger_schema
|
31
|
+
schema, found = input.swagger_schema
|
32
|
+
|
33
|
+
merged =
|
34
|
+
if schema.key?(:$ref)
|
35
|
+
{ allOf: [schema] }
|
36
|
+
else
|
37
|
+
schema
|
38
|
+
end.merge(enum: values)
|
39
|
+
[merged, found]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module SoberSwag
|
2
|
+
module Reporting
|
3
|
+
module Input
|
4
|
+
##
|
5
|
+
# Specify that something must match a particular format.
|
6
|
+
# Note: said format is just a string.
|
7
|
+
class Format < Base
|
8
|
+
def initialize(input, format)
|
9
|
+
@input = input
|
10
|
+
@format = format
|
11
|
+
end
|
12
|
+
|
13
|
+
##
|
14
|
+
# @return [Interface]
|
15
|
+
attr_reader :input
|
16
|
+
|
17
|
+
##
|
18
|
+
# @return [String]
|
19
|
+
attr_reader :format
|
20
|
+
|
21
|
+
def call(object)
|
22
|
+
input.call(object)
|
23
|
+
end
|
24
|
+
|
25
|
+
def swagger_schema
|
26
|
+
schema, found = input.swagger_schema
|
27
|
+
|
28
|
+
merged =
|
29
|
+
if schema.key?(:$ref)
|
30
|
+
{ allOf: [schema] }
|
31
|
+
else
|
32
|
+
schema
|
33
|
+
end.merge(format: format)
|
34
|
+
[merged, found]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module SoberSwag
|
2
|
+
module Reporting
|
3
|
+
module Input
|
4
|
+
##
|
5
|
+
# Module for interface methods.
|
6
|
+
module Interface
|
7
|
+
##
|
8
|
+
# Make a new input that is either this type or the argument.
|
9
|
+
#
|
10
|
+
# @argument other [Interface] other input type
|
11
|
+
# @return [Either] this input, or some other input.
|
12
|
+
def or(other)
|
13
|
+
Either.new(self, other)
|
14
|
+
end
|
15
|
+
|
16
|
+
##
|
17
|
+
# @see {#or}
|
18
|
+
def |(other)
|
19
|
+
Either.new(self, other)
|
20
|
+
end
|
21
|
+
|
22
|
+
##
|
23
|
+
# This, or null.
|
24
|
+
#
|
25
|
+
# @return [Either] an either type of this or nil.
|
26
|
+
def optional
|
27
|
+
self | Null.new
|
28
|
+
end
|
29
|
+
|
30
|
+
##
|
31
|
+
# A list of this input.
|
32
|
+
#
|
33
|
+
# @return [List] the new input.
|
34
|
+
def list
|
35
|
+
List.new(self)
|
36
|
+
end
|
37
|
+
|
38
|
+
def referenced(name)
|
39
|
+
Referenced.new(self, name)
|
40
|
+
end
|
41
|
+
|
42
|
+
def format(format)
|
43
|
+
Format.new(self, format)
|
44
|
+
end
|
45
|
+
|
46
|
+
def described(desc)
|
47
|
+
Described.new(self, desc)
|
48
|
+
end
|
49
|
+
|
50
|
+
def enum(*cases)
|
51
|
+
Enum.new(self, cases)
|
52
|
+
end
|
53
|
+
|
54
|
+
##
|
55
|
+
# Map a function after this input runs.
|
56
|
+
#
|
57
|
+
# @return [Mapped] the new input.
|
58
|
+
def mapped(&block)
|
59
|
+
Mapped.new(self, block)
|
60
|
+
end
|
61
|
+
|
62
|
+
def call!(value)
|
63
|
+
res = call(value)
|
64
|
+
raise Report::Error.new(res) if res.is_a?(Report::Base) # rubocop:disable Style/RaiseArgs
|
65
|
+
|
66
|
+
res
|
67
|
+
end
|
68
|
+
|
69
|
+
def swagger_path_schema
|
70
|
+
raise InvalidSchemaError::InvalidForPathError.new(self) # rubocop:disable Style/RaiseArgs
|
71
|
+
end
|
72
|
+
|
73
|
+
def swagger_query_schema
|
74
|
+
raise InvalidSchemaError::InvalidForQueryError.new(self) # rubocop:disable Style/RaiseArgs
|
75
|
+
end
|
76
|
+
|
77
|
+
def add_schema_key(base, addition)
|
78
|
+
if base.key?(:$ref)
|
79
|
+
{ allOf: [base] }.merge(addition)
|
80
|
+
else
|
81
|
+
base.merge(addition)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module SoberSwag
|
2
|
+
module Reporting
|
3
|
+
module Input
|
4
|
+
##
|
5
|
+
# Class to parse an array, where each element has the same type.
|
6
|
+
#
|
7
|
+
# Called List to avoid name conflicts.
|
8
|
+
class List < Base
|
9
|
+
##
|
10
|
+
# @param element [Base] the parser for elements
|
11
|
+
def initialize(element)
|
12
|
+
@element = element
|
13
|
+
end
|
14
|
+
|
15
|
+
##
|
16
|
+
# @return [Base] the parser for elements
|
17
|
+
attr_reader :element
|
18
|
+
|
19
|
+
def call(value)
|
20
|
+
return Report::Value.new(['was not an array']) unless value.is_a?(Array)
|
21
|
+
|
22
|
+
# obtain a hash of indexes => errors
|
23
|
+
errs = {}
|
24
|
+
# yes, side effects in a map are evil, but we avoid traversal twice
|
25
|
+
mapped = value.map.with_index do |item, idx|
|
26
|
+
element.call(item).tap { |e| errs[idx] = e if e.is_a?(Report::Base) }
|
27
|
+
end
|
28
|
+
|
29
|
+
if errs.any?
|
30
|
+
Report::List.new(errs)
|
31
|
+
else
|
32
|
+
mapped
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def swagger_schema
|
37
|
+
schema, found = element.swagger_schema
|
38
|
+
|
39
|
+
[{ type: 'list', items: schema }, found]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module SoberSwag
|
2
|
+
module Reporting
|
3
|
+
module Input
|
4
|
+
##
|
5
|
+
# Apply a mapping function over an input.
|
6
|
+
class Mapped < Base
|
7
|
+
##
|
8
|
+
# @param mapper [#call] the mapping function
|
9
|
+
# @param input [Base] the base input
|
10
|
+
def initialize(input, mapper)
|
11
|
+
@mapper = mapper
|
12
|
+
@input = input
|
13
|
+
end
|
14
|
+
|
15
|
+
##
|
16
|
+
# @return [#call] mapping function
|
17
|
+
attr_reader :mapper
|
18
|
+
##
|
19
|
+
# @return [Base] base input
|
20
|
+
attr_reader :input
|
21
|
+
|
22
|
+
def call(value)
|
23
|
+
val = input.call(value)
|
24
|
+
|
25
|
+
return val if val.is_a?(Report::Base)
|
26
|
+
|
27
|
+
mapper.call(val)
|
28
|
+
end
|
29
|
+
|
30
|
+
def swagger_schema
|
31
|
+
input.swagger_schema
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module SoberSwag
|
2
|
+
module Reporting
|
3
|
+
module Input
|
4
|
+
##
|
5
|
+
# Merge two object types together, in an allof stype relationship
|
6
|
+
class MergeObjects < Base
|
7
|
+
def initialize(parent, child)
|
8
|
+
@parent = parent
|
9
|
+
@child = child
|
10
|
+
end
|
11
|
+
|
12
|
+
##
|
13
|
+
# @return [Interface] parent type
|
14
|
+
attr_reader :parent
|
15
|
+
|
16
|
+
##
|
17
|
+
# @return [Interface] child type
|
18
|
+
attr_reader :child
|
19
|
+
|
20
|
+
def call(value)
|
21
|
+
parent_attrs = parent.call(value)
|
22
|
+
|
23
|
+
return parent_attrs if parent_attrs.is_a?(Report::Value)
|
24
|
+
|
25
|
+
# otherwise, object type, so we want to get a full error report
|
26
|
+
|
27
|
+
child_attrs = child.call(value)
|
28
|
+
|
29
|
+
return child_attrs if child_attrs.is_a?(Report::Value)
|
30
|
+
|
31
|
+
merge_results(parent_attrs, child_attrs)
|
32
|
+
end
|
33
|
+
|
34
|
+
def swagger_schema
|
35
|
+
parent_schema, parent_found = parent.swagger_schema
|
36
|
+
child_schema, child_found = child.swagger_schema
|
37
|
+
|
38
|
+
[
|
39
|
+
{
|
40
|
+
allOf: (parent_schema[:allOf] || [parent_schema]) + (child_schema[:allOf] || [child_schema])
|
41
|
+
},
|
42
|
+
parent_found.merge(child_found)
|
43
|
+
]
|
44
|
+
end
|
45
|
+
|
46
|
+
def swagger_path_schema
|
47
|
+
parent.swagger_path_schema + child.swagger_path_schema
|
48
|
+
end
|
49
|
+
|
50
|
+
def swagger_query_schema
|
51
|
+
parent.swagger_query_schema + child.swagger_query_schema
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def merge_results(par, chi) # rubocop:disable Metrics/MethodLength
|
57
|
+
if par.is_a?(Report::Base)
|
58
|
+
if chi.is_a?(Report::Base)
|
59
|
+
Report::MergedObject.new(par, chi)
|
60
|
+
else
|
61
|
+
par
|
62
|
+
end
|
63
|
+
elsif chi.is_a?(Report::Base)
|
64
|
+
chi
|
65
|
+
else
|
66
|
+
par.to_h.merge(chi.to_h)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module SoberSwag
|
2
|
+
module Reporting
|
3
|
+
module Input
|
4
|
+
##
|
5
|
+
# Null input values.
|
6
|
+
# Validates that the input is null.
|
7
|
+
class Null < Base
|
8
|
+
def call(value)
|
9
|
+
return nil if value.nil?
|
10
|
+
|
11
|
+
Report::Value.new(['was not nil'])
|
12
|
+
end
|
13
|
+
|
14
|
+
def hash
|
15
|
+
[self.class.hash, 1].hash
|
16
|
+
end
|
17
|
+
|
18
|
+
def eql?(other)
|
19
|
+
other.class == self.class
|
20
|
+
end
|
21
|
+
|
22
|
+
def <=>(other)
|
23
|
+
eql?(other) ? 0 : nil
|
24
|
+
end
|
25
|
+
|
26
|
+
include Comparable
|
27
|
+
|
28
|
+
def swagger_schema
|
29
|
+
[{ type: 'null' }, {}]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module SoberSwag
|
2
|
+
module Reporting
|
3
|
+
module Input
|
4
|
+
##
|
5
|
+
# Parse some kind of number.
|
6
|
+
class Number < Base
|
7
|
+
def call(input)
|
8
|
+
return Report::Value.new(['is not a number']) unless input.is_a?(Numeric)
|
9
|
+
|
10
|
+
input
|
11
|
+
end
|
12
|
+
|
13
|
+
def swagger_schema
|
14
|
+
[{ type: 'number' }, {}]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|