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/headers reference #2091

Merged
merged 6 commits into from
Jan 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
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
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 @@
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)

Check warning

Code scanning / CodeQL

Useless type test Warning

There is no need to test whether an instance of
OpenApiLink
is also an instance of
IOpenApiReferenceable
- it always is.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will be fixed when we migrate the link proxy design pattern implementation to

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

{
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
Loading