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: callback reference proxy implementation #2090

Merged
merged 3 commits into from
Jan 27, 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
3 changes: 2 additions & 1 deletion src/Microsoft.OpenApi.Hidi/StatsVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Models.Interfaces;
using Microsoft.OpenApi.Services;

namespace Microsoft.OpenApi.Hidi
Expand Down Expand Up @@ -68,7 +69,7 @@ public override void Visit(OpenApiLink link)

public int CallbackCount { get; set; }

public override void Visit(OpenApiCallback callback)
public override void Visit(IOpenApiCallback callback)
{
CallbackCount++;
}
Expand Down
3 changes: 2 additions & 1 deletion src/Microsoft.OpenApi.Workbench/StatsVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Models.Interfaces;
using Microsoft.OpenApi.Services;

namespace Microsoft.OpenApi.Workbench
Expand Down Expand Up @@ -68,7 +69,7 @@ public override void Visit(OpenApiLink link)

public int CallbackCount { get; set; }

public override void Visit(OpenApiCallback callback)
public override void Visit(IOpenApiCallback callback)
{
CallbackCount++;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
/// <typeparam name="V">The type of the interface implemented by both the target and the reference type</typeparam>
public interface IOpenApiReferenceHolder<out T, V> : IOpenApiReferenceHolder<T> where T : IOpenApiReferenceable, V
{
//TODO merge this interface with the previous once all implementations are updated

Check warning on line 26 in src/Microsoft.OpenApi/Interfaces/IOpenApiReferenceHolder.cs

View workflow job for this annotation

GitHub Actions / Build

Complete the task associated to this 'TODO' comment. (https://rules.sonarsource.com/csharp/RSPEC-1135)
/// <summary>
/// Copy the reference as a target element with overrides.
/// </summary>
Expand All @@ -38,6 +38,7 @@
/// Indicates if object is populated with data or is just a reference to the data
/// </summary>
bool UnresolvedReference { get; set; }
//TODO the UnresolvedReference property setter should be removed and a default implementation that checks whether the target is null for the getter should be provided instead

Check warning on line 41 in src/Microsoft.OpenApi/Interfaces/IOpenApiReferenceHolder.cs

View workflow job for this annotation

GitHub Actions / Build

Complete the task associated to this 'TODO' comment. (https://rules.sonarsource.com/csharp/RSPEC-1135)

/// <summary>
/// Reference object.
Expand Down
18 changes: 18 additions & 0 deletions src/Microsoft.OpenApi/Models/Interfaces/IOpenApiCallback.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@

using System.Collections.Generic;
using Microsoft.OpenApi.Expressions;
using Microsoft.OpenApi.Interfaces;

namespace Microsoft.OpenApi.Models.Interfaces;

/// <summary>
/// Defines the base properties for the callback object.
/// This interface is provided for type assertions but should not be implemented by package consumers beyond automatic mocking.
/// </summary>
public interface IOpenApiCallback : IOpenApiSerializable, IOpenApiReadOnlyExtensible
{
/// <summary>
/// A Path Item Object used to define a callback request and expected responses.
/// </summary>
public Dictionary<RuntimeExpression, OpenApiPathItem> PathItems { get; }
}
30 changes: 9 additions & 21 deletions src/Microsoft.OpenApi/Models/OpenApiCallback.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,35 +5,25 @@
using System.Collections.Generic;
using Microsoft.OpenApi.Expressions;
using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Models.Interfaces;
using Microsoft.OpenApi.Writers;

namespace Microsoft.OpenApi.Models
{
/// <summary>
/// Callback Object: A map of possible out-of band callbacks related to the parent operation.
/// </summary>
public class OpenApiCallback : IOpenApiReferenceable, IOpenApiExtensible
public class OpenApiCallback : IOpenApiReferenceable, IOpenApiExtensible, IOpenApiCallback
{
/// <summary>
/// A Path Item Object used to define a callback request and expected responses.
/// </summary>
public virtual Dictionary<RuntimeExpression, OpenApiPathItem> PathItems { get; set; }
= new();
/// <inheritdoc/>
public Dictionary<RuntimeExpression, OpenApiPathItem> PathItems { 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>
/// This object MAY be extended with Specification Extensions.
/// </summary>
public virtual IDictionary<string, IOpenApiExtension> Extensions { get; set; } = new Dictionary<string, IOpenApiExtension>();
public IDictionary<string, IOpenApiExtension> Extensions { get; set; } = new Dictionary<string, IOpenApiExtension>();

/// <summary>
/// Parameter-less constructor
Expand All @@ -43,11 +33,9 @@ public OpenApiCallback() { }
/// <summary>
/// Initializes a copy of an <see cref="OpenApiCallback"/> object
/// </summary>
public OpenApiCallback(OpenApiCallback callback)
public OpenApiCallback(IOpenApiCallback callback)
{
PathItems = callback?.PathItems != null ? new(callback?.PathItems) : null;
UnresolvedReference = callback?.UnresolvedReference ?? UnresolvedReference;
Reference = callback?.Reference != null ? new(callback?.Reference) : null;
Extensions = callback?.Extensions != null ? new Dictionary<string, IOpenApiExtension>(callback.Extensions) : null;
}

Expand All @@ -71,15 +59,15 @@ public void AddPathItem(RuntimeExpression expression, OpenApiPathItem pathItem)
/// </summary>
/// <param name="writer"></param>
/// <exception cref="System.NotImplementedException"></exception>
public virtual void SerializeAsV31(IOpenApiWriter writer)
public void SerializeAsV31(IOpenApiWriter writer)
{
SerializeInternal(writer, OpenApiSpecVersion.OpenApi3_1, (writer, element) => element.SerializeAsV31(writer));
}

/// <summary>
/// Serialize <see cref="OpenApiCallback"/> 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));
}
Expand Down
22 changes: 11 additions & 11 deletions src/Microsoft.OpenApi/Models/OpenApiComponents.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,55 +25,55 @@ public class OpenApiComponents : IOpenApiSerializable, IOpenApiExtensible
/// <summary>
/// An object to hold reusable <see cref="OpenApiResponse"/> Objects.
/// </summary>
public virtual IDictionary<string, OpenApiResponse>? Responses { get; set; } = new Dictionary<string, OpenApiResponse>();
public IDictionary<string, OpenApiResponse>? Responses { get; set; } = new Dictionary<string, OpenApiResponse>();

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

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

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

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

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

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

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

/// <summary>
/// An object to hold reusable <see cref="OpenApiPathItem"/> Object.
/// </summary>
public virtual IDictionary<string, OpenApiPathItem>? PathItems { get; set; } = new Dictionary<string, OpenApiPathItem>();
public IDictionary<string, OpenApiPathItem>? PathItems { get; set; } = new Dictionary<string, OpenApiPathItem>();

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

/// <summary>
/// Parameter-less constructor
Expand All @@ -93,7 +93,7 @@ public OpenApiComponents(OpenApiComponents? components)
Headers = components?.Headers != null ? new Dictionary<string, OpenApiHeader>(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, OpenApiCallback>(components.Callbacks) : 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 @@ -608,7 +608,7 @@ public bool AddComponent<T>(string id, T componentToRegister)
Components.Links.Add(id, openApiLink);
break;
case OpenApiCallback openApiCallback:
Components.Callbacks ??= new Dictionary<string, OpenApiCallback>();
Components.Callbacks ??= new Dictionary<string, IOpenApiCallback>();
Components.Callbacks.Add(id, openApiCallback);
break;
case OpenApiPathItem openApiPathItem:
Expand Down
5 changes: 3 additions & 2 deletions src/Microsoft.OpenApi/Models/OpenApiOperation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Models.Interfaces;
using Microsoft.OpenApi.Models.References;
using Microsoft.OpenApi.Writers;

Expand Down Expand Up @@ -80,7 +81,7 @@ public class OpenApiOperation : IOpenApiSerializable, IOpenApiExtensible, IOpenA
/// The key value used to identify the callback object is an expression, evaluated at runtime,
/// that identifies a URL to use for the callback operation.
/// </summary>
public IDictionary<string, OpenApiCallback>? Callbacks { get; set; } = new Dictionary<string, OpenApiCallback>();
public IDictionary<string, IOpenApiCallback>? Callbacks { get; set; } = new Dictionary<string, IOpenApiCallback>();

/// <summary>
/// Declares this operation to be deprecated. Consumers SHOULD refrain from usage of the declared operation.
Expand Down Expand Up @@ -129,7 +130,7 @@ public OpenApiOperation(OpenApiOperation? operation)
Parameters = operation?.Parameters != null ? new List<OpenApiParameter>(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, OpenApiCallback>(operation.Callbacks) : null;
Callbacks = operation?.Callbacks != null ? new Dictionary<string, IOpenApiCallback>(operation.Callbacks) : null;
Deprecated = operation?.Deprecated ?? Deprecated;
Security = operation?.Security != null ? new List<OpenApiSecurityRequirement>(operation.Security) : null;
Servers = operation?.Servers != null ? new List<OpenApiServer>(operation.Servers) : null;
Expand Down
59 changes: 43 additions & 16 deletions src/Microsoft.OpenApi/Models/References/OpenApiCallbackReference.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,23 @@
using System.Collections.Generic;
using Microsoft.OpenApi.Expressions;
using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Models.Interfaces;
using Microsoft.OpenApi.Writers;

namespace Microsoft.OpenApi.Models.References
{
/// <summary>
/// Callback Object Reference: A reference to a map of possible out-of band callbacks related to the parent operation.
/// </summary>
public class OpenApiCallbackReference : OpenApiCallback, IOpenApiReferenceHolder<OpenApiCallback>
public class OpenApiCallbackReference : IOpenApiCallback, IOpenApiReferenceHolder<OpenApiCallback, IOpenApiCallback>
{
#nullable enable
internal OpenApiCallback _target;
private readonly OpenApiReference _reference;
/// <inheritdoc/>
public OpenApiReference Reference { get; set; }

/// <inheritdoc/>
public bool UnresolvedReference { get; set; }

/// <summary>
/// Gets the target callback.
Expand All @@ -29,7 +34,7 @@ public OpenApiCallback Target
{
get
{
_target ??= Reference.HostDocument.ResolveReferenceTo<OpenApiCallback>(_reference);
_target ??= Reference.HostDocument.ResolveReferenceTo<OpenApiCallback>(Reference);
return _target;
}
}
Expand All @@ -48,41 +53,49 @@ public OpenApiCallbackReference(string referenceId, OpenApiDocument hostDocument
{
Utils.CheckArgumentNullOrEmpty(referenceId);

_reference = new OpenApiReference()
Reference = new OpenApiReference()
{
Id = referenceId,
HostDocument = hostDocument,
Type = ReferenceType.Callback,
ExternalResource = externalResource
};
}

Reference = _reference;
/// <summary>
/// Copy constructor
/// </summary>
/// <param name="callback">The callback reference to copy</param>
public OpenApiCallbackReference(OpenApiCallbackReference callback)
{
Utils.CheckArgumentNull(callback);
Reference = callback?.Reference != null ? new(callback.Reference) : null;
UnresolvedReference = callback?.UnresolvedReference ?? false;
}

internal OpenApiCallbackReference(OpenApiCallback target, string referenceId)
{
_target = target;

_reference = new OpenApiReference()
Reference = new OpenApiReference()
{
Id = referenceId,
Type = ReferenceType.Callback,
};
}

/// <inheritdoc/>
public override Dictionary<RuntimeExpression, OpenApiPathItem> PathItems { get => Target.PathItems; set => Target.PathItems = value; }
public Dictionary<RuntimeExpression, OpenApiPathItem> PathItems { get => Target.PathItems; }

/// <inheritdoc/>
public override IDictionary<string, IOpenApiExtension> Extensions { get => Target.Extensions; set => Target.Extensions = value; }
public IDictionary<string, IOpenApiExtension> Extensions { get => Target.Extensions; }

/// <inheritdoc/>
public override void SerializeAsV3(IOpenApiWriter writer)
public void SerializeAsV3(IOpenApiWriter writer)
{
if (!writer.GetSettings().ShouldInlineReference(_reference))
if (!writer.GetSettings().ShouldInlineReference(Reference))
{
_reference.SerializeAsV3(writer);
return;
Reference.SerializeAsV3(writer);
}
else
{
Expand All @@ -91,19 +104,33 @@ public override void SerializeAsV3(IOpenApiWriter writer)
}

/// <inheritdoc/>
public override void SerializeAsV31(IOpenApiWriter writer)
public void SerializeAsV31(IOpenApiWriter writer)
{
if (!writer.GetSettings().ShouldInlineReference(_reference))
if (!writer.GetSettings().ShouldInlineReference(Reference))
{
_reference.SerializeAsV31(writer);
return;
Reference.SerializeAsV31(writer);
}
else
{
SerializeInternal(writer, (writer, element) => element.SerializeAsV31(writer));
}
}

/// <inheritdoc/>
public IOpenApiCallback CopyReferenceAsTargetElementWithOverrides(IOpenApiCallback openApiExample)
{
// the copy here is never called since callbacks do not have any overridable fields.
// if the spec evolves to include overridable fields for callbacks, the serialize methods will need to call this copy method.
return openApiExample is OpenApiCallback ? new OpenApiCallback(this) : openApiExample;
}

/// <inheritdoc/>
public void SerializeAsV2(IOpenApiWriter writer)
{
// examples components are not supported in OAS 2.0
Reference.SerializeAsV2(writer);
}

/// <inheritdoc/>
private void SerializeInternal(IOpenApiWriter writer,
Action<IOpenApiWriter, IOpenApiReferenceable> action)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public class OpenApiExampleReference : IOpenApiReferenceHolder<OpenApiExample, I
public OpenApiReference Reference { get; set; }

/// <inheritdoc/>
public bool UnresolvedReference { get; set; } = false;
public bool UnresolvedReference { get; set; }
internal OpenApiExample _target;

/// <summary>
Expand Down Expand Up @@ -68,7 +68,7 @@ public OpenApiExampleReference(OpenApiExampleReference example)
{
Utils.CheckArgumentNull(example);
Reference = example?.Reference != null ? new(example.Reference) : null;
UnresolvedReference = example?.UnresolvedReference ?? UnresolvedReference;
UnresolvedReference = example?.UnresolvedReference ?? false;
//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