diff --git a/backend/core/models/domainlayer/codequality/cq_issues.go b/backend/core/models/domainlayer/codequality/cq_issues.go index 6f5d2a465e8..243e675fa73 100644 --- a/backend/core/models/domainlayer/codequality/cq_issues.go +++ b/backend/core/models/domainlayer/codequality/cq_issues.go @@ -52,3 +52,14 @@ type CqIssue struct { func (CqIssue) TableName() string { return "cq_issues" } + +type CqIssueImpact struct { + common.NoPKModel + CqIssueId string `gorm:"primaryKey;type:varchar(255)"` + SoftwareQuality string `gorm:"primaryKey;type:varchar(255)"` + Severity string `gorm:"type:varchar(100)"` +} + +func (CqIssueImpact) TableName() string { + return "cq_issue_impacts" +} diff --git a/backend/core/models/domainlayer/domaininfo/domaininfo.go b/backend/core/models/domainlayer/domaininfo/domaininfo.go index 6837126f895..3bec3b810c7 100644 --- a/backend/core/models/domainlayer/domaininfo/domaininfo.go +++ b/backend/core/models/domainlayer/domaininfo/domaininfo.go @@ -53,6 +53,7 @@ func GetDomainTablesInfo() []dal.Tabler { &codequality.CqFileMetrics{}, &codequality.CqIssueCodeBlock{}, &codequality.CqIssue{}, + &codequality.CqIssueImpact{}, &codequality.CqProject{}, // crossdomain &crossdomain.Account{}, diff --git a/backend/core/models/migrationscripts/20241010_add_ca_issue_impacts.go b/backend/core/models/migrationscripts/20241010_add_ca_issue_impacts.go new file mode 100644 index 00000000000..307df0147cc --- /dev/null +++ b/backend/core/models/migrationscripts/20241010_add_ca_issue_impacts.go @@ -0,0 +1,49 @@ +/* +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You under the Apache License, Version 2.0 +(the "License"); you may not use this file except in compliance with +the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package migrationscripts + +import ( + "github.com/apache/incubator-devlake/core/context" + "github.com/apache/incubator-devlake/core/errors" + "github.com/apache/incubator-devlake/core/models/migrationscripts/archived" + "github.com/apache/incubator-devlake/core/plugin" +) + +var _ plugin.MigrationScript = (*addCqIssueImpacts)(nil) + +type cqIssueImpacts struct { + archived.NoPKModel + CqIssueId string `gorm:"primaryKey;type:varchar(255)"` + SoftwareQuality string `gorm:"primaryKey;type:varchar(255)"` + Severity string `gorm:"type:varchar(100)"` +} + +type addCqIssueImpacts struct { +} + +func (script *addCqIssueImpacts) Up(basicRes context.BasicRes) errors.Error { + return basicRes.GetDal().AutoMigrate(&cqIssueImpacts{}) +} + +func (*addCqIssueImpacts) Version() uint64 { + return 20241010162658 +} + +func (*addCqIssueImpacts) Name() string { + return "add cq_issue_impacts table" +} diff --git a/backend/core/models/migrationscripts/register.go b/backend/core/models/migrationscripts/register.go index 3a5bb5ec486..cd1cfb7c823 100644 --- a/backend/core/models/migrationscripts/register.go +++ b/backend/core/models/migrationscripts/register.go @@ -133,5 +133,6 @@ func All() []plugin.MigrationScript { new(addAssigneeToIncident), new(addIsSubtaskToIssue), new(addIsChildToCicdPipeline), + new(addCqIssueImpacts), } } diff --git a/backend/helpers/pluginhelper/api/api_client.go b/backend/helpers/pluginhelper/api/api_client.go index 5e1535de27d..1e7e57d44f5 100644 --- a/backend/helpers/pluginhelper/api/api_client.go +++ b/backend/helpers/pluginhelper/api/api_client.go @@ -62,6 +62,7 @@ type ApiClient struct { data map[string]interface{} data_mutex sync.Mutex + authFunc plugin.ApiClientBeforeRequest beforeRequest plugin.ApiClientBeforeRequest afterResponse plugin.ApiClientAfterResponse ctx gocontext.Context @@ -92,7 +93,7 @@ func NewApiClientFromConnection( // if connection requires authorization if authenticator, ok := connection.(plugin.ApiAuthenticator); ok { - apiClient.SetBeforeFunction(func(req *http.Request) errors.Error { + apiClient.SetAuthFunction(func(req *http.Request) errors.Error { return authenticator.SetupAuthentication(req) }) } @@ -255,6 +256,16 @@ func (apiClient *ApiClient) SetBeforeFunction(callback plugin.ApiClientBeforeReq apiClient.beforeRequest = callback } +// GetAuthFunction +func (apiClient *ApiClient) GetAuthFunction() plugin.ApiClientBeforeRequest { + return apiClient.authFunc +} + +// SetAuthFunction +func (apiClient *ApiClient) SetAuthFunction(callback plugin.ApiClientBeforeRequest) { + apiClient.authFunc = callback +} + // GetAfterFunction return afterResponseFunction func (apiClient *ApiClient) GetAfterFunction() plugin.ApiClientAfterResponse { return apiClient.afterResponse @@ -345,6 +356,14 @@ func (apiClient *ApiClient) Do( } var res *http.Response + // authFunc + if apiClient.authFunc != nil { + err = apiClient.authFunc(req) + if err != nil { + apiClient.logError(err, "[api-client] authFunc returned error for %s", req.URL.String()) + return nil, err + } + } // before send if apiClient.beforeRequest != nil { err = apiClient.beforeRequest(req) diff --git a/backend/plugins/sonarqube/impl/impl.go b/backend/plugins/sonarqube/impl/impl.go index 9fbef921363..5c1a774867b 100644 --- a/backend/plugins/sonarqube/impl/impl.go +++ b/backend/plugins/sonarqube/impl/impl.go @@ -80,6 +80,7 @@ func (p Sonarqube) GetTablesInfo() []dal.Tabler { &models.SonarqubeConnection{}, &models.SonarqubeProject{}, &models.SonarqubeIssue{}, + &models.SonarqubeIssueImpact{}, &models.SonarqubeIssueCodeBlock{}, &models.SonarqubeHotspot{}, &models.SonarqubeFileMetrics{}, @@ -102,6 +103,7 @@ func (p Sonarqube) SubTaskMetas() []plugin.SubTaskMeta { tasks.ExtractAccountsMeta, tasks.ConvertProjectsMeta, tasks.ConvertIssuesMeta, + tasks.ConvertIssueImpactsMeta, tasks.ConvertIssueCodeBlocksMeta, tasks.ConvertHotspotsMeta, tasks.ConvertFileMetricsMeta, diff --git a/backend/plugins/sonarqube/models/connection.go b/backend/plugins/sonarqube/models/connection.go index e4113e306ef..66c32f57c49 100644 --- a/backend/plugins/sonarqube/models/connection.go +++ b/backend/plugins/sonarqube/models/connection.go @@ -20,9 +20,10 @@ package models import ( "encoding/base64" "fmt" - "github.com/apache/incubator-devlake/core/utils" "net/http" + "github.com/apache/incubator-devlake/core/utils" + "github.com/apache/incubator-devlake/core/errors" "github.com/apache/incubator-devlake/core/plugin" helper "github.com/apache/incubator-devlake/helpers/pluginhelper/api" @@ -49,6 +50,7 @@ func (sat SonarqubeAccessToken) GetEncodedToken() string { type SonarqubeConn struct { helper.RestConnection `mapstructure:",squash"` SonarqubeAccessToken `mapstructure:",squash"` + Organization string `gorm:"serializer:json" json:"org" mapstructure:"org"` } func (connection SonarqubeConn) Sanitize() SonarqubeConn { @@ -89,3 +91,24 @@ func (connection *SonarqubeConnection) MergeFromRequest(target *SonarqubeConnect } return nil } + +func (connection *SonarqubeConnection) IsCloud() bool { + return connection.Endpoint == "https://sonarcloud.io/api/" +} + +const ORG = "org" + +func (connection *SonarqubeConn) PrepareApiClient(apiClient plugin.ApiClient) errors.Error { + apiClient.SetData(ORG, connection.Organization) + apiClient.SetBeforeFunction(func(req *http.Request) errors.Error { + org := apiClient.GetData(ORG).(string) + if org != "" { + query := req.URL.Query() + query.Add("organization", org) + req.URL.RawQuery = query.Encode() + } + return nil + }) + + return nil +} diff --git a/backend/plugins/sonarqube/models/migrationscripts/20240930_add_connection_orgs.go b/backend/plugins/sonarqube/models/migrationscripts/20240930_add_connection_orgs.go new file mode 100644 index 00000000000..87f64a1d477 --- /dev/null +++ b/backend/plugins/sonarqube/models/migrationscripts/20240930_add_connection_orgs.go @@ -0,0 +1,50 @@ +/* +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You under the Apache License, Version 2.0 +(the "License"); you may not use this file except in compliance with +the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package migrationscripts + +import ( + "github.com/apache/incubator-devlake/core/context" + "github.com/apache/incubator-devlake/core/errors" + "github.com/apache/incubator-devlake/core/plugin" + "github.com/apache/incubator-devlake/helpers/migrationhelper" +) + +var _ plugin.MigrationScript = (*addOrgToConn)(nil) + +type connection20240930 struct { + Organization string +} + +func (connection20240930) TableName() string { + return "_tool_sonarqube_connections" +} + +type addOrgToConn struct { +} + +func (script *addOrgToConn) Up(basicRes context.BasicRes) errors.Error { + return migrationhelper.AutoMigrateTables(basicRes, &connection20240930{}) +} + +func (*addOrgToConn) Version() uint64 { + return 20240930151715 +} + +func (*addOrgToConn) Name() string { + return "add organizations to the connections table" +} diff --git a/backend/plugins/sonarqube/models/migrationscripts/20241010_add_issue_impacts.go b/backend/plugins/sonarqube/models/migrationscripts/20241010_add_issue_impacts.go new file mode 100644 index 00000000000..00ed2435878 --- /dev/null +++ b/backend/plugins/sonarqube/models/migrationscripts/20241010_add_issue_impacts.go @@ -0,0 +1,55 @@ +/* +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You under the Apache License, Version 2.0 +(the "License"); you may not use this file except in compliance with +the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package migrationscripts + +import ( + "github.com/apache/incubator-devlake/core/context" + "github.com/apache/incubator-devlake/core/errors" + "github.com/apache/incubator-devlake/core/models/migrationscripts/archived" + "github.com/apache/incubator-devlake/core/plugin" + "github.com/apache/incubator-devlake/helpers/migrationhelper" +) + +var _ plugin.MigrationScript = (*addIssueImpacts)(nil) + +type issueImpacts20241010 struct { + ConnectionId uint64 `gorm:"primaryKey"` + IssueKey string `gorm:"primaryKey;type:varchar(100)"` + SoftwareQuality string `gorm:"primaryKey;type:varchar(255)"` + Severity string `gorm:"type:varchar(100)"` + archived.NoPKModel +} + +func (issueImpacts20241010) TableName() string { + return "_tool_sonarqube_issue_impacts" +} + +type addIssueImpacts struct { +} + +func (script *addIssueImpacts) Up(basicRes context.BasicRes) errors.Error { + return migrationhelper.AutoMigrateTables(basicRes, &issueImpacts20241010{}) +} + +func (*addIssueImpacts) Version() uint64 { + return 20241010162943 +} + +func (*addIssueImpacts) Name() string { + return "add issue_impacts table for sonarcloud" +} diff --git a/backend/plugins/sonarqube/models/migrationscripts/register.go b/backend/plugins/sonarqube/models/migrationscripts/register.go index b11549cb8d5..849f112488b 100644 --- a/backend/plugins/sonarqube/models/migrationscripts/register.go +++ b/backend/plugins/sonarqube/models/migrationscripts/register.go @@ -36,5 +36,7 @@ func All() []plugin.MigrationScript { new(modifyNameLength), new(changeIssueComponentType), new(increaseProjectKeyLength), + new(addOrgToConn), + new(addIssueImpacts), } } diff --git a/backend/plugins/sonarqube/models/sonarqube_issue.go b/backend/plugins/sonarqube/models/sonarqube_issue.go index fefd75721f1..0c8df229241 100644 --- a/backend/plugins/sonarqube/models/sonarqube_issue.go +++ b/backend/plugins/sonarqube/models/sonarqube_issue.go @@ -50,3 +50,15 @@ type SonarqubeIssue struct { func (SonarqubeIssue) TableName() string { return "_tool_sonarqube_issues" } + +type SonarqubeIssueImpact struct { + ConnectionId uint64 `gorm:"primaryKey"` + IssueKey string `gorm:"primaryKey;type:varchar(100)"` + SoftwareQuality string `gorm:"primaryKey;type:varchar(255)"` + Severity string `gorm:"type:varchar(100)"` + common.NoPKModel +} + +func (SonarqubeIssueImpact) TableName() string { + return "_tool_sonarqube_issue_impacts" +} diff --git a/backend/plugins/sonarqube/tasks/issue_impacts_convertor.go b/backend/plugins/sonarqube/tasks/issue_impacts_convertor.go new file mode 100644 index 00000000000..be8ab451b7c --- /dev/null +++ b/backend/plugins/sonarqube/tasks/issue_impacts_convertor.go @@ -0,0 +1,75 @@ +/* +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You under the Apache License, Version 2.0 +(the "License"); you may not use this file except in compliance with +the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package tasks + +import ( + "reflect" + + "github.com/apache/incubator-devlake/core/dal" + "github.com/apache/incubator-devlake/core/errors" + "github.com/apache/incubator-devlake/core/models/domainlayer/codequality" + "github.com/apache/incubator-devlake/core/models/domainlayer/didgen" + "github.com/apache/incubator-devlake/core/plugin" + "github.com/apache/incubator-devlake/helpers/pluginhelper/api" + sonarqubeModels "github.com/apache/incubator-devlake/plugins/sonarqube/models" +) + +var ConvertIssueImpactsMeta = plugin.SubTaskMeta{ + Name: "convertIssueImpacts", + EntryPoint: ConvertIssueImpacts, + EnabledByDefault: true, + Description: "Convert tool layer table sonarqube_issue_impacts into domain layer table cq_issue_impacts", + DomainTypes: []string{plugin.DOMAIN_TYPE_CODE_QUALITY}, +} + +func ConvertIssueImpacts(taskCtx plugin.SubTaskContext) errors.Error { + db := taskCtx.GetDal() + rawDataSubTaskArgs, data := CreateRawDataSubTaskArgs(taskCtx, RAW_ISSUES_TABLE) + cursor, err := db.Cursor( + dal.From("_tool_sonarqube_issue_impacts p"), + dal.Join("LEFT JOIN _tool_sonarqube_issues i ON (i.connection_id = p.connection_id AND i.issue_key = p.issue_key)"), + dal.Where("i.connection_id = ? AND i.project_key = ?", data.Options.ConnectionId, data.Options.ProjectKey)) + if err != nil { + return err + } + defer cursor.Close() + + issueIdGen := didgen.NewDomainIdGenerator(&sonarqubeModels.SonarqubeIssue{}) + converter, err := api.NewDataConverter(api.DataConverterArgs{ + InputRowType: reflect.TypeOf(sonarqubeModels.SonarqubeIssueImpact{}), + Input: cursor, + RawDataSubTaskArgs: *rawDataSubTaskArgs, + Convert: func(inputRow interface{}) ([]interface{}, errors.Error) { + impact := inputRow.(*sonarqubeModels.SonarqubeIssueImpact) + domainIssueImpact := &codequality.CqIssueImpact{ + CqIssueId: issueIdGen.Generate(data.Options.ConnectionId, impact.IssueKey), + SoftwareQuality: impact.SoftwareQuality, + Severity: impact.Severity, + } + return []interface{}{ + domainIssueImpact, + }, nil + }, + }) + + if err != nil { + return err + } + + return converter.Execute() +} diff --git a/backend/plugins/sonarqube/tasks/issues_extractor.go b/backend/plugins/sonarqube/tasks/issues_extractor.go index 80c1193c917..1cd9a93c062 100644 --- a/backend/plugins/sonarqube/tasks/issues_extractor.go +++ b/backend/plugins/sonarqube/tasks/issues_extractor.go @@ -107,6 +107,16 @@ func ExtractIssues(taskCtx plugin.SubTaskContext) errors.Error { results = append(results, codeBlock) } } + + for _, v := range body.Impacts { + impact := &models.SonarqubeIssueImpact{ + ConnectionId: data.Options.ConnectionId, + IssueKey: sonarqubeIssue.IssueKey, + SoftwareQuality: v.SoftwareQuality, + Severity: v.Severity, + } + results = append(results, impact) + } return results, nil }, }) @@ -151,6 +161,10 @@ type IssuesResponse struct { Type string `json:"type"` Scope string `json:"scope"` QuickFixAvailable bool `json:"quickFixAvailable"` + Impacts []struct { + SoftwareQuality string `json:"softwareQuality"` + Severity string `json:"severity"` + } `json:"impacts"` } type flow struct { diff --git a/config-ui/src/plugins/components/connection-form/fields/endpoint.tsx b/config-ui/src/plugins/components/connection-form/fields/endpoint.tsx index 2dc3d3ed4e6..b719f21a5e0 100644 --- a/config-ui/src/plugins/components/connection-form/fields/endpoint.tsx +++ b/config-ui/src/plugins/components/connection-form/fields/endpoint.tsx @@ -29,6 +29,7 @@ interface Props { disabled?: boolean; name: string; multipleVersions?: Record; + cloudName?: string; initialValue: string; value: string; error: string; @@ -41,6 +42,7 @@ export const ConnectionEndpoint = ({ disabled = false, name, multipleVersions, + cloudName, initialValue, value, setValue, @@ -79,7 +81,7 @@ export const ConnectionEndpoint = ({ <> - {name} Cloud + {cloudName ? cloudName : `${name} Cloud`} {name} Server {multipleVersions.server ? multipleVersions.server : '(to be supported)'} diff --git a/config-ui/src/plugins/register/sonarqube/config.tsx b/config-ui/src/plugins/register/sonarqube/config.tsx index a2922a4eeda..60d1d483749 100644 --- a/config-ui/src/plugins/register/sonarqube/config.tsx +++ b/config-ui/src/plugins/register/sonarqube/config.tsx @@ -21,6 +21,8 @@ import { IPluginConfig } from '@/types'; import Icon from './assets/icon.svg?react'; +import { Organization } from './connection-fields/organization'; + export const SonarQubeConfig: IPluginConfig = { plugin: 'sonarqube', name: 'SonarQube', @@ -28,12 +30,31 @@ export const SonarQubeConfig: IPluginConfig = { sort: 11, connection: { docLink: DOC_URL.PLUGIN.SONARQUBE.BASIS, + initialValues: { + endpoint: 'https://sonarcloud.io/api/', + }, fields: [ 'name', { key: 'endpoint', - subLabel: 'Provide the SonarQube instance API endpoint. E.g. http://:/api/', + multipleVersions: { + cloud: 'https://sonarcloud.io/api/', + server: ' ', + }, + cloudName: 'SonarCloud', + subLabel: 'The URL should be `http://:/api/`', }, + ({ type, initialValues, values, errors, setValues, setErrors }: any) => ( + + ), 'token', 'proxy', { diff --git a/config-ui/src/plugins/register/sonarqube/connection-fields/organization.tsx b/config-ui/src/plugins/register/sonarqube/connection-fields/organization.tsx new file mode 100644 index 00000000000..4537ed1fd3e --- /dev/null +++ b/config-ui/src/plugins/register/sonarqube/connection-fields/organization.tsx @@ -0,0 +1,71 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import { Input } from 'antd'; + +import { Block, ExternalLink } from '@/components'; +import { useEffect } from 'react'; + +interface Props { + type: 'create' | 'update'; + initialValues: any; + values: any; + errors: any; + setValues: (value: any) => void; + setErrors: (value: any) => void; +} + +export const Organization = ({ initialValues, values, setValues, setErrors }: Props) => { + const { endpoint } = values; + + useEffect(() => { + setValues({ org: initialValues.org }); + }, [initialValues.org]); + + useEffect(() => { + setErrors({ + org: values.org ? '' : 'organization is required', + }); + }, [values.org]); + + const handleChange = (e: React.ChangeEvent) => { + setValues({ + org: e.target.value, + }); + }; + + if (endpoint !== 'https://sonarcloud.io/api/') { + return null; + } + + return ( + + Copy the organization key at{' '} + here. If you have more than + one, please create another connection. + + } + required + > + + + ); +}; diff --git a/grafana/dashboards/SonarQubeCloud.json b/grafana/dashboards/SonarQubeCloud.json new file mode 100644 index 00000000000..1a290703897 --- /dev/null +++ b/grafana/dashboards/SonarQubeCloud.json @@ -0,0 +1,1503 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 40, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "gridPos": { + "h": 4, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 19, + "links": [ + { + "targetBlank": true, + "title": "SonarQube", + "url": "https://devlake.apache.org/docs/Plugins/sonarqube" + } + ], + "options": { + "code": { + "language": "plaintext", + "showLineNumbers": false, + "showMiniMap": false + }, + "content": "- Use Cases: This dashboard shows the code quality metrics from SonarCloud.\n- Data Source Required: SonarCloud\n- This dashboard does not honor the time filter on the top-right side as SonarQube metrics are all from the latest scan.", + "mode": "markdown" + }, + "pluginVersion": "11.2.0", + "targets": [ + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "queryType": "randomWalk", + "refId": "A" + } + ], + "title": "Dashboard Introduction", + "type": "text" + }, + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 4 + }, + "id": 16, + "targets": [ + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "refId": "A" + } + ], + "title": "Software Quality", + "type": "row" + }, + { + "datasource": "mysql", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 8, + "x": 0, + "y": 5 + }, + "id": 3, + "links": [ + { + "targetBlank": true, + "title": "Code Quality Issue Count", + "url": "https://devlake.apache.org/docs/Metrics/CQIssueCount" + } + ], + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "text": {}, + "textMode": "value", + "wideLayout": true + }, + "pluginVersion": "11.2.0", + "targets": [ + { + "dataset": "lake", + "datasource": "mysql", + "editorMode": "code", + "format": "table", + "rawQuery": true, + "rawSql": "SELECT\n count(distinct ci.id)\nFROM \n cq_issues ci\n join cq_issue_impacts cii on ci.id = cii.cq_issue_id\nWHERE\n ci.project_key in (${project_id})\n and cii.software_quality = 'SECURITY'\n and ci.severity in (${severity})", + "refId": "A", + "sql": { + "columns": [ + { + "parameters": [], + "type": "function" + } + ], + "groupBy": [ + { + "property": { + "type": "string" + }, + "type": "groupBy" + } + ], + "limit": 50 + } + } + ], + "title": "Security", + "type": "stat" + }, + { + "datasource": "mysql", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 8, + "x": 8, + "y": 5 + }, + "id": 2, + "links": [ + { + "targetBlank": true, + "title": "Code Quality Issue Count", + "url": "https://devlake.apache.org/docs/Metrics/CQIssueCount" + } + ], + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "center", + "orientation": "vertical", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "/.*/", + "values": false + }, + "showPercentChange": false, + "text": {}, + "textMode": "value", + "wideLayout": true + }, + "pluginVersion": "11.2.0", + "targets": [ + { + "dataset": "lake", + "datasource": "mysql", + "editorMode": "code", + "format": "table", + "rawQuery": true, + "rawSql": "SELECT\n count(distinct ci.id)\nFROM \n cq_issues ci\n join cq_issue_impacts cii on ci.id = cii.cq_issue_id\nWHERE\n ci.project_key in (${project_id})\n and cii.software_quality = 'RELIABILITY'\n and ci.severity in (${severity})", + "refId": "A", + "sql": { + "columns": [ + { + "parameters": [], + "type": "function" + } + ], + "groupBy": [ + { + "property": { + "type": "string" + }, + "type": "groupBy" + } + ], + "limit": 50 + } + } + ], + "title": "Reliability", + "type": "stat" + }, + { + "datasource": "mysql", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 8, + "x": 16, + "y": 5 + }, + "id": 20, + "links": [ + { + "targetBlank": true, + "title": "Code Quality Issue Count", + "url": "https://devlake.apache.org/docs/Metrics/CQIssueCount" + } + ], + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "center", + "orientation": "vertical", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "/.*/", + "values": false + }, + "showPercentChange": false, + "text": {}, + "textMode": "value", + "wideLayout": true + }, + "pluginVersion": "11.2.0", + "targets": [ + { + "dataset": "lake", + "datasource": "mysql", + "editorMode": "code", + "format": "table", + "rawQuery": true, + "rawSql": "SELECT\n count(distinct ci.id)\nFROM \n cq_issues ci\n join cq_issue_impacts cii on ci.id = cii.cq_issue_id\nWHERE\n ci.project_key in (${project_id})\n and cii.software_quality = 'MAINTAINABILITY'\n and ci.severity in (${severity})", + "refId": "A", + "sql": { + "columns": [ + { + "parameters": [], + "type": "function" + } + ], + "groupBy": [ + { + "property": { + "type": "string" + }, + "type": "groupBy" + } + ], + "limit": 50 + } + } + ], + "title": "Maintainability", + "type": "stat" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 10 + }, + "id": 21, + "panels": [], + "title": "Security Review", + "type": "row" + }, + { + "datasource": "mysql", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 8, + "x": 0, + "y": 11 + }, + "id": 4, + "links": [ + { + "targetBlank": true, + "title": "Code Quality Issue Count", + "url": "https://devlake.apache.org/docs/Metrics/CQIssueCount" + } + ], + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "horizontal", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "/.*/", + "values": false + }, + "showPercentChange": false, + "text": {}, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.2.0", + "targets": [ + { + "dataset": "lake", + "datasource": "mysql", + "editorMode": "code", + "format": "table", + "rawQuery": true, + "rawSql": "SELECT\n COUNT(distinct id) AS 'Security Hotspots'\nFROM cq_issues\nWHERE\n project_key in (${project_id})\n and type = 'HOTSPOTS'\n and severity in (${severity})\n", + "refId": "A", + "sql": { + "columns": [ + { + "parameters": [], + "type": "function" + } + ], + "groupBy": [ + { + "property": { + "type": "string" + }, + "type": "groupBy" + } + ], + "limit": 50 + } + } + ], + "title": "Security Hotspots", + "type": "stat" + }, + { + "datasource": "mysql", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 8, + "x": 8, + "y": 11 + }, + "id": 13, + "links": [ + { + "targetBlank": true, + "title": "Code Quality Issue Count", + "url": "https://devlake.apache.org/docs/Metrics/CQIssueCount" + } + ], + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "horizontal", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "/.*/", + "values": false + }, + "showPercentChange": false, + "text": {}, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.2.0", + "targets": [ + { + "dataset": "lake", + "datasource": "mysql", + "editorMode": "code", + "format": "table", + "rawQuery": true, + "rawSql": "SELECT\n CONCAT(ROUND(COUNT(IF(status != 'TO_REVIEW', id, NULL)) / COUNT(distinct id) * 100, 2), '%') AS 'Reviewed'\nFROM cq_issues\nWHERE\n project_key in (${project_id})\n and type = 'HOTSPOTS'\n and severity in (${severity})", + "refId": "A", + "sql": { + "columns": [ + { + "parameters": [], + "type": "function" + } + ], + "groupBy": [ + { + "property": { + "type": "string" + }, + "type": "groupBy" + } + ], + "limit": 50 + } + } + ], + "title": "Hotspots Reviewed", + "type": "stat" + }, + { + "collapsed": false, + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 16 + }, + "id": 12, + "panels": [], + "targets": [ + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "refId": "A" + } + ], + "title": "Test & Maintainability", + "type": "row" + }, + { + "datasource": "mysql", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 8, + "x": 0, + "y": 17 + }, + "id": 8, + "links": [ + { + "targetBlank": true, + "title": "Code Quality Test", + "url": "https://devlake.apache.org/docs/Metrics/CQTest" + } + ], + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "/.*/", + "values": false + }, + "showPercentChange": false, + "text": {}, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.2.0", + "targets": [ + { + "dataset": "lake", + "datasource": "mysql", + "editorMode": "code", + "format": "table", + "rawQuery": true, + "rawSql": "SELECT\n CONCAT(ROUND((sum(lines_to_cover) - sum(uncovered_lines)) / sum(lines_to_cover) * 100, 1), '% ', 'Coverage on ', ROUND(sum(lines_to_cover) / 1000, 0),'k Lines to cover')\nFROM cq_file_metrics\nWHERE\n project_key in (${project_id})\n", + "refId": "A", + "sql": { + "columns": [ + { + "parameters": [], + "type": "function" + } + ], + "groupBy": [ + { + "property": { + "type": "string" + }, + "type": "groupBy" + } + ], + "limit": 50 + } + } + ], + "title": "Test Coverage", + "type": "stat" + }, + { + "datasource": "mysql", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "code smells" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 8, + "x": 8, + "y": 17 + }, + "id": 14, + "links": [ + { + "targetBlank": true, + "title": "Code Quality Maintainability-Debt", + "url": "https://devlake.apache.org/docs/Metrics/CQMaintainability-Debt" + } + ], + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "horizontal", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "/.*/", + "values": false + }, + "showPercentChange": false, + "text": {}, + "textMode": "value", + "wideLayout": true + }, + "pluginVersion": "11.2.0", + "targets": [ + { + "dataset": "lake", + "datasource": "mysql", + "editorMode": "code", + "format": "table", + "rawQuery": true, + "rawSql": "SELECT\n\tCOUNT(distinct id) as 'Code Smells'\nFROM cq_issues\nWHERE\n project_key in (${project_id})\n and type = 'CODE_SMELL'\n and severity in (${severity})", + "refId": "A", + "sql": { + "columns": [ + { + "parameters": [], + "type": "function" + } + ], + "groupBy": [ + { + "property": { + "type": "string" + }, + "type": "groupBy" + } + ], + "limit": 50 + } + } + ], + "title": "Maintainability - Code Smells", + "type": "stat" + }, + { + "datasource": "mysql", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "string" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 8, + "x": 16, + "y": 17 + }, + "id": 7, + "links": [ + { + "targetBlank": true, + "title": "Code Quality Maintainability-Debt", + "url": "https://devlake.apache.org/docs/Metrics/CQMaintainability-Debt" + } + ], + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "horizontal", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "/.*/", + "values": false + }, + "showPercentChange": false, + "text": {}, + "textMode": "value", + "wideLayout": true + }, + "pluginVersion": "11.2.0", + "targets": [ + { + "dataset": "lake", + "datasource": "mysql", + "editorMode": "code", + "format": "table", + "rawQuery": true, + "rawSql": "SELECT\n concat(FLOOR(SUM(debt)/8/60), \" day(s) \", FLOOR((SUM(debt)%480)/60), \" hour(s) \") AS 'Debt'\nFROM cq_issues\nWHERE\n project_key in (${project_id})\n and type = 'CODE_SMELL'\n and severity in (${severity})", + "refId": "A", + "sql": { + "columns": [ + { + "parameters": [], + "type": "function" + } + ], + "groupBy": [ + { + "property": { + "type": "string" + }, + "type": "groupBy" + } + ], + "limit": 50 + } + } + ], + "title": "Maintainability - Debt", + "type": "stat" + }, + { + "collapsed": false, + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 22 + }, + "id": 6, + "panels": [], + "targets": [ + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "refId": "A" + } + ], + "title": "Duplications", + "type": "row" + }, + { + "datasource": "mysql", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 8, + "x": 0, + "y": 23 + }, + "id": 10, + "links": [ + { + "targetBlank": true, + "title": "Code Quality Duplicated Blocks", + "url": "https://devlake.apache.org/docs/Metrics/CQDuplicatedBlocks" + } + ], + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "horizontal", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "/.*/", + "values": false + }, + "showPercentChange": false, + "text": {}, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.2.0", + "targets": [ + { + "dataset": "lake", + "datasource": "mysql", + "editorMode": "code", + "format": "table", + "rawQuery": true, + "rawSql": "SELECT\n sum(duplicated_blocks)\nFROM cq_file_metrics\nWHERE\n project_key in (${project_id})", + "refId": "A", + "sql": { + "columns": [ + { + "parameters": [], + "type": "function" + } + ], + "groupBy": [ + { + "property": { + "type": "string" + }, + "type": "groupBy" + } + ], + "limit": 50 + } + } + ], + "title": "Duplicated Blocks", + "type": "stat" + }, + { + "datasource": "mysql", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 8, + "x": 8, + "y": 23 + }, + "id": 9, + "links": [ + { + "targetBlank": true, + "title": "Code Quality Duplicated Lines", + "url": "https://devlake.apache.org/docs/Metrics/CQDuplicatedLines" + } + ], + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "/.*/", + "values": false + }, + "showPercentChange": false, + "text": {}, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.2.0", + "targets": [ + { + "dataset": "lake", + "datasource": "mysql", + "editorMode": "code", + "format": "table", + "rawQuery": true, + "rawSql": "SELECT\n CONCAT(ROUND(sum(duplicated_lines) / sum(num_of_lines) * 100, 1), '% ', 'Duplications on ', ROUND(sum(ncloc) / 1000, 0),'k Lines')\nFROM cq_file_metrics\nWHERE\n project_key in (${project_id})", + "refId": "A", + "sql": { + "columns": [ + { + "parameters": [], + "type": "function" + } + ], + "groupBy": [ + { + "property": { + "type": "string" + }, + "type": "groupBy" + } + ], + "limit": 50 + } + } + ], + "title": "Duplicated Lines", + "type": "stat" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 28 + }, + "id": 22, + "panels": [], + "title": "Size", + "type": "row" + }, + { + "datasource": "mysql", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 8, + "x": 0, + "y": 29 + }, + "id": 23, + "links": [ + { + "targetBlank": true, + "title": "Code Quality Duplicated Blocks", + "url": "https://devlake.apache.org/docs/Metrics/CQDuplicatedBlocks" + } + ], + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "horizontal", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "/.*/", + "values": false + }, + "showPercentChange": false, + "text": {}, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.2.0", + "targets": [ + { + "dataset": "lake", + "datasource": "mysql", + "editorMode": "code", + "format": "table", + "rawQuery": true, + "rawSql": "SELECT\n sum(ncloc)\nFROM cq_file_metrics\nWHERE\n project_key in (${project_id})", + "refId": "A", + "sql": { + "columns": [ + { + "parameters": [], + "type": "function" + } + ], + "groupBy": [ + { + "property": { + "type": "string" + }, + "type": "groupBy" + } + ], + "limit": 50 + } + } + ], + "title": "Lines of Code", + "type": "stat" + }, + { + "datasource": "mysql", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 8, + "x": 8, + "y": 29 + }, + "id": 24, + "links": [ + { + "targetBlank": true, + "title": "Code Quality Duplicated Blocks", + "url": "https://devlake.apache.org/docs/Metrics/CQDuplicatedBlocks" + } + ], + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "horizontal", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "/.*/", + "values": false + }, + "showPercentChange": false, + "text": {}, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.2.0", + "targets": [ + { + "dataset": "lake", + "datasource": "mysql", + "editorMode": "code", + "format": "table", + "rawQuery": true, + "rawSql": "SELECT\n sum(num_of_lines)\nFROM cq_file_metrics\nWHERE\n project_key in (${project_id})", + "refId": "A", + "sql": { + "columns": [ + { + "parameters": [], + "type": "function" + } + ], + "groupBy": [ + { + "property": { + "type": "string" + }, + "type": "groupBy" + } + ], + "limit": 50 + } + } + ], + "title": "Lines", + "type": "stat" + }, + { + "datasource": "mysql", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 8, + "x": 16, + "y": 29 + }, + "id": 25, + "links": [ + { + "targetBlank": true, + "title": "Code Quality Duplicated Blocks", + "url": "https://devlake.apache.org/docs/Metrics/CQDuplicatedBlocks" + } + ], + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "horizontal", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "/.*/", + "values": false + }, + "showPercentChange": false, + "text": {}, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.2.0", + "targets": [ + { + "dataset": "lake", + "datasource": "mysql", + "editorMode": "code", + "format": "table", + "rawQuery": true, + "rawSql": "SELECT\n count(distinct file_path)\nFROM cq_file_metrics\nWHERE\n project_key in (${project_id})", + "refId": "A", + "sql": { + "columns": [ + { + "parameters": [], + "type": "function" + } + ], + "groupBy": [ + { + "property": { + "type": "string" + }, + "type": "groupBy" + } + ], + "limit": 50 + } + } + ], + "title": "Files", + "type": "stat" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 34 + }, + "id": 26, + "panels": [], + "title": "Overall Code Quality Metrics", + "type": "row" + }, + { + "datasource": "mysql", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "color-text" + }, + "filterable": false, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 24, + "x": 0, + "y": 35 + }, + "id": 17, + "links": [ + { + "targetBlank": true, + "title": "Code Quality Issue Count", + "url": "https://devlake.apache.org/docs/Metrics/CQIssueCount" + } + ], + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "11.2.0", + "targets": [ + { + "dataset": "lake", + "datasource": "mysql", + "editorMode": "code", + "format": "table", + "rawQuery": true, + "rawSql": "SELECT\n\tfile_name, num_of_lines as 'Lines of Code', bugs as 'Bugs', vulnerabilities as 'Vulnerabilities', code_smells as 'Code Smells', \n\tsecurity_hotspots as 'Security Hotspots', CONCAT(ROUND(coverage, 2), '%') as 'Coverage', CONCAT(ROUND(duplicated_lines_density, 2), '%') as 'Duplications'\nFROM cq_file_metrics\nWHERE\n project_key in (${project_id})\nORDER BY bugs desc\nlimit 20", + "refId": "A", + "sql": { + "columns": [ + { + "parameters": [], + "type": "function" + } + ], + "groupBy": [ + { + "property": { + "type": "string" + }, + "type": "groupBy" + } + ], + "limit": 50 + } + } + ], + "title": "Code Quality Metrics by Files (Top 20 order by Bugs)", + "type": "table" + } + ], + "refresh": "", + "schemaVersion": 39, + "tags": [ + "Data Source Dashboard", + "Stable Data Sources" + ], + "templating": { + "list": [ + { + "current": { + "selected": true, + "text": [ + "All" + ], + "value": [ + "$__all" + ] + }, + "datasource": "mysql", + "definition": "select concat(name, '--', id) as text from cq_projects", + "hide": 0, + "includeAll": true, + "label": "SonarQube Project", + "multi": true, + "name": "project_id", + "options": [], + "query": "select concat(name, '--', id) as text from cq_projects", + "refresh": 1, + "regex": "/^(?.*)--(?.*)$/", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "current": { + "selected": true, + "text": [ + "All" + ], + "value": [ + "$__all" + ] + }, + "datasource": "mysql", + "definition": "select distinct severity from cq_issues where id like 'sonar%'", + "hide": 0, + "includeAll": true, + "label": "Severity", + "multi": true, + "name": "severity", + "options": [], + "query": "select distinct severity from cq_issues where id like 'sonar%'", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + } + ] + }, + "time": { + "from": "now", + "to": "now" + }, + "timepicker": {}, + "timezone": "utc", + "title": "SonarQube Cloud", + "uid": "WA0qbuJ4l", + "version": 1, + "weekStart": "" +} \ No newline at end of file