Problem/Motivation
The existing test coverage and the existing code is unable to deal with entity types that have no canonical
link template:
-
When POSTing content entities without a
canonical
link template, such as theparagraph
entity type (https://www.drupal.org/project/paragraphs), you get a 500 response due to an unhandled exception. Because theEntityResource::post()
method assumes that the created entity has acanonical
URL and tries to return it.Conclusion:
EntityResource::post()
is wrong.(First reported in #2836121: PHP error when POSTing content entities without a "canonical" link template.)
-
We have the following assertions in
\Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase::testPost
:// DX: 404 when resource not provisioned, but HTML if canonical route. $response = $this->request('POST', $url, $request_options); if ($has_canonical_url) { $this->assertSame(404, $response->getStatusCode()); $this->assertSame(['text/html; charset=UTF-8'], $response->getHeader('Content-Type')); } else { $this->assertResourceErrorResponse(404, 'No route found for "GET ' . str_replace($this->baseUrl, '', $this->getUrl()->setAbsolute()->toString()) . '"', $response); }
This fails for entity types with no canonical routes because the
Content-Type
is stilltext/html; charset=UTF-8
.Conclusion: the "else" case was never actually exercised.
In other words: the bug in both the controller logic and in the test coverage exists because nothing was exercising either one! Hence they must be solved in tandem.
Proposed resolution
- Update the
EntityResource::post()
code to first check if an entity type actually has acanonical
link template, and only then return the canonical URL. - Add
EntityTestLabel
test coverage, because it has nocanonical
link template, so that we are then testing the "else" case.
Remaining tasks
None.
User interface changes
None.
API changes
None.
Data model changes
None.
Comment | File | Size | Author |
---|---|---|---|
#21 | 2853211-21.patch | 21.53 KB | Wim Leers |
#13 | assert_text_html-2853211-13.patch | 7.83 KB | Wim Leers |
#14 | entity_test_label-only.patch | 11.46 KB | Anonymous (not verified) |
#18 | entity_test_label-only-2853211-17.patch | 11.47 KB | Anonymous (not verified) |
#18 | 2853211-17.patch | 19.17 KB | Anonymous (not verified) |
Comments
Comment #2
Wim LeersThat's the point.
The point is that
\Drupal\Tests\rest\Functional\ResourceTestBase::assertResourceResponse()
and\Drupal\Tests\rest\Functional\ResourceTestBase::assertResourceErrorResponse()
are the methods you should use 95% of the time. They make things simple, short, clean, nice.Whenever you need to deviate from that, suffer the consequences. Usually this is because of an unfortunate edge case or a bug! So let it be long, ugly, in-your-face.
Comment #3
shadcn CreditAttribution: shadcn at Chapter Three commentedHmm, I'll need to try and test another entity with no canonical route to confirm.
There's also the following in
\Drupal\Tests\rest\Functional\ResourceTestBase::assertResourceResponse
:)Comment #4
Wim LeersOh hahah, that's because #2805281: ?_format=hal_json error responses are application/json, yet should be application/hal+json should have simplified that but forgot to do so! That definitely should be improved, of course :)
Comment #5
shadcn CreditAttribution: shadcn at Chapter Three commented@Wim Leers, so a follow up question, wouldn't entities with no canonical url return
text/html; charset=UTF-8
as well? Shouldn't they?Comment #6
Wim LeersNo, because:
\Symfony\Component\HttpKernel\Exception\NotFoundHttpException
exception is thrown by the routing system.json
(orhal_json
, or …), which is one of the serialization formats.NotFoundHttpException
(404) is caught by\Drupal\serialization\EventSubscriber\DefaultExceptionSubscriber
, which converts it to a JSON/HAL+JSON/… response.Comment #7
shadcn CreditAttribution: shadcn at Chapter Three commented@Wim Leers Thanks. A follow up question. Why do test for
text/html; charset=UTF-8
for entities with canonical url but not for non-canonical?Comment #8
Wim Leers#7: because
$this->assertResourceErrorResponse()
takes care of asserting theContent-Type
response header for us :) That's the thing: anything that behaves in a strange/unexpected way is going to have additional/ugly looking assertions. The fact that we get a HTML response is strange. So the assertions stand out.Comment #9
shadcn CreditAttribution: shadcn at Chapter Three commentedUpdating the title to reflect what we need to change here. See also https://www.drupal.org/node/2843752#comment-11963791.
Comment #10
shadcn CreditAttribution: shadcn at Chapter Three commentedComment #11
shadcn CreditAttribution: shadcn at Chapter Three commentedOK. Let's try this.
Comment #12
shadcn CreditAttribution: shadcn at Chapter Three commentedDidn't mean to comment out the uuid tests :)
Comment #13
Wim LeersHah, interesting! Apparently
testPost()
didn't even need$has_canonical_url
at all then!These aren't consistent with the comments in
testGet()
. Fixed.Same remark as above, and exceeds 80 cols. Fixed.
This comment is no longer accurate. Fixed.
The "if" case" now is missing an assertion for the content type.
And AFAICT, this can use the original assertion!
Fixed.
Interesting, so we never actually hit the "else" case here. Great catch.
However, I suspect this is because we have no test coverage for entity types without a
canonical
link template.I think that before we make this change, we should add test coverage for one of these, e.g.
\Drupal\entity_test\Entity\EntityTestLabel
, so that we can be certain that we really don't need this.(We do e.g. have
ConfigTest
, which also doesn't have acanonical
link template, but that one only supportsGET
, because we don't yet support modifications to config entities.)I think this was clearer before. Although the bit about "plain text or HTML" should be appended. Fixed.
Unnecessary change, reverting.
Correct change, but made consistent with comments in
testGet()
again. Fixed.The "if" case" now is missing an assertion for the content type.
And AFAICT, this can use the original assertion!
Fixed.
I fixed all my own nits. You only need to address point 6.
Comment #14
Anonymous (not verified) CreditAttribution: Anonymous commented#13 amazing review!
I wanted to make a new issue for point 6, but it is heavily dependent on the current patch. Therefore, I attached here. Patch entity_test_label-only.patch is interdiff between 13 and 14 patches.
Also patch has
because testPost() causes a failure here:
I'm absolutely not sure in my skill, but if test is correct, hence
PATCH
work normal with point 6 change.Comment #17
Anonymous (not verified) CreditAttribution: Anonymous commentedoops, sorry!
Comment #18
Anonymous (not verified) CreditAttribution: Anonymous commentedsorry again :(
Comment #21
Wim Leers#14: heh glad you liked my review :)
The failures of the test-only patch in #18 prove that indeed the existing assertions are wrong. And like I said in #13.6: we never actually hit the "else" case with the existing tests. This new test does and fails. Perfect.
Thanks to this additional test coverage, this guarantees that the changes made by this patch are actually correct.
#18 looks perfect. And #18 is identical to #13, the only changes are test coverage expansions. Unfortunately, perfect with one exception:
This we can't have.
You described in #14 why this is failing. In fact, you have the answer right there in the exception that you quoted! :) The root cause is that
EntityTestLabel
has nocanonical
link template (which is why we're testing this entity type in the first place, see #13.6), yetEntityResource::post()
andEntityResourceTestBase::testPost()
ignore this, and assume that it's possible to always send aLocation
header. But that's not the case. Also note that https://tools.ietf.org/html/rfc2616#section-10.2.2 says "SHOULD", not "MUST" (about providing a location in the response).Comment #22
Wim Leers(I RTBC'ed the work by @arshadcn + @vaplas, and made only trivial changes in #21 to address the last small remark. I think that's acceptable, given that I'm a REST module maintainer.)
Comment #23
alexpottDoesn't this mean we're fixing a bug here? The only run-time change in this patch was introduced in #21 - the comment that rtbc'd the patch. Setting back to needs review just to make sure that this bit gets further review. It looks sensible to me - but perhaps the issue title or summary needs an update to detail what is being fixed. At the moment it sounds all about tests.
Comment #24
Wim LeersYes, the title/IS need to be updated. This is fixing a bug in test coverage, which led us to discover a small bug in
EntityResource
, i.e. outside test coverage.Comment #25
Wim LeersEh… turns out that the bug that I fixed in #21 was reported at #2836121: PHP error when POSTing content entities without a "canonical" link template.
I'll migrate the necessary information onto this one. This issue includes the test coverage already.
Comment #26
Wim LeersIS completely rewritten. Title updated.
Comment #27
Wim LeersThis blocks the https://www.drupal.org/project/paragraphs contrib module because it's impossible to
POST
paragraph
content entities: they result in an unhandled exception and hence a 500 response.Comment #28
Wim LeersThis also blocks #2843752: EntityResource: Provide comprehensive test coverage for Item entity.
Comment #29
Wim LeersBack to RTBC.
Comment #30
Wim LeersClosed #2836121: PHP error when POSTing content entities without a "canonical" link template as a duplicate. Migrated its tags (accidentally did that in #29).
Comment #31
cosmicdreams CreditAttribution: cosmicdreams commentedI believe I'm running into this issue when using the Entity Pilot git module. My specific issue is that whenever it uses TypeLinkManager::getTypeInternalIds is called the $type_uri that is supplied is like
/rest/type/taxonomy_term/category
whereas the type_uri that is retrieved from TypeLinkManager::getTypes looks likehttp://:/rest/type/taxonomy_term/product_category
So I've been investigating whether the types are being stored incorrectly or retrieved incorrectly. I guess i don't know what my expectations should be because to me the json looks right.
but somehow when json_decode is finished with decoding that json it converts to
/rest/type/taxonomy_term/category
Which leads me to this issue and how it appears that this canonical url needs to be fixed. Is there any chance we can get this patch working with Drupal 8.2 / 8.3 ?
Comment #32
Wim Leers@cosmicdreams: what you are describing is all specific to the HAL module. AFAICT it's completely unrelated to this issue. Please open a new issue against the
hal.module
component.Comment #33
cosmicdreams CreditAttribution: cosmicdreams commentedThanks Wim. Will do.
Comment #34
Wim LeersThank you! Will respond to that issue as soon as you open it. Please make sure to provide steps to reproduce, then I'm certain we can get it fixed very quickly.
Comment #35
cosmicdreams CreditAttribution: cosmicdreams commentedCool, here ya go #2859641: Need to setup proper Request object for console commands
Comment #37
Anonymous (not verified) CreditAttribution: Anonymous commentedLast fail is random fail. Retest is green. Separate issue #2859704: Intermittent segfaults on DrupalCI (some "did not complete due to a fatal error" with no additional info) and back to RTBC.
Comment #38
shadcn CreditAttribution: shadcn at Chapter Three commentedYeah let's get this in. I have some time this week to work on more REST entity tests :)
Comment #40
Anonymous (not verified) CreditAttribution: Anonymous commentedLast fail is random fail. Retest is green. Separate issue #2861067: Random fail in Drupal\aggregator\Tests\FeedAdminDisplayTest::testFeedUpdateFields and back to RTBC.
Comment #41
Wim LeersThis is also blocking #2843139: EntityResource: Provide comprehensive test coverage for File entity, and tighten access control handler.
Comment #42
alexpottCommitted 0edd07b and pushed to 8.4.x. Thanks!
I think we should commit this to 8.3.x - going to ask other committers.
Comment #43
Anonymous (not verified) CreditAttribution: Anonymous commented@alexpott, thank you very match! We really need this for 8.3.x, otherwise a whole series of tests (issues) just stall.
Comment #44
Wim LeersThis is a pure bugfix.
Contrib modules having their own entity types without a canonical URL would also run into this bug. In fact, that's already the case, see #2836121: PHP error when POSTing content entities without a "canonical" link template, which reported it for the
paragraph
entity type!Therefore I strongly agree this should be committed to 8.3.x.
Comment #45
alexpottSo let's make this a bugfix.
Comment #47
catchYep #44 is the only real change here, and agreed it's just a bugfix and that we should backport it.
Comment #48
Wim LeersYeah it was originally a task (improve test coverage), then in doing so we found an actual bug outside of test coverage. Sorry for not changing the category earlier.
Comment #50
alexpottFixed a couple of merge conflicts on commit in comments and what with what is in the 8.4.x patch. The conflicts where caused by what was in HEAD.
Comment #51
Wim LeersThanks!
Comment #52
Wim LeersCan we please also assign issue credit to @ruloweb (for his contributions at #2836121-2: PHP error when POSTing content entities without a "canonical" link template, which was closed as a duplicate). Perhaps also @dagmar.
Comment #54
groovedork CreditAttribution: groovedork commentedI ran into this bug. How will this patch end up in normal Drupal installations? When Drupal 8.4 arrives? Or with a new Paragraphs version?