public ChangeFeedIteratorCore( ContainerInternal container, ChangeFeedStartFrom changeFeedStartFrom, ChangeFeedRequestOptions changeFeedRequestOptions) { this.container = container ?? throw new ArgumentNullException(nameof(container)); this.clientContext = container.ClientContext; this.changeFeedOptions = changeFeedRequestOptions ?? new ChangeFeedRequestOptions(); this.lazyContainerRid = new AsyncLazy <TryCatch <string> >(valueFactory: (innerCancellationToken) => { return(this.TryInitializeContainerRIdAsync(innerCancellationToken)); }); this.hasMoreResults = true; this.changeFeedStartFrom = changeFeedStartFrom; if (this.changeFeedStartFrom is ChangeFeedStartFromContinuation startFromContinuation) { if (!FeedRangeContinuation.TryParse(startFromContinuation.Continuation, out FeedRangeContinuation feedRangeContinuation)) { throw new ArgumentException(string.Format(ClientResources.FeedToken_UnknownFormat, startFromContinuation.Continuation)); } this.FeedRangeContinuation = feedRangeContinuation; FeedRange feedRange = feedRangeContinuation.GetFeedRange(); string etag = feedRangeContinuation.GetContinuation(); this.changeFeedStartFrom = new ChangeFeedStartFromContinuationAndFeedRange(etag, (FeedRangeInternal)feedRange); } }
public async Task ReadFeedIteratorCore_ReadNextAsync_Conflicts_Query() { string continuation = "TBD"; ResponseMessage responseMessage = new ResponseMessage(HttpStatusCode.OK); responseMessage.Headers.ContinuationToken = continuation; responseMessage.Headers[Documents.HttpConstants.HttpHeaders.ItemCount] = "1"; responseMessage.Content = new MemoryStream(Encoding.UTF8.GetBytes("{}")); Mock <CosmosClientContext> cosmosClientContext = new Mock <CosmosClientContext>(); cosmosClientContext.Setup(c => c.SerializerCore).Returns(new CosmosSerializerCore()); cosmosClientContext.Setup(c => c.ClientOptions).Returns(new CosmosClientOptions()); cosmosClientContext .Setup(c => c.ProcessResourceOperationStreamAsync( It.IsAny <Uri>(), It.Is <Documents.ResourceType>(rt => rt == Documents.ResourceType.Conflict), It.Is <Documents.OperationType>(ot => ot == Documents.OperationType.Query), It.IsAny <RequestOptions>(), It.IsAny <ContainerInternal>(), It.IsAny <PartitionKey?>(), It.Is <Stream>(stream => stream != null), It.IsAny <Action <RequestMessage> >(), It.IsAny <CosmosDiagnosticsContext>(), It.IsAny <CancellationToken>())) .Returns(Task.FromResult(responseMessage)); ContainerInternal containerCore = Mock.Of <ContainerInternal>(); Mock.Get(containerCore) .Setup(c => c.ClientContext) .Returns(cosmosClientContext.Object); FeedRangeInternal range = Mock.Of <FeedRangeInternal>(); Mock.Get(range) .Setup(f => f.Accept(It.IsAny <FeedRangeRequestMessagePopulatorVisitor>())); FeedRangeContinuation feedToken = Mock.Of <FeedRangeContinuation>(); Mock.Get(feedToken) .Setup(f => f.FeedRange) .Returns(range); Mock.Get(feedToken) .Setup(f => f.HandleSplitAsync(It.Is <ContainerInternal>(c => c == containerCore), It.IsAny <ResponseMessage>(), It.IsAny <CancellationToken>())) .Returns(Task.FromResult(Documents.ShouldRetryResult.NoRetry())); Mock.Get(feedToken) .Setup(f => f.GetContinuation()) .Returns(continuation); Mock.Get(feedToken) .Setup(f => f.IsDone) .Returns(true); FeedRangeIteratorCore feedTokenIterator = new FeedRangeIteratorCore(containerCore, feedToken, new QueryRequestOptions(), Documents.ResourceType.Conflict, queryDefinition: new QueryDefinition("select * from c")); ResponseMessage response = await feedTokenIterator.ReadNextAsync(); Mock.Get(feedToken) .Verify(f => f.ReplaceContinuation(It.Is <string>(ct => ct == continuation)), Times.Once); Mock.Get(feedToken) .Verify(f => f.HandleSplitAsync(It.Is <ContainerInternal>(c => c == containerCore), It.IsAny <ResponseMessage>(), It.IsAny <CancellationToken>()), Times.Once); }
public async Task ChangeFeedIteratorCore_ReadNextAsync() { string continuation = "TBD"; ResponseMessage responseMessage = new ResponseMessage(HttpStatusCode.OK); responseMessage.Headers.ETag = continuation; responseMessage.Headers[Documents.HttpConstants.HttpHeaders.ItemCount] = "1"; Mock <CosmosClientContext> cosmosClientContext = new Mock <CosmosClientContext>(); cosmosClientContext.Setup(c => c.ClientOptions).Returns(new CosmosClientOptions()); cosmosClientContext .Setup(c => c.ProcessResourceOperationStreamAsync( It.IsAny <Uri>(), It.IsAny <Documents.ResourceType>(), It.IsAny <Documents.OperationType>(), It.IsAny <RequestOptions>(), It.IsAny <ContainerInternal>(), It.IsAny <PartitionKey?>(), It.IsAny <Stream>(), It.IsAny <Action <RequestMessage> >(), It.IsAny <CosmosDiagnosticsContext>(), It.IsAny <CancellationToken>())) .Returns(Task.FromResult(responseMessage)); ContainerInternal containerCore = Mock.Of <ContainerInternal>(); Mock.Get(containerCore) .Setup(c => c.ClientContext) .Returns(cosmosClientContext.Object); FeedRangeInternal range = Mock.Of <FeedRangeInternal>(); Mock.Get(range) .Setup(f => f.Accept(It.IsAny <FeedRangeVisitor>())); FeedRangeContinuation feedToken = Mock.Of <FeedRangeContinuation>(); Mock.Get(feedToken) .Setup(f => f.Accept(It.IsAny <FeedRangeVisitor>(), It.IsAny <Action <RequestMessage, string> >())); Mock.Get(feedToken) .Setup(f => f.FeedRange) .Returns(range); Mock.Get(feedToken) .Setup(f => f.HandleSplitAsync(It.Is <ContainerInternal>(c => c == containerCore), It.IsAny <ResponseMessage>(), It.IsAny <CancellationToken>())) .Returns(Task.FromResult(Documents.ShouldRetryResult.NoRetry())); Mock.Get(feedToken) .Setup(f => f.HandleChangeFeedNotModified(It.IsAny <ResponseMessage>())) .Returns(Documents.ShouldRetryResult.NoRetry()); ChangeFeedIteratorCore changeFeedIteratorCore = new ChangeFeedIteratorCore(containerCore, feedToken, null); ResponseMessage response = await changeFeedIteratorCore.ReadNextAsync(); Mock.Get(feedToken) .Verify(f => f.ReplaceContinuation(It.Is <string>(ct => ct == continuation)), Times.Once); Mock.Get(feedToken) .Verify(f => f.HandleSplitAsync(It.Is <ContainerInternal>(c => c == containerCore), It.IsAny <ResponseMessage>(), It.IsAny <CancellationToken>()), Times.Once); Mock.Get(feedToken) .Verify(f => f.HandleChangeFeedNotModified(It.IsAny <ResponseMessage>()), Times.Once); }
public async Task ChangeFeedIteratorCore_DoesNotUpdateContinuation_OnError() { string continuation = "TBD"; ResponseMessage responseMessage = new ResponseMessage(HttpStatusCode.Gone); responseMessage.Headers.ETag = continuation; Mock <CosmosClientContext> cosmosClientContext = new Mock <CosmosClientContext>(); cosmosClientContext.Setup(c => c.ClientOptions).Returns(new CosmosClientOptions()); cosmosClientContext .Setup(c => c.ProcessResourceOperationStreamAsync( It.IsAny <string>(), It.IsAny <Documents.ResourceType>(), It.IsAny <Documents.OperationType>(), It.IsAny <RequestOptions>(), It.IsAny <ContainerInternal>(), It.IsAny <PartitionKey?>(), It.IsAny <Stream>(), It.IsAny <Action <RequestMessage> >(), It.IsAny <CosmosDiagnosticsContext>(), It.IsAny <CancellationToken>())) .Returns(Task.FromResult(responseMessage)); ContainerInternal containerCore = Mock.Of <ContainerInternal>(); Mock.Get(containerCore) .Setup(c => c.ClientContext) .Returns(cosmosClientContext.Object); FeedRangeInternal range = Mock.Of <FeedRangeInternal>(); Mock.Get(range) .Setup(f => f.Accept(It.IsAny <FeedRangeRequestMessagePopulatorVisitor>())); FeedRangeContinuation feedToken = Mock.Of <FeedRangeContinuation>(); Mock.Get(feedToken) .Setup(f => f.FeedRange) .Returns(range); Mock.Get(feedToken) .Setup(f => f.HandleSplitAsync(It.Is <ContainerInternal>(c => c == containerCore), It.IsAny <ResponseMessage>(), It.IsAny <CancellationToken>())) .Returns(Task.FromResult(Documents.ShouldRetryResult.NoRetry())); Mock.Get(feedToken) .Setup(f => f.HandleChangeFeedNotModified(It.IsAny <ResponseMessage>())) .Returns(Documents.ShouldRetryResult.NoRetry()); ChangeFeedIteratorCore changeFeedIteratorCore = CreateWithCustomFeedToken(containerCore, feedToken); ResponseMessage response = await changeFeedIteratorCore.ReadNextAsync(); Assert.IsFalse(changeFeedIteratorCore.HasMoreResults); Mock.Get(feedToken) .Verify(f => f.ReplaceContinuation(It.Is <string>(ct => ct == continuation)), Times.Never); Mock.Get(feedToken) .Verify(f => f.HandleSplitAsync(It.Is <ContainerInternal>(c => c == containerCore), It.IsAny <ResponseMessage>(), It.IsAny <CancellationToken>()), Times.Once); Mock.Get(feedToken) .Verify(f => f.HandleChangeFeedNotModified(It.IsAny <ResponseMessage>()), Times.Once); }
public async Task ChangeFeedIteratorCore_HandlesSplitsThroughPipeline() { int executionCount = 0; CosmosClientContext cosmosClientContext = GetMockedClientContext((RequestMessage requestMessage, CancellationToken cancellationToken) => { // Force OnBeforeRequestActions call requestMessage.ToDocumentServiceRequest(); if (executionCount++ == 0) { return(TestHandler.ReturnStatusCode(HttpStatusCode.Gone, Documents.SubStatusCodes.PartitionKeyRangeGone)); } return(TestHandler.ReturnStatusCode(HttpStatusCode.OK)); }); ContainerInternal containerCore = Mock.Of <ContainerInternal>(); Mock.Get(containerCore) .Setup(c => c.ClientContext) .Returns(cosmosClientContext); Mock.Get(containerCore) .Setup(c => c.LinkUri) .Returns(new Uri("https://dummy.documents.azure.com:443/dbs")); FeedRangeInternal range = Mock.Of <FeedRangeInternal>(); Mock.Get(range) .Setup(f => f.Accept(It.IsAny <FeedRangeVisitor>())); FeedRangeContinuation feedToken = Mock.Of <FeedRangeContinuation>(); Mock.Get(feedToken) .Setup(f => f.Accept(It.IsAny <FeedRangeVisitor>(), It.IsAny <Action <RequestMessage, string> >())); Mock.Get(feedToken) .Setup(f => f.FeedRange) .Returns(range); Mock.Get(feedToken) .Setup(f => f.Accept(It.IsAny <FeedRangeVisitor>(), It.IsAny <Action <RequestMessage, string> >())); Mock.Get(feedToken) .Setup(f => f.HandleSplitAsync(It.Is <ContainerInternal>(c => c == containerCore), It.IsAny <ResponseMessage>(), It.IsAny <CancellationToken>())) .Returns(Task.FromResult(Documents.ShouldRetryResult.NoRetry())); Mock.Get(feedToken) .Setup(f => f.HandleChangeFeedNotModified(It.IsAny <ResponseMessage>())) .Returns(Documents.ShouldRetryResult.NoRetry()); ChangeFeedIteratorCore changeFeedIteratorCore = new ChangeFeedIteratorCore(containerCore, feedToken, null); ResponseMessage response = await changeFeedIteratorCore.ReadNextAsync(); Assert.AreEqual(1, executionCount, "PartitionKeyRangeGoneRetryHandler handled the Split"); Assert.AreEqual(HttpStatusCode.Gone, response.StatusCode); Mock.Get(feedToken) .Verify(f => f.ReplaceContinuation(It.IsAny <string>()), Times.Never); Mock.Get(feedToken) .Verify(f => f.HandleSplitAsync(It.Is <ContainerInternal>(c => c == containerCore), It.IsAny <ResponseMessage>(), It.IsAny <CancellationToken>()), Times.Once); Mock.Get(feedToken) .Verify(f => f.HandleChangeFeedNotModified(It.IsAny <ResponseMessage>()), Times.Once); }
public async Task ReadFeedIteratorCore_WithNoInitialState_ReadNextAsync() { string continuation = "TBD"; ResponseMessage responseMessage = new ResponseMessage(HttpStatusCode.OK); responseMessage.Headers.ContinuationToken = continuation; responseMessage.Headers[Documents.HttpConstants.HttpHeaders.ItemCount] = "1"; responseMessage.Content = new MemoryStream(Encoding.UTF8.GetBytes("{}")); MultiRangeMockDocumentClient documentClient = new MultiRangeMockDocumentClient(); Mock <CosmosClientContext> cosmosClientContext = new Mock <CosmosClientContext>(); cosmosClientContext.Setup(c => c.ClientOptions).Returns(new CosmosClientOptions()); cosmosClientContext.Setup(c => c.DocumentClient).Returns(documentClient); cosmosClientContext .Setup(c => c.ProcessResourceOperationStreamAsync( It.IsAny <string>(), It.Is <Documents.ResourceType>(rt => rt == Documents.ResourceType.Document), It.IsAny <Documents.OperationType>(), It.IsAny <RequestOptions>(), It.IsAny <ContainerInternal>(), It.IsAny <PartitionKey?>(), It.IsAny <Stream>(), It.IsAny <Action <RequestMessage> >(), It.IsAny <CosmosDiagnosticsContext>(), It.IsAny <CancellationToken>())) .Returns(Task.FromResult(responseMessage)); ContainerInternal containerCore = Mock.Of <ContainerInternal>(); Mock.Get(containerCore) .Setup(c => c.ClientContext) .Returns(cosmosClientContext.Object); Mock.Get(containerCore) .Setup(c => c.GetRIDAsync(It.IsAny <CancellationToken>())) .ReturnsAsync(Guid.NewGuid().ToString()); FeedRangeIteratorCore feedTokenIterator = FeedRangeIteratorCore.Create(containerCore, null, null, new QueryRequestOptions()); ResponseMessage response = await feedTokenIterator.ReadNextAsync(); Assert.IsTrue(FeedRangeContinuation.TryParse(response.ContinuationToken, out FeedRangeContinuation parsedToken)); FeedRangeCompositeContinuation feedRangeCompositeContinuation = parsedToken as FeedRangeCompositeContinuation; FeedRangeEPK feedTokenEPKRange = feedRangeCompositeContinuation.FeedRange as FeedRangeEPK; // Assert that a FeedToken for the entire range is used Assert.AreEqual(Documents.Routing.PartitionKeyInternal.MinimumInclusiveEffectivePartitionKey, feedTokenEPKRange.Range.Min); Assert.AreEqual(Documents.Routing.PartitionKeyInternal.MaximumExclusiveEffectivePartitionKey, feedTokenEPKRange.Range.Max); Assert.AreEqual(continuation, feedRangeCompositeContinuation.CompositeContinuationTokens.Peek().Token); Assert.IsFalse(feedRangeCompositeContinuation.IsDone); }
public void FeedRangeCompositeContinuation_TryParse() { const string containerRid = "containerRid"; List <Documents.Routing.Range <string> > keyRanges = new List <Documents.Routing.Range <string> >() { new Documents.Routing.Range <string>("A", "B", true, false), new Documents.Routing.Range <string>("D", "E", true, false), }; FeedRangeInternal feedRangeInternal = new FeedRangeEPK(new Documents.Routing.Range <string>("A", "E", true, false)); FeedRangeCompositeContinuation token = new FeedRangeCompositeContinuation(containerRid, feedRangeInternal, keyRanges); Assert.IsTrue(FeedRangeContinuation.TryParse(token.ToString(), out _)); Assert.IsFalse(FeedRangeContinuation.TryParse("whatever", out _)); }
public async Task ReadFeedIteratorCore_HandlesSplitsThroughPipeline() { int executionCount = 0; CosmosClientContext cosmosClientContext = GetMockedClientContext((RequestMessage requestMessage, CancellationToken cancellationToken) => { // Force OnBeforeRequestActions call requestMessage.ToDocumentServiceRequest(); if (executionCount++ == 0) { return(TestHandler.ReturnStatusCode(HttpStatusCode.Gone, Documents.SubStatusCodes.PartitionKeyRangeGone)); } return(TestHandler.ReturnStatusCode(HttpStatusCode.OK)); }); ContainerInternal containerCore = Mock.Of <ContainerInternal>(); Mock.Get(containerCore) .Setup(c => c.ClientContext) .Returns(cosmosClientContext); Mock.Get(containerCore) .Setup(c => c.LinkUri) .Returns("/dbs/db/colls/colls"); FeedRangeInternal range = Mock.Of <FeedRangeInternal>(); Mock.Get(range) .Setup(f => f.Accept(It.IsAny <FeedRangeVisitor>())); FeedRangeContinuation feedToken = Mock.Of <FeedRangeContinuation>(); Mock.Get(feedToken) .Setup(f => f.FeedRange) .Returns(range); Mock.Get(feedToken) .Setup(f => f.Accept(It.IsAny <FeedRangeVisitor>(), It.IsAny <Action <RequestMessage, string> >())); Mock.Get(feedToken) .Setup(f => f.HandleSplitAsync(It.Is <ContainerInternal>(c => c == containerCore), It.IsAny <ResponseMessage>(), It.IsAny <CancellationToken>())) .Returns(Task.FromResult(Documents.ShouldRetryResult.NoRetry())); FeedRangeIteratorCore changeFeedIteratorCore = new FeedRangeIteratorCore(containerCore, feedToken, new QueryRequestOptions(), Documents.ResourceType.Document, queryDefinition: null); ResponseMessage response = await changeFeedIteratorCore.ReadNextAsync(); Assert.AreEqual(1, executionCount, "Pipeline handled the Split"); Assert.AreEqual(HttpStatusCode.Gone, response.StatusCode); }
private static ChangeFeedIteratorCore CreateWithCustomFeedToken( ContainerInternal containerInternal, FeedRangeContinuation feedToken) { ChangeFeedIteratorCore changeFeedIteratorCore = new ChangeFeedIteratorCore( containerInternal, ChangeFeedStartFrom.Beginning(), changeFeedRequestOptions: default); System.Reflection.FieldInfo prop = changeFeedIteratorCore .GetType() .GetField( "FeedRangeContinuation", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); prop.SetValue(changeFeedIteratorCore, feedToken); return(changeFeedIteratorCore); }
private void FeedRange_PartitionKey_Validate(PartitionKey partitionKey) { string continuationToken = "TBD"; string containerRid = Guid.NewGuid().ToString(); FeedRangePartitionKey feedTokenPartitionKey = new FeedRangePartitionKey(partitionKey); FeedRangeCompositeContinuation feedRangeSimpleContinuation = new FeedRangeCompositeContinuation(containerRid, feedTokenPartitionKey, new List <Documents.Routing.Range <string> >() { Documents.Routing.Range <string> .GetEmptyRange("AA") }, continuationToken); string serialized = feedRangeSimpleContinuation.ToString(); Assert.IsTrue(FeedRangeContinuation.TryParse(serialized, out FeedRangeContinuation deserialized)); FeedRangeCompositeContinuation deserializedContinuation = deserialized as FeedRangeCompositeContinuation; FeedRangePartitionKey deserializedFeedRange = deserializedContinuation.FeedRange as FeedRangePartitionKey; Assert.AreEqual(feedTokenPartitionKey.PartitionKey.ToJsonString(), deserializedFeedRange.PartitionKey.ToJsonString()); Assert.AreEqual(continuationToken, deserializedContinuation.GetContinuation()); }
public async Task FeedRange_EPK_Serialization() { string continuation = "TBD"; string containerRid = Guid.NewGuid().ToString(); List <FeedRange> ranges = (await this.Container.GetFeedRangesAsync()).ToList(); List <string> serializations = new List <string>(); List <FeedRangeCompositeContinuation> tokens = new List <FeedRangeCompositeContinuation>(); foreach (FeedRange range in ranges) { FeedRangeEpk feedRangeEPK = range as FeedRangeEpk; FeedRangeCompositeContinuation feedRangeCompositeContinuation = new FeedRangeCompositeContinuation(containerRid, feedRangeEPK, new List <Documents.Routing.Range <string> >() { feedRangeEPK.Range }, continuation); tokens.Add(feedRangeCompositeContinuation); serializations.Add(feedRangeCompositeContinuation.ToString()); } List <FeedRangeContinuation> deserialized = new List <FeedRangeContinuation>(); foreach (string serialized in serializations) { Assert.IsTrue(FeedRangeContinuation.TryParse(serialized, out FeedRangeContinuation token)); deserialized.Add(token); } Assert.AreEqual(tokens.Count, deserialized.Count); for (int i = 0; i < tokens.Count; i++) { FeedRangeCompositeContinuation originalToken = tokens[i] as FeedRangeCompositeContinuation; FeedRangeCompositeContinuation deserializedToken = deserialized[i] as FeedRangeCompositeContinuation; Assert.AreEqual(originalToken.GetContinuation(), deserializedToken.GetContinuation()); Assert.AreEqual(originalToken.ContainerRid, deserializedToken.ContainerRid); Assert.AreEqual(originalToken.CompositeContinuationTokens.Count, deserializedToken.CompositeContinuationTokens.Count); Assert.AreEqual(originalToken.CompositeContinuationTokens.Peek().Token, deserializedToken.CompositeContinuationTokens.Peek().Token); Assert.AreEqual(originalToken.CompositeContinuationTokens.Peek().Range.Min, deserializedToken.CompositeContinuationTokens.Peek().Range.Min); Assert.AreEqual(originalToken.CompositeContinuationTokens.Peek().Range.Max, deserializedToken.CompositeContinuationTokens.Peek().Range.Max); Assert.AreEqual(originalToken.CompositeContinuationTokens.Peek().Range.IsMinInclusive, deserializedToken.CompositeContinuationTokens.Peek().Range.IsMinInclusive); Assert.AreEqual(originalToken.CompositeContinuationTokens.Peek().Range.IsMaxInclusive, deserializedToken.CompositeContinuationTokens.Peek().Range.IsMaxInclusive); } }
public async Task FeedRange_PKRangeId_Serialization() { string continuationToken = "TBD"; string containerRid = Guid.NewGuid().ToString(); DocumentFeedResponse <Documents.PartitionKeyRange> ranges = await this.Container.ClientContext.DocumentClient.ReadPartitionKeyRangeFeedAsync(this.Container.LinkUri); Documents.PartitionKeyRange oneRange = ranges.First(); FeedRangePartitionKeyRange original = new FeedRangePartitionKeyRange(oneRange.Id); FeedRangeCompositeContinuation feedRangeSimpleContinuation = new FeedRangeCompositeContinuation(containerRid, original, new List <Documents.Routing.Range <string> >() { oneRange.ToRange() }, continuationToken); string serialized = feedRangeSimpleContinuation.ToString(); Assert.IsTrue(FeedRangeContinuation.TryParse(serialized, out FeedRangeContinuation feedRangeContinuation)); FeedRangeCompositeContinuation deserialized = feedRangeContinuation as FeedRangeCompositeContinuation; FeedRangePartitionKeyRange deserializedFeedRange = deserialized.FeedRange as FeedRangePartitionKeyRange; Assert.IsNotNull(deserialized, "Error deserializing to FeedRangePartitionKeyRange"); Assert.AreEqual(original.PartitionKeyRangeId, deserializedFeedRange.PartitionKeyRangeId); Assert.AreEqual(continuationToken, deserialized.GetContinuation()); }
public async Task ReadFeedIteratorCore_OfT_ReadNextAsync() { string continuation = "TBD"; ResponseMessage responseMessage = new ResponseMessage(HttpStatusCode.OK); responseMessage.Headers.ContinuationToken = continuation; responseMessage.Headers[Documents.HttpConstants.HttpHeaders.ItemCount] = "1"; responseMessage.Content = new MemoryStream(Encoding.UTF8.GetBytes("{}")); Mock <CosmosClientContext> cosmosClientContext = new Mock <CosmosClientContext>(); cosmosClientContext.Setup(c => c.ClientOptions).Returns(new CosmosClientOptions()); cosmosClientContext .Setup(c => c.ProcessResourceOperationStreamAsync( It.IsAny <string>(), It.Is <Documents.ResourceType>(rt => rt == Documents.ResourceType.Document), It.IsAny <Documents.OperationType>(), It.IsAny <RequestOptions>(), It.IsAny <ContainerInternal>(), It.IsAny <PartitionKey?>(), It.IsAny <Stream>(), It.IsAny <Action <RequestMessage> >(), It.IsAny <CosmosDiagnosticsContext>(), It.IsAny <CancellationToken>())) .Returns(Task.FromResult(responseMessage)); ContainerInternal containerCore = Mock.Of <ContainerInternal>(); Mock.Get(containerCore) .Setup(c => c.ClientContext) .Returns(cosmosClientContext.Object); FeedRangeInternal range = Mock.Of <FeedRangeInternal>(); Mock.Get(range) .Setup(f => f.Accept(It.IsAny <FeedRangeVisitor>())); FeedRangeContinuation feedToken = Mock.Of <FeedRangeContinuation>(); Mock.Get(feedToken) .Setup(f => f.FeedRange) .Returns(range); Mock.Get(feedToken) .Setup(f => f.Accept(It.IsAny <FeedRangeVisitor>(), It.IsAny <Action <RequestMessage, string> >())); Mock.Get(feedToken) .Setup(f => f.HandleSplitAsync(It.Is <ContainerInternal>(c => c == containerCore), It.IsAny <ResponseMessage>(), It.IsAny <CancellationToken>())) .Returns(Task.FromResult(Documents.ShouldRetryResult.NoRetry())); Mock.Get(feedToken) .Setup(f => f.GetContinuation()) .Returns(continuation); Mock.Get(feedToken) .Setup(f => f.IsDone) .Returns(true); FeedRangeIteratorCore feedTokenIterator = new FeedRangeIteratorCore(containerCore, feedToken, new QueryRequestOptions(), Documents.ResourceType.Document, queryDefinition: null); bool creatorCalled = false; Func <ResponseMessage, FeedResponse <dynamic> > creator = (ResponseMessage r) => { creatorCalled = true; return(Mock.Of <FeedResponse <dynamic> >()); }; FeedIteratorCore <dynamic> feedTokenIteratorOfT = new FeedIteratorCore <dynamic>(feedTokenIterator, creator); FeedResponse <dynamic> response = await feedTokenIteratorOfT.ReadNextAsync(); Assert.IsTrue(creatorCalled, "Response creator not called"); Mock.Get(feedToken) .Verify(f => f.ReplaceContinuation(It.Is <string>(ct => ct == continuation)), Times.Once); Mock.Get(feedToken) .Verify(f => f.HandleSplitAsync(It.Is <ContainerInternal>(c => c == containerCore), It.IsAny <ResponseMessage>(), It.IsAny <CancellationToken>()), Times.Once); }
public async Task ChangeFeedIteratorCore_OfT_ReadNextAsync() { string continuation = "TBD"; ResponseMessage responseMessage = new ResponseMessage(HttpStatusCode.OK); responseMessage.Headers.ETag = continuation; responseMessage.Headers[Documents.HttpConstants.HttpHeaders.ItemCount] = "1"; Mock <CosmosClientContext> cosmosClientContext = new Mock <CosmosClientContext>(); cosmosClientContext.Setup(c => c.ClientOptions).Returns(new CosmosClientOptions()); cosmosClientContext .Setup(c => c.ProcessResourceOperationStreamAsync( It.IsAny <string>(), It.IsAny <Documents.ResourceType>(), It.IsAny <Documents.OperationType>(), It.IsAny <RequestOptions>(), It.IsAny <ContainerInternal>(), It.IsAny <PartitionKey?>(), It.IsAny <Stream>(), It.IsAny <Action <RequestMessage> >(), It.IsAny <CosmosDiagnosticsContext>(), It.IsAny <CancellationToken>())) .Returns(Task.FromResult(responseMessage)); ContainerInternal containerCore = Mock.Of <ContainerInternal>(); Mock.Get(containerCore) .Setup(c => c.ClientContext) .Returns(cosmosClientContext.Object); FeedRangeInternal range = Mock.Of <FeedRangeInternal>(); Mock.Get(range) .Setup(f => f.Accept(It.IsAny <FeedRangeRequestMessagePopulatorVisitor>())); FeedRangeContinuation feedToken = Mock.Of <FeedRangeContinuation>(); Mock.Get(feedToken) .Setup(f => f.FeedRange) .Returns(range); Mock.Get(feedToken) .Setup(f => f.HandleSplitAsync(It.Is <ContainerInternal>(c => c == containerCore), It.IsAny <ResponseMessage>(), It.IsAny <CancellationToken>())) .Returns(Task.FromResult(Documents.ShouldRetryResult.NoRetry())); Mock.Get(feedToken) .Setup(f => f.HandleChangeFeedNotModified(It.IsAny <ResponseMessage>())) .Returns(Documents.ShouldRetryResult.NoRetry()); ChangeFeedIteratorCore changeFeedIteratorCore = CreateWithCustomFeedToken(containerCore, feedToken); bool creatorCalled = false; Func <ResponseMessage, FeedResponse <dynamic> > creator = (ResponseMessage r) => { creatorCalled = true; return(Mock.Of <FeedResponse <dynamic> >()); }; FeedIteratorCore <dynamic> changeFeedIteratorCoreOfT = new FeedIteratorCore <dynamic>(changeFeedIteratorCore, creator); FeedResponse <dynamic> response = await changeFeedIteratorCoreOfT.ReadNextAsync(); Mock.Get(feedToken) .Verify(f => f.ReplaceContinuation(It.Is <string>(ct => ct == continuation)), Times.Once); Mock.Get(feedToken) .Verify(f => f.HandleSplitAsync(It.Is <ContainerInternal>(c => c == containerCore), It.IsAny <ResponseMessage>(), It.IsAny <CancellationToken>()), Times.Once); Mock.Get(feedToken) .Verify(f => f.HandleChangeFeedNotModified(It.IsAny <ResponseMessage>()), Times.Once); Assert.IsTrue(creatorCalled, "Response creator not called"); }
public ReadFeedIteratorCore( IDocumentContainer documentContainer, string continuationToken, ReadFeedPaginationOptions readFeedPaginationOptions, QueryRequestOptions queryRequestOptions, CancellationToken cancellationToken) { this.queryRequestOptions = queryRequestOptions; readFeedPaginationOptions ??= ReadFeedPaginationOptions.Default; if (!string.IsNullOrEmpty(continuationToken)) { bool isNewArrayFormat = (continuationToken.Length >= 2) && (continuationToken[0] == '[') && (continuationToken[continuationToken.Length - 1] == ']'); if (!isNewArrayFormat) { // One of the two older formats if (!FeedRangeContinuation.TryParse(continuationToken, out FeedRangeContinuation feedRangeContinuation)) { // Backward compatible with old format feedRangeContinuation = new FeedRangeCompositeContinuation( containerRid: string.Empty, FeedRangeEpk.FullRange, new List <Documents.Routing.Range <string> >() { new Documents.Routing.Range <string>( Documents.Routing.PartitionKeyInternal.MinimumInclusiveEffectivePartitionKey, Documents.Routing.PartitionKeyInternal.MaximumExclusiveEffectivePartitionKey, isMinInclusive: true, isMaxInclusive: false) }, continuationToken); } // need to massage it a little List <CosmosElement> feedRangeStates = new List <CosmosElement>(); string oldContinuationFormat = feedRangeContinuation.ToString(); if (feedRangeContinuation.FeedRange is FeedRangePartitionKey feedRangePartitionKey) { CosmosObject cosmosObject = CosmosObject.Parse(oldContinuationFormat); CosmosArray continuations = (CosmosArray)cosmosObject["Continuation"]; if (continuations.Count != 1) { throw new InvalidOperationException("Expected only one continuation for partition key queries"); } CosmosElement continuation = continuations[0]; CosmosObject continuationObject = (CosmosObject)continuation; CosmosElement token = continuationObject["token"]; ReadFeedState state; if (token is CosmosNull) { state = ReadFeedState.Beginning(); } else { CosmosString tokenAsString = (CosmosString)token; state = ReadFeedState.Continuation(CosmosElement.Parse(tokenAsString.Value)); } FeedRangeState <ReadFeedState> feedRangeState = new FeedRangeState <ReadFeedState>(feedRangePartitionKey, state); feedRangeStates.Add(ReadFeedFeedRangeStateSerializer.ToCosmosElement(feedRangeState)); } else { CosmosObject cosmosObject = CosmosObject.Parse(oldContinuationFormat); CosmosArray continuations = (CosmosArray)cosmosObject["Continuation"]; foreach (CosmosElement continuation in continuations) { CosmosObject continuationObject = (CosmosObject)continuation; CosmosObject rangeObject = (CosmosObject)continuationObject["range"]; string min = ((CosmosString)rangeObject["min"]).Value; string max = ((CosmosString)rangeObject["max"]).Value; CosmosElement token = continuationObject["token"]; FeedRangeInternal feedRange = new FeedRangeEpk(new Documents.Routing.Range <string>(min, max, isMinInclusive: true, isMaxInclusive: false)); ReadFeedState state; if (token is CosmosNull) { state = ReadFeedState.Beginning(); } else { CosmosString tokenAsString = (CosmosString)token; state = ReadFeedState.Continuation(CosmosElement.Parse(tokenAsString.Value)); } FeedRangeState <ReadFeedState> feedRangeState = new FeedRangeState <ReadFeedState>(feedRange, state); feedRangeStates.Add(ReadFeedFeedRangeStateSerializer.ToCosmosElement(feedRangeState)); } } CosmosArray cosmosArrayContinuationTokens = CosmosArray.Create(feedRangeStates); continuationToken = cosmosArrayContinuationTokens.ToString(); } } TryCatch <ReadFeedCrossFeedRangeState> monadicReadFeedState; if (continuationToken == null) { FeedRange feedRange; if ((this.queryRequestOptions != null) && this.queryRequestOptions.PartitionKey.HasValue) { feedRange = new FeedRangePartitionKey(this.queryRequestOptions.PartitionKey.Value); } else if ((this.queryRequestOptions != null) && (this.queryRequestOptions.FeedRange != null)) { feedRange = this.queryRequestOptions.FeedRange; } else { feedRange = FeedRangeEpk.FullRange; } monadicReadFeedState = TryCatch <ReadFeedCrossFeedRangeState> .FromResult(ReadFeedCrossFeedRangeState.CreateFromBeginning(feedRange)); } else { monadicReadFeedState = ReadFeedCrossFeedRangeState.Monadic.Parse(continuationToken); } if (monadicReadFeedState.Failed) { this.monadicEnumerator = TryCatch <CrossPartitionReadFeedAsyncEnumerator> .FromException(monadicReadFeedState.Exception); } else { this.monadicEnumerator = TryCatch <CrossPartitionReadFeedAsyncEnumerator> .FromResult( CrossPartitionReadFeedAsyncEnumerator.Create( documentContainer, new CrossFeedRangeState <ReadFeedState>(monadicReadFeedState.Result.FeedRangeStates), readFeedPaginationOptions, cancellationToken)); } this.hasMoreResults = true; }
public async Task ChangeFeed_FeedRange_FromV2SDK() { ContainerResponse largerContainer = await this.database.CreateContainerAsync( new ContainerProperties(id : Guid.NewGuid().ToString(), partitionKeyPath : "/status"), throughput : 20000, cancellationToken : this.cancellationToken); ContainerCore container = (ContainerInlineCore)largerContainer; int expected = 100; int count = 0; await this.CreateRandomItems(container, expected, randomPartitionKey : true); IReadOnlyList <FeedRange> feedRanges = await container.GetFeedRangesAsync(); List <string> continuations = new List <string>(); // First do one request to construct the old model information based on Etag foreach (FeedRange feedRange in feedRanges) { IEnumerable <string> pkRangeIds = await container.GetPartitionKeyRangesAsync(feedRange); ChangeFeedRequestOptions requestOptions = new ChangeFeedRequestOptions() { StartTime = DateTime.MinValue.ToUniversalTime(), MaxItemCount = 1 }; ChangeFeedIteratorCore feedIterator = container.GetChangeFeedStreamIterator(feedRange: feedRange, changeFeedRequestOptions: requestOptions) as ChangeFeedIteratorCore; ResponseMessage firstResponse = await feedIterator.ReadNextAsync(); if (firstResponse.IsSuccessStatusCode) { Collection <ToDoActivity> response = TestCommon.SerializerCore.FromStream <CosmosFeedResponseUtil <ToDoActivity> >(firstResponse.Content).Data; count += response.Count; } // Construct the continuation's range, using PKRangeId + ETag List <dynamic> ct = new List <dynamic>() { new { min = string.Empty, max = string.Empty, token = firstResponse.Headers.ETag } }; // Extract Etag and manually construct the continuation dynamic oldContinuation = new { V = 0, PKRangeId = pkRangeIds.First(), Continuation = ct }; continuations.Add(JsonConvert.SerializeObject(oldContinuation)); } // Now start the new iterators with the constructed continuations from migration foreach (string continuation in continuations) { ChangeFeedRequestOptions requestOptions = new ChangeFeedRequestOptions() { MaxItemCount = 100 }; ChangeFeedIteratorCore feedIterator = container.GetChangeFeedStreamIterator(continuationToken: continuation, changeFeedRequestOptions: requestOptions) as ChangeFeedIteratorCore; ResponseMessage firstResponse = await feedIterator.ReadNextAsync(); if (firstResponse.IsSuccessStatusCode) { Collection <ToDoActivity> response = TestCommon.SerializerCore.FromStream <CosmosFeedResponseUtil <ToDoActivity> >(firstResponse.Content).Data; count += response.Count; string migratedContinuation = firstResponse.ContinuationToken; Assert.IsTrue(FeedRangeContinuation.TryParse(migratedContinuation, out FeedRangeContinuation feedRangeContinuation)); Assert.IsTrue(feedRangeContinuation.FeedRange is FeedRangeEPK); } } Assert.AreEqual(expected, count); }
public async Task ChangeFeed_FeedRange_FromV0Token() { ContainerResponse largerContainer = await this.database.CreateContainerAsync( new ContainerProperties(id : Guid.NewGuid().ToString(), partitionKeyPath : "/status"), throughput : 20000, cancellationToken : this.cancellationToken); ContainerInternal container = (ContainerInlineCore)largerContainer; int expected = 100; int count = 0; await this.CreateRandomItems((ContainerCore)container, expected, randomPartitionKey : true); IReadOnlyList <FeedRange> feedRanges = await container.GetFeedRangesAsync(); List <string> continuations = new List <string>(); // First do one request to construct the old model information based on Etag foreach (FeedRange feedRange in feedRanges) { IEnumerable <string> pkRangeIds = await container.GetPartitionKeyRangesAsync(feedRange); ChangeFeedRequestOptions requestOptions = new ChangeFeedRequestOptions() { PageSizeHint = 1 }; ChangeFeedIteratorCore feedIterator = container.GetChangeFeedStreamIterator( changeFeedStartFrom: ChangeFeedStartFrom.Beginning(feedRange), changeFeedMode: ChangeFeedMode.Incremental, changeFeedRequestOptions: requestOptions) as ChangeFeedIteratorCore; ResponseMessage firstResponse = await feedIterator.ReadNextAsync(); FeedRangeEpk FeedRangeEpk = feedRange as FeedRangeEpk; // Construct the continuation's range, using PKRangeId + ETag List <dynamic> ct = new List <dynamic>() { new { min = FeedRangeEpk.Range.Min, max = FeedRangeEpk.Range.Max, token = (string)null } }; // Extract Etag and manually construct the continuation dynamic oldContinuation = new { V = 0, Rid = await container.GetCachedRIDAsync(cancellationToken : this.cancellationToken), Continuation = ct }; continuations.Add(JsonConvert.SerializeObject(oldContinuation)); } // Now start the new iterators with the constructed continuations from migration foreach (string continuation in continuations) { ChangeFeedRequestOptions requestOptions = new ChangeFeedRequestOptions() { PageSizeHint = 100, EmitOldContinuationToken = true, }; ChangeFeedIteratorCore feedIterator = container.GetChangeFeedStreamIterator( changeFeedStartFrom: ChangeFeedStartFrom.ContinuationToken(continuation), changeFeedMode: ChangeFeedMode.Incremental, changeFeedRequestOptions: requestOptions) as ChangeFeedIteratorCore; ResponseMessage firstResponse = await feedIterator.ReadNextAsync(); if (firstResponse.IsSuccessStatusCode) { Collection <ToDoActivity> response = TestCommon.SerializerCore.FromStream <CosmosFeedResponseUtil <ToDoActivity> >(firstResponse.Content).Data; count += response.Count; string migratedContinuation = firstResponse.ContinuationToken; Assert.IsTrue(FeedRangeContinuation.TryParse(migratedContinuation, out FeedRangeContinuation feedRangeContinuation)); Assert.IsTrue(feedRangeContinuation.FeedRange is FeedRangeEpk); } } Assert.AreEqual(expected, count); }