diff --git a/CHANGELOG.md b/CHANGELOG.md index 2509467e7..732202840 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 https://github.com/webonyx/graphql-php/pull/1849 + ## v15.30.2 ### Fixed diff --git a/src/Type/Introspection.php b/src/Type/Introspection.php index 806658611..8591a6732 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,6 +42,12 @@ * - 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 @@ -86,6 +93,7 @@ public static function getIntrospectionQuery(array $options = []): string $optionsWithDefaults = array_merge([ 'descriptions' => true, 'directiveIsRepeatable' => false, + 'includeDeprecated' => true, 'schemaDescription' => false, 'typeIsOneOf' => false, ], $options); @@ -102,6 +110,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..bd43be7b0 100644 --- a/tests/Type/IntrospectionTest.php +++ b/tests/Type/IntrospectionTest.php @@ -2020,4 +2020,26 @@ public function testIncludeDescriptionFieldOnSchema(): void preg_match_all('/\bdescription\b/', Introspection::getIntrospectionQuery(['descriptions' => false, 'schemaDescription' => true]), $matches); self::assertCount(0, $matches[0]); } + + 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..d00995cbb 100644 --- a/tests/Utils/BuildClientSchemaTest.php +++ b/tests/Utils/BuildClientSchemaTest.php @@ -166,6 +166,38 @@ public function testUsesBuiltInScalarsWhenPossible(): void ); } + 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 {