示例#1
0
        // We need to append the key value path segment from $id.
        private static void AppendIdForRef(IList <ODL.ODataPathSegment> segments, ODL.KeySegment id)
        {
            if (id == null || !(segments.Last() is ODL.NavigationPropertyLinkSegment))
            {
                return;
            }

            segments.Add(id);
        }
示例#2
0
        /// <summary>
        /// Check if this segment is equal to another segment.
        /// </summary>
        /// <param name="other">the other segment to check.</param>
        /// <returns>true if the other segment is equal.</returns>
        /// <exception cref="System.ArgumentNullException">Throws if the input other is null.</exception>
        internal override bool Equals(ODataPathSegment other)
        {
            ExceptionUtils.CheckArgumentNotNull(other, "other");
            KeySegment otherKeySegment = other as KeySegment;

            return(otherKeySegment != null &&
                   otherKeySegment.Keys.SequenceEqual(this.Keys) &&
                   otherKeySegment.EdmType == this.edmType &&
                   otherKeySegment.NavigationSource == this.navigationSource);
        }
        /// <summary>
        /// Translate a KeySegment
        /// </summary>
        /// <param name="segment">the segment to Translate</param>
        /// <returns>Defined by the implementer.</returns>
        public override string Translate(KeySegment segment)
        {
            Debug.Assert(segment != null, "segment != null");
            List <KeyValuePair <string, object> > keys = segment.Keys.ToList();

            StringBuilder builder = new StringBuilder();

            KeySerializer.AppendKeyExpression(builder, new Collection <KeyValuePair <string, object> >(keys), p => p.Key, p => p.Value);

            return(builder.ToString());
        }
        /// <summary>
        /// Add the key segment in the ODataPath, the method does not modify current ODataPath instance,
        /// it returns a new ODataPath without ending type segment.
        /// If last segment is type cast, the key would be appended before type cast segment.
        /// </summary>
        /// <param name="path">Path to perform the computation on.</param>
        /// <param name="keys">The set of key property names and the values to be used in searching for the given item.</param>
        /// <param name="edmType">The type of the item this key returns.</param>
        /// <param name="navigationSource">The navigation source that this key is used to search.</param>
        /// <returns>A new ODataPath with key segment added</returns>
        public static ODataPath AddKeySegment(this ODataPath path, IEnumerable <KeyValuePair <string, object> > keys, IEdmEntityType edmType, IEdmNavigationSource navigationSource)
        {
            var handler = new SplitEndingSegmentOfTypeHandler <TypeSegment>();

            path.WalkWith(handler);
            KeySegment keySegment = new KeySegment(keys, edmType, navigationSource);
            ODataPath  newPath    = handler.FirstPart;

            newPath.Add(keySegment);
            AppendLastSegment(handler, newPath);

            return(newPath);
        }
示例#5
0
        /// <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();
            }

            return(new KeySegment(segment, keyPairs, targetEntityType, segment.TargetEdmNavigationSource));
        }
示例#6
0
        /// <summary>
        /// Parse the odata path.
        /// </summary>
        /// <param name="model">The model to use for path parsing.</param>
        /// <param name="serviceRoot">The service root of the OData path.</param>
        /// <param name="odataPath">The OData path to parse.</param>
        /// <param name="template">The flag indicates whether the path is template or not.</param>
        /// <param name="serviceProvider">The service proivder.</param>
        /// <param name="httpContext"></param>
        /// <param name="resolverSettings"></param>
        /// <returns>A parsed representation of the path, or <c>null</c> if the path does not match the model.</returns>
        public static ODataPath Parse(IEdmModel model, string serviceRoot, string odataPath, bool template, IServiceProvider serviceProvider, ODataUriResolverSettings resolverSettings)
        {
            ODL.ODataUriParser uriParser;
            Uri serviceRootUri = null;
            Uri fullUri        = null;
            // TODO: Replace this type.
            //NameValueCollection queryString = null;

            ODataOptions options = serviceProvider.GetRequiredService <IOptions <ODataOptions> >().Value;

            if (template)
            {
                uriParser = new ODL.ODataUriParser(model, new Uri(odataPath, UriKind.Relative), serviceProvider);
                uriParser.EnableUriTemplateParsing = true;
            }
            else
            {
                Contract.Assert(serviceRoot != null);

                serviceRootUri = new Uri(
                    serviceRoot.EndsWith("/", StringComparison.Ordinal) ?
                    serviceRoot :
                    serviceRoot + "/");

                fullUri = new Uri(serviceRootUri, odataPath);
                //queryString = fullUri.ParseQueryString();
                uriParser = new ODL.ODataUriParser(model, serviceRootUri, fullUri, serviceProvider);
            }

            uriParser.Resolver = resolverSettings.CreateResolver();

            if (options.UrlKeyDelimiter != null)
            {
                uriParser.UrlKeyDelimiter = options.UrlKeyDelimiter;
            }
            else
            {
                // ODL changes to use ODataUrlKeyDelimiter.Slash as default value.
                // Web API still uses the ODataUrlKeyDelimiter.Parentheses as default value.
                // Please remove it after fix: https://github.com/OData/odata.net/issues/642
                uriParser.UrlKeyDelimiter = ODataUrlKeyDelimiter.Parentheses;
            }

            ODL.ODataPath         path;
            UnresolvedPathSegment unresolvedPathSegment = null;

            ODL.KeySegment id = null;
            try
            {
                path = uriParser.ParsePath();
            }
            catch (ODL.ODataUnrecognizedPathException ex)
            {
                if (ex.ParsedSegments != null &&
                    ex.ParsedSegments.Any() &&
                    (ex.ParsedSegments.Last().EdmType is IEdmComplexType ||
                     ex.ParsedSegments.Last().EdmType is IEdmEntityType) &&
                    ex.CurrentSegment != ODataSegmentKinds.Count)
                {
                    if (!ex.UnparsedSegments.Any())
                    {
                        path = new ODL.ODataPath(ex.ParsedSegments);
                        unresolvedPathSegment = new UnresolvedPathSegment(ex.CurrentSegment);
                    }
                    else
                    {
                        // Throw ODataException if there is some segment following the unresolved segment.
                        throw new ODataException(Error.Format(
                                                     SRResources.InvalidPathSegment,
                                                     ex.UnparsedSegments.First(),
                                                     ex.CurrentSegment));
                    }
                }
                else
                {
                    throw;
                }
            }

            if (!template && path.LastSegment is ODL.NavigationPropertyLinkSegment)
            {
                IEdmCollectionType lastSegmentEdmType = path.LastSegment.EdmType as IEdmCollectionType;

                if (lastSegmentEdmType != null)
                {
                    ODL.EntityIdSegment entityIdSegment = null;
                    bool exceptionThrown = false;

                    try
                    {
                        entityIdSegment = uriParser.ParseEntityId();

                        if (entityIdSegment != null)
                        {
                            // Create another ODataUriParser to parse $id, which is absolute or relative.
                            ODL.ODataUriParser parser = new ODL.ODataUriParser(model, serviceRootUri, entityIdSegment.Id);
                            id = parser.ParsePath().LastSegment as ODL.KeySegment;
                        }
                    }
                    catch (ODataException)
                    {
                        // Exception was thrown while parsing the $id.
                        // We will throw another exception about the invalid $id.
                        exceptionThrown = true;
                    }

                    if (exceptionThrown ||
                        (entityIdSegment != null &&
                         (id == null ||
                          !(id.EdmType.IsOrInheritsFrom(lastSegmentEdmType.ElementType.Definition) ||
                            lastSegmentEdmType.ElementType.Definition.IsOrInheritsFrom(id.EdmType)))))
                    {
                        throw new ODataException(Error.Format(SRResources.InvalidDollarId, "$id" /*queryString.Get("$id")*/));
                    }
                }
            }

            // do validation for the odata path
            path.WalkWith(new DefaultODataPathValidator(model));

            // do segment translator (for example parameter alias, key & function parameter template, etc)
            var segments =
                ODataPathSegmentTranslator.Translate(model, path, uriParser.ParameterAliasNodes).ToList();

            if (unresolvedPathSegment != null)
            {
                segments.Add(unresolvedPathSegment);
            }

            if (!template)
            {
                AppendIdForRef(segments, id);
            }

            return(new ODataPath(segments)
            {
                ODLPath = path
            });
        }
 /// <summary>
 /// Translate a KeySegment
 /// </summary>
 /// <param name="segment">the segment to Translate</param>
 /// <returns>UserDefinedValue</returns>
 /// <exception cref="System.ArgumentNullException">Throws if the input segment is null.</exception>
 public override bool Translate(KeySegment segment)
 {
     ExceptionUtils.CheckArgumentNotNull(segment, "segment");
     return(false);
 }
示例#8
0
 /// <summary>
 /// Handle a KeySegment
 /// </summary>
 /// <param name="segment">the segment to Handle</param>
 public virtual void Handle(KeySegment segment)
 {
     throw new NotImplementedException();
 }
 /// <summary>
 /// Handle a KeySegment
 /// </summary>
 /// <param name="segment">the segment to Handle</param>
 public override void Handle(KeySegment segment)
 {
     CommonHandler(segment);
 }
示例#10
0
        /// <summary>
        /// Find any related keys from the parent key segment, if it exists, and add them to the raw key values that
        /// we already have from the uri.
        /// </summary>
        /// <param name="rawKeyValuesFromUri">The raw key values as we've parsed them from the uri.</param>
        /// <param name="targetEntityKeyProperties">The list of key properties on the target entity.</param>
        /// <param name="currentNavigationProperty">The current navigation property that we're trying to follow using the raw key values</param>
        /// <param name="keySegmentOfParentEntity">The key segment of the parent entity in this path, if it exists. Null otherwise</param>
        /// <returns>A new SegmentArgumentParser with any keys that were found added to its list of NamedValues.</returns>
        /// <throws>Throws if the input currentNavigationProperty is null.</throws>
        public static SegmentArgumentParser FindAndUseKeysFromRelatedSegment(SegmentArgumentParser rawKeyValuesFromUri, IEnumerable <IEdmStructuralProperty> targetEntityKeyProperties, IEdmNavigationProperty currentNavigationProperty, KeySegment keySegmentOfParentEntity)
        {
            ExceptionUtils.CheckArgumentNotNull(currentNavigationProperty, "currentNavigationProperty");
            ExceptionUtils.CheckArgumentNotNull(rawKeyValuesFromUri, "rawKeyValuesFromUri");

            ReadOnlyCollection <IEdmStructuralProperty> targetKeyPropertyList = targetEntityKeyProperties != null ? new ReadOnlyCollection <IEdmStructuralProperty>(targetEntityKeyProperties.ToList()) : new ReadOnlyCollection <IEdmStructuralProperty>(new List <IEdmStructuralProperty>());

            // should only get here if the number of raw parameters from the uri is different than the number of key properties for the target entity.
            Debug.Assert(rawKeyValuesFromUri.ValueCount < targetKeyPropertyList.Count, "rawKeyValuesFromUri.ValueCount < targetEntityKeyProperties.Count()");

            // if the raw key from the uri has positional values, there must be only one of them
            // its important to cache this value here because we'll change it when we add new
            // named values below (the implementation of AreValuesNamed is just namedValues !=null)
            bool hasPositionalValues = !rawKeyValuesFromUri.AreValuesNamed;

            if (hasPositionalValues && rawKeyValuesFromUri.ValueCount > 1)
            {
                return(rawKeyValuesFromUri);
            }

            if (keySegmentOfParentEntity == null)
            {
                return(rawKeyValuesFromUri);
            }

            // TODO: p2 merge the below 2 pieces of codes
            // find out if any target entity key properties have referential constraints that link them to the previous rawKeyValuesFromUri.
            List <EdmReferentialConstraintPropertyPair> keysFromReferentialIntegrityConstraint = ExtractMatchingPropertyPairsFromNavProp(currentNavigationProperty, targetKeyPropertyList).ToList();

            foreach (EdmReferentialConstraintPropertyPair keyFromReferentialIntegrityConstraint in keysFromReferentialIntegrityConstraint)
            {
                KeyValuePair <string, object> valueFromParent = keySegmentOfParentEntity.Keys.SingleOrDefault(x => x.Key == keyFromReferentialIntegrityConstraint.DependentProperty.Name);
                if (valueFromParent.Key != null)
                {
                    // if the key from the referential integrity constraint is one of the target key properties
                    // and that key property isn't already populated in the raw key values from the uri, then
                    // we set that value to the value from the parent key segment.
                    if (targetKeyPropertyList.Any(x => x.Name == keyFromReferentialIntegrityConstraint.PrincipalProperty.Name))
                    {
                        rawKeyValuesFromUri.AddNamedValue(
                            keyFromReferentialIntegrityConstraint.PrincipalProperty.Name,
                            ConvertKeyValueToUriLiteral(valueFromParent.Value, rawKeyValuesFromUri.KeyAsSegment));
                    }
                }
            }

            // also need to look to see if any nav props exist in the target set that refer back to this same set, which might have
            // referential constraints also.
            keysFromReferentialIntegrityConstraint.Clear();
            IEdmNavigationProperty reverseNavProp = currentNavigationProperty.Partner;

            if (reverseNavProp != null)
            {
                keysFromReferentialIntegrityConstraint.AddRange(ExtractMatchingPropertyPairsFromReversedNavProp(reverseNavProp, targetKeyPropertyList));
            }

            foreach (EdmReferentialConstraintPropertyPair keyFromReferentialIntegrityConstraint in keysFromReferentialIntegrityConstraint)
            {
                KeyValuePair <string, object> valueFromParent = keySegmentOfParentEntity.Keys.SingleOrDefault(x => x.Key == keyFromReferentialIntegrityConstraint.PrincipalProperty.Name);
                if (valueFromParent.Key != null)
                {
                    // if the key from the referential integrity constraint is one of the target key properties
                    // and that key property isn't already populated in the raw key values from the uri, then
                    // we set that value to the value from the parent key segment.
                    if (targetKeyPropertyList.Any(x => x.Name == keyFromReferentialIntegrityConstraint.DependentProperty.Name))
                    {
                        rawKeyValuesFromUri.AddNamedValue(
                            keyFromReferentialIntegrityConstraint.DependentProperty.Name,
                            ConvertKeyValueToUriLiteral(valueFromParent.Value, rawKeyValuesFromUri.KeyAsSegment));
                    }
                }
            }

            // if we had a positional value before, then we need to add that value as a new named value.
            // the name that we choose will be the only value from the target entity key properties
            // that isn't already set in the NamedValues list.
            if (hasPositionalValues)
            {
                if (rawKeyValuesFromUri.NamedValues != null)
                {
                    List <IEdmStructuralProperty> unassignedProperties = targetKeyPropertyList.Where(x => !rawKeyValuesFromUri.NamedValues.ContainsKey(x.Name)).ToList();

                    if (unassignedProperties.Count == 1)
                    {
                        rawKeyValuesFromUri.AddNamedValue(unassignedProperties[0].Name, rawKeyValuesFromUri.PositionalValues[0]);
                    }
                    else
                    {
                        return(rawKeyValuesFromUri);
                    }

                    // clear out the positional value so that we keep a consistent state in the
                    // raw keys from uri.
                    rawKeyValuesFromUri.PositionalValues.Clear();
                }
                else
                {
                    return(rawKeyValuesFromUri);
                }
            }

            return(rawKeyValuesFromUri);
        }
示例#11
0
 /// <summary>
 /// Translate a KeySegment
 /// </summary>
 /// <param name="segment">the segment to Translate</param>
 /// <returns>Defined by the implementer.</returns>
 public virtual T Translate(KeySegment segment)
 {
     throw new NotImplementedException();
 }
示例#12
0
        private ODataPath Parse(string serviceRoot, string odataPath, IServiceProvider requestContainer, bool template)
        {
            ODataUriParser uriParser;
            Uri            serviceRootUri = null;
            Uri            fullUri        = null;
            IEdmModel      model          = requestContainer.GetRequiredService <IEdmModel>();

            if (template)
            {
                uriParser = new ODataUriParser(model, new Uri(odataPath, UriKind.Relative), requestContainer);
                uriParser.EnableUriTemplateParsing = true;
            }
            else
            {
                Contract.Assert(serviceRoot != null);

                serviceRootUri = new Uri(
                    serviceRoot.EndsWith("/", StringComparison.Ordinal)
                        ? serviceRoot
                        : serviceRoot + "/");

                // Concatenate the root and path and create a Uri. Using Uri to build a Uri from
                // a root and relative path changes the casing on .NetCore. However, odataPath may
                // be a full Uri.
                if (!Uri.TryCreate(odataPath, UriKind.Absolute, out fullUri))
                {
                    fullUri = new Uri(serviceRootUri + odataPath);
                }

                // Due to a bug in the System.Uri some relative paths are rejected if they contain
                // a ':' symbol on a position greater than 1024. This careful check should mitigate
                // this problem by encoding these characters before the path is combined with
                // service roor Uri.
                // https://github.com/dotnet/corefx/issues/29011
                if (!Uri.IsWellFormedUriString(odataPath, UriKind.RelativeOrAbsolute) &&
                    odataPath.IndexOf(':') > MaxUriSchemeName)
                {
                    var odataPathColonEncoded = odataPath.Replace(":", "%3A");
                    if (Uri.IsWellFormedUriString(odataPathColonEncoded, UriKind.Relative))
                    {
                        odataPath = odataPathColonEncoded;
                    }
                }

                fullUri   = new Uri(serviceRootUri, odataPath);
                uriParser = new ODataUriParser(model, serviceRootUri, fullUri, requestContainer);
            }

            if (UrlKeyDelimiter != null)
            {
                uriParser.UrlKeyDelimiter = UrlKeyDelimiter;
            }
            else
            {
                uriParser.UrlKeyDelimiter = ODataUrlKeyDelimiter.Slash;
            }

            ODL.ODataPath         path;
            UnresolvedPathSegment unresolvedPathSegment = null;

            ODL.KeySegment id = null;
            try
            {
                path = uriParser.ParsePath();
            }
            catch (ODataUnrecognizedPathException ex)
            {
                if (ex.ParsedSegments != null &&
                    ex.ParsedSegments.Any() &&
                    (ex.ParsedSegments.Last().EdmType is IEdmComplexType ||
                     ex.ParsedSegments.Last().EdmType is IEdmEntityType) &&
                    ex.CurrentSegment != ODataSegmentKinds.Count)
                {
                    if (!ex.UnparsedSegments.Any())
                    {
                        path = new ODL.ODataPath(ex.ParsedSegments);
                        unresolvedPathSegment = new UnresolvedPathSegment(ex.CurrentSegment);
                    }
                    else
                    {
                        // Throw ODataException if there is some segment following the unresolved segment.
                        throw new ODataException(Error.Format(
                                                     SRResources.InvalidPathSegment,
                                                     ex.UnparsedSegments.First(),
                                                     ex.CurrentSegment));
                    }
                }
                else
                {
                    throw;
                }
            }

            if (!template && path.LastSegment is ODL.NavigationPropertyLinkSegment)
            {
                IEdmCollectionType lastSegmentEdmType = path.LastSegment.EdmType as IEdmCollectionType;

                if (lastSegmentEdmType != null)
                {
                    ODL.EntityIdSegment entityIdSegment = null;
                    bool exceptionThrown = false;

                    try
                    {
                        entityIdSegment = uriParser.ParseEntityId();

                        if (entityIdSegment != null)
                        {
                            // Create another ODataUriParser to parse $id, which is absolute or relative.
                            ODataUriParser parser = new ODataUriParser(model, serviceRootUri, entityIdSegment.Id, requestContainer);
                            id = parser.ParsePath().LastSegment as ODL.KeySegment;
                        }
                    }
                    catch (ODataException)
                    {
                        // Exception was thrown while parsing the $id.
                        // We will throw another exception about the invalid $id.
                        exceptionThrown = true;
                    }

                    if (exceptionThrown ||
                        (entityIdSegment != null &&
                         (id == null ||
                          !(id.EdmType.IsOrInheritsFrom(lastSegmentEdmType.ElementType.Definition) ||
                            lastSegmentEdmType.ElementType.Definition.IsOrInheritsFrom(id.EdmType)))))
                    {
                        // System.Net.Http on NetCore does not have the Uri extension method
                        // ParseQueryString(), to avoid a platform-specific call, extract $id manually.
                        string idValue = fullUri.Query;
                        string idParam = "$id=";
                        int    start   = idValue.IndexOf(idParam, StringComparison.OrdinalIgnoreCase);
                        if (start >= 0)
                        {
                            int end = idValue.IndexOf("&", start, StringComparison.OrdinalIgnoreCase);
                            if (end >= 0)
                            {
                                idValue = idValue.Substring(start + idParam.Length, end - 1);
                            }
                            else
                            {
                                idValue = idValue.Substring(start + idParam.Length);
                            }
                        }

                        throw new ODataException(Error.Format(SRResources.InvalidDollarId, idValue));
                    }
                }
            }

            // do validation for the odata path
            path.WalkWith(new DefaultODataPathValidator(model));

            // do segment translator (for example parameter alias, key & function parameter template, etc)
            var segments =
                ODataPathSegmentTranslator.Translate(model, path, uriParser.ParameterAliasNodes).ToList();

            if (unresolvedPathSegment != null)
            {
                segments.Add(unresolvedPathSegment);
            }

            if (!template)
            {
                AppendIdForRef(segments, id);
            }

            return(new ODataPath(segments)
            {
                Path = path
            });
        }
 /// <summary>
 /// Determine the NavigationSource of a KeySegment
 /// </summary>
 /// <param name="segment">The KeySegment to look in.</param>
 /// <returns>The IEdmNavigationSource of this KeySegment</returns>
 /// <exception cref="System.ArgumentNullException">Throws if the input segment is null.</exception>
 public override IEdmNavigationSource Translate(KeySegment segment)
 {
     ExceptionUtils.CheckArgumentNotNull(segment, "segment");
     return(segment.NavigationSource);
 }
示例#14
0
        /// <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="odataUrlKeyDelimiter">Key delimiter used in url.</param>
        /// <param name="resolver">The resolver to use.</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>
        /// <returns>Whether or not the segment was interpreted as a key.</returns>
        internal static bool TryHandleSegmentAsKey(string segmentText, ODataPathSegment previous, KeySegment previousKeySegment, ODataUrlKeyDelimiter odataUrlKeyDelimiter, ODataUriResolver resolver, out KeySegment keySegment, bool enableUriTemplateParsing = false)
        {
            Debug.Assert(previous != null, "previous != null");
            Debug.Assert(odataUrlKeyDelimiter != null, "odataUrlKeyDelimiter != null");
            Debug.Assert(resolver != null, "resolver != null");

            keySegment = null;

            // If the current convention does not support keys-as-segments, then this does not apply.
            if (!odataUrlKeyDelimiter.EnableKeyAsSegment)
            {
                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);
        }
示例#15
0
        /// <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="resolver">The resolver to use.</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>
        /// <returns>Whether the key was non-empty.</returns>
        internal static bool TryCreateKeySegmentFromParentheses(ODataPathSegment previous, KeySegment previousKeySegment, string parenthesisExpression, ODataUriResolver resolver, out ODataPathSegment keySegment, bool enableUriTemplateParsing = false)
        {
            Debug.Assert(parenthesisExpression != null, "parenthesisExpression != null");
            Debug.Assert(previous != null, "segment!= null");
            Debug.Assert(resolver != null, "resolver != null");

            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);
        }
示例#16
0
        public override ODataPath Parse(string serviceRoot, string odataPath, IServiceProvider requestContainer)
        {
            ODL.ODataUriParser uriParser;
            Uri       serviceRootUri = null;
            Uri       fullUri        = null;
            string    dataSourceName = odataPath.Split('/')[0];
            IEdmModel model          = requestContainer.GetService(typeof(IEdmModel)) as IEdmModel;

            Contract.Assert(serviceRoot != null);

            serviceRootUri = new Uri(
                serviceRoot.EndsWith("/", StringComparison.Ordinal)
                    ? serviceRoot
                    : serviceRoot + "/");


            // Concatenate the root and path and create a Uri. Using Uri to build a Uri from
            // a root and relative path changes the casing on .NetCore. However, odataPath may
            // be a full Uri.
            if (!Uri.TryCreate(odataPath, UriKind.Absolute, out fullUri))
            {
                fullUri = new Uri(serviceRootUri + odataPath);
            }
            serviceRootUri = new Uri(serviceRootUri, dataSourceName);
            uriParser      = new ODL.ODataUriParser(model, serviceRootUri, fullUri, requestContainer);

            if (UrlKeyDelimiter != null)
            {
                uriParser.UrlKeyDelimiter = UrlKeyDelimiter;
            }
            else
            {
                // ODL changes to use ODataUrlKeyDelimiter.Slash as default value.
                // Web API still uses the ODataUrlKeyDelimiter.Parentheses as default value.
                // Please remove it after fix: https://github.com/OData/odata.net/issues/642
                uriParser.UrlKeyDelimiter = ODataUrlKeyDelimiter.Parentheses;
            }

            ODL.ODataPath         path;
            UnresolvedPathSegment unresolvedPathSegment = null;

            ODL.KeySegment id = null;

            try
            {
                path = uriParser.ParsePath();
            }
            catch (ODL.ODataUnrecognizedPathException ex)
            {
                if (ex.ParsedSegments != null &&
                    ex.ParsedSegments.Any() &&
                    (ex.ParsedSegments.Last().EdmType is IEdmComplexType ||
                     ex.ParsedSegments.Last().EdmType is IEdmEntityType) &&
                    ex.CurrentSegment != ODataSegmentKinds.Count)
                {
                    if (!ex.UnparsedSegments.Any())
                    {
                        path = new ODL.ODataPath(ex.ParsedSegments);
                        unresolvedPathSegment = new UnresolvedPathSegment(ex.CurrentSegment);
                    }
                    else
                    {
                        // Throw ODataException if there is some segment following the unresolved segment.
                        throw new ODataException(String.Format(CultureInfo.CurrentCulture,
                                                               "InvalidPathSegment",
                                                               ex.UnparsedSegments.First(),
                                                               ex.CurrentSegment));
                    }
                }
                else
                {
                    throw;
                }
            }
            if (path.LastSegment is ODL.NavigationPropertyLinkSegment)
            {
                IEdmCollectionType lastSegmentEdmType = path.LastSegment.EdmType as IEdmCollectionType;

                if (lastSegmentEdmType != null)
                {
                    ODL.EntityIdSegment entityIdSegment = null;
                    bool exceptionThrown = false;

                    try
                    {
                        entityIdSegment = uriParser.ParseEntityId();

                        if (entityIdSegment != null)
                        {
                            // Create another ODataUriParser to parse $id, which is absolute or relative.
                            ODL.ODataUriParser parser = new ODL.ODataUriParser(model, serviceRootUri, entityIdSegment.Id, requestContainer);
                            id = parser.ParsePath().LastSegment as ODL.KeySegment;
                        }
                    }
                    catch (ODataException)
                    {
                        // Exception was thrown while parsing the $id.
                        // We will throw another exception about the invalid $id.
                        exceptionThrown = true;
                    }

                    if (exceptionThrown ||
                        (entityIdSegment != null &&
                         (id == null ||
                          !(id.EdmType.IsOrInheritsFrom(lastSegmentEdmType.ElementType.Definition) ||
                            lastSegmentEdmType.ElementType.Definition.IsOrInheritsFrom(id.EdmType)))))
                    {
                        // System.Net.Http on NetCore does not have the Uri extension method
                        // ParseQueryString(), to avoid a platform-specific call, extract $id manually.
                        string idValue = fullUri.Query;
                        string idParam = "$id=";
                        int    start   = idValue.IndexOf(idParam, StringComparison.OrdinalIgnoreCase);
                        if (start >= 0)
                        {
                            int end = idValue.IndexOf("&", start, StringComparison.OrdinalIgnoreCase);
                            if (end >= 0)
                            {
                                idValue = idValue.Substring(start + idParam.Length, end - 1);
                            }
                            else
                            {
                                idValue = idValue.Substring(start + idParam.Length);
                            }
                        }

                        throw new ODataException(String.Format(CultureInfo.CurrentCulture, "InvalidDollarId", idValue));
                    }
                }
            }

            // do validation for the odata path
            path.WalkWith(new DefaultODataPathValidator(model));

            // do segment translator (for example parameter alias, key & function parameter template, etc)
            var segments =
                ODataPathSegmentTranslator.Translate(model, path, uriParser.ParameterAliasNodes).ToList();

            if (unresolvedPathSegment != null)
            {
                segments.Add(unresolvedPathSegment);
            }
            AppendIdForRef(segments, id);
            return(new ODataPath(segments));
        }