Skip to content

Commit 73ec5a7

Browse files
Copilotbaywet
andcommitted
Add metadata annotations to OpenApiSchemaReference
- Add Default, Title, Deprecated, ReadOnly, WriteOnly, Examples to OpenApiReference - Update OpenApiSchemaReference properties to use override pattern like Description - Add Summary property to OpenApiSchemaReference - Update serialization for v3.1 to include new annotation fields - Update deserialization to handle new annotation fields - Add comprehensive unit tests for new functionality - Update public API approvals Co-authored-by: baywet <[email protected]>
1 parent 7967256 commit 73ec5a7

File tree

5 files changed

+345
-19
lines changed

5 files changed

+345
-19
lines changed

src/Microsoft.OpenApi/Models/OpenApiReference.cs

Lines changed: 110 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the MIT license.
33

44
using System;
5+
using System.Collections.Generic;
56
using System.Linq;
67
using System.Text.Json.Nodes;
78
using Microsoft.OpenApi.Reader;
@@ -26,6 +27,42 @@ public class OpenApiReference : IOpenApiSerializable, IOpenApiDescribedElement,
2627
/// </summary>
2728
public string? Description { get; set; }
2829

30+
/// <summary>
31+
/// A default value which by default SHOULD override that of the referenced component.
32+
/// If the referenced object-type does not allow a default field, then this field has no effect.
33+
/// </summary>
34+
public JsonNode? Default { get; set; }
35+
36+
/// <summary>
37+
/// A title which by default SHOULD override that of the referenced component.
38+
/// If the referenced object-type does not allow a title field, then this field has no effect.
39+
/// </summary>
40+
public string? Title { get; set; }
41+
42+
/// <summary>
43+
/// Indicates whether the referenced component is deprecated.
44+
/// If the referenced object-type does not allow a deprecated field, then this field has no effect.
45+
/// </summary>
46+
public bool? Deprecated { get; set; }
47+
48+
/// <summary>
49+
/// Indicates whether the referenced component is read-only.
50+
/// If the referenced object-type does not allow a readOnly field, then this field has no effect.
51+
/// </summary>
52+
public bool? ReadOnly { get; set; }
53+
54+
/// <summary>
55+
/// Indicates whether the referenced component is write-only.
56+
/// If the referenced object-type does not allow a writeOnly field, then this field has no effect.
57+
/// </summary>
58+
public bool? WriteOnly { get; set; }
59+
60+
/// <summary>
61+
/// Example values which by default SHOULD override those of the referenced component.
62+
/// If the referenced object-type does not allow examples, then this field has no effect.
63+
/// </summary>
64+
public IList<JsonNode>? Examples { get; set; }
65+
2966
/// <summary>
3067
/// External resource in the reference.
3168
/// It maybe:
@@ -153,6 +190,12 @@ public OpenApiReference(OpenApiReference reference)
153190
Utils.CheckArgumentNull(reference);
154191
Summary = reference.Summary;
155192
Description = reference.Description;
193+
Default = reference.Default;
194+
Title = reference.Title;
195+
Deprecated = reference.Deprecated;
196+
ReadOnly = reference.ReadOnly;
197+
WriteOnly = reference.WriteOnly;
198+
Examples = reference.Examples;
156199
ExternalResource = reference.ExternalResource;
157200
Type = reference.Type;
158201
Id = reference.Id;
@@ -169,6 +212,17 @@ public void SerializeAsV31(IOpenApiWriter writer)
169212
// summary and description are in 3.1 but not in 3.0
170213
w.WriteProperty(OpenApiConstants.Summary, Summary);
171214
w.WriteProperty(OpenApiConstants.Description, Description);
215+
216+
// Additional schema metadata annotations in 3.1
217+
w.WriteOptionalObject(OpenApiConstants.Default, Default, (w, d) => w.WriteAny(d));
218+
w.WriteProperty(OpenApiConstants.Title, Title);
219+
w.WriteProperty(OpenApiConstants.Deprecated, Deprecated, false);
220+
w.WriteProperty(OpenApiConstants.ReadOnly, ReadOnly, false);
221+
w.WriteProperty(OpenApiConstants.WriteOnly, WriteOnly, false);
222+
if (Examples != null && Examples.Any())
223+
{
224+
w.WriteOptionalCollection(OpenApiConstants.Examples, Examples, (w, e) => w.WriteAny(e));
225+
}
172226
});
173227
}
174228

@@ -293,13 +347,15 @@ internal void EnsureHostDocumentIsSet(OpenApiDocument currentDocument)
293347
}
294348
private static string? GetPropertyValueFromNode(JsonObject jsonObject, string key) =>
295349
jsonObject.TryGetPropertyValue(key, out var valueNode) && valueNode is JsonValue valueCast && valueCast.TryGetValue<string>(out var strValue) ? strValue : null;
296-
internal void SetSummaryAndDescriptionFromMapNode(MapNode mapNode)
350+
internal void SetMetadataFromMapNode(MapNode mapNode)
297351
{
298-
var (description, summary) = mapNode.JsonNode switch {
299-
JsonObject jsonObject => (GetPropertyValueFromNode(jsonObject, OpenApiConstants.Description),
300-
GetPropertyValueFromNode(jsonObject, OpenApiConstants.Summary)),
301-
_ => (null, null)
302-
};
352+
if (mapNode.JsonNode is not JsonObject jsonObject) return;
353+
354+
// Summary and Description
355+
var description = GetPropertyValueFromNode(jsonObject, OpenApiConstants.Description);
356+
var summary = GetPropertyValueFromNode(jsonObject, OpenApiConstants.Summary);
357+
var title = GetPropertyValueFromNode(jsonObject, OpenApiConstants.Title);
358+
303359
if (!string.IsNullOrEmpty(description))
304360
{
305361
Description = description;
@@ -308,6 +364,54 @@ internal void SetSummaryAndDescriptionFromMapNode(MapNode mapNode)
308364
{
309365
Summary = summary;
310366
}
367+
if (!string.IsNullOrEmpty(title))
368+
{
369+
Title = title;
370+
}
371+
372+
// Boolean properties
373+
if (jsonObject.TryGetPropertyValue(OpenApiConstants.Deprecated, out var deprecatedNode) && deprecatedNode is JsonValue deprecatedValue)
374+
{
375+
if (deprecatedValue.TryGetValue<bool>(out var deprecated))
376+
{
377+
Deprecated = deprecated;
378+
}
379+
}
380+
381+
if (jsonObject.TryGetPropertyValue(OpenApiConstants.ReadOnly, out var readOnlyNode) && readOnlyNode is JsonValue readOnlyValue)
382+
{
383+
if (readOnlyValue.TryGetValue<bool>(out var readOnly))
384+
{
385+
ReadOnly = readOnly;
386+
}
387+
}
388+
389+
if (jsonObject.TryGetPropertyValue(OpenApiConstants.WriteOnly, out var writeOnlyNode) && writeOnlyNode is JsonValue writeOnlyValue)
390+
{
391+
if (writeOnlyValue.TryGetValue<bool>(out var writeOnly))
392+
{
393+
WriteOnly = writeOnly;
394+
}
395+
}
396+
397+
// Default value
398+
if (jsonObject.TryGetPropertyValue(OpenApiConstants.Default, out var defaultNode))
399+
{
400+
Default = defaultNode;
401+
}
402+
403+
// Examples
404+
if (jsonObject.TryGetPropertyValue(OpenApiConstants.Examples, out var examplesNode) && examplesNode is JsonArray examplesArray)
405+
{
406+
Examples = new List<JsonNode>();
407+
foreach (var example in examplesArray)
408+
{
409+
if (example != null)
410+
{
411+
Examples.Add(example);
412+
}
413+
}
414+
}
311415
}
312416

313417
internal void SetJsonPointerPath(string pointer, string nodeLocation)

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

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,21 @@ public string? Description
4040
set => Reference.Description = value;
4141
}
4242

43+
/// <summary>
44+
/// A short summary which by default SHOULD override that of the referenced component.
45+
/// </summary>
46+
public string? Summary
47+
{
48+
get => string.IsNullOrEmpty(Reference.Summary) ? null : Reference.Summary;
49+
set => Reference.Summary = value;
50+
}
51+
4352
/// <inheritdoc/>
44-
public string? Title { get => Target?.Title; }
53+
public string? Title
54+
{
55+
get => string.IsNullOrEmpty(Reference.Title) ? Target?.Title : Reference.Title;
56+
set => Reference.Title = value;
57+
}
4558
/// <inheritdoc/>
4659
public Uri? Schema { get => Target?.Schema; }
4760
/// <inheritdoc/>
@@ -79,11 +92,23 @@ public string? Description
7992
/// <inheritdoc/>
8093
public decimal? MultipleOf { get => Target?.MultipleOf; }
8194
/// <inheritdoc/>
82-
public JsonNode? Default { get => Target?.Default; }
95+
public JsonNode? Default
96+
{
97+
get => Reference.Default ?? Target?.Default;
98+
set => Reference.Default = value;
99+
}
83100
/// <inheritdoc/>
84-
public bool ReadOnly { get => Target?.ReadOnly ?? false; }
101+
public bool ReadOnly
102+
{
103+
get => Reference.ReadOnly ?? Target?.ReadOnly ?? false;
104+
set => Reference.ReadOnly = value;
105+
}
85106
/// <inheritdoc/>
86-
public bool WriteOnly { get => Target?.WriteOnly ?? false; }
107+
public bool WriteOnly
108+
{
109+
get => Reference.WriteOnly ?? Target?.WriteOnly ?? false;
110+
set => Reference.WriteOnly = value;
111+
}
87112
/// <inheritdoc/>
88113
public IList<IOpenApiSchema>? AllOf { get => Target?.AllOf; }
89114
/// <inheritdoc/>
@@ -119,15 +144,23 @@ public string? Description
119144
/// <inheritdoc/>
120145
public JsonNode? Example { get => Target?.Example; }
121146
/// <inheritdoc/>
122-
public IList<JsonNode>? Examples { get => Target?.Examples; }
147+
public IList<JsonNode>? Examples
148+
{
149+
get => Reference.Examples ?? Target?.Examples;
150+
set => Reference.Examples = value;
151+
}
123152
/// <inheritdoc/>
124153
public IList<JsonNode>? Enum { get => Target?.Enum; }
125154
/// <inheritdoc/>
126155
public bool UnevaluatedProperties { get => Target?.UnevaluatedProperties ?? false; }
127156
/// <inheritdoc/>
128157
public OpenApiExternalDocs? ExternalDocs { get => Target?.ExternalDocs; }
129158
/// <inheritdoc/>
130-
public bool Deprecated { get => Target?.Deprecated ?? false; }
159+
public bool Deprecated
160+
{
161+
get => Reference.Deprecated ?? Target?.Deprecated ?? false;
162+
set => Reference.Deprecated = value;
163+
}
131164
/// <inheritdoc/>
132165
public OpenApiXml? Xml { get => Target?.Xml; }
133166
/// <inheritdoc/>

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,7 @@ public static IOpenApiSchema LoadSchema(ParseNode node, OpenApiDocument hostDocu
368368
{
369369
var reference = GetReferenceIdAndExternalResource(pointer);
370370
var result = new OpenApiSchemaReference(reference.Item1, hostDocument, reference.Item2);
371-
result.Reference.SetSummaryAndDescriptionFromMapNode(mapNode);
371+
result.Reference.SetMetadataFromMapNode(mapNode);
372372
result.Reference.SetJsonPointerPath(pointer, nodeLocation);
373373
return result;
374374
}

0 commit comments

Comments
 (0)