Business Process Orchestration with Azure Function

Business Process Orchestration (or BPM) can simply be defined as execution of series of events in parallel and/or sequential manner. Azure Cloud made our job much easier to manage Business Process with the invention of Azure Function App as platform. Today, we are going to demonstrate orchestration of order processing (in part) using series of Function Apps in Visual Studio 2017.

1-2-3 of Business Process Orchestration!

(1) Customer orders a product. (2) Order goes to Azure Queue Storage via HttpTrigger. (3) As soon as order goes into the queue, email is sent to customer via QueueTrigger and SendGrid.

Learning Objectives:

  • HttpTrigger with POCO IN parameter
  • How to insert a message in Azure Storage Queue
  • QueueTrigger to read messages from Azure Storage Queue
  • How to use OUT parameter with IAsyncCollector<T>
  • SendGrid to deliver emails
  • Debug Azure Function locally in Visual Studio
  • Use Azure Function CLI for debugging
  • Use Azure Storage Explorer to view Queue items

We are going to demo how to build Azure Function App using Visual Studio 2017, debug the events and verify if the process is working as expected. It would be good to review my previous article, Azure Function App- the evolution of software as a service, as it would provide information on how to setup Visual Studio for Azure Function project. Let’s jump to the codes!

OrderQueue Function:

Insert a POCO order object to Azure Queue Storage. Parameter order object must be defined as HttpTrigger. This is needed for the object to be deserialized automatically by Azure Function. Req object should not be defined at HttpTrigger.


using System.Linq;
using System;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Queue;
using Newtonsoft.Json;

namespace AzureFunctionApps
{
/// <summary>
/// QueueOrder: Insert a POCO order object to Azure Queue Storage. Parameter order object must be defined as HttpTrigger.
/// This is needed for the object to be deserialized automatically by Azure Function. Req object should not be defined at HttpTrigger.
/// https://docs.microsoft.com/en-us/azure/storage/queues/storage-dotnet-how-to-use-queues
/// </summary>
public static class QueueOrder
{
[FunctionName("QueueOrder")]
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req,
[HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] POCOOrder order, TraceWriter log)
{
string msg = $"Hello " + order.CustomerName + "! Your order for " + order.ProductName + " with quantity of " + order.OrderCount + " has been received. You will receive email at " + order.CustomerEmail + " as soon as order is shipped.";
log.Info(msg);

var connectionString = GetEnvironmentVariable("AzureWebJobsStorage");

// Retrieve storage account from connection string.
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(connectionString);

// Create the queue client.
CloudQueueClient queueClient = storageAccount.CreateCloudQueueClient();

// Retrieve a reference to a container.
CloudQueue queue = queueClient.GetQueueReference("orderqueue");

// Create the queue if it doesn't already exist
queue.CreateIfNotExists();

// Serialize POCO Order
var orderData = JsonConvert.SerializeObject(order, Formatting.Indented);

// Create a message and add it to the queue.
CloudQueueMessage message = new CloudQueueMessage(orderData);
queue.AddMessage(message);

// Send an acknowledgement to client app
return await Task.Run(() =>
{
return new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent(JsonConvert.SerializeObject(msg, Formatting.Indented), Encoding.UTF8, "application/json")
};
});
}

private static string GetEnvironmentVariable(string name)
{
return System.Environment.GetEnvironmentVariable(name, EnvironmentVariableTarget.Process);
}
}

/// <summary>
/// This is just a simple/sample object class to demonstrate intake of parameter as POCO object.
/// </summary>
public class POCOOrder
{
public string CustomerName { get; set; }
public string CustomerEmail { get; set; }
public int ProductId { get; set; }
public string ProductName { get; set; }
public int OrderCount { get; set; }
}
}

SendEmail Function:

SendEmail: This function is triggered when an order is queued into Azure Queue Storage. Additionally, this function sends Email using SendGrid and IAsyncCollector<T> as output parameter. All you have to do is- add a message to output parameter!


using System;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Host;
using SendGrid.Helpers.Mail;
using System.Threading.Tasks;
using System.Net.Http;
using System.Net;

namespace AzureFunctionApps
{
/// <summary>
/// SendEmail: This function is triggered when an order is queued into Azure Queue Storage. Additionally, this function sends Email
/// using SendGrid and IAsyncCollector<T> as output parameter. All you have to do is- add a message to output parameter!
/// </summary>
public static class SendEmail
{
[FunctionName("SendEmail")]
public static async void Run([QueueTrigger("orderqueue", Connection = "AzureWebJobsStorage")]POCOOrder order,
TraceWriter log, [SendGrid( ApiKey = "AzureWebJobsSendGridApiKey")] IAsyncCollector<Mail> outputMessage)
{
string msg = $"Hello " + order.CustomerName + "! Your order for " + order.ProductName + " with quantity of " + order.OrderCount + " has been shipped.";
log.Info(msg);

Mail message = new Mail
{
Subject = msg,
From = new Email("Prodip.Kumar@outlook.com")
};

var personalization = new Personalization();
// Change To email of recipient
personalization.AddTo(new Email(order.CustomerEmail));

Content content = new Content
{
Type = "text/plain",
Value = msg + "Thank you for being such a nice customer!"
};
message.AddContent(content);
message.AddPersonalization(personalization);

await outputMessage.AddAsync(message);
}
}
}

Typically, We need a user interface (i.e. Angular app) to call QueueOrder function but we are going to use Google Postman to send the order-

Let’s check if the order indeed landed in the queue. We can use Azure Storage Explorer to see what’s the box! Yea, it’s there!

Okay, Order is in the queue and it should automatically trigger the SendEmail Function and email will be sent to customer. To see this in action, I started the debugger in Visual Studio and it started the Azure Function CLI.

You don’t see SendEmail function is the CLI list because it is void function. Rest of the function endpoints are listed. I added a break point to inspect QueueTrigger and it sure hit the break few milliseconds after order inserted into the queue!

We can inspect the CLI interface and it would show the logs (and error if any). We can see logs showing order has been queued and email has been sent!

Did SendGrid really deliver the email? We don’t know until customer gets the email but you can login to app.sendgrid.com to check the status. I signed up for Free Tier (25K mails/month) which is more than enough for my demo!

Full Source Codes can be obtained @ https://github.com/aspnet4you/azure-functions-aspnet4you

Well, I did not get the mails and reporting the issue to SendGrid resulted in generic response! What do you expect for free service! Found this article online- “Using SendGrid and emails are not being delivered to Hotmail/Outlook/Live/MSN.com recipients?” and it solved the problem after I added my domain (aspnet4you.com) to Whitelabel at SendGrid Settings. Follow the instruction and it would ask you update three (3) CNAME record at your DNS provider (GoDaddy in my case). Once you added all three CNAME, you can validate the whitelabel.

Add email.aspnet4you.com to SendGrid Whitelabel:

Update DNS Recrods: Add CNAME as values provided by SendGrid.

Updated the codes to change the From and ReplyTo fields and it worked!

You got mail!!

 

 

Leave a Reply