< Summary

Line coverage
100%
Covered lines: 126
Uncovered lines: 0
Coverable lines: 126
Total lines: 231
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: RegisterMutation()100%22100%
File 2: Initialize(...)100%22100%
File 2: .ctor(...)100%11100%
File 2: CreateParameterDescriptor_RegisterMutation_input()100%11100%
File 2: RegisterMutation()100%11100%
File 2: RegisterMutation()100%11100%

File(s)

/home/runner/work/ProjectRead.ing/ProjectRead.ing/Api/Auth/Mutations/RegisterMutation.cs

#LineLine coverage
 1// SPDX-FileCopyrightText: 2026 Alper Çelik <[email protected]>
 2//
 3// SPDX-License-Identifier: AGPL-3.0-or-later
 4
 5using System.ComponentModel.DataAnnotations;
 6using System.Text;
 7using Api.Auth.Models;
 8using Api.Auth.Utils;
 9using Api.Database;
 10using Api.Database.Utils;
 11using Api.Utils;
 12using FluentValidation;
 13using Geralt;
 14using HotChocolate.Authorization;
 15using Microsoft.EntityFrameworkCore;
 16
 17namespace Api.Auth.Mutations;
 18
 19public record RegisterInput(
 20    string Email,
 21    string Password,
 22    bool AdminRegistration,
 23    string ClientName = "unknown"
 24);
 25
 26[MutationType]
 27public static partial class RegisterMutations
 28{
 29    [AllowAnonymous]
 30    public static async Task<LoginPayload> RegisterMutation(
 31        [Service] PGContext db,
 32        [Service] IEFTransactionDIAccessorService txAccessor,
 33        [Service] IValidator<RegisterInput> validator,
 34        RegisterInput input,
 35        CancellationToken ct
 36    )
 137    {
 138        await validator.ValidateOrThrowAsync(input, ct);
 39
 140        var tx = await txAccessor.BeginOrGetTransactionAsync();
 41
 142        var password_bytes = Encoding.UTF8.GetBytes(input.Password.Normalize());
 143        var hash_chars = new char[Argon2id.HashSize];
 144        Argon2id.ComputeHash(
 145            hash_chars,
 146            password_bytes,
 147            AuthUtils.ARGON2ID_ITER,
 148            AuthUtils.ARGON2ID_MEM_BYTES
 149        );
 150        string hash = new([.. hash_chars.Where(c => c != (char)byte.MinValue)]);
 51
 152        var now = Now();
 153        var user = new UserEF()
 154        {
 155            Id = Guid.CreateVersion7().WithPostfix(UserEF.IdPostfix),
 156            MetadataAddedAt = now,
 157            MetadataUpdatedAt = now,
 158
 159            Email = input.Email.NormalizeEmail(),
 160            PasswordHash = hash,
 161            Admin = (await AuthUtils.CanAdminRegister(db)) && input.AdminRegistration,
 162        };
 63
 164        await db.Users.AddAsync(user);
 165        string token = await AuthUtils.CreateUserSession(user.Id, input.ClientName, db);
 166        await db.SaveChangesAsync();
 167        await tx.CommitAsync();
 68
 169        return new(Queries.UserMapper.ToDto(user), token);
 170    }
 71}
 72
 73public class RegisterInputValidator : AbstractValidator<RegisterInput>
 74{
 75    public RegisterInputValidator(PGContext db, IEFTransactionDIAccessorService tx)
 76    {
 77        RuleFor(w => w).BeginTransaction(tx);
 78
 79        RuleFor(r => r.Email)
 80            .Must(e => new EmailAddressAttribute().IsValid(e.NormalizeEmail()))
 81            .WithMessage("Email is invalid");
 82
 83        RuleFor(r => r.Email)
 84            .MustAsync(
 85                async (e, ct) =>
 86                    !await db.Users.Where(u => u.Email == e.NormalizeEmail()).AnyAsync(ct)
 87            )
 88            .WithMessage("Email is already used");
 89
 90        RuleFor(r => r.AdminRegistration)
 91            .MustAsync(async (adr, ct) => !adr || await AuthUtils.CanAdminRegister(db))
 92            .WithMessage("Can't Register As Admin");
 93    }
 94}

/home/runner/work/ProjectRead.ing/ProjectRead.ing/Api/obj/Debug/net10.0/generated/HotChocolate.Types.Analyzers/HotChocolate.Types.Analyzers.GraphQLServerGenerator/RegisterMutations.hs33rHgC5AHky1fX2QOBKQ.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.Auth.Mutations
 15{
 16    public static partial class RegisterMutations
 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.Auth.Mutations.RegisterMutations);
 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.Auth.Mutations.RegisterMutations),
 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("RegisterMutation", 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.Auth.Mutations.LoginPayload), HotChocolate.Types.Typ
 146                        new global::HotChocolate.Language.NonNullTypeNode(new global::HotChocolate.Language.NamedTypeNod
 147                    configuration.ResultType = typeof(global::Api.Auth.Mutations.LoginPayload);
 148                    configuration.DeclaringType = context.ThisType;
 149
 150                    configuration.SetSourceGeneratorFlags();
 151
 152                    var bindingInfo = field.Context.ParameterBindingResolver;
 153                    var parameter = context.Resolvers.CreateParameterDescriptor_RegisterMutation_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.Auth.Mutations.RegisterInput), HotChocolate.
 163                                new global::HotChocolate.Language.NonNullTypeNode(new global::HotChocolate.Language.Name
 164                            RuntimeType = typeof(global::Api.Auth.Mutations.RegisterInput)
 165                        };
 166
 167                        configuration.Arguments.Add(argumentConfiguration);
 168                    }
 169
 170                    configuration.Member = context.ThisType.GetMethod(
 171                        "RegisterMutation",
 172                        global::HotChocolate.Utilities.ReflectionUtils.StaticMemberFlags,
 173                        new global::System.Type[]
 174                        {
 175                            typeof(global::Api.Database.PGContext),
 176                            typeof(global::Api.Database.Utils.IEFTransactionDIAccessorService),
 177                            typeof(global::FluentValidation.IValidator<global::Api.Auth.Mutations.RegisterInput>),
 178                            typeof(global::Api.Auth.Mutations.RegisterInput),
 179                            typeof(global::System.Threading.CancellationToken)
 180                        })!;
 181
 182                    var fieldDescriptor = global::HotChocolate.Types.Descriptors.ObjectFieldDescriptor.From(field.Contex
 183                    HotChocolate.Internal.ConfigurationHelper.ApplyConfiguration(
 184                        field.Context,
 185                        fieldDescriptor,
 186                        configuration.Member,
 187                        new global::HotChocolate.Authorization.AllowAnonymousAttribute());
 188
 189                    bindingResolver.ApplyConfiguration(
 190                        context.Resolvers.CreateParameterDescriptor_RegisterMutation_input(),
 191                        fieldDescriptor);
 192
 193                    configuration.ConfigurationsAreApplied = true;
 194                    fieldDescriptor.CreateConfiguration();
 195
 196                    configuration.Resolvers = context.Resolvers.RegisterMutation();
 197                },
 198                (Resolvers: resolvers, ThisType: thisType));
 99
 100            Configure(descriptor);
 1101        }
 102
 103        static partial void Configure(global::HotChocolate.Types.IObjectTypeDescriptor descriptor);
 104
 105        private sealed class __Resolvers
 106        {
 107            private readonly global::HotChocolate.Internal.IParameterBinding _binding_RegisterMutation_input;
 108
 1109            public __Resolvers(global::HotChocolate.Resolvers.ParameterBindingResolver bindingResolver)
 1110            {
 1111                _binding_RegisterMutation_input = bindingResolver.GetBinding(CreateParameterDescriptor_RegisterMutation_
 1112            }
 113
 114            public global::HotChocolate.Internal.ParameterDescriptor CreateParameterDescriptor_RegisterMutation_input()
 1115                => new HotChocolate.Internal.ParameterDescriptor(
 1116                    "input",
 1117                    typeof(global::Api.Auth.Mutations.RegisterInput),
 1118                    isNullable: false,
 1119                    []);
 120
 121            public HotChocolate.Resolvers.FieldResolverDelegates RegisterMutation()
 1122                => new global::HotChocolate.Resolvers.FieldResolverDelegates(resolver: RegisterMutation);
 123
 124            private async global::System.Threading.Tasks.ValueTask<global::System.Object?> RegisterMutation(global::HotC
 1125            {
 1126                var args0 = context.Service<global::Api.Database.PGContext>();
 1127                var args1 = context.Service<global::Api.Database.Utils.IEFTransactionDIAccessorService>();
 1128                var args2 = context.Service<global::FluentValidation.IValidator<global::Api.Auth.Mutations.RegisterInput
 1129                var args3 = _binding_RegisterMutation_input.Execute<global::Api.Auth.Mutations.RegisterInput>(context);
 1130                var args4 = context.RequestAborted;
 1131                var result = await global::Api.Auth.Mutations.RegisterMutations.RegisterMutation(args0, args1, args2, ar
 1132                return result;
 1133            }
 134        }
 135    }
 136}
 137