背景

1
每次写完代码发现sonar给的建议,有时候比较难理解,一眼看过去,可能还不知道如何修改代码,如果让AI帮忙理解sonar的建议,再根据提供的代码,让AI直接提供修改后的代码

设计过程

  1. 访问 sonarQube 服务
  2. 获取对应服务Bugs的接口
  3. 获取Bugs触发的sonar规则

Bugs接口列表

  • 接口:/api/issues/search?componentKeys={componentKeys}&s=FILE_LINE&resolved=false&types=BUG&ps=100&organization=default-organization&facets=severities%2CsonarsourceSecurity%2Ctypes&additionalFields=_all

    其中的 componentKeys 为 你的服务

20241120105343.png

获取SONAR规则详情

  • 接口:/api/rules/show?key={rule}&organization=default-organization

    其中的rule表示触发的规则编码

20241120110042.png

具体实现代码

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
108
109
110
111
112
import requests  

from utils.chat_utils import ChatUtils
from utils.prompt_utils import PromptUtils
import streamlit as st

class SonarAnalysisResult:
def __init__(self, component, line, message, rule, rule_detail):
self.component = component
self.line = line
self.message = message
self.rule = rule
self.rule_detail = rule_detail
self.fix_suggestions = ""

def set_fix_suggestions(self, fix_suggestions):
self.fix_suggestions = fix_suggestions

class SonarService:
def __init__(self):
self.cookie = 'LtpaToken=AAECAzY3MzU1Q0IyNxxxsP1MNIcSGzTxQR1TOVkg'
self.headers = {
'Cookie': self.cookie
}
self.project_name = "xxx-xxx-service"

def __init__(self, project_name, cookies):
self.project_name = project_name
self.cookie = cookies
self.headers = {
'Cookie': self.cookie
}

# 获取规则详情
def get_rule_detail(self, rule):
url = f"https://sonar.xxx.com/api/rules/show?key={rule}&organization=default-organization"
response = requests.get(url, headers=self.headers)
if response.status_code == 200:
result = response.json()
rule = result.get("rule")
mdDesc = rule.get("mdDesc")
return mdDesc
else:
return None

# 获取sonar分析结果
def get_sonar_analysis_result(self):
componentKeys = self.project_name
url = f"https://sonar.xxx.com/api/issues/search?componentKeys={componentKeys}&s=FILE_LINE&resolved=false&types=BUG&ps=100&organization=default-organization&facets=severities%2CsonarsourceSecurity%2Ctypes&additionalFields=_all"
response = requests.get(url, headers=self.headers)
if response.status_code == 200:
result = response.json()
issues = result.get("issues", [])
return issues
else:
return None

def get_issues(self):
sonar_analysis_result = []
issues = self.get_sonar_analysis_result()
for issue in issues:
# xxxx-service:xxxx-service/src/main/java/com/xxx/xxx/xxx/xxxx/application/xx/BxxdAxxxsageService.java
component = issue.get("component")
# 49
line = issue.get("line")
# Method com.yjh.xxx.xxx.xxx.xxx.command.BrandAxxxeService.cleaxxxsage() builds a list from one element using Arrays.asList rather than Collections.singletonList
message = issue.get("message")
# fb-contrib:LUI_USE_SINGLETON_LIST
rule = issue.get("rule")
'''
<p>This method builds a list using Arrays.asList(foo), passing in a single element. Arrays.asList needs to first create an array from this one element, and then build a List that wraps this array. It is simpler to use Collections.singletonList(foo), which does not create the array, and produces a far simpler instance of List. Since both of these arrays are immutable (from the List's point of view) they are equivalent from a usage standpoint. </p> <p>There is one difference between Array.asList and Collections.singletonList that you should be mindful of. The rarely used set(index, value) method is allowed to be used with a List created by Array.asList, but not with Collections.singletonList. So if you do use the set(index, value) method continue using Arrays.asList. '''
rule_detail = self.get_rule_detail(rule)
sonar_analysis_result.append(SonarAnalysisResult(component, line, message, rule, rule_detail))
return sonar_analysis_result

# 获取指定文件,指定行数的代码
def get_code(self, component, line):
start_line = line - 20
end_line = line + 20
url = f"https://sonar.xxx.com/api/sources/lines?key={component}&from={start_line}&to={end_line}"
response = requests.get(url, headers=self.headers)
code = ""
if response.status_code == 200:
result = response.json()
sources = result.get("sources", [])
for source in sources:
code = f'{code}\n{source.get("code")}'
return code

# 让AI根据代码和规则详情给出修复建议
def get_fix_suggestions(self, code, rule_detail):
prompt = PromptUtils.read_prompt("fix_suggestions.md").replace('${code}', code).replace('${rule_detail}', rule_detail)
# 调用AI接口,根据代码和规则详情给出修复建议
return ChatUtils.invoke(prompt).content

def main(self):
sonar_analysis_result = self.get_issues()
for sonar_analysis_result in sonar_analysis_result:
code = self.get_code(sonar_analysis_result.component, sonar_analysis_result.line)
fix_suggestions = self.get_fix_suggestions(code, sonar_analysis_result.rule_detail)
sonar_analysis_result.set_fix_suggestions(fix_suggestions)
msg = f"## 文件:{class_name} \n[] 文件路径:{sonar_analysis_result.component} \n 行数:{sonar_analysis_result.line} \n{sonar_analysis_result.fix_suggestions}"
print(msg)
# 使用streamlit 打印
# class_name = sonar_analysis_result.component.split("/")[-1]
# st.markdown(mgs)
# st.markdown("---")
return sonar_analysis_result

if __name__ == "__main__":
sonar_service = SonarService()
sonar_service.main()

最终效果

20241120112018.png