diff --git a/Modules/Quotas/src/Quotas.Application/IntegrationEvents/Incoming/TierQuotaDefinitionDeleted/TierQuotaDefinitionDeletedIntegrationEventHandler.cs b/Modules/Quotas/src/Quotas.Application/IntegrationEvents/Incoming/TierQuotaDefinitionDeleted/TierQuotaDefinitionDeletedIntegrationEventHandler.cs index b46065bee5..ddcf76bc18 100644 --- a/Modules/Quotas/src/Quotas.Application/IntegrationEvents/Incoming/TierQuotaDefinitionDeleted/TierQuotaDefinitionDeletedIntegrationEventHandler.cs +++ b/Modules/Quotas/src/Quotas.Application/IntegrationEvents/Incoming/TierQuotaDefinitionDeleted/TierQuotaDefinitionDeletedIntegrationEventHandler.cs @@ -1,5 +1,7 @@ using Backbone.Modules.Quotas.Application.Infrastructure.Persistence.Repository; using Backbone.Modules.Quotas.Application.IntegrationEvents.Outgoing; +using Backbone.Modules.Quotas.Application.Metrics; +using Backbone.Modules.Quotas.Domain.Aggregates.Metrics; using Backbone.Modules.Quotas.Domain.Aggregates.Tiers; using Enmeshed.BuildingBlocks.Application.Abstractions.Infrastructure.EventBus; using Microsoft.Extensions.Logging; @@ -8,38 +10,30 @@ namespace Backbone.Modules.Quotas.Application.IntegrationEvents.Incoming.TierQuo public class TierQuotaDefinitionDeletedIntegrationEventHandler : IIntegrationEventHandler { private readonly IIdentitiesRepository _identitiesRepository; - private readonly ITiersRepository _tiersRepository; + private readonly IMetricStatusesService _metricStatusesService; private readonly ILogger _logger; - public TierQuotaDefinitionDeletedIntegrationEventHandler(IIdentitiesRepository identitiesRepository, - ITiersRepository tiersRepository, ILogger logger) + public TierQuotaDefinitionDeletedIntegrationEventHandler(IIdentitiesRepository identitiesRepository, IMetricStatusesService metricStatusesService, ILogger logger) { + _metricStatusesService = metricStatusesService; _identitiesRepository = identitiesRepository; - _tiersRepository = tiersRepository; _logger = logger; } public async Task Handle(TierQuotaDefinitionDeletedIntegrationEvent @event) { _logger.LogTrace("Handling '{eventName}' ... ", nameof(TierQuotaDefinitionDeletedIntegrationEvent)); + await RecalculateMetricStatuses(@event); + } - var identitiesWithTier = await _identitiesRepository.FindWithTier(new TierId(@event.TierId), CancellationToken.None, true); - - if (!identitiesWithTier.Any()) - { - _logger.LogTrace("No identities found with tier ID: '{tierId}'", @event.TierId); - return; - } - - var tierQuotaDefinition = await _tiersRepository.FindTierQuotaDefinition(@event.TierQuotaDefinitionId, CancellationToken.None, true); - - foreach (var identity in identitiesWithTier) - { - identity.DeleteTierQuotaFromDefinitionId(tierQuotaDefinition.Id); - } - - await _identitiesRepository.Update(identitiesWithTier, CancellationToken.None); + private async Task RecalculateMetricStatuses(TierQuotaDefinitionDeletedIntegrationEvent @event) + { + var identitiesWithTier = await _identitiesRepository.FindWithTier(new TierId(@event.TierId), CancellationToken.None); - _logger.LogTrace("Successfully deleted quotas for Identities."); + await _metricStatusesService.RecalculateMetricStatuses( + identitiesWithTier.Select(i => i.Address).ToList(), + MetricKey.GetSupportedMetricKeyValues().ToList(), + CancellationToken.None + ); } } diff --git a/Modules/Quotas/src/Quotas.Application/Tiers/Commands/DeleteTierQuotaDefinition/Handler.cs b/Modules/Quotas/src/Quotas.Application/Tiers/Commands/DeleteTierQuotaDefinition/Handler.cs index f8d86b39a7..64fe235103 100644 --- a/Modules/Quotas/src/Quotas.Application/Tiers/Commands/DeleteTierQuotaDefinition/Handler.cs +++ b/Modules/Quotas/src/Quotas.Application/Tiers/Commands/DeleteTierQuotaDefinition/Handler.cs @@ -23,8 +23,6 @@ public Handler(ITiersRepository tiersRepository, ILogger logger, IEvent public async Task Handle(DeleteTierQuotaDefinitionCommand request, CancellationToken cancellationToken) { - _logger.LogTrace("Deleting tier quota definition with id: '{tierQuotaDefinitionId}'.", request.TierQuotaDefinitionId); - var tier = await _tiersRepository.Find(request.TierId, cancellationToken, true) ?? throw new NotFoundException(nameof(Tier)); var result = tier.DeleteQuota(request.TierQuotaDefinitionId); diff --git a/Modules/Quotas/src/Quotas.Domain/AssemblyInfo.cs b/Modules/Quotas/src/Quotas.Domain/AssemblyInfo.cs index f887ecf544..610b33381f 100644 --- a/Modules/Quotas/src/Quotas.Domain/AssemblyInfo.cs +++ b/Modules/Quotas/src/Quotas.Domain/AssemblyInfo.cs @@ -2,3 +2,4 @@ [assembly: InternalsVisibleTo("Backbone.Modules.Quotas.Domain.Tests")] [assembly: InternalsVisibleTo("Backbone.Modules.Quotas.Application.Tests")] +[assembly: InternalsVisibleTo("Backbone.Modules.Quotas.Infrastructure")] diff --git a/Modules/Quotas/src/Quotas.Infrastructure.Database.Postgres/Migrations/20231012092205_TiersTierQuotasTierQuotaDefinitionsCascades.Designer.cs b/Modules/Quotas/src/Quotas.Infrastructure.Database.Postgres/Migrations/20231012092205_TiersTierQuotasTierQuotaDefinitionsCascades.Designer.cs new file mode 100644 index 0000000000..33ad08c413 --- /dev/null +++ b/Modules/Quotas/src/Quotas.Infrastructure.Database.Postgres/Migrations/20231012092205_TiersTierQuotasTierQuotaDefinitionsCascades.Designer.cs @@ -0,0 +1,350 @@ +// +using System; +using Backbone.Modules.Quotas.Infrastructure.Persistence.Database; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Backbone.Modules.Quotas.Infrastructure.Database.Postgres.Migrations +{ + [DbContext(typeof(QuotasDbContext))] + [Migration("20231012092205_TiersTierQuotasTierQuotaDefinitionsCascades")] + partial class TiersTierQuotasTierQuotaDefinitionsCascades + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Backbone.Modules.Quotas.Domain.Aggregates.FileMetadata.FileMetadata", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("CipherSize") + .HasColumnType("bigint"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("FileMetadata", "Files", t => + { + t.ExcludeFromMigrations(); + }); + }); + + modelBuilder.Entity("Backbone.Modules.Quotas.Domain.Aggregates.Identities.Identity", b => + { + b.Property("Address") + .HasMaxLength(36) + .IsUnicode(false) + .HasColumnType("character(36)") + .IsFixedLength(); + + b.Property("TierId") + .IsRequired() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("character(20)") + .IsFixedLength(); + + b.HasKey("Address"); + + b.HasIndex("TierId"); + + b.ToTable("Identities"); + }); + + modelBuilder.Entity("Backbone.Modules.Quotas.Domain.Aggregates.Identities.IndividualQuota", b => + { + b.Property("Id") + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("character(20)") + .IsFixedLength(); + + b.Property("ApplyTo") + .IsRequired() + .HasColumnType("character(36)"); + + b.Property("Max") + .HasColumnType("integer"); + + b.Property("MetricKey") + .IsRequired() + .HasMaxLength(50) + .IsUnicode(true) + .HasColumnType("character varying(50)") + .IsFixedLength(false); + + b.Property("Period") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("ApplyTo"); + + b.ToTable("IndividualQuotas", (string)null); + }); + + modelBuilder.Entity("Backbone.Modules.Quotas.Domain.Aggregates.Identities.MetricStatus", b => + { + b.Property("Owner") + .HasColumnType("character(36)"); + + b.Property("MetricKey") + .HasMaxLength(50) + .IsUnicode(true) + .HasColumnType("character varying(50)") + .IsFixedLength(false); + + b.Property("IsExhaustedUntil") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Owner", "MetricKey"); + + b.ToTable("MetricStatuses", (string)null); + }); + + modelBuilder.Entity("Backbone.Modules.Quotas.Domain.Aggregates.Identities.TierQuota", b => + { + b.Property("Id") + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("character(20)") + .IsFixedLength(); + + b.Property("ApplyTo") + .IsRequired() + .HasColumnType("character(36)"); + + b.Property("_definitionId") + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("character(20)") + .HasColumnName("DefinitionId") + .IsFixedLength(); + + b.HasKey("Id"); + + b.HasIndex("ApplyTo"); + + b.HasIndex("_definitionId"); + + b.ToTable("TierQuotas", (string)null); + }); + + modelBuilder.Entity("Backbone.Modules.Quotas.Domain.Aggregates.Messages.Message", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Messages", "Messages", t => + { + t.ExcludeFromMigrations(); + }); + }); + + modelBuilder.Entity("Backbone.Modules.Quotas.Domain.Aggregates.Relationships.Relationship", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("From") + .IsRequired() + .HasColumnType("text"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("To") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Relationships", "Relationships", t => + { + t.ExcludeFromMigrations(); + }); + }); + + modelBuilder.Entity("Backbone.Modules.Quotas.Domain.Aggregates.Relationships.RelationshipTemplate", b => + { + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.ToTable("RelationshipTemplates", "Relationships", t => + { + t.ExcludeFromMigrations(); + }); + }); + + modelBuilder.Entity("Backbone.Modules.Quotas.Domain.Aggregates.Tiers.Tier", b => + { + b.Property("Id") + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("character(20)") + .IsFixedLength(); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .IsUnicode(true) + .HasColumnType("character varying(30)") + .IsFixedLength(false); + + b.HasKey("Id"); + + b.ToTable("Tiers"); + }); + + modelBuilder.Entity("Backbone.Modules.Quotas.Domain.Aggregates.Tiers.TierQuotaDefinition", b => + { + b.Property("Id") + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("character(20)") + .IsFixedLength(); + + b.Property("Max") + .HasColumnType("integer"); + + b.Property("MetricKey") + .IsRequired() + .HasMaxLength(50) + .IsUnicode(true) + .HasColumnType("character varying(50)") + .IsFixedLength(false); + + b.Property("Period") + .HasColumnType("integer"); + + b.Property("TierId") + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("character(20)") + .IsFixedLength(); + + b.HasKey("Id"); + + b.HasIndex("TierId"); + + b.ToTable("TierQuotaDefinitions", (string)null); + }); + + modelBuilder.Entity("Backbone.Modules.Quotas.Domain.Aggregates.Tokens.Token", b => + { + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.ToTable("Tokens", "Tokens", t => + { + t.ExcludeFromMigrations(); + }); + }); + + modelBuilder.Entity("Backbone.Modules.Quotas.Domain.Aggregates.Identities.Identity", b => + { + b.HasOne("Backbone.Modules.Quotas.Domain.Aggregates.Tiers.Tier", null) + .WithMany() + .HasForeignKey("TierId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + }); + + modelBuilder.Entity("Backbone.Modules.Quotas.Domain.Aggregates.Identities.IndividualQuota", b => + { + b.HasOne("Backbone.Modules.Quotas.Domain.Aggregates.Identities.Identity", null) + .WithMany("IndividualQuotas") + .HasForeignKey("ApplyTo") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Backbone.Modules.Quotas.Domain.Aggregates.Identities.MetricStatus", b => + { + b.HasOne("Backbone.Modules.Quotas.Domain.Aggregates.Identities.Identity", null) + .WithMany("MetricStatuses") + .HasForeignKey("Owner") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Backbone.Modules.Quotas.Domain.Aggregates.Identities.TierQuota", b => + { + b.HasOne("Backbone.Modules.Quotas.Domain.Aggregates.Identities.Identity", null) + .WithMany("TierQuotas") + .HasForeignKey("ApplyTo") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Backbone.Modules.Quotas.Domain.Aggregates.Tiers.TierQuotaDefinition", "_definition") + .WithMany() + .HasForeignKey("_definitionId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("_definition"); + }); + + modelBuilder.Entity("Backbone.Modules.Quotas.Domain.Aggregates.Tiers.TierQuotaDefinition", b => + { + b.HasOne("Backbone.Modules.Quotas.Domain.Aggregates.Tiers.Tier", null) + .WithMany("Quotas") + .HasForeignKey("TierId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Backbone.Modules.Quotas.Domain.Aggregates.Identities.Identity", b => + { + b.Navigation("IndividualQuotas"); + + b.Navigation("MetricStatuses"); + + b.Navigation("TierQuotas"); + }); + + modelBuilder.Entity("Backbone.Modules.Quotas.Domain.Aggregates.Tiers.Tier", b => + { + b.Navigation("Quotas"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Modules/Quotas/src/Quotas.Infrastructure.Database.Postgres/Migrations/20231012092205_TiersTierQuotasTierQuotaDefinitionsCascades.cs b/Modules/Quotas/src/Quotas.Infrastructure.Database.Postgres/Migrations/20231012092205_TiersTierQuotasTierQuotaDefinitionsCascades.cs new file mode 100644 index 0000000000..4a7e587024 --- /dev/null +++ b/Modules/Quotas/src/Quotas.Infrastructure.Database.Postgres/Migrations/20231012092205_TiersTierQuotasTierQuotaDefinitionsCascades.cs @@ -0,0 +1,87 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Backbone.Modules.Quotas.Infrastructure.Database.Postgres.Migrations +{ + /// + public partial class TiersTierQuotasTierQuotaDefinitionsCascades : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Identities_Tiers_TierId", + table: "Identities"); + + migrationBuilder.DropForeignKey( + name: "FK_TierQuotaDefinitions_Tiers_TierId", + table: "TierQuotaDefinitions"); + + migrationBuilder.DropForeignKey( + name: "FK_TierQuotas_TierQuotaDefinitions_DefinitionId", + table: "TierQuotas"); + + migrationBuilder.AddForeignKey( + name: "FK_Identities_Tiers_TierId", + table: "Identities", + column: "TierId", + principalTable: "Tiers", + principalColumn: "Id"); + + migrationBuilder.AddForeignKey( + name: "FK_TierQuotaDefinitions_Tiers_TierId", + table: "TierQuotaDefinitions", + column: "TierId", + principalTable: "Tiers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + name: "FK_TierQuotas_TierQuotaDefinitions_DefinitionId", + table: "TierQuotas", + column: "DefinitionId", + principalTable: "TierQuotaDefinitions", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Identities_Tiers_TierId", + table: "Identities"); + + migrationBuilder.DropForeignKey( + name: "FK_TierQuotaDefinitions_Tiers_TierId", + table: "TierQuotaDefinitions"); + + migrationBuilder.DropForeignKey( + name: "FK_TierQuotas_TierQuotaDefinitions_DefinitionId", + table: "TierQuotas"); + + migrationBuilder.AddForeignKey( + name: "FK_Identities_Tiers_TierId", + table: "Identities", + column: "TierId", + principalTable: "Tiers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + name: "FK_TierQuotaDefinitions_Tiers_TierId", + table: "TierQuotaDefinitions", + column: "TierId", + principalTable: "Tiers", + principalColumn: "Id"); + + migrationBuilder.AddForeignKey( + name: "FK_TierQuotas_TierQuotaDefinitions_DefinitionId", + table: "TierQuotas", + column: "DefinitionId", + principalTable: "TierQuotaDefinitions", + principalColumn: "Id"); + } + } +} diff --git a/Modules/Quotas/src/Quotas.Infrastructure.Database.Postgres/Migrations/QuotasDbContextModelSnapshot.cs b/Modules/Quotas/src/Quotas.Infrastructure.Database.Postgres/Migrations/QuotasDbContextModelSnapshot.cs index e8144bff7b..bb9f52a290 100644 --- a/Modules/Quotas/src/Quotas.Infrastructure.Database.Postgres/Migrations/QuotasDbContextModelSnapshot.cs +++ b/Modules/Quotas/src/Quotas.Infrastructure.Database.Postgres/Migrations/QuotasDbContextModelSnapshot.cs @@ -17,7 +17,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "7.0.10") + .HasAnnotation("ProductVersion", "7.0.11") .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); @@ -282,7 +282,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasOne("Backbone.Modules.Quotas.Domain.Aggregates.Tiers.Tier", null) .WithMany() .HasForeignKey("TierId") - .OnDelete(DeleteBehavior.Cascade) + .OnDelete(DeleteBehavior.NoAction) .IsRequired(); }); @@ -314,7 +314,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasOne("Backbone.Modules.Quotas.Domain.Aggregates.Tiers.TierQuotaDefinition", "_definition") .WithMany() - .HasForeignKey("_definitionId"); + .HasForeignKey("_definitionId") + .OnDelete(DeleteBehavior.Cascade); b.Navigation("_definition"); }); @@ -323,7 +324,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) { b.HasOne("Backbone.Modules.Quotas.Domain.Aggregates.Tiers.Tier", null) .WithMany("Quotas") - .HasForeignKey("TierId"); + .HasForeignKey("TierId") + .OnDelete(DeleteBehavior.Cascade); }); modelBuilder.Entity("Backbone.Modules.Quotas.Domain.Aggregates.Identities.Identity", b => diff --git a/Modules/Quotas/src/Quotas.Infrastructure.Database.SqlServer/Migrations/20231011153533_TiersTierQuotasTierQuotaDefinitionsCascades.Designer.cs b/Modules/Quotas/src/Quotas.Infrastructure.Database.SqlServer/Migrations/20231011153533_TiersTierQuotasTierQuotaDefinitionsCascades.Designer.cs new file mode 100644 index 0000000000..40ce719c23 --- /dev/null +++ b/Modules/Quotas/src/Quotas.Infrastructure.Database.SqlServer/Migrations/20231011153533_TiersTierQuotasTierQuotaDefinitionsCascades.Designer.cs @@ -0,0 +1,350 @@ +// +using System; +using Backbone.Modules.Quotas.Infrastructure.Persistence.Database; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Backbone.Modules.Quotas.Infrastructure.Database.SqlServer.Migrations +{ + [DbContext(typeof(QuotasDbContext))] + [Migration("20231011153533_TiersTierQuotasTierQuotaDefinitionsCascades")] + partial class TiersTierQuotasTierQuotaDefinitionsCascades + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Backbone.Modules.Quotas.Domain.Aggregates.FileMetadata.FileMetadata", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("CipherSize") + .HasColumnType("bigint"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("FileMetadata", "Files", t => + { + t.ExcludeFromMigrations(); + }); + }); + + modelBuilder.Entity("Backbone.Modules.Quotas.Domain.Aggregates.Identities.Identity", b => + { + b.Property("Address") + .HasMaxLength(36) + .IsUnicode(false) + .HasColumnType("char(36)") + .IsFixedLength(); + + b.Property("TierId") + .IsRequired() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("char(20)") + .IsFixedLength(); + + b.HasKey("Address"); + + b.HasIndex("TierId"); + + b.ToTable("Identities"); + }); + + modelBuilder.Entity("Backbone.Modules.Quotas.Domain.Aggregates.Identities.IndividualQuota", b => + { + b.Property("Id") + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("char(20)") + .IsFixedLength(); + + b.Property("ApplyTo") + .IsRequired() + .HasColumnType("char(36)"); + + b.Property("Max") + .HasColumnType("int"); + + b.Property("MetricKey") + .IsRequired() + .HasMaxLength(50) + .IsUnicode(true) + .HasColumnType("nvarchar(50)") + .IsFixedLength(false); + + b.Property("Period") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ApplyTo"); + + b.ToTable("IndividualQuotas", (string)null); + }); + + modelBuilder.Entity("Backbone.Modules.Quotas.Domain.Aggregates.Identities.MetricStatus", b => + { + b.Property("Owner") + .HasColumnType("char(36)"); + + b.Property("MetricKey") + .HasMaxLength(50) + .IsUnicode(true) + .HasColumnType("nvarchar(50)") + .IsFixedLength(false); + + b.Property("IsExhaustedUntil") + .HasColumnType("datetime2"); + + b.HasKey("Owner", "MetricKey"); + + b.ToTable("MetricStatuses", (string)null); + }); + + modelBuilder.Entity("Backbone.Modules.Quotas.Domain.Aggregates.Identities.TierQuota", b => + { + b.Property("Id") + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("char(20)") + .IsFixedLength(); + + b.Property("ApplyTo") + .IsRequired() + .HasColumnType("char(36)"); + + b.Property("_definitionId") + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("char(20)") + .HasColumnName("DefinitionId") + .IsFixedLength(); + + b.HasKey("Id"); + + b.HasIndex("ApplyTo"); + + b.HasIndex("_definitionId"); + + b.ToTable("TierQuotas", (string)null); + }); + + modelBuilder.Entity("Backbone.Modules.Quotas.Domain.Aggregates.Messages.Message", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("Messages", "Messages", t => + { + t.ExcludeFromMigrations(); + }); + }); + + modelBuilder.Entity("Backbone.Modules.Quotas.Domain.Aggregates.Relationships.Relationship", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("From") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("To") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("Relationships", "Relationships", t => + { + t.ExcludeFromMigrations(); + }); + }); + + modelBuilder.Entity("Backbone.Modules.Quotas.Domain.Aggregates.Relationships.RelationshipTemplate", b => + { + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.ToTable("RelationshipTemplates", "Relationships", t => + { + t.ExcludeFromMigrations(); + }); + }); + + modelBuilder.Entity("Backbone.Modules.Quotas.Domain.Aggregates.Tiers.Tier", b => + { + b.Property("Id") + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("char(20)") + .IsFixedLength(); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .IsUnicode(true) + .HasColumnType("nvarchar(30)") + .IsFixedLength(false); + + b.HasKey("Id"); + + b.ToTable("Tiers"); + }); + + modelBuilder.Entity("Backbone.Modules.Quotas.Domain.Aggregates.Tiers.TierQuotaDefinition", b => + { + b.Property("Id") + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("char(20)") + .IsFixedLength(); + + b.Property("Max") + .HasColumnType("int"); + + b.Property("MetricKey") + .IsRequired() + .HasMaxLength(50) + .IsUnicode(true) + .HasColumnType("nvarchar(50)") + .IsFixedLength(false); + + b.Property("Period") + .HasColumnType("int"); + + b.Property("TierId") + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("char(20)") + .IsFixedLength(); + + b.HasKey("Id"); + + b.HasIndex("TierId"); + + b.ToTable("TierQuotaDefinitions", (string)null); + }); + + modelBuilder.Entity("Backbone.Modules.Quotas.Domain.Aggregates.Tokens.Token", b => + { + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.ToTable("Tokens", "Tokens", t => + { + t.ExcludeFromMigrations(); + }); + }); + + modelBuilder.Entity("Backbone.Modules.Quotas.Domain.Aggregates.Identities.Identity", b => + { + b.HasOne("Backbone.Modules.Quotas.Domain.Aggregates.Tiers.Tier", null) + .WithMany() + .HasForeignKey("TierId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + }); + + modelBuilder.Entity("Backbone.Modules.Quotas.Domain.Aggregates.Identities.IndividualQuota", b => + { + b.HasOne("Backbone.Modules.Quotas.Domain.Aggregates.Identities.Identity", null) + .WithMany("IndividualQuotas") + .HasForeignKey("ApplyTo") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Backbone.Modules.Quotas.Domain.Aggregates.Identities.MetricStatus", b => + { + b.HasOne("Backbone.Modules.Quotas.Domain.Aggregates.Identities.Identity", null) + .WithMany("MetricStatuses") + .HasForeignKey("Owner") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Backbone.Modules.Quotas.Domain.Aggregates.Identities.TierQuota", b => + { + b.HasOne("Backbone.Modules.Quotas.Domain.Aggregates.Identities.Identity", null) + .WithMany("TierQuotas") + .HasForeignKey("ApplyTo") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Backbone.Modules.Quotas.Domain.Aggregates.Tiers.TierQuotaDefinition", "_definition") + .WithMany() + .HasForeignKey("_definitionId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("_definition"); + }); + + modelBuilder.Entity("Backbone.Modules.Quotas.Domain.Aggregates.Tiers.TierQuotaDefinition", b => + { + b.HasOne("Backbone.Modules.Quotas.Domain.Aggregates.Tiers.Tier", null) + .WithMany("Quotas") + .HasForeignKey("TierId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Backbone.Modules.Quotas.Domain.Aggregates.Identities.Identity", b => + { + b.Navigation("IndividualQuotas"); + + b.Navigation("MetricStatuses"); + + b.Navigation("TierQuotas"); + }); + + modelBuilder.Entity("Backbone.Modules.Quotas.Domain.Aggregates.Tiers.Tier", b => + { + b.Navigation("Quotas"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Modules/Quotas/src/Quotas.Infrastructure.Database.SqlServer/Migrations/20231011153533_TiersTierQuotasTierQuotaDefinitionsCascades.cs b/Modules/Quotas/src/Quotas.Infrastructure.Database.SqlServer/Migrations/20231011153533_TiersTierQuotasTierQuotaDefinitionsCascades.cs new file mode 100644 index 0000000000..934791280f --- /dev/null +++ b/Modules/Quotas/src/Quotas.Infrastructure.Database.SqlServer/Migrations/20231011153533_TiersTierQuotasTierQuotaDefinitionsCascades.cs @@ -0,0 +1,87 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Backbone.Modules.Quotas.Infrastructure.Database.SqlServer.Migrations +{ + /// + public partial class TiersTierQuotasTierQuotaDefinitionsCascades : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Identities_Tiers_TierId", + table: "Identities"); + + migrationBuilder.DropForeignKey( + name: "FK_TierQuotaDefinitions_Tiers_TierId", + table: "TierQuotaDefinitions"); + + migrationBuilder.DropForeignKey( + name: "FK_TierQuotas_TierQuotaDefinitions_DefinitionId", + table: "TierQuotas"); + + migrationBuilder.AddForeignKey( + name: "FK_Identities_Tiers_TierId", + table: "Identities", + column: "TierId", + principalTable: "Tiers", + principalColumn: "Id"); + + migrationBuilder.AddForeignKey( + name: "FK_TierQuotaDefinitions_Tiers_TierId", + table: "TierQuotaDefinitions", + column: "TierId", + principalTable: "Tiers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + name: "FK_TierQuotas_TierQuotaDefinitions_DefinitionId", + table: "TierQuotas", + column: "DefinitionId", + principalTable: "TierQuotaDefinitions", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Identities_Tiers_TierId", + table: "Identities"); + + migrationBuilder.DropForeignKey( + name: "FK_TierQuotaDefinitions_Tiers_TierId", + table: "TierQuotaDefinitions"); + + migrationBuilder.DropForeignKey( + name: "FK_TierQuotas_TierQuotaDefinitions_DefinitionId", + table: "TierQuotas"); + + migrationBuilder.AddForeignKey( + name: "FK_Identities_Tiers_TierId", + table: "Identities", + column: "TierId", + principalTable: "Tiers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + name: "FK_TierQuotaDefinitions_Tiers_TierId", + table: "TierQuotaDefinitions", + column: "TierId", + principalTable: "Tiers", + principalColumn: "Id"); + + migrationBuilder.AddForeignKey( + name: "FK_TierQuotas_TierQuotaDefinitions_DefinitionId", + table: "TierQuotas", + column: "DefinitionId", + principalTable: "TierQuotaDefinitions", + principalColumn: "Id"); + } + } +} diff --git a/Modules/Quotas/src/Quotas.Infrastructure.Database.SqlServer/Migrations/QuotasDbContextModelSnapshot.cs b/Modules/Quotas/src/Quotas.Infrastructure.Database.SqlServer/Migrations/QuotasDbContextModelSnapshot.cs index 809b4a44ed..b545d5dfa1 100644 --- a/Modules/Quotas/src/Quotas.Infrastructure.Database.SqlServer/Migrations/QuotasDbContextModelSnapshot.cs +++ b/Modules/Quotas/src/Quotas.Infrastructure.Database.SqlServer/Migrations/QuotasDbContextModelSnapshot.cs @@ -17,7 +17,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "7.0.10") + .HasAnnotation("ProductVersion", "7.0.11") .HasAnnotation("Relational:MaxIdentifierLength", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); @@ -282,7 +282,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasOne("Backbone.Modules.Quotas.Domain.Aggregates.Tiers.Tier", null) .WithMany() .HasForeignKey("TierId") - .OnDelete(DeleteBehavior.Cascade) + .OnDelete(DeleteBehavior.NoAction) .IsRequired(); }); @@ -314,7 +314,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasOne("Backbone.Modules.Quotas.Domain.Aggregates.Tiers.TierQuotaDefinition", "_definition") .WithMany() - .HasForeignKey("_definitionId"); + .HasForeignKey("_definitionId") + .OnDelete(DeleteBehavior.Cascade); b.Navigation("_definition"); }); @@ -323,7 +324,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) { b.HasOne("Backbone.Modules.Quotas.Domain.Aggregates.Tiers.Tier", null) .WithMany("Quotas") - .HasForeignKey("TierId"); + .HasForeignKey("TierId") + .OnDelete(DeleteBehavior.Cascade); }); modelBuilder.Entity("Backbone.Modules.Quotas.Domain.Aggregates.Identities.Identity", b => diff --git a/Modules/Quotas/src/Quotas.Infrastructure/Persistence/Database/EntityConfigurations/IdentityEntityTypeConfiguration.cs b/Modules/Quotas/src/Quotas.Infrastructure/Persistence/Database/EntityConfigurations/IdentityEntityTypeConfiguration.cs index 124f646e8f..e1244449ad 100644 --- a/Modules/Quotas/src/Quotas.Infrastructure/Persistence/Database/EntityConfigurations/IdentityEntityTypeConfiguration.cs +++ b/Modules/Quotas/src/Quotas.Infrastructure/Persistence/Database/EntityConfigurations/IdentityEntityTypeConfiguration.cs @@ -11,9 +11,9 @@ public class IdentityEntityTypeConfiguration : IEntityTypeConfiguration builder) { builder.HasKey(x => x.Address); - builder.HasOne().WithMany().HasForeignKey(x => x.TierId); - builder.HasMany(x => x.TierQuotas).WithOne().HasForeignKey(x => x.ApplyTo); - builder.HasMany(x => x.IndividualQuotas).WithOne().HasForeignKey(x => x.ApplyTo); + builder.HasOne().WithMany().HasForeignKey(x => x.TierId).OnDelete(DeleteBehavior.NoAction); + builder.HasMany(x => x.TierQuotas).WithOne().HasForeignKey(x => x.ApplyTo); + builder.HasMany(x => x.IndividualQuotas).WithOne().HasForeignKey(x => x.ApplyTo); builder.Property(x => x.Address).IsUnicode(false).IsFixedLength().HasMaxLength(IdentityAddress.MAX_LENGTH); builder.Property(x => x.TierId).IsUnicode(false).IsFixedLength().HasMaxLength(20); } diff --git a/Modules/Quotas/src/Quotas.Infrastructure/Persistence/Database/EntityConfigurations/TierEntityTypeConfiguration.cs b/Modules/Quotas/src/Quotas.Infrastructure/Persistence/Database/EntityConfigurations/TierEntityTypeConfiguration.cs index 924b75d85e..3f67b56e18 100644 --- a/Modules/Quotas/src/Quotas.Infrastructure/Persistence/Database/EntityConfigurations/TierEntityTypeConfiguration.cs +++ b/Modules/Quotas/src/Quotas.Infrastructure/Persistence/Database/EntityConfigurations/TierEntityTypeConfiguration.cs @@ -9,5 +9,6 @@ public void Configure(EntityTypeBuilder builder) { builder.Property(x => x.Id); builder.Property(x => x.Name).IsUnicode(true).IsFixedLength(false).HasMaxLength(30); + builder.HasMany(x => x.Quotas).WithOne().OnDelete(DeleteBehavior.Cascade); } } diff --git a/Modules/Quotas/src/Quotas.Infrastructure/Persistence/Database/EntityConfigurations/TierQuotaEntityTypeConfiguration.cs b/Modules/Quotas/src/Quotas.Infrastructure/Persistence/Database/EntityConfigurations/TierQuotaEntityTypeConfiguration.cs index 8cfc88715b..2244606d38 100644 --- a/Modules/Quotas/src/Quotas.Infrastructure/Persistence/Database/EntityConfigurations/TierQuotaEntityTypeConfiguration.cs +++ b/Modules/Quotas/src/Quotas.Infrastructure/Persistence/Database/EntityConfigurations/TierQuotaEntityTypeConfiguration.cs @@ -11,7 +11,7 @@ public void Configure(EntityTypeBuilder builder) builder.HasKey(x => x.Id); builder.ToTable($"{nameof(TierQuota)}s"); builder.Ignore(x => x.Weight); - builder.HasOne("_definition"); + builder.HasOne("_definition").WithMany().OnDelete(DeleteBehavior.Cascade); builder.Property("_definitionId").HasColumnName("DefinitionId"); builder.Ignore(x => x.DefinitionId); } diff --git a/Modules/Quotas/src/Quotas.Infrastructure/Persistence/Repository/TiersRepository .cs b/Modules/Quotas/src/Quotas.Infrastructure/Persistence/Repository/TiersRepository.cs similarity index 96% rename from Modules/Quotas/src/Quotas.Infrastructure/Persistence/Repository/TiersRepository .cs rename to Modules/Quotas/src/Quotas.Infrastructure/Persistence/Repository/TiersRepository.cs index 84d8afbf0d..586ebb8c64 100644 --- a/Modules/Quotas/src/Quotas.Infrastructure/Persistence/Repository/TiersRepository .cs +++ b/Modules/Quotas/src/Quotas.Infrastructure/Persistence/Repository/TiersRepository.cs @@ -3,6 +3,8 @@ using Backbone.Modules.Quotas.Infrastructure.Persistence.Database; using Backbone.Modules.Quotas.Infrastructure.Persistence.Database.QueryableExtensions; using Enmeshed.BuildingBlocks.Application.Extensions; +using Enmeshed.BuildingBlocks.Domain; +using Enmeshed.BuildingBlocks.Domain.Errors; using Microsoft.EntityFrameworkCore; namespace Backbone.Modules.Quotas.Infrastructure.Persistence.Repository; diff --git a/Modules/Quotas/test/Quotas.Application.Tests/Tests/Identities/GetIdentity/HandlerTests.cs b/Modules/Quotas/test/Quotas.Application.Tests/Tests/Identities/GetIdentity/HandlerTests.cs index ca091e8ff7..bbf80f21d6 100644 --- a/Modules/Quotas/test/Quotas.Application.Tests/Tests/Identities/GetIdentity/HandlerTests.cs +++ b/Modules/Quotas/test/Quotas.Application.Tests/Tests/Identities/GetIdentity/HandlerTests.cs @@ -55,7 +55,7 @@ public async void Gets_identity_quotas_by_address() } [Fact] - public async void Fails_when_no_identity_found() + public void Fails_when_no_identity_found() { // Arrange var metricsRepository = A.Fake(); diff --git a/Modules/Quotas/test/Quotas.Application.Tests/Tests/Identities/TierQuotaDefinitionDeletedIntegrationEventHandlerTests.cs b/Modules/Quotas/test/Quotas.Application.Tests/Tests/Identities/TierQuotaDefinitionDeletedIntegrationEventHandlerTests.cs deleted file mode 100644 index 71780e9d14..0000000000 --- a/Modules/Quotas/test/Quotas.Application.Tests/Tests/Identities/TierQuotaDefinitionDeletedIntegrationEventHandlerTests.cs +++ /dev/null @@ -1,52 +0,0 @@ -using Backbone.Modules.Quotas.Application.Infrastructure.Persistence.Repository; -using Backbone.Modules.Quotas.Application.IntegrationEvents.Incoming.TierQuotaDefinitionDeleted; -using Backbone.Modules.Quotas.Application.IntegrationEvents.Outgoing; -using Backbone.Modules.Quotas.Application.Tests.TestDoubles; -using Backbone.Modules.Quotas.Domain.Aggregates.Identities; -using Backbone.Modules.Quotas.Domain.Aggregates.Metrics; -using Backbone.Modules.Quotas.Domain.Aggregates.Tiers; -using FakeItEasy; -using Microsoft.Extensions.Logging; -using Xunit; - -namespace Backbone.Modules.Quotas.Application.Tests.Tests.Identities; -public class TierQuotaDefinitionDeletedIntegrationEventHandlerTests -{ - [Fact] - public async void Deletes_tier_quota_after_consuming_integration_event() - { - // Arrange - var tierId = new TierId("SomeTierId"); - var tier = new Tier(tierId, "some-tier-name"); - - var tierQuotaDefinition = new TierQuotaDefinition(MetricKey.NumberOfSentMessages, 5, QuotaPeriod.Month); - var tierQuotaDefinitionsRepository = new FindTierQuotaDefinitionsStubRepository(tierQuotaDefinition); - - var firstIdentity = new Identity("some-identity-address-one", tierId); - firstIdentity.AssignTierQuotaFromDefinition(tierQuotaDefinition); - - var secondIdentity = new Identity("some-identity-address-two", tierId); - secondIdentity.AssignTierQuotaFromDefinition(tierQuotaDefinition); - - var identities = new List { firstIdentity, secondIdentity }; - var identitiesRepository = A.Fake(); - A.CallTo(() => identitiesRepository.FindWithTier(tierId, CancellationToken.None, true)).Returns(identities); - - var handler = CreateHandler(identitiesRepository, tierQuotaDefinitionsRepository); - - // Act - await handler.Handle(new TierQuotaDefinitionDeletedIntegrationEvent(tier.Id, tierQuotaDefinition.Id)); - - // Assert - A.CallTo(() => identitiesRepository.Update(A>.That.Matches(ids => - ids.All(i => i.TierQuotas.Count == 0)) - , CancellationToken.None) - ).MustHaveHappened(); - } - - private TierQuotaDefinitionDeletedIntegrationEventHandler CreateHandler(IIdentitiesRepository identities, FindTierQuotaDefinitionsStubRepository tierQuotaDefinitions) - { - var logger = A.Fake>(); - return new TierQuotaDefinitionDeletedIntegrationEventHandler(identities, tierQuotaDefinitions, logger); - } -} diff --git a/Modules/Quotas/test/Quotas.Application.Tests/Tests/Quotas/DeleteTierQuotaDefinition/HandlerTests.cs b/Modules/Quotas/test/Quotas.Application.Tests/Tests/Quotas/DeleteTierQuotaDefinition/HandlerTests.cs index 64c207fa8b..12c7f7c0b0 100644 --- a/Modules/Quotas/test/Quotas.Application.Tests/Tests/Quotas/DeleteTierQuotaDefinition/HandlerTests.cs +++ b/Modules/Quotas/test/Quotas.Application.Tests/Tests/Quotas/DeleteTierQuotaDefinition/HandlerTests.cs @@ -26,6 +26,29 @@ public HandlerTests() AssertionScope.Current.FormattingOptions.MaxLines = 1000; } + [Fact] + public async Task Triggers_TierQuotaDefinitionDeletedIntegrationEvent() + { + // Arrange + var tierId = new TierId("SomeTierId"); + var tier = new Tier(tierId, "some-tier-name"); + + tier.CreateQuota(MetricKey.NumberOfSentMessages, 5, QuotaPeriod.Month); + + var command = new DeleteTierQuotaDefinitionCommand(tier.Id, tier.Quotas.First().Id); + + var tiersRepository = A.Fake(); + A.CallTo(() => tiersRepository.Find(tierId, A._, A._)).Returns(tier); + + var handler = CreateHandler(tiersRepository); + + // Act + await handler.Handle(command, CancellationToken.None); + + // Assert + A.CallTo(() => _eventBus.Publish(A.That.IsInstanceOf(typeof(TierQuotaDefinitionDeletedIntegrationEvent)))).MustHaveHappenedOnceExactly(); + } + [Fact] public async Task Deletes_tier_quota_definition() { @@ -123,29 +146,6 @@ public async Task Fails_to_delete_tier_quota_definition_for_missing_quota() await acting.Should().ThrowAsync().WithErrorCode("error.platform.recordNotFound"); } - [Fact] - public async Task Triggers_TierQuotaDefinitionDeletedIntegrationEvent() - { - // Arrange - var tierId = new TierId("SomeTierId"); - var tier = new Tier(tierId, "some-tier-name"); - - tier.CreateQuota(MetricKey.NumberOfSentMessages, 5, QuotaPeriod.Month); - - var command = new DeleteTierQuotaDefinitionCommand(tier.Id, tier.Quotas.First().Id); - - var tiersRepository = A.Fake(); - A.CallTo(() => tiersRepository.Find(tierId, A._, A._)).Returns(tier); - - var handler = CreateHandler(tiersRepository); - - // Act - await handler.Handle(command, CancellationToken.None); - - // Assert - A.CallTo(() => _eventBus.Publish(A.That.IsInstanceOf(typeof(TierQuotaDefinitionDeletedIntegrationEvent)))).MustHaveHappenedOnceExactly(); - } - private Handler CreateHandler(ITiersRepository tiersRepository) { var logger = A.Fake>();