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

Patch : Refactors the TrySerializeValueParameter method for PatchOperation and makes it public #2398

Merged
merged 5 commits into from
Apr 27, 2021
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions Microsoft.Azure.Cosmos/src/Patch/PatchOperation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Microsoft.Azure.Cosmos
{
using System;
using System.IO;
using Newtonsoft.Json;

/// <summary>
Expand All @@ -29,9 +30,16 @@ abstract class PatchOperation
[JsonProperty(PropertyName = PatchConstants.PropertyNames.Path)]
public abstract string Path { get; }

internal virtual bool TrySerializeValueParameter(
/// <summary>
/// Serializes the value parameter, if specified for the PatchOperation.
/// </summary>
/// <param name="cosmosSerializer">Serializer to be used.</param>
/// <param name="valueParam">Outputs the serialized stream if value parameter is specified, null otherwise.</param>
/// <returns>True if value is serialized, false otherwise.</returns>
/// <remarks>Output stream should be disposed after use.</remarks>
public virtual bool TrySerializeValueParameter(
CosmosSerializer cosmosSerializer,
out string valueParam)
out Stream valueParam)
{
valueParam = null;
return false;
Expand Down
20 changes: 11 additions & 9 deletions Microsoft.Azure.Cosmos/src/Patch/PatchOperationCore{T}.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,20 +33,22 @@ public PatchOperationCore(

public override string Path { get; }

internal override bool TrySerializeValueParameter(
public override bool TrySerializeValueParameter(
CosmosSerializer cosmosSerializer,
out string valueParam)
out Stream valueParam)
{
// Use the user serializer so custom conversions are correctly handled
using (Stream stream = cosmosSerializer.ToStream(this.Value))
// If value is of type Stream, do not serialize
if (typeof(Stream).IsAssignableFrom(typeof(T)))
{
using (StreamReader streamReader = new StreamReader(stream))
{
valueParam = streamReader.ReadToEnd();
}
valueParam = (Stream)(object)this.Value;
}
else
{
// Use the user serializer so custom conversions are correctly handled
valueParam = cosmosSerializer.ToStream(this.Value);
}

return true;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ namespace Microsoft.Azure.Cosmos
{
using System;
using System.Collections.Generic;
using System.IO;
using Newtonsoft.Json;

/// <summary>
Expand Down Expand Up @@ -87,8 +88,17 @@ public override void WriteJson(
writer.WritePropertyName(PatchConstants.PropertyNames.Path);
writer.WriteValue(operation.Path);

if (operation.TrySerializeValueParameter(this.userSerializer, out string valueParam))
if (operation.TrySerializeValueParameter(this.userSerializer, out Stream valueStream))
{
string valueParam;
using (valueStream)
{
using (StreamReader streamReader = new StreamReader(valueStream))
{
valueParam = streamReader.ReadToEnd();
j82w marked this conversation as resolved.
Show resolved Hide resolved
}
}

writer.WritePropertyName(PatchConstants.PropertyNames.Value);
writer.WriteRawValue(valueParam);
anujtoshniwal marked this conversation as resolved.
Show resolved Hide resolved
}
Expand Down
7 changes: 1 addition & 6 deletions Microsoft.Azure.Cosmos/src/Patch/PatchOperation{T}.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,7 @@ namespace Microsoft.Azure.Cosmos
/// Defines PatchOperation with a value parameter.
/// </summary>
/// <typeparam name="T">Data type of value provided for PatchOperation.</typeparam>
#if PREVIEW
public
#else
internal
#endif
abstract class PatchOperation<T> : PatchOperation
internal abstract class PatchOperation<T> : PatchOperation
{
/// <summary>
/// Value parameter.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2092,7 +2092,7 @@ public async Task ItemPatchViaGatewayTest()
}

[TestMethod]
public async Task ItemPatchCustomSerilizerTest()
public async Task ItemPatchCustomSerializerTest()
{
CosmosClientOptions clientOptions = new CosmosClientOptions()
{
Expand All @@ -2115,9 +2115,23 @@ public async Task ItemPatchCustomSerilizerTest()
};

DateTime patchDate = new DateTime(2020, 07, 01, 01, 02, 03);
List<PatchOperation> patchOperations = new List<PatchOperation>()
Stream patchDateStreamInput = new CosmosJsonDotNetSerializer().ToStream(patchDate);
string streamDateJson;
using (Stream stream = new MemoryStream())
{
patchDateStreamInput.CopyTo(stream);
stream.Position = 0;
patchDateStreamInput.Position = 0;
using (StreamReader streamReader = new StreamReader(stream))
{
streamDateJson = streamReader.ReadToEnd();
}
}

List <PatchOperation> patchOperations = new List<PatchOperation>()
{
PatchOperation.Add("/date", patchDate)
PatchOperation.Add("/date", patchDate),
PatchOperation.Add("/dateStream", patchDateStreamInput)
};

ItemResponse<dynamic> response = await containerInternal.PatchItemAsync<dynamic>(
Expand All @@ -2140,6 +2154,60 @@ public async Task ItemPatchCustomSerilizerTest()
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
Assert.IsNotNull(response.Resource);
Assert.IsTrue(dateJson.Contains(response.Resource["date"].ToString()));
Assert.AreEqual(patchDate.ToString(), response.Resource["dateStream"].ToString());
Assert.AreNotEqual(response.Resource["date"], response.Resource["dateStream"]);
}

[TestMethod]
public async Task ItemPatchStreamInputTest()
{
dynamic testItem = new
{
id = "test",
cost = (double?)null,
totalCost = 98.2789,
pk = "MyCustomStatus",
taskNum = 4909,
itemIds = new int[] { 1, 5, 10 },
itemCode = new byte?[5] { 0x16, (byte)'\0', 0x3, null, (byte)'}' },
};

// Create item
await this.Container.CreateItemAsync<dynamic>(item: testItem);
ContainerInternal containerInternal = (ContainerInternal)this.Container;

dynamic testItemUpdated = new
{
cost = 100,
totalCost = 198.2789,
taskNum = 4910,
itemCode = new byte?[3] { 0x14, (byte)'\0', (byte)'{' }
};

CosmosJsonDotNetSerializer cosmosJsonDotNetSerializer = new CosmosJsonDotNetSerializer();

List<PatchOperation> patchOperations = new List<PatchOperation>()
{
PatchOperation.Replace("/cost", cosmosJsonDotNetSerializer.ToStream(testItemUpdated.cost)),
PatchOperation.Replace("/totalCost", cosmosJsonDotNetSerializer.ToStream(testItemUpdated.totalCost)),
PatchOperation.Replace("/taskNum", cosmosJsonDotNetSerializer.ToStream(testItemUpdated.taskNum)),
PatchOperation.Replace("/itemCode", cosmosJsonDotNetSerializer.ToStream(testItemUpdated.itemCode)),
};

ItemResponse<dynamic> response = await containerInternal.PatchItemAsync<dynamic>(
id: testItem.id,
partitionKey: new Cosmos.PartitionKey(testItem.pk),
patchOperations: patchOperations);

Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
Assert.IsNotNull(response.Resource);

Assert.AreEqual(testItemUpdated.cost.ToString(), response.Resource.cost.ToString());
Assert.AreEqual(testItemUpdated.totalCost.ToString(), response.Resource.totalCost.ToString());
Assert.AreEqual(testItemUpdated.taskNum.ToString(), response.Resource.taskNum.ToString());
Assert.AreEqual(testItemUpdated.itemCode[0].ToString(), response.Resource.itemCode[0].ToString());
Assert.AreEqual(testItemUpdated.itemCode[1].ToString(), response.Resource.itemCode[1].ToString());
Assert.AreEqual(testItemUpdated.itemCode[2].ToString(), response.Resource.itemCode[2].ToString());
}

// Read write non partition Container item.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1369,27 +1369,13 @@
"NestedTypes": {}
},
"Microsoft.Azure.Cosmos.PatchOperation;System.Object;IsAbstract:True;IsSealed:False;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": {
"Subclasses": {
"Microsoft.Azure.Cosmos.PatchOperation`1;Microsoft.Azure.Cosmos.PatchOperation;IsAbstract:True;IsSealed:False;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:True;IsSerializable:False": {
"Subclasses": {},
"Members": {
"T get_Value()": {
"Type": "Method",
"Attributes": [],
"MethodInfo": "T get_Value();IsAbstract:True;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
},
"T Value[Newtonsoft.Json.JsonPropertyAttribute(PropertyName = \"value\")]": {
"Type": "Property",
"Attributes": [
"JsonPropertyAttribute"
],
"MethodInfo": "T Value;CanRead:True;CanWrite:False;T get_Value();IsAbstract:True;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
}
},
"NestedTypes": {}
}
},
"Subclasses": {},
"Members": {
"Boolean TrySerializeValueParameter(Microsoft.Azure.Cosmos.CosmosSerializer, System.IO.Stream ByRef)": {
"Type": "Method",
"Attributes": [],
"MethodInfo": "Boolean TrySerializeValueParameter(Microsoft.Azure.Cosmos.CosmosSerializer, System.IO.Stream ByRef);IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
},
"Microsoft.Azure.Cosmos.PatchOperation Add[T](System.String, T)": {
"Type": "Method",
"Attributes": [],
Expand Down Expand Up @@ -1447,24 +1433,6 @@
},
"NestedTypes": {}
},
"Microsoft.Azure.Cosmos.PatchOperation`1;Microsoft.Azure.Cosmos.PatchOperation;IsAbstract:True;IsSealed:False;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:True;IsSerializable:False": {
"Subclasses": {},
"Members": {
"T get_Value()": {
"Type": "Method",
"Attributes": [],
"MethodInfo": "T get_Value();IsAbstract:True;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
},
"T Value[Newtonsoft.Json.JsonPropertyAttribute(PropertyName = \"value\")]": {
"Type": "Property",
"Attributes": [
"JsonPropertyAttribute"
],
"MethodInfo": "T Value;CanRead:True;CanWrite:False;T get_Value();IsAbstract:True;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
}
},
"NestedTypes": {}
},
"Microsoft.Azure.Cosmos.PatchOperationType;System.Enum;IsAbstract:False;IsSealed:True;IsInterface:False;IsEnum:True;IsClass:False;IsValueType:True;IsNested:False;IsGenericType:False;IsSerializable:True": {
"Subclasses": {},
"Members": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,13 @@ public void ValidatePatchOperationSerialization()
// custom serializer is used since there is Add operation type also
using (Stream stream = serializerCore.ToStream(new PatchSpec(patch, patchRequestOptions))) { }
Assert.AreEqual(1, toCount);

patch.Clear();
toCount = 0;
patch.Add(PatchOperation.Add("/addPath", new CosmosJsonDotNetSerializer().ToStream("addValue")));
// custom serializer is not used since the input value is of type stream
using (Stream stream = serializerCore.ToStream(new PatchSpec(patch, patchRequestOptions))) { }
Assert.AreEqual(0, toCount);
}

[TestMethod]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,14 +72,26 @@ private static void ValidateOperations<T>(PatchOperation patchOperation, PatchOp
{
string expected;
CosmosSerializer cosmosSerializer = new CosmosJsonDotNetSerializer();
Stream stream = cosmosSerializer.ToStream(value);
using (StreamReader streamReader = new StreamReader(stream))
using (Stream stream = cosmosSerializer.ToStream(value))
{
expected = streamReader.ReadToEnd();
using (StreamReader streamReader = new StreamReader(stream))
{
expected = streamReader.ReadToEnd();
}
}

Assert.IsTrue(patchOperation.TrySerializeValueParameter(new CustomSerializer(), out string valueParam));
Assert.AreEqual(expected, valueParam);
Assert.IsTrue(patchOperation.TrySerializeValueParameter(new CustomSerializer(), out Stream valueParam));

string actual;
using (valueParam)
{
using (StreamReader streamReader = new StreamReader(valueParam))
{
actual = streamReader.ReadToEnd();
}
}

Assert.AreEqual(expected, actual);
}
}

Expand Down