< Summary

Information
Class: Api.Works.DTOs.WorkAddDTOValidator
Assembly: Api
File(s): /home/runner/work/ProjectRead.ing/ProjectRead.ing/Api/Works/DTOs/WorkDTOs.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 27
Coverable lines: 27
Total lines: 272
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 4
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)0%2040%
<-ctor()100%210%
<-ctor()100%210%

File(s)

/home/runner/work/ProjectRead.ing/ProjectRead.ing/Api/Works/DTOs/WorkDTOs.cs

#LineLine coverage
 1// SPDX-FileCopyrightText: 2026 Alper Çelik <[email protected]>
 2//
 3// SPDX-License-Identifier: AGPL-3.0-or-later
 4
 5using Api.Auth.Utils;
 6using Api.Database;
 7using Api.Database.Utils;
 8using Api.Works.Models;
 9
 10using FluentValidation;
 11using FluentValidation.Results;
 12
 13using Riok.Mapperly.Abstractions;
 14
 15namespace Api.Works.DTOs;
 16
 17#region WorkUpdateDTO
 18public record WorkUpdateDTO(
 19        Guid Id,
 20        int RowVersion,
 21
 22        string Title,
 23        string? Description,
 24
 25        NodaTime.ZonedDateTime? WorkPublishedAt,
 26        NodaTime.ZonedDateTime? WorkUpdatedAt,
 27
 28        Guid[] TagIds,
 29        Guid[] AuthorIds,
 30
 31        WorkIdentifierDTO[] WorkIdentifiers
 32        );
 33
 34[Mapper]
 35public static partial class WorkUpdateDTOMapper
 36{
 37
 38    public static Work FromWorkUpdateDTO(WorkUpdateDTO w)
 39    {
 40        var now = NodaTime.SystemClock.Instance.GetCurrentInstant();
 41        return new Work()
 42        {
 43            Id = w.Id,
 44            RowVersion = w.RowVersion,
 45
 46            Title = w.Title,
 47            Description = w.Description,
 48
 49            WorkPublishedAt = w.WorkPublishedAt,
 50            WorkUpdatedAt = w.WorkUpdatedAt,
 51
 52            WorkTag_Works = [.. w.TagIds.Select(wTagId => new WorkTag_Work(){
 53                    WorkId = w.Id,
 54                    WorkTagId =wTagId
 55                    })],
 56
 57            Work_Authors = [.. w.AuthorIds.Select(wAuthorId => new Work_Author(){
 58                    WorkId = w.Id,
 59                    AuthorId = wAuthorId
 60                    })],
 61
 62            WorkIdentifiers = [.. w.WorkIdentifiers.Select(wid =>
 63                    new WorkIdentifier(wid.WorkIdentifierType,wid.WorkIdentifierValue)
 64            )],
 65
 66        };
 67    }
 68}
 69
 70
 71public class WorkUpdateDTOValidator : AbstractValidator<WorkUpdateDTO>
 72{
 73    private readonly IEFTransactionDIAccessorService _tx;
 74
 75    public WorkUpdateDTOValidator(
 76            PGContext db,
 77            IEFTransactionDIAccessorService tx,
 78            ICurrentUserId userId)
 79    {
 80        RuleFor(w => w.TagIds)
 81            .Must(ids => ids.Length == ids.Distinct().Count())
 82            .WithMessage("Duplicate tagIds are forbidden");
 83
 84        RuleFor(w => w.TagIds)
 85            .MustAsync(async (ids, ct) =>
 86                    await Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.CountAsync(
 87                     db.WorkTags
 88                    .Where(wt => wt.OwnerId == userId.Id
 89                        && ids.Contains(wt.Id)), ct)
 90                    == ids.Length
 91                    )
 92            .WithMessage("Some or all tagIds doesn't exist");
 93
 94
 95
 96        RuleFor(w => w.AuthorIds)
 97           .Must(ids => ids.Length == ids.Distinct().Count())
 98           .WithMessage("Duplicate AuthorIds are forbidden");
 99
 100        RuleFor(w => w.AuthorIds)
 101            .MustAsync(async (ids, ct) =>
 102                    await Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.CountAsync(
 103                     db.WorkTags
 104                    .Where(wt => wt.OwnerId == userId.Id
 105                        && ids.Contains(wt.Id)), ct)
 106                    == ids.Length
 107                    )
 108            .WithMessage("Some or all AuthorIds doesn't exist");
 109        _tx = tx;
 110    }
 111
 112    public override async Task<ValidationResult> ValidateAsync(ValidationContext<WorkUpdateDTO> context, CancellationTok
 113    {
 114        await _tx.BeginOrGetTransactionAsync();
 115        return await base.ValidateAsync(context, cancellation);
 116    }
 117
 118    protected override bool PreValidate(ValidationContext<WorkUpdateDTO> context, ValidationResult result)
 119    {
 120        _tx.BeginOrGetTransaction();
 121        return base.PreValidate(context, result);
 122    }
 123}
 124#endregion
 125
 126
 127#region WorkGetDTO
 128public record WorkGetDTO(
 129        Guid Id,
 130        int RowVersion,
 131        NodaTime.Instant MetadataAddedAt,
 132        NodaTime.Instant MetadataUpdatedAt,
 133
 134        string Title,
 135        string? Description,
 136
 137        NodaTime.ZonedDateTime? WorkPublishedAt,
 138        NodaTime.ZonedDateTime? WorkUpdatedAt,
 139
 140        TagGetDTO[] Tags,
 141        AuthorGetDTO[] Authors,
 142
 143        WorkIdentifierDTO[] WorkIdentifiers
 144        ) : IEntityMetadata;
 145
 146[Mapper]
 147public static partial class WorkGetDTOMapper
 148{
 149    [MapProperty(nameof(Work.WorkTags), nameof(WorkGetDTO.Tags))]
 150    public static partial WorkGetDTO ToDto(Work w);
 151}
 152#endregion
 153
 154#region WorkAddDTO
 155public record WorkAddDTO(
 156        string Title,
 157        string? Description,
 158
 159        NodaTime.ZonedDateTime? WorkPublishedAt,
 160        NodaTime.ZonedDateTime? WorkUpdatedAt,
 161
 162        Guid[] TagIds,
 163        Guid[] AuthorIds,
 164
 165        WorkIdentifierDTO[] WorkIdentifiers
 166        );
 167
 168[Mapper]
 169public static partial class WorkAddDTOMapper
 170{
 171
 172    public static Work FromWorkAddDTO(WorkAddDTO w)
 173    {
 174        var id = Guid.CreateVersion7();
 175        var now = NodaTime.SystemClock.Instance.GetCurrentInstant();
 176        return new Work()
 177        {
 178            Id = id,
 179            Title = w.Title,
 180            Description = w.Description,
 181
 182            WorkPublishedAt = w.WorkPublishedAt,
 183            WorkUpdatedAt = w.WorkUpdatedAt,
 184
 185            MetadataAddedAt = now,
 186            MetadataUpdatedAt = now,
 187
 188            WorkTag_Works = [.. w.TagIds.Select(wTagId => new WorkTag_Work(){
 189                    WorkId = id,
 190                    WorkTagId =wTagId
 191                    })],
 192
 193            Work_Authors = [.. w.AuthorIds.Select(wAuthorId => new Work_Author(){
 194                    WorkId = id,
 195                    AuthorId = wAuthorId
 196                    })],
 197
 198            WorkIdentifiers = [.. w.WorkIdentifiers.Select(wid =>
 199                    new WorkIdentifier (wid.WorkIdentifierType,wid.WorkIdentifierValue)
 200                    )],
 201
 202        };
 203    }
 204}
 205
 206public class WorkAddDTOValidator : AbstractValidator<WorkAddDTO>
 207{
 0208    public WorkAddDTOValidator(PGContext db, ICurrentUserId userId)
 0209    {
 0210        RuleFor(w => w.TagIds)
 0211            .Must(ids => ids.Length == ids.Distinct().Count())
 0212            .WithMessage("Duplicate tagIds are forbidden");
 213
 0214        RuleFor(w => w.TagIds)
 0215            .MustAsync(async (ids, ct) =>
 0216                    await Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.CountAsync(
 0217                     db.WorkTags
 0218                    .Where(wt => wt.OwnerId == userId.Id
 0219                        && ids.Contains(wt.Id)), ct)
 0220                    == ids.Length
 0221                    )
 0222            .WithMessage("Some or all tagIds doesn't exist");
 223
 224
 225
 0226        RuleFor(w => w.AuthorIds)
 0227           .Must(ids => ids.Length == ids.Distinct().Count())
 0228           .WithMessage("Duplicate AuthorIds are forbidden");
 229
 0230        RuleFor(w => w.AuthorIds)
 0231            .MustAsync(async (ids, ct) =>
 0232                    await Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.CountAsync(
 0233                     db.WorkTags
 0234                    .Where(wt => wt.OwnerId == userId.Id
 0235                        && ids.Contains(wt.Id)), ct)
 0236                    == ids.Length
 0237                    )
 0238            .WithMessage("Some or all AuthorIds doesn't exist");
 0239    }
 240}
 241#endregion
 242public record struct WorkIdentifierDTO(
 243        string WorkIdentifierType,
 244        string WorkIdentifierValue
 245        );
 246
 247
 248public record struct WorksGetDTO(
 249        AuthorGetDTO[] ReferencedAuthors,
 250        WorkSmallDTO[] Works
 251        );
 252
 253#region WorkSmallDto
 254public record struct WorkSmallDTO(
 255        Guid Id,
 256        string Title,
 257        Guid[] AuthorIds,
 258        Guid? CoverId = null
 259        );
 260
 261public static partial class WorkSmallDTOMapper
 262{
 263    public static IQueryable<WorkSmallDTO> ProjectToDTO(IQueryable<Work> q) => q.Select(w => new WorkSmallDTO(
 264
 265         w.Id,
 266         w.Title,
 267         w.Authors!.Select(a => a.Id).ToArray(),
 268         null
 269    ));
 270
 271}
 272#endregion