oas_rails 0.11.0 → 0.13.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0c02c7a454cec38660f5d327e61f35c88e3d7ab22b4fc5c18adbed3707c656d7
4
- data.tar.gz: 8fb227b665f88c0c6299f1a1bbd9d4c681f1438c13fa93e6f552cd96b1ef7a00
3
+ metadata.gz: d0b32fa577fb8e747811369e952c3508011a6270366784ab3eb0ff69d579c51b
4
+ data.tar.gz: 251e05d5a07104a8cea8635197dbf110d0749576f383aa38de6ebe7cebf89c06
5
5
  SHA512:
6
- metadata.gz: 2d30c6f893f17476716208e57311b2c290f99208431781085de77d97d9c6c2d5601df5c29e53155d485a3e2d1268383ca6151dd97ae629461db2b8c369df9cdc
7
- data.tar.gz: 6b7edd8df46e2ea3b8c20fdbc41ceed453f560a0c619fed3138ae49a5366625e34b36fd0ee6c5d1aff549602c00ae2e4e1b880b58ae1e6f62ffed4c1578e4c6d
6
+ metadata.gz: 9bcb332e43ce4caa1be24e598589a9fdba1dad280488d9c3a6b3764b0037bba2b484c525f5733bfd26fa77b76ee3f9944c88c6d6fbe3dc6649c33203d1cf9d47
7
+ data.tar.gz: 4153c860f49b50c870e3ee86382b08fa29686e96aef9f2e39056b4d4f6364617963e6d71a1e48e3bdee25ef7149548c5974986c7884c5ab6be235b642d13a8c0
data/README.md CHANGED
@@ -39,7 +39,7 @@ The goal is to minimize the effort required to create comprehensive documentatio
39
39
 
40
40
  ## Documentation
41
41
 
42
- For see how to install, configure and use OasRails please refere to the [OasRailsBook](http://a-chacon.com/oas_rails/book)
42
+ For see how to install, configure and use OasRails please refere to the [OasRailsBook](http://a-chacon.com/oas_rails)
43
43
 
44
44
  ## Contributing
45
45
 
@@ -133,6 +133,7 @@
133
133
  nav-item-spacing="relaxed"
134
134
  allow-spec-file-download="true"
135
135
  schema-style="table"
136
+ sort-tags="true"
136
137
  >
137
138
  </rapi-doc>
138
139
 
@@ -26,6 +26,8 @@ OasRails.configure do |config|
26
26
 
27
27
  3. Use Yard tags in your controller methods to provide detailed API endpoint descriptions.
28
28
 
29
+ Docs: <https://a-chacon.com/oas_rails/>
30
+
29
31
  ## Features
30
32
 
31
33
  - Automatic OAS 3.1 document generation
@@ -105,6 +107,7 @@ OasRails.configure do |config|
105
107
  # Example, if you add forbidden then it will be added only if the endpoint requires authentication.
106
108
  # Example: not_found will be setted to the endpoint only if the operation is a show/update/destroy action.
107
109
  # config.set_default_responses = true
108
- # config.possible_default_responses = [:not_found, :unauthorized, :forbidden]
109
- # config.response_body_of_default = { message: String }
110
+ # config.possible_default_responses = [:not_found, :unauthorized, :forbidden, :internal_server_error, :unprocessable_entity]
111
+ # config.response_body_of_default = "Hash{ message: String }"
112
+ # config.response_body_of_unprocessable_entity= "Hash{ errors: Array<String> }"
110
113
  end
@@ -33,7 +33,7 @@ module OasRails
33
33
  end
34
34
 
35
35
  def from_model_class(klass)
36
- return self unless klass.ancestors.map(&:to_s).include? 'ActiveRecord::Base'
36
+ return self unless Utils.active_record_class?(klass)
37
37
 
38
38
  model_schema = Builders::EsquemaBuilder.send("build_#{@context}_schema", klass:)
39
39
  model_schema["required"] = []
@@ -18,7 +18,7 @@ module OasRails
18
18
  end
19
19
 
20
20
  def from_tags(tag:, examples_tags: [])
21
- if tag.klass.ancestors.map(&:to_s).include? 'ActiveRecord::Base'
21
+ if Utils.active_record_class?(tag.klass)
22
22
  from_model_class(klass: tag.klass, description: tag.text, required: tag.required, examples_tags:)
23
23
  else
24
24
  @request_body.description = tag.text
@@ -32,29 +32,49 @@ module OasRails
32
32
  def add_default_responses(oas_route, security)
33
33
  return self unless OasRails.config.set_default_responses
34
34
 
35
- content = ContentBuilder.new(@specification, :outgoing).with_schema(JsonSchemaGenerator.process_string(OasRails.config.response_body_of_default)[:json_schema]).build
35
+ common_errors = determine_common_errors(oas_route, security)
36
+ add_responses_for_errors(common_errors)
37
+
38
+ self
39
+ end
40
+
41
+ def build
42
+ @responses
43
+ end
44
+
45
+ private
46
+
47
+ def determine_common_errors(oas_route, security)
36
48
  common_errors = []
37
- common_errors.push(:unauthorized, :forbidden) if security
49
+ common_errors.push(:unauthorized, :forbidden, :internal_server_error) if security
38
50
 
39
51
  case oas_route.method
40
- when "show", "update", "destroy"
52
+ when "show", "destroy"
41
53
  common_errors.push(:not_found)
42
- when "create", "index"
43
- # possible errors for this methods?
54
+ when "create"
55
+ common_errors.push(:unprocessable_entity)
56
+ when "update"
57
+ common_errors.push(:not_found, :unprocessable_entity)
44
58
  end
45
59
 
46
- (OasRails.config.possible_default_responses & common_errors).each do |e|
47
- code = Utils.status_to_integer(e)
60
+ OasRails.config.possible_default_responses & common_errors
61
+ end
62
+
63
+ def add_responses_for_errors(errors)
64
+ errors.each do |error|
65
+ response_body = resolve_response_body(error)
66
+ content = ContentBuilder.new(@specification, :outgoing).with_schema(JsonSchemaGenerator.process_string(response_body)[:json_schema]).build
67
+ code = Utils.status_to_integer(error)
48
68
  response = ResponseBuilder.new(@specification).with_code(code).with_description(Utils.get_definition(code)).with_content(content).build
49
69
 
50
70
  @responses.add_response(response) if @responses.responses[response.code].blank?
51
71
  end
52
-
53
- self
54
72
  end
55
73
 
56
- def build
57
- @responses
74
+ def resolve_response_body(error)
75
+ OasRails.config.public_send("response_body_of_#{error}")
76
+ rescue StandardError
77
+ OasRails.config.response_body_of_default
58
78
  end
59
79
  end
60
80
  end
@@ -11,11 +11,11 @@ module OasRails
11
11
  :authenticate_all_routes_by_default,
12
12
  :set_default_responses,
13
13
  :possible_default_responses,
14
- :response_body_of_default,
15
14
  :http_verbs,
16
15
  :use_model_names,
17
16
  :rapidoc_theme
18
- attr_reader :servers, :tags, :security_schema
17
+
18
+ attr_reader :servers, :tags, :security_schema, :include_mode, :response_body_of_default
19
19
 
20
20
  def initialize
21
21
  @info = Spec::Info.new
@@ -32,11 +32,28 @@ module OasRails
32
32
  @security_schema = nil
33
33
  @security_schemas = {}
34
34
  @set_default_responses = true
35
- @possible_default_responses = [:not_found, :unauthorized, :forbidden]
35
+ @possible_default_responses = [:not_found, :unauthorized, :forbidden, :internal_server_error, :unprocessable_entity]
36
36
  @http_verbs = [:get, :post, :put, :patch, :delete]
37
- @response_body_of_default = "Hash{ success: !Boolean, message: String }"
37
+ @response_body_of_default = "Hash{ status: !Integer, error: String }"
38
38
  @use_model_names = false
39
39
  @rapidoc_theme = :rails
40
+ @include_mode = :all
41
+
42
+ @possible_default_responses.each do |response|
43
+ method_name = "response_body_of_#{response}="
44
+ variable_name = "@response_body_of_#{response}"
45
+
46
+ define_singleton_method(method_name) do |value|
47
+ raise ArgumentError, "#{method_name} must be a String With a valid object" unless value.is_a?(String)
48
+
49
+ OasRails::JsonSchemaGenerator.parse_type(value)
50
+ instance_variable_set(variable_name, value)
51
+ end
52
+
53
+ define_singleton_method("response_body_of_#{response}") do
54
+ instance_variable_get(variable_name) || @response_body_of_default
55
+ end
56
+ end
40
57
  end
41
58
 
42
59
  def security_schema=(value)
@@ -64,6 +81,20 @@ module OasRails
64
81
  def excluded_columns_outgoing
65
82
  []
66
83
  end
84
+
85
+ def include_mode=(value)
86
+ valid_modes = [:all, :with_tags, :explicit]
87
+ raise ArgumentError, "include_mode must be one of #{valid_modes}" unless valid_modes.include?(value)
88
+
89
+ @include_mode = value
90
+ end
91
+
92
+ def response_body_of_default=(value)
93
+ raise ArgumentError, "response_body_of_default must be a String With a valid object" unless value.is_a?(String)
94
+
95
+ OasRails::JsonSchemaGenerator.parse_type(value)
96
+ @response_body_of_default = value
97
+ end
67
98
  end
68
99
 
69
100
  DEFAULT_SECURITY_SCHEMES = {
@@ -60,7 +60,7 @@ module OasRails
60
60
  maybe_a_model, errors = content.gsub('@', "").split(".")
61
61
  klass = maybe_a_model.singularize.camelize(:upper).constantize
62
62
 
63
- if klass.ancestors.map(&:to_s).include? 'ActiveRecord::Base'
63
+ if Utils.active_record_class?(klass)
64
64
  schema = Builders::EsquemaBuilder.build_outgoing_schema(klass:)
65
65
  if test_singularity(maybe_a_model)
66
66
  build_singular_model_schema_and_examples(maybe_a_model, errors, klass, schema)
@@ -45,41 +45,14 @@ module OasRails
45
45
  route.gsub('(.:format)', '').gsub(/:\w+/) { |match| "{#{match[1..]}}" }
46
46
  end
47
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
48
  private
80
49
 
81
50
  def extract_host_routes
82
- valid_routes.map { |r| OasRoute.new_from_rails_route(rails_route: r) }
51
+ routes = valid_routes.map { |r| OasRoute.new_from_rails_route(rails_route: r) }
52
+
53
+ routes.select! { |route| route.docstring.tags.any? } if OasRails.config.include_mode == :with_tags
54
+ routes.select! { |route| route.docstring.tags.any? { |t| t.tag_name == "oas_include" } } if OasRails.config.include_mode == :explicit
55
+ routes
83
56
  end
84
57
 
85
58
  def valid_routes
@@ -29,6 +29,8 @@ module OasRails
29
29
  { type: :object, required:, properties: parse_object_properties(::Regexp.last_match(1)) }
30
30
  when /^Array<(.+)>$/i
31
31
  { type: :array, required:, items: parse_type(::Regexp.last_match(1)) }
32
+ when ->(t) { Utils.active_record_class?(t) }
33
+ Builders::EsquemaBuilder.build_outgoing_schema(klass: type.constantize)
32
34
  else
33
35
  { type: type.downcase.to_sym, required: }
34
36
  end
@@ -100,6 +102,8 @@ module OasRails
100
102
  type: 'array',
101
103
  items: to_json_schema(parsed[:items])
102
104
  }
105
+ when nil
106
+ parsed
103
107
  else
104
108
  ruby_type_to_json_schema_type(parsed[:type])
105
109
  end
@@ -14,6 +14,8 @@ module OasRails
14
14
  }.freeze
15
15
 
16
16
  HTTP_STATUS_DEFINITIONS = {
17
+ 200 => "The request has succeeded.",
18
+ 201 => "The request has been fulfilled and resulted in a new resource being created.",
17
19
  404 => "The requested resource could not be found.",
18
20
  401 => "You are not authorized to access this resource. You need to authenticate yourself first.",
19
21
  403 => "You are not allowed to access this resource. You do not have the necessary permissions.",
@@ -34,19 +36,6 @@ module OasRails
34
36
  end
35
37
  end
36
38
 
37
- # TODO: check if it is in use
38
- def type_to_schema(type_string)
39
- if type_string.start_with?('Array<')
40
- inner_type = type_string[/Array<(.+)>$/, 1]
41
- {
42
- "type" => "array",
43
- "items" => type_to_schema(inner_type)
44
- }
45
- else
46
- { "type" => TYPE_MAPPING.fetch(type_string, 'string') }
47
- end
48
- end
49
-
50
39
  def hash_to_json_schema(hash)
51
40
  {
52
41
  type: 'object',
@@ -116,6 +105,16 @@ module OasRails
116
105
 
117
106
  nil # Return nil if no matching constant is found
118
107
  end
108
+
109
+ # Checks if a given text refers to an ActiveRecord class.
110
+ # @param text [String] The text to check.
111
+ # @return [Boolean] True if the text refers to an ActiveRecord class, false otherwise.
112
+ def active_record_class?(klass_or_string)
113
+ klass = klass_or_string.is_a?(Class) ? klass_or_string : klass_or_string.constantize
114
+ klass.ancestors.map(&:to_s).include? 'ActiveRecord::Base'
115
+ rescue StandardError
116
+ false
117
+ end
119
118
  end
120
119
  end
121
120
  end
@@ -1,3 +1,3 @@
1
1
  module OasRails
2
- VERSION = "0.11.0"
2
+ VERSION = "0.13.0"
3
3
  end
@@ -147,23 +147,13 @@ module OasRails
147
147
  match
148
148
  end
149
149
 
150
- # Checks if a given text refers to an ActiveRecord class.
151
- # @param text [String] The text to check.
152
- # @return [Boolean] True if the text refers to an ActiveRecord class, false otherwise.
153
- def active_record_class?(text)
154
- klass = text.constantize
155
- klass.ancestors.map(&:to_s).include? 'ActiveRecord::Base'
156
- rescue StandardError
157
- false
158
- end
159
-
160
150
  # Converts type text to a schema, checking if it's an ActiveRecord class.
161
151
  # @param text [String] The type text to convert.
162
152
  # @return [Array] An array containing the class, schema, and required flag.
163
153
  def type_text_to_schema(text)
164
154
  type_text, required = text_and_required(text)
165
155
 
166
- if active_record_class?(type_text)
156
+ if Utils.active_record_class?(type_text)
167
157
  klass = type_text.constantize
168
158
  schema = Builders::EsquemaBuilder.build_outgoing_schema(klass:)
169
159
  else
data/lib/oas_rails.rb CHANGED
@@ -90,7 +90,8 @@ module OasRails
90
90
  'Endpoint Tags' => [:tags],
91
91
  'Summary' => [:summary],
92
92
  'No Auth' => [:no_auth],
93
- 'Auth methods' => [:auth, :with_types]
93
+ 'Auth methods' => [:auth, :with_types],
94
+ 'OAS Include' => [:oas_include]
94
95
  }
95
96
  yard_tags.each do |tag_name, (method_name, handler)|
96
97
  ::YARD::Tags::Library.define_tag(tag_name, method_name, handler)
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: oas_rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.11.0
4
+ version: 0.13.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - a-chacon
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-04-07 00:00:00.000000000 Z
11
+ date: 2025-04-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: easy_talk
14
+ name: easy_talk_two
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.0'
19
+ version: 1.1.2
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '1.0'
26
+ version: 1.1.2
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: method_source
29
29
  requirement: !ruby/object:Gem::Requirement