tag:github.com,2008:https://github.com/TraxSharp/Trax.Samples/releasesTags from Trax.Samples2026-06-02T20:37:33Ztag:github.com,2008:Repository/1161061168/v1.29.02026-06-02T20:42:22Zv1.29.0<p>feat: multi-server keyed GraphQL client sample (<a class="issue-link js-issue-link" href="https://github.com/TraxSharp/Trax.Samples/pull/67">#67</a>)</p>
<p>* feat: multi-server keyed GraphQL client sample</p>
<p>Extend the GraphQLClient sample with two real Trax GraphQL servers that expose
<br />different schemas (inventory: discover.inventory.getProduct, billing:
<br />discover.billing.getInvoice), both on in-memory effects so no database is
<br />needed. A Gateway console starts both servers in-process and drives them through
<br />two keyed clients registered in a single container, printing both results and
<br />the rejected cross-schema query.</p>
<p>The E2E suite boots both servers via WebApplicationFactory, registers the two
<br />keyed clients in one container, and proves each client queries its own server,
<br />that a query for one server is rejected through the other key, and that both
<br />resolve to distinct executors from the same container.</p>
<p>* test: cover the not-found path in the sample train junctions</p>
<p>The inventory and billing junctions fall back to an "Unknown" result when the
<br />SKU/invoice id is missing; only the found branch was exercised. Add E2E cases
<br />that query a missing id through each keyed client and assert the fallback.</p>
<p>Leaves the Gateway console Program uncovered, consistent with the other console
<br />sample entry points (PersistedOperations.Client, ContentShield.Runner,
<br />EnergyHub.Worker), none of which are tested or excluded from coverage.</p>traxsharptag:github.com,2008:Repository/1161061168/v1.28.02026-05-21T20:38:47Zv1.28.0<p>feat(persisted-ops sample): demonstrate [TraxAuthorize] coexistence (<a class="issue-link js-issue-link" href="https://github.com/TraxSharp/Trax.Samples/pull/62">#62</a></p>
<p><a class="issue-link js-issue-link" href="https://github.com/TraxSharp/Trax.Samples/pull/62"></a>)</p>
<p>* feat(persisted-ops sample): demonstrate [TraxAuthorize] coexistence</p>
<p>* test(persisted-ops sample): cover UserNote round-trip and drop bootstrap try/catch</p>traxsharptag:github.com,2008:Repository/1161061168/v1.27.02026-05-21T14:42:07Zv1.27.0<p>feat(graphql-client sample): nested-path typed request via Path attri…</p>
<p>…bute (<a class="issue-link js-issue-link" href="https://github.com/TraxSharp/Trax.Samples/pull/61">#61</a>)</p>
<p>Adds LookupPlayerByNestedPathRequest to the GraphQLClient sample, demonstrating
<br />Path = "discover.players" + RootField = "lookupPlayer" against an envelope that
<br />mirrors the shape Trax itself produces server-side for
<br />[TraxQuery(Namespace = "players")]-decorated trains. The same player is fetched
<br />through both the flat typed request (mode D) and the nested-path variant to
<br />show consumers don't need to fall back to raw-string mode when targeting a
<br />namespaced schema.</p>
<p>PlayerSchemaConfiguration now exposes a discover.players.lookupPlayer surface
<br />alongside the existing flat fields so the sample remains self-hosted (server +
<br />client in one process). The existing flat-mode comparisons (A == E, A ~ D) are
<br />unchanged.</p>traxsharptag:github.com,2008:Repository/1161061168/v1.26.02026-05-19T22:56:53Zv1.26.0<p>feat(GameServer): demonstrate [TraxAllowAnonymous] on a [TraxQueryMod…</p>
<p>…el] entity (<a class="issue-link js-issue-link" href="https://github.com/TraxSharp/Trax.Samples/pull/60">#60</a>)</p>
<p>Adds PublicAnnouncement to the GameServer sample: anonymously readable
<br />"news feed" entries with an optional FK to MatchRecord (admin-gated)
<br />so the Option B no-cascade contract is exercised against a real host.</p>
<p>E2E suite (8 tests) covers anonymous + authenticated direct reads,
<br />Connection-scalar reachability, the cascade-from-anonymous-to-gated
<br />rejection path, and a regression check that the pre-existing
<br />MatchRecord Admin gate still holds.</p>traxsharptag:github.com,2008:Repository/1161061168/v1.25.02026-05-15T14:30:40Zv1.25.0<p>feat: Blazor Server sample exercising the SignalR sink (<a class="issue-link js-issue-link" href="https://github.com/TraxSharp/Trax.Samples/pull/58">#58</a>)</p>
<p>A single-process Blazor Server sample that wires UseSignalRHub end-to-end:</p>
<p> - A PingTrain scheduled every 10 seconds, executed by the scheduler's
<br /> default LocalWorkerService
<br /> - UseBroadcaster(b => b.UseSignalRHub(opts => opts.OnlyForEvents(...)))
<br /> so every lifecycle event flows to connected clients
<br /> - MapTraxTrainEventHub() at /hubs/trax-events
<br /> - A /events Blazor page that opens its own HubConnection, subscribes to
<br /> TrainEvent, and renders a rolling list of the last 50 events
<br /> - The Trax dashboard at /trax for parity with other samples</p>
<p>Verified end-to-end against a standalone Microsoft.AspNetCore.SignalR.Client
<br />console: scheduled IPingTrain events arrive at the client with the default
<br />TraxClientEvent shape and event types (Started / Completed).</p>
<p>Listens on http://localhost:5210. Uses the workspace Postgres from
<br />docker-compose. No RabbitMQ needed — this is the standalone topology the
<br />SignalR sink supports via its no-op transport fallbacks.</p>traxsharptag:github.com,2008:Repository/1161061168/v1.24.02026-05-14T21:12:28Zv1.24.0<p>feat(GameServer): demonstrate [TraxAuthorize] on a [TraxQueryModel] e…</p>
<p>…ntity (<a class="issue-link js-issue-link" href="https://github.com/TraxSharp/Trax.Samples/pull/57">#57</a>)</p>
<p>* feat(GameServer): demonstrate [TraxAuthorize] on a [TraxQueryModel] entity</p>
<p>Gates MatchRecord with [TraxAuthorize(Roles = nameof(GameRole.Admin))] so
<br />the sample shows how to expose a query-model entity to admins only. The
<br />attribute attaches <a class="user-mention notranslate" href="https://github.com/authorize">@authorize</a> at both the entry field and ObjectType, so
<br />top-level discover.matches.matchRecords and any transitive navigation
<br />that resolves a MatchRecord are gated uniformly; totalCount and pageInfo
<br />side channels are blocked too.</p>
<p>Pins the default authentication scheme to api-key so HC's <a class="user-mention notranslate" href="https://github.com/authorize">@authorize</a>
<br />directive sees an authenticated principal when more than one scheme is
<br />registered. With a single scheme, ASP.NET Core auto-selects it as
<br />default; with multiple, no scheme is auto-default and HC reads an
<br />anonymous principal — every gated query rejects every caller, including
<br />admins. The pin is conditional on neither scheme nor environment, so it
<br />applies consistently regardless of whether the Google JWT scheme is
<br />configured.</p>
<p>Tests:
<br /> - Existing MatchRecord query / filter / pagination tests now use the
<br /> Admin key.
<br /> - New rejection tests cover Player and anonymous direct access plus
<br /> the totalCount-only side-channel case (proves field-level gating
<br /> closes the loophole that type-level alone would leave open).</p>
<p>* feat(GameServer): demonstrate [TraxAuthorize] on a [TraxQueryModel] entity</p>
<p>Gate MatchRecord with [TraxAuthorize(Roles = Admin)] so the sample demonstrates
<br />per-model authorization end-to-end. The attribute attaches the <a class="user-mention notranslate" href="https://github.com/authorize">@authorize</a>
<br />directive at both the entry field (discover.matches.matchRecords) and the
<br />ObjectType level — Connection scalars like totalCount and pageInfo are blocked
<br />too, so a Player API key cannot even enumerate match cardinality.</p>
<p>Existing MatchRecord query tests switched from PlayerKey to AdminKey. Three
<br />new rejection-path tests pin the contract: Player and anonymous callers see
<br />TRAX_AUTHORIZATION; totalCount-only queries are blocked even though they
<br />never resolve a node of the gated type.</p>
<p>* test(GameServer.E2E): warmup MatchRecord auth cache before assertions</p>
<p>The three MatchRecords_*_ReturnsAuthorizationError tests fired the first
<br />request against a [TraxAuthorize]-gated entity in the fixture lifecycle.
<br />HotChocolate validates and caches operation documents on first execution.
<br />If the validation pipeline runs while any part of the request pipeline
<br />is still settling, the cached operation can capture a pre-gated state,
<br />and that specific document permanently bypasses the directive. This
<br />showed up in CI as a deterministic-on-cold-start, passes-on-rerun flake:
<br />3 failures clustered in the first 400ms of GameServer.E2E execution,
<br />then 102 other tests against the same entity passed for the next 2 min.</p>
<p>WarmupAuthorizationCache polls both gated query shapes with an anonymous
<br />caller in [OneTimeSetUp]. Each shape is exercised until it returns
<br />"Not authorized." once, which proves the directive is wired AND seeds
<br />the operation cache with a validated entry that enforces it. Tests then
<br />assert against a known steady-state cache.</p>
<p>Bounded by a 30s timeout that throws TimeoutException with a clear
<br />diagnostic if the directive never attaches. Per the project's
<br />determinism rule: polls the actual condition, fails loudly on timeout,
<br />never sleeps as a stand-in for synchronisation.</p>
<p>Verified: 8 consecutive runs of the fixture pass in 253-273ms with no
<br />variance.</p>traxsharptag:github.com,2008:Repository/1161061168/v1.23.12026-05-12T17:13:37Zv1.23.1<p>fix(persisted-operations sample): demo a genuinely different hot-fix …</p>
<p>…doc (<a class="issue-link js-issue-link" href="https://github.com/TraxSharp/Trax.Samples/pull/56">#56</a>)</p>
<p>The hot-fix demo re-uploaded the same document text from manifest.json,
<br />so it would have passed even if the underlying cache-invalidation path
<br />in Trax.Api.GraphQL.PersistedOperations had been silently broken (and
<br />in fact it was — see the matching fix in Trax.Api). The client now
<br />re-uploads a shape-changed document (adds __typename) with
<br />bypassShapeDiff and asserts the new field appears in the response, so
<br />the sample fails loudly if HC's IDocumentCache / IPreparedOperationCache
<br />are not invalidated on upsert.</p>
<p>Also extend POSTGRES_MULTIPLE_DATABASES with the trax_api_* databases
<br />the Trax.Api integration tests need; CI creates these out-of-band and
<br />local dev was the only place they were missing.</p>traxsharptag:github.com,2008:Repository/1161061168/v1.23.02026-05-12T15:52:06Zv1.23.0<p>feat: wire dashboard into persisted-ops sample, route client through …</p>
<p>…GraphQL (<a class="issue-link js-issue-link" href="https://github.com/TraxSharp/Trax.Samples/pull/55">#55</a>)</p>
<p>- The persisted-ops sample API now hosts Trax.Dashboard at /trax,
<br /> giving the new Persisted Operations page a real backend to drive.
<br /> Adds the scheduler builder (no-op) so the dashboard's existing pages
<br /> (manifest groups, work queue, dead letters) compose, and scopes
<br /> UsePersistedOperationsEnforcement to /trax/graphql so it does not
<br /> buffer Blazor's SignalR traffic.
<br />- The sample client drops its ServiceCollection + IPersistedOperationStore
<br /> bootstrap entirely. Manifest uploads now go through the
<br /> uploadPersistedOperation GraphQL mutation, which is what real shipped
<br /> clients and the dashboard both do. The client csproj loses every
<br /> Trax package reference.
<br />- New MutationFlowTests E2E covering upload happy path, schema-mismatch
<br /> error projection, shape-diff with and without bypass, and deactivation
<br /> blocking future executions by id.</p>traxsharptag:github.com,2008:Repository/1161061168/v1.22.02026-05-08T19:53:07Zv1.22.0<p>feat: persisted-operations sample + E2E test project (<a class="issue-link js-issue-link" href="https://github.com/TraxSharp/Trax.Samples/pull/53">#53</a>)</p>
<p>* feat: persisted-operations sample + E2E test project</p>
<p>Two-process sample under samples/PersistedOperations:
<br />- Trax.Samples.PersistedOperations.Api: GraphQL host with persisted-op
<br /> enforcement enabled. Demonstrates AllowOperationsMatching for dev_*
<br /> carve-outs and shadow-mode logging.
<br />- Trax.Samples.PersistedOperations.Client: console client that uploads
<br /> a manifest, sends a request by id, hot-fixes the document, and
<br /> re-queries to demonstrate the no-redeploy flow.</p>
<p>E2E test project under tests/Trax.Samples.PersistedOperations.E2E:
<br />- WebApplicationFactory<Program>-hosted in-process API, real Postgres
<br /> via the docker-compose container.
<br />- Enforcement, dev-prefix bypass, deactivate/restore lifecycle, hot-fix
<br /> flow tests. The persisted-id resolution path (3 tests) is currently
<br /> Ignored pending follow-up on HC v15's IOperationDocumentStorage
<br /> pipeline wiring; the storage layer itself is fully exercised by the
<br /> Trax.Api integration suite.</p>
<p>* fix: align rabbitmq creds with CI and reformat E2E tests</p>
<p>Switch the dev broker to trax/trax123 so local docker-compose mirrors
<br />the CI service container, and apply csharpier formatting to two E2E
<br />test files that were failing the format check.</p>traxsharptag:github.com,2008:Repository/1161061168/v1.21.02026-05-06T16:59:36Zv1.21.0<p>ci: add postgres and rabbitmq services to release workflow (<a class="issue-link js-issue-link" href="https://github.com/TraxSharp/Trax.Samples/pull/52">#52</a>)</p>
<p>Release runs were failing in JobHunt.E2E because the workflow had no
<br />Postgres available, so DatabaseMigrator.Migrate() couldn't open a
<br />connection. PR builds already wire up both services; mirror the same
<br />setup in nuget_release.yml so coverage uploads to Codecov on main.</p>traxsharp