Skip to content

Commit

Permalink
Support passing OpenAPI spec version and document name via dotnet-get…
Browse files Browse the repository at this point in the history
…document (#55823)
  • Loading branch information
captainsafia authored May 22, 2024
1 parent 5f792ae commit da02847
Show file tree
Hide file tree
Showing 17 changed files with 479 additions and 68 deletions.
38 changes: 38 additions & 0 deletions AspNetCore.sln
Original file line number Diff line number Diff line change
Expand Up @@ -1812,6 +1812,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Static
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.StaticAssets.Tests", "src\StaticAssets\test\Microsoft.AspNetCore.StaticAssets.Tests.csproj", "{9536C284-65B4-4884-BB50-06D629095C3E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GetDocumentInsider.Tests", "src\Tools\GetDocumentInsider\tests\GetDocumentInsider.Tests.csproj", "{6A19D94D-2BC6-4198-BE2E-342688FDBA4B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GetDocumentSample", "src\Tools\GetDocumentInsider\sample\GetDocumentSample.csproj", "{D8F7091E-A2D1-4E81-BA7C-97EAE392D683}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -10941,6 +10945,38 @@ Global
{9536C284-65B4-4884-BB50-06D629095C3E}.Release|x64.Build.0 = Release|Any CPU
{9536C284-65B4-4884-BB50-06D629095C3E}.Release|x86.ActiveCfg = Release|Any CPU
{9536C284-65B4-4884-BB50-06D629095C3E}.Release|x86.Build.0 = Release|Any CPU
{6A19D94D-2BC6-4198-BE2E-342688FDBA4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6A19D94D-2BC6-4198-BE2E-342688FDBA4B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6A19D94D-2BC6-4198-BE2E-342688FDBA4B}.Debug|arm64.ActiveCfg = Debug|Any CPU
{6A19D94D-2BC6-4198-BE2E-342688FDBA4B}.Debug|arm64.Build.0 = Debug|Any CPU
{6A19D94D-2BC6-4198-BE2E-342688FDBA4B}.Debug|x64.ActiveCfg = Debug|Any CPU
{6A19D94D-2BC6-4198-BE2E-342688FDBA4B}.Debug|x64.Build.0 = Debug|Any CPU
{6A19D94D-2BC6-4198-BE2E-342688FDBA4B}.Debug|x86.ActiveCfg = Debug|Any CPU
{6A19D94D-2BC6-4198-BE2E-342688FDBA4B}.Debug|x86.Build.0 = Debug|Any CPU
{6A19D94D-2BC6-4198-BE2E-342688FDBA4B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6A19D94D-2BC6-4198-BE2E-342688FDBA4B}.Release|Any CPU.Build.0 = Release|Any CPU
{6A19D94D-2BC6-4198-BE2E-342688FDBA4B}.Release|arm64.ActiveCfg = Release|Any CPU
{6A19D94D-2BC6-4198-BE2E-342688FDBA4B}.Release|arm64.Build.0 = Release|Any CPU
{6A19D94D-2BC6-4198-BE2E-342688FDBA4B}.Release|x64.ActiveCfg = Release|Any CPU
{6A19D94D-2BC6-4198-BE2E-342688FDBA4B}.Release|x64.Build.0 = Release|Any CPU
{6A19D94D-2BC6-4198-BE2E-342688FDBA4B}.Release|x86.ActiveCfg = Release|Any CPU
{6A19D94D-2BC6-4198-BE2E-342688FDBA4B}.Release|x86.Build.0 = Release|Any CPU
{D8F7091E-A2D1-4E81-BA7C-97EAE392D683}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D8F7091E-A2D1-4E81-BA7C-97EAE392D683}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D8F7091E-A2D1-4E81-BA7C-97EAE392D683}.Debug|arm64.ActiveCfg = Debug|Any CPU
{D8F7091E-A2D1-4E81-BA7C-97EAE392D683}.Debug|arm64.Build.0 = Debug|Any CPU
{D8F7091E-A2D1-4E81-BA7C-97EAE392D683}.Debug|x64.ActiveCfg = Debug|Any CPU
{D8F7091E-A2D1-4E81-BA7C-97EAE392D683}.Debug|x64.Build.0 = Debug|Any CPU
{D8F7091E-A2D1-4E81-BA7C-97EAE392D683}.Debug|x86.ActiveCfg = Debug|Any CPU
{D8F7091E-A2D1-4E81-BA7C-97EAE392D683}.Debug|x86.Build.0 = Debug|Any CPU
{D8F7091E-A2D1-4E81-BA7C-97EAE392D683}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D8F7091E-A2D1-4E81-BA7C-97EAE392D683}.Release|Any CPU.Build.0 = Release|Any CPU
{D8F7091E-A2D1-4E81-BA7C-97EAE392D683}.Release|arm64.ActiveCfg = Release|Any CPU
{D8F7091E-A2D1-4E81-BA7C-97EAE392D683}.Release|arm64.Build.0 = Release|Any CPU
{D8F7091E-A2D1-4E81-BA7C-97EAE392D683}.Release|x64.ActiveCfg = Release|Any CPU
{D8F7091E-A2D1-4E81-BA7C-97EAE392D683}.Release|x64.Build.0 = Release|Any CPU
{D8F7091E-A2D1-4E81-BA7C-97EAE392D683}.Release|x86.ActiveCfg = Release|Any CPU
{D8F7091E-A2D1-4E81-BA7C-97EAE392D683}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -11836,6 +11872,8 @@ Global
{274100A5-5B2D-4EA2-AC42-A62257FC6BDC} = {017429CC-C5FB-48B4-9C46-034E29EE2F06}
{4D8DE54A-4F32-4881-B07B-DDC79619E573} = {274100A5-5B2D-4EA2-AC42-A62257FC6BDC}
{9536C284-65B4-4884-BB50-06D629095C3E} = {274100A5-5B2D-4EA2-AC42-A62257FC6BDC}
{6A19D94D-2BC6-4198-BE2E-342688FDBA4B} = {A1B75FC7-A777-4412-A635-D0C9ED8FE7A0}
{D8F7091E-A2D1-4E81-BA7C-97EAE392D683} = {A1B75FC7-A777-4412-A635-D0C9ED8FE7A0}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {3E8720B3-DBDD-498C-B383-2CC32A054E8F}
Expand Down
3 changes: 3 additions & 0 deletions src/OpenApi/src/Services/IDocumentProvider.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.OpenApi;

namespace Microsoft.Extensions.ApiDescriptions;

/// <summary>
Expand All @@ -20,4 +22,5 @@ internal interface IDocumentProvider
{
IEnumerable<string> GetDocumentNames();
Task GenerateAsync(string documentName, TextWriter writer);
Task GenerateAsync(string documentName, TextWriter writer, OpenApiSpecVersion openApiSpecVersion);
}
26 changes: 20 additions & 6 deletions src/OpenApi/src/Services/OpenApiDocumentProvider.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.OpenApi.Writers;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.OpenApi;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using System.Linq;
using Microsoft.OpenApi;
using Microsoft.OpenApi.Extensions;
using Microsoft.OpenApi.Writers;
using System.Linq;

namespace Microsoft.Extensions.ApiDescriptions;

Expand All @@ -19,16 +20,29 @@ internal sealed class OpenApiDocumentProvider(IServiceProvider serviceProvider)
/// <param name="documentName">The name of the document to resolve.</param>
/// <param name="writer">A text writer associated with the document to write to.</param>
public async Task GenerateAsync(string documentName, TextWriter writer)
{
var optionsSnapshot = serviceProvider.GetRequiredService<IOptionsSnapshot<OpenApiOptions>>();
var namedOption = optionsSnapshot.Get(documentName);
var resolvedOpenApiVersion = namedOption.OpenApiVersion;
await GenerateAsync(documentName, writer, resolvedOpenApiVersion);
}

/// <summary>
/// Serializes the OpenAPI document associated with a given document name to
/// the provided writer under the provided OpenAPI spec version.
/// </summary>
/// <param name="documentName">The name of the document to resolve.</param>
/// <param name="writer">A text writer associated with the document to write to.</param>
/// <param name="openApiSpecVersion">The OpenAPI specification version to use when serializing the document.</param>
public async Task GenerateAsync(string documentName, TextWriter writer, OpenApiSpecVersion openApiSpecVersion)
{
// Microsoft.OpenAPI does not provide async APIs for writing the JSON
// document to a file. See https://github.com/microsoft/OpenAPI.NET/issues/421 for
// more info.
var targetDocumentService = serviceProvider.GetRequiredKeyedService<OpenApiDocumentService>(documentName);
var options = serviceProvider.GetRequiredService<IOptionsSnapshot<OpenApiOptions>>();
var namedOption = options.Get(documentName);
var document = await targetDocumentService.GetOpenApiDocumentAsync();
var jsonWriter = new OpenApiJsonWriter(writer);
document.Serialize(jsonWriter, namedOption.OpenApiVersion);
document.Serialize(jsonWriter, openApiSpecVersion);
}

/// <summary>
Expand Down
17 changes: 17 additions & 0 deletions src/Tools/GetDocumentInsider/sample/GetDocumentSample.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<AssemblyName>GetDocumentSample</AssemblyName>
</PropertyGroup>

<ItemGroup>
<Reference Include="Microsoft.AspNetCore" />
<Reference Include="Microsoft.AspNetCore.Hosting" />
<Reference Include="Microsoft.AspNetCore.OpenApi" />
<Reference Include="Microsoft.AspNetCore.Http" />
</ItemGroup>

</Project>
25 changes: 25 additions & 0 deletions src/Tools/GetDocumentInsider/sample/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace GetDocumentSample;

public class Program
{
private static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddOpenApi();
builder.Services.AddOpenApi("internal");

var app = builder.Build();

app.MapOpenApi();

app.MapGet("/hello/{name}", (string name) => $"Hello {name}!");
app.MapGet("/bye/{name}", (string name) => $"Bye {name}!")
.WithGroupName("internal");

app.Run();
}
}
38 changes: 38 additions & 0 deletions src/Tools/GetDocumentInsider/sample/Properties/launchSettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"$schema": "https://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:29400",
"sslPort": 44315
}
},
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "http://localhost:5148",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "https://localhost:7067;http://localhost:5148",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
9 changes: 9 additions & 0 deletions src/Tools/GetDocumentInsider/sample/appsettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
14 changes: 13 additions & 1 deletion src/Tools/GetDocumentInsider/src/Commands/GetDocumentCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ internal sealed class GetDocumentCommand : ProjectCommandBase
{
private CommandOption _fileListPath;
private CommandOption _output;
private CommandOption _openApiVersion;
private CommandOption _documentName;

public GetDocumentCommand(IConsole console) : base(console)
{
Expand All @@ -28,6 +30,8 @@ public override void Configure(CommandLineApplication command)

_fileListPath = command.Option("--file-list <Path>", Resources.FileListDescription);
_output = command.Option("--output <Directory>", Resources.OutputDescription);
_openApiVersion = command.Option("--openapi-version <Version>", Resources.OpenApiVersionDescription);
_documentName = command.Option("--document-name <Name>", Resources.DocumentNameDescription);
}

protected override void Validate()
Expand All @@ -43,6 +47,12 @@ protected override void Validate()
{
throw new CommandException(Resources.FormatMissingOption(_output.LongName));
}

// No need to validate --openapi-version, we'll fallback to whatever is configured by
// the runtime in the event that none is provided.

// No need to validate --document-name, we'll fallback to generating OpenAPI files for
// documents registered in the application in the event that none is provided.
}

protected override int Execute()
Expand All @@ -52,7 +62,7 @@ protected override int Execute()
var toolsDirectory = ToolsDirectory.Value();
var packagedAssemblies = Directory
.EnumerateFiles(toolsDirectory, "*.dll")
.Except(new[] { Path.GetFullPath(thisAssembly.Location) })
.Except([Path.GetFullPath(thisAssembly.Location)])
.ToDictionary(Path.GetFileNameWithoutExtension, path => new AssemblyInfo(path));

// Explicitly load all assemblies we need first to preserve target project as much as possible. This
Expand Down Expand Up @@ -128,6 +138,8 @@ protected override int Execute()
AssemblyName = Path.GetFileNameWithoutExtension(assemblyPath),
FileListPath = _fileListPath.Value(),
OutputDirectory = _output.Value(),
OpenApiVersion = _openApiVersion.Value(),
DocumentName = _documentName.Value(),
ProjectName = ProjectName.Value(),
Reporter = Reporter,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,16 @@ public class GetDocumentCommandContext

public string ProjectName { get; set; }

/// <summary>
/// The version of the OpenAPI document to generate.
/// Maps to <see cref="OpenApi.OpenApiSpecVersion" />.
/// Can be null, in which case <see cref="OpenApiSpecVersion.OpenApi3_0"/> is used.
/// </summary>
public string OpenApiVersion { get; set; }

// The name of the OpenAPI document to generate.
// Generates all documents if not provided.
public string DocumentName { get; set; }

public IReporter Reporter { get; set; }
}
Loading

0 comments on commit da02847

Please sign in to comment.