Skip to content

Commit

Permalink
Merge pull request #2092 from microsoft/fix/link-reference
Browse files Browse the repository at this point in the history
fix: open API link reference proxy design pattern implementation
  • Loading branch information
MaggieKimani1 authored Jan 28, 2025
2 parents 694a74b + aa993b1 commit 6a96462
Show file tree
Hide file tree
Showing 20 changed files with 206 additions and 166 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 @@ -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 @@ public class OpenApiResponse : IOpenApiReferenceable, IOpenApiExtensible
/// 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 @@ public OpenApiResponse(OpenApiResponse response)
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;
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

0 comments on commit 6a96462

Please sign in to comment.