diff --git a/composer.json b/composer.json index b77cc093cc7..07fe258d918 100644 --- a/composer.json +++ b/composer.json @@ -88,7 +88,7 @@ "symfony/yaml": "^3.4 || ^4.0 || ^5.0", "teohhanhui/stubs-mongodb": "@dev", "twig/twig": "^1.42.3 || ^2.12", - "webonyx/graphql-php": ">=0.13.1 <1.0" + "webonyx/graphql-php": "^14.0" }, "conflict": { "doctrine/common": "<2.7", diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 71bcca5310e..0ee1f7d1868 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -38,9 +38,6 @@ parameters: - message: '#Variable \$positionPm might not be defined\.#' path: src/Util/ClassInfoTrait.php - - - message: "#Cannot access offset 'node' on bool\\.#" - path: src/GraphQl/Serializer/SerializerContextBuilder.php - '#Access to an undefined property Prophecy\\Prophecy\\ObjectProphecy<(\\?[a-zA-Z0-9_]+)+>::\$[a-zA-Z0-9_]+#' - message: '#Call to an undefined method Doctrine\\Persistence\\ObjectManager::getConnection\(\)#' diff --git a/src/GraphQl/Action/EntrypointAction.php b/src/GraphQl/Action/EntrypointAction.php index 18326878a14..7254f667f00 100644 --- a/src/GraphQl/Action/EntrypointAction.php +++ b/src/GraphQl/Action/EntrypointAction.php @@ -16,6 +16,7 @@ use ApiPlatform\Core\GraphQl\ExecutorInterface; use ApiPlatform\Core\GraphQl\Type\SchemaBuilderInterface; use GraphQL\Error\Debug; +use GraphQL\Error\DebugFlag; use GraphQL\Error\Error; use GraphQL\Error\UserError; use GraphQL\Executor\ExecutionResult; @@ -46,7 +47,11 @@ public function __construct(SchemaBuilderInterface $schemaBuilder, ExecutorInter $this->executor = $executor; $this->graphiQlAction = $graphiQlAction; $this->graphQlPlaygroundAction = $graphQlPlaygroundAction; - $this->debug = $debug ? Debug::INCLUDE_DEBUG_MESSAGE | Debug::INCLUDE_TRACE : false; + if (class_exists(Debug::class)) { + $this->debug = $debug ? Debug::INCLUDE_DEBUG_MESSAGE | Debug::INCLUDE_TRACE : false; + } else { + $this->debug = $debug ? DebugFlag::INCLUDE_DEBUG_MESSAGE | DebugFlag::INCLUDE_TRACE : DebugFlag::NONE; + } $this->graphiqlEnabled = $graphiqlEnabled; $this->graphQlPlaygroundEnabled = $graphQlPlaygroundEnabled; $this->defaultIde = $defaultIde; @@ -210,7 +215,7 @@ private function decodeVariables(string $variables): array private function buildExceptionResponse(\Exception $e, int $statusCode): JsonResponse { - $executionResult = new ExecutionResult(null, [new Error($e->getMessage(), null, null, null, null, $e)]); + $executionResult = new ExecutionResult(null, [new Error($e->getMessage(), null, null, [], null, $e)]); return new JsonResponse($executionResult->toArray($this->debug), $statusCode); } diff --git a/src/GraphQl/Type/Definition/IterableType.php b/src/GraphQl/Type/Definition/IterableType.php index dd1be487a0d..bf488c58667 100644 --- a/src/GraphQl/Type/Definition/IterableType.php +++ b/src/GraphQl/Type/Definition/IterableType.php @@ -18,6 +18,8 @@ use GraphQL\Language\AST\FloatValueNode; use GraphQL\Language\AST\IntValueNode; use GraphQL\Language\AST\ListValueNode; +use GraphQL\Language\AST\Node; +use GraphQL\Language\AST\NullValueNode; use GraphQL\Language\AST\ObjectValueNode; use GraphQL\Language\AST\StringValueNode; use GraphQL\Language\AST\ValueNode; @@ -72,8 +74,10 @@ public function parseValue($value) /** * {@inheritdoc} + * + * @param ObjectValueNode|ListValueNode|IntValueNode|FloatValueNode|StringValueNode|BooleanValueNode|NullValueNode $valueNode */ - public function parseLiteral($valueNode, array $variables = null) + public function parseLiteral(Node $valueNode, ?array $variables = null) { if ($valueNode instanceof ObjectValueNode || $valueNode instanceof ListValueNode) { return $this->parseIterableLiteral($valueNode); diff --git a/src/GraphQl/Type/Definition/TypeInterface.php b/src/GraphQl/Type/Definition/TypeInterface.php index fc4ff950306..b974f05a50d 100644 --- a/src/GraphQl/Type/Definition/TypeInterface.php +++ b/src/GraphQl/Type/Definition/TypeInterface.php @@ -13,14 +13,12 @@ namespace ApiPlatform\Core\GraphQl\Type\Definition; -use GraphQL\Type\Definition\LeafType; - /** * @experimental * * @author Alan Poulain */ -interface TypeInterface extends LeafType +interface TypeInterface { public function getName(): string; } diff --git a/src/GraphQl/Type/Definition/UploadType.php b/src/GraphQl/Type/Definition/UploadType.php index e0b2a76b9eb..36f3b715653 100644 --- a/src/GraphQl/Type/Definition/UploadType.php +++ b/src/GraphQl/Type/Definition/UploadType.php @@ -14,6 +14,7 @@ namespace ApiPlatform\Core\GraphQl\Type\Definition; use GraphQL\Error\Error; +use GraphQL\Language\AST\Node; use GraphQL\Type\Definition\ScalarType; use GraphQL\Utils\Utils; use Symfony\Component\HttpFoundation\File\UploadedFile; @@ -61,7 +62,7 @@ public function parseValue($value): UploadedFile /** * {@inheritdoc} */ - public function parseLiteral($valueNode, array $variables = null) + public function parseLiteral(Node $valueNode, array $variables = null) { throw new Error('`Upload` cannot be hardcoded in query, be sure to conform to GraphQL multipart request specification.', $valueNode); } diff --git a/src/GraphQl/Type/FieldsBuilder.php b/src/GraphQl/Type/FieldsBuilder.php index 237f1746e5c..cd3108700a8 100644 --- a/src/GraphQl/Type/FieldsBuilder.php +++ b/src/GraphQl/Type/FieldsBuilder.php @@ -93,7 +93,7 @@ public function getItemQueryFields(string $resourceClass, ResourceMetadata $reso $shortName = $resourceMetadata->getShortName(); $fieldName = lcfirst('item_query' === $queryName ? $shortName : $queryName.$shortName); - $deprecationReason = (string) $resourceMetadata->getGraphqlAttribute($queryName, 'deprecation_reason', '', true); + $deprecationReason = $resourceMetadata->getGraphqlAttribute($queryName, 'deprecation_reason', null, true); if ($fieldConfiguration = $this->getResourceFieldConfiguration(null, null, $deprecationReason, new Type(Type::BUILTIN_TYPE_OBJECT, true, $resourceClass), $resourceClass, false, $queryName, null)) { $args = $this->resolveResourceArgs($configuration['args'] ?? [], $queryName, $shortName); @@ -113,7 +113,7 @@ public function getCollectionQueryFields(string $resourceClass, ResourceMetadata $shortName = $resourceMetadata->getShortName(); $fieldName = lcfirst('collection_query' === $queryName ? $shortName : $queryName.$shortName); - $deprecationReason = (string) $resourceMetadata->getGraphqlAttribute($queryName, 'deprecation_reason', '', true); + $deprecationReason = $resourceMetadata->getGraphqlAttribute($queryName, 'deprecation_reason', null, true); if ($fieldConfiguration = $this->getResourceFieldConfiguration(null, null, $deprecationReason, new Type(Type::BUILTIN_TYPE_OBJECT, false, null, true, null, new Type(Type::BUILTIN_TYPE_OBJECT, false, $resourceClass)), $resourceClass, false, $queryName, null)) { $args = $this->resolveResourceArgs($configuration['args'] ?? [], $queryName, $shortName); @@ -133,7 +133,7 @@ public function getMutationFields(string $resourceClass, ResourceMetadata $resou $mutationFields = []; $shortName = $resourceMetadata->getShortName(); $resourceType = new Type(Type::BUILTIN_TYPE_OBJECT, true, $resourceClass); - $deprecationReason = $resourceMetadata->getGraphqlAttribute($mutationName, 'deprecation_reason', '', true); + $deprecationReason = $resourceMetadata->getGraphqlAttribute($mutationName, 'deprecation_reason', null, true); if ($fieldConfiguration = $this->getResourceFieldConfiguration(null, ucfirst("{$mutationName}s a $shortName."), $deprecationReason, $resourceType, $resourceClass, false, null, $mutationName)) { $fieldConfiguration['args'] += ['input' => $this->getResourceFieldConfiguration(null, null, $deprecationReason, $resourceType, $resourceClass, true, null, $mutationName)]; @@ -194,7 +194,7 @@ public function getResourceObjectTypeFields(?string $resourceClass, ResourceMeta continue; } - if ($fieldConfiguration = $this->getResourceFieldConfiguration($property, $propertyMetadata->getDescription(), $propertyMetadata->getAttribute('deprecation_reason', ''), $propertyType, $resourceClass, $input, $queryName, $mutationName, $depth)) { + if ($fieldConfiguration = $this->getResourceFieldConfiguration($property, $propertyMetadata->getDescription(), $propertyMetadata->getAttribute('deprecation_reason', null), $propertyType, $resourceClass, $input, $queryName, $mutationName, $depth)) { $fields['id' === $property ? '_id' : $this->normalizePropertyName($property)] = $fieldConfiguration; } } @@ -228,7 +228,7 @@ public function resolveResourceArgs(array $args, string $operationName, string $ * * @see http://webonyx.github.io/graphql-php/type-system/object-types/ */ - private function getResourceFieldConfiguration(?string $property, ?string $fieldDescription, string $deprecationReason, Type $type, string $rootResource, bool $input, ?string $queryName, ?string $mutationName, int $depth = 0): ?array + private function getResourceFieldConfiguration(?string $property, ?string $fieldDescription, ?string $deprecationReason, Type $type, string $rootResource, bool $input, ?string $queryName, ?string $mutationName, int $depth = 0): ?array { try { $resourceClass = $this->typeBuilder->isCollection($type) && ($collectionValueType = $type->getCollectionValueType()) ? $collectionValueType->getClassName() : $type->getClassName(); diff --git a/tests/Fixtures/TestBundle/GraphQl/Type/Definition/DateTimeType.php b/tests/Fixtures/TestBundle/GraphQl/Type/Definition/DateTimeType.php index f103ec3f798..d6b64b656c8 100644 --- a/tests/Fixtures/TestBundle/GraphQl/Type/Definition/DateTimeType.php +++ b/tests/Fixtures/TestBundle/GraphQl/Type/Definition/DateTimeType.php @@ -15,6 +15,7 @@ use ApiPlatform\Core\GraphQl\Type\Definition\TypeInterface; use GraphQL\Error\Error; +use GraphQL\Language\AST\Node; use GraphQL\Language\AST\StringValueNode; use GraphQL\Type\Definition\ScalarType; use GraphQL\Utils\Utils; @@ -77,7 +78,7 @@ public function parseValue($value) /** * {@inheritdoc} */ - public function parseLiteral($valueNode, ?array $variables = null) + public function parseLiteral(Node $valueNode, ?array $variables = null) { if ($valueNode instanceof StringValueNode && false !== \DateTime::createFromFormat(\DateTime::ATOM, $valueNode->value)) { return $valueNode->value; diff --git a/tests/GraphQl/Action/EntrypointActionTest.php b/tests/GraphQl/Action/EntrypointActionTest.php index 74ffd74aad0..a0e16a5020e 100644 --- a/tests/GraphQl/Action/EntrypointActionTest.php +++ b/tests/GraphQl/Action/EntrypointActionTest.php @@ -18,6 +18,7 @@ use ApiPlatform\Core\GraphQl\Action\GraphQlPlaygroundAction; use ApiPlatform\Core\GraphQl\ExecutorInterface; use ApiPlatform\Core\GraphQl\Type\SchemaBuilderInterface; +use GraphQL\Error\DebugFlag; use GraphQL\Executor\ExecutionResult; use GraphQL\Type\Schema; use PHPUnit\Framework\TestCase; @@ -229,7 +230,7 @@ private function getEntrypointAction(array $variables = ['graphqlVariable']): En $schemaBuilderProphecy->getSchema()->willReturn($schema->reveal()); $executionResultProphecy = $this->prophesize(ExecutionResult::class); - $executionResultProphecy->toArray(false)->willReturn(['GraphQL']); + $executionResultProphecy->toArray(DebugFlag::NONE)->willReturn(['GraphQL']); $executorProphecy = $this->prophesize(ExecutorInterface::class); $executorProphecy->executeQuery(Argument::is($schema->reveal()), 'graphqlQuery', null, null, $variables, 'graphqlOperationName')->willReturn($executionResultProphecy->reveal()); diff --git a/tests/GraphQl/Resolver/ResourceFieldResolverTest.php b/tests/GraphQl/Resolver/ResourceFieldResolverTest.php index 12e648a5210..33e0799efdb 100644 --- a/tests/GraphQl/Resolver/ResourceFieldResolverTest.php +++ b/tests/GraphQl/Resolver/ResourceFieldResolverTest.php @@ -17,6 +17,7 @@ use ApiPlatform\Core\GraphQl\Resolver\ResourceFieldResolver; use ApiPlatform\Core\GraphQl\Serializer\ItemNormalizer; use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\Dummy; +use GraphQL\Type\Definition\FieldDefinition; use GraphQL\Type\Definition\ObjectType; use GraphQL\Type\Definition\ResolveInfo; use GraphQL\Type\Schema; @@ -29,7 +30,7 @@ public function testId() $iriConverterProphecy = $this->prophesize(IriConverterInterface::class); $iriConverterProphecy->getItemIriFromResourceClass(Dummy::class, ['id' => 1])->willReturn('/dummies/1')->shouldBeCalled(); - $resolveInfo = new ResolveInfo('id', [], new ObjectType(['name' => '']), new ObjectType(['name' => '']), [], new Schema([]), [], null, null, []); + $resolveInfo = new ResolveInfo(FieldDefinition::create(['name' => 'id', 'type' => new ObjectType(['name' => ''])]), [], new ObjectType(['name' => '']), [], new Schema([]), [], null, null, []); $resolver = new ResourceFieldResolver($iriConverterProphecy->reveal()); $this->assertEquals('/dummies/1', $resolver([ItemNormalizer::ITEM_RESOURCE_CLASS_KEY => Dummy::class, ItemNormalizer::ITEM_IDENTIFIERS_KEY => ['id' => 1]], [], [], $resolveInfo)); @@ -39,7 +40,7 @@ public function testOriginalId() { $iriConverterProphecy = $this->prophesize(IriConverterInterface::class); - $resolveInfo = new ResolveInfo('_id', [], new ObjectType(['name' => '']), new ObjectType(['name' => '']), [], new Schema([]), [], null, null, []); + $resolveInfo = new ResolveInfo(FieldDefinition::create(['name' => '_id', 'type' => new ObjectType(['name' => ''])]), [], new ObjectType(['name' => '']), [], new Schema([]), [], null, null, []); $resolver = new ResourceFieldResolver($iriConverterProphecy->reveal()); $this->assertEquals(1, $resolver(['id' => 1], [], [], $resolveInfo)); @@ -49,7 +50,7 @@ public function testDirectAccess() { $iriConverterProphecy = $this->prophesize(IriConverterInterface::class); - $resolveInfo = new ResolveInfo('foo', [], new ObjectType(['name' => '']), new ObjectType(['name' => '']), [], new Schema([]), [], null, null, []); + $resolveInfo = new ResolveInfo(FieldDefinition::create(['name' => 'foo', 'type' => new ObjectType(['name' => ''])]), [], new ObjectType(['name' => '']), [], new Schema([]), [], null, null, []); $resolver = new ResourceFieldResolver($iriConverterProphecy->reveal()); $this->assertEquals('bar', $resolver(['foo' => 'bar'], [], [], $resolveInfo)); @@ -61,7 +62,7 @@ public function testNonResource() $iriConverterProphecy = $this->prophesize(IriConverterInterface::class); $iriConverterProphecy->getIriFromItem($dummy)->willReturn('/dummies/1')->shouldNotBeCalled(); - $resolveInfo = new ResolveInfo('id', [], new ObjectType(['name' => '']), new ObjectType(['name' => '']), [], new Schema([]), [], null, null, []); + $resolveInfo = new ResolveInfo(FieldDefinition::create(['name' => 'id', 'type' => new ObjectType(['name' => ''])]), [], new ObjectType(['name' => '']), [], new Schema([]), [], null, null, []); $resolver = new ResourceFieldResolver($iriConverterProphecy->reveal()); $this->assertNull($resolver([], [], [], $resolveInfo)); diff --git a/tests/GraphQl/Type/FieldsBuilderTest.php b/tests/GraphQl/Type/FieldsBuilderTest.php index 014113d45ed..eaac3a037df 100644 --- a/tests/GraphQl/Type/FieldsBuilderTest.php +++ b/tests/GraphQl/Type/FieldsBuilderTest.php @@ -162,7 +162,7 @@ public function itemQueryFieldsProvider(): array 'id' => ['type' => GraphQLType::nonNull(GraphQLType::id())], ], 'resolve' => $resolver, - 'deprecationReason' => '', + 'deprecationReason' => null, ], ], ], @@ -174,7 +174,7 @@ public function itemQueryFieldsProvider(): array 'description' => null, 'args' => [], 'resolve' => null, - 'deprecationReason' => '', + 'deprecationReason' => null, 'name' => 'customActionName', ], ], @@ -191,7 +191,7 @@ public function itemQueryFieldsProvider(): array ], ], 'resolve' => null, - 'deprecationReason' => '', + 'deprecationReason' => null, ], ], ], @@ -290,7 +290,7 @@ public function collectionQueryFieldsProvider(): array 'dateField' => new InputObjectType(['name' => 'ShortNameFilter_dateField', 'fields' => ['before' => $graphqlType]]), ], 'resolve' => $resolver, - 'deprecationReason' => '', + 'deprecationReason' => null, ], ], ], @@ -303,7 +303,7 @@ public function collectionQueryFieldsProvider(): array 'description' => null, 'args' => [], 'resolve' => $resolver, - 'deprecationReason' => '', + 'deprecationReason' => null, 'name' => 'customActionName', ], ], @@ -321,7 +321,7 @@ public function collectionQueryFieldsProvider(): array ], ], 'resolve' => $resolver, - 'deprecationReason' => '', + 'deprecationReason' => null, ], ], ], @@ -381,11 +381,11 @@ public function mutationFieldsProvider(): array 'description' => null, 'args' => [], 'resolve' => null, - 'deprecationReason' => '', + 'deprecationReason' => null, ], ], 'resolve' => $collectionResolver, - 'deprecationReason' => '', + 'deprecationReason' => null, ], ], ], @@ -437,14 +437,14 @@ public function resourceObjectTypeFieldsProvider(): array 'description' => null, 'args' => [], 'resolve' => null, - 'deprecationReason' => '', + 'deprecationReason' => null, ], 'name_converted' => [ 'type' => GraphQLType::nonNull(GraphQLType::string()), 'description' => null, 'args' => [], 'resolve' => null, - 'deprecationReason' => '', + 'deprecationReason' => null, ], ], ], @@ -463,7 +463,7 @@ public function resourceObjectTypeFieldsProvider(): array 'description' => null, 'args' => [], 'resolve' => null, - 'deprecationReason' => '', + 'deprecationReason' => null, ], ], ], @@ -483,7 +483,7 @@ public function resourceObjectTypeFieldsProvider(): array 'description' => null, 'args' => [], 'resolve' => null, - 'deprecationReason' => '', + 'deprecationReason' => null, ], ], ], @@ -511,14 +511,14 @@ public function resourceObjectTypeFieldsProvider(): array 'description' => null, 'args' => [], 'resolve' => null, - 'deprecationReason' => '', + 'deprecationReason' => null, ], '_id' => [ 'type' => GraphQLType::nonNull(GraphQLType::string()), 'description' => null, 'args' => [], 'resolve' => null, - 'deprecationReason' => '', + 'deprecationReason' => null, ], 'clientMutationId' => GraphQLType::string(), ], @@ -546,7 +546,7 @@ public function resourceObjectTypeFieldsProvider(): array 'description' => null, 'args' => [], 'resolve' => null, - 'deprecationReason' => '', + 'deprecationReason' => null, ], 'clientMutationId' => GraphQLType::string(), ], @@ -565,7 +565,7 @@ public function resourceObjectTypeFieldsProvider(): array 'description' => null, 'args' => [], 'resolve' => null, - 'deprecationReason' => '', + 'deprecationReason' => null, ], 'clientMutationId' => GraphQLType::string(), ],