Skip to content

Commit

Permalink
Decouple Change from Image
Browse files Browse the repository at this point in the history
  • Loading branch information
Zhiyuan-Amos committed Nov 28, 2021
1 parent 51c11b0 commit cd6d1e9
Show file tree
Hide file tree
Showing 24 changed files with 225 additions and 28 deletions.
37 changes: 37 additions & 0 deletions .github/workflows/azure-messaging-function.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# https://docs.microsoft.com/en-us/azure/azure-functions/functions-how-to-github-actions?tabs=dotnet#deploy-the-function-app

name: Deploy DotNet project to function app with a Linux environment

on:
[push]

env:
AZURE_FUNCTIONAPP_NAME: change-event
AZURE_FUNCTIONAPP_PACKAGE_PATH: '.\Messaging'
DOTNET_VERSION: '6.0.x'

jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: 'Checkout GitHub Action'
uses: actions/checkout@v2

- name: Setup DotNet ${{ env.DOTNET_VERSION }} Environment
uses: actions/setup-dotnet@v1
with:
dotnet-version: ${{ env.DOTNET_VERSION }}

- name: 'Resolve Project Dependencies Using Dotnet'
shell: bash
run: |
pushd './${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}'
dotnet build --configuration Release --output ./output
popd
- name: 'Run Azure Functions Action'
uses: Azure/functions-action@v1
id: fa
with:
app-name: ${{ env.AZURE_FUNCTIONAPP_NAME }}
package: '${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}/output'
publish-profile: ${{ secrets.AZURE_MESSAGING_FUNCTIONAPP_PUBLISH_PROFILE }}
4 changes: 4 additions & 0 deletions Api/Data/ChangeContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
.Property(change => change.Id)
.ToJsonProperty("id")
.HasConversion<string>();

modelBuilder.Entity<Change>()
.Property(change => change.Ttl)
.ToJsonProperty("ttl");
}
}
}
35 changes: 8 additions & 27 deletions Api/Features/Change/DeleteChangeFunction.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
using System;
using System.Linq;
using System.Net;
using System.Text.Json;
using System.Threading.Tasks;
using Azure.Storage.Blobs;
using Couple.Api.Data;
using Couple.Api.Infrastructure;
using Couple.Shared.Model;
using Couple.Shared.Model.Change;
using FluentValidation;
using Microsoft.Azure.Functions.Worker;
Expand All @@ -22,7 +18,7 @@ public class DeleteChangesFunction
private readonly ChangeContext _context;

public DeleteChangesFunction(ICurrentUserService currentUserService,
ChangeContext context)
ChangeContext context)
{
_currentUserService = currentUserService;
_context = context;
Expand Down Expand Up @@ -53,13 +49,6 @@ public async Task<HttpResponseData> Run(
.Where(change => model.Guids.Contains(change.Id))
.ToListAsync();

var areIdsValid = model.Guids.Count == toDelete.Count;

if (!areIdsValid)
{
return req.CreateResponse(HttpStatusCode.BadRequest);
}

var claims = _currentUserService.GetClaims(req.Headers);
var canDelete = toDelete.All(change => change.UserId == claims.Id);

Expand All @@ -68,24 +57,16 @@ public async Task<HttpResponseData> Run(
return req.CreateResponse(HttpStatusCode.Forbidden);
}

foreach (var change in toDelete)
{
change.Ttl = 3600;
}

_context
.Changes
.RemoveRange(toDelete);
await _context.SaveChangesAsync();

var imageIdsToDelete = toDelete
.Where(change => change.Command == Command.CreateImage || change.Command == Command.UpdateImage)
.Select(change => change.Content)
.Select(content => JsonSerializer.Deserialize<Model.Image>(content))
.Select(image => image.Id.ToString())
.ToList();
.UpdateRange(toDelete);

var connectionString = Environment.GetEnvironmentVariable("ImagesConnectionString");
foreach (var id in imageIdsToDelete)
{
var client = new BlobClient(connectionString, "images", id);
await client.DeleteIfExistsAsync();
}
await _context.SaveChangesAsync();

return req.CreateResponse(HttpStatusCode.OK);
}
Expand Down
1 change: 1 addition & 0 deletions Api/Features/Change/SynchronizeChangeFunction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ public async Task<HttpResponseData> Run(
var toReturn = await _context
.Changes
.Where(change => change.UserId == claims.Id)
.Where(change => change.Ttl == -1)
.OrderBy(change => change.Timestamp)
.ProjectTo<ChangeDto>(_mapper.ConfigurationProvider)
.ToListAsync();
Expand Down
1 change: 1 addition & 0 deletions Api/Features/Event/CreateEventFunction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ public async Task<HttpResponseData> Run(
Command.CreateEvent,
claims.PartnerId,
_dateTimeService.Now,
form.Value.Event.Id,
form.Json);

_context
Expand Down
1 change: 1 addition & 0 deletions Api/Features/Event/DeleteEventFunction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ public async Task<HttpResponseData> Run(
Command.DeleteEvent,
claims.PartnerId,
_dateTimeService.Now,
id,
JsonSerializer.Serialize(id));

_context
Expand Down
1 change: 1 addition & 0 deletions Api/Features/Event/UpdateEventFunction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ public async Task<HttpResponseData> Run(
Command.UpdateEvent,
claims.PartnerId,
_dateTimeService.Now,
form.Value.Event.Id,
form.Json);

_changeContext
Expand Down
1 change: 1 addition & 0 deletions Api/Features/Image/CreateImageFunction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ public async Task<HttpResponseData> Run(
Command.CreateImage,
claims.PartnerId,
_dateTimeService.Now,
dto.Id,
JsonSerializer.Serialize(_mapper.Map<Model.Image>(dto)));

_context
Expand Down
1 change: 1 addition & 0 deletions Api/Features/Image/DeleteImageFunction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ public async Task<HttpResponseData> Run(
Command.DeleteImage,
claims.PartnerId,
_dateTimeService.Now,
id,
JsonSerializer.Serialize(id));

_context
Expand Down
1 change: 1 addition & 0 deletions Api/Features/Image/UpdateImageFunction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ public async Task<HttpResponseData> Run(
Command.UpdateImage,
claims.PartnerId,
_dateTimeService.Now,
dto.Id,
JsonSerializer.Serialize(_mapper.Map<Model.Image>(dto)));

_context
Expand Down
1 change: 1 addition & 0 deletions Api/Features/Issue/CompleteTaskFunction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ public async Task<HttpResponseData> Run(
Command.CompleteTask,
claims.PartnerId,
_dateTimeService.Now,
form.Value.Id,
form.Json);

_context
Expand Down
1 change: 1 addition & 0 deletions Api/Features/Issue/CreateIssueFunction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ public async Task<HttpResponseData> Run(
Command.CreateIssue,
claims.PartnerId,
_dateTimeService.Now,
form.Value.Id,
form.Json);

_context
Expand Down
1 change: 1 addition & 0 deletions Api/Features/Issue/DeleteIssueFunction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ public async Task<HttpResponseData> Run(
Command.DeleteIssue,
claims.PartnerId,
_dateTimeService.Now,
id,
JsonSerializer.Serialize(id));

_context
Expand Down
1 change: 1 addition & 0 deletions Api/Features/Issue/UpdateIssueFunction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ public async Task<HttpResponseData> Run(
Command.UpdateIssue,
claims.PartnerId,
_dateTimeService.Now,
form.Value.Id,
form.Json);

_context
Expand Down
12 changes: 11 additions & 1 deletion Api/Model/Change.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,27 @@ public class Change
public string Command { get; init; }
public string UserId { get; init; }
public DateTime Timestamp { get; init; }
public Guid ContentId { get; init; }
public string Content { get; init; }
public int? Ttl { get; set; }

private Change() { }

public Change(Guid id, string command, string userId, DateTime timestamp, string content)
public Change(Guid id, string command, string userId, DateTime timestamp, Guid contentId, string content)
{
Id = id;
Command = command;
UserId = userId;
Timestamp = timestamp;
ContentId = contentId;
Content = content;

// 1. Annotating this property with Json Attributes don't seem to work e.g.
// [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] and [JsonPropertyName("ttl")]
// 2. The alternative is to implement the converter
// https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-migrate-from-newtonsoft-how-to?pivots=dotnet-6-0#conditionally-ignore-a-property
// which is much more troublesome than this solution.
Ttl = -1;
}
}
}
6 changes: 6 additions & 0 deletions Couple.sln
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Couple.Api", "Api\Couple.Ap
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Couple.Shared", "Shared\Couple.Shared.csproj", "{BF40FDE1-00B3-420B-B3C8-3AC60A075A85}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Couple.Messaging", "Messaging\Couple.Messaging.csproj", "{B6CF82EB-15D6-4E71-BEAE-F52FC2190109}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -27,6 +29,10 @@ Global
{BF40FDE1-00B3-420B-B3C8-3AC60A075A85}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BF40FDE1-00B3-420B-B3C8-3AC60A075A85}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BF40FDE1-00B3-420B-B3C8-3AC60A075A85}.Release|Any CPU.Build.0 = Release|Any CPU
{B6CF82EB-15D6-4E71-BEAE-F52FC2190109}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B6CF82EB-15D6-4E71-BEAE-F52FC2190109}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B6CF82EB-15D6-4E71-BEAE-F52FC2190109}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B6CF82EB-15D6-4E71-BEAE-F52FC2190109}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
21 changes: 21 additions & 0 deletions Messaging/Couple.Messaging.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<AzureFunctionsVersion>v4</AzureFunctionsVersion>
<AnalysisLevel>latest</AnalysisLevel>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Azure.Storage.Blobs" Version="12.10.0" />
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.CosmosDB" Version="3.0.10" />
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.EventGrid" Version="3.0.0" />
<PackageReference Include="Microsoft.NET.Sdk.Functions" Version="4.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Shared\Couple.Shared.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="host.json" CopyToOutputDirectory="PreserveNewest" />
<None Update="local.settings.json" CopyToOutputDirectory="PreserveNewest" CopyToPublishDirectory="Never" />
</ItemGroup>
</Project>
40 changes: 40 additions & 0 deletions Messaging/Features/ChangeDeletedEventFunction.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using Azure.Messaging.EventGrid;
using Couple.Messaging.Model;
using Couple.Shared.Model;
using Microsoft.Azure.Documents;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.EventGrid;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;

namespace Couple.Messaging.Features
{
public class DeletedEventFunction
{
[FunctionName("ChangeDeletedEventFunction")]
public async Task Run([CosmosDBTrigger("%DatabaseName%",
"%CollectionName%",
ConnectionStringSetting = "DatabaseConnectionString",
CreateLeaseCollectionIfNotExists = true)] IReadOnlyList<Document> documents,
[EventGrid(TopicEndpointUri = "EventGridEndpoint",
TopicKeySetting = "EventGridKey")] IAsyncCollector<EventGridEvent> eventCollector)
{
var changes = documents.Select(d => d.ToString())
.Select(json => JsonSerializer.Deserialize<Change>(json)!)
.Where(change => change.Ttl != -1)
.Where(change => change.Command is Command.CreateImage or Command.UpdateImage)
.ToList();

List<Task> tasks = new();
foreach (var change in changes)
{
var task = eventCollector.AddAsync(new(change.ContentId.ToString(), "ImageDeleted", "1", new { }));
tasks.Add(task);
}

await Task.WhenAll(tasks);
}
}
}
20 changes: 20 additions & 0 deletions Messaging/Features/ImageDeletedEventFunction.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using Azure.Storage.Blobs;
using Couple.Messaging.Model;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.EventGrid;
using System;
using System.Threading.Tasks;

namespace Couple.Messaging.Features
{
public class ImageDeletedEventFunction
{
[FunctionName("ImageDeletedEventFunction")]
public async Task Run([EventGridTrigger] Event @event)
{
var connectionString = Environment.GetEnvironmentVariable("ImagesConnectionString");
var client = new BlobClient(connectionString, "images", @event.Subject);
await client.DeleteIfExistsAsync();
}
}
}
18 changes: 18 additions & 0 deletions Messaging/Model/Change.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System;
using System.Text.Json.Serialization;

namespace Couple.Messaging.Model
{
public class Change
{
public Guid Id { get; init; }
public string? Command { get; init; }
public string? UserId { get; init; }
public DateTime Timestamp { get; init; }
public Guid ContentId { get; init; }
public string? Content { get; init; }

[JsonPropertyName("ttl")]
public int? Ttl { get; set; }
}
}
14 changes: 14 additions & 0 deletions Messaging/Model/Event.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;

namespace Couple.Messaging.Model
{
public class Event
{
public string? Topic { get; init; }
public string? Subject { get; init; }
public string? EventType { get; init; }
public DateTime EventTime { get; init; }
public IDictionary<string, object>? Data { get; init; }
}
}
9 changes: 9 additions & 0 deletions Messaging/Properties/launchSettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"profiles": {
"Api": {
"commandName": "Executable",
"executablePath": "func",
"commandLineArgs": "start"
}
}
}
10 changes: 10 additions & 0 deletions Messaging/host.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"version": "2.0",
"logging": {
"applicationInsights": {
"samplingSettings": {
"isEnabled": true
}
}
}
}
Loading

0 comments on commit cd6d1e9

Please sign in to comment.