diff --git a/spec/Section 4 -- Composition.md b/spec/Section 4 -- Composition.md index 1a9bca9..09e5e4b 100644 --- a/spec/Section 4 -- Composition.md +++ b/spec/Section 4 -- Composition.md @@ -14,6 +14,114 @@ run in sequence to produce the composite execution schema. ### Pre Merge Validation +#### Enum Type Default Value Uses Inaccessible Value + +**Error Code** + +`ENUM_TYPE_DEFAULT_VALUE_INACCESSIBLE` + +**Formal Specification** + +- {ValidateArgumentDefaultValues()} must be true. +- {ValidateInputFieldDefaultValues()} must be true. + +ValidateArgumentDefaultValues(): + +- Let {arguments} be all arguments of fields and directives across all source + schemas +- For each {argument} in {arguments} + - If {IsExposed(argument)} is true and has a default value: + - Let {defaultValue} be the default value of {argument} + - If not {ValidateDefaultValue(defaultValue)} + - return false +- return true + +ValidateInputFieldDefaultValues(): + +- Let {inputFields} be all input fields across all source schemas +- For each {inputField} in {inputFields}: + - Let {type} be the type of {inputField} + - If {IsExposed(inputField)} is true and {inputField} has a default value: + - Let {defaultValue} be the default value of {inputField} + - If {ValidateDefaultValue(defaultValue)} is false + - return false +- return true + +ValidateDefaultValue(defaultValue): + +- If {defaultValue} is a ListValue: + - For each {valueNode} in {defaultValue}: + - If {ValidateDefaultValue(valueNode)} is false + - return false +- If {defaultValue} is an ObjectValue: + - Let {objectFields} be a list of all fields of {defaultValue} + - Let {fields} be a list of all fields {objectFields} are referring to + - For each {field} in {fields}: + - If {IsExposed(field)} is false + - return false + - For each {objectField} in {objectFields}: + - Let {value} be the value of {objectField} + - return {ValidateDefaultValue(value)} +- If {defaultValue} is an EnumValue: + - If {IsExposed(defaultValue)} is false + - return false +- return true + +**Explanatory Text** + +This rule ensures that inaccessible enum values are not exposed in the composed +schema through default values. Output field arguments, input fields, and +directive arguments must only use enum values as their default value when not +annotated with the `@inaccessible` directive. + +In this example the `FOO` value in the `Enum1` enum is not marked with +`@inaccessible`, hence it does not violate the rule. + +```graphql +type Query { + field(type: Enum1 = FOO): [Baz!]! +} + +enum Enum1 { + FOO + BAR +} +``` + +The following example violates this rule because the default value for the field +`field` in type `Input1` references an enum value (`FOO`) that is marked as +`@inaccessible`. + +```graphql counter-example +type Query { + field(arg: Enum1 = FOO): [Baz!]! +} + +input Input1 { + field: Enum1 = FOO +} + +directive @directive1(arg: Enum1 = FOO) on FIELD_DEFINITION + +enum Enum1 { + FOO @inaccessible + BAR +} +``` + +```graphql counter-example +type Query { + field(arg: Input1 = { field2: "ERROR" }): [Baz!]! +} + +directive @directive1(arg: Input1 = { field2: "ERROR" }) on FIELD_DEFINITION + +input Input1 { + field1: String + field2: String @inaccessible +} +``` + #### Output Field Types Mergeable **Error Code**