Skip to content

Commit eeffba9

Browse files
committed
feat: enables references as components
Signed-off-by: Vincent Biret <[email protected]>
1 parent ca7ccdd commit eeffba9

23 files changed

+256
-64
lines changed

src/Microsoft.OpenApi/Interfaces/IOpenApiReferenceHolder.cs

+5-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,11 @@ public interface IOpenApiReferenceHolder<out T, V> : IOpenApiReferenceHolder whe
1515
/// <summary>
1616
/// Gets the resolved target object.
1717
/// </summary>
18-
T Target { get; }
18+
V Target { get; }
19+
/// <summary>
20+
/// Gets the recursively resolved target object.
21+
/// </summary>
22+
T RecursiveTarget { get; }
1923
/// <summary>
2024
/// Copy the reference as a target element with overrides.
2125
/// </summary>

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ namespace Microsoft.OpenApi.Models.Interfaces;
99
/// Defines the base properties for the callback object.
1010
/// This interface is provided for type assertions but should not be implemented by package consumers beyond automatic mocking.
1111
/// </summary>
12-
public interface IOpenApiCallback : IOpenApiSerializable, IOpenApiReadOnlyExtensible, IShallowCopyable<IOpenApiCallback>
12+
public interface IOpenApiCallback : IOpenApiReadOnlyExtensible, IShallowCopyable<IOpenApiCallback>, IOpenApiReferenceable
1313
{
1414
/// <summary>
1515
/// A Path Item Object used to define a callback request and expected responses.

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ namespace Microsoft.OpenApi.Models.Interfaces;
77
/// Defines the base properties for the example object.
88
/// This interface is provided for type assertions but should not be implemented by package consumers beyond automatic mocking.
99
/// </summary>
10-
public interface IOpenApiExample : IOpenApiDescribedElement, IOpenApiSummarizedElement, IOpenApiSerializable, IOpenApiReadOnlyExtensible, IShallowCopyable<IOpenApiExample>
10+
public interface IOpenApiExample : IOpenApiDescribedElement, IOpenApiSummarizedElement, IOpenApiReadOnlyExtensible, IShallowCopyable<IOpenApiExample>, IOpenApiReferenceable
1111
{
1212
/// <summary>
1313
/// Embedded literal example. The value field and externalValue field are mutually

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ namespace Microsoft.OpenApi.Models.Interfaces;
99
/// Defines the base properties for the headers object.
1010
/// This interface is provided for type assertions but should not be implemented by package consumers beyond automatic mocking.
1111
/// </summary>
12-
public interface IOpenApiHeader : IOpenApiDescribedElement, IOpenApiSerializable, IOpenApiReadOnlyExtensible, IShallowCopyable<IOpenApiHeader>
12+
public interface IOpenApiHeader : IOpenApiDescribedElement, IOpenApiReadOnlyExtensible, IShallowCopyable<IOpenApiHeader>, IOpenApiReferenceable
1313
{
1414
/// <summary>
1515
/// Determines whether this header is mandatory.

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ namespace Microsoft.OpenApi.Models.Interfaces;
77
/// Defines the base properties for the link object.
88
/// This interface is provided for type assertions but should not be implemented by package consumers beyond automatic mocking.
99
/// </summary>
10-
public interface IOpenApiLink : IOpenApiDescribedElement, IOpenApiSerializable, IOpenApiReadOnlyExtensible, IShallowCopyable<IOpenApiLink>
10+
public interface IOpenApiLink : IOpenApiDescribedElement, IOpenApiReadOnlyExtensible, IShallowCopyable<IOpenApiLink>, IOpenApiReferenceable
1111
{
1212
/// <summary>
1313
/// A relative or absolute reference to an OAS operation.

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ namespace Microsoft.OpenApi.Models.Interfaces;
88
/// 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>
11-
public interface IOpenApiParameter : IOpenApiDescribedElement, IOpenApiSerializable, IOpenApiReadOnlyExtensible, IShallowCopyable<IOpenApiParameter>
11+
public interface IOpenApiParameter : IOpenApiDescribedElement, IOpenApiReadOnlyExtensible, IShallowCopyable<IOpenApiParameter>, IOpenApiReferenceable
1212
{
1313
/// <summary>
1414
/// REQUIRED. The name of the parameter. Parameter names are case sensitive.

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ namespace Microsoft.OpenApi.Models.Interfaces;
88
/// Defines the base properties for the path item object.
99
/// This interface is provided for type assertions but should not be implemented by package consumers beyond automatic mocking.
1010
/// </summary>
11-
public interface IOpenApiPathItem : IOpenApiDescribedElement, IOpenApiSummarizedElement, IOpenApiSerializable, IOpenApiReadOnlyExtensible, IShallowCopyable<IOpenApiPathItem>
11+
public interface IOpenApiPathItem : IOpenApiDescribedElement, IOpenApiSummarizedElement, IOpenApiReadOnlyExtensible, IShallowCopyable<IOpenApiPathItem>, IOpenApiReferenceable
1212
{
1313
/// <summary>
1414
/// Gets the definition of operations on this path.

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ namespace Microsoft.OpenApi.Models.Interfaces;
88
/// Defines the base properties for the request body object.
99
/// This interface is provided for type assertions but should not be implemented by package consumers beyond automatic mocking.
1010
/// </summary>
11-
public interface IOpenApiRequestBody : IOpenApiDescribedElement, IOpenApiSerializable, IOpenApiReadOnlyExtensible, IShallowCopyable<IOpenApiRequestBody>
11+
public interface IOpenApiRequestBody : IOpenApiDescribedElement, IOpenApiReadOnlyExtensible, IShallowCopyable<IOpenApiRequestBody>, IOpenApiReferenceable
1212
{
1313
/// <summary>
1414
/// Determines if the request body is required in the request. Defaults to false.

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ namespace Microsoft.OpenApi.Models.Interfaces;
77
/// Defines the base properties for the response object.
88
/// This interface is provided for type assertions but should not be implemented by package consumers beyond automatic mocking.
99
/// </summary>
10-
public interface IOpenApiResponse : IOpenApiDescribedElement, IOpenApiSerializable, IOpenApiReadOnlyExtensible, IShallowCopyable<IOpenApiResponse>
10+
public interface IOpenApiResponse : IOpenApiDescribedElement, IOpenApiReadOnlyExtensible, IShallowCopyable<IOpenApiResponse>, IOpenApiReferenceable
1111
{
1212
/// <summary>
1313
/// Maps a header name to its definition.

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ namespace Microsoft.OpenApi.Models.Interfaces;
99
/// Defines the base properties for the schema object.
1010
/// This interface is provided for type assertions but should not be implemented by package consumers beyond automatic mocking.
1111
/// </summary>
12-
public interface IOpenApiSchema : IOpenApiDescribedElement, IOpenApiSerializable, IOpenApiReadOnlyExtensible, IShallowCopyable<IOpenApiSchema>
12+
public interface IOpenApiSchema : IOpenApiDescribedElement, IOpenApiReadOnlyExtensible, IShallowCopyable<IOpenApiSchema>, IOpenApiReferenceable
1313
{
1414

1515
/// <summary>

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ namespace Microsoft.OpenApi.Models.Interfaces;
88
/// Defines the base properties for the security scheme object.
99
/// This interface is provided for type assertions but should not be implemented by package consumers beyond automatic mocking.
1010
/// </summary>
11-
public interface IOpenApiSecurityScheme : IOpenApiDescribedElement, IOpenApiSerializable, IOpenApiReadOnlyExtensible, IShallowCopyable<IOpenApiSecurityScheme>
11+
public interface IOpenApiSecurityScheme : IOpenApiDescribedElement, IOpenApiReadOnlyExtensible, IShallowCopyable<IOpenApiSecurityScheme>, IOpenApiReferenceable
1212
{
1313
/// <summary>
1414
/// REQUIRED. The type of the security scheme. Valid values are "apiKey", "http", "oauth2", "openIdConnect".

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ namespace Microsoft.OpenApi.Models.Interfaces;
66
/// Defines the base properties for the path item object.
77
/// This interface is provided for type assertions but should not be implemented by package consumers beyond automatic mocking.
88
/// </summary>
9-
public interface IOpenApiTag : IOpenApiSerializable, IOpenApiReadOnlyExtensible, IOpenApiReadOnlyDescribedElement, IShallowCopyable<IOpenApiTag>
9+
public interface IOpenApiTag : IOpenApiReadOnlyExtensible, IOpenApiReadOnlyDescribedElement, IShallowCopyable<IOpenApiTag>, IOpenApiReferenceable
1010
{
1111
/// <summary>
1212
/// The name of the tag.

src/Microsoft.OpenApi/Models/OpenApiDocument.cs

+5-7
Original file line numberDiff line numberDiff line change
@@ -463,16 +463,14 @@ public void SetReferenceHostDocument()
463463
/// <summary>
464464
/// Load the referenced <see cref="IOpenApiReferenceable"/> object from a <see cref="OpenApiReference"/> object
465465
/// </summary>
466-
internal T? ResolveReferenceTo<T>(OpenApiReference reference) where T : class, IOpenApiReferenceable
466+
internal T? ResolveReferenceTo<T>(OpenApiReference reference) where T : IOpenApiReferenceable
467467
{
468-
if (reference.IsExternal)
469-
{
470-
return ResolveReference(reference, true) as T;
471-
}
472-
else
468+
469+
if (ResolveReference(reference, reference.IsExternal) is T result)
473470
{
474-
return ResolveReference(reference, false) as T;
471+
return result;
475472
}
473+
return default;
476474
}
477475

478476
/// <summary>

src/Microsoft.OpenApi/Models/OpenApiHeader.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ public void SerializeAsV2(IOpenApiWriter writer)
174174

175175
// schema
176176
var targetSchema = Schema switch {
177-
OpenApiSchemaReference schemaReference => schemaReference.Target,
177+
OpenApiSchemaReference schemaReference => schemaReference.RecursiveTarget,
178178
OpenApiSchema schema => schema,
179179
_ => null,
180180
};

src/Microsoft.OpenApi/Models/OpenApiParameter.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ public void SerializeAsV2(IOpenApiWriter writer)
232232
// enum
233233
// multipleOf
234234
var targetSchema = Schema switch {
235-
OpenApiSchemaReference schemaReference => schemaReference.Target,
235+
OpenApiSchemaReference schemaReference => schemaReference.RecursiveTarget,
236236
OpenApiSchema schema => schema,
237237
_ => null,
238238
};

src/Microsoft.OpenApi/Models/OpenApiReference.cs

+22
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22
// Licensed under the MIT license.
33

44
using System;
5+
using System.Text.Json.Nodes;
56
using Microsoft.OpenApi.Extensions;
67
using Microsoft.OpenApi.Interfaces;
78
using Microsoft.OpenApi.Models.Interfaces;
9+
using Microsoft.OpenApi.Reader.ParseNodes;
810
using Microsoft.OpenApi.Writers;
911

1012
namespace Microsoft.OpenApi.Models
@@ -292,5 +294,25 @@ internal void EnsureHostDocumentIsSet(OpenApiDocument currentDocument)
292294
Utils.CheckArgumentNull(currentDocument);
293295
hostDocument ??= currentDocument;
294296
}
297+
#nullable enable
298+
private static string? GetPropertyValueFromNode(JsonObject jsonObject, string key) =>
299+
jsonObject.TryGetPropertyValue(key, out var valueNode) && valueNode is JsonValue valueCast && valueCast.TryGetValue<string>(out var strValue) ? strValue : null;
300+
#nullable restore
301+
internal void SetSummaryAndDescriptionFromMapNode(MapNode mapNode)
302+
{
303+
var (description, summary) = mapNode.JsonNode switch {
304+
JsonObject jsonObject => (GetPropertyValueFromNode(jsonObject, OpenApiConstants.Description),
305+
GetPropertyValueFromNode(jsonObject, OpenApiConstants.Summary)),
306+
_ => (null, null)
307+
};
308+
if (!string.IsNullOrEmpty(description))
309+
{
310+
Description = description;
311+
}
312+
if (!string.IsNullOrEmpty(summary))
313+
{
314+
Summary = summary;
315+
}
316+
}
295317
}
296318
}

src/Microsoft.OpenApi/Models/OpenApiSchema.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ namespace Microsoft.OpenApi.Models
1818
/// <summary>
1919
/// The Schema Object allows the definition of input and output data types.
2020
/// </summary>
21-
public class OpenApiSchema : IOpenApiReferenceable, IOpenApiExtensible, IOpenApiSchema
21+
public class OpenApiSchema : IOpenApiExtensible, IOpenApiSchema
2222
{
2323
/// <inheritdoc />
2424
public string Title { get; set; }

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

+17-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System;
1+
using System;
22
using Microsoft.OpenApi.Interfaces;
33
using Microsoft.OpenApi.Writers;
44

@@ -8,14 +8,27 @@ namespace Microsoft.OpenApi.Models.References;
88
/// </summary>
99
/// <typeparam name="T">The concrete class implementation type for the model.</typeparam>
1010
/// <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
11+
public abstract class BaseOpenApiReferenceHolder<T, V> : IOpenApiReferenceHolder<T, V> where T : class, IOpenApiReferenceable, V where V : IOpenApiReferenceable, IOpenApiSerializable
1212
{
1313
/// <inheritdoc/>
14-
public virtual T Target
14+
public virtual V Target
1515
{
1616
get
1717
{
18-
return Reference.HostDocument?.ResolveReferenceTo<T>(Reference);
18+
if (Reference.HostDocument is null) return default;
19+
return Reference.HostDocument.ResolveReferenceTo<V>(Reference);
20+
}
21+
}
22+
/// <inheritdoc/>
23+
public T RecursiveTarget
24+
{
25+
get
26+
{
27+
return Target switch {
28+
BaseOpenApiReferenceHolder<T, V> recursiveTarget => recursiveTarget.RecursiveTarget,
29+
T concrete => concrete,
30+
_ => null
31+
};
1932
}
2033
}
2134
/// <summary>

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public class OpenApiTagReference : BaseOpenApiReferenceHolder<OpenApiTag, IOpenA
1717
/// <summary>
1818
/// Resolved target of the reference.
1919
/// </summary>
20-
public override OpenApiTag Target
20+
public override IOpenApiTag Target
2121
{
2222
get
2323
{

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

+3-1
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,9 @@ public static IOpenApiSchema LoadSchema(ParseNode node, OpenApiDocument hostDocu
257257
if (pointer != null)
258258
{
259259
var reference = GetReferenceIdAndExternalResource(pointer);
260-
return new OpenApiSchemaReference(reference.Item1, hostDocument, reference.Item2);
260+
var result = new OpenApiSchemaReference(reference.Item1, hostDocument, reference.Item2);
261+
result.Reference.SetSummaryAndDescriptionFromMapNode(mapNode);
262+
return result;
261263
}
262264

263265
var schema = new OpenApiSchema();

src/Microsoft.OpenApi/Services/CopyReferences.cs

+10-10
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ public override void Visit(IOpenApiReferenceHolder referenceHolder)
8585
base.Visit(referenceHolder);
8686
}
8787

88-
private void AddSchemaToComponents(OpenApiSchema schema, string referenceId = null)
88+
private void AddSchemaToComponents(IOpenApiSchema schema, string referenceId = null)
8989
{
9090
EnsureComponentsExist();
9191
EnsureSchemasExist();
@@ -95,7 +95,7 @@ private void AddSchemaToComponents(OpenApiSchema schema, string referenceId = nu
9595
}
9696
}
9797

98-
private void AddParameterToComponents(OpenApiParameter parameter, string referenceId = null)
98+
private void AddParameterToComponents(IOpenApiParameter parameter, string referenceId = null)
9999
{
100100
EnsureComponentsExist();
101101
EnsureParametersExist();
@@ -105,7 +105,7 @@ private void AddParameterToComponents(OpenApiParameter parameter, string referen
105105
}
106106
}
107107

108-
private void AddResponseToComponents(OpenApiResponse response, string referenceId = null)
108+
private void AddResponseToComponents(IOpenApiResponse response, string referenceId = null)
109109
{
110110
EnsureComponentsExist();
111111
EnsureResponsesExist();
@@ -114,7 +114,7 @@ private void AddResponseToComponents(OpenApiResponse response, string referenceI
114114
Components.Responses.Add(referenceId, response);
115115
}
116116
}
117-
private void AddRequestBodyToComponents(OpenApiRequestBody requestBody, string referenceId = null)
117+
private void AddRequestBodyToComponents(IOpenApiRequestBody requestBody, string referenceId = null)
118118
{
119119
EnsureComponentsExist();
120120
EnsureRequestBodiesExist();
@@ -123,7 +123,7 @@ private void AddRequestBodyToComponents(OpenApiRequestBody requestBody, string r
123123
Components.RequestBodies.Add(referenceId, requestBody);
124124
}
125125
}
126-
private void AddLinkToComponents(OpenApiLink link, string referenceId = null)
126+
private void AddLinkToComponents(IOpenApiLink link, string referenceId = null)
127127
{
128128
EnsureComponentsExist();
129129
EnsureLinksExist();
@@ -132,7 +132,7 @@ private void AddLinkToComponents(OpenApiLink link, string referenceId = null)
132132
Components.Links.Add(referenceId, link);
133133
}
134134
}
135-
private void AddCallbackToComponents(OpenApiCallback callback, string referenceId = null)
135+
private void AddCallbackToComponents(IOpenApiCallback callback, string referenceId = null)
136136
{
137137
EnsureComponentsExist();
138138
EnsureCallbacksExist();
@@ -141,7 +141,7 @@ private void AddCallbackToComponents(OpenApiCallback callback, string referenceI
141141
Components.Callbacks.Add(referenceId, callback);
142142
}
143143
}
144-
private void AddHeaderToComponents(OpenApiHeader header, string referenceId = null)
144+
private void AddHeaderToComponents(IOpenApiHeader header, string referenceId = null)
145145
{
146146
EnsureComponentsExist();
147147
EnsureHeadersExist();
@@ -150,7 +150,7 @@ private void AddHeaderToComponents(OpenApiHeader header, string referenceId = nu
150150
Components.Headers.Add(referenceId, header);
151151
}
152152
}
153-
private void AddExampleToComponents(OpenApiExample example, string referenceId = null)
153+
private void AddExampleToComponents(IOpenApiExample example, string referenceId = null)
154154
{
155155
EnsureComponentsExist();
156156
EnsureExamplesExist();
@@ -159,7 +159,7 @@ private void AddExampleToComponents(OpenApiExample example, string referenceId =
159159
Components.Examples.Add(referenceId, example);
160160
}
161161
}
162-
private void AddPathItemToComponents(OpenApiPathItem pathItem, string referenceId = null)
162+
private void AddPathItemToComponents(IOpenApiPathItem pathItem, string referenceId = null)
163163
{
164164
EnsureComponentsExist();
165165
EnsurePathItemsExist();
@@ -168,7 +168,7 @@ private void AddPathItemToComponents(OpenApiPathItem pathItem, string referenceI
168168
Components.PathItems.Add(referenceId, pathItem);
169169
}
170170
}
171-
private void AddSecuritySchemeToComponents(OpenApiSecurityScheme securityScheme, string referenceId = null)
171+
private void AddSecuritySchemeToComponents(IOpenApiSecurityScheme securityScheme, string referenceId = null)
172172
{
173173
EnsureComponentsExist();
174174
EnsureSecuritySchemesExist();

0 commit comments

Comments
 (0)