aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/VNLib.Plugins.Extensions.Data/src/ProtectedEntityExtensions.cs39
-rw-r--r--lib/VNLib.Plugins.Extensions.Data/src/VNLib.Plugins.Extensions.Data.csproj4
-rw-r--r--lib/VNLib.Plugins.Extensions.Loading.Sql/src/VNLib.Plugins.Extensions.Loading.Sql.csproj6
-rw-r--r--lib/VNLib.Plugins.Extensions.Loading/src/Events/EventManagment.cs2
-rw-r--r--lib/VNLib.Plugins.Extensions.Loading/src/IAsyncBackgroundWork.cs46
-rw-r--r--lib/VNLib.Plugins.Extensions.Loading/src/LoadingExtensions.cs22
-rw-r--r--lib/VNLib.Plugins.Extensions.Loading/src/RoutingExtensions.cs40
-rw-r--r--lib/VNLib.Plugins.Extensions.Loading/src/VNLib.Plugins.Extensions.Loading.csproj2
-rw-r--r--lib/VNLib.Plugins.Extensions.Loading/src/VaultSecrets.cs2
9 files changed, 134 insertions, 29 deletions
diff --git a/lib/VNLib.Plugins.Extensions.Data/src/ProtectedEntityExtensions.cs b/lib/VNLib.Plugins.Extensions.Data/src/ProtectedEntityExtensions.cs
index dc23bf4..ea8d8cb 100644
--- a/lib/VNLib.Plugins.Extensions.Data/src/ProtectedEntityExtensions.cs
+++ b/lib/VNLib.Plugins.Extensions.Data/src/ProtectedEntityExtensions.cs
@@ -25,10 +25,12 @@
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using System.Collections.Generic;
using VNLib.Utils;
using VNLib.Plugins.Extensions.Data.Abstractions;
+
namespace VNLib.Plugins.Extensions.Data
{
public static class ProtectedEntityExtensions
@@ -40,7 +42,7 @@ namespace VNLib.Plugins.Extensions.Data
/// <param name="record">The record to update</param>
/// <param name="userId">The userid of the record owner</param>
/// <returns>A task that evaluates to the number of records modified</returns>
- public static Task<ERRNO> UpdateAsync<TEntity>(this IDataStore<TEntity> store, TEntity record, string userId) where TEntity : class, IDbModel, IUserEntity
+ public static Task<ERRNO> UpdateUserRecordAsync<TEntity>(this IDataStore<TEntity> store, TEntity record, string userId) where TEntity : class, IDbModel, IUserEntity
{
record.UserId = userId;
return store.UpdateAsync(record);
@@ -53,7 +55,7 @@ namespace VNLib.Plugins.Extensions.Data
/// <param name="record">The record to update</param>
/// <param name="userId">The userid of the record owner</param>
/// <returns>A task that evaluates to the number of records modified</returns>
- public static Task<ERRNO> CreateAsync<TEntity>(this IDataStore<TEntity> store, TEntity record, string userId) where TEntity : class, IDbModel, IUserEntity
+ public static Task<ERRNO> CreateUserRecordAsync<TEntity>(this IDataStore<TEntity> store, TEntity record, string userId) where TEntity : class, IDbModel, IUserEntity
{
record.UserId = userId;
return store.CreateAsync(record);
@@ -66,21 +68,50 @@ namespace VNLib.Plugins.Extensions.Data
/// <param name="key">The unique id of the entity</param>
/// <param name="userId">The user's id that owns the resource</param>
/// <returns>A task that resolves the entity or null if not found</returns>
- public static Task<TEntity?> GetSingleAsync<TEntity>(this IDataStore<TEntity> store, string key, string userId) where TEntity : class, IDbModel, IUserEntity
+ public static Task<TEntity?> GetSingleUserRecordAsync<TEntity>(this IDataStore<TEntity> store, string key, string userId) where TEntity : class, IDbModel, IUserEntity
{
return store.GetSingleAsync(key, userId);
}
/// <summary>
+ /// Gets a page by its number offset constrained by its limit,
+ /// for the given user id
+ /// </summary>
+ /// <typeparam name="TEntity"></typeparam>
+ /// <param name="store"></param>
+ /// <param name="collection">The collection to store found records</param>
+ /// <param name="userId">The user to get the page for</param>
+ /// <param name="page">The page offset</param>
+ /// <param name="limit">The record limit for the page</param>
+ /// <returns>A task that resolves the number of entities added to the collection</returns>
+ public static Task<int> GetUserPageAsync<TEntity>(this IPaginatedDataStore<TEntity> store, ICollection<TEntity> collection, string userId, int page, int limit)
+ where TEntity : class, IDbModel, IUserEntity
+ {
+ return store.GetPageAsync(collection, page, limit, userId);
+ }
+
+ /// <summary>
/// Deletes a single entiry by its ID only if it belongs to the speicifed user
/// </summary>
/// <param name="store"></param>
/// <param name="key">The unique id of the entity</param>
/// <param name="userId">The user's id that owns the resource</param>
/// <returns>A task the resolves the number of eneities deleted (should evaluate to true or false)</returns>
- public static Task<ERRNO> DeleteAsync<TEntity>(this IDataStore<TEntity> store, string key, string userId) where TEntity : class, IDbModel, IUserEntity
+ public static Task<ERRNO> DeleteUserRecordAsync<TEntity>(this IDataStore<TEntity> store, string key, string userId) where TEntity : class, IDbModel, IUserEntity
{
return store.DeleteAsync(key, userId);
}
+
+ /// <summary>
+ /// Gets the record count for the specified userId
+ /// </summary>
+ /// <typeparam name="TEntity"></typeparam>
+ /// <param name="store"></param>
+ /// <param name="userId">The unique id of the user to query record count</param>
+ /// <returns>A task that resolves the number of records belonging to the specified user</returns>
+ public static Task<long> GetUserRecordCountAsync<TEntity>(this IDataStore<TEntity> store, string userId) where TEntity : class, IDbModel, IUserEntity
+ {
+ return store.GetCountAsync(userId);
+ }
}
}
diff --git a/lib/VNLib.Plugins.Extensions.Data/src/VNLib.Plugins.Extensions.Data.csproj b/lib/VNLib.Plugins.Extensions.Data/src/VNLib.Plugins.Extensions.Data.csproj
index bf09e6d..67310b6 100644
--- a/lib/VNLib.Plugins.Extensions.Data/src/VNLib.Plugins.Extensions.Data.csproj
+++ b/lib/VNLib.Plugins.Extensions.Data/src/VNLib.Plugins.Extensions.Data.csproj
@@ -37,8 +37,8 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
- <PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.11" />
- <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.11" />
+ <PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.13" />
+ <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.13" />
</ItemGroup>
<ItemGroup>
diff --git a/lib/VNLib.Plugins.Extensions.Loading.Sql/src/VNLib.Plugins.Extensions.Loading.Sql.csproj b/lib/VNLib.Plugins.Extensions.Loading.Sql/src/VNLib.Plugins.Extensions.Loading.Sql.csproj
index a663946..f944cd6 100644
--- a/lib/VNLib.Plugins.Extensions.Loading.Sql/src/VNLib.Plugins.Extensions.Loading.Sql.csproj
+++ b/lib/VNLib.Plugins.Extensions.Loading.Sql/src/VNLib.Plugins.Extensions.Loading.Sql.csproj
@@ -28,9 +28,9 @@
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="6.0.11" />
- <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.11" />
- <PackageReference Include="MySqlConnector" Version="2.2.0" />
+ <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="6.0.13" />
+ <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.13" />
+ <PackageReference Include="MySqlConnector" Version="2.2.5" />
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="6.0.2" />
</ItemGroup>
diff --git a/lib/VNLib.Plugins.Extensions.Loading/src/Events/EventManagment.cs b/lib/VNLib.Plugins.Extensions.Loading/src/Events/EventManagment.cs
index fde67f7..bde6986 100644
--- a/lib/VNLib.Plugins.Extensions.Loading/src/Events/EventManagment.cs
+++ b/lib/VNLib.Plugins.Extensions.Loading/src/Events/EventManagment.cs
@@ -60,7 +60,7 @@ namespace VNLib.Plugins.Extensions.Loading.Events
plugin.Log.Verbose("Interval for {t} scheduled", interval);
//Run interval on plugins bg scheduler
- _ = plugin.DeferTask(() => RunIntervalOnPluginScheduler(plugin, asyncCallback, interval, immediate));
+ _ = plugin.ObserveTask(() => RunIntervalOnPluginScheduler(plugin, asyncCallback, interval, immediate));
}
private static async Task RunIntervalOnPluginScheduler(PluginBase plugin, AsyncSchedulableCallback callback, TimeSpan interval, bool immediate)
diff --git a/lib/VNLib.Plugins.Extensions.Loading/src/IAsyncBackgroundWork.cs b/lib/VNLib.Plugins.Extensions.Loading/src/IAsyncBackgroundWork.cs
new file mode 100644
index 0000000..9fb66a2
--- /dev/null
+++ b/lib/VNLib.Plugins.Extensions.Loading/src/IAsyncBackgroundWork.cs
@@ -0,0 +1,46 @@
+/*
+* Copyright (c) 2022 Vaughn Nugent
+*
+* Library: VNLib
+* Package: VNLib.Plugins.Extensions.Loading
+* File: LoadingExtensions.cs
+*
+* LoadingExtensions.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.Threading.Tasks;
+using VNLib.Utils.Logging;
+using System.Threading;
+
+namespace VNLib.Plugins.Extensions.Loading
+{
+ /// <summary>
+ /// Represents a low priority or long running work task to be done
+ /// and observed by a loaded plugin
+ /// </summary>
+ public interface IAsyncBackgroundWork
+ {
+ /// <summary>
+ /// Called when low priority work is ready to be run and its results
+ /// marshaled back to the plugin context
+ /// </summary>
+ /// <param name="pluginLog">The plugins default log provider</param>
+ /// <param name="exitToken">A token that signals when the plugin is unloading and work should be cancelled</param>
+ /// <returns>A task representing the low priority work to observed</returns>
+ Task DoWorkAsync(ILogProvider pluginLog, CancellationToken exitToken);
+ }
+}
diff --git a/lib/VNLib.Plugins.Extensions.Loading/src/LoadingExtensions.cs b/lib/VNLib.Plugins.Extensions.Loading/src/LoadingExtensions.cs
index 25dc4ec..743566d 100644
--- a/lib/VNLib.Plugins.Extensions.Loading/src/LoadingExtensions.cs
+++ b/lib/VNLib.Plugins.Extensions.Loading/src/LoadingExtensions.cs
@@ -38,6 +38,7 @@ using VNLib.Plugins.Essentials.Accounts;
namespace VNLib.Plugins.Extensions.Loading
{
+
/// <summary>
/// Provides common loading (and unloading when required) extensions for plugins
/// </summary>
@@ -225,7 +226,7 @@ namespace VNLib.Plugins.Extensions.Loading
/// <param name="delayMs">An optional startup delay for the operation</param>
/// <returns>A task that completes when the deferred task completes </returns>
/// <exception cref="ObjectDisposedException"></exception>
- public static async Task DeferTask(this PluginBase plugin, Func<Task> asyncTask, int delayMs = 0)
+ public static async Task ObserveTask(this PluginBase plugin, Func<Task> asyncTask, int delayMs = 0)
{
/*
* Motivation:
@@ -266,6 +267,20 @@ namespace VNLib.Plugins.Extensions.Loading
}
}
+
+ /// <summary>
+ /// Schedules work to begin after the specified delay to be observed by the plugin while
+ /// passing plugin specifie information. Exceptions are logged to the default plugin log
+ /// </summary>
+ /// <param name="plugin"></param>
+ /// <param name="work">The work to be observed</param>
+ /// <param name="delayMs">The time (in milliseconds) to delay dispatching the work item</param>
+ /// <returns>The task that represents the scheduled work</returns>
+ public static Task ObserveWork(this PluginBase plugin, IAsyncBackgroundWork work, int delayMs = 0)
+ {
+ return ObserveTask(plugin, () => work.DoWorkAsync(plugin.Log, plugin.UnloadToken), delayMs);
+ }
+
/// <summary>
/// Registers an event to occur when the plugin is unloaded on a background thread
/// and will cause the Plugin.Unload() method to block until the event completes
@@ -292,10 +307,9 @@ namespace VNLib.Plugins.Extensions.Loading
}
//Registaer the task to cause the plugin to wait
- return pbase.DeferTask(() => WaitForUnload(pbase, callback));
+ return pbase.ObserveTask(() => WaitForUnload(pbase, callback));
}
-
private sealed class PluginLocalCache
{
private readonly PluginBase _plugin;
@@ -356,7 +370,7 @@ namespace VNLib.Plugins.Extensions.Loading
public void LoadSecret(PluginBase pbase)
{
- _ = pbase.DeferTask(() => LoadSecretInternal(pbase));
+ _ = pbase.ObserveTask(() => LoadSecretInternal(pbase));
}
private async Task LoadSecretInternal(PluginBase pbase)
diff --git a/lib/VNLib.Plugins.Extensions.Loading/src/RoutingExtensions.cs b/lib/VNLib.Plugins.Extensions.Loading/src/RoutingExtensions.cs
index f27b3b3..c1c6bb6 100644
--- a/lib/VNLib.Plugins.Extensions.Loading/src/RoutingExtensions.cs
+++ b/lib/VNLib.Plugins.Extensions.Loading/src/RoutingExtensions.cs
@@ -30,6 +30,7 @@ using System.Collections.Generic;
using System.Runtime.CompilerServices;
using VNLib.Plugins.Extensions.Loading.Events;
+using System.Net;
namespace VNLib.Plugins.Extensions.Loading.Routing
{
@@ -50,42 +51,53 @@ namespace VNLib.Plugins.Extensions.Loading.Routing
public static T Route<T>(this PluginBase plugin, string? pluginConfigPathName) where T : IEndpoint
{
Type endpointType = typeof(T);
+
+ T endpoint;
+
//If the config attribute is not set, then ignore the config variables
if (string.IsNullOrWhiteSpace(pluginConfigPathName))
{
ConstructorInfo? constructor = endpointType.GetConstructor(new Type[] { typeof(PluginBase) });
+
_ = constructor ?? throw new EntryPointNotFoundException($"No constructor found for {endpointType.Name}");
+
//Create the new endpoint and pass the plugin instance
- T endpoint = (T)constructor.Invoke(new object[] { plugin });
+ endpoint = (T)constructor.Invoke(new object[] { plugin });
+
//Register event handlers for the endpoint
ScheduleIntervals(plugin, endpoint, endpointType, null);
- //Route the endpoint
- plugin.Route(endpoint);
-
- //Store ref to plugin for endpoint
- _pluginRefs.Add(endpoint, plugin);
-
- return endpoint;
}
else
{
ConstructorInfo? constructor = endpointType.GetConstructor(new Type[] { typeof(PluginBase), typeof(IReadOnlyDictionary<string, JsonElement>) });
+
//Make sure the constructor exists
_ = constructor ?? throw new EntryPointNotFoundException($"No constructor found for {endpointType.Name}");
+
//Get config variables for the endpoint
IReadOnlyDictionary<string, JsonElement> conf = plugin.GetConfig(pluginConfigPathName);
+
//Create the new endpoint and pass the plugin instance along with the configuration object
- T endpoint = (T)constructor.Invoke(new object[] { plugin, conf });
+ endpoint = (T)constructor.Invoke(new object[] { plugin, conf });
+
//Register event handlers for the endpoint
ScheduleIntervals(plugin, endpoint, endpointType, conf);
- //Route the endpoint
- plugin.Route(endpoint);
+ }
- //Store ref to plugin for endpoint
- _pluginRefs.Add(endpoint, plugin);
+ //Route the endpoint
+ plugin.Route(endpoint);
- return endpoint;
+ //Store ref to plugin for endpoint
+ _pluginRefs.Add(endpoint, plugin);
+
+ //See if the endpoint is disposable
+ if (endpoint is IDisposable dis)
+ {
+ //Register dispose for unload
+ _ = plugin.RegisterForUnload(dis.Dispose);
}
+
+ return endpoint;
}
/// <summary>
diff --git a/lib/VNLib.Plugins.Extensions.Loading/src/VNLib.Plugins.Extensions.Loading.csproj b/lib/VNLib.Plugins.Extensions.Loading/src/VNLib.Plugins.Extensions.Loading.csproj
index c31ae1c..a660a7e 100644
--- a/lib/VNLib.Plugins.Extensions.Loading/src/VNLib.Plugins.Extensions.Loading.csproj
+++ b/lib/VNLib.Plugins.Extensions.Loading/src/VNLib.Plugins.Extensions.Loading.csproj
@@ -28,7 +28,7 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="McMaster.NETCore.Plugins" Version="1.4.0" />
- <PackageReference Include="VaultSharp" Version="1.7.1" />
+ <PackageReference Include="VaultSharp" Version="1.12.2.1" />
</ItemGroup>
<ItemGroup>
diff --git a/lib/VNLib.Plugins.Extensions.Loading/src/VaultSecrets.cs b/lib/VNLib.Plugins.Extensions.Loading/src/VaultSecrets.cs
index d3bdf42..da6650a 100644
--- a/lib/VNLib.Plugins.Extensions.Loading/src/VaultSecrets.cs
+++ b/lib/VNLib.Plugins.Extensions.Loading/src/VaultSecrets.cs
@@ -78,6 +78,7 @@ namespace VNLib.Plugins.Extensions.Loading
/// <exception cref="ObjectDisposedException"></exception>
public static Task<SecretResult?> TryGetSecretAsync(this PluginBase plugin, string secretName)
{
+ plugin.ThrowIfUnloaded();
//Get the secret from the config file raw
string? rawSecret = TryGetSecretInternal(plugin, secretName);
if (rawSecret == null)
@@ -159,6 +160,7 @@ namespace VNLib.Plugins.Extensions.Loading
/// <exception cref="ObjectDisposedException"></exception>
public static Task<X509Certificate?> TryGetCertificateAsync(this PluginBase plugin, string secretName)
{
+ plugin.ThrowIfUnloaded();
//Get the secret from the config file raw
string? rawSecret = TryGetSecretInternal(plugin, secretName);
if (rawSecret == null)