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

Information
Class: AsyncResponse.Transports.Kafka.KafkaCorrelationIdExtractor
Assembly: AsyncResponse.Transports.Kafka
File(s): /home/runner/work/AsyncResponse/AsyncResponse/src/Transports/AsyncResponse.Transports.Kafka/KafkaCorrelationIdExtractor.cs
Line coverage
100%
Covered lines: 59
Uncovered lines: 0
Coverable lines: 59
Total lines: 119
Line coverage: 100%
Branch coverage
100%
Covered branches: 46
Total branches: 46
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%
TryReadHeader(...)100%44100%
TryReadPath(...)100%1212100%
TryGetProperty(...)100%66100%
UnwrapJsonString(...)100%1010100%

File(s)

/home/runner/work/AsyncResponse/AsyncResponse/src/Transports/AsyncResponse.Transports.Kafka/KafkaCorrelationIdExtractor.cs

#LineLine coverage
 1using System.Text.Json;
 2using System.Text.Json.Nodes;
 3
 4namespace AsyncResponse.Transports.Kafka;
 5
 6internal static class KafkaCorrelationIdExtractor
 7{
 8    /// <summary>Extracts the correlation id from the supplied message.</summary>
 9    public static string? Extract(
 10        IReadOnlyList<KafkaTransportHeader> headers,
 11        string messageJson,
 12        KafkaAsyncResponseTransportOptions options)
 13    {
 314        var headerName = KafkaTransportOptionsValidator.Required(
 315            options.CorrelationIdHeader,
 316            nameof(options.CorrelationIdHeader));
 17
 318        var headerValue = TryReadHeader(headers, headerName);
 319        if (!string.IsNullOrWhiteSpace(headerValue))
 320            return headerValue;
 21
 322        var jsonPaths = options.CorrelationIdJsonPaths;
 323        if (jsonPaths is null || jsonPaths.Length == 0 || string.IsNullOrWhiteSpace(messageJson))
 324            return null;
 25
 26        JsonNode? root;
 27        try
 28        {
 329            root = JsonNode.Parse(messageJson);
 330        }
 331        catch (JsonException)
 32        {
 333            return null;
 34        }
 35
 336        if (root is null)
 337            return null;
 38
 339        foreach (var path in jsonPaths)
 40        {
 341            var value = TryReadPath(root, path);
 342            if (!string.IsNullOrWhiteSpace(value))
 343                return value;
 44        }
 45
 246        return null;
 347    }
 48
 49    internal static string? TryReadHeader(IReadOnlyList<KafkaTransportHeader> headers, string headerName)
 50    {
 51        // Kafka headers allow duplicate keys; the first match wins, mirroring broker tooling.
 352        foreach (var header in headers)
 53        {
 354            if (StringComparer.Ordinal.Equals(header.Key, headerName))
 355                return header.ValueUtf8;
 56        }
 57
 358        return null;
 359    }
 60
 61    private static string? TryReadPath(JsonNode root, string path)
 62    {
 363        if (string.IsNullOrWhiteSpace(path))
 364            return null;
 65
 366        var current = root;
 367        foreach (var segment in path.Split('.', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries))
 68        {
 369            current = UnwrapJsonString(current);
 370            if (current is not JsonObject obj)
 371                return null;
 72
 373            current = TryGetProperty(obj, segment);
 374            if (current is null)
 375                return null;
 76        }
 77
 378        current = UnwrapJsonString(current);
 379        return current switch
 380        {
 381            JsonValue value when value.TryGetValue<string>(out var s) => s,
 282            JsonValue value => value.ToString(),
 383            _ => null
 384        };
 85    }
 86
 87    private static JsonNode? TryGetProperty(JsonObject obj, string name)
 88    {
 389        if (obj.TryGetPropertyValue(name, out var exact))
 390            return exact;
 91
 292        foreach (var property in obj)
 93        {
 294            if (string.Equals(property.Key, name, StringComparison.OrdinalIgnoreCase))
 295                return property.Value;
 96        }
 97
 298        return null;
 399    }
 100
 101    private static JsonNode? UnwrapJsonString(JsonNode? node)
 102    {
 3103        if (node is not JsonValue value || !value.TryGetValue<string>(out var text))
 3104            return node;
 105
 3106        var trimmed = text.AsSpan().TrimStart();
 3107        if (trimmed.Length == 0 || (trimmed[0] != '{' && trimmed[0] != '['))
 3108            return node;
 109
 110        try
 111        {
 2112            return JsonNode.Parse(text);
 113        }
 2114        catch (JsonException)
 115        {
 2116            return node;
 117        }
 3118    }
 119}