From afc3ed31d7714185d3ddc5182e4f6ed2e426eb39 Mon Sep 17 00:00:00 2001 From: shmax Date: Fri, 18 Oct 2024 06:35:59 -0700 Subject: [PATCH 1/4] fix tests --- src/Type/Definition/PhpEnumType.php | 43 +++++++++------ tests/Type/EnumTypeTest.php | 4 +- .../{PhpEnum.php => MyCustomPhpEnum.php} | 2 +- tests/Type/PhpEnumTypeTest.php | 52 ++++++++++++------- 4 files changed, 64 insertions(+), 37 deletions(-) rename tests/Type/PhpEnumType/{PhpEnum.php => MyCustomPhpEnum.php} (93%) diff --git a/src/Type/Definition/PhpEnumType.php b/src/Type/Definition/PhpEnumType.php index 19e2d84d5..29b9945f2 100644 --- a/src/Type/Definition/PhpEnumType.php +++ b/src/Type/Definition/PhpEnumType.php @@ -4,28 +4,37 @@ use GraphQL\Error\SerializationError; use GraphQL\Utils\PhpDoc; +use GraphQL\Language\AST\EnumValueDefinitionNode; +use GraphQL\Language\AST\EnumTypeDefinitionNode; +use GraphQL\Language\AST\EnumTypeExtensionNode; use GraphQL\Utils\Utils; -/** @phpstan-import-type PartialEnumValueConfig from EnumType */ +/** + * @phpstan-import-type PartialEnumValueConfig from EnumTypev + * + * @phpstan-type PhpEnumTypeConfig array{ + * name?: string|null, + * description?: string|null, + * enumClass: class-string<\UnitEnum>, + * astNode?: EnumTypeDefinitionNode|null, + * extensionASTNodes?: array|null + */ class PhpEnumType extends EnumType { public const MULTIPLE_DESCRIPTIONS_DISALLOWED = 'Using more than 1 Description attribute is not supported.'; public const MULTIPLE_DEPRECATIONS_DISALLOWED = 'Using more than 1 Deprecated attribute is not supported.'; - /** @var class-string<\UnitEnum> */ - protected string $enumClass; /** - * @param class-string<\UnitEnum> $enum - * @param string|null $name The name the enum will have in the schema, defaults to the basename of the given class + * @phpstan-param PhpEnumTypeConfig $config * * @throws \Exception * @throws \ReflectionException */ - public function __construct(string $enum, ?string $name = null) + public function __construct(array $config) { - $this->enumClass = $enum; - $reflection = new \ReflectionEnum($enum); + $enumClass = $config['enumClass']; + $reflection = new \ReflectionEnum($enumClass); /** * @var array $enumDefinitions @@ -40,37 +49,39 @@ public function __construct(string $enum, ?string $name = null) } parent::__construct([ - 'name' => $name ?? $this->baseName($enum), + 'name' => $config['name'] ?? $this->baseName($enumClass), 'values' => $enumDefinitions, - 'description' => $this->extractDescription($reflection), + 'description' => $config['description'] ?? $this->extractDescription($reflection), + 'enumClass' => $enumClass ]); } public function serialize($value): string { - if ($value instanceof $this->enumClass) { + $enumClass = $this->config['enumClass']; + if ($value instanceof $enumClass) { return $value->name; } - if (is_a($this->enumClass, \BackedEnum::class, true)) { + if (! is_a($value, $enumClass)) { try { - $instance = $this->enumClass::from($value); + $instance = $enumClass::from($value); } catch (\ValueError|\TypeError $_) { $notEnumInstanceOrValue = Utils::printSafe($value); - throw new SerializationError("Cannot serialize value as enum: {$notEnumInstanceOrValue}, expected instance or valid value of {$this->enumClass}."); + throw new SerializationError("Cannot serialize value as enum: {$notEnumInstanceOrValue}, expected instance or valid value of {$enumClass}."); } return $instance->name; } $notEnum = Utils::printSafe($value); - throw new SerializationError("Cannot serialize value as enum: {$notEnum}, expected instance of {$this->enumClass}."); + throw new SerializationError("Cannot serialize value as enum: {$notEnum}, expected instance of {$enumClass}."); } public function parseValue($value) { // Can happen when variable values undergo a serialization cycle before execution - if ($value instanceof $this->enumClass) { + if ($value instanceof $this->config['enumClass']) { return $value; } diff --git a/tests/Type/EnumTypeTest.php b/tests/Type/EnumTypeTest.php index 597f917f8..ebd22838e 100644 --- a/tests/Type/EnumTypeTest.php +++ b/tests/Type/EnumTypeTest.php @@ -8,7 +8,7 @@ use GraphQL\Language\Parser; use GraphQL\Language\SourceLocation; use GraphQL\Tests\Type\PhpEnumType\BackedPhpEnum; -use GraphQL\Tests\Type\PhpEnumType\PhpEnum; +use GraphQL\Tests\Type\PhpEnumType\MyCustomPhpEnum; use GraphQL\Tests\Type\TestClasses\OtherEnumType; use GraphQL\Type\Definition\EnumType; use GraphQL\Type\Definition\EnumValueDefinition; @@ -715,7 +715,7 @@ enum PhpEnum { $this->schema = BuildSchema::build($documentNode); $resolvers = [ - 'phpEnum' => fn (): PhpEnum => PhpEnum::B, + 'phpEnum' => fn (): MyCustomPhpEnum => MyCustomPhpEnum::B, ]; self::assertSame( diff --git a/tests/Type/PhpEnumType/PhpEnum.php b/tests/Type/PhpEnumType/MyCustomPhpEnum.php similarity index 93% rename from tests/Type/PhpEnumType/PhpEnum.php rename to tests/Type/PhpEnumType/MyCustomPhpEnum.php index dd3f4b4e6..99eb178f9 100644 --- a/tests/Type/PhpEnumType/PhpEnum.php +++ b/tests/Type/PhpEnumType/MyCustomPhpEnum.php @@ -6,7 +6,7 @@ use GraphQL\Type\Definition\Description; #[Description(description: 'foo')] -enum PhpEnum +enum MyCustomPhpEnum { #[Description(description: 'bar')] case A; diff --git a/tests/Type/PhpEnumTypeTest.php b/tests/Type/PhpEnumTypeTest.php index c49aa5f93..f833fac46 100644 --- a/tests/Type/PhpEnumTypeTest.php +++ b/tests/Type/PhpEnumTypeTest.php @@ -6,12 +6,13 @@ use GraphQL\Error\SerializationError; use GraphQL\GraphQL; use GraphQL\Tests\TestCaseBase; +use GraphQL\Tests\Type\PhpEnumType\BackedPhpEnum; use GraphQL\Tests\Type\PhpEnumType\DocBlockPhpEnum; use GraphQL\Tests\Type\PhpEnumType\IntPhpEnum; use GraphQL\Tests\Type\PhpEnumType\MultipleDeprecationsPhpEnum; use GraphQL\Tests\Type\PhpEnumType\MultipleDescriptionsCasePhpEnum; use GraphQL\Tests\Type\PhpEnumType\MultipleDescriptionsPhpEnum; -use GraphQL\Tests\Type\PhpEnumType\PhpEnum; +use GraphQL\Tests\Type\PhpEnumType\MyCustomPhpEnum; use GraphQL\Type\Definition\ObjectType; use GraphQL\Type\Definition\PhpEnumType; use GraphQL\Type\Definition\ResolveInfo; @@ -32,10 +33,12 @@ protected function setUp(): void public function testConstructEnumTypeFromPhpEnum(): void { - $enumType = new PhpEnumType(PhpEnum::class); + $enumType = new PhpEnumType([ + "enumClass" => MyCustomPhpEnum::class + ]); self::assertSame(<<<'GRAPHQL' "foo" -enum PhpEnum { +enum MyCustomPhpEnum { "bar" A B @deprecated @@ -46,7 +49,7 @@ enum PhpEnum { public function testConstructEnumTypeFromIntPhpEnum(): void { - $enumType = new PhpEnumType(IntPhpEnum::class); + $enumType = new PhpEnumType(['enumClass' => IntPhpEnum::class]); self::assertSame(<<<'GRAPHQL' enum IntPhpEnum { A @@ -56,7 +59,10 @@ enum IntPhpEnum { public function testConstructEnumTypeFromPhpEnumWithCustomName(): void { - $enumType = new PhpEnumType(PhpEnum::class, 'CustomNamedPhpEnum'); + $enumType = new PhpEnumType([ + "enumClass"=>MyCustomPhpEnum::class, + "name"=>'CustomNamedPhpEnum' + ]); self::assertSame(<<<'GRAPHQL' "foo" enum CustomNamedPhpEnum { @@ -70,7 +76,7 @@ enum CustomNamedPhpEnum { public function testConstructEnumTypeFromPhpEnumWithDocBlockDescriptions(): void { - $enumType = new PhpEnumType(DocBlockPhpEnum::class); + $enumType = new PhpEnumType(['enumClass' => DocBlockPhpEnum::class]); self::assertSame(<<<'GRAPHQL' "foo" enum DocBlockPhpEnum { @@ -89,24 +95,32 @@ enum DocBlockPhpEnum { public function testMultipleDescriptionsDisallowed(): void { self::expectExceptionObject(new \Exception(PhpEnumType::MULTIPLE_DESCRIPTIONS_DISALLOWED)); - new PhpEnumType(MultipleDescriptionsPhpEnum::class); + new PhpEnumType([ + "enumClass"=>MultipleDescriptionsPhpEnum::class + ]); } public function testMultipleDescriptionsDisallowedOnCase(): void { self::expectExceptionObject(new \Exception(PhpEnumType::MULTIPLE_DESCRIPTIONS_DISALLOWED)); - new PhpEnumType(MultipleDescriptionsCasePhpEnum::class); + new PhpEnumType([ + "enumClass" => MultipleDescriptionsCasePhpEnum::class + ]); } public function testMultipleDeprecationsDisallowed(): void { self::expectExceptionObject(new \Exception(PhpEnumType::MULTIPLE_DEPRECATIONS_DISALLOWED)); - new PhpEnumType(MultipleDeprecationsPhpEnum::class); + new PhpEnumType([ + "enumClass"=>MultipleDeprecationsPhpEnum::class + ]); } public function testExecutesWithEnumTypeFromPhpEnum(): void { - $enumType = new PhpEnumType(PhpEnum::class); + $enumType = new PhpEnumType([ + "enumClass" => MyCustomPhpEnum::class, + ]); $schema = new Schema([ 'query' => new ObjectType([ 'name' => 'Query', @@ -118,9 +132,9 @@ public function testExecutesWithEnumTypeFromPhpEnum(): void 'type' => Type::nonNull($enumType), ], ], - 'resolve' => static function ($_, array $args): PhpEnum { + 'resolve' => static function ($_, array $args): MyCustomPhpEnum { $bar = $args['bar']; - assert($bar === PhpEnum::A); + assert($bar === MyCustomPhpEnum::A); return $bar; }, @@ -138,7 +152,9 @@ public function testExecutesWithEnumTypeFromPhpEnum(): void public function testSerializesBackedEnumsByValue(): void { - $enumType = new PhpEnumType(IntPhpEnum::class); + $enumType = new PhpEnumType([ + "enumClass"=>IntPhpEnum::class, + ]); $schema = new Schema([ 'query' => new ObjectType([ 'name' => 'Query', @@ -160,7 +176,7 @@ public function testSerializesBackedEnumsByValue(): void public function testAcceptsEnumFromVariableValues(): void { - $enumType = new PhpEnumType(PhpEnum::class); + $enumType = new PhpEnumType(MyCustomPhpEnum::class); $schema = null; $schema = new Schema([ @@ -174,9 +190,9 @@ public function testAcceptsEnumFromVariableValues(): void 'type' => Type::nonNull($enumType), ], ], - 'resolve' => static function (bool $executeAgain, array $args, $context, ResolveInfo $resolveInfo) use (&$schema): PhpEnum { + 'resolve' => static function (bool $executeAgain, array $args, $context, ResolveInfo $resolveInfo) use (&$schema): MyCustomPhpEnum { $bar = $args['bar']; - assert($bar === PhpEnum::A); + assert($bar === MyCustomPhpEnum::A); assert($schema instanceof Schema); @@ -218,7 +234,7 @@ public function testAcceptsEnumFromVariableValues(): void public function testFailsToSerializeNonEnum(): void { - $enumType = new PhpEnumType(PhpEnum::class); + $enumType = new PhpEnumType(['enumClass' => MyCustomPhpEnum::class]); $schema = new Schema([ 'query' => new ObjectType([ 'name' => 'Query', @@ -239,7 +255,7 @@ public function testFailsToSerializeNonEnum(): void public function testFailsToSerializeNonEnumValue(): void { - $enumType = new PhpEnumType(IntPhpEnum::class); + $enumType = new PhpEnumType(['enumClass'=>IntPhpEnum::class]); $schema = new Schema([ 'query' => new ObjectType([ 'name' => 'Query', From 93dfeb083e7a602aeb32597c0265fe29f70e6dee Mon Sep 17 00:00:00 2001 From: shmax Date: Fri, 18 Oct 2024 06:45:44 -0700 Subject: [PATCH 2/4] fix more tests --- src/Type/Definition/PhpEnumType.php | 2 +- tests/Type/PhpEnumTypeTest.php | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Type/Definition/PhpEnumType.php b/src/Type/Definition/PhpEnumType.php index 29b9945f2..536bd5b56 100644 --- a/src/Type/Definition/PhpEnumType.php +++ b/src/Type/Definition/PhpEnumType.php @@ -63,7 +63,7 @@ public function serialize($value): string return $value->name; } - if (! is_a($value, $enumClass)) { + if (is_a($enumClass, \BackedEnum::class, true)) { try { $instance = $enumClass::from($value); } catch (\ValueError|\TypeError $_) { diff --git a/tests/Type/PhpEnumTypeTest.php b/tests/Type/PhpEnumTypeTest.php index f833fac46..690f0f9c1 100644 --- a/tests/Type/PhpEnumTypeTest.php +++ b/tests/Type/PhpEnumTypeTest.php @@ -176,7 +176,7 @@ public function testSerializesBackedEnumsByValue(): void public function testAcceptsEnumFromVariableValues(): void { - $enumType = new PhpEnumType(MyCustomPhpEnum::class); + $enumType = new PhpEnumType(['enumClass' => MyCustomPhpEnum::class]); $schema = null; $schema = new Schema([ @@ -199,7 +199,7 @@ public function testAcceptsEnumFromVariableValues(): void if ($executeAgain) { $executionResult = GraphQL::executeQuery( $schema, - 'query ($bar: PhpEnum!) { foo(bar: $bar) }', + 'query ($bar: MyCustomPhpEnum!) { foo(bar: $bar) }', false, null, $resolveInfo->variableValues @@ -220,7 +220,7 @@ public function testAcceptsEnumFromVariableValues(): void $executionResult = GraphQL::executeQuery( $schema, - 'query ($bar: PhpEnum!) { foo(bar: $bar) }', + 'query ($bar: MyCustomPhpEnum!) { foo(bar: $bar) }', true, null, ['bar' => 'A'] @@ -249,7 +249,7 @@ public function testFailsToSerializeNonEnum(): void $result = GraphQL::executeQuery($schema, '{ foo }'); - self::expectExceptionObject(new SerializationError('Cannot serialize value as enum: "A", expected instance of GraphQL\\Tests\\Type\\PhpEnumType\\PhpEnum.')); + self::expectExceptionObject(new SerializationError('Cannot serialize value as enum: "A", expected instance of GraphQL\\Tests\\Type\\PhpEnumType\\MyCustomPhpEnum.')); $result->toArray(DebugFlag::RETHROW_INTERNAL_EXCEPTIONS); } From 0c03387ca0e7547022bd9bca3ba9d1dbaabd29dd Mon Sep 17 00:00:00 2001 From: shmax Date: Fri, 18 Oct 2024 09:33:35 -0700 Subject: [PATCH 3/4] go back to enumClass member --- src/Type/Definition/PhpEnumType.php | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/Type/Definition/PhpEnumType.php b/src/Type/Definition/PhpEnumType.php index 536bd5b56..3382e38e7 100644 --- a/src/Type/Definition/PhpEnumType.php +++ b/src/Type/Definition/PhpEnumType.php @@ -4,13 +4,12 @@ use GraphQL\Error\SerializationError; use GraphQL\Utils\PhpDoc; -use GraphQL\Language\AST\EnumValueDefinitionNode; use GraphQL\Language\AST\EnumTypeDefinitionNode; use GraphQL\Language\AST\EnumTypeExtensionNode; use GraphQL\Utils\Utils; /** - * @phpstan-import-type PartialEnumValueConfig from EnumTypev + * @phpstan-import-type PartialEnumValueConfig from EnumType * * @phpstan-type PhpEnumTypeConfig array{ * name?: string|null, @@ -18,6 +17,7 @@ * enumClass: class-string<\UnitEnum>, * astNode?: EnumTypeDefinitionNode|null, * extensionASTNodes?: array|null + * } */ class PhpEnumType extends EnumType { @@ -25,6 +25,11 @@ class PhpEnumType extends EnumType public const MULTIPLE_DEPRECATIONS_DISALLOWED = 'Using more than 1 Deprecated attribute is not supported.'; + /** + * @var class-string<\UnitEnum> + */ + protected string $enumClass; + /** * @phpstan-param PhpEnumTypeConfig $config * @@ -33,8 +38,8 @@ class PhpEnumType extends EnumType */ public function __construct(array $config) { - $enumClass = $config['enumClass']; - $reflection = new \ReflectionEnum($enumClass); + $this->enumClass = $config['enumClass']; + $reflection = new \ReflectionEnum($this->enumClass); /** * @var array $enumDefinitions @@ -49,16 +54,16 @@ public function __construct(array $config) } parent::__construct([ - 'name' => $config['name'] ?? $this->baseName($enumClass), + 'name' => $config['name'] ?? $this->baseName($this->enumClass), 'values' => $enumDefinitions, 'description' => $config['description'] ?? $this->extractDescription($reflection), - 'enumClass' => $enumClass + 'enumClass' => $this->enumClass ]); } public function serialize($value): string { - $enumClass = $this->config['enumClass']; + $enumClass = $this->enumClass; if ($value instanceof $enumClass) { return $value->name; } @@ -81,7 +86,7 @@ public function serialize($value): string public function parseValue($value) { // Can happen when variable values undergo a serialization cycle before execution - if ($value instanceof $this->config['enumClass']) { + if ($value instanceof $this->enumClass) { return $value; } From 90c97514fcabbff1faf241d3cd3425f8ae213738 Mon Sep 17 00:00:00 2001 From: shmax Date: Fri, 18 Oct 2024 16:34:41 +0000 Subject: [PATCH 4/4] Apply php-cs-fixer changes --- src/Type/Definition/PhpEnumType.php | 9 +++------ tests/Type/PhpEnumTypeTest.php | 19 +++++++++---------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/src/Type/Definition/PhpEnumType.php b/src/Type/Definition/PhpEnumType.php index 3382e38e7..7ab207b78 100644 --- a/src/Type/Definition/PhpEnumType.php +++ b/src/Type/Definition/PhpEnumType.php @@ -3,9 +3,9 @@ namespace GraphQL\Type\Definition; use GraphQL\Error\SerializationError; -use GraphQL\Utils\PhpDoc; use GraphQL\Language\AST\EnumTypeDefinitionNode; use GraphQL\Language\AST\EnumTypeExtensionNode; +use GraphQL\Utils\PhpDoc; use GraphQL\Utils\Utils; /** @@ -24,10 +24,7 @@ class PhpEnumType extends EnumType public const MULTIPLE_DESCRIPTIONS_DISALLOWED = 'Using more than 1 Description attribute is not supported.'; public const MULTIPLE_DEPRECATIONS_DISALLOWED = 'Using more than 1 Deprecated attribute is not supported.'; - - /** - * @var class-string<\UnitEnum> - */ + /** @var class-string<\UnitEnum> */ protected string $enumClass; /** @@ -57,7 +54,7 @@ public function __construct(array $config) 'name' => $config['name'] ?? $this->baseName($this->enumClass), 'values' => $enumDefinitions, 'description' => $config['description'] ?? $this->extractDescription($reflection), - 'enumClass' => $this->enumClass + 'enumClass' => $this->enumClass, ]); } diff --git a/tests/Type/PhpEnumTypeTest.php b/tests/Type/PhpEnumTypeTest.php index 690f0f9c1..33b3478cf 100644 --- a/tests/Type/PhpEnumTypeTest.php +++ b/tests/Type/PhpEnumTypeTest.php @@ -6,7 +6,6 @@ use GraphQL\Error\SerializationError; use GraphQL\GraphQL; use GraphQL\Tests\TestCaseBase; -use GraphQL\Tests\Type\PhpEnumType\BackedPhpEnum; use GraphQL\Tests\Type\PhpEnumType\DocBlockPhpEnum; use GraphQL\Tests\Type\PhpEnumType\IntPhpEnum; use GraphQL\Tests\Type\PhpEnumType\MultipleDeprecationsPhpEnum; @@ -34,7 +33,7 @@ protected function setUp(): void public function testConstructEnumTypeFromPhpEnum(): void { $enumType = new PhpEnumType([ - "enumClass" => MyCustomPhpEnum::class + 'enumClass' => MyCustomPhpEnum::class, ]); self::assertSame(<<<'GRAPHQL' "foo" @@ -60,8 +59,8 @@ enum IntPhpEnum { public function testConstructEnumTypeFromPhpEnumWithCustomName(): void { $enumType = new PhpEnumType([ - "enumClass"=>MyCustomPhpEnum::class, - "name"=>'CustomNamedPhpEnum' + 'enumClass' => MyCustomPhpEnum::class, + 'name' => 'CustomNamedPhpEnum', ]); self::assertSame(<<<'GRAPHQL' "foo" @@ -96,7 +95,7 @@ public function testMultipleDescriptionsDisallowed(): void { self::expectExceptionObject(new \Exception(PhpEnumType::MULTIPLE_DESCRIPTIONS_DISALLOWED)); new PhpEnumType([ - "enumClass"=>MultipleDescriptionsPhpEnum::class + 'enumClass' => MultipleDescriptionsPhpEnum::class, ]); } @@ -104,7 +103,7 @@ public function testMultipleDescriptionsDisallowedOnCase(): void { self::expectExceptionObject(new \Exception(PhpEnumType::MULTIPLE_DESCRIPTIONS_DISALLOWED)); new PhpEnumType([ - "enumClass" => MultipleDescriptionsCasePhpEnum::class + 'enumClass' => MultipleDescriptionsCasePhpEnum::class, ]); } @@ -112,14 +111,14 @@ public function testMultipleDeprecationsDisallowed(): void { self::expectExceptionObject(new \Exception(PhpEnumType::MULTIPLE_DEPRECATIONS_DISALLOWED)); new PhpEnumType([ - "enumClass"=>MultipleDeprecationsPhpEnum::class + 'enumClass' => MultipleDeprecationsPhpEnum::class, ]); } public function testExecutesWithEnumTypeFromPhpEnum(): void { $enumType = new PhpEnumType([ - "enumClass" => MyCustomPhpEnum::class, + 'enumClass' => MyCustomPhpEnum::class, ]); $schema = new Schema([ 'query' => new ObjectType([ @@ -153,7 +152,7 @@ public function testExecutesWithEnumTypeFromPhpEnum(): void public function testSerializesBackedEnumsByValue(): void { $enumType = new PhpEnumType([ - "enumClass"=>IntPhpEnum::class, + 'enumClass' => IntPhpEnum::class, ]); $schema = new Schema([ 'query' => new ObjectType([ @@ -255,7 +254,7 @@ public function testFailsToSerializeNonEnum(): void public function testFailsToSerializeNonEnumValue(): void { - $enumType = new PhpEnumType(['enumClass'=>IntPhpEnum::class]); + $enumType = new PhpEnumType(['enumClass' => IntPhpEnum::class]); $schema = new Schema([ 'query' => new ObjectType([ 'name' => 'Query',