diff --git a/src/forge/Server/api.js b/src/forge/Server/api.js index 52dc9dca4..acf0679af 100644 --- a/src/forge/Server/api.js +++ b/src/forge/Server/api.js @@ -3,8 +3,8 @@ import fetch from './fetch'; // 获取列表 export function getList(params, owner, repo) { return fetch({ - // url: `/api/v1/${owner}/${repo}/sonarqubes/issues_search.json`, - url: `/api/issues/search?components=kingchanx-fluid-cloudnative_fluid&s=FILE_LINE&issueStatuses=CONFIRMED%2COPEN&&additionalFields=_all&timeZone=Asia%2FShanghai`, + url: `/api/v1/${owner}/${repo}/sonarqubes/issues_search.json?s=FILE_LINE`, + // url: `/api/issues/search?components=kingchanx-fluid-cloudnative_fluid&s=FILE_LINE&issueStatuses=CONFIRMED%2COPEN&&additionalFields=_all&timeZone=Asia%2FShanghai`, method: 'get', params }); @@ -12,25 +12,25 @@ export function getList(params, owner, repo) { // 代码片段 -export function getCodeSnippets(key) { +export function getCodeSnippets(key, owner, repo) { return fetch({ - url: `/api/sources/issue_snippets?issueKey=${ key }`, + url: `/api/v1/${owner}/${repo}/sonarqubes/sources_issue_snippet.json?issueKey=${ key }`, method: 'get', }); } // 问题分析 -export function getProblemDetail(key) { +export function getProblemDetail(key, owner, repo) { return fetch({ - url: `/api/rules/show?key=${ key }`, + url: `/api/v1/${owner}/${repo}/sonarqubes/rules_show.json?key=${ key }`, method: 'get', }); } // 数据汇总 -export function getProblemData(component, keys) { +export function getProblemData(owner, repo , keys) { return fetch({ - url: `/api/measures/component?component=${ component }&metricKeys=${ keys }`, + url: `/api/v1/${owner}/${repo}/sonarqubes/measures_component.json?component=${ owner }-${ repo }&metricKeys=${ keys }`, method: 'get', }); } @@ -41,4 +41,29 @@ export function initSonar(open, owner, repo) { url: `/api/v1/${owner}/${repo}/sonarqubes/sonar_initialize.json?has_actions=${ open }`, method: 'post', }); -} \ No newline at end of file +} + +// 开始分析 +export function startAlalyze( branch , owner, repo) { + return fetch({ + url: `/api/v1/${owner}/${repo}/sonarqubes/insert_file.json?branch=${ branch }`, + method: 'post', + }); +} + +// // 获取action任务列表 +// export function getActionList(owner, repo ) { +// return fetch({ +// url: `/api/v1/${owner}/${repo}/actions`, +// method: 'get', +// }); +// } + +// 获取action任务列表 +export function getActionList(owner, repo ) { + return fetch({ + url: `/api/v1/${owner}/${repo}/actions/runs?workflow=SonarScanner.yaml`, + method: 'get', + }); +} + diff --git a/src/forge/Server/constants/quality.js b/src/forge/Server/constants/quality.js index 2cf44037a..c44461377 100644 --- a/src/forge/Server/constants/quality.js +++ b/src/forge/Server/constants/quality.js @@ -32,4 +32,4 @@ export const maintainabilityIcon = { LOW: low, MEDIUM: medium, HIGH: high -} \ No newline at end of file +} diff --git a/src/forge/Server/quality/detail.jsx b/src/forge/Server/quality/detail.jsx index 172f9f122..8adbadd68 100644 --- a/src/forge/Server/quality/detail.jsx +++ b/src/forge/Server/quality/detail.jsx @@ -39,7 +39,7 @@ function issueDetail(props){ useEffect(() => { if (selectedIssue) { - getCodeSnippets(selectedIssue.key).then(res => { + getCodeSnippets(selectedIssue.key, owner, projectsId).then(res => { processIssues(res[selectedIssue.component].sources, issues[selectedIssue.component]) setSnippetsDetail(res[selectedIssue.component]); }) @@ -160,7 +160,7 @@ function issueDetail(props){ } function detail() { - getProblemDetail(selectedIssue.rule).then(res => { + getProblemDetail( selectedIssue.rule, owner, projectsId).then(res => { setIssueInfo(res?.rule) setDetailVisible(true) }) diff --git a/src/forge/Server/quality/detail.scss b/src/forge/Server/quality/detail.scss index a49bfb454..69991a2a9 100644 --- a/src/forge/Server/quality/detail.scss +++ b/src/forge/Server/quality/detail.scss @@ -169,6 +169,13 @@ margin-bottom: 10px; cursor: pointer; word-break: break-word; + span { + font-weight: 400; + font-size: 14px; + color: #466AFF; + margin-left: 16px; + cursor: pointer; + } } .selected-issue { border: 1px solid rgb(253, 162, 155); diff --git a/src/forge/Server/quality/index.jsx b/src/forge/Server/quality/index.jsx index f1fae6bec..5549b0c03 100644 --- a/src/forge/Server/quality/index.jsx +++ b/src/forge/Server/quality/index.jsx @@ -1,9 +1,9 @@ import React, { useState, useEffect, Fragment } from "react"; -import { getList, getCodeSnippets, getProblemDetail, getProblemData, initSonar } from "../api"; +import { getList, getProblemDetail, getProblemData, initSonar, startAlalyze, getActionList } from "../api"; import './detail.scss'; import ProblemDetail from "./component/ProblemDetail"; import { issueType, maintainability, issueTypeIcon, maintainabilityIcon } from "../constants/quality"; -import { Divider, Pagination, Button } from 'antd'; +import { Divider, Pagination, Button, message, Spin } from 'antd'; import { formatNumber, formatNumberWithCommas } from '../../Utils/utils'; import logo from '../img/logo4-1.png'; import labelIcon from '../img/quality/label.png'; @@ -12,6 +12,15 @@ import repeat from '../img/quality/repeat.png'; import cover from '../img/quality/cover.png'; import { Link } from "react-router-dom"; + +const actionStatus = { + success: 1, + failure: 2, + waiting: 5, + running: 6, + none: 7 +} + function IssueList(props) { const [issues, setIssues] = useState({}); const [baseInfo, setBaseInfo] = useState({}); @@ -25,7 +34,9 @@ function IssueList(props) { const [selectedTag, setSelectedTag] = useState([]); const [pageNum, setPageNum] = useState(1); const [pageSize, setPageSize] = useState(10); - const [isopen, setIsopen] = useState(false); + const [loading, setLoading] = useState(false); + const [pageLoading, setPageLoading] = useState(true); + const [analysisStatus, setAnalysisStatus] = useState(status.none); const { projectDetail, history } = props; const metricKeys = 'lines,coverage,lines_to_cover,duplicated_lines_density,maintainability_issues,vulnerabilities,bugs,code_smells'; const facets = 'impactSeverities,tags,types'; @@ -43,19 +54,36 @@ function IssueList(props) { if (projectDetail) { const { author, name } = projectDetail; document.title = `代码质量分析-${author.name}/${name}`; - getIssueList(author.name, name); + setPageLoading(true) + getActionList(owner, projectsId).then(res => { + setPageLoading(false) + if (res.runs.length === 0) { + setAnalysisStatus(actionStatus.none) + } else { + setAnalysisStatus(res.runs[0].status) + if (res.runs[0].status) { + getData() + getIssueList() + } + } + }) } - getProblemData('kingchanx-fluid-cloudnative_fluid', metricKeys).then(res => { - const measures = res.component.measures; - const result = {}; - measures.forEach(measure => { - result[measure.metric] = measure.value; - }); - setBaseData(result); - }); }, [projectDetail]); - const getIssueList = (owner, name) => { + const getData = () => { + getProblemData(owner, projectsId, metricKeys).then(res => { + if (res.status === 0) { + const measures = res.component.measures; + const result = {}; + measures.forEach(measure => { + result[measure.metric] = measure.value; + }); + setBaseData(result); + } + }); + } + + const getIssueList = () => { let params = { facets: facets, types: selectedType.join(','), @@ -64,7 +92,9 @@ function IssueList(props) { p: pageNum, ps: pageSize }; - getList(params, owner, name).then(res => { + setPageLoading(true) + getList(params, owner, projectsId).then(res => { + setPageLoading(false) const issues = res.issues.reduce((acc, issue) => { if (acc[issue.component]) { acc[issue.component].push(issue); @@ -86,16 +116,22 @@ function IssueList(props) { }; useEffect(() => { - setPageNum(1) + if (pageNum === 1) { + getIssueList() + } else { + setPageNum(1) + } }, [selectedType, selectedSeverity, selectedTag]); useEffect(() => { - getIssueList(); + if (projectDetail && analysisStatus === actionStatus.success) { + getIssueList() + } }, [pageNum, pageSize]); const detail = (e) => { setSelectedIssue(e); - getProblemDetail(e.rule).then(res => { + getProblemDetail(e.rule, owner, projectsId).then(res => { setIssueInfo(res?.rule); setDetailVisible(true); }); @@ -159,90 +195,124 @@ function IssueList(props) { } const openAnalysis = () => { - initSonar(true, owner, projectsId) + setLoading(true) + initSonar(true, owner, projectsId).then(res => { + if (res.status === 0) { + startAlalyze(projectDetail.default_branch, owner, projectsId).then(res1=> { + if (res1.status === 0) { + message.success('创建分析成功,请等待分析完成') + setAnalysisStatus(actionStatus.waiting) + } + setLoading(false) + }) + } else { + setLoading(false) + } + }) + } + + const getStatusButton = () => { + switch (analysisStatus) { + case actionStatus.none: + return + case actionStatus.waiting: + case actionStatus.running: + return + case actionStatus.failure: + return
+

代码正在分析中,请重试

+ +
+ default: + return + } } return ( -
- setDetailVisible(false)} - /> - { - !isopen && -
sonar代码质量分析工具
-
- -

欢迎使用代码质量分析工具

-
代码质量分析工具通过静态代码分析帮助开发者识别和修复代码库潜在的缺陷、代码异味、复杂度过高的代码段以及潜在的安全漏洞。同时能够评估代码的结构和风格,提供关于代码覆盖率、重复率等关键指标的深入洞察。
-
- -
-
- } - { - isopen &&
-
-

代码分析结果

-
- {renderTotalItem("Bugs", baseData.bugs, issueTypeIcon.BUG)} - {renderTotalItem("漏洞", formatNumber(baseData.vulnerabilities), issueTypeIcon.VULNERABILITY)} - {renderTotalItem("异味", formatNumber(baseData.code_smells), issueTypeIcon.CODE_SMELL)} - {renderDetailedItem("重复率", baseData.lines, baseData.duplicated_lines_density, "代码", repeat)} - {renderDetailedItem("覆盖率", baseData.lines_to_cover, baseData.coverage, "代码", cover)} - {renderTotalItem("代码行数", formatNumberWithCommas(baseData.lines), code_lines)} + +
+ setDetailVisible(false)} + /> + { + analysisStatus !== actionStatus.success && +
sonar代码质量分析工具
+
+ +

欢迎使用代码质量分析工具

+
代码质量分析工具通过静态代码分析帮助开发者识别和修复代码库潜在的缺陷、代码异味、复杂度过高的代码段以及潜在的安全漏洞。同时能够评估代码的结构和风格,提供关于代码覆盖率、重复率等关键指标的深入洞察。
+
+ { + getStatusButton() + } +
+
+ } + { + analysisStatus === actionStatus.success &&
+
+

代码分析结果

+
+ {renderTotalItem("Bugs", baseData.bugs, issueTypeIcon.BUG)} + {renderTotalItem("漏洞", formatNumber(baseData.vulnerabilities), issueTypeIcon.VULNERABILITY)} + {renderTotalItem("异味", formatNumber(baseData.code_smells), issueTypeIcon.CODE_SMELL)} + {renderDetailedItem("重复率", baseData.lines, baseData.duplicated_lines_density, "代码", repeat)} + {renderDetailedItem("覆盖率", baseData.lines_to_cover, baseData.coverage, "代码", cover)} + {renderTotalItem("代码行数", formatNumberWithCommas(baseData.lines), code_lines)} +
-
- {issues &&
发现{ baseData.maintainability_issues && JSON.parse(baseData.maintainability_issues).total }个问题
} -
-
- { (selectedType.length > 0 || selectedSeverity.length > 0 || selectedTag .length > 0) && } - {renderFilterItems(listFilter.types, selectedType, setSelectedType, "问题类型")} - {renderFilterItems(listFilter.impactSeverities, selectedSeverity, setSelectedSeverity, "严重程度")} - {renderFilterItems(listFilter.tags, selectedTag, setSelectedTag, "标签")} -
-
- {issues && Object.entries(issues).map(([key, item]) => ( -
-
- {key.split(':')[1]} -
- {item.map(e => ( -
toDetail(e)} - > -

- {e.message} - detail(e)}>问题分析 - L{e.line} -

-

- {issueType[e.type]} - {maintainability[e.impacts[0].severity]} - {e.debt}工作 - - {e.tags && e.tags.length > 0 && } - {e.tags && e.tags.map((tag, index) => ( - - {tag}{index < e.tags.length - 1 ? ',' : ''} - - ))} - -

+ {issues &&
发现{ baseData.maintainability_issues && JSON.parse(baseData.maintainability_issues).total }个问题
} +
+
+ { (selectedType.length > 0 || selectedSeverity.length > 0 || selectedTag .length > 0) && } + {renderFilterItems(listFilter.types, selectedType, setSelectedType, "问题类型")} + {renderFilterItems(listFilter.impactSeverities, selectedSeverity, setSelectedSeverity, "严重程度")} + {renderFilterItems(listFilter.tags, selectedTag, setSelectedTag, "标签")} +
+
+ {issues && Object.entries(issues).map(([key, item]) => ( +
+
+ {key.split(':')[1]}
- ))} -
- ))} - setPageNum(e)} onShowSizeChange={(current, size) => setPageSize(size)} /> + {item.map(e => ( +
toDetail(e)} + > +

+ {e.message} + { ele.stopPropagation();detail(e)}}>问题分析 + L{e.line} +

+

+ {issueType[e.type]} + {maintainability[e.impacts[0].severity]} + {e.debt}工作 + + {e.tags && e.tags.length > 0 && } + {e.tags && e.tags.map((tag, index) => ( + + {tag}{index < e.tags.length - 1 ? ',' : ''} + + ))} + +

+
+ ))} +
+ ))} + setPageNum(e)} onShowSizeChange={(current, size) => setPageSize(size)} /> +
-
- } -
+ } +
+
); } diff --git a/src/forge/javaFetch.js b/src/forge/javaFetch.js index 56ec563b7..f33e69891 100644 --- a/src/forge/javaFetch.js +++ b/src/forge/javaFetch.js @@ -6,9 +6,9 @@ import Login from './Wiki/components/Login'; export const TokenKey = 'autologin_trustie'; export default function javaFetch(actionUrl){ - // if (window.location.href.indexOf('localhost') < 0) { + if (window.location.href.indexOf('localhost') < 0) { axios.defaults.withCredentials = true; - // } + } // 创建axios实例 const service = axios.create({ diff --git a/src/services/project.js b/src/services/project.js index 694cdaac1..fa81bc0b5 100644 --- a/src/services/project.js +++ b/src/services/project.js @@ -6,9 +6,8 @@ let baseZoneUrl = `${zoneUrl}/api` if (__CLIENT__ ) { let settings = localStorage.chromesetting && localStorage.chromesetting !== 'undefined' &&JSON.parse(localStorage.chromesetting); - // 测试待删 - // baseUrl = settings ? `${settings && settings.common.main_site_url}/api` : '/api'; - baseUrl = settings ? `http://172.20.32.202:4000/api` : '/api'; + baseUrl = settings ? `${settings && settings.common.main_site_url}/api` : '/api'; + // baseUrl = settings ? `http://172.20.32.202:4000/api` : '/api'; baseZoneUrl = settings ? `${settings && settings.common.zone}/api` : `${zoneUrl}/api`; }