Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Display plugin health score details on individual pages #1431

Merged
merged 30 commits into from
Oct 24, 2023
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
003b536
Creates plugin health score page
alecharp Jun 20, 2023
0ed229e
Trying to get health score from data
alecharp Jun 21, 2023
06cdf31
Merge branch 'master' into feature/plugin-health-score
alecharp Jun 21, 2023
f9437f3
Fixes template display name
alecharp Jun 22, 2023
f1280da
Keeps healthscore in one work
alecharp Jun 22, 2023
9263be3
Creates node for plugin health statistics
alecharp Jun 27, 2023
14bd773
Do not commit the dev url to PHS
alecharp Jun 28, 2023
6c42dbb
Updates the GraphQL query to match the new API
alecharp Jun 28, 2023
956d81a
Fixes the score components format
alecharp Jun 28, 2023
93aee4e
Adds description to probe entries
alecharp Jun 29, 2023
bf6635f
Displays raw data from plugin health
alecharp Jun 29, 2023
791ddd1
Tries to show health score details
alecharp Jun 29, 2023
b85566c
Highlights the plugin score
alecharp Jun 30, 2023
7829315
Merge branch 'master' into feature/plugin-health-score
alecharp Oct 4, 2023
3fea8d0
Merge branch 'master' into feature/plugin-health-score
alecharp Oct 13, 2023
9107af8
Updates graphql schemas to 3.1.0 version of PHS
alecharp Oct 16, 2023
db22d58
ESLint fix
zbynek Oct 17, 2023
524dacf
Fixing score section sort function
alecharp Oct 17, 2023
577b554
Improves score visualisation layout
alecharp Oct 18, 2023
7bf6467
Fixes PHS API response for test
alecharp Oct 18, 2023
c9d3214
Forgot about the test snapshot
alecharp Oct 18, 2023
240262e
Removes unnecessary decimal point
alecharp Oct 18, 2023
f68aaec
ECMAScript for Jest requires experimental node option
alecharp Oct 19, 2023
57bde89
Update test snapshot with jest
alecharp Oct 19, 2023
06b06d4
Groups score components into same root
alecharp Oct 19, 2023
12148ca
Display values as percentage
alecharp Oct 20, 2023
4c90a5e
Improves visualization of score category name
alecharp Oct 20, 2023
618d0ac
Cross-platform way of setting experimental modules
zbynek Oct 21, 2023
251319a
Updates plugin score link to new tab
alecharp Oct 23, 2023
efec8a8
Shows score as percentage in search result
alecharp Oct 23, 2023
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
26 changes: 21 additions & 5 deletions plugins/gatsby-source-jenkinsplugins/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -501,12 +501,13 @@ const fetchPluginVersions = async ({createNode, reporter, firstReleases}) => {
const fetchPluginHealthScore = async ({createNode, reporter}) => {
const sectionActivity = reporter.activityTimer('fetch plugin health score');
sectionActivity.start();
const url = 'https://plugin-health.jenkins.io/api/scores';
const json = await requestGET({url, reporter});
for (const pluginName of Object.keys(json.plugins)) {
const data = json.plugins[pluginName];
const url = 'http://localhost:8080/api/scores';
alecharp marked this conversation as resolved.
Show resolved Hide resolved
const {plugins, statistics} = await requestGET({url, reporter});
for (const pluginName of Object.keys(plugins)) {
const {value, details} = plugins[pluginName];
createNode({
...data,
value,
details,
alecharp marked this conversation as resolved.
Show resolved Hide resolved
id: pluginName,
parent: null,
children: [],
Expand All @@ -519,6 +520,21 @@ const fetchPluginHealthScore = async ({createNode, reporter}) => {
}
});
}

createNode({
...statistics,
id: 'pluginHealthStatistics',
name: 'pluginHealthStatistics',
parent: null,
children: [],
internal: {
type: 'JenkinsPluginHealthScoreStatistics',
contentDigest: crypto
.createHash('md5')
.update('pluginHealthScoreStatistics')
.digest('hex')
}
});
sectionActivity.end();
};

Expand Down
8 changes: 8 additions & 0 deletions plugins/plugin-site/gatsby-node.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ async function createPluginPages({graphql, createPage}) {
const templatesPluginDocumentation = path.resolve('src/templates/plugin_documentation.jsx');
const templatesPluginIssues = path.resolve('src/templates/plugin_issues.jsx');
const templatesPluginDependencies = path.resolve('src/templates/plugin_dependencies.jsx');
const templatesPluginHealthScore = path.resolve('src/templates/plugin_healthScore.jsx');
const result = await graphql(`{
allJenkinsPlugin {
edges {
Expand Down Expand Up @@ -51,6 +52,13 @@ async function createPluginPages({graphql, createPage}) {
name: edge.node.name.trim()
}
});
createPage({
path: `/${edge.node.name.trim()}/healthscore/`,
component: templatesPluginHealthScore,
context: {
name: edge.node.name.trim()
}
});
});
}

Expand Down
4 changes: 2 additions & 2 deletions plugins/plugin-site/src/components/Plugin.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import Icon from '../components/Icon';
import PluginLabels from '../components/PluginLabels';
import PluginLastReleased from '../components/PluginLastReleased';
import PluginDevelopers from '../components/PluginDevelopers';
import PluginHealthScore from '../components/PluginHealthScore';
import PluginHealthScoreProgressBar from './PluginHealthScoreProgressBar';


function Developers({developers}) {
Expand Down Expand Up @@ -51,7 +51,7 @@ function Plugin({plugin: {name, title, stats, labels, excerpt, developers, build
<Developers developers={developers} />
</div>
<div className="Plugin--HealthScoreContainer">
<PluginHealthScore healthScore={healthScore} name={name}/>
<PluginHealthScoreProgressBar healthScore={healthScore} name={name}/>
</div>
</div>
);
Expand Down
67 changes: 9 additions & 58 deletions plugins/plugin-site/src/components/PluginHealthScore.jsx
Original file line number Diff line number Diff line change
@@ -1,67 +1,18 @@
import React, {useState} from 'react';
import React from 'react';
import PropTypes from 'prop-types';
import {Progress, Tooltip} from 'reactstrap';

function PluginHealthScore({healthScore, name}) {
if (!healthScore) {
return null;
}

const score = healthScore.value || 0;
const color =
score > 80 ? 'success' : score > 60 ? 'warning' : 'danger';

const tooltipId = `tooltip-${name}`;
const [tooltipOpen, setTooltipOpen] = useState(false);
const toggle = () => setTooltipOpen(!tooltipOpen);
function PluginHealthScore(healthScore) {
return (
<>
<div>
<div>
Health Score
<a
href={`https://plugin-health.jenkins.io/scores/${name}`}
id={tooltipId}
style={{marginLeft: '5px'}}
onClick={(e) => {
e.stopPropagation();
setTooltipOpen(!tooltipOpen);
}}
>
?
</a>
<Tooltip
placement="top"
isOpen={tooltipOpen}
autohide={false}
target={tooltipId}
toggle={toggle}
onClick={(e) => {e.stopPropagation();}}
>
View the details about plugin&apos;s
{' '}
<a
href={`https://plugin-health.jenkins.io/scores/${name}`}
>
health score
</a>
</Tooltip>
</div>
<div>
{score}
/100
</div>
</div>
<Progress value={score} color={color} style={{height: '10px'}} striped/>
</>
<div id="pluginHealthScore--container" className="container">
<pre>{JSON.stringify(healthScore, null, 2)}</pre>
</div>
);
}

PluginHealthScore.propTypes = {
healthScore: PropTypes.shape({
value: PropTypes.number,
}),
name: PropTypes.string,
healthScore: {
value: PropTypes.number.isRequired,
details: PropTypes.shape({}).isRequired
}
};

export default PluginHealthScore;
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import React, {useState} from 'react';
import PropTypes from 'prop-types';
import {Progress, Tooltip} from 'reactstrap';

function PluginHealthScoreProgressBar({healthScore, name}) {
if (!healthScore) {
return null;
}

const score = healthScore.value || 0;
const color =
score > 80 ? 'success' : score > 60 ? 'warning' : 'danger';

const tooltipId = `tooltip-${name}`;
const [tooltipOpen, setTooltipOpen] = useState(false);
const toggle = () => setTooltipOpen(!tooltipOpen);
return (
<>
<div>
<div>
Health Score
<a
href={`https://plugin-health.jenkins.io/scores/${name}`}
id={tooltipId}
style={{marginLeft: '5px'}}
onClick={(e) => {
e.stopPropagation();
setTooltipOpen(!tooltipOpen);
}}
>
?
</a>
<Tooltip
placement="top"
isOpen={tooltipOpen}
autohide={false}
target={tooltipId}
toggle={toggle}
onClick={(e) => {e.stopPropagation();}}
>
View the details about plugin&apos;s
{' '}
<a
href={`https://plugin-health.jenkins.io/scores/${name}`}
>
health score
</a>
</Tooltip>
</div>
<div>
{score}
/100
</div>
</div>
<Progress value={score} color={color} style={{height: '10px'}} striped/>
</>
);
}

PluginHealthScoreProgressBar.propTypes = {
healthScore: PropTypes.shape({
value: PropTypes.number,
}),
name: PropTypes.string,
};

export default PluginHealthScoreProgressBar;
1 change: 1 addition & 0 deletions plugins/plugin-site/src/components/PluginPageLayout.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ function PluginPageLayout({plugin, children}) {
{id: 'releases', to: `/${plugin.name}/releases/`, label: 'Releases'},
{id: 'issues', to: `/${plugin.name}/issues/`, label: 'Issues'},
{id: 'dependencies', to: `/${plugin.name}/dependencies/`, label: 'Dependencies'},
{id: 'health-score', to: `/${plugin.name}/healthscore/`, label: 'Health Score'},
];

const [isShowInstructions, setShowInstructions] = React.useState(false);
Expand Down
41 changes: 41 additions & 0 deletions plugins/plugin-site/src/templates/plugin_healthScore.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import {graphql} from 'gatsby';
import React from 'react';
import PropTypes from 'prop-types';
import {PropTypesJenkinsPlugin} from '../proptypes';

import PluginPageLayout from '../components/PluginPageLayout';
import PluginHealthScore from '../components/PluginHealthScore';

const TemplatePluginHealthScore = ({data: {jenkinsPlugin: plugin, healthScore}}) => {
return (
<PluginPageLayout plugin={plugin}>
<PluginHealthScore healthScore={healthScore}/>
</PluginPageLayout>
);
};

TemplatePluginHealthScore.displayName = 'TemplatePluginHealthScore';
TemplatePluginHealthScore.propTypes = {
data: PropTypes.shape({
jenkinsPlugin: PropTypesJenkinsPlugin.isRequired,
healthScore: PropTypes.shape({
}).isRequired
})
};

/* eslint no-undef: "off" */
export const pageQuery = graphql`
query ($name: String!) {
jenkinsPlugin(name: {eq: $name}) {
...JenkinsPluginFragment
}

healthScore: allJenkinsPluginHealthScore(filter: {name: {eq: $name}}) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

all prefix returns an array. I think you just want jenkinsPluginHealthScore(name: {eq: $name})

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also if you goto localhost:3000/__graphql you get the full graphql explorer.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually, jenkinsPlugin actually links them
https://github.com/jenkins-infra/plugin-site/blob/master/plugins/gatsby-source-jenkinsplugins/gatsby-node.js#L41

        jenkinsPlugin(name: {eq: $name}) {
            ...JenkinsPluginFragment
            healthScore: {
            }
        }

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thank you! This is what I didn't understand!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I didn't structure the data in the API correctly because I would need to describe all the graphql for any new details when this should be easily extensible. I need to change the object structure for an array so that we can easily create a table.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the source plugin can also do some data mashing for you, and graphql can do some joins. So like if you want to do one dataset for detail definitions, and one dataset for the actual results, we can do a join.

i'm just not sure if you have a question or not. Feel free to ping me again.

nodes {
id
}
}
}
`;

export default TemplatePluginHealthScore;