sober_swag 0.18.0 → 0.22.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (128) hide show
  1. checksums.yaml +4 -4
  2. data/.github/dependabot.yml +15 -0
  3. data/.github/workflows/benchmark.yml +39 -0
  4. data/.github/workflows/lint.yml +2 -4
  5. data/.github/workflows/ruby.yml +1 -1
  6. data/.gitignore +3 -0
  7. data/.rubocop.yml +6 -1
  8. data/.yardopts +7 -0
  9. data/CHANGELOG.md +22 -0
  10. data/Gemfile +12 -0
  11. data/README.md +1 -1
  12. data/bench/benchmark.rb +34 -0
  13. data/bench/benchmarks/basic_field_serializer.rb +21 -0
  14. data/bench/benchmarks/view_selection.rb +47 -0
  15. data/bin/console +30 -10
  16. data/docs/reporting.md +190 -0
  17. data/docs/serializers.md +4 -1
  18. data/example/Gemfile +2 -2
  19. data/example/Gemfile.lock +116 -123
  20. data/example/app/controllers/application_controller.rb +4 -0
  21. data/example/app/controllers/people_controller.rb +44 -28
  22. data/example/app/output_objects/identified_output.rb +7 -0
  23. data/example/app/output_objects/person_output_object.rb +37 -11
  24. data/example/app/output_objects/post_output_object.rb +0 -4
  25. data/example/app/output_objects/reporting_post_output.rb +18 -0
  26. data/example/bin/rspec +29 -0
  27. data/example/config/environments/production.rb +1 -1
  28. data/example/spec/requests/people/create_spec.rb +3 -2
  29. data/example/spec/requests/people/index_spec.rb +1 -1
  30. data/lib/sober_swag/compiler/path.rb +45 -4
  31. data/lib/sober_swag/compiler/paths.rb +20 -0
  32. data/lib/sober_swag/compiler/primitive.rb +17 -0
  33. data/lib/sober_swag/compiler/type.rb +105 -22
  34. data/lib/sober_swag/compiler.rb +87 -15
  35. data/lib/sober_swag/controller/route.rb +147 -28
  36. data/lib/sober_swag/controller.rb +57 -17
  37. data/lib/sober_swag/input_object.rb +124 -7
  38. data/lib/sober_swag/nodes/array.rb +19 -0
  39. data/lib/sober_swag/nodes/attribute.rb +45 -4
  40. data/lib/sober_swag/nodes/base.rb +27 -7
  41. data/lib/sober_swag/nodes/binary.rb +30 -13
  42. data/lib/sober_swag/nodes/enum.rb +16 -1
  43. data/lib/sober_swag/nodes/list.rb +20 -0
  44. data/lib/sober_swag/nodes/nullable_primitive.rb +3 -0
  45. data/lib/sober_swag/nodes/object.rb +4 -1
  46. data/lib/sober_swag/nodes/one_of.rb +11 -3
  47. data/lib/sober_swag/nodes/primitive.rb +34 -2
  48. data/lib/sober_swag/nodes/sum.rb +8 -0
  49. data/lib/sober_swag/output_object/definition.rb +57 -1
  50. data/lib/sober_swag/output_object/field.rb +31 -11
  51. data/lib/sober_swag/output_object/field_syntax.rb +19 -3
  52. data/lib/sober_swag/output_object/view.rb +46 -1
  53. data/lib/sober_swag/output_object.rb +40 -19
  54. data/lib/sober_swag/parser.rb +7 -1
  55. data/lib/sober_swag/reporting/compiler.rb +39 -0
  56. data/lib/sober_swag/reporting/input/base.rb +11 -0
  57. data/lib/sober_swag/reporting/input/bool.rb +19 -0
  58. data/lib/sober_swag/reporting/input/converting/bool.rb +24 -0
  59. data/lib/sober_swag/reporting/input/converting/date.rb +30 -0
  60. data/lib/sober_swag/reporting/input/converting/date_time.rb +28 -0
  61. data/lib/sober_swag/reporting/input/converting/decimal.rb +24 -0
  62. data/lib/sober_swag/reporting/input/converting/integer.rb +19 -0
  63. data/lib/sober_swag/reporting/input/converting.rb +16 -0
  64. data/lib/sober_swag/reporting/input/defer.rb +29 -0
  65. data/lib/sober_swag/reporting/input/described.rb +38 -0
  66. data/lib/sober_swag/reporting/input/dictionary.rb +37 -0
  67. data/lib/sober_swag/reporting/input/either.rb +51 -0
  68. data/lib/sober_swag/reporting/input/enum.rb +44 -0
  69. data/lib/sober_swag/reporting/input/format.rb +39 -0
  70. data/lib/sober_swag/reporting/input/interface.rb +87 -0
  71. data/lib/sober_swag/reporting/input/list.rb +44 -0
  72. data/lib/sober_swag/reporting/input/mapped.rb +36 -0
  73. data/lib/sober_swag/reporting/input/merge_objects.rb +72 -0
  74. data/lib/sober_swag/reporting/input/null.rb +34 -0
  75. data/lib/sober_swag/reporting/input/number.rb +19 -0
  76. data/lib/sober_swag/reporting/input/object/property.rb +53 -0
  77. data/lib/sober_swag/reporting/input/object.rb +100 -0
  78. data/lib/sober_swag/reporting/input/pattern.rb +46 -0
  79. data/lib/sober_swag/reporting/input/referenced.rb +38 -0
  80. data/lib/sober_swag/reporting/input/struct.rb +271 -0
  81. data/lib/sober_swag/reporting/input/text.rb +42 -0
  82. data/lib/sober_swag/reporting/input.rb +54 -0
  83. data/lib/sober_swag/reporting/invalid_schema_error.rb +21 -0
  84. data/lib/sober_swag/reporting/output/base.rb +25 -0
  85. data/lib/sober_swag/reporting/output/bool.rb +25 -0
  86. data/lib/sober_swag/reporting/output/defer.rb +69 -0
  87. data/lib/sober_swag/reporting/output/described.rb +42 -0
  88. data/lib/sober_swag/reporting/output/dictionary.rb +46 -0
  89. data/lib/sober_swag/reporting/output/interface.rb +83 -0
  90. data/lib/sober_swag/reporting/output/list.rb +54 -0
  91. data/lib/sober_swag/reporting/output/merge_objects.rb +97 -0
  92. data/lib/sober_swag/reporting/output/null.rb +25 -0
  93. data/lib/sober_swag/reporting/output/number.rb +25 -0
  94. data/lib/sober_swag/reporting/output/object/property.rb +45 -0
  95. data/lib/sober_swag/reporting/output/object.rb +54 -0
  96. data/lib/sober_swag/reporting/output/partitioned.rb +77 -0
  97. data/lib/sober_swag/reporting/output/pattern.rb +50 -0
  98. data/lib/sober_swag/reporting/output/referenced.rb +42 -0
  99. data/lib/sober_swag/reporting/output/struct.rb +262 -0
  100. data/lib/sober_swag/reporting/output/text.rb +25 -0
  101. data/lib/sober_swag/reporting/output/via_map.rb +67 -0
  102. data/lib/sober_swag/reporting/output/viewed.rb +72 -0
  103. data/lib/sober_swag/reporting/output.rb +54 -0
  104. data/lib/sober_swag/reporting/report/base.rb +57 -0
  105. data/lib/sober_swag/reporting/report/either.rb +36 -0
  106. data/lib/sober_swag/reporting/report/error.rb +15 -0
  107. data/lib/sober_swag/reporting/report/list.rb +28 -0
  108. data/lib/sober_swag/reporting/report/merged_object.rb +25 -0
  109. data/lib/sober_swag/reporting/report/object.rb +29 -0
  110. data/lib/sober_swag/reporting/report/output.rb +14 -0
  111. data/lib/sober_swag/reporting/report/value.rb +28 -0
  112. data/lib/sober_swag/reporting/report.rb +16 -0
  113. data/lib/sober_swag/reporting.rb +11 -0
  114. data/lib/sober_swag/serializer/array.rb +27 -3
  115. data/lib/sober_swag/serializer/base.rb +75 -25
  116. data/lib/sober_swag/serializer/conditional.rb +33 -1
  117. data/lib/sober_swag/serializer/field_list.rb +23 -5
  118. data/lib/sober_swag/serializer/hash.rb +53 -0
  119. data/lib/sober_swag/serializer/mapped.rb +10 -1
  120. data/lib/sober_swag/serializer/optional.rb +18 -1
  121. data/lib/sober_swag/serializer/primitive.rb +3 -0
  122. data/lib/sober_swag/serializer.rb +1 -0
  123. data/lib/sober_swag/server.rb +27 -11
  124. data/lib/sober_swag/type/named.rb +14 -0
  125. data/lib/sober_swag/types/comma_array.rb +4 -0
  126. data/lib/sober_swag/version.rb +1 -1
  127. data/lib/sober_swag.rb +7 -1
  128. metadata +72 -2
@@ -1,8 +1,11 @@
1
1
  module SoberSwag
2
2
  class Compiler
3
3
  ##
4
- # Compile a singular path, and that's it.
5
- # Only handles the actual body.
4
+ # This compiler transforms a {SoberSwag::Controller::Route} object into its associated OpenAPI V3 definition.
5
+ # These definitions are [called "paths" in the OpenAPI V3 spec](https://swagger.io/docs/specification/paths-and-operations/),
6
+ # thus the name of this compiler.
7
+ #
8
+ # It only compiles a *single* "path" at a time.
6
9
  class Path
7
10
  ##
8
11
  # @param route [SoberSwag::Controller::Route] a route to use
@@ -12,8 +15,18 @@ module SoberSwag
12
15
  @compiler = compiler
13
16
  end
14
17
 
15
- attr_reader :route, :compiler
18
+ ##
19
+ # @return [SoberSwag::Controller::Route]
20
+ attr_reader :route
21
+
22
+ ##
23
+ # @return [SoberSwag::Compiler] the compiler used for type compilation
24
+ attr_reader :compiler
16
25
 
26
+ ##
27
+ # The OpenAPI V3 "path" object for the associated {SoberSwag::Controller::Route}
28
+ #
29
+ # @return [Hash] the OpenAPI V3 description
17
30
  def schema
18
31
  base = {}
19
32
  base[:summary] = route.summary if route.summary
@@ -25,6 +38,11 @@ module SoberSwag
25
38
  base
26
39
  end
27
40
 
41
+ ##
42
+ # An array of "response" objects from swagger.
43
+ #
44
+ # @return [Hash{String => Hash}]
45
+ # response code to response object.
28
46
  def responses # rubocop:disable Metrics/MethodLength
29
47
  route.response_serializers.map { |status, serializer|
30
48
  [
@@ -33,7 +51,9 @@ module SoberSwag
33
51
  description: route.response_descriptions[status],
34
52
  content: {
35
53
  'application/json': {
36
- schema: compiler.response_for(serializer.type)
54
+ schema: compiler.response_for(
55
+ serializer.respond_to?(:swagger_schema) ? serializer : serializer.type
56
+ )
37
57
  }
38
58
  }
39
59
  }
@@ -41,10 +61,19 @@ module SoberSwag
41
61
  }.to_h
42
62
  end
43
63
 
64
+ ##
65
+ # An array of all parameters, be they in the query or in the path.
66
+ # See [this page](https://swagger.io/docs/specification/serialization/) for what that looks like.
67
+ #
68
+ # @return [Array<Hash>]
44
69
  def params
45
70
  query_params + path_params
46
71
  end
47
72
 
73
+ ##
74
+ # An array of schemas for all query parameters.
75
+ #
76
+ # @return [Array<Hash>] the schemas
48
77
  def query_params
49
78
  if route.query_params_class
50
79
  compiler.query_params_for(route.query_params_class)
@@ -53,6 +82,10 @@ module SoberSwag
53
82
  end
54
83
  end
55
84
 
85
+ ##
86
+ # An array of schemas for all path parameters.
87
+ #
88
+ # @return [Array<Hash>] the schemas
56
89
  def path_params
57
90
  if route.path_params_class
58
91
  compiler.path_params_for(route.path_params_class)
@@ -61,6 +94,11 @@ module SoberSwag
61
94
  end
62
95
  end
63
96
 
97
+ ##
98
+ # The schema for a request body.
99
+ # Matches [this spec.](https://swagger.io/docs/specification/paths-and-operations/)
100
+ #
101
+ # @return [Hash] the schema
64
102
  def request_body
65
103
  return nil unless route.request_body_class
66
104
 
@@ -74,6 +112,9 @@ module SoberSwag
74
112
  }
75
113
  end
76
114
 
115
+ ##
116
+ # The tags for this path.
117
+ # @return [Array<String>] the tags
77
118
  def tags
78
119
  return nil unless route.tags.any?
79
120
 
@@ -2,15 +2,28 @@ module SoberSwag
2
2
  class Compiler
3
3
  ##
4
4
  # Compile multiple routes into a paths set.
5
+ # This basically just aggregates {SoberSwag::Controller::Route} objects.
5
6
  class Paths
7
+ ##
8
+ # Set up a new paths compiler with no routes in it.
6
9
  def initialize
7
10
  @routes = []
8
11
  end
9
12
 
13
+ ##
14
+ # Add on a new {SoberSwag::Controller::Route}
15
+ #
16
+ # @param route [SoberSwag::Controller::Route] the route description to add to compilation
17
+ # @return [SoberSwag::Compiler::Paths] self
10
18
  def add_route(route)
11
19
  @routes << route
20
+
21
+ self
12
22
  end
13
23
 
24
+ ##
25
+ # In the OpenAPI V3 spec, we group action definitions by their path.
26
+ # This helps us do that.
14
27
  def grouped_paths
15
28
  routes.group_by(&:path)
16
29
  end
@@ -20,6 +33,9 @@ module SoberSwag
20
33
  # paths list. Since this is only a compiler for paths,
21
34
  # it has *no idea* how to handle types. So, it takes a compiler
22
35
  # which it will use to do that for it.
36
+ #
37
+ # @param compiler [SoberSwag::Compiler::Type] the type compiler to use
38
+ # @return [Hash] a schema for all contained routes.
23
39
  def paths_list(compiler)
24
40
  grouped_paths.transform_values do |values|
25
41
  values.map { |route|
@@ -31,6 +47,8 @@ module SoberSwag
31
47
  ##
32
48
  # Get a list of all types we discovered when compiling
33
49
  # the paths.
50
+ #
51
+ # @yield [Class] all the types found in all the routes described in here.
34
52
  def found_types
35
53
  return enum_for(:found_types) unless block_given?
36
54
 
@@ -41,6 +59,8 @@ module SoberSwag
41
59
  end
42
60
  end
43
61
 
62
+ ##
63
+ # @return [Array<SoberSwag::Controller::Route>] the routes to document
44
64
  attr_reader :routes
45
65
 
46
66
  private
@@ -4,7 +4,11 @@ module SoberSwag
4
4
  # Compiles a primitive type.
5
5
  # Almost always constructed with the values from
6
6
  # {SoberSwag::Nodes::Primitive}.
7
+ #
8
+ # This works by either generating a swagger primitive definition, *or* a `$ref` to one with a given identifier.
7
9
  class Primitive
10
+ ##
11
+ # @param type [Class] the swagger-able class to document.
8
12
  def initialize(type)
9
13
  @type = type
10
14
 
@@ -13,6 +17,8 @@ module SoberSwag
13
17
 
14
18
  attr_reader :type
15
19
 
20
+ ##
21
+ # Is this documenting one of the build-in swagger types?
16
22
  def swagger_primitive?
17
23
  SWAGGER_PRIMITIVE_DEFS.include?(type)
18
24
  end
@@ -25,6 +31,9 @@ module SoberSwag
25
31
 
26
32
  ##
27
33
  # Turn this type into a swagger hash with a proper type key.
34
+ # This is suitable for use as the value of a `schema` key in a definition.
35
+ #
36
+ # @return [Hash] the schema.
28
37
  def type_hash
29
38
  if swagger_primitive?
30
39
  SWAGGER_PRIMITIVE_DEFS.fetch(type)
@@ -37,10 +46,16 @@ module SoberSwag
37
46
  end
38
47
  end
39
48
 
49
+ ##
50
+ # Primitive schema used for ruby `Date` values.
40
51
  DATE_PRIMITIVE = { type: :string, format: :date }.freeze
52
+ ##
53
+ # Primitive schema used for ruby `DateTime` values.
41
54
  DATE_TIME_PRIMITIVE = { type: :string, format: :'date-time' }.freeze
42
55
  HASH_PRIMITIVE = { type: :object, additionalProperties: true }.freeze
43
56
 
57
+ ##
58
+ # Map of types that are considered "primitive types" in the OpenAPI V3 spec.
44
59
  SWAGGER_PRIMITIVE_DEFS =
45
60
  {
46
61
  NilClass => :null,
@@ -57,6 +72,8 @@ module SoberSwag
57
72
  Hash => HASH_PRIMITIVE
58
73
  ).transform_values(&:freeze).freeze
59
74
 
75
+ ##
76
+ # @return [String] the schema reference
60
77
  def ref_name
61
78
  raise Error, 'is not a type that is named!' if swagger_primitive?
62
79
 
@@ -1,45 +1,95 @@
1
1
  module SoberSwag
2
2
  class Compiler
3
3
  ##
4
- # A compiler for DRY-Struct data types, essentially.
5
- # It only consumes one type at a time.
4
+ # A compiler for swagger-able types.
5
+ #
6
+ # This class turns Swagger-able types into a *schema*.
7
+ # This Schema may be:
8
+ # - a [schema object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#schemaObject) with {#object_schema}
9
+ # - a [path schema](https://swagger.io/docs/specification/describing-parameters/#path-parameters) with {#path_schema}
10
+ # - a [query schema](https://swagger.io/docs/specification/describing-parameters/#query-parameters) with {#query_schema}
11
+ #
12
+ # As such, it compiles all types to all applicable schemas.
13
+ #
14
+ # While this class compiles *one* type at a time, it *keeps track* of the other types needed to describe this schema.
15
+ # It stores these types in a set, available at {#found_types}.
16
+ #
17
+ # For example, with a schema like:
18
+ #
19
+ # ```ruby
20
+ # class Bar < SoberSwag::InputObject
21
+ # attribute :baz, primitive(:String)
22
+ # end
23
+ #
24
+ # class Foo < SoberSwag::InputObject
25
+ # attribute :bar, Bar
26
+ # end
27
+ # ```
28
+ #
29
+ # If you compile `Foo` with this class, {#found_types} will include `Bar`.
30
+ #
6
31
  class Type # rubocop:disable Metrics/ClassLength
32
+ ##
33
+ # An error raised when a type is too complicated for a given schema.
34
+ # This may be due to containing too many layers of nesting.
7
35
  class TooComplicatedError < ::SoberSwag::Compiler::Error; end
36
+ ##
37
+ # An error raised when a type is too complicated to transform into a *path* schema.
8
38
  class TooComplicatedForPathError < TooComplicatedError; end
39
+ ##
40
+ # An error raised when a type is too complicated to transform into a *query* schema.
9
41
  class TooComplicatedForQueryError < TooComplicatedError; end
10
42
 
43
+ ##
44
+ # A list of acceptable keys to use as metadata for an object schema.
45
+ # All other metadata keys defined on a type with {SoberSwag::InputObject.meta} will be ignored.
46
+ #
47
+ # @return [Array<Symbol>] valid keys.
11
48
  METADATA_KEYS = %i[description deprecated].freeze
12
49
 
50
+ ##
51
+ # Create a new compiler for a swagger-able type.
52
+ # @param type [Class] the type to compile
13
53
  def initialize(type)
14
54
  @type = type
15
55
  end
16
56
 
57
+ ##
58
+ # @return [Class] the type we are compiling.
17
59
  attr_reader :type
18
60
 
19
61
  ##
20
62
  # Is this type standalone, IE, worth serializing on its own
21
63
  # in the schemas section of our schema?
64
+ # @return [true,false]
22
65
  def standalone?
23
66
  type.is_a?(Class)
24
67
  end
25
68
 
69
+ ##
70
+ # Get back the [schema object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#schemaObject)
71
+ # for the type described.
72
+ #
73
+ # @return [Hash]
26
74
  def object_schema
27
75
  @object_schema ||=
28
76
  make_object_schema
29
77
  end
30
78
 
31
- def object_schema_meta
32
- return {} unless standalone? && type <= SoberSwag::Type::Named
33
-
34
- {
35
- description: type.description
36
- }.reject { |_, v| v.nil? }
37
- end
38
-
79
+ ##
80
+ # Give a "stub type" for this schema.
81
+ # This is suitable to use as the schema for attributes of other schemas.
82
+ # Almost always generates a ref object.
83
+ # @return [Hash] the OpenAPI V3 schema stub
39
84
  def schema_stub
40
85
  @schema_stub ||= generate_schema_stub
41
86
  end
42
87
 
88
+ ##
89
+ # The schema for this type when it is path of the path.
90
+ #
91
+ # @raise [TooComplicatedForPathError] when the compiled type is too complicated to use in a path
92
+ # @return [Hash] a [path parameters hash](https://swagger.io/docs/specification/describing-parameters/#path-parameters) for this type.
43
93
  def path_schema
44
94
  path_schema_stub.map do |e|
45
95
  ensure_uncomplicated(e[:name], e[:schema])
@@ -51,16 +101,30 @@ module SoberSwag
51
101
 
52
102
  DEFAULT_QUERY_SCHEMA_ATTRS = { in: :query, style: :deepObject, explode: true }.freeze
53
103
 
104
+ ##
105
+ # The schema for this type when it is part of the query.
106
+ # @raise [TooComplicatedForQueryError] when this type is too complicated to use in a query schema
107
+ # @return [Hash] a [query parameters hash](https://swagger.io/docs/specification/describing-parameters/#query-parameters) for this type.
54
108
  def query_schema
55
109
  path_schema_stub.map { |e| DEFAULT_QUERY_SCHEMA_ATTRS.merge(e) }
56
110
  rescue TooComplicatedError => e
57
111
  raise TooComplicatedForQueryError, e.message
58
112
  end
59
113
 
114
+ ##
115
+ # Get the name of this type if it is to be used in a `$ref` key.
116
+ # This is useful if we are going to use this type compiler to compile an *attribute* of another object.
117
+ #
118
+ # @return [String] a reference specifier for this type
60
119
  def ref_name
61
120
  SoberSwag::Compiler::Primitive.new(type).ref_name
62
121
  end
63
122
 
123
+ ##
124
+ # Get a set of all other types needed to compile this type.
125
+ # This set will *not* include the type being compiled.
126
+ #
127
+ # @return [Set<Class>]
64
128
  def found_types
65
129
  @found_types ||=
66
130
  begin
@@ -69,32 +133,51 @@ module SoberSwag
69
133
  end
70
134
  end
71
135
 
72
- def mapped_type
73
- @mapped_type ||= parsed_type.map { |v| SoberSwag::Compiler::Primitive.new(v).type_hash }
74
- end
75
-
76
- def parsed_type
77
- @parsed_type ||=
78
- begin
79
- (parsed,) = parsed_result
80
- parsed
81
- end
82
- end
83
-
136
+ ##
137
+ # This type, parsed into an AST.
84
138
  def parsed_result
85
139
  @parsed_result ||= Parser.new(type_for_parser).run_parser
86
140
  end
87
141
 
142
+ ##
143
+ # Standard ruby equality.
88
144
  def eql?(other)
89
145
  other.class == self.class && other.type == type
90
146
  end
91
147
 
148
+ ##
149
+ # Standard ruby hashing method.
150
+ # Compilers hash to the same value if they are compiling the same type.
92
151
  def hash
93
152
  [self.class, type].hash
94
153
  end
95
154
 
96
155
  private
97
156
 
157
+ ##
158
+ # Get metadata attributes to be used if compiling an object schema.
159
+ #
160
+ # @return [Hash]
161
+ def object_schema_meta
162
+ return {} unless standalone? && type <= SoberSwag::Type::Named
163
+
164
+ {
165
+ description: type.description
166
+ }.reject { |_, v| v.nil? }
167
+ end
168
+
169
+ def parsed_type
170
+ @parsed_type ||=
171
+ begin
172
+ (parsed,) = parsed_result
173
+ parsed
174
+ end
175
+ end
176
+
177
+ def mapped_type
178
+ @mapped_type ||= parsed_type.map { |v| SoberSwag::Compiler::Primitive.new(v).type_hash }
179
+ end
180
+
98
181
  def generate_schema_stub
99
182
  if type.is_a?(Class)
100
183
  SoberSwag::Compiler::Primitive.new(type).type_hash
@@ -13,10 +13,13 @@ 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
  ##
19
20
  # Convert a compiler to the overall type definition.
21
+ #
22
+ # @return Hash the swagger definition.
20
23
  def to_swagger
21
24
  {
22
25
  paths: path_schemas,
@@ -26,19 +29,32 @@ module SoberSwag
26
29
  }
27
30
  end
28
31
 
32
+ ##
33
+ # @return [SoberSwag::Reporting::Compiler::Schema]
34
+ attr_reader :reporting_types
35
+
29
36
  ##
30
37
  # Add a path to be compiled.
31
38
  # @param route [SoberSwag::Controller::Route] the route to add.
39
+ # @return [Compiler] self
32
40
  def add_route(route)
33
41
  tap { @paths.add_route(route) }
34
42
  end
35
43
 
44
+ ##
45
+ # Get the schema of each object type defined in this Compiler.
46
+ #
47
+ # @return [Hash]
36
48
  def object_schemas
37
- @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
+ )
38
52
  end
39
53
 
40
54
  ##
41
55
  # The path section of the swagger schema.
56
+ #
57
+ # @return [Hash]
42
58
  def path_schemas
43
59
  @paths.paths_list(self)
44
60
  end
@@ -46,60 +62,116 @@ module SoberSwag
46
62
  ##
47
63
  # Compile a type to a new, path-params list.
48
64
  # This will add all subtypes to the found types list.
65
+ #
66
+ # @param type [Class] the type to get a path_params definition for
67
+ # @return [Hash]
49
68
  def path_params_for(type)
50
- with_types_discovered(type).path_schema
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
51
76
  end
52
77
 
53
78
  ##
54
79
  # Get the query params list for a type.
55
80
  # All found types will be added to the reference dictionary.
81
+ #
82
+ # @param type [Class] the type to get the query_params definitions for
83
+ # @return [Hash]
56
84
  def query_params_for(type)
57
- with_types_discovered(type).query_schema
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
58
92
  end
59
93
 
60
94
  ##
61
95
  # Get the request body definition for a type.
62
96
  # This will always be a ref.
97
+ #
98
+ # @param type [Class] the type to get the body definition for
99
+ # @return [Hash]
63
100
  def body_for(type)
64
101
  add_type(type)
102
+
103
+ return reporting_types.compile(type) if type.respond_to?(:swagger_schema)
104
+
65
105
  Type.new(type).schema_stub
66
106
  end
67
107
 
68
108
  ##
69
- # Get the definition of a response type
109
+ # Get the definition of a response type.
110
+ #
111
+ # This is an alias of {#body_for}
112
+ # @see body_for
70
113
  def response_for(type)
71
114
  body_for(type)
72
115
  end
73
116
 
74
117
  ##
75
- # Get the existing schema for a given type
118
+ # Get the existing schema for a given type.
119
+ #
120
+ # @param type [Class] the type to get the schema for
121
+ # @return [Hash,nil] the swagger schema for this object, or nil if it was not found.
76
122
  def schema_for(type)
77
123
  @types.find { |type_comp| type_comp.type == type }&.object_schema
78
124
  end
79
125
 
80
126
  ##
81
- # Add a type in the types reference dictionary, essentially
127
+ # Add a type in the types reference dictionary, essentially.
128
+ # @param type [Class] the type to compiler
129
+ # @return [SoberSwag::Compiler] self
82
130
  def add_type(type)
83
131
  # use tap here to avoid an explicit self at the end of this
84
132
  # which makes this method chainable
85
133
  tap do
86
- type_compiler = Type.new(type)
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
87
141
 
88
- ##
89
- # Do nothing if we already have a type
90
- return self if @types.include?(type_compiler)
142
+ private
91
143
 
92
- @types.add(type_compiler) if type_compiler.standalone?
144
+ def add_dry_type(type)
145
+ type_compiler = Type.new(type)
93
146
 
94
- type_compiler.found_types.each do |ft|
95
- add_type(ft)
96
- end
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)
97
155
  end
98
156
  end
99
157
 
100
- private
158
+ def add_reporting_type(type)
159
+ reporting_types.compile(type)
160
+ end
101
161
 
102
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)
103
175
  Type.new(type).tap do |type_compiler|
104
176
  type_compiler.found_types.each { |ft| add_type(ft) }
105
177
  end