Skip to content

Make OpenAPI.NET library trim-compatible #1717

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 20 commits into from
Jul 22, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
d8eda7a
Make library trim-compatible
captainsafia Jul 4, 2024
08718e1
Avoid using generic overload for all enum types
captainsafia Jul 4, 2024
11466d7
Update PublicApi.Approved.txt
captainsafia Jul 4, 2024
a0b6f9d
Avoid binary compat break in CloneFromCopyConstructor
captainsafia Jul 6, 2024
5b006b2
Add obsoletion attributes and fix CloneFromCopyConstructor
captainsafia Jul 9, 2024
8ea0964
Add ReferenceType.Path and tests for display names
captainsafia Jul 9, 2024
2128408
Guard ReferenceType consumption for ExternalResource
captainsafia Jul 15, 2024
bced7d5
Add trimming test project and fix warnings
captainsafia Jul 17, 2024
be1046d
Apply suggestions from code review
baywet Jul 17, 2024
4cc030d
ci: adds trimming project to solution
baywet Jul 17, 2024
74275b8
ci: adds readers project to trimming test project
baywet Jul 17, 2024
7f7c54a
ci: adds missing trimming root assembly directive
baywet Jul 17, 2024
06b54c3
Fix build, address feedback, and remove Readers from trim tests
captainsafia Jul 17, 2024
c117bc5
Rely on enum fields never being trimmed
captainsafia Jul 18, 2024
35811cc
Address feedback
captainsafia Jul 19, 2024
8267c40
Add back doc comment on M.O.Readers
captainsafia Jul 19, 2024
6200767
Update test/Microsoft.OpenApi.Tests/Attributes/DisplayAttributeTests.cs
MaggieKimani1 Jul 22, 2024
47ad39d
Fix syntax error
MaggieKimani1 Jul 22, 2024
3f597ba
Merge branch 'vnext' into safia/trimmable
MaggieKimani1 Jul 22, 2024
321e20d
Use native AOT-friendly generic overload
MaggieKimani1 Jul 22, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 5 additions & 6 deletions src/Microsoft.OpenApi/Any/OpenApiAnyCloneHelper.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

using System.Reflection;
using System.Diagnostics.CodeAnalysis;

namespace Microsoft.OpenApi.Any
{
Expand All @@ -15,17 +15,16 @@ public class OpenApiAnyCloneHelper
/// </summary>
/// <param name="obj">The object instance.</param>
/// <returns>A clone copy or the object itself.</returns>
public static IOpenApiAny CloneFromCopyConstructor(IOpenApiAny obj)
public static IOpenApiAny CloneFromCopyConstructor<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>(T obj) where T : IOpenApiAny
{
if (obj != null)
{
var t = obj.GetType();
foreach (var ci in t.GetConstructors())
foreach (var ci in typeof(T).GetConstructors())
{
var pi = ci.GetParameters();
if (pi.Length == 1 && pi[0].ParameterType == t)
if (pi.Length == 1 && pi[0].ParameterType == typeof(T))
{
return (IOpenApiAny)ci.Invoke(new object[] { obj });
return (IOpenApiAny)ci.Invoke([obj]);
}
}
}
Expand Down
145 changes: 145 additions & 0 deletions src/Microsoft.OpenApi/Attributes/TrimmingAttributes.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

#nullable enable

namespace System.Diagnostics.CodeAnalysis
{
#if !NET7_0_OR_GREATER
/// <summary>
/// Indicates that certain members on a specified <see cref="Type"/> are accessed dynamically,
/// for example through <see cref="System.Reflection"/>.
/// </summary>
/// <remarks>
/// 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 <see cref="Type"/> or <see cref="string"/>.
///
/// When this attribute is applied to a location of type <see cref="string"/>, 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 <see cref="Type"/> instances returned from calling
/// <see cref="object.GetType"/> 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).
/// </remarks>
[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
{
/// <summary>
/// Initializes a new instance of the <see cref="DynamicallyAccessedMembersAttribute"/> class
/// with the specified member types.
/// </summary>
/// <param name="memberTypes">The types of members dynamically accessed.</param>
public DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes memberTypes)
{
MemberTypes = memberTypes;
}

/// <summary>
/// Gets the <see cref="DynamicallyAccessedMemberTypes"/> which specifies the type
/// of members dynamically accessed.
/// </summary>
public DynamicallyAccessedMemberTypes MemberTypes { get; }
}

/// <summary>
/// Specifies the types of members that are dynamically accessed.
///
/// This enumeration has a <see cref="FlagsAttribute"/> attribute that allows a
/// bitwise combination of its member values.
/// </summary>
[Flags]
internal enum DynamicallyAccessedMemberTypes
{
/// <summary>
/// Specifies no members.
/// </summary>
None = 0,

/// <summary>
/// Specifies the default, parameterless public constructor.
/// </summary>
PublicParameterlessConstructor = 0x0001,

/// <summary>
/// Specifies all public constructors.
/// </summary>
PublicConstructors = 0x0002 | PublicParameterlessConstructor,

/// <summary>
/// Specifies all non-public constructors.
/// </summary>
NonPublicConstructors = 0x0004,

/// <summary>
/// Specifies all public methods.
/// </summary>
PublicMethods = 0x0008,

/// <summary>
/// Specifies all non-public methods.
/// </summary>
NonPublicMethods = 0x0010,

/// <summary>
/// Specifies all public fields.
/// </summary>
PublicFields = 0x0020,

/// <summary>
/// Specifies all non-public fields.
/// </summary>
NonPublicFields = 0x0040,

/// <summary>
/// Specifies all public nested types.
/// </summary>
PublicNestedTypes = 0x0080,

/// <summary>
/// Specifies all non-public nested types.
/// </summary>
NonPublicNestedTypes = 0x0100,

/// <summary>
/// Specifies all public properties.
/// </summary>
PublicProperties = 0x0200,

/// <summary>
/// Specifies all non-public properties.
/// </summary>
NonPublicProperties = 0x0400,

/// <summary>
/// Specifies all public events.
/// </summary>
PublicEvents = 0x0800,

/// <summary>
/// Specifies all non-public events.
/// </summary>
NonPublicEvents = 0x1000,

/// <summary>
/// Specifies all interfaces implemented by the type.
/// </summary>
Interfaces = 0x2000,

/// <summary>
/// Specifies all members.
/// </summary>
All = ~None
}
#endif
}
103 changes: 103 additions & 0 deletions src/Microsoft.OpenApi/Extensions/EnumExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Linq;
using System.Reflection;
using Microsoft.OpenApi.Attributes;
using Microsoft.OpenApi.Models;

namespace Microsoft.OpenApi.Extensions
{
Expand Down Expand Up @@ -42,5 +43,107 @@ public static string GetDisplayName(this Enum enumValue)
var attribute = enumValue.GetAttributeOfType<DisplayAttribute>();
return attribute == null ? enumValue.ToString() : attribute.Name;
}

/// <summary>
/// Gets the enum display name for <see typeparamref="T"/> without the use of reflection.
/// </summary>
/// <typeparam name="T">The type of the enum value.</typeparam>
/// <param name="enumValue">The enum value.</param>
/// <returns>The display string to use.</returns>
public static string GetDisplayName<T>(this T enumValue) where T : Enum
{
return enumValue switch
{
ParameterStyle parameterStyle => parameterStyle.GetDisplayName(),
ParameterLocation parameterLocation => parameterLocation.GetDisplayName(),
ReferenceType referenceType => referenceType.GetDisplayName(),
OperationType operationType => operationType.GetDisplayName(),
SecuritySchemeType securitySchemeType => securitySchemeType.GetDisplayName(),
_ => enumValue.ToString()
};
}

/// <summary>
/// Gets the enum display for name <see cref="ParameterStyle" /> without the use of reflection.
/// </summary>
/// <param name="parameterStyle">The enum value.</param>
/// <returns>The display string to use.</returns>
internal static string GetDisplayName(this ParameterStyle parameterStyle) => parameterStyle switch
{
ParameterStyle.Matrix => "matrix",
ParameterStyle.Label => "label",
ParameterStyle.Form => "form",
ParameterStyle.Simple => "simple",
ParameterStyle.SpaceDelimited => "spaceDelimited",
ParameterStyle.PipeDelimited => "pipeDelimited",
ParameterStyle.DeepObject => "deepObject",
_ => parameterStyle.ToString()
};

/// <summary>
/// Gets the enum display for name <see cref="ParameterLocation" /> without the use of reflection.
/// </summary>
/// <param name="parameterLocation">The enum value.</param>
/// <returns>The display string to use.</returns>
internal static string GetDisplayName(this ParameterLocation parameterLocation) => parameterLocation switch
{
ParameterLocation.Query => "query",
ParameterLocation.Header => "header",
ParameterLocation.Path => "path",
ParameterLocation.Cookie => "cookie",
_ => parameterLocation.ToString()
};

/// <summary>
/// Gets the enum display for name <see cref="ReferenceType" /> without the use of reflection.
/// </summary>
/// <param name="referenceType">The enum value.</param>
/// <returns>The display string to use.</returns>
internal static string GetDisplayName(this ReferenceType referenceType) => referenceType switch
{
ReferenceType.Schema => "schemas",
ReferenceType.Response => "responses",
ReferenceType.Parameter => "parameters",
ReferenceType.Example => "examples",
ReferenceType.RequestBody => "requestBodies",
ReferenceType.Header => "headers",
ReferenceType.SecurityScheme => "securitySchemes",
ReferenceType.Link => "links",
ReferenceType.Callback => "callbacks",
ReferenceType.Tag => "tags",
_ => referenceType.ToString()
};

/// <summary>
/// Gets the enum display for name <see cref="OperationType" /> without the use of reflection.
/// </summary>
/// <param name="operationType">The enum value.</param>
/// <returns>The display string to use.</returns>
internal static string GetDisplayName(this OperationType operationType) => operationType switch
{
OperationType.Get => "get",
OperationType.Put => "put",
OperationType.Post => "post",
OperationType.Delete => "delete",
OperationType.Options => "options",
OperationType.Head => "head",
OperationType.Patch => "patch",
OperationType.Trace => "trace",
_ => operationType.ToString()
};

/// <summary>
/// Gets the enum display for name <see cref="SecuritySchemeType" /> without the use of reflection.
/// </summary>
/// <param name="securitySchemeType">The enum value.</param>
/// <returns>The display string to use.</returns>
internal static string GetDisplayName(this SecuritySchemeType securitySchemeType) => securitySchemeType switch
{
SecuritySchemeType.ApiKey => "apiKey",
SecuritySchemeType.Http => "http",
SecuritySchemeType.OAuth2 => "oauth2",
SecuritySchemeType.OpenIdConnect => "openIdConnect",
_ => securitySchemeType.ToString()
};
}
}
4 changes: 2 additions & 2 deletions src/Microsoft.OpenApi/Models/OpenApiReference.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@
return Id;
}

return "#/components/" + Type.GetDisplayName() + "/" + Id;
return "#/components/" + Type.Value.GetDisplayName() + "/" + Id;
}
}

Expand Down Expand Up @@ -201,7 +201,7 @@
return ExternalResource + "#" + Id;
}

return ExternalResource + "#/components/" + Type.GetDisplayName() + "/"+ Id;
return ExternalResource + "#/components/" + Type.Value.GetDisplayName() + "/"+ Id;
}

return ExternalResource;
Expand Down
Loading