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

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new e48face394f1 CAMEL-25943: camel-jira - a consumer with maxResults 0 or 
less searches forever (#28469)
e48face394f1 is described below

commit e48face394f190f43df2bba2a4190c1badaeee93
Author: allthingssecurity <[email protected]>
AuthorDate: Mon Oct 26 13:30:53 2026 +0530

    CAMEL-25943: camel-jira - a consumer with maxResults 0 or less searches 
forever (#28469)
    
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
---
 .../jira/consumer/AbstractJiraConsumer.java        |   4 +-
 .../WatchUpdatesConsumerMaxResultsZeroTest.java    | 130 +++++++++++++++++++++
 2 files changed, 133 insertions(+), 1 deletion(-)

diff --git 
a/components/camel-jira/src/main/java/org/apache/camel/component/jira/consumer/AbstractJiraConsumer.java
 
b/components/camel-jira/src/main/java/org/apache/camel/component/jira/consumer/AbstractJiraConsumer.java
index 017d4f67fe78..02b842aa2ba1 100644
--- 
a/components/camel-jira/src/main/java/org/apache/camel/component/jira/consumer/AbstractJiraConsumer.java
+++ 
b/components/camel-jira/src/main/java/org/apache/camel/component/jira/consumer/AbstractJiraConsumer.java
@@ -122,7 +122,9 @@ public abstract class AbstractJiraConsumer extends 
ScheduledBatchPollingConsumer
     protected Queue<Issue> getIssues(String jql, int start, int maxPerQuery, 
int maxResults) {
         LOG.debug("Start indexing current JIRA issues...");
 
-        if (maxResults < maxPerQuery) {
+        // a page of maxResults issues if that is fewer: not when maxResults 
is ignored, a page of 0 issues would never
+        // move the search forward
+        if (maxResults > 0 && maxResults < maxPerQuery) {
             maxPerQuery = maxResults;
         }
 
diff --git 
a/components/camel-jira/src/test/java/org/apache/camel/component/jira/consumer/WatchUpdatesConsumerMaxResultsZeroTest.java
 
b/components/camel-jira/src/test/java/org/apache/camel/component/jira/consumer/WatchUpdatesConsumerMaxResultsZeroTest.java
new file mode 100644
index 000000000000..85aac2aad5e1
--- /dev/null
+++ 
b/components/camel-jira/src/test/java/org/apache/camel/component/jira/consumer/WatchUpdatesConsumerMaxResultsZeroTest.java
@@ -0,0 +1,130 @@
+/*
+ * 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 org.apache.camel.component.jira.consumer;
+
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import com.atlassian.jira.rest.client.api.JiraRestClient;
+import com.atlassian.jira.rest.client.api.JiraRestClientFactory;
+import com.atlassian.jira.rest.client.api.SearchRestClient;
+import com.atlassian.jira.rest.client.api.domain.Issue;
+import com.atlassian.jira.rest.client.api.domain.Priority;
+import com.atlassian.jira.rest.client.api.domain.SearchResult;
+import io.atlassian.util.concurrent.Promises;
+import org.apache.camel.CamelContext;
+import org.apache.camel.EndpointInject;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.jira.JiraComponent;
+import org.apache.camel.component.jira.JiraConstants;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.spi.Registry;
+import org.apache.camel.test.junit6.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import static org.apache.camel.component.jira.JiraConstants.JIRA;
+import static 
org.apache.camel.component.jira.JiraConstants.JIRA_REST_CLIENT_FACTORY;
+import static 
org.apache.camel.component.jira.JiraTestConstants.JIRA_CREDENTIALS;
+import static org.apache.camel.component.jira.JiraTestConstants.PROJECT;
+import static 
org.apache.camel.component.jira.JiraTestConstants.WATCHED_COMPONENTS;
+import static org.apache.camel.component.jira.Utils.createIssue;
+import static org.apache.camel.component.jira.Utils.setPriority;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.when;
+
+/**
+ * maxResults=0 (ignored, as for any value <= 0) on Jira Server / Data Center, 
with more issues than one page of the
+ * search: the consumer reads every page instead of asking for pages of 0 
issues at the same start index forever.
+ */
+@ExtendWith(MockitoExtension.class)
+class WatchUpdatesConsumerMaxResultsZeroTest extends CamelTestSupport {
+
+    private static final int ISSUES = 60;
+
+    private final List<Issue> issues = new CopyOnWriteArrayList<>();
+
+    @Mock
+    private JiraRestClient jiraClient;
+
+    @Mock
+    private JiraRestClientFactory jiraRestClientFactory;
+
+    @Mock
+    private SearchRestClient searchRestClient;
+
+    @EndpointInject("mock:result")
+    private MockEndpoint mockResult;
+
+    @Override
+    protected void bindToRegistry(Registry registry) {
+        registry.bind(JIRA_REST_CLIENT_FACTORY, jiraRestClientFactory);
+    }
+
+    @Override
+    protected CamelContext createCamelContext() throws Exception {
+        for (long id = 1; id <= ISSUES; id++) {
+            issues.add(createIssue(id));
+        }
+        // the search of Jira Server / Data Center: a page of at most 
maxResults issues from startAt, and the total of
+        // the search (maxResults=0 returns no issue, only the total)
+        AtomicInteger sameStart = new AtomicInteger();
+        AtomicInteger previousStart = new AtomicInteger(-1);
+        when(searchRestClient.searchJql(any(), any(), any(), 
any())).then(invocation -> {
+            int maxResults = invocation.getArgument(1);
+            int startAt = invocation.getArgument(2);
+            if (previousStart.getAndSet(startAt) == startAt && 
sameStart.incrementAndGet() > 10) {
+                throw new IllegalStateException("The search is repeated at 
startAt " + startAt);
+            }
+            List<Issue> page = issues.subList(Math.min(startAt, ISSUES), 
Math.min(startAt + maxResults, ISSUES));
+            return Promises.promise(new SearchResult(startAt, maxResults, 
ISSUES, List.copyOf(page)));
+        });
+        when(jiraClient.getSearchClient()).thenReturn(searchRestClient);
+        when(jiraRestClientFactory.createWithBasicHttpAuthentication(any(), 
any(), any())).thenReturn(jiraClient);
+
+        CamelContext camelContext = super.createCamelContext();
+        camelContext.disableJMX();
+        camelContext.addComponent(JIRA, new JiraComponent(camelContext));
+        return camelContext;
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            @Override
+            public void configure() {
+                from("jira://watchUpdates?jiraUrl=" + JIRA_CREDENTIALS + 
"&jql=project=" + PROJECT
+                     + "&delay=500&maxResults=0&watchedFields=" + 
WATCHED_COMPONENTS)
+                        .to(mockResult);
+            }
+        };
+    }
+
+    @Test
+    void changeOfAnIssueOnTheSecondPageIsConsumed() throws Exception {
+        Issue changed = setPriority(issues.get(54), new Priority(null, 4L, 
"High", null, null, null));
+        mockResult.expectedBodiesReceived(changed.getPriority());
+        mockResult.expectedHeaderReceived(JiraConstants.ISSUE_KEY, 
changed.getKey());
+
+        issues.set(54, changed);
+
+        mockResult.assertIsSatisfied();
+    }
+}

Reply via email to