mirror of
https://github.com/langgenius/dify.git
synced 2026-05-03 17:08:03 +08:00
feat(api): support variable reference and substitution in Email delivery
The EmailDeliveryConfig.body now support referencing variables generated by precedent nodes.
This commit is contained in:
@ -1,4 +1,5 @@
|
||||
from core.workflow.nodes.human_input.entities import EmailDeliveryConfig, EmailRecipients
|
||||
from core.workflow.runtime import VariablePool
|
||||
|
||||
|
||||
def test_replace_url_placeholder_with_value():
|
||||
@ -23,3 +24,17 @@ def test_replace_url_placeholder_missing_value():
|
||||
result = config.body_with_url(None)
|
||||
|
||||
assert result == "No link available."
|
||||
|
||||
|
||||
def test_render_body_template_replaces_variable_values():
|
||||
config = EmailDeliveryConfig(
|
||||
recipients=EmailRecipients(),
|
||||
subject="Subject",
|
||||
body="Hello {{#node1.value#}} {{#url#}}",
|
||||
)
|
||||
variable_pool = VariablePool()
|
||||
variable_pool.add(["node1", "value"], "World")
|
||||
|
||||
result = config.render_body_template(body=config.body, url="https://example.com", variable_pool=variable_pool)
|
||||
|
||||
assert result == "Hello World https://example.com"
|
||||
|
||||
@ -14,6 +14,7 @@ from services.human_input_delivery_test_service import (
|
||||
DeliveryTestError,
|
||||
EmailDeliveryTestHandler,
|
||||
)
|
||||
from core.workflow.runtime import VariablePool
|
||||
|
||||
|
||||
def _make_email_method() -> EmailDeliveryMethod:
|
||||
@ -48,3 +49,49 @@ def test_email_delivery_test_handler_rejects_when_feature_disabled(monkeypatch:
|
||||
|
||||
with pytest.raises(DeliveryTestError, match="Email delivery is not available"):
|
||||
handler.send_test(context=context, method=method)
|
||||
|
||||
|
||||
def test_email_delivery_test_handler_replaces_body_variables(monkeypatch: pytest.MonkeyPatch):
|
||||
class DummyMail:
|
||||
def __init__(self):
|
||||
self.sent: list[dict[str, str]] = []
|
||||
|
||||
def is_inited(self) -> bool:
|
||||
return True
|
||||
|
||||
def send(self, *, to: str, subject: str, html: str):
|
||||
self.sent.append({"to": to, "subject": subject, "html": html})
|
||||
|
||||
mail = DummyMail()
|
||||
monkeypatch.setattr(service_module, "mail", mail)
|
||||
monkeypatch.setattr(service_module, "render_email_template", lambda template, _substitutions: template)
|
||||
monkeypatch.setattr(
|
||||
service_module.FeatureService,
|
||||
"get_features",
|
||||
lambda _tenant_id: SimpleNamespace(human_input_email_delivery_enabled=True),
|
||||
)
|
||||
|
||||
handler = EmailDeliveryTestHandler(session_factory=object())
|
||||
handler._resolve_recipients = lambda **_kwargs: ["tester@example.com"] # type: ignore[assignment]
|
||||
|
||||
method = EmailDeliveryMethod(
|
||||
config=EmailDeliveryConfig(
|
||||
recipients=EmailRecipients(whole_workspace=False, items=[ExternalRecipient(email="tester@example.com")]),
|
||||
subject="Subject",
|
||||
body="Value {{#node1.value#}}",
|
||||
)
|
||||
)
|
||||
variable_pool = VariablePool()
|
||||
variable_pool.add(["node1", "value"], "OK")
|
||||
context = DeliveryTestContext(
|
||||
tenant_id="tenant-1",
|
||||
app_id="app-1",
|
||||
node_id="node-1",
|
||||
node_title="Human Input",
|
||||
rendered_content="content",
|
||||
variable_pool=variable_pool,
|
||||
)
|
||||
|
||||
handler.send_test(context=context, method=method)
|
||||
|
||||
assert mail.sent[0]["html"] == "Value OK"
|
||||
|
||||
@ -49,7 +49,7 @@ def _build_job(recipient_count: int = 1) -> task_module._EmailDeliveryJob:
|
||||
|
||||
def test_dispatch_human_input_email_task_sends_to_each_recipient(monkeypatch: pytest.MonkeyPatch):
|
||||
mail = _DummyMail()
|
||||
form = SimpleNamespace(id="form-1", tenant_id="tenant-1")
|
||||
form = SimpleNamespace(id="form-1", tenant_id="tenant-1", workflow_run_id=None)
|
||||
|
||||
def fake_render(template: str, substitutions: dict[str, str]) -> str:
|
||||
return template.replace("{{ form_token }}", substitutions["form_token"]).replace(
|
||||
@ -79,7 +79,7 @@ def test_dispatch_human_input_email_task_sends_to_each_recipient(monkeypatch: py
|
||||
|
||||
def test_dispatch_human_input_email_task_skips_when_feature_disabled(monkeypatch: pytest.MonkeyPatch):
|
||||
mail = _DummyMail()
|
||||
form = SimpleNamespace(id="form-1", tenant_id="tenant-1")
|
||||
form = SimpleNamespace(id="form-1", tenant_id="tenant-1", workflow_run_id=None)
|
||||
|
||||
monkeypatch.setattr(task_module, "mail", mail)
|
||||
monkeypatch.setattr(
|
||||
@ -96,3 +96,37 @@ def test_dispatch_human_input_email_task_skips_when_feature_disabled(monkeypatch
|
||||
)
|
||||
|
||||
assert mail.sent == []
|
||||
|
||||
|
||||
def test_dispatch_human_input_email_task_replaces_body_variables(monkeypatch: pytest.MonkeyPatch):
|
||||
mail = _DummyMail()
|
||||
form = SimpleNamespace(id="form-1", tenant_id="tenant-1", workflow_run_id="run-1")
|
||||
job = task_module._EmailDeliveryJob(
|
||||
form_id="form-1",
|
||||
workflow_run_id="run-1",
|
||||
subject="Subject",
|
||||
body="Body {{#node1.value#}}",
|
||||
form_content="content",
|
||||
recipients=[task_module._EmailRecipient(email="user@example.com", token="token-1")],
|
||||
)
|
||||
|
||||
variable_pool = task_module.VariablePool()
|
||||
variable_pool.add(["node1", "value"], "OK")
|
||||
|
||||
monkeypatch.setattr(task_module, "mail", mail)
|
||||
monkeypatch.setattr(task_module, "render_email_template", lambda template, _substitutions: template)
|
||||
monkeypatch.setattr(
|
||||
task_module.FeatureService,
|
||||
"get_features",
|
||||
lambda _tenant_id: SimpleNamespace(human_input_email_delivery_enabled=True),
|
||||
)
|
||||
monkeypatch.setattr(task_module, "_load_email_jobs", lambda _session, _form: [job])
|
||||
monkeypatch.setattr(task_module, "_load_variable_pool", lambda _workflow_run_id: variable_pool)
|
||||
|
||||
task_module.dispatch_human_input_email_task(
|
||||
form_id="form-1",
|
||||
node_title="Approve",
|
||||
session_factory=lambda: _DummySession(form),
|
||||
)
|
||||
|
||||
assert mail.sent[0]["html"] == "Body OK"
|
||||
|
||||
Reference in New Issue
Block a user