Problem/Motivation
This is part 1 of splitting the large contribution in #3595219: 3.0 Exploration into reviewable pieces. It is the foundation the per-element issues build on, and should land first.
When the module resolves a built form's elements, it walks the render array produced by Webform and exposes each element's metadata, default value and options. Two correctness problems exist in 3.x today:
- Cacheability is dropped. Element render arrays carry cache metadata (cache tags, contexts, and a max-age) — for example a default value computed from a token like
[current-user:mail]is per-user and must not be cached across users, and option lists built from entities carry the entities' cache tags. The current resolvers read values out of the render array but never bubble its#cachemetadata into the GraphQL response, so the response is over-cached and can leak one user's data to another. - Non-renderable and placeholder elements are exposed. Elements that Webform decides not to render on the form (access-denied,
#access = FALSE, value/markup-only containers in some cases) are still walked and surface as meaningless entries. Related to this, an unavailable form (see #3595219: 3.0 Exploration'sWebformFormwork) can still produce stray element entries.
Steps to reproduce
- Add a textfield whose
#default_valueis[current-user:mail]. - Query the form's
elements { ... defaultValue }as user A, then as user B. - Expected: each user sees their own e-mail; the response is not cached across users (a
usercache context is present).
Actual: the token-derived default value is cached and the second user can be served the first user's value; the per-user cache context is missing from the response.
Proposed resolution
Make the element resolvers cacheability-aware and stop exposing elements Webform would not render:
- Bubble the
#cachemetadata (tags, contexts, max-age) of every element render array into the GraphQL field context, so it is merged into the response's cache metadata — the same mechanism GraphQL already uses elsewhere. This includes honouring max-age (e.g.0for uncacheable elements) rather than only tags/contexts. - Resolve
defaultValuethrough a dedicatedwebform_element_default_valuedata producer that renders the default value in a render context and captures the resulting cacheability, instead of reading a bare value. This is what makes token-derived and entity-derived defaults safe to cache. A read-only producer keeps the logic testable and reusable by the per-element issues. - Fix the cacheability leak from token resolution: tokens replaced while building element data must contribute their cache metadata (e.g.
usercontext) to the response. - Exclude elements that are not meant to render on the form from the exposed element list, and make an unavailable form return no stray elements.
The kernel test base (GraphQLWebformKernelTestBase) gains assertion helpers for asserting the response's cache tags / contexts / max-age, so every per-element issue that follows can assert cacheability honestly.
Remaining tasks
- Bubble element render-array cacheability (tags, contexts, max-age) in
src/Plugin/GraphQL/DataProducer/WebformElements.php. - Add
src/Plugin/GraphQL/DataProducer/WebformElementDefaultValue.phpand routedefaultValuethrough it. - Fix the token-resolution cacheability leak.
- Exclude non-renderable elements in
WebformElements.php/WebformElementMetadata.php; return no elements for an unavailable form. - Add cacheability assertion helpers to
tests/src/Kernel/GraphQLWebformKernelTestBase.php. - Tests:
DefaultValueTest,DefaultValueTokenCacheabilityTest,TokenPrefillCacheabilityTest, plus updated expectations for existing element/markup/title tests now that cache metadata is reported.
API changes
No schema shape changes. The cache metadata of responses changes: responses that were previously (incorrectly) cacheable now carry the correct cache contexts/tags/max-age — e.g. token-derived default values gain a user cache context. This is a correctness fix, not a field addition.
Release notes snippet
Element data (default values, options) now correctly propagates Drupal cache metadata into the GraphQL response, so per-user and entity-derived values are no longer over-cached, and elements that Webform does not render are no longer exposed as placeholder entries.
Issue fork graphql_webform-3598935
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 #3
pfrenssen