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
@@ -5,35 +5,40 @@ class PeopleController < ApplicationController
|
|
5
5
|
|
6
6
|
before_action :load_person, only: %i[show update]
|
7
7
|
|
8
|
-
|
9
|
-
|
8
|
+
##
|
9
|
+
# Parameters to create or update a person.
|
10
|
+
class ReportingPersonParams < SoberSwag::Reporting::Input::Struct
|
11
|
+
identifier 'PersonReportingParams'
|
10
12
|
|
11
|
-
attribute :first_name, SoberSwag::
|
12
|
-
attribute :last_name, SoberSwag::
|
13
|
-
attribute? :date_of_birth, SoberSwag::
|
13
|
+
attribute :first_name, SoberSwag::Reporting::Input::Text.new.with_pattern(/.+/)
|
14
|
+
attribute :last_name, SoberSwag::Reporting::Input::Text.new.with_pattern(/.+/)
|
15
|
+
attribute? :date_of_birth, SoberSwag::Reporting::Input::Converting::DateTime.optional
|
14
16
|
end
|
15
17
|
|
16
|
-
|
17
|
-
|
18
|
+
##
|
19
|
+
# Parameters to create a person.
|
20
|
+
class ReportingPersonCreate < SoberSwag::Reporting::Input::Struct
|
21
|
+
identifier 'ReportingPersonCreate'
|
18
22
|
|
19
|
-
attribute
|
20
|
-
attribute? :last_name, SoberSwag::Types::String
|
21
|
-
attribute? :date_of_birth, SoberSwag::Types::Params::DateTime.optional
|
23
|
+
attribute :person, ReportingPersonParams
|
22
24
|
end
|
23
25
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
26
|
+
##
|
27
|
+
# Patch body for a person.
|
28
|
+
class ReportingPersonPatchParams < SoberSwag::Reporting::Input::Struct
|
29
|
+
identifier 'PersonReportingPatchParams'
|
28
30
|
|
29
|
-
|
30
|
-
|
31
|
-
attribute :
|
31
|
+
attribute? :first_name, SoberSwag::Reporting::Input.text.with_pattern(/.+/)
|
32
|
+
attribute? :last_name, SoberSwag::Reporting::Input.text.with_pattern(/.+/)
|
33
|
+
attribute? :date_of_birth, SoberSwag::Reporting::Input::Converting::DateTime.optional
|
32
34
|
end
|
33
35
|
|
34
36
|
define :post, :create, '/people/' do
|
35
|
-
|
37
|
+
summary 'Create a person'
|
38
|
+
|
39
|
+
request_body(ReportingPersonCreate)
|
36
40
|
response(:ok, 'the person created', PersonOutputObject)
|
41
|
+
response(:bad_request, 'the parse errors', SoberSwag::Reporting::Report::Output)
|
37
42
|
response(:unprocessable_entity, 'the validation errors', PersonErrorsOutputObject)
|
38
43
|
tags 'people', 'create'
|
39
44
|
end
|
@@ -47,9 +52,14 @@ class PeopleController < ApplicationController
|
|
47
52
|
end
|
48
53
|
|
49
54
|
define :patch, :update, '/people/{id}' do
|
50
|
-
|
51
|
-
|
55
|
+
summary 'Update a person'
|
56
|
+
|
57
|
+
request_body(reporting: true) do
|
58
|
+
attribute :person, ReportingPersonPatchParams
|
59
|
+
end
|
60
|
+
path_params(reporting: true) { attribute :id, SoberSwag::Reporting::Input::Converting::Integer }
|
52
61
|
response(:ok, 'the person updated', PersonOutputObject)
|
62
|
+
response(:bad_request, 'the parse errors', SoberSwag::Reporting::Report::Output)
|
53
63
|
response(:unprocessable_entity, 'the validation errors', PersonErrorsOutputObject)
|
54
64
|
tags 'people', 'update'
|
55
65
|
end
|
@@ -62,28 +72,34 @@ class PeopleController < ApplicationController
|
|
62
72
|
end
|
63
73
|
|
64
74
|
define :get, :index, '/people/' do
|
65
|
-
|
75
|
+
summary 'List persons'
|
76
|
+
|
77
|
+
query_params(reporting: true) do
|
66
78
|
attribute? :filters do
|
67
|
-
attribute? :first_name,
|
68
|
-
attribute? :last_name,
|
79
|
+
attribute? :first_name, SoberSwag::Reporting::Input.text
|
80
|
+
attribute? :last_name, SoberSwag::Reporting::Input.text
|
69
81
|
end
|
70
|
-
attribute :view,
|
82
|
+
attribute? :view, SoberSwag::Reporting::Input.text.enum('base', 'detail')
|
71
83
|
end
|
72
|
-
response(:ok, 'all the people', PersonOutputObject.
|
84
|
+
response(:ok, 'all the people', PersonOutputObject.list)
|
85
|
+
response(:bad_request, 'the parse errors', SoberSwag::Reporting::Report::Output)
|
73
86
|
tags 'people', 'list'
|
74
87
|
end
|
75
88
|
def index
|
76
89
|
@people = Person.all
|
77
90
|
@people = @people.where('UPPER(first_name) LIKE UPPER(?)', "%#{parsed_query.filters.first_name}%") if parsed_query.filters&.first_name
|
78
91
|
@people = @people.where('UPPER(last_name) LIKE UPPER(?)', "%#{parsed_query.filters.last_name}%") if parsed_query.filters&.last_name
|
79
|
-
respond!(:ok, @people.includes(:posts), serializer_opts: { view: parsed_query.view })
|
92
|
+
respond!(:ok, @people.includes(:posts), serializer_opts: { view: parsed_query.view || :base })
|
80
93
|
end
|
81
94
|
|
82
95
|
define :get, :show, '/people/{id}' do
|
83
|
-
|
84
|
-
|
96
|
+
summary 'Get a single person by id'
|
97
|
+
|
98
|
+
path_params(reporting: true) do
|
99
|
+
attribute :id, SoberSwag::Reporting::Input::Converting::Integer
|
85
100
|
end
|
86
101
|
response(:ok, 'the person requested', PersonOutputObject)
|
102
|
+
response(:bad_request, 'the parse errors', SoberSwag::Reporting::Report::Output)
|
87
103
|
tags 'people', 'show'
|
88
104
|
end
|
89
105
|
def show
|
@@ -1,15 +1,41 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
is a good thing to read!
|
1
|
+
##
|
2
|
+
# Output object that serializes a person.
|
3
|
+
class PersonOutputObject < IdentifiedOutput
|
4
|
+
identifier 'PersonOutput'
|
5
|
+
|
6
|
+
description <<~MARKDOWN
|
7
|
+
Basic as hell serializer for a person.
|
9
8
|
MARKDOWN
|
10
|
-
field :last_name, primitive(:String)
|
11
9
|
|
12
|
-
|
13
|
-
|
10
|
+
field :id, SoberSwag::Reporting::Output::Text.new.via_map(&:to_s)
|
11
|
+
field(
|
12
|
+
:first_name,
|
13
|
+
SoberSwag::Reporting::Output::Text.new,
|
14
|
+
description: <<~MARKDOWN
|
15
|
+
This is the first name of a person.
|
16
|
+
Note that you can't use this as a unique identifier, and you really should understand how names work before using this.
|
17
|
+
[Falsehoods programmers believe about names](https://www.kalzumeus.com/2010/06/17/falsehoods-programmers-believe-about-names/)
|
18
|
+
is a good thing to read!
|
19
|
+
MARKDOWN
|
20
|
+
)
|
21
|
+
field(
|
22
|
+
:last_name, SoberSwag::Reporting::Output::Text.new
|
23
|
+
)
|
24
|
+
|
25
|
+
define_view :detail do
|
26
|
+
field(
|
27
|
+
:initials,
|
28
|
+
SoberSwag::Reporting::Output::Text.new
|
29
|
+
) do |person|
|
30
|
+
[person.first_name[0..0], person.last_name[0..0]].compact.map { |x| "#{x}." }.join(' ')
|
31
|
+
end
|
32
|
+
|
33
|
+
field(
|
34
|
+
:posts,
|
35
|
+
SoberSwag::Reporting::Output::Defer.defer do
|
36
|
+
ReportingPostOutput.view(:base).array
|
37
|
+
end,
|
38
|
+
description: 'all posts this user has made'
|
39
|
+
)
|
14
40
|
end
|
15
41
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
##
|
2
|
+
# A reporting output object for a post.
|
3
|
+
class ReportingPostOutput < SoberSwag::Reporting::Output::Struct
|
4
|
+
identifier 'ReportingPostOutput'
|
5
|
+
|
6
|
+
field :id, SoberSwag::Reporting::Output::Text.new.via_map(&:to_s)
|
7
|
+
field :title, SoberSwag::Reporting::Output::Text.new
|
8
|
+
field :body, SoberSwag::Reporting::Output::Text.new
|
9
|
+
|
10
|
+
define_view :detail do
|
11
|
+
field(
|
12
|
+
:person,
|
13
|
+
SoberSwag::Reporting::Output::Defer.defer do
|
14
|
+
PersonOutputObject.view(:base)
|
15
|
+
end
|
16
|
+
)
|
17
|
+
end
|
18
|
+
end
|
data/example/bin/rspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'rspec' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
require 'pathname'
|
12
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile',
|
13
|
+
Pathname.new(__FILE__).realpath)
|
14
|
+
|
15
|
+
bundle_binstub = File.expand_path('bundle', __dir__)
|
16
|
+
|
17
|
+
if File.file?(bundle_binstub)
|
18
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
19
|
+
load(bundle_binstub)
|
20
|
+
else
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require 'rubygems'
|
27
|
+
require 'bundler/setup'
|
28
|
+
|
29
|
+
load Gem.bin_path('rspec-core', 'rspec')
|
@@ -34,7 +34,8 @@ RSpec.describe 'people controller create', type: :request do
|
|
34
34
|
|
35
35
|
it { should_not be_successful }
|
36
36
|
it { should_not be_server_error }
|
37
|
-
it { should
|
37
|
+
it { should be_bad_request }
|
38
|
+
it { should have_attributes(status: 400) }
|
38
39
|
end
|
39
40
|
|
40
41
|
describe 'the act of requesting' do
|
@@ -46,7 +47,7 @@ RSpec.describe 'people controller create', type: :request do
|
|
46
47
|
describe 'the response body' do
|
47
48
|
subject { request && response && JSON.parse(response.body) }
|
48
49
|
|
49
|
-
it { should have_key('first_name') }
|
50
|
+
it { should have_key('$.person.first_name') }
|
50
51
|
end
|
51
52
|
end
|
52
53
|
end
|
@@ -51,7 +51,9 @@ module SoberSwag
|
|
51
51
|
description: route.response_descriptions[status],
|
52
52
|
content: {
|
53
53
|
'application/json': {
|
54
|
-
schema: compiler.response_for(
|
54
|
+
schema: compiler.response_for(
|
55
|
+
serializer.respond_to?(:swagger_schema) ? serializer : serializer.type
|
56
|
+
)
|
55
57
|
}
|
56
58
|
}
|
57
59
|
}
|
data/lib/sober_swag/compiler.rb
CHANGED
@@ -13,6 +13,7 @@ module SoberSwag
|
|
13
13
|
def initialize
|
14
14
|
@types = Set.new
|
15
15
|
@paths = Paths.new
|
16
|
+
@reporting_types = SoberSwag::Reporting::Compiler::Schema.new
|
16
17
|
end
|
17
18
|
|
18
19
|
##
|
@@ -28,6 +29,10 @@ module SoberSwag
|
|
28
29
|
}
|
29
30
|
end
|
30
31
|
|
32
|
+
##
|
33
|
+
# @return [SoberSwag::Reporting::Compiler::Schema]
|
34
|
+
attr_reader :reporting_types
|
35
|
+
|
31
36
|
##
|
32
37
|
# Add a path to be compiled.
|
33
38
|
# @param route [SoberSwag::Controller::Route] the route to add.
|
@@ -41,7 +46,9 @@ module SoberSwag
|
|
41
46
|
#
|
42
47
|
# @return [Hash]
|
43
48
|
def object_schemas
|
44
|
-
@types.map { |v| [v.ref_name, v.object_schema] }.to_h
|
49
|
+
@types.map { |v| [v.ref_name, v.object_schema] }.to_h.merge(
|
50
|
+
reporting_types.references
|
51
|
+
)
|
45
52
|
end
|
46
53
|
|
47
54
|
##
|
@@ -59,7 +66,13 @@ module SoberSwag
|
|
59
66
|
# @param type [Class] the type to get a path_params definition for
|
60
67
|
# @return [Hash]
|
61
68
|
def path_params_for(type)
|
62
|
-
with_types_discovered(type)
|
69
|
+
compiler = with_types_discovered(type)
|
70
|
+
|
71
|
+
if compiler.respond_to?(:swagger_path_schema)
|
72
|
+
compiler.swagger_path_schema
|
73
|
+
else
|
74
|
+
compiler.path_schema
|
75
|
+
end
|
63
76
|
end
|
64
77
|
|
65
78
|
##
|
@@ -69,7 +82,13 @@ module SoberSwag
|
|
69
82
|
# @param type [Class] the type to get the query_params definitions for
|
70
83
|
# @return [Hash]
|
71
84
|
def query_params_for(type)
|
72
|
-
with_types_discovered(type)
|
85
|
+
compiler = with_types_discovered(type)
|
86
|
+
|
87
|
+
if compiler.respond_to?(:swagger_query_schema)
|
88
|
+
compiler.swagger_query_schema
|
89
|
+
else
|
90
|
+
compiler.query_schema
|
91
|
+
end
|
73
92
|
end
|
74
93
|
|
75
94
|
##
|
@@ -80,6 +99,9 @@ module SoberSwag
|
|
80
99
|
# @return [Hash]
|
81
100
|
def body_for(type)
|
82
101
|
add_type(type)
|
102
|
+
|
103
|
+
return reporting_types.compile(type) if type.respond_to?(:swagger_schema)
|
104
|
+
|
83
105
|
Type.new(type).schema_stub
|
84
106
|
end
|
85
107
|
|
@@ -109,23 +131,47 @@ module SoberSwag
|
|
109
131
|
# use tap here to avoid an explicit self at the end of this
|
110
132
|
# which makes this method chainable
|
111
133
|
tap do
|
112
|
-
|
134
|
+
if type.is_a?(SoberSwag::Reporting::Input::Interface) || type.is_a?(SoberSwag::Reporting::Output::Interface)
|
135
|
+
add_reporting_type(type)
|
136
|
+
else
|
137
|
+
add_dry_type(type)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
113
141
|
|
114
|
-
|
115
|
-
# Do nothing if we already have a type
|
116
|
-
return self if @types.include?(type_compiler)
|
142
|
+
private
|
117
143
|
|
118
|
-
|
144
|
+
def add_dry_type(type)
|
145
|
+
type_compiler = Type.new(type)
|
119
146
|
|
120
|
-
|
121
|
-
|
122
|
-
|
147
|
+
##
|
148
|
+
# Do nothing if we already have a type
|
149
|
+
return self if @types.include?(type_compiler)
|
150
|
+
|
151
|
+
@types.add(type_compiler) if type_compiler.standalone?
|
152
|
+
|
153
|
+
type_compiler.found_types.each do |ft|
|
154
|
+
add_type(ft)
|
123
155
|
end
|
124
156
|
end
|
125
157
|
|
126
|
-
|
158
|
+
def add_reporting_type(type)
|
159
|
+
reporting_types.compile(type)
|
160
|
+
end
|
127
161
|
|
128
162
|
def with_types_discovered(type)
|
163
|
+
if type.respond_to?(:swagger_schema)
|
164
|
+
with_reporting_types_discovered(type)
|
165
|
+
else
|
166
|
+
with_dry_types_discovered(type)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
def with_reporting_types_discovered(type)
|
171
|
+
type.tap { |t| reporting_types.compile(t) }
|
172
|
+
end
|
173
|
+
|
174
|
+
def with_dry_types_discovered(type)
|
129
175
|
Type.new(type).tap do |type_compiler|
|
130
176
|
type_compiler.found_types.each { |ft| add_type(ft) }
|
131
177
|
end
|
@@ -2,7 +2,7 @@ module SoberSwag
|
|
2
2
|
module Controller
|
3
3
|
##
|
4
4
|
# Describe a single controller endpoint.
|
5
|
-
class Route
|
5
|
+
class Route # rubocop:disable Metrics/ClassLength
|
6
6
|
##
|
7
7
|
# @param method [Symbol] the HTTP method to get
|
8
8
|
# @param action_name [Symbol] the name of the rails action
|
@@ -83,8 +83,11 @@ module SoberSwag
|
|
83
83
|
# @overload request_body(base = SoberSwag::InputObject, &block)
|
84
84
|
# Define a Swagger-able type inline to use to parse the request body.
|
85
85
|
# @see SoberSwag.input_object
|
86
|
-
|
87
|
-
|
86
|
+
# @overload request_body(base = SoberSwag::Reporting::Input::Struct, reporting: true, &block)
|
87
|
+
# Define a swagger-able type inline, using the new reporting system.
|
88
|
+
# @see SoberSwag::Reporting::Input::Struct
|
89
|
+
def request_body(base = SoberSwag::InputObject, reporting: false, &block)
|
90
|
+
@request_body_class = make_input_object!(base, reporting: reporting, &block)
|
88
91
|
action_module.const_set('RequestBody', @request_body_class)
|
89
92
|
end
|
90
93
|
|
@@ -101,8 +104,11 @@ module SoberSwag
|
|
101
104
|
# @overload query_params(base = SoberSwag::InputObject, &block)
|
102
105
|
# Define a Swagger-able type inline to use to parse the query params.
|
103
106
|
# @see SoberSwag.input_object
|
104
|
-
|
105
|
-
|
107
|
+
# @overload query_params(base = SoberSwag::Reporting::Input::Struct, reporting: true, &block)
|
108
|
+
# Define a swagger-able type inline, using the new reporting system.
|
109
|
+
# @see SoberSwag::Reporting::Input::Struct
|
110
|
+
def query_params(base = SoberSwag::InputObject, reporting: false, &block)
|
111
|
+
@query_params_class = make_input_object!(base, reporting: reporting, &block)
|
106
112
|
action_module.const_set('QueryParams', @query_params_class)
|
107
113
|
end
|
108
114
|
|
@@ -119,8 +125,11 @@ module SoberSwag
|
|
119
125
|
# @overload path_params(base = SoberSwag::InputObject, &block)
|
120
126
|
# Define a Swagger-able type inline to use to parse the path params.
|
121
127
|
# @see SoberSwag.input_object
|
122
|
-
|
123
|
-
|
128
|
+
# @overload path_params(base = SoberSwag::Reporting::Input::Struct, reporting: true, &block)
|
129
|
+
# Define a swagger-able type inline, using the new reporting system.
|
130
|
+
# @see SoberSwag::Reporting::Input::Struct
|
131
|
+
def path_params(base = SoberSwag::InputObject, reporting: false, &block)
|
132
|
+
@path_params_class = make_input_object!(base, reporting: reporting, &block)
|
124
133
|
action_module.const_set('PathParams', @path_params_class)
|
125
134
|
end
|
126
135
|
|
@@ -214,7 +223,34 @@ module SoberSwag
|
|
214
223
|
@response_module ||= Module.new.tap { |m| action_module.const_set(:Response, m) }
|
215
224
|
end
|
216
225
|
|
217
|
-
def make_input_object!(base, &block)
|
226
|
+
def make_input_object!(base, reporting:, &block)
|
227
|
+
if reporting
|
228
|
+
make_reporting_input!(
|
229
|
+
base == SoberSwag::InputObject ? SoberSwag::Reporting::Input::Struct : base,
|
230
|
+
&block
|
231
|
+
)
|
232
|
+
else
|
233
|
+
make_non_reporting_input!(base, &block)
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
def make_reporting_input!(base, &block)
|
238
|
+
if block
|
239
|
+
raise ArgumentError, 'non-class passed along with block' unless base.is_a?(Class)
|
240
|
+
|
241
|
+
make_reporting_input_struct!(base, &block)
|
242
|
+
else
|
243
|
+
base
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
def make_reporting_input_struct!(base, &block)
|
248
|
+
raise ArgumentError, 'base class must be a soberswag reporting class!' unless base <= SoberSwag::Reporting::Input::Struct
|
249
|
+
|
250
|
+
Class.new(base, &block)
|
251
|
+
end
|
252
|
+
|
253
|
+
def make_non_reporting_input!(base, &block)
|
218
254
|
if base.is_a?(Class)
|
219
255
|
make_input_class(base, block)
|
220
256
|
elsif block
|
@@ -114,7 +114,7 @@ module SoberSwag
|
|
114
114
|
r = current_action_def
|
115
115
|
raise UndefinedPathError unless r&.path_params_class
|
116
116
|
|
117
|
-
r.path_params_class
|
117
|
+
build_parsed_sober_swag(r.path_params_class, request.path_parameters)
|
118
118
|
end
|
119
119
|
end
|
120
120
|
|
@@ -128,7 +128,7 @@ module SoberSwag
|
|
128
128
|
r = current_action_def
|
129
129
|
raise UndefinedBodyError unless r&.request_body_class
|
130
130
|
|
131
|
-
r.request_body_class
|
131
|
+
build_parsed_sober_swag(r.request_body_class, body_params)
|
132
132
|
end
|
133
133
|
end
|
134
134
|
|
@@ -142,7 +142,7 @@ module SoberSwag
|
|
142
142
|
r = current_action_def
|
143
143
|
raise UndefinedQueryError unless r&.query_params_class
|
144
144
|
|
145
|
-
r.query_params_class
|
145
|
+
build_parsed_sober_swag(r.query_params_class, request.query_parameters)
|
146
146
|
end
|
147
147
|
end
|
148
148
|
|
@@ -154,8 +154,13 @@ module SoberSwag
|
|
154
154
|
def respond!(status, entity, serializer_opts: {}, rails_opts: {})
|
155
155
|
r = current_action_def
|
156
156
|
serializer = r.response_serializers[Rack::Utils.status_code(status)]
|
157
|
-
|
158
|
-
|
157
|
+
if serializer.respond_to?(:reporting?) && serializer.reporting?
|
158
|
+
serializer = serializer.view(serializer_opts[:view].to_sym) if serializer_opts.key?(:view)
|
159
|
+
render json: serializer.call(entity), status: status, **rails_opts
|
160
|
+
else
|
161
|
+
serializer ||= serializer.new if serializer.respond_to?(:new)
|
162
|
+
render json: serializer.serialize(entity, serializer_opts), status: status, **rails_opts
|
163
|
+
end
|
159
164
|
end
|
160
165
|
|
161
166
|
##
|
@@ -177,6 +182,14 @@ module SoberSwag
|
|
177
182
|
def current_action_def
|
178
183
|
self.class.find_route(params[:action])
|
179
184
|
end
|
185
|
+
|
186
|
+
def build_parsed_sober_swag(parser, params)
|
187
|
+
if parser.respond_to?(:call!)
|
188
|
+
parser.call!(params)
|
189
|
+
else
|
190
|
+
parser.call(params)
|
191
|
+
end
|
192
|
+
end
|
180
193
|
end
|
181
194
|
end
|
182
195
|
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module SoberSwag
|
2
|
+
module Reporting
|
3
|
+
module Compiler
|
4
|
+
##
|
5
|
+
# Compile component schemas.
|
6
|
+
class Schema
|
7
|
+
def initialize
|
8
|
+
@references = {}
|
9
|
+
@referenced_schemas = Set.new
|
10
|
+
end
|
11
|
+
|
12
|
+
##
|
13
|
+
# Hash of references to type definitions.
|
14
|
+
# Suitable for us as the components hash.
|
15
|
+
attr_reader :references
|
16
|
+
|
17
|
+
def compile(value)
|
18
|
+
compile_inner(value.swagger_schema)
|
19
|
+
end
|
20
|
+
|
21
|
+
def compile_inner(value)
|
22
|
+
initial, found = value
|
23
|
+
|
24
|
+
merge_found(found)
|
25
|
+
|
26
|
+
initial
|
27
|
+
end
|
28
|
+
|
29
|
+
def merge_found(found)
|
30
|
+
found.each do |k, v|
|
31
|
+
next unless @referenced_schemas.add?(k)
|
32
|
+
|
33
|
+
@references[k] = compile_inner(v.call)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module SoberSwag
|
2
|
+
module Reporting
|
3
|
+
module Input
|
4
|
+
##
|
5
|
+
# Convert booleans.
|
6
|
+
class Bool < Base
|
7
|
+
def call(val)
|
8
|
+
return Report::Value.new(['was not a bool']) unless [true, false].include?(val)
|
9
|
+
|
10
|
+
val
|
11
|
+
end
|
12
|
+
|
13
|
+
def swagger_schema
|
14
|
+
[{ type: 'boolean' }, {}]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module SoberSwag
|
2
|
+
module Reporting
|
3
|
+
module Input
|
4
|
+
module Converting
|
5
|
+
##
|
6
|
+
# Try to convert a boolean-like value to an actual boolean.
|
7
|
+
Bool = # rubocop:disable Naming/ConstantName
|
8
|
+
(SoberSwag::Reporting::Input::Bool.new |
|
9
|
+
(
|
10
|
+
SoberSwag::Reporting::Input::Text
|
11
|
+
.new
|
12
|
+
.enum(*(%w[y yes true t].flat_map { |x| [x, x.upcase] } + ['1'])) |
|
13
|
+
SoberSwag::Reporting::Input::Number.new.enum(1)).mapped { |_| true } |
|
14
|
+
(
|
15
|
+
SoberSwag::Reporting::Input::Text
|
16
|
+
.new
|
17
|
+
.enum(*(%w[false no n f].flat_map { |x| [x, x.upcase] } + ['0'])) |
|
18
|
+
SoberSwag::Reporting::Input::Number.new.enum(0)
|
19
|
+
).mapped { |_| false }
|
20
|
+
)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|