From 26eabeceae7ed8836e5c82ac6695391711d5831c Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Fri, 26 Nov 2021 14:10:08 -0500 Subject: [PATCH] - adds support for singleton downcast Signed-off-by: Vincent Biret --- .../ODataTypeCastGetOperationHandler.cs | 47 ++++++++++++---- .../ODataTypeCastGetOperationHandlerTests.cs | 54 ++++++++++++++++++- 2 files changed, 91 insertions(+), 10 deletions(-) diff --git a/src/Microsoft.OpenApi.OData.Reader/Operation/ODataTypeCastGetOperationHandler.cs b/src/Microsoft.OpenApi.OData.Reader/Operation/ODataTypeCastGetOperationHandler.cs index 093fc0c..8b35987 100644 --- a/src/Microsoft.OpenApi.OData.Reader/Operation/ODataTypeCastGetOperationHandler.cs +++ b/src/Microsoft.OpenApi.OData.Reader/Operation/ODataTypeCastGetOperationHandler.cs @@ -34,12 +34,14 @@ internal class ODataTypeCastGetOperationHandler : OperationHandler private bool isKeySegment; private bool IsSingleElement { - get => isKeySegment || //TODO add is singleton when implemented + get => isKeySegment || + singleton != null || (navigationProperty != null && !navigationProperty.Type.IsCollection() && entitySet == null); } private NavigationPropertyRestriction restriction; + private IEdmSingleton singleton; private IEdmEntitySet entitySet; private IEdmNavigationProperty navigationProperty; private IEdmEntityType parentEntityType; @@ -60,9 +62,12 @@ internal class ODataTypeCastGetOperationHandler : OperationHandler { SetNavigationPropertyAndRestrictionFromNavigationSegment(navigationPropertySegment, path); } - else if(LastSecondSegment is ODataNavigationSourceSegment sourceSegment && sourceSegment.NavigationSource is IEdmEntitySet) + else if(LastSecondSegment is ODataNavigationSourceSegment sourceSegment) { - SetEntitySetAndRestrictionFromSourceSegment(sourceSegment); + if(sourceSegment.NavigationSource is IEdmEntitySet) + SetEntitySetAndRestrictionFromSourceSegment(sourceSegment); + else if (sourceSegment.NavigationSource is IEdmSingleton) + SetSingletonAndRestrictionFromSourceSegment(sourceSegment); } else if(LastSecondSegment is ODataKeySegment) { @@ -77,7 +82,6 @@ internal class ODataTypeCastGetOperationHandler : OperationHandler SetEntitySetAndRestrictionFromSourceSegment(sourceSegment1); } } - //TODO singleton if(path.Last() is ODataTypeCastSegment oDataTypeCastSegment) { targetEntityType = oDataTypeCastSegment.EntityType; @@ -112,11 +116,26 @@ internal class ODataTypeCastGetOperationHandler : OperationHandler if(sourceSegment.NavigationSource is IEdmEntitySet eSet) { entitySet = eSet; - NavigationRestrictionsType navigation = Context.Model.GetRecord(eSet, CapabilitiesConstants.NavigationRestrictions); - if (navigation?.RestrictedProperties != null) - { - restriction = navigation.RestrictedProperties.FirstOrDefault(r => r.NavigationProperty == null); - } + SetRestrictionFromAnnotable(eSet); + } + } + + private void SetSingletonAndRestrictionFromSourceSegment(ODataNavigationSourceSegment sourceSegment) + { + if(sourceSegment.NavigationSource is IEdmSingleton sTon) + { + singleton = sTon; + SetRestrictionFromAnnotable(sTon); + } + + } + + private void SetRestrictionFromAnnotable(IEdmVocabularyAnnotatable annotable) + { + NavigationRestrictionsType navigation = Context.Model.GetRecord(annotable, CapabilitiesConstants.NavigationRestrictions); + if (navigation?.RestrictedProperties != null) + { + restriction = navigation.RestrictedProperties.FirstOrDefault(r => r.NavigationProperty == null); } } @@ -337,6 +356,16 @@ internal class ODataTypeCastGetOperationHandler : OperationHandler .ForEach(p => operation.Parameters.Add(p)); } } + else if(singleton != null) + { + new OpenApiParameter[] { + Context.CreateSelect(singleton), + Context.CreateExpand(singleton), + } + .Where(x => x != null) + .ToList() + .ForEach(p => operation.Parameters.Add(p)); + } } private IEnumerable GetParametersForAnnotableOfMany(IEdmVocabularyAnnotatable annotable) { diff --git a/test/Microsoft.OpenAPI.OData.Reader.Tests/Operation/ODataTypeCastGetOperationHandlerTests.cs b/test/Microsoft.OpenAPI.OData.Reader.Tests/Operation/ODataTypeCastGetOperationHandlerTests.cs index ae086f7..bdd9408 100644 --- a/test/Microsoft.OpenAPI.OData.Reader.Tests/Operation/ODataTypeCastGetOperationHandlerTests.cs +++ b/test/Microsoft.OpenAPI.OData.Reader.Tests/Operation/ODataTypeCastGetOperationHandlerTests.cs @@ -238,5 +238,57 @@ public class ODataTypeCastGetOperationHandlerTests } Assert.False(operation.Responses["200"].Content["application/json"].Schema.Properties.ContainsKey("value")); } - //TODO test on cast singleton + [Theory] + [InlineData(true, true)] + [InlineData(true, false)] + [InlineData(false, true)] + [InlineData(false, false)] + public void CreateODataTypeCastGetOperationReturnsCorrectOperationForSingleton(bool enableOperationId, bool enablePagination) + {// .../Me/Microsoft.OData.Service.Sample.TrippinInMemory.Models.Employee + // Arrange + IEdmModel model = EdmModelHelper.TripServiceModel; + OpenApiConvertSettings settings = new() + { + EnableOperationId = enableOperationId, + EnablePagination = enablePagination, + }; + ODataContext context = new(model, settings); + IEdmSingleton me = model.EntityContainer.FindSingleton("Me"); + Assert.NotNull(me); + + IEdmEntityType employee = model.SchemaElements.OfType().First(c => c.Name == "Employee"); + ODataPath path = new(new ODataNavigationSourceSegment(me), + new ODataTypeCastSegment(employee)); + + // Act + var operation = _operationHandler.CreateOperation(context, path); + + // Assert + Assert.NotNull(operation); + Assert.Equal("Get the item of type Microsoft.OData.Service.Sample.TrippinInMemory.Models.Person as Microsoft.OData.Service.Sample.TrippinInMemory.Models.Employee", operation.Summary); + Assert.NotNull(operation.Tags); + var tag = Assert.Single(operation.Tags); + Assert.Equal("Person.Employee", tag.Name); + Assert.Empty(tag.Extensions); + + Assert.NotNull(operation.Parameters); + Assert.Equal(2, operation.Parameters.Count); //select, expand + + Assert.Null(operation.RequestBody); + if(enablePagination) + Assert.Empty(operation.Extensions); + + Assert.Equal(2, operation.Responses.Count); + Assert.Equal(new string[] { "200", "default" }, operation.Responses.Select(e => e.Key)); + + if (enableOperationId) + { + Assert.Equal("Get.Microsoft.OData.Service.Sample.TrippinInMemory.Models.Person.As.Microsoft.OData.Service.Sample.TrippinInMemory.Models.Employee", operation.OperationId); + } + else + { + Assert.Null(operation.OperationId); + } + Assert.False(operation.Responses["200"].Content["application/json"].Schema.Properties.ContainsKey("value")); + } }