- adds unit test for Cast operation handler & cast path item handler

- adds support for query parameters when in navigation properties

Signed-off-by: Vincent Biret <vibiret@microsoft.com>
This commit is contained in:
Vincent Biret 2021-11-25 14:19:20 -05:00
parent 7eee372958
commit 4ee8e9e5eb
No known key found for this signature in database
GPG key ID: 32426322EDFFB7E3
4 changed files with 228 additions and 6 deletions

View file

@ -7,10 +7,12 @@ using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.OData.Edm;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.OData.Common;
using Microsoft.OpenApi.OData.Edm;
using Microsoft.OpenApi.OData.Generator;
using Microsoft.OpenApi.OData.Vocabulary.Capabilities;
namespace Microsoft.OpenApi.OData.Operation;
@ -28,6 +30,8 @@ internal class ODataTypeCastGetOperationHandler : OperationHandler
/// </summary>
internal ODataSegment LastSecondSegment { get; set; }
private NavigationPropertyRestriction restriction;
private IEdmNavigationProperty navigationProperty;
private IEdmEntityType parentEntityType;
private IEdmEntityType targetEntityType;
private const int SecondLastSegmentIndex = 2;
@ -42,6 +46,30 @@ internal class ODataTypeCastGetOperationHandler : OperationHandler
LastSecondSegment = path.Segments.ElementAt(count - SecondLastSegmentIndex);
parentEntityType = LastSecondSegment.EntityType;
if(LastSecondSegment is ODataNavigationPropertySegment navigationPropertySegment)
{
navigationProperty = navigationPropertySegment.NavigationProperty;
var navigationPropertyPath = string.Join("/",
Path.Segments.Where(s => !(s is ODataKeySegment || s is ODataNavigationSourceSegment
|| s is ODataStreamContentSegment || s is ODataStreamPropertySegment)).Select(e => e.Identifier));
if(path.FirstSegment is ODataNavigationSourceSegment navigationSourceSegment)
{
NavigationRestrictionsType navigation = navigationSourceSegment.NavigationSource switch {
IEdmEntitySet entitySet => Context.Model.GetRecord<NavigationRestrictionsType>(entitySet, CapabilitiesConstants.NavigationRestrictions),
IEdmSingleton singleton => Context.Model.GetRecord<NavigationRestrictionsType>(singleton, CapabilitiesConstants.NavigationRestrictions),
_ => null
};
if (navigation?.RestrictedProperties != null)
{
restriction = navigation.RestrictedProperties.FirstOrDefault(r => r.NavigationProperty != null && r.NavigationProperty == navigationPropertyPath);
}
}
}
//TODO previous segment is a key
//TODO previous segment is a single nav property
//TODO previous segment is an entity set
if(path.Last() is ODataTypeCastSegment oDataTypeCastSegment)
{
targetEntityType = oDataTypeCastSegment.EntityType;
@ -140,7 +168,130 @@ internal class ODataTypeCastGetOperationHandler : OperationHandler
base.SetResponses(operation);
}
//TODO query parameters?
//TODO extensions?
}
//TODO unit tests
/// <inheritdoc/>
protected override void SetTags(OpenApiOperation operation)
{
IList<string> items = new List<string>
{
parentEntityType.Name,
targetEntityType.Name,
};
string name = string.Join(".", items);
OpenApiTag tag = new()
{
Name = name
};
tag.Extensions.Add(Constants.xMsTocType, new OpenApiString("page"));
operation.Tags.Add(tag);
Context.AppendTag(tag);
base.SetTags(operation);
}
/// <inheritdoc/>
protected override void SetParameters(OpenApiOperation operation)
{
base.SetParameters(operation);
if(navigationProperty != null) {
if (navigationProperty.TargetMultiplicity() == EdmMultiplicity.Many)
{
// Need to verify that TopSupported or others should be applied to navigation source.
// So, how about for the navigation property.
OpenApiParameter parameter = Context.CreateTop(navigationProperty);
if (parameter != null)
{
operation.Parameters.Add(parameter);
}
parameter = Context.CreateSkip(navigationProperty);
if (parameter != null)
{
operation.Parameters.Add(parameter);
}
parameter = Context.CreateSearch(navigationProperty);
if (parameter != null)
{
operation.Parameters.Add(parameter);
}
parameter = Context.CreateFilter(navigationProperty);
if (parameter != null)
{
operation.Parameters.Add(parameter);
}
parameter = Context.CreateCount(navigationProperty);
if (parameter != null)
{
operation.Parameters.Add(parameter);
}
parameter = Context.CreateOrderBy(navigationProperty);
if (parameter != null)
{
operation.Parameters.Add(parameter);
}
parameter = Context.CreateSelect(navigationProperty);
if (parameter != null)
{
operation.Parameters.Add(parameter);
}
parameter = Context.CreateExpand(navigationProperty);
if (parameter != null)
{
operation.Parameters.Add(parameter);
}
}
else
{
OpenApiParameter parameter = Context.CreateSelect(navigationProperty);
if (parameter != null)
{
operation.Parameters.Add(parameter);
}
parameter = Context.CreateExpand(navigationProperty);
if (parameter != null)
{
operation.Parameters.Add(parameter);
}
}
}
}
protected override void SetSecurity(OpenApiOperation operation)
{
if (restriction == null || restriction.ReadRestrictions == null)
{
return;
}
ReadRestrictionsBase readBase = restriction.ReadRestrictions;
operation.Security = Context.CreateSecurityRequirements(readBase.Permissions).ToList();
}
protected override void SetExtensions(OpenApiOperation operation)
{
if (Context.Settings.EnablePagination)
{
if (navigationProperty != null && navigationProperty.TargetMultiplicity() == EdmMultiplicity.Many)
{
OpenApiObject extension = new()
{
{ "nextLinkName", new OpenApiString("@odata.nextLink")},
{ "operationName", new OpenApiString(Context.Settings.PageableOperationName)}
};
operation.Extensions.Add(Constants.xMsPageable, extension);
}
}
base.SetExtensions(operation);
}
}

View file

@ -22,5 +22,3 @@ internal class ODataTypeCastPathItemHandler : PathItemHandler
AddOperation(item, OperationType.Get);
}
}
//TODO unit test for the ODataTypeCastPathItemHandler

View file

@ -0,0 +1,72 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
// ------------------------------------------------------------
using System.Linq;
using Microsoft.OData.Edm;
using Microsoft.OpenApi.Extensions;
using Microsoft.OpenApi.OData.Edm;
using Microsoft.OpenApi.OData.PathItem.Tests;
using Microsoft.OpenApi.OData.Tests;
using Xunit;
namespace Microsoft.OpenApi.OData.Operation.Tests;
public class ODataTypeCastGetOperationHandlerTests
{
private readonly ODataTypeCastGetOperationHandler _operationHandler = new ();
[Theory]
[InlineData(true)]
[InlineData(false)]
public void CreateODataTypeCastGetOperationReturnsCorrectOperation(bool enableOperationId)
{
// Arrange
IEdmModel model = EdmModelHelper.TripServiceModel;
OpenApiConvertSettings settings = new()
{
EnableOperationId = enableOperationId
};
ODataContext context = new(model, settings);
IEdmEntitySet people = model.EntityContainer.FindEntitySet("People");
Assert.NotNull(people);
IEdmEntityType person = model.SchemaElements.OfType<IEdmEntityType>().First(c => c.Name == "Person");
IEdmEntityType employee = model.SchemaElements.OfType<IEdmEntityType>().First(c => c.Name == "Employee");
IEdmNavigationProperty navProperty = person.DeclaredNavigationProperties().First(c => c.Name == "Friends");
ODataPath path = new(new ODataNavigationSourceSegment(people),
new ODataKeySegment(people.EntityType()),
new ODataNavigationPropertySegment(navProperty),
new ODataTypeCastSegment(employee));
// Act
var operation = _operationHandler.CreateOperation(context, path);
// Assert
Assert.NotNull(operation);
Assert.Equal("Get the items of type Microsoft.OData.Service.Sample.TrippinInMemory.Models.Employee in the Microsoft.OData.Service.Sample.TrippinInMemory.Models.Person collection", operation.Summary);
Assert.NotNull(operation.Tags);
var tag = Assert.Single(operation.Tags);
Assert.Equal("Person.Employee", tag.Name);
Assert.NotNull(operation.Parameters);
Assert.Equal(9, operation.Parameters.Count);
Assert.Null(operation.RequestBody);
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);
}
}
//TODO test on entity set
//TODO test on cast cast key
//TODO test on cast on single nav property
}

View file

@ -22,6 +22,7 @@ namespace Microsoft.OpenApi.OData.PathItem.Tests
[InlineData(ODataPathKind.MediaEntity, typeof(MediaEntityPathItemHandler))]
[InlineData(ODataPathKind.Metadata, typeof(MetadataPathItemHandler))]
[InlineData(ODataPathKind.DollarCount, typeof(DollarCountPathItemHandler))]
[InlineData(ODataPathKind.TypeCast, typeof(ODataTypeCastPathItemHandler))]
public void GetHandlerReturnsCorrectHandlerType(ODataPathKind pathKind, Type handlerType)
{
// Arrange