Skip to content

Commit f6ffdac

Browse files
committed
Add Oakton config description system part
1 parent cfa9e0d commit f6ffdac

File tree

11 files changed

+400
-0
lines changed

11 files changed

+400
-0
lines changed

OaktonDescribe/.vscode/launch.json

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
{
2+
// Use IntelliSense to learn about possible attributes.
3+
// Hover to view descriptions of existing attributes.
4+
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5+
"version": "0.2.0",
6+
"configurations": [
7+
{
8+
"name": ".NET Core Launch (web)",
9+
"type": "coreclr",
10+
"request": "launch",
11+
"preLaunchTask": "build",
12+
"program": "${workspaceFolder}/bin/Debug/net5.0/temp.dll",
13+
"args": [],
14+
"cwd": "${workspaceFolder}",
15+
"stopAtEntry": false,
16+
"serverReadyAction": {
17+
"action": "openExternally",
18+
"pattern": "\\bNow listening on:\\s+(https?://\\S+)"
19+
},
20+
"env": {
21+
"ASPNETCORE_ENVIRONMENT": "Development"
22+
},
23+
"sourceFileMap": {
24+
"/Views": "${workspaceFolder}/Views"
25+
}
26+
},
27+
{
28+
"name": ".NET Core Attach",
29+
"type": "coreclr",
30+
"request": "attach",
31+
"processId": "${command:pickProcess}"
32+
}
33+
]
34+
}

OaktonDescribe/.vscode/tasks.json

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
{
2+
"version": "2.0.0",
3+
"tasks": [
4+
{
5+
"label": "build",
6+
"command": "dotnet",
7+
"type": "process",
8+
"args": [
9+
"build",
10+
"${workspaceFolder}/temp.csproj",
11+
"/property:GenerateFullPaths=true",
12+
"/consoleloggerparameters:NoSummary"
13+
],
14+
"problemMatcher": "$msCompile"
15+
},
16+
{
17+
"label": "publish",
18+
"command": "dotnet",
19+
"type": "process",
20+
"args": [
21+
"publish",
22+
"${workspaceFolder}/temp.csproj",
23+
"/property:GenerateFullPaths=true",
24+
"/consoleloggerparameters:NoSummary"
25+
],
26+
"problemMatcher": "$msCompile"
27+
},
28+
{
29+
"label": "watch",
30+
"command": "dotnet",
31+
"type": "process",
32+
"args": [
33+
"watch",
34+
"run",
35+
"${workspaceFolder}/temp.csproj",
36+
"/property:GenerateFullPaths=true",
37+
"/consoleloggerparameters:NoSummary"
38+
],
39+
"problemMatcher": "$msCompile"
40+
}
41+
]
42+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
using System.Collections.Generic;
2+
using System.IO;
3+
using System.Linq;
4+
using System.Threading.Tasks;
5+
using Microsoft.Extensions.Configuration;
6+
using Oakton.Descriptions;
7+
using Spectre.Console;
8+
9+
namespace OaktonDescribe
10+
{
11+
public class ConfigDescriptionSystemPart: IDescribedSystemPart, IWriteToConsole
12+
{
13+
readonly IConfigurationRoot _configRoot;
14+
15+
public ConfigDescriptionSystemPart(IConfiguration config)
16+
{
17+
_configRoot = config as IConfigurationRoot;
18+
}
19+
20+
public string Title => "Configuration values and sources";
21+
22+
public Task Write(TextWriter writer)
23+
{
24+
return writer.WriteAsync(_configRoot.GetDebugView());
25+
}
26+
27+
public Task WriteToConsole()
28+
{
29+
void RecurseChildren(IHasTreeNodes node, IEnumerable<IConfigurationSection> children)
30+
{
31+
foreach (IConfigurationSection child in children)
32+
{
33+
(string Value, IConfigurationProvider Provider) valueAndProvider = GetValueAndProvider(_configRoot, child.Path);
34+
35+
IHasTreeNodes parent = node;
36+
if (valueAndProvider.Provider != null)
37+
{
38+
node.AddNode(new Table()
39+
.Border(TableBorder.None)
40+
.HideHeaders()
41+
.AddColumn("Key")
42+
.AddColumn("Value")
43+
.AddColumn("Provider")
44+
.HideHeaders()
45+
.AddRow($"[yellow]{child.Key}[/]", valueAndProvider.Value, $@"([grey]{valueAndProvider.Provider}[/])")
46+
);
47+
}
48+
else
49+
{
50+
parent = node.AddNode($"[yellow]{child.Key}[/]");
51+
}
52+
53+
RecurseChildren(parent, child.GetChildren());
54+
}
55+
}
56+
57+
var tree = new Tree(string.Empty);
58+
59+
RecurseChildren(tree, _configRoot.GetChildren());
60+
61+
AnsiConsole.Render(tree);
62+
63+
return Task.CompletedTask;
64+
}
65+
66+
private static (string Value, IConfigurationProvider Provider) GetValueAndProvider(
67+
IConfigurationRoot root,
68+
string key)
69+
{
70+
foreach (IConfigurationProvider provider in root.Providers.Reverse())
71+
{
72+
if (provider.TryGet(key, out string value))
73+
{
74+
return (value, provider);
75+
}
76+
}
77+
78+
return (null, null);
79+
}
80+
}
81+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Threading.Tasks;
5+
using Microsoft.AspNetCore.Mvc;
6+
using Microsoft.Extensions.Logging;
7+
8+
namespace OaktonDescribe.Controllers
9+
{
10+
[ApiController]
11+
[Route("[controller]")]
12+
public class WeatherForecastController : ControllerBase
13+
{
14+
private static readonly string[] Summaries = new[]
15+
{
16+
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
17+
};
18+
19+
private readonly ILogger<WeatherForecastController> _logger;
20+
21+
public WeatherForecastController(ILogger<WeatherForecastController> logger)
22+
{
23+
_logger = logger;
24+
}
25+
26+
[HttpGet]
27+
public string Get([FromQuery] TestClass model)
28+
{
29+
_logger.LogWarning("Value: {id}", model.Id);
30+
return model.Id.ToString();
31+
}
32+
}
33+
34+
public class TestClass
35+
{
36+
public OrderId Id {get;set;}
37+
}
38+
39+
[StronglyTypedId(backingType: StronglyTypedIdBackingType.Guid, jsonConverter: StronglyTypedIdJsonConverter.NewtonsoftJson)]
40+
public partial struct OrderId {}
41+
}

OaktonDescribe/OaktonDescribe.csproj

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<Project Sdk="Microsoft.NET.Sdk.Web">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net5.0</TargetFramework>
5+
<UserSecretsId>1589AE0E-AE34-416A-9E6A-EB0CABDDABB6</UserSecretsId>
6+
</PropertyGroup>
7+
8+
<ItemGroup>
9+
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="5.0.1" NoWarn="NU1605" />
10+
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="5.0.1" NoWarn="NU1605" />
11+
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="5.0.2" />
12+
<PackageReference Include="Oakton" Version="3.0.2" />
13+
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.6.3" />
14+
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
15+
<PackageReference Include="StronglyTypedId" Version="0.2.0" />
16+
</ItemGroup>
17+
18+
</Project>

OaktonDescribe/Program.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Threading.Tasks;
5+
using Microsoft.AspNetCore.Hosting;
6+
using Microsoft.Extensions.Configuration;
7+
using Microsoft.Extensions.Hosting;
8+
using Microsoft.Extensions.Logging;
9+
using Oakton;
10+
11+
namespace OaktonDescribe
12+
{
13+
public class Program
14+
{
15+
public static Task<int> Main(string[] args)
16+
{
17+
System.Console.OutputEncoding = System.Text.Encoding.Unicode;
18+
return CreateHostBuilder(args)
19+
.RunOaktonCommands(args);
20+
}
21+
22+
public static IHostBuilder CreateHostBuilder(string[] args) =>
23+
Host.CreateDefaultBuilder(args)
24+
.ConfigureWebHostDefaults(webBuilder =>
25+
{
26+
webBuilder.UseStartup<Startup>();
27+
});
28+
}
29+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
{
2+
"$schema": "http://json.schemastore.org/launchsettings.json",
3+
"iisSettings": {
4+
"windowsAuthentication": false,
5+
"anonymousAuthentication": true,
6+
"iisExpress": {
7+
"applicationUrl": "http://localhost:26578",
8+
"sslPort": 44345
9+
}
10+
},
11+
"profiles": {
12+
"IIS Express": {
13+
"commandName": "IISExpress",
14+
"launchBrowser": true,
15+
"launchUrl": "swagger",
16+
"environmentVariables": {
17+
"ASPNETCORE_ENVIRONMENT": "Development"
18+
}
19+
},
20+
"temp": {
21+
"commandName": "Project",
22+
"dotnetRunMessages": "true",
23+
"launchBrowser": true,
24+
"launchUrl": "swagger",
25+
"applicationUrl": "https://localhost:5001;http://localhost:5000",
26+
"environmentVariables": {
27+
"ASPNETCORE_ENVIRONMENT": "Development"
28+
}
29+
},
30+
"describe": {
31+
"commandName": "Project",
32+
"dotnetRunMessages": "false",
33+
"launchBrowser": true,
34+
"launchUrl": "swagger",
35+
"applicationUrl": "https://localhost:5001;http://localhost:5000",
36+
"commandLineArgs": "describe",
37+
"environmentVariables": {
38+
"ASPNETCORE_ENVIRONMENT": "Development"
39+
}
40+
}
41+
}
42+
}

OaktonDescribe/Startup.cs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using Microsoft.AspNetCore.Builder;
5+
using Microsoft.AspNetCore.Hosting;
6+
using Microsoft.AspNetCore.Http;
7+
using Microsoft.AspNetCore.HttpsPolicy;
8+
using Microsoft.AspNetCore.Mvc;
9+
using Microsoft.Extensions.Configuration;
10+
using Microsoft.Extensions.DependencyInjection;
11+
using Microsoft.Extensions.Hosting;
12+
using Microsoft.Extensions.Logging;
13+
using Microsoft.OpenApi.Models;
14+
using Oakton.Descriptions;
15+
16+
namespace OaktonDescribe
17+
{
18+
public class Startup
19+
{
20+
public Startup(IConfiguration configuration)
21+
{
22+
Configuration = configuration;
23+
}
24+
25+
public IConfiguration Configuration { get; }
26+
27+
// This method gets called by the runtime. Use this method to add services to the container.
28+
public void ConfigureServices(IServiceCollection services)
29+
{
30+
31+
services
32+
.AddControllers()
33+
.AddNewtonsoftJson();
34+
services.AddSwaggerGen(c =>
35+
{
36+
c.SwaggerDoc("v1", new OpenApiInfo { Title = "OaktonDescribe", Version = "v1" });
37+
});
38+
39+
services.AddDescription<ConfigDescriptionSystemPart>();
40+
}
41+
42+
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
43+
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
44+
{
45+
if (env.IsDevelopment())
46+
{
47+
app.UseDeveloperExceptionPage();
48+
app.UseSwagger();
49+
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "OaktonDescribe v1"));
50+
}
51+
52+
app.UseHttpsRedirection();
53+
54+
app.UseRouting();
55+
56+
app.UseAuthorization();
57+
58+
app.UseEndpoints(endpoints =>
59+
{
60+
endpoints.MapControllers();
61+
62+
if(env.IsDevelopment())
63+
{
64+
endpoints.MapGet("/debug-config", ctx =>
65+
{
66+
var config = (Configuration as IConfigurationRoot).GetDebugView();
67+
return ctx.Response.WriteAsync(config);
68+
});
69+
}
70+
});
71+
}
72+
}
73+
}

OaktonDescribe/WeatherForecast.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using System;
2+
3+
namespace OaktonDescribe
4+
{
5+
public class WeatherForecast
6+
{
7+
public DateTime Date { get; set; }
8+
9+
public int TemperatureC { get; set; }
10+
11+
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
12+
13+
public string Summary { get; set; }
14+
}
15+
}

0 commit comments

Comments
 (0)