Staram się przekonać programistów, że obecnie powinniśmy zmienić nasze podejście do budowania aplikacji w sposób efektywny. Dotychczasowe budowanie aplikacji w sposób samodzielny od prawie zera powinniśmy zamienić na komponowanie rozwiązania przy wykorzystaniu gotowych elementów. Tak, aby ograniczyć ilość wytwarzanego przez nas kodu. Pewnie zgodzicie się, ze mną, że wraz ze zmniejszeniem ilości kodu, maleje również prawdopodobieństwo popełnienia błędu.

Taki podejście chciałbym zastosować w tym przypadku. Naturalnym wyborem dla wysyłania maili w przypadku Azure Functions jest usługa SendGrid.

SendGrid – konfiguracja

Zacznijmy od samego początku. Przed jakąkolwiek pracą musimy stworzyć usługę SendGrid w Azure Portal-u:

Tam oczywiście musimy wypełnić wszystkie wymagane pola, zgodzić się na warunki usługi oraz wybrać plan. Wybierając darmowy będziemy mogli wysłać 25000 wiadomości co miesiąc. Po uzupełnieniu wszystkich szczegółów naciskamy przycisk Create:

W tym momencie usługa jest tworzona w wybranej przez nas resource group-ie. Po chwili będzie ona dla nas dostępna. Jeśli nie pojawi się ona na liście to należy odświeżyć widok. Oczywiście musimy ją wybrać, a następnie otworzyć stronę Manage.

W tym momencie jesteśmy przekierowywani bezpośrednio na stronę SendGrid-a, na której będziemy poproszeni o potwierdzenie naszej tożsamości. Po tym etapie możemy przejść do wygenerowania klucza API. Będzie on potrzebny w funkcji. Z menu wybieramy Settings, a następnie API Keys. Na tej stronie istnieje możliwość utworzenia klucza – naciskamy przycisk Create API Key. W tym momencie otworzy nam się nowe okno, na którym będziemy mogli wybrać poziom dostępu oraz zdefiniować nazwę klucza. Oczywiście pamiętajmy o skopiowaniu jego wartości.

Wysyłanie maili z Azure Functions

Funkcja jako skrypt

W tym momencie dotarliśmy do połowy naszej podróży. Teraz musimy przerzucić się do Azure Functions. Mam nadzieję, że każdy z Was potrafi utworzyć nową funkcję. Wykorzystam najprostszą z nich – Http Trigger, tak abym mógł pokazać proces krok po kroku. Na liście znajdziecie również, szablon Scheduled mail, który zrobi prawie wszystkie rzeczy za nas.

Wróćmy do funkcji. Po utworzeniu funkcji należy otworzyć sekcję Integrate, a następnie dodać nowy parametr wyjściowy typu SendGrid.

Po jego wyborze otworzy nam się okno konfiguracyjne. Większość parametrów powinna być zrozumiała. Wydaje mi się, że powinniśmy się tylko przyjrzeć dwóm z nich:

  • SendGrid API Key App Setting – ten jest najbardziej zdradziecki. Dość dużo osób w polu tekstowym wpisuje wartość skopiowanego klucza, natomiast powinniśmy tam podać nazwę klucza z App Setting, który przechowuje skopiowany klucz do SendGrid-a. Oczywiście jeszcze takiego klucza nie utworzyliśmy, ale możemy to zrobić dość prosto – wybierając link new,
  • Use function return value – ta opcja decyduje, czy obiekt przechowujący informację o wysyłanej wiadomości zostanie zwrócony jako wartość funkcji, czy jako argument z modyfikatorem out. W naszym przypadku wybiorę drugą opcję i tym samym zostawię to pole niezaznaczone.

Po zapisaniu konfiguracji możemy przejść do pliku function.json i zobaczyć jak te wartości zostały przekształcone.

Na ekranie integracji mamy interesujący element, który jest dość często pomijany – dokumentację. Jest ona bardzo dobrze przygotowana i powiązana bezpośrednio z wybranym typem integracji.

Jest to też pierwsze miejsce, w którym powinniśmy szukać kodu, który chcemy wykorzystać w naszej funkcji:

#r "SendGrid"

using System;
using SendGrid.Helpers.Mail;
using Microsoft.Azure.WebJobs.Host;
public static Mail Run(Order order, TraceWriter log)
{
  log.Info($"C# Queue trigger function processed order: {order.OrderId}");
  Mail message = new Mail()
  {
    Subject = $"Thanks for your order (#{order.OrderId})!"
  };
  
  Content content = new Content
  {
    Type = "text/plain",
    Value = $"{order.CustomerName}, your order ({order.OrderId}) is being processed!"
  };

  message.AddContent(content);
  return message;
}

public class Order
{
  public string OrderId { get; set; }
  public string CustomerName { get; set; }
  public string CustomerEmail { get; set; }
}

Jak pewnie widzicie, przypadek ten różni się trochę od naszego – pobieramy wiadomości z kolejki, które następnie są wysyłane jako mail.

Spróbujmy go dostosować do naszych potrzeb – chciałbym stworzyć funkcję, która będzie wysyłała wiadomość e-mail z treścią przeze mnie podaną. Kod takiej funkcji wygląda następująco:

#r "SendGrid"

using System;
using SendGrid.Helpers.Mail;
using System.Net;

public static HttpResponseMessage Run(HttpRequestMessage req, out Mail message)
{
  // parse query parameter
  string messageText = req.GetQueryNameValuePairs()
    .FirstOrDefault(q => string.Compare(q.Key, "message", true) == 0)
    .Value;

  message = new Mail();
  Content content = new Content
  {
    Type = "text/plain",
    Value = messageText
  };
  
  message.AddContent(content);

  return req.CreateResponse(HttpStatusCode.Accepted);
}

Pamiętajcie, że testując powyższą funkcję, akceptuje ona tylko parametry podane w query. Usunąłem część kodu odpowiedzialną za odczytanie parametrów z body w celu zwiększenia czytelności kodu.

Jeśli wszystko zrobiliście dobrze to po uruchomieniu tej funkcji powinniście otrzymać maila z przesłaną wiadomością.

Oczywiście wszystkie parametry maila jesteśmy w stanie zmieniać z poziomu kodu.

Funkcja utworzona jako kod

Ostatni element, na który chciałbym zwrócić uwagę jest możliwość utworzenia funkcji bezpośrednio w Visual Studio. W tym przypadku do konfiguracji integracji z SendGrid-em możemy wykorzystać atrybut. Oczywiście musimy wykonać większość opisanych wcześniej kroków. Zmieni się tylko kod, który będzie wyglądał następująco:

[FunctionName("SendThankYouEmailMessage")]
public static void Run(
  [QueueTrigger("tosendemail", Connection = "registration2storage_STORAGE")] Customer customer,
  [SendGrid(
    To = "{Email}",
    Subject = "Thank you!",
    Text = "Hi {Name}, Thank you for registering!!!!",
    From = "[email protected]"
  )]
  out Mail message)
{
  message = new Mail();
}

Proszę zwróćcie uwagę, że w atrybucie dotyczącym SendGrid-a możemy skonsumować bezpośrednio właściwości obiektu pobranego z kolejki.