< Summary - Kestrun — Combined Coverage

Information
Class: Kestrun.Callback.CallbackPlan
Assembly: Kestrun
File(s): /home/runner/work/Kestrun/Kestrun/src/CSharp/Kestrun/Callback/CallbackPlanCompiler.cs
Tag: Kestrun/Kestrun@ca54e35c77799b76774b3805b6f075cdbc0c5fbe
Line coverage
100%
Covered lines: 8
Uncovered lines: 0
Coverable lines: 8
Total lines: 133
Line coverage: 100%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100 01/02/2026 - 00:16:25 Line coverage: 100% (8/8) Total lines: 133 Tag: Kestrun/Kestrun@8405dc23b786b9d436fba0d65fb80baa4171e1d0

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
get_CallbackId()100%11100%
get_UrlTemplate()100%11100%
get_Method()100%11100%
get_OperationId()100%11100%
get_PathParams()100%11100%
get_Body()100%11100%

File(s)

/home/runner/work/Kestrun/Kestrun/src/CSharp/Kestrun/Callback/CallbackPlanCompiler.cs

#LineLine coverage
 1using Microsoft.OpenApi;
 2
 3namespace Kestrun.Callback;
 4
 5/// <summary>
 6/// Represents a plan for executing a callback operation.
 7/// </summary>
 8/// <param name="CallbackId">The identifier for the callback</param>
 9/// <param name="UrlTemplate">The URL template for the callback</param>
 10/// <param name="Method"> The HTTP method for the callback</param>
 11/// <param name="OperationId"> The operation identifier for the callback</param>
 12/// <param name="PathParams"> The list of path parameters for the callback</param>
 13/// <param name="Body">The body plan for the callback</param>
 814public sealed record CallbackPlan(
 1115    string CallbackId,                       // "paymentStatus"
 616    string UrlTemplate,                      // "{$request.body#/callbackUrls/status}/v1/payments/{paymentId}/status"
 517    HttpMethod Method,                       // POST
 818    string OperationId,                      // paymentStatusCallback__post__status
 119    IReadOnlyList<CallbackParamPlan> PathParams,
 620    CallbackBodyPlan? Body                  // schema info (ref) + media type
 821);
 22
 23/// <summary>
 24/// Represents an execution plan for a callback, including resolved parameters.
 25/// </summary>
 26/// <param name="CallbackId">The identifier for the callback</param>
 27/// <param name="Plan">The callback plan</param>
 28/// <param name="BodyParameterName">The name of the body parameter, if any</param>
 29/// <param name="Parameters">The resolved parameters for the callback</param>
 30public sealed record CallBackExecutionPlan(
 31    string CallbackId,
 32    CallbackPlan Plan,
 33    string? BodyParameterName,
 34    Dictionary<string, object?> Parameters
 35);
 36
 37/// <summary>
 38/// Represents a plan for a callback parameter.
 39/// </summary>
 40/// <param name="Name">The Parameter name</param>
 41/// <param name="Location">The location of the parameter (e.g., "path")</param>
 42public sealed record CallbackParamPlan(
 43    string Name,                             // "paymentId"
 44    string Location                         // "path" (keep string; or enum)
 45
 46);
 47
 48/// <summary>
 49/// Represents a plan for the body of a callback operation.
 50/// </summary>
 51/// <param name="MediaType">The media type of the callback body</param>
 52public sealed record CallbackBodyPlan(
 53    string MediaType                        // "application/json"
 54);
 55internal static class CallbackPlanCompiler
 56{
 57    internal static IReadOnlyList<CallbackPlan> Compile(OpenApiCallback callback, string callbackId)
 58    {
 59        ArgumentNullException.ThrowIfNull(callback);
 60        ArgumentNullException.ThrowIfNull(callbackId);
 61
 62        var plans = new List<CallbackPlan>();
 63
 64        if (callback.PathItems is null || callback.PathItems.Count == 0)
 65        {
 66            return plans;
 67        }
 68
 69        foreach (var (expr, pathItemIntf) in callback.PathItems)
 70        {
 71            if (pathItemIntf is not OpenApiPathItem pathItem || pathItem.Operations is null)
 72            {
 73                continue;
 74            }
 75
 76            foreach (var (method, op) in pathItem.Operations)
 77            {
 78                if (op is null)
 79                {
 80                    continue;
 81                }
 82
 83                var pathParams = ExtractPathParams(op);
 84                var bodyPlan = ExtractBodyPlan(op);
 85
 86                plans.Add(new CallbackPlan(
 87                    CallbackId: callbackId,
 88                    UrlTemplate: expr.Expression,
 89                    Method: method,
 90                    OperationId: op.OperationId ?? $"{callbackId}__{method.Method.ToLowerInvariant()}",
 91                    PathParams: pathParams,
 92                    Body: bodyPlan
 93                ));
 94            }
 95        }
 96
 97        return plans;
 98    }
 99
 100    private static IReadOnlyList<CallbackParamPlan> ExtractPathParams(OpenApiOperation op)
 101    {
 102        return op.Parameters is null || op.Parameters.Count == 0
 103            ? []
 104            : [.. op.Parameters
 105            .Where(p => p is not null && string.Equals(p.In?.ToString(), "path", StringComparison.OrdinalIgnoreCase))
 106            .Select(p => new CallbackParamPlan(
 107                Name: p.Name ?? "",
 108                Location: "path"
 109
 110            ))
 111            .Where(p => !string.IsNullOrWhiteSpace(p.Name))];
 112    }
 113
 114    private static CallbackBodyPlan? ExtractBodyPlan(OpenApiOperation op)
 115    {
 116        var rb = op.RequestBody;
 117        if (rb?.Content is null || rb.Content.Count == 0)
 118        {
 119            return null;
 120        }
 121
 122        // Prefer application/json if present
 123        var kv = rb.Content.FirstOrDefault(c => string.Equals(c.Key, "application/json", StringComparison.OrdinalIgnoreC
 124        if (string.IsNullOrEmpty(kv.Key))
 125        {
 126            // otherwise take first
 127            kv = rb.Content.First();
 128        }
 129
 130        var mediaType = kv.Key;
 131        return new CallbackBodyPlan(MediaType: mediaType);
 132    }
 133}