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

SystemTextJsonSerializer: Adds UseSystemTextJsonSerializerWithOptions in CosmosClientOptions to Set the Default STJ Serializer #4589

Merged
merged 25 commits into from
Jul 30, 2024
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
d2efdd6
Code changes to make STJ serializer public for preview.
kundadebdatta Jul 16, 2024
bfdb850
Code changes to make STJ serializer public for GA.
kundadebdatta Jul 16, 2024
93a0f9c
Code changes to hide STJ serializer implementation behind a boolean f…
kundadebdatta Jul 19, 2024
c1ee171
Code changes to fix tests.
kundadebdatta Jul 19, 2024
b590a4e
Revert "Code changes to fix tests."
kundadebdatta Jul 19, 2024
e6cd9f4
Code changes to fix baseline test.
kundadebdatta Jul 19, 2024
9d93df2
Merge branch 'master' into users/kundadebdatta/make_stj_serializer_pu…
kundadebdatta Jul 19, 2024
c6a172c
Code changes to fix root cause.
kundadebdatta Jul 19, 2024
9015d6c
Code changes to update tests.
kundadebdatta Jul 19, 2024
7460f30
Code changes to address review comments.
kundadebdatta Jul 22, 2024
8c07f8f
Code changes to add serializer options as contract.
kundadebdatta Jul 22, 2024
7902872
Code changes to add serializer options as public contract in builder.
kundadebdatta Jul 22, 2024
8226f7b
Code changes to update contract to remove the STJ serializer boolean …
kundadebdatta Jul 23, 2024
6976042
Code changes to update summary.
kundadebdatta Jul 23, 2024
078ab71
Code changes for minor cosmetic update.
kundadebdatta Jul 23, 2024
100b8e0
Merge branch 'master' into users/kundadebdatta/make_stj_serializer_pu…
kundadebdatta Jul 23, 2024
ee2f83a
Code changes to fix xml comment.
kundadebdatta Jul 23, 2024
ef7073a
Code changes to move remarks to summary. Updated test.
kundadebdatta Jul 24, 2024
1f65a66
Code changes to move the validation logic in cosmos client options.
kundadebdatta Jul 24, 2024
58981c2
Code changes to update contract.
kundadebdatta Jul 24, 2024
3ba71ea
Merge branch 'master' into users/kundadebdatta/make_stj_serializer_pu…
kundadebdatta Jul 24, 2024
ac5d07e
Code changes to remove client context core logic in cosmos client opt…
kundadebdatta Jul 26, 2024
77bf9ec
Code changes to update some tests.
kundadebdatta Jul 29, 2024
ac50590
Merge branch 'master' into users/kundadebdatta/make_stj_serializer_pu…
kundadebdatta Jul 29, 2024
f246ba8
Code changes to update contract. Made cosmetic changes.
kundadebdatta Jul 29, 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
51 changes: 45 additions & 6 deletions Microsoft.Azure.Cosmos/src/CosmosClientOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace Microsoft.Azure.Cosmos
using System.Net;
using System.Net.Http;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography.X509Certificates;
kundadebdatta marked this conversation as resolved.
Show resolved Hide resolved
using Microsoft.Azure.Cosmos.Fluent;
using Microsoft.Azure.Documents;
using Microsoft.Azure.Documents.Client;
Expand Down Expand Up @@ -60,7 +60,8 @@ public class CosmosClientOptions
/// </summary>
private int gatewayModeMaxConnectionLimit;
private CosmosSerializationOptions serializerOptions;
private CosmosSerializer serializerInternal;
private CosmosSerializer serializerInternal;
private System.Text.Json.JsonSerializerOptions stjSerializerOptions;

private ConnectionMode connectionMode;
private Protocol connectionProtocol;
Expand Down Expand Up @@ -392,6 +393,44 @@ public ConnectionMode ConnectionMode
/// <seealso cref="ItemRequestOptions.EnableContentResponseOnWrite"/>
/// <seealso cref="TransactionalBatchItemRequestOptions.EnableContentResponseOnWrite"/>
public bool? EnableContentResponseOnWrite { get; set; }

/// <summary>
/// Sets the <see cref="System.Text.Json.JsonSerializerOptions"/> for the System.Text.Json serializer.
/// Note that if this option is provided, then the SDK will use the System.Text.Json as the default serializer and set
/// the serializer options as the constructor args.
/// </summary>
/// <example>
/// An example on how to configure the System.Text.Json serializer options to ignore null values
/// <code language="c#">
/// <![CDATA[
/// CosmosClientOptions clientOptions = new CosmosClientOptions()
/// {
/// UseSystemTextJsonSerializerWithOptions = new System.Text.Json.JsonSerializerOptions()
/// {
/// DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull,
/// }
/// };
///
/// CosmosClient client = new CosmosClient("endpoint", "key", clientOptions);
/// ]]>
/// </code>
/// </example>
public System.Text.Json.JsonSerializerOptions UseSystemTextJsonSerializerWithOptions
{
private get => this.stjSerializerOptions;
set
{
if (this.Serializer != null || this.SerializerOptions != null)
{
throw new ArgumentException(
$"{nameof(this.UseSystemTextJsonSerializerWithOptions)} is not compatible with {nameof(this.Serializer)} or {nameof(this.SerializerOptions)}. Only one can be set. ");
}

this.stjSerializerOptions = value;
this.serializerInternal = new CosmosSystemTextJsonSerializer(
this.stjSerializerOptions);
}
}

/// <summary>
/// Gets or sets the advanced replica selection flag. The advanced replica selection logic keeps track of the replica connection
Expand Down Expand Up @@ -543,10 +582,10 @@ public CosmosSerializationOptions SerializerOptions
get => this.serializerOptions;
set
{
if (this.Serializer != null)
if (this.Serializer != null || this.UseSystemTextJsonSerializerWithOptions != null)
{
throw new ArgumentException(
$"{nameof(this.SerializerOptions)} is not compatible with {nameof(this.Serializer)}. Only one can be set. ");
$"{nameof(this.SerializerOptions)} is not compatible with {nameof(this.Serializer)} or {nameof(this.UseSystemTextJsonSerializerWithOptions)}. Only one can be set. ");
}

this.serializerOptions = value;
Expand Down Expand Up @@ -578,10 +617,10 @@ public CosmosSerializer Serializer
get => this.serializerInternal;
set
{
if (this.SerializerOptions != null)
if (this.SerializerOptions != null || this.UseSystemTextJsonSerializerWithOptions != null)
{
throw new ArgumentException(
$"{nameof(this.Serializer)} is not compatible with {nameof(this.SerializerOptions)}. Only one can be set. ");
$"{nameof(this.Serializer)} is not compatible with {nameof(this.SerializerOptions)} or {nameof(this.UseSystemTextJsonSerializerWithOptions)}. Only one can be set. ");
}

this.serializerInternal = value;
Expand Down
19 changes: 17 additions & 2 deletions Microsoft.Azure.Cosmos/src/Fluent/CosmosClientBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -646,7 +646,22 @@ public CosmosClientBuilder WithContentResponseOnWrite(bool contentResponseOnWrit
{
this.clientOptions.EnableContentResponseOnWrite = contentResponseOnWrite;
return this;
}
}

/// <summary>
/// Configures the <see cref="CosmosClientBuilder"/> to use System.Text.Json for serialization.
/// Use <see cref="System.Text.Json.JsonSerializerOptions" /> to use System.Text.Json with a default configuration.
/// If no options are specified, Newtonsoft.Json will be used for serialization instead.
/// </summary>
/// <param name="serializerOptions">An instance of <see cref="System.Text.Json.JsonSerializerOptions"/>
/// containing the system text json serializer options.</param>
/// <returns>The <see cref="CosmosClientBuilder"/> object</returns>
public CosmosClientBuilder WithSystemTextJsonSerializerOptions(
System.Text.Json.JsonSerializerOptions serializerOptions)
{
this.clientOptions.UseSystemTextJsonSerializerWithOptions = serializerOptions;
return this;
}

/// <summary>
/// The event handler to be invoked before the request is sent.
Expand Down Expand Up @@ -725,7 +740,7 @@ internal CosmosClientBuilder WithPartitionLevelFailoverEnabled()
{
this.clientOptions.EnablePartitionLevelFailover = true;
return this;
}
}

/// <summary>
/// Enables SDK to inject fault. Used for testing applications.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ public class LinqAggregateCustomSerializationBaseline : BaselineTests<LinqAggreg
private static Cosmos.Database testDb;
private static Container testContainer;

private static CosmosSerializer stjCosmosSerializer;
private static CosmosClient stjClient;
private static Cosmos.Database testDbSTJ;
private static Container testContainerSTJ;
Expand Down Expand Up @@ -64,10 +63,10 @@ public async static Task Initialize(TestContext textContext)
testDb = await client.CreateDatabaseAsync(dbName);
testContainer = testDb.CreateContainerAsync(new ContainerProperties(id: Guid.NewGuid().ToString(), partitionKeyPath: "/Pk")).Result;

stjCosmosSerializer = new CosmosSystemTextJsonSerializer(new JsonSerializerOptions());

stjClient = TestCommon.CreateCosmosClient((cosmosClientBuilder)
=> cosmosClientBuilder.WithCustomSerializer(stjCosmosSerializer));
=> cosmosClientBuilder.WithSystemTextJsonSerializerOptions(
new JsonSerializerOptions()),
kundadebdatta marked this conversation as resolved.
Show resolved Hide resolved
useCustomSeralizer: false);

// Set a callback to get the handle of the last executed query to do the verification
// This is neede because aggregate queries return type is a scalar so it can't be used
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,10 @@ public async static Task Initialize(TestContext textContext)
TestDb = await CosmosClient.CreateDatabaseAsync(dbName);

CosmosDefaultSTJClient = TestCommon.CreateCosmosClient((cosmosClientBuilder)
=> cosmosClientBuilder.WithCustomSerializer(new CosmosSystemTextJsonSerializer(new JsonSerializerOptions())));
=> cosmosClientBuilder
.WithSystemTextJsonSerializerOptions(
new JsonSerializerOptions()),
kundadebdatta marked this conversation as resolved.
Show resolved Hide resolved
useCustomSeralizer: false);

string dbNameSTJ = $"{nameof(LinqTranslationBaselineTests)}-{Guid.NewGuid():N}";
TestDbSTJDefault = await CosmosDefaultSTJClient.CreateDatabaseAsync(dbNameSTJ);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2988,6 +2988,11 @@
],
"MethodInfo": "System.String get_ApplicationRegion();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
},
"System.Text.Json.JsonSerializerOptions UseSystemTextJsonSerializerWithOptions": {
"Type": "Property",
"Attributes": [],
"MethodInfo": "System.Text.Json.JsonSerializerOptions UseSystemTextJsonSerializerWithOptions;CanRead:True;CanWrite:True;Void set_UseSystemTextJsonSerializerWithOptions(System.Text.Json.JsonSerializerOptions);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
},
"System.TimeSpan get_RequestTimeout()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": {
"Type": "Method",
"Attributes": [
Expand Down Expand Up @@ -3165,6 +3170,11 @@
],
"MethodInfo": "Void set_TokenCredentialBackgroundRefreshInterval(System.Nullable`1[System.TimeSpan]);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
},
"Void set_UseSystemTextJsonSerializerWithOptions(System.Text.Json.JsonSerializerOptions)": {
"Type": "Method",
"Attributes": [],
"MethodInfo": "Void set_UseSystemTextJsonSerializerWithOptions(System.Text.Json.JsonSerializerOptions);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
},
"Void set_WebProxy(System.Net.IWebProxy)": {
"Type": "Method",
"Attributes": [],
Expand Down Expand Up @@ -4737,6 +4747,11 @@
"Attributes": [],
"MethodInfo": "Microsoft.Azure.Cosmos.Fluent.CosmosClientBuilder WithSerializerOptions(Microsoft.Azure.Cosmos.CosmosSerializationOptions);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
},
"Microsoft.Azure.Cosmos.Fluent.CosmosClientBuilder WithSystemTextJsonSerializerOptions(System.Text.Json.JsonSerializerOptions)": {
"Type": "Method",
"Attributes": [],
"MethodInfo": "Microsoft.Azure.Cosmos.Fluent.CosmosClientBuilder WithSystemTextJsonSerializerOptions(System.Text.Json.JsonSerializerOptions);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
},
"Microsoft.Azure.Cosmos.Fluent.CosmosClientBuilder WithThrottlingRetryOptions(System.TimeSpan, Int32)": {
"Type": "Method",
"Attributes": [],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ namespace Microsoft.Azure.Cosmos.Tests
using System.Net.Security;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using global::Azure.Core;
using Microsoft.Azure.Cosmos.Fluent;
using Microsoft.Azure.Documents;
Expand Down Expand Up @@ -476,6 +477,26 @@ public void UserAgentContainsEnvironmentInformation()
Assert.AreEqual(userAgentSuffix, connectionPolicy.UserAgentSuffix);
Assert.IsTrue(connectionPolicy.UserAgentContainer.UserAgent.StartsWith(expectedValue));
Assert.IsTrue(connectionPolicy.UserAgentContainer.UserAgent.EndsWith(userAgentSuffix));
}

[TestMethod]
public void ValidateThatCustomSerializerGetsOverriddenWhenSTJSerializerEnabled()
{
CosmosClientOptions options = new CosmosClientOptions()
{
UseSystemTextJsonSerializerWithOptions = new System.Text.Json.JsonSerializerOptions()
{
PropertyNamingPolicy = System.Text.Json.JsonNamingPolicy.CamelCase,
}
};

CosmosClient client = new(
"https://fake-account.documents.azure.com:443/",
Convert.ToBase64String(Encoding.UTF8.GetBytes(Guid.NewGuid().ToString())),
options
);

Assert.AreEqual(typeof(CosmosSystemTextJsonSerializer), client.ClientOptions.Serializer.GetType());
}

[TestMethod]
Expand All @@ -500,8 +521,58 @@ public void ThrowOnCustomSerializerWithSerializerOptions()
};

options.Serializer = new CosmosJsonDotNetSerializer();
}

}

[TestMethod]
[DataRow(false, DisplayName = "Test when the client options order is maintained")]
[DataRow(true, DisplayName = "Test when the client options order is reversed")]
[ExpectedException(typeof(ArgumentException))]
public void ThrowOnCustomSerializerWithSTJSerializerEnabled(
bool reverseOrder)
{
if (reverseOrder)
{
CosmosClientOptions options = new CosmosClientOptions()
{
Serializer = new CosmosJsonDotNetSerializer(),
UseSystemTextJsonSerializerWithOptions = new System.Text.Json.JsonSerializerOptions(),
};
}
else
{
CosmosClientOptions options = new CosmosClientOptions()
{
UseSystemTextJsonSerializerWithOptions = new System.Text.Json.JsonSerializerOptions(),
Serializer = new CosmosJsonDotNetSerializer(),
};
}
}

[TestMethod]
[DataRow(false, DisplayName = "Test when the client options order is maintained")]
[DataRow(true, DisplayName = "Test when the client options order is reversed")]
[ExpectedException(typeof(ArgumentException))]
public void ThrowOnSerializerOptionsWithSTJSerializerEnabled(
bool reverseOrder)
{
if (reverseOrder)
{
CosmosClientOptions options = new CosmosClientOptions()
{
SerializerOptions = new CosmosSerializationOptions(),
UseSystemTextJsonSerializerWithOptions = new System.Text.Json.JsonSerializerOptions(),
};
}
else
{
CosmosClientOptions options = new CosmosClientOptions()
{
UseSystemTextJsonSerializerWithOptions = new System.Text.Json.JsonSerializerOptions(),
SerializerOptions = new CosmosSerializationOptions(),
};
}
}
kundadebdatta marked this conversation as resolved.
Show resolved Hide resolved

[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void ThrowOnNullTokenCredential()
Expand Down
Loading