From b93ecb2ee32b79a396e38ab9d9c2bd7f55128284 Mon Sep 17 00:00:00 2001 From: Maxwell Weru Date: Tue, 19 Sep 2023 16:07:49 +0300 Subject: [PATCH] Use controllers for update_jobs/{id}/* --- .../Controllers/UpdateJobsController.cs | 117 ++++++++++++++++++ .../Controllers/WebhooksController.cs | 2 +- .../Models/PayloadWithData.cs | 12 ++ server/Tingle.Dependabot/Program.cs | 95 -------------- 4 files changed, 130 insertions(+), 96 deletions(-) create mode 100644 server/Tingle.Dependabot/Controllers/UpdateJobsController.cs create mode 100644 server/Tingle.Dependabot/Models/PayloadWithData.cs diff --git a/server/Tingle.Dependabot/Controllers/UpdateJobsController.cs b/server/Tingle.Dependabot/Controllers/UpdateJobsController.cs new file mode 100644 index 00000000..60ef8e3a --- /dev/null +++ b/server/Tingle.Dependabot/Controllers/UpdateJobsController.cs @@ -0,0 +1,117 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using System.ComponentModel.DataAnnotations; +using System.Text.Json; +using System.Text.Json.Nodes; +using Tingle.Dependabot.Events; +using Tingle.Dependabot.Models; +using Tingle.Dependabot.Models.Dependabot; +using Tingle.Dependabot.Models.Management; +using Tingle.EventBus; + +namespace Tingle.Dependabot.Controllers; + +[ApiController] +[Route("/update_jobs")] +[Authorize(AuthConstants.PolicyNameUpdater)] +public class UpdateJobsController : ControllerBase // TODO: unit and integration test this +{ + private readonly MainDbContext dbContext; + private readonly IEventPublisher publisher; + private readonly ILogger logger; + + public UpdateJobsController(MainDbContext dbContext, IEventPublisher publisher, ILogger logger) + { + this.dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext)); + this.publisher = publisher ?? throw new ArgumentNullException(nameof(publisher)); + this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } + + // TODO: implement logic for *pull_request endpoints + + [HttpPost("{id}/create_pull_request")] + public async Task CreatePullRequestAsync([FromRoute, Required] string id, [FromBody] PayloadWithData model) + { + var job = await dbContext.UpdateJobs.SingleAsync(p => p.Id == id); + logger.LogInformation("Received request to create a pull request from job {JobId} but we did nothing.\r\n{ModelJson}", id, JsonSerializer.Serialize(model)); + return Ok(); + } + + [HttpPost("{id}/update_pull_request")] + public async Task UpdatePullRequestAsync([FromRoute, Required] string id, [FromBody] PayloadWithData model) + { + var job = await dbContext.UpdateJobs.SingleAsync(p => p.Id == id); + logger.LogInformation("Received request to update a pull request from job {JobId} but we did nothing.\r\n{ModelJson}", id, JsonSerializer.Serialize(model)); + return Ok(); + } + + [HttpPost("{id}/close_pull_request")] + public async Task ClosePullRequestAsync([FromRoute, Required] string id, [FromBody] PayloadWithData model) + { + var job = await dbContext.UpdateJobs.SingleAsync(p => p.Id == id); + logger.LogInformation("Received request to close a pull request from job {JobId} but we did nothing.\r\n{ModelJson}", id, JsonSerializer.Serialize(model)); + return Ok(); + } + + [HttpPost("{id}/record_update_job_error")] + public async Task RecordErrorAsync([FromRoute, Required] string id, [FromBody] PayloadWithData model) + { + var job = await dbContext.UpdateJobs.SingleAsync(p => p.Id == id); + + job.Error = new UpdateJobError + { + Type = model.Data!.ErrorType, + Detail = model.Data.ErrorDetail, + }; + + await dbContext.SaveChangesAsync(); + + return Ok(); + } + + [HttpPatch("{id}/mark_as_processed")] + public async Task MarkAsProcessedAsync([FromRoute, Required] string id, [FromBody] PayloadWithData model) + { + var job = await dbContext.UpdateJobs.SingleAsync(p => p.Id == id); + + // publish event that will run update the job and collect logs + var evt = new UpdateJobCheckStateEvent { JobId = id, }; + await publisher.PublishAsync(evt); + + return Ok(); + } + + [HttpPost("{id}/update_dependency_list")] + public async Task UpdateDependencyListAsync([FromRoute, Required] string id, [FromBody] PayloadWithData model) + { + var job = await dbContext.UpdateJobs.SingleAsync(p => p.Id == id); + var repository = await dbContext.Repositories.SingleAsync(r => r.Id == job.RepositoryId); + + // update the database + var update = repository.Updates.SingleOrDefault(u => u.PackageEcosystem == job.PackageEcosystem && u.Directory == job.Directory); + if (update is not null) + { + update.Files = model.Data?.DependencyFiles ?? new(); + } + await dbContext.SaveChangesAsync(); + + return Ok(); + } + + [HttpPost("{id}/record_ecosystem_versions")] + public async Task RecordEcosystemVersionsAsync([FromRoute, Required] string id, [FromBody] JsonNode model) + { + var job = await dbContext.UpdateJobs.SingleAsync(p => p.Id == id); + logger.LogInformation("Received request to record ecosystem version from job {JobId} but we did nothing.\r\n{ModelJson}", id, model.ToJsonString()); + return Ok(); + } + + [HttpPost("{id}/increment_metric")] + public async Task IncrementMetricAsync([FromRoute, Required] string id, [FromBody] JsonNode model) + { + var job = await dbContext.UpdateJobs.SingleAsync(p => p.Id == id); + logger.LogInformation("Received metrics from job {JobId} but we did nothing with them.\r\n{ModelJson}", id, model.ToJsonString()); + return Ok(); + } +} diff --git a/server/Tingle.Dependabot/Controllers/WebhooksController.cs b/server/Tingle.Dependabot/Controllers/WebhooksController.cs index 89881ece..d2b1fe12 100644 --- a/server/Tingle.Dependabot/Controllers/WebhooksController.cs +++ b/server/Tingle.Dependabot/Controllers/WebhooksController.cs @@ -10,7 +10,7 @@ namespace Tingle.Dependabot.Controllers; [ApiController] [Route("/webhooks")] [Authorize(AuthConstants.PolicyNameServiceHooks)] -public class WebhooksController : ControllerBase +public class WebhooksController : ControllerBase // TODO: unit test this { private readonly IEventPublisher publisher; private readonly ILogger logger; diff --git a/server/Tingle.Dependabot/Models/PayloadWithData.cs b/server/Tingle.Dependabot/Models/PayloadWithData.cs new file mode 100644 index 00000000..962148bd --- /dev/null +++ b/server/Tingle.Dependabot/Models/PayloadWithData.cs @@ -0,0 +1,12 @@ +using System.ComponentModel.DataAnnotations; + +namespace Tingle.Dependabot.Models; + +public class PayloadWithData where T : new() +{ + [Required] + public T? Data { get; set; } + + [System.Text.Json.Serialization.JsonExtensionData] + public Dictionary? Extensions { get; set; } +} diff --git a/server/Tingle.Dependabot/Program.cs b/server/Tingle.Dependabot/Program.cs index 8de6e3ca..100d597f 100644 --- a/server/Tingle.Dependabot/Program.cs +++ b/server/Tingle.Dependabot/Program.cs @@ -117,7 +117,6 @@ app.MapHealthChecks("/liveness", new HealthCheckOptions { Predicate = _ => false, }); app.MapControllers(); app.MapManagementApi(); -app.MapUpdateJobsApi(); // setup the application environment await AppSetup.SetupAsync(app); @@ -227,98 +226,4 @@ public static IEndpointRouteBuilder MapManagementApi(this IEndpointRouteBuilder return builder; } - - public static IEndpointRouteBuilder MapUpdateJobsApi(this IEndpointRouteBuilder builder) - { - var logger = builder.ServiceProvider.GetRequiredService().CreateLogger("UpdateJobsApi"); - - // endpoints accessed by the updater during execution - - var group = builder.MapGroup("update_jobs"); - group.RequireAuthorization(AuthConstants.PolicyNameUpdater); - - // TODO: implement logic for *pull_request endpoints - group.MapPost("/{id}/create_pull_request", async (MainDbContext dbContext, [FromRoute, Required] string id, [FromBody] PayloadWithData model) => - { - var job = await dbContext.UpdateJobs.SingleAsync(p => p.Id == id); - logger.LogInformation("Received request to create a pull request from job {JobId} but we did nothing.\r\n{ModelJson}", id, JsonSerializer.Serialize(model)); - return Results.Ok(); - }); - group.MapPost("/{id}/update_pull_request", async (MainDbContext dbContext, [FromRoute, Required] string id, [FromBody] PayloadWithData model) => - { - var job = await dbContext.UpdateJobs.SingleAsync(p => p.Id == id); - logger.LogInformation("Received request to update a pull request from job {JobId} but we did nothing.\r\n{ModelJson}", id, JsonSerializer.Serialize(model)); - return Results.Ok(); - }); - group.MapPost("/{id}/close_pull_request", async (MainDbContext dbContext, [FromRoute, Required] string id, [FromBody] PayloadWithData model) => - { - var job = await dbContext.UpdateJobs.SingleAsync(p => p.Id == id); - logger.LogInformation("Received request to close a pull request from job {JobId} but we did nothing.\r\n{ModelJson}", id, JsonSerializer.Serialize(model)); - return Results.Ok(); - }); - - group.MapPost("/{id}/record_update_job_error", async (MainDbContext dbContext, [FromRoute, Required] string id, [FromBody] PayloadWithData model) => - { - var job = await dbContext.UpdateJobs.SingleAsync(p => p.Id == id); - - job.Error = new UpdateJobError - { - Type = model.Data!.ErrorType, - Detail = model.Data.ErrorDetail, - }; - - await dbContext.SaveChangesAsync(); - - return Results.Ok(); - }); - group.MapPatch("/{id}/mark_as_processed", async (IEventPublisher publisher, MainDbContext dbContext, [FromRoute, Required] string id, [FromBody] PayloadWithData model) => - { - var job = await dbContext.UpdateJobs.SingleAsync(p => p.Id == id); - - // publish event that will run update the job and collect logs - var evt = new UpdateJobCheckStateEvent { JobId = id, }; - await publisher.PublishAsync(evt); - - return Results.Ok(); - }); - group.MapPost("/{id}/update_dependency_list", async (MainDbContext dbContext, [FromRoute, Required] string id, [FromBody] PayloadWithData model) => - { - var job = await dbContext.UpdateJobs.SingleAsync(p => p.Id == id); - var repository = await dbContext.Repositories.SingleAsync(r => r.Id == job.RepositoryId); - - // update the database - var update = repository.Updates.SingleOrDefault(u => u.PackageEcosystem == job.PackageEcosystem && u.Directory == job.Directory); - if (update is not null) - { - update.Files = model.Data?.DependencyFiles ?? new(); - } - await dbContext.SaveChangesAsync(); - - return Results.Ok(); - }); - - group.MapPost("/{id}/record_ecosystem_versions", async (MainDbContext dbContext, [FromRoute, Required] string id, [FromBody] JsonNode model) => - { - var job = await dbContext.UpdateJobs.SingleAsync(p => p.Id == id); - logger.LogInformation("Received request to record ecosystem version from job {JobId} but we did nothing.\r\n{ModelJson}", id, model.ToJsonString()); - return Results.Ok(); - }); - group.MapPost("/{id}/increment_metric", async (MainDbContext dbContext, [FromRoute, Required] string id, [FromBody] JsonNode model) => - { - var job = await dbContext.UpdateJobs.SingleAsync(p => p.Id == id); - logger.LogInformation("Received metrics from job {JobId} but we did nothing with them.\r\n{ModelJson}", id, model.ToJsonString()); - return Results.Ok(); - }); - - return builder; - } - - public class PayloadWithData where T : new() - { - [Required] - public T? Data { get; set; } - - [System.Text.Json.Serialization.JsonExtensionData] - public Dictionary? Extensions { get; set; } - } }