tag:github.com,2008:https://github.com/TraxSharp/Trax.Samples/releases Tags from Trax.Samples 2026-06-02T20:37:33Z tag:github.com,2008:Repository/1161061168/v1.29.0 2026-06-02T20:42:22Z v1.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> traxsharp tag:github.com,2008:Repository/1161061168/v1.28.0 2026-05-21T20:38:47Z v1.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> traxsharp tag:github.com,2008:Repository/1161061168/v1.27.0 2026-05-21T14:42:07Z v1.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> traxsharp tag:github.com,2008:Repository/1161061168/v1.26.0 2026-05-19T22:56:53Z v1.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> traxsharp tag:github.com,2008:Repository/1161061168/v1.25.0 2026-05-15T14:30:40Z v1.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 =&gt; b.UseSignalRHub(opts =&gt; 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> traxsharp tag:github.com,2008:Repository/1161061168/v1.24.0 2026-05-14T21:12:28Z v1.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> traxsharp tag:github.com,2008:Repository/1161061168/v1.23.1 2026-05-12T17:13:37Z v1.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> traxsharp tag:github.com,2008:Repository/1161061168/v1.23.0 2026-05-12T15:52:06Z v1.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> traxsharp tag:github.com,2008:Repository/1161061168/v1.22.0 2026-05-08T19:53:07Z v1.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&lt;Program&gt;-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> traxsharp tag:github.com,2008:Repository/1161061168/v1.21.0 2026-05-06T16:59:36Z v1.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