Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,14 @@
<!-- Microsoft -->
<PackageVersion Include="Microsoft.AspNetCore.OpenApi" Version="9.0.2" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.9" />
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="9.0.9" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="9.0.9" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.9" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
<!-- Shouldly -->
<PackageVersion Include="Shouldly" Version="4.3.0" />
<!-- StringTemplates -->
<PackageVersion Include="StringTemplates" Version="9.0.9" />
<!-- xUnit -->
<PackageVersion Include="xunit" Version="2.9.3" />
<PackageVersion Include="xunit.runner.visualstudio" Version="2.8.2" />
Expand Down
9 changes: 8 additions & 1 deletion StringTemplates.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,16 @@
<Folder Name="/src/">
<Project Path="src\StringTemplates\StringTemplates.csproj" Type="Classic C#" />
</Folder>
<Folder Name="/src/plugins/" />
<Folder Name="/src/plugins/">
<Project Path="src\plugins\StringTemplates.Plugins.Configuration\StringTemplates.Plugins.Configuration.csproj" Type="Classic C#" />
</Folder>
<Folder Name="/tests/">
<Project Path="tests\StringTemplates.IntegrationTests.Common\StringTemplates.IntegrationTests.Common.csproj" Type="Classic C#" />
<Project Path="tests\StringTemplates.IntegrationTests\StringTemplates.IntegrationTests.csproj" Type="Classic C#" />
<Project Path="tests\StringTemplates.UnitTests\StringTemplates.UnitTests.csproj" Type="Classic C#" />
</Folder>
<Folder Name="/tests/plugins/" />
<Folder Name="/tests/plugins/Configuration/">
<Project Path="tests\plugins\Configuration\StringTemplates.Plugins.Configuration.UnitTests\StringTemplates.Plugins.Configuration.UnitTests.csproj" Type="Classic C#" />
</Folder>
</Solution>
6 changes: 4 additions & 2 deletions examples/StringTemplates.Demo/Program.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
using Microsoft.AspNetCore.Mvc;
using StringTemplates.Extensions;
using StringTemplates.Plugins.Configuration;
using StringTemplates.Services;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddStringTemplates();
builder.Services.AddStringTemplates(options => options.AddPlugins(opts =>
opts.AddPluginSingleton<ConfigurationTemplatePlugin>()));

// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
builder.Services.AddOpenApi();
Expand All @@ -19,7 +21,7 @@
}

// Map endpoints
app.MapPost("system", (
app.MapPost("general", (
[FromBody] SystemRequest request,
[FromServices] ITemplateService templateService) =>
Results.Ok(templateService.ReplacePlaceholders(request.Template)));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\plugins\StringTemplates.Plugins.Configuration\StringTemplates.Plugins.Configuration.csproj" />
<ProjectReference Include="..\..\src\StringTemplates\StringTemplates.csproj" />
</ItemGroup>

Expand Down
17 changes: 13 additions & 4 deletions examples/StringTemplates.Demo/StringTemplates.http
Original file line number Diff line number Diff line change
@@ -1,18 +1,27 @@
### POST 1 request to use system template
POST https://localhost:7228/system
### POST 1 request to use generic plugins
POST https://localhost:7228/general
Content-Type: application/json

{
"template": "The year is \u007B\u007B#System#Year#System#\u007D\u007D."
}
###

### POST 2 request to use system template
POST https://localhost:7228/system
### POST 2 request to use generic plugins
POST https://localhost:7228/general
Content-Type: application/json

{
"template": "The time and date now is \u007B\u007B#System#Time.Now#System#\u007D\u007D of \u007B\u007B#System#Day.OfMonth#System#\u007D\u007Dth of \u007B\u007B#System#Month#System#\u007D\u007D \u007B\u007B#System#Year#System#\u007D\u007D."
}
###

### POST 3 request to use generic plugins
POST https://localhost:7228/general
Content-Type: application/json

{
"template": "This project was created in \u007B\u007B#System#Year#System#\u007D\u007D and the database used is '\u007B\u007B#Configuration#ConnectionStrings.DefaultConnection#Configuration#\u007D\u007D'."
}
###
###
4 changes: 4 additions & 0 deletions examples/StringTemplates.Demo/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,9 @@
"Microsoft.AspNetCore": "Warning"
}
},
"Random:Value": "This is a random value from appsettings.json",
"ConnectionStrings": {
"DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=aspnet-StringTemplates.Demo;Trusted_Connection=True;MultipleActiveResultSets=true"
},
"AllowedHosts": "*"
}
2 changes: 1 addition & 1 deletion src/StringTemplates/StringTemplates.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<Authors>Stratis-Dermanoutsos</Authors>
<Company>Stratis-Dermanoutsos</Company>

<Copyright>Copyright (c) Stratis Dermanoutsos 2025</Copyright>
<Copyright>Copyright © Stratis Dermanoutsos 2025</Copyright>
<PackageLicenseFile>LICENSE</PackageLicenseFile>

<PackageProjectUrl>https://github.com/Stratis-OSS/StringTemplates.Net</PackageProjectUrl>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using Microsoft.Extensions.Configuration;
using StringTemplates.Services;

namespace StringTemplates.Plugins.Configuration;

/// <summary>
/// <see cref="ITemplatePlugin"/> implementation for handling configuration values inside of template strings.
/// <br/><br/>
/// Placeholders are detected using the <c>Configuration</c> tag, E.g.
/// <list type="bullet">
/// <item><c>{{#Configuration#ConnectionStrings.Database#Configuration#}}</c></item>
/// <item><c>{{#Configuration#KeyVault.Auth.ClientId#Configuration#}}</c></item>
/// </list>
/// </summary>
/// <param name="configuration">The application's <see cref="IConfiguration"/> instance to read values from.</param>
/// <remarks>
/// For internal use only.
/// Not recommended to expose this plugin to outside users as it could be used to expose Environment Variables.
/// </remarks>
/// <para>
/// Separators in keys can be a dot (<c>.</c>), e.g. <c>ConnectionStrings.Database</c>.
/// The plugin later replaces dots with colons (<c>:</c>) to match the format used by <see cref="IConfiguration"/>.
/// E.g. <c>ConnectionStrings:Database</c>.
/// </para>
public sealed class ConfigurationTemplatePlugin(IConfiguration configuration) : ITemplatePlugin
{
public string PlaceholderTag => "Configuration";

public string? GetValueOrDefault(string placeholder)
{
return configuration[placeholder.Replace(".", ":")];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<PackageId>StringTemplates.Configuration</PackageId>
<Title>StringTemplates.Configuration</Title>
<Version>9.0.9</Version>

<Authors>Stratis-Dermanoutsos</Authors>
<Company>Stratis-Dermanoutsos</Company>

<Copyright>Copyright © Stratis Dermanoutsos 2025</Copyright>
<PackageLicenseFile>LICENSE</PackageLicenseFile>

<PackageProjectUrl>https://github.com/Stratis-OSS/StringTemplates.Net</PackageProjectUrl>
<RepositoryUrl>https://github.com/Stratis-OSS/StringTemplates.Net</RepositoryUrl>

<Description>The best way to handle templates and replace placeholders using IConfiguration.</Description>
<PackageTags>open-source oss csharp dotnet strings templates placeholders environment config</PackageTags>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageReleaseNotes>* First release.
* IConfiguration plugin.
</PackageReleaseNotes>

<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
</PropertyGroup>

<ItemGroup>
<None Include="../../../LICENSE" Pack="true" PackagePath="/"/>
<None Include="../../../README.md" Pack="true" PackagePath="/"/>
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" />
<PackageReference Include="StringTemplates"/>
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using Microsoft.AspNetCore.Mvc.Testing;

namespace StringTemplates.IntegrationTests;
namespace StringTemplates.IntegrationTests.Common;

public class ApiFactory : WebApplicationFactory<Program>
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Net.Http.Json;

namespace StringTemplates.IntegrationTests.Services.Abstractions;
// ReSharper disable once CheckNamespace
namespace StringTemplates.IntegrationTests.Common;

public abstract class ApiTests(ApiFactory factory)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">

<ItemGroup>
<ProjectReference Include="..\..\examples\StringTemplates.Demo\StringTemplates.Demo.csproj" />
<ProjectReference Include="..\..\src\StringTemplates\StringTemplates.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" />
</ItemGroup>

<ItemGroup>
<Folder Include="Services\" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using Shouldly;
using StringTemplates.IntegrationTests.Services.Abstractions;
using StringTemplates.IntegrationTests.Common;

namespace StringTemplates.IntegrationTests.Services;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using System.Globalization;
using Shouldly;
using StringTemplates.IntegrationTests.Services.Abstractions;
using StringTemplates.IntegrationTests.Common;

namespace StringTemplates.IntegrationTests.Services;

Expand All @@ -14,7 +14,7 @@ public async Task System_Date_Now_Replaced()
var payload = new { template = "Today is {{#System#Date.Now#System#}}." };

// Act
var result = await PostForStringAsync("system", payload);
var result = await PostForStringAsync("general", payload);

// Assert
result.ShouldBe($"Today is {expected}.");
Expand All @@ -28,7 +28,7 @@ public async Task System_Date_UtcNow_Replaced()
var payload = new { template = "UTC date: {{#System#Date.UtcNow#System#}}" };

// Act
var result = await PostForStringAsync("system", payload);
var result = await PostForStringAsync("general", payload);

// Assert
result.ShouldBe($"UTC date: {expected}");
Expand All @@ -41,7 +41,7 @@ public async Task System_DateTime_Now_Format_Is_Correct()
var payload = new { template = "[{{#System#DateTime.Now#System#}}]" };

// Act
var result = await PostForStringAsync("system", payload);
var result = await PostForStringAsync("general", payload);

// Assert
result.ShouldMatch(@"^\[\d{2}/\d{2}/\d{4} \d{2}:\d{2}:\d{2}\]$");
Expand All @@ -54,7 +54,7 @@ public async Task System_DateTime_UtcNow_Format_Is_Correct()
var payload = new { template = "<{{#System#DateTime.UtcNow#System#}}>" };

// Act
var result = await PostForStringAsync("system", payload);
var result = await PostForStringAsync("general", payload);

// Assert
result.ShouldMatch(@"^<\d{2}/\d{2}/\d{4} \d{2}:\d{2}:\d{2}>$");
Expand All @@ -71,8 +71,8 @@ public async Task System_Day_And_Month_Replaced()
var monthPayload = new { template = "It is {{#System#Month#System#}} now." };

// Act
var dayResult = await PostForStringAsync("system", dayPayload);
var monthResult = await PostForStringAsync("system", monthPayload);
var dayResult = await PostForStringAsync("general", dayPayload);
var monthResult = await PostForStringAsync("general", monthPayload);

// Assert
dayResult.ShouldBe($"Happy {expectedDay}!");
Expand All @@ -86,7 +86,7 @@ public async Task System_Time_Now_Format_Is_Correct()
var payload = new { template = "time={{#System#Time.Now#System#}}" };

// Act
var result = await PostForStringAsync("system", payload);
var result = await PostForStringAsync("general", payload);

// Assert
result.ShouldMatch(@"^time=\d{2}:\d{2}:\d{2}$");
Expand All @@ -99,7 +99,7 @@ public async Task System_Time_UtcNow_Format_Is_Correct()
var payload = new { template = "utc={{#System#Time.UtcNow#System#}}" };

// Act
var result = await PostForStringAsync("system", payload);
var result = await PostForStringAsync("general", payload);

// Assert
result.ShouldMatch(@"^utc=\d{2}:\d{2}:\d{2}$");
Expand All @@ -113,7 +113,7 @@ public async Task System_Unknown_Placeholder_Remains()
var payload = new { template };

// Act
var result = await PostForStringAsync("system", payload);
var result = await PostForStringAsync("general", payload);

// Assert
result.ShouldBe(template);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

<ItemGroup>
<PackageReference Include="coverlet.collector"/>
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing"/>
<PackageReference Include="Microsoft.NET.Test.Sdk"/>
<PackageReference Include="Shouldly"/>
<PackageReference Include="xunit"/>
Expand All @@ -18,8 +17,8 @@
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\examples\StringTemplates.Demo\StringTemplates.Demo.csproj" />
<ProjectReference Include="..\..\src\StringTemplates\StringTemplates.csproj" />
<ProjectReference Include="..\StringTemplates.IntegrationTests.Common\StringTemplates.IntegrationTests.Common.csproj" />
</ItemGroup>

</Project>
Loading