oas_rails 0.1.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 +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +147 -0
- data/Rakefile +8 -0
- data/app/assets/config/oas_rails_manifest.js +1 -0
- data/app/assets/stylesheets/oas_rails/application.css +15 -0
- data/app/controllers/oas_rails/application_controller.rb +4 -0
- data/app/controllers/oas_rails/oas_rails_controller.rb +9 -0
- data/app/helpers/oas_rails/application_helper.rb +4 -0
- data/app/helpers/oas_rails/oas_rails_helper.rb +4 -0
- data/app/helpers/oas_rails/test_helper.rb +4 -0
- data/app/jobs/oas_rails/application_job.rb +4 -0
- data/app/mailers/oas_rails/application_mailer.rb +6 -0
- data/app/models/oas_rails/application_record.rb +5 -0
- data/app/views/layouts/oas_rails/application.html.erb +18 -0
- data/app/views/oas_rails/oas_rails/index.html.erb +1 -0
- data/app/views/oas_rails/test/show.html.erb +1 -0
- data/config/routes.rb +4 -0
- data/lib/generators/oas_rails/config/config_generator.rb +11 -0
- data/lib/generators/oas_rails/config/templates/oas_rails_initializer.rb +49 -0
- data/lib/oas_rails/configuration.rb +28 -0
- data/lib/oas_rails/contact.rb +12 -0
- data/lib/oas_rails/engine.rb +5 -0
- data/lib/oas_rails/info.rb +60 -0
- data/lib/oas_rails/license.rb +11 -0
- data/lib/oas_rails/media_type.rb +57 -0
- data/lib/oas_rails/oas_base.rb +29 -0
- data/lib/oas_rails/oas_route.rb +143 -0
- data/lib/oas_rails/operation.rb +118 -0
- data/lib/oas_rails/parameter.rb +47 -0
- data/lib/oas_rails/path_item.rb +25 -0
- data/lib/oas_rails/paths.rb +19 -0
- data/lib/oas_rails/request_body.rb +29 -0
- data/lib/oas_rails/response.rb +12 -0
- data/lib/oas_rails/responses.rb +20 -0
- data/lib/oas_rails/route_extractor.rb +87 -0
- data/lib/oas_rails/server.rb +10 -0
- data/lib/oas_rails/specification.rb +42 -0
- data/lib/oas_rails/tag.rb +17 -0
- data/lib/oas_rails/version.rb +3 -0
- data/lib/oas_rails/yard/oas_yard_factory.rb +160 -0
- data/lib/oas_rails.rb +120 -0
- metadata +159 -0
@@ -0,0 +1,143 @@
|
|
1
|
+
module OasRails
|
2
|
+
class OasRoute
|
3
|
+
attr_accessor(:controller_class, :controller_action, :controller, :controller_path, :method, :verb, :path,
|
4
|
+
:rails_route, :docstring, :source_string)
|
5
|
+
|
6
|
+
def initialize; end
|
7
|
+
|
8
|
+
def self.new_from_rails_route(rails_route: ActionDispatch::Journey::Route)
|
9
|
+
instance = new
|
10
|
+
instance.rails_route = rails_route
|
11
|
+
instance.extract_rails_route_data
|
12
|
+
instance
|
13
|
+
end
|
14
|
+
|
15
|
+
def extract_rails_route_data
|
16
|
+
@controller_action = "#{@rails_route.defaults[:controller].camelize}Controller##{@rails_route.defaults[:action]}"
|
17
|
+
@controller_class = "#{@rails_route.defaults[:controller].camelize}Controller"
|
18
|
+
@controller = @rails_route.defaults[:controller]
|
19
|
+
@controller_path = controller_path_extractor(@rails_route.defaults[:controller])
|
20
|
+
@method = @rails_route.defaults[:action]
|
21
|
+
@verb = @rails_route.verb
|
22
|
+
@path = RouteExtractor.clean_route(@rails_route.path.spec.to_s)
|
23
|
+
@docstring = extract_docstring
|
24
|
+
@source_string = extract_source_string
|
25
|
+
end
|
26
|
+
|
27
|
+
def extract_docstring
|
28
|
+
YARD::Docstring.parser.parse(
|
29
|
+
controller_class.constantize.instance_method(method).comment.lines.map { |line| line.sub(/^#\s*/, '') }.join
|
30
|
+
).to_docstring
|
31
|
+
end
|
32
|
+
|
33
|
+
def extract_source_string
|
34
|
+
@controller_class.constantize.instance_method(method).source
|
35
|
+
end
|
36
|
+
|
37
|
+
def path_params
|
38
|
+
@rails_route.path.spec.to_s.scan(/:(\w+)/).flatten.reject! { |e| e == 'format' }
|
39
|
+
end
|
40
|
+
|
41
|
+
def controller_path_extractor(controller)
|
42
|
+
Rails.root.join("app/controllers/#{controller}_controller.rb").to_s
|
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
|
+
OasRails.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
|
+
end
|
143
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
module OasRails
|
2
|
+
class Operation < OasBase
|
3
|
+
attr_accessor :tags, :summary, :description, :operation_id, :parameters, :method, :docstring, :request_body, :responses
|
4
|
+
|
5
|
+
def initialize(method:, summary:, operation_id:, **kwargs)
|
6
|
+
super()
|
7
|
+
@method = method
|
8
|
+
@summary = summary
|
9
|
+
@operation_id = operation_id
|
10
|
+
@tags = kwargs[:tags] || []
|
11
|
+
@description = kwargs[:description] || @summary
|
12
|
+
@parameters = kwargs[:parameters] || []
|
13
|
+
@request_body = kwargs[:request_body] || {}
|
14
|
+
@responses = kwargs[:responses] || {}
|
15
|
+
end
|
16
|
+
|
17
|
+
class << self
|
18
|
+
def from_oas_route(oas_route:)
|
19
|
+
summary = extract_summary(oas_route:)
|
20
|
+
operation_id = extract_operation_id(oas_route:)
|
21
|
+
tags = extract_tags(oas_route:)
|
22
|
+
description = oas_route.docstring
|
23
|
+
parameters = extract_parameters(oas_route:)
|
24
|
+
request_body = extract_request_body(oas_route:)
|
25
|
+
responses = extract_responses(oas_route:)
|
26
|
+
new(method: oas_route.verb.downcase, summary:, operation_id:, tags:, description:, parameters:, request_body:, responses:)
|
27
|
+
end
|
28
|
+
|
29
|
+
def extract_summary(oas_route:)
|
30
|
+
oas_route.docstring.tags(:summary).first.try(:text) || generate_crud_name(oas_route.method, oas_route.controller.downcase) || oas_route.verb + " " + oas_route.path
|
31
|
+
end
|
32
|
+
|
33
|
+
def generate_crud_name(method, controller)
|
34
|
+
controller_name = controller.to_s.underscore.humanize.downcase.pluralize
|
35
|
+
|
36
|
+
case method.to_sym
|
37
|
+
when :index
|
38
|
+
"List #{controller_name}"
|
39
|
+
when :show
|
40
|
+
"View #{controller_name.singularize}"
|
41
|
+
when :create
|
42
|
+
"Create new #{controller_name.singularize}"
|
43
|
+
when :update
|
44
|
+
"Update #{controller_name.singularize}"
|
45
|
+
when :destroy
|
46
|
+
"Delete #{controller_name.singularize}"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def extract_operation_id(oas_route:)
|
51
|
+
"#{oas_route.method}#{oas_route.path.gsub('/', '_').gsub(/[{}]/, '')}"
|
52
|
+
end
|
53
|
+
|
54
|
+
# This method should check tags defined by yard, then extract tag from path namespace or controller name depending on configuration
|
55
|
+
def extract_tags(oas_route:)
|
56
|
+
tags = oas_route.docstring.tags(:tags).first
|
57
|
+
if !tags.nil?
|
58
|
+
tags.text.split(",").map(&:strip).map(&:titleize)
|
59
|
+
else
|
60
|
+
default_tags(oas_route:)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def default_tags(oas_route:)
|
65
|
+
tags = []
|
66
|
+
if OasRails.config.default_tags_from == "namespace"
|
67
|
+
tag = oas_route.path.split('/').reject(&:empty?).first.try(:titleize)
|
68
|
+
tags << tag unless tag.nil?
|
69
|
+
else
|
70
|
+
tags << oas_route.controller.titleize
|
71
|
+
end
|
72
|
+
tags
|
73
|
+
end
|
74
|
+
|
75
|
+
def extract_parameters(oas_route:)
|
76
|
+
parameters = []
|
77
|
+
parameters.concat(parameters_from_tags(tags: oas_route.docstring.tags(:parameter)))
|
78
|
+
oas_route.path_params.try(:map) do |p|
|
79
|
+
parameters << Parameter.from_path(path: oas_route.path, param: p) unless parameters.any? { |param| param.name.to_s == p.to_s }
|
80
|
+
end
|
81
|
+
parameters
|
82
|
+
end
|
83
|
+
|
84
|
+
def parameters_from_tags(tags:)
|
85
|
+
tags.map do |t|
|
86
|
+
Parameter.new(name: t.name, location: t.location, required: t.required, schema: t.schema, description: t.text)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def extract_request_body(oas_route:)
|
91
|
+
tag_request_body = oas_route.docstring.tags(:request_body).first
|
92
|
+
if tag_request_body.nil? && OasRails.config.request_body_automatically
|
93
|
+
oas_route.detect_request_body if %w[create update].include? oas_route.method
|
94
|
+
elsif !tag_request_body.nil?
|
95
|
+
RequestBody.from_tags(tag: tag_request_body, examples_tags: oas_route.docstring.tags(:request_body_example))
|
96
|
+
else
|
97
|
+
{}
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def extract_responses(oas_route:)
|
102
|
+
responses = Responses.from_tags(tags: oas_route.docstring.tags(:response))
|
103
|
+
|
104
|
+
if OasRails.config.autodiscover_responses
|
105
|
+
new_responses = oas_route.extract_responses_from_source
|
106
|
+
|
107
|
+
new_responses.each do |new_response|
|
108
|
+
responses.responses << new_response unless responses.responses.any? { |r| r.code == new_response.code }
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
responses
|
113
|
+
end
|
114
|
+
|
115
|
+
def external_docs; end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module OasRails
|
2
|
+
class Parameter
|
3
|
+
STYLE_DEFAULTS = { query: 'form', path: 'simple', header: 'simple', cookie: 'form' }.freeze
|
4
|
+
|
5
|
+
attr_accessor :name, :in, :style, :description, :required, :schema
|
6
|
+
|
7
|
+
def initialize(name:, location:, description:, **kwargs)
|
8
|
+
@name = name
|
9
|
+
@in = location
|
10
|
+
@description = description
|
11
|
+
|
12
|
+
@required = kwargs[:required] || required?
|
13
|
+
@style = kwargs[:style] || default_from_in
|
14
|
+
@schema = kwargs[:schema] || { "type": 'string' }
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.from_path(path:, param:)
|
18
|
+
new(name: param, location: 'path',
|
19
|
+
description: "#{param.split('_')[-1].titleize} of existing #{extract_word_before(path, param).singularize}.")
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.extract_word_before(string, param)
|
23
|
+
regex = %r{/(\w+)/\{#{param}\}}
|
24
|
+
match = string.match(regex)
|
25
|
+
match ? match[1] : nil
|
26
|
+
end
|
27
|
+
|
28
|
+
def default_from_in
|
29
|
+
STYLE_DEFAULTS[@in.to_sym]
|
30
|
+
end
|
31
|
+
|
32
|
+
def required?
|
33
|
+
@in == 'path'
|
34
|
+
end
|
35
|
+
|
36
|
+
def to_spec
|
37
|
+
{
|
38
|
+
"name": @name,
|
39
|
+
"in": @in,
|
40
|
+
"description": @description,
|
41
|
+
"required": @required,
|
42
|
+
"schema": @schema,
|
43
|
+
"style": @style
|
44
|
+
}
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module OasRails
|
2
|
+
class PathItem
|
3
|
+
attr_reader :path, :operations, :parameters
|
4
|
+
|
5
|
+
def initialize(path:, operations:, parameters:)
|
6
|
+
@path = path
|
7
|
+
@operations = operations
|
8
|
+
@parameters = parameters
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.from_oas_routes(path:, oas_routes:)
|
12
|
+
new(path: path, operations: oas_routes.map do |oas_route|
|
13
|
+
Operation.from_oas_route(oas_route: oas_route)
|
14
|
+
end, parameters: [])
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_spec
|
18
|
+
spec = {}
|
19
|
+
@operations.each do |o|
|
20
|
+
spec[o.method] = o.to_spec
|
21
|
+
end
|
22
|
+
spec
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module OasRails
|
2
|
+
class Paths
|
3
|
+
attr_accessor :path_items
|
4
|
+
|
5
|
+
def initialize(path_items:)
|
6
|
+
@path_items = path_items
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.from_string_paths(string_paths:)
|
10
|
+
new(path_items: string_paths.map do |s|
|
11
|
+
PathItem.from_oas_routes(path: s, oas_routes: RouteExtractor.host_routes_by_path(s))
|
12
|
+
end)
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_spec
|
16
|
+
@path_items.each_with_object({}) { |p, object| object[p.path] = p.to_spec }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module OasRails
|
2
|
+
class RequestBody < OasBase
|
3
|
+
attr_accessor :description, :content, :required
|
4
|
+
|
5
|
+
def initialize(description:, content:, required: false)
|
6
|
+
super()
|
7
|
+
@description = description
|
8
|
+
@content = content # Should be an array of media type object
|
9
|
+
@required = required
|
10
|
+
end
|
11
|
+
|
12
|
+
class << self
|
13
|
+
def from_tags(tag:, examples_tags: [])
|
14
|
+
if tag.klass.ancestors.include? ActiveRecord::Base
|
15
|
+
from_model_class(klass: tag.klass, description: tag.text, required: tag.required, examples_tags:)
|
16
|
+
else
|
17
|
+
# hash content to schema
|
18
|
+
content = { "application/json": MediaType.new(schema: tag.schema, examples: MediaType.tags_to_examples(tags: examples_tags)) }
|
19
|
+
new(description: tag.text, content:, required: tag.required)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def from_model_class(klass:, **kwargs)
|
24
|
+
content = { "application/json": MediaType.from_model_class(klass:, examples: MediaType.tags_to_examples(tags: kwargs[:examples_tags] || {})) }
|
25
|
+
new(description: kwargs[:description] || klass.to_s, content:, required: kwargs[:required])
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module OasRails
|
2
|
+
class Response < OasBase
|
3
|
+
attr_accessor :code, :description, :content
|
4
|
+
|
5
|
+
def initialize(code:, description:, content:)
|
6
|
+
super()
|
7
|
+
@code = code
|
8
|
+
@description = description
|
9
|
+
@content = content # Should be an array of media type object
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module OasRails
|
2
|
+
class Responses < OasBase
|
3
|
+
attr_accessor :responses
|
4
|
+
|
5
|
+
def initialize(responses)
|
6
|
+
super()
|
7
|
+
@responses = responses
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_spec
|
11
|
+
@responses.each_with_object({}) { |r, object| object[r.code] = r.to_spec }
|
12
|
+
end
|
13
|
+
|
14
|
+
class << self
|
15
|
+
def from_tags(tags:)
|
16
|
+
new(tags.map { |t| Response.new(code: t.name.to_i, description: t.text, content: { "application/json": MediaType.new(schema: t.schema) }) })
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module OasRails
|
2
|
+
class RouteExtractor
|
3
|
+
RAILS_DEFAULT_CONTROLLERS = %w[
|
4
|
+
rails/info
|
5
|
+
rails/mailers
|
6
|
+
active_storage/blobs
|
7
|
+
active_storage/disk
|
8
|
+
active_storage/direct_uploads
|
9
|
+
active_storage/representations
|
10
|
+
rails/conductor/continuous_integration
|
11
|
+
rails/conductor/multiple_databases
|
12
|
+
rails/conductor/action_mailbox
|
13
|
+
rails/conductor/action_text
|
14
|
+
action_cable
|
15
|
+
].freeze
|
16
|
+
|
17
|
+
RAILS_DEFAULT_PATHS = %w[
|
18
|
+
/rails/action_mailbox/
|
19
|
+
].freeze
|
20
|
+
|
21
|
+
class << self
|
22
|
+
def host_routes_by_path(path)
|
23
|
+
@host_routes ||= extract_host_routes
|
24
|
+
@host_routes.select { |r| r.path == path }
|
25
|
+
end
|
26
|
+
|
27
|
+
def host_routes
|
28
|
+
@host_routes ||= extract_host_routes
|
29
|
+
end
|
30
|
+
|
31
|
+
def host_paths
|
32
|
+
@host_paths ||= host_routes.map(&:path).uniq.sort
|
33
|
+
end
|
34
|
+
|
35
|
+
def clean_route(route)
|
36
|
+
route.gsub('(.:format)', '').gsub(/:\w+/) { |match| "{#{match[1..]}}" }
|
37
|
+
end
|
38
|
+
|
39
|
+
# THIS CODE IS NOT IN USE BUT CAN BE USEFULL WITH GLOBAL TAGS OR AUTH TAGS
|
40
|
+
# def get_controller_comments(controller_path)
|
41
|
+
# YARD.parse_string(File.read(controller_path))
|
42
|
+
# controller_class = YARD::Registry.all(:class).first
|
43
|
+
# if controller_class
|
44
|
+
# class_comment = controller_class.docstring.all
|
45
|
+
# method_comments = controller_class.meths.map do |method|
|
46
|
+
# {
|
47
|
+
# name: method.name,
|
48
|
+
# comment: method.docstring.all
|
49
|
+
# }
|
50
|
+
# end
|
51
|
+
# YARD::Registry.clear
|
52
|
+
# {
|
53
|
+
# class_comment: class_comment,
|
54
|
+
# method_comments: method_comments
|
55
|
+
# }
|
56
|
+
# else
|
57
|
+
# YARD::Registry.clear
|
58
|
+
# nil
|
59
|
+
# end
|
60
|
+
# rescue StandardError
|
61
|
+
# nil
|
62
|
+
# end
|
63
|
+
#
|
64
|
+
# def get_controller_comment(controller_path)
|
65
|
+
# get_controller_comments(controller_path)&.dig(:class_comment) || ''
|
66
|
+
# rescue StandardError
|
67
|
+
# ''
|
68
|
+
# end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def extract_host_routes
|
73
|
+
Rails.application.routes.routes.select do |route|
|
74
|
+
valid_api_route?(route)
|
75
|
+
end.map { |r| OasRoute.new_from_rails_route(rails_route: r) }
|
76
|
+
end
|
77
|
+
|
78
|
+
def valid_api_route?(route)
|
79
|
+
return false if route.defaults[:controller].nil?
|
80
|
+
return false if RAILS_DEFAULT_CONTROLLERS.any? { |default| route.defaults[:controller].start_with?(default) }
|
81
|
+
return false if RAILS_DEFAULT_PATHS.any? { |path| route.path.spec.to_s.include?(path) }
|
82
|
+
|
83
|
+
true
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module OasRails
|
4
|
+
class Specification
|
5
|
+
def initialize
|
6
|
+
OasRails.configure_esquema!
|
7
|
+
OasRails.configure_yard!
|
8
|
+
@specification = base_spec
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_json(*_args)
|
12
|
+
@specification.to_json
|
13
|
+
rescue StandardError => e
|
14
|
+
Rails.logger.error("Error Generating OAS: #{e.message}")
|
15
|
+
{}
|
16
|
+
end
|
17
|
+
|
18
|
+
def base_spec
|
19
|
+
{
|
20
|
+
openapi: '3.1.0',
|
21
|
+
info: OasRails.config.info.to_spec,
|
22
|
+
servers: OasRails.config.servers.map(&:to_spec),
|
23
|
+
paths: paths_spec,
|
24
|
+
components: components_spec,
|
25
|
+
security: [],
|
26
|
+
tags: OasRails.config.tags.map(&:to_spec),
|
27
|
+
externalDocs: {}
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
31
|
+
def paths_spec
|
32
|
+
Paths.from_string_paths(string_paths: RouteExtractor.host_paths).to_spec
|
33
|
+
end
|
34
|
+
|
35
|
+
def components_spec
|
36
|
+
{
|
37
|
+
schemas: {}, parameters: {}, securitySchemas: {}, requestBodies: {}, responses: {},
|
38
|
+
headers: {}, examples: {}, links: {}, callbacks: {}
|
39
|
+
}
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module OasRails
|
2
|
+
class Tag
|
3
|
+
attr_accessor :name, :description
|
4
|
+
|
5
|
+
def initialize(name:, description:)
|
6
|
+
@name = name.titleize
|
7
|
+
@description = description
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_spec
|
11
|
+
{
|
12
|
+
name: @name,
|
13
|
+
description: @description
|
14
|
+
}
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|