public override void Verify(ODataRequest request, ODataResponse response)
        {
            base.Verify(request, response);

            string expectedETag = this.CalculateExpectedETag(request, response);
            this.Verify(expectedETag, request, response);
        }
 /// <summary>
 /// Verifies the given OData request/response pair
 /// </summary>
 /// <param name="request">The request to verify</param>
 /// <param name="response">The response to verify</param>
 public virtual void Verify(ODataRequest request, ODataResponse response)
 {
     ExceptionUtilities.CheckArgumentNotNull(request, "request");
     ExceptionUtilities.CheckArgumentNotNull(response, "response");
     ExceptionUtilities.CheckAllRequiredDependencies(this);
     ExceptionUtilities.CheckObjectNotNull(this.Logger, "Cannot run verifier without logger");
 }
        /// <summary>
        /// Verifies that any navigation or relationship/association links in the payload are correct.
        /// </summary>
        /// <param name="request">The request that was sent</param>
        /// <param name="response">The response to verify</param>
        public override void Verify(ODataRequest request, ODataResponse response)
        {
            base.Verify(request, response);

            // Do not need to verify relationship links in metadata response. Skip it to avoid NullReferenceException in GetExpectedEntitySet call
            // TODO: Make the Json Light decision based on the request URI. Skip for now, since the links are not serialized by default.
            if (request.Uri.IsMetadata() || this.IsJsonLightResponse(response))
            {
                return;
            }

            // gather up all the entities to verify
            List<EntityInstance> entities = new List<EntityInstance>();
            if (response.RootElement.ElementType == ODataPayloadElementType.EntityInstance)
            {
                entities.Add(response.RootElement as EntityInstance);
            }
            else if (response.RootElement.ElementType == ODataPayloadElementType.EntitySetInstance)
            {
                foreach (EntityInstance ei in response.RootElement as EntitySetInstance)
                {
                    entities.Add(ei as EntityInstance);
                }
            }

            this.VerifyEntityLinks(request, response, entities);
        }
        private string RetrieveAcceptedContentType(ODataRequest request)
        {
            string acceptedContentType = string.Empty;

            if (request.Uri.Format != null)
            {
                switch (request.Uri.Format)
                {
                    case FormatQueryOptions.Atom:
                        acceptedContentType = MimeTypes.ApplicationAtomXml;
                        break;
                    case FormatQueryOptions.Xml:
                        acceptedContentType = MimeTypes.ApplicationXml;
                        break;
                    default:
                        ExceptionUtilities.Assert(request.Uri.Format == FormatQueryOptions.Json, "Unexpected request.Uri.Format.");
                        acceptedContentType = MimeTypes.ApplicationJson;
                        break;
                }
            }
            else if (!request.Headers.TryGetValue(HttpHeaders.Accept, out acceptedContentType))
            {
                // TODO: be strict for the data-services implementation
                acceptedContentType = MimeTypes.Any;
            }

            return acceptedContentType;
        }
        /// <summary>
        /// Checks that the response's status code matches the expected value
        /// </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);

            this.Assert(response.StatusCode == this.ExpectedStatusCode, string.Format(CultureInfo.InvariantCulture, "Expected status code '{0}', observed '{1}'", this.ExpectedStatusCode, response.StatusCode), request, response);
            
            this.Logger.WriteLine(LogLevel.Verbose, CultureInfo.InvariantCulture, "Status code was '{0}'", this.ExpectedStatusCode);
        }
        /// <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>
        /// Verifies the given OData request/response pair
        /// </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);

            if (response.RootElement != null)
            {
                this.Validator.ValidateDateTimeFormatting(response.RootElement);
            }
        }
        /// <summary>
        /// Verifies response has 'X-Content-Type-Options' HTTP 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 noSniffResponseHeaderValue = null;
            bool noSniffExists = response.Headers.TryGetValue(HttpHeaders.XContentTypeOptions, out noSniffResponseHeaderValue);
            this.Assert(noSniffExists, HttpHeaders.XContentTypeOptions + " header was not found in the response", request, response);
            this.Assert(NoSniffValue.Equals(noSniffResponseHeaderValue), HttpHeaders.XContentTypeOptions + " header contained incorrect value " + noSniffResponseHeaderValue, request, response);
        }
 /// <summary>
 /// Returns true if this is not action
 /// </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)
 {
     // ActionResponseVerifier verifies action status code for assumed working scenario, other wise should expect error
     if (request.Uri.IsAction())
     {
         return ((int)this.ExpectedStatusCode) > 399; 
     }
     
     return true; 
 }
        /// <summary>
        /// Helper method for verifiers to make assertions
        /// </summary>
        /// <param name="condition">The condition to assert is true</param>
        /// <param name="message">The message to write to the log if the assertion fails</param>
        /// <param name="request">The request being verified</param>
        /// <param name="response">The response being verified</param>
        protected void Assert(bool condition, string message, ODataRequest request, ODataResponse response)
        {
            if (!condition)
            {
                ExceptionUtilities.CheckArgumentNotNull(message, "message");
                ExceptionUtilities.CheckArgumentNotNull(request, "request");
                ExceptionUtilities.CheckArgumentNotNull(response, "response");

                this.Logger.WriteLine(LogLevel.Error, message);
                this.ReportFailure(request, response);
                throw new ResponseVerificationException();
            }
        }
        /// <summary>
        /// Helper method for verifiers to report failure
        /// </summary>
        /// <param name="request">The request that failed verification</param>
        /// <param name="response">The response that failed verification</param>
        protected void ReportFailure(ODataRequest request, ODataResponse response)
        {
            ExceptionUtilities.CheckArgumentNotNull(request, "request");
            ExceptionUtilities.CheckArgumentNotNull(response, "response");

            if (this.OnReportingFailure != null)
            {
                this.OnReportingFailure(request, response);
            }

            this.Logger.WriteLine(LogLevel.Verbose, "Response verification failure");
            request.WriteToLog(this.Logger, LogLevel.Verbose);
            response.WriteToLog(this.Logger, LogLevel.Verbose);
        }
 internal void Verify(string expectedETag, ODataRequest request, ODataResponse response)
 {
     string etagValue;
     bool hadETag = response.Headers.TryGetValue(HttpHeaders.ETag, out etagValue);
     if (expectedETag == null)
     {
         this.Assert(!hadETag, string.Format(CultureInfo.InvariantCulture, "Response contained unexpected ETag header with value '{0}'.", etagValue), request, response);
     }
     else
     {
         this.Assert(hadETag, string.Format(CultureInfo.InvariantCulture, "ETag header unexpectedly missing from response. Expected '{0}'.", expectedETag), request, response);
         this.Assert(expectedETag == etagValue, string.Format(CultureInfo.InvariantCulture, "ETag header value incorrect.\r\nExpected: '{0}'\r\nActual:   '{1}'", expectedETag, etagValue), request, response);
     }
 }
        /// <summary>
        /// Returns true if this is a query-like GET 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.Verb != HttpVerb.Get)
            {
                return false;
            }

            if (request.Uri.IsBatch() || request.Uri.IsMetadata() || request.Uri.IsServiceDocument() || request.Uri.IsMediaResource())
            {
                return false;
            }

            return true;
        }
        /// <summary>
        /// Checks that the response's status code matches the expected value
        /// </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);

            ODataErrorPayload errorPayload;
            this.Assert(TryGetErrorPayloadFromResponse(response, this.FormatSelector, out errorPayload), "Expected an error payload", request, response);

            this.ExpectedErrorMessage.VerifyExceptionMessage(
                this.ResourceVerifier, 
                errorPayload, 
                (assertion, errorMessage) =>
                {
                    this.Assert(assertion, errorMessage, request, response);
                });
        }
        public override void Verify(ODataRequest request, ODataResponse response)
        {
            base.Verify(request, response);

            try
            {
                var expected = this.Evaluator.Evaluate(request.Uri);
                this.VerificationServices.ValidateResponsePayload(request.Uri, response, expected, this.maxProtocolVersion);
            }
            catch (Exception e)
            {
                this.ReportFailure(request, response);
                throw new ResponseVerificationException(e);
            }
        }
        /// <summary>
        /// Checks that the response's status code matches the expected value
        /// </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);

            if (request.Uri.IsBatch())
            {
                throw new TaupoNotSupportedException("Batching isn't supported yet, when it is we need to iterate over sets of requests in batch and calculate the version");
            }

            var expectedDataServiceProtocolVersion = this.CalculateDataServiceProtocolVersion(request, response);
            
            string expectedProtocolVersion = expectedDataServiceProtocolVersion.ToString().Replace("V", string.Empty) + ".0;";

            string actualDataServiceVersionHeaderValue = response.Headers[HttpHeaders.DataServiceVersion];

            this.Assert(expectedProtocolVersion.Equals(actualDataServiceVersionHeaderValue), string.Format(CultureInfo.InvariantCulture, "Data Service Version Header error, Expected '{0}' Actual '{1}'", expectedProtocolVersion, actualDataServiceVersionHeaderValue), request, response);
        }
        public override void Verify(ODataRequest request, ODataResponse response)
        {
            base.Verify(request, response);

            if (response.StatusCode == HttpStatusCode.NoContent)
            {
                this.Assert(
                    !response.Headers.ContainsKey(HttpHeaders.ContentType),
                    "Content-Type header should not be returned for the responses without content.",
                    request,
                    response);
            }
            else
            {
                // Verify the response Content-Type header exists and is consistent with the request Accept header
                string responseContentType;
                this.Assert(
                    response.Headers.TryGetValue(HttpHeaders.ContentType, out responseContentType),
                    "Response Content-Type header does not exist.",
                    request,
                    response);

                string acceptedContentType = this.RetrieveAcceptedContentType(request);
                
                // TODO: strict verification based on the type of the uri
                var acceptedMimeTypes = acceptedContentType.Split(',').Select(type => type.ToLowerInvariant()).ToList();

                this.Assert(
                    acceptedMimeTypes.Any(h => responseContentType.StartsWith(h, StringComparison.Ordinal))
                    || acceptedMimeTypes.Any(acceptType => acceptType == MimeTypes.Any),
                    "Response Content-Type header should be consistent with request Accept header.",
                    request,
                    response);

                if (responseContentType.StartsWith(MimeTypes.ApplicationJson, StringComparison.OrdinalIgnoreCase)) 
                {
                    this.Assert(
                        responseContentType.StartsWith(MimeTypes.ApplicationJsonODataLightNonStreaming, StringComparison.Ordinal) ||
                        responseContentType.StartsWith(MimeTypes.ApplicationJsonODataLightStreaming, StringComparison.Ordinal),
                        "JSON responses should be fully qualified",
                        request,
                        response);
                }
            }
        }
        /// <summary>
        /// Informs the context that verification for the given request has begun. Can be called multiple times.
        /// Disposing the returned value indicates that verification has finished.
        /// </summary>
        /// <param name="request">The request being verified</param>
        /// <returns>A disposable token which, when disposed, indicates that verification is finished.</returns>
        public IDisposable Begin(ODataRequest request)
        {
            ExceptionUtilities.CheckArgumentNotNull(request, "request");

            if (!this.requestsBegun.Add(request))
            {
                return new DelegateBasedDisposable(() => { });
            }

            return new DelegateBasedDisposable(
                () =>
                {
                    // clear all caches
                    insertedEntityCache.Remove(request);
                    updatedEntityCache.Remove(request);
                    requestsBegun.Remove(request);
                });
        }
        /// <summary>
        /// Verify the given OData request/response pair against all attached verifiers that apply
        /// </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);

            IDisposable scope = null;
            if (this.Context != null)
            {
                scope = this.Context.Begin(request);
            }

            try
            {
                foreach (var verifier in this.Verifiers)
                {
                    // make sure the verifier applies
                    var selective = verifier as ISelectiveResponseVerifier;
                    if (selective == null || (selective.Applies(request) && selective.Applies(response)))
                    {
                        try
                        {
                            verifier.Verify(request, response);
                        }
                        catch (ResponseVerificationException e)
                        {
                            // wrap and re-throw to preserve original call-stack. Ideally we would just let these through.
                            throw new ResponseVerificationException(e);
                        }
                        catch (Exception e)
                        {
                            this.Logger.WriteLine(LogLevel.Error, "Verifier '{0}' threw unexpected exception '{1}'", verifier, e.Message);
                            this.ReportFailure(request, response);
                            throw new ResponseVerificationException(e);
                        }
                    }
                }
            }
            finally
            {
                if (scope != null)
                {
                    scope.Dispose();
                }
            }
        }
        /// <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.IsEntity() || request.Uri.IsEntitySet() || request.Uri.IsEntityReferenceLink())
            {
                if (request.Uri.IsAction())
                {
                    return false;
                }

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

                return request.Verb == HttpVerb.Get;
            }

            return false;
        }
        /// <summary>
        /// Verifies the given OData request/response pair using the delegate given at construction time.
        /// </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);

            try
            {
                this.verify(request, response);
            }
            catch (ResponseVerificationException e)
            {
                // wrap and re-throw to preserve original call-stack. Ideally we would just let these through.
                throw new ResponseVerificationException(e);
            }
            catch (Exception e)
            {
                this.ReportFailure(request, response);
                throw new ResponseVerificationException(e);
            }
        }
        /// <summary>
        /// Verifies the response payload type is consistent with the request uri
        /// </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);

            var expectedPayloadType = request.Uri.GetExpectedPayloadType();

            // Posting to entity set expects entity instance in response, but posting to service operation response should be consistent with its definition
            if (request.Verb == HttpVerb.Post && request.Uri.IsEntitySet() && (!request.Uri.IsWebInvokeServiceOperation()) && (!request.Uri.IsAction()))
            {
                expectedPayloadType = ODataPayloadElementType.EntityInstance;
            }

            string message = null;
            if (response.RootElement.ElementType != expectedPayloadType)
            {
                message = @"Response payload type did not match. 
Expected: '{0}'
Actual:   '{1}'";
                message = string.Format(CultureInfo.InvariantCulture, message, expectedPayloadType, response.RootElement.ElementType);
            }

            this.Assert(message == null, message, request, response);
        }
Exemple #23
0
 /// <inheritdoc />
 protected override async Task WriteSingleContentAsync(HttpContext httpContext, ODataRequest odataRequest, ODataEntity fields)
 {
     await WriteAsync(new ODataSingleContent { FieldData = fields }, httpContext, odataRequest).ConfigureAwait(false);
 }
Exemple #24
0
 protected override void AssignHeaders(ODataRequest request)
 {
     request.Headers.Add(HttpLiteral.Prefer, request.ResultRequired ? HttpLiteral.ReturnContent : HttpLiteral.ReturnNoContent);
 }
        private void VerifyPropertyValues(ODataRequest request, IQueryScalarValueToClrValueComparer primitiveComparer, QueryStructuralValue afterUpdate)
        {
            var namedValues = this.ExtractNamedValues(request);

            this.Comparer.Compare(afterUpdate, namedValues, primitiveComparer);
        }
 /// <summary>
 /// Returns true if this verifier applies to responses for this kind of request
 /// </summary>
 /// <param name="request">The request that was issued</param>
 /// <returns>Whether or not this verifier applies to responses for this kind of request</returns>
 public bool Applies(ODataRequest request)
 {
     // Do not need to verify action/function links in metadata response.
     return(!request.Uri.IsMetadata());
 }
        /// <summary>
        /// Verifies that any action/function in the payload element are correct.
        /// </summary>
        /// <param name="payloadElement">The payload element to verify</param>
        /// <param name="request">The request that was sent</param>
        /// <param name="response">The response to verify</param>
        /// <param name="isTopLevelElement">Whether the payloadElement is the top level of response</param>
        private void VerifyPayloadElementOperations(ODataPayloadElement payloadElement, ODataRequest request, ODataResponse response, bool isTopLevelElement)
        {
            // find all the entities and verify action/functions for the entities.
            List <EntityInstance> entities = new List <EntityInstance>();

            if (payloadElement.ElementType == ODataPayloadElementType.EntityInstance)
            {
                entities.Add(payloadElement as EntityInstance);
            }
            else if (payloadElement.ElementType == ODataPayloadElementType.EntitySetInstance)
            {
                foreach (EntityInstance ei in payloadElement as EntitySetInstance)
                {
                    entities.Add(ei as EntityInstance);
                }
            }

            this.VerifyEntityOperations(request, response, entities, isTopLevelElement);
        }
Exemple #28
0
        public void QueryString_BuildFromProperties_EmptyIsBuggy()
        {
            var req = new ODataRequest();

            Assert.AreEqual("", req.ToString());
        }
        /// <summary>
        /// Verifies the delete succeeded
        /// </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);

            var originalUri = request.Uri;

            var entityUri = originalUri.ScopeToEntity();

            var beforeSync = this.Evaluator.Evaluate(entityUri, false, false) as QueryStructuralValue;

            ExceptionUtilities.CheckObjectNotNull(beforeSync, "Could not get entity before syncing");
            ExceptionUtilities.Assert(!beforeSync.IsNull, "Entity was null before syncing");

            var afterSync = this.SynchronizeAndEvaluate(entityUri, beforeSync, originalUri.IsEntity());

            QueryValue currentValue = afterSync;
            string     message      = null;

            if (originalUri.IsEntity())
            {
                // DELETE Customers(1)
                message = "Entity was not deleted";
            }
            else if (originalUri.IsPropertyValue())
            {
                // DELETE Customers(1)/Name/$value
                message = "Property value was not null";

                // drill down by each property in the uri after the entity portion
                foreach (var propertySegment in originalUri.Segments.Skip(entityUri.Segments.Count).OfType <PropertySegment>())
                {
                    currentValue = ((QueryStructuralValue)currentValue).GetValue(propertySegment.Property.Name);
                }
            }
            else if (originalUri.IsEntityReferenceLink())
            {
                // TODO: verify back-links?
                var navigation = originalUri.Segments.OfType <NavigationSegment>().Last();

                if (navigation.NavigationProperty.ToAssociationEnd.Multiplicity == EndMultiplicity.Many)
                {
                    // DELETE Customers(1)/Orders/$ref?$id=Orders(1)
                    message = "Collection link was not deleted";

                    var linkUri = new ODataUri(entityUri.Segments.Concat(navigation, originalUri.Segments.OfType <KeyExpressionSegment>().Last()));
                    currentValue = this.Evaluator.Evaluate(linkUri, false, false);
                }
                else
                {
                    // DELETE Customers(1)/BestFriend/$ref
                    message = "Reference link was not deleted";

                    currentValue = ((QueryStructuralValue)afterSync).GetValue(navigation.NavigationProperty.Name);
                }
            }

            // at this point, the current value should be null if the delete was successful
            ExceptionUtilities.CheckObjectNotNull(message, "Uri did not represent an entity, value, or link: '{0}'", request.GetRequestUri());
            this.Assert(currentValue.IsNull, message, request, response);

            if (originalUri.IsEntity())
            {
                this.RequeryEntityAndVerifyStatusCode(request, response, entityUri);

                if (entityUri.Segments.OfType <NavigationSegment>().Any())
                {
                    // convert the uri into a top-level access
                    entityUri = GetTopLevelUri(beforeSync);
                    entityUri.Segments.Insert(0, originalUri.Segments.OfType <ServiceRootSegment>().Single());

                    this.RequeryEntityAndVerifyStatusCode(request, response, entityUri);
                }
            }
        }
Exemple #30
0
 /// <inheritdoc />
 protected override Task WriteCountAsync(HttpContext httpContext, ODataRequest odataRequest, int count)
 {
     return(WriteRawAsync(count, httpContext, odataRequest));
 }
 /// <summary>
 /// Verify that the title attribute of navigation/relationship link is not null and has the value consistent with the navigation property name.
 /// </summary>
 /// <param name="navigation">navigation property instance for the navigation link</param>
 /// <param name="link">The navigation/relationship link to verify.</param>
 /// <param name="request">The request needed for error report.</param>
 /// <param name="response">The response needed for error report.</param>
 private void VerifyAtomTitleAttribute(NavigationPropertyInstance navigation, ODataPayloadElement link, ODataRequest request, ODataResponse response)
 {
     TitleAnnotation titleAnnotation = link.Annotations.OfType<TitleAnnotation>().SingleOrDefault();
     if (titleAnnotation == null || string.Compare(titleAnnotation.Value, navigation.Name, StringComparison.CurrentCulture) != 0)
     {
         this.Logger.WriteLine(LogLevel.Verbose, CultureInfo.InvariantCulture, "Expected navigation property Title attribute '{0}', observed '{1}'", navigation.Name, titleAnnotation == null ? "null" : titleAnnotation.Value);
         this.ReportFailure(request, response);
         throw new ResponseVerificationException();
     }
 }
        /// <summary>
        /// Verify that the type attribute of navigation link is not null and has the expected value.
        /// </summary>
        /// <param name="link">The relationship link to verify.</param>
        /// <param name="request">The request needed for error report.</param>
        /// <param name="response">The response needed for error report.</param>
        private void VerifyAtomNavigationLinkTypeAttribute(ODataPayloadElement link, ODataRequest request, ODataResponse response)
        {
            ContentTypeAnnotation typeAnnotation = link.Annotations.OfType<ContentTypeAnnotation>().SingleOrDefault();

            if (typeAnnotation == null ||
                (!typeAnnotation.Value.Equals("application/atom+xml;type=feed", StringComparison.Ordinal) &&
                !typeAnnotation.Value.Equals("application/atom+xml;type=entry", StringComparison.Ordinal)))
            {
                this.Logger.WriteLine(LogLevel.Verbose, CultureInfo.InvariantCulture, "Expected navigation link type attribute application/atom+xml;type=feed or application/atom+xml;type=entry, observed '{0}'", typeAnnotation == null ? "null" : typeAnnotation.Value);
                this.ReportFailure(request, response);
                throw new ResponseVerificationException();
            }
        }
        /// <summary>
        /// Verify the expanded navigation link is as expected.
        /// </summary>
        /// <param name="navigation">expanded navigation link to verify</param>
        /// <param name="expectedNavigationUri">expected link value</param>
        /// <param name="request">The request needed for error report.</param>
        /// <param name="response">The response to check the content type.</param>
        private void VerifyNavigationExpandedLink(NavigationPropertyInstance navigation, string expectedNavigationUri, ODataRequest request, ODataResponse response)
        {
            if (this.IsAtomResponse(response))
            {
                this.VerifyNavigationLink(navigation, expectedNavigationUri, request, response);
            }
            else
            {
                // JSON never has navigation links for expanded elements
                string expandedLinkUri = ((ExpandedLink)navigation.Value).UriString;
                if (expandedLinkUri != null)
                {
                    this.Logger.WriteLine(LogLevel.Verbose, CultureInfo.InvariantCulture, "Expected expanded link uri == null, observed {0}", expandedLinkUri);
                    this.ReportFailure(request, response);
                    throw new ResponseVerificationException();
                }
            }

            List<EntityInstance> expandedEntities = new List<EntityInstance>();
            ODataPayloadElement expandedElement = ((ExpandedLink)navigation.Value).ExpandedElement;
            if (expandedElement != null)
            {
                EntityInstance entityInstance = expandedElement as EntityInstance;
                if (entityInstance != null)
                {
                    expandedEntities.Add(entityInstance);
                }
                else
                {
                    EntitySetInstance entitySetInstance = expandedElement as EntitySetInstance;

                    foreach (EntityInstance ei in entitySetInstance)
                    {
                        expandedEntities.Add(ei);
                    }
                }

                this.VerifyEntityLinks(request, response, expandedEntities);
            }
        }
Exemple #34
0
 /// <inheritdoc />
 protected override async Task WriteActionsPropertyAsync(HttpContext httpContext, ODataRequest odataRequest, ODataActionItem[] actions, bool raw)
 {
     if (raw)
     {
         await WriteAsync(actions, httpContext, odataRequest)
         .ConfigureAwait(false);
     }
     else
     {
         await WriteAsync(new ODataSingleContent { FieldData = new ODataEntity {
                                                       { ODataMiddleware.ActionsPropertyName, actions }
                                                   } }, httpContext, odataRequest)
         .ConfigureAwait(false);
     }
 }
Exemple #35
0
 /// <inheritdoc />
 protected override async Task WriteMultipleContentAsync(HttpContext httpContext, ODataRequest odataRequest, IEnumerable <ODataEntity> contents, int count)
 {
     await WriteAsync(ODataMultipleContent.Create(contents, count), httpContext, odataRequest).ConfigureAwait(false);
 }
Exemple #36
0
        private async Task <object[]> GetOperationParametersAsync(ActionBase action,
                                                                  HttpContext httpContext, ODataRequest odataRequest)
        {
            if (action.ActionParameters.Length == 0)
            {
                return(ActionParameter.EmptyValues);
            }

            var inputStream = httpContext?.Request?.Body;
            var values      = new object[action.ActionParameters.Length];

            var parameters = action.ActionParameters;

            if (parameters.Length == 1 && parameters[0].Name == null)
            {
                var parameter = parameters[0];
                if (parameter.Type == null)
                {
                    using (var reader = new StreamReader(inputStream))
                        values[0] = reader.ReadToEnd();
                    if (parameter.Required && values[0] == null)
                    {
                        // ReSharper disable once NotResolvedInText
                        throw new ArgumentNullException("[unnamed]", "Request parameter is required.");
                    }
                }
                else
                {
                    values[0] = ODataMiddleware.Read(inputStream, parameter.Type);
                    if (parameter.Required && values[0] == null)
                    {
                        // ReSharper disable once NotResolvedInText
                        throw new ArgumentNullException("[unnamed]", "Request parameter is required. Type: " + parameter.Type.FullName);
                    }
                }
            }
            else
            {
                var model = await ODataMiddleware.ReadToJsonAsync(httpContext);

                var i = 0;
                foreach (var parameter in parameters)
                {
                    var name = parameter.Name;
                    var type = parameter.Type;
                    if (type == typeof(HttpContext))
                    {
                        values[i] = httpContext;
                    }
                    else if (type == typeof(ODataRequest))
                    {
                        values[i] = odataRequest;
                    }
                    else
                    {
                        var val = model?[name];
                        if (val == null)
                        {
                            if (parameter.Required)
                            {
                                throw new ArgumentNullException(parameter.Name);
                            }
                            values[i] = Type.Missing;
                        }
                        else
                        {
                            var valStr = val.ToString();

                            if (type == typeof(string))
                            {
                                values[i] = valStr;
                            }
                            else if (type == typeof(Boolean))
                            {
                                // we handle "True", "true" and "1" as boolean true values
                                values[i] = JsonConvert.DeserializeObject(valStr.ToLower(), type);
                            }
                            else if (type.IsEnum)
                            {
                                values[i] = Enum.Parse(type, valStr, true);
                            }
                            else
                            {
                                values[i] = JsonConvert.DeserializeObject(valStr, type);
                            }
                        }
                    }
                    i++;
                }
            }
            return(values);
        }
 /// <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);
 }
        /// <summary>
        /// Calculates the ResourceStringInformation based on the ODataRequest and maxProtocol version to determine if this i
        /// </summary>
        /// <param name="request">Request to calculate from</param>
        /// <param name="maxProtocolVersion">Max Protocol version of the server</param>
        /// <param name="expectedErrorMessage">Calculated Version Error information</param>
        /// <returns>boolean value of if a Error ResourceString Information was calculated or not</returns>
        public bool TryCalculateError(ODataRequest request, DataServiceProtocolVersion maxProtocolVersion, out ExpectedErrorMessage expectedErrorMessage)
        {
            ExceptionUtilities.CheckArgumentNotNull(request, "request");

            expectedErrorMessage = null;

            DataServiceProtocolVersion dataServiceVersion    = VersionHelper.ConvertToDataServiceProtocolVersion(request.GetHeaderValueIfExists(HttpHeaders.DataServiceVersion));
            DataServiceProtocolVersion maxDataServiceVersion = VersionHelper.ConvertToDataServiceProtocolVersion(request.GetHeaderValueIfExists(HttpHeaders.MaxDataServiceVersion));

            if (dataServiceVersion == DataServiceProtocolVersion.Unspecified)
            {
                dataServiceVersion = VersionHelper.CalculateDataServiceVersionIfNotSpecified(maxProtocolVersion, maxDataServiceVersion);
            }

            if (TryCalculateDataServiceVersionTooHighError(dataServiceVersion, maxProtocolVersion, out expectedErrorMessage))
            {
                return(true);
            }

            string contentType = request.GetHeaderValueIfExists(HttpHeaders.ContentType);
            string acceptType  = request.GetHeaderValueIfExists(HttpHeaders.Accept);

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

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

            // Do Uri processing first
            if (this.TryCalculateODataUriProcessingError(request, maxDataServiceVersion, maxProtocolVersion, acceptType, out expectedErrorMessage))
            {
                return(true);
            }

            // Now check metadata of what the request should look like, will it cause a version bump greater than the DSV
            EntitySet expectedEntitySet = null;

            if (request.Uri.TryGetExpectedEntitySet(out expectedEntitySet))
            {
                DataServiceProtocolVersion entitySetUriVersion = CalculateEntitySetUriSegmentRequestVersion(request, expectedEntitySet, maxProtocolVersion, maxDataServiceVersion, contentType);
                if (TryCalculateVersionError(entitySetUriVersion, dataServiceVersion, maxProtocolVersion, out expectedErrorMessage))
                {
                    return(true);
                }
            }

            // First ensure that the version is greater than mpv
            if (maxProtocolVersion < dataServiceVersion)
            {
                expectedErrorMessage = new ExpectedErrorMessage(DataServiceRequestVersionMustBeLessThanMPV, dataServiceVersion.ConvertToHeaderFormat(), maxProtocolVersion.ConvertToHeaderFormat());
                return(true);
            }

            // Now review the version of the payload and create the right errors
            if (this.TryCalculateODataResponseError(request, dataServiceVersion, maxDataServiceVersion, maxProtocolVersion, out expectedErrorMessage))
            {
                return(true);
            }

            // Now check for any errors from reading the payload
            return(TryCalculateReaderError(request, dataServiceVersion, this.StringResourceVerifiers, this.MetadataResolver, out expectedErrorMessage));
        }
Exemple #39
0
        public void QueryString_ParameterArray_RemoveWellKnownByName()
        {
            void ParamTest(string name, string value, string propertyName, object propertyValue)
            {
                var request = new ODataRequest(new ServerContext {
                    Url = "https://example.com"
                })
                {
                    Path = "/Root/MyContent", IsCollectionRequest = true
                };

                request.Parameters.Add(name, value);

                var requestAcc = new ObjectAccessor(request);
                var propValue  = requestAcc.GetProperty(propertyName);

                if (propertyValue is string[] stringArrayValue)
                {
                    propertyValue = string.Join(",", stringArrayValue);
                    propValue     = string.Join(",", (string[])propValue);
                }

                Assert.AreEqual(0, request.Parameters.Count);
                Assert.AreEqual(propertyValue, propValue);

                request.Parameters.Remove(name);

                propValue = requestAcc.GetProperty(propertyName);

                object defaultValue = null;

                if (propertyValue is bool)
                {
                    defaultValue = default(bool);
                }
                if (propertyValue is int)
                {
                    defaultValue = default(int);
                }
                if (propertyValue is InlineCountOptions)
                {
                    defaultValue = default(InlineCountOptions);
                }
                if (propertyValue is MetadataFormat)
                {
                    defaultValue = default(MetadataFormat);
                }
                if (propertyValue is FilterStatus)
                {
                    defaultValue = default(FilterStatus);
                }

                Assert.AreEqual(0, request.Parameters.Count);
                Assert.AreEqual(defaultValue, propValue);
            }

            ParamTest("$top", "5", "Top", 5);
            ParamTest("$skip", "10", "Skip", 10);
            ParamTest("$expand", "a, b ,c/d", "Expand", new[] { "a", "b", "c/d" });
            ParamTest("$select", "a, b ,c/d", "Select", new[] { "a", "b", "c/d" });
            ParamTest("$filter", "isof(Folder)", "ChildrenFilter", "isof(Folder)");
            ParamTest("$orderby", "A Desc,B", "OrderBy", new[] { "A Desc", "B" });
            ParamTest("$inlinecount", "allpages", "InlineCount", InlineCountOptions.AllPages);
            ////ParamTest("$format", "value");
            //ParamTest("$count", "true");
            ParamTest("metadata", "no", "Metadata", MetadataFormat.None);
            ParamTest("metadata", "minimal", "Metadata", MetadataFormat.Minimal);
            ParamTest("enableautofilters", "true", "AutoFilters", FilterStatus.Enabled);
            ParamTest("enableautofilters", "false", "AutoFilters", FilterStatus.Disabled);
            ParamTest("enablelifespanfilter", "true", "LifespanFilter", FilterStatus.Enabled);
            ParamTest("enablelifespanfilter", "false", "LifespanFilter", FilterStatus.Disabled);
            ParamTest("version", "V1.0.P", "Version", "V1.0.P");
            ParamTest("scenario", "Scenario1", "Scenario", "Scenario1");
            ParamTest("query", "+A:a +B:b .COUNTONLY", "ContentQuery", "+A:a +B:b .COUNTONLY");
            ParamTest("permissions", "Open, Approve", "Permissions", new[] { "Open", "Approve" });
            ParamTest("user", "/root///user1", "User", "/root///user1");
        }
        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);
        }
        private void VerifyStream(AstoriaQueryStreamValue streamValue, string contentType, ODataRequest request, ODataResponse response)
        {
            this.AssertAreEqual(contentType, streamValue.ContentType, "Stream content type did not match", request, response);

            var expectedPayload = (byte[])((PrimitiveValue)request.Body.RootElement).ClrValue;

            this.AssertAreEqual(expectedPayload, streamValue.Value, "Stream content did not match", request, response);
        }
Exemple #42
0
        private async Task WriteActionsAsync(ODataActionItem[] actionItems, HttpContext httpContext, ODataRequest req)
        {
            if (actionItems == null)
            {
                return;
            }

            var projector = Projector.Create(req, true);

            var enumerable = Content.CreateCollection(actionItems, ODataActionItem.Ctd);

            var filteredEnumerable = new FilteredContentEnumerable(enumerable,
                                                                   (LambdaExpression)req.Filter, req.Sort, req.Top, req.Skip);

            var contents = filteredEnumerable
                           .Select(x => CreateFieldDictionary(x, projector, httpContext))
                           .ToArray();

            var count = req.InlineCount == InlineCount.AllPages ? filteredEnumerable.AllCount : contents.Length;

            await WriteMultipleContentAsync(httpContext, contents, count).ConfigureAwait(false);
        }
        private void VerifyEntityOperations(ODataRequest request, ODataResponse response, List <EntityInstance> entities, bool isTopLevelElement)
        {
            foreach (var entity in entities.Where(et => !et.IsNull))
            {
                var instanceEntityType = this.model.EntityTypes.Single(et => et.FullName == entity.FullTypeName);

                // look at each action/function bound to the same type as entity, ensure it is advertised correctly
                int numberOfDescriptorsVerified = 0;
                var functionsWithBindings       = this.model.Functions.Where(f => f.Annotations.OfType <ServiceOperationAnnotation>().Any(s => s.BindingKind.IsBound())).ToArray();
                foreach (Function function in functionsWithBindings)
                {
                    ServiceOperationAnnotation serviceOperationAnnotation = function.Annotations.OfType <ServiceOperationAnnotation>().SingleOrDefault();
                    if (serviceOperationAnnotation != null)
                    {
                        DataType       bindingParameterDataType = function.Parameters[0].DataType;
                        EntityDataType bindingEntityDataType    = bindingParameterDataType as EntityDataType;
                        if (bindingEntityDataType == null)
                        {
                            ExceptionUtilities.Assert(bindingParameterDataType is CollectionDataType, "Unsupported binding parameter data type.");
                            ExceptionUtilities.Assert(entity.ServiceOperationDescriptors.SingleOrDefault(d => d.Title == function.Name) == null, "Unexpected feed-bound action advertised in entry.");
                            continue;
                        }

                        // Check the base type as well as derived types
                        bool beginServiceOpMatchMatching = bindingEntityDataType.Definition.Model.EntityTypes.Where(t => t.IsKindOf(bindingEntityDataType.Definition)).Where(a => a.FullName.Equals(entity.FullTypeName)).Any();

                        if (beginServiceOpMatchMatching)
                        {
                            // find ServiceOperationDescriptor that matches the function
                            ServiceOperationDescriptor descriptor = entity.ServiceOperationDescriptors.SingleOrDefault(d => ExtractSimpleActionName(d.Title) == function.Name);
                            bool expectActionDescriptor           = true;
                            if (request.Uri.SelectSegments.Count > 0)
                            {
                                expectActionDescriptor = this.ExpectActionWithProjection(request.Uri, function, isTopLevelElement);
                            }

                            if (descriptor == null)
                            {
                                ExceptionUtilities.Assert(!expectActionDescriptor, "Missing service operation descriptor for " + function.Name);
                            }
                            else
                            {
                                expectActionDescriptor = this.VerifyIfOpenType(function, bindingEntityDataType, expectActionDescriptor);

                                ExceptionUtilities.Assert(expectActionDescriptor, "Unexpected service operation descriptor for " + function.Name);

                                // JSONLight will always add the type segment if its a derived type now
                                string derivedTypeFullName = string.Empty;
                                var    acceptHeaderValue   = response.Headers[HttpHeaders.ContentType];
                                if (bindingEntityDataType.Definition.BaseType != null)
                                {
                                    derivedTypeFullName = string.Concat(bindingEntityDataType.Definition.FullName, "/");
                                }

                                if (bindingEntityDataType.Definition.FullName != instanceEntityType.FullName)
                                {
                                    derivedTypeFullName = string.Concat(instanceEntityType.FullName, "/");
                                }

                                // check if there is a possible name collision between a property and an action, if so add the Entity Container name to the expected result
                                string containerName = this.BuildExpectedContainerName(function, bindingEntityDataType, instanceEntityType);
                                string expectedMetadataContainerName = string.Concat(function.Model.GetDefaultEntityContainer().Name, ".");

                                // When running in JSONLight descriptor returns full name, not partial
                                if (IsJsonLightResponse(acceptHeaderValue))
                                {
                                    containerName = string.Concat(function.Model.GetDefaultEntityContainer().Name, ".");

                                    expectedMetadataContainerName = containerName;
                                }

                                string expectedTarget   = string.Concat(entity.Id.TrimEnd('/'), "/", derivedTypeFullName, containerName, function.Name);
                                string expectedMetadata = string.Concat(((ServiceRootSegment)request.Uri.RootSegment).Uri.AbsoluteUri.TrimEnd('/'), "/", Endpoints.Metadata, "#", expectedMetadataContainerName, function.Name);

                                this.Assert(descriptor.Target == expectedTarget, "Expected target " + expectedTarget + " Actual " + descriptor.Target, request, response);
                                this.Assert(descriptor.Metadata == expectedMetadata, "Expected Metadata " + expectedMetadata + " Actual " + descriptor.Metadata, request, response);

                                // verify IsAction flag
                                this.Assert(serviceOperationAnnotation.IsAction == descriptor.IsAction, "Expected action " + serviceOperationAnnotation.IsAction + " Actual " + descriptor.IsAction, request, response);
                                numberOfDescriptorsVerified++;
                            }
                        }
                    }
                }

                this.Assert(numberOfDescriptorsVerified == entity.ServiceOperationDescriptors.Count, "Wrong number of action/function.", request, response);
                this.VerifyExpandedEntityOperations(entity, request, response);
            }
        }
Exemple #44
0
        internal async Task WriteContentPropertyAsync(string path, string propertyName, bool rawValue, HttpContext httpContext, ODataRequest req, IConfiguration appConfig)
        {
            var content = ODataMiddleware.LoadContentByVersionRequest(path, httpContext);

            if (content == null)
            {
                ODataMiddleware.ContentNotFound(httpContext);
                return;
            }

            if (propertyName == ODataMiddleware.ActionsPropertyName)
            {
                var actionItems = ODataTools.GetActionItems(content, req, httpContext).ToArray();
                await WriteActionsAsync(actionItems, httpContext, req);

                return;
            }
            if (propertyName == ODataMiddleware.ChildrenPropertyName)
            {
                await WriteChildrenCollectionAsync(path, httpContext, req)
                .ConfigureAwait(false);
            }

            if (content.Fields.TryGetValue(propertyName, out var field))
            {
                if (field is ReferenceField refField)
                {
                    var refFieldSetting = refField.FieldSetting as ReferenceFieldSetting;
                    var isMultiRef      = true;
                    if (refFieldSetting != null)
                    {
                        isMultiRef = refFieldSetting.AllowMultiple == true;
                    }
                    if (isMultiRef)
                    {
                        await WriteMultiRefContentsAsync(refField.GetData(), httpContext, req)
                        .ConfigureAwait(false);
                    }
                    else
                    {
                        await WriteSingleRefContentAsync(refField.GetData(), httpContext)
                        .ConfigureAwait(false);
                    }
                }
                else if (field is AllowedChildTypesField actField)
                {
                    await WriteMultiRefContentsAsync(actField.GetData(), httpContext, req)
                    .ConfigureAwait(false);
                }
                else if (!rawValue)
                {
                    await WriteSingleContentAsync(httpContext, new ODataEntity { { propertyName, field.GetData() } })
                    .ConfigureAwait(false);
                }
                else
                {
                    await WriteRawAsync(field.GetData(), httpContext)
                    .ConfigureAwait(false);
                }
            }
            else
            {
                await WriteGetOperationResultAsync(httpContext, req, appConfig)
                .ConfigureAwait(false);
            }
        }
 /// <summary>
 /// Verifies that the actions/functions in the response are correct.
 /// </summary>
 /// <param name="request">The request that was sent</param>
 /// <param name="response">The response to verify</param>
 public override void Verify(ODataRequest request, ODataResponse response)
 {
     base.Verify(request, response);
     this.VerifyPayloadElementOperations(response.RootElement, request, response, true);
 }
Exemple #46
0
        private async Task WriteOperationResultAsync(object result, HttpContext httpContext, ODataRequest odataReq, int allCount)
        {
            if (result is Content content)
            {
                await WriteSingleContentAsync(content, httpContext)
                .ConfigureAwait(false);

                return;
            }

            if (result is IEnumerable <Content> enumerable)
            {
                await WriteMultiRefContentsAsync(enumerable, httpContext, odataReq)
                .ConfigureAwait(false);

                return;
            }

            await WriteOperationCustomResultAsync(httpContext, result,
                                                  odataReq.InlineCount == InlineCount.AllPages?allCount : (int?)null)
            .ConfigureAwait(false);
        }
Exemple #47
0
        private IEnumerable <ODataEntity> ProcessOperationQueryResponse(ChildrenDefinition qdef, ODataRequest req, HttpContext httpContext, out int count)
        {
            var queryText = qdef.ContentQuery;

            if (queryText.Contains("}}"))
            {
                queryText = ContentQuery.ResolveInnerQueries(qdef.ContentQuery, new QuerySettings
                {
                    EnableAutofilters    = qdef.EnableAutofilters,
                    EnableLifespanFilter = qdef.EnableLifespanFilter,
                    QueryExecutionMode   = qdef.QueryExecutionMode,
                    Sort = qdef.Sort
                });
            }

            var cdef = new ChildrenDefinition
            {
                PathUsage            = qdef.PathUsage,
                ContentQuery         = queryText,
                Top                  = req.Top > 0 ? req.Top : qdef.Top,
                Skip                 = req.Skip > 0 ? req.Skip : qdef.Skip,
                Sort                 = req.Sort != null && req.Sort.Any() ? req.Sort : qdef.Sort,
                CountAllPages        = req.HasInlineCount ? req.InlineCount == InlineCount.AllPages : qdef.CountAllPages,
                EnableAutofilters    = req.AutofiltersEnabled != FilterStatus.Default ? req.AutofiltersEnabled : qdef.EnableAutofilters,
                EnableLifespanFilter = req.LifespanFilterEnabled != FilterStatus.Default ? req.AutofiltersEnabled : qdef.EnableLifespanFilter,
                QueryExecutionMode   = req.QueryExecutionMode != QueryExecutionMode.Default ? req.QueryExecutionMode : qdef.QueryExecutionMode,
            };

            var snQuery = SnExpression.BuildQuery(req.Filter, typeof(Content), null, cdef);

            if (cdef.EnableAutofilters != FilterStatus.Default)
            {
                snQuery.EnableAutofilters = cdef.EnableAutofilters;
            }
            if (cdef.EnableLifespanFilter != FilterStatus.Default)
            {
                snQuery.EnableLifespanFilter = cdef.EnableLifespanFilter;
            }
            if (cdef.QueryExecutionMode != QueryExecutionMode.Default)
            {
                snQuery.QueryExecutionMode = cdef.QueryExecutionMode;
            }

            var result = snQuery.Execute(new SnQueryContext(null, User.Current.Id));

            // for optimization purposes this combined condition is examined separately
            if (req.InlineCount == InlineCount.AllPages && req.CountOnly)
            {
                count = result.TotalCount;
                return(null);
            }

            var ids = result.Hits.ToArray();

            count = req.InlineCount == InlineCount.AllPages ? result.TotalCount : ids.Length;
            if (req.CountOnly)
            {
                return(null);
            }

            var contents   = new List <ODataEntity>();
            var projector  = Projector.Create(req, true);
            var missingIds = new List <int>();

            foreach (var id in ids)
            {
                var content = Content.Load(id);
                if (content == null)
                {
                    // collect missing ids for logging purposes
                    missingIds.Add(id);
                    continue;
                }

                var fields = CreateFieldDictionary(content, projector, httpContext);
                contents.Add(fields);
            }

            if (missingIds.Count > 0)
            {
                // subtract missing count from result count
                count = Math.Max(0, count - missingIds.Count);

                // index anomaly: there are ids in the index that could not be loaded from the database
                SnLog.WriteWarning("Missing ids found in the index that could not be loaded from the database. See id list below.",
                                   EventId.Indexing,
                                   properties: new ODataEntity
                {
                    { "MissingIds", string.Join(", ", missingIds.OrderBy(id => id)) }
                });
            }

            return(contents);
        }
Exemple #48
0
        private IEnumerable <Content> ProcessODataFilters(IEnumerable <Content> inputContents, ODataRequest req, out int?totalCount)
        {
            var x = inputContents;

            if (req.HasFilter)
            {
                if (x is IQueryable <Content> y)
                {
                    x = y.Where((Expression <Func <Content, bool> >)req.Filter);
                }
                else
                {
                    var filter     = SnExpression.GetCaseInsensitiveFilter(req.Filter);
                    var lambdaExpr = (LambdaExpression)filter;
                    x = x.Where((Func <Content, bool>)lambdaExpr.Compile());
                }
            }
            if (req.HasSort)
            {
                x = AddSortToCollectionExpression(x, req.Sort);
            }

            if (req.InlineCount == InlineCount.AllPages)
            {
                x          = x.ToList();
                totalCount = ((IList)x).Count;
            }
            else
            {
                totalCount = null;
            }

            if (req.HasSkip)
            {
                x = x.Skip(req.Skip);
            }
            if (req.HasTop)
            {
                x = x.Take(req.Top);
            }

            return(x);
        }
Exemple #49
0
        /// <inheritdoc />
        protected override async Task WriteOperationCustomResultAsync(HttpContext httpContext, ODataRequest odataRequest, object result, int?allCount)
        {
            if (result is IEnumerable <ODataEntity> dictionaryList)
            {
                await WriteAsync(ODataMultipleContent.Create(dictionaryList, allCount ?? dictionaryList.Count()), httpContext, odataRequest)
                .ConfigureAwait(false);

                return;
            }
            if (result is IEnumerable <ODataObject> customContentList)
            {
                await WriteAsync(ODataMultipleContent.Create(customContentList, allCount ?? 0), httpContext, odataRequest)
                .ConfigureAwait(false);

                return;
            }

            await WriteAsync(result, httpContext, odataRequest)
            .ConfigureAwait(false);
        }
Exemple #50
0
        /// <inheritdoc />
        protected override async Task WriteServiceDocumentAsync(HttpContext httpContext, ODataRequest odataRequest, IEnumerable <string> names)
        {
            using (var writer = new StringWriter())
            {
                var x = new { d = new { EntitySets = names } };
                JsonSerializer.Create(new JsonSerializerSettings {
                    NullValueHandling = NullValueHandling.Ignore
                })
                .Serialize(writer, x);

                await WriteRawAsync(writer.GetStringBuilder().ToString(), httpContext, odataRequest);
            }
        }
 /// <summary>
 /// Verify that the navigation/relationship link value is consistent with expected value.
 /// </summary>
 /// <param name="link">The navigation/relationship link to verify.</param>
 /// <param name="expectedLinkValue">The expected link.</param>
 /// <param name="request">The request needed for error report.</param>
 /// <param name="response">The response needed for error report.</param>
 private void VerifyLinkUriValue(string link, string expectedLinkValue, ODataRequest request, ODataResponse response)
 {
     if (link == null ||
         !link.Equals(expectedLinkValue, StringComparison.Ordinal))
     {
         this.Logger.WriteLine(LogLevel.Error, CultureInfo.InvariantCulture, "Expected link {0}, observed {1}", expectedLinkValue, link == null ? "Link not Found." : link);
         this.ReportFailure(request, response);
         throw new ResponseVerificationException();
     }
 }
 public ClientWithResponse(ISession session, ODataRequest request, HttpResponseMessage responseMessage)
 {
     _session        = session;
     _request        = request;
     ResponseMessage = responseMessage;
 }
        /// <summary>
        /// Verify that the type attribute of relationship link is not null and has the expected value "application/xml".
        /// </summary>
        /// <param name="link">The navigation link to verify.</param>
        /// <param name="request">The request needed for error report.</param>
        /// <param name="response">The response needed for error report.</param>
        private void VerifyAtomAssociationLinkTypeAttribute(ODataPayloadElement link, ODataRequest request, ODataResponse response)
        {
            ContentTypeAnnotation typeAnnotation = link.Annotations.OfType<ContentTypeAnnotation>().SingleOrDefault();
            string expectedTypeAttributeValue = "application/xml";

            if (typeAnnotation == null || string.Compare(typeAnnotation.Value, expectedTypeAttributeValue, StringComparison.CurrentCulture) != 0)
            {
                this.Logger.WriteLine(LogLevel.Verbose, CultureInfo.InvariantCulture, "Expected relationship link type attribute '{0}', observed '{1}'", expectedTypeAttributeValue, typeAnnotation == null ? "null" : typeAnnotation.Value);
                this.ReportFailure(request, response);
                throw new ResponseVerificationException();
            }
        }
 /// <summary>
 /// Returns true if this is an action 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.Uri.IsAction());
 }
 /// <summary>
 /// Returns true for all requests rather than trying to guess which requests have responses with links in the payload
 /// </summary>
 /// <param name="request">The request that was issued</param>
 /// <returns>Whether or not this verifier applies to responses for this kind of request</returns>
 public bool Applies(ODataRequest request)
 {
     // filter based only on the response
     return true;
 }
Exemple #56
0
 internal void Initialize(ODataRequest odataRequest)
 {
     this.ODataRequest = odataRequest;
 }
        internal static bool TryGetExpectedType(ODataRequest request, out EntityType entityType)
        {
            EntitySet entitySet;
            if (request.Uri.TryGetExpectedEntitySet(out entitySet))
            {
                var possibleTypes = entitySet.Container.Model.EntityTypes.Where(t => t.IsKindOf(entitySet.EntityType)).ToList();
                if (possibleTypes.Count == 1)
                {
                    entityType = possibleTypes[0];
                    return true;
                }

                string typeName = null;
                if (entitySet.EntityType.HasStream())
                {
                    typeName = request.GetHeaderValueIfExists(HttpHeaders.MediaLinkEntryEntityTypeHint);
                }
                else if (request.Body != null && request.Body.RootElement.ElementType == ODataPayloadElementType.EntityInstance)
                {
                    typeName = ((EntityInstance)request.Body.RootElement).FullTypeName;
                }

                if (typeName != null)
                {
                    entityType = possibleTypes.SingleOrDefault(t => t.FullName == typeName);
                    return entityType != null;
                }
            }

            entityType = null;
            return false;
        }
Exemple #58
0
        internal async Task WriteChildrenCollectionAsync(String path, HttpContext httpContext, ODataRequest req)
        {
            var content = Content.Load(path);
            var chdef   = content.ChildrenDefinition;

            if (req.HasContentQuery)
            {
                chdef.ContentQuery = ContentQuery.AddClause(req.ContentQueryText, String.Concat("InTree:'", path, "'"), LogicalOperator.And);

                if (req.AutofiltersEnabled != FilterStatus.Default)
                {
                    chdef.EnableAutofilters = req.AutofiltersEnabled;
                }
                if (req.LifespanFilterEnabled != FilterStatus.Default)
                {
                    chdef.EnableLifespanFilter = req.LifespanFilterEnabled;
                }
                if (req.QueryExecutionMode != QueryExecutionMode.Default)
                {
                    chdef.QueryExecutionMode = req.QueryExecutionMode;
                }
                if (req.Top > 0)
                {
                    chdef.Top = req.Top;
                }
                if (req.Skip > 0)
                {
                    chdef.Skip = req.Skip;
                }
                if (req.Sort.Any())
                {
                    chdef.Sort = req.Sort;
                }
            }
            else
            {
                chdef.EnableAutofilters = FilterStatus.Disabled;
                if (string.IsNullOrEmpty(chdef.ContentQuery))
                {
                    chdef.ContentQuery = ContentQuery.AddClause(chdef.ContentQuery, String.Concat("InFolder:'", path, "'"), LogicalOperator.And);
                }
            }


            var contents = ProcessOperationQueryResponse(chdef, req, httpContext, out var count);

            if (req.CountOnly)
            {
                await WriteCountAsync(httpContext, count).ConfigureAwait(false);
            }
            else
            {
                await WriteMultipleContentAsync(httpContext, contents, count).ConfigureAwait(false);
            }
        }
        /// <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);
        }
Exemple #60
0
        private async Task WriteMultiRefContentsAsync(object references, HttpContext httpContext, ODataRequest req)
        {
            if (references == null)
            {
                return;
            }

            var node      = references as Node;
            var projector = Projector.Create(req, true);

            if (node != null)
            {
                var contents = new List <ODataEntity>
                {
                    CreateFieldDictionary(Content.Create(node), projector, httpContext)
                };
                //TODO: ODATA: multiref item: get available types from reference property
                await WriteMultipleContentAsync(httpContext, contents, 1)
                .ConfigureAwait(false);
            }
            else
            {
                if (references is IEnumerable enumerable)
                {
                    var filteredEnumerable = new FilteredContentEnumerable(enumerable,
                                                                           (LambdaExpression)req.Filter, req.Sort, req.Top, req.Skip);

                    var contents = filteredEnumerable
                                   .Select(x => CreateFieldDictionary(x, projector, httpContext))
                                   .ToArray();

                    var count = req.InlineCount == InlineCount.AllPages ? filteredEnumerable.AllCount : contents.Length;

                    await WriteMultipleContentAsync(httpContext, contents, count).ConfigureAwait(false);
                }
            }
        }