< Summary - AsyncResponse (Release / net8.0+net10.0 / unit+integration)

Information
Class: AsyncResponse.Transports.Redis.RedisCorrelationIdExtractor
Assembly: AsyncResponse.Transports.Redis
File(s): /home/runner/work/AsyncResponse/AsyncResponse/src/Transports/AsyncResponse.Transports.Redis/RedisCorrelationIdExtractor.cs
Line coverage
100%
Covered lines: 60
Uncovered lines: 0
Coverable lines: 60
Total lines: 124
Line coverage: 100%
Branch coverage
100%
Covered branches: 48
Total branches: 48
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
Extract(...)100%1414100%
TryReadField(...)100%66100%
TryReadPath(...)100%1212100%
TryGetProperty(...)100%66100%
UnwrapJsonString(...)100%1010100%

File(s)

/home/runner/work/AsyncResponse/AsyncResponse/src/Transports/AsyncResponse.Transports.Redis/RedisCorrelationIdExtractor.cs

#LineLine coverage
 1using StackExchange.Redis;
 2using System.Text.Json;
 3using System.Text.Json.Nodes;
 4
 5namespace AsyncResponse.Transports.Redis;
 6
 7internal static class RedisCorrelationIdExtractor
 8{
 9    /// <summary>Extracts the correlation id from the supplied message.</summary>
 10    public static string? Extract(
 11        StreamEntry entry,
 12        string messageJson,
 13        RedisAsyncResponseTransportOptions options)
 14    {
 315        var field = RedisTransportOptionsValidator.Required(
 316            options.CorrelationIdField,
 317            nameof(options.CorrelationIdField));
 18
 319        var fieldValue = TryReadField(entry, field);
 320        if (!string.IsNullOrWhiteSpace(fieldValue))
 321            return fieldValue;
 22
 323        var jsonPaths = options.CorrelationIdJsonPaths;
 324        if (jsonPaths is null || jsonPaths.Length == 0 || string.IsNullOrWhiteSpace(messageJson))
 325            return null;
 26
 27        JsonNode? root;
 28        try
 29        {
 330            root = JsonNode.Parse(messageJson);
 331        }
 332        catch (JsonException)
 33        {
 334            return null;
 35        }
 36
 337        if (root is null)
 338            return null;
 39
 340        foreach (var path in jsonPaths)
 41        {
 342            var value = TryReadPath(root, path);
 343            if (!string.IsNullOrWhiteSpace(value))
 344                return value;
 45        }
 46
 247        return null;
 348    }
 49
 50    internal static string? TryReadField(StreamEntry entry, string fieldName)
 51    {
 52        // A tombstone left after trimming (an entry still referenced by a PEL but deleted from the
 53        // stream) has null Values; treat it as a missing field rather than throwing.
 354        if (entry.Values is null)
 355            return null;
 56
 357        foreach (var value in entry.Values)
 58        {
 359            if (StringComparer.Ordinal.Equals(value.Name.ToString(), fieldName))
 360                return value.Value.ToString();
 61        }
 62
 363        return null;
 64    }
 65
 66    private static string? TryReadPath(JsonNode root, string path)
 67    {
 368        if (string.IsNullOrWhiteSpace(path))
 369            return null;
 70
 371        var current = root;
 372        foreach (var segment in path.Split('.', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries))
 73        {
 374            current = UnwrapJsonString(current);
 375            if (current is not JsonObject obj)
 376                return null;
 77
 378            current = TryGetProperty(obj, segment);
 379            if (current is null)
 380                return null;
 81        }
 82
 383        current = UnwrapJsonString(current);
 384        return current switch
 385        {
 386            JsonValue value when value.TryGetValue<string>(out var s) => s,
 287            JsonValue value => value.ToString(),
 388            _ => null
 389        };
 90    }
 91
 92    private static JsonNode? TryGetProperty(JsonObject obj, string name)
 93    {
 394        if (obj.TryGetPropertyValue(name, out var exact))
 395            return exact;
 96
 297        foreach (var property in obj)
 98        {
 299            if (string.Equals(property.Key, name, StringComparison.OrdinalIgnoreCase))
 2100                return property.Value;
 101        }
 102
 2103        return null;
 3104    }
 105
 106    private static JsonNode? UnwrapJsonString(JsonNode? node)
 107    {
 3108        if (node is not JsonValue value || !value.TryGetValue<string>(out var text))
 3109            return node;
 110
 3111        var trimmed = text.AsSpan().TrimStart();
 3112        if (trimmed.Length == 0 || (trimmed[0] != '{' && trimmed[0] != '['))
 3113            return node;
 114
 115        try
 116        {
 2117            return JsonNode.Parse(text);
 118        }
 2119        catch (JsonException)
 120        {
 2121            return node;
 122        }
 3123    }
 124}