Support for Drupal 7 is ending on 5 January 2025—it’s time to migrate to Drupal 10! Learn about the many benefits of Drupal 10 and find migration tools in our resource center.
Problem/Motivation
Quoting Berdir from #2300677-153: JSON:API POST/PATCH support for fully validatable config entities:
But unless I'm blind (which is possible), we have no assertion that the title change was actually *persisted*. The test is happy as long as there is a 200 response, which makes sense because the patch() method isn't really doing anything.
Berdir is right, our test coverage should be stricter.
Proposed resolution
For both POST
and PATCH
requests, do the following:
- Decode the response body. This should be the updated/created entity's normalization.
- Load the updated/created entity from entity storage, unchanged. Normalize it. This should be identical to the response body.
- Assert that the decoded
PATCH
/POST
request body is a strict subset of the full normalization of the entity, which we got from steps 1+2.
Remaining tasks
Roll patch.
User interface changes
None.
API changes
None.
Data model changes
None.
Comment | File | Size | Author |
---|---|---|---|
#30 | rest_test_patch_and_post-2882717-30.patch | 8.64 KB | Wim Leers |
Comments
Comment #2
Wim LeersComment #3
Wim LeersComment #4
Wim LeersAdded to #2852860 in #2852860-36: REST: top priorities for Drupal 8.4.x.
Comment #6
Wim LeersEvery single
hal+json
format test is failing. Probably due to_links
.Also, the others that fail are:
Comment
EntityTestLabel
EntityTest
Shortcut
Term
User
Anybody interested in pushing this further?
Comment #7
dawehnerI might give it a try later ..., let's see :(
Comment #8
Wim LeersInteresting that the number of failures is different on 8.3.x vs 8.4.x!
Comment #9
dawehnerI tried to do some research why it fails ... and well, it turns out the output format of an entity with an entity reference is different to its input for hal_json. Just compare
the entity_id entry with the bits produced in
\Drupal\Tests\hal\Functional\EntityResource\Comment\CommentHalJsonTestBase::getExpectedNormalizedEntity
It feels like
\Drupal\Tests\hal\Functional\EntityResource\Comment\CommentHalJsonTestBase::getNormalizedPostEntity
is implemented in the wrong way, as it needs some transformation applied, OR the new testcode should call out to getExpectedNormalizedEntity instead.Comment #10
Wim LeersThis fixes a whole bunch of the failures, which were happening because there is a discrepancy between what the denormalizers accept vs the normalizers generate: we accept
['type' => 'blah']
because Field API magic automatically maps this to['type' => ['value' => 'blah']]
for single-property field types. Some of the request bodies were using that "feature". Simply changing the request body to their "fully expressed" equivalents solves many of the failures (because it means we can actually check that the request body is a subset of the response body).Comment #11
Wim LeersSimilar, yet different:
UserResourceTestBase::getNormalizedPostEntity()
was using a randomly generated name. This meant that every call to that method returned a different name. Which makes testing impossible. It's also the only entity REST test to do this. So, shifting to a hardcoded name, just like all other tests, is enough.Comment #12
Wim LeersThe
Term
tests are failing in a different place: loading the entity from storage and then normalizing it yields a different result than the normalization we get from decoding the serialization in the response body.Why?
Turns out that
causes
$term->fields->parent
to be initialized to have this value:'target_id' => 0
. So that's also what we see in the PATCH/POST response body:['parent' => [['target_id' => 0]]
.But if we load a stored term entity,
$term->fields->parent
does not exist at all, because there's no value set for it. Hence it's normalized to['parent' => []]
.Similar story for
$term->description
.A potential work-around is for us to explicitly specify a
target_id
and adescription
in our PATCH/POST request body. But turns out that only solves thedescription
case — apparently specifyingtarget_id = 0
specifically still causes a['parent' => []]
normalization! Actually, even settingtarget_id = 1
causes this. Probably because is configured to use custom storage:But then I realized… we already have an issue for this: #2543726: Make $term->parent behave like any other entity reference field, to fix REST and Migrate support and de-customize its Views integration.
Comment #15
Wim LeersThe remaining problems with
User
are field-access related. The new assertions being added by this patch contain a simple but severe bug:This is normalizing the given entity. Content entity normalization checks field access. Field access uses the current user by default. In this environment, there isn't a current user, so it falls back to the anonymous user. That explains why
User*AnonTest
are passing, but not basic_auth/cookie tests!So, we need this to normalize with the same user account as our actual tests. That's all :)
Comment #17
Wim Leers#12 should bring it down to 10 failures. #15 to 4.
#8:
The reason is simple:
\Drupal\Tests\datetime\Functional\EntityResource\EntityTest\EntityTestDateonlyTest
and\Drupal\Tests\datetime\Functional\EntityResource\EntityTest\EntityTestDatetimeTest
only exist in 8.4.x! And since #10 fixed the failures forEntityTest
, those failures were also fixed.That now only leaves
CommentHalJson*Test
andItemHalJson*Test
.Comment #19
Wim LeersThe failing
CommentHalJson*Test
: for some reason theentity_id
field is missing in POST response bodies (so, @dawehner, thank you very much for #9, but AFAICT that's not what is happening :O). I tried reproducing this manually: POSTing a HAL+JSON comment, and stepping through the normalization process to observe whyentity_id
was missing… only to observe that it's actually generated just fine :/ I have no explanation for this. Other than: WTF?! — at least for now :)Similarly, the failing
ItemHalJson*Test
the problem is similar: thefid
field is missing in POST response bodies. I haven't done any step-by-step debugging there yet.If somebody else could push this one over the finish line, that'd be wonderful!
Comment #20
Wim LeersThis is blocking #2300677: JSON:API POST/PATCH support for fully validatable config entities, which is major, hence this is major too.
Comment #21
dawehnerWell, if you have a look at
\Drupal\hal\Normalizer\EntityReferenceItemNormalizer::normalize
you'll not see the entity_id to be included, also see\Drupal\Tests\hal\Functional\EntityResource\HalEntityNormalizationTrait::applyHalFieldNormalization
:Comment #22
Wim LeersComment #23
Wim Leers#21 Turns out that's it. I wonder how the hell I managed to observe something else in my manual testing in #19 then… I think I might've confused
entity_id
andid
? (That is quite confusing inComment
entities.)With that confirmed, let's work on the fix. The root cause of this problem is:
Using
assertArraySubset()
is simply a bad assumption: this assumption only holds for the simplest possible normalization, i.e. the default normalization that Drupal uses. But it breaks down for HAL+JSON, as well as for e.g. JSON API.Then a better way to assert that the sent values are indeed present is by iterating over all fields in the normalization that's being POSTed and then comparing that with the actually stored values.
This reroll brings us to just 3 failures. The remaining 3 failures are in
UserHal*Test::testPatch()
. Investigating…Comment #24
Wim LeersThose 3 remaining failures had the same root cause as #15, I just only thought of fixing it for
testPost()
in #15. This then fixes it fortestPatch()
too.This is ready for final review.
Comment #26
tedbowThis looks good to me.
Nice work @Wim Leers and @dawehner
Comment #27
dawehnerNice!
Comment #28
Wim LeersWhew, so glad to have this be RTBC! It'll be great to finally start pushing #2300677: JSON:API POST/PATCH support for fully validatable config entities forward again, after @dawehner's incredible effort to get that going again!
Comment #29
catch"and that the response body the". Is it missing 'contains'?
Otherwise looks good though.
Comment #30
Wim LeersIndeed that word is missing. It's present for the
PATCH
test coverage. Fixed :)Comment #31
catchCommitted/pushed to 8.4.x, thanks!
Comment #33
Wim LeersThis unblocked #2300677: JSON:API POST/PATCH support for fully validatable config entities, yay!
Comment #35
Wim LeersAnd now we're removing the temporary work-around that this introduced: #2942522: Follow-up for #2543726: remove work-around in POST test coverage for bug in Term entity type that has been fixed — hurray!