diff --git a/Modules/Devices/src/Devices.Application/Devices/Commands/DeleteDevice/Handler.cs b/Modules/Devices/src/Devices.Application/Devices/Commands/DeleteDevice/Handler.cs index 39abd3bb4d..c7d4eedecd 100644 --- a/Modules/Devices/src/Devices.Application/Devices/Commands/DeleteDevice/Handler.cs +++ b/Modules/Devices/src/Devices.Application/Devices/Commands/DeleteDevice/Handler.cs @@ -26,10 +26,11 @@ public async Task Handle(DeleteDeviceCommand request, CancellationToken cancella var deviceId = DeviceId.Parse(request.DeviceId); var deviceThatIsBeingDeleted = await _identitiesRepository.GetDeviceById(deviceId, cancellationToken, track: true) ?? throw new NotFoundException(nameof(Device)); - deviceThatIsBeingDeleted.MarkAsDeleted(_userContext.GetDeviceId(), _userContext.GetAddress()); - await _identitiesRepository.Update(deviceThatIsBeingDeleted, cancellationToken); + deviceThatIsBeingDeleted.EnsureCanBeDeleted(_userContext.GetAddress()); - _logger.MarkedDeviceAsDeleted(); + await _identitiesRepository.DeleteDevice(deviceThatIsBeingDeleted, cancellationToken); + + _logger.DeviceDeleted(); } } @@ -37,8 +38,8 @@ internal static partial class DeleteDeviceLogs { [LoggerMessage( EventId = 776010, - EventName = "Devices.MarkDeviceAsDeleted.MarkedDeviceAsDeleted", + EventName = "Devices.DeleteDevice.DeviceDeleted", Level = LogLevel.Information, - Message = "Successfully marked the device as deleted.")] - public static partial void MarkedDeviceAsDeleted(this ILogger logger); + Message = "The device was deleted.")] + public static partial void DeviceDeleted(this ILogger logger); } diff --git a/Modules/Devices/src/Devices.Application/Infrastructure/Persistence/Repository/IIdentitiesRepository.cs b/Modules/Devices/src/Devices.Application/Infrastructure/Persistence/Repository/IIdentitiesRepository.cs index 7a93b6813a..b4fe595614 100644 --- a/Modules/Devices/src/Devices.Application/Infrastructure/Persistence/Repository/IIdentitiesRepository.cs +++ b/Modules/Devices/src/Devices.Application/Infrastructure/Persistence/Repository/IIdentitiesRepository.cs @@ -32,6 +32,7 @@ public interface IIdentitiesRepository Task Update(Device device, CancellationToken cancellationToken); Task FindDevices(Expression> filter, Expression> selector, CancellationToken cancellationToken, bool track = false); Task HasBackupDevice(IdentityAddress identity, CancellationToken cancellationToken); + Task DeleteDevice(Device device, CancellationToken cancellationToken); #endregion diff --git a/Modules/Devices/src/Devices.Domain/Entities/Identities/Device.cs b/Modules/Devices/src/Devices.Domain/Entities/Identities/Device.cs index f60ce6abc6..5d6e5650e1 100644 --- a/Modules/Devices/src/Devices.Domain/Entities/Identities/Device.cs +++ b/Modules/Devices/src/Devices.Domain/Entities/Identities/Device.cs @@ -77,11 +77,16 @@ public Device(Identity identity, CommunicationLanguage communicationLanguage, De public bool IsBackupDevice { get; private set; } - public DateTime? DeletedAt { get; set; } - public DeviceId? DeletedByDevice { get; set; } - public bool IsOnboarded => User.HasLoggedIn; + public void EnsureCanBeDeleted(IdentityAddress addressOfActiveIdentity) + { + var error = CanBeDeletedBy(addressOfActiveIdentity); + + if (error != null) + throw new DomainException(error); + } + private DomainError? CanBeDeletedBy(IdentityAddress addressOfActiveIdentity) { if (IsOnboarded) @@ -105,17 +110,6 @@ public bool Update(CommunicationLanguage communicationLanguage) return hasChanges; } - public void MarkAsDeleted(DeviceId deletedByDevice, IdentityAddress addressOfActiveIdentity) - { - var error = CanBeDeletedBy(addressOfActiveIdentity); - - if (error != null) - throw new DomainException(error); - - DeletedAt = SystemTime.UtcNow; - DeletedByDevice = deletedByDevice; - } - public void LoginOccurred() { if (IsBackupDevice) @@ -134,9 +128,6 @@ public static Device CreateTestDevice(Identity identity, CommunicationLanguage c #region Expressions - public static Expression> IsNotDeleted => - device => device.DeletedAt == null && device.DeletedByDevice == null; - public static Expression> IsBackup => device => device.IsBackupDevice; diff --git a/Modules/Devices/src/Devices.Infrastructure.Database.Postgres/Migrations/20241211083753_RemoveSoftDeleteColumnsFromDevicesTable.Designer.cs b/Modules/Devices/src/Devices.Infrastructure.Database.Postgres/Migrations/20241211083753_RemoveSoftDeleteColumnsFromDevicesTable.Designer.cs new file mode 100644 index 0000000000..b55568cf22 --- /dev/null +++ b/Modules/Devices/src/Devices.Infrastructure.Database.Postgres/Migrations/20241211083753_RemoveSoftDeleteColumnsFromDevicesTable.Designer.cs @@ -0,0 +1,923 @@ +// +using System; +using System.Collections.Generic; +using Backbone.Modules.Devices.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.Devices.Infrastructure.Database.Postgres.Migrations +{ + [DbContext(typeof(DevicesDbContext))] + [Migration("20241211083753_RemoveSoftDeleteColumnsFromDevicesTable")] + partial class RemoveSoftDeleteColumnsFromDevicesTable + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("Devices") + .HasAnnotation("ProductVersion", "9.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Backbone.Modules.Devices.Domain.Aggregates.PushNotifications.PnsRegistration", b => + { + b.Property("DeviceId") + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("character(20)") + .IsFixedLength(); + + b.Property("AppId") + .IsRequired() + .HasColumnType("text"); + + b.Property("DevicePushIdentifier") + .IsRequired() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("character(20)") + .IsFixedLength(); + + b.Property("Environment") + .HasColumnType("integer"); + + b.Property("Handle") + .IsRequired() + .HasMaxLength(200) + .IsUnicode(true) + .HasColumnType("character varying(200)") + .IsFixedLength(false); + + b.Property("IdentityAddress") + .IsRequired() + .HasMaxLength(80) + .IsUnicode(false) + .HasColumnType("character varying(80)") + .IsFixedLength(false); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("DeviceId"); + + b.ToTable("PnsRegistrations", "Devices"); + }); + + modelBuilder.Entity("Backbone.Modules.Devices.Domain.Aggregates.Tier.Tier", b => + { + b.Property("Id") + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("character(20)") + .IsFixedLength(); + + b.Property("CanBeManuallyAssigned") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("CanBeUsedAsDefaultForClient") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .IsUnicode(true) + .HasColumnType("character varying(30)") + .IsFixedLength(false); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("Tiers", "Devices"); + }); + + modelBuilder.Entity("Backbone.Modules.Devices.Domain.Entities.Challenge", b => + { + b.Property("Id") + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("character(20)") + .IsFixedLength(); + + b.Property("ExpiresAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("Challenges", "Challenges", t => + { + t.ExcludeFromMigrations(); + }); + }); + + modelBuilder.Entity("Backbone.Modules.Devices.Domain.Entities.Identities.ApplicationUser", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AccessFailedCount") + .HasColumnType("integer"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeviceId") + .IsRequired() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("character(20)") + .IsFixedLength(); + + b.Property("LastLoginAt") + .HasColumnType("timestamp with time zone"); + + b.Property("LockoutEnabled") + .HasColumnType("boolean"); + + b.Property("LockoutEnd") + .HasColumnType("timestamp with time zone"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("PasswordHash") + .HasColumnType("text"); + + b.Property("SecurityStamp") + .HasColumnType("text"); + + b.Property("UserName") + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("character(20)") + .IsFixedLength(); + + b.HasKey("Id"); + + b.HasIndex("DeviceId") + .IsUnique(); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers", "Devices"); + }); + + modelBuilder.Entity("Backbone.Modules.Devices.Domain.Entities.Identities.Device", b => + { + b.Property("Id") + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("character(20)") + .IsFixedLength(); + + b.Property("CommunicationLanguage") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(2) + .IsUnicode(false) + .HasColumnType("character(2)") + .HasDefaultValue("en") + .IsFixedLength(); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedByDevice") + .IsRequired() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("character(20)") + .IsFixedLength(); + + b.Property("IdentityAddress") + .IsRequired() + .HasMaxLength(80) + .IsUnicode(false) + .HasColumnType("character varying(80)") + .IsFixedLength(false); + + b.Property("IsBackupDevice") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.HasIndex("IdentityAddress"); + + b.ToTable("Devices", "Devices"); + }); + + modelBuilder.Entity("Backbone.Modules.Devices.Domain.Entities.Identities.Identity", b => + { + b.Property("Address") + .HasMaxLength(80) + .IsUnicode(false) + .HasColumnType("character varying(80)") + .IsFixedLength(false); + + b.Property("ClientId") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletionGracePeriodEndsAt") + .HasColumnType("timestamp with time zone"); + + b.Property("IdentityVersion") + .HasColumnType("smallint"); + + b.Property("PublicKey") + .IsRequired() + .HasColumnType("bytea"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("TierId") + .IsRequired() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("character(20)") + .IsFixedLength(); + + b.Property("TierIdBeforeDeletion") + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("character(20)") + .IsFixedLength(); + + b.HasKey("Address"); + + b.HasIndex("ClientId"); + + NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("ClientId"), "hash"); + + b.HasIndex("TierId"); + + NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("TierId"), "hash"); + + b.ToTable("Identities", "Devices"); + }); + + modelBuilder.Entity("Backbone.Modules.Devices.Domain.Entities.Identities.IdentityDeletionProcess", b => + { + b.Property("Id") + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("character(20)") + .IsFixedLength(); + + b.Property("ApprovalReminder1SentAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ApprovalReminder2SentAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ApprovalReminder3SentAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ApprovedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ApprovedByDevice") + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("character(20)") + .IsFixedLength(); + + b.Property("CancelledAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CancelledByDevice") + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("character(20)") + .IsFixedLength(); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletionStartedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("GracePeriodEndsAt") + .HasColumnType("timestamp with time zone"); + + b.Property("GracePeriodReminder1SentAt") + .HasColumnType("timestamp with time zone"); + + b.Property("GracePeriodReminder2SentAt") + .HasColumnType("timestamp with time zone"); + + b.Property("GracePeriodReminder3SentAt") + .HasColumnType("timestamp with time zone"); + + b.Property("IdentityAddress") + .IsRequired() + .HasMaxLength(80) + .IsUnicode(false) + .HasColumnType("character varying(80)") + .IsFixedLength(false); + + b.Property("RejectedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("RejectedByDevice") + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("character(20)") + .IsFixedLength(); + + b.Property("Status") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("IdentityAddress"); + + b.HasIndex(new[] { "IdentityAddress" }, "IX_only_one_active_deletion_process") + .IsUnique() + .HasFilter("\"Status\" = 1"); + + b.ToTable("IdentityDeletionProcesses", "Devices"); + }); + + modelBuilder.Entity("Backbone.Modules.Devices.Domain.Entities.Identities.IdentityDeletionProcessAuditLogEntry", b => + { + b.Property("Id") + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("character(20)") + .IsFixedLength(); + + b.Property("AdditionalData") + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeviceIdHash") + .HasColumnType("bytea"); + + b.Property("IdentityAddressHash") + .IsRequired() + .HasColumnType("bytea"); + + b.Property("IdentityDeletionProcessId") + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("character(20)") + .IsFixedLength(); + + b.Property("MessageKey") + .IsRequired() + .HasColumnType("text"); + + b.Property("NewStatus") + .HasColumnType("integer"); + + b.Property("OldStatus") + .HasColumnType("integer"); + + b.PrimitiveCollection>("UsernameHashesBase64") + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.HasIndex("IdentityDeletionProcessId"); + + b.ToTable("IdentityDeletionProcessAuditLog", "Devices"); + }); + + modelBuilder.Entity("Backbone.Modules.Devices.Infrastructure.OpenIddict.CustomOpenIddictEntityFrameworkCoreApplication", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ClientId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ClientSecret") + .HasColumnType("text"); + + b.Property("ClientType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConsentType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DefaultTier") + .IsRequired() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("character(20)") + .IsFixedLength(); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("JsonWebKeySet") + .HasColumnType("text"); + + b.Property("MaxIdentities") + .HasColumnType("integer"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("PostLogoutRedirectUris") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedirectUris") + .HasColumnType("text"); + + b.Property("Requirements") + .HasColumnType("text"); + + b.Property("Settings") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ClientId") + .IsUnique(); + + b.HasIndex("DefaultTier"); + + b.ToTable("OpenIddictApplications", "Devices"); + }); + + modelBuilder.Entity("Backbone.Modules.Devices.Infrastructure.OpenIddict.CustomOpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Scopes") + .HasColumnType("text"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictAuthorizations", "Devices"); + }); + + modelBuilder.Entity("Backbone.Modules.Devices.Infrastructure.OpenIddict.CustomOpenIddictEntityFrameworkCoreScope", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Descriptions") + .HasColumnType("text"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Resources") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("OpenIddictScopes", "Devices"); + }); + + modelBuilder.Entity("Backbone.Modules.Devices.Infrastructure.OpenIddict.CustomOpenIddictEntityFrameworkCoreToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("AuthorizationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Payload") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedemptionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ReferenceId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("AuthorizationId"); + + b.HasIndex("ReferenceId") + .IsUnique(); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictTokens", "Devices"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles", "Devices"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", "Devices"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", "Devices"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("ProviderKey") + .HasColumnType("text"); + + b.Property("ProviderDisplayName") + .HasColumnType("text"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", "Devices"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("text"); + + b.Property("RoleId") + .HasColumnType("text"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", "Devices"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("text"); + + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("text"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", "Devices"); + }); + + modelBuilder.Entity("Backbone.Modules.Devices.Domain.Entities.Identities.ApplicationUser", b => + { + b.HasOne("Backbone.Modules.Devices.Domain.Entities.Identities.Device", "Device") + .WithOne("User") + .HasForeignKey("Backbone.Modules.Devices.Domain.Entities.Identities.ApplicationUser", "DeviceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Device"); + }); + + modelBuilder.Entity("Backbone.Modules.Devices.Domain.Entities.Identities.Device", b => + { + b.HasOne("Backbone.Modules.Devices.Domain.Entities.Identities.Identity", "Identity") + .WithMany("Devices") + .HasForeignKey("IdentityAddress") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Identity"); + }); + + modelBuilder.Entity("Backbone.Modules.Devices.Domain.Entities.Identities.IdentityDeletionProcess", b => + { + b.HasOne("Backbone.Modules.Devices.Domain.Entities.Identities.Identity", null) + .WithMany("DeletionProcesses") + .HasForeignKey("IdentityAddress") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Backbone.Modules.Devices.Domain.Entities.Identities.IdentityDeletionProcessAuditLogEntry", b => + { + b.HasOne("Backbone.Modules.Devices.Domain.Entities.Identities.IdentityDeletionProcess", null) + .WithMany("AuditLog") + .HasForeignKey("IdentityDeletionProcessId") + .OnDelete(DeleteBehavior.SetNull); + }); + + modelBuilder.Entity("Backbone.Modules.Devices.Infrastructure.OpenIddict.CustomOpenIddictEntityFrameworkCoreApplication", b => + { + b.HasOne("Backbone.Modules.Devices.Domain.Aggregates.Tier.Tier", null) + .WithMany() + .HasForeignKey("DefaultTier") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("Backbone.Modules.Devices.Infrastructure.OpenIddict.CustomOpenIddictEntityFrameworkCoreAuthorization", b => + { + b.HasOne("Backbone.Modules.Devices.Infrastructure.OpenIddict.CustomOpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Authorizations") + .HasForeignKey("ApplicationId"); + + b.Navigation("Application"); + }); + + modelBuilder.Entity("Backbone.Modules.Devices.Infrastructure.OpenIddict.CustomOpenIddictEntityFrameworkCoreToken", b => + { + b.HasOne("Backbone.Modules.Devices.Infrastructure.OpenIddict.CustomOpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Tokens") + .HasForeignKey("ApplicationId"); + + b.HasOne("Backbone.Modules.Devices.Infrastructure.OpenIddict.CustomOpenIddictEntityFrameworkCoreAuthorization", "Authorization") + .WithMany("Tokens") + .HasForeignKey("AuthorizationId"); + + b.Navigation("Application"); + + b.Navigation("Authorization"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Backbone.Modules.Devices.Domain.Entities.Identities.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Backbone.Modules.Devices.Domain.Entities.Identities.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Backbone.Modules.Devices.Domain.Entities.Identities.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Backbone.Modules.Devices.Domain.Entities.Identities.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Backbone.Modules.Devices.Domain.Entities.Identities.Device", b => + { + b.Navigation("User") + .IsRequired(); + }); + + modelBuilder.Entity("Backbone.Modules.Devices.Domain.Entities.Identities.Identity", b => + { + b.Navigation("DeletionProcesses"); + + b.Navigation("Devices"); + }); + + modelBuilder.Entity("Backbone.Modules.Devices.Domain.Entities.Identities.IdentityDeletionProcess", b => + { + b.Navigation("AuditLog"); + }); + + modelBuilder.Entity("Backbone.Modules.Devices.Infrastructure.OpenIddict.CustomOpenIddictEntityFrameworkCoreApplication", b => + { + b.Navigation("Authorizations"); + + b.Navigation("Tokens"); + }); + + modelBuilder.Entity("Backbone.Modules.Devices.Infrastructure.OpenIddict.CustomOpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Navigation("Tokens"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Modules/Devices/src/Devices.Infrastructure.Database.Postgres/Migrations/20241211083753_RemoveSoftDeleteColumnsFromDevicesTable.cs b/Modules/Devices/src/Devices.Infrastructure.Database.Postgres/Migrations/20241211083753_RemoveSoftDeleteColumnsFromDevicesTable.cs new file mode 100644 index 0000000000..ac5191b766 --- /dev/null +++ b/Modules/Devices/src/Devices.Infrastructure.Database.Postgres/Migrations/20241211083753_RemoveSoftDeleteColumnsFromDevicesTable.cs @@ -0,0 +1,48 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Backbone.Modules.Devices.Infrastructure.Database.Postgres.Migrations +{ + /// + public partial class RemoveSoftDeleteColumnsFromDevicesTable : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.Sql("""DELETE FROM "Devices"."Devices" WHERE "DeletedAt" IS NOT NULL"""); + + migrationBuilder.DropColumn( + name: "DeletedAt", + schema: "Devices", + table: "Devices"); + + migrationBuilder.DropColumn( + name: "DeletedByDevice", + schema: "Devices", + table: "Devices"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "DeletedAt", + schema: "Devices", + table: "Devices", + type: "timestamp with time zone", + nullable: true); + + migrationBuilder.AddColumn( + name: "DeletedByDevice", + schema: "Devices", + table: "Devices", + type: "character(20)", + unicode: false, + fixedLength: true, + maxLength: 20, + nullable: true); + } + } +} diff --git a/Modules/Devices/src/Devices.Infrastructure.Database.Postgres/Migrations/DevicesDbContextModelSnapshot.cs b/Modules/Devices/src/Devices.Infrastructure.Database.Postgres/Migrations/DevicesDbContextModelSnapshot.cs index b7e1dd1b00..654c0ddbb0 100644 --- a/Modules/Devices/src/Devices.Infrastructure.Database.Postgres/Migrations/DevicesDbContextModelSnapshot.cs +++ b/Modules/Devices/src/Devices.Infrastructure.Database.Postgres/Migrations/DevicesDbContextModelSnapshot.cs @@ -206,15 +206,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("character(20)") .IsFixedLength(); - b.Property("DeletedAt") - .HasColumnType("timestamp with time zone"); - - b.Property("DeletedByDevice") - .HasMaxLength(20) - .IsUnicode(false) - .HasColumnType("character(20)") - .IsFixedLength(); - b.Property("IdentityAddress") .IsRequired() .HasMaxLength(80) diff --git a/Modules/Devices/src/Devices.Infrastructure.Database.SqlServer/Migrations/20241211083751_RemoveSoftDeleteColumnsFromDevicesTable.Designer.cs b/Modules/Devices/src/Devices.Infrastructure.Database.SqlServer/Migrations/20241211083751_RemoveSoftDeleteColumnsFromDevicesTable.Designer.cs new file mode 100644 index 0000000000..6153c825c2 --- /dev/null +++ b/Modules/Devices/src/Devices.Infrastructure.Database.SqlServer/Migrations/20241211083751_RemoveSoftDeleteColumnsFromDevicesTable.Designer.cs @@ -0,0 +1,925 @@ +// +using System; +using Backbone.Modules.Devices.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.Devices.Infrastructure.Database.SqlServer.Migrations +{ + [DbContext(typeof(DevicesDbContext))] + [Migration("20241211083751_RemoveSoftDeleteColumnsFromDevicesTable")] + partial class RemoveSoftDeleteColumnsFromDevicesTable + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("Devices") + .HasAnnotation("ProductVersion", "9.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Backbone.Modules.Devices.Domain.Aggregates.PushNotifications.PnsRegistration", b => + { + b.Property("DeviceId") + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("char(20)") + .IsFixedLength(); + + b.Property("AppId") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("DevicePushIdentifier") + .IsRequired() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("char(20)") + .IsFixedLength(); + + b.Property("Environment") + .HasColumnType("int"); + + b.Property("Handle") + .IsRequired() + .HasMaxLength(200) + .IsUnicode(true) + .HasColumnType("nvarchar(200)") + .IsFixedLength(false); + + b.Property("IdentityAddress") + .IsRequired() + .HasMaxLength(80) + .IsUnicode(false) + .HasColumnType("varchar(80)") + .IsFixedLength(false); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.HasKey("DeviceId"); + + b.ToTable("PnsRegistrations", "Devices"); + }); + + modelBuilder.Entity("Backbone.Modules.Devices.Domain.Aggregates.Tier.Tier", b => + { + b.Property("Id") + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("char(20)") + .IsFixedLength(); + + b.Property("CanBeManuallyAssigned") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("CanBeUsedAsDefaultForClient") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .IsUnicode(true) + .HasColumnType("nvarchar(30)") + .IsFixedLength(false); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("Tiers", "Devices"); + }); + + modelBuilder.Entity("Backbone.Modules.Devices.Domain.Entities.Challenge", b => + { + b.Property("Id") + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("char(20)") + .IsFixedLength(); + + b.Property("ExpiresAt") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.ToTable("Challenges", "Challenges", t => + { + t.ExcludeFromMigrations(); + }); + }); + + modelBuilder.Entity("Backbone.Modules.Devices.Domain.Entities.Identities.ApplicationUser", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("DeviceId") + .IsRequired() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("char(20)") + .IsFixedLength(); + + b.Property("LastLoginAt") + .HasColumnType("datetime2"); + + b.Property("LockoutEnabled") + .HasColumnType("bit"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("SecurityStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("UserName") + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("char(20)") + .IsFixedLength(); + + b.HasKey("Id"); + + b.HasIndex("DeviceId") + .IsUnique(); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.ToTable("AspNetUsers", "Devices"); + }); + + modelBuilder.Entity("Backbone.Modules.Devices.Domain.Entities.Identities.Device", b => + { + b.Property("Id") + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("char(20)") + .IsFixedLength(); + + b.Property("CommunicationLanguage") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(2) + .IsUnicode(false) + .HasColumnType("char(2)") + .HasDefaultValue("en") + .IsFixedLength(); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedByDevice") + .IsRequired() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("char(20)") + .IsFixedLength(); + + b.Property("IdentityAddress") + .IsRequired() + .HasMaxLength(80) + .IsUnicode(false) + .HasColumnType("varchar(80)") + .IsFixedLength(false); + + b.Property("IsBackupDevice") + .HasColumnType("bit"); + + b.HasKey("Id"); + + b.HasIndex("IdentityAddress"); + + b.ToTable("Devices", "Devices"); + }); + + modelBuilder.Entity("Backbone.Modules.Devices.Domain.Entities.Identities.Identity", b => + { + b.Property("Address") + .HasMaxLength(80) + .IsUnicode(false) + .HasColumnType("varchar(80)") + .IsFixedLength(false); + + b.Property("ClientId") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("DeletionGracePeriodEndsAt") + .HasColumnType("datetime2"); + + b.Property("IdentityVersion") + .HasColumnType("tinyint"); + + b.Property("PublicKey") + .IsRequired() + .HasColumnType("varbinary(max)"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("TierId") + .IsRequired() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("char(20)") + .IsFixedLength(); + + b.Property("TierIdBeforeDeletion") + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("char(20)") + .IsFixedLength(); + + b.HasKey("Address"); + + b.HasIndex("ClientId") + .HasAnnotation("Npgsql:IndexMethod", "hash"); + + b.HasIndex("TierId") + .HasAnnotation("Npgsql:IndexMethod", "hash"); + + b.ToTable("Identities", "Devices"); + }); + + modelBuilder.Entity("Backbone.Modules.Devices.Domain.Entities.Identities.IdentityDeletionProcess", b => + { + b.Property("Id") + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("char(20)") + .IsFixedLength(); + + b.Property("ApprovalReminder1SentAt") + .HasColumnType("datetime2"); + + b.Property("ApprovalReminder2SentAt") + .HasColumnType("datetime2"); + + b.Property("ApprovalReminder3SentAt") + .HasColumnType("datetime2"); + + b.Property("ApprovedAt") + .HasColumnType("datetime2"); + + b.Property("ApprovedByDevice") + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("char(20)") + .IsFixedLength(); + + b.Property("CancelledAt") + .HasColumnType("datetime2"); + + b.Property("CancelledByDevice") + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("char(20)") + .IsFixedLength(); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("DeletionStartedAt") + .HasColumnType("datetime2"); + + b.Property("GracePeriodEndsAt") + .HasColumnType("datetime2"); + + b.Property("GracePeriodReminder1SentAt") + .HasColumnType("datetime2"); + + b.Property("GracePeriodReminder2SentAt") + .HasColumnType("datetime2"); + + b.Property("GracePeriodReminder3SentAt") + .HasColumnType("datetime2"); + + b.Property("IdentityAddress") + .IsRequired() + .HasMaxLength(80) + .IsUnicode(false) + .HasColumnType("varchar(80)") + .IsFixedLength(false); + + b.Property("RejectedAt") + .HasColumnType("datetime2"); + + b.Property("RejectedByDevice") + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("char(20)") + .IsFixedLength(); + + b.Property("Status") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("IdentityAddress"); + + b.HasIndex(new[] { "IdentityAddress" }, "IX_only_one_active_deletion_process") + .IsUnique() + .HasFilter("\"Status\" = 1"); + + b.ToTable("IdentityDeletionProcesses", "Devices"); + }); + + modelBuilder.Entity("Backbone.Modules.Devices.Domain.Entities.Identities.IdentityDeletionProcessAuditLogEntry", b => + { + b.Property("Id") + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("char(20)") + .IsFixedLength(); + + b.Property("AdditionalData") + .HasColumnType("nvarchar(max)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("DeviceIdHash") + .HasColumnType("varbinary(max)"); + + b.Property("IdentityAddressHash") + .IsRequired() + .HasColumnType("varbinary(max)"); + + b.Property("IdentityDeletionProcessId") + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("char(20)") + .IsFixedLength(); + + b.Property("MessageKey") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("NewStatus") + .HasColumnType("int"); + + b.Property("OldStatus") + .HasColumnType("int"); + + b.PrimitiveCollection("UsernameHashesBase64") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityDeletionProcessId"); + + b.ToTable("IdentityDeletionProcessAuditLog", "Devices"); + }); + + modelBuilder.Entity("Backbone.Modules.Devices.Infrastructure.OpenIddict.CustomOpenIddictEntityFrameworkCoreApplication", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("nvarchar(450)"); + + b.Property("ApplicationType") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("ClientId") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("ClientSecret") + .HasColumnType("nvarchar(max)"); + + b.Property("ClientType") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("ConsentType") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("DefaultTier") + .IsRequired() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("char(20)") + .IsFixedLength(); + + b.Property("DisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("DisplayNames") + .HasColumnType("nvarchar(max)"); + + b.Property("JsonWebKeySet") + .HasColumnType("nvarchar(max)"); + + b.Property("MaxIdentities") + .HasColumnType("int"); + + b.Property("Permissions") + .HasColumnType("nvarchar(max)"); + + b.Property("PostLogoutRedirectUris") + .HasColumnType("nvarchar(max)"); + + b.Property("Properties") + .HasColumnType("nvarchar(max)"); + + b.Property("RedirectUris") + .HasColumnType("nvarchar(max)"); + + b.Property("Requirements") + .HasColumnType("nvarchar(max)"); + + b.Property("Settings") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("ClientId") + .IsUnique() + .HasFilter("[ClientId] IS NOT NULL"); + + b.HasIndex("DefaultTier"); + + b.ToTable("OpenIddictApplications", "Devices"); + }); + + modelBuilder.Entity("Backbone.Modules.Devices.Infrastructure.OpenIddict.CustomOpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("nvarchar(450)"); + + b.Property("ApplicationId") + .HasColumnType("nvarchar(450)"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("CreationDate") + .HasColumnType("datetime2"); + + b.Property("Properties") + .HasColumnType("nvarchar(max)"); + + b.Property("Scopes") + .HasColumnType("nvarchar(max)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("nvarchar(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictAuthorizations", "Devices"); + }); + + modelBuilder.Entity("Backbone.Modules.Devices.Infrastructure.OpenIddict.CustomOpenIddictEntityFrameworkCoreScope", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("nvarchar(450)"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("Descriptions") + .HasColumnType("nvarchar(max)"); + + b.Property("DisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("DisplayNames") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("Properties") + .HasColumnType("nvarchar(max)"); + + b.Property("Resources") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique() + .HasFilter("[Name] IS NOT NULL"); + + b.ToTable("OpenIddictScopes", "Devices"); + }); + + modelBuilder.Entity("Backbone.Modules.Devices.Infrastructure.OpenIddict.CustomOpenIddictEntityFrameworkCoreToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("nvarchar(450)"); + + b.Property("ApplicationId") + .HasColumnType("nvarchar(450)"); + + b.Property("AuthorizationId") + .HasColumnType("nvarchar(450)"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("CreationDate") + .HasColumnType("datetime2"); + + b.Property("ExpirationDate") + .HasColumnType("datetime2"); + + b.Property("Payload") + .HasColumnType("nvarchar(max)"); + + b.Property("Properties") + .HasColumnType("nvarchar(max)"); + + b.Property("RedemptionDate") + .HasColumnType("datetime2"); + + b.Property("ReferenceId") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("nvarchar(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.HasKey("Id"); + + b.HasIndex("AuthorizationId"); + + b.HasIndex("ReferenceId") + .IsUnique() + .HasFilter("[ReferenceId] IS NOT NULL"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictTokens", "Devices"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("AspNetRoles", "Devices"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", "Devices"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", "Devices"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderKey") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderDisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", "Devices"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("RoleId") + .HasColumnType("nvarchar(450)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", "Devices"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("Name") + .HasColumnType("nvarchar(450)"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", "Devices"); + }); + + modelBuilder.Entity("Backbone.Modules.Devices.Domain.Entities.Identities.ApplicationUser", b => + { + b.HasOne("Backbone.Modules.Devices.Domain.Entities.Identities.Device", "Device") + .WithOne("User") + .HasForeignKey("Backbone.Modules.Devices.Domain.Entities.Identities.ApplicationUser", "DeviceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Device"); + }); + + modelBuilder.Entity("Backbone.Modules.Devices.Domain.Entities.Identities.Device", b => + { + b.HasOne("Backbone.Modules.Devices.Domain.Entities.Identities.Identity", "Identity") + .WithMany("Devices") + .HasForeignKey("IdentityAddress") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Identity"); + }); + + modelBuilder.Entity("Backbone.Modules.Devices.Domain.Entities.Identities.IdentityDeletionProcess", b => + { + b.HasOne("Backbone.Modules.Devices.Domain.Entities.Identities.Identity", null) + .WithMany("DeletionProcesses") + .HasForeignKey("IdentityAddress") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Backbone.Modules.Devices.Domain.Entities.Identities.IdentityDeletionProcessAuditLogEntry", b => + { + b.HasOne("Backbone.Modules.Devices.Domain.Entities.Identities.IdentityDeletionProcess", null) + .WithMany("AuditLog") + .HasForeignKey("IdentityDeletionProcessId") + .OnDelete(DeleteBehavior.SetNull); + }); + + modelBuilder.Entity("Backbone.Modules.Devices.Infrastructure.OpenIddict.CustomOpenIddictEntityFrameworkCoreApplication", b => + { + b.HasOne("Backbone.Modules.Devices.Domain.Aggregates.Tier.Tier", null) + .WithMany() + .HasForeignKey("DefaultTier") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("Backbone.Modules.Devices.Infrastructure.OpenIddict.CustomOpenIddictEntityFrameworkCoreAuthorization", b => + { + b.HasOne("Backbone.Modules.Devices.Infrastructure.OpenIddict.CustomOpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Authorizations") + .HasForeignKey("ApplicationId"); + + b.Navigation("Application"); + }); + + modelBuilder.Entity("Backbone.Modules.Devices.Infrastructure.OpenIddict.CustomOpenIddictEntityFrameworkCoreToken", b => + { + b.HasOne("Backbone.Modules.Devices.Infrastructure.OpenIddict.CustomOpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Tokens") + .HasForeignKey("ApplicationId"); + + b.HasOne("Backbone.Modules.Devices.Infrastructure.OpenIddict.CustomOpenIddictEntityFrameworkCoreAuthorization", "Authorization") + .WithMany("Tokens") + .HasForeignKey("AuthorizationId"); + + b.Navigation("Application"); + + b.Navigation("Authorization"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Backbone.Modules.Devices.Domain.Entities.Identities.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Backbone.Modules.Devices.Domain.Entities.Identities.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Backbone.Modules.Devices.Domain.Entities.Identities.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Backbone.Modules.Devices.Domain.Entities.Identities.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Backbone.Modules.Devices.Domain.Entities.Identities.Device", b => + { + b.Navigation("User") + .IsRequired(); + }); + + modelBuilder.Entity("Backbone.Modules.Devices.Domain.Entities.Identities.Identity", b => + { + b.Navigation("DeletionProcesses"); + + b.Navigation("Devices"); + }); + + modelBuilder.Entity("Backbone.Modules.Devices.Domain.Entities.Identities.IdentityDeletionProcess", b => + { + b.Navigation("AuditLog"); + }); + + modelBuilder.Entity("Backbone.Modules.Devices.Infrastructure.OpenIddict.CustomOpenIddictEntityFrameworkCoreApplication", b => + { + b.Navigation("Authorizations"); + + b.Navigation("Tokens"); + }); + + modelBuilder.Entity("Backbone.Modules.Devices.Infrastructure.OpenIddict.CustomOpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Navigation("Tokens"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Modules/Devices/src/Devices.Infrastructure.Database.SqlServer/Migrations/20241211083751_RemoveSoftDeleteColumnsFromDevicesTable.cs b/Modules/Devices/src/Devices.Infrastructure.Database.SqlServer/Migrations/20241211083751_RemoveSoftDeleteColumnsFromDevicesTable.cs new file mode 100644 index 0000000000..5534128414 --- /dev/null +++ b/Modules/Devices/src/Devices.Infrastructure.Database.SqlServer/Migrations/20241211083751_RemoveSoftDeleteColumnsFromDevicesTable.cs @@ -0,0 +1,48 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Backbone.Modules.Devices.Infrastructure.Database.SqlServer.Migrations +{ + /// + public partial class RemoveSoftDeleteColumnsFromDevicesTable : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.Sql("""DELETE FROM [Devices].[Devices] WHERE "DeletedAt" IS NOT NULL"""); + + migrationBuilder.DropColumn( + name: "DeletedAt", + schema: "Devices", + table: "Devices"); + + migrationBuilder.DropColumn( + name: "DeletedByDevice", + schema: "Devices", + table: "Devices"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "DeletedAt", + schema: "Devices", + table: "Devices", + type: "datetime2", + nullable: true); + + migrationBuilder.AddColumn( + name: "DeletedByDevice", + schema: "Devices", + table: "Devices", + type: "char(20)", + unicode: false, + fixedLength: true, + maxLength: 20, + nullable: true); + } + } +} diff --git a/Modules/Devices/src/Devices.Infrastructure.Database.SqlServer/Migrations/DevicesDbContextModelSnapshot.cs b/Modules/Devices/src/Devices.Infrastructure.Database.SqlServer/Migrations/DevicesDbContextModelSnapshot.cs index 014f36201c..84ac70c716 100644 --- a/Modules/Devices/src/Devices.Infrastructure.Database.SqlServer/Migrations/DevicesDbContextModelSnapshot.cs +++ b/Modules/Devices/src/Devices.Infrastructure.Database.SqlServer/Migrations/DevicesDbContextModelSnapshot.cs @@ -206,15 +206,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("char(20)") .IsFixedLength(); - b.Property("DeletedAt") - .HasColumnType("datetime2"); - - b.Property("DeletedByDevice") - .HasMaxLength(20) - .IsUnicode(false) - .HasColumnType("char(20)") - .IsFixedLength(); - b.Property("IdentityAddress") .IsRequired() .HasMaxLength(80) diff --git a/Modules/Devices/src/Devices.Infrastructure/Persistence/Database/QueryableExtensions/DeviceQueryableExtensions.cs b/Modules/Devices/src/Devices.Infrastructure/Persistence/Database/QueryableExtensions/DeviceQueryableExtensions.cs index d83692001a..35d3be6c3a 100644 --- a/Modules/Devices/src/Devices.Infrastructure/Persistence/Database/QueryableExtensions/DeviceQueryableExtensions.cs +++ b/Modules/Devices/src/Devices.Infrastructure/Persistence/Database/QueryableExtensions/DeviceQueryableExtensions.cs @@ -1,6 +1,5 @@ using Backbone.DevelopmentKit.Identity.ValueObjects; using Backbone.Modules.Devices.Domain.Entities.Identities; -using Microsoft.EntityFrameworkCore; namespace Backbone.Modules.Devices.Infrastructure.Persistence.Database.QueryableExtensions; @@ -16,18 +15,8 @@ public static IQueryable OfIdentity(this IQueryable query, Ident return query.Where(d => d.IdentityAddress == address); } - public static IQueryable NotDeleted(this IQueryable query) - { - return query.Where(Device.IsNotDeleted); - } - public static IQueryable WithIdIn(this IQueryable query, IEnumerable ids) { return query.Where(d => ids.Contains(d.Id)); } - - public static IQueryable IncludeUser(this IQueryable query) - { - return query.Include(d => d.User); - } } diff --git a/Modules/Devices/src/Devices.Infrastructure/Persistence/Repository/IdentitiesRepository.cs b/Modules/Devices/src/Devices.Infrastructure/Persistence/Repository/IdentitiesRepository.cs index 33255da85a..690c704251 100644 --- a/Modules/Devices/src/Devices.Infrastructure/Persistence/Repository/IdentitiesRepository.cs +++ b/Modules/Devices/src/Devices.Infrastructure/Persistence/Repository/IdentitiesRepository.cs @@ -61,6 +61,12 @@ public async Task HasBackupDevice(IdentityAddress identity, CancellationTo .AnyAsync(cancellationToken); } + public async Task DeleteDevice(Device device, CancellationToken cancellationToken) + { + _devices.Remove(device); + await _dbContext.SaveChangesAsync(cancellationToken); + } + public async Task> GetIdentityDeletionProcessAuditLogs(Expression> filter, CancellationToken cancellationToken, bool track = false) { @@ -115,7 +121,6 @@ public async Task UpdateWithNewDevice(Identity identity, string password) public async Task> FindAllDevicesOfIdentity(IdentityAddress identity, IEnumerable ids, PaginationFilter paginationFilter, CancellationToken cancellationToken) { var query = _readonlyDevices - .NotDeleted() .IncludeAll(_dbContext) .OfIdentity(identity); @@ -128,7 +133,6 @@ public async Task> FindAllDevicesOfIdentity(IdentityA public async Task GetDeviceById(DeviceId deviceId, CancellationToken cancellationToken, bool track = false) { return await (track ? _devices : _readonlyDevices) - .NotDeleted() .IncludeAll(_dbContext) .FirstOrDefaultAsync(d => d.Id == deviceId, cancellationToken); } @@ -136,7 +140,6 @@ public async Task> FindAllDevicesOfIdentity(IdentityA public async Task> GetDevicesByIds(IEnumerable deviceIds, CancellationToken cancellationToken, bool track = false) { return await (track ? _devices : _readonlyDevices) - .NotDeleted() .IncludeAll(_dbContext) .Where(d => deviceIds.Contains(d.Id)) .ToListAsync(cancellationToken); diff --git a/Modules/Devices/test/Devices.Domain.Tests/Domain/DeviceTests.cs b/Modules/Devices/test/Devices.Domain.Tests/Domain/DeviceTests.cs index e66e2a1796..748411e7a2 100644 --- a/Modules/Devices/test/Devices.Domain.Tests/Domain/DeviceTests.cs +++ b/Modules/Devices/test/Devices.Domain.Tests/Domain/DeviceTests.cs @@ -81,20 +81,19 @@ public void IsOnboarded_returns_true_if_user_has_been_used_to_login() } [Fact] - public void An_unOnboarded_device_can_be_deleted() + public void An_unonboarded_device_can_be_deleted() { // Arrange var identity = TestDataGenerator.CreateIdentityWithoutDevice(); - var activeDevice = CreateOnboardedDevice(identity); + CreateOnboardedDevice(identity); var unOnboardedDevice = CreateUnonboardedDevice(identity); // Act - unOnboardedDevice.MarkAsDeleted(activeDevice.Id, identity.Address); + var acting = () => unOnboardedDevice.EnsureCanBeDeleted(identity.Address); // Assert - unOnboardedDevice.DeletedAt.Should().NotBeNull(); - unOnboardedDevice.DeletedByDevice.Should().Be(activeDevice.Id); + acting.Should().NotThrow(); } [Fact] @@ -103,14 +102,14 @@ public void An_onboarded_device_cannot_be_deleted() // Arrange var identity = TestDataGenerator.CreateIdentity(); - var activeDevice = CreateOnboardedDevice(identity); + CreateOnboardedDevice(identity); var onboardedDevice = CreateOnboardedDevice(identity); // Act - var action = () => onboardedDevice.MarkAsDeleted(activeDevice.Id, identity.Address); + var acting = () => onboardedDevice.EnsureCanBeDeleted(identity.Address); // Assert - var domainException = action.Should().Throw().Which; + var domainException = acting.Should().Throw().Which; domainException.Code.Should().Be("error.platform.validation.device.deviceCannotBeDeleted"); } @@ -121,11 +120,11 @@ public void A_device_not_owned_by_active_identity_cannot_be_deleted() var activeIdentity = TestDataGenerator.CreateIdentity(); var otherIdentity = TestDataGenerator.CreateIdentityWithoutDevice(); - var activeDevice = CreateOnboardedDevice(activeIdentity); - var unOnboardedDeviceOfOtherIdentity = CreateUnonboardedDevice(otherIdentity); + CreateOnboardedDevice(activeIdentity); + var unonboardedDeviceOfOtherIdentity = CreateUnonboardedDevice(otherIdentity); // Act - var action = () => unOnboardedDeviceOfOtherIdentity.MarkAsDeleted(activeDevice.Id, activeIdentity.Address); + var action = () => unonboardedDeviceOfOtherIdentity.EnsureCanBeDeleted(activeIdentity.Address); // Assert var domainException = action.Should().Throw().Which;