diff --git a/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI/.vscode/launch.json b/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI/.vscode/launch.json index 464db8e8..745377ff 100644 --- a/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI/.vscode/launch.json +++ b/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI/.vscode/launch.json @@ -10,7 +10,7 @@ "request": "launch", "preLaunchTask": "build", // If you have changed target frameworks, make sure to update the program path. - "program": "${workspaceFolder}/bin/Debug/netcoreapp2.1/azfuncopenapi.dll", + "program": "${workspaceFolder}/bin/Debug/net6.0/azfuncopenapi.dll", "args": [], "cwd": "${workspaceFolder}", // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console diff --git a/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI/.vscode/tasks.json b/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI/.vscode/tasks.json index 2b1a0ff2..f8cdf3ee 100644 --- a/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI/.vscode/tasks.json +++ b/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI/.vscode/tasks.json @@ -7,7 +7,7 @@ "type": "process", "args": [ "build", - "${workspaceFolder}/Aliencube.AzureFunctions.Extensions.OpenApi.CLI.csproj", + "${workspaceFolder}/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.csproj", "/property:GenerateFullPaths=true", "/consoleloggerparameters:NoSummary" ], @@ -19,7 +19,7 @@ "type": "process", "args": [ "publish", - "${workspaceFolder}/Aliencube.AzureFunctions.Extensions.OpenApi.CLI.csproj", + "${workspaceFolder}/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.csproj", "/property:GenerateFullPaths=true", "/consoleloggerparameters:NoSummary" ], @@ -32,7 +32,7 @@ "args": [ "watch", "run", - "${workspaceFolder}/Aliencube.AzureFunctions.Extensions.OpenApi.CLI.csproj", + "${workspaceFolder}/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.csproj", "/property:GenerateFullPaths=true", "/consoleloggerparameters:NoSummary" ], diff --git a/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI/Abstractions/ICustomApiMockCreator.cs b/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI/Abstractions/ICustomApiMockCreator.cs new file mode 100644 index 00000000..67735009 --- /dev/null +++ b/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI/Abstractions/ICustomApiMockCreator.cs @@ -0,0 +1,9 @@ +using Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.Model; + +namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.Abstractions +{ + public interface ICustomApiMockCreator + { + ApiMock SetupApi(string projectPath, string configuration, string targetFramework); + } +} \ No newline at end of file diff --git a/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI/Abstractions/ICustomOpenApiCreator.cs b/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI/Abstractions/ICustomOpenApiCreator.cs new file mode 100644 index 00000000..fdfad130 --- /dev/null +++ b/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI/Abstractions/ICustomOpenApiCreator.cs @@ -0,0 +1,17 @@ +using System.Threading.Tasks; +using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Enums; +using Microsoft.OpenApi.Models; + +namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.Abstractions +{ + public interface ICustomOpenApiCreator + { + Task CreateOpenApiDocument( + string apiBaseUrl, + string compiledDllPath, + string routePrefix, + OpenApiInfo openApiInfo, + OpenApiVersionType version, + OpenApiFormatType format); + } +} \ No newline at end of file diff --git a/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI/Abstractions/ICustomOpenApiWriter.cs b/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI/Abstractions/ICustomOpenApiWriter.cs new file mode 100644 index 00000000..ba01c697 --- /dev/null +++ b/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI/Abstractions/ICustomOpenApiWriter.cs @@ -0,0 +1,10 @@ +using System.Threading.Tasks; +using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Enums; + +namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.Abstractions +{ + public interface ICustomOpenApiWriter + { + Task WriteOpenApiToFile(string openApiDocument, string outputPath, OpenApiFormatType format); + } +} \ No newline at end of file diff --git a/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI/ConsoleApp/GenerateSwaggerConsoleApp.cs b/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI/ConsoleApp/GenerateSwaggerConsoleApp.cs new file mode 100644 index 00000000..00aa844a --- /dev/null +++ b/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI/ConsoleApp/GenerateSwaggerConsoleApp.cs @@ -0,0 +1,53 @@ +using System.Threading.Tasks; +using Cocona; +using Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.Abstractions; +using Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.Extensions; +using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Enums; + +namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.ConsoleApp +{ + public class GenerateSwaggerConsoleApp + { + /// + /// Generates the OpenAPI document. + /// + /// Project path. + /// + /// Compile configuration. + /// + /// OpenAPI version. + /// OpenAPI output format. + /// Output path. + public async Task Run( + [FromService] ICustomApiMockCreator customApiMockCreator, + [FromService] ICustomOpenApiCreator customOpenApiCreator, + [FromService] ICustomOpenApiWriter customOpenApiWriter, + [Option('p', Description = "Api Project path. Default is current directory")] + string project = ".", + [Option('a', Description = "ApiBaseUrl. Default is 'localhost'")] + string apibaseurl = "localhost", + [Option('c', Description = "Configuration. Default is 'Debug'")] + string configuration = "Debug", + [Option('t', Description = "Target framework. Default is 'net6.0'")] + string target = "net6.0", + [Option('v', Description = "OpenAPI spec version. Value can be either 'v2' or 'v3'. Default is 'v2'")] + OpenApiVersionType version = OpenApiVersionType.V2, + [Option('f', Description = "OpenAPI output format. Value can be either 'json' or 'yaml'. Default is 'json'")] + OpenApiFormatType format = OpenApiFormatType.Json, + [Option('o', Description = "Generated OpenAPI output location. Default is 'output'")] + string output = "output") + { + var apiMock = customApiMockCreator.SetupApi(project, configuration, target); + + var openApiDocument = await customOpenApiCreator.CreateOpenApiDocument( + apibaseurl, + apiMock.CompiledDllPath, + apiMock.HttpSettings.RoutePrefix, + apiMock.OpenApiInfo, + version, + format); + + await customOpenApiWriter.WriteOpenApiToFile(openApiDocument, output.GetOutputPath(apiMock.CompiledPath), format); + } + } +} diff --git a/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI/Extensions/ProjectPathExtensions.cs b/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI/Extensions/ProjectPathExtensions.cs new file mode 100644 index 00000000..b7b3ac24 --- /dev/null +++ b/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI/Extensions/ProjectPathExtensions.cs @@ -0,0 +1,73 @@ +using System; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Xml; + +namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.Extensions +{ + public static class ProjectPathExtensions + { + public static readonly char DirectorySeparator = Path.DirectorySeparatorChar; + + public static string TrimProjectPath(this string path) + { + var filePath = !Path.IsPathFullyQualified(path) + ? $"{Environment.CurrentDirectory.TrimEnd(DirectorySeparator)}{DirectorySeparator}{path}" + : path; + + return new DirectoryInfo(filePath).FullName.TrimEnd(DirectorySeparator); + } + + public static string GetCsProjFileName(this string path) + { + var filePath = !Path.IsPathFullyQualified(path) + ? $"{Environment.CurrentDirectory.TrimEnd(DirectorySeparator)}{DirectorySeparator}{path}" + : path; + + var segments = new DirectoryInfo(filePath).FullName.Split(new[] + { + DirectorySeparator + }, StringSplitOptions.RemoveEmptyEntries); + + return $"{segments.Last()}.csproj"; + } + + public static string GetProjectDllFileName(this string projectPath, string csprojFileName) + { + var doc = new XmlDocument(); + + doc.Load($"{projectPath}{DirectorySeparator}{csprojFileName}"); + + var elements = doc.GetElementsByTagName(nameof(AssemblyName)); + + var dllName = elements?.Cast()?.FirstOrDefault()?.InnerText; + + return string.IsNullOrWhiteSpace(dllName) + ? csprojFileName.Replace(".csproj", ".dll") + : $"{dllName}.dll"; + } + + public static string GetProjectCompiledPath(this string path, string configuration, string targetFramework) + { + return $"{path.TrimEnd(DirectorySeparator)}{DirectorySeparator}bin{DirectorySeparator}{configuration}{DirectorySeparator}{targetFramework}"; + } + + public static string GetProjectCompiledDllPath(this string compiledPath, string dllFileName) + { + return $"{compiledPath}{DirectorySeparator}{dllFileName}"; + } + + public static string GetProjectHostJsonPath(this string compiledPath) + { + return $"{compiledPath}{DirectorySeparator}host.json"; + } + + public static string GetOutputPath(this string output, string compiledPath) + { + return !Path.IsPathFullyQualified(output) + ? $"{compiledPath}{DirectorySeparator}{output}" + : output; + } + } +} diff --git a/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI/Extensions/SetupHostExtensions.cs b/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI/Extensions/SetupHostExtensions.cs new file mode 100644 index 00000000..d1bc55c8 --- /dev/null +++ b/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI/Extensions/SetupHostExtensions.cs @@ -0,0 +1,43 @@ +using System; +using System.Linq; +using System.Reflection; +using Microsoft.Azure.WebJobs.Extensions.OpenApi.Configurations.AppSettings.Extensions; +using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Abstractions; +using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Configurations; +using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Extensions; +using Microsoft.Extensions.Configuration; +using Microsoft.OpenApi.Models; + +namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.Extensions +{ + public static class SetupHostExtensions + { + public static HttpSettings SetHostSettings(this string hostJsonPath) + { + var host = new ConfigurationBuilder() + .SetBasePath(hostJsonPath.Replace("host.json", "")) + .AddJsonFile("host.json") + .AddEnvironmentVariables() + .Build(); + + var version = host.GetSection("version").Value; + + HttpSettings hostJsonSetting; + if (string.IsNullOrWhiteSpace(version)) + hostJsonSetting = host.Get("http"); + else if (version.Equals("2.0", StringComparison.CurrentCultureIgnoreCase)) + hostJsonSetting = host.Get("extensions:http"); + else + hostJsonSetting = host.Get("http"); + + return hostJsonSetting; + } + + public static OpenApiInfo SetOpenApiInfo(this string compiledDllPath) + { + var assembly = Assembly.LoadFrom(compiledDllPath); + var type = assembly.GetLoadableTypes().SingleOrDefault(p => p.GetInterface(nameof(IOpenApiConfigurationOptions), true).IsNullOrDefault() == false); + return !type.IsNullOrDefault() ? (Activator.CreateInstance(type) as IOpenApiConfigurationOptions)?.Info : new DefaultOpenApiConfigurationOptions().Info; + } + } +} \ No newline at end of file diff --git a/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.csproj b/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.csproj index 102edbc2..839c95b4 100644 --- a/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.csproj +++ b/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.csproj @@ -4,21 +4,23 @@ Exe - netcoreapp3.1 + net5.0;net6.0 + win-x64;linux-x64;osx-x64 azfuncopenapi Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI - + - + + diff --git a/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI/Model/ApiMock.cs b/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI/Model/ApiMock.cs new file mode 100644 index 00000000..1cec2606 --- /dev/null +++ b/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI/Model/ApiMock.cs @@ -0,0 +1,13 @@ +using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Configurations; +using Microsoft.OpenApi.Models; + +namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.Model +{ + public class ApiMock + { + public string CompiledPath { set; get; } + public string CompiledDllPath { set; get; } + public HttpSettings HttpSettings { set; get; } + public OpenApiInfo OpenApiInfo { set; get; } + } +} \ No newline at end of file diff --git a/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI/Program.cs b/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI/Program.cs index ece1bcce..6e52afc8 100644 --- a/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI/Program.cs +++ b/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI/Program.cs @@ -1,114 +1,26 @@ -using System; -using System.IO; -using System.Text; - using Cocona; - -using Microsoft.AspNetCore.Http; -using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core; -using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Abstractions; -using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Configurations; -using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Enums; -using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Extensions; -using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Visitors; - -using Moq; - -using Newtonsoft.Json.Serialization; +using Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.ConsoleApp; namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI { /// - /// This represents the console app entity. + /// This represents the console app entity. /// public class Program { - private static readonly char directorySeparator = Path.DirectorySeparatorChar; - /// - /// Invokes the console app. + /// Invokes the console app. /// /// List of arguments. public static void Main(string[] args) { - CoconaLiteApp.Run(args); - } - - /// - /// Generates the OpenAPI document. - /// - /// Project path. - /// Copile configuration. - /// OpenAPI version. - /// OpenAPI output format. - /// Output path. - /// Value indicating whether to render the document on console or not. - public void Generate( - [Option('p', Description = "Project path. Default is current directory")] string project = ".", - [Option('c', Description = "Configuration. Default is 'Debug'")] string configuration = "Debug", - [Option('t', Description = "Target framework. Default is 'netcoreapp2.1'")] string target = "netcoreapp2.1", - [Option('v', Description = "OpenAPI spec version. Value can be either 'v2' or 'v3'. Default is 'v2'")] OpenApiVersionType version = OpenApiVersionType.V2, - [Option('f', Description = "OpenAPI output format. Value can be either 'json' or 'yaml'. Default is 'yaml'")] OpenApiFormatType format = OpenApiFormatType.Json, - [Option('o', Description = "Generated OpenAPI output location. Default is 'output'")] string output = "output", - bool console = false) - { - var pi = default(ProjectInfo); - try - { - pi = new ProjectInfo(project, configuration, target); - } - catch (Exception ex) - { - Console.WriteLine(ex.Message); - Console.WriteLine(ex.StackTrace); - - return; - } - - var req = new Mock(); - req.SetupGet(p => p.Scheme).Returns("http"); - req.SetupGet(p => p.Host).Returns(new HostString("localhost", 7071)); - - var filter = new RouteConstraintFilter(); - var acceptor = new OpenApiSchemaAcceptor(); - var namingStrategy = new CamelCaseNamingStrategy(); - var collection = VisitorCollection.CreateInstance(); - var helper = new DocumentHelper(filter, acceptor); - var document = new Document(helper); - - var swagger = default(string); - try - { - swagger = document.InitialiseDocument() - .AddMetadata(pi.OpenApiInfo) - .AddServer(req.Object, pi.HostJsonHttpSettings.RoutePrefix) - .AddNamingStrategy(namingStrategy) - .AddVisitors(collection) - .Build(pi.CompiledDllPath) - .RenderAsync(version.ToOpenApiSpecVersion(), format.ToOpenApiFormat()) - .Result; - } - catch (Exception ex) - { - Console.WriteLine(ex.Message); - Console.WriteLine(ex.StackTrace); - } - - if (console) - { - Console.WriteLine(swagger); - } - - var outputpath = Path.IsPathFullyQualified(output) - ? output - : $"{pi.CompiledPath}{directorySeparator}{output}"; - - if (!Directory.Exists(outputpath)) - { - Directory.CreateDirectory(outputpath); - } - - File.WriteAllText($"{outputpath}{directorySeparator}swagger.{format.ToDisplayName()}", swagger, Encoding.UTF8); + CoconaApp + .CreateHostBuilder() + .ConfigureServices(services => + { + services.ConfigureInternalServices(); + }) + .Run(args); } } -} +} \ No newline at end of file diff --git a/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI/ProjectInfo.cs b/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI/ProjectInfo.cs deleted file mode 100644 index a5fad394..00000000 --- a/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI/ProjectInfo.cs +++ /dev/null @@ -1,247 +0,0 @@ -using System; -using System.IO; -using System.Linq; -using System.Reflection; - -using Microsoft.Azure.WebJobs.Extensions.OpenApi.Configurations.AppSettings.Extensions; -using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Abstractions; -using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Configurations; -using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Extensions; -using Microsoft.Extensions.Configuration; -using Microsoft.OpenApi.Models; - -namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI -{ - /// - /// This represents the project info entity. - /// - public class ProjectInfo - { - private static readonly char directorySeparator = System.IO.Path.DirectorySeparatorChar; - - private string _projectPath; - private string _filename; - private string _configuration; - private string _targetFramework; - - /// - /// Initializes a new instance of the class. - /// - public ProjectInfo() - { - } - - /// - /// Initializes a new instance of the class. - /// - /// Project path. - /// Configuration value. - /// Target framework. - public ProjectInfo(string projectPath, string configuration, string targetFramework) - { - this.SetProjectPath(projectPath.ThrowIfNullOrWhiteSpace()); - this._configuration = configuration.ThrowIfNullOrWhiteSpace(); - this._targetFramework = targetFramework.ThrowIfNullOrWhiteSpace(); - - this.SetHostSettings(); - this.SetOpenApiInfo(); - } - - /// - /// Gets or sets the fully qualified project path. - /// - public virtual string Path - { - get - { - return this._projectPath; - } - set - { - this.SetProjectPath(value); - } - } - - /// - /// Gets or sets the compile configuration value. - /// - public virtual string Configuration - { - get - { - return this._configuration; - } - set - { - this._configuration = value; - } - } - - /// - /// Gets or sets the compile target framework value. - /// - public virtual string TargetFramework - { - get - { - return this._targetFramework; - } - set - { - this._targetFramework = value; - } - } - - /// - /// Gets the project filename, ie) *.csproj - /// - public virtual string Filename - { - get - { - return this._filename; - } - } - - /// - /// Gets the fully qualified compiled directory path. - /// - public virtual string CompiledPath - { - get - { - this.Path.ThrowIfNullOrWhiteSpace(); - this.Configuration.ThrowIfNullOrWhiteSpace(); - this.TargetFramework.ThrowIfNullOrWhiteSpace(); - - return $"{this.Path.TrimEnd(directorySeparator)}{directorySeparator}bin{directorySeparator}{this.Configuration}{directorySeparator}{this.TargetFramework}"; - } - } - - /// - /// Gets the fully qualified compiled .dll file path. - /// - public virtual string CompiledDllPath - { - get - { - this.CompiledPath.ThrowIfNullOrWhiteSpace(); - this.Filename.ThrowIfNullOrWhiteSpace(); - - return $"{this.CompiledPath}{directorySeparator}bin{directorySeparator}{this.Filename}".Replace(".csproj", ".dll"); - } - } - - /// - /// Gets the fully qualified file path of host.json. - /// - public virtual string HostJsonPath - { - get - { - this.CompiledPath.ThrowIfNullOrWhiteSpace(); - - return $"{this.CompiledPath}{directorySeparator}host.json"; - } - } - - /// - /// Gets the host.json settings. - /// - public virtual IConfiguration HostSettings { get; private set; } - - /// - /// Gets the HTTP settings in host.json. - /// - public virtual HttpSettings HostJsonHttpSettings { get; private set; } - - /// - /// Gets the instance. - /// - public virtual OpenApiInfo OpenApiInfo { get; private set; } - - private void SetProjectPath(string path) - { - if (path.IsNullOrWhiteSpace() || path == ".") - { - this._projectPath = Environment.CurrentDirectory.TrimEnd(directorySeparator); - - var filepath = Directory.GetFiles(this._projectPath, "*.csproj").SingleOrDefault(); - if (filepath.IsNullOrWhiteSpace()) - { - throw new FileNotFoundException(); - } - - var csproj = new FileInfo(filepath); - this._filename = csproj.Name; - - return; - } - - var fqpath = System.IO.Path.IsPathFullyQualified(path) - ? path - : $"{Environment.CurrentDirectory.TrimEnd(directorySeparator)}{directorySeparator}{path}"; - - if (fqpath.EndsWith(".csproj")) - { - var csproj = new FileInfo(fqpath); - - this._projectPath = csproj.DirectoryName.TrimEnd(directorySeparator); - this._filename = csproj.Name; - - return; - } - - var di = new DirectoryInfo(fqpath); - this._projectPath = di.FullName.TrimEnd(directorySeparator); - - var segments = di.FullName.Split(new[] { directorySeparator }, StringSplitOptions.RemoveEmptyEntries); - this._filename = $"{segments.Last()}.csproj"; - } - - private void SetHostSettings() - { - this.HostJsonPath.ThrowIfNullOrWhiteSpace(); - - var host = new ConfigurationBuilder() - .SetBasePath(this.HostJsonPath.Replace("host.json", "")) - .AddJsonFile("host.json") - .Build(); - - this.HostSettings = host; - - var version = this.HostSettings.GetSection("version").Value; - this.HostJsonHttpSettings = string.IsNullOrWhiteSpace(version) - ? host.Get("http") - : (version.Equals("2.0", StringComparison.CurrentCultureIgnoreCase) - ? host.Get("extensions:http") - : host.Get("http")); - } - - private void SetOpenApiInfo() - { - var assembly = Assembly.LoadFrom(this.CompiledDllPath); - - var type = assembly.GetLoadableTypes() - .SingleOrDefault(p => p.GetInterface("IOpenApiConfigurationOptions", ignoreCase: true).IsNullOrDefault() == false); - if (type.IsNullOrDefault()) - { - var settings = new DefaultOpenApiConfigurationOptions(); - this.OpenApiInfo = settings.Info; - - return; - } - - var options = Activator.CreateInstance(type); - - this.OpenApiInfo = (options as IOpenApiConfigurationOptions).Info; - } - - private bool IsValidOpenApiInfo(OpenApiInfo openApiInfo) - { - openApiInfo.ThrowIfNullOrDefault(); - - return !openApiInfo.IsNullOrDefault() && !openApiInfo.Version.IsNullOrDefault() && !openApiInfo.Title.IsNullOrWhiteSpace(); - } - } -} diff --git a/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI/Services/CustomApiMockCreator.cs b/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI/Services/CustomApiMockCreator.cs new file mode 100644 index 00000000..6e62388c --- /dev/null +++ b/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI/Services/CustomApiMockCreator.cs @@ -0,0 +1,41 @@ +using System; +using Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.Abstractions; +using Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.Extensions; +using Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.Model; + +namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.Services +{ + public class CustomApiMockCreator : ICustomApiMockCreator + { + public ApiMock SetupApi(string projectPath, string configuration, string targetFramework) + { + try + { + var trimProjectPath = projectPath.TrimProjectPath(); + var csProjFileName = trimProjectPath.GetCsProjFileName(); + var dllFileName = trimProjectPath.GetProjectDllFileName(csProjFileName); + var compiledPath = trimProjectPath.GetProjectCompiledPath(configuration, targetFramework); + var compiledDllPath = compiledPath.GetProjectCompiledDllPath(dllFileName); + var hostJsonPath = compiledPath.GetProjectHostJsonPath(); + var httpSettings = hostJsonPath.SetHostSettings(); + var openApiInfo = compiledDllPath.SetOpenApiInfo(); + + var apiMock = new ApiMock + { + CompiledPath = compiledPath, + CompiledDllPath = compiledDllPath, + HttpSettings = httpSettings, + OpenApiInfo = openApiInfo + }; + + return apiMock; + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + Console.WriteLine(ex.StackTrace); + throw; + } + } + } +} \ No newline at end of file diff --git a/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI/Services/CustomOpenApiCreator.cs b/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI/Services/CustomOpenApiCreator.cs new file mode 100644 index 00000000..b330dbe1 --- /dev/null +++ b/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI/Services/CustomOpenApiCreator.cs @@ -0,0 +1,55 @@ +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.Abstractions; +using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core; +using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Abstractions; +using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Configurations; +using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Enums; +using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Extensions; +using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Visitors; +using Microsoft.OpenApi.Models; +using Moq; +using Newtonsoft.Json.Serialization; + +namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.Services +{ + public class CustomOpenApiCreator : ICustomOpenApiCreator + { + public async Task CreateOpenApiDocument( + string apiBaseUrl, + string compiledDllPath, + string routePrefix, + OpenApiInfo openApiInfo, + OpenApiVersionType version, + OpenApiFormatType format) + { + try + { + var query = new Mock(); + var request = new Mock(); + request.SetupGet(p => p.Scheme).Returns("https"); + request.SetupGet(p => p.Host).Returns(new HostString(apiBaseUrl)); + request.SetupGet(p => p.Query).Returns(query.Object); + + var document = new Functions.Worker.Extensions.OpenApi.Document(new DocumentHelper(new RouteConstraintFilter(), new OpenApiSchemaAcceptor())); + + var swagger = await document.InitialiseDocument() + .AddMetadata(openApiInfo) + .AddServer(request.Object, routePrefix) + .AddNamingStrategy(new CamelCaseNamingStrategy()) + .AddVisitors(VisitorCollection.CreateInstance()) + .Build(compiledDllPath) + .RenderAsync(version.ToOpenApiSpecVersion(), format.ToOpenApiFormat()); + + return swagger; + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + Console.WriteLine(ex.StackTrace); + throw; + } + } + } +} \ No newline at end of file diff --git a/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI/Services/CustomOpenApiWriter.cs b/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI/Services/CustomOpenApiWriter.cs new file mode 100644 index 00000000..cd8f5b67 --- /dev/null +++ b/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI/Services/CustomOpenApiWriter.cs @@ -0,0 +1,21 @@ +using System.IO; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.Abstractions; +using Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.Extensions; +using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Enums; +using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Extensions; + +namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.Services +{ + public class CustomOpenApiWriter : ICustomOpenApiWriter + { + public async Task WriteOpenApiToFile(string openApiDocument, string outputPath, OpenApiFormatType format) + { + if (!Directory.Exists(outputPath)) + Directory.CreateDirectory(outputPath); + + await File.WriteAllTextAsync($"{outputPath}{ProjectPathExtensions.DirectorySeparator}swagger.{format.ToDisplayName()}", openApiDocument, Encoding.UTF8); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI/Setup.cs b/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI/Setup.cs new file mode 100644 index 00000000..a8a83f04 --- /dev/null +++ b/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI/Setup.cs @@ -0,0 +1,16 @@ +using Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.Abstractions; +using Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.Services; +using Microsoft.Extensions.DependencyInjection; + +namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI +{ + public static class Setup + { + public static void ConfigureInternalServices(this IServiceCollection services) + { + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI/package.json b/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI/package.json index 8932f4f8..75451bb7 100644 --- a/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI/package.json +++ b/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI/package.json @@ -11,13 +11,13 @@ "openapi", "swagger" ], - "homepage": "https://github.com/aliencube/AzureFunctions.Extensions", + "homepage": "https://github.com/Azure/azure-functions-openapi-extension", "bugs": { - "url": "https://github.com/aliencube/AzureFunctions.Extensions/issues" + "url": "https://github.com/Azure/azure-functions-openapi-extension/issues" }, "repository": { "type": "git", - "url": "https://github.com/aliencube/AzureFunctions.Extensions.git" + "url": "https://github.com/Azure/azure-functions-openapi-extension.git" }, "scripts": { "postinstall": "node lib/install.js", diff --git a/test/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.Tests/Extensions/ProjectPathExtensionsTests.cs b/test/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.Tests/Extensions/ProjectPathExtensionsTests.cs new file mode 100644 index 00000000..0d70655d --- /dev/null +++ b/test/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.Tests/Extensions/ProjectPathExtensionsTests.cs @@ -0,0 +1,130 @@ +using System.Diagnostics; +using System.IO; +using System.Reflection; +using FluentAssertions; +using Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.Extensions; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.Tests.Extensions +{ + [TestClass] + public class ProjectPathExtensionsTests + { + private string _compiledPath; + private string _configuration; + private bool _isDebug; + private string _projectPath; + private string _target; + + [TestInitialize] + public void Init() + { + this.IsDebugCheck(ref this._isDebug); + + var directory = Assembly.GetExecutingAssembly().Location; + var solutionDirectory = Directory.GetParent(directory).Parent.Parent.Parent.Parent.Parent.FullName; + this._projectPath = $"{solutionDirectory}/samples/Microsoft.Azure.Functions.Worker.Extensions.OpenApi.FunctionApp.OutOfProc"; + this._configuration = this._isDebug ? "Debug" : "Release"; + this._target = "net6.0"; + this._compiledPath = $"{this._projectPath}{ProjectPathExtensions.DirectorySeparator}bin{ProjectPathExtensions.DirectorySeparator}{this._configuration}{ProjectPathExtensions.DirectorySeparator}{this._target}"; + } + + [Conditional("DEBUG")] + private void IsDebugCheck(ref bool isDebug) + { + isDebug = true; + } + + [TestMethod] + public void TrimProjectPath() + { + // Arrange + var projectPath = $"{this._projectPath}{ProjectPathExtensions.DirectorySeparator}"; + + // Act + var result = projectPath.TrimProjectPath(); + + // Assert + result.Should().NotBeNull(); + } + + [TestMethod] + public void GetCsProjFileName() + { + // Arrange + var projectPath = this._projectPath; + + // Act + var result = projectPath.GetCsProjFileName(); + + // Assert + result.Should().NotBeNull(); + } + + [TestMethod] + public void GetProjectDllFileName() + { + // Arrange + var csprojFileName = "Microsoft.Azure.Functions.Worker.Extensions.OpenApi.FunctionApp.OutOfProc.csproj"; + + + // Act + var result = this._projectPath.GetProjectDllFileName(csprojFileName); + + // Assert + result.Should().NotBeNull(); + } + + [TestMethod] + public void GetProjectCompiledPath() + { + // Arrange + var projectPath = this._projectPath; + + // Act + var result = projectPath.GetProjectCompiledPath(this._configuration, this._target); + + // Assert + result.Should().NotBeNull(); + } + + [TestMethod] + public void GetProjectCompiledDllPath() + { + // Arrange + var dllFileName = "Microsoft.Azure.Functions.Worker.Extensions.OpenApi.FunctionApp.OutOfProc.dll"; + + // Act + var result = this._compiledPath.GetProjectCompiledDllPath(dllFileName); + + // Assert + result.Should().NotBeNull(); + } + + [TestMethod] + public void GetProjectHostJsonPath() + { + // Arrange + var projectPath = this._projectPath; + + // Act + var result = this._compiledPath.GetProjectHostJsonPath(); + + // Assert + result.Should().NotBeNull(); + } + + [TestMethod] + public void GetOutputPath() + { + // Arrange + var output = "output"; + + // Act + var result = output.GetOutputPath(this._compiledPath); + + // Assert + result.Should().NotBeNull(); + } + } +} diff --git a/test/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.Tests/Extensions/SetupHostExtensionsTests.cs b/test/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.Tests/Extensions/SetupHostExtensionsTests.cs new file mode 100644 index 00000000..6648365f --- /dev/null +++ b/test/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.Tests/Extensions/SetupHostExtensionsTests.cs @@ -0,0 +1,40 @@ +using System.IO; +using System.Reflection; +using FluentAssertions; +using Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.Extensions; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.Tests.Extensions +{ + [TestClass] + public class SetupHostExtensionsTests + { + [TestMethod] + public void HttpSettings() + { + // Arrange + var path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); + var hostJsonPath = $"{path}/host.json"; + + // Act + var result = hostJsonPath.SetHostSettings(); + + // Assert + result.Should().NotBeNull(); + } + + [TestMethod] + public void SetOpenApiInfo() + { + // Arrange + var path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); + var compiledDllPath = $"{path}/Microsoft.Azure.Functions.Worker.Extensions.OpenApi.FunctionApp.OutOfProc.dll"; + + // Act + var result = compiledDllPath.SetOpenApiInfo(); + + // Assert + result.Should().NotBeNull(); + } + } +} diff --git a/test/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.Tests/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.Tests.csproj b/test/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.Tests/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.Tests.csproj index e203d998..a2d71dd8 100644 --- a/test/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.Tests/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.Tests.csproj +++ b/test/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.Tests/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.Tests.csproj @@ -1,7 +1,7 @@ - netcoreapp3.1 + net6.0 false @@ -26,6 +26,7 @@ + diff --git a/test/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.Tests/ProjectInfoTests.cs b/test/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.Tests/ProjectInfoTests.cs deleted file mode 100644 index fe8efc11..00000000 --- a/test/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.Tests/ProjectInfoTests.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; - -using FluentAssertions; - -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.Tests -{ - [TestClass] - public class ProjectInfoTests - { - [TestMethod] - public void Given_Null_Parameters_When_Instantiated_Then_It_Should_Throw_Exception() - { - Action action = () => new ProjectInfo(null, null, null); - action.Should().Throw(); - - action = () => new ProjectInfo("abc", null, null); - action.Should().Throw(); - - action = () => new ProjectInfo("abc", "abc", null); - action.Should().Throw(); - } - } -} diff --git a/test/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.Tests/Services/CustomApiMockCreatorTests.cs b/test/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.Tests/Services/CustomApiMockCreatorTests.cs new file mode 100644 index 00000000..0cc7a45a --- /dev/null +++ b/test/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.Tests/Services/CustomApiMockCreatorTests.cs @@ -0,0 +1,55 @@ +using System.Diagnostics; +using System.IO; +using System.Reflection; +using FluentAssertions; +using Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.Services; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.Tests.Services +{ + [TestClass] + public class CustomApiMockCreatorTests + { + private bool _isDebug; + + [TestInitialize] + public void Init() + { + this.IsDebugCheck(ref this._isDebug); + } + + [Conditional("DEBUG")] + private void IsDebugCheck(ref bool isDebug) + { + isDebug = true; + } + + [TestMethod] + public void SetupApi() + { + // Arrange + var directory = Assembly.GetExecutingAssembly().Location; + var solutionDirectory = Directory.GetParent(directory).Parent.Parent.Parent.Parent.Parent.FullName; + var projectPath = $"{solutionDirectory}/samples/Microsoft.Azure.Functions.Worker.Extensions.OpenApi.FunctionApp.OutOfProc"; + var configuration = this._isDebug ? "Debug" : "Release"; + var target = "net6.0"; + + var service = this.SetupSut(); + + // Act + var result = service.SetupApi( + projectPath, + configuration, + target); + + // Assert + result.Should().NotBeNull(); + } + + private CustomApiMockCreator SetupSut() + { + var service = new CustomApiMockCreator(); + return service; + } + } +} diff --git a/test/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.Tests/Services/CustomOpenApiCreatorTests.cs b/test/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.Tests/Services/CustomOpenApiCreatorTests.cs new file mode 100644 index 00000000..86abf7e6 --- /dev/null +++ b/test/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.Tests/Services/CustomOpenApiCreatorTests.cs @@ -0,0 +1,49 @@ +using System.IO; +using System.Reflection; +using System.Threading.Tasks; +using FluentAssertions; +using Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.Extensions; +using Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.Services; +using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Enums; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.Tests.Services +{ + [TestClass] + public class CustomOpenApiCreatorTests + { + [TestMethod] + public async Task CreateOpenApiDocument() + { + // Arrange + var apiBaseUrl = "http://test.function.com/"; + var path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); + var compiledDllPath = $"{path}/Microsoft.Azure.Functions.Worker.Extensions.OpenApi.FunctionApp.OutOfProc.dll"; + var hostJsonPath = $"{path}/host.json"; + var httpSettings = hostJsonPath.SetHostSettings(); + var openApiInfo = compiledDllPath.SetOpenApiInfo(); + var openApiVersionType = OpenApiVersionType.V2; + var openApiFormatType = OpenApiFormatType.Json; + + var service = this.SetupSut(); + + // Act + var result = await service.CreateOpenApiDocument( + apiBaseUrl, + compiledDllPath, + httpSettings.RoutePrefix, + openApiInfo, + openApiVersionType, + openApiFormatType); + + // Assert + result.Should().NotBeNull(); + } + + private CustomOpenApiCreator SetupSut() + { + var service = new CustomOpenApiCreator(); + return service; + } + } +} diff --git a/test/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.Tests/Services/CustomOpenApiWriterTests.cs b/test/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.Tests/Services/CustomOpenApiWriterTests.cs new file mode 100644 index 00000000..36e912dd --- /dev/null +++ b/test/Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.Tests/Services/CustomOpenApiWriterTests.cs @@ -0,0 +1,42 @@ +using System.IO; +using System.Reflection; +using System.Threading.Tasks; +using FluentAssertions; +using Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.Extensions; +using Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.Services; +using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Enums; +using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Extensions; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.CLI.Tests.Services +{ + [TestClass] + public class CustomOpenApiWriterTests + { + [TestMethod] + public async Task WriteOpenApiToFile() + { + // Arrange + var openApiDocument = "{\"Test\": \"Test\"}"; + var path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); + var openApiFormatType = OpenApiFormatType.Json; + + var service = this.SetupSut(); + + // Act + await service.WriteOpenApiToFile( + openApiDocument, + path, + openApiFormatType); + + // Assert + File.Exists($"{path}{ProjectPathExtensions.DirectorySeparator}swagger.{openApiFormatType.ToDisplayName()}").Should().BeTrue(); + } + + private CustomOpenApiWriter SetupSut() + { + var service = new CustomOpenApiWriter(); + return service; + } + } +}