diff --git a/Alchegos.Gitea.Webhook.csproj b/Alchegos.Gitea.Webhook.csproj index b89cedd..ee6b80a 100644 --- a/Alchegos.Gitea.Webhook.csproj +++ b/Alchegos.Gitea.Webhook.csproj @@ -12,15 +12,12 @@ .dockerignore - - - - - - - - + + + + + diff --git a/AlchegosEventDispatcher.cs b/AlchegosEventDispatcher.cs new file mode 100644 index 0000000..b920816 --- /dev/null +++ b/AlchegosEventDispatcher.cs @@ -0,0 +1,6 @@ +namespace Alchegos.Gitea.Webhook; + +public class AlchegosEventDispatcher +{ + +} \ No newline at end of file diff --git a/GiteaEventDispatcher.cs b/GiteaEventDispatcher.cs new file mode 100644 index 0000000..7b78038 --- /dev/null +++ b/GiteaEventDispatcher.cs @@ -0,0 +1,25 @@ +using System.Text.Json.Nodes; +using Alchegos.Core.Services.RabbitMQ; +using Alchegos.Gitea.Webhook.Handlers; + +namespace Alchegos.Gitea.Webhook; + +public class GiteaEventDispatcher +{ + private Dictionary Handlers { get; set; } = new (StringComparer.OrdinalIgnoreCase) + { + {"push", new PushEventHandler()}, + {"issues", new IssuesEventHandler()}, + {"issue_comment", new IssueCommentEventHandler()}, + {"pull_request", new PullRequestEventHandler()} + }; + + + public async Task DispatchAsync(string eventType, JsonNode payload, IRabbitPublisher publisher) + { + if (Handlers.TryGetValue(eventType, out var handler)) + { + await handler.HandleAsync(payload, publisher); + } + } +} \ No newline at end of file diff --git a/Handlers/IAlchegosEventHandler.cs b/Handlers/IAlchegosEventHandler.cs new file mode 100644 index 0000000..3442df1 --- /dev/null +++ b/Handlers/IAlchegosEventHandler.cs @@ -0,0 +1,6 @@ +namespace Alchegos.Gitea.Webhook.Handlers; + +public interface IAlchegosEventHandler : IWebhookEventHandler +{ + +} \ No newline at end of file diff --git a/Handlers/IGiteaEventHandler.cs b/Handlers/IGiteaEventHandler.cs new file mode 100644 index 0000000..3eec880 --- /dev/null +++ b/Handlers/IGiteaEventHandler.cs @@ -0,0 +1,6 @@ +namespace Alchegos.Gitea.Webhook.Handlers; + +public interface IGiteaEventHandler: IWebhookEventHandler +{ + +} \ No newline at end of file diff --git a/Handlers/IWebhookEventHandler.cs b/Handlers/IWebhookEventHandler.cs new file mode 100644 index 0000000..a8bcce9 --- /dev/null +++ b/Handlers/IWebhookEventHandler.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Nodes; +using Alchegos.Core.Services.RabbitMQ; + +namespace Alchegos.Gitea.Webhook.Handlers; + +public interface IWebhookEventHandler +{ + Task HandleAsync(JsonNode payload, IRabbitPublisher publisher); +} \ No newline at end of file diff --git a/Handlers/IssueCommentEventHandler.cs b/Handlers/IssueCommentEventHandler.cs new file mode 100644 index 0000000..827acb5 --- /dev/null +++ b/Handlers/IssueCommentEventHandler.cs @@ -0,0 +1,24 @@ +using System.Text.Json; +using System.Text.Json.Nodes; +using Alchegos.Core.Services.RabbitMQ; + +namespace Alchegos.Gitea.Webhook.Handlers; + +public class IssueCommentEventHandler : IGiteaEventHandler +{ + public async Task HandleAsync(JsonNode payload, IRabbitPublisher publisher) + { + if (payload["action"]?.ToString() != "created") + return; + + var message = new Dictionary + { + {"repo_url", payload["repository"]?["url"]?.ToString()}, + {"repo_owner", payload["repository"]?["owner"]?["login"]?.ToString()}, + {"repo_name", payload["repository"]?["name"]?.ToString()}, + {"issue_id", payload["issue"]?["id"]?.ToString()}, + {"comment_id", payload["comment"]?["id"]?.ToString()}, + }; + await publisher.PublishAsync("alchegos", "issue_commented", JsonSerializer.Serialize(message)); + } +} diff --git a/Handlers/IssuesEventHandler.cs b/Handlers/IssuesEventHandler.cs new file mode 100644 index 0000000..da55969 --- /dev/null +++ b/Handlers/IssuesEventHandler.cs @@ -0,0 +1,50 @@ +using System.Text.Json; +using System.Text.Json.Nodes; +using Alchegos.Core.Services.RabbitMQ; + +namespace Alchegos.Gitea.Webhook.Handlers; + +public class IssuesEventHandler : IGiteaEventHandler +{ + public async Task HandleAsync(JsonNode payload, IRabbitPublisher publisher) + { + string action = payload["action"]?.ToString() ?? ""; + string repo_url = payload["repository"]?["url"]?.ToString(); + string repo_owner = payload["repository"]?["owner"]?["login"]?.ToString(); + string repo_name = payload["repository"]?["name"]?.ToString(); + string issue_url = payload["issue"]?["url"]?.ToString(); + string issue_id = payload["issue"]?["id"]?.ToString(); + + var labels = payload["issue"]?["labels"]?.AsArray() ?? new JsonArray(); + + var routingKeys = new Dictionary + { + {"status/ready", "ready_to_develop"}, + {"status/completed", "task_completed"} + }; + + if (action is "opened" or "label_updated") + { + foreach (var label in labels) + { + string labelName = label["name"]?.ToString().Trim() ?? label.ToString().Trim(); + if (routingKeys.TryGetValue(labelName, out string routingKey)) + { + var message = new Dictionary + { + { "repo_url", repo_url }, + { "repo_owner", repo_owner }, + { "repo_name", repo_name }, + { "issue_url", issue_url }, + { "issue_id", issue_id }, + }; + await publisher.PublishAsync( + exchange: "alchegos", + routingKey: routingKey, + message: JsonSerializer.Serialize(message)); + break; + } + } + } + } +} diff --git a/Handlers/ProjectPlanEventHandler.cs b/Handlers/ProjectPlanEventHandler.cs new file mode 100644 index 0000000..adcf791 --- /dev/null +++ b/Handlers/ProjectPlanEventHandler.cs @@ -0,0 +1,32 @@ +using System.Text.Json; +using System.Text.Json.Nodes; +using Alchegos.Core.Services.RabbitMQ; + +namespace Alchegos.Gitea.Webhook.Handlers; + +public class ProjectPlanEventHandler : IGiteaEventHandler +{ + public async Task HandleAsync(JsonNode payload, IRabbitPublisher publisher) + { + string projectName = payload["title"]?.ToString(); + string action = payload["action"]?.ToString(); + string plan = payload["content"]?.ToString(); + + Dictionary routingKeys = new Dictionary + { + {"create", "new_project_plan_created"}, + {"update", "project_plan_updated"}, + }; + + Dictionary message = new() + { + { "project_name", projectName }, + { "project_plan", plan } + }; + await publisher.PublishAsync( + exchange: "alchegos", + routingKey: routingKeys.GetValueOrDefault(action, ""), + message: JsonSerializer.Serialize(message) + ); + } +} \ No newline at end of file diff --git a/Handlers/PullRequestEventHandler.cs b/Handlers/PullRequestEventHandler.cs new file mode 100644 index 0000000..c79d952 --- /dev/null +++ b/Handlers/PullRequestEventHandler.cs @@ -0,0 +1,49 @@ +using System.Text.Json; +using System.Text.Json.Nodes; +using Alchegos.Core.Services.RabbitMQ; + +namespace Alchegos.Gitea.Webhook.Handlers; + +public class PullRequestEventHandler : IGiteaEventHandler +{ + public async Task HandleAsync(JsonNode payload, IRabbitPublisher publisher) + { + string action = payload["action"]?.ToString() ?? ""; + string repo_url = payload["repository"]?["url"]?.ToString(); + string repo_owner = payload["repository"]?["owner"]?["login"]?.ToString(); + string repo_name = payload["repository"]?["name"]?.ToString(); + string branch = payload["pull_request"]?["head"]?["ref"]?.ToString(); + + JsonArray labels = payload["pull_request"]?["labels"]?.AsArray() ?? new JsonArray(); + + Dictionary routingKeys = new Dictionary + { + {"status/pending_review", "pull_request_pending_review"}, + {"status/pending_test", "pull_request_pending_test"} + }; + + if (action is "opened" or "label_updated") + { + foreach (JsonNode label in labels) + { + string labelName = label["name"]?.ToString().Trim() ?? label.ToString().Trim(); + if (routingKeys.TryGetValue(labelName, out string routingKey)) + { + Dictionary message = new Dictionary + { + {"repo_url", repo_url}, + {"repo_owner", repo_owner}, + {"repo_name", repo_name}, + {"branch", branch} + }; + await publisher.PublishAsync( + exchange: "alchegos", + routingKey: routingKey, + message: JsonSerializer.Serialize(message) + ); + break; + } + } + } + } +} \ No newline at end of file diff --git a/Handlers/PushEventHandler.cs b/Handlers/PushEventHandler.cs new file mode 100644 index 0000000..92631de --- /dev/null +++ b/Handlers/PushEventHandler.cs @@ -0,0 +1,34 @@ +using System.Text.Json; +using System.Text.Json.Nodes; +using Alchegos.Core.Services.RabbitMQ; + +namespace Alchegos.Gitea.Webhook.Handlers; + +public class PushEventHandler : IGiteaEventHandler +{ + public async Task HandleAsync(JsonNode payload, IRabbitPublisher publisher) + { + string branch = payload["ref"]?.ToString()?.Replace("refs/heads/", "") ?? ""; + if (branch == "main") + return; + + JsonArray commits = payload["commits"]?.AsArray() ?? new JsonArray(); + foreach (var commit in commits) + { + if (commit["message"]?.ToString()?.Trim() == "init") + { + Dictionary message = new Dictionary + { + {"repo_url", payload["repository"]?["url"]?.ToString()}, + {"repo_owner", payload["repository"]?["owner"]?["login"]?.ToString()}, + {"repo_name", payload["repository"]?["name"]?.ToString()} + }; + await publisher.PublishAsync( + exchange: "alchegos", + routingKey:"project_initialized", + message: JsonSerializer.Serialize(message)); + break; + } + } + } +} diff --git a/Models/GiteaWebhookPayload.cs b/Models/GiteaWebhookPayload.cs deleted file mode 100644 index 1d66fa9..0000000 --- a/Models/GiteaWebhookPayload.cs +++ /dev/null @@ -1,39 +0,0 @@ -namespace Alchegos.Gitea.Webhook.Models; -using System.Text.Json.Serialization; - -public class GiteaWebhookPayload -{ - [JsonPropertyName("repository")] - public Repository? Repository { get; set; } - - [JsonPropertyName("pusher")] - public User? Pusher { get; set; } - - [JsonPropertyName("commits")] - public List? Commits { get; set; } - - [JsonPropertyName("head_commit")] - public CommitInfo? HeadCommit { get; set; } - -} - -public class Repository -{ - [JsonPropertyName("name")] - public string? Name { get; set; } -} - -public class User -{ - [JsonPropertyName("username")] - public string? UserName { get; set; } -} - -public class CommitInfo -{ - [JsonPropertyName("id")] - public string? Id { get; set; } - - [JsonPropertyName("message")] - public string? Message { get; set; } -} diff --git a/Program.cs b/Program.cs index c98edd5..efc018a 100644 --- a/Program.cs +++ b/Program.cs @@ -1,9 +1,11 @@ +using System.Text.Json.Nodes; using Alchegos.Core; using Alchegos.Core.Services.RabbitMQ; +using Alchegos.Gitea.Webhook; GlobalRegistry.Instance.Start(); -var builder = WebApplication.CreateBuilder(args); +WebApplicationBuilder builder = WebApplication.CreateBuilder(args); builder.Logging.ClearProviders(); builder.Logging.AddConsole(); builder.Services.Configure( @@ -14,26 +16,23 @@ builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); -var app = builder.Build(); -var logger = app.Services.GetRequiredService>(); +builder.Services.AddSingleton(); +WebApplication app = builder.Build(); +ILogger logger = app.Services.GetRequiredService>(); logger.LogInformation("Starting web hook /webhook/gitea"); -app.MapPost("/webhook/gitea", async (HttpRequest request, HttpResponse response, IRabbitPublisher publisher) => +app.MapPost("/webhook/gitea", + async ( + HttpRequest request, + HttpResponse response, + IRabbitPublisher publisher, + GiteaEventDispatcher dispatcher + ) => { - logger.LogInformation("Received gitea webhook request"); - using var reader = new StreamReader(request.Body); - var jsonBody = await reader.ReadToEndAsync(); - - var giteaEvent = request.Headers["X-Gitea-Event"].ToString() ?? "unknown"; - - logger.LogInformation($"Received gitea webhook post: {giteaEvent}"); - logger.LogInformation($" {jsonBody}"); - - await publisher.PublishAsync( - exchange: "gitea.webhook.exchange", - routingKey: giteaEvent, - message: jsonBody - ); - response.StatusCode = 200; - await response.WriteAsync(jsonBody); + string jsonBody = await new StreamReader(request.Body).ReadToEndAsync(); + string giteaEvent = request.Headers["X-Gitea-Event"].ToString() ?? "unknown"; + JsonNode payload = JsonNode.Parse(jsonBody); + if(payload is not null) + await dispatcher.DispatchAsync(giteaEvent, payload, publisher); + return Results.Ok(); }); app.Run(); \ No newline at end of file