Loupe

Mise en place d'un micro-service d'envoi d’emails avec Azure Function, Sendgrid et Node JS

Une problématique courante dans les applications web comme les sites e-commerce, les plateformes collaboratives ou dans toute application qui demande à ses utilisateurs de créer un compte est l’envoi d’emails aux utilisateurs. Une bonne pratique est de mettre en place un micro service pour déléguer cette fonctionnalité. Dans cet article nous allons utiliser une Azure Function couplée avec le service « SendGrid » pour mettre en place ce micro-service. Le code de la fonction sera rédigé en Node JS.

 

Architecture applicative et approche « Micro-service »

Ici l’objectif est simple, mettre en place un micro-service qui doit pouvoir être utilisable par plusieurs applications de façon transparente !

Pour cela, il est d’usage d’utiliser un service de « messagerie ». Dans Azure on distingue deux services de messagerie, Azure Storage Queue et Azure Services Bus. Les différences entre ces deux services sont expliquées ici. Dans notre cas nous allons utiliser une Azure Storage Queue, pour la simple et bonne raison que par défaut un compte de stockage est créé avec une Azure Fonction, nous allons donc utiliser ce compte de stockage pour y créer une queue.

L’idée est de mettre en place l’architecture suivante (sans développer les applications web et mobiles qui sont présentes sur le schéma à titre d’exemple !)

Schema

 

Architecture Azure

Création des ressources Azure

Pour mettre en place ce micro-service nous avons besoin de 2 ressources Azure :

  • Une Azure Function App (service plan « dynamic »)
  • Un compte de stockage

Dans mon cas, j’ai créé une Azure Fonction App nommée « SenderFunctionApp »  dans un groupe de ressources nommé « mailing-azureFunction-RG ». Automatiquement lors de la création de la « function app », un ensemble de ressources a été déployé dans le groupe de ressource :

  • Un compte de stockage utilisé pour les logs des fonctions
  • Une Azure Function App
  • Un Service plan qui définit le plan de service du site web* qui va hoster les fonctions
  • Un compte application insight déployé automatiquement avec le site web

 

resource-group

 

Avant de passer à l’implémentation de la fonction, il est nécessaire de créer une queue nommée « email » dans le compte de stockage « function449c9733ac70 ». Cette queue fera office de déclencheur (« input trigger ») pour la fonction Azure qui aura la responsabilité d’envoyer les emails.

*Pour rappel, une Azure Function App crée en background un site web qui a pour responsabilité d’héberger les fonctions. C’est une Azure Web App classique qui peut être configurée à souhait. D’ailleurs les App Settings de cette web app sont utilisables directement depuis les fonctions.

 

Envoi des emails avec « Sendgrid »

Sendgrid est un service qui permet d’envoyer des emails par l’intermédiaire d’API, des SDK C#, Ruby, NodeJS et PHP permettent d’utiliser les API Sendgrid et ainsi envoyer des emails grâce à quelques lignes de codes. Les clients Azure on accès à 25000 emails gratuits chaque mois avec Sendgrid, pour cela il suffit de créer un compte depuis le « Marketplace » du portail Azure.

 

marketplace-sendgrid

 

Une fois le compte créé, il faut aller sur le backoffice Sendgrid pour créer et récupérer une « API Key» qui permet de contacter les API Sendgrid depuis les SDK. Après la création de l’API Key, il sera possible de l’utiliser directement dans la fonction en la stockant dans une variable, mais la bonne pratique veut que l’on stocke cette API Key en tant qu’App Setting du site web qui va hoster les fonctions.

La documentation du SDK Node JS utilisé dans ce post est disponible ici.

 

Azure Function et NodeJS

La fonction

Dans notre cas, la logique métier de notre fonction va être développée en JavaScript / Node JS et il est nécessaire que la fonction soit exécutée lorsqu’un message est envoyé dans une queue Azure, il existe un « Template » de Function destiné à cet effet nommée « QueueTrigger - Node » :

QueueTriggerNode

 

Un fois ce template sélectionné, il est nécessaire de configurer la fonction pour qu’elle utilise comme déclencheur la queue « email » que nous avons créée précédemment :

 

function-configuration

 

Maintenant que la fonction est créée et qu’elle est configurée pour « écouter » la queue « email », il est temps de débuter l’implémentation de la fonction.

Le template « vide » de la fonction est le suivant :

module.exports = function (context, myQueueItem) {
    context.log('Node.js queue trigger function processed work item', myQueueItem);
    context.done();
};

Afin que la runtime puisse exécuter la fonction Azure, la fonction Javascript doit prendre en paramètre un object « context ». Cet object « context » permet de communiquer avec la runtime en exposant plusieurs méthodes dont « Log » et « done » qui doit être appelée en fin de traitement. Les autres paramètres qui correspondent aux inputs et outputs de la fonction sont optionnels car ils sont accessibles par l’intermédiaire de la fonction « context.bindings ».

Ainsi nous pourrions utiliser notre input « myQueueItem » sans le passer en tant que paramètre à la fonction :

var input = context.bindings.myQueueItem;

La fonction « context.binding » permet également de définir le contenu des outputs de la fonction Azure.

 

Installation des packages avec KUDU :

Pour pouvoir utiliser  Sendgrid depuis la fonction Azure, il faut que le package node JS « SendGrid » soit accessible par la fonction, nous allons donc utiliser l’invite de commande KUDU pour le récupérer.

Pour cela il faut se rendre sur la partie « configuration » du site web de l’application Azure Function. Cette partie configuration, couramment nommée « scm » pour « site configuration manager » est accessible avec l’url générique suivante : http://{myHostName}.scm.azurewebsites.net/.

Dans mon cas mon site possède comme « host name » par défaut le nom de l’application Azure Fonction, il est donc accessible à l’adresse suivante :

http://senderfunctionapp.azurewebsites.net et son site de configuration à cette adresse http://senderfunctionapp.scm.azurewebsites.net.

Une fois sur le site de configuration, on accède la console depuis l’onglet « Debug console ».

kudu

Il faut maintenant effectuer les étapes suivantes :

  1. Se placer dans le dossier de la fonction D:\home\site\wwwroot\{name_function}
  2. Uploder le fichier « package.json » qui définit la version du package sendgrid à récupérer
{
  "dependencies": {
    "sendgrid": "^1.9.2"
  }
}

   3.   Exécuter la commande « npm install »

kudu-cmd

Un dossier « node_modules » est alors créé dans le dossier courant avec un package « sendgrid » à l’intérieur, ce dernier sera donc utilisable depuis la fonction.

 

Le code

Pour fonctionner convenablement la fonction a besoin de récupérer les informations de l’email à envoyer. Ces informations sont contenues à l’intérieur des messages stockés dans la queue « emails ».

Ces messages sont stockés en JSON dans la queue et possèdent le format suivant :

{
  "to" : "tranise@infinitesquare.com",
  "from" :  "test@no-reply.com",
  "subject" : "Subject goes here",
  "body" : "<p> Hello World </p>",
  "files" : [
        {
          "filename" : "Ftp.PNG",
          "url" : "http://aaaaaaaaaa.blob.core.windows.net/img/Ftp.PNG"
        },
        {
          "filename" : "change-password-sql-server.PNG",
          "url" : "http://aaaaaaaaaaa.blob.core.windows.net/img/change-password-sql-server.PNG"
        }
      ]
}

Et voici enfin le code de la fonction Azure :

module.exports = function (context, myQueueItem) {
    
    // Get the api key from the website appSettings
    var apiKey = GetAppSettings("SendGridApiKey");
    
    // Log  the sendGrid module using the api key
    var sendgrid  = require('sendgrid')(apiKey);
    
    var email  = new sendgrid.Email({
      to:       myQueueItem.to,
      from:     myQueueItem.from,
      subject:  myQueueItem.subject,
      text:     myQueueItem.body,
      files:    myQueueItem.files
    });

    // Send the email  
    sendgrid.send(email, function(error, json) {
      if (error) { return context.log(error); }
      context.log(json);
    });
    
    context.done();
};

// Helper function used to retrieve AppSettings
function GetAppSettings(name)
{
    return process.env[name];
}

Happy Coding Sourire

Ces billets pourraient aussi vous intéresser

Vous nous direz ?!

Commentaires

comments powered by Disqus