internal static DataServiceProtocolVersion CalculateEntitySetUriSegmentRequestVersion(ODataRequest request, EntitySet entitySet, DataServiceProtocolVersion maxProtocolVersion, DataServiceProtocolVersion maxDataServiceVersion, string contentType)
        {
            bool processTypeMetadata = false;

            DataServiceProtocolVersion entitySetSegmentVersion = DataServiceProtocolVersion.V4;

            IEnumerable <EntityType> entityTypes = VersionHelper.GetEntityTypes(entitySet);

            if (request.GetEffectiveVerb().IsUpdateVerb() || request.GetEffectiveVerb() == HttpVerb.Post)
            {
                // Typically for all posts there is some type of payload so we should analyze the metadata
                if (request.GetEffectiveVerb() == HttpVerb.Post)
                {
                    processTypeMetadata = true;
                }
                else if (request.PreferHeaderApplies(maxProtocolVersion))
                {
                    processTypeMetadata = true;
                }

                // Whenever there is an update operation and EPM is involved we need to check the metadata version
                if (entityTypes.SelectMany(et => et.Annotations.OfType <PropertyMappingAnnotation>()).Where(fma => fma.KeepInContent == false).Any())
                {
                    processTypeMetadata = true;
                }
            }

            if (processTypeMetadata)
            {
                entitySetSegmentVersion = entitySetSegmentVersion.IncreaseVersionIfRequired(entitySet.CalculateMinEntityPropertyMappingVersion(VersionCalculationType.Request, contentType, maxProtocolVersion, maxDataServiceVersion));
            }

            return(entitySetSegmentVersion);
        }
Beispiel #2
0
        /// <summary>
        /// Calculates the version based on the ODataUri provided
        /// </summary>
        /// <param name="request">Request to calculate from</param>
        /// <param name="maxProtocolVersion">Max Protocol version of the server</param>
        /// <returns>Data Service Protocol Version</returns>
        public DataServiceProtocolVersion CalculateMinRequestVersion(ODataRequest request, DataServiceProtocolVersion maxProtocolVersion)
        {
            ExceptionUtilities.CheckArgumentNotNull(request, "request");
            ExceptionUtilities.Assert(maxProtocolVersion != DataServiceProtocolVersion.Unspecified, "Max protocol version cannot be unspecified when calculating the MinVersion");

            string contentType = request.GetHeaderValueIfExists(HttpHeaders.ContentType);
            DataServiceProtocolVersion dataServiceVersion    = VersionHelper.ConvertToDataServiceProtocolVersion(request.GetHeaderValueIfExists(HttpHeaders.DataServiceVersion));
            DataServiceProtocolVersion maxDataServiceVersion = VersionHelper.ConvertToDataServiceProtocolVersion(request.GetHeaderValueIfExists(HttpHeaders.MaxDataServiceVersion));
            HttpVerb effectiveVerb = request.GetEffectiveVerb();

            if (contentType == null)
            {
                contentType = MimeTypes.Any;
            }

            // No real request payload for Delete so its automatically version 1
            if (request.GetEffectiveVerb() == HttpVerb.Delete)
            {
                return(DataServiceProtocolVersion.V4);
            }

            if (effectiveVerb.IsUpdateVerb() || effectiveVerb == HttpVerb.Post)
            {
                EntitySet entitySet = null;
                if (request.Uri.TryGetExpectedEntitySet(out entitySet))
                {
                    // Determine if the operation and Uri combined yields something that we need to look at the metadata to determine the version or not
                    bool processTypeMetadata = false;

                    // Typically for all posts there is some type of payload so we should analyze the metadata
                    if (effectiveVerb == HttpVerb.Post)
                    {
                        processTypeMetadata = true;
                    }
                    else if (dataServiceVersion != DataServiceProtocolVersion.Unspecified && request.PreferHeaderApplies(maxProtocolVersion))
                    {
                        processTypeMetadata = true;
                    }

                    IEnumerable <EntityType> entityTypes = VersionHelper.GetEntityTypes(entitySet);

                    // Whenever there is an update operation and EPM is involved we need to check the metadata version
                    if (entityTypes.SelectMany(et => et.Annotations.OfType <PropertyMappingAnnotation>()).Where(fma => fma.KeepInContent == false).Any())
                    {
                        processTypeMetadata = true;
                    }

                    if (processTypeMetadata)
                    {
                        return(VersionHelper.GetMaximumVersion(entityTypes.Select(et => et.CalculateEntityPropertyMappingProtocolVersion(VersionCalculationType.Request, contentType, maxProtocolVersion, maxDataServiceVersion)).ToArray()));
                    }
                }

                return(DataServiceProtocolVersion.V4);
            }

            return(VersionHelper.CalculateUriMinRequestProtocolVersion(request.Uri, contentType, maxProtocolVersion, maxDataServiceVersion));
        }
        /// <summary>
        /// Calculates the version based on the ODataUri provided
        /// </summary>
        /// <param name="request">Request to calculate from</param>
        /// <param name="maxProtocolVersion">Max Protocol version of the server</param>
        /// <returns>Data Service Protocol Version</returns>
        public DataServiceProtocolVersion CalculateMinRequestVersion(ODataRequest request, DataServiceProtocolVersion maxProtocolVersion)
        {
            ExceptionUtilities.CheckArgumentNotNull(request, "request");
            ExceptionUtilities.Assert(maxProtocolVersion != DataServiceProtocolVersion.Unspecified, "Max protocol version cannot be unspecified when calculating the MinVersion");

            string contentType = request.GetHeaderValueIfExists(HttpHeaders.ContentType);
            DataServiceProtocolVersion dataServiceVersion = VersionHelper.ConvertToDataServiceProtocolVersion(request.GetHeaderValueIfExists(HttpHeaders.DataServiceVersion));
            DataServiceProtocolVersion maxDataServiceVersion = VersionHelper.ConvertToDataServiceProtocolVersion(request.GetHeaderValueIfExists(HttpHeaders.MaxDataServiceVersion));
            HttpVerb effectiveVerb = request.GetEffectiveVerb();

            if (contentType == null)
            {
                contentType = MimeTypes.Any;
            }

            // No real request payload for Delete so its automatically version 1
            if (request.GetEffectiveVerb() == HttpVerb.Delete)
            {
                return DataServiceProtocolVersion.V4;
            }

            if (effectiveVerb.IsUpdateVerb() || effectiveVerb == HttpVerb.Post)
            {
                EntitySet entitySet = null;
                if (request.Uri.TryGetExpectedEntitySet(out entitySet))
                {
                    // Determine if the operation and Uri combined yields something that we need to look at the metadata to determine the version or not
                    bool processTypeMetadata = false;

                    // Typically for all posts there is some type of payload so we should analyze the metadata
                    if (effectiveVerb == HttpVerb.Post)
                    {
                        processTypeMetadata = true;
                    }
                    else if (dataServiceVersion != DataServiceProtocolVersion.Unspecified && request.PreferHeaderApplies(maxProtocolVersion))
                    {
                        processTypeMetadata = true;
                    }

                    IEnumerable<EntityType> entityTypes = VersionHelper.GetEntityTypes(entitySet);

                    // Whenever there is an update operation and EPM is involved we need to check the metadata version
                    if (entityTypes.SelectMany(et => et.Annotations.OfType<PropertyMappingAnnotation>()).Where(fma => fma.KeepInContent == false).Any())
                    {
                        processTypeMetadata = true;
                    }

                    if (processTypeMetadata)
                    {
                        return VersionHelper.GetMaximumVersion(entityTypes.Select(et => et.CalculateEntityPropertyMappingProtocolVersion(VersionCalculationType.Request, contentType, maxProtocolVersion, maxDataServiceVersion)).ToArray());
                    }
                }

                return DataServiceProtocolVersion.V4;
            }

            return VersionHelper.CalculateUriMinRequestProtocolVersion(request.Uri, contentType, maxProtocolVersion, maxDataServiceVersion);
        }
Beispiel #4
0
        /// <summary>
        /// Gets the pre and post update representations of the entity updated by the given request
        /// </summary>
        /// <param name="request">The request</param>
        /// <param name="beforeUpdate">The entity before the update</param>
        /// <param name="afterUpdate">The entity after the update</param>
        public void GetUpdatedEntity(ODataRequest request, out QueryStructuralValue beforeUpdate, out QueryStructuralValue afterUpdate)
        {
            ExceptionUtilities.CheckArgumentNotNull(request, "request");
            ExceptionUtilities.Assert(this.requestsBegun.Contains(request), "Cannot use GetUpdatedEntity before calling Begin");
            ExceptionUtilities.Assert(request.GetEffectiveVerb().IsUpdateVerb(), "Cannot use GetUpdatedEntity on non update requests");

            KeyValuePair <QueryStructuralValue, QueryStructuralValue> beforeAndAfter;

            if (!this.updatedEntityCache.TryGetValue(request, out beforeAndAfter))
            {
                var entityUri = request.Uri.ScopeToEntity();
                var entity    = (QueryStructuralValue)this.Evaluator.Evaluate(entityUri, false, false);

                beforeUpdate = this.QueryValueCopier.PerformDeepCopy(entity);

                SyncHelpers.ExecuteActionAndWait(c => this.Synchronizer.SynchronizeEntity(c, entity));

                afterUpdate = entity;

                beforeAndAfter = new KeyValuePair <QueryStructuralValue, QueryStructuralValue>(beforeUpdate, afterUpdate);
                this.updatedEntityCache[request] = beforeAndAfter;
            }
            else
            {
                beforeUpdate = beforeAndAfter.Key;
                afterUpdate  = beforeAndAfter.Value;
            }
        }
Beispiel #5
0
        internal string CalculateExpectedETag(ODataRequest request, ODataResponse response)
        {
            // error responses should never have an ETag
            if (response.StatusCode.IsError())
            {
                return(null);
            }

            // $ref never involves ETags
            if (request.Uri.IsEntityReferenceLink())
            {
                return(null);
            }

            var verb = request.GetEffectiveVerb();

            if (verb == HttpVerb.Get)
            {
                return(this.CalculateExpectedETagForRetrieve(request));
            }

            if (verb == HttpVerb.Delete)
            {
                // TODO: Server should write ETag header in response to property-value deletes
                return(null);
            }

            if (verb == HttpVerb.Post)
            {
                if (request.Uri.IsServiceOperation())
                {
                    return(this.CalculateExpectedETagForRetrieve(request));
                }

                if (!request.Uri.IsEntitySet())
                {
                    return(null);
                }

                using (this.Context.Begin(request))
                {
                    var insertedEntity = this.Context.GetInsertedEntity(request, response);
                    return(this.LiteralConverter.ConstructWeakETag(insertedEntity));
                }
            }

            return(this.CalculateExpectedETagForUpdate(request));
        }
        internal static bool IsStreamRequest(ODataRequest request)
        {
            if (request.Uri.IsNamedStream() || request.Uri.IsMediaResource())
            {
                return(true);
            }

            if (request.GetEffectiveVerb() == HttpVerb.Post && request.Uri.IsEntitySet())
            {
                EntitySet entitySet;
                ExceptionUtilities.Assert(request.Uri.TryGetExpectedEntitySet(out entitySet), "Could not infer entity set from uri");
                return(entitySet.EntityType.HasStream());
            }

            return(false);
        }
Beispiel #7
0
        private void VerifyStoreData(ODataRequest request, ODataResponse response, QueryStructuralValue storeValue)
        {
            string contentType;

            ExceptionUtilities.Assert(request.Headers.TryGetValue(HttpHeaders.ContentType, out contentType), "Could not get Content-Type header from request");

            if (request.Uri.IsNamedStream())
            {
                string streamName  = request.Uri.Segments.OfType <NamedStreamSegment>().Last().Name;
                var    streamValue = storeValue.GetStreamValue(streamName);
                this.VerifyStream(streamValue, contentType, request, response);
                return;
            }

            bool isInsert        = request.GetEffectiveVerb() == HttpVerb.Post;
            bool isMediaResource = request.Uri.IsMediaResource();

            if (isInsert)
            {
                EntitySet expectedEntitySet;
                if (request.Uri.TryGetExpectedEntitySet(out expectedEntitySet))
                {
                    isMediaResource = expectedEntitySet.EntityType.HasStream();
                }
            }

            if (isMediaResource)
            {
                var streamValue = storeValue.GetDefaultStreamValue();
                this.VerifyStream(streamValue, contentType, request, response);
            }
            else
            {
                if (isInsert)
                {
                    this.VerifyTypeNameForInsert(request, response, storeValue);
                }

                var formatStrategy    = this.FormatSelector.GetStrategy(contentType, request.Uri);
                var primitiveComparer = formatStrategy.GetPrimitiveComparer();

                // TODO: verify relationships
                // TODO: verify PUT vs PATCH semantics
                this.VerifyPropertyValues(request, primitiveComparer, storeValue);
            }
        }
Beispiel #8
0
 private void GetUpdatedEntityBeforeAndAfter(ODataRequest request, ODataResponse response, out QueryStructuralValue beforeUpdate, out QueryStructuralValue afterUpdate)
 {
     using (this.Context.Begin(request))
     {
         if (request.GetEffectiveVerb() == HttpVerb.Post)
         {
             afterUpdate = this.Context.GetInsertedEntity(request, response);
             ExceptionUtilities.CheckObjectNotNull(afterUpdate, "Structural value returned by GetInsertedEntity unexpectedly null");
             beforeUpdate = afterUpdate.Type.NullValue;
         }
         else
         {
             this.Context.GetUpdatedEntity(request, out beforeUpdate, out afterUpdate);
             ExceptionUtilities.CheckObjectNotNull(beforeUpdate, "Before-update structural value returned by GetUpdatedEntity unexpectedly null");
             ExceptionUtilities.CheckObjectNotNull(afterUpdate, "After-update structural value returned by GetUpdatedEntity unexpectedly null");
         }
     }
 }
Beispiel #9
0
        /// <summary>
        /// Gets the entity inserted by the given request.
        /// </summary>
        /// <param name="request">The request</param>
        /// <param name="response">The response</param>
        /// <returns>The inserted entity</returns>
        public QueryStructuralValue GetInsertedEntity(ODataRequest request, ODataResponse response)
        {
            ExceptionUtilities.CheckArgumentNotNull(request, "request");
            ExceptionUtilities.CheckArgumentNotNull(response, "response");
            ExceptionUtilities.Assert(this.requestsBegun.Contains(request), "Cannot use GetInsertedEntity before calling Begin");
            ExceptionUtilities.Assert(request.GetEffectiveVerb() == HttpVerb.Post, "Cannot use GetInsertedEntity on non POST requests");

            QueryStructuralValue insertedEntity;

            if (!this.insertedEntityCache.TryGetValue(request, out insertedEntity))
            {
                EntitySet entitySet;
                ExceptionUtilities.Assert(request.Uri.TryGetExpectedEntitySet(out entitySet), "Could not infer entity set from URI");

                var entitySetUri = new ODataUri(new EntitySetSegment(entitySet));

                var beforeSync = this.Evaluator.Evaluate(entitySetUri, false, false) as QueryCollectionValue;
                ExceptionUtilities.CheckObjectNotNull(beforeSync, "Could not evaluate entity set '{0}' before syncing", entitySet.Name);

                // create a shallow copy
                beforeSync = beforeSync.Type.CreateCollectionWithValues(beforeSync.Elements);

                SyncHelpers.ExecuteActionAndWait(c => this.Synchronizer.SynchronizeEntireEntitySet(c, entitySet.Name));

                var afterSync = this.Evaluator.Evaluate(entitySetUri, false, false) as QueryCollectionValue;
                ExceptionUtilities.CheckObjectNotNull(afterSync, "Could not evaluate entity set '{0}' after syncing", entitySet.Name);

                // TODO: handle deep insert (using location header or response payload)
                var newElements = afterSync.Elements.Except(beforeSync.Elements).OfType <QueryStructuralValue>().ToList();
                this.insertedEntityCache[request] = insertedEntity = newElements.Single();

                // update the parent entity, if there is one
                var segments = request.Uri.Segments;
                var lastNavigationSegment = segments.OfType <NavigationSegment>().LastOrDefault();
                if (lastNavigationSegment != null)
                {
                    var segmentsBeforeNavigation = segments.TakeWhile(t => t != lastNavigationSegment);
                    var parentEntity             = (QueryStructuralValue)this.Evaluator.Evaluate(new ODataUri(segmentsBeforeNavigation), false, false);
                    SyncHelpers.ExecuteActionAndWait(c => this.Synchronizer.SynchronizeEntity(c, parentEntity));
                }
            }

            return(insertedEntity);
        }
        internal bool TryCalculateODataUriProcessingError(ODataRequest request, DataServiceProtocolVersion maxDataServiceVersion, DataServiceProtocolVersion maxProtocolVersion, string acceptType, out ExpectedErrorMessage expectedErrorMessage)
        {
            expectedErrorMessage = null;

            if (request.Uri.IsEntity() || request.Uri.IsEntitySet() || request.Uri.IsServiceOperation())
            {
                bool isEntityFromNonOperationQuery = request.GetEffectiveVerb() == HttpVerb.Get && !request.Uri.IsServiceOperation();
                bool isEntityFromOperation         = request.Uri.IsServiceOperation() && (request.Uri.IsEntity() || request.Uri.IsEntitySet());

                // Do an EPM on all returning sets for the response
                if (isEntityFromNonOperationQuery || isEntityFromOperation)
                {
                    EntitySet startingEntitySet = null;
                    if (request.Uri.TryGetExpectedEntitySet(out startingEntitySet))
                    {
                        DataServiceProtocolVersion minVersion = startingEntitySet.CalculateEntitySetProtocolVersion(acceptType, VersionCalculationType.Response, maxProtocolVersion, maxDataServiceVersion);

                        // Check Epm First
                        if (maxDataServiceVersion != DataServiceProtocolVersion.Unspecified && minVersion > maxDataServiceVersion)
                        {
                            expectedErrorMessage = new ExpectedErrorMessage(MaxDataServiceVersionTooLow, maxDataServiceVersion.ConvertToHeaderFormat(), minVersion.ConvertToIntegerFormat(), "0");
                            return(true);
                        }
                    }

                    // we need to check the root Entity Set first, then any/all, and later the Expand
                    foreach (EntitySet entitySet in request.Uri.GetIncludingExpandsSets())
                    {
                        DataServiceProtocolVersion minVersion = entitySet.CalculateEntitySetProtocolVersion(acceptType, VersionCalculationType.Response, maxProtocolVersion, maxDataServiceVersion);

                        // Check Epm First
                        if (maxDataServiceVersion != DataServiceProtocolVersion.Unspecified && minVersion > maxDataServiceVersion)
                        {
                            expectedErrorMessage = new ExpectedErrorMessage(MaxDataServiceVersionTooLow, maxDataServiceVersion.ConvertToHeaderFormat(), minVersion.ConvertToIntegerFormat(), "0");
                            return(true);
                        }
                    }
                }
            }

            return(false);
        }
Beispiel #11
0
        /// <summary>
        /// Returns true if this is an update request
        /// </summary>
        /// <param name="request">The request being verified</param>
        /// <returns>Whether or not this verifier applies to the request</returns>
        public bool Applies(ODataRequest request)
        {
            if (request.Uri.LastSegment is FunctionSegment)
            {
                return(false);
            }

            if (request.Uri.IsServiceOperation())
            {
                return(false);
            }

            if (request.Uri.IsBatch())
            {
                return(false);
            }

            var verb = request.GetEffectiveVerb();

            return(verb != HttpVerb.Delete && verb != HttpVerb.Get);
        }
Beispiel #12
0
        /// <summary>
        /// Calculates the version based on the ODataUri provided
        /// </summary>
        /// <param name="request">Request to calculate from</param>
        /// <param name="maxProtocolVersion">Max Protocol version of the server</param>
        /// <returns>Data Service Protocol Version</returns>
        public DataServiceProtocolVersion CalculateMinResponseVersion(ODataRequest request, DataServiceProtocolVersion maxProtocolVersion)
        {
            ExceptionUtilities.CheckArgumentNotNull(request, "request");
            ExceptionUtilities.Assert(maxProtocolVersion != DataServiceProtocolVersion.Unspecified, "Max protocol version cannot be unspecified when calculating the MinVersion");

            string contentType = request.GetHeaderValueIfExists(HttpHeaders.Accept);
            DataServiceProtocolVersion maxDataServiceVersion = VersionHelper.ConvertToDataServiceProtocolVersion(request.GetHeaderValueIfExists(HttpHeaders.MaxDataServiceVersion));
            HttpVerb effectiveVerb = request.GetEffectiveVerb();

            if (contentType == null)
            {
                contentType = MimeTypes.Any;
            }

            if (effectiveVerb == HttpVerb.Post || effectiveVerb.IsUpdateVerb())
            {
                DataServiceProtocolVersion version = DataServiceProtocolVersion.V4;

                string preferHeaderValue = request.GetHeaderValueIfExists(HttpHeaders.Prefer);

                // Bump to Version 3 if prefer header is specified and its > V3 server
                if (preferHeaderValue != null && request.PreferHeaderApplies(maxProtocolVersion))
                {
                    version = version.IncreaseVersionIfRequired(DataServiceProtocolVersion.V4);
                }

                // for insert or update, versioning is specific to the type
                EntityType expectedEntityType;
                if (TryGetExpectedType(request, out expectedEntityType))
                {
                    version = VersionHelper.IncreaseVersionIfRequired(version, VersionHelper.CalculateProtocolVersion(expectedEntityType, contentType, VersionCalculationType.Response, maxProtocolVersion, maxDataServiceVersion));
                }

                return(version);
            }

            return(VersionHelper.CalculateUriResponseMinProtocolVersion(request.Uri, contentType, maxProtocolVersion, maxDataServiceVersion));
        }
        /// <summary>
        /// Calculates the version based on the ODataUri provided
        /// </summary>
        /// <param name="request">Request to calculate from</param>
        /// <param name="maxProtocolVersion">Max Protocol version of the server</param>
        /// <returns>Data Service Protocol Version</returns>
        public DataServiceProtocolVersion CalculateMinResponseVersion(ODataRequest request, DataServiceProtocolVersion maxProtocolVersion)
        {
            ExceptionUtilities.CheckArgumentNotNull(request, "request");
            ExceptionUtilities.Assert(maxProtocolVersion != DataServiceProtocolVersion.Unspecified, "Max protocol version cannot be unspecified when calculating the MinVersion");

            string contentType = request.GetHeaderValueIfExists(HttpHeaders.Accept);
            DataServiceProtocolVersion maxDataServiceVersion = VersionHelper.ConvertToDataServiceProtocolVersion(request.GetHeaderValueIfExists(HttpHeaders.MaxDataServiceVersion));
            HttpVerb effectiveVerb = request.GetEffectiveVerb();

            if (contentType == null)
            {
                contentType = MimeTypes.Any;
            }

            if (effectiveVerb == HttpVerb.Post || effectiveVerb.IsUpdateVerb())
            {
                DataServiceProtocolVersion version = DataServiceProtocolVersion.V4;

                string preferHeaderValue = request.GetHeaderValueIfExists(HttpHeaders.Prefer);

                // Bump to Version 3 if prefer header is specified and its > V3 server
                if (preferHeaderValue != null && request.PreferHeaderApplies(maxProtocolVersion))
                {
                    version = version.IncreaseVersionIfRequired(DataServiceProtocolVersion.V4);
                }

                // for insert or update, versioning is specific to the type
                EntityType expectedEntityType;
                if (TryGetExpectedType(request, out expectedEntityType))
                {
                    version = VersionHelper.IncreaseVersionIfRequired(version, VersionHelper.CalculateProtocolVersion(expectedEntityType, contentType, VersionCalculationType.Response, maxProtocolVersion, maxDataServiceVersion));
                }

                return version;
            }

            return VersionHelper.CalculateUriResponseMinProtocolVersion(request.Uri, contentType, maxProtocolVersion, maxDataServiceVersion);
        }
        private void VerifyStoreData(ODataRequest request, ODataResponse response, QueryStructuralValue storeValue)
        {
            string contentType;
            ExceptionUtilities.Assert(request.Headers.TryGetValue(HttpHeaders.ContentType, out contentType), "Could not get Content-Type header from request");

            if (request.Uri.IsNamedStream())
            {
                string streamName = request.Uri.Segments.OfType<NamedStreamSegment>().Last().Name;
                var streamValue = storeValue.GetStreamValue(streamName);
                this.VerifyStream(streamValue, contentType, request, response);
                return;
            }

            bool isInsert = request.GetEffectiveVerb() == HttpVerb.Post;
            bool isMediaResource = request.Uri.IsMediaResource();
            if (isInsert)
            {
                EntitySet expectedEntitySet;
                if (request.Uri.TryGetExpectedEntitySet(out expectedEntitySet))
                {
                    isMediaResource = expectedEntitySet.EntityType.HasStream();
                }
            }

            if (isMediaResource)
            {
                var streamValue = storeValue.GetDefaultStreamValue();
                this.VerifyStream(streamValue, contentType, request, response);
            }
            else
            {
                if (isInsert)
                {
                    this.VerifyTypeNameForInsert(request, response, storeValue);
                }

                var formatStrategy = this.FormatSelector.GetStrategy(contentType, request.Uri);
                var primitiveComparer = formatStrategy.GetPrimitiveComparer();

                // TODO: verify relationships
                // TODO: verify PUT vs PATCH semantics
                this.VerifyPropertyValues(request, primitiveComparer, storeValue);
            }
        }
        internal string CalculateExpectedETag(ODataRequest request, ODataResponse response)
        {
            // error responses should never have an ETag
            if (response.StatusCode.IsError())
            {
                return null;
            }

            // $ref never involves ETags
            if (request.Uri.IsEntityReferenceLink())
            {
                return null;
            }

            var verb = request.GetEffectiveVerb();

            if (verb == HttpVerb.Get)
            {
                return this.CalculateExpectedETagForRetrieve(request);
            }

            if (verb == HttpVerb.Delete)
            {
                // TODO: Server should write ETag header in response to property-value deletes
                return null;
            }
            
            if (verb == HttpVerb.Post)
            {
                if (request.Uri.IsServiceOperation())
                {
                    return this.CalculateExpectedETagForRetrieve(request);
                }

                if (!request.Uri.IsEntitySet())
                {
                    return null;
                }

                using (this.Context.Begin(request))
                {
                    var insertedEntity = this.Context.GetInsertedEntity(request, response);
                    return this.LiteralConverter.ConstructWeakETag(insertedEntity);
                }
            }

            return this.CalculateExpectedETagForUpdate(request);
        }
        private DataServiceProtocolVersion CalculateDataServiceProtocolVersion(ODataRequest request, ODataResponse response)
        {
            DataServiceProtocolVersion dataServiceVersion = VersionHelper.ConvertToDataServiceProtocolVersion(request.GetHeaderValueIfExists(HttpHeaders.DataServiceVersion));
            DataServiceProtocolVersion maxDataServiceVersion = VersionHelper.ConvertToDataServiceProtocolVersion(request.GetHeaderValueIfExists(HttpHeaders.MaxDataServiceVersion));

            var responseContentType = response.GetHeaderValueIfExists(HttpHeaders.ContentType);
            if (responseContentType != null)
            {
                if (responseContentType.StartsWith(MimeTypes.ApplicationJsonODataLightNonStreaming, StringComparison.OrdinalIgnoreCase) ||
                    responseContentType.StartsWith(MimeTypes.ApplicationJsonODataLightStreaming, StringComparison.OrdinalIgnoreCase))
                {
                    return DataServiceProtocolVersion.V4;
                }
            }

            if (response.StatusCode.IsError())
            {
                return DataServiceProtocolVersion.V4;
            }

            DataServiceProtocolVersion expectedVersion = DataServiceProtocolVersion.V4;
            
            // Apply minDsv if MPV > V2
            if (maxDataServiceVersion != DataServiceProtocolVersion.Unspecified && this.maxProtocolVersion >= maxDataServiceVersion)
            {
                expectedVersion = maxDataServiceVersion;
            }
            else
            {
                expectedVersion = this.maxProtocolVersion;
            }

            // If body of a response is empty, the version is V1 unless it has prefer header.
            if (!this.IsResponseBodyEmpty(response))
            {
                if (request.Uri.IsMetadata())
                {
                    // metadata payloads are not handled by the normal payload element visitor, but the response header will match the model version exactly
                    expectedVersion = VersionHelper.IncreaseVersionIfRequired(expectedVersion, this.ModelVersionCalculator.CalculateProtocolVersion(this.model));
                }
                else
                {
                    // GET requests are versioned based on the URI because type information is not known until serialization
                    if (request.GetEffectiveVerb() == HttpVerb.Get || request.Uri.IsServiceOperation() || request.Uri.IsAction())
                    {
                        expectedVersion = VersionHelper.IncreaseVersionIfRequired(expectedVersion, this.UriVersionCalculator.CalculateProtocolVersion(request.Uri, response.Headers[HttpHeaders.ContentType], this.maxProtocolVersion, dataServiceVersion, maxDataServiceVersion));
                    }

                    // Post and update requests are versioned based on the specific instance
                    if (response.RootElement != null)
                    {
                        expectedVersion = VersionHelper.IncreaseVersionIfRequired(expectedVersion, this.PayloadElementVersionCalculator.CalculateProtocolVersion(response.RootElement, response.Headers[HttpHeaders.ContentType], this.maxProtocolVersion, maxDataServiceVersion));
                    }
                }
            }
            else
            {
                if (request.Uri.IsAction())
                {
                    expectedVersion = VersionHelper.IncreaseVersionIfRequired(expectedVersion, this.UriVersionCalculator.CalculateProtocolVersion(request.Uri, response.GetHeaderValueIfExists(HttpHeaders.ContentType), this.maxProtocolVersion, dataServiceVersion, maxDataServiceVersion));
                }
            }

            // NOTE: the prefer verifier will ensure that this header is present if it should be, so our only concern here
            // is that the version is >= V3 if it is present
            if (response.Headers.ContainsKey(HttpHeaders.PreferenceApplied))
            {
                expectedVersion = VersionHelper.IncreaseVersionIfRequired(expectedVersion, DataServiceProtocolVersion.V4);
            }

            return expectedVersion;
        } 
 /// <summary>
 /// Returns true if this is a DELETE request
 /// </summary>
 /// <param name="request">The request being verified</param>
 /// <returns>Whether or not this verifier applies to the request</returns>
 public bool Applies(ODataRequest request)
 {
     return(request.GetEffectiveVerb() == HttpVerb.Delete);
 }
Beispiel #18
0
        /// <summary>
        /// Verifies response to request with prefer header
        /// </summary>
        /// <param name="request">The request to verify</param>
        /// <param name="response">The response to verify</param>
        public override void Verify(ODataRequest request, ODataResponse response)
        {
            base.Verify(request, response);

            string preferAppliedHeaderValue;
            string preferHeaderValue = null;
            bool   preferIsValid     = request.Headers.TryGetValue(HttpHeaders.Prefer, out preferHeaderValue) && validPreferValues.Contains(preferHeaderValue);

            // if the prefer header is present, valid, and applies to the request...
            if (preferIsValid && request.PreferHeaderApplies(this.maxProtocolVersion))
            {
                // ensure the preference-applied header is there
                this.Assert(
                    response.Headers.TryGetValue(HttpHeaders.PreferenceApplied, out preferAppliedHeaderValue),
                    "Missing Preference-Applied header in the response",
                    request,
                    response);

                // ensure the value of preference-applied matches the prefer header sent
                this.Assert(
                    preferHeaderValue == preferAppliedHeaderValue,
                    "The Preference-Applied Header value on response is different from request",
                    request,
                    response);

                // verify whether the content matches the preference
                if (preferAppliedHeaderValue.Equals(HttpHeaders.ReturnContent))
                {
                    // if the URI refers to a property value, it may be empty if the value is empty
                    // we will verify elsewhere that the value matches the expected property value
                    if (!request.Uri.IsPropertyValue())
                    {
                        this.AssertResponseIsNonEmpty(request, response);
                    }
                }
                else if (preferAppliedHeaderValue.Equals(HttpHeaders.ReturnNoContent))
                {
                    this.AssertResponseIsEmpty(request, response);
                }
            }
            else
            {
                // no preference-applied header should be present
                this.Assert(
                    !response.Headers.TryGetValue(HttpHeaders.PreferenceApplied, out preferAppliedHeaderValue),
                    "Unexpected Preference-Applied header in the response",
                    request,
                    response);

                // the content should match the default behavior
                if (request.PreferHeaderApplies(this.maxProtocolVersion))
                {
                    if (request.GetEffectiveVerb() == HttpVerb.Post)
                    {
                        this.AssertResponseIsNonEmpty(request, response);
                    }
                    else if (request.Uri.IsEntity())
                    {
                        this.AssertResponseIsEmpty(request, response);
                    }
                }
            }
        }
        /// <summary>
        /// Returns true if this is an update request
        /// </summary>
        /// <param name="request">The request being verified</param>
        /// <returns>Whether or not this verifier applies to the request</returns>
        public bool Applies(ODataRequest request)
        {
            if (request.Uri.LastSegment is FunctionSegment)
            {
                return false;
            }

            if (request.Uri.IsServiceOperation())
            {
                return false;
            }

            if (request.Uri.IsBatch())
            {
                return false;
            }

            var verb = request.GetEffectiveVerb();
            return verb != HttpVerb.Delete && verb != HttpVerb.Get;
        }
 private void GetUpdatedEntityBeforeAndAfter(ODataRequest request, ODataResponse response, out QueryStructuralValue beforeUpdate, out QueryStructuralValue afterUpdate)
 {
     using (this.Context.Begin(request))
     {
         if (request.GetEffectiveVerb() == HttpVerb.Post)
         {
             afterUpdate = this.Context.GetInsertedEntity(request, response);
             ExceptionUtilities.CheckObjectNotNull(afterUpdate, "Structural value returned by GetInsertedEntity unexpectedly null");
             beforeUpdate = afterUpdate.Type.NullValue;
         }
         else
         {
             this.Context.GetUpdatedEntity(request, out beforeUpdate, out afterUpdate);
             ExceptionUtilities.CheckObjectNotNull(beforeUpdate, "Before-update structural value returned by GetUpdatedEntity unexpectedly null");
             ExceptionUtilities.CheckObjectNotNull(afterUpdate, "After-update structural value returned by GetUpdatedEntity unexpectedly null");
         }
     }
 }
        /// <summary>
        /// Gets the entity inserted by the given request.
        /// </summary>
        /// <param name="request">The request</param>
        /// <param name="response">The response</param>
        /// <returns>The inserted entity</returns>
        public QueryStructuralValue GetInsertedEntity(ODataRequest request, ODataResponse response)
        {
            ExceptionUtilities.CheckArgumentNotNull(request, "request");
            ExceptionUtilities.CheckArgumentNotNull(response, "response");
            ExceptionUtilities.Assert(this.requestsBegun.Contains(request), "Cannot use GetInsertedEntity before calling Begin");
            ExceptionUtilities.Assert(request.GetEffectiveVerb() == HttpVerb.Post, "Cannot use GetInsertedEntity on non POST requests");

            QueryStructuralValue insertedEntity;
            if (!this.insertedEntityCache.TryGetValue(request, out insertedEntity))
            {
                EntitySet entitySet;
                ExceptionUtilities.Assert(request.Uri.TryGetExpectedEntitySet(out entitySet), "Could not infer entity set from URI");

                var entitySetUri = new ODataUri(new EntitySetSegment(entitySet));

                var beforeSync = this.Evaluator.Evaluate(entitySetUri, false, false) as QueryCollectionValue;
                ExceptionUtilities.CheckObjectNotNull(beforeSync, "Could not evaluate entity set '{0}' before syncing", entitySet.Name);
                
                // create a shallow copy
                beforeSync = beforeSync.Type.CreateCollectionWithValues(beforeSync.Elements);

                SyncHelpers.ExecuteActionAndWait(c => this.Synchronizer.SynchronizeEntireEntitySet(c, entitySet.Name));

                var afterSync = this.Evaluator.Evaluate(entitySetUri, false, false) as QueryCollectionValue;
                ExceptionUtilities.CheckObjectNotNull(afterSync, "Could not evaluate entity set '{0}' after syncing", entitySet.Name);

                // TODO: handle deep insert (using location header or response payload)
                var newElements = afterSync.Elements.Except(beforeSync.Elements).OfType<QueryStructuralValue>().ToList();
                this.insertedEntityCache[request] = insertedEntity = newElements.Single();

                // update the parent entity, if there is one
                var segments = request.Uri.Segments;
                var lastNavigationSegment = segments.OfType<NavigationSegment>().LastOrDefault();
                if (lastNavigationSegment != null)
                {
                    var segmentsBeforeNavigation = segments.TakeWhile(t => t != lastNavigationSegment);
                    var parentEntity = (QueryStructuralValue)this.Evaluator.Evaluate(new ODataUri(segmentsBeforeNavigation), false, false);
                    SyncHelpers.ExecuteActionAndWait(c => this.Synchronizer.SynchronizeEntity(c, parentEntity));
                }
            }

            return insertedEntity;
        }
        /// <summary>
        /// Gets the pre and post update representations of the entity updated by the given request
        /// </summary>
        /// <param name="request">The request</param>
        /// <param name="beforeUpdate">The entity before the update</param>
        /// <param name="afterUpdate">The entity after the update</param>
        public void GetUpdatedEntity(ODataRequest request, out QueryStructuralValue beforeUpdate, out QueryStructuralValue afterUpdate)
        {
            ExceptionUtilities.CheckArgumentNotNull(request, "request");
            ExceptionUtilities.Assert(this.requestsBegun.Contains(request), "Cannot use GetUpdatedEntity before calling Begin");
            ExceptionUtilities.Assert(request.GetEffectiveVerb().IsUpdateVerb(), "Cannot use GetUpdatedEntity on non update requests");

            KeyValuePair<QueryStructuralValue, QueryStructuralValue> beforeAndAfter;
            if (!this.updatedEntityCache.TryGetValue(request, out beforeAndAfter))
            {
                var entityUri = request.Uri.ScopeToEntity();
                var entity = (QueryStructuralValue)this.Evaluator.Evaluate(entityUri, false, false);

                beforeUpdate = this.QueryValueCopier.PerformDeepCopy(entity);

                SyncHelpers.ExecuteActionAndWait(c => this.Synchronizer.SynchronizeEntity(c, entity));

                afterUpdate = entity;

                beforeAndAfter = new KeyValuePair<QueryStructuralValue, QueryStructuralValue>(beforeUpdate, afterUpdate);
                this.updatedEntityCache[request] = beforeAndAfter;
            }
            else
            {
                beforeUpdate = beforeAndAfter.Key;
                afterUpdate = beforeAndAfter.Value;
            }
        }
 /// <summary>
 /// Returns true if this is a DELETE request
 /// </summary>
 /// <param name="request">The request being verified</param>
 /// <returns>Whether or not this verifier applies to the request</returns>
 public bool Applies(ODataRequest request)
 {
     return request.GetEffectiveVerb() == HttpVerb.Delete;
 }
Beispiel #24
0
        private DataServiceProtocolVersion CalculateDataServiceProtocolVersion(ODataRequest request, ODataResponse response)
        {
            DataServiceProtocolVersion dataServiceVersion    = VersionHelper.ConvertToDataServiceProtocolVersion(request.GetHeaderValueIfExists(HttpHeaders.DataServiceVersion));
            DataServiceProtocolVersion maxDataServiceVersion = VersionHelper.ConvertToDataServiceProtocolVersion(request.GetHeaderValueIfExists(HttpHeaders.MaxDataServiceVersion));

            var responseContentType = response.GetHeaderValueIfExists(HttpHeaders.ContentType);

            if (responseContentType != null)
            {
                if (responseContentType.StartsWith(MimeTypes.ApplicationJsonODataLightNonStreaming, StringComparison.OrdinalIgnoreCase) ||
                    responseContentType.StartsWith(MimeTypes.ApplicationJsonODataLightStreaming, StringComparison.OrdinalIgnoreCase))
                {
                    return(DataServiceProtocolVersion.V4);
                }
            }

            if (response.StatusCode.IsError())
            {
                return(DataServiceProtocolVersion.V4);
            }

            DataServiceProtocolVersion expectedVersion = DataServiceProtocolVersion.V4;

            // Apply minDsv if MPV > V2
            if (maxDataServiceVersion != DataServiceProtocolVersion.Unspecified && this.maxProtocolVersion >= maxDataServiceVersion)
            {
                expectedVersion = maxDataServiceVersion;
            }
            else
            {
                expectedVersion = this.maxProtocolVersion;
            }

            // If body of a response is empty, the version is V1 unless it has prefer header.
            if (!this.IsResponseBodyEmpty(response))
            {
                if (request.Uri.IsMetadata())
                {
                    // metadata payloads are not handled by the normal payload element visitor, but the response header will match the model version exactly
                    expectedVersion = VersionHelper.IncreaseVersionIfRequired(expectedVersion, this.ModelVersionCalculator.CalculateProtocolVersion(this.model));
                }
                else
                {
                    // GET requests are versioned based on the URI because type information is not known until serialization
                    if (request.GetEffectiveVerb() == HttpVerb.Get || request.Uri.IsServiceOperation() || request.Uri.IsAction())
                    {
                        expectedVersion = VersionHelper.IncreaseVersionIfRequired(expectedVersion, this.UriVersionCalculator.CalculateProtocolVersion(request.Uri, response.Headers[HttpHeaders.ContentType], this.maxProtocolVersion, dataServiceVersion, maxDataServiceVersion));
                    }

                    // Post and update requests are versioned based on the specific instance
                    if (response.RootElement != null)
                    {
                        expectedVersion = VersionHelper.IncreaseVersionIfRequired(expectedVersion, this.PayloadElementVersionCalculator.CalculateProtocolVersion(response.RootElement, response.Headers[HttpHeaders.ContentType], this.maxProtocolVersion, maxDataServiceVersion));
                    }
                }
            }
            else
            {
                if (request.Uri.IsAction())
                {
                    expectedVersion = VersionHelper.IncreaseVersionIfRequired(expectedVersion, this.UriVersionCalculator.CalculateProtocolVersion(request.Uri, response.GetHeaderValueIfExists(HttpHeaders.ContentType), this.maxProtocolVersion, dataServiceVersion, maxDataServiceVersion));
                }
            }

            // NOTE: the prefer verifier will ensure that this header is present if it should be, so our only concern here
            // is that the version is >= V3 if it is present
            if (response.Headers.ContainsKey(HttpHeaders.PreferenceApplied))
            {
                expectedVersion = VersionHelper.IncreaseVersionIfRequired(expectedVersion, DataServiceProtocolVersion.V4);
            }

            return(expectedVersion);
        }
        /// <summary>
        /// Verifies response to request with prefer header 
        /// </summary>
        /// <param name="request">The request to verify</param>
        /// <param name="response">The response to verify</param>
        public override void Verify(ODataRequest request, ODataResponse response)
        {
            base.Verify(request, response);

            string preferAppliedHeaderValue;
            string preferHeaderValue = null;
            bool preferIsValid = request.Headers.TryGetValue(HttpHeaders.Prefer, out preferHeaderValue) && validPreferValues.Contains(preferHeaderValue);

            // if the prefer header is present, valid, and applies to the request...
            if (preferIsValid && request.PreferHeaderApplies(this.maxProtocolVersion))
            {
                // ensure the preference-applied header is there
                this.Assert(
                    response.Headers.TryGetValue(HttpHeaders.PreferenceApplied, out preferAppliedHeaderValue),
                    "Missing Preference-Applied header in the response",
                    request,
                    response);

                // ensure the value of preference-applied matches the prefer header sent
                this.Assert(
                    preferHeaderValue == preferAppliedHeaderValue,
                    "The Preference-Applied Header value on response is different from request",
                    request,
                    response);

                // verify whether the content matches the preference
                if (preferAppliedHeaderValue.Equals(HttpHeaders.ReturnContent))
                {
                    // if the URI refers to a property value, it may be empty if the value is empty
                    // we will verify elsewhere that the value matches the expected property value
                    if (!request.Uri.IsPropertyValue())
                    {
                        this.AssertResponseIsNonEmpty(request, response);
                    }
                }
                else if (preferAppliedHeaderValue.Equals(HttpHeaders.ReturnNoContent))
                {
                    this.AssertResponseIsEmpty(request, response);
                }
            }
            else
            {
                // no preference-applied header should be present
                this.Assert(
                    !response.Headers.TryGetValue(HttpHeaders.PreferenceApplied, out preferAppliedHeaderValue),
                    "Unexpected Preference-Applied header in the response",
                    request,
                    response);

                // the content should match the default behavior
                if (request.PreferHeaderApplies(this.maxProtocolVersion))
                {
                    if (request.GetEffectiveVerb() == HttpVerb.Post)
                    {
                        this.AssertResponseIsNonEmpty(request, response);
                    }
                    else if (request.Uri.IsEntity())
                    {
                        this.AssertResponseIsEmpty(request, response);
                    }
                }
            }
        }