praxis 0.22.pre.2 → 2.0.pre.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (111) hide show
  1. checksums.yaml +5 -5
  2. data/.travis.yml +5 -20
  3. data/CHANGELOG.md +333 -324
  4. data/lib/praxis.rb +14 -9
  5. data/lib/praxis/action_definition.rb +8 -10
  6. data/lib/praxis/action_definition/headers_dsl_compiler.rb +1 -1
  7. data/lib/praxis/api_definition.rb +27 -44
  8. data/lib/praxis/api_general_info.rb +23 -3
  9. data/lib/praxis/application.rb +15 -142
  10. data/lib/praxis/bootloader.rb +1 -2
  11. data/lib/praxis/bootloader_stages/environment.rb +13 -0
  12. data/lib/praxis/config.rb +1 -1
  13. data/lib/praxis/controller.rb +0 -2
  14. data/lib/praxis/dispatcher.rb +4 -6
  15. data/lib/praxis/docs/generator.rb +19 -24
  16. data/lib/praxis/docs/link_builder.rb +1 -1
  17. data/lib/praxis/docs/open_api_generator.rb +255 -0
  18. data/lib/praxis/docs/openapi/info_object.rb +31 -0
  19. data/lib/praxis/docs/openapi/media_type_object.rb +59 -0
  20. data/lib/praxis/docs/openapi/operation_object.rb +40 -0
  21. data/lib/praxis/docs/openapi/parameter_object.rb +69 -0
  22. data/lib/praxis/docs/openapi/paths_object.rb +58 -0
  23. data/lib/praxis/docs/openapi/request_body_object.rb +51 -0
  24. data/lib/praxis/docs/openapi/response_object.rb +63 -0
  25. data/lib/praxis/docs/openapi/responses_object.rb +44 -0
  26. data/lib/praxis/docs/openapi/schema_object.rb +87 -0
  27. data/lib/praxis/docs/openapi/server_object.rb +24 -0
  28. data/lib/praxis/docs/openapi/tag_object.rb +21 -0
  29. data/lib/praxis/error_handler.rb +5 -5
  30. data/lib/praxis/extensions/attribute_filtering/active_record_filter_query_builder.rb +1 -1
  31. data/lib/praxis/extensions/attribute_filtering/filtering_params.rb +4 -0
  32. data/lib/praxis/extensions/attribute_filtering/sequel_filter_query_builder.rb +125 -0
  33. data/lib/praxis/extensions/field_selection.rb +1 -12
  34. data/lib/praxis/extensions/field_selection/active_record_query_selector.rb +28 -34
  35. data/lib/praxis/extensions/field_selection/field_selector.rb +4 -0
  36. data/lib/praxis/extensions/field_selection/sequel_query_selector.rb +35 -39
  37. data/lib/praxis/extensions/rendering.rb +1 -1
  38. data/lib/praxis/file_group.rb +1 -1
  39. data/lib/praxis/handlers/xml.rb +1 -1
  40. data/lib/praxis/links.rb +4 -0
  41. data/lib/praxis/mapper/active_model_compat.rb +98 -0
  42. data/lib/praxis/mapper/resource.rb +242 -0
  43. data/lib/praxis/mapper/selector_generator.rb +150 -0
  44. data/lib/praxis/mapper/sequel_compat.rb +76 -0
  45. data/lib/praxis/media_type_identifier.rb +2 -1
  46. data/lib/praxis/middleware_app.rb +13 -15
  47. data/lib/praxis/multipart/part.rb +8 -7
  48. data/lib/praxis/notifications.rb +1 -1
  49. data/lib/praxis/plugins/mapper_plugin.rb +64 -0
  50. data/lib/praxis/request.rb +14 -7
  51. data/lib/praxis/request_stages/response.rb +2 -3
  52. data/lib/praxis/resource_definition.rb +15 -19
  53. data/lib/praxis/response.rb +6 -5
  54. data/lib/praxis/response_definition.rb +6 -8
  55. data/lib/praxis/response_template.rb +3 -4
  56. data/lib/praxis/responses/http.rb +36 -0
  57. data/lib/praxis/responses/internal_server_error.rb +12 -3
  58. data/lib/praxis/responses/multipart_ok.rb +11 -4
  59. data/lib/praxis/responses/validation_error.rb +10 -1
  60. data/lib/praxis/route.rb +1 -1
  61. data/lib/praxis/router.rb +3 -3
  62. data/lib/praxis/routing_config.rb +1 -1
  63. data/lib/praxis/tasks/api_docs.rb +24 -9
  64. data/lib/praxis/tasks/routes.rb +0 -1
  65. data/lib/praxis/trait.rb +1 -1
  66. data/lib/praxis/types/media_type_common.rb +12 -2
  67. data/lib/praxis/types/multipart.rb +1 -1
  68. data/lib/praxis/types/multipart_array.rb +64 -2
  69. data/lib/praxis/types/multipart_array/part_definition.rb +1 -1
  70. data/lib/praxis/version.rb +1 -1
  71. data/praxis.gemspec +11 -9
  72. data/spec/functional_spec.rb +0 -1
  73. data/spec/praxis/action_definition_spec.rb +16 -27
  74. data/spec/praxis/api_definition_spec.rb +8 -13
  75. data/spec/praxis/api_general_info_spec.rb +8 -3
  76. data/spec/praxis/application_spec.rb +8 -14
  77. data/spec/praxis/collection_spec.rb +3 -2
  78. data/spec/praxis/config_spec.rb +2 -2
  79. data/spec/praxis/extensions/field_selection/active_record_query_selector_spec.rb +106 -0
  80. data/spec/praxis/extensions/field_selection/sequel_query_selector_spec.rb +147 -0
  81. data/spec/praxis/extensions/field_selection/support/spec_resources_active_model.rb +130 -0
  82. data/spec/praxis/extensions/field_selection/support/spec_resources_sequel.rb +106 -0
  83. data/spec/praxis/handlers/xml_spec.rb +2 -2
  84. data/spec/praxis/mapper/resource_spec.rb +169 -0
  85. data/spec/praxis/mapper/selector_generator_spec.rb +325 -0
  86. data/spec/praxis/media_type_spec.rb +0 -10
  87. data/spec/praxis/middleware_app_spec.rb +16 -10
  88. data/spec/praxis/request_spec.rb +7 -17
  89. data/spec/praxis/request_stages/action_spec.rb +8 -1
  90. data/spec/praxis/request_stages/validate_spec.rb +1 -1
  91. data/spec/praxis/resource_definition_spec.rb +10 -12
  92. data/spec/praxis/response_definition_spec.rb +19 -30
  93. data/spec/praxis/response_spec.rb +6 -13
  94. data/spec/praxis/responses/internal_server_error_spec.rb +5 -2
  95. data/spec/praxis/router_spec.rb +5 -9
  96. data/spec/spec_app/app/controllers/instances.rb +1 -1
  97. data/spec/spec_app/config.ru +6 -1
  98. data/spec/spec_app/config/environment.rb +3 -21
  99. data/spec/spec_app/design/api.rb +6 -0
  100. data/spec/spec_helper.rb +13 -17
  101. data/spec/support/be_deep_equal_matcher.rb +39 -0
  102. data/spec/support/spec_resources.rb +124 -0
  103. metadata +86 -53
  104. data/lib/praxis/extensions/attribute_filtering.rb +0 -28
  105. data/lib/praxis/extensions/attribute_filtering/query_builder.rb +0 -39
  106. data/lib/praxis/extensions/mapper_selectors.rb +0 -16
  107. data/lib/praxis/media_type_collection.rb +0 -127
  108. data/lib/praxis/plugins/praxis_mapper_plugin.rb +0 -246
  109. data/spec/praxis/media_type_collection_spec.rb +0 -157
  110. data/spec/praxis/plugins/praxis_mapper_plugin_spec.rb +0 -142
  111. data/spec/spec_app/app/models/person.rb +0 -3
@@ -47,9 +47,8 @@ module Praxis
47
47
  stages << BootloaderStages::WarnUnloadedFiles.new(:warn_unloaded_files, application)
48
48
 
49
49
  after(:app) do
50
- Praxis::Mapper.finalize!
51
50
  Praxis::Blueprint.finalize!
52
- Praxis::ResourceDefinition.finalize!(application: self.application)
51
+ Praxis::ResourceDefinition.finalize!
53
52
  end
54
53
 
55
54
  end
@@ -8,6 +8,7 @@ module Praxis
8
8
  # 1) the environment.rb file - generic stuff for all environments
9
9
  # 2) "Deployer.environment".rb - environment specific stuff
10
10
  def execute
11
+ setup_initial_config!
11
12
 
12
13
  env_file = application.root + "config/environment.rb"
13
14
  require env_file if File.exists? env_file
@@ -36,6 +37,18 @@ module Praxis
36
37
  end
37
38
  end
38
39
 
40
+ # TODO: not really sure I like this here... but where else is better?
41
+ def setup_initial_config!
42
+ application.config do
43
+ attribute :praxis do
44
+ attribute :validate_responses, Attributor::Boolean, default: false
45
+ attribute :validate_response_bodies, Attributor::Boolean, default: false
46
+
47
+ attribute :show_exceptions, Attributor::Boolean, default: false
48
+ attribute :x_cascade, Attributor::Boolean, default: true
49
+ end
50
+ end
51
+ end
39
52
 
40
53
  end
41
54
 
@@ -54,7 +54,7 @@ module Praxis
54
54
  )
55
55
  end
56
56
  top.options.merge!(opts)
57
- top.type.attributes(opts, &block)
57
+ top.type.attributes(**opts, &block)
58
58
  else
59
59
  @attribute.attributes[key] = Attributor::Attribute.new(type, opts, &block)
60
60
  end
@@ -20,8 +20,6 @@ module Praxis
20
20
  end
21
21
 
22
22
  definition.controller = self
23
- # `implements` should only be processed while the application initializes/setup
24
- # So we will use the `.instance` function to get the "current" application instance
25
23
  Application.instance.controllers << self
26
24
  end
27
25
 
@@ -28,13 +28,11 @@ module Praxis
28
28
  @deferred_callbacks[:after] << [conditions, block]
29
29
  end
30
30
 
31
- # Typically, this is only called from the router, and the app will always be known.
32
- # But we'll leave the application param as optional if we know there is a dispatcher in the thread
33
- def self.current(thread: Thread.current, application: nil)
31
+ def self.current(thread: Thread.current, application: Application.instance)
34
32
  thread[:praxis_dispatcher] ||= self.new(application: application)
35
33
  end
36
34
 
37
- def initialize(application:)
35
+ def initialize(application: Application.instance)
38
36
  @stages = []
39
37
  @application = application
40
38
  setup_stages!
@@ -106,9 +104,9 @@ module Praxis
106
104
  response_stage.run
107
105
 
108
106
  payload[:response] = controller.response
109
- controller.response.finish(application: application)
107
+ controller.response.finish
110
108
  rescue => e
111
- @application.error_handler.handle!(request, e, app: application)
109
+ @application.error_handler.handle!(request, e)
112
110
  end
113
111
  end
114
112
  end
@@ -5,8 +5,7 @@ module Praxis
5
5
  require 'active_support/core_ext/enumerable' # For index_by
6
6
 
7
7
  API_DOCS_DIRNAME = 'docs/api'
8
-
9
- attr_reader :app_instance
8
+
10
9
  attr_reader :resources_by_version, :types_by_id, :infos_by_version
11
10
  attr_reader :doc_root_dir
12
11
 
@@ -25,21 +24,13 @@ module Praxis
25
24
  Attributor::URI,
26
25
  ]).freeze
27
26
 
28
- def self.generate(root, name:, skip_sub_directory: false)
29
- instance = Praxis::Application.registered_apps[name]
30
- Thread.current[:praxis_instance] = instance
31
- self.new(root, instance: instance, name: name, skip_sub_directory: skip_sub_directory).save!
32
- Thread.current[:praxis_instance] = nil
33
- end
34
-
35
- def initialize(root, instance:, name:, skip_sub_directory:)
27
+
28
+ def initialize(root)
36
29
  require 'yaml'
37
30
  @resources_by_version = Hash.new do |h,k|
38
31
  h[k] = Set.new
39
32
  end
40
- @app_instance = instance
41
- subdir = skip_sub_directory ? nil : name
42
- initialize_directories(root, subdir: subdir )
33
+ initialize_directories(root)
43
34
 
44
35
  Attributor::AttributeResolver.current = Attributor::AttributeResolver.new
45
36
  collect_infos
@@ -57,10 +48,9 @@ module Praxis
57
48
 
58
49
  private
59
50
 
60
- def initialize_directories(root, subdir: nil )
51
+ def initialize_directories(root)
61
52
  @doc_root_dir = File.join(root, API_DOCS_DIRNAME)
62
- @doc_root_dir = File.join(@doc_root_dir, subdir) if subdir
63
-
53
+
64
54
  # remove previous data (and reset the directory)
65
55
  FileUtils.rm_rf @doc_root_dir if File.exists?(@doc_root_dir)
66
56
  FileUtils.mkdir_p @doc_root_dir unless File.exists? @doc_root_dir
@@ -68,7 +58,7 @@ module Praxis
68
58
 
69
59
  def collect_resources
70
60
  # load all resource definitions registered with Praxis
71
- app_instance.resource_definitions.map do |resource|
61
+ Praxis::Application.instance.resource_definitions.map do |resource|
72
62
  # skip resources with doc_visibility of :none
73
63
  next if resource.metadata[:doc_visibility] == :none
74
64
  version = resource.version
@@ -86,7 +76,7 @@ module Praxis
86
76
 
87
77
  def collect_infos
88
78
  # All infos. Including keys for `:global`, "n/a", and any string version
89
- @infos_by_version = app_instance.api_definition.describe
79
+ @infos_by_version = ApiDefinition.instance.describe
90
80
  end
91
81
 
92
82
 
@@ -130,12 +120,7 @@ module Praxis
130
120
  File.open(filename, 'w') {|f| f.write(JSON.pretty_generate(data))}
131
121
  end
132
122
 
133
- def write_version_file( version )
134
- version_info = infos_by_version[version]
135
- # Hack, let's "inherit/copy" all traits of a version from the global definition
136
- # Eventually traits should be defined for a version (and inheritable from global) so we'll emulate that here
137
- version_info[:traits] = infos_by_version[:traits]
138
- dumped_resources = dump_resources( resources_by_version[version] )
123
+ def scan_types_for_version(version, dumped_resources)
139
124
  found_media_types = resources_by_version[version].select{|r| r.media_type}.collect {|r| r.media_type.describe }
140
125
 
141
126
  # We'll start by processing the rendered mediatypes
@@ -160,7 +145,17 @@ module Praxis
160
145
  processed_types += newfound
161
146
  newfound = scan_dump_for_types( dumped, processed_types )
162
147
  end
148
+ processed_types
149
+ end
163
150
 
151
+ def write_version_file( version )
152
+ version_info = infos_by_version[version]
153
+ # Hack, let's "inherit/copy" all traits of a version from the global definition
154
+ # Eventually traits should be defined for a version (and inheritable from global) so we'll emulate that here
155
+ version_info[:traits] = infos_by_version[:traits]
156
+
157
+ dumped_resources = dump_resources( resources_by_version[version] )
158
+ processed_types = scan_types_for_version(version, dumped_resources)
164
159
  dumped_schemas = dump_schemas( processed_types )
165
160
  full_data = {
166
161
  info: version_info[:info],
@@ -20,7 +20,7 @@ module Praxis
20
20
 
21
21
  def endpoint
22
22
  @endpoint ||= begin
23
- endpoint = Application.current_instance.api_definition.global_info.documentation_url
23
+ endpoint = ApiDefinition.instance.global_info.documentation_url
24
24
  endpoint.gsub(/\/index\.html$/i, '/') if endpoint
25
25
  end
26
26
  end
@@ -0,0 +1,255 @@
1
+ require_relative 'openapi/info_object.rb'
2
+ require_relative 'openapi/server_object.rb'
3
+ require_relative 'openapi/paths_object.rb'
4
+ require_relative 'openapi/tag_object.rb'
5
+
6
+ module Praxis
7
+ module Docs
8
+
9
+ class OpenApiGenerator < Generator
10
+ API_DOCS_DIRNAME = 'docs/openapi'
11
+
12
+ # substitutes ":params_like_so" for {params_like_so}
13
+ def self.templatize_url( string )
14
+ Mustermann.new(string).to_templates.first
15
+ end
16
+
17
+ def save!
18
+ initialize_directories
19
+ # Restrict the versions listed in the index file to the ones for which we have at least 1 resource
20
+ write_index_file( for_versions: resources_by_version.keys )
21
+ resources_by_version.keys.each do |version|
22
+ write_version_file(version)
23
+ end
24
+ end
25
+
26
+ def initialize(root)
27
+ require 'yaml'
28
+ @root = root
29
+ @resources_by_version = Hash.new do |h,k|
30
+ h[k] = Set.new
31
+ end
32
+
33
+ @infos = ApiDefinition.instance.infos
34
+ collect_resources
35
+ collect_types
36
+ end
37
+
38
+ private
39
+
40
+ def write_index_file( for_versions: )
41
+ # TODO. create a simple html file that can link to the individual versions available
42
+ end
43
+
44
+ def write_version_file( version )
45
+ # version_info = infos_by_version[version]
46
+ # # Hack, let's "inherit/copy" all traits of a version from the global definition
47
+ # # Eventually traits should be defined for a version (and inheritable from global) so we'll emulate that here
48
+ # version_info[:traits] = infos_by_version[:traits]
49
+ dumped_resources = dump_resources( resources_by_version[version] )
50
+ processed_types = scan_types_for_version(version, dumped_resources)
51
+
52
+ # Here we have:
53
+ # processed types: which includes mediatypes and normal types...real classes
54
+ # processed resources for this version: resources_by_version[version]
55
+
56
+ info_object = OpenApi::InfoObject.new(version: version, api_definition_info: @infos[version])
57
+ # We only support a server in Praxis ... so we'll use the base path
58
+ server_object = OpenApi::ServerObject.new( url: @infos[version].base_path )
59
+
60
+ paths_object = OpenApi::PathsObject.new( resources: resources_by_version[version])
61
+
62
+ full_data = {
63
+ openapi: "3.0.2",
64
+ info: info_object.dump,
65
+ servers: [server_object.dump],
66
+ paths: paths_object.dump,
67
+ # responses: {}, #TODO!! what do we get here? the templates?...need to transform to "Responses Definitions Object"
68
+ # securityDefinitions: {}, # NOTE: No security definitions in Praxis
69
+ # security: [], # NOTE: No security definitions in Praxis
70
+ }
71
+
72
+ # Create the top level tags by:
73
+ # 1- First adding all the resource display names (and descriptions)
74
+ tags_for_resources = resources_by_version[version].collect do |resource|
75
+ OpenApi::TagObject.new(name: resource.display_name, description: resource.description ).dump
76
+ end
77
+ full_data[:tags] = tags_for_resources
78
+ # 2- Then adding all of the top level traits but marking them special with the x-traitTag (of Redoc)
79
+ tags_for_traits = (ApiDefinition.instance.traits).collect do |name, info|
80
+ OpenApi::TagObject.new(name: name, description: info.description).dump.merge(:'x-traitTag' => true)
81
+ end
82
+ unless tags_for_traits.empty?
83
+ full_data[:tags] = full_data[:tags] + tags_for_traits
84
+ end
85
+
86
+ # Include only MTs (i.e., not custom types or simple types...)
87
+ component_schemas = reusable_schema_objects(processed_types.select{|t| t < Praxis::MediaType})
88
+
89
+ # 3- Then adding all of the top level Mediatypes...so we can present them at the bottom, otherwise they don't show
90
+ tags_for_mts = component_schemas.map do |(name, info)|
91
+ special_redoc_anchor = "<SchemaDefinition schemaRef=\"#/components/schemas/#{name}\" showReadOnly={true} showWriteOnly={true} />"
92
+ guessed_display = name.split('-').last # TODO!!!the informational hash does not seem to come with the "description" value set...hmm
93
+ OpenApi::TagObject.new(name: name, description: special_redoc_anchor).dump.merge(:'x-displayName' => guessed_display)
94
+ end
95
+ unless tags_for_mts.empty?
96
+ full_data[:tags] = full_data[:tags] + tags_for_mts
97
+ end
98
+
99
+ # Include all the reusable schemas in the components hash
100
+ full_data[:components] = {
101
+ schemas: component_schemas
102
+ }
103
+
104
+ # REDOC specific grouping of sidebar
105
+ resource_tags = { name: 'Resources', tags: tags_for_resources.map{|t| t[:name]} }
106
+ schema_tags = { name: 'Models', tags: tags_for_mts.map{|t| t[:name]} }
107
+ full_data['x-tagGroups'] = [resource_tags, schema_tags]
108
+
109
+ # if parameter_object = convert_to_parameter_object( version_info[:info][:base_params] )
110
+ # full_data[:parameters] = parameter_object
111
+ # end
112
+ #puts JSON.pretty_generate( full_data )
113
+ # Write the file
114
+ version_file = ( version == "n/a" ? "unversioned" : version )
115
+ filename = File.join(doc_root_dir, version_file, 'openapi')
116
+
117
+ puts "Generating Open API file : #{filename} (json and yml) "
118
+ json_data = JSON.pretty_generate(full_data)
119
+ File.open(filename+".json", 'w') {|f| f.write(json_data)}
120
+ converted_full_data = JSON.parse( json_data ) # So symbols disappear
121
+ File.open(filename+".yml", 'w') {|f| f.write(YAML.dump(converted_full_data))}
122
+
123
+ html =<<-EOB
124
+ <!DOCTYPE html>
125
+ <html>
126
+ <head>
127
+ <title>ReDoc</title>
128
+ <!-- needed for adaptive design -->
129
+ <meta charset="utf-8"/>
130
+ <meta name="viewport" content="width=device-width, initial-scale=1">
131
+ <link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet">
132
+
133
+ <!--
134
+ ReDoc doesn't change outer page styles
135
+ -->
136
+ <style>
137
+ body {
138
+ margin: 0;
139
+ padding: 0;
140
+ }
141
+ </style>
142
+ </head>
143
+ <body>
144
+ <redoc spec-url='http://localhost:9090/#{version_file}/openapi.json'></redoc>
145
+ <script src="https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js"> </script>
146
+ </body>
147
+ </html>
148
+ EOB
149
+ html_file = File.join(doc_root_dir, version_file, 'index.html')
150
+ File.write(html_file, html)
151
+ end
152
+
153
+ def initialize_directories
154
+ @doc_root_dir = File.join(@root, API_DOCS_DIRNAME)
155
+
156
+ # remove previous data (and reset the directory)
157
+ FileUtils.rm_rf @doc_root_dir if File.exists?(@doc_root_dir)
158
+ FileUtils.mkdir_p @doc_root_dir unless File.exists? @doc_root_dir
159
+ resources_by_version.keys.each do |version|
160
+ FileUtils.mkdir_p @doc_root_dir + '/' + version
161
+ end
162
+ FileUtils.mkdir_p @doc_root_dir + '/unversioned'
163
+ end
164
+
165
+ def normalize_media_types( mtis )
166
+ mtis.collect do |mti|
167
+ MediaTypeIdentifier.load(mti).to_s
168
+ end
169
+ end
170
+
171
+ def reusable_schema_objects(types)
172
+ types.each_with_object({}) do |(type), accum|
173
+ the_type = \
174
+ if type.respond_to? :as_json_schema
175
+ type
176
+ else # If it is a blueprint ... for now, it'd be through the attribute
177
+ type.attribute
178
+ end
179
+ accum[type.id] = the_type.as_json_schema(shallow: false)
180
+ end
181
+ end
182
+
183
+ def convert_to_parameter_object( params )
184
+ # TODO!! actually convert each of them
185
+ puts "TODO! convert to parameter object"
186
+ params
187
+ end
188
+
189
+ def convert_traits_to_tags( traits )
190
+ traits.collect do |name, info|
191
+ { name: name, description: info[:description] }
192
+ end
193
+ end
194
+
195
+
196
+ def dump_responses_object( responses )
197
+ responses.each_with_object({}) do |(name, info), hash|
198
+ data = { description: info[:description] || "" }
199
+ if payload = info[:payload]
200
+ body_type= payload[:id]
201
+ raise "WAIT! response payload doesn't have an existing id for the schema!!! (do an if, and describe it if so)" unless body_type
202
+ data[:schema] = {"$ref" => "#/definitions/#{body_type}" }
203
+ end
204
+
205
+ # data[:schema] = ???TODO!!
206
+ if headers_object = dump_response_headers_object( info[:headers] )
207
+ data[:headers] = headers_object
208
+ end
209
+ if info[:payload] && ( examples_object = dump_response_examples_object( info[:payload][:examples] ) )
210
+ data[:examples] = examples_object
211
+ end
212
+ hash[info[:status]] = data
213
+ end
214
+ end
215
+ # def dump_response_headers_object( headers )
216
+ # puts "WARNING!! Finish this. It seems that headers for responses are never set in the hash??"
217
+ # unless headers.empty?
218
+ # binding.pry
219
+ # puts headers
220
+ # end
221
+ # end
222
+
223
+ def dump_response_examples_object( examples )
224
+ examples.each_with_object({}) do |(name, info), hash|
225
+ hash[info[:content_type]] = info[:body]
226
+ end
227
+ end
228
+
229
+
230
+ def dump_resources( resources )
231
+ resources.each_with_object({}) do |r, hash|
232
+ # Do not report undocumentable resources
233
+ next if r.metadata[:doc_visibility] == :none
234
+ context = [r.id]
235
+ resource_description = r.describe(context: context)
236
+
237
+ # strip actions with doc_visibility of :none
238
+ resource_description[:actions].reject! { |a| a[:metadata][:doc_visibility] == :none }
239
+
240
+ # Go through the params/payload of each action and augment them by
241
+ # adding a generated example (then stick it into the description hash)
242
+ r.actions.each do |action_name, action|
243
+ # skip actions with doc_visibility of :none
244
+ next if action.metadata[:doc_visibility] == :none
245
+
246
+ action_description = resource_description[:actions].find {|a| a[:name] == action_name }
247
+ end
248
+
249
+ hash[r.id] = resource_description
250
+ end
251
+ end
252
+
253
+ end
254
+ end
255
+ end
@@ -0,0 +1,31 @@
1
+ module Praxis
2
+ module Docs
3
+ module OpenApi
4
+ class InfoObject
5
+ attr_reader :info, :version
6
+ def initialize(version: , api_definition_info: )
7
+ @version = version
8
+ @info = api_definition_info
9
+ raise "OpenApi docs require a 'Title' for your API." unless info.title
10
+ end
11
+
12
+ def dump
13
+ data ={
14
+ title: info.title,
15
+ description: info.description,
16
+ termsOfService: info.termsOfService,
17
+ contact: info.contact,
18
+ license: info.license,
19
+ version: version,
20
+ :'x-name' => info.name,
21
+ :'x-logo' => {
22
+ url: info.logo_url,
23
+ backgroundColor: "#FFFFFF",
24
+ altText: info.title
25
+ }
26
+ }
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end