praxis 2.0.pre.18 → 2.0.pre.19

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 +6 -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 +64 -94
  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 +64 -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 +10 -13
  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 +6 -3
  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 +16 -16
  45. data/lib/praxis/docs/open_api/server_object.rb +5 -2
  46. data/lib/praxis/docs/open_api/tag_object.rb +6 -3
  47. data/lib/praxis/docs/open_api_generator.rb +92 -95
  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 +171 -180
  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 +46 -47
  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 -16
  113. data/lib/praxis/request_stages/validate_payload.rb +25 -27
  114. data/lib/praxis/request_superclassing.rb +3 -3
  115. data/lib/praxis/resource_definition.rb +1 -0
  116. data/lib/praxis/response.rb +13 -25
  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 +15 -15
  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 +88 -112
  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 +40 -52
  145. data/spec/praxis/action_definition_spec.rb +36 -46
  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 +27 -30
  173. data/spec/praxis/mapper/selector_generator_spec.rb +50 -50
  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 +28 -39
  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 -18
  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 +5 -5
  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 +9 -15
  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 +2 -1
  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 +20 -18
  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,35 +1,33 @@
1
- module Praxis
1
+ # frozen_string_literal: true
2
2
 
3
+ module Praxis
3
4
  CONTEXT_FOR = {
4
- params: [Attributor::ROOT_PREFIX, "params".freeze],
5
- headers: [Attributor::ROOT_PREFIX, "headers".freeze],
6
- payload: [Attributor::ROOT_PREFIX, "payload".freeze]
5
+ params: [Attributor::ROOT_PREFIX, 'params'],
6
+ headers: [Attributor::ROOT_PREFIX, 'headers'],
7
+ payload: [Attributor::ROOT_PREFIX, 'payload']
7
8
  }.freeze
8
9
 
9
10
  class Dispatcher
10
- attr_reader :controller
11
- attr_reader :action
12
- attr_reader :request
13
- attr_reader :application
11
+ attr_reader :controller, :action, :request, :application
14
12
 
15
- @deferred_callbacks = Hash.new do |hash,stage|
16
- hash[stage] = {before: [], after:[]}
13
+ @deferred_callbacks = Hash.new do |hash, stage|
14
+ hash[stage] = { before: [], after: [] }
17
15
  end
18
16
 
19
17
  class << self
20
18
  attr_reader :deferred_callbacks
21
19
  end
22
20
 
23
- def self.before(*stage_path, **conditions, &block)
21
+ def self.before(*_stage_path, **conditions, &block)
24
22
  @deferred_callbacks[:before] << [conditions, block]
25
23
  end
26
24
 
27
- def self.after(*stage_path, **conditions, &block)
25
+ def self.after(*_stage_path, **conditions, &block)
28
26
  @deferred_callbacks[:after] << [conditions, block]
29
27
  end
30
28
 
31
29
  def self.current(thread: Thread.current, application: Application.instance)
32
- thread[:praxis_dispatcher] ||= self.new(application: application)
30
+ thread[:praxis_dispatcher] ||= new(application: application)
33
31
  end
34
32
 
35
33
  def initialize(application: Application.instance)
@@ -43,20 +41,18 @@ module Praxis
43
41
  @stages << RequestStages::Validate.new(:validate, self)
44
42
  @stages << RequestStages::Action.new(:action, self)
45
43
  @stages << RequestStages::Response.new(:response, self)
46
- @stages.each do |s|
47
- s.setup!
48
- end
44
+ @stages.each(&:setup!)
49
45
  setup_deferred_callbacks!
50
46
  end
51
47
 
52
48
  def setup_deferred_callbacks!
53
49
  self.class.deferred_callbacks.each do |stage_name, callbacks|
54
50
  callbacks[:before].each do |(*stage_path, block)|
55
- self.before(stage_name, *stage_path, &block)
51
+ before(stage_name, *stage_path, &block)
56
52
  end
57
53
 
58
54
  callbacks[:after].each do |(*stage_path, block)|
59
- self.after(stage_name, *stage_path, &block)
55
+ after(stage_name, *stage_path, &block)
60
56
  end
61
57
  end
62
58
  end
@@ -76,38 +72,35 @@ module Praxis
76
72
  @action = action
77
73
  @request = request
78
74
 
79
- payload = {request: request, response: nil, controller: @controller}
80
-
81
- instrumented_dispatch( payload )
75
+ payload = { request: request, response: nil, controller: @controller }
82
76
 
77
+ instrumented_dispatch(payload)
83
78
  ensure
84
79
  @controller = nil
85
80
  @action = nil
86
81
  @request = nil
87
82
  end
88
83
 
89
- def instrumented_dispatch( payload )
90
- Notifications.instrument 'praxis.request.all'.freeze, payload do
91
- begin
92
- # the response stage must be the final stage in the list
93
- *stages, response_stage = @stages
94
-
95
- stages.each do |stage|
96
- result = stage.run
97
- case result
98
- when Response
99
- controller.response = result
100
- break
101
- end
84
+ def instrumented_dispatch(payload)
85
+ Notifications.instrument 'praxis.request.all', payload do
86
+ # the response stage must be the final stage in the list
87
+ *stages, response_stage = @stages
88
+
89
+ stages.each do |stage|
90
+ result = stage.run
91
+ case result
92
+ when Response
93
+ controller.response = result
94
+ break
102
95
  end
96
+ end
103
97
 
104
- response_stage.run
98
+ response_stage.run
105
99
 
106
- payload[:response] = controller.response
107
- controller.response.finish
108
- rescue => e
109
- @application.error_handler.handle!(request, e)
110
- end
100
+ payload[:response] = controller.response
101
+ controller.response.finish
102
+ rescue StandardError => e
103
+ @application.error_handler.handle!(request, e)
111
104
  end
112
105
  end
113
106
 
@@ -116,9 +109,8 @@ module Praxis
116
109
  return unless Praxis::Blueprint.caching_enabled?
117
110
 
118
111
  Praxis::Blueprint.cache = Hash.new do |hash, key|
119
- hash[key] = Hash.new
112
+ hash[key] = {}
120
113
  end
121
114
  end
122
-
123
115
  end
124
116
  end
@@ -1,9 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Praxis
2
4
  module Docs
3
5
  module OpenApi
4
6
  class InfoObject
5
7
  attr_reader :info, :version
6
- def initialize(version: , api_definition_info: )
8
+
9
+ def initialize(version:, api_definition_info:)
7
10
  @version = version
8
11
  @info = api_definition_info
9
12
  raise "OpenApi docs require a 'Title' for your API." unless info.title
@@ -11,12 +14,12 @@ module Praxis
11
14
 
12
15
  def dump
13
16
  data = { version: version }
14
- [
15
- :title,
16
- :description,
17
- :termsOfService,
18
- :contact,
19
- :license
17
+ %i[
18
+ title
19
+ description
20
+ termsOfService
21
+ contact
22
+ license
20
23
  ].each do |attr|
21
24
  val = info.send(attr)
22
25
  data[attr] = val if val
@@ -27,7 +30,7 @@ module Praxis
27
30
  if info.logo_url
28
31
  data[:'x-logo'] = {
29
32
  url: info.logo_url,
30
- backgroundColor: "#FFFFFF",
33
+ backgroundColor: '#FFFFFF',
31
34
  altText: info.title
32
35
  }
33
36
  end
@@ -1,8 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Praxis
2
4
  module Docs
3
5
  module OpenApi
4
6
  class MediaTypeObject
5
7
  attr_reader :schema, :example
8
+
6
9
  def initialize(schema:, example:)
7
10
  @schema = schema
8
11
  @example = example
@@ -10,26 +13,24 @@ module Praxis
10
13
 
11
14
  def dump
12
15
  {
13
- schema: schema,
14
- example: example,
15
- # encoding: TODO SUPPORT IT maybe be great/necessary for multipart
16
+ schema: schema,
17
+ example: example
18
+ # encoding: TODO SUPPORT IT maybe be great/necessary for multipart
16
19
  }
17
20
  end
18
21
 
19
22
  # Helper to create the typical content attribute for responses and request bodies
20
- def self.create_content_attribute_helper(type: , example_payload:, example_handlers: nil)
23
+ def self.create_content_attribute_helper(type:, example_payload:, example_handlers: nil)
21
24
  # Will produce 1 example encoded with a given handler (and marking it with the given content type)
22
- unless example_handlers
23
- example_handlers = [ {'application/json' => 'json' } ]
24
- end
25
+ example_handlers ||= [{ 'application/json' => 'json' }]
25
26
  # NOTE2: we should just create a $ref here unless it's an anon mediatype...
26
- return {} if type.is_a? SimpleMediaType # NOTE: skip if it's a SimpleMediaType?? ... is that correct?
27
+ return {} if type.is_a? SimpleMediaType # NOTE: skip if it's a SimpleMediaType?? ... is that correct?
27
28
 
28
- the_schema = if type.anonymous? || ! (type < Praxis::MediaType) # Avoid referencing custom/simple Types? (i.e., just MTs)
29
- SchemaObject.new(info: type).dump_schema
30
- else
31
- { '$ref': "#/components/schemas/#{type.id}" }
32
- end
29
+ the_schema = if type.anonymous? || !(type < Praxis::MediaType) # Avoid referencing custom/simple Types? (i.e., just MTs)
30
+ SchemaObject.new(info: type).dump_schema
31
+ else
32
+ { '$ref': "#/components/schemas/#{type.id}" }
33
+ end
33
34
 
34
35
  if example_payload
35
36
  examples_by_content_type = {}
@@ -40,16 +41,16 @@ module Praxis
40
41
  handler = Praxis::Application.instance.handlers[handler_name]
41
42
  # ReDoc is not happy to display json generated outputs when served as JSON...wtf?
42
43
  generated = handler.generate(rendered_payload)
43
- final = ( handler_name == 'json') ? JSON.parse(generated) : generated
44
+ final = handler_name == 'json' ? JSON.parse(generated) : generated
44
45
  examples_by_content_type[content_type] = final
45
46
  end
46
47
  end
47
48
 
48
49
  # Key string (of MT) , value MTObject
49
- content_hash = examples_by_content_type.each_with_object({}) do |(content_type, example_hash),accum|
50
- accum[content_type] = MediaTypeObject.new(
50
+ examples_by_content_type.transform_values do |example_hash|
51
+ MediaTypeObject.new(
51
52
  schema: the_schema, # Every MT will have the same exact type..oh well .. maybe a REF?
52
- example: example_hash,
53
+ example: example_hash
53
54
  ).dump
54
55
  end
55
56
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'parameter_object'
2
4
  require_relative 'request_body_object'
3
5
  require_relative 'responses_object'
@@ -8,6 +10,7 @@ module Praxis
8
10
  class OperationObject
9
11
  # https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#operation-object
10
12
  attr_reader :id, :url, :action, :tags
13
+
11
14
  def initialize(id:, url:, action:, tags:)
12
15
  @id = id
13
16
  @url = url
@@ -21,9 +24,9 @@ module Praxis
21
24
  h = {
22
25
  summary: action.name.to_s,
23
26
  description: action.description,
24
- #externalDocs: {}, # TODO/FIXME
27
+ # externalDocs: {}, # TODO/FIXME
25
28
  operationId: id,
26
- responses: ResponsesObject.new(responses: action.responses).dump,
29
+ responses: ResponsesObject.new(responses: action.responses).dump
27
30
  # callbacks
28
31
  # deprecated: false
29
32
  # security: [{}]
@@ -31,7 +34,7 @@ module Praxis
31
34
  }
32
35
  h[:tags] = all_tags.uniq unless all_tags.empty?
33
36
  h[:parameters] = all_parameters unless all_parameters.empty?
34
- h[:requestBody] = RequestBodyObject.new(attribute: action.payload ).dump if action.payload
37
+ h[:requestBody] = RequestBodyObject.new(attribute: action.payload).dump if action.payload
35
38
  h
36
39
  end
37
40
  end
@@ -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,11 +1,14 @@
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
8
  attr_reader :type, :attribute
9
+
7
10
  def initialize(info:)
8
- #info could be an attribute ... or a type?
11
+ # info could be an attribute ... or a type?
9
12
  if info.is_a? Attributor::Attribute
10
13
  @attribute = info
11
14
  else
@@ -29,7 +32,7 @@ module Praxis
29
32
  else
30
33
  type.as_json_schema(shallow: true, example: nil)
31
34
  end
32
- # # TODO: FIXME: return a generic object type if the passed info was weird.
35
+ # # TODO: FIXME: return a generic object type if the passed info was weird.
33
36
  # return { type: :object } unless info
34
37
 
35
38
  # h = {
@@ -41,7 +44,7 @@ module Praxis
41
44
  # h[:default] = info[:default] if info[:default]
42
45
  # h[:pattern] = info[:regexp] if info[:regexp]
43
46
  # # TODO: there are other possible things we can do..maximum, minimum...etc
44
-
47
+
45
48
  # if h[:type] == :array
46
49
  # # FIXME: ... hack it for MultiPart arrays...where there's no member attr
47
50
  # member_type = info[:type][:member_attribute]
@@ -51,27 +54,25 @@ module Praxis
51
54
  # h[:items] = SchemaObject.new(info: member_type ).dump_schema
52
55
  # end
53
56
  # h
54
- rescue => e
57
+ rescue StandardError => e
55
58
  puts "Error dumping schema #{e}"
56
59
  end
57
-
58
- def convert_family_to_json_type( praxis_type )
60
+
61
+ def convert_family_to_json_type(praxis_type)
59
62
  case praxis_type[:family].to_sym
60
63
  when :string
61
64
  :string
62
65
  when :hash
63
66
  :object
64
- when :array #Warning! Multipart types are arrays!
67
+ when :array # Warning! Multipart types are arrays!
65
68
  :array
66
69
  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
70
+ jtypes = {
71
+ 'Attributor-Integer' => :integer,
72
+ 'Attributor-BigDecimal' => :integer,
73
+ 'Attributor-Float' => :number
74
+ }
75
+ jtypes[praxis_type[:id]]
75
76
  when :temporal
76
77
  :string
77
78
  when :boolean
@@ -80,7 +81,6 @@ module Praxis
80
81
  raise "Unknown praxis family type: #{praxis_type[:family]}"
81
82
  end
82
83
  end
83
-
84
84
  end
85
85
  end
86
86
  end