Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Don't automatically add key fields to union selections #5562

Merged
merged 2 commits into from Jan 25, 2024

Conversation

BoD
Copy link
Contributor

@BoD BoD commented Jan 24, 2024

Key fields were added to selections of unions, but that's not valid as unions have no fields (except __typename), and was crashing later on here.

We can still check if selections have the key fields at least.

Copy link

netlify bot commented Jan 24, 2024

Deploy Preview for apollo-android-docs canceled.

Name Link
🔨 Latest commit 307c5b4
🔍 Latest deploy log https://app.netlify.com/sites/apollo-android-docs/deploys/65b25fcdede6390008a38729

Comment on lines 90 to 95
val requiredFieldNames = if (schema.typeDefinition(parentType) is GQLUnionTypeDefinition) {
// Can't select any fields on unions other than __typename
mutableSetOf()
} else {
schema.keyFields(parentType).toMutableSet()
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we allow @typePolicy on unions in the first place?

# This feels out of place. SomeUnion has no `id` field.
extend union SomeUnion @typePolicy(keyFields: "id") 

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah I was wondering about this :) We could forbid it too, it might be less confusing.

Copy link
Contributor

Choose a reason for hiding this comment

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

For the record, we talked about this and decided to keep the directive definition untouched but add validation that the keyFields actually exists, which should make an error here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Turns out this validation already existed but wasn't performed on interfaces nor unions.

…tion for embeddedFields and connectionFields
@BoD BoD force-pushed the fix-typepolicy-keyfields-on-union branch from 65ab702 to c636040 Compare January 25, 2024 11:31
Comment on lines +449 to +458
i.directives.forEach { directive ->
validateDirective(directive, i) {
issues.add(it.constContextError())
}
}

i.fields.forEach { gqlFieldDefinition ->
gqlFieldDefinition.directives.forEach { gqlDirective ->
validateDirective(gqlDirective, gqlFieldDefinition) {
issues.add(it.constContextError())
Copy link
Contributor

Choose a reason for hiding this comment

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

FWIW, this is where having a visitor pattern for validation would be useful. Maybe not right now but at some point.

Comment on lines 239 to 282
val keyFieldsArg = directive.arguments.firstOrNull { it.name == "keyFields" }
if (keyFieldsArg != null) {
(keyFieldsArg.value as GQLStringValue).value.parseAsGQLSelections().getOrThrow().forEach { selection ->
if (selection !is GQLField) {
registerIssue("Fragments are not supported in @$TYPE_POLICY directives", keyFieldsArg.sourceLocation)
} else if (selection.selections.isNotEmpty()) {
registerIssue("Composite fields are not supported in @$TYPE_POLICY directives", keyFieldsArg.sourceLocation)
} else {
val definition = fieldDefinitions.firstOrNull { it.name == selection.name }
if (definition == null) {
registerIssue("Field '${selection.name}' is not a valid key field for type '$type'", keyFieldsArg.sourceLocation)
}
}
}
}

val embeddedFieldsArg = directive.arguments.firstOrNull { it.name == "embeddedFields" }
if (embeddedFieldsArg != null) {
(embeddedFieldsArg.value as GQLStringValue).value.parseAsGQLSelections().getOrThrow().forEach { selection ->
if (selection !is GQLField) {
registerIssue("Fragments are not supported in @$TYPE_POLICY directives", embeddedFieldsArg.sourceLocation)
} else if (selection.selections.isNotEmpty()) {
registerIssue("Composite fields are not supported in @$TYPE_POLICY directives", embeddedFieldsArg.sourceLocation)
} else {
val definition = fieldDefinitions.firstOrNull { it.name == selection.name }
if (definition == null) {
registerIssue("Field '${selection.name}' is not a valid embedded field for type '$type'", embeddedFieldsArg.sourceLocation)
}
}
}
}

val connectionFieldsArg = directive.arguments.firstOrNull { it.name == "connectionFields" }
if (connectionFieldsArg != null) {
(connectionFieldsArg.value as GQLStringValue).value.parseAsGQLSelections().getOrThrow().forEach { selection ->
if (selection !is GQLField) {
registerIssue("Fragments are not supported in @$TYPE_POLICY directives", connectionFieldsArg.sourceLocation)
} else if (selection.selections.isNotEmpty()) {
registerIssue("Composite fields are not supported in @$TYPE_POLICY directives", connectionFieldsArg.sourceLocation)
} else {
val definition = fieldDefinitions.firstOrNull { it.name == selection.name }
if (definition == null) {
registerIssue("Field '${selection.name}' is not a valid connection field for type '$type'", connectionFieldsArg.sourceLocation)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

👍
Also, can this logic be factored in? Looks like it's very similar?

Comment on lines 119 to 121
check(issues.any { it.message.contains("Field 'id' is not a valid key field for type 'Animal'") })
check(issues.any { it.message.contains("Field 'version' is not a valid key field for type 'Node'") })
check(issues.any { it.message.contains("Field 'x' is not a valid key field for type 'Foo'") })
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: I'd change the error to something like

"No such field: 'Foo.x'"

I think there's no need to mention "key" since it's easy to see from the context and sourceLocation.

@BoD BoD merged commit 36f684a into main Jan 25, 2024
9 checks passed
@BoD BoD deleted the fix-typepolicy-keyfields-on-union branch January 25, 2024 16:40
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.

None yet

2 participants