Skip to content

Commit 6bf780c

Browse files
authored
Merge pull request Azure#55 from Azure/module9-review
Module 9 review as well as other Miscellaneous changes
2 parents 6487990 + 8a3c72d commit 6bf780c

13 files changed

Lines changed: 268 additions & 173 deletions

File tree

docs/aca/01-deploy-api-to-aca/index.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ canonical_url: 'https://bitoftech.net/2022/08/25/deploy-microservice-application
33
---
44

55
# Module 1 - Deploy Backend API to ACA
6+
!!! info "Module Duration"
7+
60 minutes
68

79
In this module, we will start by creating the first microservice named `ACA Web API – Backend` as illustrated in the [architecture diagram](../../assets/images/00-workshop-intro/ACA-Architecture-workshop.jpg). Followed by that we will provision the Azure resources needed to deploy the service to Azure Container Apps using the Azure CLI.
810
### 1. Create the backend API project (Web API)

docs/aca/02-aca-comm/index.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22
canonical_url: https://bitoftech.net/2022/08/25/communication-microservices-azure-container-apps/
33
---
44

5-
# Module 2 - Communication between Microservices in ACA
5+
# Module 2 - Communication Between Microservices in ACA
6+
!!! info "Module Duration"
7+
60 minutes
68

79
In this module, we will add a service named `ACA Web API – Frontend` as illustrated in the [architecture diagram](../../assets/images/00-workshop-intro/ACA-Architecture-workshop.jpg). This service will host a simple ASP.NET Razor pages web app which allows the end users to manage their tasks. After that we will provision Azure resources needed to deploy the service to ACA using Azure CLI.
8-
### 1. Create the frontend Web App project (Web APP)
10+
### 1. Create the Frontend Web App project (Web APP)
911

1012
- Open a command-line terminal and navigate to root folder of your project. Create a new folder as shown below:
1113
```shell

docs/aca/03-aca-dapr-integration/index.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@ canonical_url: https://bitoftech.net/2022/08/29/dapr-integration-with-azure-cont
33
---
44

55
# Module 3 - Dapr Integration with ACA
6+
!!! info "Module Duration"
7+
60 minutes
68

79
In this module, we will start integrating Dapr into both services and see how Dapr with ACA will simplify complex microservices scenarios such as service discovery, service-to-service invocation, calling services asynchronously via pub/sub patterns, auto-scaling for overloaded services, etc..
810

9-
### Benefits of integrating Dapr in Azure Container Apps
11+
### Benefits of Integrating Dapr in Azure Container Apps
1012

1113
The Tasks Tracker microservice application is composed of multiple microservices (2 microservices so far), and function calls are spread across the network. To support the distributed nature of microservices,
1214
we need to account for failures, retries, and timeouts. While Container Apps features the building blocks for running microservices, the use of Dapr provides an even richer microservices programming model.
@@ -22,10 +24,10 @@ Although we won't tap into all these benefits in this workshop its worth keeping
2224
- Control what operations clients can do using access control policies.
2325
- Capture traces and metrics for all calls between services to provide insights and diagnostics.
2426

25-
### Configure Dapr on a local development machine
27+
### Configure Dapr on a Local Development Machine
2628
In order to run applications using Dapr, we need to install and initialize Dapr CLI locally. The official documentation is quite clear, and we can follow the steps needed to [install](https://docs.dapr.io/getting-started/install-dapr-cli/) Dapr and then [Initialize](https://docs.dapr.io/getting-started/install-dapr-selfhost/) it.
2729

28-
### Run Backend API and Frontend Web App locally using Dapr
30+
### Run Backend API and Frontend Web App Locally Using Dapr
2931
You are now ready to run the applications locally using Dapr sidecar in a self-hosted mode. There is a VS code extension called [Dapr](https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-dapr) which will allow you to run, debug, and interact with Dapr-enabled applications in VS Code.
3032

3133
- Let's start by running the Backend Web API service using Dapr. From VS Code open a new PowerShell terminal, run the below commands in PS terminal based on your .NET version.

docs/aca/04-aca-dapr-stateapi/index.md

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ canonical_url: https://bitoftech.net/2022/08/29/azure-container-apps-state-store
33
---
44

55
# Module 4 - ACA State Store With Dapr State Management API
6+
!!! info "Module Duration"
7+
60 minutes
68

79
In this module we will switch the in-memory store of tasks and use a key/value persistent store (Azure Cosmos DB). By using the [Dapr State Management Building Block](https://docs.dapr.io/developing-applications/building-blocks/state-management/state-management-overview/), we will see how we can store the data in Azure Cosmos DB without installing any Cosmos DB SDK or write specific code to integrate our Backend API with Azure Cosmos DB.
810
Moreover, we will use Redis to store tasks when we are running the application locally. You will see that we can switch between different stores without any code changes, thanks to the [Dapr pluggable state stores feature](https://docs.dapr.io/developing-applications/building-blocks/state-management/state-management-overview/#pluggable-state-stores). It is a matter of adding new Dapr Component files and the underlying store will be changed. This page shows the [supported state stores](https://docs.dapr.io/reference/components-reference/supported-state-stores/) in Dapr.
@@ -11,7 +13,7 @@ Moreover, we will use Redis to store tasks when we are running the application l
1113

1214
### Overview of Dapr State Management API
1315

14-
Dapr's state management API allows you to save, read, and query key/value pairs in the supported state stores. To try this out and without doing any code changes or installing any NuGet packages we can directly invoke the State Management API and store the data on Redis locally. When you initialized Dapr in your local development environment, it installed Redis container instance locally. So we can use Redis locally to store and retrieve state. If you navigate to the path `%USERPROFILE%\.dapr\components` you will find a file named `statestore.yaml`. Inside this file, you will see the properties needed to access the local Redis instance. The [state store template component file structure](https://docs.dapr.io/operations/components/setup-state-store/) can be found on this link.
16+
Dapr's state management API allows you to save, read, and query key/value pairs in the supported state stores. To try this out and without doing any code changes or installing any NuGet packages we can directly invoke the State Management API and store the data on Redis locally. When you initialized Dapr in your local development environment, it installed Redis container instance locally. So we can use Redis locally to store and retrieve state. If you navigate to the path `%USERPROFILE%\.dapr\components (assuming you are using windows)` you will find a file named `statestore.yaml`. Inside this file, you will see the properties needed to access the local Redis instance. The [state store template component file structure](https://docs.dapr.io/operations/components/setup-state-store/) can be found on this link.
1517

1618
To try out the State Management APIs, run the Backend API from VS Code by running the following command. Remember to replace the place holders with your own values:
1719

@@ -102,11 +104,11 @@ For example if you execute the following GET [http://localhost:3500/v1.0/state/s
102104
}
103105
```
104106

105-
### Use Dapr Client SDK for State Store Management
107+
### Use Dapr Client SDK For State Store Management
106108

107109
Whereas in the previous section we demonstrated using Dapr State Store without code changes, we will now introduce a change on the Backend API and create a new service named `TasksStoreManager.cs` which will implement the interface `ITasksManager.cs` to start storing tasks data on the persist store. Locally we will start testing with Redis, then we are going to change the state store to use Azure Cosmos DB.
108110

109-
#### 1. Add Dapr Client SDK to the Backend API
111+
#### 1. Add Dapr Client SDK to The Backend API
110112

111113
Similar to what we have done in the Frontend Web App, we need to use Dapr Client SDK to manage the state store. Update below file with highlighted lines:
112114

@@ -119,7 +121,7 @@ Similar to what we have done in the Frontend Web App, we need to use Dapr Client
119121
</ItemGroup>
120122
```
121123

122-
#### 2. Create a new concrete implementation to manage tasks persistence
124+
#### 2. Create a New Concrete Implementation to Manage Tasks Persistence
123125

124126
As you recall from the previous module, we were storing the tasks in memory. Now we need to store them in Redis and later on Azure Cosmos DB.
125127
The key thing to keep in mind here is that switching from redis to Azure Cosmos DB won't require changing the code below which is a huge advantage of using Dapr.
@@ -139,7 +141,7 @@ Add below file under the folder named **Services**. This file will implement the
139141
The query API will not work against the local Redis store as you need to install [RediSearch](https://redis.io/docs/stack/search/) locally on your machine which is out of the scope for this workshop.
140142
It will work locally once we switch to Azure Cosmos DB.
141143

142-
#### 3. Register the TasksStoreManager new service and DaprClient
144+
#### 3. Register the TasksStoreManager New Service and DaprClient
143145

144146
Now we need to register the new service named `TasksStoreManager` and `DaprClient` when the Backend API app starts up. Update the below file with the highlighted text as shown below.
145147

@@ -216,7 +218,7 @@ az cosmosdb keys list `
216218
--resource-group $RESOURCE_GROUP
217219
```
218220

219-
**2. Create a Component file for State Store Management:** Dapr uses a modular design where functionality is delivered as a component. Each component has an interface definition.
221+
**2. Create a Component File for State Store Management:** Dapr uses a modular design where functionality is delivered as a component. Each component has an interface definition.
220222
All the components are pluggable so that you can swap out one component with the same interface for another
221223

222224
Components are configured at design-time with a YAML file which is stored in either a components/local folder within your solution, or globally in the `.dapr` folder created when invoking `dapr init`.
@@ -331,7 +333,7 @@ Keep a note of the property `principalId` as we are going to use it in the next
331333
}
332334
```
333335

334-
#### 2. Assign the container app system-identity to the built-in Cosmos DB role
336+
#### 2. Assign the Container App System-Identity To the Built-in Cosmos DB Role
335337

336338
Next, we need to associate the container app system-identity with the target Cosmos DB resource.
337339
You can read more about Azure built-in roles for Cosmos DB or how to create custom fine-tuned roles [here](https://learn.microsoft.com/en-us/azure/cosmos-db/how-to-setup-rbac#built-in-role-definitions).
@@ -361,7 +363,7 @@ We have to create a [dapr component schema file](https://learn.microsoft.com/en-
361363
Azure Container Apps. Reason for this variance is that ACA Dapr schema is slightly simplified to support Dapr components and removes unnecessary fields, including `apiVersion`, `kind`, and redundant metadata and
362364
spec properties.
363365

364-
#### 1. Create an ACA-Dapr Component file for State Store Management
366+
#### 1. Create an ACA-Dapr Component File For State Store Management
365367

366368
Here it is recommended to separate the component files that will be used when deploying to Azure Container Apps from the ones which we will use when running our application locally (Dapr self-hosted).
367369

@@ -381,7 +383,7 @@ Create a new folder named **aca-components** under the directory **TasksTracker.
381383
- We are not referencing any Cosmos DB Keys/Connection strings as the authentication between Dapr and Cosmos DB will be configured using Managed Identities.
382384
- We are setting the `scopes` array value to `tasksmanager-backend-api` to ensure Cosmos DB component is loaded at runtime by only the appropriate container apps. In our case it will be needed only for the container apps with Dapr application IDs `tasksmanager-backend-api`. In future modules we are going to include another container app which needs to access Cosmos DB.
383385

384-
#### 2. Build Frontend Web App and Backend API App images and push them to ACR
386+
#### 2. Build Frontend Web App and Backend API App Images and Push Them to ACR
385387

386388
As we have done previously we need to build and deploy both app images to ACR, so they are ready to be deployed to Azure Container Apps.
387389
To do so, continue using the same PowerShell console and paste the code below (Make sure you are on the following directory **TasksTracker.ContainerApps**):
@@ -428,7 +430,7 @@ az containerapp dapr enable --name $FRONTEND_WEBAPP_NAME `
428430

429431
For a complete list of the supported Dapr sidecar configurations in Container Apps, you can refer to [this link](https://learn.microsoft.com/en-us/azure/container-apps/dapr-overview?tabs=bicep1%2Cyaml#dapr-enablement).
430432

431-
#### 5. Deploy new revisions of the Frontend Web App and Backend API to Container Apps
433+
#### 5. Deploy New Revisions of the Frontend Web App and Backend API to Container Apps
432434

433435
The last thing we need to do here is to update both container apps and deploy the new images from ACR. To do so we need to run the commands found below.
434436

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
using Dapr.Client;
22
using Microsoft.AspNetCore.Mvc;
3+
using SendGrid;
4+
using SendGrid.Helpers.Mail;
35
using TasksTracker.Processor.Backend.Svc.Models;
46

57
namespace TasksTracker.Processor.Backend.Svc.Controllers
@@ -10,27 +12,50 @@ public class TasksNotifierController : ControllerBase
1012
{
1113
private readonly IConfiguration _config;
1214
private readonly ILogger _logger;
13-
public TasksNotifierController(IConfiguration config, ILogger<TasksNotifierController> logger)
15+
private readonly DaprClient _daprClient;
16+
public TasksNotifierController(IConfiguration config, ILogger<TasksNotifierController> logger, DaprClient daprClient)
1417
{
1518
_config = config;
1619
_logger = logger;
20+
_daprClient = daprClient;
1721
}
1822

19-
public IActionResult Get()
20-
{
21-
return Ok();
22-
}
23-
24-
[Dapr.Topic("taskspubsub", "tasksavedtopic")]
23+
[Dapr.Topic("dapr-pubsub-servicebus", "tasksavedtopic")]
2524
[HttpPost("tasksaved")]
2625
public async Task<IActionResult> TaskSaved([FromBody] TaskModel taskModel)
2726
{
2827
_logger.LogInformation("Started processing message with Task Name '{0}'", taskModel.TaskName);
29-
30-
//Do the actual sending of emails here, return 200 ok to consumer of the message
31-
return Ok();
32-
//In case we need to return message back to the topic, return http 400 bad request
33-
//return BadRequest();
28+
29+
var sendGridResponse = await SendEmail(taskModel);
30+
31+
if (sendGridResponse.Item1)
32+
{
33+
return Ok($"SendGrid response staus code: {sendGridResponse.Item1}");
34+
}
35+
36+
return BadRequest($"Failed to send email, SendGrid response status code: {sendGridResponse.Item1}");
37+
}
38+
39+
private async Task<Tuple<bool, string>> SendEmail(TaskModel taskModel)
40+
{
41+
42+
var apiKey = _config.GetValue<string>("SendGrid:ApiKey");
43+
var sendEmailResponse = true;
44+
var sendEmailStatusCode = System.Net.HttpStatusCode.Accepted;
45+
var client = new SendGridClient(apiKey);
46+
var from = new EmailAddress("taiseer.joudeh@gmail.com", "Tasks Tracker Notification");
47+
var subject = $"Task '{taskModel.TaskName}' is assigned to you!";
48+
var to = new EmailAddress(taskModel.TaskAssignedTo, taskModel.TaskAssignedTo);
49+
var plainTextContent = $"Task '{taskModel.TaskName}' is assigned to you. Task should be completed by the end of: {taskModel.TaskDueDate.ToString("dd/MM/yyyy")}";
50+
var htmlContent = plainTextContent;
51+
var msg = MailHelper.CreateSingleEmail(from, to, subject, plainTextContent, htmlContent);
52+
53+
var response = await client.SendEmailAsync(msg);
54+
sendEmailResponse = response.IsSuccessStatusCode;
55+
sendEmailStatusCode = response.StatusCode;
56+
57+
return new Tuple<bool, string>(sendEmailResponse, sendEmailStatusCode.ToString());
58+
3459
}
3560
}
3661
}

0 commit comments

Comments
 (0)