summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLee Rowlands2017-11-06 09:14:37 +1000
committerLee Rowlands2017-11-06 09:14:37 +1000
commitc74c39e13327809f442a0b309b7a995da8a9c6fa (patch)
treee1a432f5dbf88aca88e23ec50e6c2a227e1576f7
parentf2c1ef4f168acda23ec09d9af0cd7d7fe24440cc (diff)
Issue #2871591 by tedbow, Wim Leers, dagmar, borisson_, amateescu, dawehner, damiankloip, tstoeckler, mradcliffe, larowlan: Allow ComplexData in TypedData to specify computed properties that should be exposed in normalization and other contexts
-rw-r--r--core/lib/Drupal/Core/Field/BaseFieldDefinition.php8
-rw-r--r--core/lib/Drupal/Core/Field/FieldConfigBase.php11
-rw-r--r--core/lib/Drupal/Core/TypedData/DataDefinition.php24
-rw-r--r--core/lib/Drupal/Core/TypedData/DataDefinitionInterface.php11
-rw-r--r--core/lib/Drupal/Core/TypedData/TypedDataInternalPropertiesHelper.php25
-rw-r--r--core/modules/hal/src/Normalizer/ContentEntityNormalizer.php10
-rw-r--r--core/modules/hal/src/Normalizer/FieldItemNormalizer.php3
-rw-r--r--core/modules/hal/tests/src/Functional/EntityResource/EntityTest/EntityTestHalJsonInternalPropertyNormalizerTest.php85
-rw-r--r--core/modules/rest/tests/src/Functional/EntityResource/EntityTest/EntityTestJsonInternalPropertyNormalizerTest.php87
-rw-r--r--core/modules/rest/tests/src/Functional/EntityResource/EntityTest/EntityTestResourceTestBase.php11
-rw-r--r--core/modules/serialization/src/Normalizer/ComplexDataNormalizer.php10
-rw-r--r--core/modules/serialization/src/Normalizer/ContentEntityNormalizer.php5
-rw-r--r--core/modules/serialization/tests/src/Unit/Normalizer/ComplexDataNormalizerTest.php125
-rw-r--r--core/modules/serialization/tests/src/Unit/Normalizer/ContentEntityNormalizerTest.php53
-rw-r--r--core/modules/serialization/tests/src/Unit/Normalizer/EntityReferenceFieldItemNormalizerTest.php10
-rw-r--r--core/modules/serialization/tests/src/Unit/Normalizer/InternalTypedDataTestTrait.php36
-rw-r--r--core/modules/serialization/tests/src/Unit/Normalizer/TimestampItemNormalizerTest.php16
-rw-r--r--core/modules/system/tests/modules/entity_test/config/schema/entity_test.data_types.schema.yml5
-rw-r--r--core/modules/system/tests/modules/entity_test/entity_test.module5
-rw-r--r--core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/InternalPropertyTestFieldItem.php45
-rw-r--r--core/modules/system/tests/modules/entity_test/src/TypedData/ComputedString.php30
21 files changed, 513 insertions, 102 deletions
diff --git a/core/lib/Drupal/Core/Field/BaseFieldDefinition.php b/core/lib/Drupal/Core/Field/BaseFieldDefinition.php
index 991385f..b13a164 100644
--- a/core/lib/Drupal/Core/Field/BaseFieldDefinition.php
+++ b/core/lib/Drupal/Core/Field/BaseFieldDefinition.php
@@ -855,4 +855,12 @@ class BaseFieldDefinition extends ListDataDefinition implements FieldDefinitionI
$this->propertyDefinitions = NULL;
}
+ /**
+ * {@inheritdoc}
+ */
+ public function isInternal() {
+ // All fields are not internal unless explicitly set.
+ return !empty($this->definition['internal']);
+ }
+
}
diff --git a/core/lib/Drupal/Core/Field/FieldConfigBase.php b/core/lib/Drupal/Core/Field/FieldConfigBase.php
index e53e84b..a32ff42 100644
--- a/core/lib/Drupal/Core/Field/FieldConfigBase.php
+++ b/core/lib/Drupal/Core/Field/FieldConfigBase.php
@@ -591,4 +591,15 @@ abstract class FieldConfigBase extends ConfigEntityBase implements FieldConfigIn
return $this;
}
+ /**
+ * {@inheritdoc}
+ */
+ public function isInternal() {
+ // Respect the definition, otherwise default to TRUE for computed fields.
+ if (isset($this->definition['internal'])) {
+ return $this->definition['internal'];
+ }
+ return $this->isComputed();
+ }
+
}
diff --git a/core/lib/Drupal/Core/TypedData/DataDefinition.php b/core/lib/Drupal/Core/TypedData/DataDefinition.php
index 52a4394..332659f 100644
--- a/core/lib/Drupal/Core/TypedData/DataDefinition.php
+++ b/core/lib/Drupal/Core/TypedData/DataDefinition.php
@@ -352,4 +352,28 @@ class DataDefinition implements DataDefinitionInterface, \ArrayAccess {
return array_keys($vars);
}
+ /**
+ * {@inheritdoc}
+ */
+ public function isInternal() {
+ // Respect the definition, otherwise default to TRUE for computed fields.
+ if (isset($this->definition['internal'])) {
+ return $this->definition['internal'];
+ }
+ return $this->isComputed();
+ }
+
+ /**
+ * Sets the whether the data value should be internal.
+ *
+ * @param bool $internal
+ * Whether the data value should be internal.
+ *
+ * @return $this
+ */
+ public function setInternal($internal) {
+ $this->definition['internal'] = $internal;
+ return $this;
+ }
+
}
diff --git a/core/lib/Drupal/Core/TypedData/DataDefinitionInterface.php b/core/lib/Drupal/Core/TypedData/DataDefinitionInterface.php
index 64d779c..d12ccfd 100644
--- a/core/lib/Drupal/Core/TypedData/DataDefinitionInterface.php
+++ b/core/lib/Drupal/Core/TypedData/DataDefinitionInterface.php
@@ -218,4 +218,15 @@ interface DataDefinitionInterface {
*/
public function addConstraint($constraint_name, $options = NULL);
+ /**
+ * Determines whether the data value is internal.
+ *
+ * This can be used in a scenario when it is not desirable to expose this data
+ * value to an external system.
+ *
+ * @return bool
+ * Whether the data value is internal.
+ */
+ public function isInternal();
+
}
diff --git a/core/lib/Drupal/Core/TypedData/TypedDataInternalPropertiesHelper.php b/core/lib/Drupal/Core/TypedData/TypedDataInternalPropertiesHelper.php
new file mode 100644
index 0000000..a9a59d7
--- /dev/null
+++ b/core/lib/Drupal/Core/TypedData/TypedDataInternalPropertiesHelper.php
@@ -0,0 +1,25 @@
+<?php
+
+namespace Drupal\Core\TypedData;
+
+/**
+ * Helper class for internal properties.
+ */
+class TypedDataInternalPropertiesHelper {
+
+ /**
+ * Gets an array non-internal properties from a complex data object.
+ *
+ * @param \Drupal\Core\TypedData\ComplexDataInterface $data
+ * The complex data object.
+ *
+ * @return \Drupal\Core\TypedData\TypedDataInterface[]
+ * The non-internal properties, keyed by property name.
+ */
+ public static function getNonInternalProperties(ComplexDataInterface $data) {
+ return array_filter($data->getProperties(TRUE), function (TypedDataInterface $property) {
+ return !$property->getDataDefinition()->isInternal();
+ });
+ }
+
+}
diff --git a/core/modules/hal/src/Normalizer/ContentEntityNormalizer.php b/core/modules/hal/src/Normalizer/ContentEntityNormalizer.php
index eb45247..4a9f7c4 100644
--- a/core/modules/hal/src/Normalizer/ContentEntityNormalizer.php
+++ b/core/modules/hal/src/Normalizer/ContentEntityNormalizer.php
@@ -6,6 +6,7 @@ use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\TypedData\TypedDataInternalPropertiesHelper;
use Drupal\hal\LinkManager\LinkManagerInterface;
use Drupal\serialization\Normalizer\FieldableEntityNormalizerTrait;
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
@@ -72,15 +73,10 @@ class ContentEntityNormalizer extends NormalizerBase {
],
];
+ $field_items = TypedDataInternalPropertiesHelper::getNonInternalProperties($entity->getTypedData());
// If the fields to use were specified, only output those field values.
if (isset($context['included_fields'])) {
- $field_items = [];
- foreach ($context['included_fields'] as $field_name) {
- $field_items[] = $entity->get($field_name);
- }
- }
- else {
- $field_items = $entity->getFields();
+ $field_items = array_intersect_key($field_items, array_flip($context['included_fields']));
}
foreach ($field_items as $field) {
// Continue if the current user does not have access to view this field.
diff --git a/core/modules/hal/src/Normalizer/FieldItemNormalizer.php b/core/modules/hal/src/Normalizer/FieldItemNormalizer.php
index 4c4ea51..6d10b06 100644
--- a/core/modules/hal/src/Normalizer/FieldItemNormalizer.php
+++ b/core/modules/hal/src/Normalizer/FieldItemNormalizer.php
@@ -3,6 +3,7 @@
namespace Drupal\hal\Normalizer;
use Drupal\Core\Field\FieldItemInterface;
+use Drupal\Core\TypedData\TypedDataInternalPropertiesHelper;
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
/**
@@ -91,7 +92,7 @@ class FieldItemNormalizer extends NormalizerBase {
// We normalize each individual property, so each can do their own casting,
// if needed.
/** @var \Drupal\Core\TypedData\TypedDataInterface $property */
- foreach ($field_item as $property_name => $property) {
+ foreach (TypedDataInternalPropertiesHelper::getNonInternalProperties($field_item) as $property_name => $property) {
$normalized[$property_name] = $this->serializer->normalize($property, $format, $context);
}
diff --git a/core/modules/hal/tests/src/Functional/EntityResource/EntityTest/EntityTestHalJsonInternalPropertyNormalizerTest.php b/core/modules/hal/tests/src/Functional/EntityResource/EntityTest/EntityTestHalJsonInternalPropertyNormalizerTest.php
new file mode 100644
index 0000000..8ee3215
--- /dev/null
+++ b/core/modules/hal/tests/src/Functional/EntityResource/EntityTest/EntityTestHalJsonInternalPropertyNormalizerTest.php
@@ -0,0 +1,85 @@
+<?php
+
+namespace Drupal\Tests\hal\Functional\EntityResource\EntityTest;
+
+use Drupal\field\Entity\FieldConfig;
+use Drupal\field\Entity\FieldStorageConfig;
+use Drupal\Tests\hal\Functional\EntityResource\HalEntityNormalizationTrait;
+use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
+
+/**
+ * Test that internal properties are not exposed in the 'hal_json' format.
+ *
+ * @group hal
+ */
+class EntityTestHalJsonInternalPropertyNormalizerTest extends EntityTestHalJsonAnonTest {
+
+ use AnonResourceTestTrait, HalEntityNormalizationTrait;
+
+ /**
+ * {@inheritdoc}
+ */
+ public static $modules = ['hal'];
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getExpectedNormalizedEntity() {
+ $default_normalization = parent::getExpectedNormalizedEntity();
+
+ $normalization = $this->applyHalFieldNormalization($default_normalization);
+ // The 'internal_value' property in test field type will not be returned in
+ // normalization because setInternal(FALSE) was not called for this
+ // property.
+ // @see \Drupal\entity_test\Plugin\Field\FieldType\InternalPropertyTestFieldItem::propertyDefinitions
+ $normalization['field_test_internal'] = [
+ [
+ 'value' => 'This value shall not be internal!',
+ 'non_internal_value' => 'Computed! This value shall not be internal!',
+ ],
+ ];
+ return $normalization;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function createEntity() {
+ if (!FieldStorageConfig::loadByName('entity_test', 'field_test_internal')) {
+ FieldStorageConfig::create([
+ 'entity_type' => 'entity_test',
+ 'field_name' => 'field_test_internal',
+ 'type' => 'internal_property_test',
+ 'cardinality' => 1,
+ 'translatable' => FALSE,
+ ])->save();
+ FieldConfig::create([
+ 'entity_type' => 'entity_test',
+ 'field_name' => 'field_test_internal',
+ 'bundle' => 'entity_test',
+ 'label' => 'Test field with internal and non-internal properties',
+ ])->save();
+ }
+
+ $entity = parent::createEntity();
+ $entity->field_test_internal = [
+ 'value' => 'This value shall not be internal!',
+ ];
+ $entity->save();
+ return $entity;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getNormalizedPostEntity() {
+ return parent::getNormalizedPostEntity() + [
+ 'field_test_internal' => [
+ [
+ 'value' => 'This value shall not be internal!',
+ ],
+ ],
+ ];
+ }
+
+}
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/EntityTest/EntityTestJsonInternalPropertyNormalizerTest.php b/core/modules/rest/tests/src/Functional/EntityResource/EntityTest/EntityTestJsonInternalPropertyNormalizerTest.php
new file mode 100644
index 0000000..c369e17
--- /dev/null
+++ b/core/modules/rest/tests/src/Functional/EntityResource/EntityTest/EntityTestJsonInternalPropertyNormalizerTest.php
@@ -0,0 +1,87 @@
+<?php
+
+namespace Drupal\Tests\rest\Functional\EntityResource\EntityTest;
+
+use Drupal\field\Entity\FieldConfig;
+use Drupal\field\Entity\FieldStorageConfig;
+use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
+
+/**
+ * Test that internal properties are not exposed in the 'json' format.
+ *
+ * @group rest
+ */
+class EntityTestJsonInternalPropertyNormalizerTest extends EntityTestResourceTestBase {
+
+ use AnonResourceTestTrait;
+
+ /**
+ * {@inheritdoc}
+ */
+ protected static $format = 'json';
+
+ /**
+ * {@inheritdoc}
+ */
+ protected static $mimeType = 'application/json';
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getExpectedNormalizedEntity() {
+ $expected = parent::getExpectedNormalizedEntity();
+ // The 'internal_value' property in test field type is not exposed in the
+ // normalization because setInternal(FALSE) was not called for this
+ // property.
+ // @see \Drupal\entity_test\Plugin\Field\FieldType\InternalPropertyTestFieldItem::propertyDefinitions
+ $expected['field_test_internal'] = [
+ [
+ 'value' => 'This value shall not be internal!',
+ 'non_internal_value' => 'Computed! This value shall not be internal!',
+ ],
+ ];
+ return $expected;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function createEntity() {
+ if (!FieldStorageConfig::loadByName('entity_test', 'field_test_internal')) {
+ FieldStorageConfig::create([
+ 'entity_type' => 'entity_test',
+ 'field_name' => 'field_test_internal',
+ 'type' => 'internal_property_test',
+ 'cardinality' => 1,
+ 'translatable' => FALSE,
+ ])->save();
+ FieldConfig::create([
+ 'entity_type' => 'entity_test',
+ 'field_name' => 'field_test_internal',
+ 'bundle' => 'entity_test',
+ 'label' => 'Test field with internal and non-internal properties',
+ ])->save();
+ }
+
+ $entity = parent::createEntity();
+ $entity->field_test_internal = [
+ 'value' => 'This value shall not be internal!',
+ ];
+ $entity->save();
+ return $entity;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getNormalizedPostEntity() {
+ return parent::getNormalizedPostEntity() + [
+ 'field_test_internal' => [
+ [
+ 'value' => 'This value shall not be internal!',
+ ],
+ ],
+ ];
+ }
+
+}
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/EntityTest/EntityTestResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/EntityTest/EntityTestResourceTestBase.php
index e2c0ccd..d14ec38 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/EntityTest/EntityTestResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/EntityTest/EntityTestResourceTestBase.php
@@ -53,9 +53,20 @@ abstract class EntityTestResourceTestBase extends EntityResourceTestBase {
* {@inheritdoc}
*/
protected function createEntity() {
+ // Set flag so that internal field 'internal_string_field' is created.
+ // @see entity_test_entity_base_field_info()
+ $this->container->get('state')->set('entity_test.internal_field', TRUE);
+ \Drupal::entityDefinitionUpdateManager()->applyUpdates();
+
$entity_test = EntityTest::create([
'name' => 'Llama',
'type' => 'entity_test',
+ // Set a value for the internal field to confirm that it will not be
+ // returned in normalization.
+ // @see entity_test_entity_base_field_info().
+ 'internal_string_field' => [
+ 'value' => 'This value shall not be internal!',
+ ],
]);
$entity_test->setOwnerId(0);
$entity_test->save();
diff --git a/core/modules/serialization/src/Normalizer/ComplexDataNormalizer.php b/core/modules/serialization/src/Normalizer/ComplexDataNormalizer.php
index 33beb37..e4fcf52 100644
--- a/core/modules/serialization/src/Normalizer/ComplexDataNormalizer.php
+++ b/core/modules/serialization/src/Normalizer/ComplexDataNormalizer.php
@@ -2,6 +2,9 @@
namespace Drupal\serialization\Normalizer;
+use Drupal\Core\TypedData\ComplexDataInterface;
+use Drupal\Core\TypedData\TypedDataInternalPropertiesHelper;
+
/**
* Converts the Drupal entity object structures to a normalized array.
*
@@ -26,6 +29,13 @@ class ComplexDataNormalizer extends NormalizerBase {
*/
public function normalize($object, $format = NULL, array $context = []) {
$attributes = [];
+ // $object will not always match $supportedInterfaceOrClass.
+ // @see \Drupal\serialization\Normalizer\EntityNormalizer
+ // Other normalizers that extend this class may only provide $object that
+ // implements \Traversable.
+ if ($object instanceof ComplexDataInterface) {
+ $object = TypedDataInternalPropertiesHelper::getNonInternalProperties($object);
+ }
/** @var \Drupal\Core\TypedData\TypedDataInterface $property */
foreach ($object as $name => $property) {
$attributes[$name] = $this->serializer->normalize($property, $format, $context);
diff --git a/core/modules/serialization/src/Normalizer/ContentEntityNormalizer.php b/core/modules/serialization/src/Normalizer/ContentEntityNormalizer.php
index d3abef6..85635a9 100644
--- a/core/modules/serialization/src/Normalizer/ContentEntityNormalizer.php
+++ b/core/modules/serialization/src/Normalizer/ContentEntityNormalizer.php
@@ -2,6 +2,8 @@
namespace Drupal\serialization\Normalizer;
+use Drupal\Core\TypedData\TypedDataInternalPropertiesHelper;
+
/**
* Normalizes/denormalizes Drupal content entities into an array structure.
*/
@@ -21,7 +23,8 @@ class ContentEntityNormalizer extends EntityNormalizer {
];
$attributes = [];
- foreach ($entity as $name => $field_items) {
+ /** @var \Drupal\Core\Entity\Entity $entity */
+ foreach (TypedDataInternalPropertiesHelper::getNonInternalProperties($entity->getTypedData()) as $name => $field_items) {
if ($field_items->access('view', $context['account'])) {
$attributes[$name] = $this->serializer->normalize($field_items, $format, $context);
}
diff --git a/core/modules/serialization/tests/src/Unit/Normalizer/ComplexDataNormalizerTest.php b/core/modules/serialization/tests/src/Unit/Normalizer/ComplexDataNormalizerTest.php
index dc14d6f..1bf6cf4 100644
--- a/core/modules/serialization/tests/src/Unit/Normalizer/ComplexDataNormalizerTest.php
+++ b/core/modules/serialization/tests/src/Unit/Normalizer/ComplexDataNormalizerTest.php
@@ -8,7 +8,6 @@
namespace Drupal\Tests\serialization\Unit\Normalizer;
use Drupal\Core\TypedData\ComplexDataInterface;
-use Drupal\Core\TypedData\TraversableTypedDataInterface;
use Drupal\serialization\Normalizer\ComplexDataNormalizer;
use Drupal\Tests\UnitTestCase;
use Symfony\Component\Serializer\Serializer;
@@ -19,6 +18,8 @@ use Symfony\Component\Serializer\Serializer;
*/
class ComplexDataNormalizerTest extends UnitTestCase {
+ use InternalTypedDataTestTrait;
+
/**
* Test format string.
*
@@ -44,103 +45,77 @@ class ComplexDataNormalizerTest extends UnitTestCase {
* @covers ::supportsNormalization
*/
public function testSupportsNormalization() {
- $this->assertTrue($this->normalizer->supportsNormalization(new TestComplexData()));
+ $complex_data = $this->prophesize(ComplexDataInterface::class)->reveal();
+ $this->assertTrue($this->normalizer->supportsNormalization($complex_data));
// Also test that an object not implementing ComplexDataInterface fails.
$this->assertFalse($this->normalizer->supportsNormalization(new \stdClass()));
}
/**
+ * Test normalizing complex data.
+ *
* @covers ::normalize
*/
- public function testNormalize() {
- $context = ['test' => 'test'];
-
+ public function testNormalizeComplexData() {
$serializer_prophecy = $this->prophesize(Serializer::class);
- $serializer_prophecy->normalize('A', static::TEST_FORMAT, $context)
- ->shouldBeCalled();
- $serializer_prophecy->normalize('B', static::TEST_FORMAT, $context)
+ $non_internal_property = $this->getTypedDataProperty(FALSE);
+
+ $serializer_prophecy->normalize($non_internal_property, static::TEST_FORMAT, [])
+ ->willReturn('A-normalized')
->shouldBeCalled();
$this->normalizer->setSerializer($serializer_prophecy->reveal());
- $complex_data = new TestComplexData(['a' => 'A', 'b' => 'B']);
- $this->normalizer->normalize($complex_data, static::TEST_FORMAT, $context);
-
- }
-
-}
-
-/**
- * Test class implementing ComplexDataInterface and IteratorAggregate.
- */
-class TestComplexData implements \IteratorAggregate, ComplexDataInterface {
-
- private $values;
-
- public function __construct(array $values = []) {
- $this->values = $values;
- }
-
- public function getIterator() {
- return new \ArrayIterator($this->values);
- }
-
- public function applyDefaultValue($notify = TRUE) {
- }
-
- public static function createInstance($definition, $name = NULL, TraversableTypedDataInterface $parent = NULL) {
- }
-
- public function get($property_name) {
- }
-
- public function getConstraints() {
- }
-
- public function getDataDefinition() {
- }
-
- public function getName() {
- }
-
- public function getParent() {
- }
-
- public function getProperties($include_computed = FALSE) {
- }
-
- public function getPropertyPath() {
- }
-
- public function getRoot() {
- }
+ $complex_data = $this->prophesize(ComplexDataInterface::class);
+ $complex_data->getProperties(TRUE)
+ ->willReturn([
+ 'prop:a' => $non_internal_property,
+ 'prop:internal' => $this->getTypedDataProperty(TRUE),
+ ])
+ ->shouldBeCalled();
- public function getString() {
+ $normalized = $this->normalizer->normalize($complex_data->reveal(), static::TEST_FORMAT);
+ $this->assertEquals(['prop:a' => 'A-normalized'], $normalized);
}
- public function getValue() {
- }
+ /**
+ * Test normalize() where $object does not implement ComplexDataInterface.
+ *
+ * Normalizers extending ComplexDataNormalizer may have a different supported
+ * class.
+ *
+ * @covers ::normalize
+ */
+ public function testNormalizeNonComplex() {
+ $normalizer = new TestExtendedNormalizer();
+ $serialization_context = ['test' => 'test'];
- public function isEmpty() {
- }
+ $serializer_prophecy = $this->prophesize(Serializer::class);
+ $serializer_prophecy->normalize('A', static::TEST_FORMAT, $serialization_context)
+ ->willReturn('A-normalized')
+ ->shouldBeCalled();
+ $serializer_prophecy->normalize('B', static::TEST_FORMAT, $serialization_context)
+ ->willReturn('B-normalized')
+ ->shouldBeCalled();
- public function onChange($name) {
- }
+ $normalizer->setSerializer($serializer_prophecy->reveal());
- public function set($property_name, $value, $notify = TRUE) {
- }
+ $stdClass = new \stdClass();
+ $stdClass->a = 'A';
+ $stdClass->b = 'B';
- public function setContext($name = NULL, TraversableTypedDataInterface $parent = NULL) {
- }
+ $normalized = $normalizer->normalize($stdClass, static::TEST_FORMAT, $serialization_context);
+ $this->assertEquals(['a' => 'A-normalized', 'b' => 'B-normalized'], $normalized);
- public function setValue($value, $notify = TRUE) {
}
- public function toArray() {
- }
+}
- public function validate() {
- }
+/**
+ * Test normalizer with a different supported class.
+ */
+class TestExtendedNormalizer extends ComplexDataNormalizer {
+ protected $supportedInterfaceOrClass = \stdClass::class;
}
diff --git a/core/modules/serialization/tests/src/Unit/Normalizer/ContentEntityNormalizerTest.php b/core/modules/serialization/tests/src/Unit/Normalizer/ContentEntityNormalizerTest.php
index 4beecf4..e5d02fa 100644
--- a/core/modules/serialization/tests/src/Unit/Normalizer/ContentEntityNormalizerTest.php
+++ b/core/modules/serialization/tests/src/Unit/Normalizer/ContentEntityNormalizerTest.php
@@ -2,6 +2,9 @@
namespace Drupal\Tests\serialization\Unit\Normalizer;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\Core\TypedData\ComplexDataInterface;
+use Drupal\Core\TypedData\DataDefinitionInterface;
use Drupal\serialization\Normalizer\ContentEntityNormalizer;
use Drupal\Tests\UnitTestCase;
@@ -67,16 +70,20 @@ class ContentEntityNormalizerTest extends UnitTestCase {
->will($this->returnValue('test'));
$definitions = [
- 'field_1' => $this->createMockFieldListItem(),
- 'field_2' => $this->createMockFieldListItem(FALSE),
+ 'field_accessible_external' => $this->createMockFieldListItem(TRUE, FALSE),
+ 'field_non-accessible_external' => $this->createMockFieldListItem(FALSE, FALSE),
+ 'field_accessible_internal' => $this->createMockFieldListItem(TRUE, TRUE),
+ 'field_non-accessible_internal' => $this->createMockFieldListItem(FALSE, TRUE),
];
$content_entity_mock = $this->createMockForContentEntity($definitions);
$normalized = $this->contentEntityNormalizer->normalize($content_entity_mock, 'test_format');
- $this->assertArrayHasKey('field_1', $normalized);
- $this->assertEquals('test', $normalized['field_1']);
- $this->assertArrayNotHasKey('field_2', $normalized);
+ $this->assertArrayHasKey('field_accessible_external', $normalized);
+ $this->assertEquals('test', $normalized['field_accessible_external']);
+ $this->assertArrayNotHasKey('field_non-accessible_external', $normalized);
+ $this->assertArrayNotHasKey('field_accessible_internal', $normalized);
+ $this->assertArrayNotHasKey('field_non-accessible_internal', $normalized);
}
/**
@@ -99,8 +106,8 @@ class ContentEntityNormalizerTest extends UnitTestCase {
// The mock account should get passed directly into the access() method on
// field items from $context['account'].
$definitions = [
- 'field_1' => $this->createMockFieldListItem(TRUE, $mock_account),
- 'field_2' => $this->createMockFieldListItem(FALSE, $mock_account),
+ 'field_1' => $this->createMockFieldListItem(TRUE, FALSE, $mock_account),
+ 'field_2' => $this->createMockFieldListItem(FALSE, FALSE, $mock_account),
];
$content_entity_mock = $this->createMockForContentEntity($definitions);
@@ -121,11 +128,15 @@ class ContentEntityNormalizerTest extends UnitTestCase {
public function createMockForContentEntity($definitions) {
$content_entity_mock = $this->getMockBuilder('Drupal\Core\Entity\ContentEntityBase')
->disableOriginalConstructor()
- ->setMethods(['getFields'])
+ ->setMethods(['getTypedData'])
->getMockForAbstractClass();
- $content_entity_mock->expects($this->once())
- ->method('getFields')
- ->will($this->returnValue($definitions));
+ $typed_data = $this->prophesize(ComplexDataInterface::class);
+ $typed_data->getProperties(TRUE)
+ ->willReturn($definitions)
+ ->shouldBeCalled();
+ $content_entity_mock->expects($this->any())
+ ->method('getTypedData')
+ ->will($this->returnValue($typed_data->reveal()));
return $content_entity_mock;
}
@@ -134,16 +145,26 @@ class ContentEntityNormalizerTest extends UnitTestCase {
* Creates a mock field list item.
*
* @param bool $access
+ * @param bool $internal
+ * @param \Drupal\Core\Session\AccountInterface $user_context
*
* @return \Drupal\Core\Field\FieldItemListInterface|\PHPUnit_Framework_MockObject_MockObject
*/
- protected function createMockFieldListItem($access = TRUE, $user_context = NULL) {
+ protected function createMockFieldListItem($access, $internal, AccountInterface $user_context = NULL) {
+ $data_definition = $this->prophesize(DataDefinitionInterface::class);
$mock = $this->getMock('Drupal\Core\Field\FieldItemListInterface');
$mock->expects($this->once())
- ->method('access')
- ->with('view', $user_context)
- ->will($this->returnValue($access));
-
+ ->method('getDataDefinition')
+ ->will($this->returnValue($data_definition->reveal()));
+ $data_definition->isInternal()
+ ->willReturn($internal)
+ ->shouldBeCalled();
+ if (!$internal) {
+ $mock->expects($this->once())
+ ->method('access')
+ ->with('view', $user_context)
+ ->will($this->returnValue($access));
+ }
return $mock;
}
diff --git a/core/modules/serialization/tests/src/Unit/Normalizer/EntityReferenceFieldItemNormalizerTest.php b/core/modules/serialization/tests/src/Unit/Normalizer/EntityReferenceFieldItemNormalizerTest.php
index e0561a1..de2a4a6 100644
--- a/core/modules/serialization/tests/src/Unit/Normalizer/EntityReferenceFieldItemNormalizerTest.php
+++ b/core/modules/serialization/tests/src/Unit/Normalizer/EntityReferenceFieldItemNormalizerTest.php
@@ -23,6 +23,8 @@ use Symfony\Component\Serializer\Serializer;
*/
class EntityReferenceFieldItemNormalizerTest extends UnitTestCase {
+ use InternalTypedDataTestTrait;
+
/**
* The mock serializer.
*
@@ -122,6 +124,10 @@ class EntityReferenceFieldItemNormalizerTest extends UnitTestCase {
->willReturn($entity_reference)
->shouldBeCalled();
+ $this->fieldItem->getProperties(TRUE)
+ ->willReturn(['target_id' => $this->getTypedDataProperty(FALSE)])
+ ->shouldBeCalled();
+
$normalized = $this->normalizer->normalize($this->fieldItem->reveal());
$expected = [
@@ -146,6 +152,10 @@ class EntityReferenceFieldItemNormalizerTest extends UnitTestCase {
->willReturn($entity_reference->reveal())
->shouldBeCalled();
+ $this->fieldItem->getProperties(TRUE)
+ ->willReturn(['target_id' => $this->getTypedDataProperty(FALSE)])
+ ->shouldBeCalled();
+
$normalized = $this->normalizer->normalize($this->fieldItem->reveal());
$expected = [
diff --git a/core/modules/serialization/tests/src/Unit/Normalizer/InternalTypedDataTestTrait.php b/core/modules/serialization/tests/src/Unit/Normalizer/InternalTypedDataTestTrait.php
new file mode 100644
index 0000000..3829934
--- /dev/null
+++ b/core/modules/serialization/tests/src/Unit/Normalizer/InternalTypedDataTestTrait.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace Drupal\Tests\serialization\Unit\Normalizer;
+
+use Drupal\Core\TypedData\DataDefinitionInterface;
+use Drupal\Core\TypedData\TypedDataInterface;
+
+/**
+ * Trait that provides mocked typed data objects.
+ */
+trait InternalTypedDataTestTrait {
+
+ /**
+ * Gets a typed data property.
+ *
+ * @param bool $internal
+ * Whether the typed data property is internal.
+ *
+ * @return \Drupal\Core\TypedData\TypedDataInterface
+ * The typed data property.
+ */
+ protected function getTypedDataProperty($internal = TRUE) {
+ $definition = $this->prophesize(DataDefinitionInterface::class);
+ $definition->isInternal()
+ ->willReturn($internal)
+ ->shouldBeCalled();
+ $definition = $definition->reveal();
+
+ $property = $this->prophesize(TypedDataInterface::class);
+ $property->getDataDefinition()
+ ->willReturn($definition)
+ ->shouldBeCalled();
+ return $property->reveal();
+ }
+
+}
diff --git a/core/modules/serialization/tests/src/Unit/Normalizer/TimestampItemNormalizerTest.php b/core/modules/serialization/tests/src/Unit/Normalizer/TimestampItemNormalizerTest.php
index fd1fc9c..c4e3514 100644
--- a/core/modules/serialization/tests/src/Unit/Normalizer/TimestampItemNormalizerTest.php
+++ b/core/modules/serialization/tests/src/Unit/Normalizer/TimestampItemNormalizerTest.php
@@ -18,6 +18,8 @@ use Symfony\Component\Serializer\Serializer;
*/
class TimestampItemNormalizerTest extends UnitTestCase {
+ use InternalTypedDataTestTrait;
+
/**
* @var \Drupal\serialization\Normalizer\TimestampItemNormalizer
*/
@@ -77,8 +79,18 @@ class TimestampItemNormalizerTest extends UnitTestCase {
$timestamp_item->getIterator()
->willReturn(new \ArrayIterator(['value' => 1478422920]));
- $serializer = new Serializer();
- $this->normalizer->setSerializer($serializer);
+ $value_property = $this->getTypedDataProperty(FALSE);
+ $timestamp_item->getProperties(TRUE)
+ ->willReturn(['value' => $value_property])
+ ->shouldBeCalled();
+
+ $serializer_prophecy = $this->prophesize(Serializer::class);
+
+ $serializer_prophecy->normalize($value_property, NULL, [])
+ ->willReturn(1478422920)
+ ->shouldBeCalled();
+
+ $this->normalizer->setSerializer($serializer_prophecy->reveal());
$normalized = $this->normalizer->normalize($timestamp_item->reveal());
$this->assertSame($expected, $normalized);
diff --git a/core/modules/system/tests/modules/entity_test/config/schema/entity_test.data_types.schema.yml b/core/modules/system/tests/modules/entity_test/config/schema/entity_test.data_types.schema.yml
new file mode 100644
index 0000000..cebaf03
--- /dev/null
+++ b/core/modules/system/tests/modules/entity_test/config/schema/entity_test.data_types.schema.yml
@@ -0,0 +1,5 @@
+# Schema for the configuration of the 'internal property test' field type.
+
+field.storage_settings.internal_property_test:
+ type: field.storage_settings.string
+ label: 'Internal property settings'
diff --git a/core/modules/system/tests/modules/entity_test/entity_test.module b/core/modules/system/tests/modules/entity_test/entity_test.module
index bcf55f4..025cb5f 100644
--- a/core/modules/system/tests/modules/entity_test/entity_test.module
+++ b/core/modules/system/tests/modules/entity_test/entity_test.module
@@ -109,6 +109,11 @@ function entity_test_entity_type_alter(array &$entity_types) {
function entity_test_entity_base_field_info(EntityTypeInterface $entity_type) {
$fields = [];
+ if ($entity_type->id() === 'entity_test' && \Drupal::state()->get('entity_test.internal_field')) {
+ $fields['internal_string_field'] = BaseFieldDefinition::create('string')
+ ->setLabel('Internal field')
+ ->setInternal(TRUE);
+ }
if ($entity_type->id() == 'entity_test_mulrev' && \Drupal::state()->get('entity_test.field_test_item')) {
$fields['field_test_item'] = BaseFieldDefinition::create('field_test')
->setLabel(t('Field test'))
diff --git a/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/InternalPropertyTestFieldItem.php b/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/InternalPropertyTestFieldItem.php
new file mode 100644
index 0000000..2bc8154
--- /dev/null
+++ b/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/InternalPropertyTestFieldItem.php
@@ -0,0 +1,45 @@
+<?php
+
+namespace Drupal\entity_test\Plugin\Field\FieldType;
+
+use Drupal\Core\Field\FieldStorageDefinitionInterface;
+use Drupal\Core\Field\Plugin\Field\FieldType\StringItem;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\Core\TypedData\DataDefinition;
+use Drupal\entity_test\TypedData\ComputedString;
+
+/**
+ * Defines the 'Internal Property' entity test field type.
+ *
+ * @FieldType(
+ * id = "internal_property_test",
+ * label = @Translation("Internal Property (test)"),
+ * description = @Translation("A field containing one string, from which two strings are computed (one internal, one not)."),
+ * category = @Translation("Test"),
+ * default_widget = "string_textfield",
+ * default_formatter = "string"
+ * )
+ */
+class InternalPropertyTestFieldItem extends StringItem {
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
+ $properties = parent::propertyDefinitions($field_definition);
+
+ // Add a computed property that is non-internal.
+ $properties['non_internal_value'] = DataDefinition::create('string')
+ ->setLabel(new TranslatableMarkup('Computed string, non-internal property'))
+ ->setComputed(TRUE)
+ ->setClass(ComputedString::class)
+ ->setInternal(FALSE);
+ // Add a computed property that is internal.
+ $properties['internal_value'] = DataDefinition::create('string')
+ ->setLabel(new TranslatableMarkup('Computed string, internal property'))
+ ->setComputed(TRUE)
+ ->setClass(ComputedString::class);
+ return $properties;
+ }
+
+}
diff --git a/core/modules/system/tests/modules/entity_test/src/TypedData/ComputedString.php b/core/modules/system/tests/modules/entity_test/src/TypedData/ComputedString.php
new file mode 100644
index 0000000..121699c
--- /dev/null
+++ b/core/modules/system/tests/modules/entity_test/src/TypedData/ComputedString.php
@@ -0,0 +1,30 @@
+<?php
+
+namespace Drupal\entity_test\TypedData;
+
+use Drupal\Core\TypedData\TypedData;
+
+/**
+ * A computed property for test strings.
+ */
+class ComputedString extends TypedData {
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getValue() {
+ /** @var \Drupal\Core\Field\FieldItemInterface $item */
+ $item = $this->getParent();
+ $computed_value = "Computed! " . $item->get('value')->getString();
+
+ return $computed_value;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getCastedValue() {
+ return $this->getString();
+ }
+
+}