From c47e873510a56e0082bb256fc0fd8369149705d9 Mon Sep 17 00:00:00 2001 From: Robert Date: Thu, 4 Mar 2021 00:26:30 +0100 Subject: [PATCH] Plugin endpoints - Added DataModelJsonPluginEndPoint --- .../Artemis.Core.csproj.DotSettings | 1 + .../EndPoints/DataModelJsonPluginEndPoint.cs | 71 +++++++++++++++++++ .../EndpointExceptionEventArgs.cs | 0 .../EventArgs/EndpointRequestEventArgs.cs | 21 ++++++ .../WebServer/EndPoints/PluginEndPoint.cs | 28 ++++++++ .../WebServer/Interfaces/IWebServerService.cs | 20 ++++++ .../Services/WebServer/WebServerService.cs | 25 +++++++ 7 files changed, 166 insertions(+) create mode 100644 src/Artemis.Core/Services/WebServer/EndPoints/DataModelJsonPluginEndPoint.cs rename src/Artemis.Core/Services/WebServer/EndPoints/{ => EventArgs}/EndpointExceptionEventArgs.cs (100%) create mode 100644 src/Artemis.Core/Services/WebServer/EndPoints/EventArgs/EndpointRequestEventArgs.cs diff --git a/src/Artemis.Core/Artemis.Core.csproj.DotSettings b/src/Artemis.Core/Artemis.Core.csproj.DotSettings index 7d6c52363..5a6e7bef3 100644 --- a/src/Artemis.Core/Artemis.Core.csproj.DotSettings +++ b/src/Artemis.Core/Artemis.Core.csproj.DotSettings @@ -68,6 +68,7 @@ True True True + True True True True diff --git a/src/Artemis.Core/Services/WebServer/EndPoints/DataModelJsonPluginEndPoint.cs b/src/Artemis.Core/Services/WebServer/EndPoints/DataModelJsonPluginEndPoint.cs new file mode 100644 index 000000000..30532aa2b --- /dev/null +++ b/src/Artemis.Core/Services/WebServer/EndPoints/DataModelJsonPluginEndPoint.cs @@ -0,0 +1,71 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using Artemis.Core.DataModelExpansions; +using Artemis.Core.Modules; +using EmbedIO; +using Newtonsoft.Json; + +namespace Artemis.Core.Services +{ + /// + /// Represents a plugin web endpoint receiving an object of type and returning any + /// or . + /// Note: Both will be deserialized and serialized respectively using JSON. + /// + public class DataModelJsonPluginEndPoint : PluginEndPoint where T : DataModel + { + private readonly Module? _module; + private readonly DataModelExpansion? _dataModelExpansion; + + internal DataModelJsonPluginEndPoint(Module module, string name, PluginsModule pluginsModule) : base(module, name, pluginsModule) + { + _module = module ?? throw new ArgumentNullException(nameof(module)); + + ThrowOnFail = true; + Accepts = MimeType.Json; + } + + internal DataModelJsonPluginEndPoint(DataModelExpansion dataModelExpansion, string name, PluginsModule pluginsModule) : base(dataModelExpansion, name, pluginsModule) + { + _dataModelExpansion = dataModelExpansion ?? throw new ArgumentNullException(nameof(dataModelExpansion)); + + ThrowOnFail = true; + Accepts = MimeType.Json; + } + + /// + /// Whether or not the end point should throw an exception if deserializing the received JSON fails. + /// If set to malformed JSON is silently ignored; if set to malformed + /// JSON throws a . + /// + public bool ThrowOnFail { get; set; } + + #region Overrides of PluginEndPoint + + /// + protected override async Task ProcessRequest(IHttpContext context) + { + if (context.Request.HttpVerb != HttpVerbs.Post && context.Request.HttpVerb != HttpVerbs.Put) + throw HttpException.MethodNotAllowed("This end point only accepts POST and PUT calls"); + + context.Response.ContentType = MimeType.Json; + + using TextReader reader = context.OpenRequestText(); + try + { + if (_module != null) + JsonConvert.PopulateObject(await reader.ReadToEndAsync(), _module.DataModel); + else + JsonConvert.PopulateObject(await reader.ReadToEndAsync(), _dataModelExpansion!.DataModel); + } + catch (JsonException) + { + if (ThrowOnFail) + throw; + } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Artemis.Core/Services/WebServer/EndPoints/EndpointExceptionEventArgs.cs b/src/Artemis.Core/Services/WebServer/EndPoints/EventArgs/EndpointExceptionEventArgs.cs similarity index 100% rename from src/Artemis.Core/Services/WebServer/EndPoints/EndpointExceptionEventArgs.cs rename to src/Artemis.Core/Services/WebServer/EndPoints/EventArgs/EndpointExceptionEventArgs.cs diff --git a/src/Artemis.Core/Services/WebServer/EndPoints/EventArgs/EndpointRequestEventArgs.cs b/src/Artemis.Core/Services/WebServer/EndPoints/EventArgs/EndpointRequestEventArgs.cs new file mode 100644 index 000000000..6b483cc06 --- /dev/null +++ b/src/Artemis.Core/Services/WebServer/EndPoints/EventArgs/EndpointRequestEventArgs.cs @@ -0,0 +1,21 @@ +using System; +using EmbedIO; + +namespace Artemis.Core.Services +{ + /// + /// Provides data about endpoint request related events + /// + public class EndpointRequestEventArgs : EventArgs + { + internal EndpointRequestEventArgs(IHttpContext context) + { + Context = context; + } + + /// + /// Gets the HTTP context of the request + /// + public IHttpContext Context { get; } + } +} \ No newline at end of file diff --git a/src/Artemis.Core/Services/WebServer/EndPoints/PluginEndPoint.cs b/src/Artemis.Core/Services/WebServer/EndPoints/PluginEndPoint.cs index 504fd0eff..c62c41fe3 100644 --- a/src/Artemis.Core/Services/WebServer/EndPoints/PluginEndPoint.cs +++ b/src/Artemis.Core/Services/WebServer/EndPoints/PluginEndPoint.cs @@ -57,6 +57,16 @@ namespace Artemis.Core.Services /// public event EventHandler? RequestException; + /// + /// Occurs whenever a request is about to be processed + /// + public event EventHandler? ProcessingRequest; + + /// + /// Occurs whenever a request was processed + /// + public event EventHandler? ProcessedRequest; + /// /// Called whenever the end point has to process a request /// @@ -72,11 +82,29 @@ namespace Artemis.Core.Services RequestException?.Invoke(this, new EndpointExceptionEventArgs(e)); } + /// + /// Invokes the event + /// + protected virtual void OnProcessingRequest(IHttpContext context) + { + ProcessingRequest?.Invoke(this, new EndpointRequestEventArgs(context)); + } + + /// + /// Invokes the event + /// + protected virtual void OnProcessedRequest(IHttpContext context) + { + ProcessedRequest?.Invoke(this, new EndpointRequestEventArgs(context)); + } + internal async Task InternalProcessRequest(IHttpContext context) { try { + OnProcessingRequest(context); await ProcessRequest(context); + OnProcessedRequest(context); } catch (Exception e) { diff --git a/src/Artemis.Core/Services/WebServer/Interfaces/IWebServerService.cs b/src/Artemis.Core/Services/WebServer/Interfaces/IWebServerService.cs index 704a4a19c..ef98d3fa0 100644 --- a/src/Artemis.Core/Services/WebServer/Interfaces/IWebServerService.cs +++ b/src/Artemis.Core/Services/WebServer/Interfaces/IWebServerService.cs @@ -1,5 +1,7 @@ using System; using System.Threading.Tasks; +using Artemis.Core.DataModelExpansions; +using Artemis.Core.Modules; using EmbedIO; using EmbedIO.WebApi; @@ -43,6 +45,24 @@ namespace Artemis.Core.Services /// The resulting end point JsonPluginEndPoint AddResponsiveJsonEndPoint(PluginFeature feature, string endPointName, Func requestHandler); + /// + /// Adds a new endpoint that directly maps received JSON to the data model of the provided . + /// + /// The data model type of the module + /// The module whose datamodel to apply the received JSON to + /// The name of the end point, must be unique + /// The resulting end point + DataModelJsonPluginEndPoint AddDataModelJsonEndPoint(Module module, string endPointName) where T : DataModel; + + /// + /// Adds a new endpoint that directly maps received JSON to the data model of the provided . + /// + /// The data model type of the module + /// The data model expansion whose datamodel to apply the received JSON to + /// The name of the end point, must be unique + /// The resulting end point + DataModelJsonPluginEndPoint AddDataModelJsonEndPoint(DataModelExpansion dataModelExpansion, string endPointName) where T : DataModel; + /// /// Adds a new endpoint for the given plugin feature receiving an a . /// diff --git a/src/Artemis.Core/Services/WebServer/WebServerService.cs b/src/Artemis.Core/Services/WebServer/WebServerService.cs index e19e6a4a9..8de5ce9e4 100644 --- a/src/Artemis.Core/Services/WebServer/WebServerService.cs +++ b/src/Artemis.Core/Services/WebServer/WebServerService.cs @@ -3,6 +3,8 @@ using System.Collections.Generic; using System.IO; using System.Text; using System.Threading.Tasks; +using Artemis.Core.DataModelExpansions; +using Artemis.Core.Modules; using EmbedIO; using EmbedIO.WebApi; using Newtonsoft.Json; @@ -125,6 +127,29 @@ namespace Artemis.Core.Services return endPoint; } + public DataModelJsonPluginEndPoint AddDataModelJsonEndPoint(Module module, string endPointName) where T : DataModel + { + if (module == null) throw new ArgumentNullException(nameof(module)); + if (endPointName == null) throw new ArgumentNullException(nameof(endPointName)); + DataModelJsonPluginEndPoint endPoint = new(module, endPointName, PluginsModule); + PluginsModule.AddPluginEndPoint(endPoint); + return endPoint; + } + + public DataModelJsonPluginEndPoint AddDataModelJsonEndPoint(DataModelExpansion dataModelExpansion, string endPointName) where T : DataModel + { + if (dataModelExpansion == null) throw new ArgumentNullException(nameof(dataModelExpansion)); + if (endPointName == null) throw new ArgumentNullException(nameof(endPointName)); + DataModelJsonPluginEndPoint endPoint = new(dataModelExpansion, endPointName, PluginsModule); + PluginsModule.AddPluginEndPoint(endPoint); + return endPoint; + } + + private void HandleDataModelRequest(Module module, T value) where T : DataModel + { + + } + public void RemovePluginEndPoint(PluginEndPoint endPoint) { PluginsModule.RemovePluginEndPoint(endPoint);