oas_rails 0.2.3 → 0.4.0
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.
- checksums.yaml +4 -4
- data/README.md +10 -1
- data/app/controllers/oas_rails/oas_rails_controller.rb +1 -1
- data/lib/generators/oas_rails/config/templates/oas_rails_initializer.rb +11 -0
- data/lib/oas_rails/builders/content_builder.rb +55 -0
- data/lib/oas_rails/builders/operation_builder.rb +32 -0
- data/lib/oas_rails/builders/parameter_builder.rb +28 -0
- data/lib/oas_rails/builders/parameters_builder.rb +39 -0
- data/lib/oas_rails/builders/path_item_builder.rb +22 -0
- data/lib/oas_rails/builders/request_body_builder.rb +60 -0
- data/lib/oas_rails/builders/response_builder.rb +40 -0
- data/lib/oas_rails/builders/responses_builder.rb +58 -0
- data/lib/oas_rails/configuration.rb +25 -5
- data/lib/oas_rails/esquema_builder.rb +37 -0
- data/lib/oas_rails/extractors/oas_route_extractor.rb +66 -0
- data/lib/oas_rails/extractors/render_response_extractor.rb +148 -0
- data/lib/oas_rails/extractors/route_extractor.rb +125 -0
- data/lib/oas_rails/oas_route.rb +1 -99
- data/lib/oas_rails/spec/components.rb +85 -0
- data/lib/oas_rails/spec/contact.rb +18 -0
- data/lib/oas_rails/spec/hashable.rb +39 -0
- data/lib/oas_rails/{info.rb → spec/info.rb} +30 -24
- data/lib/oas_rails/spec/license.rb +18 -0
- data/lib/oas_rails/spec/media_type.rb +84 -0
- data/lib/oas_rails/spec/operation.rb +25 -0
- data/lib/oas_rails/spec/parameter.rb +34 -0
- data/lib/oas_rails/spec/path_item.rb +33 -0
- data/lib/oas_rails/spec/paths.rb +26 -0
- data/lib/oas_rails/spec/reference.rb +16 -0
- data/lib/oas_rails/spec/request_body.rb +21 -0
- data/lib/oas_rails/spec/response.rb +20 -0
- data/lib/oas_rails/spec/responses.rb +25 -0
- data/lib/oas_rails/spec/server.rb +17 -0
- data/lib/oas_rails/spec/specable.rb +51 -0
- data/lib/oas_rails/spec/specification.rb +50 -0
- data/lib/oas_rails/spec/tag.rb +18 -0
- data/lib/oas_rails/utils.rb +39 -0
- data/lib/oas_rails/version.rb +1 -1
- data/lib/oas_rails.rb +47 -26
- metadata +32 -18
- data/lib/oas_rails/contact.rb +0 -12
- data/lib/oas_rails/license.rb +0 -11
- data/lib/oas_rails/media_type.rb +0 -76
- data/lib/oas_rails/oas_base.rb +0 -30
- data/lib/oas_rails/operation.rb +0 -134
- data/lib/oas_rails/parameter.rb +0 -47
- data/lib/oas_rails/path_item.rb +0 -25
- data/lib/oas_rails/paths.rb +0 -19
- data/lib/oas_rails/request_body.rb +0 -29
- data/lib/oas_rails/response.rb +0 -12
- data/lib/oas_rails/responses.rb +0 -20
- data/lib/oas_rails/route_extractor.rb +0 -119
- data/lib/oas_rails/server.rb +0 -10
- data/lib/oas_rails/specification.rb +0 -72
- data/lib/oas_rails/tag.rb +0 -17
@@ -0,0 +1,148 @@
|
|
1
|
+
module OasRails
|
2
|
+
module Extractors
|
3
|
+
# Extracts and processes render responses from a given source.
|
4
|
+
module RenderResponseExtractor
|
5
|
+
class << self
|
6
|
+
# Extracts responses from the provided source string.
|
7
|
+
#
|
8
|
+
# @param source [String] The source string containing render calls.
|
9
|
+
# @return [Array<Response>] An array of Response objects extracted from the source.
|
10
|
+
def extract_responses_from_source(specification, source:)
|
11
|
+
render_calls = extract_render_calls(source)
|
12
|
+
return [Builders::ResponseBuilder.new(specification).with_description("No content").with_code(204).build] if render_calls.empty?
|
13
|
+
|
14
|
+
render_calls.map { |render_content, status| process_render_content(specification, render_content.strip, status) }
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
# Extracts render calls from the source string.
|
20
|
+
#
|
21
|
+
# @param source [String] The source string containing render calls.
|
22
|
+
# @return [Array<Array<String, String>>] An array of arrays, each containing render content and status.
|
23
|
+
def extract_render_calls(source)
|
24
|
+
source.scan(/render json: ((?:\{.*?\}|\S+))(?:, status: :(\w+))?(?:,.*?)?$/m)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Processes the render content and status to build a Response object.
|
28
|
+
#
|
29
|
+
# @param content [String] The content extracted from the render call.
|
30
|
+
# @param status [String] The status code associated with the render call.
|
31
|
+
# @return [Response] A Response object based on the processed content and status.
|
32
|
+
def process_render_content(specification, content, status)
|
33
|
+
schema, examples = build_schema_and_examples(content)
|
34
|
+
status_int = Utils.status_to_integer(status)
|
35
|
+
content = Builders::ContentBuilder.new(specification, :outgoing).with_schema(schema).with_examples(examples).build
|
36
|
+
|
37
|
+
Builders::ResponseBuilder.new(specification).with_code(status_int).with_description(Utils.status_code_to_text(status_int)).with_content(content).build
|
38
|
+
end
|
39
|
+
|
40
|
+
# Builds schema and examples based on the content type.
|
41
|
+
#
|
42
|
+
# @param content [String] The content extracted from the render call.
|
43
|
+
# @return [Array<Hash, Hash>] An array where the first element is the schema and the second is the examples.
|
44
|
+
def build_schema_and_examples(content)
|
45
|
+
if content.start_with?('{')
|
46
|
+
[Utils.hash_to_json_schema(parse_hash_structure(content)), {}]
|
47
|
+
else
|
48
|
+
process_non_hash_content(content)
|
49
|
+
end
|
50
|
+
rescue StandardError => e
|
51
|
+
Rails.logger.debug("Error building schema: #{e.message}")
|
52
|
+
[{}]
|
53
|
+
end
|
54
|
+
|
55
|
+
# Processes non-hash content (e.g., model or method calls) to build schema and examples.
|
56
|
+
#
|
57
|
+
# @param content [String] The content extracted from the render call.
|
58
|
+
# @return [Array<Hash, Hash>] An array where the first element is the schema and the second is the examples.
|
59
|
+
def process_non_hash_content(content)
|
60
|
+
maybe_a_model, errors = content.gsub('@', "").split(".")
|
61
|
+
klass = maybe_a_model.singularize.camelize(:upper).constantize
|
62
|
+
|
63
|
+
if klass.ancestors.include?(ActiveRecord::Base)
|
64
|
+
schema = EsquemaBuilder.build_outgoing_schema(klass:)
|
65
|
+
if test_singularity(maybe_a_model)
|
66
|
+
build_singular_model_schema_and_examples(maybe_a_model, errors, klass, schema)
|
67
|
+
else
|
68
|
+
build_array_model_schema_and_examples(maybe_a_model, klass, schema)
|
69
|
+
end
|
70
|
+
else
|
71
|
+
[{}]
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Builds schema and examples for singular models.
|
76
|
+
#
|
77
|
+
# @param maybe_a_model [String] The model name or variable.
|
78
|
+
# @param errors [String, nil] Errors related to the model.
|
79
|
+
# @param klass [Class] The class associated with the model.
|
80
|
+
# @param schema [Hash] The schema for the model.
|
81
|
+
# @return [Array<Hash, Hash>] An array where the first element is the schema and the second is the examples.
|
82
|
+
def build_singular_model_schema_and_examples(_maybe_a_model, errors, klass, schema)
|
83
|
+
if errors.nil?
|
84
|
+
[schema, Spec::MediaType.search_for_examples_in_tests(klass, context: :outgoing)]
|
85
|
+
else
|
86
|
+
# TODO: this is not building the real schema.
|
87
|
+
[
|
88
|
+
{
|
89
|
+
type: "object",
|
90
|
+
properties: {
|
91
|
+
success: { type: "boolean" },
|
92
|
+
errors: {
|
93
|
+
type: "object",
|
94
|
+
additionalProperties: {
|
95
|
+
type: "array",
|
96
|
+
items: { type: "string" }
|
97
|
+
}
|
98
|
+
}
|
99
|
+
}
|
100
|
+
},
|
101
|
+
{}
|
102
|
+
]
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# Builds schema and examples for array models.
|
107
|
+
#
|
108
|
+
# @param maybe_a_model [String] The model name or variable.
|
109
|
+
# @param klass [Class] The class associated with the model.
|
110
|
+
# @param schema [Hash] The schema for the model.
|
111
|
+
# @return [Array<Hash, Hash>] An array where the first element is the schema and the second is the examples.
|
112
|
+
def build_array_model_schema_and_examples(maybe_a_model, klass, schema)
|
113
|
+
examples = { maybe_a_model => { value: Spec::MediaType.search_for_examples_in_tests(klass, context: :outgoing).values.map { |p| p.dig(:value, maybe_a_model.singularize.to_sym) } } }
|
114
|
+
[{ type: "array", items: schema }, examples]
|
115
|
+
end
|
116
|
+
|
117
|
+
# Determines if a string represents a singular model.
|
118
|
+
#
|
119
|
+
# @param str [String] The string to test.
|
120
|
+
# @return [Boolean] True if the string is a singular model, false otherwise.
|
121
|
+
def test_singularity(str)
|
122
|
+
str.pluralize != str && str.singularize == str
|
123
|
+
end
|
124
|
+
|
125
|
+
# Parses a hash literal to determine its structure.
|
126
|
+
#
|
127
|
+
# @param hash_literal [String] The hash literal string.
|
128
|
+
# @return [Hash<Symbol, String>] A hash representing the structure of the input.
|
129
|
+
def parse_hash_structure(hash_literal)
|
130
|
+
structure = {}
|
131
|
+
|
132
|
+
hash_literal.scan(/(\w+):\s*(\S+)/) do |key, value|
|
133
|
+
structure[key.to_sym] = case value
|
134
|
+
when 'true', 'false'
|
135
|
+
'Boolean'
|
136
|
+
when /^\d+$/
|
137
|
+
'Number'
|
138
|
+
else
|
139
|
+
'Object'
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
structure
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
module OasRails
|
2
|
+
module Extractors
|
3
|
+
class RouteExtractor
|
4
|
+
RAILS_DEFAULT_CONTROLLERS = %w[
|
5
|
+
rails/info
|
6
|
+
rails/mailers
|
7
|
+
active_storage/blobs
|
8
|
+
active_storage/disk
|
9
|
+
active_storage/direct_uploads
|
10
|
+
active_storage/representations
|
11
|
+
rails/conductor/continuous_integration
|
12
|
+
rails/conductor/multiple_databases
|
13
|
+
rails/conductor/action_mailbox
|
14
|
+
rails/conductor/action_text
|
15
|
+
action_cable
|
16
|
+
].freeze
|
17
|
+
|
18
|
+
RAILS_DEFAULT_PATHS = %w[
|
19
|
+
/rails/action_mailbox/
|
20
|
+
].freeze
|
21
|
+
|
22
|
+
class << self
|
23
|
+
def host_routes_by_path(path)
|
24
|
+
@host_routes ||= extract_host_routes
|
25
|
+
@host_routes.select { |r| r.path == path }
|
26
|
+
end
|
27
|
+
|
28
|
+
def host_routes
|
29
|
+
@host_routes ||= extract_host_routes
|
30
|
+
end
|
31
|
+
|
32
|
+
# Clear Class Instance Variable @host_routes
|
33
|
+
#
|
34
|
+
# This method clear the class instance variable @host_routes
|
35
|
+
# to force a extraction of the routes again.
|
36
|
+
def clear_cache
|
37
|
+
@host_routes = nil
|
38
|
+
end
|
39
|
+
|
40
|
+
def host_paths
|
41
|
+
@host_paths ||= host_routes.map(&:path).uniq.sort
|
42
|
+
end
|
43
|
+
|
44
|
+
def clean_route(route)
|
45
|
+
route.gsub('(.:format)', '').gsub(/:\w+/) { |match| "{#{match[1..]}}" }
|
46
|
+
end
|
47
|
+
|
48
|
+
# THIS CODE IS NOT IN USE BUT CAN BE USEFULL WITH GLOBAL TAGS OR AUTH TAGS
|
49
|
+
# def get_controller_comments(controller_path)
|
50
|
+
# YARD.parse_string(File.read(controller_path))
|
51
|
+
# controller_class = YARD::Registry.all(:class).first
|
52
|
+
# if controller_class
|
53
|
+
# class_comment = controller_class.docstring.all
|
54
|
+
# method_comments = controller_class.meths.map do |method|
|
55
|
+
# {
|
56
|
+
# name: method.name,
|
57
|
+
# comment: method.docstring.all
|
58
|
+
# }
|
59
|
+
# end
|
60
|
+
# YARD::Registry.clear
|
61
|
+
# {
|
62
|
+
# class_comment: class_comment,
|
63
|
+
# method_comments: method_comments
|
64
|
+
# }
|
65
|
+
# else
|
66
|
+
# YARD::Registry.clear
|
67
|
+
# nil
|
68
|
+
# end
|
69
|
+
# rescue StandardError
|
70
|
+
# nil
|
71
|
+
# end
|
72
|
+
#
|
73
|
+
# def get_controller_comment(controller_path)
|
74
|
+
# get_controller_comments(controller_path)&.dig(:class_comment) || ''
|
75
|
+
# rescue StandardError
|
76
|
+
# ''
|
77
|
+
# end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
def extract_host_routes
|
82
|
+
valid_routes.map { |r| OasRoute.new_from_rails_route(rails_route: r) }
|
83
|
+
end
|
84
|
+
|
85
|
+
def valid_routes
|
86
|
+
Rails.application.routes.routes.select do |route|
|
87
|
+
valid_api_route?(route)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def valid_api_route?(route)
|
92
|
+
return false unless valid_route_implementation?(route)
|
93
|
+
return false if RAILS_DEFAULT_CONTROLLERS.any? { |default| route.defaults[:controller].start_with?(default) }
|
94
|
+
return false if RAILS_DEFAULT_PATHS.any? { |path| route.path.spec.to_s.include?(path) }
|
95
|
+
return false unless route.path.spec.to_s.start_with?(OasRails.config.api_path)
|
96
|
+
|
97
|
+
true
|
98
|
+
end
|
99
|
+
|
100
|
+
# Checks if a route has a valid implementation.
|
101
|
+
#
|
102
|
+
# This method verifies that both the controller and the action specified
|
103
|
+
# in the route exist. It checks if the controller class is defined and
|
104
|
+
# if the action method is implemented within that controller.
|
105
|
+
#
|
106
|
+
# @param route [ActionDispatch::Journey::Route] The route to check.
|
107
|
+
# @return [Boolean] true if both the controller and action exist, false otherwise.
|
108
|
+
def valid_route_implementation?(route)
|
109
|
+
controller_name = route.defaults[:controller]&.camelize
|
110
|
+
action_name = route.defaults[:action]
|
111
|
+
|
112
|
+
return false if controller_name.blank? || action_name.blank?
|
113
|
+
|
114
|
+
controller_class = "#{controller_name}Controller".safe_constantize
|
115
|
+
|
116
|
+
if controller_class.nil?
|
117
|
+
false
|
118
|
+
else
|
119
|
+
controller_class.instance_methods.include?(action_name.to_sym)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
data/lib/oas_rails/oas_route.rb
CHANGED
@@ -19,7 +19,7 @@ module OasRails
|
|
19
19
|
@controller_path = controller_path_extractor(@rails_route.defaults[:controller])
|
20
20
|
@method = @rails_route.defaults[:action]
|
21
21
|
@verb = @rails_route.verb
|
22
|
-
@path = RouteExtractor.clean_route(@rails_route.path.spec.to_s)
|
22
|
+
@path = Extractors::RouteExtractor.clean_route(@rails_route.path.spec.to_s)
|
23
23
|
@docstring = extract_docstring
|
24
24
|
@source_string = extract_source_string
|
25
25
|
end
|
@@ -41,103 +41,5 @@ module OasRails
|
|
41
41
|
def controller_path_extractor(controller)
|
42
42
|
Rails.root.join("app/controllers/#{controller}_controller.rb").to_s
|
43
43
|
end
|
44
|
-
|
45
|
-
def detect_request_body
|
46
|
-
klass = @controller.singularize.camelize.constantize
|
47
|
-
RequestBody.from_model_class(klass:, required: true)
|
48
|
-
end
|
49
|
-
|
50
|
-
def extract_responses_from_source
|
51
|
-
render_calls = @source_string.scan(/render json: ((?:\{.*?\}|\S+))(?:, status: :(\w+))?(?:,.*?)?$/m)
|
52
|
-
|
53
|
-
return [Response.new(code: 204, description: "No Content", content: {})] if render_calls.empty?
|
54
|
-
|
55
|
-
render_calls.map do |render_content, status|
|
56
|
-
content = render_content.strip
|
57
|
-
|
58
|
-
# TODO: manage when is an array of errors
|
59
|
-
schema = {}
|
60
|
-
begin
|
61
|
-
schema = if content.start_with?('{')
|
62
|
-
Utils.hash_to_json_schema(parse_hash_structure(content))
|
63
|
-
else
|
64
|
-
# It's likely a variable or method call
|
65
|
-
maybe_a_model, errors = content.gsub('@', "").split(".")
|
66
|
-
klass = maybe_a_model.singularize.camelize(:upper).constantize
|
67
|
-
return {} unless klass.ancestors.include? ActiveRecord::Base
|
68
|
-
|
69
|
-
e = Esquema::Builder.new(klass).build_schema.as_json
|
70
|
-
if test_singularity(maybe_a_model)
|
71
|
-
if errors.nil?
|
72
|
-
e
|
73
|
-
else
|
74
|
-
{
|
75
|
-
type: "object",
|
76
|
-
properties: {
|
77
|
-
success: {
|
78
|
-
type: "boolean"
|
79
|
-
},
|
80
|
-
errors: {
|
81
|
-
type: "object",
|
82
|
-
additionalProperties: {
|
83
|
-
type: "array",
|
84
|
-
items: {
|
85
|
-
type: "string"
|
86
|
-
}
|
87
|
-
}
|
88
|
-
}
|
89
|
-
}
|
90
|
-
} end
|
91
|
-
else
|
92
|
-
{ type: "array", items: e }
|
93
|
-
end
|
94
|
-
end
|
95
|
-
rescue StandardError => e
|
96
|
-
Rails.logger.debug("Error building schema: #{e.message}")
|
97
|
-
end
|
98
|
-
|
99
|
-
status_int = status_to_integer(status)
|
100
|
-
Response.new(code: status_int, description: status_code_to_text(status_int), content: { "application/json": MediaType.new(schema:) })
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
|
-
def test_singularity(str)
|
105
|
-
str.pluralize != str && str.singularize == str
|
106
|
-
end
|
107
|
-
|
108
|
-
def parse_hash_structure(hash_literal)
|
109
|
-
structure = {}
|
110
|
-
|
111
|
-
hash_literal.scan(/(\w+):\s*(\S+)/) do |key, value|
|
112
|
-
structure[key.to_sym] = case value
|
113
|
-
when 'true', 'false'
|
114
|
-
'Boolean'
|
115
|
-
when /^\d+$/
|
116
|
-
'Number'
|
117
|
-
when '@user.errors'
|
118
|
-
'Object'
|
119
|
-
else
|
120
|
-
'Object'
|
121
|
-
end
|
122
|
-
end
|
123
|
-
|
124
|
-
structure
|
125
|
-
end
|
126
|
-
|
127
|
-
def status_to_integer(status)
|
128
|
-
return 200 if status.nil?
|
129
|
-
|
130
|
-
if status.to_s =~ /^\d+$/
|
131
|
-
status.to_i
|
132
|
-
else
|
133
|
-
status = "unprocessable_content" if status == "unprocessable_entity"
|
134
|
-
Rack::Utils::SYMBOL_TO_STATUS_CODE[status.to_sym]
|
135
|
-
|
136
|
-
end
|
137
|
-
end
|
138
|
-
|
139
|
-
def status_code_to_text(status_code)
|
140
|
-
Rack::Utils::HTTP_STATUS_CODES[status_code] || "Unknown Status Code"
|
141
|
-
end
|
142
44
|
end
|
143
45
|
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module OasRails
|
2
|
+
module Spec
|
3
|
+
class Components
|
4
|
+
include Specable
|
5
|
+
|
6
|
+
attr_accessor :schemas, :parameters, :security_schemes, :request_bodies, :responses, :headers, :examples, :links, :callbacks
|
7
|
+
|
8
|
+
def initialize(specification)
|
9
|
+
@specification = specification
|
10
|
+
@schemas = {}
|
11
|
+
@parameters = {}
|
12
|
+
@security_schemes = OasRails.config.security_schemas
|
13
|
+
@request_bodies = {}
|
14
|
+
@responses = {}
|
15
|
+
@headers = {}
|
16
|
+
@examples = {}
|
17
|
+
@links = {}
|
18
|
+
@callbacks = {}
|
19
|
+
end
|
20
|
+
|
21
|
+
def oas_fields
|
22
|
+
[:request_bodies, :examples, :responses, :schemas, :parameters, :security_schemes]
|
23
|
+
end
|
24
|
+
|
25
|
+
def add_response(response)
|
26
|
+
key = response.hash_key
|
27
|
+
@responses[key] = response unless @responses.key? key
|
28
|
+
|
29
|
+
response_reference(key)
|
30
|
+
end
|
31
|
+
|
32
|
+
def add_parameter(parameter)
|
33
|
+
key = parameter.hash_key
|
34
|
+
@parameters[key] = parameter unless @parameters.key? key
|
35
|
+
|
36
|
+
parameter_reference(key)
|
37
|
+
end
|
38
|
+
|
39
|
+
def add_request_body(request_body)
|
40
|
+
key = request_body.hash_key
|
41
|
+
@request_bodies[key] = request_body unless @request_bodies.key? key
|
42
|
+
|
43
|
+
request_body_reference(key)
|
44
|
+
end
|
45
|
+
|
46
|
+
def add_schema(schema)
|
47
|
+
key = Hashable.generate_hash(schema)
|
48
|
+
@schemas[key] = schema if @schemas[key].nil?
|
49
|
+
|
50
|
+
schema_reference(key)
|
51
|
+
end
|
52
|
+
|
53
|
+
def add_example(example)
|
54
|
+
key = Hashable.generate_hash(example)
|
55
|
+
@examples[key] = example if @examples[key].nil?
|
56
|
+
|
57
|
+
example_reference(key)
|
58
|
+
end
|
59
|
+
|
60
|
+
def create_reference(type, name)
|
61
|
+
"#/components/#{type}/#{name}"
|
62
|
+
end
|
63
|
+
|
64
|
+
def schema_reference(name)
|
65
|
+
Reference.new(create_reference('schemas', name))
|
66
|
+
end
|
67
|
+
|
68
|
+
def response_reference(name)
|
69
|
+
Reference.new(create_reference('responses', name))
|
70
|
+
end
|
71
|
+
|
72
|
+
def parameter_reference(name)
|
73
|
+
Reference.new(create_reference('parameters', name))
|
74
|
+
end
|
75
|
+
|
76
|
+
def example_reference(name)
|
77
|
+
Reference.new(create_reference('examples', name))
|
78
|
+
end
|
79
|
+
|
80
|
+
def request_body_reference(name)
|
81
|
+
Reference.new(create_reference('requestBodies', name))
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module OasRails
|
2
|
+
module Spec
|
3
|
+
class Contact
|
4
|
+
include Specable
|
5
|
+
attr_accessor :name, :url, :email
|
6
|
+
|
7
|
+
def initialize(**kwargs)
|
8
|
+
@name = kwargs[:name] || ''
|
9
|
+
@url = kwargs[:url] || ''
|
10
|
+
@email = kwargs[:email] || ''
|
11
|
+
end
|
12
|
+
|
13
|
+
def oas_fields
|
14
|
+
[:name, :url, :email]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module OasRails
|
2
|
+
module Spec
|
3
|
+
require 'digest'
|
4
|
+
|
5
|
+
module Hashable
|
6
|
+
def hash_key
|
7
|
+
Hashable.generate_hash(hash_representation)
|
8
|
+
end
|
9
|
+
|
10
|
+
def hash_representation
|
11
|
+
public_instance_variables.sort.to_h { |var| [var, instance_variable_get(var)] }
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.generate_hash(obj)
|
15
|
+
Digest::MD5.hexdigest(hash_representation_recursive(obj).to_s)
|
16
|
+
end
|
17
|
+
|
18
|
+
def public_instance_variables
|
19
|
+
instance_variables.select do |var|
|
20
|
+
method_name = var.to_s.delete('@')
|
21
|
+
respond_to?(method_name) || respond_to?("#{method_name}=")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.hash_representation_recursive(obj)
|
26
|
+
case obj
|
27
|
+
when Hash
|
28
|
+
obj.transform_values { |v| hash_representation_recursive(v) }
|
29
|
+
when Array
|
30
|
+
obj.map { |v| hash_representation_recursive(v) }
|
31
|
+
when Hashable
|
32
|
+
obj.hash_representation
|
33
|
+
else
|
34
|
+
obj
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -1,28 +1,33 @@
|
|
1
1
|
module OasRails
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
2
|
+
module Spec
|
3
|
+
class Info
|
4
|
+
include Specable
|
5
|
+
attr_accessor :title, :summary, :description, :terms_of_service, :contact, :license, :version
|
6
|
+
|
7
|
+
def initialize(**kwargs)
|
8
|
+
@title = kwargs[:title] || default_title
|
9
|
+
@summary = kwargs[:summary] || default_summary
|
10
|
+
@description = kwargs[:description] || default_description
|
11
|
+
@terms_of_service = kwargs[:terms_of_service] || ''
|
12
|
+
@contact = Spec::Contact.new
|
13
|
+
@license = Spec::License.new
|
14
|
+
@version = kwargs[:version] || '0.0.1'
|
15
|
+
end
|
16
|
+
|
17
|
+
def oas_fields
|
18
|
+
[:title, :summary, :description, :terms_of_service, :contact, :license, :version]
|
19
|
+
end
|
20
|
+
|
21
|
+
def default_title
|
22
|
+
"OasRails #{VERSION}"
|
23
|
+
end
|
24
|
+
|
25
|
+
def default_summary
|
26
|
+
"OasRails: Automatic Interactive API Documentation for Rails"
|
27
|
+
end
|
28
|
+
|
29
|
+
def default_description
|
30
|
+
"# Welcome to OasRails
|
26
31
|
|
27
32
|
OasRails automatically generates interactive documentation for your Rails APIs using the OpenAPI Specification 3.1 (OAS 3.1) and displays it with a nice UI.
|
28
33
|
|
@@ -55,6 +60,7 @@ Explore your API documentation and enjoy the power of OasRails!
|
|
55
60
|
For more information and advanced usage, visit the [OasRails GitHub repository](https://github.com/a-chacon/oas_rails).
|
56
61
|
|
57
62
|
"
|
63
|
+
end
|
58
64
|
end
|
59
65
|
end
|
60
66
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module OasRails
|
2
|
+
module Spec
|
3
|
+
class License
|
4
|
+
include Specable
|
5
|
+
|
6
|
+
attr_accessor :name, :url
|
7
|
+
|
8
|
+
def initialize(**kwargs)
|
9
|
+
@name = kwargs[:name] || 'GPL 3.0'
|
10
|
+
@url = kwargs[:url] || 'https://www.gnu.org/licenses/gpl-3.0.html#license-text'
|
11
|
+
end
|
12
|
+
|
13
|
+
def oas_fields
|
14
|
+
[:name, :url]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|