/// <summary>Tries to create a key segment for the given filter if it is non empty.</summary> /// <param name="previous">Segment on which to compose.</param> /// <param name="previousKeySegment">The parent node's key segment.</param> /// <param name="parenthesisExpression">Parenthesis expression of segment.</param> /// <param name="keySegment">The key segment that was created if the key was non-empty.</param> /// <param name="enableUriTemplateParsing">Whether Uri template parsing is enabled.</param> /// <param name="resolver">The resolver to use.</param> /// <returns>Whether the key was non-empty.</returns> internal static bool TryCreateKeySegmentFromParentheses(ODataPathSegment previous, KeySegment previousKeySegment, string parenthesisExpression, out ODataPathSegment keySegment, bool enableUriTemplateParsing = false, ODataUriResolver resolver = null) { Debug.Assert(parenthesisExpression != null, "parenthesisExpression != null"); Debug.Assert(previous != null, "segment!= null"); if (resolver == null) { resolver = ODataUriResolver.Default; } if (previous.SingleResult) { throw ExceptionUtil.CreateSyntaxError(); } SegmentArgumentParser key; if (!SegmentArgumentParser.TryParseKeysFromUri(parenthesisExpression, out key, enableUriTemplateParsing)) { throw ExceptionUtil.CreateSyntaxError(); } // People/NS.Employees() is OK, just like People() is OK if (key.IsEmpty) { keySegment = null; return false; } keySegment = CreateKeySegment(previous, previousKeySegment, key, resolver); return true; }
/// <summary> /// Initializes a new instance of the <see cref="UnresolvedPathSegment" /> class. /// </summary> /// <param name="previous">The property being accessed by this segment.</param> /// <param name="segmentValue">The unresolved segment value.</param> public UnresolvedPathSegment(ODataPathSegment previous, string segmentValue) : base(previous) { if (segmentValue == null) { throw Error.ArgumentNull("segmentValue"); } SegmentValue = segmentValue; }
public SegmentKeyHandlerTests() { var typeWithStringKey = new EdmEntityType("NS.Foo", "TypeWithStringKey"); var originalType = new EdmEntityType("NS.Foo", "SourceType"); var keyProp = typeWithStringKey.AddStructuralProperty("KeyProp", EdmPrimitiveTypeKind.String); typeWithStringKey.AddKeys(keyProp); var multipleResultsWithSingleKeyNavProp = originalType.AddUnidirectionalNavigation(new EdmNavigationPropertyInfo() { Name = "NavProp", Target = typeWithStringKey, TargetMultiplicity = EdmMultiplicity.Many }); this.singleResultSegmentWithSingleKey = new NavigationPropertySegment(HardCodedTestModel.GetPersonMyDogNavProp(), null) { SingleResult = true }; this.multipleResultSegmentWithSingleKey = new NavigationPropertySegment(multipleResultsWithSingleKeyNavProp, null) { SingleResult = false }; this.multipleResultSegmentWithCompositeKey = new NavigationPropertySegment(HardCodedTestModel.GetPersonMyLionsNavProp(), null) { SingleResult = false }; }
/// <summary> /// Tries to handle the current segment as a key property value. /// </summary> /// <param name="segmentText">The segment text.</param> /// <param name="previous">The previous segment.</param> /// <param name="previousKeySegment">The parent node's key segment.</param> /// <param name="urlConvention">The current url convention for the server.</param> /// <param name="keySegment">The key segment that was created if the segment could be interpreted as a key.</param> /// <param name="enableUriTemplateParsing">Whether Uri template parsing is enabled.</param> /// <param name="resolver">The resolver to use.</param> /// <returns>Whether or not the segment was interpreted as a key.</returns> internal static bool TryHandleSegmentAsKey(string segmentText, ODataPathSegment previous, KeySegment previousKeySegment, UrlConvention urlConvention, out KeySegment keySegment, bool enableUriTemplateParsing = false, ODataUriResolver resolver = null) { Debug.Assert(previous != null, "previous != null"); Debug.Assert(urlConvention != null, "urlConvention != null"); if (resolver == null) { resolver = ODataUriResolver.Default; } keySegment = null; // If the current convention is not keys-as-segments, then this does not apply. if (!urlConvention.GenerateKeyAsSegment) { return false; } // Keys only apply to collections, so if the prior segment is already a singleton, do not treat this segment as a key. if (previous.SingleResult) { return false; } // System segments (ie '$count') are never keys. if (IsSystemSegment(segmentText)) { return false; } // If the previous type is not an entity collection type // TODO: collapse this and SingleResult. IEdmEntityType targetEntityType; if (previous.TargetEdmType == null || !previous.TargetEdmType.IsEntityOrEntityCollectionType(out targetEntityType)) { return false; } // Previously KeyAsSegment only allows single key, but we can also leverage related key finder to auto fill // missed key value from referential constraint information, which would be done in CreateKeySegment. // CreateKeySegment method will check whether key properties are missing after taking in related key values. keySegment = CreateKeySegment(previous, previousKeySegment, SegmentArgumentParser.FromSegment(segmentText, enableUriTemplateParsing), resolver); return true; }
private static IEdmEntityType GetTargetEntityType(ODataPathSegment segment) { Contract.Assert(segment != null); EntitySetSegment entitySetSegment = segment as EntitySetSegment; if (entitySetSegment != null) { return entitySetSegment.EntitySet.EntityType(); } SingletonSegment singletonSegment = segment as SingletonSegment; if (singletonSegment != null) { return singletonSegment.Singleton.EntityType(); } NavigationPropertySegment navigationPropertySegment = segment as NavigationPropertySegment; if (navigationPropertySegment != null) { return navigationPropertySegment.NavigationSource.EntityType(); } return null; }
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); bool hasCollectionInPath = false; // 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. currentLevelType = lastSegment.EdmType as IEdmStructuredType; var collectionType = lastSegment.EdmType as IEdmCollectionType; if ((currentLevelType == null || currentLevelType.TypeKind != EdmTypeKind.Complex) && (collectionType == null || collectionType.ElementType.TypeKind() != EdmTypeKind.Complex)) { break; } NonSystemToken nextToken = tokenIn.NextToken as NonSystemToken; if (nextToken == null) { break; } lastSegment = null; // This means last segment a collection of complex type, // current segment can only be type cast and cannot be proprty name. if (currentLevelType == null) { currentLevelType = collectionType.ElementType.Definition as IEdmStructuredType; hasCollectionInPath = true; } else if (!hasCollectionInPath) { // 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); } // 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); }
/// <summary> /// Tries to get the resource property for the given segment, if it is one of the segment types that refers to a property, a navigation, or a navigation before $ref. /// </summary> /// <param name="segment">The segment.</param> /// <param name="projectedProperty">The property, if the segment represented a property or navigation.</param> /// <returns>Whether the segment represented a property or navigation.</returns> private static bool TryGetPropertyFromSegment(ODataPathSegment segment, out ResourceProperty projectedProperty) { Debug.Assert(segment != null, "segment != null"); IEdmProperty edmProperty = null; var propertySegment = segment as PropertySegment; if (propertySegment != null) { edmProperty = propertySegment.Property; } else { var navigationSegment = segment as NavigationPropertySegment; if (navigationSegment != null) { edmProperty = navigationSegment.NavigationProperty; } else { var propertyLinkSegment = segment as NavigationPropertyLinkSegment; if (propertyLinkSegment != null) { edmProperty = propertyLinkSegment.NavigationProperty; } } } if (edmProperty != null) { projectedProperty = ((IResourcePropertyBasedEdmProperty)edmProperty).ResourceProperty; Debug.Assert(projectedProperty != null, "projectedProperty != null"); return true; } projectedProperty = null; return false; }
/// <summary> /// Throws if the given segment must be a leaf, as a later segment is being created. /// </summary> /// <param name="previous">The previous segment which may need to be a leaf.</param> private void ThrowIfMustBeLeafSegment(ODataPathSegment previous) { OperationImportSegment operationImportSegment = previous as OperationImportSegment; if (operationImportSegment != null) { foreach (var operationImport in operationImportSegment.OperationImports) { if (operationImport.IsActionImport() || (operationImport.IsFunctionImport() && !((IEdmFunctionImport)operationImport).Function.IsComposable)) { throw ExceptionUtil.CreateBadRequestError(ODataErrorStrings.RequestUriProcessor_MustBeLeafSegment(previous.Identifier)); } } } OperationSegment operationSegment = previous as OperationSegment; if (operationSegment != null) { foreach (var operation in operationSegment.Operations) { if (operation.IsAction() || (operation.IsFunction() && !((IEdmFunction)operation).IsComposable)) { throw ExceptionUtil.CreateBadRequestError(ODataErrorStrings.RequestUriProcessor_MustBeLeafSegment(previous.Identifier)); } } } if (previous.TargetKind == RequestTargetKind.Batch /* $batch */ || previous.TargetKind == RequestTargetKind.Metadata /* $metadata */ || previous.TargetKind == RequestTargetKind.PrimitiveValue /* $value, see TryCreateValueSegment */ || previous.TargetKind == RequestTargetKind.OpenPropertyValue /* $value, see TryCreateValueSegment */ || previous.TargetKind == RequestTargetKind.EnumValue /* $value, see TryCreateValueSegment */ || previous.TargetKind == RequestTargetKind.MediaResource /* $value or Media resource, see TryCreateValueSegment/CreateNamedStreamSegment */ || previous.TargetKind == RequestTargetKind.VoidOperation /* service operation with void return type */ || previous.TargetKind == RequestTargetKind.Nothing /* Nothing targeted (e.g. PathTemplate) */) { // Nothing can come after a $metadata, $value or $batch segment. // Nothing can come after a service operation with void return type. // Nothing can come after a collection property. throw ExceptionUtil.ResourceNotFoundError(ODataErrorStrings.RequestUriProcessor_MustBeLeafSegment(previous.Identifier)); } }
private bool TryCreateSegmentForOperation(ODataPathSegment previousSegment, string identifier, string parenthesisExpression) { // Parse Arguments syntactically var bindingType = previousSegment == null ? null : previousSegment.EdmType; ICollection<OperationSegmentParameter> resolvedParameters; IEdmOperation singleOperation; if (!TryBindingParametersAndMatchingOperation(identifier, parenthesisExpression, bindingType, this.configuration, out resolvedParameters, out singleOperation)) { return false; } if (!UriEdmHelpers.IsBindingTypeValid(bindingType)) { throw ExceptionUtil.CreateBadRequestError(ODataErrorStrings.RequestUriProcessor_OperationSegmentBoundToANonEntityType); } if (previousSegment != null && bindingType == null) { throw new ODataException(ODataErrorStrings.FunctionCallBinder_CallingFunctionOnOpenProperty(identifier)); } IEdmTypeReference returnType = singleOperation.ReturnType; IEdmEntitySetBase targetset = null; if (returnType != null) { IEdmNavigationSource source = previousSegment == null ? null : previousSegment.TargetEdmNavigationSource; targetset = singleOperation.GetTargetEntitySet(source, this.configuration.Model); } // If previous segment is cross-referenced then we explicitly dissallow the service action call if (previousSegment is BatchReferenceSegment) { throw ExceptionUtil.CreateBadRequestError(ODataErrorStrings.RequestUriProcessor_BatchedActionOnEntityCreatedInSameChangeset(identifier)); } // TODO: change constructor to take single import ODataPathSegment segment = new OperationSegment(new[] { singleOperation }, resolvedParameters, targetset) { Identifier = identifier }; DetermineEntitySetForSegment(identifier, returnType, segment, targetset, singleOperation); this.parsedSegments.Add(segment); this.TryBindKeySegmentIfNoResolvedParametersAndParathesisValueExsts(parenthesisExpression, returnType, resolvedParameters, segment); return true; }
private void HandleNavigationPathSegment(ODataPathSegment segment) { var navigationSegment = (NavigationPropertySegment)segment; var entityParameterExpression = Expression.Parameter(this.currentType); var navigationPropertyExpression = Expression.Property(entityParameterExpression, navigationSegment.NavigationProperty.Name); // Check whether property is null or not before further selection var whereExpression = CreateNotEqualsNullExpression(navigationPropertyExpression, entityParameterExpression); this.queryable = ExpressionHelpers.Where(this.queryable, whereExpression, this.currentType); if (navigationSegment.NavigationProperty.TargetMultiplicity() == EdmMultiplicity.Many) { // get the element type of the target // (the type should be an EntityCollection<T> for navigation queries). this.currentType = navigationPropertyExpression.Type.GetEnumerableItemType(); // need to explicitly define the delegate type as IEnumerable<T> Type delegateType = typeof(Func<,>).MakeGenericType( queryable.ElementType, typeof(IEnumerable<>).MakeGenericType(this.currentType)); LambdaExpression selectBody = Expression.Lambda(delegateType, navigationPropertyExpression, entityParameterExpression); this.queryable = ExpressionHelpers.SelectMany(this.queryable, selectBody, this.currentType); } else { this.currentType = navigationPropertyExpression.Type; LambdaExpression selectBody = Expression.Lambda(navigationPropertyExpression, entityParameterExpression); this.queryable = ExpressionHelpers.Select(this.queryable, selectBody); } }
private static bool TryBindAsDeclaredProperty(PathSegmentToken tokenIn, IEdmStructuredType edmType, ODataUriResolver resolver, out ODataPathSegment segment) { IEdmProperty prop = resolver.ResolveProperty(edmType, tokenIn.Identifier); if (prop == null) { segment = null; return false; } if (prop.PropertyKind == EdmPropertyKind.Structural) { segment = new PropertySegment((IEdmStructuralProperty)prop); return true; } if (prop.PropertyKind == EdmPropertyKind.Navigation) { segment = new NavigationPropertySegment((IEdmNavigationProperty)prop, null /*TODO: set*/); return true; } throw new ODataException(ODataErrorStrings.SelectExpandBinder_UnknownPropertyType(prop.Name)); }
public void FunctionNameResultsInOperationSelectionItemWithAllMatchingOverloads() { ODataPathSegment segment = SelectPathSegmentTokenBinder.ConvertNonTypeTokenToSegment(new NonSystemToken("Fully.Qualified.Namespace.HasDog", null, null), HardCodedTestModel.TestModel, HardCodedTestModel.GetPersonType()); segment.ShouldBeOperationSegment(HardCodedTestModel.GetAllHasDogFunctionOverloadsForPeople() /* TODO: parameters */); }
private void EmptyHandler(ODataPathSegment segment) { // Nothing will be done }
public void NavPropNameResultsInNavigationPropertyLinkSelectionItem() { ODataPathSegment segment = SelectPathSegmentTokenBinder.ConvertNonTypeTokenToSegment(new NonSystemToken("MyDog", null, null), HardCodedTestModel.TestModel, HardCodedTestModel.GetPersonType()); segment.ShouldBeNavigationPropertySegment(HardCodedTestModel.GetPersonMyDogNavProp()); }
public void ActionNameResultsInOperationSelectionItem() { ODataPathSegment segment = SelectPathSegmentTokenBinder.ConvertNonTypeTokenToSegment(new NonSystemToken("Fully.Qualified.Namespace.Walk", null, null), HardCodedTestModel.TestModel, HardCodedTestModel.GetDogType()); segment.ShouldBeOperationSegment(HardCodedTestModel.GetDogWalkAction()); }
public void PropertyNameResultsInStructuralPropertySelectionItem() { ODataPathSegment segment = SelectPathSegmentTokenBinder.ConvertNonTypeTokenToSegment(new NonSystemToken("Shoe", null, null), HardCodedTestModel.TestModel, HardCodedTestModel.GetPersonType()); segment.ShouldBePropertySegment(HardCodedTestModel.GetPersonShoeProp()); }
public void UnfoundProperyOnOpenTypeResultsInOpenPropertySelectionItem() { ODataPathSegment segment = SelectPathSegmentTokenBinder.ConvertNonTypeTokenToSegment(new NonSystemToken("Fully.Qualified.Namespace.Effervescence", null, null), HardCodedTestModel.TestModel, HardCodedTestModel.GetPaintingType()); segment.ShouldBeOpenPropertySegment("Fully.Qualified.Namespace.Effervescence"); }
public void QualifiedActionNameOnOpenTypeShouldBeInterpretedAsAnOperation() { ODataPathSegment segment = SelectPathSegmentTokenBinder.ConvertNonTypeTokenToSegment(new NonSystemToken("Fully.Qualified.Namespace.Restore", null, null), HardCodedTestModel.TestModel, HardCodedTestModel.GetPaintingType()); segment.ShouldBeOperationSegment(HardCodedTestModel.GetRestoreAction()); }
/// <summary> /// Creates the next segment. /// </summary> /// <param name="previous">The previous segment.</param> /// <param name="segment">The the next segment.</param> /// <returns>The newly created next segment.</returns> private SegmentInfo CreateNextSegment(SegmentInfo previous, ODataPathSegment segment) { if (segment is ValueSegment) { return CreateValueSegment(previous); } SegmentInfo segmentInfo; if (segment is CountSegment) { segmentInfo = CreateCountSegment(previous); // DEVNOTE: Prior to refactor, $count was handled alongside properties. See more detailed comment in HandleCountSegment Debug.Assert(segmentInfo.TargetSource == RequestTargetSource.Property, "segment.TargetSource == RequestTargetSource.Property"); return segmentInfo; } // if its not one of the recognized special segments, then it must be a property, type-segment, or key value. Debug.Assert( previous.TargetKind == RequestTargetKind.ComplexObject || previous.TargetKind == RequestTargetKind.Resource || previous.TargetKind == RequestTargetKind.OpenProperty || previous.TargetKind == RequestTargetKind.Link, "previous.TargetKind(" + previous.TargetKind + ") can have properties"); CheckSegmentIsComposable(previous); // if the segment corresponds to a declared property, handle it // otherwise, fall back to type-segments, actions, and dynamic/open properties ResourceProperty projectedProperty; if (TryGetPropertyFromSegment(segment, out projectedProperty)) { CheckSingleResult(previous.SingleResult, previous.Identifier); if (projectedProperty.IsOfKind(ResourcePropertyKind.Stream)) { segmentInfo = CreateNamedStreamSegment(previous, projectedProperty); Debug.Assert(segmentInfo.TargetSource == RequestTargetSource.Property, "segment.TargetSource == RequestTargetSource.Property"); return segmentInfo; } segmentInfo = this.CreatePropertySegment(previous, projectedProperty); Debug.Assert(segmentInfo.TargetSource == RequestTargetSource.Property, "segment.TargetSource == RequestTargetSource.Property"); return segmentInfo; } // If the property resolution failed, and the previous segment was targeting an entity, then we should // try and resolve the identifier as type name. SegmentInfo typeNameSegment; if (this.TryCreateTypeNameSegment(previous, segment, out typeNameSegment)) { Debug.Assert(typeNameSegment.TargetSource == previous.TargetSource, "segment.TargetSource == previous.TargetSource"); return typeNameSegment; } OperationWrapper serviceAction = null; var actionSegment = segment as OperationSegment; if (actionSegment != null) { Debug.Assert(actionSegment.Operations.Count() == 1, "Operation segment should only ever have exactly one operation. Was a change made to how MetadataProviderEdmModel finds actions/service operations"); serviceAction = ((MetadataProviderEdmOperation)actionSegment.Operations.Single()).ServiceOperation; Debug.Assert(serviceAction != null, "serviceAction != null"); Debug.Assert(serviceAction.Kind == OperationKind.Action, "serviceAction.Kind == OperationKind.Action"); } if (serviceAction != null) { Debug.Assert(serviceAction.Kind == OperationKind.Action, "serviceAction.Kind == OperationKind.Action"); // Service Actions can bind to a set with any ResourceSetRights except ResourceSetRights.None. segmentInfo = this.CreateSegmentForServiceAction(previous, serviceAction); Debug.Assert(segmentInfo.TargetSource == RequestTargetSource.ServiceOperation, "segment.TargetSource == RequestTargetSource.ServiceOperation"); return segmentInfo; } CheckSingleResult(previous.SingleResult, previous.Identifier); segmentInfo = CreateOpenPropertySegment(previous, ((OpenPropertySegment)segment).PropertyName); Debug.Assert(segmentInfo.TargetSource == RequestTargetSource.Property, "segment.TargetSource == RequestTargetSource.Property"); return segmentInfo; }
public void FunctionNameResultsInOperationSelectionItemWithOnlyOverloadsBoundToTheGivenType() { ODataPathSegment segment = SelectPathSegmentTokenBinder.ConvertNonTypeTokenToSegment(new NonSystemToken("Fully.Qualified.Namespace.HasDog", null, null), HardCodedTestModel.TestModel, HardCodedTestModel.GetEmployeeType()); segment.ShouldBeOperationSegment(HardCodedTestModel.GetHasDogOverloadForEmployee()); }
private void HandleEntitySetPathSegment(ODataPathSegment segment) { var entitySetPathSegment = (EntitySetSegment)segment; var entitySet = entitySetPathSegment.EntitySet; this.queryable = this.api.GetQueryableSource(entitySet.Name, (object[])null); this.currentType = this.queryable.ElementType; }
public void ActionNameResultsInOperationSelectionForDerivedBindingTypeItemWithAllMatchingOverloads() { ODataPathSegment segment = SelectPathSegmentTokenBinder.ConvertNonTypeTokenToSegment(new NonSystemToken("Fully.Qualified.Namespace.Move", null, null), HardCodedTestModel.TestModel, HardCodedTestModel.GetEmployeeType()); segment.ShouldBeOperationSegment(HardCodedTestModel.GetMoveOverloadForEmployee() /* TODO: parameters */); }
private void HandleValuePathSegment(ODataPathSegment segment) { this.IsValuePathSegmentPresent = true; }
public void ActionNameResultsInOperationSelectionItemWithAllMatchingOverloadsMatchingBindingType() { ODataPathSegment segment = SelectPathSegmentTokenBinder.ConvertNonTypeTokenToSegment(new NonSystemToken("Fully.Qualified.Namespace.Move", null, null), HardCodedTestModel.TestModel, HardCodedTestModel.GetPersonType()); segment.ShouldBeOperationSegment(HardCodedTestModel.GetMoveOverloadForPerson()); }
// This only covers entity type cast // complex type cast uses ComplexCastPathSegment and is not supported by EF now // CLR type is got from model annotation, which means model must include that annotation. private void HandleEntityTypeSegment(ODataPathSegment segment) { var typeSegment = (TypeSegment)segment; var edmType = typeSegment.EdmType; if (typeSegment.EdmType.TypeKind == EdmTypeKind.Collection) { edmType = ((IEdmCollectionType)typeSegment.EdmType).ElementType.Definition; } if (edmType.TypeKind == EdmTypeKind.Entity) { this.currentType = edmType.GetClrType(api); this.queryable = ExpressionHelpers.OfType(this.queryable, this.currentType); } }
/// <summary> /// Matches the template with an <see cref="ODataPathSegment"/>. /// </summary> /// <param name="pathSegment">The path segment to match this template with.</param> /// <param name="values">The dictionary of matches to be updated if the segment matches the template.</param> /// <returns><c>true</c> if the segment matches the template; otherwise, <c>false</c>.</returns> public override bool TryMatch(ODataPathSegment pathSegment, IDictionary <string, object> values) { return(pathSegment is T); }
private void CreatePropertySegment(ODataPathSegment previous, IEdmProperty property, string queryPortion) { if (property.Type.IsStream()) { // The server used to allow arbitrary key expressions after named streams because this check was missing. if (queryPortion != null) { throw ExceptionUtil.CreateSyntaxError(); } this.CreateNamedStreamSegment(previous, property); return; } // Handle a strongly-typed property. ODataPathSegment segment = null; if (property.PropertyKind == EdmPropertyKind.Navigation) { var navigationProperty = (IEdmNavigationProperty)property; IEdmNavigationSource navigationSource = previous.TargetEdmNavigationSource.FindNavigationTarget(navigationProperty); segment = new NavigationPropertySegment(navigationProperty, navigationSource); } else { segment = new PropertySegment((IEdmStructuralProperty)property); switch (property.Type.TypeKind()) { case EdmTypeKind.Complex: segment.TargetKind = RequestTargetKind.ComplexObject; break; case EdmTypeKind.Collection: segment.TargetKind = RequestTargetKind.Collection; break; case EdmTypeKind.Enum: segment.TargetKind = RequestTargetKind.Enum; break; default: Debug.Assert(property.Type.IsPrimitive() || property.Type.IsTypeDefinition(), "must be primitive type or type definition property"); segment.TargetKind = RequestTargetKind.Primitive; break; } } this.parsedSegments.Add(segment); if (!(queryPortion == null || property.Type.IsCollection() && property.Type.AsCollection().ElementType().IsEntity())) { throw ExceptionUtil.CreateSyntaxError(); } this.TryBindKeyFromParentheses(queryPortion); }
private ODataPayloadKind ParseContextUriFragment(string fragment, Func <IEdmType, string, IEdmType> clientCustomTypeResolver, bool throwIfMetadataConflict, 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", StringComparison.Ordinal)) { 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.CollectionPrefix + "(" + ODataConstants.EntityReferenceSegmentName + ")", StringComparison.Ordinal)) { detectedPayloadKind = ODataPayloadKind.EntityReferenceLinks; } else if (fragment.Equals(ODataConstants.EntityReferenceSegmentName, StringComparison.Ordinal)) { 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, throwIfMetadataConflict); 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 || this.parseResult.EdmType.TypeKind == EdmTypeKind.Untyped, "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); }
/// <summary> /// Creates a named stream segment /// </summary> /// <param name="previous">previous segment info.</param> /// <param name="streamProperty">stream property to create the segment for.</param> private void CreateNamedStreamSegment(ODataPathSegment previous, IEdmProperty streamProperty) { Debug.Assert(streamProperty.Type.IsStream(), "streamProperty.Type.IsStream()"); // Handle Named Stream. ODataPathSegment segment = new PropertySegment((IEdmStructuralProperty)streamProperty); segment.TargetKind = RequestTargetKind.MediaResource; segment.SingleResult = true; segment.TargetEdmType = previous.TargetEdmType; Debug.Assert(segment.Identifier != UriQueryConstants.ValueSegment, "'$value' cannot be the name of a named stream."); this.parsedSegments.Add(segment); }
/// <summary> /// Initializes a new instance of the <see cref="LinksPathSegment" /> class. /// </summary> /// <param name="previous">The previous segment in the path.</param> public LinksPathSegment(ODataPathSegment previous) : base(previous) { EdmType = previous.EdmType; EntitySet = previous.EntitySet; }
public void UnqualifiedActionNameOnOpenTypeShouldBeInterpretedAsAnOpenProperty() { ODataPathSegment segment = SelectPathSegmentTokenBinder.ConvertNonTypeTokenToSegment(new NonSystemToken("Restore", null, null), HardCodedTestModel.TestModel, HardCodedTestModel.GetPaintingType()); segment.ShouldBeOpenPropertySegment("Restore"); }
/// <summary> /// Java specific function to generate Object constructor section of a code snippet. In the event that another object is needed in the middle of generation, /// a recursive call is made to sort out the needed object. /// </summary> /// <param name="pathSegment">Odata Function/Entity from which the object is needed</param> /// <param name="jsonString">Json string from which the information of the object to be initialized is held</param> /// <param name="path">List of strings/identifier showing the path through the Edm/json structure to reach the Class Identifier from the segment</param> /// <param name="usedVarNames">List to keep track of variable names used to prevent use of the same variable name</param> private static string JavaGenerateObjectFromJson(ODataPathSegment pathSegment, string jsonString, List <string> path, List <string> usedVarNames = null) { var stringBuilder = new StringBuilder(); var jsonObject = JsonConvert.DeserializeObject(jsonString); usedVarNames = usedVarNames ?? new List <string>();//make sure list is not null switch (jsonObject) { case string _: { var enumString = GenerateEnumString(jsonObject.ToString(), pathSegment, path); if (!string.IsNullOrEmpty(enumString)) { //Enum is accessed as the Classname then enum type e.g Importance.LOW stringBuilder.Append($"{enumString.Split(".").First()} {path.Last()} = {enumString};\r\n"); } else if (jsonObject.Equals("true") || jsonObject.Equals("false")) { stringBuilder.Append($"boolean {path.Last()} = {jsonObject};\r\n"); //boolean primitives values masquerading as strings. } else { stringBuilder.Append($"String {path.Last()} = \"{jsonObject}\";\r\n"); } } break; case JObject jObject: { var currentVarName = EnsureJavaVariableNameIsUnique(path.Last(), usedVarNames); var className = GetJavaClassNameFromOdataPath(pathSegment, path); stringBuilder.Append($"{className} { currentVarName } = new {className}();\r\n"); //initialize each member/property of the object foreach (var(key, jToken) in jObject) { var value = JsonConvert.SerializeObject(jToken); var newPath = path.Append(key).ToList(); //add new identifier to the path if (key.Contains("@odata") || key.StartsWith("@")) //sometimes @odata maybe in the middle e.g."*****@*****.**" { stringBuilder = GenerateJavaAdditionalDataSection(stringBuilder, key, jToken.ToString(), className, currentVarName); continue; } switch (jToken.Type) { case JTokenType.Array: case JTokenType.Object: //new nested object needs to be constructed so call this function recursively to make it stringBuilder.Append($"{JavaGenerateObjectFromJson(pathSegment, value, newPath, usedVarNames)}"); stringBuilder.Append(jToken.Type == JTokenType.Array ? $"{ currentVarName }.{ newPath.Last() } = { EnsureJavaVariableNameIsUnique(newPath.Last()+"List", usedVarNames) };\r\n" : $"{ currentVarName }.{ newPath.Last() } = { EnsureJavaVariableNameIsUnique(newPath.Last(), usedVarNames) };\r\n"); break; case JTokenType.String: var enumString = GenerateEnumString(jToken.ToString(), pathSegment, newPath); //check if the type is an enum and handle it stringBuilder.Append(!string.IsNullOrEmpty(enumString) ? $"{ currentVarName }.{newPath.Last()} = {enumString};\r\n" : $"{ currentVarName }.{newPath.Last()} = {value.Replace("\n", "").Replace("\r", "")};\r\n"); break; default: stringBuilder.Append($"{ currentVarName }.{newPath.Last()} = { value.Replace("\n", "").Replace("\r", "") };\r\n"); break; } usedVarNames.Add(jToken.Type == JTokenType.Array? $"{newPath.Last()}List": newPath.Last()); //add used variable name to used list } } break; case JArray array: { var objectList = array.Children <JObject>(); if (objectList.Any()) { var className = GetJavaClassNameFromOdataPath(pathSegment, path); var currentListName = EnsureJavaVariableNameIsUnique(path.Last() + "List", usedVarNames); //Item is a list/array so declare a typed list stringBuilder.Append($"LinkedList<{className}> {currentListName} = new LinkedList<{className}>();\r\n"); foreach (var item in objectList) { var currentListItemName = EnsureJavaVariableNameIsUnique(path.Last(), usedVarNames); var jsonItemString = JsonConvert.SerializeObject(item); //we need to create a new object var objectStringFromJson = JavaGenerateObjectFromJson(pathSegment, jsonItemString, path, usedVarNames); stringBuilder.Append($"{objectStringFromJson}"); stringBuilder.Append($"{currentListName}.add({currentListItemName});\r\n"); usedVarNames.Add(path.Last()); //add used variable name to used list } } else { stringBuilder.Append($"LinkedList<String> {path.Last()}List = new LinkedList<String>();\r\n"); //its not nested objects but a string collection foreach (var element in array) { stringBuilder.Append($"{path.Last()}List.add(\"{element.Value<string>()}\");\r\n"); } } } break; case null: //do nothing break; default: var primitive = jsonObject.ToString(); //json deserializer capitalizes the bool types so undo that if (primitive.Equals("True", StringComparison.Ordinal) || primitive.Equals("False", StringComparison.Ordinal)) { stringBuilder.Append($"boolean {path.Last()} = {CommonGenerator.LowerCaseFirstLetter(primitive)};\r\n"); } else { stringBuilder.Append($"int {path.Last()} = {primitive};\r\n"); //item is a primitive print as is } break; } //check if this is the outermost object in a potential nested object structure and needs the semicolon termination character. return(path.Count == 1 ? $"{stringBuilder.ToString().TrimEnd()}\r\n\r\n" : stringBuilder.ToString()); }
/// <summary> /// Initializes a new instance of the <see cref="ValuePathSegment" /> class. /// </summary> /// <param name="previous">The previous segment in the path.</param> public ValuePathSegment(ODataPathSegment previous) : base(previous) { EdmType = previous.EdmType; EntitySet = previous.EntitySet; }
/// <summary> /// Matches the template with an <see cref="ODataPathSegment"/>. /// </summary> /// <param name="pathSegment">The path segment to match this template with.</param> /// <param name="values">The dictionary of matches to be updated if the segment matches the template.</param> /// <returns><c>true</c> if the segment matches the template; otherwise, <c>false</c>.</returns> public virtual bool TryMatch(ODataPathSegment pathSegment, IDictionary <string, object> values) { return(false); }
/// <summary>Creates the first <see cref="SegmentInfo"/> for a request.</summary> /// <param name="segment">The text of the segment.</param> /// <returns>A description of the information on the segment.</returns> private SegmentInfo CreateFirstSegment(ODataPathSegment segment) { Debug.Assert(segment != null, "identifier != null"); // Look for well-known system entry points. if (segment is MetadataSegment) { return new SegmentInfo { Identifier = XmlConstants.UriMetadataSegment, TargetKind = RequestTargetKind.Metadata }; } if (segment is BatchSegment) { return new SegmentInfo { Identifier = XmlConstants.UriBatchSegment, TargetKind = RequestTargetKind.Batch }; } if (segment is CountSegment) { // $count on root: throw throw DataServiceException.CreateResourceNotFound(Strings.RequestUriProcessor_CountOnRoot); } // Look for a service operation. OperationImportSegment serviceOperation = segment as OperationImportSegment; if (serviceOperation != null) { Debug.Assert(serviceOperation.OperationImports.Count() == 1, "Operation import segment should only ever have exactly one operation. Was a change made to how MetadataProviderEdmModel finds actions/service operations"); var operationImport = serviceOperation.OperationImports.Single(); var operation = ((MetadataProviderEdmOperationImport)operationImport).ServiceOperation; Debug.Assert(operation != null, "operation != null"); if (operation.Kind == OperationKind.ServiceOperation) { return CreateSegmentForServiceOperation(operation); } Debug.Assert(operation.Kind == OperationKind.Action, "serviceAction.Kind == OperationKind.Action"); return this.CreateSegmentForServiceAction(null /*previousSegment*/, operation); } var batchReferenceSegment = segment as BatchReferenceSegment; if (batchReferenceSegment != null) { SegmentInfo referencedSegmentInfo = this.crossReferenceCallback(batchReferenceSegment.ContentId); Debug.Assert(referencedSegmentInfo != null, "Could not find SegmentInfo for content-id: " + batchReferenceSegment.ContentId); referencedSegmentInfo.Identifier = batchReferenceSegment.ContentId; return referencedSegmentInfo; } // Look for an entity set. EntitySetSegment entitySetSegment = segment as EntitySetSegment; if (entitySetSegment != null) { var container = ((IResourceSetBasedEdmEntitySet)entitySetSegment.EntitySet).ResourceSet; Debug.Assert(container != null, "container != null"); SegmentInfo segmentInfo = new SegmentInfo { Identifier = container.Name, TargetResourceSet = container, TargetResourceType = container.ResourceType, TargetSource = RequestTargetSource.EntitySet, TargetKind = RequestTargetKind.Resource, SingleResult = false }; return segmentInfo; } WebUtil.CheckResourceExists(false, segment.ToString()); return null; }
internal static bool TryBindAsOperation(PathSegmentToken pathToken, IEdmModel model, IEdmStructuredType entityType, out ODataPathSegment segment) { Debug.Assert(pathToken != null, "pathToken != null"); Debug.Assert(entityType != null, "bindingType != null"); List <IEdmOperation> possibleFunctions = new List <IEdmOperation>(); // Catch all catchable exceptions as FindDeclaredBoundOperations is implemented by anyone. // If an exception occurs it will be supressed and the possible functions will be empty and return false. try { int wildCardPos = pathToken.Identifier.IndexOf("*", StringComparison.Ordinal); if (wildCardPos > -1) { string namespaceName = pathToken.Identifier.Substring(0, wildCardPos - 1); possibleFunctions = model.FindBoundOperations(entityType).Where(o => o.Namespace == namespaceName).ToList(); } else { NonSystemToken nonSystemToken = pathToken as NonSystemToken; IList <string> parameterNames = new List <string>(); if (nonSystemToken != null && nonSystemToken.NamedValues != null) { parameterNames = nonSystemToken.NamedValues.Select(s => s.Name).ToList(); } if (parameterNames.Count > 0) { // Always force to use fully qualified name when select operation possibleFunctions = model.FindBoundOperations(entityType).FilterByName(true, pathToken.Identifier).FilterOperationsByParameterNames(parameterNames, false).ToList(); } else { possibleFunctions = model.FindBoundOperations(entityType).FilterByName(true, pathToken.Identifier).ToList(); } } } catch (Exception exc) { if (!ExceptionUtils.IsCatchableExceptionType(exc)) { throw; } } possibleFunctions = possibleFunctions.EnsureOperationsBoundWithBindingParameter().ToList(); // Only filter if there is more than one and its needed. if (possibleFunctions.Count > 1) { possibleFunctions = possibleFunctions.FilterBoundOperationsWithSameTypeHierarchyToTypeClosestToBindingType(entityType).ToList(); } if (possibleFunctions.Count <= 0) { segment = null; return(false); } segment = new OperationSegment(possibleFunctions, null /*entitySet*/); return(true); }
/// <summary> /// Tries to create a type name segment if the given identifier refers to a known type. /// </summary> /// <param name="previous">previous segment info.</param> /// <param name="segment">The segment being interpreted.</param> /// <param name="typeNameSegment">The type name segment, if one was created.</param> /// <returns>Whether or not a type segment was created for the identifier.</returns> private bool TryCreateTypeNameSegment(SegmentInfo previous, ODataPathSegment segment, out SegmentInfo typeNameSegment) { var typeSegment = segment as TypeSegment; if (typeSegment == null || previous.TargetResourceSet == null) { typeNameSegment = null; return false; } ResourceType targetResourceType = MetadataProviderUtils.GetResourceType(typeSegment); // if the new type segment prevents any results from possibly being returned, then short-circuit and throw a 404. ResourceType previousResourceType = previous.TargetResourceType; Debug.Assert(previousResourceType != null, "previous.TargetResourceType != null"); if (!targetResourceType.IsAssignableFrom(previousResourceType) && !previousResourceType.IsAssignableFrom(targetResourceType)) { throw DataServiceException.CreateBadRequestError(Strings.RequestUriProcessor_InvalidTypeIdentifier_UnrelatedType(targetResourceType.FullName, previousResourceType.FullName)); } // Since we allow derived navigation properties or named streams in V1/V2, the server will generate edit links and navigation links with type segment in it. // Hence we need to be able to process type segment in the request even when the server MPV is set to V1/V2. But we do not want to expose new functionality // like filtering collections based on type, etc on V1/V2 servers. Hence only checking for MPV to be v3 or greater if the previous segment is a collection if (!previous.SingleResult) { VersionUtil.CheckMaxProtocolVersion(VersionUtil.Version4Dot0, this.maxProtocolVersion); } typeNameSegment = new SegmentInfo { Identifier = targetResourceType.FullName, Operation = previous.Operation, TargetKind = previous.TargetKind, TargetSource = previous.TargetSource, TargetResourceType = targetResourceType, SingleResult = previous.SingleResult, TargetResourceSet = previous.TargetResourceSet, ProjectedProperty = previous.ProjectedProperty, Key = previous.Key, RequestExpression = previous.RequestExpression, RequestEnumerable = previous.RequestEnumerable, IsTypeIdentifierSegment = true }; return true; }
private static bool TryBindAsDeclaredProperty(PathSegmentToken tokenIn, IEdmStructuredType edmType, ODataUriResolver resolver, out ODataPathSegment segment) { IEdmProperty prop = resolver.ResolveProperty(edmType, tokenIn.Identifier); if (prop == null) { segment = null; return(false); } if (prop.PropertyKind == EdmPropertyKind.Structural) { segment = new PropertySegment((IEdmStructuralProperty)prop); return(true); } if (prop.PropertyKind == EdmPropertyKind.Navigation) { segment = new NavigationPropertySegment((IEdmNavigationProperty)prop, null /*TODO set*/); return(true); } throw new ODataException(ODataErrorStrings.SelectExpandBinder_UnknownPropertyType(prop.Name)); }
private void HandleSingletonPathSegment(ODataPathSegment segment) { var singletonPathSegment = (SingletonSegment)segment; var singleton = singletonPathSegment.Singleton; this.queryable = this.api.GetQueryableSource(singleton.Name, (object[])null); this.currentType = this.queryable.ElementType; }
/// <summary> /// Java specific function to generate Object constructor section of a code snippet. In the event that another object is needed in the middle of generation, /// a recursive call is made to sort out the needed object. /// </summary> /// <param name="pathSegment">Odata Function/Entity from which the object is needed</param> /// <param name="jsonString">Json string from which the information of the object to be initialized is held</param> /// <param name="path">List of strings/identifier showing the path through the Edm/json structure to reach the Class Identifier from the segment</param> /// <param name="usedVarNames">List to keep track of variable names used to prevent use of the same variable name</param> private string JavaGenerateObjectFromJson(ODataPathSegment pathSegment, string jsonString, List <string> path, List <string> usedVarNames = null) { var stringBuilder = new StringBuilder(); var jsonObject = JsonConvert.DeserializeObject(jsonString); usedVarNames = usedVarNames ?? new List <string>();//make sure list is not null var className = GetJavaReturnTypeName(pathSegment, path); switch (jsonObject) { case string _: { var enumIsFlags = CommonGenerator.GetEdmTypeFromIdentifier(pathSegment, path) is IEdmEnumType enumType && enumType.IsFlags; var enumString = GenerateEnumString(jsonObject.ToString(), pathSegment, path); var currentVarName = EnsureJavaVariableNameIsUnique(path.Last(), usedVarNames); if (className == "String") { stringBuilder.Append($"String {currentVarName} = \"{jsonObject}\";\r\n"); } else if (!string.IsNullOrEmpty(enumString)) { //Enum is accessed as the Classname then enum type e.g Importance.LOW var enumTypeName = enumString.Split(".").First(); if (enumIsFlags) { stringBuilder.Append($"EnumSet<{enumTypeName}> {currentVarName} = EnumSet.of({enumString});\r\n"); } else { stringBuilder.Append($"{enumTypeName} {currentVarName} = {enumString.Split(",", StringSplitOptions.RemoveEmptyEntries).FirstOrDefault()};\r\n"); } } else if (jsonObject.Equals("true") || jsonObject.Equals("false")) { stringBuilder.Append($"boolean {currentVarName} = {jsonObject};\r\n"); //boolean primitives values masquerading as strings. } else { stringBuilder.Append($"{GetJavaReturnTypeName(pathSegment, path)} {currentVarName} = {GenerateSpecialClassString($"{jsonObject}", pathSegment, path)};\r\n"); } } break; case JObject jObject: { var currentVarName = EnsureJavaVariableNameIsUnique(path.Last(), usedVarNames); var localClassNameOverride = className.Equals("JsonElement") ? "JsonObject" : className; //jsonelements are abstract and cannot be instanciated stringBuilder.Append($"{className} { currentVarName } = new {localClassNameOverride}();\r\n"); //initialize each member/property of the object foreach (var(key, jToken) in jObject) { var value = JsonConvert.SerializeObject(jToken); var newPath = path.Append(key).ToList(); //add new identifier to the path if (key.Contains("@odata") || key.StartsWith("@")) //sometimes @odata maybe in the middle e.g."*****@*****.**" { stringBuilder = GenerateJavaAdditionalDataSection(stringBuilder, key, jToken.ToString(), className, currentVarName); continue; } switch (jToken.Type) { case JTokenType.Array: case JTokenType.Object: // in the case we have a collection page property, we need to reference the collection page variable instead var collectionType = CommonGenerator.GetEdmTypeFromIdentifier(pathSegment, newPath); var referenceSuffix = collectionType.TypeKind == EdmTypeKind.Entity ? $"Collection{page}" : "List"; var variableName = (referenceSuffix.Contains(page) ? collectionType.FullTypeName().Split('.').Last() : newPath.Last()) + referenceSuffix; //new nested object needs to be constructed so call this function recursively to make it stringBuilder.Append($"{JavaGenerateObjectFromJson(pathSegment, value, newPath, usedVarNames)}"); stringBuilder.Append(jToken.Type == JTokenType.Array ? $"{ currentVarName }.{ newPath.Last() } = { EnsureJavaVariableNameIsUnique(variableName, usedVarNames) };\r\n" : $"{ currentVarName }.{ newPath.Last() } = { EnsureJavaVariableNameIsUnique(newPath.Last(), usedVarNames) };\r\n"); break; case JTokenType.String: var enumString = GenerateEnumString(jToken.ToString(), pathSegment, newPath); var enumIsFlags = CommonGenerator.GetEdmTypeFromIdentifier(pathSegment, newPath) is IEdmEnumType enumType && enumType.IsFlags; stringBuilder.Append($"{ currentVarName }.{newPath.Last()} = "); if (string.IsNullOrEmpty(enumString)) { stringBuilder.Append($"{GenerateSpecialClassString($"{value}", pathSegment, newPath)};\r\n"); } else if (enumIsFlags) { stringBuilder.Append($"EnumSet.of({enumString});\r\n"); } else { stringBuilder.Append($"{enumString.Split(",", StringSplitOptions.RemoveEmptyEntries).FirstOrDefault()};\r\n"); } break; default: stringBuilder.Append($"{ currentVarName }.{newPath.Last()} = { GenerateSpecialClassString($"{value}", pathSegment, newPath)};\r\n"); break; } usedVarNames.Add(jToken.Type == JTokenType.Array ? $"{newPath.Last()}List" : newPath.Last()); //add used variable name to used list } } break; case JArray array: { var objectList = array.Children <JObject>(); var currentListName = EnsureJavaVariableNameIsUnique(path.Last() + "List", usedVarNames); var collectionType = CommonGenerator.GetEdmTypeFromIdentifier(pathSegment, path); if (objectList.Any()) { //Item is a list/array so declare a typed list stringBuilder.Append($"LinkedList<{className}> {currentListName} = new LinkedList<{className}>();\r\n"); foreach (var item in objectList) { var currentListItemName = EnsureJavaVariableNameIsUnique(path.Last(), usedVarNames); var jsonItemString = JsonConvert.SerializeObject(item); //we need to create a new object var objectStringFromJson = JavaGenerateObjectFromJson(pathSegment, jsonItemString, path, usedVarNames); stringBuilder.Append($"{objectStringFromJson}"); stringBuilder.Append($"{currentListName}.add({currentListItemName});\r\n"); usedVarNames.Add(path.Last()); //add used variable name to used list } if (collectionType.TypeKind == EdmTypeKind.Entity) { var currentPageTypeName = CommonGenerator.UppercaseFirstLetter($"{CommonGenerator.UppercaseFirstLetter(collectionType.FullTypeName().Split('.').Last())}Collection{page}"); var currentPageCollectionName = EnsureJavaVariableNameIsUnique(CommonGenerator.LowerCaseFirstLetter(currentPageTypeName), usedVarNames); var currentResponseTypeName = currentPageTypeName.Replace(page, "Response"); var currentResponseCollectionName = EnsureJavaVariableNameIsUnique(CommonGenerator.LowerCaseFirstLetter(currentResponseTypeName), usedVarNames); stringBuilder.Append($"{currentResponseTypeName} {currentResponseCollectionName} = new {currentResponseTypeName}();\r\n"); stringBuilder.Append($"{currentResponseCollectionName}.value = {currentListName};\r\n"); stringBuilder.Append($"{currentPageTypeName} {currentPageCollectionName} = new {currentPageTypeName}({currentResponseCollectionName}, null);\r\n"); } } else if (collectionType.TypeKind == EdmTypeKind.Enum) { stringBuilder.Append($"LinkedList<{className}> {currentListName} = new LinkedList<{className}>();\r\n"); array?.Select(x => x.Value <string>())?. SelectMany(x => GenerateEnumString(x, pathSegment, path)?. Split(",", StringSplitOptions.RemoveEmptyEntries))?. ToList()?. ForEach(x => stringBuilder.Append($"{currentListName}.add({x});\r\n")); } else { stringBuilder.Append($"LinkedList<{className}> {currentListName} = new LinkedList<{className}>();\r\n"); //its not nested objects but a string collection foreach (var element in array) { stringBuilder.Append($"{currentListName}.add({(className == "String" ? AddQuotesIfMising(element.Value<string>()) : GenerateSpecialClassString(element.Value<string>(), pathSegment, path))});\r\n"); } } } break; case null: //do nothing break; default: var primitive = jsonObject.ToString(); var curVarName = EnsureJavaVariableNameIsUnique(path.Last(), usedVarNames); stringBuilder.Append($"{className} {curVarName} = {(className == "String" ? AddQuotesIfMising(primitive) : GenerateSpecialClassString(primitive, pathSegment, path))};\r\n"); //item is a primitive print as is break; } //check if this is the outermost object in a potential nested object structure and needs the semicolon termination character. return(path.Count == 1 ? $"{stringBuilder.ToString().TrimEnd()}\r\n\r\n" : stringBuilder.ToString()); }
private void HandleCountPathSegment(ODataPathSegment segment) { this.IsCountPathSegmentPresent = true; }
/// <summary> /// Handle validating a ODataPathSegment /// </summary> /// <param name="segment">The path segment to valdiate.</param> public override void Handle(ODataPathSegment segment) { ValidateItemAndType(segment); }
private void HandleKeyValuePathSegment(ODataPathSegment segment) { var keySegment = (KeySegment)segment; var parameterExpression = Expression.Parameter(this.currentType, DefaultNameOfParameterExpression); var keyValues = GetPathKeyValues(keySegment); BinaryExpression keyFilter = null; foreach (KeyValuePair<string, object> keyValuePair in keyValues) { var equalsExpression = CreateEqualsExpression(parameterExpression, keyValuePair.Key, keyValuePair.Value); keyFilter = keyFilter == null ? equalsExpression : Expression.And(keyFilter, equalsExpression); } var whereExpression = Expression.Lambda(keyFilter, parameterExpression); this.queryable = ExpressionHelpers.Where(this.queryable, whereExpression, this.currentType); }
/// <summary> /// Entry point for validating an <see cref="ODataPathSegment"/>. /// </summary> /// <param name="segment">The odata path segment to validate</param> public void ValidatePath(ODataPathSegment segment) { segment.HandleWith(this); }
private void HandlePropertyAccessPathSegment(ODataPathSegment segment) { var propertySegment = (PropertySegment)segment; var entityParameterExpression = Expression.Parameter(this.currentType); var structuralPropertyExpression = Expression.Property(entityParameterExpression, propertySegment.Property.Name); // Check whether property is null or not before further selection if (propertySegment.Property.Type.IsNullable && !propertySegment.Property.Type.IsPrimitive()) { var whereExpression = CreateNotEqualsNullExpression(structuralPropertyExpression, entityParameterExpression); this.queryable = ExpressionHelpers.Where(this.queryable, whereExpression, this.currentType); } if (propertySegment.Property.Type.IsCollection()) { // Produces new query like 'queryable.SelectMany(param => param.PropertyName)'. // Suppose 'param.PropertyName' is of type 'IEnumerable<T>', the type of the // resulting query would be 'IEnumerable<T>' too. this.currentType = structuralPropertyExpression.Type.GetEnumerableItemType(); var delegateType = typeof(Func<,>).MakeGenericType( this.queryable.ElementType, typeof(IEnumerable<>).MakeGenericType(this.currentType)); var selectBody = Expression.Lambda(delegateType, structuralPropertyExpression, entityParameterExpression); this.queryable = ExpressionHelpers.SelectMany(this.queryable, selectBody, this.currentType); } else { // Produces new query like 'queryable.Select(param => param.PropertyName)'. this.currentType = structuralPropertyExpression.Type; LambdaExpression selectBody = Expression.Lambda(structuralPropertyExpression, entityParameterExpression); this.queryable = ExpressionHelpers.Select(this.queryable, selectBody); } }
/// <inheritdoc/> public override bool TryMatch(ODataPathSegment pathSegment, IDictionary <string, object> values) { EntitySetSegment otherEntitySet = pathSegment as EntitySetSegment; return(otherEntitySet != null && otherEntitySet.EntitySet == Segment.EntitySet); }
internal static bool TryBindAsOperation(PathSegmentToken pathToken, IEdmModel model, IEdmStructuredType entityType, out ODataPathSegment segment) { Debug.Assert(pathToken != null, "pathToken != null"); Debug.Assert(entityType != null, "bindingType != null"); List<IEdmOperation> possibleFunctions = new List<IEdmOperation>(); // Catch all catchable exceptions as FindDeclaredBoundOperations is implemented by anyone. // If an exception occurs it will be supressed and the possible functions will be empty and return false. try { int wildCardPos = pathToken.Identifier.IndexOf("*", StringComparison.Ordinal); if (wildCardPos > -1) { string namespaceName = pathToken.Identifier.Substring(0, wildCardPos - 1); possibleFunctions = model.FindBoundOperations(entityType).Where(o => o.Namespace == namespaceName).ToList(); } else { NonSystemToken nonSystemToken = pathToken as NonSystemToken; IList<string> parameterNames = new List<string>(); if (nonSystemToken != null && nonSystemToken.NamedValues != null) { parameterNames = nonSystemToken.NamedValues.Select(s => s.Name).ToList(); } if (parameterNames.Count > 0) { // Always force to use fully qualified name when select operation possibleFunctions = model.FindBoundOperations(entityType).FilterByName(true, pathToken.Identifier).FilterOperationsByParameterNames(parameterNames, false).ToList(); } else { possibleFunctions = model.FindBoundOperations(entityType).FilterByName(true, pathToken.Identifier).ToList(); } } } catch (Exception exc) { if (!ExceptionUtils.IsCatchableExceptionType(exc)) { throw; } } possibleFunctions = possibleFunctions.EnsureOperationsBoundWithBindingParameter().ToList(); // Only filter if there is more than one and its needed. if (possibleFunctions.Count > 1) { possibleFunctions = possibleFunctions.FilterBoundOperationsWithSameTypeHierarchyToTypeClosestToBindingType(entityType).ToList(); } if (possibleFunctions.Count <= 0) { segment = null; return false; } segment = new OperationSegment(possibleFunctions, null /*entitySet*/); return true; }
private bool TryCreateTypeNameSegment(ODataPathSegment previous, string identifier, string parenthesisExpression) { IEdmType targetEdmType; if (previous.TargetEdmType == null || (targetEdmType = UriEdmHelpers.FindTypeFromModel(this.configuration.Model, identifier, this.configuration.Resolver)) == null) { return false; } // if the new type segment prevents any results from possibly being returned, then short-circuit and throw a 404. IEdmType previousEdmType = previous.TargetEdmType; Debug.Assert(previousEdmType != null, "previous.TargetEdmType != null"); if (previousEdmType.TypeKind == EdmTypeKind.Collection) { previousEdmType = ((IEdmCollectionType)previousEdmType).ElementType.Definition; } if (!targetEdmType.IsOrInheritsFrom(previousEdmType) && !previousEdmType.IsOrInheritsFrom(targetEdmType)) { throw ExceptionUtil.CreateBadRequestError(ODataErrorStrings.RequestUriProcessor_InvalidTypeIdentifier_UnrelatedType(targetEdmType.ODataFullName(), previousEdmType.ODataFullName())); } // We want the type of the type segment to be a collection if the previous segment was a collection IEdmType actualTypeOfTheTypeSegment = targetEdmType; if (previous.EdmType.TypeKind == EdmTypeKind.Collection) { // creating a new collection type here because the type in the request is just the item type, there is no user-provided collection type. var actualEntityTypeOfTheTypeSegment = actualTypeOfTheTypeSegment as IEdmEntityType; if (actualEntityTypeOfTheTypeSegment != null) { actualTypeOfTheTypeSegment = new EdmCollectionType(new EdmEntityTypeReference(actualEntityTypeOfTheTypeSegment, false)); } else { throw new ODataException(Strings.PathParser_TypeCastOnlyAllowedAfterEntityCollection(identifier)); } } var typeNameSegment = (ODataPathSegment)new TypeSegment(actualTypeOfTheTypeSegment, previous.TargetEdmNavigationSource) { Identifier = identifier, TargetKind = previous.TargetKind, SingleResult = previous.SingleResult, TargetEdmType = targetEdmType }; this.parsedSegments.Add(typeNameSegment); // Key expressions are allowed on Type segments this.TryBindKeyFromParentheses(parenthesisExpression); return true; }
/// <summary> /// Determines the entity set for segment. /// </summary> /// <param name="identifier">The identifier.</param> /// <param name="returnType">Type of the return.</param> /// <param name="segment">The segment.</param> /// <param name="targetset">The targetset.</param> /// <param name="singleOperation">The single operation.</param> /// <exception cref="ODataException">Throws and exception if entity set not specified.</exception> private static void DetermineEntitySetForSegment(string identifier, IEdmTypeReference returnType, ODataPathSegment segment, IEdmEntitySetBase targetset, IEdmOperation singleOperation) { if (returnType != null) { segment.TargetEdmNavigationSource = targetset; segment.TargetEdmType = returnType.Definition; segment.TargetKind = TargetKindFromType(segment.TargetEdmType); segment.SingleResult = !singleOperation.ReturnType.IsCollection(); } else { segment.TargetEdmNavigationSource = null; segment.TargetEdmType = null; segment.TargetKind = RequestTargetKind.VoidOperation; } }
/// <summary> /// Function to find the <see cref="EdmType"/> being used by identifier that is the last item in the path collection. This function gets /// the right <see cref="IEdmType"/> that is is to be used for the search based on the segment type> /// </summary> /// <param name="oDataPathSegment">Odata path segment that is the root of the search </param> /// <param name="path">List of string that show the depth of the search into the definition from the odataPath type definition</param> /// <returns>pair of Edm type name and whether type is found as navigation property</returns> public (IEdmType type, bool isNavigationProperty) GetEdmTypeFromIdentifierAndNavigationProperty(ODataPathSegment oDataPathSegment, ICollection <string> path) { IEdmType type; bool isNavigationProperty; switch (oDataPathSegment) { case KeySegment _: case EntitySetSegment _: case NavigationPropertySegment _: case NavigationPropertyLinkSegment _: case SingletonSegment _: case PropertySegment _: case ValueSegment _: (type, isNavigationProperty) = SearchForEdmType(oDataPathSegment.EdmType, path); if (null != type) { return(type, isNavigationProperty); } break; case OperationSegment operationSegment: foreach (var parameters in operationSegment.Operations.First().Parameters) { if (!parameters.Name.Equals(path.FirstOrDefault(), StringComparison.OrdinalIgnoreCase)) { continue; } (type, isNavigationProperty) = SearchForEdmType(parameters.Type.Definition, path); if (null != type) { return(type, isNavigationProperty); } } break; } throw new Exception($"No Class Name Found for Identifier {path.Last()}"); }
/// <summary> /// Creates a new segment for an open property. /// </summary> /// <param name="previous">previous segment info.</param> /// <param name="identifier">name of the segment.</param> /// <param name="parenthesisExpression">whether this segment has a query portion or not.</param> private void CreateOpenPropertySegment(ODataPathSegment previous, string identifier, string parenthesisExpression) { ODataPathSegment segment = new OpenPropertySegment(identifier); // Handle an open type property. If the current leaf isn't an // object (which implies it's already an open type), then // it should be marked as an open type. if (previous.TargetEdmType != null && !previous.TargetEdmType.IsOpenType()) { throw ExceptionUtil.CreateResourceNotFoundError(segment.Identifier); } // Open navigation properties are not supported on OpenTypes. if (parenthesisExpression != null) { throw ExceptionUtil.CreateBadRequestError(ODataErrorStrings.OpenNavigationPropertiesNotSupportedOnOpenTypes(segment.Identifier)); } this.parsedSegments.Add(segment); }
private static void ValidatePathSegment(ODataPathSegment segment, ODataUrlValidationContext validationContext) { validationContext.PathValidator.ValidatePath(segment); }
/// <summary> /// Tries the bind key segment if no resolved parameters and parathesis value exsts. /// </summary> /// <param name="parenthesisExpression">The parenthesis expression.</param> /// <param name="returnType">Type of the return.</param> /// <param name="resolvedParameters">The resolved parameters.</param> /// <param name="segment">The segment.</param> private void TryBindKeySegmentIfNoResolvedParametersAndParathesisValueExsts(string parenthesisExpression, IEdmTypeReference returnType, ICollection<OperationSegmentParameter> resolvedParameters, ODataPathSegment segment) { IEdmCollectionTypeReference collectionTypeReference = returnType as IEdmCollectionTypeReference; if (collectionTypeReference != null && collectionTypeReference.ElementType().IsEntity() && resolvedParameters == null && parenthesisExpression != null) { // The parameters in the parathesis is a key segment. if (this.TryBindKeyFromParentheses(parenthesisExpression)) { this.ThrowIfMustBeLeafSegment(segment); } } }
/// <summary> /// Function to find the <see cref="EdmType"/> being used by identifier that is the last item in the path collection. This function gets /// the right <see cref="IEdmType"/> that is is to be used for the search based on the segment type> /// </summary> /// <param name="oDataPathSegment">Odata path segment that is the root of the search </param> /// <param name="path">List of string that show the depth of the search into the definition from the odataPath type definition</param> public IEdmType GetEdmTypeFromIdentifier(ODataPathSegment oDataPathSegment, ICollection <string> path) { var(edmType, _) = GetEdmTypeFromIdentifierAndNavigationProperty(oDataPathSegment, path); return(edmType); }
/// <inheritdoc/> public override bool TryMatch(ODataPathSegment pathSegment, IDictionary <string, object> values) { SingletonSegment otherSingleton = pathSegment as SingletonSegment; return(otherSingleton != null && otherSingleton.Singleton == Segment.Singleton); }
private void BuildSelections( SelectExpandClause selectExpandClause, HashSet <IEdmStructuralProperty> allStructuralProperties, HashSet <IEdmNavigationProperty> allNavigationProperties, HashSet <IEdmAction> allActions, HashSet <IEdmFunction> allFunctions) { foreach (SelectItem selectItem in selectExpandClause.SelectedItems) { if (selectItem is ExpandedNavigationSelectItem) { continue; } PathSelectItem pathSelectItem = selectItem as PathSelectItem; if (pathSelectItem != null) { ValidatePathIsSupported(pathSelectItem.SelectedPath); ODataPathSegment segment = pathSelectItem.SelectedPath.LastSegment; NavigationPropertySegment navigationPropertySegment = segment as NavigationPropertySegment; if (navigationPropertySegment != null) { IEdmNavigationProperty navigationProperty = navigationPropertySegment.NavigationProperty; if (allNavigationProperties.Contains(navigationProperty)) { SelectedNavigationProperties.Add(navigationProperty); } continue; } PropertySegment structuralPropertySegment = segment as PropertySegment; if (structuralPropertySegment != null) { IEdmStructuralProperty structuralProperty = structuralPropertySegment.Property; if (allStructuralProperties.Contains(structuralProperty)) { SelectedStructuralProperties.Add(structuralProperty); } continue; } OperationSegment operationSegment = segment as OperationSegment; if (operationSegment != null) { AddOperations(allActions, allFunctions, operationSegment); continue; } throw new ODataException(Error.Format(SRResources.SelectionTypeNotSupported, segment.GetType().Name)); } WildcardSelectItem wildCardSelectItem = selectItem as WildcardSelectItem; if (wildCardSelectItem != null) { SelectedStructuralProperties = allStructuralProperties; SelectedNavigationProperties = allNavigationProperties; continue; } NamespaceQualifiedWildcardSelectItem wildCardActionSelection = selectItem as NamespaceQualifiedWildcardSelectItem; if (wildCardActionSelection != null) { SelectedActions = allActions; SelectedFunctions = allFunctions; continue; } throw new ODataException(Error.Format(SRResources.SelectionTypeNotSupported, selectItem.GetType().Name)); } }
/// <summary> /// Parses the key properties based on the segment's target type, then creates a new segment for the key. /// </summary> /// <param name="segment">The segment to apply the key to.</param> /// <param name="previousKeySegment">The parent node's key segment.</param> /// <param name="key">The key to apply.</param> /// <param name="resolver">The resolver to use.</param> /// <returns>The newly created key segment.</returns> private static KeySegment CreateKeySegment(ODataPathSegment segment, KeySegment previousKeySegment, SegmentArgumentParser key, ODataUriResolver resolver) { Debug.Assert(segment != null, "segment != null"); Debug.Assert(key != null && !key.IsEmpty, "key != null && !key.IsEmpty"); Debug.Assert(segment.SingleResult == false, "segment.SingleResult == false"); IEdmEntityType targetEntityType = null; if (!(segment.TargetEdmType != null && segment.TargetEdmType.IsEntityOrEntityCollectionType(out targetEntityType))) { throw ExceptionUtil.CreateSyntaxError(); } Debug.Assert(targetEntityType != null, "targetEntityType != null"); // Make sure the keys specified in the uri matches with the number of keys in the metadata var keyProperties = targetEntityType.Key().ToList(); if (keyProperties.Count != key.ValueCount) { NavigationPropertySegment currentNavPropSegment = segment as NavigationPropertySegment; if (currentNavPropSegment != null) { key = KeyFinder.FindAndUseKeysFromRelatedSegment(key, keyProperties, currentNavPropSegment.NavigationProperty, previousKeySegment); } // if we still didn't find any keys, then throw an error. if (keyProperties.Count != key.ValueCount && resolver.GetType() == typeof(ODataUriResolver)) { throw ExceptionUtil.CreateBadRequestError(ErrorStrings.BadRequest_KeyCountMismatch(targetEntityType.FullName())); } } if (!key.AreValuesNamed && key.ValueCount > 1 && resolver.GetType() == typeof(ODataUriResolver)) { throw ExceptionUtil.CreateBadRequestError(ErrorStrings.RequestUriProcessor_KeysMustBeNamed); } IEnumerable<KeyValuePair<string, object>> keyPairs; if (!key.TryConvertValues(targetEntityType, out keyPairs, resolver)) { throw ExceptionUtil.CreateSyntaxError(); } IEdmEntityType entityType; bool isEntity = segment.TargetEdmType.IsEntityOrEntityCollectionType(out entityType); Debug.Assert(isEntity, "Key target type should be an entity type."); var keySegment = new KeySegment(keyPairs, entityType, segment.TargetEdmNavigationSource); keySegment.CopyValuesFrom(segment); keySegment.SingleResult = true; return keySegment; }
/// <summary>Tries to create a key segment for the given filter if it is non empty.</summary> /// <param name="previous">Segment on which to compose.</param> /// <param name="previousKeySegment">The parent node's key segment.</param> /// <param name="parenthesisExpression">Parenthesis expression of segment.</param> /// <param name="keySegment">The key segment that was created if the key was non-empty.</param> /// <param name="enableUriTemplateParsing">Whether Uri template parsing is enabled.</param> /// <param name="resolver">The resolver to use.</param> /// <returns>Whether the key was non-empty.</returns> internal static bool TryCreateKeySegmentFromParentheses(ODataPathSegment previous, KeySegment previousKeySegment, string parenthesisExpression, out ODataPathSegment keySegment, bool enableUriTemplateParsing = false, ODataUriResolver resolver = null) { Debug.Assert(parenthesisExpression != null, "parenthesisExpression != null"); Debug.Assert(previous != null, "segment!= null"); if (resolver == null) { resolver = ODataUriResolver.Default; } if (previous.SingleResult) { throw ExceptionUtil.CreateSyntaxError(); } SegmentArgumentParser key; if (!SegmentArgumentParser.TryParseKeysFromUri(parenthesisExpression, out key, enableUriTemplateParsing)) { throw ExceptionUtil.CreateSyntaxError(); } // People/NS.Employees() is OK, just like People() is OK if (key.IsEmpty) { keySegment = null; return(false); } keySegment = CreateKeySegment(previous, previousKeySegment, key, resolver); return(true); }