Files
git.stella-ops.org/src/Integrations/__Plugins/StellaOps.Integrations.Plugin.GitHubApp/CodeScanning/CodeScanningAlert.cs
2026-01-09 18:27:46 +02:00

281 lines
7.3 KiB
C#

// <copyright file="CodeScanningAlert.cs" company="StellaOps">
// Copyright (c) StellaOps. Licensed under the AGPL-3.0-or-later.
// </copyright>
using System.Text.Json.Serialization;
namespace StellaOps.Integrations.Plugin.GitHubApp.CodeScanning;
/// <summary>
/// Code scanning alert from GitHub.
/// Sprint: SPRINT_20260109_010_002 Task: Implement models
/// </summary>
public sealed record CodeScanningAlert
{
/// <summary>
/// Alert number.
/// </summary>
[JsonPropertyName("number")]
public required int Number { get; init; }
/// <summary>
/// Alert state (open, closed, dismissed, fixed).
/// </summary>
[JsonPropertyName("state")]
public required string State { get; init; }
/// <summary>
/// Rule ID that triggered the alert.
/// </summary>
public required string RuleId { get; init; }
/// <summary>
/// Rule severity.
/// </summary>
public required string RuleSeverity { get; init; }
/// <summary>
/// Rule description.
/// </summary>
public required string RuleDescription { get; init; }
/// <summary>
/// Tool that produced the alert.
/// </summary>
public required string Tool { get; init; }
/// <summary>
/// HTML URL to the alert.
/// </summary>
[JsonPropertyName("html_url")]
public required string HtmlUrl { get; init; }
/// <summary>
/// When the alert was created.
/// </summary>
[JsonPropertyName("created_at")]
public required DateTimeOffset CreatedAt { get; init; }
/// <summary>
/// When the alert was dismissed (if applicable).
/// </summary>
[JsonPropertyName("dismissed_at")]
public DateTimeOffset? DismissedAt { get; init; }
/// <summary>
/// Reason for dismissal.
/// </summary>
[JsonPropertyName("dismissed_reason")]
public string? DismissedReason { get; init; }
/// <summary>
/// Who dismissed the alert.
/// </summary>
[JsonPropertyName("dismissed_by")]
public string? DismissedBy { get; init; }
/// <summary>
/// Most recent instance of the alert.
/// </summary>
[JsonPropertyName("most_recent_instance")]
public AlertInstance? MostRecentInstance { get; init; }
/// <summary>
/// Creates alert from GitHub API response.
/// </summary>
public static CodeScanningAlert FromApiResponse(GitHubAlertResponse response) => new()
{
Number = response.Number,
State = response.State ?? "unknown",
RuleId = response.Rule?.Id ?? "unknown",
RuleSeverity = response.Rule?.Severity ?? "unknown",
RuleDescription = response.Rule?.Description ?? "",
Tool = response.Tool?.Name ?? "unknown",
HtmlUrl = response.HtmlUrl ?? "",
CreatedAt = response.CreatedAt,
DismissedAt = response.DismissedAt,
DismissedReason = response.DismissedReason,
DismissedBy = response.DismissedBy?.Login,
MostRecentInstance = response.MostRecentInstance is not null
? AlertInstance.FromApiResponse(response.MostRecentInstance)
: null
};
}
/// <summary>
/// Alert instance location.
/// </summary>
public sealed record AlertInstance
{
/// <summary>
/// Git ref where the alert was found.
/// </summary>
public required string Ref { get; init; }
/// <summary>
/// Analysis key.
/// </summary>
public string? AnalysisKey { get; init; }
/// <summary>
/// Environment (e.g., "production").
/// </summary>
public string? Environment { get; init; }
/// <summary>
/// Location in the code.
/// </summary>
public AlertLocation? Location { get; init; }
/// <summary>
/// Creates instance from API response.
/// </summary>
public static AlertInstance FromApiResponse(GitHubAlertInstanceResponse response) => new()
{
Ref = response.Ref ?? "unknown",
AnalysisKey = response.AnalysisKey,
Environment = response.Environment,
Location = response.Location is not null
? new AlertLocation
{
Path = response.Location.Path ?? "",
StartLine = response.Location.StartLine,
EndLine = response.Location.EndLine,
StartColumn = response.Location.StartColumn,
EndColumn = response.Location.EndColumn
}
: null
};
}
/// <summary>
/// Alert location in source code.
/// </summary>
public sealed record AlertLocation
{
/// <summary>
/// File path.
/// </summary>
public required string Path { get; init; }
/// <summary>
/// Start line.
/// </summary>
public int? StartLine { get; init; }
/// <summary>
/// End line.
/// </summary>
public int? EndLine { get; init; }
/// <summary>
/// Start column.
/// </summary>
public int? StartColumn { get; init; }
/// <summary>
/// End column.
/// </summary>
public int? EndColumn { get; init; }
}
#region GitHub API Response Models
/// <summary>
/// GitHub API alert response.
/// </summary>
public sealed record GitHubAlertResponse
{
[JsonPropertyName("number")]
public int Number { get; init; }
[JsonPropertyName("state")]
public string? State { get; init; }
[JsonPropertyName("rule")]
public GitHubRuleResponse? Rule { get; init; }
[JsonPropertyName("tool")]
public GitHubToolResponse? Tool { get; init; }
[JsonPropertyName("html_url")]
public string? HtmlUrl { get; init; }
[JsonPropertyName("created_at")]
public DateTimeOffset CreatedAt { get; init; }
[JsonPropertyName("dismissed_at")]
public DateTimeOffset? DismissedAt { get; init; }
[JsonPropertyName("dismissed_reason")]
public string? DismissedReason { get; init; }
[JsonPropertyName("dismissed_by")]
public GitHubUserResponse? DismissedBy { get; init; }
[JsonPropertyName("most_recent_instance")]
public GitHubAlertInstanceResponse? MostRecentInstance { get; init; }
}
public sealed record GitHubRuleResponse
{
[JsonPropertyName("id")]
public string? Id { get; init; }
[JsonPropertyName("severity")]
public string? Severity { get; init; }
[JsonPropertyName("description")]
public string? Description { get; init; }
}
public sealed record GitHubToolResponse
{
[JsonPropertyName("name")]
public string? Name { get; init; }
[JsonPropertyName("version")]
public string? Version { get; init; }
}
public sealed record GitHubUserResponse
{
[JsonPropertyName("login")]
public string? Login { get; init; }
}
public sealed record GitHubAlertInstanceResponse
{
[JsonPropertyName("ref")]
public string? Ref { get; init; }
[JsonPropertyName("analysis_key")]
public string? AnalysisKey { get; init; }
[JsonPropertyName("environment")]
public string? Environment { get; init; }
[JsonPropertyName("location")]
public GitHubLocationResponse? Location { get; init; }
}
public sealed record GitHubLocationResponse
{
[JsonPropertyName("path")]
public string? Path { get; init; }
[JsonPropertyName("start_line")]
public int? StartLine { get; init; }
[JsonPropertyName("end_line")]
public int? EndLine { get; init; }
[JsonPropertyName("start_column")]
public int? StartColumn { get; init; }
[JsonPropertyName("end_column")]
public int? EndColumn { get; init; }
}
#endregion