Skip to content

Merge 2.3 into master #2359

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Dec 1, 2018
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
3 changes: 1 addition & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
],
"require": {
"php": ">=7.1",

"doctrine/inflector": "^1.0",
"psr/cache": "^1.0",
"psr/container": "^1.0",
Expand Down Expand Up @@ -70,7 +69,7 @@
"symfony/validator": "^3.3 || ^4.0",
"symfony/web-profiler-bundle": "^3.3 || ^4.0",
"symfony/yaml": "^3.3 || ^4.0",
"webonyx/graphql-php": ">=0.12 <1.0"
"webonyx/graphql-php": ">=0.13 <1.0"
},
"conflict": {
"symfony/dependency-injection": "<3.4"
Expand Down
1 change: 0 additions & 1 deletion features/graphql/introspection.feature
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,6 @@ Feature: GraphQL introspection support
And the JSON node "data.typeCreatePayload.fields[2].name" should be equal to "baz"
And the JSON node "data.typeCreatePayload.fields[3].name" should be equal to "clientMutationId"

@dropSchema
Scenario: Retrieve an item through a GraphQL query
Given there are 4 dummy objects with relatedDummy
When I send the following GraphQL request:
Expand Down
2 changes: 0 additions & 2 deletions features/graphql/mutation.feature
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,6 @@ Feature: GraphQL mutation support
And the JSON node "data.deleteFoo.id" should be equal to "/foos/1"
And the JSON node "data.deleteFoo.clientMutationId" should be equal to "anotherId"

@dropSchema
Scenario: Delete an item with composite identifiers through a mutation
Given there are Composite identifier objects
When I send the following GraphQL request:
Expand Down Expand Up @@ -250,7 +249,6 @@ Feature: GraphQL mutation support
And the JSON node "data.createDummyGroup.baz" should be null
And the JSON node "data.createDummyGroup.clientMutationId" should be equal to "myId"

@dropSchema
Scenario: Trigger a validation error
When I send the following GraphQL request:
"""
Expand Down
61 changes: 60 additions & 1 deletion features/graphql/query.feature
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,6 @@ Feature: GraphQL query support
And the header "Content-Type" should be equal to "application/json"
And the JSON node "data.dummy._id" should be equal to "1"

@dropSchema
Scenario: Retrieve an nonexistent item through a GraphQL query
When I send the following GraphQL request:
"""
Expand All @@ -152,3 +151,63 @@ Feature: GraphQL query support
And the response should be in JSON
And the header "Content-Type" should be equal to "application/json"
And the JSON node "data.dummy" should be null

Scenario: Retrieve an nonexistent IRI through a GraphQL query
When I send the following GraphQL request:
"""
{
foo(id: "/foo/1") {
name
}
}
"""
Then the response status code should be 200
And the response should be in JSON
And the header "Content-Type" should be equal to "application/json"
And the JSON node "errors[0].debugMessage" should be equal to 'No route matches "/foo/1".'
And the JSON should be valid according to this schema:
"""
{
"type": "object",
"properties": {
"errors": {
"type": "array",
"items": {
"type": "object",
"properties": {
"debugMessage": {"type": "string"},
"message": {"type": "string"},
"extensions": {"type": "object"},
"locations": {"type": "array"},
"path": {"type": "array"},
"trace": {
"type": "array",
"items": {
"type": "object",
"properties": {
"file": {"type": "string"},
"line": {"type": "integer"},
"call": {"type": ["string", "null"]},
"function": {"type": ["string", "null"]}
},
"additionalProperties": false
},
"minItems": 1
}
},
"required": [
"debugMessage",
"message",
"extensions",
"locations",
"path",
"trace"
],
"additionalProperties": false
},
"minItems": 1,
"maxItems": 1
}
}
}
"""
1 change: 0 additions & 1 deletion features/hal/collection.feature
Original file line number Diff line number Diff line change
Expand Up @@ -808,7 +808,6 @@ Feature: HAL Collections support
}
"""

@dropSchema
Scenario: Allow passing 0 to `itemsPerPage`
When I add "Accept" header equal to "application/hal+json"
And I send a "GET" request to "/dummies?itemsPerPage=0"
Expand Down
1 change: 0 additions & 1 deletion features/hal/max_depth.feature
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,6 @@ Feature: Max depth handling
}
"""

@dropSchema
Scenario: Add a 2nd level of descendants when eager fetching is disabled
Given there is a max depth dummy with 1 level of descendants
When I add "Accept" header equal to "application/hal+json"
Expand Down
1 change: 0 additions & 1 deletion features/jsonld/max_depth.feature
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ Feature: Max depth handling
}
"""

@dropSchema
Scenario: Add a 2nd level of descendants
When I add "Content-Type" header equal to "application/ld+json"
And I send a "PUT" request to "max_depth_eager_dummies/1" with body:
Expand Down
1 change: 0 additions & 1 deletion features/main/exposed_state.feature
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ Feature: Expose persisted object state
}
"""

@dropSchema
Scenario: Update a resource with truncable value value should return the correct object state
When I add "Content-Type" header equal to "application/ld+json"
And I send a "PUT" request to "/truncated_dummies/1" with body:
Expand Down
1 change: 0 additions & 1 deletion features/main/operation.feature
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ Feature: Operation support
"""

@createSchema
@dropSchema
Scenario: Select a resource and it's embedded data
Given there are 1 embedded dummy objects
When I send a "GET" request to "/embedded_dummies_groups/1"
Expand Down
2 changes: 0 additions & 2 deletions features/main/relation.feature
Original file line number Diff line number Diff line change
Expand Up @@ -555,7 +555,6 @@ Feature: Relations support
}
"""

@dropSchema
Scenario: Passing an invalid IRI to a relation
When I add "Content-Type" header equal to "application/ld+json"
And I send a "POST" request to "/relation_embedders" with body:
Expand All @@ -569,7 +568,6 @@ Feature: Relations support
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
And the JSON node "hydra:description" should contain "Invalid value provided (invalid IRI?)."

@dropSchema
Scenario: Passing an invalid type to a relation
When I add "Content-Type" header equal to "application/ld+json"
And I send a "POST" request to "/relation_embedders" with body:
Expand Down
1 change: 0 additions & 1 deletion features/main/subresource.feature
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,6 @@ Feature: Subresource support
}
"""

@dropSchema
Scenario: Recursive resource
When I send a "GET" request to "/dummy_products/2"
And the response status code should be 200
Expand Down
3 changes: 0 additions & 3 deletions features/main/validation.feature
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ Feature: Using validations groups
I need to be able to use validation groups

@createSchema
@dropSchema
Scenario: Create a resource
When I add "Content-Type" header equal to "application/ld+json"
And I send a "POST" request to "/dummy_validation" with body:
Expand All @@ -17,7 +16,6 @@ Feature: Using validations groups
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"

@createSchema
@dropSchema
Scenario: Create a resource with validation
When I add "Content-Type" header equal to "application/ld+json"
And I send a "POST" request to "/dummy_validation/validation_groups" with body:
Expand Down Expand Up @@ -46,7 +44,6 @@ Feature: Using validations groups
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"

@createSchema
@dropSchema
Scenario: Create a resource with validation group sequence
When I add "Content-Type" header equal to "application/ld+json"
And I send a "POST" request to "/dummy_validation/validation_sequence" with body:
Expand Down
8 changes: 2 additions & 6 deletions phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ parameters:
- '#Parameter \#1 \$function of function call_user_func expects callable, .+ given\.#'
- '#Parameter \#1 \$classes of class ApiPlatform\\Core\\Metadata\\Resource\\ResourceNameCollection constructor expects array<string>, array<int, int\|string> given\.#'
- '#Method ApiPlatform\\Core\\Util\\RequestParser::parseRequestParams\(\) should return array but returns array\|false\.#'
- '#Parameter \#1 \$vars of class GraphQL\\Language\\AST\\(IntValue|ObjectField|ObjectValue|BooleanValue|ListValue|StringValue)Node constructor expects array<GraphQL\\Language\\AST\\Location\|GraphQL\\Language\\AST\\NameNode\|GraphQL\\Language\\AST\\NodeList\|GraphQL\\Language\\AST\\SelectionSetNode\|string\|null>, array<string, .+> given\.#'
- '#Parameter \#1 \$defaultContext of class Symfony\\Component\\Serializer\\Encoder\\Json(De|En)code constructor expects array, (int|true) given\.#'
# Temporary fix while the PHPStan extension for Prophecy isn't compatible with 0.10
- '#Parameter .* expects .*, .*object.* given\.#'
- '#Parameter \#[0-9] \$filterLocator of class ApiPlatform\\Core\\Bridge\\Doctrine\\Orm\\Extension\\FilterExtension constructor expects ApiPlatform\\Core\\Api\\FilterCollection|Psr\\Container\\ContainerInterface(\|null)?, ArrayObject given\.#'
Expand All @@ -38,9 +40,3 @@ parameters:
- '#Method ApiPlatform\\Core\\Bridge\\Doctrine\\Orm\\Filter\\FilterInterface::apply\(\) invoked with 5 parameters, 3-4 required\.#'
- '#Method ApiPlatform\\Core\\Bridge\\Doctrine\\Orm\\Filter\\OrderFilter::filterProperty\(\) invoked with 7 parameters, 5-6 required\.#'
- '#Method ApiPlatform\\Core\\DataProvider\\CollectionDataProviderInterface::getCollection\(\) invoked with 3 parameters, 1-2 required\.#'

# Expected, due to feature detection, to adapt when Symfony 4.2 will be released
- '#Class Symfony\\Component\\Serializer\\NameConverter\\AdvancedNameConverterInterface not found\.#'
- '#Method Symfony\\Component\\Serializer\\Normalizer\\AbstractObjectNormalizer::__construct\(\) invoked with 6 parameters, 0-4 required\.#'
- '#Parameter \#1 \$bitmask of class Symfony\\Component\\Serializer\\Encoder\\JsonEncode constructor expects int, array<string, int> given\.#'
- '#Parameter \#1 \$associative of class Symfony\\Component\\Serializer\\Encoder\\JsonDecode constructor expects bool, array<string, true> given\.#'
5 changes: 3 additions & 2 deletions src/GraphQl/Action/EntrypointAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

use ApiPlatform\Core\GraphQl\ExecutorInterface;
use ApiPlatform\Core\GraphQl\Type\SchemaBuilderInterface;
use GraphQL\Error\Debug;
use GraphQL\Error\Error;
use GraphQL\Executor\ExecutionResult;
use Symfony\Component\HttpFoundation\JsonResponse;
Expand All @@ -40,7 +41,7 @@ public function __construct(SchemaBuilderInterface $schemaBuilder, ExecutorInter
$this->schemaBuilder = $schemaBuilder;
$this->executor = $executor;
$this->twig = $twig;
$this->debug = $debug;
$this->debug = $debug ? Debug::INCLUDE_DEBUG_MESSAGE | Debug::INCLUDE_TRACE : false;
$this->graphiqlEnabled = $graphiqlEnabled;
$this->title = $title;
}
Expand All @@ -64,7 +65,7 @@ public function __invoke(Request $request): Response
try {
$executionResult = $this->executor->executeQuery($this->schemaBuilder->getSchema(), $query, null, null, $variables, $operation);
} catch (\Exception $e) {
$executionResult = new ExecutionResult(null, [$e] + ($this->debug ? ['trace' => $e->getTraceAsString()] : []));
$executionResult = new ExecutionResult(null, [new Error($e->getMessage(), null, null, null, null, $e)]);
}

return new JsonResponse($executionResult->toArray($this->debug));
Expand Down
8 changes: 4 additions & 4 deletions src/GraphQl/Type/SchemaBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -208,15 +208,15 @@ private function getResourceFieldConfiguration(string $resourceClass, ResourceMe
}

$graphqlWrappedType = $graphqlType instanceof WrappingType ? $graphqlType->getWrappedType() : $graphqlType;
$isInternalGraphqlType = \in_array($graphqlWrappedType, GraphQLType::getInternalTypes(), true);
if ($isInternalGraphqlType) {
$isStandardGraphqlType = \in_array($graphqlWrappedType, GraphQLType::getStandardTypes(), true);
if ($isStandardGraphqlType) {
$className = '';
} else {
$className = $this->isCollection($type) ? $type->getCollectionValueType()->getClassName() : $type->getClassName();
}

$args = [];
if (!$input && null === $mutationName && !$isInternalGraphqlType && $this->isCollection($type)) {
if (!$input && null === $mutationName && !$isStandardGraphqlType && $this->isCollection($type)) {
if ($this->paginationEnabled) {
$args = [
'first' => [
Expand Down Expand Up @@ -258,7 +258,7 @@ private function getResourceFieldConfiguration(string $resourceClass, ResourceMe
$args = $this->convertFilterArgsToTypes($args);
}

if ($isInternalGraphqlType || $input) {
if ($isStandardGraphqlType || $input) {
$resolve = null;
} elseif ($this->isCollection($type)) {
$resolverFactory = $this->collectionResolverFactory;
Expand Down
8 changes: 4 additions & 4 deletions tests/GraphQl/Action/EntrypointActionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ public function testBadContentTypePostAction()
$mockedEntrypoint = $this->getEntrypointAction();

$this->assertEquals(400, $mockedEntrypoint($request)->getStatusCode());
$this->assertEquals('{"errors":[{"message":"GraphQL query is not valid","category":"graphql"}]}', $mockedEntrypoint($request)->getContent());
$this->assertEquals('{"errors":[{"message":"GraphQL query is not valid","extensions":{"category":"graphql"}}]}', $mockedEntrypoint($request)->getContent());
}

public function testBadMethodAction()
Expand All @@ -86,7 +86,7 @@ public function testBadMethodAction()
$mockedEntrypoint = $this->getEntrypointAction();

$this->assertEquals(400, $mockedEntrypoint($request)->getStatusCode());
$this->assertEquals('{"errors":[{"message":"GraphQL query is not valid","category":"graphql"}]}', $mockedEntrypoint($request)->getContent());
$this->assertEquals('{"errors":[{"message":"GraphQL query is not valid","extensions":{"category":"graphql"}}]}', $mockedEntrypoint($request)->getContent());
}

public function testBadVariablesAction()
Expand All @@ -96,7 +96,7 @@ public function testBadVariablesAction()
$mockedEntrypoint = $this->getEntrypointAction();

$this->assertEquals(400, $mockedEntrypoint($request)->getStatusCode());
$this->assertEquals('{"errors":[{"message":"GraphQL variables are not valid JSON","category":"graphql"}]}', $mockedEntrypoint($request)->getContent());
$this->assertEquals('{"errors":[{"message":"GraphQL variables are not valid JSON","extensions":{"category":"graphql"}}]}', $mockedEntrypoint($request)->getContent());
}

private function getEntrypointAction(): EntrypointAction
Expand All @@ -106,7 +106,7 @@ private function getEntrypointAction(): EntrypointAction
$schemaBuilderProphecy->getSchema()->willReturn($schema->reveal());

$executionResultProphecy = $this->prophesize(ExecutionResult::class);
$executionResultProphecy->toArray(true)->willReturn(['GraphQL']);
$executionResultProphecy->toArray(3)->willReturn(['GraphQL']);
$executorProphecy = $this->prophesize(ExecutorInterface::class);
$executorProphecy->executeQuery(Argument::is($schema->reveal()), 'graphqlQuery', null, null, ['graphqlVariable'], 'graphqlOperationName')->willReturn($executionResultProphecy->reveal());

Expand Down
23 changes: 8 additions & 15 deletions tests/GraphQl/Resolver/Factory/CollectionResolverFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@
use ApiPlatform\Core\Metadata\Resource\ResourceMetadata;
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\Dummy;
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\RelatedDummy;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\ResolveInfo;
use GraphQL\Type\Definition\Type;
use GraphQL\Type\Schema;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Symfony\Component\HttpFoundation\Request;
Expand All @@ -44,9 +47,7 @@ public function testCreateCollectionResolverNoCollection(bool $paginationEnabled
$factory = $this->createCollectionResolverFactory([], [], ['id' => 1], $paginationEnabled);
$resolver = $factory(RelatedDummy::class, Dummy::class, 'operationName');

$resolveInfo = new ResolveInfo([]);
$resolveInfo->fieldName = 'relatedDummies';
$resolveInfo->fieldNodes = [];
$resolveInfo = new ResolveInfo('relatedDummies', [], null, new ObjectType(['name' => '']), '', new Schema([]), null, null, null, null);

$this->assertEquals($expected, $resolver(null, [], null, $resolveInfo));
}
Expand All @@ -68,9 +69,7 @@ public function testCreateCollectionResolverNoPagination()

$resolver = $factory(RelatedDummy::class, Dummy::class, 'operationName');

$resolveInfo = new ResolveInfo([]);
$resolveInfo->fieldName = 'rootProperty';
$resolveInfo->fieldNodes = [];
$resolveInfo = new ResolveInfo('rootProperty', [], null, new ObjectType(['name' => '']), '', new Schema([]), null, null, null, null);

$this->assertEquals(['normalizedObject1', 'normalizedObject2'], $resolver(null, [], null, $resolveInfo));
}
Expand All @@ -87,9 +86,7 @@ public function testCreateSubresourceCollectionResolverNoPagination(array $subco

$resolver = $factory(RelatedDummy::class, Dummy::class, 'operationName');

$resolveInfo = new ResolveInfo([]);
$resolveInfo->fieldName = 'relatedDummies';
$resolveInfo->fieldNodes = [];
$resolveInfo = new ResolveInfo('relatedDummies', [], null, new ObjectType(['name' => '']), '', new Schema([]), null, null, null, null);

$dummy = new Dummy();
$dummy->setId(1);
Expand Down Expand Up @@ -122,9 +119,7 @@ public function testCreateCollectionResolver(string $cursor, array $expectedCurs

$resolver = $factory(RelatedDummy::class, Dummy::class, 'operationName');

$resolveInfo = new ResolveInfo([]);
$resolveInfo->fieldName = 'relatedDummies';
$resolveInfo->fieldNodes = [];
$resolveInfo = new ResolveInfo('relatedDummies', [], null, new ObjectType(['name' => '']), '', new Schema([]), null, null, null, null);

if ('$bad$' === $cursor) {
$this->expectException(\Exception::class);
Expand Down Expand Up @@ -167,9 +162,7 @@ public function testCreatePaginatorCollectionResolver()
$resolverFactory = $this->createCollectionResolverFactory($collectionPaginatorProphecy->reveal(), [], [], true, $cursor);
$resolver = $resolverFactory(RelatedDummy::class, Dummy::class, 'operationName');

$resolveInfo = new ResolveInfo([]);
$resolveInfo->fieldName = 'relatedDummies';
$resolveInfo->fieldNodes = [];
$resolveInfo = new ResolveInfo('relatedDummies', [], null, new ObjectType(['name' => '']), '', new Schema([]), null, null, null, null);

$this->assertEquals(
['edges' => [['node' => 'normalizedObject1', 'cursor' => 'Mg==']], 'pageInfo' => ['endCursor' => 'MTY=', 'hasNextPage' => true], 'totalCount' => 17],
Expand Down
Loading