Skip to content

Commit 01f875c

Browse files
authored
Throw exception if the function call without providing the parameter … (#2306)
* Throw exception if the function call without providing the parameter value * Make the ASP.NET Classic test pass
1 parent d905bd1 commit 01f875c

4 files changed

Lines changed: 85 additions & 12 deletions

File tree

src/Microsoft.AspNet.OData.Shared/Common/SRResources.Designer.cs

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Microsoft.AspNet.OData.Shared/Common/SRResources.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,9 @@
222222
<data name="CannotRecognizeNodeType" xml:space="preserve">
223223
<value>'{0}' does not recognize the node with type '{1}'.</value>
224224
</data>
225+
<data name="MissingConvertNode" xml:space="preserve">
226+
<value>Missing the value of the parameter '{0}' in the function '{1}' calling.</value>
227+
</data>
225228
<data name="TargetKindNotImplemented" xml:space="preserve">
226229
<value>'{0}' of kind '{1}' is not implemented.</value>
227230
</data>

src/Microsoft.AspNet.OData.Shared/Routing/ODataPathSegmentHandler.cs

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,8 @@ public override void Handle(OperationImportSegment segment)
157157
{
158158
_navigationSource = segment.EntitySet;
159159

160-
IEdmActionImport actionImport = segment.OperationImports.Single() as IEdmActionImport;
160+
IEdmOperationImport operationImport = segment.OperationImports.Single();
161+
IEdmActionImport actionImport = operationImport as IEdmActionImport;
161162

162163
if (actionImport != null)
163164
{
@@ -166,14 +167,14 @@ public override void Handle(OperationImportSegment segment)
166167
}
167168
else
168169
{
170+
IEdmFunctionImport function = (IEdmFunctionImport)operationImport;
171+
169172
_pathTemplate.Add(ODataSegmentKinds.UnboundFunction); // unbound function
170173

171174
// Translate the nodes in ODL path to string literals as parameter of UnboundFunctionPathSegment.
172175
Dictionary<string, string> parameterValues = segment.Parameters.ToDictionary(
173176
parameterValue => parameterValue.Name,
174-
parameterValue => TranslateNode(parameterValue.Value));
175-
176-
IEdmFunctionImport function = (IEdmFunctionImport)segment.OperationImports.Single();
177+
parameterValue => TranslateNode(parameterValue.Value, function.Name, parameterValue.Name));
177178

178179
IEnumerable<string> parameters = parameterValues.Select(v => String.Format(CultureInfo.InvariantCulture, "{0}={1}", v.Key, v.Value));
179180
string literal = String.Format(CultureInfo.InvariantCulture, "{0}({1})", function.Name, String.Join(",", parameters));
@@ -190,7 +191,8 @@ public override void Handle(OperationSegment segment)
190191
{
191192
_navigationSource = segment.EntitySet;
192193

193-
IEdmAction action = segment.Operations.Single() as IEdmAction;
194+
IEdmOperation edmOperation = segment.Operations.Single();
195+
IEdmAction action = edmOperation as IEdmAction;
194196

195197
if (action != null)
196198
{
@@ -199,16 +201,15 @@ public override void Handle(OperationSegment segment)
199201
}
200202
else
201203
{
204+
IEdmFunction function = (IEdmFunction)edmOperation;
202205
_pathTemplate.Add(ODataSegmentKinds.Function); // function
203206

204207
// Translate the nodes in ODL path to string literals as parameter of BoundFunctionPathSegment.
205208
Dictionary<string, string> parameterValues = segment.Parameters.ToDictionary(
206209
parameterValue => parameterValue.Name,
207-
parameterValue => TranslateNode(parameterValue.Value));
210+
parameterValue => TranslateNode(parameterValue.Value, function.Name, parameterValue.Name));
208211

209212
// TODO: refactor the function literal for parameter alias
210-
IEdmFunction function = (IEdmFunction)segment.Operations.Single();
211-
212213
IEnumerable<string> parameters = parameterValues.Select(v => String.Format(CultureInfo.InvariantCulture, "{0}={1}", v.Key, v.Value));
213214
string literal = String.Format(CultureInfo.InvariantCulture, "{0}({1})", function.FullName(), String.Join(",", parameters));
214215

@@ -414,9 +415,17 @@ private static string TranslateKeySegmentValue(object value)
414415
return ODataUriUtils.ConvertToUriLiteral(value, ODataVersion.V4);
415416
}
416417

417-
private static string TranslateNode(object node)
418+
private static string TranslateNode(object node, string functionName, string parameterName)
418419
{
419-
Contract.Assert(node != null);
420+
// If the function parameter is null, for example myFunction(param=null),
421+
// the input node here is not null, it is a contant node with a value as "null".
422+
// However, if a function call (or key) using parameter alias but without providing the parameter alias value,
423+
// the input node here is a null.
424+
if (node == null)
425+
{
426+
// We can't throw ODataException here because ODataException will be caught and return 404 response with empty message.
427+
throw new InvalidOperationException(Error.Format(SRResources.MissingConvertNode, parameterName, functionName));
428+
}
420429

421430
ConstantNode constantNode = node as ConstantNode;
422431
if (constantNode != null)
@@ -440,7 +449,7 @@ private static string TranslateNode(object node)
440449
ConvertNode convertNode = node as ConvertNode;
441450
if (convertNode != null)
442451
{
443-
return TranslateNode(convertNode.Source);
452+
return TranslateNode(convertNode.Source, functionName, parameterName);
444453
}
445454

446455
ParameterAliasNode parameterAliasNode = node as ParameterAliasNode;

test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Formatter/ODataFunctionTests.cs

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,47 @@ public async Task FunctionWorks_WithParameters_OnlyWithAttributeRouting_ForUnTyp
205205
Assert.True((bool)result["value"]);
206206
}
207207

208+
[Fact]
209+
public void FunctionCallingFails_WithParametersValueMissing()
210+
{
211+
// Arrange
212+
string complexFunction = "ComplexFunction(address=@p)";
213+
string requestUri = BaseAddress + "odata/FCustomers(2)/NS." + complexFunction;
214+
215+
#if NETCORE
216+
// Act
217+
AggregateException exception = Assert.Throws<AggregateException>(() => _client.GetAsync(requestUri).Result);
218+
219+
// Assert
220+
Assert.Contains("Missing the value of the parameter 'address' in the function 'ComplexFunction' calling", exception.Message);
221+
#else
222+
// Act
223+
var response = _client.GetAsync(requestUri).Result;
224+
225+
// Assert
226+
Assert.Equal(System.Net.HttpStatusCode.InternalServerError, response.StatusCode);
227+
228+
string result = response.Content.ReadAsStringAsync().Result;
229+
Assert.Contains("Missing the value of the parameter 'address' in the function 'ComplexFunction' calling", result);
230+
#endif
231+
}
232+
233+
[Fact]
234+
public async Task FunctionCallingSuccess_WithParametersValueAsNull()
235+
{
236+
// Arrange
237+
string complexFunction = "ComplexFunction(address=null)";
238+
string requestUri = BaseAddress + "odata/FCustomers(99)/NS." + complexFunction;
239+
240+
// Act
241+
var response = await _client.GetAsync(requestUri);
242+
243+
// Assert
244+
ExceptionAssert.DoesNotThrow(() => response.EnsureSuccessStatusCode());
245+
dynamic result = JObject.Parse(await response.Content.ReadAsStringAsync());
246+
Assert.False((bool)result["value"]);
247+
}
248+
208249
[Fact]
209250
public async Task Response_Includes_FunctionLinkForFeed_WithAcceptHeader()
210251
{
@@ -265,7 +306,7 @@ private static IEdmModel GetUnTypedEdmModel()
265306
model.AddElement(container);
266307
container.AddEntitySet("FCustomers", customer);
267308

268-
EdmComplexTypeReference complexType = new EdmComplexTypeReference(address, isNullable: false);
309+
EdmComplexTypeReference complexType = new EdmComplexTypeReference(address, isNullable: true);
269310
EdmCollectionTypeReference complexCollectionType = new EdmCollectionTypeReference(new EdmCollectionType(complexType));
270311

271312
EdmEnumTypeReference enumType = new EdmEnumTypeReference(colorEnum, isNullable: false);
@@ -422,6 +463,12 @@ public bool EnumCollectionFunction(int key, [FromODataUri] EdmEnumObjectCollecti
422463
[ODataRoute("UnboundComplexFunction(key={key},address={address})")]
423464
public bool ComplexFunction(int key, [FromODataUri] EdmComplexObject address)
424465
{
466+
if (key == 99)
467+
{
468+
Assert.Null(address);
469+
return false;
470+
}
471+
425472
Assert.NotNull(address);
426473
dynamic result = address;
427474
Assert.Equal("NS.Address", address.GetEdmType().FullName());

0 commit comments

Comments
 (0)