< Summary

Line coverage
100%
Covered lines: 47
Uncovered lines: 0
Coverable lines: 47
Total lines: 163
Line coverage: 100%
Branch coverage
100%
Covered branches: 6
Total branches: 6
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
File 1: CreateFromDtoInternal(...)100%44100%
File 1: MapToWorkIdentifier(...)100%11100%
File 1: MapToListOfWorkIdentifier(...)100%22100%
File 2: CreateFromDto(...)100%11100%
File 2: FromInstantToZonedDateTime(...)100%11100%

File(s)

/home/runner/work/ProjectRead.ing/ProjectRead.ing/Api/obj/Debug/net10.0/generated/Riok.Mapperly/Riok.Mapperly.MapperGenerator/AddWorkInputMapper.g.cs

#LineLine coverage
 1// <auto-generated />
 2#nullable enable
 3namespace Api.Works.Mutations
 4{
 5    public static partial class AddWorkInputMapper
 6    {
 7        [global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "4.3.1.0")]
 8        private static partial global::Api.Works.Models.Work CreateFromDtoInternal(global::Api.Works.Mutations.AddWorkIn
 19        {
 110            var target = new global::Api.Works.Models.Work()
 111            {
 112                Title = w.Title,
 113            };
 114            target.Id = id;
 115            target.MetadataAddedAt = metadataAddedAt;
 116            target.MetadataUpdatedAt = metadataUpdatedAt;
 117            target.Description = w.Description;
 118            if (w.WorkPublishedAt != null)
 119            {
 120                target.WorkPublishedAt = FromInstantToZonedDateTime(w.WorkPublishedAt.Value);
 121            }
 22            else
 123            {
 124                target.WorkPublishedAt = null;
 125            }
 126            if (w.WorkUpdatedAt != null)
 127            {
 128                target.WorkUpdatedAt = FromInstantToZonedDateTime(w.WorkUpdatedAt.Value);
 129            }
 30            else
 131            {
 132                target.WorkUpdatedAt = null;
 133            }
 134            target.WorkIdentifiers = MapToListOfWorkIdentifier(w.WorkIdentifiers);
 135            return target;
 136        }
 37
 38        [global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "4.3.1.0")]
 39        private static global::Api.Works.Models.WorkIdentifier MapToWorkIdentifier(global::Api.Works.Queries.WorkIdentif
 140        {
 141            var target = new global::Api.Works.Models.WorkIdentifier(source.WorkIdentifierType, source.WorkIdentifierVal
 142            return target;
 143        }
 44
 45        [global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "4.3.1.0")]
 46        private static global::System.Collections.Generic.List<global::Api.Works.Models.WorkIdentifier> MapToListOfWorkI
 147        {
 148            var target = new global::System.Collections.Generic.List<global::Api.Works.Models.WorkIdentifier>(source.Cou
 149            foreach (var item in source)
 150            {
 151                target.Add(MapToWorkIdentifier(item));
 152            }
 153            return target;
 154        }
 55    }
 56}

/home/runner/work/ProjectRead.ing/ProjectRead.ing/Api/Works/Mutations/AddWorkMutation.cs

#LineLine coverage
 1// SPDX-FileCopyrightText: 2026 Alper Çelik <alper@alper-celik.dev>
 2//
 3// SPDX-License-Identifier: AGPL-3.0-or-later
 4using Api.Auth.Handlers;
 5using Api.Auth.Utils;
 6using Api.Database;
 7using Api.Database.Utils;
 8using Api.Utils;
 9using Api.Works.Models;
 10using Api.Works.Queries;
 11using FluentValidation;
 12using NodaTime;
 13using Riok.Mapperly.Abstractions;
 14using static Api.Utils.ValidatorUtils;
 15
 16namespace Api.Works.Mutations;
 17
 18[MutationType]
 19public static partial class AddWorkMutations
 20{
 21    [PermissionCheckAuthorize(Auth.Models.UserPermissionBits.WorkWrite)]
 22    [Error(typeof(DomainError))]
 23    public static async Task<AddWorkPayload> AddWorkAsync(
 24        [Service] PGContext db,
 25        [Service] ICurrentUserId userId,
 26        [Service] IEFTransactionDIAccessorService txGetter,
 27        [Service] IValidator<AddWorkInput> validator,
 28        AddWorkInput input,
 29        CancellationToken ct
 30    )
 31    {
 32        await validator.ValidateOrThrowInputAsync(input, ct);
 33
 34        var tx = await txGetter.BeginOrGetTransactionAsync();
 35
 36        var work = AddWorkInputMapper.CreateFromDto(input, userId.Id!.Value, Now());
 37
 38        await db.AddAsync(work, cancellationToken: ct);
 39        await db.SaveChangesAsync(cancellationToken: ct);
 40
 41        await tx.CommitAsync(ct);
 42        return new AddWorkPayload(WorkMapper.ToDto(work));
 43    }
 44}
 45
 46public record AddWorkPayload(Queries.Work Work);
 47
 48public record AddWorkInput
 49{
 50    public required string Title { get; init; }
 51
 52    public string? Description { get; init; }
 53
 54    public Instant? WorkPublishedAt { get; init; }
 55    public Instant? WorkUpdatedAt { get; init; }
 56    public List<Queries.WorkIdentifier> WorkIdentifiers { get; init; } = [];
 57    public required List<Guid> TagIds { get; init; } = [];
 58    public required List<Guid> AuthorIds { get; init; } = [];
 59
 60    public class AddWorkInputValidator : AbstractValidator<AddWorkInput>
 61    {
 62        public AddWorkInputValidator(
 63            PGContext db,
 64            ICurrentUserId userId,
 65            IEFTransactionDIAccessorService tx
 66        )
 67        {
 68            RuleFor(w => w).BeginTransaction(tx);
 69
 70            RuleFor(w => w.TagIds).MustBeDistinct(nameof(TagIds));
 71            RuleFor(w => w.TagIds).IdsMustExist(db.WorkTags, userId.Id);
 72
 73            RuleFor(w => w.AuthorIds).MustBeDistinct(nameof(AuthorIds));
 74            RuleFor(w => w.AuthorIds).IdsMustExist(db.Authors, userId.Id);
 75        }
 76    }
 77}
 78
 79[Mapper]
 80public static partial class AddWorkInputMapper
 81{
 82    [MapperIgnoreTarget(nameof(Models.Work.RowVersion))]
 83    [MapperIgnoreSource(nameof(AddWorkInput.TagIds))]
 84    [MapperIgnoreSource(nameof(AddWorkInput.AuthorIds))]
 85    private static partial Models.Work CreateFromDtoInternal(
 86        AddWorkInput w,
 87        Guid id,
 88        Instant metadataAddedAt,
 89        Instant metadataUpdatedAt
 90    );
 91
 92    public static Models.Work CreateFromDto(AddWorkInput w, Guid ownerId, Instant now)
 193    {
 194        var id = Guid.CreateVersion7().WithPostfix(Models.Work.IdPostfix);
 195        var work = CreateFromDtoInternal(w, id, now, now);
 196        work.OwnerId = ownerId;
 97
 198        work.WorkTag_Works = [.. w.TagIds.Select(tId => new WorkTag_Work(id, tId))];
 199        work.Work_Authors = [.. w.AuthorIds.Select(aId => new Work_Author(id, aId))];
 100
 1101        return work;
 1102    }
 103
 104    [UserMapping(Default = true)]
 105    public static ZonedDateTime FromInstantToZonedDateTime(Instant i) =>
 1106        MapperUtils.FromInstantToZonedDateTime(i);
 107}