Skip to content

Commit

Permalink
Refine service reference warnings and errors
Browse files Browse the repository at this point in the history
- #12792
- client:
  - add error if OpenAPI file does not exist
  - separate unsupported TFM case from targets being called when disabled
- server:
  - generalize multi-targeting handling in Microsoft.Extensions.ApiDescription.Server
    - add `$(_OpenApiGenerateDocumentsTFM)`, the TFM used when invoking inner build
    - default `$(OpenApiGenerateDocuments)` to 'true' only when a supported TFM exists
  - add separate error for non-existent cache file in `OpenApiGetDocuments` target
  - add error in `GetDocumentInsider` if no documents are found
    - make `<Warning />` in the targets file an `<Error />`
    - add more text to existing `ServiceNotFound` error
- both:
  - write errors to `stderr`
  - clean up top-level output for `Exception`s
    - stop writing the `Message` twice

* Correct "Open API" mentions; should be "OpenAPI"
  - change package tags and comments

* Add service reference projects to MvcNoDeps.slnf

nits:
- add "service reference" tag in Microsoft.Extensions.ApiDescription.* packages
- add "document generation" tag in Microsoft.Extensions.ApiDescription.Server package
- `.Trim()` TFM properties because `<TargetFrameworks>;netcoreapp3.0;;</TargetFrameworks>` is allowed
- don't use bold black for verbose messages
- reorder MSBuild property settings for readability
  • Loading branch information
dougbu committed Aug 16, 2019
1 parent aafb081 commit c0d5248
Show file tree
Hide file tree
Showing 13 changed files with 151 additions and 122 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<IncludeBuildOutput>false</IncludeBuildOutput>
<NuspecFile>$(MSBuildProjectName).nuspec</NuspecFile>
<PackageId>$(MSBuildProjectName)</PackageId>
<PackageTags>Build Tasks;MSBuild;Swagger;Open API;code generation; Web API client</PackageTags>
<PackageTags>Build Tasks;MSBuild;Swagger;OpenAPI;code generation;Web API client;service reference</PackageTags>
<IsShippingPackage>true</IsShippingPackage>
<TargetFrameworks>netstandard2.0;net461</TargetFrameworks>
<DevelopmentDependency>true</DevelopmentDependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,15 @@

<!--
If 'true' (the default), build projects referenced in @(OpenApiProjectReference) items before retrieving that
project's Open API documents list (or generating code). If 'false', ensure the referenced projects build before
project's OpenAPI documents list (or generating code). If 'false', ensure the referenced projects build before
this one in the solution or through other means. IDEs may be confused about the project dependency graph in this
case.
-->
<OpenApiBuildReferencedProjects
Condition="'$(OpenApiBuildReferencedProjects)' == ''">true</OpenApiBuildReferencedProjects>

<!--
Default folder to place code generated from Open API documents. Value is interpreted relative to the project
Default folder to place code generated from OpenAPI documents. Value is interpreted relative to the project
folder, unless already an absolute path. Part of the default %(OutputPath) metadata of @(OpenApiReference) and
@(OpenApiProjectReference) items.
-->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@
Condition="$(OpenApiGenerateCodeAtDesignTime) OR ('$(DesignTimeBuild)' != 'true' AND '$(BuildingProject)' == 'true')"
Inputs="@(OpenApiReference)"
Outputs="%(OutputPath)">
<Error Condition="!Exists('%(OpenApiReference.FullPath)')" Text="Input OpenAPI file %(Identity) does not exist." />

<MSBuild Projects="$(MSBuildProjectFullPath)"
BuildInParallel="$(BuildInParallel)"
Properties="GeneratorTargetPath=%(OpenApiReference.OutputPath);GeneratorTarget=Generate%(CodeGenerator);GeneratorMetadata=%(SerializedMetadata)"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<HasReferenceAssembly>false</HasReferenceAssembly>
<NuspecFile>$(MSBuildProjectName).nuspec</NuspecFile>
<PackageId>$(MSBuildProjectName)</PackageId>
<PackageTags>MSBuild;Swagger;Open API;code generation;Web API</PackageTags>
<PackageTags>MSBuild;Swagger;OpenAPI;code generation;Web API;service reference;document generation</PackageTags>
<IsShippingPackage>true</IsShippingPackage>
<DevelopmentDependency>true</DevelopmentDependency>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,27 @@
-->
<PropertyGroup>
<!--
Options added to the Open API document generation tool ('dotnet-getdocument') command line. Available options
Options added to the OpenAPI document generation tool ('dotnet-getdocument') command line. Available options
control console output: 'no-color', 'prefix-output' and 'verbose'. All require a double-dash prefix.
-->
<OpenApiGenerateDocumentsOptions Condition=" '$(OpenApiGenerateDocumentsOptions)' == '' " />

<!--
If 'true' (the default when targeting .NET Framework or .NET Core 2.1 and later), enable generation of Open API
If 'true' (the default when targeting .NET Framework or .NET Core 2.1 and later), enable generation of OpenAPI
documents. Otherwise, this feature is completely disabled. This controls whether the 'OpenApiGenerateDocuments'
project capability is visible, enables / disables the 'GenerateOpenApiDocuments' target and provides the
$(OpenApiGenerateDocumentsOnBuild) default.
-->
<OpenApiGenerateDocuments Condition=" '$(OpenApiGenerateDocuments)' == '' " />

<!--
If 'true' (the default if $(OpenApiGenerateDocuments) is 'true'), will generate Open API documents after every
If 'true' (the default if $(OpenApiGenerateDocuments) is 'true'), will generate OpenAPI documents after every
build. Set to 'false' when targets are invoked from the command line or tied to another target.
-->
<OpenApiGenerateDocumentsOnBuild Condition=" '$(OpenApiGenerateDocumentsOnBuild)' == '' " />

<!--
Where to place Open API documents generated from the application. Value is interpreted relative to the project
Where to place OpenAPI documents generated from the application. Value is interpreted relative to the project
folder, unless already an absolute path.
-->
<OpenApiDocumentsDirectory
Expand Down
Original file line number Diff line number Diff line change
@@ -1,38 +1,50 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<Project>
<PropertyGroup Condition=" '$(OpenApiGenerateDocuments)' == '' ">
<OpenApiGenerateDocuments>true</OpenApiGenerateDocuments>
<OpenApiGenerateDocuments
Condition=" '$(TargetFrameworkIdentifier)' == '.NETCoreApp' AND '$(TargetFrameworkVersion.TrimStart(&quot;vV&quot;))' &lt; '2.1' ">false</OpenApiGenerateDocuments>
<OpenApiGenerateDocuments Condition=" '$(OpenApiGenerateDocuments)' == '' ">true</OpenApiGenerateDocuments>
Condition=" '$(TargetFrameworkIdentifier)' == '.NETCoreApp' AND '$(TargetFrameworkVersion.TrimStart(&quot;vV&quot;))' &lt; '2.1' " />
</PropertyGroup>
<PropertyGroup Condition=" '$(OpenApiGenerateDocumentsOnBuild)' == '' ">
<OpenApiGenerateDocumentsOnBuild>$(OpenApiGenerateDocuments)</OpenApiGenerateDocumentsOnBuild>
</PropertyGroup>
<PropertyGroup>
<_OpenApiDocumentsCache>$(BaseIntermediateOutputPath)$(MSBuildProjectName).OpenApiFiles.cache</_OpenApiDocumentsCache>
<OpenApiGenerateDocumentsOnBuild
Condition=" '$(OpenApiGenerateDocumentsOnBuild)' == '' ">$(OpenApiGenerateDocuments)</OpenApiGenerateDocumentsOnBuild>
</PropertyGroup>

<ItemGroup Condition=" '$(OpenApiGenerateDocuments)' == 'true' ">
<ProjectCapability Include="OpenApiGenerateDocuments" />
</ItemGroup>

<Target Name="OpenApiGetDocuments" Returns="@(_OpenApiProjectDocuments)">
<Error Text="OpenAPI document generation is disabled. Add '&lt;OpenApiGenerateDocuments>true&lt;/OpenApiGenerateDocuments>' to the project."
Condition=" '$(OpenApiGenerateDocuments)' != 'true' " />
<!-- E.g. a client project has @(OpenApiProjectReference) item for project that does not support doc generation. -->
<Error Condition=" '$(OpenApiGenerateDocuments)' != 'true' "
Text="OpenAPI document generation is disabled. Add '&lt;OpenApiGenerateDocuments>true&lt;/OpenApiGenerateDocuments>' to the project." />
<!-- E.g. project set $(OpenApiGenerateDocuments) to 'true' but TFM is not supported. -->
<Error
Condition=" '$(TargetFrameworkIdentifier)' == '.NETCoreApp' AND '$(TargetFrameworkVersion.TrimStart(&quot;vV&quot;))' &lt; '2.1' "
Text="OpenAPI document generation is not supported when targeting netcoreapp2.0 or earlier. Disable the feature or move to a later target framework." />
<!-- E.g. project set $(OpenApiGenerateDocumentsOnBuild) to 'false' but did not invoke GenerateOpenApiDocuments. -->
<Error Condition=" !Exists('$(_OpenApiDocumentsCache)' )"
Text="$(_OpenApiDocumentsCache) file does not exist. Add '&lt;OpenApiGenerateDocumentsOnBuild>true&lt;/OpenApiGenerateDocumentsOnBuild>' to the project or call the GenerateOpenApiDocuments target." />

<ReadLinesFromFile File="$(_OpenApiDocumentsCache)">
<Output TaskParameter="Lines" ItemName="_OpenApiProjectDocuments" />
</ReadLinesFromFile>

<Warning Text="Application does not have any registered documents. Update its 'Startup' class to register a document."
Condition=" '@(_OpenApiProjectDocuments)' == '' " />
<!-- Fallback error in case something slips through the insider's error checking. -->
<Error Condition=" '@(_OpenApiProjectDocuments)' == '' "
Text="Application does not have any registered documents. Update its 'Startup' class to register a document." />
</Target>

<Target Name="GenerateOpenApiDocuments"
Condition=" '$(OpenApiGenerateDocuments)' == 'true' "
Inputs="$(TargetPath)"
Outputs="$(_OpenApiDocumentsCache)">
<Error Text="OpenAPI document generation is not supported when targeting netcoreapp2.0 or earlier. Disable the feature or move to a later target framework."
Condition=" '$(TargetFrameworkIdentifier)' == '.NETCoreApp' AND '$(TargetFrameworkVersion.TrimStart(&quot;vV&quot;))' &lt; '2.1' " />
<Target Name="GenerateOpenApiDocuments" Inputs="$(TargetPath)" Outputs="$(_OpenApiDocumentsCache)">
<!-- E.g. project sets $(OpenApiGenerateDocumentsOnBuild) to 'true' but $(OpenApiGenerateDocuments) is 'false'. -->
<Error Condition=" '$(OpenApiGenerateDocuments)' != 'true' "
Text="OpenAPI document generation is disabled. Add '&lt;OpenApiGenerateDocuments>true&lt;/OpenApiGenerateDocuments>' to the project." />
<!-- E.g. project sets $(OpenApiGenerateDocuments) to 'true' but TFM is not supported. -->
<Error
Condition=" '$(TargetFrameworkIdentifier)' == '.NETCoreApp' AND '$(TargetFrameworkVersion.TrimStart(&quot;vV&quot;))' &lt; '2.1' "
Text="OpenAPI document generation is not supported when targeting netcoreapp2.0 or earlier. Disable the feature or move to a later target framework." />

<PropertyGroup>
<_Command>dotnet "$(MSBuildThisFileDirectory)/../tools/dotnet-getdocument.dll" --assembly "$(TargetPath)"</_Command>
Expand All @@ -47,7 +59,7 @@

<Message Importance="high" Text="%0AGenerateOpenApiDocuments:" />
<Message Importance="high" Text=" $(_Command)" />
<Exec Command="$(_Command)" />
<Exec Command="$(_Command)" LogStandardErrorAsError="true" />
</Target>

<!-- Unless this is an inner build or default timing is disabled, tie document retrieval into the build. -->
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,25 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<Project>
<ItemGroup>
<_OpenApiGenerateDocumentsTFMs Remove="@(_OpenApiGenerateDocumentsTFMs)" />
<_OpenApiGenerateDocumentsTFMs Include="$(TargetFrameworks)" Exclude="netcoreapp1.0;netcoreapp1.1;netcoreapp2.0" />
</ItemGroup>

<PropertyGroup>
<OpenApiGenerateDocuments Condition=" '$(OpenApiGenerateDocuments)' == '' ">true</OpenApiGenerateDocuments>
<!-- Default value may lead to an inner build error if $(OpenApiGenerateDocuments) is explicitly set to 'true'. -->
<_OpenApiGenerateDocumentsTFM>$(TargetFrameworks.Trim(';').Split(';')[0])</_OpenApiGenerateDocumentsTFM>

<!-- Prefer first TFM of those the tool supports. -->
<_Temporary>$(@(_OpenApiGenerateDocumentsTFMs).Trim(';'))</_Temporary>
<_OpenApiGenerateDocumentsTFM
Condition=" '$(_Temporary)' != '' ">$(_Temporary.Split(';')[0])</_OpenApiGenerateDocumentsTFM>

<OpenApiGenerateDocuments
Condition=" '$(OpenApiGenerateDocuments)' == '' AND '$(_Temporary)' != '' ">true</OpenApiGenerateDocuments>
<OpenApiGenerateDocumentsOnBuild
Condition=" '$(OpenApiGenerateDocumentsOnBuild)' == '' ">$(OpenApiGenerateDocuments)</OpenApiGenerateDocumentsOnBuild>

<_Temporary />
</PropertyGroup>

<ItemGroup Condition=" '$(OpenApiGenerateDocuments)' == 'true' ">
Expand All @@ -13,7 +29,7 @@
<Target Name="GenerateOpenApiDocuments">
<MSBuild Projects="$(MSBuildProjectFile)"
Targets="GenerateOpenApiDocuments"
Properties="TargetFramework=$(TargetFrameworks.Split(';')[0])"
Properties="TargetFramework=$(_OpenApiGenerateDocumentsTFM)"
RemoveProperties="RuntimeIdentifier" />
</Target>

Expand All @@ -23,27 +39,11 @@
DependsOnTargets="GenerateOpenApiDocuments" />

<Target Name="OpenApiGetDocuments" Returns="@(_OpenApiProjectDocuments)">
<ItemGroup>
<_Temporary Remove="@(_Temporary)" />
<_Temporary Include="$(TargetFrameworks)" Exclude="netcoreapp1.0;netcoreapp1.1;netcoreapp2.0" />
</ItemGroup>
<PropertyGroup>
<_Temporary>@(_Temporary)</_Temporary>
</PropertyGroup>

<MSBuild Projects="$(MSBuildProjectFile)"
Targets="OpenApiGetDocuments"
Condition=" '$(_Temporary)' != '' "
Properties="TargetFramework=$(_Temporary.Split(';')[0])"
Properties="TargetFramework=$(_OpenApiGenerateDocumentsTFM)"
RemoveProperties="RuntimeIdentifier">
<Output TaskParameter="TargetOutputs" ItemName="_OpenApiProjectDocuments" />
</MSBuild>

<ItemGroup>
<_Temporary Remove="@(_Temporary)" />
</ItemGroup>
<PropertyGroup>
<_Temporary />
</PropertyGroup>
</Target>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ private static bool GetDocuments(GetDocumentCommandContext context, IServiceProv
}

// Write out the documents.
var found = false;
Directory.CreateDirectory(context.OutputDirectory);
var filePathList = new List<string>();
foreach (var documentName in documentNames)
Expand All @@ -153,14 +154,20 @@ private static bool GetDocuments(GetDocumentCommandContext context, IServiceProv
}

filePathList.Add(filePath);
found = true;
}

// Write out the cache file.
var stream = File.Create(context.FileListPath);
using var writer = new StreamWriter(stream);
writer.WriteLine(string.Join(Environment.NewLine, filePathList));

return true;
if (!found)
{
Reporter.WriteError(Resources.DocumentsNotFound);
}

return found;
}

private static string GetDocument(
Expand Down
6 changes: 2 additions & 4 deletions src/Mvc/GetDocumentInsider/src/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,13 @@ private static int Main(string[] args)
{
if (ex is CommandException || ex is CommandParsingException)
{
Reporter.WriteVerbose(ex.ToString());
Reporter.WriteError(ex.Message);
}
else
{
Reporter.WriteInformation(ex.ToString());
Reporter.WriteError(ex.ToString());
}

Reporter.WriteError(ex.Message);

return 1;
}
}
Expand Down
13 changes: 8 additions & 5 deletions src/Mvc/GetDocumentInsider/src/Reporter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ namespace Microsoft.Extensions.ApiDescription.Tool
{
internal static class Reporter
{
private static AnsiTextWriter Error = new AnsiTextWriter(Console.Error);
private static AnsiTextWriter Out = new AnsiTextWriter(Console.Out);

public static bool IsVerbose { get; set; }
public static bool NoColor { get; set; }
public static bool PrefixOutput { get; set; }
Expand All @@ -17,7 +20,7 @@ public static string Colorize(string value, Func<string, string> colorizeFunc)
=> NoColor ? value : colorizeFunc(value);

public static void WriteError(string message)
=> WriteLine(Prefix("error: ", Colorize(message, x => Bold + Red + x + Reset)));
=> WriteLine(Prefix("error: ", Colorize(message, x => Bold + Red + x + Reset)), isError: true);

public static void WriteWarning(string message)
=> WriteLine(Prefix("warn: ", Colorize(message, x => Bold + Yellow + x + Reset)));
Expand All @@ -32,7 +35,7 @@ public static void WriteVerbose(string message)
{
if (IsVerbose)
{
WriteLine(Prefix("verbose: ", Colorize(message, x => Bold + Black + x + Reset)));
WriteLine(Prefix("verbose: ", Colorize(message, x => Gray + x + Reset)));
}
}

Expand All @@ -43,15 +46,15 @@ private static string Prefix(string prefix, string value)
value.Split(new[] { Environment.NewLine }, StringSplitOptions.None).Select(l => prefix + l))
: value;

private static void WriteLine(string value)
private static void WriteLine(string value, bool isError = false)
{
if (NoColor)
{
Console.WriteLine(value);
(isError ? Console.Error : Console.Out).WriteLine(value);
}
else
{
AnsiConsole.WriteLine(value);
(isError ? Error : Out).WriteLine(value);
}
}
}
Expand Down
7 changes: 6 additions & 1 deletion src/Mvc/GetDocumentInsider/src/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,8 @@
<value>Method '{0}' not found in type '{1}' with expected signature.</value>
</data>
<data name="ServiceNotFound" xml:space="preserve">
<value>Unable to find service type '{0}' in dependency injection container.</value>
<value>Unable to find service type '{0}' in dependency injection container. Update the 'Startup' class to register a document.</value>
<comment>Do not translate 'Startup'</comment>
</data>
<data name="MethodReturnedNull" xml:space="preserve">
<value>Method '{0}' of type '{1}' returned null. Must return a non-null '{2}'.</value>
Expand Down Expand Up @@ -186,4 +187,8 @@
<data name="ServiceProviderNotFound" xml:space="preserve">
<value>Unable to resolve a non-null '{0}' implementation using method '{1}', '{2}' or '{3}' of type '{4}'.</value>
</data>
<data name="DocumentsNotFound" xml:space="preserve">
<value>Unable to find any registered documents. Update the 'Startup' class to register a document.</value>
<comment>Do not translate 'Startup'</comment>
</data>
</root>
Loading

0 comments on commit c0d5248

Please sign in to comment.