Problem/Motivation
#2871591: Allow ComplexData in TypedData to specify computed properties that should be exposed in normalization and other contexts introduced the ability to mark Typed Data definitions as "internal": \Drupal\Core\TypedData\DataDefinitionInterface::isInternal()
and \Drupal\Core\TypedData\DataDefinition::setInternal()
were introduced. Change record: https://www.drupal.org/node/2916592.
This means that both fields and properties can be marked as internal now. The core Serialization and REST modules will respect this, meaning that they will not normalize those fields and properties.
The JSON API Extras project page mentions this:
- Disable fields.
That is currently implemented in a JSON API-specific way. In Drupal 8.5, JSON API should start using the setInternal()
API to mark certain fields as internal, which will then also be respected by Serialization/REST in core, and GraphQL (sister issue: https://github.com/drupal-graphql/graphql/issues/429) in contrib.
Of course, the tricky thing is going to be that this will need an update path, because that API addition is only available in 8.5, not 8.4.
Proposed resolution
- Update JSON API to respect 8.5's
DataDefinitionInterface::isInternal()
- Update JSON API Extras to use 8.5's
DataDefinition::setInternal()
rather than its own metadata. Perhaps this should become a new contrib module, because it's no longer specific to JSON API — it would affect Serialization + REST + JSON API + GraphQL.
Remaining tasks
TBD
User interface changes
TBD
API changes
TBD
Data model changes
TBD
Comment | File | Size | Author |
---|---|---|---|
#47 | 2921257-47.patch | 5.34 KB | Wim Leers |
| |||
#47 | interdiff-44-47.txt | 1.28 KB | Wim Leers |
#44 | 2921257-44.patch | 4.32 KB | Wim Leers |
| |||
#44 | interdiff-43-44.txt | 2.96 KB | Wim Leers |
#43 | 2921257-43.patch | 1.39 KB | Wim Leers |
Comments
Comment #2
Wim LeersComment #3
Wim LeersComment #4
Wim LeersComment #5
Wim LeersComment #6
Wim LeersComment #7
Wim Leers#6 is going to fail against 8.3, and should pass against 8.5. The new core API that this patch uses is only available in 8.5.
Comment #9
Wim LeersSee #2825812-107: ImageItem should have an "derivatives" computed property, to expose all image style URLs: thanks to #6, image fields normalized by JSON API automatically get an
image_styles
property in themeta
section for image field normalizations:Comment #10
Wim Leers#6 only updated the entity reference field case, but we also need to update the generic field case.
Still needs explicit tests.
Comment #12
Wim LeersSee #2577923-119: MenuLinkContent entities pointing to nodes are not deployable: LinkItem should have a "target_uuid" computed property, thanks to #10, link fields normalized by JSON API automatically get a
target_uuid
property for link fields that point to an entity:Comment #13
e0ipsoThanks for taking this one Wim!
Do you have a plan so we can maintain compatibility for 8.3 through 8.4?
I'm not sure I like this solution. This is verbose explanation why https://www.lullabot.com/articles/decoupled-drupal-hard-problems-image-s...
Comment #14
Wim LeersNot yet, but of course I will try to achieve that. The above patches are just the bare minimum to make things work.
I'm hesitant to do this before we have full test coverage of JSON API, i.e. the equivalent of #2737719: EntityResource: Provide comprehensive test coverage: for every entity type, every format, every method for JSON API, because without that test coverage, how would we possibly know for certain that we wouldn't be breaking BC?
It's up to the administrator of the API server/host to set up image styles that can be used for multiple clients. 10 image styles per consumer seems a lot, and zero reuse across consumers also seems pretty extreme.
That doesn't mean I don't agree that often it should be the client that does the resizing… that is true. But if that's the case, then this site could just mark the
image_styles
computed property asinternal
, and it'd disappear from JSON API responses (as well as REST responses).This is not meant to be an optimal solution. Different API servers/hosts will have different use cases: there's no perfect answer. This just happens to be the default answer Drupal would give. Upon installing https://www.drupal.org/project/consumer_image_styles, that default behavior would be overridden.
Doesn't that seem acceptable?
Comment #15
e0ipsoIn my experience I think it's a rather optimistic number, but I guess that depends on each project. Drupal aims to support all kinds of projects, so we need to plan for the worst case scenario.
That's not always a possibility (public APIs). And when it is, it's not necessarily advisable because having clear dependencies on external configuration is necessary for maintainability in a decoupled architecture. I hope that was communicated in that article, as it was one of the goals.
Even though that's not my preference, that will have to do. I created #2922945: [PP-1] Mark ImageItem's "image_styles" computed property as "internal" upon installation for that purpose.
Maybe this is derailing the conversation, but I'd like to have a payload for the default image styles compatible with the one provided by
consumer_image_styles
for "BC" reasons. Meaning an output like:Comment #16
Wim LeersAgreed! 👌
We must allow for any scenario, but provide sensible defaults. And requiring a contrib module to be able use image styles, is not a sensible default. I think you agree with that? 🙂
BC would be up to the https://www.drupal.org/project/consumer_image_styles module to provide, not the JSON API module.
If you feel strongly that the key should not be
image_styles
, butderivatives
, please post a comment at #2825812: ImageItem should have an "derivatives" computed property, to expose all image style URLs expressing why that is. Interestingly, theimage
module calls it in the UI, but in most of the code. I can totally see "image styles" being too Drupal-y, and "derivatives" being a better name, because it's understandable by a broader audience.Posted this understanding as a comment at #2825812-113: ImageItem should have an "derivatives" computed property, to expose all image style URLs. Please reply to it, to clarify your thoughts. In fact, I even implemented it in #2825812-115: ImageItem should have an "derivatives" computed property, to expose all image style URLs.
Hope you like it! 😄
On the topic of BC with the the existing
consumer_image_styles
module: I do think that the data provided by https://www.drupal.org/project/consumer_image_styles is insufficient: without the width and height, browsers cannot efficiently render<img>
tags. Not every image style has fixed dimensions, some just resize to e.g. 50%. And even if there are fixed dimensions, it should not be necessary to hardcode those in every consumer, because that means 3 pieces of coupled knowledge: A) image style name, B) image style width, C) image style height. It's better to have only 1 (image style name).Comment #17
e0ipsoI don't actually. I dream of a day when contrib is a first class citizen, of a day when we have more tools other than putting things in core, and of a day when we clearly communicate that Drupal can now do X! is different from Drupal core can now do X!. But that's a significantly different conversation (that I would love to have).
In this case I still believe that image styles are presentation info. I still believe that presentation of Consumer A should not leak all over Consumer B. We can achieve that by using a separate contrib, or by piling that contrib into JSON API. I think the former is better.
Yup. That was my reason.
What's the hold up with that? I think I missed the use case for the image dimensions. I'm interested, maybe that grants a major version bump of consumer_image_styles and we take the chance to address BC.
Maintaining an ecosystem of modules around this topic on my free time is incredibly hard and time consuming. Sometimes I need to cut myself slack and have
jsonapi
help withconsumer_image_styles
BC 😉.😗 I did!
Comment #18
Wim LeersNot specifying width & height causes reflows: browsers render DOM elements while images are being downloaded. If the image dimensions aren't known, they need to be read from the downloaded image. And if the image is not yet downloaded, the browser of course can't know it. This then causes a layout reflow.
Absolutely! And it's made harder by the fact that we rushed to ship things that weren't done, which then cause BC breaks for that ecosystem :( I'm trying to fix the entire ecosystem as you know, and that's even harder — and I'm only able to do it because it's my primary task in my job for the past ~1.5 year. So … *hugs* 🙂
Comment #19
Wim LeersI have the impression that this will not land until at least some computed property in core is doing
setInternal(FALSE)
.#13 through #18 got sidetracked with the hypothetical example I showed in #12.
As soon as any of the following 3 issues land, we'll have a concrete thing to look at:
Comment #20
Wim LeersAnd it's probably better to do this after #2930028: Comprehensive JSON API integration test coverage phase 1: for every entity type, individual resources only anyway.
Comment #21
Wim LeersTwo issues mentioned in #19 weren't yet in the list of related issues.
Comment #22
Wim Leers#2825487: Fix normalization of File entities: file entities should expose the file URL as a computed property on the 'uri' base field landed, which is the second item in the list in #19. This is now unblocked! Except we still want to wait for #2930028: Comprehensive JSON API integration test coverage phase 1: for every entity type, individual resources only first.
Comment #23
larowlan#2626924: Include processed text in normalizations: "text" field type's "processed" computed property should be non-internal and carry cacheability metadata is in, which I think makes this no longer postponed?
Comment #24
Wim Leers#23: thanks for updating this, but it's still postponed. It's still blocked by #2930028: Comprehensive JSON API integration test coverage phase 1: for every entity type, individual resources only, which I'm actively working on right now :)
#19 explained that this needed >=1 core issue that added a non-internal computed field property to core. #22 already indicated that #2825487: Fix normalization of File entities: file entities should expose the file URL as a computed property on the 'uri' base field was that first example. And thanks to #2626924: Include processed text in normalizations: "text" field type's "processed" computed property should be non-internal and carry cacheability metadata, there's now two of those.
Comment #25
Wim LeersAs of #2775205-46: [PP-2] [FEATURE] Discus how to make JSON API use text input formats for long text fields, this is a hard blocker for #2775205.
Without this issue, JSON API will not gain the two serialization gap fixes that currently exist in core. The sooner this lands, the sooner we can further validate Drupal 8.5 core's architecture/strategy/solution, and if necessary, change course. So, bumping this to critical.
On top of that, this is also listed as a must-have in #2931785: The path for JSON API to core, further indicating the criticalness of this issue.
Comment #26
Wim LeersNote that ideally this issue would land first, and then we would be able to solve #2926463: [>=8.5] Remove JSON API's "file URL" field work-around now that Drupal core 8.5 fixed it.
Comment #27
Wim LeersAlso, hurray, everything is coming together, all tricky things are being addressed! 🍾
Comment #28
Wim LeersTo clarify: this is not blocked on #2926463: [>=8.5] Remove JSON API's "file URL" field work-around now that Drupal core 8.5 fixed it (that is actually blocked on this). This is blocked on #2930028: Comprehensive JSON API integration test coverage phase 1: for every entity type, individual resources only (see #22).
Comment #29
Wim LeersAnd #2930028: Comprehensive JSON API integration test coverage phase 1: for every entity type, individual resources only is nearing completion!
Comment #30
Wim Leers#2930028: Comprehensive JSON API integration test coverage phase 1: for every entity type, individual resources only and #2932031: Comprehensive JSON API integration test coverage phase 2: for every entity type, complex use-cases landed!
Comment #31
Wim LeersRebased #10 — now that #2930028: Comprehensive JSON API integration test coverage phase 1: for every entity type, individual resources only and #2932031: Comprehensive JSON API integration test coverage phase 2: for every entity type, complex use-cases have landed, we already have all the integration test coverage we need! It'll become clear which normalizations would change for all entity types across Drupal core.
Comment #33
Wim Leers5 fails on 8.6, 30 fails on 8.4. Because on 8.4 the API that #31 uses doesn't exist yet.
This should fix all fails.
Comment #34
Wim Leers#33 passed on 8.4.x but failed on 8.5.x/8.6.x. That already failed before, I just forgot to fix it.
Comment #35
gabesulliceThis ternary syntax is starting to grow on me.
supernit: Deci"d"e
Nothing here can't be fixed on commit.
Comment #36
Wim LeersAssigning to @e0ipso for final review.
Comment #37
Wim Leers#2937850: Mark revision_default as internal for REST consumers just landed, which means we'll need to update our expectations (#2930028: Comprehensive JSON API integration test coverage phase 1: for every entity type, individual resources only and #2930028: Comprehensive JSON API integration test coverage phase 1: for every entity type, individual resources only are expecting
revision_default
). But before we can update our expectations, this needs to land, since #2937850 didn't remove those fields, but merely marked them "internal", and JSON API will only start respecting that as of this patch.So this patch will need a reroll to update those expectations. Currently retesting #34, that should fail.
Comment #38
e0ipso@Wim Leers I'm ready to merge this. Since #37 is not critical. It only changes a newly added field that no one is using. That may break our tests (they didn't break as expected), but we can follow-up.
Merging and re-assinging to @Wim Leers for follow-ups.
PS: I fixed the nit typo on commit.
Comment #40
e0ipsoBack to Needs Work. Feel free to close Wim.
Comment #41
Wim Leers… because this patch did not update JSON API's
ContentEntityNormalizer
yet, oops.This does that, and should hence fail tests.
Comment #43
Wim Leers::isInternal()
only exists on >= 8.5.Comment #44
Wim Leers#43 should still fail on 8.5/8.6, this should make it pass again.
Comment #45
Wim LeersComment #46
Wim LeersIn
MediaTest
:This is wrong. This is blocked on #2925520: Add a computed 'file_url' property to FileItem (for exposing file URL in file field normalization) — it's not related to this issue!
So I should remove those comments as well.
Comment #47
Wim LeersDid #46.
Comment #48
gabesulliceComment #49
e0ipsoI see that the implementation of this two methods is a bit different. I am a little concerned that we are not replacing by the exact same thing. Will we be bitten by an edge case we are not contemplating here?
Would something like this be safer?:
I am probably worrying over nothing and you can put my mind at ease. If that's the case, please move back to RTBC.
Comment #50
Wim LeersSo you're concerned about
$entity->getFields()
vs$entity->getTypedData()
. Excellent question!First:
\Drupal\serialization\Normalizer\ContentEntityNormalizer::normalize()
also usesgetTypedData()
.Second:
\Drupal\Core\TypedData\TypedDataInternalPropertiesHelper::getNonInternalProperties()
's sole parameter must be of the typeComplexDataInterface
, i.e. it must be typed data. Hence we callgetTypedData()
.getFields()
is intended for code that does not care about traversing typed data, i.e. typical custom Drupal code. In theory we could make it work withgetFields()
, but then JSON API would be implementing its own logic rather than using\Drupal\Core\TypedData\TypedDataInternalPropertiesHelper()
which core specifically provides for this purpose.Third: the "else" will be removed once JSON API requires Drupal >=8.5, so the difference will disappear.
Fourth: they will behave differently in that the
$entity->getFields()
call (on <8.5) omits computed fields, whereas theTypedDataInternalPropertiesHelper::getNonInternalProperties($entity->getTypedData())
call omits internal fields. Computed fields are still internal by default though. That's off course the goal.Fifth: the test coverage already proves that they're equivalent! The normalized output is identical — the only difference is that computed fields (internal by default!) that >=8.5 explicitly overrides to be non-internal now do show up. That's of course the entire goal!
Comment #51
e0ipsoExcellent! Thanks @Wim Leers for the clarifications. Merging.
Comment #53
Wim Leers🎉