When an entities has an ER field with dangling reference (for instance if the targeted entity has been deleted but the referencing entity is not up-to-date), you get a "clean" result when GETting the entity through JSON API : the inexistant entity is filtered out.
But if you try to PATCH this entity by providing a completely unrelated field (such as title, for node) without providing any other field (because, PATCH, you know...) this will fail with a validation error stating that entity has invalid references.
I get that it's completely "normal" behavior given the design of Entity API, yet it's quite confusing that JSON API "cleans" dangling references when GETting the entity but not when PATCHing.
The only workaround I have as an API consumer (beside patching JSON API itself) is to make sure that I also provide the value of the ER field even if I don't mean to change it (which could lead to race condition if the ER field is updated by another consumer at the same time).
A way to solve this would be to ensure we PATCH the "cleaned" entity, but I'm not sure if it's acceptable to silently update the ER field as it was no intended to by the original request. From another perspective, when you edit a node through Drupal UI, any ER field with dangling reference will be silently "corrected" (not sure of this if the user has no write access on the given field, though).
I'll try to provide a failing test case which exhibit the behavior soon.
| Comment | File | Size | Author |
|---|---|---|---|
| #18 | 2968972-18.patch | 5.42 KB | wim leers |
| #18 | interdiff.txt | 752 bytes | wim leers |
| #17 | Screen Shot 2018-06-15 at 15.46.18.png | 120.99 KB | wim leers |
| #15 | interdiff_9-15.txt | 1.85 KB | axle_foley00 |
| #15 | 2968972-15.patch | 4.54 KB | axle_foley00 |
Comments
Comment #2
wim leersThanks for the extremely clear issue summary! 🙏
If you could do that, I can promise you that this will get a higher priority in getting fixed!
Note to self: we should check whether the same behavior exists in core's
rest.module.Comment #3
axle_foley00 commentedI made an attempt at writing the failing test case for the behaviour mentioned by @garphy. See attached file. Hope this helps.
Comment #4
axle_foley00 commentedComment #5
gabesulliceThanks @axle_foley00! I'll let @Wim Leers review it since I contributed a bit to the test patch.
WRT, the proposed solution:
I'd like to shy away from "correcting" the data. Instead, I think we can simply filter the constraint violations by the submitted fields, so only PATCHed fields return a 422 and associated errors.
This can be fixed in 1.x.
Comment #7
gabesulliceFailure was unrelated to the patch, requeued tests
Comment #8
wim leersWOOOT! Thank you so much, @axle_foley00!
#3 seems to trigger the failure we want: it reproduces the reported bug!
I agree. That'd be magic. Magic gets in the way of clarity.
Agreed! Regarding how to implement that:
\Drupal\jsonapi\Controller\EntityResource::validate()already does:That calls
\Drupal\Core\Entity\EntityConstraintViolationListInterface::filterByFieldAccess(). I think we should be able to use\Drupal\Core\Entity\EntityConstraintViolationListInterface::filterByFields()for what we need here. But the challenge is: how to get the list of received fields? It looks like\Drupal\jsonapi\Controller\EntityResource::patchIndividual()may already contain this information :)Comment #9
axle_foley00 commented@wim-leers/@gabesullice:
Here is the first attempt at the patch. It's still not passing though, but figured I could get some feedback to see if I'm on the right track.
Comment #10
wim leersLooking good! 👍
Why make this new parameter optional? Let's make it required, so that we always only validate the received fields.
I'm guessing that you went with this because in the case of
EntityResource::createIndividual(), all fields should be validated?Comment #11
axle_foley00 commentedSo yes, In discussing it with @GabeSullice I wasn't sure if the change would cause any issues with other calls to the
validate()method, so we figured it would be the safest way to make the change. Would it cause an issue inEntityResource::createIndividual()or anywhere else?Comment #12
wim leersRight. But the patch is now only updating the
::validate()call inEntityResource::patchIndividual(). I'm wondering if it should also update those in:::createRelationship()::patchRelationship()::deleteRelationship()Oh wait, those are always just modifying a single field: the entity reference field representing the relationship you're modifying. Of course!
Then yes, +1 to #10! Now we just need those tests brought back, and once this is green, it's ready! :) 🎉
Comment #13
wim leers@gabesullice just pointed out how similar this is to #2821077: PATCHing entities validates the entire entity, also unmodified fields, so unmodified fields can throw validation errors in core REST; it's essentially the same solution!
Comment #14
wim leersActually, on second thought, #2821077: PATCHing entities validates the entire entity, also unmodified fields, so unmodified fields can throw validation errors goes quite a bit further. It tracks which fields were actually changed. It's a much more complete solution.
The patch here considers every received field a changed field. But as #2821077: PATCHing entities validates the entire entity, also unmodified fields, so unmodified fields can throw validation errors shows, that's inaccurate. It's perfectly possible to send the current value for a field in a
PATCHrequest; that field is then not being changed.That being said, the patch here, while simpler and not as complete, still is a step forward. We should still do it.
Comment #15
axle_foley00 commented@Wim-leers/@gabesullice, thanks for pointing out that #2821077 PATCHing entities validates the entire entity, also unmodified fields, so unmodified fields can throw validation errors was similar. I was looking through and noticed that you had done the following in the patch for the
validate()method:$violations->filterByFields(array_diff(array_keys($entity->getFieldDefinitions()), $field_names));So I tried that, but the test is still not passing. I'm getting the following failure:
If I instead use what I had before:
$violations->filterByFields($field_names);the test still fails as before with the 422 Unprocessable Entity code:
Comment #16
wim leersI've been pushing #2821077: PATCHing entities validates the entire entity, also unmodified fields, so unmodified fields can throw validation errors forward; now taking a look at this. I'd like to get this fixed today.
Comment #17
wim leersApparently

\Drupal\jsonapi\ResourceType\ResourceType::getRelatableResourceTypes()contains non-numerically keyed values for thefield_issuefield, and that's what's causing the failure:Comment #18
wim leersEasy fix :)
Comment #19
axle_foley00 commented@Wim-leers: Thanks! Your patch worked for me. And the test now passes.
Comment #20
wim leersI think it's actually probably better to port the test coverage from #2821077: PATCHing entities validates the entire entity, also unmodified fields, so unmodified fields can throw validation errors.
And just now, @tstoeckler posted a comment at #2821077-95: PATCHing entities validates the entire entity, also unmodified fields, so unmodified fields can throw validation errors showing how this could cause a security vulnerability… So postponing this issue on that one for now.
Comment #21
wim leersSince #2821077: PATCHing entities validates the entire entity, also unmodified fields, so unmodified fields can throw validation errors is now practically RTBC, we can unpostpone this!
Comment #22
wim leersWell, actually, since
\Drupal\Tests\jsonapi\Functional\ResourceTestBasereuses core'srest_testmodule, this is sort of painful to do in the JSON API module… ideally the core patch would land, and then JSON API would follow suit.In the mean time, it's okay to use the patch above. It does not suffer from that (very very obscure) information disclosure vulnerability pointed out in #2821077-95: PATCHing entities validates the entire entity, also unmodified fields, so unmodified fields can throw validation errors, because this patch does not limit validation to fields that are actually being modified; it only limits validation to fields that are actually being sent. That's safe.
Let's get this committed; it's a step forward. We should still port over #2821077: PATCHing entities validates the entire entity, also unmodified fields, so unmodified fields can throw validation errors though. Creating an issue for that…
Comment #23
wim leersDone: #2979945: Port #2821077 to JSON API: limit validation of fields in PATCH requests to fields *changed*.
Comment #24
wim leersI'd like another maintainer to review & commit this, to double-check my analysis :)
Comment #29
gabesulliceLGTM! Thanks everyone!
@axle_foley00, congratulations on your first JSON API credit!
Comment #30
axle_foley00 commentedGreat! Thanks @gabsullice and @wim-leers. Thanks for your help and thoughtful feedback. It made for a good first experience as well.
Comment #31
wim leers@axle_foley00 Thank you! And it's great to hear that it was a good first experience! ❤️ :)
Comment #33
rpayanmMoving to Drupal core's issue queue.
I'm working on https://www.drupal.org/project/drupal/issues/3122113