Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/1822 depends on atomicity group id v4 #1831

Merged
merged 1 commit into from
Aug 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
15 changes: 8 additions & 7 deletions src/Microsoft.OData.Core/Batch/ODataBatchReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,13 @@ protected virtual string GetCurrentGroupIdImplementation()
/// <returns>The batch reader state after the read.</returns>
protected abstract ODataBatchReaderState ReadAtChangesetEndImplementation();

/// <summary>
/// Validate the dependsOnIds.
/// </summary>
/// <param name="contentId">The request Id</param>
/// <param name="dependsOnIds">The dependsOn ids specifying current request's prerequisites.</param>
protected abstract void ValidateDependsOnIds(string contentId, IEnumerable<string> dependsOnIds);

/// <summary>
/// Instantiate an <see cref="ODataBatchOperationRequestMessage"/> instance.
/// </summary>
Expand Down Expand Up @@ -318,13 +325,7 @@ protected virtual string GetCurrentGroupIdImplementation()
{
if (dependsOnRequestIds != null && dependsOnIdsValidationRequired)
{
foreach (string id in dependsOnRequestIds)
{
if (!this.PayloadUriConverter.ContainsContentId(id))
{
throw new ODataException(Strings.ODataBatchReader_DependsOnIdNotFound(id, contentId));
}
}
ValidateDependsOnIds(contentId, dependsOnRequestIds);
}

Uri uri = ODataBatchUtils.CreateOperationRequestUri(
Expand Down
18 changes: 9 additions & 9 deletions src/Microsoft.OData.Core/Batch/ODataBatchWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public abstract class ODataBatchWriter : IODataStreamListener, IODataOutputInStr
private readonly ODataOutputContext outputContext;

/// <summary>The batch-specific URL converter that stores the content IDs found in a changeset and supports resolving cross-referencing URLs.</summary>
private readonly ODataBatchPayloadUriConverter payloadUriConverter;
internal readonly ODataBatchPayloadUriConverter payloadUriConverter;

/// <summary>The dependency injection container to get related services.</summary>
private readonly IServiceProvider container;
Expand Down Expand Up @@ -498,6 +498,13 @@ protected void SetState(BatchWriterState newState)
/// <returns>An enumerable consists of request ids.</returns>
protected abstract IEnumerable<string> GetDependsOnRequestIds(IEnumerable<string> dependsOnIds);

/// <summary>
/// Validate the dependsOnIds.
/// </summary>
/// <param name="contentId">The request Id</param>
/// <param name="dependsOnIds">The dependsOn ids specifying current request's prerequisites.</param>
protected abstract void ValidateDependsOnIds(string contentId, IEnumerable<string> dependsOnIds);

/// <summary>
/// Wrapper method to create an operation request message that can be used to write the operation content to, utilizing
/// private members <see cref="ODataBatchPayloadUriConverter"/> and <see cref="IServiceProvider"/>.
Expand All @@ -517,14 +524,7 @@ protected void SetState(BatchWriterState newState)

if (dependsOnIds != null)
{
// Validate explicit dependsOnIds cases.
foreach (string id in convertedDependsOnIds)
{
if (!this.payloadUriConverter.ContainsContentId(id))
{
throw new ODataException(Strings.ODataBatchReader_DependsOnIdNotFound(id, contentId));
}
}
ValidateDependsOnIds(contentId, convertedDependsOnIds);
}

// If dependsOnIds is not specified, use the <code>payloadUrlConverter</code>; otherwise use the dependOnIds converted
Expand Down
34 changes: 34 additions & 0 deletions src/Microsoft.OData.Core/JsonLight/ODataJsonLightBatchReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ internal sealed class ODataJsonLightBatchReader : ODataBatchReader
/// </summary>
private ODataJsonLightBatchPayloadItemPropertiesCache messagePropertiesCache = null;

/// <summary>
/// Collection for keeping track of unique atomic group ids and member request ids.
/// </summary>
private HashSet<string> requestIds = new HashSet<string>();

/// <summary>
Copy link
Member

@mikepizzo mikepizzo Jul 10, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you need dictionaries, it looks like you just check to see if the dictionary contains the key, not the relationship between the id and the atomicity group. Can you just use a list? #Resolved

Copy link
Contributor Author

@KenitoInc KenitoInc Jul 14, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have changed it to a HashSet. #Resolved

/// Constructor.
/// </summary>
Expand Down Expand Up @@ -124,6 +129,16 @@ protected override ODataBatchOperationRequestMessage CreateOperationRequestMessa
string atomicityGroupId = (string)this.messagePropertiesCache.GetPropertyValue(
ODataJsonLightBatchPayloadItemPropertiesCache.PropertyNameAtomicityGroup);

if (id != null)
{
this.requestIds.Add(id);
}
Copy link
Member

@mikepizzo mikepizzo Jul 10, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than do an explicit ContainsKey check before calling Add, why not just do:
if (id != null)
{
this.requestIdToAtomicGroupId.TryAdd(id, atomicityGroupId);
} #Resolved

Copy link
Contributor Author

@KenitoInc KenitoInc Jul 14, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed #Resolved

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should you use TryAdd(id) rather than Add(id) to avoid possible exceptions if the id already exists, or are we asserting that the id doesn't already exist? (same for below)


In reply to: 454179343 [](ancestors = 454179343)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Never mind; I see that you moved to HashSet, which is perfect.


In reply to: 467600534 [](ancestors = 467600534,454179343)


if (atomicityGroupId != null)
{
this.requestIds.Add(atomicityGroupId);
}

// dependsOn
// Flatten the dependsOn list by converting every groupId into request Ids, so that the caller
// can decide, at the earliest opportunity, whether the depending request can be invoked.
Expand Down Expand Up @@ -332,6 +347,25 @@ protected override ODataBatchOperationResponseMessage CreateOperationResponseMes
return responseMessage;
}

/// <summary>
/// Validate the dependsOnIds.
/// </summary>
/// <param name="dependsOnIds">The dependsOn ids specifying current request's prerequisites.</param>
protected override void ValidateDependsOnIds(string contentId, IEnumerable<string> dependsOnIds)
{
foreach (var id in dependsOnIds)
{
// Content-ID cannot be part of dependsOnIds. This is to avoid self referencing.
// The dependsOnId must be an existing request ID or atomicityGroup
if (id == contentId ||
(!this.requestIds.Contains(id) &&
!this.requestIds.Contains(id)))
{
throw new ODataException(Strings.ODataBatchReader_DependsOnIdNotFound(id, contentId));
}
}
}

/// <summary>
/// Validate that the property value is not null.
/// </summary>
Expand Down
17 changes: 17 additions & 0 deletions src/Microsoft.OData.Core/JsonLight/ODataJsonLightBatchWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,23 @@ protected override IEnumerable<string> GetDependsOnRequestIds(IEnumerable<string
return dependsOnRequestIds;
}

/// <summary>
/// Validate the dependsOnIds.
/// </summary>
/// <param name="dependsOnIds">The dependsOn ids specifying current request's prerequisites.</param>
protected override void ValidateDependsOnIds(string contentId, IEnumerable<string> dependsOnIds)
{
foreach(var id in dependsOnIds)
{
// Content-ID cannot be part of dependsOnIds. This is to avoid self referencing.
// The dependsOnId must be an existing request ID.
if (id == contentId || !this.requestIdToAtomicGroupId.ContainsKey(id))
{
throw new ODataException(Strings.ODataBatchReader_DependsOnIdNotFound(id, contentId));
}
}
}

/// <summary>
/// Ends a batch - implementation of the actual functionality.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,22 @@ protected override ODataBatchReaderState ReadAtChangesetEndImplementation()
return this.SkipToNextPartAndReadHeaders();
}

/// <summary>
/// Validate if the dependsOnIds are in the ContentIdCache.
/// </summary>
/// <param name="dependsOnIds">The dependsOn ids specifying current request's prerequisites.</param>
protected override void ValidateDependsOnIds(string contentId, IEnumerable<string> dependsOnIds)
{
// Validate explicit dependsOnIds cases.
foreach (string id in dependsOnIds)
{
if (!this.PayloadUriConverter.ContainsContentId(id))
{
throw new ODataException(Strings.ODataBatchReader_DependsOnIdNotFound(id, contentId));
}
}
}

/// <summary>
/// Returns the next state of the batch reader after an end boundary has been found.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,22 @@ protected override IEnumerable<string> GetDependsOnRequestIds(IEnumerable<string
return dependsOnIds ?? this.dependsOnIdsTracker.GetDependsOnIds();
}

/// <summary>
/// Validate if the dependsOnIds are in the ContentIdCache.
/// </summary>
/// <param name="dependsOnIds">The dependsOn ids specifying current request's prerequisites.</param>
protected override void ValidateDependsOnIds(string contentId, IEnumerable<string> dependsOnIds)
{
// Validate explicit dependsOnIds cases.
foreach (string id in dependsOnIds)
{
if (!this.payloadUriConverter.ContainsContentId(id))
{
throw new ODataException(Strings.ODataBatchReader_DependsOnIdNotFound(id, contentId));
}
}
}

/// <summary>
/// Creates an <see cref="ODataBatchOperationRequestMessage"/> for writing an operation of a batch request
/// - implementation of the actual functionality.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1023,7 +1023,12 @@ public void BatchJsonLightSelfReferenceUriTest()
Assert.True(expectedExceptionThrown, "Uri self-referencing with its Content-ID should not be allowed.");
}

[Fact]
// This test isn't relevant in Json Batch v4.
// We use atomicGroup Id in dependsOn for 2 reasons:
// 1. To extract Request ids for use in reference Uris (in v4.01).
// 2. To sequence requests so that they are executed in a certain order (in both v4 and v4.01).
// This test made an assumption that we only use atomicityGroup for 1 above.
/*[Fact]
public void BatchJsonLightReferenceUriV4TestShouldThrow()
{
bool exceptionThrown = true;
Expand All @@ -1040,6 +1045,16 @@ public void BatchJsonLightReferenceUriV4TestShouldThrow()

Assert.True(exceptionThrown, "An exception should have been thrown when trying to refer to the " +
"content Id of the last request of a change set or atomic group in V4.");
}*/

[Fact]
public void BatchJsonLightDependsOnAtomicityGroupV4Test()
{
byte[] requestPayload = this.CreateReferenceUriBatchRequest(ODataVersion.V4);
VerifyPayload(requestPayload, ExpectedReferenceUriRequestPayload);

byte[] responsePayload = this.ServiceReadReferenceUriBatchRequestAndWriteResponse(requestPayload);
VerifyPayload(responsePayload, ExpectedReferenceUriResponsePayload);
}

[Fact]
Expand Down Expand Up @@ -1643,7 +1658,7 @@ private byte[] CreateCreateReferenceUriBatchRequestUseDifferentBaseUri(ODataVers
}
else if (useRequestIdOfGroupForDependsOnIds)
{
dependsOnIds = new string[] {"1", "2"};
dependsOnIds = new string[] { "1", "2" };
}
else
{
Expand Down