Skip to content

Commit

Permalink
feat(ui): add graph for code coverage on the overview app page (#3653)
Browse files Browse the repository at this point in the history
  • Loading branch information
bnjjj authored and sguiheux committed Dec 3, 2018
1 parent 558f97f commit a0dca3e
Show file tree
Hide file tree
Showing 7 changed files with 100 additions and 23 deletions.
22 changes: 22 additions & 0 deletions engine/api/metrics/elasticsearch.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"time"

"github.com/go-gorp/gorp"
coverage "github.com/sguiheux/go-coverage"
"gopkg.in/olivere/elastic.v5"

"github.com/ovh/cds/engine/api/services"
Expand Down Expand Up @@ -108,3 +109,24 @@ func PushUnitTests(projKey string, appID int64, workflowID int64, num int64, tes

metricsChan <- m
}

// PushCoverage Create metrics from coverage and send them
func PushCoverage(projKey string, appID int64, workflowID int64, num int64, cover coverage.Report) {
m := sdk.Metric{
Date: time.Now(),
ProjectKey: projKey,
ApplicationID: appID,
WorkflowID: workflowID,
Key: sdk.MetricKeyCoverage,
Num: num,
}

summary := make(map[string]float64, 3)
summary["covered_lines"] = float64(cover.CoveredLines)
summary["total_lines"] = float64(cover.TotalLines)
summary["percent"] = (summary["covered_lines"] / summary["total_lines"]) * 100

m.Value = summary

metricsChan <- m
}
26 changes: 18 additions & 8 deletions engine/api/ui.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,30 +31,31 @@ func (api *API) getApplicationOverviewHandler() service.Handler {
vars := mux.Vars(r)
key := vars["key"]
appName := vars["permApplicationName"]
db := api.mustDB()

p, errP := project.Load(api.mustDB(), api.Cache, key, getUser(ctx))
p, errP := project.Load(db, api.Cache, key, getUser(ctx))
if errP != nil {
return sdk.WrapError(errP, "getApplicationOverviewHandler> unable to load project")
}

app, errA := application.LoadByName(api.mustDB(), api.Cache, key, appName, getUser(ctx))
app, errA := application.LoadByName(db, api.Cache, key, appName, getUser(ctx))
if errA != nil {
return sdk.WrapError(errA, "getApplicationOverviewHandler> unable to load application")
}

usage, errU := loadApplicationUsage(api.mustDB(), key, appName)
usage, errU := loadApplicationUsage(db, key, appName)
if errU != nil {
return sdk.WrapError(errU, "getApplicationOverviewHandler> Cannot load application usage")
}
app.Usage = &usage

appOverview := sdk.ApplicationOverview{
Graphs: make([]sdk.ApplicationOverviewGraph, 0, 2),
Graphs: make([]sdk.ApplicationOverviewGraph, 0, 3),
History: make(map[string][]sdk.WorkflowRun, len(app.Usage.Workflows)),
}

// GET METRICS
m1, errMV := metrics.GetMetrics(api.mustDB(), key, app.ID, sdk.MetricKeyVulnerability)
m1, errMV := metrics.GetMetrics(db, key, app.ID, sdk.MetricKeyVulnerability)
if errMV != nil {
return sdk.WrapError(errMV, "getApplicationOverviewHandler> Cannot list vulnerability metrics")
}
Expand All @@ -63,7 +64,7 @@ func (api *API) getApplicationOverviewHandler() service.Handler {
Datas: m1,
})

m2, errUT := metrics.GetMetrics(api.mustDB(), key, app.ID, sdk.MetricKeyUnitTest)
m2, errUT := metrics.GetMetrics(db, key, app.ID, sdk.MetricKeyUnitTest)
if errUT != nil {
return sdk.WrapError(errUT, "getApplicationOverviewHandler> Cannot list Unit test metrics")
}
Expand All @@ -72,10 +73,19 @@ func (api *API) getApplicationOverviewHandler() service.Handler {
Datas: m2,
})

mCov, errCov := metrics.GetMetrics(db, key, app.ID, sdk.MetricKeyCoverage)
if errCov != nil {
return sdk.WrapError(errCov, "getApplicationOverviewHandler> Cannot list coverage metrics")
}
appOverview.Graphs = append(appOverview.Graphs, sdk.ApplicationOverviewGraph{
Type: sdk.MetricKeyCoverage,
Datas: mCov,
})

// GET VCS URL
// Get vcs info to known if we are on the default branch or not
if projectVCSServer := repositoriesmanager.GetProjectVCSServer(p, app.VCSServer); projectVCSServer != nil {
client, erra := repositoriesmanager.AuthorizedClient(ctx, api.mustDB(), api.Cache, projectVCSServer)
client, erra := repositoriesmanager.AuthorizedClient(ctx, db, api.Cache, projectVCSServer)
if erra != nil {
return sdk.WrapError(sdk.ErrNoReposManagerClientAuth, "getApplicationOverviewHandler> Cannot get repo client %s: %v", app.VCSServer, erra)
}
Expand All @@ -93,7 +103,7 @@ func (api *API) getApplicationOverviewHandler() service.Handler {
tagFilter := make(map[string]string, 1)
tagFilter["git.branch"] = defaultBranch
for _, w := range app.Usage.Workflows {
runs, _, _, _, errR := workflow.LoadRuns(api.mustDB(), key, w.Name, 0, 5, tagFilter)
runs, _, _, _, errR := workflow.LoadRuns(db, key, w.Name, 0, 5, tagFilter)
if errR != nil {
return sdk.WrapError(errR, "getApplicationOverviewHandler> Unable to load runs")
}
Expand Down
4 changes: 4 additions & 0 deletions engine/api/workflow/dao_coverage.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"github.com/ovh/cds/engine/api/cache"
"github.com/ovh/cds/engine/api/database/gorpmapping"
"github.com/ovh/cds/engine/api/metrics"
"github.com/ovh/cds/engine/api/repositoriesmanager"
"github.com/ovh/cds/sdk"
)
Expand Down Expand Up @@ -194,6 +195,9 @@ func ComputeLatestDefaultBranchReport(ctx context.Context, db gorp.SqlExecutor,
}
defaultCoverage.Report.Files = nil
covReport.Trend.DefaultBranch = defaultCoverage.Report
} else {
metrics.PushCoverage(proj.Key, wnr.ApplicationID, wnr.WorkflowID, wnr.Number, covReport.Report)
}

return nil
}
1 change: 1 addition & 0 deletions sdk/metric.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import "time"
const (
MetricKeyVulnerability = "Vulnerability"
MetricKeyUnitTest = "UnitTest"
MetricKeyCoverage = "Coverage"
)

// Metric represent a CDS metric
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import {Component, Input, OnInit} from '@angular/core';
import {TranslateService} from '@ngx-translate/core';
import {Application, Overview, Severity} from '../../../../model/application.model';
import {ChartData, ChartSeries, GraphConfiguration, GraphType} from '../../../../model/graph.model';
import {Metric} from '../../../../model/metric.model';
import {Tests} from '../../../../model/pipeline.model';
import {Project} from '../../../../model/project.model';
import {ApplicationNoCacheService} from '../../../../service/application/application.nocache.service';
import { Component, Input, OnInit } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { Application, Overview, Severity } from '../../../../model/application.model';
import { ChartData, ChartSeries, GraphConfiguration, GraphType } from '../../../../model/graph.model';
import { Metric } from '../../../../model/metric.model';
import { Tests } from '../../../../model/pipeline.model';
import { Project } from '../../../../model/project.model';
import { ApplicationNoCacheService } from '../../../../service/application/application.nocache.service';

@Component({
selector: 'app-home',
Expand All @@ -31,13 +31,18 @@ export class ApplicationHomeComponent implements OnInit {
this.overview = d;
if (d && d.graphs.length > 0) {
d.graphs.forEach(g => {
switch (g.type) {
case 'Vulnerability':
this.createVulnDashboard(g.datas);
break;
case 'UnitTest':
this.createUnitTestDashboard(g.datas);
break;
if (g.datas && g.datas.length) {
switch (g.type) {
case 'Vulnerability':
this.createVulnDashboard(g.datas);
break;
case 'UnitTest':
this.createUnitTestDashboard(g.datas);
break;
case 'Coverage':
this.createCoverageDashboard(g.datas);
break;
}
}
});
}
Expand Down Expand Up @@ -122,4 +127,35 @@ export class ApplicationHomeComponent implements OnInit {
});
this.dashboards.push(cc);
}

createCoverageDashboard(metrics: Array<Metric>): void {
let cc = new GraphConfiguration(GraphType.AREA_STACKED);
cc.title = this._translate.instant('graph_coverage_title');
cc.colorScheme = { domain: [] };
cc.gradient = false;
cc.showXAxis = true;
cc.showYAxis = true;
cc.showLegend = false;
cc.showXAxisLabel = true;
cc.showYAxisLabel = true;
cc.xAxisLabel = this._translate.instant('graph_vulnerability_x');
cc.yAxisLabel = this._translate.instant('graph_coverage_y');
cc.datas = new Array<ChartData>();

let cd = new ChartData();
cd.name = this._translate.instant('graph_coverage_y');
cd.series = new Array<ChartSeries>();
metrics.forEach(m => {
let v = m.value['percent'];
if (v) {
let cs = new ChartSeries();
cs.name = m.run.toString();
cs.value = v;
cd.series.push(cs);
}
});
cc.datas.push(cd);
cc.colorScheme['domain'].push('#4286f4');
this.dashboards.push(cc);
}
}
2 changes: 2 additions & 0 deletions ui/src/assets/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,8 @@
"graph_unittest_title": "Unit Tests",
"graph_unittest_x": "N° workflow",
"graph_unittest_y": "Total",
"graph_coverage_title": "Code coverage",
"graph_coverage_y": "Percentage",

"group_added": "Group added",
"group_deleted": "Group deleted",
Expand Down
2 changes: 2 additions & 0 deletions ui/src/assets/i18n/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,8 @@
"graph_unittest_title": "Tests unitaires",
"graph_unittest_x": "N° workflow",
"graph_unittest_y": "Total",
"graph_coverage_title": "Couverture",
"graph_coverage_y": "Taux de couverture",

"group_added": "Groupe ajouté",
"group_deleted": "Groupe supprimé",
Expand Down

0 comments on commit a0dca3e

Please sign in to comment.