-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #37 from rubberduck-vba/webhook
Add webhook controller
- Loading branch information
Showing
12 changed files
with
335 additions
and
70 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
namespace rubberduckvba.Server.Api.Admin; | ||
|
||
public readonly record struct GitRef | ||
{ | ||
private readonly string _value; | ||
|
||
public GitRef(string value) | ||
{ | ||
_value = value; | ||
IsTag = value?.StartsWith("refs/tags/") ?? false; | ||
Name = value?.Split('/').Last() ?? string.Empty; | ||
} | ||
|
||
public bool IsTag { get; } | ||
public string Name { get; } | ||
|
||
public override string ToString() => _value; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
using Hangfire; | ||
using rubberduckvba.Server; | ||
using rubberduckvba.Server.ContentSynchronization; | ||
using rubberduckvba.Server.Hangfire; | ||
using rubberduckvba.Server.Services; | ||
|
||
namespace rubberduckvba.Server.Api.Admin; | ||
|
||
public class HangfireLauncherService(IBackgroundJobClient backgroundJob, ILogger<HangfireLauncherService> logger) | ||
{ | ||
public string UpdateXmldocContent() | ||
{ | ||
var parameters = new XmldocSyncRequestParameters { RepositoryId = RepositoryId.Rubberduck, RequestId = Guid.NewGuid() }; | ||
var jobId = backgroundJob.Enqueue(HangfireConstants.ManualQueueName, () => QueuedUpdateOrchestrator.UpdateXmldocContent(parameters, null!)); | ||
|
||
if (string.IsNullOrWhiteSpace(jobId)) | ||
{ | ||
throw new InvalidOperationException("UpdateXmldocContent was requested but enqueueing a Hangfire job did not return a JobId."); | ||
} | ||
else | ||
{ | ||
logger.LogInformation("JobId {jobId} was enqueued (queue: {queueName}) for xmldoc sync request {requestId}", jobId, HangfireConstants.ManualQueueName, parameters.RequestId); | ||
} | ||
|
||
return jobId; | ||
} | ||
|
||
public string UpdateTagMetadata() | ||
{ | ||
var parameters = new TagSyncRequestParameters { RepositoryId = RepositoryId.Rubberduck, RequestId = Guid.NewGuid() }; | ||
var jobId = backgroundJob.Enqueue(HangfireConstants.ManualQueueName, () => QueuedUpdateOrchestrator.UpdateInstallerDownloadStats(parameters, null!)); | ||
|
||
if (string.IsNullOrWhiteSpace(jobId)) | ||
{ | ||
throw new InvalidOperationException("UpdateXmldocContent was requested but enqueueing a Hangfire job did not return a JobId."); | ||
} | ||
else | ||
{ | ||
logger.LogInformation("JobId {jobId} was enqueued (queue: {queueName}) for tag sync request {requestId}", jobId, HangfireConstants.ManualQueueName, parameters.RequestId); | ||
} | ||
return jobId; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
using Microsoft.AspNetCore.Authorization; | ||
using Microsoft.AspNetCore.Mvc; | ||
using Newtonsoft.Json.Linq; | ||
|
||
namespace rubberduckvba.Server.Api.Admin; | ||
|
||
[ApiController] | ||
public class WebhookController : RubberduckApiController | ||
{ | ||
private readonly WebhookPayloadValidationService _validator; | ||
private readonly HangfireLauncherService _hangfire; | ||
|
||
public WebhookController( | ||
ILogger<WebhookController> logger, | ||
HangfireLauncherService hangfire, | ||
WebhookPayloadValidationService validator) | ||
: base(logger) | ||
{ | ||
_validator = validator; | ||
_hangfire = hangfire; | ||
} | ||
|
||
[Authorize("webhook", AuthenticationSchemes = "webhook-signature")] | ||
[HttpPost("webhook/github")] | ||
public IActionResult GitHub([FromBody] JToken payload) | ||
{ | ||
var eventType = _validator.Validate(payload, Request.Headers, out var content, out var gitref); | ||
|
||
if (eventType == WebhookPayloadType.Push) | ||
{ | ||
var jobId = _hangfire.UpdateXmldocContent(); | ||
var message = $"Webhook push event was accepted. Tag '{gitref?.Name}' associated to the payload will be processed by JobId '{jobId}'."; | ||
|
||
Logger.LogInformation(message); | ||
return Ok(message); | ||
} | ||
else if (eventType == WebhookPayloadType.Greeting) | ||
{ | ||
Logger.LogInformation("Webhook push event was accepted; nothing to process. {content}", content); | ||
return string.IsNullOrWhiteSpace(content) ? NoContent() : Ok(content); | ||
} | ||
|
||
// reject the payload | ||
return BadRequest(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
namespace rubberduckvba.Server.Api.Admin; | ||
|
||
public enum WebhookPayloadType | ||
{ | ||
Unsupported, | ||
Greeting, | ||
Push | ||
} |
39 changes: 39 additions & 0 deletions
39
rubberduckvba.Server/Api/Admin/WebhookPayloadValidationService.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
using Newtonsoft.Json.Linq; | ||
|
||
namespace rubberduckvba.Server.Api.Admin; | ||
|
||
public class WebhookPayloadValidationService(ConfigurationOptions options) | ||
{ | ||
public WebhookPayloadType Validate(JToken payload, IHeaderDictionary headers, out string? content, out GitRef? gitref) | ||
{ | ||
content = default; | ||
gitref = default; | ||
|
||
if (!IsValidHeaders(headers) || !IsValidSource(payload) || !IsValidEvent(payload)) | ||
{ | ||
return WebhookPayloadType.Unsupported; | ||
} | ||
|
||
gitref = new GitRef(payload.Value<string>("ref")); | ||
if (!(payload.Value<bool>("created") && gitref.HasValue && gitref.Value.IsTag)) | ||
{ | ||
content = payload.Value<string>("zen"); | ||
return WebhookPayloadType.Greeting; | ||
} | ||
|
||
return WebhookPayloadType.Push; | ||
} | ||
|
||
private bool IsValidHeaders(IHeaderDictionary headers) => | ||
headers.TryGetValue("X-GitHub-Event", out Microsoft.Extensions.Primitives.StringValues values) && values.Contains("push"); | ||
|
||
private bool IsValidSource(JToken payload) => | ||
payload["repository"].Value<string>("name") == options.GitHubOptions.Value.Rubberduck && | ||
payload["owner"].Value<int>("id") == options.GitHubOptions.Value.RubberduckOrgId; | ||
|
||
private bool IsValidEvent(JToken payload) | ||
{ | ||
var ev = payload["hook"]?["events"]?.Values<string>() ?? []; | ||
return ev.Contains("push"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.