Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Neste tutorial, crias um python-run-function operador para o Lakeflow Designer que envia o conteúdo de um DataFrame como anexo CSV via Gmail. Use este exemplo para aprender a construir operadores baseados em YAML que realizam efeitos secundários, como enviar notificações ou escrever para sistemas externos. Para saber mais, consulte Operadores definidos pelo utilizador no Lakeflow Designer.
Requirements
- Um espaço de trabalho Azure Databricks com acesso para criar escopos secretos.
- Uma conta Gmail com uma palavra-passe da Google App (necessária quando a autenticação multifator (MFA) está ativada).
- A CLI do Databricks instalada na sua máquina de desenvolvimento local.
Passo 1: Estabelecer segredos
Guarde as suas credenciais do Gmail num escopo secreto do Azure Databricks para que o operador as possa recuperar em tempo de execução.
Crie um escopo secreto usando a CLI do Azure Databricks:
databricks secrets create-scope my_email_scopeGuarde a sua palavra-passe da aplicação do Gmail no âmbito:
databricks secrets put-secret my_email_scope gmail_app_passwordÉ solicitado que introduza o valor secreto. Cola a palavra-passe da tua aplicação do Gmail e guarda.
Passo 2: Escrever a run() função
O python-run-function tipo de operador requer uma run() função com esta assinatura:
def run(config: Dict[str, Any], inputs: Dict[str, Any], spark) -> Dict[str, Any]:
-
config: Valores de configuração fornecidos pelo utilizador na interface do Lakeflow Designer. -
inputs: DataFrames de entrada codificados pelo nome da porta. -
spark: A sessão ativa do Spark.
A função deve devolver um dicionário de DataFrames de saída indexado pelo nome da porta de saída.
Defina e teste a função numa célula de caderno:
from typing import Dict, Any
def run(config: Dict[str, Any], inputs: Dict[str, Any], spark) -> Dict[str, Any]:
input_df = inputs["data"]
# Skip side effects during Designer preview
if config.get("is_preview", False):
return {"data": input_df}
import smtplib
import os
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders
sender_email = config.get("sender_email", "")
secret_scope = config.get("secret_scope", "")
secret_key = config.get("secret_key", "")
recipients_raw = config.get("recipients", "")
subject = config.get("subject", "")
body = config.get("body", "")
if not sender_email:
raise ValueError("Sender Email is required.")
if not secret_scope or not secret_key:
raise ValueError("Secret Scope and Secret Key are required.")
if not recipients_raw:
raise ValueError("At least one recipient is required.")
recipients = [r.strip() for r in recipients_raw.split(",") if r.strip()]
if not recipients:
raise ValueError("At least one valid recipient email is required.")
# Retrieve password from Databricks secrets
from pyspark.dbutils import DBUtils
dbutils = DBUtils(spark)
sender_password = dbutils.secrets.get(scope=secret_scope, key=secret_key)
# Convert DataFrame to CSV
pdf = input_df.toPandas()
file_path = "/tmp/designer_email_attachment.csv"
pdf.to_csv(file_path, index=False)
# Send email to each recipient
for recipient in recipients:
msg = MIMEMultipart()
msg["From"] = sender_email
msg["To"] = recipient
msg["Subject"] = subject
msg.attach(MIMEText(body, "plain"))
with open(file_path, "rb") as attachment:
part = MIMEBase("application", "octet-stream")
part.set_payload(attachment.read())
encoders.encode_base64(part)
part.add_header(
"Content-Disposition",
f"attachment; filename={os.path.basename(file_path)}",
)
msg.attach(part)
with smtplib.SMTP_SSL("smtp.gmail.com", 465) as server:
server.login(sender_email, sender_password)
server.send_message(msg)
# Clean up temp file
if os.path.exists(file_path):
os.remove(file_path)
return {"data": input_df}
Passo 3: Testar a função
Teste a função com um exemplo de DataFrame:
test_df = spark.createDataFrame(
[("Alice", 100), ("Bob", 200)],
["name", "amount"]
)
# Test in preview mode (no email sent)
result = run(
config={
"is_preview": True,
"sender_email": "you@gmail.com",
"secret_scope": "my_email_scope",
"secret_key": "gmail_app_password",
"recipients": "alice@example.com",
"subject": "Test",
"body": "Test body"
},
inputs={"data": test_df},
spark=spark
)
result["data"].show()
# Expected: the original DataFrame, unchanged
Note
Os valores secret_scope e secret_key na configuração são os nomes do âmbito do segredo e da chave que criou no Passo 1 — não a palavra-passe real. O operador utiliza estes nomes para recuperar a palavra-passe dos segredos do Azure Databricks em tempo de execução.
Importante
Teste com is_preview definido para True primeiro para verificar o comportamento de passagem sem enviar qualquer email. Quando estiver pronto para testar o email real, defina is_preview para False.
Passo 4: Construir a definição YAML
Crie um ficheiro chamado gmail_email_sender.yaml com o seguinte conteúdo:
schema: user-defined-operator-v0.1.0
id: gmail_email_sender
type: python-run-function
version: '1.0.0'
name: Gmail Email Sender
description: Sends the input DataFrame as a CSV attachment via Gmail SMTP to one or more recipients.
config:
type: object
properties:
is_preview:
type: boolean
format: is_preview
default: false
sender_email:
type: string
title: Sender Email
default: ''
examples:
- 'you@gmail.com'
x-ui:
widget: input
secret_scope:
type: string
title: Secret Scope
default: ''
examples:
- 'my_email_scope'
x-ui:
widget: input
secret_key:
type: string
title: Secret Key
default: ''
examples:
- 'gmail_app_password'
x-ui:
widget: input
recipients:
type: string
title: Recipients
default: ''
examples:
- 'alice@example.com, bob@example.com'
x-ui:
widget: textarea
rows: 2
subject:
type: string
title: Subject
default: ''
examples:
- 'Designer Output Data'
x-ui:
widget: input
body:
type: string
title: Email Body
default: "Hello,\n\nAttached is the latest data.\n\nBest,\nDatabricks Workflow"
x-ui:
widget: textarea
rows: 6
required:
- sender_email
- secret_scope
- secret_key
- recipients
- subject
additionalProperties: false
ports:
input:
- name: data
title: Input Data
mime: application/vnd.databricks.dataframe
output:
- name: data
title: Output Data
mime: application/vnd.databricks.dataframe
run_function:
type: inline
code: |
from typing import Dict, Any
def run(config: Dict[str, Any], inputs: Dict[str, Any], spark) -> Dict[str, Any]:
input_df = inputs["data"]
if config.get("is_preview", False):
return {"data": input_df}
import smtplib
import os
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders
sender_email = config.get("sender_email", "")
secret_scope = config.get("secret_scope", "")
secret_key = config.get("secret_key", "")
recipients_raw = config.get("recipients", "")
subject = config.get("subject", "")
body = config.get("body", "")
if not sender_email:
raise ValueError("Sender Email is required.")
if not secret_scope or not secret_key:
raise ValueError("Secret Scope and Secret Key are required.")
if not recipients_raw:
raise ValueError("At least one recipient is required.")
recipients = [r.strip() for r in recipients_raw.split(",") if r.strip()]
if not recipients:
raise ValueError("At least one valid recipient email is required.")
from pyspark.dbutils import DBUtils
dbutils = DBUtils(spark)
sender_password = dbutils.secrets.get(scope=secret_scope, key=secret_key)
pdf = input_df.toPandas()
file_path = "/tmp/designer_email_attachment.csv"
pdf.to_csv(file_path, index=False)
for recipient in recipients:
msg = MIMEMultipart()
msg["From"] = sender_email
msg["To"] = recipient
msg["Subject"] = subject
msg.attach(MIMEText(body, "plain"))
with open(file_path, "rb") as attachment:
part = MIMEBase("application", "octet-stream")
part.set_payload(attachment.read())
encoders.encode_base64(part)
part.add_header(
"Content-Disposition",
f"attachment; filename={os.path.basename(file_path)}",
)
msg.attach(part)
with smtplib.SMTP_SSL("smtp.gmail.com", 465) as server:
server.login(sender_email, sender_password)
server.send_message(msg)
if os.path.exists(file_path):
os.remove(file_path)
return {"data": input_df}
Passo 5: Guardar e registar o operador
Guarde o ficheiro YAML no seu espaço de trabalho Azure Databricks. Por exemplo:
/Workspace/Users/<user-name>/gmail_email_sender.yamlAdicione o operador ao seu
.user_defined_operators.yamlficheiro:operators: - /Workspace/Users/<user-name>/gmail_email_sender.yaml
Para mais informações sobre opções de registo, consulte Tornar o seu operador descobrível.
Permissions
Os utilizadores que executam um fluxo de trabalho contendo este operador precisam READ de acesso ao âmbito secreto, ou podem fornecer o seu próprio âmbito secreto e valores-chave na configuração do operador. Os utilizadores também precisam de acesso de leitura ao ficheiro YAML no espaço de trabalho.
Para conceder acesso ao escopo secreto:
databricks secrets put-acl my_email_scope <user-or-group> READ
Utilize o operador no Lakeflow Designer
Após o registo, o operador aparece no Lakeflow Designer com uma porta de entrada para a sua fonte de dados e campos de configuração para email do remetente, âmbito secreto, chave secreta, destinatários, assunto e corpo.
Quando o fluxo de trabalho é executado, o operador converte o DataFrame de entrada em CSV, anexa-o a um email e envia-o a cada destinatário. O DataFrame passa sem alterações para a porta de saída, por isso podes encadear operadores adicionais a jusante. Durante a pré-visualização do fluxo de trabalho, não é enviado nenhum email.