/// <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); }
private bool TryCalculateODataResponseError(ODataRequest request, DataServiceProtocolVersion dataServiceVersion, DataServiceProtocolVersion maxDataServiceVersion, DataServiceProtocolVersion maxProtocolVersion, out ExpectedErrorMessage expectedErrorMessage) { expectedErrorMessage = null; string preferHeader = request.GetHeaderValueIfExists(HttpHeaders.Prefer); if (preferHeader != null && maxProtocolVersion > DataServiceProtocolVersion.V4 && dataServiceVersion > DataServiceProtocolVersion.V4 && maxDataServiceVersion != DataServiceProtocolVersion.Unspecified && maxDataServiceVersion < DataServiceProtocolVersion.V4 && request.Verb != HttpVerb.Delete) { expectedErrorMessage = new ExpectedErrorMessage(MaxDataServiceVersionTooLow, maxDataServiceVersion.ConvertToHeaderFormat(), DataServiceProtocolVersion.V4.ConvertToIntegerFormat(), "0"); return(true); } // Now do payload processing DataServiceProtocolVersion expectedResponseVersion = this.MinimumVersionRequiredCalculator.CalculateMinResponseVersion(request, maxProtocolVersion); // Now ensure its not greater than than MDSV if (maxDataServiceVersion != DataServiceProtocolVersion.Unspecified && expectedResponseVersion > maxDataServiceVersion) { expectedErrorMessage = new ExpectedErrorMessage(MaxDataServiceVersionTooLow, maxDataServiceVersion.ConvertToHeaderFormat(), expectedResponseVersion.ConvertToIntegerFormat(), "0"); return(true); } return(false); }
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); }
/// <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 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); }
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; }
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> /// 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)); }
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); }