< Summary

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

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.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
 0128public record WorkGetDTO(
 0129        Guid Id,
 0130        int RowVersion,
 0131        NodaTime.Instant MetadataAddedAt,
 0132        NodaTime.Instant MetadataUpdatedAt,
 0133
 0134        string Title,
 0135        string? Description,
 0136
 0137        NodaTime.ZonedDateTime? WorkPublishedAt,
 0138        NodaTime.ZonedDateTime? WorkUpdatedAt,
 0139
 0140        TagGetDTO[] Tags,
 0141        AuthorGetDTO[] Authors,
 0142
 0143        WorkIdentifierDTO[] WorkIdentifiers
 0144        ) : 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{
 208    public WorkAddDTOValidator(PGContext db, ICurrentUserId userId)
 209    {
 210        RuleFor(w => w.TagIds)
 211            .Must(ids => ids.Length == ids.Distinct().Count())
 212            .WithMessage("Duplicate tagIds are forbidden");
 213
 214        RuleFor(w => w.TagIds)
 215            .MustAsync(async (ids, ct) =>
 216                    await Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.CountAsync(
 217                     db.WorkTags
 218                    .Where(wt => wt.OwnerId == userId.Id
 219                        && ids.Contains(wt.Id)), ct)
 220                    == ids.Length
 221                    )
 222            .WithMessage("Some or all tagIds doesn't exist");
 223
 224
 225
 226        RuleFor(w => w.AuthorIds)
 227           .Must(ids => ids.Length == ids.Distinct().Count())
 228           .WithMessage("Duplicate AuthorIds are forbidden");
 229
 230        RuleFor(w => w.AuthorIds)
 231            .MustAsync(async (ids, ct) =>
 232                    await Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.CountAsync(
 233                     db.WorkTags
 234                    .Where(wt => wt.OwnerId == userId.Id
 235                        && ids.Contains(wt.Id)), ct)
 236                    == ids.Length
 237                    )
 238            .WithMessage("Some or all AuthorIds doesn't exist");
 239    }
 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