diff --git a/Microsoft.OpenApi.sln b/Microsoft.OpenApi.sln
index bb3c028e7..67f3f0e17 100644
--- a/Microsoft.OpenApi.sln
+++ b/Microsoft.OpenApi.sln
@@ -30,6 +30,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.OpenApi.Hidi", "s
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.OpenApi.Hidi.Tests", "test\Microsoft.OpenApi.Hidi.Tests\Microsoft.OpenApi.Hidi.Tests.csproj", "{D8F799DD-04AC-4A13-B344-45A5B944450A}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.OpenApi.Trimming.Tests", "test\Microsoft.OpenApi.Trimming.Tests\Microsoft.OpenApi.Trimming.Tests.csproj", "{1D2E0C6E-B103-4CB6-912E-D56FA1501296}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -68,6 +70,10 @@ Global
{D8F799DD-04AC-4A13-B344-45A5B944450A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D8F799DD-04AC-4A13-B344-45A5B944450A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D8F799DD-04AC-4A13-B344-45A5B944450A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1D2E0C6E-B103-4CB6-912E-D56FA1501296}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1D2E0C6E-B103-4CB6-912E-D56FA1501296}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1D2E0C6E-B103-4CB6-912E-D56FA1501296}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1D2E0C6E-B103-4CB6-912E-D56FA1501296}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -81,6 +87,7 @@ Global
{AD79B61D-88CF-497C-9ED5-41AE3867C5AC} = {6357D7FD-2DE4-4900-ADB9-ABC37052040A}
{254841B5-7DAC-4D1D-A9C5-44FE5CE467BE} = {E546B92F-20A8-49C3-8323-4B25BB78F3E1}
{D8F799DD-04AC-4A13-B344-45A5B944450A} = {6357D7FD-2DE4-4900-ADB9-ABC37052040A}
+ {1D2E0C6E-B103-4CB6-912E-D56FA1501296} = {6357D7FD-2DE4-4900-ADB9-ABC37052040A}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {9F171EFC-0DB5-4B10-ABFA-AF48D52CC565}
diff --git a/src/Microsoft.OpenApi/Any/OpenApiAnyCloneHelper.cs b/src/Microsoft.OpenApi/Any/OpenApiAnyCloneHelper.cs
index f880b7d64..eaa1dac31 100644
--- a/src/Microsoft.OpenApi/Any/OpenApiAnyCloneHelper.cs
+++ b/src/Microsoft.OpenApi/Any/OpenApiAnyCloneHelper.cs
@@ -1,6 +1,9 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
+using System;
+using System.Diagnostics.CodeAnalysis;
+
namespace Microsoft.OpenApi.Any
{
///
@@ -13,6 +16,8 @@ public class OpenApiAnyCloneHelper
///
/// The object instance.
/// A clone copy or the object itself.
+ [Obsolete("Use native AoT-friendly generic overload of CloneFromCopyConstructor instead.")]
+ [RequiresUnreferencedCode("CloneFromCopyConstructor is not trim-compatible. Recommended to use native AoT-friendly type-specific overloads of CloneFromCopyConstructor instead.")]
public static IOpenApiAny CloneFromCopyConstructor(IOpenApiAny obj)
{
if (obj != null)
@@ -30,5 +35,27 @@ public static IOpenApiAny CloneFromCopyConstructor(IOpenApiAny obj)
return obj;
}
+
+ ///
+ /// Clones an instance of object from the copy constructor
+ ///
+ /// The object instance.
+ /// A clone copy or the object itself.
+ public static T CloneFromCopyConstructor<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>(T obj) where T : IOpenApiAny
+ {
+ if (obj != null)
+ {
+ foreach (var ci in typeof(T).GetConstructors())
+ {
+ var pi = ci.GetParameters();
+ if (pi.Length == 1 && pi[0].ParameterType == typeof(T))
+ {
+ return (T)ci.Invoke([obj]);
+ }
+ }
+ }
+
+ return obj;
+ }
}
}
diff --git a/src/Microsoft.OpenApi/Any/OpenApiArray.cs b/src/Microsoft.OpenApi/Any/OpenApiArray.cs
index 461e2284a..5a9af0fff 100644
--- a/src/Microsoft.OpenApi/Any/OpenApiArray.cs
+++ b/src/Microsoft.OpenApi/Any/OpenApiArray.cs
@@ -29,7 +29,7 @@ public OpenApiArray(OpenApiArray array)
AnyType = array.AnyType;
foreach (var item in array)
{
- Add(OpenApiAnyCloneHelper.CloneFromCopyConstructor(item));
+ Add(OpenApiAnyCloneHelper.CloneFromCopyConstructor(item));
}
}
diff --git a/src/Microsoft.OpenApi/Any/OpenApiObject.cs b/src/Microsoft.OpenApi/Any/OpenApiObject.cs
index 60cfc75c3..95783cc23 100644
--- a/src/Microsoft.OpenApi/Any/OpenApiObject.cs
+++ b/src/Microsoft.OpenApi/Any/OpenApiObject.cs
@@ -29,7 +29,7 @@ public OpenApiObject(OpenApiObject obj)
AnyType = obj.AnyType;
foreach (var key in obj.Keys)
{
- this[key] = OpenApiAnyCloneHelper.CloneFromCopyConstructor(obj[key]);
+ this[key] = OpenApiAnyCloneHelper.CloneFromCopyConstructor(obj[key]);
}
}
diff --git a/src/Microsoft.OpenApi/Attributes/TrimmingAttributes.cs b/src/Microsoft.OpenApi/Attributes/TrimmingAttributes.cs
new file mode 100644
index 000000000..538ed521e
--- /dev/null
+++ b/src/Microsoft.OpenApi/Attributes/TrimmingAttributes.cs
@@ -0,0 +1,425 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license.
+
+#nullable enable
+
+// This collection of attribute definitions are helpers for accessing trim-related attributes in
+// projects targeting .NET 6 or lower. Since the trimmer queries for these attributes by name, having
+// these attributes source included is sufficient for the trimmer to recognize them. For more information
+// on this approach, see https://devblogs.microsoft.com/dotnet/creating-aot-compatible-libraries/#approach-2-define-the-attributes-internally.
+namespace System.Diagnostics.CodeAnalysis
+{
+#if !NET7_0_OR_GREATER
+ ///
+ /// Indicates that the specified method requires the ability to generate new code at runtime,
+ /// for example through .
+ ///
+ ///
+ /// This allows tools to understand which methods are unsafe to call when compiling ahead of time.
+ ///
+ [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Class, Inherited = false)]
+ internal sealed class RequiresDynamicCodeAttribute : Attribute
+ {
+ ///
+ /// Initializes a new instance of the class
+ /// with the specified message.
+ ///
+ ///
+ /// A message that contains information about the usage of dynamic code.
+ ///
+ public RequiresDynamicCodeAttribute(string message)
+ {
+ Message = message;
+ }
+
+ ///
+ /// Gets a message that contains information about the usage of dynamic code.
+ ///
+ public string Message { get; }
+
+ ///
+ /// Gets or sets an optional URL that contains more information about the method,
+ /// why it requires dynamic code, and what options a consumer has to deal with it.
+ ///
+ public string? Url { get; set; }
+ }
+#endif
+
+#if !NET5_0_OR_GREATER
+ ///
+ /// Indicates that the specified method requires dynamic access to code that is not referenced
+ /// statically, for example through .
+ ///
+ ///
+ /// This allows tools to understand which methods are unsafe to call when removing unreferenced
+ /// code from an application.
+ ///
+ [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Class, Inherited = false)]
+ internal sealed class RequiresUnreferencedCodeAttribute : Attribute
+ {
+ ///
+ /// Initializes a new instance of the class
+ /// with the specified message.
+ ///
+ ///
+ /// A message that contains information about the usage of unreferenced code.
+ ///
+ public RequiresUnreferencedCodeAttribute(string message)
+ {
+ Message = message;
+ }
+
+ ///
+ /// Gets a message that contains information about the usage of unreferenced code.
+ ///
+ public string Message { get; }
+
+ ///
+ /// Gets or sets an optional URL that contains more information about the method,
+ /// why it requires unreferenced code, and what options a consumer has to deal with it.
+ ///
+ public string? Url { get; set; }
+ }
+
+ ///
+ /// Suppresses reporting of a specific rule violation, allowing multiple suppressions on a
+ /// single code artifact.
+ ///
+ ///
+ /// is different than
+ /// in that it doesn't have a
+ /// . So it is always preserved in the compiled assembly.
+ ///
+ [AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true)]
+ internal sealed class UnconditionalSuppressMessageAttribute : Attribute
+ {
+ ///
+ /// Initializes a new instance of the
+ /// class, specifying the category of the tool and the identifier for an analysis rule.
+ ///
+ /// The category for the attribute.
+ /// The identifier of the analysis rule the attribute applies to.
+ public UnconditionalSuppressMessageAttribute(string category, string checkId)
+ {
+ Category = category;
+ CheckId = checkId;
+ }
+
+ ///
+ /// Gets the category identifying the classification of the attribute.
+ ///
+ ///
+ /// The property describes the tool or tool analysis category
+ /// for which a message suppression attribute applies.
+ ///
+ public string Category { get; }
+
+ ///
+ /// Gets the identifier of the analysis tool rule to be suppressed.
+ ///
+ ///
+ /// Concatenated together, the and
+ /// properties form a unique check identifier.
+ ///
+ public string CheckId { get; }
+
+ ///
+ /// Gets or sets the scope of the code that is relevant for the attribute.
+ ///
+ ///
+ /// The Scope property is an optional argument that specifies the metadata scope for which
+ /// the attribute is relevant.
+ ///
+ public string? Scope { get; set; }
+
+ ///
+ /// Gets or sets a fully qualified path that represents the target of the attribute.
+ ///
+ ///
+ /// The property is an optional argument identifying the analysis target
+ /// of the attribute. An example value is "System.IO.Stream.ctor():System.Void".
+ /// Because it is fully qualified, it can be long, particularly for targets such as parameters.
+ /// The analysis tool user interface should be capable of automatically formatting the parameter.
+ ///
+ public string? Target { get; set; }
+
+ ///
+ /// Gets or sets an optional argument expanding on exclusion criteria.
+ ///
+ ///
+ /// The property is an optional argument that specifies additional
+ /// exclusion where the literal metadata target is not sufficiently precise. For example,
+ /// the cannot be applied within a method,
+ /// and it may be desirable to suppress a violation against a statement in the method that will
+ /// give a rule violation, but not against all statements in the method.
+ ///
+ public string? MessageId { get; set; }
+
+ ///
+ /// Gets or sets the justification for suppressing the code analysis message.
+ ///
+ public string? Justification { get; set; }
+ }
+
+ ///
+ /// States a dependency that one member has on another.
+ ///
+ ///
+ /// This can be used to inform tooling of a dependency that is otherwise not evident purely from
+ /// metadata and IL, for example a member relied on via reflection.
+ ///
+ [AttributeUsage(
+ AttributeTargets.Constructor | AttributeTargets.Field | AttributeTargets.Method,
+ AllowMultiple = true, Inherited = false)]
+ internal sealed class DynamicDependencyAttribute : Attribute
+ {
+ ///
+ /// Initializes a new instance of the class
+ /// with the specified signature of a member on the same type as the consumer.
+ ///
+ /// The signature of the member depended on.
+ public DynamicDependencyAttribute(string memberSignature)
+ {
+ MemberSignature = memberSignature;
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with the specified signature of a member on a .
+ ///
+ /// The signature of the member depended on.
+ /// The containing .
+ public DynamicDependencyAttribute(string memberSignature, Type type)
+ {
+ MemberSignature = memberSignature;
+ Type = type;
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with the specified signature of a member on a type in an assembly.
+ ///
+ /// The signature of the member depended on.
+ /// The full name of the type containing the specified member.
+ /// The assembly name of the type containing the specified member.
+ public DynamicDependencyAttribute(string memberSignature, string typeName, string assemblyName)
+ {
+ MemberSignature = memberSignature;
+ TypeName = typeName;
+ AssemblyName = assemblyName;
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with the specified types of members on a .
+ ///
+ /// The types of members depended on.
+ /// The containing the specified members.
+ public DynamicDependencyAttribute(DynamicallyAccessedMemberTypes memberTypes, Type type)
+ {
+ MemberTypes = memberTypes;
+ Type = type;
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with the specified types of members on a type in an assembly.
+ ///
+ /// The types of members depended on.
+ /// The full name of the type containing the specified members.
+ /// The assembly name of the type containing the specified members.
+ public DynamicDependencyAttribute(DynamicallyAccessedMemberTypes memberTypes, string typeName, string assemblyName)
+ {
+ MemberTypes = memberTypes;
+ TypeName = typeName;
+ AssemblyName = assemblyName;
+ }
+
+ ///
+ /// Gets the signature of the member depended on.
+ ///
+ ///
+ /// Either must be a valid string or
+ /// must not equal , but not both.
+ ///
+ public string? MemberSignature { get; }
+
+ ///
+ /// Gets the which specifies the type
+ /// of members depended on.
+ ///
+ ///
+ /// Either must be a valid string or
+ /// must not equal , but not both.
+ ///
+ public DynamicallyAccessedMemberTypes MemberTypes { get; }
+
+ ///
+ /// Gets the containing the specified member.
+ ///
+ ///
+ /// If neither nor are specified,
+ /// the type of the consumer is assumed.
+ ///
+ public Type? Type { get; }
+
+ ///
+ /// Gets the full name of the type containing the specified member.
+ ///
+ ///
+ /// If neither nor are specified,
+ /// the type of the consumer is assumed.
+ ///
+ public string? TypeName { get; }
+
+ ///
+ /// Gets the assembly name of the specified type.
+ ///
+ ///
+ /// is only valid when is specified.
+ ///
+ public string? AssemblyName { get; }
+
+ ///
+ /// Gets or sets the condition in which the dependency is applicable, e.g. "DEBUG".
+ ///
+ public string? Condition { get; set; }
+ }
+
+ ///
+ /// Indicates that certain members on a specified are accessed dynamically,
+ /// for example through .
+ ///
+ ///
+ /// This allows tools to understand which members are being accessed during the execution
+ /// of a program.
+ ///
+ /// This attribute is valid on members whose type is or .
+ ///
+ /// When this attribute is applied to a location of type , the assumption is
+ /// that the string represents a fully qualified type name.
+ ///
+ /// When this attribute is applied to a class, interface, or struct, the members specified
+ /// can be accessed dynamically on instances returned from calling
+ /// on instances of that class, interface, or struct.
+ ///
+ /// If the attribute is applied to a method it's treated as a special case and it implies
+ /// the attribute should be applied to the "this" parameter of the method. As such the attribute
+ /// should only be used on instance methods of types assignable to System.Type (or string, but no methods
+ /// will use it there).
+ ///
+ [AttributeUsage(
+ AttributeTargets.Field | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter |
+ AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Method |
+ AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct,
+ Inherited = false)]
+ internal sealed class DynamicallyAccessedMembersAttribute : Attribute
+ {
+ ///
+ /// Initializes a new instance of the class
+ /// with the specified member types.
+ ///
+ /// The types of members dynamically accessed.
+ public DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes memberTypes)
+ {
+ MemberTypes = memberTypes;
+ }
+
+ ///
+ /// Gets the which specifies the type
+ /// of members dynamically accessed.
+ ///
+ public DynamicallyAccessedMemberTypes MemberTypes { get; }
+ }
+
+ ///
+ /// Specifies the types of members that are dynamically accessed.
+ ///
+ /// This enumeration has a attribute that allows a
+ /// bitwise combination of its member values.
+ ///
+ [Flags]
+ internal enum DynamicallyAccessedMemberTypes
+ {
+ ///
+ /// Specifies no members.
+ ///
+ None = 0,
+
+ ///
+ /// Specifies the default, parameterless public constructor.
+ ///
+ PublicParameterlessConstructor = 0x0001,
+
+ ///
+ /// Specifies all public constructors.
+ ///
+ PublicConstructors = 0x0002 | PublicParameterlessConstructor,
+
+ ///
+ /// Specifies all non-public constructors.
+ ///
+ NonPublicConstructors = 0x0004,
+
+ ///
+ /// Specifies all public methods.
+ ///
+ PublicMethods = 0x0008,
+
+ ///
+ /// Specifies all non-public methods.
+ ///
+ NonPublicMethods = 0x0010,
+
+ ///
+ /// Specifies all public fields.
+ ///
+ PublicFields = 0x0020,
+
+ ///
+ /// Specifies all non-public fields.
+ ///
+ NonPublicFields = 0x0040,
+
+ ///
+ /// Specifies all public nested types.
+ ///
+ PublicNestedTypes = 0x0080,
+
+ ///
+ /// Specifies all non-public nested types.
+ ///
+ NonPublicNestedTypes = 0x0100,
+
+ ///
+ /// Specifies all public properties.
+ ///
+ PublicProperties = 0x0200,
+
+ ///
+ /// Specifies all non-public properties.
+ ///
+ NonPublicProperties = 0x0400,
+
+ ///
+ /// Specifies all public events.
+ ///
+ PublicEvents = 0x0800,
+
+ ///
+ /// Specifies all non-public events.
+ ///
+ NonPublicEvents = 0x1000,
+
+ ///
+ /// Specifies all interfaces implemented by the type.
+ ///
+ Interfaces = 0x2000,
+
+ ///
+ /// Specifies all members.
+ ///
+ All = ~None
+ }
+#endif
+}
diff --git a/src/Microsoft.OpenApi/Extensions/EnumExtensions.cs b/src/Microsoft.OpenApi/Extensions/EnumExtensions.cs
index 4e2e795d3..1eadaa5f4 100644
--- a/src/Microsoft.OpenApi/Extensions/EnumExtensions.cs
+++ b/src/Microsoft.OpenApi/Extensions/EnumExtensions.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT license.
using System;
+using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using Microsoft.OpenApi.Attributes;
@@ -21,10 +22,11 @@ public static class EnumExtensions
///
/// The attribute of the specified type or null.
///
+ [UnconditionalSuppressMessage("Trimming", "IL2075", Justification = "Fields are never trimmed for enum types.")]
public static T GetAttributeOfType(this Enum enumValue) where T : Attribute
{
var type = enumValue.GetType();
- var memInfo = type.GetMember(enumValue.ToString()).First();
+ var memInfo = type.GetField(enumValue.ToString(), BindingFlags.Public | BindingFlags.Static);
var attributes = memInfo.GetCustomAttributes(false);
return attributes.FirstOrDefault();
}
diff --git a/src/Microsoft.OpenApi/Extensions/StringExtensions.cs b/src/Microsoft.OpenApi/Extensions/StringExtensions.cs
index 51ce37365..541523df5 100644
--- a/src/Microsoft.OpenApi/Extensions/StringExtensions.cs
+++ b/src/Microsoft.OpenApi/Extensions/StringExtensions.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT license.
using System;
+using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using Microsoft.OpenApi.Attributes;
@@ -16,7 +17,7 @@ public static class StringExtensions
/// Gets the enum value based on the given enum type and display name.
///
/// The display name.
- public static T GetEnumFromDisplayName(this string displayName)
+ public static T GetEnumFromDisplayName<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] T>(this string displayName)
{
var type = typeof(T);
if (!type.IsEnum)
@@ -24,14 +25,12 @@ public static T GetEnumFromDisplayName(this string displayName)
return default;
}
- foreach (var value in Enum.GetValues(type))
+ foreach (var field in type.GetFields(BindingFlags.Public | BindingFlags.Static))
{
- var field = type.GetField(value.ToString());
-
var displayAttribute = (DisplayAttribute)field.GetCustomAttribute(typeof(DisplayAttribute));
if (displayAttribute != null && displayAttribute.Name == displayName)
{
- return (T)value;
+ return (T)field.GetValue(null);
}
}
diff --git a/src/Microsoft.OpenApi/Models/OpenApiExample.cs b/src/Microsoft.OpenApi/Models/OpenApiExample.cs
index 1b9d31022..c78353127 100644
--- a/src/Microsoft.OpenApi/Models/OpenApiExample.cs
+++ b/src/Microsoft.OpenApi/Models/OpenApiExample.cs
@@ -66,7 +66,7 @@ public OpenApiExample(OpenApiExample example)
{
Summary = example?.Summary ?? Summary;
Description = example?.Description ?? Description;
- Value = OpenApiAnyCloneHelper.CloneFromCopyConstructor(example?.Value);
+ Value = OpenApiAnyCloneHelper.CloneFromCopyConstructor(example?.Value);
ExternalValue = example?.ExternalValue ?? ExternalValue;
Extensions = example?.Extensions != null ? new Dictionary(example.Extensions) : null;
Reference = example?.Reference != null ? new(example?.Reference) : null;
diff --git a/src/Microsoft.OpenApi/Models/OpenApiHeader.cs b/src/Microsoft.OpenApi/Models/OpenApiHeader.cs
index 0e5fa4e8d..4093775a2 100644
--- a/src/Microsoft.OpenApi/Models/OpenApiHeader.cs
+++ b/src/Microsoft.OpenApi/Models/OpenApiHeader.cs
@@ -106,7 +106,7 @@ public OpenApiHeader(OpenApiHeader header)
Explode = header?.Explode ?? Explode;
AllowReserved = header?.AllowReserved ?? AllowReserved;
Schema = header?.Schema != null ? new(header?.Schema) : null;
- Example = OpenApiAnyCloneHelper.CloneFromCopyConstructor(header?.Example);
+ Example = OpenApiAnyCloneHelper.CloneFromCopyConstructor(header?.Example);
Examples = header?.Examples != null ? new Dictionary(header.Examples) : null;
Content = header?.Content != null ? new Dictionary(header.Content) : null;
Extensions = header?.Extensions != null ? new Dictionary(header.Extensions) : null;
diff --git a/src/Microsoft.OpenApi/Models/OpenApiMediaType.cs b/src/Microsoft.OpenApi/Models/OpenApiMediaType.cs
index 444f41ba5..4751c4b28 100644
--- a/src/Microsoft.OpenApi/Models/OpenApiMediaType.cs
+++ b/src/Microsoft.OpenApi/Models/OpenApiMediaType.cs
@@ -54,7 +54,7 @@ public OpenApiMediaType() {}
public OpenApiMediaType(OpenApiMediaType mediaType)
{
Schema = mediaType?.Schema != null ? new(mediaType?.Schema) : null;
- Example = OpenApiAnyCloneHelper.CloneFromCopyConstructor(mediaType?.Example);
+ Example = OpenApiAnyCloneHelper.CloneFromCopyConstructor(mediaType?.Example);
Examples = mediaType?.Examples != null ? new Dictionary(mediaType.Examples) : null;
Encoding = mediaType?.Encoding != null ? new Dictionary(mediaType.Encoding) : null;
Extensions = mediaType?.Extensions != null ? new Dictionary(mediaType.Extensions) : null;
diff --git a/src/Microsoft.OpenApi/Models/OpenApiParameter.cs b/src/Microsoft.OpenApi/Models/OpenApiParameter.cs
index fc1eaf8cc..1cfbd2c6c 100644
--- a/src/Microsoft.OpenApi/Models/OpenApiParameter.cs
+++ b/src/Microsoft.OpenApi/Models/OpenApiParameter.cs
@@ -166,7 +166,7 @@ public OpenApiParameter(OpenApiParameter parameter)
AllowReserved = parameter?.AllowReserved ?? AllowReserved;
Schema = parameter?.Schema != null ? new(parameter?.Schema) : null;
Examples = parameter?.Examples != null ? new Dictionary(parameter.Examples) : null;
- Example = OpenApiAnyCloneHelper.CloneFromCopyConstructor(parameter?.Example);
+ Example = OpenApiAnyCloneHelper.CloneFromCopyConstructor(parameter?.Example);
Content = parameter?.Content != null ? new Dictionary(parameter.Content) : null;
Extensions = parameter?.Extensions != null ? new Dictionary(parameter.Extensions) : null;
AllowEmptyValue = parameter?.AllowEmptyValue ?? AllowEmptyValue;
diff --git a/src/Microsoft.OpenApi/Models/OpenApiReference.cs b/src/Microsoft.OpenApi/Models/OpenApiReference.cs
index e366bf10d..86b165d65 100644
--- a/src/Microsoft.OpenApi/Models/OpenApiReference.cs
+++ b/src/Microsoft.OpenApi/Models/OpenApiReference.cs
@@ -83,7 +83,7 @@ public string ReferenceV3
return Id;
}
- return "#/components/" + Type.GetDisplayName() + "/" + Id;
+ return "#/components/" + Type.Value.GetDisplayName() + "/" + Id;
}
}
@@ -200,8 +200,11 @@ private string GetExternalReferenceV3()
{
return ExternalResource + "#" + Id;
}
-
- return ExternalResource + "#/components/" + Type.GetDisplayName() + "/"+ Id;
+
+ if (Type.HasValue)
+ {
+ return ExternalResource + "#/components/" + Type.Value.GetDisplayName() + "/"+ Id;
+ }
}
return ExternalResource;
diff --git a/src/Microsoft.OpenApi/Models/OpenApiSchema.cs b/src/Microsoft.OpenApi/Models/OpenApiSchema.cs
index fd38927b6..40adf9a31 100644
--- a/src/Microsoft.OpenApi/Models/OpenApiSchema.cs
+++ b/src/Microsoft.OpenApi/Models/OpenApiSchema.cs
@@ -263,7 +263,7 @@ public OpenApiSchema(OpenApiSchema schema)
MinLength = schema?.MinLength ?? MinLength;
Pattern = schema?.Pattern ?? Pattern;
MultipleOf = schema?.MultipleOf ?? MultipleOf;
- Default = OpenApiAnyCloneHelper.CloneFromCopyConstructor(schema?.Default);
+ Default = OpenApiAnyCloneHelper.CloneFromCopyConstructor(schema?.Default);
ReadOnly = schema?.ReadOnly ?? ReadOnly;
WriteOnly = schema?.WriteOnly ?? WriteOnly;
AllOf = schema?.AllOf != null ? new List(schema.AllOf) : null;
@@ -281,7 +281,7 @@ public OpenApiSchema(OpenApiSchema schema)
AdditionalPropertiesAllowed = schema?.AdditionalPropertiesAllowed ?? AdditionalPropertiesAllowed;
AdditionalProperties = schema?.AdditionalProperties != null ? new(schema?.AdditionalProperties) : null;
Discriminator = schema?.Discriminator != null ? new(schema?.Discriminator) : null;
- Example = OpenApiAnyCloneHelper.CloneFromCopyConstructor(schema?.Example);
+ Example = OpenApiAnyCloneHelper.CloneFromCopyConstructor(schema?.Example);
Enum = schema?.Enum != null ? new List(schema.Enum) : null;
Nullable = schema?.Nullable ?? Nullable;
ExternalDocs = schema?.ExternalDocs != null ? new(schema?.ExternalDocs) : null;
diff --git a/src/Microsoft.OpenApi/Models/RuntimeExpressionAnyWrapper.cs b/src/Microsoft.OpenApi/Models/RuntimeExpressionAnyWrapper.cs
index 1ef1aaaa1..796c72c3e 100644
--- a/src/Microsoft.OpenApi/Models/RuntimeExpressionAnyWrapper.cs
+++ b/src/Microsoft.OpenApi/Models/RuntimeExpressionAnyWrapper.cs
@@ -26,7 +26,7 @@ public RuntimeExpressionAnyWrapper() {}
///
public RuntimeExpressionAnyWrapper(RuntimeExpressionAnyWrapper runtimeExpressionAnyWrapper)
{
- Any = OpenApiAnyCloneHelper.CloneFromCopyConstructor(runtimeExpressionAnyWrapper?.Any);
+ Any = OpenApiAnyCloneHelper.CloneFromCopyConstructor(runtimeExpressionAnyWrapper?.Any);
Expression = runtimeExpressionAnyWrapper?.Expression;
}
diff --git a/src/Microsoft.OpenApi/Validations/ValidationRuleSet.cs b/src/Microsoft.OpenApi/Validations/ValidationRuleSet.cs
index 3dd916755..448683fd9 100644
--- a/src/Microsoft.OpenApi/Validations/ValidationRuleSet.cs
+++ b/src/Microsoft.OpenApi/Validations/ValidationRuleSet.cs
@@ -186,16 +186,15 @@ private static ValidationRuleSet BuildDefaultRuleSet()
{
var ruleSet = new ValidationRuleSet();
var validationRuleType = typeof(ValidationRule);
+
+ var ruleTypeProperties = GetValidationRuleTypes();
- var rules = typeof(ValidationRuleSet).Assembly.GetTypes()
- .Where(t => t.IsClass
- && t != typeof(object)
- && t.GetCustomAttributes(typeof(OpenApiRuleAttribute), false).Any())
- .SelectMany(t2 => t2.GetProperties(BindingFlags.Static | BindingFlags.Public)
- .Where(p => validationRuleType.IsAssignableFrom(p.PropertyType)));
-
- foreach (var property in rules)
+ foreach (var property in ruleTypeProperties)
{
+ if (!validationRuleType.IsAssignableFrom(property.PropertyType))
+ {
+ continue;
+ }
var propertyValue = property.GetValue(null); // static property
if (propertyValue is ValidationRule rule)
{
@@ -205,5 +204,28 @@ private static ValidationRuleSet BuildDefaultRuleSet()
return ruleSet;
}
+
+ internal static PropertyInfo[] GetValidationRuleTypes()
+ {
+ return [
+ ..typeof(OpenApiComponentsRules).GetProperties(BindingFlags.Static | BindingFlags.Public),
+ ..typeof(OpenApiContactRules).GetProperties(BindingFlags.Static | BindingFlags.Public),
+ ..typeof(OpenApiDocumentRules).GetProperties(BindingFlags.Static | BindingFlags.Public),
+ ..typeof(OpenApiExtensibleRules).GetProperties(BindingFlags.Static | BindingFlags.Public),
+ ..typeof(OpenApiExternalDocsRules).GetProperties(BindingFlags.Static | BindingFlags.Public),
+ ..typeof(OpenApiInfoRules).GetProperties(BindingFlags.Static | BindingFlags.Public),
+ ..typeof(OpenApiLicenseRules).GetProperties(BindingFlags.Static | BindingFlags.Public),
+ ..typeof(OpenApiMediaTypeRules).GetProperties(BindingFlags.Static | BindingFlags.Public),
+ ..typeof(OpenApiOAuthFlowRules).GetProperties(BindingFlags.Static | BindingFlags.Public),
+ ..typeof(OpenApiServerRules).GetProperties(BindingFlags.Static | BindingFlags.Public),
+ ..typeof(OpenApiResponseRules).GetProperties(BindingFlags.Static | BindingFlags.Public),
+ ..typeof(OpenApiResponsesRules).GetProperties(BindingFlags.Static | BindingFlags.Public),
+ ..typeof(OpenApiSchemaRules).GetProperties(BindingFlags.Static | BindingFlags.Public),
+ ..typeof(OpenApiHeaderRules).GetProperties(BindingFlags.Static | BindingFlags.Public),
+ ..typeof(OpenApiTagRules).GetProperties(BindingFlags.Static | BindingFlags.Public),
+ ..typeof(OpenApiPathsRules).GetProperties(BindingFlags.Static | BindingFlags.Public),
+ ..typeof(OpenApiParameterRules).GetProperties(BindingFlags.Static | BindingFlags.Public),
+ ];
+ }
}
}
diff --git a/test/Microsoft.OpenApi.Tests/Attributes/DisplayAttributeTests.cs b/test/Microsoft.OpenApi.Tests/Attributes/DisplayAttributeTests.cs
index 274258601..f9bb8beff 100644
--- a/test/Microsoft.OpenApi.Tests/Attributes/DisplayAttributeTests.cs
+++ b/test/Microsoft.OpenApi.Tests/Attributes/DisplayAttributeTests.cs
@@ -1,4 +1,5 @@
-using Microsoft.OpenApi.Attributes;
+using System;
+using Microsoft.OpenApi.Attributes;
using Microsoft.OpenApi.Extensions;
using Xunit;
@@ -14,6 +15,19 @@ public enum ApiLevel
Corporate = 3
}
+ [Flags]
+ public enum UserType
+ {
+ [DisplayAttribute("admin")]
+ Admin = 1,
+ [DisplayAttribute("editor")]
+ Editor = 2,
+ [DisplayAttribute("publisher")]
+ Publisher = 3,
+ [DisplayAttribute("all")]
+ All = Admin | Editor | Publisher
+ }
+
public class DisplayAttributeTests
{
[Theory]
@@ -24,5 +38,23 @@ public void GetDisplayNameExtensionShouldUseDisplayAttribute(ApiLevel apiLevel,
{
Assert.Equal(expected, apiLevel.GetDisplayName());
}
+
+ [Theory]
+ [InlineData(ApiLevel.Private,"private")]
+ [InlineData(ApiLevel.Public, "public")]
+ [InlineData(ApiLevel.Corporate, "corporate")]
+ public void GetEnumFromDisplayNameShouldReturnEnumValue(ApiLevel expected, string displayName)
+ {
+ Assert.Equal(expected, displayName.GetEnumFromDisplayName());
+ }
+
+ [Theory]
+ [InlineData(UserType.Admin,"admin")]
+ [InlineData(UserType.Publisher, "publisher")]
+ [InlineData(UserType.Editor, "editor")]
+ public void GetEnumFromDisplayNameShouldReturnEnumValueForFlagsEnum(UserType expected, string displayName)
+ {
+ Assert.Equal(expected, displayName.GetEnumFromDisplayName());
+ }
}
}
diff --git a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt
index 37f40bb11..82c5f6a88 100755
--- a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt
+++ b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt
@@ -22,7 +22,10 @@ namespace Microsoft.OpenApi.Any
public class OpenApiAnyCloneHelper
{
public OpenApiAnyCloneHelper() { }
+ [System.Obsolete("Use native AoT-friendly generic overload of CloneFromCopyConstructor instead.")]
public static Microsoft.OpenApi.Any.IOpenApiAny CloneFromCopyConstructor(Microsoft.OpenApi.Any.IOpenApiAny obj) { }
+ public static T CloneFromCopyConstructor(T obj)
+ where T : Microsoft.OpenApi.Any.IOpenApiAny { }
}
public class OpenApiArray : System.Collections.Generic.List, Microsoft.OpenApi.Any.IOpenApiAny, Microsoft.OpenApi.Interfaces.IOpenApiElement, Microsoft.OpenApi.Interfaces.IOpenApiExtension
{
diff --git a/test/Microsoft.OpenApi.Tests/Validations/ValidationRuleSetTests.cs b/test/Microsoft.OpenApi.Tests/Validations/ValidationRuleSetTests.cs
index 708d6ee64..3c302be13 100644
--- a/test/Microsoft.OpenApi.Tests/Validations/ValidationRuleSetTests.cs
+++ b/test/Microsoft.OpenApi.Tests/Validations/ValidationRuleSetTests.cs
@@ -1,6 +1,9 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
+using System.Linq;
+using System.Reflection;
+using Microsoft.OpenApi.Validations.Rules;
using Xunit;
namespace Microsoft.OpenApi.Validations.Tests
@@ -37,5 +40,25 @@ public void DefaultRuleSetPropertyReturnsTheCorrectRules()
// Update the number if you add new default rule(s).
Assert.Equal(23, rules.Count);
}
+
+ [Fact]
+ public void GetValidationRuleTypesReturnsAllSupportedTypes()
+ {
+ var declaredRuleTypeProperties = typeof(ValidationRuleSet).Assembly.GetTypes()
+ .Where(t => t.IsClass
+ && t != typeof(object)
+ && t.GetCustomAttributes(typeof(OpenApiRuleAttribute), false).Any())
+ .SelectMany(t2 => t2.GetProperties(BindingFlags.Static | BindingFlags.Public))
+ .ToList();
+
+ var resolvedRuleTypeProperties = ValidationRuleSet.GetValidationRuleTypes();
+
+ foreach (var ruleTypeProperty in resolvedRuleTypeProperties)
+ {
+ Assert.True(declaredRuleTypeProperties.Remove(ruleTypeProperty));
+ }
+
+ Assert.Empty(declaredRuleTypeProperties);
+ }
}
}
diff --git a/test/Microsoft.OpenApi.Trimming.Tests/Microsoft.OpenApi.Trimming.Tests.csproj b/test/Microsoft.OpenApi.Trimming.Tests/Microsoft.OpenApi.Trimming.Tests.csproj
new file mode 100644
index 000000000..3e6daf74c
--- /dev/null
+++ b/test/Microsoft.OpenApi.Trimming.Tests/Microsoft.OpenApi.Trimming.Tests.csproj
@@ -0,0 +1,19 @@
+
+
+ Exe
+ net8.0
+ enable
+ enable
+ true
+ false
+ true
+ false
+
+
+
+
+
+
+
+
+
diff --git a/test/Microsoft.OpenApi.Trimming.Tests/Program.cs b/test/Microsoft.OpenApi.Trimming.Tests/Program.cs
new file mode 100644
index 000000000..1bc52a60a
--- /dev/null
+++ b/test/Microsoft.OpenApi.Trimming.Tests/Program.cs
@@ -0,0 +1 @@
+Console.WriteLine("Hello, World!");