Skip to content

Commit a1144f4

Browse files
CF/P AVAD: Fixes Deserialization of ChangeFeedItem and ChangeFeedMetadata to support System.Text.Json and Newtonsoft.Json (#4618)
* checkin in * support for both STJ and NSJ * update contracts. * name change PreviousLsn * STJ TypeConverter support for ChangeFeedMetadata * adding bacl StringEnumConverter * test for Writes ChangeFeedMetadata * removing DateTimeOffset as results are inconsistent. * trying to get GMT, not local * static UnixEpoch * static qualifier in tests * PropertyNameCaseInsensitive = false tests. copy of True tests. * setting PropertyNameCaseInsensitive correctly for tests * removed duplication for propertyNameCaseInsensitive tests * remove JsonStringEnumConverter(), from tests --------- Co-authored-by: Matias Quaranta <[email protected]>
1 parent 2fbc278 commit a1144f4

File tree

8 files changed

+947
-58
lines changed

8 files changed

+947
-58
lines changed

Microsoft.Azure.Cosmos/src/Resource/FullFidelity/ChangeFeedItem.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace Microsoft.Azure.Cosmos
66
{
7+
using System.Text.Json.Serialization;
78
using Newtonsoft.Json;
89

910
/// <summary>
@@ -59,18 +60,21 @@ class ChangeFeedItem<T>
5960
/// The full fidelity change feed current item.
6061
/// </summary>
6162
[JsonProperty(PropertyName = "current")]
63+
[JsonPropertyName("current")]
6264
public T Current { get; set; }
6365

6466
/// <summary>
6567
/// The full fidelity change feed metadata.
6668
/// </summary>
6769
[JsonProperty(PropertyName = "metadata", NullValueHandling = NullValueHandling.Ignore)]
70+
[JsonPropertyName("metadata")]
6871
public ChangeFeedMetadata Metadata { get; set; }
6972

7073
/// <summary>
7174
/// For delete operations, previous image is always going to be provided. The previous image on replace operations is not going to be exposed by default and requires account-level or container-level opt-in.
7275
/// </summary>
7376
[JsonProperty(PropertyName = "previous", NullValueHandling = NullValueHandling.Ignore)]
77+
[JsonPropertyName("previous")]
7478
public T Previous { get; set; }
7579
}
7680
}

Microsoft.Azure.Cosmos/src/Resource/FullFidelity/ChangeFeedMetadata.cs

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,50 +5,54 @@
55
namespace Microsoft.Azure.Cosmos
66
{
77
using System;
8+
using System.Text.Json;
9+
using Microsoft.Azure.Cosmos.Resource.FullFidelity;
10+
using Microsoft.Azure.Cosmos.Resource.FullFidelity.Converters;
811
using Microsoft.Azure.Documents;
912
using Newtonsoft.Json;
1013
using Newtonsoft.Json.Converters;
1114

1215
/// <summary>
1316
/// The metadata of a change feed resource with <see cref="ChangeFeedMode"/> is initialized to <see cref="ChangeFeedMode.AllVersionsAndDeletes"/>.
1417
/// </summary>
18+
[System.Text.Json.Serialization.JsonConverter(typeof(ChangeFeedMetadataConverter))]
1519
#if PREVIEW
1620
public
1721
#else
1822
internal
19-
#endif
23+
#endif
2024
class ChangeFeedMetadata
2125
{
2226
/// <summary>
23-
/// The conflict resolution timestamp.
27+
/// The change's conflict resolution timestamp.
2428
/// </summary>
25-
[JsonProperty(PropertyName = "crts", NullValueHandling = NullValueHandling.Ignore)]
29+
[JsonProperty(PropertyName = ChangeFeedMetadataFields.ConflictResolutionTimestamp, NullValueHandling = NullValueHandling.Ignore)]
2630
[JsonConverter(typeof(UnixDateTimeConverter))]
2731
public DateTime ConflictResolutionTimestamp { get; internal set; }
2832

2933
/// <summary>
30-
/// The current logical sequence number.
34+
/// The current change's logical sequence number.
3135
/// </summary>
32-
[JsonProperty(PropertyName = "lsn", NullValueHandling = NullValueHandling.Ignore)]
33-
public long Lsn { get; internal set; }
36+
[JsonProperty(PropertyName = ChangeFeedMetadataFields.Lsn, NullValueHandling = NullValueHandling.Ignore)]
37+
public long Lsn { get; internal set; }
3438

3539
/// <summary>
36-
/// The change feed operation type.
40+
/// The change's feed operation type <see cref="ChangeFeedOperationType"/>.
3741
/// </summary>
38-
[JsonProperty(PropertyName = "operationType")]
42+
[JsonProperty(PropertyName = ChangeFeedMetadataFields.OperationType, NullValueHandling = NullValueHandling.Ignore)]
3943
[JsonConverter(typeof(StringEnumConverter))]
4044
public ChangeFeedOperationType OperationType { get; internal set; }
4145

4246
/// <summary>
43-
/// The previous logical sequence number.
47+
/// The previous change's logical sequence number.
4448
/// </summary>
45-
[JsonProperty(PropertyName = "previousImageLSN", NullValueHandling = NullValueHandling.Ignore)]
49+
[JsonProperty(PropertyName = ChangeFeedMetadataFields.PreviousImageLSN, NullValueHandling = NullValueHandling.Ignore)]
4650
public long PreviousLsn { get; internal set; }
4751

4852
/// <summary>
4953
/// Used to distinquish explicit deletes (e.g. via DeleteItem) from deletes caused by TTL expiration (a collection may define time-to-live policy for documents).
5054
/// </summary>
51-
[JsonProperty(PropertyName = "timeToLiveExpired", NullValueHandling= NullValueHandling.Ignore)]
55+
[JsonProperty(PropertyName = ChangeFeedMetadataFields.TimeToLiveExpired, NullValueHandling = NullValueHandling.Ignore)]
5256
public bool IsTimeToLiveExpired { get; internal set; }
5357
}
5458
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//------------------------------------------------------------
2+
// Copyright (c) Microsoft Corporation. All rights reserved.
3+
//------------------------------------------------------------
4+
5+
namespace Microsoft.Azure.Cosmos.Resource.FullFidelity
6+
{
7+
internal class ChangeFeedMetadataFields
8+
{
9+
public const string ConflictResolutionTimestamp = "crts";
10+
public const string Lsn = "lsn";
11+
public const string OperationType = "operationType";
12+
public const string PreviousImageLSN = "previousImageLSN";
13+
public const string TimeToLiveExpired = "timeToLiveExpired";
14+
}
15+
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
//------------------------------------------------------------
2+
// Copyright (c) Microsoft Corporation. All rights reserved.
3+
//------------------------------------------------------------
4+
5+
namespace Microsoft.Azure.Cosmos.Resource.FullFidelity.Converters
6+
{
7+
using System;
8+
using System.Globalization;
9+
using System.Text.Json;
10+
using System.Text.Json.Serialization;
11+
using Microsoft.Azure.Cosmos.Resource.FullFidelity;
12+
using Microsoft.Azure.Documents;
13+
14+
/// <summary>
15+
/// Converter used to support System.Text.Json de/serialization of type ChangeFeedMetadata/>.
16+
/// </summary>
17+
internal class ChangeFeedMetadataConverter : JsonConverter<ChangeFeedMetadata>
18+
{
19+
private readonly static DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
20+
21+
public override ChangeFeedMetadata Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
22+
{
23+
if (reader.TokenType == JsonTokenType.Null)
24+
{
25+
return null;
26+
}
27+
28+
if (reader.TokenType != JsonTokenType.StartObject)
29+
{
30+
throw new JsonException(string.Format(CultureInfo.CurrentCulture, RMResources.JsonUnexpectedToken));
31+
}
32+
33+
JsonElement element = JsonDocument.ParseValue(ref reader).RootElement;
34+
35+
ChangeFeedMetadata metadata = new ();
36+
37+
foreach (JsonProperty property in element.EnumerateObject())
38+
{
39+
if (property.NameEquals(ChangeFeedMetadataFields.Lsn))
40+
{
41+
metadata.Lsn = property.Value.GetInt64();
42+
}
43+
else if (property.NameEquals(ChangeFeedMetadataFields.ConflictResolutionTimestamp))
44+
{
45+
metadata.ConflictResolutionTimestamp = ChangeFeedMetadataConverter.ToDateTimeFromUnixTimeInSeconds(property.Value.GetInt64());
46+
}
47+
else if (property.NameEquals(ChangeFeedMetadataFields.OperationType))
48+
{
49+
metadata.OperationType = (ChangeFeedOperationType)Enum.Parse(enumType: typeof(ChangeFeedOperationType), value: property.Value.GetString(), ignoreCase: true);
50+
}
51+
else if (property.NameEquals(ChangeFeedMetadataFields.TimeToLiveExpired))
52+
{
53+
metadata.IsTimeToLiveExpired = property.Value.GetBoolean();
54+
}
55+
else if (property.NameEquals(ChangeFeedMetadataFields.PreviousImageLSN))
56+
{
57+
metadata.PreviousLsn = property.Value.GetInt64();
58+
}
59+
}
60+
61+
return metadata;
62+
}
63+
64+
public override void Write(Utf8JsonWriter writer, ChangeFeedMetadata value, JsonSerializerOptions options)
65+
{
66+
if (value == null)
67+
{
68+
return;
69+
}
70+
71+
writer.WriteStartObject();
72+
73+
writer.WriteNumber(ChangeFeedMetadataFields.ConflictResolutionTimestamp, ChangeFeedMetadataConverter.ToUnixTimeInSecondsFromDateTime(value.ConflictResolutionTimestamp));
74+
writer.WriteBoolean(ChangeFeedMetadataFields.TimeToLiveExpired, value.IsTimeToLiveExpired);
75+
writer.WriteNumber(ChangeFeedMetadataFields.Lsn, value.Lsn);
76+
writer.WriteString(ChangeFeedMetadataFields.OperationType, value.OperationType.ToString());
77+
writer.WriteNumber(ChangeFeedMetadataFields.PreviousImageLSN, value.PreviousLsn);
78+
79+
writer.WriteEndObject();
80+
}
81+
82+
private static long ToUnixTimeInSecondsFromDateTime(DateTime date)
83+
{
84+
return (long)(date - ChangeFeedMetadataConverter.UnixEpoch).TotalSeconds;
85+
}
86+
87+
private static DateTime ToDateTimeFromUnixTimeInSeconds(long unixTimeInSeconds)
88+
{
89+
return ChangeFeedMetadataConverter.UnixEpoch.AddSeconds(unixTimeInSeconds);
90+
}
91+
}
92+
}

0 commit comments

Comments
 (0)