refactor the operation handler

This commit is contained in:
Sam Xu 2018-08-28 15:47:44 -07:00
parent 009ca7fef3
commit 0f8b01df3e
32 changed files with 1126 additions and 162 deletions

View file

@ -0,0 +1,32 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
// ------------------------------------------------------------
using System.Security.Cryptography;
using System.Text;
namespace Microsoft.OpenApi.OData.Common
{
internal static class CryptographyExtensions
{
/// <summary>
/// Calculates the MD5 hash for the given string.
/// </summary>
/// <returns>A 32 char long hash.</returns>
public static string GetHashMd5(this string input)
{
Utils.CheckArgumentNull(input, nameof(input));
var hasher = new MD5CryptoServiceProvider();
var inputBytes = Encoding.UTF8.GetBytes(input);
var hashBytes = hasher.ComputeHash(inputBytes);
var hash = new StringBuilder();
foreach (var b in hashBytes)
{
hash.Append(string.Format("{0:x2}", b));
}
return hash.ToString();
}
}
}

View file

@ -3,6 +3,9 @@
// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
// ------------------------------------------------------------
using System;
using System.Linq;
using System.Text;
using Microsoft.OData.Edm;
using Microsoft.OpenApi.OData.Common;
@ -31,6 +34,40 @@ namespace Microsoft.OpenApi.OData.Edm
public override ODataSegmentKind Kind => ODataSegmentKind.OperationImport;
/// <inheritdoc />
public override string GetPathItemName(OpenApiConvertSettings settings) => OperationImport.Name;
public override string GetPathItemName(OpenApiConvertSettings settings)
{
Utils.CheckArgumentNull(settings, nameof(settings));
if (OperationImport.IsFunctionImport())
{
return FunctionImportName(OperationImport as IEdmFunctionImport, settings);
}
return OperationImport.Name;
}
private string FunctionImportName(IEdmFunctionImport functionImport, OpenApiConvertSettings settings)
{
StringBuilder functionName = new StringBuilder(functionImport.Name);
functionName.Append("(");
// Structured or collection-valued parameters are represented as a parameter alias in the path template
// and the parameters array contains a Parameter Object for the parameter alias as a query option of type string.
IEdmFunction function = functionImport.Function;
functionName.Append(String.Join(",", function.Parameters.Select(p =>
{
if (p.Type.IsStructured() || p.Type.IsCollection())
{
return p.Name + "=@" + p.Name;
}
else
{
return p.Name + "={" + p.Name + "}";
}
})));
functionName.Append(")");
return functionName.ToString();
}
}
}

View file

@ -36,6 +36,8 @@ namespace Microsoft.OpenApi.OData.Edm
/// <inheritdoc />
public override string GetPathItemName(OpenApiConvertSettings settings)
{
Utils.CheckArgumentNull(settings, nameof(settings));
if (Operation.IsFunction())
{
return FunctionName(Operation as IEdmFunction, settings);

View file

@ -143,6 +143,62 @@ namespace Microsoft.OpenApi.OData.Generator
return parameters;
}
/// <summary>
/// Create key parameters for the <see cref="ODataKeySegment"/>.
/// </summary>
/// <param name="keySegment">The key segment.</param>
/// <returns>The created the list of <see cref="OpenApiParameter"/>.</returns>
public static IList<OpenApiParameter> CreateKeyParameters(this ODataContext context, ODataKeySegment keySegment)
{
Utils.CheckArgumentNull(context, nameof(context));
Utils.CheckArgumentNull(keySegment, nameof(keySegment));
IList<OpenApiParameter> parameters = new List<OpenApiParameter>();
IEdmEntityType entityType = keySegment.EntityType;
IList<IEdmStructuralProperty> keys = entityType.Key().ToList();
if (keys.Count() == 1)
{
string keyName = keys.First().Name;
if (context.Settings.PrefixEntityTypeNameBeforeKey)
{
keyName = entityType.Name + "-" + keys.First().Name;
}
OpenApiParameter parameter = new OpenApiParameter
{
Name = keyName,
In = ParameterLocation.Path,
Required = true,
Description = "key: " + keyName,
Schema = context.CreateEdmTypeSchema(keys.First().Type)
};
parameter.Extensions.Add(Constants.xMsKeyType, new OpenApiString(entityType.Name));
parameters.Add(parameter);
}
else
{
// append key parameter
foreach (var keyProperty in entityType.Key())
{
OpenApiParameter parameter = new OpenApiParameter
{
Name = keyProperty.Name,
In = ParameterLocation.Path,
Required = true,
Description = "key: " + keyProperty.Name,
Schema = context.CreateEdmTypeSchema(keyProperty.Type)
};
parameter.Extensions.Add(Constants.xMsKeyType, new OpenApiString(entityType.Name));
parameters.Add(parameter);
}
}
return parameters;
}
/// <summary>
/// Create the $top parameter.
/// </summary>

View file

@ -35,12 +35,24 @@ namespace Microsoft.OpenApi.OData.Operation
/// <inheritdoc/>
protected override void SetBasicInfo(OpenApiOperation operation)
{
operation.Summary = "Invoke " + (EdmOperationImport.IsActionImport() ? "action " : "function ") + EdmOperationImport.Name;
operation.Summary = "Invoke " + (EdmOperationImport.IsActionImport() ? "actionImport " : "functionImport ") + EdmOperationImport.Name;
if (Context.Settings.OperationId)
{
string key = "OperationImport." + EdmOperationImport.Name;
operation.OperationId += "OperationImport." + Context.GetIndex(key) + "-" + Utils.UpperFirstChar(EdmOperationImport.Name);
if (EdmOperationImport.IsActionImport())
{
operation.OperationId = "OperationImport." + EdmOperationImport.Name;
}
else
{
ODataOperationImportSegment operationImportSegment = Path.LastSegment as ODataOperationImportSegment;
string pathItemName = operationImportSegment.GetPathItemName(Context.Settings);
string md5 = pathItemName.GetHashMd5();
operation.OperationId = "OperationImport." + EdmOperationImport.Name + "." + md5.Substring(8);
}
}
}

View file

@ -5,6 +5,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.OData.Edm;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
@ -57,9 +58,32 @@ namespace Microsoft.OpenApi.OData.Operation
// OperationId
if (Context.Settings.OperationId)
{
string key = NavigationSource.Name + "-" + Utils.UpperFirstChar(EdmOperation.Name);
int index = Context.GetIndex(key);
operation.OperationId = NavigationSource.Name + "." + index + "-" + Utils.UpperFirstChar(EdmOperation.Name);
StringBuilder operationId = new StringBuilder(NavigationSource.Name);
if (HasTypeCast)
{
ODataTypeCastSegment typeCast = Path.Segments.FirstOrDefault(s => s is ODataTypeCastSegment) as ODataTypeCastSegment;
operationId.Append(".");
operationId.Append(typeCast.EntityType.Name);
}
else
{
operationId.Append(".");
operationId.Append(NavigationSource.EntityType().Name);
}
operationId.Append(".");
operationId.Append(EdmOperation.Name);
if (EdmOperation.IsAction())
{
operation.OperationId = operationId.ToString();
}
else
{
ODataOperationSegment operationSegment = Path.LastSegment as ODataOperationSegment;
string pathItemName = operationSegment.GetPathItemName(Context.Settings);
string md5 = pathItemName.GetHashMd5();
operation.OperationId = operationId.Append(".").Append(md5.Substring(8)).ToString();
}
}
}
@ -81,19 +105,6 @@ namespace Microsoft.OpenApi.OData.Operation
protected override void SetParameters(OpenApiOperation operation)
{
base.SetParameters(operation);
/*
IEdmSingleton singleton = NavigationSource as IEdmSingleton;
if (singleton == null && EdmOperation.IsBound)
{
IEdmOperationParameter bindingParameter = EdmOperation.Parameters.FirstOrDefault();
if (bindingParameter != null &&
!bindingParameter.Type.IsCollection() && // bound to a single entity
bindingParameter.Type.IsEntity())
{
operation.Parameters = Context.CreateKeyParameters(bindingParameter
.Type.AsEntity().EntityDefinition());
}
}*/
if (EdmOperation.IsFunction())
{

View file

@ -36,11 +36,6 @@ namespace Microsoft.OpenApi.OData.Operation
prefix = "List";
}
/*
string key = NavigationSource.Name + "." + NavigationProperty.Name + "-" + prefix + Utils.UpperFirstChar(NavigationProperty.ToEntityType().Name);
int index = Context.GetIndex(key);
operation.OperationId = NavigationSource.Name + "." + NavigationProperty.Name + index + "-" + prefix + Utils.UpperFirstChar(NavigationProperty.ToEntityType().Name);
*/
operation.OperationId = GetOperationId(prefix);
}
}
@ -88,10 +83,10 @@ namespace Microsoft.OpenApi.OData.Operation
operation.Parameters = new List<OpenApiParameter>();
}
if (NavigationProperty.TargetMultiplicity() == EdmMultiplicity.Many)
if (!LastSegmentIsKeySegment && NavigationProperty.TargetMultiplicity() == EdmMultiplicity.Many)
{
// The parameters array contains Parameter Objects for system query options allowed for this entity set,
// and it does not list system query options not allowed for this entity set.
// Need to verify that TopSupported or others should be applyed to navigaiton source.
// So, how about for the navigation property.
OpenApiParameter parameter = Context.CreateTop(NavigationProperty);
if (parameter != null)
{

View file

@ -28,6 +28,11 @@ namespace Microsoft.OpenApi.OData.Operation
/// </summary>
protected IEdmNavigationSource NavigationSource { get; private set; }
/// <summary>
/// Gets the navigation path.
/// </summary>
protected string NavigationPropertyPath { get; private set; }
/// <summary>
/// Gets a bool value indicating whether the last segment is a key segment.
/// </summary>
@ -48,6 +53,9 @@ namespace Microsoft.OpenApi.OData.Operation
}
NavigationProperty = npSegment.NavigationProperty;
NavigationPropertyPath = string.Join("/",
path.Segments.OfType<ODataNavigationPropertySegment>().Select(p => p.NavigationProperty.Name));
base.Initialize(context, path);
}
@ -87,7 +95,6 @@ namespace Microsoft.OpenApi.OData.Operation
string name = string.Join(".", items);
OpenApiTag tag = new OpenApiTag
{
// Name = NavigationSource.Name + "." + NavigationProperty.ToEntityType().Name,
Name = name
};
tag.Extensions.Add(Constants.xMsTocType, new OpenApiString("page"));
@ -109,16 +116,6 @@ namespace Microsoft.OpenApi.OData.Operation
ODataNavigationPropertySegment npSegment = segment as ODataNavigationPropertySegment;
if (npSegment != null)
{
/*
if (npSegment.NavigationProperty == NavigationProperty)
{
items.Add(prefix + Utils.UpperFirstChar(NavigationProperty.ToEntityType().Name));
break;
}
else
{
items.Add(segment.Name);
}*/
if (segment == lastpath)
{
items.Add(prefix + Utils.UpperFirstChar(npSegment.NavigationProperty.Name));

View file

@ -31,10 +31,6 @@ namespace Microsoft.OpenApi.OData.Operation
if (Context.Settings.OperationId)
{
string prefix = "Update";
//string key = NavigationSource.Name + "." + NavigationProperty.Name + "-" + prefix + Utils.UpperFirstChar(NavigationProperty.ToEntityType().Name);
// int index = Context.GetIndex(key);
// operation.OperationId = NavigationSource.Name + "." + NavigationProperty.Name + index + "-" + prefix + Utils.UpperFirstChar(NavigationProperty.ToEntityType().Name);
operation.OperationId = GetOperationId(prefix);
}
}

View file

@ -31,11 +31,6 @@ namespace Microsoft.OpenApi.OData.Operation
if (Context.Settings.OperationId)
{
string prefix = "Create";
/*
string key = NavigationSource.Name + "." + NavigationProperty.Name + "-" + prefix + Utils.UpperFirstChar(NavigationProperty.ToEntityType().Name);
int index = Context.GetIndex(key);
operation.OperationId = NavigationSource.Name + "." + NavigationProperty.Name + index + "-" + prefix + Utils.UpperFirstChar(NavigationProperty.ToEntityType().Name);
*/
operation.OperationId = GetOperationId(prefix);
}
}

View file

@ -112,7 +112,7 @@ namespace Microsoft.OpenApi.OData.Operation
{
foreach (ODataKeySegment keySegment in Path.OfType<ODataKeySegment>())
{
foreach (var p in Context.CreateKeyParameters(keySegment.EntityType))
foreach (var p in Context.CreateKeyParameters(keySegment))
{
operation.Parameters.Add(p);
}

View file

@ -46,7 +46,6 @@ namespace Microsoft.OpenApi.OData.Operation
base.SetParameters(operation);
operation.Parameters = new List<OpenApiParameter>();
IEdmEntityType entityType = Singleton.EntityType();
// $select
OpenApiParameter parameter = Context.CreateSelect(Singleton);

View file

@ -38,7 +38,9 @@ namespace Microsoft.OpenApi.OData.Operation
{
Name = Singleton.Name + "." + Singleton.EntityType().Name,
};
tag.Extensions.Add(Constants.xMsTocType, new OpenApiString("page"));
operation.Tags.Add(tag);
Context.AppendTag(tag);

View file

@ -97,16 +97,16 @@ namespace Microsoft.OpenApi.OData.Reader.Capabilities.Tests
private static IEdmModel GetEdmModel(string template, EdmVocabularyAnnotationSerializationLocation location)
{
string countAnnotation = @"<Annotation Term=""Org.OData.Capabilities.V1.TopSupported"" Bool=""false"" />";
string topAnnotation = @"<Annotation Term=""Org.OData.Capabilities.V1.TopSupported"" Bool=""false"" />";
if (location == EdmVocabularyAnnotationSerializationLocation.OutOfLine)
{
countAnnotation = string.Format(template, countAnnotation);
return CapabilitiesModelHelper.GetEdmModelOutline(countAnnotation);
topAnnotation = string.Format(template, topAnnotation);
return CapabilitiesModelHelper.GetEdmModelOutline(topAnnotation);
}
else
{
return CapabilitiesModelHelper.GetEdmModelTypeInline(countAnnotation);
return CapabilitiesModelHelper.GetEdmModelTypeInline(topAnnotation);
}
}
}

View file

@ -11,13 +11,19 @@ namespace Microsoft.OpenApi.OData.Edm.Tests
{
public class ODataOperationImportSegmentTests
{
private IEdmOperationImport _operationImport;
private IEdmOperationImport _actionImport;
private IEdmOperationImport _functionImport;
public ODataOperationImportSegmentTests()
{
IEdmEntityContainer container = new EdmEntityContainer("NS", "default");
IEdmAction action = new EdmAction("NS", "MyAction", null);
_operationImport = new EdmActionImport(container, "MyAction", action);
_actionImport = new EdmActionImport(container, "MyAction", action);
EdmFunction function = new EdmFunction("NS", "MyFunction", EdmCoreModel.Instance.GetString(false), false, null, false);
function.AddParameter("firstName", EdmCoreModel.Instance.GetString(false));
function.AddParameter("lastName", EdmCoreModel.Instance.GetString(false));
_functionImport = new EdmFunctionImport(container, "MyFunction", function);
}
[Fact]
@ -31,17 +37,17 @@ namespace Microsoft.OpenApi.OData.Edm.Tests
public void CtorSetOperationImportProperty()
{
// Arrange & Act
var segment = new ODataOperationImportSegment(_operationImport);
var segment = new ODataOperationImportSegment(_actionImport);
// Assert
Assert.Same(_operationImport, segment.OperationImport);
Assert.Same(_actionImport, segment.OperationImport);
}
[Fact]
public void GetEntityTypeThrowsNotImplementedException()
{
// Arrange & Act
var segment = new ODataOperationImportSegment(_operationImport);
var segment = new ODataOperationImportSegment(_actionImport);
// Assert
Assert.Throws<NotImplementedException>(() => segment.EntityType);
@ -51,20 +57,31 @@ namespace Microsoft.OpenApi.OData.Edm.Tests
public void KindPropertyReturnsOperationImportEnumMember()
{
// Arrange & Act
var segment = new ODataOperationImportSegment(_operationImport);
var segment = new ODataOperationImportSegment(_actionImport);
// Assert
Assert.Equal(ODataSegmentKind.OperationImport, segment.Kind);
}
[Fact]
public void GetPathItemNameReturnsCorrectOperationImportLiteral()
public void GetPathItemNameReturnsCorrectActionImportLiteral()
{
// Arrange & Act
var segment = new ODataOperationImportSegment(_operationImport);
var segment = new ODataOperationImportSegment(_actionImport);
// Assert
Assert.Equal("MyAction", segment.GetPathItemName(new OpenApiConvertSettings()));
}
[Fact]
public void GetPathItemNameReturnsCorrectFunctionImportLiteral()
{
// Arrange & Act
var segment = new ODataOperationImportSegment(_functionImport);
// Assert
Assert.Equal("MyFunction(firstName={firstName},lastName={lastName})",
segment.GetPathItemName(new OpenApiConvertSettings()));
}
}
}

View file

@ -31,7 +31,7 @@ namespace Microsoft.OpenApi.OData.Operation.Tests
// Assert
Assert.NotNull(operation);
Assert.Equal("Invoke action ResetDataSource", operation.Summary);
Assert.Equal("Invoke actionImport ResetDataSource", operation.Summary);
Assert.NotNull(operation.Tags);
Assert.NotNull(operation.Parameters);
@ -42,5 +42,47 @@ namespace Microsoft.OpenApi.OData.Operation.Tests
Assert.Equal(2, operation.Responses.Count);
Assert.Equal(new string[] { "204", "default" }, operation.Responses.Select(e => e.Key));
}
[Theory]
[InlineData(true)]
[InlineData(false)]
public void CreateOperationForEdmActionReturnsCorrectOperationId(bool enableOperationId)
{
// Arrange
EdmModel model = new EdmModel();
EdmEntityType customer = new EdmEntityType("NS", "Customer");
customer.AddKeys(customer.AddStructuralProperty("ID", EdmPrimitiveTypeKind.Int32));
model.AddElement(customer);
EdmAction action = new EdmAction("NS", "MyAction", EdmCoreModel.Instance.GetString(false), false, null);
action.AddParameter("param", EdmCoreModel.Instance.GetString(false));
model.AddElement(action);
EdmEntityContainer container = new EdmEntityContainer("NS", "Default");
EdmEntitySet customers = new EdmEntitySet(container, "Customers", customer);
EdmActionImport actionImport = new EdmActionImport(container, "MyAction", action);
model.AddElement(container);
OpenApiConvertSettings settings = new OpenApiConvertSettings
{
OperationId = enableOperationId
};
ODataContext context = new ODataContext(model, settings);
ODataPath path = new ODataPath(new ODataOperationImportSegment(actionImport));
// Act
var operation = _operationHandler.CreateOperation(context, path);
// Assert
Assert.NotNull(operation);
if (enableOperationId)
{
Assert.Equal("OperationImport.MyAction", operation.OperationId);
}
else
{
Assert.Null(operation.OperationId);
}
}
}
}

View file

@ -48,5 +48,96 @@ namespace Microsoft.OpenApi.OData.Operation.Tests
Assert.Equal(2, operation.Responses.Count);
Assert.Equal(new string[] { "204", "default" }, operation.Responses.Select(e => e.Key));
}
[Theory]
[InlineData(true)]
[InlineData(false)]
public void CreateOperationForEdmActionReturnsCorrectOperationId(bool enableOperationId)
{
// Arrange
EdmModel model = new EdmModel();
EdmEntityType customer = new EdmEntityType("NS", "Customer");
customer.AddKeys(customer.AddStructuralProperty("ID", EdmPrimitiveTypeKind.Int32));
model.AddElement(customer);
EdmAction action = new EdmAction("NS", "MyAction", EdmCoreModel.Instance.GetString(false), true, null);
action.AddParameter("entity", new EdmEntityTypeReference(customer, false));
action.AddParameter("param", EdmCoreModel.Instance.GetString(false));
model.AddElement(action);
EdmEntityContainer container = new EdmEntityContainer("NS", "Default");
EdmEntitySet customers = new EdmEntitySet(container, "Customers", customer);
model.AddElement(container);
OpenApiConvertSettings settings = new OpenApiConvertSettings
{
OperationId = enableOperationId
};
ODataContext context = new ODataContext(model, settings);
ODataPath path = new ODataPath(new ODataNavigationSourceSegment(customers),
new ODataKeySegment(customer),
new ODataOperationSegment(action));
// Act
var operation = _operationHandler.CreateOperation(context, path);
// Assert
Assert.NotNull(operation);
if (enableOperationId)
{
Assert.Equal("Customers.Customer.MyAction", operation.OperationId);
}
else
{
Assert.Null(operation.OperationId);
}
}
[Theory]
[InlineData(true)]
[InlineData(false)]
public void CreateOperationForEdmActionWithTypeCastReturnsCorrectOperationId(bool enableOperationId)
{
// Arrange
EdmModel model = new EdmModel();
EdmEntityType customer = new EdmEntityType("NS", "Customer");
customer.AddKeys(customer.AddStructuralProperty("ID", EdmPrimitiveTypeKind.Int32));
model.AddElement(customer);
EdmEntityType vipCustomer = new EdmEntityType("NS", "VipCustomer", customer);
model.AddElement(vipCustomer);
EdmAction action = new EdmAction("NS", "MyAction", EdmCoreModel.Instance.GetString(false), true, null);
action.AddParameter("entity", new EdmEntityTypeReference(vipCustomer, false));
action.AddParameter("param", EdmCoreModel.Instance.GetString(false));
model.AddElement(action);
EdmEntityContainer container = new EdmEntityContainer("NS", "Default");
EdmEntitySet customers = new EdmEntitySet(container, "Customers", customer);
model.AddElement(container);
OpenApiConvertSettings settings = new OpenApiConvertSettings
{
OperationId = enableOperationId
};
ODataContext context = new ODataContext(model, settings);
ODataPath path = new ODataPath(new ODataNavigationSourceSegment(customers),
new ODataKeySegment(customer),
new ODataTypeCastSegment(vipCustomer),
new ODataOperationSegment(action));
// Act
var operation = _operationHandler.CreateOperation(context, path);
// Assert
Assert.NotNull(operation);
if (enableOperationId)
{
Assert.Equal("Customers.VipCustomer.MyAction", operation.OperationId);
}
else
{
Assert.Null(operation.OperationId);
}
}
}
}

View file

@ -30,7 +30,7 @@ namespace Microsoft.OpenApi.OData.Operation.Tests
// Assert
Assert.NotNull(operation);
Assert.Equal("Invoke function GetPersonWithMostFriends", operation.Summary);
Assert.Equal("Invoke functionImport GetPersonWithMostFriends", operation.Summary);
Assert.NotNull(operation.Tags);
var tag = Assert.Single(operation.Tags);
Assert.Equal("People", tag.Name);
@ -43,5 +43,47 @@ namespace Microsoft.OpenApi.OData.Operation.Tests
Assert.Equal(2, operation.Responses.Count);
Assert.Equal(new string[] { "200", "default" }, operation.Responses.Select(e => e.Key));
}
[Theory]
[InlineData(true)]
[InlineData(false)]
public void CreateOperationForEdmFunctionImportReturnsCorrectOperationId(bool enableOperationId)
{
// Arrange
EdmModel model = new EdmModel();
EdmEntityType customer = new EdmEntityType("NS", "Customer");
customer.AddKeys(customer.AddStructuralProperty("ID", EdmPrimitiveTypeKind.Int32));
model.AddElement(customer);
EdmFunction function = new EdmFunction("NS", "MyFunction", EdmCoreModel.Instance.GetString(false), false, null, false);
function.AddParameter("entity", new EdmEntityTypeReference(customer, false));
function.AddParameter("param", EdmCoreModel.Instance.GetString(false));
model.AddElement(function);
EdmEntityContainer container = new EdmEntityContainer("NS", "Default");
EdmFunctionImport functionImport = new EdmFunctionImport(container, "MyFunction", function);
model.AddElement(container);
OpenApiConvertSettings settings = new OpenApiConvertSettings
{
OperationId = enableOperationId
};
ODataContext context = new ODataContext(model, settings);
ODataPath path = new ODataPath(new ODataOperationImportSegment(functionImport));
// Act
var operation = _operationHandler.CreateOperation(context, path);
// Assert
Assert.NotNull(operation);
if (enableOperationId)
{
Assert.Equal("OperationImport.MyFunction.a5ea52712c5e17e3bd081e4f", operation.OperationId);
}
else
{
Assert.Null(operation.OperationId);
}
}
}
}

View file

@ -3,8 +3,12 @@
// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
// ------------------------------------------------------------
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Microsoft.OData.Edm;
using Microsoft.OData.Edm.Csdl;
using Microsoft.OData.Edm.Validation;
using Microsoft.OpenApi.OData.Edm;
using Microsoft.OpenApi.OData.Tests;
using Xunit;
@ -48,5 +52,96 @@ namespace Microsoft.OpenApi.OData.Operation.Tests
Assert.Equal(2, operation.Responses.Count);
Assert.Equal(new string[] { "200", "default" }, operation.Responses.Select(e => e.Key));
}
[Theory]
[InlineData(true)]
[InlineData(false)]
public void CreateOperationForEdmFunctionReturnsCorrectOperationId(bool enableOperationId)
{
// Arrange
EdmModel model = new EdmModel();
EdmEntityType customer = new EdmEntityType("NS", "Customer");
customer.AddKeys(customer.AddStructuralProperty("ID", EdmPrimitiveTypeKind.Int32));
model.AddElement(customer);
EdmFunction function = new EdmFunction("NS", "MyFunction", EdmCoreModel.Instance.GetString(false), true, null, false);
function.AddParameter("entity", new EdmEntityTypeReference(customer, false));
function.AddParameter("param", EdmCoreModel.Instance.GetString(false));
model.AddElement(function);
EdmEntityContainer container = new EdmEntityContainer("NS", "Default");
EdmEntitySet customers = new EdmEntitySet(container, "Customers", customer);
model.AddElement(container);
OpenApiConvertSettings settings = new OpenApiConvertSettings
{
OperationId = enableOperationId
};
ODataContext context = new ODataContext(model, settings);
ODataPath path = new ODataPath(new ODataNavigationSourceSegment(customers),
new ODataKeySegment(customer),
new ODataOperationSegment(function));
// Act
var operation = _operationHandler.CreateOperation(context, path);
// Assert
Assert.NotNull(operation);
if (enableOperationId)
{
Assert.Equal("Customers.Customer.MyFunction.2c7bd5b91474cb963a16e394", operation.OperationId);
}
else
{
Assert.Null(operation.OperationId);
}
}
[Theory]
[InlineData(true)]
[InlineData(false)]
public void CreateOperationForEdmFunctionWithTypeCastReturnsCorrectOperationId(bool enableOperationId)
{
// Arrange
EdmModel model = new EdmModel();
EdmEntityType customer = new EdmEntityType("NS", "Customer");
customer.AddKeys(customer.AddStructuralProperty("ID", EdmPrimitiveTypeKind.Int32));
model.AddElement(customer);
EdmEntityType vipCustomer = new EdmEntityType("NS", "VipCustomer", customer);
model.AddElement(vipCustomer);
EdmFunction function = new EdmFunction("NS", "MyFunction", EdmCoreModel.Instance.GetString(false), true, null, false);
function.AddParameter("entity", new EdmEntityTypeReference(vipCustomer, false));
function.AddParameter("param", EdmCoreModel.Instance.GetString(false));
model.AddElement(function);
EdmEntityContainer container = new EdmEntityContainer("NS", "Default");
EdmEntitySet customers = new EdmEntitySet(container, "Customers", customer);
model.AddElement(container);
OpenApiConvertSettings settings = new OpenApiConvertSettings
{
OperationId = enableOperationId
};
ODataContext context = new ODataContext(model, settings);
ODataPath path = new ODataPath(new ODataNavigationSourceSegment(customers),
new ODataKeySegment(customer),
new ODataTypeCastSegment(vipCustomer),
new ODataOperationSegment(function));
// Act
var operation = _operationHandler.CreateOperation(context, path);
// Assert
Assert.NotNull(operation);
if (enableOperationId)
{
Assert.Equal("Customers.VipCustomer.MyFunction.2c7bd5b91474cb963a16e394", operation.OperationId);
}
else
{
Assert.Null(operation.OperationId);
}
}
}
}

View file

@ -6,7 +6,6 @@
using System.Linq;
using Microsoft.OData.Edm;
using Microsoft.OpenApi.OData.Edm;
using Microsoft.OpenApi.OData.Tests;
using Xunit;
namespace Microsoft.OpenApi.OData.Operation.Tests
@ -15,13 +14,19 @@ namespace Microsoft.OpenApi.OData.Operation.Tests
{
private EntityDeleteOperationHandler _operationHandler = new EntityDeleteOperationHandler();
[Fact]
public void CreateEntityDeleteOperationReturnsCorrectOperation()
[Theory]
[InlineData(true)]
[InlineData(false)]
public void CreateEntityDeleteOperationReturnsCorrectOperation(bool enableOperationId)
{
// Arrange
IEdmModel model = EdmModelHelper.BasicEdmModel;
IEdmEntitySet entitySet = model.EntityContainer.FindEntitySet("People");
ODataContext context = new ODataContext(model);
IEdmModel model = EntitySetGetOperationHandlerTests.GetEdmModel("");
IEdmEntitySet entitySet = model.EntityContainer.FindEntitySet("Customers");
OpenApiConvertSettings settings = new OpenApiConvertSettings
{
OperationId = enableOperationId
};
ODataContext context = new ODataContext(model, settings);
ODataPath path = new ODataPath(new ODataNavigationSourceSegment(entitySet), new ODataKeySegment(entitySet.EntityType()));
// Act
@ -29,10 +34,10 @@ namespace Microsoft.OpenApi.OData.Operation.Tests
// Assert
Assert.NotNull(delete);
Assert.Equal("Delete entity from People", delete.Summary);
Assert.Equal("Delete entity from Customers", delete.Summary);
Assert.NotNull(delete.Tags);
var tag = Assert.Single(delete.Tags);
Assert.Equal("People.Person", tag.Name);
Assert.Equal("Customers.Customer", tag.Name);
Assert.NotNull(delete.Parameters);
Assert.Equal(2, delete.Parameters.Count);
@ -42,6 +47,15 @@ namespace Microsoft.OpenApi.OData.Operation.Tests
Assert.NotNull(delete.Responses);
Assert.Equal(2, delete.Responses.Count);
Assert.Equal(new[] { "204", "default" }, delete.Responses.Select(r => r.Key));
if (enableOperationId)
{
Assert.Equal("Customers.Customer.DeleteCustomer", delete.OperationId);
}
else
{
Assert.Null(delete.OperationId);
}
}
}
}

View file

@ -3,10 +3,10 @@
// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
// ------------------------------------------------------------
using System;
using System.Linq;
using Microsoft.OData.Edm;
using Microsoft.OpenApi.OData.Edm;
using Microsoft.OpenApi.OData.Tests;
using Xunit;
namespace Microsoft.OpenApi.OData.Operation.Tests
@ -15,13 +15,19 @@ namespace Microsoft.OpenApi.OData.Operation.Tests
{
private EntityGetOperationHandler _operationHandler = new EntityGetOperationHandler();
[Fact]
public void CreateEntityGetOperationReturnsCorrectOperation()
[Theory]
[InlineData(true)]
[InlineData(false)]
public void CreateEntityGetOperationReturnsCorrectOperation(bool enableOperationId)
{
// Arrange
IEdmModel model = EdmModelHelper.BasicEdmModel;
IEdmEntitySet entitySet = model.EntityContainer.FindEntitySet("People");
ODataContext context = new ODataContext(model);
IEdmModel model = EntitySetGetOperationHandlerTests.GetEdmModel("");
IEdmEntitySet entitySet = model.EntityContainer.FindEntitySet("Customers");
OpenApiConvertSettings settings = new OpenApiConvertSettings
{
OperationId = enableOperationId
};
ODataContext context = new ODataContext(model, settings);
ODataPath path = new ODataPath(new ODataNavigationSourceSegment(entitySet), new ODataKeySegment(entitySet.EntityType()));
// Act
@ -29,10 +35,10 @@ namespace Microsoft.OpenApi.OData.Operation.Tests
// Assert
Assert.NotNull(get);
Assert.Equal("Get entity from People by key", get.Summary);
Assert.Equal("Get entity from Customers by key", get.Summary);
Assert.NotNull(get.Tags);
var tag = Assert.Single(get.Tags);
Assert.Equal("People.Person", tag.Name);
Assert.Equal("Customers.Customer", tag.Name);
Assert.NotNull(get.Parameters);
Assert.Equal(3, get.Parameters.Count);
@ -41,6 +47,85 @@ namespace Microsoft.OpenApi.OData.Operation.Tests
Assert.NotNull(get.Responses);
Assert.Equal(2, get.Responses.Count);
Assert.Equal(new[] { "200", "default" }, get.Responses.Select(r => r.Key));
if (enableOperationId)
{
Assert.Equal("Customers.Customer.GetCustomer", get.OperationId);
}
else
{
Assert.Null(get.OperationId);
}
}
[Theory]
[InlineData(false, true)]
[InlineData(false, false)]
[InlineData(true, true)]
[InlineData(true, false)]
public void CreateEntityGetOperationReturnsParameterForExpandRestrictions(bool hasRestriction, bool expandable)
{
// Arrange
string annotation = String.Format(@"
<Annotation Term=""Org.OData.Capabilities.V1.ExpandRestrictions"">
<Record>
<PropertyValue Property=""Expandable"" Bool=""{0}"" />
</Record>
</Annotation>", expandable);
// Act & Assert
VerifyParameter(annotation, hasRestriction, expandable, "$expand");
}
[Theory]
[InlineData(false, "Recursive")]
[InlineData(false, "Single")]
[InlineData(false, "None")]
[InlineData(true, "Recursive")]
[InlineData(true, "Single")]
[InlineData(true, "None")]
public void CreateEntityGetOperationReturnsParameterForNavigationRestrictions(bool hasRestriction, string navigability)
{
// Arrange
string annotation = String.Format(@"
<Annotation Term=""Org.OData.Capabilities.V1.NavigationRestrictions"">
<Record>
<PropertyValue Property=""Navigability"">
<EnumMember>Org.OData.Capabilities.V1.NavigationType/{0}</EnumMember >
</PropertyValue>
</Record>
</Annotation>", navigability);
// Act & Assert
VerifyParameter(annotation, hasRestriction, navigability == "None" ? false : true, "$select");
}
private void VerifyParameter(string annotation, bool hasRestriction, bool supported, string queryOption)
{
// Arrange
IEdmModel model = EntitySetGetOperationHandlerTests.GetEdmModel(hasRestriction ? annotation : "");
ODataContext context = new ODataContext(model);
IEdmEntitySet customers = model.EntityContainer.FindEntitySet("Customers");
Assert.NotNull(customers); // guard
ODataPath path = new ODataPath(new ODataNavigationSourceSegment(customers), new ODataKeySegment(customers.EntityType()));
// Act
var get = _operationHandler.CreateOperation(context, path);
// Assert
Assert.NotNull(get);
Assert.NotNull(get.Parameters);
if (!hasRestriction || supported)
{
Assert.Equal(3, get.Parameters.Count);
Assert.Contains(queryOption, get.Parameters.Select(p => p.Name));
}
else
{
Assert.Equal(2, get.Parameters.Count);
Assert.DoesNotContain(queryOption, get.Parameters.Select(p => p.Name));
}
}
}
}

View file

@ -6,7 +6,6 @@
using System.Linq;
using Microsoft.OData.Edm;
using Microsoft.OpenApi.OData.Edm;
using Microsoft.OpenApi.OData.Tests;
using Xunit;
namespace Microsoft.OpenApi.OData.Operation.Tests
@ -15,13 +14,19 @@ namespace Microsoft.OpenApi.OData.Operation.Tests
{
private EntityPatchOperationHandler _operationHandler = new EntityPatchOperationHandler();
[Fact]
public void CreateEntityPatchOperationReturnsCorrectOperation()
[Theory]
[InlineData(true)]
[InlineData(false)]
public void CreateEntityPatchOperationReturnsCorrectOperation(bool enableOperationId)
{
// Arrange
IEdmModel model = EdmModelHelper.BasicEdmModel;
IEdmEntitySet entitySet = model.EntityContainer.FindEntitySet("People");
ODataContext context = new ODataContext(model);
IEdmModel model = EntitySetGetOperationHandlerTests.GetEdmModel("");
IEdmEntitySet entitySet = model.EntityContainer.FindEntitySet("Customers");
OpenApiConvertSettings settings = new OpenApiConvertSettings
{
OperationId = enableOperationId
};
ODataContext context = new ODataContext(model, settings);
ODataPath path = new ODataPath(new ODataNavigationSourceSegment(entitySet), new ODataKeySegment(entitySet.EntityType()));
// Act
@ -29,10 +34,10 @@ namespace Microsoft.OpenApi.OData.Operation.Tests
// Assert
Assert.NotNull(patch);
Assert.Equal("Update entity in People", patch.Summary);
Assert.Equal("Update entity in Customers", patch.Summary);
Assert.NotNull(patch.Tags);
var tag = Assert.Single(patch.Tags);
Assert.Equal("People.Person", tag.Name);
Assert.Equal("Customers.Customer", tag.Name);
Assert.NotNull(patch.Parameters);
Assert.Equal(1, patch.Parameters.Count);
@ -42,6 +47,15 @@ namespace Microsoft.OpenApi.OData.Operation.Tests
Assert.NotNull(patch.Responses);
Assert.Equal(2, patch.Responses.Count);
Assert.Equal(new[] { "204", "default" }, patch.Responses.Select(r => r.Key));
if (enableOperationId)
{
Assert.Equal("Customers.Customer.UpdateCustomer", patch.OperationId);
}
else
{
Assert.Null(patch.OperationId);
}
}
}
}

View file

@ -3,9 +3,14 @@
// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
// ------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Microsoft.OData.Edm;
using Microsoft.OData.Edm.Csdl;
using Microsoft.OData.Edm.Validation;
using Microsoft.OpenApi.OData.Edm;
using Microsoft.OpenApi.OData.Tests;
using Xunit;
namespace Microsoft.OpenApi.OData.Operation.Tests
@ -14,13 +19,19 @@ namespace Microsoft.OpenApi.OData.Operation.Tests
{
private EntitySetGetOperationHandler _operationHandler = new EntitySetGetOperationHandler();
[Fact]
public void CreateEntitySetGetOperationReturnsCorrectOperation()
[Theory]
[InlineData(true)]
[InlineData(false)]
public void CreateEntitySetGetOperationReturnsCorrectOperation(bool enableOperationId)
{
// Arrange
IEdmModel model = EdmModelHelper.BasicEdmModel;
IEdmEntitySet entitySet = model.EntityContainer.FindEntitySet("People");
ODataContext context = new ODataContext(model);
IEdmModel model = GetEdmModel("");
IEdmEntitySet entitySet = model.EntityContainer.FindEntitySet("Customers");
OpenApiConvertSettings settings = new OpenApiConvertSettings
{
OperationId = enableOperationId
};
ODataContext context = new ODataContext(model, settings);
ODataPath path = new ODataPath(new ODataNavigationSourceSegment(entitySet));
// Act
@ -31,13 +42,240 @@ namespace Microsoft.OpenApi.OData.Operation.Tests
Assert.Equal("Get entities from " + entitySet.Name, get.Summary);
Assert.NotNull(get.Tags);
var tag = Assert.Single(get.Tags);
Assert.Equal("People.Person", tag.Name);
Assert.Equal("Customers.Customer", tag.Name);
Assert.NotNull(get.Parameters);
Assert.Equal(8, get.Parameters.Count);
Assert.NotNull(get.Responses);
Assert.Equal(2, get.Responses.Count);
if (enableOperationId)
{
Assert.Equal("Customers.Customer.ListCustomer", get.OperationId);
}
else
{
Assert.Null(get.OperationId);
}
}
[Theory]
[InlineData(false, true)]
[InlineData(false, false)]
[InlineData(true, true)]
[InlineData(true, false)]
public void CreateEntitySetGetOperationReturnsParameterForTopSupportedRestrictions(bool hasRestriction, bool supported)
{
// Arrange
string annotation = String.Format(@"<Annotation Term=""Org.OData.Capabilities.V1.TopSupported"" Bool=""{0}"" />", supported);
// Act & Assert
VerifyParameter(annotation, hasRestriction, supported, "top");
}
[Theory]
[InlineData(false, true)]
[InlineData(false, false)]
[InlineData(true, true)]
[InlineData(true, false)]
public void CreateEntitySetGetOperationReturnsParameterForSkipSupportedRestrictions(bool hasRestriction, bool supported)
{
// Arrange
string annotation = String.Format(@"<Annotation Term=""Org.OData.Capabilities.V1.SkipSupported"" Bool=""{0}"" />", supported);
// Act & Assert
VerifyParameter(annotation, hasRestriction, supported, "skip");
}
[Theory]
[InlineData(false, true)]
[InlineData(false, false)]
[InlineData(true, true)]
[InlineData(true, false)]
public void CreateEntitySetGetOperationReturnsParameterForSearchRestrictions(bool hasRestriction, bool searchable)
{
// Arrange
string annotation = String.Format(@"
<Annotation Term=""Org.OData.Capabilities.V1.SearchRestrictions"">
<Record>
<PropertyValue Property=""Searchable"" Bool=""{0}"" />
</Record>
</Annotation>", searchable);
// Act & Assert
VerifyParameter(annotation, hasRestriction, searchable, "search");
}
[Theory]
[InlineData(false, true)]
[InlineData(false, false)]
[InlineData(true, true)]
[InlineData(true, false)]
public void CreateEntitySetGetOperationReturnsParameterForFilterRestrictions(bool hasRestriction, bool filterable)
{
// Arrange
string annotation = String.Format(@"
<Annotation Term=""Org.OData.Capabilities.V1.FilterRestrictions"">
<Record>
<PropertyValue Property=""Filterable"" Bool=""{0}"" />
</Record>
</Annotation>", filterable);
// Act & Assert
VerifyParameter(annotation, hasRestriction, filterable, "filter");
}
[Theory]
[InlineData(false, true)]
[InlineData(false, false)]
[InlineData(true, true)]
[InlineData(true, false)]
public void CreateEntitySetGetOperationReturnsParameterForCountRestrictions(bool hasRestriction, bool countable)
{
// Arrange
string annotation = String.Format(@"
<Annotation Term=""Org.OData.Capabilities.V1.CountRestrictions"">
<Record>
<PropertyValue Property=""Countable"" Bool=""{0}"" />
</Record>
</Annotation>", countable);
// Act & Assert
VerifyParameter(annotation, hasRestriction, countable, "count");
}
[Theory]
[InlineData(false, true)]
[InlineData(false, false)]
[InlineData(true, true)]
[InlineData(true, false)]
public void CreateEntitySetGetOperationReturnsParameterForSortRestrictions(bool hasRestriction, bool sortable)
{
// Arrange
string annotation = String.Format(@"
<Annotation Term=""Org.OData.Capabilities.V1.SortRestrictions"">
<Record>
<PropertyValue Property=""Sortable"" Bool=""{0}"" />
</Record>
</Annotation>", sortable);
// Act & Assert
VerifyParameter(annotation, hasRestriction, sortable, "$orderby", false);
}
[Theory]
[InlineData(false, true)]
[InlineData(false, false)]
[InlineData(true, true)]
[InlineData(true, false)]
public void CreateEntitySetGetOperationReturnsParameterForExpandRestrictions(bool hasRestriction, bool expandable)
{
// Arrange
string annotation = String.Format(@"
<Annotation Term=""Org.OData.Capabilities.V1.ExpandRestrictions"">
<Record>
<PropertyValue Property=""Expandable"" Bool=""{0}"" />
</Record>
</Annotation>", expandable);
// Act & Assert
VerifyParameter(annotation, hasRestriction, expandable, "$expand", false);
}
[Theory]
[InlineData(false, "Recursive")]
[InlineData(false, "Single")]
[InlineData(false, "None")]
[InlineData(true, "Recursive")]
[InlineData(true, "Single")]
[InlineData(true, "None")]
public void CreateEntitySetGetOperationReturnsParameterForNavigationRestrictions(bool hasRestriction, string navigability)
{
// Arrange
string annotation = String.Format(@"
<Annotation Term=""Org.OData.Capabilities.V1.NavigationRestrictions"">
<Record>
<PropertyValue Property=""Navigability"">
<EnumMember>Org.OData.Capabilities.V1.NavigationType/{0}</EnumMember >
</PropertyValue>
</Record>
</Annotation>", navigability);
// Act & Assert
VerifyParameter(annotation, hasRestriction, navigability == "None" ? false : true, "$select", false);
}
public static IEdmModel GetEdmModel(string annotation)
{
const string template = @"<edmx:Edmx Version=""4.0"" xmlns:edmx=""http://docs.oasis-open.org/odata/ns/edmx"">
<edmx:DataServices>
<Schema Namespace=""NS"" xmlns=""http://docs.oasis-open.org/odata/ns/edm"">
<EntityType Name=""Customer"">
<Key>
<PropertyRef Name=""ID"" />
</Key>
<Property Name=""ID"" Type=""Edm.Int32"" Nullable=""false"" />
</EntityType>
<EntityContainer Name =""Default"">
<EntitySet Name=""Customers"" EntityType=""NS.Customer"" />
</EntityContainer>
<Annotations Target=""NS.Default/Customers"">
{0}
</Annotations>
</Schema>
</edmx:DataServices>
</edmx:Edmx>";
string modelText = string.Format(template, annotation);
IEdmModel model;
IEnumerable<EdmError> errors;
bool result = CsdlReader.TryParse(XElement.Parse(modelText).CreateReader(), out model, out errors);
Assert.True(result);
return model;
}
private void VerifyParameter(string annotation, bool hasRestriction, bool supported, string queryOption, bool isReference = true)
{
// Arrange
IEdmModel model = GetEdmModel(hasRestriction ? annotation : "");
ODataContext context = new ODataContext(model);
IEdmEntitySet customers = model.EntityContainer.FindEntitySet("Customers");
Assert.NotNull(customers); // guard
ODataPath path = new ODataPath(new ODataNavigationSourceSegment(customers));
// Act
var get = _operationHandler.CreateOperation(context, path);
// Assert
Assert.NotNull(get);
Assert.NotNull(get.Parameters);
if (!hasRestriction || supported)
{
Assert.Equal(8, get.Parameters.Count);
if (isReference)
{
Assert.Contains(queryOption, get.Parameters.Select(p => p.Reference?.Id));
}
else
{
Assert.Contains(queryOption, get.Parameters.Select(p => p.Name));
}
}
else
{
Assert.Equal(7, get.Parameters.Count);
if (isReference)
{
Assert.DoesNotContain(queryOption, get.Parameters.Select(p => p.Reference?.Id));
}
else
{
Assert.DoesNotContain(queryOption, get.Parameters.Select(p => p.Name));
}
}
}
}
}

View file

@ -5,7 +5,6 @@
using Microsoft.OData.Edm;
using Microsoft.OpenApi.OData.Edm;
using Microsoft.OpenApi.OData.Tests;
using Xunit;
namespace Microsoft.OpenApi.OData.Operation.Tests
@ -14,13 +13,19 @@ namespace Microsoft.OpenApi.OData.Operation.Tests
{
private EntitySetPostOperationHandler _operationHandler = new EntitySetPostOperationHandler();
[Fact]
public void CreateEntitySetPostOperationReturnsCorrectOperation()
[Theory]
[InlineData(true)]
[InlineData(false)]
public void CreateEntitySetPostOperationReturnsCorrectOperation(bool enableOperationId)
{
// Arrange
IEdmModel model = EdmModelHelper.BasicEdmModel;
IEdmEntitySet entitySet = model.EntityContainer.FindEntitySet("People");
ODataContext context = new ODataContext(model);
IEdmModel model = EntitySetGetOperationHandlerTests.GetEdmModel("");
IEdmEntitySet entitySet = model.EntityContainer.FindEntitySet("Customers");
OpenApiConvertSettings settings = new OpenApiConvertSettings
{
OperationId = enableOperationId
};
ODataContext context = new ODataContext(model, settings);
ODataPath path = new ODataPath(new ODataNavigationSourceSegment(entitySet));
// Act
@ -31,13 +36,22 @@ namespace Microsoft.OpenApi.OData.Operation.Tests
Assert.Equal("Add new entity to " + entitySet.Name, post.Summary);
Assert.NotNull(post.Tags);
var tag = Assert.Single(post.Tags);
Assert.Equal("People.Person", tag.Name);
Assert.Equal("Customers.Customer", tag.Name);
Assert.Empty(post.Parameters);
Assert.NotNull(post.RequestBody);
Assert.NotNull(post.Responses);
Assert.Equal(2, post.Responses.Count);
if (enableOperationId)
{
Assert.Equal("Customers.Customer.CreateCustomer", post.OperationId);
}
else
{
Assert.Null(post.OperationId);
}
}
}
}

View file

@ -15,12 +15,18 @@ namespace Microsoft.OpenApi.OData.Operation.Tests
{
private NavigationPropertyGetOperationHandler _operationHandler = new NavigationPropertyGetOperationHandler();
[Fact]
public void CreateNavigationGetOperationReturnsCorrectOperation()
[Theory]
[InlineData(true)]
[InlineData(false)]
public void CreateNavigationGetOperationReturnsCorrectOperation(bool enableOperationId)
{
// Arrange
IEdmModel model = EdmModelHelper.TripServiceModel;
ODataContext context = new ODataContext(model);
OpenApiConvertSettings settings = new OpenApiConvertSettings
{
OperationId = enableOperationId
};
ODataContext context = new ODataContext(model, settings);
IEdmEntitySet people = model.EntityContainer.FindEntitySet("People");
Assert.NotNull(people);
@ -45,6 +51,15 @@ namespace Microsoft.OpenApi.OData.Operation.Tests
Assert.Equal(2, operation.Responses.Count);
Assert.Equal(new string[] { "200", "default" }, operation.Responses.Select(e => e.Key));
if (enableOperationId)
{
Assert.Equal("People.ListTrips", operation.OperationId);
}
else
{
Assert.Null(operation.OperationId);
}
}
}
}

View file

@ -15,12 +15,18 @@ namespace Microsoft.OpenApi.OData.Operation.Tests
{
private NavigationPropertyPatchOperationHandler _operationHandler = new NavigationPropertyPatchOperationHandler();
[Fact]
public void CreateNavigationPatchOperationReturnsCorrectOperation()
[Theory]
[InlineData(true)]
[InlineData(false)]
public void CreateNavigationPatchOperationReturnsCorrectOperation(bool enableOperationId)
{
// Arrange
IEdmModel model = EdmModelHelper.TripServiceModel;
ODataContext context = new ODataContext(model);
OpenApiConvertSettings settings = new OpenApiConvertSettings
{
OperationId = enableOperationId
};
ODataContext context = new ODataContext(model, settings);
IEdmEntitySet people = model.EntityContainer.FindEntitySet("People");
Assert.NotNull(people);
@ -46,6 +52,15 @@ namespace Microsoft.OpenApi.OData.Operation.Tests
Assert.Equal(2, operation.Responses.Count);
Assert.Equal(new string[] { "204", "default" }, operation.Responses.Select(e => e.Key));
if (enableOperationId)
{
Assert.Equal("People.UpdateBestFriend", operation.OperationId);
}
else
{
Assert.Null(operation.OperationId);
}
}
}
}

View file

@ -15,12 +15,18 @@ namespace Microsoft.OpenApi.OData.Operation.Tests
{
private NavigationPropertyPostOperationHandler _operationHandler = new NavigationPropertyPostOperationHandler();
[Fact]
public void CreateNavigationPostOperationReturnsCorrectOperation()
[Theory]
[InlineData(true)]
[InlineData(false)]
public void CreateNavigationPostOperationReturnsCorrectOperation(bool enableOperationId)
{
// Arrange
IEdmModel model = EdmModelHelper.TripServiceModel;
ODataContext context = new ODataContext(model);
OpenApiConvertSettings settings = new OpenApiConvertSettings
{
OperationId = enableOperationId
};
ODataContext context = new ODataContext(model, settings);
IEdmEntitySet people = model.EntityContainer.FindEntitySet("People");
Assert.NotNull(people);
@ -46,6 +52,15 @@ namespace Microsoft.OpenApi.OData.Operation.Tests
Assert.Equal(2, operation.Responses.Count);
Assert.Equal(new string[] { "201", "default" }, operation.Responses.Select(e => e.Key));
if (enableOperationId)
{
Assert.Equal("People.CreateTrips", operation.OperationId);
}
else
{
Assert.Null(operation.OperationId);
}
}
}
}

View file

@ -3,10 +3,14 @@
// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
// ------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Microsoft.OData.Edm;
using Microsoft.OData.Edm.Csdl;
using Microsoft.OData.Edm.Validation;
using Microsoft.OpenApi.OData.Edm;
using Microsoft.OpenApi.OData.Tests;
using Xunit;
namespace Microsoft.OpenApi.OData.Operation.Tests
@ -15,13 +19,19 @@ namespace Microsoft.OpenApi.OData.Operation.Tests
{
private SingletonGetOperationHandler _operationHandler = new SingletonGetOperationHandler();
[Fact]
public void CreateSingletonGetOperationReturnsCorrectOperation()
[Theory]
[InlineData(true)]
[InlineData(false)]
public void CreateSingletonGetOperationReturnsCorrectOperation(bool enableOperationId)
{
// Arrange
IEdmModel model = EdmModelHelper.BasicEdmModel;
IEdmModel model = GetEdmModel("");
IEdmSingleton singleton = model.EntityContainer.FindSingleton("Me");
ODataContext context = new ODataContext(model);
OpenApiConvertSettings settings = new OpenApiConvertSettings
{
OperationId = enableOperationId
};
ODataContext context = new ODataContext(model, settings);
ODataPath path = new ODataPath(new ODataNavigationSourceSegment(singleton));
// Act
@ -32,7 +42,7 @@ namespace Microsoft.OpenApi.OData.Operation.Tests
Assert.Equal("Get Me", get.Summary);
Assert.NotNull(get.Tags);
var tag = Assert.Single(get.Tags);
Assert.Equal("Me.Person", tag.Name);
Assert.Equal("Me.Customer", tag.Name);
Assert.NotNull(get.Parameters);
Assert.Equal(2, get.Parameters.Count);
@ -42,6 +52,115 @@ namespace Microsoft.OpenApi.OData.Operation.Tests
Assert.NotNull(get.Responses);
Assert.Equal(2, get.Responses.Count);
Assert.Equal(new[] { "200", "default" }, get.Responses.Select(r => r.Key));
if (enableOperationId)
{
Assert.Equal("Me.Customer.GetCustomer", get.OperationId);
}
else
{
Assert.Null(get.OperationId);
}
}
[Theory]
[InlineData(false, true)]
[InlineData(false, false)]
[InlineData(true, true)]
[InlineData(true, false)]
public void CreateSingletonSetGetOperationReturnsParameterForExpandRestrictions(bool hasRestriction, bool expandable)
{
// Arrange
string annotation = String.Format(@"
<Annotation Term=""Org.OData.Capabilities.V1.ExpandRestrictions"">
<Record>
<PropertyValue Property=""Expandable"" Bool=""{0}"" />
</Record>
</Annotation>", expandable);
// Act & Assert
VerifyParameter(annotation, hasRestriction, expandable, "$expand");
}
[Theory]
[InlineData(false, "Recursive")]
[InlineData(false, "Single")]
[InlineData(false, "None")]
[InlineData(true, "Recursive")]
[InlineData(true, "Single")]
[InlineData(true, "None")]
public void CreateSingletonGetOperationReturnsParameterForNavigationRestrictions(bool hasRestriction, string navigability)
{
// Arrange
string annotation = String.Format(@"
<Annotation Term=""Org.OData.Capabilities.V1.NavigationRestrictions"">
<Record>
<PropertyValue Property=""Navigability"">
<EnumMember>Org.OData.Capabilities.V1.NavigationType/{0}</EnumMember >
</PropertyValue>
</Record>
</Annotation>", navigability);
// Act & Assert
VerifyParameter(annotation, hasRestriction, navigability == "None" ? false : true, "$select");
}
public static IEdmModel GetEdmModel(string annotation)
{
const string template = @"<edmx:Edmx Version=""4.0"" xmlns:edmx=""http://docs.oasis-open.org/odata/ns/edmx"">
<edmx:DataServices>
<Schema Namespace=""NS"" xmlns=""http://docs.oasis-open.org/odata/ns/edm"">
<EntityType Name=""Customer"">
<Key>
<PropertyRef Name=""ID"" />
</Key>
<Property Name=""ID"" Type=""Edm.Int32"" Nullable=""false"" />
</EntityType>
<EntityContainer Name =""Default"">
<Singleton Name=""Me"" Type=""NS.Customer"" />
</EntityContainer>
<Annotations Target=""NS.Default/Me"">
{0}
</Annotations>
</Schema>
</edmx:DataServices>
</edmx:Edmx>";
string modelText = string.Format(template, annotation);
IEdmModel model;
IEnumerable<EdmError> errors;
bool result = CsdlReader.TryParse(XElement.Parse(modelText).CreateReader(), out model, out errors);
Assert.True(result);
return model;
}
private void VerifyParameter(string annotation, bool hasRestriction, bool supported, string queryOption)
{
// Arrange
IEdmModel model = GetEdmModel(hasRestriction ? annotation : "");
ODataContext context = new ODataContext(model);
IEdmSingleton me = model.EntityContainer.FindSingleton("Me");
Assert.NotNull(me); // guard
ODataPath path = new ODataPath(new ODataNavigationSourceSegment(me));
// Act
var get = _operationHandler.CreateOperation(context, path);
// Assert
Assert.NotNull(get);
Assert.NotNull(get.Parameters);
if (!hasRestriction || supported)
{
Assert.Equal(2, get.Parameters.Count);
Assert.Contains(queryOption, get.Parameters.Select(p => p.Name));
}
else
{
Assert.Equal(1, get.Parameters.Count);
Assert.DoesNotContain(queryOption, get.Parameters.Select(p => p.Name));
}
}
}
}

View file

@ -6,7 +6,6 @@
using System.Linq;
using Microsoft.OData.Edm;
using Microsoft.OpenApi.OData.Edm;
using Microsoft.OpenApi.OData.Tests;
using Xunit;
namespace Microsoft.OpenApi.OData.Operation.Tests
@ -15,13 +14,19 @@ namespace Microsoft.OpenApi.OData.Operation.Tests
{
private SingletonPatchOperationHandler _operationHandler = new SingletonPatchOperationHandler();
[Fact]
public void CreateSingletonPatchOperationReturnsCorrectOperation()
[Theory]
[InlineData(true)]
[InlineData(false)]
public void CreateSingletonPatchOperationReturnsCorrectOperation(bool enableOperationId)
{
// Arrange
IEdmModel model = EdmModelHelper.BasicEdmModel;
IEdmModel model = SingletonGetOperationHandlerTests.GetEdmModel("");
IEdmSingleton singleton = model.EntityContainer.FindSingleton("Me");
ODataContext context = new ODataContext(model);
OpenApiConvertSettings settings = new OpenApiConvertSettings
{
OperationId = enableOperationId
};
ODataContext context = new ODataContext(model, settings);
ODataPath path = new ODataPath(new ODataNavigationSourceSegment(singleton));
// Act
@ -32,7 +37,7 @@ namespace Microsoft.OpenApi.OData.Operation.Tests
Assert.Equal("Update Me", patch.Summary);
Assert.NotNull(patch.Tags);
var tag = Assert.Single(patch.Tags);
Assert.Equal("Me.Person", tag.Name);
Assert.Equal("Me.Customer", tag.Name);
Assert.Empty(patch.Parameters);
Assert.NotNull(patch.RequestBody);
@ -40,6 +45,15 @@ namespace Microsoft.OpenApi.OData.Operation.Tests
Assert.NotNull(patch.Responses);
Assert.Equal(2, patch.Responses.Count);
Assert.Equal(new[] { "204", "default" }, patch.Responses.Select(r => r.Key));
if (enableOperationId)
{
Assert.Equal("Me.Customer.UpdateCustomer", patch.OperationId);
}
else
{
Assert.Null(patch.OperationId);
}
}
}
}

View file

@ -66,7 +66,7 @@ namespace Microsoft.OpenApi.OData.PathItem.Tests
.OperationImports().FirstOrDefault(o => o.Name == operationImport);
Assert.NotNull(edmOperationImport); // guard
string expectSummary = "Invoke " +
(edmOperationImport.IsActionImport() ? "action " : "function ") + operationImport;
(edmOperationImport.IsActionImport() ? "actionImport " : "functionImport ") + operationImport;
ODataPath path = new ODataPath(new ODataOperationImportSegment(edmOperationImport));
// Act

View file

@ -362,7 +362,7 @@
"People.Functions"
],
"summary": "Invoke function GetFavoriteAirline",
"operationId": "People.0-GetFavoriteAirline",
"operationId": "People.Person.GetFavoriteAirline.1c526eb64c7a82083ebab0f2",
"parameters": [
{
"name": "UserName",
@ -404,7 +404,7 @@
"People.Functions"
],
"summary": "Invoke function GetFriendsTrips",
"operationId": "People.0-GetFriendsTrips",
"operationId": "People.Person.GetFriendsTrips.9b42e09ab8ef6d73b88be6e9",
"parameters": [
{
"name": "UserName",
@ -457,7 +457,7 @@
"People.Functions"
],
"summary": "Invoke function UpdatePersonLastName",
"operationId": "People.0-UpdatePersonLastName",
"operationId": "People.Person.UpdatePersonLastName.e4ea38792d2cfa3aa5bab91b",
"parameters": [
{
"name": "UserName",
@ -503,7 +503,7 @@
"People.Actions"
],
"summary": "Invoke action ShareTrip",
"operationId": "People.0-ShareTrip",
"operationId": "People.Person.ShareTrip",
"parameters": [
{
"name": "UserName",
@ -1462,7 +1462,7 @@
"NewComePeople.Functions"
],
"summary": "Invoke function GetFavoriteAirline",
"operationId": "NewComePeople.0-GetFavoriteAirline",
"operationId": "NewComePeople.Person.GetFavoriteAirline.1c526eb64c7a82083ebab0f2",
"parameters": [
{
"name": "UserName",
@ -1504,7 +1504,7 @@
"NewComePeople.Functions"
],
"summary": "Invoke function GetFriendsTrips",
"operationId": "NewComePeople.0-GetFriendsTrips",
"operationId": "NewComePeople.Person.GetFriendsTrips.9b42e09ab8ef6d73b88be6e9",
"parameters": [
{
"name": "UserName",
@ -1557,7 +1557,7 @@
"NewComePeople.Functions"
],
"summary": "Invoke function UpdatePersonLastName",
"operationId": "NewComePeople.0-UpdatePersonLastName",
"operationId": "NewComePeople.Person.UpdatePersonLastName.e4ea38792d2cfa3aa5bab91b",
"parameters": [
{
"name": "UserName",
@ -1603,7 +1603,7 @@
"NewComePeople.Actions"
],
"summary": "Invoke action ShareTrip",
"operationId": "NewComePeople.0-ShareTrip",
"operationId": "NewComePeople.Person.ShareTrip",
"parameters": [
{
"name": "UserName",
@ -1755,7 +1755,7 @@
"Me.Functions"
],
"summary": "Invoke function GetFavoriteAirline",
"operationId": "Me.0-GetFavoriteAirline",
"operationId": "Me.Person.GetFavoriteAirline.1c526eb64c7a82083ebab0f2",
"responses": {
"200": {
"description": "Success",
@ -1785,7 +1785,7 @@
"Me.Functions"
],
"summary": "Invoke function GetFriendsTrips",
"operationId": "Me.0-GetFriendsTrips",
"operationId": "Me.Person.GetFriendsTrips.9b42e09ab8ef6d73b88be6e9",
"parameters": [
{
"name": "userName",
@ -1828,7 +1828,7 @@
"Me.Functions"
],
"summary": "Invoke function UpdatePersonLastName",
"operationId": "Me.0-UpdatePersonLastName",
"operationId": "Me.Person.UpdatePersonLastName.e4ea38792d2cfa3aa5bab91b",
"parameters": [
{
"name": "lastName",
@ -1864,7 +1864,7 @@
"Me.Actions"
],
"summary": "Invoke action ShareTrip",
"operationId": "Me.0-ShareTrip",
"operationId": "Me.Person.ShareTrip",
"requestBody": {
"description": "Action parameters",
"content": {
@ -1898,13 +1898,13 @@
"x-ms-docs-operation-type": "action"
}
},
"/GetPersonWithMostFriends": {
"/GetPersonWithMostFriends()": {
"get": {
"tags": [
"People"
],
"summary": "Invoke function GetPersonWithMostFriends",
"operationId": "OperationImport.0-GetPersonWithMostFriends",
"summary": "Invoke functionImport GetPersonWithMostFriends",
"operationId": "OperationImport.GetPersonWithMostFriends.de8b4a5ab51ba6c8c254fe80",
"responses": {
"200": {
"description": "Success",
@ -1928,13 +1928,13 @@
"x-ms-docs-operation-type": "functionImport"
}
},
"/GetNearestAirport": {
"/GetNearestAirport(lat={lat},lon={lon})": {
"get": {
"tags": [
"Airports"
],
"summary": "Invoke function GetNearestAirport",
"operationId": "OperationImport.0-GetNearestAirport",
"summary": "Invoke functionImport GetNearestAirport",
"operationId": "OperationImport.GetNearestAirport.6896b1324a4eff76ad9867a0",
"parameters": [
{
"name": "lat",
@ -2011,8 +2011,8 @@
"tags": [
"ResetDataSource"
],
"summary": "Invoke action ResetDataSource",
"operationId": "OperationImport.0-ResetDataSource",
"summary": "Invoke actionImport ResetDataSource",
"operationId": "OperationImport.ResetDataSource",
"responses": {
"204": {
"description": "Success"

View file

@ -251,7 +251,7 @@ paths:
tags:
- People.Functions
summary: Invoke function GetFavoriteAirline
operationId: People.0-GetFavoriteAirline
operationId: People.Person.GetFavoriteAirline.1c526eb64c7a82083ebab0f2
parameters:
- name: UserName
in: path
@ -277,7 +277,7 @@ paths:
tags:
- People.Functions
summary: Invoke function GetFriendsTrips
operationId: People.0-GetFriendsTrips
operationId: People.Person.GetFriendsTrips.9b42e09ab8ef6d73b88be6e9
parameters:
- name: UserName
in: path
@ -310,7 +310,7 @@ paths:
tags:
- People.Functions
summary: Invoke function UpdatePersonLastName
operationId: People.0-UpdatePersonLastName
operationId: People.Person.UpdatePersonLastName.e4ea38792d2cfa3aa5bab91b
parameters:
- name: UserName
in: path
@ -340,7 +340,7 @@ paths:
tags:
- People.Actions
summary: Invoke action ShareTrip
operationId: People.0-ShareTrip
operationId: People.Person.ShareTrip
parameters:
- name: UserName
in: path
@ -981,7 +981,7 @@ paths:
tags:
- NewComePeople.Functions
summary: Invoke function GetFavoriteAirline
operationId: NewComePeople.0-GetFavoriteAirline
operationId: NewComePeople.Person.GetFavoriteAirline.1c526eb64c7a82083ebab0f2
parameters:
- name: UserName
in: path
@ -1007,7 +1007,7 @@ paths:
tags:
- NewComePeople.Functions
summary: Invoke function GetFriendsTrips
operationId: NewComePeople.0-GetFriendsTrips
operationId: NewComePeople.Person.GetFriendsTrips.9b42e09ab8ef6d73b88be6e9
parameters:
- name: UserName
in: path
@ -1040,7 +1040,7 @@ paths:
tags:
- NewComePeople.Functions
summary: Invoke function UpdatePersonLastName
operationId: NewComePeople.0-UpdatePersonLastName
operationId: NewComePeople.Person.UpdatePersonLastName.e4ea38792d2cfa3aa5bab91b
parameters:
- name: UserName
in: path
@ -1070,7 +1070,7 @@ paths:
tags:
- NewComePeople.Actions
summary: Invoke action ShareTrip
operationId: NewComePeople.0-ShareTrip
operationId: NewComePeople.Person.ShareTrip
parameters:
- name: UserName
in: path
@ -1176,7 +1176,7 @@ paths:
tags:
- Me.Functions
summary: Invoke function GetFavoriteAirline
operationId: Me.0-GetFavoriteAirline
operationId: Me.Person.GetFavoriteAirline.1c526eb64c7a82083ebab0f2
responses:
'200':
description: Success
@ -1194,7 +1194,7 @@ paths:
tags:
- Me.Functions
summary: Invoke function GetFriendsTrips
operationId: Me.0-GetFriendsTrips
operationId: Me.Person.GetFriendsTrips.9b42e09ab8ef6d73b88be6e9
parameters:
- name: userName
in: path
@ -1220,7 +1220,7 @@ paths:
tags:
- Me.Functions
summary: Invoke function UpdatePersonLastName
operationId: Me.0-UpdatePersonLastName
operationId: Me.Person.UpdatePersonLastName.e4ea38792d2cfa3aa5bab91b
parameters:
- name: lastName
in: path
@ -1243,7 +1243,7 @@ paths:
tags:
- Me.Actions
summary: Invoke action ShareTrip
operationId: Me.0-ShareTrip
operationId: Me.Person.ShareTrip
requestBody:
description: Action parameters
content:
@ -1265,12 +1265,12 @@ paths:
default:
$ref: '#/components/responses/error'
x-ms-docs-operation-type: action
/GetPersonWithMostFriends:
/GetPersonWithMostFriends():
get:
tags:
- People
summary: Invoke function GetPersonWithMostFriends
operationId: OperationImport.0-GetPersonWithMostFriends
summary: Invoke functionImport GetPersonWithMostFriends
operationId: OperationImport.GetPersonWithMostFriends.de8b4a5ab51ba6c8c254fe80
responses:
'200':
description: Success
@ -1283,12 +1283,12 @@ paths:
default:
$ref: '#/components/responses/error'
x-ms-docs-operation-type: functionImport
/GetNearestAirport:
'/GetNearestAirport(lat={lat},lon={lon})':
get:
tags:
- Airports
summary: Invoke function GetNearestAirport
operationId: OperationImport.0-GetNearestAirport
summary: Invoke functionImport GetNearestAirport
operationId: OperationImport.GetNearestAirport.6896b1324a4eff76ad9867a0
parameters:
- name: lat
in: path
@ -1330,8 +1330,8 @@ paths:
post:
tags:
- ResetDataSource
summary: Invoke action ResetDataSource
operationId: OperationImport.0-ResetDataSource
summary: Invoke actionImport ResetDataSource
operationId: OperationImport.ResetDataSource
responses:
'204':
description: Success