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 <[email protected]>
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,[email protected],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,[email protected],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,[email protected],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,[email protected],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,[email protected],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,[email protected],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.,,,[email protected],,,,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.,,,[email protected],,,,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.,,,[email protected],,,,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,[email protected],,,,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,[email protected],,,,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,[email protected],,,,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,[email protected],,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,[email protected],,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,[email protected],,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,[email protected],,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,[email protected],,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,[email protected],,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)
+       }
+}


Reply via email to