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,56 +1,71 @@
1
- require_relative 'open_api/info_object.rb'
2
- require_relative 'open_api/server_object.rb'
3
- require_relative 'open_api/paths_object.rb'
4
- require_relative 'open_api/tag_object.rb'
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'open_api/info_object'
4
+ require_relative 'open_api/server_object'
5
+ require_relative 'open_api/paths_object'
6
+ require_relative 'open_api/tag_object'
5
7
 
6
8
  module Praxis
7
9
  module Docs
8
-
9
10
  class OpenApiGenerator
10
11
  require 'active_support/core_ext/enumerable' # For index_by
12
+ include Singleton
11
13
 
12
- API_DOCS_DIRNAME = 'docs/openapi'
14
+ API_DOCS_DIRNAME = 'docs/openapi'
13
15
  EXCLUDED_TYPES_FROM_OUTPUT = Set.new([
14
- Attributor::Boolean,
15
- Attributor::CSV,
16
- Attributor::DateTime,
17
- Attributor::Date,
18
- Attributor::Float,
19
- Attributor::Hash,
20
- Attributor::Ids,
21
- Attributor::Integer,
22
- Attributor::Object,
23
- Attributor::String,
24
- Attributor::Symbol,
25
- Attributor::URI,
26
- ]).freeze
27
-
28
- attr_reader :resources_by_version, :types_by_id, :infos_by_version, :doc_root_dir
16
+ Attributor::Boolean,
17
+ Attributor::CSV,
18
+ Attributor::DateTime,
19
+ Attributor::Date,
20
+ Attributor::Float,
21
+ Attributor::Hash,
22
+ Attributor::Ids,
23
+ Attributor::Integer,
24
+ Attributor::Object,
25
+ Attributor::String,
26
+ Attributor::Symbol,
27
+ Attributor::URI
28
+ ]).freeze
29
+
30
+ attr_reader :resources_by_version, :infos_by_version, :doc_root_dir
29
31
 
30
32
  # substitutes ":params_like_so" for {params_like_so}
31
- def self.templatize_url( string )
33
+ def self.templatize_url(string)
32
34
  Mustermann.new(string).to_templates.first
33
35
  end
34
36
 
35
37
  def save!
38
+ raise 'You need to configure the root directory before saving (configure_root(<dir>))' unless @root
39
+
36
40
  initialize_directories
37
41
  # Restrict the versions listed in the index file to the ones for which we have at least 1 resource
38
- write_index_file( for_versions: resources_by_version.keys )
39
- resources_by_version.keys.each do |version|
42
+ write_index_file(for_versions: resources_by_version.keys)
43
+ resources_by_version.each_key do |version|
44
+ @seen_components_for_current_version = Set.new
40
45
  write_version_file(version)
41
46
  end
42
47
  end
43
48
 
44
- def initialize(root)
49
+ def initialize
45
50
  require 'yaml'
46
- @root = root
47
- @resources_by_version = Hash.new do |h,k|
51
+
52
+ @resources_by_version = Hash.new do |h, k|
48
53
  h[k] = Set.new
49
54
  end
50
-
55
+ # List of types that we have seen/marked as necessary to list in the components/schemas section
56
+ # These should contain any mediatype define in the versioned controllers plus any type
57
+ # for which we've explicitly rendered a $ref schema
58
+ @seen_components_for_current_version = Set.new
51
59
  @infos = ApiDefinition.instance.infos
52
60
  collect_resources
53
- collect_types
61
+ end
62
+
63
+ def configure_root(root)
64
+ @root = root
65
+ end
66
+
67
+ def register_seen_component(type)
68
+ @seen_components_for_current_version.add(type)
54
69
  end
55
70
 
56
71
  private
@@ -60,6 +75,7 @@ module Praxis
60
75
  Praxis::Application.instance.endpoint_definitions.map do |resource|
61
76
  # skip resources with doc_visibility of :none
62
77
  next if resource.metadata[:doc_visibility] == :none
78
+
63
79
  version = resource.version
64
80
  # TODO: it seems that we shouldn't hardcode n/a in Praxis
65
81
  # version = "unversioned" if version == "n/a"
@@ -67,88 +83,41 @@ module Praxis
67
83
  end
68
84
  end
69
85
 
70
- def collect_types
71
- @types_by_id = ObjectSpace.each_object( Class ).select do |obj|
72
- obj < Attributor::Type
73
- end.index_by(&:id)
74
- end
75
-
76
- def write_index_file( for_versions: )
86
+ def write_index_file(for_versions:)
77
87
  # TODO. create a simple html file that can link to the individual versions available
78
88
  end
79
89
 
80
- def scan_types_for_version(version, dumped_resources)
81
- found_media_types = resources_by_version[version].select{|r| r.media_type}.collect {|r| r.media_type.describe }
82
-
90
+ # TODO: Change this function name to scan_default_mediatypes...
91
+ def collect_default_mediatypes(endpoints)
83
92
  # We'll start by processing the rendered mediatypes
84
- processed_types = Set.new(resources_by_version[version].select do|r|
85
- r.media_type && !r.media_type.is_a?(Praxis::SimpleMediaType)
93
+ Set.new(endpoints.select do |endpoint|
94
+ endpoint.media_type && !endpoint.media_type.is_a?(Praxis::SimpleMediaType)
86
95
  end.collect(&:media_type))
87
-
88
- newfound = Set.new
89
- found_media_types.each do |mt|
90
- newfound += scan_dump_for_types( { type: mt} , processed_types )
91
- end
92
- # Then will process the rendered resources (noting)
93
- newfound += scan_dump_for_types( dumped_resources, Set.new )
94
-
95
- # At this point we've done a scan of the dumped resources and mediatypes.
96
- # In that scan we've discovered a bunch of types, however, many of those might have appeared in the JSON
97
- # rendered in just shallow mode, so it is not guaranteed that we've seen all the available types.
98
- # For that we'll do a (non-shallow) dump of all the types we found, and scan them until the scans do not
99
- # yield types we haven't seen before
100
- while !newfound.empty? do
101
- dumped = newfound.collect(&:describe)
102
- processed_types += newfound
103
- newfound = scan_dump_for_types( dumped, processed_types )
104
- end
105
- processed_types
106
- end
107
-
108
- def scan_dump_for_types( data, processed_types )
109
- newfound_types = Set.new
110
- case data
111
- when Array
112
- data.collect{|item| newfound_types += scan_dump_for_types( item , processed_types ) }
113
- when Hash
114
- if data.key?(:type) && data[:type].kind_of?(Hash) && ( [:id,:name,:family] - data[:type].keys ).empty?
115
- type_id = data[:type][:id]
116
- unless type_id.nil? || type_id == Praxis::SimpleMediaType.id #SimpleTypes shouldn't be collected
117
- unless types_by_id[type_id]
118
- raise "Error! We have detected a reference to a 'Type' with id='#{type_id}' which is not derived from Attributor::Type" +
119
- " Document generation cannot proceed."
120
- end
121
- newfound_types << types_by_id[type_id] unless processed_types.include? types_by_id[type_id]
122
- end
123
- end
124
- data.values.map{|item| newfound_types += scan_dump_for_types( item , processed_types)}
125
- end
126
- newfound_types
127
96
  end
128
97
 
129
- def write_version_file( version )
98
+ def write_version_file(version)
130
99
  # version_info = infos_by_version[version]
131
100
  # # Hack, let's "inherit/copy" all traits of a version from the global definition
132
101
  # # Eventually traits should be defined for a version (and inheritable from global) so we'll emulate that here
133
102
  # version_info[:traits] = infos_by_version[:traits]
134
- dumped_resources = dump_resources( resources_by_version[version] )
135
- processed_types = scan_types_for_version(version, dumped_resources)
136
103
 
104
+ # We'll for sure include any of the default mediatypes in the endpoints for this version
105
+ @seen_components_for_current_version.merge(collect_default_mediatypes(resources_by_version[version]))
137
106
  # Here we have:
138
- # processed types: which includes mediatypes and normal types...real classes
107
+ # processed types: which includes default mediatypes for the processed endpoints
139
108
  # processed resources for this version: resources_by_version[version]
140
109
 
141
110
  info_object = OpenApi::InfoObject.new(version: version, api_definition_info: @infos[version])
142
111
  # We only support a server in Praxis ... so we'll use the base path
143
- server_object = OpenApi::ServerObject.new( url: @infos[version].base_path )
144
-
145
- paths_object = OpenApi::PathsObject.new( resources: resources_by_version[version])
112
+ server_object = OpenApi::ServerObject.new(url: @infos[version].base_path)
113
+
114
+ paths_object = OpenApi::PathsObject.new(resources: resources_by_version[version])
146
115
 
147
116
  full_data = {
148
- openapi: "3.0.2",
117
+ openapi: '3.0.2',
149
118
  info: info_object.dump,
150
119
  servers: [server_object.dump],
151
- paths: paths_object.dump,
120
+ paths: paths_object.dump
152
121
  # responses: {}, #TODO!! what do we get here? the templates?...need to transform to "Responses Definitions Object"
153
122
  # securityDefinitions: {}, # NOTE: No security definitions in Praxis
154
123
  # security: [], # NOTE: No security definitions in Praxis
@@ -157,29 +126,25 @@ module Praxis
157
126
  # Create the top level tags by:
158
127
  # 1- First adding all the resource display names (and descriptions)
159
128
  tags_for_resources = resources_by_version[version].collect do |resource|
160
- OpenApi::TagObject.new(name: resource.display_name, description: resource.description ).dump
129
+ OpenApi::TagObject.new(name: resource.display_name, description: resource.description).dump
161
130
  end
162
131
  full_data[:tags] = tags_for_resources
163
132
  # 2- Then adding all of the top level traits but marking them special with the x-traitTag (of Redoc)
164
- tags_for_traits = (ApiDefinition.instance.traits).collect do |name, info|
165
- OpenApi::TagObject.new(name: name, description: info.description).dump.merge(:'x-traitTag' => true)
166
- end
167
- unless tags_for_traits.empty?
168
- full_data[:tags] = full_data[:tags] + tags_for_traits
133
+ tags_for_traits = ApiDefinition.instance.traits.collect do |name, info|
134
+ OpenApi::TagObject.new(name: name, description: info.description).dump.merge('x-traitTag': true)
169
135
  end
136
+ full_data[:tags] = full_data[:tags] + tags_for_traits unless tags_for_traits.empty?
137
+
138
+ # Include only MTs and Blueprints (i.e., no simple types...)
139
+ component_schemas = add_component_schemas(@seen_components_for_current_version.clone, {})
170
140
 
171
- # Include only MTs (i.e., not custom types or simple types...)
172
- component_schemas = reusable_schema_objects(processed_types.select{|t| t < Praxis::MediaType})
173
-
174
141
  # 3- Then adding all of the top level Mediatypes...so we can present them at the bottom, otherwise they don't show
175
- tags_for_mts = component_schemas.map do |(name, info)|
142
+ tags_for_mts = component_schemas.map do |(name, _info)|
176
143
  special_redoc_anchor = "<SchemaDefinition schemaRef=\"#/components/schemas/#{name}\" showReadOnly={true} showWriteOnly={true} />"
177
144
  guessed_display = name.split('-').last # TODO!!!the informational hash does not seem to come with the "description" value set...hmm
178
- OpenApi::TagObject.new(name: name, description: special_redoc_anchor).dump.merge(:'x-displayName' => guessed_display)
179
- end
180
- unless tags_for_mts.empty?
181
- full_data[:tags] = full_data[:tags] + tags_for_mts
145
+ OpenApi::TagObject.new(name: name, description: special_redoc_anchor).dump.merge('x-displayName': guessed_display)
182
146
  end
147
+ full_data[:tags] = full_data[:tags] + tags_for_mts unless tags_for_mts.empty?
183
148
 
184
149
  # Include all the reusable schemas in the components hash
185
150
  full_data[:components] = {
@@ -187,25 +152,25 @@ module Praxis
187
152
  }
188
153
 
189
154
  # REDOC specific grouping of sidebar
190
- resource_tags = { name: 'Resources', tags: tags_for_resources.map{|t| t[:name]} }
191
- schema_tags = { name: 'Models', tags: tags_for_mts.map{|t| t[:name]} }
155
+ resource_tags = { name: 'Resources', tags: tags_for_resources.map { |t| t[:name] } }
156
+ schema_tags = { name: 'Models', tags: tags_for_mts.map { |t| t[:name] } }
192
157
  full_data['x-tagGroups'] = [resource_tags, schema_tags]
193
158
 
194
159
  # if parameter_object = convert_to_parameter_object( version_info[:info][:base_params] )
195
160
  # full_data[:parameters] = parameter_object
196
161
  # end
197
- #puts JSON.pretty_generate( full_data )
162
+ # puts JSON.pretty_generate( full_data )
198
163
  # Write the file
199
- version_file = ( version == "n/a" ? "unversioned" : version )
164
+ version_file = (version == 'n/a' ? 'unversioned' : version)
200
165
  filename = File.join(doc_root_dir, version_file, 'openapi')
201
166
 
202
167
  puts "Generating Open API file : #{filename} (json and yml) "
203
168
  json_data = JSON.pretty_generate(full_data)
204
- File.open(filename+".json", 'w') {|f| f.write(json_data)}
205
- converted_full_data = JSON.parse( json_data ) # So symbols disappear
206
- File.open(filename+".yml", 'w') {|f| f.write(YAML.dump(converted_full_data))}
169
+ File.open("#{filename}.json", 'w') { |f| f.write(json_data) }
170
+ converted_full_data = JSON.parse(json_data) # So symbols disappear
171
+ File.open("#{filename}.yml", 'w') { |f| f.write(YAML.dump(converted_full_data)) }
207
172
 
208
- html =<<-EOB
173
+ html = <<-HTML
209
174
  <!DOCTYPE html>
210
175
  <html>
211
176
  <head>
@@ -214,7 +179,7 @@ module Praxis
214
179
  <meta charset="utf-8"/>
215
180
  <meta name="viewport" content="width=device-width, initial-scale=1">
216
181
  <link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet">
217
-
182
+
218
183
  <!--
219
184
  ReDoc doesn't change outer page styles
220
185
  -->
@@ -230,7 +195,7 @@ module Praxis
230
195
  <script src="https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js"> </script>
231
196
  </body>
232
197
  </html>
233
- EOB
198
+ HTML
234
199
  html_file = File.join(doc_root_dir, version_file, 'index.html')
235
200
  File.write(html_file, html)
236
201
  end
@@ -239,59 +204,59 @@ module Praxis
239
204
  @doc_root_dir = File.join(@root, API_DOCS_DIRNAME)
240
205
 
241
206
  # remove previous data (and reset the directory)
242
- FileUtils.rm_rf @doc_root_dir if File.exists?(@doc_root_dir)
243
- FileUtils.mkdir_p @doc_root_dir unless File.exists? @doc_root_dir
244
- resources_by_version.keys.each do |version|
245
- FileUtils.mkdir_p @doc_root_dir + '/' + version
207
+ FileUtils.rm_rf @doc_root_dir if File.exist?(@doc_root_dir)
208
+ FileUtils.mkdir_p @doc_root_dir unless File.exist? @doc_root_dir
209
+ resources_by_version.each_key do |version|
210
+ FileUtils.mkdir_p "#{@doc_root_dir}/#{version}"
246
211
  end
247
- FileUtils.mkdir_p @doc_root_dir + '/unversioned' if resources_by_version.keys.include?('n/a')
212
+ FileUtils.mkdir_p "#{@doc_root_dir}/unversioned" if resources_by_version.keys.include?('n/a')
248
213
  end
249
214
 
250
- def normalize_media_types( mtis )
215
+ def normalize_media_types(mtis)
251
216
  mtis.collect do |mti|
252
- MediaTypeIdentifier.load(mti).to_s
253
- end
217
+ MediaTypeIdentifier.load(mti).to_s
218
+ end
254
219
  end
255
220
 
256
- def reusable_schema_objects(types)
257
- types.each_with_object({}) do |(type), accum|
258
- the_type = \
259
- if type.respond_to? :as_json_schema
260
- type
261
- else # If it is a blueprint ... for now, it'd be through the attribute
262
- type.attribute
263
- end
264
- accum[type.id] = the_type.as_json_schema(shallow: false)
221
+ def add_component_schemas(types_to_add, components_hash)
222
+ initial = @seen_components_for_current_version.dup
223
+ types_to_add.each_with_object(components_hash) do |(type), accum|
224
+ # For components, we want the first level to be fully dumped (only references below that)
225
+ accum[type.id] = OpenApi::SchemaObject.new(info: type).dump_schema(allow_ref: false, shallow: false)
265
226
  end
227
+ newfound = @seen_components_for_current_version - initial
228
+ # Process the new types if they have discovered
229
+ add_component_schemas(newfound, components_hash) unless newfound.empty?
230
+ components_hash
266
231
  end
267
232
 
268
- def convert_to_parameter_object( params )
233
+ def convert_to_parameter_object(params)
269
234
  # TODO!! actually convert each of them
270
- puts "TODO! convert to parameter object"
235
+ puts 'TODO! convert to parameter object'
271
236
  params
272
237
  end
273
238
 
274
- def convert_traits_to_tags( traits )
239
+ def convert_traits_to_tags(traits)
275
240
  traits.collect do |name, info|
276
241
  { name: name, description: info[:description] }
277
242
  end
278
243
  end
279
244
 
280
-
281
- def dump_responses_object( responses )
282
- responses.each_with_object({}) do |(name, info), hash|
283
- data = { description: info[:description] || "" }
284
- if payload = info[:payload]
285
- body_type= payload[:id]
245
+ def dump_responses_object(responses)
246
+ responses.each_with_object({}) do |(_name, info), hash|
247
+ data = { description: info[:description] || '' }
248
+ if (payload = info[:payload])
249
+ body_type = payload[:id]
286
250
  raise "WAIT! response payload doesn't have an existing id for the schema!!! (do an if, and describe it if so)" unless body_type
287
- data[:schema] = {"$ref" => "#/definitions/#{body_type}" }
251
+
252
+ data[:schema] = { '$ref' => "#/definitions/#{body_type}" }
288
253
  end
289
254
 
290
- # data[:schema] = ???TODO!!
291
- if headers_object = dump_response_headers_object( info[:headers] )
255
+ # data[:schema] = ???TODO!!
256
+ if (headers_object = dump_response_headers_object(info[:headers]))
292
257
  data[:headers] = headers_object
293
258
  end
294
- if info[:payload] && ( examples_object = dump_response_examples_object( info[:payload][:examples] ) )
259
+ if info[:payload] && (examples_object = dump_response_examples_object(info[:payload][:examples]))
295
260
  data[:examples] = examples_object
296
261
  end
297
262
  hash[info[:status]] = data
@@ -305,17 +270,17 @@ module Praxis
305
270
  # end
306
271
  # end
307
272
 
308
- def dump_response_examples_object( examples )
309
- examples.each_with_object({}) do |(name, info), hash|
273
+ def dump_response_examples_object(examples)
274
+ examples.each_with_object({}) do |(_name, info), hash|
310
275
  hash[info[:content_type]] = info[:body]
311
276
  end
312
277
  end
313
278
 
314
-
315
- def dump_resources( resources )
279
+ def dump_resources(resources)
316
280
  resources.each_with_object({}) do |r, hash|
317
281
  # Do not report undocumentable resources
318
282
  next if r.metadata[:doc_visibility] == :none
283
+
319
284
  context = [r.id]
320
285
  resource_description = r.describe(context: context)
321
286
 
@@ -328,13 +293,12 @@ module Praxis
328
293
  # skip actions with doc_visibility of :none
329
294
  next if action.metadata[:doc_visibility] == :none
330
295
 
331
- action_description = resource_description[:actions].find {|a| a[:name] == action_name }
296
+ resource_description[:actions].find { |a| a[:name] == action_name }
332
297
  end
333
298
 
334
299
  hash[r.id] = resource_description
335
300
  end
336
301
  end
337
-
338
302
  end
339
303
  end
340
304
  end