v7.0.0-preview.4
Pre-release
Pre-release
·
0 commits
to main
since this release
This is a pre-release of 7.0.0. Packages are published to NuGet with the
7.0.0-preview.4
version suffix, sodotnet add packageonly resolves them when a pre-release version is requested
explicitly or--prereleaseis passed.The notes below cover what changed since
7.0.0-preview.3. For everything landing in
7.0.0, see the 7.0.0 changelog.
What changed since v7.0.0-preview.3. Acts on the second round of feedback from the application migrating onto the v7
previews. The theme is ownership: an application can now define its own audit record shape, and two checks that could
not be satisfied as shipped no longer have to be turned off to get a build through.
Added
- An application can own the audit record shape.
AuditingBuilder.UseRecordWriter<TWriter>(lifetime)and
UseRecordWriterInstance, overMessageModuleBuilder.UseAuditRecordWriter, replace the writer that turns a
completed mediation into a record.AuditRecordwas always a handoff toIAuditTrailrather than a persistence
schema, so a different set of fields needs no new abstraction: it needs a differentIAuditRecordWriter, which is
the entire contract between the pipeline and auditing. A replacement keeps the completion-stage placement, the
Observabilitypriority and the per-axis selection, and replaces exactly the record building. Everything the
built-in writer uses to do that was already public: the audit position throughIMessageRegistry,
AuditReasonMissingException,IAuditScope,AmbientExecutionContext, andIAuditOutcomeMapper. Skipping a
message that declares no audit position is part of the contract rather than a detail, because without it every
message on the selected axes produces a record. There is deliberately no genericAuditRecord<T>or
IAuditTrail<TRecord>: a type parameter there spreads through the writer, three axis completion handlers, the
probe, the builder andAddAuditing, so every application wanting the default would pay for the few that do not,
and it buys nothing a writer cannot already do. LiteBusCompositionSummary.AuditRecordWriternames a writer that replaced the built-in one, and the startup line
reports it. Replacing the writer replaces the whole of record building and nothing else in the composition would
say so, which makes a misregistration look exactly like auditing working.
Changed
- The
litebus.audit.trailprobe no longer asserts anIAuditTrailis registered when the record writer was
replaced. It readsLiteBusCompositionSummary.AuditRecordWriterand reportsHealthywith the writer instead: a
writer LiteBus did not build need not use a trail or an actor resolver at all, so demanding either reported a
correct configuration as unhealthy. It reads the summary rather than resolvingIAuditRecordWriter, because
resolving the built-in writer with no trail throws, which is the state the probe exists to report.
Fixed
RequireExplicitOpenGenericswas unsatisfiable for an open generic living in a scanned assembly, which is every
handler worth sharing. An explicitRegister(typeof(MyGuard<>))did not clear the scanned mark in either order,
andRegisterFromAssemblyre-marked any open generic in the assembly it walked, so the only way to satisfy the
check was to move the handler into an assembly nothing scans. The exception even named a fix that did not work. The
registry now records the two origins separately andIMessageReader.ScannedOpenGenericHandlerssubtracts them, so a
registration line clears the mark whichever order the two arrive in, and clearing it does not cost the closure.LB1018andLB1020could not seeIMessageDefinition<TMessage>.Describe, the shape the documentation calls the
one to reach for. Both shareDeclarationAnalysis, which looked only for the attributes and the keyed
IMessageDefinition<TMessage, TValue>, so a message declared the recommended way was reported as declaring nothing
and the only remedy was to turn the rule off. The pass now reads theDescribebody:Declare<TValue>and
Exempt<TValue>match on the type argument, andAudited(...)andNotAudited(...)both count as declaring
AuditDeclaration, because each states an audit position and the rule asks whether the message answered rather
than which answer it gave. A body that cannot be read, one in a referenced assembly or one that hands the collector
to another method, is treated as declared: a false positive is a build that cannot be made to pass, while a false
negative leaves a ruleRequireDeclarationstill enforces at startup.
Breaking
AuditActor.Kindis required, andAuditActor.UserandForno longer take a display name. An actor of no kind is
a state no audit query can use and no factory produced, so requiring it removes a fourth answer nobody wanted.
DisplayNameis documented as the field that makes the trail hold personal data and that LiteBus deliberately does
not populate, so putting it behind an optional second argument made the easiest thing at a call site the thing the
documentation warns about. WriteAuditActor.User(id) with { DisplayName = name }, which is the same amount of code
and a different amount of attention.Kindstays a string, because the set of things that can act belongs to the
application, and an application whose set is closed keeps it closed where it constructs the actor.
Documentation
- A preview after the first now publishes only what changed since the preview before it, which is why these notes are
a delta rather than the whole v7.0.0 section. The first preview of a line and its stable release still publish that
section in full.AGENTS.mdandCONTRIBUTING.mdrecord the convention.