Skip to content

Commit

Permalink
Merge pull request #1 from apiaskowski/bugfix/issue-2656/batch-reques…
Browse files Browse the repository at this point in the history
…t-handling-of-duplicate-http-attributes

Bugfix/issue 2656/batch request handling of duplicate http attributes
  • Loading branch information
apiaskowski authored Jul 18, 2023
2 parents 399df92 + 6271298 commit 5bcca3c
Show file tree
Hide file tree
Showing 15 changed files with 476 additions and 36 deletions.
10 changes: 10 additions & 0 deletions src/Microsoft.OData.Client/PublicAPI/net45/PublicAPI.Shipped.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
Microsoft.OData.Client.DataServiceClientResponsePipelineConfiguration.OnDeltaFeedEnded(System.Action<Microsoft.OData.Client.ReadingDeltaFeedArgs> action) -> Microsoft.OData.Client.DataServiceClientResponsePipelineConfiguration
Microsoft.OData.Client.DataServiceClientResponsePipelineConfiguration.OnDeltaFeedStarted(System.Action<Microsoft.OData.Client.ReadingDeltaFeedArgs> action) -> Microsoft.OData.Client.DataServiceClientResponsePipelineConfiguration
Microsoft.OData.Client.ReadingDeltaFeedArgs
Microsoft.OData.Client.ReadingDeltaFeedArgs.DeltaFeed.get -> Microsoft.OData.ODataDeltaResourceSet
Microsoft.OData.Client.ReadingDeltaFeedArgs.ReadingDeltaFeedArgs(Microsoft.OData.ODataDeltaResourceSet deltaFeed) -> void
Microsoft.OData.Client.SaveChangesOptions.BulkUpdate = 128 -> Microsoft.OData.Client.SaveChangesOptions
Microsoft.OData.Client.SendingRequest2EventArgs.IsBulkUpdate.get -> bool
virtual Microsoft.OData.Client.DataServiceContext.BulkUpdate<T>(params T[] objects) -> Microsoft.OData.Client.DataServiceResponse
virtual Microsoft.OData.Client.DataServiceContext.BulkUpdateAsync<T>(params T[] objects) -> System.Threading.Tasks.Task<Microsoft.OData.Client.DataServiceResponse>
virtual Microsoft.OData.Client.DataServiceContext.BulkUpdateAsync<T>(System.Threading.CancellationToken cancellationToken, params T[] objects) -> System.Threading.Tasks.Task<Microsoft.OData.Client.DataServiceResponse>
virtual Microsoft.OData.Client.DataServiceContext.AutoNullPropagation.get -> bool
virtual Microsoft.OData.Client.DataServiceContext.AutoNullPropagation.set -> void
abstract Microsoft.OData.Client.ALinq.UriParser.PathSegmentToken.Accept(Microsoft.OData.Client.ALinq.UriParser.IPathSegmentTokenVisitor visitor) -> void
Expand Down
10 changes: 0 additions & 10 deletions src/Microsoft.OData.Client/PublicAPI/net45/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1,10 +0,0 @@
Microsoft.OData.Client.DataServiceClientResponsePipelineConfiguration.OnDeltaFeedEnded(System.Action<Microsoft.OData.Client.ReadingDeltaFeedArgs> action) -> Microsoft.OData.Client.DataServiceClientResponsePipelineConfiguration
Microsoft.OData.Client.DataServiceClientResponsePipelineConfiguration.OnDeltaFeedStarted(System.Action<Microsoft.OData.Client.ReadingDeltaFeedArgs> action) -> Microsoft.OData.Client.DataServiceClientResponsePipelineConfiguration
Microsoft.OData.Client.ReadingDeltaFeedArgs
Microsoft.OData.Client.ReadingDeltaFeedArgs.DeltaFeed.get -> Microsoft.OData.ODataDeltaResourceSet
Microsoft.OData.Client.ReadingDeltaFeedArgs.ReadingDeltaFeedArgs(Microsoft.OData.ODataDeltaResourceSet deltaFeed) -> void
Microsoft.OData.Client.SaveChangesOptions.BulkUpdate = 128 -> Microsoft.OData.Client.SaveChangesOptions
Microsoft.OData.Client.SendingRequest2EventArgs.IsBulkUpdate.get -> bool
virtual Microsoft.OData.Client.DataServiceContext.BulkUpdate<T>(params T[] objects) -> Microsoft.OData.Client.DataServiceResponse
virtual Microsoft.OData.Client.DataServiceContext.BulkUpdateAsync<T>(params T[] objects) -> System.Threading.Tasks.Task<Microsoft.OData.Client.DataServiceResponse>
virtual Microsoft.OData.Client.DataServiceContext.BulkUpdateAsync<T>(System.Threading.CancellationToken cancellationToken, params T[] objects) -> System.Threading.Tasks.Task<Microsoft.OData.Client.DataServiceResponse>
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
Microsoft.OData.Client.DataServiceClientResponsePipelineConfiguration.OnDeltaFeedEnded(System.Action<Microsoft.OData.Client.ReadingDeltaFeedArgs> action) -> Microsoft.OData.Client.DataServiceClientResponsePipelineConfiguration
Microsoft.OData.Client.DataServiceClientResponsePipelineConfiguration.OnDeltaFeedStarted(System.Action<Microsoft.OData.Client.ReadingDeltaFeedArgs> action) -> Microsoft.OData.Client.DataServiceClientResponsePipelineConfiguration
Microsoft.OData.Client.ReadingDeltaFeedArgs
Microsoft.OData.Client.ReadingDeltaFeedArgs.DeltaFeed.get -> Microsoft.OData.ODataDeltaResourceSet
Microsoft.OData.Client.ReadingDeltaFeedArgs.ReadingDeltaFeedArgs(Microsoft.OData.ODataDeltaResourceSet deltaFeed) -> void
Microsoft.OData.Client.SaveChangesOptions.BulkUpdate = 128 -> Microsoft.OData.Client.SaveChangesOptions
Microsoft.OData.Client.SendingRequest2EventArgs.IsBulkUpdate.get -> bool
virtual Microsoft.OData.Client.DataServiceContext.BulkUpdate<T>(params T[] objects) -> Microsoft.OData.Client.DataServiceResponse
virtual Microsoft.OData.Client.DataServiceContext.BulkUpdateAsync<T>(params T[] objects) -> System.Threading.Tasks.Task<Microsoft.OData.Client.DataServiceResponse>
virtual Microsoft.OData.Client.DataServiceContext.BulkUpdateAsync<T>(System.Threading.CancellationToken cancellationToken, params T[] objects) -> System.Threading.Tasks.Task<Microsoft.OData.Client.DataServiceResponse>
virtual Microsoft.OData.Client.DataServiceContext.AutoNullPropagation.get -> bool
virtual Microsoft.OData.Client.DataServiceContext.AutoNullPropagation.set -> void
abstract Microsoft.OData.Client.ALinq.UriParser.PathSegmentToken.Accept(Microsoft.OData.Client.ALinq.UriParser.IPathSegmentTokenVisitor visitor) -> void
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +0,0 @@
Microsoft.OData.Client.DataServiceClientResponsePipelineConfiguration.OnDeltaFeedEnded(System.Action<Microsoft.OData.Client.ReadingDeltaFeedArgs> action) -> Microsoft.OData.Client.DataServiceClientResponsePipelineConfiguration
Microsoft.OData.Client.DataServiceClientResponsePipelineConfiguration.OnDeltaFeedStarted(System.Action<Microsoft.OData.Client.ReadingDeltaFeedArgs> action) -> Microsoft.OData.Client.DataServiceClientResponsePipelineConfiguration
Microsoft.OData.Client.ReadingDeltaFeedArgs
Microsoft.OData.Client.ReadingDeltaFeedArgs.DeltaFeed.get -> Microsoft.OData.ODataDeltaResourceSet
Microsoft.OData.Client.ReadingDeltaFeedArgs.ReadingDeltaFeedArgs(Microsoft.OData.ODataDeltaResourceSet deltaFeed) -> void
Microsoft.OData.Client.SaveChangesOptions.BulkUpdate = 128 -> Microsoft.OData.Client.SaveChangesOptions
Microsoft.OData.Client.SendingRequest2EventArgs.IsBulkUpdate.get -> bool
virtual Microsoft.OData.Client.DataServiceContext.BulkUpdate<T>(params T[] objects) -> Microsoft.OData.Client.DataServiceResponse
virtual Microsoft.OData.Client.DataServiceContext.BulkUpdateAsync<T>(params T[] objects) -> System.Threading.Tasks.Task<Microsoft.OData.Client.DataServiceResponse>
virtual Microsoft.OData.Client.DataServiceContext.BulkUpdateAsync<T>(System.Threading.CancellationToken cancellationToken, params T[] objects) -> System.Threading.Tasks.Task<Microsoft.OData.Client.DataServiceResponse>
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,12 @@ private void ScanJsonProperties()
// Convert to upper case to support case-insensitive request property names
string propertyName = Normalize(this.jsonReader.ReadPropertyName());

// Throw an ODataException, if a duplicate json property was detected
if (jsonProperties.ContainsKey(propertyName))
{
throw new ODataException(Strings.ODataJsonLightBatchPayloadItemPropertiesCache_DuplicatePropertyForRequestInBatch(propertyName));
}

switch (propertyName)
{
case PropertyNameId:
Expand Down Expand Up @@ -269,6 +275,12 @@ private void ScanJsonProperties()
string headerName = this.jsonReader.ReadPropertyName();
string headerValue = this.jsonReader.ReadPrimitiveValue().ToString();

// Throw an ODataException, if a duplicate header was detected
if (headers.ContainsKeyOrdinal(headerName))
{
throw new ODataException(Strings.ODataJsonLightBatchPayloadItemPropertiesCache_DuplicateHeaderForRequestInBatch(headerName));
}

// Remember the Content-Type header value.
if (headerName.Equals(ODataConstants.ContentTypeHeader, StringComparison.OrdinalIgnoreCase))
{
Expand Down Expand Up @@ -359,6 +371,12 @@ await this.asynchronousJsonReader.ReadStartObjectAsync()
// Convert to upper case to support case-insensitive request property names
string propertyName = Normalize(await this.asynchronousJsonReader.ReadPropertyNameAsync().ConfigureAwait(false));

// Throw an ODataException, if a duplicate json property was detected
if (jsonProperties.ContainsKey(propertyName))
{
throw new ODataException(Strings.ODataJsonLightBatchPayloadItemPropertiesCache_DuplicatePropertyForRequestInBatch(propertyName));
}

switch (propertyName)
{
case PropertyNameId:
Expand Down Expand Up @@ -409,6 +427,12 @@ await this.asynchronousJsonReader.ReadStartObjectAsync()
.ConfigureAwait(false);
string headerValue = (await this.asynchronousJsonReader.ReadPrimitiveValueAsync().ConfigureAwait(false)).ToString();

// Throw an ODataException, if a duplicate header was detected
if (headers.ContainsKeyOrdinal(headerName))
{
throw new ODataException(Strings.ODataJsonLightBatchPayloadItemPropertiesCache_DuplicateHeaderForRequestInBatch(headerName));
}

// Remember the Content-Type header value.
if (headerName.Equals(ODataConstants.ContentTypeHeader, StringComparison.OrdinalIgnoreCase))
{
Expand Down
2 changes: 2 additions & 0 deletions src/Microsoft.OData.Core/Microsoft.OData.Core.cs
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,8 @@ internal sealed class TextRes {
internal const string ODataBatchReaderStream_UnexpectedEndOfInput = "ODataBatchReaderStream_UnexpectedEndOfInput";
internal const string ODataBatchReaderStreamBuffer_BoundaryLineSecurityLimitReached = "ODataBatchReaderStreamBuffer_BoundaryLineSecurityLimitReached";
internal const string ODataJsonLightBatchPayloadItemPropertiesCache_UnknownPropertyForMessageInBatch = "ODataJsonLightBatchPayloadItemPropertiesCache_UnknownPropertyForMessageInBatch";
internal const string ODataJsonLightBatchPayloadItemPropertiesCache_DuplicatePropertyForRequestInBatch = "ODataJsonLightBatchPayloadItemPropertiesCache_DuplicatePropertyForRequestInBatch";
internal const string ODataJsonLightBatchPayloadItemPropertiesCache_DuplicateHeaderForRequestInBatch = "ODataJsonLightBatchPayloadItemPropertiesCache_DuplicateHeaderForRequestInBatch";
internal const string ODataJsonLightBatchBodyContentReaderStream_UnexpectedNodeType = "ODataJsonLightBatchBodyContentReaderStream_UnexpectedNodeType";
internal const string ODataJsonLightBatchBodyContentReaderStream_UnsupportedContentTypeInHeader = "ODataJsonLightBatchBodyContentReaderStream_UnsupportedContentTypeInHeader";
internal const string ODataAsyncWriter_CannotCreateResponseWhenNotWritingResponse = "ODataAsyncWriter_CannotCreateResponseWhenNotWritingResponse";
Expand Down
2 changes: 2 additions & 0 deletions src/Microsoft.OData.Core/Microsoft.OData.Core.txt
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,8 @@ ODataBatchReaderStream_UnexpectedEndOfInput=Encountered an unexpected end of inp
ODataBatchReaderStreamBuffer_BoundaryLineSecurityLimitReached=Too many white spaces after a boundary delimiter and before the terminating line resource set. For security reasons, the total number of characters for a boundary including white spaces must not exceed {0}.

ODataJsonLightBatchPayloadItemPropertiesCache_UnknownPropertyForMessageInBatch=Unknown property name '{0}' for message in batch.
ODataJsonLightBatchPayloadItemPropertiesCache_DuplicatePropertyForRequestInBatch=Duplicate property name '{0}' for request in batch.
ODataJsonLightBatchPayloadItemPropertiesCache_DuplicateHeaderForRequestInBatch=Duplicate header name '{0}' for request in batch.

ODataJsonLightBatchBodyContentReaderStream_UnexpectedNodeType=Unexpected reader.NodeType: {0}.
ODataJsonLightBatchBodyContentReaderStream_UnsupportedContentTypeInHeader=Unknown/undefined type, new type that needs to be supported: {0}?
Expand Down
18 changes: 13 additions & 5 deletions src/Microsoft.OData.Core/ODataWriterCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2932,7 +2932,7 @@ private void PromoteNestedResourceInfoScope(ODataItem content)
NestedResourceInfoScope newScope = previousScope.Clone(WriterState.NestedResourceInfoWithContent);

this.scopeStack.Push(newScope);
if (newScope.ItemType == null && content != null && !SkipWriting && !(content is ODataPrimitiveValue))
if ((newScope.ItemType == null || newScope.ItemType.IsUntyped()) && content != null && !SkipWriting && !(content is ODataPrimitiveValue))
{
ODataPrimitiveValue primitiveValue = content as ODataPrimitiveValue;
if (primitiveValue != null)
Expand All @@ -2941,10 +2941,18 @@ private void PromoteNestedResourceInfoScope(ODataItem content)
}
else
{
ODataResourceBase resource = content as ODataResourceBase;
newScope.ResourceType = resource != null
? GetResourceType(resource)
: GetResourceSetType(content as ODataResourceSetBase);
if (content is ODataResourceBase resource)
{
newScope.ResourceType = string.IsNullOrEmpty(resource.TypeName) ?
EdmUntypedStructuredType.Instance :
GetResourceType(resource);
}
else if (content is ODataResourceSetBase resourceSet)
{
newScope.ResourceType = string.IsNullOrEmpty(resourceSet.TypeName) ?
EdmUntypedStructuredType.Instance :
GetResourceSetType(resourceSet);
}
}
}
}
Expand Down
16 changes: 16 additions & 0 deletions src/Microsoft.OData.Core/Parameterized.Microsoft.OData.Core.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1655,6 +1655,22 @@ internal static string ODataJsonLightBatchPayloadItemPropertiesCache_UnknownProp
return Microsoft.OData.TextRes.GetString(Microsoft.OData.TextRes.ODataJsonLightBatchPayloadItemPropertiesCache_UnknownPropertyForMessageInBatch, p0);
}

/// <summary>
/// A string like "Duplicate property name '{0}' for request in batch."
/// </summary>
internal static string ODataJsonLightBatchPayloadItemPropertiesCache_DuplicatePropertyForRequestInBatch(object p0)
{
return Microsoft.OData.TextRes.GetString(Microsoft.OData.TextRes.ODataJsonLightBatchPayloadItemPropertiesCache_DuplicatePropertyForRequestInBatch, p0);
}

/// <summary>
/// A string like "Duplicate header name '{0}' for request in batch."
/// </summary>
internal static string ODataJsonLightBatchPayloadItemPropertiesCache_DuplicateHeaderForRequestInBatch(object p0)
{
return Microsoft.OData.TextRes.GetString(Microsoft.OData.TextRes.ODataJsonLightBatchPayloadItemPropertiesCache_DuplicateHeaderForRequestInBatch, p0);
}

/// <summary>
/// A string like "Unexpected reader.NodeType: {0}."
/// </summary>
Expand Down
5 changes: 4 additions & 1 deletion src/Microsoft.OData.Core/TypeNameOracle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,10 @@ private static IEdmTypeReference ResolveTypeFromMetadataAndValue(IEdmTypeReferen
return typeReferenceFromMetadata;
}

Debug.Assert(typeReferenceFromValue.TypeKind() == typeReferenceFromMetadata.TypeKind(), "typeReferenceFromValue.TypeKind() == typeReferenceFromMetadata.TypeKind()");
if (!typeReferenceFromMetadata.IsUntyped())
{
Debug.Assert(typeReferenceFromValue.TypeKind() == typeReferenceFromMetadata.TypeKind(), "typeReferenceFromValue.TypeKind() == typeReferenceFromMetadata.TypeKind()");
}

writerValidator.ValidateTypeReference(typeReferenceFromMetadata, typeReferenceFromValue);

Expand Down
7 changes: 7 additions & 0 deletions src/Microsoft.OData.Core/ValidationUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,13 @@ internal static void ValidateIsExpectedPrimitiveType(object value, IEdmPrimitive
}

Debug.Assert(valuePrimitiveTypeReference.IsEquivalentTo(EdmLibraryExtensions.GetPrimitiveTypeReference(value.GetType())), "The value and valuePrimitiveTypeReference don't match.");

// If the expected type is 'Edm.Untyped', we don't need to verify the value type.
if (expectedTypeReference.IsUntyped())
{
return;
}

if (!expectedTypeReference.IsODataPrimitiveTypeKind() && !expectedTypeReference.IsODataTypeDefinitionTypeKind())
{
// non-primitive type found for primitive value.
Expand Down
23 changes: 15 additions & 8 deletions src/Microsoft.OData.Core/WriterValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -151,8 +151,12 @@ public virtual void ValidateTypeReference(IEdmTypeReference typeReferenceFromMet
{
if (settings.ThrowIfTypeConflictsWithMetadata)
{
if (typeReferenceFromMetadata.IsUntyped())
{
; // do nothing here
}
// Make sure the types are the same
if (typeReferenceFromValue.IsODataPrimitiveTypeKind())
else if (typeReferenceFromValue.IsODataPrimitiveTypeKind())
{
// Primitive types must match exactly except for nullability
ValidationUtils.ValidateMetadataPrimitiveType(typeReferenceFromMetadata,
Expand All @@ -172,14 +176,17 @@ public virtual void ValidateTypeReference(IEdmTypeReference typeReferenceFromMet
}
else if (typeReferenceFromMetadata.IsCollection())
{
// Collection types must match exactly.
if (!typeReferenceFromMetadata.Definition.IsElementTypeEquivalentTo(
typeReferenceFromValue.Definition))
if (!typeReferenceFromMetadata.AsCollection().ElementType().IsUntyped())
{
throw new ODataException(
Strings.ValidationUtils_IncompatibleType(
typeReferenceFromValue.FullName(),
typeReferenceFromMetadata.FullName()));
// Collection types must match exactly.
if (!typeReferenceFromMetadata.Definition.IsElementTypeEquivalentTo(
typeReferenceFromValue.Definition))
{
throw new ODataException(
Strings.ValidationUtils_IncompatibleType(
typeReferenceFromValue.FullName(),
typeReferenceFromMetadata.FullName()));
}
}
}
else
Expand Down
Loading

0 comments on commit 5bcca3c

Please sign in to comment.