diff --git a/Microsoft.Azure.Cosmos/src/Resource/ClientEncryptionKey/ClientEncryptionKeyProperties.cs b/Microsoft.Azure.Cosmos/src/Resource/ClientEncryptionKey/ClientEncryptionKeyProperties.cs index 7c3af2a1f5..602b3b7419 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/ClientEncryptionKey/ClientEncryptionKeyProperties.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/ClientEncryptionKey/ClientEncryptionKeyProperties.cs @@ -9,6 +9,7 @@ namespace Microsoft.Azure.Cosmos using System.Linq; using Microsoft.Azure.Documents; using Newtonsoft.Json; + using Newtonsoft.Json.Linq; /// /// Details of an encryption key for use with the Azure Cosmos DB service. @@ -61,6 +62,7 @@ internal ClientEncryptionKeyProperties(ClientEncryptionKeyProperties source) this.WrappedDataEncryptionKey = new byte[source.WrappedDataEncryptionKey.Length]; source.WrappedDataEncryptionKey.CopyTo(this.WrappedDataEncryptionKey, index: 0); } + this.AdditionalProperties = source.AdditionalProperties; } /// @@ -148,6 +150,13 @@ internal ClientEncryptionKeyProperties(ClientEncryptionKeyProperties source) [JsonProperty(PropertyName = Constants.Properties.RId, NullValueHandling = NullValueHandling.Ignore)] internal string ResourceId { get; set; } + /// + /// This contains additional values for scenarios where the SDK is not aware of new fields. + /// This ensures that if resource is read and updated none of the fields will be lost in the process. + /// + [JsonExtensionData] + internal IDictionary AdditionalProperties { get; private set; } + /// /// Compares this instance of client encryption key properties to another object. /// @@ -170,6 +179,7 @@ public bool Equals(ClientEncryptionKeyProperties other) this.EncryptionAlgorithm == other.EncryptionAlgorithm && ClientEncryptionKeyProperties.Equals(this.WrappedDataEncryptionKey, other.WrappedDataEncryptionKey) && EqualityComparer.Default.Equals(this.EncryptionKeyWrapMetadata, other.EncryptionKeyWrapMetadata) && + this.AdditionalProperties.EqualsTo(other.AdditionalProperties) && this.CreatedTime == other.CreatedTime && this.ETag == other.ETag && this.LastModified == other.LastModified && diff --git a/Microsoft.Azure.Cosmos/src/Resource/ClientEncryptionKey/EncryptionKeyWrapMetadata.cs b/Microsoft.Azure.Cosmos/src/Resource/ClientEncryptionKey/EncryptionKeyWrapMetadata.cs index e30babd71d..a5bc5e26aa 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/ClientEncryptionKey/EncryptionKeyWrapMetadata.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/ClientEncryptionKey/EncryptionKeyWrapMetadata.cs @@ -7,6 +7,7 @@ namespace Microsoft.Azure.Cosmos using System; using System.Collections.Generic; using Newtonsoft.Json; + using Newtonsoft.Json.Linq; /// /// Metadata that a key wrapping provider can use to wrap/unwrap data encryption keys. @@ -69,6 +70,13 @@ public EncryptionKeyWrapMetadata(EncryptionKeyWrapMetadata source) [JsonProperty(PropertyName = "value", NullValueHandling = NullValueHandling.Ignore)] public string Value { get; private set; } + /// + /// This contains additional values for scenarios where the SDK is not aware of new fields. + /// This ensures that if resource is read and updated none of the fields will be lost in the process. + /// + [JsonExtensionData] + internal IDictionary AdditionalProperties { get; private set; } + /// public override bool Equals(object obj) { @@ -98,7 +106,8 @@ public bool Equals(EncryptionKeyWrapMetadata other) return other != null && this.Type == other.Type && this.Name == other.Name && - this.Value == other.Value; + this.Value == other.Value && + this.AdditionalProperties.EqualsTo(other.AdditionalProperties); } } } \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos/src/Resource/Offer/OfferAutoscaleAutoUpgradeProperties.cs b/Microsoft.Azure.Cosmos/src/Resource/Offer/OfferAutoscaleAutoUpgradeProperties.cs index 2ac915e196..4d31d95afd 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/Offer/OfferAutoscaleAutoUpgradeProperties.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/Offer/OfferAutoscaleAutoUpgradeProperties.cs @@ -4,8 +4,10 @@ namespace Microsoft.Azure.Cosmos { + using System.Collections.Generic; using Microsoft.Azure.Documents; using Newtonsoft.Json; + using Newtonsoft.Json.Linq; internal sealed class OfferAutoscaleAutoUpgradeProperties { @@ -25,6 +27,13 @@ internal OfferAutoscaleAutoUpgradeProperties(int incrementPercent) [JsonProperty(PropertyName = Constants.Properties.AutopilotThroughputPolicy, NullValueHandling = NullValueHandling.Ignore)] public AutoscaleThroughputProperties ThroughputProperties { get; private set; } + /// + /// This contains additional values for scenarios where the SDK is not aware of new fields. + /// This ensures that if resource is read and updated none of the fields will be lost in the process. + /// + [JsonExtensionData] + internal IDictionary AdditionalProperties { get; private set; } + internal string GetJsonString() { return JsonConvert.SerializeObject(this, Formatting.None); @@ -39,6 +48,14 @@ public AutoscaleThroughputProperties(int incrementPercent) [JsonProperty(PropertyName = Constants.Properties.AutopilotThroughputPolicyIncrementPercent, NullValueHandling = NullValueHandling.Ignore)] public int IncrementPercent { get; private set; } + + /// + /// This contains additional values for scenarios where the SDK is not aware of new fields. + /// This ensures that if resource is read and updated none of the fields will be lost in the process. + /// + [JsonExtensionData] + internal IDictionary AdditionalProperties { get; private set; } + } } } diff --git a/Microsoft.Azure.Cosmos/src/Resource/Offer/OfferAutoscaleProperties.cs b/Microsoft.Azure.Cosmos/src/Resource/Offer/OfferAutoscaleProperties.cs index d5c2439973..a4d99af8ce 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/Offer/OfferAutoscaleProperties.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/Offer/OfferAutoscaleProperties.cs @@ -4,8 +4,10 @@ namespace Microsoft.Azure.Cosmos { + using System.Collections.Generic; using Microsoft.Azure.Documents; using Newtonsoft.Json; + using Newtonsoft.Json.Linq; internal sealed class OfferAutoscaleProperties { @@ -39,6 +41,13 @@ internal OfferAutoscaleProperties( [JsonProperty(PropertyName = Constants.Properties.AutopilotAutoUpgradePolicy, NullValueHandling = NullValueHandling.Ignore)] public OfferAutoscaleAutoUpgradeProperties AutoscaleAutoUpgradeProperties { get; private set; } + /// + /// This contains additional values for scenarios where the SDK is not aware of new fields. + /// This ensures that if resource is read and updated none of the fields will be lost in the process. + /// + [JsonExtensionData] + internal IDictionary AdditionalProperties { get; private set; } + internal string GetJsonString() { return JsonConvert.SerializeObject(this, Formatting.None); diff --git a/Microsoft.Azure.Cosmos/src/Resource/Offer/OfferContentProperties.cs b/Microsoft.Azure.Cosmos/src/Resource/Offer/OfferContentProperties.cs index c4de03a3e2..41776992b1 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/Offer/OfferContentProperties.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/Offer/OfferContentProperties.cs @@ -5,8 +5,10 @@ namespace Microsoft.Azure.Cosmos { using System; + using System.Collections.Generic; using Microsoft.Azure.Documents; using Newtonsoft.Json; + using Newtonsoft.Json.Linq; internal sealed class OfferContentProperties { @@ -54,6 +56,13 @@ private OfferContentProperties(OfferAutoscaleProperties autoscaleProperties) [JsonProperty(PropertyName = Constants.Properties.OfferLastReplaceTimestamp, DefaultValueHandling = DefaultValueHandling.Ignore)] internal long? OfferLastReplaceTimestamp { get; private set; } + /// + /// This contains additional values for scenarios where the SDK is not aware of new fields. + /// This ensures that if resource is read and updated none of the fields will be lost in the process. + /// + [JsonExtensionData] + internal IDictionary AdditionalProperties { get; private set; } + public static OfferContentProperties CreateManualOfferConent(int throughput) { return new OfferContentProperties(manualThroughput: throughput); diff --git a/Microsoft.Azure.Cosmos/src/Resource/Settings/AccountConsistency.cs b/Microsoft.Azure.Cosmos/src/Resource/Settings/AccountConsistency.cs index d29fd59de5..e80c7a9ef4 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/Settings/AccountConsistency.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/Settings/AccountConsistency.cs @@ -3,9 +3,11 @@ //------------------------------------------------------------ namespace Microsoft.Azure.Cosmos { + using System.Collections.Generic; using Microsoft.Azure.Documents; using Newtonsoft.Json; using Newtonsoft.Json.Converters; + using Newtonsoft.Json.Linq; /// /// Represents the consistency policy of a database account of the Azure Cosmos DB service. @@ -44,6 +46,13 @@ public class AccountConsistency [JsonProperty(PropertyName = Constants.Properties.MaxStalenessIntervalInSeconds)] public int MaxStalenessIntervalInSeconds { get; internal set; } + /// + /// This contains additional values for scenarios where the SDK is not aware of new fields. + /// This ensures that if resource is read and updated none of the fields will be lost in the process. + /// + [JsonExtensionData] + internal IDictionary AdditionalProperties { get; private set; } + internal Documents.ConsistencyLevel ToDirectConsistencyLevel() { return (Documents.ConsistencyLevel)this.DefaultConsistencyLevel; diff --git a/Microsoft.Azure.Cosmos/src/Resource/Settings/AccountProperties.cs b/Microsoft.Azure.Cosmos/src/Resource/Settings/AccountProperties.cs index 99afcae6b3..87b178697c 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/Settings/AccountProperties.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/Settings/AccountProperties.cs @@ -9,6 +9,7 @@ namespace Microsoft.Azure.Cosmos using System.Collections.ObjectModel; using Microsoft.Azure.Documents; using Newtonsoft.Json; + using Newtonsoft.Json.Linq; /// /// Represents a . A AccountProperties is the container for databases in the Azure Cosmos DB service. @@ -233,5 +234,12 @@ private IDictionary QueryStringToDictConverter() return new Dictionary(); } } + + /// + /// This contains additional values for scenarios where the SDK is not aware of new fields. + /// This ensures that if resource is read and updated none of the fields will be lost in the process. + /// + [JsonExtensionData] + internal IDictionary AdditionalProperties { get; private set; } } } diff --git a/Microsoft.Azure.Cosmos/src/Resource/Settings/AccountRegion.cs b/Microsoft.Azure.Cosmos/src/Resource/Settings/AccountRegion.cs index fe3e83a7fa..61a34d87d3 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/Settings/AccountRegion.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/Settings/AccountRegion.cs @@ -3,8 +3,10 @@ //------------------------------------------------------------ namespace Microsoft.Azure.Cosmos { + using System.Collections.Generic; using Microsoft.Azure.Documents; using Newtonsoft.Json; + using Newtonsoft.Json.Linq; /// /// The AccountLocation class represents an Azure Cosmos DB database account in a specific region. @@ -25,5 +27,12 @@ public class AccountRegion /// [JsonProperty(PropertyName = Constants.Properties.DatabaseAccountEndpoint)] public string Endpoint { get; internal set; } + + /// + /// This contains additional values for scenarios where the SDK is not aware of new fields. + /// This ensures that if resource is read and updated none of the fields will be lost in the process. + /// + [JsonExtensionData] + internal IDictionary AdditionalProperties { get; private set; } } } diff --git a/Microsoft.Azure.Cosmos/src/Resource/Settings/BoundingBoxProperties.cs b/Microsoft.Azure.Cosmos/src/Resource/Settings/BoundingBoxProperties.cs index 003914e248..7348b4b710 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/Settings/BoundingBoxProperties.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/Settings/BoundingBoxProperties.cs @@ -3,7 +3,9 @@ //------------------------------------------------------------ namespace Microsoft.Azure.Cosmos { + using System.Collections.Generic; using Newtonsoft.Json; + using Newtonsoft.Json.Linq; /// /// Represents bounding box for geometry spatial path in the Azure Cosmos DB service @@ -70,5 +72,12 @@ public double Ymax { get; set; } + + /// + /// This contains additional values for scenarios where the SDK is not aware of new fields. + /// This ensures that if resource is read and updated none of the fields will be lost in the process. + /// + [JsonExtensionData] + internal IDictionary AdditionalProperties { get; private set; } } } \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos/src/Resource/Settings/ChangeFeedPolicy.cs b/Microsoft.Azure.Cosmos/src/Resource/Settings/ChangeFeedPolicy.cs index 558dcfead1..e0b6d3ad16 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/Settings/ChangeFeedPolicy.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/Settings/ChangeFeedPolicy.cs @@ -5,8 +5,10 @@ namespace Microsoft.Azure.Cosmos { using System; + using System.Collections.Generic; using Microsoft.Azure.Documents; using Newtonsoft.Json; + using Newtonsoft.Json.Linq; /// /// Represents the change feed policy configuration for a container in the Azure Cosmos DB service. @@ -73,5 +75,13 @@ public TimeSpan FullFidelityRetention /// Disables the retention log. /// public static TimeSpan FullFidelityNoRetention => TimeSpan.Zero; + + /// + /// This contains additional values for scenarios where the SDK is not aware of new fields. + /// This ensures that if resource is read and updated none of the fields will be lost in the process. + /// + [JsonExtensionData] + internal IDictionary AdditionalProperties { get; private set; } + } } diff --git a/Microsoft.Azure.Cosmos/src/Resource/Settings/ClientEncryptionIncludedPath.cs b/Microsoft.Azure.Cosmos/src/Resource/Settings/ClientEncryptionIncludedPath.cs index b37a7ad54e..fec626802d 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/Settings/ClientEncryptionIncludedPath.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/Settings/ClientEncryptionIncludedPath.cs @@ -4,7 +4,9 @@ namespace Microsoft.Azure.Cosmos { + using System.Collections.Generic; using Newtonsoft.Json; + using Newtonsoft.Json.Linq; /// /// Path that needs encryption and the associated settings within . @@ -39,5 +41,12 @@ sealed class ClientEncryptionIncludedPath /// [JsonProperty(PropertyName = "encryptionAlgorithm")] public string EncryptionAlgorithm { get; set; } + + /// + /// This contains additional values for scenarios where the SDK is not aware of new fields. + /// This ensures that if resource is read and updated none of the fields will be lost in the process. + /// + [JsonExtensionData] + internal IDictionary AdditionalProperties { get; private set; } } } diff --git a/Microsoft.Azure.Cosmos/src/Resource/Settings/ClientEncryptionPolicy.cs b/Microsoft.Azure.Cosmos/src/Resource/Settings/ClientEncryptionPolicy.cs index a1e3348f07..305a59eb74 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/Settings/ClientEncryptionPolicy.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/Settings/ClientEncryptionPolicy.cs @@ -9,6 +9,7 @@ namespace Microsoft.Azure.Cosmos using System.Diagnostics; using System.Linq; using Newtonsoft.Json; + using Newtonsoft.Json.Linq; /// /// Client encryption policy. @@ -51,6 +52,13 @@ public IEnumerable IncludedPaths [JsonProperty(PropertyName = "policyFormatVersion")] public int PolicyFormatVersion { get; private set; } + /// + /// This contains additional values for scenarios where the SDK is not aware of new fields. + /// This ensures that if resource is read and updated none of the fields will be lost in the process. + /// + [JsonExtensionData] + internal IDictionary AdditionalProperties { get; private set; } + /// /// Ensures that partition key paths are not specified in the client encryption policy for encryption. /// diff --git a/Microsoft.Azure.Cosmos/src/Resource/Settings/CompositePath.cs b/Microsoft.Azure.Cosmos/src/Resource/Settings/CompositePath.cs index ec96db0014..e6fccb95ac 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/Settings/CompositePath.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/Settings/CompositePath.cs @@ -3,10 +3,12 @@ //------------------------------------------------------------ namespace Microsoft.Azure.Cosmos { + using System.Collections.Generic; using System.Runtime.Serialization; using Microsoft.Azure.Documents; using Newtonsoft.Json; using Newtonsoft.Json.Converters; + using Newtonsoft.Json.Linq; /// /// Defines the target data type of an index path specification in the Azure Cosmos DB service. @@ -49,5 +51,12 @@ public sealed class CompositePath [JsonProperty(PropertyName = Constants.Properties.Order)] [JsonConverter(typeof(StringEnumConverter))] public CompositePathSortOrder Order { get; set; } + + /// + /// This contains additional values for scenarios where the SDK is not aware of new fields. + /// This ensures that if resource is read and updated none of the fields will be lost in the process. + /// + [JsonExtensionData] + internal IDictionary AdditionalProperties { get; private set; } } } \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos/src/Resource/Settings/ConflictProperties.cs b/Microsoft.Azure.Cosmos/src/Resource/Settings/ConflictProperties.cs index f01744a2bd..4bb1b98354 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/Settings/ConflictProperties.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/Settings/ConflictProperties.cs @@ -5,8 +5,10 @@ namespace Microsoft.Azure.Cosmos { using System; + using System.Collections.Generic; using Newtonsoft.Json; using Newtonsoft.Json.Converters; + using Newtonsoft.Json.Linq; /// /// Represents a conflict in the Azure Cosmos DB service. @@ -65,5 +67,12 @@ public class ConflictProperties [JsonProperty(PropertyName = Documents.Constants.Properties.ConflictLSN)] internal long ConflictLSN { get; set; } + + /// + /// This contains additional values for scenarios where the SDK is not aware of new fields. + /// This ensures that if resource is read and updated none of the fields will be lost in the process. + /// + [JsonExtensionData] + internal IDictionary AdditionalProperties { get; private set; } } } diff --git a/Microsoft.Azure.Cosmos/src/Resource/Settings/ConflictResolutionPolicy.cs b/Microsoft.Azure.Cosmos/src/Resource/Settings/ConflictResolutionPolicy.cs index cd543c34a0..f91e564755 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/Settings/ConflictResolutionPolicy.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/Settings/ConflictResolutionPolicy.cs @@ -4,9 +4,11 @@ namespace Microsoft.Azure.Cosmos { + using System.Collections.Generic; using Microsoft.Azure.Cosmos.Scripts; using Newtonsoft.Json; using Newtonsoft.Json.Converters; + using Newtonsoft.Json.Linq; /// /// Represents the conflict resolution policy configuration for specifying how to resolve conflicts @@ -72,5 +74,13 @@ public ConflictResolutionPolicy() /// [JsonProperty(PropertyName = Documents.Constants.Properties.ConflictResolutionProcedure, NullValueHandling = NullValueHandling.Ignore)] public string ResolutionProcedure { get; set; } + + /// + /// This contains additional values for scenarios where the SDK is not aware of new fields. + /// This ensures that if resource is read and updated none of the fields will be lost in the process. + /// + [JsonExtensionData] + internal IDictionary AdditionalProperties { get; private set; } + } } diff --git a/Microsoft.Azure.Cosmos/src/Resource/Settings/ContainerProperties.cs b/Microsoft.Azure.Cosmos/src/Resource/Settings/ContainerProperties.cs index 5bea80b7ad..693ab64243 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/Settings/ContainerProperties.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/Settings/ContainerProperties.cs @@ -10,6 +10,7 @@ namespace Microsoft.Azure.Cosmos using Microsoft.Azure.Documents; using Microsoft.Azure.Documents.Routing; using Newtonsoft.Json; + using Newtonsoft.Json.Linq; /// /// Represents a document container in the Azure Cosmos DB service. A container is a named logical container for documents. @@ -77,6 +78,13 @@ public class ContainerProperties [JsonProperty(PropertyName = "clientEncryptionPolicy", NullValueHandling = NullValueHandling.Ignore)] private ClientEncryptionPolicy clientEncryptionPolicyInternal; + /// + /// This contains additional values for scenarios where the SDK is not aware of new fields. + /// This ensures that if resource is read and updated none of the fields will be lost in the process. + /// + [JsonExtensionData] + internal IDictionary AdditionalProperties { get; private set; } + private IReadOnlyList> partitionKeyPathTokens; private string id; diff --git a/Microsoft.Azure.Cosmos/src/Resource/Settings/DatabaseProperties.cs b/Microsoft.Azure.Cosmos/src/Resource/Settings/DatabaseProperties.cs index b07961cca6..e99f1bb9cc 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/Settings/DatabaseProperties.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/Settings/DatabaseProperties.cs @@ -5,8 +5,10 @@ namespace Microsoft.Azure.Cosmos { using System; + using System.Collections.Generic; using Microsoft.Azure.Documents; using Newtonsoft.Json; + using Newtonsoft.Json.Linq; /// /// Represents a database in the Azure Cosmos DB account. @@ -118,5 +120,12 @@ public string Id /// [JsonProperty(PropertyName = Constants.Properties.RId, NullValueHandling = NullValueHandling.Ignore)] internal string ResourceId { get; private set; } + + /// + /// This contains additional values for scenarios where the SDK is not aware of new fields. + /// This ensures that if resource is read and updated none of the fields will be lost in the process. + /// + [JsonExtensionData] + internal IDictionary AdditionalProperties { get; private set; } } } diff --git a/Microsoft.Azure.Cosmos/src/Resource/Settings/ExcludedPath.cs b/Microsoft.Azure.Cosmos/src/Resource/Settings/ExcludedPath.cs index e489e7b77f..837e84f275 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/Settings/ExcludedPath.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/Settings/ExcludedPath.cs @@ -4,8 +4,10 @@ namespace Microsoft.Azure.Cosmos { + using System.Collections.Generic; using Microsoft.Azure.Documents; using Newtonsoft.Json; + using Newtonsoft.Json.Linq; /// /// Specifies a path within a JSON document to be excluded while indexing data for the Azure Cosmos DB service. @@ -20,5 +22,12 @@ public sealed class ExcludedPath /// [JsonProperty(PropertyName = Constants.Properties.Path)] public string Path { get; set; } + + /// + /// This contains additional values for scenarios where the SDK is not aware of new fields. + /// This ensures that if resource is read and updated none of the fields will be lost in the process. + /// + [JsonExtensionData] + internal IDictionary AdditionalProperties { get; private set; } } } diff --git a/Microsoft.Azure.Cosmos/src/Resource/Settings/GeospatialConfig.cs b/Microsoft.Azure.Cosmos/src/Resource/Settings/GeospatialConfig.cs index 74c9f9ec9b..b78e4b7657 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/Settings/GeospatialConfig.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/Settings/GeospatialConfig.cs @@ -3,8 +3,10 @@ //------------------------------------------------------------ namespace Microsoft.Azure.Cosmos { + using System.Collections.Generic; using Newtonsoft.Json; using Newtonsoft.Json.Converters; + using Newtonsoft.Json.Linq; /// /// Represents geospatial configuration for a collection in the Azure Cosmos DB service @@ -55,5 +57,12 @@ public GeospatialType GeospatialType { get; set; } + + /// + /// This contains additional values for scenarios where the SDK is not aware of new fields. + /// This ensures that if resource is read and updated none of the fields will be lost in the process. + /// + [JsonExtensionData] + internal IDictionary AdditionalProperties { get; private set; } } } diff --git a/Microsoft.Azure.Cosmos/src/Resource/Settings/IncludedPath.cs b/Microsoft.Azure.Cosmos/src/Resource/Settings/IncludedPath.cs index 7cbc76dbba..577b290047 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/Settings/IncludedPath.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/Settings/IncludedPath.cs @@ -4,9 +4,11 @@ namespace Microsoft.Azure.Cosmos { + using System.Collections.Generic; using System.Collections.ObjectModel; using Microsoft.Azure.Documents; using Newtonsoft.Json; + using Newtonsoft.Json.Linq; /// /// Specifies a path within a JSON document to be included in the Azure Cosmos DB service. @@ -40,5 +42,12 @@ public sealed class IncludedPath /// [JsonProperty(PropertyName = Constants.Properties.IsFullIndex, NullValueHandling = NullValueHandling.Ignore)] internal bool? IsFullIndex { get; set; } + + /// + /// This contains additional values for scenarios where the SDK is not aware of new fields. + /// This ensures that if resource is read and updated none of the fields will be lost in the process. + /// + [JsonExtensionData] + internal IDictionary AdditionalProperties { get; private set; } } } diff --git a/Microsoft.Azure.Cosmos/src/Resource/Settings/Index.cs b/Microsoft.Azure.Cosmos/src/Resource/Settings/Index.cs index 4141a0e1fa..7b04846f1f 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/Settings/Index.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/Settings/Index.cs @@ -4,9 +4,11 @@ namespace Microsoft.Azure.Cosmos { + using System.Collections.Generic; using Microsoft.Azure.Documents; using Newtonsoft.Json; using Newtonsoft.Json.Converters; + using Newtonsoft.Json.Linq; /// /// Base class for IndexingPolicy Indexes in the Azure Cosmos DB service, you should use a concrete Index like HashIndex or RangeIndex. @@ -32,6 +34,13 @@ protected Index(IndexKind kind) [JsonConverter(typeof(StringEnumConverter))] public IndexKind Kind { get; set; } + /// + /// This contains additional values for scenarios where the SDK is not aware of new fields. + /// This ensures that if resource is read and updated none of the fields will be lost in the process. + /// + [JsonExtensionData] + internal IDictionary AdditionalProperties { get; private set; } + /// /// Returns an instance of the class with specified DataType for the Azure Cosmos DB service. /// diff --git a/Microsoft.Azure.Cosmos/src/Resource/Settings/IndexingPolicy.cs b/Microsoft.Azure.Cosmos/src/Resource/Settings/IndexingPolicy.cs index 82d8c67f31..fa56bdbb54 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/Settings/IndexingPolicy.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/Settings/IndexingPolicy.cs @@ -10,6 +10,7 @@ namespace Microsoft.Azure.Cosmos using Microsoft.Azure.Documents; using Newtonsoft.Json; using Newtonsoft.Json.Converters; + using Newtonsoft.Json.Linq; /// /// Represents the indexing policy configuration for a collection in the Azure Cosmos DB service. @@ -116,6 +117,13 @@ public IndexingPolicy() [JsonProperty(PropertyName = Constants.Properties.SpatialIndexes)] public Collection SpatialIndexes { get; internal set; } = new Collection(); + /// + /// This contains additional values for scenarios where the SDK is not aware of new fields. + /// This ensures that if resource is read and updated none of the fields will be lost in the process. + /// + [JsonExtensionData] + internal IDictionary AdditionalProperties { get; private set; } + #if INTERNAL /// /// Indexing policy annotation. @@ -141,7 +149,9 @@ public bool Equals(CompositePath compositePath1, CompositePath compositePath2) return false; } - if (compositePath1.Path == compositePath2.Path && compositePath2.Order == compositePath2.Order) + if (compositePath1.Path == compositePath2.Path && + compositePath1.Order == compositePath2.Order && + compositePath1.AdditionalProperties.EqualsTo(compositePath2.AdditionalProperties)) { return true; } @@ -237,7 +247,7 @@ public int GetHashCode(Collection> compositeIndexes) foreach (Collection compositePaths in compositeIndexes) { HashSet hashedCompositePaths = new HashSet(compositePaths, compositePathEqualityComparer); - hashCode = hashCode ^ compositePathsEqualityComparer.GetHashCode(hashedCompositePaths); + hashCode ^= compositePathsEqualityComparer.GetHashCode(hashedCompositePaths); } return hashCode; @@ -268,7 +278,8 @@ public bool Equals(SpatialPath spatialSpec1, SpatialPath spatialSpec2) HashSet hashedSpatialTypes1 = new HashSet(spatialSpec1.SpatialTypes); HashSet hashedSpatialTypes2 = new HashSet(spatialSpec2.SpatialTypes); - if (!hashedSpatialTypes1.SetEquals(hashedSpatialTypes2)) + if (!hashedSpatialTypes1.SetEquals(hashedSpatialTypes2) || + !spatialSpec1.AdditionalProperties.EqualsTo(spatialSpec2.AdditionalProperties)) { return false; } @@ -308,7 +319,7 @@ public bool Equals(Collection additionalSpatialIndexes1, Collection HashSet hashedAdditionalSpatialIndexes1 = new HashSet(additionalSpatialIndexes1, spatialSpecEqualityComparer); HashSet hashedAdditionalSpatialIndexes2 = new HashSet(additionalSpatialIndexes2, spatialSpecEqualityComparer); - return hashedAdditionalSpatialIndexes1.SetEquals(additionalSpatialIndexes2); + return hashedAdditionalSpatialIndexes1.SetEquals(hashedAdditionalSpatialIndexes2); } public int GetHashCode(Collection additionalSpatialIndexes) @@ -316,7 +327,7 @@ public int GetHashCode(Collection additionalSpatialIndexes) int hashCode = 0; foreach (SpatialPath spatialSpec in additionalSpatialIndexes) { - hashCode = hashCode ^ spatialSpecEqualityComparer.GetHashCode(spatialSpec); + hashCode ^= spatialSpecEqualityComparer.GetHashCode(spatialSpec); } return hashCode; @@ -339,7 +350,7 @@ public bool Equals(Index index1, Index index2) return false; } - if (index1.Kind != index2.Kind) + if (index1.Kind != index2.Kind || !index1.AdditionalProperties.EqualsTo(index2.AdditionalProperties)) { return false; } @@ -387,22 +398,22 @@ public bool Equals(Index index1, Index index2) public int GetHashCode(Index index) { int hashCode = 0; - hashCode = hashCode ^ (int)index.Kind; + hashCode ^= (int)index.Kind; switch (index.Kind) { case IndexKind.Hash: hashCode = hashCode ^ ((HashIndex)index).Precision ?? 0; - hashCode = hashCode ^ ((HashIndex)index).DataType.GetHashCode(); + hashCode ^= ((HashIndex)index).DataType.GetHashCode(); break; case IndexKind.Range: hashCode = hashCode ^ ((RangeIndex)index).Precision ?? 0; - hashCode = hashCode ^ ((RangeIndex)index).DataType.GetHashCode(); + hashCode ^= ((RangeIndex)index).DataType.GetHashCode(); break; case IndexKind.Spatial: - hashCode = hashCode ^ ((SpatialIndex)index).DataType.GetHashCode(); + hashCode ^= ((SpatialIndex)index).DataType.GetHashCode(); break; default: @@ -429,7 +440,8 @@ public bool Equals(IncludedPath includedPath1, IncludedPath includedPath2) return false; } - if (includedPath1.Path != includedPath2.Path) + if (includedPath1.Path != includedPath2.Path || + !includedPath1.AdditionalProperties.EqualsTo(includedPath2.AdditionalProperties)) { return false; } @@ -443,10 +455,10 @@ public bool Equals(IncludedPath includedPath1, IncludedPath includedPath2) public int GetHashCode(IncludedPath includedPath) { int hashCode = 0; - hashCode = hashCode ^ includedPath.Path.GetHashCode(); + hashCode ^= includedPath.Path.GetHashCode(); foreach (Index index in includedPath.Indexes) { - hashCode = hashCode ^ indexEqualityComparer.GetHashCode(index); + hashCode ^= indexEqualityComparer.GetHashCode(index); } return hashCode; @@ -469,7 +481,8 @@ public bool Equals(ExcludedPath excludedPath1, ExcludedPath excludedPath2) return false; } - return (excludedPath1.Path == excludedPath2.Path); + return excludedPath1.Path == excludedPath2.Path && + excludedPath1.AdditionalProperties.EqualsTo(excludedPath2.AdditionalProperties); } public int GetHashCode(ExcludedPath excludedPath1) @@ -494,11 +507,12 @@ public bool Equals(IndexingPolicy indexingPolicy1, IndexingPolicy indexingPolicy } bool isEqual = true; - isEqual &= (indexingPolicy1 != null && indexingPolicy2 != null); - isEqual &= (indexingPolicy1.Automatic == indexingPolicy2.Automatic); - isEqual &= (indexingPolicy1.IndexingMode == indexingPolicy2.IndexingMode); + isEqual &= indexingPolicy1 != null && indexingPolicy2 != null; + isEqual &= indexingPolicy1.Automatic == indexingPolicy2.Automatic; + isEqual &= indexingPolicy1.IndexingMode == indexingPolicy2.IndexingMode; isEqual &= compositeIndexesEqualityComparer.Equals(indexingPolicy1.CompositeIndexes, indexingPolicy2.CompositeIndexes); isEqual &= additionalSpatialIndexesEqualityComparer.Equals(indexingPolicy1.SpatialIndexes, indexingPolicy2.SpatialIndexes); + isEqual &= indexingPolicy1.AdditionalProperties.EqualsTo(indexingPolicy2.AdditionalProperties); HashSet includedPaths1 = new HashSet(indexingPolicy1.IncludedPaths, includedPathEqualityComparer); HashSet includedPaths2 = new HashSet(indexingPolicy2.IncludedPaths, includedPathEqualityComparer); @@ -514,19 +528,19 @@ public bool Equals(IndexingPolicy indexingPolicy1, IndexingPolicy indexingPolicy public int GetHashCode(IndexingPolicy indexingPolicy) { int hashCode = 0; - hashCode = hashCode ^ indexingPolicy.Automatic.GetHashCode(); - hashCode = hashCode ^ indexingPolicy.IndexingMode.GetHashCode(); - hashCode = hashCode ^ compositeIndexesEqualityComparer.GetHashCode(indexingPolicy.CompositeIndexes); - hashCode = hashCode ^ additionalSpatialIndexesEqualityComparer.GetHashCode(indexingPolicy.SpatialIndexes); + hashCode ^= indexingPolicy.Automatic.GetHashCode(); + hashCode ^= indexingPolicy.IndexingMode.GetHashCode(); + hashCode ^= compositeIndexesEqualityComparer.GetHashCode(indexingPolicy.CompositeIndexes); + hashCode ^= additionalSpatialIndexesEqualityComparer.GetHashCode(indexingPolicy.SpatialIndexes); foreach (IncludedPath includedPath in indexingPolicy.IncludedPaths) { - hashCode = hashCode ^ includedPathEqualityComparer.GetHashCode(includedPath); + hashCode ^= includedPathEqualityComparer.GetHashCode(includedPath); } foreach (ExcludedPath excludedPath in indexingPolicy.ExcludedPaths) { - hashCode = hashCode ^ excludedPathEqualityComparer.GetHashCode(excludedPath); + hashCode ^= excludedPathEqualityComparer.GetHashCode(excludedPath); } return hashCode; diff --git a/Microsoft.Azure.Cosmos/src/Resource/Settings/PermissionProperties.cs b/Microsoft.Azure.Cosmos/src/Resource/Settings/PermissionProperties.cs index c4a15dc331..0cf9c8029a 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/Settings/PermissionProperties.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/Settings/PermissionProperties.cs @@ -5,10 +5,12 @@ namespace Microsoft.Azure.Cosmos { using System; + using System.Collections.Generic; using System.Text; using Microsoft.Azure.Documents; using Newtonsoft.Json; using Newtonsoft.Json.Converters; + using Newtonsoft.Json.Linq; /// /// Represents a permission in the Azure Cosmos DB service. @@ -203,5 +205,12 @@ public PartitionKey? ResourcePartitionKey [JsonProperty(PropertyName = Constants.Properties.ResourcePartitionKey, NullValueHandling = NullValueHandling.Ignore)] internal Documents.Routing.PartitionKeyInternal InternalResourcePartitionKey { get; private set; } + + /// + /// This contains additional values for scenarios where the SDK is not aware of new fields. + /// This ensures that if resource is read and updated none of the fields will be lost in the process. + /// + [JsonExtensionData] + internal IDictionary AdditionalProperties { get; private set; } } } diff --git a/Microsoft.Azure.Cosmos/src/Resource/Settings/SpatialPath.cs b/Microsoft.Azure.Cosmos/src/Resource/Settings/SpatialPath.cs index e2d78cbb40..a7b79d9348 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/Settings/SpatialPath.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/Settings/SpatialPath.cs @@ -4,10 +4,12 @@ namespace Microsoft.Azure.Cosmos { using System; + using System.Collections.Generic; using System.Collections.ObjectModel; using Microsoft.Azure.Documents; using Newtonsoft.Json; using Newtonsoft.Json.Converters; + using Newtonsoft.Json.Linq; /// /// Spatial index specification @@ -54,15 +56,7 @@ public Collection SpatialTypes } return this.spatialTypesInternal; } - internal set - { - if (value == null) - { - throw new ArgumentNullException(); - } - - this.spatialTypesInternal = value; - } + internal set => this.spatialTypesInternal = value ?? throw new ArgumentNullException(); } /// @@ -73,5 +67,12 @@ public BoundingBoxProperties BoundingBox { get; set; } + + /// + /// This contains additional values for scenarios where the SDK is not aware of new fields. + /// This ensures that if resource is read and updated none of the fields will be lost in the process. + /// + [JsonExtensionData] + internal IDictionary AdditionalProperties { get; private set; } } } diff --git a/Microsoft.Azure.Cosmos/src/Resource/Settings/StoredProcedureProperties.cs b/Microsoft.Azure.Cosmos/src/Resource/Settings/StoredProcedureProperties.cs index 3249b4a716..a0c0803322 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/Settings/StoredProcedureProperties.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/Settings/StoredProcedureProperties.cs @@ -5,8 +5,10 @@ namespace Microsoft.Azure.Cosmos.Scripts { using System; + using System.Collections.Generic; using Microsoft.Azure.Documents; using Newtonsoft.Json; + using Newtonsoft.Json.Linq; /// /// Represents a stored procedure in the Azure Cosmos DB service. @@ -116,5 +118,12 @@ public string Id /// [JsonProperty(PropertyName = Constants.Properties.RId, NullValueHandling = NullValueHandling.Ignore)] internal string ResourceId { get; private set; } + + /// + /// This contains additional values for scenarios where the SDK is not aware of new fields. + /// This ensures that if resource is read and updated none of the fields will be lost in the process. + /// + [JsonExtensionData] + internal IDictionary AdditionalProperties { get; private set; } } } diff --git a/Microsoft.Azure.Cosmos/src/Resource/Settings/ThroughputProperties.cs b/Microsoft.Azure.Cosmos/src/Resource/Settings/ThroughputProperties.cs index e8bccead17..988cb1f3ce 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/Settings/ThroughputProperties.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/Settings/ThroughputProperties.cs @@ -5,8 +5,10 @@ namespace Microsoft.Azure.Cosmos { using System; + using System.Collections.Generic; using Microsoft.Azure.Documents; using Newtonsoft.Json; + using Newtonsoft.Json.Linq; /// /// Represents a throughput of the resources in the Azure Cosmos DB service. @@ -169,5 +171,12 @@ internal static ThroughputProperties CreateAutoscaleThroughput( /// [JsonProperty(PropertyName = Constants.Properties.OfferVersion, DefaultValueHandling = DefaultValueHandling.Ignore)] internal string OfferVersion { get; private set; } + + /// + /// This contains additional values for scenarios where the SDK is not aware of new fields. + /// This ensures that if resource is read and updated none of the fields will be lost in the process. + /// + [JsonExtensionData] + internal IDictionary AdditionalProperties { get; private set; } } } diff --git a/Microsoft.Azure.Cosmos/src/Resource/Settings/TriggerProperties.cs b/Microsoft.Azure.Cosmos/src/Resource/Settings/TriggerProperties.cs index befdd74be3..9d4e044de3 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/Settings/TriggerProperties.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/Settings/TriggerProperties.cs @@ -4,9 +4,11 @@ namespace Microsoft.Azure.Cosmos.Scripts { + using System.Collections.Generic; using Microsoft.Azure.Documents; using Newtonsoft.Json; using Newtonsoft.Json.Converters; + using Newtonsoft.Json.Linq; /// /// Represents a trigger in the Azure Cosmos DB service. @@ -88,5 +90,12 @@ public class TriggerProperties /// [JsonProperty(PropertyName = Constants.Properties.SelfLink, NullValueHandling = NullValueHandling.Ignore)] public string SelfLink { get; private set; } + + /// + /// This contains additional values for scenarios where the SDK is not aware of new fields. + /// This ensures that if resource is read and updated none of the fields will be lost in the process. + /// + [JsonExtensionData] + internal IDictionary AdditionalProperties { get; private set; } } } diff --git a/Microsoft.Azure.Cosmos/src/Resource/Settings/UniqueKey.cs b/Microsoft.Azure.Cosmos/src/Resource/Settings/UniqueKey.cs index e4d9ce358f..922bc48874 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/Settings/UniqueKey.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/Settings/UniqueKey.cs @@ -4,9 +4,11 @@ namespace Microsoft.Azure.Cosmos { + using System.Collections.Generic; using System.Collections.ObjectModel; using Microsoft.Azure.Documents; using Newtonsoft.Json; + using Newtonsoft.Json.Linq; /// /// Represents a unique key on that enforces uniqueness constraint on documents in the collection in the Azure Cosmos DB service. @@ -33,6 +35,13 @@ public class UniqueKey [JsonProperty(PropertyName = Constants.Properties.Paths)] public Collection Paths { get; internal set; } = new Collection(); + /// + /// This contains additional values for scenarios where the SDK is not aware of new fields. + /// This ensures that if resource is read and updated none of the fields will be lost in the process. + /// + [JsonExtensionData] + internal IDictionary AdditionalProperties { get; private set; } + #if INTERNAL /// /// Filter for sparse unique keys. diff --git a/Microsoft.Azure.Cosmos/src/Resource/Settings/UniqueKeyPolicy.cs b/Microsoft.Azure.Cosmos/src/Resource/Settings/UniqueKeyPolicy.cs index 0813b0550f..30bba1b604 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/Settings/UniqueKeyPolicy.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/Settings/UniqueKeyPolicy.cs @@ -3,9 +3,11 @@ //------------------------------------------------------------ namespace Microsoft.Azure.Cosmos { + using System.Collections.Generic; using System.Collections.ObjectModel; using Microsoft.Azure.Documents; using Newtonsoft.Json; + using Newtonsoft.Json.Linq; /// /// Represents the unique key policy configuration for specifying uniqueness constraints on documents in the collection in the Azure Cosmos DB service. @@ -25,5 +27,12 @@ public sealed class UniqueKeyPolicy /// [JsonProperty(PropertyName = Constants.Properties.UniqueKeys)] public Collection UniqueKeys { get; internal set; } = new Collection(); + + /// + /// This contains additional values for scenarios where the SDK is not aware of new fields. + /// This ensures that if resource is read and updated none of the fields will be lost in the process. + /// + [JsonExtensionData] + internal IDictionary AdditionalProperties { get; private set; } } } diff --git a/Microsoft.Azure.Cosmos/src/Resource/Settings/UserDefinedFunctionProperties.cs b/Microsoft.Azure.Cosmos/src/Resource/Settings/UserDefinedFunctionProperties.cs index e34498dccc..170a8bc815 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/Settings/UserDefinedFunctionProperties.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/Settings/UserDefinedFunctionProperties.cs @@ -4,8 +4,10 @@ namespace Microsoft.Azure.Cosmos.Scripts { + using System.Collections.Generic; using Microsoft.Azure.Documents; using Newtonsoft.Json; + using Newtonsoft.Json.Linq; /// /// Represents a user defined function in the Azure Cosmos service. @@ -102,5 +104,12 @@ public class UserDefinedFunctionProperties /// [JsonProperty(PropertyName = Constants.Properties.SelfLink, NullValueHandling = NullValueHandling.Ignore)] public string SelfLink { get; private set; } + + /// + /// This contains additional values for scenarios where the SDK is not aware of new fields. + /// This ensures that if resource is read and updated none of the fields will be lost in the process. + /// + [JsonExtensionData] + internal IDictionary AdditionalProperties { get; private set; } } } diff --git a/Microsoft.Azure.Cosmos/src/Resource/Settings/UserProperties.cs b/Microsoft.Azure.Cosmos/src/Resource/Settings/UserProperties.cs index 120ea09daf..7d5dab1df3 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/Settings/UserProperties.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/Settings/UserProperties.cs @@ -5,8 +5,10 @@ namespace Microsoft.Azure.Cosmos { using System; + using System.Collections.Generic; using Microsoft.Azure.Documents; using Newtonsoft.Json; + using Newtonsoft.Json.Linq; /// /// Represents a user in the Azure Cosmos DB service. @@ -113,5 +115,12 @@ public string Id /// /// The self-link of the permissions associated with the user. internal string PermissionsLink => $"{this.SelfLink?.TrimEnd('/')}/{ this.Permissions}"; + + /// + /// This contains additional values for scenarios where the SDK is not aware of new fields. + /// This ensures that if resource is read and updated none of the fields will be lost in the process. + /// + [JsonExtensionData] + internal IDictionary AdditionalProperties { get; private set; } } } diff --git a/Microsoft.Azure.Cosmos/src/Util/DictionaryExtensions.cs b/Microsoft.Azure.Cosmos/src/Util/DictionaryExtensions.cs new file mode 100644 index 0000000000..4e5b63d74d --- /dev/null +++ b/Microsoft.Azure.Cosmos/src/Util/DictionaryExtensions.cs @@ -0,0 +1,40 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos +{ + using System; + using System.Collections.Generic; + using System.Text; + using Newtonsoft.Json.Linq; + + internal static class DictionaryExtensions + { + /// + /// Compare two dictionaries and return true if they have same pair of key-values + /// + internal static bool EqualsTo(this IDictionary dict1, IDictionary dict2) + { + if (dict1 == null && dict2 == null) + { + return true; + } + + if (dict1 == null || dict2 == null || dict1.Count != dict2.Count) + { + return false; + } + + foreach (KeyValuePair pair in dict1) + { + if (!dict2.TryGetValue(pair.Key, out JToken value) || !JToken.DeepEquals(value, pair.Value)) + { + return false; + } + } + + return true; + } + } +} diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/IndexingPolicyEqualityComparer.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/IndexingPolicyEqualityComparer.cs index e8c39b950d..ee96fffcd1 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/IndexingPolicyEqualityComparer.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/IndexingPolicyEqualityComparer.cs @@ -25,7 +25,9 @@ public bool Equals(CompositePath compositePath1, CompositePath compositePath2) return false; } - if (compositePath1.Path == compositePath2.Path && compositePath2.Order == compositePath2.Order) + if (compositePath1.Path == compositePath2.Path && + compositePath1.Order == compositePath2.Order && + compositePath1.AdditionalProperties.EqualsTo(compositePath2.AdditionalProperties)) { return true; } @@ -121,7 +123,7 @@ public int GetHashCode(Collection> compositeIndexes) foreach (Collection compositePaths in compositeIndexes) { HashSet hashedCompositePaths = new HashSet(compositePaths, compositePathEqualityComparer); - hashCode = hashCode ^ compositePathsEqualityComparer.GetHashCode(hashedCompositePaths); + hashCode ^= compositePathsEqualityComparer.GetHashCode(hashedCompositePaths); } return hashCode; @@ -144,7 +146,8 @@ public bool Equals(SpatialPath spatialSpec1, SpatialPath spatialSpec2) return false; } - if (spatialSpec1.Path != spatialSpec2.Path) + if (spatialSpec1.Path != spatialSpec2.Path || + !spatialSpec1.AdditionalProperties.EqualsTo(spatialSpec2.AdditionalProperties)) { return false; } @@ -192,7 +195,7 @@ public bool Equals(Collection additionalSpatialSpeces1, Collection< HashSet hashedAdditionalSpatialSpeces1 = new HashSet(additionalSpatialSpeces1, spatialSpecEqualityComparer); HashSet hashedAdditionalSpatialSpeces2 = new HashSet(additionalSpatialSpeces2, spatialSpecEqualityComparer); - return hashedAdditionalSpatialSpeces1.SetEquals(additionalSpatialSpeces2); + return hashedAdditionalSpatialSpeces1.SetEquals(hashedAdditionalSpatialSpeces2); } public int GetHashCode(Collection additionalSpatialSpeces) @@ -200,7 +203,7 @@ public int GetHashCode(Collection additionalSpatialSpeces) int hashCode = 0; foreach (SpatialPath spatialSpec in additionalSpatialSpeces) { - hashCode = hashCode ^ spatialSpecEqualityComparer.GetHashCode(spatialSpec); + hashCode ^= spatialSpecEqualityComparer.GetHashCode(spatialSpec); } return hashCode; @@ -223,7 +226,8 @@ public bool Equals(Index index1, Index index2) return false; } - if (index1.Kind != index2.Kind) + if (index1.Kind != index2.Kind || + !index1.AdditionalProperties.EqualsTo(index2.AdditionalProperties)) { return false; } @@ -271,22 +275,22 @@ public bool Equals(Index index1, Index index2) public int GetHashCode(Index index) { int hashCode = 0; - hashCode = hashCode ^ (int)index.Kind; + hashCode ^= (int)index.Kind; switch (index.Kind) { case IndexKind.Hash: hashCode = hashCode ^ ((HashIndex)index).Precision ?? 0; - hashCode = hashCode ^ ((HashIndex)index).DataType.GetHashCode(); + hashCode ^= ((HashIndex)index).DataType.GetHashCode(); break; case IndexKind.Range: hashCode = hashCode ^ ((RangeIndex)index).Precision ?? 0; - hashCode = hashCode ^ ((RangeIndex)index).DataType.GetHashCode(); + hashCode ^= ((RangeIndex)index).DataType.GetHashCode(); break; case IndexKind.Spatial: - hashCode = hashCode ^ ((SpatialIndex)index).DataType.GetHashCode(); + hashCode ^= ((SpatialIndex)index).DataType.GetHashCode(); break; default: @@ -313,7 +317,8 @@ public bool Equals(IncludedPath includedPath1, IncludedPath includedPath2) return false; } - if (includedPath1.Path != includedPath2.Path) + if (includedPath1.Path != includedPath2.Path || + !includedPath1.AdditionalProperties.EqualsTo(includedPath2.AdditionalProperties)) { return false; } @@ -327,10 +332,10 @@ public bool Equals(IncludedPath includedPath1, IncludedPath includedPath2) public int GetHashCode(IncludedPath includedPath) { int hashCode = 0; - hashCode = hashCode ^ includedPath.Path.GetHashCode(); + hashCode ^= includedPath.Path.GetHashCode(); foreach (Cosmos.Index index in includedPath.Indexes) { - hashCode = hashCode ^ indexEqualityComparer.GetHashCode(index); + hashCode ^= indexEqualityComparer.GetHashCode(index); } return hashCode; @@ -353,7 +358,7 @@ public bool Equals(ExcludedPath excludedPath1, ExcludedPath excludedPath2) return false; } - return (excludedPath1.Path == excludedPath2.Path); + return excludedPath1.Path == excludedPath2.Path && excludedPath1.AdditionalProperties.EqualsTo(excludedPath2.AdditionalProperties); } public int GetHashCode(ExcludedPath excludedPath1) @@ -378,9 +383,10 @@ public bool Equals(IndexingPolicy indexingPolicy1, IndexingPolicy indexingPolicy } bool isEqual = true; - isEqual &= (indexingPolicy1 != null && indexingPolicy2 != null); - isEqual &= (indexingPolicy1.Automatic == indexingPolicy2.Automatic); - isEqual &= (indexingPolicy1.IndexingMode == indexingPolicy2.IndexingMode); + isEqual &= indexingPolicy1 != null && indexingPolicy2 != null; + isEqual &= indexingPolicy1.Automatic == indexingPolicy2.Automatic; + isEqual &= indexingPolicy1.IndexingMode == indexingPolicy2.IndexingMode; + isEqual &= indexingPolicy1.AdditionalProperties.EqualsTo(indexingPolicy2.AdditionalProperties); isEqual &= compositeIndexesEqualityComparer.Equals(indexingPolicy1.CompositeIndexes, indexingPolicy2.CompositeIndexes); isEqual &= additionalSpatialSpecesEqualityComparer.Equals(indexingPolicy1.SpatialIndexes, indexingPolicy2.SpatialIndexes); @@ -398,19 +404,19 @@ public bool Equals(IndexingPolicy indexingPolicy1, IndexingPolicy indexingPolicy public int GetHashCode(IndexingPolicy indexingPolicy) { int hashCode = 0; - hashCode = hashCode ^ indexingPolicy.Automatic.GetHashCode(); - hashCode = hashCode ^ indexingPolicy.IndexingMode.GetHashCode(); - hashCode = hashCode ^ compositeIndexesEqualityComparer.GetHashCode(indexingPolicy.CompositeIndexes); - hashCode = hashCode ^ additionalSpatialSpecesEqualityComparer.GetHashCode(indexingPolicy.SpatialIndexes); + hashCode ^= indexingPolicy.Automatic.GetHashCode(); + hashCode ^= indexingPolicy.IndexingMode.GetHashCode(); + hashCode ^= compositeIndexesEqualityComparer.GetHashCode(indexingPolicy.CompositeIndexes); + hashCode ^= additionalSpatialSpecesEqualityComparer.GetHashCode(indexingPolicy.SpatialIndexes); foreach (IncludedPath includedPath in indexingPolicy.IncludedPaths) { - hashCode = hashCode ^ includedPathEqualityComparer.GetHashCode(includedPath); + hashCode ^= includedPathEqualityComparer.GetHashCode(includedPath); } foreach (ExcludedPath excludedPath in indexingPolicy.ExcludedPaths) { - hashCode = hashCode ^ excludedPathEqualityComparer.GetHashCode(excludedPath); + hashCode ^= excludedPathEqualityComparer.GetHashCode(excludedPath); } return hashCode; diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/SettingsContractTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/SettingsContractTests.cs index 41ac4dbf0f..2232fc2ffa 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/SettingsContractTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/SettingsContractTests.cs @@ -11,6 +11,7 @@ namespace Microsoft.Azure.Cosmos.Tests using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; + using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; using System.Linq; @@ -62,13 +63,13 @@ public void ConflictsSettingsDefaults() [TestMethod] public void OperationKindMatchesDirect() { - AssertEnums(); + this.AssertEnums(); } [TestMethod] public void TriggerOperationMatchesDirect() { - AssertEnums(); + this.AssertEnums(); } [TestMethod] @@ -216,6 +217,7 @@ public void ContainerSettingsSimpleTest() // Two equivalent definitions ContainerProperties cosmosContainerSettings = new ContainerProperties(id, pkPath); + DocumentCollection collection = new DocumentCollection() { Id = id, @@ -239,10 +241,371 @@ public void ContainerSettingsSimpleTest() Assert.AreEqual(cosmosContainerSettings.PartitionKeyPath, collectionDeser.PartitionKey.Paths[0]); } + [TestMethod] + public void ValidateAdditionalPropertiesAttributeInPropertiesFiles() + { + IEnumerable allClasses = from t in Assembly.GetAssembly(typeof(CosmosClient)).GetTypes() + where t.IsClass && + t.IsPublic && + !t.IsAbstract + where t.Name.EndsWith("Properties") + select t; + + foreach(Type className in allClasses) + { + SettingsContractTests.ValidateAdditionalProperties(className); + } + + } + + /// + /// All property types must have an AdditionalProperties with newtonsoft attribute to ensure that an old SDK does not lose any fields that a newer contract may have. + /// + /// + private static void ValidateAdditionalProperties(Type className) + { + PropertyInfo property = className.GetProperty("AdditionalProperties", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); + Assert.IsTrue(property != null, "AdditionalProperties property is not there for " + className); + Assert.AreEqual("Newtonsoft.Json.JsonExtensionDataAttribute", property.CustomAttributes.First().AttributeType.FullName, "AdditionalProperties property is not Newtonsoft.JsonJsonExtensionDataAttribute"); + + PropertyInfo[] propertyInfoArr = className.GetProperties(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); + foreach (PropertyInfo propInfo in propertyInfoArr) + { + ValidateProperty(propInfo.PropertyType); + } + } + + private static void ValidateProperty(Type propInfoType) + { + if (propInfoType.ToString().Contains("Microsoft.Azure.Cosmos") && + (propInfoType.BaseType == null || propInfoType.BaseType.Name.Equals("Object"))) + { + if (propInfoType.GenericTypeArguments.Length == 0) + { + SettingsContractTests.ValidateAdditionalProperties(propInfoType); + } + else + { + foreach (Type genericTypeArgs in propInfoType.GenericTypeArguments) + { + SettingsContractTests.ValidateProperty(genericTypeArgs); + } + + } + } + } + + [TestMethod] + public void SettingsDeserializeWithAdditionalDataTest() + { + this.DeserializeWithAdditionalDataTest(); + this.DeserializeWithAdditionalDataTest(); + this.DeserializeWithAdditionalDataTest(); + this.DeserializeWithAdditionalDataTest(); + this.DeserializeWithAdditionalDataTest(); + this.DeserializeWithAdditionalDataTest(); + } + + [TestMethod] + public void AccountPropertiesDeserializeWithAdditionalDataTest() + { + string cosmosSerialized = "{\"id\":\"2a9f501b-6948-4795-8fd1-797defb5c466\",\"writableLocations\":[],\"readableLocations\":[{\"name\":\"region1\",\"additionalRegion\":\"regionValue\",\"databaseAccountEndpoint\":null}],\"userConsistencyPolicy\":{\"defaultConsistencyLevel\":\"Strong\",\"maxStalenessPrefix\":0,\"additionalConsistency\":\"consistencyValue\",\"maxIntervalInSeconds\":1},\"addresses\":null,\"userReplicationPolicy\":null,\"systemReplicationPolicy\":null,\"readPolicy\":null,\"queryEngineConfiguration\":null,\"enableMultipleWriteLocations\":false}"; + + JObject complexObject = JObject.FromObject(new { id = 1, name = new { fname = "fname", lname = "lname" } }); + + // Adding additional information + JObject jobject = JObject.Parse(cosmosSerialized); + jobject.Add(new JProperty("simple string", "policy value")); + jobject.Add(new JProperty("complex object", complexObject)); + + // Serialized string + cosmosSerialized = SettingsContractTests.CosmosSerialize(jobject); + + AccountProperties containerDeserSettings = SettingsContractTests.CosmosDeserialize(cosmosSerialized); + + Assert.AreEqual("2a9f501b-6948-4795-8fd1-797defb5c466", containerDeserSettings.Id); + Assert.AreEqual(2, containerDeserSettings.AdditionalProperties.Count); + Assert.AreEqual("policy value", (string)containerDeserSettings.AdditionalProperties["simple string"]); + Assert.AreEqual(complexObject.ToString(), JObject.FromObject(containerDeserSettings.AdditionalProperties["complex object"]).ToString()); + + Assert.AreEqual(1, containerDeserSettings.ReadableRegions.First().AdditionalProperties.Count); + Assert.AreEqual("regionValue", containerDeserSettings.ReadableRegions.First().AdditionalProperties["additionalRegion"]); + + Assert.AreEqual(1, containerDeserSettings.Consistency.AdditionalProperties.Count); + Assert.AreEqual("consistencyValue", containerDeserSettings.Consistency.AdditionalProperties["additionalConsistency"]); + + } + + [TestMethod] + public void ContainerPropertiesDeserializeWithAdditionalDataTest() + { + string cosmosSerialized = "{\"indexingPolicy\":{\"automatic\":true,\"indexingMode\":\"Consistent\",\"additionalIndexPolicy\":\"indexpolicyvalue\",\"includedPaths\":[{\"path\":\"/included/path\",\"additionalIncludedPath\":\"includedPathValue\",\"indexes\":[]}],\"excludedPaths\":[{\"path\":\"/excluded/path\",\"additionalExcludedPath\":\"excludedPathValue\"}],\"compositeIndexes\":[[{\"path\":\"/composite/path\",\"additionalCompositeIndex\":\"compositeIndexValue\",\"order\":\"ascending\"}]],\"spatialIndexes\":[{\"path\":\"/spatial/path\",\"additionalSpatialIndexes\":\"spatialIndexValue\",\"types\":[]}]},\"geospatialConfig\":{\"type\":\"Geography\",\"additionalGeospatialConfig\":\"geospatialConfigValue\"},\"uniqueKeyPolicy\":{\"additionalUniqueKeyPolicy\":\"uniqueKeyPolicyValue\",\"uniqueKeys\":[{\"paths\":[\"/unique/key/path/1\",\"/unique/key/path/2\"]}]},\"conflictResolutionPolicy\":{\"mode\":\"LastWriterWins\",\"additionalConflictResolutionPolicy\":\"conflictResolutionValue\"},\"clientEncryptionPolicy\":{\"includedPaths\":[{\"path\":\"/path\",\"clientEncryptionKeyId\":\"clientEncryptionKeyId\",\"encryptionType\":\"Randomized\",\"additionalIncludedPath\":\"includedPathValue\",\"encryptionAlgorithm\":\"AEAD_AES_256_CBC_HMAC_SHA256\"}],\"policyFormatVersion\":1,\"additionalEncryptionPolicy\":\"clientEncryptionpolicyValue\"},\"id\":\"2a9f501b-6948-4795-8fd1-797defb5c466\",\"partitionKey\":{\"paths\":[],\"kind\":\"Hash\"}}"; + + JObject complexObject = JObject.FromObject(new { id = 1, name = new { fname = "fname", lname = "lname" } }); + + // Adding additional information + JObject jobject = JObject.Parse(cosmosSerialized); + jobject.Add(new JProperty("simple string", "policy value")); + jobject.Add(new JProperty("complex object", complexObject)); + + // Serialized string + cosmosSerialized = SettingsContractTests.CosmosSerialize(jobject); + + ContainerProperties containerDeserSettings = SettingsContractTests.CosmosDeserialize(cosmosSerialized); + + Assert.AreEqual("2a9f501b-6948-4795-8fd1-797defb5c466", containerDeserSettings.Id); + + Assert.AreEqual(2, containerDeserSettings.AdditionalProperties.Count); + Assert.AreEqual("policy value", (string)containerDeserSettings.AdditionalProperties["simple string"]); + Assert.AreEqual(complexObject.ToString(), JObject.FromObject(containerDeserSettings.AdditionalProperties["complex object"]).ToString()); + + Assert.AreEqual(1, containerDeserSettings.IndexingPolicy.AdditionalProperties.Count); + Assert.AreEqual("indexpolicyvalue", containerDeserSettings.IndexingPolicy.AdditionalProperties["additionalIndexPolicy"]); + + Assert.AreEqual(1, containerDeserSettings.IndexingPolicy.SpatialIndexes[0].AdditionalProperties.Count); + Assert.AreEqual("spatialIndexValue", containerDeserSettings.IndexingPolicy.SpatialIndexes[0].AdditionalProperties["additionalSpatialIndexes"]); + + Assert.AreEqual(1, containerDeserSettings.IndexingPolicy.CompositeIndexes[0][0].AdditionalProperties.Count); + Assert.AreEqual("compositeIndexValue", containerDeserSettings.IndexingPolicy.CompositeIndexes[0][0].AdditionalProperties["additionalCompositeIndex"]); + + Assert.AreEqual(1, containerDeserSettings.IndexingPolicy.IncludedPaths[0].AdditionalProperties.Count); + Assert.AreEqual("includedPathValue", containerDeserSettings.IndexingPolicy.IncludedPaths[0].AdditionalProperties["additionalIncludedPath"]); + + Assert.AreEqual(1, containerDeserSettings.IndexingPolicy.ExcludedPaths[0].AdditionalProperties.Count); + Assert.AreEqual("excludedPathValue", containerDeserSettings.IndexingPolicy.ExcludedPaths[0].AdditionalProperties["additionalExcludedPath"]); + + Assert.AreEqual(1, containerDeserSettings.GeospatialConfig.AdditionalProperties.Count); + Assert.AreEqual("geospatialConfigValue", containerDeserSettings.GeospatialConfig.AdditionalProperties["additionalGeospatialConfig"]); + + Assert.AreEqual(1, containerDeserSettings.UniqueKeyPolicy.AdditionalProperties.Count); + Assert.AreEqual("uniqueKeyPolicyValue", containerDeserSettings.UniqueKeyPolicy.AdditionalProperties["additionalUniqueKeyPolicy"]); + + Assert.AreEqual(1, containerDeserSettings.ConflictResolutionPolicy.AdditionalProperties.Count); + Assert.AreEqual("conflictResolutionValue", containerDeserSettings.ConflictResolutionPolicy.AdditionalProperties["additionalConflictResolutionPolicy"]); + + Assert.AreEqual(1, containerDeserSettings.ClientEncryptionPolicy.AdditionalProperties.Count); + Assert.AreEqual("clientEncryptionpolicyValue", containerDeserSettings.ClientEncryptionPolicy.AdditionalProperties["additionalEncryptionPolicy"]); + + Assert.AreEqual(1, containerDeserSettings.ClientEncryptionPolicy.IncludedPaths.First().AdditionalProperties.Count); + Assert.AreEqual("includedPathValue", containerDeserSettings.ClientEncryptionPolicy.IncludedPaths.First().AdditionalProperties["additionalIncludedPath"]); + } + + [TestMethod] + public void ClientEncryptionKeyPropertiesDeserializeWithAdditionalDataTest() + { + string cosmosSerialized = "{\"id\":\"id\",\"encryptionAlgorithm\":\"encryptionAlgorithm\",\"wrappedDataEncryptionKey\":\"AA==\",\"keyWrapMetadata\":{\"type\":\"type\",\"name\":\"name\",\"value\":\"value\", \"additional\":\"value\"}}"; + + JObject complexObject = JObject.FromObject(new { id = 1, name = new { fname = "fname", lname = "lname" } }); + + // Adding additional information + JObject jobject = JObject.Parse(cosmosSerialized); + jobject.Add(new JProperty("simple string", "policy value")); + jobject.Add(new JProperty("complex object", complexObject)); + + // Serialized string + string modifiedCosmosSerialized = SettingsContractTests.CosmosSerialize(jobject); + + ClientEncryptionKeyProperties deserSettings = SettingsContractTests.CosmosDeserialize(modifiedCosmosSerialized); + ClientEncryptionKeyProperties deserSettingsIntance2 = SettingsContractTests.CosmosDeserialize(modifiedCosmosSerialized); + + Assert.AreEqual("id", deserSettings.Id); + Assert.AreEqual("encryptionAlgorithm", deserSettings.EncryptionAlgorithm); + Assert.AreEqual(1, deserSettings.WrappedDataEncryptionKey.Length); + + Assert.AreEqual("type", deserSettings.EncryptionKeyWrapMetadata.Type); + Assert.AreEqual("name", deserSettings.EncryptionKeyWrapMetadata.Name); + Assert.AreEqual("value", deserSettings.EncryptionKeyWrapMetadata.Value); + Assert.AreEqual(1, deserSettings.EncryptionKeyWrapMetadata.AdditionalProperties.Count); + Assert.AreEqual("value", deserSettings.EncryptionKeyWrapMetadata.AdditionalProperties["additional"]); + + Assert.AreEqual(2, deserSettings.AdditionalProperties.Count); + Assert.AreEqual("policy value", (string)deserSettings.AdditionalProperties["simple string"]); + Assert.AreEqual(complexObject.ToString(), JObject.FromObject(deserSettings.AdditionalProperties["complex object"]).ToString()); + + Assert.AreEqual(deserSettings, deserSettingsIntance2); // Testing equal function changes + + JObject newComplexObject = JObject.FromObject(new { id = 1, name = new { fname = "fname", lname = "lname1" } }); + // Adding additional information + JObject jobject_for_non_equality = JObject.Parse(cosmosSerialized); + jobject_for_non_equality.Add(new JProperty("simple string", "policy value")); + jobject_for_non_equality.Add(new JProperty("complex object", newComplexObject)); + + // Serialized string + string modifiedForNonEqualityCheckCosmosSerialized = SettingsContractTests.CosmosSerialize(jobject_for_non_equality); + ClientEncryptionKeyProperties deserSettingsIntance3 = SettingsContractTests.CosmosDeserialize(modifiedForNonEqualityCheckCosmosSerialized); + + Assert.AreNotEqual(deserSettingsIntance2, deserSettingsIntance3); // Testing equal function changes + } + + [TestMethod] + public void TriggerPropertiesDeserializeWithAdditionalDataTest() + { + TriggerProperties triggerProperties = new TriggerProperties + { + Body = "body" + }; + + string cosmosSerialized = SettingsContractTests.CosmosSerialize(triggerProperties); + + JObject complexObject = JObject.FromObject(new { id = 1, name = new { fname = "fname", lname = "lname" } }); + + // Adding additional information + JObject jobject = JObject.Parse(cosmosSerialized); + jobject.Add(new JProperty("simple string", "policy value")); + jobject.Add(new JProperty("complex object", complexObject)); + + // Serialized string + cosmosSerialized = SettingsContractTests.CosmosSerialize(jobject); + + TriggerProperties deserSettings = SettingsContractTests.CosmosDeserialize(cosmosSerialized); + + Assert.AreEqual("body", deserSettings.Body); + Assert.AreEqual(2, deserSettings.AdditionalProperties.Count); + Assert.AreEqual("policy value", (string)deserSettings.AdditionalProperties["simple string"]); + Assert.AreEqual(complexObject.ToString(), JObject.FromObject(deserSettings.AdditionalProperties["complex object"]).ToString()); + } + + [TestMethod] + public void ThroughputPropertiesDeserializeWithAdditionalDataTest() + { + ThroughputProperties manualThroughputProperties = ThroughputProperties.CreateManualThroughput(1); + ThroughputProperties autoscaleThroughputProperties = ThroughputProperties.CreateAutoscaleThroughput(2); + + string cosmosManualSerialized = SettingsContractTests.CosmosSerialize(manualThroughputProperties); + string cosmosAutoscaleSerialized = SettingsContractTests.CosmosSerialize(autoscaleThroughputProperties); + + JObject complexObject = JObject.FromObject(new { id = 1, name = new { fname = "fname", lname = "lname" } }); + + // Adding additional information + JObject manualJobject = JObject.Parse(cosmosManualSerialized); + manualJobject.Add(new JProperty("simple string", "policy value")); + manualJobject.Add(new JProperty("complex object", complexObject)); + + JObject autoscaleJobject = JObject.Parse(cosmosAutoscaleSerialized); + autoscaleJobject.Add(new JProperty("simple string", "policy value")); + autoscaleJobject.Add(new JProperty("complex object", complexObject)); + + // Serialized string + cosmosManualSerialized = SettingsContractTests.CosmosSerialize(manualJobject); + cosmosAutoscaleSerialized = SettingsContractTests.CosmosSerialize(autoscaleJobject); + + ThroughputProperties manualDeserSettings = SettingsContractTests.CosmosDeserialize(cosmosManualSerialized); + ThroughputProperties autoscaleDeserSettings = SettingsContractTests.CosmosDeserialize(cosmosAutoscaleSerialized); + + Assert.AreEqual(1, manualDeserSettings.Content.OfferThroughput); + Assert.AreEqual(2, manualDeserSettings.AdditionalProperties.Count); + Assert.AreEqual("policy value", (string)manualDeserSettings.AdditionalProperties["simple string"]); + Assert.AreEqual(complexObject.ToString(), JObject.FromObject(manualDeserSettings.AdditionalProperties["complex object"]).ToString()); + + Assert.AreEqual(2, autoscaleDeserSettings.Content.OfferAutoscaleSettings.MaxThroughput); + Assert.AreEqual(2, autoscaleDeserSettings.AdditionalProperties.Count); + Assert.AreEqual("policy value", (string)autoscaleDeserSettings.AdditionalProperties["simple string"]); + Assert.AreEqual(complexObject.ToString(), JObject.FromObject(autoscaleDeserSettings.AdditionalProperties["complex object"]).ToString()); + } + + [TestMethod] + public void BoundingBoxPropertiesDeserializeWithAdditionalDataTest() + { + BoundingBoxProperties boundingBoxProperties = new BoundingBoxProperties + { + Xmin = 10 + }; + + string cosmosSerialized = SettingsContractTests.CosmosSerialize(boundingBoxProperties); + + JObject complexObject = JObject.FromObject(new { id = 1, name = new { fname = "fname", lname = "lname" } }); + + // Adding additional information + JObject jobject = JObject.Parse(cosmosSerialized); + jobject.Add(new JProperty("simple string", "policy value")); + jobject.Add(new JProperty("complex object", complexObject)); + + // Serialized string + cosmosSerialized = SettingsContractTests.CosmosSerialize(jobject); + + BoundingBoxProperties deserSettings = SettingsContractTests.CosmosDeserialize(cosmosSerialized); + + Assert.AreEqual(10, deserSettings.Xmin); + Assert.AreEqual(2, deserSettings.AdditionalProperties.Count); + Assert.AreEqual("policy value", (string)deserSettings.AdditionalProperties["simple string"]); + Assert.AreEqual(complexObject.ToString(), JObject.FromObject(deserSettings.AdditionalProperties["complex object"]).ToString()); + } + + [TestMethod] + public void OfferContentPropertiesDeserializeWithAdditionalDataTest() + { + OfferContentProperties offerContentProperties = OfferContentProperties.CreateManualOfferConent(1); + + string cosmosSerialized = SettingsContractTests.CosmosSerialize(offerContentProperties); + + JObject complexObject = JObject.FromObject(new { id = 1, name = new { fname = "fname", lname = "lname" } }); + + // Adding additional information + JObject jobject = JObject.Parse(cosmosSerialized); + jobject.Add(new JProperty("simple string", "policy value")); + jobject.Add(new JProperty("complex object", complexObject)); + + // Serialized string + cosmosSerialized = SettingsContractTests.CosmosSerialize(jobject); + + OfferContentProperties containerDeserSettings = SettingsContractTests.CosmosDeserialize(cosmosSerialized); + + Assert.AreEqual(1, containerDeserSettings.OfferThroughput); + Assert.AreEqual(2, containerDeserSettings.AdditionalProperties.Count); + Assert.AreEqual("policy value", (string)containerDeserSettings.AdditionalProperties["simple string"]); + Assert.AreEqual(complexObject.ToString(), JObject.FromObject(containerDeserSettings.AdditionalProperties["complex object"]).ToString()); + } + + [TestMethod] + public void OfferAutoscaleAutoUpgradePropertiesDeserializeWithAdditionalDataTest() + { + string cosmosSerialized = "{\"throughputPolicy\":{\"incrementPercent\":1, \"additional\":\"property\"}}"; + + JObject complexObject = JObject.FromObject(new { id = 1, name = new { fname = "fname", lname = "lname" } }); + + // Adding additional information + JObject jobject = JObject.Parse(cosmosSerialized); + jobject.Add(new JProperty("simple string", "policy value")); + jobject.Add(new JProperty("complex object", complexObject)); + + // Serialized string + cosmosSerialized = SettingsContractTests.CosmosSerialize(jobject); + + OfferAutoscaleAutoUpgradeProperties containerDeserSettings = SettingsContractTests.CosmosDeserialize(cosmosSerialized); + + Assert.AreEqual(1, containerDeserSettings.ThroughputProperties.IncrementPercent); + Assert.AreEqual(2, containerDeserSettings.AdditionalProperties.Count); + Assert.AreEqual("policy value", (string)containerDeserSettings.AdditionalProperties["simple string"]); + Assert.AreEqual(complexObject.ToString(), JObject.FromObject(containerDeserSettings.AdditionalProperties["complex object"]).ToString()); + Assert.AreEqual("property", (string)containerDeserSettings.ThroughputProperties.AdditionalProperties["additional"]); + } + + private void DeserializeWithAdditionalDataTest() + { + string cosmosSerialized = "{\"id\":\"2a9f501b-6948-4795-8fd1-797defb5c466\"}"; + + JObject complexObject = JObject.FromObject(new { id = 1, name = new { fname = "fname", lname = "lname" } }); + + // Adding additional information + JObject jobject = JObject.Parse(cosmosSerialized); + jobject.Add(new JProperty("simple string", "policy value")); + jobject.Add(new JProperty("complex object", complexObject)); + + // Serialized string + cosmosSerialized = SettingsContractTests.CosmosSerialize(jobject); + + dynamic containerDeserSettings = SettingsContractTests.CosmosDeserialize(cosmosSerialized); + + Assert.AreEqual("2a9f501b-6948-4795-8fd1-797defb5c466", containerDeserSettings.Id); + Assert.AreEqual(2, containerDeserSettings.AdditionalProperties.Count); + Assert.AreEqual("policy value", (string)containerDeserSettings.AdditionalProperties["simple string"]); + Assert.AreEqual(complexObject.ToString(), JObject.FromObject(containerDeserSettings.AdditionalProperties["complex object"]).ToString()); + } + [TestMethod] public void PartitionKeyDefinitionVersionValuesTest() { - AssertEnums(); + this.AssertEnums(); } [TestMethod] @@ -545,17 +908,19 @@ public async Task ContainerV2CompatTest() [TestMethod] public void CosmosAccountSettingsSerializationTest() { - AccountProperties cosmosAccountSettings = new AccountProperties(); - cosmosAccountSettings.Id = "someId"; - cosmosAccountSettings.EnableMultipleWriteLocations = true; - cosmosAccountSettings.ResourceId = "/uri"; - cosmosAccountSettings.ETag = "etag"; - cosmosAccountSettings.WriteLocationsInternal = new Collection() { new AccountRegion() { Name = "region1", Endpoint = "endpoint1" } }; - cosmosAccountSettings.ReadLocationsInternal = new Collection() { new AccountRegion() { Name = "region2", Endpoint = "endpoint2" } }; - cosmosAccountSettings.AddressesLink = "link"; - cosmosAccountSettings.Consistency = new AccountConsistency() { DefaultConsistencyLevel = Cosmos.ConsistencyLevel.BoundedStaleness }; - cosmosAccountSettings.ReplicationPolicy = new ReplicationPolicy() { AsyncReplication = true }; - cosmosAccountSettings.ReadPolicy = new ReadPolicy() { PrimaryReadCoefficient = 10 }; + AccountProperties cosmosAccountSettings = new AccountProperties + { + Id = "someId", + EnableMultipleWriteLocations = true, + ResourceId = "/uri", + ETag = "etag", + WriteLocationsInternal = new Collection() { new AccountRegion() { Name = "region1", Endpoint = "endpoint1" } }, + ReadLocationsInternal = new Collection() { new AccountRegion() { Name = "region2", Endpoint = "endpoint2" } }, + AddressesLink = "link", + Consistency = new AccountConsistency() { DefaultConsistencyLevel = Cosmos.ConsistencyLevel.BoundedStaleness }, + ReplicationPolicy = new ReplicationPolicy() { AsyncReplication = true }, + ReadPolicy = new ReadPolicy() { PrimaryReadCoefficient = 10 } + }; string cosmosSerialized = SettingsContractTests.CosmosSerialize(cosmosAccountSettings); @@ -654,7 +1019,6 @@ public void ChangeFeedPolicySerialization_Disabled() string serialization = JsonConvert.SerializeObject(containerSettings); Assert.IsFalse(serialization.Contains(Constants.Properties.ChangeFeedPolicy), "Change Feed Policy should not be included by default"); - TimeSpan desiredTimeSpan = TimeSpan.FromHours(1); containerSettings.ChangeFeedPolicy = new Cosmos.ChangeFeedPolicy() { FullFidelityRetention = Cosmos.ChangeFeedPolicy.FullFidelityNoRetention }; string serializationWithValues = JsonConvert.SerializeObject(containerSettings); Assert.IsTrue(serializationWithValues.Contains(Constants.Properties.ChangeFeedPolicy), "Change Feed Policy should be included");