public override async Task <ItemResponse <T> > UpsertItemAsync <T>(
            T item,
            PartitionKey?partitionKey           = null,
            ItemRequestOptions requestOptions   = null,
            CancellationToken cancellationToken = default)
        {
            if (item == null)
            {
                throw new ArgumentNullException(nameof(item));
            }

            if (!(requestOptions is EncryptionItemRequestOptions encryptionItemRequestOptions) ||
                encryptionItemRequestOptions.EncryptionOptions == null)
            {
                return(await this.container.UpsertItemAsync(
                           item,
                           partitionKey,
                           requestOptions,
                           cancellationToken));
            }

            if (partitionKey == null)
            {
                throw new NotSupportedException($"{nameof(partitionKey)} cannot be null for operations using {nameof(EncryptionContainer)}.");
            }

            CosmosDiagnosticsContext diagnosticsContext = CosmosDiagnosticsContext.Create(requestOptions);

            using (diagnosticsContext.CreateScope("UpsertItem"))
            {
                ResponseMessage responseMessage;

                if (item is EncryptableItem encryptableItem)
                {
                    using (Stream streamPayload = encryptableItem.ToStream(this.CosmosSerializer))
                    {
                        responseMessage = await this.UpsertItemHelperAsync(
                            streamPayload,
                            partitionKey.Value,
                            requestOptions,
                            decryptResponse : false,
                            diagnosticsContext,
                            cancellationToken);
                    }

                    encryptableItem.SetDecryptableItem(
                        EncryptionProcessor.BaseSerializer.FromStream <JObject>(responseMessage.Content),
                        this.Encryptor,
                        this.CosmosSerializer);

                    return(new EncryptionItemResponse <T>(
                               responseMessage,
                               item));
                }
                else
                {
                    using (Stream itemStream = this.CosmosSerializer.ToStream <T>(item))
                    {
                        responseMessage = await this.UpsertItemHelperAsync(
                            itemStream,
                            partitionKey.Value,
                            requestOptions,
                            decryptResponse : true,
                            diagnosticsContext,
                            cancellationToken);
                    }

                    return(this.ResponseFactory.CreateItemResponse <T>(responseMessage));
                }
            }
        }
        /// <remarks>
        /// If there isn't any PathsToEncrypt, input stream will be returned without any modification.
        /// Else input stream will be disposed, and a new stream is returned.
        /// In case of an exception, input stream won't be disposed, but position will be end of stream.
        /// </remarks>
        public static async Task <Stream> EncryptAsync(
            Stream input,
            Encryptor encryptor,
            EncryptionOptions encryptionOptions,
            CosmosDiagnosticsContext diagnosticsContext,
            CancellationToken cancellationToken)
        {
            EncryptionProcessor.ValidateInputForEncrypt(
                input,
                encryptor,
                encryptionOptions);

            if (!encryptionOptions.PathsToEncrypt.Any())
            {
                return(input);
            }

            if (!encryptionOptions.PathsToEncrypt.Distinct().SequenceEqual(encryptionOptions.PathsToEncrypt))
            {
                throw new InvalidOperationException("Duplicate paths in PathsToEncrypt passed via EncryptionOptions.");
            }

            foreach (string path in encryptionOptions.PathsToEncrypt)
            {
                if (string.IsNullOrWhiteSpace(path) || path[0] != '/' || path.LastIndexOf('/') != 0)
                {
                    throw new InvalidOperationException($"Invalid path {path ?? string.Empty}, {nameof(encryptionOptions.PathsToEncrypt)}");
                }

                if (string.Equals(path.Substring(1), "id"))
                {
                    throw new InvalidOperationException($"{nameof(encryptionOptions.PathsToEncrypt)} includes a invalid path: '{path}'.");
                }
            }

            JObject              itemJObj             = EncryptionProcessor.BaseSerializer.FromStream <JObject>(input);
            List <string>        pathsEncrypted       = new List <string>();
            EncryptionProperties encryptionProperties = null;

            byte[]     plainText  = null;
            byte[]     cipherText = null;
            TypeMarker typeMarker;

            switch (encryptionOptions.EncryptionAlgorithm)
            {
            case CosmosEncryptionAlgorithm.MdeAeadAes256CbcHmac256Randomized:

                foreach (string pathToEncrypt in encryptionOptions.PathsToEncrypt)
                {
                    string propertyName = pathToEncrypt.Substring(1);
                    if (!itemJObj.TryGetValue(propertyName, out JToken propertyValue))
                    {
                        continue;
                    }

                    if (propertyValue.Type == JTokenType.Null)
                    {
                        continue;
                    }

                    (typeMarker, plainText) = EncryptionProcessor.Serialize(propertyValue);

                    cipherText = await encryptor.EncryptAsync(
                        plainText,
                        encryptionOptions.DataEncryptionKeyId,
                        encryptionOptions.EncryptionAlgorithm);

                    if (cipherText == null)
                    {
                        throw new InvalidOperationException($"{nameof(Encryptor)} returned null cipherText from {nameof(EncryptAsync)}.");
                    }

                    byte[] cipherTextWithTypeMarker = new byte[cipherText.Length + 1];
                    cipherTextWithTypeMarker[0] = (byte)typeMarker;
                    Buffer.BlockCopy(cipherText, 0, cipherTextWithTypeMarker, 1, cipherText.Length);
                    itemJObj[propertyName] = cipherTextWithTypeMarker;
                    pathsEncrypted.Add(pathToEncrypt);
                }

                encryptionProperties = new EncryptionProperties(
                    encryptionFormatVersion: 3,
                    encryptionOptions.EncryptionAlgorithm,
                    encryptionOptions.DataEncryptionKeyId,
                    encryptedData: null,
                    pathsEncrypted);
                break;

            case CosmosEncryptionAlgorithm.AEAes256CbcHmacSha256Randomized:

                JObject toEncryptJObj = new JObject();

                foreach (string pathToEncrypt in encryptionOptions.PathsToEncrypt)
                {
                    string propertyName = pathToEncrypt.Substring(1);
                    if (!itemJObj.TryGetValue(propertyName, out JToken propertyValue))
                    {
                        continue;
                    }

                    toEncryptJObj.Add(propertyName, propertyValue.Value <JToken>());
                    itemJObj.Remove(propertyName);
                }

                MemoryStream memoryStream = EncryptionProcessor.BaseSerializer.ToStream <JObject>(toEncryptJObj);
                Debug.Assert(memoryStream != null);
                Debug.Assert(memoryStream.TryGetBuffer(out _));
                plainText = memoryStream.ToArray();

                cipherText = await encryptor.EncryptAsync(
                    plainText,
                    encryptionOptions.DataEncryptionKeyId,
                    encryptionOptions.EncryptionAlgorithm,
                    cancellationToken);

                if (cipherText == null)
                {
                    throw new InvalidOperationException($"{nameof(Encryptor)} returned null cipherText from {nameof(EncryptAsync)}.");
                }

                encryptionProperties = new EncryptionProperties(
                    encryptionFormatVersion: 2,
                    encryptionOptions.EncryptionAlgorithm,
                    encryptionOptions.DataEncryptionKeyId,
                    encryptedData: cipherText,
                    encryptionOptions.PathsToEncrypt);
                break;

            default:
                throw new NotSupportedException($"Encryption Algorithm : {encryptionOptions.EncryptionAlgorithm} is not supported.");
            }

            itemJObj.Add(Constants.EncryptedInfo, JObject.FromObject(encryptionProperties));
            input.Dispose();
            return(EncryptionProcessor.BaseSerializer.ToStream(itemJObj));
        }