Management mutations and queries

UsePersistedOperations registers six fields on the schema for browsing and editing persisted operations, all under the operations.persistedOperations namespace. This matches the layout for every other Trax management feature (operations.manifestGroups, operations.deadLetters, etc.). Every field calls IPersistedOperationsService, the same service the Trax dashboard's persisted-operations pages call, so both accept and refuse the same things.

These fields always bypass persisted-operation enforcement (PersistedOperationEnforcementMiddleware, in the HotChocolate execution pipeline), because persisting them by id would be a chicken-and-egg. They are protected by the operations namespace's authorization posture: GateOperations(...), the builder's RequireAuthorization(), or an explicit AllowAnonymousOperations(). A host that exposes the namespace with none of the three refuses to start.

Mutations

uploadPersistedOperation

Insert or update an operation. Runs schema validation, requires exactly one operation in the document, then runs the shape-diff guardrail.

Input fieldTypeRequiredNotes
idString!yesBuild-time-stable identifier. Opaque string - no parse rule. The <name>_v<N> convention is recommended for readability.
documentString!yesThe GraphQL document the id resolves to.
descriptionStringnoOperator-facing note recorded on the row.
bypassShapeDiffBooleannoWhen true, allows an edit that changes the response shape. Default false.
versionIntnoOperator-controlled metadata stored on the row. Not used for routing. Default 0.
tenantKeyStringnoTenant scope. Null targets the single-tenant row set.

Payload: { success, operation, errors[] }.

deactivatePersistedOperation

Soft-delete; subsequent requests for the id resolve to null. The reason is required and recorded in the audit log.

Input fieldTypeRequired
idString!yes
reasonString!yes
tenantKeyStringno

restorePersistedOperation

Reactivate a deactivated row.

Input fieldTypeRequired
idString!yes
tenantKeyStringno

Queries

persistedOperations(filter, take, skip)

Paginated list, newest-updated first.

Filter fieldTypeNotes
isActiveBooleanWhen set, restricts to active or deactivated rows.
tenantKeyStringTenant scope.
idStartsWithStringPrefix filter on the id.

Defaults: take is 50 when not given, zero or negative, or over 200 (a take of 500 returns 50 rows, not 200); skip is floored at 0.

persistedOperation(id, tenantKey)

Look up a single row. Returns null when missing.

persistedOperationHistory(id, tenantKey, take, skip)

Audit history for an operation, most-recent first. Same take / skip defaults as persistedOperations.

Error payload

All mutations return errors via the payload errors[] array; mutations never throw to the client. Each entry has:

FieldTypeNotes
codeString!Stable code: PARSE_FAILED, SCHEMA_VALIDATION_FAILED, SHAPE_DIFF_VIOLATION, NOT_FOUND, INVALID_INPUT (an empty required field, or a document with other than one operation).
messageString!Human-readable message.
locations[Location!]1-based line / column. Present on parse errors and most schema-validation errors.
path[String!]Response path. Present on some schema-validation errors.
oldFingerprintStringPresent only on SHAPE_DIFF_VIOLATION.
newFingerprintStringPresent only on SHAPE_DIFF_VIOLATION.

See PersistedOperationException for the underlying exception types and how the codes map.

Example: upload

mutation Upload($input: UploadPersistedOperationInput!) {
  operations {
    persistedOperations {
      uploadPersistedOperation(input: $input) {
        success
        operation { id shapeFingerprint isActive }
        errors {
          code
          message
          locations { line column }
          oldFingerprint
          newFingerprint
        }
      }
    }
  }
}
{
  "input": {
    "id": "userProfile_v1",
    "document": "query UserProfile($id: Int!) { user(id: $id) { id name email } }"
  }
}