Skip to content

Implement SendEmailFromTemplate#331

Draft
Bircck wants to merge 7 commits into
masterfrom
worktree-send-email-from-template
Draft

Implement SendEmailFromTemplate#331
Bircck wants to merge 7 commits into
masterfrom
worktree-send-email-from-template

Conversation

@Bircck
Copy link
Copy Markdown

@Bircck Bircck commented Jun 2, 2026

🚧 Draft — waiting on a regenerated XrmContext.cs to replace the standalone Template.cs.

Adds a mock of SendEmailFromTemplate that creates the email from a template, then delegates to the existing Create and SendEmail handlers so security, plugins, and status all behave like a normal send.

Why a renderer (EmailTemplateRenderer): Dataverse stores template subject/body as XSLT, not plain text. Copying it verbatim would dump raw markup and merge nothing, so it's rendered against the regarding record + sending user (verified against a live org). Plain-text templates pass through unchanged.

Template.cs: stopgap early-bound type so tests can stay early-bound — should be folded into a regenerated XrmContext.cs (the draft blocker).

TemplateMetadata.xml: template entity metadata, auto-merged from the Metadata/ dir.

Tests cover rendering, sender merge, plain-text, and the validation/error paths.

Bircck added 7 commits June 2, 2026 11:08
Implements the SendEmailFromTemplate message by validating the request
(TemplateId, Target email, RegardingId/RegardingType), setting the
regarding object, and delegating to the existing Create and SendEmail
handlers so security, plugins and email status transitions are applied
consistently.

Performs a best-effort template content merge (subject/body) when the
'template' entity is present in metadata; token substitution is not
modelled. Guarding on metadata keeps it safe where 'template' was not
generated.
A Dataverse template's subject/body are XSLT stylesheets transformed
against a <data> document built from the records the e-mail draws from.
EmailTemplateRenderer reproduces this: it builds <data> from the
regarding record and the sending user and runs the stylesheet with
XslCompiledTransform, matching the platform's merge behaviour. Plain-text
or non-XSLT content is returned unchanged.

The handler now loads the template and renders subject/body into the
e-mail before creating and sending it.

Tests:
- TestEmailTemplateRenderer exercises the renderer with the actual XSLT
  of the built-in "Thank you for registering" contact template.
- An end-to-end SendEmailFromTemplate test creates a real template record
  and asserts the rendered subject/body on the sent e-mail. This required
  adding the 'template' entity to the test fixture metadata
  (TemplateMetadata.xml).
- Add a generated-style early-bound Template proxy type so template
  records can be created strongly-typed in tests (XrmContext generates
  from a fixed reference org that lacks 'template', so this is added by
  hand following the same pattern).
- Use it in the end-to-end SendEmailFromTemplate test.
- Strengthen the renderer test to assert the exact "Dear MrSmith"
  output, verified against a live SendEmailFromTemplate call: real
  Dataverse strips the whitespace-only stylesheet node identically.
- Document the two cosmetic decorations the platform adds that the mock
  intentionally does not reproduce (HTML body envelope, subject tracking
  token).
The early-bound Template type is now generated by Delegate XrmContext v3.0.1
directly from the live org (https://orgd3a12bfa.crm4.dynamics.com), scoped to
the 'template' entity, rather than hand-written. v3.0.1 was required because
the bundled XrmContext v1.8 only supports WS-Trust, which the OAuth/MFA org
rejects.

Adjusted to the existing XrmContext.cs style: dropped the v3 cosmetic
[DisplayName]/[MaxLength]/[Range] annotations and their System.ComponentModel*
usings (net462 has no DataAnnotations reference and the existing context omits
them), and the unused Template_TemplateTypeCode enum (templatetypecode is an
EntityName string). Attribute data is unchanged; full suite passes on net462
and net8.0.
Format the extracted single-entity metadata file with indentation and the
same UTF-8 BOM + XML declaration as the main Metadata.xml, instead of one
long line. No content change; full suite still passes on net462 and net8.0.
Throw FaultException when the template or regarding record does not exist
and when the template type does not match the regarding object type, rather
than silently sending unmerged mail. Existence and type validation are
isolated in RetrieveOrThrow and ValidateTemplateType helpers so the core
Execute flow stays a simple linear sequence. Add tests covering the
template-not-found and type-mismatch paths.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant