praxis 2.0.pre.17 → 2.0.pre.21

Sign up to get free protection for your applications and to get access to all the features.
Files changed (235) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +54 -0
  3. data/.simplecov +3 -1
  4. data/.travis.yml +2 -1
  5. data/CHANGELOG.md +19 -0
  6. data/CONTRIBUTING.md +2 -79
  7. data/Gemfile +5 -1
  8. data/Guardfile +6 -4
  9. data/LICENSE +0 -2
  10. data/MAINTAINERS.md +1 -0
  11. data/README.md +15 -22
  12. data/Rakefile +4 -2
  13. data/bin/praxis +55 -58
  14. data/lib/praxis/action_definition/headers_dsl_compiler.rb +5 -6
  15. data/lib/praxis/action_definition.rb +65 -95
  16. data/lib/praxis/api_definition.rb +21 -29
  17. data/lib/praxis/api_general_info.rb +55 -66
  18. data/lib/praxis/application.rb +15 -32
  19. data/lib/praxis/blueprint.rb +80 -73
  20. data/lib/praxis/bootloader.rb +24 -33
  21. data/lib/praxis/bootloader_stages/environment.rb +5 -10
  22. data/lib/praxis/bootloader_stages/file_loader.rb +3 -6
  23. data/lib/praxis/bootloader_stages/plugin_config_load.rb +4 -6
  24. data/lib/praxis/bootloader_stages/plugin_config_prepare.rb +2 -2
  25. data/lib/praxis/bootloader_stages/plugin_loader.rb +3 -7
  26. data/lib/praxis/bootloader_stages/plugin_setup.rb +3 -3
  27. data/lib/praxis/bootloader_stages/routing.rb +5 -8
  28. data/lib/praxis/bootloader_stages/subgroup_loader.rb +2 -10
  29. data/lib/praxis/bootloader_stages/warn_unloaded_files.rb +15 -19
  30. data/lib/praxis/callbacks.rb +12 -11
  31. data/lib/praxis/collection.rb +11 -14
  32. data/lib/praxis/config.rb +17 -28
  33. data/lib/praxis/config_hash.rb +2 -1
  34. data/lib/praxis/controller.rb +7 -6
  35. data/lib/praxis/dispatcher.rb +34 -42
  36. data/lib/praxis/docs/open_api/info_object.rb +11 -8
  37. data/lib/praxis/docs/open_api/media_type_object.rb +18 -17
  38. data/lib/praxis/docs/open_api/operation_object.rb +7 -4
  39. data/lib/praxis/docs/open_api/parameter_object.rb +17 -14
  40. data/lib/praxis/docs/open_api/paths_object.rb +11 -9
  41. data/lib/praxis/docs/open_api/request_body_object.rb +14 -13
  42. data/lib/praxis/docs/open_api/response_object.rb +24 -18
  43. data/lib/praxis/docs/open_api/responses_object.rb +3 -1
  44. data/lib/praxis/docs/open_api/schema_object.rb +61 -29
  45. data/lib/praxis/docs/open_api/server_object.rb +5 -2
  46. data/lib/praxis/docs/open_api/tag_object.rb +9 -6
  47. data/lib/praxis/docs/open_api_generator.rb +114 -150
  48. data/lib/praxis/endpoint_definition.rb +60 -77
  49. data/lib/praxis/error_handler.rb +2 -2
  50. data/lib/praxis/exception.rb +2 -0
  51. data/lib/praxis/exceptions/config.rb +3 -1
  52. data/lib/praxis/exceptions/config_load.rb +2 -0
  53. data/lib/praxis/exceptions/config_validation.rb +3 -1
  54. data/lib/praxis/exceptions/invalid_configuration.rb +3 -1
  55. data/lib/praxis/exceptions/invalid_response.rb +3 -1
  56. data/lib/praxis/exceptions/invalid_trait.rb +3 -1
  57. data/lib/praxis/exceptions/stage_not_found.rb +3 -1
  58. data/lib/praxis/exceptions/validation.rb +4 -3
  59. data/lib/praxis/extensions/attribute_filtering/active_record_filter_query_builder.rb +163 -149
  60. data/lib/praxis/extensions/attribute_filtering/active_record_patches/5x.rb +18 -13
  61. data/lib/praxis/extensions/attribute_filtering/active_record_patches/6_0.rb +13 -9
  62. data/lib/praxis/extensions/attribute_filtering/active_record_patches/6_1_plus.rb +14 -11
  63. data/lib/praxis/extensions/attribute_filtering/active_record_patches.rb +12 -9
  64. data/lib/praxis/extensions/attribute_filtering/filter_tree_node.rb +8 -5
  65. data/lib/praxis/extensions/attribute_filtering/filtering_params.rb +89 -65
  66. data/lib/praxis/extensions/attribute_filtering/filters_parser.rb +68 -62
  67. data/lib/praxis/extensions/attribute_filtering.rb +3 -1
  68. data/lib/praxis/extensions/field_expansion.rb +6 -4
  69. data/lib/praxis/extensions/field_selection/active_record_query_selector.rb +10 -8
  70. data/lib/praxis/extensions/field_selection/field_selector.rb +91 -92
  71. data/lib/praxis/extensions/field_selection/sequel_query_selector.rb +12 -12
  72. data/lib/praxis/extensions/field_selection.rb +3 -1
  73. data/lib/praxis/extensions/pagination/active_record_pagination_handler.rb +6 -4
  74. data/lib/praxis/extensions/pagination/header_generator.rb +16 -11
  75. data/lib/praxis/extensions/pagination/ordering_params.rb +29 -28
  76. data/lib/praxis/extensions/pagination/pagination_handler.rb +44 -42
  77. data/lib/praxis/extensions/pagination/pagination_params.rb +29 -48
  78. data/lib/praxis/extensions/pagination/sequel_pagination_handler.rb +8 -7
  79. data/lib/praxis/extensions/pagination.rb +10 -15
  80. data/lib/praxis/extensions/rails_compat/request_methods.rb +3 -4
  81. data/lib/praxis/extensions/rails_compat.rb +2 -0
  82. data/lib/praxis/extensions/rendering.rb +12 -12
  83. data/lib/praxis/field_expander.rb +8 -9
  84. data/lib/praxis/file_group.rb +8 -12
  85. data/lib/praxis/finalizable.rb +1 -0
  86. data/lib/praxis/handlers/json.rb +5 -2
  87. data/lib/praxis/handlers/plain.rb +2 -1
  88. data/lib/praxis/handlers/www_form.rb +6 -3
  89. data/lib/praxis/handlers/{xml-sample.rb → xml_sample.rb} +26 -22
  90. data/lib/praxis/mapper/active_model_compat.rb +13 -10
  91. data/lib/praxis/mapper/resource.rb +196 -181
  92. data/lib/praxis/mapper/selector_generator.rb +106 -112
  93. data/lib/praxis/mapper/sequel_compat.rb +70 -67
  94. data/lib/praxis/media_type.rb +2 -2
  95. data/lib/praxis/media_type_identifier.rb +26 -22
  96. data/lib/praxis/middleware_app.rb +18 -15
  97. data/lib/praxis/multipart/parser.rb +46 -51
  98. data/lib/praxis/multipart/part.rb +78 -110
  99. data/lib/praxis/notifications.rb +2 -4
  100. data/lib/praxis/plugin.rb +11 -18
  101. data/lib/praxis/plugin_concern.rb +12 -15
  102. data/lib/praxis/plugins/mapper_plugin.rb +15 -13
  103. data/lib/praxis/plugins/pagination_plugin.rb +8 -6
  104. data/lib/praxis/plugins/rails_plugin.rb +33 -28
  105. data/lib/praxis/renderer.rb +11 -15
  106. data/lib/praxis/request.rb +48 -44
  107. data/lib/praxis/request_stages/action.rb +4 -6
  108. data/lib/praxis/request_stages/load_request.rb +2 -4
  109. data/lib/praxis/request_stages/request_stage.rb +19 -23
  110. data/lib/praxis/request_stages/response.rb +4 -6
  111. data/lib/praxis/request_stages/validate.rb +3 -5
  112. data/lib/praxis/request_stages/validate_params_and_headers.rb +15 -22
  113. data/lib/praxis/request_stages/validate_payload.rb +25 -28
  114. data/lib/praxis/request_superclassing.rb +3 -3
  115. data/lib/praxis/resource_definition.rb +1 -0
  116. data/lib/praxis/response.rb +24 -26
  117. data/lib/praxis/response_definition.rb +77 -122
  118. data/lib/praxis/response_template.rb +11 -15
  119. data/lib/praxis/responses/http.rb +23 -44
  120. data/lib/praxis/responses/internal_server_error.rb +18 -21
  121. data/lib/praxis/responses/multipart_ok.rb +4 -9
  122. data/lib/praxis/responses/validation_error.rb +8 -15
  123. data/lib/praxis/route.rb +8 -10
  124. data/lib/praxis/router/rack.rb +13 -7
  125. data/lib/praxis/router/simple.rb +10 -5
  126. data/lib/praxis/router.rb +27 -34
  127. data/lib/praxis/routing_config.rb +52 -29
  128. data/lib/praxis/simple_media_type.rb +5 -8
  129. data/lib/praxis/stage.rb +17 -25
  130. data/lib/praxis/tasks/api_docs.rb +17 -16
  131. data/lib/praxis/tasks/console.rb +3 -1
  132. data/lib/praxis/tasks/environment.rb +2 -0
  133. data/lib/praxis/tasks/routes.rb +26 -24
  134. data/lib/praxis/tasks.rb +3 -1
  135. data/lib/praxis/trait.rb +37 -46
  136. data/lib/praxis/types/fuzzy_hash.rb +13 -14
  137. data/lib/praxis/types/media_type_common.rb +11 -10
  138. data/lib/praxis/types/multipart_array/part_definition.rb +14 -17
  139. data/lib/praxis/types/multipart_array.rb +100 -115
  140. data/lib/praxis/validation_handler.rb +5 -3
  141. data/lib/praxis/version.rb +3 -1
  142. data/lib/praxis.rb +4 -5
  143. data/praxis.gemspec +22 -21
  144. data/spec/functional_spec.rb +44 -56
  145. data/spec/praxis/action_definition_spec.rb +39 -48
  146. data/spec/praxis/api_definition_spec.rb +45 -47
  147. data/spec/praxis/api_general_info_spec.rb +28 -29
  148. data/spec/praxis/application_spec.rb +18 -14
  149. data/spec/praxis/blueprint_spec.rb +33 -34
  150. data/spec/praxis/bootloader_spec.rb +32 -30
  151. data/spec/praxis/callbacks_spec.rb +37 -37
  152. data/spec/praxis/collection_spec.rb +18 -25
  153. data/spec/praxis/config_hash_spec.rb +5 -4
  154. data/spec/praxis/config_spec.rb +27 -26
  155. data/spec/praxis/controller_spec.rb +8 -9
  156. data/spec/praxis/endpoint_definition_spec.rb +25 -32
  157. data/spec/praxis/extensions/attribute_filtering/active_record_filter_query_builder_spec.rb +171 -114
  158. data/spec/praxis/extensions/attribute_filtering/filter_tree_node_spec.rb +22 -21
  159. data/spec/praxis/extensions/attribute_filtering/filtering_params_spec.rb +112 -60
  160. data/spec/praxis/extensions/attribute_filtering/filters_parser_spec.rb +37 -38
  161. data/spec/praxis/extensions/field_expansion_spec.rb +8 -10
  162. data/spec/praxis/extensions/field_selection/active_record_query_selector_spec.rb +14 -13
  163. data/spec/praxis/extensions/field_selection/field_selector_spec.rb +9 -16
  164. data/spec/praxis/extensions/field_selection/sequel_query_selector_spec.rb +50 -49
  165. data/spec/praxis/extensions/pagination/active_record_pagination_handler_spec.rb +32 -31
  166. data/spec/praxis/extensions/rendering_spec.rb +9 -9
  167. data/spec/praxis/extensions/support/spec_resources_active_model.rb +32 -49
  168. data/spec/praxis/extensions/support/spec_resources_sequel.rb +48 -48
  169. data/spec/praxis/field_expander_spec.rb +6 -5
  170. data/spec/praxis/file_group_spec.rb +3 -1
  171. data/spec/praxis/handlers/json_spec.rb +6 -5
  172. data/spec/praxis/mapper/resource_spec.rb +39 -29
  173. data/spec/praxis/mapper/selector_generator_spec.rb +80 -46
  174. data/spec/praxis/media_type_identifier_spec.rb +13 -10
  175. data/spec/praxis/media_type_spec.rb +12 -12
  176. data/spec/praxis/middleware_app_spec.rb +23 -22
  177. data/spec/praxis/multipart/parser_spec.rb +7 -9
  178. data/spec/praxis/notifications_spec.rb +4 -4
  179. data/spec/praxis/plugin_concern_spec.rb +5 -6
  180. data/spec/praxis/renderer_spec.rb +10 -9
  181. data/spec/praxis/request_spec.rb +38 -41
  182. data/spec/praxis/request_stages/action_spec.rb +14 -15
  183. data/spec/praxis/request_stages/request_stage_spec.rb +30 -41
  184. data/spec/praxis/request_stages/validate_spec.rb +3 -1
  185. data/spec/praxis/response_definition_spec.rb +79 -92
  186. data/spec/praxis/response_spec.rb +35 -40
  187. data/spec/praxis/responses/internal_server_error_spec.rb +6 -9
  188. data/spec/praxis/responses/validation_error_spec.rb +17 -18
  189. data/spec/praxis/route_spec.rb +4 -7
  190. data/spec/praxis/router_spec.rb +69 -79
  191. data/spec/praxis/routing_config_spec.rb +15 -14
  192. data/spec/praxis/stage_spec.rb +56 -53
  193. data/spec/praxis/trait_spec.rb +17 -17
  194. data/spec/praxis/types/fuzzy_hash_spec.rb +11 -9
  195. data/spec/praxis/types/multipart_array/part_definition_spec.rb +3 -2
  196. data/spec/praxis/types/multipart_array_spec.rb +33 -48
  197. data/spec/spec_app/app/concerns/authenticated.rb +5 -5
  198. data/spec/spec_app/app/concerns/basic_api.rb +3 -1
  199. data/spec/spec_app/app/concerns/log_wrapper.rb +5 -3
  200. data/spec/spec_app/app/controllers/base_class.rb +6 -5
  201. data/spec/spec_app/app/controllers/instances.rb +31 -34
  202. data/spec/spec_app/app/controllers/volumes.rb +6 -6
  203. data/spec/spec_app/app/responses/multipart.rb +1 -2
  204. data/spec/spec_app/app/responses/other_response.rb +2 -2
  205. data/spec/spec_app/config/environment.rb +19 -6
  206. data/spec/spec_app/config.ru +4 -3
  207. data/spec/spec_app/design/api.rb +13 -15
  208. data/spec/spec_app/design/media_types/instance.rb +6 -6
  209. data/spec/spec_app/design/media_types/volume.rb +2 -1
  210. data/spec/spec_app/design/media_types/volume_snapshot.rb +2 -1
  211. data/spec/spec_app/design/resources/instances.rb +11 -17
  212. data/spec/spec_app/design/resources/volume_snapshots.rb +4 -5
  213. data/spec/spec_app/design/resources/volumes.rb +4 -5
  214. data/spec/spec_helper.rb +11 -13
  215. data/spec/support/be_deep_equal_matcher.rb +5 -0
  216. data/spec/support/spec_authorization_plugin.rb +7 -12
  217. data/spec/support/spec_blueprints.rb +5 -4
  218. data/spec/support/spec_complex_authentication_plugin.rb +17 -34
  219. data/spec/support/spec_endpoint_definitions.rb +2 -3
  220. data/spec/support/spec_media_types.rb +28 -35
  221. data/spec/support/spec_resources.rb +22 -16
  222. data/spec/support/spec_simple_authentication_plugin.rb +5 -9
  223. data/tasks/loader.thor +4 -2
  224. data/tasks/thor/app.rb +7 -5
  225. data/tasks/thor/example.rb +23 -22
  226. data/tasks/thor/model.rb +7 -7
  227. data/tasks/thor/scaffold.rb +23 -23
  228. data/tasks/thor/templates/generator/example_app/app/v1/resources/user.rb +0 -8
  229. data/tasks/thor/templates/generator/scaffold/implementation/resources/item.rb +1 -2
  230. metadata +72 -84
  231. data/MAINTAINERS +0 -2
  232. data/TODO.md +0 -25
  233. data/spec/praxis/api_resource_spec.rb +0 -0
  234. data/spec/praxis/dispatcher_spec.rb +0 -0
  235. data/spec/spec_app/app/responses/bulk_response.rb +0 -0
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'schema_object'
2
4
 
3
5
  module Praxis
@@ -6,7 +8,8 @@ module Praxis
6
8
  class ParameterObject
7
9
  # https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#parameter-object
8
10
  attr_reader :location, :name, :is_required, :info
9
- def initialize(location: , name:, is_required:, info:)
11
+
12
+ def initialize(location:, name:, is_required:, info:)
10
13
  @location = location
11
14
  @name = name
12
15
  @info = info
@@ -25,39 +28,39 @@ module Praxis
25
28
  # style
26
29
  # explode
27
30
  # allowReserved
28
-
31
+
29
32
  # Now merge the rest schema and example
30
33
  # schema
31
34
  # example
32
35
  # examples (Example and Examples are mutually exclusive)
33
36
  schema = SchemaObject.new(info: info)
34
37
  h[:schema] = schema.dump_schema
35
- # Note: we do not support the 'content' key...we always use schema
38
+ # NOTE: we do not support the 'content' key...we always use schema
36
39
  h[:example] = schema.dump_example
37
40
  h
38
41
  end
39
42
 
40
- def self.process_parameters( action )
43
+ def self.process_parameters(action)
41
44
  output = []
42
- # An array, with one hash per param inside
45
+ # An array, with one hash per param inside
43
46
  if action.headers
44
- (action.headers.attributes||{}).each_with_object(output) do |(name, info), out|
45
- out << ParameterObject.new( location: 'header', name: name, is_required: info.options[:required], info: info ).dump
47
+ (action.headers.attributes || {}).each_with_object(output) do |(name, info), out|
48
+ out << ParameterObject.new(location: 'header', name: name, is_required: info.options[:required], info: info).dump
46
49
  end
47
50
  end
48
-
51
+
49
52
  if action.params
50
53
  route_params = \
51
- unless action.route
54
+ if action.route
55
+ action.route.path.named_captures.keys.collect(&:to_sym)
56
+ else
52
57
  warn "Warning: No routes defined for action #{action.name}"
53
58
  []
54
- else
55
- action.route.path.named_captures.keys.collect(&:to_sym)
56
59
  end
57
- (action.params.attributes||{}).each_with_object(output) do |(name, info), out|
60
+ (action.params.attributes || {}).each_with_object(output) do |(name, info), out|
58
61
  in_type = route_params.include?(name) ? :path : :query
59
- is_required = (in_type == :path ) ? true : info.options[:required]
60
- out << ParameterObject.new( location: in_type, name: name, is_required: is_required, info: info ).dump
62
+ is_required = in_type == :path ? true : info.options[:required]
63
+ out << ParameterObject.new(location: in_type, name: name, is_required: is_required, info: info).dump
61
64
  end
62
65
  end
63
66
 
@@ -1,10 +1,13 @@
1
- require_relative 'operation_object.rb'
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'operation_object'
2
4
  module Praxis
3
5
  module Docs
4
6
  module OpenApi
5
7
  class PathsObject
6
8
  # https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#paths-object
7
9
  attr_reader :resources, :paths
10
+
8
11
  def initialize(resources:)
9
12
  @resources = resources
10
13
  # A hash with keys of paths, and values of hash
@@ -15,18 +18,17 @@ module Praxis
15
18
  # "post": { ...}
16
19
  # "/humans": {
17
20
  # "get": {...},
18
- @paths = Hash.new {|h,k| h[k] = {} }
21
+ @paths = Hash.new { |h, k| h[k] = {} }
19
22
  end
20
23
 
21
-
22
24
  def dump
23
25
  resources.each do |resource|
24
- compute_resource_paths( resource )
26
+ compute_resource_paths(resource)
25
27
  end
26
28
  paths
27
29
  end
28
30
 
29
- def compute_resource_paths( resource )
31
+ def compute_resource_paths(resource)
30
32
  id = resource.id
31
33
  # fill in the paths hash with a key for each path for each action/route
32
34
  resource.actions.each do |action_name, action|
@@ -38,15 +40,15 @@ module Praxis
38
40
  path_entry = paths[templetized_path]
39
41
  # Let's fill in verb stuff within the working hash
40
42
  raise "VERB #{_verb} already defined for #{id}!?!?!" if path_entry[verb]
41
-
43
+
42
44
  action_uid = "action-#{action_name}-#{id}"
43
45
  # Add a tag matching the resource name (hoping all actions of a resource are grouped)
44
46
  action_tags = [resource.display_name]
45
- path_entry[verb] = OperationObject.new( id: action_uid, url: url, action: action, tags: action_tags).dump
47
+ path_entry[verb] = OperationObject.new(id: action_uid, url: url, action: action, tags: action_tags).dump
46
48
  end
47
- # For each path, we can further annotate with
49
+ # For each path, we can further annotate with
48
50
  # servers
49
- # parameters
51
+ # parameters
50
52
  # But we don't have that concept in praxis
51
53
  end
52
54
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'schema_object'
2
4
 
3
5
  module Praxis
@@ -5,7 +7,8 @@ module Praxis
5
7
  module OpenApi
6
8
  class RequestBodyObject
7
9
  # https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#request-body-object
8
- attr_reader :attribute
10
+ attr_reader :attribute
11
+
9
12
  def initialize(attribute:)
10
13
  @attribute = attribute
11
14
  end
@@ -18,18 +21,18 @@ module Praxis
18
21
  # OpenApi wants a set of bodies per MediaType/Content-Type
19
22
  # For us there's really only one schema (regardless of encoding)...
20
23
  # so we'll show all the supported MTs...but repeating the schema
21
- #dumped_schema = SchemaObject.new(info: attribute).dump_schema
24
+ # dumped_schema = SchemaObject.new(info: attribute).dump_schema
22
25
 
23
26
  example_handlers = if attribute.type < Praxis::Types::MultipartArray
24
- ident = MediaTypeIdentifier.load('multipart/form-data')
25
- [{ident.to_s => 'plain'}] # Multipart content type, but with the plain renderer (so there's no modification)
26
- else
27
- # TODO: We could run it through other handlers I guess...if they're registered
28
- [{'application/json' => 'json'}]
29
- end
30
-
31
- h[:content] = MediaTypeObject.create_content_attribute_helper(type: attribute.type,
32
- example_payload: attribute.example(nil),
27
+ ident = MediaTypeIdentifier.load('multipart/form-data')
28
+ [{ ident.to_s => 'plain' }] # Multipart content type, but with the plain renderer (so there's no modification)
29
+ else
30
+ # TODO: We could run it through other handlers I guess...if they're registered
31
+ [{ 'application/json' => 'json' }]
32
+ end
33
+
34
+ h[:content] = MediaTypeObject.create_content_attribute_helper(type: attribute.type,
35
+ example_payload: attribute.example(nil),
33
36
  example_handlers: example_handlers)
34
37
  # # Key string (of MT) , value MTObject
35
38
  # content_hash = info[:examples].each_with_object({}) do |(handler, example_hash),accum|
@@ -43,8 +46,6 @@ module Praxis
43
46
  # h[:content] = content_hash
44
47
  h
45
48
  end
46
-
47
-
48
49
  end
49
50
  end
50
51
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'media_type_object'
2
4
 
3
5
  module Praxis
@@ -6,33 +8,39 @@ module Praxis
6
8
  class ResponseObject
7
9
  # https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#response-object
8
10
  attr_reader :info
11
+
9
12
  def initialize(info:)
10
13
  @info = info
11
14
  default_handlers = ApiDefinition.instance.info.produces
12
- @output_handlers = Praxis::Application.instance.handlers.select do |k,v|
15
+ @output_handlers = Praxis::Application.instance.handlers.select do |k, _v|
13
16
  default_handlers.include?(k)
14
17
  end
15
18
  end
16
19
 
17
- def dump_response_headers_object( headers )
18
- headers.each_with_object({}) do |(name,data),accum|
19
- # each header comes from Praxis::ResponseDefinition
20
+ def dump_response_headers_object(headers)
21
+ headers.each_with_object({}) do |(name, data), accum|
22
+ # each header comes from Praxis::ResponseDefinition
20
23
  # the keys are the header names, and value can be:
21
24
  # "true" => means it only needs to exist
22
25
  # String => which means that it has to fully match
23
26
  # Regex => which means it has to regexp match it
24
27
 
25
28
  # Get the schema from the type (defaulting to string in case the type doesn't have the as_json_schema defined)
26
- schema = data[:attribute].type.as_json_schema rescue { type: :string }
29
+ schema = begin
30
+ data[:attribute].type.as_json_schema
31
+ rescue StandardError
32
+ { type: :string }
33
+ end
27
34
  hash = { description: data[:description] || '', schema: schema }
28
- # Note, our Headers in response definition are not full types...they're basically only
35
+ # Note, our Headers in response definition are not full types...they're basically only
29
36
  # strings, which can either match anything, match the exact word or match a regex
30
37
  # they don't even have a description...
31
38
  data_value = data[:value]
32
- if data_value.is_a? String
39
+ case data_value
40
+ when String
33
41
  hash[:pattern] = "^#{data_value}$" # Exact String match
34
- elsif data_value.is_a? Regexp
35
- sanitized_pattern = data_value.inspect[1..-2] #inspect returns enclosing '/' characters
42
+ when Regexp
43
+ sanitized_pattern = data_value.inspect[1..-2] # inspect returns enclosing '/' characters
36
44
  hash[:pattern] = sanitized_pattern
37
45
  end
38
46
 
@@ -41,10 +49,10 @@ module Praxis
41
49
  end
42
50
 
43
51
  def dump
44
- data = {
52
+ data = {
45
53
  description: info.description || ''
46
54
  }
47
- if headers_object = dump_response_headers_object( info.headers )
55
+ if (headers_object = dump_response_headers_object(info.headers))
48
56
  data[:headers] = headers_object
49
57
  end
50
58
 
@@ -52,12 +60,13 @@ module Praxis
52
60
 
53
61
  identifier = MediaTypeIdentifier.load(info.media_type.identifier)
54
62
  example_handlers = @output_handlers.each_with_object([]) do |(name, _handler), accum|
55
- accum.push({ (identifier + name).to_s => name})
63
+ accum.push({ (identifier + name).to_s => name })
56
64
  end
57
65
  data[:content] = MediaTypeObject.create_content_attribute_helper(
58
- type: info.media_type,
59
- example_payload: info.example(nil),
60
- example_handlers: example_handlers)
66
+ type: info.media_type,
67
+ example_payload: info.example(nil),
68
+ example_handlers: example_handlers
69
+ )
61
70
  end
62
71
 
63
72
  # if payload = info[:payload]
@@ -66,12 +75,9 @@ module Praxis
66
75
  # data[:schema] = {"$ref" => "#/definitions/#{body_type}" }
67
76
  # end
68
77
 
69
-
70
78
  # TODO: we do not support 'links'
71
79
  data
72
80
  end
73
-
74
-
75
81
  end
76
82
  end
77
83
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'response_object'
2
4
 
3
5
  module Praxis
@@ -6,11 +8,11 @@ module Praxis
6
8
  class ResponsesObject
7
9
  # https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#responses-object
8
10
  attr_reader :responses
11
+
9
12
  def initialize(responses:)
10
13
  @responses = responses
11
14
  end
12
15
 
13
-
14
16
  def dump
15
17
  # {
16
18
  # "200": {
@@ -1,35 +1,70 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Praxis
2
4
  module Docs
3
5
  module OpenApi
4
6
  class SchemaObject
5
7
  # https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#schema-object
6
- attr_reader :type, :attribute
8
+ attr_reader :type
9
+
7
10
  def initialize(info:)
8
- #info could be an attribute ... or a type?
9
- if info.is_a? Attributor::Attribute
10
- @attribute = info
11
+ @attribute_options = {}
12
+
13
+ # info could be an attribute ... or a type
14
+ if info.is_a?(Attributor::Attribute)
15
+ @type = info.type
16
+ # Save the options that might be attached to the attribute, to add them to the type schema later
17
+ @attribute_options = info.options
11
18
  else
12
19
  @type = info
13
20
  end
21
+
22
+ # Mediatypes have the description method, lower types don't
23
+ @attribute_options[:description] = @type.description if @type.respond_to?(:description)
24
+ @collection = type.respond_to?(:member_type)
14
25
  end
15
26
 
16
27
  def dump_example
17
- ex = \
18
- if attribute
19
- attribute.example
20
- else
21
- type.example
22
- end
28
+ ex = type.example
23
29
  ex.respond_to?(:dump) ? ex.dump : ex
24
30
  end
25
31
 
26
- def dump_schema
27
- if attribute
28
- attribute.as_json_schema(shallow: true, example: nil)
32
+ def dump_schema(shallow: false, allow_ref: false)
33
+ # We will dump schemas for mediatypes by simply creating a reference to the components' section
34
+ if type < Attributor::Container
35
+ if (type < Praxis::Blueprint || type < Attributor::Model) && allow_ref && !type.anonymous?
36
+ # TODO: Do we even need a description?
37
+ h = @attribute_options[:description] ? { 'description' => @attribute_options[:description] } : {}
38
+
39
+ Praxis::Docs::OpenApiGenerator.instance.register_seen_component(type)
40
+ h.merge!('$ref' => "#/components/schemas/#{type.id}")
41
+ elsif @collection
42
+ items = OpenApi::SchemaObject.new(info: type.member_type).dump_schema(allow_ref: allow_ref, shallow: false)
43
+ h = @attribute_options[:description] ? { 'description' => @attribute_options[:description] } : {}
44
+ h.merge!(type: 'array', items: items)
45
+ else # Attributor::Struct, etc
46
+ props = type.attributes.transform_values do |definition|
47
+ OpenApi::SchemaObject.new(info: definition).dump_schema(allow_ref: true, shallow: shallow)
48
+ end
49
+ h = { type: :object, properties: props } # TODO: Example?
50
+ end
29
51
  else
30
- type.as_json_schema(shallow: true, example: nil)
52
+ # OpenApi::SchemaObject.new(info:target).dump_schema(allow_ref: allow_ref, shallow: shallow)
53
+ # TODO...we need to make sure we can use refs in the underlying components after the first level...
54
+ # ... maybe we need to loop over the attributes if it's an object/struct?...
55
+ h = type.as_json_schema(shallow: shallow, example: nil, attribute_options: @attribute_options)
31
56
  end
32
- # # TODO: FIXME: return a generic object type if the passed info was weird.
57
+
58
+ # Tag on OpenAPI specific requirements that aren't already added in the underlying JSON schema model
59
+ # Nullable: (it seems we need to ensure there is a null option to the enum, if there is one)
60
+ if @attribute_options[:null]
61
+ h[:nullable] = @attribute_options[:null]
62
+ h[:enum] = h[:enum] + [nil] if h[:enum] && !h[:enum].include?(nil)
63
+ end
64
+
65
+ h
66
+
67
+ # # TODO: FIXME: return a generic object type if the passed info was weird.
33
68
  # return { type: :object } unless info
34
69
 
35
70
  # h = {
@@ -41,7 +76,7 @@ module Praxis
41
76
  # h[:default] = info[:default] if info[:default]
42
77
  # h[:pattern] = info[:regexp] if info[:regexp]
43
78
  # # TODO: there are other possible things we can do..maximum, minimum...etc
44
-
79
+
45
80
  # if h[:type] == :array
46
81
  # # FIXME: ... hack it for MultiPart arrays...where there's no member attr
47
82
  # member_type = info[:type][:member_attribute]
@@ -51,27 +86,25 @@ module Praxis
51
86
  # h[:items] = SchemaObject.new(info: member_type ).dump_schema
52
87
  # end
53
88
  # h
54
- rescue => e
89
+ rescue StandardError => e
55
90
  puts "Error dumping schema #{e}"
56
91
  end
57
-
58
- def convert_family_to_json_type( praxis_type )
92
+
93
+ def convert_family_to_json_type(praxis_type)
59
94
  case praxis_type[:family].to_sym
60
95
  when :string
61
96
  :string
62
97
  when :hash
63
98
  :object
64
- when :array #Warning! Multipart types are arrays!
99
+ when :array # Warning! Multipart types are arrays!
65
100
  :array
66
101
  when :numeric
67
- case praxis_type[:id]
68
- when 'Attributor-Integer'
69
- :integer
70
- when 'Attributor-BigDecimal'
71
- :integer
72
- when 'Attributor-Float'
73
- :number
74
- end
102
+ jtypes = {
103
+ 'Attributor-Integer' => :integer,
104
+ 'Attributor-BigDecimal' => :integer,
105
+ 'Attributor-Float' => :number
106
+ }
107
+ jtypes[praxis_type[:id]]
75
108
  when :temporal
76
109
  :string
77
110
  when :boolean
@@ -80,7 +113,6 @@ module Praxis
80
113
  raise "Unknown praxis family type: #{praxis_type[:family]}"
81
114
  end
82
115
  end
83
-
84
116
  end
85
117
  end
86
118
  end
@@ -1,10 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Praxis
2
4
  module Docs
3
5
  module OpenApi
4
6
  class ServerObject
5
7
  # https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#server-object
6
8
  attr_reader :url, :description, :variables
7
- def initialize(url: , description: nil, variables: [])
9
+
10
+ def initialize(url:, description: nil, variables: [])
8
11
  @url = url
9
12
  @description = description
10
13
  @variables = variables
@@ -12,7 +15,7 @@ module Praxis
12
15
  end
13
16
 
14
17
  def dump
15
- result = {url: url}
18
+ result = { url: url }
16
19
  result[:description] = description if description
17
20
  result[:variables] = variables unless variables.empty?
18
21
 
@@ -1,19 +1,22 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Praxis
2
4
  module Docs
3
5
  module OpenApi
4
6
  class TagObject
5
7
  attr_reader :name, :description
6
- def initialize(name:,description: )
8
+
9
+ def initialize(name:, description:)
7
10
  @name = name
8
11
  @description = description
9
12
  end
10
13
 
11
14
  def dump
12
- {
13
- name: name,
14
- description: description,
15
- #externalDocs: ???,
16
- }
15
+ h = description ? { description: description } : {}
16
+ h.merge(
17
+ name: name
18
+ # externalDocs: ???,
19
+ )
17
20
  end
18
21
  end
19
22
  end