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: open API link reference proxy design pattern implementation #2092

Merged
merged 2 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 @@ -62,7 +62,7 @@ public override void Visit(OpenApiOperation operation)

public int LinkCount { get; set; }

public override void Visit(OpenApiLink link)
public override void Visit(IOpenApiLink link)
{
LinkCount++;
}
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 @@ -62,7 +62,7 @@ public override void Visit(OpenApiOperation operation)

public int LinkCount { get; set; }

public override void Visit(OpenApiLink link)
public override void Visit(IOpenApiLink link)
{
LinkCount++;
}
Expand Down
37 changes: 37 additions & 0 deletions src/Microsoft.OpenApi/Models/Interfaces/IOpenApiLink.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using System.Collections.Generic;
using Microsoft.OpenApi.Interfaces;

namespace Microsoft.OpenApi.Models.Interfaces;

/// <summary>
/// Defines the base properties for the link object.
/// This interface is provided for type assertions but should not be implemented by package consumers beyond automatic mocking.
/// </summary>
public interface IOpenApiLink : IOpenApiDescribedElement, IOpenApiSerializable, IOpenApiReadOnlyExtensible
{
/// <summary>
/// A relative or absolute reference to an OAS operation.
/// This field is mutually exclusive of the operationId field, and MUST point to an Operation Object.
/// </summary>
public string OperationRef { get; }

/// <summary>
/// The name of an existing, resolvable OAS operation, as defined with a unique operationId.
/// This field is mutually exclusive of the operationRef field.
/// </summary>
public string OperationId { get; }

/// <summary>
/// A map representing parameters to pass to an operation as specified with operationId or identified via operationRef.
/// </summary>
public IDictionary<string, RuntimeExpressionAnyWrapper> Parameters { get; }

/// <summary>
/// A literal value or {expression} to use as a request body when calling the target operation.
/// </summary>
public RuntimeExpressionAnyWrapper RequestBody { get; }
/// <summary>
/// A server object to be used by the target operation.
/// </summary>
public OpenApiServer Server { get; }
}
6 changes: 3 additions & 3 deletions src/Microsoft.OpenApi/Models/OpenApiComponents.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,9 @@ public class OpenApiComponents : IOpenApiSerializable, IOpenApiExtensible
new Dictionary<string, OpenApiSecurityScheme>();

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

/// <summary>
/// An object to hold reusable <see cref="OpenApiCallback"/> Objects.
Expand Down Expand Up @@ -92,7 +92,7 @@ public OpenApiComponents(OpenApiComponents? components)
RequestBodies = components?.RequestBodies != null ? new Dictionary<string, OpenApiRequestBody>(components.RequestBodies) : 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;
Links = components?.Links != null ? new Dictionary<string, IOpenApiLink>(components.Links) : null;
Callbacks = components?.Callbacks != null ? new Dictionary<string, IOpenApiCallback>(components.Callbacks) : null;
PathItems = components?.PathItems != null ? new Dictionary<string, OpenApiPathItem>(components.PathItems) : null;
Extensions = components?.Extensions != null ? new Dictionary<string, IOpenApiExtension>(components.Extensions) : 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 @@ -604,7 +604,7 @@ public bool AddComponent<T>(string id, T componentToRegister)
Components.RequestBodies.Add(id, openApiRequestBody);
break;
case OpenApiLink openApiLink:
Components.Links ??= new Dictionary<string, OpenApiLink>();
Components.Links ??= new Dictionary<string, IOpenApiLink>();
Components.Links.Add(id, openApiLink);
break;
case OpenApiCallback openApiCallback:
Expand Down
95 changes: 31 additions & 64 deletions src/Microsoft.OpenApi/Models/OpenApiLink.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,62 +4,36 @@
using System;
using System.Collections.Generic;
using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Models.Interfaces;
using Microsoft.OpenApi.Writers;

namespace Microsoft.OpenApi.Models
{
/// <summary>
/// Link Object.
/// </summary>
public class OpenApiLink : IOpenApiReferenceable, IOpenApiExtensible
public class OpenApiLink : IOpenApiReferenceable, IOpenApiExtensible, IOpenApiLink
{
/// <summary>
/// A relative or absolute reference to an OAS operation.
/// This field is mutually exclusive of the operationId field, and MUST point to an Operation Object.
/// </summary>
public virtual string OperationRef { get; set; }
/// <inheritdoc/>
public string OperationRef { get; set; }

/// <summary>
/// The name of an existing, resolvable OAS operation, as defined with a unique operationId.
/// This field is mutually exclusive of the operationRef field.
/// </summary>
public virtual string OperationId { get; set; }
/// <inheritdoc/>
public string OperationId { get; set; }

/// <summary>
/// A map representing parameters to pass to an operation as specified with operationId or identified via operationRef.
/// </summary>
public virtual Dictionary<string, RuntimeExpressionAnyWrapper> Parameters { get; set; } =
new();
/// <inheritdoc/>
public IDictionary<string, RuntimeExpressionAnyWrapper> Parameters { get; set; } = new Dictionary<string, RuntimeExpressionAnyWrapper>();

/// <summary>
/// A literal value or {expression} to use as a request body when calling the target operation.
/// </summary>
public virtual RuntimeExpressionAnyWrapper RequestBody { get; set; }

/// <summary>
/// A description of the link.
/// </summary>
public virtual string Description { get; set; }

/// <summary>
/// A server object to be used by the target operation.
/// </summary>
public virtual OpenApiServer Server { get; set; }
/// <inheritdoc/>
public RuntimeExpressionAnyWrapper RequestBody { get; set; }

/// <summary>
/// This object MAY be extended with Specification Extensions.
/// </summary>
public virtual IDictionary<string, IOpenApiExtension> Extensions { get; set; } = new Dictionary<string, IOpenApiExtension>();
/// <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; }
/// <inheritdoc/>
public OpenApiServer Server { get; set; }

/// <summary>
/// Reference pointer.
/// </summary>
public OpenApiReference Reference { get; set; }
/// <inheritdoc/>
public IDictionary<string, IOpenApiExtension> Extensions { get; set; } = new Dictionary<string, IOpenApiExtension>();

/// <summary>
/// Parameterless constructor
Expand All @@ -69,36 +43,31 @@ public OpenApiLink() { }
/// <summary>
/// Initializes a copy of an <see cref="OpenApiLink"/> object
/// </summary>
public OpenApiLink(OpenApiLink link)
public OpenApiLink(IOpenApiLink link)
{
OperationRef = link?.OperationRef ?? OperationRef;
OperationId = link?.OperationId ?? OperationId;
Parameters = link?.Parameters != null ? new(link?.Parameters) : null;
RequestBody = link?.RequestBody != null ? new(link?.RequestBody) : null;
Description = link?.Description ?? Description;
Server = link?.Server != null ? new(link?.Server) : null;
Extensions = link?.Extensions != null ? new Dictionary<string, IOpenApiExtension>(link.Extensions) : null;
UnresolvedReference = link?.UnresolvedReference ?? UnresolvedReference;
Reference = link?.Reference != null ? new(link?.Reference) : null;
Utils.CheckArgumentNull(link);
OperationRef = link.OperationRef ?? OperationRef;
OperationId = link.OperationId ?? OperationId;
Parameters = link.Parameters != null ? new Dictionary<string, RuntimeExpressionAnyWrapper>(link.Parameters) : null;
RequestBody = link.RequestBody != null ? new(link.RequestBody) : null;
Description = link.Description ?? Description;
Server = link.Server != null ? new(link.Server) : null;
Extensions = link.Extensions != null ? new Dictionary<string, IOpenApiExtension>(link.Extensions) : null;
}

/// <summary>
/// Serialize <see cref="OpenApiLink"/> to Open Api v3.1
/// </summary>
public virtual void SerializeAsV31(IOpenApiWriter writer)
/// <inheritdoc/>
public void SerializeAsV31(IOpenApiWriter writer)
{
SerializeInternal(writer, (writer, element) => element.SerializeAsV31(writer));
}

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

internal virtual void SerializeInternal(IOpenApiWriter writer, Action<IOpenApiWriter, IOpenApiSerializable> callback)
internal void SerializeInternal(IOpenApiWriter writer, Action<IOpenApiWriter, IOpenApiSerializable> callback)
{
Utils.CheckArgumentNull(writer);

Expand Down Expand Up @@ -128,9 +97,7 @@ internal virtual void SerializeInternal(IOpenApiWriter writer, Action<IOpenApiWr
writer.WriteEndObject();
}

/// <summary>
/// Serialize <see cref="OpenApiLink"/> to Open Api v2.0
/// </summary>
/// <inheritdoc/>
public void SerializeAsV2(IOpenApiWriter writer)
{
// Link object does not exist in V2.
Expand Down
4 changes: 2 additions & 2 deletions src/Microsoft.OpenApi/Models/OpenApiResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
/// The key of the map is a short name for the link,
/// following the naming constraints of the names for Component Objects.
/// </summary>
public virtual IDictionary<string, OpenApiLink> Links { get; set; } = new Dictionary<string, OpenApiLink>();
public virtual IDictionary<string, IOpenApiLink> Links { get; set; } = new Dictionary<string, IOpenApiLink>();

/// <summary>
/// This object MAY be extended with Specification Extensions.
Expand Down Expand Up @@ -66,7 +66,7 @@
Description = response?.Description ?? Description;
Headers = response?.Headers != null ? new Dictionary<string, IOpenApiHeader>(response.Headers) : null;
Content = response?.Content != null ? new Dictionary<string, OpenApiMediaType>(response.Content) : null;
Links = response?.Links != null ? new Dictionary<string, OpenApiLink>(response.Links) : null;
Links = response?.Links != null ? new Dictionary<string, IOpenApiLink>(response.Links) : null;

Check warning

Code scanning / CodeQL

Virtual call in constructor or destructor Warning

Avoid virtual calls in a constructor or destructor.
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 get fixed when we fix the proxy design pattern implementation for OpenAPI Response

Extensions = response?.Extensions != null ? new Dictionary<string, IOpenApiExtension>(response.Extensions) : null;
UnresolvedReference = response?.UnresolvedReference ?? UnresolvedReference;
Reference = response?.Reference != null ? new(response?.Reference) : null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ public OpenApiCallbackReference(string referenceId, OpenApiDocument hostDocument
public OpenApiCallbackReference(OpenApiCallbackReference callback)
{
Utils.CheckArgumentNull(callback);
Reference = callback?.Reference != null ? new(callback.Reference) : null;
UnresolvedReference = callback?.UnresolvedReference ?? false;
Reference = callback.Reference != null ? new(callback.Reference) : null;
UnresolvedReference = callback.UnresolvedReference;
}

internal OpenApiCallbackReference(OpenApiCallback target, string referenceId)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ public OpenApiExampleReference(string referenceId, OpenApiDocument hostDocument,
public OpenApiExampleReference(OpenApiExampleReference example)
{
Utils.CheckArgumentNull(example);
Reference = example?.Reference != null ? new(example.Reference) : null;
UnresolvedReference = example?.UnresolvedReference ?? false;
Reference = example.Reference != null ? new(example.Reference) : null;
UnresolvedReference = example.UnresolvedReference;
//no need to copy summary and description as if they are not overridden, they will be fetched from the target
//if they are, the reference copy will handle it
}
Expand Down
Loading
Loading