Better error messages for config validation

This commit is contained in:
vladar 2015-08-30 13:41:41 +06:00
parent ee2ce29899
commit 5241c8a5d3
3 changed files with 22 additions and 15 deletions

View File

@ -41,7 +41,8 @@ class Config
public static function validate(array $config, array $definition)
{
if (self::$enableValidation) {
self::_validateMap($config, $definition);
$name = isset($config['name']) ? $config['name'] : 'UnnamedType';
self::_validateMap($name, $config, $definition);
}
}
@ -73,29 +74,29 @@ class Config
return $tmp;
}
private static function _validateMap(array $map, array $definitions, $pathStr = null)
private static function _validateMap($typeName, array $map, array $definitions, $pathStr = null)
{
$suffix = $pathStr ? " at $pathStr" : '';
// Make sure there are no unexpected keys in map
$unexpectedKeys = array_keys(array_diff_key($map, $definitions));
Utils::invariant(empty($unexpectedKeys), 'Unexpected keys "%s" ' . $suffix, implode(', ', $unexpectedKeys));
Utils::invariant(empty($unexpectedKeys), 'Error in "'.$typeName.'" type definition: Unexpected keys "%s" ' . $suffix, implode(', ', $unexpectedKeys));
// Make sure that all required keys are present in map
$requiredKeys = array_filter($definitions, function($def) {return (self::_getFlags($def) & self::REQUIRED) > 0;});
$missingKeys = array_keys(array_diff_key($requiredKeys, $map));
Utils::invariant(empty($missingKeys), 'Required keys missing: "%s"' . $suffix, implode(', ', $missingKeys));
Utils::invariant(empty($missingKeys), 'Error in "'.$typeName.'" type definition: Required keys missing: "%s"' . $suffix, implode(', ', $missingKeys));
// Make sure that every map value is valid given the definition
foreach ($map as $key => $value) {
self::_validateEntry($key, $value, $definitions[$key], $pathStr ? "$pathStr:$key" : $key);
self::_validateEntry($typeName, $key, $value, $definitions[$key], $pathStr ? "$pathStr:$key" : $key);
}
}
private static function _validateEntry($key, $value, $def, $pathStr)
private static function _validateEntry($typeName, $key, $value, $def, $pathStr)
{
$type = Utils::getVariableType($value);
$err = 'Expecting %s at "' . $pathStr . '", but got "' . $type . '"';
$err = 'Error in "'.$typeName.'" type definition: expecting %s at "' . $pathStr . '", but got "' . $type . '"';
if ($def instanceof \stdClass) {
if ($def->flags & self::REQUIRED === 0 && $value === null) {
@ -107,14 +108,14 @@ class Config
if ($def->flags & self::KEY_AS_NAME) {
$value += ['name' => $key];
}
self::_validateMap($value, $def->definition, $pathStr);
self::_validateMap($typeName, $value, $def->definition, $pathStr);
} else if (!empty($def->isArray)) {
if ($def->flags & self::REQUIRED) {
Utils::invariant(!empty($value), "Value at '$pathStr' cannot be empty array");
Utils::invariant(!empty($value), 'Error in "'.$typeName.'" type definition: ' . "Value at '$pathStr' cannot be empty array");
}
$err = "Each entry at '$pathStr' must be an array, but '%s' is '%s'";
$err = 'Error in "'.$typeName.'" type definition:' . "Each entry at '$pathStr' must be an array, but '%s' is '%s'";
foreach ($value as $arrKey => $arrValue) {
if (is_array($def->definition)) {
@ -123,21 +124,25 @@ class Config
if ($def->flags & self::KEY_AS_NAME) {
$arrValue += ['name' => $arrKey];
}
self::_validateMap($arrValue, $def->definition, "$pathStr:$arrKey");
self::_validateMap($typeName, $arrValue, $def->definition, "$pathStr:$arrKey");
} else {
self::_validateEntry($arrKey, $arrValue, $def->definition, "$pathStr:$arrKey");
self::_validateEntry($typeName, $arrKey, $arrValue, $def->definition, "$pathStr:$arrKey");
}
}
} else {
throw new \Exception("Unexpected definition: " . print_r($def, true));
throw new \Exception('Error in "'.$typeName.'" type definition:' . "unexpected definition: " . print_r($def, true));
}
} else {
Utils::invariant(is_int($def), "Definition for '$pathStr' is expected to be single integer value");
Utils::invariant(is_int($def), 'Error in "'.$typeName.'" type definition:' . "Definition for '$pathStr' is expected to be single integer value");
if ($def & self::REQUIRED) {
Utils::invariant($value !== null, 'Value at "%s" can not be null', $pathStr);
}
if (null === $value) {
return ; // Allow nulls for non-required fields
}
switch (true) {
case $def & self::ANY:
break;

View File

@ -68,6 +68,8 @@ class ObjectType extends Type implements OutputType, CompositeType
public function __construct(array $config)
{
Utils::invariant(!empty($config['name']), 'Every type is expected to have name');
$this->name = $config['name'];
$this->description = isset($config['description']) ? $config['description'] : null;
$this->_config = $config;

View File

@ -335,7 +335,7 @@ class DefinitionTest extends \PHPUnit_Framework_TestCase
$this->fail('Expected exception not thrown');
} catch (\Exception $e) {
$this->assertSame(
'Expecting callable or instance of GraphQL\Type\Definition\ObjectType at "types:0", but got "' . get_class($type) . '"',
'Error in "BadUnion" type definition: expecting callable or instance of GraphQL\Type\Definition\ObjectType at "types:0", but got "' . get_class($type) . '"',
$e->getMessage()
);
}