diff --git a/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.Core/DocumentHelper.cs b/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.Core/DocumentHelper.cs index cdfcc362..1666274d 100644 --- a/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.Core/DocumentHelper.cs +++ b/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.Core/DocumentHelper.cs @@ -135,7 +135,8 @@ public Dictionary GetOpenApiSchemas(List elem var responses = elements.SelectMany(p => p.GetCustomAttributes(inherit: false)) .Select(p => p.BodyType); var types = requests.Union(responses) - .Select(p => p.IsOpenApiArray() || p.IsOpenApiDictionary() ? p.GetOpenApiSubType() : p) + .SelectMany(p => p.IsReferencedOpenApiArray() ? new[] { p, p.GetOpenApiSubType() } : + p.IsOpenApiArray() || p.IsOpenApiDictionary() ? new [] { p.GetOpenApiSubType() } : new[] { p }) .Distinct() .Where(p => !p.IsSimpleType()) .Where(p => p.IsReferentialType()) diff --git a/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.Core/Extensions/OpenApiParameterAttributeExtensions.cs b/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.Core/Extensions/OpenApiParameterAttributeExtensions.cs index 1e07499e..708b67e3 100644 --- a/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.Core/Extensions/OpenApiParameterAttributeExtensions.cs +++ b/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.Core/Extensions/OpenApiParameterAttributeExtensions.cs @@ -53,7 +53,7 @@ public static OpenApiParameter ToOpenApiParameter(this OpenApiParameterAttribute Schema = schema }; - if (type.IsOpenApiArray()) + if (type.IsOpenApiArray() || type.IsReferencedOpenApiArray()) { if (attribute.In == ParameterLocation.Path) { diff --git a/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.Core/Extensions/OpenApiPayloadAttributeExtensions.cs b/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.Core/Extensions/OpenApiPayloadAttributeExtensions.cs index da0270de..3cb5180e 100644 --- a/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.Core/Extensions/OpenApiPayloadAttributeExtensions.cs +++ b/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.Core/Extensions/OpenApiPayloadAttributeExtensions.cs @@ -55,7 +55,7 @@ public static OpenApiMediaType ToOpenApiMediaType(this T attribute, NamingStr } // For array and dictionary object, the reference has already been added by the visitor. - if (type.IsReferentialType() && !type.IsOpenApiNullable() && !type.IsOpenApiArray() && !type.IsOpenApiDictionary()) + if (type.IsReferentialType() && !type.IsOpenApiNullable() && !type.IsOpenApiArray() && !type.IsReferencedOpenApiArray() && !type.IsOpenApiDictionary()) { var reference = new OpenApiReference() { diff --git a/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.Core/Extensions/TypeExtensions.cs b/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.Core/Extensions/TypeExtensions.cs index 95875b50..66041bd2 100644 --- a/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.Core/Extensions/TypeExtensions.cs +++ b/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.Core/Extensions/TypeExtensions.cs @@ -322,6 +322,21 @@ public static bool IsOpenApiArray(this Type type) } return type.IsArrayType(); + } + + /// + /// Checks whether the given type is an array type that should be referenced or not, from the OpenAPI perspective. + /// + /// instance. + /// Returns True, if the type is identified as array; otherwise returns False. + public static bool IsReferencedOpenApiArray(this Type type) + { + if (type.IsNullOrDefault()) + { + return false; + } + + return type.IsInheritedArrayType(); } /// @@ -460,9 +475,9 @@ public static Type GetUnderlyingType(this Type type) underlyingType = nullableUnderlyingType; } - if (type.IsOpenApiArray()) + if (type.IsOpenApiArray() || type.IsReferencedOpenApiArray()) { - underlyingType = type.GetElementType() ?? type.GetGenericArguments()[0]; + underlyingType = type.GetElementType() ?? type.GetArrayTypeGenericArgument(); } if (type.IsOpenApiDictionary()) @@ -559,9 +574,9 @@ public static Type GetOpenApiSubType(this Type type) return type.GetElementType(); } - if (type.IsArrayType()) + if (type.IsArrayType() || type.IsInheritedArrayType()) { - return type.GetGenericArguments()[0]; + return type.GetArrayTypeGenericArgument(); } return null; @@ -594,9 +609,9 @@ public static string GetOpenApiSubTypeName(this Type type, NamingStrategy naming return namingStrategy.GetPropertyName(name, hasSpecifiedName: false); } - if (type.IsArrayType()) + if (type.IsArrayType() || type.IsInheritedArrayType()) { - var name = type.GetGenericArguments()[0].Name; + var name = type.GetArrayTypeGenericArgument().Name; return namingStrategy.GetPropertyName(name, hasSpecifiedName: false); } @@ -738,12 +753,40 @@ public static string GetTypeName(this Type type ,bool useFullName = false){ var name = useFullName ? type.FullName : type.Name; return name; - } + } + + private static Type GetArrayTypeGenericArgument(this Type type) + { + if (type.Name.Equals("IEnumerable`1", StringComparison.InvariantCultureIgnoreCase) == true) + { + return type.GetGenericArguments()[0]; + } + return type.GetInterfaces() + .Where(p => p.IsInterface) + .Where(p => p.Name.Equals("IEnumerable`1", StringComparison.InvariantCultureIgnoreCase) == true) + .FirstOrDefault()? + .GetGenericArguments()[0]; + } private static bool IsArrayType(this Type type) { var isArrayType = type.Name.Equals("String", StringComparison.InvariantCultureIgnoreCase) == false && + type.Namespace?.StartsWith("System") == true && + type.GetInterfaces() + .Where(p => p.IsInterface) + .Where(p => p.Name.Equals("IEnumerable", StringComparison.InvariantCultureIgnoreCase) == true) + .Any() && + type.IsJObjectType() == false && + type.IsDictionaryType() == false; + + return isArrayType; + } + + private static bool IsInheritedArrayType(this Type type) + { + var isArrayType = type.Name.Equals("String", StringComparison.InvariantCultureIgnoreCase) == false && + type.Namespace?.StartsWith("System") != true && type.GetInterfaces() .Where(p => p.IsInterface) .Where(p => p.Name.Equals("IEnumerable", StringComparison.InvariantCultureIgnoreCase) == true) diff --git a/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.Core/Visitors/ListObjectTypeVisitor.cs b/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.Core/Visitors/ListObjectTypeVisitor.cs index 97ea509d..e19092f3 100644 --- a/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.Core/Visitors/ListObjectTypeVisitor.cs +++ b/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.Core/Visitors/ListObjectTypeVisitor.cs @@ -26,7 +26,7 @@ public ListObjectTypeVisitor(VisitorCollection visitorCollection) /// public override bool IsVisitable(Type type) { - var isVisitable = this.IsVisitable(type, TypeCode.Object) && type.IsOpenApiArray(); + var isVisitable = this.IsVisitable(type, TypeCode.Object) && (type.IsOpenApiArray() || type.IsReferencedOpenApiArray()); return isVisitable; } @@ -98,6 +98,23 @@ public override void Visit(IAcceptor acceptor, KeyValuePair type, ) .ToDictionary(p => p.Key, p => p.Value); + if (type.Value.IsReferencedOpenApiArray()) + { + var reference = new OpenApiReference() + { + Type = ReferenceType.Schema, + Id = type.Value.GetOpenApiReferenceId(isDictionary: false, isList: false, namingStrategy, useFullName) + }; + + instance.Schemas[name].Title = type.Value.IsGenericType + ? namingStrategy.GetPropertyName(type.Value.GetTypeName(useFullName).Split('`').First(), hasSpecifiedName: false) + "_" + + string.Join("_", + type.Value.GenericTypeArguments.Select(a => namingStrategy.GetPropertyName(a.GetTypeName(useFullName), false))) + : namingStrategy.GetPropertyName(type.Value.GetTypeName(useFullName), hasSpecifiedName: false); + instance.Schemas[name].Type = "array"; + instance.Schemas[name].Reference = reference; + } + if (!schemasToBeAdded.Any()) { return; @@ -125,6 +142,18 @@ public override bool IsParameterVisitable(Type type) /// public override OpenApiSchema ParameterVisit(Type type, NamingStrategy namingStrategy) { + if (type.IsReferencedOpenApiArray()) + { + return new OpenApiSchema() + { + Reference = new OpenApiReference() + { + Type = ReferenceType.Schema, + Id = type.GetOpenApiReferenceId(isDictionary: false, isList: false, namingStrategy) + } + }; + } + var schema = this.ParameterVisit(dataType: "array", dataFormat: null); var underlyingType = type.GetUnderlyingType(); @@ -146,8 +175,19 @@ public override bool IsPayloadVisitable(Type type) /// public override OpenApiSchema PayloadVisit(Type type, NamingStrategy namingStrategy, bool useFullName = false) { - var schema = this.PayloadVisit(dataType: "array", dataFormat: null); + if (type.IsReferencedOpenApiArray()) + { + return new OpenApiSchema() + { + Reference = new OpenApiReference() + { + Type = ReferenceType.Schema, + Id = type.GetOpenApiReferenceId(isDictionary: false, isList: false, namingStrategy, useFullName) + } + }; + } + var schema = this.PayloadVisit(dataType: "array", dataFormat: null); // Gets the schema for the underlying type. var underlyingType = type.GetUnderlyingType(); var items = this.VisitorCollection.PayloadVisit(underlyingType, namingStrategy, useFullName); diff --git a/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.Core/Visitors/ObjectTypeVisitor.cs b/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.Core/Visitors/ObjectTypeVisitor.cs index 1c867422..c6a88aaf 100644 --- a/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.Core/Visitors/ObjectTypeVisitor.cs +++ b/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.Core/Visitors/ObjectTypeVisitor.cs @@ -226,7 +226,7 @@ private void ProcessProperties(IOpenApiSchemaAcceptor instance, string schemaNam // Adds schemas to the root. var schemasToBeAdded = subAcceptor.Schemas .Where(p => !instance.Schemas.Keys.Contains(p.Key)) - .Where(p => p.Value.IsOpenApiSchemaObject()) + .Where(p => p.Value.IsOpenApiSchemaObject() || (p.Value.IsOpenApiSchemaArray() && !p.Value.Title.IsNullOrWhiteSpace())) .GroupBy(p => p.Value.Title) .Select(p => p.First()) .ToDictionary(p => p.Value.Title, p => p.Value); diff --git a/test-integration/Microsoft.Azure.WebJobs.Extensions.OpenApi.Document.Tests/Get_ApplicationJson_ArrayObject_Tests.cs b/test-integration/Microsoft.Azure.WebJobs.Extensions.OpenApi.Document.Tests/Get_ApplicationJson_ArrayObject_Tests.cs index a3fd0fdc..80fd9608 100644 --- a/test-integration/Microsoft.Azure.WebJobs.Extensions.OpenApi.Document.Tests/Get_ApplicationJson_ArrayObject_Tests.cs +++ b/test-integration/Microsoft.Azure.WebJobs.Extensions.OpenApi.Document.Tests/Get_ApplicationJson_ArrayObject_Tests.cs @@ -86,6 +86,18 @@ public void Given_OpenApiDocument_Then_It_Should_Return_ComponentSchemaProperty( value.Value("type").Should().Be(propertyType); } + [DataTestMethod] + [DataRow("microsoft.Azure.WebJobs.Extensions.OpenApi.TestApp.Models.ArrayObjectModel", "object", "listStringObjectValue", "microsoft.Azure.WebJobs.Extensions.OpenApi.TestApp.Models.ListStringObjectModel")] + public void Given_OpenApiDocument_Then_It_Should_Return_ComponentSchemaPropertyWithReference(string @ref, string refType, string propertyName, string reference) + { + var properties = this._doc["components"]["schemas"][@ref]["properties"]; + + var value = properties[propertyName]; + + value.Should().NotBeNull(); + value.Value("$ref").Should().Be($"#/components/schemas/{reference}"); + } + [DataTestMethod] [DataRow("microsoft.Azure.WebJobs.Extensions.OpenApi.TestApp.Models.ArrayObjectModel", "object", "objectValue", "array", "object")] [DataRow("microsoft.Azure.WebJobs.Extensions.OpenApi.TestApp.Models.ArrayObjectModel", "object", "booleanValue", "array", "boolean")] diff --git a/test-integration/Microsoft.Azure.WebJobs.Extensions.OpenApi.Document.Tests/Get_ApplicationJson_Array_Tests.cs b/test-integration/Microsoft.Azure.WebJobs.Extensions.OpenApi.Document.Tests/Get_ApplicationJson_Array_Tests.cs index ad75d004..66354b54 100644 --- a/test-integration/Microsoft.Azure.WebJobs.Extensions.OpenApi.Document.Tests/Get_ApplicationJson_Array_Tests.cs +++ b/test-integration/Microsoft.Azure.WebJobs.Extensions.OpenApi.Document.Tests/Get_ApplicationJson_Array_Tests.cs @@ -32,6 +32,7 @@ public async Task Init() [DataRow("/get-applicationjson-int-array")] [DataRow("/get-applicationjson-bool-array")] [DataRow("/get-applicationjson-int-list")] + [DataRow("/get-applicationjson-named-list")] public void Given_OpenApiDocument_Then_It_Should_Return_Path(string path) { var paths = this._doc["paths"]; @@ -44,6 +45,7 @@ public void Given_OpenApiDocument_Then_It_Should_Return_Path(string path) [DataRow("/get-applicationjson-int-array", "get")] [DataRow("/get-applicationjson-bool-array", "get")] [DataRow("/get-applicationjson-int-list", "get")] + [DataRow("/get-applicationjson-named-list", "get")] public void Given_OpenApiDocument_Then_It_Should_Return_OperationType(string path, string operationType) { var pathItem = this._doc["paths"][path]; @@ -56,6 +58,7 @@ public void Given_OpenApiDocument_Then_It_Should_Return_OperationType(string pat [DataRow("/get-applicationjson-int-array", "get", "200")] [DataRow("/get-applicationjson-bool-array", "get", "200")] [DataRow("/get-applicationjson-int-list", "get", "200")] + [DataRow("/get-applicationjson-named-list", "get", "200")] public void Given_OpenApiDocument_Then_It_Should_Return_OperationResponse(string path, string operationType, string responseCode) { var responses = this._doc["paths"][path][operationType]["responses"]; @@ -68,6 +71,7 @@ public void Given_OpenApiDocument_Then_It_Should_Return_OperationResponse(string [DataRow("/get-applicationjson-int-array", "get", "200", "application/json")] [DataRow("/get-applicationjson-bool-array", "get", "200", "application/json")] [DataRow("/get-applicationjson-int-list", "get", "200", "application/json")] + [DataRow("/get-applicationjson-named-list", "get", "200", "application/json")] public void Given_OpenApiDocument_Then_It_Should_Return_OperationResponseContentType(string path, string operationType, string responseCode, string contentType) { var content = this._doc["paths"][path][operationType]["responses"][responseCode]["content"]; @@ -89,6 +93,29 @@ public void Given_OpenApiDocument_Then_It_Should_Return_OperationResponseContent schema.Value("type").Should().Be(dataType); } + [DataTestMethod] + [DataRow("/get-applicationjson-named-list", "get", "200", "application/json", "microsoft.Azure.WebJobs.Extensions.OpenApi.TestApp.Models.ListStringObjectModel")] + public void Given_OpenApiDocument_Then_It_Should_Return_OperationResponseContentTypeSchemaWithReference(string path, string operationType, string responseCode, string contentType, string reference) + { + var content = this._doc["paths"][path][operationType]["responses"][responseCode]["content"]; + + var @ref = content[contentType]["schema"]["$ref"]; + + @ref.Value().Should().Be($"#/components/schemas/{reference}"); + } + + [DataTestMethod] + [DataRow("microsoft.Azure.WebJobs.Extensions.OpenApi.TestApp.Models.ListStringObjectModel", "array")] + public void Given_OpenApiDocument_Then_It_Should_Return_ComponentSchemaProperty(string reference, string referenceType) + { + var properties = this._doc["components"]["schemas"][reference]; + + var type = properties["type"]; + + type.Should().NotBeNull(); + type.Value().Should().Be(referenceType); + } + [DataTestMethod] [DataRow("/get-applicationjson-int-array", "get", "200", "application/json", "array", "integer", "int32")] [DataRow("/get-applicationjson-int-list", "get", "200", "application/json", "array", "integer", "int32")] @@ -113,5 +140,13 @@ public void Given_OpenApiDocument_Then_It_Should_Return_OperationResponseContent items.Value("type").Should().Be(itemType); } + + [DataTestMethod] + [DataRow("microsoft.Azure.WebJobs.Extensions.OpenApi.TestApp.Models.ListStringObjectModel", "microsoft.Azure.WebJobs.Extensions.OpenApi.TestApp.Models.StringObjectModel")] + public void Given_OpenApiDocument_Then_It_Should_Return_ComponentSchemaPropertyItems(string reference, string itemRef) + { + var items = this._doc["components"]["schemas"][reference]["items"]; + items.Value("$ref").Should().Be($"#/components/schemas/{itemRef}"); + } } } diff --git a/test-integration/Microsoft.Azure.WebJobs.Extensions.OpenApi.TestApp/Get_ApplicationJson_Array_HttpTrigger.cs b/test-integration/Microsoft.Azure.WebJobs.Extensions.OpenApi.TestApp/Get_ApplicationJson_Array_HttpTrigger.cs index 1e5314d0..88827074 100644 --- a/test-integration/Microsoft.Azure.WebJobs.Extensions.OpenApi.TestApp/Get_ApplicationJson_Array_HttpTrigger.cs +++ b/test-integration/Microsoft.Azure.WebJobs.Extensions.OpenApi.TestApp/Get_ApplicationJson_Array_HttpTrigger.cs @@ -1,4 +1,6 @@ +using System.Collections; using System.Collections.Generic; +using System.Linq; using System.Net; using System.Threading.Tasks; @@ -14,7 +16,7 @@ namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.TestApp { public static class Get_ApplicationJson_Array_HttpTrigger { - + [FunctionName(nameof(Get_ApplicationJson_Array_HttpTrigger.Get_ApplicationJson_StringArray))] [OpenApiOperation(operationId: nameof(Get_ApplicationJson_Array_HttpTrigger.Get_ApplicationJson_StringArray), tags: new[] { "array" })] [OpenApiResponseWithBody(statusCode: HttpStatusCode.OK, contentType: "application/json", bodyType: typeof(string[]), Description = "The OK response")] @@ -22,7 +24,7 @@ public static async Task Get_ApplicationJson_StringArray( [HttpTrigger(AuthorizationLevel.Anonymous, "GET", Route = "get-applicationjson-string-array")] HttpRequest req, ILogger log) { - var result = new OkResult(); + var result = new OkResult(); return await Task.FromResult(result).ConfigureAwait(false); } @@ -34,7 +36,7 @@ public static async Task Get_ApplicationJson_IntArray( [HttpTrigger(AuthorizationLevel.Anonymous, "GET", Route = "get-applicationjson-int-array")] HttpRequest req, ILogger log) { - var result = new OkResult(); + var result = new OkResult(); return await Task.FromResult(result).ConfigureAwait(false); } @@ -46,7 +48,7 @@ public static async Task Get_ApplicationJson_BoolArray( [HttpTrigger(AuthorizationLevel.Anonymous, "GET", Route = "get-applicationjson-bool-array")] HttpRequest req, ILogger log) { - var result = new OkResult(); + var result = new OkResult(); return await Task.FromResult(result).ConfigureAwait(false); } @@ -58,9 +60,21 @@ public static async Task Get_ApplicationJson_IntList( [HttpTrigger(AuthorizationLevel.Anonymous, "GET", Route = "get-applicationjson-int-list")] HttpRequest req, ILogger log) { - var result = new OkResult(); + var result = new OkResult(); + + return await Task.FromResult(result).ConfigureAwait(false); + } + + [FunctionName(nameof(Get_ApplicationJson_Array_HttpTrigger.Get_ApplicationJson_NamedList))] + [OpenApiOperation(operationId: nameof(Get_ApplicationJson_Array_HttpTrigger.Get_ApplicationJson_NamedList), tags: new[] { "array" })] + [OpenApiResponseWithBody(statusCode: HttpStatusCode.OK, contentType: "application/json", bodyType: typeof(ListStringObjectModel), Description = "The OK response")] + public static async Task Get_ApplicationJson_NamedList( + [HttpTrigger(AuthorizationLevel.Anonymous, "GET", Route = "get-applicationjson-named-list")] HttpRequest req, + ILogger log) + { + var result = new OkResult(); return await Task.FromResult(result).ConfigureAwait(false); } } -} \ No newline at end of file +} diff --git a/test-integration/Microsoft.Azure.WebJobs.Extensions.OpenApi.TestApp/Models/ArrayObjectModel.cs b/test-integration/Microsoft.Azure.WebJobs.Extensions.OpenApi.TestApp/Models/ArrayObjectModel.cs index f7a0674b..ca346711 100644 --- a/test-integration/Microsoft.Azure.WebJobs.Extensions.OpenApi.TestApp/Models/ArrayObjectModel.cs +++ b/test-integration/Microsoft.Azure.WebJobs.Extensions.OpenApi.TestApp/Models/ArrayObjectModel.cs @@ -12,7 +12,7 @@ public class ArrayObjectModel public IReadOnlyCollection FloatValue { get; set; } public HashSet DecimalValue { get; set; } public ISet StringObjectValue { get; set; } - public List ObjectArrayValue { get; set; } + public ListStringObjectModel ListStringObjectValue { get; set; } } } diff --git a/test-integration/Microsoft.Azure.WebJobs.Extensions.OpenApi.TestApp/Models/ListStringObjectModel.cs b/test-integration/Microsoft.Azure.WebJobs.Extensions.OpenApi.TestApp/Models/ListStringObjectModel.cs new file mode 100644 index 00000000..3e9d22f3 --- /dev/null +++ b/test-integration/Microsoft.Azure.WebJobs.Extensions.OpenApi.TestApp/Models/ListStringObjectModel.cs @@ -0,0 +1,12 @@ +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.TestApp.Models; + +public class ListStringObjectModel : IEnumerable +{ + public IEnumerator GetEnumerator() => Enumerable.Empty().GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); +} diff --git a/test/Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Tests.Fakes/FakeList.cs b/test/Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Tests.Fakes/FakeList.cs new file mode 100644 index 00000000..ea8fd989 --- /dev/null +++ b/test/Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Tests.Fakes/FakeList.cs @@ -0,0 +1,13 @@ +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Tests.Fakes +{ + public class FakeList : IEnumerable + { + public IEnumerator GetEnumerator() => Enumerable.Empty().GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); + } +} diff --git a/test/Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Tests/Extensions/TypeExtensionsTests.cs b/test/Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Tests/Extensions/TypeExtensionsTests.cs index 0d6a81d7..7874bc88 100644 --- a/test/Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Tests/Extensions/TypeExtensionsTests.cs +++ b/test/Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Tests/Extensions/TypeExtensionsTests.cs @@ -33,6 +33,7 @@ public class TypeExtensionsTests [DataRow(typeof(IReadOnlyCollection), true)] [DataRow(typeof(HashSet), true)] [DataRow(typeof(ISet), true)] + [DataRow(typeof(FakeList), false)] [DataRow(typeof(Dictionary), false)] [DataRow(typeof(IDictionary), false)] [DataRow(typeof(IReadOnlyDictionary), false)] @@ -59,6 +60,7 @@ public void Given_ArrayTypes_When_IsOpenApiArray_Invoked_Then_It_Should_Return_R [DataRow(typeof(IReadOnlyCollection), false)] [DataRow(typeof(HashSet), false)] [DataRow(typeof(ISet), false)] + [DataRow(typeof(FakeList), false)] [DataRow(typeof(Dictionary), true)] [DataRow(typeof(IDictionary), true)] [DataRow(typeof(IReadOnlyDictionary), true)] @@ -119,6 +121,7 @@ public void Given_NullableType_When_GetUnderlyingType_Invoked_Then_It_Should_Ret [DataRow(typeof(List), typeof(int))] [DataRow(typeof(List), typeof(bool))] [DataRow(typeof(List), typeof(FakeModel))] + [DataRow(typeof(FakeList), typeof(FakeModel))] public void Given_ListType_When_GetUnderlyingType_Invoked_Then_It_Should_Return_Result(Type type, Type expected) { var result = TypeExtensions.GetUnderlyingType(type); diff --git a/test/Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Tests/Visitors/ListObjectTypeVisitorTests.cs b/test/Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Tests/Visitors/ListObjectTypeVisitorTests.cs index 60d67e26..6d841723 100644 --- a/test/Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Tests/Visitors/ListObjectTypeVisitorTests.cs +++ b/test/Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Tests/Visitors/ListObjectTypeVisitorTests.cs @@ -54,6 +54,7 @@ public void Given_Type_When_IsNavigatable_Invoked_Then_It_Should_Return_Result(T [DataRow(typeof(IReadOnlyCollection), true)] [DataRow(typeof(HashSet), true)] [DataRow(typeof(ISet), true)] + [DataRow(typeof(FakeList), true)] [DataRow(typeof(int), false)] public void Given_Type_When_IsVisitable_Invoked_Then_It_Should_Return_Result(Type type, bool expected) { @@ -71,6 +72,7 @@ public void Given_Type_When_IsVisitable_Invoked_Then_It_Should_Return_Result(Typ [DataRow(typeof(IReadOnlyCollection), true)] [DataRow(typeof(HashSet), true)] [DataRow(typeof(ISet), true)] + [DataRow(typeof(FakeList), true)] [DataRow(typeof(int), false)] public void Given_Type_When_IsParameterVisitable_Invoked_Then_It_Should_Return_Result(Type type, bool expected) { @@ -88,6 +90,7 @@ public void Given_Type_When_IsParameterVisitable_Invoked_Then_It_Should_Return_R [DataRow(typeof(IReadOnlyCollection), true)] [DataRow(typeof(HashSet), true)] [DataRow(typeof(ISet), true)] + [DataRow(typeof(FakeList), true)] [DataRow(typeof(int), false)] public void Given_Type_When_IsPayloadVisitable_Invoked_Then_It_Should_Return_Result(Type type, bool expected) { @@ -113,6 +116,7 @@ public void Given_Type_When_IsPayloadVisitable_Invoked_Then_It_Should_Return_Res [DataRow(typeof(IReadOnlyCollection), "array", null, "object", true, "fakeModel", 1)] [DataRow(typeof(HashSet), "array", null, "object", true, "fakeModel", 1)] [DataRow(typeof(ISet), "array", null, "object", true, "fakeModel", 1)] + [DataRow(typeof(FakeList), "array", null, "object", true, "fakeModel", 1)] public void Given_Type_When_Visit_Invoked_Then_It_Should_Return_Result(Type listType, string dataType, string dataFormat, string itemType, bool isReferential, string referenceId, int expected) { var name = "hello"; @@ -154,6 +158,7 @@ public void Given_Type_When_Visit_Invoked_Then_It_Should_Return_Result(Type list [DataRow(typeof(IReadOnlyCollection), 1)] [DataRow(typeof(HashSet), 1)] [DataRow(typeof(ISet), 1)] + [DataRow(typeof(FakeList), 1)] public void Given_MinLengthAttribute_When_Visit_Invoked_Then_It_Should_Return_Result(Type listType, int length) { var name = "hello"; @@ -185,6 +190,7 @@ public void Given_MinLengthAttribute_When_Visit_Invoked_Then_It_Should_Return_Re [DataRow(typeof(IReadOnlyCollection), 10)] [DataRow(typeof(HashSet), 10)] [DataRow(typeof(ISet), 10)] + [DataRow(typeof(FakeList), 10)] public void Given_MaxLengthAttribute_When_Visit_Invoked_Then_It_Should_Return_Result(Type listType, int length) { var name = "hello"; @@ -260,6 +266,21 @@ public void Given_Type_When_ParameterVisit_Invoked_Then_It_Should_Return_Result( result.Items.Should().NotBeNull(); result.Items.Type.Should().Be(itemType); } + } + + [DataTestMethod] + [DataRow(typeof(FakeList), "fakeList")] + public void Given_ReferencedType_When_ParameterVisit_Invoked_Then_It_Should_Return_Result(Type listType, string referenceId) + { + var result = this._visitor.PayloadVisit(listType, this._strategy, this._options.UseFullName); + + result.Type.Should().BeNull(); + result.Format.Should().BeNull(); + + result.Items.Should().BeNull(); + + result.Reference.Type.Should().Be(ReferenceType.Schema); + result.Reference.Id.Should().Be(referenceId); } [DataTestMethod] @@ -300,6 +321,21 @@ public void Given_Type_When_PayloadVisit_Invoked_Then_It_Should_Return_Result(Ty } } + [DataTestMethod] + [DataRow(typeof(FakeList), "fakeList")] + public void Given_ReferencedType_When_PayloadVisit_Invoked_Then_It_Should_Return_Result(Type listType, string referenceId) + { + var result = this._visitor.PayloadVisit(listType, this._strategy, this._options.UseFullName); + + result.Type.Should().BeNull(); + result.Format.Should().BeNull(); + + result.Items.Should().BeNull(); + + result.Reference.Type.Should().Be(ReferenceType.Schema); + result.Reference.Id.Should().Be(referenceId); + } + [DataTestMethod] [DataRow(typeof(List), typeof(FakeAliasCollectionModel), typeof(FakeAliasSubModel), typeof(FakeSubModel), typeof(FakeDummyModel))] [DataRow(typeof(IList), typeof(FakeAliasCollectionModel), typeof(FakeAliasSubModel), typeof(FakeSubModel), typeof(FakeDummyModel))]