Browse Source

webhooks API & client

pull/937/head
eiximenis 6 years ago
parent
commit
9b0eaddb15
16 changed files with 161 additions and 36 deletions
  1. +0
    -1
      docker-compose.override.yml
  2. +2
    -2
      src/Services/Webhooks/Webhooks.API/Controllers/WebhooksController.cs
  3. +17
    -5
      src/Services/Webhooks/Webhooks.API/Services/GrantUrlTesterService.cs
  4. +1
    -0
      src/Web/WebhookClient/.dockerignore
  5. +43
    -0
      src/Web/WebhookClient/Controllers/WebhooksReceivedController.cs
  6. +2
    -0
      src/Web/WebhookClient/Models/WebHookReceived.cs
  7. +16
    -0
      src/Web/WebhookClient/Models/WebhookData.cs
  8. +8
    -4
      src/Web/WebhookClient/Pages/Index.cshtml
  9. +1
    -1
      src/Web/WebhookClient/Pages/Index.cshtml.cs
  10. +6
    -1
      src/Web/WebhookClient/Pages/RegisterWebhook.cshtml
  11. +27
    -3
      src/Web/WebhookClient/Pages/RegisterWebhook.cshtml.cs
  12. +5
    -2
      src/Web/WebhookClient/Pages/Shared/_Layout.cshtml
  13. +17
    -16
      src/Web/WebhookClient/Services/WebhooksClient.cs
  14. +12
    -0
      src/Web/WebhookClient/SessionKeys.cs
  15. +2
    -0
      src/Web/WebhookClient/Settings.cs
  16. +2
    -1
      src/Web/WebhookClient/Startup.cs

+ 0
- 1
docker-compose.override.yml View File

@ -397,7 +397,6 @@ services:
webhooks.client:
environment:
- ASPNETCORE_ENVIRONMENT=Development
- ASPNETCORE_URLS=http://0.0.0.0:80
- Token=6168DB8D-DC58-4094-AF24-483278923590 # Webhooks are registered with this token (any value is valid) but the client won't check it
- IdentityUrl=http://10.0.75.1:5105


+ 2
- 2
src/Services/Webhooks/Webhooks.API/Controllers/WebhooksController.cs View File

@ -38,7 +38,7 @@ namespace Webhooks.API.Controllers
}
[Authorize]
[HttpGet("{id:int}", Name = "Get")]
[HttpGet("{id:int}")]
[ProducesResponseType(typeof(WebhookSubscription), (int)HttpStatusCode.OK)]
[ProducesResponseType((int)HttpStatusCode.NotFound)]
public async Task<IActionResult> GetByUserAndId(int id)
@ -82,7 +82,7 @@ namespace Webhooks.API.Controllers
_dbContext.Add(subscription);
await _dbContext.SaveChangesAsync();
return CreatedAtAction("Get", new { id = subscription.Id });
return CreatedAtAction("GetByUserAndId", new { id = subscription.Id }, subscription);
}
else
{


+ 17
- 5
src/Services/Webhooks/Webhooks.API/Services/GrantUrlTesterService.cs View File

@ -1,4 +1,5 @@
using System;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
@ -9,19 +10,30 @@ namespace Webhooks.API.Services
class GrantUrlTesterService : IGrantUrlTesterService
{
private readonly IHttpClientFactory _clientFactory;
public GrantUrlTesterService(IHttpClientFactory factory)
private readonly ILogger _logger;
public GrantUrlTesterService(IHttpClientFactory factory, ILogger<IGrantUrlTesterService> logger)
{
_clientFactory = factory;
_logger = logger;
}
public async Task<bool> TestGrantUrl(string url, string token)
{
var client = _clientFactory.CreateClient("GrantClient");
var msg = new HttpRequestMessage(HttpMethod.Options, url);
msg.Headers.Add("X-eshop-whtoken", token);
var response = await client.SendAsync(msg);
return response.IsSuccessStatusCode;
_logger.LogTrace($"Sending the OPTIONS message to {url} with token {token ?? string.Empty}");
try
{
var response = await client.SendAsync(msg);
_logger.LogInformation($"Response code is {response.StatusCode} for url {url}");
return response.IsSuccessStatusCode;
}
catch (Exception ex)
{
_logger.LogWarning($"Exception {ex.GetType().Name} when sending OPTIONS request. Url can't be granted.");
return false;
}
}
}
}

+ 1
- 0
src/Web/WebhookClient/.dockerignore View File

@ -0,0 +1 @@
!wwwroot

+ 43
- 0
src/Web/WebhookClient/Controllers/WebhooksReceivedController.cs View File

@ -0,0 +1,43 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using WebhookClient.Models;
namespace WebhookClient.Controllers
{
[ApiController]
[Route("webhook-received")]
public class WebhooksReceivedController : Controller
{
private readonly Settings _settings;
public WebhooksReceivedController(IOptions<Settings> settings)
{
_settings = settings.Value;
}
public IActionResult NewWebhook(WebhookData hook)
{
var header = Request.Headers[HeaderNames.WebHookCheckHeader];
var token = header.FirstOrDefault();
if (!_settings.ValidateToken || _settings.Token == token)
{
var received = HttpContext.Session.Get<IEnumerable<WebHookReceived>>(SessionKeys.HooksKey)?.ToList() ?? new List<WebHookReceived>();
received.Add(new WebHookReceived()
{
Data = hook.Payload,
When = hook.When,
Token = token
});
return Ok();
}
return BadRequest();
}
}
}

+ 2
- 0
src/Web/WebhookClient/Models/WebHookReceived.cs View File

@ -9,5 +9,7 @@ namespace WebhookClient.Models
{
public DateTime When { get; set; }
public string Data { get; set; }
public string Token { get; set; }
}
}

+ 16
- 0
src/Web/WebhookClient/Models/WebhookData.cs View File

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace WebhookClient.Models
{
public class WebhookData
{
public DateTime When { get; }
public string Payload { get; }
public string Type { get; }
}
}

+ 8
- 4
src/Web/WebhookClient/Pages/Index.cshtml View File

@ -7,21 +7,25 @@
<div class="text-center">
<h1 class="display-4">Welcome</h1>
<p>eShopOnContainers - Webhook client</p>
<a class="btn-primary btn" href="@Url.Action("SignIn","Account")">Login</a>
<p>Why I need to login? You only need to login <bold>to setup a new webhook</bold>.</p>
@if (!User.Identity.IsAuthenticated)
{
<a class="btn-primary btn" href="@Url.Action("SignIn", "Account")">Login</a>
<p>Why I need to login? You only need to login <bold>to setup a new webhook</bold>.</p>
}
</div>
@if (User.Identity.IsAuthenticated)
{
<div class="table">
<h3>Current webhooks invoked</h3>
<h3>Current webhooks received</h3>
<p>(Data since last time web started up)<p>
<table class="table">
@foreach (var webhook in Model.WebHooksReceived)
{
<tr>
<td>@webhook.When</td>
<td><pre>@webhook.Data</pre></td>
<td>@(webhook.Token ?? "--None--")</td>
</tr>
}
</table>


+ 1
- 1
src/Web/WebhookClient/Pages/Index.cshtml.cs View File

@ -17,7 +17,7 @@ namespace WebhookClient.Pages
public void OnGet()
{
WebHooksReceived = HttpContext.Session.Get<IEnumerable<WebHookReceived>>("webhooks.received") ?? Enumerable.Empty<WebHookReceived>();
WebHooksReceived = HttpContext.Session.Get<IEnumerable<WebHookReceived>>(SessionKeys.HooksKey) ?? Enumerable.Empty<WebHookReceived>();
}
}
}

+ 6
- 1
src/Web/WebhookClient/Pages/RegisterWebhook.cshtml View File

@ -11,4 +11,9 @@
<form method="post">
<p>Token: <input type="text" asp-for="Token" /></p>
<input type="submit" value="send" />
</form>
</form>
@if (Model.ResponseCode != (int)System.Net.HttpStatusCode.OK)
{
<p>Error @Model.ResponseCode (@Model.ResponseMessage) when calling the Webhooks API (@Model.RequestUrl) with GrantUrl: @Model.GrantUrl):(</p>
}

+ 27
- 3
src/Web/WebhookClient/Pages/RegisterWebhook.cshtml.cs View File

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Formatting;
using System.Threading.Tasks;
@ -20,6 +21,11 @@ namespace WebhookClient.Pages
[BindProperty] public string Token { get; set; }
public int ResponseCode { get; set; }
public string RequestUrl { get; set; }
public string GrantUrl { get; set; }
public string ResponseMessage { get; set; }
public RegisterWebhookModel(IOptions<Settings> settings, IHttpClientFactory httpClientFactory)
{
@ -29,15 +35,21 @@ namespace WebhookClient.Pages
public void OnGet()
{
ResponseCode = (int)HttpStatusCode.OK;
Token = _settings.Token;
}
public async Task OnPost()
public async Task<IActionResult> OnPost()
{
ResponseCode = (int)HttpStatusCode.OK;
var protocol = Request.IsHttps ? "https" : "http";
var selfurl = !string.IsNullOrEmpty(_settings.SelfUrl) ? _settings.SelfUrl : $"{protocol}://{Request.Host}/{Request.PathBase}";
if (!selfurl.EndsWith("/"))
{
selfurl = selfurl + "/";
}
var granturl = $"{selfurl}check";
var url = $"{selfurl}webhook";
var url = $"{selfurl}webhook-received";
var client = _httpClientFactory.CreateClient("GrantClient");
var payload = new WebhookSubscriptionRequest()
@ -49,7 +61,19 @@ namespace WebhookClient.Pages
};
var response = await client.PostAsync<WebhookSubscriptionRequest>(_settings.WebhooksUrl + "/api/v1/webhooks", payload, new JsonMediaTypeFormatter());
RedirectToPage("Index");
if (response.IsSuccessStatusCode)
{
return RedirectToPage("WebhooksList");
}
else
{
ResponseCode = (int)response.StatusCode;
ResponseMessage = response.ReasonPhrase;
GrantUrl = granturl;
RequestUrl = $"{response.RequestMessage.Method} {response.RequestMessage.RequestUri}";
}
return Page();
}
}
}

+ 5
- 2
src/Web/WebhookClient/Pages/Shared/_Layout.cshtml View File

@ -32,7 +32,10 @@
<a class="nav-link text-dark" asp-area="" asp-page="/Index">Home</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-page="/Privacy">Privacy</a>
<a class="nav-link text-dark" asp-area="" asp-page="/RegisterWebhook">Register webhook</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-page="/WebhooksList">Webhooks registered (in API)</a>
</li>
</ul>
</div>
@ -47,7 +50,7 @@
<footer class="border-top footer text-muted">
<div class="container">
&copy; 2019 - WebhookClient - <a asp-area="" asp-page="/Privacy">Privacy</a>
&copy; 2019 - WebhookClient - <a asp-area="" asp-page="/RegisterWebhook">Register Webhook</a> | <a asp-area="" asp-page="/WebhooksList">Webhooks registered in API</a>
</div>
</footer>


+ 17
- 16
src/Web/WebhookClient/Services/WebhooksClient.cs View File

@ -1,5 +1,8 @@
using System;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using WebhookClient.Models;
@ -7,23 +10,21 @@ namespace WebhookClient.Services
{
public class WebhooksClient : IWebhooksClient
{
private readonly IHttpClientFactory _httpClientFactory;
private readonly Settings _settings;
public WebhooksClient(IHttpClientFactory httpClientFactory, IOptions<Settings> settings)
{
_httpClientFactory = httpClientFactory;
_settings = settings.Value;
}
public async Task<IEnumerable<WebhookResponse>> LoadWebhooks()
{
return new[]{
new WebhookResponse()
{
Date = DateTime.Now,
DestUrl = "http://aaaaa.me",
Token = "3282832as2"
},
new WebhookResponse()
{
Date = DateTime.Now.Subtract(TimeSpan.FromSeconds(392)),
DestUrl = "http://bbbbb.me",
Token = "ds2"
}
};
var client = _httpClientFactory.CreateClient("GrantClient");
var response = await client.GetAsync(_settings.WebhooksUrl + "/api/v1/webhooks");
var json = await response.Content.ReadAsStringAsync();
var subscriptions = JsonConvert.DeserializeObject<IEnumerable<WebhookResponse>>(json);
return subscriptions;
}
}
}

+ 12
- 0
src/Web/WebhookClient/SessionKeys.cs View File

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace WebhookClient
{
static class SessionKeys
{
public const string HooksKey = "webhooks.received";
}
}

+ 2
- 0
src/Web/WebhookClient/Settings.cs View File

@ -13,5 +13,7 @@ namespace WebhookClient
public string WebhooksUrl { get; set; }
public string SelfUrl { get; set; }
public bool ValidateToken { get; set; }
}
}

+ 2
- 1
src/Web/WebhookClient/Startup.cs View File

@ -59,9 +59,10 @@ namespace WebhookClient
{
if ("OPTIONS".Equals(context.Request.Method, StringComparison.InvariantCultureIgnoreCase))
{
var validateToken = bool.TrueString.Equals(Configuration["ValidateToken"], StringComparison.InvariantCultureIgnoreCase);
var header = context.Request.Headers[HeaderNames.WebHookCheckHeader];
var value = header.FirstOrDefault();
if (value == Configuration["Token"])
if (!validateToken || value == Configuration["Token"])
{
context.Response.StatusCode = (int)HttpStatusCode.OK;
}


Loading…
Cancel
Save