directiveLocator = $directiveLocator; } public function handle(ValidateSchema $validateSchema): void { $schema = $validateSchema->schema; foreach ($schema->getTypeMap() as $type) { if ($type instanceof ObjectType) { $this->validateObjectType($type); } } } protected function validateObjectType(ObjectType $type): void { $ast = $type->astNode; if ($ast !== null) { $directives = $this->directiveLocator->associated($ast); /** @var \Nuwave\Lighthouse\Support\Contracts\Directive $directive */ foreach ($directives as $directive) { if ($directive instanceof KeyDirective) { $this->validateKeySelectionSet($directive->fields(), $type); } } } } /** * @throws \Nuwave\Lighthouse\Exceptions\FederationException */ protected function validateKeySelectionSet(SelectionSetNode $selectionSet, ObjectType $type): void { foreach ($selectionSet->selections as $selection) { if (! $selection instanceof FieldNode) { throw new FederationException("Must only use field selections in the `fields` argument of @key, got: {$selection->kind}."); } try { // Throws if the field is not defined $field = $type->getField($selection->name->value); } catch (InvariantViolation $i) { throw new FederationException($i->getMessage(), $i->getCode(), $i); } if ( ASTHelper::hasDirective($type->astNode, ExtendsDirective::NAME) && ! ASTHelper::hasDirective($field->astNode, ExternalDirective::NAME) ) { throw new FederationException("A @key directive on `{$type->name}` specifies the `{$field->name}` field which has no @external directive."); } $nestedSelection = $selection->selectionSet; if ($nestedSelection !== null) { $type = $field->getType(); if ($type instanceof WrappingType) { $type = $type->getWrappedType(true); } $this->validateKeySelectionSet($nestedSelection, $type); } } } }