Release v2.2.0
Summary
Brings the Kotlin/Java Chronicle client to parity with the other Chronicle clients across two phases of work:
- Earlier phase: four new service modules (ExternalServices, Webhooks, Jobs, EventStoreSubscriptions), event type migrations, 15 new model-bound projection attributes, a richer declarative projection builder, a richer ReadModels API, reactor side-effect events, and PII encryption key deletion.
- This phase: three confirmed bug fixes, full
IEventSequence/IEventLogcompleteness (redact, concurrency scopes, bookmarked reads, stream completion, a live append feed),IEventStoreinterface completeness, a richerUnitOfWork, constraint scoping, and typed read-model observation.
Fully additive — nothing existing changes behavior, except one safe, pre-release-only behavior change noted below.
Added
Service modules, migrations, projections, read models (earlier phase)
ExternalServices— register named HTTP or database (MS SQL / PostgreSQL) endpoints with basic/bearer/OAuth authentication, addressable by name from other Chronicle integrations.Webhooks— register webhooks either via a discoverable@Webhook+IWebhookDefinerclass or imperatively, with authentication, header, event-type filtering, and replay/activation options; list and remove webhooks.Jobs— list jobs and their steps, get a single job, and stop/resume/delete a job.EventStoreSubscriptions— subscribe an event store to another event store's outbox with optional event-type filtering, list, and unsubscribe. Now defaults to every event type this client has registered when a subscription isn't narrowed explicitly, instead of subscribing to nothing.- Event type migrations —
IEventTypeMigration/EventTypeMigrationbase class plus anEventTypeMigrationBuilderDSL (renamedFrom,defaultValue,split,combine) for upcasting/downcasting between event type generations, discovered alongside@EventTypeclasses atregister(). - 15 new model-bound projection attributes:
@Join,@ChildrenFrom,@Nested,@ClearWith,@Count,@Increment,@Decrement,@AddFrom,@SubtractFrom,@FromAll,@FromEvery,@NotRewindable,@RemovedWith,@RemovedWithJoin,@NoAutoMap. - Declarative projection builder methods:
.join(),.fromEvery()/.fromAll(),.removedWith()/.removedWithJoin(),.children(),.nested(),.notRewindable(), plus composite/constant keys. - Reactor handlers can now return a follow-up event, a
List<Any>, or the newEventForEventSourceIdwrapper, and it's auto-appended as a side effect — same-stream or cross-stream. ComplianceService.deleteEncryptionKeyfor permanently erasing a subject's PII ("right to be forgotten").
IEventSequence / IEventLog completeness (this phase)
redact/redactForEventSource— permanently rewrites one event's content, or every event's content for an event source (optionally narrowed to specific event types). Destructive and irreversible; the single most significant addition in this phase.- Real
ConcurrencyScopesupport viaAppendOptions.concurrencyScopeandConcurrencyScopeBuilder, andappendManynow commits the whole batch through a single atomic RPC instead of oneAppendcall per event. getTailSequenceNumber,getNextSequenceNumber,getTailSequenceNumberForObserver— the current/next position in the sequence, including relative to the event types a specific reactor/reducer handles.getForEventSourceIdAndEventTypes,getFromSequenceNumber— reading events back for one event source (narrowed by type/stream) or forward from a bookmarked position.completeStream— closes an event stream type/id pair so it can no longer be appended to.appendOperations— a hotFlowthat emits after every completed append made through a specificIEventSequenceinstance.
IEventStore / IChronicleClient completeness (this phase)
compliance,eventTypes,namespaces,externalServices,jobs,eventStoreSubscriptions,webhooks, andidentitiesare now all on theIEventStoreinterface itself (previously only on the concreteEventStoreclass), plusgetEventSequence(id)for non-default event sequences.IChronicleClient.getEventStores()/evictEventStores()— list every event store known to the kernel, and clear this client's cachedEventStoreinstances without disposing the client.IIdentityManagerService.rename— renames the human-readable name the kernel has stored for an identity.INamespacesService.getAll()— lists every namespace in the event store.ChronicleConnectionString.toString()— renders a parsed connection string back to its textual form; round-trips throughparse().
Transactions, constraints, seeding, read models (this phase)
- Richer
IUnitOfWork:isSuccess,getConstraintViolations/getConcurrencyViolations/getAppendErrors,onCompleted,tryGetLastCommittedEventSequenceNumber. RicherIUnitOfWorkManager:tryGetFor,setCurrent. - Constraint scoping —
IConstraintBuilder.perEventSourceType()/.perEventStreamType()/.perEventStreamId()narrow a constraint's uniqueness check to a dimension instead of checking globally across the whole event store. IEventSeedingBuilder.forNamespace()— a scoped builder for seed data targeting a namespace other than the event store's own; typedforEventType()for both the top-level and namespace-scoped builders.watch()/getSnapshotsById()now deserialize straight into the caller's read model type instead of raw JSON;releaseManydecrypts@Piiproperties for a batch of instances in one call.
Samples, docs, and Java interop (both phases)
- Both Kotlin and Java console samples demonstrate every feature above end-to-end, wired into the existing interactive command loop — including, this phase, a single-event redact, a bulk "GDPR erase everything for this customer" redact, constraint scoping,
IdentityManager.rename, read-model snapshot history, and a live background watcher using typedwatch(). - Java bridge parity for all of the above via
io.cratis.chronicle.java.*JavaBridge, including new bridges for the value-class-typed members Java cannot call directly (redact, the event-lookup methods,completeStream,ConcurrencyScopeBuilder.withSequenceNumber, and a callback-based subscription towatch/appendOperations). - New and expanded guide/reference pages (Webhooks, Jobs, Event Type Migrations, Event Store Subscriptions, External Services, EventStore API, Seeding, Configuration, Constraints, Transactions, Read Models, Annotations), with matching validated snippets in both the Kotlin and Java client-snippet trees.
Fixed
UniqueConstraintBuilder.on()was silently ignoring the property actually passed to it and always keying the constraint on the first declared property instead.- Event type and read model registration sent an empty
"{}"schema to the kernel instead of a real JSON schema reflecting the type's properties; PII compliance metadata is now wired into that generated schema too.
Behavior change (safe — pre-release, within this same unreleased PR)
IUnitOfWorkManager.currentnow throwsNoUnitOfWorkHasBeenStartedwhen no unit of work has been begun on the current thread, rather than any prior placeholder behavior. Since this entireUnitOfWorksurface is new in this unreleased PR, there is no released behavior to break.
Known follow-up
EventTypesService's migration-building path,ProjectionsService's new reflection/wire-building paths, andReactorsService's side-effect appending don't have dedicated unit tests yet — there's no established gRPC-stub-mocking pattern in this codebase for those specific paths, only pure-logic builders (EventTypeMigrationBuilder,ProjectionBuilderFor) are covered. Flagging as a follow-up rather than blocking this PR.- The event type migration builder DSL (
renamedFrom/defaultValue/split/combine) has no Java bridge yet, since it relies on Kotlin property references (KProperty1) that Java can't produce directly. The Java client-snippet for migrations is left as an honest "not supported" placeholder rather than a misleading example.
Test plan
-
./gradlew clean build test— zero errors, 224 tests passing (up from 133 at the start of this PR) -
python3 Documentation/validate-client-snippets.py— all Kotlin and Java client snippets compile -
bash Documentation/verify-markdown.sh— markdown lint and link checks pass - Both samples (
Samples/Kotlin/Console,Samples/Java/Console) build cleanly