/// <summary> /// Initialises a new instance of the <see cref="OrderByProperty"/> class. /// </summary> /// <param name="rawValue">The raw value.</param> /// <param name="model">The model.</param> /// <exception cref="ArgumentNullException">Thrown if <paramref name="rawValue"/> or <paramref name="model"/> are null.</exception> /// <exception cref="ODataException">Thrown if there is an error parsing the <paramref name="rawValue"/>.</exception> internal OrderByProperty(string rawValue, EdmComplexType model) { RawValue = rawValue ?? throw new ArgumentNullException(nameof(rawValue)); if (model is null) { throw new ArgumentNullException(nameof(model)); } if (rawValue.IndexOf(' ') == -1) { PropertyPath = PropertyPath.For(rawValue, model); } else { PropertyPath = PropertyPath.For(rawValue.SubstringBefore(' '), model); if (rawValue.EndsWith(" asc", StringComparison.Ordinal)) { Direction = OrderByDirection.Ascending; } else if (rawValue.EndsWith(" desc", StringComparison.Ordinal)) { Direction = OrderByDirection.Descending; } else { throw ODataException.BadRequest(ExceptionMessage.InvalidOrderByDirection(rawValue.SubstringAfter(' '), PropertyPath.Property.Name), "$orderby"); } } }
internal static UnaryOperatorKind ToUnaryOperatorKind(this string operatorType) { switch (operatorType) { case "not": return(UnaryOperatorKind.Not); default: throw ODataException.BadRequest(ExceptionMessage.InvalidOperator(operatorType), "$filter"); } }
/// <summary> /// Gets the <see cref="EdmProperty"/> declared in this type with the specified name. /// </summary> /// <param name="name">The name of the property.</param> /// <returns>The <see cref="EdmProperty"/> declared in this type with the specified name.</returns> /// <exception cref="ODataException">The type does not contain a property with the specified name.</exception> public EdmProperty GetProperty(string name) { for (int i = 0; i < Properties.Count; i++) { EdmProperty property = Properties[i]; if (property.Name.Equals(name, StringComparison.Ordinal)) { return(property); } } throw ODataException.BadRequest(ExceptionMessage.EdmTypeDoesNotContainProperty(FullName, name), FullName); }
/// <summary> /// Validates the specified query options. /// </summary> /// <param name="queryOptions">The query options.</param> /// <param name="validationSettings">The validation settings.</param> /// <exception cref="ODataException">Thrown if the validation fails.</exception> internal static void Validate(ODataQueryOptions queryOptions, ODataValidationSettings validationSettings) { if (queryOptions.RawValues.Top is null) { return; } if ((validationSettings.AllowedQueryOptions & AllowedQueryOptions.Top) != AllowedQueryOptions.Top) { throw ODataException.NotImplemented("The query option $top is not implemented by this service", "$top"); } if (queryOptions.Top.Value < 0 || queryOptions.Top.Value > validationSettings.MaxTop) { throw ODataException.BadRequest($"The integer value for $top is invalid, it must be an integer greater than zero and below the max value of {validationSettings.MaxTop.ToString(CultureInfo.InvariantCulture)} allowed by this service", "$top"); } }
/// <summary> /// Validates the specified query options. /// </summary> /// <param name="queryOptions">The query options.</param> /// <param name="validationSettings">The validation settings.</param> /// <exception cref="ODataException">Thrown if the validation fails.</exception> internal static void Validate(ODataQueryOptions queryOptions, ODataValidationSettings validationSettings) { if (queryOptions.RawValues.Skip is null) { return; } if ((validationSettings.AllowedQueryOptions & AllowedQueryOptions.Skip) != AllowedQueryOptions.Skip) { throw ODataException.NotImplemented("The query option $skip is not implemented by this service", "$skip"); } if (queryOptions.Skip.Value < 0) { throw ODataException.BadRequest("The value for OData query $skip must be a non-negative numeric value", "$skip"); } }
private static int?ParseInt(string rawValue) { if (rawValue is null) { return(null); } string value = rawValue.SubstringAfter('='); if (int.TryParse(value, out int integer)) { return(integer); } string queryOption = rawValue.SubstringBefore('='); throw ODataException.BadRequest(ExceptionMessage.QueryOptionValueMustBePositiveInteger(queryOption), queryOption); }
/// <summary> /// Validates the specified query options. /// </summary> /// <param name="queryOptions">The query options.</param> /// <param name="validationSettings">The validation settings.</param> /// <exception cref="ODataException">Thrown if the validation fails.</exception> internal static void Validate(ODataQueryOptions queryOptions, ODataValidationSettings validationSettings) { if (queryOptions.RawValues.Count is null) { return; } if ((validationSettings.AllowedQueryOptions & AllowedQueryOptions.Count) != AllowedQueryOptions.Count) { throw ODataException.NotImplemented("The query option $count is not implemented by this service", "$count"); } if (queryOptions.RawValues.Count != "$count=true" && queryOptions.RawValues.Count != "$count=false") { throw ODataException.BadRequest("The supplied value for OData query $count is invalid, valid options are 'true' and 'false'", "$count"); } }
internal QueryNode ParseQueryNode(Lexer lexer) { while (lexer.MoveNext()) { Token token = lexer.Current; switch (token.TokenType) { case TokenType.And: case TokenType.Or: _nextBinaryOperatorKind = token.Value.ToBinaryOperatorKind(); UpdateExpressionTree(); break; default: _tokens.Enqueue(token); break; } } _nextBinaryOperatorKind = BinaryOperatorKind.None; UpdateExpressionTree(); if (_groupingDepth != 0 || _nodeStack.Count != 1) { throw ODataException.BadRequest(ExceptionMessage.UnableToParseFilter("an extra opening or missing closing parenthesis may be present"), "$filter"); } QueryNode node = _nodeStack.Pop(); if (node is BinaryOperatorNode binaryNode && binaryNode.Right is null) { throw ODataException.BadRequest(ExceptionMessage.UnableToParseFilter($"the binary operator {binaryNode.OperatorKind.ToString()} has no right node"), "$filter"); } return(node); }
private QueryNode ParseQueryNode() { if (_tokens.Count == 0) { throw ODataException.BadRequest(ExceptionMessage.UnableToParseFilter("an incomplete filter has been specified"), "$filter"); } QueryNode node; switch (_tokens.Peek().TokenType) { case TokenType.FunctionName: node = ParseFunctionCallNode(); break; case TokenType.OpenParentheses: _groupingDepth++; _tokens.Dequeue(); node = ParseQueryNode(); break; case TokenType.PropertyName: node = ParsePropertyAccessNode(); break; case TokenType.UnaryOperator: Token token = _tokens.Dequeue(); node = ParseQueryNode(); node = new UnaryOperatorNode(node, token.Value.ToUnaryOperatorKind()); break; default: throw ODataException.BadRequest(ExceptionMessage.UnableToParseFilter($"unexpected {_tokens.Peek().Value}", _tokens.Peek().Position), "$filter"); } return(node); }
internal bool MoveNext() { if (_content.Length == _position) { return(false); } for (int i = 0; i < s_tokenDefinitions.Length; i++) { TokenDefinition tokenDefinition = s_tokenDefinitions[i]; Match match = tokenDefinition.Regex.Match(_content, _position); if (match.Success) { if (tokenDefinition.Ignore) { _position += match.Length; i = -1; continue; } Current = tokenDefinition.CreateToken(match, _position); _position += match.Length; return(true); } } if (_content.Length != _position) { throw ODataException.BadRequest(ExceptionMessage.GenericUnableToParseFilter, "$filter"); } return(false); }
/// <summary> /// Initialises a new instance of the <see cref="ODataRawQueryOptions"/> class. /// </summary> /// <param name="rawQuery">The raw query.</param> /// <exception cref="ArgumentNullException">Thrown if <paramref name="rawQuery"/> is null.</exception> /// <exception cref="ODataException">Thrown if an error occurs parsing the <paramref name="rawQuery"/>.</exception> internal ODataRawQueryOptions(string rawQuery) { if (rawQuery is null) { throw new ArgumentNullException(nameof(rawQuery)); } // Any + signs we want in the data should have been encoded as %2B, // so do the replace first otherwise we replace legitemate + signs! _rawQuery = rawQuery.Replace('+', ' '); if (_rawQuery.Length > 0) { foreach (string queryOption in _rawQuery.Slice('&', _rawQuery.IndexOf('?') + 1)) { // Decode the chunks to prevent splitting the query on an '&' which is actually part of a string value string rawQueryOption = Uri.UnescapeDataString(queryOption); if (rawQueryOption.StartsWith("$select=", StringComparison.Ordinal)) { if (rawQueryOption.Equals("$select=", StringComparison.Ordinal)) { throw ODataException.BadRequest(ExceptionMessage.QueryOptionValueCannotBeEmpty("$select"), "$select"); } Select = rawQueryOption; } else if (rawQueryOption.StartsWith("$filter=", StringComparison.Ordinal)) { if (rawQueryOption.Equals("$filter=", StringComparison.Ordinal)) { throw ODataException.BadRequest(ExceptionMessage.QueryOptionValueCannotBeEmpty("$filter"), "$filter"); } Filter = rawQueryOption; } else if (rawQueryOption.StartsWith("$orderby=", StringComparison.Ordinal)) { if (rawQueryOption.Equals("$orderby=", StringComparison.Ordinal)) { throw ODataException.BadRequest(ExceptionMessage.QueryOptionValueCannotBeEmpty("$orderby"), "$orderby"); } OrderBy = rawQueryOption; } else if (rawQueryOption.StartsWith("$skip=", StringComparison.Ordinal)) { if (rawQueryOption.Equals("$skip=", StringComparison.Ordinal)) { throw ODataException.BadRequest(ExceptionMessage.QueryOptionValueCannotBeEmpty("$skip"), "$skip"); } Skip = rawQueryOption; } else if (rawQueryOption.StartsWith("$top=", StringComparison.Ordinal)) { if (rawQueryOption.Equals("$top=", StringComparison.Ordinal)) { throw ODataException.BadRequest(ExceptionMessage.QueryOptionValueCannotBeEmpty("$top"), "$top"); } Top = rawQueryOption; } else if (rawQueryOption.StartsWith("$count=", StringComparison.Ordinal)) { if (rawQueryOption.Equals("$count=", StringComparison.Ordinal)) { throw ODataException.BadRequest(ExceptionMessage.QueryOptionValueCannotBeEmpty("$count"), "$count"); } Count = rawQueryOption; } else if (rawQueryOption.StartsWith("$format=", StringComparison.Ordinal)) { if (rawQueryOption.Equals("$format=", StringComparison.Ordinal)) { throw ODataException.BadRequest(ExceptionMessage.QueryOptionValueCannotBeEmpty("$format"), "$format"); } Format = rawQueryOption; } else if (rawQueryOption.StartsWith("$expand=", StringComparison.Ordinal)) { if (rawQueryOption.Equals("$expand=", StringComparison.Ordinal)) { throw ODataException.BadRequest(ExceptionMessage.QueryOptionValueCannotBeEmpty("$expand"), "$expand"); } Expand = rawQueryOption; } else if (rawQueryOption.StartsWith("$search=", StringComparison.Ordinal)) { if (rawQueryOption.Equals("$search=", StringComparison.Ordinal)) { throw ODataException.BadRequest(ExceptionMessage.QueryOptionValueCannotBeEmpty("$search"), "$search"); } Search = rawQueryOption; } else if (rawQueryOption.StartsWith("$skiptoken=", StringComparison.Ordinal)) { if (rawQueryOption.Equals("$skiptoken=", StringComparison.Ordinal)) { throw ODataException.BadRequest(ExceptionMessage.QueryOptionValueCannotBeEmpty("$skiptoken"), "$skiptoken"); } SkipToken = rawQueryOption; } else if (rawQueryOption.StartsWith("$", StringComparison.Ordinal)) { string optionName = rawQueryOption.SubstringBefore('='); throw ODataException.BadRequest(ExceptionMessage.UnsupportedQueryOption(optionName), optionName); } } } }
internal static BinaryOperatorKind ToBinaryOperatorKind(this string operatorType) => s_operatorTypeMap.TryGetValue(operatorType, out BinaryOperatorKind binaryOperatorKind) ? binaryOperatorKind : throw ODataException.BadRequest(ExceptionMessage.InvalidOperator(operatorType), "$filter");
internal static ConstantNode ParseConstantNode(Token token) { switch (token.TokenType) { case TokenType.Base64Binary: byte[] binaryValue = Convert.FromBase64String(token.Value); return(ConstantNode.Binary(token.Value, binaryValue)); case TokenType.Date: if (DateTime.TryParseExact(token.Value, ParserSettings.ODataDateFormat, ParserSettings.CultureInfo, DateTimeStyles.AssumeLocal, out DateTime dateTimeValue)) { return(ConstantNode.Date(token.Value, dateTimeValue)); } throw ODataException.BadRequest(ExceptionMessage.UnableToParseDate, "$filter"); case TokenType.DateTimeOffset: if (DateTimeOffset.TryParse(token.Value, ParserSettings.CultureInfo, ParserSettings.DateTimeStyles, out DateTimeOffset dateTimeOffsetValue)) { return(ConstantNode.DateTimeOffset(token.Value, dateTimeOffsetValue)); } throw ODataException.BadRequest(ExceptionMessage.UnableToParseDateTimeOffset, "$filter"); case TokenType.Decimal: string decimalText = token.Value.EndsWith("m", StringComparison.OrdinalIgnoreCase) ? token.Value.Substring(0, token.Value.Length - 1) : token.Value; decimal decimalValue = decimal.Parse(decimalText, ParserSettings.CultureInfo); return(ConstantNode.Decimal(token.Value, decimalValue)); case TokenType.Double: return(ParseDouble(token)); case TokenType.Duration: string durationText = token.Value.Substring(9, token.Value.Length - 10) .Replace("P", string.Empty) .Replace("DT", ".") .Replace("H", ":") .Replace("M", ":") .Replace("S", string.Empty); var durationTimeSpanValue = TimeSpan.Parse(durationText, ParserSettings.CultureInfo); return(ConstantNode.Duration(token.Value, durationTimeSpanValue)); case TokenType.EdmType: EdmType edmType = EdmType.GetEdmType(token.Value); return(ConstantNode.EdmTypeNode(token.Value, edmType)); case TokenType.Enum: int firstQuote = token.Value.IndexOf('\''); string edmEnumTypeName = token.Value.Substring(0, firstQuote); EdmEnumType edmEnumType = (EdmEnumType)EdmType.GetEdmType(edmEnumTypeName); string edmEnumMemberName = token.Value.Substring(firstQuote + 1, token.Value.Length - firstQuote - 2); object enumValue = edmEnumType.GetClrValue(edmEnumMemberName); Type constantNodeType = typeof(ConstantNode <>).MakeGenericType(edmEnumType.ClrType); return((ConstantNode)Activator.CreateInstance(constantNodeType, BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { edmEnumType, token.Value, enumValue }, null)); case TokenType.False: return(ConstantNode.False); case TokenType.Guid: var guidValue = Guid.ParseExact(token.Value, "D"); return(ConstantNode.Guid(token.Value, guidValue)); case TokenType.Integer: return(ParseInteger(token)); case TokenType.Null: return(ConstantNode.Null); case TokenType.Single: string singleText = token.Value.Substring(0, token.Value.Length - 1); float singleValue = float.Parse(singleText, ParserSettings.CultureInfo); return(ConstantNode.Single(token.Value, singleValue)); case TokenType.String: string stringText = token.Value.Trim('\'').Replace("''", "'"); return(ConstantNode.String(token.Value, stringText)); case TokenType.TimeOfDay: var timeSpanTimeOfDayValue = TimeSpan.Parse(token.Value, ParserSettings.CultureInfo); return(ConstantNode.Time(token.Value, timeSpanTimeOfDayValue)); case TokenType.True: return(ConstantNode.True); default: throw new NotSupportedException(token.TokenType.ToString()); } }
private QueryNode ParseFunctionCallNode() { BinaryOperatorNode binaryNode = null; FunctionCallNode node = null; var stack = new Stack <FunctionCallNode>(); while (_tokens.Count > 0) { Token token = _tokens.Dequeue(); switch (token.TokenType) { case TokenType.Base64Binary: case TokenType.Date: case TokenType.DateTimeOffset: case TokenType.Decimal: case TokenType.Double: case TokenType.Duration: case TokenType.EdmType: case TokenType.Enum: case TokenType.False: case TokenType.Guid: case TokenType.Integer: case TokenType.Null: case TokenType.Single: case TokenType.String: case TokenType.TimeOfDay: case TokenType.True: ConstantNode constantNode = ConstantNodeParser.ParseConstantNode(token); if (stack.Count > 0) { stack.Peek().AddParameter(constantNode); } else { if (binaryNode == null) { throw ODataException.BadRequest(ExceptionMessage.GenericUnableToParseFilter, "$filter"); } binaryNode.Right = constantNode; } break; case TokenType.BinaryOperator: binaryNode = new BinaryOperatorNode(node, token.Value.ToBinaryOperatorKind(), null); break; case TokenType.CloseParentheses: if (_groupingDepth == 0) { throw ODataException.BadRequest(ExceptionMessage.UnableToParseFilter($"the closing parenthesis not expected", token.Position), "$filter"); } _groupingDepth--; if (stack.Count > 0) { FunctionCallNode lastNode = stack.Pop(); if (stack.Count > 0) { stack.Peek().AddParameter(lastNode); } else { if (binaryNode != null) { binaryNode.Right = lastNode; } else { node = lastNode; } } } break; case TokenType.Comma: if (_tokens.Count > 0 && _tokens.Peek().TokenType == TokenType.CloseParentheses) { // If there is a comma in a function call, there should be another parameter followed by a closing comma throw ODataException.BadRequest(ExceptionMessage.UnableToParseFilter($"the function {node?.Name} has a missing parameter or extra comma", token.Position), "$filter"); } break; case TokenType.FunctionName: node = new FunctionCallNode(token.Value); break; case TokenType.OpenParentheses: if (_tokens.Count > 0 && _tokens.Peek().TokenType == TokenType.CloseParentheses) { // All OData functions have at least 1 or 2 parameters throw ODataException.BadRequest(ExceptionMessage.UnableToParseFilter($"the function {node?.Name} has no parameters specified", token.Position), "$filter"); } _groupingDepth++; stack.Push(node); break; case TokenType.PropertyName: var propertyAccessNode = new PropertyAccessNode(PropertyPath.For(token.Value, _model)); if (stack.Count > 0) { stack.Peek().AddParameter(propertyAccessNode); } else { if (binaryNode == null) { throw ODataException.BadRequest(ExceptionMessage.GenericUnableToParseFilter, "$filter"); } binaryNode.Right = propertyAccessNode; } break; default: throw ODataException.BadRequest(ExceptionMessage.UnableToParseFilter($"unexpected {token.Value}", token.Position), "$filter"); } } if (binaryNode != null) { return(binaryNode); } return(node); }
private QueryNode ParsePropertyAccessNode() { QueryNode result = null; QueryNode leftNode = null; BinaryOperatorKind operatorKind = BinaryOperatorKind.None; QueryNode rightNode = null; while (_tokens.Count > 0) { Token token = _tokens.Dequeue(); switch (token.TokenType) { case TokenType.Base64Binary: case TokenType.Date: case TokenType.DateTimeOffset: case TokenType.Decimal: case TokenType.Double: case TokenType.Duration: case TokenType.Enum: case TokenType.False: case TokenType.Guid: case TokenType.Integer: case TokenType.Null: case TokenType.Single: case TokenType.String: case TokenType.TimeOfDay: case TokenType.True: rightNode = ConstantNodeParser.ParseConstantNode(token); break; case TokenType.BinaryOperator: if (operatorKind != BinaryOperatorKind.None) { result = new BinaryOperatorNode(leftNode, operatorKind, rightNode); leftNode = null; rightNode = null; } operatorKind = token.Value.ToBinaryOperatorKind(); break; case TokenType.CloseParentheses: _groupingDepth--; break; case TokenType.FunctionName: rightNode = new FunctionCallNode(token.Value); break; case TokenType.OpenParentheses: _groupingDepth++; break; case TokenType.PropertyName: var propertyAccessNode = new PropertyAccessNode(PropertyPath.For(token.Value, _model)); if (leftNode is null) { leftNode = propertyAccessNode; } else if (rightNode is null) { rightNode = propertyAccessNode; } break; default: throw ODataException.BadRequest(ExceptionMessage.UnableToParseFilter($"unexpected {token.Value}", token.Position), "$filter"); } } result = result is null ? new BinaryOperatorNode(leftNode, operatorKind, rightNode) : new BinaryOperatorNode(result, operatorKind, leftNode ?? rightNode); return(result); }