Enable Uri escape function call
This commit is contained in:
parent
740123d36a
commit
35a35a5f56
|
@ -18,6 +18,25 @@ namespace Microsoft.OpenApi.OData.Edm
|
|||
/// </summary>
|
||||
public static class EdmModelExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Determines whether the specified operation is UrlEscape function or not.
|
||||
/// </summary>
|
||||
/// <param name="model">The Edm model.</param>
|
||||
/// <param name="operation">The specified operation.</param>
|
||||
/// <returns><c>true</c> if the specified operation is UrlEscape function; otherwise, <c>false</c>.</returns>
|
||||
public static bool IsUrlEscapeFunction(this IEdmModel model, IEdmOperation operation)
|
||||
{
|
||||
Utils.CheckArgumentNull(model, nameof(model));
|
||||
Utils.CheckArgumentNull(operation, nameof(operation));
|
||||
|
||||
if (operation.IsAction())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return model.IsUrlEscapeFunction((IEdmFunction)operation);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified function is UrlEscape function or not.
|
||||
/// </summary>
|
||||
|
|
|
@ -22,8 +22,18 @@ namespace Microsoft.OpenApi.OData.Edm
|
|||
/// </summary>
|
||||
/// <param name="operation">The operation.</param>
|
||||
public ODataOperationSegment(IEdmOperation operation)
|
||||
: this(operation, false)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="ODataOperationSegment"/> class.
|
||||
/// </summary>
|
||||
/// <param name="operation">The operation.</param>
|
||||
public ODataOperationSegment(IEdmOperation operation, bool isEscapedFunction)
|
||||
{
|
||||
Operation = operation ?? throw Error.ArgumentNull(nameof(operation));
|
||||
IsEscapedFunction = isEscapedFunction;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -31,6 +41,11 @@ namespace Microsoft.OpenApi.OData.Edm
|
|||
/// </summary>
|
||||
public IEdmOperation Operation { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the is escaped function.
|
||||
/// </summary>
|
||||
public bool IsEscapedFunction { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ODataSegmentKind Kind => ODataSegmentKind.Operation;
|
||||
|
||||
|
@ -52,6 +67,22 @@ namespace Microsoft.OpenApi.OData.Edm
|
|||
|
||||
private string FunctionName(IEdmFunction function, OpenApiConvertSettings settings, HashSet<string> parameters)
|
||||
{
|
||||
if (settings.EnableUriEscapeFunctionCall && IsEscapedFunction)
|
||||
{
|
||||
// Debug.Assert(function.Parameters.Count == 2); It should be verify at Edm model.
|
||||
// Debug.Assert(function.IsBound == true);
|
||||
string parameterName = function.Parameters.Last().Name;
|
||||
parameterName = Utils.GetUniqueName(parameterName, parameters);
|
||||
if (function.IsComposable)
|
||||
{
|
||||
return $"{{{parameterName}}}:";
|
||||
}
|
||||
else
|
||||
{
|
||||
return $"{{{parameterName}}}";
|
||||
}
|
||||
}
|
||||
|
||||
StringBuilder functionName = new StringBuilder();
|
||||
if (settings.EnableUnqualifiedCall)
|
||||
{
|
||||
|
|
|
@ -159,6 +159,17 @@ namespace Microsoft.OpenApi.OData.Edm
|
|||
}
|
||||
else // other segments
|
||||
{
|
||||
if (segment.Kind == ODataSegmentKind.Operation)
|
||||
{
|
||||
ODataOperationSegment operation = (ODataOperationSegment)segment;
|
||||
if (operation.IsEscapedFunction && settings.EnableUriEscapeFunctionCall)
|
||||
{
|
||||
sb.Append(":/");
|
||||
sb.Append(pathItemName);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
sb.Append("/");
|
||||
sb.Append(pathItemName);
|
||||
}
|
||||
|
|
|
@ -319,15 +319,18 @@ namespace Microsoft.OpenApi.OData.Edm
|
|||
private bool AppendBoundOperationOnNavigationSourcePath(IEdmOperation edmOperation, bool isCollection, IEdmEntityType bindingEntityType)
|
||||
{
|
||||
bool found = false;
|
||||
|
||||
if (_allNavigationSourcePaths.TryGetValue(bindingEntityType, out IList<ODataPath> value))
|
||||
{
|
||||
bool isEscapedFunction = _model.IsUrlEscapeFunction(edmOperation);
|
||||
|
||||
foreach (var subPath in value)
|
||||
{
|
||||
if ((isCollection && subPath.Kind == ODataPathKind.EntitySet) ||
|
||||
(!isCollection && subPath.Kind != ODataPathKind.EntitySet))
|
||||
{
|
||||
ODataPath newPath = subPath.Clone();
|
||||
newPath.Push(new ODataOperationSegment(edmOperation));
|
||||
newPath.Push(new ODataOperationSegment(edmOperation, isEscapedFunction));
|
||||
AppendPath(newPath);
|
||||
found = true;
|
||||
}
|
||||
|
@ -340,6 +343,7 @@ namespace Microsoft.OpenApi.OData.Edm
|
|||
private bool AppendBoundOperationOnNavigationPropertyPath(IEdmOperation edmOperation, bool isCollection, IEdmEntityType bindingEntityType)
|
||||
{
|
||||
bool found = false;
|
||||
bool isEscapedFunction = _model.IsUrlEscapeFunction(edmOperation);
|
||||
|
||||
if (_allNavigationPropertyPaths.TryGetValue(bindingEntityType, out IList<ODataPath> value))
|
||||
{
|
||||
|
@ -370,7 +374,7 @@ namespace Microsoft.OpenApi.OData.Edm
|
|||
}
|
||||
|
||||
ODataPath newPath = path.Clone();
|
||||
newPath.Push(new ODataOperationSegment(edmOperation));
|
||||
newPath.Push(new ODataOperationSegment(edmOperation, isEscapedFunction));
|
||||
AppendPath(newPath);
|
||||
found = true;
|
||||
}
|
||||
|
@ -383,6 +387,7 @@ namespace Microsoft.OpenApi.OData.Edm
|
|||
{
|
||||
bool found = false;
|
||||
|
||||
bool isEscapedFunction = _model.IsUrlEscapeFunction(edmOperation);
|
||||
foreach (var baseType in bindingEntityType.FindAllBaseTypes())
|
||||
{
|
||||
if (_allNavigationSources.TryGetValue(baseType, out IList<IEdmNavigationSource> baseNavigationSource))
|
||||
|
@ -394,7 +399,7 @@ namespace Microsoft.OpenApi.OData.Edm
|
|||
if (ns is IEdmEntitySet)
|
||||
{
|
||||
ODataPath newPath = new ODataPath(new ODataNavigationSourceSegment(ns), new ODataTypeCastSegment(bindingEntityType),
|
||||
new ODataOperationSegment(edmOperation));
|
||||
new ODataOperationSegment(edmOperation, isEscapedFunction));
|
||||
AppendPath(newPath);
|
||||
found = true;
|
||||
}
|
||||
|
@ -404,7 +409,7 @@ namespace Microsoft.OpenApi.OData.Edm
|
|||
if (ns is IEdmSingleton)
|
||||
{
|
||||
ODataPath newPath = new ODataPath(new ODataNavigationSourceSegment(ns), new ODataTypeCastSegment(bindingEntityType),
|
||||
new ODataOperationSegment(edmOperation));
|
||||
new ODataOperationSegment(edmOperation, isEscapedFunction));
|
||||
AppendPath(newPath);
|
||||
found = true;
|
||||
}
|
||||
|
@ -412,7 +417,7 @@ namespace Microsoft.OpenApi.OData.Edm
|
|||
{
|
||||
ODataPath newPath = new ODataPath(new ODataNavigationSourceSegment(ns), new ODataKeySegment(ns.EntityType()),
|
||||
new ODataTypeCastSegment(bindingEntityType),
|
||||
new ODataOperationSegment(edmOperation));
|
||||
new ODataOperationSegment(edmOperation, isEscapedFunction));
|
||||
AppendPath(newPath);
|
||||
found = true;
|
||||
}
|
||||
|
|
|
@ -68,6 +68,11 @@ namespace Microsoft.OpenApi.OData
|
|||
/// </summary>
|
||||
public bool EnableOperationId { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets/sets a value indicating whether to output the binding function as Uri escape function if applied the UriEscapeFunction term.
|
||||
/// </summary>
|
||||
public bool EnableUriEscapeFunctionCall { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets/sets a value indicating whether to verify the edm model before converter.
|
||||
/// </summary>
|
||||
|
@ -103,6 +108,7 @@ namespace Microsoft.OpenApi.OData
|
|||
newSettings.VerifyEdmModel = this.VerifyEdmModel;
|
||||
newSettings.IEEE754Compatible = this.IEEE754Compatible;
|
||||
newSettings.TopExample = this.TopExample;
|
||||
newSettings.EnableUriEscapeFunctionCall = this.EnableUriEscapeFunctionCall;
|
||||
|
||||
return newSettings;
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
// ------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Microsoft.OData.Edm;
|
||||
using Xunit;
|
||||
|
||||
|
@ -95,11 +96,57 @@ namespace Microsoft.OpenApi.OData.Edm.Tests
|
|||
Assert.Equal(expected, segment.GetPathItemName(settings));
|
||||
}
|
||||
|
||||
private EdmFunction BoundFunction(string funcName, bool isBound, IEdmTypeReference firstParameterType)
|
||||
[Theory]
|
||||
[InlineData(true, true, "{param}")]
|
||||
[InlineData(true, false, "NS.MyFunction(param={param})")]
|
||||
[InlineData(false, true, "NS.MyFunction(param={param})")]
|
||||
[InlineData(false, false, "NS.MyFunction(param={param})")]
|
||||
public void GetPathItemNameReturnsCorrectFunctionLiteralForEscapedFunction(bool isEscapedFunction, bool enableEscapeFunctionCall, string expected)
|
||||
{
|
||||
// Arrange & Act
|
||||
IEdmEntityTypeReference entityTypeReference = new EdmEntityTypeReference(new EdmEntityType("NS", "Entity"), false);
|
||||
IEdmTypeReference parameterType = EdmCoreModel.Instance.GetPrimitive(EdmPrimitiveTypeKind.String, isNullable: false);
|
||||
EdmFunction boundFunction = BoundFunction("MyFunction", true, entityTypeReference);
|
||||
boundFunction.AddParameter("param", parameterType);
|
||||
|
||||
var segment = new ODataOperationSegment(boundFunction, isEscapedFunction);
|
||||
OpenApiConvertSettings settings = new OpenApiConvertSettings
|
||||
{
|
||||
EnableUriEscapeFunctionCall = enableEscapeFunctionCall
|
||||
};
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expected, segment.GetPathItemName(settings));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true, true, "{param}:")]
|
||||
[InlineData(true, false, "NS.MyFunction(param={param})")]
|
||||
[InlineData(false, true, "NS.MyFunction(param={param})")]
|
||||
[InlineData(false, false, "NS.MyFunction(param={param})")]
|
||||
public void GetPathItemNameReturnsCorrectFunctionLiteralForEscapedComposableFunction(bool isEscapedFunction, bool enableEscapeFunctionCall, string expected)
|
||||
{
|
||||
// Arrange & Act
|
||||
IEdmEntityTypeReference entityTypeReference = new EdmEntityTypeReference(new EdmEntityType("NS", "Entity"), false);
|
||||
IEdmTypeReference parameterType = EdmCoreModel.Instance.GetPrimitive(EdmPrimitiveTypeKind.String, isNullable: false);
|
||||
EdmFunction boundFunction = BoundFunction("MyFunction", true, entityTypeReference, true);
|
||||
boundFunction.AddParameter("param", parameterType);
|
||||
|
||||
var segment = new ODataOperationSegment(boundFunction, isEscapedFunction);
|
||||
OpenApiConvertSettings settings = new OpenApiConvertSettings
|
||||
{
|
||||
EnableUriEscapeFunctionCall = enableEscapeFunctionCall
|
||||
};
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expected, segment.GetPathItemName(settings));
|
||||
}
|
||||
|
||||
private EdmFunction BoundFunction(string funcName, bool isBound, IEdmTypeReference firstParameterType, bool isComposable = false)
|
||||
{
|
||||
IEdmTypeReference returnType = EdmCoreModel.Instance.GetPrimitive(EdmPrimitiveTypeKind.Boolean, isNullable: false);
|
||||
EdmFunction boundFunction = new EdmFunction("NS", funcName, returnType,
|
||||
isBound: isBound, entitySetPathExpression: null, isComposable: false);
|
||||
isBound: isBound, entitySetPathExpression: null, isComposable: isComposable);
|
||||
boundFunction.AddParameter("entity", firstParameterType);
|
||||
return boundFunction;
|
||||
}
|
||||
|
|
|
@ -5,6 +5,9 @@
|
|||
|
||||
using System;
|
||||
using Microsoft.OData.Edm;
|
||||
using Microsoft.OData.Edm.Csdl;
|
||||
using Microsoft.OData.Edm.Vocabularies;
|
||||
using Microsoft.OData.Edm.Vocabularies.Community.V1;
|
||||
using Microsoft.OpenApi.OData.Edm;
|
||||
using Microsoft.OpenApi.OData.Tests;
|
||||
using Xunit;
|
||||
|
@ -64,5 +67,58 @@ namespace Microsoft.OpenApi.OData.Generator.Tests
|
|||
Assert.Contains("/CountryOrRegion/{Name}", pathItems.Keys);
|
||||
Assert.Contains("/Me", pathItems.Keys);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true, true, true, "/Customers({ID}):/{param}:")]
|
||||
[InlineData(true, true, false, "/Customers({ID}):/{param}")]
|
||||
|
||||
[InlineData(true, false, true, "/Customers({ID})/NS.MyFunction(param={param})")]
|
||||
[InlineData(true, false, false, "/Customers({ID})/NS.MyFunction(param={param})")]
|
||||
[InlineData(false, true, true, "/Customers({ID})/NS.MyFunction(param={param})")]
|
||||
[InlineData(false, true, false, "/Customers({ID})/NS.MyFunction(param={param})")]
|
||||
[InlineData(false, false, true, "/Customers({ID})/NS.MyFunction(param={param})")]
|
||||
[InlineData(false, false, false, "/Customers({ID})/NS.MyFunction(param={param})")]
|
||||
public void CreatePathItemsReturnsForEscapeFunctionModel(bool enableEscaped, bool hasEscapedAnnotation, bool isComposable, string expected)
|
||||
{
|
||||
// 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, isComposable);
|
||||
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);
|
||||
container.AddElement(customers);
|
||||
model.AddElement(container);
|
||||
|
||||
if (hasEscapedAnnotation)
|
||||
{
|
||||
IEdmBooleanConstantExpression booleanConstant = new EdmBooleanConstant(true);
|
||||
IEdmTerm term = CommunityVocabularyModel.UrlEscapeFunctionTerm;
|
||||
EdmVocabularyAnnotation annotation = new EdmVocabularyAnnotation(function, term, booleanConstant);
|
||||
annotation.SetSerializationLocation(model, EdmVocabularyAnnotationSerializationLocation.Inline);
|
||||
model.SetVocabularyAnnotation(annotation);
|
||||
}
|
||||
|
||||
OpenApiConvertSettings settings = new OpenApiConvertSettings
|
||||
{
|
||||
EnableUriEscapeFunctionCall = enableEscaped
|
||||
};
|
||||
ODataContext context = new ODataContext(model, settings);
|
||||
|
||||
// Act
|
||||
var pathItems = context.CreatePathItems();
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(pathItems);
|
||||
Assert.Equal(3, pathItems.Count);
|
||||
|
||||
Assert.Contains("/Customers", pathItems.Keys);
|
||||
Assert.Contains("/Customers({ID})", pathItems.Keys);
|
||||
Assert.Contains(expected, pathItems.Keys);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue