Skip to content

Commit e076b38

Browse files
committed
- update to add pre-coercion of OneOf errors, following this pending PR to graphql-js: graphql/graphql-js#4195
1 parent 3cc0c71 commit e076b38

File tree

2 files changed

+70
-2
lines changed

2 files changed

+70
-2
lines changed

src/Utils/Value.php

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ public static function coerceInputValue($value, InputType $type, ?array $path =
8888
$coercedItem = self::coerceInputValue(
8989
$itemValue,
9090
$itemType,
91-
[...$path ?? [], $index]
91+
$path === null ? [$index] : [...$path, $index]
9292
);
9393

9494
if (isset($coercedItem['errors'])) {
@@ -132,7 +132,7 @@ public static function coerceInputValue($value, InputType $type, ?array $path =
132132
$coercedField = self::coerceInputValue(
133133
$fieldValue,
134134
$field->getType(),
135-
[...$path ?? [], $fieldName],
135+
$path === null ? [$fieldName] : [...$path, $fieldName],
136136
);
137137

138138
if (isset($coercedField['errors'])) {
@@ -171,6 +171,38 @@ public static function coerceInputValue($value, InputType $type, ?array $path =
171171
);
172172
}
173173

174+
// Validate OneOf constraints if this is a OneOf input type
175+
if ($type->isOneOf()) {
176+
$providedFieldCount = 0;
177+
$nullFieldName = null;
178+
179+
foreach ($coercedValue as $fieldName => $fieldValue) {
180+
if ($fieldValue !== null) {
181+
$providedFieldCount++;
182+
} else {
183+
$nullFieldName = $fieldName;
184+
}
185+
}
186+
187+
// Check for null field values first (takes precedence)
188+
if ($nullFieldName !== null) {
189+
$errors = self::add(
190+
$errors,
191+
CoercionError::make("OneOf input object \"{$type->name}\" field \"{$nullFieldName}\" must be non-null.", $path, $value)
192+
);
193+
} elseif ($providedFieldCount === 0) {
194+
$errors = self::add(
195+
$errors,
196+
CoercionError::make("OneOf input object \"{$type->name}\" must specify exactly one field.", $path, $value)
197+
);
198+
} elseif ($providedFieldCount > 1) {
199+
$errors = self::add(
200+
$errors,
201+
CoercionError::make("OneOf input object \"{$type->name}\" must specify exactly one field.", $path, $value)
202+
);
203+
}
204+
}
205+
174206
return $errors === []
175207
? self::ofValue($type->parseValue($coercedValue))
176208
: self::ofErrors($errors);

tests/Type/OneOfInputObjectTest.php

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use GraphQL\Type\Definition\ObjectType;
99
use GraphQL\Type\Definition\Type;
1010
use GraphQL\Type\Schema;
11+
use GraphQL\Utils\Value;
1112
use PHPUnit\Framework\TestCase;
1213

1314
class OneOfInputObjectTest extends TestCase
@@ -198,4 +199,39 @@ public function testOneOfIntrospection(): void
198199
self::assertTrue($oneOfType['isOneOf']);
199200
self::assertFalse($regularType['isOneOf']); // Should be false for regular input objects
200201
}
202+
203+
public function testOneOfCoercionValidation(): void
204+
{
205+
$oneOfType = new InputObjectType([
206+
'name' => 'OneOfInput',
207+
'fields' => [
208+
'stringField' => Type::string(),
209+
'intField' => Type::int(),
210+
],
211+
'isOneOf' => true,
212+
]);
213+
214+
// Test valid input (exactly one field)
215+
$validResult = Value::coerceInputValue(['stringField' => 'test'], $oneOfType);
216+
$this->assertNull($validResult['errors']);
217+
$this->assertEquals(['stringField' => 'test'], $validResult['value']);
218+
219+
// Test invalid input (no fields)
220+
$noFieldsResult = Value::coerceInputValue([], $oneOfType);
221+
$this->assertNotNull($noFieldsResult['errors']);
222+
$this->assertCount(1, $noFieldsResult['errors']);
223+
$this->assertEquals('OneOf input object "OneOfInput" must specify exactly one field.', $noFieldsResult['errors'][0]->getMessage());
224+
225+
// Test invalid input (multiple fields)
226+
$multipleFieldsResult = Value::coerceInputValue(['stringField' => 'test', 'intField' => 42], $oneOfType);
227+
$this->assertNotNull($multipleFieldsResult['errors']);
228+
$this->assertCount(1, $multipleFieldsResult['errors']);
229+
$this->assertEquals('OneOf input object "OneOfInput" must specify exactly one field.', $multipleFieldsResult['errors'][0]->getMessage());
230+
231+
// Test invalid input (null field value)
232+
$nullFieldResult = Value::coerceInputValue(['stringField' => null], $oneOfType);
233+
$this->assertNotNull($nullFieldResult['errors']);
234+
$this->assertCount(1, $nullFieldResult['errors']);
235+
$this->assertEquals('OneOf input object "OneOfInput" field "stringField" must be non-null.', $nullFieldResult['errors'][0]->getMessage());
236+
}
201237
}

0 commit comments

Comments
 (0)