Skip to content

Commit e5cc0f9

Browse files
committed
Making it possible to have nullable compound types
1 parent 39682ac commit e5cc0f9

File tree

7 files changed

+58
-3
lines changed

7 files changed

+58
-3
lines changed

src/Mappers/CannotMapTypeException.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,4 +177,9 @@ public static function createForPhpDocType(PhpDocumentorType $type): self
177177
{
178178
return new self("don't know how to handle type " . (string) $type);
179179
}
180+
181+
public static function createForNonNullReturnByTypeMapper(): self
182+
{
183+
return new self('a type mapper returned a GraphQL\Type\Definition\NonNull instance. All instances returned by type mappers should be nullable. It is the role of the NullableTypeMapperAdapter class to make a GraphQL type in a "NonNull". Note: this is an error in the TypeMapper code or in GraphQLite itself. Please check your custom type mappers or open an issue on GitHub if you don\'t have any custom type mapper.');
184+
}
180185
}

src/Mappers/Root/CompoundTypeMapper.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,9 @@ private function getTypeFromUnion(array $unionTypes): GraphQLType
138138
if (count($unionTypes) === 1) {
139139
$graphQlType = $unionTypes[0];
140140
Assert::isInstanceOf($graphQlType, NonNull::class);
141+
// If we have only one type, let's make it nullable (it is the role of the NullableTypeMapperAdapter to make it non nullable)
142+
$graphQlType = $graphQlType->getWrappedType();
143+
assert($graphQlType instanceof OutputType);
141144
} else {
142145
$badTypes = [];
143146
$nonNullableUnionTypes = [];

src/Mappers/Root/IteratorTypeMapper.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ public function toGraphQLOutputType(Type $type, ?OutputType $subType, Reflection
7878
return $this->next->toGraphQLOutputType($type, $subType, $refMethod, $docBlockObj);
7979
}
8080
Assert::isInstanceOf($result, OutputType::class);
81+
Assert::notInstanceOf($result, NonNull::class);
8182

8283
return $result;
8384
}
@@ -99,7 +100,11 @@ public function toGraphQLInputType(Type $type, ?InputType $subType, string $argu
99100
}
100101

101102
$result = $this->toGraphQLType($type, function (Type $type, ?InputType $subType) use ($refMethod, $docBlockObj, $argumentName) {
102-
return $this->topRootTypeMapper->toGraphQLInputType($type, $subType, $argumentName, $refMethod, $docBlockObj);
103+
$topType = $this->topRootTypeMapper->toGraphQLInputType($type, $subType, $argumentName, $refMethod, $docBlockObj);
104+
if ($topType instanceof NonNull) {
105+
$topType = $topType->getWrappedType();
106+
}
107+
return $topType;
103108
}, false);
104109
if ($result === null) {
105110
return $this->next->toGraphQLInputType($type, $subType, $argumentName, $refMethod, $docBlockObj);
@@ -197,6 +202,9 @@ private function toGraphQLType(Compound $type, Closure $topToGraphQLType, bool $
197202

198203
if (count($unionTypes) === 1) {
199204
$graphQlType = $unionTypes[0];
205+
if ($graphQlType instanceof NonNull) {
206+
$graphQlType = $graphQlType->getWrappedType();
207+
}
200208
/*} elseif ($isOutputType) {
201209
// This clearly cannot work. We are only gathering types from arrays and we cannot join arrays (I think)
202210
$graphQlType = new UnionType($unionTypes, $this->recursiveTypeMapper);

src/Mappers/Root/NullableTypeMapperAdapter.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
use GraphQL\Type\Definition\InputType;
88
use GraphQL\Type\Definition\NamedType;
9+
use GraphQL\Type\Definition\NonNull;
910
use GraphQL\Type\Definition\NullableType;
1011
use GraphQL\Type\Definition\OutputType;
1112
use GraphQL\Type\Definition\Type as GraphQLType;
@@ -57,6 +58,10 @@ public function toGraphQLOutputType(Type $type, ?OutputType $subType, Reflection
5758

5859
$graphQlType = $this->next->toGraphQLOutputType($type, $subType, $refMethod, $docBlockObj);
5960

61+
if ($graphQlType instanceof NonNull) {
62+
throw CannotMapTypeException::createForNonNullReturnByTypeMapper();
63+
}
64+
6065
if (! $isNullable && $graphQlType instanceof NullableType) {
6166
$graphQlType = GraphQLType::nonNull($graphQlType);
6267
}

tests/Integration/EndToEndTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1568,7 +1568,7 @@ public function testNullableResult(){
15681568
if (isset($resultArray['errors']) || !isset($resultArray['data'])) {
15691569
$this->fail('Expected a successful answer. Got '.json_encode($resultArray, JSON_PRETTY_PRINT));
15701570
}
1571-
$this->assertNull($resultArray['data']['getNullableResult']);
1571+
$this->assertNull($resultArray['data']['nullableResult']);
15721572
}
15731573

15741574
}

tests/Mappers/Root/CompoundTypeMapperTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public function testException3()
4646
$compoundTypeMapper = $this->getRootTypeMapper();
4747

4848
$this->expectException(CannotMapTypeException::class);
49-
$this->expectExceptionMessage('the value must be iterable, but its computed GraphQL type (String!) is not a list.');
49+
$this->expectExceptionMessage('the value must be iterable, but its computed GraphQL type (String) is not a list.');
5050
$compoundTypeMapper->toGraphQLOutputType(new Compound([new Iterable_(), new String_()]), null, new ReflectionMethod(__CLASS__, 'testException1'), new DocBlock());
5151
}
5252
}

tests/Mappers/Root/NullableTypeMapperAdapterTest.php

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,14 @@
22

33
namespace TheCodingMachine\GraphQLite\Mappers\Root;
44

5+
use GraphQL\Type\Definition\InputType;
6+
use GraphQL\Type\Definition\NamedType;
57
use GraphQL\Type\Definition\NonNull;
8+
use GraphQL\Type\Definition\OutputType;
9+
use GraphQL\Type\Definition\StringType;
10+
use GraphQL\Type\Definition\Type as GraphQLType;
611
use phpDocumentor\Reflection\DocBlock;
12+
use phpDocumentor\Reflection\Type;
713
use ReflectionMethod;
814
use TheCodingMachine\GraphQLite\AbstractQueryProviderTest;
915
use TheCodingMachine\GraphQLite\Fixtures\TestObject;
@@ -37,4 +43,32 @@ public function testOnlyNull2(): void
3743
$this->expectExceptionMessage('type-hinting against null only in the PHPDoc is not allowed.');
3844
$compoundTypeMapper->toGraphQLInputType($this->resolveType('null'), null, 'foo', new ReflectionMethod(__CLASS__, 'testMultipleCompound'), new DocBlock());
3945
}
46+
47+
public function testNonNullableReturnedByWrappedMapper(): void
48+
{
49+
$typeMapper = new NullableTypeMapperAdapter();
50+
51+
$typeMapper->setNext(new class implements RootTypeMapperInterface {
52+
53+
public function toGraphQLOutputType(Type $type, ?OutputType $subType, ReflectionMethod $refMethod, DocBlock $docBlockObj): OutputType
54+
{
55+
return new NonNull(new StringType());
56+
}
57+
58+
public function toGraphQLInputType(Type $type, ?InputType $subType, string $argumentName, ReflectionMethod $refMethod, DocBlock $docBlockObj): InputType
59+
{
60+
throw new \RuntimeException('Not implemented');
61+
}
62+
63+
public function mapNameToType(string $typeName): NamedType
64+
{
65+
throw new \RuntimeException('Not implemented');
66+
}
67+
});
68+
69+
70+
$this->expectException(CannotMapTypeException::class);
71+
$this->expectExceptionMessage('a type mapper returned a GraphQL\\Type\\Definition\\NonNull instance.');
72+
$typeMapper->toGraphQLOutputType($this->resolveType(TestObject::class.'|'.TestObject2::class.'|null'), null, new ReflectionMethod(__CLASS__, 'testMultipleCompound'), new DocBlock());
73+
}
4074
}

0 commit comments

Comments
 (0)