Skip to content

Email Executor generic template data handling#2485

Merged
ThaminduDilshan merged 1 commit intoasgardeo:mainfrom
RandithaK:improvement/adaptive-email-template
Apr 29, 2026
Merged

Email Executor generic template data handling#2485
ThaminduDilshan merged 1 commit intoasgardeo:mainfrom
RandithaK:improvement/adaptive-email-template

Conversation

@RandithaK
Copy link
Copy Markdown
Contributor

@RandithaK RandithaK commented Apr 29, 2026

Purpose

The email sending capability has been generalised to support templating without code changes.

Now the email system accepts template name and data and applies as needed


Approach

Make the email system templating easier to scale with diverging use-cases, using forwarded data.

The caller should use the following pattern.

execResp.ForwardedData[common.ForwardedDataKeyTemplateData] = map[string]string{
		"inviteLink": inviteLink,
		"appName":    ctx.Application.Name,
	}
	if ctx.FlowType == common.FlowTypeRegistration {
		execResp.ForwardedData[common.ForwardedDataKeyTemplateName] = string(template.ScenarioSelfRegistration)
	} else {
		execResp.ForwardedData[common.ForwardedDataKeyTemplateName] = string(template.ScenarioUserInvite)
	}

Related Issues

  • N/A

Related PRs

  • N/A

Checklist

  • Followed the contribution guidelines.
  • Manual test round performed and verified.
  • Documentation provided. (Add links if there are any)
    • Ran Vale and fixed all errors and warnings
  • Tests provided. (Add links if there are any)
    • Unit Tests
    • Integration Tests
  • Breaking changes. (Fill if applicable)
    • Breaking changes section filled.
    • breaking change label added.

Security checks

  • Followed secure coding standards in WSO2 Secure Coding Guidelines
  • Confirmed that this PR doesn't commit any keys, passwords, tokens, usernames, or other secrets.

Summary by CodeRabbit

  • New Features

    • Added stricter email address validation (trims whitespace; rejects CR/LF; stricter syntax).
    • Support for forwarding template parameters through workflow execution so notifications can render with provided template data.
  • Bug Fixes

    • Invite-link absence is no longer fatal for certain notification flows.
    • Email notifications now require an explicit template property; missing/empty template properties produce clear validation errors.
  • Tests

    • Updated and added tests covering email validation, template forwarding, and executor behaviors.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 29, 2026

📝 Walkthrough

Walkthrough

Adds a new forwarded-template-data key (templateData), has InviteExecutor populate forwarded template data with the generated invite link, updates EmailExecutor to consume forwarded template data (failing when the node emailTemplate property is missing/empty and no forwarded template exists), introduces an email syntax validator, and updates SMTP/client tests and executor tests.

Changes

Cohort / File(s) Summary
Forwarded-data constant
backend/internal/flow/common/constants.go
Adds exported ForwardedDataKeyTemplateData ("templateData") to forward template parameters.
Invite executor (forwarding)
backend/internal/flow/executor/invite_executor.go, backend/internal/flow/executor/invite_executor_test.go
Populate ExecutorResponse.ForwardedData with template forwarding data (includes inviteLink); adds test asserting forwarded templateData map and removal of top-level templateName in forwarded payload.
Email executor (consuming & validation)
backend/internal/flow/executor/email_executor.go, backend/internal/flow/executor/email_executor_test.go
Require emailTemplate property when not provided via forwarded data (fail loudly if missing/empty); stop relying on runtime inviteLink/appName in rendering; always render using resolveTemplateData(ctx) which uses forwarded templateData (stringified map) or empty TemplateData; tests updated for forwarded-data flows and negative render cases.
Email validation helper & tests
backend/internal/system/email/validator.go, backend/internal/system/email/validator_test.go
Adds IsValidEmail(string) bool (rejects CR/LF, trims, parses and enforces exact match); adds test suite covering valid/invalid addresses and CRLF injection attempts.
SMTP client changes & tests
backend/internal/system/email/smtp_client.go, backend/internal/system/email/smtp_client_test.go
Switch recipient/sender validation to IsValidEmail, trim/persist from in config, simplify invalid-recipient errors; test renames of local vars (errorerr, configconf) with no behavioral change.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant InviteExecutor as Invite\r\nExecutor
    participant EmailExecutor as Email\r\nExecutor
    participant TemplateService as Template\r\nService
    participant SMTP as Email\r\nService

    Client->>InviteExecutor: Execute GenerateInvite
    InviteExecutor->>InviteExecutor: create inviteLink / token
    InviteExecutor->>InviteExecutor: set ExecutorResponse.ForwardedData.templateData(inviteLink...)
    InviteExecutor-->>Client: return ExecutorResponse (with ForwardedData)

    Client->>EmailExecutor: ExecuteSend (receives/uses ForwardedData)
    EmailExecutor->>EmailExecutor: select templateName (forwarded or node property)
    EmailExecutor->>TemplateService: Render(templateName, templateData)
    TemplateService-->>EmailExecutor: rendered content
    EmailExecutor->>SMTP: Send(email data)
    SMTP-->>EmailExecutor: send result
    EmailExecutor-->>Client: ExecComplete
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

Suggested reviewers

  • DonOmalVindula
  • ThaminduDilshan
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: generalizing email executor to handle generic template data via forwarded data instead of hardcoded scenarios.
Description check ✅ Passed The description covers Purpose and Approach sections with a practical code example, and includes most checklist items marked. However, documentation and integration tests sections are incomplete.
Docstring Coverage ✅ Passed Docstring coverage is 80.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share
Review rate limit: 0/1 reviews remaining, refill in 60 minutes.

Comment @coderabbitai help to get the list of available commands and usage tips.

@RandithaK
Copy link
Copy Markdown
Contributor Author

Please mark this as an improvement, and a breaking change.

Note, that MagicLink implementation #1879 depends on this PR.

@RandithaK RandithaK marked this pull request as ready for review April 29, 2026 04:33
Copilot AI review requested due to automatic review settings April 29, 2026 04:33
@rajithacharith
Copy link
Copy Markdown
Contributor

@RandithaK Why this is a breaking change?

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 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/common/constants.go`:
- Around line 256-259: The change introduces a breaking public contract by
adding ForwardedDataKeyTemplateName and ForwardedDataKeyTemplateData (used as
ForwardedData["templateName"] and ForwardedData["templateData"]) for email
templating but the docs were not updated; add documentation under docs/
describing the forwarded-data contract (keys ForwardedDataKeyTemplateName:
string and ForwardedDataKeyTemplateData: map[string]string), show example
payloads and formats, include migration guidance for existing callers/flows
(notably User Invitation) and update any API/reference pages (e.g.,
docs/content/guides/... and docs/content/apis.mdx) to reflect the new keys
before merging.

In `@backend/internal/flow/executor/email_executor.go`:
- Around line 125-127: The forwarded-template path must validate the contract
and fail fast: in the branch using e.resolveForwardedTemplateScenario and when
reading ctx.NodeProperties[propertyKeyEmailTemplate], ensure that if a forwarded
structure is present you check both that the scenario string is non-empty and
that templateName and templateData exist and have the expected types (e.g.,
templateName is string, templateData is map/struct or expected type); if any of
these checks fail return an ExecFailure (or error) immediately rather than
silently continuing. Apply the same strict validation logic to the other
forwarded-template handling area referenced around the later branch (the block
covering the other forwarded-template processing) so both paths validate types
and presence of templateName/templateData and produce explicit ExecFailure with
a clear message when malformed.

In `@backend/internal/system/email/smtp_client.go`:
- Around line 45-47: The change tightens sender/recipient validation in
smtp_client.go (IsValidEmail used with config.from and related recipient checks
that now return ErrorInvalidSender) and requires updating user-facing
documentation; add or update docs under docs/content/guides (e.g., an email
configuration/usage guide) clarifying that SMTP sender/recipient must be plain
addr-spec (no display-name forms like "Name <a@b.com>") and that CR/LF or
display-name formatted addresses will be rejected, and mention the
ErrorInvalidSender behavior so callers know to sanitize payloads; ensure any
API/flow docs that describe email payload shapes are updated accordingly.
- Around line 45-47: The email validation uses IsValidEmail which trims
internally but leaves config.from unmodified, causing MAIL FROM failures for
values with surrounding whitespace; after the IsValidEmail check in the SMTP
client setup (the block referencing IsValidEmail and ErrorInvalidSender for
config.from) assign the trimmed value back to config.from (and apply the same
change to the analogous check referencing lines 59-61, e.g. for reply-to or
other sender fields) so the stored envelope sender is normalized before
returning/using it; use strings.TrimSpace to normalize the value immediately
after the validation succeeds.
🪄 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: 9703c31a-0bbc-4d72-b0c4-ade09cff5c6b

📥 Commits

Reviewing files that changed from the base of the PR and between 414922e and 726414e.

📒 Files selected for processing (8)
  • backend/internal/flow/common/constants.go
  • backend/internal/flow/executor/email_executor.go
  • backend/internal/flow/executor/email_executor_test.go
  • backend/internal/flow/executor/invite_executor.go
  • backend/internal/system/email/smtp_client.go
  • backend/internal/system/email/smtp_client_test.go
  • backend/internal/system/email/validator.go
  • backend/internal/system/email/validator_test.go

Comment thread backend/internal/flow/common/constants.go Outdated
Comment thread backend/internal/flow/executor/email_executor.go Outdated
Comment thread backend/internal/system/email/smtp_client.go Outdated
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 29, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 87.43%. Comparing base (34cd74d) to head (884589f).
⚠️ Report is 4 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2485      +/-   ##
==========================================
- Coverage   87.44%   87.43%   -0.02%     
==========================================
  Files         889      889              
  Lines       62489    62499      +10     
==========================================
  Hits        54645    54645              
- Misses       5899     5910      +11     
+ Partials     1945     1944       -1     
Flag Coverage Δ
backend-integration-postgres 51.91% <3.12%> (+<0.01%) ⬆️
backend-integration-redis 51.49% <3.12%> (-0.08%) ⬇️
backend-integration-sqlite 52.38% <3.12%> (-0.02%) ⬇️
backend-unit 80.48% <100.00%> (+<0.01%) ⬆️
frontend-apps-console-unit 89.34% <ø> (-0.01%) ⬇️
frontend-apps-gate-unit 97.35% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

This PR generalizes email sending to support choosing a template scenario and supplying template parameters via forwarded data, and centralizes email address validation (including CR/LF injection protection).

Changes:

  • Add IsValidEmail helper + unit tests and switch SMTP sender/recipient validation to use it.
  • Introduce forwarded data contract keys (templateName, templateData) and populate them in the invite executor.
  • Update email executor to resolve template scenario/data from ForwardedData, and adjust executor unit tests accordingly.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
backend/internal/system/email/validator.go Adds IsValidEmail with CR/LF rejection and strict parsing.
backend/internal/system/email/validator_test.go Adds unit tests for IsValidEmail valid/invalid and injection cases.
backend/internal/system/email/smtp_client.go Replaces net/mail parsing with IsValidEmail; refactors error var naming; adds docs.
backend/internal/system/email/smtp_client_test.go Refactors variable naming (errorerr, configconf) for clarity/consistency.
backend/internal/flow/common/constants.go Adds forwarded data keys for template scenario and template data.
backend/internal/flow/executor/invite_executor.go Populates forwarded templateName and templateData for invite flows.
backend/internal/flow/executor/email_executor.go Resolves template scenario/data via forwarded data and removes inviteLink runtime enforcement.
backend/internal/flow/executor/email_executor_test.go Updates expectations to use forwarded template contract and empty template data defaults.

Comment thread backend/internal/flow/executor/email_executor.go Outdated
Comment thread backend/internal/flow/executor/email_executor.go
Comment thread backend/internal/flow/executor/email_executor.go Outdated
Comment thread backend/internal/flow/executor/email_executor.go Outdated
Comment thread backend/internal/system/email/smtp_client.go
Comment thread backend/internal/flow/executor/email_executor_test.go Outdated
@RandithaK
Copy link
Copy Markdown
Contributor Author

@RandithaK Why this is a breaking change?

The email templating will not support the previous pattern of use, and need to have a templateName, templateData, in forwarded data to apply templates

@RandithaK RandithaK force-pushed the improvement/adaptive-email-template branch from 726414e to 8176c60 Compare April 29, 2026 05:56
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
backend/internal/flow/executor/invite_executor.go (1)

113-117: Prefer explicit flow-type mapping for template scenario selection.

Current else maps every non-registration flow to USER_INVITE. A switch with explicit supported flow types plus a guarded default avoids silent misclassification when new flow types are introduced.

Refactor sketch
-	if ctx.FlowType == common.FlowTypeRegistration {
-		execResp.ForwardedData[common.ForwardedDataKeyTemplateName] = string(template.ScenarioSelfRegistration)
-	} else {
-		execResp.ForwardedData[common.ForwardedDataKeyTemplateName] = string(template.ScenarioUserInvite)
-	}
+	switch ctx.FlowType {
+	case common.FlowTypeRegistration:
+		execResp.ForwardedData[common.ForwardedDataKeyTemplateName] = string(template.ScenarioSelfRegistration)
+	case common.FlowTypeUserOnboarding:
+		execResp.ForwardedData[common.ForwardedDataKeyTemplateName] = string(template.ScenarioUserInvite)
+	default:
+		execResp.ForwardedData[common.ForwardedDataKeyTemplateName] = string(template.ScenarioUserInvite)
+		logger.Debug("Unhandled flow type for invite template scenario", log.String("flowType", string(ctx.FlowType)))
+	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/internal/flow/executor/invite_executor.go` around lines 113 - 117,
Replace the loose if/else that sets
execResp.ForwardedData[common.ForwardedDataKeyTemplateName] based on
ctx.FlowType with an explicit switch over ctx.FlowType that handles supported
flow types (e.g., common.FlowTypeRegistration ->
template.ScenarioSelfRegistration, common.FlowTypeUserInvite ->
template.ScenarioUserInvite) and includes a default branch that returns an error
or logs and fails the execution to avoid silent misclassification; update any
callers to handle the new error path if necessary and keep references to
execResp.ForwardedData[common.ForwardedDataKeyTemplateName], ctx.FlowType,
template.ScenarioSelfRegistration and template.ScenarioUserInvite to locate the
change.
🤖 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/system/email/validator.go`:
- Around line 26-44: The change to IsValidEmail tightens accepted formats (it
now rejects CR/LF control characters and requires the parsed address to exactly
match the input, thus rejecting display-name forms like "Name
<user@example.com>"); add documentation describing these email validation
constraints by creating/updating a guide under docs/content/guides/ (e.g.,
"email-sending-constraints.md") that lists the new rules (no CR/LF, no
display-name formats, examples of accepted/rejected values) and update
docs/content/apis.mdx to reference this guide where email request payloads or
sender/recipient behavior is documented so users are aware of the behavior
change.

---

Nitpick comments:
In `@backend/internal/flow/executor/invite_executor.go`:
- Around line 113-117: Replace the loose if/else that sets
execResp.ForwardedData[common.ForwardedDataKeyTemplateName] based on
ctx.FlowType with an explicit switch over ctx.FlowType that handles supported
flow types (e.g., common.FlowTypeRegistration ->
template.ScenarioSelfRegistration, common.FlowTypeUserInvite ->
template.ScenarioUserInvite) and includes a default branch that returns an error
or logs and fails the execution to avoid silent misclassification; update any
callers to handle the new error path if necessary and keep references to
execResp.ForwardedData[common.ForwardedDataKeyTemplateName], ctx.FlowType,
template.ScenarioSelfRegistration and template.ScenarioUserInvite to locate the
change.
🪄 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: 059481b9-5c8e-4ffa-8500-dd06999c5e82

📥 Commits

Reviewing files that changed from the base of the PR and between 726414e and 8176c60.

📒 Files selected for processing (8)
  • backend/internal/flow/common/constants.go
  • backend/internal/flow/executor/email_executor.go
  • backend/internal/flow/executor/email_executor_test.go
  • backend/internal/flow/executor/invite_executor.go
  • backend/internal/system/email/smtp_client.go
  • backend/internal/system/email/smtp_client_test.go
  • backend/internal/system/email/validator.go
  • backend/internal/system/email/validator_test.go
🚧 Files skipped from review as they are similar to previous changes (3)
  • backend/internal/flow/executor/email_executor.go
  • backend/internal/flow/executor/email_executor_test.go
  • backend/internal/system/email/smtp_client.go

Comment thread backend/internal/system/email/validator.go
@RandithaK
Copy link
Copy Markdown
Contributor Author

Manually tested and ready
image

@RandithaK RandithaK force-pushed the improvement/adaptive-email-template branch 2 times, most recently from f49ce25 to 0fe3cf6 Compare April 29, 2026 06:20
Comment thread backend/internal/flow/executor/email_executor.go Outdated
@RandithaK RandithaK force-pushed the improvement/adaptive-email-template branch 2 times, most recently from 5a8c61f to 884589f Compare April 29, 2026 09:43
@ThaminduDilshan ThaminduDilshan added this pull request to the merge queue Apr 29, 2026
Signed-off-by: RandithaK <me@randitha.net>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
backend/internal/system/email/smtp_client.go (1)

42-47: ⚠️ Potential issue | 🟠 Major

🔴 Documentation Required for stricter SMTP address format behavior.

At Line 42 and Line 135, validation now enforces stricter addr-spec inputs (e.g., rejecting display-name formats like Name <a@b.com>). This changes operator/runtime behavior for SMTP sender and recipient values, but no matching docs update is included in this PR context.

Please update docs under docs/content/guides/ (email/SMTP usage) and any related flow docs to explicitly document accepted sender/recipient formats and rejection behavior.

As per coding guidelines: if a user-facing change is introduced without corresponding updates under docs/, it must be flagged as a major issue with a documentation-required note.

Also applies to: 135-137

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/internal/system/email/smtp_client.go` around lines 42 - 47, The PR
tightens SMTP addr-spec validation in smtp_client.go by rejecting display-name
formats (e.g., "Name <a@b.com>") when building the sender (variable sender and
IsValidEmail usage) and similarly for recipients around the recipient validation
block; update documentation under docs/content/guides/ (email/SMTP usage) and
any related flow docs to clearly state the accepted formats (pure addr-spec
only, no display-name or angle-bracketed forms), list examples of accepted and
rejected inputs, and note the runtime rejection behavior so operators know to
adjust configuration/inputs accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@backend/internal/system/email/smtp_client.go`:
- Around line 42-47: The PR tightens SMTP addr-spec validation in smtp_client.go
by rejecting display-name formats (e.g., "Name <a@b.com>") when building the
sender (variable sender and IsValidEmail usage) and similarly for recipients
around the recipient validation block; update documentation under
docs/content/guides/ (email/SMTP usage) and any related flow docs to clearly
state the accepted formats (pure addr-spec only, no display-name or
angle-bracketed forms), list examples of accepted and rejected inputs, and note
the runtime rejection behavior so operators know to adjust configuration/inputs
accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 5bab9ddd-d7f9-4f07-883e-e7578d17ca08

📥 Commits

Reviewing files that changed from the base of the PR and between 5a8c61f and 0a95155.

📒 Files selected for processing (9)
  • backend/internal/flow/common/constants.go
  • backend/internal/flow/executor/email_executor.go
  • backend/internal/flow/executor/email_executor_test.go
  • backend/internal/flow/executor/invite_executor.go
  • backend/internal/flow/executor/invite_executor_test.go
  • backend/internal/system/email/smtp_client.go
  • backend/internal/system/email/smtp_client_test.go
  • backend/internal/system/email/validator.go
  • backend/internal/system/email/validator_test.go
✅ Files skipped from review due to trivial changes (1)
  • backend/internal/system/email/smtp_client_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • backend/internal/system/email/validator.go
  • backend/internal/flow/executor/email_executor.go

Merged via the queue into asgardeo:main with commit 2f2f7d4 Apr 29, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants