-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Feature/idp 823 explicit contract first flow (#21)
* [IDP-823] Add parameter to control Contract First vs Code first and opt-in code generation for validation * Add limitation of running system test locally and workaround --------- Co-authored-by: Mathieu Gamache <[email protected]> Co-authored-by: Anthony Simmon <[email protected]>
- Loading branch information
1 parent
7f24464
commit 94ab454
Showing
29 changed files
with
550 additions
and
47 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,36 @@ | ||
# Workleap.OpenApi.MSBuild | ||
|
||
Validates at build time that the OpenAPI specification files extracted from the ASP.NET Core Web API being built conform to Workleap API guidelines. | ||
|
||
Depending if the user chose the Contract-First or Code-First development mode this MSBuild task will: | ||
|
||
- Install tools: [OasDiff](https://github.com/Tufin/oasdiff), [Spectral](https://github.com/stoplightio/spectral), [SwashbuckleCLI](https://github.com/domaindrivendev/Swashbuckle.AspNetCore?tab=readme-ov-file#swashbuckleaspnetcorecli) | ||
- Generate the OpenAPI specification file from the associated Web API | ||
- Validate [Workleap Spectral rules](https://github.com/gsoft-inc/wl-api-guidelines/blob/main/.spectral.yaml) | ||
- Compare the given OpenAPI specification file with the generated one | ||
|
||
## How it works | ||
|
||
[Official Documentation](https://learn.microsoft.com/en-us/visualstudio/msbuild/tutorial-custom-task-code-generation?view=vs-2022#include-msbuild-properties-and-targets-in-a-package) | ||
|
||
For the TLDR version: | ||
|
||
- The entry point is `ValidateOpenApiTask.ExecuteAsync()` and will be executed after the referencing project is built. This is defined in `./src/Workleap.OpenApi.MSBuild/msbuild/tools/Workleap.OpenApi.MSBuild.targets` as a `UsingTask.TaskName` | ||
- The default value are defined in the property group on the target `ValidateOpenApi` in this file `./src/Workleap.OpenApi.MSBuild/msbuild/tools/Workleap.OpenApi.MSBuild.targets` | ||
|
||
## How to test locally | ||
|
||
### How debug the project | ||
|
||
Since it's a MSBuild task named `ValidateOpenApi` you can run it this command: `msbuild /t:ValidateOpenApi`. `./src/WebApiDebugger` already have a `launchSettings.json` named `ValidateOpenApi` that you can run in debug. | ||
|
||
### With the system test | ||
|
||
This command `./Run-SystemTest.ps1` will: | ||
|
||
1. Pack the MSBuild library | ||
2. Build the WebApi.MsBuild.SystemTest and inject the previously packed library | ||
|
||
Be careful since it will update the project dependencies to use a local version: do not commit this. | ||
|
||
Also if you run it multiple time on the same branch you need to clear the local cache `%UserProfile%\.nuget\packages\workleap.openapi.msbuild` since the name won't change. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
namespace Workleap.OpenApi.MSBuild; | ||
|
||
/// <summary> | ||
/// For a Code First approach it will: | ||
/// 1. (if not disabled) Generate the OpenAPI specification files from the code | ||
/// 2. Validate the OpenAPI specification files base on spectral rules | ||
/// </summary> | ||
internal class CodeFirstProcess | ||
{ | ||
private const string DisableSpecGenEnvVarName = "WL_DISABLE_SPECGEN"; | ||
|
||
private readonly SpectralManager _spectralManager; | ||
private readonly SwaggerManager _swaggerManager; | ||
|
||
internal CodeFirstProcess(SpectralManager spectralManager, SwaggerManager swaggerManager) | ||
{ | ||
this._spectralManager = spectralManager; | ||
this._swaggerManager = swaggerManager; | ||
} | ||
|
||
internal async Task Execute( | ||
string[] openApiSpecificationFiles, | ||
string[] openApiSwaggerDocumentNames, | ||
string openApiSpectralRulesetUrl, | ||
CancellationToken cancellationToken) | ||
{ | ||
var isGenerationEnabled = string.Equals(Environment.GetEnvironmentVariable(DisableSpecGenEnvVarName), "true", StringComparison.OrdinalIgnoreCase); | ||
|
||
await this.InstallDependencies(isGenerationEnabled, cancellationToken); | ||
|
||
if (isGenerationEnabled) | ||
{ | ||
var generateOpenApiDocsPath = (await this._swaggerManager.RunSwaggerAsync(openApiSwaggerDocumentNames, cancellationToken)).ToList(); | ||
} | ||
|
||
await this._spectralManager.RunSpectralAsync(openApiSpecificationFiles, openApiSpectralRulesetUrl, cancellationToken); | ||
} | ||
|
||
private async Task InstallDependencies( | ||
bool isGenerationEnable, | ||
CancellationToken cancellationToken) | ||
{ | ||
var installationTasks = new List<Task>(); | ||
installationTasks.Add(this._spectralManager.InstallSpectralAsync(cancellationToken)); | ||
|
||
if (!isGenerationEnable) | ||
{ | ||
installationTasks.Add(this._swaggerManager.InstallSwaggerCliAsync(cancellationToken)); | ||
} | ||
|
||
await Task.WhenAll(installationTasks); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
namespace Workleap.OpenApi.MSBuild; | ||
|
||
/// <summary> | ||
/// For a Contract First approach it will: | ||
/// 1. Validate the OpenAPI specification files base on spectral rules | ||
/// 2. If <see cref="CompareCodeAgainstSpecFile"/> is enabled, will generate the OpenAPI specification files from the code and validate if it match the provided specifications. | ||
/// </summary> | ||
internal class ContractFirstProcess | ||
{ | ||
private readonly ILoggerWrapper _loggerWrapper; | ||
private readonly SpectralManager _spectralManager; | ||
private readonly SwaggerManager _swaggerManager; | ||
private readonly OasdiffManager _oasdiffManager; | ||
|
||
internal ContractFirstProcess(ILoggerWrapper loggerWrapper, SpectralManager spectralManager, SwaggerManager swaggerManager, OasdiffManager oasdiffManager) | ||
{ | ||
this._loggerWrapper = loggerWrapper; | ||
this._spectralManager = spectralManager; | ||
this._swaggerManager = swaggerManager; | ||
this._oasdiffManager = oasdiffManager; | ||
} | ||
|
||
internal enum CompareCodeAgainstSpecFile | ||
{ | ||
Disabled, | ||
Enabled, | ||
} | ||
|
||
internal async Task<bool> Execute( | ||
string[] openApiSpecificationFiles, | ||
string openApiToolsDirectoryPath, | ||
string[] openApiSwaggerDocumentNames, | ||
string openApiSpectralRulesetUrl, | ||
CompareCodeAgainstSpecFile compareCodeAgainstSpecFile, | ||
CancellationToken cancellationToken) | ||
{ | ||
if (!this.CheckIfBaseSpecExists(openApiSpecificationFiles, openApiToolsDirectoryPath)) | ||
{ | ||
return false; | ||
} | ||
|
||
await this.InstallDependencies(compareCodeAgainstSpecFile, cancellationToken); | ||
|
||
if (compareCodeAgainstSpecFile == CompareCodeAgainstSpecFile.Enabled) | ||
{ | ||
var generateOpenApiDocsPath = (await this._swaggerManager.RunSwaggerAsync(openApiSwaggerDocumentNames, cancellationToken)).ToList(); | ||
await this._oasdiffManager.RunOasdiffAsync(openApiSpecificationFiles, generateOpenApiDocsPath, cancellationToken); | ||
} | ||
|
||
await this._spectralManager.RunSpectralAsync(openApiSpecificationFiles, openApiSpectralRulesetUrl, cancellationToken); | ||
|
||
return true; | ||
} | ||
|
||
private bool CheckIfBaseSpecExists( | ||
string[] openApiSpecificationFiles, | ||
string openApiToolsDirectoryPath) | ||
{ | ||
foreach (var file in openApiSpecificationFiles) | ||
{ | ||
if (File.Exists(file)) | ||
{ | ||
continue; | ||
} | ||
|
||
this._loggerWrapper.LogWarning( | ||
"The file '{0}' does not exist. If you are running this for the first time, we have generated specification here '{1}' which can be used as base specification. " + | ||
"Please copy specification file(s) to your project directory and rebuild.", | ||
file, | ||
openApiToolsDirectoryPath); | ||
|
||
return false; | ||
} | ||
|
||
return true; | ||
} | ||
|
||
private async Task InstallDependencies( | ||
CompareCodeAgainstSpecFile compareCodeAgainstSpecFile, | ||
CancellationToken cancellationToken) | ||
{ | ||
var installationTasks = new List<Task>(); | ||
installationTasks.Add(this._spectralManager.InstallSpectralAsync(cancellationToken)); | ||
|
||
if (compareCodeAgainstSpecFile == CompareCodeAgainstSpecFile.Enabled) | ||
{ | ||
installationTasks.Add(this._swaggerManager.InstallSwaggerCliAsync(cancellationToken)); | ||
installationTasks.Add(this._oasdiffManager.InstallOasdiffAsync(cancellationToken)); | ||
} | ||
|
||
await Task.WhenAll(installationTasks); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.