Paweł Łukasiewicz
2022-04-20
Paweł Łukasiewicz
2022-04-20
Udostępnij Udostępnij Kontakt
Wprowadzenie

DynamoDB może być wyzwalaczem dla Lambdy w momencie dodawania, aktualizacji oraz usuwania danych z tabeli. W tym wpisie przygotujemy kolejny prosty przykład wykorzystując język C# - tym razem wraz z dodaniem danych do tabeli dojdzie do uruchomienia funkcji, która odczyta wprowadzone dane a następnie wyśle je w wiadomości e-mail.

Kolejne kroki, które wykonamy w ramach tego wpisu:

  1. utworzenie tabeli DynamoDB z kluczem głównym;
  2. utworzenie roli, która będzie miała uprawnienia do pracy z DynamoDB oraz Lambdą;
  3. utworzenie funkcji Lambda;
  4. dodanie wyzwalacza na funkcji w celu wysłania wiadomości e-mail;
  5. dodanie danych do tabeli - akcja, która będzie wyzwalaczem dla całej naszej logiki.

Dokładny opis działania

Zanim przejdziemy do właściwej implementacji poświęcimy kilka chwil na omówienie powyższych kroków w nieco bardziej szczegółowy sposób skupiając się na podstawowej interakcji pomiędzy DynamoDB i Lambdą:

  • W pierwszym kroku użytkownik dodaje dane do utworzonej tabeli DynamoDB;
  • Dodanie danych do DynamoDB spowoduje uruchomienie w tle funkcji Lambda, która wykorzystując usługę SNS wyśle wiadomość e-mail ze szczegółami operacji przeprowadzonej na tabeli;
  • Dodamy również logi, aby mieć pewność, że dana operacja została zarejestrowana w naszym "systemie" - do podglądu dziennika zdarzeń wykorzystamy CloudWatch.

Spójrzcie jeszcze jak prezentuje się "architektura" takiej aplikacji: DynamoDB: przykładowa architektura

Tworzenie tabeli w DynamoDB

Początkowe kroki znacie już doskonale z poprzedniej serii. Przechodzimy do ekranu zarządzania usługą DynamoDB a następnie klikamy przycisk Create table. Na nowo otwartym ekranie konfiguracyjnym wprowadzamy nazwę tabeli oraz ustawiamy klucz główny tabeli: DynamoDB: tworzenie tabeli

Po kliknięciu przycisku Create musimy odczekać parę sekund. Po pomyślnie zakończonym procesie tworzenia zobaczymy ekran podsumowujący zawierający utworzoną konfigurację: DynamoDB: konfiguracja tabeli

Skoro już jesteśmy na tym ekranie spójrzcie na zakładkę Items oraz akcję Create item - na poźniejszym etapie wpisu z tego miejsca dodamy nowy obiekt do naszej tabeli dzięki czemu wyzwolimy całą logikę aplikacji. Najpierw jednak musimy utworzyć odpowiednią rolę z dostępem do DynamoDB oraz Lambda.

Tworzenie roli

Nowe role tworzymy z poziomu Identity and Access Management (IAM). Tym razem musimy pamiętać, żeby wskazać DynamoDB jako serwis. Dodatkowo będziemy zobligowani o wskazanie przypadku użycia. Na potrzeby testu wybieramy Amazon DynamoDB Accelerator (DAX) - DynamoDB access i klikamy przycisk Next: Permissions: DynamoDB: przypadek użycia Zanim pójdziemy dalej: Czym jest DAX? Jest to usługa pozwalająca na cachowanie danych dzięki czemu możemy uzyskać znacznie lepszą wydajność.

Na ekranie konfiguracyjnym roli możemy zobaczyć, że automatycznie została wskazana polityka AmazonDynamoDBFullAccess. W tym momencie nie musimy robić nic więcej ponieważ zawiera ona wszystkie uprawnienia, których potrzebujemy: DynamoDB: full access

Klikamy przycisk Next: Tags a następnie od razu robimy Review: DynamoDB: polityka dostępu

Tworzenie roli dla DynamoDB i Lambda

Korzystając z wyszukiwarki usług przechodzimy do Identity and Access Management (IAM) wybierając z panelu nawigacyjnego po lewej stronie Roles: DynamoDB: tworzenie roli

Klikamy przycisk Create role a następnie wybieramy Lambdę jako usługę, która będzie korzystać z tej roli. Następnie wybieramy Next: Permissions: DynamoDB: dostępność

Z poziomu widoku dostępnych polityk wybieramy: AmazonSNSFullAccess, AmazonLambda_FullAccess, CloudWatchFullAccess oraz AmazonDynamoDBFullAccess. Kilikamy przycisk Next: Tags oraz od razu robimy Review: DynamoDB: rola full access

Ostatni krok w tym punkcie to sprawdzenie czy cztery powyższe polityki zostały przypisane do roli, następnie zdefiniowane nazwy dla roli oraz kliknięcie przycisku Create role.

Tworzenie funkcji Lambda

W tej części wpisu dodamy Lambdę a następnie ustawimy na niej wyzwalacz (trigger) na DynamoDB.

Dodajemy nową Lambdę, definiujemy nazwę, wskazujemy środowisko .NET Core 3.1 oraz wybieramy utworzoną w poprzednim kroku rolę: DynamoDB: konfiguracja funkcji lambda

Wraz z pomyślnym utworzeniem funkcji dodajemy trigger na DynamoDB: DynamoDB: wyzwalacz

Zanim przejdziemy do właściwej implementacji musimy jeszcze dokonać drobnych konfiguracji, żeby być w stanie wysyłać wiadomości e-mail. Korzystając z wyszukiwarki usług w konsoli AWS przechodzimy do Amazon Simple Email Service, gdzie wybieramy z panelu nawigacyjnego Email Addresses a następnie klikamy przycisk Verify a New Email Address. Na wskazany adres SNS będzie wysyłał powiadomienia e-mail. Przed tym jednak musimy dokonać wspomnianej powyższej weryfikacji: DynamoDB: weryfikacja SNS

Implementacja

Podobnie jak w poprzednim wypadku przechodzimy do naszego IDE ponieważ w momencie przygotowywania tego wpisu AWS, z poziomu konsoli, nie udostępnia nam edytora dla naszego języka/technologii. Implementacja naszej funkcji może przyjąć poniższą formę:

using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Amazon;
using Amazon.Lambda.Core;
using Amazon.Lambda.DynamoDBEvents;
using Amazon.SimpleEmail;
using Amazon.SimpleEmail.Model;

// Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class.
[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))]

namespace awsLambdaDynamoEmail
{
    public class Function
    {
   	 /// <summary>
   	 /// Prosta funkcja reagująca na dodanie nowych danych do DynamoDB
   	 /// Wykonujemy prostą analizę + wysłanie wiadomości email
   	 /// </summary>
   	 /// <param name="dynamoEvent"></param>
   	 /// <param name="context"></param>
   	 /// <returns></returns>
   	 public async Task FunctionHandler(DynamoDBEvent dynamoEvent, ILambdaContext context)
   	 {
   		 context.Logger.LogLine($"Beginning to process {dynamoEvent.Records.Count} records...");
   		 StringBuilder sb = new StringBuilder();

   		 foreach (var record in dynamoEvent.Records)
   		 {
   			 context.Logger.LogLine($"Event ID: {record.EventID}");
   			 context.Logger.LogLine($"Event Name: {record.EventName}");

   			 // TODO: Dodać logikę biznesową przetwarzania obiektu (w ramach Waszych testów)
   			 // W naszym przypadku pobieramy identyfikator klucza oraz jego wartość
   			 foreach (var data in record.Dynamodb.NewImage)
   			 {
   				 context.Logger.LogLine($"Key: {data.Key}");

   				 // Dynamiczne mapowanie używane w DynamoDB
   				 // N jest symbolem pola numerycznego
   				 // S jest symbolem pola tekstowego
   				 if (data.Value.N != null)
   				 {
   					 context.Logger.LogLine($"Value: {data.Value.N}");
   					 sb.AppendLine($"Klucz: {data.Key}, Wartosc: {data.Value.N}");
   				 }
   				 else
   				 {
   					 context.Logger.LogLine($"Value: {data.Value.S}");
   					 sb.AppendLine($"Klucz: {data.Key}, Wartosc: {data.Value.S}");
   				 }
   			 }
   		 }

   		 // Ustawcie adres email zweryfikowany w obrębie konkretnego regionu
   		 string senderAddress = "podaj_zweryfikowany_adres@gmail.com";
   		 // Pamiętajcie o ustawieniu regionu w którym dokonaliście weryfikacji adresu email
   		 // W przeciwnym wypadku zobaczycie poniższy błąd:
   		 // Email address is not verified.
   		 // The following identities failed the check in region EU-WEST-2: adres_email@gmail.com
   		 using (var client = new AmazonSimpleEmailServiceClient(RegionEndpoint.USEast1))
   		 {
   			 var sendRequest = new SendEmailRequest
   			 {
   				 Source = senderAddress,
   				 Destination = new Destination
   				 {
   					 ToAddresses =
   					 new List<string> { "podaj_zweryfikowany_adres@gmail.com" }
   				 },
   				 Message = new Message
   				 {
   					 Subject = new Content("Wpisz temat wysyłanej wiadomości"),
   					 Body = new Body
   					 {
   						 Html = new Content
   						 {
   							 Charset = "UTF-8",
   							 Data = sb.ToString()
   						 },
   						 Text = new Content
   						 {
   							 Charset = "UTF-8",
   							 Data = "body"
   						 }
   					 }
   				 }
   			 };
   			 var response = await client.SendEmailAsync(sendRequest);
   		 }

   		 context.Logger.LogLine("Stream processing complete.");
   	 }
    }
}

Publikacji funkcji dokonamy używając zainstalowanej wtyczki z drobnymi zmianami pozwalającymi podpiąć się pod istniejącą funkcję: DynamoDB: publikowanie funkcji lambda

Testy

Pora na ostatnie kroki w tym wpisie. Nasza Lambda zostanie uruchomiona pod wpływem dodania nowych danych do tabeli. W tym celu wykorzystamy konsolę AWS definiując prosty obiekt: DynamoDB: dodawanie danych do tabeli

Wraz z poprawnym dodaniem obiektu do tabeli DynamoDB możemy przejść do dziennika zdarzeń naszej funkcji w celu sprawdzenia czy Lambda wykonała swoje zadanie: DynamoDB: podgląd dziennika zdarzeń

Możecie również zajrzeć do swojej skrzynki mailowej w celu sprawdzenia czy wysłana wiadomość dotarła na docelowy adres: DynamoDB: wiadomość e-mail

Wszystko działa poprawnie chodź sam przykład jest niezwykle trywialny. Głównym założeniem jest jednak natychmiastowa akcja w postaci wywołania funkcji, która może dokonać dalszego przetwarzania czy analizy danych, powiadomienia osób trzecich bądź powiadomienia innego (mikro)serwisu o nadejściu nowego zamówienia i natychmiastowym wdrożeniu kolejnych kroków niezbędnych do realizacji usługi.