diff --git a/src/GraphQL/Execution/ExecutionResult.php b/src/GraphQL/Execution/ExecutionResult.php index ad17b6817..ceec543ad 100644 --- a/src/GraphQL/Execution/ExecutionResult.php +++ b/src/GraphQL/Execution/ExecutionResult.php @@ -9,4 +9,16 @@ class ExecutionResult extends LibraryExecutionResult implements CacheableDependencyInterface { use RefinableCacheableDependencyTrait; + /** + * PHP Serialization: skip some class members when serializing during tests. + */ + public function __sleep(): array { + // PHPUnit error: Fatal error: Uncaught Exception: Serialization of + // 'Closure' is not allowed. + // Remove some closure-containing members before serializing. + $vars = get_object_vars($this); + unset($vars['extensions']); + return array_keys($vars); + } + } diff --git a/src/Plugin/GraphQL/DataProducer/EntityDefinition/Fields.php b/src/Plugin/GraphQL/DataProducer/EntityDefinition/Fields.php index 24ebdb2b8..a9341c10f 100644 --- a/src/Plugin/GraphQL/DataProducer/EntityDefinition/Fields.php +++ b/src/Plugin/GraphQL/DataProducer/EntityDefinition/Fields.php @@ -2,7 +2,7 @@ namespace Drupal\graphql\Plugin\GraphQL\DataProducer\EntityDefinition; -use Drupal\Core\Entity\ContentEntityType; +use Drupal\Core\Entity\ContentEntityTypeInterface; use Drupal\Core\Entity\EntityFieldManager; use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Entity\EntityTypeManager; @@ -121,24 +121,22 @@ public function resolve( ?array $field_types_context = NULL, FieldContext $field_context ): \Iterator { - $entity_definition->getBundleEntityType(); - if ($entity_definition instanceof ContentEntityType) { + + if ($entity_definition instanceof ContentEntityTypeInterface) { + $entity_type_id = $entity_definition->id(); if ($bundle_context) { $key = $bundle_context['key']; - $id = $entity_definition->id(); - $fields = $this->entityFieldManager->getFieldDefinitions($id, $key); + $fields = $this->entityFieldManager->getFieldDefinitions($entity_type_id, $key); // Set entity form default display as context. - $entity_id = $id . '.' . $key . '.default'; - /** @var \Drupal\Core\Config\Entity\ConfigEntityStorage $form_display_context */ + $form_display_id = $entity_type_id . '.' . $key . '.default'; $form_display_context = $this->entityTypeManager ->getStorage('entity_form_display') - ->load($entity_id); + ->load($form_display_id); $field_context->setContextValue('entity_form_display', $form_display_context); } else { - $id = $entity_definition->id(); - $fields = $this->entityFieldManager->getFieldDefinitions($id, $id); + $fields = $this->entityFieldManager->getFieldDefinitions($entity_type_id, $entity_type_id); } if ($field_types_context) { diff --git a/src/Plugin/GraphQL/DataProducer/EntityDefinition/Fields/Multiple.php b/src/Plugin/GraphQL/DataProducer/EntityDefinition/Fields/Multiple.php index c82252118..edf16a212 100644 --- a/src/Plugin/GraphQL/DataProducer/EntityDefinition/Fields/Multiple.php +++ b/src/Plugin/GraphQL/DataProducer/EntityDefinition/Fields/Multiple.php @@ -3,6 +3,7 @@ namespace Drupal\graphql\Plugin\GraphQL\DataProducer\EntityDefinition\Fields; use Drupal\Core\Field\FieldDefinitionInterface; +use Drupal\Core\Field\FieldStorageDefinitionInterface; use Drupal\graphql\Plugin\GraphQL\DataProducer\DataProducerPluginBase; /** @@ -34,6 +35,9 @@ class Multiple extends DataProducerPluginBase { * If the field contains multiple values or just single value. */ public function resolve(FieldDefinitionInterface $entity_definition_field): bool { + if ($entity_definition_field instanceof FieldStorageDefinitionInterface) { + return $entity_definition_field->isMultiple(); + } return $entity_definition_field->isList(); } diff --git a/src/Plugin/GraphQL/DataProducer/EntityDefinition/Fields/Reference.php b/src/Plugin/GraphQL/DataProducer/EntityDefinition/Fields/Reference.php index fe3c11f2e..4723ea4c7 100644 --- a/src/Plugin/GraphQL/DataProducer/EntityDefinition/Fields/Reference.php +++ b/src/Plugin/GraphQL/DataProducer/EntityDefinition/Fields/Reference.php @@ -2,10 +2,7 @@ namespace Drupal\graphql\Plugin\GraphQL\DataProducer\EntityDefinition\Fields; -use Drupal\Core\Field\BaseFieldDefinition; -use Drupal\Core\Field\Entity\BaseFieldOverride; use Drupal\Core\Field\FieldDefinitionInterface; -use Drupal\field\Entity\FieldConfig; use Drupal\graphql\Plugin\GraphQL\DataProducer\DataProducerPluginBase; /** @@ -37,36 +34,7 @@ class Reference extends DataProducerPluginBase { * If the field is referencing entities (is the entity reference type). */ public function resolve(FieldDefinitionInterface $entity_definition_field): bool { - if ($entity_definition_field instanceof BaseFieldDefinition) { - /** @var \Drupal\Core\Field\BaseFieldDefinition $entity_definition_field */ - if ($entity_definition_field->getType() === 'entity_reference') { - return TRUE; - } - else { - return FALSE; - } - } - elseif ($entity_definition_field instanceof FieldConfig) { - /** @var \Drupal\field\Entity\FieldConfig $entity_definition_field */ - if ($entity_definition_field->getType() === 'entity_reference') { - return TRUE; - } - else { - return FALSE; - } - } - elseif ($entity_definition_field instanceof BaseFieldOverride) { - /** @var \Drupal\field\Entity\FieldConfig $entity_definition_field */ - if ($entity_definition_field->getType() === 'entity_reference') { - return TRUE; - } - else { - return FALSE; - } - } - else { - return FALSE; - } + return $entity_definition_field->getType() === 'entity_reference'; } } diff --git a/tests/src/Kernel/DataProducer/EntityDefinitionTest.php b/tests/src/Kernel/DataProducer/EntityDefinitionTest.php new file mode 100644 index 000000000..d73d8e61e --- /dev/null +++ b/tests/src/Kernel/DataProducer/EntityDefinitionTest.php @@ -0,0 +1,529 @@ + 'article', + 'name' => 'article', + ]); + $content_type->save(); + + // Create a form display. + $form_display = EntityFormDisplay::create([ + 'targetEntityType' => 'node', + 'bundle' => 'article', + 'mode' => 'default', + ]); + $form_display->save(); + + $schema = <<setUpSchema($schema); + + $registry = $this->registry; + $builder = new ResolverBuilder(); + + // Entity definition query. + $registry->addFieldResolver('Query', 'entityDefinition', + $builder->produce('entity_definition', [ + 'entity_type' => $builder->fromArgument('entity_type'), + 'bundle' => $builder->fromArgument('bundle'), + 'field_types' => $builder->fromArgument('field_types'), + ]) + ); + // Entity definition fields. + $registry->addFieldResolver('EntityDefinition', 'label', + $builder->produce('entity_definition_label', [ + 'entity_definition' => $builder->fromParent(), + ]) + ); + $registry->addFieldResolver('EntityDefinition', 'fields', + $builder->produce('entity_definition_fields', [ + 'entity_definition' => $builder->fromParent(), + 'bundle_context' => $builder->fromContext('bundle'), + 'field_types_context' => $builder->fromContext('field_types'), + ]) + ); + $registry->addFieldResolver('EntityDefinitionField', 'id', + $builder->produce('entity_definition_field_id', [ + 'entity_definition_field' => $builder->fromParent(), + ]) + ); + $registry->addFieldResolver('EntityDefinitionField', 'label', + $builder->produce('entity_definition_field_label', [ + 'entity_definition_field' => $builder->fromParent(), + ]) + ); + $registry->addFieldResolver('EntityDefinitionField', 'description', + $builder->produce('entity_definition_field_description', [ + 'entity_definition_field' => $builder->fromParent(), + ]) + ); + $registry->addFieldResolver('EntityDefinitionField', 'type', + $builder->produce('entity_definition_field_type', [ + 'entity_definition_field' => $builder->fromParent(), + ]) + ); + $registry->addFieldResolver('EntityDefinitionField', 'required', + $builder->produce('entity_definition_field_required', [ + 'entity_definition_field' => $builder->fromParent(), + ]) + ); + $registry->addFieldResolver('EntityDefinitionField', 'multiple', + $builder->produce('entity_definition_field_multiple', [ + 'entity_definition_field' => $builder->fromParent(), + ]) + ); + $registry->addFieldResolver('EntityDefinitionField', 'maxNumItems', + $builder->produce('entity_definition_field_max_num_items', [ + 'entity_definition_field' => $builder->fromParent(), + ]) + ); + $registry->addFieldResolver('EntityDefinitionField', 'status', + $builder->produce('entity_definition_field_status', [ + 'entity_definition_field' => $builder->fromParent(), + ]) + ); + $registry->addFieldResolver('EntityDefinitionField', 'defaultValue', + $builder->produce('entity_definition_field_default_value', [ + 'entity_definition_field' => $builder->fromParent(), + ]) + ); + $registry->addFieldResolver('EntityDefinitionField', 'defaultValues', + $builder->produce('entity_definition_field_additional_default_value', [ + 'entity_definition_field' => $builder->fromParent(), + ]) + ); + $registry->addFieldResolver('EntityDefinitionField', 'isReference', + $builder->produce('entity_definition_field_reference', [ + 'entity_definition_field' => $builder->fromParent(), + ]) + ); + $registry->addFieldResolver('EntityDefinitionField', 'isHidden', + $builder->produce('entity_definition_field_hidden', [ + 'entity_definition_field' => $builder->fromParent(), + 'entity_form_display_context' => $builder->fromContext('entity_form_display'), + ]) + ); + $registry->addFieldResolver('EntityDefinitionField', 'weight', + $builder->produce('entity_definition_field_weight', [ + 'entity_definition_field' => $builder->fromParent(), + 'entity_form_display_context' => $builder->fromContext('entity_form_display'), + ]) + ); + $registry->addFieldResolver('EntityDefinitionField', 'settings', + $builder->produce('translatable_entity_definition_field_settings', [ + 'entity_definition_field' => $builder->fromParent(), + 'entity_form_display_context' => $builder->fromContext('entity_form_display'), + ]) + ); + } + + /** + * Tests that retrieving an entity definition works. + */ + public function testEntityDefinition() { + $query = <<assertResults($query, [], [ + 'entityDefinition' => + [ + 'label' => 'Content', + 'fields' => + [ + 0 => + [ + 'id' => 'nid', + 'label' => 'ID', + 'description' => NULL, + 'type' => 'integer', + 'required' => FALSE, + 'multiple' => FALSE, + 'maxNumItems' => 1, + 'status' => TRUE, + 'defaultValue' => NULL, + 'isReference' => FALSE, + 'isHidden' => FALSE, + 'weight' => 0, + 'settings' => NULL, + ], + 1 => + [ + 'id' => 'uuid', + 'label' => 'UUID', + 'description' => NULL, + 'type' => 'uuid', + 'required' => FALSE, + 'multiple' => FALSE, + 'maxNumItems' => 1, + 'status' => TRUE, + 'defaultValue' => NULL, + 'isReference' => FALSE, + 'isHidden' => FALSE, + 'weight' => 0, + 'settings' => NULL, + ], + 2 => + [ + 'id' => 'vid', + 'label' => 'Revision ID', + 'description' => NULL, + 'type' => 'integer', + 'required' => FALSE, + 'multiple' => FALSE, + 'maxNumItems' => 1, + 'status' => TRUE, + 'defaultValue' => NULL, + 'isReference' => FALSE, + 'isHidden' => FALSE, + 'weight' => 0, + 'settings' => NULL, + ], + 3 => + [ + 'id' => 'langcode', + 'label' => 'Language', + 'description' => NULL, + 'type' => 'language', + 'required' => FALSE, + 'multiple' => FALSE, + 'maxNumItems' => 1, + 'status' => TRUE, + 'defaultValue' => NULL, + 'isReference' => FALSE, + 'isHidden' => FALSE, + 'weight' => 2, + 'settings' => NULL, + ], + 4 => + [ + 'id' => 'type', + 'label' => 'Content type', + 'description' => NULL, + 'type' => 'entity_reference', + 'required' => TRUE, + 'multiple' => FALSE, + 'maxNumItems' => 1, + 'status' => TRUE, + 'defaultValue' => NULL, + 'isReference' => TRUE, + 'isHidden' => FALSE, + 'weight' => 0, + 'settings' => NULL, + ], + 5 => + [ + 'id' => 'revision_timestamp', + 'label' => 'Revision create time', + 'description' => 'The time that the current revision was created.', + 'type' => 'created', + 'required' => FALSE, + 'multiple' => FALSE, + 'maxNumItems' => 1, + 'status' => TRUE, + 'defaultValue' => NULL, + 'isReference' => FALSE, + 'isHidden' => FALSE, + 'weight' => 0, + 'settings' => NULL, + ], + 6 => + [ + 'id' => 'revision_uid', + 'label' => 'Revision user', + 'description' => 'The user ID of the author of the current revision.', + 'type' => 'entity_reference', + 'required' => FALSE, + 'multiple' => FALSE, + 'maxNumItems' => 1, + 'status' => TRUE, + 'defaultValue' => NULL, + 'isReference' => TRUE, + 'isHidden' => FALSE, + 'weight' => 0, + 'settings' => NULL, + ], + 7 => + [ + 'id' => 'revision_log', + 'label' => 'Revision log message', + 'description' => 'Briefly describe the changes you have made.', + 'type' => 'string_long', + 'required' => FALSE, + 'multiple' => FALSE, + 'maxNumItems' => 1, + 'status' => TRUE, + 'defaultValue' => NULL, + 'isReference' => FALSE, + 'isHidden' => FALSE, + 'weight' => 25, + 'settings' => NULL, + ], + 8 => + [ + 'id' => 'status', + 'label' => 'Published', + 'description' => NULL, + 'type' => 'boolean', + 'required' => FALSE, + 'multiple' => FALSE, + 'maxNumItems' => 1, + 'status' => TRUE, + 'defaultValue' => '1', + 'isReference' => FALSE, + 'isHidden' => FALSE, + 'weight' => 120, + 'settings' => NULL, + ], + 9 => + [ + 'id' => 'uid', + 'label' => 'Authored by', + 'description' => 'The username of the content author.', + 'type' => 'entity_reference', + 'required' => FALSE, + 'multiple' => FALSE, + 'maxNumItems' => 1, + 'status' => TRUE, + 'defaultValue' => NULL, + 'isReference' => TRUE, + 'isHidden' => FALSE, + 'weight' => 5, + 'settings' => NULL, + ], + 10 => + [ + 'id' => 'title', + 'label' => 'Title', + 'description' => NULL, + 'type' => 'string', + 'required' => TRUE, + 'multiple' => FALSE, + 'maxNumItems' => 1, + 'status' => TRUE, + 'defaultValue' => NULL, + 'isReference' => FALSE, + 'isHidden' => FALSE, + 'weight' => -5, + 'settings' => NULL, + ], + 11 => + [ + 'id' => 'created', + 'label' => 'Authored on', + 'description' => 'The time that the node was created.', + 'type' => 'created', + 'required' => FALSE, + 'multiple' => FALSE, + 'maxNumItems' => 1, + 'status' => TRUE, + 'defaultValue' => NULL, + 'isReference' => FALSE, + 'isHidden' => FALSE, + 'weight' => 10, + 'settings' => NULL, + ], + 12 => + [ + 'id' => 'changed', + 'label' => 'Changed', + 'description' => 'The time that the node was last edited.', + 'type' => 'changed', + 'required' => FALSE, + 'multiple' => FALSE, + 'maxNumItems' => 1, + 'status' => TRUE, + 'defaultValue' => NULL, + 'isReference' => FALSE, + 'isHidden' => FALSE, + 'weight' => 0, + 'settings' => NULL, + ], + 13 => + [ + 'id' => 'promote', + 'label' => 'Promoted to front page', + 'description' => NULL, + 'type' => 'boolean', + 'required' => FALSE, + 'multiple' => FALSE, + 'maxNumItems' => 1, + 'status' => TRUE, + 'defaultValue' => '1', + 'isReference' => FALSE, + 'isHidden' => FALSE, + 'weight' => 15, + 'settings' => NULL, + ], + 14 => + [ + 'id' => 'sticky', + 'label' => 'Sticky at top of lists', + 'description' => NULL, + 'type' => 'boolean', + 'required' => FALSE, + 'multiple' => FALSE, + 'maxNumItems' => 1, + 'status' => TRUE, + 'defaultValue' => '', + 'isReference' => FALSE, + 'isHidden' => FALSE, + 'weight' => 16, + 'settings' => NULL, + ], + 15 => + [ + 'id' => 'default_langcode', + 'label' => 'Default translation', + 'description' => 'A flag indicating whether this is the default translation.', + 'type' => 'boolean', + 'required' => FALSE, + 'multiple' => FALSE, + 'maxNumItems' => 1, + 'status' => TRUE, + 'defaultValue' => '1', + 'isReference' => FALSE, + 'isHidden' => FALSE, + 'weight' => 0, + 'settings' => NULL, + ], + 16 => + [ + 'id' => 'revision_default', + 'label' => 'Default revision', + 'description' => 'A flag indicating whether this was a default revision when it was saved.', + 'type' => 'boolean', + 'required' => FALSE, + 'multiple' => FALSE, + 'maxNumItems' => 1, + 'status' => TRUE, + 'defaultValue' => '', + 'isReference' => FALSE, + 'isHidden' => FALSE, + 'weight' => 0, + 'settings' => NULL, + ], + 17 => + [ + 'id' => 'revision_translation_affected', + 'label' => 'Revision translation affected', + 'description' => 'Indicates if the last edit of a translation belongs to current revision.', + 'type' => 'boolean', + 'required' => FALSE, + 'multiple' => FALSE, + 'maxNumItems' => 1, + 'status' => TRUE, + 'defaultValue' => '', + 'isReference' => FALSE, + 'isHidden' => FALSE, + 'weight' => 0, + 'settings' => NULL, + ], + ], + ], + ] + ); + } + + /** + * {@inheritdoc} + */ + protected function defaultCacheMaxAge() { + // @todo this is wrong, we should have a maximum of caching for entity + // definitions, not 0. + return 0; + } + + /** + * {@inheritdoc} + */ + protected function defaultCacheContexts() { + return ['languages:language_interface', 'user.permissions']; + } + + /** + * {@inheritdoc} + */ + protected function defaultCacheTags() { + $tags = parent::defaultCacheTags(); + $tags[] = 'config:core.entity_form_display.node.article.default'; + return $tags; + } + +}