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

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.
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