Skip to content

Add Input/Output model splitting to GraphQL mutation engine#60

Draft
FionaBronwen wants to merge 2 commits intofionabronwen/emitter-integrationfrom
fionabronwen/input-output-splitting
Draft

Add Input/Output model splitting to GraphQL mutation engine#60
FionaBronwen wants to merge 2 commits intofionabronwen/emitter-integrationfrom
fionabronwen/input-output-splitting

Conversation

@FionaBronwen
Copy link

Summary

This PR adds usage-aware model mutations that create separate input and output type variants based on how models are used in operations. This brings the TSP models in line with the GraphQL requirement of separate input and output types.

Changes

  • GraphQLMutationOptions - Added usageFlag and mutationKey for usage-aware caching
  • GraphQLModelMutation - Adds "Input" suffix for input types
  • GraphQLMutationEngine.mutateModel() - Returns ModelMutationResult with separate input/output mutations
  • ModelTypeMap - Supports GraphQLInputObjectType for input types
  • Schema emitter - Handles both input and output model variants
  • New tests for input/output model splitting

How it works

  1. resolveUsages() analyzes type usage at engine creation
  2. mutateModel() checks usage flags and creates appropriate variants
  3. Input types get "Input" suffix (e.g., PersonInput)
  4. Output types keep original name (e.g., Person)
  5. Types used both ways get both variants

@FionaBronwen FionaBronwen force-pushed the fionabronwen/emitter-integration branch from b4208ef to 336b711 Compare January 7, 2026 20:06
@FionaBronwen FionaBronwen force-pushed the fionabronwen/input-output-splitting branch from d900bb0 to 1bd1b71 Compare January 7, 2026 20:06
@FionaBronwen FionaBronwen force-pushed the fionabronwen/emitter-integration branch from 336b711 to ccc10f6 Compare January 7, 2026 20:43
@FionaBronwen FionaBronwen force-pushed the fionabronwen/input-output-splitting branch from 1bd1b71 to 0c1ebbc Compare January 7, 2026 20:43
@FionaBronwen FionaBronwen force-pushed the fionabronwen/emitter-integration branch from ccc10f6 to 9c89a06 Compare January 7, 2026 21:15
@FionaBronwen FionaBronwen force-pushed the fionabronwen/input-output-splitting branch from 0c1ebbc to da1b154 Compare January 7, 2026 21:51
@FionaBronwen FionaBronwen force-pushed the fionabronwen/emitter-integration branch 8 times, most recently from 8cd1884 to c469bda Compare January 8, 2026 17:18
@FionaBronwen FionaBronwen force-pushed the fionabronwen/input-output-splitting branch 4 times, most recently from 64b077f to 50cd3e4 Compare January 8, 2026 17:32
@FionaBronwen FionaBronwen force-pushed the fionabronwen/emitter-integration branch 2 times, most recently from decde60 to bcfae7d Compare January 23, 2026 21:09
- Update GraphQLMutationOptions with usageFlag and mutationKey
- GraphQLModelMutation adds 'Input' suffix for input types
- GraphQLMutationEngine.mutateModel() returns ModelMutationResult with
  separate input/output mutations based on resolveUsages()
- ModelTypeMap supports GraphQLInputObjectType for input types
- Schema emitter handles both input and output model variants
- Add 5 new tests for input/output splitting behavior
@FionaBronwen FionaBronwen force-pushed the fionabronwen/input-output-splitting branch from 50cd3e4 to 56e78bf Compare January 23, 2026 21:10
@FionaBronwen FionaBronwen marked this pull request as ready for review January 28, 2026 19:07
@FionaBronwen FionaBronwen removed the request for review from swatkatz January 28, 2026 19:07
this.engine = new MutationEngine(tk, graphqlMutationRegistry);

// Resolve usages once at construction time
this.usageTracker = resolveUsages(namespace);

Choose a reason for hiding this comment

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

This is an expensive operation so we probably don't want to do it in the constructor.

Expensive calls can be made lazily; i.e. if we ask for usage information (in getUsage) and we have not yet resolved usages, we do so (and store the result). Callers should also have the flexibility to trigger the resolve explicitly to optimize their call flow.

*/
readonly usageFlag: UsageFlags;

constructor(usageFlag: UsageFlags = UsageFlags.None) {

Choose a reason for hiding this comment

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

It's valid for me to do

new GraphQLMutationOptions(usageFlag: UsageFlags.Input | UsageFlags.Output);

or any other combination of the flags.

So we either need to account for this in mutationKey(), or do an explicit check at construction time that usageFlag is only the values that we expect.

}
return this.materializeOutputType(name, tspModel);
return this.materializeOutputType(tspModel, name);
}

Choose a reason for hiding this comment

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

Why do we need to pass the name explicitly here; can't we access the name property on the tspModel in the functions we're calling?

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