Skip to content
Open
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
6 changes: 6 additions & 0 deletions execution/engine/engine_config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,12 @@ directive @specifiedBy(
url: String!
) on SCALAR

"""
The @oneOf built-in directive is used within the type system definition language
to indicate an Input Object is a OneOf Input Object.
"""
directive @oneOf on INPUT_OBJECT

"""
A Directive provides a way to describe alternate runtime execution and type validation behavior in a GraphQL document.
In some cases, you need to provide options to alter GraphQL's execution behavior
Expand Down
8 changes: 8 additions & 0 deletions execution/engine/testdata/full_introspection.json
Original file line number Diff line number Diff line change
Expand Up @@ -735,6 +735,14 @@
"defaultValue": null
}
]
},
{
"name": "oneOf",
"description": "The @oneOf built-in directive is used within the type system definition language\nto indicate an Input Object is a OneOf Input Object.",
"locations": [
"INPUT_OBJECT"
],
"args": []
}
]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -769,6 +769,14 @@
"defaultValue": null
}
]
},
{
"name": "oneOf",
"description": "The @oneOf built-in directive is used within the type system definition language\nto indicate an Input Object is a OneOf Input Object.",
"locations": [
"INPUT_OBJECT"
],
"args": []
}
]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -831,6 +831,15 @@
"defaultValue": null
}
]
},
{
"__typename": "__Directive",
"name": "oneOf",
"description": "The @oneOf built-in directive is used within the type system definition language\nto indicate an Input Object is a OneOf Input Object.",
"locations": [
"INPUT_OBJECT"
],
"args": []
}
]
}
Expand Down
6 changes: 6 additions & 0 deletions v2/pkg/asttransform/baseschema.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,12 @@ directive @deprecated(

directive @specifiedBy(url: String!) on SCALAR

"""
The @oneOf built-in directive is used within the type system definition language
to indicate an Input Object is a OneOf Input Object.
"""
directive @oneOf on INPUT_OBJECT

"""
A Directive provides a way to describe alternate runtime execution and type validation behavior in a GraphQL document.
In some cases, you need to provide options to alter GraphQL's execution behavior
Expand Down
6 changes: 6 additions & 0 deletions v2/pkg/asttransform/fixtures/complete.golden
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@ directive @specifiedBy(
url: String!
) on SCALAR

"""
The @oneOf built-in directive is used within the type system definition language
to indicate an Input Object is a OneOf Input Object.
"""
directive @oneOf on INPUT_OBJECT

"""
A Directive provides a way to describe alternate runtime execution and type validation behavior in a GraphQL document.
In some cases, you need to provide options to alter GraphQL's execution behavior
Expand Down
6 changes: 6 additions & 0 deletions v2/pkg/asttransform/fixtures/custom_query_name.golden
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@ directive @specifiedBy(
url: String!
) on SCALAR

"""
The @oneOf built-in directive is used within the type system definition language
to indicate an Input Object is a OneOf Input Object.
"""
directive @oneOf on INPUT_OBJECT

"""
A Directive provides a way to describe alternate runtime execution and type validation behavior in a GraphQL document.
In some cases, you need to provide options to alter GraphQL's execution behavior
Expand Down
6 changes: 6 additions & 0 deletions v2/pkg/asttransform/fixtures/mutation_only.golden
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ directive @specifiedBy(
url: String!
) on SCALAR

"""
The @oneOf built-in directive is used within the type system definition language
to indicate an Input Object is a OneOf Input Object.
"""
directive @oneOf on INPUT_OBJECT

"""
A Directive provides a way to describe alternate runtime execution and type validation behavior in a GraphQL document.
In some cases, you need to provide options to alter GraphQL's execution behavior
Expand Down
6 changes: 6 additions & 0 deletions v2/pkg/asttransform/fixtures/schema_missing.golden
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@ directive @specifiedBy(
url: String!
) on SCALAR

"""
The @oneOf built-in directive is used within the type system definition language
to indicate an Input Object is a OneOf Input Object.
"""
directive @oneOf on INPUT_OBJECT

"""
A Directive provides a way to describe alternate runtime execution and type validation behavior in a GraphQL document.
In some cases, you need to provide options to alter GraphQL's execution behavior
Expand Down
6 changes: 6 additions & 0 deletions v2/pkg/asttransform/fixtures/simple.golden
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@ directive @specifiedBy(
url: String!
) on SCALAR

"""
The @oneOf built-in directive is used within the type system definition language
to indicate an Input Object is a OneOf Input Object.
"""
directive @oneOf on INPUT_OBJECT

"""
A Directive provides a way to describe alternate runtime execution and type validation behavior in a GraphQL document.
In some cases, you need to provide options to alter GraphQL's execution behavior
Expand Down
6 changes: 6 additions & 0 deletions v2/pkg/asttransform/fixtures/subscription_only.golden
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ directive @specifiedBy(
url: String!
) on SCALAR

"""
The @oneOf built-in directive is used within the type system definition language
to indicate an Input Object is a OneOf Input Object.
"""
directive @oneOf on INPUT_OBJECT

"""
A Directive provides a way to describe alternate runtime execution and type validation behavior in a GraphQL document.
In some cases, you need to provide options to alter GraphQL's execution behavior
Expand Down
6 changes: 6 additions & 0 deletions v2/pkg/asttransform/fixtures/subscription_renamed.golden
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ directive @specifiedBy(
url: String!
) on SCALAR

"""
The @oneOf built-in directive is used within the type system definition language
to indicate an Input Object is a OneOf Input Object.
"""
directive @oneOf on INPUT_OBJECT

"""
A Directive provides a way to describe alternate runtime execution and type validation behavior in a GraphQL document.
In some cases, you need to provide options to alter GraphQL's execution behavior
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@ directive @specifiedBy(
url: String!
) on SCALAR

"""
The @oneOf built-in directive is used within the type system definition language
to indicate an Input Object is a OneOf Input Object.
"""
directive @oneOf on INPUT_OBJECT

"""
A Directive provides a way to describe alternate runtime execution and type validation behavior in a GraphQL document.
In some cases, you need to provide options to alter GraphQL's execution behavior
Expand Down
73 changes: 73 additions & 0 deletions v2/pkg/astvalidation/operation_rule_values.go
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,11 @@ func (v *valuesVisitor) valueSatisfiesInputObjectTypeDefinition(value ast.Value,
return false
}

// Validate @oneOf constraint if present
if v.objectValueViolatesOneOf(value, inputObjectTypeDefinition) {
return false
}

return true
}

Expand Down Expand Up @@ -464,6 +469,74 @@ func (v *valuesVisitor) objectValueHasDuplicateFields(objectValue int) bool {
return hasDuplicates
}

// objectValueViolatesOneOf checks if an input object value violates the @oneOf directive constraint.
func (v *valuesVisitor) objectValueViolatesOneOf(objectValue ast.Value, defRef int) bool {
def := v.definition.InputObjectTypeDefinitions[defRef]
// Check if the input object type has @oneOf directive
if !def.HasDirectives {
return false
}
hasOneOfDirective := def.Directives.HasDirectiveByName(v.definition, "oneOf")
if !hasOneOfDirective {
return false
}

fieldRefs := v.operation.ObjectValues[objectValue.Ref].Refs
if len(fieldRefs) != 1 {
objName := v.definition.InputObjectTypeDefinitionNameBytes(defRef)
v.Report.AddExternalError(operationreport.ErrOneOfInputObjectFieldCount(objName, len(fieldRefs), objectValue.Position))
return true
}

type nullableVar struct {
fieldRef int
fieldValue ast.Value
variableDefinitionRef int
}
var nullableVars []nullableVar

for _, fieldRef := range fieldRefs {
fieldValue := v.operation.ObjectFieldValue(fieldRef)

if fieldValue.Kind == ast.ValueKindNull {
objName := v.definition.InputObjectTypeDefinitionNameBytes(defRef)
fieldName := v.operation.ObjectFieldNameBytes(fieldRef)
v.Report.AddExternalError(operationreport.ErrOneOfInputObjectNullValue(objName, fieldName, fieldValue.Position))
return true
}

if fieldValue.Kind == ast.ValueKindVariable {
// For variables, check if the variable type is nullable
variableDefinitionRef, variableTypeRef, _, ok := v.operationVariableType(fieldValue.Ref)
if !ok {
continue
}

// Collect nullable variables
if v.operation.Types[variableTypeRef].TypeKind != ast.TypeKindNonNull {
nullableVars = append(nullableVars, nullableVar{
fieldRef,
fieldValue,
variableDefinitionRef})
}
}
}

// If exactly one field, but it's a nullable variable, report that error
if len(nullableVars) > 0 {
violation := nullableVars[0]
objName := v.definition.InputObjectTypeDefinitionNameBytes(defRef)
fieldName := v.operation.ObjectFieldNameBytes(violation.fieldRef)
variableName := v.operation.VariableValueNameBytes(violation.fieldValue.Ref)
v.Report.AddExternalError(operationreport.ErrOneOfInputObjectNullableVariable(
objName, fieldName, variableName, violation.fieldValue.Position,
v.operation.VariableDefinitions[violation.variableDefinitionRef].VariableValue.Position))
return true
}

return false
}

func (v *valuesVisitor) objectFieldDefined(objectField, inputObjectTypeDefinition int) bool {
name := v.operation.ObjectFieldNameBytes(objectField)
for _, i := range v.definition.InputObjectTypeDefinitions[inputObjectTypeDefinition].InputFieldsDefinition.Refs {
Expand Down
Loading
Loading