some functions converted over

This commit is contained in:
Ben Roberts 2017-11-16 13:53:01 -05:00
parent 7aebf2dbf7
commit 3811181f49

View File

@ -5,8 +5,19 @@
namespace GraphQL\Utils; namespace GraphQL\Utils;
use GraphQL\Type\Definition\EnumType;
use GraphQL\Type\Definition\InputObjectType;
use GraphQL\Type\Definition\InterfaceType;
use GraphQL\Type\Definition\ListOfType;
use GraphQL\Type\Definition\NonNull;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\ScalarType;
use GraphQL\Type\Definition\Type;
use GraphQL\Type\Definition\UnionType;
use GraphQL\Type\Schema;
class FindBreakingChanges { class FindBreakingChanges
{
const BREAKING_CHANGE_FIELD_CHANGED = 'FIELD_CHANGED_KIND'; const BREAKING_CHANGE_FIELD_CHANGED = 'FIELD_CHANGED_KIND';
const BREAKING_CHANGE_FIELD_REMOVED = 'FIELD_REMOVED'; const BREAKING_CHANGE_FIELD_REMOVED = 'FIELD_REMOVED';
@ -26,6 +37,24 @@ class FindBreakingChanges {
TYPE_ADDED_TO_UNION: 'TYPE_ADDED_TO_UNION', TYPE_ADDED_TO_UNION: 'TYPE_ADDED_TO_UNION',
};*/ };*/
/**
* Given two schemas, returns an Array containing descriptions of all the types
* of potentially dangerous changes covered by the other functions down below.
*
* @return array
*/
public function findDangerousChanges(
Schema $oldSchema, Schema $newSchema
)
{
return [
/* ...findArgChanges(oldSchema, newSchema).dangerousChanges,
...findValuesAddedToEnums(oldSchema, newSchema),
...findTypesAddedToUnions(oldSchema, newSchema)
*/
];
}
/** /**
* Given two schemas, returns an Array containing descriptions of all the types * Given two schemas, returns an Array containing descriptions of all the types
* of breaking changes covered by the other functions down below. * of breaking changes covered by the other functions down below.
@ -34,7 +63,8 @@ class FindBreakingChanges {
*/ */
public function findBreakingChanges( public function findBreakingChanges(
$oldSchema, $newSchema $oldSchema, $newSchema
) { )
{
return [ return [
/*...findRemovedTypes(oldSchema, newSchema), /*...findRemovedTypes(oldSchema, newSchema),
...findTypesThatChangedKind(oldSchema, newSchema), ...findTypesThatChangedKind(oldSchema, newSchema),
@ -47,43 +77,35 @@ class FindBreakingChanges {
]; ];
} }
/**
* Given two schemas, returns an Array containing descriptions of all the types
* of potentially dangerous changes covered by the other functions down below.
*
* @return array
*/
public function findDangerousChanges(
$oldSchema, $newSchema
) {
return [
/* ...findArgChanges(oldSchema, newSchema).dangerousChanges,
...findValuesAddedToEnums(oldSchema, newSchema),
...findTypesAddedToUnions(oldSchema, newSchema)
*/
];
}
/** /**
* Given two schemas, returns an Array containing descriptions of any breaking * Given two schemas, returns an Array containing descriptions of any breaking
* changes in the newSchema related to removing an entire type. * changes in the newSchema related to removing an entire type.
*/ */
public function findRemovedTypes( public function findRemovedTypes(
$oldSchema, $newSchema Schema $oldSchema, Schema $newSchema
) { )
/*const oldTypeMap = oldSchema.getTypeMap(); {
const newTypeMap = newSchema.getTypeMap(); $oldTypeMap = $oldSchema->getTypeMap();
$newTypeMap = $newSchema->getTypeMap();
const breakingChanges = []; $breakingChanges = [];
Object.keys(oldTypeMap).forEach(typeName => { foreach ($oldTypeMap as $typeName => $typeDefinition) {
if (!isset($newTypeMap[$typeName])) {
$breakingChanges[] =
['type' => self::BREAKING_CHANGE_TYPE_REMOVED, 'description' => "${$typeName} was removed."];
}
}
/*Object.keys(oldTypeMap).forEach(typeName => {
if (!newTypeMap[typeName]) { if (!newTypeMap[typeName]) {
breakingChanges.push({ breakingChanges.push({
type: BreakingChangeType.TYPE_REMOVED, type: BreakingChangeType.TYPE_REMOVED,
description: `${typeName} was removed.`, description: `${typeName} was removed.`,
}); });
} }
}); });*/
return breakingChanges;*/
return $breakingChanges;
} }
/** /**
@ -91,12 +113,29 @@ class FindBreakingChanges {
* changes in the newSchema related to changing the type of a type. * changes in the newSchema related to changing the type of a type.
*/ */
public function findTypesThatChangedKind( public function findTypesThatChangedKind(
$oldSchema, $newSchema Schema $oldSchema, Schema $newSchema
) { )
/*const oldTypeMap = oldSchema.getTypeMap(); {
const newTypeMap = newSchema.getTypeMap(); $oldTypeMap = $oldSchema->getTypeMap();
$newTypeMap = $newSchema->getTypeMap();
const breakingChanges = []; $breakingChanges = [];
foreach ($oldTypeMap as $typeName => $typeDefinition) {
if (!isset($newTypeMap[$typeName])) {
continue;
}
$newTypeDefinition = $newTypeMap[$typeName];
if (!($typeDefinition instanceof $newTypeDefinition)) {
$oldTypeKindName = self::typeKindName($typeDefinition);
$newTypeKindName = self::typeKindName($newTypeDefinition);
$breakingChanges[] = [
'type' => self::BREAKING_CHANGE_TYPE_CHANGED,
'description' => "${$typeName} changed from ${oldTypeKindName} to ${newTypeKindName}."
];
}
}
/*
Object.keys(oldTypeMap).forEach(typeName => { Object.keys(oldTypeMap).forEach(typeName => {
if (!newTypeMap[typeName]) { if (!newTypeMap[typeName]) {
return; return;
@ -110,9 +149,9 @@ class FindBreakingChanges {
`${typeKindName(oldType)} to ${typeKindName(newType)}.` `${typeKindName(oldType)} to ${typeKindName(newType)}.`
}); });
} }
}); });*/
return breakingChanges;
*/ return $breakingChanges;
} }
/** /**
@ -122,14 +161,77 @@ class FindBreakingChanges {
* argument's default value). * argument's default value).
*/ */
public function findArgChanges( public function findArgChanges(
$oldSchema, $newSchema Schema $oldSchema, Schema $newSchema
) { )
/* const oldTypeMap = oldSchema.getTypeMap(); {
const newTypeMap = newSchema.getTypeMap(); $oldTypeMap = $oldSchema->getTypeMap();
$newTypeMap = $newSchema->getTypeMap();
const breakingChanges = []; $breakingChanges = [];
const dangerousChanges = []; $dangerousChanges = [];
foreach ($oldTypeMap as $oldTypeName => $oldTypeDefinition) {
$newTypeDefinition = isset($newTypeMap[$oldTypeName]) ? $newTypeMap[$oldTypeName] : null;
if (!($oldTypeDefinition instanceof ObjectType || $oldTypeDefinition instanceof InterfaceType) ||
!($newTypeDefinition instanceof $oldTypeDefinition)) {
continue;
}
$oldTypeFields = $oldTypeDefinition->getFields();
$newTypeFields = $newTypeDefinition->getFields();
foreach ($oldTypeFields as $fieldName => $fieldDefinition) {
if (!isset($newTypeFields[$fieldName])) {
continue;
}
foreach ($fieldDefinition->args as $oldArgName => $oldArgDef) {
$newArgs = $newTypeFields[$fieldName]->args;
$newArgDef = Utils::find(
$newArgs, function ($arg) use ($oldArgDef) {
return $arg->name === $oldArgDef->name;
}
);
if (!$newArgDef) {
$breakingChanges[] = [
'type' => self::BREAKING_CHANGE_ARG_REMOVED,
'description' => "${$oldTypeName}->${$fieldName} arg ${oldArgName} was removed"
];
} else {
$isSafe = self::isChangeSafeForInputObjectFieldOrFieldArg($oldArgDef->getType(), $newArgDef->getType());
if (!$isSafe) {
$oldArgType = $oldArgDef->getType();
$newArgType = $newArgDef->getType();
$breakingChanges[] = [
'type' => self::BREAKING_CHANGE_ARG_CHANGED,
'description' => "${oldTypeName}->${fieldName} arg ${oldArgName} has changed type from ${oldArgType} to ${newArgType}"
];
} elseif ($oldArgDef->defaultValueExists() && $oldArgDef->defaultValue !== $newArgDef->defaultValue) {
$dangerousChanges[] = []; // TODO
}
}
// Check if a non-null arg was added to the field
foreach ($newTypeFields[$fieldName]->args as $newArgName => $newArgDef) {
$oldArgs = $oldTypeFields[$fieldName]->args;
$oldArgDef = Utils::find(
$oldArgs, function ($arg) use ($newArgDef) {
return $arg->name === $newArgDef->name;
}
);
if (!$oldArgDef && $newArgDef->getType() instanceof NonNull) {
$newTypeName = $newTypeDefinition->name;
$breakingChanges[] = [
'type' => self::BREAKING_CHANGE_NON_NULL_ARG_ADDED,
'description' => "A non-null arg ${$newArgName} on ${newTypeName}->${fieldName} was added."
];
}
}
}
}
}
return ['breakingChanges' => $breakingChanges, 'dangerousChanges' => $dangerousChanges];
/*
Object.keys(oldTypeMap).forEach(typeName => { Object.keys(oldTypeMap).forEach(typeName => {
const oldType = oldTypeMap[typeName]; const oldType = oldTypeMap[typeName];
const newType = newTypeMap[typeName]; const newType = newTypeMap[typeName];
@ -207,26 +309,29 @@ class FindBreakingChanges {
};*/ };*/
} }
private static function typeKindName($type) { /**
/* if (type instanceof GraphQLScalarType) { * @param Type $type
* @return string
*
* @throws \TypeError
*/
private static function typeKindName(Type $type)
{
if ($type instanceof ScalarType) {
return 'a Scalar type'; return 'a Scalar type';
} } elseif($type instanceof ObjectType) {
if (type instanceof GraphQLObjectType) {
return 'an Object type'; return 'an Object type';
} } elseif ($type instanceof InterfaceType) {
if (type instanceof GraphQLInterfaceType) {
return 'an Interface type'; return 'an Interface type';
} } elseif ($type instanceof UnionType) {
if (type instanceof GraphQLUnionType) {
return 'a Union type'; return 'a Union type';
} } elseif ($type instanceof EnumType) {
if (type instanceof GraphQLEnumType) {
return 'an Enum type'; return 'an Enum type';
} } elseif ($type instanceof InputObjectType) {
if (type instanceof GraphQLInputObjectType) {
return 'an Input type'; return 'an Input type';
} }
throw new TypeError('Unknown type ' + type.constructor.name);*/
throw new \TypeError('unknown type ' . $type->name);
} }
/** /**
@ -236,8 +341,9 @@ class FindBreakingChanges {
* a non-null field is added to an input type. * a non-null field is added to an input type.
*/ */
public static function findFieldsThatChangedType( public static function findFieldsThatChangedType(
$oldSchema, $newSchema Schema $oldSchema, Schema $newSchema
) { )
{
/*return [ /*return [
...findFieldsThatChangedTypeOnObjectOrInterfaceTypes(oldSchema, newSchema), ...findFieldsThatChangedTypeOnObjectOrInterfaceTypes(oldSchema, newSchema),
...findFieldsThatChangedTypeOnInputObjectTypes(oldSchema, newSchema), ...findFieldsThatChangedTypeOnInputObjectTypes(oldSchema, newSchema),
@ -246,7 +352,8 @@ class FindBreakingChanges {
private static function findFieldsThatChangedTypeOnObjectOrInterfaceTypes( private static function findFieldsThatChangedTypeOnObjectOrInterfaceTypes(
$oldSchema, $newSchema $oldSchema, $newSchema
) { )
{
/*const oldTypeMap = oldSchema.getTypeMap(); /*const oldTypeMap = oldSchema.getTypeMap();
const newTypeMap = newSchema.getTypeMap(); const newTypeMap = newSchema.getTypeMap();
@ -296,8 +403,9 @@ class FindBreakingChanges {
} }
public static function findFieldsThatChangedTypeOnInputObjectTypes( public static function findFieldsThatChangedTypeOnInputObjectTypes(
$oldSchema, $newSchema Schema $oldSchema, Schema $newSchema
) { )
{
/* const oldTypeMap = oldSchema.getTypeMap(); /* const oldTypeMap = oldSchema.getTypeMap();
const newTypeMap = newSchema.getTypeMap(); const newTypeMap = newSchema.getTypeMap();
@ -360,8 +468,31 @@ class FindBreakingChanges {
} }
private static function isChangeSafeForObjectOrInterfaceField( private static function isChangeSafeForObjectOrInterfaceField(
$oldType, $newType Type $oldType, Type $newType
) { )
{
if (self::isNamedType($oldType)) {
// if they're both named types, see if their names are equivalent
return (self::isNamedType($newType) && $oldType->name === $newType->name)
// moving from nullable to non-null of the same underlying type is safe
|| ($newType instanceof NonNull
&& self::isChangeSafeForObjectOrInterfaceField(
$oldType, $newType->getWrappedType()
));
} elseif ($oldType instanceof ListOfType) {
// if they're both lists, make sure the underlying types are compatible
return ($newType instanceof ListOfType &&
self::isChangeSafeForObjectOrInterfaceField($oldType->getWrappedType(), $newType->getWrappedType())) ||
// moving from nullable to non-null of the same underlying type is safe
($newType instanceof NonNull &&
self::isChangeSafeForObjectOrInterfaceField($oldType, $newType->getWrappedType()));
} elseif ($oldType instanceof NonNull) {
// if they're both non-null, make sure the underlying types are compatible
return $newType instanceof NonNull &&
self::isChangeSafeForObjectOrInterfaceField($oldType->getWrappedType(), $newType->getWrappedType());
}
return false;
/*if (isNamedType(oldType)) { /*if (isNamedType(oldType)) {
return ( return (
// if they're both named types, see if their names are equivalent // if they're both named types, see if their names are equivalent
@ -404,8 +535,9 @@ class FindBreakingChanges {
} }
private static function isChangeSafeForInputObjectFieldOrFieldArg( private static function isChangeSafeForInputObjectFieldOrFieldArg(
$oldType, $newType Schema $oldSchema, Schema $newSchema
) { )
{
/* if (isNamedType(oldType)) { /* if (isNamedType(oldType)) {
// if they're both named types, see if their names are equivalent // if they're both named types, see if their names are equivalent
return isNamedType(newType) && oldType.name === newType.name; return isNamedType(newType) && oldType.name === newType.name;
@ -443,8 +575,9 @@ class FindBreakingChanges {
* changes in the newSchema related to removing types from a union type. * changes in the newSchema related to removing types from a union type.
*/ */
public static function findTypesRemovedFromUnions( public static function findTypesRemovedFromUnions(
$oldSchema, $newSchema Schema $oldSchema, Schema $newSchema
) { )
{
/* const oldTypeMap = oldSchema.getTypeMap(); /* const oldTypeMap = oldSchema.getTypeMap();
const newTypeMap = newSchema.getTypeMap(); const newTypeMap = newSchema.getTypeMap();
@ -477,8 +610,9 @@ class FindBreakingChanges {
* changes in the newSchema related to adding types to a union type. * changes in the newSchema related to adding types to a union type.
*/ */
public static function findTypesAddedToUnions( public static function findTypesAddedToUnions(
$oldSchema, $newSchema Schema $oldSchema, Schema $newSchema
) { )
{
/* const oldTypeMap = oldSchema.getTypeMap(); /* const oldTypeMap = oldSchema.getTypeMap();
const newTypeMap = newSchema.getTypeMap(); const newTypeMap = newSchema.getTypeMap();
@ -511,8 +645,9 @@ class FindBreakingChanges {
* changes in the newSchema related to removing values from an enum type. * changes in the newSchema related to removing values from an enum type.
*/ */
public static function findValuesRemovedFromEnums( public static function findValuesRemovedFromEnums(
$oldSchema, $newSchema Schema $oldSchema, Schema $newSchema
) { )
{
/* const oldTypeMap = oldSchema.getTypeMap(); /* const oldTypeMap = oldSchema.getTypeMap();
const newTypeMap = newSchema.getTypeMap(); const newTypeMap = newSchema.getTypeMap();
@ -545,8 +680,9 @@ class FindBreakingChanges {
* changes in the newSchema related to adding values to an enum type. * changes in the newSchema related to adding values to an enum type.
*/ */
public static function findValuesAddedToEnums( public static function findValuesAddedToEnums(
$oldSchema, $newSchema Schema $oldSchema, Schema $newSchema
) { )
{
/* const oldTypeMap = oldSchema.getTypeMap(); /* const oldTypeMap = oldSchema.getTypeMap();
const newTypeMap = newSchema.getTypeMap(); const newTypeMap = newSchema.getTypeMap();
@ -576,8 +712,9 @@ class FindBreakingChanges {
} }
public static function findInterfacesRemovedFromObjectTypes( public static function findInterfacesRemovedFromObjectTypes(
$oldSchema, $newSchema Schema $oldSchema, Schema $newSchema
) { )
{
/* const oldTypeMap = oldSchema.getTypeMap(); /* const oldTypeMap = oldSchema.getTypeMap();
const newTypeMap = newSchema.getTypeMap(); const newTypeMap = newSchema.getTypeMap();
const breakingChanges = []; const breakingChanges = [];
@ -606,4 +743,21 @@ class FindBreakingChanges {
}); });
return breakingChanges;*/ return breakingChanges;*/
} }
/**
* @param Type $type
*
* @return bool
*/
private static function isNamedType(Type $type)
{
return (
$type instanceof ScalarType ||
$type instanceof ObjectType ||
$type instanceof InterfaceType ||
$type instanceof UnionType ||
$type instanceof EnumType ||
$type instanceof InputObjectType
);
}
} }