Merge pull request #8656 from dotnet-maestro-bot/merge/release/2.2-to-master

[automated] Merge branch 'release/2.2' => 'master'
This commit is contained in:
Doug Bunting 2018-10-27 16:53:19 -07:00 committed by GitHub
commit 0cea1b2a5f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 182 additions and 30 deletions

View File

@ -15,7 +15,7 @@ namespace Microsoft.Extensions.ApiDescription.Tool.Commands
internal class GetDocumentCommand : ProjectCommandBase internal class GetDocumentCommand : ProjectCommandBase
{ {
internal const string FallbackDocumentName = "v1"; internal const string FallbackDocumentName = "v1";
internal const string FallbackMethod = "Generate"; internal const string FallbackMethod = "GenerateAsync";
internal const string FallbackService = "Microsoft.Extensions.ApiDescription.IDocumentProvider"; internal const string FallbackService = "Microsoft.Extensions.ApiDescription.IDocumentProvider";
private CommandOption _documentName; private CommandOption _documentName;
@ -139,7 +139,7 @@ namespace Microsoft.Extensions.ApiDescription.Tool.Commands
AssemblyName = Path.GetFileNameWithoutExtension(assemblyPath), AssemblyName = Path.GetFileNameWithoutExtension(assemblyPath),
DocumentName = _documentName.Value(), DocumentName = _documentName.Value(),
Method = _method.Value(), Method = _method.Value(),
Output = _output.Value(), OutputPath = _output.Value(),
Service = _service.Value(), Service = _service.Value(),
}; };

View File

@ -18,7 +18,7 @@ namespace Microsoft.Extensions.ApiDescription.Tool.Commands
public string Method { get; set; } public string Method { get; set; }
public string Output { get; set; } public string OutputPath { get; set; }
public string Service { get; set; } public string Service { get; set; }
} }

View File

@ -4,8 +4,8 @@
using System; using System;
using System.IO; using System.IO;
using System.Reflection; using System.Reflection;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.Extensions.ApiDescription.Tool.Commands namespace Microsoft.Extensions.ApiDescription.Tool.Commands
{ {
@ -56,41 +56,91 @@ namespace Microsoft.Extensions.ApiDescription.Tool.Commands
try try
{ {
var serviceType = Type.GetType(serviceName, throwOnError: true); Type serviceType = null;
var method = serviceType.GetMethod(methodName, new[] { typeof(TextWriter), typeof(string) }); foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
var service = services.GetRequiredService(serviceType);
var success = true;
using (var writer = File.CreateText(context.Output))
{ {
if (method.ReturnType == typeof(bool)) serviceType = assembly.GetType(serviceName, throwOnError: false);
if (serviceType != null)
{ {
success = (bool)method.Invoke(service, new object[] { writer, documentName }); break;
}
else
{
method.Invoke(service, new object[] { writer, documentName });
} }
} }
if (!success) // As part of the aspnet/Mvc#8425 fix, make all warnings in this method errors unless the file already
// exists.
if (serviceType == null)
{ {
// As part of the aspnet/Mvc#8425 fix, make this an error unless the file already exists. Reporter.WriteWarning(Resources.FormatServiceTypeNotFound(serviceName));
var message = Resources.FormatMethodInvocationFailed(methodName, serviceName, documentName); return false;
Reporter.WriteWarning(message);
} }
return success; var method = serviceType.GetMethod(methodName, new[] { typeof(string), typeof(TextWriter) });
if (method == null)
{
Reporter.WriteWarning(Resources.FormatMethodNotFound(methodName, serviceName));
return false;
}
else if (!typeof(Task).IsAssignableFrom(method.ReturnType))
{
Reporter.WriteWarning(Resources.FormatMethodReturnTypeUnsupported(
methodName,
serviceName,
method.ReturnType,
typeof(Task)));
return false;
}
var service = services.GetService(serviceType);
if (service == null)
{
Reporter.WriteWarning(Resources.FormatServiceNotFound(serviceName));
return false;
}
// Create the output FileStream last to avoid corrupting an existing file or writing partial data.
var stream = new MemoryStream();
using (var writer = new StreamWriter(stream))
{
var resultTask = (Task)method.Invoke(service, new object[] { documentName, writer });
if (resultTask == null)
{
Reporter.WriteWarning(
Resources.FormatMethodReturnedNull(methodName, serviceName, nameof(Task)));
return false;
}
var finished = Task.WhenAny(resultTask, Task.Delay(TimeSpan.FromMinutes(1)));
if (!ReferenceEquals(resultTask, finished))
{
Reporter.WriteWarning(Resources.FormatMethodTimedOut(methodName, serviceName, 1));
return false;
}
writer.Flush();
stream.Position = 0L;
using (var outStream = File.Create(context.OutputPath))
{
stream.CopyTo(outStream);
}
}
return true;
}
catch (AggregateException ex) when (ex.InnerException != null)
{
foreach (var innerException in ex.Flatten().InnerExceptions)
{
Reporter.WriteWarning(FormatException(innerException));
}
} }
catch (Exception ex) catch (Exception ex)
{ {
var message = FormatException(ex); Reporter.WriteWarning(FormatException(ex));
// As part of the aspnet/Mvc#8425 fix, make this an error unless the file already exists.
Reporter.WriteWarning(message);
return false;
} }
File.Delete(context.OutputPath);
return false;
} }
// TODO: Use Microsoft.AspNetCore.Hosting.WebHostBuilderFactory.Sources once we have dev feed available. // TODO: Use Microsoft.AspNetCore.Hosting.WebHostBuilderFactory.Sources once we have dev feed available.

View File

@ -220,6 +220,90 @@ namespace Microsoft.Extensions.ApiDescription.Tool
internal static string FormatMissingEntryPoint(object p0) internal static string FormatMissingEntryPoint(object p0)
=> string.Format(CultureInfo.CurrentCulture, GetString("MissingEntryPoint"), p0); => string.Format(CultureInfo.CurrentCulture, GetString("MissingEntryPoint"), p0);
/// <summary>
/// Unable to find service type '{0}' in loaded assemblies.
/// </summary>
internal static string ServiceTypeNotFound
{
get => GetString("ServiceTypeNotFound");
}
/// <summary>
/// Unable to find service type '{0}' in loaded assemblies.
/// </summary>
internal static string FormatServiceTypeNotFound(object p0)
=> string.Format(CultureInfo.CurrentCulture, GetString("ServiceTypeNotFound"), p0);
/// <summary>
/// Unable to find method named '{0}' in '{1}' implementation.
/// </summary>
internal static string MethodNotFound
{
get => GetString("MethodNotFound");
}
/// <summary>
/// Unable to find method named '{0}' in '{1}' implementation.
/// </summary>
internal static string FormatMethodNotFound(object p0, object p1)
=> string.Format(CultureInfo.CurrentCulture, GetString("MethodNotFound"), p0, p1);
/// <summary>
/// Unable to find service of type '{0}' in dependency injection container.
/// </summary>
internal static string ServiceNotFound
{
get => GetString("ServiceNotFound");
}
/// <summary>
/// Unable to find service of type '{0}' in dependency injection container.
/// </summary>
internal static string FormatServiceNotFound(object p0)
=> string.Format(CultureInfo.CurrentCulture, GetString("ServiceNotFound"), p0);
/// <summary>
/// Method '{0}' of service '{1}' returned null. Must return a non-null '{2}'.
/// </summary>
internal static string MethodReturnedNull
{
get => GetString("MethodReturnedNull");
}
/// <summary>
/// Method '{0}' of service '{1}' returned null. Must return a non-null '{2}'.
/// </summary>
internal static string FormatMethodReturnedNull(object p0, object p1, object p2)
=> string.Format(CultureInfo.CurrentCulture, GetString("MethodReturnedNull"), p0, p1, p2);
/// <summary>
/// Method '{0}' of service '{1}' has unsupported return type '{2}'. Must return a '{3}'.
/// </summary>
internal static string MethodReturnTypeUnsupported
{
get => GetString("MethodReturnTypeUnsupported");
}
/// <summary>
/// Method '{0}' of service '{1}' has unsupported return type '{2}'. Must return a '{3}'.
/// </summary>
internal static string FormatMethodReturnTypeUnsupported(object p0, object p1, object p2, object p3)
=> string.Format(CultureInfo.CurrentCulture, GetString("MethodReturnTypeUnsupported"), p0, p1, p2, p3);
/// <summary>
/// Method '{0}' of service '{1}' timed out. Must complete execution within {2} minute.
/// </summary>
internal static string MethodTimedOut
{
get => GetString("MethodTimedOut");
}
/// <summary>
/// Method '{0}' of service '{1}' timed out. Must complete execution within {2} minute.
/// </summary>
internal static string FormatMethodTimedOut(object p0, object p1, object p2)
=> string.Format(CultureInfo.CurrentCulture, GetString("MethodTimedOut"), p0, p1, p2);
private static string GetString(string name, params string[] formatterNames) private static string GetString(string name, params string[] formatterNames)
{ {
var value = _resourceManager.GetString(name); var value = _resourceManager.GetString(name);

View File

@ -162,4 +162,22 @@
<data name="MissingEntryPoint" xml:space="preserve"> <data name="MissingEntryPoint" xml:space="preserve">
<value>Assembly '{0}' does not contain an entry point.</value> <value>Assembly '{0}' does not contain an entry point.</value>
</data> </data>
<data name="ServiceTypeNotFound" xml:space="preserve">
<value>Unable to find service type '{0}' in loaded assemblies.</value>
</data>
<data name="MethodNotFound" xml:space="preserve">
<value>Unable to find method named '{0}' in '{1}' implementation.</value>
</data>
<data name="ServiceNotFound" xml:space="preserve">
<value>Unable to find service of type '{0}' in dependency injection container.</value>
</data>
<data name="MethodReturnedNull" xml:space="preserve">
<value>Method '{0}' of service '{1}' returned null. Must return a non-null '{2}'.</value>
</data>
<data name="MethodReturnTypeUnsupported" xml:space="preserve">
<value>Method '{0}' of service '{1}' has unsupported return type '{2}'. Must return a '{3}'.</value>
</data>
<data name="MethodTimedOut" xml:space="preserve">
<value>Method '{0}' of service '{1}' timed out. Must complete execution within {2} minute.</value>
</data>
</root> </root>

View File

@ -97,7 +97,7 @@
<DocumentPath /> <DocumentPath />
<!-- <!--
Method Default document generator should invoke on the %(Service) to generate document. Method Default document generator should invoke on the %(Service) to generate document.
Default is set in server project, falling back to "Generate". Default is set in server project, falling back to "GenerateAsync".
--> -->
<Method /> <Method />
<!-- <!--

View File

@ -305,7 +305,7 @@ namespace Microsoft.Extensions.ApiDescription.Tool
=> GetString("OutputDescription"); => GetString("OutputDescription");
/// <summary> /// <summary>
/// Unable to retrieve '{0}' project metadata. Ensure '{1}' is set. /// Unable to retrieve '{0}' project metadata. Ensure '$({1})' is set.
/// </summary> /// </summary>
internal static string GetMetadataValueFailed internal static string GetMetadataValueFailed
{ {
@ -313,7 +313,7 @@ namespace Microsoft.Extensions.ApiDescription.Tool
} }
/// <summary> /// <summary>
/// Unable to retrieve '{0}' project metadata. Ensure '{1}' is set. /// Unable to retrieve '{0}' project metadata. Ensure '$({1})' is set.
/// </summary> /// </summary>
internal static string FormatGetMetadataValueFailed(object p0, object p1) internal static string FormatGetMetadataValueFailed(object p0, object p1)
=> string.Format(CultureInfo.CurrentCulture, GetString("GetMetadataValueFailed"), p0, p1); => string.Format(CultureInfo.CurrentCulture, GetString("GetMetadataValueFailed"), p0, p1);