You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@devlake.apache.org by ab...@apache.org on 2023/02/22 10:38:35 UTC

[incubator-devlake] branch main updated: feat(sonarqube): change field type and add unit test (#4484)

This is an automated email from the ASF dual-hosted git repository.

abeizn pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-devlake.git


The following commit(s) were added to refs/heads/main by this push:
     new 99bffed9a feat(sonarqube): change field type and add unit test (#4484)
99bffed9a is described below

commit 99bffed9a22f2c0b5aa0ebd038a3f0d46e15e8c9
Author: Warren Chen <yi...@merico.dev>
AuthorDate: Wed Feb 22 18:38:30 2023 +0800

    feat(sonarqube): change field type and add unit test (#4484)
---
 .../domainlayer/codequality/cq_file_metrics.go     |  14 +--
 .../models/domainlayer/codequality/cq_issues.go    |   4 +-
 .../migrationscripts/20230208_add_code_quality.go  |  11 ++-
 .../migrationscripts/archived/cq_file_metrics.go   |  14 +--
 .../models/migrationscripts/archived/cq_issues.go  |   4 +-
 .../_tool_sonarqube_filemetrics.csv                |  16 ++--
 .../e2e/snapshot_tables/_tool_sonarqube_issues.csv |  12 +--
 .../sonarqube/e2e/snapshot_tables/filemetrics.csv  |  12 +--
 .../e2e/snapshot_tables/issue_hotspots.csv         |   6 +-
 .../sonarqube/e2e/snapshot_tables/issues.csv       |   6 +-
 .../migrationscripts/20230111_add_init_tables.go   |  13 ++-
 .../archived/sonarqube_file_metrics.go             |   4 +-
 .../migrationscripts/archived/sonarqube_issue.go   |   4 +-
 .../sonarqube/models/sonarqube_file_metrics.go     |   4 +-
 .../plugins/sonarqube/models/sonarqube_issue.go    |   4 +-
 .../sonarqube/tasks/filemetrics_extractor.go       |  10 +-
 .../plugins/sonarqube/tasks/issues_extractor.go    |  23 ++---
 backend/plugins/sonarqube/tasks/shared.go          |  42 +++++++++
 backend/plugins/sonarqube/tasks/shared_test.go     | 105 +++++++++++++++++++++
 19 files changed, 239 insertions(+), 69 deletions(-)

diff --git a/backend/core/models/domainlayer/codequality/cq_file_metrics.go b/backend/core/models/domainlayer/codequality/cq_file_metrics.go
index 7a2801c0a..97416c17b 100644
--- a/backend/core/models/domainlayer/codequality/cq_file_metrics.go
+++ b/backend/core/models/domainlayer/codequality/cq_file_metrics.go
@@ -23,13 +23,13 @@ import (
 
 type CqFileMetrics struct {
 	domainlayer.DomainEntity
-	ProjectKey               string  `gorm:"index;type:varchar(255)"` //domain project key
-	FileName                 string  `json:"file_name"`
-	FilePath                 string  `json:"file_path"`
-	FileLanguage             string  `json:"file_language"`
-	CodeSmells               int     `json:"code_smells"`
-	SqaleIndex               string  `json:"sqale_index"`
-	SqaleRating              string  `json:"sqale_rating"`
+	ProjectKey               string `gorm:"index;type:varchar(255)"` //domain project key
+	FileName                 string `json:"file_name"`
+	FilePath                 string `json:"file_path"`
+	FileLanguage             string `json:"file_language"`
+	CodeSmells               int    `json:"code_smells"`
+	SqaleIndex               int
+	SqaleRating              float64
 	Bugs                     int     `json:"bugs"`
 	ReliabilityRating        string  `json:"reliability_rating"`
 	Vulnerabilities          int     `json:"vulnerabilities"`
diff --git a/backend/core/models/domainlayer/codequality/cq_issues.go b/backend/core/models/domainlayer/codequality/cq_issues.go
index 2d658e724..bdec07115 100644
--- a/backend/core/models/domainlayer/codequality/cq_issues.go
+++ b/backend/core/models/domainlayer/codequality/cq_issues.go
@@ -31,8 +31,8 @@ type CqIssue struct {
 	Line                     int              `json:"line"`
 	Status                   string           `json:"status" gorm:"type:varchar(255)"`
 	Message                  string           `json:"message"`
-	Debt                     string           `json:"debt" gorm:"type:varchar(255)"`
-	Effort                   string           `json:"effort" gorm:"type:varchar(255)"`
+	Debt                     int              `json:"debt"`
+	Effort                   int              `json:"effort"`
 	CommitAuthorEmail        string           `json:"author" gorm:"type:varchar(255)"`
 	Assignee                 string           `json:"assignee" gorm:"type:varchar(255)"`
 	Hash                     string           `json:"hash" gorm:"type:varchar(255)"`
diff --git a/backend/core/models/migrationscripts/20230208_add_code_quality.go b/backend/core/models/migrationscripts/20230208_add_code_quality.go
index a1d23986b..a60e5bb42 100644
--- a/backend/core/models/migrationscripts/20230208_add_code_quality.go
+++ b/backend/core/models/migrationscripts/20230208_add_code_quality.go
@@ -27,6 +27,15 @@ import (
 type addCodeQuality struct{}
 
 func (u *addCodeQuality) Up(basicRes context.BasicRes) errors.Error {
+	err := basicRes.GetDal().DropTables(
+		&archived.CqProject{},
+		&archived.CqIssue{},
+		&archived.CqIssueCodeBlock{},
+		&archived.CqFileMetrics{},
+	)
+	if err != nil {
+		return err
+	}
 	return migrationhelper.AutoMigrateTables(
 		basicRes,
 		&archived.CqProject{},
@@ -37,7 +46,7 @@ func (u *addCodeQuality) Up(basicRes context.BasicRes) errors.Error {
 }
 
 func (*addCodeQuality) Version() uint64 {
-	return 20230208000019
+	return 20230221000022
 }
 
 func (*addCodeQuality) Name() string {
diff --git a/backend/core/models/migrationscripts/archived/cq_file_metrics.go b/backend/core/models/migrationscripts/archived/cq_file_metrics.go
index de33a8b77..399311502 100644
--- a/backend/core/models/migrationscripts/archived/cq_file_metrics.go
+++ b/backend/core/models/migrationscripts/archived/cq_file_metrics.go
@@ -19,13 +19,13 @@ package archived
 
 type CqFileMetrics struct {
 	DomainEntity
-	ProjectKey               string  `gorm:"index;type:varchar(255)"` //domain project key
-	FileName                 string  `json:"file_name"`
-	FilePath                 string  `json:"file_path"`
-	FileLanguage             string  `json:"file_language"`
-	CodeSmells               int     `json:"code_smells"`
-	SqaleIndex               string  `json:"sqale_index"`
-	SqaleRating              string  `json:"sqale_rating"`
+	ProjectKey               string `gorm:"index;type:varchar(255)"` //domain project key
+	FileName                 string `json:"file_name"`
+	FilePath                 string `json:"file_path"`
+	FileLanguage             string `json:"file_language"`
+	CodeSmells               int    `json:"code_smells"`
+	SqaleIndex               int
+	SqaleRating              float64
 	Bugs                     int     `json:"bugs"`
 	ReliabilityRating        string  `json:"reliability_rating"`
 	Vulnerabilities          int     `json:"vulnerabilities"`
diff --git a/backend/core/models/migrationscripts/archived/cq_issues.go b/backend/core/models/migrationscripts/archived/cq_issues.go
index e548ffb94..8652137eb 100644
--- a/backend/core/models/migrationscripts/archived/cq_issues.go
+++ b/backend/core/models/migrationscripts/archived/cq_issues.go
@@ -30,8 +30,8 @@ type CqIssue struct {
 	Line                     int              `json:"line"`
 	Status                   string           `json:"status" gorm:"type:varchar(255)"`
 	Message                  string           `json:"message"`
-	Debt                     string           `json:"debt" gorm:"type:varchar(255)"`
-	Effort                   string           `json:"effort" gorm:"type:varchar(255)"`
+	Debt                     int              `json:"debt"`
+	Effort                   int              `json:"effort"`
 	CommitAuthorEmail        string           `json:"author" gorm:"type:varchar(255)"`
 	Assignee                 string           `json:"assignee" gorm:"type:varchar(255)"`
 	Hash                     string           `json:"hash" gorm:"type:varchar(255)"`
diff --git a/backend/plugins/sonarqube/e2e/snapshot_tables/_tool_sonarqube_filemetrics.csv b/backend/plugins/sonarqube/e2e/snapshot_tables/_tool_sonarqube_filemetrics.csv
index 7efe88a5f..9faef947d 100644
--- a/backend/plugins/sonarqube/e2e/snapshot_tables/_tool_sonarqube_filemetrics.csv
+++ b/backend/plugins/sonarqube/e2e/snapshot_tables/_tool_sonarqube_filemetrics.csv
@@ -1,9 +1,9 @@
 connection_id,file_metrics_key,project_key,file_name,file_path,file_language,code_smells,sqale_index,sqale_rating,bugs,reliability_rating,vulnerabilities,security_rating,security_hotspots,security_hotspots_reviewed,security_review_rating,ncloc,coverage,lines_to_cover,duplicated_lines_density,duplicated_blocks
-1,02c1047b-f87c-4c35-a6b5-76c6b607d37f:db/migrate/20170206220334_add_start_date_to_sponsors_and_paid.rb,02c1047b-f87c-4c35-a6b5-76c6b607d37f,20170206220334_add_start_date_to_sponsors_and_paid.rb,db/migrate/20170206220334_add_start_date_to_sponsors_and_paid.rb,ruby,0,0,1.0,0,A,0,A,0,0,A,6,0,2,0,0
-1,02c1047b-f87c-4c35-a6b5-76c6b607d37f:db/migrate/20170208152018_acts_as_follower_migration.rb,02c1047b-f87c-4c35-a6b5-76c6b607d37f,20170208152018_acts_as_follower_migration.rb,db/migrate/20170208152018_acts_as_follower_migration.rb,ruby,0,0,1.0,0,A,0,A,0,0,A,15,0,8,0,0
-1,02c1047b-f87c-4c35-a6b5-76c6b607d37f:db/migrate/20170209164016_add_email_follower_notifications.rb,02c1047b-f87c-4c35-a6b5-76c6b607d37f,20170209164016_add_email_follower_notifications.rb,db/migrate/20170209164016_add_email_follower_notifications.rb,ruby,0,0,1.0,0,A,0,A,0,0,A,5,0,1,0,0
-1,02c1047b-f87c-4c35-a6b5-76c6b607d37f:db/migrate/20170213183337_add_sign_up_information_to_users.rb,02c1047b-f87c-4c35-a6b5-76c6b607d37f,20170213183337_add_sign_up_information_to_users.rb,db/migrate/20170213183337_add_sign_up_information_to_users.rb,ruby,0,0,1.0,0,A,0,A,0,0,A,7,0,3,0,0
-1,02c1047b-f87c-4c35-a6b5-76c6b607d37f:db/migrate/20170216145500_add_location_fields_etc_to_users.rb,02c1047b-f87c-4c35-a6b5-76c6b607d37f,20170216145500_add_location_fields_etc_to_users.rb,db/migrate/20170216145500_add_location_fields_etc_to_users.rb,ruby,0,0,1.0,0,A,0,A,0,0,A,20,0,16,0,0
-1,02c1047b-f87c-4c35-a6b5-76c6b607d37f:db/migrate/20170228174838_add_identity_data_to_users.rb,02c1047b-f87c-4c35-a6b5-76c6b607d37f,20170228174838_add_identity_data_to_users.rb,db/migrate/20170228174838_add_identity_data_to_users.rb,ruby,0,0,1.0,0,A,0,A,0,0,A,9,0,5,0,0
-2,e2c6d5e9-a321-4e8c-b322-03d9599ef962:db/migrate/20170302152930_add_attributes_to_tags.rb,e2c6d5e9-a321-4e8c-b322-03d9599ef962,20170302152930_add_attributes_to_tags.rb,db/migrate/20170302152930_add_attributes_to_tags.rb,ruby,0,0,1.0,0,A,0,A,0,0,A,16,0,12,0,0
-2,e2c6d5e9-a321-4e8c-b322-03d9599ef962:db/migrate/20170303171502_add_social_image_to_tags.rb,e2c6d5e9-a321-4e8c-b322-03d9599ef962,20170303171502_add_social_image_to_tags.rb,db/migrate/20170303171502_add_social_image_to_tags.rb,ruby,0,0,1.0,0,A,0,A,0,0,A,5,0,1,0,0
+1,02c1047b-f87c-4c35-a6b5-76c6b607d37f:db/migrate/20170206220334_add_start_date_to_sponsors_and_paid.rb,02c1047b-f87c-4c35-a6b5-76c6b607d37f,20170206220334_add_start_date_to_sponsors_and_paid.rb,db/migrate/20170206220334_add_start_date_to_sponsors_and_paid.rb,ruby,0,0,1,0,A,0,A,0,0,A,6,0,2,0,0
+1,02c1047b-f87c-4c35-a6b5-76c6b607d37f:db/migrate/20170208152018_acts_as_follower_migration.rb,02c1047b-f87c-4c35-a6b5-76c6b607d37f,20170208152018_acts_as_follower_migration.rb,db/migrate/20170208152018_acts_as_follower_migration.rb,ruby,0,0,1,0,A,0,A,0,0,A,15,0,8,0,0
+1,02c1047b-f87c-4c35-a6b5-76c6b607d37f:db/migrate/20170209164016_add_email_follower_notifications.rb,02c1047b-f87c-4c35-a6b5-76c6b607d37f,20170209164016_add_email_follower_notifications.rb,db/migrate/20170209164016_add_email_follower_notifications.rb,ruby,0,0,1,0,A,0,A,0,0,A,5,0,1,0,0
+1,02c1047b-f87c-4c35-a6b5-76c6b607d37f:db/migrate/20170213183337_add_sign_up_information_to_users.rb,02c1047b-f87c-4c35-a6b5-76c6b607d37f,20170213183337_add_sign_up_information_to_users.rb,db/migrate/20170213183337_add_sign_up_information_to_users.rb,ruby,0,0,1,0,A,0,A,0,0,A,7,0,3,0,0
+1,02c1047b-f87c-4c35-a6b5-76c6b607d37f:db/migrate/20170216145500_add_location_fields_etc_to_users.rb,02c1047b-f87c-4c35-a6b5-76c6b607d37f,20170216145500_add_location_fields_etc_to_users.rb,db/migrate/20170216145500_add_location_fields_etc_to_users.rb,ruby,0,0,1,0,A,0,A,0,0,A,20,0,16,0,0
+1,02c1047b-f87c-4c35-a6b5-76c6b607d37f:db/migrate/20170228174838_add_identity_data_to_users.rb,02c1047b-f87c-4c35-a6b5-76c6b607d37f,20170228174838_add_identity_data_to_users.rb,db/migrate/20170228174838_add_identity_data_to_users.rb,ruby,0,0,1,0,A,0,A,0,0,A,9,0,5,0,0
+2,e2c6d5e9-a321-4e8c-b322-03d9599ef962:db/migrate/20170302152930_add_attributes_to_tags.rb,e2c6d5e9-a321-4e8c-b322-03d9599ef962,20170302152930_add_attributes_to_tags.rb,db/migrate/20170302152930_add_attributes_to_tags.rb,ruby,0,0,1,0,A,0,A,0,0,A,16,0,12,0,0
+2,e2c6d5e9-a321-4e8c-b322-03d9599ef962:db/migrate/20170303171502_add_social_image_to_tags.rb,e2c6d5e9-a321-4e8c-b322-03d9599ef962,20170303171502_add_social_image_to_tags.rb,db/migrate/20170303171502_add_social_image_to_tags.rb,ruby,0,0,1,0,A,0,A,0,0,A,5,0,1,0,0
diff --git a/backend/plugins/sonarqube/e2e/snapshot_tables/_tool_sonarqube_issues.csv b/backend/plugins/sonarqube/e2e/snapshot_tables/_tool_sonarqube_issues.csv
index 2b5680e4f..9eca2e89b 100644
--- a/backend/plugins/sonarqube/e2e/snapshot_tables/_tool_sonarqube_issues.csv
+++ b/backend/plugins/sonarqube/e2e/snapshot_tables/_tool_sonarqube_issues.csv
@@ -1,7 +1,7 @@
 connection_id,issue_key,rule,severity,component,project_key,line,status,message,debt,effort,author,hash,tags,type,scope,start_line,end_line,start_offset,end_offset,creation_date,update_date
-1,AYUwBbCC46XwcL-YZOTH,java:S5993,MAJOR,f5a50c63-2e8f-4107-9014-853f6f467757:core/src/main/java/com/airbnb/aerosolve/core/util/FeatureDictionary.java,f5a50c63-2e8f-4107-9014-853f6f467757,24,OPEN,"Change the visibility of this constructor to ""protected"".",2min,2min,hector.yee@airbnb.com,cb21cfee164b0548717edfe840aea8a2,design,CODE_SMELL,MAIN,24,24,2,8,2015-05-12T19:22:15.000+00:00,2022-12-20T14:50:30.000+00:00
-1,AYUwBbGD46XwcL-YZOUu,java:S3824,MAJOR,f5a50c63-2e8f-4107-9014-853f6f467757:core/src/main/java/com/airbnb/aerosolve/core/util/Util.java,f5a50c63-2e8f-4107-9014-853f6f467757,171,OPEN,"Replace this ""Map.get()"" and condition with a call to ""Map.computeIfAbsent()"".",10min,10min,hector.yee@airbnb.com,136741d9bacea2c123cc5a81be33aa4b,java8,CODE_SMELL,MAIN,171,171,25,49,2015-05-12T19:22:15.000+00:00,2022-12-20T14:50:30.000+00:00
-1,AYUwBbGD46XwcL-YZOUv,java:S3824,MAJOR,f5a50c63-2e8f-4107-9014-853f6f467757:core/src/main/java/com/airbnb/aerosolve/core/util/Util.java,f5a50c63-2e8f-4107-9014-853f6f467757,182,OPEN,"Replace this ""Map.get()"" and condition with a call to ""Map.computeIfAbsent()"".",10min,10min,hector.yee@airbnb.com,fbe0119b6af3a6db83bfd6fdb9a6833b,java8,CODE_SMELL,MAIN,182,182,33,56,2015-05-12T19:22:15.000+00:00,2022-12-20T14:50:30.000+00:00
-2,AYYrCDQwCVdTZLqEeJtw,go:S3776,CRITICAL,testWarrenEtcd:api/etcdserverpb/etcdserver.pb.go,testWarrenEtcd,174,OPEN,Refactor this method to reduce its Cognitive Complexity from 21 to the 15 allowed.,11min,11min,,4b568473b2815c6fdf0ed4a6d0083517,brain-overload,CODE_SMELL,MAIN,174,174,18,38,2023-02-07T08:39:18.000+00:00,2023-02-07T08:39:18.000+00:00
-2,AYYrCDQwCVdTZLqEeJtx,go:S3776,CRITICAL,testWarrenEtcd:api/etcdserverpb/etcdserver.pb.go,testWarrenEtcd,394,OPEN,Refactor this method to reduce its Cognitive Complexity from 371 to the 15 allowed.,6h1min,6h1min,,5757b656ecd68cade0c4ffbee99d2cf0,brain-overload,CODE_SMELL,MAIN,394,394,18,27,2023-02-07T08:39:18.000+00:00,2023-02-07T08:39:18.000+00:00
-2,AYYrCDQwCVdTZLqEeJty,go:S3776,CRITICAL,testWarrenEtcd:api/etcdserverpb/etcdserver.pb.go,testWarrenEtcd,830,OPEN,Refactor this method to reduce its Cognitive Complexity from 65 to the 15 allowed.,55min,55min,,cea1920565d72b55451cd8145aa6e6fb,brain-overload,CODE_SMELL,MAIN,830,830,19,28,2023-02-07T08:39:18.000+00:00,2023-02-07T08:39:18.000+00:00
+1,AYUwBbCC46XwcL-YZOTH,java:S5993,MAJOR,f5a50c63-2e8f-4107-9014-853f6f467757:core/src/main/java/com/airbnb/aerosolve/core/util/FeatureDictionary.java,f5a50c63-2e8f-4107-9014-853f6f467757,24,OPEN,"Change the visibility of this constructor to ""protected"".",2,2,hector.yee@airbnb.com,cb21cfee164b0548717edfe840aea8a2,design,CODE_SMELL,MAIN,24,24,2,8,2015-05-12T19:22:15.000+00:00,2022-12-20T14:50:30.000+00:00
+1,AYUwBbGD46XwcL-YZOUu,java:S3824,MAJOR,f5a50c63-2e8f-4107-9014-853f6f467757:core/src/main/java/com/airbnb/aerosolve/core/util/Util.java,f5a50c63-2e8f-4107-9014-853f6f467757,171,OPEN,"Replace this ""Map.get()"" and condition with a call to ""Map.computeIfAbsent()"".",10,10,hector.yee@airbnb.com,136741d9bacea2c123cc5a81be33aa4b,java8,CODE_SMELL,MAIN,171,171,25,49,2015-05-12T19:22:15.000+00:00,2022-12-20T14:50:30.000+00:00
+1,AYUwBbGD46XwcL-YZOUv,java:S3824,MAJOR,f5a50c63-2e8f-4107-9014-853f6f467757:core/src/main/java/com/airbnb/aerosolve/core/util/Util.java,f5a50c63-2e8f-4107-9014-853f6f467757,182,OPEN,"Replace this ""Map.get()"" and condition with a call to ""Map.computeIfAbsent()"".",10,10,hector.yee@airbnb.com,fbe0119b6af3a6db83bfd6fdb9a6833b,java8,CODE_SMELL,MAIN,182,182,33,56,2015-05-12T19:22:15.000+00:00,2022-12-20T14:50:30.000+00:00
+2,AYYrCDQwCVdTZLqEeJtw,go:S3776,CRITICAL,testWarrenEtcd:api/etcdserverpb/etcdserver.pb.go,testWarrenEtcd,174,OPEN,Refactor this method to reduce its Cognitive Complexity from 21 to the 15 allowed.,11,11,,4b568473b2815c6fdf0ed4a6d0083517,brain-overload,CODE_SMELL,MAIN,174,174,18,38,2023-02-07T08:39:18.000+00:00,2023-02-07T08:39:18.000+00:00
+2,AYYrCDQwCVdTZLqEeJtx,go:S3776,CRITICAL,testWarrenEtcd:api/etcdserverpb/etcdserver.pb.go,testWarrenEtcd,394,OPEN,Refactor this method to reduce its Cognitive Complexity from 371 to the 15 allowed.,361,361,,5757b656ecd68cade0c4ffbee99d2cf0,brain-overload,CODE_SMELL,MAIN,394,394,18,27,2023-02-07T08:39:18.000+00:00,2023-02-07T08:39:18.000+00:00
+2,AYYrCDQwCVdTZLqEeJty,go:S3776,CRITICAL,testWarrenEtcd:api/etcdserverpb/etcdserver.pb.go,testWarrenEtcd,830,OPEN,Refactor this method to reduce its Cognitive Complexity from 65 to the 15 allowed.,55,55,,cea1920565d72b55451cd8145aa6e6fb,brain-overload,CODE_SMELL,MAIN,830,830,19,28,2023-02-07T08:39:18.000+00:00,2023-02-07T08:39:18.000+00:00
diff --git a/backend/plugins/sonarqube/e2e/snapshot_tables/filemetrics.csv b/backend/plugins/sonarqube/e2e/snapshot_tables/filemetrics.csv
index b2390e3ab..82470bddb 100644
--- a/backend/plugins/sonarqube/e2e/snapshot_tables/filemetrics.csv
+++ b/backend/plugins/sonarqube/e2e/snapshot_tables/filemetrics.csv
@@ -1,7 +1,7 @@
 id,project_key,file_name,file_path,file_language,code_smells,sqale_index,sqale_rating,bugs,reliability_rating,vulnerabilities,security_rating,security_hotspots,security_hotspots_reviewed,security_review_rating,ncloc,coverage,lines_to_cover,duplicated_lines_density,duplicated_blocks
-sonarqube:SonarqubeFileMetrics:1:02c1047b-f87c-4c35-a6b5-76c6b607d37f:db/migrate/20170206220334_add_start_date_to_sponsors_and_paid.rb,sonarqube:SonarqubeProject:1:02c1047b-f87c-4c35-a6b5-76c6b607d37f,20170206220334_add_start_date_to_sponsors_and_paid.rb,db/migrate/20170206220334_add_start_date_to_sponsors_and_paid.rb,ruby,0,0,1.0,0,A,0,A,0,0,A,6,0,2,0,0
-sonarqube:SonarqubeFileMetrics:1:02c1047b-f87c-4c35-a6b5-76c6b607d37f:db/migrate/20170208152018_acts_as_follower_migration.rb,sonarqube:SonarqubeProject:1:02c1047b-f87c-4c35-a6b5-76c6b607d37f,20170208152018_acts_as_follower_migration.rb,db/migrate/20170208152018_acts_as_follower_migration.rb,ruby,0,0,1.0,0,A,0,A,0,0,A,15,0,8,0,0
-sonarqube:SonarqubeFileMetrics:1:02c1047b-f87c-4c35-a6b5-76c6b607d37f:db/migrate/20170209164016_add_email_follower_notifications.rb,sonarqube:SonarqubeProject:1:02c1047b-f87c-4c35-a6b5-76c6b607d37f,20170209164016_add_email_follower_notifications.rb,db/migrate/20170209164016_add_email_follower_notifications.rb,ruby,0,0,1.0,0,A,0,A,0,0,A,5,0,1,0,0
-sonarqube:SonarqubeFileMetrics:1:02c1047b-f87c-4c35-a6b5-76c6b607d37f:db/migrate/20170213183337_add_sign_up_information_to_users.rb,sonarqube:SonarqubeProject:1:02c1047b-f87c-4c35-a6b5-76c6b607d37f,20170213183337_add_sign_up_information_to_users.rb,db/migrate/20170213183337_add_sign_up_information_to_users.rb,ruby,0,0,1.0,0,A,0,A,0,0,A,7,0,3,0,0
-sonarqube:SonarqubeFileMetrics:1:02c1047b-f87c-4c35-a6b5-76c6b607d37f:db/migrate/20170216145500_add_location_fields_etc_to_users.rb,sonarqube:SonarqubeProject:1:02c1047b-f87c-4c35-a6b5-76c6b607d37f,20170216145500_add_location_fields_etc_to_users.rb,db/migrate/20170216145500_add_location_fields_etc_to_users.rb,ruby,0,0,1.0,0,A,0,A,0,0,A,20,0,16,0,0
-sonarqube:SonarqubeFileMetrics:1:02c1047b-f87c-4c35-a6b5-76c6b607d37f:db/migrate/20170228174838_add_identity_data_to_users.rb,sonarqube:SonarqubeProject:1:02c1047b-f87c-4c35-a6b5-76c6b607d37f,20170228174838_add_identity_data_to_users.rb,db/migrate/20170228174838_add_identity_data_to_users.rb,ruby,0,0,1.0,0,A,0,A,0,0,A,9,0,5,0,0
+sonarqube:SonarqubeFileMetrics:1:02c1047b-f87c-4c35-a6b5-76c6b607d37f:db/migrate/20170206220334_add_start_date_to_sponsors_and_paid.rb,sonarqube:SonarqubeProject:1:02c1047b-f87c-4c35-a6b5-76c6b607d37f,20170206220334_add_start_date_to_sponsors_and_paid.rb,db/migrate/20170206220334_add_start_date_to_sponsors_and_paid.rb,ruby,0,0,1,0,A,0,A,0,0,A,6,0,2,0,0
+sonarqube:SonarqubeFileMetrics:1:02c1047b-f87c-4c35-a6b5-76c6b607d37f:db/migrate/20170208152018_acts_as_follower_migration.rb,sonarqube:SonarqubeProject:1:02c1047b-f87c-4c35-a6b5-76c6b607d37f,20170208152018_acts_as_follower_migration.rb,db/migrate/20170208152018_acts_as_follower_migration.rb,ruby,0,0,1,0,A,0,A,0,0,A,15,0,8,0,0
+sonarqube:SonarqubeFileMetrics:1:02c1047b-f87c-4c35-a6b5-76c6b607d37f:db/migrate/20170209164016_add_email_follower_notifications.rb,sonarqube:SonarqubeProject:1:02c1047b-f87c-4c35-a6b5-76c6b607d37f,20170209164016_add_email_follower_notifications.rb,db/migrate/20170209164016_add_email_follower_notifications.rb,ruby,0,0,1,0,A,0,A,0,0,A,5,0,1,0,0
+sonarqube:SonarqubeFileMetrics:1:02c1047b-f87c-4c35-a6b5-76c6b607d37f:db/migrate/20170213183337_add_sign_up_information_to_users.rb,sonarqube:SonarqubeProject:1:02c1047b-f87c-4c35-a6b5-76c6b607d37f,20170213183337_add_sign_up_information_to_users.rb,db/migrate/20170213183337_add_sign_up_information_to_users.rb,ruby,0,0,1,0,A,0,A,0,0,A,7,0,3,0,0
+sonarqube:SonarqubeFileMetrics:1:02c1047b-f87c-4c35-a6b5-76c6b607d37f:db/migrate/20170216145500_add_location_fields_etc_to_users.rb,sonarqube:SonarqubeProject:1:02c1047b-f87c-4c35-a6b5-76c6b607d37f,20170216145500_add_location_fields_etc_to_users.rb,db/migrate/20170216145500_add_location_fields_etc_to_users.rb,ruby,0,0,1,0,A,0,A,0,0,A,20,0,16,0,0
+sonarqube:SonarqubeFileMetrics:1:02c1047b-f87c-4c35-a6b5-76c6b607d37f:db/migrate/20170228174838_add_identity_data_to_users.rb,sonarqube:SonarqubeProject:1:02c1047b-f87c-4c35-a6b5-76c6b607d37f,20170228174838_add_identity_data_to_users.rb,db/migrate/20170228174838_add_identity_data_to_users.rb,ruby,0,0,1,0,A,0,A,0,0,A,9,0,5,0,0
diff --git a/backend/plugins/sonarqube/e2e/snapshot_tables/issue_hotspots.csv b/backend/plugins/sonarqube/e2e/snapshot_tables/issue_hotspots.csv
index 5f7a7de85..1bf95e693 100644
--- a/backend/plugins/sonarqube/e2e/snapshot_tables/issue_hotspots.csv
+++ b/backend/plugins/sonarqube/e2e/snapshot_tables/issue_hotspots.csv
@@ -1,4 +1,4 @@
 id,rule,severity,component,project_key,line,status,message,debt,effort,commit_author_email,assignee,hash,tags,type,scope,start_line,end_line,start_offset,end_offset,vulnerability_probability,security_category,creation_date,update_date
-sonarqube:SonarqubeHotspot:1:AYUwBajH46XwcL-YZONY,,weak-cryptography,f5a50c63-2e8f-4107-9014-853f6f467757:core/src/main/java/com/airbnb/aerosolve/core/models/MlpModel.java,sonarqube:SonarqubeProject:1:f5a50c63-2e8f-4107-9014-853f6f467757,143,TO_REVIEW,Make sure that using this pseudorandom number generator is safe here.,,,peng.ye@airbnb.com,,,,HOTSPOTS,,143,0,0,0,MEDIUM,,2016-02-26T22:41:48.000+00:00,2022-12-20T14:50:30.000+00:00
-sonarqube:SonarqubeHotspot:1:AYUwBajH46XwcL-YZONZ,,weak-cryptography,f5a50c63-2e8f-4107-9014-853f6f467757:core/src/main/java/com/airbnb/aerosolve/core/models/MlpModel.java,sonarqube:SonarqubeProject:1:f5a50c63-2e8f-4107-9014-853f6f467757,172,TO_REVIEW,Make sure that using this pseudorandom number generator is safe here.,,,peng.ye@airbnb.com,,,,HOTSPOTS,,172,0,0,0,MEDIUM,,2016-02-26T22:41:48.000+00:00,2022-12-20T14:50:30.000+00:00
-sonarqube:SonarqubeHotspot:1:AYUwBamj46XwcL-YZOPh,,weak-cryptography,f5a50c63-2e8f-4107-9014-853f6f467757:core/src/main/java/com/airbnb/aerosolve/core/function/MultiDimensionSpline.java,sonarqube:SonarqubeProject:1:f5a50c63-2e8f-4107-9014-853f6f467757,295,REVIEWED,Make sure that using this pseudorandom number generator is safe here.,,,julian.qian@airbnb.com,,,,HOTSPOTS,,295,0,0,0,MEDIUM,,2016-04-25T19:02:16.000+00:00,2022-12-21T08:30:43.000+00:00
+sonarqube:SonarqubeHotspot:1:AYUwBajH46XwcL-YZONY,,weak-cryptography,f5a50c63-2e8f-4107-9014-853f6f467757:core/src/main/java/com/airbnb/aerosolve/core/models/MlpModel.java,sonarqube:SonarqubeProject:1:f5a50c63-2e8f-4107-9014-853f6f467757,143,TO_REVIEW,Make sure that using this pseudorandom number generator is safe here.,0,0,peng.ye@airbnb.com,,,,HOTSPOTS,,143,0,0,0,MEDIUM,,2016-02-26T22:41:48.000+00:00,2022-12-20T14:50:30.000+00:00
+sonarqube:SonarqubeHotspot:1:AYUwBajH46XwcL-YZONZ,,weak-cryptography,f5a50c63-2e8f-4107-9014-853f6f467757:core/src/main/java/com/airbnb/aerosolve/core/models/MlpModel.java,sonarqube:SonarqubeProject:1:f5a50c63-2e8f-4107-9014-853f6f467757,172,TO_REVIEW,Make sure that using this pseudorandom number generator is safe here.,0,0,peng.ye@airbnb.com,,,,HOTSPOTS,,172,0,0,0,MEDIUM,,2016-02-26T22:41:48.000+00:00,2022-12-20T14:50:30.000+00:00
+sonarqube:SonarqubeHotspot:1:AYUwBamj46XwcL-YZOPh,,weak-cryptography,f5a50c63-2e8f-4107-9014-853f6f467757:core/src/main/java/com/airbnb/aerosolve/core/function/MultiDimensionSpline.java,sonarqube:SonarqubeProject:1:f5a50c63-2e8f-4107-9014-853f6f467757,295,REVIEWED,Make sure that using this pseudorandom number generator is safe here.,0,0,julian.qian@airbnb.com,,,,HOTSPOTS,,295,0,0,0,MEDIUM,,2016-04-25T19:02:16.000+00:00,2022-12-21T08:30:43.000+00:00
diff --git a/backend/plugins/sonarqube/e2e/snapshot_tables/issues.csv b/backend/plugins/sonarqube/e2e/snapshot_tables/issues.csv
index 1069cafb4..6da8c7405 100644
--- a/backend/plugins/sonarqube/e2e/snapshot_tables/issues.csv
+++ b/backend/plugins/sonarqube/e2e/snapshot_tables/issues.csv
@@ -1,4 +1,4 @@
 id,rule,severity,component,project_key,line,status,message,debt,effort,commit_author_email,assignee,hash,tags,type,scope,start_line,end_line,start_offset,end_offset,vulnerability_probability,security_category,creation_date,update_date
-sonarqube:SonarqubeIssue:1:AYUwBbCC46XwcL-YZOTH,java:S5993,MAJOR,f5a50c63-2e8f-4107-9014-853f6f467757:core/src/main/java/com/airbnb/aerosolve/core/util/FeatureDictionary.java,sonarqube:SonarqubeProject:1:f5a50c63-2e8f-4107-9014-853f6f467757,24,OPEN,"Change the visibility of this constructor to ""protected"".",2min,2min,hector.yee@airbnb.com,,cb21cfee164b0548717edfe840aea8a2,design,CODE_SMELL,MAIN,24,24,2,8,,,2015-05-12T19:22:15.000+00:00,2022-12-20T14:50:30.000+00:00
-sonarqube:SonarqubeIssue:1:AYUwBbGD46XwcL-YZOUu,java:S3824,MAJOR,f5a50c63-2e8f-4107-9014-853f6f467757:core/src/main/java/com/airbnb/aerosolve/core/util/Util.java,sonarqube:SonarqubeProject:1:f5a50c63-2e8f-4107-9014-853f6f467757,171,OPEN,"Replace this ""Map.get()"" and condition with a call to ""Map.computeIfAbsent()"".",10min,10min,hector.yee@airbnb.com,,136741d9bacea2c123cc5a81be33aa4b,java8,CODE_SMELL,MAIN,171,171,25,49,,,2015-05-12T19:22:15.000+00:00,2022-12-20T14:50:30.000+00:00
-sonarqube:SonarqubeIssue:1:AYUwBbGD46XwcL-YZOUv,java:S3824,MAJOR,f5a50c63-2e8f-4107-9014-853f6f467757:core/src/main/java/com/airbnb/aerosolve/core/util/Util.java,sonarqube:SonarqubeProject:1:f5a50c63-2e8f-4107-9014-853f6f467757,182,OPEN,"Replace this ""Map.get()"" and condition with a call to ""Map.computeIfAbsent()"".",10min,10min,hector.yee@airbnb.com,,fbe0119b6af3a6db83bfd6fdb9a6833b,java8,CODE_SMELL,MAIN,182,182,33,56,,,2015-05-12T19:22:15.000+00:00,2022-12-20T14:50:30.000+00:00
+sonarqube:SonarqubeIssue:1:AYUwBbCC46XwcL-YZOTH,java:S5993,MAJOR,f5a50c63-2e8f-4107-9014-853f6f467757:core/src/main/java/com/airbnb/aerosolve/core/util/FeatureDictionary.java,sonarqube:SonarqubeProject:1:f5a50c63-2e8f-4107-9014-853f6f467757,24,OPEN,"Change the visibility of this constructor to ""protected"".",2,2,hector.yee@airbnb.com,,cb21cfee164b0548717edfe840aea8a2,design,CODE_SMELL,MAIN,24,24,2,8,,,2015-05-12T19:22:15.000+00:00,2022-12-20T14:50:30.000+00:00
+sonarqube:SonarqubeIssue:1:AYUwBbGD46XwcL-YZOUu,java:S3824,MAJOR,f5a50c63-2e8f-4107-9014-853f6f467757:core/src/main/java/com/airbnb/aerosolve/core/util/Util.java,sonarqube:SonarqubeProject:1:f5a50c63-2e8f-4107-9014-853f6f467757,171,OPEN,"Replace this ""Map.get()"" and condition with a call to ""Map.computeIfAbsent()"".",10,10,hector.yee@airbnb.com,,136741d9bacea2c123cc5a81be33aa4b,java8,CODE_SMELL,MAIN,171,171,25,49,,,2015-05-12T19:22:15.000+00:00,2022-12-20T14:50:30.000+00:00
+sonarqube:SonarqubeIssue:1:AYUwBbGD46XwcL-YZOUv,java:S3824,MAJOR,f5a50c63-2e8f-4107-9014-853f6f467757:core/src/main/java/com/airbnb/aerosolve/core/util/Util.java,sonarqube:SonarqubeProject:1:f5a50c63-2e8f-4107-9014-853f6f467757,182,OPEN,"Replace this ""Map.get()"" and condition with a call to ""Map.computeIfAbsent()"".",10,10,hector.yee@airbnb.com,,fbe0119b6af3a6db83bfd6fdb9a6833b,java8,CODE_SMELL,MAIN,182,182,33,56,,,2015-05-12T19:22:15.000+00:00,2022-12-20T14:50:30.000+00:00
diff --git a/backend/plugins/sonarqube/models/migrationscripts/20230111_add_init_tables.go b/backend/plugins/sonarqube/models/migrationscripts/20230111_add_init_tables.go
index d61ee253a..e448a5a6e 100644
--- a/backend/plugins/sonarqube/models/migrationscripts/20230111_add_init_tables.go
+++ b/backend/plugins/sonarqube/models/migrationscripts/20230111_add_init_tables.go
@@ -27,6 +27,17 @@ import (
 type addInitTables struct{}
 
 func (*addInitTables) Up(basicRes context.BasicRes) errors.Error {
+	err := basicRes.GetDal().DropTables(
+		&archived.SonarqubeProject{},
+		&archived.SonarqubeHotspot{},
+		&archived.SonarqubeIssue{},
+		&archived.SonarqubeFileMetrics{},
+		&archived.SonarqubeIssueCodeBlock{},
+		&archived.SonarqubeAccount{},
+	)
+	if err != nil {
+		return err
+	}
 	return migrationhelper.AutoMigrateTables(
 		basicRes,
 		&archived.SonarqubeConnection{},
@@ -40,7 +51,7 @@ func (*addInitTables) Up(basicRes context.BasicRes) errors.Error {
 }
 
 func (*addInitTables) Version() uint64 {
-	return 20230208220025
+	return 20230221220030
 }
 
 func (*addInitTables) Name() string {
diff --git a/backend/plugins/sonarqube/models/migrationscripts/archived/sonarqube_file_metrics.go b/backend/plugins/sonarqube/models/migrationscripts/archived/sonarqube_file_metrics.go
index 95a2fa746..a288de898 100644
--- a/backend/plugins/sonarqube/models/migrationscripts/archived/sonarqube_file_metrics.go
+++ b/backend/plugins/sonarqube/models/migrationscripts/archived/sonarqube_file_metrics.go
@@ -29,8 +29,8 @@ type SonarqubeFileMetrics struct {
 	FilePath                 string
 	FileLanguage             string
 	CodeSmells               int
-	SqaleIndex               string
-	SqaleRating              string
+	SqaleIndex               int
+	SqaleRating              float64
 	Bugs                     int
 	ReliabilityRating        string
 	Vulnerabilities          int
diff --git a/backend/plugins/sonarqube/models/migrationscripts/archived/sonarqube_issue.go b/backend/plugins/sonarqube/models/migrationscripts/archived/sonarqube_issue.go
index 66a470c49..f457d5570 100644
--- a/backend/plugins/sonarqube/models/migrationscripts/archived/sonarqube_issue.go
+++ b/backend/plugins/sonarqube/models/migrationscripts/archived/sonarqube_issue.go
@@ -32,8 +32,8 @@ type SonarqubeIssue struct {
 	Line         int
 	Status       string
 	Message      string
-	Debt         string
-	Effort       string
+	Debt         int
+	Effort       int
 	Author       string
 	Hash         string
 	Tags         string
diff --git a/backend/plugins/sonarqube/models/sonarqube_file_metrics.go b/backend/plugins/sonarqube/models/sonarqube_file_metrics.go
index da647170f..c895e44f2 100644
--- a/backend/plugins/sonarqube/models/sonarqube_file_metrics.go
+++ b/backend/plugins/sonarqube/models/sonarqube_file_metrics.go
@@ -29,8 +29,8 @@ type SonarqubeFileMetrics struct {
 	FilePath                 string
 	FileLanguage             string
 	CodeSmells               int
-	SqaleIndex               string
-	SqaleRating              string
+	SqaleIndex               int
+	SqaleRating              float64
 	Bugs                     int
 	ReliabilityRating        string
 	Vulnerabilities          int
diff --git a/backend/plugins/sonarqube/models/sonarqube_issue.go b/backend/plugins/sonarqube/models/sonarqube_issue.go
index bbe327e22..343a7c639 100644
--- a/backend/plugins/sonarqube/models/sonarqube_issue.go
+++ b/backend/plugins/sonarqube/models/sonarqube_issue.go
@@ -32,8 +32,8 @@ type SonarqubeIssue struct {
 	Line         int
 	Status       string
 	Message      string
-	Debt         string
-	Effort       string
+	Debt         int
+	Effort       int
 	Author       string
 	Hash         string
 	Tags         string
diff --git a/backend/plugins/sonarqube/tasks/filemetrics_extractor.go b/backend/plugins/sonarqube/tasks/filemetrics_extractor.go
index eff31792b..d3392513e 100644
--- a/backend/plugins/sonarqube/tasks/filemetrics_extractor.go
+++ b/backend/plugins/sonarqube/tasks/filemetrics_extractor.go
@@ -58,9 +58,15 @@ func ExtractFilemetrics(taskCtx plugin.SubTaskContext) errors.Error {
 			for _, v := range body.Measures {
 				switch v.Metric {
 				case "sqale_index":
-					fileMetrics.SqaleIndex = v.Value
+					fileMetrics.SqaleIndex, err = errors.Convert01(strconv.Atoi(v.Value))
+					if err != nil {
+						return nil, err
+					}
 				case "sqale_rating":
-					fileMetrics.SqaleRating = v.Value
+					fileMetrics.SqaleRating, err = errors.Convert01(strconv.ParseFloat(v.Value, 32))
+					if err != nil {
+						return nil, err
+					}
 				case "reliability_rating":
 					fileMetrics.ReliabilityRating = alphabetMap[v.Value]
 				case "security_rating":
diff --git a/backend/plugins/sonarqube/tasks/issues_extractor.go b/backend/plugins/sonarqube/tasks/issues_extractor.go
index 3facf5ead..e0b008afc 100644
--- a/backend/plugins/sonarqube/tasks/issues_extractor.go
+++ b/backend/plugins/sonarqube/tasks/issues_extractor.go
@@ -19,23 +19,19 @@ package tasks
 
 import (
 	"crypto/sha256"
-	"encoding/hex"
 	"encoding/json"
-	"fmt"
 	"github.com/apache/incubator-devlake/core/errors"
 	"github.com/apache/incubator-devlake/core/plugin"
 	helper "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
 	"github.com/apache/incubator-devlake/plugins/sonarqube/models"
-	"hash"
 	"strings"
 )
 
 var _ plugin.SubTaskEntryPoint = ExtractIssues
-var hashCodeBlock hash.Hash
 
 func ExtractIssues(taskCtx plugin.SubTaskContext) errors.Error {
 	rawDataSubTaskArgs, data := CreateRawDataSubTaskArgs(taskCtx, RAW_ISSUES_TABLE)
-	hashCodeBlock = sha256.New()
+	hashCodeBlock := sha256.New()
 	extractor, err := helper.NewApiExtractor(helper.ApiExtractorArgs{
 		RawDataSubTaskArgs: *rawDataSubTaskArgs,
 		Extract: func(resData *helper.RawData) ([]interface{}, errors.Error) {
@@ -54,8 +50,6 @@ func ExtractIssues(taskCtx plugin.SubTaskContext) errors.Error {
 				Line:         body.Line,
 				Status:       body.Status,
 				Message:      body.Message,
-				Debt:         body.Debt,
-				Effort:       body.Effort,
 				Author:       body.Author,
 				Hash:         body.Hash,
 				Type:         body.Type,
@@ -67,6 +61,14 @@ func ExtractIssues(taskCtx plugin.SubTaskContext) errors.Error {
 				CreationDate: body.CreationDate,
 				UpdateDate:   body.UpdateDate,
 			}
+			sonarqubeIssue.Debt = convertTimeToMinutes(body.Debt)
+			if err != nil {
+				return nil, err
+			}
+			sonarqubeIssue.Effort = convertTimeToMinutes(body.Effort)
+			if err != nil {
+				return nil, err
+			}
 			if len(body.Tags) > 0 {
 				sonarqubeIssue.Tags = strings.Join(body.Tags, ",")
 			}
@@ -85,7 +87,7 @@ func ExtractIssues(taskCtx plugin.SubTaskContext) errors.Error {
 						StartOffset:  location.TextRange.StartOffset,
 						EndOffset:    location.TextRange.EndOffset,
 					}
-					generateId(codeBlock)
+					generateId(hashCodeBlock, codeBlock)
 					results = append(results, codeBlock)
 				}
 			}
@@ -149,8 +151,3 @@ type Location struct {
 	TextRange TextRange `json:"textRange"`
 	Msg       string    `json:"msg"`
 }
-
-func generateId(entity *models.SonarqubeIssueCodeBlock) {
-	hashCodeBlock.Write([]byte(fmt.Sprintf("%s-%s-%d-%d-%d-%d-%s", entity.IssueKey, entity.Component, entity.StartLine, entity.EndLine, entity.StartOffset, entity.EndOffset, entity.Msg)))
-	entity.Id = hex.EncodeToString(hashCodeBlock.Sum(nil))
-}
diff --git a/backend/plugins/sonarqube/tasks/shared.go b/backend/plugins/sonarqube/tasks/shared.go
index 024cffcb7..43f7d3cdf 100644
--- a/backend/plugins/sonarqube/tasks/shared.go
+++ b/backend/plugins/sonarqube/tasks/shared.go
@@ -18,11 +18,15 @@ limitations under the License.
 package tasks
 
 import (
+	"encoding/hex"
+	"fmt"
 	"github.com/apache/incubator-devlake/core/errors"
 	"github.com/apache/incubator-devlake/core/plugin"
 	"github.com/apache/incubator-devlake/helpers/pluginhelper/api"
 	"github.com/apache/incubator-devlake/plugins/sonarqube/models"
+	"hash"
 	"net/http"
+	"unicode"
 )
 
 func CreateRawDataSubTaskArgs(taskCtx plugin.SubTaskContext, rawTable string) (*api.RawDataSubTaskArgs, *SonarqubeTaskData) {
@@ -85,3 +89,41 @@ func ConvertProject(sonarqubeApiProject *SonarqubeApiProject) *models.SonarqubeP
 	}
 	return sonarqubeProject
 }
+
+func generateId(hashCodeBlock hash.Hash, entity *models.SonarqubeIssueCodeBlock) {
+	hashCodeBlock.Write([]byte(fmt.Sprintf("%s-%s-%d-%d-%d-%d-%s", entity.IssueKey, entity.Component, entity.StartLine, entity.EndLine, entity.StartOffset, entity.EndOffset, entity.Msg)))
+	entity.Id = hex.EncodeToString(hashCodeBlock.Sum(nil))
+}
+
+func convertTimeToMinutes(timeStr string) int {
+	days := 0
+	hours := 0
+	minutes := 0
+
+	var currentNum int
+	var currentUnit string
+
+	for i := 0; i < len(timeStr); i++ {
+		c := timeStr[i]
+
+		if unicode.IsDigit(rune(c)) {
+			currentNum = currentNum*10 + int(c-'0')
+		} else {
+			currentUnit += string(c)
+			if currentUnit == "d" {
+				days = currentNum
+			} else if currentUnit == "h" {
+				hours = currentNum
+			} else if currentUnit == "min" {
+				minutes = currentNum
+			} else {
+				continue
+			}
+			currentNum = 0
+			currentUnit = ""
+		}
+	}
+
+	totalMinutes := days*24*60 + hours*60 + minutes
+	return totalMinutes
+}
diff --git a/backend/plugins/sonarqube/tasks/shared_test.go b/backend/plugins/sonarqube/tasks/shared_test.go
new file mode 100644
index 000000000..0cd211a9e
--- /dev/null
+++ b/backend/plugins/sonarqube/tasks/shared_test.go
@@ -0,0 +1,105 @@
+/*
+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 (
+	"crypto/sha256"
+	"github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+	"github.com/apache/incubator-devlake/plugins/sonarqube/models"
+	"io/ioutil"
+	"net/http"
+	"strings"
+	"testing"
+)
+
+func TestConvertTimeToMinutes(t *testing.T) {
+	testCases := []struct {
+		timeStr     string
+		expectedMin int
+	}{
+		//{"1min", 1},
+		//{"30min", 30},
+		//{"1h30min", 90},
+		{"1d1h30min", 1530},
+		{"3d5h10min", 4630},
+	}
+
+	for _, tc := range testCases {
+		actualMin := convertTimeToMinutes(tc.timeStr)
+		if actualMin != tc.expectedMin {
+			t.Errorf("convertTimeToMinutes(%v) = %v; expected %v", tc.timeStr, actualMin, tc.expectedMin)
+		}
+	}
+}
+
+func TestGenerateId(t *testing.T) {
+	entity := &models.SonarqubeIssueCodeBlock{
+		IssueKey:    "ISSUE-123",
+		Component:   "com.example:example-project",
+		StartLine:   10,
+		EndLine:     20,
+		StartOffset: 5,
+		EndOffset:   10,
+		Msg:         "Example message",
+	}
+	hashCodeBlock := sha256.New()
+	generateId(hashCodeBlock, entity)
+
+	expectedId := "c590f554324b82421b898723e51b4aa9217dc897aa79e5d55b1716df88a5af1e"
+	if entity.Id != expectedId {
+		t.Errorf("generateId did not generate the expected ID. Got %v, expected %v", entity.Id, expectedId)
+	}
+}
+
+func TestGetTotalPagesFromResponse(t *testing.T) {
+	// mock response body
+	responseBody := `{
+		"paging": {
+			"pageIndex": 1,
+			"pageSize": 10,
+			"total": 20
+		},
+		"results": [
+			{"id": 1, "name": "project 1"},
+			{"id": 2, "name": "project 2"}
+		]
+	}`
+
+	// create a mock HTTP response with the above body
+	response := &http.Response{
+		StatusCode: http.StatusOK,
+		Body:       ioutil.NopCloser(strings.NewReader(responseBody)),
+	}
+
+	// create mock ApiCollectorArgs
+	args := &api.ApiCollectorArgs{
+		PageSize: 10,
+	}
+
+	// call the function to get the total number of pages
+	totalPages, err := GetTotalPagesFromResponse(response, args)
+	if err != nil {
+		t.Fatalf("Error: %v", err)
+	}
+
+	// verify the result
+	expectedPages := 2
+	if totalPages != expectedPages {
+		t.Fatalf("Expected %v pages, but got %v", expectedPages, totalPages)
+	}
+}