Skip to content

Commit

Permalink
Merge pull request #2093 from microsoft/fix/parameter-reference
Browse files Browse the repository at this point in the history
fix: parameter reference proxy design pattern implementation
  • Loading branch information
MaggieKimani1 authored Jan 28, 2025
2 parents 6a96462 + 1006879 commit ed6ffa1
Show file tree
Hide file tree
Showing 50 changed files with 533 additions and 501 deletions.
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

0 comments on commit ed6ffa1

Please sign in to comment.