diff --git a/src/OpenApi/gen/Helpers/AssemblyTypeSymbolsVisitor.cs b/src/OpenApi/gen/Helpers/AssemblyTypeSymbolsVisitor.cs index 5f962a0c48fd..548ef98c2b84 100644 --- a/src/OpenApi/gen/Helpers/AssemblyTypeSymbolsVisitor.cs +++ b/src/OpenApi/gen/Helpers/AssemblyTypeSymbolsVisitor.cs @@ -44,7 +44,7 @@ public override void VisitNamedType(INamedTypeSymbol type) { _cancellationToken.ThrowIfCancellationRequested(); - if (!IsDeclaredInAssembly(type) || !_exportedTypes.Add(type)) + if (!IsAccessibleType(type) || !_exportedTypes.Add(type)) { return; } @@ -61,7 +61,7 @@ public override void VisitNamedType(INamedTypeSymbol type) foreach (var property in properties) { _cancellationToken.ThrowIfCancellationRequested(); - if (IsDeclaredInAssembly(property) && _exportedProperties.Add(property)) + if (IsAccessibleType(property) && _exportedProperties.Add(property)) { property.Type.Accept(this); } @@ -70,13 +70,16 @@ public override void VisitNamedType(INamedTypeSymbol type) foreach (var method in methods) { _cancellationToken.ThrowIfCancellationRequested(); - if (IsDeclaredInAssembly(method) && _exportedMethods.Add(method)) + if (IsAccessibleType(method) && _exportedMethods.Add(method)) { method.Accept(this); } } } - private bool IsDeclaredInAssembly(ISymbol symbol) => - SymbolEqualityComparer.Default.Equals(symbol.ContainingAssembly, assemblySymbol); + private bool IsAccessibleType(ISymbol symbol) => + SymbolEqualityComparer.Default.Equals(symbol.ContainingAssembly, assemblySymbol) + && (symbol.DeclaredAccessibility == Accessibility.Public || + symbol.DeclaredAccessibility == Accessibility.Internal || + symbol.DeclaredAccessibility == Accessibility.ProtectedOrInternal); } diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/AdditionalTextsTests.Schemas.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/AdditionalTextsTests.Schemas.cs index 04a8afbd2422..b30dee1a8c3e 100644 --- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/AdditionalTextsTests.Schemas.cs +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/AdditionalTextsTests.Schemas.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Globalization; using System.Text.Json.Nodes; using Microsoft.OpenApi.Models; @@ -67,6 +68,34 @@ public class BoardItem /// public string Name { get; set; } } + + private class Element + { + /// + /// The unique identifier for the element. + /// + /// + /// This won't be emitted since it is a public + /// property on a private class. + /// + public string Name { get; set; } + } + + protected internal class ProtectedInternalElement + { + /// + /// The unique identifier for the element. + /// + public string Name { get; set; } + } + + protected class ProtectedElement + { + /// + /// The unique identifier for the element. + /// + public string Name { get; set; } + } } /// @@ -201,18 +230,17 @@ await SnapshotTestHelper.VerifyOpenApi(compilation, additionalAssemblies, docume var longTypeExample = Assert.IsAssignableFrom(typeWithExamples.Properties["longType"].Example); Assert.Equal(1234567890123456789, longTypeExample.GetValue()); - // Broken due to https://github.com/microsoft/OpenAPI.NET/issues/2137 - // var doubleTypeExample = Assert.IsAssignableFrom(typeWithExamples.Properties["doubleType"].Example); - // Assert.Equal("3.14", doubleTypeExample.GetValue()); + var doubleTypeExample = Assert.IsAssignableFrom(typeWithExamples.Properties["doubleType"].Example); + Assert.Equal(3.14, doubleTypeExample.GetValue()); - // var floatTypeExample = Assert.IsAssignableFrom(typeWithExamples.Properties["floatType"].Example); - // Assert.Equal(3.14f, floatTypeExample.GetValue()); + var floatTypeExample = Assert.IsAssignableFrom(typeWithExamples.Properties["floatType"].Example); + Assert.Equal(3.14f, floatTypeExample.GetValue()); - // var dateTimeTypeExample = Assert.IsAssignableFrom(typeWithExamples.Properties["dateTimeType"].Example); - // Assert.Equal(DateTime.Parse("2022-01-01T00:00:00Z", CultureInfo.InvariantCulture), dateTimeTypeExample.GetValue()); + var dateTimeTypeExample = Assert.IsAssignableFrom(typeWithExamples.Properties["dateTimeType"].Example); + Assert.Equal(new DateTime(2022, 01, 01), dateTimeTypeExample.GetValue()); - // var dateOnlyTypeExample = Assert.IsAssignableFrom(typeWithExamples.Properties["dateOnlyType"].Example); - // Assert.Equal(DateOnly.Parse("2022-01-01", CultureInfo.InvariantCulture), dateOnlyTypeExample.GetValue()); + var dateOnlyTypeExample = Assert.IsAssignableFrom(typeWithExamples.Properties["dateOnlyType"].Example); + Assert.Equal("2022-01-01", dateOnlyTypeExample.GetValue()); var stringTypeExample = Assert.IsAssignableFrom(typeWithExamples.Properties["stringType"].Example); Assert.Equal("Hello, World!", stringTypeExample.GetValue()); @@ -223,15 +251,14 @@ await SnapshotTestHelper.VerifyOpenApi(compilation, additionalAssemblies, docume var byteTypeExample = Assert.IsAssignableFrom(typeWithExamples.Properties["byteType"].Example); Assert.Equal(255, byteTypeExample.GetValue()); - // Broken due to https://github.com/microsoft/OpenAPI.NET/issues/2137 - // var timeOnlyTypeExample = Assert.IsAssignableFrom(typeWithExamples.Properties["timeOnlyType"].Example); - // Assert.Equal(TimeOnly.Parse("12:30:45", CultureInfo.InvariantCulture), timeOnlyTypeExample.GetValue()); + var timeOnlyTypeExample = Assert.IsAssignableFrom(typeWithExamples.Properties["timeOnlyType"].Example); + Assert.Equal("12:30:45", timeOnlyTypeExample.GetValue()); - // var timeSpanTypeExample = Assert.IsAssignableFrom(typeWithExamples.Properties["timeSpanType"].Example); - // Assert.Equal(TimeSpan.Parse("P3DT4H5M", CultureInfo.InvariantCulture), timeSpanTypeExample.GetValue()); + var timeSpanTypeExample = Assert.IsAssignableFrom(typeWithExamples.Properties["timeSpanType"].Example); + Assert.Equal("P3DT4H5M", timeSpanTypeExample.GetValue()); - // var decimalTypeExample = Assert.IsAssignableFrom(typeWithExamples.Properties["decimalType"].Example); - // Assert.Equal(3.14159265359m, decimalTypeExample.GetValue()); + var decimalTypeExample = Assert.IsAssignableFrom(typeWithExamples.Properties["decimalType"].Example); + Assert.Equal(3.14159265359m, decimalTypeExample.GetValue()); var uriTypeExample = Assert.IsAssignableFrom(typeWithExamples.Properties["uriType"].Example); Assert.Equal("https://example.com", uriTypeExample.GetValue()); diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/SchemaTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/SchemaTests.cs index 19760ed60d63..1caa65378abd 100644 --- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/SchemaTests.cs +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/SchemaTests.cs @@ -27,6 +27,7 @@ public async Task SupportsXmlCommentsOnSchemas() app.MapPost("/todo", (Todo todo) => { }); app.MapPost("/project", (Project project) => { }); app.MapPost("/board", (ProjectBoard.BoardItem boardItem) => { }); +app.MapPost("/protected-internal-element", (ProjectBoard.ProtectedInternalElement element) => { }); app.MapPost("/project-record", (ProjectRecord project) => { }); app.MapPost("/todo-with-description", (TodoWithDescription todo) => { }); app.MapPost("/type-with-examples", (TypeWithExamples typeWithExamples) => { }); @@ -58,6 +59,43 @@ public class BoardItem { public string Name { get; set; } } + + /// + /// No XML comment processed here. + /// + private class Element + { + /// + /// The unique identifier for the element. + /// + /// + /// This won't be emitted since it is a public + /// property on a private class. + /// + public string Name { get; set; } + } + + /// + /// Can find this XML comment. + /// + protected internal class ProtectedInternalElement + { + /// + /// The unique identifier for the element. + /// + public string Name { get; set; } + } + + /// + /// No XML comment processed here. + /// + protected class ProtectedElement + { + /// + /// The unique identifier for the element. + /// + public string Name { get; set; } + } } /// @@ -130,7 +168,7 @@ public interface IUser } /// -public class User : IUser +internal class User : IUser { /// public int Id { get; set; } @@ -155,6 +193,11 @@ await SnapshotTestHelper.VerifyOpenApi(compilation, document => var board = path.RequestBody.Content["application/json"].Schema; Assert.Equal("An item on the board.", board.Description); + path = document.Paths["/protected-internal-element"].Operations[OperationType.Post]; + var element = path.RequestBody.Content["application/json"].Schema; + Assert.Equal("The unique identifier for the element.", element.Properties["name"].Description); + Assert.Equal("Can find this XML comment.", element.Description); + path = document.Paths["/project-record"].Operations[OperationType.Post]; project = path.RequestBody.Content["application/json"].Schema; @@ -179,18 +222,17 @@ await SnapshotTestHelper.VerifyOpenApi(compilation, document => var longTypeExample = Assert.IsAssignableFrom(typeWithExamples.Properties["longType"].Example); Assert.Equal(1234567890123456789, longTypeExample.GetValue()); - // Broken due to https://github.com/microsoft/OpenAPI.NET/issues/2137 - // var doubleTypeExample = Assert.IsAssignableFrom(typeWithExamples.Properties["doubleType"].Example); - // Assert.Equal("3.14", doubleTypeExample.GetValue()); + var doubleTypeExample = Assert.IsAssignableFrom(typeWithExamples.Properties["doubleType"].Example); + Assert.Equal(3.14, doubleTypeExample.GetValue()); - // var floatTypeExample = Assert.IsAssignableFrom(typeWithExamples.Properties["floatType"].Example); - // Assert.Equal(3.14f, floatTypeExample.GetValue()); + var floatTypeExample = Assert.IsAssignableFrom(typeWithExamples.Properties["floatType"].Example); + Assert.Equal(3.14f, floatTypeExample.GetValue()); - // var dateTimeTypeExample = Assert.IsAssignableFrom(typeWithExamples.Properties["dateTimeType"].Example); - // Assert.Equal(DateTime.Parse("2022-01-01T00:00:00Z", CultureInfo.InvariantCulture), dateTimeTypeExample.GetValue()); + var dateTimeTypeExample = Assert.IsAssignableFrom(typeWithExamples.Properties["dateTimeType"].Example); + Assert.Equal(new DateTime(2022, 01, 01), dateTimeTypeExample.GetValue()); - // var dateOnlyTypeExample = Assert.IsAssignableFrom(typeWithExamples.Properties["dateOnlyType"].Example); - // Assert.Equal(DateOnly.Parse("2022-01-01", CultureInfo.InvariantCulture), dateOnlyTypeExample.GetValue()); + var dateOnlyTypeExample = Assert.IsAssignableFrom(typeWithExamples.Properties["dateOnlyType"].Example); + Assert.Equal("2022-01-01", dateOnlyTypeExample.GetValue()); var stringTypeExample = Assert.IsAssignableFrom(typeWithExamples.Properties["stringType"].Example); Assert.Equal("Hello, World!", stringTypeExample.GetValue()); @@ -201,15 +243,14 @@ await SnapshotTestHelper.VerifyOpenApi(compilation, document => var byteTypeExample = Assert.IsAssignableFrom(typeWithExamples.Properties["byteType"].Example); Assert.Equal(255, byteTypeExample.GetValue()); - // Broken due to https://github.com/microsoft/OpenAPI.NET/issues/2137 - // var timeOnlyTypeExample = Assert.IsAssignableFrom(typeWithExamples.Properties["timeOnlyType"].Example); - // Assert.Equal(TimeOnly.Parse("12:30:45", CultureInfo.InvariantCulture), timeOnlyTypeExample.GetValue()); + var timeOnlyTypeExample = Assert.IsAssignableFrom(typeWithExamples.Properties["timeOnlyType"].Example); + Assert.Equal("12:30:45", timeOnlyTypeExample.GetValue()); - // var timeSpanTypeExample = Assert.IsAssignableFrom(typeWithExamples.Properties["timeSpanType"].Example); - // Assert.Equal(TimeSpan.Parse("P3DT4H5M", CultureInfo.InvariantCulture), timeSpanTypeExample.GetValue()); + var timeSpanTypeExample = Assert.IsAssignableFrom(typeWithExamples.Properties["timeSpanType"].Example); + Assert.Equal("P3DT4H5M", timeSpanTypeExample.GetValue()); - // var decimalTypeExample = Assert.IsAssignableFrom(typeWithExamples.Properties["decimalType"].Example); - // Assert.Equal(3.14159265359m, decimalTypeExample.GetValue()); + var decimalTypeExample = Assert.IsAssignableFrom(typeWithExamples.Properties["decimalType"].Example); + Assert.Equal(3.14159265359m, decimalTypeExample.GetValue()); var uriTypeExample = Assert.IsAssignableFrom(typeWithExamples.Properties["uriType"].Example); Assert.Equal("https://example.com", uriTypeExample.GetValue()); diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/SchemaTests.SupportsXmlCommentsOnSchemas#OpenApiXmlCommentSupport.generated.verified.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/SchemaTests.SupportsXmlCommentsOnSchemas#OpenApiXmlCommentSupport.generated.verified.cs index 1eab4e75a810..0a5b3be9e07f 100644 --- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/SchemaTests.SupportsXmlCommentsOnSchemas#OpenApiXmlCommentSupport.generated.verified.cs +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/SchemaTests.SupportsXmlCommentsOnSchemas#OpenApiXmlCommentSupport.generated.verified.cs @@ -189,8 +189,10 @@ private static Dictionary GenerateCacheEntries() _cache.Add(new MemberKey(typeof(global::Todo), MemberType.Type, null, null, []), new XmlComment(@"This is a todo item.", null, null, null, null, false, null, null, null)); _cache.Add(new MemberKey(typeof(global::Project), MemberType.Type, null, null, []), new XmlComment(@"The project that contains Todo items.", null, null, null, null, false, null, null, null)); _cache.Add(new MemberKey(typeof(global::ProjectBoard.BoardItem), MemberType.Type, null, null, []), new XmlComment(@"An item on the board.", null, null, null, null, false, null, null, null)); + _cache.Add(new MemberKey(typeof(global::ProjectBoard.ProtectedInternalElement), MemberType.Type, null, null, []), new XmlComment(@"Can find this XML comment.", null, null, null, null, false, null, null, null)); _cache.Add(new MemberKey(typeof(global::ProjectRecord), MemberType.Type, null, null, []), new XmlComment(@"The project that contains Todo items.", null, null, null, null, false, null, [new XmlParameterComment(@"Name", @"The name of the project.", null, false), new XmlParameterComment(@"Description", @"The description of the project.", null, false)], null)); _cache.Add(new MemberKey(typeof(global::User), MemberType.Type, null, null, []), new XmlComment(null, null, null, null, null, false, null, null, null)); + _cache.Add(new MemberKey(typeof(global::ProjectBoard.ProtectedInternalElement), MemberType.Property, "Name", null, []), new XmlComment(@"The unique identifier for the element.", null, null, null, null, false, null, null, null)); _cache.Add(new MemberKey(typeof(global::ProjectRecord), MemberType.Property, "Name", null, []), new XmlComment(@"The name of the project.", null, null, null, null, false, null, null, null)); _cache.Add(new MemberKey(typeof(global::ProjectRecord), MemberType.Property, "Description", null, []), new XmlComment(@"The description of the project.", null, null, null, null, false, null, null, null)); _cache.Add(new MemberKey(typeof(global::TodoWithDescription), MemberType.Property, "Id", null, []), new XmlComment(@"The identifier of the todo.", null, null, null, null, false, null, null, null));