diff --git a/NuGetPackageVerifier.json b/NuGetPackageVerifier.json
index b153ab1515..079b7cef51 100644
--- a/NuGetPackageVerifier.json
+++ b/NuGetPackageVerifier.json
@@ -1,7 +1,16 @@
{
- "Default": {
- "rules": [
- "DefaultCompositeRule"
- ]
+ "Default": {
+ "rules": [
+ "DefaultCompositeRule"
+ ],
+ "packages": {
+ "Microsoft.Extensions.ApiDescription.Client": {
+ "Exclusions": {
+ "BUILD_ITEMS_FRAMEWORK": {
+ "*": "Package includes tool with different target frameworks."
+ }
+ }
+ }
}
-}
\ No newline at end of file
+ }
+}
diff --git a/build/dependencies.props b/build/dependencies.props
index 304c16413f..eed739b78e 100644
--- a/build/dependencies.props
+++ b/build/dependencies.props
@@ -7,7 +7,7 @@
is not otherwise referenced. They avoid unnecessary changes to the Universe build graph or to product
dependencies. Do not use these properties elsewhere.
-->
-
+
0.9.9
0.10.13
2.1.1
@@ -32,6 +32,7 @@
2.2.0-preview3-35359
2.2.0-preview3-35359
2.2.0-preview3-35359
+ 2.0.0
2.2.0-preview3-35359
2.2.0-preview3-35359
2.2.0-preview3-35359
@@ -54,7 +55,6 @@
2.2.0-preview3-35359
2.2.0-preview3-35359
2.2.0-preview3-35359
- 2.0.0
2.2.0-preview3-35359
2.2.0-preview3-35359
5.2.6
diff --git a/src/GetDocumentInsider/Commands/GetDocumentCommand.cs b/src/GetDocumentInsider/Commands/GetDocumentCommand.cs
index 583031b324..cd45255f09 100644
--- a/src/GetDocumentInsider/Commands/GetDocumentCommand.cs
+++ b/src/GetDocumentInsider/Commands/GetDocumentCommand.cs
@@ -17,13 +17,11 @@ namespace Microsoft.Extensions.ApiDescription.Client.Commands
internal const string FallbackDocumentName = "v1";
internal const string FallbackMethod = "Generate";
internal const string FallbackService = "Microsoft.Extensions.ApiDescription.IDocumentProvider";
- private const string WorkerType = "Microsoft.Extensions.ApiDescription.Client.Commands.GetDocumentCommandWorker";
private CommandOption _documentName;
private CommandOption _method;
private CommandOption _output;
private CommandOption _service;
- private CommandOption _uri;
public override void Configure(CommandLineApplication command)
{
@@ -35,7 +33,6 @@ namespace Microsoft.Extensions.ApiDescription.Client.Commands
_method = command.Option("--method ", Resources.FormatMethodDescription(FallbackMethod));
_output = command.Option("--output ", Resources.OutputDescription);
_service = command.Option("--service ", Resources.FormatServiceDescription(FallbackService));
- _uri = command.Option("--uri ", Resources.UriDescription);
}
protected override void Validate()
@@ -131,12 +128,9 @@ namespace Microsoft.Extensions.ApiDescription.Client.Commands
#error target frameworks need to be updated.
#endif
- // Now safe to reference TestHost type.
+ // Now safe to reference the application's code.
try
{
- var workerType = thisAssembly.GetType(WorkerType, throwOnError: true);
- var methodInfo = workerType.GetMethod("Process", BindingFlags.Public | BindingFlags.Static);
-
var assemblyPath = AssemblyPath.Value();
var context = new GetDocumentCommandContext
{
@@ -147,10 +141,9 @@ namespace Microsoft.Extensions.ApiDescription.Client.Commands
Method = _method.Value(),
Output = _output.Value(),
Service = _service.Value(),
- Uri = _uri.Value(),
};
- return (int)methodInfo.Invoke(obj: null, parameters: new[] { context });
+ return GetDocumentCommandWorker.Process(context);
}
catch (Exception ex)
{
diff --git a/src/GetDocumentInsider/Commands/GetDocumentCommandContext.cs b/src/GetDocumentInsider/Commands/GetDocumentCommandContext.cs
index a109977c20..c4fc0b6e45 100644
--- a/src/GetDocumentInsider/Commands/GetDocumentCommandContext.cs
+++ b/src/GetDocumentInsider/Commands/GetDocumentCommandContext.cs
@@ -21,7 +21,5 @@ namespace Microsoft.Extensions.ApiDescription.Client.Commands
public string Output { get; set; }
public string Service { get; set; }
-
- public string Uri { get; set; }
}
}
diff --git a/src/GetDocumentInsider/Commands/GetDocumentCommandWorker.cs b/src/GetDocumentInsider/Commands/GetDocumentCommandWorker.cs
index ac90640171..b0434e453c 100644
--- a/src/GetDocumentInsider/Commands/GetDocumentCommandWorker.cs
+++ b/src/GetDocumentInsider/Commands/GetDocumentCommandWorker.cs
@@ -2,15 +2,10 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
-using System.Diagnostics;
using System.IO;
using System.Reflection;
-using System.Threading;
-using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
-using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
namespace Microsoft.Extensions.ApiDescription.Client.Commands
{
@@ -34,25 +29,10 @@ namespace Microsoft.Extensions.ApiDescription.Client.Commands
}
var success = TryProcess(context, services);
- if (!success && string.IsNullOrEmpty(context.Uri))
+ if (!success)
{
- return 4;
- }
-
- var builder = GetBuilder(entryPointType, context.AssemblyPath, context.AssemblyName);
- if (builder == null)
- {
- return 5;
- }
-
- // Mute the HttpsRedirectionMiddleware warning about HTTPS configuration.
- builder.ConfigureLogging(loggingBuilder => loggingBuilder.AddFilter(
- "Microsoft.AspNetCore.HttpsPolicy.HttpsRedirectionMiddleware",
- LogLevel.Error));
-
- using (var server = new TestServer(builder))
- {
- ProcessAsync(context, server).Wait();
+ // As part of the aspnet/Mvc#8425 fix, return 4 here.
+ return 0;
}
return 0;
@@ -95,15 +75,9 @@ namespace Microsoft.Extensions.ApiDescription.Client.Commands
if (!success)
{
+ // As part of the aspnet/Mvc#8425 fix, make this an error unless the file already exists.
var message = Resources.FormatMethodInvocationFailed(methodName, serviceName, documentName);
- if (string.IsNullOrEmpty(context.Uri) && !File.Exists(context.Output))
- {
- Reporter.WriteError(message);
- }
- else
- {
- Reporter.WriteWarning(message);
- }
+ Reporter.WriteWarning(message);
}
return success;
@@ -111,35 +85,14 @@ namespace Microsoft.Extensions.ApiDescription.Client.Commands
catch (Exception ex)
{
var message = FormatException(ex);
- if (string.IsNullOrEmpty(context.Uri) && !File.Exists(context.Output))
- {
- Reporter.WriteError(message);
- }
- else
- {
- Reporter.WriteWarning(message);
- }
+
+ // As part of the aspnet/Mvc#8425 fix, make this an error unless the file already exists.
+ Reporter.WriteWarning(message);
return false;
}
}
- public static async Task ProcessAsync(GetDocumentCommandContext context, TestServer server)
- {
-
- Debug.Assert(!string.IsNullOrEmpty(context.Uri));
- Reporter.WriteInformation(Resources.FormatUsingUri(context.Uri));
-
- var httpClient = server.CreateClient();
- await DownloadFileCore.DownloadAsync(
- context.Uri,
- context.Output,
- httpClient,
- new LogWrapper(),
- CancellationToken.None,
- timeoutSeconds: 60);
- }
-
// TODO: Use Microsoft.AspNetCore.Hosting.WebHostBuilderFactory.Sources once we have dev feed available.
private static IServiceProvider GetServices(Type entryPointType, string assemblyPath, string assemblyName)
{
@@ -203,54 +156,7 @@ namespace Microsoft.Extensions.ApiDescription.Client.Commands
}
}
- // Startup
- return new WebHostBuilder().UseStartup(assemblyName).Build().Services;
- }
-
- // TODO: Use Microsoft.AspNetCore.Hosting.WebHostBuilderFactory.Sources once we have dev feed available.
- private static IWebHostBuilder GetBuilder(Type entryPointType, string assemblyPath, string assemblyName)
- {
- var methodInfo = entryPointType.GetMethod("BuildWebHost");
- if (methodInfo != null)
- {
- // BuildWebHost cannot be used. Fall through, most likely to Startup fallback.
- Reporter.WriteWarning(
- "BuildWebHost method cannot be used. Falling back to minimal Startup configuration.");
- }
-
- methodInfo = entryPointType.GetMethod("CreateWebHostBuilder");
- if (methodInfo != null)
- {
- // CreateWebHostBuilder
- var parameters = methodInfo.GetParameters();
- if (!methodInfo.IsStatic ||
- parameters.Length != 1 ||
- typeof(string[]) != parameters[0].ParameterType ||
- typeof(IWebHostBuilder) != methodInfo.ReturnType)
- {
- Reporter.WriteError(
- "CreateWebHostBuilder method found in {assemblyPath} does not have expected signature.");
-
- return null;
- }
-
- try
- {
- var args = new[] { Array.Empty() };
- var builder = (IWebHostBuilder)methodInfo.Invoke(obj: null, parameters: args);
-
- return builder;
- }
- catch (Exception ex)
- {
- Reporter.WriteError($"CreateWebHostBuilder method threw: {FormatException(ex)}");
-
- return null;
- }
- }
-
- // Startup
- return new WebHostBuilder().UseStartup(assemblyName);
+ return null;
}
private static string FormatException(Exception exception)
diff --git a/src/GetDocumentInsider/DownloadFileCore.cs b/src/GetDocumentInsider/DownloadFileCore.cs
deleted file mode 100644
index 7bae9e0227..0000000000
--- a/src/GetDocumentInsider/DownloadFileCore.cs
+++ /dev/null
@@ -1,121 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using System;
-using System.IO;
-using System.Net.Http;
-using System.Net.Sockets;
-using System.Security.Cryptography;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace Microsoft.Extensions.ApiDescription.Client
-{
- internal static class DownloadFileCore
- {
- public static async Task DownloadAsync(
- string uri,
- string destinationPath,
- HttpClient httpClient,
- ILogWrapper log,
- CancellationToken cancellationToken,
- int timeoutSeconds)
- {
- // Timeout if the response has not begun within 1 minute
- httpClient.Timeout = TimeSpan.FromMinutes(1);
-
- var destinationExists = File.Exists(destinationPath);
- var reachedCopy = false;
- try
- {
- using (var response = await httpClient.GetAsync(uri, cancellationToken))
- {
- response.EnsureSuccessStatusCode();
- cancellationToken.ThrowIfCancellationRequested();
-
- using (var responseStreamTask = response.Content.ReadAsStreamAsync())
- {
- var finished = await Task.WhenAny(
- responseStreamTask,
- Task.Delay(TimeSpan.FromSeconds(timeoutSeconds)));
-
- if (!ReferenceEquals(responseStreamTask, finished))
- {
- throw new TimeoutException($"Download failed to complete in {timeoutSeconds} seconds.");
- }
-
- var responseStream = await responseStreamTask;
- if (destinationExists)
- {
- // Check hashes before using the downloaded information.
- var downloadHash = GetHash(responseStream);
- responseStream.Position = 0L;
-
- byte[] destinationHash;
- using (var destinationStream = File.OpenRead(destinationPath))
- {
- destinationHash = GetHash(destinationStream);
- }
-
- var sameHashes = downloadHash.LongLength == destinationHash.LongLength;
- for (var i = 0L; sameHashes && i < downloadHash.LongLength; i++)
- {
- sameHashes = downloadHash[i] == destinationHash[i];
- }
-
- if (sameHashes)
- {
- log.LogInformational($"Not overwriting existing and matching file '{destinationPath}'.");
- return;
- }
- }
- else
- {
- // May need to create directory to hold the file.
- var destinationDirectory = Path.GetDirectoryName(destinationPath);
- if (!(string.IsNullOrEmpty(destinationDirectory) || Directory.Exists(destinationDirectory)))
- {
- Directory.CreateDirectory(destinationDirectory);
- }
- }
-
- // Create or overwrite the destination file.
- reachedCopy = true;
- using (var outStream = File.Create(destinationPath))
- {
- responseStream.CopyTo(outStream);
- }
- }
- }
- }
- catch (HttpRequestException ex) when (destinationExists)
- {
- if (ex.InnerException is SocketException socketException)
- {
- log.LogWarning($"Unable to download {uri}, socket error code '{socketException.SocketErrorCode}'.");
- }
- else
- {
- log.LogWarning($"Unable to download {uri}: {ex.Message}");
- }
- }
- catch (Exception ex)
- {
- log.LogError($"Downloading '{uri}' failed.");
- log.LogError(ex, showStackTrace: true);
- if (reachedCopy)
- {
- File.Delete(destinationPath);
- }
- }
- }
-
- private static byte[] GetHash(Stream stream)
- {
- using (var algorithm = SHA256.Create())
- {
- return algorithm.ComputeHash(stream);
- }
- }
- }
-}
diff --git a/src/GetDocumentInsider/GetDocumentInsider.csproj b/src/GetDocumentInsider/GetDocumentInsider.csproj
index 93849f9cc4..b2c4fed5c6 100644
--- a/src/GetDocumentInsider/GetDocumentInsider.csproj
+++ b/src/GetDocumentInsider/GetDocumentInsider.csproj
@@ -13,6 +13,10 @@
-
+
+
+
+
+
diff --git a/src/GetDocumentInsider/ILogWrapper.cs b/src/GetDocumentInsider/ILogWrapper.cs
deleted file mode 100644
index 261e6df868..0000000000
--- a/src/GetDocumentInsider/ILogWrapper.cs
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using System;
-
-namespace Microsoft.Extensions.ApiDescription.Client
-{
- internal interface ILogWrapper
- {
- ///
- /// Logs specified informational . Implementations should be thread safe.
- ///
- /// The message to log.
- /// Optional arguments for formatting the string.
- ///
- /// Thrown when is .
- ///
- void LogInformational(string message, params object[] messageArgs);
-
- ///
- /// Logs a warning with the specified . Implementations should be thread safe.
- ///
- /// The message to log.
- /// Optional arguments for formatting the string.
- ///
- /// Thrown when is .
- ///
- void LogWarning(string message, params object[] messageArgs);
-
- ///
- /// Logs an error with the specified . Implementations should be thread safe.
- ///
- /// The message to log.
- /// Optional arguments for formatting the string.
- ///
- /// Thrown when is .
- ///
- void LogError(string message, params object[] messageArgs);
-
- ///
- /// Logs an error with the message and (optionally) the stack trace of the given .
- /// Implementations should be thread safe.
- ///
- /// The to log.
- ///
- /// If , append stack trace to 's message.
- ///
- ///
- /// Thrown when is .
- ///
- void LogError(Exception exception, bool showStackTrace);
- }
-}
diff --git a/src/GetDocumentInsider/Json.cs b/src/GetDocumentInsider/Json.cs
deleted file mode 100644
index 53885c5825..0000000000
--- a/src/GetDocumentInsider/Json.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using Microsoft.DotNet.Cli.CommandLine;
-
-namespace Microsoft.Extensions.ApiDescription.Client
-{
- internal static class Json
- {
- public static CommandOption ConfigureOption(CommandLineApplication command)
- => command.Option("--json", Resources.JsonDescription);
-
- public static string Literal(string text)
- => text != null
- ? "\"" + text.Replace("\\", "\\\\").Replace("\"", "\\\"") + "\""
- : "null";
- }
-}
diff --git a/src/GetDocumentInsider/LogWrapper.cs b/src/GetDocumentInsider/LogWrapper.cs
deleted file mode 100644
index e7c4b7b8c7..0000000000
--- a/src/GetDocumentInsider/LogWrapper.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using System;
-
-namespace Microsoft.Extensions.ApiDescription.Client
-{
- public class LogWrapper : ILogWrapper
- {
- public void LogError(string message, params object[] messageArgs)
- {
- Reporter.WriteError(string.Format(message, messageArgs));
- }
-
- public void LogError(Exception exception, bool showStackTrace)
- {
- var message = showStackTrace ? exception.ToString() : exception.Message;
- Reporter.WriteError(message);
- }
-
- public void LogInformational(string message, params object[] messageArgs)
- {
- Reporter.WriteInformation(string.Format(message, messageArgs));
- }
-
- public void LogWarning(string message, params object[] messageArgs)
- {
- Reporter.WriteWarning(string.Format(message, messageArgs));
- }
- }
-}
diff --git a/src/GetDocumentInsider/Program.cs b/src/GetDocumentInsider/Program.cs
index 2f2f43a4c0..af1bdc298a 100644
--- a/src/GetDocumentInsider/Program.cs
+++ b/src/GetDocumentInsider/Program.cs
@@ -30,10 +30,7 @@ namespace Microsoft.Extensions.ApiDescription.Client
}
catch (Exception ex)
{
- if (ex is CommandException
- || ex is CommandParsingException
- || (ex is WrappedException wrappedException
- && wrappedException.Type == "Microsoft.Extensions.ApiDescription.Client.Design.OperationException"))
+ if (ex is CommandException || ex is CommandParsingException)
{
Reporter.WriteVerbose(ex.ToString());
}
diff --git a/src/GetDocumentInsider/Properties/Resources.Designer.cs b/src/GetDocumentInsider/Properties/Resources.Designer.cs
index 460ca76235..c9576ef8c0 100644
--- a/src/GetDocumentInsider/Properties/Resources.Designer.cs
+++ b/src/GetDocumentInsider/Properties/Resources.Designer.cs
@@ -24,20 +24,6 @@ namespace Microsoft.Extensions.ApiDescription.Client
internal static string FormatAssemblyDescription()
=> GetString("AssemblyDescription");
- ///
- /// Show JSON output.
- ///
- internal static string JsonDescription
- {
- get => GetString("JsonDescription");
- }
-
- ///
- /// Show JSON output.
- ///
- internal static string FormatJsonDescription()
- => GetString("JsonDescription");
-
///
/// Missing required option '--{0}'.
///
@@ -94,48 +80,6 @@ namespace Microsoft.Extensions.ApiDescription.Client
internal static string FormatPrefixDescription()
=> GetString("PrefixDescription");
- ///
- /// Using application base '{0}'.
- ///
- internal static string UsingApplicationBase
- {
- get => GetString("UsingApplicationBase");
- }
-
- ///
- /// Using application base '{0}'.
- ///
- internal static string FormatUsingApplicationBase(object p0)
- => string.Format(CultureInfo.CurrentCulture, GetString("UsingApplicationBase"), p0);
-
- ///
- /// Using assembly '{0}'.
- ///
- internal static string UsingAssembly
- {
- get => GetString("UsingAssembly");
- }
-
- ///
- /// Using assembly '{0}'.
- ///
- internal static string FormatUsingAssembly(object p0)
- => string.Format(CultureInfo.CurrentCulture, GetString("UsingAssembly"), p0);
-
- ///
- /// Using configuration file '{0}'.
- ///
- internal static string UsingConfigurationFile
- {
- get => GetString("UsingConfigurationFile");
- }
-
- ///
- /// Using configuration file '{0}'.
- ///
- internal static string FormatUsingConfigurationFile(object p0)
- => string.Format(CultureInfo.CurrentCulture, GetString("UsingConfigurationFile"), p0);
-
///
/// Show verbose output.
///
@@ -150,34 +94,6 @@ namespace Microsoft.Extensions.ApiDescription.Client
internal static string FormatVerboseDescription()
=> GetString("VerboseDescription");
- ///
- /// Writing '{0}'...
- ///
- internal static string WritingFile
- {
- get => GetString("WritingFile");
- }
-
- ///
- /// Writing '{0}'...
- ///
- internal static string FormatWritingFile(object p0)
- => string.Format(CultureInfo.CurrentCulture, GetString("WritingFile"), p0);
-
- ///
- /// Using working directory '{0}'.
- ///
- internal static string UsingWorkingDirectory
- {
- get => GetString("UsingWorkingDirectory");
- }
-
- ///
- /// Using working directory '{0}'.
- ///
- internal static string FormatUsingWorkingDirectory(object p0)
- => string.Format(CultureInfo.CurrentCulture, GetString("UsingWorkingDirectory"), p0);
-
///
/// Location from which inside man was copied (in the .NET Framework case) or loaded.
///
@@ -192,20 +108,6 @@ namespace Microsoft.Extensions.ApiDescription.Client
internal static string FormatToolsDirectoryDescription()
=> GetString("ToolsDirectoryDescription");
- ///
- /// The URI to download the document from.
- ///
- internal static string UriDescription
- {
- get => GetString("UriDescription");
- }
-
- ///
- /// The URI to download the document from.
- ///
- internal static string FormatUriDescription()
- => GetString("UriDescription");
-
///
/// The name of the method to invoke on the '--service' instance. Default value '{0}'.
///
@@ -234,20 +136,6 @@ namespace Microsoft.Extensions.ApiDescription.Client
internal static string FormatServiceDescription(object p0)
=> string.Format(CultureInfo.CurrentCulture, GetString("ServiceDescription"), p0);
- ///
- /// Missing required option '--{0}' or '--{1}'.
- ///
- internal static string MissingOptions
- {
- get => GetString("MissingOptions");
- }
-
- ///
- /// Missing required option '--{0}' or '--{1}'.
- ///
- internal static string FormatMissingOptions(object p0, object p1)
- => string.Format(CultureInfo.CurrentCulture, GetString("MissingOptions"), p0, p1);
-
///
/// The name of the document to pass to the '--method' method. Default value '{0}'.
///
@@ -304,20 +192,6 @@ namespace Microsoft.Extensions.ApiDescription.Client
internal static string FormatUsingService(object p0)
=> string.Format(CultureInfo.CurrentCulture, GetString("UsingService"), p0);
- ///
- /// Using URI '{0}'.
- ///
- internal static string UsingUri
- {
- get => GetString("UsingUri");
- }
-
- ///
- /// Using URI '{0}'.
- ///
- internal static string FormatUsingUri(object p0)
- => string.Format(CultureInfo.CurrentCulture, GetString("UsingUri"), p0);
-
///
/// Method '{0}' of service '{1}' failed to generate document '{2}'.
///
diff --git a/src/GetDocumentInsider/Resources.resx b/src/GetDocumentInsider/Resources.resx
index f42b6144e6..fffabb44f3 100644
--- a/src/GetDocumentInsider/Resources.resx
+++ b/src/GetDocumentInsider/Resources.resx
@@ -120,9 +120,6 @@
The assembly to use.
-
- Show JSON output.
-
Missing required option '--{0}'.
@@ -135,39 +132,18 @@
Prefix console output with logging level.
-
- Using application base '{0}'.
-
-
- Using assembly '{0}'.
-
-
- Using configuration file '{0}'.
-
Show verbose output.
-
- Writing '{0}'...
-
-
- Using working directory '{0}'.
-
Location from which inside man was copied (in the .NET Framework case) or loaded.
-
- The URI to download the document from.
-
The name of the method to invoke on the '--service' instance. Default value '{0}'.
The qualified name of the service type to retrieve from dependency injection. Default value '{0}'.
-
- Missing required option '--{0}' or '--{1}'.
-
The name of the document to pass to the '--method' method. Default value '{0}'.
@@ -180,13 +156,10 @@
Using service '{0}'.
-
- Using URI '{0}'.
-
Method '{0}' of service '{1}' failed to generate document '{2}'.
Assembly '{0}' does not contain an entry point.
-
+
\ No newline at end of file
diff --git a/src/GetDocumentInsider/WrappedException.cs b/src/GetDocumentInsider/WrappedException.cs
deleted file mode 100644
index ec90fb6061..0000000000
--- a/src/GetDocumentInsider/WrappedException.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using System;
-
-namespace Microsoft.Extensions.ApiDescription.Client
-{
- internal class WrappedException : Exception
- {
- private readonly string _stackTrace;
-
- public WrappedException(string type, string message, string stackTrace)
- : base(message)
- {
- Type = type;
- _stackTrace = stackTrace;
- }
-
- public string Type { get; }
-
- public override string ToString()
- => _stackTrace;
- }
-}
diff --git a/src/Microsoft.Extensions.ApiDescription.Client/DownloadFile.cs b/src/Microsoft.Extensions.ApiDescription.Client/DownloadFile.cs
index 1e79fd9722..14c5545b2b 100644
--- a/src/Microsoft.Extensions.ApiDescription.Client/DownloadFile.cs
+++ b/src/Microsoft.Extensions.ApiDescription.Client/DownloadFile.cs
@@ -4,6 +4,9 @@
using System;
using System.IO;
using System.Net.Http;
+using System.Net.Sockets;
+using System.Reflection;
+using System.Security.Cryptography;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Build.Framework;
@@ -94,19 +97,130 @@ namespace Microsoft.Extensions.ApiDescription.Client
return;
}
- log.LogMessage($"Downloading '{uri}' to '{destinationPath}'.");
+ log.LogMessage(MessageImportance.High, $"Downloading '{uri}' to '{destinationPath}'.");
- using (var httpClient = new HttpClient
+ using (var httpClient = new HttpClient())
{
- })
+ await DownloadAsync(uri, destinationPath, httpClient, cancellationToken, log, timeoutSeconds);
+ }
+ }
+
+ public static async Task DownloadAsync(
+ string uri,
+ string destinationPath,
+ HttpClient httpClient,
+ CancellationToken cancellationToken,
+ TaskLoggingHelper log,
+ int timeoutSeconds)
+ {
+ // Timeout if the response has not begun within 1 minute
+ httpClient.Timeout = TimeSpan.FromMinutes(1);
+
+ var destinationExists = File.Exists(destinationPath);
+ var reachedCopy = false;
+ try
{
- await DownloadFileCore.DownloadAsync(
- uri,
- destinationPath,
- httpClient,
- new LogWrapper(log),
- cancellationToken,
- timeoutSeconds);
+ using (var response = await httpClient.GetAsync(uri, cancellationToken))
+ {
+ response.EnsureSuccessStatusCode();
+ cancellationToken.ThrowIfCancellationRequested();
+
+ using (var responseStreamTask = response.Content.ReadAsStreamAsync())
+ {
+ var finished = await Task.WhenAny(
+ responseStreamTask,
+ Task.Delay(TimeSpan.FromSeconds(timeoutSeconds)));
+
+ if (!ReferenceEquals(responseStreamTask, finished))
+ {
+ throw new TimeoutException($"Download failed to complete in {timeoutSeconds} seconds.");
+ }
+
+ using (var responseStream = await responseStreamTask)
+ {
+ if (destinationExists)
+ {
+ // Check hashes before using the downloaded information.
+ var downloadHash = GetHash(responseStream);
+ responseStream.Position = 0L;
+
+ byte[] destinationHash;
+ using (var destinationStream = File.OpenRead(destinationPath))
+ {
+ destinationHash = GetHash(destinationStream);
+ }
+
+ var sameHashes = downloadHash.Length == destinationHash.Length;
+ for (var i = 0; sameHashes && i < downloadHash.Length; i++)
+ {
+ sameHashes = downloadHash[i] == destinationHash[i];
+ }
+
+ if (sameHashes)
+ {
+ log.LogMessage($"Not overwriting existing and matching file '{destinationPath}'.");
+ return;
+ }
+ }
+ else
+ {
+ // May need to create directory to hold the file.
+ var destinationDirectory = Path.GetDirectoryName(destinationPath);
+ if (!string.IsNullOrEmpty(destinationDirectory))
+ {
+ Directory.CreateDirectory(destinationDirectory);
+ }
+ }
+
+ // Create or overwrite the destination file.
+ reachedCopy = true;
+ using (var outStream = File.Create(destinationPath))
+ {
+ await responseStream.CopyToAsync(outStream);
+ }
+ }
+ }
+ }
+ }
+ catch (HttpRequestException ex) when (destinationExists)
+ {
+ if (ex.InnerException is SocketException socketException)
+ {
+ log.LogWarning($"Unable to download {uri}, socket error code '{socketException.SocketErrorCode}'.");
+ }
+ else
+ {
+ log.LogWarning($"Unable to download {uri}: {ex.Message}");
+ }
+ }
+ catch (Exception ex)
+ {
+ log.LogError($"Downloading '{uri}' failed.");
+ log.LogErrorFromException(ex, showStackTrace: true);
+ if (reachedCopy)
+ {
+ File.Delete(destinationPath);
+ }
+ }
+ }
+
+ private static byte[] GetHash(Stream stream)
+ {
+ SHA256 algorithm;
+ try
+ {
+ algorithm = SHA256.Create();
+ }
+ catch (TargetInvocationException)
+ {
+ // SHA256.Create is documented to throw this exception on FIPS-compliant machines. See
+ // https://msdn.microsoft.com/en-us/library/z08hz7ad Fall back to a FIPS-compliant SHA256 algorithm.
+ algorithm = new SHA256CryptoServiceProvider();
+ }
+
+ using (algorithm)
+ {
+ return algorithm.ComputeHash(stream);
}
}
}
diff --git a/src/Microsoft.Extensions.ApiDescription.Client/LogWrapper.cs b/src/Microsoft.Extensions.ApiDescription.Client/LogWrapper.cs
deleted file mode 100644
index f058620621..0000000000
--- a/src/Microsoft.Extensions.ApiDescription.Client/LogWrapper.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using System;
-using Microsoft.Build.Utilities;
-
-namespace Microsoft.Extensions.ApiDescription.Client
-{
- internal class LogWrapper : ILogWrapper
- {
- private readonly TaskLoggingHelper _log;
-
- public LogWrapper(TaskLoggingHelper log)
- {
- _log = log;
- }
-
- public void LogError(string message, params object[] messageArgs)
- {
- _log.LogError(message, messageArgs);
- }
-
- public void LogError(Exception exception, bool showStackTrace)
- {
- _log.LogErrorFromException(exception, showStackTrace);
- }
-
- public void LogInformational(string message, params object[] messageArgs)
- {
- _log.LogMessage(message, messageArgs);
- }
-
- public void LogWarning(string message, params object[] messageArgs)
- {
- _log.LogWarning(message, messageArgs);
- }
- }
-}
diff --git a/src/Microsoft.Extensions.ApiDescription.Client/Microsoft.Extensions.ApiDescription.Client.csproj b/src/Microsoft.Extensions.ApiDescription.Client/Microsoft.Extensions.ApiDescription.Client.csproj
index daec0b3b26..946f3d1d16 100644
--- a/src/Microsoft.Extensions.ApiDescription.Client/Microsoft.Extensions.ApiDescription.Client.csproj
+++ b/src/Microsoft.Extensions.ApiDescription.Client/Microsoft.Extensions.ApiDescription.Client.csproj
@@ -8,21 +8,52 @@
MSBuild tasks and targets for code generation
false
- false
+ false
+ false
+ false
$(MSBuildProjectName).nuspec
- Build Tasks;msbuild;DownloadFile;GetFilenameFromUri;code generation
+ Build Tasks;MSBuild;Swagger;Open API;code generation; Web API client
netstandard2.0;net461
-
-
-
+
+
-
-
-
+
+
+ $(AssemblySigningCertName)
+ tasks/$(TargetFramework)/$(TargetFileName)
+ $(AssemblySigningStrongName)
+
+
+
+
+
+
+ $(AssemblySigningCertName)
+ tools/dotnet-getdocument.dll
+ $(AssemblySigningStrongName)
+
+
+ $(AssemblySigningCertName)
+ tools/net461/GetDocument.Insider.exe
+ $(AssemblySigningStrongName)
+
+
+ $(AssemblySigningCertName)
+ tools/net461-x86/GetDocument.Insider.exe
+ $(AssemblySigningStrongName)
+
+
+ $(AssemblySigningCertName)
+ tools/netcoreapp2.0/GetDocument.Insider.exe
+ $(AssemblySigningStrongName)
+
diff --git a/src/Microsoft.Extensions.ApiDescription.Client/Microsoft.Extensions.ApiDescription.Client.nuspec b/src/Microsoft.Extensions.ApiDescription.Client/Microsoft.Extensions.ApiDescription.Client.nuspec
index 5b840bd379..e35a37d51c 100644
--- a/src/Microsoft.Extensions.ApiDescription.Client/Microsoft.Extensions.ApiDescription.Client.nuspec
+++ b/src/Microsoft.Extensions.ApiDescription.Client/Microsoft.Extensions.ApiDescription.Client.nuspec
@@ -12,17 +12,19 @@
$owners$
$projectUrl$
- false
+ true
$tags$
$version$
-
-
-
-
-
-
+
+
+
+
+
+
+
+
diff --git a/src/Microsoft.Extensions.ApiDescription.Client/ServiceProjectReferenceMetadata.targets b/src/Microsoft.Extensions.ApiDescription.Client/ServiceProjectReferenceMetadata.targets
deleted file mode 100644
index 5ed88a3f6c..0000000000
--- a/src/Microsoft.Extensions.ApiDescription.Client/ServiceProjectReferenceMetadata.targets
+++ /dev/null
@@ -1,32 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/Microsoft.Extensions.ApiDescription.Client/build/Microsoft.Extensions.ApiDescription.Client.props b/src/Microsoft.Extensions.ApiDescription.Client/build/Microsoft.Extensions.ApiDescription.Client.props
index d01b8044e8..0e1260da7c 100644
--- a/src/Microsoft.Extensions.ApiDescription.Client/build/Microsoft.Extensions.ApiDescription.Client.props
+++ b/src/Microsoft.Extensions.ApiDescription.Client/build/Microsoft.Extensions.ApiDescription.Client.props
@@ -93,11 +93,6 @@
Default is set in server project, falling back to "Microsoft.Extensions.ApiDescription.IDocumentProvider".
-->
-
-
diff --git a/src/Microsoft.Extensions.ApiDescription.Client/build/Microsoft.Extensions.ApiDescription.Client.targets b/src/Microsoft.Extensions.ApiDescription.Client/build/Microsoft.Extensions.ApiDescription.Client.targets
index e7c60f9017..051922a579 100644
--- a/src/Microsoft.Extensions.ApiDescription.Client/build/Microsoft.Extensions.ApiDescription.Client.targets
+++ b/src/Microsoft.Extensions.ApiDescription.Client/build/Microsoft.Extensions.ApiDescription.Client.targets
@@ -179,7 +179,7 @@
- dotnet getdocument --no-build --project %(FullPath) --output %(DocumentPath)
+ dotnet $(MSBuildThisFileDirectory)/../tools/dotnet-getdocument.dll --project %(FullPath) --output %(DocumentPath)
$(DefaultDocumentGeneratorDefaultOptions)
%(Command) --service %(Service)
-
- %(Command) --uri %(Uri)
-
%(Command) --configuration %(ProjectConfiguration) %(DefaultDocumentGeneratorOptions)
diff --git a/src/dotnet-getdocument/Commands/InvokeCommand.cs b/src/dotnet-getdocument/Commands/InvokeCommand.cs
index b376b3b9d2..b1426c057d 100644
--- a/src/dotnet-getdocument/Commands/InvokeCommand.cs
+++ b/src/dotnet-getdocument/Commands/InvokeCommand.cs
@@ -159,12 +159,6 @@ namespace Microsoft.Extensions.ApiDescription.Client.Commands
args.Add(project.DefaultService);
}
- if (!(args.Contains("--uri") || string.IsNullOrEmpty(project.DefaultUri)))
- {
- args.Add("--uri");
- args.Add(project.DefaultUri);
- }
-
if (_output.HasValue())
{
args.Add("--output");
diff --git a/src/dotnet-getdocument/Project.cs b/src/dotnet-getdocument/Project.cs
index ce3325b7e8..f9fc0da93e 100644
--- a/src/dotnet-getdocument/Project.cs
+++ b/src/dotnet-getdocument/Project.cs
@@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
@@ -11,7 +12,8 @@ namespace Microsoft.Extensions.ApiDescription.Client
{
internal class Project
{
- private const string MSBuildResourceName = "Microsoft.Extensions.ApiDescription.Client.ServiceProjectReferenceMetadata";
+ private const string ResourceFilename = "ServiceProjectReferenceMetadata.targets";
+ private const string MSBuildResourceName = "Microsoft.Extensions.ApiDescription.Client." + ResourceFilename;
private Project()
{
@@ -33,8 +35,6 @@ namespace Microsoft.Extensions.ApiDescription.Client
public string DefaultService { get; private set; }
- public string DefaultUri { get; private set; }
-
public string DepsPath { get; private set; }
public string Directory { get; private set; }
@@ -53,41 +53,36 @@ namespace Microsoft.Extensions.ApiDescription.Client
public string RuntimeFrameworkVersion { get; private set; }
+ public string RuntimeIdentifier { get; private set; }
+
public string TargetFramework { get; private set; }
public string TargetFrameworkMoniker { get; private set; }
public static Project FromFile(
- string file,
+ string projectFile,
string buildExtensionsDirectory,
string framework = null,
string configuration = null,
string runtime = null)
{
- Debug.Assert(!string.IsNullOrEmpty(file), "file is null or empty.");
+ if (string.IsNullOrEmpty(projectFile))
+ {
+ throw new ArgumentNullException(nameof(projectFile));
+ }
if (string.IsNullOrEmpty(buildExtensionsDirectory))
{
- buildExtensionsDirectory = Path.Combine(Path.GetDirectoryName(file), "obj");
+ buildExtensionsDirectory = Path.Combine(Path.GetDirectoryName(projectFile), "obj");
}
IODirectory.CreateDirectory(buildExtensionsDirectory);
var assembly = typeof(Project).Assembly;
- var propsPath = Path.Combine(
+ var targetsPath = Path.Combine(
buildExtensionsDirectory,
- Path.GetFileName(file) + ".ServiceProjectReferenceMetadata.props");
- using (var input = assembly.GetManifestResourceStream($"{MSBuildResourceName}.props"))
- {
- using (var output = File.OpenWrite(propsPath))
- {
- Reporter.WriteVerbose(Resources.FormatWritingFile(propsPath));
- input.CopyTo(output);
- }
- }
-
- var targetsPath = Path.ChangeExtension(propsPath, ".targets");
- using (var input = assembly.GetManifestResourceStream($"{MSBuildResourceName}.targets"))
+ $"{Path.GetFileName(projectFile)}.{ResourceFilename}");
+ using (var input = assembly.GetManifestResourceStream(MSBuildResourceName))
{
using (var output = File.OpenWrite(targetsPath))
{
@@ -101,32 +96,29 @@ namespace Microsoft.Extensions.ApiDescription.Client
var metadataPath = Path.GetTempFileName();
try
{
- var propertyArg = "/property:ServiceProjectReferenceMetadataPath=" + metadataPath;
- if (!string.IsNullOrEmpty(framework))
- {
- propertyArg += ";TargetFramework=" + framework;
- }
- if (!string.IsNullOrEmpty(configuration))
- {
- propertyArg += ";Configuration=" + configuration;
- }
- if (!string.IsNullOrEmpty(runtime))
- {
- propertyArg += ";RuntimeIdentifier=" + runtime;
- }
-
var args = new List
{
"msbuild",
"/target:WriteServiceProjectReferenceMetadata",
- propertyArg,
"/verbosity:quiet",
- "/nologo"
+ "/nologo",
+ $"/property:ServiceProjectReferenceMetadataPath={metadataPath}",
+ projectFile,
};
- if (!string.IsNullOrEmpty(file))
+ if (!string.IsNullOrEmpty(framework))
{
- args.Add(file);
+ args.Add($"/property:TargetFramework={framework}");
+ }
+
+ if (!string.IsNullOrEmpty(configuration))
+ {
+ args.Add($"/property:Configuration={configuration}");
+ }
+
+ if (!string.IsNullOrEmpty(runtime))
+ {
+ args.Add($"/property:RuntimeIdentifier={runtime}");
}
var exitCode = Exe.Run("dotnet", args);
@@ -140,21 +132,20 @@ namespace Microsoft.Extensions.ApiDescription.Client
}
finally
{
- File.Delete(propsPath);
File.Delete(metadataPath);
File.Delete(targetsPath);
}
var project = new Project
{
+ DefaultDocumentName = metadata[nameof(DefaultDocumentName)],
+ DefaultMethod = metadata[nameof(DefaultMethod)],
+ DefaultService = metadata[nameof(DefaultService)],
+
AssemblyName = metadata[nameof(AssemblyName)],
AssemblyPath = metadata[nameof(AssemblyPath)],
AssetsPath = metadata[nameof(AssetsPath)],
Configuration = metadata[nameof(Configuration)],
- DefaultDocumentName = metadata[nameof(DefaultDocumentName)],
- DefaultMethod = metadata[nameof(DefaultMethod)],
- DefaultService = metadata[nameof(DefaultService)],
- DefaultUri = metadata[nameof(DefaultUri)],
DepsPath = metadata[nameof(DepsPath)],
Directory = metadata[nameof(Directory)],
ExtensionsPath = metadata[nameof(ExtensionsPath)],
@@ -164,6 +155,7 @@ namespace Microsoft.Extensions.ApiDescription.Client
PlatformTarget = metadata[nameof(PlatformTarget)] ?? metadata[nameof(Platform)],
RuntimeConfigPath = metadata[nameof(RuntimeConfigPath)],
RuntimeFrameworkVersion = metadata[nameof(RuntimeFrameworkVersion)],
+ RuntimeIdentifier = metadata[nameof(RuntimeIdentifier)],
TargetFramework = metadata[nameof(TargetFramework)],
TargetFrameworkMoniker = metadata[nameof(TargetFrameworkMoniker)],
};
@@ -193,6 +185,11 @@ namespace Microsoft.Extensions.ApiDescription.Client
project.AssemblyPath = Path.GetFullPath(Path.Combine(project.Directory, project.AssemblyPath));
}
+ if (!Path.IsPathRooted(project.ExtensionsPath))
+ {
+ project.ExtensionsPath = Path.GetFullPath(Path.Combine(project.Directory, project.ExtensionsPath));
+ }
+
if (!Path.IsPathRooted(project.OutputPath))
{
project.OutputPath = Path.GetFullPath(Path.Combine(project.Directory, project.OutputPath));
diff --git a/src/dotnet-getdocument/Properties/Resources.Designer.cs b/src/dotnet-getdocument/Properties/Resources.Designer.cs
index eb38ce67a7..8ab2cece4f 100644
--- a/src/dotnet-getdocument/Properties/Resources.Designer.cs
+++ b/src/dotnet-getdocument/Properties/Resources.Designer.cs
@@ -277,7 +277,7 @@ namespace Microsoft.Extensions.ApiDescription.Client
=> string.Format(CultureInfo.CurrentCulture, GetString("WritingFile"), p0);
///
- /// Project output not found and --no-build was specified. Project must be up-to-date when using the --no-build option.
+ /// Project output not found. Project must be up-to-date when using this tool.
///
internal static string MustBuild
{
@@ -285,7 +285,7 @@ namespace Microsoft.Extensions.ApiDescription.Client
}
///
- /// Project output not found and --no-build was specified. Project must be up-to-date when using the --no-build option.
+ /// Project output not found. Project must be up-to-date when using this tool.
///
internal static string FormatMustBuild()
=> GetString("MustBuild");
diff --git a/src/dotnet-getdocument/Resources.resx b/src/dotnet-getdocument/Resources.resx
index 7db5873e7d..9829182dc6 100644
--- a/src/dotnet-getdocument/Resources.resx
+++ b/src/dotnet-getdocument/Resources.resx
@@ -175,7 +175,7 @@
Writing '{0}'...
- Project output not found and --no-build was specified. Project must be up-to-date when using the --no-build option.
+ Project output not found. Project must be up-to-date when using this tool.
The file to write the result to.
diff --git a/src/dotnet-getdocument/ServiceProjectReferenceMetadata.props b/src/dotnet-getdocument/ServiceProjectReferenceMetadata.props
deleted file mode 100644
index 30f045f3c0..0000000000
--- a/src/dotnet-getdocument/ServiceProjectReferenceMetadata.props
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
-
-
- $(WriteServiceProjectReferenceMetadataDependsOn)
-
-
-
-
- $(WriteServiceProjectReferenceMetadataDependsOn)
-
-
- $(WriteServiceProjectReferenceMetadataDependsOn)
-
-
-
diff --git a/src/dotnet-getdocument/ServiceProjectReferenceMetadata.targets b/src/dotnet-getdocument/ServiceProjectReferenceMetadata.targets
index e8840ea37b..a9a3115b94 100644
--- a/src/dotnet-getdocument/ServiceProjectReferenceMetadata.targets
+++ b/src/dotnet-getdocument/ServiceProjectReferenceMetadata.targets
@@ -1,20 +1,14 @@
-
-
-
+
-
-
-
+
+
@@ -29,21 +23,21 @@
+
-
-
+
+
-
+
diff --git a/src/dotnet-getdocument/dotnet-getdocument.csproj b/src/dotnet-getdocument/dotnet-getdocument.csproj
index 53967535a7..370514484b 100644
--- a/src/dotnet-getdocument/dotnet-getdocument.csproj
+++ b/src/dotnet-getdocument/dotnet-getdocument.csproj
@@ -1,89 +1,25 @@
-
- $(GenerateNuspecDependsOn);PopulateNuspec
-
dotnet-getdocument
GetDocument Command-line Tool outside man
false
- true
- false
false
- $(MSBuildProjectName).nuspec
Exe
- true
- GetDocument;command line;command-line;tool
Microsoft.Extensions.ApiDescription.Client
netcoreapp2.1
-
-
-
+
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- <_Temporary Remove="@(Temporary)" />
- <_Temporary Include="../GetDocumentInsider/GetDocumentInsider.csproj" Properties="TargetFramework=net461" />
- <_Temporary Include="../GetDocumentInsider/GetDocumentInsider.csproj" Properties="TargetFramework=net461;Platform=x86" />
- <_Temporary Include="../GetDocumentInsider/GetDocumentInsider.csproj" Properties="TargetFramework=netcoreapp2.0" />
-
-
-
-
-
-
- <_Temporary Remove="@(_Temporary)" />
-
-
-
-
- authors=$(Authors);
- copyright=$(Copyright);
- description=$(Description);
- iconUrl=$(PackageIconUrl);
- id=$(PackageId);
- InsiderNet461Output=..\GetDocumentInsider\bin\$(Configuration)\net461\publish\*;
- InsiderNet461X86Output=..\GetDocumentInsider\bin\x86\$(Configuration)\net461\publish\*;
- InsiderNetCoreOutput=..\GetDocumentInsider\bin\$(Configuration)\netcoreapp2.0\publish\*;
- licenseUrl=$(PackageLicenseUrl);
- Output=$(PublishDir)**\*;
- OutputShims=$(IntermediateOutputPath)shims\**\*;
- packageType=$(PackageType);
- projectUrl=$(PackageProjectUrl);
- repositoryCommit=$(RepositoryCommit);
- repositoryUrl=$(RepositoryUrl);
- SettingsFile=$(_ToolsSettingsFilePath);
- tags=$(PackageTags.Replace(';', ' '));
- targetFramework=$(TargetFramework);
- version=$(PackageVersion);
-
-
-
diff --git a/src/dotnet-getdocument/dotnet-getdocument.nuspec b/src/dotnet-getdocument/dotnet-getdocument.nuspec
deleted file mode 100644
index 60c51f7869..0000000000
--- a/src/dotnet-getdocument/dotnet-getdocument.nuspec
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
- $id$
- $version$
- $authors$
- true
- $licenseUrl$
- $projectUrl$
- $iconUrl$
- $description$
- $copyright$
- $tags$
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-