< Summary

Information
Class: Elsa.Hosting.Management.Services.ConfiguredApplicationInstanceNameProvider
Assembly: Elsa.Hosting.Management
File(s): /home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Hosting.Management/Services/ConfiguredApplicationInstanceNameProvider.cs
Line coverage
97%
Covered lines: 48
Uncovered lines: 1
Coverable lines: 49
Total lines: 118
Line coverage: 97.9%
Branch coverage
94%
Covered branches: 34
Total branches: 36
Branch coverage: 94.4%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
.ctor(...)100%66100%
GetName()100%11100%
ResolveConfiguredInstanceName(...)100%44100%
IsValidConfiguredInstanceName(...)92.85%161480%
IsAsciiLetterOrDigit(...)91.66%1212100%
ShortenConfiguredInstanceName(...)100%11100%

File(s)

/home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Hosting.Management/Services/ConfiguredApplicationInstanceNameProvider.cs

#LineLine coverage
 1using Elsa.Hosting.Management.Contracts;
 2using Elsa.Hosting.Management.Options;
 3using Microsoft.Extensions.Logging;
 4using Microsoft.Extensions.Options;
 5using System.Security.Cryptography;
 6
 7namespace Elsa.Hosting.Management.Services;
 8
 9/// <summary>
 10/// Resolves the application instance name from <see cref="ApplicationInstanceOptions"/>, allowing a
 11/// stable name to be configured so that per-instance transport entities are reused across restarts
 12/// instead of accumulating. Falls back to a random name when no stable name is configured, which
 13/// preserves the previous default behaviour.
 14/// </summary>
 15/// <remarks>
 16/// Resolution order:
 17/// <list type="number">
 18/// <item><description><see cref="ApplicationInstanceOptions.InstanceName"/> when set.</description></item>
 19/// <item><description>The environment variable named by <see cref="ApplicationInstanceOptions.InstanceNameEnvironmentVa
 20/// <item><description>A randomly generated name (legacy behaviour).</description></item>
 21/// </list>
 22/// </remarks>
 23public class ConfiguredApplicationInstanceNameProvider : IApplicationInstanceNameProvider
 24{
 25    internal const int AzureServiceBusSubscriptionNameMaxLength = 50;
 26    internal const string TriggerChangeTokenSignalEndpointNameSuffix = "-elsa-tct";
 27    private const int ShortenedNameHashLength = 16;
 128    internal static readonly int ConfiguredInstanceNameMaxLength = AzureServiceBusSubscriptionNameMaxLength - TriggerCha
 29
 30    private readonly string _instanceName;
 31
 32    /// <summary>
 33    /// Initializes a new instance of the <see cref="ConfiguredApplicationInstanceNameProvider"/> class.
 34    /// </summary>
 2135    public ConfiguredApplicationInstanceNameProvider(
 2136        IOptions<ApplicationInstanceOptions> options,
 2137        RandomIntIdentityGenerator randomIdentityGenerator,
 2138        ILogger<ConfiguredApplicationInstanceNameProvider> logger)
 39    {
 2140        var value = options.Value;
 41
 2142        if (!string.IsNullOrWhiteSpace(value.InstanceName))
 43        {
 1244            _instanceName = ResolveConfiguredInstanceName(value.InstanceName, $"{nameof(ApplicationInstanceOptions)}.{na
 845            return;
 46        }
 47
 948        if (!string.IsNullOrWhiteSpace(value.InstanceNameEnvironmentVariable))
 49        {
 750            var environmentVariable = value.InstanceNameEnvironmentVariable.Trim();
 751            var fromEnvironment = Environment.GetEnvironmentVariable(environmentVariable);
 52
 753            if (!string.IsNullOrWhiteSpace(fromEnvironment))
 54            {
 555                _instanceName = ResolveConfiguredInstanceName(fromEnvironment, $"environment variable '{environmentVaria
 556                return;
 57            }
 58
 259            logger.LogWarning(
 260                "The configured instance-name environment variable '{EnvironmentVariable}' is not set or empty. Falling 
 261                "A random name causes per-instance transport entities (such as the Azure Service Bus change-token subscr
 262                "which can accumulate until the transport's per-topic limit is reached.",
 263                environmentVariable);
 64        }
 65
 466        _instanceName = randomIdentityGenerator.GenerateId();
 467    }
 68
 69    /// <inheritdoc />
 1770    public string GetName() => _instanceName;
 71
 72    private static string ResolveConfiguredInstanceName(string value, string source, ILogger logger)
 73    {
 1774        var instanceName = value.Trim();
 75
 1776        if (!IsValidConfiguredInstanceName(instanceName))
 477            throw new InvalidOperationException(
 478                $"The configured application instance name from {source} contains invalid characters. " +
 479                "Use only letters, numbers, periods, hyphens, or underscores, and start and end the value with a letter 
 80
 1381        if (instanceName.Length <= ConfiguredInstanceNameMaxLength)
 982            return instanceName;
 83
 484        var shortenedName = ShortenConfiguredInstanceName(instanceName);
 85
 486        logger.LogWarning(
 487            "The configured application instance name from {Source} is {Length} characters long, exceeding the {MaxLengt
 488            "Using deterministic shortened instance name '{ShortenedName}' instead.",
 489            source,
 490            instanceName.Length,
 491            ConfiguredInstanceNameMaxLength,
 492            shortenedName);
 93
 494        return shortenedName;
 95    }
 96
 97    private static bool IsValidConfiguredInstanceName(string instanceName)
 98    {
 1799        if (instanceName.Length == 0)
 0100            return false;
 101
 17102        return IsAsciiLetterOrDigit(instanceName[0])
 17103            && IsAsciiLetterOrDigit(instanceName[^1])
 303104            && instanceName.All(c => IsAsciiLetterOrDigit(c) || c is '.' or '-' or '_');
 105    }
 106
 107    private static bool IsAsciiLetterOrDigit(char value) =>
 319108        value is >= 'a' and <= 'z' or >= 'A' and <= 'Z' or >= '0' and <= '9';
 109
 110    private static string ShortenConfiguredInstanceName(string instanceName)
 111    {
 4112        var hash = Convert.ToHexString(SHA256.HashData(System.Text.Encoding.UTF8.GetBytes(instanceName))).ToLowerInvaria
 4113        var prefixLength = ConfiguredInstanceNameMaxLength - hash.Length - 1;
 4114        var prefix = instanceName[..prefixLength];
 115
 4116        return $"{prefix}-{hash}";
 117    }
 118}