SteeltoeOSS / Steeltoe

.NET Components for Externalized Configuration, Database Connectors, Service Discovery, Logging and Distributed Tracing, Application Management, Security, and more.
https://steeltoe.io
Apache License 2.0
1k stars 164 forks source link

Support for Functions in IConfiguration Placeholder Resolver #1345

Open alexanderpino opened 3 weeks ago

alexanderpino commented 3 weeks ago

Summary: We request the enhancement of the IConfiguration placeholder resolver in Steeltoe to support the application of functions to configuration values. This functionality would significantly improve the flexibility and usability of configuration management, particularly for dynamic values such as URLs, AWS ARNs, and other environment-specific settings.

Use Case: In scenarios where configuration values need to be dynamically adjusted based on the environment or other factors, the ability to apply functions directly within placeholders would be highly beneficial. For example, consider a scenario where the environment name needs to be transformed to lowercase before being included in a URL:

http://${lower(environment)?www}.domain/

In this example, the lower function would convert the environment value to lowercase, ensuring consistency in URL formatting across different environments. Similar use cases include formatting AWS ARNs, normalizing paths, or applying custom transformations to configuration values.

Proposed Implementation: We propose extending the IConfiguration placeholder resolver to support a set of common functions (e.g., lower, upper, trim, replace) that can be applied within the placeholders. The syntax could follow the pattern of ${function(configKey)}, allowing for intuitive and flexible usage.

bart-vmware commented 3 weeks ago

Hi @alexanderpino, thanks for reaching out.

While I understand the potential convenience of such a feature, the implementation is rather problematic.

First of all, the set of built-in functions requested is kind of arbitrary. It's pretty easy to come up with many more, such as min/max, substring, length, if, etc. This raises all kinds of design questions (aside from the time it takes to implement/test/document/maintain). For example, would upper work in the current culture, or the invariant culture? Does length only work on strings, or collections too? We don't intend to implement a programming language in the configuration system. Earlier versions of Steeltoe contained SpEL, but we dropped it because of its complexity and poor interop with .NET (for example, generics and modern C# language features can't be used).

Therefore, I think it would make more sense if developers could plug in their own .NET functions into the placeholder resolver, so they can build them the way they like. But that raises another problem. Each time a configuration value is requested, these functions need to re-execute (because Steeltoe can't know if something changed). And configuration may reload at any time. Executing (potentially expensive) user code could lock up the whole system. Aside from that, the experience would be quite poor, as there's no way to inject dependencies (because the IoC container isn't built yet). Sooner or later developers will want to execute async code in these functions, etc.

The next best thing is to add an in-memory configuration source at startup (assuming the environment doesn't change), for example:

var builder = WebApplication.CreateBuilder();
builder.Configuration.AddInMemoryCollection(new Dictionary.....);
builder.Configuration.AddPlaceholderResolver();

The in-memory dictionary would define the placeholder with the value obtained from builder.Environment.EnvironmentName.

Would this cover your use case?