Issue #2895532 by caseylau, Wim Leers, tedbow, dawehner, DamienMcKenna, gabesullice, Berdir, mistermoper, skyredwang, larowlan, bojanz: @DataType=map cannot be normalized, affects @FieldType=link, @FieldType=map
parent
3f44f5bbcb
commit
725e36091e
|
@ -168,9 +168,8 @@ class TypedDataManager extends DefaultPluginManager implements TypedDataManagerI
|
||||||
// a shorter string than the serialized form, so array access is faster.
|
// a shorter string than the serialized form, so array access is faster.
|
||||||
$parts[] = json_encode($settings);
|
$parts[] = json_encode($settings);
|
||||||
}
|
}
|
||||||
// Property path for the requested data object. When creating a list item,
|
// Property path for the requested data object.
|
||||||
// use 0 in the key as all items look the same.
|
$parts[] = $object->getPropertyPath() . '.' . $property_name;
|
||||||
$parts[] = $object->getPropertyPath() . '.' . (is_numeric($property_name) ? 0 : $property_name);
|
|
||||||
$key = implode(':', $parts);
|
$key = implode(':', $parts);
|
||||||
|
|
||||||
// Create the prototype if needed.
|
// Create the prototype if needed.
|
||||||
|
|
|
@ -92,7 +92,10 @@ class FieldItemNormalizer extends NormalizerBase {
|
||||||
// We normalize each individual property, so each can do their own casting,
|
// We normalize each individual property, so each can do their own casting,
|
||||||
// if needed.
|
// if needed.
|
||||||
/** @var \Drupal\Core\TypedData\TypedDataInterface $property */
|
/** @var \Drupal\Core\TypedData\TypedDataInterface $property */
|
||||||
foreach (TypedDataInternalPropertiesHelper::getNonInternalProperties($field_item) as $property_name => $property) {
|
$field_properties = !empty($field_item->getProperties(TRUE))
|
||||||
|
? TypedDataInternalPropertiesHelper::getNonInternalProperties($field_item)
|
||||||
|
: $field_item->getValue();
|
||||||
|
foreach ($field_properties as $property_name => $property) {
|
||||||
$normalized[$property_name] = $this->serializer->normalize($property, $format, $context);
|
$normalized[$property_name] = $this->serializer->normalize($property, $format, $context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ class LinkNotExistingInternalConstraintValidator extends ConstraintValidator {
|
||||||
if ($url->isRouted()) {
|
if ($url->isRouted()) {
|
||||||
$allowed = TRUE;
|
$allowed = TRUE;
|
||||||
try {
|
try {
|
||||||
$url->toString();
|
$url->toString(TRUE);
|
||||||
}
|
}
|
||||||
// The following exceptions are all possible during URL generation, and
|
// The following exceptions are all possible during URL generation, and
|
||||||
// should be considered as disallowed URLs.
|
// should be considered as disallowed URLs.
|
||||||
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\Tests\link\Kernel;
|
||||||
|
|
||||||
|
use Drupal\field\Entity\FieldStorageConfig;
|
||||||
|
use Drupal\field\Entity\FieldConfig;
|
||||||
|
use Drupal\link\LinkItemInterface;
|
||||||
|
use Drupal\entity_test\Entity\EntityTest;
|
||||||
|
use Drupal\Component\Utility\UrlHelper;
|
||||||
|
use Drupal\Tests\field\Kernel\FieldKernelTestBase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests link field serialization.
|
||||||
|
*
|
||||||
|
* @group link
|
||||||
|
*/
|
||||||
|
class LinkItemSerializationTest extends FieldKernelTestBase {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public static $modules = ['link', 'serialization'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The serializer service.
|
||||||
|
*
|
||||||
|
* @var \Symfony\Component\Serializer\SerializerInterface
|
||||||
|
*/
|
||||||
|
protected $serializer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function setUp() {
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$this->installEntitySchema('user');
|
||||||
|
$this->serializer = \Drupal::service('serializer');
|
||||||
|
|
||||||
|
// Create a generic, external, and internal link fields for validation.
|
||||||
|
FieldStorageConfig::create([
|
||||||
|
'entity_type' => 'entity_test',
|
||||||
|
'field_name' => 'field_test',
|
||||||
|
'type' => 'link',
|
||||||
|
])->save();
|
||||||
|
|
||||||
|
FieldConfig::create([
|
||||||
|
'entity_type' => 'entity_test',
|
||||||
|
'field_name' => 'field_test',
|
||||||
|
'bundle' => 'entity_test',
|
||||||
|
'settings' => ['link_type' => LinkItemInterface::LINK_GENERIC],
|
||||||
|
])->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the serialization.
|
||||||
|
*/
|
||||||
|
public function testLinkSerialization() {
|
||||||
|
// Create entity.
|
||||||
|
$entity = EntityTest::create();
|
||||||
|
$url = 'https://www.drupal.org?test_param=test_value';
|
||||||
|
$parsed_url = UrlHelper::parse($url);
|
||||||
|
$title = $this->randomMachineName();
|
||||||
|
$class = $this->randomMachineName();
|
||||||
|
$entity->field_test->uri = $parsed_url['path'];
|
||||||
|
$entity->field_test->title = $title;
|
||||||
|
$entity->field_test->first()
|
||||||
|
->get('options')
|
||||||
|
->set('query', $parsed_url['query']);
|
||||||
|
$entity->field_test->first()
|
||||||
|
->get('options')
|
||||||
|
->set('attributes', ['class' => $class]);
|
||||||
|
$entity->save();
|
||||||
|
$serialized = $this->serializer->serialize($entity, 'json');
|
||||||
|
$deserialized = $this->serializer->deserialize($serialized, EntityTest::class, 'json');
|
||||||
|
$options_expected = [
|
||||||
|
'query' => $parsed_url['query'],
|
||||||
|
'attributes' => ['class' => $class],
|
||||||
|
];
|
||||||
|
$this->assertSame($options_expected, $deserialized->field_test->options);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -59,7 +59,15 @@ abstract class MenuLinkContentResourceTestBase extends EntityResourceTestBase {
|
||||||
'id' => 'llama',
|
'id' => 'llama',
|
||||||
'title' => 'Llama Gabilondo',
|
'title' => 'Llama Gabilondo',
|
||||||
'description' => 'Llama Gabilondo',
|
'description' => 'Llama Gabilondo',
|
||||||
'link' => 'https://nl.wikipedia.org/wiki/Llama',
|
'link' => [
|
||||||
|
'uri' => 'https://nl.wikipedia.org/wiki/Llama',
|
||||||
|
'options' => [
|
||||||
|
'fragment' => 'a-fragment',
|
||||||
|
'attributes' => [
|
||||||
|
'class' => ['example-class'],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
'weight' => 0,
|
'weight' => 0,
|
||||||
'menu_name' => 'main',
|
'menu_name' => 'main',
|
||||||
]);
|
]);
|
||||||
|
@ -81,6 +89,12 @@ abstract class MenuLinkContentResourceTestBase extends EntityResourceTestBase {
|
||||||
'link' => [
|
'link' => [
|
||||||
[
|
[
|
||||||
'uri' => 'http://www.urbandictionary.com/define.php?term=drama%20llama',
|
'uri' => 'http://www.urbandictionary.com/define.php?term=drama%20llama',
|
||||||
|
'options' => [
|
||||||
|
'fragment' => 'a-fragment',
|
||||||
|
'attributes' => [
|
||||||
|
'class' => ['example-class'],
|
||||||
|
],
|
||||||
|
],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
'bundle' => [
|
'bundle' => [
|
||||||
|
@ -115,7 +129,12 @@ abstract class MenuLinkContentResourceTestBase extends EntityResourceTestBase {
|
||||||
[
|
[
|
||||||
'uri' => 'https://nl.wikipedia.org/wiki/Llama',
|
'uri' => 'https://nl.wikipedia.org/wiki/Llama',
|
||||||
'title' => NULL,
|
'title' => NULL,
|
||||||
'options' => [],
|
'options' => [
|
||||||
|
'fragment' => 'a-fragment',
|
||||||
|
'attributes' => [
|
||||||
|
'class' => ['example-class'],
|
||||||
|
],
|
||||||
|
],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
'weight' => [
|
'weight' => [
|
||||||
|
|
|
@ -617,7 +617,10 @@ abstract class EntityResourceTestBase extends ResourceTestBase {
|
||||||
// Only run this for fieldable entities. It doesn't make sense for config
|
// Only run this for fieldable entities. It doesn't make sense for config
|
||||||
// entities as config values are already casted. They also run through the
|
// entities as config values are already casted. They also run through the
|
||||||
// ConfigEntityNormalizer, which doesn't deal with fields individually.
|
// ConfigEntityNormalizer, which doesn't deal with fields individually.
|
||||||
if ($this->entity instanceof FieldableEntityInterface) {
|
// Also exclude entity_test_map_field — that has a "map" base field, which
|
||||||
|
// only became normalizable since Drupal 8.6, so its normalization
|
||||||
|
// containing non-stringified numbers or booleans does not break BC.
|
||||||
|
if ($this->entity instanceof FieldableEntityInterface && static::$entityTypeId !== 'entity_test_map_field') {
|
||||||
// Test primitive data casting BC (strings).
|
// Test primitive data casting BC (strings).
|
||||||
$this->config('serialization.settings')->set('bc_primitives_as_strings', TRUE)->save(TRUE);
|
$this->config('serialization.settings')->set('bc_primitives_as_strings', TRUE)->save(TRUE);
|
||||||
// Rebuild the container so new config is reflected in the addition of the
|
// Rebuild the container so new config is reflected in the addition of the
|
||||||
|
@ -921,17 +924,7 @@ abstract class EntityResourceTestBase extends ResourceTestBase {
|
||||||
$created_entity = $this->entityStorage->loadUnchanged(static::$firstCreatedEntityId);
|
$created_entity = $this->entityStorage->loadUnchanged(static::$firstCreatedEntityId);
|
||||||
$created_entity_normalization = $this->serializer->normalize($created_entity, static::$format, ['account' => $this->account]);
|
$created_entity_normalization = $this->serializer->normalize($created_entity, static::$format, ['account' => $this->account]);
|
||||||
$this->assertSame($created_entity_normalization, $this->serializer->decode((string) $response->getBody(), static::$format));
|
$this->assertSame($created_entity_normalization, $this->serializer->decode((string) $response->getBody(), static::$format));
|
||||||
// Assert that the entity was indeed created using the POSTed values.
|
$this->assertStoredEntityMatchesSentNormalization($this->getNormalizedPostEntity(), $created_entity);
|
||||||
foreach ($this->getNormalizedPostEntity() as $field_name => $field_normalization) {
|
|
||||||
// Some top-level keys in the normalization may not be fields on the
|
|
||||||
// entity (for example '_links' and '_embedded' in the HAL normalization).
|
|
||||||
if ($created_entity->hasField($field_name)) {
|
|
||||||
// Subset, not same, because we can e.g. send just the target_id for the
|
|
||||||
// bundle in a POST request; the response will include more properties.
|
|
||||||
$this->assertArraySubset(static::castToString($field_normalization), $created_entity->get($field_name)
|
|
||||||
->getValue(), TRUE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->config('rest.settings')->set('bc_entity_resource_permissions', TRUE)->save(TRUE);
|
$this->config('rest.settings')->set('bc_entity_resource_permissions', TRUE)->save(TRUE);
|
||||||
|
@ -1182,16 +1175,7 @@ abstract class EntityResourceTestBase extends ResourceTestBase {
|
||||||
$updated_entity = $this->entityStorage->loadUnchanged($this->entity->id());
|
$updated_entity = $this->entityStorage->loadUnchanged($this->entity->id());
|
||||||
$updated_entity_normalization = $this->serializer->normalize($updated_entity, static::$format, ['account' => $this->account]);
|
$updated_entity_normalization = $this->serializer->normalize($updated_entity, static::$format, ['account' => $this->account]);
|
||||||
$this->assertSame($updated_entity_normalization, $this->serializer->decode((string) $response->getBody(), static::$format));
|
$this->assertSame($updated_entity_normalization, $this->serializer->decode((string) $response->getBody(), static::$format));
|
||||||
// Assert that the entity was indeed created using the PATCHed values.
|
$this->assertStoredEntityMatchesSentNormalization($this->getNormalizedPatchEntity(), $updated_entity);
|
||||||
foreach ($this->getNormalizedPatchEntity() as $field_name => $field_normalization) {
|
|
||||||
// Some top-level keys in the normalization may not be fields on the
|
|
||||||
// entity (for example '_links' and '_embedded' in the HAL normalization).
|
|
||||||
if ($updated_entity->hasField($field_name)) {
|
|
||||||
// Subset, not same, because we can e.g. send just the target_id for the
|
|
||||||
// bundle in a PATCH request; the response will include more properties.
|
|
||||||
$this->assertArraySubset(static::castToString($field_normalization), $updated_entity->get($field_name)->getValue(), TRUE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Ensure that fields do not get deleted if they're not present in the PATCH
|
// Ensure that fields do not get deleted if they're not present in the PATCH
|
||||||
// request. Test this using the configurable field that we added, but which
|
// request. Test this using the configurable field that we added, but which
|
||||||
// is not sent in the PATCH request.
|
// is not sent in the PATCH request.
|
||||||
|
@ -1516,4 +1500,33 @@ abstract class EntityResourceTestBase extends ResourceTestBase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that the stored entity matches the sent normalization.
|
||||||
|
*
|
||||||
|
* @param array $sent_normalization
|
||||||
|
* An entity normalization.
|
||||||
|
* @param \Drupal\Core\Entity\FieldableEntityInterface $modified_entity
|
||||||
|
* The entity object of the modified (PATCHed or POSTed) entity.
|
||||||
|
*/
|
||||||
|
protected function assertStoredEntityMatchesSentNormalization(array $sent_normalization, FieldableEntityInterface $modified_entity) {
|
||||||
|
foreach ($sent_normalization as $field_name => $field_normalization) {
|
||||||
|
// Some top-level keys in the normalization may not be fields on the
|
||||||
|
// entity (for example '_links' and '_embedded' in the HAL normalization).
|
||||||
|
if ($modified_entity->hasField($field_name)) {
|
||||||
|
$field_type = $modified_entity->get($field_name)->getFieldDefinition()->getType();
|
||||||
|
// Fields are stored in the database, when read they are represented
|
||||||
|
// as strings in PHP memory. The exception: field types that are
|
||||||
|
// stored in a serialized way. Hence we need to cast most expected
|
||||||
|
// field normalizations to strings.
|
||||||
|
$expected_field_normalization = ($field_type !== 'map')
|
||||||
|
? static::castToString($field_normalization)
|
||||||
|
: $field_normalization;
|
||||||
|
// Subset, not same, because we can e.g. send just the target_id for the
|
||||||
|
// bundle in a PATCH or POST request; the response will include more
|
||||||
|
// properties.
|
||||||
|
$this->assertArraySubset($expected_field_normalization, $modified_entity->get($field_name)->getValue(), TRUE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,10 @@ class ComplexDataNormalizer extends NormalizerBase {
|
||||||
// Other normalizers that extend this class may only provide $object that
|
// Other normalizers that extend this class may only provide $object that
|
||||||
// implements \Traversable.
|
// implements \Traversable.
|
||||||
if ($object instanceof ComplexDataInterface) {
|
if ($object instanceof ComplexDataInterface) {
|
||||||
$object = TypedDataInternalPropertiesHelper::getNonInternalProperties($object);
|
// If there are no properties to normalize, just normalize the value.
|
||||||
|
$object = !empty($object->getProperties(TRUE))
|
||||||
|
? TypedDataInternalPropertiesHelper::getNonInternalProperties($object)
|
||||||
|
: $object->getValue();
|
||||||
}
|
}
|
||||||
/** @var \Drupal\Core\TypedData\TypedDataInterface $property */
|
/** @var \Drupal\Core\TypedData\TypedDataInterface $property */
|
||||||
foreach ($object as $name => $property) {
|
foreach ($object as $name => $property) {
|
||||||
|
|
|
@ -0,0 +1,136 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\Tests\serialization\Kernel;
|
||||||
|
|
||||||
|
use Drupal\Core\TypedData\DataDefinition;
|
||||||
|
use Drupal\Core\TypedData\MapDataDefinition;
|
||||||
|
use Drupal\KernelTests\KernelTestBase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @group typedData
|
||||||
|
*/
|
||||||
|
class MapDataNormalizerTest extends KernelTestBase {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public static $modules = ['system', 'serialization'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The serializer service.
|
||||||
|
*
|
||||||
|
* @var \Symfony\Component\Serializer\Serializer
|
||||||
|
*/
|
||||||
|
protected $serializer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The typed data manager.
|
||||||
|
*
|
||||||
|
* @var \Drupal\Core\TypedData\TypedDataManagerInterface
|
||||||
|
*/
|
||||||
|
protected $typedDataManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function setUp() {
|
||||||
|
parent::setUp();
|
||||||
|
$this->serializer = \Drupal::service('serializer');
|
||||||
|
$this->typedDataManager = \Drupal::typedDataManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests whether map data can be normalized.
|
||||||
|
*/
|
||||||
|
public function testMapNormalize() {
|
||||||
|
$typed_data = $this->buildExampleTypedData();
|
||||||
|
$data = $this->serializer->normalize($typed_data, 'json');
|
||||||
|
$expect_value = [
|
||||||
|
'key1' => 'value1',
|
||||||
|
'key2' => 'value2',
|
||||||
|
'key3' => 3,
|
||||||
|
'key4' => [
|
||||||
|
0 => TRUE,
|
||||||
|
1 => 'value6',
|
||||||
|
'key7' => 'value7',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
$this->assertSame($expect_value, $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test whether map data with properties can be normalized.
|
||||||
|
*/
|
||||||
|
public function testMapWithPropertiesNormalize() {
|
||||||
|
$typed_data = $this->buildExampleTypedDataWithProperties();
|
||||||
|
$data = $this->serializer->normalize($typed_data, 'json');
|
||||||
|
$expect_value = [
|
||||||
|
'key1' => 'value1',
|
||||||
|
'key2' => 'value2',
|
||||||
|
'key3' => 3,
|
||||||
|
'key4' => [
|
||||||
|
0 => TRUE,
|
||||||
|
1 => 'value6',
|
||||||
|
'key7' => 'value7',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
$this->assertSame($expect_value, $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds some example typed data object with no properties.
|
||||||
|
*/
|
||||||
|
protected function buildExampleTypedData() {
|
||||||
|
$tree = [
|
||||||
|
'key1' => 'value1',
|
||||||
|
'key2' => 'value2',
|
||||||
|
'key3' => 3,
|
||||||
|
'key4' => [
|
||||||
|
0 => TRUE,
|
||||||
|
1 => 'value6',
|
||||||
|
'key7' => 'value7',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
$map_data_definition = MapDataDefinition::create();
|
||||||
|
$typed_data = $this->typedDataManager->create(
|
||||||
|
$map_data_definition,
|
||||||
|
$tree,
|
||||||
|
'test name'
|
||||||
|
);
|
||||||
|
return $typed_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds some example typed data object with properties.
|
||||||
|
*/
|
||||||
|
protected function buildExampleTypedDataWithProperties() {
|
||||||
|
$tree = [
|
||||||
|
'key1' => 'value1',
|
||||||
|
'key2' => 'value2',
|
||||||
|
'key3' => 3,
|
||||||
|
'key4' => [
|
||||||
|
0 => TRUE,
|
||||||
|
1 => 'value6',
|
||||||
|
'key7' => 'value7',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
$map_data_definition = MapDataDefinition::create()
|
||||||
|
->setPropertyDefinition('key1', DataDefinition::create('string'))
|
||||||
|
->setPropertyDefinition('key2', DataDefinition::create('string'))
|
||||||
|
->setPropertyDefinition('key3', DataDefinition::create('integer'))
|
||||||
|
->setPropertyDefinition('key4', MapDataDefinition::create()
|
||||||
|
->setPropertyDefinition(0, DataDefinition::create('boolean'))
|
||||||
|
->setPropertyDefinition(1, DataDefinition::create('string'))
|
||||||
|
->setPropertyDefinition('key7', DataDefinition::create('string'))
|
||||||
|
);
|
||||||
|
|
||||||
|
$typed_data = $this->typedDataManager->create(
|
||||||
|
$map_data_definition,
|
||||||
|
$tree,
|
||||||
|
'test name'
|
||||||
|
);
|
||||||
|
|
||||||
|
return $typed_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -58,6 +58,9 @@ abstract class ShortcutResourceTestBase extends EntityResourceTestBase {
|
||||||
'weight' => -20,
|
'weight' => -20,
|
||||||
'link' => [
|
'link' => [
|
||||||
'uri' => 'internal:/admin/content/comment',
|
'uri' => 'internal:/admin/content/comment',
|
||||||
|
'options' => [
|
||||||
|
'fragment' => 'new',
|
||||||
|
],
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
$shortcut->save();
|
$shortcut->save();
|
||||||
|
@ -96,7 +99,9 @@ abstract class ShortcutResourceTestBase extends EntityResourceTestBase {
|
||||||
[
|
[
|
||||||
'uri' => 'internal:/admin/content/comment',
|
'uri' => 'internal:/admin/content/comment',
|
||||||
'title' => NULL,
|
'title' => NULL,
|
||||||
'options' => [],
|
'options' => [
|
||||||
|
'fragment' => 'new',
|
||||||
|
],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
'weight' => [
|
'weight' => [
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\entity_test\Entity;
|
||||||
|
|
||||||
|
use Drupal\Core\Entity\EntityTypeInterface;
|
||||||
|
use Drupal\Core\Field\BaseFieldDefinition;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An entity used for testing map base field values.
|
||||||
|
*
|
||||||
|
* @ContentEntityType(
|
||||||
|
* id = "entity_test_map_field",
|
||||||
|
* label = @Translation("Entity Test map field"),
|
||||||
|
* base_table = "entity_test_map_field",
|
||||||
|
* entity_keys = {
|
||||||
|
* "uuid" = "uuid",
|
||||||
|
* "id" = "id",
|
||||||
|
* "label" = "name",
|
||||||
|
* "langcode" = "langcode",
|
||||||
|
* },
|
||||||
|
* admin_permission = "administer entity_test content",
|
||||||
|
* )
|
||||||
|
*/
|
||||||
|
class EntityTestMapField extends EntityTest {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
|
||||||
|
$fields = parent::baseFieldDefinitions($entity_type);
|
||||||
|
|
||||||
|
$fields['data'] = BaseFieldDefinition::create('map')
|
||||||
|
->setLabel(t('Data'))
|
||||||
|
->setDescription(t('A serialized array of additional data.'));
|
||||||
|
|
||||||
|
return $fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,103 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\Tests\entity_test\Functional\Hal;
|
||||||
|
|
||||||
|
use Drupal\Tests\entity_test\Functional\Rest\EntityTestMapFieldResourceTestBase;
|
||||||
|
use Drupal\Tests\hal\Functional\EntityResource\HalEntityNormalizationTrait;
|
||||||
|
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
|
||||||
|
use Drupal\user\Entity\User;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @group hal
|
||||||
|
*/
|
||||||
|
class EntityTestMapFieldHalJsonAnonTest extends EntityTestMapFieldResourceTestBase {
|
||||||
|
|
||||||
|
use HalEntityNormalizationTrait;
|
||||||
|
use AnonResourceTestTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public static $modules = ['hal'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected static $format = 'hal_json';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected static $mimeType = 'application/hal+json';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function getExpectedNormalizedEntity() {
|
||||||
|
$default_normalization = parent::getExpectedNormalizedEntity();
|
||||||
|
|
||||||
|
$normalization = $this->applyHalFieldNormalization($default_normalization);
|
||||||
|
|
||||||
|
$author = User::load(0);
|
||||||
|
return $normalization + [
|
||||||
|
'_links' => [
|
||||||
|
'self' => [
|
||||||
|
'href' => '',
|
||||||
|
],
|
||||||
|
'type' => [
|
||||||
|
'href' => $this->baseUrl . '/rest/type/entity_test_map_field/entity_test_map_field',
|
||||||
|
],
|
||||||
|
$this->baseUrl . '/rest/relation/entity_test_map_field/entity_test_map_field/user_id' => [
|
||||||
|
[
|
||||||
|
'href' => $this->baseUrl . '/user/0?_format=hal_json',
|
||||||
|
'lang' => 'en',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'_embedded' => [
|
||||||
|
$this->baseUrl . '/rest/relation/entity_test_map_field/entity_test_map_field/user_id' => [
|
||||||
|
[
|
||||||
|
'_links' => [
|
||||||
|
'self' => [
|
||||||
|
'href' => $this->baseUrl . '/user/0?_format=hal_json',
|
||||||
|
],
|
||||||
|
'type' => [
|
||||||
|
'href' => $this->baseUrl . '/rest/type/user/user',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'uuid' => [
|
||||||
|
[
|
||||||
|
'value' => $author->uuid(),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'lang' => 'en',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function getNormalizedPostEntity() {
|
||||||
|
return parent::getNormalizedPostEntity() + [
|
||||||
|
'_links' => [
|
||||||
|
'type' => [
|
||||||
|
'href' => $this->baseUrl . '/rest/type/entity_test_map_field/entity_test_map_field',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function getExpectedCacheContexts() {
|
||||||
|
return [
|
||||||
|
'url.site',
|
||||||
|
'user.permissions',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\Tests\entity_test\Functional\Rest;
|
||||||
|
|
||||||
|
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @group rest
|
||||||
|
*/
|
||||||
|
class EntityTestMapFieldJsonAnonTest extends EntityTestMapFieldResourceTestBase {
|
||||||
|
|
||||||
|
use AnonResourceTestTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected static $format = 'json';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected static $mimeType = 'application/json';
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,152 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\Tests\entity_test\Functional\Rest;
|
||||||
|
|
||||||
|
use Drupal\entity_test\Entity\EntityTestMapField;
|
||||||
|
use Drupal\Tests\rest\Functional\BcTimestampNormalizerUnixTestTrait;
|
||||||
|
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
|
||||||
|
use Drupal\Tests\Traits\ExpectDeprecationTrait;
|
||||||
|
use Drupal\user\Entity\User;
|
||||||
|
|
||||||
|
abstract class EntityTestMapFieldResourceTestBase extends EntityResourceTestBase {
|
||||||
|
|
||||||
|
use BcTimestampNormalizerUnixTestTrait;
|
||||||
|
use ExpectDeprecationTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public static $modules = ['entity_test'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected static $entityTypeId = 'entity_test_map_field';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected static $patchProtectedFieldNames = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Drupal\entity_test\Entity\EntityTestMapField
|
||||||
|
*/
|
||||||
|
protected $entity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The complex nested value to assign to a @FieldType=map field.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected static $mapValue = [
|
||||||
|
'key1' => 'value',
|
||||||
|
'key2' => 'no, val you',
|
||||||
|
'π' => 3.14159,
|
||||||
|
TRUE => 42,
|
||||||
|
'nested' => [
|
||||||
|
'bird' => 'robin',
|
||||||
|
'doll' => 'Russian',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function setUpAuthorization($method) {
|
||||||
|
$this->grantPermissionsToTestedRole(['administer entity_test content']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function createEntity() {
|
||||||
|
$entity = EntityTestMapField::create([
|
||||||
|
'name' => 'Llama',
|
||||||
|
'type' => 'entity_test_map_field',
|
||||||
|
'data' => [
|
||||||
|
static::$mapValue,
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
$entity->setOwnerId(0);
|
||||||
|
$entity->save();
|
||||||
|
return $entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function getExpectedNormalizedEntity() {
|
||||||
|
$author = User::load(0);
|
||||||
|
return [
|
||||||
|
'uuid' => [
|
||||||
|
[
|
||||||
|
'value' => $this->entity->uuid(),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'id' => [
|
||||||
|
[
|
||||||
|
'value' => 1,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'name' => [
|
||||||
|
[
|
||||||
|
'value' => 'Llama',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'langcode' => [
|
||||||
|
[
|
||||||
|
'value' => 'en',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'created' => [
|
||||||
|
$this->formatExpectedTimestampItemValues((int) $this->entity->get('created')->value),
|
||||||
|
],
|
||||||
|
'user_id' => [
|
||||||
|
[
|
||||||
|
'target_id' => (int) $author->id(),
|
||||||
|
'target_type' => 'user',
|
||||||
|
'target_uuid' => $author->uuid(),
|
||||||
|
'url' => $author->toUrl()->toString(),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'data' => [
|
||||||
|
static::$mapValue,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function getNormalizedPostEntity() {
|
||||||
|
return [
|
||||||
|
'name' => [
|
||||||
|
[
|
||||||
|
'value' => 'Dramallama',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'data' => [
|
||||||
|
0 => static::$mapValue,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function getExpectedUnauthorizedAccessMessage($method) {
|
||||||
|
if ($this->config('rest.settings')->get('bc_entity_resource_permissions')) {
|
||||||
|
return parent::getExpectedUnauthorizedAccessMessage($method);
|
||||||
|
}
|
||||||
|
|
||||||
|
return "The 'administer entity_test content' permission is required.";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function getExpectedCacheContexts() {
|
||||||
|
return ['user.permissions'];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue