Skip to content

Commit

Permalink
Consumer API: Multiple active identity deletion processes can exist (#…
Browse files Browse the repository at this point in the history
…843)

* feat: Use Semaphore (only temporary)

* chore: Remove semaphore

* fix: Use unique filtered index

* feat: Add filtered index to SQL Server

* feat: Catch Update Exception to throw a 400 response

* chore: Fix formatting

* fix: remove code added to Init migrations

* chore: delete MigrationOperations

* feat: add migration via EntityTypeConfiguration

* chore: simplify ef core scripts by using DatabaseMigrator as startup assembly

* chore: Handle update exception in repository

* fix: correct casing of exception message recognition

---------

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Co-authored-by: Timo Notheisen <[email protected]>
  • Loading branch information
3 people authored Sep 10, 2024
1 parent 0939c07 commit 92c2bc4
Show file tree
Hide file tree
Showing 18 changed files with 2,112 additions and 106 deletions.
4 changes: 0 additions & 4 deletions Applications/AdminApi/src/AdminApi/AdminApi.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,6 @@
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OData" Version="9.0.0" />
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="10.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.8">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="OpenIddict.EntityFrameworkCore" Version="5.8.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.7.3" />
<PackageReference Include="FluentValidation.AspNetCore" Version="11.3.0" />
Expand Down
49 changes: 22 additions & 27 deletions Applications/ConsumerApi/src/ConsumerApi.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,16 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="AspNetCore.HealthChecks.SqlServer" Version="8.0.2" />
<PackageReference Include="FluentValidation.AspNetCore" Version="11.3.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.8" />
<PackageReference Include="NetEscapades.AspNetCore.SecurityHeaders" Version="0.23.0" />
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="10.0.0" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.8" />
<PackageReference Include="ReHackt.Extensions.Options.Validation" Version="8.0.2" />
<PackageReference Include="Serilog.Enrichers.Sensitive" Version="1.7.3" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.7.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="8.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.8">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<TreatAsUsed>true</TreatAsUsed>
</PackageReference>
<PackageReference Include="AspNetCore.HealthChecks.SqlServer" Version="8.0.2"/>
<PackageReference Include="FluentValidation.AspNetCore" Version="11.3.0"/>
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.8"/>
<PackageReference Include="NetEscapades.AspNetCore.SecurityHeaders" Version="0.23.0"/>
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="10.0.0"/>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.8"/>
<PackageReference Include="ReHackt.Extensions.Options.Validation" Version="8.0.2"/>
<PackageReference Include="Serilog.Enrichers.Sensitive" Version="1.7.3"/>
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.7.3"/>
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="8.0.8"/>
<PackageReference Include="Serilog" Version="4.0.1"/>
<PackageReference Include="Serilog.AspNetCore" Version="8.0.2"/>
<PackageReference Include="Serilog.Enrichers.ClientInfo" Version="2.1.1"/>
Expand All @@ -33,20 +28,20 @@
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\BuildingBlocks\src\BuildingBlocks.API\BuildingBlocks.API.csproj" />
<ProjectReference Include="..\..\..\Infrastructure\Infrastructure.csproj" />
<ProjectReference Include="..\..\..\Modules\Challenges\src\Challenges.ConsumerApi\Challenges.ConsumerApi.csproj" />
<ProjectReference Include="..\..\..\Modules\Devices\src\Devices.ConsumerApi\Devices.ConsumerApi.csproj" />
<ProjectReference Include="..\..\..\Modules\Files\src\Files.ConsumerApi\Files.ConsumerApi.csproj" />
<ProjectReference Include="..\..\..\Modules\Messages\src\Messages.ConsumerApi\Messages.ConsumerApi.csproj" />
<ProjectReference Include="..\..\..\Modules\Quotas\src\Quotas.ConsumerApi\Quotas.ConsumerApi.csproj" />
<ProjectReference Include="..\..\..\Modules\Relationships\src\Relationships.ConsumerApi\Relationships.ConsumerApi.csproj" />
<ProjectReference Include="..\..\..\Modules\Synchronization\src\Synchronization.ConsumerApi\Synchronization.ConsumerApi.csproj" />
<ProjectReference Include="..\..\..\Modules\Tokens\src\Tokens.ConsumerApi\Tokens.ConsumerApi.csproj" />
<ProjectReference Include="..\..\..\BuildingBlocks\src\BuildingBlocks.API\BuildingBlocks.API.csproj"/>
<ProjectReference Include="..\..\..\Infrastructure\Infrastructure.csproj"/>
<ProjectReference Include="..\..\..\Modules\Challenges\src\Challenges.ConsumerApi\Challenges.ConsumerApi.csproj"/>
<ProjectReference Include="..\..\..\Modules\Devices\src\Devices.ConsumerApi\Devices.ConsumerApi.csproj"/>
<ProjectReference Include="..\..\..\Modules\Files\src\Files.ConsumerApi\Files.ConsumerApi.csproj"/>
<ProjectReference Include="..\..\..\Modules\Messages\src\Messages.ConsumerApi\Messages.ConsumerApi.csproj"/>
<ProjectReference Include="..\..\..\Modules\Quotas\src\Quotas.ConsumerApi\Quotas.ConsumerApi.csproj"/>
<ProjectReference Include="..\..\..\Modules\Relationships\src\Relationships.ConsumerApi\Relationships.ConsumerApi.csproj"/>
<ProjectReference Include="..\..\..\Modules\Synchronization\src\Synchronization.ConsumerApi\Synchronization.ConsumerApi.csproj"/>
<ProjectReference Include="..\..\..\Modules\Tokens\src\Tokens.ConsumerApi\Tokens.ConsumerApi.csproj"/>
</ItemGroup>

<Target Name="PreBuild" BeforeTargets="Build" Condition="$(Configuration) == Debug">
<Delete Files="$(ProjectDir)appsettings.override.json" />
<Copy SourceFiles="..\..\..\appsettings.override.json" DestinationFolder="$(ProjectDir)" UseHardlinksIfPossible="true" />
<Delete Files="$(ProjectDir)appsettings.override.json"/>
<Copy SourceFiles="..\..\..\appsettings.override.json" DestinationFolder="$(ProjectDir)" UseHardlinksIfPossible="true"/>
</Target>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -6,56 +6,60 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="10.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.0" />
<PackageReference Include="ReHackt.Extensions.Options.Validation" Version="8.0.2" />
<PackageReference Include="Serilog" Version="4.0.1" />
<PackageReference Include="Serilog.Enrichers.Demystifier" Version="1.0.2" />
<PackageReference Include="Serilog.Enrichers.Environment" Version="3.0.1" />
<PackageReference Include="Serilog.Exceptions" Version="8.4.0" />
<PackageReference Include="Serilog.Exceptions.EntityFrameworkCore" Version="8.4.0" />
<PackageReference Include="Serilog.Formatting.Compact" Version="3.0.0" />
<PackageReference Include="Serilog.Settings.Configuration" Version="8.0.2" />
<PackageReference Include="Serilog.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
<PackageReference Include="Serilog.Sinks.Debug" Version="3.0.0" />
<PackageReference Include="Serilog.Sinks.Http" Version="9.0.0" />
<PackageReference Include="Serilog.Sinks.Seq" Version="8.0.0" />
<PackageReference Include="System.Data.SqlClient" Version="4.8.6" />
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="10.0.0"/>
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.8">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0"/>
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.0"/>
<PackageReference Include="ReHackt.Extensions.Options.Validation" Version="8.0.2"/>
<PackageReference Include="Serilog" Version="4.0.1"/>
<PackageReference Include="Serilog.Enrichers.Demystifier" Version="1.0.2"/>
<PackageReference Include="Serilog.Enrichers.Environment" Version="3.0.1"/>
<PackageReference Include="Serilog.Exceptions" Version="8.4.0"/>
<PackageReference Include="Serilog.Exceptions.EntityFrameworkCore" Version="8.4.0"/>
<PackageReference Include="Serilog.Formatting.Compact" Version="3.0.0"/>
<PackageReference Include="Serilog.Settings.Configuration" Version="8.0.2"/>
<PackageReference Include="Serilog.Extensions.Hosting" Version="8.0.0"/>
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0"/>
<PackageReference Include="Serilog.Sinks.Debug" Version="3.0.0"/>
<PackageReference Include="Serilog.Sinks.Http" Version="9.0.0"/>
<PackageReference Include="Serilog.Sinks.Seq" Version="8.0.0"/>
<PackageReference Include="System.Data.SqlClient" Version="4.8.6"/>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\..\Applications\AdminApi\src\AdminApi.Infrastructure.Database.Postgres\AdminApi.Infrastructure.Database.Postgres.csproj" />
<ProjectReference Include="..\..\..\..\Applications\AdminApi\src\AdminApi.Infrastructure.Database.SqlServer\AdminApi.Infrastructure.Database.SqlServer.csproj" />
<ProjectReference Include="..\..\..\..\Modules\Challenges\src\Challenges.Infrastructure.Database.Postgres\Challenges.Infrastructure.Database.Postgres.csproj" />
<ProjectReference Include="..\..\..\..\Modules\Challenges\src\Challenges.Infrastructure.Database.SqlServer\Challenges.Infrastructure.Database.SqlServer.csproj" />
<ProjectReference Include="..\..\..\..\Modules\Challenges\src\Challenges.Infrastructure\Challenges.Infrastructure.csproj" />
<ProjectReference Include="..\..\..\..\Modules\Devices\src\Devices.Infrastructure.Database.Postgres\Devices.Infrastructure.Database.Postgres.csproj" />
<ProjectReference Include="..\..\..\..\Modules\Devices\src\Devices.Infrastructure.Database.SqlServer\Devices.Infrastructure.Database.SqlServer.csproj" />
<ProjectReference Include="..\..\..\..\Modules\Devices\src\Devices.Infrastructure\Devices.Infrastructure.csproj" />
<ProjectReference Include="..\..\..\..\Modules\Files\src\Files.Infrastructure.Database.Postgres\Files.Infrastructure.Database.Postgres.csproj" />
<ProjectReference Include="..\..\..\..\Modules\Files\src\Files.Infrastructure.Database.SqlServer\Files.Infrastructure.Database.SqlServer.csproj" />
<ProjectReference Include="..\..\..\..\Modules\Files\src\Files.Infrastructure\Files.Infrastructure.csproj" />
<ProjectReference Include="..\..\..\..\Modules\Messages\src\Messages.Infrastructure.Database.Postgres\Messages.Infrastructure.Database.Postgres.csproj" />
<ProjectReference Include="..\..\..\..\Modules\Messages\src\Messages.Infrastructure.Database.SqlServer\Messages.Infrastructure.Database.SqlServer.csproj" />
<ProjectReference Include="..\..\..\..\Modules\Messages\src\Messages.Infrastructure\Messages.Infrastructure.csproj" />
<ProjectReference Include="..\..\..\..\Modules\Quotas\src\Quotas.Infrastructure.Database.Postgres\Quotas.Infrastructure.Database.Postgres.csproj" />
<ProjectReference Include="..\..\..\..\Modules\Quotas\src\Quotas.Infrastructure.Database.SqlServer\Quotas.Infrastructure.Database.SqlServer.csproj" />
<ProjectReference Include="..\..\..\..\Modules\Quotas\src\Quotas.Infrastructure\Quotas.Infrastructure.csproj" />
<ProjectReference Include="..\..\..\..\Modules\Relationships\src\Relationships.Infrastructure.Database.Postgres\Relationships.Infrastructure.Database.Postgres.csproj" />
<ProjectReference Include="..\..\..\..\Modules\Relationships\src\Relationships.Infrastructure.Database.SqlServer\Relationships.Infrastructure.Database.SqlServer.csproj" />
<ProjectReference Include="..\..\..\..\Modules\Relationships\src\Relationships.Infrastructure\Relationships.Infrastructure.csproj" />
<ProjectReference Include="..\..\..\..\Modules\Synchronization\src\Synchronization.Infrastructure.Database.Postgres\Synchronization.Infrastructure.Database.Postgres.csproj" />
<ProjectReference Include="..\..\..\..\Modules\Synchronization\src\Synchronization.Infrastructure.Database.SqlServer\Synchronization.Infrastructure.Database.SqlServer.csproj" />
<ProjectReference Include="..\..\..\..\Modules\Synchronization\src\Synchronization.Infrastructure\Synchronization.Infrastructure.csproj" />
<ProjectReference Include="..\..\..\..\Modules\Tokens\src\Tokens.Infrastructure.Database.Postgres\Tokens.Infrastructure.Database.Postgres.csproj" />
<ProjectReference Include="..\..\..\..\Modules\Tokens\src\Tokens.Infrastructure.Database.SqlServer\Tokens.Infrastructure.Database.SqlServer.csproj" />
<ProjectReference Include="..\..\..\..\Modules\Tokens\src\Tokens.Infrastructure\Tokens.Infrastructure.csproj" />
<ProjectReference Include="..\..\..\..\Applications\AdminApi\src\AdminApi.Infrastructure.Database.Postgres\AdminApi.Infrastructure.Database.Postgres.csproj"/>
<ProjectReference Include="..\..\..\..\Applications\AdminApi\src\AdminApi.Infrastructure.Database.SqlServer\AdminApi.Infrastructure.Database.SqlServer.csproj"/>
<ProjectReference Include="..\..\..\..\Modules\Challenges\src\Challenges.Infrastructure.Database.Postgres\Challenges.Infrastructure.Database.Postgres.csproj"/>
<ProjectReference Include="..\..\..\..\Modules\Challenges\src\Challenges.Infrastructure.Database.SqlServer\Challenges.Infrastructure.Database.SqlServer.csproj"/>
<ProjectReference Include="..\..\..\..\Modules\Challenges\src\Challenges.Infrastructure\Challenges.Infrastructure.csproj"/>
<ProjectReference Include="..\..\..\..\Modules\Devices\src\Devices.Infrastructure.Database.Postgres\Devices.Infrastructure.Database.Postgres.csproj"/>
<ProjectReference Include="..\..\..\..\Modules\Devices\src\Devices.Infrastructure.Database.SqlServer\Devices.Infrastructure.Database.SqlServer.csproj"/>
<ProjectReference Include="..\..\..\..\Modules\Devices\src\Devices.Infrastructure\Devices.Infrastructure.csproj"/>
<ProjectReference Include="..\..\..\..\Modules\Files\src\Files.Infrastructure.Database.Postgres\Files.Infrastructure.Database.Postgres.csproj"/>
<ProjectReference Include="..\..\..\..\Modules\Files\src\Files.Infrastructure.Database.SqlServer\Files.Infrastructure.Database.SqlServer.csproj"/>
<ProjectReference Include="..\..\..\..\Modules\Files\src\Files.Infrastructure\Files.Infrastructure.csproj"/>
<ProjectReference Include="..\..\..\..\Modules\Messages\src\Messages.Infrastructure.Database.Postgres\Messages.Infrastructure.Database.Postgres.csproj"/>
<ProjectReference Include="..\..\..\..\Modules\Messages\src\Messages.Infrastructure.Database.SqlServer\Messages.Infrastructure.Database.SqlServer.csproj"/>
<ProjectReference Include="..\..\..\..\Modules\Messages\src\Messages.Infrastructure\Messages.Infrastructure.csproj"/>
<ProjectReference Include="..\..\..\..\Modules\Quotas\src\Quotas.Infrastructure.Database.Postgres\Quotas.Infrastructure.Database.Postgres.csproj"/>
<ProjectReference Include="..\..\..\..\Modules\Quotas\src\Quotas.Infrastructure.Database.SqlServer\Quotas.Infrastructure.Database.SqlServer.csproj"/>
<ProjectReference Include="..\..\..\..\Modules\Quotas\src\Quotas.Infrastructure\Quotas.Infrastructure.csproj"/>
<ProjectReference Include="..\..\..\..\Modules\Relationships\src\Relationships.Infrastructure.Database.Postgres\Relationships.Infrastructure.Database.Postgres.csproj"/>
<ProjectReference Include="..\..\..\..\Modules\Relationships\src\Relationships.Infrastructure.Database.SqlServer\Relationships.Infrastructure.Database.SqlServer.csproj"/>
<ProjectReference Include="..\..\..\..\Modules\Relationships\src\Relationships.Infrastructure\Relationships.Infrastructure.csproj"/>
<ProjectReference Include="..\..\..\..\Modules\Synchronization\src\Synchronization.Infrastructure.Database.Postgres\Synchronization.Infrastructure.Database.Postgres.csproj"/>
<ProjectReference Include="..\..\..\..\Modules\Synchronization\src\Synchronization.Infrastructure.Database.SqlServer\Synchronization.Infrastructure.Database.SqlServer.csproj"/>
<ProjectReference Include="..\..\..\..\Modules\Synchronization\src\Synchronization.Infrastructure\Synchronization.Infrastructure.csproj"/>
<ProjectReference Include="..\..\..\..\Modules\Tokens\src\Tokens.Infrastructure.Database.Postgres\Tokens.Infrastructure.Database.Postgres.csproj"/>
<ProjectReference Include="..\..\..\..\Modules\Tokens\src\Tokens.Infrastructure.Database.SqlServer\Tokens.Infrastructure.Database.SqlServer.csproj"/>
<ProjectReference Include="..\..\..\..\Modules\Tokens\src\Tokens.Infrastructure\Tokens.Infrastructure.csproj"/>
</ItemGroup>

<Target Name="PreBuild" BeforeTargets="Build" Condition="$(Configuration) == Debug">
<Delete Files="$(ProjectDir)appsettings.override.json" />
<Copy SourceFiles="..\..\..\..\appsettings.override.json" DestinationFolder="$(ProjectDir)" UseHardlinksIfPossible="true" />
<Delete Files="$(ProjectDir)appsettings.override.json"/>
<Copy SourceFiles="..\..\..\..\appsettings.override.json" DestinationFolder="$(ProjectDir)" UseHardlinksIfPossible="true"/>
</Target>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public static bool HasReason(this DbUpdateException ex, DbUpdateExceptionReason
{
return reason switch
{
DbUpdateExceptionReason.DuplicateIndex => ex.Message.Contains("Index") || ex.InnerException != null && ex.InnerException.Message.Contains("UNIQUE"),
DbUpdateExceptionReason.DuplicateIndex => ex.Message.Contains("Index") || ex.InnerException != null && ex.InnerException.Message.ToLower().Contains("unique"),
DbUpdateExceptionReason.UniqueKeyViolation => ex.GetBaseException() is SqlException { Number: 2627 } // SqlServer
or PostgresException { SqlState: "23505" },
_ => throw new ArgumentException("The given reason does not exist.")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
using Backbone.BuildingBlocks.Application.Abstractions.Exceptions;
using Backbone.BuildingBlocks.Application.Abstractions.Infrastructure.UserContext;
using Backbone.BuildingBlocks.Application.PushNotifications;
using Backbone.BuildingBlocks.Domain;
using Backbone.BuildingBlocks.Infrastructure.Exceptions;
using Backbone.Modules.Devices.Application.Infrastructure.Persistence.Repository;
using Backbone.Modules.Devices.Application.Infrastructure.PushNotifications.DeletionProcess;
using Backbone.Modules.Devices.Domain;
using Backbone.Modules.Devices.Domain.Entities.Identities;
using MediatR;

Expand All @@ -27,7 +30,14 @@ public async Task<StartDeletionProcessAsOwnerResponse> Handle(StartDeletionProce

var deletionProcess = identity.StartDeletionProcessAsOwner(_userContext.GetDeviceId());

await _identitiesRepository.Update(identity, cancellationToken);
try
{
await _identitiesRepository.Update(identity, cancellationToken);
}
catch (OnlyOneActiveDeletionProcessAllowedException)
{
throw new DomainException(DomainErrors.OnlyOneActiveDeletionProcessAllowed());
}

await _notificationSender.SendNotification(identity.Address, new DeletionProcessStartedPushNotification(), cancellationToken);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace Backbone.Modules.Devices.Application;

public class OnlyOneActiveDeletionProcessAllowedException : Exception
{
private const string MESSAGE = "Only one active deletion process is allowed.";

public OnlyOneActiveDeletionProcessAllowedException() : base(MESSAGE)
{
}

public OnlyOneActiveDeletionProcessAllowedException(Exception innerException) : base(MESSAGE, innerException)
{
}
}
Loading

0 comments on commit 92c2bc4

Please sign in to comment.