Flexible schema support with default fallback in Email Executor#2461
Conversation
📝 WalkthroughWalkthroughThe changes extend the email executor to support delivery skipping via runtime configuration and refactor recipient email resolution to support multiple data sources, including extraction via an entity provider. New constants and utility functions are introduced to support these capabilities. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #2461 +/- ##
==========================================
+ Coverage 88.85% 88.86% +0.01%
==========================================
Files 871 871
Lines 60244 60282 +38
==========================================
+ Hits 53528 53571 +43
+ Misses 4937 4930 -7
- Partials 1779 1781 +2
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
5aecc23 to
97d3af9
Compare
|
Please mark this as an improvement. Note, that MagicLink implementation #1879 depends on this PR. |
There was a problem hiding this comment.
Pull request overview
Adds dynamic recipient email resolution to EmailExecutor so flows can use a non-default email attribute identifier (e.g., workemail) and optionally fall back to resolving the attribute from the DB via EntityProvider.
Changes:
- Add
GetUserAttributehelper to extract a string attribute from a user entity’s JSONAttributes. - Enhance
EmailExecutorto resolve recipient email using the configured EMAIL_INPUT identifier (user inputs/runtime/forwarded data), with DB fallback and askipDeliveryruntime flag. - Update unit tests and wiring to pass
EntityProviderintoEmailExecutor.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| backend/internal/flow/executor/utils.go | Adds GetUserAttribute JSON attribute extraction helper. |
| backend/internal/flow/executor/utils_test.go | Adds tests for GetUserAttribute. |
| backend/internal/flow/executor/email_executor.go | Resolves recipient email using dynamic identifier + forwarded/DB fallback; adds skipDelivery. |
| backend/internal/flow/executor/email_executor_test.go | Expands tests for new resolution paths and updates mocks/expectations. |
| backend/internal/flow/executor/init.go | Wires entityProvider into newEmailExecutor. |
| backend/internal/flow/common/constants.go | Adds RuntimeKeySkipDelivery, InputTypeEmail, and AttributeEmail. |
Comments suppressed due to low confidence (1)
backend/internal/flow/executor/email_executor.go:62
- The executor defines
defaultEmailInputasEMAIL_INPUT, but the base executor is still registered with a default required input of typeTEXT_INPUT(userAttributeEmail). If this executor ever falls back to default inputs (whenctx.NodeInputsis empty), the engine/UI will treat the required email as a text input. Consider usingdefaultEmailInput(andcommon.InputTypeEmail) for the base executor defaults to keep input typing consistent with the new dynamic-email logic.
base := flowFactory.CreateExecutor(
ExecutorNameEmailExecutor,
common.ExecutorTypeUtility,
[]common.Input{},
[]common.Input{
{Identifier: userAttributeEmail, Type: common.InputTypeText, Required: true},
},
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (3)
backend/internal/flow/executor/utils.go (1)
50-53: Optional: preserve the underlying JSON parse error.
errors.New("failed to parse user attributes")discards the originaljson.Unmarshalerror, which can make debugging malformedAttributespayloads harder. Consider wrapping it.♻️ Suggested change
- if err := json.Unmarshal(user.Attributes, &attrs); err != nil { - return "", errors.New("failed to parse user attributes") - } + if err := json.Unmarshal(user.Attributes, &attrs); err != nil { + return "", fmt.Errorf("failed to parse user attributes: %w", err) + }If you adopt this, the
errorsimport can also be dropped since the only remainingerrors.Newat line 47 can be folded into afmt.Errorf(or kept as-is).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@backend/internal/flow/executor/utils.go` around lines 50 - 53, The json.Unmarshal error for user.Attributes is being discarded; update the attrs parse block (the variable attrs and the json.Unmarshal call) to wrap and return the original error (e.g., using fmt.Errorf("failed to parse user attributes: %w", err)) so the underlying JSON error is preserved for debugging, and after that remove or consolidate the now-unnecessary errors import (you can replace other errors.New uses with fmt.Errorf if desired).backend/internal/flow/common/constants.go (1)
226-234: Merge the newAttributeEmailinto the existing attribute const block.
AttributeMobileNumberandAttributeEmailare both "well-known user attribute" names (per the section comment at line 226). Splitting them across twoconstblocks fragments the section unnecessarily.♻️ Suggested change
// Attribute name constants for well-known user attributes used across flow executors. const ( // AttributeMobileNumber is the default attribute name for a user's mobile phone number. AttributeMobileNumber = "mobileNumber" -) -const ( // AttributeEmail is the default attribute name for a user's email. AttributeEmail = "email" )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@backend/internal/flow/common/constants.go` around lines 226 - 234, Merge AttributeEmail into the existing "well-known user attributes" const block so both AttributeMobileNumber and AttributeEmail are declared together; update the const block containing AttributeMobileNumber to also include AttributeEmail (preserve comments for each constant) and remove the separate second const block to avoid fragmenting the related attribute constants.backend/internal/flow/executor/email_executor.go (1)
197-204: Optional: Simplify control flow by removing unnecessaryelseblock.The
elseblock following a return statement is unnecessary and can be simplified. This improves readability by following the idiomatic Go pattern of handling error cases early.♻️ Suggested change
- if recipientEmail, err := GetUserAttribute(user, emailAttr); err == nil { - return recipientEmail - } else { - e.logger.Debug("Email attribute not found in user entity", log.String("attribute", emailAttr), - log.Error(err)) - } + recipientEmail, err := GetUserAttribute(user, emailAttr) + if err == nil { + return recipientEmail + } + e.logger.Debug("Email attribute not found in user entity", + log.String("attribute", emailAttr), log.Error(err))🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@backend/internal/flow/executor/email_executor.go` around lines 197 - 204, The block inside the loop uses an unnecessary else after a return; simplify by handling the error case early and returning on success directly: in the loop that calls GetUserAttribute (using recipientEmail, err, and emailAttr), remove the else branch and instead, after checking err == nil return recipientEmail, and otherwise call e.logger.Debug(...) for the error path; update the code around GetUserAttribute and e.logger.Debug to follow this early-return idiom.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@backend/internal/flow/executor/email_executor.go`:
- Around line 94-98: Add documentation under docs/content/guides that explains
the three user-visible changes introduced by EmailExecutor: (1) document the new
EMAIL_INPUT input type (common.InputTypeEmail) and show how to declare a node
input with type: "EMAIL_INPUT" and a custom identifier (e.g., workEmail) so flow
authors can drive recipient resolution (reference resolveEmailInput to show
where this applies); (2) document the skipDelivery runtime key
(common.RuntimeKeySkipDelivery) including its semantics and example usage to
bypass email sending (reference the early-return block that checks
RuntimeKeySkipDelivery); and (3) document the updated recipient resolution order
and DB fallback logic (UserInputs → RuntimeData → ForwardedData → entityProvider
lookup by userId) and call out the behavioral change where EmailExecutor reads
the email attribute from the user entity when not present in
inputs/runtime/forwarded data (reference the EmailExecutor recipient-resolution
code paths). Ensure the guide includes examples and any authoring notes for
existing flows that may be affected.
---
Nitpick comments:
In `@backend/internal/flow/common/constants.go`:
- Around line 226-234: Merge AttributeEmail into the existing "well-known user
attributes" const block so both AttributeMobileNumber and AttributeEmail are
declared together; update the const block containing AttributeMobileNumber to
also include AttributeEmail (preserve comments for each constant) and remove the
separate second const block to avoid fragmenting the related attribute
constants.
In `@backend/internal/flow/executor/email_executor.go`:
- Around line 197-204: The block inside the loop uses an unnecessary else after
a return; simplify by handling the error case early and returning on success
directly: in the loop that calls GetUserAttribute (using recipientEmail, err,
and emailAttr), remove the else branch and instead, after checking err == nil
return recipientEmail, and otherwise call e.logger.Debug(...) for the error
path; update the code around GetUserAttribute and e.logger.Debug to follow this
early-return idiom.
In `@backend/internal/flow/executor/utils.go`:
- Around line 50-53: The json.Unmarshal error for user.Attributes is being
discarded; update the attrs parse block (the variable attrs and the
json.Unmarshal call) to wrap and return the original error (e.g., using
fmt.Errorf("failed to parse user attributes: %w", err)) so the underlying JSON
error is preserved for debugging, and after that remove or consolidate the
now-unnecessary errors import (you can replace other errors.New uses with
fmt.Errorf if desired).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: d50033b3-6506-4e42-a07e-ba1742d2b030
📒 Files selected for processing (6)
backend/internal/flow/common/constants.gobackend/internal/flow/executor/email_executor.gobackend/internal/flow/executor/email_executor_test.gobackend/internal/flow/executor/init.gobackend/internal/flow/executor/utils.gobackend/internal/flow/executor/utils_test.go
There was a problem hiding this comment.
🧹 Nitpick comments (1)
backend/internal/flow/executor/constants.go (1)
129-132: Consolidate with the existinguserAttributeEmailto avoid duplicate string literals.
AttributeEmailduplicates the value ofuserAttributeEmaildefined at line 66. Two constants holding the same"email"string for the same purpose risk drift if one is ever changed. Either drop the unexported one in favor of the exported constant (and update internal references), or define one in terms of the other. Also, the new constant breaks the existinguserAttribute*naming pattern used elsewhere in this file — considerUserAttributeEmailfor consistency if external callers need it exported.♻️ Proposed consolidation
@@ - userAttributeUsername = "username" - userAttributePassword = "password" - userAttributeUserID = "userID" - userAttributeEmail = "email" - userAttributeGroups = "groups" - userAttributeSub = "sub" + userAttributeUsername = "username" + userAttributePassword = "password" + userAttributeUserID = "userID" + userAttributeEmail = AttributeEmail + userAttributeGroups = "groups" + userAttributeSub = "sub" @@ -const ( - // AttributeEmail is the default attribute name for a user's email. - AttributeEmail = "email" -) +// User attribute name constants exported for cross-package use. +const ( + // AttributeEmail is the default attribute name for a user's email. + AttributeEmail = "email" +)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@backend/internal/flow/executor/constants.go` around lines 129 - 132, The file introduces a duplicate "email" constant: AttributeEmail and the existing userAttributeEmail; remove the duplication by consolidating them—either delete AttributeEmail and replace its references with userAttributeEmail (or vice‑versa), or define one in terms of the other (e.g., AttributeEmail = userAttributeEmail) to keep a single source of truth; if the exported name is required by other packages, prefer renaming to a consistent exported identifier (e.g., UserAttributeEmail) and update all usages accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@backend/internal/flow/executor/constants.go`:
- Around line 129-132: The file introduces a duplicate "email" constant:
AttributeEmail and the existing userAttributeEmail; remove the duplication by
consolidating them—either delete AttributeEmail and replace its references with
userAttributeEmail (or vice‑versa), or define one in terms of the other (e.g.,
AttributeEmail = userAttributeEmail) to keep a single source of truth; if the
exported name is required by other packages, prefer renaming to a consistent
exported identifier (e.g., UserAttributeEmail) and update all usages
accordingly.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 945c8e75-5fbc-407f-89ac-988ad0957a38
📒 Files selected for processing (3)
backend/internal/flow/common/constants.gobackend/internal/flow/executor/constants.gobackend/internal/flow/executor/email_executor.go
✅ Files skipped from review due to trivial changes (1)
- backend/internal/flow/common/constants.go
🚧 Files skipped from review as they are similar to previous changes (1)
- backend/internal/flow/executor/email_executor.go
96a13f1 to
e2161c5
Compare
e1c0548 to
1d55815
Compare
Signed-off-by: RandithaK <me@randitha.net>
1d55815 to
256493a
Compare
Purpose
Added capability to resolve and use a flexible name for email, in the user schema.
Example Use Case,
In case the usertype has workemail instead of email.
Approach
Resolve the email address containing field from the relevant flows json file.
Tested with the following file, as user onboarding json, with a modified user schema
{ "name": "User Onboarding Flow", "handle": "default-user-onboarding", "flowType": "USER_ONBOARDING", "nodes": [ { "id": "start", "type": "START", "onSuccess": "permission_validator" }, { "id": "permission_validator", "type": "TASK_EXECUTION", "properties": { "requiredScopes": ["system"] }, "executor": { "name": "PermissionValidator" }, "onSuccess": "user_type_resolver" }, { "id": "user_type_resolver", "type": "TASK_EXECUTION", "executor": { "name": "UserTypeResolver" }, "onSuccess": "ou_selection", "onIncomplete": "prompt_usertype" }, { "id": "prompt_usertype", "type": "PROMPT", "meta": { "components": [ { "align": "center", "type": "TEXT", "id": "heading_usertype", "label": "{{ t(onboarding:forms.user_type.title) }}", "variant": "HEADING_1" }, { "type": "TEXT", "id": "subtitle_usertype", "label": "{{ t(onboarding:forms.user_type.subtitle) }}" }, { "type": "BLOCK", "id": "block_usertype", "components": [ { "type": "SELECT", "id": "usertype_input", "ref": "userType", "label": "{{ t(onboarding:forms.user_type.fields.user_type.label) }}", "placeholder": "{{ t(onboarding:forms.user_type.fields.user_type.placeholder) }}", "required": true, "options": [] }, { "type": "ACTION", "id": "action_usertype", "label": "{{ t(onboarding:forms.user_type.actions.continue.label) }}", "variant": "PRIMARY", "eventType": "SUBMIT" } ] } ] }, "prompts": [ { "inputs": [ { "ref": "usertype_input", "identifier": "userType", "type": "SELECT", "required": true } ], "action": { "ref": "action_usertype", "nextNode": "user_type_resolver" } } ] }, { "id": "ou_selection", "type": "TASK_EXECUTION", "properties": { "resolveFrom": "prompt" }, "executor": { "name": "OUResolverExecutor" }, "onSuccess": "prompt_email", "onIncomplete": "prompt_ou_selection" }, { "id": "prompt_ou_selection", "type": "PROMPT", "meta": { "components": [ { "align": "center", "type": "TEXT", "id": "heading_ou_selection", "label": "{{ t(onboarding:forms.ou_selection.title) }}", "variant": "HEADING_1" }, { "type": "TEXT", "id": "subtitle_ou_selection", "label": "{{ t(onboarding:forms.ou_selection.subtitle) }}" }, { "type": "BLOCK", "id": "block_ou_selection", "components": [ { "type": "OU_SELECT", "id": "ou_selection_input", "ref": "ouId", "label": "{{ t(onboarding:forms.ou_selection.fields.ou.label) }}", "required": true }, { "type": "ACTION", "id": "action_ou_selection", "label": "{{ t(onboarding:forms.ou_selection.actions.continue.label) }}", "variant": "PRIMARY", "eventType": "SUBMIT" } ] } ] }, "prompts": [ { "inputs": [ { "ref": "ou_selection_input", "identifier": "ouId", "type": "OU_SELECT", "required": true } ], "action": { "ref": "action_ou_selection", "nextNode": "ou_selection" } } ] }, { "id": "prompt_email", "type": "PROMPT", "meta": { "components": [ { "align": "center", "type": "TEXT", "id": "text_header_email", "label": "{{ t(onboarding:forms.email.title) }}", "variant": "HEADING_1" }, { "type": "BLOCK", "id": "block_email", "components": [ { "id": "input_prompt_email", "ref": "workemail", "type": "EMAIL_INPUT", "label": "{{ t(onboarding:forms.email.fields.email.label) }}", "required": true, "placeholder": "{{ t(onboarding:forms.email.fields.email.placeholder) }}" }, { "type": "ACTION", "id": "action_submit_email", "label": "{{ t(onboarding:forms.email.actions.next.label) }}", "variant": "PRIMARY", "eventType": "SUBMIT" } ] } ] }, "prompts": [ { "inputs": [ { "ref": "input_prompt_email", "identifier": "workemail", "type": "EMAIL_INPUT", "required": true } ], "action": { "ref": "action_submit_email", "nextNode": "check_email_uniqueness" } } ] }, { "id": "check_email_uniqueness", "type": "TASK_EXECUTION", "executor": { "name": "AttributeUniquenessValidator" }, "onSuccess": "prompt_invite_method", "onIncomplete": "prompt_email" }, { "id": "prompt_invite_method", "type": "PROMPT", "meta": { "components": [ { "type": "TEXT", "id": "text_header_invite_method", "label": "{{ t(onboarding:forms.invite_mode.title) }}", "variant": "HEADING_1", "align": "center" }, { "type": "TEXT", "id": "text_subtitle_invite_method", "label": "{{ t(onboarding:forms.invite_mode.subtitle) }}" }, { "type": "BLOCK", "id": "block_invite_method_actions", "components": [ { "type": "STACK", "id": "stack_invite_actions", "direction": "row", "justify": "center", "components": [ { "type": "ACTION", "id": "action_share_manually", "label": "{{ t(onboarding:forms.invite_mode.actions.link.label) }}", "variant": "OUTLINED", "eventType": "SUBMIT" }, { "type": "ACTION", "id": "action_send_email_invite", "label": "{{ t(onboarding:forms.invite_mode.actions.email.label) }}", "variant": "PRIMARY", "eventType": "SUBMIT" } ] } ] } ] }, "prompts": [ { "action": { "ref": "action_send_email_invite", "nextNode": "invite_generate_email" } }, { "action": { "ref": "action_share_manually", "nextNode": "invite_generate_manual" } } ] }, { "id": "invite_generate_manual", "type": "TASK_EXECUTION", "executor": { "name": "InviteExecutor", "mode": "generate" }, "onSuccess": "invite_link_status" }, { "id": "invite_generate_email", "type": "TASK_EXECUTION", "executor": { "name": "InviteExecutor", "mode": "generate" }, "onSuccess": "send_invite_email" }, { "id": "send_invite_email", "type": "TASK_EXECUTION", "properties": { "emailTemplate": "USER_INVITE" }, "executor": { "name": "EmailExecutor", "mode": "send", "inputs": [ { "ref": "input_workemail", "identifier": "workemail", "type": "EMAIL_INPUT", "required": true } ] }, "onSuccess": "email_invite_status", "onFailure": "invite_email_service_unavailable" }, { "id": "email_invite_status", "type": "PROMPT", "meta": { "components": [ { "align": "center", "type": "TEXT", "id": "email_status_icon", "label": "✅", "variant": "HEADING_1" }, { "align": "center", "type": "TEXT", "id": "email_status_heading", "label": "{{ t(onboarding:forms.invite_email_sent.title) }}", "variant": "HEADING_1" }, { "align": "center", "type": "TEXT", "id": "email_status_message", "label": "{{ t(onboarding:forms.invite_email_sent.message) }}" } ] }, "message": "{{ t(onboarding:forms.invite_email_sent.title) }}", "next": "invite_verify" }, { "id": "invite_email_service_unavailable", "type": "PROMPT", "meta": { "components": [ { "align": "center", "type": "TEXT", "id": "email_unavailable_icon", "label": "⚠️", "variant": "HEADING_1" }, { "align": "center", "type": "TEXT", "id": "email_unavailable_heading", "label": "{{ t(onboarding:forms.invite_email_unavailable.title) }}", "variant": "HEADING_1" }, { "align": "center", "type": "TEXT", "id": "email_unavailable_message", "label": "{{ t(onboarding:forms.invite_email_unavailable.message) }}" }, { "type": "COPYABLE_TEXT", "id": "email_unavailable_link_copyable", "label": "{{ t(onboarding:forms.invite_link_status.link_label) }}", "source": "inviteLink" } ] }, "message": "{{ t(onboarding:forms.invite_email_unavailable.title) }}", "next": "invite_link_status" }, { "id": "invite_link_status", "type": "PROMPT", "meta": { "components": [ { "align": "center", "type": "TEXT", "id": "link_status_icon", "label": "✅", "variant": "HEADING_1" }, { "align": "center", "type": "TEXT", "id": "link_status_heading", "label": "{{ t(onboarding:forms.invite_link_status.title) }}", "variant": "HEADING_1" }, { "align": "center", "type": "TEXT", "id": "link_status_message", "label": "{{ t(onboarding:forms.invite_link_status.message) }}" }, { "type": "COPYABLE_TEXT", "id": "invite_link_copyable", "label": "{{ t(onboarding:forms.invite_link_status.link_label) }}", "source": "inviteLink" } ] }, "message": "The invite link is ready to share", "next": "invite_verify" }, { "id": "invite_verify", "type": "TASK_EXECUTION", "executor": { "name": "InviteExecutor", "mode": "verify", "inputs": [ { "ref": "input_003", "identifier": "inviteToken", "type": "HIDDEN", "required": true } ] }, "onSuccess": "prompt_user_details" }, { "id": "prompt_user_details", "type": "PROMPT", "meta": { "components": [ { "align": "center", "type": "TEXT", "id": "text_header_details", "label": "{{ t(onboarding:forms.user_details.title) }}", "variant": "HEADING_1" }, { "type": "BLOCK", "id": "block_user_details", "components": [ { "id": "input_prompt_username", "ref": "username", "type": "TEXT_INPUT", "label": "{{ t(onboarding:forms.user_details.fields.username.label) }}", "required": true, "placeholder": "{{ t(onboarding:forms.user_details.fields.username.placeholder) }}" }, { "id": "input_prompt_given_name", "ref": "given_name", "type": "TEXT_INPUT", "label": "{{ t(onboarding:forms.user_details.fields.first_name.label) }}", "required": false, "placeholder": "{{ t(onboarding:forms.user_details.fields.first_name.placeholder) }}" }, { "id": "input_prompt_family_name", "ref": "family_name", "type": "TEXT_INPUT", "label": "{{ t(onboarding:forms.user_details.fields.last_name.label) }}", "required": false, "placeholder": "{{ t(onboarding:forms.user_details.fields.last_name.placeholder) }}" }, { "type": "ACTION", "id": "action_submit_details", "label": "{{ t(onboarding:forms.user_details.actions.next.label) }}", "variant": "PRIMARY", "eventType": "SUBMIT" } ] } ] }, "prompts": [ { "inputs": [ { "ref": "input_prompt_username", "identifier": "username", "type": "TEXT_INPUT", "required": true }, { "ref": "input_prompt_given_name", "identifier": "given_name", "type": "TEXT_INPUT", "required": false }, { "ref": "input_prompt_family_name", "identifier": "family_name", "type": "TEXT_INPUT", "required": false } ], "action": { "ref": "action_submit_details", "nextNode": "check_user_details_uniqueness" } } ] }, { "id": "check_user_details_uniqueness", "type": "TASK_EXECUTION", "executor": { "name": "AttributeUniquenessValidator" }, "onSuccess": "prompt_credential", "onIncomplete": "prompt_user_details" }, { "id": "prompt_credential", "type": "PROMPT", "meta": { "components": [ { "align": "center", "type": "TEXT", "id": "text_header_pwd", "label": "{{ t(onboarding:forms.credential.title) }}", "variant": "HEADING_1" }, { "type": "BLOCK", "id": "block_pwd", "components": [ { "id": "input_prompt_password", "ref": "password", "type": "PASSWORD_INPUT", "label": "{{ t(onboarding:forms.credential.fields.password.label) }}", "required": true, "placeholder": "{{ t(onboarding:forms.credential.fields.password.placeholder) }}" }, { "type": "ACTION", "id": "action_submit_pwd", "label": "{{ t(onboarding:forms.credential.actions.submit.label) }}", "variant": "PRIMARY", "eventType": "SUBMIT" } ] } ] }, "prompts": [ { "inputs": [ { "ref": "input_prompt_password", "identifier": "password", "type": "PASSWORD_INPUT", "required": true } ], "action": { "ref": "action_submit_pwd", "nextNode": "provisioning" } } ] }, { "id": "provisioning", "type": "TASK_EXECUTION", "executor": { "name": "ProvisioningExecutor", "inputs": [ { "ref": "input_email", "identifier": "workemail", "type": "EMAIL_INPUT", "required": true }, { "ref": "input_username", "identifier": "username", "type": "TEXT_INPUT", "required": true }, { "ref": "input_password", "identifier": "password", "type": "PASSWORD_INPUT", "required": true }, { "ref": "input_given_name", "identifier": "given_name", "type": "TEXT_INPUT", "required": false }, { "ref": "input_family_name", "identifier": "family_name", "type": "TEXT_INPUT", "required": false } ] }, "onSuccess": "registration_complete" }, { "id": "registration_complete", "type": "PROMPT", "meta": { "components": [ { "align": "center", "type": "TEXT", "id": "registration_complete_icon", "label": "✅", "variant": "HEADING_1" }, { "align": "center", "type": "TEXT", "id": "registration_complete_heading", "label": "{{ t(invite:complete.title) }}", "variant": "HEADING_1" }, { "align": "center", "type": "TEXT", "id": "registration_complete_message", "label": "{{ t(invite:complete.description) }}" } ] }, "message": "Registration complete", "next": "end" }, { "id": "end", "type": "END" } ] }Related Issues
Related PRs
Checklist
breaking changelabel added.Security checks
Summary by CodeRabbit
Release Notes
New Features
Tests