diff --git a/src/Microsoft.OpenApi/Models/OpenApiParameter.cs b/src/Microsoft.OpenApi/Models/OpenApiParameter.cs index f1300f027..6e4e6028c 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiParameter.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiParameter.cs @@ -116,6 +116,20 @@ internal void SerializeInternal(IOpenApiWriter writer, OpenApiSpecVersion versio { Utils.CheckArgumentNull(writer); + // Check for querystring restrictions + if (In == ParameterLocation.QueryString) + { + if (version < OpenApiSpecVersion.OpenApi3_2) + { + throw new InvalidOperationException("Parameter location 'querystring' is only supported in OpenAPI 3.2.0 and above."); + } + // Only throw if forbidden properties are explicitly set (not just default values) + if ((_style.HasValue) || (_explode.HasValue && _explode.Value) || AllowReserved || Schema != null) + { + throw new InvalidOperationException("When 'in' is 'querystring', 'style', 'explode', 'allowReserved', and 'schema' properties MUST NOT be used as per OpenAPI 3.2 specification."); + } + } + writer.WriteStartObject(); // name @@ -252,6 +266,12 @@ public virtual void SerializeAsV2(IOpenApiWriter writer) { Utils.CheckArgumentNull(writer); + // Throw if 'querystring' is used in V2 + if (In == ParameterLocation.QueryString) + { + throw new InvalidOperationException("Parameter location 'querystring' is not supported in OpenAPI 2.0."); + } + writer.WriteStartObject(); // in diff --git a/src/Microsoft.OpenApi/Models/ParameterLocation.cs b/src/Microsoft.OpenApi/Models/ParameterLocation.cs index 28d282496..4a5683190 100644 --- a/src/Microsoft.OpenApi/Models/ParameterLocation.cs +++ b/src/Microsoft.OpenApi/Models/ParameterLocation.cs @@ -27,6 +27,11 @@ public enum ParameterLocation /// /// Used to pass a specific cookie value to the API. /// - [Display("cookie")] Cookie + [Display("cookie")] Cookie, + + /// + /// Parameters that are appended to the URL query string (OpenAPI 3.2+ only). + /// + [Display("querystring")] QueryString } } diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.cs index 8bd30a7c9..0cd2726dd 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.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.Globalization; using System.IO; @@ -406,5 +407,52 @@ public async Task SerializeParameterWithFormStyleAndExplodeTrueWorksAsync(bool p // Assert await Verifier.Verify(outputStringWriter).UseParameters(produceTerseOutput); } + + [Fact] + public void SerializeQueryStringParameter_BelowV32_Throws() + { + var parameter = new OpenApiParameter + { + Name = "foo", + In = ParameterLocation.QueryString + }; + var writer = new OpenApiJsonWriter(new StringWriter()); + // Style, Explode, AllowReserved, and Schema must be unset for this test to throw as expected + Assert.Throws(() => parameter.SerializeAsV3(writer)); + Assert.Throws(() => parameter.SerializeAsV31(writer)); + Assert.Throws(() => parameter.SerializeAsV2(writer)); + } + + [Fact] + public void SerializeQueryStringParameter_WithForbiddenProperties_Throws() + { + var parameter = new OpenApiParameter + { + Name = "foo", + In = ParameterLocation.QueryString, + Style = ParameterStyle.Form, + Explode = true, + AllowReserved = true, + Schema = new OpenApiSchema { Type = JsonSchemaType.String } + }; + var writer = new OpenApiJsonWriter(new StringWriter()); + Assert.Throws(() => parameter.SerializeAsV32(writer)); + } + + [Fact] + public async Task SerializeQueryStringParameter_V32_Succeeds() + { + var parameter = new OpenApiParameter + { + Name = "foo", + In = ParameterLocation.QueryString, + Style = null, + AllowReserved = false, + Schema = null, + // Explode must be false (default) and not set + }; + var json = await parameter.SerializeAsJsonAsync(OpenApiSpecVersion.OpenApi3_2); + Assert.Contains("querystring", json); + } } } diff --git a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt index d2a93e2c8..dafa995e4 100644 --- a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt +++ b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt @@ -1736,6 +1736,8 @@ namespace Microsoft.OpenApi Path = 2, [Microsoft.OpenApi.Display("cookie")] Cookie = 3, + [Microsoft.OpenApi.Display("querystring")] + QueryString = 4, } public enum ParameterStyle {