shashank created CAMEL-25576:
--------------------------------

             Summary: camel-box - event-logs/getEnterpriseEvents returns only 
the first chunk of the event log (at most 500 events); the events after it are 
dropped silently and cannot be requested
                 Key: CAMEL-25576
                 URL: https://issues.apache.org/jira/browse/CAMEL-25576
             Project: Camel
          Issue Type: Bug
          Components: camel-box
            Reporter: shashank
            Assignee: shashank


{{BoxEventLogsManager.getEnterpriseEvents}} (lines 68-96 at main c3829069cb72), 
the only method of the {{box:event-logs}} API:
{code:java}
EnterpriseEventsRequest request = new EnterpriseEventsRequest();
request.position(position).after(after).before(before).types(types);
EventLog eventLog = EventLog.getEnterpriseEvents(boxConnection, request);

List<BoxEvent> results = new ArrayList<>();
for (BoxEvent event : eventLog) {
    results.add(event);
}
return results;
{code}
An {{EventLog}} of box-java-sdk 4.16.4 is one response of the events endpoint: 
{{EnterpriseEventsRequest}} asks for {{limit = 500}} (the constant in its 
constructor), and the response has the events of that chunk and 
{{getNextStreamPosition()}} for the next one. The manager sends one request and 
returns the events of that response. It does not read the next stream position 
and does not return it either (the result is a plain {{List<BoxEvent>}}).

So when more than 500 events lie between {{after}} and {{before}}, the caller 
gets the first 500 and has no means to ask for the rest: the {{position}} 
parameter needs a stream position, which only the dropped {{EventLog}} had. 
Nothing is logged. The javadoc of the method says "@return A list of all the 
events that met the given criteria", and for {{position}}: "May be null in 
which case all events within bounds returned".

h3. Reproduction

New {{BoxEventLogsManagerPagingTest}} (3 tests) in camel-box-component. A real 
{{BoxAPIConnection}} gets a {{RequestInterceptor}} that answers the events 
request from memory like the service: at most 500 events from the requested 
{{stream_position}}, and {{next_stream_position}} (the fake fails the test 
after 20 requests). So the SDK parses real responses. On main all 3 fail:
* {{returnsTheEventsOfEveryChunk}} (1200 events): {{all the events within 
bounds, not only the first chunk ==> expected: <1200> but was: <500>}}.
* {{startsAtThePositionOfTheCallerAndReadsToTheEnd}} (position 100 of 1200): 
1100 events expected, 500 returned.
* {{stopsWhenTheServiceDoesNotMoveThePosition}}: {{expected: <2> but was: <1>}} 
(requests; it checks that the new loop ends when a response repeats the 
position it was asked for, and that events returned twice are kept once).

h3. Proposed fix

In {{BoxEventLogsManager.getEnterpriseEvents}}: read on from 
{{getNextStreamPosition()}} until a chunk is empty, which is the end of the 
events within bounds; also stop when the service returns no next position or 
the position it was asked for. The events are collected in a {{LinkedHashSet}} 
({{BoxResource.equals}} compares the id), so an event that the service returns 
in two chunks is kept once and the order of the service is kept. The signature 
is unchanged; the javadoc of the return value now says that the log is read 
chunk by chunk and kept in memory.

Behaviour change, with a 4.24 upgrade-guide entry: the operation returns all 
the events between {{after}} and {{before}}. Cost: one request per chunk of 500 
events and one more that finds the end (two requests where main sent one, when 
everything fits in one chunk). A wide {{after}} / {{before}} window now returns 
every event in one list, with that many requests and more memory; the upgrade 
note and the javadoc say to narrow the window for a large event log. The loop 
has no limit of its own on the number of chunks: it ends on an empty chunk, on 
a missing next position or on a position that did not move. I end the loop on 
an empty chunk and not on a chunk shorter than the limit because I could not 
confirm from the SDK that a short chunk always means the end. camel-box-api and 
camel-box-component unit tests with the fix: 6, 0 failures (the integration 
tests need a Box account and were not run; nothing was run against Box).

Found with a Lean 4 model of a position-paged service: the event log is a list, 
a response is a {{take}} / {{drop}} slice of it and the position after it. 
Property: the result is the log from the position of the caller to its end, 
every event once and in order. Counterexample for main: with chunks of two, 
{{[1, 2, 3]}} gives {{[1, 2]}} ({{cex_third_event_dropped}}); with a chunk of 
500, 501 events give 500 ({{cex_501_events}}). In the model, reading chunk 
after chunk returns the list from the start position to its end 
({{fix_meets_spec}}), and the same as main when the events fit in one chunk 
({{fix_eq_main_one_chunk}}). The model says nothing about the real service. 
Then confirmed with the real class as described.

Affected: main (4.23.0-SNAPSHOT at c3829069cb72), and the release branches 
camel-4.18.x (c3fd5317a5a0), camel-4.22.x (2821f284056c) and camel-4.23.x 
(197e70656a41): lines 66-95 of {{BoxEventLogsManager}} (the javadoc of the 
return value and the whole method body) are identical to main on all three. 
Read, not run.

Duplicate check (2026-10-12): JIRA text "box getEnterpriseEvents" (none), "box 
enterprise events position" (2, unrelated: CAMEL-12998, CAMEL-12297); GitHub 
pull requests "box enterprise events": none; no open pull request touches 
camel-box.

_Filed with Claude Code on behalf of allthingssecurity._




--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to