Skip to content

Commit 8c97363

Browse files
authored
fix: RecursiveTypeMapper returns exception which can't be handled by StandardServer (#646)
* fix: RecursiveTypeMapper returns exception which can't be handled by StandardServer RecursiveTypeMapper throws CannotMapTypeException when fails to find class by name. This exception goes through the GraphQl executor instead of catching and wrapping into ExecutionResult. Non wrapped exception does not trigger HttpCodeDecider. Schema test trust more to input than configuration which is not optimal, great possibility to improve configuration validation. Fixes: #336 * feat: new special Exception class when request type unknown Fixes: #336 * fix: move asserts Fixes: #336 * polish changes Fixes: #336
1 parent 6f31b08 commit 8c97363

File tree

4 files changed

+70
-30
lines changed

4 files changed

+70
-30
lines changed

src/Mappers/RecursiveTypeMapper.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,6 @@ public function mapNameToType(string $typeName): Type&NamedType
521521
return $this->mapClassToInterfaceOrType($className, null);
522522
}
523523

524-
throw CannotMapTypeException::createForName($typeName);
524+
throw TypeNotFoundException::createError($typeName);
525525
}
526526
}

src/Mappers/TypeNotFoundException.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace TheCodingMachine\GraphQLite\Mappers;
6+
7+
use GraphQL\Error\Error;
8+
use GraphQL\Utils\Utils;
9+
10+
/**
11+
* TypeNotFoundException thrown when RecursiveTypeMapper fails to find a type.
12+
*
13+
* While CannotMapTypeException is more about error with configuration this exception can occur when request
14+
* contains a type which is unknown to the server.
15+
* Should not be handled by the user, webonyx/graphql-php will transform this under 'errors' key in the response.
16+
*/
17+
class TypeNotFoundException extends Error
18+
{
19+
public static function createError(string $typeName): Error
20+
{
21+
return static::createLocatedError('Unknown type ' . Utils::printSafe($typeName));
22+
}
23+
}

tests/Mappers/RecursiveTypeMapperTest.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ public function testMapNameToType2(): void
9393
$this->assertSame('ClassA', $recursiveMapper->mapNameToType('ClassA')->name);
9494
$this->assertSame('ClassAInterface', $recursiveMapper->mapNameToType('ClassAInterface')->name);
9595

96-
$this->expectException(CannotMapTypeException::class);
96+
$this->expectException(TypeNotFoundException::class);
9797
$recursiveMapper->mapNameToType('NotExists');
9898
}
9999

@@ -233,6 +233,8 @@ public function testDuplicateDetection(): void
233233

234234
/**
235235
* Tests that the RecursiveTypeMapper behaves correctly if there are no types to map.
236+
*
237+
* @see \GraphQL\Server\Helper::promiseToExecuteOperation()
236238
*/
237239
public function testMapNoTypes(): void
238240
{
@@ -244,7 +246,7 @@ public function testMapNoTypes(): void
244246
$this->getAnnotationReader()
245247
);
246248

247-
$this->expectException(CannotMapTypeException::class);
249+
$this->expectException(TypeNotFoundException::class);
248250
$recursiveTypeMapper->mapNameToType('Foo');
249251
}
250252

tests/SchemaFactoryTest.php

Lines changed: 42 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
<?php
22

3+
declare(strict_types=1);
4+
35
namespace TheCodingMachine\GraphQLite;
46

7+
use Doctrine\Common\Annotations\AnnotationReader;
58
use GraphQL\Error\DebugFlag;
9+
use GraphQL\Executor\ExecutionResult;
610
use GraphQL\GraphQL;
711
use GraphQL\Type\SchemaConfig;
812
use Mouf\Composer\ClassNameMapper;
@@ -20,22 +24,19 @@
2024
use TheCodingMachine\GraphQLite\Fixtures\Integration\Types\ContactOtherType;
2125
use TheCodingMachine\GraphQLite\Fixtures\Integration\Types\ContactType;
2226
use TheCodingMachine\GraphQLite\Fixtures\Integration\Types\ExtendedContactType;
23-
use TheCodingMachine\GraphQLite\Mappers\CannotMapTypeException;
27+
use TheCodingMachine\GraphQLite\Fixtures\TestSelfType;
2428
use TheCodingMachine\GraphQLite\Mappers\CompositeTypeMapper;
2529
use TheCodingMachine\GraphQLite\Mappers\DuplicateMappingException;
30+
use TheCodingMachine\GraphQLite\Mappers\Parameters\ParameterMiddlewarePipe;
2631
use TheCodingMachine\GraphQLite\Mappers\Root\VoidRootTypeMapperFactory;
2732
use TheCodingMachine\GraphQLite\Mappers\StaticClassListTypeMapperFactory;
2833
use TheCodingMachine\GraphQLite\Middlewares\FieldMiddlewarePipe;
29-
use TheCodingMachine\GraphQLite\Mappers\Parameters\ParameterMiddlewarePipe;
3034
use TheCodingMachine\GraphQLite\Middlewares\InputFieldMiddlewarePipe;
3135
use TheCodingMachine\GraphQLite\Security\VoidAuthenticationService;
3236
use TheCodingMachine\GraphQLite\Security\VoidAuthorizationService;
33-
use TheCodingMachine\GraphQLite\Fixtures\TestSelfType;
34-
3537

3638
class SchemaFactoryTest extends TestCase
3739
{
38-
3940
public function testCreateSchema(): void
4041
{
4142
$container = new BasicAutoWiringContainer(new EmptyContainer());
@@ -65,7 +66,7 @@ public function testSetters(): void
6566

6667
$factory->addControllerNamespace('TheCodingMachine\\GraphQLite\\Fixtures\\Integration\\Controllers');
6768
$factory->addTypeNamespace('TheCodingMachine\\GraphQLite\\Fixtures\\Integration');
68-
$factory->setDoctrineAnnotationReader(new \Doctrine\Common\Annotations\AnnotationReader())
69+
$factory->setDoctrineAnnotationReader(new AnnotationReader())
6970
->setAuthenticationService(new VoidAuthenticationService())
7071
->setAuthorizationService(new VoidAuthorizationService())
7172
->setNamingStrategy(new NamingStrategy())
@@ -89,8 +90,8 @@ public function testClassNameMapperInjectionWithValidMapper(): void
8990
$factory = new SchemaFactory(
9091
new Psr16Cache(new ArrayAdapter()),
9192
new BasicAutoWiringContainer(
92-
new EmptyContainer()
93-
)
93+
new EmptyContainer(),
94+
),
9495
);
9596
$factory->setAuthenticationService(new VoidAuthenticationService())
9697
->setAuthorizationService(new VoidAuthorizationService())
@@ -132,17 +133,16 @@ public function testClassNameMapperInjectionWithInvalidMapper(): void
132133
$factory = new SchemaFactory(
133134
new Psr16Cache(new ArrayAdapter()),
134135
new BasicAutoWiringContainer(
135-
new EmptyContainer()
136-
)
136+
new EmptyContainer(),
137+
),
137138
);
138139
$factory->setAuthenticationService(new VoidAuthenticationService())
139140
->setAuthorizationService(new VoidAuthorizationService())
140141
->setClassNameMapper(new ClassNameMapper())
141142
->addControllerNamespace('TheCodingMachine\\GraphQLite\\Fixtures\\Integration\\Controllers')
142143
->addTypeNamespace('TheCodingMachine\\GraphQLite\\Fixtures\\Integration');
143144

144-
$this->expectException(CannotMapTypeException::class);
145-
$this->doTestSchema($factory->createSchema());
145+
$this->doTestSchemaWithError($factory->createSchema());
146146
}
147147

148148
public function testException(): void
@@ -168,9 +168,8 @@ public function testException2(): void
168168
$factory->createSchema();
169169
}
170170

171-
private function doTestSchema(Schema $schema): void
171+
private function execTestQuery(Schema $schema): ExecutionResult
172172
{
173-
174173
$schema->assertValid();
175174

176175
$queryString = '
@@ -185,34 +184,50 @@ private function doTestSchema(Schema $schema): void
185184
}
186185
';
187186

188-
$result = GraphQL::executeQuery(
187+
return GraphQL::executeQuery(
189188
$schema,
190-
$queryString
189+
$queryString,
191190
);
191+
}
192+
193+
private function doTestSchemaWithError(Schema $schema): void
194+
{
195+
$result = $this->execTestQuery($schema);
196+
$resultArr = $result->toArray(DebugFlag::RETHROW_INTERNAL_EXCEPTIONS);
197+
$this->assertArrayHasKey('errors', $resultArr);
198+
$this->assertArrayNotHasKey('data', $resultArr);
199+
$this->assertCount(1, $resultArr);
200+
$this->assertSame('Unknown type "User"', $resultArr['errors'][0]['message']);
201+
}
192202

203+
private function doTestSchema(Schema $schema): void
204+
{
205+
$result = $this->execTestQuery($schema);
206+
$resultArr = $result->toArray(DebugFlag::RETHROW_INTERNAL_EXCEPTIONS);
207+
$this->assertArrayHasKey('data', $resultArr);
193208
$this->assertSame([
194209
'contacts' => [
195210
[
196211
'name' => 'Joe',
197-
'uppercaseName' => 'JOE'
212+
'uppercaseName' => 'JOE',
198213
],
199214
[
200215
'name' => 'Bill',
201216
'uppercaseName' => 'BILL',
202-
'email' => '[email protected]'
203-
]
217+
'email' => '[email protected]',
218+
],
204219

205-
]
206-
], $result->toArray(DebugFlag::RETHROW_INTERNAL_EXCEPTIONS)['data']);
220+
],
221+
], $resultArr['data']);
207222
}
208223

209224
public function testDuplicateQueryException(): void
210225
{
211226
$factory = new SchemaFactory(
212227
new Psr16Cache(new ArrayAdapter()),
213228
new BasicAutoWiringContainer(
214-
new EmptyContainer()
215-
)
229+
new EmptyContainer(),
230+
),
216231
);
217232
$factory->setAuthenticationService(new VoidAuthenticationService())
218233
->setAuthorizationService(new VoidAuthorizationService())
@@ -229,7 +244,7 @@ public function testDuplicateQueryException(): void
229244
';
230245
GraphQL::executeQuery(
231246
$schema,
232-
$queryString
247+
$queryString,
233248
);
234249
}
235250

@@ -238,8 +253,8 @@ public function testDuplicateQueryInTwoControllersException(): void
238253
$factory = new SchemaFactory(
239254
new Psr16Cache(new ArrayAdapter()),
240255
new BasicAutoWiringContainer(
241-
new EmptyContainer()
242-
)
256+
new EmptyContainer(),
257+
),
243258
);
244259
$factory->setAuthenticationService(new VoidAuthenticationService())
245260
->setAuthorizationService(new VoidAuthorizationService())
@@ -256,7 +271,7 @@ public function testDuplicateQueryInTwoControllersException(): void
256271
';
257272
GraphQL::executeQuery(
258273
$schema,
259-
$queryString
274+
$queryString,
260275
);
261276
}
262277
}

0 commit comments

Comments
 (0)