Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: parameter reference proxy design pattern implementation #2093

Merged
merged 2 commits into from
Jan 28, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions src/Microsoft.OpenApi.Hidi/Formatters/PowerShellFormatter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Humanizer.Inflections;
using Microsoft.OpenApi.Hidi.Extensions;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Models.Interfaces;
using Microsoft.OpenApi.Services;

namespace Microsoft.OpenApi.Hidi.Formatters
Expand Down Expand Up @@ -69,13 +70,13 @@ public override void Visit(OpenApiOperation operation)

var operationId = operation.OperationId;
var operationTypeExtension = operation.Extensions?.GetExtension("x-ms-docs-operation-type");
if (operationTypeExtension.IsEquals("function"))
operation.Parameters = ResolveFunctionParameters(operation.Parameters ?? new List<OpenApiParameter>());
if (operationTypeExtension.IsEquals("function") && operation.Parameters is { Count :> 0})
ResolveFunctionParameters(operation.Parameters);

// Order matters. Resolve operationId.
operationId = RemoveHashSuffix(operationId);
if (operationTypeExtension.IsEquals("action") || operationTypeExtension.IsEquals("function"))
operationId = RemoveKeyTypeSegment(operationId, operation.Parameters ?? new List<OpenApiParameter>());
operationId = RemoveKeyTypeSegment(operationId, operation.Parameters ?? new List<IOpenApiParameter>());
operationId = SingularizeAndDeduplicateOperationId(operationId.SplitByChar('.'));
operationId = ResolveODataCastOperationId(operationId);
operationId = ResolveByRefOperationId(operationId);
Expand Down Expand Up @@ -143,7 +144,7 @@ private static string RemoveHashSuffix(string operationId)
return s_hashSuffixRegex.Match(operationId).Value;
}

private static string RemoveKeyTypeSegment(string operationId, IList<OpenApiParameter> parameters)
private static string RemoveKeyTypeSegment(string operationId, IList<IOpenApiParameter> parameters)
{
var segments = operationId.SplitByChar('.');
foreach (var parameter in parameters)
Expand All @@ -157,9 +158,9 @@ private static string RemoveKeyTypeSegment(string operationId, IList<OpenApiPara
return string.Join('.', segments);
}

private static IList<OpenApiParameter> ResolveFunctionParameters(IList<OpenApiParameter> parameters)
private static void ResolveFunctionParameters(IList<IOpenApiParameter> parameters)
{
foreach (var parameter in parameters.Where(static p => p.Content?.Any() ?? false))
foreach (var parameter in parameters.OfType<OpenApiParameter>().Where(static p => p.Content?.Any() ?? false))
{
// Replace content with a schema object of type array
// for structured or collection-valued function parameters
Expand All @@ -173,7 +174,6 @@ private static IList<OpenApiParameter> ResolveFunctionParameters(IList<OpenApiPa
}
};
}
return parameters;
}

private void AddAdditionalPropertiesToSchema(OpenApiSchema schema)
Expand Down
2 changes: 1 addition & 1 deletion src/Microsoft.OpenApi.Hidi/StatsVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ internal class StatsVisitor : OpenApiVisitorBase
{
public int ParameterCount { get; set; }

public override void Visit(OpenApiParameter parameter)
public override void Visit(IOpenApiParameter parameter)
{
ParameterCount++;
}
Expand Down
2 changes: 1 addition & 1 deletion src/Microsoft.OpenApi.Workbench/StatsVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ internal class StatsVisitor : OpenApiVisitorBase
{
public int ParameterCount { get; set; }

public override void Visit(OpenApiParameter parameter)
public override void Visit(IOpenApiParameter parameter)
{
ParameterCount++;
}
Expand Down
106 changes: 106 additions & 0 deletions src/Microsoft.OpenApi/Models/Interfaces/IOpenApiParameter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
using System.Collections.Generic;
using System.Text.Json.Nodes;
using Microsoft.OpenApi.Interfaces;

namespace Microsoft.OpenApi.Models.Interfaces;

/// <summary>
/// Defines the base properties for the example object.
/// This interface is provided for type assertions but should not be implemented by package consumers beyond automatic mocking.
/// </summary>
public interface IOpenApiParameter : IOpenApiDescribedElement, IOpenApiSerializable, IOpenApiReadOnlyExtensible
{
/// <summary>
/// REQUIRED. The name of the parameter. Parameter names are case sensitive.
/// If in is "path", the name field MUST correspond to the associated path segment from the path field in the Paths Object.
/// If in is "header" and the name field is "Accept", "Content-Type" or "Authorization", the parameter definition SHALL be ignored.
/// For all other cases, the name corresponds to the parameter name used by the in property.
/// </summary>
public string Name { get; }

/// <summary>
/// REQUIRED. The location of the parameter.
/// Possible values are "query", "header", "path" or "cookie".
/// </summary>
public ParameterLocation? In { get; }

/// <summary>
/// Determines whether this parameter is mandatory.
/// If the parameter location is "path", this property is REQUIRED and its value MUST be true.
/// Otherwise, the property MAY be included and its default value is false.
/// </summary>
public bool Required { get; }

/// <summary>
/// Specifies that a parameter is deprecated and SHOULD be transitioned out of usage.
/// </summary>
public bool Deprecated { get; }

/// <summary>
/// Sets the ability to pass empty-valued parameters.
/// This is valid only for query parameters and allows sending a parameter with an empty value.
/// Default value is false.
/// If style is used, and if behavior is n/a (cannot be serialized),
/// the value of allowEmptyValue SHALL be ignored.
/// </summary>
public bool AllowEmptyValue { get; }

/// <summary>
/// Describes how the parameter value will be serialized depending on the type of the parameter value.
/// Default values (based on value of in): for query - form; for path - simple; for header - simple;
/// for cookie - form.
/// </summary>
public ParameterStyle? Style { get; }

/// <summary>
/// When this is true, parameter values of type array or object generate separate parameters
/// for each value of the array or key-value pair of the map.
/// For other types of parameters this property has no effect.
/// When style is form, the default value is true.
/// For all other styles, the default value is false.
/// </summary>
public bool Explode { get; }

/// <summary>
/// Determines whether the parameter value SHOULD allow reserved characters,
/// as defined by RFC3986 :/?#[]@!$&amp;'()*+,;= to be included without percent-encoding.
/// This property only applies to parameters with an in value of query.
/// The default value is false.
/// </summary>
public bool AllowReserved { get; }

/// <summary>
/// The schema defining the type used for the parameter.
/// </summary>
public OpenApiSchema Schema { get; }

/// <summary>
/// Examples of the media type. Each example SHOULD contain a value
/// in the correct format as specified in the parameter encoding.
/// The examples object is mutually exclusive of the example object.
/// Furthermore, if referencing a schema which contains an example,
/// the examples value SHALL override the example provided by the schema.
/// </summary>
public IDictionary<string, IOpenApiExample> Examples { get; }

/// <summary>
/// Example of the media type. The example SHOULD match the specified schema and encoding properties
/// if present. The example object is mutually exclusive of the examples object.
/// Furthermore, if referencing a schema which contains an example,
/// the example value SHALL override the example provided by the schema.
/// To represent examples of media types that cannot naturally be represented in JSON or YAML,
/// a string value can contain the example with escaping where necessary.
/// </summary>
public JsonNode Example { get; }

/// <summary>
/// A map containing the representations for the parameter.
/// The key is the media type and the value describes it.
/// The map MUST only contain one entry.
/// For more complex scenarios, the content property can define the media type and schema of the parameter.
/// A parameter MUST contain either a schema property, or a content property, but not both.
/// When example or examples are provided in conjunction with the schema object,
/// the example MUST follow the prescribed serialization strategy for the parameter.
/// </summary>
public IDictionary<string, OpenApiMediaType> Content { get; }
}
8 changes: 4 additions & 4 deletions src/Microsoft.OpenApi/Models/OpenApiComponents.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ public class OpenApiComponents : IOpenApiSerializable, IOpenApiExtensible
public IDictionary<string, OpenApiResponse>? Responses { get; set; } = new Dictionary<string, OpenApiResponse>();

/// <summary>
/// An object to hold reusable <see cref="OpenApiParameter"/> Objects.
/// An object to hold reusable <see cref="IOpenApiParameter"/> Objects.
/// </summary>
public IDictionary<string, OpenApiParameter>? Parameters { get; set; } =
new Dictionary<string, OpenApiParameter>();
public IDictionary<string, IOpenApiParameter>? Parameters { get; set; } =
new Dictionary<string, IOpenApiParameter>();

/// <summary>
/// An object to hold reusable <see cref="OpenApiExample"/> Objects.
Expand Down Expand Up @@ -87,7 +87,7 @@ public OpenApiComponents(OpenApiComponents? components)
{
Schemas = components?.Schemas != null ? new Dictionary<string, OpenApiSchema>(components.Schemas) : null;
Responses = components?.Responses != null ? new Dictionary<string, OpenApiResponse>(components.Responses) : null;
Parameters = components?.Parameters != null ? new Dictionary<string, OpenApiParameter>(components.Parameters) : null;
Parameters = components?.Parameters != null ? new Dictionary<string, IOpenApiParameter>(components.Parameters) : null;
Examples = components?.Examples != null ? new Dictionary<string, IOpenApiExample>(components.Examples) : null;
RequestBodies = components?.RequestBodies != null ? new Dictionary<string, OpenApiRequestBody>(components.RequestBodies) : null;
Headers = components?.Headers != null ? new Dictionary<string, IOpenApiHeader>(components.Headers) : null;
Expand Down
6 changes: 3 additions & 3 deletions src/Microsoft.OpenApi/Models/OpenApiDocument.cs
Original file line number Diff line number Diff line change
Expand Up @@ -277,8 +277,8 @@ public void SerializeAsV2(IOpenApiWriter writer)

// parameters
var parameters = Components?.Parameters != null
? new Dictionary<string, OpenApiParameter>(Components.Parameters)
: new Dictionary<string, OpenApiParameter>();
? new Dictionary<string, IOpenApiParameter>(Components.Parameters)
: [];

if (Components?.RequestBodies != null)
{
Expand Down Expand Up @@ -592,7 +592,7 @@ public bool AddComponent<T>(string id, T componentToRegister)
Components.Schemas.Add(id, openApiSchema);
break;
case OpenApiParameter openApiParameter:
Components.Parameters ??= new Dictionary<string, OpenApiParameter>();
Components.Parameters ??= new Dictionary<string, IOpenApiParameter>();
Components.Parameters.Add(id, openApiParameter);
break;
case OpenApiResponse openApiResponse:
Expand Down
6 changes: 3 additions & 3 deletions src/Microsoft.OpenApi/Models/OpenApiOperation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public class OpenApiOperation : IOpenApiSerializable, IOpenApiExtensible, IOpenA
/// The list MUST NOT include duplicated parameters. A unique parameter is defined by a combination of a name and location.
/// The list can use the Reference Object to link to parameters that are defined at the OpenAPI Object's components/parameters.
/// </summary>
public IList<OpenApiParameter>? Parameters { get; set; } = new List<OpenApiParameter>();
public IList<IOpenApiParameter>? Parameters { get; set; } = [];

/// <summary>
/// The request body applicable for this operation.
Expand Down Expand Up @@ -127,7 +127,7 @@ public OpenApiOperation(OpenApiOperation? operation)
Description = operation?.Description ?? Description;
ExternalDocs = operation?.ExternalDocs != null ? new(operation?.ExternalDocs) : null;
OperationId = operation?.OperationId ?? OperationId;
Parameters = operation?.Parameters != null ? new List<OpenApiParameter>(operation.Parameters) : null;
Parameters = operation?.Parameters != null ? new List<IOpenApiParameter>(operation.Parameters) : null;
RequestBody = operation?.RequestBody != null ? new(operation?.RequestBody) : null;
Responses = operation?.Responses != null ? new(operation?.Responses) : null;
Callbacks = operation?.Callbacks != null ? new Dictionary<string, IOpenApiCallback>(operation.Callbacks) : null;
Expand Down Expand Up @@ -235,7 +235,7 @@ public void SerializeAsV2(IOpenApiWriter writer)
// operationId
writer.WriteProperty(OpenApiConstants.OperationId, OperationId);

List<OpenApiParameter> parameters;
List<IOpenApiParameter> parameters;
if (Parameters == null)
{
parameters = [];
Expand Down
Loading
Loading