-
Notifications
You must be signed in to change notification settings - Fork 101
/
Copy pathCannotMapTypeException.php
198 lines (166 loc) · 8.11 KB
/
CannotMapTypeException.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
<?php
declare(strict_types=1);
namespace TheCodingMachine\GraphQLite\Mappers;
use Exception;
use GraphQL\Error\Error;
use GraphQL\Type\Definition\InputObjectType;
use GraphQL\Type\Definition\InterfaceType;
use GraphQL\Type\Definition\NamedType;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type;
use phpDocumentor\Reflection\Type as PhpDocumentorType;
use phpDocumentor\Reflection\Types\Array_;
use phpDocumentor\Reflection\Types\Callable_;
use phpDocumentor\Reflection\Types\Iterable_;
use phpDocumentor\Reflection\Types\Mixed_;
use phpDocumentor\Reflection\Types\Object_;
use ReflectionMethod;
use ReflectionProperty;
use TheCodingMachine\GraphQLite\Annotations\ExtendType;
use function array_map;
use function assert;
use function implode;
use function sprintf;
class CannotMapTypeException extends Exception implements CannotMapTypeExceptionInterface
{
use CannotMapTypeTrait;
/** @param class-string<object> $className */
public static function createForType(string $className): self
{
return new self('cannot map class "' . $className . '" to a known GraphQL type. Check your TypeMapper configuration.');
}
/** @param class-string<object> $className */
public static function createForInputType(string $className): self
{
return new self('cannot map class "' . $className . '" to a known GraphQL input type. Are you missing a @Factory annotation? If you have a @Factory annotation, is it in a namespace analyzed by GraphQLite?');
}
public static function createForName(string $name): self
{
return new self('cannot find GraphQL type "' . $name . '". Check your TypeMapper configuration.');
}
public static function createForParseError(Error $error): self
{
return new self($error->getMessage(), $error->getCode(), $error);
}
/** @param class-string<object> $className */
public static function createForMissingIteratorValue(string $className, self $e): self
{
$message = sprintf(
'"%s" is iterable. Please provide a more specific type. For instance: %s|User[].',
$className,
$className,
);
return new self($message, 0, $e);
}
/**
* @param Type[] $unionTypes
*
* @return CannotMapTypeException
*/
public static function createForBadTypeInUnion(array $unionTypes): self
{
$disallowedTypeNames = array_map(static function (Type $type) {
return (string) $type;
}, $unionTypes);
return new self('in GraphQL, you can only use union types between objects. These types cannot be used in union types: ' . implode(', ', $disallowedTypeNames));
}
public static function createForBadTypeInUnionWithIterable(Type $type): self
{
return new self('the value must be iterable, but its computed GraphQL type (' . $type . ') is not a list.');
}
public static function mustBeOutputType(string $subTypeName): self
{
return new self('type "' . $subTypeName . '" must be an output type.');
}
public static function mustBeInputType(string $subTypeName): self
{
return new self('type "' . $subTypeName . '" must be an input type (if you declared an input type with the name "' . $subTypeName . '", make sure that there are no output type with the same name as this is forbidden by the GraphQL spec).');
}
/**
* @param class-string<object> $className
* @param NamedType&(ObjectType|InterfaceType) $type
*
* @return CannotMapTypeException
*/
public static function createForExtendType(string $className, NamedType $type): self
{
return new self('cannot extend GraphQL type "' . $type->name . '" mapped by class "' . $className . '". Check your TypeMapper configuration.');
}
/**
* @param NamedType&(ObjectType|InterfaceType) $type
*
* @return CannotMapTypeException
*/
public static function createForExtendName(string $name, NamedType $type): self
{
return new self('cannot extend GraphQL type "' . $type->name . '" with type "' . $name . '". Check your TypeMapper configuration.');
}
public static function createForDecorateName(string $name, InputObjectType $type): self
{
return new self('cannot decorate GraphQL input type "' . $type->name . '" with type "' . $name . '". Check your TypeMapper configuration.');
}
/** @param class-string<object> $className */
public static function extendTypeWithBadTargetedClass(string $className, ExtendType $extendType): self
{
return new self('For ' . self::extendTypeToString($extendType) . ' annotation declared in class "' . $className . '", the pointed at GraphQL type cannot be extended. You can only target types extending the MutableObjectType (like types created with the @Type annotation).');
}
/** @param Array_|Iterable_|Object_|Mixed_|Callable_ $type */
public static function createForMissingPhpDoc(PhpDocumentorType $type, ReflectionMethod|ReflectionProperty $reflector, string|null $argumentName = null): self
{
$typeStr = '';
if ($type instanceof Array_) {
$typeStr = 'array';
} elseif ($type instanceof Iterable_) {
$typeStr = 'iterable';
} elseif ($type instanceof Object_) {
$typeStr = sprintf('object ("%s")', $type->getFqsen());
} elseif ($type instanceof Mixed_) {
$typeStr = 'mixed';
} elseif ($type instanceof Callable_) {
$typeStr = 'callable';
}
assert($typeStr !== '');
if ($argumentName === null) {
if ($typeStr === 'mixed') {
return new self('a type-hint is missing (or PHPDoc specifies a "mixed" type-hint). Please provide a better type-hint.');
}
return new self(sprintf('please provide an additional @return in the PHPDoc block to further specify the return type of %s. For instance: @return string[]', $typeStr));
}
if ($typeStr === 'mixed') {
return new self(sprintf('a type-hint is missing (or PHPDoc specifies a "mixed" type-hint). Please provide a better type-hint. For instance: "string $%s".', $argumentName));
}
return new self(sprintf('please provide an additional @param in the PHPDoc block to further specify the type of the %s. For instance: @param string[] $%s.', $typeStr, $argumentName));
}
public static function createForDateTime(): self
{
return new self('type-hinting against DateTime is not allowed. Please use the DateTimeImmutable type instead.');
}
public static function createForNull(): self
{
return new self('type-hinting against null only in the PHPDoc is not allowed.');
}
public static function createForInputUnionType(PhpDocumentorType $type): self
{
return new self('parameter is type-hinted to "' . $type . '". Type-hinting a parameter to a union type is forbidden in GraphQL. Only return types can be union types.');
}
public static function createForPhpDocType(PhpDocumentorType $type): self
{
return new self("don't know how to handle type " . (string) $type);
}
public static function createForNonNullReturnByTypeMapper(): self
{
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.');
}
public static function createForUnexpectedCallableParameters(): self
{
return new self('callable() type-hint must not specify any parameters.');
}
public static function createForMissingCallableReturnType(): self
{
return new self('callable() type-hint must specify its return type. For instance: callable(): int');
}
public static function createForCallableAsInput(): self
{
return new self('callable() type-hint can only be used as output type.');
}
}