接口联调
This commit is contained in:
parent
0e3aeda1cf
commit
b4a0f9795c
|
|
@ -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',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 开始分析
|
||||
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',
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -32,4 +32,4 @@ export const maintainabilityIcon = {
|
|||
LOW: low,
|
||||
MEDIUM: medium,
|
||||
HIGH: high
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 <Button type="primary" style={{width: '112px', height: '36px'}} onClick={openAnalysis} loading={loading}>开始体验</Button>
|
||||
case actionStatus.waiting:
|
||||
case actionStatus.running:
|
||||
return <Button type="primary" style={{height: '36px'}} disabled>代码正在分析中,请稍候</Button>
|
||||
case actionStatus.failure:
|
||||
return <div>
|
||||
<p>代码正在分析中,请重试</p>
|
||||
<Button type="primary" style={{width: '112px', height: '36px'}} onClick={openAnalysis} loading={loading}>开始分析</Button>
|
||||
</div>
|
||||
default:
|
||||
return <Button type="primary" style={{width: '112px', height: '36px'}} onClick={openAnalysis} loading={loading}>开始体验</Button>
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="issue-total">
|
||||
<ProblemDetail
|
||||
visible={detailVisible}
|
||||
issue={selectedIssue}
|
||||
issueDetail={issueInfo}
|
||||
onClose={() => setDetailVisible(false)}
|
||||
/>
|
||||
{
|
||||
!isopen && <Fragment>
|
||||
<div className="headBox font-16 pl15">sonar代码质量分析工具</div>
|
||||
<div className="nullStoreBox mt25">
|
||||
<img src={logo} alt="" className="loBox mt50"/>
|
||||
<p className="font-22 mt10">欢迎使用代码质量分析工具</p>
|
||||
<div className="introBox font-15">代码质量分析工具通过静态代码分析帮助开发者识别和修复代码库潜在的缺陷、代码异味、复杂度过高的代码段以及潜在的安全漏洞。同时能够评估代码的结构和风格,提供关于代码覆盖率、重复率等关键指标的深入洞察。</div>
|
||||
<div className="borBox"></div>
|
||||
<Button type="primary" style={{width: '112px', height: '36px'}} onClick={openAnalysis}>开始体验</Button>
|
||||
</div>
|
||||
</Fragment>
|
||||
}
|
||||
{
|
||||
isopen && <div>
|
||||
<div className="container total-info">
|
||||
<p>代码分析结果</p>
|
||||
<div className="info-list">
|
||||
{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)}
|
||||
<Spin spinning={pageLoading}>
|
||||
<div className="issue-total">
|
||||
<ProblemDetail
|
||||
visible={detailVisible}
|
||||
issue={selectedIssue}
|
||||
issueDetail={issueInfo}
|
||||
onClose={() => setDetailVisible(false)}
|
||||
/>
|
||||
{
|
||||
analysisStatus !== actionStatus.success && <Fragment>
|
||||
<div className="headBox font-16 pl15">sonar代码质量分析工具</div>
|
||||
<div className="nullStoreBox mt25">
|
||||
<img src={logo} alt="" className="loBox mt50"/>
|
||||
<p className="font-22 mt10">欢迎使用代码质量分析工具</p>
|
||||
<div className="introBox font-15">代码质量分析工具通过静态代码分析帮助开发者识别和修复代码库潜在的缺陷、代码异味、复杂度过高的代码段以及潜在的安全漏洞。同时能够评估代码的结构和风格,提供关于代码覆盖率、重复率等关键指标的深入洞察。</div>
|
||||
<div className="borBox"></div>
|
||||
{
|
||||
getStatusButton()
|
||||
}
|
||||
</div>
|
||||
</Fragment>
|
||||
}
|
||||
{
|
||||
analysisStatus === actionStatus.success && <div>
|
||||
<div className="container total-info">
|
||||
<p>代码分析结果</p>
|
||||
<div className="info-list">
|
||||
{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)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{issues && <div className="count-title">发现{ baseData.maintainability_issues && JSON.parse(baseData.maintainability_issues).total }个问题</div>}
|
||||
<div className="issue-list">
|
||||
<div className="container left-content">
|
||||
{ (selectedType.length > 0 || selectedSeverity.length > 0 || selectedTag .length > 0) && <button className="clear-button" onClick={() => setter([])}>清空所有条件</button>}
|
||||
{renderFilterItems(listFilter.types, selectedType, setSelectedType, "问题类型")}
|
||||
{renderFilterItems(listFilter.impactSeverities, selectedSeverity, setSelectedSeverity, "严重程度")}
|
||||
{renderFilterItems(listFilter.tags, selectedTag, setSelectedTag, "标签")}
|
||||
</div>
|
||||
<div className="container right-content">
|
||||
{issues && Object.entries(issues).map(([key, item]) => (
|
||||
<div key={key}>
|
||||
<div className="doc-title" title={key.split(':')[1]}>
|
||||
{key.split(':')[1]}
|
||||
</div>
|
||||
{item.map(e => (
|
||||
<div
|
||||
key={e.key}
|
||||
className={`issue`}
|
||||
onClick={() => toDetail(e)}
|
||||
>
|
||||
<p className="issue-message">
|
||||
{e.message}
|
||||
<span onClick={() => detail(e)}>问题分析</span>
|
||||
<span>L{e.line}</span>
|
||||
</p>
|
||||
<p className="content-info">
|
||||
<span className="icon-text"> <img src={ issueTypeIcon[e.type]} alt="" /> {issueType[e.type]}</span>
|
||||
<span className="icon-text"> <img src={ maintainabilityIcon[e.impacts[0].severity] } alt="" /> {maintainability[e.impacts[0].severity]}</span>
|
||||
<span>{e.debt}工作</span>
|
||||
<span className="icon-text">
|
||||
{e.tags && e.tags.length > 0 && <img src={ labelIcon } alt="" />}
|
||||
{e.tags && e.tags.map((tag, index) => (
|
||||
<span key={tag}>
|
||||
{tag}{index < e.tags.length - 1 ? ',' : ''}
|
||||
</span>
|
||||
))}
|
||||
</span>
|
||||
</p>
|
||||
{issues && <div className="count-title">发现{ baseData.maintainability_issues && JSON.parse(baseData.maintainability_issues).total }个问题</div>}
|
||||
<div className="issue-list">
|
||||
<div className="container left-content">
|
||||
{ (selectedType.length > 0 || selectedSeverity.length > 0 || selectedTag .length > 0) && <button className="clear-button" onClick={() => setter([])}>清空所有条件</button>}
|
||||
{renderFilterItems(listFilter.types, selectedType, setSelectedType, "问题类型")}
|
||||
{renderFilterItems(listFilter.impactSeverities, selectedSeverity, setSelectedSeverity, "严重程度")}
|
||||
{renderFilterItems(listFilter.tags, selectedTag, setSelectedTag, "标签")}
|
||||
</div>
|
||||
<div className="container right-content">
|
||||
{issues && Object.entries(issues).map(([key, item]) => (
|
||||
<div key={key}>
|
||||
<div className="doc-title" title={key.split(':')[1]}>
|
||||
{key.split(':')[1]}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
<Pagination showSizeChanger showQuickJumper current={pageNum} total={baseInfo.total} onChange={e => setPageNum(e)} onShowSizeChange={(current, size) => setPageSize(size)} />
|
||||
{item.map(e => (
|
||||
<div
|
||||
key={e.key}
|
||||
className={`issue`}
|
||||
onClick={() => toDetail(e)}
|
||||
>
|
||||
<p className="issue-message">
|
||||
{e.message}
|
||||
<span onClick={(ele) => { ele.stopPropagation();detail(e)}}>问题分析</span>
|
||||
<span>L{e.line}</span>
|
||||
</p>
|
||||
<p className="content-info">
|
||||
<span className="icon-text"> <img src={ issueTypeIcon[e.type]} alt="" /> {issueType[e.type]}</span>
|
||||
<span className="icon-text"> <img src={ maintainabilityIcon[e.impacts[0].severity] } alt="" /> {maintainability[e.impacts[0].severity]}</span>
|
||||
<span>{e.debt}工作</span>
|
||||
<span className="icon-text">
|
||||
{e.tags && e.tags.length > 0 && <img src={ labelIcon } alt="" />}
|
||||
{e.tags && e.tags.map((tag, index) => (
|
||||
<span key={tag}>
|
||||
{tag}{index < e.tags.length - 1 ? ',' : ''}
|
||||
</span>
|
||||
))}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
<Pagination showSizeChanger showQuickJumper current={pageNum} total={baseInfo.total} onChange={e => setPageNum(e)} onShowSizeChange={(current, size) => setPageSize(size)} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</Spin>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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({
|
||||
|
|
|
|||
|
|
@ -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`;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue