Skip to content

Commit 2cbb0fa

Browse files
committed
fix: callback reference proxy implementation
1 parent cc28ff2 commit 2cbb0fa

File tree

18 files changed

+132
-98
lines changed

18 files changed

+132
-98
lines changed

src/Microsoft.OpenApi.Hidi/StatsVisitor.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System;
55
using System.Collections.Generic;
66
using Microsoft.OpenApi.Models;
7+
using Microsoft.OpenApi.Models.Interfaces;
78
using Microsoft.OpenApi.Services;
89

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

6970
public int CallbackCount { get; set; }
7071

71-
public override void Visit(OpenApiCallback callback)
72+
public override void Visit(IOpenApiCallback callback)
7273
{
7374
CallbackCount++;
7475
}

src/Microsoft.OpenApi.Workbench/StatsVisitor.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System;
55
using System.Collections.Generic;
66
using Microsoft.OpenApi.Models;
7+
using Microsoft.OpenApi.Models.Interfaces;
78
using Microsoft.OpenApi.Services;
89

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

6970
public int CallbackCount { get; set; }
7071

71-
public override void Visit(OpenApiCallback callback)
72+
public override void Visit(IOpenApiCallback callback)
7273
{
7374
CallbackCount++;
7475
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
2+
using System.Collections.Generic;
3+
using Microsoft.OpenApi.Expressions;
4+
using Microsoft.OpenApi.Interfaces;
5+
6+
namespace Microsoft.OpenApi.Models.Interfaces;
7+
8+
/// <summary>
9+
/// Defines the base properties for the callback object.
10+
/// This interface is provided for type assertions but should not be implemented by package consumers beyond automatic mocking.
11+
/// </summary>
12+
public interface IOpenApiCallback : IOpenApiSerializable, IOpenApiReadOnlyExtensible
13+
{
14+
/// <summary>
15+
/// A Path Item Object used to define a callback request and expected responses.
16+
/// </summary>
17+
public Dictionary<RuntimeExpression, OpenApiPathItem> PathItems { get; }
18+
}

src/Microsoft.OpenApi/Models/OpenApiCallback.cs

+9-21
Original file line numberDiff line numberDiff line change
@@ -5,35 +5,25 @@
55
using System.Collections.Generic;
66
using Microsoft.OpenApi.Expressions;
77
using Microsoft.OpenApi.Interfaces;
8+
using Microsoft.OpenApi.Models.Interfaces;
89
using Microsoft.OpenApi.Writers;
910

1011
namespace Microsoft.OpenApi.Models
1112
{
1213
/// <summary>
1314
/// Callback Object: A map of possible out-of band callbacks related to the parent operation.
1415
/// </summary>
15-
public class OpenApiCallback : IOpenApiReferenceable, IOpenApiExtensible
16+
public class OpenApiCallback : IOpenApiReferenceable, IOpenApiExtensible, IOpenApiCallback
1617
{
17-
/// <summary>
18-
/// A Path Item Object used to define a callback request and expected responses.
19-
/// </summary>
20-
public virtual Dictionary<RuntimeExpression, OpenApiPathItem> PathItems { get; set; }
21-
= new();
18+
/// <inheritdoc/>
19+
public Dictionary<RuntimeExpression, OpenApiPathItem> PathItems { get; set; }
20+
= [];
2221

23-
/// <summary>
24-
/// Indicates if object is populated with data or is just a reference to the data
25-
/// </summary>
26-
public virtual bool UnresolvedReference { get; set; }
27-
28-
/// <summary>
29-
/// Reference pointer.
30-
/// </summary>
31-
public OpenApiReference Reference { get; set; }
3222

3323
/// <summary>
3424
/// This object MAY be extended with Specification Extensions.
3525
/// </summary>
36-
public virtual IDictionary<string, IOpenApiExtension> Extensions { get; set; } = new Dictionary<string, IOpenApiExtension>();
26+
public IDictionary<string, IOpenApiExtension> Extensions { get; set; } = new Dictionary<string, IOpenApiExtension>();
3727

3828
/// <summary>
3929
/// Parameter-less constructor
@@ -43,11 +33,9 @@ public OpenApiCallback() { }
4333
/// <summary>
4434
/// Initializes a copy of an <see cref="OpenApiCallback"/> object
4535
/// </summary>
46-
public OpenApiCallback(OpenApiCallback callback)
36+
public OpenApiCallback(IOpenApiCallback callback)
4737
{
4838
PathItems = callback?.PathItems != null ? new(callback?.PathItems) : null;
49-
UnresolvedReference = callback?.UnresolvedReference ?? UnresolvedReference;
50-
Reference = callback?.Reference != null ? new(callback?.Reference) : null;
5139
Extensions = callback?.Extensions != null ? new Dictionary<string, IOpenApiExtension>(callback.Extensions) : null;
5240
}
5341

@@ -71,15 +59,15 @@ public void AddPathItem(RuntimeExpression expression, OpenApiPathItem pathItem)
7159
/// </summary>
7260
/// <param name="writer"></param>
7361
/// <exception cref="System.NotImplementedException"></exception>
74-
public virtual void SerializeAsV31(IOpenApiWriter writer)
62+
public void SerializeAsV31(IOpenApiWriter writer)
7563
{
7664
SerializeInternal(writer, OpenApiSpecVersion.OpenApi3_1, (writer, element) => element.SerializeAsV31(writer));
7765
}
7866

7967
/// <summary>
8068
/// Serialize <see cref="OpenApiCallback"/> to Open Api v3.0
8169
/// </summary>
82-
public virtual void SerializeAsV3(IOpenApiWriter writer)
70+
public void SerializeAsV3(IOpenApiWriter writer)
8371
{
8472
SerializeInternal(writer, OpenApiSpecVersion.OpenApi3_0, (writer, element) => element.SerializeAsV3(writer));
8573
}

src/Microsoft.OpenApi/Models/OpenApiComponents.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ public class OpenApiComponents : IOpenApiSerializable, IOpenApiExtensible
6363
/// <summary>
6464
/// An object to hold reusable <see cref="OpenApiCallback"/> Objects.
6565
/// </summary>
66-
public virtual IDictionary<string, OpenApiCallback>? Callbacks { get; set; } = new Dictionary<string, OpenApiCallback>();
66+
public virtual IDictionary<string, IOpenApiCallback>? Callbacks { get; set; } = new Dictionary<string, IOpenApiCallback>();
6767

6868
/// <summary>
6969
/// An object to hold reusable <see cref="OpenApiPathItem"/> Object.
@@ -93,7 +93,7 @@ public OpenApiComponents(OpenApiComponents? components)
9393
Headers = components?.Headers != null ? new Dictionary<string, OpenApiHeader>(components.Headers) : null;
9494
SecuritySchemes = components?.SecuritySchemes != null ? new Dictionary<string, OpenApiSecurityScheme>(components.SecuritySchemes) : null;
9595
Links = components?.Links != null ? new Dictionary<string, OpenApiLink>(components.Links) : null;
96-
Callbacks = components?.Callbacks != null ? new Dictionary<string, OpenApiCallback>(components.Callbacks) : null;
96+
Callbacks = components?.Callbacks != null ? new Dictionary<string, IOpenApiCallback>(components.Callbacks) : null;
9797
PathItems = components?.PathItems != null ? new Dictionary<string, OpenApiPathItem>(components.PathItems) : null;
9898
Extensions = components?.Extensions != null ? new Dictionary<string, IOpenApiExtension>(components.Extensions) : null;
9999
}

src/Microsoft.OpenApi/Models/OpenApiDocument.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -608,7 +608,7 @@ public bool AddComponent<T>(string id, T componentToRegister)
608608
Components.Links.Add(id, openApiLink);
609609
break;
610610
case OpenApiCallback openApiCallback:
611-
Components.Callbacks ??= new Dictionary<string, OpenApiCallback>();
611+
Components.Callbacks ??= new Dictionary<string, IOpenApiCallback>();
612612
Components.Callbacks.Add(id, openApiCallback);
613613
break;
614614
case OpenApiPathItem openApiPathItem:

src/Microsoft.OpenApi/Models/OpenApiOperation.cs

+3-2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Collections.Generic;
66
using System.Linq;
77
using Microsoft.OpenApi.Interfaces;
8+
using Microsoft.OpenApi.Models.Interfaces;
89
using Microsoft.OpenApi.Models.References;
910
using Microsoft.OpenApi.Writers;
1011

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

8586
/// <summary>
8687
/// Declares this operation to be deprecated. Consumers SHOULD refrain from usage of the declared operation.
@@ -129,7 +130,7 @@ public OpenApiOperation(OpenApiOperation? operation)
129130
Parameters = operation?.Parameters != null ? new List<OpenApiParameter>(operation.Parameters) : null;
130131
RequestBody = operation?.RequestBody != null ? new(operation?.RequestBody) : null;
131132
Responses = operation?.Responses != null ? new(operation?.Responses) : null;
132-
Callbacks = operation?.Callbacks != null ? new Dictionary<string, OpenApiCallback>(operation.Callbacks) : null;
133+
Callbacks = operation?.Callbacks != null ? new Dictionary<string, IOpenApiCallback>(operation.Callbacks) : null;
133134
Deprecated = operation?.Deprecated ?? Deprecated;
134135
Security = operation?.Security != null ? new List<OpenApiSecurityRequirement>(operation.Security) : null;
135136
Servers = operation?.Servers != null ? new List<OpenApiServer>(operation.Servers) : null;

src/Microsoft.OpenApi/Models/References/OpenApiCallbackReference.cs

+43-16
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,23 @@
55
using System.Collections.Generic;
66
using Microsoft.OpenApi.Expressions;
77
using Microsoft.OpenApi.Interfaces;
8+
using Microsoft.OpenApi.Models.Interfaces;
89
using Microsoft.OpenApi.Writers;
910

1011
namespace Microsoft.OpenApi.Models.References
1112
{
1213
/// <summary>
1314
/// Callback Object Reference: A reference to a map of possible out-of band callbacks related to the parent operation.
1415
/// </summary>
15-
public class OpenApiCallbackReference : OpenApiCallback, IOpenApiReferenceHolder<OpenApiCallback>
16+
public class OpenApiCallbackReference : IOpenApiCallback, IOpenApiReferenceHolder<OpenApiCallback, IOpenApiCallback>
1617
{
1718
#nullable enable
1819
internal OpenApiCallback _target;
19-
private readonly OpenApiReference _reference;
20+
/// <inheritdoc/>
21+
public OpenApiReference Reference { get; set; }
22+
23+
/// <inheritdoc/>
24+
public bool UnresolvedReference { get; set; }
2025

2126
/// <summary>
2227
/// Gets the target callback.
@@ -29,7 +34,7 @@ public OpenApiCallback Target
2934
{
3035
get
3136
{
32-
_target ??= Reference.HostDocument.ResolveReferenceTo<OpenApiCallback>(_reference);
37+
_target ??= Reference.HostDocument.ResolveReferenceTo<OpenApiCallback>(Reference);
3338
return _target;
3439
}
3540
}
@@ -48,41 +53,49 @@ public OpenApiCallbackReference(string referenceId, OpenApiDocument hostDocument
4853
{
4954
Utils.CheckArgumentNullOrEmpty(referenceId);
5055

51-
_reference = new OpenApiReference()
56+
Reference = new OpenApiReference()
5257
{
5358
Id = referenceId,
5459
HostDocument = hostDocument,
5560
Type = ReferenceType.Callback,
5661
ExternalResource = externalResource
5762
};
63+
}
5864

59-
Reference = _reference;
65+
/// <summary>
66+
/// Copy constructor
67+
/// </summary>
68+
/// <param name="callback">The callback reference to copy</param>
69+
public OpenApiCallbackReference(OpenApiCallbackReference callback)
70+
{
71+
Utils.CheckArgumentNull(callback);
72+
Reference = callback?.Reference != null ? new(callback.Reference) : null;
73+
UnresolvedReference = callback?.UnresolvedReference ?? false;
6074
}
6175

6276
internal OpenApiCallbackReference(OpenApiCallback target, string referenceId)
6377
{
6478
_target = target;
6579

66-
_reference = new OpenApiReference()
80+
Reference = new OpenApiReference()
6781
{
6882
Id = referenceId,
6983
Type = ReferenceType.Callback,
7084
};
7185
}
7286

7387
/// <inheritdoc/>
74-
public override Dictionary<RuntimeExpression, OpenApiPathItem> PathItems { get => Target.PathItems; set => Target.PathItems = value; }
88+
public Dictionary<RuntimeExpression, OpenApiPathItem> PathItems { get => Target.PathItems; }
7589

7690
/// <inheritdoc/>
77-
public override IDictionary<string, IOpenApiExtension> Extensions { get => Target.Extensions; set => Target.Extensions = value; }
91+
public IDictionary<string, IOpenApiExtension> Extensions { get => Target.Extensions; }
7892

7993
/// <inheritdoc/>
80-
public override void SerializeAsV3(IOpenApiWriter writer)
94+
public void SerializeAsV3(IOpenApiWriter writer)
8195
{
82-
if (!writer.GetSettings().ShouldInlineReference(_reference))
96+
if (!writer.GetSettings().ShouldInlineReference(Reference))
8397
{
84-
_reference.SerializeAsV3(writer);
85-
return;
98+
Reference.SerializeAsV3(writer);
8699
}
87100
else
88101
{
@@ -91,19 +104,33 @@ public override void SerializeAsV3(IOpenApiWriter writer)
91104
}
92105

93106
/// <inheritdoc/>
94-
public override void SerializeAsV31(IOpenApiWriter writer)
107+
public void SerializeAsV31(IOpenApiWriter writer)
95108
{
96-
if (!writer.GetSettings().ShouldInlineReference(_reference))
109+
if (!writer.GetSettings().ShouldInlineReference(Reference))
97110
{
98-
_reference.SerializeAsV31(writer);
99-
return;
111+
Reference.SerializeAsV31(writer);
100112
}
101113
else
102114
{
103115
SerializeInternal(writer, (writer, element) => element.SerializeAsV31(writer));
104116
}
105117
}
106118

119+
/// <inheritdoc/>
120+
public IOpenApiCallback CopyReferenceAsTargetElementWithOverrides(IOpenApiCallback openApiExample)
121+
{
122+
// the copy here is never called since callbacks do not have any overridable fields.
123+
// if the spec evolves to include overridable fields for callbacks, the serialize methods will need to call this copy method.
124+
return openApiExample is OpenApiCallback ? new OpenApiCallback(this) : openApiExample;
125+
}
126+
127+
/// <inheritdoc/>
128+
public void SerializeAsV2(IOpenApiWriter writer)
129+
{
130+
// examples components are not supported in OAS 2.0
131+
Reference.SerializeAsV2(writer);
132+
}
133+
107134
/// <inheritdoc/>
108135
private void SerializeInternal(IOpenApiWriter writer,
109136
Action<IOpenApiWriter, IOpenApiReferenceable> action)

src/Microsoft.OpenApi/Models/References/OpenApiExampleReference.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public class OpenApiExampleReference : IOpenApiReferenceHolder<OpenApiExample, I
1919
public OpenApiReference Reference { get; set; }
2020

2121
/// <inheritdoc/>
22-
public bool UnresolvedReference { get; set; } = false;
22+
public bool UnresolvedReference { get; set; }
2323
internal OpenApiExample _target;
2424

2525
/// <summary>
@@ -68,7 +68,7 @@ public OpenApiExampleReference(OpenApiExampleReference example)
6868
{
6969
Utils.CheckArgumentNull(example);
7070
Reference = example?.Reference != null ? new(example.Reference) : null;
71-
UnresolvedReference = example?.UnresolvedReference ?? UnresolvedReference;
71+
UnresolvedReference = example?.UnresolvedReference ?? false;
7272
//no need to copy summary and description as if they are not overridden, they will be fetched from the target
7373
//if they are, the reference copy will handle it
7474
}

src/Microsoft.OpenApi/Reader/V3/OpenApiCallbackDeserializer.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using Microsoft.OpenApi.Expressions;
55
using Microsoft.OpenApi.Extensions;
66
using Microsoft.OpenApi.Models;
7+
using Microsoft.OpenApi.Models.Interfaces;
78
using Microsoft.OpenApi.Models.References;
89
using Microsoft.OpenApi.Reader.ParseNodes;
910

@@ -24,7 +25,7 @@ internal static partial class OpenApiV3Deserializer
2425
{s => s.StartsWith("x-"), (o, p, n, _) => o.AddExtension(p, LoadExtension(p,n))},
2526
};
2627

27-
public static OpenApiCallback LoadCallback(ParseNode node, OpenApiDocument hostDocument)
28+
public static IOpenApiCallback LoadCallback(ParseNode node, OpenApiDocument hostDocument)
2829
{
2930
var mapNode = node.CheckMapNode("callback");
3031

src/Microsoft.OpenApi/Reader/V31/OpenApiCallbackDeserializer.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using Microsoft.OpenApi.Models;
55
using Microsoft.OpenApi.Reader.ParseNodes;
66
using Microsoft.OpenApi.Models.References;
7+
using Microsoft.OpenApi.Models.Interfaces;
78

89
namespace Microsoft.OpenApi.Reader.V31
910
{
@@ -23,7 +24,7 @@ internal static partial class OpenApiV31Deserializer
2324
{s => s.StartsWith("x-", StringComparison.OrdinalIgnoreCase), (o, p, n, _) => o.AddExtension(p, LoadExtension(p,n))},
2425
};
2526

26-
public static OpenApiCallback LoadCallback(ParseNode node, OpenApiDocument hostDocument)
27+
public static IOpenApiCallback LoadCallback(ParseNode node, OpenApiDocument hostDocument)
2728
{
2829
var mapNode = node.CheckMapNode("callback");
2930

src/Microsoft.OpenApi/Services/CopyReferences.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -136,9 +136,9 @@ private void AddCallbackToComponents(OpenApiCallback callback, string referenceI
136136
{
137137
EnsureComponentsExist();
138138
EnsureCallbacksExist();
139-
if (!Components.Callbacks.ContainsKey(referenceId ?? callback.Reference.Id))
139+
if (!Components.Callbacks.ContainsKey(referenceId))
140140
{
141-
Components.Callbacks.Add(referenceId ?? callback.Reference.Id, callback);
141+
Components.Callbacks.Add(referenceId, callback);
142142
}
143143
}
144144
private void AddHeaderToComponents(OpenApiHeader header, string referenceId = null)
@@ -230,7 +230,7 @@ private void EnsureHeadersExist()
230230

231231
private void EnsureCallbacksExist()
232232
{
233-
_target.Components.Callbacks ??= new Dictionary<string, OpenApiCallback>();
233+
_target.Components.Callbacks ??= new Dictionary<string, IOpenApiCallback>();
234234
}
235235

236236
private void EnsureLinksExist()

0 commit comments

Comments
 (0)