public void SingleLevelExpandTermTokenWorks() { ExpandTermToken expandTermToken = new ExpandTermToken(new NonSystemToken("MyDog", null, null)); ExpandToken expandToken = new ExpandToken(new ExpandTermToken[] {expandTermToken}); var item = this.binderForPerson.Bind(BuildUnifiedSelectExpandToken(expandToken)); item.SelectedItems.First().ShouldBeExpansionFor(HardCodedTestModel.GetPersonMyDogNavProp()).And.SelectAndExpand.AllSelected.Should().BeTrue(); }
/// <summary> /// Invert the all of the paths in an expandToken, such that they are now in the same order as they are present in the /// base url /// </summary> /// <param name="treeToInvert">the tree to invert paths on</param> /// <returns>a new tree with all of its paths inverted</returns> public ExpandToken NormalizePaths(ExpandToken treeToInvert) { // iterate through each expand term token, and reverse the tree in its path property List<ExpandTermToken> updatedTerms = new List<ExpandTermToken>(); foreach (ExpandTermToken term in treeToInvert.ExpandTerms) { PathReverser pathReverser = new PathReverser(); PathSegmentToken reversedPath = term.PathToNavProp.Accept(pathReverser); // we also need to call the select token normalizer for this level to reverse the select paths SelectToken newSelectToken = term.SelectOption; if (term.SelectOption != null) { SelectTreeNormalizer selectTreeNormalizer = new SelectTreeNormalizer(); newSelectToken = selectTreeNormalizer.NormalizeSelectTree(term.SelectOption); } ExpandToken subExpandTree; if (term.ExpandOption != null) { subExpandTree = this.NormalizePaths(term.ExpandOption); } else { subExpandTree = null; } ExpandTermToken newTerm = new ExpandTermToken(reversedPath, term.FilterOption, term.OrderByOptions, term.TopOption, term.SkipOption, term.CountQueryOption, term.LevelsOption, term.SearchOption, newSelectToken, subExpandTree); updatedTerms.Add(newTerm); } return new ExpandToken(updatedTerms); }
/// <summary> /// Collapse all redundant terms in an expand tree /// </summary> /// <param name="treeToCollapse">the tree to collapse</param> /// <returns>A new tree with all redundant terms collapsed.</returns> public ExpandToken CombineTerms(ExpandToken treeToCollapse) { var combinedTerms = new Dictionary<PathSegmentToken, ExpandTermToken>(new PathSegmentTokenEqualityComparer()); foreach (ExpandTermToken termToken in treeToCollapse.ExpandTerms) { ExpandTermToken finalTermToken = termToken; if (termToken.ExpandOption != null) { ExpandToken newSubExpand = CombineTerms(termToken.ExpandOption); finalTermToken = new ExpandTermToken( termToken.PathToNavProp, termToken.FilterOption, termToken.OrderByOptions, termToken.TopOption, termToken.SkipOption, termToken.CountQueryOption, termToken.LevelsOption, termToken.SearchOption, RemoveDuplicateSelect(termToken.SelectOption), newSubExpand); } AddOrCombine(combinedTerms, finalTermToken); } return new ExpandToken(combinedTerms.Values); }
public void CombineTermsWorksOnASingleTerm() { // $expand=stuff ExpandToken expand = new ExpandToken(new ExpandTermToken[] { new ExpandTermToken(new NonSystemToken("stuff", null, null)) }); ExpandTreeNormalizer expandTreeNormalizer = new ExpandTreeNormalizer(); ExpandToken combinedExpand = expandTreeNormalizer.CombineTerms(expand); combinedExpand.ExpandTerms.Single().ShouldBeExpandTermToken("stuff", false); }
/// <summary> /// Normalize an expand syntax tree into the new ExpandOption syntax. /// </summary> /// <param name="treeToNormalize">the tree to normalize</param> /// <returns>a new tree, in the new ExpandOption syntax</returns> public ExpandToken NormalizeExpandTree(ExpandToken treeToNormalize) { // To normalize the expand tree we need to // 1) invert the path tree on each of its expand term tokens // 2) combine terms that start with the path tree ExpandToken invertedPathTree = this.NormalizePaths(treeToNormalize); return CombineTerms(invertedPathTree); }
public void InvertPathsActuallyInvertsPaths() { // $expand=1/2 ExpandToken expand = new ExpandToken(new ExpandTermToken[] { new ExpandTermToken(new NonSystemToken("2", null, new NonSystemToken("1", null, null))) }); ExpandTreeNormalizer expandTreeNormalizer = new ExpandTreeNormalizer(); ExpandToken invertedPaths = expandTreeNormalizer.NormalizePaths(expand); invertedPaths.ExpandTerms.Single().ShouldBeExpandTermToken("1", false) .And.PathToNavProp.NextToken.ShouldBeNonSystemToken("2"); }
public void BindingOnTreeWithWithMultipleNavPropPathsThrows() { NonSystemToken topLevelSegment = new NonSystemToken("MyDog", null, null); NonSystemToken navProp = new NonSystemToken("MyPeople", null, topLevelSegment); ExpandTermToken expandTerm = new ExpandTermToken(navProp); ExpandToken expandToken = new ExpandToken(new ExpandTermToken[] {expandTerm}); Action bindTreeWithMultipleNavPropPaths = () => this.binderForPerson.Bind(BuildUnifiedSelectExpandToken(expandToken)); bindTreeWithMultipleNavPropPaths.ShouldThrow<ODataException>() .WithMessage(ODataErrorStrings.ExpandItemBinder_TraversingMultipleNavPropsInTheSamePath); }
public void NormalizeTreeWorksWhenPathsHaveArguments() { // $expand=1(name=value)/2 ExpandToken expand = new ExpandToken(new ExpandTermToken[] { new ExpandTermToken(new NonSystemToken("1", new NamedValue[] { new NamedValue("name", new LiteralToken("value")) }, null)) }); ExpandTreeNormalizer expandTreeNormalizer = new ExpandTreeNormalizer(); ExpandToken normalizedExpand = expandTreeNormalizer.NormalizeExpandTree(expand); normalizedExpand.ExpandTerms.Single().ShouldBeExpandTermToken("1", true) .And.PathToNavProp.As<NonSystemToken>().NamedValues.Single().ShouldBeNamedValue("name", "value"); normalizedExpand.ExpandTerms.Single().ExpandOption.Should().BeNull(); }
public void NullOriginalSelectTokenIsReflectedInNewTopLevelExpandToken() { ExpandToken originalExpand = new ExpandToken( new List<ExpandTermToken>() { new ExpandTermToken(new NonSystemToken("MyDog", /*namedValues*/null, /*nextToken*/null), /*SelectOption*/null, /*ExpandOption*/null) }); SelectToken originalSelect = null; ExpandToken unifiedExpand = SelectExpandSyntacticUnifier.Combine(originalExpand, originalSelect); unifiedExpand.ExpandTerms.Single().SelectOption.Should().BeNull(); }
public void NormalizeAnExpandOptionSyntaxTreeResultsInUnchangedOutput() { // $expand=1($expand=2;) ExpandToken innerExpand = new ExpandToken(new ExpandTermToken[] { new ExpandTermToken(new NonSystemToken("2", null, null)) }); ExpandToken expand = new ExpandToken(new ExpandTermToken[]{new ExpandTermToken(new NonSystemToken("1", null, null), null /*selectOption*/, innerExpand)}); ExpandTreeNormalizer expandTreeNormalizer = new ExpandTreeNormalizer(); ExpandToken normalizedExpand = expandTreeNormalizer.NormalizeExpandTree(expand); normalizedExpand.ExpandTerms.Single().ShouldBeExpandTermToken("1", true) .And.ExpandOption.ExpandTerms.Single().ShouldBeExpandTermToken("2", true) .And.ExpandOption.Should().BeNull(); }
public void CombineTermsWorksForMultipleTerms() { // $expand=1($expand=2), 1($expand=3) List<ExpandTermToken> expandTerms = new List<ExpandTermToken>(); var token2 = new NonSystemToken("2", null, null); var token3 = new NonSystemToken("3", null, null); expandTerms.Add(new ExpandTermToken(new NonSystemToken("1", /*namedValues*/null, /*nextToken*/null), /*SelectToken*/null, new ExpandToken(new List<ExpandTermToken>() { new ExpandTermToken(token2) }))); expandTerms.Add(new ExpandTermToken(new NonSystemToken("1", /*namedValues*/null, /*nextToken*/null), /*SelectToken*/null, new ExpandToken(new List<ExpandTermToken>() { new ExpandTermToken(token3) }))); ExpandToken expand = new ExpandToken(expandTerms); ExpandTreeNormalizer expandTreeNormalizer = new ExpandTreeNormalizer(); ExpandToken combinedExpand = expandTreeNormalizer.CombineTerms(expand); combinedExpand.ExpandTerms.Single().ShouldBeExpandTermToken("1", true); combinedExpand.ExpandTerms.ElementAt(0).ExpandOption.ExpandTerms.Should().Contain(t => t.PathToNavProp == token2); combinedExpand.ExpandTerms.ElementAt(0).ExpandOption.ExpandTerms.Should().Contain(t => t.PathToNavProp == token3); }
public void NewTopLevelExpandTokenReferencesDollarIt() { ExpandToken originalExpand = new ExpandToken( new List<ExpandTermToken>() { new ExpandTermToken(new NonSystemToken("MyDog", /*namedValues*/null, /*nextToken*/null), /*SelectOption*/null, /*ExpandOption*/null) }); SelectToken originalSelect = new SelectToken( new List<PathSegmentToken>() { new NonSystemToken("Name", /*namedValues*/null, /*nextToken*/null) }); ExpandToken unifiedExpand = SelectExpandSyntacticUnifier.Combine(originalExpand, originalSelect); unifiedExpand.ExpandTerms.Single().ShouldBeExpandTermToken(ExpressionConstants.It, true); }
public void SelectClauseIsAddedAsNewTopLevelExpandToken() { ExpandToken originalExpand = new ExpandToken( new List<ExpandTermToken>() { new ExpandTermToken(new NonSystemToken("MyDog", /*namedValues*/null, /*nextToken*/null), /*SelectOption*/null, /*ExpandOption*/null) }); SelectToken originalSelect = new SelectToken( new List<PathSegmentToken>() { new NonSystemToken("Name", /*namedValues*/null, /*nextToken*/null) }); ExpandToken unifiedExpand = SelectExpandSyntacticUnifier.Combine(originalExpand, originalSelect); unifiedExpand.ExpandTerms.Single().As<ExpandTermToken>().SelectOption.ShouldBeSelectToken(new string[] {"Name"}); }
public void OriginalExpandTokenIsUnChanged() { ExpandToken originalExpand = new ExpandToken( new List<ExpandTermToken>() { new ExpandTermToken(new NonSystemToken("MyDog", /*namedValues*/null, /*nextToken*/null), /*SelectOption*/null, /*ExpandOption*/null) }); SelectToken originalSelect = new SelectToken( new List<PathSegmentToken>() { new NonSystemToken("Name", /*namedValues*/null, /*nextToken*/null) }); ExpandToken unifiedExpand = SelectExpandSyntacticUnifier.Combine(originalExpand, originalSelect); var subExpand = unifiedExpand.ExpandTerms.Single().As<ExpandTermToken>().ExpandOption; subExpand.ExpandTerms.Single().ShouldBeExpandTermToken("MyDog", false); }
/// <summary> /// Add semantic meaning to a Select or Expand Token /// </summary> /// <param name="elementType">the top level entity type.</param> /// <param name="navigationSource">the top level navigation source</param> /// <param name="expandToken">the syntactically parsed expand token</param> /// <param name="selectToken">the syntactically parsed select token</param> /// <param name="configuration">The configuration to use for parsing.</param> /// <returns>A select expand clause bound to metadata.</returns> public SelectExpandClause Bind( IEdmStructuredType elementType, IEdmNavigationSource navigationSource, ExpandToken expandToken, SelectToken selectToken, ODataUriParserConfiguration configuration) { ExpandToken unifiedSelectExpandToken = SelectExpandSyntacticUnifier.Combine(expandToken, selectToken); ExpandTreeNormalizer expandTreeNormalizer = new ExpandTreeNormalizer(); ExpandToken normalizedSelectExpandToken = expandTreeNormalizer.NormalizeExpandTree(unifiedSelectExpandToken); SelectExpandBinder selectExpandBinder = new SelectExpandBinder(configuration, elementType, navigationSource); SelectExpandClause clause = selectExpandBinder.Bind(normalizedSelectExpandToken); SelectExpandClauseFinisher.AddExplicitNavPropLinksWhereNecessary(clause); new ExpandDepthAndCountValidator(configuration.Settings.MaximumExpansionDepth, configuration.Settings.MaximumExpansionCount).Validate(clause); return clause; }
/// <summary> /// Parse the raw select and expand strings into Abstract Syntax Trees /// </summary> /// <param name="selectClause">The raw select string</param> /// <param name="expandClause">the raw expand string</param> /// <param name="configuration">Configuration parameters</param> /// <param name="expandTree">the resulting expand AST</param> /// <param name="selectTree">the resulting select AST</param> public static void Parse( string selectClause, string expandClause, ODataUriParserConfiguration configuration, out ExpandToken expandTree, out SelectToken selectTree) { SelectExpandParser selectParser = new SelectExpandParser(selectClause, configuration.Settings.SelectExpandLimit, configuration.EnableCaseInsensitiveBuiltinIdentifier) { MaxPathDepth = configuration.Settings.PathLimit }; selectTree = selectParser.ParseSelect(); SelectExpandParser expandParser = new SelectExpandParser(expandClause, configuration.Settings.SelectExpandLimit, configuration.EnableCaseInsensitiveBuiltinIdentifier) { MaxPathDepth = configuration.Settings.PathLimit, MaxFilterDepth = configuration.Settings.FilterLimit, MaxOrderByDepth = configuration.Settings.OrderByLimit, MaxSearchDepth = configuration.Settings.SearchLimit }; expandTree = expandParser.ParseExpand(); }
/// <summary> /// Parse the raw select and expand strings into Abstract Syntax Trees /// </summary> /// <param name="selectClause">the raw select string</param> /// <param name="expandClause">the raw expand string</param> /// <param name="parentEntityType">the parent entity type for expand option</param> /// <param name="configuration">the OData URI parser configuration</param> /// <param name="expandTree">the resulting expand AST</param> /// <param name="selectTree">the resulting select AST</param> public static void Parse( string selectClause, string expandClause, IEdmStructuredType parentEntityType, ODataUriParserConfiguration configuration, out ExpandToken expandTree, out SelectToken selectTree) { SelectExpandParser selectParser = new SelectExpandParser(selectClause, configuration.Settings.SelectExpandLimit, configuration.EnableCaseInsensitiveUriFunctionIdentifier) { MaxPathDepth = configuration.Settings.PathLimit }; selectTree = selectParser.ParseSelect(); SelectExpandParser expandParser = new SelectExpandParser(configuration.Resolver, expandClause, parentEntityType, configuration.Settings.SelectExpandLimit, configuration.EnableCaseInsensitiveUriFunctionIdentifier) { MaxPathDepth = configuration.Settings.PathLimit, MaxFilterDepth = configuration.Settings.FilterLimit, MaxOrderByDepth = configuration.Settings.OrderByLimit, MaxSearchDepth = configuration.Settings.SearchLimit }; expandTree = expandParser.ParseExpand(); }
public void ExpandSetCorrectly() { ExpandToken expand = new ExpandToken(new ExpandTermToken[]{new ExpandTermToken(new NonSystemToken("stuff", null, null), null /*selectOption*/, null /*expandOption*/ )}); ExpandTermToken expandTerm = new ExpandTermToken(new NonSystemToken("stuff", null, null), null /*selectOption*/, expand); expandTerm.ExpandOption.Should().NotBeNull(); }
public void MultiLevelExpandTermTokenWorks() { ExpandTermToken innerExpandTerm = new ExpandTermToken(new NonSystemToken("MyPeople", null, null)); ExpandTermToken outerExpandTerm = new ExpandTermToken(new NonSystemToken("MyDog", null, null), null, new ExpandToken(new ExpandTermToken[] {innerExpandTerm})); ExpandToken expandToken = new ExpandToken(new ExpandTermToken[] {outerExpandTerm}); var item = this.binderForPerson.Bind(BuildUnifiedSelectExpandToken(expandToken)); var subSelectExpand = item.SelectedItems.First().ShouldBeExpansionFor(HardCodedTestModel.GetPersonMyDogNavProp()).And.SelectAndExpand; subSelectExpand.AllSelected.Should().BeTrue(); subSelectExpand.SelectedItems.First().ShouldBeExpansionFor(HardCodedTestModel.GetDogMyPeopleNavProp()) .And.SelectAndExpand.AllSelected.Should().BeTrue(); }
/// <summary> /// Create an expand term token /// </summary> /// <param name="pathToNavProp">the nav prop for this expand term</param> /// <param name="filterOption">the filter option for this expand term</param> /// <param name="orderByOptions">the orderby options for this expand term</param> /// <param name="topOption">the top option for this expand term</param> /// <param name="skipOption">the skip option for this expand term</param> /// <param name="countQueryOption">the query count option for this expand term</param> /// <param name="levelsOption">the levels option for this expand term</param> /// <param name="searchOption">the search option for this expand term</param> /// <param name="selectOption">the select option for this expand term</param> /// <param name="expandOption">the expand option for this expand term</param> public ExpandTermToken(PathSegmentToken pathToNavProp, QueryToken filterOption, IEnumerable <OrderByToken> orderByOptions, long?topOption, long?skipOption, bool?countQueryOption, long?levelsOption, QueryToken searchOption, SelectToken selectOption, ExpandToken expandOption) { ExceptionUtils.CheckArgumentNotNull(pathToNavProp, "property"); this.pathToNavProp = pathToNavProp; this.filterOption = filterOption; this.orderByOptions = orderByOptions; this.topOption = topOption; this.skipOption = skipOption; this.countQueryOption = countQueryOption; this.levelsOption = levelsOption; this.searchOption = searchOption; this.selectOption = selectOption; this.expandOption = expandOption; }
/// <summary> /// Visits an ExpandToken /// </summary> /// <param name="tokenIn">The ExpandToken to visit</param> /// <returns>A QueryNode bound to this ExpandToken</returns> public virtual T Visit(ExpandToken tokenIn) { throw new NotImplementedException(); }
public bool Visit(ExpandToken tokenIn) { throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "QueryToken of type '{0}' is not supported.", tokenIn.Kind)); }
public void CombineChildNodesWorksForTwoPopulatedNodes() { // $expand=1($expand=2), 1($expand=3) ExpandTermToken innerExpandTerm1 = new ExpandTermToken(new NonSystemToken("2", null, null)); ExpandToken innerExpand1 = new ExpandToken(new ExpandTermToken[] { innerExpandTerm1 }); ExpandTermToken outerExpandTerm1 = new ExpandTermToken(new NonSystemToken("1", null, null), null /*selectOption*/, innerExpand1); ExpandTermToken innerExpandTerm2 = new ExpandTermToken(new NonSystemToken("3", null, null)); ExpandToken innerExpand2 = new ExpandToken(new ExpandTermToken[] { innerExpandTerm2 }); ExpandTermToken outerExpandTerm2 = new ExpandTermToken(new NonSystemToken("1", null, null), null /*selectOption*/, innerExpand2); ExpandTreeNormalizer expandTreeNormalizer = new ExpandTreeNormalizer(); IEnumerable<ExpandTermToken> combinedChildren = expandTreeNormalizer.CombineChildNodes(outerExpandTerm1, outerExpandTerm2); combinedChildren.Should().Contain(innerExpandTerm1).And.Contain(innerExpandTerm2); }
/// <summary> /// Building off of a PathSegmentToken, continue parsing any expand options (nested $filter, $expand, etc) /// to build up an ExpandTermToken which fully represents the tree that makes up this expand term. /// </summary> /// <param name="pathToken">The PathSegmentToken representing the parsed expand path whose options we are now parsing.</param> /// <param name="optionsText">A string of the text between the parenthesis after an expand option.</param> /// <returns>An expand term token based on the path token, and all available expand options.</returns> internal ExpandTermToken BuildExpandTermToken(PathSegmentToken pathToken, string optionsText) { // Setup a new lexer for parsing the optionsText this.lexer = new ExpressionLexer(optionsText ?? "", true /*moveToFirstToken*/, true /*useSemicolonDelimiter*/); QueryToken filterOption = null; IEnumerable <OrderByToken> orderByOptions = null; long? topOption = null; long? skipOption = null; bool? countOption = null; long? levelsOption = null; QueryToken searchOption = null; SelectToken selectOption = null; ExpandToken expandOption = null; if (this.lexer.CurrentToken.Kind == ExpressionTokenKind.OpenParen) { // advance past the '(' this.lexer.NextToken(); // Check for (), which is not allowed. if (this.lexer.CurrentToken.Kind == ExpressionTokenKind.CloseParen) { throw new ODataException(ODataErrorStrings.UriParser_MissingExpandOption(pathToken.Identifier)); } // Look for all the supported query options while (this.lexer.CurrentToken.Kind != ExpressionTokenKind.CloseParen) { string text = this.enableCaseInsensitiveBuiltinIdentifier ? this.lexer.CurrentToken.Text.ToLowerInvariant() : this.lexer.CurrentToken.Text; switch (text) { case ExpressionConstants.QueryOptionFilter: { // advance to the equal sign this.lexer.NextToken(); string filterText = this.ReadQueryOption(); UriQueryExpressionParser filterParser = new UriQueryExpressionParser(this.MaxFilterDepth, enableCaseInsensitiveBuiltinIdentifier); filterOption = filterParser.ParseFilter(filterText); break; } case ExpressionConstants.QueryOptionOrderby: { // advance to the equal sign this.lexer.NextToken(); string orderByText = this.ReadQueryOption(); UriQueryExpressionParser orderbyParser = new UriQueryExpressionParser(this.MaxOrderByDepth, enableCaseInsensitiveBuiltinIdentifier); orderByOptions = orderbyParser.ParseOrderBy(orderByText); break; } case ExpressionConstants.QueryOptionTop: { // advance to the equal sign this.lexer.NextToken(); string topText = this.ReadQueryOption(); // TryParse requires a non-nullable non-negative long. long top; if (!long.TryParse(topText, out top) || top < 0) { throw new ODataException(ODataErrorStrings.UriSelectParser_InvalidTopOption(topText)); } topOption = top; break; } case ExpressionConstants.QueryOptionSkip: { // advance to the equal sign this.lexer.NextToken(); string skipText = this.ReadQueryOption(); // TryParse requires a non-nullable non-negative long. long skip; if (!long.TryParse(skipText, out skip) || skip < 0) { throw new ODataException(ODataErrorStrings.UriSelectParser_InvalidSkipOption(skipText)); } skipOption = skip; break; } case ExpressionConstants.QueryOptionCount: { // advance to the equal sign this.lexer.NextToken(); string countText = this.ReadQueryOption(); switch (countText) { case ExpressionConstants.KeywordTrue: { countOption = true; break; } case ExpressionConstants.KeywordFalse: { countOption = false; break; } default: { throw new ODataException(ODataErrorStrings.UriSelectParser_InvalidCountOption(countText)); } } break; } case ExpressionConstants.QueryOptionLevels: { // advance to the equal sign this.lexer.NextToken(); string levelsText = this.ReadQueryOption(); long level; if (string.Equals( ExpressionConstants.KeywordMax, levelsText, this.enableCaseInsensitiveBuiltinIdentifier ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal)) { levelsOption = long.MinValue; } else if (!long.TryParse(levelsText, out level) || level < 0) { throw new ODataException(ODataErrorStrings.UriSelectParser_InvalidLevelsOption(levelsText)); } else { levelsOption = level; } break; } case ExpressionConstants.QueryOptionSearch: { // advance to the equal sign this.lexer.NextToken(); string searchText = this.ReadQueryOption(); SearchParser searchParser = new SearchParser(this.MaxSearchDepth); searchOption = searchParser.ParseSearch(searchText); break; } case ExpressionConstants.QueryOptionSelect: { // advance to the equal sign this.lexer.NextToken(); string selectText = this.ReadQueryOption(); SelectExpandParser innerSelectParser = new SelectExpandParser(selectText, this.maxRecursionDepth, enableCaseInsensitiveBuiltinIdentifier); selectOption = innerSelectParser.ParseSelect(); break; } case ExpressionConstants.QueryOptionExpand: { // advance to the equal sign this.lexer.NextToken(); string expandText = this.ReadQueryOption(); SelectExpandParser innerExpandParser = new SelectExpandParser(expandText, this.maxRecursionDepth - 1, enableCaseInsensitiveBuiltinIdentifier); expandOption = innerExpandParser.ParseExpand(); break; } default: { throw new ODataException(ODataErrorStrings.UriSelectParser_TermIsNotValid(this.lexer.ExpressionText)); } } } // Move past the ')' this.lexer.NextToken(); } // Either there was no '(' at all or we just read past the ')' so we should be at the end if (this.lexer.CurrentToken.Kind != ExpressionTokenKind.End) { throw new ODataException(ODataErrorStrings.UriSelectParser_TermIsNotValid(this.lexer.ExpressionText)); } return(new ExpandTermToken(pathToken, filterOption, orderByOptions, topOption, skipOption, countOption, levelsOption, searchOption, selectOption, expandOption)); }
public void CanSelectPropertyOnNonEntityType() { ExpandTermToken expandTermToken = new ExpandTermToken(new SystemToken(ExpressionConstants.It, null), new SelectToken(new List<PathSegmentToken>(){new NonSystemToken("City", null, null)}), null ); ExpandToken expandToken = new ExpandToken(new ExpandTermToken[] { expandTermToken }); var item = this.binderForAddress.Bind(expandToken); item.SelectedItems.Single().ShouldBePathSelectionItem(new ODataPath(new PropertySegment(HardCodedTestModel.GetAddressCityProperty()))); }
public void SelectedAndExpandedNavPropProduceExpandedNavPropSelectionItemAndPathSelectionItem() { ExpandTermToken innerExpandTermToken = new ExpandTermToken(new NonSystemToken("MyDog", null, null)); ExpandToken innerExpandToken = new ExpandToken(new ExpandTermToken[] { innerExpandTermToken }); ExpandTermToken topLevelExpandTermToken = new ExpandTermToken(new SystemToken(ExpressionConstants.It, /*nextToken*/null), new SelectToken(new List<PathSegmentToken>(){new NonSystemToken("MyDog", /*namedValues*/null, /*nextToken*/null)}), innerExpandToken); ExpandToken topLevelExpandToken = new ExpandToken(new ExpandTermToken[]{topLevelExpandTermToken}); var item = this.binderForPerson.Bind(topLevelExpandToken); item.SelectedItems.Should().HaveCount(2); item.SelectedItems.First().ShouldBeExpansionFor(HardCodedTestModel.GetPersonMyDogNavProp()); item.SelectedItems.Last().ShouldBePathSelectionItem(new ODataPath(new NavigationPropertySegment(HardCodedTestModel.GetPersonMyDogNavProp(), HardCodedTestModel.GetPeopleSet()))); }
public void EntitySetCorrectlyPopulatedAtEachLevel() { ExpandTermToken innerExpandTerm = new ExpandTermToken(new NonSystemToken("MyPeople", null, null)); ExpandTermToken outerExpandTerm = new ExpandTermToken(new NonSystemToken("MyDog", null, null), null, new ExpandToken(new ExpandTermToken[] { innerExpandTerm })); ExpandToken expandToken = new ExpandToken(new ExpandTermToken[] { outerExpandTerm }); var item = this.binderForPerson.Bind(BuildUnifiedSelectExpandToken(expandToken)); var myDogExpandItem = item.SelectedItems.First().ShouldBeExpansionFor(HardCodedTestModel.GetPersonMyDogNavProp()).And; myDogExpandItem.NavigationSource.Should().Be(HardCodedTestModel.GetDogsSet()); var myPeopleExpandItem = myDogExpandItem.SelectAndExpand.SelectedItems.First().ShouldBeExpansionFor(HardCodedTestModel.GetDogMyPeopleNavProp()).And; myPeopleExpandItem.NavigationSource.Should().Be(HardCodedTestModel.GetPeopleSet()); myPeopleExpandItem.SelectAndExpand.SelectedItems.Should().BeEmpty(); }
/// <summary> /// Create a select term using only the property and its supporting query options. /// </summary> /// <param name="pathToProperty">the path to the property for this select term</param> /// <param name="filterOption">the filter option for this select term</param> /// <param name="orderByOptions">the orderby options for this select term</param> /// <param name="topOption">the top option for this select term</param> /// <param name="skipOption">the skip option for this select term</param> /// <param name="countQueryOption">the query count option for this select term</param> /// <param name="searchOption">the search option for this select term</param> /// <param name="selectOption">the select option for this select term</param> /// <param name="expandOption">the expand option for this select term</param> public SelectTermToken(PathSegmentToken pathToProperty, QueryToken filterOption, IEnumerable <OrderByToken> orderByOptions, long?topOption, long?skipOption, bool?countQueryOption, QueryToken searchOption, SelectToken selectOption, ExpandToken expandOption, ComputeToken computeOption) : base(pathToProperty, filterOption, orderByOptions, topOption, skipOption, countQueryOption, searchOption, selectOption, expandOption, computeOption) { }
/// <summary> /// Bind a sub level expand /// </summary> /// <param name="tokenIn">the token to visit</param> /// <returns>a SelectExpand clause based on this ExpandToken</returns> private SelectExpandClause BindSubLevel(ExpandToken tokenIn) { List <SelectItem> expandTerms = tokenIn.ExpandTerms.Select(this.GenerateExpandItem).Where(expandedNavigationSelectItem => expandedNavigationSelectItem != null).Cast <SelectItem>().ToList(); return(new SelectExpandClause(expandTerms, true)); }
/// <summary> /// Parses the <paramref name="queryUri"/> and returns a new instance of <see cref="SyntacticTree"/> /// describing the query specified by the uri. /// </summary> /// <param name="queryUri">The absolute URI which holds the query to parse. This must be a path relative to the <paramref name="serviceBaseUri"/>.</param> /// <param name="serviceBaseUri">The base URI of the service.</param> /// <param name="maxDepth">The maximum depth of any single query part. Security setting to guard against DoS attacks causing stack overflows and such.</param> /// <returns>A new instance of <see cref="SyntacticTree"/> which represents the query specified in the <paramref name="queryUri"/>.</returns> public static SyntacticTree ParseUri(Uri queryUri, Uri serviceBaseUri, int maxDepth) { ExceptionUtils.CheckArgumentNotNull(queryUri, "queryUri"); if (!queryUri.IsAbsoluteUri) { throw new ArgumentException(Strings.SyntacticTree_UriMustBeAbsolute(queryUri), "queryUri"); } ExceptionUtils.CheckArgumentNotNull(serviceBaseUri, "serviceBaseUri"); if (!serviceBaseUri.IsAbsoluteUri) { throw new ArgumentException(Strings.SyntacticTree_UriMustBeAbsolute(serviceBaseUri), "serviceBaseUri"); } if (maxDepth <= 0) { throw new ArgumentException(Strings.SyntacticTree_MaxDepthInvalid, "maxDepth"); } UriPathParser pathParser = new UriPathParser(maxDepth); var path = pathParser.ParsePathIntoSegments(queryUri, serviceBaseUri); List <CustomQueryOptionToken> queryOptions = UriUtils.ParseQueryOptions(queryUri); IDictionary <string, string> parameterAliases = queryOptions.GetParameterAliases(); QueryToken filter = null; string filterQuery = queryOptions.GetQueryOptionValueAndRemove(UriQueryConstants.FilterQueryOption); if (filterQuery != null) { UriQueryExpressionParser expressionParser = new UriQueryExpressionParser(maxDepth); filter = expressionParser.ParseFilter(filterQuery); } IEnumerable <OrderByToken> orderByTokens = null; string orderByQuery = queryOptions.GetQueryOptionValueAndRemove(UriQueryConstants.OrderByQueryOption); if (orderByQuery != null) { UriQueryExpressionParser expressionParser = new UriQueryExpressionParser(maxDepth); orderByTokens = expressionParser.ParseOrderBy(orderByQuery); } SelectToken select = null; string selectQuery = queryOptions.GetQueryOptionValueAndRemove(UriQueryConstants.SelectQueryOption); if (selectQuery != null) { SelectExpandParser selectParser = new SelectExpandParser(selectQuery, ODataUriParserSettings.DefaultSelectExpandLimit); select = selectParser.ParseSelect(); } ExpandToken expand = null; string expandQuery = queryOptions.GetQueryOptionValueAndRemove(UriQueryConstants.ExpandQueryOption); if (expandQuery != null) { SelectExpandParser expandParser = new SelectExpandParser(expandQuery, ODataUriParserSettings.DefaultSelectExpandLimit); expand = expandParser.ParseExpand(); } int? skip = null; string skipQuery = queryOptions.GetQueryOptionValueAndRemove(UriQueryConstants.SkipQueryOption); if (skipQuery != null) { int skipValue; if (!UriPrimitiveTypeParser.TryUriStringToNonNegativeInteger(skipQuery, out skipValue)) { throw new ODataException(Strings.SyntacticTree_InvalidSkipQueryOptionValue(skipQuery)); } skip = skipValue; } int? top = null; string topQuery = queryOptions.GetQueryOptionValueAndRemove(UriQueryConstants.TopQueryOption); if (topQuery != null) { int topValue; if (!UriPrimitiveTypeParser.TryUriStringToNonNegativeInteger(topQuery, out topValue)) { throw new ODataException(Strings.SyntacticTree_InvalidTopQueryOptionValue(topQuery)); } top = topValue; } string countQuery = queryOptions.GetQueryOptionValueAndRemove(UriQueryConstants.CountQueryOption); bool? count = QueryTokenUtils.ParseQueryCount(countQuery); string format = queryOptions.GetQueryOptionValueAndRemove(UriQueryConstants.FormatQueryOption); return(new SyntacticTree( parameterAliases, path, filter, orderByTokens, select, expand, skip, top, count, format, queryOptions.Count == 0 ? null : new ReadOnlyCollection <CustomQueryOptionToken>(queryOptions))); }
public void AddTermsDoesNothingForIdenticalTrees() { // $expand=1($expand=2;) ExpandToken innerExpand = new ExpandToken(new ExpandTermToken[] { new ExpandTermToken(new NonSystemToken("2", null, null)) }); ExpandTermToken outerExpandToken = new ExpandTermToken(new NonSystemToken("1", null, null), null /*selectOption*/, innerExpand); ExpandTreeNormalizer expandTreeNormalizer = new ExpandTreeNormalizer(); var addedToken = expandTreeNormalizer.CombineTerms(outerExpandToken, outerExpandToken); addedToken.ShouldBeExpandTermToken("1", true).And.ExpandOption.ExpandTerms.Single().ShouldBeExpandTermToken("2", true); }
public void AddTermsWorksForOneLevelBelow() { // $expand=1($expand=2;), 1($expand=3;) ExpandTermToken innerExpandTerm1 = new ExpandTermToken(new NonSystemToken("2", null, null)); ExpandToken innerExpand1 = new ExpandToken(new ExpandTermToken[] { innerExpandTerm1 }); ExpandTermToken outerToken1 = new ExpandTermToken(new NonSystemToken("1", null, null), null /*selectOption*/, innerExpand1); ExpandTermToken innerExpandTerm2 = new ExpandTermToken(new NonSystemToken("3", null, null)); ExpandToken innerExpand2 = new ExpandToken(new ExpandTermToken[] { innerExpandTerm2 }); ExpandTermToken outerToken2 = new ExpandTermToken(new NonSystemToken("1", null, null), null /*selectOption*/, innerExpand2); ExpandTreeNormalizer expandTreeNormalizer = new ExpandTreeNormalizer(); var addedToken = expandTreeNormalizer.CombineTerms(outerToken1, outerToken2); addedToken.ShouldBeExpandTermToken("1", true).And.ExpandOption.ExpandTerms.Should().Contain(innerExpandTerm2).And.Contain(innerExpandTerm1); }
public void LowerLevelEmptySelectedItemsListDoesNotThrow() { ExpandToken lowerLevelExpand = new ExpandToken(new List<ExpandTermToken>()); ExpandTermToken topLevelExpandTermToken = new ExpandTermToken(new NonSystemToken("MyDog", /*namedValues*/null, /*nextToken*/null), null, lowerLevelExpand); ExpandToken topLevelExpand = new ExpandToken(new List<ExpandTermToken>(){topLevelExpandTermToken}); Action bindWithEmptySelectedItemsList = () => this.binderForPerson.Bind(BuildUnifiedSelectExpandToken(topLevelExpand)); bindWithEmptySelectedItemsList.ShouldNotThrow(); }
public void CombineChildNodesWorksForSingleEmptyNode() { // $expand=1($expand=2;), 1 ExpandToken innerExpand = new ExpandToken(new ExpandTermToken[] { new ExpandTermToken(new NonSystemToken("2", null, null)) }); ExpandTermToken outerExpandTerm1 = new ExpandTermToken(new NonSystemToken("1", null, null), null /*selectOption*/, innerExpand); ExpandTermToken outerExpandTerm2 = new ExpandTermToken(new NonSystemToken("1", null, null)); ExpandTreeNormalizer expandTreeNormalizer = new ExpandTreeNormalizer(); IEnumerable<ExpandTermToken> combinedChildren = expandTreeNormalizer.CombineChildNodes(outerExpandTerm1, outerExpandTerm2); combinedChildren.Single().ShouldBeExpandTermToken("2", false); }
private static ExpandToken BuildUnifiedSelectExpandToken(ExpandToken expandToken, SelectToken selectToken) { return new ExpandToken( new ExpandTermToken[] { new ExpandTermToken(new NonSystemToken(ExpressionConstants.It, /*namedValues*/null, /*nextToken*/null), selectToken, expandToken) }); }
/// <summary> /// Building off of a PathSegmentToken, continue parsing any expand options (nested $filter, $expand, etc) /// to build up an ExpandTermToken which fully represents the tree that makes up this expand term. /// </summary> /// <param name="pathToken">The PathSegmentToken representing the parsed expand path whose options we are now parsing.</param> /// <param name="optionsText">A string of the text between the parenthesis after an expand option.</param> /// <returns>The list of expand term tokens based on the path token, and all available expand options.</returns> internal List <ExpandTermToken> BuildExpandTermToken(PathSegmentToken pathToken, string optionsText) { // Setup a new lexer for parsing the optionsText this.lexer = new ExpressionLexer(optionsText ?? "", true /*moveToFirstToken*/, true /*useSemicolonDelimiter*/); // $expand option with star only support $ref option, $expand option property could be "*" or "*/$ref", special logic will be adopted. if (pathToken.Identifier == UriQueryConstants.Star || (pathToken.Identifier == UriQueryConstants.RefSegment && pathToken.NextToken.Identifier == UriQueryConstants.Star)) { return(BuildStarExpandTermToken(pathToken)); } QueryToken filterOption = null; IEnumerable <OrderByToken> orderByOptions = null; long? topOption = null; long? skipOption = null; bool? countOption = null; long? levelsOption = null; QueryToken searchOption = null; SelectToken selectOption = null; ExpandToken expandOption = null; if (this.lexer.CurrentToken.Kind == ExpressionTokenKind.OpenParen) { // advance past the '(' this.lexer.NextToken(); // Check for (), which is not allowed. if (this.lexer.CurrentToken.Kind == ExpressionTokenKind.CloseParen) { throw new ODataException(ODataErrorStrings.UriParser_MissingExpandOption(pathToken.Identifier)); } // Look for all the supported query options while (this.lexer.CurrentToken.Kind != ExpressionTokenKind.CloseParen) { string text = this.enableCaseInsensitiveBuiltinIdentifier ? this.lexer.CurrentToken.Text.ToLowerInvariant() : this.lexer.CurrentToken.Text; switch (text) { case ExpressionConstants.QueryOptionFilter: { // advance to the equal sign this.lexer.NextToken(); string filterText = this.ReadQueryOption(); UriQueryExpressionParser filterParser = new UriQueryExpressionParser(this.MaxFilterDepth, enableCaseInsensitiveBuiltinIdentifier); filterOption = filterParser.ParseFilter(filterText); break; } case ExpressionConstants.QueryOptionOrderby: { // advance to the equal sign this.lexer.NextToken(); string orderByText = this.ReadQueryOption(); UriQueryExpressionParser orderbyParser = new UriQueryExpressionParser(this.MaxOrderByDepth, enableCaseInsensitiveBuiltinIdentifier); orderByOptions = orderbyParser.ParseOrderBy(orderByText); break; } case ExpressionConstants.QueryOptionTop: { // advance to the equal sign this.lexer.NextToken(); string topText = this.ReadQueryOption(); // TryParse requires a non-nullable non-negative long. long top; if (!long.TryParse(topText, out top) || top < 0) { throw new ODataException(ODataErrorStrings.UriSelectParser_InvalidTopOption(topText)); } topOption = top; break; } case ExpressionConstants.QueryOptionSkip: { // advance to the equal sign this.lexer.NextToken(); string skipText = this.ReadQueryOption(); // TryParse requires a non-nullable non-negative long. long skip; if (!long.TryParse(skipText, out skip) || skip < 0) { throw new ODataException(ODataErrorStrings.UriSelectParser_InvalidSkipOption(skipText)); } skipOption = skip; break; } case ExpressionConstants.QueryOptionCount: { // advance to the equal sign this.lexer.NextToken(); string countText = this.ReadQueryOption(); switch (countText) { case ExpressionConstants.KeywordTrue: { countOption = true; break; } case ExpressionConstants.KeywordFalse: { countOption = false; break; } default: { throw new ODataException(ODataErrorStrings.UriSelectParser_InvalidCountOption(countText)); } } break; } case ExpressionConstants.QueryOptionLevels: { levelsOption = ResolveLevelOption(); break; } case ExpressionConstants.QueryOptionSearch: { // advance to the equal sign this.lexer.NextToken(); string searchText = this.ReadQueryOption(); SearchParser searchParser = new SearchParser(this.MaxSearchDepth); searchOption = searchParser.ParseSearch(searchText); break; } case ExpressionConstants.QueryOptionSelect: { // advance to the equal sign this.lexer.NextToken(); string selectText = this.ReadQueryOption(); SelectExpandParser innerSelectParser = new SelectExpandParser(selectText, this.maxRecursionDepth, enableCaseInsensitiveBuiltinIdentifier); selectOption = innerSelectParser.ParseSelect(); break; } case ExpressionConstants.QueryOptionExpand: { // advance to the equal sign this.lexer.NextToken(); string expandText = this.ReadQueryOption(); // As 2016/1/8, the navigation property is only supported in entity type, and will support in ComplexType in future. IEdmStructuredType targetEntityType = null; if (this.resolver != null && this.parentEntityType != null) { var parentProperty = this.resolver.ResolveProperty(parentEntityType, pathToken.Identifier) as IEdmNavigationProperty; // it is a navigation property, need to find the type. Like $expand=Friends($expand=Trips($expand=*)), when expandText becomes "Trips($expand=*)", find navigation property Trips of Friends, then get Entity type of Trips. if (parentProperty != null) { targetEntityType = parentProperty.ToEntityType(); } } SelectExpandParser innerExpandParser = new SelectExpandParser(resolver, expandText, targetEntityType, this.maxRecursionDepth - 1, enableCaseInsensitiveBuiltinIdentifier); expandOption = innerExpandParser.ParseExpand(); break; } default: { throw new ODataException(ODataErrorStrings.UriSelectParser_TermIsNotValid(this.lexer.ExpressionText)); } } } // Move past the ')' this.lexer.NextToken(); } // Either there was no '(' at all or we just read past the ')' so we should be at the end if (this.lexer.CurrentToken.Kind != ExpressionTokenKind.End) { throw new ODataException(ODataErrorStrings.UriSelectParser_TermIsNotValid(this.lexer.ExpressionText)); } // TODO, there should be some check here in case pathToken identifier is $ref, select, expand and levels options are not allowed. List <ExpandTermToken> expandTermTokenList = new List <ExpandTermToken>(); ExpandTermToken currentToken = new ExpandTermToken(pathToken, filterOption, orderByOptions, topOption, skipOption, countOption, levelsOption, searchOption, selectOption, expandOption); expandTermTokenList.Add(currentToken); return(expandTermTokenList); }
/// <summary> /// Create an expand term using only the property and its subexpand/select /// </summary> /// <param name="pathToNavProp">the path to the navigation property for this expand term</param> /// <param name="selectOption">the sub select for this token</param> /// <param name="expandOption">the sub expand for this token</param> public ExpandTermToken(PathSegmentToken pathToNavProp, SelectToken selectOption, ExpandToken expandOption) : this(pathToNavProp, null, null, null, null, null, null, null, selectOption, expandOption) { }
private static ExpandToken BuildUnifiedSelectExpandToken(ExpandToken expandToken) { return BuildUnifiedSelectExpandToken(expandToken, null); }
/// <summary> /// Parses the <paramref name="queryUri"/> and returns a new instance of <see cref="SyntacticTree"/> /// describing the query specified by the uri. /// </summary> /// <param name="queryUri">The absolute URI which holds the query to parse. This must be a path relative to the <paramref name="serviceBaseUri"/>.</param> /// <param name="serviceBaseUri">The base URI of the service.</param> /// <param name="maxDepth">The maximum depth of any single query part. Security setting to guard against DoS attacks causing stack overflows and such.</param> /// <returns>A new instance of <see cref="SyntacticTree"/> which represents the query specified in the <paramref name="queryUri"/>.</returns> public static SyntacticTree ParseUri(Uri queryUri, Uri serviceBaseUri, int maxDepth) { ExceptionUtils.CheckArgumentNotNull(queryUri, "queryUri"); if (!queryUri.IsAbsoluteUri) { throw new ArgumentException(Strings.SyntacticTree_UriMustBeAbsolute(queryUri), "queryUri"); } ExceptionUtils.CheckArgumentNotNull(serviceBaseUri, "serviceBaseUri"); if (!serviceBaseUri.IsAbsoluteUri) { throw new ArgumentException(Strings.SyntacticTree_UriMustBeAbsolute(serviceBaseUri), "serviceBaseUri"); } if (maxDepth <= 0) { throw new ArgumentException(Strings.SyntacticTree_MaxDepthInvalid, "maxDepth"); } UriPathParser pathParser = new UriPathParser(maxDepth); var path = pathParser.ParsePathIntoSegments(queryUri, serviceBaseUri); // COMPAT 32: Differencies in query options parsing in WCF DS // // We allow non-system $ query options in the lexical space. // We allow multiple instances of a custom or non-system $ query option in the lexical space. // TODO: we need to decide whether we want to allow multiple system $ query options with the same name (OIPI suggests that this is valid); we currently don't. List <CustomQueryOptionToken> queryOptions = UriUtils.ParseQueryOptions(queryUri); IDictionary <string, string> parameterAliases = queryOptions.GetParameterAliases(); QueryToken filter = null; string filterQuery = queryOptions.GetQueryOptionValueAndRemove(UriQueryConstants.FilterQueryOption); if (filterQuery != null) { UriQueryExpressionParser expressionParser = new UriQueryExpressionParser(maxDepth); filter = expressionParser.ParseFilter(filterQuery); } IEnumerable <OrderByToken> orderByTokens = null; string orderByQuery = queryOptions.GetQueryOptionValueAndRemove(UriQueryConstants.OrderByQueryOption); if (orderByQuery != null) { UriQueryExpressionParser expressionParser = new UriQueryExpressionParser(maxDepth); orderByTokens = expressionParser.ParseOrderBy(orderByQuery); } SelectToken select = null; string selectQuery = queryOptions.GetQueryOptionValueAndRemove(UriQueryConstants.SelectQueryOption); if (selectQuery != null) { SelectExpandParser selectParser = new SelectExpandParser(selectQuery, ODataUriParserSettings.DefaultSelectExpandLimit); select = selectParser.ParseSelect(); } ExpandToken expand = null; string expandQuery = queryOptions.GetQueryOptionValueAndRemove(UriQueryConstants.ExpandQueryOption); if (expandQuery != null) { SelectExpandParser expandParser = new SelectExpandParser(expandQuery, ODataUriParserSettings.DefaultSelectExpandLimit); expand = expandParser.ParseExpand(); } int? skip = null; string skipQuery = queryOptions.GetQueryOptionValueAndRemove(UriQueryConstants.SkipQueryOption); if (skipQuery != null) { int skipValue; if (!TryUriStringToNonNegativeInteger(skipQuery, out skipValue)) { throw new ODataException(Strings.SyntacticTree_InvalidSkipQueryOptionValue(skipQuery)); } skip = skipValue; } int? top = null; string topQuery = queryOptions.GetQueryOptionValueAndRemove(UriQueryConstants.TopQueryOption); if (topQuery != null) { int topValue; if (!TryUriStringToNonNegativeInteger(topQuery, out topValue)) { throw new ODataException(Strings.SyntacticTree_InvalidTopQueryOptionValue(topQuery)); } top = topValue; } string countQuery = queryOptions.GetQueryOptionValueAndRemove(UriQueryConstants.CountQueryOption); bool? count = ParseQueryCount(countQuery); string format = queryOptions.GetQueryOptionValueAndRemove(UriQueryConstants.FormatQueryOption); return(new SyntacticTree( parameterAliases, path, filter, orderByTokens, select, expand, skip, top, count, format, queryOptions.Count == 0 ? null : new ReadOnlyCollection <CustomQueryOptionToken>(queryOptions))); }
public void InvertPathsKeepsExpandOptionsInvariant() { //$expand=1($filter=filter, $orderby=orderby, $top=top, $skip=skip;) ExpandToken expand = new ExpandToken( new ExpandTermToken[] { new ExpandTermToken( new NonSystemToken("1", null, null), new LiteralToken("filter"), new OrderByToken []{ new OrderByToken(new LiteralToken("orderby"), OrderByDirection.Descending)}, 1, 2, false, 3, new StringLiteralToken("searchme"), new SelectToken(null), new ExpandToken(null)) } ); ExpandTreeNormalizer expandTreeNormalizer = new ExpandTreeNormalizer(); ExpandToken invertedPaths = expandTreeNormalizer.NormalizePaths(expand); var invertedToken = invertedPaths.ExpandTerms.Single(); invertedToken.ShouldBeExpandTermToken("1", true); invertedToken.FilterOption.ShouldBeLiteralQueryToken("filter"); invertedToken.OrderByOptions.Single().Expression.ShouldBeLiteralQueryToken("orderby"); invertedToken.OrderByOptions.Single().Direction.Should().Be(OrderByDirection.Descending); invertedToken.TopOption.Should().Be(1); invertedToken.SkipOption.Should().Be(2); invertedToken.CountQueryOption.Should().BeFalse(); invertedToken.LevelsOption.Should().Be(3); invertedToken.SearchOption.ShouldBeStringLiteralToken("searchme"); invertedToken.SelectOption.Properties.Should().BeEmpty(); invertedToken.ExpandOption.ExpandTerms.Should().BeEmpty(); }
/// <summary> /// Create an select term using only the property and its subexpand/select /// </summary> /// <param name="pathToProperty">the path to the property for this select term</param> /// <param name="selectOption">the sub select for this token</param> /// <param name="expandOption">the sub expand for this token</param> public SelectTermToken(PathSegmentToken pathToProperty, SelectToken selectOption, ExpandToken expandOption) : this(pathToProperty, null, null, null, null, null, null, selectOption, expandOption, null) { }