Problem/Motivation

If you have an entity (a), that references another entity (b) and (b) is a content entity that does not have a uuid field, then you will get this error when you request entity (a) in the hal_json format:


InvalidArgumentException: Field uuid is unknown. in Drupal\Core\Entity\ContentEntityBase->getTranslatedField() (line 471 of core/lib/Drupal/Core/Entity/ContentEntityBase.php).
Drupal\Core\Entity\ContentEntityBase->get('uuid') (Line: 84)
Drupal\hal\Normalizer\ContentEntityNormalizer->normalize(Object, 'hal_json', Array) (Line: 128)
Symfony\Component\Serializer\Serializer->normalize(Object, 'hal_json', Array) (Line: 70)
Drupal\hal\Normalizer\EntityReferenceItemNormalizer->normalize(Object, 'hal_json', Array) (Line: 128)
Symfony\Component\Serializer\Serializer->normalize(Object, 'hal_json', Array) (Line: 98)
Drupal\hal\Normalizer\FieldNormalizer->normalizeFieldItems(Object, 'hal_json', Array) (Line: 34)
Drupal\hal\Normalizer\FieldNormalizer->normalize(Object, 'hal_json', Array) (Line: 128)
Symfony\Component\Serializer\Serializer->normalize(Object, 'hal_json', Array) (Line: 96)
Drupal\hal\Normalizer\ContentEntityNormalizer->normalize(Object, 'hal_json', Array) (Line: 128)
Symfony\Component\Serializer\Serializer->normalize(Object, 'hal_json', Array) (Line: 101)
Symfony\Component\Serializer\Serializer->serialize(Object, 'hal_json') (Line: 119)
Drupal\rest\RequestHandler->Drupal\rest\{closure}() (Line: 574)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 120)
Drupal\rest\RequestHandler->handle(Object, Object)
call_user_func_array(Array, Array) (Line: 123)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->Drupal\Core\EventSubscriber\{closure}() (Line: 574)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 124)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->wrapControllerExecutionInRenderContext(Array, Array) (Line: 97)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->Drupal\Core\EventSubscriber\{closure}()
call_user_func_array(Object, Array) (Line: 144)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 64)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 57)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 47)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 47)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 50)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 31)
Drupal\gc_api\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 628)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)

This is happening because in EntityReferenceItemNormalizer we have this:

$context['included_fields'] = array('uuid');

// Normalize the target entity.
$embedded = $this->serializer->normalize($target_entity, $format, $context);

and then in ContentEntityNormalizer we have this:

// If the fields to use were specified, only output those field values.
if (isset($context['included_fields'])) {
  $fields = array();
  foreach ($context['included_fields'] as $field_name) {
    $fields[] = $entity->get($field_name);
  }
}
else {
  $fields = $entity->getFields();
}

Since entity (b) does not have uuid then $entity->get('uuid') throws a fatal error.

Proposed resolution

Check to see if the referenced entity has a uuid field before attempting to access it.

Remaining tasks

  1. Write Tests
  2. Write Patch

User interface changes

None.

API changes

Fixes a major bug in the hal module.

Data model changes

None.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

davidwbarratt created an issue. See original summary.

davidwbarratt’s picture

Status: Active » Needs review
FileSize
2 KB

A failing test...

Status: Needs review » Needs work

The last submitted patch, 2: missing_uuid-2835576-2.patch, failed testing.

davidwbarratt’s picture

Status: Needs work » Needs review
FileSize
3.45 KB
1.97 KB
davidwbarratt’s picture

Issue summary: View changes
davidwbarratt’s picture

davidwbarratt’s picture

davidwbarratt’s picture

davidwbarratt’s picture

Title: InvalidArgumentException: Field uuid is unknown. in Drupal\Core\Entity\ContentEntityBase->getTranslatedField() (line 471 of core/lib/Drupal/Core/Entity/ContentEntityBase.php). » InvalidArgumentException: Field uuid is unknown. in Drupal\Core\Entity\ContentEntityBase->getTranslatedField()

Version: 8.3.x-dev » 8.4.x-dev

Drupal 8.3.0-alpha1 will be released the week of January 30, 2017, which means new developments and disruptive changes should now be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Wim Leers’s picture

Priority: Major » Normal
Issue tags: +Needs tests

At minimum, this needs tests.

I know that UUIDs are not strictly required, but that was mostly intended for config entities AFAIK… because if content entities don't have UUIDs, they cannot be synced, so they cannot take advantage of automatic syncing and the Workflow initiative for example.

Demoting to Normal, precisely because it's extremely rare to have content entities that don't have UUIDs.

Wim Leers’s picture

Status: Needs review » Needs work
shadcn’s picture

Assigned: Unassigned » shadcn

@Wim Leers I'll write the tests.

Wim Leers’s picture

@arshadcn++

shadcn’s picture

So looking at this again. I think the tests in #2 and #4 combined with the tests we wrote in #2843752: EntityResource: Provide comprehensive test coverage for Item entity should cover this (I saw the same issue when writing the tests for Item entity).

What do you think @Wim Leers?

Wim Leers’s picture

Sorry, lost track of this issue! So much activity in the REST issue queue that this already was on the second issue page for all API-first core modules :)

I think you're right! #2843752: EntityResource: Provide comprehensive test coverage for Item entity already updated EntityResourceTestBase to support entity types without UUIDs. So we already have test coverage that proves REST works for entity types without UUIDs. This issue then should be concerned only with was originally reported: the HAL normalization of a content entity that references another content entity and that referenced content entity does not have a UUID.

But let's confirm this by uploading the patch in #4 without the fix: that should fail.


In the mean time, here's a review already:

  1. +++ b/core/modules/hal/tests/src/Functional/EntityResource/EntityTest/EntityTestHalJsonRelatedTest.php
    @@ -0,0 +1,89 @@
    +    // Reload entity so that it has the new field.
    +    $this->entity = $this->entityStorage->loadUnchanged($this->entity->id());
    +
    +    // 'Entity Test without label' is also missing a UUID.
    +    // @see https://www.drupal.org/node/2835576
    +    $entity_test_no_label = EntityTestNoLabel::create([
    +      'name' => 'Baby Llama',
    +      'type' => 'entity_test_no_label',
    +    ]);
    +    $entity_test_no_label->save();
    +
    +    // Set a default value on the field.
    +    $this->entity->set($field_name, [
    +      'target_id' => $entity_test_no_label->id(),
    +    ]);
    +
    +    $this->entity->save();
    

    Let's move all of this to ::createEntity(), that will be much clearer.

    s/also//

    Then the "Set a default value on the field." stuff can be done in a far less confusing way.

  2. +++ b/core/modules/hal/tests/src/Functional/EntityResource/EntityTest/EntityTestHalJsonRelatedTest.php
    @@ -0,0 +1,89 @@
    +      'required' => FALSE,
    

    And then this can become required instead of optional.

  3. +++ b/core/modules/hal/tests/src/Functional/EntityResource/EntityTest/EntityTestHalJsonRelatedTest.php
    @@ -0,0 +1,89 @@
    +    $normalized['_embedded'][$this->baseUrl . '/rest/relation/entity_test/entity_test/field_entity_test_no_label'] = [
    +      [
    +        '_links' => [
    +          'self' => [
    +            'href' => '',
    +          ],
    +          'type' => [
    +            'href' => $this->baseUrl . '/rest/type/entity_test_no_label/entity_test_no_label',
    +          ],
    +        ],
    +        'lang' => 'en',
    +      ],
    +    ];
    

    What's key here is that uuid is missing. That's what this issue is changing. But… devil's advocate… then how are you supposed to fetch this referenced entity? The whole point is that referenced entities are "embedded" automatically, but the embedding is limited to just the UUID field. But if there's no UUID field… then how are you supposed to find the referenced entity?

    Should this then fall back to embedding the ID field?

    This is why it's so hugely problematic to have the HAL normalization require UUIDs, and then some entity types not having UUIDs at all!

Wim Leers’s picture

But let's confirm this by uploading the patch in #4 without the fix: that should fail.

Done.

(Had to rebase, but git did that all automatically for me :))

Wim Leers’s picture

Status: Needs review » Needs work

Now back to NW for the remarks in #16.

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.0-alpha1 will be released the week of July 31, 2017, which means new developments and disruptive changes should now be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.0-alpha1 will be released the week of January 17, 2018, which means new developments and disruptive changes should now be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.6.x-dev » 8.7.x-dev

Drupal 8.6.0-alpha1 will be released the week of July 16, 2018, which means new developments and disruptive changes should now be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.7.x-dev » 8.8.x-dev

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

larowlan’s picture

Version: 9.4.x-dev » 10.0.x-dev
Status: Needs work » Needs review
Issue tags: -Needs tests +Bug Smash Initiative
FileSize
2.76 KB

This came up as a daily triage target

Here's a re-roll of the test only-patch.

The actual patch doesn't apply anymore because the code there is different, and I suspect the current code-path doesn't contain this bug.

If this passes, we can close as works as designed.

larowlan’s picture

larowlan’s picture

Status: Needs review » Needs work

The last submitted patch, 31: 2835576-32-fail.patch, failed testing. View results

larowlan’s picture

Ok, so this fails, but differently

The old fail was Field uuid is unknown

The new fail is just that the expected value differs.

I still think this now works as designed by way of the array_intersect_key added in #2871591: Allow ComplexData in TypedData to specify computed properties that should be exposed in normalization and other contexts

@Wim Leers or @shadcn could you confirm?

Spokje’s picture

Project: Drupal core » HAL
Version: 10.0.x-dev »
Component: hal.module » Code

The hal module has moved out of Drupal Core and into a Contrib Module.
Moving this issue to the Contrib Module queue.

Spokje’s picture

Project: HAL » Hypermedia Application Language (HAL)
Version: » 1.0.x-dev

Let's try that again...

larowlan’s picture