diff --git a/README.md b/README.md index abd8598b..34be59f8 100644 --- a/README.md +++ b/README.md @@ -137,9 +137,12 @@ RSpec::OpenAPI.info = { } } -# Set `headers` - generate parameters with headers for a request +# Set request `headers` - generate parameters with headers for a request RSpec::OpenAPI.request_headers = %w[X-Authorization-Token] +# Set response `headers` - generate parameters with headers for a response +RSpec::OpenAPI.response_headers = %w[X-Cursor] + # Set `servers` - generate servers of a schema file RSpec::OpenAPI.servers = [{ url: 'http://localhost:3000' }] diff --git a/lib/rspec/openapi.rb b/lib/rspec/openapi.rb index 1d1fdc24..e5f10319 100644 --- a/lib/rspec/openapi.rb +++ b/lib/rspec/openapi.rb @@ -11,8 +11,18 @@ module RSpec::OpenAPI @request_headers = [] @servers = [] @example_types = %i[request] + @response_headers = [] class << self - attr_accessor :path, :comment, :enable_example, :description_builder, :info, :application_version, :request_headers, :servers, :example_types + attr_accessor :path, + :comment, + :enable_example, + :description_builder, + :info, + :application_version, + :request_headers, + :servers, + :example_types, + :response_headers end end diff --git a/lib/rspec/openapi/record.rb b/lib/rspec/openapi/record.rb index f04743b7..984f09d8 100644 --- a/lib/rspec/openapi/record.rb +++ b/lib/rspec/openapi/record.rb @@ -11,6 +11,7 @@ :description, # @param [String] - "returns a status" :status, # @param [Integer] - 200 :response_body, # @param [Object] - {"status" => "ok"} + :response_headers, # @param [Array] - [["header_key1", "header_value1"], ["header_key2", "header_value2"]] :response_content_type, # @param [String] - "application/json" :response_content_disposition, # @param [String] - "inline" keyword_init: true, diff --git a/lib/rspec/openapi/record_builder.rb b/lib/rspec/openapi/record_builder.rb index 8dee85fb..a8a6e7d8 100644 --- a/lib/rspec/openapi/record_builder.rb +++ b/lib/rspec/openapi/record_builder.rb @@ -42,6 +42,12 @@ def build(context, example:) metadata_options = example.metadata[:openapi] || {} + response_headers = RSpec::OpenAPI.response_headers.each_with_object([]) do |header, headers_arr| + header_key = header + header_value = response.headers[header_key] + headers_arr << [header_key, header_value] if header_value + end + RSpec::OpenAPI::Record.new( method: request.request_method, path: path, @@ -55,6 +61,7 @@ def build(context, example:) description: metadata_options[:description] || RSpec::OpenAPI.description_builder.call(example), status: response.status, response_body: response_body, + response_headers: response_headers, response_content_type: response.media_type, response_content_disposition: response.header["Content-Disposition"], ).freeze diff --git a/lib/rspec/openapi/schema_builder.rb b/lib/rspec/openapi/schema_builder.rb index 435d4992..a659cba6 100644 --- a/lib/rspec/openapi/schema_builder.rb +++ b/lib/rspec/openapi/schema_builder.rb @@ -6,6 +6,9 @@ def build(record) description: record.description, } + response_headers = build_response_headers(record) + response[:headers] = response_headers unless response_headers.empty? + if record.response_body disposition = normalize_content_disposition(record.response_content_disposition) response[:content] = { @@ -81,6 +84,18 @@ def build_parameters(record) parameters end + def build_response_headers(record) + headers = {} + + record.response_headers.each do |key, value| + headers[key] = { + schema: build_property(try_cast(value)), + }.compact + end + + headers + end + def build_parameter_name(key, value) key = key.to_s if value.is_a?(Hash) && (value_keys = value.keys).size == 1 diff --git a/spec/rails/app/controllers/tables_controller.rb b/spec/rails/app/controllers/tables_controller.rb index dbe482ea..f43daa1c 100644 --- a/spec/rails/app/controllers/tables_controller.rb +++ b/spec/rails/app/controllers/tables_controller.rb @@ -4,6 +4,7 @@ class TablesController < ApplicationController before_action :authenticate def index + response.set_header('X-Cursor', 100) render json: [find_table] end diff --git a/spec/rails/doc/openapi.json b/spec/rails/doc/openapi.json index a9da48d5..24be9e89 100644 --- a/spec/rails/doc/openapi.json +++ b/spec/rails/doc/openapi.json @@ -101,6 +101,13 @@ } ] } + }, + "headers": { + "X-Cursor": { + "schema": { + "type": "integer" + } + } } } }, @@ -647,4 +654,4 @@ } } } -} +} \ No newline at end of file diff --git a/spec/rails/doc/openapi.yaml b/spec/rails/doc/openapi.yaml index 481edf78..e5f99bd0 100644 --- a/spec/rails/doc/openapi.yaml +++ b/spec/rails/doc/openapi.yaml @@ -93,10 +93,14 @@ paths: database: id: 2 name: production - null_sample: + null_sample: storage_size: 12.3 created_at: '2020-07-17T00:00:00+00:00' updated_at: '2020-07-17T00:00:00+00:00' + headers: + X-Cursor: + schema: + type: integer '401': description: does not return tables if unauthorized content: @@ -165,7 +169,7 @@ paths: database: id: 2 name: production - null_sample: + null_sample: storage_size: 12.3 created_at: '2020-07-17T00:00:00+00:00' updated_at: '2020-07-17T00:00:00+00:00' @@ -218,7 +222,7 @@ paths: database: id: 2 name: production - null_sample: + null_sample: storage_size: 12.3 created_at: '2020-07-17T00:00:00+00:00' updated_at: '2020-07-17T00:00:00+00:00' @@ -310,7 +314,7 @@ paths: database: id: 2 name: production - null_sample: + null_sample: storage_size: 12.3 created_at: '2020-07-17T00:00:00+00:00' updated_at: '2020-07-17T00:00:00+00:00' @@ -362,7 +366,7 @@ paths: database: id: 2 name: production - null_sample: + null_sample: storage_size: 12.3 created_at: '2020-07-17T00:00:00+00:00' updated_at: '2020-07-17T00:00:00+00:00' diff --git a/spec/rails/doc/smart/expected.yaml b/spec/rails/doc/smart/expected.yaml index c282c95b..53cc93ab 100644 --- a/spec/rails/doc/smart/expected.yaml +++ b/spec/rails/doc/smart/expected.yaml @@ -56,6 +56,10 @@ paths: storage_size: 12.3 created_at: '2020-07-17T00:00:00+00:00' updated_at: '2020-07-17T00:00:00+00:00' + headers: + X-Cursor: + schema: + type: integer '401': description: does not return tables if unauthorized content: diff --git a/spec/requests/rails_smart_merge_spec.rb b/spec/requests/rails_smart_merge_spec.rb index a59e8562..5d19b8d1 100644 --- a/spec/requests/rails_smart_merge_spec.rb +++ b/spec/requests/rails_smart_merge_spec.rb @@ -6,6 +6,7 @@ require 'rspec/rails' RSpec::OpenAPI.request_headers = %w[X-Authorization-Token] +RSpec::OpenAPI.response_headers = %w[X-Cursor] RSpec::OpenAPI.path = File.expand_path("../rails/doc/smart/openapi.#{ENV['OPENAPI_OUTPUT']}", __dir__) RSpec::OpenAPI.comment = <<~COMMENT This file is auto-generated by rspec-openapi https://github.com/k0kubun/rspec-openapi @@ -30,6 +31,7 @@ # These new params replace them in old spec get '/tables', params: { page: '42', per: '10' }, headers: { authorization: 'k0kubun', "X-Authorization-Token": 'token' } + response.set_header('X-Cursor', 100) expect(response.status).to eq(200) end end diff --git a/spec/requests/rails_spec.rb b/spec/requests/rails_spec.rb index 63252eae..f4ff9303 100644 --- a/spec/requests/rails_spec.rb +++ b/spec/requests/rails_spec.rb @@ -6,6 +6,7 @@ require 'rspec/rails' RSpec::OpenAPI.request_headers = %w[X-Authorization-Token] +RSpec::OpenAPI.response_headers = %w[X-Cursor] RSpec::OpenAPI.path = File.expand_path("../rails/doc/openapi.#{ENV['OPENAPI_OUTPUT']}", __dir__) RSpec::OpenAPI.comment = <<~COMMENT This file is auto-generated by rspec-openapi https://github.com/k0kubun/rspec-openapi