forked from exoego/rspec-openapi
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrecord_builder.rb
More file actions
107 lines (95 loc) · 3.57 KB
/
Copy pathrecord_builder.rb
File metadata and controls
107 lines (95 loc) · 3.57 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
require 'action_dispatch'
require 'rspec/openapi/record'
class << RSpec::OpenAPI::RecordBuilder = Object.new
# @param [RSpec::ExampleGroups::*] context
# @param [RSpec::Core::Example] example
# @return [RSpec::OpenAPI::Record,nil]
def build(context, example:)
if rack_test?(context)
request = ActionDispatch::Request.new(context.last_request.env)
request.body.rewind if request.body.respond_to?(:rewind)
response = ActionDispatch::TestResponse.new(*context.last_response.to_a)
else
request = context.request
response = context.response
end
return if request.nil?
# Generate `path` and `summary` in a framework-friendly manner when possible
if rails?
route = find_rails_route(request)
path = route.path.spec.to_s.delete_suffix('(.:format)')
summary = "#{route.requirements[:action]}"
tags = [route.requirements[:controller].classify]
else
path = request.path
summary = "#{request.method} #{request.path}"
end
response_body =
begin
response.parsed_body
rescue JSON::ParserError
nil
end
request_headers = RSpec::OpenAPI.headers.each_with_object([]) do |header, headers_arr|
header_key = header.gsub(/-/, '_').upcase
header_value = request.get_header(['HTTP', header_key].join('_')) || request.get_header(header_key)
headers_arr << [header, header_value] if header_value
end
RSpec::OpenAPI::Record.new(
method: request.request_method,
path: path,
path_params: raw_path_params(request),
query_params: request.query_parameters,
request_params: raw_request_params(request),
request_content_type: request.media_type,
request_headers: request_headers,
summary: summary,
tags: tags,
description: RSpec::OpenAPI.description_builder.call(example),
status: response.status,
response_body: response_body,
response_content_type: response.media_type,
response_content_disposition: response.header["Content-Disposition"],
).freeze
end
private
def rails?
defined?(Rails) && Rails.application
end
def rack_test?(context)
defined?(Rack::Test::Methods) && context.class < Rack::Test::Methods
end
# @param [ActionDispatch::Request] request
def find_rails_route(request, app: Rails.application, fix_path: true)
# Reverse the destructive modification by Rails https://github.com/rails/rails/blob/v6.0.3.4/actionpack/lib/action_dispatch/journey/router.rb#L33-L41
if fix_path && !request.script_name.empty?
request = request.dup
request.path_info = File.join(request.script_name, request.path_info)
end
app.routes.router.recognize(request) do |route|
unless route.path.anchored
route = find_rails_route(request, app: route.app.app, fix_path: false)
end
return route
end
raise "No route matched for #{request.request_method} #{request.path_info}"
end
# :controller and :action always exist. :format is added when routes is configured as such.
def raw_path_params(request)
if rails?
request.path_parameters.reject do |key, _value|
%i[controller action format].include?(key)
end
else
request.path_parameters
end
end
# workaround to get real request parameters
# because ActionController::ParamsWrapper overwrites request_parameters
def raw_request_params(request)
original = request.delete_header('action_dispatch.request.request_parameters')
request.request_parameters
ensure
request.set_header('action_dispatch.request.request_parameters', original)
end
end