Azurite and Durable Functions Testing Demo
In this blog post, we’re diving into a practical example that showcases how we can leverage Azure Durable Functions for orchestrating a workflow that processes invoices. Specifically, we’ll discuss generating and storing PDF invoices in Azure Blob Storage. Additionally, we’ll touch upon how Azurite, a lightweight storage emulator for Azure Storage, can be used for local development and testing purposes. This approach greatly simplifies development workflows by allowing developers to test their Azure Storage solutions locally, without the need for an actual Azure subscription during the initial development phase.
What are Azure Durable Functions?
Azure Durable Functions is an extension of Azure Functions that enables the creation of stateful functions in a serverless compute environment. The extension manages state, checkpoints, and restarts for you, making it simpler to code complex stateful workflows.
What is Azurite?
Azurite is an open-source storage emulator supported by Azure. It provides a local environment that emulates Azure Blob, Queue, and Table Storage. It’s an excellent tool for developers looking to test their Azure Storage applications locally, without incurring costs or network latency associated with Azure’s cloud storage.
Scenario Overview
In our scenario, we have an orchestration function that coordinates two activities:
- Fetching an invoice: Simulates fetching invoice data, potentially from a database or an external API.
- Generating and storing a PDF invoice: Creates a PDF invoice from the fetched data and stores it in Azure Blob Storage.
Here’s a brief overview of the code components involved:
- FetchInvoice Activity Function: Simulates the retrieval of invoice data.
- GenerateAndStorePDF Activity Function: Generates a PDF from the invoice data and uploads it to Azure Blob Storage.
- InvoiceOrchestrator: Orchestrates the call to the activity functions in a specific order.
- HTTP Triggers: Two HTTP-triggered functions to start the orchestration and check the processing status.
Deep Dive into the Code
Now, let’s break down the key parts of the code:
FetchInvoice Activity Function
This function simulates fetching invoice data. In a real-world scenario, this could involve querying a database or an external service. The mock data is returned with a simulated delay to mimic processing time.
const FetchInvoice = {
handler: async (context) => {
await new Promise(resolve => setTimeout(resolve, 1000));
return {
success: true,
data: mockInvoice,
timestamp: new Date().toISOString()
};
}
};
GenerateAndStorePDF Activity Function
This function generates a PDF invoice using the pdfkit
library and uploads it to Azure Blob Storage. It requires valid invoiceId
in the input data to proceed.
const GenerateAndStorePDF = {
handler: async (input, context) => {
// PDF generation and Azure Storage upload logic here
}
};
InvoiceOrchestrator Function
The orchestrator function coordinates the execution of the activity functions. It first calls FetchInvoice
to get the invoice data, then passes this data to GenerateAndStorePDF
to create and store the PDF invoice in Azure Blob Storage.
df.app.orchestration('InvoiceOrchestrator', function* (context) {
// Orchestrator logic here
});
HTTP Trigger to Start the Orchestration
An HTTP-triggered function is used to start the orchestration. This provides an endpoint that can be called to initiate the invoice processing workflow.
app.http('InvoiceHttpStart', {
// HTTP trigger logic here
});
Testing with Azurite
To test this scenario locally, we can use Azurite as a substitute for Azure Blob Storage. This allows us to run and debug the entire process on our local development machine.
-
Install Azurite: First, ensure that Azurite is installed and running on your local machine. You can install Azurite via npm:
npm install -g azurite
-
Start Azurite: Run Azurite with the following command:
azurite --silent --location .\azurite --debug azurite\debug.log
-
Configure Local Storage Connection String: In your application, ensure that the connection string points to your local Azurite instance, typically:
DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://127.0.0.1:10000/devstoreaccount1;
-
Run Your Application: With Azurite running, execute your application. All Azure Storage operations will interact with your local Azurite instance.
By following these steps, you can develop and test Azure Durable Functions and Azure Storage interactions without needing an Azure subscription for initial development stages. This approach speeds up the development process and helps in creating robust, cloud-ready applications.
Conclusion
In this demo, we’ve seen how to leverage Azure Durable Functions for orchestrating a simple invoice processing workflow, including the generation and storage of PDF invoices in Azure Blob Storage. Furthermore, we’ve introduced Azurite as a valuable tool for local development and testing of Azure Storage solutions, enabling faster iteration and development cycles. Whether you’re developing complex workflows or simple Azure Storage interactions, tools like Azure Durable Functions and Azurite are essential in building scalable and efficient cloud-based applications. full code example including jest and working in a github actions piline can be found here