Overview

Unlike for the block component source plugin (see #3524399: `BlockComponent::validateComponentInput()` allows garbage to pile up), no garbage values are AFAICT allowed for the sdc and js component sources.

This is thanks to GeneratedFieldExplicitInputUxComponentSourceBase::validateComponentInput() calling

$this->componentValidator->validateProps($resolvedInputValues, $this->getSdcPlugin());

which would complain about unused props.

… or so I think 😅 Nope: see @akhil babu's research in #5.

Proposed resolution

Add test coverage to prove that the SDC + JS component sources are unaffected. → disproven by @akhil babu in #5.

  1. Test coverage: a new GeneratedFieldExplicitInputUxComponentSourceBase::testValidateComponentInput(), that tests garbage values being set for the sdc.xb_test_sdc.props-slotsSDC.
  2. Fix GeneratedFieldExplicitInputUxComponentSourceBase::validateComponentInput().

User interface changes

None.

Issue fork canvas-3524401

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:

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

wim leers created an issue. See original summary.

wim leers’s picture

Issue tags: +garbage
akhil babu’s picture

Could you please give an example for 'garbage value'?

wim leers’s picture

An SDC with the following in its *.component.yml:

props:
  type: object
  required:
    - heading
  properties:
    heading:
      type: string
      title: Heading
      examples:
        - blabla

… yet the explicit input it receives is not

heading: "something"

but

heading: "something"
foo: :"bar"

IOW: some key-value pairs that are not expected by the SDC!

akhil babu’s picture

Thanks @wim leers🙏. I just tested this using the 'Heading' SDC, by adding an extra property 'textUnwanted' when making a curl request to the endpoint /xb/api/v0/layout/xb_page/5 and the changes were successfully saved. There were no validation errors regarding the unexpected prop

curl --location 'https://starshot.ddev.site/xb/api/v0/layout/xb_page/5' \
--header 'accept: */*' \
--header 'accept-language: en-US,en-GB;q=0.9,en;q=0.8' \
--header 'content-type: application/json' \
--header 'origin: https://starshot.ddev.site' \
--header 'priority: u=1, i' \
--header 'referer: https://starshot.ddev.site/xb/xb_page/5/editor/component/fdc18261-ef6e-41bd-bdba-99ecc72bf359' \
--header 'sec-ch-ua: "Chromium";v="136", "Google Chrome";v="136", "Not.A/Brand";v="99"' \
--header 'sec-ch-ua-mobile: ?0' \
--header 'sec-ch-ua-platform: "Linux"' \
--header 'sec-fetch-dest: empty' \
--header 'sec-fetch-mode: cors' \
--header 'sec-fetch-site: same-origin' \
--header 'user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36' \
--header 'x-csrf-token: PIqRn04kuCGgm5leY3w_pc0Oq9ZGv0mzWXds0SE7cmg' \
--header 'Cookie: SSESS1a128ff57fee5bd2b5663cea7bd52db5=n4epfmoesptq7m91dcj6skrvtl' \
--data '{
  "layout": [
    {
      "nodeType": "region",
      "id": "content",
      "name": "Content",
      "components": [
        {
          "slots": [],
          "nodeType": "component",
          "type": "sdc.experience_builder.heading",
          "uuid": "fdc18261-ef6e-41bd-bdba-99ecc72bf359"
        }
      ]
    }
  ],
  "model": {
    "fdc18261-ef6e-41bd-bdba-99ecc72bf359": {
      "name": "Heading",
      "resolved": {
        "text": "A heading element new text",
        "style": "primary",
        "element": "h1",
        "textUnwanted": "An unwanted heading" // ⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️Invalid prop
      },
      "source": {
        "text": {
          "expression": "ℹ︎string␟value",
          "sourceType": "static:field_item:string",
          "value": [
            {
              "value": "A heading element new text"
            }
          ]
        },
        "textUnwanted": { // ⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️Invalid prop
          "expression": "ℹ︎string␟value",
          "sourceType": "static:field_item:string",
          "value": [
            {
              "value": "An unwanted heading"
            }
          ]
        },
        "style": {
          "expression": "ℹ︎list_string␟value",
          "sourceType": "static:field_item:list_string",
          "value": [
            {
              "value": "primary"
            }
          ],
          "sourceTypeSettings": {
            "storage": {
              "allowed_values": [
                {
                  "value": "primary",
                  "label": "primary"
                },
                {
                  "value": "secondary",
                  "label": "secondary"
                }
              ]
            }
          }
        },
        "element": {
          "expression": "ℹ︎list_string␟value",
          "sourceType": "static:field_item:list_string",
          "value": [
            {
              "value": "h1"
            }
          ],
          "sourceTypeSettings": {
            "storage": {
              "allowed_values": [
                {
                  "value": "div",
                  "label": "div"
                },
                {
                  "value": "h1",
                  "label": "h1"
                },
                {
                  "value": "h2",
                  "label": "h2"
                },
                {
                  "value": "h3",
                  "label": "h3"
                },
                {
                  "value": "h4",
                  "label": "h4"
                },
                {
                  "value": "h5",
                  "label": "h5"
                },
                {
                  "value": "h6",
                  "label": "h6"
                }
              ]
            }
          }
        }
      }
    }
  },
  "entity_form_fields": {
    "langcode[0][value]": "en",
    "revision_log[0][value]": "",
    "title[0][value]": "Untitled page",
    "path[0][alias]": "",
    "path[0][source]": "/page/5",
    "path[0][langcode]": "en",
    "image[media_library_selection]": "",
    "description[0][value]": ""
  }
}'

Just to confirm, does this mean validateComponentInput() currently allows unexpected properties (i.e., garbage values) for SDCs?

wim leers’s picture

Title: Test coverage to prove `GeneratedFieldExplicitInputUxComponentSourceBase::validateComponentInput()` disallows garbage values » `GeneratedFieldExplicitInputUxComponentSourceBase::validateComponentInput()` allows garbage to pile up
Issue summary: View changes

Just to confirm, does this mean validateComponentInput() currently allows unexpected properties (i.e., garbage values) for SDCs?

Indeed it does 😭

Matching the title of #3524399: `BlockComponent::validateComponentInput()` allows garbage to pile up then. 😭

Updated issue summary with an adjusted proposed resolution.

Credited you for the valuable research you've already done here, @akhil babu! 🙏

akhil babu’s picture

Thanks @wim leers. I have updated GeneratedFieldExplicitInputUxComponentSourceBase::validateComponentInput() and added a check for unexpected props.

However, no exception was thrown for the curl request mentioned in #5, and the heading component was still added to the canvas. The expected error only appeared when publishing the page

Shouldn't the error have been thrown at the time of the request?

I also tried to validate 'missing' props but that resulted in many test failures as some props are not always part of user input (Like 'attribute' prop in sdc.experience_builder.my-hero component)

wim leers’s picture

Issue tags: +stable blocker
lauriii’s picture

Issue tags: -stable blocker

Project: Experience Builder » Drupal Canvas
Version: 0.x-dev » 1.x-dev

Experience Builder has been renamed to Drupal Canvas in preparation for its beta release. You can now track issues on the new project page.

attilatilman made their first commit to this issue’s fork.

larowlan made their first commit to this issue’s fork.

larowlan’s picture

Status: Active » Needs review

Updated the MR based on my review - but I'm wondering if we should just do GC on those properties in \Drupal\canvas\ComponentSource\ComponentSourceInterface::clientModelToInput instead rather than raise an Exception?

wim leers’s picture

Status: Needs review » Needs work

Nice progress!

!424 looks great, but incomplete?

!1039 can be closed as a duplicate of !424?

penyaskito’s picture

Assigned: Unassigned » attilatilman

Added suggestions for expanding missing test coverage.

attilatilman’s picture

Assigned: attilatilman » Unassigned
Status: Needs work » Needs review

penyaskito’s picture

Assigned: Unassigned » attilatilman
Status: Needs review » Reviewed & tested by the community
Issue tags: -Needs tests

Looks good to me. The missing test coverage was already there ¿? Guess I overlooked it.

Fixed some logic for this to be reused by any other component source extending GeneratedFieldExplicitInputUxComponentSourceBase, even in contrib.

mglaman made their first commit to this issue’s fork.

mglaman’s picture

Assigned: attilatilman » Unassigned
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.

wim leers’s picture

Nice to see this in! 👏

Next: the same for its sibling issue for the other component source that Canvas ships with: #3524399: `BlockComponent::validateComponentInput()` allows garbage to pile up.

wim leers’s picture

Issue tags: +technical debt

Status: Fixed » Closed (fixed)

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