Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 96 additions & 0 deletions TestResultSummaryService/routes/getReleaseSummary.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
const { TestResultsDB } = require('../Database');

/**
* getReleaseSummary returns health metrics for major JDK releases
*
* @route GET /api/getReleaseSummary
* @group Release - Operations about releases
* @return {object} - releaseSummary - map of jdk versions to their health stats
*/

module.exports = async (req, res) => {
try {
const testResultsDB = new TestResultsDB();

// We want to aggregate health across recent builds for each major JDK version
const aggregateQuery = [
{
// Only look at builds from the last 7 days to keep dashboard relevant
$match: {
timestamp: { $gt: Date.now() - 7 * 24 * 60 * 60 * 1000 },
// Focus on actual test/perf jobs
buildName: { $regex: /^(Test|Perf)_openjdk/ },
},
},
{
// Extract JDK version from buildName (e.g., Test_openjdk11_... -> 11)
$addFields: {
jdkVersionMatch: {
$regexFind: {
input: '$buildName',
regex: /openjdk(\d+)/,
},
},
},
},
{
$addFields: {
jdkVersion: '$jdkVersionMatch.captures',
},
},
{
// Flatten the captures array to get the actual version string
$unwind: '$jdkVersion',
},
{
// Group by JDK version and build result
$group: {
_id: {
jdkVersion: '$jdkVersion',
result: '$buildResult',
},
count: { $sum: 1 },
},
},
{
// Reshape to make it easier for the frontend
$group: {
_id: '$_id.jdkVersion',
results: {
$push: {
result: '$_id.result',
count: '$count',
},
},
},
},
{
$sort: { _id: 1 },
},
];

const result = await testResultsDB.aggregate(aggregateQuery);

// Transform the array into a more convenient object format
const summary = {};
result.forEach((item) => {
const stats = {
SUCCESS: 0,
FAILURE: 0,
UNSTABLE: 0,
ABORTED: 0,
total: 0,
};
item.results.forEach((res) => {
const r = res.result || 'UNKNOWN';
stats[r] = res.count;
stats.total += res.count;
});
summary[item._id] = stats;
});

res.send(summary);
} catch (error) {
res.status(500).send({ error: error.message });
}
};
2 changes: 2 additions & 0 deletions TestResultSummaryService/routes/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,11 @@ app.get('/getFeedbackUrl', require('./getFeedbackUrl'));
app.get('/rescanBuild', require('./rescanBuild'));
app.get('/testParserViaFile', require('./test/testParserViaFile'));
app.get('/testParserViaLogStream', require('./test/testParserViaLogStream'));
app.get('/getReleaseSummary', require('./getReleaseSummary'));

app.get('/updateComments', require('./updateComments'));
app.get('/updateKeepForever', require('./updateKeepForever'));
app.post('/updateStats', require('./updateStats'));

// jwt
app.post('/auth/register', require('./jwt/register'));
Expand Down
5 changes: 5 additions & 0 deletions test-result-summary-client/src/Dashboard/Dashboard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ export default class Dashboard extends Component {
key: '2',
children: <TabInfo tab="Custom" />,
},
{
label: 'Release',
key: '3',
children: <TabInfo tab="Release" />,
},
]}
/>
);
Expand Down
10 changes: 10 additions & 0 deletions test-result-summary-client/src/Dashboard/Defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,14 @@ export default {
},
],
},
Release: {
widgets: [
{
type: 'ReleaseHealthWidget',
x: 0,
y: 0,
settings: {},
},
],
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import React, { Component } from 'react';
import { Table, Tooltip, Progress, Typography } from 'antd';
import { fetchData } from '../../utils/Utils';

export default class ReleaseHealthWidget extends Component {
static Title = (props) => 'Release Health Overview';
static defaultSize = { w: 4, h: 4 };
static defaultSettings = {};

state = {
data: {},
loading: true,
};

async componentDidMount() {
await this.updateData();
// Update every 5 minutes
this.intervalId = setInterval(
() => {
this.updateData();
},
5 * 60 * 1000
);
}

componentWillUnmount() {
clearInterval(this.intervalId);
}

updateData = async () => {
this.setState({ loading: true });
const summary = await fetchData('/api/getReleaseSummary');
if (summary) {
this.setState({ data: summary, loading: false });
} else {
this.setState({ loading: false });
}
};

render() {
const { data, loading } = this.state;

const tableData = Object.keys(data).map((jdkVersion) => {
const stats = data[jdkVersion];
const passRate =
stats.total > 0
? Math.round((stats.SUCCESS / stats.total) * 100)
: 0;

return {
key: jdkVersion,
jdkVersion: `JDK ${jdkVersion}`,
success: stats.SUCCESS,
failure: stats.FAILURE,
unstable: stats.UNSTABLE,
total: stats.total,
passRate: passRate,
};
});

const columns = [
{
title: 'JDK Version',
dataIndex: 'jdkVersion',
key: 'jdkVersion',
sorter: (a, b) => parseInt(a.key) - parseInt(b.key),
},
{
title: 'Success',
dataIndex: 'success',
key: 'success',
render: (val) => (
<span style={{ color: '#52c41a' }}>{val}</span>
),
},
{
title: 'Failure',
dataIndex: 'failure',
key: 'failure',
render: (val) => (
<span style={{ color: '#ff4d4f' }}>{val}</span>
),
},
{
title: 'Unstable',
dataIndex: 'unstable',
key: 'unstable',
render: (val) => (
<span style={{ color: '#faad14' }}>{val}</span>
),
},
{
title: 'Health',
dataIndex: 'passRate',
key: 'passRate',
render: (percent) => (
<Tooltip title={`${percent}% Success Rate`}>
<Progress
percent={percent}
size="small"
status={
percent > 90
? 'success'
: percent > 70
? 'normal'
: 'exception'
}
/>
</Tooltip>
),
sorter: (a, b) => a.passRate - b.passRate,
},
];

return (
<div style={{ padding: '10px' }}>
<Table
columns={columns}
dataSource={tableData}
pagination={false}
size="middle"
loading={loading}
/>
</div>
);
}
}
1 change: 1 addition & 0 deletions test-result-summary-client/src/Dashboard/Widgets/index.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './BuildStatus/';
export * from './Graph/';
export { default as ReleaseHealthWidget } from './ReleaseHealthWidget';
Loading