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:
@@ -37,6 +37,11 @@
|
|||||||
<artifactId>spring-security-core</artifactId>
|
<artifactId>spring-security-core</artifactId>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||||
|
<artifactId>jackson-datatype-jsr310</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.junit.jupiter</groupId>
|
<groupId>org.junit.jupiter</groupId>
|
||||||
<artifactId>junit-jupiter</artifactId>
|
<artifactId>junit-jupiter</artifactId>
|
||||||
|
|||||||
@@ -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
|
||||||
|
) {}
|
||||||
@@ -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
|
||||||
|
) {}
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user