Skip to content

Commit

Permalink
Merge pull request #2091 from microsoft/fix/headers-reference
Browse files Browse the repository at this point in the history
fix/headers reference
  • Loading branch information
baywet authored Jan 28, 2025
2 parents 16ba957 + 0cb4ccb commit 1b65d40
Show file tree
Hide file tree
Showing 35 changed files with 321 additions and 246 deletions.
2 changes: 1 addition & 1 deletion src/Microsoft.OpenApi.Hidi/StatsVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public override void Visit(OpenApiSchema schema)

public int HeaderCount { get; set; }

public override void Visit(IDictionary<string, OpenApiHeader> headers)
public override void Visit(IDictionary<string, IOpenApiHeader> headers)
{
HeaderCount++;
}
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 @@ -27,7 +27,7 @@ public override void Visit(OpenApiSchema schema)

public int HeaderCount { get; set; }

public override void Visit(IDictionary<string, OpenApiHeader> headers)
public override void Visit(IDictionary<string, IOpenApiHeader> headers)
{
HeaderCount++;
}
Expand Down
23 changes: 16 additions & 7 deletions src/Microsoft.OpenApi/Extensions/OpenApiReferencableExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,15 +92,24 @@ private static IOpenApiReferenceable ResolveReferenceOnResponseElement(
string mapKey,
JsonPointer pointer)
{
switch (propertyName)
if (!string.IsNullOrEmpty(mapKey))
{
case OpenApiConstants.Headers when mapKey != null:
return responseElement.Headers[mapKey];
case OpenApiConstants.Links when mapKey != null:
return responseElement.Links[mapKey];
default:
throw new OpenApiException(string.Format(SRResource.InvalidReferenceId, pointer));
if (OpenApiConstants.Headers.Equals(propertyName, StringComparison.Ordinal) &&
responseElement?.Headers != null &&
responseElement.Headers.TryGetValue(mapKey, out var headerElement) &&
headerElement is IOpenApiReferenceable referenceable)
{
return referenceable;
}
if (OpenApiConstants.Links.Equals(propertyName, StringComparison.Ordinal) &&
responseElement?.Links != null &&
responseElement.Links.TryGetValue(mapKey, out var linkElement) &&
linkElement is IOpenApiReferenceable referenceable2)
{
return referenceable2;
}
}
throw new OpenApiException(string.Format(SRResource.InvalidReferenceId, pointer));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,10 @@
namespace Microsoft.OpenApi.Models.Interfaces;

/// <summary>
/// Describes an element that has a summary and description.
/// Describes an element that has a description.
/// </summary>
public interface IOpenApiDescribedElement : IOpenApiElement
{
/// <summary>
/// Short description for the example.
/// </summary>
public string Summary { get; set; }

/// <summary>
/// Long description for the example.
/// CommonMark syntax MAY be used for rich text representation.
Expand Down
2 changes: 1 addition & 1 deletion src/Microsoft.OpenApi/Models/Interfaces/IOpenApiExample.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace Microsoft.OpenApi.Models.Interfaces;
/// 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 IOpenApiExample : IOpenApiDescribedElement, IOpenApiSerializable, IOpenApiReadOnlyExtensible
public interface IOpenApiExample : IOpenApiDescribedElement, IOpenApiSummarizedElement, IOpenApiSerializable, IOpenApiReadOnlyExtensible
{
/// <summary>
/// Embedded literal example. The value field and externalValue field are mutually
Expand Down
65 changes: 65 additions & 0 deletions src/Microsoft.OpenApi/Models/Interfaces/IOpenApiHeader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@

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 headers object.
/// This interface is provided for type assertions but should not be implemented by package consumers beyond automatic mocking.
/// </summary>
public interface IOpenApiHeader : IOpenApiDescribedElement, IOpenApiSerializable, IOpenApiReadOnlyExtensible
{
/// <summary>
/// Determines whether this header is mandatory.
/// </summary>
public bool Required { get; }

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

/// <summary>
/// Sets the ability to pass empty-valued headers.
/// </summary>
public bool AllowEmptyValue { get; }

/// <summary>
/// Describes how the header value will be serialized depending on the type of the header value.
/// </summary>
public ParameterStyle? Style { get; }

/// <summary>
/// When this is true, header values of type array or object generate separate parameters
/// for each value of the array or key-value pair of the map.
/// </summary>
public bool Explode { get; }

/// <summary>
/// Determines whether the header value SHOULD allow reserved characters, as defined by RFC3986.
/// </summary>
public bool AllowReserved { get; }

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

/// <summary>
/// Example of the media type.
/// </summary>
public JsonNode Example { get; }

/// <summary>
/// Examples of the media type.
/// </summary>
public IDictionary<string, IOpenApiExample> Examples { get; }

/// <summary>
/// A map containing the representations for the header.
/// </summary>
public IDictionary<string, OpenApiMediaType> Content { get; }

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using Microsoft.OpenApi.Interfaces;

namespace Microsoft.OpenApi.Models.Interfaces;
/// <summary>
/// Describes an element that has a summary.
/// </summary>
public interface IOpenApiSummarizedElement : IOpenApiElement
{
/// <summary>
/// Short description for the example.
/// </summary>
public string Summary { get; set; }
}
6 changes: 3 additions & 3 deletions src/Microsoft.OpenApi/Models/OpenApiComponents.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ public class OpenApiComponents : IOpenApiSerializable, IOpenApiExtensible
new Dictionary<string, OpenApiRequestBody>();

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

/// <summary>
/// An object to hold reusable <see cref="OpenApiSecurityScheme"/> Objects.
Expand Down Expand Up @@ -90,7 +90,7 @@ public OpenApiComponents(OpenApiComponents? components)
Parameters = components?.Parameters != null ? new Dictionary<string, OpenApiParameter>(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, OpenApiHeader>(components.Headers) : null;
Headers = components?.Headers != null ? new Dictionary<string, IOpenApiHeader>(components.Headers) : null;
SecuritySchemes = components?.SecuritySchemes != null ? new Dictionary<string, OpenApiSecurityScheme>(components.SecuritySchemes) : null;
Links = components?.Links != null ? new Dictionary<string, OpenApiLink>(components.Links) : null;
Callbacks = components?.Callbacks != null ? new Dictionary<string, IOpenApiCallback>(components.Callbacks) : null;
Expand Down
2 changes: 1 addition & 1 deletion src/Microsoft.OpenApi/Models/OpenApiDocument.cs
Original file line number Diff line number Diff line change
Expand Up @@ -620,7 +620,7 @@ public bool AddComponent<T>(string id, T componentToRegister)
Components.Examples.Add(id, openApiExample);
break;
case OpenApiHeader openApiHeader:
Components.Headers ??= new Dictionary<string, OpenApiHeader>();
Components.Headers ??= new Dictionary<string, IOpenApiHeader>();
Components.Headers.Add(id, openApiHeader);
break;
case OpenApiSecurityScheme openApiSecurityScheme:
Expand Down
5 changes: 3 additions & 2 deletions src/Microsoft.OpenApi/Models/OpenApiEncoding.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Collections.Generic;
using Microsoft.OpenApi.Extensions;
using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Models.Interfaces;
using Microsoft.OpenApi.Writers;

namespace Microsoft.OpenApi.Models
Expand All @@ -24,7 +25,7 @@ public class OpenApiEncoding : IOpenApiSerializable, IOpenApiExtensible
/// <summary>
/// A map allowing additional information to be provided as headers.
/// </summary>
public IDictionary<string, OpenApiHeader> Headers { get; set; } = new Dictionary<string, OpenApiHeader>();
public IDictionary<string, IOpenApiHeader> Headers { get; set; } = new Dictionary<string, IOpenApiHeader>();

/// <summary>
/// Describes how a specific property value will be serialized depending on its type.
Expand Down Expand Up @@ -64,7 +65,7 @@ public OpenApiEncoding() { }
public OpenApiEncoding(OpenApiEncoding encoding)
{
ContentType = encoding?.ContentType ?? ContentType;
Headers = encoding?.Headers != null ? new Dictionary<string, OpenApiHeader>(encoding.Headers) : null;
Headers = encoding?.Headers != null ? new Dictionary<string, IOpenApiHeader>(encoding.Headers) : null;
Style = encoding?.Style ?? Style;
Explode = encoding?.Explode ?? Explode;
AllowReserved = encoding?.AllowReserved ?? AllowReserved;
Expand Down
11 changes: 6 additions & 5 deletions src/Microsoft.OpenApi/Models/OpenApiExample.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,12 @@ public OpenApiExample() { }
/// <param name="example">The <see cref="IOpenApiExample"/> object</param>
public OpenApiExample(IOpenApiExample example)
{
Summary = example?.Summary ?? Summary;
Description = example?.Description ?? Description;
Value = example?.Value != null ? JsonNodeCloneHelper.Clone(example.Value) : null;
ExternalValue = example?.ExternalValue ?? ExternalValue;
Extensions = example?.Extensions != null ? new Dictionary<string, IOpenApiExtension>(example.Extensions) : null;
Utils.CheckArgumentNull(example);
Summary = example.Summary ?? Summary;
Description = example.Description ?? Description;
Value = example.Value != null ? JsonNodeCloneHelper.Clone(example.Value) : null;
ExternalValue = example.ExternalValue ?? ExternalValue;
Extensions = example.Extensions != null ? new Dictionary<string, IOpenApiExtension>(example.Extensions) : null;
}

/// <inheritdoc/>
Expand Down
105 changes: 31 additions & 74 deletions src/Microsoft.OpenApi/Models/OpenApiHeader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,84 +16,43 @@ namespace Microsoft.OpenApi.Models
/// Header Object.
/// The Header Object follows the structure of the Parameter Object.
/// </summary>
public class OpenApiHeader : IOpenApiReferenceable, IOpenApiExtensible
public class OpenApiHeader : IOpenApiHeader, IOpenApiReferenceable, IOpenApiExtensible
{
private OpenApiSchema _schema;
/// <inheritdoc/>
public string Description { get; set; }

/// <summary>
/// Indicates if object is populated with data or is just a reference to the data
/// </summary>
public virtual bool UnresolvedReference { get; set; }

/// <summary>
/// Reference pointer.
/// </summary>
public OpenApiReference Reference { get; set; }

/// <summary>
/// A brief description of the header.
/// </summary>
public virtual string Description { get; set; }
/// <inheritdoc/>
public bool Required { get; set; }

/// <summary>
/// Determines whether this header is mandatory.
/// </summary>
public virtual bool Required { get; set; }

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

/// <summary>
/// Sets the ability to pass empty-valued headers.
/// </summary>
public virtual bool AllowEmptyValue { get; set; }
/// <inheritdoc/>
public bool AllowEmptyValue { get; set; }

/// <summary>
/// Describes how the header value will be serialized depending on the type of the header value.
/// </summary>
public virtual ParameterStyle? Style { get; set; }
/// <inheritdoc/>
public ParameterStyle? Style { get; set; }

/// <summary>
/// When this is true, header values of type array or object generate separate parameters
/// for each value of the array or key-value pair of the map.
/// </summary>
public virtual bool Explode { get; set; }
/// <inheritdoc/>
public bool Explode { get; set; }

/// <summary>
/// Determines whether the header value SHOULD allow reserved characters, as defined by RFC3986.
/// </summary>
public virtual bool AllowReserved { get; set; }
/// <inheritdoc/>
public bool AllowReserved { get; set; }

/// <summary>
/// The schema defining the type used for the request body.
/// </summary>
public virtual OpenApiSchema Schema
{
get => _schema;
set => _schema = value;
}
/// <inheritdoc/>
public OpenApiSchema Schema { get; set; }

/// <summary>
/// Example of the media type.
/// </summary>
public virtual JsonNode Example { get; set; }
/// <inheritdoc/>
public JsonNode Example { get; set; }

/// <summary>
/// Examples of the media type.
/// </summary>
public virtual IDictionary<string, IOpenApiExample> Examples { get; set; } = new Dictionary<string, IOpenApiExample>();
/// <inheritdoc/>
public IDictionary<string, IOpenApiExample> Examples { get; set; } = new Dictionary<string, IOpenApiExample>();

/// <summary>
/// A map containing the representations for the header.
/// </summary>
public virtual IDictionary<string, OpenApiMediaType> Content { get; set; } = new Dictionary<string, OpenApiMediaType>();
/// <inheritdoc/>
public IDictionary<string, OpenApiMediaType> Content { get; set; } = new Dictionary<string, OpenApiMediaType>();

/// <summary>
/// This object MAY be extended with Specification Extensions.
/// </summary>
public virtual IDictionary<string, IOpenApiExtension> Extensions { get; set; } = new Dictionary<string, IOpenApiExtension>();
/// <inheritdoc/>
public IDictionary<string, IOpenApiExtension> Extensions { get; set; } = new Dictionary<string, IOpenApiExtension>();

/// <summary>
/// Parameter-less constructor
Expand All @@ -103,18 +62,16 @@ public OpenApiHeader() { }
/// <summary>
/// Initializes a copy of an <see cref="OpenApiHeader"/> object
/// </summary>
public OpenApiHeader(OpenApiHeader header)
public OpenApiHeader(IOpenApiHeader header)
{
UnresolvedReference = header?.UnresolvedReference ?? UnresolvedReference;
Reference = header?.Reference != null ? new(header?.Reference) : null;
Description = header?.Description ?? Description;
Required = header?.Required ?? Required;
Deprecated = header?.Deprecated ?? Deprecated;
AllowEmptyValue = header?.AllowEmptyValue ?? AllowEmptyValue;
Style = header?.Style ?? Style;
Explode = header?.Explode ?? Explode;
AllowReserved = header?.AllowReserved ?? AllowReserved;
_schema = header?.Schema != null ? new(header.Schema) : null;
Schema = header?.Schema != null ? new(header.Schema) : null;
Example = header?.Example != null ? JsonNodeCloneHelper.Clone(header.Example) : null;
Examples = header?.Examples != null ? new Dictionary<string, IOpenApiExample>(header.Examples) : null;
Content = header?.Content != null ? new Dictionary<string, OpenApiMediaType>(header.Content) : null;
Expand All @@ -124,20 +81,20 @@ public OpenApiHeader(OpenApiHeader header)
/// <summary>
/// Serialize <see cref="OpenApiHeader"/> to Open Api v3.1
/// </summary>
public virtual void SerializeAsV31(IOpenApiWriter writer)
public void SerializeAsV31(IOpenApiWriter writer)
{
SerializeInternal(writer, OpenApiSpecVersion.OpenApi3_0, (writer, element) => element.SerializeAsV31(writer));
}

/// <summary>
/// Serialize <see cref="OpenApiHeader"/> to Open Api v3.0
/// </summary>
public virtual void SerializeAsV3(IOpenApiWriter writer)
public void SerializeAsV3(IOpenApiWriter writer)
{
SerializeInternal(writer, OpenApiSpecVersion.OpenApi3_0, (writer, element) => element.SerializeAsV3(writer));
}

internal virtual void SerializeInternal(IOpenApiWriter writer, OpenApiSpecVersion version,
internal void SerializeInternal(IOpenApiWriter writer, OpenApiSpecVersion version,
Action<IOpenApiWriter, IOpenApiSerializable> callback)
{
Utils.CheckArgumentNull(writer);
Expand Down Expand Up @@ -186,7 +143,7 @@ internal virtual void SerializeInternal(IOpenApiWriter writer, OpenApiSpecVersio
/// <summary>
/// Serialize to OpenAPI V2 document without using reference.
/// </summary>
public virtual void SerializeAsV2(IOpenApiWriter writer)
public void SerializeAsV2(IOpenApiWriter writer)
{
Utils.CheckArgumentNull(writer);

Expand Down
Loading

0 comments on commit 1b65d40

Please sign in to comment.