Notatka
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
W tym samouczku tworzysz python-run-function operator dla Lakeflow Designer, który wysyła zawartość DataFrame'a jako załącznik CSV przez Gmail. Skorzystaj z tego przykładu, aby dowiedzieć się, jak tworzyć operatory oparte na języku YAML, które wykonują skutki uboczne, takie jak wysyłanie powiadomień lub zapisywanie w systemach zewnętrznych. Aby dowiedzieć się więcej, zobacz niestandardowe operatory w Lakeflow Designer.
Overview
Operator odczytuje hasło do aplikacji Gmail z zakresu wpisów tajnych Azure Databricks. Przekazuje wejściowy DataFrame bez zmian do portu wyjściowego, więc możesz dodawać kolejne operatory dalej. Podczas podglądu workflow nie wysyła żadnego maila.
Requirements
- Obszar roboczy Azure Databricks z uprawnieniami do tworzenia zakresów wpisów tajnych.
- Konto Gmail z hasłem aplikacji Google (wymagane w przypadku włączenia uwierzytelniania wieloskładnikowego).
- Interfejs wiersza polecenia (CLI) Databricks zainstalowany na lokalnej maszynie deweloperskiej.
Krok 1. Konfigurowanie wpisów tajnych
Zapisz poświadczenia Gmaila w zakresie wpisów tajnych Azure Databricks, aby operator mógł je pobrać w czasie wykonywania.
Utwórz zakres wpisów tajnych za pomocą interfejsu CLI usługi Azure Databricks:
databricks secrets create-scope my_email_scopeZapisz hasło aplikacji Gmail w obrębie zakresu:
databricks secrets put-secret my_email_scope gmail_app_passwordPojawi się monit o wprowadzenie tajnej wartości. Wklej hasło aplikacji Gmail i zapisz.
Krok 2. Pisanie run() funkcji
python-run-function Typ operatora wymaga funkcji z tym podpisemrun():
def run(config: Dict[str, Any], inputs: Dict[str, Any], spark) -> Dict[str, Any]:
-
config: wartości konfiguracji udostępniane przez użytkownika w interfejsie użytkownika projektanta lakeflow. -
inputs: Wejściowe ramki danych kluczowane według nazwy portu. -
spark: aktywna sesja platformy Spark.
Funkcja musi zwrócić słownik wyjściowych obiektów DataFrame, gdzie kluczami są nazwy portów wyjściowych.
Zdefiniuj i przetestuj funkcję w komórce notatnika. Gdy is_preview jest ustawiony, funkcja pomija swoje skutki uboczne i zwraca dane wejściowe bez zmian:
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}
Krok 3. Testowanie funkcji
Przetestuj funkcję na przykładowym DataFrame w trybie podglądu, który zwraca wejściowy DataFrame bez zmian i nie wysyła e-maila. Zamień <your-email@gmail.com> na swój adres Gmail.
test_df = spark.createDataFrame(
[("Alice", 100), ("Bob", 200)],
["name", "amount"]
)
result = run(
config={
"is_preview": True,
"sender_email": "<your-email@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()
Note
Wartości secret_scope i secret_key w konfiguracji to nazwy tajnego zakresu i klucza, które utworzyłeś w kroku 1, a nie rzeczywiste hasło. Operator używa tych nazw do pobierania hasła z wpisów tajnych w Azure Databricks w czasie wykonywania.
Ważna
Najpierw przetestuj przy ustawieniu is_preview na True, aby zweryfikować działanie w trybie pass-through bez wysyłania żadnych wiadomości e-mail. Gdy wszystko będzie gotowe do przetestowania rzeczywistej wiadomości e-mail, ustaw is_preview na False.
Krok 4. Tworzenie definicji YAML
Utwórz plik o nazwie gmail_email_sender.yaml z następującą zawartością:
schema: custom-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}
Krok 5. Zapisywanie i rejestrowanie operatora
Zapisz plik YAML w obszarze roboczym Azure Databricks. Przykład:
/Workspace/Users/<user-name>/gmail_email_sender.yamlDodaj operator do pliku
.custom_operators.yaml:operators: - /Workspace/Users/<user-name>/gmail_email_sender.yaml
Aby uzyskać więcej informacji o opcjach rejestracji, zobacz Zarejestruj i wdróż operator do Lakeflow Designer.
Krok 6. Konfigurowanie uprawnień
Użytkownicy, którzy uruchamiają przepływ pracy zawierający ten operator, muszą mieć READ dostęp do zakresu wpisów tajnych lub mogą podać własny zakres wpisów tajnych i wartości kluczy w konfiguracji operatora. Użytkownicy potrzebują również dostępu do odczytu do pliku YAML w obszarze roboczym.
Aby udzielić dostępu do zakresu wpisu tajnego:
databricks secrets put-acl my_email_scope <user-or-group> READ
Krok 7: Użyj operatora w Lakeflow Designer
- W wizualnym przygotowaniu danych otwórz menu Operatorzy w lewym panelu i znajdź operatora Gmail Email Sender .
- Przeciągnij operator na obszar roboczy i podłącz do niego wejście.
- Kliknij dwukrotnie operatora, aby otworzyć panel konfiguracyjny, a następnie wypełnij jego pola.
- Wybierz operatora, aby podglądać jego wyniki w panelu wyjściowym.
Operator ma port wejściowy dla źródła danych oraz pola konfiguracyjne dla e-maila nadawcy, zakresu wpisu tajnego, tajnego klucza, odbiorców, tematu i treści.