Overview
As recently pointed out by @isholgueras on the MR for #3518620: Image prop shape matching should be case-insensitive, but JSON Schema doesn't support that, the current 3.1.2.a structured data → matching field instances ⇒ dynamic prop source infrastructure for matching $ref: json-schema-definitions://experience_builder.module/image-uri and $ref: json-schema-definitions://experience_builder.module/image, which rely on this:
"image-uri": {
"title": "Image URL",
"type": "string",
"format": "uri-reference",
"pattern": "^(/|https?://)?.*\\.(png|gif|jpg|jpeg|webp)(\\?.*)?(#.*)?$"
},
getting matched against Drupal core's (semantically equivalent) FileExtension validation constraint, which necessarily requires translating from/to two wildly different representations.
The key problem: the list of file extensions is hardcoded. Which means customizations to the list of allowed image types would be a problem. Recently, AVIF support landed in #3372932: [Meta] High-performance images (nearly) out of the box, which shows how likely this is to happen over time.
The current code dates back to April 2024, _before_ we had even started using d.o issues. This is some of the oldest code in XB and dates back to the research__data_model PoC research branch.
Note: this follows a 2017 best practice!
Blockers discovered while working on this
- #3540470: Require 6.x version of `justinrainbow/json-schema` package to get correct JSON Schema validation
- #3543770: `noUi: true` SDCs do still show up in the UI
- #3543783: `card-with-stream-wrapper-image` test SDC generates an invalid `<img src>`
- #3543805: Resolved image file URLs invalid in kernel tests
Proposed resolution
Use contentMediaType.
So then we'd go from:
"image-uri": {
"title": "Image URL",
"type": "string",
"format": "uri-reference",
"pattern": "^(/|https?://)?.*\\.(png|gif|jpg|jpeg|webp)(\\?.*)?(#.*)?$"
},
to:
"image-uri": {
"title": "Image URL",
"type": "string",
"format": "uri-reference",
"contentMediaType": "image/*"
},
🐛 EDIT: This is in adequate — because it would also allow ftp://cat.jpg. Which means we need to keep pattern but make it less specific:
"image-uri": {
"title": "Image URL",
"type": "string",
"format": "uri-reference",
"contentMediaType": "image/*",
"pattern": "^(/|https?://)?(?!.*\\://)[^\\s]+$"
},
(explanation: see the new SchemaJsonPatternsTest)
… which is not quite exactly how contentMediaType is meant to be used (that's really describing the contents of the string itself), but is … close enough? Something along these lines has been proposed as resourceMediaType.
(Examples of actually defining new JSON Schema formats seem impossible to find.)
This would then also allow us to apply a similar improvement to "video URLs", which is being added at #3524130: Define JSON Schema $refs for linking/embedding videos and linking documents.
User interface changes
None.
| Comment | File | Size | Author |
|---|---|---|---|
| #23 | Screenshot 2025-09-01 at 6.35.24 PM.png | 483.95 KB | wim leers |
Issue fork canvas-3530351
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Issue fork experience_builder-3530351
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
wim leersThe intent has been from the start to avoid adding "Drupalism" to SDC's use of JSON Schema.
That's why we started using
/schema.jsonand defined$ref: json-schema-definitions://experience_builder.module/image-uri.However, given that #3499279: Make link widget autocomplete work (for uri and uri-reference props) is proposing to add a variation for
format: urinamedformat: uri+entity_autocomplete(which required a core change: #3516359: ComponentValidator ignores the set validator and creates a new one)So, an alternative HERE could be that we start using custom
formats (fortype: string). Which is exactly what @lauriii expressed he'd strongly prefer over at #3462705-47: [META] Missing features in SDC needed for XB. (Note that JSON Schema does support custom format attributes: https://json-schema.org/draft/2020-12/json-schema-validation#section-7 + https://json-schema.org/draft/2020-12/json-schema-validation#name-custom...)Then I propose that we'd useOr better yet: useformat: uri+mime+image/*or something like that to express this more genericallycontentMediaType.So then we'd go from:
to:
… which is not quite exactly how
contentMediaTypeis meant to be used (that's really describing the contents of the string itself), but is … close enough? Something along these lines has been proposed asresourceMediaType.Examples of actually defining new JSON Schema formats seem impossible to find.
This would then also allow us to apply a similar improvement to "video URLs", which is being added at #3524130: Define JSON Schema $refs for linking/embedding videos and linking documents.
Comment #3
wim leersRelated but different: #3530521: Decouple image shape matching from the `image` MediaType: support matching multiple bundles of a single MediaSources (`image`).
Comment #4
wim leersBumping to priority. Because without this, we'll need to keep doing awkward dances/updates and we'll never match all image fields.
IOW: without this, we won't be able to match any image fields or image media on existing Drupal sites that do not allow AVIF uploads. That's bad! XB users will be (rightfully!) baffled and frustrated that they can't use their years worth of uploaded images.
Comment #5
wim leersSame is true for video, which is currently limited to just MP4.
Comment #6
wim leersComment #7
wim leersThe pivot from
uri-referencetourishould be uneventful, because as described in https://github.com/json-schema/json-schema/issues/81, URI references are a subset of URLs.But in practice, this does not seem to be the case, due to the utterly broken JSON Schema validation of URIs in
justinrainbow/json-schema. Hopefully #3540470-14: Require 6.x version of `justinrainbow/json-schema` package to get correct JSON Schema validation brings relief.For now, sticking to
uri-referenceto reign in scope.Note that even doing
to transform
to
doesn't fix it, nor does updating to version 6.4.2 of
justinrainbow/json-schema.It seems https://github.com/jsonrainbow/json-schema/pull/800 still didn't fix it quite right. 😬
Comment #11
wim leersLooks like #7 is turning out to become a reality, but for a different reason 🙃
The problem appears to be that
pattern: ^(/|https?://)?is not considered valid in 5.3 (which CI is testing with), but is considered valid in 6.4. 🫠 Which I'm developing against locally, and I don't really have a choice, because I'm on a non-ancient version of Composer, which forces this:Comment #12
wim leersThe 5.3 regex validation logic is apparently this:
— https://github.com/jsonrainbow/json-schema/blob/5.3.0/src/JsonSchema/Con...
Which indeed fails: https://3v4l.org/hPCA3.
In 6.3.1 and later it is very different: https://github.com/jsonrainbow/json-schema/blob/6.3.1/src/JsonSchema/Con...
Comment #13
wim leersLOLOL reading #3516348: Allow 6.x version of justinrainbow/json-schema points to Composer 2.8.6 vs 2.8.8, while here 2.8.10 is used. https://github.com/composer/composer/releases/tag/2.8.10 points to https://github.com/composer/composer/pull/12376, which is … very much a similar problem 😅 (It touches upon
patternbeing quite brittle, and literally does what we did in #3518620: Image prop shape matching should be case-insensitive, but JSON Schema doesn't support that!)Comment #14
wim leersThe
justinrainbow/json-schemaversion bump didn't work, root cause was #3538439-13: Running tests locally is difficult to match with the CI. Being fixed at #3540470-16: Require 6.x version of `justinrainbow/json-schema` package to get correct JSON Schema validation.Will continue after the weekend.
Comment #15
wim leersThis MR was getting unwieldy due to 2 bits we overlooked (well, I failed to spot as a reviewer) in #3535453: Create an Image SDC that can be included by other SDCs:
Comment #16
wim leersMerged in upstream now that #3543770: `noUi: true` SDCs do still show up in the UI is in 👍
Comment #17
wim leersNew blocker: #3543805: Resolved image file URLs invalid in kernel tests — will make this MR far less overwhelming. Also: #3543783: `card-with-stream-wrapper-image` test SDC generates an invalid `<img src>` is green — awaiting @justafish's approval.
Comment #18
wim leers#3543805: Resolved image file URLs invalid in kernel tests is in, bringing that into the branch reduces it from 39 to 27 files.
#3543783: `card-with-stream-wrapper-image` test SDC generates an invalid `<img src>` will bring it down more 👍
Comment #19
wim leersComment #20
wim leers#7 was fixed in #3543805: Resolved image file URLs invalid in kernel tests.
Comment #21
wim leers#3543783: `card-with-stream-wrapper-image` test SDC generates an invalid `<img src>` is also in — yay! Down to 25 files changed now.
Comment #22
wim leersI won't be able to finish this today. To make
card-with-stream-wrapper-imagework correctly as well requires further fixes.Also: the whole
pattern(aka Regex) based matching seems brittle. It's better in this MR than it is in HEAD, and pretty proven thanks to the newSchemaJsonPatternsTest, but it's still too brittle.I propose to stop using
patternaltogether and instead introducex-allowed-schemes, similar to how we already introducedx-required-variablesandx-formatting-context. IOW:Will refactor towards that on Monday unless I hear counterarguments.
Comment #23
wim leersGreen!
This removes 7
@todos and adds 3.Literally the last failure is
… which is testing with an INVALID stream wrapper URI entered into a
input[type=text], which the XB UX is intended to never show/allow, and indeed is no longer the case: it now shows the image field type's widget!So: removing that bit of test coverage. Will try to refactor it in the morning to make it pick an image from the media library and hence prove it works end-to-end. Will then merge this MR and address #22 in a subsequent MR.
Change record updated: https://www.drupal.org/node/3542579
Comment #24
wim leersDone! (updated test, minor blocking infra fix, update shape matching to use media library for stream wrapper image URIs).
As soon as this is green, will merge this MR and address #22 in a subsequent MR.
Comment #25
wim leersHad to add explicit waits because CI is so slow, but it's passing now! 🥳
Comment #27
wim leersThat's in! Now back to for #22.
Comment #28
wim leersComment #31
wim leers@effulgentsia just +1'd #22 in a meeting 😊👍
Comment #32
wim leersWill land this tomorrow AM to reduce the change rate for #3519247 now that it's unblocked again per #3519247-23: Acquia DAM and Canvas integration.
Comment #33
wim leersThis is now actively blocking #3519247: Acquia DAM and Canvas integration, so picking up again.
Comment #34
wim leersLooks like adopting Symfony's
UrlValidatordoes not what we need — I had to readsrc/Symfony/Component/Validator/Tests/Constraints/UrlValidatorTest.phpbecause the docs at https://symfony.com/doc/current/reference/constraints/Url.html#relativep... are absolute 💩Comment #35
wim leersThis should now be green, at last. Manual testing shows it still works fine, including for the e2e test that was failing for a bit (which was uploading a video).
Comment #36
wim leersFYI this ended up fixing the matching of field instances: #3548298-16: Support linking field types marked SUPPORTED from #3512433 to props in a `ContentTemplate`. 🎉
Comment #37
penyaskito+1 RTBC
Comment #39
wim leers🚢
Comment #41
wim leersUpdated & published the change record: https://www.drupal.org/node/3542579 🎉
Comment #42
wim leersDid the necessary follow-through over at #3542895: `format: uri` and `format: uri-reference` shape matching is too coarse, `StaticPropSource`s using the `link` field type allow incorrect values — that now has an MR up too. Was easy/fast, because I had all this in my brain 👍
Comment #45
wim leersNote: this failed to update
docs/shape-matching.md. Fixed that at https://git.drupalcode.org/project/canvas/-/merge_requests/222/diffs#not... in #3545859: Add a `host-entity-url` prop source for linking to the host entity.