Add context map to mutation response payloads#258
Merged
Conversation
f5b5364 to
95ccab3
Compare
context map to mutation response payloadscontext map to V2/V3 mutation response payloads
context map to V2/V3 mutation response payloadscontext map to mutation response payloads
e2a8caf to
95ccab3
Compare
Query payloads (`EdgePayload`, `EdgeCountPayload`, `EdgeAggPayload`) already carry a `context: Map<String, Any?>` slot for per-item metadata; add the same slot to mutation payloads. Nullable with `@JsonInclude(NON_NULL)` — existing callers see byte-identical responses. The key is only emitted once a caller populates it (see #259, #254). - `core.MutationResult.context: Map<String, Any?>? = null` - V3 `EdgeMutationResponse.Item` and `MultiEdgeMutationResponse.Item`: nullable + `@field:JsonInclude(Include.NON_NULL)` - V2 `engine.edge.MutationResultItem`: same - `from(...)` factories thread `MutationResult.context` through Tests (ObjectSource-driven, one class per Item type): - `EdgeMutationResponseItemContextTest` (core) — V3 edge item - `MultiEdgeMutationResponseItemContextTest` (core) — V3 multi-edge item - `MutationResultItemContextTest` (engine) — V2 mutation result item Each class has two parameterized methods (`Item serializes`, `Item deserializes`) whose YAML cases list input and expected JSON/context — makes intent readable from the test data alone. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
95ccab3 to
f3389bc
Compare
Contributor
Author
|
Merging |
em3s
added a commit
that referenced
this pull request
Apr 23, 2026
Add an opt-in header (`AB-Include-Mutation-Context: true`, case-insensitive) that enriches mutation responses with an empty `context` map. Without the header, `context` stays `null` and is omitted from JSON — wire-identical for existing callers. End-to-end tests drive the full request → response path to confirm #258 is non-breaking on the wire. - New `mapToResponseEntity(exchange: ServerWebExchange)` overload on `Mono<T>` that reads the header and enriches V2 `MutationResult`, V3 `EdgeMutationResponse`, and V3 `MultiEdgeMutationResponse` items - V3 `EdgeMutationController` / `MultiEdgeMutationController` now accept `ServerWebExchange` and route through the new overload - `ResponseEntityExtensionsTest` (unit) — V2 + V3 enrichment, case-insensitive header, pass-through for non-mutation bodies - `MutationContextOptInE2ETest` (e2e via `WebTestClient`) — V3 sync endpoints, header on/off Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
em3s
added a commit
that referenced
this pull request
Apr 23, 2026
Add an opt-in header (`AB-Include-Mutation-Context: true`, case-insensitive, parsed as Boolean) that flows from V2/V3 mutation controllers into the service layer. When set, the service attaches an empty `context` map to each response item; otherwise `context` stays `null` and is omitted from JSON — wire-identical for existing callers. End-to-end tests drive both V3 and V2 paths to confirm #258 is non-breaking on the wire. Server stays thin: controllers receive the header as a typed Boolean and pass it through. The enrichment itself happens in `MutationService` (V3) and `Graph` (V2), right where `MutationResult` is assembled. - V3 `MutationService.mutate(..., includeContext)` — flag threads through `.collectList()` and maps `it.copy(context = emptyMap())` when set - V2 `Graph.mutate(...)` and its `upsert` / `update` / `delete` / id-variant overloads — same pattern - V3 controllers (`EdgeMutationController`, `MultiEdgeMutationController`) and V2 `EdgeController` receive `@RequestHeader(INCLUDE_MUTATION_CONTEXT_HEADER, required = false, defaultValue = "false") includeContext: Boolean` - Other V2 service-label controllers (`ServiceLabelEdge*`) keep the default `false` via `Graph`'s default parameter — no call-site changes - `ResponseEntityExtensions.mapToResponseEntity()` restored to its original one-liner; only `INCLUDE_MUTATION_CONTEXT_HEADER` const remains for controllers to reference - `ResponseEntityExtensionsTest` removed — the enrichment logic now lives in engine code and is exercised through the e2e test - `MutationContextOptInE2ETest` covers V3 edge/multi-edge sync and V2 edge endpoints, header on/off Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
em3s
added a commit
that referenced
this pull request
Apr 23, 2026
Add an opt-in header `AB-Include-Mutation-Context` (case-insensitive, parsed as Boolean) that attaches an empty `context` map to each mutation response item. Without it, `context` stays `null` and is omitted from JSON — wire-identical for existing callers. End-to-end tests cover both V3 and V2 paths to confirm #258 is non-breaking on the wire. V3 stays zero-touch on controllers: the flag rides on `RequestContext`, which the existing argument resolver already assembles from headers. `MutationService` checks `requestContext.includeContext` when materialising `MutationResult`s. V2 `Graph.upsert/update/delete` gain an `includeContext: Boolean = false` parameter that threads into `Graph.mutate`. V2 `EdgeController` reads the header and forwards it; other V2 service-label controllers pick up the default (`false`) without any call-site change. - `HttpHeaderConstants.INCLUDE_MUTATION_CONTEXT` — new header name - `engine.context.RequestContext.includeContext` — new field, default `false` - `ServerRequestContextArgumentResolver` — reads the header - `engine.service.MutationService.mutate` — reads flag from `requestContext` - `v2.engine.Graph.mutate` + `upsert`/`update`/`delete` overloads — `includeContext` parameter, applies `emptyMap()` copy on the list - V2 `EdgeController` — `@RequestHeader(... INCLUDE_MUTATION_CONTEXT, defaultValue = "false")` - `MutationContextOptInE2ETest` — V3 edge/multi-edge sync + V2 edge, header on/off, case-insensitive Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
em3s
added a commit
that referenced
this pull request
Apr 23, 2026
Add an opt-in header `AB-Include-Mutation-Context` (case-insensitive, parsed as Boolean) that attaches an empty `context` map to each mutation response item. Without it, `context` stays `null` and is omitted from JSON — wire-identical for existing callers. End-to-end tests cover both V3 and V2 paths to confirm #258 is non-breaking on the wire. V3 stays zero-touch on controllers: the flag rides on `RequestContext`, which the existing argument resolver already assembles from headers. `MutationService` checks `requestContext.includeContext` when materialising `MutationResult`s. V2 `Graph.upsert/update/delete` gain an `includeContext: Boolean = false` parameter that threads into `Graph.mutate`. V2 `EdgeController` reads the header and forwards it; other V2 service-label controllers pick up the default (`false`) without any call-site change. - `HttpHeaderConstants.INCLUDE_MUTATION_CONTEXT` — new header name - `engine.context.RequestContext.includeContext` — new field, default `false` - `ServerRequestContextArgumentResolver` — reads the header - `engine.service.MutationService.mutate` — reads flag from `requestContext` - `v2.engine.Graph.mutate` + `upsert`/`update`/`delete` overloads — `includeContext` parameter, applies `emptyMap()` copy on the list - V2 `EdgeController` — `@RequestHeader(... INCLUDE_MUTATION_CONTEXT, defaultValue = "false")` - `MutationContextOptInE2ETest` — V3 edge/multi-edge sync + V2 edge, header on/off, case-insensitive Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
em3s
added a commit
that referenced
this pull request
Apr 23, 2026
Add an opt-in header `AB-Include-Mutation-Context` (case-insensitive, parsed as Boolean) that attaches an empty `context` map to each mutation response item. Without it, `context` stays `null` and is omitted from JSON — wire-identical for existing callers. End-to-end tests cover both V3 and V2 paths to confirm #258 is non-breaking on the wire. V3 stays zero-touch on controllers: the flag rides on `RequestContext`, which the existing argument resolver already assembles from headers. `MutationService` checks `requestContext.includeContext` when materialising `MutationResult`s. V2 `Graph.upsert/update/delete` gain an `includeContext: Boolean = false` parameter that threads into `Graph.mutate`. V2 `EdgeController` reads the header and forwards it; other V2 service-label controllers pick up the default (`false`) without any call-site change. - `HttpHeaderConstants.INCLUDE_MUTATION_CONTEXT` — new header name - `engine.context.RequestContext.includeContext` — new field, default `false` - `ServerRequestContextArgumentResolver` — reads the header - `engine.service.MutationService.mutate` — reads flag from `requestContext` - `v2.engine.Graph.mutate` + `upsert`/`update`/`delete` overloads — `includeContext` parameter, applies `emptyMap()` copy on the list - V2 `EdgeController` — `@RequestHeader(... INCLUDE_MUTATION_CONTEXT, defaultValue = "false")` - `MutationContextOptInE2ETest` — V3 edge/multi-edge sync + V2 edge, header on/off, case-insensitive Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced Apr 23, 2026
em3s
added a commit
that referenced
this pull request
Apr 23, 2026
Closes #253. Follows #258 and #259. When the caller sends `AB-Include-Mutation-Context`, mutation responses include the Put/Delete/Increment operations issued to HBase under `context["storage_ops"]`, so shadow tests can compare at the operation level. - `core.storage.StorageOp` — sealed hierarchy for hex-encoded ops - `engine.storage.StorageOpCollector` — thread-safe appender with a per-RMW cap so an opt-in request can't produce an unbounded payload - Collector allocated only when `RequestContext.includeContext` is true, threaded through `MutationContext` and `TableBinding.write(..., collector)` - V3 `MutationService.mutate` — passes collector into `readModifyWrite`, attaches `context = mapOf("storage_ops" to ops)` on completion - V2 `Graph.mutate` — same pattern via `AbstractLabel` and `HBaseHashLabel.handleDeferredRequests` - `HBaseOpConverter` — HBase `Put` / `Delete` / `Increment` → `StorageOp` - `CdcContext.storageOps` is `@JsonIgnore` so captured ops never leak into CDC / Kafka payloads Tests: - `HBaseOpConverterTest` — conversion round-trip - `CdcContextSerializationTest` — CDC payload excludes `storageOps` - `EdgeMutationStorageOpsSpec` — V3 edge + multi-edge e2e, header on/off - `V2EdgeStorageOpsSpec` — V2 `/graph/v2/edge` e2e; service-scoped V2 controllers keep the default and are out of scope Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
em3s
added a commit
that referenced
this pull request
Apr 23, 2026
Closes #253. Follows #258 and #259. When the caller sends `AB-Include-Mutation-Context`, mutation responses include the Put/Delete/Increment operations issued to HBase under `context["storage_ops"]`, so shadow tests can compare at the operation level. - `core.storage.StorageOp` — sealed hierarchy for hex-encoded ops - `engine.storage.StorageOpCollector` — thread-safe appender with a per-RMW cap so an opt-in request can't produce an unbounded payload - V3 (`MutationService`): the opt-in check happens once at the top; a per-RMW `StorageOpCollector?` rides the rest of the chain. `readModifyWrite(..., collector)` has no Boolean flag — null means don't capture, non-null means do. - V2 (`Graph` / `AbstractLabel` / `NilLabel` / `LocalBackedJdbcHashLabel`): `mutate`/`upsert`/`update`/`delete` take a `collectorFactory: (() -> StorageOpCollector)? = null`. `AbstractLabel` invokes the factory per edge, giving each `MutationResultItem` its own `storage_ops`. No Boolean `includeContext` parameter anywhere. - V2 `EdgeController` builds the factory from `RequestContext.includeContext` once and forwards it. - `HBaseOpConverter` — HBase `Put` / `Delete` / `Increment` → `StorageOp` - `CdcContext.storageOps` is `@JsonIgnore` so captured ops never leak into CDC / Kafka payloads Tests: - `HBaseOpConverterTest` — conversion round-trip - `CdcContextSerializationTest` — CDC payload excludes `storageOps` - `EdgeMutationStorageOpsSpec` — V3 edge + multi-edge e2e, header on/off - `V2EdgeStorageOpsSpec` — V2 `/graph/v2/edge` e2e; service-scoped V2 controllers keep the default and are out of scope Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
em3s
added a commit
that referenced
this pull request
Apr 23, 2026
Follows #258 and #259. Introduces the request-scoped collector mechanism that will surface storage-level operations on mutation responses (see #253). This PR lands the API surface and the plumbing only — `StorageOpCollector.collect` is a no-op scaffold, so header-on requests emit `context.storage_ops = []` but no actual ops are captured yet. - `core.storage.StorageOp` — sealed hierarchy (`Put`/`Delete`/`Increment` with hex cells/deltas) - `engine.storage.StorageOpCollector` — thread-safe appender with a per-RMW cap. Backend-agnostic: `collect(request, table)` is a no-op; interpretation lands in the follow-up - V3 `MutationService.readModifyWrite` — takes `collector: StorageOpCollector?`; `MutationService.mutate` gates on `RequestContext.includeContext`, creates a per-RMW collector, and attaches `context = mapOf("storage_ops" to ops)` - V2 `Graph.mutate` + `upsert`/`update`/`delete` (and id-variants) — take `collectorFactory: (() -> StorageOpCollector)? = null`; `AbstractLabel` invokes the factory per edge so each `MutationResultItem` gets its own `storage_ops` - V2 `Label` / `NilLabel` / `LocalBackedJdbcHashLabel` — signature updates for the factory - V2 `EdgeController` — `RequestContext.collectorFactory()` extension builds the factory from the header - `HBaseHashLabel.handleDeferredRequests` — one line: `collector?.collectAll(deferredRequests, t.edge.name.nameAsString)` - `CdcContext.storageOps` — nullable + `@JsonIgnore` so the field exists in the pipeline but CDC/Kafka payloads never carry it Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
em3s
added a commit
that referenced
this pull request
Apr 23, 2026
Closes #253. Follows #258, #259, #261. Extends the scaffolded `StorageOp` marker with the backend-facing shape and teaches `StorageOpCollector.collect` to interpret raw HBase requests. No changes to call sites — the plumbing was done in #261. When the `AB-Include-Mutation-Context` header is set, mutation responses include the captured ops under `context["storage_ops"]`. - `StorageOp` — adds `Put`/`Delete`/`Increment` data classes with `Cell`/`Delta` nested types, hex-encoded row/family/qualifier/value - `StorageOpCollector.collect(request, table)` — `when` on `Put`/`Delete`/`Increment`, extracts cells/deltas, hex-encodes row/family/qualifier/value. Unknown request types are silently dropped so new backends can join without touching call sites. Tests: - `StorageOpCollectorTest` — data-driven via `@ObjectSource` (Put/Delete/Delete-columns/Increment/multi-byte) + two narrow `@Test` cases (non-Mutation skip, truncation cap) - `CdcContextSerializationTest` — `storageOps` excluded from CDC - `EdgeMutationStorageOpsSpec` — V3 edge + multi-edge e2e with header on, asserts `context.storage_ops[0].rowHex` shape - `V2EdgeStorageOpsSpec` — V2 `/graph/v2/edge` e2e with header on Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
em3s
added a commit
that referenced
this pull request
Apr 23, 2026
Closes #253. Follows #258, #259, #261. Extends the scaffolded `StorageOp` marker with the backend-facing shape and teaches `StorageOpCollector.collect` to interpret raw HBase requests. No changes to call sites — the plumbing was done in #261. When the `AB-Include-Mutation-Context` header is set, mutation responses include the captured ops under `context["storage_ops"]`. - `StorageOp` — adds `Put`/`Delete`/`Increment` data classes with `Cell`/`Delta` nested types, hex-encoded row/family/qualifier/value - `StorageOpCollector.collect(request, table)` — `when` on `Put`/`Delete`/`Increment`, extracts cells/deltas, hex-encodes row/family/qualifier/value. Unknown request types are silently dropped so new backends can join without touching call sites. Tests: - `StorageOpCollectorTest` — data-driven via `@ObjectSource` (Put/Delete/Delete-columns/Increment/multi-byte) + two narrow `@Test` cases (non-Mutation skip, truncation cap) - `CdcContextSerializationTest` — `storageOps` excluded from CDC - `EdgeMutationStorageOpsSpec` — V3 edge + multi-edge e2e with header on, asserts `context.storage_ops[0].rowHex` shape - `V2EdgeStorageOpsSpec` — V2 `/graph/v2/edge` e2e with header on Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
em3s
added a commit
that referenced
this pull request
Apr 23, 2026
Closes #253. Follows #258, #259, #261. Extends the scaffolded `StorageOp` marker with the backend-facing shape and teaches `StorageOpCollector.collect` to interpret raw HBase requests. No changes to call sites — the plumbing was done in #261. When the `AB-Include-Mutation-Context` header is set, mutation responses include the captured ops under `context["storage_ops"]`. - `StorageOp` — adds `Put`/`Delete`/`Increment` data classes with `Cell`/`Delta` nested types, hex-encoded row/family/qualifier/value - `StorageOpCollector.collect(request, table)` — `when` on `Put`/`Delete`/`Increment`, extracts cells/deltas, hex-encodes row/family/qualifier/value. Unknown request types are silently dropped so new backends can join without touching call sites. Tests: - `StorageOpCollectorTest` — data-driven via `@ObjectSource` (Put/Delete/Delete-columns/Increment/multi-byte) + two narrow `@Test` cases (non-Mutation skip, truncation cap) - `CdcContextSerializationTest` — `storageOps` excluded from CDC - `EdgeMutationStorageOpsSpec` — V3 edge + multi-edge e2e with header on, asserts `context.storage_ops[0].rowHex` shape - `V2EdgeStorageOpsSpec` — V2 `/graph/v2/edge` e2e with header on Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
em3s
added a commit
that referenced
this pull request
Apr 23, 2026
Closes #253. Follows #258, #259, #261. Extends the scaffolded `StorageOp` marker with the backend-facing shape and teaches `StorageOpCollector.collect` to interpret raw HBase requests. No changes to call sites — the plumbing was done in #261. When the `AB-Include-Mutation-Context` header is set, mutation responses include the captured ops under `context["storage_ops"]`. - `StorageOp` — adds `Put`/`Delete`/`Increment` data classes with `Cell`/`Delta` nested types, hex-encoded row/family/qualifier/value - `StorageOpCollector.collect(request, table)` — `when` on `Put`/`Delete`/`Increment`, extracts cells/deltas, hex-encodes row/family/qualifier/value. Unknown request types are silently dropped so new backends can join without touching call sites. Tests: - `StorageOpCollectorTest` — data-driven via `@ObjectSource` (Put/Delete/Delete-columns/Increment/multi-byte) + two narrow `@Test` cases (non-Mutation skip, truncation cap) - `CdcContextSerializationTest` — `storageOps` excluded from CDC - `EdgeMutationStorageOpsSpec` — V3 edge + multi-edge e2e with header on, asserts `context.storage_ops[0].rowHex` shape - `V2EdgeStorageOpsSpec` — V2 `/graph/v2/edge` e2e with header on Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
em3s
added a commit
that referenced
this pull request
Apr 23, 2026
Closes #253. Follows #258, #259, #261. Extends the scaffolded `StorageOp` marker with the backend-facing shape and teaches `StorageOpCollector.collect` to interpret raw HBase requests. No changes to call sites — the plumbing was done in #261. When the `AB-Include-Mutation-Context` header is set, mutation responses include the captured ops under `context["storage_ops"]`. - `StorageOp` — adds `Put`/`Delete`/`Increment` data classes with `Cell`/`Delta` nested types, hex-encoded row/family/qualifier/value - `StorageOpCollector.collect(request, table)` — `when` on `Put`/`Delete`/`Increment`, extracts cells/deltas, hex-encodes row/family/qualifier/value. Unknown request types are silently dropped so new backends can join without touching call sites. Tests: - `StorageOpCollectorTest` — data-driven via `@ObjectSource` (Put/Delete/Delete-columns/Increment/multi-byte) + two narrow `@Test` cases (non-Mutation skip, truncation cap) - `CdcContextSerializationTest` — `storageOps` excluded from CDC - `EdgeMutationStorageOpsSpec` — V3 edge + multi-edge e2e with header on, asserts `context.storage_ops[0].rowHex` shape - `V2EdgeStorageOpsSpec` — V2 `/graph/v2/edge` e2e with header on Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
em3s
added a commit
that referenced
this pull request
Apr 23, 2026
Closes #253. Follows #258, #259, #261. Extends the scaffolded `StorageOp` marker with the backend-facing shape and teaches `StorageOpCollector.collect` to interpret raw HBase requests. No changes to call sites — the plumbing was done in #261. When the `AB-Include-Mutation-Context` header is set, mutation responses include the captured ops under `context["storage_ops"]`. - `StorageOp` — adds `Put`/`Delete`/`Increment` data classes with `Cell`/`Delta` nested types, hex-encoded row/family/qualifier/value - `StorageOpCollector.collect(request, table)` — `when` on `Put`/`Delete`/`Increment`, extracts cells/deltas, hex-encodes row/family/qualifier/value. Unknown request types are silently dropped so new backends can join without touching call sites. Tests: - `StorageOpCollectorTest` — data-driven via `@ObjectSource` (Put/Delete/Delete-columns/Increment/multi-byte) + two narrow `@Test` cases (non-Mutation skip, truncation cap) - `CdcContextSerializationTest` — `storageOps` excluded from CDC - `EdgeMutationStorageOpsSpec` — V3 edge + multi-edge e2e with header on, asserts `context.storage_ops[0].rowHex` shape - `V2EdgeStorageOpsSpec` — V2 `/graph/v2/edge` e2e with header on Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
em3s
added a commit
that referenced
this pull request
Apr 23, 2026
Closes #253. Follows #258, #259, #261. Extends the scaffolded `StorageOp` marker with the backend-facing shape and teaches `StorageOpCollector.collect` to interpret raw HBase requests. No changes to call sites — the plumbing was done in #261. When the `AB-Include-Mutation-Context` header is set, mutation responses include the captured ops under `context["storage_ops"]`. - `StorageOp` — adds `Put`/`Delete`/`Increment` data classes with `Cell`/`Delta` nested types, hex-encoded row/family/qualifier/value - `StorageOpCollector.collect(request, table)` — `when` on `Put`/`Delete`/`Increment`, extracts cells/deltas, hex-encodes row/family/qualifier/value. Unknown request types are silently dropped so new backends can join without touching call sites. Tests: - `StorageOpCollectorTest` — data-driven via `@ObjectSource` (Put/Delete/Delete-columns/Increment/multi-byte) + two narrow `@Test` cases (non-Mutation skip, truncation cap) - `CdcContextSerializationTest` — `storageOps` excluded from CDC - `EdgeMutationStorageOpsSpec` — V3 edge + multi-edge e2e with header on, asserts `context.storage_ops[0].rowHex` shape - `V2EdgeStorageOpsSpec` — V2 `/graph/v2/edge` e2e with header on Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
em3s
added a commit
that referenced
this pull request
Apr 23, 2026
Closes #253. Follows #258, #259, #261. Implements `StorageOpCollector.collect` so it decodes raw HBase requests into the `StorageOp` sealed type. The scaffolded marker is extended with the backend-facing shape: `Put` / `Delete` / `Increment` data classes with `Cell` / `Delta` nested types, base64-encoded row / family / qualifier / value. No call-site changes — the plumbing was done in #261. When the `AB-Include-Mutation-Context` header is set, mutation responses include the captured ops under `context["storage_ops"]`. Unknown request types become `StorageOp.Unknown(type = class name)` so new backends can join without touching call sites. Tests: - `StorageOpCollectorTest` — data-driven via `@ObjectSource` (Put/Delete/Delete-columns/Increment/multi-byte) + `@Test` for non-Mutation fallthrough and truncation cap - `CdcContextSerializationTest` — `storageOps` excluded from CDC - `V2EdgeStorageOpsSpec` — V2 `/graph/v2/edge` e2e with header on - `EdgeMutationStorageOpsSpec` — V3 edge + multi-edge e2e with header on, asserts `context.storage_ops[0].row` / cells shape Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
em3s
added a commit
that referenced
this pull request
Apr 23, 2026
Closes #253. Follows #258, #259, #261. Implements `StorageOpCollector.collect` so it decodes raw HBase requests into the `StorageOp` sealed type. The scaffolded marker is extended with the backend-facing shape: `Put` / `Delete` / `Increment` data classes with `Cell` / `Delta` nested types, base64-encoded row / family / qualifier / value. No call-site changes — the plumbing was done in #261. When the `AB-Include-Mutation-Context` header is set, mutation responses include the captured ops under `context["storageOps"]`. When more than `DEFAULT_MAX_OPS` (1024) ops would be collected, the collector sets `context["storageOpsTruncated"] = true` so clients can tell a complete snapshot from a silently truncated one. `StorageOp.Cell.type` captures the HBase `Cell.Type` name (`Put`, `DeleteColumn`, `DeleteFamily`, `DeleteFamilyVersion`, `Delete`) so shadow tests can distinguish delete subtypes. Unknown request types become `StorageOp.Unknown(type = qualified class name)` with a one-shot `warn`-level log per type, so new backends can join without silent drops. Tests: - `StorageOpCollectorTest` — data-driven via `@ObjectSource` (Put / Delete-full-row / Delete-columns / Delete-family / Increment / multi-byte) + `@Test` for multi-CF Put, non-Mutation fallthrough, `Append` → `Unknown`, truncation cap, `toContextMap` flag - `CdcContextSerializationTest` — `storageOps` and `storageOpsTruncated` excluded from CDC - `V2EdgeStorageOpsSpec` — V2 `/graph/v2/edge` e2e with header on - `EdgeMutationStorageOpsSpec` — V3 edge + multi-edge e2e with header on, asserts `context.storageOps[0].row` / cells shape Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
em3s
added a commit
that referenced
this pull request
Apr 23, 2026
Closes #253. Follows #258, #259, #261. Implements `StorageOpCollector.collect` so it decodes raw HBase requests into the `StorageOp` sealed type. The scaffolded marker is extended with the backend-facing shape: `Put` / `Delete` / `Increment` data classes with `Cell` / `Delta` nested types, base64-encoded row / family / qualifier / value. No call-site changes — the plumbing was done in #261. When the `AB-Include-Mutation-Context` header is set, mutation responses include the captured ops under `context["storageOps"]`. When more than `DEFAULT_MAX_OPS` (1024) ops would be collected, the collector sets `context["storageOpsTruncated"] = true` so clients can tell a complete snapshot from a silently truncated one. `StorageOp.Cell.type` captures the HBase `Cell.Type` name (`Put`, `DeleteColumn`, `DeleteFamily`, `DeleteFamilyVersion`, `Delete`) so shadow tests can distinguish delete subtypes. Unknown request types become `StorageOp.Unknown(type = qualified class name)` with a one-shot `warn`-level log per type, so new backends can join without silent drops. Tests: - `StorageOpCollectorTest` — data-driven via `@ObjectSource` (Put / Delete-full-row / Delete-columns / Delete-family / Increment / multi-byte) + `@Test` for multi-CF Put, non-Mutation fallthrough, `Append` → `Unknown`, truncation cap, `toContextMap` flag - `CdcContextSerializationTest` — `storageOps` and `storageOpsTruncated` excluded from CDC - `V2EdgeStorageOpsSpec` — V2 `/graph/v2/edge` e2e with header on - `EdgeMutationStorageOpsSpec` — V3 edge + multi-edge e2e with header on, asserts `context.storageOps[0].row` / cells shape Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
em3s
added a commit
that referenced
this pull request
Apr 23, 2026
Closes #253. Follows #258, #259, #261. Implements `StorageOpCollector.collect` so it decodes raw HBase requests into the `StorageOp` sealed type. The scaffolded marker is extended with the backend-facing shape: `Put` / `Delete` / `Increment` data classes with `Cell` / `Delta` nested types, base64-encoded row / family / qualifier / value. No call-site changes — the plumbing was done in #261. When the `AB-Include-Mutation-Context` header is set, mutation responses include the captured ops under `context["storageOps"]`. When more than `DEFAULT_MAX_OPS` (1024) ops would be collected, the collector sets `context["storageOpsTruncated"] = true` so clients can tell a complete snapshot from a silently truncated one. `StorageOp.Cell.type` captures the HBase `Cell.Type` name (`Put`, `DeleteColumn`, `DeleteFamily`, `DeleteFamilyVersion`, `Delete`) so shadow tests can distinguish delete subtypes. Unknown request types become `StorageOp.Unknown(type = qualified class name)` with a one-shot `warn`-level log per type, so new backends can join without silent drops. Tests: - `StorageOpCollectorTest` — data-driven via `@ObjectSource` (Put / Delete-full-row / Delete-columns / Delete-family / Increment / multi-byte) + `@Test` for multi-CF Put, non-Mutation fallthrough, `Append` → `Unknown`, truncation cap, `toContextMap` flag - `CdcContextSerializationTest` — `storageOps` and `storageOpsTruncated` excluded from CDC - `V2EdgeStorageOpsSpec` — V2 `/graph/v2/edge` e2e with header on - `EdgeMutationStorageOpsSpec` — V3 edge + multi-edge e2e with header on, asserts `context.storageOps[0].row` / cells shape Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
em3s
added a commit
that referenced
this pull request
Apr 23, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Query payloads (
EdgePayload, etc.) already carry acontext: Map<String, Any?>?slot; add the same slot to mutation payloads for symmetry. Applies to both V2 and V3 endpoints.Nullable with
@JsonInclude(NON_NULL)— non-breaking. Existing callers are unaffected.Changes
core.MutationResult.context: Map<String, Any?>? = nullEdgeMutationResponse.Item.contextandMultiEdgeMutationResponse.Item.contextengine.edge.MutationResultItem.contextfrom(...)factories thread it through unchangedTests
MutationResponseContextTest(core) — V3 items ser/der: null omitted, non-null round-tripMutationResultItemContextTest(engine) — same for V2MutationResultItem