diff --git a/src/Microsoft.OpenApi/Interfaces/IOpenApiAnnotatable.cs b/src/Microsoft.OpenApi/Interfaces/IOpenApiAnnotatable.cs
new file mode 100644
index 000000000..dc1ee84a0
--- /dev/null
+++ b/src/Microsoft.OpenApi/Interfaces/IOpenApiAnnotatable.cs
@@ -0,0 +1,19 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license.
+
+using System.Collections.Generic;
+
+namespace Microsoft.OpenApi.Interfaces
+{
+ ///
+ /// Represents an Open API element that can be annotated with
+ /// non-serializable properties in a property bag.
+ ///
+ public interface IOpenApiAnnotatable
+ {
+ ///
+ /// A collection of properties associated with the current OpenAPI element.
+ ///
+ IDictionary Annotations { get; set; }
+ }
+}
diff --git a/src/Microsoft.OpenApi/Models/OpenApiDocument.cs b/src/Microsoft.OpenApi/Models/OpenApiDocument.cs
index 8d4526a20..712bddb03 100644
--- a/src/Microsoft.OpenApi/Models/OpenApiDocument.cs
+++ b/src/Microsoft.OpenApi/Models/OpenApiDocument.cs
@@ -17,7 +17,7 @@ namespace Microsoft.OpenApi.Models
///
/// Describes an OpenAPI object (OpenAPI document). See: https://swagger.io/specification
///
- public class OpenApiDocument : IOpenApiSerializable, IOpenApiExtensible
+ public class OpenApiDocument : IOpenApiSerializable, IOpenApiExtensible, IOpenApiAnnotatable
{
///
/// Related workspace containing OpenApiDocuments that are referenced in this document
@@ -70,6 +70,9 @@ public class OpenApiDocument : IOpenApiSerializable, IOpenApiExtensible
///
public string HashCode => GenerateHashValue(this);
+ ///
+ public IDictionary Annotations { get; set; }
+
///
/// Parameter-less constructor
///
@@ -89,6 +92,7 @@ public OpenApiDocument(OpenApiDocument document)
Tags = document?.Tags != null ? new List(document.Tags) : null;
ExternalDocs = document?.ExternalDocs != null ? new(document?.ExternalDocs) : null;
Extensions = document?.Extensions != null ? new Dictionary(document.Extensions) : null;
+ Annotations = document?.Annotations != null ? new Dictionary(document.Annotations) : null;
}
///
diff --git a/src/Microsoft.OpenApi/Models/OpenApiOperation.cs b/src/Microsoft.OpenApi/Models/OpenApiOperation.cs
index 360cfe7c1..69054740e 100644
--- a/src/Microsoft.OpenApi/Models/OpenApiOperation.cs
+++ b/src/Microsoft.OpenApi/Models/OpenApiOperation.cs
@@ -12,7 +12,7 @@ namespace Microsoft.OpenApi.Models
///
/// Operation Object.
///
- public class OpenApiOperation : IOpenApiSerializable, IOpenApiExtensible
+ public class OpenApiOperation : IOpenApiSerializable, IOpenApiExtensible, IOpenApiAnnotatable
{
///
/// Default value for .
@@ -105,6 +105,9 @@ public class OpenApiOperation : IOpenApiSerializable, IOpenApiExtensible
///
public IDictionary Extensions { get; set; } = new Dictionary();
+ ///
+ public IDictionary Annotations { get; set; }
+
///
/// Parameterless constructor
///
@@ -128,6 +131,7 @@ public OpenApiOperation(OpenApiOperation operation)
Security = operation?.Security != null ? new List(operation.Security) : null;
Servers = operation?.Servers != null ? new List(operation.Servers) : null;
Extensions = operation?.Extensions != null ? new Dictionary(operation.Extensions) : null;
+ Annotations = operation?.Annotations != null ? new Dictionary(operation.Annotations) : null;
}
///
diff --git a/src/Microsoft.OpenApi/Models/OpenApiSchema.cs b/src/Microsoft.OpenApi/Models/OpenApiSchema.cs
index 40adf9a31..957e0f946 100644
--- a/src/Microsoft.OpenApi/Models/OpenApiSchema.cs
+++ b/src/Microsoft.OpenApi/Models/OpenApiSchema.cs
@@ -12,7 +12,7 @@ namespace Microsoft.OpenApi.Models
///
/// Schema Object.
///
- public class OpenApiSchema : IOpenApiReferenceable, IEffective, IOpenApiExtensible
+ public class OpenApiSchema : IOpenApiReferenceable, IEffective, IOpenApiExtensible, IOpenApiAnnotatable
{
///
/// Follow JSON Schema definition. Short text providing information about the data.
@@ -241,6 +241,9 @@ public class OpenApiSchema : IOpenApiReferenceable, IEffective, I
///
public OpenApiReference Reference { get; set; }
+ ///
+ public IDictionary Annotations { get; set; }
+
///
/// Parameterless constructor
///
@@ -290,6 +293,7 @@ public OpenApiSchema(OpenApiSchema schema)
Extensions = schema?.Extensions != null ? new Dictionary(schema.Extensions) : null;
UnresolvedReference = schema?.UnresolvedReference ?? UnresolvedReference;
Reference = schema?.Reference != null ? new(schema?.Reference) : null;
+ Annotations = schema?.Annotations != null ? new Dictionary(schema?.Annotations) : null;
}
///
diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.cs
index 7a7f883f6..168ec3ade 100644
--- a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.cs
+++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.cs
@@ -29,7 +29,8 @@ public class OpenApiDocumentTests
{
Type = ReferenceType.Schema,
Id = "schema2"
- }
+ },
+ Annotations = new Dictionary { { "x-foo", "bar" } }
},
["schema2"] = new()
{
@@ -38,7 +39,8 @@ public class OpenApiDocumentTests
{
["property1"] = new()
{
- Type = "string"
+ Type = "string",
+ Annotations = new Dictionary { { "key1", "value" } }
}
}
},
@@ -56,9 +58,11 @@ public class OpenApiDocumentTests
{
["property1"] = new()
{
- Type = "string"
+ Type = "string",
+ Annotations = new Dictionary { { "key1", "value" } }
}
},
+ Annotations = new Dictionary { { "key1", "value" } },
Reference = new()
{
Type = ReferenceType.Schema,
@@ -100,6 +104,7 @@ public class OpenApiDocumentTests
{
Version = "1.0.0"
},
+ Annotations = new Dictionary { { "key1", "value" } },
Components = TopLevelReferencingComponents
};
@@ -109,6 +114,7 @@ public class OpenApiDocumentTests
{
Version = "1.0.0"
},
+ Annotations = new Dictionary { { "key1", "value" } },
Components = TopLevelSelfReferencingComponentsWithOtherProperties
};
@@ -118,6 +124,7 @@ public class OpenApiDocumentTests
{
Version = "1.0.0"
},
+ Annotations = new Dictionary { { "key1", "value" } },
Components = TopLevelSelfReferencingComponents
};
@@ -509,6 +516,7 @@ public class OpenApiDocumentTests
}
}
},
+ Annotations = new Dictionary { { "key1", "value" } },
Components = AdvancedComponentsWithReference
};
@@ -884,6 +892,7 @@ public class OpenApiDocumentTests
}
}
},
+ Annotations = new Dictionary { { "key1", "value" } },
Components = AdvancedComponents
};
@@ -1272,6 +1281,7 @@ public class OpenApiDocumentTests
}
}
},
+ Annotations = new Dictionary { { "key1", "value" } },
Components = AdvancedComponents
};
@@ -1827,5 +1837,26 @@ public void SerializeV2DocumentWithStyleAsNullDoesNotWriteOutStyleValue()
expected = expected.MakeLineBreaksEnvironmentNeutral();
actual.Should().Be(expected);
}
+
+ [Fact]
+ public void OpenApiDocumentCopyConstructorWithAnnotationsSucceeds()
+ {
+ var baseDocument = new OpenApiDocument
+ {
+ Annotations = new Dictionary
+ {
+ ["key1"] = "value1",
+ ["key2"] = 2
+ }
+ };
+
+ var actualDocument = new OpenApiDocument(baseDocument);
+
+ Assert.Equal(baseDocument.Annotations["key1"], actualDocument.Annotations["key1"]);
+
+ baseDocument.Annotations["key1"] = "value2";
+
+ Assert.NotEqual(baseDocument.Annotations["key1"], actualDocument.Annotations["key1"]);
+ }
}
}
diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiOperationTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiOperationTests.cs
index ec6ca8326..52906e61c 100644
--- a/test/Microsoft.OpenApi.Tests/Models/OpenApiOperationTests.cs
+++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiOperationTests.cs
@@ -87,7 +87,8 @@ public class OpenApiOperationTests
Url = "http://server.com",
Description = "serverDescription"
}
- }
+ },
+ Annotations = new Dictionary { { "key1", "value1" }, { "key2", 2 } },
};
private static readonly OpenApiOperation _advancedOperationWithTagsAndSecurity = new()
@@ -864,5 +865,26 @@ public void EnsureOpenApiOperationCopyConstructor_SerializationResultsInSame()
actual.Should().Be(expected);
}
}
+
+ [Fact]
+ public void OpenApiOperationCopyConstructorWithAnnotationsSucceeds()
+ {
+ var baseOperation = new OpenApiOperation
+ {
+ Annotations = new Dictionary
+ {
+ ["key1"] = "value1",
+ ["key2"] = 2
+ }
+ };
+
+ var actualOperation = new OpenApiOperation(baseOperation);
+
+ Assert.Equal(baseOperation.Annotations["key1"], actualOperation.Annotations["key1"]);
+
+ baseOperation.Annotations["key1"] = "value2";
+
+ Assert.NotEqual(baseOperation.Annotations["key1"], actualOperation.Annotations["key1"]);
+ }
}
}
diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.cs
index 0acd55925..1906dbbc4 100644
--- a/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.cs
+++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.cs
@@ -36,7 +36,8 @@ public class OpenApiSchemaTests
ExternalDocs = new()
{
Url = new("http://example.com/externalDocs")
- }
+ },
+ Annotations = new Dictionary { { "key1", "value1" }, { "key2", 2 } }
};
public static readonly OpenApiSchema AdvancedSchemaObject = new()
@@ -483,6 +484,27 @@ public void OpenApiSchemaCopyConstructorSucceeds()
Assert.True(actualSchema.Nullable);
}
+ [Fact]
+ public void OpenApiSchemaCopyConstructorWithAnnotationsSucceeds()
+ {
+ var baseSchema = new OpenApiSchema
+ {
+ Annotations = new Dictionary
+ {
+ ["key1"] = "value1",
+ ["key2"] = 2
+ }
+ };
+
+ var actualSchema = new OpenApiSchema(baseSchema);
+
+ Assert.Equal(baseSchema.Annotations["key1"], actualSchema.Annotations["key1"]);
+
+ baseSchema.Annotations["key1"] = "value2";
+
+ Assert.NotEqual(baseSchema.Annotations["key1"], actualSchema.Annotations["key1"]);
+ }
+
public static TheoryData SchemaExamples()
{
return new()
diff --git a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt
index 82c5f6a88..fbfe564f3 100755
--- a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt
+++ b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt
@@ -302,6 +302,10 @@ namespace Microsoft.OpenApi.Interfaces
{
T GetEffective(Microsoft.OpenApi.Models.OpenApiDocument document);
}
+ public interface IOpenApiAnnotatable
+ {
+ System.Collections.Generic.IDictionary Annotations { get; set; }
+ }
public interface IOpenApiElement { }
public interface IOpenApiExtensible : Microsoft.OpenApi.Interfaces.IOpenApiElement
{
@@ -592,10 +596,11 @@ namespace Microsoft.OpenApi.Models
public void SerializeAsV2(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { }
public void SerializeAsV3(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { }
}
- public class OpenApiDocument : Microsoft.OpenApi.Interfaces.IOpenApiElement, Microsoft.OpenApi.Interfaces.IOpenApiExtensible, Microsoft.OpenApi.Interfaces.IOpenApiSerializable
+ public class OpenApiDocument : Microsoft.OpenApi.Interfaces.IOpenApiAnnotatable, Microsoft.OpenApi.Interfaces.IOpenApiElement, Microsoft.OpenApi.Interfaces.IOpenApiExtensible, Microsoft.OpenApi.Interfaces.IOpenApiSerializable
{
public OpenApiDocument() { }
public OpenApiDocument(Microsoft.OpenApi.Models.OpenApiDocument document) { }
+ public System.Collections.Generic.IDictionary Annotations { get; set; }
public Microsoft.OpenApi.Models.OpenApiComponents Components { get; set; }
public System.Collections.Generic.IDictionary Extensions { get; set; }
public Microsoft.OpenApi.Models.OpenApiExternalDocs ExternalDocs { get; set; }
@@ -774,11 +779,12 @@ namespace Microsoft.OpenApi.Models
public void SerializeAsV2(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { }
public void SerializeAsV3(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { }
}
- public class OpenApiOperation : Microsoft.OpenApi.Interfaces.IOpenApiElement, Microsoft.OpenApi.Interfaces.IOpenApiExtensible, Microsoft.OpenApi.Interfaces.IOpenApiSerializable
+ public class OpenApiOperation : Microsoft.OpenApi.Interfaces.IOpenApiAnnotatable, Microsoft.OpenApi.Interfaces.IOpenApiElement, Microsoft.OpenApi.Interfaces.IOpenApiExtensible, Microsoft.OpenApi.Interfaces.IOpenApiSerializable
{
public const bool DeprecatedDefault = false;
public OpenApiOperation() { }
public OpenApiOperation(Microsoft.OpenApi.Models.OpenApiOperation operation) { }
+ public System.Collections.Generic.IDictionary Annotations { get; set; }
public System.Collections.Generic.IDictionary Callbacks { get; set; }
public bool Deprecated { get; set; }
public string Description { get; set; }
@@ -900,13 +906,14 @@ namespace Microsoft.OpenApi.Models
public OpenApiResponses() { }
public OpenApiResponses(Microsoft.OpenApi.Models.OpenApiResponses openApiResponses) { }
}
- public class OpenApiSchema : Microsoft.OpenApi.Interfaces.IEffective, Microsoft.OpenApi.Interfaces.IOpenApiElement, Microsoft.OpenApi.Interfaces.IOpenApiExtensible, Microsoft.OpenApi.Interfaces.IOpenApiReferenceable, Microsoft.OpenApi.Interfaces.IOpenApiSerializable
+ public class OpenApiSchema : Microsoft.OpenApi.Interfaces.IEffective, Microsoft.OpenApi.Interfaces.IOpenApiAnnotatable, Microsoft.OpenApi.Interfaces.IOpenApiElement, Microsoft.OpenApi.Interfaces.IOpenApiExtensible, Microsoft.OpenApi.Interfaces.IOpenApiReferenceable, Microsoft.OpenApi.Interfaces.IOpenApiSerializable
{
public OpenApiSchema() { }
public OpenApiSchema(Microsoft.OpenApi.Models.OpenApiSchema schema) { }
public Microsoft.OpenApi.Models.OpenApiSchema AdditionalProperties { get; set; }
public bool AdditionalPropertiesAllowed { get; set; }
public System.Collections.Generic.IList AllOf { get; set; }
+ public System.Collections.Generic.IDictionary Annotations { get; set; }
public System.Collections.Generic.IList AnyOf { get; set; }
public Microsoft.OpenApi.Any.IOpenApiAny Default { get; set; }
public bool Deprecated { get; set; }