feat: add server-side ExecutionChunk and FlatProcessorRecord DTOs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-03-31 19:02:47 +02:00
parent a96fe59840
commit 20c8e17843
4 changed files with 198 additions and 0 deletions

View File

@@ -37,6 +37,11 @@
<artifactId>spring-security-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>

View File

@@ -0,0 +1,42 @@
package com.cameleer3.server.core.storage.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.time.Instant;
import java.util.List;
import java.util.Map;
/**
* Chunk document: exchange envelope + list of FlatProcessorRecords.
* Mirrors cameleer3-common ExecutionChunk — replace with common lib import when available.
*/
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(JsonInclude.Include.NON_NULL)
public record ExecutionChunk(
String exchangeId,
String applicationName,
String agentId,
String routeId,
String correlationId,
String status,
Instant startTime,
Instant endTime,
Long durationMs,
String engineLevel,
String errorMessage,
String errorStackTrace,
String errorType,
String errorCategory,
String rootCauseType,
String rootCauseMessage,
Map<String, String> attributes,
String traceId,
String spanId,
String originalExchangeId,
String replayExchangeId,
int chunkSeq,
@JsonProperty("final") boolean isFinal,
List<FlatProcessorRecord> processors
) {}

View File

@@ -0,0 +1,42 @@
package com.cameleer3.server.core.storage.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import java.time.Instant;
import java.util.Map;
/**
* Flat processor execution record with seq/parentSeq for tree reconstruction.
* Mirrors cameleer3-common FlatProcessorRecord — replace with common lib import when available.
*/
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(JsonInclude.Include.NON_NULL)
public record FlatProcessorRecord(
int seq,
Integer parentSeq,
String parentProcessorId,
String processorId,
String processorType,
Integer iteration,
Integer iterationSize,
String status,
Instant startTime,
long durationMs,
String resolvedEndpointUri,
String inputBody,
String outputBody,
Map<String, String> inputHeaders,
Map<String, String> outputHeaders,
String errorMessage,
String errorStackTrace,
String errorType,
String errorCategory,
String rootCauseType,
String rootCauseMessage,
Map<String, String> attributes,
String circuitBreakerState,
Boolean fallbackTriggered,
Boolean filterMatched,
Boolean duplicateMessage
) {}

View File

@@ -0,0 +1,109 @@
package com.cameleer3.server.core.storage.model;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.junit.jupiter.api.Test;
import java.time.Instant;
import java.util.List;
import java.util.Map;
import static org.assertj.core.api.Assertions.assertThat;
class ExecutionChunkDeserializationTest {
private static final ObjectMapper MAPPER = new ObjectMapper().registerModule(new JavaTimeModule());
@Test
void roundTrip_fullChunk() throws Exception {
ExecutionChunk chunk = new ExecutionChunk(
"ex-1", "order-service", "pod-1", "order-route",
"corr-1", "COMPLETED",
Instant.parse("2026-03-31T10:00:00Z"),
Instant.parse("2026-03-31T10:00:01Z"), 1000L,
"REGULAR",
null, null, null, null, null, null,
Map.of("orderId", "ORD-1"),
"trace-1", "span-1", null, null,
2, true,
List.of(new FlatProcessorRecord(
1, null, null, "log1", "log",
null, null, "COMPLETED",
Instant.parse("2026-03-31T10:00:00.100Z"), 5L,
null, "body", null, null, null,
null, null, null, null, null, null,
null, null, null, null, null)));
String json = MAPPER.writeValueAsString(chunk);
ExecutionChunk deserialized = MAPPER.readValue(json, ExecutionChunk.class);
assertThat(deserialized.exchangeId()).isEqualTo("ex-1");
assertThat(deserialized.isFinal()).isTrue();
assertThat(deserialized.chunkSeq()).isEqualTo(2);
assertThat(deserialized.processors()).hasSize(1);
assertThat(deserialized.processors().get(0).seq()).isEqualTo(1);
assertThat(deserialized.processors().get(0).processorId()).isEqualTo("log1");
assertThat(deserialized.attributes()).containsEntry("orderId", "ORD-1");
}
@Test
void roundTrip_runningChunkWithIterations() throws Exception {
ExecutionChunk chunk = new ExecutionChunk(
"ex-2", "app", "agent-1", "route-1",
"ex-2", "RUNNING",
Instant.parse("2026-03-31T10:00:00Z"),
null, null, "REGULAR",
null, null, null, null, null, null,
null, null, null, null, null,
0, false,
List.of(
new FlatProcessorRecord(
1, null, null, "split1", "split",
null, 3, "COMPLETED",
Instant.parse("2026-03-31T10:00:00Z"), 100L,
null, null, null, null, null,
null, null, null, null, null, null,
null, null, null, null, null),
new FlatProcessorRecord(
2, 1, "split1", "log1", "log",
0, null, "COMPLETED",
Instant.parse("2026-03-31T10:00:00Z"), 5L,
null, "item-0", null, null, null,
null, null, null, null, null, null,
null, null, null, null, null),
new FlatProcessorRecord(
3, 1, "split1", "log1", "log",
1, null, "COMPLETED",
Instant.parse("2026-03-31T10:00:00Z"), 5L,
null, "item-1", null, null, null,
null, null, null, null, null, null,
null, null, null, null, null)));
String json = MAPPER.writeValueAsString(chunk);
ExecutionChunk deserialized = MAPPER.readValue(json, ExecutionChunk.class);
assertThat(deserialized.isFinal()).isFalse();
assertThat(deserialized.processors()).hasSize(3);
FlatProcessorRecord split = deserialized.processors().get(0);
assertThat(split.iterationSize()).isEqualTo(3);
assertThat(split.parentSeq()).isNull();
FlatProcessorRecord child0 = deserialized.processors().get(1);
assertThat(child0.parentSeq()).isEqualTo(1);
assertThat(child0.parentProcessorId()).isEqualTo("split1");
assertThat(child0.iteration()).isEqualTo(0);
}
@Test
void deserialize_unknownFieldsIgnored() throws Exception {
String json = """
{"exchangeId":"ex-1","routeId":"r1","status":"COMPLETED",
"startTime":"2026-03-31T10:00:00Z","chunkSeq":0,"final":true,
"futureField":"ignored","processors":[]}
""";
ExecutionChunk chunk = MAPPER.readValue(json, ExecutionChunk.class);
assertThat(chunk.exchangeId()).isEqualTo("ex-1");
assertThat(chunk.isFinal()).isTrue();
}
}