diff --git a/.azure-pipelines/ci-build.yml b/.azure-pipelines/ci-build.yml
index 84b98fbed..e343ee342 100644
--- a/.azure-pipelines/ci-build.yml
+++ b/.azure-pipelines/ci-build.yml
@@ -21,7 +21,7 @@ pool:
variables:
buildPlatform: 'Any CPU'
buildConfiguration: 'Release'
- ProductBinPath: '$(Build.SourcesDirectory)\src\Microsoft.OpenApi\bin\$(BuildConfiguration)'
+ ProductBinPath: '$(Build.SourcesDirectory)\src\Microsoft.OpenApi\bin\$(BuildConfiguration)'
stages:
@@ -31,22 +31,22 @@ stages:
- job: build
steps:
- task: UseDotNet@2
- displayName: 'Use .NET 2' # needed for ESRP signing
+ displayName: 'Use .NET 6' # needed for ESRP signing
inputs:
- version: 2.x
+ version: 6.x
- task: UseDotNet@2
displayName: 'Use .NET 8'
inputs:
version: 8.x
- - task: PoliCheck@1
+ - task: PoliCheck@2
displayName: 'Run PoliCheck "/src"'
inputs:
inputType: CmdLine
cmdLineArgs: '/F:$(Build.SourcesDirectory)/src /T:9 /Sev:"1|2" /PE:2 /O:poli_result_src.xml'
- - task: PoliCheck@1
+ - task: PoliCheck@2
displayName: 'Run PoliCheck "/test"'
inputs:
inputType: CmdLine
@@ -75,14 +75,14 @@ stages:
arguments: '--configuration $(BuildConfiguration) --no-build'
# CredScan
- - task: securedevelopmentteam.vss-secure-development-tools.build-task-credscan.CredScan@2
+ - task: securedevelopmentteam.vss-secure-development-tools.build-task-credscan.CredScan@3
displayName: 'Run CredScan - Src'
inputs:
toolMajorVersion: 'V2'
scanFolder: '$(Build.SourcesDirectory)\src'
debugMode: false
- - task: securedevelopmentteam.vss-secure-development-tools.build-task-credscan.CredScan@2
+ - task: securedevelopmentteam.vss-secure-development-tools.build-task-credscan.CredScan@3
displayName: 'Run CredScan - Test'
inputs:
toolMajorVersion: 'V2'
@@ -95,34 +95,38 @@ stages:
FileDirPath: '$(ProductBinPath)'
enabled: false
- - task: BinSkim@3
+ - task: BinSkim@4
displayName: 'Run BinSkim - Product Binaries'
inputs:
InputType: Basic
- AnalyzeTarget: '$(ProductBinPath)\**\Microsoft.OpenApi.dll'
+ AnalyzeTargetGlob: '$(ProductBinPath)\**\Microsoft.OpenApi.dll'
AnalyzeSymPath: '$(ProductBinPath)'
AnalyzeVerbose: true
AnalyzeHashes: true
AnalyzeEnvironment: true
- - task: PublishSecurityAnalysisLogs@2
+ - task: PublishSecurityAnalysisLogs@3
displayName: 'Publish Security Analysis Logs'
inputs:
ArtifactName: SecurityLogs
- - task: PostAnalysis@1
+ - task: PostAnalysis@2
displayName: 'Post Analysis'
inputs:
BinSkim: true
CredScan: true
PoliCheck: true
- - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1
+ - task: EsrpCodeSigning@2
displayName: 'ESRP CodeSigning'
inputs:
ConnectedServiceName: 'microsoftgraph ESRP CodeSign DLL and NuGet (AKV)'
FolderPath: src
signConfigType: inlineSignParams
+ UseMinimatch: true
+ Pattern: |
+ **\*.exe
+ **\*.dll
inlineOperation: |
[
{
@@ -162,26 +166,27 @@ stages:
}
]
SessionTimeout: 20
-
+
# Pack
- pwsh: dotnet pack $(Build.SourcesDirectory)/src/Microsoft.OpenApi/Microsoft.OpenApi.csproj -o $(Build.ArtifactStagingDirectory) --configuration $(BuildConfiguration) --no-build --include-symbols --include-source /p:SymbolPackageFormat=snupkg
displayName: 'pack OpenAPI'
-
+
# Pack
- pwsh: dotnet pack $(Build.SourcesDirectory)/src/Microsoft.OpenApi.Readers/Microsoft.OpenApi.Readers.csproj -o $(Build.ArtifactStagingDirectory) --configuration $(BuildConfiguration) --no-build --include-symbols --include-source /p:SymbolPackageFormat=snupkg
displayName: 'pack Readers'
# Pack
- pwsh: dotnet pack $(Build.SourcesDirectory)/src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj -o $(Build.ArtifactStagingDirectory) --configuration $(BuildConfiguration) --no-build --include-symbols --include-source /p:SymbolPackageFormat=snupkg
- displayName: 'pack Hidi'
-
- - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1
+ displayName: 'pack Hidi'
+
+ - task: EsrpCodeSigning@2
displayName: 'ESRP CodeSigning Nuget Packages'
inputs:
ConnectedServiceName: 'microsoftgraph ESRP CodeSign DLL and NuGet (AKV)'
FolderPath: '$(Build.ArtifactStagingDirectory)'
Pattern: '*.nupkg'
signConfigType: inlineSignParams
+ UseMinimatch: true
inlineOperation: |
[
{
@@ -209,7 +214,7 @@ stages:
$xml = [Xml] (Get-Content .\src\Microsoft.OpenApi.Hidi\Microsoft.OpenApi.Hidi.csproj)
$version = $xml.Project.PropertyGroup.Version
echo $version
- echo "##vso[task.setvariable variable=hidiversion]$version"
+ echo "##vso[task.setvariable variable=hidiversion]$version"
# publish hidi as an .exe
- task: DotNetCoreCLI@2
@@ -219,7 +224,7 @@ stages:
arguments: -c Release --runtime win-x64 /p:PublishSingleFile=true /p:PackAsTool=false --self-contained --output $(Build.ArtifactStagingDirectory)/Microsoft.OpenApi.Hidi-v$(hidiversion)
projects: 'src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj'
publishWebProjects: False
- zipAfterPublish: false
+ zipAfterPublish: false
- task: CopyFiles@2
displayName: Prepare staging folder for upload
@@ -236,7 +241,7 @@ stages:
- task: PublishBuildArtifacts@1
displayName: 'Publish Artifact: Hidi'
- inputs:
+ inputs:
ArtifactName: Microsoft.OpenApi.Hidi-v$(hidiversion)
PathtoPublish: '$(Build.ArtifactStagingDirectory)/Microsoft.OpenApi.Hidi-v$(hidiversion)'
@@ -295,8 +300,8 @@ stages:
{ "label" : "enhancement", "V2-Enhancement", "displayName" : "Enhancements", "state" : "closed" },
{ "label" : "bug", "bug-fix", "displayName" : "Bugs", "state" : "closed" },
{ "label" : "documentation", "doc", "displayName" : "Documentation", "state" : "closed"},
- { "label" : "dependencies", "displayName" : "Package Updates", "state" : "closed" }]'
-
+ { "label" : "dependencies", "displayName" : "Package Updates", "state" : "closed" }]'
+
- deployment: deploy_lib
dependsOn: []
environment: nuget-org
diff --git a/.github/workflows/auto-merge-dependabot.yml b/.github/workflows/auto-merge-dependabot.yml
index 6e5953f56..145487dab 100644
--- a/.github/workflows/auto-merge-dependabot.yml
+++ b/.github/workflows/auto-merge-dependabot.yml
@@ -19,7 +19,7 @@ jobs:
steps:
- name: Dependabot metadata
id: metadata
- uses: dependabot/fetch-metadata@v1.6.0
+ uses: dependabot/fetch-metadata@v2.0.0
with:
github-token: "${{ secrets.GITHUB_TOKEN }}"
diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml
index 76130546e..01861ee2d 100644
--- a/.github/workflows/ci-cd.yml
+++ b/.github/workflows/ci-cd.yml
@@ -2,6 +2,9 @@ name: CI/CD Pipeline
on: [push, pull_request, workflow_dispatch]
+permissions:
+ contents: write
+
jobs:
ci:
name: Continuous Integration
@@ -49,7 +52,7 @@ jobs:
- if: steps.conditionals_handler.outputs.is_default_branch == 'true'
name: Bump GH tag
id: tag_generator
- uses: mathieudutour/github-tag-action@v6.1
+ uses: mathieudutour/github-tag-action@v6.2
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
default_bump: false
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index 0de297dc4..22eb5f8fa 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -7,6 +7,11 @@ on:
schedule:
- cron: '0 8 * * *'
+permissions:
+ contents: read # these permissions are required to run the codeql analysis
+ actions: read
+ security-events: write
+
jobs:
analyze:
name: CodeQL Analysis
@@ -23,7 +28,7 @@ jobs:
- name: Initialize CodeQL
id: init_codeql
- uses: github/codeql-action/init@v2
+ uses: github/codeql-action/init@v3
with:
queries: security-and-quality
@@ -43,6 +48,6 @@ jobs:
- name: Perform CodeQL Analysis
id: analyze_codeql
- uses: github/codeql-action/analyze@v2
+ uses: github/codeql-action/analyze@v3
# Built with ❤ by [Pipeline Foundation](https://pipeline.foundation)
\ No newline at end of file
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index 6241be83e..3895975fc 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -17,7 +17,7 @@ jobs:
- name: Check out the repo
uses: actions/checkout@v4
- name: Login to GitHub package feed
- uses: docker/login-action@v3.0.0
+ uses: docker/login-action@v3.1.0
with:
username: ${{ secrets.ACR_USERNAME }}
password: ${{ secrets.ACR_PASSWORD }}
@@ -30,13 +30,13 @@ jobs:
id: getversion
- name: Push to GitHub Packages - Nightly
if: ${{ github.ref == 'refs/heads/vnext' }}
- uses: docker/build-push-action@v5.1.0
+ uses: docker/build-push-action@v5.3.0
with:
push: true
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:nightly
- name: Push to GitHub Packages - Release
if: ${{ github.ref == 'refs/heads/master' }}
- uses: docker/build-push-action@v5.1.0
+ uses: docker/build-push-action@v5.3.0
with:
push: true
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest,${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.getversion.outputs.version }}
diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml
index 1fec4ad89..de89669f4 100644
--- a/.github/workflows/sonarcloud.yml
+++ b/.github/workflows/sonarcloud.yml
@@ -9,6 +9,11 @@ on:
types: [opened, synchronize, reopened]
paths-ignore: ['.vscode/**']
+
+permissions:
+ contents: read
+ pull-requests: read
+
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
@@ -34,10 +39,6 @@ jobs:
with:
distribution: 'adopt'
java-version: 17
- - name: Setup .NET 5 # At the moment the scanner requires dotnet 5 https://www.nuget.org/packages/dotnet-sonarscanner
- uses: actions/setup-dotnet@v4
- with:
- dotnet-version: 5.0.x
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
@@ -46,14 +47,14 @@ jobs:
with:
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
- name: Cache SonarCloud packages
- uses: actions/cache@v3
+ uses: actions/cache@v4
with:
path: ~/.sonar/cache
key: ${{ runner.os }}-sonar
restore-keys: ${{ runner.os }}-sonar
- name: Cache SonarCloud scanner
id: cache-sonar-scanner
- uses: actions/cache@v3
+ uses: actions/cache@v4
with:
path: ./.sonar/scanner
key: ${{ runner.os }}-sonar-scanner
diff --git a/.vscode/launch.json b/.vscode/launch.json
index acf828db0..c8714e62e 100644
--- a/.vscode/launch.json
+++ b/.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}/src/Microsoft.OpenApi.Hidi/bin/Debug/net7.0/Microsoft.OpenApi.Hidi.dll",
+ "program": "${workspaceFolder}/src/Microsoft.OpenApi.Hidi/bin/Debug/net8.0/Microsoft.OpenApi.Hidi.dll",
"args": ["plugin",
"-m","C:\\Users\\darrmi\\src\\github\\microsoft\\openapi.net\\test\\Microsoft.OpenApi.Hidi.Tests\\UtilityFiles\\exampleapimanifest.json",
"--of","./output"],
@@ -28,7 +28,7 @@
"request": "launch",
"preLaunchTask": "build",
// If you have changed target frameworks, make sure to update the program path.
- "program": "${workspaceFolder}/src/Microsoft.OpenApi.WorkBench/bin/Debug/net7.0-windows/Microsoft.OpenApi.Workbench.exe",
+ "program": "${workspaceFolder}/src/Microsoft.OpenApi.WorkBench/bin/Debug/net8.0-windows/Microsoft.OpenApi.Workbench.exe",
"args": [],
"cwd": "${workspaceFolder}/src/Microsoft.OpenApi.Workbench",
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
diff --git a/Dockerfile b/Dockerfile
index e67c8c389..fd821e3e4 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,4 +1,4 @@
-FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build-env
+FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build-env
WORKDIR /app
COPY ./src ./hidi/src
@@ -7,10 +7,10 @@ COPY ./README.md ./hidi/README.md
WORKDIR /app/hidi
RUN dotnet publish ./src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj -c Release
-FROM mcr.microsoft.com/dotnet/runtime:7.0 AS runtime
+FROM mcr.microsoft.com/dotnet/runtime:8.0-jammy-chiseled AS runtime
WORKDIR /app
-COPY --from=build-env /app/hidi/src/Microsoft.OpenApi.Hidi/bin/Release/net7.0 ./
+COPY --from=build-env /app/hidi/src/Microsoft.OpenApi.Hidi/bin/Release/net8.0 ./
VOLUME /app/output
VOLUME /app/openapi.yml
diff --git a/src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj b/src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj
index 09c79d070..8d548ef67 100644
--- a/src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj
+++ b/src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj
@@ -9,7 +9,7 @@
enable
hidi
./../../artifacts
- 1.3.6
+ 1.4.0
OpenAPI.NET CLI tool for slicing OpenAPI documents
true
@@ -30,12 +30,12 @@
-
+
-
+
diff --git a/src/Microsoft.OpenApi.Hidi/OpenApiService.cs b/src/Microsoft.OpenApi.Hidi/OpenApiService.cs
index be97e8dc3..c4d34d4cf 100644
--- a/src/Microsoft.OpenApi.Hidi/OpenApiService.cs
+++ b/src/Microsoft.OpenApi.Hidi/OpenApiService.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
+// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
using System;
@@ -285,7 +285,7 @@ private static async Task GetOpenApi(HidiOptions options, strin
predicate = OpenApiFilterService.CreatePredicate(tags: filterByTags);
}
- if (requestUrls.Count != 0)
+ if (requestUrls.Count > 0)
{
logger.LogTrace("Creating predicate based on the paths and Http methods defined in the Postman collection.");
predicate = OpenApiFilterService.CreatePredicate(requestUrls: requestUrls, source: document);
diff --git a/src/Microsoft.OpenApi.Hidi/readme.md b/src/Microsoft.OpenApi.Hidi/readme.md
index 111ee704b..8e89e2a87 100644
--- a/src/Microsoft.OpenApi.Hidi/readme.md
+++ b/src/Microsoft.OpenApi.Hidi/readme.md
@@ -68,24 +68,28 @@ Used to convert file formats from JSON to YAML and vice versa and performs slici
This command accepts the following parameters:
• --openapi(-d) - OpenAPI description file path in the local filesystem or a valid URL hosted on a HTTPS server
- • --csdl(-cs) - CSDL file path in the local filesystem or a valid URL hosted on a HTTPS server
- • --csdlfilter(-csf) - a filter parameter that a user can use to select a subset of a large CSDL file. They do so by providing a comma delimited list of EntitySet and Singleton names that appear in the EntityContainer.
- • --output(-o) - Output directory path for the transformed document
- • --clean-ouput(-co) - an optional param that allows a user to overwrite an existing file.
- • --version(-v) - OpenAPI specification version
+ • --csdl(--cs) - CSDL file path in the local filesystem or a valid URL hosted on a HTTPS server
+ • --csdlfilter(--csf) - a filter parameter that a user can use to select a subset of a large CSDL file. They do so by providing a comma delimited list of EntitySet and Singleton names that appear in the EntityContainer.
+ • --output(-o) - Output directory path for the transformed document.
+ • --output-folder(--of) - The output directory path for the generated files.
+ • --clean-ouput(--co) - an optional param that allows a user to overwrite an existing file.
+ • --version(-v) - OpenAPI specification version.
+ • --metadata-version(--mv) - the metadata version to use.
• --format(-f) - File format
- • --loglevel(-ll) - The log level to use when logging messages to the main output
- • --inline(-i) - Inline $ref instances
- • --resolveExternal(-ex) - Resolve external $refs
- • --filterByOperationIds(-op) - Slice document based on OperationId(s) provided. Accepts a comma delimited list of operation ids.
+ • --terse-output(--to) - Produce terse json output
+ • --settings-path(--sp) - The configuration file with CSDL conversion settings.
+ • --loglevel(--ll) - The log level to use when logging messages to the main output
+ • --inline-local - Inline local $ref instances
+ • --inline-external(--ex) - Inline external $refs
+ • --filterByOperationIds(--op) - Slice document based on OperationId(s) provided. Accepts a comma delimited list of operation ids.
• --filterByTags(-t) - Slice document based on tag(s) provided. Accepts a comma delimited list of tags.
• --filterByCollection(-c) - Slices the OpenAPI document based on the Postman Collection file generated by Resource Explorer
- • --filterByManifest (-m) - Slices the OpenAPI document based on the requests defined in the API Manifest file referenced by the provided URI. For API manifests with multiple API Dependenties, use a fragment identifier to select the desired one. e.g ./apimanifest.json#example
+ • --manifest (-m) - Slices the OpenAPI document based on the requests defined in the API Manifest file referenced by the provided URI. For API manifests with multiple API Dependenties, use a fragment identifier to select the desired one. e.g ./apimanifest.json#example
**Examples:**
1. Filtering by OperationIds
- hidi transform -d files\People.yml -f yaml -o files\People.yml -v OpenApi3_0 -op users_UpdateInsights -co
+ hidi transform -d files\People.yml -f yaml -o files\People.yml -v OpenApi3_0 --op users_UpdateInsights --co
2. Filtering by Postman collection
hidi transform --openapi files\People.yml --format yaml --output files\People2.yml --version OpenApi3_0 --filterByCollection Graph-Collection-0017059134807617005.postman_collection.json
@@ -94,7 +98,7 @@ This command accepts the following parameters:
hidi transform --input Files/Todo.xml --output Files/Todo-subset.yml --format yaml --version OpenApi3_0 --filterByOperationIds Todos.Todo.UpdateTodo
4. CSDL Filtering by EntitySets and Singletons
- hidi transform -cs dataverse.csdl --csdlFilter "appointments,opportunities" -o appointmentsAndOpportunities.yaml -ll trace
+ hidi transform --cs dataverse.csdl --csdlFilter "appointments,opportunities" -o appointmentsAndOpportunities.yaml --ll trace
Run transform -h to see all the available usage options.
diff --git a/src/Microsoft.OpenApi.Readers/Microsoft.OpenApi.Readers.csproj b/src/Microsoft.OpenApi.Readers/Microsoft.OpenApi.Readers.csproj
index 0569a7c06..5441dcd61 100644
--- a/src/Microsoft.OpenApi.Readers/Microsoft.OpenApi.Readers.csproj
+++ b/src/Microsoft.OpenApi.Readers/Microsoft.OpenApi.Readers.csproj
@@ -1,9 +1,9 @@
-
+
netstandard2.0
latest
true
- 1.6.11
+ 1.6.14
OpenAPI.NET Readers for JSON and YAML documents
true
@@ -26,7 +26,7 @@
-
+
diff --git a/src/Microsoft.OpenApi.Workbench/Microsoft.OpenApi.Workbench.csproj b/src/Microsoft.OpenApi.Workbench/Microsoft.OpenApi.Workbench.csproj
index 75c80bcac..248c60d21 100644
--- a/src/Microsoft.OpenApi.Workbench/Microsoft.OpenApi.Workbench.csproj
+++ b/src/Microsoft.OpenApi.Workbench/Microsoft.OpenApi.Workbench.csproj
@@ -9,7 +9,7 @@
-
+
diff --git a/src/Microsoft.OpenApi/Microsoft.OpenApi.csproj b/src/Microsoft.OpenApi/Microsoft.OpenApi.csproj
index b5db340ba..fc3540a42 100644
--- a/src/Microsoft.OpenApi/Microsoft.OpenApi.csproj
+++ b/src/Microsoft.OpenApi/Microsoft.OpenApi.csproj
@@ -3,7 +3,7 @@
netstandard2.0
Latest
true
- 1.6.11
+ 1.6.14
.NET models with JSON and YAML writers for OpenAPI specification
true
diff --git a/src/Microsoft.OpenApi/Models/OpenApiConstants.cs b/src/Microsoft.OpenApi/Models/OpenApiConstants.cs
index 107d9cc15..34b546352 100644
--- a/src/Microsoft.OpenApi/Models/OpenApiConstants.cs
+++ b/src/Microsoft.OpenApi/Models/OpenApiConstants.cs
@@ -605,6 +605,11 @@ public static class OpenApiConstants
///
public const string BodyName = "x-bodyName";
+ ///
+ /// Field: Examples Extension
+ ///
+ public const string ExamplesExtension = "x-examples";
+
///
/// Field: version3_0_0
///
diff --git a/src/Microsoft.OpenApi/Models/OpenApiDocument.cs b/src/Microsoft.OpenApi/Models/OpenApiDocument.cs
index 292a9bf9a..42b6734f7 100644
--- a/src/Microsoft.OpenApi/Models/OpenApiDocument.cs
+++ b/src/Microsoft.OpenApi/Models/OpenApiDocument.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
+// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
using System;
diff --git a/src/Microsoft.OpenApi/Models/OpenApiExample.cs b/src/Microsoft.OpenApi/Models/OpenApiExample.cs
index d55c57daa..b32810a64 100644
--- a/src/Microsoft.OpenApi/Models/OpenApiExample.cs
+++ b/src/Microsoft.OpenApi/Models/OpenApiExample.cs
@@ -121,6 +121,16 @@ public virtual void SerializeAsV3WithoutReference(IOpenApiWriter writer)
}
internal void SerializeInternalWithoutReference(IOpenApiWriter writer, OpenApiSpecVersion version)
+ {
+ Serialize(writer, OpenApiSpecVersion.OpenApi3_0);
+ }
+
+ ///
+ /// Writes out existing examples in a mediatype object
+ ///
+ ///
+ ///
+ public void Serialize(IOpenApiWriter writer, OpenApiSpecVersion version)
{
writer.WriteStartObject();
diff --git a/src/Microsoft.OpenApi/Models/OpenApiParameter.cs b/src/Microsoft.OpenApi/Models/OpenApiParameter.cs
index 8c33a4412..048e29cb5 100644
--- a/src/Microsoft.OpenApi/Models/OpenApiParameter.cs
+++ b/src/Microsoft.OpenApi/Models/OpenApiParameter.cs
@@ -1,9 +1,10 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
+// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
using System;
using System.Collections.Generic;
using Json.Schema;
+using System.Linq;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Extensions;
using Microsoft.OpenApi.Helpers;
@@ -389,6 +390,20 @@ public void SerializeAsV2WithoutReference(IOpenApiWriter writer)
}
}
+ //examples
+ if (Examples != null && Examples.Any())
+ {
+ writer.WritePropertyName(OpenApiConstants.ExamplesExtension);
+ writer.WriteStartObject();
+
+ foreach (var example in Examples)
+ {
+ writer.WritePropertyName(example.Key);
+ example.Value.Serialize(writer, OpenApiSpecVersion.OpenApi2_0);
+ }
+ writer.WriteEndObject();
+ }
+
// extensions
writer.WriteExtensions(extensionsClone, OpenApiSpecVersion.OpenApi2_0);
diff --git a/src/Microsoft.OpenApi/Models/OpenApiRequestBody.cs b/src/Microsoft.OpenApi/Models/OpenApiRequestBody.cs
index 2ff1d6fd2..00d50a7be 100644
--- a/src/Microsoft.OpenApi/Models/OpenApiRequestBody.cs
+++ b/src/Microsoft.OpenApi/Models/OpenApiRequestBody.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
+// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
using System;
@@ -100,14 +100,7 @@ private void SerializeInternal(IOpenApiWriter writer, ActionOpenApiRequestBody
public OpenApiRequestBody GetEffective(OpenApiDocument doc)
{
- if (Reference != null)
- {
- return doc.ResolveReferenceTo(Reference);
- }
- else
- {
- return this;
- }
+ return Reference != null ? doc.ResolveReferenceTo(Reference) : this;
}
///
@@ -173,6 +166,7 @@ internal OpenApiBodyParameter ConvertToBodyParameter()
// To allow round-tripping we use an extension to hold the name
Name = "body",
Schema = Content.Values.FirstOrDefault()?.Schema ?? new JsonSchemaBuilder(),
+ Examples = Content.Values.FirstOrDefault()?.Examples,
Required = Required,
Extensions = Extensions.ToDictionary(static k => k.Key, static v => v.Value) // Clone extensions so we can remove the x-bodyName extensions from the output V2 model.
};
@@ -206,6 +200,7 @@ internal IEnumerable ConvertToFormDataParameters()
Description = property.Value.GetDescription(),
Name = property.Key,
Schema = property.Value,
+ Examples = Content.Values.FirstOrDefault()?.Examples,
Required = Content.First().Value.Schema.GetRequired().Contains(property.Key)
};
}
diff --git a/src/Microsoft.OpenApi/Models/OpenApiResponse.cs b/src/Microsoft.OpenApi/Models/OpenApiResponse.cs
index d34e203a9..e0785fe67 100644
--- a/src/Microsoft.OpenApi/Models/OpenApiResponse.cs
+++ b/src/Microsoft.OpenApi/Models/OpenApiResponse.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
+// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
using System;
@@ -188,6 +188,22 @@ public void SerializeAsV2WithoutReference(IOpenApiWriter writer)
writer.WriteEndObject();
}
+ if (Content.Values.Any(m => m.Examples != null && m.Examples.Any()))
+ {
+ writer.WritePropertyName(OpenApiConstants.ExamplesExtension);
+ writer.WriteStartObject();
+
+ foreach (var example in Content
+ .Where(mediaTypePair => mediaTypePair.Value.Examples != null && mediaTypePair.Value.Examples.Any())
+ .SelectMany(mediaTypePair => mediaTypePair.Value.Examples))
+ {
+ writer.WritePropertyName(example.Key);
+ example.Value.Serialize(writer, OpenApiSpecVersion.OpenApi2_0);
+ }
+
+ writer.WriteEndObject();
+ }
+
writer.WriteExtensions(mediatype.Value.Extensions, OpenApiSpecVersion.OpenApi2_0);
foreach (var key in mediatype.Value.Extensions.Keys)
diff --git a/src/Microsoft.OpenApi/Reader/OpenApiModelFactory.cs b/src/Microsoft.OpenApi/Reader/OpenApiModelFactory.cs
index e2ec7bdc9..d81bedabb 100644
--- a/src/Microsoft.OpenApi/Reader/OpenApiModelFactory.cs
+++ b/src/Microsoft.OpenApi/Reader/OpenApiModelFactory.cs
@@ -112,7 +112,7 @@ public static async Task LoadAsync(Stream input, string format, Open
bufferedStream.Position = 0;
}
- using var reader = new StreamReader(bufferedStream);
+ using var reader = new StreamReader(bufferedStream, default, true, -1, settings.LeaveStreamOpen);
return await LoadAsync(reader, format, settings, cancellationToken);
}
diff --git a/src/Microsoft.OpenApi/Reader/V2/OpenApiOperationDeserializer.cs b/src/Microsoft.OpenApi/Reader/V2/OpenApiOperationDeserializer.cs
index ad6033a14..6e8d3d53c 100644
--- a/src/Microsoft.OpenApi/Reader/V2/OpenApiOperationDeserializer.cs
+++ b/src/Microsoft.OpenApi/Reader/V2/OpenApiOperationDeserializer.cs
@@ -199,7 +199,8 @@ internal static OpenApiRequestBody CreateRequestBody(
k => k,
_ => new OpenApiMediaType
{
- Schema = bodyParameter.Schema
+ Schema = bodyParameter.Schema,
+ Examples = bodyParameter.Examples
}),
Extensions = bodyParameter.Extensions
};
diff --git a/src/Microsoft.OpenApi/Reader/V2/OpenApiParameterDeserializer.cs b/src/Microsoft.OpenApi/Reader/V2/OpenApiParameterDeserializer.cs
index 07634ddd4..ca1110761 100644
--- a/src/Microsoft.OpenApi/Reader/V2/OpenApiParameterDeserializer.cs
+++ b/src/Microsoft.OpenApi/Reader/V2/OpenApiParameterDeserializer.cs
@@ -122,12 +122,17 @@ internal static partial class OpenApiV2Deserializer
"schema",
(o, n) => o.Schema = LoadSchema(n)
},
+ {
+ "x-examples",
+ LoadParameterExamplesExtension
+ },
};
private static readonly PatternFieldMap _parameterPatternFields =
new()
{
- {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, LoadExtension(p, n))}
+ {s => s.StartsWith("x-") && !s.Equals(OpenApiConstants.ExamplesExtension, StringComparison.OrdinalIgnoreCase),
+ (o, p, n) => o.AddExtension(p, LoadExtension(p, n))}
};
private static void LoadStyle(OpenApiParameter p, string v)
@@ -158,6 +163,12 @@ private static void LoadStyle(OpenApiParameter p, string v)
return;
}
}
+
+ private static void LoadParameterExamplesExtension(OpenApiParameter parameter, ParseNode node)
+ {
+ var examples = LoadExamplesExtension(node);
+ node.Context.SetTempStorage(TempStorageKeys.Examples, examples, parameter);
+ }
private static JsonSchemaBuilder GetOrCreateParameterSchemaBuilder()
{
@@ -228,6 +239,14 @@ public static OpenApiParameter LoadParameter(ParseNode node, bool loadRequestBod
node.Context.SetTempStorage("schema", null);
}
+ // load examples from storage and add them to the parameter
+ var examples = node.Context.GetFromTempStorage>(TempStorageKeys.Examples, parameter);
+ if (examples != null)
+ {
+ parameter.Examples = examples;
+ node.Context.SetTempStorage("examples", null);
+ }
+
var isBodyOrFormData = (bool)node.Context.GetFromTempStorage
public static ValidationRule SchemaMismatchedDataType =>
- new ValidationRule(
+ new ValidationRule(nameof(SchemaMismatchedDataType),
(context, jsonSchema) =>
{
// default
@@ -79,7 +79,7 @@ public static class JsonSchemaRules
/// Validates Schema Discriminator
///
public static ValidationRule ValidateSchemaDiscriminator =>
- new ValidationRule(
+ new ValidationRule(nameof(ValidateSchemaDiscriminator),
(context, jsonSchema) =>
{
// discriminator
diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiComponentsRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiComponentsRules.cs
index f2f3a649c..93eba5c71 100644
--- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiComponentsRules.cs
+++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiComponentsRules.cs
@@ -24,7 +24,7 @@ public static class OpenApiComponentsRules
/// that MUST use keys that match the regular expression: ^[a-zA-Z0-9\.\-_]+$.
///
public static ValidationRule KeyMustBeRegularExpression =>
- new(
+ new(nameof(KeyMustBeRegularExpression),
(context, components) =>
{
ValidateKeys(context, components.Schemas?.Keys, "schemas");
diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiContactRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiContactRules.cs
index cfa8d9927..e31dc1e07 100644
--- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiContactRules.cs
+++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiContactRules.cs
@@ -17,7 +17,7 @@ public static class OpenApiContactRules
/// Email field MUST be email address.
///
public static ValidationRule EmailMustBeEmailFormat =>
- new(
+ new(nameof(EmailMustBeEmailFormat),
(context, item) =>
{
context.Enter("email");
diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiDocumentRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiDocumentRules.cs
index fdb6f87b7..c98c9a038 100644
--- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiDocumentRules.cs
+++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiDocumentRules.cs
@@ -17,7 +17,7 @@ public static class OpenApiDocumentRules
/// The Info field is required.
///
public static ValidationRule OpenApiDocumentFieldIsMissing =>
- new(
+ new(nameof(OpenApiDocumentFieldIsMissing),
(context, item) =>
{
// info
diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiExtensionRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiExtensionRules.cs
index 5d124e8de..890be82d7 100644
--- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiExtensionRules.cs
+++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiExtensionRules.cs
@@ -17,7 +17,7 @@ public static class OpenApiExtensibleRules
/// Extension name MUST start with "x-".
///
public static ValidationRule ExtensionNameMustStartWithXDash =>
- new(
+ new(nameof(ExtensionNameMustStartWithXDash),
(context, item) =>
{
context.Enter("extensions");
diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiExternalDocsRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiExternalDocsRules.cs
index ff4fde4a2..1754dbee2 100644
--- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiExternalDocsRules.cs
+++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiExternalDocsRules.cs
@@ -17,7 +17,7 @@ public static class OpenApiExternalDocsRules
/// Validate the field is required.
///
public static ValidationRule UrlIsRequired =>
- new(
+ new(nameof(UrlIsRequired),
(context, item) =>
{
// url
diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiHeaderRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiHeaderRules.cs
index 4e07d3ca0..cf983158e 100644
--- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiHeaderRules.cs
+++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiHeaderRules.cs
@@ -16,7 +16,7 @@ public static class OpenApiHeaderRules
/// Validate the data matches with the given data type.
///
public static ValidationRule HeaderMismatchedDataType =>
- new(
+ new(nameof(HeaderMismatchedDataType),
(context, header) =>
{
// example
diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiInfoRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiInfoRules.cs
index 88b534c02..337bf8687 100644
--- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiInfoRules.cs
+++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiInfoRules.cs
@@ -17,7 +17,7 @@ public static class OpenApiInfoRules
/// Validate the field is required.
///
public static ValidationRule InfoRequiredFields =>
- new(
+ new(nameof(InfoRequiredFields),
(context, item) =>
{
// title
diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiLicenseRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiLicenseRules.cs
index edbf19bf5..08f67d209 100644
--- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiLicenseRules.cs
+++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiLicenseRules.cs
@@ -17,7 +17,7 @@ public static class OpenApiLicenseRules
/// REQUIRED.
///
public static ValidationRule LicenseRequiredFields =>
- new(
+ new(nameof(LicenseRequiredFields),
(context, license) =>
{
context.Enter("name");
diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiMediaTypeRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiMediaTypeRules.cs
index 998bd778a..3b56bffd7 100644
--- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiMediaTypeRules.cs
+++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiMediaTypeRules.cs
@@ -24,7 +24,7 @@ public static class OpenApiMediaTypeRules
/// Validate the data matches with the given data type.
///
public static ValidationRule MediaTypeMismatchedDataType =>
- new(
+ new(nameof(MediaTypeMismatchedDataType),
(context, mediaType) =>
{
// example
diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiOAuthFlowRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiOAuthFlowRules.cs
index de31d933d..c6db49d7c 100644
--- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiOAuthFlowRules.cs
+++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiOAuthFlowRules.cs
@@ -17,7 +17,7 @@ public static class OpenApiOAuthFlowRules
/// Validate the field is required.
///
public static ValidationRule OAuthFlowRequiredFields =>
- new(
+ new(nameof(OAuthFlowRequiredFields),
(context, flow) =>
{
// authorizationUrl
diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiParameterRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiParameterRules.cs
index cbeb75e05..512c518ce 100644
--- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiParameterRules.cs
+++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiParameterRules.cs
@@ -17,7 +17,7 @@ public static class OpenApiParameterRules
/// Validate the field is required.
///
public static ValidationRule ParameterRequiredFields =>
- new(
+ new(nameof(ParameterRequiredFields),
(context, item) =>
{
// name
@@ -43,7 +43,7 @@ public static class OpenApiParameterRules
/// Validate the "required" field is true when "in" is path.
///
public static ValidationRule RequiredMustBeTrueWhenInIsPath =>
- new(
+ new(nameof(RequiredMustBeTrueWhenInIsPath),
(context, item) =>
{
// required
@@ -62,7 +62,7 @@ public static class OpenApiParameterRules
/// Validate the data matches with the given data type.
///
public static ValidationRule ParameterMismatchedDataType =>
- new(
+ new(nameof(ParameterMismatchedDataType),
(context, parameter) =>
{
// example
@@ -101,7 +101,7 @@ public static class OpenApiParameterRules
/// Validate that a path parameter should always appear in the path
///
public static ValidationRule PathParameterShouldBeInThePath =>
- new(
+ new(nameof(PathParameterShouldBeInThePath),
(context, parameter) =>
{
if (parameter.In == ParameterLocation.Path &&
diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiPathsRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiPathsRules.cs
index c25ca8aff..9c23f7220 100644
--- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiPathsRules.cs
+++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiPathsRules.cs
@@ -3,7 +3,6 @@
using System;
using System.Collections.Generic;
-using System.Text.RegularExpressions;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Properties;
@@ -19,7 +18,7 @@ public static class OpenApiPathsRules
/// A relative path to an individual endpoint. The field name MUST begin with a slash.
///
public static ValidationRule PathNameMustBeginWithSlash =>
- new(
+ new(nameof(PathNameMustBeginWithSlash),
(context, item) =>
{
foreach (var pathName in item.Keys)
@@ -36,12 +35,11 @@ public static class OpenApiPathsRules
}
});
- private static readonly Regex regexPath = new Regex("\\{([^/]+)\\}", RegexOptions.Compiled, TimeSpan.FromMilliseconds(100));
///
/// A relative path to an individual endpoint. The field name MUST begin with a slash.
///
public static ValidationRule PathMustBeUnique =>
- new ValidationRule(
+ new ValidationRule(nameof(PathMustBeUnique),
(context, item) =>
{
var hashSet = new HashSet();
@@ -50,7 +48,7 @@ public static class OpenApiPathsRules
{
context.Enter(path);
- var pathSignature = regexPath.Replace(path, "{}");
+ var pathSignature = GetPathSignature(path);
if (!hashSet.Add(pathSignature))
context.CreateError(nameof(PathMustBeUnique),
@@ -60,6 +58,28 @@ public static class OpenApiPathsRules
}
});
+ ///
+ /// Replaces placeholders in the path with {}, e.g. /pets/{petId} becomes /pets/{} .
+ ///
+ /// The input path
+ /// The path signature
+ private static string GetPathSignature(string path)
+ {
+ for (int openBrace = path.IndexOf('{'); openBrace > -1; openBrace = path.IndexOf('{', openBrace + 2))
+ {
+ int closeBrace = path.IndexOf('}', openBrace);
+
+ if (closeBrace < 0)
+ {
+ return path;
+ }
+
+ path = path.Substring(0, openBrace + 1) + path.Substring(closeBrace);
+ }
+
+ return path;
+ }
+
// add more rules
}
}
diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiResponseRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiResponseRules.cs
index 0f725c90e..f30b49ea0 100644
--- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiResponseRules.cs
+++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiResponseRules.cs
@@ -17,7 +17,7 @@ public static class OpenApiResponseRules
/// Validate the field is required.
///
public static ValidationRule ResponseRequiredFields =>
- new(
+ new(nameof(ResponseRequiredFields),
(context, response) =>
{
// description
diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiResponsesRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiResponsesRules.cs
index 1afe9a388..a2b91dc31 100644
--- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiResponsesRules.cs
+++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiResponsesRules.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
+// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
using System.Linq;
@@ -17,7 +17,7 @@ public static class OpenApiResponsesRules
/// An OpenAPI operation must contain at least one response
///
public static ValidationRule ResponsesMustContainAtLeastOneResponse =>
- new(
+ new(nameof(ResponsesMustContainAtLeastOneResponse),
(context, responses) =>
{
if (!responses.Keys.Any())
@@ -31,7 +31,7 @@ public static class OpenApiResponsesRules
/// The response key must either be "default" or an HTTP status code (1xx, 2xx, 3xx, 4xx, 5xx).
///
public static ValidationRule ResponsesMustBeIdentifiedByDefaultOrStatusCode =>
- new(
+ new(nameof(ResponsesMustBeIdentifiedByDefaultOrStatusCode),
(context, responses) =>
{
foreach (var key in responses.Keys)
diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiServerRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiServerRules.cs
index 292fd1fd0..dd11a661d 100644
--- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiServerRules.cs
+++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiServerRules.cs
@@ -17,7 +17,7 @@ public static class OpenApiServerRules
/// Validate the field is required.
///
public static ValidationRule ServerRequiredFields =>
- new(
+ new(nameof(ServerRequiredFields),
(context, server) =>
{
context.Enter("url");
diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiTagRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiTagRules.cs
index f28732e1e..cc006f971 100644
--- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiTagRules.cs
+++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiTagRules.cs
@@ -17,7 +17,7 @@ public static class OpenApiTagRules
/// Validate the field is required.
///
public static ValidationRule TagRequiredFields =>
- new(
+ new(nameof(TagRequiredFields),
(context, tag) =>
{
context.Enter("name");
diff --git a/src/Microsoft.OpenApi/Validations/ValidationRule.cs b/src/Microsoft.OpenApi/Validations/ValidationRule.cs
index aa866734a..bccb28be6 100644
--- a/src/Microsoft.OpenApi/Validations/ValidationRule.cs
+++ b/src/Microsoft.OpenApi/Validations/ValidationRule.cs
@@ -1,7 +1,8 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
+// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
using System;
+using Microsoft.OpenApi.Exceptions;
using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Properties;
@@ -17,12 +18,22 @@ public abstract class ValidationRule
///
internal abstract Type ElementType { get; }
+ ///
+ /// Validation rule Name.
+ ///
+ public string Name { get; }
+
///
/// Validate the object.
///
/// The context.
/// The object item.
internal abstract void Evaluate(IValidationContext context, object item);
+
+ internal ValidationRule(string name)
+ {
+ Name = !string.IsNullOrEmpty(name) ? name : throw new ArgumentNullException(nameof(name));
+ }
}
///
@@ -32,14 +43,26 @@ public abstract class ValidationRule
public class ValidationRule : ValidationRule
{
private readonly Action _validate;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Action to perform the validation.
+ [Obsolete("Please use the other constructor and specify a name")]
+ public ValidationRule(Action validate)
+ : this (Guid.NewGuid().ToString("D"), validate)
+ {
+ }
///
/// Initializes a new instance of the class.
///
+ /// Validation rule name.
/// Action to perform the validation.
- public ValidationRule(Action validate)
+ public ValidationRule(string name, Action validate)
+ : base(name)
{
- _validate = Utils.CheckArgumentNull(validate);
+ _validate = Utils.CheckArgumentNull(validate);
}
internal override Type ElementType
diff --git a/src/Microsoft.OpenApi/Validations/ValidationRuleSet.cs b/src/Microsoft.OpenApi/Validations/ValidationRuleSet.cs
index af30c04bc..e5950c300 100644
--- a/src/Microsoft.OpenApi/Validations/ValidationRuleSet.cs
+++ b/src/Microsoft.OpenApi/Validations/ValidationRuleSet.cs
@@ -1,4 +1,5 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
+
+// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
using System;
@@ -8,6 +9,7 @@
using Microsoft.OpenApi.Exceptions;
using Microsoft.OpenApi.Properties;
using Microsoft.OpenApi.Validations.Rules;
+using System.Data;
namespace Microsoft.OpenApi.Validations
{
@@ -16,16 +18,12 @@ namespace Microsoft.OpenApi.Validations
///
public sealed class ValidationRuleSet
{
- private Dictionary> _rulesDictionary = new();
+ private Dictionary> _rulesDictionary = new();
private static ValidationRuleSet _defaultRuleSet;
private List _emptyRules = new();
- ///
- /// Gets the keys in this rule set.
- ///
- public ICollection Keys => _rulesDictionary.Keys;
///
/// Gets the rules in this rule set.
@@ -45,13 +43,13 @@ public ValidationRuleSet()
}
///
- /// Retrieve the rules that are related to a specific key.
+ /// Retrieve the rules that are related to a specific type
///
- /// The key of the rules to search for.
- /// Either the rules related to the given key, or an empty list.
- public IList FindRules(string key)
+ /// The type that is to be validated
+ /// Either the rules related to the type, or an empty list.
+ public IList FindRules(Type type)
{
- _rulesDictionary.TryGetValue(key, out var results);
+ _rulesDictionary.TryGetValue(type, out var results);
return results ?? _emptyRules;
}
@@ -92,7 +90,7 @@ public static ValidationRuleSet GetEmptyRuleSet()
/// The rule set to add validation rules to.
/// The validation rules to be added to the rules set.
/// Throws a null argument exception if the arguments are null.
- public static void AddValidationRules(ValidationRuleSet ruleSet, IDictionary> rules)
+ public static void AddValidationRules(ValidationRuleSet ruleSet, IDictionary> rules)
{
if (ruleSet == null || rules == null)
{
@@ -118,7 +116,7 @@ public ValidationRuleSet(ValidationRuleSet ruleSet)
foreach (var rule in ruleSet)
{
- Add(rule.ElementType.Name, rule);
+ Add(rule.ElementType, rule);
}
}
@@ -126,7 +124,7 @@ public ValidationRuleSet(ValidationRuleSet ruleSet)
/// Initializes a new instance of the class.
///
/// Rules to be contained in this ruleset.
- public ValidationRuleSet(IDictionary> rules)
+ public ValidationRuleSet(IDictionary> rules)
{
if (rules == null)
{
@@ -144,7 +142,7 @@ public ValidationRuleSet(IDictionary> rules)
///
/// The key for the rule.
/// The list of rules.
- public void Add(string key, IList rules)
+ public void Add(Type key, IList rules)
{
foreach (var rule in rules)
{
@@ -158,7 +156,7 @@ public void Add(string key, IList rules)
/// The key for the rule.
/// The rule.
/// Exception thrown when rule already exists.
- public void Add(string key, ValidationRule rule)
+ public void Add(Type key, ValidationRule rule)
{
if (!_rulesDictionary.ContainsKey(key))
{
@@ -180,7 +178,7 @@ public void Add(string key, ValidationRule rule)
/// The new rule.
/// The old rule.
/// true, if the update was successful; otherwise false.
- public bool Update(string key, ValidationRule newRule, ValidationRule oldRule)
+ public bool Update(Type key, ValidationRule newRule, ValidationRule oldRule)
{
if (_rulesDictionary.TryGetValue(key, out var currentRules))
{
@@ -195,18 +193,33 @@ public bool Update(string key, ValidationRule newRule, ValidationRule oldRule)
///
/// The key of the collection of rules to be removed.
/// true if the collection of rules with the provided key is removed; otherwise, false.
- public bool Remove(string key)
+ public bool Remove(Type key)
{
return _rulesDictionary.Remove(key);
}
+ ///
+ /// Remove a rule by its name from all types it is used by.
+ ///
+ /// Name of the rule.
+ public void Remove(string ruleName)
+ {
+ foreach (KeyValuePair> rule in _rulesDictionary)
+ {
+ _rulesDictionary[rule.Key] = rule.Value.Where(vr => !vr.Name.Equals(ruleName, StringComparison.Ordinal)).ToList();
+ }
+
+ // Remove types with no rule
+ _rulesDictionary = _rulesDictionary.Where(r => r.Value.Any()).ToDictionary(r => r.Key, r => r.Value);
+ }
+
///
/// Removes a rule by key.
///
/// The key of the rule to be removed.
/// The rule to be removed.
/// true if the rule is successfully removed; otherwise, false.
- public bool Remove(string key, ValidationRule rule)
+ public bool Remove(Type key, ValidationRule rule)
{
if (_rulesDictionary.TryGetValue(key, out IList validationRules))
{
@@ -239,7 +252,7 @@ public void Clear()
///
/// The key to locate in the rule set.
/// true if the rule set contains an element with the key; otherwise, false.
- public bool ContainsKey(string key)
+ public bool ContainsKey(Type key)
{
return _rulesDictionary.ContainsKey(key);
}
@@ -250,7 +263,7 @@ public bool ContainsKey(string key)
/// The key to locate.
/// The rule to locate.
///
- public bool Contains(string key, ValidationRule rule)
+ public bool Contains(Type key, ValidationRule rule)
{
return _rulesDictionary.TryGetValue(key, out IList validationRules) && validationRules.Contains(rule);
}
@@ -263,7 +276,7 @@ public bool Contains(string key, ValidationRule rule)
/// key is found; otherwise, an empty object.
/// This parameter is passed uninitialized.
/// true if the specified key has rules.
- public bool TryGetValue(string key, out IList rules)
+ public bool TryGetValue(Type key, out IList rules)
{
return _rulesDictionary.TryGetValue(key, out rules);
}
@@ -300,7 +313,7 @@ private static ValidationRuleSet BuildDefaultRuleSet()
var propertyValue = property.GetValue(null); // static property
if (propertyValue is ValidationRule rule)
{
- ruleSet.Add(rule.ElementType.Name, rule);
+ ruleSet.Add(rule.ElementType, rule);
}
}
diff --git a/test/Microsoft.OpenApi.Hidi.Tests/Microsoft.OpenApi.Hidi.Tests.csproj b/test/Microsoft.OpenApi.Hidi.Tests/Microsoft.OpenApi.Hidi.Tests.csproj
index 638e05153..b5f1cf6bc 100644
--- a/test/Microsoft.OpenApi.Hidi.Tests/Microsoft.OpenApi.Hidi.Tests.csproj
+++ b/test/Microsoft.OpenApi.Hidi.Tests/Microsoft.OpenApi.Hidi.Tests.csproj
@@ -11,12 +11,12 @@
-
-
+
+
-
-
-
+
+
+
diff --git a/test/Microsoft.OpenApi.Readers.Tests/Microsoft.OpenApi.Readers.Tests.csproj b/test/Microsoft.OpenApi.Readers.Tests/Microsoft.OpenApi.Readers.Tests.csproj
index 85479d684..63e28a8a9 100644
--- a/test/Microsoft.OpenApi.Readers.Tests/Microsoft.OpenApi.Readers.Tests.csproj
+++ b/test/Microsoft.OpenApi.Readers.Tests/Microsoft.OpenApi.Readers.Tests.csproj
@@ -17,14 +17,14 @@
-
+
+
+
-
-
-
-
-
+
+
+
diff --git a/test/Microsoft.OpenApi.Readers.Tests/OpenApiReaderTests/OpenApiStreamReaderTests.cs b/test/Microsoft.OpenApi.Readers.Tests/OpenApiReaderTests/OpenApiStreamReaderTests.cs
index 816a58226..e05c9ba9d 100644
--- a/test/Microsoft.OpenApi.Readers.Tests/OpenApiReaderTests/OpenApiStreamReaderTests.cs
+++ b/test/Microsoft.OpenApi.Readers.Tests/OpenApiReaderTests/OpenApiStreamReaderTests.cs
@@ -34,5 +34,20 @@ public void StreamShouldNotCloseIfLeaveStreamOpenSettingEqualsTrue()
_ = OpenApiDocument.Load(stream, "yaml", settings);
Assert.True(stream.CanRead);
}
+
+ [Fact]
+ public async void StreamShouldNotBeDisposedIfLeaveStreamOpenSettingIsTrue()
+ {
+ var memoryStream = new MemoryStream();
+ using var fileStream = Resources.GetStream(Path.Combine(SampleFolderPath, "petStore.yaml"));
+
+ await fileStream.CopyToAsync(memoryStream);
+ memoryStream.Position = 0;
+ var stream = memoryStream;
+
+ var result = OpenApiDocument.Load(stream, "yaml", new OpenApiReaderSettings { LeaveStreamOpen = true });
+ stream.Seek(0, SeekOrigin.Begin); // does not throw an object disposed exception
+ Assert.True(stream.CanRead);
+ }
}
}
diff --git a/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiOperationTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiOperationTests.cs
index f77ab0cb0..f264c23f6 100644
--- a/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiOperationTests.cs
+++ b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiOperationTests.cs
@@ -9,9 +9,13 @@
using Json.Schema;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Extensions;
+using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Reader.ParseNodes;
using Microsoft.OpenApi.Reader.V2;
+using Microsoft.OpenApi.Reader.V3;
+using Microsoft.OpenApi.Tests;
+using Microsoft.OpenApi.Writers;
using Xunit;
namespace Microsoft.OpenApi.Readers.Tests.V2Tests
@@ -293,5 +297,259 @@ public void ParseOperationWithResponseExamplesShouldSucceed()
.Excluding(o => o.Responses["200"].Content["application/json"].Example.Node[2].Parent)
.Excluding(o => o.Responses["200"].Content["application/json"].Example.Node[2].Root));
}
+
+ [Fact]
+ public void ParseOperationWithEmptyProducesArraySetsResponseSchemaIfExists()
+ {
+ // Arrange
+ MapNode node;
+ using var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "operationWithEmptyProducesArrayInResponse.json"));
+ node = TestHelper.CreateYamlMapNode(stream);
+
+ // Act
+ var operation = OpenApiV2Deserializer.LoadOperation(node);
+ var expected = @"{
+ ""produces"": [
+ ""application/octet-stream""
+ ],
+ ""responses"": {
+ ""200"": {
+ ""description"": ""OK"",
+ ""schema"": {
+ ""type"": ""string"",
+ ""description"": ""The content of the file."",
+ ""format"": ""binary"",
+ ""x-ms-summary"": ""File Content""
+ }
+ }
+ }
+}";
+
+ var stringBuilder = new StringBuilder();
+ var jsonWriter = new OpenApiJsonWriter(new StringWriter(stringBuilder));
+ operation.SerializeAsV2(jsonWriter);
+
+ // Assert
+ var actual = stringBuilder.ToString();
+ actual.MakeLineBreaksEnvironmentNeutral().Should().BeEquivalentTo(expected.MakeLineBreaksEnvironmentNeutral());
+ }
+
+ [Fact]
+ public void ParseOperationWithBodyAndEmptyConsumesSetsRequestBodySchemaIfExists()
+ {
+ // Arrange
+ MapNode node;
+ using var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "operationWithBodyAndEmptyConsumes.yaml"));
+ node = TestHelper.CreateYamlMapNode(stream);
+
+ // Act
+ var operation = OpenApiV2Deserializer.LoadOperation(node);
+
+ // Assert
+ operation.Should().BeEquivalentTo(_operationWithBody, options => options.IgnoringCyclicReferences());
+ }
+
+ [Fact]
+ public void ParseV2ResponseWithExamplesExtensionWorks()
+ {
+ // Arrange
+ MapNode node;
+ using (var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "opWithResponseExamplesExtension.yaml")))
+ {
+ node = TestHelper.CreateYamlMapNode(stream);
+ }
+
+ // Act
+ var operation = OpenApiV2Deserializer.LoadOperation(node);
+ var actual = operation.SerializeAsYaml(OpenApiSpecVersion.OpenApi3_0);
+
+ // Assert
+ var expected = @"summary: Get all pets
+responses:
+ '200':
+ description: Successful response
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ type: object
+ properties:
+ name:
+ type: string
+ age:
+ type: integer
+ examples:
+ example1:
+ summary: Example - List of Pets
+ value:
+ - name: Buddy
+ age: 2
+ - name: Whiskers
+ age: 1
+ example2:
+ summary: Example - Playful Cat
+ value:
+ name: Whiskers
+ age: 1";
+
+ // Assert
+ actual = actual.MakeLineBreaksEnvironmentNeutral();
+ expected = expected.MakeLineBreaksEnvironmentNeutral();
+ actual.Should().Be(expected);
+ }
+
+ [Fact]
+ public void LoadV3ExamplesInResponseAsExtensionsWorks()
+ {
+ // Arrange
+ MapNode node;
+ using (var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "v3OperationWithResponseExamples.yaml")))
+ {
+ node = TestHelper.CreateYamlMapNode(stream);
+ }
+
+ // Act
+ var operation = OpenApiV3Deserializer.LoadOperation(node);
+ var actual = operation.SerializeAsYaml(OpenApiSpecVersion.OpenApi2_0);
+
+ // Assert
+ var expected = @"summary: Get all pets
+produces:
+ - application/json
+responses:
+ '200':
+ description: Successful response
+ schema:
+ type: array
+ items:
+ type: object
+ properties:
+ name:
+ type: string
+ age:
+ type: integer
+ x-examples:
+ example1:
+ summary: Example - List of Pets
+ value:
+ - name: Buddy
+ age: 2
+ - name: Whiskers
+ age: 1
+ example2:
+ summary: Example - Playful Cat
+ value:
+ name: Whiskers
+ age: 1";
+
+ // Assert
+ actual = actual.MakeLineBreaksEnvironmentNeutral();
+ expected = expected.MakeLineBreaksEnvironmentNeutral();
+ actual.Should().Be(expected);
+ }
+
+ [Fact]
+ public void LoadV2OperationWithBodyParameterExamplesWorks()
+ {
+ // Arrange
+ MapNode node;
+ using (var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "opWithBodyParameterExamples.yaml")))
+ {
+ node = TestHelper.CreateYamlMapNode(stream);
+ }
+
+ // Act
+ var operation = OpenApiV2Deserializer.LoadOperation(node);
+ var actual = operation.SerializeAsYaml(OpenApiSpecVersion.OpenApi3_0);
+
+ // Assert
+ var expected = @"summary: Get all pets
+requestBody:
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ type: object
+ properties:
+ name:
+ type: string
+ age:
+ type: integer
+ examples:
+ example1:
+ summary: Example - List of Pets
+ value:
+ - name: Buddy
+ age: 2
+ - name: Whiskers
+ age: 1
+ example2:
+ summary: Example - Playful Cat
+ value:
+ name: Whiskers
+ age: 1
+ required: true
+ x-bodyName: body
+responses: { }";
+
+ // Assert
+ actual = actual.MakeLineBreaksEnvironmentNeutral();
+ expected = expected.MakeLineBreaksEnvironmentNeutral();
+ actual.Should().Be(expected);
+ }
+
+ [Fact]
+ public void LoadV3ExamplesInRequestBodyParameterAsExtensionsWorks()
+ {
+ // Arrange
+ MapNode node;
+ using (var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "v3OperationWithBodyParameterExamples.yaml")))
+ {
+ node = TestHelper.CreateYamlMapNode(stream);
+ }
+
+ // Act
+ var operation = OpenApiV3Deserializer.LoadOperation(node);
+ var actual = operation.SerializeAsYaml(OpenApiSpecVersion.OpenApi2_0);
+
+ // Assert
+ var expected = @"summary: Get all pets
+consumes:
+ - application/json
+parameters:
+ - in: body
+ name: body
+ required: true
+ schema:
+ type: array
+ items:
+ type: object
+ properties:
+ name:
+ type: string
+ age:
+ type: integer
+ x-examples:
+ example1:
+ summary: Example - List of Pets
+ value:
+ - name: Buddy
+ age: 2
+ - name: Whiskers
+ age: 1
+ example2:
+ summary: Example - Playful Cat
+ value:
+ name: Whiskers
+ age: 1
+responses: { }";
+
+ // Assert
+ actual = actual.MakeLineBreaksEnvironmentNeutral();
+ expected = expected.MakeLineBreaksEnvironmentNeutral();
+ actual.Should().Be(expected);
+ }
}
}
diff --git a/test/Microsoft.OpenApi.Readers.Tests/V2Tests/Samples/OpenApiOperation/opWithBodyParameterExamples.yaml b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/Samples/OpenApiOperation/opWithBodyParameterExamples.yaml
new file mode 100644
index 000000000..e2ffcc7df
--- /dev/null
+++ b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/Samples/OpenApiOperation/opWithBodyParameterExamples.yaml
@@ -0,0 +1,29 @@
+summary: Get all pets
+consumes:
+ - application/json
+parameters:
+ - in: body
+ name: body
+ required: true
+ schema:
+ type: array
+ items:
+ type: object
+ properties:
+ name:
+ type: string
+ age:
+ type: integer
+ x-examples:
+ example1:
+ summary: Example - List of Pets
+ value:
+ - name: Buddy
+ age: 2
+ - name: Whiskers
+ age: 1
+ example2:
+ summary: Example - Playful Cat
+ value:
+ name: Whiskers
+ age: 1
\ No newline at end of file
diff --git a/test/Microsoft.OpenApi.Readers.Tests/V2Tests/Samples/OpenApiOperation/opWithResponseExamplesExtension.yaml b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/Samples/OpenApiOperation/opWithResponseExamplesExtension.yaml
new file mode 100644
index 000000000..5dcc89d97
--- /dev/null
+++ b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/Samples/OpenApiOperation/opWithResponseExamplesExtension.yaml
@@ -0,0 +1,28 @@
+summary: Get all pets
+produces:
+- application/json
+responses:
+ '200':
+ description: Successful response
+ schema:
+ type: array
+ items:
+ type: object
+ properties:
+ name:
+ type: string
+ age:
+ type: integer
+ x-examples:
+ example1:
+ summary: Example - List of Pets
+ value:
+ - name: "Buddy"
+ age: 2
+ - name: "Whiskers"
+ age: 1
+ example2:
+ summary: Example - Playful Cat
+ value:
+ name: "Whiskers"
+ age: 1
\ No newline at end of file
diff --git a/test/Microsoft.OpenApi.Readers.Tests/V2Tests/Samples/OpenApiOperation/v3OperationWithBodyParameterExamples.yaml b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/Samples/OpenApiOperation/v3OperationWithBodyParameterExamples.yaml
new file mode 100644
index 000000000..a0358125a
--- /dev/null
+++ b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/Samples/OpenApiOperation/v3OperationWithBodyParameterExamples.yaml
@@ -0,0 +1,27 @@
+summary: Get all pets
+requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ type: object
+ properties:
+ name:
+ type: string
+ age:
+ type: integer
+ examples:
+ example1:
+ summary: Example - List of Pets
+ value:
+ - name: "Buddy"
+ age: 2
+ - name: "Whiskers"
+ age: 1
+ example2:
+ summary: Example - Playful Cat
+ value:
+ name: "Whiskers"
+ age: 1
\ No newline at end of file
diff --git a/test/Microsoft.OpenApi.Readers.Tests/V2Tests/Samples/OpenApiOperation/v3OperationWithResponseExamples.yaml b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/Samples/OpenApiOperation/v3OperationWithResponseExamples.yaml
new file mode 100644
index 000000000..c3b124685
--- /dev/null
+++ b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/Samples/OpenApiOperation/v3OperationWithResponseExamples.yaml
@@ -0,0 +1,28 @@
+summary: Get all pets
+responses:
+ '200':
+ description: Successful response
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ type: object
+ properties:
+ name:
+ type: string
+ age:
+ type: integer
+ examples:
+ example1:
+ summary: Example - List of Pets
+ value:
+ - name: "Buddy"
+ age: 2
+ - name: "Whiskers"
+ age: 1
+ example2:
+ summary: Example - Playful Cat
+ value:
+ name: "Whiskers"
+ age: 1
\ No newline at end of file
diff --git a/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs
index 33bc1c0d5..b337a5166 100644
--- a/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs
+++ b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
+// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
using System;
@@ -1052,6 +1052,20 @@ public void ParseDocumentWithJsonSchemaReferencesWorks()
Assert.Equal(expectedSchema, actualSchema);
}
+ [Fact]
+ public void ValidateExampleShouldNotHaveDataTypeMismatch()
+ {
+ // Act
+ var result = OpenApiDocument.Load(Path.Combine(SampleFolderPath, "documentWithDateExampleInSchema.yaml"), new OpenApiReaderSettings
+ {
+ ReferenceResolution = ReferenceResolutionSetting.ResolveLocalReferences
+
+ });
+
+ // Assert
+ var warnings = result.OpenApiDiagnostic.Warnings;
+ Assert.False(warnings.Any());
+ }
[Fact]
public void ParseDocWithRefsUsingProxyReferencesSucceeds()
{
diff --git a/test/Microsoft.OpenApi.Readers.Tests/V3Tests/Samples/OpenApiDocument/documentWithDateExampleInSchema.yaml b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/Samples/OpenApiDocument/documentWithDateExampleInSchema.yaml
new file mode 100644
index 000000000..ad8c525cd
--- /dev/null
+++ b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/Samples/OpenApiDocument/documentWithDateExampleInSchema.yaml
@@ -0,0 +1,42 @@
+openapi: 3.0.0
+info:
+ title: Sample API
+ description: Lorem Ipsum
+ version: 1.0.0
+servers:
+ - url: http://api.example.com/v1
+ description: Lorem Ipsum
+paths:
+ /issues:
+ get:
+ summary: Returns a list of issues.
+ description: Lorem Ipsum
+ responses:
+ "200":
+ description: Lorem Ipsum
+ content:
+ application/json:
+ schema:
+ type: object
+ required:
+ - data
+ properties:
+ data:
+ type: array
+ items:
+ $ref: "#/components/schemas/issueData"
+ example:
+ data:
+ - issuedAt: "2023-10-12"
+components:
+ schemas:
+ issueData:
+ type: object
+ title: Issue Data
+ description: Information about the issue.
+ properties:
+ issuedAt:
+ type: string
+ format: date
+ description: Lorem Ipsum
+ example: "2023-10-12"
\ No newline at end of file
diff --git a/test/Microsoft.OpenApi.SmokeTests/Microsoft.OpenApi.SmokeTests.csproj b/test/Microsoft.OpenApi.SmokeTests/Microsoft.OpenApi.SmokeTests.csproj
index 6475efa53..b878a8faf 100644
--- a/test/Microsoft.OpenApi.SmokeTests/Microsoft.OpenApi.SmokeTests.csproj
+++ b/test/Microsoft.OpenApi.SmokeTests/Microsoft.OpenApi.SmokeTests.csproj
@@ -8,12 +8,12 @@
-
-
-
+
+
+
-
-
+
+
diff --git a/test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj b/test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj
index 0f087c4aa..6674a13d7 100644
--- a/test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj
+++ b/test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj
@@ -8,16 +8,16 @@
-
-
+
+
-
+
-
-
-
-
+
+
+
+
diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiCallbackTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiCallbackTests.cs
index c7935e768..310511db8 100644
--- a/test/Microsoft.OpenApi.Tests/Models/OpenApiCallbackTests.cs
+++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiCallbackTests.cs
@@ -15,7 +15,6 @@
namespace Microsoft.OpenApi.Tests.Models
{
[Collection("DefaultSettings")]
- [UsesVerify]
public class OpenApiCallbackTests
{
public static OpenApiCallback AdvancedCallback = new()
diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.cs
index d093ee64c..6af4ed8d0 100644
--- a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.cs
+++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.cs
@@ -23,7 +23,6 @@
namespace Microsoft.OpenApi.Tests.Models
{
[Collection("DefaultSettings")]
- [UsesVerify]
public class OpenApiDocumentTests
{
public OpenApiDocumentTests()
diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.cs
index 6da171ec3..bec6f6b23 100644
--- a/test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.cs
+++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.cs
@@ -18,7 +18,6 @@
namespace Microsoft.OpenApi.Tests.Models
{
[Collection("DefaultSettings")]
- [UsesVerify]
public class OpenApiExampleTests
{
public static OpenApiExample AdvancedExample = new()
diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.cs
index 4d120531b..d63330a09 100644
--- a/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.cs
+++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.cs
@@ -14,7 +14,6 @@
namespace Microsoft.OpenApi.Tests.Models
{
[Collection("DefaultSettings")]
- [UsesVerify]
public class OpenApiHeaderTests
{
public static OpenApiHeader AdvancedHeader = new()
diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiLinkTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiLinkTests.cs
index e930aacb9..4468ed201 100644
--- a/test/Microsoft.OpenApi.Tests/Models/OpenApiLinkTests.cs
+++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiLinkTests.cs
@@ -16,7 +16,6 @@
namespace Microsoft.OpenApi.Tests.Models
{
[Collection("DefaultSettings")]
- [UsesVerify]
public class OpenApiLinkTests
{
public static readonly OpenApiLink AdvancedLink = new()
diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithSchemaReferenceAsV2JsonWorksAsync_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithSchemaReferenceAsV2JsonWorksAsync_produceTerseOutput=False.verified.txt
index 0542c58ce..744f8451c 100644
--- a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithSchemaReferenceAsV2JsonWorksAsync_produceTerseOutput=False.verified.txt
+++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithSchemaReferenceAsV2JsonWorksAsync_produceTerseOutput=False.verified.txt
@@ -3,5 +3,11 @@
"name": "name1",
"description": "description1",
"required": true,
- "type": "string"
+ "type": "string",
+ "x-examples": {
+ "test": {
+ "summary": "summary3",
+ "description": "description3"
+ }
+ }
}
\ No newline at end of file
diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithSchemaReferenceAsV2JsonWorksAsync_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithSchemaReferenceAsV2JsonWorksAsync_produceTerseOutput=True.verified.txt
index b80b263d3..26b158865 100644
--- a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithSchemaReferenceAsV2JsonWorksAsync_produceTerseOutput=True.verified.txt
+++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithSchemaReferenceAsV2JsonWorksAsync_produceTerseOutput=True.verified.txt
@@ -1 +1 @@
-{"in":"header","name":"name1","description":"description1","required":true,"type":"string"}
\ No newline at end of file
+{"in":"header","name":"name1","description":"description1","required":true,"type":"string","x-examples":{"test":{"summary":"summary3","description":"description3"}}}
\ No newline at end of file
diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithSchemaTypeObjectAsV2JsonWorksAsync_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithSchemaTypeObjectAsV2JsonWorksAsync_produceTerseOutput=False.verified.txt
index 0542c58ce..744f8451c 100644
--- a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithSchemaTypeObjectAsV2JsonWorksAsync_produceTerseOutput=False.verified.txt
+++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithSchemaTypeObjectAsV2JsonWorksAsync_produceTerseOutput=False.verified.txt
@@ -3,5 +3,11 @@
"name": "name1",
"description": "description1",
"required": true,
- "type": "string"
+ "type": "string",
+ "x-examples": {
+ "test": {
+ "summary": "summary3",
+ "description": "description3"
+ }
+ }
}
\ No newline at end of file
diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithSchemaTypeObjectAsV2JsonWorksAsync_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithSchemaTypeObjectAsV2JsonWorksAsync_produceTerseOutput=True.verified.txt
index b80b263d3..26b158865 100644
--- a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithSchemaTypeObjectAsV2JsonWorksAsync_produceTerseOutput=True.verified.txt
+++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithSchemaTypeObjectAsV2JsonWorksAsync_produceTerseOutput=True.verified.txt
@@ -1 +1 @@
-{"in":"header","name":"name1","description":"description1","required":true,"type":"string"}
\ No newline at end of file
+{"in":"header","name":"name1","description":"description1","required":true,"type":"string","x-examples":{"test":{"summary":"summary3","description":"description3"}}}
\ No newline at end of file
diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.cs
index f861e0189..b173f2363 100644
--- a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.cs
+++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.cs
@@ -19,7 +19,6 @@
namespace Microsoft.OpenApi.Tests.Models
{
[Collection("DefaultSettings")]
- [UsesVerify]
public class OpenApiParameterTests
{
public static OpenApiParameter BasicParameter = new()
@@ -293,7 +292,13 @@ public void SerializeAdvancedParameterAsV2JsonWorks()
"name": "name1",
"description": "description1",
"required": true,
- "format": "double"
+ "format": "double",
+ "x-examples": {
+ "test": {
+ "summary": "summary3",
+ "description": "description3"
+ }
+ }
}
""";
diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiRequestBodyTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiRequestBodyTests.cs
index 0e205b71e..93d9f337f 100644
--- a/test/Microsoft.OpenApi.Tests/Models/OpenApiRequestBodyTests.cs
+++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiRequestBodyTests.cs
@@ -14,7 +14,6 @@
namespace Microsoft.OpenApi.Tests.Models
{
[Collection("DefaultSettings")]
- [UsesVerify]
public class OpenApiRequestBodyTests
{
public static OpenApiRequestBody AdvancedRequestBody = new()
diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiResponseTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiResponseTests.cs
index 421505ee2..d9006ec09 100644
--- a/test/Microsoft.OpenApi.Tests/Models/OpenApiResponseTests.cs
+++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiResponseTests.cs
@@ -20,7 +20,6 @@
namespace Microsoft.OpenApi.Tests.Models
{
[Collection("DefaultSettings")]
- [UsesVerify]
public class OpenApiResponseTests
{
public static OpenApiResponse BasicResponse = new OpenApiResponse();
diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiSecuritySchemeTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiSecuritySchemeTests.cs
index 19bac6305..49a5dcbfd 100644
--- a/test/Microsoft.OpenApi.Tests/Models/OpenApiSecuritySchemeTests.cs
+++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiSecuritySchemeTests.cs
@@ -16,7 +16,6 @@
namespace Microsoft.OpenApi.Tests.Models
{
[Collection("DefaultSettings")]
- [UsesVerify]
public class OpenApiSecuritySchemeTests
{
public static OpenApiSecurityScheme ApiKeySecurityScheme = new()
diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.cs
index e43170f5a..c02f7598c 100644
--- a/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.cs
+++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.cs
@@ -15,7 +15,6 @@
namespace Microsoft.OpenApi.Tests.Models
{
[Collection("DefaultSettings")]
- [UsesVerify]
public class OpenApiTagTests
{
public static OpenApiTag BasicTag = new();
diff --git a/test/Microsoft.OpenApi.Tests/Models/References/OpenApiCallbackReferenceTests.cs b/test/Microsoft.OpenApi.Tests/Models/References/OpenApiCallbackReferenceTests.cs
index 3e5917932..38a39beae 100644
--- a/test/Microsoft.OpenApi.Tests/Models/References/OpenApiCallbackReferenceTests.cs
+++ b/test/Microsoft.OpenApi.Tests/Models/References/OpenApiCallbackReferenceTests.cs
@@ -16,7 +16,6 @@
namespace Microsoft.OpenApi.Tests.Models.References
{
[Collection("DefaultSettings")]
- [UsesVerify]
public class OpenApiCallbackReferenceTests
{
private const string OpenApi = @"
diff --git a/test/Microsoft.OpenApi.Tests/Models/References/OpenApiExampleReferenceTests.cs b/test/Microsoft.OpenApi.Tests/Models/References/OpenApiExampleReferenceTests.cs
index 0faedb3e7..a48ffd906 100644
--- a/test/Microsoft.OpenApi.Tests/Models/References/OpenApiExampleReferenceTests.cs
+++ b/test/Microsoft.OpenApi.Tests/Models/References/OpenApiExampleReferenceTests.cs
@@ -16,7 +16,6 @@
namespace Microsoft.OpenApi.Tests.Models.References
{
[Collection("DefaultSettings")]
- [UsesVerify]
public class OpenApiExampleReferenceTests
{
private const string OpenApi = @"
diff --git a/test/Microsoft.OpenApi.Tests/Models/References/OpenApiHeaderReferenceTests.cs b/test/Microsoft.OpenApi.Tests/Models/References/OpenApiHeaderReferenceTests.cs
index 46bfdcd39..74d8e5797 100644
--- a/test/Microsoft.OpenApi.Tests/Models/References/OpenApiHeaderReferenceTests.cs
+++ b/test/Microsoft.OpenApi.Tests/Models/References/OpenApiHeaderReferenceTests.cs
@@ -16,7 +16,6 @@
namespace Microsoft.OpenApi.Tests.Models.References
{
[Collection("DefaultSettings")]
- [UsesVerify]
public class OpenApiHeaderReferenceTests
{
private const string OpenApi= @"
diff --git a/test/Microsoft.OpenApi.Tests/Models/References/OpenApiLinkReferenceTests.cs b/test/Microsoft.OpenApi.Tests/Models/References/OpenApiLinkReferenceTests.cs
index 93e31dc11..9a52b5234 100644
--- a/test/Microsoft.OpenApi.Tests/Models/References/OpenApiLinkReferenceTests.cs
+++ b/test/Microsoft.OpenApi.Tests/Models/References/OpenApiLinkReferenceTests.cs
@@ -16,7 +16,6 @@
namespace Microsoft.OpenApi.Tests.Models.References
{
[Collection("DefaultSettings")]
- [UsesVerify]
public class OpenApiLinkReferenceTests
{
private const string OpenApi = @"
diff --git a/test/Microsoft.OpenApi.Tests/Models/References/OpenApiParameterReferenceTests.cs b/test/Microsoft.OpenApi.Tests/Models/References/OpenApiParameterReferenceTests.cs
index e3583b0bf..8f76fc526 100644
--- a/test/Microsoft.OpenApi.Tests/Models/References/OpenApiParameterReferenceTests.cs
+++ b/test/Microsoft.OpenApi.Tests/Models/References/OpenApiParameterReferenceTests.cs
@@ -16,7 +16,6 @@
namespace Microsoft.OpenApi.Tests.Models.References
{
[Collection("DefaultSettings")]
- [UsesVerify]
public class OpenApiParameterReferenceTests
{
private const string OpenApi = @"
diff --git a/test/Microsoft.OpenApi.Tests/Models/References/OpenApiPathItemReferenceTests.cs b/test/Microsoft.OpenApi.Tests/Models/References/OpenApiPathItemReferenceTests.cs
index dea2313e5..d4aba67c3 100644
--- a/test/Microsoft.OpenApi.Tests/Models/References/OpenApiPathItemReferenceTests.cs
+++ b/test/Microsoft.OpenApi.Tests/Models/References/OpenApiPathItemReferenceTests.cs
@@ -16,7 +16,6 @@
namespace Microsoft.OpenApi.Tests.Models.References
{
[Collection("DefaultSettings")]
- [UsesVerify]
public class OpenApiPathItemReferenceTests
{
private const string OpenApi = @"
diff --git a/test/Microsoft.OpenApi.Tests/Models/References/OpenApiRequestBodyReferenceTests.cs b/test/Microsoft.OpenApi.Tests/Models/References/OpenApiRequestBodyReferenceTests.cs
index f443960e3..dd417f093 100644
--- a/test/Microsoft.OpenApi.Tests/Models/References/OpenApiRequestBodyReferenceTests.cs
+++ b/test/Microsoft.OpenApi.Tests/Models/References/OpenApiRequestBodyReferenceTests.cs
@@ -18,7 +18,6 @@
namespace Microsoft.OpenApi.Tests.Models.References
{
[Collection("DefaultSettings")]
- [UsesVerify]
public class OpenApiRequestBodyReferenceTests
{
private readonly string OpenApi = @"
diff --git a/test/Microsoft.OpenApi.Tests/Models/References/OpenApiResponseReferenceTest.cs b/test/Microsoft.OpenApi.Tests/Models/References/OpenApiResponseReferenceTest.cs
index a905a7b1b..f460374a8 100644
--- a/test/Microsoft.OpenApi.Tests/Models/References/OpenApiResponseReferenceTest.cs
+++ b/test/Microsoft.OpenApi.Tests/Models/References/OpenApiResponseReferenceTest.cs
@@ -17,7 +17,6 @@
namespace Microsoft.OpenApi.Tests.Models.References
{
[Collection("DefaultSettings")]
- [UsesVerify]
public class OpenApiResponseReferenceTest
{
private const string OpenApi = @"
diff --git a/test/Microsoft.OpenApi.Tests/Models/References/OpenApiSecuritySchemeReferenceTests.cs b/test/Microsoft.OpenApi.Tests/Models/References/OpenApiSecuritySchemeReferenceTests.cs
index a0bea6e39..dccd41692 100644
--- a/test/Microsoft.OpenApi.Tests/Models/References/OpenApiSecuritySchemeReferenceTests.cs
+++ b/test/Microsoft.OpenApi.Tests/Models/References/OpenApiSecuritySchemeReferenceTests.cs
@@ -15,7 +15,6 @@
namespace Microsoft.OpenApi.Tests.Models.References
{
[Collection("DefaultSettings")]
- [UsesVerify]
public class OpenApiSecuritySchemeReferenceTests
{
private const string OpenApi = @"
diff --git a/test/Microsoft.OpenApi.Tests/Models/References/OpenApiTagReferenceTest.cs b/test/Microsoft.OpenApi.Tests/Models/References/OpenApiTagReferenceTest.cs
index 0b2efe1b0..82f1b27a2 100644
--- a/test/Microsoft.OpenApi.Tests/Models/References/OpenApiTagReferenceTest.cs
+++ b/test/Microsoft.OpenApi.Tests/Models/References/OpenApiTagReferenceTest.cs
@@ -15,7 +15,6 @@
namespace Microsoft.OpenApi.Tests.Models.References
{
[Collection("DefaultSettings")]
- [UsesVerify]
public class OpenApiTagReferenceTest
{
private const string OpenApi = @"openapi: 3.0.3
diff --git a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt
index 1455b29ca..8cce0b6f5 100755
--- a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt
+++ b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt
@@ -478,6 +478,7 @@ namespace Microsoft.OpenApi.Models
public const string Enum = "enum";
public const string Example = "example";
public const string Examples = "examples";
+ public const string ExamplesExtension = "x-examples";
public const string ExclusiveMaximum = "exclusiveMaximum";
public const string ExclusiveMinimum = "exclusiveMinimum";
public const string Explode = "explode";
@@ -666,6 +667,7 @@ namespace Microsoft.OpenApi.Models
public virtual string Summary { get; set; }
public virtual bool UnresolvedReference { get; set; }
public virtual Microsoft.OpenApi.Any.OpenApiAny Value { get; set; }
+ public void Serialize(Microsoft.OpenApi.Writers.IOpenApiWriter writer, Microsoft.OpenApi.OpenApiSpecVersion version) { }
public void SerializeAsV2(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { }
public void SerializeAsV2WithoutReference(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { }
public virtual void SerializeAsV3(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { }
@@ -1606,35 +1608,37 @@ namespace Microsoft.OpenApi.Validations
}
public abstract class ValidationRule
{
- protected ValidationRule() { }
+ public string Name { get; }
}
public sealed class ValidationRuleSet
{
public ValidationRuleSet() { }
public ValidationRuleSet(Microsoft.OpenApi.Validations.ValidationRuleSet ruleSet) { }
- public ValidationRuleSet(System.Collections.Generic.IDictionary> rules) { }
+ public ValidationRuleSet(System.Collections.Generic.IDictionary> rules) { }
public int Count { get; }
- public System.Collections.Generic.ICollection Keys { get; }
public System.Collections.Generic.IList Rules { get; }
- public void Add(string key, Microsoft.OpenApi.Validations.ValidationRule rule) { }
- public void Add(string key, System.Collections.Generic.IList rules) { }
+ public void Add(System.Type key, Microsoft.OpenApi.Validations.ValidationRule rule) { }
+ public void Add(System.Type key, System.Collections.Generic.IList rules) { }
public void Clear() { }
- public bool Contains(string key, Microsoft.OpenApi.Validations.ValidationRule rule) { }
- public bool ContainsKey(string key) { }
- public System.Collections.Generic.IList FindRules(string key) { }
+ public bool Contains(System.Type key, Microsoft.OpenApi.Validations.ValidationRule rule) { }
+ public bool ContainsKey(System.Type key) { }
+ public System.Collections.Generic.IList FindRules(System.Type type) { }
public System.Collections.Generic.IEnumerator GetEnumerator() { }
public bool Remove(Microsoft.OpenApi.Validations.ValidationRule rule) { }
- public bool Remove(string key) { }
- public bool Remove(string key, Microsoft.OpenApi.Validations.ValidationRule rule) { }
- public bool TryGetValue(string key, out System.Collections.Generic.IList rules) { }
- public bool Update(string key, Microsoft.OpenApi.Validations.ValidationRule newRule, Microsoft.OpenApi.Validations.ValidationRule oldRule) { }
- public static void AddValidationRules(Microsoft.OpenApi.Validations.ValidationRuleSet ruleSet, System.Collections.Generic.IDictionary> rules) { }
+ public void Remove(string ruleName) { }
+ public bool Remove(System.Type key) { }
+ public bool Remove(System.Type key, Microsoft.OpenApi.Validations.ValidationRule rule) { }
+ public bool TryGetValue(System.Type key, out System.Collections.Generic.IList rules) { }
+ public bool Update(System.Type key, Microsoft.OpenApi.Validations.ValidationRule newRule, Microsoft.OpenApi.Validations.ValidationRule oldRule) { }
+ public static void AddValidationRules(Microsoft.OpenApi.Validations.ValidationRuleSet ruleSet, System.Collections.Generic.IDictionary> rules) { }
public static Microsoft.OpenApi.Validations.ValidationRuleSet GetDefaultRuleSet() { }
public static Microsoft.OpenApi.Validations.ValidationRuleSet GetEmptyRuleSet() { }
}
public class ValidationRule : Microsoft.OpenApi.Validations.ValidationRule
{
+ [System.Obsolete("Please use the other constructor and specify a name")]
public ValidationRule(System.Action validate) { }
+ public ValidationRule(string name, System.Action validate) { }
}
}
namespace Microsoft.OpenApi.Validations.Rules
diff --git a/test/Microsoft.OpenApi.Tests/Services/OpenApiUrlTreeNodeTests.cs b/test/Microsoft.OpenApi.Tests/Services/OpenApiUrlTreeNodeTests.cs
index ba4fae149..d92fe0493 100644
--- a/test/Microsoft.OpenApi.Tests/Services/OpenApiUrlTreeNodeTests.cs
+++ b/test/Microsoft.OpenApi.Tests/Services/OpenApiUrlTreeNodeTests.cs
@@ -12,7 +12,6 @@
namespace Microsoft.OpenApi.Tests.Services
{
- [UsesVerify]
public class OpenApiUrlTreeNodeTests
{
private OpenApiDocument OpenApiDocumentSample_1 => new()
diff --git a/test/Microsoft.OpenApi.Tests/Services/OpenApiValidatorTests.cs b/test/Microsoft.OpenApi.Tests/Services/OpenApiValidatorTests.cs
index 1fc8206d9..e5fcc346f 100644
--- a/test/Microsoft.OpenApi.Tests/Services/OpenApiValidatorTests.cs
+++ b/test/Microsoft.OpenApi.Tests/Services/OpenApiValidatorTests.cs
@@ -30,12 +30,13 @@ public void ResponseMustHaveADescription()
Title = "foo",
Version = "1.2.2"
};
- openApiDocument.Paths = new();
- openApiDocument.Paths.Add(
- "/test",
- new()
+ openApiDocument.Paths = new()
+ {
{
- Operations =
+ "/test",
+ new()
+ {
+ Operations =
{
[OperationType.Get] = new()
{
@@ -45,7 +46,9 @@ public void ResponseMustHaveADescription()
}
}
}
- });
+ }
+ }
+ };
var validator = new OpenApiValidator(ValidationRuleSet.GetDefaultRuleSet());
var walker = new OpenApiWalker(validator);
@@ -98,8 +101,8 @@ public void ValidateCustomExtension()
{
var ruleset = ValidationRuleSet.GetDefaultRuleSet();
- ruleset.Add(typeof(OpenApiAny).Name,
- new ValidationRule(
+ ruleset.Add(typeof(OpenApiAny),
+ new ValidationRule("FooExtensionRule",
(context, item) =>
{
if (item.Node["Bar"].ToString() == "hey")
@@ -138,6 +141,44 @@ public void ValidateCustomExtension()
new OpenApiValidatorError("FooExtensionRule", "#/info/x-foo", "Don't say hey")
});
}
+
+ [Fact]
+ public void RemoveRuleByName_Invalid()
+ {
+ Assert.Throws(() => new ValidationRule(null, (vc, oaa) => { }));
+ Assert.Throws(() => new ValidationRule(string.Empty, (vc, oaa) => { }));
+ }
+
+ [Fact]
+ public void RemoveRuleByName()
+ {
+ var ruleset = ValidationRuleSet.GetDefaultRuleSet();
+ int expected = ruleset.Rules.Count - 1;
+ ruleset.Remove("KeyMustBeRegularExpression");
+
+ Assert.Equal(expected, ruleset.Rules.Count);
+
+ ruleset.Remove("KeyMustBeRegularExpression");
+ ruleset.Remove("UnknownName");
+
+ Assert.Equal(expected, ruleset.Rules.Count);
+ }
+
+ [Fact]
+ public void RemoveRuleByType()
+ {
+ var ruleset = ValidationRuleSet.GetDefaultRuleSet();
+ int expected = ruleset.Rules.Count - 1;
+
+ ruleset.Remove(typeof(OpenApiComponents));
+
+ Assert.Equal(expected, ruleset.Rules.Count);
+
+ ruleset.Remove(typeof(OpenApiComponents));
+ ruleset.Remove(typeof(int));
+
+ Assert.Equal(expected, ruleset.Rules.Count);
+ }
}
internal class FooExtension : IOpenApiExtension, IOpenApiElement
diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiPathsValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiPathsValidationTests.cs
index 23a0a3e0f..6d0282748 100644
--- a/test/Microsoft.OpenApi.Tests/Validations/OpenApiPathsValidationTests.cs
+++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiPathsValidationTests.cs
@@ -45,5 +45,38 @@ public void ValidatePathsAreUnique()
errors.Should().NotBeEmpty();
errors.Select(e => e.Message).Should().BeEquivalentTo(error);
}
+ [Fact]
+ public void ValidatePathsAreUniqueDoesNotConsiderMultiParametersAsIdentical()
+ {
+ // Arrange
+ var paths = new OpenApiPaths
+ {
+ {"/drives/{drive-id}/items/{driveItem-id}/workbook/worksheets/{workbookWorksheet-id}/charts/{workbookChart-id}/image(width={width},height={height},fittingMode='{fittingMode}')",new OpenApiPathItem() },
+ {"/drives/{drive-id}/items/{driveItem-id}/workbook/worksheets/{workbookWorksheet-id}/charts/{workbookChart-id}/image(width={width},height={height})",new OpenApiPathItem() },
+ {"/drives/{drive-id}/items/{driveItem-id}/workbook/worksheets/{workbookWorksheet-id}/charts/{workbookChart-id}/image(width={width})", new OpenApiPathItem() },
+ };
+
+ // Act
+ var errors = paths.Validate(ValidationRuleSet.GetDefaultRuleSet());
+
+ // Assert
+ errors.Should().BeEmpty();
+ }
+ [Fact]
+ public void ValidatePathsAreUniqueConsidersMultiParametersAsIdentical()
+ {
+ // Arrange
+ var paths = new OpenApiPaths
+ {
+ {"/drives/{drive-id}/items/{driveItem-id}/workbook/worksheets/{workbookWorksheet-id}/charts/{workbookChart-id}/image(width={width},height={height})",new OpenApiPathItem() },
+ {"/drives/{drive-id}/items/{driveItem-id}/workbook/worksheets/{workbookWorksheet-id}/charts/{workbookChart-id}/image(width={width},height={size})",new OpenApiPathItem() },
+ };
+
+ // Act
+ var errors = paths.Validate(ValidationRuleSet.GetDefaultRuleSet());
+
+ // Assert
+ errors.Should().NotBeEmpty();
+ }
}
}
diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiReferenceValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiReferenceValidationTests.cs
index 2ba962ca5..e011d80ee 100644
--- a/test/Microsoft.OpenApi.Tests/Validations/OpenApiReferenceValidationTests.cs
+++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiReferenceValidationTests.cs
@@ -1,6 +1,7 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
+// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
+using System;
using System.Collections.Generic;
using System.Linq;
using Json.Schema;
@@ -56,9 +57,9 @@ public void ReferencedSchemaShouldOnlyBeValidatedOnce()
};
// Act
- var rules = new Dictionary>()
+ var rules = new Dictionary>()
{
- { typeof(JsonSchema).Name,
+ { typeof(JsonSchema),
new List() { new AlwaysFailRule() }
}
};
@@ -106,9 +107,9 @@ public void UnresolvedSchemaReferencedShouldNotBeValidated()
};
// Act
- var rules = new Dictionary>()
+ var rules = new Dictionary>()
{
- { typeof(JsonSchema).Name,
+ { typeof(JsonSchema),
new List() { new AlwaysFailRule() }
}
};
@@ -122,7 +123,7 @@ public void UnresolvedSchemaReferencedShouldNotBeValidated()
public class AlwaysFailRule : ValidationRule
{
- public AlwaysFailRule() : base((c, _) => c.CreateError("x", "y"))
+ public AlwaysFailRule() : base("AlwaysFailRule", (c, _) => c.CreateError("x", "y"))
{
}
}
diff --git a/test/Microsoft.OpenApi.Tests/Validations/ValidationRuleSetTests.cs b/test/Microsoft.OpenApi.Tests/Validations/ValidationRuleSetTests.cs
index 55ae552d1..4bc7e7cfd 100644
--- a/test/Microsoft.OpenApi.Tests/Validations/ValidationRuleSetTests.cs
+++ b/test/Microsoft.OpenApi.Tests/Validations/ValidationRuleSetTests.cs
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
+using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.OpenApi.Models;
@@ -10,24 +11,24 @@ namespace Microsoft.OpenApi.Validations.Tests
{
public class ValidationRuleSetTests
{
- private readonly ValidationRule _contactValidationRule = new ValidationRule(
+ private readonly ValidationRule _contactValidationRule = new ValidationRule(nameof(_contactValidationRule),
(context, item) => { });
- private readonly ValidationRule _headerValidationRule = new ValidationRule(
+ private readonly ValidationRule _headerValidationRule = new ValidationRule(nameof(_headerValidationRule),
(context, item) => { });
- private readonly ValidationRule _parameterValidationRule = new ValidationRule(
+ private readonly ValidationRule _parameterValidationRule = new ValidationRule(nameof(_parameterValidationRule),
(context, item) => { });
- private readonly IDictionary> _rulesDictionary;
+ private readonly IDictionary> _rulesDictionary;
public ValidationRuleSetTests()
{
- _rulesDictionary = new Dictionary>()
+ _rulesDictionary = new Dictionary>()
{
- {"contact", new List { _contactValidationRule } },
- {"header", new List { _headerValidationRule } },
- {"parameter", new List { _parameterValidationRule } }
+ {typeof(OpenApiContact), new List { _contactValidationRule } },
+ {typeof(OpenApiHeader), new List { _headerValidationRule } },
+ {typeof(OpenApiParameter), new List { _parameterValidationRule } }
};
}
@@ -41,7 +42,7 @@ public void RuleSetConstructorsReturnsTheCorrectRules()
var ruleSet_4 = new ValidationRuleSet();
// Assert
- Assert.NotNull(ruleSet_1?.Rules);
+ Assert.NotNull(ruleSet_1?.Rules);
Assert.NotNull(ruleSet_2?.Rules);
Assert.NotNull(ruleSet_3?.Rules);
Assert.NotNull(ruleSet_4);
@@ -62,7 +63,7 @@ public void RemoveValidatioRuleGivenTheValidationRuleWorks()
{
// Arrange
var ruleSet = new ValidationRuleSet(_rulesDictionary);
- var responseValidationRule = new ValidationRule((context, item) => { });
+ var responseValidationRule = new ValidationRule("ValidateResponses", (context, item) => { });
// Act and Assert
Assert.True(ruleSet.Remove(_contactValidationRule));
@@ -77,9 +78,9 @@ public void RemoveValidationRuleGivenTheKeyAndValidationRuleWorks()
var ruleSet = new ValidationRuleSet(_rulesDictionary);
// Act
- ruleSet.Remove("contact", _contactValidationRule);
- ruleSet.Remove("parameter", _headerValidationRule); // validation rule not in parameter key; shouldn't remove
- ruleSet.Remove("foo", _parameterValidationRule); // key does not exist; shouldn't remove
+ ruleSet.Remove(typeof(OpenApiContact), _contactValidationRule);
+ ruleSet.Remove("parameter"); // validation rule not in parameter key; shouldn't remove
+ ruleSet.Remove("foo"); // key does not exist; shouldn't remove
var rules = ruleSet.Rules;
@@ -94,16 +95,16 @@ public void RemoveRulesGivenAKeyWorks()
{
// Arrange
var ruleSet = new ValidationRuleSet(_rulesDictionary);
- var responseValidationRule = new ValidationRule((context, item) => { });
- ruleSet.Add("response", new List { responseValidationRule });
- Assert.True(ruleSet.ContainsKey("response"));
+ var responseValidationRule = new ValidationRule("ValidateResponses", (context, item) => { });
+ ruleSet.Add(typeof(OpenApiResponse), new List { responseValidationRule });
+ Assert.True(ruleSet.ContainsKey(typeof(OpenApiResponse)));
Assert.True(ruleSet.Rules.Contains(responseValidationRule)); // guard
// Act
- ruleSet.Remove("response");
+ ruleSet.Remove(typeof(OpenApiResponse));
// Assert
- Assert.False(ruleSet.ContainsKey("response"));
+ Assert.False(ruleSet.ContainsKey(typeof(OpenApiResponse)));
}
[Fact]
@@ -111,24 +112,24 @@ public void AddNewValidationRuleWorks()
{
// Arrange
var ruleSet = new ValidationRuleSet(_rulesDictionary);
- var responseValidationRule = new ValidationRule((context, item) => { });
- var tagValidationRule = new ValidationRule((context, item) => { });
- var pathsValidationRule = new ValidationRule((context, item) => { });
+ var responseValidationRule = new ValidationRule("ValidateResponses", (context, item) => { });
+ var tagValidationRule = new ValidationRule("ValidateTags", (context, item) => { });
+ var pathsValidationRule = new ValidationRule("ValidatePaths", (context, item) => { });
// Act
- ruleSet.Add("response", new List { responseValidationRule });
- ruleSet.Add("tag", new List { tagValidationRule });
- var rulesDictionary = new Dictionary>()
+ ruleSet.Add(typeof(OpenApiResponse), new List { responseValidationRule });
+ ruleSet.Add(typeof(OpenApiTag), new List { tagValidationRule });
+ var rulesDictionary = new Dictionary>()
{
- {"paths", new List { pathsValidationRule } }
+ {typeof(OpenApiPaths), new List { pathsValidationRule } }
};
ValidationRuleSet.AddValidationRules(ruleSet, rulesDictionary);
-
+
// Assert
- Assert.True(ruleSet.ContainsKey("response"));
- Assert.True(ruleSet.ContainsKey("tag"));
- Assert.True(ruleSet.ContainsKey("paths"));
+ Assert.True(ruleSet.ContainsKey(typeof(OpenApiResponse)));
+ Assert.True(ruleSet.ContainsKey(typeof(OpenApiTag)));
+ Assert.True(ruleSet.ContainsKey(typeof(OpenApiPaths)));
Assert.True(ruleSet.Rules.Contains(responseValidationRule));
Assert.True(ruleSet.Rules.Contains(tagValidationRule));
Assert.True(ruleSet.Rules.Contains(pathsValidationRule));
@@ -139,16 +140,16 @@ public void UpdateValidationRuleWorks()
{
// Arrange
var ruleSet = new ValidationRuleSet(_rulesDictionary);
- var responseValidationRule = new ValidationRule((context, item) => { });
- ruleSet.Add("response", new List { responseValidationRule });
+ var responseValidationRule = new ValidationRule("ValidateResponses", (context, item) => { });
+ ruleSet.Add(typeof(OpenApiResponse), new List { responseValidationRule });
// Act
- var pathsValidationRule = new ValidationRule((context, item) => { });
- ruleSet.Update("response", pathsValidationRule, responseValidationRule);
+ var pathsValidationRule = new ValidationRule("ValidatePaths", (context, item) => { });
+ ruleSet.Update(typeof(OpenApiResponse), pathsValidationRule, responseValidationRule);
// Assert
- Assert.True(ruleSet.Contains("response", pathsValidationRule));
- Assert.False(ruleSet.Contains("response", responseValidationRule));
+ Assert.True(ruleSet.Contains(typeof(OpenApiResponse), pathsValidationRule));
+ Assert.False(ruleSet.Contains(typeof(OpenApiResponse), responseValidationRule));
}
[Fact]
@@ -158,8 +159,8 @@ public void TryGetValueWorks()
var ruleSet = new ValidationRuleSet(_rulesDictionary);
// Act
- ruleSet.TryGetValue("contact", out var validationRules);
-
+ ruleSet.TryGetValue(typeof(OpenApiContact), out var validationRules);
+
// Assert
Assert.True(validationRules.Any());
Assert.True(validationRules.Contains(_contactValidationRule));
@@ -170,12 +171,12 @@ public void ClearAllRulesWorks()
{
// Arrange
var ruleSet = new ValidationRuleSet();
- var tagValidationRule = new ValidationRule((context, item) => { });
- var pathsValidationRule = new ValidationRule((context, item) => { });
- var rulesDictionary = new Dictionary>()
+ var tagValidationRule = new ValidationRule("ValidateTags", (context, item) => { });
+ var pathsValidationRule = new ValidationRule("ValidatePaths", (context, item) => { });
+ var rulesDictionary = new Dictionary>()
{
- {"paths", new List { pathsValidationRule } },
- {"tag", new List { tagValidationRule } }
+ {typeof(OpenApiPaths), new List { pathsValidationRule } },
+ {typeof(OpenApiTag), new List { tagValidationRule } }
};
ValidationRuleSet.AddValidationRules(ruleSet, rulesDictionary);
diff --git a/test/Microsoft.OpenApi.Tests/Writers/OpenApiJsonWriterTests.cs b/test/Microsoft.OpenApi.Tests/Writers/OpenApiJsonWriterTests.cs
index 784750ab6..11b429300 100644
--- a/test/Microsoft.OpenApi.Tests/Writers/OpenApiJsonWriterTests.cs
+++ b/test/Microsoft.OpenApi.Tests/Writers/OpenApiJsonWriterTests.cs
@@ -7,7 +7,11 @@
using System.Globalization;
using System.IO;
using System.Linq;
+using System.Text;
using FluentAssertions;
+using Json.Schema;
+using Microsoft.OpenApi.Any;
+using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Writers;
using Newtonsoft.Json;
using Xunit;
@@ -265,5 +269,22 @@ public void WriteDateTimeAsJsonShouldMatchExpected(DateTimeOffset dateTimeOffset
// Assert
writtenString.Should().Be(expectedString);
}
+
+ [Fact]
+ public void OpenApiJsonWriterOutputsValidJsonValueWhenSchemaHasNanOrInfinityValues()
+ {
+ // Arrange
+ var schema = new JsonSchemaBuilder().Enum("NaN", "Infinity", "-Infinity");
+
+ // Act
+ var schemaBuilder = new StringBuilder();
+ var jsonWriter = new OpenApiJsonWriter(new StringWriter(schemaBuilder));
+ jsonWriter.WriteJsonSchema(schema, OpenApiSpecVersion.OpenApi3_0);
+ var jsonString = schemaBuilder.ToString();
+
+ // Assert
+ var exception = Record.Exception(() => System.Text.Json.JsonSerializer.Deserialize>(jsonString));
+ Assert.Null(exception);
+ }
}
}
diff --git a/test/Microsoft.OpenApi.Tests/Writers/OpenApiWriterAnyExtensionsTests.cs b/test/Microsoft.OpenApi.Tests/Writers/OpenApiWriterAnyExtensionsTests.cs
index 633610aca..6e1a883c4 100644
--- a/test/Microsoft.OpenApi.Tests/Writers/OpenApiWriterAnyExtensionsTests.cs
+++ b/test/Microsoft.OpenApi.Tests/Writers/OpenApiWriterAnyExtensionsTests.cs
@@ -18,7 +18,6 @@
namespace Microsoft.OpenApi.Tests.Writers
{
[Collection("DefaultSettings")]
- [UsesVerify]
public class OpenApiWriterAnyExtensionsTests
{
static bool[] shouldProduceTerseOutputValues = new[] { true, false };