Problem/Motivation
- make a comment type named "forterm", and Target entity type set to "Taxonomy term".
- add a comment field to vocabulary "tags", and set Comment type to "forterm".
- Add a entity_reference field to tags, set machine name as field_myuser and target to user.
- Create a tags' term
GET jsonapi/comment/forterm?include=entity_id,entity_id.field_myuser will get error
Group module's Group_content has similar error.
This error caused by jsonapi don't take entity's bundle override field
on http://cgit.drupalcode.org/jsonapi/tree/src/Context/FieldResolver.php#n104
$definitions = $this->fieldManager->getFieldStorageDefinitions($entity_type_id)
// We also need the base field definitions in case there are
// relationships coming from computed fields.
+ $this->fieldManager->getBaseFieldDefinitions($entity_type_id);
The comment's entity_id field will be override by every comment bundle.
So error occured.
Entity_reference's default target type is node. So no error didn't appear on node's comment
JSONAPI 8.x-1.1 don't have this problem.
Proposed resolution
- make FieldResolver take the right field definitions on the position below
http://cgit.drupalcode.org/jsonapi/tree/src/Context/FieldResolver.php#n104
$definitions = $this->fieldManager->getFieldStorageDefinitions($entity_type_id)
// We also need the base field definitions in case there are
// relationships coming from computed fields.
+ $this->fieldManager->getBaseFieldDefinitions($entity_type_id);
Remaining tasks
User interface changes
API changes
Data model changes
Comments
Comment #2
dravenkThis is a quick fix patch.
Comment #3
dravenkJust in my case. #2797793: Entities identified by strings as group content
I found just change one line can mede the output normal. But this issues still need investigate.
I guess if the contribution module create the table column name be
entity_idwill be broken. I'm no sure.Comment #4
dravenkPlease reviews.
Comment #6
dravenkSteps to reproduce:
1. Install drupal
2. composer require 'drupal/group:1.x-dev'
3. composer require 'drupal/jsonapi:1.x-dev'
4. enable Group, JSON API,
`6 modules have been enabled: HTTP Basic Authentication, Group, HAL, JSON API, RESTful Web Services, Serialization.`
5. create one group type and one group
6. cd modules/contrib/jsonapi
7. reset to the JsonApi 8.x-1.1
git reset --hard c205a527a2cb84307bc85a9f678ab839cb2e2a1b8. clearcache (show endpoints)
9. Get `/jsonapi/group_content/default-group_membership?include=entity_id,entity_id.roles`
10 .reset to the JsonApi 8.x-1.2
git reset --hard eb00d4dfa9bb648e319fc1cf1ea0bca487322a9c11. Get `/jsonapi/group_content/default-group_membership?include=entity_id,entity_id.roles`
In the JsonApi 8.x-1.1
src/Normalizer/JsonApiDocumentTopLevelNormalizer.phpline 238 ~ 246:
but, JsonApi 8.x-1.2 :
The output was broken.
Comment #7
wim leersComment #8
skyredwangComment #9
skyredwangComment #10
alan_blake commentedthe patch make loading field definitions on specific bundle.
Comment #11
alan_blake commentedComment #12
e0ipsoIt's interesting to notice that D8.5 has more test fails than D8.3
Comment #13
lawxen commentedCore's comment test:
jsonapi/comment/hieckcomment?include=entity_id,entity_id.field_juicereturn error.
The comment has the problem too.
#11 may be the right direction.
Comment #14
alan_blake commentedcan't agree with #13.
1. make a comment type named "forterm", and Target entity type set to "Taxonomy term".
2. add a comment field to vocabulary "tags", and set Comment type to "forterm".
3. GET
jsonapi/comment/forterm?include=entity_id,entity_id.vidwork correctly.Comment #15
jonathan1055 commentedFrom #12
This is due to #2883680: Force all route filters and route enhancers to be non-lazy where the class
/RouteEnhancerInterfaceis now deprecated and will cause a test failure. Currently if contrib modules fix this then the tests fail at 8.4 and 8.3 because the new class interfaces have not been backported. I have raised #2924899: Backport \Routing\EnhancerInterface to core 8.4 to request this.Comment #16
lawxen commented@alan_blake
your request is
jsonapi/comment/forterm?include=entity_id,entity_id.vidmulti-level include is
entity_id.vid.
the jsonapi's FieldResolver.php#n124
return node.
FieldResolver.php#n104
node has the field vid, so you don't run into the error.
So when come to comment about this issue, you must include a relationship whose name don't exist on node's field names, Then it will return the error.
If you according to the steps i write on #13
I ensure you can find the error absolutely.
Comment #17
lawxen commented@alan_blake Oh, I forget one task 3
GET jsonapi/comment/forterm?include=entity_id,entity_id.field_myuser will get error
Comment #18
lawxen commentedhttp://groupjson.ap:8080/jsonapi/comment/forterm?include=entity_idBut when you get
http://groupjson.ap:8080/jsonapi/comment/forterm?include=entity_id,entity_id.field_myuserYou will get error
Comment #19
skyredwangFollowing #17, I was able to reproduce a similar error on simplytest.me with Core 8.4.2 and JSONAPI 8.x-1.3:
Comment #20
lawxen commentedOn Core 8.4.2 and JSONAPI 8.x-1.1
Same step with #17, no error
Comment #21
alan_blake commentedload field definitions from bundle first.
Comment #22
dravenkAs #15 say. I add the test with php7.1 and drupal8.4. The test result looks well . I run
../vendor/phpunit/phpunit/phpunit ../modules/contrib/jsonapiwith drupal8.5 that is verify test for this.we can put it to status Needs review after write the test.
Comment #23
dravenkRecovery status, supplementary test
Comment #24
lawxen commentedComment #26
lawxen commentedComment #27
lawxen commentedComment #28
wim leers#12
See #2926413: RouteEnhancerInterface is deprecated in Drupal 8.5.0.
Comment #29
wim leersGreat work here! 👍 Thanks for helping to fix this :)
We're modifying this.
Then we should also be able to expand the test coverage in
\Drupal\Tests\jsonapi\Kernel\Context\FieldResolverTest.Is this the correct order?
The JSON API spec (http://jsonapi.org/format/) calls this "nested relationship" vs "direct relationships".
So let's s/multi-level/nested/
The JSON API module already is using the term "nested" in several place (for example "nested filtering"), which further confirms this.
Comment #30
gabesulliceComment #31
gabesulliceGonna pick this one up since it's been stalled for a month now.
Comment #32
gabesulliceHey everyone! I'm so thankful for all your work on this! Sorry it's taken so long for us to get back around to it.
Unfortunately, this code has been a focus of some attention in various other issues. The solutions that were committed elsewhere fixed the problem by coincidence without addressing the root problem leading to a lot of confusion here.
Anyway, I'm gonna do a little "review" of the current patch even though I'm about to work on it. I hope that will clarify some of the changes I'll be making for later reviewers.
This is only getting base fields, but we're naming the variable
$bundle_field_definitions?There are a few strange things in here. First, why did we filter out base fields above, just to add them right back? Second, this is broken because by grabbing storage definitions by
$entity_type_idwe're going to get fields which don't belong to the bundle we're dealing with (the tests for this are incorrect). Finally, there's just no practical reason to grab storage definitions (if we need them, it means we can't rely on computed fields...).I don't think we need a namespace just for comments.
same.
I think we can get away without a base class just for comments.
Comment #33
dravenkIt looks a little bit relevant #2346347: Finalize API for creating, overriding, and altering code-defined bundle fields
Comment #34
gabesulliceOkay, this is still a work in progress (I have to simplify the tests), but I think I've gotten to the root problem.
The original problem can be seen in the line above.
$bundlewas never updated as the path was traversed. I.e. it was always the bundle ID of the root entity of the request. This meant that once the code traversed an entity reference this would never return any field definitions unless the bundle was coincidentally the same as the bundle of the root request.This explains why the other code seemed to fix the problem in most cases. By getting field storage definitions and base field definitions, we were ignoring the bundle and adding all fields for whatever entity type we had; it's possible this caused fields to be seen as valid even when they weren't (I don't think anyone would have ever noticed this in the wild).
Anyway, this is almost done. We need to craft a test case which illustrates the problem in a purer way and exercise this all more thoroughly. Then remove the commented out tests from FieldResolverTest.php (which actually show how this was working incorrectly before) that need to be turned into tests for thrown exceptions.
Comment #35
gabesulliceAlright, cleaned up and added more tests.
This is almost ready, except for one error in another test because we're not passing the correct entity type and bundle into
resolveInternal. The FieldResolver code is correct though.I've removed the tests under
tests/src/Kernel/commentsbecause I think the FieldResolverTests now cover the problem, but I've included them in one of the attached patches to confirm that they do in fact pass.Comment #36
gabesulliceExplaining myself:
I removed this because I think we should be throwing an exception if the field doesn't exist, even if the given path isn't "nested".
Adding 'handler_settings' is the only thing that's actually different with the fields created in
setUp, even though it looks drastically different.These should never have passed because
field_test_ref1only referencesbundle2andbundle3, neither of which havefield_test1orfield_test2.Comment #38
gabesulliceOkay, I've tracked down the problem causing the test failure and there's already an issue for it that just needs tests!
I've marked the failing test in this issue as incomplete so that this fix can be committed independently. We can finish the test and verify the solution in #2922121: When fetching related resources, getResourceType() in CurrentContext gets the wrong resource type. in one sweep then.
Comment #39
gabesulliceWhoops, leftover change from another patch removed.
Comment #40
gabesulliceComment #41
wim leers#32: excellent review :)
#34: Thanks for explaining the root cause! I've been bitten by similar bugs in the past while working on Entity API bugs and on core's REST module. This also reminds me how confusing it is that for entity types without bundles, the "bundle" is the same as the entity type ID. I hope that weirdness gets fixed in a future version of Entity API.
Now that I've got a better grasp on this bit of code, I think
resolveInternal()is a pretty vague name.Its docblock also doesn't help:
It does far more than that: when dealing with "nested abbreviated field expressions", which follow entity references, it resolves those reference fields, resulting in the "nested fully resolved field expressions".
This example misleads more than it clarifies, because it includes both "nested abbreviated field expression" resolving *and* JSON API Extras field alias name resolving.
This comment should not be necessary. If the method call is not sufficiently clear, then we should rename it to incorporate that extra nuance.
In this case, I think
getCandidateFieldDefinitions()might be clearer?Nit: s/, given/, given/
Also: I'd expect the breadcrumb to be used here, otherwise the error message still could be hard to understand/less than helpful.
The previous comment contained a bit more detail. I think a mix of both the old and new comment would be clearer.
Because AFAIK "complex field" is a concept that doesn't exist in Drupal core: it's a JSON API-invented concept?
Nit: s/patch/path/
s/resources/resource types/
"all" really isn't "all" here, but "all results so far".
I think
$resultwould therefore be a less ambiguous variable name.I think "referenceable" is clearer than "possibly referenced".
Also: s/with list of field definitions/for a set of field definitions/
Missing trailing
[]in typehint.The referenceable target resource types.
These lines can be merged.
Also: AFAICT the return value of that method call returns an indexed array. Which means that this code allows for duplicates.
Is that okay?
(IOW: this logically behaves like merging sets, but in reality it doesn't.)
Why this change? This seems less robust?
Rather than doing this dance, why not typehint to
\Drupal\Core\Field\TypedData\FieldItemDataDefinitionInterface, so we can remove this strange "auto resolve" logic? It'd make this less errorprone.I think this can become much clearer:
That'd be much easier to understand, I think?
Why this change?
Comment #42
gabesullicePhew, and yours!
Responding to a few questions and comments below. If I didn't respond to something, you can assume that I agree and will change it.
+1 The complexity of this has far surpassed the original method.
I went back and forth on this. The breadcrumbs contain the real Drupal field names, not the original query fields. Since we're calling
getInternalName, then these would be different from the fields used by the developer in their request. I felt that would be more confusing to show field names that the developer didn't actually put in the request query.Yeah... it's ugly :( I had to change this because the previous method of detection relied on
FieldStorageDefinitionInterfaceand methods we can no longer access.I couldn't figure out how to type check for DataReferenceInterface from this (which I would have preferred), so I followed the footsteps of the documentation, "Note that this definition class assumes that the data type for referencing a certain target type is named "{TARGET_TYPE}_reference".
If there is a better way, I would love to use that instead :)
Comment #43
gabesulliceI've written a lot more documentation here to better reflect the behavior of this method.
I wouldn't say we "invented" the concept, but it's not based on anything explicit in core. It's a reference to two things:
$fieldparameter).ComplexDataInterfaceand the corresponding fact that a field might have sub-properties (like the text field)I've tried to clarify those comments.
Technically, yes. It would just mean we'd do extra work. I've de-duped this anyway.
This was a great suggestion! It let me get rid of that nasty check for the "_reference" string and go back to the more stable method.
@Wim Leers++
Couldn't do exactly that, but it's much cleaner now using the helper method you suggested.
Comment #44
gabesulliceComment #47
e0ipsoIt looks like there is an issue with D8.4.
Comment #48
lawxen commentedSame test with #17
GET jsonapi/comment/forterm?include=entity_id,entity_id.field_tagsuser
Drupal8.5.x:-
- Before apply 2920194-44.patch:
- After apply 2920194-44.patch:
-
- Before apply 2920194-44.patch:
- After apply 2920194-44.patch:
http://jsonapi85.dn8/jsonapi/comment/forterm?include=entity_id,entity_id.field_tagsuser get error.
http://jsonapi85.dn8/jsonapi/comment/forterm?include=entity_id,entity_id.field_tagsuser get right result.
Drupal8.4.x:
/jsonapi/comment/forterm?include=entity_id get right result
/jsonapi/comment/forterm?include=entity_id,entity_id.field_tagsuser get error
/jsonapi/comment/forterm?include=entity_id get error.
Because include=entity_id get error, can't test include=entity_id,entity_id.field_tagsuser.
TypeError: Argument 1 passed to Drupal\jsonapi\Context\FieldResolver::collectResourceTypesForReference() must be an instance of Drupal\Core\Field\TypedData\FieldItemDataDefinitionInterface, instance of Drupal\Core\Field\TypedData\FieldItemDataDefinition given, called in /app/www/jsonapi/modules/contrib/jsonapi/src/Context/FieldResolver.php on line 209 in Drupal\jsonapi\Context\FieldResolver->collectResourceTypesForReference() (line 227 of /app/www/jsonapi/modules/contrib/jsonapi/src/Context/FieldResolver.php) #0 /app/www/jsonapi/modules/contrib/jsonapi/src/Context/FieldResolver.php(209): Drupal\jsonapi\Context\FieldResolver->collectResourceTypesForReference(Object(Drupal\Core\Field\TypedData\FieldItemDataDefinition)) #1......Comment #49
e0ipsoFieldItemDataDefinitionInterface is D8.5 only. We'll need to figure out a different way if we want to keep support for existing D8 versions.
It's sad that this happens more and more often. Supporting old and new versions of core is more and more difficult, while "minor" upgrades for existing sites are more and more challenging. People are not updating core "just like that", on top of that contribs are implicitly (and explicitly) dropping support for those versions. I see this as one of the biggest risks to Drupal's future.
Comment #50
gabesulliceI think this should work with 8.4. Tests will find out :)
Fixed a doc typo too.
Comment #51
lawxen commentedI have tested 2920194-50.patch both on drupal8.5.x and drupal8.4.x
Functional works well.
Comment #52
lawxen commentedGreat patch, I have test it. Works well. But It has some coding standards problem:
Parameter tags must be defined first in a doc comment
Missing function doc comment
Line exceeds 80 characters.
Missing function doc comment
Comment #53
gabesulliceThanks for reviewing this with your real use-case @caseylau, that's super helpful!
And thank you for keeping me honest :P You got me to finally set up a code sniffer on my new laptop.
I've fixed all the violations and some others. FWIW, "Parameter tags must be defined first in a doc comment" wasn't the real issue. The description should come before
@params. I just had to make my@seeinto a@link.Comment #54
wim leers#43: (reviewing just the changes introduced there)
Nit: s/word/keyword/
This entire docblock is now 10x better, but this paragraph in particular is GOLD.
Why not keep both? Was the original wrong after all?
I don't think I've seen
array_push()be used in years.Why not the slightly simpler:
We do this all over Drupal core already.
Nit s/-//
Nit: s/ids/IDs/
#47+#48+#49+#50: darn! I didn't realize that this interface was 8.5-only! Was apparently added in #2734345: Comments: entity_id base field not overridden correctly in comment bundle field definitions for comment bundles attached to different entity target_type ~3 months ago. Turns out this is just the formalizing in an interface what
\Drupal\Core\Field\TypedData\FieldItemDataDefinitionhas been providing already. So, it's actually safe to typehint to that instead, and then have a@todo+ an issue to change the hint to the interface when JSON API becomes 8.5-only (at the release of 8.6 at the earliest). Then we ensure the follow-up doesn't end up being forgotten :)The work-around that #50 added (typehinting to
\Drupal\Core\TypedData\ComplexDataDefinitionInterfaceinstead) is dangerous: it doesn't guarantee that a reference definition is being passed in.#53 (reviewing the entire patch):
Nit: The entity type bundle info.
Nit: The JSON API resource type repository.
This refactoring could easily be moved to a separate issue, titled .
Then this patch would become SIGNIFICANTLY smaller, and MUCH easier to understand.
I'd instantly commit such a zero-functional-change patch.
Would
$candidate_fieldsmake the code clearer?And
$candidate_resource_typeshere?I suggested this method name, but it's probably better to omit "Candidate" from this, and instead just make clear in the variable name that it's about candidates. The method itself has no logic at all to restrict to potential candidates, it just returns all of them, because they're all candidates.
My bad!
Unnecessary change.
Comment #55
gabesullice@Wim Leers here's a refactor and clean-up issue as suggested: #2933892: Refactor FieldResolver and tests
Comment #56
gabesulliceThe attached patch depends on #2933892-9: Refactor FieldResolver and tests.
Comment #58
gabesulliceStill needs review. When the other patch is committed, this can be retested.
Comment #59
wim leers#2920194: Inclusion of nested relationships fails for bundle-specific fields is in!
Comment #60
wim leersWhy do we need this change?
referenced vs referenceable
I think the latter is the more accurate adjective?
This should typehint to
FieldItemDataDefinition, and in the follow-up issue that should be changed to\Drupal\Core\Field\TypedData\FieldItemDataDefinitionInterface.Also clarified that in #2933895-5: [>=8.5] Update type hint to interface instead of concrete class in FieldResolver.
Wait, so these suddenly are no longer valid?
These new "should fail" cases do NOT fail without the non-test changes in this patch, right?
Let's prove that with a test-only patch that fails.
Comment #61
gabesullice@Wim Leers, most of these answers have more detail in my comment #2920194-36: Inclusion of nested relationships fails for bundle-specific fields, but to recap:
ResourceType::getInternalNameand I felt we should consistently be checking that the field exists (nested or not).Comment #62
gabesulliceComment #63
wim leersTests-only patch failed. Proper patch passed. All concerns addressed. RTBC :)
Comment #64
e0ipsoThis was a tricky review, but it's committed now (with a tiny change).
Comment #66
e0ipsoThis patch caused a regression as noticed in #2934730: Server error when accessing non-fieldable entity resources with filters.