Skip to content
Merged
Show file tree
Hide file tree
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ You can find and compare releases at the [GitHub release page](https://github.co

## Unreleased

### Fixed

- Keep lazy type loading lazy on `webonyx/graphql-php >= 15.31.0` by passing scalar overrides explicitly when `SchemaConfig::setScalarOverrides` is available https://github.com/nuwave/lighthouse/pull/2772

## v6.69.0

### Added
Expand Down
8 changes: 8 additions & 0 deletions src/Schema/SchemaBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,14 @@ protected function build(DocumentAST $documentAST): Schema
fn (): array => $this->typeRegistry->possibleTypes(),
);

// Passing scalar overrides explicitly prevents the first lookup of a built-in scalar
// from discovering them by resolving the lazy types callable, which would eagerly
// build every type in the schema, see https://github.com/nuwave/lighthouse/issues/2771.
// TODO remove this check when the minimum version of webonyx/graphql-php includes the method
if (method_exists($config, 'setScalarOverrides')) {
$config->setScalarOverrides($this->typeRegistry->scalarOverrides());
}

// There is no way to resolve directives lazily, so we convert them eagerly
$directiveFactory = new DirectiveFactory(
new ExecutableTypeNodeConverter($this->typeRegistry),
Expand Down
26 changes: 26 additions & 0 deletions src/Schema/TypeRegistry.php
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,32 @@ public function possibleTypes(): array
return array_filter($this->types);
}

/**
* Built-in scalar types that are overridden in this schema.
*
* A scalar override is a type named after a built-in scalar such as `String`,
* defined in the schema or registered programmatically.
*
* @return array<int, \GraphQL\Type\Definition\ScalarType>
*/
public function scalarOverrides(): array
{
$overrides = [];
foreach (Type::BUILT_IN_SCALAR_NAMES as $name) {
if (
isset($this->types[$name])
|| isset($this->documentAST->types[$name])
|| isset($this->lazyTypes[$name])
) {
$override = $this->get($name);
assert($override instanceof ScalarType);
$overrides[] = $override;
}
}

return $overrides;
}
Comment on lines +242 to +266

/**
* Get the types that are currently resolved.
*
Expand Down
77 changes: 77 additions & 0 deletions tests/Unit/Schema/SchemaBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,13 @@
use GraphQL\Type\Definition\InputObjectType;
use GraphQL\Type\Definition\InterfaceType;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\ScalarType;
use GraphQL\Type\Definition\Type;
use Nuwave\Lighthouse\Schema\RootType;
use Nuwave\Lighthouse\Schema\SchemaBuilder;
use Nuwave\Lighthouse\Schema\TypeRegistry;
use Tests\TestCase;
use Tests\Utils\Scalars\Email;

final class SchemaBuilderTest extends TestCase
{
Expand Down Expand Up @@ -209,6 +213,79 @@ public function testExtendTypes(): void
$this->assertSame('yo?', $type->getField('bar')->description);
}

public function testRegistersExplicitlyEmptyScalarOverrides(): void
{
$schema = $this->buildSchemaWithPlaceholderQuery('');

$config = $schema->getConfig();
if (! method_exists($config, 'getScalarOverrides')) {
$this->markTestSkipped('Requires a version of webonyx/graphql-php that supports SchemaConfig::setScalarOverrides.');
}

$this->assertSame([], $config->getScalarOverrides());
}

public function testRegistersRedefinedBuiltInScalarsAsScalarOverrides(): void
{
$schema = $this->buildSchemaWithPlaceholderQuery(/** @lang GraphQL */ '
scalar String @scalar(class: "Email")
');

$config = $schema->getConfig();
if (! method_exists($config, 'getScalarOverrides')) {
$this->markTestSkipped('Requires a version of webonyx/graphql-php that supports SchemaConfig::setScalarOverrides.');
}

$scalarOverrides = $config->getScalarOverrides();
$this->assertNotNull($scalarOverrides);
$this->assertCount(1, $scalarOverrides);

$stringOverride = $scalarOverrides[Type::STRING];
$this->assertInstanceOf(Email::class, $stringOverride);
$this->assertSame(Type::STRING, $stringOverride->name);
}

public function testRegistersProgrammaticallyOverwrittenBuiltInScalarsAsScalarOverrides(): void
{
$typeRegistry = $this->app->make(TypeRegistry::class);
$typeRegistry->overwrite(new Email(['name' => Type::STRING]));

$schema = $this->buildSchemaWithPlaceholderQuery('');

$config = $schema->getConfig();
if (! method_exists($config, 'getScalarOverrides')) {
$this->markTestSkipped('Requires a version of webonyx/graphql-php that supports SchemaConfig::setScalarOverrides.');
}

$scalarOverrides = $config->getScalarOverrides();
$this->assertNotNull($scalarOverrides);
$this->assertCount(1, $scalarOverrides);

$stringOverride = $scalarOverrides[Type::STRING];
$this->assertInstanceOf(Email::class, $stringOverride);
$this->assertSame(Type::STRING, $stringOverride->name);
}

public function testBuiltInScalarLookupDoesNotResolveAllTypes(): void
{
$schema = $this->buildSchemaWithPlaceholderQuery(/** @lang GraphQL */ '
type Foo {
bar: Int
}
');

$config = $schema->getConfig();
if (! method_exists($config, 'getScalarOverrides')) {
$this->markTestSkipped('Requires a version of webonyx/graphql-php that supports SchemaConfig::setScalarOverrides.');
}

$booleanType = $schema->getType(Type::BOOLEAN);
$this->assertInstanceOf(ScalarType::class, $booleanType);

$typeRegistry = $this->app->make(TypeRegistry::class);
$this->assertArrayNotHasKey('Foo', $typeRegistry->resolvedTypes());
}

public function testResolvesEnumDefaultValuesToInternalValues(): void
{
$schema = $this->buildSchema(/** @lang GraphQL */ <<<'GRAPHQL'
Expand Down
Loading