Skip to content
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
224bf6c
Update for adding jira site config parameter maxIssuesFromJqlSearch
Hardikrathod01 May 29, 2025
d3b34b9
Update for replacing hard coded value in DescriptorImplTest with Defa…
Hardikrathod01 May 29, 2025
56ba76d
Fix testcase replaceWithFixVersionByRegex
Hardikrathod01 May 29, 2025
4066698
Update for fixing format violations
Hardikrathod01 May 29, 2025
df344f1
- Update for adding maximum allowed value check
May 30, 2025
f330693
Use validation message from messages
May 30, 2025
56e650e
Remove check method for form field validation
May 30, 2025
9c15e78
Apply spotless fix
May 30, 2025
72685e3
Fix test case
May 30, 2025
ea54995
Fix test case and a validation message grammar
May 30, 2025
a83a835
Fix test case because old config will not have maxIssuesFromJqlSearch
May 30, 2025
0ed4a86
Remove parameter maxIssuesFromJqlSearch from doValidate and related p…
May 30, 2025
b97bac5
Implement maximum param value check in setter method
May 30, 2025
6ebfa72
Use variable for comparison of max allowed value
May 30, 2025
e9bd57b
Use ternary operator in setter method
May 30, 2025
3a64256
Remove parameter from old jira configuration
May 30, 2025
358f535
Merge remote-tracking branch 'upstream/master' into issue_662_configu…
May 30, 2025
61cef1e
Resolve conflicts
May 30, 2025
9a07f94
Fix jira session tests
May 30, 2025
bade3ad
Fix test cases because of stubbing problem
May 30, 2025
85750d8
Fix indentation
May 30, 2025
7df98e1
Use Integer instead of int
May 30, 2025
d6823c4
Fix default value not being used when migrating from old config
May 30, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 9 additions & 8 deletions src/main/java/hudson/plugins/jira/JiraSession.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,6 @@
public class JiraSession {
private static final Logger LOGGER = Logger.getLogger(JiraSession.class.getName());

public static final Integer MAX_ISSUES = 100;

public final JiraRestService service;

/**
Expand All @@ -51,9 +49,12 @@

private final String jiraSiteName;

private final Integer maxIssuesFromJqlSearch;

/* package */ JiraSession(JiraSite site, JiraRestService jiraRestService) {
this.service = jiraRestService;
this.jiraSiteName = site.getName();
this.maxIssuesFromJqlSearch = site.getMaxIssuesFromJqlSearch();
}

/**
Expand Down Expand Up @@ -132,102 +133,102 @@
* @return issues matching the JQL query
*/
public List<Issue> getIssuesFromJqlSearch(final String jqlSearch) throws TimeoutException {
return service.getIssuesFromJqlSearch(jqlSearch, MAX_ISSUES);
return service.getIssuesFromJqlSearch(jqlSearch, maxIssuesFromJqlSearch);
}

/**
* Get all versions from the given project
*
* @param projectKey The key for the project
* @return An array of versions
*/
public List<ExtendedVersion> getVersions(String projectKey) {
LOGGER.fine("Fetching versions from project: " + projectKey);
return service.getVersions(projectKey);
}

/**
* Get a version by its name
*
* @param projectKey The key for the project
* @param name The version name
* @return A RemoteVersion, or null if not found
*/
public ExtendedVersion getVersionByName(String projectKey, String name) {
LOGGER.fine("Fetching versions from project: " + projectKey);
List<ExtendedVersion> versions = getVersions(projectKey);
if (versions == null) {
return null;
}
for (ExtendedVersion version : versions) {
if (version.getName().equals(name)) {
return version;
}
}
return null;
}

public List<Issue> getIssuesWithFixVersion(String projectKey, String version) throws TimeoutException {
return getIssuesWithFixVersion(projectKey, version, "");
}

public List<Issue> getIssuesWithFixVersion(String projectKey, String version, String filter)
throws TimeoutException {
LOGGER.fine("Fetching versions from project: " + projectKey + " with fixVersion:" + version);
if (isNotEmpty(filter)) {
return service.getIssuesFromJqlSearch(
String.format("project = \"%s\" and fixVersion = \"%s\" and " + filter, projectKey, version),
MAX_ISSUES);
maxIssuesFromJqlSearch);
}
return service.getIssuesFromJqlSearch(
String.format("project = \"%s\" and fixVersion = \"%s\"", projectKey, version), MAX_ISSUES);
String.format("project = \"%s\" and fixVersion = \"%s\"", projectKey, version), maxIssuesFromJqlSearch);
}

/**
* Get all issue types
*
* @return An array of issue types
*/
public List<IssueType> getIssueTypes() {
LOGGER.fine("Fetching issue types");
return service.getIssueTypes();
}

/**
* Get all priorities
*
* @return An array of priorities
*/
public List<Priority> getPriorities() {
LOGGER.fine("Fetching priorities");
return service.getPriorities();
}

/**
* Release given version in given project
*/
public void releaseVersion(String projectKey, ExtendedVersion version) {
LOGGER.fine("Releasing version: " + version.getName());
service.releaseVersion(projectKey, version);
}

/**
* Replaces the fix version list of all issues matching the JQL Query with the version specified.
*
* @param projectKey The Jira Project key
* @param version The replacement version
* @param query The JQL Query
*/
public void migrateIssuesToFixVersion(String projectKey, String version, String query) throws TimeoutException {

Version newVersion = getVersionByName(projectKey, version);
if (newVersion == null) {
LOGGER.warning("Version " + version + " was not found");
return;
}

LOGGER.fine("Fetching versions with JQL:" + query);
List<Issue> issues = service.getIssuesFromJqlSearch(query, MAX_ISSUES);
List<Issue> issues = service.getIssuesFromJqlSearch(query, maxIssuesFromJqlSearch);

Check warning on line 231 in src/main/java/hudson/plugins/jira/JiraSession.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 136-231 are not covered by tests
if (issues == null || issues.isEmpty()) {
return;
}
Expand Down Expand Up @@ -257,7 +258,7 @@
}

LOGGER.fine("Fetching versions with JQL:" + query);
List<Issue> issues = service.getIssuesFromJqlSearch(query, MAX_ISSUES);
List<Issue> issues = service.getIssuesFromJqlSearch(query, maxIssuesFromJqlSearch);
if (issues == null) {
return;
}
Expand Down Expand Up @@ -313,7 +314,7 @@
}

LOGGER.fine("Fetching issues with JQL:" + query);
List<Issue> issues = service.getIssuesFromJqlSearch(query, MAX_ISSUES);
List<Issue> issues = service.getIssuesFromJqlSearch(query, maxIssuesFromJqlSearch);
if (issues == null || issues.isEmpty()) {
return;
}
Expand Down
48 changes: 37 additions & 11 deletions src/main/java/hudson/plugins/jira/JiraSite.java
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@
* needed to access this Jira.
* </p>
* <b>When adding new fields do not miss to look at readResolve method!!</b>
*
* @author Kohsuke Kawaguchi
*/
public class JiraSite extends AbstractDescribableImpl<JiraSite> {
Expand All @@ -117,6 +118,8 @@

public static final int DEFAULT_THREAD_EXECUTOR_NUMBER = 10;

public static final Integer MAX_ALLOWED_ISSUES_FROM_JQL = 5000;

/**
* URL of Jira for Jenkins access, like {@code http://jira.codehaus.org/}.
* Mandatory. Normalized to end with '/'
Expand Down Expand Up @@ -150,13 +153,15 @@

/**
* User name needed to login. Optional.
*
* @deprecated use credentialsId
*/
@Deprecated
private transient String userName;

/**
* Password needed to login. Optional.
*
* @deprecated use credentialsId
*/
@Deprecated
Expand Down Expand Up @@ -221,12 +226,14 @@

/**
* response timeout for jira rest call
*
* @since 3.0.3
*/
private int readTimeout = DEFAULT_READ_TIMEOUT;

/**
* thread pool number
*
* @since 3.0.3
*/
private int threadExecutorNumber = DEFAULT_THREAD_EXECUTOR_NUMBER;
Expand All @@ -238,10 +245,14 @@

/**
* To add scm entry change date and time in jira comments.
*
*/
private boolean appendChangeTimestamp;

/**
* To allow configurable value of max issues from jql search via jira site global configuration.
*/
private int maxIssuesFromJqlSearch;

private int ioThreadCount = Integer.getInteger(JiraSite.class.getName() + ".httpclient.options.ioThreadCount", 2);

/**
Expand Down Expand Up @@ -502,6 +513,7 @@
/**
* Sets connect timeout (in seconds).
* If not specified, a default timeout will be used.
*
* @param timeoutSec Timeout in seconds
*/
@DataBoundSetter
Expand All @@ -516,6 +528,7 @@
/**
* Sets read timeout (in seconds).
* If not specified, a default timeout will be used.
*
* @param readTimeout Timeout in seconds
*/
@DataBoundSetter
Expand Down Expand Up @@ -646,6 +659,17 @@
this.updateJiraIssueForAllStatus = updateJiraIssueForAllStatus;
}

@DataBoundSetter
public void setMaxIssuesFromJqlSearch(int maxIssuesFromJqlSearch) {
this.maxIssuesFromJqlSearch = maxIssuesFromJqlSearch > MAX_ALLOWED_ISSUES_FROM_JQL

Check warning on line 664 in src/main/java/hudson/plugins/jira/JiraSite.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 664 is only partially covered, one branch is missing
? MAX_ALLOWED_ISSUES_FROM_JQL

Check warning on line 665 in src/main/java/hudson/plugins/jira/JiraSite.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered line

Line 665 is not covered by tests
: maxIssuesFromJqlSearch;
}

public int getMaxIssuesFromJqlSearch() {
return maxIssuesFromJqlSearch;
}

@SuppressWarnings("unused")
protected Object readResolve() throws FormException {
JiraSite jiraSite;
Expand Down Expand Up @@ -683,7 +707,7 @@
jiraSite.setDisableChangelogAnnotations(disableChangelogAnnotations);
jiraSite.setDateTimePattern(dateTimePattern);
jiraSite.setUseBearerAuth(useBearerAuth);

jiraSite.setMaxIssuesFromJqlSearch(maxIssuesFromJqlSearch);
return jiraSite;
}

Expand Down Expand Up @@ -760,7 +784,8 @@
/**
* This method only supports credential matching by credentialsId.
* Older methods are not and will not be supported as the credentials should have been migrated already.
* @param item can be <code>null</code> if top level
*
* @param item can be <code>null</code> if top level
* @param uiValidation if <code>true</code> and credentials not found at item level will not go up
*/
private StandardUsernamePasswordCredentials resolveCredentials(Item item, boolean uiValidation) {
Expand Down Expand Up @@ -1132,9 +1157,10 @@

/**
* Returns all versions for the given project key.
* @deprecated use {@link JiraSession#getVersions(String)}
*
* @param projectKey Project Key
* @return A set of JiraVersions
* @deprecated use {@link JiraSession#getVersions(String)}
*/
@Deprecated
public Set<ExtendedVersion> getVersions(String projectKey) {
Expand All @@ -1148,7 +1174,7 @@
/**
* Generates release notes for a given version.
*
* @param projectKey the project key
* @param projectKey the project key
* @param versionName the version
* @param filter Additional JQL Filter. Example: status in (Resolved,Closed)
* @return release notes
Expand Down Expand Up @@ -1234,9 +1260,9 @@
/**
* Adds new fix version to issues matching the jql.
*
* @param projectKey the project key
* @param projectKey the project key
* @param versionName the version
* @param query the query
* @param query the query
* @throws TimeoutException if too long
*/
public void addFixVersionToIssue(String projectKey, String versionName, String query) throws TimeoutException {
Expand All @@ -1251,10 +1277,10 @@
* Progresses all issues matching the JQL search, using the given workflow action. Optionally
* adds a comment to the issue(s) at the same time.
*
* @param jqlSearch the query
* @param jqlSearch the query
* @param workflowActionName the workflowActionName
* @param comment the comment
* @param console the console
* @param comment the comment
* @param console the console
* @throws TimeoutException TimeoutException if too long
*/
public boolean progressMatchingIssues(
Expand Down Expand Up @@ -1521,7 +1547,6 @@
// yes this class hierarchy can be a real big mess...

/**
*
* @param item the Jenkins {@link Item} can be a {@link Job} or {@link Folder}
* @return the parent as {@link ItemGroup} which can be {@link Jenkins} or {@link Folder}
*/
Expand Down Expand Up @@ -1567,6 +1592,7 @@
/**
* Gets the effective {@link JiraSite} associated with the given project
* and creates automatically jiraSession for each jiraSite found
*
* @return <code>null</code> if no such was found.
*/
@Nullable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@
<f:entry title="${%Jira comments timestamp format}" field="dateTimePattern">
<f:textbox />
</f:entry>
<f:entry title="${%Max Issues From Jql Search}" field="maxIssuesFromJqlSearch">
<f:number default="100" max="5000" clazz="non-negative-number-required" />
</f:entry>
<f:entry>
<f:validateButton title="${%Validate Settings}"
method="validate" with="url,credentialsId,groupVisibility,roleVisibility,useHTTPAuth,alternativeUrl,timeout,readTimeout,threadExecutorNumber,useBearerAuth" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<div>
Specifies the maximum number of issues to load from the JQL search query. If this number exceeds the limit configured in Jira, only the maximum allowed by Jira will be retrieved.
</div>
9 changes: 4 additions & 5 deletions src/test/java/JiraTester.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import com.atlassian.jira.rest.client.api.domain.Transition;
import com.atlassian.jira.rest.client.api.domain.User;
import hudson.plugins.jira.JiraRestService;
import hudson.plugins.jira.JiraSession;
import hudson.plugins.jira.JiraSite;
import hudson.plugins.jira.JiraSite.ExtendedAsynchronousJiraRestClientFactory;
import hudson.plugins.jira.extension.ExtendedJiraRestClient;
Expand Down Expand Up @@ -57,7 +56,7 @@ public static void main(String[] args) throws Exception {
// restService.createIssue("TESTPROJECT", "This is a test issue created using Jira jenkins plugin. Please
// ignore it.", "TESTUSER", components1, "test issue from Jira jenkins plugin");

final List<Issue> searchResults = restService.getIssuesFromJqlSearch("project = \"TESTPROJECT\"", 3);
final List<Issue> searchResults = restService.getIssuesFromJqlSearch("project = \"TESTPROJECT\"", 100);
for (Issue searchResult : searchResults) {
System.out.println("JQL search result: " + searchResult);
}
Expand Down Expand Up @@ -102,8 +101,8 @@ public static void main(String[] args) throws Exception {

private static void callUniq(final JiraRestService restService) throws Exception {
long start = System.currentTimeMillis();
List<Issue> issues =
restService.getIssuesFromJqlSearch("key in ('JENKINS-53320','JENKINS-51057')", JiraSession.MAX_ISSUES);
List<Issue> issues = restService.getIssuesFromJqlSearch(
"key in ('JENKINS-53320','JENKINS-51057')", JiraSite.MAX_ALLOWED_ISSUES_FROM_JQL);
long end = System.currentTimeMillis();
System.out.println("time uniq " + (end - start));
}
Expand All @@ -112,7 +111,7 @@ private static void callDuplicate(final JiraRestService restService) throws Exce
long start = System.currentTimeMillis();
List<Issue> issues = restService.getIssuesFromJqlSearch(
"key in ('JENKINS-53320','JENKINS-53320','JENKINS-53320','JENKINS-53320','JENKINS-53320','JENKINS-51057','JENKINS-51057','JENKINS-51057','JENKINS-51057','JENKINS-51057')",
JiraSession.MAX_ISSUES);
JiraSite.MAX_ALLOWED_ISSUES_FROM_JQL);
long end = System.currentTimeMillis();
System.out.println("time duplicate " + (end - start));
}
Expand Down
8 changes: 3 additions & 5 deletions src/test/java/JiraTesterBearerAuth.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import com.atlassian.jira.rest.client.api.domain.Transition;
import com.atlassian.jira.rest.client.api.domain.User;
import hudson.plugins.jira.JiraRestService;
import hudson.plugins.jira.JiraSession;
import hudson.plugins.jira.JiraSite;
import hudson.plugins.jira.JiraSite.ExtendedAsynchronousJiraRestClientFactory;
import hudson.plugins.jira.auth.BearerHttpAuthenticationHandler;
Expand Down Expand Up @@ -59,7 +58,7 @@ public static void main(String[] args) throws Exception {
// restService.createIssue("TESTPROJECT", "This is a test issue created using Jira jenkins plugin. Please
// ignore it.", "TESTUSER", components1, "test issue from Jira jenkins plugin");

final List<Issue> searchResults = restService.getIssuesFromJqlSearch("project = \"TESTPROJECT\"", 3);
final List<Issue> searchResults = restService.getIssuesFromJqlSearch("project = \"TESTPROJECT\"", 100);
for (Issue searchResult : searchResults) {
System.out.println("JQL search result: " + searchResult);
}
Expand Down Expand Up @@ -104,8 +103,7 @@ public static void main(String[] args) throws Exception {

private static void callUniq(final JiraRestService restService) throws Exception {
long start = System.currentTimeMillis();
List<Issue> issues =
restService.getIssuesFromJqlSearch("key in ('JENKINS-53320','JENKINS-51057')", JiraSession.MAX_ISSUES);
List<Issue> issues = restService.getIssuesFromJqlSearch("key in ('JENKINS-53320','JENKINS-51057')", 100);
long end = System.currentTimeMillis();
System.out.println("time uniq " + (end - start));
}
Expand All @@ -114,7 +112,7 @@ private static void callDuplicate(final JiraRestService restService) throws Exce
long start = System.currentTimeMillis();
List<Issue> issues = restService.getIssuesFromJqlSearch(
"key in ('JENKINS-53320','JENKINS-53320','JENKINS-53320','JENKINS-53320','JENKINS-53320','JENKINS-51057','JENKINS-51057','JENKINS-51057','JENKINS-51057','JENKINS-51057')",
JiraSession.MAX_ISSUES);
100);
long end = System.currentTimeMillis();
System.out.println("time duplicate " + (end - start));
}
Expand Down
10 changes: 5 additions & 5 deletions src/test/java/hudson/plugins/jira/JiraSessionTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ void replaceWithFixVersionByRegex() throws URISyntaxException, TimeoutException
ArrayList<Issue> issues = new ArrayList<>();
issues.add(getIssue(Arrays.asList("v1.0"), 1L));
issues.add(getIssue(Arrays.asList("v1.0", "v2.0", "v2.0.0"), 2L));
when(service.getIssuesFromJqlSearch(QUERY, JiraSession.MAX_ISSUES)).thenReturn(issues);
when(service.getIssuesFromJqlSearch(QUERY, 0)).thenReturn(issues);

jiraSession.replaceFixVersion(PROJECT_KEY, "/v1.*/", newVersion.getName(), QUERY);

Expand Down Expand Up @@ -99,7 +99,7 @@ void replaceFixVersion() throws URISyntaxException, TimeoutException {
issues.add(getIssue(Arrays.asList("v1.0"), 1L));
issues.add(getIssue(Arrays.asList("v1.0", "v1.0.0", "v2.0.0"), 2L));
issues.add(getIssue(null, 3L));
when(service.getIssuesFromJqlSearch(QUERY, JiraSession.MAX_ISSUES)).thenReturn(issues);
when(service.getIssuesFromJqlSearch(QUERY, 0)).thenReturn(issues);

jiraSession.replaceFixVersion(PROJECT_KEY, "v1.0", newVersion.getName(), QUERY);

Expand Down Expand Up @@ -183,7 +183,7 @@ void shouldAddVersionToAllIssues() throws Exception {
List<Issue> issues = new ArrayList<>();
issues.add(getIssue(Arrays.asList("v1.0"), 1L));
issues.add(getIssue(Arrays.asList("v2.0", "v3.0"), 2L));
when(service.getIssuesFromJqlSearch(QUERY, JiraSession.MAX_ISSUES)).thenReturn(issues);
when(service.getIssuesFromJqlSearch(QUERY, 0)).thenReturn(issues);

jiraSession.addFixVersion(PROJECT_KEY, newVersion.getName(), QUERY);

Expand Down Expand Up @@ -213,7 +213,7 @@ void shouldAddVersionWhenNoFixVersions() throws Exception {
when(jiraSession.getVersions(PROJECT_KEY)).thenReturn(myVersions);

List<Issue> issues = List.of(getIssue(null, 3L));
when(service.getIssuesFromJqlSearch(QUERY, JiraSession.MAX_ISSUES)).thenReturn(issues);
when(service.getIssuesFromJqlSearch(QUERY, 0)).thenReturn(issues);

jiraSession.addFixVersion(PROJECT_KEY, newVersion.getName(), QUERY);

Expand All @@ -233,7 +233,7 @@ void shouldNotCallUpdateIfNoIssues() throws Exception {
List<ExtendedVersion> myVersions = List.of(newVersion);
when(jiraSession.getVersions(PROJECT_KEY)).thenReturn(myVersions);

when(service.getIssuesFromJqlSearch(QUERY, JiraSession.MAX_ISSUES)).thenReturn(new ArrayList<>());
when(service.getIssuesFromJqlSearch(QUERY, 0)).thenReturn(new ArrayList<>());

jiraSession.addFixVersion(PROJECT_KEY, newVersion.getName(), QUERY);

Expand Down