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

"""
The @oneOf built-in directive marks an input object as a OneOf Input Object.
Exactly one field must be provided and its value must be non-null at runtime.
All fields defined within a @oneOf input must be nullable in the schema.
"""
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
7 changes: 7 additions & 0 deletions v2/pkg/asttransform/baseschema.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,13 @@ directive @deprecated(

directive @specifiedBy(url: String!) on SCALAR

"""
The @oneOf built-in directive marks an input object as a OneOf Input Object.
Exactly one field must be provided and its value must be non-null at runtime.
All fields defined within a @oneOf input must be nullable in the schema.
"""
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
7 changes: 7 additions & 0 deletions v2/pkg/asttransform/fixtures/complete.golden
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,13 @@ directive @specifiedBy(
url: String!
) on SCALAR

"""
The @oneOf built-in directive marks an input object as a OneOf Input Object.
Exactly one field must be provided and its value must be non-null at runtime.
All fields defined within a @oneOf input must be nullable in the schema.
"""
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
7 changes: 7 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,13 @@ directive @specifiedBy(
url: String!
) on SCALAR

"""
The @oneOf built-in directive marks an input object as a OneOf Input Object.
Exactly one field must be provided and its value must be non-null at runtime.
All fields defined within a @oneOf input must be nullable in the schema.
"""
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
7 changes: 7 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,13 @@ directive @specifiedBy(
url: String!
) on SCALAR

"""
The @oneOf built-in directive marks an input object as a OneOf Input Object.
Exactly one field must be provided and its value must be non-null at runtime.
All fields defined within a @oneOf input must be nullable in the schema.
"""
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
7 changes: 7 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,13 @@ directive @specifiedBy(
url: String!
) on SCALAR

"""
The @oneOf built-in directive marks an input object as a OneOf Input Object.
Exactly one field must be provided and its value must be non-null at runtime.
All fields defined within a @oneOf input must be nullable in the schema.
"""
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
7 changes: 7 additions & 0 deletions v2/pkg/asttransform/fixtures/simple.golden
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,13 @@ directive @specifiedBy(
url: String!
) on SCALAR

"""
The @oneOf built-in directive marks an input object as a OneOf Input Object.
Exactly one field must be provided and its value must be non-null at runtime.
All fields defined within a @oneOf input must be nullable in the schema.
"""
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
7 changes: 7 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,13 @@ directive @specifiedBy(
url: String!
) on SCALAR

"""
The @oneOf built-in directive marks an input object as a OneOf Input Object.
Exactly one field must be provided and its value must be non-null at runtime.
All fields defined within a @oneOf input must be nullable in the schema.
"""
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
7 changes: 7 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,13 @@ directive @specifiedBy(
url: String!
) on SCALAR

"""
The @oneOf built-in directive marks an input object as a OneOf Input Object.
Exactly one field must be provided and its value must be non-null at runtime.
All fields defined within a @oneOf input must be nullable in the schema.
"""
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,13 @@ directive @specifiedBy(
url: String!
) on SCALAR

"""
The @oneOf built-in directive marks an input object as a OneOf Input Object.
Exactly one field must be provided and its value must be non-null at runtime.
All fields defined within a @oneOf input must be nullable in the schema.
"""
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
57 changes: 57 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,58 @@ 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
}

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 {
objName := v.definition.InputObjectTypeDefinitionNameBytes(defRef)
fieldName := v.operation.ObjectFieldNameBytes(fieldRef)
variableName := v.operation.VariableValueNameBytes(fieldValue.Ref)
v.Report.AddExternalError(operationreport.ErrOneOfInputObjectNullableVariable(
objName, fieldName, variableName, fieldValue.Position,
v.operation.VariableDefinitions[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