-
Notifications
You must be signed in to change notification settings - Fork 24
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
Prevent multiple source fields even if list is not leaf field #503
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,6 +8,7 @@ import graphql.nadel.dsl.RemoteArgumentSource.SourceType.ObjectField | |
import graphql.nadel.dsl.RemoteArgumentSource.SourceType.StaticArgument | ||
import graphql.nadel.dsl.UnderlyingServiceHydration | ||
import graphql.nadel.engine.util.getFieldAt | ||
import graphql.nadel.engine.util.getFieldsAlong | ||
import graphql.nadel.engine.util.isList | ||
import graphql.nadel.engine.util.isNonNull | ||
import graphql.nadel.engine.util.partitionCount | ||
|
@@ -120,30 +121,31 @@ internal class NadelHydrationValidation( | |
hydrations: List<UnderlyingServiceHydration>, | ||
): List<NadelSchemaValidationError> { | ||
if (hydrations.size > 1) { | ||
val anyListSourceInputField = hydrations | ||
.any { hydration -> | ||
val parentType = parent.underlying as GraphQLFieldsContainer | ||
val pathsToSourceFields = hydrations | ||
.asSequence() | ||
.flatMap { hydration -> | ||
hydration | ||
.arguments | ||
.asSequence() | ||
.mapNotNull { argument -> | ||
argument.remoteArgumentSource.pathToField | ||
} | ||
.any { path -> | ||
parentType.getFieldAt(path)?.type?.unwrapNonNull()?.isList == true | ||
.mapNotNull { it.remoteArgumentSource.pathToField } | ||
} | ||
.toList() | ||
|
||
val parentType = parent.underlying as GraphQLFieldsContainer | ||
val anyListSourceInputField = pathsToSourceFields | ||
.any { pathToField -> | ||
parentType | ||
.getFieldsAlong(pathToField) | ||
.any { field -> | ||
field.type.unwrapNonNull().isList | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the actual change. e.g. for Previously we only checked the end field to see if it was a list, this checks everything. |
||
} | ||
} | ||
|
||
if (anyListSourceInputField) { | ||
val sourceFields = hydrations | ||
.flatMapTo(LinkedHashSet()) { hydration -> | ||
hydration.arguments | ||
.mapNotNull { argument -> | ||
argument.remoteArgumentSource.pathToField | ||
} | ||
} | ||
|
||
if (sourceFields.size > 1) { | ||
val uniqueSourceFieldPaths = pathsToSourceFields | ||
.toSet() | ||
|
||
if (uniqueSourceFieldPaths.size > 1) { | ||
return listOf( | ||
NadelSchemaValidationError.MultipleHydrationSourceInputFields(parent, overallField), | ||
) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -170,6 +170,97 @@ class NadelHydrationValidationTest2 { | |
assertTrue(errors.single().subject.name == "data") | ||
} | ||
|
||
@Test | ||
fun `prohibit multiple source fields where list not the leaf`() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should actually be an allowed feature, but it's not supported right now. |
||
val fixture = NadelValidationTestFixture( | ||
overallSchema = mapOf( | ||
"activity" to /* language=GraphQL*/ """ | ||
type Query { | ||
myActivity: [Activity] | ||
} | ||
union ActivityContent = User | Issue | ||
type Activity { | ||
id: ID! | ||
data: [ActivityContent] | ||
@hydrated( | ||
service: "users" | ||
field: "usersByIds" | ||
arguments: [ | ||
{name: "ids", value: "$source.contexts.userId"} | ||
] | ||
) | ||
@hydrated( | ||
service: "issues" | ||
field: "issuesByIds" | ||
arguments: [ | ||
{name: "ids", value: "$source.contexts.issueId"} | ||
] | ||
) | ||
} | ||
""".trimIndent(), | ||
"users" to /* language=GraphQL*/ """ | ||
type Query { | ||
usersByIds(ids: [ID]!): [User] | ||
} | ||
type User { | ||
id: ID! | ||
name: String! | ||
} | ||
""".trimIndent(), | ||
"issues" to /* language=GraphQL*/ """ | ||
type Query { | ||
issuesByIds(ids: [ID]!): [Issue] | ||
} | ||
type Issue { | ||
id: ID! | ||
key: String | ||
} | ||
""".trimIndent(), | ||
), | ||
underlyingSchema = mapOf( | ||
"activity" to /* language=GraphQL*/ """ | ||
type Query { | ||
myActivity: [Activity] | ||
} | ||
type ActivityContext { | ||
issueId: ID | ||
userId: ID | ||
} | ||
type Activity { | ||
id: ID! | ||
contexts: [ActivityContext] | ||
} | ||
""".trimIndent(), | ||
"users" to /* language=GraphQL*/ """ | ||
type Query { | ||
usersByIds(ids: [ID]!): [User] | ||
} | ||
type User { | ||
id: ID! | ||
name: String! | ||
} | ||
type Account { | ||
id: ID! | ||
} | ||
""".trimIndent(), | ||
"issues" to /* language=GraphQL*/ """ | ||
type Query { | ||
issuesByIds(ids: [ID]!): [Issue] | ||
} | ||
type Issue { | ||
id: ID! | ||
key: String | ||
} | ||
""".trimIndent(), | ||
), | ||
) | ||
|
||
val errors = validate(fixture) | ||
assertTrue(errors.map { it.message }.isNotEmpty()) | ||
assertTrue(errors.single() is NadelSchemaValidationError.MultipleHydrationSourceInputFields) | ||
assertTrue(errors.single().subject.name == "data") | ||
} | ||
|
||
@Test | ||
fun `prohibit mixing list and non-list source input fields`() { | ||
val fixture = NadelValidationTestFixture( | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pulled the paths out to a list