Skip to content

Commit 7876018

Browse files
Merge pull request #1769 from captainsafia/property-bag-support
Add support for unserializable annotations on OpenAPI document
2 parents d96749f + 2df890a commit 7876018

File tree

8 files changed

+124
-11
lines changed

8 files changed

+124
-11
lines changed
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT license.
3+
4+
using System.Collections.Generic;
5+
6+
namespace Microsoft.OpenApi.Interfaces
7+
{
8+
/// <summary>
9+
/// Represents an Open API element that can be annotated with
10+
/// non-serializable properties in a property bag.
11+
/// </summary>
12+
public interface IOpenApiAnnotatable
13+
{
14+
/// <summary>
15+
/// A collection of properties associated with the current OpenAPI element.
16+
/// </summary>
17+
IDictionary<string, object> Annotations { get; set; }
18+
}
19+
}

src/Microsoft.OpenApi/Models/OpenApiDocument.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ namespace Microsoft.OpenApi.Models
1717
/// <summary>
1818
/// Describes an OpenAPI object (OpenAPI document). See: https://swagger.io/specification
1919
/// </summary>
20-
public class OpenApiDocument : IOpenApiSerializable, IOpenApiExtensible
20+
public class OpenApiDocument : IOpenApiSerializable, IOpenApiExtensible, IOpenApiAnnotatable
2121
{
2222
/// <summary>
2323
/// Related workspace containing OpenApiDocuments that are referenced in this document
@@ -70,6 +70,9 @@ public class OpenApiDocument : IOpenApiSerializable, IOpenApiExtensible
7070
/// </summary>
7171
public string HashCode => GenerateHashValue(this);
7272

73+
/// <inheritdoc />
74+
public IDictionary<string, object> Annotations { get; set; }
75+
7376
/// <summary>
7477
/// Parameter-less constructor
7578
/// </summary>
@@ -89,6 +92,7 @@ public OpenApiDocument(OpenApiDocument document)
8992
Tags = document?.Tags != null ? new List<OpenApiTag>(document.Tags) : null;
9093
ExternalDocs = document?.ExternalDocs != null ? new(document?.ExternalDocs) : null;
9194
Extensions = document?.Extensions != null ? new Dictionary<string, IOpenApiExtension>(document.Extensions) : null;
95+
Annotations = document?.Annotations != null ? new Dictionary<string, object>(document.Annotations) : null;
9296
}
9397

9498
/// <summary>

src/Microsoft.OpenApi/Models/OpenApiOperation.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ namespace Microsoft.OpenApi.Models
1212
/// <summary>
1313
/// Operation Object.
1414
/// </summary>
15-
public class OpenApiOperation : IOpenApiSerializable, IOpenApiExtensible
15+
public class OpenApiOperation : IOpenApiSerializable, IOpenApiExtensible, IOpenApiAnnotatable
1616
{
1717
/// <summary>
1818
/// Default value for <see cref="Deprecated"/>.
@@ -105,6 +105,9 @@ public class OpenApiOperation : IOpenApiSerializable, IOpenApiExtensible
105105
/// </summary>
106106
public IDictionary<string, IOpenApiExtension> Extensions { get; set; } = new Dictionary<string, IOpenApiExtension>();
107107

108+
/// <inheritdoc />
109+
public IDictionary<string, object> Annotations { get; set; }
110+
108111
/// <summary>
109112
/// Parameterless constructor
110113
/// </summary>
@@ -128,6 +131,7 @@ public OpenApiOperation(OpenApiOperation operation)
128131
Security = operation?.Security != null ? new List<OpenApiSecurityRequirement>(operation.Security) : null;
129132
Servers = operation?.Servers != null ? new List<OpenApiServer>(operation.Servers) : null;
130133
Extensions = operation?.Extensions != null ? new Dictionary<string, IOpenApiExtension>(operation.Extensions) : null;
134+
Annotations = operation?.Annotations != null ? new Dictionary<string, object>(operation.Annotations) : null;
131135
}
132136

133137
/// <summary>

src/Microsoft.OpenApi/Models/OpenApiSchema.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ namespace Microsoft.OpenApi.Models
1212
/// <summary>
1313
/// Schema Object.
1414
/// </summary>
15-
public class OpenApiSchema : IOpenApiReferenceable, IEffective<OpenApiSchema>, IOpenApiExtensible
15+
public class OpenApiSchema : IOpenApiReferenceable, IEffective<OpenApiSchema>, IOpenApiExtensible, IOpenApiAnnotatable
1616
{
1717
/// <summary>
1818
/// Follow JSON Schema definition. Short text providing information about the data.
@@ -241,6 +241,9 @@ public class OpenApiSchema : IOpenApiReferenceable, IEffective<OpenApiSchema>, I
241241
/// </summary>
242242
public OpenApiReference Reference { get; set; }
243243

244+
/// <inheritdoc />
245+
public IDictionary<string, object> Annotations { get; set; }
246+
244247
/// <summary>
245248
/// Parameterless constructor
246249
/// </summary>
@@ -290,6 +293,7 @@ public OpenApiSchema(OpenApiSchema schema)
290293
Extensions = schema?.Extensions != null ? new Dictionary<string, IOpenApiExtension>(schema.Extensions) : null;
291294
UnresolvedReference = schema?.UnresolvedReference ?? UnresolvedReference;
292295
Reference = schema?.Reference != null ? new(schema?.Reference) : null;
296+
Annotations = schema?.Annotations != null ? new Dictionary<string, object>(schema?.Annotations) : null;
293297
}
294298

295299
/// <summary>

test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.cs

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ public class OpenApiDocumentTests
2929
{
3030
Type = ReferenceType.Schema,
3131
Id = "schema2"
32-
}
32+
},
33+
Annotations = new Dictionary<string, object> { { "x-foo", "bar" } }
3334
},
3435
["schema2"] = new()
3536
{
@@ -38,7 +39,8 @@ public class OpenApiDocumentTests
3839
{
3940
["property1"] = new()
4041
{
41-
Type = "string"
42+
Type = "string",
43+
Annotations = new Dictionary<string, object> { { "key1", "value" } }
4244
}
4345
}
4446
},
@@ -56,9 +58,11 @@ public class OpenApiDocumentTests
5658
{
5759
["property1"] = new()
5860
{
59-
Type = "string"
61+
Type = "string",
62+
Annotations = new Dictionary<string, object> { { "key1", "value" } }
6063
}
6164
},
65+
Annotations = new Dictionary<string, object> { { "key1", "value" } },
6266
Reference = new()
6367
{
6468
Type = ReferenceType.Schema,
@@ -100,6 +104,7 @@ public class OpenApiDocumentTests
100104
{
101105
Version = "1.0.0"
102106
},
107+
Annotations = new Dictionary<string, object> { { "key1", "value" } },
103108
Components = TopLevelReferencingComponents
104109
};
105110

@@ -109,6 +114,7 @@ public class OpenApiDocumentTests
109114
{
110115
Version = "1.0.0"
111116
},
117+
Annotations = new Dictionary<string, object> { { "key1", "value" } },
112118
Components = TopLevelSelfReferencingComponentsWithOtherProperties
113119
};
114120

@@ -118,6 +124,7 @@ public class OpenApiDocumentTests
118124
{
119125
Version = "1.0.0"
120126
},
127+
Annotations = new Dictionary<string, object> { { "key1", "value" } },
121128
Components = TopLevelSelfReferencingComponents
122129
};
123130

@@ -509,6 +516,7 @@ public class OpenApiDocumentTests
509516
}
510517
}
511518
},
519+
Annotations = new Dictionary<string, object> { { "key1", "value" } },
512520
Components = AdvancedComponentsWithReference
513521
};
514522

@@ -884,6 +892,7 @@ public class OpenApiDocumentTests
884892
}
885893
}
886894
},
895+
Annotations = new Dictionary<string, object> { { "key1", "value" } },
887896
Components = AdvancedComponents
888897
};
889898

@@ -1272,6 +1281,7 @@ public class OpenApiDocumentTests
12721281
}
12731282
}
12741283
},
1284+
Annotations = new Dictionary<string, object> { { "key1", "value" } },
12751285
Components = AdvancedComponents
12761286
};
12771287

@@ -1827,5 +1837,26 @@ public void SerializeV2DocumentWithStyleAsNullDoesNotWriteOutStyleValue()
18271837
expected = expected.MakeLineBreaksEnvironmentNeutral();
18281838
actual.Should().Be(expected);
18291839
}
1840+
1841+
[Fact]
1842+
public void OpenApiDocumentCopyConstructorWithAnnotationsSucceeds()
1843+
{
1844+
var baseDocument = new OpenApiDocument
1845+
{
1846+
Annotations = new Dictionary<string, object>
1847+
{
1848+
["key1"] = "value1",
1849+
["key2"] = 2
1850+
}
1851+
};
1852+
1853+
var actualDocument = new OpenApiDocument(baseDocument);
1854+
1855+
Assert.Equal(baseDocument.Annotations["key1"], actualDocument.Annotations["key1"]);
1856+
1857+
baseDocument.Annotations["key1"] = "value2";
1858+
1859+
Assert.NotEqual(baseDocument.Annotations["key1"], actualDocument.Annotations["key1"]);
1860+
}
18301861
}
18311862
}

test/Microsoft.OpenApi.Tests/Models/OpenApiOperationTests.cs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,8 @@ public class OpenApiOperationTests
8787
Url = "http://server.com",
8888
Description = "serverDescription"
8989
}
90-
}
90+
},
91+
Annotations = new Dictionary<string, object> { { "key1", "value1" }, { "key2", 2 } },
9192
};
9293

9394
private static readonly OpenApiOperation _advancedOperationWithTagsAndSecurity = new()
@@ -864,5 +865,26 @@ public void EnsureOpenApiOperationCopyConstructor_SerializationResultsInSame()
864865
actual.Should().Be(expected);
865866
}
866867
}
868+
869+
[Fact]
870+
public void OpenApiOperationCopyConstructorWithAnnotationsSucceeds()
871+
{
872+
var baseOperation = new OpenApiOperation
873+
{
874+
Annotations = new Dictionary<string, object>
875+
{
876+
["key1"] = "value1",
877+
["key2"] = 2
878+
}
879+
};
880+
881+
var actualOperation = new OpenApiOperation(baseOperation);
882+
883+
Assert.Equal(baseOperation.Annotations["key1"], actualOperation.Annotations["key1"]);
884+
885+
baseOperation.Annotations["key1"] = "value2";
886+
887+
Assert.NotEqual(baseOperation.Annotations["key1"], actualOperation.Annotations["key1"]);
888+
}
867889
}
868890
}

test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.cs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ public class OpenApiSchemaTests
3636
ExternalDocs = new()
3737
{
3838
Url = new("http://example.com/externalDocs")
39-
}
39+
},
40+
Annotations = new Dictionary<string, object> { { "key1", "value1" }, { "key2", 2 } }
4041
};
4142

4243
public static readonly OpenApiSchema AdvancedSchemaObject = new()
@@ -483,6 +484,27 @@ public void OpenApiSchemaCopyConstructorSucceeds()
483484
Assert.True(actualSchema.Nullable);
484485
}
485486

487+
[Fact]
488+
public void OpenApiSchemaCopyConstructorWithAnnotationsSucceeds()
489+
{
490+
var baseSchema = new OpenApiSchema
491+
{
492+
Annotations = new Dictionary<string, object>
493+
{
494+
["key1"] = "value1",
495+
["key2"] = 2
496+
}
497+
};
498+
499+
var actualSchema = new OpenApiSchema(baseSchema);
500+
501+
Assert.Equal(baseSchema.Annotations["key1"], actualSchema.Annotations["key1"]);
502+
503+
baseSchema.Annotations["key1"] = "value2";
504+
505+
Assert.NotEqual(baseSchema.Annotations["key1"], actualSchema.Annotations["key1"]);
506+
}
507+
486508
public static TheoryData<IOpenApiAny> SchemaExamples()
487509
{
488510
return new()

test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,10 @@ namespace Microsoft.OpenApi.Interfaces
302302
{
303303
T GetEffective(Microsoft.OpenApi.Models.OpenApiDocument document);
304304
}
305+
public interface IOpenApiAnnotatable
306+
{
307+
System.Collections.Generic.IDictionary<string, object> Annotations { get; set; }
308+
}
305309
public interface IOpenApiElement { }
306310
public interface IOpenApiExtensible : Microsoft.OpenApi.Interfaces.IOpenApiElement
307311
{
@@ -592,10 +596,11 @@ namespace Microsoft.OpenApi.Models
592596
public void SerializeAsV2(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { }
593597
public void SerializeAsV3(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { }
594598
}
595-
public class OpenApiDocument : Microsoft.OpenApi.Interfaces.IOpenApiElement, Microsoft.OpenApi.Interfaces.IOpenApiExtensible, Microsoft.OpenApi.Interfaces.IOpenApiSerializable
599+
public class OpenApiDocument : Microsoft.OpenApi.Interfaces.IOpenApiAnnotatable, Microsoft.OpenApi.Interfaces.IOpenApiElement, Microsoft.OpenApi.Interfaces.IOpenApiExtensible, Microsoft.OpenApi.Interfaces.IOpenApiSerializable
596600
{
597601
public OpenApiDocument() { }
598602
public OpenApiDocument(Microsoft.OpenApi.Models.OpenApiDocument document) { }
603+
public System.Collections.Generic.IDictionary<string, object> Annotations { get; set; }
599604
public Microsoft.OpenApi.Models.OpenApiComponents Components { get; set; }
600605
public System.Collections.Generic.IDictionary<string, Microsoft.OpenApi.Interfaces.IOpenApiExtension> Extensions { get; set; }
601606
public Microsoft.OpenApi.Models.OpenApiExternalDocs ExternalDocs { get; set; }
@@ -774,11 +779,12 @@ namespace Microsoft.OpenApi.Models
774779
public void SerializeAsV2(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { }
775780
public void SerializeAsV3(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { }
776781
}
777-
public class OpenApiOperation : Microsoft.OpenApi.Interfaces.IOpenApiElement, Microsoft.OpenApi.Interfaces.IOpenApiExtensible, Microsoft.OpenApi.Interfaces.IOpenApiSerializable
782+
public class OpenApiOperation : Microsoft.OpenApi.Interfaces.IOpenApiAnnotatable, Microsoft.OpenApi.Interfaces.IOpenApiElement, Microsoft.OpenApi.Interfaces.IOpenApiExtensible, Microsoft.OpenApi.Interfaces.IOpenApiSerializable
778783
{
779784
public const bool DeprecatedDefault = false;
780785
public OpenApiOperation() { }
781786
public OpenApiOperation(Microsoft.OpenApi.Models.OpenApiOperation operation) { }
787+
public System.Collections.Generic.IDictionary<string, object> Annotations { get; set; }
782788
public System.Collections.Generic.IDictionary<string, Microsoft.OpenApi.Models.OpenApiCallback> Callbacks { get; set; }
783789
public bool Deprecated { get; set; }
784790
public string Description { get; set; }
@@ -900,13 +906,14 @@ namespace Microsoft.OpenApi.Models
900906
public OpenApiResponses() { }
901907
public OpenApiResponses(Microsoft.OpenApi.Models.OpenApiResponses openApiResponses) { }
902908
}
903-
public class OpenApiSchema : Microsoft.OpenApi.Interfaces.IEffective<Microsoft.OpenApi.Models.OpenApiSchema>, Microsoft.OpenApi.Interfaces.IOpenApiElement, Microsoft.OpenApi.Interfaces.IOpenApiExtensible, Microsoft.OpenApi.Interfaces.IOpenApiReferenceable, Microsoft.OpenApi.Interfaces.IOpenApiSerializable
909+
public class OpenApiSchema : Microsoft.OpenApi.Interfaces.IEffective<Microsoft.OpenApi.Models.OpenApiSchema>, Microsoft.OpenApi.Interfaces.IOpenApiAnnotatable, Microsoft.OpenApi.Interfaces.IOpenApiElement, Microsoft.OpenApi.Interfaces.IOpenApiExtensible, Microsoft.OpenApi.Interfaces.IOpenApiReferenceable, Microsoft.OpenApi.Interfaces.IOpenApiSerializable
904910
{
905911
public OpenApiSchema() { }
906912
public OpenApiSchema(Microsoft.OpenApi.Models.OpenApiSchema schema) { }
907913
public Microsoft.OpenApi.Models.OpenApiSchema AdditionalProperties { get; set; }
908914
public bool AdditionalPropertiesAllowed { get; set; }
909915
public System.Collections.Generic.IList<Microsoft.OpenApi.Models.OpenApiSchema> AllOf { get; set; }
916+
public System.Collections.Generic.IDictionary<string, object> Annotations { get; set; }
910917
public System.Collections.Generic.IList<Microsoft.OpenApi.Models.OpenApiSchema> AnyOf { get; set; }
911918
public Microsoft.OpenApi.Any.IOpenApiAny Default { get; set; }
912919
public bool Deprecated { get; set; }

0 commit comments

Comments
 (0)