< Summary - Heimdall API

Information
Class: ArturRios.Heimdall.Command.Services.EmailVerificationService
Assembly: ArturRios.Heimdall.Command
File(s): /home/runner/work/heimdall-api/heimdall-api/src/Application/ArturRios.Heimdall.Command/Services/EmailVerificationService.cs
Line coverage
100%
Covered lines: 28
Uncovered lines: 0
Coverable lines: 28
Total lines: 60
Line coverage: 100%
Branch coverage
100%
Covered branches: 2
Total branches: 2
Branch coverage: 100%
Method coverage
100%
Covered methods: 2
Fully covered methods: 2
Total methods: 2
Method coverage: 100%
Full method coverage: 100%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
IssueAndSendAsync()100%22100%

File(s)

/home/runner/work/heimdall-api/heimdall-api/src/Application/ArturRios.Heimdall.Command/Services/EmailVerificationService.cs

#LineLine coverage
 1using ArturRios.Data.Relational.Core.Interfaces;
 2using ArturRios.Heimdall.Domain.Entities;
 3using ArturRios.Util.Random;
 4using Microsoft.EntityFrameworkCore;
 5
 6namespace ArturRios.Heimdall.Command.Services;
 7
 8/// <summary>
 9///     Default <see cref="IEmailVerificationService" />: builds a random, time-limited
 10///     <see cref="EmailVerificationToken" />, persists it, then delegates delivery to the configured
 11///     <see cref="IEmailVerificationSender" />. A send failure does not undo the created person — the
 12///     persisted token can be re-sent later (UC-15).
 13/// </summary>
 14/// <remarks>
 15///     The token matches <see cref="PasswordResetService" />: 48 letters and digits drawn from
 16///     <c>RandomNumberGenerator</c> by <c>CustomRandom.Text</c>. As of ArturRios.Util 1.5.0 the
 17///     character-set flags are honoured for every character, so the token is alphanumeric — and
 18///     therefore URL-safe — throughout.
 19/// </remarks>
 13720public class EmailVerificationService(
 13721    IAsyncRepository<EmailVerificationToken, long> tokenWriter,
 13722    IAsyncReadOnlyRepository<Scope, long> scopeReader,
 13723    IEmailVerificationSender sender,
 13724    EmailVerificationOptions options)
 25    : IEmailVerificationService
 26{
 27    public async Task IssueAndSendAsync(Person person)
 28    {
 2929        var token = CustomRandom.Text(new RandomStringOptions
 2930        {
 2931            Length = 48,
 2932            IncludeLowercase = true,
 2933            IncludeUppercase = true,
 2934            IncludeDigits = true,
 2935            IncludeSpecialCharacters = false
 2936        });
 37
 2938        await tokenWriter.CreateAsync(new EmailVerificationToken
 2939        {
 2940            PersonId = person.Id,
 2941            TokenHash = SingleUseTokenHash.Of(token),
 2942            ExpiresAt = DateTime.UtcNow.Add(options.TokenLifetime),
 2943            Used = false
 2944        });
 45
 46        // The person's scope may point the link at its own client application. An administrator has
 47        // no scope, and a scope with no link set leaves this null: both take the global setting.
 2948        string? linkBase = null;
 49
 2950        if (person.ScopeId is { } scopeId)
 51        {
 1152            linkBase = await scopeReader.Query()
 1153                .Where(scope => scope.Id == scopeId)
 1154                .Select(scope => scope.EmailVerificationUrl)
 1155                .FirstOrDefaultAsync();
 56        }
 57
 2958        await sender.SendAsync(person.Email, token, linkBase);
 2959    }
 60}