diff --git a/Run-SystemTest.ps1 b/Run-SystemTest.ps1 index e0860e8..cc70da0 100644 --- a/Run-SystemTest.ps1 +++ b/Run-SystemTest.ps1 @@ -5,6 +5,26 @@ Begin { } Process { + function BuildProject { + param ( + [string]$openApiMsBuildSource, + [string]$projectPath, + [bool]$isFailureExpected, + [string]$extraArgs + ) + Push-Location $projectPath + + Exec { & dotnet add package Workleap.OpenApi.MSBuild --prerelease --source $openApiMsBuildSource } + + $buildProcess = Start-Process -FilePath "dotnet" -ArgumentList "build -c Release $extraArgs" -NoNewWindow -PassThru -Wait + + if ($isFailureExpected -and $buildProcess.ExitCode -eq 0 ) { + Write-Error "The build for project $projectPath was expected to fail, but it succeeded." + } elseif (!$isFailureExpected -and $buildProcess.ExitCode -ne 0) { + Write-Error "The build for project $projectPath was expected to succeed, but it failed." + } + } + function Exec([scriptblock]$Command) { & $Command if ($LASTEXITCODE -ne 0) { @@ -14,24 +34,24 @@ Process { $workingDir = Join-Path $PSScriptRoot "src" $outputDir = Join-Path $PSScriptRoot ".output" + $contractFirstSysTestDir = Join-Path $PSScriptRoot "src/tests/WebApi.MsBuild.SystemTest.ContractFirst" $codeFirstSysTestDir = Join-Path $PSScriptRoot "src/tests/WebApi.MsBuild.SystemTest.CodeFirst" + $oasDiffErrorSysTestDir = Join-Path $PSScriptRoot "src/tests/WebApi.MsBuild.SystemTest.OasDiffError" + $spectralErrorSysTestDir = Join-Path $PSScriptRoot "src/tests/WebApi.MsBuild.SystemTest.SpectralError" try { Push-Location $workingDir + # Build the OpenApi.MSBuild package to be used in the system tests Exec { & dotnet pack -c Release -o "$outputDir" } - Push-Location $contractFirstSysTestDir - - Exec { & dotnet add package Workleap.OpenApi.MSBuild --prerelease --source $outputDir } - Exec { & dotnet build -c Release -warnaserror } - - Push-Location $codeFirstSysTestDir - - Exec { & dotnet add package Workleap.OpenApi.MSBuild --prerelease --source $outputDir } - Exec { & dotnet build -c Release -warnaserror } - + BuildProject -openApiMsBuildSource $outputDir -projectPath $contractFirstSysTestDir -isFailureExpected $false + BuildProject -openApiMsBuildSource $outputDir -projectPath $codeFirstSysTestDir -isFailureExpected $false + BuildProject -openApiMsBuildSource $outputDir -projectPath $oasDiffErrorSysTestDir -isFailureExpected $true + BuildProject -openApiMsBuildSource $outputDir -projectPath $spectralErrorSysTestDir -isFailureExpected $true + BuildProject -openApiMsBuildSource $outputDir -projectPath $oasDiffErrorSysTestDir -isFailureExpected $false -extraArgs "/p:OpenApiTreatWarningsAsErrors=false" + BuildProject -openApiMsBuildSource $outputDir -projectPath $spectralErrorSysTestDir -isFailureExpected $false -extraArgs "/p:OpenApiTreatWarningsAsErrors=false" } finally { Pop-Location diff --git a/src/Workleap.OpenApi.MSBuild.sln b/src/Workleap.OpenApi.MSBuild.sln index 17d5350..714f10a 100644 --- a/src/Workleap.OpenApi.MSBuild.sln +++ b/src/Workleap.OpenApi.MSBuild.sln @@ -23,6 +23,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebApi.MsBuild.SystemTest.C EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebApi.MsBuild.SystemTest.CodeFirst", "tests\WebApi.MsBuild.SystemTest.CodeFirst\WebApi.MsBuild.SystemTest.CodeFirst.csproj", "{575E14D8-52E8-4B5B-A93C-D3E86E30BD3C}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebApi.MsBuild.SystemTest.OasDiffError", "tests\WebApi.MsBuild.SystemTest.OasDiffError\WebApi.MsBuild.SystemTest.OasDiffError.csproj", "{3909D38E-7584-4206-9CDC-3E56203BE6DB}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebApi.MsBuild.SystemTest.SpectralError", "tests\WebApi.MsBuild.SystemTest.SpectralError\WebApi.MsBuild.SystemTest.SpectralError.csproj", "{C42C2836-4997-49D3-9BC6-E6A0E1B8C472}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -52,10 +56,20 @@ Global {575E14D8-52E8-4B5B-A93C-D3E86E30BD3C}.Debug|Any CPU.Build.0 = Debug|Any CPU {575E14D8-52E8-4B5B-A93C-D3E86E30BD3C}.Release|Any CPU.ActiveCfg = Release|Any CPU {575E14D8-52E8-4B5B-A93C-D3E86E30BD3C}.Release|Any CPU.Build.0 = Release|Any CPU + {3909D38E-7584-4206-9CDC-3E56203BE6DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3909D38E-7584-4206-9CDC-3E56203BE6DB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3909D38E-7584-4206-9CDC-3E56203BE6DB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3909D38E-7584-4206-9CDC-3E56203BE6DB}.Release|Any CPU.Build.0 = Release|Any CPU + {C42C2836-4997-49D3-9BC6-E6A0E1B8C472}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C42C2836-4997-49D3-9BC6-E6A0E1B8C472}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C42C2836-4997-49D3-9BC6-E6A0E1B8C472}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C42C2836-4997-49D3-9BC6-E6A0E1B8C472}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {A4C90BE2-889F-46BB-8B4D-2219B1084695} = {07F66FC2-73B9-44C7-843C-36C13905AE9B} {B8A81C76-574B-40FD-B34B-FA893D6C1423} = {4DDE83BF-D190-4CC9-AD36-E9250DABB27D} {575E14D8-52E8-4B5B-A93C-D3E86E30BD3C} = {4DDE83BF-D190-4CC9-AD36-E9250DABB27D} + {3909D38E-7584-4206-9CDC-3E56203BE6DB} = {4DDE83BF-D190-4CC9-AD36-E9250DABB27D} + {C42C2836-4997-49D3-9BC6-E6A0E1B8C472} = {4DDE83BF-D190-4CC9-AD36-E9250DABB27D} EndGlobalSection EndGlobal diff --git a/src/Workleap.OpenApi.MSBuild/LoggerWrapper.cs b/src/Workleap.OpenApi.MSBuild/LoggerWrapper.cs index ee759c4..2a987d8 100644 --- a/src/Workleap.OpenApi.MSBuild/LoggerWrapper.cs +++ b/src/Workleap.OpenApi.MSBuild/LoggerWrapper.cs @@ -5,13 +5,30 @@ namespace Workleap.OpenApi.MSBuild; internal sealed class LoggerWrapper : ILoggerWrapper { - private readonly TaskLoggingHelper _taskLoggingHelper; + private readonly TaskLoggingHelper _helper; + private readonly bool _treatWarningAsError; - public LoggerWrapper(TaskLoggingHelper helper) => this._taskLoggingHelper = helper; + public LoggerWrapper(TaskLoggingHelper helper, bool treatWarningAsError) + { + this._helper = helper; + this._treatWarningAsError = treatWarningAsError; + } + + public void LogMessage(string message, MessageImportance importance = MessageImportance.Low, params object[] messageArgs) + => this._helper.LogMessage(importance, message, messageArgs); public void LogWarning(string message, params object[] messageArgs) - => this._taskLoggingHelper.LogWarning(message, messageArgs); + { + if (this._treatWarningAsError) + { + this._helper.LogError(message, messageArgs); + } + else + { + this._helper.LogWarning(message, messageArgs); + } + } - public void LogMessage(string message, MessageImportance importance = MessageImportance.Low, params object[] messageArgs) - => this._taskLoggingHelper.LogMessage(importance, message, messageArgs); + public void LogError(string message, params object[] messageArgs) + => this._helper.LogError(message, messageArgs); } \ No newline at end of file diff --git a/src/Workleap.OpenApi.MSBuild/OasdiffManager.cs b/src/Workleap.OpenApi.MSBuild/OasdiffManager.cs index feab67a..32d3d99 100644 --- a/src/Workleap.OpenApi.MSBuild/OasdiffManager.cs +++ b/src/Workleap.OpenApi.MSBuild/OasdiffManager.cs @@ -5,7 +5,7 @@ namespace Workleap.OpenApi.MSBuild; internal sealed class OasdiffManager : IOasdiffManager { - private const string OasdiffVersion = "1.9.6"; + private const string OasdiffVersion = "1.10.11"; private const string OasdiffDownloadUrlFormat = "https://github.com/Tufin/oasdiff/releases/download/v{0}/{1}"; private readonly ILoggerWrapper _loggerWrapper; @@ -59,9 +59,19 @@ public async Task RunOasdiffAsync(IReadOnlyCollection openApiSpecFiles, this._loggerWrapper.LogMessage("- Specification file path: {0}", MessageImportance.High, baseSpecFile); this._loggerWrapper.LogMessage("- Specification generated from code path: {0} \n", MessageImportance.High, generatedSpecFilePath); - var result = await this._processWrapper.RunProcessAsync(oasdiffExecutePath, new[] { "diff", baseSpecFile, generatedSpecFilePath, "--exclude-elements", "description,examples,title,summary", "-f", "text", "-o" }, cancellationToken); - this._loggerWrapper.LogMessage(result.StandardOutput, MessageImportance.High); - if (!string.IsNullOrEmpty(result.StandardError)) + var result = await this._processWrapper.RunProcessAsync(oasdiffExecutePath, new[] { "diff", baseSpecFile, generatedSpecFilePath, "--exclude-elements", "description,examples,title,summary", "-o" }, cancellationToken); + if (string.IsNullOrEmpty(result.StandardError)) + { + var isChangesDetected = result.ExitCode != 0; + this._loggerWrapper.LogMessage("Oasdiff returned: {0}", MessageImportance.Normal, result.ExitCode); + if (isChangesDetected) + { + this._loggerWrapper.LogWarning($"Your web API does not respect the following OpenAPI specification: {fileName}. Please review the logs below for details."); + } + + this._loggerWrapper.LogMessage(result.StandardOutput, MessageImportance.High); + } + else { this._loggerWrapper.LogWarning(result.StandardError); } diff --git a/src/Workleap.OpenApi.MSBuild/ValidateOpenApiTask.cs b/src/Workleap.OpenApi.MSBuild/ValidateOpenApiTask.cs index 1d0dcd5..305227b 100644 --- a/src/Workleap.OpenApi.MSBuild/ValidateOpenApiTask.cs +++ b/src/Workleap.OpenApi.MSBuild/ValidateOpenApiTask.cs @@ -1,4 +1,3 @@ -using System.Diagnostics; using Microsoft.Build.Framework; namespace Workleap.OpenApi.MSBuild; @@ -7,23 +6,23 @@ public sealed class ValidateOpenApiTask : CancelableAsyncTask { private const string CodeFirst = "CodeFirst"; private const string ContractFirst = "ContractFirst"; - + /// - /// 2 supported modes: + /// 2 supported modes: /// - CodeFirst: Generate the OpenAPI specification files from the code /// - ContractFirst: Will use the OpenAPI specification files provided /// [Required] public string OpenApiDevelopmentMode { get; set; } = string.Empty; - /// When Development mode is Contract first, will validate if the specification match the code. + /// When Development mode is Contract first, will validate if the specification match the code. [Required] public bool OpenApiCompareCodeAgainstSpecFile { get; set; } = false; - + /// The path of the ASP.NET Core project startup assembly directory. [Required] public string StartupAssemblyPath { get; set; } = string.Empty; - + /// The path of the ASP.NET Core project being built. [Required] public string OpenApiWebApiAssemblyPath { get; set; } = string.Empty; @@ -44,12 +43,16 @@ public sealed class ValidateOpenApiTask : CancelableAsyncTask [Required] public string[] OpenApiSpecificationFiles { get; set; } = Array.Empty(); + /// If warnings should be logged as errors instead. + public bool OpenApiTreatWarningsAsErrors { get; set; } + protected override async Task ExecuteAsync(CancellationToken cancellationToken) { - this.Log.LogMessage(MessageImportance.Normal, "\n******** Starting {0} ********\n", nameof(ValidateOpenApiTask)); - + var loggerWrapper = new LoggerWrapper(this.Log, this.OpenApiTreatWarningsAsErrors); + + loggerWrapper.LogMessage("\n******** Starting {0} ********\n", MessageImportance.Normal, nameof(ValidateOpenApiTask)); + var reportsPath = Path.Combine(this.OpenApiToolsDirectoryPath, "reports"); - var loggerWrapper = new LoggerWrapper(this.Log); var processWrapper = new ProcessWrapper(this.StartupAssemblyPath); var swaggerManager = new SwaggerManager(loggerWrapper, processWrapper, this.OpenApiToolsDirectoryPath, this.OpenApiWebApiAssemblyPath); @@ -57,22 +60,23 @@ protected override async Task ExecuteAsync(CancellationToken cancellationT var spectralManager = new SpectralManager(loggerWrapper, processWrapper, this.OpenApiToolsDirectoryPath, reportsPath, httpClientWrapper); var oasdiffManager = new OasdiffManager(loggerWrapper, processWrapper, this.OpenApiToolsDirectoryPath, httpClientWrapper); - var specGeneratorManager = new SpecGeneratorManager(loggerWrapper); + var specGeneratorManager = new SpecGeneratorManager(loggerWrapper); var codeFirstProcess = new CodeFirstProcess(loggerWrapper, spectralManager, swaggerManager, specGeneratorManager, oasdiffManager); var contractFirstProcess = new ContractFirstProcess(loggerWrapper, spectralManager, swaggerManager, oasdiffManager); - this.Log.LogMessage(MessageImportance.Normal, "{0} = '{1}'", nameof(this.OpenApiDevelopmentMode), this.OpenApiDevelopmentMode); - this.Log.LogMessage(MessageImportance.Normal, "{0} = '{1}'", nameof(this.OpenApiCompareCodeAgainstSpecFile), this.OpenApiCompareCodeAgainstSpecFile); - this.Log.LogMessage(MessageImportance.Low, "{0} = '{1}'", nameof(this.OpenApiWebApiAssemblyPath), this.OpenApiWebApiAssemblyPath); - this.Log.LogMessage(MessageImportance.Low, "{0} = '{1}'", nameof(this.OpenApiToolsDirectoryPath), this.OpenApiToolsDirectoryPath); - this.Log.LogMessage(MessageImportance.Low, "{0} = '{1}'", nameof(this.OpenApiSpectralRulesetUrl), this.OpenApiSpectralRulesetUrl); - this.Log.LogMessage(MessageImportance.Low, "{0} = '{1}'", nameof(this.OpenApiSwaggerDocumentNames), string.Join(", ", this.OpenApiSwaggerDocumentNames)); - this.Log.LogMessage(MessageImportance.Low, "{0} = '{1}'", nameof(this.OpenApiSpecificationFiles), string.Join(", ", this.OpenApiSpecificationFiles)); + loggerWrapper.LogMessage("{0} = '{1}'", MessageImportance.Normal, nameof(this.OpenApiDevelopmentMode), this.OpenApiDevelopmentMode); + loggerWrapper.LogMessage("{0} = '{1}'", MessageImportance.Normal, nameof(this.OpenApiCompareCodeAgainstSpecFile), this.OpenApiCompareCodeAgainstSpecFile); + loggerWrapper.LogMessage("{0} = '{1}'", MessageImportance.Low, nameof(this.OpenApiTreatWarningsAsErrors), this.OpenApiTreatWarningsAsErrors); + loggerWrapper.LogMessage("{0} = '{1}'", MessageImportance.Low, nameof(this.OpenApiWebApiAssemblyPath), this.OpenApiWebApiAssemblyPath); + loggerWrapper.LogMessage("{0} = '{1}'", MessageImportance.Low, nameof(this.OpenApiToolsDirectoryPath), this.OpenApiToolsDirectoryPath); + loggerWrapper.LogMessage("{0} = '{1}'", MessageImportance.Low, nameof(this.OpenApiSpectralRulesetUrl), this.OpenApiSpectralRulesetUrl); + loggerWrapper.LogMessage("{0} = '{1}'", MessageImportance.Low, nameof(this.OpenApiSwaggerDocumentNames), string.Join(", ", this.OpenApiSwaggerDocumentNames)); + loggerWrapper.LogMessage("{0} = '{1}'", MessageImportance.Low, nameof(this.OpenApiSpecificationFiles), string.Join(", ", this.OpenApiSpecificationFiles)); if (this.OpenApiSpecificationFiles.Length != this.OpenApiSwaggerDocumentNames.Length) { - this.Log.LogWarning("You must provide the same amount of open api specification file names and swagger document file names."); + loggerWrapper.LogWarning("You must provide the same amount of open api specification file names and swagger document file names."); return false; } @@ -85,7 +89,7 @@ protected override async Task ExecuteAsync(CancellationToken cancellationT switch (this.OpenApiDevelopmentMode) { case CodeFirst: - this.Log.LogMessage(MessageImportance.Normal, "\nStarting code first..."); + loggerWrapper.LogMessage("\nStarting code first process...", MessageImportance.Normal); await codeFirstProcess.Execute( this.OpenApiSpecificationFiles, this.OpenApiSwaggerDocumentNames, @@ -93,9 +97,9 @@ await codeFirstProcess.Execute( this.OpenApiCompareCodeAgainstSpecFile ? CodeFirstProcess.CodeFirstMode.SpecComparison : CodeFirstProcess.CodeFirstMode.SpecGeneration, cancellationToken); break; - + case ContractFirst: - this.Log.LogMessage(MessageImportance.Normal, "\nStarting contract first..."); + loggerWrapper.LogMessage("\nStarting contract first process...", MessageImportance.Normal); var isSuccess = await contractFirstProcess.Execute( this.OpenApiSpecificationFiles, this.OpenApiToolsDirectoryPath, @@ -110,15 +114,15 @@ await codeFirstProcess.Execute( } break; - + default: - this.Log.LogError("Invalid value of '{0}' for {1}. Allowed values are '{2}' or '{3}'", this.OpenApiDevelopmentMode, nameof(ValidateOpenApiTask.OpenApiDevelopmentMode), ContractFirst, CodeFirst); + loggerWrapper.LogError("Invalid value of '{0}' for {1}. Allowed values are '{2}' or '{3}'", this.OpenApiDevelopmentMode, nameof(this.OpenApiDevelopmentMode), ContractFirst, CodeFirst); return false; } } catch (OpenApiTaskFailedException e) { - this.Log.LogWarning("An error occurred while validating the OpenAPI specification: {0}", e.Message); + loggerWrapper.LogWarning("An error occurred while validating the OpenAPI specification: {0}", e.Message); } return true; diff --git a/src/Workleap.OpenApi.MSBuild/msbuild/tools/Workleap.OpenApi.MSBuild.targets b/src/Workleap.OpenApi.MSBuild/msbuild/tools/Workleap.OpenApi.MSBuild.targets index f3a7d7c..e44d814 100644 --- a/src/Workleap.OpenApi.MSBuild/msbuild/tools/Workleap.OpenApi.MSBuild.targets +++ b/src/Workleap.OpenApi.MSBuild/msbuild/tools/Workleap.OpenApi.MSBuild.targets @@ -38,6 +38,10 @@ v1 + + + $(TreatWarningsAsErrors) + false @@ -64,6 +68,7 @@ OpenApiSpectralRulesetUrl="$(OpenApiSpectralRulesetUrl)" OpenApiSwaggerDocumentNames="$(OpenApiSwaggerDocumentNames)" OpenApiSpecificationFiles="$(OpenApiSpecificationFiles)" + OpenApiTreatWarningsAsErrors="$(OpenApiTreatWarningsAsErrors)" /> \ No newline at end of file diff --git a/src/tests/WebApi.MsBuild.SystemTest.CodeFirst/WebApi.MsBuild.SystemTest.CodeFirst.csproj b/src/tests/WebApi.MsBuild.SystemTest.CodeFirst/WebApi.MsBuild.SystemTest.CodeFirst.csproj index 06744ba..5d9606a 100644 --- a/src/tests/WebApi.MsBuild.SystemTest.CodeFirst/WebApi.MsBuild.SystemTest.CodeFirst.csproj +++ b/src/tests/WebApi.MsBuild.SystemTest.CodeFirst/WebApi.MsBuild.SystemTest.CodeFirst.csproj @@ -11,6 +11,7 @@ CodeFirst v1;v1-management + true diff --git a/src/tests/WebApi.MsBuild.SystemTest.ContractFirst/WebApi.MsBuild.SystemTest.ContractFirst.csproj b/src/tests/WebApi.MsBuild.SystemTest.ContractFirst/WebApi.MsBuild.SystemTest.ContractFirst.csproj index ea442e0..0a51f05 100644 --- a/src/tests/WebApi.MsBuild.SystemTest.ContractFirst/WebApi.MsBuild.SystemTest.ContractFirst.csproj +++ b/src/tests/WebApi.MsBuild.SystemTest.ContractFirst/WebApi.MsBuild.SystemTest.ContractFirst.csproj @@ -14,6 +14,7 @@ true v1;v1-management ./custom.spectral.yaml + true diff --git a/src/tests/WebApi.MsBuild.SystemTest.OasDiffError/Controllers/WeatherForecastController.cs b/src/tests/WebApi.MsBuild.SystemTest.OasDiffError/Controllers/WeatherForecastController.cs new file mode 100644 index 0000000..3971198 --- /dev/null +++ b/src/tests/WebApi.MsBuild.SystemTest.OasDiffError/Controllers/WeatherForecastController.cs @@ -0,0 +1,36 @@ +using Microsoft.AspNetCore.Mvc; + +namespace WebApi.MsBuild.SystemTest.Controllers; + +[ApiController] +[Route("[controller]")] +[Produces("application/json")] +[ApiExplorerSettings(GroupName = "v1")] +public class WeatherForecastController : ControllerBase +{ + private static readonly string[] Summaries = new[] + { + "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching", + }; + + private readonly ILogger _logger; + + public WeatherForecastController(ILogger logger) + { + this._logger = logger; + } + + [HttpGet(Name = "GetWeatherForecast")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status403Forbidden)] + public IEnumerable Get() + { + return Enumerable.Range(1, 5).Select(index => new WeatherForecast + { + Date = DateTime.Now.AddDays(index), + TemperatureC = Random.Shared.Next(-20, 55), + Summary = Summaries[Random.Shared.Next(Summaries.Length)], + }) + .ToArray(); + } +} \ No newline at end of file diff --git a/src/tests/WebApi.MsBuild.SystemTest.OasDiffError/Controllers/WeatherManagementController.cs b/src/tests/WebApi.MsBuild.SystemTest.OasDiffError/Controllers/WeatherManagementController.cs new file mode 100644 index 0000000..8b4d9c0 --- /dev/null +++ b/src/tests/WebApi.MsBuild.SystemTest.OasDiffError/Controllers/WeatherManagementController.cs @@ -0,0 +1,30 @@ +using Microsoft.AspNetCore.Mvc; + +namespace WebApi.MsBuild.SystemTest.Controllers; + +[ApiController] +[Route("[controller]")] +[Produces("application/json")] +[ApiExplorerSettings(GroupName = "v1-management")] +public class WeatherManagementController : ControllerBase +{ + private static readonly string[] Sources = + { + "Accuweather", "AerisWeather", "Foreca", "Open Weathermap", "National Oceanic and Atmospheric Administration", + }; + + private readonly ILogger _logger; + + public WeatherManagementController(ILogger logger) + { + this._logger = logger; + } + + [HttpGet(Name = "GetWeatherSources")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status403Forbidden)] + public IEnumerable Get() + { + return Sources; + } +} \ No newline at end of file diff --git a/src/tests/WebApi.MsBuild.SystemTest.OasDiffError/Program.cs b/src/tests/WebApi.MsBuild.SystemTest.OasDiffError/Program.cs new file mode 100644 index 0000000..0dd2a6c --- /dev/null +++ b/src/tests/WebApi.MsBuild.SystemTest.OasDiffError/Program.cs @@ -0,0 +1,28 @@ +using Microsoft.OpenApi.Models; + +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddControllers(); +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(c => +{ + c.SwaggerDoc("v1", new OpenApiInfo { Title = "V1 API General", Version = "v1" }); + c.SwaggerDoc("v1-management", new OpenApiInfo { Title = "V1 API management", Version = "v1-management" }); +}); + +var app = builder.Build(); + +app.UseSwagger(); +app.UseSwaggerUI(options => +{ + options.SwaggerEndpoint("v1/swagger.json", "v1"); + options.SwaggerEndpoint("v1-management/swagger.json", "v1-management"); +}); + +app.UseHttpsRedirection(); + +app.UseAuthorization(); + +app.MapControllers(); + +app.Run(); \ No newline at end of file diff --git a/src/tests/WebApi.MsBuild.SystemTest.OasDiffError/Properties/launchSettings.json b/src/tests/WebApi.MsBuild.SystemTest.OasDiffError/Properties/launchSettings.json new file mode 100644 index 0000000..9ecf63d --- /dev/null +++ b/src/tests/WebApi.MsBuild.SystemTest.OasDiffError/Properties/launchSettings.json @@ -0,0 +1,31 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:65488", + "sslPort": 44333 + } + }, + "profiles": { + "WebApi.SystemTest": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:7045;http://localhost:5245", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/tests/WebApi.MsBuild.SystemTest.OasDiffError/WeatherForecast.cs b/src/tests/WebApi.MsBuild.SystemTest.OasDiffError/WeatherForecast.cs new file mode 100644 index 0000000..ae21257 --- /dev/null +++ b/src/tests/WebApi.MsBuild.SystemTest.OasDiffError/WeatherForecast.cs @@ -0,0 +1,12 @@ +namespace WebApi.MsBuild.SystemTest; + +public class WeatherForecast +{ + public DateTime Date { get; set; } + + public int TemperatureC { get; init; } + + public int TemperatureF => 32 + (int)(this.TemperatureC / 0.5556); + + public string? Summary { get; set; } +} \ No newline at end of file diff --git a/src/tests/WebApi.MsBuild.SystemTest.OasDiffError/WebApi.MsBuild.SystemTest.OasDiffError.csproj b/src/tests/WebApi.MsBuild.SystemTest.OasDiffError/WebApi.MsBuild.SystemTest.OasDiffError.csproj new file mode 100644 index 0000000..cae1b61 --- /dev/null +++ b/src/tests/WebApi.MsBuild.SystemTest.OasDiffError/WebApi.MsBuild.SystemTest.OasDiffError.csproj @@ -0,0 +1,23 @@ + + + + net8.0 + enable + enable + false + false + WebApi.MsBuild.SystemTest + + + + CodeFirst + true + v1;v1-management + true + + + + + + + diff --git a/src/tests/WebApi.MsBuild.SystemTest.OasDiffError/appsettings.Development.json b/src/tests/WebApi.MsBuild.SystemTest.OasDiffError/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/src/tests/WebApi.MsBuild.SystemTest.OasDiffError/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/src/tests/WebApi.MsBuild.SystemTest.OasDiffError/appsettings.json b/src/tests/WebApi.MsBuild.SystemTest.OasDiffError/appsettings.json new file mode 100644 index 0000000..10f68b8 --- /dev/null +++ b/src/tests/WebApi.MsBuild.SystemTest.OasDiffError/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/src/tests/WebApi.MsBuild.SystemTest.OasDiffError/openapi-v1-management.yaml b/src/tests/WebApi.MsBuild.SystemTest.OasDiffError/openapi-v1-management.yaml new file mode 100644 index 0000000..eab7525 --- /dev/null +++ b/src/tests/WebApi.MsBuild.SystemTest.OasDiffError/openapi-v1-management.yaml @@ -0,0 +1,47 @@ +openapi: 3.0.1 +info: + title: V1 API management + version: v1-management +paths: + /WeatherManagement: + get: + tags: + - WeatherManagement + operationId: GetWeatherSources + responses: + '200': + description: Success + content: + application/json: + schema: + type: array + items: + type: string + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ProblemDetails' +components: + schemas: + ProblemDetails: + type: object + properties: + type: + type: string + nullable: true + title: + type: string + nullable: true + status: + type: integer + format: int32 + nullable: true + detail: + type: string + nullable: true + instance: + type: string + nullable: true + additionalProperties: {} \ No newline at end of file diff --git a/src/tests/WebApi.MsBuild.SystemTest.OasDiffError/openapi-v1.yaml b/src/tests/WebApi.MsBuild.SystemTest.OasDiffError/openapi-v1.yaml new file mode 100644 index 0000000..1eb2d0e --- /dev/null +++ b/src/tests/WebApi.MsBuild.SystemTest.OasDiffError/openapi-v1.yaml @@ -0,0 +1,64 @@ +openapi: 2.0.1 +info: + title: V1 API General + version: v1 +paths: + /WeatherForecast2: + get: + tags: + - WeatherForecast + operationId: GetWeatherForecast + responses: + '200': + description: Success + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/WeatherForecast' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ProblemDetails' +components: + schemas: + ProblemDetails: + type: object + properties: + type: + type: string + nullable: true + title: + type: string + nullable: true + status: + type: integer + format: int32 + nullable: true + detail: + type: string + nullable: true + instance: + type: string + nullable: true + additionalProperties: {} + WeatherForecast: + type: object + properties: + date: + type: string + format: date-time + temperatureC: + type: integer + format: int32 + temperatureF: + type: integer + format: int32 + readOnly: true + summary: + type: string + nullable: true + additionalProperties: false \ No newline at end of file diff --git a/src/tests/WebApi.MsBuild.SystemTest.SpectralError/Controllers/WeatherForecastController.cs b/src/tests/WebApi.MsBuild.SystemTest.SpectralError/Controllers/WeatherForecastController.cs new file mode 100644 index 0000000..3971198 --- /dev/null +++ b/src/tests/WebApi.MsBuild.SystemTest.SpectralError/Controllers/WeatherForecastController.cs @@ -0,0 +1,36 @@ +using Microsoft.AspNetCore.Mvc; + +namespace WebApi.MsBuild.SystemTest.Controllers; + +[ApiController] +[Route("[controller]")] +[Produces("application/json")] +[ApiExplorerSettings(GroupName = "v1")] +public class WeatherForecastController : ControllerBase +{ + private static readonly string[] Summaries = new[] + { + "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching", + }; + + private readonly ILogger _logger; + + public WeatherForecastController(ILogger logger) + { + this._logger = logger; + } + + [HttpGet(Name = "GetWeatherForecast")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status403Forbidden)] + public IEnumerable Get() + { + return Enumerable.Range(1, 5).Select(index => new WeatherForecast + { + Date = DateTime.Now.AddDays(index), + TemperatureC = Random.Shared.Next(-20, 55), + Summary = Summaries[Random.Shared.Next(Summaries.Length)], + }) + .ToArray(); + } +} \ No newline at end of file diff --git a/src/tests/WebApi.MsBuild.SystemTest.SpectralError/Controllers/WeatherManagementController.cs b/src/tests/WebApi.MsBuild.SystemTest.SpectralError/Controllers/WeatherManagementController.cs new file mode 100644 index 0000000..8b4d9c0 --- /dev/null +++ b/src/tests/WebApi.MsBuild.SystemTest.SpectralError/Controllers/WeatherManagementController.cs @@ -0,0 +1,30 @@ +using Microsoft.AspNetCore.Mvc; + +namespace WebApi.MsBuild.SystemTest.Controllers; + +[ApiController] +[Route("[controller]")] +[Produces("application/json")] +[ApiExplorerSettings(GroupName = "v1-management")] +public class WeatherManagementController : ControllerBase +{ + private static readonly string[] Sources = + { + "Accuweather", "AerisWeather", "Foreca", "Open Weathermap", "National Oceanic and Atmospheric Administration", + }; + + private readonly ILogger _logger; + + public WeatherManagementController(ILogger logger) + { + this._logger = logger; + } + + [HttpGet(Name = "GetWeatherSources")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status403Forbidden)] + public IEnumerable Get() + { + return Sources; + } +} \ No newline at end of file diff --git a/src/tests/WebApi.MsBuild.SystemTest.SpectralError/Program.cs b/src/tests/WebApi.MsBuild.SystemTest.SpectralError/Program.cs new file mode 100644 index 0000000..0dd2a6c --- /dev/null +++ b/src/tests/WebApi.MsBuild.SystemTest.SpectralError/Program.cs @@ -0,0 +1,28 @@ +using Microsoft.OpenApi.Models; + +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddControllers(); +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(c => +{ + c.SwaggerDoc("v1", new OpenApiInfo { Title = "V1 API General", Version = "v1" }); + c.SwaggerDoc("v1-management", new OpenApiInfo { Title = "V1 API management", Version = "v1-management" }); +}); + +var app = builder.Build(); + +app.UseSwagger(); +app.UseSwaggerUI(options => +{ + options.SwaggerEndpoint("v1/swagger.json", "v1"); + options.SwaggerEndpoint("v1-management/swagger.json", "v1-management"); +}); + +app.UseHttpsRedirection(); + +app.UseAuthorization(); + +app.MapControllers(); + +app.Run(); \ No newline at end of file diff --git a/src/tests/WebApi.MsBuild.SystemTest.SpectralError/Properties/launchSettings.json b/src/tests/WebApi.MsBuild.SystemTest.SpectralError/Properties/launchSettings.json new file mode 100644 index 0000000..9ecf63d --- /dev/null +++ b/src/tests/WebApi.MsBuild.SystemTest.SpectralError/Properties/launchSettings.json @@ -0,0 +1,31 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:65488", + "sslPort": 44333 + } + }, + "profiles": { + "WebApi.SystemTest": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:7045;http://localhost:5245", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/tests/WebApi.MsBuild.SystemTest.SpectralError/WeatherForecast.cs b/src/tests/WebApi.MsBuild.SystemTest.SpectralError/WeatherForecast.cs new file mode 100644 index 0000000..ae21257 --- /dev/null +++ b/src/tests/WebApi.MsBuild.SystemTest.SpectralError/WeatherForecast.cs @@ -0,0 +1,12 @@ +namespace WebApi.MsBuild.SystemTest; + +public class WeatherForecast +{ + public DateTime Date { get; set; } + + public int TemperatureC { get; init; } + + public int TemperatureF => 32 + (int)(this.TemperatureC / 0.5556); + + public string? Summary { get; set; } +} \ No newline at end of file diff --git a/src/tests/WebApi.MsBuild.SystemTest.SpectralError/WebApi.MsBuild.SystemTest.SpectralError.csproj b/src/tests/WebApi.MsBuild.SystemTest.SpectralError/WebApi.MsBuild.SystemTest.SpectralError.csproj new file mode 100644 index 0000000..3c01dc2 --- /dev/null +++ b/src/tests/WebApi.MsBuild.SystemTest.SpectralError/WebApi.MsBuild.SystemTest.SpectralError.csproj @@ -0,0 +1,23 @@ + + + + net8.0 + enable + enable + false + false + WebApi.MsBuild.SystemTest + + + + CodeFirst + true + v1;v1-management + true + + + + + + + diff --git a/src/tests/WebApi.MsBuild.SystemTest.SpectralError/appsettings.Development.json b/src/tests/WebApi.MsBuild.SystemTest.SpectralError/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/src/tests/WebApi.MsBuild.SystemTest.SpectralError/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/src/tests/WebApi.MsBuild.SystemTest.SpectralError/appsettings.json b/src/tests/WebApi.MsBuild.SystemTest.SpectralError/appsettings.json new file mode 100644 index 0000000..10f68b8 --- /dev/null +++ b/src/tests/WebApi.MsBuild.SystemTest.SpectralError/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/src/tests/WebApi.MsBuild.SystemTest.SpectralError/openapi-v1-management.yaml b/src/tests/WebApi.MsBuild.SystemTest.SpectralError/openapi-v1-management.yaml new file mode 100644 index 0000000..eab7525 --- /dev/null +++ b/src/tests/WebApi.MsBuild.SystemTest.SpectralError/openapi-v1-management.yaml @@ -0,0 +1,47 @@ +openapi: 3.0.1 +info: + title: V1 API management + version: v1-management +paths: + /WeatherManagement: + get: + tags: + - WeatherManagement + operationId: GetWeatherSources + responses: + '200': + description: Success + content: + application/json: + schema: + type: array + items: + type: string + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ProblemDetails' +components: + schemas: + ProblemDetails: + type: object + properties: + type: + type: string + nullable: true + title: + type: string + nullable: true + status: + type: integer + format: int32 + nullable: true + detail: + type: string + nullable: true + instance: + type: string + nullable: true + additionalProperties: {} \ No newline at end of file diff --git a/src/tests/WebApi.MsBuild.SystemTest.SpectralError/openapi-v1.yaml b/src/tests/WebApi.MsBuild.SystemTest.SpectralError/openapi-v1.yaml new file mode 100644 index 0000000..dd0a058 --- /dev/null +++ b/src/tests/WebApi.MsBuild.SystemTest.SpectralError/openapi-v1.yaml @@ -0,0 +1,64 @@ +openapi: 3.0.1 +info: + title: V1 API General + version: v1 +paths: + /WeatherForecast2: + get: + tags: + - WeatherForecast + operationId: GetWeatherForecast + responses: + '200': + description: Success + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/WeatherForecast' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ProblemDetails' +components: + schemas: + ProblemDetails: + type: object + properties: + type: + type: string + nullable: true + title: + type: string + nullable: true + status: + type: integer + format: int32 + nullable: true + detail: + type: string + nullable: true + instance: + type: string + nullable: true + additionalProperties: {} + WeatherForecast: + type: object + properties: + date: + type: string + format: date-time + temperatureC: + type: integer + format: int32 + temperatureF: + type: integer + format: int32 + readOnly: true + summary: + type: string + nullable: true + additionalProperties: false \ No newline at end of file