Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions SS14.Labeller.Tests/GitHubApi/GithubRetryHandlerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,14 @@
_mockInnerHandler.Send(Arg.Any<HttpRequestMessage>(), Arg.Any<CancellationToken>())
.Returns(Task.FromResult(new HttpResponseMessage { StatusCode = HttpStatusCode.OK }));

var handler = new GithubRetryHandler(_mockInnerHandler, _config, _logger);
var handler = new GithubRetryHandler(_config, _logger) { InnerHandler = _mockInnerHandler };
var httpClient = new HttpClient(handler);

// Act
var result = httpClient.SendAsync(_httpRequestMessage, default).Result;

// Assert
Assert.AreEqual(HttpStatusCode.OK, result.StatusCode);

Check warning on line 49 in SS14.Labeller.Tests/GitHubApi/GithubRetryHandlerTests.cs

View workflow job for this annotation

GitHub Actions / build

Consider using the constraint model, Assert.That(actual, Is.EqualTo(expected)), instead of the classic model, Assert.AreEqual(expected, actual) (https://github.com/nunit/nunit.analyzers/tree/master/documentation/NUnit2005.md)
}

[Test]
Expand All @@ -59,14 +59,14 @@
_=> Task.FromResult(new HttpResponseMessage { StatusCode = HttpStatusCode.OK })
);

var handler = new GithubRetryHandler(_mockInnerHandler, _config, _logger);
var handler = new GithubRetryHandler(_config, _logger) { InnerHandler = _mockInnerHandler };
var httpClient = new HttpClient(handler);

// Act
var result = httpClient.SendAsync(_httpRequestMessage, default).Result;

// Assert
Assert.AreEqual(HttpStatusCode.OK, result.StatusCode);

Check warning on line 69 in SS14.Labeller.Tests/GitHubApi/GithubRetryHandlerTests.cs

View workflow job for this annotation

GitHub Actions / build

Consider using the constraint model, Assert.That(actual, Is.EqualTo(expected)), instead of the classic model, Assert.AreEqual(expected, actual) (https://github.com/nunit/nunit.analyzers/tree/master/documentation/NUnit2005.md)
}

[Test]
Expand All @@ -87,7 +87,7 @@
_mockInnerHandler.Send(Arg.Any<HttpRequestMessage>(), Arg.Any<CancellationToken>())
.Returns(response1, response2);

var handler = new GithubRetryHandler(_mockInnerHandler, _config, _logger);
var handler = new GithubRetryHandler(_config, _logger) { InnerHandler = _mockInnerHandler };
var httpClient = new HttpClient(handler);

// Act
Expand All @@ -108,7 +108,7 @@

_gitHubConfig.MaxRetryAttempt = 2;

var handler = new GithubRetryHandler(_mockInnerHandler, _config, _logger);
var handler = new GithubRetryHandler(_config, _logger) { InnerHandler = _mockInnerHandler };
var httpClient = new HttpClient(handler);

// Act & Assert
Expand All @@ -128,7 +128,7 @@
return Send(request, cancellationToken);
}

public virtual Task<HttpResponseMessage> Send(HttpRequestMessage request, CancellationToken cancellationToken)

Check warning on line 131 in SS14.Labeller.Tests/GitHubApi/GithubRetryHandlerTests.cs

View workflow job for this annotation

GitHub Actions / build

'MockHttpMessageHandler.Send(HttpRequestMessage, CancellationToken)' hides inherited member 'HttpMessageHandler.Send(HttpRequestMessage, CancellationToken)'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword.

Check warning on line 131 in SS14.Labeller.Tests/GitHubApi/GithubRetryHandlerTests.cs

View workflow job for this annotation

GitHub Actions / build

'MockHttpMessageHandler.Send(HttpRequestMessage, CancellationToken)' hides inherited member 'HttpMessageHandler.Send(HttpRequestMessage, CancellationToken)'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword.
{
throw new NotImplementedException();
}
Expand Down
4 changes: 3 additions & 1 deletion SS14.Labeller/DiscourseApi/DummyDiscourseClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

namespace SS14.Labeller.DiscourseApi;

public class DummyDiscourseClient : IDiscourseClient
#pragma warning disable CS9113 // Parameter is unread.
public class DummyDiscourseClient(HttpClient _) : IDiscourseClient
#pragma warning restore CS9113 // Parameter is unread.
{
public Task<DiscourseCreatedPost> CreateTopic(int category, string body, string title, CancellationToken ct)
=> Task.FromResult(new DiscourseCreatedPost()
Expand Down
5 changes: 2 additions & 3 deletions SS14.Labeller/Endpoints/GithubWebhook.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Mvc;
using SS14.Labeller.Handlers;
using SS14.Labeller.Middlewares;
using SS14.Labeller.Models;

Expand All @@ -24,10 +23,10 @@ public static void MapGithubWebhook(this WebApplication app)

private static async Task<NoContent> HandleWebhook(
EventBase @event,
[FromServices] GitHubWebhookHandlerService handler,
[FromServices] GenericPublisher publisher,
CancellationToken ct)
{
await handler.Handle(@event, ct);
await publisher.PublishByType(@event, ct);
return TypedResults.NoContent();
}
}
35 changes: 35 additions & 0 deletions SS14.Labeller/GenericPublisher.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using System.Collections.Concurrent;
using MessagePipe;

namespace SS14.Labeller;

public class GenericPublisher(IServiceProvider sp)
{
private static readonly ConcurrentDictionary<Type, Func<object, CancellationToken, ValueTask>> HandlingDelegates = new();

public ValueTask PublishByType(object message, CancellationToken ct)
{
var type = message.GetType();

var handlingDelegate = HandlingDelegates.GetOrAdd(type, (t, state) =>
{
var methodInfo = state.GetType().GetMethod(nameof(Publish));
var targetMethod = methodInfo?.MakeGenericMethod(t);
return (object msg, CancellationToken tkn) =>
{
var result = targetMethod?.Invoke(this, [msg, tkn]);
return result == null
? ValueTask.CompletedTask
: (ValueTask)result;
};
}, this);

return handlingDelegate.Invoke(message, ct);
}

public ValueTask Publish<T>(T message, CancellationToken ct)
{
var publisher = sp.GetRequiredService<IAsyncPublisher<T>>();
return publisher.PublishAsync(message, cancellationToken: ct);
}
}
2 changes: 1 addition & 1 deletion SS14.Labeller/GitHubApi/GithubRetryHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ namespace SS14.Labeller.GitHubApi;
/// <br/> <see href="https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api?apiVersion=2022-11-28">Rate limit information</see>
/// </summary>
/// <remarks> This was designed for the 2022-11-28 version of the API. </remarks>
public sealed class GithubRetryHandler(HttpMessageHandler innerHandler, IOptionsMonitor<GitHubConfig> githubConfig, ILogger<GithubRetryHandler> logger) : DelegatingHandler(innerHandler)
public sealed class GithubRetryHandler(IOptionsMonitor<GitHubConfig> githubConfig, ILogger<GithubRetryHandler> logger) : DelegatingHandler
{
private const int MaxWaitSeconds = 32;

Expand Down
12 changes: 0 additions & 12 deletions SS14.Labeller/Handlers/GitHubWebhookHandlerService.cs

This file was deleted.

15 changes: 8 additions & 7 deletions SS14.Labeller/Handlers/LabelIssueHandler.cs
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
using SS14.Labeller.GitHubApi;
using MessagePipe;
using SS14.Labeller.GitHubApi;
using SS14.Labeller.Labelling.Labels;
using SS14.Labeller.Models;

namespace SS14.Labeller.Handlers;

public class LabelIssueHandler(IGitHubApiClient client) : RequestHandlerBase<IssuesEvent>
public class LabelIssueHandler(IGitHubApiClient client) : IAsyncMessageHandler<IssuesEvent>
{
/// <inheritdoc />
protected override async Task HandleInternal(IssuesEvent request, CancellationToken ct)
public async ValueTask HandleAsync(IssuesEvent message, CancellationToken ct)
{
var action = request.Action;
var action = message.Action;
if (action == "opened")
{
var number = request.Issue.Number;
var labels = request.Issue.Labels;
var number = message.Issue.Number;
var labels = message.Issue.Labels;

if (labels.Length == 0)
await client.AddLabel(request.Repository, number, StatusLabel.Untriaged, ct);
await client.AddLabel(message.Repository, number, StatusLabel.Untriaged, ct);
}
}
}
54 changes: 28 additions & 26 deletions SS14.Labeller/Handlers/LabelPullRequestHandler.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Microsoft.Extensions.FileSystemGlobbing;
using MessagePipe;
using Microsoft.Extensions.FileSystemGlobbing;
using Microsoft.Extensions.Options;
using SS14.Labeller.Configuration;
using SS14.Labeller.DiscourseApi;
Expand All @@ -17,54 +18,55 @@ public class LabelPullRequestHandler(
IDiscourseTopicsRepository topicsRepository,
ILabelManager labelManager,
IOptions<DiscourseConfig> config
) : RequestHandlerBase<PullRequestEvent>
) : IAsyncMessageHandler<PullRequestEvent>
{
private readonly DiscourseConfig _discourseConfig = config.Value;

/// <inheritdoc />
protected override async Task HandleInternal(PullRequestEvent request, CancellationToken ct)
public async ValueTask HandleAsync(PullRequestEvent message, CancellationToken ct)

{
var pr = request.PullRequest;
var pr = message.PullRequest;

var prNumber = pr.Number;

var repoOwner = request.Repository.Owner.Login;
var repoName = request.Repository.Name;
var repoOwner = message.Repository.Owner.Login;
var repoName = message.Repository.Name;

var labels = pr.Labels
.Select(x => x.Name)
.ToArray();

// basic labels
var repository = request.Repository;
var repository = message.Repository;

await (request.EventType switch
await (message.EventType switch
{
PullRequestEventType.Labelled => OnLabelAdd(request, ct, repoOwner, repoName, prNumber, repository),
PullRequestEventType.ClosedRejected => OnClosed(ct, repoOwner, repoName, prNumber),
PullRequestEventType.ClosedMerged => OnMerged(ct, repoOwner, repoName, prNumber, labels, repository),
PullRequestEventType.Opened => OnOpened(request, ct, labels, pr, repository),
PullRequestEventType.ReviewRequested => OnReviewRequested(request, ct, repository),
PullRequestEventType.Labelled => OnLabelAdd(message, repoOwner, repoName, prNumber, repository, ct),
PullRequestEventType.ClosedRejected => OnClosed(repoOwner, repoName, prNumber, ct),
PullRequestEventType.ClosedMerged => OnMerged(repoOwner, repoName, prNumber, labels, repository, ct),
PullRequestEventType.Opened => OnOpened(message, labels, pr, repository, ct),
PullRequestEventType.ReviewRequested => OnReviewRequested(message, repository, ct),
_ => Task.CompletedTask
});

var totalDiff = pr.Additions + pr.Deletions;
if (SizeLabel.TryGetLabelFor(totalDiff, out var sizeLabel))
{
await labelManager.EnsureLabeled(request, sizeLabel, ct);
await labelManager.EnsureLabeled(message, sizeLabel, ct);
}

if(!ContainsLabelsStartingWith(labels, "A:", "T:", "P:"))
await labelManager.EnsureLabeled(request, StatusLabel.Untriaged, ct);
await labelManager.EnsureLabeled(message, StatusLabel.Untriaged, ct);

var changedFiles = await client.GetChangedFiles(repository, prNumber, ct);

await EnsureChangesLabels(ChangesLabel.Sprites, ["**/*.rsi/*.png"], request, changedFiles, ct: ct);
await EnsureChangesLabels(ChangesLabel.Map, ["Resources/Maps/**/*.yml", "Resources/Prototypes/Maps/**/*.yml"], request, changedFiles, ct: ct);
await EnsureChangesLabels(ChangesLabel.Ui, ["**/*.xaml*"], request, changedFiles, ct:ct);
await EnsureChangesLabels(ChangesLabel.Shaders, ["**/*.sws"], request, changedFiles, ct: ct);
await EnsureChangesLabels(ChangesLabel.Audio, ["**/*.ogg"], request, changedFiles, ct: ct);
await EnsureChangesLabels(ChangesLabel.NoCSharp, ["**/*.cs"], request, changedFiles, isInverted: true, ct: ct);
await EnsureChangesLabels(ChangesLabel.Sprites, ["**/*.rsi/*.png"], message, changedFiles, ct: ct);
await EnsureChangesLabels(ChangesLabel.Map, ["Resources/Maps/**/*.yml", "Resources/Prototypes/Maps/**/*.yml"], message, changedFiles, ct: ct);
await EnsureChangesLabels(ChangesLabel.Ui, ["**/*.xaml*"], message, changedFiles, ct: ct);
await EnsureChangesLabels(ChangesLabel.Shaders, ["**/*.sws"], message, changedFiles, ct: ct);
await EnsureChangesLabels(ChangesLabel.Audio, ["**/*.ogg"], message, changedFiles, ct: ct);
await EnsureChangesLabels(ChangesLabel.NoCSharp, ["**/*.cs"], message, changedFiles, isInverted: true, ct: ct);
}

private async Task EnsureChangesLabels(
Expand All @@ -89,7 +91,7 @@ private async Task EnsureChangesLabels(
await labelManager.EnsureNotLabeled(request, label, ct);
}

private async Task OnClosed(CancellationToken ct, string repoOwner, string repoName, int prNumber)
private async Task OnClosed(string repoOwner, string repoName, int prNumber, CancellationToken ct)
{
// pr was just closed, not merged.
var discussion = await topicsRepository.FindTopicIdForDiscussion(repoOwner, repoName, prNumber, ct);
Expand All @@ -100,7 +102,7 @@ private async Task OnClosed(CancellationToken ct, string repoOwner, string repoN
}
}

private async Task OnMerged(CancellationToken ct, string repoOwner, string repoName, int prNumber, string?[] labels, GithubRepo repository)
private async Task OnMerged(string repoOwner, string repoName, int prNumber, string?[] labels, GithubRepo repository, CancellationToken ct)
{
// PR got merged
var discussion = await topicsRepository.FindTopicIdForDiscussion(repoOwner, repoName, prNumber, ct);
Expand All @@ -117,15 +119,15 @@ private async Task OnMerged(CancellationToken ct, string repoOwner, string repoN
}
}

private async Task OnReviewRequested(PullRequestEvent request, CancellationToken ct, GithubRepo repository)
private async Task OnReviewRequested(PullRequestEvent request, GithubRepo repository, CancellationToken ct)
{
if (await client.IsMaintainer(request.RequestedReviewer!.Login, repository, ct))
{
await labelManager.EnsureLabeled(request, StageOfWorkLabel.RequireReview, ct);
}
}

private async Task OnLabelAdd(PullRequestEvent request, CancellationToken ct, string repoOwner, string repoName, int prNumber, GithubRepo repository)
private async Task OnLabelAdd(PullRequestEvent request, string repoOwner, string repoName, int prNumber, GithubRepo repository, CancellationToken ct)
{
if(!_discourseConfig.Enable)
return;
Expand Down Expand Up @@ -155,7 +157,7 @@ private async Task OnLabelAdd(PullRequestEvent request, CancellationToken ct, st
}
}

private async Task OnOpened(PullRequestEvent request, CancellationToken ct, string?[] labels, PullRequest pr, GithubRepo repository)
private async Task OnOpened(PullRequestEvent request, string?[] labels, PullRequest pr, GithubRepo repository, CancellationToken ct)
{
var targetBranch = pr.Base.Ref;
if (targetBranch == "stable")
Expand Down
27 changes: 14 additions & 13 deletions SS14.Labeller/Handlers/LabelPullRequestReviewHandler.cs
Original file line number Diff line number Diff line change
@@ -1,28 +1,29 @@
using SS14.Labeller.GitHubApi;
using MessagePipe;
using SS14.Labeller.GitHubApi;
using SS14.Labeller.Labelling;
using SS14.Labeller.Labelling.Labels;
using SS14.Labeller.Models;

namespace SS14.Labeller.Handlers;

public class LabelPullRequestReviewHandler(IGitHubApiClient client, ILabelManager labelManager)
: RequestHandlerBase<PullRequestReviewEvent>
: IAsyncMessageHandler<PullRequestReviewEvent>
{
/// <inheritdoc />
protected override async Task HandleInternal(PullRequestReviewEvent request, CancellationToken ct)
public async ValueTask HandleAsync(PullRequestReviewEvent message, CancellationToken ct)
{
var pr = request.PullRequest;
var repo = request.Repository;
var user = request.Review.User.Login;
var pr = message.PullRequest;
var repo = message.Repository;
var user = message.Review.User.Login;

// only process if the review state is "approved" or "changes_requested" (ignore comments and other states)
var state = request.Review.State;
if (state != "approved" && state != "changes_requested")
// only process if the review state is "approved" or "changes_messageed" (ignore comments and other states)
var state = message.Review.State;
if (state != "approved" && state != "changes_messageed")
return;

// Ignore reviews if PR is closed or merged
// "closed" means closed or merged, but let's also check for merged explicitly if available
var isClosed = request.Review.State == "closed";
var isClosed = message.Review.State == "closed";
var isMerged = pr.MergedAt != null;
if (isClosed || isMerged)
return;
Expand All @@ -34,9 +35,9 @@ protected override async Task HandleInternal(PullRequestReviewEvent request, Can
await (state switch
{
"approved"
=> labelManager.EnsureLabeled(request, StatusLabel.Approved, ct),
"changes_requested"
=> labelManager.EnsureLabeled(request, StageOfWorkLabel.AwaitingChanges, ct)
=> labelManager.EnsureLabeled(message, StatusLabel.Approved, ct),
"changes_messageed"
=> labelManager.EnsureLabeled(message, StageOfWorkLabel.AwaitingChanges, ct)
});
#pragma warning restore CS8509 // The switch expression does not handle all possible values of its input type (it is not exhaustive).
}
Expand Down
31 changes: 0 additions & 31 deletions SS14.Labeller/Handlers/RequestHandlerBase.cs

This file was deleted.

Loading
Loading