rules

package
v2.5.19 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 7, 2024 License: MIT Imports: 9 Imported by: 17

Documentation

Index

Constants

This section is empty.

Variables

View Source
var FieldsOnCorrectTypeRule = Rule{
	Name: "FieldsOnCorrectType",
	RuleFunc: func(observers *Events, addError AddErrFunc) {
		ruleFuncFieldsOnCorrectType(observers, addError, false)
	},
}
View Source
var FieldsOnCorrectTypeRuleWithoutSuggestions = Rule{
	Name: "FieldsOnCorrectTypeWithoutSuggestions",
	RuleFunc: func(observers *Events, addError AddErrFunc) {
		ruleFuncFieldsOnCorrectType(observers, addError, true)
	},
}
View Source
var FragmentsOnCompositeTypesRule = Rule{
	Name: "FragmentsOnCompositeTypes",
	RuleFunc: func(observers *Events, addError AddErrFunc) {
		observers.OnInlineFragment(func(walker *Walker, inlineFragment *ast.InlineFragment) {
			fragmentType := walker.Schema.Types[inlineFragment.TypeCondition]
			if fragmentType == nil || fragmentType.IsCompositeType() {
				return
			}

			message := fmt.Sprintf(`Fragment cannot condition on non composite type "%s".`, inlineFragment.TypeCondition)

			addError(
				Message("%s", message),
				At(inlineFragment.Position),
			)
		})

		observers.OnFragment(func(walker *Walker, fragment *ast.FragmentDefinition) {
			if fragment.Definition == nil || fragment.TypeCondition == "" || fragment.Definition.IsCompositeType() {
				return
			}

			message := fmt.Sprintf(`Fragment "%s" cannot condition on non composite type "%s".`, fragment.Name, fragment.TypeCondition)

			addError(
				Message("%s", message),
				At(fragment.Position),
			)
		})
	},
}
View Source
var KnownArgumentNamesRule = Rule{
	Name: "KnownArgumentNames",
	RuleFunc: func(observers *Events, addError AddErrFunc) {
		ruleFuncKnownArgumentNames(observers, addError, false)
	},
}
View Source
var KnownArgumentNamesRuleWithoutSuggestions = Rule{
	Name: "KnownArgumentNamesWithoutSuggestions",
	RuleFunc: func(observers *Events, addError AddErrFunc) {
		ruleFuncKnownArgumentNames(observers, addError, true)
	},
}
View Source
var KnownDirectivesRule = Rule{
	Name: "KnownDirectives",
	RuleFunc: func(observers *Events, addError AddErrFunc) {
		type mayNotBeUsedDirective struct {
			Name   string
			Line   int
			Column int
		}
		seen := map[mayNotBeUsedDirective]bool{}
		observers.OnDirective(func(walker *Walker, directive *ast.Directive) {
			if directive.Definition == nil {
				addError(
					Message(`Unknown directive "@%s".`, directive.Name),
					At(directive.Position),
				)
				return
			}

			for _, loc := range directive.Definition.Locations {
				if loc == directive.Location {
					return
				}
			}

			tmp := mayNotBeUsedDirective{
				Name:   directive.Name,
				Line:   directive.Position.Line,
				Column: directive.Position.Column,
			}

			if !seen[tmp] {
				addError(
					Message(`Directive "@%s" may not be used on %s.`, directive.Name, directive.Location),
					At(directive.Position),
				)
				seen[tmp] = true
			}
		})
	},
}
View Source
var KnownFragmentNamesRule = Rule{
	Name: "KnownFragmentNames",
	RuleFunc: func(observers *Events, addError AddErrFunc) {
		observers.OnFragmentSpread(func(walker *Walker, fragmentSpread *ast.FragmentSpread) {
			if fragmentSpread.Definition == nil {
				addError(
					Message(`Unknown fragment "%s".`, fragmentSpread.Name),
					At(fragmentSpread.Position),
				)
			}
		})
	},
}
View Source
var KnownRootTypeRule = Rule{
	Name: "KnownRootType",
	RuleFunc: func(observers *Events, addError AddErrFunc) {

		observers.OnOperation(func(walker *Walker, operation *ast.OperationDefinition) {
			var def *ast.Definition
			switch operation.Operation {
			case ast.Query, "":
				def = walker.Schema.Query
			case ast.Mutation:
				def = walker.Schema.Mutation
			case ast.Subscription:
				def = walker.Schema.Subscription
			default:

				panic(fmt.Sprintf(`got unknown operation type "%s"`, operation.Operation))
			}
			if def == nil {
				addError(
					Message(`Schema does not support operation type "%s"`, operation.Operation),
					At(operation.Position))
			}
		})
	},
}
View Source
var KnownTypeNamesRule = Rule{
	Name: "KnownTypeNames",
	RuleFunc: func(observers *Events, addError AddErrFunc) {
		ruleFuncKnownTypeNames(observers, addError, false)
	},
}
View Source
var KnownTypeNamesRuleWithoutSuggestions = Rule{
	Name: "KnownTypeNamesWithoutSuggestions",
	RuleFunc: func(observers *Events, addError AddErrFunc) {
		ruleFuncKnownTypeNames(observers, addError, true)
	},
}
View Source
var LoneAnonymousOperationRule = Rule{
	Name: "LoneAnonymousOperation",
	RuleFunc: func(observers *Events, addError AddErrFunc) {
		observers.OnOperation(func(walker *Walker, operation *ast.OperationDefinition) {
			if operation.Name == "" && len(walker.Document.Operations) > 1 {
				addError(
					Message(`This anonymous operation must be the only defined operation.`),
					At(operation.Position),
				)
			}
		})
	},
}
View Source
var NoFragmentCyclesRule = Rule{
	Name: "NoFragmentCycles",
	RuleFunc: func(observers *Events, addError AddErrFunc) {
		visitedFrags := make(map[string]bool)

		observers.OnFragment(func(walker *Walker, fragment *ast.FragmentDefinition) {
			var spreadPath []*ast.FragmentSpread
			spreadPathIndexByName := make(map[string]int)

			var recursive func(fragment *ast.FragmentDefinition)
			recursive = func(fragment *ast.FragmentDefinition) {
				if visitedFrags[fragment.Name] {
					return
				}

				visitedFrags[fragment.Name] = true

				spreadNodes := getFragmentSpreads(fragment.SelectionSet)
				if len(spreadNodes) == 0 {
					return
				}
				spreadPathIndexByName[fragment.Name] = len(spreadPath)

				for _, spreadNode := range spreadNodes {
					spreadName := spreadNode.Name

					cycleIndex, ok := spreadPathIndexByName[spreadName]

					spreadPath = append(spreadPath, spreadNode)
					if !ok {
						spreadFragment := walker.Document.Fragments.ForName(spreadName)
						if spreadFragment != nil {
							recursive(spreadFragment)
						}
					} else {
						cyclePath := spreadPath[cycleIndex : len(spreadPath)-1]
						var fragmentNames []string
						for _, fs := range cyclePath {
							fragmentNames = append(fragmentNames, fmt.Sprintf(`"%s"`, fs.Name))
						}
						var via string
						if len(fragmentNames) != 0 {
							via = fmt.Sprintf(" via %s", strings.Join(fragmentNames, ", "))
						}
						addError(
							Message(`Cannot spread fragment "%s" within itself%s.`, spreadName, via),
							At(spreadNode.Position),
						)
					}

					spreadPath = spreadPath[:len(spreadPath)-1]
				}

				delete(spreadPathIndexByName, fragment.Name)
			}

			recursive(fragment)
		})
	},
}
View Source
var NoUndefinedVariablesRule = Rule{
	Name: "NoUndefinedVariables",
	RuleFunc: func(observers *Events, addError AddErrFunc) {
		observers.OnValue(func(walker *Walker, value *ast.Value) {
			if walker.CurrentOperation == nil || value.Kind != ast.Variable || value.VariableDefinition != nil {
				return
			}

			if walker.CurrentOperation.Name != "" {
				addError(
					Message(`Variable "%s" is not defined by operation "%s".`, value, walker.CurrentOperation.Name),
					At(value.Position),
				)
			} else {
				addError(
					Message(`Variable "%s" is not defined.`, value),
					At(value.Position),
				)
			}
		})
	},
}
View Source
var NoUnusedFragmentsRule = Rule{
	Name: "NoUnusedFragments",
	RuleFunc: func(observers *Events, addError AddErrFunc) {
		inFragmentDefinition := false
		fragmentNameUsed := make(map[string]bool)

		observers.OnFragmentSpread(func(walker *Walker, fragmentSpread *ast.FragmentSpread) {
			if !inFragmentDefinition {
				fragmentNameUsed[fragmentSpread.Name] = true
			}
		})

		observers.OnFragment(func(walker *Walker, fragment *ast.FragmentDefinition) {
			inFragmentDefinition = true
			if !fragmentNameUsed[fragment.Name] {
				addError(
					Message(`Fragment "%s" is never used.`, fragment.Name),
					At(fragment.Position),
				)
			}
		})
	},
}
View Source
var NoUnusedVariablesRule = Rule{
	Name: "NoUnusedVariables",
	RuleFunc: func(observers *Events, addError AddErrFunc) {
		observers.OnOperation(func(walker *Walker, operation *ast.OperationDefinition) {
			for _, varDef := range operation.VariableDefinitions {
				if varDef.Used {
					continue
				}

				if operation.Name != "" {
					addError(
						Message(`Variable "$%s" is never used in operation "%s".`, varDef.Variable, operation.Name),
						At(varDef.Position),
					)
				} else {
					addError(
						Message(`Variable "$%s" is never used.`, varDef.Variable),
						At(varDef.Position),
					)
				}
			}
		})
	},
}
View Source
var OverlappingFieldsCanBeMergedRule = Rule{
	Name: "OverlappingFieldsCanBeMerged",
	RuleFunc: func(observers *Events, addError AddErrFunc) {

		m := &overlappingFieldsCanBeMergedManager{
			comparedFragmentPairs: pairSet{data: make(map[string]map[string]bool)},
		}

		observers.OnOperation(func(walker *Walker, operation *ast.OperationDefinition) {
			m.walker = walker
			conflicts := m.findConflictsWithinSelectionSet(operation.SelectionSet)
			for _, conflict := range conflicts {
				conflict.addFieldsConflictMessage(addError)
			}
		})
		observers.OnField(func(walker *Walker, field *ast.Field) {
			if walker.CurrentOperation == nil {

				return
			}
			m.walker = walker
			conflicts := m.findConflictsWithinSelectionSet(field.SelectionSet)
			for _, conflict := range conflicts {
				conflict.addFieldsConflictMessage(addError)
			}
		})
		observers.OnInlineFragment(func(walker *Walker, inlineFragment *ast.InlineFragment) {
			m.walker = walker
			conflicts := m.findConflictsWithinSelectionSet(inlineFragment.SelectionSet)
			for _, conflict := range conflicts {
				conflict.addFieldsConflictMessage(addError)
			}
		})
		observers.OnFragment(func(walker *Walker, fragment *ast.FragmentDefinition) {
			m.walker = walker
			conflicts := m.findConflictsWithinSelectionSet(fragment.SelectionSet)
			for _, conflict := range conflicts {
				conflict.addFieldsConflictMessage(addError)
			}
		})
	},
}
View Source
var PossibleFragmentSpreadsRule = Rule{
	Name: "PossibleFragmentSpreads",
	RuleFunc: func(observers *Events, addError AddErrFunc) {
		validate := func(walker *Walker, parentDef *ast.Definition, fragmentName string, emitError func()) {
			if parentDef == nil {
				return
			}

			var parentDefs []*ast.Definition
			switch parentDef.Kind {
			case ast.Object:
				parentDefs = []*ast.Definition{parentDef}
			case ast.Interface, ast.Union:
				parentDefs = walker.Schema.GetPossibleTypes(parentDef)
			default:
				return
			}

			fragmentDefType := walker.Schema.Types[fragmentName]
			if fragmentDefType == nil {
				return
			}
			if !fragmentDefType.IsCompositeType() {

				return
			}
			fragmentDefs := walker.Schema.GetPossibleTypes(fragmentDefType)

			for _, fragmentDef := range fragmentDefs {
				for _, parentDef := range parentDefs {
					if parentDef.Name == fragmentDef.Name {
						return
					}
				}
			}

			emitError()
		}

		observers.OnInlineFragment(func(walker *Walker, inlineFragment *ast.InlineFragment) {
			validate(walker, inlineFragment.ObjectDefinition, inlineFragment.TypeCondition, func() {
				addError(
					Message(`Fragment cannot be spread here as objects of type "%s" can never be of type "%s".`, inlineFragment.ObjectDefinition.Name, inlineFragment.TypeCondition),
					At(inlineFragment.Position),
				)
			})
		})

		observers.OnFragmentSpread(func(walker *Walker, fragmentSpread *ast.FragmentSpread) {
			if fragmentSpread.Definition == nil {
				return
			}
			validate(walker, fragmentSpread.ObjectDefinition, fragmentSpread.Definition.TypeCondition, func() {
				addError(
					Message(`Fragment "%s" cannot be spread here as objects of type "%s" can never be of type "%s".`, fragmentSpread.Name, fragmentSpread.ObjectDefinition.Name, fragmentSpread.Definition.TypeCondition),
					At(fragmentSpread.Position),
				)
			})
		})
	},
}
View Source
var ProvidedRequiredArgumentsRule = Rule{
	Name: "ProvidedRequiredArguments",
	RuleFunc: func(observers *Events, addError AddErrFunc) {
		observers.OnField(func(walker *Walker, field *ast.Field) {
			if field.Definition == nil {
				return
			}

		argDef:
			for _, argDef := range field.Definition.Arguments {
				if !argDef.Type.NonNull {
					continue
				}
				if argDef.DefaultValue != nil {
					continue
				}
				for _, arg := range field.Arguments {
					if arg.Name == argDef.Name {
						continue argDef
					}
				}

				addError(
					Message(`Field "%s" argument "%s" of type "%s" is required, but it was not provided.`, field.Name, argDef.Name, argDef.Type.String()),
					At(field.Position),
				)
			}
		})

		observers.OnDirective(func(walker *Walker, directive *ast.Directive) {
			if directive.Definition == nil {
				return
			}

		argDef:
			for _, argDef := range directive.Definition.Arguments {
				if !argDef.Type.NonNull {
					continue
				}
				if argDef.DefaultValue != nil {
					continue
				}
				for _, arg := range directive.Arguments {
					if arg.Name == argDef.Name {
						continue argDef
					}
				}

				addError(
					Message(`Directive "@%s" argument "%s" of type "%s" is required, but it was not provided.`, directive.Definition.Name, argDef.Name, argDef.Type.String()),
					At(directive.Position),
				)
			}
		})
	},
}
View Source
var ScalarLeafsRule = Rule{
	Name: "ScalarLeafs",
	RuleFunc: func(observers *Events, addError AddErrFunc) {
		observers.OnField(func(walker *Walker, field *ast.Field) {
			if field.Definition == nil {
				return
			}

			fieldType := walker.Schema.Types[field.Definition.Type.Name()]
			if fieldType == nil {
				return
			}

			if fieldType.IsLeafType() && len(field.SelectionSet) > 0 {
				addError(
					Message(`Field "%s" must not have a selection since type "%s" has no subfields.`, field.Name, fieldType.Name),
					At(field.Position),
				)
			}

			if !fieldType.IsLeafType() && len(field.SelectionSet) == 0 {
				addError(
					Message(`Field "%s" of type "%s" must have a selection of subfields.`, field.Name, field.Definition.Type.String()),
					Suggestf(`"%s { ... }"`, field.Name),
					At(field.Position),
				)
			}
		})
	},
}
View Source
var SingleFieldSubscriptionsRule = Rule{
	Name: "SingleFieldSubscriptions",
	RuleFunc: func(observers *Events, addError AddErrFunc) {
		observers.OnOperation(func(walker *Walker, operation *ast.OperationDefinition) {
			if walker.Schema.Subscription == nil || operation.Operation != ast.Subscription {
				return
			}

			fields := retrieveTopFieldNames(operation.SelectionSet)

			name := "Anonymous Subscription"
			if operation.Name != "" {
				name = `Subscription ` + strconv.Quote(operation.Name)
			}

			if len(fields) > 1 {
				addError(
					Message(`%s must select only one top level field.`, name),
					At(fields[1].position),
				)
			}

			for _, field := range fields {
				if strings.HasPrefix(field.name, "__") {
					addError(
						Message(`%s must not select an introspection top level field.`, name),
						At(field.position),
					)
				}
			}
		})
	},
}
View Source
var UniqueArgumentNamesRule = Rule{
	Name: "UniqueArgumentNames",
	RuleFunc: func(observers *Events, addError AddErrFunc) {
		observers.OnField(func(walker *Walker, field *ast.Field) {
			checkUniqueArgs(field.Arguments, addError)
		})

		observers.OnDirective(func(walker *Walker, directive *ast.Directive) {
			checkUniqueArgs(directive.Arguments, addError)
		})
	},
}
View Source
var UniqueDirectivesPerLocationRule = Rule{
	Name: "UniqueDirectivesPerLocation",
	RuleFunc: func(observers *Events, addError AddErrFunc) {
		observers.OnDirectiveList(func(walker *Walker, directives []*ast.Directive) {
			seen := map[string]bool{}

			for _, dir := range directives {
				if dir.Name != "repeatable" && seen[dir.Name] {
					addError(
						Message(`The directive "@%s" can only be used once at this location.`, dir.Name),
						At(dir.Position),
					)
				}
				seen[dir.Name] = true
			}
		})
	},
}
View Source
var UniqueFragmentNamesRule = Rule{
	Name: "UniqueFragmentNames",
	RuleFunc: func(observers *Events, addError AddErrFunc) {
		seenFragments := map[string]bool{}

		observers.OnFragment(func(walker *Walker, fragment *ast.FragmentDefinition) {
			if seenFragments[fragment.Name] {
				addError(
					Message(`There can be only one fragment named "%s".`, fragment.Name),
					At(fragment.Position),
				)
			}
			seenFragments[fragment.Name] = true
		})
	},
}
View Source
var UniqueInputFieldNamesRule = Rule{
	Name: "UniqueInputFieldNames",
	RuleFunc: func(observers *Events, addError AddErrFunc) {
		observers.OnValue(func(walker *Walker, value *ast.Value) {
			if value.Kind != ast.ObjectValue {
				return
			}

			seen := map[string]bool{}
			for _, field := range value.Children {
				if seen[field.Name] {
					addError(
						Message(`There can be only one input field named "%s".`, field.Name),
						At(field.Position),
					)
				}
				seen[field.Name] = true
			}
		})
	},
}
View Source
var UniqueOperationNamesRule = Rule{
	Name: "UniqueOperationNames",
	RuleFunc: func(observers *Events, addError AddErrFunc) {
		seen := map[string]bool{}

		observers.OnOperation(func(walker *Walker, operation *ast.OperationDefinition) {
			if seen[operation.Name] {
				addError(
					Message(`There can be only one operation named "%s".`, operation.Name),
					At(operation.Position),
				)
			}
			seen[operation.Name] = true
		})
	},
}
View Source
var UniqueVariableNamesRule = Rule{
	Name: "UniqueVariableNames",
	RuleFunc: func(observers *Events, addError AddErrFunc) {
		observers.OnOperation(func(walker *Walker, operation *ast.OperationDefinition) {
			seen := map[string]int{}
			for _, def := range operation.VariableDefinitions {

				if seen[def.Variable] == 1 {
					addError(
						Message(`There can be only one variable named "$%s".`, def.Variable),
						At(def.Position),
					)
				}
				seen[def.Variable]++
			}
		})
	},
}
View Source
var ValuesOfCorrectTypeRule = Rule{
	Name: "ValuesOfCorrectType",
	RuleFunc: func(observers *Events, addError AddErrFunc) {
		ruleFuncValuesOfCorrectType(observers, addError, false)
	},
}
View Source
var ValuesOfCorrectTypeRuleWithoutSuggestions = Rule{
	Name: "ValuesOfCorrectTypeWithoutSuggestions",
	RuleFunc: func(observers *Events, addError AddErrFunc) {
		ruleFuncValuesOfCorrectType(observers, addError, true)
	},
}
View Source
var VariablesAreInputTypesRule = Rule{
	Name: "VariablesAreInputTypes",
	RuleFunc: func(observers *Events, addError AddErrFunc) {
		observers.OnOperation(func(walker *Walker, operation *ast.OperationDefinition) {
			for _, def := range operation.VariableDefinitions {
				if def.Definition == nil {
					continue
				}
				if !def.Definition.IsInputType() {
					addError(
						Message(
							`Variable "$%s" cannot be non-input type "%s".`,
							def.Variable,
							def.Type.String(),
						),
						At(def.Position),
					)
				}
			}
		})
	},
}
View Source
var VariablesInAllowedPositionRule = Rule{
	Name: "VariablesInAllowedPosition",
	RuleFunc: func(observers *Events, addError AddErrFunc) {
		observers.OnValue(func(walker *Walker, value *ast.Value) {
			if value.Kind != ast.Variable || value.ExpectedType == nil || value.VariableDefinition == nil || walker.CurrentOperation == nil {
				return
			}

			tmp := *value.ExpectedType

			if value.VariableDefinition.DefaultValue != nil && value.VariableDefinition.DefaultValue.Kind != ast.NullValue {
				if value.ExpectedType.NonNull {
					tmp.NonNull = false
				}
			}

			if !value.VariableDefinition.Type.IsCompatible(&tmp) {
				addError(
					Message(
						`Variable "%s" of type "%s" used in position expecting type "%s".`,
						value,
						value.VariableDefinition.Type.String(),
						value.ExpectedType.String(),
					),
					At(value.Position),
				)
			}
		})
	},
}

Functions

This section is empty.

Types

type ConflictMessage

type ConflictMessage struct {
	Message      string
	ResponseName string
	Names        []string
	SubMessage   []*ConflictMessage
	Position     *ast.Position
}

func (*ConflictMessage) String

func (m *ConflictMessage) String(buf *bytes.Buffer)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL