diff options
Diffstat (limited to 'lib/VNLib.Plugins.Extensions.Loading/src/Routing')
3 files changed, 158 insertions, 5 deletions
diff --git a/lib/VNLib.Plugins.Extensions.Loading/src/Routing/EndpointLogNameAttribute.cs b/lib/VNLib.Plugins.Extensions.Loading/src/Routing/EndpointLogNameAttribute.cs new file mode 100644 index 0000000..d47be22 --- /dev/null +++ b/lib/VNLib.Plugins.Extensions.Loading/src/Routing/EndpointLogNameAttribute.cs @@ -0,0 +1,41 @@ +/* +* Copyright (c) 2024 Vaughn Nugent +* +* Library: VNLib +* Package: VNLib.Plugins.Extensions.Loading +* File: ConfigurationExtensions.cs +* +* ConfigurationExtensions.cs is part of VNLib.Plugins.Extensions.Loading which is part of the larger +* VNLib collection of libraries and utilities. +* +* VNLib.Plugins.Extensions.Loading is free software: you can redistribute it and/or modify +* it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* VNLib.Plugins.Extensions.Loading is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Affero General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ + +using System; + +namespace VNLib.Plugins.Extensions.Loading.Routing +{ + + /// <summary> + /// Defines configurable settings for an endpoint + /// </summary> + [AttributeUsage(AttributeTargets.Class)] + public sealed class EndpointLogNameAttribute(string logName) : Attribute + { + /// <summary> + /// The name of the logging scope for the endpoint + /// </summary> + public string LogName { get; } = logName; + } +} diff --git a/lib/VNLib.Plugins.Extensions.Loading/src/Routing/EndpointPathAttribute.cs b/lib/VNLib.Plugins.Extensions.Loading/src/Routing/EndpointPathAttribute.cs new file mode 100644 index 0000000..a5ab355 --- /dev/null +++ b/lib/VNLib.Plugins.Extensions.Loading/src/Routing/EndpointPathAttribute.cs @@ -0,0 +1,40 @@ +/* +* Copyright (c) 2024 Vaughn Nugent +* +* Library: VNLib +* Package: VNLib.Plugins.Extensions.Loading +* File: ConfigurationExtensions.cs +* +* ConfigurationExtensions.cs is part of VNLib.Plugins.Extensions.Loading which is part of the larger +* VNLib collection of libraries and utilities. +* +* VNLib.Plugins.Extensions.Loading is free software: you can redistribute it and/or modify +* it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* VNLib.Plugins.Extensions.Loading is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Affero General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ + +using System; + +namespace VNLib.Plugins.Extensions.Loading.Routing +{ + /// <summary> + /// Defines configurable settings for an endpoint + /// </summary> + [AttributeUsage(AttributeTargets.Class)] + public sealed class EndpointPathAttribute(string path) : Attribute + { + /// <summary> + /// Sets the endpoint path (or configuration template if set) + /// </summary> + public string Path { get; } = path; + } +} diff --git a/lib/VNLib.Plugins.Extensions.Loading/src/Routing/RoutingExtensions.cs b/lib/VNLib.Plugins.Extensions.Loading/src/Routing/RoutingExtensions.cs index ab7dc58..6665a75 100644 --- a/lib/VNLib.Plugins.Extensions.Loading/src/Routing/RoutingExtensions.cs +++ b/lib/VNLib.Plugins.Extensions.Loading/src/Routing/RoutingExtensions.cs @@ -27,23 +27,24 @@ using System.Reflection; using System.Threading.Tasks; using System.Collections.Frozen; using System.Collections.Generic; +using System.Text.RegularExpressions; using System.Runtime.CompilerServices; using VNLib.Net.Http; using VNLib.Utils.Logging; +using VNLib.Utils.Resources; using VNLib.Plugins.Essentials.Runtime; using VNLib.Plugins.Essentials; using VNLib.Plugins.Essentials.Endpoints; using VNLib.Plugins.Extensions.Loading.Routing.Mvc; - namespace VNLib.Plugins.Extensions.Loading.Routing { /// <summary> /// Provides advanced QOL features to plugin loading /// </summary> - public static class RoutingExtensions + public static partial class RoutingExtensions { private static readonly ConditionalWeakTable<IEndpoint, PluginBase?> _pluginRefs = new(); private static readonly ConditionalWeakTable<PluginBase, EndpointCollection> _pluginEndpoints = new(); @@ -60,11 +61,14 @@ namespace VNLib.Plugins.Extensions.Loading.Routing T endpoint = plugin.CreateService<T>(); //Route the endpoint - plugin.Route(endpoint); + Route(plugin, endpoint); //Store ref to plugin for endpoint _pluginRefs.Add(endpoint, plugin); + //Function that initalizes the endpoint's path and logging variables + InitEndpointSettings(plugin, endpoint); + return endpoint; } @@ -104,17 +108,85 @@ namespace VNLib.Plugins.Extensions.Loading.Routing { _ = _pluginRefs.TryGetValue(ep, out PluginBase? pBase); return pBase ?? throw new InvalidOperationException("Endpoint was not dynamically routed"); - } + } + + private static readonly Regex ConfigSyntaxParser = ParserRegex(); + private delegate void InitFunc(string path, ILogProvider log); + + [GeneratedRegex("{{(.*?)}}", RegexOptions.Compiled)] + private static partial Regex ParserRegex(); + + private static void InitEndpointSettings<T>(PluginBase plugin, T endpoint) where T : IEndpoint + { + //Load optional config + IConfigScope config = plugin.GetConfigForType<T>(); + + ILogProvider logger = plugin.Log; + + EndpointPathAttribute? pathAttr = typeof(T).GetCustomAttribute<EndpointPathAttribute>(); + + /* + * gets the protected function for assigning the endpoint path + * and logger instance. + */ + InitFunc? initPathAndLog = ManagedLibrary.TryGetMethod<InitFunc>(endpoint, "InitPathAndLog", BindingFlags.NonPublic); + + if (pathAttr is null || initPathAndLog is null) + { + return; + } + + string? logName = typeof(T).GetCustomAttribute<EndpointLogNameAttribute>()?.LogName; + + if (!string.IsNullOrWhiteSpace(logName)) + { + logger = plugin.Log.CreateScope(SubsituteValue(logName, config)); + } + try + { + //Invoke init function and pass in variable names + initPathAndLog( + path: SubsituteValue(pathAttr.Path, config), + logger + ); + } + catch (ConfigurationException) + { + throw; + } + catch(Exception e) + { + throw new ConfigurationException($"Failed to initalize endpoint {endpoint.GetType().Name}", e); + } + + static string SubsituteValue(string pathVar, IConfigScope? config) + { + if (config is null) + { + return pathVar; + } + + // Replace the matched pattern with the corresponding value from the dictionary + return ConfigSyntaxParser.Replace(pathVar, match => + { + string varName = match.Groups[1].Value; + + //Get the value from the config scope or return the original variable unmodified + return config.GetValueOrDefault(varName, varName); + }); + } + } private sealed class EndpointCollection : IVirtualEndpointDefinition { public List<IEndpoint> Endpoints { get; } = new(); ///<inheritdoc/> - IEnumerable<IEndpoint> IVirtualEndpointDefinition.GetEndpoints() => Endpoints; + IEnumerable<IEndpoint> IVirtualEndpointDefinition.GetEndpoints() => Endpoints; } + private delegate ValueTask<VfReturnType> EndpointWorkFunc(HttpEntity entity); sealed record class HttpControllerEndpoint(MethodInfo MethodInfo, HttpEndpointAttribute Attr) |