Skip to content

noncommunicado/KutCode.KeyInject

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

28 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

KeyInject

📦 KeyInject is a .NET library designed to inject values into configurations using regular expression patterns, with support for nested patterns.
⚙️ This facilitates dynamic and flexible configuration management in .NET applications.
🔄 Configuration hot-reloads supported.

📋 Table of contents

🧩 Features

  • Regex-Based Injection: Utilize regular expressions to identify and replace placeholders in your configuration files.
  • Nested Pattern Support: Handle complex configurations with nested placeholders seamlessly.
  • Easy Integration: Designed for straightforward integration into existing .NET projects.
  • Supported versions: net5.0 and higher version supported

📜 Installation

Install KeyInject using NuGet Package Manager:

Install-Package KeyInject

Or via the .NET CLI:

dotnet add package KeyInject

All versions can be found here.

🚀 Quick Start

Look at actual examples here ./examples;

Basic example

In appsettings.json:

{
  "ConnectionStrings": {
    "Main": "server=${Database:Server};user=${DB_USER};password=${DB_PASSWORD}"
  }
}

In Environment variables:

DB_USER=addy
DB_PASSWORD=greenlover

In user secrets:

{
  "Database": {
    "Server": "1.4.8.8"
  }
}

In Program.cs file:

using KeyInject;

var builder = WebApplication.CreateBuilder(args);

// Configuration order is up to you.
// Remember, that ConfigurationProviders overrides each other!
builder.Configuration.AddJsonFile("appsettings.json");
builder.Configuration.AddEnvironmentVariables();
// ✅ Add Key Injection exactly at the latest position 
builder.Configuration.AddKeyInject();

// add services ...

var app = builder.Build();

var conn = app.Configuration.GetConnectionString("Main");
await Console.Out.WriteLineAsync(conn);
// ✅ Output: server=1.4.8.8;user=addy;password=greenlover

await app.RunAsync();

⚙️ Basic Configuration

From appsettings.json

KeyInject always enriches from appsettings.json.
It's not neccessary to provide json configuration.
By default, ${_} pattern will be used if no other patterns provided.
(All the patterns will be described below)

Example configuration

{
  "KeyInject": {
    // simply enable or disable globally 
    "Enabled": true,
    // allows to reload configuration if other Configuration Provides triggers OnReload()
    "SetReloadEnabled": true,
    // ignore case of pattern key group >> ${IgNore_Case_Of_thIs_woRD}
    "IgnoreCase": true,
    // set how many time config will be injected to resolve circular dependencies
    "ReplaceRepeatCount": 10,
    // from preset patterns ${_}, <<_>> ...
    // if Patterns is empty, "${_}" pattern will be used anyway
    "Patterns": [
      "${_}", "{{_}}", "$<_>", "<<_>>", "!{_}!", "%_%"
    ],
    // adding custom regex pattern. 
    // warn! must use ?<key> regex group, see documentation.
    // no exception throw on bad regex.
    "RegexPatterns": [
      "!\\{(?<key>[^{}]+)\\}!"
    ],
    // adding custom prefixes
    "KeyPrefixes": [
      "PRE_", "DATABASE_"
    ]
  }
}

Extended configuration see in 💉 Dependency Injection part.

🔑 Key Prefix

Prefixes used to specify that we only want to replace keys starts with some value.
In example, for configured prefix DB_:
🚫 DATABASE_USER_PASSWORD - wont be replaced
DB_USER_PASSWORD - woill be replaced

💡Notice!
If you specified prefixes, then only those patterns that start with this prefix will be replaced.

Can be registered with:

WebApplication.CreateBuilder(args)
    .Configuration.AddKeyInject(b => b
        .AddKeyPrefix("PRE_")
        .AddKeyPrefix("DATABASE_")
);

Or with appsettings:

{
  "KeyInject": {
    "KeyPrefixes": [
      "PRE_", "DATABASE_"
    ]
  }
}

🎭 Patterns

Preset patterns

💡 Here is six default pattern names:
${_}       {{_}}     $<_>
<<_>>     !{_}!     %_%

Can be registered with:

WebApplication.CreateBuilder(args)
    .Configuration.AddKeyInject(b => b
        .AddPresetPattern("${_}")
        .AddPresetPattern("$<_>")
);

Or with appsettings:

{
  "KeyInject": {
    "Patterns": [
      "${_}", "{{_}}", "$<_>", "<<_>>", "!{_}!", "%_%"
    ]
  }
}

⚠️ If no Pattern presented in explicit configuration will be used default: ${_}

Their detailed description:

  1. ${_}
  • regex: \$\{(?<key>[^\{\}]+)\}
  • example: ${SOMEKEY}, ${some_key_2}
  1. {{_}}
  • regex: \{\{(?<key>[^\{\}]+)\}\}
  • example: {{SOMEKEY}}, {{some_key_2}}
  1. $<_>
  • regex: \$<(?<key>[^<>]+)>
  • example: $<SOMEKEY>, $<some_key_2>
  1. <<_>>
  • regex: <<(?<key>[^<>]+)>>
  • example: <<SOMEKEY>>, <<some_key_2>>
  1. !{_}!
  • regex: !\{(?<key>[^{}]+)\}!
  • example: !{SOMEKEY}!, !{some_key_2}!
  1. %_%
  • regex: %(?<key>[^%]+)%
  • example: %SOMEKEY%, %some_key_2%

⚠️ Notice!
You must specify preset pattern name exactly in provided format!
Pattern names like "${...}" instead of ${_} is not supported!

Of course, you can use multiple patterns at the same time.

🔧 Custom patterns

You can use custom Regex patterns with builder or appsettings configuration.
You must to specify ?<key> regex group in pattern, like:

!\{(?<key>[^{}]+)\}!

⚠️ Group naming must be exactly: key

Custom pattern registration examples:

  • appsettings.json :
{
  "KeyInject": {
    "RegexPatterns": [
      "!\\{(?<key>[^{}]+)\\}!"
    ]
  }
}
  • with DI builder:
builder.Configuration.AddKeyInject(b => b
	// adding custom regex pattern. Warn! Must to use ?<key> regex group, see documentation.
	// no exception throw on bad regex.
	.AddRegexPattern(@"!\{(?<key>[^{}]+)\}!")
	// notice, adding built Regex CAN throw exception if regex text was incorrect
	.AddRegexPattern(new Regex(@"!\{(?<key>[^{}]+)\}!"))
);

🪆 Nested patterns

You can use nested patterns, by default supports 5 levels of nesting.
Here is an example of nesting:

  1. In appsettings.json
{
  "Connection": "${CONN}"
}
  1. In Environment variable:
CONN="server=${DB_IP};user=${DB_USER};password=${DB_PASSWORD}"
  1. In Vault config provider (or some else provider too):
DB_IP=1.2.3.4
DB_USER=rootuser
DB_PASSWORD=password123
  1. Result configuration string will be:
void DisplayConfig(IConfiguration config) {
	Console.WriteLine(config["Connection"]);
	// ✅ Output: server=1.2.3.4;user=rootuser;password=password123
}

⚠️ Default supported nesting for 5 levels, and it's enough for most cases.

You can change levels count with:

Configuration.AddKeyInject(b 
    => b.SetReplaceRepeatCount(10)
);

or in appsettings.json:

{
  "KeyInject": {
    "ReplaceRepeatCount": 10
  }
}

💉 Dependency Injection

⚠️ Warning!
Use .AddKeyInject() after other Configuration Provides!

Example:

var builder = WebApplication.CreateBuilder(args);
// ... 
builder.Configuration.AddKeyInject(b => b
	// simply enable or disable globally 
	.SetEnabled(true)
    // allows to reload configuration if other Configuration Provides triggers OnReload()
    .SetReloadEnabled(true)
	// adding custom prefixes
	.AddKeyPrefix("PRE_")
	.AddKeyPrefix("DATABASE_")
	// adding custom regex pattern. Warn! Must to use ?<key> regex group, see documentation.
	// no exception throw on bad regex.
	.AddRegexPattern(@"!\{(?<key>[^{}]+)\}!")
	// notice, adding built Regex CAN throw exception if regex text was incorrect
	.AddRegexPattern(new Regex(@"!\{(?<key>[^{}]+)\}!"))
	// from preset patterns ${_}, <<_>> ...
	.AddPresetPattern("${_}")
	// set how many time config will be injected to resolve circular dependencies
	.SetReplaceRepeatCount(10)
	// ignore case of pattern key group >> ${IgNore_Case_Of_thIs_woRD}
	.SetIgnoreCase(true)
	// choose yor custom config section instead default "KeyInject", first way:
	.EnrichFromAppSettings(builder.Configuration.GetSection("MyCustomSection"))
	// second way:
	.EnrichFromAppSettings(c => c.GetSection("MyCustomSection"))
);

🪵 Logging

You can add logging to log configuration sources and replaced keys.
Simply pass ILoggerFactory, example:

var builder = WebApplication.CreateBuilder(args);
// ... other config sources ...
builder.Configuration.AddKeyInject(LoggerFactory.Create(b => b
    .SetMinimumLevel(LogLevel.Debug)
    .AddConsole()
    .AddDebug())
);

Console output example:

info: KeyInject.Injection.InjectionProcessor[0]
      KeyInject starting process configuration
info: KeyInject.Injection.InjectionProcessor[0]
      KeyInject configuration: {"Enabled":true,"ReplaceRepeatCount":5,"IgnoreCase":true,"KeyPrefixes":[],"RegexPatterns":["\\$\\{(?\u003Ckey\u003E[^\\{\\}]\u002B)\\}"]}
info: KeyInject.Injection.InjectionProcessor[0]
      Found configuraion providers:
        MemoryConfigurationProvider
        EnvironmentVariablesConfigurationProvider Prefix: 'ASPNETCORE_'
        EnvironmentVariablesConfigurationProvider Prefix: 'DOTNET_'
        JsonConfigurationProvider for 'appsettings.json' (Optional)
        JsonConfigurationProvider for 'appsettings.Development.json' (Optional)
        EnvironmentVariablesConfigurationProvider
        Microsoft.Extensions.Configuration.ChainedConfigurationProvider
        JsonConfigurationProvider for 'appsettings.json' (Required)
info: KeyInject.Injection.InjectionProcessor[0]
      For key [ConnectionStrings:Main] made [3] replacements 
info: KeyInject.Injection.InjectionProcessor[0]
      For key [ConnectionStrings:Some:Data:Key-3] made [1] replacements 
info: KeyInject.Injection.InjectionProcessor[0]
      Configuration loaded

💡 The logging configuration is optional.
You don't have to pass ILoggerFactory to the AddKeyInject(), just call without parameters if logging is not needed.

☕ Contribution

If you wanna to buy me a coffee 😃, I will be grateful for any tokens in TON network:
💎 noncommunicado.ton
💎 UQD0zFgp0p-eFnbL4cPA6DYqoeWzGbCA81KuU6BKwdFmf8jv

Languages