diff --git a/services/Directory/FilterLists.Directory.Application/Commands/CreateList.cs b/services/Directory/FilterLists.Directory.Application/Commands/CreateList.cs index b7df3e9be..341ca74b2 100644 --- a/services/Directory/FilterLists.Directory.Application/Commands/CreateList.cs +++ b/services/Directory/FilterLists.Directory.Application/Commands/CreateList.cs @@ -1,6 +1,6 @@ -using FilterLists.Directory.Domain.Aggregates; -using FilterLists.Directory.Domain.Aggregates.Changes; -using FilterLists.Directory.Domain.Aggregates.Licenses; +using FilterLists.Directory.Domain.Aggregates.Changes; +using FilterLists.Directory.Domain.Aggregates.FilterLists; +using FilterLists.Directory.Infrastructure.Persistence.Commands.Context; using FluentValidation; using MediatR; @@ -9,6 +9,7 @@ namespace FilterLists.Directory.Application.Commands; public static class CreateList { public record Command(string Name, + ICollection ViewUrls, string? Description = default, int? LicenseId = default, Uri? HomeUrl = default, @@ -19,7 +20,8 @@ public record Command(string Name, Uri? ForumUrl = default, Uri? ChatUrl = default, string? EmailAddress = default, - Uri? DonateUrl = default) : IRequest; + Uri? DonateUrl = default, + string? ChangeReason = default) : IRequest; internal class Validator : AbstractValidator { @@ -33,24 +35,23 @@ public Validator() internal class Handler : IRequestHandler { - private readonly IChangeRepository _changeRepo; - private readonly ILicenseRepository _licenseRepo; + private readonly ICommandContext _commandContext; - public Handler(IChangeRepository changeRepo, ILicenseRepository licenseRepo) + public Handler(ICommandContext commandContext) { - _changeRepo = changeRepo; - _licenseRepo = licenseRepo; + _commandContext = commandContext; } public async Task Handle(Command request, CancellationToken cancellationToken) { var license = request.LicenseId != null - ? await _licenseRepo.GetByIdAsync(request.LicenseId.Value, cancellationToken) ?? - throw new ArgumentException($"LicenseId {request.LicenseId} not found.", - nameof(request.LicenseId)) - : null; + ? await _commandContext.Licenses.FindAsync(new object[] { request.LicenseId.Value }, + cancellationToken) ?? throw new ArgumentException($"LicenseId {request.LicenseId} not found.", + nameof(request.LicenseId)) + : default; - var filterList = new FilterList(request.Name, + var filterList = FilterList.Create( + request.Name, request.Description, license, request.HomeUrl, @@ -61,14 +62,16 @@ public async Task Handle(Command request, CancellationToken cancellati request.ForumUrl, request.ChatUrl, request.EmailAddress, - request.DonateUrl); + request.DonateUrl, + request.ViewUrls); + _commandContext.FilterLists.Add(filterList); - //Change change = null; + var change = Change.CreateFilterList(filterList, request.ChangeReason); + _commandContext.Changes.Add(change); - //await _changeRepo.AddAsync(change, cancellationToken); - //return new Response(); + await _commandContext.SaveChangesAsync(cancellationToken); - throw new NotImplementedException(); + return new Response(); } } diff --git a/services/Directory/FilterLists.Directory.Domain/Aggregates/Changes/Change.cs b/services/Directory/FilterLists.Directory.Domain/Aggregates/Changes/Change.cs index a8421ceab..0a03e8eb7 100644 --- a/services/Directory/FilterLists.Directory.Domain/Aggregates/Changes/Change.cs +++ b/services/Directory/FilterLists.Directory.Domain/Aggregates/Changes/Change.cs @@ -1,5 +1,5 @@ using System.Text.Json; -using FilterLists.Directory.Domain.Aggregates.Licenses; +using FilterLists.Directory.Domain.Aggregates.FilterLists; namespace FilterLists.Directory.Domain.Aggregates.Changes; @@ -9,26 +9,40 @@ private Change() { } - public Change(ChangeType type) - { - Type = type; - } - public ChangeType Type { get; init; } + + // TODO: push json handling from Domain to Infrastructure public JsonDocument? AggregateBefore { get; init; } public JsonDocument? AggregateAfter { get; init; } - public DateTime CreatedAt { get; init; } - public string? CreatedReason { get; init; } + public string? Reason { get; init; } + public DateTime SubmittedAt { get; init; } = DateTime.UtcNow; public DateTime? AppliedAt { get; init; } public DateTime? RejectedAt { get; init; } public string? RejectedReason { get; init; } - + //public int? FilterListId { get; init; } public FilterList? FilterList { get; init; } + //public string? LanguageIso6391 { get; init; } //public Language? Language { get; } - public License? License { get; init; } + //public int? LicenseId { get; init; } + //public License? License { get; } + //public int? MaintainerId { get; init; } //public Maintainer? Maintainer { get; } + //public int? SoftwareId { get; init; } //public Software? Software { get; } + //public int? SyntaxId { get; init; } //public Syntax? Syntax { get; } + //public int? TagId { get; init; } //public Tag? Tag { get; } + + public static Change CreateFilterList(FilterList filterList, string? reason) + { + return new Change + { + Type = ChangeType.Create, + AggregateAfter = JsonSerializer.SerializeToDocument(filterList), + Reason = reason, + FilterList = filterList + }; + } } diff --git a/services/Directory/FilterLists.Directory.Domain/Aggregates/Changes/ChangeType.cs b/services/Directory/FilterLists.Directory.Domain/Aggregates/Changes/ChangeType.cs index a703aa664..043c5e4de 100644 --- a/services/Directory/FilterLists.Directory.Domain/Aggregates/Changes/ChangeType.cs +++ b/services/Directory/FilterLists.Directory.Domain/Aggregates/Changes/ChangeType.cs @@ -2,7 +2,7 @@ public enum ChangeType { - Insert, + Create, Update, Delete } diff --git a/services/Directory/FilterLists.Directory.Domain/Aggregates/Changes/IChangeRepository.cs b/services/Directory/FilterLists.Directory.Domain/Aggregates/Changes/IChangeRepository.cs deleted file mode 100644 index f066bf5c3..000000000 --- a/services/Directory/FilterLists.Directory.Domain/Aggregates/Changes/IChangeRepository.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace FilterLists.Directory.Domain.Aggregates.Changes; - -public interface IChangeRepository -{ - Task AddAsync(Change change, CancellationToken cancellationToken); -} diff --git a/services/Directory/FilterLists.Directory.Domain/Aggregates/FilterList.cs b/services/Directory/FilterLists.Directory.Domain/Aggregates/FilterLists/FilterList.cs similarity index 50% rename from services/Directory/FilterLists.Directory.Domain/Aggregates/FilterList.cs rename to services/Directory/FilterLists.Directory.Domain/Aggregates/FilterLists/FilterList.cs index 05e4858de..95c636f93 100644 --- a/services/Directory/FilterLists.Directory.Domain/Aggregates/FilterList.cs +++ b/services/Directory/FilterLists.Directory.Domain/Aggregates/FilterLists/FilterList.cs @@ -1,6 +1,6 @@ using FilterLists.Directory.Domain.Aggregates.Licenses; -namespace FilterLists.Directory.Domain.Aggregates; +namespace FilterLists.Directory.Domain.Aggregates.FilterLists; public class FilterList { @@ -8,33 +8,6 @@ private FilterList() { } - public FilterList(string name, - string? description, - License? license, - Uri? homeUrl, - Uri? onionUrl, - Uri? policyUrl, - Uri? submissionUrl, - Uri? issuesUrl, - Uri? forumUrl, - Uri? chatUrl, - string? emailAddress, - Uri? donateUrl) - { - Name = name; - Description = description; - License = license; - HomeUrl = homeUrl; - OnionUrl = onionUrl; - PolicyUrl = policyUrl; - SubmissionUrl = submissionUrl; - IssuesUrl = issuesUrl; - ForumUrl = forumUrl; - ChatUrl = chatUrl; - EmailAddress = emailAddress; - DonateUrl = donateUrl; - } - public string Name { get; private init; } = null!; public string? Description { get; private init; } public License? License { get; private init; } @@ -47,4 +20,44 @@ public FilterList(string name, public Uri? ChatUrl { get; private init; } public string? EmailAddress { get; private init; } public Uri? DonateUrl { get; private init; } + public IEnumerable ViewUrls { get; init; } = new HashSet(); + + public static FilterList Create( + string name, + string? description, + License? license, + Uri? homeUrl, + Uri? onionUrl, + Uri? policyUrl, + Uri? submissionUrl, + Uri? issuesUrl, + Uri? forumUrl, + Uri? chatUrl, + string? emailAddress, + Uri? donateUrl, + ICollection viewUrls) + { + if (viewUrls.Count == 0) + { + // TODO: create and handle DomainExceptions + throw new ArgumentException("At lest one view URL is required", nameof(viewUrls)); + } + + return new FilterList + { + Name = name, + Description = description, + License = license, + HomeUrl = homeUrl, + OnionUrl = onionUrl, + PolicyUrl = policyUrl, + SubmissionUrl = submissionUrl, + IssuesUrl = issuesUrl, + ForumUrl = forumUrl, + ChatUrl = chatUrl, + EmailAddress = emailAddress, + DonateUrl = donateUrl, + ViewUrls = viewUrls + }; + } } diff --git a/services/Directory/FilterLists.Directory.Domain/Aggregates/FilterLists/FilterListViewUrl.cs b/services/Directory/FilterLists.Directory.Domain/Aggregates/FilterLists/FilterListViewUrl.cs new file mode 100644 index 000000000..fa4a282d5 --- /dev/null +++ b/services/Directory/FilterLists.Directory.Domain/Aggregates/FilterLists/FilterListViewUrl.cs @@ -0,0 +1,8 @@ +namespace FilterLists.Directory.Domain.Aggregates.FilterLists; + +public class FilterListViewUrl +{ + public short SegmentNumber { get; init; } + public short Primariness { get; init; } + public Uri Url { get; init; } = null!; +} diff --git a/services/Directory/FilterLists.Directory.Domain/Aggregates/Licenses/ILicenseRepository.cs b/services/Directory/FilterLists.Directory.Domain/Aggregates/Licenses/ILicenseRepository.cs deleted file mode 100644 index cc663f0f7..000000000 --- a/services/Directory/FilterLists.Directory.Domain/Aggregates/Licenses/ILicenseRepository.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace FilterLists.Directory.Domain.Aggregates.Licenses; - -public interface ILicenseRepository -{ - ValueTask GetByIdAsync(int id, CancellationToken cancellationToken); -} diff --git a/services/Directory/FilterLists.Directory.Domain/Aggregates/Licenses/License.cs b/services/Directory/FilterLists.Directory.Domain/Aggregates/Licenses/License.cs index 3fd3dc0bc..dc138149e 100644 --- a/services/Directory/FilterLists.Directory.Domain/Aggregates/Licenses/License.cs +++ b/services/Directory/FilterLists.Directory.Domain/Aggregates/Licenses/License.cs @@ -6,19 +6,6 @@ private License() { } - public License(string name, - Uri? url, - bool? permitsModification, - bool? permitsDistribution, - bool? permitsCommercialUse) - { - Name = name; - Url = url; - PermitsModification = permitsModification ?? false; - PermitsDistribution = permitsDistribution ?? false; - PermitsCommercialUse = permitsCommercialUse ?? false; - } - public string Name { get; private init; } = null!; public Uri? Url { get; private init; } public bool PermitsModification { get; private init; } diff --git a/services/Directory/FilterLists.Directory.Infrastructure/ConfigurationExtensions.cs b/services/Directory/FilterLists.Directory.Infrastructure/ConfigurationExtensions.cs index d2e8355de..f78ddcb1c 100644 --- a/services/Directory/FilterLists.Directory.Infrastructure/ConfigurationExtensions.cs +++ b/services/Directory/FilterLists.Directory.Infrastructure/ConfigurationExtensions.cs @@ -1,5 +1,4 @@ using FilterLists.Directory.Infrastructure.Persistence.Commands.Context; -using FilterLists.Directory.Infrastructure.Persistence.Commands.Repositories; using FilterLists.Directory.Infrastructure.Persistence.Queries.Context; using FilterLists.SharedKernel.Logging; using Microsoft.AspNetCore.Builder; @@ -47,10 +46,6 @@ public static void AddInfrastructure(this IServiceCollection services, IConfigur }); services.AddScoped(); services.AddScoped(); - services.Scan(s => s.FromAssembliesOf(typeof(ConfigurationExtensions)) - .AddClasses(f => f.InNamespaceOf(), false) - .AsImplementedInterfaces() - .WithScopedLifetime()); } public static void UseInfrastructure(this IApplicationBuilder app) diff --git a/services/Directory/FilterLists.Directory.Infrastructure/FilterLists.Directory.Infrastructure.csproj b/services/Directory/FilterLists.Directory.Infrastructure/FilterLists.Directory.Infrastructure.csproj index d345735da..7d82668d4 100644 --- a/services/Directory/FilterLists.Directory.Infrastructure/FilterLists.Directory.Infrastructure.csproj +++ b/services/Directory/FilterLists.Directory.Infrastructure/FilterLists.Directory.Infrastructure.csproj @@ -20,7 +20,6 @@ - diff --git a/services/Directory/FilterLists.Directory.Infrastructure/Persistence/Commands/Context/CommandDbContext.cs b/services/Directory/FilterLists.Directory.Infrastructure/Persistence/Commands/Context/CommandDbContext.cs index fa1c79186..91c11e9a1 100644 --- a/services/Directory/FilterLists.Directory.Infrastructure/Persistence/Commands/Context/CommandDbContext.cs +++ b/services/Directory/FilterLists.Directory.Infrastructure/Persistence/Commands/Context/CommandDbContext.cs @@ -1,4 +1,5 @@ -using FilterLists.Directory.Domain.Aggregates; +using FilterLists.Directory.Domain.Aggregates.Changes; +using FilterLists.Directory.Domain.Aggregates.FilterLists; using FilterLists.Directory.Domain.Aggregates.Licenses; using FilterLists.Directory.Infrastructure.Persistence.Commands.EntityTypeConfigurations; using Microsoft.EntityFrameworkCore; @@ -11,6 +12,7 @@ public CommandDbContext(DbContextOptions options) : base(optio { } + public DbSet Changes => Set(); public DbSet FilterLists => Set(); public DbSet Licenses => Set(); diff --git a/services/Directory/FilterLists.Directory.Infrastructure/Persistence/Commands/Context/ICommandContext.cs b/services/Directory/FilterLists.Directory.Infrastructure/Persistence/Commands/Context/ICommandContext.cs index 53dd0453f..e977e02e8 100644 --- a/services/Directory/FilterLists.Directory.Infrastructure/Persistence/Commands/Context/ICommandContext.cs +++ b/services/Directory/FilterLists.Directory.Infrastructure/Persistence/Commands/Context/ICommandContext.cs @@ -1,11 +1,14 @@ -using FilterLists.Directory.Domain.Aggregates; +using FilterLists.Directory.Domain.Aggregates.Changes; +using FilterLists.Directory.Domain.Aggregates.FilterLists; using FilterLists.Directory.Domain.Aggregates.Licenses; using Microsoft.EntityFrameworkCore; namespace FilterLists.Directory.Infrastructure.Persistence.Commands.Context; -internal interface ICommandContext +public interface ICommandContext { + DbSet Changes { get; } DbSet FilterLists { get; } DbSet Licenses { get; } + Task SaveChangesAsync(CancellationToken cancellationToken); } diff --git a/services/Directory/FilterLists.Directory.Infrastructure/Persistence/Commands/EntityTypeConfigurations/ChangeTypeConfiguration.cs b/services/Directory/FilterLists.Directory.Infrastructure/Persistence/Commands/EntityTypeConfigurations/ChangeTypeConfiguration.cs new file mode 100644 index 000000000..b8b62c854 --- /dev/null +++ b/services/Directory/FilterLists.Directory.Infrastructure/Persistence/Commands/EntityTypeConfigurations/ChangeTypeConfiguration.cs @@ -0,0 +1,13 @@ +using FilterLists.Directory.Domain.Aggregates.Changes; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace FilterLists.Directory.Infrastructure.Persistence.Commands.EntityTypeConfigurations; + +internal class ChangeTypeConfiguration : IEntityTypeConfiguration +{ + public virtual void Configure(EntityTypeBuilder builder) + { + builder.Property(nameof(Queries.Entities.Change.Id)); + } +} diff --git a/services/Directory/FilterLists.Directory.Infrastructure/Persistence/Commands/EntityTypeConfigurations/FilterListEntityTypeConfiguration.cs b/services/Directory/FilterLists.Directory.Infrastructure/Persistence/Commands/EntityTypeConfigurations/FilterListEntityTypeConfiguration.cs index 515f76cff..6eee05d52 100644 --- a/services/Directory/FilterLists.Directory.Infrastructure/Persistence/Commands/EntityTypeConfigurations/FilterListEntityTypeConfiguration.cs +++ b/services/Directory/FilterLists.Directory.Infrastructure/Persistence/Commands/EntityTypeConfigurations/FilterListEntityTypeConfiguration.cs @@ -1,4 +1,4 @@ -using FilterLists.Directory.Domain.Aggregates; +using FilterLists.Directory.Domain.Aggregates.FilterLists; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; diff --git a/services/Directory/FilterLists.Directory.Infrastructure/Persistence/Commands/EntityTypeConfigurations/FilterListViewUrlTypeConfiguration.cs b/services/Directory/FilterLists.Directory.Infrastructure/Persistence/Commands/EntityTypeConfigurations/FilterListViewUrlTypeConfiguration.cs new file mode 100644 index 000000000..829b2a581 --- /dev/null +++ b/services/Directory/FilterLists.Directory.Infrastructure/Persistence/Commands/EntityTypeConfigurations/FilterListViewUrlTypeConfiguration.cs @@ -0,0 +1,14 @@ +using FilterLists.Directory.Domain.Aggregates.FilterLists; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace FilterLists.Directory.Infrastructure.Persistence.Commands.EntityTypeConfigurations; + +internal class FilterListViewUrlTypeConfiguration : IEntityTypeConfiguration +{ + public virtual void Configure(EntityTypeBuilder builder) + { + builder.ToTable(nameof(FilterListViewUrl) + "s"); + builder.Property(nameof(Queries.Entities.FilterListViewUrl.Id)); + } +} diff --git a/services/Directory/FilterLists.Directory.Infrastructure/Persistence/Commands/Repositories/ChangeRepository.cs b/services/Directory/FilterLists.Directory.Infrastructure/Persistence/Commands/Repositories/ChangeRepository.cs deleted file mode 100644 index 91423c64f..000000000 --- a/services/Directory/FilterLists.Directory.Infrastructure/Persistence/Commands/Repositories/ChangeRepository.cs +++ /dev/null @@ -1,11 +0,0 @@ -using FilterLists.Directory.Domain.Aggregates.Changes; - -namespace FilterLists.Directory.Infrastructure.Persistence.Commands.Repositories; - -internal class ChangeRepository : IChangeRepository -{ - public Task AddAsync(Change change, CancellationToken cancellationToken) - { - throw new NotImplementedException(); - } -} diff --git a/services/Directory/FilterLists.Directory.Infrastructure/Persistence/Commands/Repositories/LicenseRepository.cs b/services/Directory/FilterLists.Directory.Infrastructure/Persistence/Commands/Repositories/LicenseRepository.cs deleted file mode 100644 index c6ca752dd..000000000 --- a/services/Directory/FilterLists.Directory.Infrastructure/Persistence/Commands/Repositories/LicenseRepository.cs +++ /dev/null @@ -1,19 +0,0 @@ -using FilterLists.Directory.Domain.Aggregates.Licenses; -using FilterLists.Directory.Infrastructure.Persistence.Commands.Context; - -namespace FilterLists.Directory.Infrastructure.Persistence.Commands.Repositories; - -internal class LicenseRepository : ILicenseRepository -{ - private readonly ICommandContext _context; - - public LicenseRepository(ICommandContext context) - { - _context = context; - } - - public ValueTask GetByIdAsync(int id, CancellationToken cancellationToken) - { - return _context.Licenses.FindAsync(new object[] { id }, cancellationToken); - } -} diff --git a/services/Directory/FilterLists.Directory.Infrastructure/Persistence/Queries/Entities/Change.cs b/services/Directory/FilterLists.Directory.Infrastructure/Persistence/Queries/Entities/Change.cs index 9802b5e60..cb90975a6 100644 --- a/services/Directory/FilterLists.Directory.Infrastructure/Persistence/Queries/Entities/Change.cs +++ b/services/Directory/FilterLists.Directory.Infrastructure/Persistence/Queries/Entities/Change.cs @@ -11,8 +11,8 @@ public record Change public ChangeType Type { get; init; } public JsonDocument? AggregateBefore { get; init; } public JsonDocument? AggregateAfter { get; init; } - public DateTime CreatedAt { get; init; } - public string? CreatedReason { get; init; } + public string? Reason { get; init; } + public DateTime SubmittedAt { get; init; } public DateTime? AppliedAt { get; init; } public DateTime? RejectedAt { get; init; } public string? RejectedReason { get; init; } @@ -36,7 +36,5 @@ internal class UpdateConfiguration : IEntityTypeConfiguration { public virtual void Configure(EntityTypeBuilder builder) { - builder.Property(c => c.CreatedAt) - .HasDefaultValueSql("now()"); } }