< Summary

Line coverage
91%
Covered lines: 105
Uncovered lines: 10
Coverable lines: 115
Total lines: 229
Line coverage: 91.3%
Branch coverage
100%
Covered branches: 4
Total branches: 4
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: AddFileRecordAsync()100%11100%
File 2: Initialize(...)100%22100%
File 2: .ctor(...)100%11100%
File 2: CreateParameterDescriptor_AddFileRecordAsync_input()100%11100%
File 2: AddFileRecordAsync()100%11100%
File 2: AddFileRecordAsync()100%210%

File(s)

/home/runner/work/ProjectRead.ing/ProjectRead.ing/Api/Files/Mutations/AddFileRecordMutation.cs

#LineLine coverage
 1// SPDX-FileCopyrightText: 2026 Alper Çelik <alper@alper-celik.dev>
 2//
 3// SPDX-License-Identifier: AGPL-3.0-or-later
 4
 5// Mostly Ai Generated - Start
 6using Api.Auth.Handlers;
 7using Api.Auth.Models;
 8using Api.Auth.Utils;
 9using Api.Database;
 10using Api.Database.Utils;
 11using Api.Files.FileProviders;
 12using Api.Files.Queries;
 13using Api.Utils;
 14using FluentValidation;
 15using static Api.Utils.ValidatorUtils;
 16
 17namespace Api.Files.Mutations;
 18
 19[MutationType]
 20public static partial class AddFileRecordMutations
 21{
 22    [PermissionCheckAuthorize(UserPermissionBits.FileWrite)]
 23    [Error(typeof(DomainError))]
 24    public static async Task<AddFileRecordPayload> AddFileRecordAsync(
 25        [Service] ICurrentUserId userId,
 26        [Service] IEFTransactionDIAccessorService txGetter,
 27        [Service] UserFileRouter router,
 28        [Service] IValidator<AddFileRecordInput> validator,
 29        AddFileRecordInput input,
 30        CancellationToken ct
 31    )
 132    {
 133        await validator.ValidateOrThrowInputAsync(input, ct);
 34
 135        var tx = await txGetter.BeginOrGetTransactionAsync(ct);
 36
 137        var fileRecord = await router.CreateFileAsync(
 138            userId.Id!.Value,
 139            input.FileKind,
 140            input.ContentType,
 141            input.OriginalFileName,
 142            input.SizeBytes,
 143            Convert.FromHexString(input.Sha256),
 144            ct
 145        );
 46
 147        await tx.CommitAsync(ct);
 148        return new AddFileRecordPayload(FileRecordMapper.ToDto(fileRecord));
 149    }
 50}
 51
 52public record AddFileRecordPayload(Queries.FileRecord FileRecord);
 53
 54public record AddFileRecordInput
 55{
 56    public required FileKind FileKind { get; init; }
 57
 58    public required string ContentType { get; init; }
 59
 60    public string? OriginalFileName { get; init; }
 61
 62    public required long SizeBytes { get; init; }
 63
 64    public required string Sha256 { get; init; }
 65
 66    public class AddFileRecordInputValidator : AbstractValidator<AddFileRecordInput>
 67    {
 68        public AddFileRecordInputValidator(
 69            PGContext db,
 70            ICurrentUserId userId,
 71            IEFTransactionDIAccessorService tx
 72        )
 73        {
 74            RuleFor(w => w).BeginTransaction(tx);
 75
 76            RuleFor(w => w.ContentType).NotEmpty();
 77            RuleFor(w => w.OriginalFileName).MaximumLength(1000);
 78            RuleFor(w => w.SizeBytes).GreaterThan(0);
 79            RuleFor(w => w.Sha256)
 80                .Must(BeValidSha256)
 81                .WithMessage("SHA256 must be 64 hexadecimal characters")
 82                .WithErrorCode(ErrorCodes.INVALID_SHA256);
 83        }
 84
 85        static bool BeValidSha256(string sha256) =>
 86            sha256.Length == 64 && sha256.All(Uri.IsHexDigit);
 87    }
 88}
 89// Mostly Ai Generated - End

/home/runner/work/ProjectRead.ing/ProjectRead.ing/Api/obj/Debug/net10.0/generated/HotChocolate.Types.Analyzers/HotChocolate.Types.Analyzers.GraphQLServerGenerator/AddFileRecordMutations.MuAeZK5uwlVFtMhs568cXw.hc.g.cs

#LineLine coverage
 1// <auto-generated/>
 2
 3#nullable enable
 4#pragma warning disable
 5
 6using System;
 7using System.Runtime.CompilerServices;
 8using HotChocolate;
 9using HotChocolate.Types;
 10using HotChocolate.Execution.Configuration;
 11using Microsoft.Extensions.DependencyInjection;
 12using HotChocolate.Internal;
 13
 14namespace Api.Files.Mutations
 15{
 16    public static partial class AddFileRecordMutations
 17    {
 18        internal static void Initialize(global::HotChocolate.Types.IObjectTypeDescriptor descriptor)
 119        {
 120            var extension = descriptor.Extend();
 121            var configuration = extension.Configuration;
 122            var thisType = typeof(global::Api.Files.Mutations.AddFileRecordMutations);
 123            var bindingResolver = extension.Context.ParameterBindingResolver;
 124            var resolvers = new __Resolvers(bindingResolver);
 25
 126            HotChocolate.Internal.ConfigurationHelper.ApplyConfiguration(
 127                extension.Context,
 128                descriptor,
 129                typeof(global::Api.Files.Mutations.AddFileRecordMutations),
 130                new global::HotChocolate.Types.MutationTypeAttribute());
 131            configuration.ConfigurationsAreApplied = true;
 32
 133            var naming = descriptor.Extend().Context.Naming;
 34
 135            descriptor
 136                .Field(naming.GetMemberName("AddFileRecord", global::HotChocolate.Types.MemberKind.ObjectField))
 137                .ExtendWith(static (field, context) =>
 138                {
 139                    var configuration = field.Configuration;
 140                    var typeInspector = field.Context.TypeInspector;
 141                    var bindingResolver = field.Context.ParameterBindingResolver;
 142                    var naming = field.Context.Naming;
 143
 144                    configuration.Type = global::HotChocolate.Types.Descriptors.TypeReference.Create(
 145                        typeInspector.GetTypeRef(typeof(global::Api.Files.Mutations.AddFileRecordPayload), HotChocolate.
 146                        new global::HotChocolate.Language.NonNullTypeNode(new global::HotChocolate.Language.NamedTypeNod
 147                    configuration.ResultType = typeof(global::Api.Files.Mutations.AddFileRecordPayload);
 148                    configuration.DeclaringType = context.ThisType;
 149
 150                    configuration.SetSourceGeneratorFlags();
 151
 152                    var bindingInfo = field.Context.ParameterBindingResolver;
 153                    var parameter = context.Resolvers.CreateParameterDescriptor_AddFileRecordAsync_input();
 154                    var parameterInfo = bindingInfo.GetBindingInfo(parameter);
 155
 156                    if(parameterInfo.Kind is global::HotChocolate.Internal.ArgumentKind.Argument)
 157                    {
 158                        var argumentConfiguration = new global::HotChocolate.Types.Descriptors.Configurations.ArgumentCo
 159                        {
 160                            Name = naming.GetMemberName("input", global::HotChocolate.Types.MemberKind.Argument),
 161                            Type = global::HotChocolate.Types.Descriptors.TypeReference.Create(
 162                                typeInspector.GetTypeRef(typeof(global::Api.Files.Mutations.AddFileRecordInput), HotChoc
 163                                new global::HotChocolate.Language.NonNullTypeNode(new global::HotChocolate.Language.Name
 164                            RuntimeType = typeof(global::Api.Files.Mutations.AddFileRecordInput)
 165                        };
 166
 167                        configuration.Arguments.Add(argumentConfiguration);
 168                    }
 169
 170                    configuration.Member = context.ThisType.GetMethod(
 171                        "AddFileRecordAsync",
 172                        global::HotChocolate.Utilities.ReflectionUtils.StaticMemberFlags,
 173                        new global::System.Type[]
 174                        {
 175                            typeof(global::Api.Auth.Utils.ICurrentUserId),
 176                            typeof(global::Api.Database.Utils.IEFTransactionDIAccessorService),
 177                            typeof(global::Api.Files.FileProviders.UserFileRouter),
 178                            typeof(global::FluentValidation.IValidator<global::Api.Files.Mutations.AddFileRecordInput>),
 179                            typeof(global::Api.Files.Mutations.AddFileRecordInput),
 180                            typeof(global::System.Threading.CancellationToken)
 181                        })!;
 182
 183                    var fieldDescriptor = global::HotChocolate.Types.Descriptors.ObjectFieldDescriptor.From(field.Contex
 184                    HotChocolate.Internal.ConfigurationHelper.ApplyConfiguration(
 185                        field.Context,
 186                        fieldDescriptor,
 187                        configuration.Member,
 188                        new global::Api.Auth.Handlers.PermissionCheckAuthorizeAttribute(global::Api.Auth.Models.UserPerm
 189                        new global::HotChocolate.Types.ErrorAttribute(typeof(global::Api.Utils.DomainError)));
 190
 191                    bindingResolver.ApplyConfiguration(
 192                        context.Resolvers.CreateParameterDescriptor_AddFileRecordAsync_input(),
 193                        fieldDescriptor);
 194
 195                    configuration.ConfigurationsAreApplied = true;
 196                    fieldDescriptor.CreateConfiguration();
 197
 198                    configuration.Resolvers = context.Resolvers.AddFileRecordAsync();
 199                },
 1100                (Resolvers: resolvers, ThisType: thisType));
 101
 102            Configure(descriptor);
 1103        }
 104
 105        static partial void Configure(global::HotChocolate.Types.IObjectTypeDescriptor descriptor);
 106
 107        private sealed class __Resolvers
 108        {
 109            private readonly global::HotChocolate.Internal.IParameterBinding _binding_AddFileRecordAsync_input;
 110
 1111            public __Resolvers(global::HotChocolate.Resolvers.ParameterBindingResolver bindingResolver)
 1112            {
 1113                _binding_AddFileRecordAsync_input = bindingResolver.GetBinding(CreateParameterDescriptor_AddFileRecordAs
 1114            }
 115
 116            public global::HotChocolate.Internal.ParameterDescriptor CreateParameterDescriptor_AddFileRecordAsync_input(
 1117                => new HotChocolate.Internal.ParameterDescriptor(
 1118                    "input",
 1119                    typeof(global::Api.Files.Mutations.AddFileRecordInput),
 1120                    isNullable: false,
 1121                    []);
 122
 123            public HotChocolate.Resolvers.FieldResolverDelegates AddFileRecordAsync()
 1124                => new global::HotChocolate.Resolvers.FieldResolverDelegates(resolver: AddFileRecordAsync);
 125
 126            private async global::System.Threading.Tasks.ValueTask<global::System.Object?> AddFileRecordAsync(global::Ho
 0127            {
 0128                var args0 = context.Service<global::Api.Auth.Utils.ICurrentUserId>();
 0129                var args1 = context.Service<global::Api.Database.Utils.IEFTransactionDIAccessorService>();
 0130                var args2 = context.Service<global::Api.Files.FileProviders.UserFileRouter>();
 0131                var args3 = context.Service<global::FluentValidation.IValidator<global::Api.Files.Mutations.AddFileRecor
 0132                var args4 = _binding_AddFileRecordAsync_input.Execute<global::Api.Files.Mutations.AddFileRecordInput>(co
 0133                var args5 = context.RequestAborted;
 0134                var result = await global::Api.Files.Mutations.AddFileRecordMutations.AddFileRecordAsync(args0, args1, a
 0135                return result;
 0136            }
 137        }
 138    }
 139}
 140