This project provides a framework for building and executing command-based functionality through an extensible plugin architecture. The project supports the creation of commands, hooks, and plugins that can be dynamically loaded and executed via an API.
-
Command execution : Define commands as plugins that can be dynamically loaded at runtime and executed via a common interface.
-
Hooks : Support for
PreandPostexecution hooks. -
Plugin loading : Load plugins from a directory or assembly dynamically.
-
API endpoints : Expose commands through HTTP endpoints, allowing remote execution of commands via POST and GET requests.
-
Multi-tenant support : Ability to execute commands per tenant.
-
Versioning : Commands are versioned, allowing for multiple versions of the same command to coexist.
You can install the Serverless.LambdaCommand library from NuGet by running the following command in your NuGet Package Manager Console:
Install-Package Serverless.LambdaCommandThis package provides the required components to implement a serverless-like plugin architecture where commands can be dynamically loaded and executed, similar to AWS Lambda.
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
// Configure the serverless library
services.AddCommandApiLib(options =>
{
options.PluginDirectory = "plugins"; // Directory where plugins are stored
options.EnableLogging = true; // Enable logging (can be toggled on/off)
});
// Add other necessary services for the application (e.g., database, authentication, etc.)
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ICommandRouter router, IExecutionContext context)
{
if (env.IsDevelopment())
{
// Use developer exception page in development environment
app.UseDeveloperExceptionPage();
}
app.UseRouting(); // Enable routing for the app
// Add the serverless library middleware
app.UseCommandApiLib(router, context);
// Load the static plugins (from the executing assembly)
PluginLoader.LoadPluginsFromAssembly(Assembly.GetExecutingAssembly(), router);
// Load dynamically any command plugins from a common location (e.g., the 'plugins' folder)
PluginLoader.LoadCommonPlugins(router);
// Setup any additional middleware, such as authentication, authorization, etc.
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers(); // Map controller routes if using MVC
});
}
}-
A suitable logging provider, such as Serilog
-
Clone the repository.
-
Navigate to the project directory.
-
Restore the project dependencies using the following command:
dotnet restore- Build the project:
dotnet build- Run the project:
dotnet runCommands implement the ICommand interface, which provides several overloaded Execute methods. You can define your command logic by implementing these methods.
public class AddCommand : AbstractCommand, ICommand
{
public async override Task<string> Execute(string parameters, ICommandContext? context = null, IExecutionContext ec =null)
{
await Task.Delay(1);
var numbers = parameters.Split(new char[] { ',', ' ', '+' }, StringSplitOptions.RemoveEmptyEntries)
.Select(int.Parse);
int sum = numbers.Sum();
return sum.ToString();
}
}In this example, the AddCommand splits the input parameters and calculates the sum of numbers.
The PluginLoader class provides mechanisms to dynamically load commands from assemblies or directories.
var pluginLoader = new PluginLoader(logger, router, pluginDirectory);
pluginLoader.LoadPlugins();This loads all commands and hooks available in the specified plugin directory.
Commands can be executed via HTTP endpoints.
-
POST :
/api/execute/{commandName}/{actionName}/{version}/{tenantId}/{inputFormat}/{outputFormat} -
GET :
/api/execute/{commandName}/{actionName}/{tenantId}/{version}/{outputFormat}/{param1}/{param2}/...Example of a POST request to execute theAddCommand:
curl -X POST "http://localhost:5000/api/execute/Math/Add/1.0.0.0/TenantA" -d "2+3+4"You can configure various aspects of the framework using the CommandApiOptions class:
public class CommandApiOptions
{
public string PluginDirectory { get; set; } = "plugins";
public bool EnableLogging { get; set; } = true;
}Configure the framework in Startup.cs:
services.AddCommandApiLib(options =>
{
options.PluginDirectory = "custom_plugins";
options.EnableLogging = true;
});-
HookType: Defines hook types as eitherPreorPost. -
InputDataandOutputData: Specify types of data passed into and out of the command execution.
ICommand: Represents a command to be executed. VariousExecutemethods are overloaded to accept different types of input, including strings, objects, and contexts.
-
AbstractCommand: A base class implementing theICommandinterface, serving as a foundation for creating commands. -
PluginLoader: Responsible for loading plugins from a directory or assembly. It registers all discovered commands and hooks in the providedICommandRouter.
Feel free to open issues or submit pull requests to help improve the framework.
Here’s how to initialize the serverless library in a sample program using Startup.cs. This setup includes loading command plugins dynamically from a specified directory (plugins) and configuring basic routing, logging, and plugin loading.
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
// Configure the serverless library
services.AddCommandApiLib(options =>
{
options.PluginDirectory = "plugins"; // Directory where plugins are stored
options.EnableLogging = true; // Enable logging (can be toggled on/off)
});
// Add other necessary services for the application (e.g., database, authentication, etc.)
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ICommandRouter router, IExecutionContext context)
{
if (env.IsDevelopment())
{
// Use developer exception page in development environment
app.UseDeveloperExceptionPage();
}
app.UseRouting(); // Enable routing for the app
// Add the serverless library middleware
app.UseCommandApiLib(router, context);
// Load the static plugins (from the executing assembly)
PluginLoader.LoadPluginsFromAssembly(Assembly.GetExecutingAssembly(), router);
// Load dynamically any command plugins from a common location (e.g., the 'plugins' folder)
PluginLoader.LoadCommonPlugins(router);
// Setup any additional middleware, such as authentication, authorization, etc.
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers(); // Map controller routes if using MVC
});
}
}- ConfigureServices :
- The
AddCommandApiLibmethod initializes the serverless library, specifying the directory where plugins are stored (e.g., "plugins"). The logging can be enabled or disabled as needed.
- Configure :
-
The method sets up the application's request pipeline. It includes error handling, routing, and adding the serverless library middleware (
app.UseCommandApiLib). -
Plugin Loading : Two methods,
LoadPluginsFromAssemblyandLoadCommonPlugins, handle loading both static and dynamically loaded plugins for the system. These could represent commands or functions that the system supports.
- PluginLoader :
- This loads command plugins into the router from the current assembly and a shared directory. It enables dynamic extensibility by supporting plugin architecture.
This structure allows you to dynamically deploy and run command plugins like AWS Lambda functions, making your system highly flexible and customizable.