diff --git a/services/Directory/FilterLists.Directory.Application/Commands/CreateList.cs b/services/Directory/FilterLists.Directory.Application/Commands/CreateList.cs index 157d4f688..11d2e395b 100644 --- a/services/Directory/FilterLists.Directory.Application/Commands/CreateList.cs +++ b/services/Directory/FilterLists.Directory.Application/Commands/CreateList.cs @@ -21,11 +21,6 @@ public record Command : IRequest /// EasyList public string Name { get; init; } = default!; - /// - /// The view URLs. - /// - public IEnumerable ViewUrls { get; init; } = new HashSet(); - /// /// The brief description in English (preferably quoted from the project). /// @@ -38,6 +33,29 @@ public record Command : IRequest /// 4 public long? LicenseId { get; init; } + /// + /// The identifiers of the Syntaxes implemented by this FilterList. + /// + /// [ 3 ] + public IEnumerable SyntaxIds { get; init; } = new HashSet(); + + /// + /// The identifiers of the Languages targeted by this FilterList. + /// + /// [ 37 ] + public IEnumerable LanguageIds { get; init; } = new HashSet(); + + /// + /// The identifiers of the Tags applied to this FilterList. + /// + /// [ 2 ] + public IEnumerable TagIds { get; init; } = new HashSet(); + + /// + /// The view URLs. + /// + public IEnumerable ViewUrls { get; init; } = new HashSet(); + /// /// The URL of the home page. /// @@ -92,6 +110,48 @@ public record Command : IRequest /// null public Uri? DonateUrl { get; init; } + /// + /// The identifiers of the Maintainers of this FilterList. + /// + /// [ 7 ] + public IEnumerable MaintainerIds { get; init; } = new HashSet(); + + /// + /// The identifiers of the FilterLists from which this FilterList was forked. + /// + /// [] + public IEnumerable UpstreamFilterListIds { get; init; } = new HashSet(); + + /// + /// The identifiers of the FilterLists that have been forked from this FilterList. + /// + /// [ 166, 565 ] + public IEnumerable ForkFilterListIds { get; init; } = new HashSet(); + + /// + /// The identifiers of the FilterLists that include this FilterList. + /// + /// [] + public IEnumerable IncludedInFilterListIds { get; init; } = new HashSet(); + + /// + /// The identifiers of the FilterLists that this FilterList includes. + /// + /// [ 11, 13, 168 ] + public IEnumerable IncludesFilterListIds { get; init; } = new HashSet(); + + /// + /// The identifiers of the FilterLists that this FilterList depends upon. + /// + /// [] + public IEnumerable DependencyFilterListIds { get; init; } = new HashSet(); + + /// + /// The identifiers of the FilterLists dependent upon this FilterList. + /// + /// [] + public IEnumerable DependentFilterListIds { get; init; } = new HashSet(); + /// /// The reason that the FilterList is being added to FilterLists. /// @@ -150,14 +210,90 @@ public async Task Handle(Command request, CancellationToken cancellati .WhereIsDefaultForFilterList() .SingleAsync(cancellationToken); - // TODO: accept IDs and fetch additional related aggregates + var syntaxes = request.SyntaxIds.Any() + ? await _commandContext.Syntaxes + .Where(s => request.SyntaxIds.Contains(s.Id)) + .ToListAsync(cancellationToken) + : new List(); + if (request.SyntaxIds.Any(sid => !syntaxes.Select(s => s.Id).Contains(sid))) + { + throw new ArgumentException("One or more SyntaxIds not found.", nameof(request.SyntaxIds)); + } + + var languages = request.LanguageIds.Any() + ? await _commandContext.Languages + .Where(l => request.LanguageIds.Contains(l.Id)) + .ToListAsync(cancellationToken) + : new List(); + if (request.LanguageIds.Any(lid => !languages.Select(l => l.Id).Contains(lid))) + { + throw new ArgumentException("One or more LanguageIds not found.", nameof(request.LanguageIds)); + } + + var tags = request.TagIds.Any() + ? await _commandContext.Tags + .Where(t => request.TagIds.Contains(t.Id)) + .ToListAsync(cancellationToken) + : new List(); + if (request.TagIds.Any(tig => !tags.Select(t => t.Id).Contains(tig))) + { + throw new ArgumentException("One or more TagIds not found.", nameof(request.TagIds)); + } + + var maintainers = request.MaintainerIds.Any() + ? await _commandContext.Maintainers + .Where(m => request.MaintainerIds.Contains(m.Id)) + .ToListAsync(cancellationToken) + : new List(); + if (request.MaintainerIds.Any(mid => !maintainers.Select(m => m.Id).Contains(mid))) + { + throw new ArgumentException("One or more MaintainerIds not found.", nameof(request.MaintainerIds)); + } + + var relatedFilterListIds = request.UpstreamFilterListIds + .Concat(request.ForkFilterListIds) + .Concat(request.IncludedInFilterListIds) + .Concat(request.IncludesFilterListIds) + .Concat(request.DependencyFilterListIds) + .Concat(request.DependentFilterListIds) + .ToList(); + var relatedFilterLists = relatedFilterListIds.Count > 0 + ? await _commandContext.FilterLists + .Where(f => relatedFilterListIds.Contains(f.Id)) + .ToListAsync(cancellationToken) + : new List(); + if (request.UpstreamFilterListIds.Any(fid => !relatedFilterLists.Select(f => f.Id).Contains(fid))) + { + throw new ArgumentException("One or more UpstreamFilterListIds not found.", nameof(request.UpstreamFilterListIds)); + } + if (request.ForkFilterListIds.Any(fid => !relatedFilterLists.Select(f => f.Id).Contains(fid))) + { + throw new ArgumentException("One or more ForkFilterListIds not found.", nameof(request.UpstreamFilterListIds)); + } + if (request.IncludedInFilterListIds.Any(fid => !relatedFilterLists.Select(f => f.Id).Contains(fid))) + { + throw new ArgumentException("One or more IncludedInFilterListIds not found.", nameof(request.UpstreamFilterListIds)); + } + if (request.IncludesFilterListIds.Any(fid => !relatedFilterLists.Select(f => f.Id).Contains(fid))) + { + throw new ArgumentException("One or more IncludesFilterListIds not found.", nameof(request.UpstreamFilterListIds)); + } + if (request.DependencyFilterListIds.Any(fid => !relatedFilterLists.Select(f => f.Id).Contains(fid))) + { + throw new ArgumentException("One or more DependencyFilterListIds not found.", nameof(request.UpstreamFilterListIds)); + } + if (request.DependentFilterListIds.Any(fid => !relatedFilterLists.Select(f => f.Id).Contains(fid))) + { + throw new ArgumentException("One or more DependentFilterListIds not found.", nameof(request.UpstreamFilterListIds)); + } + var filterList = FilterList.CreatePendingApproval( request.Name, request.Description, license, - new HashSet(), - new HashSet(), - new HashSet(), + syntaxes, + languages, + tags, request.ViewUrls.Select(u => (u.SegmentNumber, u.Primariness, u.Url)), request.HomeUrl, request.OnionUrl, @@ -168,13 +304,13 @@ public async Task Handle(Command request, CancellationToken cancellati request.ChatUrl, request.EmailAddress, request.DonateUrl, - new HashSet(), - new HashSet(), - new HashSet(), - new HashSet(), - new HashSet(), - new HashSet(), - new HashSet(), + maintainers, + relatedFilterLists.Where(f => request.UpstreamFilterListIds.Contains(f.Id)), + relatedFilterLists.Where(f => request.ForkFilterListIds.Contains(f.Id)), + relatedFilterLists.Where(f => request.IncludedInFilterListIds.Contains(f.Id)), + relatedFilterLists.Where(f => request.IncludesFilterListIds.Contains(f.Id)), + relatedFilterLists.Where(f => request.DependencyFilterListIds.Contains(f.Id)), + relatedFilterLists.Where(f => request.DependentFilterListIds.Contains(f.Id)), request.CreateReason); _commandContext.FilterLists.Add(filterList); diff --git a/services/Directory/FilterLists.Directory.Infrastructure/Persistence/Commands/EntityTypeConfigurations/FilterListTypeConfiguration.cs b/services/Directory/FilterLists.Directory.Infrastructure/Persistence/Commands/EntityTypeConfigurations/FilterListTypeConfiguration.cs index 69e839c83..9cbee2e19 100644 --- a/services/Directory/FilterLists.Directory.Infrastructure/Persistence/Commands/EntityTypeConfigurations/FilterListTypeConfiguration.cs +++ b/services/Directory/FilterLists.Directory.Infrastructure/Persistence/Commands/EntityTypeConfigurations/FilterListTypeConfiguration.cs @@ -14,15 +14,27 @@ public virtual void Configure(EntityTypeBuilder builder) // TODO: register and resolve INameRewriter var nr = new SnakeCaseNameRewriter(CultureInfo.InvariantCulture); + builder.HasMany(f => f.Syntaxes) + .WithMany(s => s.FilterLists) + .UsingEntity(e => e.ToTable($"{nr.RewriteName(nameof(FilterListSyntax))}es")); + builder.HasMany(f => f.Languages) + .WithMany(l => l.FilterLists) + .UsingEntity(e => e.ToTable($"{nr.RewriteName(nameof(FilterListLanguage))}s")); + builder.HasMany(f => f.Tags) + .WithMany(t => t.FilterLists) + .UsingEntity(e => e.ToTable($"{nr.RewriteName(nameof(FilterListTag))}s")); + builder.HasMany(f => f.Maintainers) + .WithMany(m => m.FilterLists) + .UsingEntity(e => e.ToTable($"{nr.RewriteName(nameof(FilterListMaintainer))}s")); builder.HasMany(f => f.UpstreamFilterLists) .WithMany(f => f.ForkFilterLists) - .UsingEntity(f => f.ToTable($"{nr.RewriteName(nameof(Fork))}s")); + .UsingEntity(e => e.ToTable($"{nr.RewriteName(nameof(Fork))}s")); builder.HasMany(f => f.IncludedInFilterLists) .WithMany(f => f.IncludesFilterLists) - .UsingEntity(f => f.ToTable($"{nr.RewriteName(nameof(Merge))}s")); + .UsingEntity(e => e.ToTable($"{nr.RewriteName(nameof(Merge))}s")); builder.HasMany(f => f.DependencyFilterLists) .WithMany(f => f.DependentFilterLists) - .UsingEntity(f => f.ToTable($"{nr.RewriteName(nameof(Dependent))}s")); + .UsingEntity(e => e.ToTable($"{nr.RewriteName(nameof(Dependent))}s")); builder.HasMany(f => f.Changes) .WithOne() .HasForeignKey(nameof(Change.FilterListId));