// Gets a cursor from container or creates a new cursor if it does not exist
        private static string GetCursor(BlobClient blobClient, BlobChangeFeedClient changeFeedClient, ILogger log)
        {
            string continuationToken = null;

            if (blobClient.Exists())
            {
                // If the continuationToken exists in blob, download and use it
                var stream = new MemoryStream();
                blobClient.DownloadTo(stream);
                continuationToken = Encoding.UTF8.GetString(stream.ToArray());
            }
            else
            {
                // If the continuationToken does not exist in the blob, get the continuationToken from the first item
                Page <BlobChangeFeedEvent> page            = changeFeedClient.GetChanges().AsPages(pageSizeHint: 1).First <Page <BlobChangeFeedEvent> >();
                BlobChangeFeedEvent        changeFeedEvent = page.Values.First <BlobChangeFeedEvent>();
                if (containerCheck.Invoke(changeFeedEvent) && eventTypeCheck.Invoke(changeFeedEvent) && blobCheck.Invoke(changeFeedEvent))
                {
                    log.LogInformation($"event: {changeFeedEvent.EventType} at {changeFeedEvent.Subject.Replace("/blobServices/default", "")} on {changeFeedEvent.EventTime}");
                }
                continuationToken = page.ContinuationToken;
            }

            return(continuationToken);
        }
Example #2
0
        public void SchemaV1Test()
        {
            // Arrange
            string rawText = File.ReadAllText(
                $"{Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)}{Path.DirectorySeparatorChar}Resources{Path.DirectorySeparatorChar}{"EventSchemaV1.json"}");

            Dictionary <string, object> rawDictionary = DeserializeEvent(rawText);

            // Act
            BlobChangeFeedEvent changeFeedEvent = new BlobChangeFeedEvent(rawDictionary);

            // Assert
            Assert.AreEqual(
                1,
                changeFeedEvent.SchemaVersion);
            Assert.AreEqual(
                "/subscriptions/dd40261b-437d-43d0-86cf-ef222b78fd15/resourceGroups/haambaga/providers/Microsoft.Storage/storageAccounts/HAAMBAGA-DEV",
                changeFeedEvent.Topic);
            Assert.AreEqual(
                "/blobServices/default/containers/apitestcontainerver/blobs/20220217_125928329_Blob_oaG6iu7ImEB1cX8M",
                changeFeedEvent.Subject);
            Assert.AreEqual(
                BlobChangeFeedEventType.BlobCreated,
                changeFeedEvent.EventType);
            Assert.AreEqual(
                DateTimeOffset.Parse("2022-02-17T12:59:41.4003102Z"),
                changeFeedEvent.EventTime);
            Assert.AreEqual(
                Guid.Parse("322343e3-8020-0000-00fe-233467066726"),
                changeFeedEvent.Id);
            Assert.AreEqual(
                BlobOperationName.PutBlob,
                changeFeedEvent.EventData.BlobOperationName);
            Assert.AreEqual(
                "f0270546-168e-4398-8fa8-107a1ac214d2",
                changeFeedEvent.EventData.ClientRequestId);
            Assert.AreEqual(
                Guid.Parse("322343e3-8020-0000-00fe-233467000000"),
                changeFeedEvent.EventData.RequestId);
            Assert.AreEqual(
                new ETag("0x8D9F2155CBF7928"),
                changeFeedEvent.EventData.ETag);
            Assert.AreEqual(
                "application/octet-stream",
                changeFeedEvent.EventData.ContentType);
            Assert.AreEqual(
                128,
                changeFeedEvent.EventData.ContentLength);
            Assert.AreEqual(
                BlobType.Block,
                changeFeedEvent.EventData.BlobType);
            Assert.AreEqual(
                new Uri("https://www.myurl.com"),
                changeFeedEvent.EventData.Uri);
            Assert.AreEqual(
                "00000000000000010000000000000002000000000000001d",
                changeFeedEvent.EventData.Sequencer);
        }
        public async Task AvroTest()
        {
            using Stream stream = File.OpenRead($"{Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)}{Path.DirectorySeparatorChar}Resources{Path.DirectorySeparatorChar}{""}");
            AvroReader avroReader = new AvroReader(stream);
            Chunk      chunk      = new Chunk(
                avroReader: avroReader,
                blockOffset: 0,
                eventIndex: 0,
                chunkPath: null);

            List <BlobChangeFeedEvent> events = new List <BlobChangeFeedEvent>();

            while (chunk.HasNext())
            {
                BlobChangeFeedEvent changeFeedEvent = await chunk.Next(async : true);

                events.Add(changeFeedEvent);
            }
        }
        public async Task Next()
        {
            // Arrange
            string chunkPath   = "chunkPath";
            long   blockOffset = 5;
            long   eventIndex  = 10;

            string         topic           = "topic";
            string         subject         = "subject";
            string         eventType       = "BlobCreated";
            DateTimeOffset eventTime       = new DateTimeOffset(2020, 4, 30, 8, 26, 30, TimeSpan.Zero);
            Guid           eventId         = Guid.NewGuid();
            long           dataVersion     = 1;
            string         metadataVersion = "1";

            string api             = "PutBlob";
            string clientRequestId = $"Azure-Storage-Powershell-{Guid.NewGuid()}";
            Guid   requestId       = Guid.NewGuid();
            ETag   etag            = new ETag("0x8D75EF45A3B8617");
            string contentType     = "contentType";
            long   contentLength   = Constants.KB;
            string blobType        = "BlockBlob";
            long   contentOffset   = 5;
            Uri    destinationUri  = new Uri("https://www.destination.com");
            Uri    sourceUri       = new Uri("https://www.source.com");
            Uri    uri             = new Uri("https://www.uri.com");
            bool   recursive       = true;
            string sequencer       = "sequencer";

            Dictionary <string, object> record = new Dictionary <string, object>
            {
                { Constants.ChangeFeed.Event.Topic, topic },
                { Constants.ChangeFeed.Event.Subject, subject },
                { Constants.ChangeFeed.Event.EventType, eventType },
                { Constants.ChangeFeed.Event.EventTime, eventTime.ToString() },
                { Constants.ChangeFeed.Event.EventId, eventId.ToString() },
                { Constants.ChangeFeed.Event.SchemaVersion, dataVersion },
                { Constants.ChangeFeed.Event.MetadataVersion, metadataVersion },
                { Constants.ChangeFeed.Event.Data, new Dictionary <string, object>
                  {
                      { Constants.ChangeFeed.EventData.Api, api },
                      { Constants.ChangeFeed.EventData.ClientRequestId, clientRequestId.ToString() },
                      { Constants.ChangeFeed.EventData.RequestId, requestId.ToString() },
                      { Constants.ChangeFeed.EventData.Etag, etag.ToString() },
                      { Constants.ChangeFeed.EventData.ContentType, contentType },
                      { Constants.ChangeFeed.EventData.ContentLength, contentLength },
                      { Constants.ChangeFeed.EventData.BlobType, blobType },
                      { Constants.ChangeFeed.EventData.ContentOffset, contentOffset },
                      { Constants.ChangeFeed.EventData.DestinationUrl, destinationUri.ToString() },
                      { Constants.ChangeFeed.EventData.SourceUrl, sourceUri.ToString() },
                      { Constants.ChangeFeed.EventData.Url, uri.ToString() },
                      { Constants.ChangeFeed.EventData.Recursive, recursive },
                      { Constants.ChangeFeed.EventData.Sequencer, sequencer }
                  } }
            };

            Mock <BlobContainerClient>          containerClient              = new Mock <BlobContainerClient>(MockBehavior.Strict);
            Mock <BlobClient>                   blobClient                   = new Mock <BlobClient>(MockBehavior.Strict);
            Mock <AvroReaderFactory>            avroReaderFactory            = new Mock <AvroReaderFactory>(MockBehavior.Strict);
            Mock <AvroReader>                   avroReader                   = new Mock <AvroReader>(MockBehavior.Strict);
            Mock <LazyLoadingBlobStreamFactory> lazyLoadingBlobStreamFactory = new Mock <LazyLoadingBlobStreamFactory>(MockBehavior.Strict);
            Mock <LazyLoadingBlobStream>        dataStream                   = new Mock <LazyLoadingBlobStream>(MockBehavior.Strict);
            Mock <LazyLoadingBlobStream>        headStream                   = new Mock <LazyLoadingBlobStream>(MockBehavior.Strict);

            containerClient.Setup(r => r.GetBlobClient(It.IsAny <string>())).Returns(blobClient.Object);
            lazyLoadingBlobStreamFactory.SetupSequence(r => r.BuildLazyLoadingBlobStream(
                                                           It.IsAny <BlobClient>(),
                                                           It.IsAny <long>(),
                                                           It.IsAny <long>()))
            .Returns(dataStream.Object)
            .Returns(headStream.Object);
            avroReaderFactory.Setup(r => r.BuildAvroReader(
                                        It.IsAny <Stream>(),
                                        It.IsAny <Stream>(),
                                        It.IsAny <long>(),
                                        It.IsAny <long>())).Returns(avroReader.Object);
            avroReader.Setup(r => r.HasNext()).Returns(true);
            avroReader.Setup(r => r.Initalize(It.IsAny <bool>(), It.IsAny <CancellationToken>())).Returns(Task.CompletedTask);
            avroReader.Setup(r => r.Next(
                                 It.IsAny <bool>(),
                                 It.IsAny <CancellationToken>()))
            .ReturnsAsync(record);

            avroReader.Setup(r => r.BlockOffset).Returns(blockOffset);
            avroReader.Setup(r => r.ObjectIndex).Returns(eventIndex);

            ChunkFactory chunkFactory = new ChunkFactory(
                containerClient.Object,
                lazyLoadingBlobStreamFactory.Object,
                avroReaderFactory.Object);
            Chunk chunk = await chunkFactory.BuildChunk(
                IsAsync,
                chunkPath,
                blockOffset,
                eventIndex);

            // Act
            BlobChangeFeedEvent changeFeedEvent = await chunk.Next(IsAsync);

            // Assert
            Assert.AreEqual(topic, changeFeedEvent.Topic);
            Assert.AreEqual(subject, changeFeedEvent.Subject);
            Assert.AreEqual(BlobChangeFeedEventType.BlobCreated, changeFeedEvent.EventType);
            Assert.AreEqual(eventTime, changeFeedEvent.EventTime);
            Assert.AreEqual(eventId, changeFeedEvent.Id);
            Assert.AreEqual(dataVersion, changeFeedEvent.SchemaVersion);
            Assert.AreEqual(metadataVersion, changeFeedEvent.MetadataVersion);

            Assert.AreEqual(BlobOperationName.PutBlob, changeFeedEvent.EventData.BlobOperationName);
            Assert.AreEqual(clientRequestId, changeFeedEvent.EventData.ClientRequestId);
            Assert.AreEqual(requestId, changeFeedEvent.EventData.RequestId);
            Assert.AreEqual(etag, changeFeedEvent.EventData.ETag);
            Assert.AreEqual(contentType, changeFeedEvent.EventData.ContentType);
            Assert.AreEqual(contentLength, changeFeedEvent.EventData.ContentLength);
            Assert.AreEqual(BlobType.Block, changeFeedEvent.EventData.BlobType);
            Assert.AreEqual(contentOffset, changeFeedEvent.EventData.ContentOffset);
            Assert.AreEqual(destinationUri, changeFeedEvent.EventData.DestinationUri);
            Assert.AreEqual(sourceUri, changeFeedEvent.EventData.SourceUri);
            Assert.AreEqual(uri, changeFeedEvent.EventData.Uri);
            Assert.AreEqual(recursive, changeFeedEvent.EventData.Recursive);
            Assert.AreEqual(sequencer, changeFeedEvent.EventData.Sequencer);

            containerClient.Verify(r => r.GetBlobClient(chunkPath));
            lazyLoadingBlobStreamFactory.Verify(r => r.BuildLazyLoadingBlobStream(
                                                    blobClient.Object,
                                                    blockOffset,
                                                    Constants.ChangeFeed.ChunkBlockDownloadSize));
            lazyLoadingBlobStreamFactory.Verify(r => r.BuildLazyLoadingBlobStream(
                                                    blobClient.Object,
                                                    0,
                                                    3 * Constants.KB));
            avroReaderFactory.Verify(r => r.BuildAvroReader(
                                         dataStream.Object,
                                         headStream.Object,
                                         blockOffset,
                                         eventIndex));
            avroReader.Verify(r => r.HasNext());
            avroReader.Verify(r => r.Next(
                                  IsAsync,
                                  default));
            avroReader.Verify(r => r.BlockOffset);
            avroReader.Verify(r => r.ObjectIndex);
        }
Example #5
0
        public void SchemaV3Test()
        {
            // Arrange
            string rawText = File.ReadAllText(
                $"{Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)}{Path.DirectorySeparatorChar}Resources{Path.DirectorySeparatorChar}{"EventSchemaV3.json"}");

            Dictionary <string, object> rawDictionary = DeserializeEvent(rawText);

            // Act
            BlobChangeFeedEvent changeFeedEvent = new BlobChangeFeedEvent(rawDictionary);

            // Assert
            Assert.AreEqual(
                3,
                changeFeedEvent.SchemaVersion);
            Assert.AreEqual(
                "/subscriptions/dd40261b-437d-43d0-86cf-ef222b78fd15/resourceGroups/haambaga/providers/Microsoft.Storage/storageAccounts/HAAMBAGA-DEV",
                changeFeedEvent.Topic);
            Assert.AreEqual(
                "/blobServices/default/containers/apitestcontainerver/blobs/20220217_130510699_Blob_oaG6iu7ImEB1cX8M",
                changeFeedEvent.Subject);
            Assert.AreEqual(
                BlobChangeFeedEventType.BlobCreated,
                changeFeedEvent.EventType);
            Assert.AreEqual(
                DateTimeOffset.Parse("2022-02-17T13:05:19.6798242Z"),
                changeFeedEvent.EventTime);
            Assert.AreEqual(
                Guid.Parse("eefe8fc8-8020-0000-00fe-23346706daaa"),
                changeFeedEvent.Id);
            Assert.AreEqual(
                BlobOperationName.PutBlob,
                changeFeedEvent.EventData.BlobOperationName);
            Assert.AreEqual(
                "00c0b6b7-bb67-4748-a3dc-86464863d267",
                changeFeedEvent.EventData.ClientRequestId);
            Assert.AreEqual(
                Guid.Parse("eefe8fc8-8020-0000-00fe-233467000000"),
                changeFeedEvent.EventData.RequestId);
            Assert.AreEqual(
                new ETag("0x8D9F216266170DC"),
                changeFeedEvent.EventData.ETag);
            Assert.AreEqual(
                "application/octet-stream",
                changeFeedEvent.EventData.ContentType);
            Assert.AreEqual(
                128,
                changeFeedEvent.EventData.ContentLength);
            Assert.AreEqual(
                BlobType.Block,
                changeFeedEvent.EventData.BlobType);
            Assert.AreEqual(
                new Uri("https://www.myurl.com"),
                changeFeedEvent.EventData.Uri);
            Assert.AreEqual(
                "00000000000000010000000000000002000000000000001d",
                changeFeedEvent.EventData.Sequencer);

            Assert.AreEqual("2022-02-17T13:08:42.4825913Z", changeFeedEvent.EventData.PreviousInfo.SoftDeleteSnapshot);
            Assert.IsTrue(changeFeedEvent.EventData.PreviousInfo.WasBlobSoftDeleted);
            Assert.AreEqual("2024-02-17T16:11:52.0781797Z", changeFeedEvent.EventData.PreviousInfo.BlobVersion);
            Assert.AreEqual("2022-02-17T16:11:52.0781797Z", changeFeedEvent.EventData.PreviousInfo.LastVersion);
            Assert.AreEqual(AccessTier.Hot, changeFeedEvent.EventData.PreviousInfo.PreviousTier);

            Assert.AreEqual(
                "2022-02-17T16:09:16.7261278Z",
                changeFeedEvent.EventData.Snapshot);

            Assert.AreEqual(6, changeFeedEvent.EventData.UpdatedBlobProperties.Count);

            Assert.AreEqual("ContentLanguage", changeFeedEvent.EventData.UpdatedBlobProperties["ContentLanguage"].PropertyName);
            Assert.AreEqual("pl-Pl", changeFeedEvent.EventData.UpdatedBlobProperties["ContentLanguage"].NewValue);
            Assert.AreEqual("nl-NL", changeFeedEvent.EventData.UpdatedBlobProperties["ContentLanguage"].PreviousValue);

            Assert.AreEqual("CacheControl", changeFeedEvent.EventData.UpdatedBlobProperties["CacheControl"].PropertyName);
            Assert.AreEqual("max-age=100", changeFeedEvent.EventData.UpdatedBlobProperties["CacheControl"].NewValue);
            Assert.AreEqual("max-age=99", changeFeedEvent.EventData.UpdatedBlobProperties["CacheControl"].PreviousValue);

            Assert.AreEqual("ContentEncoding", changeFeedEvent.EventData.UpdatedBlobProperties["ContentEncoding"].PropertyName);
            Assert.AreEqual("gzip, identity", changeFeedEvent.EventData.UpdatedBlobProperties["ContentEncoding"].NewValue);
            Assert.AreEqual("gzip", changeFeedEvent.EventData.UpdatedBlobProperties["ContentEncoding"].PreviousValue);

            Assert.AreEqual("ContentMD5", changeFeedEvent.EventData.UpdatedBlobProperties["ContentMD5"].PropertyName);
            Assert.AreEqual("Q2h1Y2sgSW51ZwDIAXR5IQ==", changeFeedEvent.EventData.UpdatedBlobProperties["ContentMD5"].NewValue);
            Assert.AreEqual("Q2h1Y2sgSW=", changeFeedEvent.EventData.UpdatedBlobProperties["ContentMD5"].PreviousValue);

            Assert.AreEqual("ContentDisposition", changeFeedEvent.EventData.UpdatedBlobProperties["ContentDisposition"].PropertyName);
            Assert.AreEqual("attachment", changeFeedEvent.EventData.UpdatedBlobProperties["ContentDisposition"].NewValue);
            Assert.AreEqual("", changeFeedEvent.EventData.UpdatedBlobProperties["ContentDisposition"].PreviousValue);

            Assert.AreEqual("ContentType", changeFeedEvent.EventData.UpdatedBlobProperties["ContentType"].PropertyName);
            Assert.AreEqual("application/json", changeFeedEvent.EventData.UpdatedBlobProperties["ContentType"].NewValue);
            Assert.AreEqual("application/octet-stream", changeFeedEvent.EventData.UpdatedBlobProperties["ContentType"].PreviousValue);
        }
Example #6
0
        public void SchemaV5Test()
        {
            // Arrange
            string rawText = File.ReadAllText(
                $"{Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)}{Path.DirectorySeparatorChar}Resources{Path.DirectorySeparatorChar}{"EventSchemaV5.json"}");

            Dictionary <string, object> rawDictionary = DeserializeEvent(rawText);

            // Act
            BlobChangeFeedEvent changeFeedEvent = new BlobChangeFeedEvent(rawDictionary);

            // Assert
            Assert.AreEqual(
                5,
                changeFeedEvent.SchemaVersion);
            Assert.AreEqual(
                "/subscriptions/dd40261b-437d-43d0-86cf-ef222b78fd15/resourceGroups/haambaga/providers/Microsoft.Storage/storageAccounts/HAAMBAGA-DEV",
                changeFeedEvent.Topic);
            Assert.AreEqual(
                "/blobServices/default/containers/apitestcontainerver/blobs/20220217_131202494_Blob_oaG6iu7ImEB1cX8M",
                changeFeedEvent.Subject);
            Assert.AreEqual(
                BlobChangeFeedEventType.BlobCreated,
                changeFeedEvent.EventType);
            Assert.AreEqual(
                DateTimeOffset.Parse("2022-02-17T13:12:11.5746587Z"),
                changeFeedEvent.EventTime);
            Assert.AreEqual(
                Guid.Parse("62616073-8020-0000-00ff-233467060cc0"),
                changeFeedEvent.Id);
            Assert.AreEqual(
                BlobOperationName.PutBlob,
                changeFeedEvent.EventData.BlobOperationName);
            Assert.AreEqual(
                "b3f9b39a-ae5a-45ac-afad-95ac9e9f2791",
                changeFeedEvent.EventData.ClientRequestId);
            Assert.AreEqual(
                Guid.Parse("62616073-8020-0000-00ff-233467000000"),
                changeFeedEvent.EventData.RequestId);
            Assert.AreEqual(
                new ETag("0x8D9F2171BE32588"),
                changeFeedEvent.EventData.ETag);
            Assert.AreEqual(
                "application/octet-stream",
                changeFeedEvent.EventData.ContentType);
            Assert.AreEqual(
                128,
                changeFeedEvent.EventData.ContentLength);
            Assert.AreEqual(
                BlobType.Block,
                changeFeedEvent.EventData.BlobType);
            Assert.AreEqual(
                "2022-02-17T16:11:52.5901564Z",
                changeFeedEvent.EventData.BlobVersion);
            Assert.AreEqual(
                "0000000000000001",
                changeFeedEvent.EventData.ContainerVersion);
            Assert.AreEqual(
                AccessTier.Archive,
                changeFeedEvent.EventData.BlobAccessTier);
            Assert.AreEqual(
                new Uri("https://www.myurl.com"),
                changeFeedEvent.EventData.Uri);
            Assert.AreEqual(
                "00000000000000010000000000000002000000000000001d",
                changeFeedEvent.EventData.Sequencer);

            Assert.AreEqual("2022-02-17T13:12:11.5726507Z", changeFeedEvent.EventData.PreviousInfo.SoftDeleteSnapshot);
            Assert.IsTrue(changeFeedEvent.EventData.PreviousInfo.WasBlobSoftDeleted);
            Assert.AreEqual("2024-02-17T16:11:52.0781797Z", changeFeedEvent.EventData.PreviousInfo.BlobVersion);
            Assert.AreEqual("2022-02-17T16:11:52.0781797Z", changeFeedEvent.EventData.PreviousInfo.LastVersion);
            Assert.AreEqual(AccessTier.Hot, changeFeedEvent.EventData.PreviousInfo.PreviousTier);

            Assert.AreEqual(
                "2022-02-17T16:09:16.7261278Z",
                changeFeedEvent.EventData.Snapshot);

            Assert.AreEqual(6, changeFeedEvent.EventData.UpdatedBlobProperties.Count);

            Assert.AreEqual("ContentLanguage", changeFeedEvent.EventData.UpdatedBlobProperties["ContentLanguage"].PropertyName);
            Assert.AreEqual("pl-Pl", changeFeedEvent.EventData.UpdatedBlobProperties["ContentLanguage"].NewValue);
            Assert.AreEqual("nl-NL", changeFeedEvent.EventData.UpdatedBlobProperties["ContentLanguage"].PreviousValue);

            Assert.AreEqual("CacheControl", changeFeedEvent.EventData.UpdatedBlobProperties["CacheControl"].PropertyName);
            Assert.AreEqual("max-age=100", changeFeedEvent.EventData.UpdatedBlobProperties["CacheControl"].NewValue);
            Assert.AreEqual("max-age=99", changeFeedEvent.EventData.UpdatedBlobProperties["CacheControl"].PreviousValue);

            Assert.AreEqual("ContentEncoding", changeFeedEvent.EventData.UpdatedBlobProperties["ContentEncoding"].PropertyName);
            Assert.AreEqual("gzip, identity", changeFeedEvent.EventData.UpdatedBlobProperties["ContentEncoding"].NewValue);
            Assert.AreEqual("gzip", changeFeedEvent.EventData.UpdatedBlobProperties["ContentEncoding"].PreviousValue);

            Assert.AreEqual("ContentMD5", changeFeedEvent.EventData.UpdatedBlobProperties["ContentMD5"].PropertyName);
            Assert.AreEqual("Q2h1Y2sgSW51ZwDIAXR5IQ==", changeFeedEvent.EventData.UpdatedBlobProperties["ContentMD5"].NewValue);
            Assert.AreEqual("Q2h1Y2sgSW=", changeFeedEvent.EventData.UpdatedBlobProperties["ContentMD5"].PreviousValue);

            Assert.AreEqual("ContentDisposition", changeFeedEvent.EventData.UpdatedBlobProperties["ContentDisposition"].PropertyName);
            Assert.AreEqual("attachment", changeFeedEvent.EventData.UpdatedBlobProperties["ContentDisposition"].NewValue);
            Assert.AreEqual("", changeFeedEvent.EventData.UpdatedBlobProperties["ContentDisposition"].PreviousValue);

            Assert.AreEqual("ContentType", changeFeedEvent.EventData.UpdatedBlobProperties["ContentType"].PropertyName);
            Assert.AreEqual("application/json", changeFeedEvent.EventData.UpdatedBlobProperties["ContentType"].NewValue);
            Assert.AreEqual("application/octet-stream", changeFeedEvent.EventData.UpdatedBlobProperties["ContentType"].PreviousValue);

            Assert.AreEqual(AccessTier.Hot, changeFeedEvent.EventData.AsyncOperationInfo.DestinationAccessTier);
            Assert.IsTrue(changeFeedEvent.EventData.AsyncOperationInfo.WasAsyncOperation);
            Assert.AreEqual("copyId", changeFeedEvent.EventData.AsyncOperationInfo.CopyId);

            Assert.AreEqual(2, changeFeedEvent.EventData.UpdatedBlobTags.PreviousTags.Count);
            Assert.AreEqual("Value1_3", changeFeedEvent.EventData.UpdatedBlobTags.PreviousTags["Tag1"]);
            Assert.AreEqual("Value2_3", changeFeedEvent.EventData.UpdatedBlobTags.PreviousTags["Tag2"]);

            Assert.AreEqual(2, changeFeedEvent.EventData.UpdatedBlobTags.NewTags.Count);
            Assert.AreEqual("Value1_4", changeFeedEvent.EventData.UpdatedBlobTags.NewTags["Tag1"]);
            Assert.AreEqual("Value2_4", changeFeedEvent.EventData.UpdatedBlobTags.NewTags["Tag2"]);
        }
        public void SchemaV4Test()
        {
            // Arrange
            string rawText = File.ReadAllText(
                $"{Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)}{Path.DirectorySeparatorChar}Resources{Path.DirectorySeparatorChar}{"EventSchemaV4.json"}");

            Dictionary <string, object> rawDictionary = DeserializeEvent(rawText);

            // Act
            BlobChangeFeedEvent changeFeedEvent = new BlobChangeFeedEvent(rawDictionary);

            // Assert
            Assert.AreEqual(
                4,
                changeFeedEvent.SchemaVersion);
            Assert.AreEqual(
                "/subscriptions/dd40261b-437d-43d0-86cf-ef222b78fd15/resourceGroups/haambaga/providers/Microsoft.Storage/storageAccounts/HAAMBAGA-DEV",
                changeFeedEvent.Topic);
            Assert.AreEqual(
                "/blobServices/default/containers/apitestcontainerver/blobs/20220217_130833395_Blob_oaG6iu7ImEB1cX8M",
                changeFeedEvent.Subject);
            Assert.AreEqual(
                BlobChangeFeedEventType.BlobCreated,
                changeFeedEvent.EventType);
            Assert.AreEqual(
                DateTimeOffset.Parse("2022-02-17T13:08:42.4835902Z"),
                changeFeedEvent.EventTime);
            Assert.AreEqual(
                Guid.Parse("ca76bce1-8020-0000-00ff-23346706e769"),
                changeFeedEvent.Id);
            Assert.AreEqual(
                BlobOperationName.PutBlob,
                changeFeedEvent.EventData.BlobOperationName);
            Assert.AreEqual(
                "58fbfee9-6cf5-4096-9666-c42980beee65",
                changeFeedEvent.EventData.ClientRequestId);
            Assert.AreEqual(
                Guid.Parse("ca76bce1-8020-0000-00ff-233467000000"),
                changeFeedEvent.EventData.RequestId);
            Assert.AreEqual(
                new ETag("0x8D9F2169F42D701"),
                changeFeedEvent.EventData.ETag);
            Assert.AreEqual(
                "application/octet-stream",
                changeFeedEvent.EventData.ContentType);
            Assert.AreEqual(
                128,
                changeFeedEvent.EventData.ContentLength);
            Assert.AreEqual(
                BlobType.Block,
                changeFeedEvent.EventData.BlobType);
            Assert.AreEqual(
                "2022-02-17T16:11:52.5901564Z",
                changeFeedEvent.EventData.BlobVersion);
            Assert.AreEqual(
                "0000000000000001",
                changeFeedEvent.EventData.ContainerVersion);
            Assert.AreEqual(
                AccessTier.Archive,
                changeFeedEvent.EventData.BlobAccessTier);
            Assert.AreEqual(
                new Uri("https://www.myurl.com"),
                changeFeedEvent.EventData.Uri);
            Assert.AreEqual(
                "00000000000000010000000000000002000000000000001d",
                changeFeedEvent.EventData.Sequencer);

            Assert.AreEqual("2022-02-17T13:08:42.4825913Z", changeFeedEvent.EventData.PreviousInfo.SoftDeleteSnapshot);
            Assert.IsTrue(changeFeedEvent.EventData.PreviousInfo.WasBlobSoftDeleted);
            Assert.AreEqual("2024-02-17T16:11:52.0781797Z", changeFeedEvent.EventData.PreviousInfo.NewBlobVersion);
            Assert.AreEqual("2022-02-17T16:11:52.0781797Z", changeFeedEvent.EventData.PreviousInfo.OldBlobVersion);
            Assert.AreEqual(AccessTier.Hot, changeFeedEvent.EventData.PreviousInfo.PreviousTier);

            Assert.AreEqual(
                "2022-02-17T16:09:16.7261278Z",
                changeFeedEvent.EventData.Snapshot);

            Assert.AreEqual(6, changeFeedEvent.EventData.UpdatedBlobProperties.Count);

            Assert.AreEqual("ContentLanguage", changeFeedEvent.EventData.UpdatedBlobProperties["ContentLanguage"].PropertyName);
            Assert.AreEqual("pl-Pl", changeFeedEvent.EventData.UpdatedBlobProperties["ContentLanguage"].NewValue);
            Assert.AreEqual("nl-NL", changeFeedEvent.EventData.UpdatedBlobProperties["ContentLanguage"].OldValue);

            Assert.AreEqual("CacheControl", changeFeedEvent.EventData.UpdatedBlobProperties["CacheControl"].PropertyName);
            Assert.AreEqual("max-age=100", changeFeedEvent.EventData.UpdatedBlobProperties["CacheControl"].NewValue);
            Assert.AreEqual("max-age=99", changeFeedEvent.EventData.UpdatedBlobProperties["CacheControl"].OldValue);

            Assert.AreEqual("ContentEncoding", changeFeedEvent.EventData.UpdatedBlobProperties["ContentEncoding"].PropertyName);
            Assert.AreEqual("gzip, identity", changeFeedEvent.EventData.UpdatedBlobProperties["ContentEncoding"].NewValue);
            Assert.AreEqual("gzip", changeFeedEvent.EventData.UpdatedBlobProperties["ContentEncoding"].OldValue);

            Assert.AreEqual("ContentMD5", changeFeedEvent.EventData.UpdatedBlobProperties["ContentMD5"].PropertyName);
            Assert.AreEqual("Q2h1Y2sgSW51ZwDIAXR5IQ==", changeFeedEvent.EventData.UpdatedBlobProperties["ContentMD5"].NewValue);
            Assert.AreEqual("Q2h1Y2sgSW=", changeFeedEvent.EventData.UpdatedBlobProperties["ContentMD5"].OldValue);

            Assert.AreEqual("ContentDisposition", changeFeedEvent.EventData.UpdatedBlobProperties["ContentDisposition"].PropertyName);
            Assert.AreEqual("attachment", changeFeedEvent.EventData.UpdatedBlobProperties["ContentDisposition"].NewValue);
            Assert.AreEqual("", changeFeedEvent.EventData.UpdatedBlobProperties["ContentDisposition"].OldValue);

            Assert.AreEqual("ContentType", changeFeedEvent.EventData.UpdatedBlobProperties["ContentType"].PropertyName);
            Assert.AreEqual("application/json", changeFeedEvent.EventData.UpdatedBlobProperties["ContentType"].NewValue);
            Assert.AreEqual("application/octet-stream", changeFeedEvent.EventData.UpdatedBlobProperties["ContentType"].OldValue);

            Assert.AreEqual(AccessTier.Hot, changeFeedEvent.EventData.LongRunningOperationInfo.DestinationAccessTier);
            Assert.IsTrue(changeFeedEvent.EventData.LongRunningOperationInfo.IsAsync);
            Assert.AreEqual("copyId", changeFeedEvent.EventData.LongRunningOperationInfo.CopyId);
        }