Skip to content

Fix hydration selection set on nested input objects - #707

Merged
gnawf merged 7 commits into
masterfrom
executable-source-fields
Dec 2, 2025
Merged

Fix hydration selection set on nested input objects#707
gnawf merged 7 commits into
masterfrom
executable-source-fields

Conversation

@gnawf

@gnawf gnawf commented Dec 1, 2025

Copy link
Copy Markdown
Collaborator

Refer to the test/src/test/kotlin/graphql/nadel/tests/next/fixtures/hydration/HydrationNestedObjectInputTest.kt test.

Effectively when the user sets up a hydration

type Issue {
  assigneeFilter: IssueUserSearchInput
  assignee: User
    @hydrated(
      field: "userByFilter"
      arguments: [{name: "filter", value: "$source.assigneeFilter"}]
    )
}
type IssueUserSearchInput {
  filter: [IssueUserSearchFilterInput!]
}
type IssueUserSearchFilterInput {
  parentId: String
  subEntityTypes: [String!]
}

and

type Query {
  userByFilter(filter: UserSearchInput!): User
}
input UserSearchInput {
  filter: [UserSearchFilterInput!]
}
input UserSearchFilterInput {
  parentId: String
  subEntityTypes: [String!]
}
type User {
  id: ID!
  name: String
}

We need to ensure the selection set is

assigneeFilter {
  filter {
    parentId
    subEntityTypes
  }
}

whereas it's currently

assigneeFilter {
  filter
}

as we don't handle nested selection sets.

Also refactored it to create the ENFs ahead of time during validation.

@github-actions

github-actions Bot commented Dec 1, 2025

Copy link
Copy Markdown

Test Results

  614 files  +  614    614 suites  +614   1m 6s ⏱️ + 1m 6s
2 030 tests +2 030  1 508 ✅ +1 508  522 💤 +522  0 ❌ ±0 
2 038 runs  +2 038  1 516 ✅ +1 516  522 💤 +522  0 ❌ ±0 

Results for commit 2cb6fff. ± Comparison against base commit 6f26e6b.

♻️ This comment has been updated with latest results.

@gnawf
gnawf force-pushed the executable-source-fields branch from 854f7fa to f835bab Compare December 1, 2025 06:37
aliasHelper.toArtificial(it)
}
.toList()
}

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

New code here, the ENFs are built during validation time, now we just insert them from the instruction.

deferSupport = nadelExecutionHints.deferSupport
shortCircuitEmptyQuery = nadelExecutionHints.shortCircuitEmptyQuery
sharedTypeRenames = nadelExecutionHints.sharedTypeRenames
virtualTypeSupport = nadelExecutionHints.virtualTypeSupport

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Added missing copy.

| }
| batch_hydration__related__relatedIds: relatedIds {
| issueId
| projectId

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

The way these fields are generated was changed, now it's merged together which should be better.

Should execute the same way since fields would be merged at execution time.

open fun makeExecutionHints(): NadelExecutionHints.Builder {
return NadelExecutionHints.Builder()
.hydrationFilterObjectTypes { true }
.hydrationExecutableSourceFields { true }

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Test have new source fields code on by default.

* This can be the fields described in [NadelHydrationArgument.ValueSource.FieldResultValue.queryPathToField]
* or [NadelBatchHydrationMatchStrategy.MatchObjectIdentifier.sourceId].
*/
val executableSourceFields: List<ExecutableNormalizedField>

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

We now build the entire ExecutableNormalizedField ahead of time instead of just computing the required NadelQueryPath

artificialFields = state.instructionsByObjectTypeNames
.flatMap { (objectTypeName, instructions) ->
NadelHydrationFieldsBuilder.makeRequiredSourceFields(
hints = executionContext.hints,

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

For FFing

val hydrationSourceType = hydrationValueSource.fieldDefinition.type.unwrapAll()

if (hydrationSourceType is GraphQLObjectType) {
return createObjectField(argument, hydrationValueSource)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This is the main change, before this was

return hydrationSourceType.fields
.map { field ->
hydrationValueSource.queryPathToField.plus(field.name)
}
.asInterimSuccess()

Which only selected one level of children.

Now it iterates through the required input object fields and then creates the selection set from that. It does this recursively to create the correct selection set.

@gnawf gnawf changed the title Executable source fields Fix hydration selection set on nested input objects Dec 2, 2025
@gnawf
gnawf marked this pull request as ready for review December 2, 2025 03:15
@gnawf
gnawf merged commit bf672e0 into master Dec 2, 2025
6 checks passed
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