|
7 | 7 | using System.Collections.Generic; |
8 | 8 | using System.IO; |
9 | 9 | using System.Linq; |
| 10 | +using System.Threading; |
| 11 | +using System.Threading.Tasks; |
10 | 12 | using System.Xml; |
11 | 13 | using System.Xml.Linq; |
12 | 14 | using Microsoft.OData.Edm; |
13 | 15 | using Microsoft.OData.Edm.Csdl; |
14 | 16 | using Microsoft.OData.Edm.Validation; |
| 17 | +using Microsoft.OpenApi; |
15 | 18 | using Microsoft.OpenApi.OData.Tests; |
16 | 19 | using Xunit; |
17 | 20 |
|
@@ -921,6 +924,83 @@ private static IEdmModel GetNavPropModel(string annotation) |
921 | 924 | return GetEdmModel(template); |
922 | 925 | } |
923 | 926 |
|
| 927 | + [Fact] |
| 928 | + public async Task GetPathsForDerivedTypeDeltaFunctionUsesCorrectReturnType() |
| 929 | + { |
| 930 | + // Arrange – mirrors the Graph scenario: |
| 931 | + // directoryObject (base) has delta with RequiresExplicitBinding |
| 932 | + // servicePrincipal (derived) has its own delta |
| 933 | + // agentIdentity (derived from servicePrincipal) causes servicePrincipal to have derived types |
| 934 | + // Bug: TryAddPath kept the base-type delta for /servicePrincipals/delta() because |
| 935 | + // servicePrincipal has derived types. |
| 936 | + string csdl = @"<edmx:Edmx Version=""4.0"" xmlns:edmx=""http://docs.oasis-open.org/odata/ns/edmx""> |
| 937 | + <edmx:DataServices> |
| 938 | + <Schema Namespace=""NS"" xmlns=""http://docs.oasis-open.org/odata/ns/edm""> |
| 939 | + <EntityType Name=""directoryObject""> |
| 940 | + <Key> |
| 941 | + <PropertyRef Name=""id"" /> |
| 942 | + </Key> |
| 943 | + <Property Name=""id"" Type=""Edm.String"" Nullable=""false"" /> |
| 944 | + </EntityType> |
| 945 | + <EntityType Name=""servicePrincipal"" BaseType=""NS.directoryObject""> |
| 946 | + <Property Name=""appId"" Type=""Edm.String"" /> |
| 947 | + </EntityType> |
| 948 | + <EntityType Name=""agentIdentity"" BaseType=""NS.servicePrincipal""> |
| 949 | + <Property Name=""blueprintId"" Type=""Edm.String"" /> |
| 950 | + </EntityType> |
| 951 | + <Function Name=""delta"" IsBound=""true""> |
| 952 | + <Parameter Name=""bindingParameter"" Type=""Collection(NS.directoryObject)"" /> |
| 953 | + <ReturnType Type=""Collection(NS.directoryObject)"" /> |
| 954 | + <Annotation Term=""Org.OData.Core.V1.RequiresExplicitBinding"" /> |
| 955 | + </Function> |
| 956 | + <Function Name=""delta"" IsBound=""true""> |
| 957 | + <Parameter Name=""bindingParameter"" Type=""Collection(NS.servicePrincipal)"" /> |
| 958 | + <ReturnType Type=""Collection(NS.servicePrincipal)"" /> |
| 959 | + </Function> |
| 960 | + <EntityContainer Name=""Default""> |
| 961 | + <EntitySet Name=""directoryObjects"" EntityType=""NS.directoryObject"" /> |
| 962 | + <EntitySet Name=""servicePrincipals"" EntityType=""NS.servicePrincipal"" /> |
| 963 | + </EntityContainer> |
| 964 | + <Annotations Target=""NS.directoryObject""> |
| 965 | + <Annotation Term=""Org.OData.Core.V1.ExplicitOperationBindings""> |
| 966 | + <Collection> |
| 967 | + <String>NS.delta</String> |
| 968 | + </Collection> |
| 969 | + </Annotation> |
| 970 | + </Annotations> |
| 971 | + </Schema> |
| 972 | + </edmx:DataServices> |
| 973 | +</edmx:Edmx>"; |
| 974 | + |
| 975 | + bool result = CsdlReader.TryParse(XElement.Parse(csdl).CreateReader(), out IEdmModel model, out _); |
| 976 | + Assert.True(result); |
| 977 | + |
| 978 | + var settings = new OpenApiConvertSettings(); |
| 979 | + var doc = model.ConvertToOpenApi(settings); |
| 980 | + |
| 981 | + // Serialize to YAML and verify the response type |
| 982 | + using var stream = new MemoryStream(); |
| 983 | + await doc.SerializeAsync(stream, OpenApiSpecVersion.OpenApi3_1, "yaml", CancellationToken.None); |
| 984 | + stream.Position = 0; |
| 985 | + string yaml = await new StreamReader(stream).ReadToEndAsync(); |
| 986 | + |
| 987 | + // The /servicePrincipals/NS.delta() path should reference servicePrincipal, not directoryObject |
| 988 | + Assert.Contains("/servicePrincipals/NS.delta()", yaml); |
| 989 | + |
| 990 | + // Extract just the path section (up to 'components:' or next top-level key) |
| 991 | + int pathIndex = yaml.IndexOf("/servicePrincipals/NS.delta():"); |
| 992 | + Assert.True(pathIndex >= 0, "Path /servicePrincipals/NS.delta() not found in YAML output"); |
| 993 | + |
| 994 | + int componentsIndex = yaml.IndexOf("\ncomponents:", pathIndex); |
| 995 | + string pathSection = componentsIndex > 0 |
| 996 | + ? yaml.Substring(pathIndex, componentsIndex - pathIndex) |
| 997 | + : yaml.Substring(pathIndex); |
| 998 | + |
| 999 | + // The response schema items $ref should reference servicePrincipal |
| 1000 | + Assert.Contains("'#/components/schemas/NS.servicePrincipal'", pathSection); |
| 1001 | + Assert.DoesNotContain("#/components/schemas/NS.directoryObject", pathSection); |
| 1002 | + } |
| 1003 | + |
924 | 1004 | private static IEdmModel GetEdmModel(string schema) |
925 | 1005 | { |
926 | 1006 | bool parsed = SchemaReader.TryParse(new XmlReader[] { XmlReader.Create(new StringReader(schema)) }, out IEdmModel parsedModel, out IEnumerable<EdmError> errors); |
|
0 commit comments