Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import graphql.nadel.definition.hydration.NadelIdHydrationDefinition
import graphql.nadel.engine.util.makeFieldCoordinates
import graphql.nadel.engine.util.unwrapAll
import graphql.schema.GraphQLArgument
import graphql.schema.GraphQLEnumType
import graphql.schema.GraphQLFieldDefinition
import graphql.schema.GraphQLFieldsContainer
import graphql.schema.GraphQLInputFieldsContainer
Expand Down Expand Up @@ -304,6 +305,29 @@ data class NadelHydrationConditionIncompatibleValueError(
override val subject = virtualField
}

data class NadelHydrationConditionInvalidEnumValueError(
val parentType: NadelServiceSchemaElement,
val virtualField: GraphQLFieldDefinition,
val hydration: NadelHydrationDefinition,
val pathToConditionField: List<String>,
val enumType: GraphQLEnumType,
val suppliedValue: String,
) : NadelSchemaValidationError {
override val message = run {
val parentTypeName = parentType.overall.name
val conditionField = pathToConditionField.joinToString(separator = ".")
val enumValues = enumType.values.joinToString(separator = ", ") { it.name }
getHydrationErrorMessage(
parentType,
virtualField,
hydration,
reason = "condition field $parentTypeName.$conditionField must equal one of enum values [$enumValues] but was \"$suppliedValue\"",
)
}

override val subject = virtualField
}

data class NadelHydrationConditionInvalidRegexError(
val parentType: NadelServiceSchemaElement,
val virtualField: GraphQLFieldDefinition,
Expand Down Expand Up @@ -535,7 +559,7 @@ data class NadelHydrationResultConditionUnsupportedFieldTypeError(
parentType,
virtualField,
hydration,
reason = "condition field $parentTypeName.$conditionField must to be of type $str, $int or $id but is $conditionFieldType",
reason = "condition field $parentTypeName.$conditionField must be of type $str, $int, $id or enum but is $conditionFieldType",
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import graphql.nadel.engine.util.isList
import graphql.nadel.engine.util.unwrapAll
import graphql.nadel.engine.util.unwrapNonNull
import graphql.nadel.validation.NadelHydrationConditionIncompatibleValueError
import graphql.nadel.validation.NadelHydrationConditionInvalidEnumValueError
import graphql.nadel.validation.NadelHydrationConditionInvalidRegexError
import graphql.nadel.validation.NadelHydrationConditionMatchesPredicateRequiresStringFieldError
import graphql.nadel.validation.NadelHydrationConditionStartsWithPredicateRequiresStringFieldError
Expand All @@ -29,8 +30,9 @@ import graphql.nadel.validation.NadelValidationInterimResult.Error.Companion.asI
import graphql.nadel.validation.NadelValidationInterimResult.Success.Companion.asInterimSuccess
import graphql.nadel.validation.ok
import graphql.nadel.validation.onErrorCast
import graphql.schema.GraphQLEnumType
import graphql.schema.GraphQLFieldDefinition
import graphql.schema.GraphQLScalarType
import graphql.schema.GraphQLType
import java.math.BigInteger

private data class NadelHydrationConditionValidationContext(
Expand All @@ -40,6 +42,28 @@ private data class NadelHydrationConditionValidationContext(
val condition: NadelHydrationConditionDefinition,
)

private sealed class NadelConditionFieldType {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Nice

abstract val graphQLType: GraphQLType

object StringType : NadelConditionFieldType() {
override val graphQLType = GraphQLString
}

object IntType : NadelConditionFieldType() {
override val graphQLType = GraphQLInt
}

object IdType : NadelConditionFieldType() {
override val graphQLType = GraphQLID
}

data class EnumType(
val enumType: GraphQLEnumType,
) : NadelConditionFieldType() {
override val graphQLType = enumType
}
}

internal class NadelHydrationConditionValidation {
context(NadelValidationContext)
fun validateHydrations(
Expand Down Expand Up @@ -93,40 +117,34 @@ internal class NadelHydrationConditionValidation {
}

/**
* The result field used in a condition must be a scalar.
* The result field used in a condition must have a type supported by [NadelHydrationCondition].
*/
context(NadelValidationContext, NadelHydrationConditionValidationContext)
private fun getResultFieldType(
pathToConditionField: List<String>,
conditionField: GraphQLFieldDefinition,
): NadelValidationInterimResult<GraphQLScalarType> {
): NadelValidationInterimResult<NadelConditionFieldType> {
val conditionFieldOutputType = if (isConditionFieldSameAsBatchId(pathToConditionField)) {
conditionField.type.unwrapAll() // Accept list if it's the batch ID, so that each batch ID can have its own instruction
} else {
conditionField.type.unwrapNonNull() // We do not accept list, hence not unwrapAll
}

val scalarType = conditionFieldOutputType as? GraphQLScalarType
?: return NadelHydrationResultConditionUnsupportedFieldTypeError(
parentType = parent,
virtualField = virtualField,
hydration = hydration,
pathToConditionField = pathToConditionField,
conditionField = conditionField,
).asInterimError()

// Limit sourceField to simple values like String, Boolean, Int etc.
if (!(scalarType == GraphQLString || scalarType == GraphQLInt || scalarType == GraphQLID)) {
return NadelHydrationResultConditionUnsupportedFieldTypeError(
parentType = parent,
virtualField = virtualField,
hydration = hydration,
pathToConditionField = pathToConditionField,
conditionField = conditionField,
).asInterimError()
}
val conditionFieldType = when (conditionFieldOutputType) {
GraphQLString -> NadelConditionFieldType.StringType
GraphQLInt -> NadelConditionFieldType.IntType
GraphQLID -> NadelConditionFieldType.IdType
is GraphQLEnumType -> NadelConditionFieldType.EnumType(conditionFieldOutputType)
else -> null
} ?: return NadelHydrationResultConditionUnsupportedFieldTypeError(
parentType = parent,
virtualField = virtualField,
hydration = hydration,
pathToConditionField = pathToConditionField,
conditionField = conditionField,
).asInterimError()

return scalarType.asInterimSuccess()
return conditionFieldType.asInterimSuccess()
}

/**
Expand Down Expand Up @@ -171,7 +189,7 @@ internal class NadelHydrationConditionValidation {
context(NadelValidationContext, NadelHydrationConditionValidationContext)
private fun validateResultCondition(
resultCondition: NadelHydrationResultConditionDefinition,
conditionFieldType: GraphQLScalarType,
conditionFieldType: NadelConditionFieldType,
predicateDefinition: NadelHydrationResultFieldPredicateDefinition,
): NadelValidationInterimResult<NadelHydrationCondition> {
if (predicateDefinition.equals != null) {
Expand Down Expand Up @@ -203,11 +221,11 @@ internal class NadelHydrationConditionValidation {

context(NadelValidationContext, NadelHydrationConditionValidationContext)
private fun validateStartsWithCondition(
conditionFieldType: GraphQLScalarType,
conditionFieldType: NadelConditionFieldType,
resultCondition: NadelHydrationResultConditionDefinition,
startsWith: String,
): NadelValidationInterimResult<NadelHydrationCondition> {
return if (conditionFieldType == GraphQLString || conditionFieldType == GraphQLID) {
return if (conditionFieldType == NadelConditionFieldType.StringType || conditionFieldType == NadelConditionFieldType.IdType) {
NadelHydrationCondition.StringResultStartsWith(
fieldPath = NadelQueryPath(resultCondition.pathToSourceField),
prefix = startsWith,
Expand All @@ -224,11 +242,11 @@ internal class NadelHydrationConditionValidation {

context(NadelValidationContext, NadelHydrationConditionValidationContext)
private fun validateMatchesCondition(
conditionFieldType: GraphQLScalarType,
conditionFieldType: NadelConditionFieldType,
resultCondition: NadelHydrationResultConditionDefinition,
matches: String,
): NadelValidationInterimResult<NadelHydrationCondition> {
return if (conditionFieldType == GraphQLString || conditionFieldType == GraphQLID) {
return if (conditionFieldType == NadelConditionFieldType.StringType || conditionFieldType == NadelConditionFieldType.IdType) {
val regex = try {
matches.toRegex()
} catch (e: Exception) {
Expand Down Expand Up @@ -256,16 +274,32 @@ internal class NadelHydrationConditionValidation {

context(NadelValidationContext, NadelHydrationConditionValidationContext)
private fun validateEqualsCondition(
conditionFieldType: GraphQLScalarType,
conditionFieldType: NadelConditionFieldType,
resultCondition: NadelHydrationResultConditionDefinition,
expectedValue: Any,
): NadelValidationInterimResult<NadelHydrationCondition> {
if (expectedValue is String && (conditionFieldType == GraphQLString || conditionFieldType == GraphQLID)) {
if (expectedValue is String && (conditionFieldType == NadelConditionFieldType.StringType || conditionFieldType == NadelConditionFieldType.IdType)) {
return NadelHydrationCondition.StringResultEquals(
fieldPath = NadelQueryPath(resultCondition.pathToSourceField),
value = expectedValue,
).asInterimSuccess()
} else if (expectedValue is String && conditionFieldType is NadelConditionFieldType.EnumType) {
if (conditionFieldType.enumType.getValue(expectedValue) == null) {
return NadelHydrationConditionInvalidEnumValueError(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Nice

parentType = parent,
virtualField = virtualField,
hydration = hydration,
pathToConditionField = resultCondition.pathToSourceField,
enumType = conditionFieldType.enumType,
suppliedValue = expectedValue,
).asInterimError()
}

return NadelHydrationCondition.StringResultEquals(
fieldPath = NadelQueryPath(resultCondition.pathToSourceField),
value = expectedValue,
).asInterimSuccess()
} else if (expectedValue is BigInteger && (conditionFieldType == GraphQLInt || conditionFieldType == GraphQLID)) {
} else if (expectedValue is BigInteger && (conditionFieldType == NadelConditionFieldType.IntType || conditionFieldType == NadelConditionFieldType.IdType)) {
return NadelHydrationCondition.LongResultEquals(
fieldPath = NadelQueryPath(resultCondition.pathToSourceField),
value = expectedValue.toLong(),
Expand All @@ -276,7 +310,7 @@ internal class NadelHydrationConditionValidation {
virtualField = virtualField,
hydration = hydration,
pathToConditionField = resultCondition.pathToSourceField,
requiredType = conditionFieldType,
requiredType = conditionFieldType.graphQLType,
suppliedValue = expectedValue,
).asInterimError()
}
Expand Down
Loading
Loading