diff --git a/src/compare/compare.controller.js b/src/compare/compare.controller.js index 0b70141..2f0a20e 100644 --- a/src/compare/compare.controller.js +++ b/src/compare/compare.controller.js @@ -34,4 +34,5 @@ function editController($scope, $http, sonarApi, sonarEndpoint) { }); } $scope.updateProjects(); + } diff --git a/src/issues/edit.html b/src/issues/edit.html new file mode 100644 index 0000000..f29c501 --- /dev/null +++ b/src/issues/edit.html @@ -0,0 +1,18 @@ +
+
+
+ + +
+
+
+
+ + +
+
+
diff --git a/src/issues/issue.controller.js b/src/issues/issue.controller.js new file mode 100644 index 0000000..fc0c417 --- /dev/null +++ b/src/issues/issue.controller.js @@ -0,0 +1,102 @@ +'use strict'; + +sonarADFWidget. + controller('sonarIssueCtrl', sonarIssueCtrl); + +function sonarIssueCtrl(data, config) { + var vm = this; + + if (data.length != 0) { + + angular.forEach(data, function (issue) { + + // Preparing displaying of issue components + if (issue.subProject) + issue.subProject = issue.subProject.slice(issue.component.search(":") + 1).replace(":", " "); + if (issue.project) + issue.project = issue.project.slice(issue.component.search(":") + 1).replace(":", " "); //eig wird noch "parent" abgeschnitten, aber keine Ahnung warum! + if (issue.component) + issue.component = issue.component.slice(issue.component.lastIndexOf(":") + 1); + + if (issue.type) + issue.type = issue.type.replace("_", " "); + + for (var i = 0; i < issue.tags.length; i++) { + if (i == 0) + issue.tag = issue.tags[i]; + else + issue.tag = issue.tag + ", " + issue.tags[i]; + } + }); + + // sorting the elements by project, subProject and component + // has the structure: projects [project, subProject, component, projectIssues[]] + vm.projects = new Array(); + vm.projects[0] = new Object(); + + var counter = 0; //counting the projects + var counter2 = 1; //counting the projectIssues per project + for (var i = 0; i < data.length; i++) { + + if (data[i].status !== "CLOSED") { + + if (!vm.projects[counter].project) { //first initialisation of an object + vm.projects[counter] = new Object(); + vm.projects[counter].project = data[i].project; + vm.projects[counter].subProject = data[i].subProject; + vm.projects[counter].component = data[i].component; + vm.projects[counter].projectIssue = new Array(); + vm.projects[counter].projectIssue[0] = data[i]; + } else { //if there is already an object in vm.projects + if (data[i].project === vm.projects[counter].project + && data[i].subProject === vm.projects[counter].subProject + && data[i].component === vm.projects[counter].component) { + vm.projects[counter].projectIssue[counter2] = data[i]; + counter2 = counter2 + 1; + } else { + counter = counter + 1; + counter2 = 1; + vm.projects[counter] = new Object(); + vm.projects[counter].project = data[i].project; + vm.projects[counter].subProject = data[i].subProject; + vm.projects[counter].component = data[i].component; + vm.projects[counter].projectIssue = new Array; + vm.projects[counter].projectIssue[0] = data[i]; + } + } + } + } + } + + vm.sorting = function (issue) { + if (config.sorting == "sortBySeverity") + return vm.sortBySeverity(issue); + else + return vm.sortByEffort(issue); + }; + + + vm.sortBySeverity = function (issue) { + var severity = 0; //4=blocker, 3=critical, 2=major, 1=minor, 0=info + for (var i = 0; i < issue.projectIssue.length; i++) { + if (issue.projectIssue[i].severity === "BLOCKER") + severity = 4; + else if (issue.projectIssue[i].severity === "CRITICAL" && severity < 3) + severity = 3; + else if (issue.projectIssue[i].severity === "MAJOR" && severity < 2) + severity = 2; + else if (issue.projectIssue[i].severity === "MINOR" && severity < 1) + severity = 1; + } + return -severity; + }; + + vm.sortByEffort = function (issue) { + var effort = 0; + for (var i = 0; i < issue.projectIssue.length; i++) { + if (issue.projectIssue[i].effort && effort < parseInt(issue.projectIssue[i].effort.slice(0, issue.projectIssue[i].effort.search("m")))) + effort = parseInt(issue.projectIssue[i].effort.slice(0, issue.projectIssue[i].effort.search("m"))); + } + return -effort; + }; +} diff --git a/src/issues/view.html b/src/issues/view.html new file mode 100644 index 0000000..8ea90f5 --- /dev/null +++ b/src/issues/view.html @@ -0,0 +1,64 @@ + +
+ You don't have any issues. +
+
+ +
+
+ + {{project.project}} + + {{project.subProject}} + + {{project.component}}
+
+
+
+ + + + + + +
{{issue.message}}L{{issue.line}}
+ + + + + + + + +
{{issue.type | lowercase}} + + + + + + {{issue.severity | lowercase}} + {{issue.status | lowercase}} {{issue.effort}} effort {{issue.tag}}
+
+
+
+ +
diff --git a/src/projectquality/edit.html b/src/projectquality/edit.html new file mode 100644 index 0000000..49ee612 --- /dev/null +++ b/src/projectquality/edit.html @@ -0,0 +1,17 @@ + +
+
+ +

+ +

+ + (*Required) +

+ +

+
+
diff --git a/src/projectquality/quality.controller.js b/src/projectquality/quality.controller.js new file mode 100644 index 0000000..0db924b --- /dev/null +++ b/src/projectquality/quality.controller.js @@ -0,0 +1,18 @@ +'use strict'; + +sonarADFWidget.controller('qualityCtrl', qualityCtrl); + +function qualityCtrl(data) { + var vm = this; + vm.name = data.name; + + angular.forEach(data.msr, function (metric) { + if (metric.key === "coverage") //going through all entries with if/elseif since there could miss some entries. So there is no special order + vm.coverage = metric.frmt_val; + else if (metric.key === "blocker_violations") + vm.blocker = metric.frmt_val; + else if (metric.key === "quality_gate_details") { + vm.qualityGateStatus = metric.data.split('"')[3]; //structure of quality_gate_details: "level":"OK",... + } + }); +} diff --git a/src/projectquality/view.html b/src/projectquality/view.html new file mode 100644 index 0000000..f57e1e0 --- /dev/null +++ b/src/projectquality/view.html @@ -0,0 +1,97 @@ + + + +
+ Please configure the widget +
+ +
+ +
+
+
+ +

Passed

+

Quality Gate

+
+
+
+
+ +

Error

+

Quality Gate

+
+
+
+
+ +

Warning

+

Quality Gate

+
+
+ +
+
+ +

unknown

+

Quality Gate

+
+
+
+ +
+ +

{{vm.coverage||"unknown"}}

+

Code Coverage

+
+
+ +

{{vm.blocker||"unknown"}}

+

Blocker Issues

+
+ +
+ diff --git a/src/service.js b/src/service.js index 0434a85..0fd44f2 100644 --- a/src/service.js +++ b/src/service.js @@ -13,10 +13,18 @@ function sonarApi($http, $q) { function createApiUrlAllProjectsStatistics(sonarUrl) { return sonarUrl + '/api/resources?metrics=ncloc,coverage'; } + + function createApiUrlAllMyIssues(sonarUrl) { + return sonarUrl + '/api/issues/search?assignees=__me__';//--> nur zum Testen, eigentlich ist es __me__! + } function createApiUrlMetrics(sonarUrl, projectname) { return sonarUrl + '/api/measures/component?componentKey=' + projectname + '&metricKeys=open_issues,ncloc,public_documented_api_density,duplicated_lines_density,sqale_index'; } + + function createApiUrlQuality(sonarUrl, projectname) { + return sonarUrl + '/api/resources?resource=' + projectname + '&metrics=coverage,blocker_violations,quality_gate_details'; + } function getProjectTime(projectBeginn, projectEnd) { var beginn = new Date(projectBeginn); @@ -24,13 +32,13 @@ function sonarApi($http, $q) { var today = new Date(); var maxDays = workingDaysBetweenDates(beginn, end); - var daysLeft = workingDaysBetweenDates(today, end) + var daysLeft = workingDaysBetweenDates(today, end); return { 'maxDays': maxDays, 'daysLeft': daysLeft }; - + } function workingDaysBetweenDates(startDate, endDate) { @@ -227,13 +235,43 @@ function sonarApi($http, $q) { return generateArray(projects); }); } + + function getAllMyIssues(sonarUrl){ + var apiUrl = createApiUrlAllMyIssues(sonarUrl); + + return $http({ + method: 'GET', + url: apiUrl, + headers: { + 'Accept': 'application/json' + } + }).then(function(response) { + return response.data.issues; + }); + } + + function getProjectquality(sonarUrl, project){ + var apiUrl = createApiUrlQuality(sonarUrl, project); + + return $http({ + method: 'GET', + url: apiUrl, + headers: { + 'Accept': 'application/json' + } + }).then(function(response) { + return response.data[0]; + }); + } return { getProjects: getProjects, getAllProjectsStatistics: getAllProjectsStatistics, getChartData: getChartData, getMetrics: getMetrics, - getProjectTime: getProjectTime + getProjectTime: getProjectTime, + getAllMyIssues: getAllMyIssues, + getProjectquality: getProjectquality }; } diff --git a/src/sonar.js b/src/sonar.js index 9cce32d..904691b 100644 --- a/src/sonar.js +++ b/src/sonar.js @@ -2,7 +2,7 @@ //app initialisation with dependencies var sonarADFWidget = angular.module('adf.widget.sonar', ['adf.provider', 'chart.js', 'ui.bootstrap', 'ui.bootstrap.datepicker','angular-svg-round-progressbar']) .constant("sonarEndpoint", { - "url": "https://sonarqube.com" + "url": "https://ecosystem.cloudogu.com/sonar"//https://sonarqube.com" }).constant("METRIC_NAMES", {"open_issues":"Open Issues","ncloc":"Lines of Code", "public_documented_api_density": "Public documented API density","duplicated_lines_density": "Duplicated Lines (%)", "sqale_index":"SQALE index", "coverage": "Coverage (%)", "tests": "Tests" }) @@ -111,6 +111,50 @@ var sonarADFWidget = angular.module('adf.widget.sonar', ['adf.provider', 'chart. edit: { templateUrl: '{widgetsPath}/sonar/src/project-progress/edit.html' } + }) + .widget('sonar-my-issues', { + title: 'Sonar: My Issues', + description: 'Displays all issues of yourself', + templateUrl: '{widgetsPath}/sonar/src/issues/view.html', + resolve: { + data: function(sonarApi, config, sonarEndpoint) { + if (config.apiUrl) { + return sonarApi.getAllMyIssues(config.apiUrl); + } + else if (sonarEndpoint.url){ + return sonarApi.getAllMyIssues(sonarEndpoint.url); + } + return 'Please Setup the Widget'; + } + }, + category: 'SonarQube', + controller: 'sonarIssueCtrl', + controllerAs: 'vm', + edit: { + templateUrl: '{widgetsPath}/sonar/src/issues/edit.html' + } + }) + .widget('sonar-projectquality', { + title: 'Sonar: Projectquality of a Project', + description: 'Displays Status of the Quality Gate, Code Coverage and Blocker Issues', + templateUrl: '{widgetsPath}/sonar/src/projectquality/view.html', + resolve: { + data: function(sonarApi, config, sonarEndpoint) { + if (config.apiUrl && config.project) { + return sonarApi.getProjectquality(config.apiUrl, config.project); + } + else if (sonarEndpoint.url && config.project){ + return sonarApi.getProjectquality(sonarEndpoint.url, config.project); + } + return 'Please Setup the Widget'; + } + }, + category: 'SonarQube', + controller: 'qualityCtrl', + controllerAs: 'vm', + edit: { + templateUrl: '{widgetsPath}/sonar/src/projectquality/edit.html' + } }); });