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

OneOf validation rule location suggestions #1

Merged
merged 4 commits into from
Oct 17, 2024
Merged
Changes from all 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
165 changes: 65 additions & 100 deletions spec/Section 5 -- Validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ type Query {
}

type Mutation {
addPet(pet: PetInput): Pet
addPet(pet: PetInput!): Pet
addPets(pet: [PetInput!]!): [Pet]
}

enum DogCommand {
Expand Down Expand Up @@ -1351,6 +1352,12 @@ query goodComplexDefaultValue($search: FindDogInput = { name: "Fido" }) {
name
}
}

mutation addPet($pet: PetInput! = { cat: { name: "Brontie" } }) {
addPet(pet: $pet) {
name
}
}
```

Non-coercible values (such as a String into an Int) are invalid. The following
Expand All @@ -1366,6 +1373,24 @@ query badComplexValue {
name
}
}

mutation oneOfWithNoFields {
addPet(pet: {}) {
name
}
}

mutation oneOfWithTwoFields($dog: DogInput) {
addPet(pet: { cat: { name: "Brontie" }, dog: $dog }) {
name
}
}

mutation listOfOneOfWithNullableVariable($dog: DogInput) {
addPets(pets: [{ dog: $dog }]) {
name
}
}
```

### Input Object Field Names
Expand Down Expand Up @@ -1452,103 +1477,6 @@ arguments, an input object may have required fields. An input field is required
if it has a non-null type and does not have a default value. Otherwise, the
input object field is optional.

### OneOf Input Objects Have Exactly One Field

**Formal Specification**

- For each {operation} in {document}:
- Let {oneofInputObjects} be all OneOf Input Objects transitively included in
the {operation}.
- For each {oneofInputObject} in {oneofInputObjects}:
- Let {fields} be the fields provided by {oneofInputObject}.
- {fields} must contain exactly one entry.
- Let {field} be the sole entry in {fields}.
- Let {value} be the value of {field}.
- {value} must not be the {null} literal.
- If {value} is a variable:
- Let {variableName} be the name of {variable}.
- Let {variableDefinition} be the {VariableDefinition} named
{variableName} defined within {operation}.
- Let {variableType} be the expected type of {variableDefinition}.
- {variableType} must be a non-null type.

**Explanatory Text**

OneOf Input Objects require that exactly one field must be supplied and that
field must not be {null}.

An empty OneOf Input Object is invalid.

```graphql counter-example
mutation addPet {
addPet(pet: {}) {
name
}
}
```

Multiple fields are not allowed.

```graphql counter-example
mutation addPet($cat: CatInput, $dog: DogInput) {
addPet(pet: { cat: $cat, dog: $dog }) {
name
}
}
```

```graphql counter-example
mutation addPet($dog: DogInput) {
addPet(pet: { cat: { name: "Brontie" }, dog: $dog }) {
name
}
}
```

```graphql counter-example
mutation addPet {
addPet(pet: { cat: { name: "Brontie" }, dog: null }) {
name
}
}
```

Variables used for OneOf Input Object fields must be non-nullable.

```graphql example
mutation addPet($cat: CatInput!) {
addPet(pet: { cat: $cat }) {
name
}
}
```

```graphql counter-example
mutation addPet($cat: CatInput) {
addPet(pet: { cat: $cat }) {
name
}
}
```

If a field with a literal value is present then the value must not be {null}.

```graphql example
mutation addPet {
addPet(pet: { cat: { name: "Brontie" } }) {
name
}
}
```

```graphql counter-example
mutation addPet {
addPet(pet: { cat: null }) {
name
}
}
```

## Directives

### Directives Are Defined
Expand Down Expand Up @@ -1989,8 +1917,8 @@ IsVariableUsageAllowed(variableDefinition, variableUsage):
- Let {variableType} be the expected type of {variableDefinition}.
- Let {locationType} be the expected type of the {Argument}, {ObjectField}, or
{ListValue} entry where {variableUsage} is located.
- If {locationType} is a non-null type AND {variableType} is NOT a non-null
type:
- If {IsNonNullPosition(locationType, variableUsage)} AND {variableType} is NOT
a non-null type:
- Let {hasNonNullVariableDefaultValue} be {true} if a default value exists for
{variableDefinition} and is not the value {null}.
- Let {hasLocationDefaultValue} be {true} if a default value exists for the
Expand All @@ -2001,6 +1929,15 @@ IsVariableUsageAllowed(variableDefinition, variableUsage):
- Return {AreTypesCompatible(variableType, nullableLocationType)}.
- Return {AreTypesCompatible(variableType, locationType)}.

IsNonNullPosition(locationType, variableUsage):

- If {locationType} is a non-null type, return {true}.
- If the location of {variableUsage} is an {ObjectField}:
- Let {parentLocationType} be the expected type of {ObjectField}'s parent
{ObjectValue}.
- If {parentLocationType} is a OneOf Input Object type, return {true}.
- Return {false}.

AreTypesCompatible(variableType, locationType):

- If {locationType} is a non-null type:
Expand Down Expand Up @@ -2089,6 +2026,34 @@ query listToNonNullList($booleanList: [Boolean]) {
This would fail validation because a `[T]` cannot be passed to a `[T]!`.
Similarly a `[T]` cannot be passed to a `[T!]`.

Variables used for OneOf Input Object fields must be non-nullable.

```graphql example
mutation addCat($cat: CatInput!) {
addPet(pet: { cat: $cat }) {
name
}
}
mutation addCatWithDefault($cat: CatInput! = { name: "Brontie" }) {
addPet(pet: { cat: $cat }) {
name
}
}
```

```graphql counter-example
mutation addNullableCat($cat: CatInput) {
addPet(pet: { cat: $cat }) {
name
}
}
mutation addNullableCatWithDefault($cat: CatInput = { name: "Brontie" }) {
addPet(pet: { cat: $cat }) {
name
}
}
```

**Allowing Optional Variables When Default Values Exist**

A notable exception to typical variable type compatibility is allowing a
Expand Down
Loading