Class: YapiCheck::Executor
- Inherits:
-
Object
- Object
- YapiCheck::Executor
- Defined in:
- lib/yapi_check/executor.rb
Constant Summary collapse
- YAPI_PARAMS_CLASS =
当前支持检查的YAPI请求参数类型
{ 'string' => :String, 'number' => :Float, 'integer' => :Integer }.freeze
Class Method Summary collapse
-
.check_request_params(_method, yapi_params, action_source) ⇒ Object
检查请求参数是否正确(json支持检查类型, query和form不支持).
-
.check_request_title(action_title, yapi_title) ⇒ Object
检查接口名称是否一致.
-
.check_response(response_params, jbuilder_source) ⇒ Object
核对响应.
-
.check_single_interface(interface_id, method_mapping, path_mapping) ⇒ Object
检查单个接口.
-
.expand_jbuilder_with_partial(jbuilder_file_name, parent_file_name = nil) ⇒ Object
使用子视图展开jbuilder(递归).
-
.get_controller_and_action(endpoint, method) ⇒ Object
获取Rails控制器和动作.
-
.get_must_not_in_strings(yapi_param) ⇒ Object
反向匹配参数类型.
-
.get_response_param_type(name, type) ⇒ Object
获取响应参数对应的类型(正则表达式).
-
.get_response_params(detail) ⇒ Object
获取响应参数.
-
.get_yapi_params(detail) ⇒ Object
获取YAPI参数.
Class Method Details
.check_request_params(_method, yapi_params, action_source) ⇒ Object
检查请求参数是否正确(json支持检查类型, query和form不支持)
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
# File 'lib/yapi_check/executor.rb', line 112 def self.check_request_params(_method, yapi_params, action_source) errors = [] yapi_params.each do |yapi_param| # 必填性验证 required_check = yapi_param.required == 1 ? "required(:#{yapi_param.name}" : "optional(:#{yapi_param.name}" errors << "#{yapi_param.name}必填性错误,期待#{required_check}" unless action_source.include?(required_check) # 类型验证 if yapi_param.category == 'json' type_errors = get_must_not_in_strings(yapi_param).select { |x| action_source.include?(x) } errors << "#{yapi_param.name}类型错误, 期待#{required_check}, #{YAPI_PARAMS_CLASS[yapi_param.type]}}" unless type_errors.empty? end end errors end |
.check_request_title(action_title, yapi_title) ⇒ Object
检查接口名称是否一致
141 142 143 144 145 |
# File 'lib/yapi_check/executor.rb', line 141 def self.check_request_title(action_title, yapi_title) errors = [] errors << action_title unless action_title == yapi_title errors end |
.check_response(response_params, jbuilder_source) ⇒ Object
核对响应
148 149 150 151 152 153 154 |
# File 'lib/yapi_check/executor.rb', line 148 def self.check_response(response_params, jbuilder_source) errors = [] response_params.each do |response_param| errors << response_param.name unless jbuilder_source =~ get_response_param_type(response_param.name, response_param.type) end errors end |
.check_single_interface(interface_id, method_mapping, path_mapping) ⇒ Object
检查单个接口
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 |
# File 'lib/yapi_check/executor.rb', line 170 def self.check_single_interface(interface_id, method_mapping, path_mapping) params = { token: YapiCheck::Config.yapi_project_token, id: interface_id } response = HTTP.get("#{YapiCheck::Config.yapi_project_domain}/api/interface/get", params: params) begin detail = JSON.parse(response.body)['data'].to_h rescue JSON::ParserError => e puts "=== 解析detail JSON出错#{e.backtrace}" return end endpoint = "#{YapiCheck::Config.yapi_api_prefix}#{path_mapping[interface_id]}" controller, action = get_controller_and_action(endpoint, method_mapping[interface_id]) unless controller.respond_to?(:instance_method) puts "=== #{endpoint} 接口未实现" return end action_source = controller.instance_method(action).source error = {} response_params = get_response_params(detail) begin if response_params.present? jbuilder_source = ("#{YapiCheck::Config.yapi_api_prefix}#{path_mapping[interface_id]}.json.jbuilder") error[:response_error] = check_response(response_params, jbuilder_source) end rescue StandardError => e puts e puts "=== #{path_mapping[interface_id]} 接口所在目录中缺少的jbuilder文件, 请判断是否需要添加对应jbuilder文件 ===" end yapi_params = get_yapi_params(detail) endpoint_error = {} error[:request_params_error] = check_request_params(method_mapping[interface_id], yapi_params, action_source) endpoint_error["#{method_mapping[interface_id]} #{path_mapping[interface_id]}"] = error if error[:request_params_error].present? || error[:response_error].present? if endpoint_error.present? puts "接口: #{path_mapping[interface_id]} 对应文档中的请求参数或响应参数与当前代码存在不一致," puts "请求参数不同之处: #{error[:request_params_error]}" puts "响应参数不同之处: #{error[:response_error]}" else puts "=== 请求类型: #{method_mapping[interface_id]}, 接口: #{path_mapping[interface_id]} 检查无误 ===" end end |
.expand_jbuilder_with_partial(jbuilder_file_name, parent_file_name = nil) ⇒ Object
使用子视图展开jbuilder(递归)
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 |
# File 'lib/yapi_check/executor.rb', line 214 def self.(jbuilder_file_name, parent_file_name = nil) parent_file_name ||= jbuilder_file_name # 若是相对路径需要知道父级的文件名 jbuilder_source = [] jbuilder_view_path = File.join('app/views', jbuilder_file_name) File.open(jbuilder_view_path) do |file| file.each_line(chomp: true) do |line| if line =~ %r{json\.partial! '([a-z0-9_/]+)'} partial_file_name = Regexp.last_match(1) partial_file_name_arr = partial_file_name.split('/') if partial_file_name_arr.size == 1 # 相对路径 base_measurement => api/v1/measurements/_base_measurement partial_view_name = File.join(File.dirname(parent_file_name), "_#{partial_file_name}") else # 绝对路径 api/v1/measurements/base_measurement => api/v1/measurements/_base_measurement partial_file_name_arr[-1] = "_#{partial_file_name_arr[-1]}" partial_view_name = partial_file_name_arr.join('/') end jbuilder_source << ("#{partial_view_name}.json.jbuilder", parent_file_name) else jbuilder_source << line end end end jbuilder_source.join("\n") end |
.get_controller_and_action(endpoint, method) ⇒ Object
获取Rails控制器和动作
102 103 104 105 106 107 108 109 |
# File 'lib/yapi_check/executor.rb', line 102 def self.get_controller_and_action(endpoint, method) request_controller_and_action = Rails.application.routes.recognize_path(endpoint, method: method) controller = "#{request_controller_and_action[:controller]}_controller".classify.constantize action = request_controller_and_action[:action].to_sym [controller, action] rescue ActionController::RoutingError [] end |
.get_must_not_in_strings(yapi_param) ⇒ Object
反向匹配参数类型
129 130 131 132 133 134 135 136 137 138 |
# File 'lib/yapi_check/executor.rb', line 129 def self.get_must_not_in_strings(yapi_param) name = yapi_param.name case yapi_param.type when 'string' then ["#{name}, :Integer)", "#{name}, :Float)"] when 'number' then ["#{name}, :String)", "#{name}, :Integer)"] when 'integer' then ["#{name}, :String)", "#{name}, :Float)"] else [] end end |
.get_response_param_type(name, type) ⇒ Object
获取响应参数对应的类型(正则表达式)
157 158 159 160 161 162 163 164 165 166 167 |
# File 'lib/yapi_check/executor.rb', line 157 def self.get_response_param_type(name, type) case type when 'string' then /json\.#{name}\s.*\.to_s/ when 'number' then /json\.#{name}\s.*\.to_f/ when 'integer' then /json\.#{name}\s.*\.to_i/ when 'array' then /json\.#{name}\s(.*\.to_a|.*\sdo)/ when 'object' then /json\.#{name}\s(.*\.to_h|do\s)/ else raise "Unknown parameter type: #{type}" end end |
.get_response_params(detail) ⇒ Object
获取响应参数
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 |
# File 'lib/yapi_check/executor.rb', line 67 def self.get_response_params(detail) yapi_params = [] begin json_params = JSON.parse(detail['res_body'] || '{}')['properties']['data']['properties'] || {} rescue JSON::ParserError => e puts "=== 解析json_params JSON出错#{e.backtrace}" return [] end json_params.each do |k, v| s = YapiParams.new s.name = k s.type = v['type'] s.category = 'json' yapi_params << s properties = nil case v['type'] when 'object' properties = v['properties'] when 'array' properties = v['items']['properties'] end next unless properties.present? properties.each do |sk, sv| s = YapiParams.new s.name = sk s.type = sv['type'] s.category = 'json' yapi_params << s end end yapi_params end |
.get_yapi_params(detail) ⇒ Object
获取YAPI参数
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 |
# File 'lib/yapi_check/executor.rb', line 11 def self.get_yapi_params(detail) yapi_params = [] form_params = Array(detail['req_body_form']) form_params.each do |form| s = YapiParams.new s.name = form['name'] s.example = form['example'] s.desc = form['desc'] s.required = form['required'].to_i s.category = 'form' yapi_params << s end query_params = Array(detail['req_query']) query_params.each do |query| s = YapiParams.new s.name = query['name'] s.example = query['example'] s.desc = query['desc'] s.required = query['required'].to_i s.category = 'query' yapi_params << s end exist_key_arr = [] begin json_data = JSON.parse(detail['req_body_other'] || '{}') rescue JSON::ParserError => e puts "=== 解析json_data JSON出错#{e.backtrace}" return yapi_params end # 先遍历必填项 json_params = json_data['required'] || {} json_params.each do |param_name| s = YapiParams.new s.name = param_name s.required = 1 s.type = json_data['properties'][param_name]['type'] s.category = 'json' exist_key_arr << param_name yapi_params << s end if json_data['properties'].present? json_data['properties'].each do |k, v| next if exist_key_arr.include?(k) s = YapiParams.new s.name = k s.type = v['type'] s.category = 'json' s.required = 0 yapi_params << s end end yapi_params end |