internal static ODataUri ConstructODataUriWithoutActionInformation(IEnumerable <EntitySet> entitySets, ODataUri uri) { var segmentsToInclude = new List <ODataUriSegment>(); ServiceOperationAnnotation serviceOperationAnnotation = null; string setName = null; foreach (var segment in uri.Segments) { var functionSegment = segment as FunctionSegment; if (functionSegment != null && functionSegment.Function.IsAction()) { serviceOperationAnnotation = functionSegment.Function.Annotations.OfType <ServiceOperationAnnotation>().Single(); var toggleBooleanAnnotation = functionSegment.Function.Annotations.OfType <ToggleBoolPropertyValueActionAnnotation>().SingleOrDefault(); if (toggleBooleanAnnotation != null) { setName = toggleBooleanAnnotation.SourceEntitySet; } break; } segmentsToInclude.Add(segment); } if (!serviceOperationAnnotation.BindingKind.IsBound()) { ExceptionUtilities.CheckObjectNotNull(setName, "Cannot find the set name that the action starts from"); var sourceEntitySet = entitySets.Single(es => es.Name == setName); segmentsToInclude.Add(new EntitySetSegment(sourceEntitySet)); } var actionlessODataUri = new ODataUri(segmentsToInclude); actionlessODataUri.CustomQueryOptions = uri.CustomQueryOptions; actionlessODataUri.Filter = uri.Filter; actionlessODataUri.InlineCount = uri.InlineCount; actionlessODataUri.OrderBy = uri.OrderBy; actionlessODataUri.Skip = uri.Skip; actionlessODataUri.SkipToken = uri.SkipToken; actionlessODataUri.Top = uri.Top; EntitySet expectedEntitySet = null; ExceptionUtilities.Assert(actionlessODataUri.TryGetExpectedEntitySet(out expectedEntitySet), "Expected entity set not found"); // expand all navigations for actionlessODataUri so that we do not need to send additional request when calculating expected action result foreach (NavigationProperty navigation in expectedEntitySet.EntityType.NavigationProperties) { actionlessODataUri.ExpandSegments.Add(new List <ODataUriSegment>() { new NavigationSegment(navigation) }); } return(actionlessODataUri); }
internal static DataServiceProtocolVersion CalculateUriMinRequestProtocolVersion(ODataUri requestUri, string contentType, DataServiceProtocolVersion maxProtocolVersion, DataServiceProtocolVersion maxDataServiceVersion) { DataServiceProtocolVersion expectedVersion = DataServiceProtocolVersion.V4; string inlineCount; if (requestUri.TryGetInlineCountValue(out inlineCount)) { expectedVersion = expectedVersion.IncreaseVersionIfRequired(DataServiceProtocolVersion.V4); } if (requestUri.SelectSegments.Count > 0) { expectedVersion = expectedVersion.IncreaseVersionIfRequired(DataServiceProtocolVersion.V4); } if (requestUri.HasAnyOrAllInFilter()) { expectedVersion = expectedVersion.IncreaseVersionIfRequired(DataServiceProtocolVersion.V4); } if (requestUri.IsCount()) { expectedVersion = expectedVersion.IncreaseVersionIfRequired(DataServiceProtocolVersion.V4); } else if (requestUri.IsProperty()) { var propertySegment = requestUri.LastSegment as PropertySegment; expectedVersion = expectedVersion.IncreaseVersionIfRequired(propertySegment.Property.CalculateProtocolVersion()); } else { EntitySet expectedEntitySet = null; if (requestUri.TryGetExpectedEntitySet(out expectedEntitySet)) { List <DataServiceProtocolVersion> dataServiceProtocolVersions = GetEntityTypes(expectedEntitySet).Select(et => et.CalculateEntityPropertyMappingProtocolVersion(VersionCalculationType.Request, contentType, maxProtocolVersion, maxDataServiceVersion)).ToList(); dataServiceProtocolVersions.Add(expectedVersion); expectedVersion = VersionHelper.GetMaximumVersion(dataServiceProtocolVersions.ToArray()); } } return(expectedVersion); }
internal static bool ResponseMightIncludeNextLink(ODataUri uri) { var payloadsWithNextLinks = new[] { ODataPayloadElementType.EntitySetInstance, ODataPayloadElementType.LinkCollection }; var expectedPayloadType = uri.GetExpectedPayloadType(); if (!payloadsWithNextLinks.Contains(expectedPayloadType)) { return(false); } EntitySet expectedSet; ExceptionUtilities.Assert(uri.TryGetExpectedEntitySet(out expectedSet), "Could not get expected entity set"); var pageSize = expectedSet.GetEffectivePageSize(); if (!pageSize.HasValue) { return(false); } // if the value of $top is less than 1 page size, no next link will be included return(!uri.Top.HasValue || uri.Top.Value > pageSize.Value); }
/// <summary> /// Returns the error expected for the Query /// </summary> /// <param name="expression">The expression.</param> /// <param name="usesClientQueryable">Whether the client uri is build from a Client Linq expression or not</param> /// <param name="clientMaxProtocolVersion">Client Max Protocol Version</param> /// <param name="maxProtocolVersion">Max Protocol version of the server</param> /// <returns> a versioning error at the Client level</returns> public ExpectedClientErrorBaseline CalculateExpectedClientVersionError(QueryExpression expression, bool usesClientQueryable, DataServiceProtocolVersion clientMaxProtocolVersion, DataServiceProtocolVersion maxProtocolVersion) { ExceptionUtilities.CheckArgumentNotNull(expression, "expression"); ExceptionUtilities.CheckAllRequiredDependencies(this); DataServiceProtocolVersion expectedDataServiceVersion = DataServiceProtocolVersion.Unspecified; DataServiceProtocolVersion minRequiredRequestVersion = DataServiceProtocolVersion.Unspecified; ExpectedErrorMessage errorInformation; // If the Client Queryable interface is not used to build the expression that is run then no error can happen at this stage // There can only be a protocol error from the server if there is no client error var serverExpression = this.ClientSideProjectionReplacer.ReplaceClientSideProjections(expression); ODataUri odataUri = this.QueryToODataUriConverter.ComputeUri(serverExpression); // Predict what the Client will generate for the data service version, if we are not using client.linq its unspecified otherwise calculate it if (usesClientQueryable) { ExpectedClientErrorBaseline expectedErrorIfTooLow; minRequiredRequestVersion = this.CalculateExpectedClientMinRequestVersion(odataUri, out expectedErrorIfTooLow); if (minRequiredRequestVersion > clientMaxProtocolVersion) { if (expectedErrorIfTooLow == null) { errorInformation = new ExpectedErrorMessage("Context_RequestVersionIsBiggerThanProtocolVersion", minRequiredRequestVersion.ConvertToHeaderFormat(), clientMaxProtocolVersion.ConvertToHeaderFormat()); expectedErrorIfTooLow = new ExpectedClientErrorBaseline(typeof(InvalidOperationException), false, errorInformation); } return(expectedErrorIfTooLow); } EntitySet expectedEntitySet = null; if (odataUri.TryGetExpectedEntitySet(out expectedEntitySet)) { DataServiceProtocolVersion entitySetVersion = expectedEntitySet.CalculateEntitySetProtocolVersion(MimeTypes.ApplicationAtomXml, VersionCalculationType.Request, maxProtocolVersion, clientMaxProtocolVersion); // Client will create a DSV based on the following pieces, metadata of the query based on sets it goes through, uri contructs (select, count, inlinecount, etc) // and headers (like DataServiceResponsePreference). In order to mimic this client behavior I will get the metadata version and a version // from the minrequestVersion and take the max. MinRequest deals with Uri and headers, metadata the metadata. expectedDataServiceVersion = VersionHelper.GetMaximumVersion(minRequiredRequestVersion, entitySetVersion); } } ODataRequest request = new ODataRequest(this.ODataUriToStringConverter) { Uri = odataUri, Verb = HttpVerb.Get, Headers = { { HttpHeaders.DataServiceVersion, expectedDataServiceVersion.ConvertToHeaderFormat() }, { HttpHeaders.MaxDataServiceVersion, clientMaxProtocolVersion.ConvertToHeaderFormat() }, { HttpHeaders.Accept, MimeTypes.ApplicationAtomXml } } }; if (this.ODataRequestVersionResourceErrorCalculator.TryCalculateError(request, maxProtocolVersion, out errorInformation)) { return(new ExpectedClientErrorBaseline(typeof(DSClient.DataServiceQueryException), true, errorInformation)); } return(null); }
/// <summary> /// Processes the given uri and produces an expected value according to the conventions of an OData server implementation /// </summary> /// <param name="uri">The uri to process</param> /// <param name="applySelectAndExpand">A value indicating whether or not $select and $expand should be applied to the query values</param> /// <param name="applyPaging">A value indicating whether server-driven paging should be applied to the query values</param> /// <returns>The value resulting from processing the uri</returns> public QueryValue Evaluate(ODataUri uri, bool applySelectAndExpand, bool applyPaging) { this.applyPagingInExpands = applyPaging; ExceptionUtilities.CheckArgumentNotNull(uri, "uri"); ExceptionUtilities.CheckCollectionNotEmpty(uri.Segments, "uri.Segments"); ExceptionUtilities.CheckAllRequiredDependencies(this); this.currentUri = uri; this.shouldLastSegmentBeSingleton = false; applyPaging &= uri.TryGetExpectedEntitySet(out this.expectedEntitySet); if (applyPaging) { // compute the expected page size for this request based on the existence of a universal page size or set-specific page size this.expectedPageSize = this.expectedEntitySet.GetEffectivePageSize(); } else { this.expectedPageSize = null; } var query = this.BuildQueryFromSegments(); ExceptionUtilities.CheckObjectNotNull(query, "Could not build query from uri '{0}'", uri); // we explicitly do not process expand or select here because they do not affect the number of rows returned or their ordering query = this.ProcessFilter(query); ExceptionUtilities.CheckObjectNotNull(query, "Could not build query from uri '{0}'", uri); if (uri.IsEntitySet()) { // TODO: skip-token processing should happen before ordering query = this.ProcessOrderBy(query); ExceptionUtilities.CheckObjectNotNull(query, "Could not build query from uri '{0}'", uri); query = this.ProcessSkipAndTop(query); ExceptionUtilities.CheckObjectNotNull(query, "Could not build query from uri '{0}'", uri); } // handle $count requests if (uri.IsCount()) { query = query.LongCount(); } // need to resolve types since we constructed new QueryExpression tree var resolvedQuery = this.QueryResolver.Resolve(query); var value = this.Evaluator.Evaluate(resolvedQuery); ExceptionUtilities.CheckObjectNotNull(value, "Could not evaluate query '{0}'", query); ExceptionUtilities.Assert(value.EvaluationError == null, "Query evaluation produced an error: {0}", value.EvaluationError); // post-process the result to defer properties that were not expanded if (applySelectAndExpand) { value = this.ApplySelectAndExpand(uri, value); } // fixup for key expressions that should return singletons if (this.shouldLastSegmentBeSingleton) { value = EnsureLastSegmentIsSingletonOrNull(value); } this.currentUri = null; this.expectedEntitySet = null; this.expectedPageSize = null; return(value); }