Skip to content

Commit ee4d4c3

Browse files
Merge pull request #2095 from microsoft/chore/references-refactoring
chore: refactors common reference work to a base class to reduce duplication
2 parents ed6ffa1 + a72aa29 commit ee4d4c3

File tree

8 files changed

+181
-483
lines changed

8 files changed

+181
-483
lines changed

src/Microsoft.OpenApi/Models/Interfaces/IOpenApiParameter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
namespace Microsoft.OpenApi.Models.Interfaces;
66

77
/// <summary>
8-
/// Defines the base properties for the example object.
8+
/// Defines the base properties for the parameter object.
99
/// This interface is provided for type assertions but should not be implemented by package consumers beyond automatic mocking.
1010
/// </summary>
1111
public interface IOpenApiParameter : IOpenApiDescribedElement, IOpenApiSerializable, IOpenApiReadOnlyExtensible
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
using System;
2+
using Microsoft.OpenApi.Interfaces;
3+
using Microsoft.OpenApi.Writers;
4+
5+
namespace Microsoft.OpenApi.Models.References;
6+
/// <summary>
7+
/// Base class for OpenApiReferenceHolder.
8+
/// </summary>
9+
/// <typeparam name="T">The concrete class implementation type for the model.</typeparam>
10+
/// <typeparam name="V">The interface type for the model.</typeparam>
11+
public abstract class BaseOpenApiReferenceHolder<T, V> : IOpenApiReferenceHolder<T, V> where T : class, IOpenApiReferenceable, V where V : IOpenApiSerializable
12+
{
13+
internal T _target;
14+
/// <inheritdoc/>
15+
public T Target
16+
{
17+
get
18+
{
19+
_target ??= Reference.HostDocument.ResolveReferenceTo<T>(Reference);
20+
return _target;
21+
}
22+
}
23+
/// <summary>
24+
/// Copy constructor
25+
/// </summary>
26+
/// <param name="source">The parameter reference to copy</param>
27+
protected BaseOpenApiReferenceHolder(BaseOpenApiReferenceHolder<T, V> source)
28+
{
29+
Utils.CheckArgumentNull(source);
30+
Reference = source.Reference != null ? new(source.Reference) : null;
31+
UnresolvedReference = source.UnresolvedReference;
32+
//no need to copy summary and description as if they are not overridden, they will be fetched from the target
33+
//if they are, the reference copy will handle it
34+
}
35+
private protected BaseOpenApiReferenceHolder(T target, string referenceId, ReferenceType referenceType)
36+
{
37+
_target = target;
38+
39+
Reference = new OpenApiReference()
40+
{
41+
Id = referenceId,
42+
Type = referenceType,
43+
};
44+
}
45+
/// <summary>
46+
/// Constructor initializing the reference object.
47+
/// </summary>
48+
/// <param name="referenceId">The reference Id.</param>
49+
/// <param name="hostDocument">The host OpenAPI document.</param>
50+
/// <param name="referenceType">The reference type.</param>
51+
/// <param name="externalResource">Optional: External resource in the reference.
52+
/// It may be:
53+
/// 1. a absolute/relative file path, for example: ../commons/pet.json
54+
/// 2. a Url, for example: http://localhost/pet.json
55+
/// </param>
56+
protected BaseOpenApiReferenceHolder(string referenceId, OpenApiDocument hostDocument, ReferenceType referenceType, string externalResource = null)
57+
{
58+
Utils.CheckArgumentNullOrEmpty(referenceId);
59+
60+
Reference = new OpenApiReference()
61+
{
62+
Id = referenceId,
63+
HostDocument = hostDocument,
64+
Type = referenceType,
65+
ExternalResource = externalResource
66+
};
67+
}
68+
/// <inheritdoc/>
69+
public bool UnresolvedReference { get; set; }
70+
/// <inheritdoc/>
71+
public OpenApiReference Reference { get; set; }
72+
/// <inheritdoc/>
73+
public abstract V CopyReferenceAsTargetElementWithOverrides(V source);
74+
/// <inheritdoc/>
75+
public void SerializeAsV3(IOpenApiWriter writer)
76+
{
77+
if (!writer.GetSettings().ShouldInlineReference(Reference))
78+
{
79+
Reference.SerializeAsV3(writer);
80+
}
81+
else
82+
{
83+
SerializeInternal(writer, (writer, element) => CopyReferenceAsTargetElementWithOverrides(element).SerializeAsV3(writer));
84+
}
85+
}
86+
87+
/// <inheritdoc/>
88+
public void SerializeAsV31(IOpenApiWriter writer)
89+
{
90+
if (!writer.GetSettings().ShouldInlineReference(Reference))
91+
{
92+
Reference.SerializeAsV31(writer);
93+
}
94+
else
95+
{
96+
SerializeInternal(writer, (writer, element) => CopyReferenceAsTargetElementWithOverrides(element).SerializeAsV31(writer));
97+
}
98+
}
99+
100+
/// <inheritdoc/>
101+
public virtual void SerializeAsV2(IOpenApiWriter writer)
102+
{
103+
if (!writer.GetSettings().ShouldInlineReference(Reference))
104+
{
105+
Reference.SerializeAsV2(writer);
106+
}
107+
else
108+
{
109+
SerializeInternal(writer, (writer, element) => CopyReferenceAsTargetElementWithOverrides(element).SerializeAsV2(writer));
110+
}
111+
}
112+
113+
/// <summary>
114+
/// Serialize the reference as a reference or the target object.
115+
/// This method is used to accelerate the serialization methods implementations.
116+
/// </summary>
117+
/// <param name="writer">The OpenApiWriter.</param>
118+
/// <param name="action">The action to serialize the target object.</param>
119+
private protected void SerializeInternal(IOpenApiWriter writer,
120+
Action<IOpenApiWriter, V> action)
121+
{
122+
Utils.CheckArgumentNull(writer);
123+
action(writer, Target);
124+
}
125+
}

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

Lines changed: 6 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -13,32 +13,8 @@ namespace Microsoft.OpenApi.Models.References
1313
/// <summary>
1414
/// Callback Object Reference: A reference to a map of possible out-of band callbacks related to the parent operation.
1515
/// </summary>
16-
public class OpenApiCallbackReference : IOpenApiCallback, IOpenApiReferenceHolder<OpenApiCallback, IOpenApiCallback>
16+
public class OpenApiCallbackReference : BaseOpenApiReferenceHolder<OpenApiCallback, IOpenApiCallback>, IOpenApiCallback
1717
{
18-
#nullable enable
19-
internal OpenApiCallback _target;
20-
/// <inheritdoc/>
21-
public OpenApiReference Reference { get; set; }
22-
23-
/// <inheritdoc/>
24-
public bool UnresolvedReference { get; set; }
25-
26-
/// <summary>
27-
/// Gets the target callback.
28-
/// </summary>
29-
/// <remarks>
30-
/// If the reference is not resolved, this will return null.
31-
/// </remarks>
32-
public OpenApiCallback Target
33-
#nullable restore
34-
{
35-
get
36-
{
37-
_target ??= Reference.HostDocument.ResolveReferenceTo<OpenApiCallback>(Reference);
38-
return _target;
39-
}
40-
}
41-
4218
/// <summary>
4319
/// Constructor initializing the reference object.
4420
/// </summary>
@@ -49,39 +25,20 @@ public OpenApiCallback Target
4925
/// 1. an absolute/relative file path, for example: ../commons/pet.json
5026
/// 2. a Url, for example: http://localhost/pet.json
5127
/// </param>
52-
public OpenApiCallbackReference(string referenceId, OpenApiDocument hostDocument, string externalResource = null)
28+
public OpenApiCallbackReference(string referenceId, OpenApiDocument hostDocument, string externalResource = null):base(referenceId, hostDocument, ReferenceType.Callback, externalResource)
5329
{
54-
Utils.CheckArgumentNullOrEmpty(referenceId);
55-
56-
Reference = new OpenApiReference()
57-
{
58-
Id = referenceId,
59-
HostDocument = hostDocument,
60-
Type = ReferenceType.Callback,
61-
ExternalResource = externalResource
62-
};
6330
}
6431

6532
/// <summary>
6633
/// Copy constructor
6734
/// </summary>
6835
/// <param name="callback">The callback reference to copy</param>
69-
public OpenApiCallbackReference(OpenApiCallbackReference callback)
36+
public OpenApiCallbackReference(OpenApiCallbackReference callback):base(callback)
7037
{
71-
Utils.CheckArgumentNull(callback);
72-
Reference = callback.Reference != null ? new(callback.Reference) : null;
73-
UnresolvedReference = callback.UnresolvedReference;
7438
}
7539

76-
internal OpenApiCallbackReference(OpenApiCallback target, string referenceId)
40+
internal OpenApiCallbackReference(OpenApiCallback target, string referenceId):base(target, referenceId, ReferenceType.Callback)
7741
{
78-
_target = target;
79-
80-
Reference = new OpenApiReference()
81-
{
82-
Id = referenceId,
83-
Type = ReferenceType.Callback,
84-
};
8542
}
8643

8744
/// <inheritdoc/>
@@ -91,52 +48,16 @@ internal OpenApiCallbackReference(OpenApiCallback target, string referenceId)
9148
public IDictionary<string, IOpenApiExtension> Extensions { get => Target?.Extensions; }
9249

9350
/// <inheritdoc/>
94-
public void SerializeAsV3(IOpenApiWriter writer)
51+
public override IOpenApiCallback CopyReferenceAsTargetElementWithOverrides(IOpenApiCallback source)
9552
{
96-
if (!writer.GetSettings().ShouldInlineReference(Reference))
97-
{
98-
Reference.SerializeAsV3(writer);
99-
}
100-
else
101-
{
102-
SerializeInternal(writer, (writer, element) => element.SerializeAsV3(writer));
103-
}
104-
}
105-
106-
/// <inheritdoc/>
107-
public void SerializeAsV31(IOpenApiWriter writer)
108-
{
109-
if (!writer.GetSettings().ShouldInlineReference(Reference))
110-
{
111-
Reference.SerializeAsV31(writer);
112-
}
113-
else
114-
{
115-
SerializeInternal(writer, (writer, element) => element.SerializeAsV31(writer));
116-
}
117-
}
118-
119-
/// <inheritdoc/>
120-
public IOpenApiCallback CopyReferenceAsTargetElementWithOverrides(IOpenApiCallback source)
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.
12453
return source is OpenApiCallback ? new OpenApiCallback(this) : source;
12554
}
12655

12756
/// <inheritdoc/>
128-
public void SerializeAsV2(IOpenApiWriter writer)
57+
public override void SerializeAsV2(IOpenApiWriter writer)
12958
{
13059
// examples components are not supported in OAS 2.0
13160
Reference.SerializeAsV2(writer);
13261
}
133-
134-
/// <inheritdoc/>
135-
private void SerializeInternal(IOpenApiWriter writer,
136-
Action<IOpenApiWriter, IOpenApiReferenceable> action)
137-
{
138-
Utils.CheckArgumentNull(writer);
139-
action(writer, Target);
140-
}
14162
}
14263
}

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

Lines changed: 6 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -13,30 +13,8 @@ namespace Microsoft.OpenApi.Models.References
1313
/// <summary>
1414
/// Example Object Reference.
1515
/// </summary>
16-
public class OpenApiExampleReference : IOpenApiReferenceHolder<OpenApiExample, IOpenApiExample>, IOpenApiExample
16+
public class OpenApiExampleReference : BaseOpenApiReferenceHolder<OpenApiExample, IOpenApiExample>, IOpenApiExample
1717
{
18-
/// <inheritdoc/>
19-
public OpenApiReference Reference { get; set; }
20-
21-
/// <inheritdoc/>
22-
public bool UnresolvedReference { get; set; }
23-
internal OpenApiExample _target;
24-
25-
/// <summary>
26-
/// Gets the target example.
27-
/// </summary>
28-
/// <remarks>
29-
/// If the reference is not resolved, this will return null.
30-
/// </remarks>
31-
public OpenApiExample Target
32-
{
33-
get
34-
{
35-
_target ??= Reference.HostDocument.ResolveReferenceTo<OpenApiExample>(Reference);
36-
return _target;
37-
}
38-
}
39-
4018
/// <summary>
4119
/// Constructor initializing the reference object.
4220
/// </summary>
@@ -47,41 +25,20 @@ public OpenApiExample Target
4725
/// 1. a absolute/relative file path, for example: ../commons/pet.json
4826
/// 2. a Url, for example: http://localhost/pet.json
4927
/// </param>
50-
public OpenApiExampleReference(string referenceId, OpenApiDocument hostDocument, string externalResource = null)
28+
public OpenApiExampleReference(string referenceId, OpenApiDocument hostDocument, string externalResource = null):base(referenceId, hostDocument, ReferenceType.Example, externalResource)
5129
{
52-
Utils.CheckArgumentNullOrEmpty(referenceId);
53-
54-
Reference = new OpenApiReference()
55-
{
56-
Id = referenceId,
57-
HostDocument = hostDocument,
58-
Type = ReferenceType.Example,
59-
ExternalResource = externalResource
60-
};
6130
}
6231

6332
/// <summary>
6433
/// Copy constructor
6534
/// </summary>
6635
/// <param name="example">The reference to copy.</param>
67-
public OpenApiExampleReference(OpenApiExampleReference example)
36+
public OpenApiExampleReference(OpenApiExampleReference example):base(example)
6837
{
69-
Utils.CheckArgumentNull(example);
70-
Reference = example.Reference != null ? new(example.Reference) : null;
71-
UnresolvedReference = example.UnresolvedReference;
72-
//no need to copy summary and description as if they are not overridden, they will be fetched from the target
73-
//if they are, the reference copy will handle it
7438
}
7539

76-
internal OpenApiExampleReference(OpenApiExample target, string referenceId)
40+
internal OpenApiExampleReference(OpenApiExample target, string referenceId):base(target, referenceId, ReferenceType.Example)
7741
{
78-
_target = target;
79-
80-
Reference = new OpenApiReference()
81-
{
82-
Id = referenceId,
83-
Type = ReferenceType.Example,
84-
};
8542
}
8643

8744
/// <inheritdoc/>
@@ -120,50 +77,16 @@ public string Summary
12077
public JsonNode Value { get => Target?.Value; }
12178

12279
/// <inheritdoc/>
123-
public void SerializeAsV3(IOpenApiWriter writer)
124-
{
125-
if (!writer.GetSettings().ShouldInlineReference(Reference))
126-
{
127-
Reference.SerializeAsV3(writer);
128-
}
129-
else
130-
{
131-
SerializeInternal(writer, (writer, referenceElement) => CopyReferenceAsTargetElementWithOverrides(referenceElement).SerializeAsV3(writer));
132-
}
133-
}
134-
135-
/// <inheritdoc/>
136-
public void SerializeAsV31(IOpenApiWriter writer)
137-
{
138-
if (!writer.GetSettings().ShouldInlineReference(Reference))
139-
{
140-
Reference.SerializeAsV31(writer);
141-
}
142-
else
143-
{
144-
SerializeInternal(writer, (writer, referenceElement) => CopyReferenceAsTargetElementWithOverrides(referenceElement).SerializeAsV31(writer));
145-
}
146-
}
147-
148-
/// <inheritdoc/>
149-
public IOpenApiExample CopyReferenceAsTargetElementWithOverrides(IOpenApiExample source)
80+
public override IOpenApiExample CopyReferenceAsTargetElementWithOverrides(IOpenApiExample source)
15081
{
15182
return source is OpenApiExample ? new OpenApiExample(this) : source;
15283
}
15384

15485
/// <inheritdoc/>
155-
public void SerializeAsV2(IOpenApiWriter writer)
86+
public override void SerializeAsV2(IOpenApiWriter writer)
15687
{
15788
// examples components are not supported in OAS 2.0
15889
Reference.SerializeAsV2(writer);
15990
}
160-
161-
/// <inheritdoc/>
162-
private void SerializeInternal(IOpenApiWriter writer,
163-
Action<IOpenApiWriter, IOpenApiExample> action)
164-
{
165-
Utils.CheckArgumentNull(writer);
166-
action(writer, Target);
167-
}
16891
}
16992
}

0 commit comments

Comments
 (0)