Skip to content

Commit 327166e

Browse files
Add validation rules for @is (#190)
Co-authored-by: Michael Staib <[email protected]>
1 parent 88259f9 commit 327166e

File tree

1 file changed

+263
-0
lines changed

1 file changed

+263
-0
lines changed

spec/Section 4 -- Composition.md

Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -728,6 +728,189 @@ interface Node {
728728
}
729729
```
730730

731+
### Validate `@is` Directive
732+
733+
#### Is Invalid Syntax
734+
735+
**Error Code**
736+
737+
`IS_INVALID_SYNTAX`
738+
739+
**Severity**
740+
741+
ERROR
742+
743+
**Formal Specification**
744+
745+
- Let {types} be the set of all {INTERFACE} and {OBJECT} types in the source
746+
schema.
747+
- For each {type} in {types}:
748+
- Let {fields} be the set of all lookup fields on {type}.
749+
- Let {arguments} be the set of all arguments on {fields}.
750+
- For each {argument} in {arguments}:
751+
- If {argument} is annotated with `@is`:
752+
- Let {fieldArg} be the string value of the `field` argument of the `@is`
753+
directive on {argument}.
754+
- {fieldArg} must be be parsable as a valid {FieldSelectionMap}.
755+
756+
**Explanatory Text**
757+
758+
The `@is` directives `field` argument must be syntactically valid GraphQL. If
759+
the {FieldSelectionMap} string is malformed (e.g., missing closing braces,
760+
unbalanced quotes, invalid tokens), then the schema cannot be composed
761+
correctly. In such cases, the error `IS_INVALID_SYNTAX` is raised.
762+
763+
**Examples**
764+
765+
In the following example, the `@is` directive’s `field` argument is a valid
766+
{FieldSelectionMap} and satisfies the rule.
767+
768+
```graphql example
769+
type Query {
770+
product(id: ID! @is(field: "id")): Product @lookup
771+
}
772+
773+
type Product {
774+
id: ID!
775+
name: String
776+
}
777+
```
778+
779+
In the following counter-example, the `@is` directives `field` argument has
780+
invalid syntax because it is missing a closing brace.
781+
782+
```graphql counter-example
783+
type Query {
784+
product(id: ID! @is(field: "{ id ")): Product @lookup
785+
}
786+
787+
type Product {
788+
id: ID!
789+
name: String
790+
}
791+
```
792+
793+
#### Is Invalid Field Type
794+
795+
**Error Code**
796+
797+
`IS_INVALID_FIELD_TYPE`
798+
799+
**Severity**
800+
801+
ERROR
802+
803+
**Formal Specification**
804+
805+
- Let {schema} be the source schema to validate.
806+
- Let {compositeTypes} be the set of all composite types in {schema}.
807+
- For each {composite} in {compositeTypes}:
808+
- Let {fields} be the set of fields on {composite}.
809+
- Let {arguments} be the set of all arguments on {fields}.
810+
- For each {argument} in {arguments}:
811+
- If {argument} is **not** annotated with `@is`:
812+
- Continue
813+
- Let {fieldArg} be the value of the `field` argument of the `@is` directive
814+
on {argument}.
815+
- If {fieldArg} is **not** a string:
816+
- Produce an `IS_INVALID_FIELD_TYPE` error.
817+
818+
**Explanatory Text**
819+
820+
When using the `@is` directive, the `field` argument must always be a string
821+
that describes how the arguments can be mapped from the entity type that the
822+
lookup field resolves. If the `field` argument is provided as a type other than
823+
a string (such as an integer, boolean, or enum), the directive usage is invalid
824+
and will cause schema composition to fail.
825+
826+
**Examples**
827+
828+
In the following example, the `@is` directive’s `field` argument is a valid
829+
string and satisfies the rule.
830+
831+
```graphql example
832+
type Query {
833+
personById(id: ID! @is(field: "id")): Person @lookup
834+
}
835+
836+
type Person {
837+
id: ID!
838+
name: String
839+
}
840+
```
841+
842+
Since `field` is set to `123` (an integer) instead of a string, this violates
843+
the rule and triggers an `IS_INVALID_FIELD_TYPE` error.
844+
845+
```graphql counter-example
846+
type Query {
847+
personById(id: ID! @is(field: 123)): Person @lookup
848+
}
849+
850+
type Person {
851+
id: ID!
852+
name: String
853+
}
854+
```
855+
856+
#### Is Invalid Usage
857+
858+
**Error Code**
859+
860+
`IS_INVALID_USAGE`
861+
862+
**Severity**
863+
864+
ERROR
865+
866+
**Formal Specification**
867+
868+
- Let {schema} be the source schema to validate.
869+
- Let {compositeTypes} be the set of all composite types in {schema}.
870+
- For each {compositeType} in {compositeTypes}:
871+
- Let {fields} be the set of fields on {compositeType}.
872+
- For each {field} in {fields}:
873+
- Let {arguments} be the set of all arguments on {field}.
874+
- For each {argument} in {arguments}:
875+
- If {argument} is **not** annotated with `@is`:
876+
- Continue
877+
- {field} must be annotated with `@lookup`
878+
879+
**Explanatory Text**
880+
881+
When using the `@is` directive, the field declaring the argument must be a
882+
lookup field (i.e. have the `@lookup` directive applied).
883+
884+
**Examples**
885+
886+
In the following example, the `@is` directive is applied to an argument declared
887+
on a field with the `@lookup` directive, satisfying the rule.
888+
889+
```graphql example
890+
type Query {
891+
personById(id: ID! @is(field: "id")): Person @lookup
892+
}
893+
894+
type Person {
895+
id: ID!
896+
name: String
897+
}
898+
```
899+
900+
In the following counter-example, the `@is` directive is applied to an argument
901+
declared on a field without the `@lookup` directive, violating the rule.
902+
903+
```graphql counter-example
904+
type Query {
905+
personById(id: ID! @is(field: "id")): Person
906+
}
907+
908+
type Person {
909+
id: ID!
910+
name: String
911+
}
912+
```
913+
731914
### Validate Key Directives
732915

733916
#### Key Fields Select Invalid Type
@@ -5863,6 +6046,86 @@ type Product @inaccessible {
58636046
}
58646047
```
58656048

6049+
### Validate Is Directives
6050+
6051+
#### Is Invalid Fields
6052+
6053+
**Error Code**
6054+
6055+
`IS_INVALID_FIELDS`
6056+
6057+
**Severity**
6058+
6059+
ERROR
6060+
6061+
**Formal Specification**
6062+
6063+
- Let {schemas} be all source schemas.
6064+
- Let {compositeTypes} be the set of all composite types in {schemas}.
6065+
- For each {composite} in {compositeTypes}:
6066+
- Let {fields} be the set of fields on {composite}.
6067+
- Let {arguments} be the set of all arguments on {fields}.
6068+
- For each {argument} in {arguments}:
6069+
- If {argument} is **not** annotated with `@is`:
6070+
- Continue
6071+
- Let {schema} be the schema that defines {argument}.
6072+
- Let {declaringField} be the field that defines {argument}.
6073+
- Let {declaringType} be the type that defines {declaringField}.
6074+
- Let {otherSchemas} be the set of all {schemas} excluding {schema}.
6075+
- Let {fieldArg} be the string value of the `field` argument of the `@is`
6076+
directive on {argument}.
6077+
- Let {parsedFieldArg} be the parsed selection map from {fieldArg}.
6078+
- The parsed selection map {parsedFieldArg} must satisfy the validation
6079+
rules defined in Appendix A, Section 6.3, using:
6080+
- {declaringType} as the initial root type.
6081+
- The combined schema context formed by the union of {otherSchemas} as the
6082+
schema context except all fields marked as `@internal`
6083+
- Validation succeeds if each required field selection path can be
6084+
resolved across this combined schema context. Individual fields in the
6085+
selection may exist in different schemas; it is not required that all
6086+
fields referenced by {parsedFieldArg} reside within a single schema.
6087+
6088+
**Explanatory Text**
6089+
6090+
Even if the field selection map for `@is(field: "")` is syntactically valid,
6091+
its contents must also be valid within the composed schema. Fields must exist on
6092+
the parent type for them to be referenced by `@is`. In addition, fields
6093+
referencing unknown fields break the valid usage of `@is`, leading to an
6094+
`IS_INVALID_FIELDS` error.
6095+
6096+
**Examples**
6097+
6098+
In the following example, the `@is` directive’s `field` argument is a valid
6099+
field selection map and satisfies the rule.
6100+
6101+
```graphql example
6102+
# Schema A
6103+
type Query {
6104+
personById(id: ID! @is(field: "id")): Person @lookup
6105+
}
6106+
6107+
type Person {
6108+
id: ID!
6109+
name: String
6110+
}
6111+
```
6112+
6113+
In this counter-example, the `@is` directive references a field (`unknownField`)
6114+
that does not exist on the return type (`Person`), causing an
6115+
`IS_INVALID_FIELDS` error.
6116+
6117+
```graphql counter-example
6118+
# Schema A
6119+
type Query {
6120+
personById(id: ID! @is(field: "unknownField")): Person @lookup
6121+
}
6122+
6123+
type Person {
6124+
id: ID!
6125+
name: String
6126+
}
6127+
```
6128+
58666129
### Validate Provides Directives
58676130

58686131
#### Provides Invalid Fields

0 commit comments

Comments
 (0)