Skip to content

Commit

Permalink
1️⃣1️⃣ 🧙 supporting files: durable entities, change feed
Browse files Browse the repository at this point in the history
  • Loading branch information
Ami Hollander committed Jan 6, 2024
1 parent 21ced63 commit 4874a1c
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 1 deletion.
14 changes: 14 additions & 0 deletions src/backend/Entities/ITodoItemEntity.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using System;
using System.Threading.Tasks;
using backend.Models;

namespace backend.Entities;

public interface ITodoItemEntity
{
Task Create(TodoItem todoItem);

Task Sync();

Task Delete();
}
113 changes: 113 additions & 0 deletions src/backend/Entities/TodoItemEntity.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
using System;
using System.Threading.Tasks;
using Microsoft.Azure.Cosmos;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.DurableTask;
using backend.Models;

namespace backend.Entities;

public class TodoItemEntity : ITodoItemEntity
{
[JsonIgnore]
private ILogger _logger;

[JsonIgnore]
private CosmosClient _cosmosClient;

[JsonProperty("id")]
public string Id { get; set; }

[JsonProperty("todoItem")]
public TodoItem TodoItem { get; set; }

public TodoItemEntity(ILogger<TodoItemEntity> logger, CosmosClient cosmosClient)
{
_logger = logger;
_cosmosClient = cosmosClient;
}

public Task Create(TodoItem todoItem)
{
if (todoItem.DueDate is null || todoItem.IsOverdue())
{
Entity.Current.SignalEntity<ITodoItemEntity>(todoItem.Id, e => e.Delete());
return Task.CompletedTask;
}

Id = todoItem.Id;
TodoItem = todoItem;

SetTimer();

return Task.CompletedTask;
}

public async Task Sync()
{
var container = _cosmosClient.GetContainer("Todo", "TodoItem");

try
{
ItemResponse<TodoItem> readResponse = await container.ReadItemAsync<TodoItem>(
id: TodoItem.Id,
partitionKey: new PartitionKey(TodoItem.Id));

if (readResponse.StatusCode != System.Net.HttpStatusCode.OK)
{
return;
}

TodoItem todoItem = readResponse.Resource;

// Update entity state
TodoItem = todoItem;

if (!todoItem.IsTimerRequired())
{
Entity.Current.SignalEntity<ITodoItemEntity>(Id, e => e.Delete());
return;
}

if (todoItem.IsOverdue())
{
todoItem.State = "overdue";
todoItem.UpdatedDate = DateTimeOffset.UtcNow.DateTime;
await container.UpsertItemAsync(todoItem);

Entity.Current.SignalEntity<ITodoItemEntity>(Id, e => e.Delete());
return;
}

SetTimer();
}
catch (CosmosException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
{
_logger.LogWarning($"Missing ToDo item in db, id: {TodoItem.Id}.");
}
}

public Task Delete()
{
_logger.LogInformation($"Deleting entity, id=[{Id}]");

Entity.Current.DeleteState();

return Task.CompletedTask;
}

private void SetTimer()
{
DateTime alarm = TodoItem.DueDate.GetValueOrDefault().DateTime;
Entity.Current.SignalEntity<ITodoItemEntity>(TodoItem.Id, alarm, e => e.Sync());
}

[FunctionName(nameof(TodoItemEntity))]
public static Task Run(
[EntityTrigger] IDurableEntityContext context)
{
return context.DispatchAsync<TodoItemEntity>();
}
}
11 changes: 10 additions & 1 deletion src/backend/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
using System.Threading.Tasks;
using Azure.Identity;
using Azure.Security.KeyVault.Secrets;
using Microsoft.Azure.Cosmos;
using Microsoft.Azure.Cosmos.Fluent;
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection;
Expand Down Expand Up @@ -34,7 +36,14 @@ public override void Configure(IFunctionsHostBuilder builder)
return new VirusTotalClient(virusTotalApiKey, httpClient, memoryCache);
})
.AddSingleton<Healthz>();
.AddSingleton<Healthz>()
.AddSingleton<CosmosClient>(serviceProvider =>
{
var accountEndpoint = Environment.GetEnvironmentVariable("CosmosConnectionOptions__accountEndpoint");
var defaultAzureCredentials = serviceProvider.GetService<DefaultAzureCredential>();
return new CosmosClientBuilder(accountEndpoint, defaultAzureCredentials).Build();
});
}
}
}
2 changes: 2 additions & 0 deletions src/backend/Todo.Backend.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@
<ItemGroup>
<PackageReference Include="Azure.Identity" Version="1.10.4" />
<PackageReference Include="Azure.Security.KeyVault.Secrets" Version="4.5.0" />
<PackageReference Include="Microsoft.Azure.Cosmos" Version="3.35.3" />
<PackageReference Include="Microsoft.Azure.Functions.Extensions" Version="1.1.0" />
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.CosmosDB" Version="4.3.0" />
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.DurableTask" Version="2.11.1" />
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.EventHubs" Version="5.4.0" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="6.0.1" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
Expand Down
5 changes: 5 additions & 0 deletions src/backend/host.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@
// "default": "Information"
"backend.Healthz": "Information"
},
"extensions": {
"durableTask": {
"hubName": "ToDoApp"
}
},
"applicationInsights": {
"samplingSettings": {
"isEnabled": true,
Expand Down

0 comments on commit 4874a1c

Please sign in to comment.