From f410b680b1c9b1421e7a46cad1bc9467fc8ba365 Mon Sep 17 00:00:00 2001 From: John Okoroafor Date: Fri, 6 Feb 2026 19:40:04 +0100 Subject: [PATCH 1/2] Make deprecated introspection optional for legacy servers --- CHANGELOG.md | 4 +++ src/Type/Introspection.php | 35 ++++++++++++++++++--------- src/Utils/BuildClientSchema.php | 4 +-- tests/Type/IntrospectionTest.php | 23 ++++++++++++++++++ tests/Utils/BuildClientSchemaTest.php | 33 +++++++++++++++++++++++++ 5 files changed, 85 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2509467e7..5f078240f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,10 @@ You can find and compare releases at the [GitHub release page](https://github.co ## Unreleased +### Changed + +- Allow omitting deprecated introspection arguments/fields for legacy servers + ## v15.30.2 ### Fixed diff --git a/src/Type/Introspection.php b/src/Type/Introspection.php index 806658611..ac9129f19 100644 --- a/src/Type/Introspection.php +++ b/src/Type/Introspection.php @@ -30,6 +30,7 @@ * @phpstan-type IntrospectionOptions array{ * descriptions?: bool, * directiveIsRepeatable?: bool, + * includeDeprecated?: bool, * schemaDescription?: bool, * typeIsOneOf?: bool, * } @@ -41,10 +42,15 @@ * - directiveIsRepeatable * Include field `isRepeatable` for directives? * Default: false + * - includeDeprecated + * Include deprecated fields/args/enum values/input fields and related indicators + * (isDeprecated/deprecationReason) in the introspection query + * Default: true + * + * @see https://graphql-ruby.org/api-doc/1.12.1/GraphQL/Introspection.html * - typeIsOneOf * Include field `isOneOf` for types? * Default: false - * * @see \GraphQL\Tests\Type\IntrospectionTest */ class Introspection @@ -86,6 +92,7 @@ public static function getIntrospectionQuery(array $options = []): string $optionsWithDefaults = array_merge([ 'descriptions' => true, 'directiveIsRepeatable' => false, + 'includeDeprecated' => true, 'schemaDescription' => false, 'typeIsOneOf' => false, ], $options); @@ -102,6 +109,13 @@ public static function getIntrospectionQuery(array $options = []): string $typeIsOneOf = $optionsWithDefaults['typeIsOneOf'] ? 'isOneOf' : ''; + $includeDeprecated = $optionsWithDefaults['includeDeprecated']; + $includeDeprecatedArg = $includeDeprecated + ? '(includeDeprecated: true)' + : ''; + $deprecationIndicators = $includeDeprecated + ? " isDeprecated\n deprecationReason" + : ''; return << $value['description'], - 'deprecationReason' => $value['deprecationReason'], + 'deprecationReason' => $value['deprecationReason'] ?? null, ]; } @@ -479,7 +479,7 @@ private function buildFieldDefMap(array $typeIntrospection): array $map[$field['name']] = [ 'description' => $field['description'], - 'deprecationReason' => $field['deprecationReason'], + 'deprecationReason' => $field['deprecationReason'] ?? null, 'type' => $this->getOutputType($field['type']), 'args' => $this->buildInputValueDefMap($field['args']), ]; diff --git a/tests/Type/IntrospectionTest.php b/tests/Type/IntrospectionTest.php index c74846998..dfee23992 100644 --- a/tests/Type/IntrospectionTest.php +++ b/tests/Type/IntrospectionTest.php @@ -2020,4 +2020,27 @@ public function testIncludeDescriptionFieldOnSchema(): void preg_match_all('/\bdescription\b/', Introspection::getIntrospectionQuery(['descriptions' => false, 'schemaDescription' => true]), $matches); self::assertCount(0, $matches[0]); } + + /** @see it('excludes deprecated fields and indicators when disabled') */ + public function testExcludeDeprecatedFieldsAndIndicatorsWhenDisabled(): void + { + $source = Introspection::getIntrospectionQuery(['includeDeprecated' => false]); + + self::assertStringNotContainsString('includeDeprecated: true', $source); + self::assertStringNotContainsString('includeDeprecated: false', $source); + self::assertStringContainsString('args {', $source); + self::assertStringContainsString('fields {', $source); + self::assertStringContainsString('inputFields {', $source); + self::assertStringContainsString('enumValues {', $source); + self::assertStringNotContainsString('isDeprecated', $source); + self::assertStringNotContainsString('deprecationReason', $source); + } + + /** @see it('keeps deprecated args enabled by default') */ + public function testIncludeDeprecatedArgumentsByDefault(): void + { + $source = Introspection::getIntrospectionQuery(); + + self::assertStringContainsString('(includeDeprecated: true)', $source); + } } diff --git a/tests/Utils/BuildClientSchemaTest.php b/tests/Utils/BuildClientSchemaTest.php index 278be5d45..74566012e 100644 --- a/tests/Utils/BuildClientSchemaTest.php +++ b/tests/Utils/BuildClientSchemaTest.php @@ -166,6 +166,39 @@ public function testUsesBuiltInScalarsWhenPossible(): void ); } + /** @see it('builds a schema from introspection without deprecated fields') */ + public function testBuildsSchemaFromIntrospectionWithoutDeprecatedFields(): void + { + $sdl = ' + type Query { + active: String + legacy: String @deprecated(reason: "Use active") + } + + enum Status { + OK + OLD @deprecated(reason: "Use OK") + } + '; + + $schema = BuildSchema::build($sdl); + $introspection = Introspection::fromSchema($schema, [ + 'includeDeprecated' => false, + ]); + + $clientSchema = BuildClientSchema::build($introspection); + + $queryType = $clientSchema->getQueryType(); + self::assertNotNull($queryType); + self::assertArrayHasKey('active', $queryType->getFields()); + self::assertArrayNotHasKey('legacy', $queryType->getFields()); + + $statusEnum = $clientSchema->getType('Status'); + self::assertInstanceOf(EnumType::class, $statusEnum); + self::assertNotNull($statusEnum->getValue('OK')); + self::assertNull($statusEnum->getValue('OLD')); + } + /** it('includes standard types only if they are used', () => {. */ public function testIncludesStandardTypesOnlyIfTheyAreUsed(): void { From d38e2196507d1c68ddd04538531be842dd1d7815 Mon Sep 17 00:00:00 2001 From: John Okoroafor Date: Sat, 7 Feb 2026 01:43:12 +0100 Subject: [PATCH 2/2] PR feedback --- CHANGELOG.md | 2 +- src/Type/Introspection.php | 1 + tests/Type/IntrospectionTest.php | 1 - tests/Utils/BuildClientSchemaTest.php | 1 - 4 files changed, 2 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f078240f..732202840 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,7 @@ You can find and compare releases at the [GitHub release page](https://github.co ### Changed -- Allow omitting deprecated introspection arguments/fields for legacy servers +- Allow omitting deprecated introspection arguments/fields for legacy servers https://github.com/webonyx/graphql-php/pull/1849 ## v15.30.2 diff --git a/src/Type/Introspection.php b/src/Type/Introspection.php index ac9129f19..8591a6732 100644 --- a/src/Type/Introspection.php +++ b/src/Type/Introspection.php @@ -51,6 +51,7 @@ * - typeIsOneOf * Include field `isOneOf` for types? * Default: false + * * @see \GraphQL\Tests\Type\IntrospectionTest */ class Introspection diff --git a/tests/Type/IntrospectionTest.php b/tests/Type/IntrospectionTest.php index dfee23992..bd43be7b0 100644 --- a/tests/Type/IntrospectionTest.php +++ b/tests/Type/IntrospectionTest.php @@ -2021,7 +2021,6 @@ public function testIncludeDescriptionFieldOnSchema(): void self::assertCount(0, $matches[0]); } - /** @see it('excludes deprecated fields and indicators when disabled') */ public function testExcludeDeprecatedFieldsAndIndicatorsWhenDisabled(): void { $source = Introspection::getIntrospectionQuery(['includeDeprecated' => false]); diff --git a/tests/Utils/BuildClientSchemaTest.php b/tests/Utils/BuildClientSchemaTest.php index 74566012e..d00995cbb 100644 --- a/tests/Utils/BuildClientSchemaTest.php +++ b/tests/Utils/BuildClientSchemaTest.php @@ -166,7 +166,6 @@ public function testUsesBuiltInScalarsWhenPossible(): void ); } - /** @see it('builds a schema from introspection without deprecated fields') */ public function testBuildsSchemaFromIntrospectionWithoutDeprecatedFields(): void { $sdl = '