diff --git a/.azure-pipelines/ci-build.yml b/.azure-pipelines/ci-build.yml index 2f1b6b9b5..4ce92b8e9 100644 --- a/.azure-pipelines/ci-build.yml +++ b/.azure-pipelines/ci-build.yml @@ -31,9 +31,14 @@ stages: - job: build steps: - task: UseDotNet@2 - displayName: 'Use .NET 6' + displayName: 'Use .NET 2' # needed for ESRP signing inputs: - version: 6.x + version: 2.x + + - task: UseDotNet@2 + displayName: 'Use .NET 7' + inputs: + version: 7.x - task: PoliCheck@1 displayName: 'Run PoliCheck "/src"' diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index b972c9848..097fc3b96 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -16,7 +16,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: 6.0.x + dotnet-version: 7.0.x - name: Data gatherer id: data_gatherer @@ -49,7 +49,7 @@ jobs: - if: steps.conditionals_handler.outputs.is_default_branch == 'true' name: Bump GH tag id: tag_generator - uses: mathieudutour/github-tag-action@v6.0 + uses: mathieudutour/github-tag-action@v6.1 with: github_token: ${{ secrets.GITHUB_TOKEN }} default_bump: false diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index eb56ea14f..89d7e62c1 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -19,7 +19,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: 6.0.x + dotnet-version: 7.0.x - name: Initialize CodeQL id: init_codeql diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index ec86e0b7f..b289449cf 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -6,7 +6,7 @@ on: paths: ['src/Microsoft.OpenApi.Hidi/**', '.github/workflows/**'] env: REGISTRY: msgraphprod.azurecr.io - IMAGE_NAME: public/hidi + IMAGE_NAME: public/openapi/hidi jobs: push_to_registry: environment: @@ -17,7 +17,7 @@ jobs: - name: Check out the repo uses: actions/checkout@v3 - name: Login to GitHub package feed - uses: docker/login-action@v2.0.0 + uses: docker/login-action@v2.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@v3.1.1 + uses: docker/build-push-action@v3.2.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@v3.1.1 + uses: docker/build-push-action@v3.2.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 new file mode 100644 index 000000000..d7efd6213 --- /dev/null +++ b/.github/workflows/sonarcloud.yml @@ -0,0 +1,78 @@ +name: Sonarcloud +on: + workflow_dispatch: + push: + branches: + - main + paths-ignore: ['.vscode/**'] + pull_request: + types: [opened, synchronize, reopened] + paths-ignore: ['.vscode/**'] + +env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + +jobs: + checksecret: + name: check if SONAR_TOKEN is set in github secrets + runs-on: ubuntu-latest + outputs: + is_SONAR_TOKEN_set: ${{ steps.checksecret_job.outputs.is_SONAR_TOKEN_set }} + steps: + - name: Check whether unity activation requests should be done + id: checksecret_job + run: | + echo "is_SONAR_TOKEN_set=${{ env.SONAR_TOKEN != '' }}" >> $GITHUB_OUTPUT + build: + needs: [checksecret] + if: needs.checksecret.outputs.is_SONAR_TOKEN_set == 'true' + name: Build + runs-on: windows-latest + steps: + - name: Set up JDK 11 + uses: actions/setup-java@v3 + with: + distribution: 'adopt' + java-version: 11 + - name: Setup .NET 5 # At the moment the scanner requires dotnet 5 https://www.nuget.org/packages/dotnet-sonarscanner + uses: actions/setup-dotnet@v3 + with: + dotnet-version: 5.0.x + - name: Setup .NET + uses: actions/setup-dotnet@v3 + with: + dotnet-version: 7.0.x + - uses: actions/checkout@v3 + with: + fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis + - name: Cache SonarCloud packages + uses: actions/cache@v3 + 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 + with: + path: ./.sonar/scanner + key: ${{ runner.os }}-sonar-scanner + restore-keys: ${{ runner.os }}-sonar-scanner + - name: Install SonarCloud scanner + if: steps.cache-sonar-scanner.outputs.cache-hit != 'true' + shell: pwsh + run: | + New-Item -Path ./.sonar/scanner -ItemType Directory + dotnet tool update dotnet-sonarscanner --tool-path ./.sonar/scanner + - name: Build and analyze + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any + CollectCoverage: true + CoverletOutputFormat: 'opencover' # https://github.com/microsoft/vstest/issues/4014#issuecomment-1307913682 + shell: pwsh + run: | + ./.sonar/scanner/dotnet-sonarscanner begin /k:"microsoft_OpenAPI.NET" /o:"microsoft" /d:sonar.login="${{ secrets.SONAR_TOKEN }}" /d:sonar.host.url="https://sonarcloud.io" /d:sonar.cs.opencover.reportsPaths="test/**/coverage.opencover.xml" + dotnet workload restore + dotnet build + dotnet test Microsoft.OpenApi.sln --no-build --verbosity normal /p:CollectCoverage=true /p:CoverletOutputFormat=opencover + ./.sonar/scanner/dotnet-sonarscanner end /d:sonar.login="${{ secrets.SONAR_TOKEN }}" \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json index b59349979..974002cfe 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/net6.0/Microsoft.OpenApi.Hidi.dll", + "program": "${workspaceFolder}/src/Microsoft.OpenApi.Hidi/bin/Debug/net7.0/Microsoft.OpenApi.Hidi.dll", "args": [], "cwd": "${workspaceFolder}/src/Microsoft.OpenApi.Hidi", // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console diff --git a/Dockerfile b/Dockerfile index 8326ce3b9..6b3507124 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,14 +1,14 @@ -FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build-env +FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build-env WORKDIR /app COPY ./src ./hidi/src WORKDIR /app/hidi RUN dotnet publish ./src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj -c Release -FROM mcr.microsoft.com/dotnet/runtime:6.0 as runtime +FROM mcr.microsoft.com/dotnet/runtime:7.0 as runtime WORKDIR /app -COPY --from=build-env /app/hidi/src/Microsoft.OpenApi.Hidi/bin/Release/net6.0 ./ +COPY --from=build-env /app/hidi/src/Microsoft.OpenApi.Hidi/bin/Release/net7.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 55d529a06..4da2ea986 100644 --- a/src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj +++ b/src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj @@ -2,7 +2,7 @@ Exe - net6.0 + net7.0 9.0 true http://go.microsoft.com/fwlink/?LinkID=288890 @@ -15,7 +15,7 @@ Microsoft.OpenApi.Hidi hidi ./../../artifacts - 1.1.0-preview2 + 1.1.0 OpenAPI.NET CLI tool for slicing OpenAPI documents © Microsoft Corporation. All rights reserved. OpenAPI .NET @@ -37,13 +37,14 @@ - - - - + + + + - - + + + @@ -51,4 +52,12 @@ + + + + <_Parameter1>Microsoft.OpenApi.Hidi.Tests + + + + diff --git a/src/Microsoft.OpenApi.Hidi/OpenApiService.cs b/src/Microsoft.OpenApi.Hidi/OpenApiService.cs index 034350f1a..896c4ca8b 100644 --- a/src/Microsoft.OpenApi.Hidi/OpenApiService.cs +++ b/src/Microsoft.OpenApi.Hidi/OpenApiService.cs @@ -107,7 +107,7 @@ CancellationToken cancellationToken { stream = await GetStream(openapi, logger, cancellationToken); - using (logger.BeginScope($"Parse OpenAPI: {openapi}",openapi)) + using (logger.BeginScope($"Parse OpenAPI: {openapi}", openapi)) { stopwatch.Restart(); var result = await new OpenApiStreamReader(new OpenApiReaderSettings @@ -245,8 +245,8 @@ private static Stream ApplyFilter(string csdl, string entitySetOrSingleton, XslC /// Implementation of the validate command /// public static async Task ValidateOpenApiDocument( - string openapi, - LogLevel logLevel, + string openapi, + LogLevel logLevel, CancellationToken cancellationToken) { using var loggerFactory = Logger.ConfigureLogger(logLevel); @@ -270,7 +270,7 @@ public static async Task ValidateOpenApiDocument( RuleSet = ValidationRuleSet.GetDefaultRuleSet() } ).ReadAsync(stream); - + logger.LogTrace("{timestamp}ms: Completed parsing.", stopwatch.ElapsedMilliseconds); document = result.OpenApiDocument; @@ -284,7 +284,7 @@ public static async Task ValidateOpenApiDocument( logger.LogError(error.ToString()); } } - } + } stopwatch.Stop(); } @@ -322,7 +322,7 @@ public static async Task ConvertCsdlToOpenApi(Stream csdl) DeclarePathParametersOnPathItem = true, EnableKeyAsSegment = true, EnableOperationId = true, - ErrorResponsesAsDefault = false, + ErrorResponsesAsDefault = false, PrefixEntityTypeNameBeforeKey = true, TagDepth = 2, EnablePagination = true, @@ -358,7 +358,7 @@ public static OpenApiDocument FixReferences(OpenApiDocument document) return doc; } - + /// /// Takes in a file stream, parses the stream into a JsonDocument and gets a list of paths and Http methods /// @@ -381,13 +381,13 @@ public static Dictionary> ParseJsonCollectionFile(Stream st private static Dictionary> EnumerateJsonDocument(JsonElement itemElement, Dictionary> paths) { var itemsArray = itemElement.GetProperty("item"); - + foreach (var item in itemsArray.EnumerateArray()) { - if(item.ValueKind == JsonValueKind.Object) + if (item.ValueKind == JsonValueKind.Object) { - if(item.TryGetProperty("request", out var request)) - { + if (item.TryGetProperty("request", out var request)) + { // Fetch list of methods and urls from collection, store them in a dictionary var path = request.GetProperty("url").GetProperty("raw").ToString(); var method = request.GetProperty("method").ToString(); @@ -399,11 +399,11 @@ private static Dictionary> EnumerateJsonDocument(JsonElemen { paths[path].Add(method); } - } - else - { + } + else + { EnumerateJsonDocument(item, paths); - } + } } else { diff --git a/src/Microsoft.OpenApi.Hidi/Program.cs b/src/Microsoft.OpenApi.Hidi/Program.cs index 926c58deb..dba3abd2d 100644 --- a/src/Microsoft.OpenApi.Hidi/Program.cs +++ b/src/Microsoft.OpenApi.Hidi/Program.cs @@ -13,7 +13,8 @@ static class Program { static async Task Main(string[] args) { - var rootCommand = new RootCommand() { + var rootCommand = new RootCommand() + { }; // command option parameters and aliases @@ -65,7 +66,7 @@ static async Task Main(string[] args) logLevelOption }; - validateCommand.Handler = new ValidateCommandHandler + validateCommand.Handler = new ValidateCommandHandler { DescriptionOption = descriptionOption, LogLevelOption = logLevelOption @@ -81,7 +82,7 @@ static async Task Main(string[] args) versionOption, formatOption, terseOutputOption, - logLevelOption, + logLevelOption, filterByOperationIdsOption, filterByTagsOption, filterByCollectionOption, diff --git a/src/Microsoft.OpenApi.Readers/Microsoft.OpenApi.Readers.csproj b/src/Microsoft.OpenApi.Readers/Microsoft.OpenApi.Readers.csproj index 0f9564c2a..816a797da 100644 --- a/src/Microsoft.OpenApi.Readers/Microsoft.OpenApi.Readers.csproj +++ b/src/Microsoft.OpenApi.Readers/Microsoft.OpenApi.Readers.csproj @@ -11,7 +11,7 @@ Microsoft Microsoft.OpenApi.Readers Microsoft.OpenApi.Readers - 1.4.4-preview1 + 1.4.5 OpenAPI.NET Readers for JSON and YAML documents © Microsoft Corporation. All rights reserved. OpenAPI .NET diff --git a/src/Microsoft.OpenApi.Readers/V2/OpenApiHeaderDeserializer.cs b/src/Microsoft.OpenApi.Readers/V2/OpenApiHeaderDeserializer.cs index 5d6cc2ff3..436ae1ca3 100644 --- a/src/Microsoft.OpenApi.Readers/V2/OpenApiHeaderDeserializer.cs +++ b/src/Microsoft.OpenApi.Readers/V2/OpenApiHeaderDeserializer.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; @@ -57,7 +57,7 @@ internal static partial class OpenApiV2Deserializer { "maximum", (o, n) => { - GetOrCreateSchema(o).Maximum = decimal.Parse(n.GetScalarValue(), CultureInfo.InvariantCulture); + GetOrCreateSchema(o).Maximum = double.Parse(n.GetScalarValue(), CultureInfo.InvariantCulture); } }, { @@ -69,7 +69,7 @@ internal static partial class OpenApiV2Deserializer { "minimum", (o, n) => { - GetOrCreateSchema(o).Minimum = decimal.Parse(n.GetScalarValue(), CultureInfo.InvariantCulture); + GetOrCreateSchema(o).Minimum = double.Parse(n.GetScalarValue(), CultureInfo.InvariantCulture); } }, { @@ -117,7 +117,7 @@ internal static partial class OpenApiV2Deserializer { "multipleOf", (o, n) => { - GetOrCreateSchema(o).MultipleOf = decimal.Parse(n.GetScalarValue(), CultureInfo.InvariantCulture); + GetOrCreateSchema(o).MultipleOf = double.Parse(n.GetScalarValue(), CultureInfo.InvariantCulture); } }, { diff --git a/src/Microsoft.OpenApi.Readers/V2/OpenApiParameterDeserializer.cs b/src/Microsoft.OpenApi.Readers/V2/OpenApiParameterDeserializer.cs index 5be08c71e..38ab18639 100644 --- a/src/Microsoft.OpenApi.Readers/V2/OpenApiParameterDeserializer.cs +++ b/src/Microsoft.OpenApi.Readers/V2/OpenApiParameterDeserializer.cs @@ -82,13 +82,13 @@ internal static partial class OpenApiV2Deserializer { "minimum", (o, n) => { - GetOrCreateSchema(o).Minimum = decimal.Parse(n.GetScalarValue(), CultureInfo.InvariantCulture); + GetOrCreateSchema(o).Minimum = double.Parse(n.GetScalarValue(), CultureInfo.InvariantCulture); } }, { "maximum", (o, n) => { - GetOrCreateSchema(o).Maximum = decimal.Parse(n.GetScalarValue(), CultureInfo.InvariantCulture); + GetOrCreateSchema(o).Maximum = double.Parse(n.GetScalarValue(), CultureInfo.InvariantCulture); } }, { diff --git a/src/Microsoft.OpenApi.Readers/V2/OpenApiSchemaDeserializer.cs b/src/Microsoft.OpenApi.Readers/V2/OpenApiSchemaDeserializer.cs index 0878eda9a..cb2c563bf 100644 --- a/src/Microsoft.OpenApi.Readers/V2/OpenApiSchemaDeserializer.cs +++ b/src/Microsoft.OpenApi.Readers/V2/OpenApiSchemaDeserializer.cs @@ -26,13 +26,13 @@ internal static partial class OpenApiV2Deserializer { "multipleOf", (o, n) => { - o.MultipleOf = decimal.Parse(n.GetScalarValue(), NumberStyles.Float, CultureInfo.InvariantCulture); + o.MultipleOf = double.Parse(n.GetScalarValue(), NumberStyles.Float, CultureInfo.InvariantCulture); } }, { "maximum", (o, n) => { - o.Maximum = decimal.Parse(n.GetScalarValue(), NumberStyles.Float, CultureInfo.InvariantCulture); + o.Maximum = double.Parse(n.GetScalarValue(), NumberStyles.Float, CultureInfo.InvariantCulture); } }, { @@ -44,7 +44,7 @@ internal static partial class OpenApiV2Deserializer { "minimum", (o, n) => { - o.Minimum = decimal.Parse(n.GetScalarValue(), NumberStyles.Float, CultureInfo.InvariantCulture); + o.Minimum = double.Parse(n.GetScalarValue(), NumberStyles.Float, CultureInfo.InvariantCulture); } }, { diff --git a/src/Microsoft.OpenApi.Readers/V3/OpenApiSchemaDeserializer.cs b/src/Microsoft.OpenApi.Readers/V3/OpenApiSchemaDeserializer.cs index 8f465e38e..6d5f9a9ce 100644 --- a/src/Microsoft.OpenApi.Readers/V3/OpenApiSchemaDeserializer.cs +++ b/src/Microsoft.OpenApi.Readers/V3/OpenApiSchemaDeserializer.cs @@ -28,13 +28,13 @@ internal static partial class OpenApiV3Deserializer { "multipleOf", (o, n) => { - o.MultipleOf = decimal.Parse(n.GetScalarValue(), NumberStyles.Float, CultureInfo.InvariantCulture); + o.MultipleOf = double.Parse(n.GetScalarValue(), NumberStyles.Float, CultureInfo.InvariantCulture); } }, { "maximum", (o, n) => { - o.Maximum = decimal.Parse(n.GetScalarValue(), NumberStyles.Float, CultureInfo.InvariantCulture); + o.Maximum = double.Parse(n.GetScalarValue(), NumberStyles.Float, CultureInfo.InvariantCulture); } }, { @@ -46,7 +46,7 @@ internal static partial class OpenApiV3Deserializer { "minimum", (o, n) => { - o.Minimum = decimal.Parse(n.GetScalarValue(), NumberStyles.Float, CultureInfo.InvariantCulture); + o.Minimum = double.Parse(n.GetScalarValue(), NumberStyles.Float, CultureInfo.InvariantCulture); } }, { diff --git a/src/Microsoft.OpenApi.Readers/V3/OpenApiV3VersionService.cs b/src/Microsoft.OpenApi.Readers/V3/OpenApiV3VersionService.cs index 8b454bf68..29a96bfdc 100644 --- a/src/Microsoft.OpenApi.Readers/V3/OpenApiV3VersionService.cs +++ b/src/Microsoft.OpenApi.Readers/V3/OpenApiV3VersionService.cs @@ -114,11 +114,12 @@ public OpenApiReference ConvertToOpenApiReference( catch (OpenApiException ex) { Diagnostic.Errors.Add(new OpenApiError(ex)); - return null; } } // Where fragments point into a non-OpenAPI document, the id will be the complete fragment identifier string id = segments[1]; + var openApiReference = new OpenApiReference(); + // $ref: externalSource.yaml#/Pet if (id.StartsWith("/components/")) { @@ -137,11 +138,13 @@ public OpenApiReference ConvertToOpenApiReference( } id = localSegments[3]; } + else + { + openApiReference.IsFragrament = true; + } return new OpenApiReference { - Summary = summary, - Description = description, ExternalResource = segments[0], Type = type, Id = id diff --git a/src/Microsoft.OpenApi.Workbench/Microsoft.OpenApi.Workbench.csproj b/src/Microsoft.OpenApi.Workbench/Microsoft.OpenApi.Workbench.csproj index a47b5af48..ad6989d05 100644 --- a/src/Microsoft.OpenApi.Workbench/Microsoft.OpenApi.Workbench.csproj +++ b/src/Microsoft.OpenApi.Workbench/Microsoft.OpenApi.Workbench.csproj @@ -1,16 +1,16 @@  - net6.0-windows + net7.0-windows WinExe false true true - + all - + diff --git a/src/Microsoft.OpenApi/Any/OpenApiPrimitive.cs b/src/Microsoft.OpenApi/Any/OpenApiPrimitive.cs index e0abda167..bd6db956a 100644 --- a/src/Microsoft.OpenApi/Any/OpenApiPrimitive.cs +++ b/src/Microsoft.OpenApi/Any/OpenApiPrimitive.cs @@ -118,7 +118,7 @@ public void Write(IOpenApiWriter writer, OpenApiSpecVersion specVersion) case PrimitiveType.Date: var dateValue = (OpenApiDate)(IOpenApiPrimitive)this; - writer.WriteValue(dateValue.Value); + writer.WriteValue(dateValue.Value.ToShortDateString()); break; case PrimitiveType.DateTime: diff --git a/src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs b/src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs new file mode 100644 index 000000000..36f08bcb0 --- /dev/null +++ b/src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs @@ -0,0 +1,130 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +using System; +using System.Collections.Generic; +using Microsoft.OpenApi.Models; + +namespace Microsoft.OpenApi.Extensions +{ + /// + /// Extension methods for . + /// + public static class OpenApiTypeMapper + { + private static readonly Dictionary> _simpleTypeToOpenApiSchema = new() + { + [typeof(bool)] = () => new OpenApiSchema { Type = "boolean" }, + [typeof(byte)] = () => new OpenApiSchema { Type = "string", Format = "byte" }, + [typeof(int)] = () => new OpenApiSchema { Type = "integer", Format = "int32" }, + [typeof(uint)] = () => new OpenApiSchema { Type = "integer", Format = "int32" }, + [typeof(long)] = () => new OpenApiSchema { Type = "integer", Format = "int64" }, + [typeof(ulong)] = () => new OpenApiSchema { Type = "integer", Format = "int64" }, + [typeof(float)] = () => new OpenApiSchema { Type = "number", Format = "float" }, + [typeof(double)] = () => new OpenApiSchema { Type = "number", Format = "double" }, + [typeof(decimal)] = () => new OpenApiSchema { Type = "number", Format = "decimal" }, + [typeof(DateTime)] = () => new OpenApiSchema { Type = "string", Format = "date-time" }, + [typeof(DateTimeOffset)] = () => new OpenApiSchema { Type = "string", Format = "date-time" }, + [typeof(Guid)] = () => new OpenApiSchema { Type = "string", Format = "uuid" }, + [typeof(char)] = () => new OpenApiSchema { Type = "string" }, + + // Nullable types + [typeof(bool?)] = () => new OpenApiSchema { Type = "boolean", Nullable = true }, + [typeof(byte?)] = () => new OpenApiSchema { Type = "string", Format = "byte", Nullable = true }, + [typeof(int?)] = () => new OpenApiSchema { Type = "integer", Format = "int32", Nullable = true }, + [typeof(uint?)] = () => new OpenApiSchema { Type = "integer", Format = "int32", Nullable = true }, + [typeof(long?)] = () => new OpenApiSchema { Type = "integer", Format = "int64", Nullable = true }, + [typeof(ulong?)] = () => new OpenApiSchema { Type = "integer", Format = "int64", Nullable = true }, + [typeof(float?)] = () => new OpenApiSchema { Type = "number", Format = "float", Nullable = true }, + [typeof(decimal?)] = () => new OpenApiSchema { Type = "number", Format = "decimal", Nullable = true }, + [typeof(double?)] = () => new OpenApiSchema { Type = "number", Format = "double", Nullable = true }, + [typeof(DateTime?)] = () => new OpenApiSchema { Type = "string", Format = "date-time", Nullable = true }, + [typeof(DateTimeOffset?)] = () => new OpenApiSchema { Type = "string", Format = "date-time", Nullable = true }, + [typeof(Guid?)] = () => new OpenApiSchema { Type = "string", Format = "uuid", Nullable = true }, + [typeof(char?)] = () => new OpenApiSchema { Type = "string", Nullable = true }, + + [typeof(Uri)] = () => new OpenApiSchema { Type = "string", Format = "uri"}, // Uri is treated as simple string + [typeof(string)] = () => new OpenApiSchema { Type = "string" }, + [typeof(object)] = () => new OpenApiSchema { Type = "object" } + }; + + /// + /// Maps a simple type to an OpenAPI data type and format. + /// + /// Simple type. + /// + /// All the following types from http://swagger.io/specification/#data-types-12 are supported. + /// Other types including nullables and URL are also supported. + /// Common Name type format Comments + /// =========== ======= ====== ========================================= + /// integer integer int32 signed 32 bits + /// long integer int64 signed 64 bits + /// float number float + /// double number double + /// string string [empty] + /// byte string byte base64 encoded characters + /// binary string binary any sequence of octets + /// boolean boolean [empty] + /// date string date As defined by full-date - RFC3339 + /// dateTime string date-time As defined by date-time - RFC3339 + /// password string password Used to hint UIs the input needs to be obscured. + /// If the type is not recognized as "simple", System.String will be returned. + /// + public static OpenApiSchema MapTypeToOpenApiPrimitiveType(this Type type) + { + if (type == null) + { + throw new ArgumentNullException(nameof(type)); + } + + return _simpleTypeToOpenApiSchema.TryGetValue(type, out var result) + ? result() + : new OpenApiSchema { Type = "string" }; + } + + /// + /// Maps an OpenAPI data type and format to a simple type. + /// + /// The OpenApi data type + /// The simple type + /// + public static Type MapOpenApiPrimitiveTypeToSimpleType(this OpenApiSchema schema) + { + if (schema == null) + { + throw new ArgumentNullException(nameof(schema)); + } + + var type = (schema.Type?.ToLowerInvariant(), schema.Format?.ToLowerInvariant(), schema.Nullable) switch + { + ("boolean", null, false) => typeof(bool), + ("integer", "int32", false) => typeof(int), + ("integer", "int64", false) => typeof(long), + ("number", "float", false) => typeof(float), + ("number", "double", false) => typeof(double), + ("number", "decimal", false) => typeof(decimal), + ("string", "byte", false) => typeof(byte), + ("string", "date-time", false) => typeof(DateTimeOffset), + ("string", "uuid", false) => typeof(Guid), + ("string", "duration", false) => typeof(TimeSpan), + ("string", "char", false) => typeof(char), + ("string", null, false) => typeof(string), + ("object", null, false) => typeof(object), + ("string", "uri", false) => typeof(Uri), + ("integer", "int32", true) => typeof(int?), + ("integer", "int64", true) => typeof(long?), + ("number", "float", true) => typeof(float?), + ("number", "double", true) => typeof(double?), + ("number", "decimal", true) => typeof(decimal?), + ("string", "byte", true) => typeof(byte?), + ("string", "date-time", true) => typeof(DateTimeOffset?), + ("string", "uuid", true) => typeof(Guid?), + ("string", "char", true) => typeof(char?), + ("boolean", null, true) => typeof(bool?), + _ => typeof(string), + }; + + return type; + } + } +} diff --git a/src/Microsoft.OpenApi/Microsoft.OpenApi.csproj b/src/Microsoft.OpenApi/Microsoft.OpenApi.csproj index 1affa74c6..a938a968f 100644 --- a/src/Microsoft.OpenApi/Microsoft.OpenApi.csproj +++ b/src/Microsoft.OpenApi/Microsoft.OpenApi.csproj @@ -11,7 +11,7 @@ Microsoft Microsoft.OpenApi Microsoft.OpenApi - 1.4.4-preview1 + 1.4.5 .NET models with JSON and YAML writers for OpenAPI specification © Microsoft Corporation. All rights reserved. OpenAPI .NET diff --git a/src/Microsoft.OpenApi/Models/OpenApiParameter.cs b/src/Microsoft.OpenApi/Models/OpenApiParameter.cs index e0e472721..fa62f0b79 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiParameter.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiParameter.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.Runtime; using Microsoft.OpenApi.Any; @@ -238,9 +239,12 @@ public void SerializeAsV3WithoutReference(IOpenApiWriter writer) // allowEmptyValue writer.WriteProperty(OpenApiConstants.AllowEmptyValue, AllowEmptyValue, false); - + // style - writer.WriteProperty(OpenApiConstants.Style, Style?.GetDisplayName()); + if (_style.HasValue) + { + writer.WriteProperty(OpenApiConstants.Style, Style.Value.GetDisplayName()); + } // explode writer.WriteProperty(OpenApiConstants.Explode, Explode, Style.HasValue && Style.Value == ParameterStyle.Form); @@ -377,7 +381,7 @@ public void SerializeAsV2WithoutReference(IOpenApiWriter writer) // allowEmptyValue writer.WriteProperty(OpenApiConstants.AllowEmptyValue, AllowEmptyValue, false); - if (this.In == ParameterLocation.Query) + if (this.In == ParameterLocation.Query && "array".Equals(Schema?.Type, StringComparison.OrdinalIgnoreCase)) { if (this.Style == ParameterStyle.Form && this.Explode == true) { diff --git a/src/Microsoft.OpenApi/Models/OpenApiReference.cs b/src/Microsoft.OpenApi/Models/OpenApiReference.cs index a558e4394..8c47d620d 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiReference.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiReference.cs @@ -58,6 +58,11 @@ public class OpenApiReference : IOpenApiSerializable /// public bool IsLocal => ExternalResource == null; + /// + /// Gets a flag indicating whether a file is a valid OpenAPI document or a fragment + /// + public bool IsFragrament = false; + /// /// The OpenApiDocument that is hosting the OpenApiReference instance. This is used to enable dereferencing the reference. /// @@ -217,7 +222,12 @@ private string GetExternalReferenceV3() { if (Id != null) { - return ExternalResource + "#/components/" + Type.GetDisplayName() + "/"+ Id; + if (IsFragrament) + { + return ExternalResource + "#" + Id; + } + + return ExternalResource + "#/components/" + Type.GetDisplayName() + "/"+ Id; } return ExternalResource; diff --git a/src/Microsoft.OpenApi/Models/OpenApiSchema.cs b/src/Microsoft.OpenApi/Models/OpenApiSchema.cs index 6019d7362..b00419d85 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiSchema.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiSchema.cs @@ -41,7 +41,7 @@ public class OpenApiSchema : IOpenApiSerializable, IOpenApiReferenceable, IEffec /// /// Follow JSON Schema definition: https://tools.ietf.org/html/draft-fge-json-schema-validation-00 /// - public decimal? Maximum { get; set; } + public double? Maximum { get; set; } /// /// Follow JSON Schema definition: https://tools.ietf.org/html/draft-fge-json-schema-validation-00 @@ -51,7 +51,7 @@ public class OpenApiSchema : IOpenApiSerializable, IOpenApiReferenceable, IEffec /// /// Follow JSON Schema definition: https://tools.ietf.org/html/draft-fge-json-schema-validation-00 /// - public decimal? Minimum { get; set; } + public double? Minimum { get; set; } /// /// Follow JSON Schema definition: https://tools.ietf.org/html/draft-fge-json-schema-validation-00 @@ -77,7 +77,7 @@ public class OpenApiSchema : IOpenApiSerializable, IOpenApiReferenceable, IEffec /// /// Follow JSON Schema definition: https://tools.ietf.org/html/draft-fge-json-schema-validation-00 /// - public decimal? MultipleOf { get; set; } + public double? MultipleOf { get; set; } /// /// Follow JSON Schema definition: https://tools.ietf.org/html/draft-fge-json-schema-validation-00 @@ -280,7 +280,7 @@ public OpenApiSchema(OpenApiSchema schema) MaxProperties = schema?.MaxProperties ?? MaxProperties; MinProperties = schema?.MinProperties ?? MinProperties; AdditionalPropertiesAllowed = schema?.AdditionalPropertiesAllowed ?? AdditionalPropertiesAllowed; - AdditionalProperties = new(schema?.AdditionalProperties); + AdditionalProperties = schema?.AdditionalProperties != null ? new(schema?.AdditionalProperties) : null; Discriminator = schema?.Discriminator != null ? new(schema?.Discriminator) : null; Example = OpenApiAnyCloneHelper.CloneFromCopyConstructor(schema?.Example); Enum = schema?.Enum != null ? new List(schema.Enum) : null; diff --git a/src/Microsoft.OpenApi/Services/OpenApiWalker.cs b/src/Microsoft.OpenApi/Services/OpenApiWalker.cs index e454e37a8..d800082e5 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiWalker.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiWalker.cs @@ -310,7 +310,7 @@ internal void Walk(IOpenApiExtensible openApiExtensible) _visitor.Visit(openApiExtensible); - if (openApiExtensible != null) + if (openApiExtensible.Extensions != null) { foreach (var item in openApiExtensible.Extensions) { diff --git a/src/Microsoft.OpenApi/Validations/OpenApiValidator.cs b/src/Microsoft.OpenApi/Validations/OpenApiValidator.cs index ba2ed4129..a0aee12e7 100644 --- a/src/Microsoft.OpenApi/Validations/OpenApiValidator.cs +++ b/src/Microsoft.OpenApi/Validations/OpenApiValidator.cs @@ -20,7 +20,7 @@ public class OpenApiValidator : OpenApiVisitorBase, IValidationContext private readonly IList _warnings = new List(); /// - /// Create a vistor that will validate an OpenAPIDocument + /// Create a visitor that will validate an OpenAPIDocument /// /// public OpenApiValidator(ValidationRuleSet ruleSet) @@ -198,6 +198,100 @@ public void AddWarning(OpenApiValidatorWarning warning) /// The object to be validated public override void Visit(IList items) => Validate(items, items.GetType()); + /// + /// Execute validation rules against a + /// + /// The object to be validated + public override void Visit(OpenApiPathItem item) => Validate(item); + + /// + /// Execute validation rules against a + /// + /// The object to be validated + public override void Visit(OpenApiServerVariable item) => Validate(item); + + /// + /// Execute validation rules against a + /// + /// The object to be validated + public override void Visit(OpenApiSecurityScheme item) => Validate(item); + + /// + /// Execute validation rules against a + /// + /// The object to be validated + public override void Visit(OpenApiSecurityRequirement item) => Validate(item); + + /// + /// Execute validation rules against a + /// + /// The object to be validated + public override void Visit(OpenApiRequestBody item) => Validate(item); + + /// + /// Execute validation rules against a + /// + /// The object to be validated + public override void Visit(OpenApiPaths item) => Validate(item); + + /// + /// Execute validation rules against a + /// + /// The object to be validated + public override void Visit(OpenApiLink item) => Validate(item); + + /// + /// Execute validation rules against a + /// + /// The object to be validated + public override void Visit(OpenApiExample item) => Validate(item); + + /// + /// Execute validation rules against a + /// + /// The object to be validated + public override void Visit(OpenApiOperation item) => Validate(item); + /// + /// Execute validation rules against a + /// + /// The object to be validated + public override void Visit(IDictionary item) => Validate(item, item.GetType()); + /// + /// Execute validation rules against a + /// + /// The object to be validated + public override void Visit(IDictionary item) => Validate(item, item.GetType()); + /// + /// Execute validation rules against a + /// + /// The object to be validated + public override void Visit(IDictionary item) => Validate(item, item.GetType()); + /// + /// Execute validation rules against a + /// + /// The object to be validated + public override void Visit(IDictionary item) => Validate(item, item.GetType()); + /// + /// Execute validation rules against a + /// + /// The object to be validated + public override void Visit(IDictionary item) => Validate(item, item.GetType()); + /// + /// Execute validation rules against a + /// + /// The object to be validated + public override void Visit(IDictionary item) => Validate(item, item.GetType()); + /// + /// Execute validation rules against a + /// + /// The object to be validated + public override void Visit(IDictionary item) => Validate(item, item.GetType()); + /// + /// Execute validation rules against a + /// + /// The object to be validated + public override void Visit(IDictionary item) => Validate(item, item.GetType()); + private void Validate(T item) { var type = typeof(T); diff --git a/src/Microsoft.OpenApi/Writers/IOpenApiWriter.cs b/src/Microsoft.OpenApi/Writers/IOpenApiWriter.cs index 8fcbf10ed..7d086806e 100644 --- a/src/Microsoft.OpenApi/Writers/IOpenApiWriter.cs +++ b/src/Microsoft.OpenApi/Writers/IOpenApiWriter.cs @@ -39,9 +39,9 @@ public interface IOpenApiWriter void WriteValue(string value); /// - /// Write the decimal value. + /// Write the double value. /// - void WriteValue(decimal value); + void WriteValue(double value); /// /// Write the int value. diff --git a/src/Microsoft.OpenApi/Writers/OpenApiWriterBase.cs b/src/Microsoft.OpenApi/Writers/OpenApiWriterBase.cs index 4d7f11032..bfac14f67 100644 --- a/src/Microsoft.OpenApi/Writers/OpenApiWriterBase.cs +++ b/src/Microsoft.OpenApi/Writers/OpenApiWriterBase.cs @@ -148,9 +148,11 @@ public virtual void WriteValue(double value) } /// - /// Write decimal value. + /// Write + /// + /// value. /// - /// The decimal value. + /// The double value. public virtual void WriteValue(decimal value) { WriteValueSeparator(); diff --git a/src/Microsoft.OpenApi/Writers/SpecialCharacterStringExtensions.cs b/src/Microsoft.OpenApi/Writers/SpecialCharacterStringExtensions.cs index d4f78a5c1..56eda2937 100644 --- a/src/Microsoft.OpenApi/Writers/SpecialCharacterStringExtensions.cs +++ b/src/Microsoft.OpenApi/Writers/SpecialCharacterStringExtensions.cs @@ -187,12 +187,14 @@ internal static string GetYamlCompatibleString(this string input) return $"'{input}'"; } - // If string can be mistaken as a number, c-style hexadecimal notation, a boolean, or a timestamp, - // wrap it in quote to indicate that this is indeed a string, not a number, c-style hexadecimal notation, a boolean, or a timestamp - if (decimal.TryParse(input, NumberStyles.Float, CultureInfo.InvariantCulture, out var _) || - IsHexadecimalNotation(input) || + // If string can be mistaken as a number, c-style hexa + // + // notation, a boolean, or a timestamp, + // wrap it in quote to indicate that this is indeed a string, not a number, c-style hexadouble notation, a boolean, or a timestamp + if (double.TryParse(input, NumberStyles.Float, CultureInfo.InvariantCulture, out var _) || + IsHexadoubleNotation(input) || bool.TryParse(input, out var _) || - DateTime.TryParse(input, out var _)) + DateTime.TryParse(input, CultureInfo.InvariantCulture, DateTimeStyles.None, out var _)) { return $"'{input}'"; } @@ -227,7 +229,7 @@ internal static string GetJsonCompatibleString(this string value) return $"\"{value}\""; } - internal static bool IsHexadecimalNotation(string input) + internal static bool IsHexadoubleNotation(string input) { return input.StartsWith("0x") && int.TryParse(input.Substring(2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var _); } 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 1a4002daa..0902ff177 100644 --- a/test/Microsoft.OpenApi.Hidi.Tests/Microsoft.OpenApi.Hidi.Tests.csproj +++ b/test/Microsoft.OpenApi.Hidi.Tests/Microsoft.OpenApi.Hidi.Tests.csproj @@ -1,7 +1,7 @@ - net6.0 + net7.0 enable enable @@ -9,14 +9,18 @@ - - + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all @@ -34,6 +38,9 @@ + + Always + Always diff --git a/test/Microsoft.OpenApi.Hidi.Tests/Services/OpenApiServiceTests.cs b/test/Microsoft.OpenApi.Hidi.Tests/Services/OpenApiServiceTests.cs index af5437aa1..c2fb3798f 100644 --- a/test/Microsoft.OpenApi.Hidi.Tests/Services/OpenApiServiceTests.cs +++ b/test/Microsoft.OpenApi.Hidi.Tests/Services/OpenApiServiceTests.cs @@ -1,10 +1,9 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. -using System; -using System.IO; -using System.Threading.Tasks; +using Microsoft.Extensions.Configuration; using Microsoft.OpenApi.Hidi; +using Microsoft.OpenApi.OData; using Microsoft.OpenApi.Services; using Xunit; @@ -51,5 +50,26 @@ public async Task ReturnFilteredOpenApiDocBasedOnOperationIdsAndInputCsdlDocumen Assert.NotEmpty(subsetOpenApiDocument.Paths); Assert.Equal(expectedPathCount, subsetOpenApiDocument.Paths.Count); } + + [Theory] + [InlineData("UtilityFiles/appsettingstest.json")] + [InlineData(null)] + public void ReturnOpenApiConvertSettingsWhenSettingsFileIsProvided(string filePath) + { + // Arrange + var config = OpenApiService.GetConfiguration(filePath); + + // Act and Assert + var settings = config.GetSection("OpenApiConvertSettings").Get(); + + if (filePath == null) + { + Assert.Null(settings); + } + else + { + Assert.NotNull(settings); + } + } } } diff --git a/test/Microsoft.OpenApi.Hidi.Tests/UtilityFiles/appsettingstest.json b/test/Microsoft.OpenApi.Hidi.Tests/UtilityFiles/appsettingstest.json new file mode 100644 index 000000000..a71d0a9fa --- /dev/null +++ b/test/Microsoft.OpenApi.Hidi.Tests/UtilityFiles/appsettingstest.json @@ -0,0 +1,21 @@ +{ + "OpenApiConvertSettings": { + "AddSingleQuotesForStringParameters": "true", + "AddEnumDescriptionExtension": "true", + "DeclarePathParametersOnPathItem": "true", + "EnableKeyAsSegment": "true", + "EnableOperationId": "true", + "ErrorResponsesAsDefault": "false", + "PrefixEntityTypeNameBeforeKey": "true", + "TagDepth": 2, + "EnablePagination": "true", + "EnableDiscriminatorValue": "true", + "EnableDerivedTypesReferencesForRequestBody": "false", + "EnableDerivedTypesReferencesForResponses": "false", + "ShowRootPath": "false", + "ShowLinks": "false", + "ExpandDerivedTypesNavigationProperties": "false", + "EnableCount": "true", + "UseSuccessStatusCodeRange": "true" + } +} \ No newline at end of file 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 73aeeac9f..a09695f70 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/Microsoft.OpenApi.Readers.Tests.csproj +++ b/test/Microsoft.OpenApi.Readers.Tests/Microsoft.OpenApi.Readers.Tests.csproj @@ -1,6 +1,6 @@  - net6.0 + net7.0 false Microsoft @@ -158,6 +158,9 @@ Never + + Never + Never @@ -262,10 +265,18 @@ - - + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + - + diff --git a/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiDocumentTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiDocumentTests.cs index 256ad2630..fa67203de 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiDocumentTests.cs +++ b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiDocumentTests.cs @@ -134,8 +134,8 @@ public void ParseDocumentWithDifferentCultureShouldSucceed(string culture) ["sampleProperty"] = new OpenApiSchema() { Type = "double", - Minimum = (decimal)100.54, - Maximum = (decimal)60000000.35, + Minimum = (double)100.54, + Maximum = (double)60000000.35, ExclusiveMaximum = true, ExclusiveMinimum = false } diff --git a/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs index 15b08166e..82556a67e 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs +++ b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs @@ -6,6 +6,7 @@ using System.Globalization; using System.IO; using System.Linq; +using System.Text; using System.Threading; using FluentAssertions; using Microsoft.OpenApi.Any; @@ -163,8 +164,8 @@ public void ParseDocumentWithDifferentCultureShouldSucceed(string culture) ["sampleProperty"] = new OpenApiSchema() { Type = "double", - Minimum = (decimal)100.54, - Maximum = (decimal)60000000.35, + Minimum = (double)100.54, + Maximum = (double)60000000.35, ExclusiveMaximum = true, ExclusiveMinimum = false } diff --git a/test/Microsoft.OpenApi.Readers.Tests/V3Tests/Samples/OpenApiDocument/documentWithExternalRefs.yaml b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/Samples/OpenApiDocument/documentWithExternalRefs.yaml new file mode 100644 index 000000000..c0b7b3a25 --- /dev/null +++ b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/Samples/OpenApiDocument/documentWithExternalRefs.yaml @@ -0,0 +1,16 @@ + openapi: 3.0.1 + info: + title: anyOf-oneOf + license: + name: MIT + version: 1.0.0 + paths: { } + components: + schemas: + Nested: + type: object + properties: + AnyOf: + anyOf: + - $ref: file:///C:/MySchemas.json#/definitions/ArrayObject + - $ref: ../foo/schemas.yaml#/components/schemas/Number \ 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 7f41b101f..0fab323f7 100644 --- a/test/Microsoft.OpenApi.SmokeTests/Microsoft.OpenApi.SmokeTests.csproj +++ b/test/Microsoft.OpenApi.SmokeTests/Microsoft.OpenApi.SmokeTests.csproj @@ -1,15 +1,23 @@  - net6.0 + net7.0 - - + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + all diff --git a/test/Microsoft.OpenApi.Tests/Extensions/OpenApiTypeMapperTests.cs b/test/Microsoft.OpenApi.Tests/Extensions/OpenApiTypeMapperTests.cs new file mode 100644 index 000000000..c2b6d9597 --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Extensions/OpenApiTypeMapperTests.cs @@ -0,0 +1,55 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +using System; +using System.Collections.Generic; +using FluentAssertions; +using Microsoft.OpenApi.Extensions; +using Microsoft.OpenApi.Models; +using Xunit; + +namespace Microsoft.OpenApi.Tests.Extensions +{ + public class OpenApiTypeMapperTests + { + public static IEnumerable PrimitiveTypeData => new List + { + new object[] { typeof(int), new OpenApiSchema { Type = "integer", Format = "int32" } }, + new object[] { typeof(string), new OpenApiSchema { Type = "string" } }, + new object[] { typeof(double), new OpenApiSchema { Type = "number", Format = "double" } }, + new object[] { typeof(float?), new OpenApiSchema { Type = "number", Format = "float", Nullable = true } }, + new object[] { typeof(DateTimeOffset), new OpenApiSchema { Type = "string", Format = "date-time" } } + }; + + public static IEnumerable OpenApiDataTypes => new List + { + new object[] { new OpenApiSchema { Type = "integer", Format = "int32"}, typeof(int) }, + new object[] { new OpenApiSchema { Type = "string" }, typeof(string) }, + new object[] { new OpenApiSchema { Type = "number", Format = "double" }, typeof(double) }, + new object[] { new OpenApiSchema { Type = "number", Format = "float", Nullable = true }, typeof(float?) }, + new object[] { new OpenApiSchema { Type = "string", Format = "date-time" }, typeof(DateTimeOffset) } + }; + + [Theory] + [MemberData(nameof(PrimitiveTypeData))] + public void MapTypeToOpenApiPrimitiveTypeShouldSucceed(Type type, OpenApiSchema expected) + { + // Arrange & Act + var actual = OpenApiTypeMapper.MapTypeToOpenApiPrimitiveType(type); + + // Assert + actual.Should().BeEquivalentTo(expected); + } + + [Theory] + [MemberData(nameof(OpenApiDataTypes))] + public void MapOpenApiSchemaTypeToSimpleTypeShouldSucceed(OpenApiSchema schema, Type expected) + { + // Arrange & Act + var actual = OpenApiTypeMapper.MapOpenApiPrimitiveTypeToSimpleType(schema); + + // Assert + actual.Should().Be(expected); + } + } +} diff --git a/test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj b/test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj index b922d72d8..5f6d0c748 100644 --- a/test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj +++ b/test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj @@ -1,6 +1,6 @@  - net6.0 + net7.0 false Microsoft @@ -15,12 +15,20 @@ - - - - + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + - + all diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentAsV2JsonWorks_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentAsV2JsonWorks_produceTerseOutput=False.verified.txt index a991e0761..a2e4fbd4c 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentAsV2JsonWorks_produceTerseOutput=False.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentAsV2JsonWorks_produceTerseOutput=False.verified.txt @@ -46,8 +46,7 @@ "name": "limit", "description": "maximum number of results to return", "type": "integer", - "format": "int32", - "collectionFormat": "multi" + "format": "int32" } ], "responses": { diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentAsV2JsonWorks_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentAsV2JsonWorks_produceTerseOutput=True.verified.txt index 5ce01bf79..081bcda08 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentAsV2JsonWorks_produceTerseOutput=True.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentAsV2JsonWorks_produceTerseOutput=True.verified.txt @@ -1 +1 @@ -{"swagger":"2.0","info":{"title":"Swagger Petstore (Simple)","description":"A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification","termsOfService":"http://helloreverb.com/terms/","contact":{"name":"Swagger API team","url":"http://swagger.io","email":"foo@example.com"},"license":{"name":"MIT","url":"http://opensource.org/licenses/MIT"},"version":"1.0.0"},"host":"petstore.swagger.io","basePath":"/api","schemes":["http"],"paths":{"/pets":{"get":{"description":"Returns all pets from the system that the user has access to","operationId":"findPets","produces":["application/json","application/xml","text/html"],"parameters":[{"in":"query","name":"tags","description":"tags to filter by","type":"array","items":{"type":"string"},"collectionFormat":"multi"},{"in":"query","name":"limit","description":"maximum number of results to return","type":"integer","format":"int32","collectionFormat":"multi"}],"responses":{"200":{"description":"pet response","schema":{"type":"array","items":{"required":["id","name"],"type":"object","properties":{"id":{"format":"int64","type":"integer"},"name":{"type":"string"},"tag":{"type":"string"}}}}},"4XX":{"description":"unexpected client error","schema":{"required":["code","message"],"type":"object","properties":{"code":{"format":"int32","type":"integer"},"message":{"type":"string"}}}},"5XX":{"description":"unexpected server error","schema":{"required":["code","message"],"type":"object","properties":{"code":{"format":"int32","type":"integer"},"message":{"type":"string"}}}}}},"post":{"description":"Creates a new pet in the store. Duplicates are allowed","operationId":"addPet","consumes":["application/json"],"produces":["application/json","text/html"],"parameters":[{"in":"body","name":"body","description":"Pet to add to the store","required":true,"schema":{"required":["name"],"type":"object","properties":{"id":{"format":"int64","type":"integer"},"name":{"type":"string"},"tag":{"type":"string"}}}}],"responses":{"200":{"description":"pet response","schema":{"required":["id","name"],"type":"object","properties":{"id":{"format":"int64","type":"integer"},"name":{"type":"string"},"tag":{"type":"string"}}}},"4XX":{"description":"unexpected client error","schema":{"required":["code","message"],"type":"object","properties":{"code":{"format":"int32","type":"integer"},"message":{"type":"string"}}}},"5XX":{"description":"unexpected server error","schema":{"required":["code","message"],"type":"object","properties":{"code":{"format":"int32","type":"integer"},"message":{"type":"string"}}}}}}},"/pets/{id}":{"get":{"description":"Returns a user based on a single ID, if the user does not have access to the pet","operationId":"findPetById","produces":["application/json","application/xml","text/html"],"parameters":[{"in":"path","name":"id","description":"ID of pet to fetch","required":true,"type":"integer","format":"int64"}],"responses":{"200":{"description":"pet response","schema":{"required":["id","name"],"type":"object","properties":{"id":{"format":"int64","type":"integer"},"name":{"type":"string"},"tag":{"type":"string"}}}},"4XX":{"description":"unexpected client error","schema":{"required":["code","message"],"type":"object","properties":{"code":{"format":"int32","type":"integer"},"message":{"type":"string"}}}},"5XX":{"description":"unexpected server error","schema":{"required":["code","message"],"type":"object","properties":{"code":{"format":"int32","type":"integer"},"message":{"type":"string"}}}}}},"delete":{"description":"deletes a single pet based on the ID supplied","operationId":"deletePet","produces":["text/html"],"parameters":[{"in":"path","name":"id","description":"ID of pet to delete","required":true,"type":"integer","format":"int64"}],"responses":{"204":{"description":"pet deleted"},"4XX":{"description":"unexpected client error","schema":{"required":["code","message"],"type":"object","properties":{"code":{"format":"int32","type":"integer"},"message":{"type":"string"}}}},"5XX":{"description":"unexpected server error","schema":{"required":["code","message"],"type":"object","properties":{"code":{"format":"int32","type":"integer"},"message":{"type":"string"}}}}}}}},"definitions":{"pet":{"required":["id","name"],"type":"object","properties":{"id":{"format":"int64","type":"integer"},"name":{"type":"string"},"tag":{"type":"string"}}},"newPet":{"required":["name"],"type":"object","properties":{"id":{"format":"int64","type":"integer"},"name":{"type":"string"},"tag":{"type":"string"}}},"errorModel":{"required":["code","message"],"type":"object","properties":{"code":{"format":"int32","type":"integer"},"message":{"type":"string"}}}}} \ No newline at end of file +{"swagger":"2.0","info":{"title":"Swagger Petstore (Simple)","description":"A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification","termsOfService":"http://helloreverb.com/terms/","contact":{"name":"Swagger API team","url":"http://swagger.io","email":"foo@example.com"},"license":{"name":"MIT","url":"http://opensource.org/licenses/MIT"},"version":"1.0.0"},"host":"petstore.swagger.io","basePath":"/api","schemes":["http"],"paths":{"/pets":{"get":{"description":"Returns all pets from the system that the user has access to","operationId":"findPets","produces":["application/json","application/xml","text/html"],"parameters":[{"in":"query","name":"tags","description":"tags to filter by","type":"array","items":{"type":"string"},"collectionFormat":"multi"},{"in":"query","name":"limit","description":"maximum number of results to return","type":"integer","format":"int32"}],"responses":{"200":{"description":"pet response","schema":{"type":"array","items":{"required":["id","name"],"type":"object","properties":{"id":{"format":"int64","type":"integer"},"name":{"type":"string"},"tag":{"type":"string"}}}}},"4XX":{"description":"unexpected client error","schema":{"required":["code","message"],"type":"object","properties":{"code":{"format":"int32","type":"integer"},"message":{"type":"string"}}}},"5XX":{"description":"unexpected server error","schema":{"required":["code","message"],"type":"object","properties":{"code":{"format":"int32","type":"integer"},"message":{"type":"string"}}}}}},"post":{"description":"Creates a new pet in the store. Duplicates are allowed","operationId":"addPet","consumes":["application/json"],"produces":["application/json","text/html"],"parameters":[{"in":"body","name":"body","description":"Pet to add to the store","required":true,"schema":{"required":["name"],"type":"object","properties":{"id":{"format":"int64","type":"integer"},"name":{"type":"string"},"tag":{"type":"string"}}}}],"responses":{"200":{"description":"pet response","schema":{"required":["id","name"],"type":"object","properties":{"id":{"format":"int64","type":"integer"},"name":{"type":"string"},"tag":{"type":"string"}}}},"4XX":{"description":"unexpected client error","schema":{"required":["code","message"],"type":"object","properties":{"code":{"format":"int32","type":"integer"},"message":{"type":"string"}}}},"5XX":{"description":"unexpected server error","schema":{"required":["code","message"],"type":"object","properties":{"code":{"format":"int32","type":"integer"},"message":{"type":"string"}}}}}}},"/pets/{id}":{"get":{"description":"Returns a user based on a single ID, if the user does not have access to the pet","operationId":"findPetById","produces":["application/json","application/xml","text/html"],"parameters":[{"in":"path","name":"id","description":"ID of pet to fetch","required":true,"type":"integer","format":"int64"}],"responses":{"200":{"description":"pet response","schema":{"required":["id","name"],"type":"object","properties":{"id":{"format":"int64","type":"integer"},"name":{"type":"string"},"tag":{"type":"string"}}}},"4XX":{"description":"unexpected client error","schema":{"required":["code","message"],"type":"object","properties":{"code":{"format":"int32","type":"integer"},"message":{"type":"string"}}}},"5XX":{"description":"unexpected server error","schema":{"required":["code","message"],"type":"object","properties":{"code":{"format":"int32","type":"integer"},"message":{"type":"string"}}}}}},"delete":{"description":"deletes a single pet based on the ID supplied","operationId":"deletePet","produces":["text/html"],"parameters":[{"in":"path","name":"id","description":"ID of pet to delete","required":true,"type":"integer","format":"int64"}],"responses":{"204":{"description":"pet deleted"},"4XX":{"description":"unexpected client error","schema":{"required":["code","message"],"type":"object","properties":{"code":{"format":"int32","type":"integer"},"message":{"type":"string"}}}},"5XX":{"description":"unexpected server error","schema":{"required":["code","message"],"type":"object","properties":{"code":{"format":"int32","type":"integer"},"message":{"type":"string"}}}}}}}},"definitions":{"pet":{"required":["id","name"],"type":"object","properties":{"id":{"format":"int64","type":"integer"},"name":{"type":"string"},"tag":{"type":"string"}}},"newPet":{"required":["name"],"type":"object","properties":{"id":{"format":"int64","type":"integer"},"name":{"type":"string"},"tag":{"type":"string"}}},"errorModel":{"required":["code","message"],"type":"object","properties":{"code":{"format":"int32","type":"integer"},"message":{"type":"string"}}}}} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentAsV3JsonWorks2_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentAsV3JsonWorks2_produceTerseOutput=False.verified.txt new file mode 100644 index 000000000..a94db37b7 --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentAsV3JsonWorks2_produceTerseOutput=False.verified.txt @@ -0,0 +1,495 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "Swagger Petstore (Simple)", + "description": "A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification", + "termsOfService": "http://helloreverb.com/terms/", + "contact": { + "name": "Swagger API team", + "url": "http://swagger.io", + "email": "foo@example.com" + }, + "license": { + "name": "MIT", + "url": "http://opensource.org/licenses/MIT" + }, + "version": "1.0.0" + }, + "servers": [ + { + "url": "http://petstore.swagger.io/api" + } + ], + "paths": { + "/pets": { + "get": { + "description": "Returns all pets from the system that the user has access to", + "operationId": "findPets", + "parameters": [ + { + "name": "tags", + "in": "query", + "description": "tags to filter by", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, + { + "name": "limit", + "in": "query", + "description": "maximum number of results to return", + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "pet response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "required": [ + "id", + "name" + ], + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + } + } + }, + "application/xml": { + "schema": { + "type": "array", + "items": { + "required": [ + "id", + "name" + ], + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + } + } + } + } + }, + "4XX": { + "description": "unexpected client error", + "content": { + "text/html": { + "schema": { + "required": [ + "code", + "message" + ], + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + } + } + } + } + } + }, + "5XX": { + "description": "unexpected server error", + "content": { + "text/html": { + "schema": { + "required": [ + "code", + "message" + ], + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + } + } + } + } + } + } + } + }, + "post": { + "description": "Creates a new pet in the store. Duplicates are allowed", + "operationId": "addPet", + "requestBody": { + "description": "Pet to add to the store", + "content": { + "application/json": { + "schema": { + "required": [ + "name" + ], + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "pet response", + "content": { + "application/json": { + "schema": { + "required": [ + "id", + "name" + ], + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + } + } + } + }, + "4XX": { + "description": "unexpected client error", + "content": { + "text/html": { + "schema": { + "required": [ + "code", + "message" + ], + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + } + } + } + } + } + }, + "5XX": { + "description": "unexpected server error", + "content": { + "text/html": { + "schema": { + "required": [ + "code", + "message" + ], + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + } + } + } + } + } + } + } + } + }, + "/pets/{id}": { + "get": { + "description": "Returns a user based on a single ID, if the user does not have access to the pet", + "operationId": "findPetById", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID of pet to fetch", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "pet response", + "content": { + "application/json": { + "schema": { + "required": [ + "id", + "name" + ], + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + } + }, + "application/xml": { + "schema": { + "required": [ + "id", + "name" + ], + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + } + } + } + }, + "4XX": { + "description": "unexpected client error", + "content": { + "text/html": { + "schema": { + "required": [ + "code", + "message" + ], + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + } + } + } + } + } + }, + "5XX": { + "description": "unexpected server error", + "content": { + "text/html": { + "schema": { + "required": [ + "code", + "message" + ], + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + } + } + } + } + } + } + } + }, + "delete": { + "description": "deletes a single pet based on the ID supplied", + "operationId": "deletePet", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID of pet to delete", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "204": { + "description": "pet deleted" + }, + "4XX": { + "description": "unexpected client error", + "content": { + "text/html": { + "schema": { + "required": [ + "code", + "message" + ], + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + } + } + } + } + } + }, + "5XX": { + "description": "unexpected server error", + "content": { + "text/html": { + "schema": { + "required": [ + "code", + "message" + ], + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + } + } + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "pet": { + "required": [ + "id", + "name" + ], + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + }, + "newPet": { + "required": [ + "name" + ], + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + }, + "errorModel": { + "required": [ + "code", + "message" + ], + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + } + } + } + } + } +} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentAsV3JsonWorks2_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentAsV3JsonWorks2_produceTerseOutput=True.verified.txt new file mode 100644 index 000000000..72106e400 --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentAsV3JsonWorks2_produceTerseOutput=True.verified.txt @@ -0,0 +1 @@ +{"openapi":"3.0.1","info":{"title":"Swagger Petstore (Simple)","description":"A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification","termsOfService":"http://helloreverb.com/terms/","contact":{"name":"Swagger API team","url":"http://swagger.io","email":"foo@example.com"},"license":{"name":"MIT","url":"http://opensource.org/licenses/MIT"},"version":"1.0.0"},"servers":[{"url":"http://petstore.swagger.io/api"}],"paths":{"/pets":{"get":{"description":"Returns all pets from the system that the user has access to","operationId":"findPets","parameters":[{"name":"tags","in":"query","description":"tags to filter by","schema":{"type":"array","items":{"type":"string"}}},{"name":"limit","in":"query","description":"maximum number of results to return","schema":{"type":"integer","format":"int32"}}],"responses":{"200":{"description":"pet response","content":{"application/json":{"schema":{"type":"array","items":{"required":["id","name"],"type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}}}},"application/xml":{"schema":{"type":"array","items":{"required":["id","name"],"type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}}}}}},"4XX":{"description":"unexpected client error","content":{"text/html":{"schema":{"required":["code","message"],"type":"object","properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}}},"5XX":{"description":"unexpected server error","content":{"text/html":{"schema":{"required":["code","message"],"type":"object","properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}}}}},"post":{"description":"Creates a new pet in the store. Duplicates are allowed","operationId":"addPet","requestBody":{"description":"Pet to add to the store","content":{"application/json":{"schema":{"required":["name"],"type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}}}},"required":true},"responses":{"200":{"description":"pet response","content":{"application/json":{"schema":{"required":["id","name"],"type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}}}}},"4XX":{"description":"unexpected client error","content":{"text/html":{"schema":{"required":["code","message"],"type":"object","properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}}},"5XX":{"description":"unexpected server error","content":{"text/html":{"schema":{"required":["code","message"],"type":"object","properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}}}}}},"/pets/{id}":{"get":{"description":"Returns a user based on a single ID, if the user does not have access to the pet","operationId":"findPetById","parameters":[{"name":"id","in":"path","description":"ID of pet to fetch","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"pet response","content":{"application/json":{"schema":{"required":["id","name"],"type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}}},"application/xml":{"schema":{"required":["id","name"],"type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}}}}},"4XX":{"description":"unexpected client error","content":{"text/html":{"schema":{"required":["code","message"],"type":"object","properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}}},"5XX":{"description":"unexpected server error","content":{"text/html":{"schema":{"required":["code","message"],"type":"object","properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}}}}},"delete":{"description":"deletes a single pet based on the ID supplied","operationId":"deletePet","parameters":[{"name":"id","in":"path","description":"ID of pet to delete","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"204":{"description":"pet deleted"},"4XX":{"description":"unexpected client error","content":{"text/html":{"schema":{"required":["code","message"],"type":"object","properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}}},"5XX":{"description":"unexpected server error","content":{"text/html":{"schema":{"required":["code","message"],"type":"object","properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}}}}}}},"components":{"schemas":{"pet":{"required":["id","name"],"type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}},"newPet":{"required":["name"],"type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}},"errorModel":{"required":["code","message"],"type":"object","properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}}} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentAsV3JsonWorks_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentAsV3JsonWorks_produceTerseOutput=False.verified.txt index 5b27add35..a94db37b7 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentAsV3JsonWorks_produceTerseOutput=False.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentAsV3JsonWorks_produceTerseOutput=False.verified.txt @@ -1,4 +1,4 @@ -{ +{ "openapi": "3.0.1", "info": { "title": "Swagger Petstore (Simple)", @@ -30,7 +30,6 @@ "name": "tags", "in": "query", "description": "tags to filter by", - "style": "form", "schema": { "type": "array", "items": { @@ -42,7 +41,6 @@ "name": "limit", "in": "query", "description": "maximum number of results to return", - "style": "form", "schema": { "type": "integer", "format": "int32" @@ -266,7 +264,6 @@ "in": "path", "description": "ID of pet to fetch", "required": true, - "style": "simple", "schema": { "type": "integer", "format": "int64" @@ -378,7 +375,6 @@ "in": "path", "description": "ID of pet to delete", "required": true, - "style": "simple", "schema": { "type": "integer", "format": "int64" diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentAsV3JsonWorks_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentAsV3JsonWorks_produceTerseOutput=True.verified.txt index e3abf0e50..72106e400 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentAsV3JsonWorks_produceTerseOutput=True.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentAsV3JsonWorks_produceTerseOutput=True.verified.txt @@ -1 +1 @@ -{"openapi":"3.0.1","info":{"title":"Swagger Petstore (Simple)","description":"A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification","termsOfService":"http://helloreverb.com/terms/","contact":{"name":"Swagger API team","url":"http://swagger.io","email":"foo@example.com"},"license":{"name":"MIT","url":"http://opensource.org/licenses/MIT"},"version":"1.0.0"},"servers":[{"url":"http://petstore.swagger.io/api"}],"paths":{"/pets":{"get":{"description":"Returns all pets from the system that the user has access to","operationId":"findPets","parameters":[{"name":"tags","in":"query","description":"tags to filter by","style":"form","schema":{"type":"array","items":{"type":"string"}}},{"name":"limit","in":"query","description":"maximum number of results to return","style":"form","schema":{"type":"integer","format":"int32"}}],"responses":{"200":{"description":"pet response","content":{"application/json":{"schema":{"type":"array","items":{"required":["id","name"],"type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}}}},"application/xml":{"schema":{"type":"array","items":{"required":["id","name"],"type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}}}}}},"4XX":{"description":"unexpected client error","content":{"text/html":{"schema":{"required":["code","message"],"type":"object","properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}}},"5XX":{"description":"unexpected server error","content":{"text/html":{"schema":{"required":["code","message"],"type":"object","properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}}}}},"post":{"description":"Creates a new pet in the store. Duplicates are allowed","operationId":"addPet","requestBody":{"description":"Pet to add to the store","content":{"application/json":{"schema":{"required":["name"],"type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}}}},"required":true},"responses":{"200":{"description":"pet response","content":{"application/json":{"schema":{"required":["id","name"],"type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}}}}},"4XX":{"description":"unexpected client error","content":{"text/html":{"schema":{"required":["code","message"],"type":"object","properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}}},"5XX":{"description":"unexpected server error","content":{"text/html":{"schema":{"required":["code","message"],"type":"object","properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}}}}}},"/pets/{id}":{"get":{"description":"Returns a user based on a single ID, if the user does not have access to the pet","operationId":"findPetById","parameters":[{"name":"id","in":"path","description":"ID of pet to fetch","required":true,"style":"simple","schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"pet response","content":{"application/json":{"schema":{"required":["id","name"],"type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}}},"application/xml":{"schema":{"required":["id","name"],"type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}}}}},"4XX":{"description":"unexpected client error","content":{"text/html":{"schema":{"required":["code","message"],"type":"object","properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}}},"5XX":{"description":"unexpected server error","content":{"text/html":{"schema":{"required":["code","message"],"type":"object","properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}}}}},"delete":{"description":"deletes a single pet based on the ID supplied","operationId":"deletePet","parameters":[{"name":"id","in":"path","description":"ID of pet to delete","required":true,"style":"simple","schema":{"type":"integer","format":"int64"}}],"responses":{"204":{"description":"pet deleted"},"4XX":{"description":"unexpected client error","content":{"text/html":{"schema":{"required":["code","message"],"type":"object","properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}}},"5XX":{"description":"unexpected server error","content":{"text/html":{"schema":{"required":["code","message"],"type":"object","properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}}}}}}},"components":{"schemas":{"pet":{"required":["id","name"],"type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}},"newPet":{"required":["name"],"type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}},"errorModel":{"required":["code","message"],"type":"object","properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}}} \ No newline at end of file +{"openapi":"3.0.1","info":{"title":"Swagger Petstore (Simple)","description":"A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification","termsOfService":"http://helloreverb.com/terms/","contact":{"name":"Swagger API team","url":"http://swagger.io","email":"foo@example.com"},"license":{"name":"MIT","url":"http://opensource.org/licenses/MIT"},"version":"1.0.0"},"servers":[{"url":"http://petstore.swagger.io/api"}],"paths":{"/pets":{"get":{"description":"Returns all pets from the system that the user has access to","operationId":"findPets","parameters":[{"name":"tags","in":"query","description":"tags to filter by","schema":{"type":"array","items":{"type":"string"}}},{"name":"limit","in":"query","description":"maximum number of results to return","schema":{"type":"integer","format":"int32"}}],"responses":{"200":{"description":"pet response","content":{"application/json":{"schema":{"type":"array","items":{"required":["id","name"],"type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}}}},"application/xml":{"schema":{"type":"array","items":{"required":["id","name"],"type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}}}}}},"4XX":{"description":"unexpected client error","content":{"text/html":{"schema":{"required":["code","message"],"type":"object","properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}}},"5XX":{"description":"unexpected server error","content":{"text/html":{"schema":{"required":["code","message"],"type":"object","properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}}}}},"post":{"description":"Creates a new pet in the store. Duplicates are allowed","operationId":"addPet","requestBody":{"description":"Pet to add to the store","content":{"application/json":{"schema":{"required":["name"],"type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}}}},"required":true},"responses":{"200":{"description":"pet response","content":{"application/json":{"schema":{"required":["id","name"],"type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}}}}},"4XX":{"description":"unexpected client error","content":{"text/html":{"schema":{"required":["code","message"],"type":"object","properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}}},"5XX":{"description":"unexpected server error","content":{"text/html":{"schema":{"required":["code","message"],"type":"object","properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}}}}}},"/pets/{id}":{"get":{"description":"Returns a user based on a single ID, if the user does not have access to the pet","operationId":"findPetById","parameters":[{"name":"id","in":"path","description":"ID of pet to fetch","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"pet response","content":{"application/json":{"schema":{"required":["id","name"],"type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}}},"application/xml":{"schema":{"required":["id","name"],"type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}}}}},"4XX":{"description":"unexpected client error","content":{"text/html":{"schema":{"required":["code","message"],"type":"object","properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}}},"5XX":{"description":"unexpected server error","content":{"text/html":{"schema":{"required":["code","message"],"type":"object","properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}}}}},"delete":{"description":"deletes a single pet based on the ID supplied","operationId":"deletePet","parameters":[{"name":"id","in":"path","description":"ID of pet to delete","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"204":{"description":"pet deleted"},"4XX":{"description":"unexpected client error","content":{"text/html":{"schema":{"required":["code","message"],"type":"object","properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}}},"5XX":{"description":"unexpected server error","content":{"text/html":{"schema":{"required":["code","message"],"type":"object","properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}}}}}}},"components":{"schemas":{"pet":{"required":["id","name"],"type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}},"newPet":{"required":["name"],"type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}},"errorModel":{"required":["code","message"],"type":"object","properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"}}}}}} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentWithReferenceAsV2JsonWorks_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentWithReferenceAsV2JsonWorks_produceTerseOutput=False.verified.txt index 1ee60de40..443881617 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentWithReferenceAsV2JsonWorks_produceTerseOutput=False.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentWithReferenceAsV2JsonWorks_produceTerseOutput=False.verified.txt @@ -46,8 +46,7 @@ "name": "limit", "description": "maximum number of results to return", "type": "integer", - "format": "int32", - "collectionFormat": "multi" + "format": "int32" } ], "responses": { diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentWithReferenceAsV2JsonWorks_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentWithReferenceAsV2JsonWorks_produceTerseOutput=True.verified.txt index 682b253b7..3818a4799 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentWithReferenceAsV2JsonWorks_produceTerseOutput=True.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentWithReferenceAsV2JsonWorks_produceTerseOutput=True.verified.txt @@ -1 +1 @@ -{"swagger":"2.0","info":{"title":"Swagger Petstore (Simple)","description":"A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification","termsOfService":"http://helloreverb.com/terms/","contact":{"name":"Swagger API team","url":"http://swagger.io","email":"foo@example.com"},"license":{"name":"MIT","url":"http://opensource.org/licenses/MIT"},"version":"1.0.0"},"host":"petstore.swagger.io","basePath":"/api","schemes":["http"],"paths":{"/pets":{"get":{"description":"Returns all pets from the system that the user has access to","operationId":"findPets","produces":["application/json","application/xml","text/html"],"parameters":[{"in":"query","name":"tags","description":"tags to filter by","type":"array","items":{"type":"string"},"collectionFormat":"multi"},{"in":"query","name":"limit","description":"maximum number of results to return","type":"integer","format":"int32","collectionFormat":"multi"}],"responses":{"200":{"description":"pet response","schema":{"type":"array","items":{"$ref":"#/definitions/pet"}}},"4XX":{"description":"unexpected client error","schema":{"$ref":"#/definitions/errorModel"}},"5XX":{"description":"unexpected server error","schema":{"$ref":"#/definitions/errorModel"}}}},"post":{"description":"Creates a new pet in the store. Duplicates are allowed","operationId":"addPet","consumes":["application/json"],"produces":["application/json","text/html"],"parameters":[{"in":"body","name":"body","description":"Pet to add to the store","required":true,"schema":{"$ref":"#/definitions/newPet"}}],"responses":{"200":{"description":"pet response","schema":{"$ref":"#/definitions/pet"}},"4XX":{"description":"unexpected client error","schema":{"$ref":"#/definitions/errorModel"}},"5XX":{"description":"unexpected server error","schema":{"$ref":"#/definitions/errorModel"}}}}},"/pets/{id}":{"get":{"description":"Returns a user based on a single ID, if the user does not have access to the pet","operationId":"findPetById","produces":["application/json","application/xml","text/html"],"parameters":[{"in":"path","name":"id","description":"ID of pet to fetch","required":true,"type":"integer","format":"int64"}],"responses":{"200":{"description":"pet response","schema":{"$ref":"#/definitions/pet"}},"4XX":{"description":"unexpected client error","schema":{"$ref":"#/definitions/errorModel"}},"5XX":{"description":"unexpected server error","schema":{"$ref":"#/definitions/errorModel"}}}},"delete":{"description":"deletes a single pet based on the ID supplied","operationId":"deletePet","produces":["text/html"],"parameters":[{"in":"path","name":"id","description":"ID of pet to delete","required":true,"type":"integer","format":"int64"}],"responses":{"204":{"description":"pet deleted"},"4XX":{"description":"unexpected client error","schema":{"$ref":"#/definitions/errorModel"}},"5XX":{"description":"unexpected server error","schema":{"$ref":"#/definitions/errorModel"}}}}}},"definitions":{"pet":{"required":["id","name"],"type":"object","properties":{"id":{"format":"int64","type":"integer"},"name":{"type":"string"},"tag":{"type":"string"}}},"newPet":{"required":["name"],"type":"object","properties":{"id":{"format":"int64","type":"integer"},"name":{"type":"string"},"tag":{"type":"string"}}},"errorModel":{"required":["code","message"],"type":"object","properties":{"code":{"format":"int32","type":"integer"},"message":{"type":"string"}}}}} \ No newline at end of file +{"swagger":"2.0","info":{"title":"Swagger Petstore (Simple)","description":"A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification","termsOfService":"http://helloreverb.com/terms/","contact":{"name":"Swagger API team","url":"http://swagger.io","email":"foo@example.com"},"license":{"name":"MIT","url":"http://opensource.org/licenses/MIT"},"version":"1.0.0"},"host":"petstore.swagger.io","basePath":"/api","schemes":["http"],"paths":{"/pets":{"get":{"description":"Returns all pets from the system that the user has access to","operationId":"findPets","produces":["application/json","application/xml","text/html"],"parameters":[{"in":"query","name":"tags","description":"tags to filter by","type":"array","items":{"type":"string"},"collectionFormat":"multi"},{"in":"query","name":"limit","description":"maximum number of results to return","type":"integer","format":"int32"}],"responses":{"200":{"description":"pet response","schema":{"type":"array","items":{"$ref":"#/definitions/pet"}}},"4XX":{"description":"unexpected client error","schema":{"$ref":"#/definitions/errorModel"}},"5XX":{"description":"unexpected server error","schema":{"$ref":"#/definitions/errorModel"}}}},"post":{"description":"Creates a new pet in the store. Duplicates are allowed","operationId":"addPet","consumes":["application/json"],"produces":["application/json","text/html"],"parameters":[{"in":"body","name":"body","description":"Pet to add to the store","required":true,"schema":{"$ref":"#/definitions/newPet"}}],"responses":{"200":{"description":"pet response","schema":{"$ref":"#/definitions/pet"}},"4XX":{"description":"unexpected client error","schema":{"$ref":"#/definitions/errorModel"}},"5XX":{"description":"unexpected server error","schema":{"$ref":"#/definitions/errorModel"}}}}},"/pets/{id}":{"get":{"description":"Returns a user based on a single ID, if the user does not have access to the pet","operationId":"findPetById","produces":["application/json","application/xml","text/html"],"parameters":[{"in":"path","name":"id","description":"ID of pet to fetch","required":true,"type":"integer","format":"int64"}],"responses":{"200":{"description":"pet response","schema":{"$ref":"#/definitions/pet"}},"4XX":{"description":"unexpected client error","schema":{"$ref":"#/definitions/errorModel"}},"5XX":{"description":"unexpected server error","schema":{"$ref":"#/definitions/errorModel"}}}},"delete":{"description":"deletes a single pet based on the ID supplied","operationId":"deletePet","produces":["text/html"],"parameters":[{"in":"path","name":"id","description":"ID of pet to delete","required":true,"type":"integer","format":"int64"}],"responses":{"204":{"description":"pet deleted"},"4XX":{"description":"unexpected client error","schema":{"$ref":"#/definitions/errorModel"}},"5XX":{"description":"unexpected server error","schema":{"$ref":"#/definitions/errorModel"}}}}}},"definitions":{"pet":{"required":["id","name"],"type":"object","properties":{"id":{"format":"int64","type":"integer"},"name":{"type":"string"},"tag":{"type":"string"}}},"newPet":{"required":["name"],"type":"object","properties":{"id":{"format":"int64","type":"integer"},"name":{"type":"string"},"tag":{"type":"string"}}},"errorModel":{"required":["code","message"],"type":"object","properties":{"code":{"format":"int32","type":"integer"},"message":{"type":"string"}}}}} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentWithReferenceAsV3JsonWorks_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentWithReferenceAsV3JsonWorks_produceTerseOutput=False.verified.txt index f272b26eb..f1da0b354 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentWithReferenceAsV3JsonWorks_produceTerseOutput=False.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeAdvancedDocumentWithReferenceAsV3JsonWorks_produceTerseOutput=False.verified.txt @@ -30,7 +30,6 @@ "name": "tags", "in": "query", "description": "tags to filter by", - "style": "form", "schema": { "type": "array", "items": { @@ -42,7 +41,6 @@ "name": "limit", "in": "query", "description": "maximum number of results to return", - "style": "form", "schema": { "type": "integer", "format": "int32" @@ -151,7 +149,6 @@ "in": "path", "description": "ID of pet to fetch", "required": true, - "style": "simple", "schema": { "type": "integer", "format": "int64" @@ -205,7 +202,6 @@ "in": "path", "description": "ID of pet to delete", "required": true, - "style": "simple", "schema": { "type": "integer", "format": "int64" diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeDuplicateExtensionsAsV3JsonWorks_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeDuplicateExtensionsAsV3JsonWorks_produceTerseOutput=False.verified.txt index 8b90dd0ee..c2e9f5312 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeDuplicateExtensionsAsV3JsonWorks_produceTerseOutput=False.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeDuplicateExtensionsAsV3JsonWorks_produceTerseOutput=False.verified.txt @@ -20,7 +20,6 @@ "in": "path", "description": "The first operand", "required": true, - "style": "simple", "schema": { "type": "integer", "my-extension": 4 @@ -32,7 +31,6 @@ "in": "path", "description": "The second operand", "required": true, - "style": "simple", "schema": { "type": "integer", "my-extension": 4 diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.cs index 6a185b556..33914113d 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Globalization; using System.IO; +using System.Threading; using System.Threading.Tasks; using FluentAssertions; using Microsoft.OpenApi.Extensions; @@ -598,7 +599,7 @@ public class OpenApiDocumentTests public static OpenApiSchema ErrorModelSchema = AdvancedComponents.Schemas["errorModel"]; - public static OpenApiDocument AdvancedDocument = new OpenApiDocument + public OpenApiDocument AdvancedDocument = new OpenApiDocument { Info = new OpenApiInfo { @@ -1059,8 +1060,8 @@ public OpenApiDocumentTests(ITestOutputHelper output) } [Theory] - [InlineData(true)] [InlineData(false)] + [InlineData(true)] public async Task SerializeAdvancedDocumentAsV3JsonWorks(bool produceTerseOutput) { // Arrange @@ -1429,6 +1430,7 @@ public void CopyConstructorForAdvancedDocumentWorks() Assert.NotNull(doc.Components); } +<<<<<<< HEAD [Theory] [InlineData(true)] [InlineData(false)] @@ -1449,10 +1451,15 @@ public async void SerializeDocumentWithWebhooksAsV3JsonWorks(bool produceTerseOu [Fact] public void SerializeDocumentWithWebhooksAsV3YamlWorks() +======= + [Fact] + public void SerializeV2DocumentWithStyleAsNullDoesNotWriteOutStyleValue() +>>>>>>> 3eb4f02c (Add test to validate) { // Arrange var expected = @"openapi: 3.0.1 info: +<<<<<<< HEAD title: Webhook Example version: 1.0.0 paths: { } @@ -1485,11 +1492,93 @@ public void SerializeDocumentWithWebhooksAsV3YamlWorks() // Act var actual = DocumentWithWebhooks.SerializeAsYaml(OpenApiSpecVersion.OpenApi3_0); +======= + title: magic style + version: 1.0.0 +paths: + /foo: + get: + parameters: + - name: id + in: query + schema: + type: object + additionalProperties: + type: integer + responses: + '200': + description: foo + content: + text/plain: + schema: + type: string"; + + var doc = new OpenApiDocument + { + Info = new OpenApiInfo + { + Title = "magic style", + Version = "1.0.0" + }, + Paths = new OpenApiPaths + { + ["/foo"] = new OpenApiPathItem + { + Operations = new Dictionary + { + [OperationType.Get] = new OpenApiOperation + { + Parameters = new List + { + new OpenApiParameter + { + Name = "id", + In = ParameterLocation.Query, + Schema = new OpenApiSchema + { + Type = "object", + AdditionalProperties = new OpenApiSchema + { + Type = "integer" + } + } + } + }, + Responses = new OpenApiResponses + { + ["200"] = new OpenApiResponse + { + Description = "foo", + Content = new Dictionary + { + ["text/plain"] = new OpenApiMediaType + { + Schema = new OpenApiSchema + { + Type = "string" + } + } + } + } + } + } + } + } + } + }; + + // Act + var actual = doc.SerializeAsYaml(OpenApiSpecVersion.OpenApi3_0); +>>>>>>> 3eb4f02c (Add test to validate) // Assert actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); +<<<<<<< HEAD Assert.Equal(expected, actual); +======= + actual.Should().Be(expected); +>>>>>>> 3eb4f02c (Add test to validate) } } } diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.SerializeReferencedExampleAsV3JsonWithoutReferenceWorks_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.SerializeReferencedExampleAsV3JsonWithoutReferenceWorks_produceTerseOutput=False.verified.txt index 45f085f73..868b45156 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.SerializeReferencedExampleAsV3JsonWithoutReferenceWorks_produceTerseOutput=False.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.SerializeReferencedExampleAsV3JsonWithoutReferenceWorks_produceTerseOutput=False.verified.txt @@ -21,6 +21,7 @@ } ] } - ] + ], + "aDate": "12/12/2022" } } \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.SerializeReferencedExampleAsV3JsonWithoutReferenceWorks_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.SerializeReferencedExampleAsV3JsonWithoutReferenceWorks_produceTerseOutput=True.verified.txt index b503d318e..7ab9ad1d6 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.SerializeReferencedExampleAsV3JsonWithoutReferenceWorks_produceTerseOutput=True.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.SerializeReferencedExampleAsV3JsonWithoutReferenceWorks_produceTerseOutput=True.verified.txt @@ -1 +1 @@ -{"value":{"versions":[{"status":"Status1","id":"v1","links":[{"href":"http://example.com/1","rel":"sampleRel1"}]},{"status":"Status2","id":"v2","links":[{"href":"http://example.com/2","rel":"sampleRel2"}]}]}} \ No newline at end of file +{"value":{"versions":[{"status":"Status1","id":"v1","links":[{"href":"http://example.com/1","rel":"sampleRel1"}]},{"status":"Status2","id":"v2","links":[{"href":"http://example.com/2","rel":"sampleRel2"}]}],"aDate":"12/12/2022"}} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.cs index 6108c3c26..d42d65ce7 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. +using System; using System.Globalization; using System.IO; using System.Text; @@ -95,7 +96,8 @@ public class OpenApiExampleTests } } } - } + }, + ["aDate"] = new OpenApiDate(DateTime.Parse("12/12/2022 00:00:00")) } }; diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiOperationTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiOperationTests.cs index 368aeb227..2079a3122 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiOperationTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiOperationTests.cs @@ -334,13 +334,11 @@ public void SerializeOperationWithBodyAsV3JsonWorks() ""parameters"": [ { ""name"": ""parameter1"", - ""in"": ""path"", - ""style"": ""simple"" + ""in"": ""path"" }, { ""name"": ""parameter2"", - ""in"": ""header"", - ""style"": ""simple"" + ""in"": ""header"" } ], ""requestBody"": { @@ -409,13 +407,11 @@ public void SerializeAdvancedOperationWithTagAndSecurityAsV3JsonWorks() ""parameters"": [ { ""name"": ""parameter1"", - ""in"": ""path"", - ""style"": ""simple"" + ""in"": ""path"" }, { ""name"": ""parameter2"", - ""in"": ""header"", - ""style"": ""simple"" + ""in"": ""header"" } ], ""requestBody"": { @@ -505,7 +501,6 @@ public void SerializeOperationWithFormDataAsV3JsonWorks() ""in"": ""path"", ""description"": ""ID of pet that needs to be updated"", ""required"": true, - ""style"": ""simple"", ""schema"": { ""type"": ""string"" } diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeReferencedParameterAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeReferencedParameterAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=False.verified.txt index f4424fa30..5275532e8 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeReferencedParameterAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=False.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeReferencedParameterAsV3JsonWithoutReferenceWorksAsync_produceTerseOutput=False.verified.txt @@ -1,5 +1,4 @@ { "name": "name1", - "in": "path", - "style": "simple" + "in": "path" } \ 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 cfcc56d15..2f57673af 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.cs @@ -213,8 +213,7 @@ public void SerializeBasicParameterAsV3JsonWorks() // Arrange var expected = @"{ ""name"": ""name1"", - ""in"": ""path"", - ""style"": ""simple"" + ""in"": ""path"" }"; // Act diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.cs index 429129c1e..447d01337 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.cs @@ -463,5 +463,24 @@ public void SerializeAsV2ShouldSetFormatPropertyInParentSchemaIfPresentInChildre // Assert Assert.Equal(expectedV2Schema, v2Schema); } + + [Fact] + public void OpenApiSchemaCopyConstructorSucceeds() + { + var baseSchema = new OpenApiSchema() + { + Type = "string", + Format = "date" + }; + + var actualSchema = new OpenApiSchema(baseSchema) + { + Nullable = true + }; + + Assert.Equal("string", actualSchema.Type); + Assert.Equal("date", actualSchema.Format); + Assert.True(actualSchema.Nullable); + } } } diff --git a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt index b42325207..3d535fafa 100755 --- a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt +++ b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt @@ -282,6 +282,11 @@ namespace Microsoft.OpenApi.Extensions public static void SerializeAsYaml(this T element, System.IO.Stream stream, Microsoft.OpenApi.OpenApiSpecVersion specVersion) where T : Microsoft.OpenApi.Interfaces.IOpenApiSerializable { } } + public static class OpenApiTypeMapper + { + public static System.Type MapOpenApiPrimitiveTypeToSimpleType(this Microsoft.OpenApi.Models.OpenApiSchema schema) { } + public static Microsoft.OpenApi.Models.OpenApiSchema MapTypeToOpenApiPrimitiveType(this System.Type type) { } + } public static class StringExtensions { public static T GetEnumFromDisplayName(this string displayName) { } @@ -780,6 +785,7 @@ namespace Microsoft.OpenApi.Models } public class OpenApiReference : Microsoft.OpenApi.Interfaces.IOpenApiElement, Microsoft.OpenApi.Interfaces.IOpenApiSerializable { + public bool IsFragrament; public OpenApiReference() { } public OpenApiReference(Microsoft.OpenApi.Models.OpenApiReference reference) { } public string Description { get; set; } @@ -856,12 +862,13 @@ namespace Microsoft.OpenApi.Models public int? MaxItems { get; set; } public int? MaxLength { get; set; } public int? MaxProperties { get; set; } - public decimal? Maximum { get; set; } + public + ? Maximum { get; set; } public int? MinItems { get; set; } public int? MinLength { get; set; } public int? MinProperties { get; set; } - public decimal? Minimum { get; set; } - public decimal? MultipleOf { get; set; } + public double? Minimum { get; set; } + public double? MultipleOf { get; set; } public Microsoft.OpenApi.Models.OpenApiSchema Not { get; set; } public bool Nullable { get; set; } public System.Collections.Generic.IList OneOf { get; set; } @@ -1224,18 +1231,35 @@ namespace Microsoft.OpenApi.Validations public override void Visit(Microsoft.OpenApi.Models.OpenApiContact item) { } public override void Visit(Microsoft.OpenApi.Models.OpenApiDocument item) { } public override void Visit(Microsoft.OpenApi.Models.OpenApiEncoding item) { } + public override void Visit(Microsoft.OpenApi.Models.OpenApiExample item) { } public override void Visit(Microsoft.OpenApi.Models.OpenApiExternalDocs item) { } public override void Visit(Microsoft.OpenApi.Models.OpenApiHeader item) { } public override void Visit(Microsoft.OpenApi.Models.OpenApiInfo item) { } public override void Visit(Microsoft.OpenApi.Models.OpenApiLicense item) { } + public override void Visit(Microsoft.OpenApi.Models.OpenApiLink item) { } public override void Visit(Microsoft.OpenApi.Models.OpenApiMediaType item) { } public override void Visit(Microsoft.OpenApi.Models.OpenApiOAuthFlow item) { } + public override void Visit(Microsoft.OpenApi.Models.OpenApiOperation item) { } public override void Visit(Microsoft.OpenApi.Models.OpenApiParameter item) { } + public override void Visit(Microsoft.OpenApi.Models.OpenApiPathItem item) { } + public override void Visit(Microsoft.OpenApi.Models.OpenApiPaths item) { } + public override void Visit(Microsoft.OpenApi.Models.OpenApiRequestBody item) { } public override void Visit(Microsoft.OpenApi.Models.OpenApiResponse item) { } public override void Visit(Microsoft.OpenApi.Models.OpenApiResponses item) { } public override void Visit(Microsoft.OpenApi.Models.OpenApiSchema item) { } + public override void Visit(Microsoft.OpenApi.Models.OpenApiSecurityRequirement item) { } + public override void Visit(Microsoft.OpenApi.Models.OpenApiSecurityScheme item) { } public override void Visit(Microsoft.OpenApi.Models.OpenApiServer item) { } + public override void Visit(Microsoft.OpenApi.Models.OpenApiServerVariable item) { } public override void Visit(Microsoft.OpenApi.Models.OpenApiTag item) { } + public override void Visit(System.Collections.Generic.IDictionary item) { } + public override void Visit(System.Collections.Generic.IDictionary item) { } + public override void Visit(System.Collections.Generic.IDictionary item) { } + public override void Visit(System.Collections.Generic.IDictionary item) { } + public override void Visit(System.Collections.Generic.IDictionary item) { } + public override void Visit(System.Collections.Generic.IDictionary item) { } + public override void Visit(System.Collections.Generic.IDictionary item) { } + public override void Visit(System.Collections.Generic.IDictionary item) { } public override void Visit(System.Collections.Generic.IList items) { } } public class OpenApiValidatorError : Microsoft.OpenApi.Models.OpenApiError @@ -1394,7 +1418,7 @@ namespace Microsoft.OpenApi.Writers void WriteStartArray(); void WriteStartObject(); void WriteValue(bool value); - void WriteValue(decimal value); + void WriteValue(double value); void WriteValue(int value); void WriteValue(object value); void WriteValue(string value); @@ -1457,7 +1481,7 @@ namespace Microsoft.OpenApi.Writers public virtual void WriteValue(bool value) { } public virtual void WriteValue(System.DateTime value) { } public virtual void WriteValue(System.DateTimeOffset value) { } - public virtual void WriteValue(decimal value) { } + public virtual void WriteValue(double value) { } public virtual void WriteValue(double value) { } public virtual void WriteValue(int value) { } public virtual void WriteValue(long value) { } diff --git a/test/Microsoft.OpenApi.Tests/Writers/OpenApiJsonWriterTests.cs b/test/Microsoft.OpenApi.Tests/Writers/OpenApiJsonWriterTests.cs index 44d1be55b..0317bfb12 100644 --- a/test/Microsoft.OpenApi.Tests/Writers/OpenApiJsonWriterTests.cs +++ b/test/Microsoft.OpenApi.Tests/Writers/OpenApiJsonWriterTests.cs @@ -188,7 +188,7 @@ private void WriteValueRecursive(OpenApiJsonWriter writer, object value) { if (value == null || value.GetType().IsPrimitive - || value is decimal + || value is double || value is string || value is DateTimeOffset || value is DateTime) diff --git a/test/Microsoft.OpenApi.Tests/Writers/OpenApiWriterSpecialCharacterTests.cs b/test/Microsoft.OpenApi.Tests/Writers/OpenApiWriterSpecialCharacterTests.cs index f23cc442a..6ac47d6c3 100644 --- a/test/Microsoft.OpenApi.Tests/Writers/OpenApiWriterSpecialCharacterTests.cs +++ b/test/Microsoft.OpenApi.Tests/Writers/OpenApiWriterSpecialCharacterTests.cs @@ -148,5 +148,26 @@ public void WriteStringWithNewlineCharactersInArrayAsYamlWorks(string input, str // Assert actual.Should().Be(expected); } + + [Theory] + [InlineData("1.8.0", " '1.8.0'", "en-US")] + [InlineData("1.8.0", " '1.8.0'", "en-GB")] + [InlineData("1.13.0", " '1.13.0'", "en-US")] + [InlineData("1.13.0", " '1.13.0'", "en-GB")] + public void WriteStringAsYamlDoesNotDependOnSystemCulture(string input, string expected, string culture) + { + CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo(culture); + + // Arrange + var outputStringWriter = new StringWriter(CultureInfo.InvariantCulture); + var writer = new OpenApiYamlWriter(outputStringWriter); + + // Act + writer.WriteValue(input); + var actual = outputStringWriter.GetStringBuilder().ToString(); + + // Assert + actual.Should().Be(expected); + } } } diff --git a/test/Microsoft.OpenApi.Tests/Writers/OpenApiYamlWriterTests.cs b/test/Microsoft.OpenApi.Tests/Writers/OpenApiYamlWriterTests.cs index bfaa3da51..fc29bbe94 100644 --- a/test/Microsoft.OpenApi.Tests/Writers/OpenApiYamlWriterTests.cs +++ b/test/Microsoft.OpenApi.Tests/Writers/OpenApiYamlWriterTests.cs @@ -256,7 +256,7 @@ private void WriteValueRecursive(OpenApiYamlWriter writer, object value) { if (value == null || value.GetType().IsPrimitive - || value is decimal + || value is double || value is string || value is DateTimeOffset || value is DateTime)