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

CF/P AVAD: Fixes Deserialization of ChangeFeedItem and ChangeFeedMetadata to support System.Text.Json and Newtonsoft.Json #4618

Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
2216d83
checkin in
philipthomas-MSFT Aug 1, 2024
28d7222
Merge branch 'master' into 4616-cfp-avad-issues-with-serializationdes…
philipthomas-MSFT Aug 1, 2024
5f7bc3f
support for both STJ and NSJ
philipthomas-MSFT Aug 5, 2024
c5a09e9
Merge remote-tracking branch 'origin/master' into 4616-cfp-avad-issue…
philipthomas-MSFT Aug 5, 2024
3a879b7
update contracts.
philipthomas-MSFT Aug 5, 2024
23e7631
name change PreviousLsn
philipthomas-MSFT Aug 5, 2024
82bc224
Merge remote-tracking branch 'origin/master' into 4616-cfp-avad-issue…
philipthomas-MSFT Aug 6, 2024
375236c
STJ TypeConverter support for ChangeFeedMetadata
philipthomas-MSFT Aug 6, 2024
7acac4d
adding bacl StringEnumConverter
philipthomas-MSFT Aug 6, 2024
461e1e8
Merge remote-tracking branch 'origin/master' into 4616-cfp-avad-issue…
philipthomas-MSFT Aug 6, 2024
6a71a82
test for Writes ChangeFeedMetadata
philipthomas-MSFT Aug 6, 2024
b6fde92
removing DateTimeOffset as results are inconsistent.
philipthomas-MSFT Aug 7, 2024
f2d1e6f
trying to get GMT, not local
philipthomas-MSFT Aug 7, 2024
84fdcc3
static UnixEpoch
philipthomas-MSFT Aug 7, 2024
2c07550
Merge branch 'master' into 4616-cfp-avad-issues-with-serializationdes…
ealsur Aug 12, 2024
e868ba5
Merge branch 'master' into 4616-cfp-avad-issues-with-serializationdes…
philipthomas-MSFT Aug 14, 2024
7ef00af
Merge remote-tracking branch 'origin/master' into 4616-cfp-avad-issue…
philipthomas-MSFT Aug 16, 2024
d71b8b0
Merge branch '4616-cfp-avad-issues-with-serializationdeserialization-…
philipthomas-MSFT Aug 16, 2024
d30e7af
static qualifier in tests
philipthomas-MSFT Aug 16, 2024
121d459
PropertyNameCaseInsensitive = false tests. copy of True tests.
philipthomas-MSFT Aug 19, 2024
0339f16
setting PropertyNameCaseInsensitive correctly for tests
philipthomas-MSFT Aug 19, 2024
3b13a15
removed duplication for propertyNameCaseInsensitive tests
philipthomas-MSFT Aug 19, 2024
625fe86
remove JsonStringEnumConverter(), from tests
philipthomas-MSFT Aug 19, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Microsoft.Azure.Cosmos
{
using System.Text.Json.Serialization;
using Newtonsoft.Json;

/// <summary>
Expand Down Expand Up @@ -59,18 +60,21 @@ class ChangeFeedItem<T>
/// The full fidelity change feed current item.
/// </summary>
[JsonProperty(PropertyName = "current")]
philipthomas-MSFT marked this conversation as resolved.
Show resolved Hide resolved
[JsonPropertyName("current")]
philipthomas-MSFT marked this conversation as resolved.
Show resolved Hide resolved
public T Current { get; set; }

/// <summary>
/// The full fidelity change feed metadata.
/// </summary>
[JsonProperty(PropertyName = "metadata", NullValueHandling = NullValueHandling.Ignore)]
[JsonPropertyName("metadata")]
public ChangeFeedMetadata Metadata { get; set; }

/// <summary>
/// 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.
/// </summary>
[JsonProperty(PropertyName = "previous", NullValueHandling = NullValueHandling.Ignore)]
[JsonPropertyName("previous")]
public T Previous { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,50 +5,54 @@
namespace Microsoft.Azure.Cosmos
{
using System;
using System.Text.Json;
using Microsoft.Azure.Cosmos.Resource.FullFidelity;
using Microsoft.Azure.Cosmos.Resource.FullFidelity.Converters;
using Microsoft.Azure.Documents;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;

/// <summary>
/// The metadata of a change feed resource with <see cref="ChangeFeedMode"/> is initialized to <see cref="ChangeFeedMode.AllVersionsAndDeletes"/>.
/// </summary>
[System.Text.Json.Serialization.JsonConverter(typeof(ChangeFeedMetadataConverter))]
#if PREVIEW
public
#else
internal
#endif
#endif
class ChangeFeedMetadata
{
/// <summary>
/// The conflict resolution timestamp.
/// The change's conflict resolution timestamp.
/// </summary>
kirankumarkolli marked this conversation as resolved.
Show resolved Hide resolved
[JsonProperty(PropertyName = "crts", NullValueHandling = NullValueHandling.Ignore)]
[JsonProperty(PropertyName = ChangeFeedMetadataFields.ConflictResolutionTimestamp, NullValueHandling = NullValueHandling.Ignore)]
kirankumarkolli marked this conversation as resolved.
Show resolved Hide resolved
[JsonConverter(typeof(UnixDateTimeConverter))]
philipthomas-MSFT marked this conversation as resolved.
Show resolved Hide resolved
public DateTime ConflictResolutionTimestamp { get; internal set; }
ealsur marked this conversation as resolved.
Show resolved Hide resolved

/// <summary>
/// The current logical sequence number.
/// The current change's logical sequence number.
/// </summary>
[JsonProperty(PropertyName = "lsn", NullValueHandling = NullValueHandling.Ignore)]
public long Lsn { get; internal set; }
[JsonProperty(PropertyName = ChangeFeedMetadataFields.Lsn, NullValueHandling = NullValueHandling.Ignore)]
public long Lsn { get; internal set; }

/// <summary>
/// The change feed operation type.
/// The change's feed operation type <see cref="ChangeFeedOperationType"/>.
/// </summary>
[JsonProperty(PropertyName = "operationType")]
[JsonProperty(PropertyName = ChangeFeedMetadataFields.OperationType, NullValueHandling = NullValueHandling.Ignore)]
[JsonConverter(typeof(StringEnumConverter))]
philipthomas-MSFT marked this conversation as resolved.
Show resolved Hide resolved
public ChangeFeedOperationType OperationType { get; internal set; }

/// <summary>
/// The previous logical sequence number.
/// The previous change's logical sequence number.
/// </summary>
[JsonProperty(PropertyName = "previousImageLSN", NullValueHandling = NullValueHandling.Ignore)]
[JsonProperty(PropertyName = ChangeFeedMetadataFields.PreviousImageLSN, NullValueHandling = NullValueHandling.Ignore)]
public long PreviousLsn { get; internal set; }

/// <summary>
/// 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).
/// </summary>
[JsonProperty(PropertyName = "timeToLiveExpired", NullValueHandling= NullValueHandling.Ignore)]
[JsonProperty(PropertyName = ChangeFeedMetadataFields.TimeToLiveExpired, NullValueHandling = NullValueHandling.Ignore)]
public bool IsTimeToLiveExpired { get; internal set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------

namespace Microsoft.Azure.Cosmos.Resource.FullFidelity
{
internal class ChangeFeedMetadataFields
{
public const string ConflictResolutionTimestamp = "crts";
public const string Lsn = "lsn";
public const string OperationType = "operationType";
public const string PreviousImageLSN = "previousImageLSN";
public const string TimeToLiveExpired = "timeToLiveExpired";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------

namespace Microsoft.Azure.Cosmos.Resource.FullFidelity.Converters
{
using System;
using System.Globalization;
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.Azure.Cosmos.Resource.FullFidelity;
using Microsoft.Azure.Documents;

/// <summary>
/// Converter used to support System.Text.Json de/serialization of type ChangeFeedMetadata/>.
/// </summary>
internal class ChangeFeedMetadataConverter : JsonConverter<ChangeFeedMetadata>
{
private readonly static DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);

public override ChangeFeedMetadata Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.Null)
{
return null;
}

if (reader.TokenType != JsonTokenType.StartObject)
{
throw new JsonException(string.Format(CultureInfo.CurrentCulture, RMResources.JsonUnexpectedToken));
}

JsonElement element = JsonDocument.ParseValue(ref reader).RootElement;

ChangeFeedMetadata metadata = new ();

foreach (JsonProperty property in element.EnumerateObject())
{
if (property.NameEquals(ChangeFeedMetadataFields.Lsn))
{
metadata.Lsn = property.Value.GetInt64();
}
else if (property.NameEquals(ChangeFeedMetadataFields.ConflictResolutionTimestamp))
{
metadata.ConflictResolutionTimestamp = ChangeFeedMetadataConverter.ToDateTimeFromUnixTimeInSeconds(property.Value.GetInt64());
}
else if (property.NameEquals(ChangeFeedMetadataFields.OperationType))
{
metadata.OperationType = (ChangeFeedOperationType)Enum.Parse(enumType: typeof(ChangeFeedOperationType), value: property.Value.GetString(), ignoreCase: true);
}
else if (property.NameEquals(ChangeFeedMetadataFields.TimeToLiveExpired))
{
metadata.IsTimeToLiveExpired = property.Value.GetBoolean();
}
else if (property.NameEquals(ChangeFeedMetadataFields.PreviousImageLSN))
{
metadata.PreviousLsn = property.Value.GetInt64();
}
}

return metadata;
}

public override void Write(Utf8JsonWriter writer, ChangeFeedMetadata value, JsonSerializerOptions options)
{
if (value == null)
philipthomas-MSFT marked this conversation as resolved.
Show resolved Hide resolved
{
return;
}

writer.WriteStartObject();

writer.WriteNumber(ChangeFeedMetadataFields.ConflictResolutionTimestamp, ChangeFeedMetadataConverter.ToUnixTimeInSecondsFromDateTime(value.ConflictResolutionTimestamp));
writer.WriteBoolean(ChangeFeedMetadataFields.TimeToLiveExpired, value.IsTimeToLiveExpired);
writer.WriteNumber(ChangeFeedMetadataFields.Lsn, value.Lsn);
writer.WriteString(ChangeFeedMetadataFields.OperationType, value.OperationType.ToString());
writer.WriteNumber(ChangeFeedMetadataFields.PreviousImageLSN, value.PreviousLsn);

writer.WriteEndObject();
}

private static long ToUnixTimeInSecondsFromDateTime(DateTime date)
{
return (long)(date - ChangeFeedMetadataConverter.UnixEpoch).TotalSeconds;
}

private static DateTime ToDateTimeFromUnixTimeInSeconds(long unixTimeInSeconds)
{
return ChangeFeedMetadataConverter.UnixEpoch.AddSeconds(unixTimeInSeconds);
}
}
}
Loading