From 226e39387687600052273de12d84fce59456da06 Mon Sep 17 00:00:00 2001 From: VitSun666 <61446535+VitSun666@users.noreply.github.com> Date: Fri, 8 Aug 2025 18:12:06 +0300 Subject: [PATCH 1/6] feat: rbac without keycloak --- .../CreateArticleCommandHandlerTests.cs | 8 +- .../Abstractions/ICurrentUserService.cs | 10 - .../Abstractions/IIdentityService.cs | 8 - .../Abstractions/IPermissionService.cs | 12 + .../Abstractions/IUserContext.cs | 9 + .../Authorization/PermissionService.cs | 46 ++ .../Constants/AuthorizationConstants.cs | 11 - Application/Data/IApplicationDbContext.cs | 4 +- .../Extensions/ServiceCollectionExt.cs | 12 +- .../CreateArticleCommandHandler.cs | 4 +- .../EditArticle/EditArticleCommandHandler.cs | 4 +- .../GetArticle/GetArticleQueryHandler.cs | 11 +- .../Articles/GetArticle/GetArticleResponse.cs | 2 +- .../GetPendingRevisionsResponse.cs | 2 +- .../GetRevisionHistoryResponse.cs | 2 +- .../GetRevisionReviewHistoryResponse.cs | 2 +- .../ReviewRevisionCommandHandler.cs | 4 +- .../CreateAuthor/CreateAuthorResponse.cs | 3 - .../Authors/EditAuthor/EditAuthorResponse.cs | 3 - Application/Features/Authors/Errors/Author.cs | 16 - Application/Features/Users/Errors/User.cs | 16 + .../Features/Users/GetUser/GetUserCommand.cs | 3 + .../Users/GetUser/GetUserCommandHandler.cs | 35 + .../Features/Users/GetUser/GetUserResponse.cs | 3 + .../Users/GetUser/IGetUserCommandHandler.cs | 7 + .../RenameUser/IRenameUserCommandHandler.cs | 8 + .../Users/RenameUser/RenameUserCommand.cs | 3 + .../RenameUser/RenameUserCommandHandler.cs | 24 + .../Users/RenameUser/RenameUserResponse.cs | 3 + Domain/Entities/Author.cs | 8 - Domain/Entities/Permission.cs | 10 + Domain/Entities/Review.cs | 4 +- Domain/Entities/Revision.cs | 4 +- Domain/Entities/Role.cs | 12 + Domain/Entities/User.cs | 10 + Domain/Rbac/Permissions.cs | 73 +++ Domain/Rbac/Roles.cs | 77 +++ .../Authorization/CurrentUserService.cs | 22 - .../Authorization/IdentityService.cs | 29 - Infrastructure/Data/ApplicationDbContext.cs | 34 +- .../20231205172438_Initial.Designer.cs | 192 ------ .../Data/Migrations/20231205172438_Initial.cs | 147 ----- ...splaynameForArticleAndCategory.Designer.cs | 204 ------ ...edIdAndDisplaynameForArticleAndCategory.cs | 166 ----- ...arentOnDeleteBehaviorToSetNull.Designer.cs | 204 ------ ...CategoryParentOnDeleteBehaviorToSetNull.cs | 42 -- ...ticleOnDeleteBehaviorToCascade.Designer.cs | 205 ------ ...edirectArticleOnDeleteBehaviorToCascade.cs | 41 -- ...0231214021324_AddedAuthorTable.Designer.cs | 243 ------- .../20231214021324_AddedAuthorTable.cs | 96 --- ...2919_RemovedAuthorFromArticles.Designer.cs | 229 ------- ...0231216012919_RemovedAuthorFromArticles.cs | 50 -- ...ToArtcicleStatusInArtcileTable.Designer.cs | 229 ------- ...IsVisibleToArtcicleStatusInArtcileTable.cs | 40 -- ...nAndRevisionTitleAndCategories.Designer.cs | 263 -------- ...leIsHiddenAndRevisionTitleAndCategories.cs | 84 --- ...0240116221526_AddedReviewTable.Designer.cs | 319 ---------- .../20240116221526_AddedReviewTable.cs | 132 ---- ...tRevisionIdForeignKeyToArticle.Designer.cs | 320 ---------- ...dedCurrentRevisionIdForeignKeyToArticle.cs | 80 --- ...rticleRemovedTitleFromRevision.Designer.cs | 309 --------- ...ddenFromArticleRemovedTitleFromRevision.cs | 40 -- ...nyBetweenArticlesAndCategories.Designer.cs | 275 -------- ...dManyToManyBetweenArticlesAndCategories.cs | 50 -- ...0240120201614_AddedNavigations.Designer.cs | 318 --------- .../20240120201614_AddedNavigations.cs | 49 -- ...edNavigationInconToNavigations.Designer.cs | 321 ---------- ...44604_AddedNavigationInconToNavigations.cs | 28 - ...ongForeignKeyNameInNavigations.Designer.cs | 321 ---------- ...2_FixedWrongForeignKeyNameInNavigations.cs | 69 -- ...rigramGistIndexForArticleTitle.Designer.cs | 327 ---------- ...dCreatedTrigramGistIndexForArticleTitle.cs | 35 - ...ationsForNavigationNameIconUri.Designer.cs | 330 ---------- ...lumnLimitationsForNavigationNameIconUri.cs | 76 --- ...010_AddedAuthorsNoteToRevision.Designer.cs | 334 ---------- ...240606134010_AddedAuthorsNoteToRevision.cs | 29 - .../20250808004657_Initial.Designer.cs | 602 ++++++++++++++++++ .../Data/Migrations/20250808004657_Initial.cs | 443 +++++++++++++ .../ApplicationDbContextModelSnapshot.cs | 312 ++++++++- .../Extensions/ServiceCollectionExt.cs | 46 +- Infrastructure/Infrastructure.csproj | 2 - WebApi/Auth/JwtBearerExtensions.cs | 87 +++ WebApi/Auth/JwtSettings.cs | 16 + WebApi/Auth/MinimalApiExtensions.cs | 11 + WebApi/Auth/PermissionRequirement.cs | 45 ++ WebApi/Auth/RequirePermissionAttribute.cs | 13 + WebApi/Auth/UserContext.cs | 38 ++ .../Extensions/ServiceCollectionExtensions.cs | 13 +- WebApi/Features/Articles/ArticlesModule.cs | 18 +- .../Features/Categories/CategoriesModule.cs | 8 +- .../Features/Navigations/NavigationsModule.cs | 6 +- WebApi/Features/Users/UsersModule.cs | 27 + WebApi/Program.cs | 48 +- WebApi/WebApi.csproj | 1 + WebApi/appsettings.Development.json | 11 + WebApi/appsettings.json | 22 +- 96 files changed, 2079 insertions(+), 6445 deletions(-) delete mode 100644 Application/Authorization/Abstractions/ICurrentUserService.cs delete mode 100644 Application/Authorization/Abstractions/IIdentityService.cs create mode 100644 Application/Authorization/Abstractions/IPermissionService.cs create mode 100644 Application/Authorization/Abstractions/IUserContext.cs create mode 100644 Application/Authorization/PermissionService.cs delete mode 100644 Application/Common/Constants/AuthorizationConstants.cs delete mode 100644 Application/Features/Authors/CreateAuthor/CreateAuthorResponse.cs delete mode 100644 Application/Features/Authors/EditAuthor/EditAuthorResponse.cs delete mode 100644 Application/Features/Authors/Errors/Author.cs create mode 100644 Application/Features/Users/Errors/User.cs create mode 100644 Application/Features/Users/GetUser/GetUserCommand.cs create mode 100644 Application/Features/Users/GetUser/GetUserCommandHandler.cs create mode 100644 Application/Features/Users/GetUser/GetUserResponse.cs create mode 100644 Application/Features/Users/GetUser/IGetUserCommandHandler.cs create mode 100644 Application/Features/Users/RenameUser/IRenameUserCommandHandler.cs create mode 100644 Application/Features/Users/RenameUser/RenameUserCommand.cs create mode 100644 Application/Features/Users/RenameUser/RenameUserCommandHandler.cs create mode 100644 Application/Features/Users/RenameUser/RenameUserResponse.cs delete mode 100644 Domain/Entities/Author.cs create mode 100644 Domain/Entities/Permission.cs create mode 100644 Domain/Entities/Role.cs create mode 100644 Domain/Entities/User.cs create mode 100644 Domain/Rbac/Permissions.cs create mode 100644 Domain/Rbac/Roles.cs delete mode 100644 Infrastructure/Authorization/CurrentUserService.cs delete mode 100644 Infrastructure/Authorization/IdentityService.cs delete mode 100644 Infrastructure/Data/Migrations/20231205172438_Initial.Designer.cs delete mode 100644 Infrastructure/Data/Migrations/20231205172438_Initial.cs delete mode 100644 Infrastructure/Data/Migrations/20231207214658_SeparatedIdAndDisplaynameForArticleAndCategory.Designer.cs delete mode 100644 Infrastructure/Data/Migrations/20231207214658_SeparatedIdAndDisplaynameForArticleAndCategory.cs delete mode 100644 Infrastructure/Data/Migrations/20231207224020_ChangedCategoryParentOnDeleteBehaviorToSetNull.Designer.cs delete mode 100644 Infrastructure/Data/Migrations/20231207224020_ChangedCategoryParentOnDeleteBehaviorToSetNull.cs delete mode 100644 Infrastructure/Data/Migrations/20231207225909_ChangedArticleRedirectArticleOnDeleteBehaviorToCascade.Designer.cs delete mode 100644 Infrastructure/Data/Migrations/20231207225909_ChangedArticleRedirectArticleOnDeleteBehaviorToCascade.cs delete mode 100644 Infrastructure/Data/Migrations/20231214021324_AddedAuthorTable.Designer.cs delete mode 100644 Infrastructure/Data/Migrations/20231214021324_AddedAuthorTable.cs delete mode 100644 Infrastructure/Data/Migrations/20231216012919_RemovedAuthorFromArticles.Designer.cs delete mode 100644 Infrastructure/Data/Migrations/20231216012919_RemovedAuthorFromArticles.cs delete mode 100644 Infrastructure/Data/Migrations/20231220205543_ReplacesIsVisibleToArtcicleStatusInArtcileTable.Designer.cs delete mode 100644 Infrastructure/Data/Migrations/20231220205543_ReplacesIsVisibleToArtcicleStatusInArtcileTable.cs delete mode 100644 Infrastructure/Data/Migrations/20231222163420_AddedArticleIsHiddenAndRevisionTitleAndCategories.Designer.cs delete mode 100644 Infrastructure/Data/Migrations/20231222163420_AddedArticleIsHiddenAndRevisionTitleAndCategories.cs delete mode 100644 Infrastructure/Data/Migrations/20240116221526_AddedReviewTable.Designer.cs delete mode 100644 Infrastructure/Data/Migrations/20240116221526_AddedReviewTable.cs delete mode 100644 Infrastructure/Data/Migrations/20240117022728_RemovedRevisionSelfReferencingAndAddedCurrentRevisionIdForeignKeyToArticle.Designer.cs delete mode 100644 Infrastructure/Data/Migrations/20240117022728_RemovedRevisionSelfReferencingAndAddedCurrentRevisionIdForeignKeyToArticle.cs delete mode 100644 Infrastructure/Data/Migrations/20240119042942_RemovedIsHiddenFromArticleRemovedTitleFromRevision.Designer.cs delete mode 100644 Infrastructure/Data/Migrations/20240119042942_RemovedIsHiddenFromArticleRemovedTitleFromRevision.cs delete mode 100644 Infrastructure/Data/Migrations/20240120033142_RemovedManyToManyBetweenArticlesAndCategories.Designer.cs delete mode 100644 Infrastructure/Data/Migrations/20240120033142_RemovedManyToManyBetweenArticlesAndCategories.cs delete mode 100644 Infrastructure/Data/Migrations/20240120201614_AddedNavigations.Designer.cs delete mode 100644 Infrastructure/Data/Migrations/20240120201614_AddedNavigations.cs delete mode 100644 Infrastructure/Data/Migrations/20240122144604_AddedNavigationInconToNavigations.Designer.cs delete mode 100644 Infrastructure/Data/Migrations/20240122144604_AddedNavigationInconToNavigations.cs delete mode 100644 Infrastructure/Data/Migrations/20240122175622_FixedWrongForeignKeyNameInNavigations.Designer.cs delete mode 100644 Infrastructure/Data/Migrations/20240122175622_FixedWrongForeignKeyNameInNavigations.cs delete mode 100644 Infrastructure/Data/Migrations/20240130191200_EnabledTrigramExtensionAndCreatedTrigramGistIndexForArticleTitle.Designer.cs delete mode 100644 Infrastructure/Data/Migrations/20240130191200_EnabledTrigramExtensionAndCreatedTrigramGistIndexForArticleTitle.cs delete mode 100644 Infrastructure/Data/Migrations/20240318004044_AddedLengthColumnLimitationsForNavigationNameIconUri.Designer.cs delete mode 100644 Infrastructure/Data/Migrations/20240318004044_AddedLengthColumnLimitationsForNavigationNameIconUri.cs delete mode 100644 Infrastructure/Data/Migrations/20240606134010_AddedAuthorsNoteToRevision.Designer.cs delete mode 100644 Infrastructure/Data/Migrations/20240606134010_AddedAuthorsNoteToRevision.cs create mode 100644 Infrastructure/Data/Migrations/20250808004657_Initial.Designer.cs create mode 100644 Infrastructure/Data/Migrations/20250808004657_Initial.cs create mode 100644 WebApi/Auth/JwtBearerExtensions.cs create mode 100644 WebApi/Auth/JwtSettings.cs create mode 100644 WebApi/Auth/MinimalApiExtensions.cs create mode 100644 WebApi/Auth/PermissionRequirement.cs create mode 100644 WebApi/Auth/RequirePermissionAttribute.cs create mode 100644 WebApi/Auth/UserContext.cs create mode 100644 WebApi/Features/Users/UsersModule.cs diff --git a/Application.Tests.Unit/Articles/CreateArticleCommandHandlerTests.cs b/Application.Tests.Unit/Articles/CreateArticleCommandHandlerTests.cs index 9ef78d6..f5d430e 100644 --- a/Application.Tests.Unit/Articles/CreateArticleCommandHandlerTests.cs +++ b/Application.Tests.Unit/Articles/CreateArticleCommandHandlerTests.cs @@ -13,7 +13,7 @@ public class CreateArticleCommandHandlerTests { private readonly Mock _mockDbContext = new(); private readonly Mock _mockSlugHelper = new(); - private readonly Mock _mockCurrentUserService = new(); + private readonly Mock _mockUserContext = new(); private readonly Mock> _mockValidator = new(); private readonly CreateArticleCommandHandler _handler; @@ -22,7 +22,7 @@ public CreateArticleCommandHandlerTests() _handler = new CreateArticleCommandHandler( _mockDbContext.Object, _mockSlugHelper.Object, - _mockCurrentUserService.Object, + _mockUserContext.Object, _mockValidator.Object ); } @@ -206,7 +206,7 @@ public async void Handle_Should_CreateRevisionWithContent_WhenGeneratedIdIsUniqu _mockDbContext.Setup(x => x.Categories).Returns(mockCategoriesDbSet.Object); _mockDbContext.Setup(x => x.Revisions).Returns(mockRevisionsDbSet.Object); _mockSlugHelper.Setup(x => x.GenerateSlug("something extra cool")).Returns("something-extra-cool"); - _mockCurrentUserService.Setup(x => x.UserId).Returns("test-user-id"); + _mockUserContext.Setup(x => x.UserId).Returns(Guid.Empty); _mockValidator.Setup(v => v.Validate(It.IsAny())) .Returns(new ValidationResult()); var expectedCategoryIds = new List {"category1", "category2"}; @@ -222,7 +222,7 @@ public async void Handle_Should_CreateRevisionWithContent_WhenGeneratedIdIsUniqu e.ArticleId == result.Value.Id && e.Content == command.Content && e.AuthorsNote == command.AuthorsNote - && e.AuthorId == _mockCurrentUserService.Object.UserId + && e.AuthorId == _mockUserContext.Object.UserId && e.Categories.Select(i => i.Id).OrderBy(i => i).SequenceEqual(expectedCategoryIds) ), It.IsAny() diff --git a/Application/Authorization/Abstractions/ICurrentUserService.cs b/Application/Authorization/Abstractions/ICurrentUserService.cs deleted file mode 100644 index d2690a9..0000000 --- a/Application/Authorization/Abstractions/ICurrentUserService.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Security.Claims; - -namespace Application.Authorization.Abstractions; - -public interface ICurrentUserService -{ - string? UserId { get; } - string? UserName { get; } - ClaimsPrincipal? Principal { get; } -} \ No newline at end of file diff --git a/Application/Authorization/Abstractions/IIdentityService.cs b/Application/Authorization/Abstractions/IIdentityService.cs deleted file mode 100644 index c70c5bd..0000000 --- a/Application/Authorization/Abstractions/IIdentityService.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Application.Authorization.Abstractions; - -public interface IIdentityService : ICurrentUserService -{ - Task AuthorizeAsync(string policy); - Task IsInRoleAsync(string role); - Task?> Roles(); -} \ No newline at end of file diff --git a/Application/Authorization/Abstractions/IPermissionService.cs b/Application/Authorization/Abstractions/IPermissionService.cs new file mode 100644 index 0000000..c3f858a --- /dev/null +++ b/Application/Authorization/Abstractions/IPermissionService.cs @@ -0,0 +1,12 @@ +using Domain.Entities; + +namespace Application.Authorization.Abstractions; + +public interface IPermissionService +{ + Task HasPermissionAsync(Guid userId, Guid permissionId); + Task HasPermissionAsync(Guid userId, Permission permission); + Task HasAnyPermissionsAsync(Guid userId, IEnumerable permissionIds); + Task HasAnyPermissionsAsync(Guid userId, IEnumerable permissions); + Task> GetPermissionsASync(Guid userId); +} \ No newline at end of file diff --git a/Application/Authorization/Abstractions/IUserContext.cs b/Application/Authorization/Abstractions/IUserContext.cs new file mode 100644 index 0000000..dc23e81 --- /dev/null +++ b/Application/Authorization/Abstractions/IUserContext.cs @@ -0,0 +1,9 @@ +using Domain.Entities; + +namespace Application.Authorization.Abstractions; + +public interface IUserContext +{ + Guid? UserId { get; } + Task GetCurrentUserAsync(); +} \ No newline at end of file diff --git a/Application/Authorization/PermissionService.cs b/Application/Authorization/PermissionService.cs new file mode 100644 index 0000000..5400f7d --- /dev/null +++ b/Application/Authorization/PermissionService.cs @@ -0,0 +1,46 @@ +using Application.Authorization.Abstractions; +using Application.Data; +using Domain.Entities; +using Microsoft.EntityFrameworkCore; + +namespace Application.Authorization; + +public class PermissionService : IPermissionService +{ + private readonly IApplicationDbContext _dbContext; + + public PermissionService(IApplicationDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task> GetPermissionsASync(Guid userId) + { + return await _dbContext.Users + .Where(a => a.Id == userId) + .SelectMany(a => a.Roles) + .SelectMany(r => r.Permissions) + .DistinctBy(p => p.Id) + .ToListAsync(); + } + + public async Task HasPermissionAsync(Guid userId, Guid permissionId) + { + return await _dbContext.Users + .Where(a => a.Id == userId) + .SelectMany(a => a.Roles) + .AnyAsync(r => r.Permissions.Any(p => p.Id == permissionId)); + } + + public async Task HasPermissionAsync(Guid userId, Permission permission) => await HasPermissionAsync(userId, permission.Id); + + public async Task HasAnyPermissionsAsync(Guid userId, IEnumerable permissionIds) + { + return await _dbContext.Users + .Where(a => a.Id == userId) + .SelectMany(a => a.Roles) + .AnyAsync(r => r.Permissions.Any(p => permissionIds.Contains(p.Id))); + } + + public async Task HasAnyPermissionsAsync(Guid userId, IEnumerable permissions) => await HasAnyPermissionsAsync(userId, permissions.Select(p => p.Id)); +} \ No newline at end of file diff --git a/Application/Common/Constants/AuthorizationConstants.cs b/Application/Common/Constants/AuthorizationConstants.cs deleted file mode 100644 index eff7ceb..0000000 --- a/Application/Common/Constants/AuthorizationConstants.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Application.Common.Constants; - -public static class AuthorizationConstants -{ - public static class Roles - { - public const string Admin = "admin-role"; - public const string Editor = "editor-role"; - public const string User = "user-role"; - } -} \ No newline at end of file diff --git a/Application/Data/IApplicationDbContext.cs b/Application/Data/IApplicationDbContext.cs index b408f65..b5d85e7 100644 --- a/Application/Data/IApplicationDbContext.cs +++ b/Application/Data/IApplicationDbContext.cs @@ -9,8 +9,10 @@ public interface IApplicationDbContext public DbSet Categories { get; set; } public DbSet Revisions { get; set; } public DbSet Reviews { get; set; } - public DbSet Authors { get; set; } + public DbSet Users { get; set; } public DbSet Navigations { get; set; } + public DbSet Roles { get; set; } + public DbSet Permissions { get; set; } Task SaveChangesAsync(CancellationToken token = default); } \ No newline at end of file diff --git a/Application/Extensions/ServiceCollectionExt.cs b/Application/Extensions/ServiceCollectionExt.cs index 13d3339..bbf1244 100644 --- a/Application/Extensions/ServiceCollectionExt.cs +++ b/Application/Extensions/ServiceCollectionExt.cs @@ -1,5 +1,7 @@ using System.Reflection; using Application.Common.Messaging; +using Application.Authorization; +using Application.Authorization.Abstractions; using Application.Features.Articles.CreateArticle; using Application.Features.Articles.DeleteArticle; using Application.Features.Articles.EditArticle; @@ -11,8 +13,6 @@ using Application.Features.Articles.ReviewRevision; using Application.Features.Articles.SearchArticles; using Application.Features.Articles.SetRedirect; -using Application.Features.Authors.CreateAuthor; -using Application.Features.Authors.EditAuthor; using Application.Features.Categories.CreateCategory; using Application.Features.Categories.DeleteCategory; using Application.Features.Categories.GetCategories; @@ -20,6 +20,8 @@ using Application.Features.Categories.GetCategoryArticles; using Application.Features.Navigations.GetNavigationTree; using Application.Features.Navigations.UpdateNavigationsTree; +using Application.Features.Users.GetUser; +using Application.Features.Users.RenameUser; using FluentValidation; using Microsoft.Extensions.DependencyInjection; using Slugify; @@ -33,6 +35,8 @@ public static IServiceCollection AddApplicationServices(this IServiceCollection services.AddValidatorsFromAssembly(Assembly.GetExecutingAssembly()); services.AddTransient(); + services.AddScoped(); + services.AddScoped, CreateArticleCommandHandler>(); services.AddScoped, DeleteArticleCommandHandler>(); services.AddScoped, EditArticleCommandHandler>(); @@ -45,8 +49,8 @@ public static IServiceCollection AddApplicationServices(this IServiceCollection services.AddScoped, SearchArticlesQueryHandler>(); services.AddScoped, SetRedirectCommandHandler>(); - services.AddScoped, CreateAuthorCommandHandler>(); - services.AddScoped, EditAuthorCommandHandler>(); + services.AddScoped(); + services.AddScoped(); services.AddScoped, CreateCategoryCommandHandler>(); services.AddScoped, DeleteCategoryCommandHandler>(); diff --git a/Application/Features/Articles/CreateArticle/CreateArticleCommandHandler.cs b/Application/Features/Articles/CreateArticle/CreateArticleCommandHandler.cs index bbb2574..ee807a9 100644 --- a/Application/Features/Articles/CreateArticle/CreateArticleCommandHandler.cs +++ b/Application/Features/Articles/CreateArticle/CreateArticleCommandHandler.cs @@ -13,7 +13,7 @@ namespace Application.Features.Articles.CreateArticle; public class CreateArticleCommandHandler( IApplicationDbContext dbContext, ISlugHelper slugHelper, - ICurrentUserService identityService, + IUserContext userContext, IValidator validator ) : ICommandHandler { @@ -47,7 +47,7 @@ public async Task> HandleAsync(CreateArticleComma Id = default!, ArticleId = id, Article = default!, - AuthorId = identityService.UserId!, + AuthorId = userContext.UserId!.Value, Author = default!, AuthorsNote = command.AuthorsNote, Content = command.Content, diff --git a/Application/Features/Articles/EditArticle/EditArticleCommandHandler.cs b/Application/Features/Articles/EditArticle/EditArticleCommandHandler.cs index e2fd8c9..e3f9be1 100644 --- a/Application/Features/Articles/EditArticle/EditArticleCommandHandler.cs +++ b/Application/Features/Articles/EditArticle/EditArticleCommandHandler.cs @@ -11,7 +11,7 @@ namespace Application.Features.Articles.EditArticle; public class EditArticleCommandHandler( IApplicationDbContext dbContext, - ICurrentUserService identityService, + IUserContext userContext, IValidator validator ) : ICommandHandler { @@ -38,7 +38,7 @@ public async Task> HandleAsync(EditArticleCommand r Id = default!, ArticleId = article.Id, Article = default!, - AuthorId = identityService.UserId!, + AuthorId = userContext.UserId!.Value, Author = default!, AuthorsNote = request.AuthorsNote, Content = request.Content, diff --git a/Application/Features/Articles/GetArticle/GetArticleQueryHandler.cs b/Application/Features/Articles/GetArticle/GetArticleQueryHandler.cs index 7251b71..c9a23fa 100644 --- a/Application/Features/Articles/GetArticle/GetArticleQueryHandler.cs +++ b/Application/Features/Articles/GetArticle/GetArticleQueryHandler.cs @@ -5,6 +5,7 @@ using Application.Common.Utils; using Application.Data; using Domain.Entities; +using Domain.Rbac; using ErrorOr; using FluentValidation; using Microsoft.EntityFrameworkCore; @@ -13,7 +14,8 @@ namespace Application.Features.Articles.GetArticle; public class GetArticleQueryHandler( IApplicationDbContext dbContext, - IIdentityService identityService, + IUserContext userContext, + IPermissionService permissionService, IValidator validator, ICacheService cacheService ) : IQueryHandler @@ -66,13 +68,14 @@ private async Task> GetByArticleId(string id, Cancel private async Task> GetByRevisionId(Guid id, CancellationToken token) { - var isInRole = await identityService.IsInRoleAsync(AuthorizationConstants.Roles.Editor) || - await identityService.IsInRoleAsync(AuthorizationConstants.Roles.Admin); + var canSeePendingRevisions = + await permissionService.HasPermissionAsync(userContext.UserId!.Value, Permissions.ArticleSeePendingRevisions); + var revision = await dbContext.Revisions .Include(e => e.LatestReview) .Include(e => e.Article) .AsNoTracking() - .FirstOrDefaultAsync(e => e.Id == id && (isInRole || e.LatestReview != null), token); + .FirstOrDefaultAsync(e => e.Id == id && (canSeePendingRevisions || e.LatestReview != null), token); if (revision == null) return Errors.Revision.NotFound; diff --git a/Application/Features/Articles/GetArticle/GetArticleResponse.cs b/Application/Features/Articles/GetArticle/GetArticleResponse.cs index 5a4bb70..5d51840 100644 --- a/Application/Features/Articles/GetArticle/GetArticleResponse.cs +++ b/Application/Features/Articles/GetArticle/GetArticleResponse.cs @@ -14,7 +14,7 @@ public record GetArticleResponse( List Categories ) { - public record Author(string Id, string Name); + public record Author(Guid Id, string Name); public record Category(string Id, string Name); } \ No newline at end of file diff --git a/Application/Features/Articles/GetPendingRevisions/GetPendingRevisionsResponse.cs b/Application/Features/Articles/GetPendingRevisions/GetPendingRevisionsResponse.cs index a873859..6d6179a 100644 --- a/Application/Features/Articles/GetPendingRevisions/GetPendingRevisionsResponse.cs +++ b/Application/Features/Articles/GetPendingRevisions/GetPendingRevisionsResponse.cs @@ -11,5 +11,5 @@ public record Element( DateTime Timestamp ); - public record Author(string Id, string Name); + public record Author(Guid Id, string Name); } \ No newline at end of file diff --git a/Application/Features/Articles/GetRevisionHistory/GetRevisionHistoryResponse.cs b/Application/Features/Articles/GetRevisionHistory/GetRevisionHistoryResponse.cs index 3116427..c66f707 100644 --- a/Application/Features/Articles/GetRevisionHistory/GetRevisionHistoryResponse.cs +++ b/Application/Features/Articles/GetRevisionHistory/GetRevisionHistoryResponse.cs @@ -12,7 +12,7 @@ public record Element( Review? LatestReview ); - public record Author(string Id, string Name); + public record Author(Guid Id, string Name); public record Review( Guid Id, diff --git a/Application/Features/Articles/GetRevisionReviewHistory/GetRevisionReviewHistoryResponse.cs b/Application/Features/Articles/GetRevisionReviewHistory/GetRevisionReviewHistoryResponse.cs index e3eff82..543aeb9 100644 --- a/Application/Features/Articles/GetRevisionReviewHistory/GetRevisionReviewHistoryResponse.cs +++ b/Application/Features/Articles/GetRevisionReviewHistory/GetRevisionReviewHistoryResponse.cs @@ -12,5 +12,5 @@ public record Element( DateTime Timestamp ); - public record Reviewer(string Id, string Name); + public record Reviewer(Guid Id, string Name); } \ No newline at end of file diff --git a/Application/Features/Articles/ReviewRevision/ReviewRevisionCommandHandler.cs b/Application/Features/Articles/ReviewRevision/ReviewRevisionCommandHandler.cs index 080c655..ba7fa65 100644 --- a/Application/Features/Articles/ReviewRevision/ReviewRevisionCommandHandler.cs +++ b/Application/Features/Articles/ReviewRevision/ReviewRevisionCommandHandler.cs @@ -13,7 +13,7 @@ namespace Application.Features.Articles.ReviewRevision; public class ReviewRevisionCommandHandler( IApplicationDbContext dbContext, - ICurrentUserService identityService, + IUserContext userContext, ICacheService cacheService, IValidator validator ) : ICommandHandler @@ -42,7 +42,7 @@ public async Task> HandleAsync(ReviewRevisionCom var review = new Review { Id = default!, - ReviewerId = identityService.UserId!, + ReviewerId = userContext.UserId!.Value, Reviewer = default!, Status = command.Status, Message = command.Review, diff --git a/Application/Features/Authors/CreateAuthor/CreateAuthorResponse.cs b/Application/Features/Authors/CreateAuthor/CreateAuthorResponse.cs deleted file mode 100644 index 0fc12ef..0000000 --- a/Application/Features/Authors/CreateAuthor/CreateAuthorResponse.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace Application.Features.Authors.CreateAuthor; - -public record CreateAuthorResponse(string Id); \ No newline at end of file diff --git a/Application/Features/Authors/EditAuthor/EditAuthorResponse.cs b/Application/Features/Authors/EditAuthor/EditAuthorResponse.cs deleted file mode 100644 index 372dae0..0000000 --- a/Application/Features/Authors/EditAuthor/EditAuthorResponse.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace Application.Features.Authors.EditAuthor; - -public record EditAuthorResponse(string Id); \ No newline at end of file diff --git a/Application/Features/Authors/Errors/Author.cs b/Application/Features/Authors/Errors/Author.cs deleted file mode 100644 index ac7347b..0000000 --- a/Application/Features/Authors/Errors/Author.cs +++ /dev/null @@ -1,16 +0,0 @@ -using ErrorOr; - -namespace Application.Features.Authors.Errors; - -public static class Author -{ - public static readonly Error NotFound = Error.NotFound( - "Author.NotFound", - "Author with this ID was not found." - ); - - public static readonly Error DuplicateId = Error.Conflict( - "Author.DuplicateId", - "Author with this ID already exists." - ); -} \ No newline at end of file diff --git a/Application/Features/Users/Errors/User.cs b/Application/Features/Users/Errors/User.cs new file mode 100644 index 0000000..2e9de6e --- /dev/null +++ b/Application/Features/Users/Errors/User.cs @@ -0,0 +1,16 @@ +using ErrorOr; + +namespace Application.Features.Users.Errors; + +public static class User +{ + public static readonly Error NotFound = Error.NotFound( + "User.NotFound", + "User with this ID was not found." + ); + + public static readonly Error DuplicateExternalId = Error.Conflict( + "User.DuplicateId", + "User with this external ID already exists." + ); +} \ No newline at end of file diff --git a/Application/Features/Users/GetUser/GetUserCommand.cs b/Application/Features/Users/GetUser/GetUserCommand.cs new file mode 100644 index 0000000..5446b86 --- /dev/null +++ b/Application/Features/Users/GetUser/GetUserCommand.cs @@ -0,0 +1,3 @@ +namespace Application.Features.Users.GetUser; + +public record GetUserCommand(Guid Id); \ No newline at end of file diff --git a/Application/Features/Users/GetUser/GetUserCommandHandler.cs b/Application/Features/Users/GetUser/GetUserCommandHandler.cs new file mode 100644 index 0000000..874a1d4 --- /dev/null +++ b/Application/Features/Users/GetUser/GetUserCommandHandler.cs @@ -0,0 +1,35 @@ +using Application.Data; +using Application.Features.Users.Errors; +using ErrorOr; +using Microsoft.EntityFrameworkCore; + +namespace Application.Features.Users.GetUser; + +public class GetUserCommandHandler(IApplicationDbContext dbContext) : IGetUserCommandHandler +{ + public async Task> Handle(GetUserCommand command, CancellationToken token) + { + var user = await dbContext.Users + .Include(u => u.Roles) + .ThenInclude(u => u.Permissions) + .AsNoTracking() + .FirstOrDefaultAsync(e => e.Id == command.Id, token); + + if (user == null) return User.NotFound; + + var roleIds = user.Roles.Select(r => r.Id).ToList(); + + var permissionIds = user.Roles + .SelectMany(r => r.Permissions) + .Select(p => p.Id) + .Distinct() + .ToList(); + + return new GetUserResponse( + user.Id, + user.Name, + roleIds, + permissionIds + ); + } +} \ No newline at end of file diff --git a/Application/Features/Users/GetUser/GetUserResponse.cs b/Application/Features/Users/GetUser/GetUserResponse.cs new file mode 100644 index 0000000..fc308f6 --- /dev/null +++ b/Application/Features/Users/GetUser/GetUserResponse.cs @@ -0,0 +1,3 @@ +namespace Application.Features.Users.GetUser; + +public record GetUserResponse(Guid Id, string Name, IEnumerable RoleIds, IEnumerable PermissionIds); \ No newline at end of file diff --git a/Application/Features/Users/GetUser/IGetUserCommandHandler.cs b/Application/Features/Users/GetUser/IGetUserCommandHandler.cs new file mode 100644 index 0000000..3262944 --- /dev/null +++ b/Application/Features/Users/GetUser/IGetUserCommandHandler.cs @@ -0,0 +1,7 @@ +namespace Application.Features.Users.GetUser; +using ErrorOr; + +public interface IGetUserCommandHandler +{ + Task> Handle(GetUserCommand command, CancellationToken token); +} \ No newline at end of file diff --git a/Application/Features/Users/RenameUser/IRenameUserCommandHandler.cs b/Application/Features/Users/RenameUser/IRenameUserCommandHandler.cs new file mode 100644 index 0000000..afe9536 --- /dev/null +++ b/Application/Features/Users/RenameUser/IRenameUserCommandHandler.cs @@ -0,0 +1,8 @@ +using ErrorOr; + +namespace Application.Features.Users.RenameUser; + +public interface IRenameUserCommandHandler +{ + Task> Handle(RenameUserCommand command, CancellationToken token); +} \ No newline at end of file diff --git a/Application/Features/Users/RenameUser/RenameUserCommand.cs b/Application/Features/Users/RenameUser/RenameUserCommand.cs new file mode 100644 index 0000000..f40a84e --- /dev/null +++ b/Application/Features/Users/RenameUser/RenameUserCommand.cs @@ -0,0 +1,3 @@ +namespace Application.Features.Users.RenameUser; + +public record RenameUserCommand(Guid Id, string Name); \ No newline at end of file diff --git a/Application/Features/Users/RenameUser/RenameUserCommandHandler.cs b/Application/Features/Users/RenameUser/RenameUserCommandHandler.cs new file mode 100644 index 0000000..c9b64bf --- /dev/null +++ b/Application/Features/Users/RenameUser/RenameUserCommandHandler.cs @@ -0,0 +1,24 @@ +using Application.Data; +using Application.Features.Users.Errors; +using ErrorOr; +using Microsoft.EntityFrameworkCore; + +namespace Application.Features.Users.RenameUser; + +public class RenameUserCommandHandler( + IApplicationDbContext dbContext +) : IRenameUserCommandHandler +{ + public async Task> Handle(RenameUserCommand command, CancellationToken token) + { + var author = await dbContext.Users.FirstOrDefaultAsync(e => e.Id == command.Id, token); + + if (author == null) return User.NotFound; + + author.Name = command.Name; + + await dbContext.SaveChangesAsync(token); + + return new RenameUserResponse(author.Id); + } +} \ No newline at end of file diff --git a/Application/Features/Users/RenameUser/RenameUserResponse.cs b/Application/Features/Users/RenameUser/RenameUserResponse.cs new file mode 100644 index 0000000..5e22872 --- /dev/null +++ b/Application/Features/Users/RenameUser/RenameUserResponse.cs @@ -0,0 +1,3 @@ +namespace Application.Features.Users.RenameUser; + +public record RenameUserResponse(Guid Id); \ No newline at end of file diff --git a/Domain/Entities/Author.cs b/Domain/Entities/Author.cs deleted file mode 100644 index dc8dcd0..0000000 --- a/Domain/Entities/Author.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Domain.Contracts; - -namespace Domain.Entities; - -public class Author : BaseEntity -{ - public required string Name { get; set; } -} \ No newline at end of file diff --git a/Domain/Entities/Permission.cs b/Domain/Entities/Permission.cs new file mode 100644 index 0000000..ea2cfa7 --- /dev/null +++ b/Domain/Entities/Permission.cs @@ -0,0 +1,10 @@ +using Domain.Contracts; + +namespace Domain.Entities; + +public class Permission : BaseEntity +{ + public required string Name { get; set; } + + public virtual ICollection Roles { get; set; } = new List(); +} \ No newline at end of file diff --git a/Domain/Entities/Review.cs b/Domain/Entities/Review.cs index 5dfa308..4a33d03 100644 --- a/Domain/Entities/Review.cs +++ b/Domain/Entities/Review.cs @@ -4,8 +4,8 @@ namespace Domain.Entities; public class Review : BaseEntity { - public required string ReviewerId { get; set; } - public required Author Reviewer { get; set; } + public required Guid ReviewerId { get; set; } + public required User Reviewer { get; set; } public ReviewStatus Status { get; set; } public required string Message { get; set; } public DateTime ReviewTimestamp { get; set; } diff --git a/Domain/Entities/Revision.cs b/Domain/Entities/Revision.cs index bc2694f..7c47f08 100644 --- a/Domain/Entities/Revision.cs +++ b/Domain/Entities/Revision.cs @@ -8,8 +8,8 @@ public class Revision : BaseEntity public virtual required Article Article { get; set; } public required string Content { get; set; } public virtual ICollection Categories { get; set; } = new List(); - public required string AuthorId { get; set; } - public virtual required Author Author { get; set; } + public required Guid AuthorId { get; set; } + public virtual required User Author { get; set; } public required string AuthorsNote { get; set; } public DateTime Timestamp { get; set; } public Guid? LatestReviewId { get; set; } diff --git a/Domain/Entities/Role.cs b/Domain/Entities/Role.cs new file mode 100644 index 0000000..a3eef3e --- /dev/null +++ b/Domain/Entities/Role.cs @@ -0,0 +1,12 @@ +using Domain.Contracts; + +namespace Domain.Entities; + +public class Role : BaseEntity +{ + public required string Name { get; set; } + + public virtual ICollection Permissions { get; set; } = new List(); + + public virtual ICollection Users { get; set; } = new List(); +} \ No newline at end of file diff --git a/Domain/Entities/User.cs b/Domain/Entities/User.cs new file mode 100644 index 0000000..f00a913 --- /dev/null +++ b/Domain/Entities/User.cs @@ -0,0 +1,10 @@ +using Domain.Contracts; + +namespace Domain.Entities; + +public class User : BaseEntity +{ + public required string ExternalId { get; set; } + public required string Name { get; set; } + public virtual ICollection Roles { get; set; } = new List(); +} \ No newline at end of file diff --git a/Domain/Rbac/Permissions.cs b/Domain/Rbac/Permissions.cs new file mode 100644 index 0000000..c75d9c5 --- /dev/null +++ b/Domain/Rbac/Permissions.cs @@ -0,0 +1,73 @@ +using Domain.Entities; + +namespace Domain.Rbac; + +public static class Permissions +{ + public static Permission ArticleCreate = new() + { + Id = new Guid("5744B9A3-2256-4F69-B6AC-4D8C164632C9"), + Name = nameof(ArticleCreate), + }; + + public static Permission ArticleEdit = new() + { + Id = new Guid("861B96A8-9A42-45E4-B1C2-733EF85BC2D6"), + Name = nameof(ArticleEdit), + }; + + public static Permission ArticleSetRedirect = new() + { + Id = new Guid("7D9BBC60-F1C8-4762-AC1D-EDAC85F22B48"), + Name = nameof(ArticleSetRedirect), + }; + + public static Permission ArticleSeePendingRevisions = new() + { + Id = new Guid("27C68A58-6F91-41A2-BD36-C4DDA391F309"), + Name = nameof(ArticleSeePendingRevisions), + }; + + public static Permission ArticleReviewRevision = new() + { + Id = new Guid("76DA2206-A875-47B2-86BB-27ECD0E9F4B9"), + Name = nameof(ArticleReviewRevision), + }; + + public static Permission ArticleDelete = new() + { + Id = new Guid("E4D1AFF3-8BC1-4730-B95D-438F1FDE6AEB"), + Name = nameof(ArticleDelete), + }; + + public static Permission CategoryCreate = new() + { + Id = new Guid("95571FEE-1313-41DE-ADFE-6B65FE53760B"), + Name = nameof(CategoryCreate), + }; + + public static Permission CategoryDelete = new() + { + Id = new Guid("FA7C5B48-0FB4-4757-96A7-014B00FDD78B"), + Name = nameof(CategoryDelete), + }; + + public static Permission NavigationsUpdateTree = new() + { + Id = new Guid("ECED433F-B27C-4C6E-AF41-D2231FB40F03"), + Name = nameof(NavigationsUpdateTree), + }; + + public static IReadOnlyCollection All => + [ + ArticleCreate, + ArticleEdit, + ArticleSetRedirect, + ArticleSeePendingRevisions, + ArticleReviewRevision, + ArticleDelete, + CategoryCreate, + CategoryDelete, + NavigationsUpdateTree + ]; +} \ No newline at end of file diff --git a/Domain/Rbac/Roles.cs b/Domain/Rbac/Roles.cs new file mode 100644 index 0000000..c9f9ca2 --- /dev/null +++ b/Domain/Rbac/Roles.cs @@ -0,0 +1,77 @@ +using Domain.Entities; + +namespace Domain.Rbac; + +public static class Roles +{ + public static Role Admin { get; } = new() + { + Id = new Guid("CA2CFE04-24ED-42D0-9237-6D5ED7885063"), + Name = "Admin", + }; + + public static Role Editor { get; } = new() + { + Id = new Guid("E1D0AD14-FB96-4488-BF64-8AAB2D1EF43D"), + Name = "Editor", + }; + + public static Role User { get; } = new() + { + Id = new Guid("3A7651C5-2CF1-4ED6-9866-C6BAC6E8F6DD"), + Name = "User", + }; + + public static Role Lurker { get; } = new() + { + Id = new Guid("2AFACDEE-55A4-4E23-9F66-2CA5A5AF9751"), + Name = "Lurker", + }; + + public static IReadOnlyCollection All => [Admin, Editor, User, Lurker]; + + public static IReadOnlyCollection GetDefaultPermissions(Role role) => role.Id switch + { + _ when role.Id == Admin.Id => AdminPermissions, + _ when role.Id == Editor.Id => EditorPermissions, + _ when role.Id == User.Id => UserPermissions, + _ when role.Id == Lurker.Id => LurkerPermissions, + _ => [] + }; + + private static readonly IReadOnlyCollection AdminPermissions = new List + { + Permissions.ArticleCreate, + Permissions.ArticleDelete, + Permissions.ArticleEdit, + Permissions.ArticleSeePendingRevisions, + Permissions.ArticleReviewRevision, + Permissions.ArticleSetRedirect, + Permissions.CategoryCreate, + Permissions.CategoryDelete, + Permissions.NavigationsUpdateTree, + }; + + private static readonly IReadOnlyCollection EditorPermissions = new List + { + Permissions.ArticleCreate, + Permissions.ArticleDelete, + Permissions.ArticleEdit, + Permissions.ArticleSeePendingRevisions, + Permissions.ArticleReviewRevision, + Permissions.ArticleSetRedirect, + Permissions.CategoryCreate, + Permissions.CategoryDelete, + Permissions.NavigationsUpdateTree, + }; + + private static readonly IReadOnlyCollection UserPermissions = new List + { + Permissions.ArticleCreate, + Permissions.ArticleEdit + }; + + private static readonly IReadOnlyCollection LurkerPermissions = new List + { + }; +} \ No newline at end of file diff --git a/Infrastructure/Authorization/CurrentUserService.cs b/Infrastructure/Authorization/CurrentUserService.cs deleted file mode 100644 index 6028492..0000000 --- a/Infrastructure/Authorization/CurrentUserService.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Security.Claims; -using Application.Authorization.Abstractions; -using Microsoft.AspNetCore.Http; - -namespace Infrastructure.Authorization; - -public class CurrentUserService(IHttpContextAccessor httpContextAccessor) : ICurrentUserService -{ - public string? UserId => httpContextAccessor - .HttpContext - ?.User - .FindFirstValue(ClaimTypes.NameIdentifier); - - public string? UserName => httpContextAccessor - .HttpContext - ?.User - .FindFirstValue("preferred_username"); - - public ClaimsPrincipal? Principal => httpContextAccessor - .HttpContext - ?.User; -} \ No newline at end of file diff --git a/Infrastructure/Authorization/IdentityService.cs b/Infrastructure/Authorization/IdentityService.cs deleted file mode 100644 index 26fb26e..0000000 --- a/Infrastructure/Authorization/IdentityService.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System.Security.Claims; -using Application.Authorization.Abstractions; -using Microsoft.AspNetCore.Authorization; - -namespace Infrastructure.Authorization; - -public class IdentityService(IAuthorizationService authorizationService, ICurrentUserService userService) - : IIdentityService -{ - public string? UserId => userService.UserId; - public string? UserName => userService.UserName; - public ClaimsPrincipal? Principal => userService.Principal; - - public async Task AuthorizeAsync(string policyName) - { - return Principal != null && (await authorizationService.AuthorizeAsync(Principal, policyName)).Succeeded; - } - - public Task IsInRoleAsync(string role) - { - return Task.FromResult(Principal?.IsInRole(role) ?? false); - } - - public Task?> Roles() - { - return Task.FromResult(Principal?.Claims.Where(c => c.Type == ClaimTypes.Role).Select(c => c.Value).ToList() ?? - null); - } -} \ No newline at end of file diff --git a/Infrastructure/Data/ApplicationDbContext.cs b/Infrastructure/Data/ApplicationDbContext.cs index 4d23d51..485879c 100644 --- a/Infrastructure/Data/ApplicationDbContext.cs +++ b/Infrastructure/Data/ApplicationDbContext.cs @@ -11,8 +11,10 @@ public class ApplicationDbContext(DbContextOptions options) : DbContext(options) public required DbSet Categories { get; set; } public required DbSet Revisions { get; set; } public required DbSet Reviews { get; set; } - public required DbSet Authors { get; set; } + public required DbSet Users { get; set; } public required DbSet Navigations { get; set; } + public required DbSet Roles { get; set; } + public required DbSet Permissions { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { @@ -61,5 +63,35 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasForeignKey(e=>e.ParentId) .OnDelete(DeleteBehavior.SetNull); }); + modelBuilder.Entity(entity => + { + entity.HasMany(e => e.Users) + .WithMany(e => e.Roles); + + entity.HasData(Domain.Rbac.Roles.All.Select(role => new Role + { + Id = role.Id, + Name = role.Name + })); + + entity.HasMany(e => e.Permissions) + .WithMany(e => e.Roles) + .UsingEntity(j => j.ToTable("RolePermission") + .HasData( + Domain.Rbac.Roles.All.SelectMany(role => + Domain.Rbac.Roles.GetDefaultPermissions(role), + (role, permission) => new { RolesId = role.Id, PermissionsId = permission.Id } + ) + )); + }); + modelBuilder.Entity(entity => + { + entity.HasData(Domain.Rbac.Permissions.All); + }); + modelBuilder.Entity(entity => + { + entity.HasIndex(e => e.ExternalId); + entity.HasIndex(e => e.Name); + }); } } \ No newline at end of file diff --git a/Infrastructure/Data/Migrations/20231205172438_Initial.Designer.cs b/Infrastructure/Data/Migrations/20231205172438_Initial.Designer.cs deleted file mode 100644 index 548f061..0000000 --- a/Infrastructure/Data/Migrations/20231205172438_Initial.Designer.cs +++ /dev/null @@ -1,192 +0,0 @@ -// -using System; -using Infrastructure.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace Infrastructure.Data.Migrations -{ - [DbContext(typeof(ApplicationDbContext))] - [Migration("20231205172438_Initial")] - partial class Initial - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "8.0.0") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("Domain.Entities.Article", b => - { - b.Property("Id") - .HasMaxLength(128) - .HasColumnType("character varying(128)"); - - b.Property("IsVisible") - .HasColumnType("boolean"); - - b.Property("RedirectArticleId") - .HasColumnType("character varying(128)"); - - b.HasKey("Id"); - - b.HasIndex("RedirectArticleId"); - - b.ToTable("Articles"); - }); - - modelBuilder.Entity("Domain.Entities.ArticleCategory", b => - { - b.Property("ArticleId") - .HasColumnType("character varying(128)"); - - b.Property("CategoryId") - .HasColumnType("character varying(128)"); - - b.HasKey("ArticleId", "CategoryId"); - - b.HasIndex("CategoryId"); - - b.ToTable("ArticleCategories"); - }); - - modelBuilder.Entity("Domain.Entities.Category", b => - { - b.Property("Id") - .HasMaxLength(128) - .HasColumnType("character varying(128)"); - - b.Property("ParentId") - .HasColumnType("character varying(128)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.ToTable("Categories"); - }); - - modelBuilder.Entity("Domain.Entities.Revision", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("ArticleId") - .IsRequired() - .HasColumnType("character varying(128)"); - - b.Property("Author") - .IsRequired() - .HasColumnType("text"); - - b.Property("Content") - .IsRequired() - .HasColumnType("text"); - - b.Property("PreviousRevisionId") - .HasColumnType("uuid"); - - b.Property("Review") - .HasColumnType("text"); - - b.Property("ReviewTimestamp") - .HasColumnType("timestamp with time zone"); - - b.Property("Reviewer") - .HasColumnType("text"); - - b.Property("Status") - .HasColumnType("integer"); - - b.Property("Timestamp") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.HasIndex("ArticleId"); - - b.HasIndex("PreviousRevisionId"); - - b.ToTable("Revisions"); - }); - - modelBuilder.Entity("Domain.Entities.Article", b => - { - b.HasOne("Domain.Entities.Article", "RedirectArticle") - .WithMany() - .HasForeignKey("RedirectArticleId"); - - b.Navigation("RedirectArticle"); - }); - - modelBuilder.Entity("Domain.Entities.ArticleCategory", b => - { - b.HasOne("Domain.Entities.Article", "Article") - .WithMany("ArticleCategories") - .HasForeignKey("ArticleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Category", "Category") - .WithMany() - .HasForeignKey("CategoryId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Article"); - - b.Navigation("Category"); - }); - - modelBuilder.Entity("Domain.Entities.Category", b => - { - b.HasOne("Domain.Entities.Category", "Parent") - .WithMany("SubCategories") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Restrict); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Domain.Entities.Revision", b => - { - b.HasOne("Domain.Entities.Article", "Article") - .WithMany("Revisions") - .HasForeignKey("ArticleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Revision", "PreviousRevision") - .WithMany() - .HasForeignKey("PreviousRevisionId"); - - b.Navigation("Article"); - - b.Navigation("PreviousRevision"); - }); - - modelBuilder.Entity("Domain.Entities.Article", b => - { - b.Navigation("ArticleCategories"); - - b.Navigation("Revisions"); - }); - - modelBuilder.Entity("Domain.Entities.Category", b => - { - b.Navigation("SubCategories"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Infrastructure/Data/Migrations/20231205172438_Initial.cs b/Infrastructure/Data/Migrations/20231205172438_Initial.cs deleted file mode 100644 index 8edb84f..0000000 --- a/Infrastructure/Data/Migrations/20231205172438_Initial.cs +++ /dev/null @@ -1,147 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Infrastructure.Data.Migrations -{ - /// - public partial class Initial : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "Articles", - columns: table => new - { - Id = table.Column(type: "character varying(128)", maxLength: 128, nullable: false), - IsVisible = table.Column(type: "boolean", nullable: false), - RedirectArticleId = table.Column(type: "character varying(128)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Articles", x => x.Id); - table.ForeignKey( - name: "FK_Articles_Articles_RedirectArticleId", - column: x => x.RedirectArticleId, - principalTable: "Articles", - principalColumn: "Id"); - }); - - migrationBuilder.CreateTable( - name: "Categories", - columns: table => new - { - Id = table.Column(type: "character varying(128)", maxLength: 128, nullable: false), - ParentId = table.Column(type: "character varying(128)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Categories", x => x.Id); - table.ForeignKey( - name: "FK_Categories_Categories_ParentId", - column: x => x.ParentId, - principalTable: "Categories", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - }); - - migrationBuilder.CreateTable( - name: "Revisions", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false), - ArticleId = table.Column(type: "character varying(128)", nullable: false), - PreviousRevisionId = table.Column(type: "uuid", nullable: true), - Content = table.Column(type: "text", nullable: false), - Author = table.Column(type: "text", nullable: false), - Timestamp = table.Column(type: "timestamp with time zone", nullable: false), - Status = table.Column(type: "integer", nullable: false), - Reviewer = table.Column(type: "text", nullable: true), - Review = table.Column(type: "text", nullable: true), - ReviewTimestamp = table.Column(type: "timestamp with time zone", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Revisions", x => x.Id); - table.ForeignKey( - name: "FK_Revisions_Articles_ArticleId", - column: x => x.ArticleId, - principalTable: "Articles", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_Revisions_Revisions_PreviousRevisionId", - column: x => x.PreviousRevisionId, - principalTable: "Revisions", - principalColumn: "Id"); - }); - - migrationBuilder.CreateTable( - name: "ArticleCategories", - columns: table => new - { - ArticleId = table.Column(type: "character varying(128)", nullable: false), - CategoryId = table.Column(type: "character varying(128)", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_ArticleCategories", x => new { x.ArticleId, x.CategoryId }); - table.ForeignKey( - name: "FK_ArticleCategories_Articles_ArticleId", - column: x => x.ArticleId, - principalTable: "Articles", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_ArticleCategories_Categories_CategoryId", - column: x => x.CategoryId, - principalTable: "Categories", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateIndex( - name: "IX_ArticleCategories_CategoryId", - table: "ArticleCategories", - column: "CategoryId"); - - migrationBuilder.CreateIndex( - name: "IX_Articles_RedirectArticleId", - table: "Articles", - column: "RedirectArticleId"); - - migrationBuilder.CreateIndex( - name: "IX_Categories_ParentId", - table: "Categories", - column: "ParentId"); - - migrationBuilder.CreateIndex( - name: "IX_Revisions_ArticleId", - table: "Revisions", - column: "ArticleId"); - - migrationBuilder.CreateIndex( - name: "IX_Revisions_PreviousRevisionId", - table: "Revisions", - column: "PreviousRevisionId"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "ArticleCategories"); - - migrationBuilder.DropTable( - name: "Revisions"); - - migrationBuilder.DropTable( - name: "Categories"); - - migrationBuilder.DropTable( - name: "Articles"); - } - } -} diff --git a/Infrastructure/Data/Migrations/20231207214658_SeparatedIdAndDisplaynameForArticleAndCategory.Designer.cs b/Infrastructure/Data/Migrations/20231207214658_SeparatedIdAndDisplaynameForArticleAndCategory.Designer.cs deleted file mode 100644 index 0095768..0000000 --- a/Infrastructure/Data/Migrations/20231207214658_SeparatedIdAndDisplaynameForArticleAndCategory.Designer.cs +++ /dev/null @@ -1,204 +0,0 @@ -// -using System; -using Infrastructure.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace Infrastructure.Data.Migrations -{ - [DbContext(typeof(ApplicationDbContext))] - [Migration("20231207214658_SeparatedIdAndDisplaynameForArticleAndCategory")] - partial class SeparatedIdAndDisplaynameForArticleAndCategory - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "8.0.0") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("Domain.Entities.Article", b => - { - b.Property("Id") - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("IsVisible") - .HasColumnType("boolean"); - - b.Property("RedirectArticleId") - .HasColumnType("character varying(512)"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("character varying(128)"); - - b.HasKey("Id"); - - b.HasIndex("RedirectArticleId"); - - b.ToTable("Articles"); - }); - - modelBuilder.Entity("Domain.Entities.ArticleCategory", b => - { - b.Property("ArticleId") - .HasColumnType("character varying(512)"); - - b.Property("CategoryId") - .HasColumnType("character varying(512)"); - - b.HasKey("ArticleId", "CategoryId"); - - b.HasIndex("CategoryId"); - - b.ToTable("ArticleCategories"); - }); - - modelBuilder.Entity("Domain.Entities.Category", b => - { - b.Property("Id") - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("character varying(128)"); - - b.Property("ParentId") - .HasColumnType("character varying(512)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.ToTable("Categories"); - }); - - modelBuilder.Entity("Domain.Entities.Revision", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("ArticleId") - .IsRequired() - .HasColumnType("character varying(512)"); - - b.Property("Author") - .IsRequired() - .HasColumnType("text"); - - b.Property("Content") - .IsRequired() - .HasColumnType("text"); - - b.Property("PreviousRevisionId") - .HasColumnType("uuid"); - - b.Property("Review") - .HasColumnType("text"); - - b.Property("ReviewTimestamp") - .HasColumnType("timestamp with time zone"); - - b.Property("Reviewer") - .HasColumnType("text"); - - b.Property("Status") - .HasColumnType("integer"); - - b.Property("Timestamp") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.HasIndex("ArticleId"); - - b.HasIndex("PreviousRevisionId"); - - b.ToTable("Revisions"); - }); - - modelBuilder.Entity("Domain.Entities.Article", b => - { - b.HasOne("Domain.Entities.Article", "RedirectArticle") - .WithMany() - .HasForeignKey("RedirectArticleId"); - - b.Navigation("RedirectArticle"); - }); - - modelBuilder.Entity("Domain.Entities.ArticleCategory", b => - { - b.HasOne("Domain.Entities.Article", "Article") - .WithMany("ArticleCategories") - .HasForeignKey("ArticleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Category", "Category") - .WithMany("CategoryArticles") - .HasForeignKey("CategoryId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Article"); - - b.Navigation("Category"); - }); - - modelBuilder.Entity("Domain.Entities.Category", b => - { - b.HasOne("Domain.Entities.Category", "Parent") - .WithMany("SubCategories") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Restrict); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Domain.Entities.Revision", b => - { - b.HasOne("Domain.Entities.Article", "Article") - .WithMany("Revisions") - .HasForeignKey("ArticleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Revision", "PreviousRevision") - .WithMany() - .HasForeignKey("PreviousRevisionId"); - - b.Navigation("Article"); - - b.Navigation("PreviousRevision"); - }); - - modelBuilder.Entity("Domain.Entities.Article", b => - { - b.Navigation("ArticleCategories"); - - b.Navigation("Revisions"); - }); - - modelBuilder.Entity("Domain.Entities.Category", b => - { - b.Navigation("CategoryArticles"); - - b.Navigation("SubCategories"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Infrastructure/Data/Migrations/20231207214658_SeparatedIdAndDisplaynameForArticleAndCategory.cs b/Infrastructure/Data/Migrations/20231207214658_SeparatedIdAndDisplaynameForArticleAndCategory.cs deleted file mode 100644 index b3830a7..0000000 --- a/Infrastructure/Data/Migrations/20231207214658_SeparatedIdAndDisplaynameForArticleAndCategory.cs +++ /dev/null @@ -1,166 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Infrastructure.Data.Migrations -{ - /// - public partial class SeparatedIdAndDisplaynameForArticleAndCategory : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterColumn( - name: "ArticleId", - table: "Revisions", - type: "character varying(512)", - nullable: false, - oldClrType: typeof(string), - oldType: "character varying(128)"); - - migrationBuilder.AlterColumn( - name: "ParentId", - table: "Categories", - type: "character varying(512)", - nullable: true, - oldClrType: typeof(string), - oldType: "character varying(128)", - oldNullable: true); - - migrationBuilder.AlterColumn( - name: "Id", - table: "Categories", - type: "character varying(512)", - maxLength: 512, - nullable: false, - oldClrType: typeof(string), - oldType: "character varying(128)", - oldMaxLength: 128); - - migrationBuilder.AddColumn( - name: "Name", - table: "Categories", - type: "character varying(128)", - maxLength: 128, - nullable: false, - defaultValue: ""); - - migrationBuilder.AlterColumn( - name: "RedirectArticleId", - table: "Articles", - type: "character varying(512)", - nullable: true, - oldClrType: typeof(string), - oldType: "character varying(128)", - oldNullable: true); - - migrationBuilder.AlterColumn( - name: "Id", - table: "Articles", - type: "character varying(512)", - maxLength: 512, - nullable: false, - oldClrType: typeof(string), - oldType: "character varying(128)", - oldMaxLength: 128); - - migrationBuilder.AddColumn( - name: "Title", - table: "Articles", - type: "character varying(128)", - maxLength: 128, - nullable: false, - defaultValue: ""); - - migrationBuilder.AlterColumn( - name: "CategoryId", - table: "ArticleCategories", - type: "character varying(512)", - nullable: false, - oldClrType: typeof(string), - oldType: "character varying(128)"); - - migrationBuilder.AlterColumn( - name: "ArticleId", - table: "ArticleCategories", - type: "character varying(512)", - nullable: false, - oldClrType: typeof(string), - oldType: "character varying(128)"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "Name", - table: "Categories"); - - migrationBuilder.DropColumn( - name: "Title", - table: "Articles"); - - migrationBuilder.AlterColumn( - name: "ArticleId", - table: "Revisions", - type: "character varying(128)", - nullable: false, - oldClrType: typeof(string), - oldType: "character varying(512)"); - - migrationBuilder.AlterColumn( - name: "ParentId", - table: "Categories", - type: "character varying(128)", - nullable: true, - oldClrType: typeof(string), - oldType: "character varying(512)", - oldNullable: true); - - migrationBuilder.AlterColumn( - name: "Id", - table: "Categories", - type: "character varying(128)", - maxLength: 128, - nullable: false, - oldClrType: typeof(string), - oldType: "character varying(512)", - oldMaxLength: 512); - - migrationBuilder.AlterColumn( - name: "RedirectArticleId", - table: "Articles", - type: "character varying(128)", - nullable: true, - oldClrType: typeof(string), - oldType: "character varying(512)", - oldNullable: true); - - migrationBuilder.AlterColumn( - name: "Id", - table: "Articles", - type: "character varying(128)", - maxLength: 128, - nullable: false, - oldClrType: typeof(string), - oldType: "character varying(512)", - oldMaxLength: 512); - - migrationBuilder.AlterColumn( - name: "CategoryId", - table: "ArticleCategories", - type: "character varying(128)", - nullable: false, - oldClrType: typeof(string), - oldType: "character varying(512)"); - - migrationBuilder.AlterColumn( - name: "ArticleId", - table: "ArticleCategories", - type: "character varying(128)", - nullable: false, - oldClrType: typeof(string), - oldType: "character varying(512)"); - } - } -} diff --git a/Infrastructure/Data/Migrations/20231207224020_ChangedCategoryParentOnDeleteBehaviorToSetNull.Designer.cs b/Infrastructure/Data/Migrations/20231207224020_ChangedCategoryParentOnDeleteBehaviorToSetNull.Designer.cs deleted file mode 100644 index 88a1af4..0000000 --- a/Infrastructure/Data/Migrations/20231207224020_ChangedCategoryParentOnDeleteBehaviorToSetNull.Designer.cs +++ /dev/null @@ -1,204 +0,0 @@ -// -using System; -using Infrastructure.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace Infrastructure.Data.Migrations -{ - [DbContext(typeof(ApplicationDbContext))] - [Migration("20231207224020_ChangedCategoryParentOnDeleteBehaviorToSetNull")] - partial class ChangedCategoryParentOnDeleteBehaviorToSetNull - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "8.0.0") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("Domain.Entities.Article", b => - { - b.Property("Id") - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("IsVisible") - .HasColumnType("boolean"); - - b.Property("RedirectArticleId") - .HasColumnType("character varying(512)"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("character varying(128)"); - - b.HasKey("Id"); - - b.HasIndex("RedirectArticleId"); - - b.ToTable("Articles"); - }); - - modelBuilder.Entity("Domain.Entities.ArticleCategory", b => - { - b.Property("ArticleId") - .HasColumnType("character varying(512)"); - - b.Property("CategoryId") - .HasColumnType("character varying(512)"); - - b.HasKey("ArticleId", "CategoryId"); - - b.HasIndex("CategoryId"); - - b.ToTable("ArticleCategories"); - }); - - modelBuilder.Entity("Domain.Entities.Category", b => - { - b.Property("Id") - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("character varying(128)"); - - b.Property("ParentId") - .HasColumnType("character varying(512)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.ToTable("Categories"); - }); - - modelBuilder.Entity("Domain.Entities.Revision", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("ArticleId") - .IsRequired() - .HasColumnType("character varying(512)"); - - b.Property("Author") - .IsRequired() - .HasColumnType("text"); - - b.Property("Content") - .IsRequired() - .HasColumnType("text"); - - b.Property("PreviousRevisionId") - .HasColumnType("uuid"); - - b.Property("Review") - .HasColumnType("text"); - - b.Property("ReviewTimestamp") - .HasColumnType("timestamp with time zone"); - - b.Property("Reviewer") - .HasColumnType("text"); - - b.Property("Status") - .HasColumnType("integer"); - - b.Property("Timestamp") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.HasIndex("ArticleId"); - - b.HasIndex("PreviousRevisionId"); - - b.ToTable("Revisions"); - }); - - modelBuilder.Entity("Domain.Entities.Article", b => - { - b.HasOne("Domain.Entities.Article", "RedirectArticle") - .WithMany() - .HasForeignKey("RedirectArticleId"); - - b.Navigation("RedirectArticle"); - }); - - modelBuilder.Entity("Domain.Entities.ArticleCategory", b => - { - b.HasOne("Domain.Entities.Article", "Article") - .WithMany("ArticleCategories") - .HasForeignKey("ArticleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Category", "Category") - .WithMany("CategoryArticles") - .HasForeignKey("CategoryId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Article"); - - b.Navigation("Category"); - }); - - modelBuilder.Entity("Domain.Entities.Category", b => - { - b.HasOne("Domain.Entities.Category", "Parent") - .WithMany("SubCategories") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.SetNull); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Domain.Entities.Revision", b => - { - b.HasOne("Domain.Entities.Article", "Article") - .WithMany("Revisions") - .HasForeignKey("ArticleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Revision", "PreviousRevision") - .WithMany() - .HasForeignKey("PreviousRevisionId"); - - b.Navigation("Article"); - - b.Navigation("PreviousRevision"); - }); - - modelBuilder.Entity("Domain.Entities.Article", b => - { - b.Navigation("ArticleCategories"); - - b.Navigation("Revisions"); - }); - - modelBuilder.Entity("Domain.Entities.Category", b => - { - b.Navigation("CategoryArticles"); - - b.Navigation("SubCategories"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Infrastructure/Data/Migrations/20231207224020_ChangedCategoryParentOnDeleteBehaviorToSetNull.cs b/Infrastructure/Data/Migrations/20231207224020_ChangedCategoryParentOnDeleteBehaviorToSetNull.cs deleted file mode 100644 index 5931232..0000000 --- a/Infrastructure/Data/Migrations/20231207224020_ChangedCategoryParentOnDeleteBehaviorToSetNull.cs +++ /dev/null @@ -1,42 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Infrastructure.Data.Migrations -{ - /// - public partial class ChangedCategoryParentOnDeleteBehaviorToSetNull : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropForeignKey( - name: "FK_Categories_Categories_ParentId", - table: "Categories"); - - migrationBuilder.AddForeignKey( - name: "FK_Categories_Categories_ParentId", - table: "Categories", - column: "ParentId", - principalTable: "Categories", - principalColumn: "Id", - onDelete: ReferentialAction.SetNull); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropForeignKey( - name: "FK_Categories_Categories_ParentId", - table: "Categories"); - - migrationBuilder.AddForeignKey( - name: "FK_Categories_Categories_ParentId", - table: "Categories", - column: "ParentId", - principalTable: "Categories", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - } - } -} diff --git a/Infrastructure/Data/Migrations/20231207225909_ChangedArticleRedirectArticleOnDeleteBehaviorToCascade.Designer.cs b/Infrastructure/Data/Migrations/20231207225909_ChangedArticleRedirectArticleOnDeleteBehaviorToCascade.Designer.cs deleted file mode 100644 index 4f7d495..0000000 --- a/Infrastructure/Data/Migrations/20231207225909_ChangedArticleRedirectArticleOnDeleteBehaviorToCascade.Designer.cs +++ /dev/null @@ -1,205 +0,0 @@ -// -using System; -using Infrastructure.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace Infrastructure.Data.Migrations -{ - [DbContext(typeof(ApplicationDbContext))] - [Migration("20231207225909_test")] - partial class ChangedArticleRedirectArticleOnDeleteBehaviorToCascade - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "8.0.0") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("Domain.Entities.Article", b => - { - b.Property("Id") - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("IsVisible") - .HasColumnType("boolean"); - - b.Property("RedirectArticleId") - .HasColumnType("character varying(512)"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("character varying(128)"); - - b.HasKey("Id"); - - b.HasIndex("RedirectArticleId"); - - b.ToTable("Articles"); - }); - - modelBuilder.Entity("Domain.Entities.ArticleCategory", b => - { - b.Property("ArticleId") - .HasColumnType("character varying(512)"); - - b.Property("CategoryId") - .HasColumnType("character varying(512)"); - - b.HasKey("ArticleId", "CategoryId"); - - b.HasIndex("CategoryId"); - - b.ToTable("ArticleCategories"); - }); - - modelBuilder.Entity("Domain.Entities.Category", b => - { - b.Property("Id") - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("character varying(128)"); - - b.Property("ParentId") - .HasColumnType("character varying(512)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.ToTable("Categories"); - }); - - modelBuilder.Entity("Domain.Entities.Revision", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("ArticleId") - .IsRequired() - .HasColumnType("character varying(512)"); - - b.Property("Author") - .IsRequired() - .HasColumnType("text"); - - b.Property("Content") - .IsRequired() - .HasColumnType("text"); - - b.Property("PreviousRevisionId") - .HasColumnType("uuid"); - - b.Property("Review") - .HasColumnType("text"); - - b.Property("ReviewTimestamp") - .HasColumnType("timestamp with time zone"); - - b.Property("Reviewer") - .HasColumnType("text"); - - b.Property("Status") - .HasColumnType("integer"); - - b.Property("Timestamp") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.HasIndex("ArticleId"); - - b.HasIndex("PreviousRevisionId"); - - b.ToTable("Revisions"); - }); - - modelBuilder.Entity("Domain.Entities.Article", b => - { - b.HasOne("Domain.Entities.Article", "RedirectArticle") - .WithMany() - .HasForeignKey("RedirectArticleId") - .OnDelete(DeleteBehavior.Cascade); - - b.Navigation("RedirectArticle"); - }); - - modelBuilder.Entity("Domain.Entities.ArticleCategory", b => - { - b.HasOne("Domain.Entities.Article", "Article") - .WithMany("ArticleCategories") - .HasForeignKey("ArticleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Category", "Category") - .WithMany("CategoryArticles") - .HasForeignKey("CategoryId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Article"); - - b.Navigation("Category"); - }); - - modelBuilder.Entity("Domain.Entities.Category", b => - { - b.HasOne("Domain.Entities.Category", "Parent") - .WithMany("SubCategories") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.SetNull); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Domain.Entities.Revision", b => - { - b.HasOne("Domain.Entities.Article", "Article") - .WithMany("Revisions") - .HasForeignKey("ArticleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Revision", "PreviousRevision") - .WithMany() - .HasForeignKey("PreviousRevisionId"); - - b.Navigation("Article"); - - b.Navigation("PreviousRevision"); - }); - - modelBuilder.Entity("Domain.Entities.Article", b => - { - b.Navigation("ArticleCategories"); - - b.Navigation("Revisions"); - }); - - modelBuilder.Entity("Domain.Entities.Category", b => - { - b.Navigation("CategoryArticles"); - - b.Navigation("SubCategories"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Infrastructure/Data/Migrations/20231207225909_ChangedArticleRedirectArticleOnDeleteBehaviorToCascade.cs b/Infrastructure/Data/Migrations/20231207225909_ChangedArticleRedirectArticleOnDeleteBehaviorToCascade.cs deleted file mode 100644 index 68af39e..0000000 --- a/Infrastructure/Data/Migrations/20231207225909_ChangedArticleRedirectArticleOnDeleteBehaviorToCascade.cs +++ /dev/null @@ -1,41 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Infrastructure.Data.Migrations -{ - /// - public partial class ChangedArticleRedirectArticleOnDeleteBehaviorToCascade : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropForeignKey( - name: "FK_Articles_Articles_RedirectArticleId", - table: "Articles"); - - migrationBuilder.AddForeignKey( - name: "FK_Articles_Articles_RedirectArticleId", - table: "Articles", - column: "RedirectArticleId", - principalTable: "Articles", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropForeignKey( - name: "FK_Articles_Articles_RedirectArticleId", - table: "Articles"); - - migrationBuilder.AddForeignKey( - name: "FK_Articles_Articles_RedirectArticleId", - table: "Articles", - column: "RedirectArticleId", - principalTable: "Articles", - principalColumn: "Id"); - } - } -} diff --git a/Infrastructure/Data/Migrations/20231214021324_AddedAuthorTable.Designer.cs b/Infrastructure/Data/Migrations/20231214021324_AddedAuthorTable.Designer.cs deleted file mode 100644 index 29fdbbf..0000000 --- a/Infrastructure/Data/Migrations/20231214021324_AddedAuthorTable.Designer.cs +++ /dev/null @@ -1,243 +0,0 @@ -// -using System; -using Infrastructure.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace Infrastructure.Data.Migrations -{ - [DbContext(typeof(ApplicationDbContext))] - [Migration("20231214021324_AddedAuthorTable")] - partial class AddedAuthorTable - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "8.0.0") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("Domain.Entities.Article", b => - { - b.Property("Id") - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("AuthorId") - .IsRequired() - .HasColumnType("text"); - - b.Property("IsVisible") - .HasColumnType("boolean"); - - b.Property("RedirectArticleId") - .HasColumnType("character varying(512)"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("character varying(128)"); - - b.HasKey("Id"); - - b.HasIndex("AuthorId"); - - b.HasIndex("RedirectArticleId"); - - b.ToTable("Articles"); - }); - - modelBuilder.Entity("Domain.Entities.ArticleCategory", b => - { - b.Property("ArticleId") - .HasColumnType("character varying(512)"); - - b.Property("CategoryId") - .HasColumnType("character varying(512)"); - - b.HasKey("ArticleId", "CategoryId"); - - b.HasIndex("CategoryId"); - - b.ToTable("ArticleCategories"); - }); - - modelBuilder.Entity("Domain.Entities.Author", b => - { - b.Property("Id") - .HasColumnType("text"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("Authors"); - }); - - modelBuilder.Entity("Domain.Entities.Category", b => - { - b.Property("Id") - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("character varying(128)"); - - b.Property("ParentId") - .HasColumnType("character varying(512)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.ToTable("Categories"); - }); - - modelBuilder.Entity("Domain.Entities.Revision", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("ArticleId") - .IsRequired() - .HasColumnType("character varying(512)"); - - b.Property("AuthorId") - .IsRequired() - .HasColumnType("text"); - - b.Property("Content") - .IsRequired() - .HasColumnType("text"); - - b.Property("PreviousRevisionId") - .HasColumnType("uuid"); - - b.Property("Review") - .HasColumnType("text"); - - b.Property("ReviewTimestamp") - .HasColumnType("timestamp with time zone"); - - b.Property("Reviewer") - .HasColumnType("text"); - - b.Property("Status") - .HasColumnType("integer"); - - b.Property("Timestamp") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.HasIndex("ArticleId"); - - b.HasIndex("AuthorId"); - - b.HasIndex("PreviousRevisionId"); - - b.ToTable("Revisions"); - }); - - modelBuilder.Entity("Domain.Entities.Article", b => - { - b.HasOne("Domain.Entities.Author", "Author") - .WithMany() - .HasForeignKey("AuthorId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Article", "RedirectArticle") - .WithMany() - .HasForeignKey("RedirectArticleId") - .OnDelete(DeleteBehavior.Cascade); - - b.Navigation("Author"); - - b.Navigation("RedirectArticle"); - }); - - modelBuilder.Entity("Domain.Entities.ArticleCategory", b => - { - b.HasOne("Domain.Entities.Article", "Article") - .WithMany("ArticleCategories") - .HasForeignKey("ArticleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Category", "Category") - .WithMany("CategoryArticles") - .HasForeignKey("CategoryId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Article"); - - b.Navigation("Category"); - }); - - modelBuilder.Entity("Domain.Entities.Category", b => - { - b.HasOne("Domain.Entities.Category", "Parent") - .WithMany("SubCategories") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.SetNull); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Domain.Entities.Revision", b => - { - b.HasOne("Domain.Entities.Article", "Article") - .WithMany("Revisions") - .HasForeignKey("ArticleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Author", "Author") - .WithMany() - .HasForeignKey("AuthorId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Revision", "PreviousRevision") - .WithMany() - .HasForeignKey("PreviousRevisionId"); - - b.Navigation("Article"); - - b.Navigation("Author"); - - b.Navigation("PreviousRevision"); - }); - - modelBuilder.Entity("Domain.Entities.Article", b => - { - b.Navigation("ArticleCategories"); - - b.Navigation("Revisions"); - }); - - modelBuilder.Entity("Domain.Entities.Category", b => - { - b.Navigation("CategoryArticles"); - - b.Navigation("SubCategories"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Infrastructure/Data/Migrations/20231214021324_AddedAuthorTable.cs b/Infrastructure/Data/Migrations/20231214021324_AddedAuthorTable.cs deleted file mode 100644 index 9c95270..0000000 --- a/Infrastructure/Data/Migrations/20231214021324_AddedAuthorTable.cs +++ /dev/null @@ -1,96 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Infrastructure.Data.Migrations -{ - /// - public partial class AddedAuthorTable : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.RenameColumn( - name: "Author", - table: "Revisions", - newName: "AuthorId"); - - migrationBuilder.AddColumn( - name: "AuthorId", - table: "Articles", - type: "text", - nullable: false, - defaultValue: ""); - - migrationBuilder.CreateTable( - name: "Authors", - columns: table => new - { - Id = table.Column(type: "text", nullable: false), - Name = table.Column(type: "text", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_Authors", x => x.Id); - }); - - migrationBuilder.CreateIndex( - name: "IX_Revisions_AuthorId", - table: "Revisions", - column: "AuthorId"); - - migrationBuilder.CreateIndex( - name: "IX_Articles_AuthorId", - table: "Articles", - column: "AuthorId"); - - migrationBuilder.AddForeignKey( - name: "FK_Articles_Authors_AuthorId", - table: "Articles", - column: "AuthorId", - principalTable: "Authors", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - - migrationBuilder.AddForeignKey( - name: "FK_Revisions_Authors_AuthorId", - table: "Revisions", - column: "AuthorId", - principalTable: "Authors", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropForeignKey( - name: "FK_Articles_Authors_AuthorId", - table: "Articles"); - - migrationBuilder.DropForeignKey( - name: "FK_Revisions_Authors_AuthorId", - table: "Revisions"); - - migrationBuilder.DropTable( - name: "Authors"); - - migrationBuilder.DropIndex( - name: "IX_Revisions_AuthorId", - table: "Revisions"); - - migrationBuilder.DropIndex( - name: "IX_Articles_AuthorId", - table: "Articles"); - - migrationBuilder.DropColumn( - name: "AuthorId", - table: "Articles"); - - migrationBuilder.RenameColumn( - name: "AuthorId", - table: "Revisions", - newName: "Author"); - } - } -} diff --git a/Infrastructure/Data/Migrations/20231216012919_RemovedAuthorFromArticles.Designer.cs b/Infrastructure/Data/Migrations/20231216012919_RemovedAuthorFromArticles.Designer.cs deleted file mode 100644 index e49e760..0000000 --- a/Infrastructure/Data/Migrations/20231216012919_RemovedAuthorFromArticles.Designer.cs +++ /dev/null @@ -1,229 +0,0 @@ -// -using System; -using Infrastructure.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace Infrastructure.Data.Migrations -{ - [DbContext(typeof(ApplicationDbContext))] - [Migration("20231216012919_RemovedAuthorFromArticles")] - partial class RemovedAuthorFromArticles - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "8.0.0") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("Domain.Entities.Article", b => - { - b.Property("Id") - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("IsVisible") - .HasColumnType("boolean"); - - b.Property("RedirectArticleId") - .HasColumnType("character varying(512)"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("character varying(128)"); - - b.HasKey("Id"); - - b.HasIndex("RedirectArticleId"); - - b.ToTable("Articles"); - }); - - modelBuilder.Entity("Domain.Entities.ArticleCategory", b => - { - b.Property("ArticleId") - .HasColumnType("character varying(512)"); - - b.Property("CategoryId") - .HasColumnType("character varying(512)"); - - b.HasKey("ArticleId", "CategoryId"); - - b.HasIndex("CategoryId"); - - b.ToTable("ArticleCategories"); - }); - - modelBuilder.Entity("Domain.Entities.Author", b => - { - b.Property("Id") - .HasColumnType("text"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("Authors"); - }); - - modelBuilder.Entity("Domain.Entities.Category", b => - { - b.Property("Id") - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("character varying(128)"); - - b.Property("ParentId") - .HasColumnType("character varying(512)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.ToTable("Categories"); - }); - - modelBuilder.Entity("Domain.Entities.Revision", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("ArticleId") - .IsRequired() - .HasColumnType("character varying(512)"); - - b.Property("AuthorId") - .IsRequired() - .HasColumnType("text"); - - b.Property("Content") - .IsRequired() - .HasColumnType("text"); - - b.Property("PreviousRevisionId") - .HasColumnType("uuid"); - - b.Property("Review") - .HasColumnType("text"); - - b.Property("ReviewTimestamp") - .HasColumnType("timestamp with time zone"); - - b.Property("Reviewer") - .HasColumnType("text"); - - b.Property("Status") - .HasColumnType("integer"); - - b.Property("Timestamp") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.HasIndex("ArticleId"); - - b.HasIndex("AuthorId"); - - b.HasIndex("PreviousRevisionId"); - - b.ToTable("Revisions"); - }); - - modelBuilder.Entity("Domain.Entities.Article", b => - { - b.HasOne("Domain.Entities.Article", "RedirectArticle") - .WithMany() - .HasForeignKey("RedirectArticleId") - .OnDelete(DeleteBehavior.Cascade); - - b.Navigation("RedirectArticle"); - }); - - modelBuilder.Entity("Domain.Entities.ArticleCategory", b => - { - b.HasOne("Domain.Entities.Article", "Article") - .WithMany("ArticleCategories") - .HasForeignKey("ArticleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Category", "Category") - .WithMany("CategoryArticles") - .HasForeignKey("CategoryId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Article"); - - b.Navigation("Category"); - }); - - modelBuilder.Entity("Domain.Entities.Category", b => - { - b.HasOne("Domain.Entities.Category", "Parent") - .WithMany("SubCategories") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.SetNull); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Domain.Entities.Revision", b => - { - b.HasOne("Domain.Entities.Article", "Article") - .WithMany("Revisions") - .HasForeignKey("ArticleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Author", "Author") - .WithMany() - .HasForeignKey("AuthorId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Revision", "PreviousRevision") - .WithMany() - .HasForeignKey("PreviousRevisionId"); - - b.Navigation("Article"); - - b.Navigation("Author"); - - b.Navigation("PreviousRevision"); - }); - - modelBuilder.Entity("Domain.Entities.Article", b => - { - b.Navigation("ArticleCategories"); - - b.Navigation("Revisions"); - }); - - modelBuilder.Entity("Domain.Entities.Category", b => - { - b.Navigation("CategoryArticles"); - - b.Navigation("SubCategories"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Infrastructure/Data/Migrations/20231216012919_RemovedAuthorFromArticles.cs b/Infrastructure/Data/Migrations/20231216012919_RemovedAuthorFromArticles.cs deleted file mode 100644 index f74634c..0000000 --- a/Infrastructure/Data/Migrations/20231216012919_RemovedAuthorFromArticles.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Infrastructure.Data.Migrations -{ - /// - public partial class RemovedAuthorFromArticles : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropForeignKey( - name: "FK_Articles_Authors_AuthorId", - table: "Articles"); - - migrationBuilder.DropIndex( - name: "IX_Articles_AuthorId", - table: "Articles"); - - migrationBuilder.DropColumn( - name: "AuthorId", - table: "Articles"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn( - name: "AuthorId", - table: "Articles", - type: "text", - nullable: false, - defaultValue: ""); - - migrationBuilder.CreateIndex( - name: "IX_Articles_AuthorId", - table: "Articles", - column: "AuthorId"); - - migrationBuilder.AddForeignKey( - name: "FK_Articles_Authors_AuthorId", - table: "Articles", - column: "AuthorId", - principalTable: "Authors", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - } - } -} diff --git a/Infrastructure/Data/Migrations/20231220205543_ReplacesIsVisibleToArtcicleStatusInArtcileTable.Designer.cs b/Infrastructure/Data/Migrations/20231220205543_ReplacesIsVisibleToArtcicleStatusInArtcileTable.Designer.cs deleted file mode 100644 index 5389d8e..0000000 --- a/Infrastructure/Data/Migrations/20231220205543_ReplacesIsVisibleToArtcicleStatusInArtcileTable.Designer.cs +++ /dev/null @@ -1,229 +0,0 @@ -// -using System; -using Infrastructure.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace Infrastructure.Data.Migrations -{ - [DbContext(typeof(ApplicationDbContext))] - [Migration("20231220205543_ReplacesIsVisibleToArtcicleStatusInArtcileTable")] - partial class ReplacesIsVisibleToArtcicleStatusInArtcileTable - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "8.0.0") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("Domain.Entities.Article", b => - { - b.Property("Id") - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("ArticleStatus") - .HasColumnType("integer"); - - b.Property("RedirectArticleId") - .HasColumnType("character varying(512)"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("character varying(128)"); - - b.HasKey("Id"); - - b.HasIndex("RedirectArticleId"); - - b.ToTable("Articles"); - }); - - modelBuilder.Entity("Domain.Entities.ArticleCategory", b => - { - b.Property("ArticleId") - .HasColumnType("character varying(512)"); - - b.Property("CategoryId") - .HasColumnType("character varying(512)"); - - b.HasKey("ArticleId", "CategoryId"); - - b.HasIndex("CategoryId"); - - b.ToTable("ArticleCategories"); - }); - - modelBuilder.Entity("Domain.Entities.Author", b => - { - b.Property("Id") - .HasColumnType("text"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("Authors"); - }); - - modelBuilder.Entity("Domain.Entities.Category", b => - { - b.Property("Id") - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("character varying(128)"); - - b.Property("ParentId") - .HasColumnType("character varying(512)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.ToTable("Categories"); - }); - - modelBuilder.Entity("Domain.Entities.Revision", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("ArticleId") - .IsRequired() - .HasColumnType("character varying(512)"); - - b.Property("AuthorId") - .IsRequired() - .HasColumnType("text"); - - b.Property("Content") - .IsRequired() - .HasColumnType("text"); - - b.Property("PreviousRevisionId") - .HasColumnType("uuid"); - - b.Property("Review") - .HasColumnType("text"); - - b.Property("ReviewTimestamp") - .HasColumnType("timestamp with time zone"); - - b.Property("Reviewer") - .HasColumnType("text"); - - b.Property("Status") - .HasColumnType("integer"); - - b.Property("Timestamp") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.HasIndex("ArticleId"); - - b.HasIndex("AuthorId"); - - b.HasIndex("PreviousRevisionId"); - - b.ToTable("Revisions"); - }); - - modelBuilder.Entity("Domain.Entities.Article", b => - { - b.HasOne("Domain.Entities.Article", "RedirectArticle") - .WithMany() - .HasForeignKey("RedirectArticleId") - .OnDelete(DeleteBehavior.Cascade); - - b.Navigation("RedirectArticle"); - }); - - modelBuilder.Entity("Domain.Entities.ArticleCategory", b => - { - b.HasOne("Domain.Entities.Article", "Article") - .WithMany("ArticleCategories") - .HasForeignKey("ArticleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Category", "Category") - .WithMany("CategoryArticles") - .HasForeignKey("CategoryId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Article"); - - b.Navigation("Category"); - }); - - modelBuilder.Entity("Domain.Entities.Category", b => - { - b.HasOne("Domain.Entities.Category", "Parent") - .WithMany("SubCategories") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.SetNull); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Domain.Entities.Revision", b => - { - b.HasOne("Domain.Entities.Article", "Article") - .WithMany("Revisions") - .HasForeignKey("ArticleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Author", "Author") - .WithMany() - .HasForeignKey("AuthorId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Revision", "PreviousRevision") - .WithMany() - .HasForeignKey("PreviousRevisionId"); - - b.Navigation("Article"); - - b.Navigation("Author"); - - b.Navigation("PreviousRevision"); - }); - - modelBuilder.Entity("Domain.Entities.Article", b => - { - b.Navigation("ArticleCategories"); - - b.Navigation("Revisions"); - }); - - modelBuilder.Entity("Domain.Entities.Category", b => - { - b.Navigation("CategoryArticles"); - - b.Navigation("SubCategories"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Infrastructure/Data/Migrations/20231220205543_ReplacesIsVisibleToArtcicleStatusInArtcileTable.cs b/Infrastructure/Data/Migrations/20231220205543_ReplacesIsVisibleToArtcicleStatusInArtcileTable.cs deleted file mode 100644 index 978eec0..0000000 --- a/Infrastructure/Data/Migrations/20231220205543_ReplacesIsVisibleToArtcicleStatusInArtcileTable.cs +++ /dev/null @@ -1,40 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Infrastructure.Data.Migrations -{ - /// - public partial class ReplacesIsVisibleToArtcicleStatusInArtcileTable : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "IsVisible", - table: "Articles"); - - migrationBuilder.AddColumn( - name: "ArticleStatus", - table: "Articles", - type: "integer", - nullable: false, - defaultValue: 0); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "ArticleStatus", - table: "Articles"); - - migrationBuilder.AddColumn( - name: "IsVisible", - table: "Articles", - type: "boolean", - nullable: false, - defaultValue: false); - } - } -} diff --git a/Infrastructure/Data/Migrations/20231222163420_AddedArticleIsHiddenAndRevisionTitleAndCategories.Designer.cs b/Infrastructure/Data/Migrations/20231222163420_AddedArticleIsHiddenAndRevisionTitleAndCategories.Designer.cs deleted file mode 100644 index 0199ec4..0000000 --- a/Infrastructure/Data/Migrations/20231222163420_AddedArticleIsHiddenAndRevisionTitleAndCategories.Designer.cs +++ /dev/null @@ -1,263 +0,0 @@ -// -using System; -using Infrastructure.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace Infrastructure.Data.Migrations -{ - [DbContext(typeof(ApplicationDbContext))] - [Migration("20231222163420_AddedArticleIsHiddenAndRevisionTitleAndCategories")] - partial class AddedArticleIsHiddenAndRevisionTitleAndCategories - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "8.0.0") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("CategoryRevision", b => - { - b.Property("CategoriesId") - .HasColumnType("character varying(512)"); - - b.Property("RevisionsId") - .HasColumnType("uuid"); - - b.HasKey("CategoriesId", "RevisionsId"); - - b.HasIndex("RevisionsId"); - - b.ToTable("CategoryRevision"); - }); - - modelBuilder.Entity("Domain.Entities.Article", b => - { - b.Property("Id") - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("IsHidden") - .HasColumnType("boolean"); - - b.Property("RedirectArticleId") - .HasColumnType("character varying(512)"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("character varying(128)"); - - b.HasKey("Id"); - - b.HasIndex("RedirectArticleId"); - - b.ToTable("Articles"); - }); - - modelBuilder.Entity("Domain.Entities.ArticleCategory", b => - { - b.Property("ArticleId") - .HasColumnType("character varying(512)"); - - b.Property("CategoryId") - .HasColumnType("character varying(512)"); - - b.HasKey("ArticleId", "CategoryId"); - - b.HasIndex("CategoryId"); - - b.ToTable("ArticleCategories"); - }); - - modelBuilder.Entity("Domain.Entities.Author", b => - { - b.Property("Id") - .HasColumnType("text"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("Authors"); - }); - - modelBuilder.Entity("Domain.Entities.Category", b => - { - b.Property("Id") - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("character varying(128)"); - - b.Property("ParentId") - .HasColumnType("character varying(512)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.ToTable("Categories"); - }); - - modelBuilder.Entity("Domain.Entities.Revision", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("ArticleId") - .IsRequired() - .HasColumnType("character varying(512)"); - - b.Property("AuthorId") - .IsRequired() - .HasColumnType("text"); - - b.Property("Content") - .IsRequired() - .HasColumnType("text"); - - b.Property("PreviousRevisionId") - .HasColumnType("uuid"); - - b.Property("Review") - .HasColumnType("text"); - - b.Property("ReviewTimestamp") - .HasColumnType("timestamp with time zone"); - - b.Property("Reviewer") - .HasColumnType("text"); - - b.Property("Status") - .HasColumnType("integer"); - - b.Property("Timestamp") - .HasColumnType("timestamp with time zone"); - - b.Property("Title") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ArticleId"); - - b.HasIndex("AuthorId"); - - b.HasIndex("PreviousRevisionId"); - - b.ToTable("Revisions"); - }); - - modelBuilder.Entity("CategoryRevision", b => - { - b.HasOne("Domain.Entities.Category", null) - .WithMany() - .HasForeignKey("CategoriesId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Revision", null) - .WithMany() - .HasForeignKey("RevisionsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Domain.Entities.Article", b => - { - b.HasOne("Domain.Entities.Article", "RedirectArticle") - .WithMany() - .HasForeignKey("RedirectArticleId") - .OnDelete(DeleteBehavior.Cascade); - - b.Navigation("RedirectArticle"); - }); - - modelBuilder.Entity("Domain.Entities.ArticleCategory", b => - { - b.HasOne("Domain.Entities.Article", "Article") - .WithMany("ArticleCategories") - .HasForeignKey("ArticleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Category", "Category") - .WithMany("CategoryArticles") - .HasForeignKey("CategoryId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Article"); - - b.Navigation("Category"); - }); - - modelBuilder.Entity("Domain.Entities.Category", b => - { - b.HasOne("Domain.Entities.Category", "Parent") - .WithMany("SubCategories") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.SetNull); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Domain.Entities.Revision", b => - { - b.HasOne("Domain.Entities.Article", "Article") - .WithMany("Revisions") - .HasForeignKey("ArticleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Author", "Author") - .WithMany() - .HasForeignKey("AuthorId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Revision", "PreviousRevision") - .WithMany() - .HasForeignKey("PreviousRevisionId"); - - b.Navigation("Article"); - - b.Navigation("Author"); - - b.Navigation("PreviousRevision"); - }); - - modelBuilder.Entity("Domain.Entities.Article", b => - { - b.Navigation("ArticleCategories"); - - b.Navigation("Revisions"); - }); - - modelBuilder.Entity("Domain.Entities.Category", b => - { - b.Navigation("CategoryArticles"); - - b.Navigation("SubCategories"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Infrastructure/Data/Migrations/20231222163420_AddedArticleIsHiddenAndRevisionTitleAndCategories.cs b/Infrastructure/Data/Migrations/20231222163420_AddedArticleIsHiddenAndRevisionTitleAndCategories.cs deleted file mode 100644 index 93dad94..0000000 --- a/Infrastructure/Data/Migrations/20231222163420_AddedArticleIsHiddenAndRevisionTitleAndCategories.cs +++ /dev/null @@ -1,84 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Infrastructure.Data.Migrations -{ - /// - public partial class AddedArticleIsHiddenAndRevisionTitleAndCategories : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "ArticleStatus", - table: "Articles"); - - migrationBuilder.AddColumn( - name: "Title", - table: "Revisions", - type: "text", - nullable: false, - defaultValue: ""); - - migrationBuilder.AddColumn( - name: "IsHidden", - table: "Articles", - type: "boolean", - nullable: false, - defaultValue: false); - - migrationBuilder.CreateTable( - name: "CategoryRevision", - columns: table => new - { - CategoriesId = table.Column(type: "character varying(512)", nullable: false), - RevisionsId = table.Column(type: "uuid", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_CategoryRevision", x => new { x.CategoriesId, x.RevisionsId }); - table.ForeignKey( - name: "FK_CategoryRevision_Categories_CategoriesId", - column: x => x.CategoriesId, - principalTable: "Categories", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_CategoryRevision_Revisions_RevisionsId", - column: x => x.RevisionsId, - principalTable: "Revisions", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateIndex( - name: "IX_CategoryRevision_RevisionsId", - table: "CategoryRevision", - column: "RevisionsId"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "CategoryRevision"); - - migrationBuilder.DropColumn( - name: "Title", - table: "Revisions"); - - migrationBuilder.DropColumn( - name: "IsHidden", - table: "Articles"); - - migrationBuilder.AddColumn( - name: "ArticleStatus", - table: "Articles", - type: "integer", - nullable: false, - defaultValue: 0); - } - } -} diff --git a/Infrastructure/Data/Migrations/20240116221526_AddedReviewTable.Designer.cs b/Infrastructure/Data/Migrations/20240116221526_AddedReviewTable.Designer.cs deleted file mode 100644 index f235a10..0000000 --- a/Infrastructure/Data/Migrations/20240116221526_AddedReviewTable.Designer.cs +++ /dev/null @@ -1,319 +0,0 @@ -// -using System; -using Infrastructure.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace Infrastructure.Data.Migrations -{ - [DbContext(typeof(ApplicationDbContext))] - [Migration("20240116221526_AddedReviewTable")] - partial class AddedReviewTable - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "8.0.1") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("CategoryRevision", b => - { - b.Property("CategoriesId") - .HasColumnType("character varying(512)"); - - b.Property("RevisionsId") - .HasColumnType("uuid"); - - b.HasKey("CategoriesId", "RevisionsId"); - - b.HasIndex("RevisionsId"); - - b.ToTable("CategoryRevision"); - }); - - modelBuilder.Entity("Domain.Entities.Article", b => - { - b.Property("Id") - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("IsHidden") - .HasColumnType("boolean"); - - b.Property("RedirectArticleId") - .HasColumnType("character varying(512)"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("character varying(128)"); - - b.HasKey("Id"); - - b.HasIndex("RedirectArticleId"); - - b.ToTable("Articles"); - }); - - modelBuilder.Entity("Domain.Entities.ArticleCategory", b => - { - b.Property("ArticleId") - .HasColumnType("character varying(512)"); - - b.Property("CategoryId") - .HasColumnType("character varying(512)"); - - b.HasKey("ArticleId", "CategoryId"); - - b.HasIndex("CategoryId"); - - b.ToTable("ArticleCategories"); - }); - - modelBuilder.Entity("Domain.Entities.Author", b => - { - b.Property("Id") - .HasColumnType("text"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("Authors"); - }); - - modelBuilder.Entity("Domain.Entities.Category", b => - { - b.Property("Id") - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("character varying(128)"); - - b.Property("ParentId") - .HasColumnType("character varying(512)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.ToTable("Categories"); - }); - - modelBuilder.Entity("Domain.Entities.Review", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("Message") - .IsRequired() - .HasColumnType("text"); - - b.Property("ReviewTimestamp") - .HasColumnType("timestamp with time zone"); - - b.Property("ReviewerId") - .IsRequired() - .HasColumnType("text"); - - b.Property("RevisionId") - .HasColumnType("uuid"); - - b.Property("Status") - .HasColumnType("integer"); - - b.HasKey("Id"); - - b.HasIndex("ReviewerId"); - - b.HasIndex("RevisionId"); - - b.ToTable("Reviews"); - }); - - modelBuilder.Entity("Domain.Entities.Revision", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("ArticleId") - .IsRequired() - .HasColumnType("character varying(512)"); - - b.Property("AuthorId") - .IsRequired() - .HasColumnType("text"); - - b.Property("Content") - .IsRequired() - .HasColumnType("text"); - - b.Property("LatestReviewId") - .HasColumnType("uuid"); - - b.Property("PreviousRevisionId") - .HasColumnType("uuid"); - - b.Property("Timestamp") - .HasColumnType("timestamp with time zone"); - - b.Property("Title") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ArticleId"); - - b.HasIndex("AuthorId"); - - b.HasIndex("LatestReviewId") - .IsUnique(); - - b.HasIndex("PreviousRevisionId"); - - b.ToTable("Revisions"); - }); - - modelBuilder.Entity("CategoryRevision", b => - { - b.HasOne("Domain.Entities.Category", null) - .WithMany() - .HasForeignKey("CategoriesId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Revision", null) - .WithMany() - .HasForeignKey("RevisionsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Domain.Entities.Article", b => - { - b.HasOne("Domain.Entities.Article", "RedirectArticle") - .WithMany() - .HasForeignKey("RedirectArticleId") - .OnDelete(DeleteBehavior.Cascade); - - b.Navigation("RedirectArticle"); - }); - - modelBuilder.Entity("Domain.Entities.ArticleCategory", b => - { - b.HasOne("Domain.Entities.Article", "Article") - .WithMany("ArticleCategories") - .HasForeignKey("ArticleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Category", "Category") - .WithMany("CategoryArticles") - .HasForeignKey("CategoryId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Article"); - - b.Navigation("Category"); - }); - - modelBuilder.Entity("Domain.Entities.Category", b => - { - b.HasOne("Domain.Entities.Category", "Parent") - .WithMany("SubCategories") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.SetNull); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Domain.Entities.Review", b => - { - b.HasOne("Domain.Entities.Author", "Reviewer") - .WithMany() - .HasForeignKey("ReviewerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Revision", "Revision") - .WithMany("Reviews") - .HasForeignKey("RevisionId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Reviewer"); - - b.Navigation("Revision"); - }); - - modelBuilder.Entity("Domain.Entities.Revision", b => - { - b.HasOne("Domain.Entities.Article", "Article") - .WithMany("Revisions") - .HasForeignKey("ArticleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Author", "Author") - .WithMany() - .HasForeignKey("AuthorId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Review", "LatestReview") - .WithOne() - .HasForeignKey("Domain.Entities.Revision", "LatestReviewId"); - - b.HasOne("Domain.Entities.Revision", "PreviousRevision") - .WithMany() - .HasForeignKey("PreviousRevisionId"); - - b.Navigation("Article"); - - b.Navigation("Author"); - - b.Navigation("LatestReview"); - - b.Navigation("PreviousRevision"); - }); - - modelBuilder.Entity("Domain.Entities.Article", b => - { - b.Navigation("ArticleCategories"); - - b.Navigation("Revisions"); - }); - - modelBuilder.Entity("Domain.Entities.Category", b => - { - b.Navigation("CategoryArticles"); - - b.Navigation("SubCategories"); - }); - - modelBuilder.Entity("Domain.Entities.Revision", b => - { - b.Navigation("Reviews"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Infrastructure/Data/Migrations/20240116221526_AddedReviewTable.cs b/Infrastructure/Data/Migrations/20240116221526_AddedReviewTable.cs deleted file mode 100644 index 68a3d85..0000000 --- a/Infrastructure/Data/Migrations/20240116221526_AddedReviewTable.cs +++ /dev/null @@ -1,132 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Infrastructure.Data.Migrations -{ - /// - public partial class AddedReviewTable : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "Review", - table: "Revisions"); - - migrationBuilder.DropColumn( - name: "ReviewTimestamp", - table: "Revisions"); - - migrationBuilder.DropColumn( - name: "Reviewer", - table: "Revisions"); - - migrationBuilder.DropColumn( - name: "Status", - table: "Revisions"); - - migrationBuilder.AddColumn( - name: "LatestReviewId", - table: "Revisions", - type: "uuid", - nullable: true); - - migrationBuilder.CreateTable( - name: "Reviews", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false), - ReviewerId = table.Column(type: "text", nullable: false), - Status = table.Column(type: "integer", nullable: false), - Message = table.Column(type: "text", nullable: false), - ReviewTimestamp = table.Column(type: "timestamp with time zone", nullable: false), - RevisionId = table.Column(type: "uuid", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_Reviews", x => x.Id); - table.ForeignKey( - name: "FK_Reviews_Authors_ReviewerId", - column: x => x.ReviewerId, - principalTable: "Authors", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_Reviews_Revisions_RevisionId", - column: x => x.RevisionId, - principalTable: "Revisions", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateIndex( - name: "IX_Revisions_LatestReviewId", - table: "Revisions", - column: "LatestReviewId", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX_Reviews_ReviewerId", - table: "Reviews", - column: "ReviewerId"); - - migrationBuilder.CreateIndex( - name: "IX_Reviews_RevisionId", - table: "Reviews", - column: "RevisionId"); - - migrationBuilder.AddForeignKey( - name: "FK_Revisions_Reviews_LatestReviewId", - table: "Revisions", - column: "LatestReviewId", - principalTable: "Reviews", - principalColumn: "Id"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropForeignKey( - name: "FK_Revisions_Reviews_LatestReviewId", - table: "Revisions"); - - migrationBuilder.DropTable( - name: "Reviews"); - - migrationBuilder.DropIndex( - name: "IX_Revisions_LatestReviewId", - table: "Revisions"); - - migrationBuilder.DropColumn( - name: "LatestReviewId", - table: "Revisions"); - - migrationBuilder.AddColumn( - name: "Review", - table: "Revisions", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "ReviewTimestamp", - table: "Revisions", - type: "timestamp with time zone", - nullable: true); - - migrationBuilder.AddColumn( - name: "Reviewer", - table: "Revisions", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Status", - table: "Revisions", - type: "integer", - nullable: false, - defaultValue: 0); - } - } -} diff --git a/Infrastructure/Data/Migrations/20240117022728_RemovedRevisionSelfReferencingAndAddedCurrentRevisionIdForeignKeyToArticle.Designer.cs b/Infrastructure/Data/Migrations/20240117022728_RemovedRevisionSelfReferencingAndAddedCurrentRevisionIdForeignKeyToArticle.Designer.cs deleted file mode 100644 index 598d69f..0000000 --- a/Infrastructure/Data/Migrations/20240117022728_RemovedRevisionSelfReferencingAndAddedCurrentRevisionIdForeignKeyToArticle.Designer.cs +++ /dev/null @@ -1,320 +0,0 @@ -// -using System; -using Infrastructure.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace Infrastructure.Data.Migrations -{ - [DbContext(typeof(ApplicationDbContext))] - [Migration("20240117022728_RemovedRevisionSelfReferencingAndAddedCurrentRevisionIdForeignKeyToArticle")] - partial class RemovedRevisionSelfReferencingAndAddedCurrentRevisionIdForeignKeyToArticle - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "8.0.1") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("CategoryRevision", b => - { - b.Property("CategoriesId") - .HasColumnType("character varying(512)"); - - b.Property("RevisionsId") - .HasColumnType("uuid"); - - b.HasKey("CategoriesId", "RevisionsId"); - - b.HasIndex("RevisionsId"); - - b.ToTable("CategoryRevision"); - }); - - modelBuilder.Entity("Domain.Entities.Article", b => - { - b.Property("Id") - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("CurrentRevisionId") - .HasColumnType("uuid"); - - b.Property("IsHidden") - .HasColumnType("boolean"); - - b.Property("RedirectArticleId") - .HasColumnType("character varying(512)"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("character varying(128)"); - - b.HasKey("Id"); - - b.HasIndex("CurrentRevisionId") - .IsUnique(); - - b.HasIndex("RedirectArticleId"); - - b.ToTable("Articles"); - }); - - modelBuilder.Entity("Domain.Entities.ArticleCategory", b => - { - b.Property("ArticleId") - .HasColumnType("character varying(512)"); - - b.Property("CategoryId") - .HasColumnType("character varying(512)"); - - b.HasKey("ArticleId", "CategoryId"); - - b.HasIndex("CategoryId"); - - b.ToTable("ArticleCategories"); - }); - - modelBuilder.Entity("Domain.Entities.Author", b => - { - b.Property("Id") - .HasColumnType("text"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("Authors"); - }); - - modelBuilder.Entity("Domain.Entities.Category", b => - { - b.Property("Id") - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("character varying(128)"); - - b.Property("ParentId") - .HasColumnType("character varying(512)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.ToTable("Categories"); - }); - - modelBuilder.Entity("Domain.Entities.Review", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("Message") - .IsRequired() - .HasColumnType("text"); - - b.Property("ReviewTimestamp") - .HasColumnType("timestamp with time zone"); - - b.Property("ReviewerId") - .IsRequired() - .HasColumnType("text"); - - b.Property("RevisionId") - .HasColumnType("uuid"); - - b.Property("Status") - .HasColumnType("integer"); - - b.HasKey("Id"); - - b.HasIndex("ReviewerId"); - - b.HasIndex("RevisionId"); - - b.ToTable("Reviews"); - }); - - modelBuilder.Entity("Domain.Entities.Revision", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("ArticleId") - .IsRequired() - .HasColumnType("character varying(512)"); - - b.Property("AuthorId") - .IsRequired() - .HasColumnType("text"); - - b.Property("Content") - .IsRequired() - .HasColumnType("text"); - - b.Property("LatestReviewId") - .HasColumnType("uuid"); - - b.Property("Timestamp") - .HasColumnType("timestamp with time zone"); - - b.Property("Title") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ArticleId"); - - b.HasIndex("AuthorId"); - - b.HasIndex("LatestReviewId") - .IsUnique(); - - b.ToTable("Revisions"); - }); - - modelBuilder.Entity("CategoryRevision", b => - { - b.HasOne("Domain.Entities.Category", null) - .WithMany() - .HasForeignKey("CategoriesId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Revision", null) - .WithMany() - .HasForeignKey("RevisionsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Domain.Entities.Article", b => - { - b.HasOne("Domain.Entities.Revision", "CurrentRevision") - .WithOne() - .HasForeignKey("Domain.Entities.Article", "CurrentRevisionId"); - - b.HasOne("Domain.Entities.Article", "RedirectArticle") - .WithMany() - .HasForeignKey("RedirectArticleId") - .OnDelete(DeleteBehavior.Cascade); - - b.Navigation("CurrentRevision"); - - b.Navigation("RedirectArticle"); - }); - - modelBuilder.Entity("Domain.Entities.ArticleCategory", b => - { - b.HasOne("Domain.Entities.Article", "Article") - .WithMany("ArticleCategories") - .HasForeignKey("ArticleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Category", "Category") - .WithMany("CategoryArticles") - .HasForeignKey("CategoryId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Article"); - - b.Navigation("Category"); - }); - - modelBuilder.Entity("Domain.Entities.Category", b => - { - b.HasOne("Domain.Entities.Category", "Parent") - .WithMany("SubCategories") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.SetNull); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Domain.Entities.Review", b => - { - b.HasOne("Domain.Entities.Author", "Reviewer") - .WithMany() - .HasForeignKey("ReviewerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Revision", "Revision") - .WithMany("Reviews") - .HasForeignKey("RevisionId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Reviewer"); - - b.Navigation("Revision"); - }); - - modelBuilder.Entity("Domain.Entities.Revision", b => - { - b.HasOne("Domain.Entities.Article", "Article") - .WithMany("Revisions") - .HasForeignKey("ArticleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Author", "Author") - .WithMany() - .HasForeignKey("AuthorId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Review", "LatestReview") - .WithOne() - .HasForeignKey("Domain.Entities.Revision", "LatestReviewId"); - - b.Navigation("Article"); - - b.Navigation("Author"); - - b.Navigation("LatestReview"); - }); - - modelBuilder.Entity("Domain.Entities.Article", b => - { - b.Navigation("ArticleCategories"); - - b.Navigation("Revisions"); - }); - - modelBuilder.Entity("Domain.Entities.Category", b => - { - b.Navigation("CategoryArticles"); - - b.Navigation("SubCategories"); - }); - - modelBuilder.Entity("Domain.Entities.Revision", b => - { - b.Navigation("Reviews"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Infrastructure/Data/Migrations/20240117022728_RemovedRevisionSelfReferencingAndAddedCurrentRevisionIdForeignKeyToArticle.cs b/Infrastructure/Data/Migrations/20240117022728_RemovedRevisionSelfReferencingAndAddedCurrentRevisionIdForeignKeyToArticle.cs deleted file mode 100644 index 879df54..0000000 --- a/Infrastructure/Data/Migrations/20240117022728_RemovedRevisionSelfReferencingAndAddedCurrentRevisionIdForeignKeyToArticle.cs +++ /dev/null @@ -1,80 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Infrastructure.Data.Migrations -{ - /// - public partial class RemovedRevisionSelfReferencingAndAddedCurrentRevisionIdForeignKeyToArticle : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropForeignKey( - name: "FK_Revisions_Revisions_PreviousRevisionId", - table: "Revisions"); - - migrationBuilder.DropIndex( - name: "IX_Revisions_PreviousRevisionId", - table: "Revisions"); - - migrationBuilder.DropColumn( - name: "PreviousRevisionId", - table: "Revisions"); - - migrationBuilder.AddColumn( - name: "CurrentRevisionId", - table: "Articles", - type: "uuid", - nullable: true); - - migrationBuilder.CreateIndex( - name: "IX_Articles_CurrentRevisionId", - table: "Articles", - column: "CurrentRevisionId", - unique: true); - - migrationBuilder.AddForeignKey( - name: "FK_Articles_Revisions_CurrentRevisionId", - table: "Articles", - column: "CurrentRevisionId", - principalTable: "Revisions", - principalColumn: "Id"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropForeignKey( - name: "FK_Articles_Revisions_CurrentRevisionId", - table: "Articles"); - - migrationBuilder.DropIndex( - name: "IX_Articles_CurrentRevisionId", - table: "Articles"); - - migrationBuilder.DropColumn( - name: "CurrentRevisionId", - table: "Articles"); - - migrationBuilder.AddColumn( - name: "PreviousRevisionId", - table: "Revisions", - type: "uuid", - nullable: true); - - migrationBuilder.CreateIndex( - name: "IX_Revisions_PreviousRevisionId", - table: "Revisions", - column: "PreviousRevisionId"); - - migrationBuilder.AddForeignKey( - name: "FK_Revisions_Revisions_PreviousRevisionId", - table: "Revisions", - column: "PreviousRevisionId", - principalTable: "Revisions", - principalColumn: "Id"); - } - } -} diff --git a/Infrastructure/Data/Migrations/20240119042942_RemovedIsHiddenFromArticleRemovedTitleFromRevision.Designer.cs b/Infrastructure/Data/Migrations/20240119042942_RemovedIsHiddenFromArticleRemovedTitleFromRevision.Designer.cs deleted file mode 100644 index 370e193..0000000 --- a/Infrastructure/Data/Migrations/20240119042942_RemovedIsHiddenFromArticleRemovedTitleFromRevision.Designer.cs +++ /dev/null @@ -1,309 +0,0 @@ -// -using System; -using Infrastructure.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace Infrastructure.Data.Migrations -{ - [DbContext(typeof(ApplicationDbContext))] - [Migration("20240119042942_RemovedIsHiddenFromArticleRemovedTitleFromRevision")] - partial class RemovedIsHiddenFromArticleRemovedTitleFromRevision - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "8.0.1") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("CategoryRevision", b => - { - b.Property("CategoriesId") - .HasColumnType("character varying(512)"); - - b.Property("RevisionsId") - .HasColumnType("uuid"); - - b.HasKey("CategoriesId", "RevisionsId"); - - b.HasIndex("RevisionsId"); - - b.ToTable("CategoryRevision"); - }); - - modelBuilder.Entity("Domain.Entities.Article", b => - { - b.Property("Id") - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("CurrentRevisionId") - .HasColumnType("uuid"); - - b.Property("RedirectArticleId") - .HasColumnType("character varying(512)"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("character varying(128)"); - - b.HasKey("Id"); - - b.HasIndex("CurrentRevisionId") - .IsUnique(); - - b.HasIndex("RedirectArticleId"); - - b.ToTable("Articles"); - }); - - modelBuilder.Entity("Domain.Entities.ArticleCategory", b => - { - b.Property("ArticleId") - .HasColumnType("character varying(512)"); - - b.Property("CategoryId") - .HasColumnType("character varying(512)"); - - b.HasKey("ArticleId", "CategoryId"); - - b.HasIndex("CategoryId"); - - b.ToTable("ArticleCategories"); - }); - - modelBuilder.Entity("Domain.Entities.Author", b => - { - b.Property("Id") - .HasColumnType("text"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("Authors"); - }); - - modelBuilder.Entity("Domain.Entities.Category", b => - { - b.Property("Id") - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("character varying(128)"); - - b.Property("ParentId") - .HasColumnType("character varying(512)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.ToTable("Categories"); - }); - - modelBuilder.Entity("Domain.Entities.Review", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("Message") - .IsRequired() - .HasColumnType("text"); - - b.Property("ReviewTimestamp") - .HasColumnType("timestamp with time zone"); - - b.Property("ReviewerId") - .IsRequired() - .HasColumnType("text"); - - b.Property("RevisionId") - .HasColumnType("uuid"); - - b.Property("Status") - .HasColumnType("integer"); - - b.HasKey("Id"); - - b.HasIndex("ReviewerId"); - - b.HasIndex("RevisionId"); - - b.ToTable("Reviews"); - }); - - modelBuilder.Entity("Domain.Entities.Revision", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("ArticleId") - .IsRequired() - .HasColumnType("character varying(512)"); - - b.Property("AuthorId") - .IsRequired() - .HasColumnType("text"); - - b.Property("Content") - .IsRequired() - .HasColumnType("text"); - - b.Property("LatestReviewId") - .HasColumnType("uuid"); - - b.Property("Timestamp") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.HasIndex("ArticleId"); - - b.HasIndex("AuthorId"); - - b.HasIndex("LatestReviewId") - .IsUnique(); - - b.ToTable("Revisions"); - }); - - modelBuilder.Entity("CategoryRevision", b => - { - b.HasOne("Domain.Entities.Category", null) - .WithMany() - .HasForeignKey("CategoriesId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Revision", null) - .WithMany() - .HasForeignKey("RevisionsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Domain.Entities.Article", b => - { - b.HasOne("Domain.Entities.Revision", "CurrentRevision") - .WithOne() - .HasForeignKey("Domain.Entities.Article", "CurrentRevisionId"); - - b.HasOne("Domain.Entities.Article", "RedirectArticle") - .WithMany() - .HasForeignKey("RedirectArticleId") - .OnDelete(DeleteBehavior.Cascade); - - b.Navigation("CurrentRevision"); - - b.Navigation("RedirectArticle"); - }); - - modelBuilder.Entity("Domain.Entities.ArticleCategory", b => - { - b.HasOne("Domain.Entities.Article", "Article") - .WithMany() - .HasForeignKey("ArticleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Category", "Category") - .WithMany() - .HasForeignKey("CategoryId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Article"); - - b.Navigation("Category"); - }); - - modelBuilder.Entity("Domain.Entities.Category", b => - { - b.HasOne("Domain.Entities.Category", "Parent") - .WithMany("SubCategories") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.SetNull); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Domain.Entities.Review", b => - { - b.HasOne("Domain.Entities.Author", "Reviewer") - .WithMany() - .HasForeignKey("ReviewerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Revision", "Revision") - .WithMany("Reviews") - .HasForeignKey("RevisionId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Reviewer"); - - b.Navigation("Revision"); - }); - - modelBuilder.Entity("Domain.Entities.Revision", b => - { - b.HasOne("Domain.Entities.Article", "Article") - .WithMany("Revisions") - .HasForeignKey("ArticleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Author", "Author") - .WithMany() - .HasForeignKey("AuthorId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Review", "LatestReview") - .WithOne() - .HasForeignKey("Domain.Entities.Revision", "LatestReviewId"); - - b.Navigation("Article"); - - b.Navigation("Author"); - - b.Navigation("LatestReview"); - }); - - modelBuilder.Entity("Domain.Entities.Article", b => - { - b.Navigation("Revisions"); - }); - - modelBuilder.Entity("Domain.Entities.Category", b => - { - b.Navigation("SubCategories"); - }); - - modelBuilder.Entity("Domain.Entities.Revision", b => - { - b.Navigation("Reviews"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Infrastructure/Data/Migrations/20240119042942_RemovedIsHiddenFromArticleRemovedTitleFromRevision.cs b/Infrastructure/Data/Migrations/20240119042942_RemovedIsHiddenFromArticleRemovedTitleFromRevision.cs deleted file mode 100644 index fd73694..0000000 --- a/Infrastructure/Data/Migrations/20240119042942_RemovedIsHiddenFromArticleRemovedTitleFromRevision.cs +++ /dev/null @@ -1,40 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Infrastructure.Data.Migrations -{ - /// - public partial class RemovedIsHiddenFromArticleRemovedTitleFromRevision : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "Title", - table: "Revisions"); - - migrationBuilder.DropColumn( - name: "IsHidden", - table: "Articles"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn( - name: "Title", - table: "Revisions", - type: "text", - nullable: false, - defaultValue: ""); - - migrationBuilder.AddColumn( - name: "IsHidden", - table: "Articles", - type: "boolean", - nullable: false, - defaultValue: false); - } - } -} diff --git a/Infrastructure/Data/Migrations/20240120033142_RemovedManyToManyBetweenArticlesAndCategories.Designer.cs b/Infrastructure/Data/Migrations/20240120033142_RemovedManyToManyBetweenArticlesAndCategories.Designer.cs deleted file mode 100644 index 22baaf6..0000000 --- a/Infrastructure/Data/Migrations/20240120033142_RemovedManyToManyBetweenArticlesAndCategories.Designer.cs +++ /dev/null @@ -1,275 +0,0 @@ -// -using System; -using Infrastructure.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace Infrastructure.Data.Migrations -{ - [DbContext(typeof(ApplicationDbContext))] - [Migration("20240120033142_RemovedManyToManyBetweenArticlesAndCategories")] - partial class RemovedManyToManyBetweenArticlesAndCategories - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "8.0.1") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("CategoryRevision", b => - { - b.Property("CategoriesId") - .HasColumnType("character varying(512)"); - - b.Property("RevisionsId") - .HasColumnType("uuid"); - - b.HasKey("CategoriesId", "RevisionsId"); - - b.HasIndex("RevisionsId"); - - b.ToTable("CategoryRevision"); - }); - - modelBuilder.Entity("Domain.Entities.Article", b => - { - b.Property("Id") - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("CurrentRevisionId") - .HasColumnType("uuid"); - - b.Property("RedirectArticleId") - .HasColumnType("character varying(512)"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("character varying(128)"); - - b.HasKey("Id"); - - b.HasIndex("CurrentRevisionId") - .IsUnique(); - - b.HasIndex("RedirectArticleId"); - - b.ToTable("Articles"); - }); - - modelBuilder.Entity("Domain.Entities.Author", b => - { - b.Property("Id") - .HasColumnType("text"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("Authors"); - }); - - modelBuilder.Entity("Domain.Entities.Category", b => - { - b.Property("Id") - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("character varying(128)"); - - b.Property("ParentId") - .HasColumnType("character varying(512)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.ToTable("Categories"); - }); - - modelBuilder.Entity("Domain.Entities.Review", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("Message") - .IsRequired() - .HasColumnType("text"); - - b.Property("ReviewTimestamp") - .HasColumnType("timestamp with time zone"); - - b.Property("ReviewerId") - .IsRequired() - .HasColumnType("text"); - - b.Property("RevisionId") - .HasColumnType("uuid"); - - b.Property("Status") - .HasColumnType("integer"); - - b.HasKey("Id"); - - b.HasIndex("ReviewerId"); - - b.HasIndex("RevisionId"); - - b.ToTable("Reviews"); - }); - - modelBuilder.Entity("Domain.Entities.Revision", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("ArticleId") - .IsRequired() - .HasColumnType("character varying(512)"); - - b.Property("AuthorId") - .IsRequired() - .HasColumnType("text"); - - b.Property("Content") - .IsRequired() - .HasColumnType("text"); - - b.Property("LatestReviewId") - .HasColumnType("uuid"); - - b.Property("Timestamp") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.HasIndex("ArticleId"); - - b.HasIndex("AuthorId"); - - b.HasIndex("LatestReviewId") - .IsUnique(); - - b.ToTable("Revisions"); - }); - - modelBuilder.Entity("CategoryRevision", b => - { - b.HasOne("Domain.Entities.Category", null) - .WithMany() - .HasForeignKey("CategoriesId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Revision", null) - .WithMany() - .HasForeignKey("RevisionsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Domain.Entities.Article", b => - { - b.HasOne("Domain.Entities.Revision", "CurrentRevision") - .WithOne() - .HasForeignKey("Domain.Entities.Article", "CurrentRevisionId"); - - b.HasOne("Domain.Entities.Article", "RedirectArticle") - .WithMany() - .HasForeignKey("RedirectArticleId") - .OnDelete(DeleteBehavior.Cascade); - - b.Navigation("CurrentRevision"); - - b.Navigation("RedirectArticle"); - }); - - modelBuilder.Entity("Domain.Entities.Category", b => - { - b.HasOne("Domain.Entities.Category", "Parent") - .WithMany("SubCategories") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.SetNull); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Domain.Entities.Review", b => - { - b.HasOne("Domain.Entities.Author", "Reviewer") - .WithMany() - .HasForeignKey("ReviewerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Revision", "Revision") - .WithMany("Reviews") - .HasForeignKey("RevisionId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Reviewer"); - - b.Navigation("Revision"); - }); - - modelBuilder.Entity("Domain.Entities.Revision", b => - { - b.HasOne("Domain.Entities.Article", "Article") - .WithMany("Revisions") - .HasForeignKey("ArticleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Author", "Author") - .WithMany() - .HasForeignKey("AuthorId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Review", "LatestReview") - .WithOne() - .HasForeignKey("Domain.Entities.Revision", "LatestReviewId"); - - b.Navigation("Article"); - - b.Navigation("Author"); - - b.Navigation("LatestReview"); - }); - - modelBuilder.Entity("Domain.Entities.Article", b => - { - b.Navigation("Revisions"); - }); - - modelBuilder.Entity("Domain.Entities.Category", b => - { - b.Navigation("SubCategories"); - }); - - modelBuilder.Entity("Domain.Entities.Revision", b => - { - b.Navigation("Reviews"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Infrastructure/Data/Migrations/20240120033142_RemovedManyToManyBetweenArticlesAndCategories.cs b/Infrastructure/Data/Migrations/20240120033142_RemovedManyToManyBetweenArticlesAndCategories.cs deleted file mode 100644 index 295cf08..0000000 --- a/Infrastructure/Data/Migrations/20240120033142_RemovedManyToManyBetweenArticlesAndCategories.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Infrastructure.Data.Migrations -{ - /// - public partial class RemovedManyToManyBetweenArticlesAndCategories : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "ArticleCategories"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "ArticleCategories", - columns: table => new - { - ArticleId = table.Column(type: "character varying(512)", nullable: false), - CategoryId = table.Column(type: "character varying(512)", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_ArticleCategories", x => new { x.ArticleId, x.CategoryId }); - table.ForeignKey( - name: "FK_ArticleCategories_Articles_ArticleId", - column: x => x.ArticleId, - principalTable: "Articles", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_ArticleCategories_Categories_CategoryId", - column: x => x.CategoryId, - principalTable: "Categories", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateIndex( - name: "IX_ArticleCategories_CategoryId", - table: "ArticleCategories", - column: "CategoryId"); - } - } -} diff --git a/Infrastructure/Data/Migrations/20240120201614_AddedNavigations.Designer.cs b/Infrastructure/Data/Migrations/20240120201614_AddedNavigations.Designer.cs deleted file mode 100644 index e93f5f8..0000000 --- a/Infrastructure/Data/Migrations/20240120201614_AddedNavigations.Designer.cs +++ /dev/null @@ -1,318 +0,0 @@ -// -using System; -using Infrastructure.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace Infrastructure.Data.Migrations -{ - [DbContext(typeof(ApplicationDbContext))] - [Migration("20240120201614_AddedNavigations")] - partial class AddedNavigations - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "8.0.1") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("CategoryRevision", b => - { - b.Property("CategoriesId") - .HasColumnType("character varying(512)"); - - b.Property("RevisionsId") - .HasColumnType("uuid"); - - b.HasKey("CategoriesId", "RevisionsId"); - - b.HasIndex("RevisionsId"); - - b.ToTable("CategoryRevision"); - }); - - modelBuilder.Entity("Domain.Entities.Article", b => - { - b.Property("Id") - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("CurrentRevisionId") - .HasColumnType("uuid"); - - b.Property("RedirectArticleId") - .HasColumnType("character varying(512)"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("character varying(128)"); - - b.HasKey("Id"); - - b.HasIndex("CurrentRevisionId") - .IsUnique(); - - b.HasIndex("RedirectArticleId"); - - b.ToTable("Articles"); - }); - - modelBuilder.Entity("Domain.Entities.Author", b => - { - b.Property("Id") - .HasColumnType("text"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("Authors"); - }); - - modelBuilder.Entity("Domain.Entities.Category", b => - { - b.Property("Id") - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("character varying(128)"); - - b.Property("ParentId") - .HasColumnType("character varying(512)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.ToTable("Categories"); - }); - - modelBuilder.Entity("Domain.Entities.Navigation", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.Property("NavigationId") - .HasColumnType("integer"); - - b.Property("ParentId") - .HasColumnType("integer"); - - b.Property("Uri") - .HasColumnType("text"); - - b.Property("Weight") - .HasColumnType("integer"); - - b.HasKey("Id"); - - b.HasIndex("NavigationId"); - - b.ToTable("Navigations"); - }); - - modelBuilder.Entity("Domain.Entities.Review", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("Message") - .IsRequired() - .HasColumnType("text"); - - b.Property("ReviewTimestamp") - .HasColumnType("timestamp with time zone"); - - b.Property("ReviewerId") - .IsRequired() - .HasColumnType("text"); - - b.Property("RevisionId") - .HasColumnType("uuid"); - - b.Property("Status") - .HasColumnType("integer"); - - b.HasKey("Id"); - - b.HasIndex("ReviewerId"); - - b.HasIndex("RevisionId"); - - b.ToTable("Reviews"); - }); - - modelBuilder.Entity("Domain.Entities.Revision", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("ArticleId") - .IsRequired() - .HasColumnType("character varying(512)"); - - b.Property("AuthorId") - .IsRequired() - .HasColumnType("text"); - - b.Property("Content") - .IsRequired() - .HasColumnType("text"); - - b.Property("LatestReviewId") - .HasColumnType("uuid"); - - b.Property("Timestamp") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.HasIndex("ArticleId"); - - b.HasIndex("AuthorId"); - - b.HasIndex("LatestReviewId") - .IsUnique(); - - b.ToTable("Revisions"); - }); - - modelBuilder.Entity("CategoryRevision", b => - { - b.HasOne("Domain.Entities.Category", null) - .WithMany() - .HasForeignKey("CategoriesId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Revision", null) - .WithMany() - .HasForeignKey("RevisionsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Domain.Entities.Article", b => - { - b.HasOne("Domain.Entities.Revision", "CurrentRevision") - .WithOne() - .HasForeignKey("Domain.Entities.Article", "CurrentRevisionId"); - - b.HasOne("Domain.Entities.Article", "RedirectArticle") - .WithMany() - .HasForeignKey("RedirectArticleId") - .OnDelete(DeleteBehavior.Cascade); - - b.Navigation("CurrentRevision"); - - b.Navigation("RedirectArticle"); - }); - - modelBuilder.Entity("Domain.Entities.Category", b => - { - b.HasOne("Domain.Entities.Category", "Parent") - .WithMany("SubCategories") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.SetNull); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Domain.Entities.Navigation", b => - { - b.HasOne("Domain.Entities.Navigation", null) - .WithMany("Children") - .HasForeignKey("NavigationId"); - }); - - modelBuilder.Entity("Domain.Entities.Review", b => - { - b.HasOne("Domain.Entities.Author", "Reviewer") - .WithMany() - .HasForeignKey("ReviewerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Revision", "Revision") - .WithMany("Reviews") - .HasForeignKey("RevisionId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Reviewer"); - - b.Navigation("Revision"); - }); - - modelBuilder.Entity("Domain.Entities.Revision", b => - { - b.HasOne("Domain.Entities.Article", "Article") - .WithMany("Revisions") - .HasForeignKey("ArticleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Author", "Author") - .WithMany() - .HasForeignKey("AuthorId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Review", "LatestReview") - .WithOne() - .HasForeignKey("Domain.Entities.Revision", "LatestReviewId"); - - b.Navigation("Article"); - - b.Navigation("Author"); - - b.Navigation("LatestReview"); - }); - - modelBuilder.Entity("Domain.Entities.Article", b => - { - b.Navigation("Revisions"); - }); - - modelBuilder.Entity("Domain.Entities.Category", b => - { - b.Navigation("SubCategories"); - }); - - modelBuilder.Entity("Domain.Entities.Navigation", b => - { - b.Navigation("Children"); - }); - - modelBuilder.Entity("Domain.Entities.Revision", b => - { - b.Navigation("Reviews"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Infrastructure/Data/Migrations/20240120201614_AddedNavigations.cs b/Infrastructure/Data/Migrations/20240120201614_AddedNavigations.cs deleted file mode 100644 index ef2e428..0000000 --- a/Infrastructure/Data/Migrations/20240120201614_AddedNavigations.cs +++ /dev/null @@ -1,49 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace Infrastructure.Data.Migrations -{ - /// - public partial class AddedNavigations : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "Navigations", - columns: table => new - { - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - Name = table.Column(type: "text", nullable: false), - Weight = table.Column(type: "integer", nullable: false), - Uri = table.Column(type: "text", nullable: true), - ParentId = table.Column(type: "integer", nullable: true), - NavigationId = table.Column(type: "integer", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Navigations", x => x.Id); - table.ForeignKey( - name: "FK_Navigations_Navigations_NavigationId", - column: x => x.NavigationId, - principalTable: "Navigations", - principalColumn: "Id"); - }); - - migrationBuilder.CreateIndex( - name: "IX_Navigations_NavigationId", - table: "Navigations", - column: "NavigationId"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "Navigations"); - } - } -} diff --git a/Infrastructure/Data/Migrations/20240122144604_AddedNavigationInconToNavigations.Designer.cs b/Infrastructure/Data/Migrations/20240122144604_AddedNavigationInconToNavigations.Designer.cs deleted file mode 100644 index 39e5d7e..0000000 --- a/Infrastructure/Data/Migrations/20240122144604_AddedNavigationInconToNavigations.Designer.cs +++ /dev/null @@ -1,321 +0,0 @@ -// -using System; -using Infrastructure.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace Infrastructure.Data.Migrations -{ - [DbContext(typeof(ApplicationDbContext))] - [Migration("20240122144604_AddedNavigationInconToNavigations")] - partial class AddedNavigationInconToNavigations - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "8.0.1") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("CategoryRevision", b => - { - b.Property("CategoriesId") - .HasColumnType("character varying(512)"); - - b.Property("RevisionsId") - .HasColumnType("uuid"); - - b.HasKey("CategoriesId", "RevisionsId"); - - b.HasIndex("RevisionsId"); - - b.ToTable("CategoryRevision"); - }); - - modelBuilder.Entity("Domain.Entities.Article", b => - { - b.Property("Id") - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("CurrentRevisionId") - .HasColumnType("uuid"); - - b.Property("RedirectArticleId") - .HasColumnType("character varying(512)"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("character varying(128)"); - - b.HasKey("Id"); - - b.HasIndex("CurrentRevisionId") - .IsUnique(); - - b.HasIndex("RedirectArticleId"); - - b.ToTable("Articles"); - }); - - modelBuilder.Entity("Domain.Entities.Author", b => - { - b.Property("Id") - .HasColumnType("text"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("Authors"); - }); - - modelBuilder.Entity("Domain.Entities.Category", b => - { - b.Property("Id") - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("character varying(128)"); - - b.Property("ParentId") - .HasColumnType("character varying(512)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.ToTable("Categories"); - }); - - modelBuilder.Entity("Domain.Entities.Navigation", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Icon") - .HasColumnType("text"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.Property("NavigationId") - .HasColumnType("integer"); - - b.Property("ParentId") - .HasColumnType("integer"); - - b.Property("Uri") - .HasColumnType("text"); - - b.Property("Weight") - .HasColumnType("integer"); - - b.HasKey("Id"); - - b.HasIndex("NavigationId"); - - b.ToTable("Navigations"); - }); - - modelBuilder.Entity("Domain.Entities.Review", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("Message") - .IsRequired() - .HasColumnType("text"); - - b.Property("ReviewTimestamp") - .HasColumnType("timestamp with time zone"); - - b.Property("ReviewerId") - .IsRequired() - .HasColumnType("text"); - - b.Property("RevisionId") - .HasColumnType("uuid"); - - b.Property("Status") - .HasColumnType("integer"); - - b.HasKey("Id"); - - b.HasIndex("ReviewerId"); - - b.HasIndex("RevisionId"); - - b.ToTable("Reviews"); - }); - - modelBuilder.Entity("Domain.Entities.Revision", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("ArticleId") - .IsRequired() - .HasColumnType("character varying(512)"); - - b.Property("AuthorId") - .IsRequired() - .HasColumnType("text"); - - b.Property("Content") - .IsRequired() - .HasColumnType("text"); - - b.Property("LatestReviewId") - .HasColumnType("uuid"); - - b.Property("Timestamp") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.HasIndex("ArticleId"); - - b.HasIndex("AuthorId"); - - b.HasIndex("LatestReviewId") - .IsUnique(); - - b.ToTable("Revisions"); - }); - - modelBuilder.Entity("CategoryRevision", b => - { - b.HasOne("Domain.Entities.Category", null) - .WithMany() - .HasForeignKey("CategoriesId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Revision", null) - .WithMany() - .HasForeignKey("RevisionsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Domain.Entities.Article", b => - { - b.HasOne("Domain.Entities.Revision", "CurrentRevision") - .WithOne() - .HasForeignKey("Domain.Entities.Article", "CurrentRevisionId"); - - b.HasOne("Domain.Entities.Article", "RedirectArticle") - .WithMany() - .HasForeignKey("RedirectArticleId") - .OnDelete(DeleteBehavior.Cascade); - - b.Navigation("CurrentRevision"); - - b.Navigation("RedirectArticle"); - }); - - modelBuilder.Entity("Domain.Entities.Category", b => - { - b.HasOne("Domain.Entities.Category", "Parent") - .WithMany("SubCategories") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.SetNull); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Domain.Entities.Navigation", b => - { - b.HasOne("Domain.Entities.Navigation", null) - .WithMany("Children") - .HasForeignKey("NavigationId"); - }); - - modelBuilder.Entity("Domain.Entities.Review", b => - { - b.HasOne("Domain.Entities.Author", "Reviewer") - .WithMany() - .HasForeignKey("ReviewerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Revision", "Revision") - .WithMany("Reviews") - .HasForeignKey("RevisionId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Reviewer"); - - b.Navigation("Revision"); - }); - - modelBuilder.Entity("Domain.Entities.Revision", b => - { - b.HasOne("Domain.Entities.Article", "Article") - .WithMany("Revisions") - .HasForeignKey("ArticleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Author", "Author") - .WithMany() - .HasForeignKey("AuthorId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Review", "LatestReview") - .WithOne() - .HasForeignKey("Domain.Entities.Revision", "LatestReviewId"); - - b.Navigation("Article"); - - b.Navigation("Author"); - - b.Navigation("LatestReview"); - }); - - modelBuilder.Entity("Domain.Entities.Article", b => - { - b.Navigation("Revisions"); - }); - - modelBuilder.Entity("Domain.Entities.Category", b => - { - b.Navigation("SubCategories"); - }); - - modelBuilder.Entity("Domain.Entities.Navigation", b => - { - b.Navigation("Children"); - }); - - modelBuilder.Entity("Domain.Entities.Revision", b => - { - b.Navigation("Reviews"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Infrastructure/Data/Migrations/20240122144604_AddedNavigationInconToNavigations.cs b/Infrastructure/Data/Migrations/20240122144604_AddedNavigationInconToNavigations.cs deleted file mode 100644 index 80087bd..0000000 --- a/Infrastructure/Data/Migrations/20240122144604_AddedNavigationInconToNavigations.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Infrastructure.Data.Migrations -{ - /// - public partial class AddedNavigationInconToNavigations : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn( - name: "Icon", - table: "Navigations", - type: "text", - nullable: true); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "Icon", - table: "Navigations"); - } - } -} diff --git a/Infrastructure/Data/Migrations/20240122175622_FixedWrongForeignKeyNameInNavigations.Designer.cs b/Infrastructure/Data/Migrations/20240122175622_FixedWrongForeignKeyNameInNavigations.Designer.cs deleted file mode 100644 index 3cd65df..0000000 --- a/Infrastructure/Data/Migrations/20240122175622_FixedWrongForeignKeyNameInNavigations.Designer.cs +++ /dev/null @@ -1,321 +0,0 @@ -// -using System; -using Infrastructure.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace Infrastructure.Data.Migrations -{ - [DbContext(typeof(ApplicationDbContext))] - [Migration("20240122175622_FixedWrongForeignKeyNameInNavigations")] - partial class FixedWrongForeignKeyNameInNavigations - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "8.0.1") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("CategoryRevision", b => - { - b.Property("CategoriesId") - .HasColumnType("character varying(512)"); - - b.Property("RevisionsId") - .HasColumnType("uuid"); - - b.HasKey("CategoriesId", "RevisionsId"); - - b.HasIndex("RevisionsId"); - - b.ToTable("CategoryRevision"); - }); - - modelBuilder.Entity("Domain.Entities.Article", b => - { - b.Property("Id") - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("CurrentRevisionId") - .HasColumnType("uuid"); - - b.Property("RedirectArticleId") - .HasColumnType("character varying(512)"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("character varying(128)"); - - b.HasKey("Id"); - - b.HasIndex("CurrentRevisionId") - .IsUnique(); - - b.HasIndex("RedirectArticleId"); - - b.ToTable("Articles"); - }); - - modelBuilder.Entity("Domain.Entities.Author", b => - { - b.Property("Id") - .HasColumnType("text"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("Authors"); - }); - - modelBuilder.Entity("Domain.Entities.Category", b => - { - b.Property("Id") - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("character varying(128)"); - - b.Property("ParentId") - .HasColumnType("character varying(512)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.ToTable("Categories"); - }); - - modelBuilder.Entity("Domain.Entities.Navigation", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Icon") - .HasColumnType("text"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.Property("ParentId") - .HasColumnType("integer"); - - b.Property("Uri") - .HasColumnType("text"); - - b.Property("Weight") - .HasColumnType("integer"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.ToTable("Navigations"); - }); - - modelBuilder.Entity("Domain.Entities.Review", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("Message") - .IsRequired() - .HasColumnType("text"); - - b.Property("ReviewTimestamp") - .HasColumnType("timestamp with time zone"); - - b.Property("ReviewerId") - .IsRequired() - .HasColumnType("text"); - - b.Property("RevisionId") - .HasColumnType("uuid"); - - b.Property("Status") - .HasColumnType("integer"); - - b.HasKey("Id"); - - b.HasIndex("ReviewerId"); - - b.HasIndex("RevisionId"); - - b.ToTable("Reviews"); - }); - - modelBuilder.Entity("Domain.Entities.Revision", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("ArticleId") - .IsRequired() - .HasColumnType("character varying(512)"); - - b.Property("AuthorId") - .IsRequired() - .HasColumnType("text"); - - b.Property("Content") - .IsRequired() - .HasColumnType("text"); - - b.Property("LatestReviewId") - .HasColumnType("uuid"); - - b.Property("Timestamp") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.HasIndex("ArticleId"); - - b.HasIndex("AuthorId"); - - b.HasIndex("LatestReviewId") - .IsUnique(); - - b.ToTable("Revisions"); - }); - - modelBuilder.Entity("CategoryRevision", b => - { - b.HasOne("Domain.Entities.Category", null) - .WithMany() - .HasForeignKey("CategoriesId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Revision", null) - .WithMany() - .HasForeignKey("RevisionsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Domain.Entities.Article", b => - { - b.HasOne("Domain.Entities.Revision", "CurrentRevision") - .WithOne() - .HasForeignKey("Domain.Entities.Article", "CurrentRevisionId"); - - b.HasOne("Domain.Entities.Article", "RedirectArticle") - .WithMany() - .HasForeignKey("RedirectArticleId") - .OnDelete(DeleteBehavior.Cascade); - - b.Navigation("CurrentRevision"); - - b.Navigation("RedirectArticle"); - }); - - modelBuilder.Entity("Domain.Entities.Category", b => - { - b.HasOne("Domain.Entities.Category", "Parent") - .WithMany("SubCategories") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.SetNull); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Domain.Entities.Navigation", b => - { - b.HasOne("Domain.Entities.Navigation", "Parent") - .WithMany("Children") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.SetNull); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Domain.Entities.Review", b => - { - b.HasOne("Domain.Entities.Author", "Reviewer") - .WithMany() - .HasForeignKey("ReviewerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Revision", "Revision") - .WithMany("Reviews") - .HasForeignKey("RevisionId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Reviewer"); - - b.Navigation("Revision"); - }); - - modelBuilder.Entity("Domain.Entities.Revision", b => - { - b.HasOne("Domain.Entities.Article", "Article") - .WithMany("Revisions") - .HasForeignKey("ArticleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Author", "Author") - .WithMany() - .HasForeignKey("AuthorId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Review", "LatestReview") - .WithOne() - .HasForeignKey("Domain.Entities.Revision", "LatestReviewId"); - - b.Navigation("Article"); - - b.Navigation("Author"); - - b.Navigation("LatestReview"); - }); - - modelBuilder.Entity("Domain.Entities.Article", b => - { - b.Navigation("Revisions"); - }); - - modelBuilder.Entity("Domain.Entities.Category", b => - { - b.Navigation("SubCategories"); - }); - - modelBuilder.Entity("Domain.Entities.Navigation", b => - { - b.Navigation("Children"); - }); - - modelBuilder.Entity("Domain.Entities.Revision", b => - { - b.Navigation("Reviews"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Infrastructure/Data/Migrations/20240122175622_FixedWrongForeignKeyNameInNavigations.cs b/Infrastructure/Data/Migrations/20240122175622_FixedWrongForeignKeyNameInNavigations.cs deleted file mode 100644 index ae82e3f..0000000 --- a/Infrastructure/Data/Migrations/20240122175622_FixedWrongForeignKeyNameInNavigations.cs +++ /dev/null @@ -1,69 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Infrastructure.Data.Migrations -{ - /// - public partial class FixedWrongForeignKeyNameInNavigations : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropForeignKey( - name: "FK_Navigations_Navigations_NavigationId", - table: "Navigations"); - - migrationBuilder.DropIndex( - name: "IX_Navigations_NavigationId", - table: "Navigations"); - - migrationBuilder.DropColumn( - name: "NavigationId", - table: "Navigations"); - - migrationBuilder.CreateIndex( - name: "IX_Navigations_ParentId", - table: "Navigations", - column: "ParentId"); - - migrationBuilder.AddForeignKey( - name: "FK_Navigations_Navigations_ParentId", - table: "Navigations", - column: "ParentId", - principalTable: "Navigations", - principalColumn: "Id", - onDelete: ReferentialAction.SetNull); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropForeignKey( - name: "FK_Navigations_Navigations_ParentId", - table: "Navigations"); - - migrationBuilder.DropIndex( - name: "IX_Navigations_ParentId", - table: "Navigations"); - - migrationBuilder.AddColumn( - name: "NavigationId", - table: "Navigations", - type: "integer", - nullable: true); - - migrationBuilder.CreateIndex( - name: "IX_Navigations_NavigationId", - table: "Navigations", - column: "NavigationId"); - - migrationBuilder.AddForeignKey( - name: "FK_Navigations_Navigations_NavigationId", - table: "Navigations", - column: "NavigationId", - principalTable: "Navigations", - principalColumn: "Id"); - } - } -} diff --git a/Infrastructure/Data/Migrations/20240130191200_EnabledTrigramExtensionAndCreatedTrigramGistIndexForArticleTitle.Designer.cs b/Infrastructure/Data/Migrations/20240130191200_EnabledTrigramExtensionAndCreatedTrigramGistIndexForArticleTitle.Designer.cs deleted file mode 100644 index e6db11f..0000000 --- a/Infrastructure/Data/Migrations/20240130191200_EnabledTrigramExtensionAndCreatedTrigramGistIndexForArticleTitle.Designer.cs +++ /dev/null @@ -1,327 +0,0 @@ -// -using System; -using Infrastructure.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace Infrastructure.Data.Migrations -{ - [DbContext(typeof(ApplicationDbContext))] - [Migration("20240130191200_EnabledTrigramExtensionAndCreatedTrigramGistIndexForArticleTitle")] - partial class EnabledTrigramExtensionAndCreatedTrigramGistIndexForArticleTitle - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "8.0.1") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "pg_trgm"); - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("CategoryRevision", b => - { - b.Property("CategoriesId") - .HasColumnType("character varying(512)"); - - b.Property("RevisionsId") - .HasColumnType("uuid"); - - b.HasKey("CategoriesId", "RevisionsId"); - - b.HasIndex("RevisionsId"); - - b.ToTable("CategoryRevision"); - }); - - modelBuilder.Entity("Domain.Entities.Article", b => - { - b.Property("Id") - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("CurrentRevisionId") - .HasColumnType("uuid"); - - b.Property("RedirectArticleId") - .HasColumnType("character varying(512)"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("character varying(128)"); - - b.HasKey("Id"); - - b.HasIndex("CurrentRevisionId") - .IsUnique(); - - b.HasIndex("RedirectArticleId"); - - b.HasIndex("Title"); - - NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("Title"), "GIST"); - NpgsqlIndexBuilderExtensions.HasOperators(b.HasIndex("Title"), new[] { "gist_trgm_ops" }); - - b.ToTable("Articles"); - }); - - modelBuilder.Entity("Domain.Entities.Author", b => - { - b.Property("Id") - .HasColumnType("text"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("Authors"); - }); - - modelBuilder.Entity("Domain.Entities.Category", b => - { - b.Property("Id") - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("character varying(128)"); - - b.Property("ParentId") - .HasColumnType("character varying(512)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.ToTable("Categories"); - }); - - modelBuilder.Entity("Domain.Entities.Navigation", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Icon") - .HasColumnType("text"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.Property("ParentId") - .HasColumnType("integer"); - - b.Property("Uri") - .HasColumnType("text"); - - b.Property("Weight") - .HasColumnType("integer"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.ToTable("Navigations"); - }); - - modelBuilder.Entity("Domain.Entities.Review", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("Message") - .IsRequired() - .HasColumnType("text"); - - b.Property("ReviewTimestamp") - .HasColumnType("timestamp with time zone"); - - b.Property("ReviewerId") - .IsRequired() - .HasColumnType("text"); - - b.Property("RevisionId") - .HasColumnType("uuid"); - - b.Property("Status") - .HasColumnType("integer"); - - b.HasKey("Id"); - - b.HasIndex("ReviewerId"); - - b.HasIndex("RevisionId"); - - b.ToTable("Reviews"); - }); - - modelBuilder.Entity("Domain.Entities.Revision", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("ArticleId") - .IsRequired() - .HasColumnType("character varying(512)"); - - b.Property("AuthorId") - .IsRequired() - .HasColumnType("text"); - - b.Property("Content") - .IsRequired() - .HasColumnType("text"); - - b.Property("LatestReviewId") - .HasColumnType("uuid"); - - b.Property("Timestamp") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.HasIndex("ArticleId"); - - b.HasIndex("AuthorId"); - - b.HasIndex("LatestReviewId") - .IsUnique(); - - b.ToTable("Revisions"); - }); - - modelBuilder.Entity("CategoryRevision", b => - { - b.HasOne("Domain.Entities.Category", null) - .WithMany() - .HasForeignKey("CategoriesId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Revision", null) - .WithMany() - .HasForeignKey("RevisionsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Domain.Entities.Article", b => - { - b.HasOne("Domain.Entities.Revision", "CurrentRevision") - .WithOne() - .HasForeignKey("Domain.Entities.Article", "CurrentRevisionId"); - - b.HasOne("Domain.Entities.Article", "RedirectArticle") - .WithMany() - .HasForeignKey("RedirectArticleId") - .OnDelete(DeleteBehavior.Cascade); - - b.Navigation("CurrentRevision"); - - b.Navigation("RedirectArticle"); - }); - - modelBuilder.Entity("Domain.Entities.Category", b => - { - b.HasOne("Domain.Entities.Category", "Parent") - .WithMany("SubCategories") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.SetNull); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Domain.Entities.Navigation", b => - { - b.HasOne("Domain.Entities.Navigation", "Parent") - .WithMany("Children") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.SetNull); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Domain.Entities.Review", b => - { - b.HasOne("Domain.Entities.Author", "Reviewer") - .WithMany() - .HasForeignKey("ReviewerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Revision", "Revision") - .WithMany("Reviews") - .HasForeignKey("RevisionId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Reviewer"); - - b.Navigation("Revision"); - }); - - modelBuilder.Entity("Domain.Entities.Revision", b => - { - b.HasOne("Domain.Entities.Article", "Article") - .WithMany("Revisions") - .HasForeignKey("ArticleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Author", "Author") - .WithMany() - .HasForeignKey("AuthorId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Review", "LatestReview") - .WithOne() - .HasForeignKey("Domain.Entities.Revision", "LatestReviewId"); - - b.Navigation("Article"); - - b.Navigation("Author"); - - b.Navigation("LatestReview"); - }); - - modelBuilder.Entity("Domain.Entities.Article", b => - { - b.Navigation("Revisions"); - }); - - modelBuilder.Entity("Domain.Entities.Category", b => - { - b.Navigation("SubCategories"); - }); - - modelBuilder.Entity("Domain.Entities.Navigation", b => - { - b.Navigation("Children"); - }); - - modelBuilder.Entity("Domain.Entities.Revision", b => - { - b.Navigation("Reviews"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Infrastructure/Data/Migrations/20240130191200_EnabledTrigramExtensionAndCreatedTrigramGistIndexForArticleTitle.cs b/Infrastructure/Data/Migrations/20240130191200_EnabledTrigramExtensionAndCreatedTrigramGistIndexForArticleTitle.cs deleted file mode 100644 index f12f2ad..0000000 --- a/Infrastructure/Data/Migrations/20240130191200_EnabledTrigramExtensionAndCreatedTrigramGistIndexForArticleTitle.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Infrastructure.Data.Migrations -{ - /// - public partial class EnabledTrigramExtensionAndCreatedTrigramGistIndexForArticleTitle : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterDatabase() - .Annotation("Npgsql:PostgresExtension:pg_trgm", ",,"); - - migrationBuilder.CreateIndex( - name: "IX_Articles_Title", - table: "Articles", - column: "Title") - .Annotation("Npgsql:IndexMethod", "GIST") - .Annotation("Npgsql:IndexOperators", new[] { "gist_trgm_ops" }); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropIndex( - name: "IX_Articles_Title", - table: "Articles"); - - migrationBuilder.AlterDatabase() - .OldAnnotation("Npgsql:PostgresExtension:pg_trgm", ",,"); - } - } -} diff --git a/Infrastructure/Data/Migrations/20240318004044_AddedLengthColumnLimitationsForNavigationNameIconUri.Designer.cs b/Infrastructure/Data/Migrations/20240318004044_AddedLengthColumnLimitationsForNavigationNameIconUri.Designer.cs deleted file mode 100644 index 2f0599d..0000000 --- a/Infrastructure/Data/Migrations/20240318004044_AddedLengthColumnLimitationsForNavigationNameIconUri.Designer.cs +++ /dev/null @@ -1,330 +0,0 @@ -// -using System; -using Infrastructure.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace Infrastructure.Data.Migrations -{ - [DbContext(typeof(ApplicationDbContext))] - [Migration("20240318004044_AddedLengthColumnLimitationsForNavigationNameIconUri")] - partial class AddedLengthColumnLimitationsForNavigationNameIconUri - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "8.0.1") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "pg_trgm"); - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("CategoryRevision", b => - { - b.Property("CategoriesId") - .HasColumnType("character varying(512)"); - - b.Property("RevisionsId") - .HasColumnType("uuid"); - - b.HasKey("CategoriesId", "RevisionsId"); - - b.HasIndex("RevisionsId"); - - b.ToTable("CategoryRevision"); - }); - - modelBuilder.Entity("Domain.Entities.Article", b => - { - b.Property("Id") - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("CurrentRevisionId") - .HasColumnType("uuid"); - - b.Property("RedirectArticleId") - .HasColumnType("character varying(512)"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("character varying(128)"); - - b.HasKey("Id"); - - b.HasIndex("CurrentRevisionId") - .IsUnique(); - - b.HasIndex("RedirectArticleId"); - - b.HasIndex("Title"); - - NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("Title"), "GIST"); - NpgsqlIndexBuilderExtensions.HasOperators(b.HasIndex("Title"), new[] { "gist_trgm_ops" }); - - b.ToTable("Articles"); - }); - - modelBuilder.Entity("Domain.Entities.Author", b => - { - b.Property("Id") - .HasColumnType("text"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("Authors"); - }); - - modelBuilder.Entity("Domain.Entities.Category", b => - { - b.Property("Id") - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("character varying(128)"); - - b.Property("ParentId") - .HasColumnType("character varying(512)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.ToTable("Categories"); - }); - - modelBuilder.Entity("Domain.Entities.Navigation", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Icon") - .HasMaxLength(128) - .HasColumnType("character varying(128)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("character varying(128)"); - - b.Property("ParentId") - .HasColumnType("integer"); - - b.Property("Uri") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)"); - - b.Property("Weight") - .HasColumnType("integer"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.ToTable("Navigations"); - }); - - modelBuilder.Entity("Domain.Entities.Review", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("Message") - .IsRequired() - .HasColumnType("text"); - - b.Property("ReviewTimestamp") - .HasColumnType("timestamp with time zone"); - - b.Property("ReviewerId") - .IsRequired() - .HasColumnType("text"); - - b.Property("RevisionId") - .HasColumnType("uuid"); - - b.Property("Status") - .HasColumnType("integer"); - - b.HasKey("Id"); - - b.HasIndex("ReviewerId"); - - b.HasIndex("RevisionId"); - - b.ToTable("Reviews"); - }); - - modelBuilder.Entity("Domain.Entities.Revision", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("ArticleId") - .IsRequired() - .HasColumnType("character varying(512)"); - - b.Property("AuthorId") - .IsRequired() - .HasColumnType("text"); - - b.Property("Content") - .IsRequired() - .HasColumnType("text"); - - b.Property("LatestReviewId") - .HasColumnType("uuid"); - - b.Property("Timestamp") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.HasIndex("ArticleId"); - - b.HasIndex("AuthorId"); - - b.HasIndex("LatestReviewId") - .IsUnique(); - - b.ToTable("Revisions"); - }); - - modelBuilder.Entity("CategoryRevision", b => - { - b.HasOne("Domain.Entities.Category", null) - .WithMany() - .HasForeignKey("CategoriesId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Revision", null) - .WithMany() - .HasForeignKey("RevisionsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Domain.Entities.Article", b => - { - b.HasOne("Domain.Entities.Revision", "CurrentRevision") - .WithOne() - .HasForeignKey("Domain.Entities.Article", "CurrentRevisionId"); - - b.HasOne("Domain.Entities.Article", "RedirectArticle") - .WithMany() - .HasForeignKey("RedirectArticleId") - .OnDelete(DeleteBehavior.Cascade); - - b.Navigation("CurrentRevision"); - - b.Navigation("RedirectArticle"); - }); - - modelBuilder.Entity("Domain.Entities.Category", b => - { - b.HasOne("Domain.Entities.Category", "Parent") - .WithMany("SubCategories") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.SetNull); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Domain.Entities.Navigation", b => - { - b.HasOne("Domain.Entities.Navigation", "Parent") - .WithMany("Children") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.SetNull); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Domain.Entities.Review", b => - { - b.HasOne("Domain.Entities.Author", "Reviewer") - .WithMany() - .HasForeignKey("ReviewerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Revision", "Revision") - .WithMany("Reviews") - .HasForeignKey("RevisionId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Reviewer"); - - b.Navigation("Revision"); - }); - - modelBuilder.Entity("Domain.Entities.Revision", b => - { - b.HasOne("Domain.Entities.Article", "Article") - .WithMany("Revisions") - .HasForeignKey("ArticleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Author", "Author") - .WithMany() - .HasForeignKey("AuthorId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Review", "LatestReview") - .WithOne() - .HasForeignKey("Domain.Entities.Revision", "LatestReviewId"); - - b.Navigation("Article"); - - b.Navigation("Author"); - - b.Navigation("LatestReview"); - }); - - modelBuilder.Entity("Domain.Entities.Article", b => - { - b.Navigation("Revisions"); - }); - - modelBuilder.Entity("Domain.Entities.Category", b => - { - b.Navigation("SubCategories"); - }); - - modelBuilder.Entity("Domain.Entities.Navigation", b => - { - b.Navigation("Children"); - }); - - modelBuilder.Entity("Domain.Entities.Revision", b => - { - b.Navigation("Reviews"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Infrastructure/Data/Migrations/20240318004044_AddedLengthColumnLimitationsForNavigationNameIconUri.cs b/Infrastructure/Data/Migrations/20240318004044_AddedLengthColumnLimitationsForNavigationNameIconUri.cs deleted file mode 100644 index cb22747..0000000 --- a/Infrastructure/Data/Migrations/20240318004044_AddedLengthColumnLimitationsForNavigationNameIconUri.cs +++ /dev/null @@ -1,76 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Infrastructure.Data.Migrations -{ - /// - public partial class AddedLengthColumnLimitationsForNavigationNameIconUri : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterColumn( - name: "Uri", - table: "Navigations", - type: "character varying(2048)", - maxLength: 2048, - nullable: true, - oldClrType: typeof(string), - oldType: "text", - oldNullable: true); - - migrationBuilder.AlterColumn( - name: "Name", - table: "Navigations", - type: "character varying(128)", - maxLength: 128, - nullable: false, - oldClrType: typeof(string), - oldType: "text"); - - migrationBuilder.AlterColumn( - name: "Icon", - table: "Navigations", - type: "character varying(128)", - maxLength: 128, - nullable: true, - oldClrType: typeof(string), - oldType: "text", - oldNullable: true); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterColumn( - name: "Uri", - table: "Navigations", - type: "text", - nullable: true, - oldClrType: typeof(string), - oldType: "character varying(2048)", - oldMaxLength: 2048, - oldNullable: true); - - migrationBuilder.AlterColumn( - name: "Name", - table: "Navigations", - type: "text", - nullable: false, - oldClrType: typeof(string), - oldType: "character varying(128)", - oldMaxLength: 128); - - migrationBuilder.AlterColumn( - name: "Icon", - table: "Navigations", - type: "text", - nullable: true, - oldClrType: typeof(string), - oldType: "character varying(128)", - oldMaxLength: 128, - oldNullable: true); - } - } -} diff --git a/Infrastructure/Data/Migrations/20240606134010_AddedAuthorsNoteToRevision.Designer.cs b/Infrastructure/Data/Migrations/20240606134010_AddedAuthorsNoteToRevision.Designer.cs deleted file mode 100644 index ae5c4e2..0000000 --- a/Infrastructure/Data/Migrations/20240606134010_AddedAuthorsNoteToRevision.Designer.cs +++ /dev/null @@ -1,334 +0,0 @@ -// -using System; -using Infrastructure.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace Infrastructure.Data.Migrations -{ - [DbContext(typeof(ApplicationDbContext))] - [Migration("20240606134010_AddedAuthorsNoteToRevision")] - partial class AddedAuthorsNoteToRevision - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "8.0.1") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "pg_trgm"); - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("CategoryRevision", b => - { - b.Property("CategoriesId") - .HasColumnType("character varying(512)"); - - b.Property("RevisionsId") - .HasColumnType("uuid"); - - b.HasKey("CategoriesId", "RevisionsId"); - - b.HasIndex("RevisionsId"); - - b.ToTable("CategoryRevision"); - }); - - modelBuilder.Entity("Domain.Entities.Article", b => - { - b.Property("Id") - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("CurrentRevisionId") - .HasColumnType("uuid"); - - b.Property("RedirectArticleId") - .HasColumnType("character varying(512)"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("character varying(128)"); - - b.HasKey("Id"); - - b.HasIndex("CurrentRevisionId") - .IsUnique(); - - b.HasIndex("RedirectArticleId"); - - b.HasIndex("Title"); - - NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("Title"), "GIST"); - NpgsqlIndexBuilderExtensions.HasOperators(b.HasIndex("Title"), new[] { "gist_trgm_ops" }); - - b.ToTable("Articles"); - }); - - modelBuilder.Entity("Domain.Entities.Author", b => - { - b.Property("Id") - .HasColumnType("text"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("Authors"); - }); - - modelBuilder.Entity("Domain.Entities.Category", b => - { - b.Property("Id") - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("character varying(128)"); - - b.Property("ParentId") - .HasColumnType("character varying(512)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.ToTable("Categories"); - }); - - modelBuilder.Entity("Domain.Entities.Navigation", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Icon") - .HasMaxLength(128) - .HasColumnType("character varying(128)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("character varying(128)"); - - b.Property("ParentId") - .HasColumnType("integer"); - - b.Property("Uri") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)"); - - b.Property("Weight") - .HasColumnType("integer"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.ToTable("Navigations"); - }); - - modelBuilder.Entity("Domain.Entities.Review", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("Message") - .IsRequired() - .HasColumnType("text"); - - b.Property("ReviewTimestamp") - .HasColumnType("timestamp with time zone"); - - b.Property("ReviewerId") - .IsRequired() - .HasColumnType("text"); - - b.Property("RevisionId") - .HasColumnType("uuid"); - - b.Property("Status") - .HasColumnType("integer"); - - b.HasKey("Id"); - - b.HasIndex("ReviewerId"); - - b.HasIndex("RevisionId"); - - b.ToTable("Reviews"); - }); - - modelBuilder.Entity("Domain.Entities.Revision", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("ArticleId") - .IsRequired() - .HasColumnType("character varying(512)"); - - b.Property("AuthorId") - .IsRequired() - .HasColumnType("text"); - - b.Property("AuthorsNote") - .IsRequired() - .HasColumnType("text"); - - b.Property("Content") - .IsRequired() - .HasColumnType("text"); - - b.Property("LatestReviewId") - .HasColumnType("uuid"); - - b.Property("Timestamp") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.HasIndex("ArticleId"); - - b.HasIndex("AuthorId"); - - b.HasIndex("LatestReviewId") - .IsUnique(); - - b.ToTable("Revisions"); - }); - - modelBuilder.Entity("CategoryRevision", b => - { - b.HasOne("Domain.Entities.Category", null) - .WithMany() - .HasForeignKey("CategoriesId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Revision", null) - .WithMany() - .HasForeignKey("RevisionsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Domain.Entities.Article", b => - { - b.HasOne("Domain.Entities.Revision", "CurrentRevision") - .WithOne() - .HasForeignKey("Domain.Entities.Article", "CurrentRevisionId"); - - b.HasOne("Domain.Entities.Article", "RedirectArticle") - .WithMany() - .HasForeignKey("RedirectArticleId") - .OnDelete(DeleteBehavior.Cascade); - - b.Navigation("CurrentRevision"); - - b.Navigation("RedirectArticle"); - }); - - modelBuilder.Entity("Domain.Entities.Category", b => - { - b.HasOne("Domain.Entities.Category", "Parent") - .WithMany("SubCategories") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.SetNull); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Domain.Entities.Navigation", b => - { - b.HasOne("Domain.Entities.Navigation", "Parent") - .WithMany("Children") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.SetNull); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Domain.Entities.Review", b => - { - b.HasOne("Domain.Entities.Author", "Reviewer") - .WithMany() - .HasForeignKey("ReviewerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Revision", "Revision") - .WithMany("Reviews") - .HasForeignKey("RevisionId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Reviewer"); - - b.Navigation("Revision"); - }); - - modelBuilder.Entity("Domain.Entities.Revision", b => - { - b.HasOne("Domain.Entities.Article", "Article") - .WithMany("Revisions") - .HasForeignKey("ArticleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Author", "Author") - .WithMany() - .HasForeignKey("AuthorId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.Review", "LatestReview") - .WithOne() - .HasForeignKey("Domain.Entities.Revision", "LatestReviewId"); - - b.Navigation("Article"); - - b.Navigation("Author"); - - b.Navigation("LatestReview"); - }); - - modelBuilder.Entity("Domain.Entities.Article", b => - { - b.Navigation("Revisions"); - }); - - modelBuilder.Entity("Domain.Entities.Category", b => - { - b.Navigation("SubCategories"); - }); - - modelBuilder.Entity("Domain.Entities.Navigation", b => - { - b.Navigation("Children"); - }); - - modelBuilder.Entity("Domain.Entities.Revision", b => - { - b.Navigation("Reviews"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Infrastructure/Data/Migrations/20240606134010_AddedAuthorsNoteToRevision.cs b/Infrastructure/Data/Migrations/20240606134010_AddedAuthorsNoteToRevision.cs deleted file mode 100644 index 11ceb2b..0000000 --- a/Infrastructure/Data/Migrations/20240606134010_AddedAuthorsNoteToRevision.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Infrastructure.Data.Migrations -{ - /// - public partial class AddedAuthorsNoteToRevision : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn( - name: "AuthorsNote", - table: "Revisions", - type: "text", - nullable: false, - defaultValue: ""); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "AuthorsNote", - table: "Revisions"); - } - } -} diff --git a/Infrastructure/Data/Migrations/20250808004657_Initial.Designer.cs b/Infrastructure/Data/Migrations/20250808004657_Initial.Designer.cs new file mode 100644 index 0000000..1de5ed3 --- /dev/null +++ b/Infrastructure/Data/Migrations/20250808004657_Initial.Designer.cs @@ -0,0 +1,602 @@ +// +using System; +using Infrastructure.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.Data.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20250808004657_Initial")] + partial class Initial + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.1") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "pg_trgm"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("CategoryRevision", b => + { + b.Property("CategoriesId") + .HasColumnType("character varying(512)"); + + b.Property("RevisionsId") + .HasColumnType("uuid"); + + b.HasKey("CategoriesId", "RevisionsId"); + + b.HasIndex("RevisionsId"); + + b.ToTable("CategoryRevision"); + }); + + modelBuilder.Entity("Domain.Entities.Article", b => + { + b.Property("Id") + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.Property("CurrentRevisionId") + .HasColumnType("uuid"); + + b.Property("RedirectArticleId") + .HasColumnType("character varying(512)"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.HasKey("Id"); + + b.HasIndex("CurrentRevisionId") + .IsUnique(); + + b.HasIndex("RedirectArticleId"); + + b.HasIndex("Title"); + + NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("Title"), "GIST"); + NpgsqlIndexBuilderExtensions.HasOperators(b.HasIndex("Title"), new[] { "gist_trgm_ops" }); + + b.ToTable("Articles"); + }); + + modelBuilder.Entity("Domain.Entities.Category", b => + { + b.Property("Id") + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ParentId") + .HasColumnType("character varying(512)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.ToTable("Categories"); + }); + + modelBuilder.Entity("Domain.Entities.Navigation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Icon") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ParentId") + .HasColumnType("integer"); + + b.Property("Uri") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Weight") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.ToTable("Navigations"); + }); + + modelBuilder.Entity("Domain.Entities.Permission", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Permissions"); + + b.HasData( + new + { + Id = new Guid("5744b9a3-2256-4f69-b6ac-4d8c164632c9"), + Name = "ArticleCreate" + }, + new + { + Id = new Guid("861b96a8-9a42-45e4-b1c2-733ef85bc2d6"), + Name = "ArticleEdit" + }, + new + { + Id = new Guid("7d9bbc60-f1c8-4762-ac1d-edac85f22b48"), + Name = "ArticleSetRedirect" + }, + new + { + Id = new Guid("27c68a58-6f91-41a2-bd36-c4dda391f309"), + Name = "ArticleSeePendingRevisions" + }, + new + { + Id = new Guid("76da2206-a875-47b2-86bb-27ecd0e9f4b9"), + Name = "ArticleReviewRevision" + }, + new + { + Id = new Guid("e4d1aff3-8bc1-4730-b95d-438f1fde6aeb"), + Name = "ArticleDelete" + }, + new + { + Id = new Guid("95571fee-1313-41de-adfe-6b65fe53760b"), + Name = "CategoryCreate" + }, + new + { + Id = new Guid("fa7c5b48-0fb4-4757-96a7-014b00fdd78b"), + Name = "CategoryDelete" + }, + new + { + Id = new Guid("eced433f-b27c-4c6e-af41-d2231fb40f03"), + Name = "NavigationsUpdateTree" + }); + }); + + modelBuilder.Entity("Domain.Entities.Review", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b.Property("ReviewTimestamp") + .HasColumnType("timestamp with time zone"); + + b.Property("ReviewerId") + .HasColumnType("uuid"); + + b.Property("RevisionId") + .HasColumnType("uuid"); + + b.Property("Status") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("ReviewerId"); + + b.HasIndex("RevisionId"); + + b.ToTable("Reviews"); + }); + + modelBuilder.Entity("Domain.Entities.Revision", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ArticleId") + .IsRequired() + .HasColumnType("character varying(512)"); + + b.Property("AuthorId") + .HasColumnType("uuid"); + + b.Property("AuthorsNote") + .IsRequired() + .HasColumnType("text"); + + b.Property("Content") + .IsRequired() + .HasColumnType("text"); + + b.Property("LatestReviewId") + .HasColumnType("uuid"); + + b.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("ArticleId"); + + b.HasIndex("AuthorId"); + + b.HasIndex("LatestReviewId") + .IsUnique(); + + b.ToTable("Revisions"); + }); + + modelBuilder.Entity("Domain.Entities.Role", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Roles"); + + b.HasData( + new + { + Id = new Guid("ca2cfe04-24ed-42d0-9237-6d5ed7885063"), + Name = "Admin" + }, + new + { + Id = new Guid("e1d0ad14-fb96-4488-bf64-8aab2d1ef43d"), + Name = "Editor" + }, + new + { + Id = new Guid("3a7651c5-2cf1-4ed6-9866-c6bac6e8f6dd"), + Name = "User" + }, + new + { + Id = new Guid("2afacdee-55a4-4e23-9f66-2ca5a5af9751"), + Name = "Lurker" + }); + }); + + modelBuilder.Entity("Domain.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ExternalId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ExternalId"); + + b.HasIndex("Name"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("PermissionRole", b => + { + b.Property("PermissionsId") + .HasColumnType("uuid"); + + b.Property("RolesId") + .HasColumnType("uuid"); + + b.HasKey("PermissionsId", "RolesId"); + + b.HasIndex("RolesId"); + + b.ToTable("RolePermission", (string)null); + + b.HasData( + new + { + PermissionsId = new Guid("5744b9a3-2256-4f69-b6ac-4d8c164632c9"), + RolesId = new Guid("ca2cfe04-24ed-42d0-9237-6d5ed7885063") + }, + new + { + PermissionsId = new Guid("e4d1aff3-8bc1-4730-b95d-438f1fde6aeb"), + RolesId = new Guid("ca2cfe04-24ed-42d0-9237-6d5ed7885063") + }, + new + { + PermissionsId = new Guid("861b96a8-9a42-45e4-b1c2-733ef85bc2d6"), + RolesId = new Guid("ca2cfe04-24ed-42d0-9237-6d5ed7885063") + }, + new + { + PermissionsId = new Guid("27c68a58-6f91-41a2-bd36-c4dda391f309"), + RolesId = new Guid("ca2cfe04-24ed-42d0-9237-6d5ed7885063") + }, + new + { + PermissionsId = new Guid("76da2206-a875-47b2-86bb-27ecd0e9f4b9"), + RolesId = new Guid("ca2cfe04-24ed-42d0-9237-6d5ed7885063") + }, + new + { + PermissionsId = new Guid("7d9bbc60-f1c8-4762-ac1d-edac85f22b48"), + RolesId = new Guid("ca2cfe04-24ed-42d0-9237-6d5ed7885063") + }, + new + { + PermissionsId = new Guid("95571fee-1313-41de-adfe-6b65fe53760b"), + RolesId = new Guid("ca2cfe04-24ed-42d0-9237-6d5ed7885063") + }, + new + { + PermissionsId = new Guid("fa7c5b48-0fb4-4757-96a7-014b00fdd78b"), + RolesId = new Guid("ca2cfe04-24ed-42d0-9237-6d5ed7885063") + }, + new + { + PermissionsId = new Guid("eced433f-b27c-4c6e-af41-d2231fb40f03"), + RolesId = new Guid("ca2cfe04-24ed-42d0-9237-6d5ed7885063") + }, + new + { + PermissionsId = new Guid("5744b9a3-2256-4f69-b6ac-4d8c164632c9"), + RolesId = new Guid("e1d0ad14-fb96-4488-bf64-8aab2d1ef43d") + }, + new + { + PermissionsId = new Guid("e4d1aff3-8bc1-4730-b95d-438f1fde6aeb"), + RolesId = new Guid("e1d0ad14-fb96-4488-bf64-8aab2d1ef43d") + }, + new + { + PermissionsId = new Guid("861b96a8-9a42-45e4-b1c2-733ef85bc2d6"), + RolesId = new Guid("e1d0ad14-fb96-4488-bf64-8aab2d1ef43d") + }, + new + { + PermissionsId = new Guid("27c68a58-6f91-41a2-bd36-c4dda391f309"), + RolesId = new Guid("e1d0ad14-fb96-4488-bf64-8aab2d1ef43d") + }, + new + { + PermissionsId = new Guid("76da2206-a875-47b2-86bb-27ecd0e9f4b9"), + RolesId = new Guid("e1d0ad14-fb96-4488-bf64-8aab2d1ef43d") + }, + new + { + PermissionsId = new Guid("7d9bbc60-f1c8-4762-ac1d-edac85f22b48"), + RolesId = new Guid("e1d0ad14-fb96-4488-bf64-8aab2d1ef43d") + }, + new + { + PermissionsId = new Guid("95571fee-1313-41de-adfe-6b65fe53760b"), + RolesId = new Guid("e1d0ad14-fb96-4488-bf64-8aab2d1ef43d") + }, + new + { + PermissionsId = new Guid("fa7c5b48-0fb4-4757-96a7-014b00fdd78b"), + RolesId = new Guid("e1d0ad14-fb96-4488-bf64-8aab2d1ef43d") + }, + new + { + PermissionsId = new Guid("eced433f-b27c-4c6e-af41-d2231fb40f03"), + RolesId = new Guid("e1d0ad14-fb96-4488-bf64-8aab2d1ef43d") + }, + new + { + PermissionsId = new Guid("5744b9a3-2256-4f69-b6ac-4d8c164632c9"), + RolesId = new Guid("3a7651c5-2cf1-4ed6-9866-c6bac6e8f6dd") + }, + new + { + PermissionsId = new Guid("861b96a8-9a42-45e4-b1c2-733ef85bc2d6"), + RolesId = new Guid("3a7651c5-2cf1-4ed6-9866-c6bac6e8f6dd") + }); + }); + + modelBuilder.Entity("RoleUser", b => + { + b.Property("RolesId") + .HasColumnType("uuid"); + + b.Property("UsersId") + .HasColumnType("uuid"); + + b.HasKey("RolesId", "UsersId"); + + b.HasIndex("UsersId"); + + b.ToTable("RoleUser"); + }); + + modelBuilder.Entity("CategoryRevision", b => + { + b.HasOne("Domain.Entities.Category", null) + .WithMany() + .HasForeignKey("CategoriesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Revision", null) + .WithMany() + .HasForeignKey("RevisionsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Domain.Entities.Article", b => + { + b.HasOne("Domain.Entities.Revision", "CurrentRevision") + .WithOne() + .HasForeignKey("Domain.Entities.Article", "CurrentRevisionId"); + + b.HasOne("Domain.Entities.Article", "RedirectArticle") + .WithMany() + .HasForeignKey("RedirectArticleId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("CurrentRevision"); + + b.Navigation("RedirectArticle"); + }); + + modelBuilder.Entity("Domain.Entities.Category", b => + { + b.HasOne("Domain.Entities.Category", "Parent") + .WithMany("SubCategories") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Domain.Entities.Navigation", b => + { + b.HasOne("Domain.Entities.Navigation", "Parent") + .WithMany("Children") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Domain.Entities.Review", b => + { + b.HasOne("Domain.Entities.User", "Reviewer") + .WithMany() + .HasForeignKey("ReviewerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Revision", "Revision") + .WithMany("Reviews") + .HasForeignKey("RevisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Reviewer"); + + b.Navigation("Revision"); + }); + + modelBuilder.Entity("Domain.Entities.Revision", b => + { + b.HasOne("Domain.Entities.Article", "Article") + .WithMany("Revisions") + .HasForeignKey("ArticleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.User", "Author") + .WithMany() + .HasForeignKey("AuthorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Review", "LatestReview") + .WithOne() + .HasForeignKey("Domain.Entities.Revision", "LatestReviewId"); + + b.Navigation("Article"); + + b.Navigation("Author"); + + b.Navigation("LatestReview"); + }); + + modelBuilder.Entity("PermissionRole", b => + { + b.HasOne("Domain.Entities.Permission", null) + .WithMany() + .HasForeignKey("PermissionsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Role", null) + .WithMany() + .HasForeignKey("RolesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("RoleUser", b => + { + b.HasOne("Domain.Entities.Role", null) + .WithMany() + .HasForeignKey("RolesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.User", null) + .WithMany() + .HasForeignKey("UsersId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Domain.Entities.Article", b => + { + b.Navigation("Revisions"); + }); + + modelBuilder.Entity("Domain.Entities.Category", b => + { + b.Navigation("SubCategories"); + }); + + modelBuilder.Entity("Domain.Entities.Navigation", b => + { + b.Navigation("Children"); + }); + + modelBuilder.Entity("Domain.Entities.Revision", b => + { + b.Navigation("Reviews"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Infrastructure/Data/Migrations/20250808004657_Initial.cs b/Infrastructure/Data/Migrations/20250808004657_Initial.cs new file mode 100644 index 0000000..61f722c --- /dev/null +++ b/Infrastructure/Data/Migrations/20250808004657_Initial.cs @@ -0,0 +1,443 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional + +namespace Infrastructure.Data.Migrations +{ + /// + public partial class Initial : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("Npgsql:PostgresExtension:pg_trgm", ",,"); + + migrationBuilder.CreateTable( + name: "Categories", + columns: table => new + { + Id = table.Column(type: "character varying(512)", maxLength: 512, nullable: false), + Name = table.Column(type: "character varying(128)", maxLength: 128, nullable: false), + ParentId = table.Column(type: "character varying(512)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Categories", x => x.Id); + table.ForeignKey( + name: "FK_Categories_Categories_ParentId", + column: x => x.ParentId, + principalTable: "Categories", + principalColumn: "Id", + onDelete: ReferentialAction.SetNull); + }); + + migrationBuilder.CreateTable( + name: "Navigations", + columns: table => new + { + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + Name = table.Column(type: "character varying(128)", maxLength: 128, nullable: false), + Weight = table.Column(type: "integer", nullable: false), + Uri = table.Column(type: "character varying(2048)", maxLength: 2048, nullable: true), + ParentId = table.Column(type: "integer", nullable: true), + Icon = table.Column(type: "character varying(128)", maxLength: 128, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Navigations", x => x.Id); + table.ForeignKey( + name: "FK_Navigations_Navigations_ParentId", + column: x => x.ParentId, + principalTable: "Navigations", + principalColumn: "Id", + onDelete: ReferentialAction.SetNull); + }); + + migrationBuilder.CreateTable( + name: "Permissions", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Name = table.Column(type: "text", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Permissions", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Roles", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Name = table.Column(type: "text", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Roles", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Users", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + ExternalId = table.Column(type: "text", nullable: false), + Name = table.Column(type: "text", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Users", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "RolePermission", + columns: table => new + { + PermissionsId = table.Column(type: "uuid", nullable: false), + RolesId = table.Column(type: "uuid", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_RolePermission", x => new { x.PermissionsId, x.RolesId }); + table.ForeignKey( + name: "FK_RolePermission_Permissions_PermissionsId", + column: x => x.PermissionsId, + principalTable: "Permissions", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_RolePermission_Roles_RolesId", + column: x => x.RolesId, + principalTable: "Roles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "RoleUser", + columns: table => new + { + RolesId = table.Column(type: "uuid", nullable: false), + UsersId = table.Column(type: "uuid", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_RoleUser", x => new { x.RolesId, x.UsersId }); + table.ForeignKey( + name: "FK_RoleUser_Roles_RolesId", + column: x => x.RolesId, + principalTable: "Roles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_RoleUser_Users_UsersId", + column: x => x.UsersId, + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "Articles", + columns: table => new + { + Id = table.Column(type: "character varying(512)", maxLength: 512, nullable: false), + Title = table.Column(type: "character varying(128)", maxLength: 128, nullable: false), + RedirectArticleId = table.Column(type: "character varying(512)", nullable: true), + CurrentRevisionId = table.Column(type: "uuid", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Articles", x => x.Id); + table.ForeignKey( + name: "FK_Articles_Articles_RedirectArticleId", + column: x => x.RedirectArticleId, + principalTable: "Articles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "CategoryRevision", + columns: table => new + { + CategoriesId = table.Column(type: "character varying(512)", nullable: false), + RevisionsId = table.Column(type: "uuid", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_CategoryRevision", x => new { x.CategoriesId, x.RevisionsId }); + table.ForeignKey( + name: "FK_CategoryRevision_Categories_CategoriesId", + column: x => x.CategoriesId, + principalTable: "Categories", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "Reviews", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + ReviewerId = table.Column(type: "uuid", nullable: false), + Status = table.Column(type: "integer", nullable: false), + Message = table.Column(type: "text", nullable: false), + ReviewTimestamp = table.Column(type: "timestamp with time zone", nullable: false), + RevisionId = table.Column(type: "uuid", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Reviews", x => x.Id); + table.ForeignKey( + name: "FK_Reviews_Users_ReviewerId", + column: x => x.ReviewerId, + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "Revisions", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + ArticleId = table.Column(type: "character varying(512)", nullable: false), + Content = table.Column(type: "text", nullable: false), + AuthorId = table.Column(type: "uuid", nullable: false), + AuthorsNote = table.Column(type: "text", nullable: false), + Timestamp = table.Column(type: "timestamp with time zone", nullable: false), + LatestReviewId = table.Column(type: "uuid", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Revisions", x => x.Id); + table.ForeignKey( + name: "FK_Revisions_Articles_ArticleId", + column: x => x.ArticleId, + principalTable: "Articles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_Revisions_Reviews_LatestReviewId", + column: x => x.LatestReviewId, + principalTable: "Reviews", + principalColumn: "Id"); + table.ForeignKey( + name: "FK_Revisions_Users_AuthorId", + column: x => x.AuthorId, + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.InsertData( + table: "Permissions", + columns: new[] { "Id", "Name" }, + values: new object[,] + { + { new Guid("27c68a58-6f91-41a2-bd36-c4dda391f309"), "ArticleSeePendingRevisions" }, + { new Guid("5744b9a3-2256-4f69-b6ac-4d8c164632c9"), "ArticleCreate" }, + { new Guid("76da2206-a875-47b2-86bb-27ecd0e9f4b9"), "ArticleReviewRevision" }, + { new Guid("7d9bbc60-f1c8-4762-ac1d-edac85f22b48"), "ArticleSetRedirect" }, + { new Guid("861b96a8-9a42-45e4-b1c2-733ef85bc2d6"), "ArticleEdit" }, + { new Guid("95571fee-1313-41de-adfe-6b65fe53760b"), "CategoryCreate" }, + { new Guid("e4d1aff3-8bc1-4730-b95d-438f1fde6aeb"), "ArticleDelete" }, + { new Guid("eced433f-b27c-4c6e-af41-d2231fb40f03"), "NavigationsUpdateTree" }, + { new Guid("fa7c5b48-0fb4-4757-96a7-014b00fdd78b"), "CategoryDelete" } + }); + + migrationBuilder.InsertData( + table: "Roles", + columns: new[] { "Id", "Name" }, + values: new object[,] + { + { new Guid("2afacdee-55a4-4e23-9f66-2ca5a5af9751"), "Lurker" }, + { new Guid("3a7651c5-2cf1-4ed6-9866-c6bac6e8f6dd"), "User" }, + { new Guid("ca2cfe04-24ed-42d0-9237-6d5ed7885063"), "Admin" }, + { new Guid("e1d0ad14-fb96-4488-bf64-8aab2d1ef43d"), "Editor" } + }); + + migrationBuilder.InsertData( + table: "RolePermission", + columns: new[] { "PermissionsId", "RolesId" }, + values: new object[,] + { + { new Guid("27c68a58-6f91-41a2-bd36-c4dda391f309"), new Guid("ca2cfe04-24ed-42d0-9237-6d5ed7885063") }, + { new Guid("27c68a58-6f91-41a2-bd36-c4dda391f309"), new Guid("e1d0ad14-fb96-4488-bf64-8aab2d1ef43d") }, + { new Guid("5744b9a3-2256-4f69-b6ac-4d8c164632c9"), new Guid("3a7651c5-2cf1-4ed6-9866-c6bac6e8f6dd") }, + { new Guid("5744b9a3-2256-4f69-b6ac-4d8c164632c9"), new Guid("ca2cfe04-24ed-42d0-9237-6d5ed7885063") }, + { new Guid("5744b9a3-2256-4f69-b6ac-4d8c164632c9"), new Guid("e1d0ad14-fb96-4488-bf64-8aab2d1ef43d") }, + { new Guid("76da2206-a875-47b2-86bb-27ecd0e9f4b9"), new Guid("ca2cfe04-24ed-42d0-9237-6d5ed7885063") }, + { new Guid("76da2206-a875-47b2-86bb-27ecd0e9f4b9"), new Guid("e1d0ad14-fb96-4488-bf64-8aab2d1ef43d") }, + { new Guid("7d9bbc60-f1c8-4762-ac1d-edac85f22b48"), new Guid("ca2cfe04-24ed-42d0-9237-6d5ed7885063") }, + { new Guid("7d9bbc60-f1c8-4762-ac1d-edac85f22b48"), new Guid("e1d0ad14-fb96-4488-bf64-8aab2d1ef43d") }, + { new Guid("861b96a8-9a42-45e4-b1c2-733ef85bc2d6"), new Guid("3a7651c5-2cf1-4ed6-9866-c6bac6e8f6dd") }, + { new Guid("861b96a8-9a42-45e4-b1c2-733ef85bc2d6"), new Guid("ca2cfe04-24ed-42d0-9237-6d5ed7885063") }, + { new Guid("861b96a8-9a42-45e4-b1c2-733ef85bc2d6"), new Guid("e1d0ad14-fb96-4488-bf64-8aab2d1ef43d") }, + { new Guid("95571fee-1313-41de-adfe-6b65fe53760b"), new Guid("ca2cfe04-24ed-42d0-9237-6d5ed7885063") }, + { new Guid("95571fee-1313-41de-adfe-6b65fe53760b"), new Guid("e1d0ad14-fb96-4488-bf64-8aab2d1ef43d") }, + { new Guid("e4d1aff3-8bc1-4730-b95d-438f1fde6aeb"), new Guid("ca2cfe04-24ed-42d0-9237-6d5ed7885063") }, + { new Guid("e4d1aff3-8bc1-4730-b95d-438f1fde6aeb"), new Guid("e1d0ad14-fb96-4488-bf64-8aab2d1ef43d") }, + { new Guid("eced433f-b27c-4c6e-af41-d2231fb40f03"), new Guid("ca2cfe04-24ed-42d0-9237-6d5ed7885063") }, + { new Guid("eced433f-b27c-4c6e-af41-d2231fb40f03"), new Guid("e1d0ad14-fb96-4488-bf64-8aab2d1ef43d") }, + { new Guid("fa7c5b48-0fb4-4757-96a7-014b00fdd78b"), new Guid("ca2cfe04-24ed-42d0-9237-6d5ed7885063") }, + { new Guid("fa7c5b48-0fb4-4757-96a7-014b00fdd78b"), new Guid("e1d0ad14-fb96-4488-bf64-8aab2d1ef43d") } + }); + + migrationBuilder.CreateIndex( + name: "IX_Articles_CurrentRevisionId", + table: "Articles", + column: "CurrentRevisionId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Articles_RedirectArticleId", + table: "Articles", + column: "RedirectArticleId"); + + migrationBuilder.CreateIndex( + name: "IX_Articles_Title", + table: "Articles", + column: "Title") + .Annotation("Npgsql:IndexMethod", "GIST") + .Annotation("Npgsql:IndexOperators", new[] { "gist_trgm_ops" }); + + migrationBuilder.CreateIndex( + name: "IX_Categories_ParentId", + table: "Categories", + column: "ParentId"); + + migrationBuilder.CreateIndex( + name: "IX_CategoryRevision_RevisionsId", + table: "CategoryRevision", + column: "RevisionsId"); + + migrationBuilder.CreateIndex( + name: "IX_Navigations_ParentId", + table: "Navigations", + column: "ParentId"); + + migrationBuilder.CreateIndex( + name: "IX_Reviews_ReviewerId", + table: "Reviews", + column: "ReviewerId"); + + migrationBuilder.CreateIndex( + name: "IX_Reviews_RevisionId", + table: "Reviews", + column: "RevisionId"); + + migrationBuilder.CreateIndex( + name: "IX_Revisions_ArticleId", + table: "Revisions", + column: "ArticleId"); + + migrationBuilder.CreateIndex( + name: "IX_Revisions_AuthorId", + table: "Revisions", + column: "AuthorId"); + + migrationBuilder.CreateIndex( + name: "IX_Revisions_LatestReviewId", + table: "Revisions", + column: "LatestReviewId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_RolePermission_RolesId", + table: "RolePermission", + column: "RolesId"); + + migrationBuilder.CreateIndex( + name: "IX_RoleUser_UsersId", + table: "RoleUser", + column: "UsersId"); + + migrationBuilder.CreateIndex( + name: "IX_Users_ExternalId", + table: "Users", + column: "ExternalId"); + + migrationBuilder.CreateIndex( + name: "IX_Users_Name", + table: "Users", + column: "Name"); + + migrationBuilder.AddForeignKey( + name: "FK_Articles_Revisions_CurrentRevisionId", + table: "Articles", + column: "CurrentRevisionId", + principalTable: "Revisions", + principalColumn: "Id"); + + migrationBuilder.AddForeignKey( + name: "FK_CategoryRevision_Revisions_RevisionsId", + table: "CategoryRevision", + column: "RevisionsId", + principalTable: "Revisions", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + name: "FK_Reviews_Revisions_RevisionId", + table: "Reviews", + column: "RevisionId", + principalTable: "Revisions", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Articles_Revisions_CurrentRevisionId", + table: "Articles"); + + migrationBuilder.DropForeignKey( + name: "FK_Reviews_Revisions_RevisionId", + table: "Reviews"); + + migrationBuilder.DropTable( + name: "CategoryRevision"); + + migrationBuilder.DropTable( + name: "Navigations"); + + migrationBuilder.DropTable( + name: "RolePermission"); + + migrationBuilder.DropTable( + name: "RoleUser"); + + migrationBuilder.DropTable( + name: "Categories"); + + migrationBuilder.DropTable( + name: "Permissions"); + + migrationBuilder.DropTable( + name: "Roles"); + + migrationBuilder.DropTable( + name: "Revisions"); + + migrationBuilder.DropTable( + name: "Articles"); + + migrationBuilder.DropTable( + name: "Reviews"); + + migrationBuilder.DropTable( + name: "Users"); + } + } +} diff --git a/Infrastructure/Data/Migrations/ApplicationDbContextModelSnapshot.cs b/Infrastructure/Data/Migrations/ApplicationDbContextModelSnapshot.cs index 0301d60..a84d7dd 100644 --- a/Infrastructure/Data/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/Infrastructure/Data/Migrations/ApplicationDbContextModelSnapshot.cs @@ -70,20 +70,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("Articles"); }); - modelBuilder.Entity("Domain.Entities.Author", b => - { - b.Property("Id") - .HasColumnType("text"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("Authors"); - }); - modelBuilder.Entity("Domain.Entities.Category", b => { b.Property("Id") @@ -139,6 +125,68 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("Navigations"); }); + modelBuilder.Entity("Domain.Entities.Permission", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Permissions"); + + b.HasData( + new + { + Id = new Guid("5744b9a3-2256-4f69-b6ac-4d8c164632c9"), + Name = "ArticleCreate" + }, + new + { + Id = new Guid("861b96a8-9a42-45e4-b1c2-733ef85bc2d6"), + Name = "ArticleEdit" + }, + new + { + Id = new Guid("7d9bbc60-f1c8-4762-ac1d-edac85f22b48"), + Name = "ArticleSetRedirect" + }, + new + { + Id = new Guid("27c68a58-6f91-41a2-bd36-c4dda391f309"), + Name = "ArticleSeePendingRevisions" + }, + new + { + Id = new Guid("76da2206-a875-47b2-86bb-27ecd0e9f4b9"), + Name = "ArticleReviewRevision" + }, + new + { + Id = new Guid("e4d1aff3-8bc1-4730-b95d-438f1fde6aeb"), + Name = "ArticleDelete" + }, + new + { + Id = new Guid("95571fee-1313-41de-adfe-6b65fe53760b"), + Name = "CategoryCreate" + }, + new + { + Id = new Guid("fa7c5b48-0fb4-4757-96a7-014b00fdd78b"), + Name = "CategoryDelete" + }, + new + { + Id = new Guid("eced433f-b27c-4c6e-af41-d2231fb40f03"), + Name = "NavigationsUpdateTree" + }); + }); + modelBuilder.Entity("Domain.Entities.Review", b => { b.Property("Id") @@ -152,9 +200,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("ReviewTimestamp") .HasColumnType("timestamp with time zone"); - b.Property("ReviewerId") - .IsRequired() - .HasColumnType("text"); + b.Property("ReviewerId") + .HasColumnType("uuid"); b.Property("RevisionId") .HasColumnType("uuid"); @@ -181,9 +228,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("character varying(512)"); - b.Property("AuthorId") - .IsRequired() - .HasColumnType("text"); + b.Property("AuthorId") + .HasColumnType("uuid"); b.Property("AuthorsNote") .IsRequired() @@ -211,6 +257,198 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("Revisions"); }); + modelBuilder.Entity("Domain.Entities.Role", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Roles"); + + b.HasData( + new + { + Id = new Guid("ca2cfe04-24ed-42d0-9237-6d5ed7885063"), + Name = "Admin" + }, + new + { + Id = new Guid("e1d0ad14-fb96-4488-bf64-8aab2d1ef43d"), + Name = "Editor" + }, + new + { + Id = new Guid("3a7651c5-2cf1-4ed6-9866-c6bac6e8f6dd"), + Name = "User" + }, + new + { + Id = new Guid("2afacdee-55a4-4e23-9f66-2ca5a5af9751"), + Name = "Lurker" + }); + }); + + modelBuilder.Entity("Domain.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ExternalId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ExternalId"); + + b.HasIndex("Name"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("PermissionRole", b => + { + b.Property("PermissionsId") + .HasColumnType("uuid"); + + b.Property("RolesId") + .HasColumnType("uuid"); + + b.HasKey("PermissionsId", "RolesId"); + + b.HasIndex("RolesId"); + + b.ToTable("RolePermission", (string)null); + + b.HasData( + new + { + PermissionsId = new Guid("5744b9a3-2256-4f69-b6ac-4d8c164632c9"), + RolesId = new Guid("ca2cfe04-24ed-42d0-9237-6d5ed7885063") + }, + new + { + PermissionsId = new Guid("e4d1aff3-8bc1-4730-b95d-438f1fde6aeb"), + RolesId = new Guid("ca2cfe04-24ed-42d0-9237-6d5ed7885063") + }, + new + { + PermissionsId = new Guid("861b96a8-9a42-45e4-b1c2-733ef85bc2d6"), + RolesId = new Guid("ca2cfe04-24ed-42d0-9237-6d5ed7885063") + }, + new + { + PermissionsId = new Guid("27c68a58-6f91-41a2-bd36-c4dda391f309"), + RolesId = new Guid("ca2cfe04-24ed-42d0-9237-6d5ed7885063") + }, + new + { + PermissionsId = new Guid("76da2206-a875-47b2-86bb-27ecd0e9f4b9"), + RolesId = new Guid("ca2cfe04-24ed-42d0-9237-6d5ed7885063") + }, + new + { + PermissionsId = new Guid("7d9bbc60-f1c8-4762-ac1d-edac85f22b48"), + RolesId = new Guid("ca2cfe04-24ed-42d0-9237-6d5ed7885063") + }, + new + { + PermissionsId = new Guid("95571fee-1313-41de-adfe-6b65fe53760b"), + RolesId = new Guid("ca2cfe04-24ed-42d0-9237-6d5ed7885063") + }, + new + { + PermissionsId = new Guid("fa7c5b48-0fb4-4757-96a7-014b00fdd78b"), + RolesId = new Guid("ca2cfe04-24ed-42d0-9237-6d5ed7885063") + }, + new + { + PermissionsId = new Guid("eced433f-b27c-4c6e-af41-d2231fb40f03"), + RolesId = new Guid("ca2cfe04-24ed-42d0-9237-6d5ed7885063") + }, + new + { + PermissionsId = new Guid("5744b9a3-2256-4f69-b6ac-4d8c164632c9"), + RolesId = new Guid("e1d0ad14-fb96-4488-bf64-8aab2d1ef43d") + }, + new + { + PermissionsId = new Guid("e4d1aff3-8bc1-4730-b95d-438f1fde6aeb"), + RolesId = new Guid("e1d0ad14-fb96-4488-bf64-8aab2d1ef43d") + }, + new + { + PermissionsId = new Guid("861b96a8-9a42-45e4-b1c2-733ef85bc2d6"), + RolesId = new Guid("e1d0ad14-fb96-4488-bf64-8aab2d1ef43d") + }, + new + { + PermissionsId = new Guid("27c68a58-6f91-41a2-bd36-c4dda391f309"), + RolesId = new Guid("e1d0ad14-fb96-4488-bf64-8aab2d1ef43d") + }, + new + { + PermissionsId = new Guid("76da2206-a875-47b2-86bb-27ecd0e9f4b9"), + RolesId = new Guid("e1d0ad14-fb96-4488-bf64-8aab2d1ef43d") + }, + new + { + PermissionsId = new Guid("7d9bbc60-f1c8-4762-ac1d-edac85f22b48"), + RolesId = new Guid("e1d0ad14-fb96-4488-bf64-8aab2d1ef43d") + }, + new + { + PermissionsId = new Guid("95571fee-1313-41de-adfe-6b65fe53760b"), + RolesId = new Guid("e1d0ad14-fb96-4488-bf64-8aab2d1ef43d") + }, + new + { + PermissionsId = new Guid("fa7c5b48-0fb4-4757-96a7-014b00fdd78b"), + RolesId = new Guid("e1d0ad14-fb96-4488-bf64-8aab2d1ef43d") + }, + new + { + PermissionsId = new Guid("eced433f-b27c-4c6e-af41-d2231fb40f03"), + RolesId = new Guid("e1d0ad14-fb96-4488-bf64-8aab2d1ef43d") + }, + new + { + PermissionsId = new Guid("5744b9a3-2256-4f69-b6ac-4d8c164632c9"), + RolesId = new Guid("3a7651c5-2cf1-4ed6-9866-c6bac6e8f6dd") + }, + new + { + PermissionsId = new Guid("861b96a8-9a42-45e4-b1c2-733ef85bc2d6"), + RolesId = new Guid("3a7651c5-2cf1-4ed6-9866-c6bac6e8f6dd") + }); + }); + + modelBuilder.Entity("RoleUser", b => + { + b.Property("RolesId") + .HasColumnType("uuid"); + + b.Property("UsersId") + .HasColumnType("uuid"); + + b.HasKey("RolesId", "UsersId"); + + b.HasIndex("UsersId"); + + b.ToTable("RoleUser"); + }); + modelBuilder.Entity("CategoryRevision", b => { b.HasOne("Domain.Entities.Category", null) @@ -264,7 +502,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("Domain.Entities.Review", b => { - b.HasOne("Domain.Entities.Author", "Reviewer") + b.HasOne("Domain.Entities.User", "Reviewer") .WithMany() .HasForeignKey("ReviewerId") .OnDelete(DeleteBehavior.Cascade) @@ -289,7 +527,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - b.HasOne("Domain.Entities.Author", "Author") + b.HasOne("Domain.Entities.User", "Author") .WithMany() .HasForeignKey("AuthorId") .OnDelete(DeleteBehavior.Cascade) @@ -306,6 +544,36 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("LatestReview"); }); + modelBuilder.Entity("PermissionRole", b => + { + b.HasOne("Domain.Entities.Permission", null) + .WithMany() + .HasForeignKey("PermissionsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Role", null) + .WithMany() + .HasForeignKey("RolesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("RoleUser", b => + { + b.HasOne("Domain.Entities.Role", null) + .WithMany() + .HasForeignKey("RolesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.User", null) + .WithMany() + .HasForeignKey("UsersId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + modelBuilder.Entity("Domain.Entities.Article", b => { b.Navigation("Revisions"); diff --git a/Infrastructure/Extensions/ServiceCollectionExt.cs b/Infrastructure/Extensions/ServiceCollectionExt.cs index 7f79540..24e7744 100644 --- a/Infrastructure/Extensions/ServiceCollectionExt.cs +++ b/Infrastructure/Extensions/ServiceCollectionExt.cs @@ -1,19 +1,10 @@ -using System.Security.Claims; -using Application.Authorization.Abstractions; -using Application.Common.Caching; +using Application.Common.Caching; using Application.Data; -using Application.Features.Authors.CreateAuthor; -using Application.Features.Authors.EditAuthor; -using Infrastructure.Authorization; using Infrastructure.Caching; using Infrastructure.Data; -using Keycloak.AuthServices.Authentication; -using Keycloak.AuthServices.Authorization; -using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using static Application.Features.Authors.Errors.Author; namespace Infrastructure.Extensions; @@ -30,44 +21,9 @@ bool isDevelopment services.AddDbContext(options => options.UseNpgsql(connectionString)); - services.AddKeycloakWebApiAuthentication(configuration, options => - { - if (isDevelopment) options.RequireHttpsMetadata = false; - options.Events ??= new JwtBearerEvents(); - options.Events.OnTokenValidated += CreateOrUpdateAuthorOnTokenValidated; - }); - services.AddAuthorization(); - services.AddKeycloakAuthorization(configuration); - - services.AddScoped(); - services.AddScoped(); - services.AddMemoryCache(); services.AddSingleton(); return services; } - - private static async Task CreateOrUpdateAuthorOnTokenValidated(TokenValidatedContext context) - { - var principal = context.Principal; - var idClaim = principal?.FindFirst(ClaimTypes.NameIdentifier); - if (idClaim == null) return; - if (principal?.Identity?.Name == null) return; - if (principal.Identity.IsAuthenticated == false) return; - - //TODO: Reimplement without MediatR - - // var mediator = context.HttpContext.RequestServices.GetService(); - // if (mediator == null) return; - // - // var createAuthorCommand = new CreateAuthorCommand(idClaim.Value, principal.Identity.Name); - // - // var createAuthorResult = await mediator.Send(createAuthorCommand); - // if (createAuthorResult.IsError && createAuthorResult.FirstError == DuplicateId) - // { - // var editAuthorCommand = new EditAuthorCommand(idClaim.Value, principal.Identity.Name); - // await mediator.Send(editAuthorCommand); - // } - } } \ No newline at end of file diff --git a/Infrastructure/Infrastructure.csproj b/Infrastructure/Infrastructure.csproj index 7813438..2767be1 100644 --- a/Infrastructure/Infrastructure.csproj +++ b/Infrastructure/Infrastructure.csproj @@ -13,8 +13,6 @@ - - all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/WebApi/Auth/JwtBearerExtensions.cs b/WebApi/Auth/JwtBearerExtensions.cs new file mode 100644 index 0000000..27d3002 --- /dev/null +++ b/WebApi/Auth/JwtBearerExtensions.cs @@ -0,0 +1,87 @@ +using System.Security.Claims; +using Application.Data; +using Domain.Entities; +using Domain.Rbac; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.EntityFrameworkCore; + +namespace WebApi.Auth; + +public static class JwtBearerExtensions +{ + public static JwtBearerEvents ConfigureJwtBearerEvents(this JwtBearerOptions options) + { + var events = new JwtBearerEvents + { + OnTokenValidated = OnTokenValidated, + OnAuthenticationFailed = OnAuthenticationFailed + }; + + return events; + } + + private static async Task OnTokenValidated(TokenValidatedContext context) + { + var dbContext = context.HttpContext.RequestServices.GetRequiredService(); + var logger = context.HttpContext.RequestServices.GetRequiredService>(); + + try + { + var externalId = context.Principal?.FindFirst(ClaimTypes.NameIdentifier)?.Value; + + var cacheKey = $"user_{externalId}"; + + if (string.IsNullOrEmpty(externalId)) + { + logger.LogWarning("Missing sub claim in token"); + context.Fail("Missing sub"); + return; + } + + var user = await dbContext.Users.FirstOrDefaultAsync(u => u.ExternalId == externalId); + + if (user == null) + { + var name = context.Principal?.Claims.FirstOrDefault(c => c.Type == "preferred_username")?.Value; + if (string.IsNullOrEmpty(name)) + { + logger.LogWarning("Missing preferred_username claim for new user"); + context.Fail("Missing preferred_username"); + return; + } + + user = new User + { + Id = default!, + ExternalId = externalId, + Name = name + }; + dbContext.Roles.Attach(Roles.Lurker); + user.Roles.Add(Roles.Lurker); + + await dbContext.Users.AddAsync(user); + await dbContext.SaveChangesAsync(); + } + + var claims = new List + { + new("internal_id", user.Id.ToString()) + }; + + var newIdentity = new ClaimsIdentity(claims, context.Scheme.Name); + context.Principal?.AddIdentity(newIdentity); + } + catch (Exception ex) + { + logger.LogError(ex, "An error occurred on token validation."); + context.Fail("Authentication error"); + } + } + + private static Task OnAuthenticationFailed(AuthenticationFailedContext context) + { + var logger = context.HttpContext.RequestServices.GetRequiredService>(); + logger.LogError(context.Exception, "Authentication failed"); + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/WebApi/Auth/JwtSettings.cs b/WebApi/Auth/JwtSettings.cs new file mode 100644 index 0000000..bd91b9f --- /dev/null +++ b/WebApi/Auth/JwtSettings.cs @@ -0,0 +1,16 @@ +namespace WebApi.Auth; + +public record JwtSettings +{ + public const string SectionName = "Jwt"; + + public required string Authority { get; init; } + public required string Audience { get; init; } + public required string Issuer { get; init; } + public required bool RequireHttpsMetadata { get; init; } + public required bool ValidateIssuer { get; init; } + public required bool ValidateAudience { get; init; } + public required bool ValidateLifetime { get; init; } + public required bool ValidateIssuerSigningKey { get; init; } + public required TimeSpan ClockSkew { get; init; } +} \ No newline at end of file diff --git a/WebApi/Auth/MinimalApiExtensions.cs b/WebApi/Auth/MinimalApiExtensions.cs new file mode 100644 index 0000000..b356a90 --- /dev/null +++ b/WebApi/Auth/MinimalApiExtensions.cs @@ -0,0 +1,11 @@ +using Domain.Entities; + +namespace WebApi.Auth; + +public static class MinimalApiExtensions +{ + public static RouteHandlerBuilder RequirePermission(this RouteHandlerBuilder builder, Permission permission) + { + return builder.RequireAuthorization($"Permission_{permission.Id:D}"); + } +} \ No newline at end of file diff --git a/WebApi/Auth/PermissionRequirement.cs b/WebApi/Auth/PermissionRequirement.cs new file mode 100644 index 0000000..6bfe187 --- /dev/null +++ b/WebApi/Auth/PermissionRequirement.cs @@ -0,0 +1,45 @@ +using System.Security.Claims; +using Application.Authorization.Abstractions; +using Domain.Entities; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; + +namespace WebApi.Auth; + +public class PermissionRequirement : IAuthorizationRequirement +{ + public Permission[] Permissions { get; } + + public PermissionRequirement(params Permission[] permissions) + { + Permissions = permissions; + } +} + +public class PermissionAuthorizationHandler : AuthorizationHandler +{ + private readonly IUserContext _userContext; + + public PermissionAuthorizationHandler(IUserContext userContext) + { + _userContext = userContext; + } + + protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement) + { + var user = await _userContext.GetCurrentUserAsync(); + if (user == null) + { + return; + } + + var hasAnyPermission = user.Roles + .Any(r => r.Permissions.Any(p => requirement.Permissions.Select(x => x.Id).Contains(p.Id))); + + if (hasAnyPermission) + { + context.Succeed(requirement); + } + } +} \ No newline at end of file diff --git a/WebApi/Auth/RequirePermissionAttribute.cs b/WebApi/Auth/RequirePermissionAttribute.cs new file mode 100644 index 0000000..782ca87 --- /dev/null +++ b/WebApi/Auth/RequirePermissionAttribute.cs @@ -0,0 +1,13 @@ +using Domain.Entities; +using Microsoft.AspNetCore.Authorization; + +namespace WebApi.Auth; + +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true)] +public class RequirePermissionAttribute : AuthorizeAttribute +{ + public RequirePermissionAttribute(Permission permission) : base(policy: $"Permission_{permission.Id}") + { + Policy = $"Permission_{permission.Id}"; + } +} \ No newline at end of file diff --git a/WebApi/Auth/UserContext.cs b/WebApi/Auth/UserContext.cs new file mode 100644 index 0000000..7a9a338 --- /dev/null +++ b/WebApi/Auth/UserContext.cs @@ -0,0 +1,38 @@ +using Application.Authorization.Abstractions; +using Application.Data; +using Domain.Entities; +using Microsoft.EntityFrameworkCore; + +namespace WebApi.Auth; + +public class UserContext : IUserContext +{ + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IApplicationDbContext _dbContext; + + public UserContext(IHttpContextAccessor httpContextAccessor, IApplicationDbContext dbContext) + { + _httpContextAccessor = httpContextAccessor; + _dbContext = dbContext; + } + + public Guid? UserId + { + get + { + var internalId = _httpContextAccessor.HttpContext?.User.FindFirst("internal_id"); + if (internalId == null) return null; + return Guid.Parse(internalId.Value); + } + } + + public async Task GetCurrentUserAsync() + { + if (UserId == null) return null; + + return await _dbContext.Users + .Include(u => u.Roles) + .ThenInclude(r => r.Permissions) + .FirstOrDefaultAsync(u => u.Id == UserId); + } +} \ No newline at end of file diff --git a/WebApi/Extensions/ServiceCollectionExtensions.cs b/WebApi/Extensions/ServiceCollectionExtensions.cs index 51b2adc..f4f37c7 100644 --- a/WebApi/Extensions/ServiceCollectionExtensions.cs +++ b/WebApi/Extensions/ServiceCollectionExtensions.cs @@ -1,4 +1,3 @@ -using Keycloak.AuthServices.Authentication; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.OpenApi.Models; using WebApi.SchemaFilters; @@ -14,9 +13,7 @@ internal static void RegisterSwagger(this IServiceCollection services, IConfigur options.CustomSchemaIds(x => x.FullName?.Replace("+", ".").Replace(x.Namespace + ".", "")); options.SupportNonNullableReferenceTypes(); options.SchemaFilter(); - var keycloakOptions = new KeycloakAuthenticationOptions(); - configuration.GetSection(KeycloakAuthenticationOptions.Section).Bind(keycloakOptions, opt => opt.BindNonPublicProperties = true); var securityScheme = new OpenApiSecurityScheme { Name = "Auth", @@ -31,14 +28,16 @@ internal static void RegisterSwagger(this IServiceCollection services, IConfigur //TODO awful hack .Replace("host.docker.internal", "localhost") for ease of testing Implicit = new OpenApiOAuthFlow { - AuthorizationUrl = new Uri($"{keycloakOptions.KeycloakUrlRealm.Replace("host.docker.internal", "localhost")}/protocol/openid-connect/auth"), - TokenUrl = new Uri($"{keycloakOptions.KeycloakUrlRealm.Replace("host.docker.internal", "localhost")}/protocol/openid-connect/token"), + //TODO: Hardcoded values should be extracted to config + AuthorizationUrl = new Uri($"{"http://localhost:8091/realms/dumb realm".Replace("host.docker.internal", "localhost")}/protocol/openid-connect/auth"), + TokenUrl = new Uri($"{"http://localhost:8091/realms/dumb realm".Replace("host.docker.internal", "localhost")}/protocol/openid-connect/token"), Scopes = new Dictionary() }, AuthorizationCode = new OpenApiOAuthFlow { - AuthorizationUrl = new Uri($"{keycloakOptions.KeycloakUrlRealm.Replace("host.docker.internal", "localhost")}/protocol/openid-connect/auth"), - TokenUrl = new Uri($"{keycloakOptions.KeycloakUrlRealm.Replace("host.docker.internal", "localhost")}/protocol/openid-connect/token"), + //TODO: Hardcoded values should be extracted to config + AuthorizationUrl = new Uri($"{"http://localhost:8091/realms/dumb realm".Replace("host.docker.internal", "localhost")}/protocol/openid-connect/auth"), + TokenUrl = new Uri($"{"http://localhost:8091/realms/dumb realm".Replace("host.docker.internal", "localhost")}/protocol/openid-connect/token"), Scopes = new Dictionary() } } diff --git a/WebApi/Features/Articles/ArticlesModule.cs b/WebApi/Features/Articles/ArticlesModule.cs index 79d231b..758c7e9 100644 --- a/WebApi/Features/Articles/ArticlesModule.cs +++ b/WebApi/Features/Articles/ArticlesModule.cs @@ -10,10 +10,10 @@ using Application.Features.Articles.ReviewRevision; using Application.Features.Articles.SearchArticles; using Application.Features.Articles.SetRedirect; -using Microsoft.AspNetCore.Authorization; +using Domain.Rbac; +using WebApi.Auth; using WebApi.Extensions; using WebApi.Features.Articles.Requests; -using static Application.Common.Constants.AuthorizationConstants; namespace WebApi.Features.Articles; @@ -41,7 +41,7 @@ async Task ( .ProducesProblem(StatusCodes.Status401Unauthorized) .ProducesProblem(StatusCodes.Status403Forbidden) .ProducesProblem(StatusCodes.Status409Conflict) - .RequireAuthorization(new AuthorizeAttribute {Roles = $"{Roles.Admin}, {Roles.Editor}, {Roles.User}"}) + .RequirePermission(Permissions.ArticleCreate) .WithOpenApi(); app.MapGet("/api/articles", @@ -124,7 +124,7 @@ async Task ( .ProducesProblem(StatusCodes.Status403Forbidden) .ProducesProblem(StatusCodes.Status404NotFound) .ProducesProblem(StatusCodes.Status409Conflict) - .RequireAuthorization(new AuthorizeAttribute {Roles = $"{Roles.Admin}, {Roles.Editor}, {Roles.User}"}) + .RequirePermission(Permissions.ArticleEdit) .WithOpenApi(); app.MapPut("/api/articles/{id}/redirect", @@ -149,7 +149,7 @@ async Task ( .ProducesProblem(StatusCodes.Status403Forbidden) .ProducesProblem(StatusCodes.Status404NotFound) .ProducesProblem(StatusCodes.Status409Conflict) - .RequireAuthorization(new AuthorizeAttribute {Roles = $"{Roles.Admin}, {Roles.Editor}"}) + .RequirePermission(Permissions.ArticleSetRedirect) .WithOpenApi(); app.MapGet("/api/articles/revisions/pending", @@ -169,7 +169,7 @@ async Task ( .Produces() .ProducesProblem(StatusCodes.Status401Unauthorized) .ProducesProblem(StatusCodes.Status403Forbidden) - .RequireAuthorization(new AuthorizeAttribute {Roles = $"{Roles.Admin}, {Roles.Editor}"}) + .RequirePermission(Permissions.ArticleSeePendingRevisions) .WithOpenApi(); app.MapGet("/api/articles/revisions/pending/count", @@ -190,7 +190,7 @@ async Task ( .Produces() .ProducesProblem(StatusCodes.Status401Unauthorized) .ProducesProblem(StatusCodes.Status403Forbidden) - .RequireAuthorization(new AuthorizeAttribute {Roles = $"{Roles.Admin}, {Roles.Editor}"}) + .RequirePermission(Permissions.ArticleSeePendingRevisions) .WithOpenApi(); app.MapGet("/api/articles/{id}/revisions", @@ -252,7 +252,7 @@ async Task ( .ProducesProblem(StatusCodes.Status401Unauthorized) .ProducesProblem(StatusCodes.Status403Forbidden) .ProducesProblem(StatusCodes.Status404NotFound) - .RequireAuthorization(new AuthorizeAttribute {Roles = $"{Roles.Admin}, {Roles.Editor}"}) + .RequirePermission(Permissions.ArticleReviewRevision) .WithOpenApi(); app.MapDelete("/api/articles/{id}", @@ -274,7 +274,7 @@ async Task ( .ProducesProblem(StatusCodes.Status401Unauthorized) .ProducesProblem(StatusCodes.Status403Forbidden) .ProducesProblem(StatusCodes.Status404NotFound) - .RequireAuthorization(new AuthorizeAttribute {Roles = $"{Roles.Admin}, {Roles.Editor}"}) + .RequirePermission(Permissions.ArticleDelete) .WithOpenApi(); } } \ No newline at end of file diff --git a/WebApi/Features/Categories/CategoriesModule.cs b/WebApi/Features/Categories/CategoriesModule.cs index d3afa46..1118d87 100644 --- a/WebApi/Features/Categories/CategoriesModule.cs +++ b/WebApi/Features/Categories/CategoriesModule.cs @@ -4,10 +4,10 @@ using Application.Features.Categories.GetCategories; using Application.Features.Categories.GetCategoriesTree; using Application.Features.Categories.GetCategoryArticles; -using Microsoft.AspNetCore.Authorization; +using Domain.Rbac; +using WebApi.Auth; using WebApi.Extensions; using WebApi.Features.Categories.Requests; -using static Application.Common.Constants.AuthorizationConstants; namespace WebApi.Features.Categories; @@ -36,7 +36,7 @@ public static void AddCategoriesEndpoints(this IEndpointRouteBuilder app) .ProducesProblem(StatusCodes.Status403Forbidden) .ProducesProblem(StatusCodes.Status404NotFound) .ProducesProblem(StatusCodes.Status409Conflict) - .RequireAuthorization(new AuthorizeAttribute {Roles = $"{Roles.Admin}, {Roles.Editor}"}) + .RequirePermission(Permissions.CategoryCreate) .WithOpenApi(); app.MapGet("/api/categories", @@ -109,7 +109,7 @@ async Task ( .ProducesProblem(StatusCodes.Status401Unauthorized) .ProducesProblem(StatusCodes.Status403Forbidden) .ProducesProblem(StatusCodes.Status404NotFound) - .RequireAuthorization(new AuthorizeAttribute {Roles = $"{Roles.Admin}, {Roles.Editor}"}) + .RequirePermission(Permissions.CategoryDelete) .WithOpenApi(); } } \ No newline at end of file diff --git a/WebApi/Features/Navigations/NavigationsModule.cs b/WebApi/Features/Navigations/NavigationsModule.cs index 3368bd6..e2a25f5 100644 --- a/WebApi/Features/Navigations/NavigationsModule.cs +++ b/WebApi/Features/Navigations/NavigationsModule.cs @@ -1,10 +1,10 @@ using Application.Common.Messaging; using Application.Features.Navigations.GetNavigationTree; using Application.Features.Navigations.UpdateNavigationsTree; -using Microsoft.AspNetCore.Authorization; +using Domain.Rbac; +using WebApi.Auth; using WebApi.Extensions; using WebApi.Features.Navigations.Requests; -using static Application.Common.Constants.AuthorizationConstants; namespace WebApi.Features.Navigations; @@ -42,7 +42,7 @@ async Task (ICommandHandler (IGetUserCommandHandler getUserCommandHandler, IUserContext userContext, CancellationToken cancellationToken) => + { + var userId = userContext.UserId; + var response = await getUserCommandHandler.Handle(new GetUserCommand(userId!.Value), cancellationToken); + return response.MatchFirst( + value => Results.Ok(value), + error => error.ToIResult() + ); + }) + .WithName("GetUser") + .WithTags("Users") + .RequireAuthorization() + .Produces() + .WithOpenApi(); + } +} \ No newline at end of file diff --git a/WebApi/Program.cs b/WebApi/Program.cs index 2f12810..d9b3750 100644 --- a/WebApi/Program.cs +++ b/WebApi/Program.cs @@ -1,13 +1,19 @@ using System.Text.Json.Serialization; +using Application.Authorization.Abstractions; using Application.Extensions; +using Domain.Rbac; using Infrastructure.Extensions; -using Keycloak.AuthServices.Authentication; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.AspNetCore.Authorization; using WebApi.Extensions; using WebApi.Features.Articles; using WebApi.Features.Categories; using WebApi.Features.Navigations; using WebApi.Middlewares; using Microsoft.AspNetCore.Mvc; +using Microsoft.IdentityModel.Tokens; +using WebApi.Auth; +using WebApi.Features.Users; var builder = WebApplication.CreateBuilder(args); @@ -31,6 +37,39 @@ builder.Services.RegisterSwagger(builder.Configuration); } +builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) + .AddJwtBearer(options => + { + var jwtSettings = builder.Configuration.GetRequiredSection(JwtSettings.SectionName).Get() + ?? throw new InvalidOperationException($"Missing {JwtSettings.SectionName} section"); + + options.Authority = jwtSettings.Authority; + options.Audience = jwtSettings.Audience; + options.RequireHttpsMetadata = jwtSettings.RequireHttpsMetadata; + options.TokenValidationParameters = new TokenValidationParameters + { + ValidateIssuer = jwtSettings.ValidateIssuer, + ValidIssuer = jwtSettings.Issuer, + ValidateAudience = jwtSettings.ValidateAudience, + ValidAudience = jwtSettings.Audience, + ValidateLifetime = jwtSettings.ValidateLifetime, + ValidateIssuerSigningKey = jwtSettings.ValidateIssuerSigningKey, + ClockSkew = jwtSettings.ClockSkew, + }; + + options.Events = options.ConfigureJwtBearerEvents(); + }); +builder.Services.AddAuthorization(options => +{ + foreach (var permission in Permissions.All) + { + options.AddPolicy($"Permission_{permission.Id:D}", + policy => policy.Requirements.Add(new PermissionRequirement(permission))); + } +}); +builder.Services.AddScoped(); +builder.Services.AddScoped(); + var app = builder.Build(); app.UseExceptionHandler(); @@ -49,15 +88,12 @@ app.AddArticlesEndpoints(); app.AddCategoriesEndpoints(); app.AddNavigationsEndpoints(); +app.AddUsersEndpoints(); if (app.Environment.IsDevelopment()) { - var keycloakOptions = new KeycloakAuthenticationOptions(); - app.Configuration.GetSection(KeycloakAuthenticationOptions.Section) - .Bind(keycloakOptions, opt => opt.BindNonPublicProperties = true); - app.UseSwagger(); - app.UseSwaggerUI(options => options.OAuthClientId(keycloakOptions.Resource)); + app.UseSwaggerUI(); } app.Run(); \ No newline at end of file diff --git a/WebApi/WebApi.csproj b/WebApi/WebApi.csproj index 047416f..d80105c 100644 --- a/WebApi/WebApi.csproj +++ b/WebApi/WebApi.csproj @@ -14,6 +14,7 @@ + all diff --git a/WebApi/appsettings.Development.json b/WebApi/appsettings.Development.json index a1234c1..4b989d4 100644 --- a/WebApi/appsettings.Development.json +++ b/WebApi/appsettings.Development.json @@ -5,5 +5,16 @@ "Microsoft.AspNetCore": "Warning", "Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware": "None" } + }, + "Jwt": { + "Authority": "http://localhost:8091/realms/dumb realm", + "Audience": "account", + "Issuer": "http://localhost:8091/realms/dumb realm", + "RequireHttpsMetadata": false, + "ValidateIssuer": true, + "ValidateAudience": true, + "ValidateLifetime": true, + "ValidateIssuerSigningKey": true, + "ClockSkew": "00:00:05" } } diff --git a/WebApi/appsettings.json b/WebApi/appsettings.json index 30e7f35..6c59f49 100644 --- a/WebApi/appsettings.json +++ b/WebApi/appsettings.json @@ -11,17 +11,15 @@ "ConnectionStrings": { "DefaultConnection": "Host=localhost;Database=wiki;Username=postgres;Password=aboba" }, - "Keycloak": { - "realm": "wiki-realm", - "auth-server-url": "http://localhost:8090/", - "ssl-required": "none", - "resource": "wiki", - "verify-token-audience": true, - "credentials": { - "secret": "" - }, - "confidential-port": 0, - "EnableRolesMapping": "ResourceAccess", - "RolesResource": "wiki" + "Jwt": { + "Authority": "https://your-keycloak-domain.com/auth/realms/your-realm", + "Audience": "your-client-id", + "Issuer": "https://your-keycloak-domain.com/auth/realms/your-realm", + "RequireHttpsMetadata": true, + "ValidateIssuer": true, + "ValidateAudience": true, + "ValidateLifetime": true, + "ValidateIssuerSigningKey": true, + "ClockSkew": "00:00:05" } } From 2efd3542d17d33a11fc4bbdcd5846899b683adda Mon Sep 17 00:00:00 2001 From: VitSun666 <61446535+VitSun666@users.noreply.github.com> Date: Fri, 8 Aug 2025 20:11:43 +0300 Subject: [PATCH 2/6] feat: updated to correspond with upstream --- .../Extensions/ServiceCollectionExt.cs | 4 ++-- .../CreateAuthor/CreateAuthorCommand.cs | 5 ---- .../CreateAuthorCommandHandler.cs | 24 ------------------- .../Authors/EditAuthor/EditAuthorCommand.cs | 5 ---- .../EditAuthor/EditAuthorCommandHandler.cs | 24 ------------------- .../Features/Users/GetUser/GetUserCommand.cs | 3 --- .../Features/Users/GetUser/GetUserQuery.cs | 5 ++++ ...mmandHandler.cs => GetUserQueryHandler.cs} | 7 +++--- .../Users/GetUser/IGetUserCommandHandler.cs | 7 ------ .../RenameUser/IRenameUserCommandHandler.cs | 8 ------- .../Users/RenameUser/RenameUserCommand.cs | 6 +++-- .../RenameUser/RenameUserCommandHandler.cs | 7 +++--- WebApi/Features/Users/UsersModule.cs | 5 ++-- 13 files changed, 22 insertions(+), 88 deletions(-) delete mode 100644 Application/Features/Authors/CreateAuthor/CreateAuthorCommand.cs delete mode 100644 Application/Features/Authors/CreateAuthor/CreateAuthorCommandHandler.cs delete mode 100644 Application/Features/Authors/EditAuthor/EditAuthorCommand.cs delete mode 100644 Application/Features/Authors/EditAuthor/EditAuthorCommandHandler.cs delete mode 100644 Application/Features/Users/GetUser/GetUserCommand.cs create mode 100644 Application/Features/Users/GetUser/GetUserQuery.cs rename Application/Features/Users/GetUser/{GetUserCommandHandler.cs => GetUserQueryHandler.cs} (69%) delete mode 100644 Application/Features/Users/GetUser/IGetUserCommandHandler.cs delete mode 100644 Application/Features/Users/RenameUser/IRenameUserCommandHandler.cs diff --git a/Application/Extensions/ServiceCollectionExt.cs b/Application/Extensions/ServiceCollectionExt.cs index bbf1244..d7593d5 100644 --- a/Application/Extensions/ServiceCollectionExt.cs +++ b/Application/Extensions/ServiceCollectionExt.cs @@ -49,8 +49,8 @@ public static IServiceCollection AddApplicationServices(this IServiceCollection services.AddScoped, SearchArticlesQueryHandler>(); services.AddScoped, SetRedirectCommandHandler>(); - services.AddScoped(); - services.AddScoped(); + services.AddScoped, RenameUserCommandHandler>(); + services.AddScoped, GetUserQueryHandler>(); services.AddScoped, CreateCategoryCommandHandler>(); services.AddScoped, DeleteCategoryCommandHandler>(); diff --git a/Application/Features/Authors/CreateAuthor/CreateAuthorCommand.cs b/Application/Features/Authors/CreateAuthor/CreateAuthorCommand.cs deleted file mode 100644 index c856b17..0000000 --- a/Application/Features/Authors/CreateAuthor/CreateAuthorCommand.cs +++ /dev/null @@ -1,5 +0,0 @@ -using Application.Common.Messaging; - -namespace Application.Features.Authors.CreateAuthor; - -public record CreateAuthorCommand(string Id, string Name) : ICommand; \ No newline at end of file diff --git a/Application/Features/Authors/CreateAuthor/CreateAuthorCommandHandler.cs b/Application/Features/Authors/CreateAuthor/CreateAuthorCommandHandler.cs deleted file mode 100644 index db373b2..0000000 --- a/Application/Features/Authors/CreateAuthor/CreateAuthorCommandHandler.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Application.Common.Messaging; -using Application.Data; -using Domain.Entities; -using ErrorOr; - -namespace Application.Features.Authors.CreateAuthor; - -public class CreateAuthorCommandHandler( - IApplicationDbContext dbContext -) : ICommandHandler -{ - public async Task> HandleAsync(CreateAuthorCommand command, CancellationToken token) - { - var author = new Author {Id = command.Id, Name = command.Name}; - - var exists = dbContext.Authors.Any(e => e.Id == author.Id); - if (exists) return Errors.Author.DuplicateId; - - dbContext.Authors.Add(author); - await dbContext.SaveChangesAsync(token); - - return new CreateAuthorResponse(author.Id); - } -} \ No newline at end of file diff --git a/Application/Features/Authors/EditAuthor/EditAuthorCommand.cs b/Application/Features/Authors/EditAuthor/EditAuthorCommand.cs deleted file mode 100644 index 025a41c..0000000 --- a/Application/Features/Authors/EditAuthor/EditAuthorCommand.cs +++ /dev/null @@ -1,5 +0,0 @@ -using Application.Common.Messaging; - -namespace Application.Features.Authors.EditAuthor; - -public record EditAuthorCommand(string Id, string Name) : ICommand; \ No newline at end of file diff --git a/Application/Features/Authors/EditAuthor/EditAuthorCommandHandler.cs b/Application/Features/Authors/EditAuthor/EditAuthorCommandHandler.cs deleted file mode 100644 index 2cffbcd..0000000 --- a/Application/Features/Authors/EditAuthor/EditAuthorCommandHandler.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Application.Common.Messaging; -using Application.Data; -using ErrorOr; -using Microsoft.EntityFrameworkCore; - -namespace Application.Features.Authors.EditAuthor; - -public class EditAuthorCommandHandler( - IApplicationDbContext dbContext -) : ICommandHandler -{ - public async Task> HandleAsync(EditAuthorCommand command, CancellationToken token) - { - var author = await dbContext.Authors.FirstOrDefaultAsync(e => e.Id == command.Id, token); - - if (author == null) return Errors.Author.NotFound; - - author.Name = command.Name; - - await dbContext.SaveChangesAsync(token); - - return new EditAuthorResponse(author.Id); - } -} \ No newline at end of file diff --git a/Application/Features/Users/GetUser/GetUserCommand.cs b/Application/Features/Users/GetUser/GetUserCommand.cs deleted file mode 100644 index 5446b86..0000000 --- a/Application/Features/Users/GetUser/GetUserCommand.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace Application.Features.Users.GetUser; - -public record GetUserCommand(Guid Id); \ No newline at end of file diff --git a/Application/Features/Users/GetUser/GetUserQuery.cs b/Application/Features/Users/GetUser/GetUserQuery.cs new file mode 100644 index 0000000..ed29d1a --- /dev/null +++ b/Application/Features/Users/GetUser/GetUserQuery.cs @@ -0,0 +1,5 @@ +using Application.Common.Messaging; + +namespace Application.Features.Users.GetUser; + +public record GetUserQuery(Guid Id) : IQuery; \ No newline at end of file diff --git a/Application/Features/Users/GetUser/GetUserCommandHandler.cs b/Application/Features/Users/GetUser/GetUserQueryHandler.cs similarity index 69% rename from Application/Features/Users/GetUser/GetUserCommandHandler.cs rename to Application/Features/Users/GetUser/GetUserQueryHandler.cs index 874a1d4..137a512 100644 --- a/Application/Features/Users/GetUser/GetUserCommandHandler.cs +++ b/Application/Features/Users/GetUser/GetUserQueryHandler.cs @@ -1,3 +1,4 @@ +using Application.Common.Messaging; using Application.Data; using Application.Features.Users.Errors; using ErrorOr; @@ -5,15 +6,15 @@ namespace Application.Features.Users.GetUser; -public class GetUserCommandHandler(IApplicationDbContext dbContext) : IGetUserCommandHandler +public class GetUserQueryHandler(IApplicationDbContext dbContext) : IQueryHandler { - public async Task> Handle(GetUserCommand command, CancellationToken token) + public async Task> HandleAsync(GetUserQuery query, CancellationToken token) { var user = await dbContext.Users .Include(u => u.Roles) .ThenInclude(u => u.Permissions) .AsNoTracking() - .FirstOrDefaultAsync(e => e.Id == command.Id, token); + .FirstOrDefaultAsync(e => e.Id == query.Id, token); if (user == null) return User.NotFound; diff --git a/Application/Features/Users/GetUser/IGetUserCommandHandler.cs b/Application/Features/Users/GetUser/IGetUserCommandHandler.cs deleted file mode 100644 index 3262944..0000000 --- a/Application/Features/Users/GetUser/IGetUserCommandHandler.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Application.Features.Users.GetUser; -using ErrorOr; - -public interface IGetUserCommandHandler -{ - Task> Handle(GetUserCommand command, CancellationToken token); -} \ No newline at end of file diff --git a/Application/Features/Users/RenameUser/IRenameUserCommandHandler.cs b/Application/Features/Users/RenameUser/IRenameUserCommandHandler.cs deleted file mode 100644 index afe9536..0000000 --- a/Application/Features/Users/RenameUser/IRenameUserCommandHandler.cs +++ /dev/null @@ -1,8 +0,0 @@ -using ErrorOr; - -namespace Application.Features.Users.RenameUser; - -public interface IRenameUserCommandHandler -{ - Task> Handle(RenameUserCommand command, CancellationToken token); -} \ No newline at end of file diff --git a/Application/Features/Users/RenameUser/RenameUserCommand.cs b/Application/Features/Users/RenameUser/RenameUserCommand.cs index f40a84e..a06348f 100644 --- a/Application/Features/Users/RenameUser/RenameUserCommand.cs +++ b/Application/Features/Users/RenameUser/RenameUserCommand.cs @@ -1,3 +1,5 @@ -namespace Application.Features.Users.RenameUser; +using Application.Common.Messaging; -public record RenameUserCommand(Guid Id, string Name); \ No newline at end of file +namespace Application.Features.Users.RenameUser; + +public record RenameUserCommand(Guid Id, string Name) : ICommand; \ No newline at end of file diff --git a/Application/Features/Users/RenameUser/RenameUserCommandHandler.cs b/Application/Features/Users/RenameUser/RenameUserCommandHandler.cs index c9b64bf..33428c2 100644 --- a/Application/Features/Users/RenameUser/RenameUserCommandHandler.cs +++ b/Application/Features/Users/RenameUser/RenameUserCommandHandler.cs @@ -1,4 +1,5 @@ -using Application.Data; +using Application.Common.Messaging; +using Application.Data; using Application.Features.Users.Errors; using ErrorOr; using Microsoft.EntityFrameworkCore; @@ -7,9 +8,9 @@ namespace Application.Features.Users.RenameUser; public class RenameUserCommandHandler( IApplicationDbContext dbContext -) : IRenameUserCommandHandler +) : ICommandHandler { - public async Task> Handle(RenameUserCommand command, CancellationToken token) + public async Task> HandleAsync(RenameUserCommand command, CancellationToken token) { var author = await dbContext.Users.FirstOrDefaultAsync(e => e.Id == command.Id, token); diff --git a/WebApi/Features/Users/UsersModule.cs b/WebApi/Features/Users/UsersModule.cs index bdfb942..9abd4ff 100644 --- a/WebApi/Features/Users/UsersModule.cs +++ b/WebApi/Features/Users/UsersModule.cs @@ -1,4 +1,5 @@ using Application.Authorization.Abstractions; +using Application.Common.Messaging; using Application.Features.Users.GetUser; using WebApi.Extensions; @@ -9,10 +10,10 @@ public static class UsersModule public static void AddUsersEndpoints(this IEndpointRouteBuilder app) { app.MapGet("/api/users/me", - async Task (IGetUserCommandHandler getUserCommandHandler, IUserContext userContext, CancellationToken cancellationToken) => + async Task (IQueryHandler getUserCommandHandler, IUserContext userContext, CancellationToken cancellationToken) => { var userId = userContext.UserId; - var response = await getUserCommandHandler.Handle(new GetUserCommand(userId!.Value), cancellationToken); + var response = await getUserCommandHandler.HandleAsync(new GetUserQuery(userId!.Value), cancellationToken); return response.MatchFirst( value => Results.Ok(value), error => error.ToIResult() From ec9e9ed147c200fad751b892d75ab12220f39789 Mon Sep 17 00:00:00 2001 From: VitSun666 <61446535+VitSun666@users.noreply.github.com> Date: Fri, 8 Aug 2025 20:22:33 +0300 Subject: [PATCH 3/6] feat: rename user endpoint --- Domain/Rbac/Permissions.cs | 9 +- Domain/Rbac/Roles.cs | 1 + ...1932_AddedUserRenamePermission.Designer.cs | 612 ++++++++++++++++++ ...0250808171932_AddedUserRenamePermission.cs | 39 ++ .../ApplicationDbContextModelSnapshot.cs | 10 + .../Users/Requests/RenameUserRequest.cs | 3 + WebApi/Features/Users/UsersModule.cs | 32 +- 7 files changed, 703 insertions(+), 3 deletions(-) create mode 100644 Infrastructure/Data/Migrations/20250808171932_AddedUserRenamePermission.Designer.cs create mode 100644 Infrastructure/Data/Migrations/20250808171932_AddedUserRenamePermission.cs create mode 100644 WebApi/Features/Users/Requests/RenameUserRequest.cs diff --git a/Domain/Rbac/Permissions.cs b/Domain/Rbac/Permissions.cs index c75d9c5..925b5e0 100644 --- a/Domain/Rbac/Permissions.cs +++ b/Domain/Rbac/Permissions.cs @@ -58,6 +58,12 @@ public static class Permissions Name = nameof(NavigationsUpdateTree), }; + public static Permission UserRename = new() + { + Id = new Guid("D7A48D11-2A3B-4896-8927-E37A6D1D7DD0"), + Name = nameof(UserRename), + }; + public static IReadOnlyCollection All => [ ArticleCreate, @@ -68,6 +74,7 @@ public static class Permissions ArticleDelete, CategoryCreate, CategoryDelete, - NavigationsUpdateTree + NavigationsUpdateTree, + UserRename ]; } \ No newline at end of file diff --git a/Domain/Rbac/Roles.cs b/Domain/Rbac/Roles.cs index c9f9ca2..5c7c3d9 100644 --- a/Domain/Rbac/Roles.cs +++ b/Domain/Rbac/Roles.cs @@ -50,6 +50,7 @@ public static class Roles Permissions.CategoryCreate, Permissions.CategoryDelete, Permissions.NavigationsUpdateTree, + Permissions.UserRename }; private static readonly IReadOnlyCollection EditorPermissions = new List diff --git a/Infrastructure/Data/Migrations/20250808171932_AddedUserRenamePermission.Designer.cs b/Infrastructure/Data/Migrations/20250808171932_AddedUserRenamePermission.Designer.cs new file mode 100644 index 0000000..1a4ccc1 --- /dev/null +++ b/Infrastructure/Data/Migrations/20250808171932_AddedUserRenamePermission.Designer.cs @@ -0,0 +1,612 @@ +// +using System; +using Infrastructure.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.Data.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20250808171932_AddedUserRenamePermission")] + partial class AddedUserRenamePermission + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.1") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "pg_trgm"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("CategoryRevision", b => + { + b.Property("CategoriesId") + .HasColumnType("character varying(512)"); + + b.Property("RevisionsId") + .HasColumnType("uuid"); + + b.HasKey("CategoriesId", "RevisionsId"); + + b.HasIndex("RevisionsId"); + + b.ToTable("CategoryRevision"); + }); + + modelBuilder.Entity("Domain.Entities.Article", b => + { + b.Property("Id") + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.Property("CurrentRevisionId") + .HasColumnType("uuid"); + + b.Property("RedirectArticleId") + .HasColumnType("character varying(512)"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.HasKey("Id"); + + b.HasIndex("CurrentRevisionId") + .IsUnique(); + + b.HasIndex("RedirectArticleId"); + + b.HasIndex("Title"); + + NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("Title"), "GIST"); + NpgsqlIndexBuilderExtensions.HasOperators(b.HasIndex("Title"), new[] { "gist_trgm_ops" }); + + b.ToTable("Articles"); + }); + + modelBuilder.Entity("Domain.Entities.Category", b => + { + b.Property("Id") + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ParentId") + .HasColumnType("character varying(512)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.ToTable("Categories"); + }); + + modelBuilder.Entity("Domain.Entities.Navigation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Icon") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ParentId") + .HasColumnType("integer"); + + b.Property("Uri") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Weight") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.ToTable("Navigations"); + }); + + modelBuilder.Entity("Domain.Entities.Permission", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Permissions"); + + b.HasData( + new + { + Id = new Guid("5744b9a3-2256-4f69-b6ac-4d8c164632c9"), + Name = "ArticleCreate" + }, + new + { + Id = new Guid("861b96a8-9a42-45e4-b1c2-733ef85bc2d6"), + Name = "ArticleEdit" + }, + new + { + Id = new Guid("7d9bbc60-f1c8-4762-ac1d-edac85f22b48"), + Name = "ArticleSetRedirect" + }, + new + { + Id = new Guid("27c68a58-6f91-41a2-bd36-c4dda391f309"), + Name = "ArticleSeePendingRevisions" + }, + new + { + Id = new Guid("76da2206-a875-47b2-86bb-27ecd0e9f4b9"), + Name = "ArticleReviewRevision" + }, + new + { + Id = new Guid("e4d1aff3-8bc1-4730-b95d-438f1fde6aeb"), + Name = "ArticleDelete" + }, + new + { + Id = new Guid("95571fee-1313-41de-adfe-6b65fe53760b"), + Name = "CategoryCreate" + }, + new + { + Id = new Guid("fa7c5b48-0fb4-4757-96a7-014b00fdd78b"), + Name = "CategoryDelete" + }, + new + { + Id = new Guid("eced433f-b27c-4c6e-af41-d2231fb40f03"), + Name = "NavigationsUpdateTree" + }, + new + { + Id = new Guid("d7a48d11-2a3b-4896-8927-e37a6d1d7dd0"), + Name = "UserRename" + }); + }); + + modelBuilder.Entity("Domain.Entities.Review", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b.Property("ReviewTimestamp") + .HasColumnType("timestamp with time zone"); + + b.Property("ReviewerId") + .HasColumnType("uuid"); + + b.Property("RevisionId") + .HasColumnType("uuid"); + + b.Property("Status") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("ReviewerId"); + + b.HasIndex("RevisionId"); + + b.ToTable("Reviews"); + }); + + modelBuilder.Entity("Domain.Entities.Revision", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ArticleId") + .IsRequired() + .HasColumnType("character varying(512)"); + + b.Property("AuthorId") + .HasColumnType("uuid"); + + b.Property("AuthorsNote") + .IsRequired() + .HasColumnType("text"); + + b.Property("Content") + .IsRequired() + .HasColumnType("text"); + + b.Property("LatestReviewId") + .HasColumnType("uuid"); + + b.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("ArticleId"); + + b.HasIndex("AuthorId"); + + b.HasIndex("LatestReviewId") + .IsUnique(); + + b.ToTable("Revisions"); + }); + + modelBuilder.Entity("Domain.Entities.Role", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Roles"); + + b.HasData( + new + { + Id = new Guid("ca2cfe04-24ed-42d0-9237-6d5ed7885063"), + Name = "Admin" + }, + new + { + Id = new Guid("e1d0ad14-fb96-4488-bf64-8aab2d1ef43d"), + Name = "Editor" + }, + new + { + Id = new Guid("3a7651c5-2cf1-4ed6-9866-c6bac6e8f6dd"), + Name = "User" + }, + new + { + Id = new Guid("2afacdee-55a4-4e23-9f66-2ca5a5af9751"), + Name = "Lurker" + }); + }); + + modelBuilder.Entity("Domain.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ExternalId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ExternalId"); + + b.HasIndex("Name"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("PermissionRole", b => + { + b.Property("PermissionsId") + .HasColumnType("uuid"); + + b.Property("RolesId") + .HasColumnType("uuid"); + + b.HasKey("PermissionsId", "RolesId"); + + b.HasIndex("RolesId"); + + b.ToTable("RolePermission", (string)null); + + b.HasData( + new + { + PermissionsId = new Guid("5744b9a3-2256-4f69-b6ac-4d8c164632c9"), + RolesId = new Guid("ca2cfe04-24ed-42d0-9237-6d5ed7885063") + }, + new + { + PermissionsId = new Guid("e4d1aff3-8bc1-4730-b95d-438f1fde6aeb"), + RolesId = new Guid("ca2cfe04-24ed-42d0-9237-6d5ed7885063") + }, + new + { + PermissionsId = new Guid("861b96a8-9a42-45e4-b1c2-733ef85bc2d6"), + RolesId = new Guid("ca2cfe04-24ed-42d0-9237-6d5ed7885063") + }, + new + { + PermissionsId = new Guid("27c68a58-6f91-41a2-bd36-c4dda391f309"), + RolesId = new Guid("ca2cfe04-24ed-42d0-9237-6d5ed7885063") + }, + new + { + PermissionsId = new Guid("76da2206-a875-47b2-86bb-27ecd0e9f4b9"), + RolesId = new Guid("ca2cfe04-24ed-42d0-9237-6d5ed7885063") + }, + new + { + PermissionsId = new Guid("7d9bbc60-f1c8-4762-ac1d-edac85f22b48"), + RolesId = new Guid("ca2cfe04-24ed-42d0-9237-6d5ed7885063") + }, + new + { + PermissionsId = new Guid("95571fee-1313-41de-adfe-6b65fe53760b"), + RolesId = new Guid("ca2cfe04-24ed-42d0-9237-6d5ed7885063") + }, + new + { + PermissionsId = new Guid("fa7c5b48-0fb4-4757-96a7-014b00fdd78b"), + RolesId = new Guid("ca2cfe04-24ed-42d0-9237-6d5ed7885063") + }, + new + { + PermissionsId = new Guid("eced433f-b27c-4c6e-af41-d2231fb40f03"), + RolesId = new Guid("ca2cfe04-24ed-42d0-9237-6d5ed7885063") + }, + new + { + PermissionsId = new Guid("d7a48d11-2a3b-4896-8927-e37a6d1d7dd0"), + RolesId = new Guid("ca2cfe04-24ed-42d0-9237-6d5ed7885063") + }, + new + { + PermissionsId = new Guid("5744b9a3-2256-4f69-b6ac-4d8c164632c9"), + RolesId = new Guid("e1d0ad14-fb96-4488-bf64-8aab2d1ef43d") + }, + new + { + PermissionsId = new Guid("e4d1aff3-8bc1-4730-b95d-438f1fde6aeb"), + RolesId = new Guid("e1d0ad14-fb96-4488-bf64-8aab2d1ef43d") + }, + new + { + PermissionsId = new Guid("861b96a8-9a42-45e4-b1c2-733ef85bc2d6"), + RolesId = new Guid("e1d0ad14-fb96-4488-bf64-8aab2d1ef43d") + }, + new + { + PermissionsId = new Guid("27c68a58-6f91-41a2-bd36-c4dda391f309"), + RolesId = new Guid("e1d0ad14-fb96-4488-bf64-8aab2d1ef43d") + }, + new + { + PermissionsId = new Guid("76da2206-a875-47b2-86bb-27ecd0e9f4b9"), + RolesId = new Guid("e1d0ad14-fb96-4488-bf64-8aab2d1ef43d") + }, + new + { + PermissionsId = new Guid("7d9bbc60-f1c8-4762-ac1d-edac85f22b48"), + RolesId = new Guid("e1d0ad14-fb96-4488-bf64-8aab2d1ef43d") + }, + new + { + PermissionsId = new Guid("95571fee-1313-41de-adfe-6b65fe53760b"), + RolesId = new Guid("e1d0ad14-fb96-4488-bf64-8aab2d1ef43d") + }, + new + { + PermissionsId = new Guid("fa7c5b48-0fb4-4757-96a7-014b00fdd78b"), + RolesId = new Guid("e1d0ad14-fb96-4488-bf64-8aab2d1ef43d") + }, + new + { + PermissionsId = new Guid("eced433f-b27c-4c6e-af41-d2231fb40f03"), + RolesId = new Guid("e1d0ad14-fb96-4488-bf64-8aab2d1ef43d") + }, + new + { + PermissionsId = new Guid("5744b9a3-2256-4f69-b6ac-4d8c164632c9"), + RolesId = new Guid("3a7651c5-2cf1-4ed6-9866-c6bac6e8f6dd") + }, + new + { + PermissionsId = new Guid("861b96a8-9a42-45e4-b1c2-733ef85bc2d6"), + RolesId = new Guid("3a7651c5-2cf1-4ed6-9866-c6bac6e8f6dd") + }); + }); + + modelBuilder.Entity("RoleUser", b => + { + b.Property("RolesId") + .HasColumnType("uuid"); + + b.Property("UsersId") + .HasColumnType("uuid"); + + b.HasKey("RolesId", "UsersId"); + + b.HasIndex("UsersId"); + + b.ToTable("RoleUser"); + }); + + modelBuilder.Entity("CategoryRevision", b => + { + b.HasOne("Domain.Entities.Category", null) + .WithMany() + .HasForeignKey("CategoriesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Revision", null) + .WithMany() + .HasForeignKey("RevisionsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Domain.Entities.Article", b => + { + b.HasOne("Domain.Entities.Revision", "CurrentRevision") + .WithOne() + .HasForeignKey("Domain.Entities.Article", "CurrentRevisionId"); + + b.HasOne("Domain.Entities.Article", "RedirectArticle") + .WithMany() + .HasForeignKey("RedirectArticleId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("CurrentRevision"); + + b.Navigation("RedirectArticle"); + }); + + modelBuilder.Entity("Domain.Entities.Category", b => + { + b.HasOne("Domain.Entities.Category", "Parent") + .WithMany("SubCategories") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Domain.Entities.Navigation", b => + { + b.HasOne("Domain.Entities.Navigation", "Parent") + .WithMany("Children") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Domain.Entities.Review", b => + { + b.HasOne("Domain.Entities.User", "Reviewer") + .WithMany() + .HasForeignKey("ReviewerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Revision", "Revision") + .WithMany("Reviews") + .HasForeignKey("RevisionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Reviewer"); + + b.Navigation("Revision"); + }); + + modelBuilder.Entity("Domain.Entities.Revision", b => + { + b.HasOne("Domain.Entities.Article", "Article") + .WithMany("Revisions") + .HasForeignKey("ArticleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.User", "Author") + .WithMany() + .HasForeignKey("AuthorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Review", "LatestReview") + .WithOne() + .HasForeignKey("Domain.Entities.Revision", "LatestReviewId"); + + b.Navigation("Article"); + + b.Navigation("Author"); + + b.Navigation("LatestReview"); + }); + + modelBuilder.Entity("PermissionRole", b => + { + b.HasOne("Domain.Entities.Permission", null) + .WithMany() + .HasForeignKey("PermissionsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.Role", null) + .WithMany() + .HasForeignKey("RolesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("RoleUser", b => + { + b.HasOne("Domain.Entities.Role", null) + .WithMany() + .HasForeignKey("RolesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entities.User", null) + .WithMany() + .HasForeignKey("UsersId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Domain.Entities.Article", b => + { + b.Navigation("Revisions"); + }); + + modelBuilder.Entity("Domain.Entities.Category", b => + { + b.Navigation("SubCategories"); + }); + + modelBuilder.Entity("Domain.Entities.Navigation", b => + { + b.Navigation("Children"); + }); + + modelBuilder.Entity("Domain.Entities.Revision", b => + { + b.Navigation("Reviews"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Infrastructure/Data/Migrations/20250808171932_AddedUserRenamePermission.cs b/Infrastructure/Data/Migrations/20250808171932_AddedUserRenamePermission.cs new file mode 100644 index 0000000..51e3538 --- /dev/null +++ b/Infrastructure/Data/Migrations/20250808171932_AddedUserRenamePermission.cs @@ -0,0 +1,39 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Infrastructure.Data.Migrations +{ + /// + public partial class AddedUserRenamePermission : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.InsertData( + table: "Permissions", + columns: new[] { "Id", "Name" }, + values: new object[] { new Guid("d7a48d11-2a3b-4896-8927-e37a6d1d7dd0"), "UserRename" }); + + migrationBuilder.InsertData( + table: "RolePermission", + columns: new[] { "PermissionsId", "RolesId" }, + values: new object[] { new Guid("d7a48d11-2a3b-4896-8927-e37a6d1d7dd0"), new Guid("ca2cfe04-24ed-42d0-9237-6d5ed7885063") }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DeleteData( + table: "RolePermission", + keyColumns: new[] { "PermissionsId", "RolesId" }, + keyValues: new object[] { new Guid("d7a48d11-2a3b-4896-8927-e37a6d1d7dd0"), new Guid("ca2cfe04-24ed-42d0-9237-6d5ed7885063") }); + + migrationBuilder.DeleteData( + table: "Permissions", + keyColumn: "Id", + keyValue: new Guid("d7a48d11-2a3b-4896-8927-e37a6d1d7dd0")); + } + } +} diff --git a/Infrastructure/Data/Migrations/ApplicationDbContextModelSnapshot.cs b/Infrastructure/Data/Migrations/ApplicationDbContextModelSnapshot.cs index a84d7dd..13255b5 100644 --- a/Infrastructure/Data/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/Infrastructure/Data/Migrations/ApplicationDbContextModelSnapshot.cs @@ -184,6 +184,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) { Id = new Guid("eced433f-b27c-4c6e-af41-d2231fb40f03"), Name = "NavigationsUpdateTree" + }, + new + { + Id = new Guid("d7a48d11-2a3b-4896-8927-e37a6d1d7dd0"), + Name = "UserRename" }); }); @@ -378,6 +383,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) RolesId = new Guid("ca2cfe04-24ed-42d0-9237-6d5ed7885063") }, new + { + PermissionsId = new Guid("d7a48d11-2a3b-4896-8927-e37a6d1d7dd0"), + RolesId = new Guid("ca2cfe04-24ed-42d0-9237-6d5ed7885063") + }, + new { PermissionsId = new Guid("5744b9a3-2256-4f69-b6ac-4d8c164632c9"), RolesId = new Guid("e1d0ad14-fb96-4488-bf64-8aab2d1ef43d") diff --git a/WebApi/Features/Users/Requests/RenameUserRequest.cs b/WebApi/Features/Users/Requests/RenameUserRequest.cs new file mode 100644 index 0000000..681a319 --- /dev/null +++ b/WebApi/Features/Users/Requests/RenameUserRequest.cs @@ -0,0 +1,3 @@ +namespace WebApi.Features.Users.Requests; + +public record RenameUserRequest(string Name); \ No newline at end of file diff --git a/WebApi/Features/Users/UsersModule.cs b/WebApi/Features/Users/UsersModule.cs index 9abd4ff..72cca83 100644 --- a/WebApi/Features/Users/UsersModule.cs +++ b/WebApi/Features/Users/UsersModule.cs @@ -1,7 +1,11 @@ using Application.Authorization.Abstractions; using Application.Common.Messaging; using Application.Features.Users.GetUser; +using Application.Features.Users.RenameUser; +using Domain.Rbac; +using WebApi.Auth; using WebApi.Extensions; +using WebApi.Features.Users.Requests; namespace WebApi.Features.Users; @@ -10,10 +14,10 @@ public static class UsersModule public static void AddUsersEndpoints(this IEndpointRouteBuilder app) { app.MapGet("/api/users/me", - async Task (IQueryHandler getUserCommandHandler, IUserContext userContext, CancellationToken cancellationToken) => + async Task (IQueryHandler getUserQueryHandler, IUserContext userContext, CancellationToken cancellationToken) => { var userId = userContext.UserId; - var response = await getUserCommandHandler.HandleAsync(new GetUserQuery(userId!.Value), cancellationToken); + var response = await getUserQueryHandler.HandleAsync(new GetUserQuery(userId!.Value), cancellationToken); return response.MatchFirst( value => Results.Ok(value), error => error.ToIResult() @@ -22,7 +26,31 @@ async Task (IQueryHandler getUserCommand .WithName("GetUser") .WithTags("Users") .RequireAuthorization() + .ProducesProblem(StatusCodes.Status401Unauthorized) .Produces() .WithOpenApi(); + + app.MapPut("/api/users/{id}", + async Task ( + Guid id, + ICommandHandler commandHandler, + RenameUserRequest request, + CancellationToken cancellationToken) => + { + var command = new RenameUserCommand(id, request.Name); + var result = await commandHandler.HandleAsync(command, cancellationToken); + return result.MatchFirst( + value => Results.Ok(value), + error => error.ToIResult() + ); + }) + .WithName("RenameUser") + .WithTags("Users") + .Produces() + .ProducesProblem(StatusCodes.Status401Unauthorized) + .ProducesProblem(StatusCodes.Status403Forbidden) + .ProducesProblem(StatusCodes.Status404NotFound) + .RequirePermission(Permissions.UserRename) + .WithOpenApi(); } } \ No newline at end of file From e3281d8802cf635e134c08bb7f5d3ab758ea35d5 Mon Sep 17 00:00:00 2001 From: VitSun666 <61446535+VitSun666@users.noreply.github.com> Date: Sat, 9 Aug 2025 01:05:29 +0300 Subject: [PATCH 4/6] test: fix CreateArticleCommandHandlerTests --- .../Articles/CreateArticleCommandHandlerTests.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Application.Tests.Unit/Articles/CreateArticleCommandHandlerTests.cs b/Application.Tests.Unit/Articles/CreateArticleCommandHandlerTests.cs index f5d430e..f1b2da8 100644 --- a/Application.Tests.Unit/Articles/CreateArticleCommandHandlerTests.cs +++ b/Application.Tests.Unit/Articles/CreateArticleCommandHandlerTests.cs @@ -134,6 +134,7 @@ public async void Handle_Should_CallSaveChangesAsync_WhenGeneratedIdIsUnique() _mockDbContext.Setup(x => x.Articles).Returns(mockArticlesDbSet.Object); _mockDbContext.Setup(x => x.Categories).Returns(mockCategoriesDbSet.Object); _mockDbContext.Setup(x => x.Revisions).Returns(mockRevisionsDbSet.Object); + _mockUserContext.Setup(x => x.UserId).Returns(Guid.Empty); _mockSlugHelper.Setup(x => x.GenerateSlug("something extra cool")).Returns("something-extra-cool"); _mockValidator.Setup(v => v.Validate(It.IsAny())) .Returns(new ValidationResult()); @@ -163,6 +164,7 @@ public async void Handle_Should_ReturnSuccessResultWithArticleId_WhenGeneratedId _mockDbContext.Setup(x => x.Articles).Returns(mockArticlesDbSet.Object); _mockDbContext.Setup(x => x.Categories).Returns(mockCategoriesDbSet.Object); _mockDbContext.Setup(x => x.Revisions).Returns(mockRevisionsDbSet.Object); + _mockUserContext.Setup(x => x.UserId).Returns(Guid.Empty); _mockSlugHelper.Setup(x => x.GenerateSlug("something extra cool")).Returns("something-extra-cool"); _mockValidator.Setup(v => v.Validate(It.IsAny())) .Returns(new ValidationResult()); @@ -205,8 +207,8 @@ public async void Handle_Should_CreateRevisionWithContent_WhenGeneratedIdIsUniqu _mockDbContext.Setup(x => x.Articles).Returns(mockArticlesDbSet.Object); _mockDbContext.Setup(x => x.Categories).Returns(mockCategoriesDbSet.Object); _mockDbContext.Setup(x => x.Revisions).Returns(mockRevisionsDbSet.Object); - _mockSlugHelper.Setup(x => x.GenerateSlug("something extra cool")).Returns("something-extra-cool"); _mockUserContext.Setup(x => x.UserId).Returns(Guid.Empty); + _mockSlugHelper.Setup(x => x.GenerateSlug("something extra cool")).Returns("something-extra-cool"); _mockValidator.Setup(v => v.Validate(It.IsAny())) .Returns(new ValidationResult()); var expectedCategoryIds = new List {"category1", "category2"}; From 0d8615d8d1b94dec7244b2c30a553a01cd01546c Mon Sep 17 00:00:00 2001 From: VitSun666 <61446535+VitSun666@users.noreply.github.com> Date: Sat, 9 Aug 2025 22:13:15 +0300 Subject: [PATCH 5/6] chore: typo --- Application/Authorization/Abstractions/IPermissionService.cs | 2 +- Application/Authorization/PermissionService.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Application/Authorization/Abstractions/IPermissionService.cs b/Application/Authorization/Abstractions/IPermissionService.cs index c3f858a..22dcf95 100644 --- a/Application/Authorization/Abstractions/IPermissionService.cs +++ b/Application/Authorization/Abstractions/IPermissionService.cs @@ -8,5 +8,5 @@ public interface IPermissionService Task HasPermissionAsync(Guid userId, Permission permission); Task HasAnyPermissionsAsync(Guid userId, IEnumerable permissionIds); Task HasAnyPermissionsAsync(Guid userId, IEnumerable permissions); - Task> GetPermissionsASync(Guid userId); + Task> GetPermissionsAsync(Guid userId); } \ No newline at end of file diff --git a/Application/Authorization/PermissionService.cs b/Application/Authorization/PermissionService.cs index 5400f7d..93662f5 100644 --- a/Application/Authorization/PermissionService.cs +++ b/Application/Authorization/PermissionService.cs @@ -14,7 +14,7 @@ public PermissionService(IApplicationDbContext dbContext) _dbContext = dbContext; } - public async Task> GetPermissionsASync(Guid userId) + public async Task> GetPermissionsAsync(Guid userId) { return await _dbContext.Users .Where(a => a.Id == userId) From c89f39dd3885d8dd0d7eb6798c8b36248ebef069 Mon Sep 17 00:00:00 2001 From: VitSun666 <61446535+VitSun666@users.noreply.github.com> Date: Sat, 9 Aug 2025 22:46:37 +0300 Subject: [PATCH 6/6] feat: get authority from config in swagger oauth auth --- WebApi/Extensions/ServiceCollectionExtensions.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/WebApi/Extensions/ServiceCollectionExtensions.cs b/WebApi/Extensions/ServiceCollectionExtensions.cs index f4f37c7..b540355 100644 --- a/WebApi/Extensions/ServiceCollectionExtensions.cs +++ b/WebApi/Extensions/ServiceCollectionExtensions.cs @@ -1,5 +1,6 @@ using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.OpenApi.Models; +using WebApi.Auth; using WebApi.SchemaFilters; namespace WebApi.Extensions; @@ -13,6 +14,8 @@ internal static void RegisterSwagger(this IServiceCollection services, IConfigur options.CustomSchemaIds(x => x.FullName?.Replace("+", ".").Replace(x.Namespace + ".", "")); options.SupportNonNullableReferenceTypes(); options.SchemaFilter(); + var jwtSettings = configuration.GetRequiredSection(JwtSettings.SectionName).Get() + ?? throw new InvalidOperationException($"Missing {JwtSettings.SectionName} section"); var securityScheme = new OpenApiSecurityScheme { @@ -28,16 +31,14 @@ internal static void RegisterSwagger(this IServiceCollection services, IConfigur //TODO awful hack .Replace("host.docker.internal", "localhost") for ease of testing Implicit = new OpenApiOAuthFlow { - //TODO: Hardcoded values should be extracted to config - AuthorizationUrl = new Uri($"{"http://localhost:8091/realms/dumb realm".Replace("host.docker.internal", "localhost")}/protocol/openid-connect/auth"), - TokenUrl = new Uri($"{"http://localhost:8091/realms/dumb realm".Replace("host.docker.internal", "localhost")}/protocol/openid-connect/token"), + AuthorizationUrl = new Uri($"{jwtSettings.Authority.Replace("host.docker.internal", "localhost")}/protocol/openid-connect/auth"), + TokenUrl = new Uri($"{jwtSettings.Authority.Replace("host.docker.internal", "localhost")}/protocol/openid-connect/token"), Scopes = new Dictionary() }, AuthorizationCode = new OpenApiOAuthFlow { - //TODO: Hardcoded values should be extracted to config - AuthorizationUrl = new Uri($"{"http://localhost:8091/realms/dumb realm".Replace("host.docker.internal", "localhost")}/protocol/openid-connect/auth"), - TokenUrl = new Uri($"{"http://localhost:8091/realms/dumb realm".Replace("host.docker.internal", "localhost")}/protocol/openid-connect/token"), + AuthorizationUrl = new Uri($"{jwtSettings.Authority.Replace("host.docker.internal", "localhost")}/protocol/openid-connect/auth"), + TokenUrl = new Uri($"{jwtSettings.Authority.Replace("host.docker.internal", "localhost")}/protocol/openid-connect/token"), Scopes = new Dictionary() } }