public void MaxNestingDepthShouldThrowIfSetToNonPositiveNumber() { Action testSubject = () => this.settings.MessageQuotas.MaxNestingDepth = 0; testSubject.ShouldThrow <ArgumentOutOfRangeException>().Where(e => e.Message.StartsWith(ODataErrorStrings.ExceptionUtils_CheckIntegerPositive(0))); }
/// <summary> /// Applies the model and validates the context URI against it. /// </summary> /// <param name="expectedPayloadKind">The payload kind we expect the context URI to conform to.</param> /// <param name="clientCustomTypeResolver">The function of client cuetom type resolver.</param> private void ParseContextUri(ODataPayloadKind expectedPayloadKind, Func <IEdmType, string, IEdmType> clientCustomTypeResolver) { bool isUndeclared = false; ODataPayloadKind detectedPayloadKind = this.ParseContextUriFragment(this.parseResult.Fragment, clientCustomTypeResolver, out isUndeclared); // unsupported payload kind indicates that this is during payload kind detection, so we should not fail. bool detectedPayloadKindMatchesExpectation = detectedPayloadKind == expectedPayloadKind || expectedPayloadKind == ODataPayloadKind.Unsupported; if (detectedPayloadKind == ODataPayloadKind.ResourceSet && this.parseResult.EdmType.IsODataComplexTypeKind()) { this.parseResult.DetectedPayloadKinds = new[] { ODataPayloadKind.ResourceSet, ODataPayloadKind.Property, ODataPayloadKind.Collection }; if (expectedPayloadKind == ODataPayloadKind.Property || expectedPayloadKind == ODataPayloadKind.Collection) { detectedPayloadKindMatchesExpectation = true; } } else if (detectedPayloadKind == ODataPayloadKind.Resource && this.parseResult.EdmType.IsODataComplexTypeKind()) { this.parseResult.DetectedPayloadKinds = new[] { ODataPayloadKind.Resource, ODataPayloadKind.Property }; if (expectedPayloadKind == ODataPayloadKind.Property) { detectedPayloadKindMatchesExpectation = true; } } else if (detectedPayloadKind == ODataPayloadKind.Collection) { // If the detected payload kind is 'collection' it can always also be treated as a property. this.parseResult.DetectedPayloadKinds = new[] { ODataPayloadKind.Collection, ODataPayloadKind.Property }; if (expectedPayloadKind == ODataPayloadKind.Property) { detectedPayloadKindMatchesExpectation = true; } } else if (detectedPayloadKind == ODataPayloadKind.Resource) { this.parseResult.DetectedPayloadKinds = new[] { ODataPayloadKind.Resource, ODataPayloadKind.Delta }; if (expectedPayloadKind == ODataPayloadKind.Delta) { this.parseResult.DeltaKind = ODataDeltaKind.Resource; detectedPayloadKindMatchesExpectation = true; } } else if (detectedPayloadKind == ODataPayloadKind.Property && isUndeclared && (expectedPayloadKind == ODataPayloadKind.Resource || expectedPayloadKind == ODataPayloadKind.ResourceSet)) { // for undeclared, we don't know whether it is a resource/resource set or not. this.parseResult.DetectedPayloadKinds = new[] { expectedPayloadKind, ODataPayloadKind.Property }; detectedPayloadKindMatchesExpectation = true; } else { this.parseResult.DetectedPayloadKinds = new[] { detectedPayloadKind }; } // If the expected and detected payload kinds don't match and we are not running payload kind detection // right now (payloadKind == ODataPayloadKind.Unsupported) and we did not detect a collection kind for // an expected property kind (which is allowed), fail. if (!detectedPayloadKindMatchesExpectation) { throw new ODataException(ODataErrorStrings.ODataJsonLightContextUriParser_ContextUriDoesNotMatchExpectedPayloadKind(UriUtils.UriToString(this.parseResult.ContextUri), expectedPayloadKind.ToString())); } // NOTE: we interpret an empty select query option to mean that nothing should be projected // (whereas a missing select query option means everything should be projected). string selectQueryOption = this.parseResult.SelectQueryOption; if (selectQueryOption != null) { if (detectedPayloadKind != ODataPayloadKind.ResourceSet && detectedPayloadKind != ODataPayloadKind.Resource && detectedPayloadKind != ODataPayloadKind.Delta) { throw new ODataException(ODataErrorStrings.ODataJsonLightContextUriParser_InvalidPayloadKindWithSelectQueryOption(expectedPayloadKind.ToString())); } } }
/// <summary> /// Binds a DottedIdentifierToken and it's parent node (if needed). /// </summary> /// <param name="dottedIdentifierToken">Token to bind to metadata.</param> /// <returns>A bound node representing the cast.</returns> internal QueryNode BindDottedIdentifier(DottedIdentifierToken dottedIdentifierToken) { ExceptionUtils.CheckArgumentNotNull(dottedIdentifierToken, "castToken"); ExceptionUtils.CheckArgumentNotNull(state, "state"); QueryNode parent = null; IEdmType parentType = null; if (state.ImplicitRangeVariable != null) { if (dottedIdentifierToken.NextToken == null) { parent = NodeFactory.CreateRangeVariableReferenceNode(state.ImplicitRangeVariable); parentType = state.ImplicitRangeVariable.TypeReference.Definition; } else { parent = this.bindMethod(dottedIdentifierToken.NextToken); parentType = parent.GetEdmType(); } } SingleResourceNode parentAsSingleResource = parent as SingleResourceNode; IEdmSchemaType childType = UriEdmHelpers.FindTypeFromModel(state.Model, dottedIdentifierToken.Identifier, this.Resolver); IEdmStructuredType childStructuredType = childType as IEdmStructuredType; if (childStructuredType == null) { FunctionCallBinder functionCallBinder = new FunctionCallBinder(bindMethod, state); QueryNode functionCallNode; if (functionCallBinder.TryBindDottedIdentifierAsFunctionCall(dottedIdentifierToken, parent as SingleValueNode, out functionCallNode)) { return(functionCallNode); } else if ((!string.IsNullOrEmpty(dottedIdentifierToken.Identifier)) && (dottedIdentifierToken.Identifier[dottedIdentifierToken.Identifier.Length - 1] == '\'')) { // check if it is enum or not QueryNode enumNode; if (EnumBinder.TryBindDottedIdentifierAsEnum(dottedIdentifierToken, parentAsSingleResource, state, out enumNode)) { return(enumNode); } else { throw new ODataException(ODataErrorStrings.Binder_IsNotValidEnumConstant(dottedIdentifierToken.Identifier)); } } else { IEdmTypeReference edmTypeReference = UriEdmHelpers.FindTypeFromModel(state.Model, dottedIdentifierToken.Identifier, this.Resolver).ToTypeReference(); if (edmTypeReference is IEdmPrimitiveTypeReference || edmTypeReference is IEdmEnumTypeReference) { return(new ConstantNode(dottedIdentifierToken.Identifier, dottedIdentifierToken.Identifier)); } else { throw new ODataException(ODataErrorStrings.CastBinder_ChildTypeIsNotEntity(dottedIdentifierToken.Identifier)); } } } // Check whether childType is a derived type of the type of its parent node UriEdmHelpers.CheckRelatedTo(parentType, childType); this.state.ParsedSegments.Add(new TypeSegment(childType, parentType, null)); CollectionResourceNode parentAsCollection = parent as CollectionResourceNode; if (parentAsCollection != null) { return(new CollectionResourceCastNode(parentAsCollection, childStructuredType)); } return(new SingleResourceCastNode(parentAsSingleResource, childStructuredType)); }
public void ContainerQualifiedWildcardNotAllowedInExpand() { Action parseWithContainerQualfiedWildcard = () => this.ParseExpandTerm("container.qualified.*"); parseWithContainerQualfiedWildcard.Throws <ODataException>(ODataErrorStrings.ExpressionLexer_SyntaxError("21", "container.qualified.*")); }
// parses $apply groupby tranformation (.e.g. groupby(ProductID, CategoryId, aggregate(UnitPrice with sum as TotalUnitPrice)) internal GroupByToken ParseGroupBy() { Debug.Assert(TokenIdentifierIs(ExpressionConstants.KeywordGroupBy), "token identifier is groupby"); lexer.NextToken(); // '(' if (this.lexer.CurrentToken.Kind != ExpressionTokenKind.OpenParen) { throw ParseError(ODataErrorStrings.UriQueryExpressionParser_OpenParenExpected(this.lexer.CurrentToken.Position, this.lexer.ExpressionText)); } this.lexer.NextToken(); // '(' if (this.lexer.CurrentToken.Kind != ExpressionTokenKind.OpenParen) { throw ParseError(ODataErrorStrings.UriQueryExpressionParser_OpenParenExpected(this.lexer.CurrentToken.Position, this.lexer.ExpressionText)); } this.lexer.NextToken(); // properties var properties = new List <EndPathToken>(); while (true) { var expression = this.ParsePrimary() as EndPathToken; if (expression == null) { throw ParseError(ODataErrorStrings.UriQueryExpressionParser_ExpressionExpected(this.lexer.CurrentToken.Position, this.lexer.ExpressionText)); } properties.Add(expression); if (this.lexer.CurrentToken.Kind != ExpressionTokenKind.Comma) { break; } this.lexer.NextToken(); } // ")" if (this.lexer.CurrentToken.Kind != ExpressionTokenKind.CloseParen) { throw ParseError(ODataErrorStrings.UriQueryExpressionParser_CloseParenOrOperatorExpected(this.lexer.CurrentToken.Position, this.lexer.ExpressionText)); } this.lexer.NextToken(); // optional child transformation ApplyTransformationToken transformationToken = null; // "," (comma) if (this.lexer.CurrentToken.Kind == ExpressionTokenKind.Comma) { this.lexer.NextToken(); if (TokenIdentifierIs(ExpressionConstants.KeywordAggregate)) { transformationToken = this.ParseAggregate(); } else { throw ParseError(ODataErrorStrings.UriQueryExpressionParser_KeywordOrIdentifierExpected(ExpressionConstants.KeywordAggregate, this.lexer.CurrentToken.Position, this.lexer.ExpressionText)); } } // ")" if (this.lexer.CurrentToken.Kind != ExpressionTokenKind.CloseParen) { throw ParseError(ODataErrorStrings.UriQueryExpressionParser_CloseParenOrCommaExpected(this.lexer.CurrentToken.Position, this.lexer.ExpressionText)); } this.lexer.NextToken(); return(new GroupByToken(properties, transformationToken)); }
/// <summary> /// Build a segment from a token. /// </summary> /// <param name="tokenIn">the token to bind</param> /// <param name="model">The model.</param> /// <param name="edmType">the type of the current scope based on type segments.</param> /// <param name="resolver">Resolver for uri parser.</param> /// <param name="state">The binding state.</param> /// <returns>The segment created from the token.</returns> public static ODataPathSegment ConvertNonTypeTokenToSegment(PathSegmentToken tokenIn, IEdmModel model, IEdmStructuredType edmType, ODataUriResolver resolver, BindingState state = null) { ExceptionUtils.CheckArgumentNotNull(resolver, "resolver"); ODataPathSegment nextSegment; if (UriParserHelper.IsAnnotation(tokenIn.Identifier)) { if (TryBindAsDeclaredTerm(tokenIn, model, resolver, out nextSegment)) { return(nextSegment); } string qualifiedTermName = tokenIn.Identifier.Remove(0, 1); int separator = qualifiedTermName.LastIndexOf(".", StringComparison.Ordinal); string namespaceName = qualifiedTermName.Substring(0, separator); string termName = qualifiedTermName.Substring(separator == 0 ? 0 : separator + 1); // Don't allow selecting odata control information if (String.Compare(namespaceName, ODataConstants.ODataPrefix, StringComparison.OrdinalIgnoreCase) == 0) { throw new ODataException(ODataErrorStrings.UriSelectParser_TermIsNotValid(tokenIn.Identifier)); } return(new AnnotationSegment(new EdmTerm(namespaceName, termName, EdmCoreModel.Instance.GetUntyped()))); } EndPathToken endPathToken = new EndPathToken(tokenIn.Identifier, null); if ((state?.IsCollapsed ?? false) && !(state?.AggregatedPropertyNames?.Contains(endPathToken) ?? false)) { throw new ODataException(ODataErrorStrings.ApplyBinder_GroupByPropertyNotPropertyAccessValue(tokenIn.Identifier)); } if (TryBindAsDeclaredProperty(tokenIn, edmType, resolver, out nextSegment)) { return(nextSegment); } // Operations must be container-qualified, and because the token type indicates it was not a .-separated identifier, we should not try to look up operations. if (tokenIn.IsNamespaceOrContainerQualified()) { if (TryBindAsOperation(tokenIn, model, edmType, out nextSegment)) { return(nextSegment); } // If an action or function is requested in a selectItem using a qualifiedActionName or a qualifiedFunctionName // and that operation cannot be bound to the entities requested, the service MUST ignore the selectItem. if (!edmType.IsOpen) { return(null); } } if (edmType.IsOpen || (state?.AggregatedPropertyNames?.Contains(endPathToken) ?? false)) { return(new DynamicPathSegment(tokenIn.Identifier)); } throw new ODataException(ODataErrorStrings.MetadataBinder_PropertyNotDeclared(edmType.FullTypeName(), tokenIn.Identifier)); }
private static string NormalizeStringCollectionItems(string literalText) { // a comma-separated list of primitive values, enclosed in parentheses, or a single expression that resolves to a collection // However, for String collection, we should process: // 1) comma could be part of the string value // 2) single quote could not be part of string value // 3) double quote could be part of string value, double quote also could be the starting and ending character. // remove the '[' and ']' string normalizedText = literalText.Substring(1, literalText.Length - 2).Trim(); int length = normalizedText.Length; StringBuilder sb = new StringBuilder(length + 2); sb.Append('['); for (int i = 0; i < length; i++) { char ch = normalizedText[i]; switch (ch) { case '"': i = ProcessDoubleQuotedStringItem(i, normalizedText, sb); break; case '\'': i = ProcessSingleQuotedStringItem(i, normalizedText, sb); break; case ' ': // ignore all whitespaces between items break; case ',': // for multiple comma(s) between items, for example ('abc',,,'xyz'), // We let it go and let the next layer to identify the problem by design. sb.Append(','); break; case 'n': // it maybe null int index = normalizedText.IndexOf(',', i + 1); string subStr; if (index < 0) { subStr = normalizedText.Substring(i).TrimEnd(' '); i = length - 1; } else { subStr = normalizedText.Substring(i, index - i).TrimEnd(' '); i = index - 1; } if (subStr == "null") { sb.Append("null"); } else { throw new ODataException(ODataErrorStrings.StringItemShouldBeQuoted(subStr)); } break; default: // any other character between items is not valid. throw new ODataException(ODataErrorStrings.StringItemShouldBeQuoted(ch)); } } sb.Append(']'); return(sb.ToString()); }
/// <summary> /// Building off of a PathSegmentToken whose value is star, only nested level options is allowed. /// </summary> /// <param name="pathToken">The PathSegmentToken representing the parsed expand path whose options we are now parsing.</param> /// <returns>An expand term token based on the path token, and all available expand options.</returns> private List <ExpandTermToken> BuildStarExpandTermToken(PathSegmentToken pathToken) { List <ExpandTermToken> expandTermTokenList = new List <ExpandTermToken>(); long?levelsOption = null; bool isRefExpand = (pathToken.Identifier == UriQueryConstants.RefSegment); // Based on the specification, // For star in expand, this will be supported, // $expand=* // $expand=EntitySet($expand=* ) // $expand=*/$ref // $expand=*,EntitySet // $expand=EntitySet, * // $expand=*/$ref,EntitySet // Parenthesized set of expand options for star expand option supported are $level per specification. // And this will throw exception, // $expand= * /$count // Parenthesized set of expand options for star expand option which will also cause exception are $filter, $select, $orderby, $skip, $top, $count, $search, and $expand per specification. // And level is not supported with "*/$ref". // As 2016/1/8, the navigation property is only supported in entity type, and will support in ComplexType in future. 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)); } // Only level option is supported by expand. while (this.lexer.CurrentToken.Kind != ExpressionTokenKind.CloseParen) { string text = this.enableCaseInsensitiveBuiltinIdentifier ? this.lexer.CurrentToken.Text.ToLowerInvariant() : this.lexer.CurrentToken.Text; switch (text) { case ExpressionConstants.QueryOptionLevels: { if (!isRefExpand) { levelsOption = ResolveLevelOption(); } else { // no option is allowed when expand with star per specification throw new ODataException(ODataErrorStrings.UriExpandParser_TermIsNotValidForStarRef(this.lexer.ExpressionText)); } break; } default: { throw new ODataException(ODataErrorStrings.UriExpandParser_TermIsNotValidForStar(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)); } // As 2016/1/8, the navigation property is only supported in entity type, and will support in ComplexType in future. var entityType = parentEntityType as IEdmEntityType; if (entityType == null) { throw new ODataException(ODataErrorStrings.UriExpandParser_ParentEntityIsNull(this.lexer.ExpressionText)); } foreach (var navigationProperty in entityType.NavigationProperties()) { var tmpPathToken = default(PathSegmentToken); // create path token for each navigation properties. if (pathToken.Identifier.Equals(UriQueryConstants.RefSegment)) { tmpPathToken = new NonSystemToken(navigationProperty.Name, null, pathToken.NextToken.NextToken); tmpPathToken = new NonSystemToken(UriQueryConstants.RefSegment, null, tmpPathToken); } else { tmpPathToken = new NonSystemToken(navigationProperty.Name, null, pathToken.NextToken); } ExpandTermToken currentToken = new ExpandTermToken(tmpPathToken, null, null, null, null, null, levelsOption, null, null, null, null, null); expandTermTokenList.Add(currentToken); } return(expandTermTokenList); }
/// <summary> /// Try to bind an identifier to a FunctionCallNode /// </summary> /// <param name="identifier">the identifier to bind</param> /// <param name="arguments">the semantically bound list of arguments.</param> /// <param name="parent">a semantically bound parent node.</param> /// <param name="state">the current state of the binding algorithm</param> /// <param name="boundFunction">a single value function call node representing this function call, if we found one.</param> /// <returns>true if we found a function for this token.</returns> private bool TryBindIdentifier(string identifier, IEnumerable <FunctionParameterToken> arguments, QueryNode parent, BindingState state, out QueryNode boundFunction) { boundFunction = null; IEdmType bindingType = null; SingleValueNode singleValueParent = parent as SingleValueNode; if (singleValueParent != null) { if (singleValueParent.TypeReference != null) { bindingType = singleValueParent.TypeReference.Definition; } } else { CollectionNode collectionValueParent = parent as CollectionNode; if (collectionValueParent != null) { bindingType = collectionValueParent.CollectionType.Definition; } } if (!UriEdmHelpers.IsBindingTypeValid(bindingType)) { return(false); } // All functions should be fully qualified, if they aren't they they aren't functions. // When using extension, there may be function call with unqualified name. So loose the restriction here. if (identifier.IndexOf(".", StringComparison.Ordinal) == -1 && this.Resolver.GetType() == typeof(ODataUriResolver)) { return(false); } IEdmOperation operation; List <FunctionParameterToken> syntacticArguments = arguments == null ? new List <FunctionParameterToken>() : arguments.ToList(); if (!FunctionOverloadResolver.ResolveOperationFromList(identifier, syntacticArguments.Select(ar => ar.ParameterName).ToList(), bindingType, state.Model, out operation, this.Resolver)) { // TODO: FunctionOverloadResolver.ResolveOperationFromList() looks up the function by parameter names, but it shouldn't ignore parameter types. (test case ParseFilter_AliasInFunction_PropertyAsValue_TypeMismatch should fail) return(false); } if (singleValueParent != null && singleValueParent.TypeReference == null) { // if the parent exists, but has no type information, then we're in open type land, and we // shouldn't go any farther. throw new ODataException(ODataErrorStrings.FunctionCallBinder_CallingFunctionOnOpenProperty(identifier)); } if (operation.IsAction()) { return(false); } IEdmFunction function = (IEdmFunction)operation; // TODO: $filter $orderby parameter expression which contains complex or collection should NOT be supported in this way // but should be parsed into token tree, and binded to node tree: parsedParameters.Select(p => this.bindMethod(p)); ICollection <FunctionParameterToken> parsedParameters = HandleComplexOrCollectionParameterValueIfExists(state.Configuration.Model, function, syntacticArguments, state.Configuration.Resolver.EnableCaseInsensitive); IEnumerable <QueryNode> boundArguments = parsedParameters.Select(p => this.bindMethod(p)); boundArguments = boundArguments.ToList(); // force enumerable to run : will immediately evaluate all this.bindMethod(p). IEdmTypeReference returnType = function.ReturnType; IEdmEntitySetBase returnSet = null; SingleResourceNode singleEntityNode = parent as SingleResourceNode; if (singleEntityNode != null) { returnSet = function.GetTargetEntitySet(singleEntityNode.NavigationSource, state.Model); } string functionName = function.FullName(); if (returnType.IsEntity()) { boundFunction = new SingleResourceFunctionCallNode(functionName, new[] { function }, boundArguments, (IEdmEntityTypeReference)returnType.Definition.ToTypeReference(), returnSet, parent); } else if (returnType.IsStructuredCollection()) { IEdmCollectionTypeReference collectionTypeReference = (IEdmCollectionTypeReference)returnType; boundFunction = new CollectionResourceFunctionCallNode(functionName, new[] { function }, boundArguments, collectionTypeReference, returnSet, parent); } else if (returnType.IsCollection()) { IEdmCollectionTypeReference collectionTypeReference = (IEdmCollectionTypeReference)returnType; boundFunction = new CollectionFunctionCallNode(functionName, new[] { function }, boundArguments, collectionTypeReference, parent); } else { boundFunction = new SingleValueFunctionCallNode(functionName, new[] { function }, boundArguments, returnType, parent); } return(true); }
/// <summary> /// Process a <see cref="PathSegmentToken"/> following any type segments if necessary. /// </summary> /// <param name="tokenIn">the path token to process.</param> /// <returns>The processed OData segments.</returns> private List <ODataPathSegment> ProcessSelectTokenPath(PathSegmentToken tokenIn) { Debug.Assert(tokenIn != null, "tokenIn != null"); List <ODataPathSegment> pathSoFar = new List <ODataPathSegment>(); IEdmStructuredType currentLevelType = this.edmType; // first, walk through all type segments in a row, converting them from tokens into segments. if (tokenIn.IsNamespaceOrContainerQualified() && !UriParserHelper.IsAnnotation(tokenIn.Identifier)) { PathSegmentToken firstNonTypeToken; pathSoFar.AddRange(SelectExpandPathBinder.FollowTypeSegments(tokenIn, this.Model, this.Settings.SelectExpandLimit, this.configuration.Resolver, ref currentLevelType, out firstNonTypeToken)); Debug.Assert(firstNonTypeToken != null, "Did not get last token."); tokenIn = firstNonTypeToken as NonSystemToken; if (tokenIn == null) { throw new ODataException(ODataErrorStrings.SelectExpandBinder_SystemTokenInSelect(firstNonTypeToken.Identifier)); } } // next, create a segment for the first non-type segment in the path. ODataPathSegment lastSegment = SelectPathSegmentTokenBinder.ConvertNonTypeTokenToSegment(tokenIn, this.Model, currentLevelType, this.configuration.Resolver, this.state); // next, create an ODataPath and add the segments to it. if (lastSegment != null) { pathSoFar.Add(lastSegment); // try create a complex type property path. while (true) { // no need to go on if the current property is not of complex type or collection of complex type, // unless the segment is a primitive type cast or a property on an open complex property. currentLevelType = lastSegment.EdmType as IEdmStructuredType; IEdmCollectionType collectionType = lastSegment.EdmType as IEdmCollectionType; IEdmPrimitiveType primitiveType = lastSegment.EdmType as IEdmPrimitiveType; DynamicPathSegment dynamicPath = lastSegment as DynamicPathSegment; if ((currentLevelType == null || currentLevelType.TypeKind != EdmTypeKind.Complex) && (collectionType == null || collectionType.ElementType.TypeKind() != EdmTypeKind.Complex) && (primitiveType == null || primitiveType.TypeKind != EdmTypeKind.Primitive) && (dynamicPath == null || tokenIn.NextToken == null)) { break; } NonSystemToken nextToken = tokenIn.NextToken as NonSystemToken; if (nextToken == null) { break; } if (UriParserHelper.IsAnnotation(nextToken.Identifier)) { lastSegment = SelectPathSegmentTokenBinder.ConvertNonTypeTokenToSegment(nextToken, this.Model, currentLevelType, this.configuration.Resolver, null); } else if (primitiveType == null && dynamicPath == null) { // This means last segment a collection of complex type, // current segment can only be type cast and cannot be property name. if (currentLevelType == null) { currentLevelType = collectionType.ElementType.Definition as IEdmStructuredType; } // If there is no collection type in the path yet, will try to bind property for the next token // first try bind the segment as property. lastSegment = SelectPathSegmentTokenBinder.ConvertNonTypeTokenToSegment(nextToken, this.Model, currentLevelType, this.configuration.Resolver, null); } else { // determine whether we are looking at a type cast or a dynamic path segment. EdmPrimitiveTypeKind nextTypeKind = EdmCoreModel.Instance.GetPrimitiveTypeKind(nextToken.Identifier); IEdmPrimitiveType castType = EdmCoreModel.Instance.GetPrimitiveType(nextTypeKind); if (castType != null) { lastSegment = new TypeSegment(castType, castType, null); } else if (dynamicPath != null) { lastSegment = new DynamicPathSegment(nextToken.Identifier); } else { throw new ODataException(ODataErrorStrings.SelectBinder_MultiLevelPathInSelect); } } // then try bind the segment as type cast. if (lastSegment == null) { IEdmStructuredType typeFromNextToken = UriEdmHelpers.FindTypeFromModel(this.Model, nextToken.Identifier, this.configuration.Resolver) as IEdmStructuredType; if (typeFromNextToken.IsOrInheritsFrom(currentLevelType)) { lastSegment = new TypeSegment(typeFromNextToken, /*entitySet*/ null); } } // type cast failed too. if (lastSegment == null) { break; } // try move to and add next path segment. tokenIn = nextToken; pathSoFar.Add(lastSegment); } } // non-navigation cases do not allow further segments in $select. if (tokenIn.NextToken != null) { throw new ODataException(ODataErrorStrings.SelectBinder_MultiLevelPathInSelect); } // Later, we can consider to create a "DynamicOperationSegment" to handle this. // But now, Let's throw exception. if (lastSegment == null) { throw new ODataException(ODataErrorStrings.MetadataBinder_InvalidIdentifierInQueryOption(tokenIn.Identifier)); } // navigation property is not allowed to append sub path in the selection. NavigationPropertySegment navPropSegment = pathSoFar.LastOrDefault() as NavigationPropertySegment; if (navPropSegment != null && tokenIn.NextToken != null) { throw new ODataException(ODataErrorStrings.SelectBinder_MultiLevelPathInSelect); } return(pathSoFar); }
/// <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; ComputeToken computeOption = null; IEnumerable <QueryToken> applyOptions = 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; // Prepend '$' prefix if needed. if (this.enableNoDollarQueryOptions && !text.StartsWith(UriQueryConstants.DollarSign, StringComparison.Ordinal)) { text = string.Format(CultureInfo.InvariantCulture, "{0}{1}", UriQueryConstants.DollarSign, 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.QueryOptionCompute: { this.lexer.NextToken(); string computeText = this.ReadQueryOption(); UriQueryExpressionParser computeParser = new UriQueryExpressionParser(this.MaxOrderByDepth, enableCaseInsensitiveBuiltinIdentifier); computeOption = computeParser.ParseCompute(computeText); break; } case ExpressionConstants.QueryOptionApply: // nested $apply { this.lexer.NextToken(); string applyText = this.ReadQueryOption(); UriQueryExpressionParser applyParser = new UriQueryExpressionParser(this.MaxOrderByDepth, enableCaseInsensitiveBuiltinIdentifier); applyOptions = applyParser.ParseApply(applyText); 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, this.enableCaseInsensitiveBuiltinIdentifier, this.enableNoDollarQueryOptions); 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, computeOption, applyOptions); expandTermTokenList.Add(currentToken); return(expandTermTokenList); }
/// <summary> /// Generate an expand item (and a select item for the implicit nav prop if necessary) based on an ExpandTermToken /// </summary> /// <param name="tokenIn">the expandTerm token to visit</param> /// <returns>the expand item for this expand term token.</returns> private SelectItem GenerateExpandItem(ExpandTermToken tokenIn) { ExceptionUtils.CheckArgumentNotNull(tokenIn, "tokenIn"); PathSegmentToken currentToken = tokenIn.PathToNavigationProp; IEdmStructuredType currentLevelEntityType = this.EdmType; List <ODataPathSegment> pathSoFar = new List <ODataPathSegment>(); PathSegmentToken firstNonTypeToken = currentToken; if (currentToken.IsNamespaceOrContainerQualified()) { pathSoFar.AddRange(SelectExpandPathBinder.FollowTypeSegments(currentToken, this.Model, this.Settings.SelectExpandLimit, this.configuration.Resolver, ref currentLevelEntityType, out firstNonTypeToken)); } IEdmProperty edmProperty = this.configuration.Resolver.ResolveProperty(currentLevelEntityType, firstNonTypeToken.Identifier); if (edmProperty == null) { throw new ODataException(ODataErrorStrings.MetadataBinder_PropertyNotDeclared(currentLevelEntityType.FullTypeName(), currentToken.Identifier)); } IEdmNavigationProperty currentNavProp = edmProperty as IEdmNavigationProperty; IEdmStructuralProperty currentComplexProp = edmProperty as IEdmStructuralProperty; if (currentNavProp == null && currentComplexProp == null) { throw new ODataException(ODataErrorStrings.ExpandItemBinder_PropertyIsNotANavigationPropertyOrComplexProperty(currentToken.Identifier, currentLevelEntityType.FullTypeName())); } if (currentComplexProp != null) { currentNavProp = ParseComplexTypesBeforeNavigation(currentComplexProp, ref firstNonTypeToken, pathSoFar); } // ensure that we're always dealing with proper V4 syntax if (firstNonTypeToken.NextToken != null && firstNonTypeToken.NextToken.NextToken != null) { throw new ODataException(ODataErrorStrings.ExpandItemBinder_TraversingMultipleNavPropsInTheSamePath); } bool isRef = false; bool isCount = false; if (firstNonTypeToken.NextToken != null) { // lastly... make sure that, since we're on a NavProp, that the next token isn't null. if (firstNonTypeToken.NextToken.Identifier == UriQueryConstants.RefSegment) { isRef = true; } else if (firstNonTypeToken.NextToken.Identifier == UriQueryConstants.CountSegment) { isCount = true; } else { throw new ODataException(ODataErrorStrings.ExpandItemBinder_TraversingMultipleNavPropsInTheSamePath); } } // Add the segments in select and expand to parsed segments List <ODataPathSegment> parsedPath = new List <ODataPathSegment>(this.parsedSegments); parsedPath.AddRange(pathSoFar); IEdmNavigationSource targetNavigationSource = null; if (this.NavigationSource != null) { IEdmPathExpression bindingPath; targetNavigationSource = this.NavigationSource.FindNavigationTarget(currentNavProp, BindingPathHelper.MatchBindingPath, parsedPath, out bindingPath); } NavigationPropertySegment navSegment = new NavigationPropertySegment(currentNavProp, targetNavigationSource); pathSoFar.Add(navSegment); parsedPath.Add(navSegment); // Add the navigation property segment to parsed segments for future usage. ODataExpandPath pathToNavProp = new ODataExpandPath(pathSoFar); // $apply ApplyClause applyOption = BindApply(tokenIn.ApplyOptions, this.ResourcePathNavigationSource, targetNavigationSource); // $compute ComputeClause computeOption = BindCompute(tokenIn.ComputeOption, this.ResourcePathNavigationSource, targetNavigationSource); var generatedProperties = GetGeneratedProperties(computeOption, applyOption); bool collapsed = applyOption?.Transformations.Any(t => t.Kind == TransformationNodeKind.Aggregate || t.Kind == TransformationNodeKind.GroupBy) ?? false; // $filter FilterClause filterOption = BindFilter(tokenIn.FilterOption, this.ResourcePathNavigationSource, targetNavigationSource, null, generatedProperties, collapsed); // $orderby OrderByClause orderbyOption = BindOrderby(tokenIn.OrderByOptions, this.ResourcePathNavigationSource, targetNavigationSource, null, generatedProperties, collapsed); // $search SearchClause searchOption = BindSearch(tokenIn.SearchOption, this.ResourcePathNavigationSource, targetNavigationSource, null); if (isRef) { return(new ExpandedReferenceSelectItem(pathToNavProp, targetNavigationSource, filterOption, orderbyOption, tokenIn.TopOption, tokenIn.SkipOption, tokenIn.CountQueryOption, searchOption, computeOption, applyOption)); } if (isCount) { return(new ExpandedCountSelectItem(pathToNavProp, targetNavigationSource, filterOption, searchOption)); } // $select & $expand SelectExpandClause subSelectExpand = BindSelectExpand(tokenIn.ExpandOption, tokenIn.SelectOption, parsedPath, this.ResourcePathNavigationSource, targetNavigationSource, null, generatedProperties, collapsed); // $levels LevelsClause levelsOption = ParseLevels(tokenIn.LevelsOption, currentLevelEntityType, currentNavProp); return(new ExpandedNavigationSelectItem(pathToNavProp, targetNavigationSource, subSelectExpand, filterOption, orderbyOption, tokenIn.TopOption, tokenIn.SkipOption, tokenIn.CountQueryOption, searchOption, levelsOption, computeOption, applyOption)); }
/// <summary> /// Uses the ExpressionLexer to visit the next ExpressionToken, and delegates parsing of segments, type segments, identifiers, /// and the star token to other methods. /// </summary> /// <param name="previousSegment">Previously parsed PathSegmentToken, or null if this is the first token.</param> /// <param name="allowRef">Whether the $ref operation is valid in this token.</param> /// <returns>A parsed PathSegmentToken representing the next segment in this path.</returns> private PathSegmentToken ParseSegment(PathSegmentToken previousSegment, bool allowRef) { if (this.lexer.CurrentToken.Text.StartsWith("$", StringComparison.Ordinal) && (!allowRef || this.lexer.CurrentToken.Text != UriQueryConstants.RefSegment) && this.lexer.CurrentToken.Text != UriQueryConstants.CountSegment) { throw new ODataException(ODataErrorStrings.UriSelectParser_SystemTokenInSelectExpand(this.lexer.CurrentToken.Text, this.lexer.ExpressionText)); } // Some check here to throw exception, prop1/*/prop2 and */$ref/prop and prop1/$count/prop2 will throw exception, all are $expand cases. if (!isSelect) { if (previousSegment != null && previousSegment.Identifier == UriQueryConstants.Star && this.lexer.CurrentToken.GetIdentifier() != UriQueryConstants.RefSegment) { // Star can only be followed with $ref. $count is not supported with star as expand option throw new ODataException(ODataErrorStrings.ExpressionToken_OnlyRefAllowWithStarInExpand); } else if (previousSegment != null && previousSegment.Identifier == UriQueryConstants.RefSegment) { // $ref should not have more property followed. throw new ODataException(ODataErrorStrings.ExpressionToken_NoPropAllowedAfterRef); } else if (previousSegment != null && previousSegment.Identifier == UriQueryConstants.CountSegment) { // $count should not have more property followed. e.g $expand=NavProperty/$count/MyProperty throw new ODataException(ODataErrorStrings.ExpressionToken_NoPropAllowedAfterDollarCount); } } if (this.lexer.CurrentToken.Text == UriQueryConstants.CountSegment && isSelect) { // $count is not allowed in $select e.g $select=NavProperty/$count throw new ODataException(ODataErrorStrings.ExpressionToken_DollarCountNotAllowedInSelect); } string propertyName; if (this.lexer.PeekNextToken().Kind == ExpressionTokenKind.Dot) { propertyName = this.lexer.ReadDottedIdentifier(this.isSelect); } else if (this.lexer.CurrentToken.Kind == ExpressionTokenKind.Star) { // "*/$ref" is supported in expand if (this.lexer.PeekNextToken().Kind == ExpressionTokenKind.Slash && isSelect) { throw new ODataException(ODataErrorStrings.ExpressionToken_IdentifierExpected(this.lexer.Position)); } else if (previousSegment != null && !isSelect) { // expand option like "customer?$expand=VIPCustomer/*" is not allowed as specification does not allowed any property before *. throw new ODataException(ODataErrorStrings.ExpressionToken_NoSegmentAllowedBeforeStarInExpand); } propertyName = this.lexer.CurrentToken.Text; this.lexer.NextToken(); } else { propertyName = this.lexer.CurrentToken.GetIdentifier(); this.lexer.NextToken(); } return(new NonSystemToken(propertyName, null, previousSegment)); }
/// <summary> /// Visit a System Token /// </summary> /// <param name="tokenIn">the system token to visit</param> public override void Visit(SystemToken tokenIn) { ExceptionUtils.CheckArgumentNotNull(tokenIn, "tokenIn"); throw new ODataException(ODataErrorStrings.SelectPropertyVisitor_SystemTokenInSelect(tokenIn.Identifier)); }
/// <summary> /// Binds a <see cref="InnerPathToken"/>. /// This includes more than just navigations - it includes complex property access and primitive collections. /// </summary> /// <param name="segmentToken">The segment token to bind.</param> /// <returns>The bound node.</returns> internal QueryNode BindInnerPathSegment(InnerPathToken segmentToken) { FunctionCallBinder functionCallBinder = new FunctionCallBinder(this.bindMethod, state); // First we get the parent node QueryNode parent = this.DetermineParentNode(segmentToken, state); Debug.Assert(parent != null, "parent should never be null"); SingleValueNode singleValueParent = parent as SingleValueNode; if (singleValueParent == null) { QueryNode boundFunction; if (functionCallBinder.TryBindInnerPathAsFunctionCall(segmentToken, parent, out boundFunction)) { return(boundFunction); } CollectionNavigationNode collectionParent = parent as CollectionNavigationNode; if (collectionParent != null) { IEdmEntityTypeReference parentType = collectionParent.EntityItemType; IEdmProperty collectionProperty = this.Resolver.ResolveProperty(parentType.StructuredDefinition(), segmentToken.Identifier); if (collectionProperty != null && collectionProperty.PropertyKind == EdmPropertyKind.Structural) { return(new AggregatedCollectionPropertyNode(collectionParent, collectionProperty)); } } throw new ODataException(ODataErrorStrings.MetadataBinder_PropertyAccessSourceNotSingleValue(segmentToken.Identifier)); } // Using the parent and name of this token, we try to get the IEdmProperty it represents IEdmProperty property = BindProperty(singleValueParent.TypeReference, segmentToken.Identifier, this.Resolver); if (property == null) { QueryNode boundFunction; if (functionCallBinder.TryBindInnerPathAsFunctionCall(segmentToken, parent, out boundFunction)) { return(boundFunction); } if (singleValueParent.TypeReference != null && !singleValueParent.TypeReference.Definition.IsOpen()) { throw new ODataException( ODataErrorStrings.MetadataBinder_PropertyNotDeclared( parent.GetEdmTypeReference().FullName(), segmentToken.Identifier)); } return(new SingleValueOpenPropertyAccessNode(singleValueParent, segmentToken.Identifier)); } IEdmStructuralProperty structuralProperty = property as IEdmStructuralProperty; if (property.Type.IsComplex()) { // Generate a segment to parsed segments for the parsed token state.ParsedSegments.Add(new PropertySegment(structuralProperty)); return(new SingleComplexNode(singleValueParent as SingleResourceNode, property)); } else if (property.Type.IsPrimitive()) { return(new SingleValuePropertyAccessNode(singleValueParent, property)); } // Note - this means nonentity collection (primitive or complex) if (property.Type.IsNonEntityCollectionType()) { if (property.Type.IsStructuredCollectionType()) { // Generate a segment to parsed segments for the parsed token state.ParsedSegments.Add(new PropertySegment(structuralProperty)); return(new CollectionComplexNode(singleValueParent as SingleResourceNode, property)); } return(new CollectionPropertyAccessNode(singleValueParent, property)); } IEdmNavigationProperty navigationProperty = property as IEdmNavigationProperty; if (navigationProperty == null) { throw new ODataException(ODataErrorStrings.MetadataBinder_IllegalSegmentType(property.Name)); } SingleResourceNode parentResource = EnsureParentIsResourceForNavProp(singleValueParent); IEdmNavigationSource navigationSource; QueryNode node = GetNavigationNode(navigationProperty, parentResource, segmentToken.NamedValues, state, new KeyBinder(this.bindMethod), out navigationSource); // Generate a segment to parsed segments for the parsed token state.ParsedSegments.Add(new NavigationPropertySegment(navigationProperty, navigationSource)); return(node); }
/// <summary> /// Validate the arguments to either isof or cast /// </summary> /// <param name="state">the current state of the binding algorithm</param> /// <param name="isCast">flag to indicate which function we're validating</param> /// <param name="args">the list of arguments, which could be changed</param> /// <returns>the return type of the function.</returns> private static IEdmTypeReference ValidateIsOfOrCast(BindingState state, bool isCast, ref List <QueryNode> args) { if (args.Count != 1 && args.Count != 2) { throw new ODataErrorException( ODataErrorStrings.MetadataBinder_CastOrIsOfExpressionWithWrongNumberOfOperands(args.Count)); } ConstantNode typeArgument = args.Last() as ConstantNode; IEdmTypeReference returnType = null; if (typeArgument != null) { returnType = TryGetTypeReference(state.Model, typeArgument.Value as string, state.Configuration.Resolver); } if (returnType == null) { throw new ODataException(ODataErrorStrings.MetadataBinder_CastOrIsOfFunctionWithoutATypeArgument); } if (returnType.IsCollection()) { throw new ODataException(ODataErrorStrings.MetadataBinder_CastOrIsOfCollectionsNotSupported); } // if we only have one argument, then add the implicit range variable as the first argument. if (args.Count == 1) { args = new List <QueryNode>() { new ResourceRangeVariableReferenceNode( state.ImplicitRangeVariable.Name, state.ImplicitRangeVariable as ResourceRangeVariable), args[0] }; } else if (!(args[0] is SingleValueNode)) { throw new ODataException(ODataErrorStrings.MetadataBinder_CastOrIsOfCollectionsNotSupported); } if (isCast && (args.Count == 2)) { // throw if cast enum to not-string : if ((args[0].GetEdmTypeReference() is IEdmEnumTypeReference) && !string.Equals(typeArgument.Value as string, Microsoft.OData.Metadata.EdmConstants.EdmStringTypeName, StringComparison.Ordinal)) { throw new ODataException(ODataErrorStrings.CastBinder_EnumOnlyCastToOrFromString); } // throw if cast not-string to enum : while (returnType is IEdmEnumTypeReference) { IEdmTypeReference edmTypeReference = args[0].GetEdmTypeReference(); if (edmTypeReference == null) { // Support cast null to enum break; } IEdmPrimitiveTypeReference referenceTmp = edmTypeReference as IEdmPrimitiveTypeReference; if (referenceTmp != null) { IEdmPrimitiveType typeTmp = referenceTmp.Definition as IEdmPrimitiveType; if ((typeTmp != null) && (typeTmp.PrimitiveKind == EdmPrimitiveTypeKind.String)) { break; } } throw new ODataException(ODataErrorStrings.CastBinder_EnumOnlyCastToOrFromString); } } if (isCast) { return(returnType); } else { return(EdmCoreModel.Instance.GetBoolean(true)); } }
/// <summary> /// Converts the given payload value to the type defined in a type definition. /// </summary> /// <param name="value">The given payload value.</param> /// <param name="edmTypeReference">The expected type reference from model.</param> /// <returns>The converted value of the type.</returns> public virtual object ConvertFromPayloadValue(object value, IEdmTypeReference edmTypeReference) { IEdmPrimitiveTypeReference primitiveTypeReference = edmTypeReference as IEdmPrimitiveTypeReference; Debug.Assert(primitiveTypeReference != null, "primitiveTypeReference != null"); if (primitiveTypeReference.PrimitiveKind() == EdmPrimitiveTypeKind.PrimitiveType) { return(value); } try { Type targetType = EdmLibraryExtensions.GetPrimitiveClrType(primitiveTypeReference.PrimitiveDefinition(), false); string stringValue = value as string; if (stringValue != null) { return(ConvertStringValue(stringValue, targetType)); } else if (value is Int32) { return(ConvertInt32Value((int)value, targetType, primitiveTypeReference)); } else if (value is Decimal) { Decimal decimalValue = (Decimal)value; if (targetType == typeof(Int64)) { return(Convert.ToInt64(decimalValue)); } if (targetType == typeof(Double)) { return(Convert.ToDouble(decimalValue)); } if (targetType == typeof(Single)) { return(Convert.ToSingle(decimalValue)); } if (targetType != typeof(Decimal)) { throw new ODataException(ODataErrorStrings.ODataJsonReaderUtils_CannotConvertDecimal(primitiveTypeReference.FullName())); } } else if (value is Double) { Double doubleValue = (Double)value; if (targetType == typeof(Single)) { return(Convert.ToSingle(doubleValue)); } if (targetType != typeof(Double)) { throw new ODataException(ODataErrorStrings.ODataJsonReaderUtils_CannotConvertDouble(primitiveTypeReference.FullName())); } } else if (value is bool) { if (targetType != typeof(bool)) { throw new ODataException(ODataErrorStrings.ODataJsonReaderUtils_CannotConvertBoolean(primitiveTypeReference.FullName())); } } else if (value is DateTime) { if (targetType != typeof(DateTime)) { throw new ODataException(ODataErrorStrings.ODataJsonReaderUtils_CannotConvertDateTime(primitiveTypeReference.FullName())); } } else if (value is DateTimeOffset) { if (targetType != typeof(DateTimeOffset)) { throw new ODataException(ODataErrorStrings.ODataJsonReaderUtils_CannotConvertDateTimeOffset(primitiveTypeReference.FullName())); } } } catch (Exception e) { if (!ExceptionUtils.IsCatchableExceptionType(e)) { throw; } throw ReaderValidationUtils.GetPrimitiveTypeConversionException(primitiveTypeReference, e, value.ToString()); } // otherwise just return the value without doing any conversion return(value); }
public void ExpandPathShouldNotAllowCountSegment() { Action createWithCountSegment = () => new ODataExpandPath(CountSegment.Instance, this.navigationSegment); createWithCountSegment.ShouldThrow <ODataException>().WithMessage(ODataErrorStrings.ODataExpandPath_InvalidExpandPathSegment("CountSegment")); }
public void OperationWithParenthesesShouldNotWork() { Action action = () => SelectPathSegmentTokenBinder.ConvertNonTypeTokenToSegment(new NonSystemToken("Move()", null, null), HardCodedTestModel.TestModel, HardCodedTestModel.GetPersonType(), DefaultUriResolver).Should(); action.ShouldThrow <ODataException>().WithMessage(ODataErrorStrings.MetadataBinder_PropertyNotDeclared("Fully.Qualified.Namespace.Person", "Move()")); }
public void ExpandPathShouldNotAllowValueSegment() { Action createWithValueSegment = () => new ODataExpandPath(new ValueSegment(HardCodedTestModel.GetPersonType()), this.navigationSegment); createWithValueSegment.ShouldThrow <ODataException>().WithMessage(ODataErrorStrings.ODataExpandPath_InvalidExpandPathSegment("ValueSegment")); }
public void WriteNonOpenEntryWithUndeclaredProperty() { ODataResource res = new ODataResource() { Properties = new[] { new ODataProperty { Name = "Key", Value = "son" }, new ODataProperty { Name = "OpenProperty", Value = "Open" } } }; ODataNestedResourceInfo nestedComplexInfo = new ODataNestedResourceInfo() { Name = "OpenComplex" }; ODataResource nestedComplex = new ODataResource() { TypeName = "Fake.ComplexType", Properties = new[] { new ODataProperty { Name = "P1", Value = "cv" } } }; ODataNestedResourceInfo nestedResInfo = new ODataNestedResourceInfo() { Name = "OpenNavigationProperty", IsCollection = false }; ODataResource nestedRes = new ODataResource() { Id = new Uri("http://temp.org/Type"), EditLink = new Uri("http://temp.org/Type"), ReadLink = new Uri("http://temp.org/Type"), ETag = "etag", TypeName = "Fake.Type", Properties = new[] { new ODataProperty { Name = "Key", Value = "son" } } }; var actual = WriteJsonLightEntryForUndeclared( isOpenType: false, writeAction: (writer) => { writer.WriteStart(res); writer.WriteStart(nestedComplexInfo); writer.WriteStart(nestedComplex); writer.WriteEnd(); writer.WriteEnd(); writer.WriteStart(nestedResInfo); writer.WriteStart(nestedRes); writer.WriteEnd(); writer.WriteEnd(); writer.WriteEnd(); }, throwOnUndeclaredProperty: false ); var expected = "{" + "\"@odata.context\":\"http://temp.org/$metadata#FakeSet/$entity\"," + "\"@odata.id\":\"FakeSet('son')\"," + "\"@odata.editLink\":\"FakeSet('son')\"," + "\"Key\":\"son\"," + "\"OpenProperty\":\"Open\"," + "\"OpenComplex\":" + "{" + "\"@odata.type\":\"#Fake.ComplexType\"," + "\"P1\":\"cv\"" + "}," + "\"[email protected]\":\"http://temp.org/FakeSet('son')/OpenNavigationProperty/$ref\"," + "\"[email protected]\":\"http://temp.org/FakeSet('son')/OpenNavigationProperty\"," + "\"OpenNavigationProperty\":" + "{" + "\"@odata.type\":\"#Fake.Type\"," + "\"@odata.id\":\"http://temp.org/Type\"," + "\"@odata.etag\":\"etag\"," + "\"@odata.editLink\":\"http://temp.org/Type\"," + "\"Key\":\"son\"" + "}" + "}"; Assert.Equal(expected, actual); // Should throw on undeclared primitive value property res = new ODataResource() { Properties = new[] { new ODataProperty { Name = "Key", Value = "son" }, new ODataProperty { Name = "OpenProperty", Value = "Open" } } }; Action writeEntry = () => WriteJsonLightEntryForUndeclared( isOpenType: false, writeAction: (writer) => { writer.WriteStart(res); writer.WriteEnd(); }, throwOnUndeclaredProperty: true ); writeEntry.ShouldThrow <ODataException>().WithMessage(ErrorStrings.ValidationUtils_PropertyDoesNotExistOnType("OpenProperty", "Fake.Type")); // Should throw on undeclared complex value property res = new ODataResource() { Properties = new[] { new ODataProperty { Name = "Key", Value = "son" } } }; nestedComplexInfo = new ODataNestedResourceInfo() { Name = "OpenComplex" }; nestedComplex = new ODataResource() { TypeName = "Fake.ComplexType", Properties = new[] { new ODataProperty { Name = "P1", Value = "cv" } } }; writeEntry = () => WriteJsonLightEntryForUndeclared( isOpenType: false, writeAction: (writer) => { writer.WriteStart(res); writer.WriteStart(nestedComplexInfo); writer.WriteStart(nestedComplex); writer.WriteEnd(); writer.WriteEnd(); writer.WriteEnd(); }, throwOnUndeclaredProperty: true ); writeEntry.ShouldThrow <ODataException>().WithMessage(ErrorStrings.ValidationUtils_PropertyDoesNotExistOnType("OpenComplex", "Fake.Type")); // Should throw on undeclared navigation property res = new ODataResource() { Properties = new[] { new ODataProperty { Name = "Key", Value = "son" } } }; nestedResInfo = new ODataNestedResourceInfo() { Name = "OpenNavigationProperty", IsCollection = false }; nestedRes = new ODataResource() { Id = new Uri("http://temp.org/Type"), EditLink = new Uri("http://temp.org/Type"), ReadLink = new Uri("http://temp.org/Type"), ETag = "etag", TypeName = "Fake.Type", Properties = new[] { new ODataProperty { Name = "Key", Value = "son" } } }; writeEntry = () => WriteJsonLightEntryForUndeclared( isOpenType: false, writeAction: (writer) => { writer.WriteStart(res); writer.WriteStart(nestedResInfo); writer.WriteStart(nestedRes); writer.WriteEnd(); writer.WriteEnd(); writer.WriteEnd(); }, throwOnUndeclaredProperty: true ); writeEntry.ShouldThrow <ODataException>().WithMessage(ErrorStrings.ValidationUtils_PropertyDoesNotExistOnType("OpenNavigationProperty", "Fake.Type")); }
public void ExpressionLexerShouldFailByDefaultForAtSymbol() { Action lex = () => new ExpressionLexer("@", moveToFirstToken: true, useSemicolonDelimiter: false); lex.ShouldThrow <ODataException>().WithMessage(ODataErrorStrings.ExpressionLexer_SyntaxError(1, "@")); }
/// <summary> /// Binds an end path token into a PropertyAccessToken, OpenPropertyToken, or FunctionCallToken. /// </summary> /// <param name="endPathToken">The property access token to bind.</param> /// <returns>A Query node representing this endpath token, bound to metadata.</returns> internal QueryNode BindEndPath(EndPathToken endPathToken) { ExceptionUtils.CheckArgumentNotNull(endPathToken, "EndPathToken"); ExceptionUtils.CheckArgumentStringNotNullOrEmpty(endPathToken.Identifier, "EndPathToken.Identifier"); // Set the parent (get the parent type, so you can check whether the Identifier inside EndPathToken really is legit offshoot of the parent type) QueryNode parent = this.DetermineParentNode(endPathToken); QueryNode boundFunction; SingleValueNode singleValueParent = parent as SingleValueNode; if (singleValueParent != null) { if (endPathToken.Identifier == ExpressionConstants.QueryOptionCount) { return(new CountVirtualPropertyNode()); } if (state.IsCollapsed && !IsAggregatedProperty(endPathToken)) { throw new ODataException(ODataErrorStrings.ApplyBinder_GroupByPropertyNotPropertyAccessValue(endPathToken.Identifier)); } // Now that we have the parent type, can find its corresponding EDM type IEdmStructuredTypeReference structuredParentType = singleValueParent.TypeReference == null ? null : singleValueParent.TypeReference.AsStructuredOrNull(); IEdmProperty property = structuredParentType == null ? null : this.Resolver.ResolveProperty(structuredParentType.StructuredDefinition(), endPathToken.Identifier); if (property != null) { return(GeneratePropertyAccessQueryNode(singleValueParent as SingleResourceNode, property, state)); } if (functionCallBinder.TryBindEndPathAsFunctionCall(endPathToken, singleValueParent, state, out boundFunction)) { return(boundFunction); } return(GeneratePropertyAccessQueryForOpenType(endPathToken, singleValueParent)); } // Collection with any or all expression is already supported and handled separately. // Add support of collection with $count segment. CollectionNode colNode = parent as CollectionNode; if (colNode != null && endPathToken.Identifier.Equals(UriQueryConstants.CountSegment, System.StringComparison.Ordinal)) { // create a collection count node for collection node property. return(new CountNode(colNode)); } CollectionNavigationNode collectionParent = parent as CollectionNavigationNode; if (collectionParent != null) { IEdmEntityTypeReference parentType = collectionParent.EntityItemType; IEdmProperty property = this.Resolver.ResolveProperty(parentType.StructuredDefinition(), endPathToken.Identifier); if (property.PropertyKind == EdmPropertyKind.Structural && !property.Type.IsCollection() && this.state.InEntitySetAggregation) { return(new AggregatedCollectionPropertyNode(collectionParent, property)); } } if (functionCallBinder.TryBindEndPathAsFunctionCall(endPathToken, parent, state, out boundFunction)) { return(boundFunction); } throw new ODataException(ODataErrorStrings.MetadataBinder_PropertyAccessSourceNotSingleValue(endPathToken.Identifier)); }
public void ExpressionLexerShouldFailAtSymbolIsLastCharacter() { Action lex = () => new ExpressionLexer("@", moveToFirstToken: true, useSemicolonDelimiter: false, parsingFunctionParameters: true); lex.ShouldThrow <ODataException>().WithMessage(ODataErrorStrings.ExpressionLexer_SyntaxError(1, "@")); }
/// <summary> /// Writes an instance annotation. /// </summary> /// <param name="instanceAnnotation">The instance annotation to write.</param> /// <param name="ignoreFilter">Whether to ignore the filter in settings.</param> /// <param name="propertyName">The name of the property this instance annotation applies to</param> internal void WriteInstanceAnnotation(ODataInstanceAnnotation instanceAnnotation, bool ignoreFilter = false, string propertyName = null) { string name = instanceAnnotation.Name; ODataValue value = instanceAnnotation.Value; Debug.Assert(!string.IsNullOrEmpty(name), "name should not be null or empty"); Debug.Assert(value != null, "value should not be null because we use ODataNullValue for null instead"); Debug.Assert(!(value is ODataStreamReferenceValue), "!(value is ODataStreamReferenceValue) -- ODataInstanceAnnotation and InstanceAnnotationCollection will throw if the value is a stream value."); Debug.Assert(this.valueSerializer.Model != null, "this.valueSerializer.Model != null"); if (!ignoreFilter && this.valueSerializer.MessageWriterSettings.ShouldSkipAnnotation(name)) { return; } IEdmTypeReference expectedType = MetadataUtils.LookupTypeOfTerm(name, this.valueSerializer.Model); if (value is ODataNullValue) { if (expectedType != null && !expectedType.IsNullable) { throw new ODataException( ODataErrorStrings.JsonLightInstanceAnnotationWriter_NullValueNotAllowedForInstanceAnnotation( instanceAnnotation.Name, expectedType.FullName())); } this.WriteInstanceAnnotationName(propertyName, name); this.valueSerializer.WriteNullValue(); return; } // If we didn't find an expected type from looking up the term in the model, treat this value the same way we would for open property values. // That is, write the type name (unless its a primitive value with a JSON-native type). If we did find an expected type, treat the annotation value like a // declared property with an expected type. This will still write out the type if the value type is more derived than the declared type, for example. bool treatLikeOpenProperty = expectedType == null; ODataCollectionValue collectionValue = value as ODataCollectionValue; if (collectionValue != null) { IEdmTypeReference typeFromCollectionValue = (IEdmCollectionTypeReference)TypeNameOracle.ResolveAndValidateTypeForCollectionValue( this.valueSerializer.Model, expectedType, collectionValue, treatLikeOpenProperty, this.writerValidator); string collectionTypeNameToWrite = this.typeNameOracle.GetValueTypeNameForWriting(collectionValue, expectedType, typeFromCollectionValue, treatLikeOpenProperty); if (collectionTypeNameToWrite != null) { this.odataAnnotationWriter.WriteODataTypePropertyAnnotation(name, collectionTypeNameToWrite); } this.WriteInstanceAnnotationName(propertyName, name); this.valueSerializer.WriteCollectionValue(collectionValue, expectedType, typeFromCollectionValue, false /*isTopLevelProperty*/, false /*isInUri*/, treatLikeOpenProperty); return; } ODataUntypedValue untypedValue = value as ODataUntypedValue; if (untypedValue != null) { this.WriteInstanceAnnotationName(propertyName, name); this.valueSerializer.WriteUntypedValue(untypedValue); return; } ODataEnumValue enumValue = value as ODataEnumValue; if (enumValue != null) { this.WriteInstanceAnnotationName(propertyName, name); this.valueSerializer.WriteEnumValue(enumValue, expectedType); return; } ODataPrimitiveValue primitiveValue = value as ODataPrimitiveValue; Debug.Assert(primitiveValue != null, "Did we add a new subclass of ODataValue?"); IEdmTypeReference typeFromPrimitiveValue = TypeNameOracle.ResolveAndValidateTypeForPrimitiveValue(primitiveValue); string primitiveTypeNameToWrite = this.typeNameOracle.GetValueTypeNameForWriting(primitiveValue, expectedType, typeFromPrimitiveValue, treatLikeOpenProperty); if (primitiveTypeNameToWrite != null) { this.odataAnnotationWriter.WriteODataTypePropertyAnnotation(name, primitiveTypeNameToWrite); } this.WriteInstanceAnnotationName(propertyName, name); this.valueSerializer.WritePrimitiveValue(primitiveValue.Value, typeFromPrimitiveValue, expectedType); }
public void ExpressionLexerShouldExpectIdentifierStartAfterAtSymbol() { Action lex = () => new ExpressionLexer("@1", moveToFirstToken: true, useSemicolonDelimiter: false, parsingFunctionParameters: true); lex.ShouldThrow <ODataException>().WithMessage(ODataErrorStrings.ExpressionLexer_InvalidCharacter("1", 1, "@1")); }
private ODataPayloadKind ParseContextUriFragment(string fragment, Func <IEdmType, string, IEdmType> clientCustomTypeResolver, out bool isUndeclared) { bool hasItemSelector = false; ODataDeltaKind kind = ODataDeltaKind.None; isUndeclared = false; // Deal with /$entity if (fragment.EndsWith(ODataConstants.ContextUriFragmentItemSelector, StringComparison.Ordinal)) { hasItemSelector = true; fragment = fragment.Substring(0, fragment.Length - ODataConstants.ContextUriFragmentItemSelector.Length); } else if (fragment.EndsWith(ODataConstants.ContextUriDeltaResourceSet, StringComparison.Ordinal)) { kind = ODataDeltaKind.ResourceSet; fragment = fragment.Substring(0, fragment.Length - ODataConstants.ContextUriDeltaResourceSet.Length); } else if (fragment.EndsWith(ODataConstants.ContextUriDeletedEntry, StringComparison.Ordinal)) { kind = ODataDeltaKind.DeletedEntry; fragment = fragment.Substring(0, fragment.Length - ODataConstants.ContextUriDeletedEntry.Length); } else if (fragment.EndsWith(ODataConstants.ContextUriDeltaLink, StringComparison.Ordinal)) { kind = ODataDeltaKind.Link; fragment = fragment.Substring(0, fragment.Length - ODataConstants.ContextUriDeltaLink.Length); } else if (fragment.EndsWith(ODataConstants.ContextUriDeletedLink, StringComparison.Ordinal)) { kind = ODataDeltaKind.DeletedLink; fragment = fragment.Substring(0, fragment.Length - ODataConstants.ContextUriDeletedLink.Length); } this.parseResult.DeltaKind = kind; // Deal with query option if (fragment.EndsWith(")", StringComparison.Ordinal)) { int index = fragment.Length - 2; for (int rcount = 1; rcount > 0 && index > 0; --index) { switch (fragment[index]) { case '(': rcount--; break; case ')': rcount++; break; } } if (index == 0) { throw new ODataException(ODataErrorStrings.ODataJsonLightContextUriParser_InvalidContextUrl(UriUtils.UriToString(this.parseResult.ContextUri))); } string previous = fragment.Substring(0, index + 1); // Don't treat Collection(Edm.Type) as SelectExpand segment if (!previous.Equals("Collection")) { string selectExpandStr = fragment.Substring(index + 2); selectExpandStr = selectExpandStr.Substring(0, selectExpandStr.Length - 1); // Do not treat Key as SelectExpand segment if (KeyPattern.IsMatch(selectExpandStr)) { throw new ODataException(ODataErrorStrings.ODataJsonLightContextUriParser_LastSegmentIsKeySegment(UriUtils.UriToString(this.parseResult.ContextUri))); } this.parseResult.SelectQueryOption = ExtractSelectQueryOption(selectExpandStr); fragment = previous; } } ODataPayloadKind detectedPayloadKind = ODataPayloadKind.Unsupported; EdmTypeResolver edmTypeResolver = new EdmTypeReaderResolver(this.model, clientCustomTypeResolver); if (!fragment.Contains(ODataConstants.UriSegmentSeparator) && !hasItemSelector && kind == ODataDeltaKind.None) { // Service document: no fragment if (fragment.Length == 0) { detectedPayloadKind = ODataPayloadKind.ServiceDocument; } else if (fragment.Equals(ODataConstants.EntityReferenceCollectionSegmentName + "(" + ODataConstants.EntityReferenceSegmentName + ")")) { detectedPayloadKind = ODataPayloadKind.EntityReferenceLinks; } else if (fragment.Equals(ODataConstants.EntityReferenceSegmentName)) { detectedPayloadKind = ODataPayloadKind.EntityReferenceLink; } else { var foundNavigationSource = this.model.FindDeclaredNavigationSource(fragment); if (foundNavigationSource != null) { // Resource Set: {schema.entity-container.entity-set} or Singleton: {schema.entity-container.singleton} this.parseResult.NavigationSource = foundNavigationSource; this.parseResult.EdmType = edmTypeResolver.GetElementType(foundNavigationSource); detectedPayloadKind = foundNavigationSource is IEdmSingleton ? ODataPayloadKind.Resource : ODataPayloadKind.ResourceSet; } else { // Property: {schema.type} or Collection({schema.type}) where schema.type is primitive or complex. detectedPayloadKind = this.ResolveType(fragment, clientCustomTypeResolver); Debug.Assert( this.parseResult.EdmType.TypeKind == EdmTypeKind.Primitive || this.parseResult.EdmType.TypeKind == EdmTypeKind.Enum || this.parseResult.EdmType.TypeKind == EdmTypeKind.TypeDefinition || this.parseResult.EdmType.TypeKind == EdmTypeKind.Complex || this.parseResult.EdmType.TypeKind == EdmTypeKind.Collection || this.parseResult.EdmType.TypeKind == EdmTypeKind.Entity, "The first context URI segment must be a set or a non-entity type."); } } } else { Debug.Assert(this.parseResult.MetadataDocumentUri.IsAbsoluteUri, "this.parseResult.MetadataDocumentUri.IsAbsoluteUri"); string metadataDocumentStr = UriUtils.UriToString(this.parseResult.MetadataDocumentUri); if (!metadataDocumentStr.EndsWith(ODataConstants.UriMetadataSegment, StringComparison.Ordinal)) { throw new ODataException(ODataErrorStrings.ODataJsonLightContextUriParser_InvalidContextUrl(UriUtils.UriToString(this.parseResult.ContextUri))); } Uri serviceRoot = new Uri(metadataDocumentStr.Substring(0, metadataDocumentStr.Length - ODataConstants.UriMetadataSegment.Length)); ODataUriParser odataUriParser = new ODataUriParser(this.model, serviceRoot, new Uri(serviceRoot, fragment)); ODataPath path; try { path = odataUriParser.ParsePath(); } catch (ODataException) { throw new ODataException(ODataErrorStrings.ODataJsonLightContextUriParser_InvalidContextUrl(UriUtils.UriToString(this.parseResult.ContextUri))); } if (path.Count == 0) { throw new ODataException(ODataErrorStrings.ODataJsonLightContextUriParser_InvalidContextUrl(UriUtils.UriToString(this.parseResult.ContextUri))); } this.parseResult.Path = path; parseResult.NavigationSource = path.NavigationSource(); parseResult.EdmType = path.LastSegment.EdmType; ODataPathSegment lastSegment = path.TrimEndingTypeSegment().LastSegment; if (lastSegment is EntitySetSegment || lastSegment is NavigationPropertySegment) { if (kind != ODataDeltaKind.None) { detectedPayloadKind = ODataPayloadKind.Delta; } else { detectedPayloadKind = hasItemSelector ? ODataPayloadKind.Resource : ODataPayloadKind.ResourceSet; } if (this.parseResult.EdmType is IEdmCollectionType) { var collectionTypeReference = this.parseResult.EdmType.ToTypeReference().AsCollection(); if (collectionTypeReference != null) { this.parseResult.EdmType = collectionTypeReference.ElementType().Definition; } } } else if (lastSegment is SingletonSegment) { detectedPayloadKind = ODataPayloadKind.Resource; } else if (path.IsIndividualProperty()) { isUndeclared = path.IsUndeclared(); detectedPayloadKind = ODataPayloadKind.Property; IEdmComplexType complexType = parseResult.EdmType as IEdmComplexType; if (complexType != null) { detectedPayloadKind = ODataPayloadKind.Resource; } else { IEdmCollectionType collectionType = parseResult.EdmType as IEdmCollectionType; if (collectionType != null) { if (collectionType.ElementType.IsComplex()) { this.parseResult.EdmType = collectionType.ElementType.Definition; detectedPayloadKind = ODataPayloadKind.ResourceSet; } else { detectedPayloadKind = ODataPayloadKind.Collection; } } } } else { throw new ODataException(ODataErrorStrings.ODataJsonLightContextUriParser_InvalidContextUrl(UriUtils.UriToString(this.parseResult.ContextUri))); } } return(detectedPayloadKind); }
public void OverClosedBracketsThrow() { ValidateLexerException <ODataException>("{stuff: morestuff}}", ODataErrorStrings.ExpressionLexer_InvalidCharacter("}", "18", "{stuff: morestuff}}")); }
private void ProcessTokenAsPath(NonSystemToken tokenIn) { Debug.Assert(tokenIn != null, "tokenIn != null"); List <ODataPathSegment> pathSoFar = new List <ODataPathSegment>(); IEdmStructuredType currentLevelType = this.edmType; // first, walk through all type segments in a row, converting them from tokens into segments. if (tokenIn.IsNamespaceOrContainerQualified()) { PathSegmentToken firstNonTypeToken; pathSoFar.AddRange(SelectExpandPathBinder.FollowTypeSegments(tokenIn, this.model, this.maxDepth, this.resolver, ref currentLevelType, out firstNonTypeToken)); Debug.Assert(firstNonTypeToken != null, "Did not get last token."); tokenIn = firstNonTypeToken as NonSystemToken; if (tokenIn == null) { throw new ODataException(ODataErrorStrings.SelectPropertyVisitor_SystemTokenInSelect(firstNonTypeToken.Identifier)); } } // next, create a segment for the first non-type segment in the path. ODataPathSegment lastSegment = SelectPathSegmentTokenBinder.ConvertNonTypeTokenToSegment(tokenIn, this.model, currentLevelType, resolver); // next, create an ODataPath and add the segments to it. if (lastSegment != null) { pathSoFar.Add(lastSegment); // try create a complex type property path. while (true) { // no need to go on if the current property is not of complex type or collection of complex type, // unless the segment is a primitive type cast or a property on an open complex property. currentLevelType = lastSegment.EdmType as IEdmStructuredType; IEdmCollectionType collectionType = lastSegment.EdmType as IEdmCollectionType; IEdmPrimitiveType primitiveType = lastSegment.EdmType as IEdmPrimitiveType; DynamicPathSegment dynamicPath = lastSegment as DynamicPathSegment; if ((currentLevelType == null || currentLevelType.TypeKind != EdmTypeKind.Complex) && (collectionType == null || collectionType.ElementType.TypeKind() != EdmTypeKind.Complex) && (primitiveType == null || primitiveType.TypeKind != EdmTypeKind.Primitive) && (dynamicPath == null || tokenIn.NextToken == null)) { break; } NonSystemToken nextToken = tokenIn.NextToken as NonSystemToken; if (nextToken == null) { break; } if (primitiveType == null && dynamicPath == null) { // This means last segment a collection of complex type, // current segment can only be type cast and cannot be property name. if (currentLevelType == null) { currentLevelType = collectionType.ElementType.Definition as IEdmStructuredType; } // If there is no collection type in the path yet, will try to bind property for the next token // first try bind the segment as property. lastSegment = SelectPathSegmentTokenBinder.ConvertNonTypeTokenToSegment(nextToken, this.model, currentLevelType, resolver); } else { // determine whether we are looking at a type cast or a dynamic path segment. EdmPrimitiveTypeKind nextTypeKind = EdmCoreModel.Instance.GetPrimitiveTypeKind(nextToken.Identifier); IEdmPrimitiveType castType = EdmCoreModel.Instance.GetPrimitiveType(nextTypeKind); if (castType != null) { lastSegment = new TypeSegment(castType, castType, null); } else if (dynamicPath != null) { lastSegment = new DynamicPathSegment(nextToken.Identifier); } else { throw new ODataException(ODataErrorStrings.SelectBinder_MultiLevelPathInSelect); } } // then try bind the segment as type cast. if (lastSegment == null) { IEdmStructuredType typeFromNextToken = UriEdmHelpers.FindTypeFromModel(this.model, nextToken.Identifier, this.resolver) as IEdmStructuredType; if (typeFromNextToken.IsOrInheritsFrom(currentLevelType)) { lastSegment = new TypeSegment(typeFromNextToken, /*entitySet*/ null); } } // type cast failed too. if (lastSegment == null) { break; } // try move to and add next path segment. tokenIn = nextToken; pathSoFar.Add(lastSegment); } } ODataSelectPath selectedPath = new ODataSelectPath(pathSoFar); var selectionItem = new PathSelectItem(selectedPath); // non-navigation cases do not allow further segments in $select. if (tokenIn.NextToken != null) { throw new ODataException(ODataErrorStrings.SelectBinder_MultiLevelPathInSelect); } // if the selected item is a nav prop, then see if its already there before we add it. NavigationPropertySegment trailingNavPropSegment = selectionItem.SelectedPath.LastSegment as NavigationPropertySegment; if (trailingNavPropSegment != null) { if (this.expandClauseToDecorate.SelectedItems.Any(x => x is PathSelectItem && ((PathSelectItem)x).SelectedPath.Equals(selectedPath))) { return; } } this.expandClauseToDecorate.AddToSelectedItems(selectionItem); }
public void MaxOperationsPerChangesetShouldThrowIfSetToNegativeNumber() { Action testSubject = () => this.settings.MessageQuotas.MaxOperationsPerChangeset = -1; testSubject.ShouldThrow <ArgumentOutOfRangeException>().Where(e => e.Message.StartsWith(ODataErrorStrings.ExceptionUtils_CheckIntegerNotNegative(-1))); }