This is an automated email from the ASF dual-hosted git repository.
bamboo 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 f271245d2 [Bug][Jira] V2 API removed #8563 (#8608)
f271245d2 is described below
commit f271245d275a0830082a9e7b4a6e3caa795004be
Author: Vibhor Mahajan <[email protected]>
AuthorDate: Tue Oct 14 06:01:51 2025 +0530
[Bug][Jira] V2 API removed #8563 (#8608)
* fix: normalize deployment type comparison in epic and issue collectors
* fix: use strings.EqualFold for case-insensitive deployment type comparison
- Replace strings.ToLower(deploymentType) == constant with strings.EqualFold
- Fixes issue where case-insensitive comparison was only converting one side
- Update tests to reflect the new comparison logic using strings.EqualFold
- Affects epic_collector.go and issue_collector.go
---------
Co-authored-by: Vibhor Mahajan <[email protected]>
---
backend/plugins/jira/tasks/epic_collector.go | 4 +-
backend/plugins/jira/tasks/epic_collector_test.go | 217 ++++++++++++++++++++++
backend/plugins/jira/tasks/issue_collector.go | 3 +-
3 files changed, 221 insertions(+), 3 deletions(-)
diff --git a/backend/plugins/jira/tasks/epic_collector.go
b/backend/plugins/jira/tasks/epic_collector.go
index 2e777f92c..d7fc45f3b 100644
--- a/backend/plugins/jira/tasks/epic_collector.go
+++ b/backend/plugins/jira/tasks/epic_collector.go
@@ -51,7 +51,7 @@ func CollectEpics(taskCtx plugin.SubTaskContext) errors.Error
{
data := taskCtx.GetData().(*JiraTaskData)
logger := taskCtx.GetLogger()
batchSize := 100
- if data.JiraServerInfo.DeploymentType == models.DeploymentServer &&
len(data.JiraServerInfo.VersionNumbers) == 3 &&
data.JiraServerInfo.VersionNumbers[0] <= 8 {
+ if strings.EqualFold(string(data.JiraServerInfo.DeploymentType),
string(models.DeploymentServer)) && len(data.JiraServerInfo.VersionNumbers) ==
3 && data.JiraServerInfo.VersionNumbers[0] <= 8 {
batchSize = 1
}
epicIterator, err := GetEpicKeysIterator(db, data, batchSize)
@@ -83,7 +83,7 @@ func CollectEpics(taskCtx plugin.SubTaskContext) errors.Error
{
}
// Choose API endpoint based on JIRA deployment type
- if data.JiraServerInfo.DeploymentType == models.DeploymentServer {
+ if strings.EqualFold(string(data.JiraServerInfo.DeploymentType),
string(models.DeploymentServer)) {
logger.Info("Using api/2/search for JIRA Server")
err = setupApiV2Collector(apiCollector, data, epicIterator, jql)
} else {
diff --git a/backend/plugins/jira/tasks/epic_collector_test.go
b/backend/plugins/jira/tasks/epic_collector_test.go
new file mode 100644
index 000000000..8587f0dc2
--- /dev/null
+++ b/backend/plugins/jira/tasks/epic_collector_test.go
@@ -0,0 +1,217 @@
+/*
+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 (
+ "strings"
+ "testing"
+
+ "github.com/apache/incubator-devlake/plugins/jira/models"
+)
+
+func TestEpicCollectorBatchSizeLogic(t *testing.T) {
+ tests := []struct {
+ name string
+ deploymentType models.DeploymentType
+ versionNumbers []int
+ expectedBatch int
+ }{
+ {
+ name: "JIRA Server v8 should use batch size
1",
+ deploymentType: models.DeploymentServer,
+ versionNumbers: []int{8, 0, 0},
+ expectedBatch: 1,
+ },
+ {
+ name: "JIRA Server v7 should use batch size
1",
+ deploymentType: models.DeploymentServer,
+ versionNumbers: []int{7, 5, 0},
+ expectedBatch: 1,
+ },
+ {
+ name: "JIRA Server v9 should use default
batch size",
+ deploymentType: models.DeploymentServer,
+ versionNumbers: []int{9, 0, 0},
+ expectedBatch: 100,
+ },
+ {
+ name: "JIRA Cloud should use default batch
size",
+ deploymentType: models.DeploymentCloud,
+ versionNumbers: []int{8, 0, 0}, // Version shouldn't
matter for cloud
+ expectedBatch: 100,
+ },
+ {
+ name: "Case insensitive server comparison",
+ deploymentType: "server", // lowercase
+ versionNumbers: []int{8, 0, 0},
+ expectedBatch: 1,
+ },
+ {
+ name: "Case insensitive cloud comparison",
+ deploymentType: "CLOUD", // uppercase
+ versionNumbers: []int{8, 0, 0},
+ expectedBatch: 100,
+ },
+ {
+ name: "Mixed case server comparison",
+ deploymentType: "SeRvEr", // mixed case
+ versionNumbers: []int{7, 0, 0},
+ expectedBatch: 1,
+ },
+ {
+ name: "JIRA Server with incomplete version
numbers",
+ deploymentType: models.DeploymentServer,
+ versionNumbers: []int{8, 0}, // Only 2 elements instead
of 3
+ expectedBatch: 100,
+ },
+ {
+ name: "JIRA Server with empty version
numbers",
+ deploymentType: models.DeploymentServer,
+ versionNumbers: []int{},
+ expectedBatch: 100,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ // Test the batch size logic from CollectEpics function
+ batchSize := 100
+
+ // Replicate the exact logic from CollectEpics
+ if strings.EqualFold(string(tt.deploymentType),
string(models.DeploymentServer)) &&
+ len(tt.versionNumbers) == 3 &&
+ tt.versionNumbers[0] <= 8 {
+ batchSize = 1
+ }
+
+ if batchSize != tt.expectedBatch {
+ t.Errorf("Batch size for %s with version %v:
got %d, want %d",
+ tt.deploymentType, tt.versionNumbers,
batchSize, tt.expectedBatch)
+ }
+ })
+ }
+}
+
+func TestEpicCollectorDeploymentTypeLogic(t *testing.T) {
+ tests := []struct {
+ name string
+ deploymentType models.DeploymentType
+ expectServer bool
+ }{
+ {
+ name: "JIRA Server constant should be
detected as server",
+ deploymentType: models.DeploymentServer,
+ expectServer: true,
+ },
+ {
+ name: "JIRA Cloud constant should not be
detected as server",
+ deploymentType: models.DeploymentCloud,
+ expectServer: false,
+ },
+ {
+ name: "Lowercase server should be detected as
server",
+ deploymentType: "server",
+ expectServer: true,
+ },
+ {
+ name: "Uppercase SERVER should be detected as
server",
+ deploymentType: "SERVER",
+ expectServer: true,
+ },
+ {
+ name: "Mixed case SeRvEr should be detected
as server",
+ deploymentType: "SeRvEr",
+ expectServer: true,
+ },
+ {
+ name: "Lowercase cloud should not be detected
as server",
+ deploymentType: "cloud",
+ expectServer: false,
+ },
+ {
+ name: "Uppercase CLOUD should not be detected
as server",
+ deploymentType: "CLOUD",
+ expectServer: false,
+ },
+ {
+ name: "Mixed case ClOuD should not be
detected as server",
+ deploymentType: "ClOuD",
+ expectServer: false,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ // Test the deployment type logic from CollectEpics
function
+ // This replicates the exact comparison used in the
collector
+ isServer :=
strings.EqualFold(string(tt.deploymentType), string(models.DeploymentServer))
+
+ if isServer != tt.expectServer {
+ t.Errorf("Deployment type detection for %s: got
isServer=%v, want %v",
+ tt.deploymentType, isServer,
tt.expectServer)
+ }
+ })
+ }
+}
+
+func TestEpicCollectorApiEndpointSelection(t *testing.T) {
+ tests := []struct {
+ name string
+ deploymentType models.DeploymentType
+ expectedEndpoint string
+ }{
+ {
+ name: "JIRA Server should use api/2/search",
+ deploymentType: models.DeploymentServer,
+ expectedEndpoint: "api/2/search",
+ },
+ {
+ name: "JIRA Cloud should use
api/3/search/jql",
+ deploymentType: models.DeploymentCloud,
+ expectedEndpoint: "api/3/search/jql",
+ },
+ {
+ name: "Lowercase server should use
api/2/search",
+ deploymentType: "server",
+ expectedEndpoint: "api/2/search",
+ },
+ {
+ name: "Uppercase CLOUD should use
api/3/search/jql",
+ deploymentType: "CLOUD",
+ expectedEndpoint: "api/3/search/jql",
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ // Test the API endpoint selection logic from
CollectEpics function
+ var selectedEndpoint string
+
+ if strings.EqualFold(string(tt.deploymentType),
string(models.DeploymentServer)) {
+ selectedEndpoint = "api/2/search"
+ } else {
+ selectedEndpoint = "api/3/search/jql"
+ }
+
+ if selectedEndpoint != tt.expectedEndpoint {
+ t.Errorf("API endpoint selection for %s: got
%s, want %s",
+ tt.deploymentType, selectedEndpoint,
tt.expectedEndpoint)
+ }
+ })
+ }
+}
\ No newline at end of file
diff --git a/backend/plugins/jira/tasks/issue_collector.go
b/backend/plugins/jira/tasks/issue_collector.go
index 84ac2d727..9a361cbbc 100644
--- a/backend/plugins/jira/tasks/issue_collector.go
+++ b/backend/plugins/jira/tasks/issue_collector.go
@@ -23,6 +23,7 @@ import (
"io"
"net/http"
"net/url"
+ "strings"
"time"
"github.com/apache/incubator-devlake/core/dal"
@@ -172,7 +173,7 @@ func getTimeZone(taskCtx plugin.SubTaskContext)
(*time.Location, errors.Error) {
var resp *http.Response
var path string
var query url.Values
- if data.JiraServerInfo.DeploymentType == models.DeploymentServer {
+ if strings.EqualFold(string(data.JiraServerInfo.DeploymentType),
string(models.DeploymentServer)) {
path = "api/2/user"
query = url.Values{"username": []string{conn.Username}}
} else {