Skip to content

Commit

Permalink
Add Pie chart widget and fix chart config (#21)
Browse files Browse the repository at this point in the history
  • Loading branch information
rajbos authored Sep 20, 2023
1 parent e1fc5c7 commit ed792df
Show file tree
Hide file tree
Showing 8 changed files with 290 additions and 75 deletions.
72 changes: 59 additions & 13 deletions chart/chart.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,35 @@
function (WidgetHelpers, Services, context) {
WidgetHelpers.IncludeWidgetStyles();
VSS.register("GHAzDoWidget.Chart", function () {
async function renderTrendLine(organization, projectName, repoId, $container, chartService) {
consoleLog('renderTrendLine');
try {
// get the trend data for alerts first
const alertTrendLines = await getAlertsTrendLines(organization, projectName, repoId)
consoleLog('Dependencies AlertTrend: ' + JSON.stringify(alertTrendLines.dependencyAlertsTrend));
consoleLog('Code scanning AlertTrend: ' + JSON.stringify(alertTrendLines.codeAlertsTrend));
consoleLog('Secrets AlertTrend: ' + JSON.stringify(alertTrendLines.secretAlertsTrend));

createChart($container, chartService, alertTrendLines);
}
catch (err) {
consoleLog(`Error loading the alerts trend: ${err}`);
}
}

async function renderPieChart(organization, projectName, repoId, $container, chartService, alertType) {
consoleLog('renderPieChart');
try {
// get the trend data for alerts first
const alertSeverityCount = await getAlertSeverityCounts(organization, projectName, repoId, alertType);

createPieChart($container, chartService, alertSeverityCount);
}
catch (err) {
consoleLog(`Error loading the alerts pie: ${err}`);
}
}

return {
load: async function(widgetSettings) {
return Services.ChartsService.getService().then(async function(chartService){
Expand Down Expand Up @@ -49,25 +78,42 @@
let repoId
// init empty object first
let alertTrendLines = {secretAlertTrend: [], dependencyAlertTrend: [], codeAlertsTrend: []};
let chartType = 1;
let alertTypeConfig = 1;
if (data && data.chartType && data.chartType !== "") {
chartType = data.chartType;
consoleLog('loaded chartType from widgetSettings: ' + chartType);
}
else {
consoleLog('chartType is not set, using default value: ' + chartType);
}

if (data && data.alertType) {
alertTypeConfig = data.alertType;
consoleLog('loaded alertType from widgetSettings: ' + alertTypeConfig);
}

if (data && data.repo && data.repo !== "") {
repoName = data.repo;
repoId = data.repoId;
consoleLog('loaded repoName from widgetSettings: ' + repoName);

$title.text(`Advanced Security Alerts Trend`)
$container.text(`${data.repo}`)

try {
// get the trend data for alerts first
alertTrendLines = await getAlertsTrendLines(organization, projectName, repoId)
consoleLog('Dependencies AlertTrend: ' + JSON.stringify(alertTrendLines.dependencyAlertsTrend));
consoleLog('Code scanning AlertTrend: ' + JSON.stringify(alertTrendLines.codeAlertsTrend));
consoleLog('Secrets AlertTrend: ' + JSON.stringify(alertTrendLines.secretAlertsTrend));

createChart($container, chartService, alertTrendLines);
}
catch (err) {
consoleLog(`Error loading the alerts trend: ${err}`);
switch (chartType) {
case "2":
try {
const alertType = GetAlertTypeFromValue(alertTypeConfig);
$title.text(`${alertType.display} Alerts by Severity`)
renderPieChart(organization, projectName, repoId, $container, chartService, alertType);
}
catch (err) {
consoleLog(`Error loading the alerts pie: ${err}`);
}
break;
default:
$title.text(`Advanced Security Alerts Trend`)
renderTrendLine(organization, projectName, repoId, $container, chartService);
break;
}
}
else {
Expand Down
39 changes: 38 additions & 1 deletion chart/chart.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,43 @@ async function createChart($container, chartService, alertTrendLines){
chartService.createChart($container, chartOptions);
}
catch (err) {
console.log(`Error creating chart: ${err}`);
console.log(`Error creating line chart: ${err}`);
}
}

async function createPieChart($container, chartService, alertSeverityCount) {
// convert alertSeverityCount to two arrays, one for the labels and one for the data
consoleLog(`createPieChart for alertSeverityCount: ${JSON.stringify(alertSeverityCount)}`);
const labels = [];
const data = [];
for (const index in alertSeverityCount) {
const item = alertSeverityCount[index];
labels.push(item.severity);
data.push(item.count);
}

var chartOptions = {
"hostOptions": {
"height": "290",
"width": "300"
},
"chartType": "pie",
"series": [{
"data": data
}],
"xAxis": {
"labelValues": labels
},
"specializedOptions": {
"showLabels": "true",
"size": 200
}
};

try {
chartService.createChart($container, chartOptions);
}
catch (err) {
console.log(`Error creating pie chart: ${err}`);
}
}
146 changes: 97 additions & 49 deletions chart/configuration_2x2.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<script src="../lib/VSS.SDK.min.js"></script>
<script src="../library.js"></script>
<link rel="stylesheet" href="../styles.css" />

<script type="text/javascript">
Expand All @@ -13,22 +14,47 @@
VSS.require(["VSS/Service", "TFS/Dashboards/WidgetHelpers", "VSS/Context", "TFS/VersionControl/GitRestClient"],
function (Service, WidgetHelpers, context, GitWebApi) {
VSS.register("GHAzDoWidget.Chart.Configuration", function () {
var $repoDropdown = $("#repo-dropdown");

async function getRepos() {
try {
const webContext = VSS.getWebContext();
const project = webContext.project;

// todo: load the available repos in this project
const gitClient = Service.getClient(GitWebApi.GitHttpClient);
repos = await gitClient.getRepositories(project.name);
console.log(`Found these repos: ${JSON.stringify(repos)}`);
return repos;
const $repoDropdown = $("#repo-dropdown");
const $chartTypeDropdown = $("#chart-type");
const $alertTypeDropdown = $("#alert-type");

function reloadWidget(widgetConfigurationContext) {
const customSettings = getSettings();
var eventName = WidgetHelpers.WidgetEvent.ConfigurationChange;
var eventArgs = WidgetHelpers.WidgetEvent.Args(customSettings);
widgetConfigurationContext.notify(eventName, eventArgs);
}

function getSettings() {
if (repos) {
// find the repo with this name
const repo = repos.find(r => r.name === $repoDropdown.val());

if (repo) {
var customSettings = {
data: JSON.stringify({
repo: $repoDropdown.val(),
repoId: repo.id,
chartType: $chartTypeDropdown.val(),
alertType: $alertTypeDropdown.val()
})
};
return customSettings;
}
}
catch (err) {
console.log(`Error loading the available repos: ${err}`);
return [];

return {};
}

function reloadChartOptions() {
const chartType = $chartTypeDropdown.val();
if (chartType === "1") {
// trend line
$("#alertTypePanel").hide();
}
else if (chartType === "2") {
// pie chart
$("#alertTypePanel").show();
}
}

Expand All @@ -37,7 +63,7 @@
var settings = JSON.parse(widgetSettings.customSettings.data);
console.log(`Loading the Chart.2x2 settings with ${JSON.stringify(settings)}`)

const repos = await getRepos();
const repos = await getRepos(VSS, Service, GitWebApi);
// add all repos as selection options to the dropdown
if (repos) {
// add a top option to select no repo
Expand All @@ -54,39 +80,35 @@
$repoDropdown.val(settings.repo);
}

$repoDropdown.on("change", function () {
let repo;
if (repos) {
// find the repo with this name
repo = repos.find(r => r.name === $repoDropdown.val());
}
if (settings && settings.chartType) {
// select the chartType that was saved in the settings
$chartTypeDropdown.val(settings.chartType);
}

var customSettings = {
data: JSON.stringify({
repo: $repoDropdown.val(),
repoId: repo.id
})
};
var eventName = WidgetHelpers.WidgetEvent.ConfigurationChange;
var eventArgs = WidgetHelpers.WidgetEvent.Args(customSettings);
widgetConfigurationContext.notify(eventName, eventArgs);
if (settings && settings.alertType) {
// select the alertType that was saved in the settings
$alertTypeDropdown.val(settings.alertType);
}

// register a change event handler for the dropdowns
$repoDropdown.on("change", function () {
reloadWidget(widgetConfigurationContext)
});

$chartTypeDropdown.on("change", function () {
reloadWidget(widgetConfigurationContext)
reloadChartOptions();
});

$alertTypeDropdown.on("change", function () {
reloadWidget(widgetConfigurationContext)
});

return WidgetHelpers.WidgetStatusHelper.Success();
},
onSave: async function() {
const repos = await getRepos();
let repo;
if (repos) {
// find the repo with this name
repo = repos.find(r => r.name === $repoDropdown.val());
}
var customSettings = {
data: JSON.stringify({
repo: $repoDropdown.val(),
repoId: repo.id
})
};
const customSettings = getSettings();

console.log(`Saving the Chart.2x2 settings with ${JSON.stringify(customSettings)}`)
return WidgetHelpers.WidgetConfigurationSave.Valid(customSettings);
}
Expand All @@ -98,12 +120,38 @@
</head>
<body>
<div class="container">
<fieldset>
<label class="label">Repository: </label>
<select id="repo-dropdown" style="margin-top:10px">
<!-- todo: dynamically load the available repos in this project-->
</select>
</fieldset>
<table class="ghazdo-table">
<tr>
<td>
<label class="label">Repository: </label>
</td>
<td>
<select id="repo-dropdown" class="dropdown" style="margin-top:10px"></select>
</td>
</tr>
<tr>
<td>
<label class="label">Chart type: </label>
</td>
<td>
<select id="chart-type" class="dropdown">
<option value="1">Trend line</option>
<option value="2">Pie chart</option>
</select>
</td>
</tr>
<tr id="alertTypePanel">
<td>
<label class="label">Alert type: </label>
</td>
<td>
<select id="alert-type" class="dropdown">
<option value="1">Dependency</option>
<option value="2">Secret</option>
<option value="3">Code</option>
</select>
</td>
</table>
</div>
</body>
</html>
Loading

0 comments on commit ed792df

Please sign in to comment.