Problem/Motivation

Currently, JavaScriptComponent config entities can declare data dependencies via dataDependencies.urls (external URLs fetched by the component) and dataDependencies.drupalSettings (Canvas Drupal settings needed by the component). However, there is no way for a JS component to declare that one of its props depends on entity field data.

This is the first foundational step toward #3573831: [META] Code Components: add an "entity reference" prop type — enables "view modes" that combine multiple entities plus static inputs!. Before the UI, runtime evaluation, or entity reference prop type can be built, the config schema needs to support declaring which entity fields each prop depends on.

Proposed resolution

Add a new optional entityFields key to the dataDependencies mapping on JavaScriptComponent config entities (canvas.js_component.*).

Schema structure:

  • entityFields is optional (requiredKey: false), a sequence ordered by key
  • Each key is a prop name from the component's props (validated via SequenceKeysMustMatch in subset mode)
  • Each value is an array of field prop expression strings, validated by ValidStructuredDataPropExpression with allowed types:
    • FieldPropExpression
    • FieldObjectPropsExpression
    • ReferenceFieldPropExpression

Validation constraints:

  • SequenceKeysMustMatch (extended): gained a matchType parameter with values same-set (default, pre-existing behavior — validated sequence must contain exactly the same keys as the target sequence) and subset (new — validated sequence's keys must all exist in the target sequence, but missing keys are allowed). Used here in subset mode so entityFields keys must be a subset of the component's props — only entity-reference props will have entityFields entries. Preferred over introducing a separate SequenceKeysAreSubsetOf constraint to avoid duplicating the sequence-comparison logic.
  • EntityFieldExpressionsSameTarget (new): ensures all expressions within a single prop target the same entity type and bundle.
  • ExpressionTargetEntityBundleExists (new): validates entity type existence and bundle existence for each expression string. (Originally scoped as ValidEntityFieldExpressionTarget; renamed for clarity — the constraint is reusable beyond the entityFields context.)
  • Entity reference props cannot be required: props with entityFields entries must not appear in required (validated in JsComponentHasValidAndSupportedSdcMetadata). A referenced entity may disappear, so the component must tolerate NULL.

Config dependency calculation:

Override JavaScriptComponent::calculateDependencies() so each expression in dataDependencies.entityFields contributes its module and config dependencies (field configs, bundle configs, entity type provider modules) to the saved config entity. Follows the same addDependencies() pattern already used by ContentTemplate, Pattern, and PageRegion for component-tree expressions. Without this, ConfigDependencyManager would not know about the field/bundle/module references, breaking cascading deletes, import ordering, and cache invalidation.

OpenAPI:
The CodeComponent schema in openapi.yml is updated to document entityFields as an object with additionalProperties of type array<string>.

Remaining tasks

Review and commit.

User interface changes

None.

API changes

The dataDependencies object in API responses for code components (GET /canvas/api/v0/config/js_component/*) may now include an entityFields key. This is a backwards-compatible addition — existing responses without entityFields remain valid.

Data model changes

New optional key in canvas.js_component.* config schema:

dataDependencies:
  mapping:
    entityFields:
      requiredKey: false
      type: sequence
      orderby: key
      constraints:
        NotBlank:
          message: "There must be >=1 entity reference prop; otherwise the 'entityFields' key should be omitted."
        SequenceKeysMustMatch:
          matchType: subset
          propertyPathToSequence: props
      sequence:
        type: sequence
        orderby: value
        constraints:
          NotBlank:
            message: 'There must be >=1 entity field expression; otherwise the entity reference prop should be deleted.'
          EntityFieldExpressionsSameTarget: ~
        sequence:
          type: string
          constraints:
            ValidStructuredDataPropExpression:
              choices:
                - FieldPropExpression
                - FieldObjectPropsExpression
                - ReferenceFieldPropExpression
            ExpressionTargetEntityBundleExists: ~

No database changes. No config migration needed (requiredKey: false means existing JS components pass validation without changes).

The dependencies array on canvas.js_component.* exports may now include new module/config entries derived from entityFields.

Deferred to follow-ups

Issue fork canvas-3585298

Command icon 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

penyaskito created an issue. See original summary.

penyaskito’s picture

Component: Theme builder » Data model

Isn't this data model component?

penyaskito’s picture

Issue summary: View changes

Updated issue-summary.

penyaskito’s picture

Status: Active » Needs review
penyaskito’s picture

wim leers’s picture

Assigned: Unassigned » penyaskito
Status: Needs review » Needs work
Issue tags: +blocker, +validation, +Configuration schema

No database changes. No config migration needed (requiredKey: false means existing JS components pass validation without changes).

Indeed! 😄🚀

Nice work on SequenceKeysAreSubsetOf, EntityFieldExpressionsSameTarget and ValidEntityFieldExpressionTarget! I think ValidEntityFieldExpressionTarget should be renamed though, and I think SequenceKeysAreSubsetOf could be done in a way that reduces maintenance cost.

I would've expected this to introduce the updated JavaScriptComponent:: calculateDependencies() too?

Approved with remarks 😊 None of my feedback is merge-blocking; I believe all of it would make future work simpler, but I defer to your judgment, @penyaskito.

penyaskito’s picture

Issue summary: View changes

Added config dependency calculation to the IS.

penyaskito’s picture

Assigned: penyaskito » longwave
Status: Needs work » Needs review

Reviewed all feedback + extra test coverage for calculateDependencies()

penyaskito’s picture

Issue summary: View changes

Updated IS

longwave’s picture

Status: Needs review » Reviewed & tested by the community

Tiny bikeshed comment about naming things but otherwise this all looks good to go to me.

  • penyaskito committed 5aa3ed14 on 1.x
    chore(Data model): #3585298 Add `dataDependencies.entityFields` to `...
longwave’s picture

Status: Reviewed & tested by the community » Fixed

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.