Skip to content

feat: collect nested user-schema fields by dotted path - #756

Draft
vitorbari wants to merge 3 commits into
mainfrom
feat/nested-user-schema-fields
Draft

feat: collect nested user-schema fields by dotted path#756
vitorbari wants to merge 3 commits into
mainfrom
feat/nested-user-schema-fields

Conversation

@vitorbari

@vitorbari vitorbari commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Summary

Flow steps can collect nested user-schema properties by dotted path:

{ "name": "address", "fields": ["address.street", "address.city"], "on_success": "create_user" }

Schema property names may no longer contain a dot, which would be ambiguous with a nested path.
packages/config mirrors the validator so zitadel plan matches apply. No client changes.

Notes

  • Arrays stay opaque and are now rejected at save time rather than misrendered.

Tests

Screenshot 2026-08-07 at 13 50 46

DB:

sqlite> SELECT * FROM user_attributes;
╭───────────────────────────────┬───────┬───────────────────────────────┬───────────────────────────────┬────────────────────────────────╮
│          project_id           │team_id│            user_id            │              key              │             value              │
╞═══════════════════════════════╪═══════╪═══════════════════════════════╪═══════════════════════════════╪════════════════════════════════╡
│proj_01KZE0GJM1G2DV4A5Q4CX9TA4A│       │user_01KZE1037KV6XNXQ08JSKDQ5YY│$schema                        │"sch_01KZE0K19QSHWK5KYH8D43Q7RQ"│
├───────────────────────────────┼───────┼───────────────────────────────┼───────────────────────────────┼────────────────────────────────┤
│proj_01KZE0GJM1G2DV4A5Q4CX9TA4A│       │user_01KZE1037KV6XNXQ08JSKDQ5YY│address.city                   │"Berlin"                        │
├───────────────────────────────┼───────┼───────────────────────────────┼───────────────────────────────┼────────────────────────────────┤
│proj_01KZE0GJM1G2DV4A5Q4CX9TA4A│       │user_01KZE1037KV6XNXQ08JSKDQ5YY│address.geo.datum.reference.   │"foo"                           │
│                               │       │                               │epsg                           │                                │
├───────────────────────────────┼───────┼───────────────────────────────┼───────────────────────────────┼────────────────────────────────┤
│proj_01KZE0GJM1G2DV4A5Q4CX9TA4A│       │user_01KZE1037KV6XNXQ08JSKDQ5YY│address.street                 │"Warsh"                         │
├───────────────────────────────┼───────┼───────────────────────────────┼───────────────────────────────┼────────────────────────────────┤
│proj_01KZE0GJM1G2DV4A5Q4CX9TA4A│       │user_01KZE1037KV6XNXQ08JSKDQ5YY│email                          │"vitor@zitadel.com"             │
╰───────────────────────────────┴───────┴───────────────────────────────┴───────────────────────────────┴────────────────────────────────╯

@vercel

vercel Bot commented Aug 5, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
nextgen Ready Ready Preview Aug 7, 2026 11:05am
nextgen-docs Ready Ready Preview Aug 7, 2026 11:05am
nextgen-mock-zitadel Ready Ready Preview Aug 7, 2026 11:05am

Request Review

@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

🦋 Changeset detected

Latest commit: 489100e

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 20 packages
Name Type
@zitadel/server Minor
@zitadel/config Minor
@zitadel/cli Minor
@zitadel/components Minor
@zitadel/testing Minor
@zitadel/sdk-angular Minor
@zitadel/sdk-next Minor
@zitadel/sdk-nuxt Minor
@zitadel/sdk-qwik Minor
@zitadel/sdk-react Minor
@zitadel/sdk-solid Minor
@zitadel/sdk-svelte Minor
@zitadel/sdk-vue Minor
@zitadel/server-linux-x64 Minor
@zitadel/server-linux-arm64 Minor
@zitadel/server-darwin-x64 Minor
@zitadel/server-darwin-arm64 Minor
@zitadel/server-win32-x64 Minor
@zitadel/api Minor
@zitadel/sdk-core Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vitorbari
vitorbari force-pushed the feat/nested-user-schema-fields branch from 3c18192 to e763bb7 Compare August 5, 2026 08:16
@vitorbari vitorbari changed the title feat(domain): collect nested user-schema properties by dotted path feat: collect nested user-schema properties by dotted path Aug 5, 2026
@vercel
vercel Bot temporarily deployed to Preview – nextgen-docs August 5, 2026 08:54 Inactive
@vitorbari vitorbari linked an issue Aug 7, 2026 that may be closed by this pull request
@vitorbari vitorbari changed the title feat: collect nested user-schema properties by dotted path fix: flow - collect nested user-schema properties by dotted path Aug 7, 2026
@vitorbari vitorbari changed the title fix: flow - collect nested user-schema properties by dotted path feat: collect nested user-schema fields by dotted path Aug 7, 2026
vitorbari and others added 3 commits August 7, 2026 12:58
A flow step could only reference a top-level scalar property. Naming an
object-typed property resolved silently as a text input and only failed at
create_user, with an error the user could not act on; naming a nested leaf
(`address.street`) failed as an unknown property. A schema whose top-level
`required` held an object was unauthorable: the only name that satisfied the
coverage check was the one that misrendered.

The attribute store already keyed nested values by their dotted path and
already honored `x-unique` on a nested leaf, so this closes the flow-engine
half of a contract the rest of the system had implemented.

- Resolve a field by walking its dotted path, one `properties` level per
  segment, reusing AttributeKey.Nodes so a field path and an attribute key
  are the same string by construction.
- Derive Required by ANDing `required` membership across the ancestor chain,
  the only rule under which satisfying the form satisfies schema validation.
- Reject an object- or array-typed property as a field with the new
  ErrFlowFieldNotScalar, including one carrying `properties` but no `type`.
- Check required coverage against nested `required`, so a required object
  demands one of its leaves.
- Merge collected values into a nested document and read them back through
  maputil.GetNested, keeping UserData in the shape the schema validates.
- Reject a schema property name holding a dot, now ambiguous with a path.

The TypeScript plan-time validator mirrors all of it so `plan` and `apply`
still speak the same words, and the normalizer recurses into nested
properties to avoid a phantom GitOps diff.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Example 07 collects an object-typed schema property one leaf at a time
(`address.street`), so the dotted-path spelling has a worked reference
next to the flows that use flat fields.

The example schema in examples_test.go gains a matching `address` object,
keeping `address` optional at the root so the required-coverage check on
examples 01-06 is unaffected. The round-trip test now covers 07, which
means the example is validated end-to-end rather than only documented.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`segments[i]` is `string | undefined` under noUncheckedIndexedAccess, so
indexing with it failed `tsc --build`. Iterating with `segments.entries()`
yields a plain string and drops the optional chain's dead branch.

Because validate.ts then failed to emit its declarations, every test file
importing it fell back to `unknown`, producing a cascade of TS6305/TS7006
errors across the package that all clear with this fix.

The one genuine test-side error went with it: deleting `.required` through
an index expression is unsound for the same reason, so that case now states
its schema shape directly instead of deriving it by mutation.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Inbox

Development

Successfully merging this pull request may close these issues.

bug(flow-engine): nested user-schema fields are unusable

1 participant