Skip to content

Commit

Permalink
feat: add @response_example tag for document examples of responses. (#43
Browse files Browse the repository at this point in the history
)

* feat: add @response_example tag for document examples of responses.

* fix: rexml update
  • Loading branch information
a-chacon authored Aug 26, 2024
1 parent 268cb46 commit 16f37c4
Show file tree
Hide file tree
Showing 11 changed files with 144 additions and 60 deletions.
108 changes: 54 additions & 54 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -10,29 +10,29 @@ PATH
GEM
remote: https://rubygems.org/
specs:
actioncable (7.2.0)
actionpack (= 7.2.0)
activesupport (= 7.2.0)
actioncable (7.2.1)
actionpack (= 7.2.1)
activesupport (= 7.2.1)
nio4r (~> 2.0)
websocket-driver (>= 0.6.1)
zeitwerk (~> 2.6)
actionmailbox (7.2.0)
actionpack (= 7.2.0)
activejob (= 7.2.0)
activerecord (= 7.2.0)
activestorage (= 7.2.0)
activesupport (= 7.2.0)
actionmailbox (7.2.1)
actionpack (= 7.2.1)
activejob (= 7.2.1)
activerecord (= 7.2.1)
activestorage (= 7.2.1)
activesupport (= 7.2.1)
mail (>= 2.8.0)
actionmailer (7.2.0)
actionpack (= 7.2.0)
actionview (= 7.2.0)
activejob (= 7.2.0)
activesupport (= 7.2.0)
actionmailer (7.2.1)
actionpack (= 7.2.1)
actionview (= 7.2.1)
activejob (= 7.2.1)
activesupport (= 7.2.1)
mail (>= 2.8.0)
rails-dom-testing (~> 2.2)
actionpack (7.2.0)
actionview (= 7.2.0)
activesupport (= 7.2.0)
actionpack (7.2.1)
actionview (= 7.2.1)
activesupport (= 7.2.1)
nokogiri (>= 1.8.5)
racc
rack (>= 2.2.4, < 3.2)
Expand All @@ -41,35 +41,35 @@ GEM
rails-dom-testing (~> 2.2)
rails-html-sanitizer (~> 1.6)
useragent (~> 0.16)
actiontext (7.2.0)
actionpack (= 7.2.0)
activerecord (= 7.2.0)
activestorage (= 7.2.0)
activesupport (= 7.2.0)
actiontext (7.2.1)
actionpack (= 7.2.1)
activerecord (= 7.2.1)
activestorage (= 7.2.1)
activesupport (= 7.2.1)
globalid (>= 0.6.0)
nokogiri (>= 1.8.5)
actionview (7.2.0)
activesupport (= 7.2.0)
actionview (7.2.1)
activesupport (= 7.2.1)
builder (~> 3.1)
erubi (~> 1.11)
rails-dom-testing (~> 2.2)
rails-html-sanitizer (~> 1.6)
activejob (7.2.0)
activesupport (= 7.2.0)
activejob (7.2.1)
activesupport (= 7.2.1)
globalid (>= 0.3.6)
activemodel (7.2.0)
activesupport (= 7.2.0)
activerecord (7.2.0)
activemodel (= 7.2.0)
activesupport (= 7.2.0)
activemodel (7.2.1)
activesupport (= 7.2.1)
activerecord (7.2.1)
activemodel (= 7.2.1)
activesupport (= 7.2.1)
timeout (>= 0.4.0)
activestorage (7.2.0)
actionpack (= 7.2.0)
activejob (= 7.2.0)
activerecord (= 7.2.0)
activesupport (= 7.2.0)
activestorage (7.2.1)
actionpack (= 7.2.1)
activejob (= 7.2.1)
activerecord (= 7.2.1)
activesupport (= 7.2.1)
marcel (~> 1.0)
activesupport (7.2.0)
activesupport (7.2.1)
base64
bigdecimal
concurrent-ruby (~> 1.0, >= 1.3.1)
Expand Down Expand Up @@ -169,30 +169,30 @@ GEM
rackup (2.1.0)
rack (>= 3)
webrick (~> 1.8)
rails (7.2.0)
actioncable (= 7.2.0)
actionmailbox (= 7.2.0)
actionmailer (= 7.2.0)
actionpack (= 7.2.0)
actiontext (= 7.2.0)
actionview (= 7.2.0)
activejob (= 7.2.0)
activemodel (= 7.2.0)
activerecord (= 7.2.0)
activestorage (= 7.2.0)
activesupport (= 7.2.0)
rails (7.2.1)
actioncable (= 7.2.1)
actionmailbox (= 7.2.1)
actionmailer (= 7.2.1)
actionpack (= 7.2.1)
actiontext (= 7.2.1)
actionview (= 7.2.1)
activejob (= 7.2.1)
activemodel (= 7.2.1)
activerecord (= 7.2.1)
activestorage (= 7.2.1)
activesupport (= 7.2.1)
bundler (>= 1.15.0)
railties (= 7.2.0)
railties (= 7.2.1)
rails-dom-testing (2.2.0)
activesupport (>= 5.0.0)
minitest
nokogiri (>= 1.6)
rails-html-sanitizer (1.6.0)
loofah (~> 2.21)
nokogiri (~> 1.14)
railties (7.2.0)
actionpack (= 7.2.0)
activesupport (= 7.2.0)
railties (7.2.1)
actionpack (= 7.2.1)
activesupport (= 7.2.1)
irb (~> 1.13)
rackup (>= 1.0.0)
rake (>= 12.2)
Expand All @@ -205,7 +205,7 @@ GEM
regexp_parser (2.9.2)
reline (0.5.9)
io-console (~> 0.5)
rexml (3.3.5)
rexml (3.3.6)
strscan
rubocop (1.65.1)
json (~> 2.3)
Expand Down
17 changes: 16 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,22 @@ Documents the responses of the endpoint and overrides the default responses foun
`# @response User not found by the provided Id(404) [Hash{success: Boolean, message: String}]`
`# @response Validation errors(422) [Hash{success: Boolean, erros: Array<Hash{field: String, type: String, detail: Array<String>}>}]`
`# @response Validation errors(422) [Hash{success: Boolean, errors: Array<Hash{field: String, type: String, detail: Array<String>}>}]`
</details>
<details>
<summary style="font-weight: bold; font-size: 1.2em;">@response_example</summary>
**Structure**: `@response_example text(code) [String Hash]`
Documents response examples of the endpoint associated to a response code.
**Example**:
`# @response_example Invalida Email(422) [{success: "false", errors: [{field: "email", type: "email", detail: ["Invalid email"]}] }]`
`# @response_example Id not exists (404) [{success: "false", message: "Nothing found with the provided ID." }]`
</details>
Expand Down
3 changes: 3 additions & 0 deletions lib/oas_rails.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,11 @@ module Spec

module YARD
autoload :RequestBodyTag, 'oas_rails/yard/request_body_tag'
autoload :ExampleTag, 'oas_rails/yard/example_tag'
autoload :RequestBodyExampleTag, 'oas_rails/yard/request_body_example_tag'
autoload :ParameterTag, 'oas_rails/yard/parameter_tag'
autoload :ResponseTag, 'oas_rails/yard/response_tag'
autoload :ResponseExampleTag, 'oas_rails/yard/response_example_tag'
autoload :OasRailsFactory, 'oas_rails/yard/oas_rails_factory'
end

Expand Down Expand Up @@ -84,6 +86,7 @@ def configure_yard!
'Request body Example' => [:request_body_example, :with_request_body_example],
'Parameter' => [:parameter, :with_parameter],
'Response' => [:response, :with_response],
'Response Example' => [:response_example, :with_response_example],
'Endpoint Tags' => [:tags],
'Summary' => [:summary],
'No Auth' => [:no_auth],
Expand Down
5 changes: 4 additions & 1 deletion lib/oas_rails/builders/responses_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ def initialize(specification)

def from_oas_route(oas_route)
oas_route.docstring.tags(:response).each do |tag|
@responses.add_response(ResponseBuilder.new(@specification).from_tag(tag).build)
content = ContentBuilder.new(@specification, :outgoing).with_schema(tag.schema).with_examples_from_tags(oas_route.docstring.tags(:response_example).filter { |re| re.code == tag.name }).build
response = ResponseBuilder.new(@specification).with_code(tag.name.to_i).with_description(tag.text).with_content(content).build

@responses.add_response(response)
end

self
Expand Down
12 changes: 12 additions & 0 deletions lib/oas_rails/yard/example_tag.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module OasRails
module YARD
class ExampleTag < ::YARD::Tags::Tag
attr_accessor :content

def initialize(tag_name, text, content: {})
super(tag_name, text, nil, nil)
@content = content
end
end
end
end
21 changes: 20 additions & 1 deletion lib/oas_rails/yard/oas_rails_factory.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,23 @@ def parse_tag_with_response(tag_name, text)
ResponseTag.new(tag_name, code, name, schema)
end

# Parses a tag that represents a response example.
# @param tag_name [String] The name of the tag.
# @param text [String] The tag text to parse.
# @return [ResponseExampleTag] The parsed response example tag object.
def parse_tag_with_response_example(tag_name, text)
description, code, hash = extract_name_code_and_hash(text)
ResponseExampleTag.new(tag_name, description, content: hash, code:)
end

private

# Reusable method for extracting description, type, and content with an option to process content.
# @param text [String] The text to parse.
# @param process_content [Boolean] Whether to evaluate the content as a hash.
# @return [Array] An array containing the description, type, and content or remaining text.
def extract_description_type_and_content(text, process_content: false)
match = text.match(/^(.*?)\s*\[(.*?)\]\s*(.*)$/)
match = text.match(/^(.*?)\s*\[(.*)\]\s*(.*)$/)
raise ArgumentError, "Invalid tag format: #{text}" if match.nil?

description = match[1].strip
Expand Down Expand Up @@ -84,6 +93,16 @@ def extract_name_code_and_schema(text)
[name, code, schema]
end

# Specific method to extract name, code, and hash for responses examples.
# @param text [String] The text to parse.
# @return [Array] An array containing the name, code, and schema.
def extract_name_code_and_hash(text)
name, code = extract_text_and_parentheses_content(text)
_, type, = extract_description_type_and_content(text)
hash = eval_content(type)
[name, code, hash]
end

# Evaluates a string as a hash, handling errors gracefully.
# @param content [String] The content string to evaluate.
# @return [Hash] The evaluated hash, or an empty hash if an error occurs.
Expand Down
5 changes: 2 additions & 3 deletions lib/oas_rails/yard/request_body_example_tag.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
module OasRails
module YARD
class RequestBodyExampleTag < ::YARD::Tags::Tag
class RequestBodyExampleTag < ExampleTag
attr_accessor :content

def initialize(tag_name, text, content: {})
super(tag_name, text, nil, nil)
@content = content
super
end
end
end
Expand Down
12 changes: 12 additions & 0 deletions lib/oas_rails/yard/response_example_tag.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module OasRails
module YARD
class ResponseExampleTag < ExampleTag
attr_accessor :code

def initialize(tag_name, text, content: {}, code: 200)
super(tag_name, text, content:)
@code = code
end
end
end
end
1 change: 1 addition & 0 deletions lib/oas_rails/yard/response_tag.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module YARD
class ResponseTag < ::YARD::Tags::Tag
attr_accessor :schema

# TODO: name == code. The name MUST be changed to code for better understanding
def initialize(tag_name, name, text, schema)
super(tag_name, text, nil, name)
@schema = schema
Expand Down
2 changes: 2 additions & 0 deletions test/dummy/app/controllers/users_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ def index
# @response User not found by the provided Id(404) [Hash{success: Boolean, message: String}]
# @response You dont have the rigth persmissions for access to this reasource(403) [Hash{success: Boolean, message: String}]
# @response A test response from an Issue(405) [Hash{message: String, data: Hash{availabilities: Array<String>, dates: Array<Date>}}]
# @response_example Nice 405 Error(405) [{message: "Hello", data: {availabilities: ["one", "two", "three"], dates: ["10-06-2020"]}}]
# @response_example Another 405 Error (405) [{message: "another", data: {availabilities: ["three"], dates: []}}]
def show
render json: @user
end
Expand Down
18 changes: 18 additions & 0 deletions test/lib/oas_rails/yard/oas_rails_factory_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
require "test_helper"

module OasRails
module YARD
class OasRailsFactoryTest < ActiveSupport::TestCase
def test_parse_tag_with_response_example
response = OasRailsFactory.new.parse_tag_with_response_example(:response_example,
'405 Error(405) [{message: "Hello", data: {availabilities: ["one", "two", "three"], dates: ["10-06-2020"]}}]')

assert response.is_a?(ResponseExampleTag)
assert_equal "405", response.code
assert_equal '405 Error', response.text
expected_hash = { message: "Hello", data: { availabilities: %w[one two three], dates: ["10-06-2020"] } }
assert_equal expected_hash, response.content
end
end
end
end

0 comments on commit 16f37c4

Please sign in to comment.