diff --git a/.travis.yml b/.travis.yml index ec67289fd..9cc22ccc5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,7 @@ env: global: - COMPOSER_ROOT_VERSION="4.x-dev" - REQUIRE_RECIPE="4.x-dev" - - REQUIRE_EXTRA="silverstripe/recipe-cms:4.x-dev silverstripe/frameworktest:0.3.0" + - REQUIRE_EXTRA="silverstripe/recipe-cms:4.x-dev silverstripe/frameworktest:^0.4" - BEHAT_SUITE="asset-admin --config vendor/silverstripe/asset-admin/behat.yml" matrix: diff --git a/src/Schema/DataObject/CreateCreator.php b/src/Schema/DataObject/CreateCreator.php index e36566784..b579520b9 100644 --- a/src/Schema/DataObject/CreateCreator.php +++ b/src/Schema/DataObject/CreateCreator.php @@ -11,6 +11,7 @@ use SilverStripe\GraphQL\QueryHandler\SchemaConfigProvider; use SilverStripe\GraphQL\QueryHandler\UserContextProvider; use SilverStripe\GraphQL\Schema\Exception\SchemaBuilderException; +use SilverStripe\GraphQL\Schema\Field\ModelField; use SilverStripe\GraphQL\Schema\Field\ModelMutation; use SilverStripe\GraphQL\Schema\Interfaces\ModelOperation; use SilverStripe\GraphQL\Schema\Schema; @@ -22,6 +23,7 @@ use Closure; use SilverStripe\GraphQL\Schema\Type\ModelType; use SilverStripe\ORM\DataObject; +use SilverStripe\ORM\FieldType\DBEnum; /** * Creates a "create" mutation for a DataObject @@ -135,7 +137,16 @@ public function provideInputTypes(ModelType $modelType, array $config = []): arr continue; } $type = $fieldObj->getNamedType(); - if ($type && Schema::isInternalType($type)) { + if (!$type) { + continue; + } + $isScalar = Schema::isInternalType($type); + if (!$isScalar && $fieldObj instanceof ModelField) { + $dataClass = $fieldObj->getMetadata()->get('dataClass'); + $isScalar = $dataClass === DBEnum::class || is_subclass_of($dataClass, DBEnum::class); + } + + if ($isScalar) { $fieldMap[$fieldName] = $type; } } diff --git a/src/Schema/DataObject/UpdateCreator.php b/src/Schema/DataObject/UpdateCreator.php index 00df83dcf..13370738b 100644 --- a/src/Schema/DataObject/UpdateCreator.php +++ b/src/Schema/DataObject/UpdateCreator.php @@ -11,6 +11,7 @@ use SilverStripe\GraphQL\QueryHandler\SchemaConfigProvider; use SilverStripe\GraphQL\QueryHandler\UserContextProvider; use SilverStripe\GraphQL\Schema\Exception\SchemaBuilderException; +use SilverStripe\GraphQL\Schema\Field\ModelField; use SilverStripe\GraphQL\Schema\Field\ModelMutation; use SilverStripe\GraphQL\Schema\Interfaces\ModelOperation; use SilverStripe\GraphQL\Schema\Schema; @@ -23,6 +24,7 @@ use SilverStripe\GraphQL\Schema\Type\ModelType; use SilverStripe\ORM\DataList; use Closure; +use SilverStripe\ORM\FieldType\DBEnum; /** * Creates an update operation for a DataObject @@ -153,8 +155,16 @@ public function provideInputTypes(ModelType $modelType, array $config = []): arr continue; } $type = $fieldObj->getNamedType(); + if (!$type) { + continue; + } + $isScalar = Schema::isInternalType($type); + if (!$isScalar && $fieldObj instanceof ModelField) { + $dataClass = $fieldObj->getMetadata()->get('dataClass'); + $isScalar = $dataClass === DBEnum::class || is_subclass_of($dataClass, DBEnum::class); + } // No nested input types... yet - if ($type && Schema::isInternalType($type)) { + if ($isScalar) { $fieldMap[$fieldName] = $type; } } diff --git a/src/Schema/Plugin/AbstractQueryFilterPlugin.php b/src/Schema/Plugin/AbstractQueryFilterPlugin.php index 90ba4756f..d775d2360 100644 --- a/src/Schema/Plugin/AbstractQueryFilterPlugin.php +++ b/src/Schema/Plugin/AbstractQueryFilterPlugin.php @@ -56,7 +56,11 @@ public static function updateSchema(Schema $schema): void if (empty($filters)) { return; } - foreach (Schema::getInternalTypes() as $typeName) { + $scalarTypes = Schema::getInternalTypes(); + foreach ($schema->getEnums() as $enum) { + $scalarTypes[] = $enum->getName(); + } + foreach ($scalarTypes as $typeName) { $type = InputType::create(static::getLeafNodeType($typeName)); foreach ($filters as $id => $filterInstance) { if ($filterInstance instanceof ListFieldFilterInterface) { diff --git a/src/Schema/Schema.php b/src/Schema/Schema.php index 63ea5fd83..a60d2810b 100644 --- a/src/Schema/Schema.php +++ b/src/Schema/Schema.php @@ -1159,17 +1159,22 @@ public static function assertValidConfig(array $config, $allowedKeys = [], $requ { static::invariant( empty($config) || ArrayLib::is_associative($config), - '%s configurations must be key value pairs of names to configurations. - Did you include an indexed array in your config?', - static::class + "%s configurations must be key value pairs of names to configurations. + Did you include an indexed array in your config? + + Context: %s", + static::class, + json_encode($config) ); if (!empty($allowedKeys)) { $invalidKeys = array_diff(array_keys($config), $allowedKeys); static::invariant( empty($invalidKeys), - 'Config contains invalid keys: %s', - implode(',', $invalidKeys) + "Config contains invalid keys: %s. Allowed keys are %s.\n\nContext: %s", + implode(',', $invalidKeys), + implode(',', $allowedKeys), + json_encode($config) ); } @@ -1177,8 +1182,9 @@ public static function assertValidConfig(array $config, $allowedKeys = [], $requ $missingKeys = array_diff($requiredKeys, array_keys($config)); static::invariant( empty($missingKeys), - 'Config is missing required keys: %s', - implode(',', $missingKeys) + "Config is missing required keys: %s.\n\nContext: %s", + implode(',', $missingKeys), + json_encode($config) ); } } diff --git a/src/Schema/Services/NestedInputBuilder.php b/src/Schema/Services/NestedInputBuilder.php index b902fd2be..c28a187ba 100644 --- a/src/Schema/Services/NestedInputBuilder.php +++ b/src/Schema/Services/NestedInputBuilder.php @@ -211,9 +211,11 @@ protected function addInputTypesToSchema( $fieldType = $fieldObj->getNamedType(); $nestedType = $this->schema->getCanonicalType($fieldType); + $isScalar = (bool) Schema::isInternalType($fieldType) || $this->schema->getEnum($fieldType); + if ($data === self::SELF_REFERENTIAL) { $inputType->addField($fieldName, $inputType->getName()); - } elseif (!is_array($data) && !$nestedType && Schema::isInternalType($fieldType)) { + } elseif (!is_array($data) && !$nestedType && $isScalar) { // Regular field, e.g. scalar $inputType->addField( $fieldName, diff --git a/src/Schema/Type/ModelType.php b/src/Schema/Type/ModelType.php index 887cc638f..7d3adfbdb 100644 --- a/src/Schema/Type/ModelType.php +++ b/src/Schema/Type/ModelType.php @@ -89,7 +89,8 @@ public function applyConfig(array $config) if (isset($fields[Schema::ALL])) { $all = $fields[Schema::ALL]; unset($fields[Schema::ALL]); - $fields = array_merge([ + $fields = array_merge( + [ Schema::ALL => $all, ], $fields diff --git a/tests/Fake/FakeProduct.php b/tests/Fake/FakeProduct.php index 6f46af5b6..cda61a9e4 100644 --- a/tests/Fake/FakeProduct.php +++ b/tests/Fake/FakeProduct.php @@ -18,6 +18,7 @@ class FakeProduct extends DataObject implements TestOnly private static $db = [ 'Title' => 'Varchar', 'Price' => 'Int', + 'Status' => "Enum('AVAILABLE, UNAVAILABLE')", ]; private static $has_one = [ diff --git a/tests/Schema/Services/NestedInputBuilderTest.php b/tests/Schema/Services/NestedInputBuilderTest.php index b716d4412..cd738631a 100644 --- a/tests/Schema/Services/NestedInputBuilderTest.php +++ b/tests/Schema/Services/NestedInputBuilderTest.php @@ -4,6 +4,7 @@ use SilverStripe\Dev\SapphireTest; use SilverStripe\GraphQL\Dev\BuildState; +use SilverStripe\GraphQL\Schema\DataObject\Plugin\DBFieldTypes; use SilverStripe\GraphQL\Schema\Exception\SchemaBuilderException; use SilverStripe\GraphQL\Schema\Field\Query; use SilverStripe\GraphQL\Schema\Schema; @@ -39,10 +40,12 @@ public function testNestedInputBuilder() $model->addField('products'); $model->addAllOperations(); }) - ->addModelbyClassName(FakeProduct::class, function (ModelType $model) { + ->addModelbyClassName(FakeProduct::class, function (ModelType $model) use ($schema) { $model->addField('title'); $model->addField('reviews'); + $model->addField('status'); $model->addField('relatedProducts'); + (new DBFieldTypes())->apply($model, $schema); }) ->addModelbyClassName(FakeReview::class, function (ModelType $model) { $model->addField('content'); @@ -67,6 +70,7 @@ public function testNestedInputBuilder() 'title' => 'String', 'reviews' => 'FakeReviewInputType', 'relatedProducts' => 'FakeProductInputType', + 'status' => 'StatusEnum', ], 'FakeReviewInputType' => [ 'id' => 'ID', @@ -135,7 +139,7 @@ public function testNestedInputBuilderBuildsRepeatedRelationsFilter() $this->assertNotNull($featuredFilter, "Field featuredProducts not found on {$filterType->getName()}"); $this->assertEquals('FakeProductFilterFields', $featuredFilter->getType()); } - + private function assertSchema(array $graph, Schema $schema) { foreach ($graph as $typeName => $fields) {