Skip to content

feat: persisted GraphQL operations#46

Merged
Theauxm merged 1 commit into
mainfrom
feat/persisted-operations
May 8, 2026
Merged

feat: persisted GraphQL operations#46
Theauxm merged 1 commit into
mainfrom
feat/persisted-operations

Conversation

@Theauxm
Copy link
Copy Markdown
Member

@Theauxm Theauxm commented May 8, 2026

Summary

New package Trax.Api.GraphQL.PersistedOperations:

  • Fluent PersistedOperationsBuilder (RequirePersisted, AllowOperations, AllowOperationsMatching, WithInMemoryCache, UseRabbitMqInvalidation, DisableIntrospection, LogNonPersistedRequests, UseDatabase). Builder validates inconsistent configs at startup with named error messages.
  • DbPersistedOperationStorage implementing both IPersistedOperationStore (programmatic CRUD) and HotChocolate's IOperationDocumentStorage. Reads/writes through the existing Trax IDataContextProviderFactory; no dedicated DbContext.
  • ASP.NET middleware enforcing inline-query rejection, allowlist + predicate bypass, automatic introspection bypass, shadow-mode logging. Handles batched JSON-array bodies (rejects whole batch on any disallowed entry).
  • Shape-diff guardrail: UpsertAsync computes a canonicalized SHA-256 fingerprint of the response shape (AST walker that inlines fragments, sorts sibling fields by alias/response-key, tracks @include/@Skip directives) and rejects edits that change the shape with ShapeDiffViolationException unless BypassShapeDiff = true.
  • Optional IPersistedOperationCache (NoOp default, in-memory opt-in via WithInMemoryCache). Optional RabbitMQ-backed multi-node invalidation (UseRabbitMqInvalidation).
  • AddPersistedOperationStore standalone DI extension for non-GraphQL admin tools.

Wires through TraxGraphQLBuilder.UsePersistedOperations(...) and app.UsePersistedOperationsEnforcement().

Models live in Trax.Effect (separate PR); this PR depends on it landing first.

Test plan

  • dotnet build Trax.Api/ produces zero warnings.
  • dotnet test tests/Trax.Api.Tests/ --filter "FullyQualifiedName~PersistedOperations" passes 197 tests.
  • Coverage on Trax.Api.GraphQL.PersistedOperations >= 95% (measured locally at 96.0%).
  • RabbitMQ-dependent tests pass against the docker-compose trax_rabbitmq container.
  • Postgres-dependent tests pass against the docker-compose trax_database container.

@Theauxm Theauxm force-pushed the feat/persisted-operations branch 2 times, most recently from b5a1352 to 1f0d893 Compare May 8, 2026 18:34
New package Trax.Api.GraphQL.PersistedOperations:

- PersistedOperationsBuilder fluent config (RequirePersisted, AllowOperations,
  WithInMemoryCache, UseRabbitMqInvalidation, DisableIntrospection,
  LogNonPersistedRequests).
- DbPersistedOperationStorage implementing both IPersistedOperationStore
  (programmatic CRUD) and HotChocolate's IOperationDocumentStorage. Uses
  the existing IDataContextProviderFactory; no separate DbContext.
- ASP.NET middleware that enforces inline-query rejection, allowlist
  bypass, automatic introspection bypass, and shadow-mode logging.
  Registered via app.UsePersistedOperationsEnforcement().
- Shape-diff fingerprint computer (sha-256 over a canonicalized AST
  walk: inlines fragments, sorts sibling fields, tracks aliases and
  @include/@Skip directives). Binding test fixture covers ~30 labeled
  pairs across same-shape and different-shape cases.
- Optional in-memory cache (NoOp default) and RabbitMQ-based multi-node
  cache invalidation broadcaster (NoOp default). Both opt-in.
- Standalone AddPersistedOperationStore extension for non-GraphQL
  hosts (admin tools, manifest uploaders).

Tests: 163 passing across unit (allowlist, introspection detector, id
parser, builder validation, shape-diff fixture, middleware, cache,
no-op broadcaster) and integration (storage CRUD, history rows,
broadcast publishing, cache hit, EF round-trip persistence, composite
PK isolation).
@Theauxm Theauxm force-pushed the feat/persisted-operations branch from 1f0d893 to 5f02337 Compare May 8, 2026 19:19
@Theauxm Theauxm merged commit c269ac2 into main May 8, 2026
1 check passed
@Theauxm Theauxm deleted the feat/persisted-operations branch May 8, 2026 19:23
@traxsharp
Copy link
Copy Markdown

traxsharp Bot commented May 8, 2026

This PR is included in version 1.26.0

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.

1 participant