Skip to content

Add related-resource authorization builder slot and fix outbox/inbox … - #685

Merged
xavierjohn merged 2 commits into
mainfrom
fix/related-resource-authorization-slot-and-outbox-inbox-options
Jul 27, 2026
Merged

Add related-resource authorization builder slot and fix outbox/inbox …#685
xavierjohn merged 2 commits into
mainfrom
fix/related-resource-authorization-slot-and-outbox-inbox-options

Conversation

@xavierjohn

Copy link
Copy Markdown
Owner

…options accumulation

TrellisServiceBuilder had no slot for indirect (via) resource authorization, so AOT and explicit-registration consumers had to call AddRelatedResourceAuthorization outside the builder. A via-command with no registered behavior fails silently — there is no analyzer and no startup guard — so the gap let a protected command run unauthorized. Adds two UseRelatedResourceAuthorization overloads (single-hop and ResolvedAuthorizationPath), placed immediately before UseClaimsActorProvider so the canonical pipeline order holds both before and after UseEntityFrameworkUnitOfWork.

AddTrellisOutbox(configure) and AddTrellisInbox(configure) registered their options with TryAddSingleton, so a second call's configure callback was silently discarded and the first call's configuration won. Both helpers now layer each callback onto the last registered options descriptor — the one the container actually resolves — applying it to a clone and committing only after Validate() succeeds, so a rejected callback cannot leave half-applied options behind. Keyed descriptors are skipped: they take no part in unkeyed resolution and reading ImplementationInstance on one throws. When a consumer owns the registration through a factory or implementation type, which cannot be cloned, the call throws instead of registering a second instance the relay or dispatcher would never receive.

The relay itself was already deduplicated by AddHostedService, which uses TryAddEnumerable, so repeated registration never produced duplicate relays; the outbox reference doc claimed otherwise and has been corrected.

Also corrects the TracingOverheadBenchmarks premise. RopTrace is internal, so the benchmark cannot start a Trellis-owned span; the ambient span exercises the source-ownership guard rejection path rather than a span write. Documentation only, and BENCHMARKS.md publishes nothing from this file.

…options accumulation

TrellisServiceBuilder had no slot for indirect (via) resource authorization, so AOT and
explicit-registration consumers had to call AddRelatedResourceAuthorization outside the
builder. A via-command with no registered behavior fails silently — there is no analyzer
and no startup guard — so the gap let a protected command run unauthorized. Adds two
UseRelatedResourceAuthorization overloads (single-hop and ResolvedAuthorizationPath),
placed immediately before UseClaimsActorProvider so the canonical pipeline order holds
both before and after UseEntityFrameworkUnitOfWork.

AddTrellisOutbox<TContext>(configure) and AddTrellisInbox<TContext>(configure) registered
their options with TryAddSingleton, so a second call's configure callback was silently
discarded and the first call's configuration won. Both helpers now layer each callback
onto the last registered options descriptor — the one the container actually resolves —
applying it to a clone and committing only after Validate() succeeds, so a rejected
callback cannot leave half-applied options behind. Keyed descriptors are skipped: they
take no part in unkeyed resolution and reading ImplementationInstance on one throws. When
a consumer owns the registration through a factory or implementation type, which cannot be
cloned, the call throws instead of registering a second instance the relay or dispatcher
would never receive.

The relay itself was already deduplicated by AddHostedService, which uses TryAddEnumerable,
so repeated registration never produced duplicate relays; the outbox reference doc claimed
otherwise and has been corrected.

Also corrects the TracingOverheadBenchmarks premise. RopTrace is internal, so the benchmark
cannot start a Trellis-owned span; the ambient span exercises the source-ownership guard
rejection path rather than a span write. Documentation only, and BENCHMARKS.md publishes
nothing from this file.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 27, 2026 21:44

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR closes a DI/composition-root gap by adding TrellisServiceBuilder slots for indirect (“via”) resource authorization, and fixes a long-standing MS.DI options-registration pitfall where repeated outbox/inbox registrations silently discarded later configuration. It also updates docs/changelog and adds regression tests to lock in behavior and pipeline ordering.

Changes:

  • Add AOT-safe UseRelatedResourceAuthorization builder overloads (single-hop and ResolvedAuthorizationPath) and corresponding tests verifying registration and pipeline order invariants.
  • Fix AddTrellisOutbox<TContext>(configure) / AddTrellisInbox<TContext>(configure) so repeated calls accumulate configuration atomically (clone → configure → validate → commit), skip keyed descriptors, and throw when layering onto a factory/type-owned registration would otherwise be ineffective.
  • Update DocFX API references, outbox/inbox docs, benchmark remarks, and CHANGELOG.md to reflect the corrected behavior and guidance.

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
Trellis.ServiceDefaults/tests/TrellisServiceBuilderTests.cs Adds regression tests for UseRelatedResourceAuthorization registration, idempotency, leaf accessor wiring, and behavior ordering vs validation/UoW.
Trellis.ServiceDefaults/src/TrellisServiceBuilder.cs Introduces the new UseRelatedResourceAuthorization builder slots and wires them into the existing typed resource-authorization registration flow.
Trellis.EntityFrameworkCore.Outbox/tests/OutboxRegistrationTests.cs New tests covering relay dedupe, configure accumulation/override, atomic failure behavior, cloning completeness, and keyed/factory registration edge cases.
Trellis.EntityFrameworkCore.Outbox/src/OutboxRegistrationExtensions.cs Reworks outbox registration to layer configuration onto the effective (last, unkeyed) OutboxOptions descriptor with atomic validate-then-commit semantics.
Trellis.EntityFrameworkCore.Outbox/src/OutboxOptions.cs Adds Clone() to support atomic options layering on repeated registration.
Trellis.EntityFrameworkCore.Inbox/tests/InboxRegistrationTests.cs New tests covering configure accumulation/override, single-instance behavior, atomic failure behavior, cloning completeness, and keyed/factory registration edge cases.
Trellis.EntityFrameworkCore.Inbox/src/InboxRegistrationExtensions.cs Reworks inbox registration to layer configuration onto the effective (last, unkeyed) InboxOptions descriptor with atomic validate-then-commit semantics.
Trellis.EntityFrameworkCore.Inbox/src/InboxOptions.cs Adds Clone() to support atomic options layering on repeated registration.
Trellis.Benchmark/TracingOverheadBenchmarks.cs Clarifies benchmark premise: ambient activity is foreign to Trellis, exercising the guard-rejection path; write path is exercised by chain benchmarks when listener is on.
docs/docfx_project/api_reference/trellis-api-servicedefaults.md Documents the new builder slots and distinguishes direct vs via resource authorization in the builder slot table.
docs/docfx_project/api_reference/trellis-api-mediator.md Notes that AddRelatedResourceAuthorization overloads now have matching builder slots.
docs/docfx_project/api_reference/trellis-api-efcore-outbox.md Corrects behavior/docs: repeated registrations accumulate configuration, relay is deduped, and factory/type-owned options registrations with configure now throw.
docs/docfx_project/api_reference/trellis-api-efcore-inbox.md Documents accumulated configuration behavior and the factory/type-owned options registration guard.
CHANGELOG.md Records the new builder slots and the outbox/inbox options accumulation fix (plus clarified relay dedupe).

Comment thread Trellis.EntityFrameworkCore.Outbox/src/OutboxOptions.cs
Comment thread Trellis.EntityFrameworkCore.Inbox/src/InboxOptions.cs
@github-actions

github-actions Bot commented Jul 27, 2026

Copy link
Copy Markdown

Test Results

7 306 tests  +27   7 288 ✅ +27   6m 32s ⏱️ +22s
   27 suites ± 0      18 💤 ± 0 
   27 files   ± 0       0 ❌ ± 0 

Results for commit def301d. ± Comparison against base commit 6eb2ebc.

♻️ This comment has been updated with latest results.

@codecov

codecov Bot commented Jul 27, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 87.61%. Comparing base (6eb2ebc) to head (def301d).

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #685      +/-   ##
==========================================
+ Coverage   87.57%   87.61%   +0.03%     
==========================================
  Files         402      402              
  Lines       17785    17842      +57     
  Branches     3747     3758      +11     
==========================================
+ Hits        15576    15633      +57     
  Misses       1345     1345              
  Partials      864      864              
Files with missing lines Coverage Δ
...llis.EntityFrameworkCore.Inbox/src/InboxOptions.cs 100.00% <100.00%> (ø)
...eworkCore.Inbox/src/InboxRegistrationExtensions.cs 100.00% <100.00%> (ø)
...is.EntityFrameworkCore.Outbox/src/OutboxOptions.cs 75.00% <100.00%> (+11.36%) ⬆️
...orkCore.Outbox/src/OutboxRegistrationExtensions.cs 100.00% <100.00%> (ø)
...ellis.ServiceDefaults/src/TrellisServiceBuilder.cs 90.68% <100.00%> (+0.47%) ⬆️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

The XML docs on OutboxOptions.Clone() and InboxOptions.Clone() named
OutboxOptionsCloneTests and InboxOptionsCloneTests, neither of which exists — the
guard tests live in OutboxRegistrationTests and InboxRegistrationTests. Point at the
fully qualified test methods so the reference is greppable and survives a class rename.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@xavierjohn
xavierjohn merged commit 8872ba8 into main Jul 27, 2026
5 of 6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants