mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
Web server - Plugin end point API WIP
This commit is contained in:
parent
5c2a96eee0
commit
d6ba573456
@ -14,6 +14,20 @@ namespace Artemis.Core.Services
|
||||
/// </summary>
|
||||
WebServer? Server { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new endpoint for the given plugin feature
|
||||
/// </summary>
|
||||
/// <param name="feature">The plugin feature the end point is associated with</param>
|
||||
/// <param name="endPointName">The name of the end point, must be unique</param>
|
||||
/// <returns>The resulting end point</returns>
|
||||
PluginEndPoint AddPluginEndPoint(PluginFeature feature, string endPointName);
|
||||
|
||||
/// <summary>
|
||||
/// Removes an existing endpoint
|
||||
/// </summary>
|
||||
/// <param name="endPoint">The end point to remove</param>
|
||||
void RemovePluginEndPoint(PluginEndPoint endPoint);
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new Web API controller and restarts the web server
|
||||
/// </summary>
|
||||
|
||||
@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using EmbedIO;
|
||||
|
||||
namespace Artemis.Core.Services
|
||||
{
|
||||
public class PluginEndPoint
|
||||
{
|
||||
private readonly PluginsModule _pluginsModule;
|
||||
|
||||
internal PluginEndPoint(PluginFeature pluginFeature, string name, PluginsModule pluginsModule)
|
||||
{
|
||||
_pluginsModule = pluginsModule;
|
||||
PluginFeature = pluginFeature;
|
||||
Name = name;
|
||||
|
||||
PluginFeature.Disabled += OnDisabled;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the plugin the data model is associated with
|
||||
/// </summary>
|
||||
public PluginFeature PluginFeature { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the end point
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the full URL of the end point
|
||||
/// </summary>
|
||||
public string Url => $"{_pluginsModule.BaseRoute}{PluginFeature.Plugin.Guid}/{Name}";
|
||||
|
||||
internal void ProcessRequest(IHttpContext context)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private void OnDisabled(object? sender, EventArgs e)
|
||||
{
|
||||
PluginFeature.Disabled -= OnDisabled;
|
||||
_pluginsModule.RemovePluginEndPoint(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,13 +1,54 @@
|
||||
using System.Threading.Tasks;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using EmbedIO;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Artemis.Core.Services
|
||||
{
|
||||
internal class PluginsModule : WebModuleBase
|
||||
{
|
||||
private readonly Dictionary<string, Dictionary<string, PluginEndPoint>> _pluginEndPoints;
|
||||
|
||||
/// <inheritdoc />
|
||||
public PluginsModule(string baseRoute) : base(baseRoute)
|
||||
{
|
||||
_pluginEndPoints = new Dictionary<string, Dictionary<string, PluginEndPoint>>();
|
||||
OnUnhandledException += HandleUnhandledExceptionJson;
|
||||
}
|
||||
|
||||
public void AddPluginEndPoint(PluginEndPoint registration)
|
||||
{
|
||||
string id = registration.PluginFeature.Plugin.Guid.ToString();
|
||||
if (!_pluginEndPoints.TryGetValue(id, out Dictionary<string, PluginEndPoint>? registrations))
|
||||
{
|
||||
registrations = new Dictionary<string, PluginEndPoint>();
|
||||
_pluginEndPoints.Add(id, registrations);
|
||||
}
|
||||
|
||||
if (registrations.ContainsKey(registration.Name))
|
||||
throw new ArtemisPluginException(registration.PluginFeature.Plugin, $"Plugin already registered an endpoint at {registration.Name}.");
|
||||
registrations.Add(registration.Name, registration);
|
||||
}
|
||||
|
||||
public void RemovePluginEndPoint(PluginEndPoint registration)
|
||||
{
|
||||
string id = registration.PluginFeature.Plugin.Guid.ToString();
|
||||
if (!_pluginEndPoints.TryGetValue(id, out Dictionary<string, PluginEndPoint>? registrations))
|
||||
return;
|
||||
if (!registrations.ContainsKey(registration.Name))
|
||||
return;
|
||||
registrations.Remove(registration.Name);
|
||||
}
|
||||
|
||||
private async Task HandleUnhandledExceptionJson(IHttpContext context, Exception exception)
|
||||
{
|
||||
await context.SendStringAsync(
|
||||
JsonConvert.SerializeObject(new ArtemisPluginException("The plugin failed to process the request", exception), Formatting.Indented),
|
||||
MimeType.Json,
|
||||
Encoding.UTF8
|
||||
);
|
||||
}
|
||||
|
||||
#region Overrides of WebModuleBase
|
||||
@ -15,6 +56,31 @@ namespace Artemis.Core.Services
|
||||
/// <inheritdoc />
|
||||
protected override async Task OnRequestAsync(IHttpContext context)
|
||||
{
|
||||
// Always stick to JSON
|
||||
context.Response.ContentType = MimeType.Json;
|
||||
|
||||
if (context.Route.SubPath == null)
|
||||
throw HttpException.NotFound();
|
||||
|
||||
// Split the sub path
|
||||
string[] pathParts = context.Route.SubPath.Substring(1).Split('/');
|
||||
// Expect a plugin ID and an endpoint
|
||||
if (pathParts == null || pathParts.Length != 2)
|
||||
throw HttpException.BadRequest("Path must contain a plugin ID and endpoint and nothing else.");
|
||||
|
||||
// Find a matching plugin
|
||||
if (!_pluginEndPoints.TryGetValue(pathParts[0], out Dictionary<string, PluginEndPoint>? endPoints))
|
||||
throw HttpException.NotFound($"Found no plugin with ID {pathParts[0]}.");
|
||||
|
||||
// Find a matching endpoint
|
||||
if (!endPoints.TryGetValue(pathParts[1], out PluginEndPoint? endPoint))
|
||||
throw HttpException.NotFound($"Found no endpoint called {pathParts[1]} for plugin with ID {pathParts[0]}.");
|
||||
|
||||
// It is up to the registration how the request is eventually handled, it might even set a response here
|
||||
endPoint.ProcessRequest(context);
|
||||
|
||||
// No need to return ourselves, assume the request is fully handled by the end point
|
||||
context.SetHandled();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@ -1,9 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using EmbedIO;
|
||||
using EmbedIO.Actions;
|
||||
using EmbedIO.WebApi;
|
||||
using Newtonsoft.Json;
|
||||
using Ninject;
|
||||
using Serilog;
|
||||
|
||||
@ -46,7 +49,7 @@ namespace Artemis.Core.Services
|
||||
.WithLocalSessionManager()
|
||||
.WithModule(apiModule)
|
||||
.WithModule(_pluginModule)
|
||||
.WithModule(new ActionModule("/", HttpVerbs.Any, ctx => ctx.SendDataAsync(new {Message = "Error"})));
|
||||
.HandleHttpException((context, exception) => HandleHttpExceptionJson(context, exception));
|
||||
|
||||
// Add controllers to the API module
|
||||
foreach (WebApiControllerRegistration registration in _controllers)
|
||||
@ -62,6 +65,11 @@ namespace Artemis.Core.Services
|
||||
return server;
|
||||
}
|
||||
|
||||
private async Task HandleHttpExceptionJson(IHttpContext context, IHttpException httpException)
|
||||
{
|
||||
await context.SendStringAsync(JsonConvert.SerializeObject(httpException, Formatting.Indented), MimeType.Json, Encoding.UTF8);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event handlers
|
||||
@ -85,6 +93,22 @@ namespace Artemis.Core.Services
|
||||
|
||||
#endregion
|
||||
|
||||
#region Plugin endpoint management
|
||||
|
||||
public PluginEndPoint AddPluginEndPoint(PluginFeature feature, string endPointName)
|
||||
{
|
||||
PluginEndPoint endPoint = new(feature, endPointName, _pluginModule);
|
||||
_pluginModule.AddPluginEndPoint(endPoint);
|
||||
return endPoint;
|
||||
}
|
||||
|
||||
public void RemovePluginEndPoint(PluginEndPoint endPoint)
|
||||
{
|
||||
_pluginModule.RemovePluginEndPoint(endPoint);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Controller management
|
||||
|
||||
public void AddController<T>() where T : WebApiController
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user