public void AddCustomFunction_FunctionNameCannotBeNull() { FunctionSignatureWithReturnType customFunctionSignature = new FunctionSignatureWithReturnType(EdmCoreModel.Instance.GetBoolean(false), EdmCoreModel.Instance.GetInt32(false)); Action addNullFunctionNameAction = () => CustomUriFunctions.AddCustomUriFunction(null, customFunctionSignature); addNullFunctionNameAction.ShouldThrow <ArgumentNullException>(); }
public void AddCustomFunction_FunctionNameCannotBeEmptyString() { FunctionSignatureWithReturnType customFunctionSignature = new FunctionSignatureWithReturnType(EdmCoreModel.Instance.GetBoolean(false), EdmCoreModel.Instance.GetInt32(false)); Action addCustomFunctionSignature = () => CustomUriFunctions.AddCustomUriFunction(string.Empty, customFunctionSignature); Assert.Throws <ArgumentNullException>("functionName", addCustomFunctionSignature); }
/// <summary xml:lang="en"> /// Registering other custom method to the OData query parser /// </summary> /// <summary xml:lang="cs"> /// Registrace dodatečné vlastní metody do OData query parseru /// </summary> public static void RegisterCustomFunction(MethodInfo methodInfo, string functionName = null) { var returnType = TypeToReference(methodInfo.ReturnType); var args = methodInfo.GetParameters() .Select(x => TypeToReference(x.ParameterType)) .ToArray(); var signature = new FunctionSignatureWithReturnType(returnType, args); ODataUriFunctions.AddCustomUriFunction(functionName ?? methodInfo.Name.ToLower(), signature, methodInfo); }
public void AddCustomFunction_CannotAddFunctionSignatureWithNullReturnType() { FunctionSignatureWithReturnType customFunctionSignatureWithNullReturnType = new FunctionSignatureWithReturnType(null, EdmCoreModel.Instance.GetInt32(false)); Action addCustomFunctionSignature = () => CustomUriFunctions.AddCustomUriFunction("my.customFunctionWithNoReturnType", customFunctionSignatureWithNullReturnType); addCustomFunctionSignature.ShouldThrow <ArgumentNullException>(); }
public void AddCustomFunction_CannotAddFunctionSignatureWithNullReturnType() { FunctionSignatureWithReturnType customFunctionSignatureWithNullReturnType = new FunctionSignatureWithReturnType(null, EdmCoreModel.Instance.GetInt32(false)); Action addCustomFunctionSignature = () => CustomUriFunctions.AddCustomUriFunction("my.customFunctionWithNoReturnType", customFunctionSignatureWithNullReturnType); Assert.Throws <ArgumentNullException>("functionSignatureWithReturnType must contain a return type", addCustomFunctionSignature); }
public void RemoveCustomFunction_CannotRemoveFunctionWhichDoesntExist_ByNameAndSignature() { string customFunctionName = "my.ExistingCustomFunction"; FunctionSignatureWithReturnType customFunctionSignature = new FunctionSignatureWithReturnType(EdmCoreModel.Instance.GetDouble(false), EdmCoreModel.Instance.GetBoolean(false)); // Test bool isRemoveSucceeded = CustomUriFunctions.RemoveCustomUriFunction(customFunctionName, customFunctionSignature); // Assert Assert.False(isRemoveSucceeded); }
public void RemoveCustomFunction_FunctionSignatureWithoutAReturnType() { FunctionSignatureWithReturnType existingCustomFunctionSignature = new FunctionSignatureWithReturnType(null, EdmCoreModel.Instance.GetBoolean(false)); // Test Action removeFunction = () => CustomUriFunctions.RemoveCustomUriFunction("FunctionName", existingCustomFunctionSignature); // Assert Assert.Throws <ArgumentNullException>("functionSignatureWithReturnType must contain a return type", removeFunction); }
public void TryFindFunctionSignatureWithNumberOfArgumentsReturnsNothingfNoMatchFound() { // regression test for: FunctionCallBinder should validate that the number of parameters matches for canonical function calls on open properties FunctionSignatureWithReturnType[] functions = new FunctionSignatureWithReturnType[] { new FunctionSignatureWithReturnType(EdmCoreModel.Instance.GetDateTimeOffset(true), new IEdmTypeReference[] { EdmCoreModel.Instance.GetString(true) }), }; // we specifically want to find just the first function that matches the number of arguments, we don't care about // ambiguity here because we're already in an ambiguous case where we don't know what kind of types // those arguments are. functions.FirstOrDefault(candidateFunction => candidateFunction.ArgumentTypes.Count() == 2).Should().BeNull(); }
/// <summary> /// Promotes types of arguments to match signature if possible. /// </summary> /// <param name="signature">The signature to match the types to.</param> /// <param name="argumentNodes">The types to promote.</param> internal static void TypePromoteArguments(FunctionSignatureWithReturnType signature, List <QueryNode> argumentNodes) { // Convert all argument nodes to the best signature argument type Debug.Assert(signature.ArgumentTypes.Length == argumentNodes.Count, "The best signature match doesn't have the same number of arguments."); for (int i = 0; i < argumentNodes.Count; i++) { Debug.Assert(argumentNodes[i] is SingleValueNode, "We should have already verified that all arguments are single values."); SingleValueNode argumentNode = (SingleValueNode)argumentNodes[i]; IEdmTypeReference signatureArgumentType = signature.ArgumentTypes[i]; Debug.Assert(signatureArgumentType.IsODataPrimitiveTypeKind(), "Only primitive types should be able to get here."); argumentNodes[i] = MetadataBindingUtils.ConvertToTypeIfNeeded(argumentNode, signatureArgumentType); } }
/// <summary> /// Promotes types of arguments to match signature if possible. /// </summary> /// <param name="signature">The signature to match the types to.</param> /// <param name="argumentNodes">The types to promote.</param> internal static void TypePromoteArguments(FunctionSignatureWithReturnType signature, List<QueryNode> argumentNodes) { // Convert all argument nodes to the best signature argument type Debug.Assert(signature.ArgumentTypes.Length == argumentNodes.Count, "The best signature match doesn't have the same number of arguments."); for (int i = 0; i < argumentNodes.Count; i++) { Debug.Assert(argumentNodes[i] is SingleValueNode, "We should have already verified that all arguments are single values."); SingleValueNode argumentNode = (SingleValueNode)argumentNodes[i]; IEdmTypeReference signatureArgumentType = signature.ArgumentTypes[i]; Debug.Assert(signatureArgumentType.IsODataPrimitiveTypeKind(), "Only primitive types should be able to get here."); argumentNodes[i] = MetadataBindingUtils.ConvertToTypeIfNeeded(argumentNode, signatureArgumentType); } }
public void TryFundFunctionSignatureAllowsAmbiguousFunctionCall() { FunctionSignatureWithReturnType first = new FunctionSignatureWithReturnType(EdmCoreModel.Instance.GetDateTimeOffset(true), new IEdmTypeReference[] { EdmCoreModel.Instance.GetString(true) }); FunctionSignatureWithReturnType second = new FunctionSignatureWithReturnType(EdmCoreModel.Instance.GetDateTimeOffset(true), new IEdmTypeReference[] { EdmCoreModel.Instance.GetInt32(true) }); FunctionSignatureWithReturnType[] functions = new FunctionSignatureWithReturnType[] { first, second }; // we specifically want to find just the first function that matches the number of arguments, we don't care about // ambiguity here because we're already in an ambiguous case where we don't know what kind of types // those arguments are. functions.FirstOrDefault(candidateFunction => candidateFunction.ArgumentTypes.Count() == 1).Should().Be(first); }
/// <summary> /// This is a shortcut of adding the custom FunctionSignature through 'CustomUriFunctions' class and /// binding the function name to it's MethodInfo through 'UriFunctionsBinder' class. /// See these classes documentations. /// In case of an exception, both operations(adding the signature and binding the function) will be undone. /// </summary> /// <param name="functionName">The uri function name that appears in the OData request uri.</param> /// <param name="functionSignature">The new custom function signature.</param> /// <param name="methodInfo">The MethodInfo to bind the given function name.</param> /// <exception cref="Exception">Any exception thrown by 'CustomUriFunctions.AddCustomUriFunction' and 'UriFunctionBinder.BindUriFunctionName' methods.</exception> public static void AddCustomUriFunction(string functionName, FunctionSignatureWithReturnType functionSignature, MethodInfo methodInfo) { try { // Add to OData.Libs function signature CustomUriFunctions.AddCustomUriFunction(functionName, functionSignature); // Bind the method to it's MethoInfo UriFunctionsBinder.BindUriFunctionName(functionName, methodInfo); } catch { // Clear in case of excpetion RemoveCustomUriFunction(functionName, functionSignature, methodInfo); throw; } }
public void RemoveCustomFunction_ShouldRemoveAnExistingFunction_ByName() { string customFunctionName = "my.ExistingCustomFunction"; // Prepare FunctionSignatureWithReturnType existingCustomFunctionSignature = new FunctionSignatureWithReturnType(EdmCoreModel.Instance.GetDouble(false), EdmCoreModel.Instance.GetBoolean(false)); CustomUriFunctions.AddCustomUriFunction(customFunctionName, existingCustomFunctionSignature); Assert.True(GetCustomFunctionSignaturesOrNull(customFunctionName)[0].Equals(existingCustomFunctionSignature)); // Test bool isRemoveSucceeded = CustomUriFunctions.RemoveCustomUriFunction(customFunctionName); // Assert Assert.True(isRemoveSucceeded); Assert.Null(GetCustomFunctionSignaturesOrNull(customFunctionName)); }
public static void RegisterFunctions(IEdmTypeReference enumRef) { if (!registred) { lock (locker) { if (!registred) { RegisterFunction(typeof(BoundFunctions), nameof(BoundFunctions.GetBestOrders)); RegisterFunction(typeof(SelectExpandTestCustomerWithCustom), nameof(SelectExpandTestCustomerWithCustom.GetInstanceBestOrders)); RegisterFunction(typeof(SelectExpandTestCustomerWithCustom), nameof(SelectExpandTestCustomerWithCustom.GetInt)); RegisterFunction(typeof(SelectExpandTestCustomerWithCustom), nameof(SelectExpandTestCustomerWithCustom.ConvertEnum)); var s = new FunctionSignatureWithReturnType(EdmLibHelpers.GetEdmPrimitiveTypeReferenceOrNull(typeof(double?)), enumRef); ODataUriFunctions.AddCustomUriFunction("convert_enum", s, typeof(BoundFunctions).GetMethods().Where(m => m.Name == nameof(BoundFunctions.ConvertEnum)).First()); registred = true; } } } }
public void ParseWithCustomUriFunction() { try { FunctionSignatureWithReturnType myStringFunction = new FunctionSignatureWithReturnType(EdmCoreModel.Instance.GetBoolean(true), EdmCoreModel.Instance.GetString(true), EdmCoreModel.Instance.GetString(true)); // Add a custom uri function CustomUriFunctions.AddCustomUriFunction("mystringfunction", myStringFunction); var fullUri = new Uri("http://www.odata.com/OData/People" + "?$filter=mystringfunction(Name, 'BlaBla')"); ODataUriParser parser = new ODataUriParser(HardCodedTestModel.TestModel, new Uri("http://www.odata.com/OData/"), fullUri); var startsWithArgs = parser.ParseFilter().Expression.ShouldBeSingleValueFunctionCallQueryNode("mystringfunction").Parameters.ToList(); startsWithArgs[0].ShouldBeSingleValuePropertyAccessQueryNode(HardCodedTestModel.GetPersonNameProp()); startsWithArgs[1].ShouldBeConstantQueryNode("BlaBla"); } finally { Assert.True(CustomUriFunctions.RemoveCustomUriFunction("mystringfunction")); } }
/// <summary> /// Finds the signature that best matches the arguments /// </summary> /// <param name="functionName">The name of the function</param> /// <param name="argumentNodes">The nodes of the arguments, can be new {null,null}.</param> /// <param name="signatures">The signatures to match against</param> /// <returns>Returns the matching signature or throws</returns> internal static FunctionSignatureWithReturnType MatchSignatureToUriFunction(string functionName, SingleValueNode[] argumentNodes, FunctionSignatureWithReturnType[] signatures) { FunctionSignatureWithReturnType signature; IEdmTypeReference[] argumentTypes = argumentNodes.Select(s => s.TypeReference).ToArray(); // Handle the cases where we don't have type information (null literal, open properties) for ANY of the arguments int argumentCount = argumentTypes.Length; if (argumentTypes.All(a => a == null) && argumentCount > 0) { // we specifically want to find just the first function that matches the number of arguments, we don't care about // ambiguity here because we're already in an ambiguous case where we don't know what kind of types // those arguments are. signature = signatures.FirstOrDefault(candidateFunction => candidateFunction.ArgumentTypes.Count() == argumentCount); if (signature == null) { throw new ODataException(ODataErrorStrings.FunctionCallBinder_CannotFindASuitableOverload(functionName, argumentTypes.Count())); } else { // in this case we can't assert the return type, we can only assert that a function exists... so // we need to set the return type to null. signature = new FunctionSignatureWithReturnType(null, signature.ArgumentTypes); } } else { signature = TypePromotionUtils.FindBestFunctionSignature(signatures, argumentNodes); if (signature == null) { throw new ODataException(ODataErrorStrings.MetadataBinder_NoApplicableFunctionFound( functionName, UriFunctionsHelper.BuildFunctionSignatureListDescription(functionName, signatures))); } } return(signature); }
public void RemoveCustomFunction_RemoveFunctionWithSameNameAndSignature_OtherOverloadsExists() { string customFunctionName = "my.ExistingCustomFunction"; try { // Prepare FunctionSignatureWithReturnType existingCustomFunctionSignature = new FunctionSignatureWithReturnType(EdmCoreModel.Instance.GetDouble(false), EdmCoreModel.Instance.GetBoolean(false)); CustomUriFunctions.AddCustomUriFunction(customFunctionName, existingCustomFunctionSignature); FunctionSignatureWithReturnType existingCustomFunctionSignatureTwo = new FunctionSignatureWithReturnType(EdmCoreModel.Instance.GetBoolean(false), EdmCoreModel.Instance.GetDate(false)); CustomUriFunctions.AddCustomUriFunction(customFunctionName, existingCustomFunctionSignatureTwo); // Validate that the two overloads as GetCustomFunctionSignaturesOrNull(customFunctionName). All(funcSignature => funcSignature.Equals(existingCustomFunctionSignature) || funcSignature.Equals(existingCustomFunctionSignatureTwo)). Should().BeTrue(); // Remove the first overload, second overload should not be removed bool isRemoveSucceeded = CustomUriFunctions.RemoveCustomUriFunction(customFunctionName, existingCustomFunctionSignature); // Assert isRemoveSucceeded.Should().BeTrue(); FunctionSignatureWithReturnType[] overloads = GetCustomFunctionSignaturesOrNull(customFunctionName); overloads.Length.Should().Be(1); overloads[0].Should().Be(existingCustomFunctionSignatureTwo); } finally { // Clean up cache CustomUriFunctions.RemoveCustomUriFunction(customFunctionName); } }
public void ParseWithCustomUriFunction_AddAsOverloadToBuiltIn() { FunctionSignatureWithReturnType customStartWithFunctionSignature = new FunctionSignatureWithReturnType(EdmCoreModel.Instance.GetBoolean(true), EdmCoreModel.Instance.GetString(true), EdmCoreModel.Instance.GetInt32(true)); try { // Add with override 'true' CustomUriFunctions.AddCustomUriFunction("startswith", customStartWithFunctionSignature); var fullUri = new Uri("http://www.odata.com/OData/People" + "?$filter=startswith(Name, 66)"); ODataUriParser parser = new ODataUriParser(HardCodedTestModel.TestModel, new Uri("http://www.odata.com/OData/"), fullUri); var startsWithArgs = parser.ParseFilter().Expression.ShouldBeSingleValueFunctionCallQueryNode("startswith").Parameters.ToList(); startsWithArgs[0].ShouldBeSingleValuePropertyAccessQueryNode(HardCodedTestModel.GetPersonNameProp()); startsWithArgs[1].ShouldBeConstantQueryNode(66); } finally { Assert.True(CustomUriFunctions.RemoveCustomUriFunction("startswith")); } }
public void ParseWithExactMatchCustomUriFunction_EnableCaseInsensitive_ShouldWorkForMultipleEquivalentArgumentsMatches() { string lowerCaseName = "myfunction"; string upperCaseName = lowerCaseName.ToUpper(); FunctionSignatureWithReturnType myStringFunction = new FunctionSignatureWithReturnType(EdmCoreModel.Instance.GetBoolean(true), EdmCoreModel.Instance.GetString(true), EdmCoreModel.Instance.GetString(true)); // Add two customer uri functions with same argument types, with names different in cases. CustomUriFunctions.AddCustomUriFunction(lowerCaseName, myStringFunction); CustomUriFunctions.AddCustomUriFunction(upperCaseName, myStringFunction); string rootUri = "http://www.odata.com/OData/"; string uriTemplate = rootUri + "People?$filter={0}(Name,'BlaBla')"; try { foreach (string functionName in new string[] { lowerCaseName, upperCaseName }) { // Uri with case-sensitive function names referring to equivalent-argument-typed functions, // should work for resolver with case insensitive enabled. var fullUri = new Uri(string.Format(uriTemplate, functionName)); ODataUriParser parser = new ODataUriParser(HardCodedTestModel.TestModel, new Uri(rootUri), fullUri); parser.Resolver.EnableCaseInsensitive = true; var startsWithArgs = parser.ParseFilter().Expression.ShouldBeSingleValueFunctionCallQueryNode(functionName) .Parameters.ToList(); startsWithArgs[0].ShouldBeSingleValuePropertyAccessQueryNode(HardCodedTestModel.GetPersonNameProp()); startsWithArgs[1].ShouldBeConstantQueryNode("BlaBla"); } } finally { Assert.True(CustomUriFunctions.RemoveCustomUriFunction(lowerCaseName)); Assert.True(CustomUriFunctions.RemoveCustomUriFunction(upperCaseName)); } }
/// <summary> /// Finds the signature that best matches the arguments /// </summary> /// <param name="functionName">The name of the function</param> /// <param name="argumentNodes">The nodes of the arguments, can be new {null,null}.</param> /// <param name="signatures">The signatures to match against</param> /// <returns>Returns the matching signature or throws</returns> internal static FunctionSignatureWithReturnType MatchSignatureToBuiltInFunction(string functionName, SingleValueNode[] argumentNodes, FunctionSignatureWithReturnType[] signatures) { FunctionSignatureWithReturnType signature; IEdmTypeReference[] argumentTypes = argumentNodes.Select(s => s.TypeReference).ToArray(); // Handle the cases where we don't have type information (null literal, open properties) for ANY of the arguments int argumentCount = argumentTypes.Length; if (argumentTypes.All(a => a == null) && argumentCount > 0) { // we specifically want to find just the first function that matches the number of arguments, we don't care about // ambiguity here because we're already in an ambiguous case where we don't know what kind of types // those arguments are. signature = signatures.FirstOrDefault(candidateFunction => candidateFunction.ArgumentTypes.Count() == argumentCount); if (signature == null) { throw new ODataException(ODataErrorStrings.FunctionCallBinder_CannotFindASuitableOverload(functionName, argumentTypes.Count())); } else { // in this case we can't assert the return type, we can only assert that a function exists... so // we need to set the return type to null. signature = new FunctionSignatureWithReturnType(null, signature.ArgumentTypes); } } else { signature = TypePromotionUtils.FindBestFunctionSignature(signatures, argumentNodes); if (signature == null) { throw new ODataException(ODataErrorStrings.MetadataBinder_NoApplicableFunctionFound( functionName, BuiltInFunctions.BuildFunctionSignatureListDescription(functionName, signatures))); } } return signature; }