Esempio n. 1
0
        public async Task DeleteSnapshots(string streamId, ulong olderThanVersion, CancellationToken cancellationToken = default)
        {
            await this.ReadHeader(streamId, cancellationToken);

            var query = this.Client.CreateDocumentQuery <SnapshotDocument>(this.DocumentCollectionUri, new FeedOptions {
                PartitionKey = this.PartitionKey
            })
                        .Where(x => x.StreamId == streamId)
                        .Where(x => x.DocumentType == DocumentType.Snapshot)
                        .Where(x => x.Version < olderThanVersion)
                        .AsDocumentQuery();

            while (query.HasMoreResults)
            {
                var page = await query.ExecuteNextAsync <Document>(cancellationToken);

                foreach (var document in page)
                {
                    if (this.DeleteMode == DeleteMode.SoftDelete)
                    {
                        var doc = EveneumDocument.Parse(document, this.JsonSerializerSettings);
                        doc.Deleted = true;
                        await this.Client.UpsertDocumentAsync(this.DocumentCollectionUri, doc, new RequestOptions { PartitionKey = this.PartitionKey }, disableAutomaticIdGeneration : true, cancellationToken);
                    }
                    else
                    {
                        await this.Client.DeleteDocumentAsync(UriFactory.CreateDocumentUri(this.Database, this.Collection, document.Id), new RequestOptions { PartitionKey = this.PartitionKey }, cancellationToken);
                    }
                }
            }
        }
Esempio n. 2
0
        public async Task ThenTheEventInVersionInStreamIsReplaced(ulong version, string streamId)
        {
            var currentDocuments = await CosmosSetup.QueryAllDocumentsInStream(this.Context.Client, this.Context.Database, this.Context.Container, streamId, DocumentType.Event);

            var eventDocument = currentDocuments.SingleOrDefault(x => x.Id == EveneumDocument.GenerateEventId(streamId, version));

            Assert.AreEqual(DocumentType.Event, eventDocument.DocumentType);
            Assert.AreEqual(streamId, eventDocument.StreamId);
            Assert.AreEqual(this.Context.EventStoreOptions.TypeProvider.GetIdentifierForType(this.Context.ReplacedEvent.Body.GetType()), eventDocument.BodyType);
            Assert.NotNull(eventDocument.Body);
            Assert.AreEqual(JToken.FromObject(this.Context.ReplacedEvent.Body, JsonSerializer.Create(this.Context.JsonSerializerSettings)), eventDocument.Body);
            Assert.NotNull(eventDocument.ETag);
            Assert.False(eventDocument.Deleted);

            if (this.Context.ReplacedEvent.Metadata == null)
            {
                Assert.IsNull(eventDocument.MetadataType);
                Assert.IsNull(eventDocument.Metadata);
            }
            else
            {
                Assert.AreEqual(this.Context.EventStoreOptions.TypeProvider.GetIdentifierForType(this.Context.ReplacedEvent.Metadata.GetType()), eventDocument.MetadataType);
                Assert.NotNull(eventDocument.Metadata);
                Assert.AreEqual(JToken.FromObject(this.Context.ReplacedEvent.Metadata, JsonSerializer.Create(this.Context.JsonSerializerSettings)), eventDocument.Metadata);
            }
        }
Esempio n. 3
0
        private async Task LoadChangeFeed(Func <IEnumerable <EveneumDocument>, Task> callback, string token = null, CancellationToken cancellationToken = default)
        {
            PartitionKeyRange partitionKeyRange = this.PartitionKey != null ? null : (await this.GetPartitionKeyRanges()).FirstOrDefault();

            var changeFeed = this.Client.CreateDocumentChangeFeedQuery(this.DocumentCollectionUri,
                                                                       new ChangeFeedOptions
            {
                PartitionKeyRangeId = partitionKeyRange?.Id,
                PartitionKey        = this.PartitionKey,
                RequestContinuation = token,
                StartFromBeginning  = true,
                MaxItemCount        = 10000,
            });

            Task callbackTask = null;

            while (changeFeed.HasMoreResults)
            {
                var page = await changeFeed.ExecuteNextAsync <Document>(cancellationToken);

                if (callbackTask != null)
                {
                    await callbackTask;
                }

                callbackTask = callback(page.Select(x => EveneumDocument.Parse(x, this.JsonSerializerSettings)));
            }
        }
        public Snapshot DeserializeSnapshot(EveneumDocument document)
        {
            var metadata = DeserializeObject(document.MetadataType, document.Metadata);
            var body     = DeserializeObject(document.BodyType, document.Body);

            return(new Snapshot(body, metadata, document.Version));
        }
        public EventData DeserializeEvent(EveneumDocument document)
        {
            var metadata = DeserializeObject(document.MetadataType, document.Metadata);
            var body     = DeserializeObject(document.BodyType, document.Body);

            return(new EventData(document.StreamId, body, metadata, document.Version));
        }
Esempio n. 6
0
        public async Task ThenTheSnapshotForVersionIsPersisted(ulong version)
        {
            var streamId = ScenarioContext.Current.GetStreamId();
            var snapshot = ScenarioContext.Current.GetSnapshot();

            var snapshotUri = UriFactory.CreateDocumentUri(this.Context.Database, this.Context.Collection, SnapshotDocument.GenerateId(streamId, version));
            var snapshotDocumentResponse = await this.Context.Client.ReadDocumentAsync <SnapshotDocument>(snapshotUri, new RequestOptions { PartitionKey = this.Context.PartitionKey });

            Assert.IsNotNull(snapshotDocumentResponse.Document);

            var snapshotDocument = snapshotDocumentResponse.Document;
            var snapshotMetadata = ScenarioContext.Current.GetSnapshotMetadata();

            Assert.AreEqual(this.Context.Partition, snapshotDocument.Partition);
            Assert.AreEqual(DocumentType.Snapshot, snapshotDocument.DocumentType);
            Assert.AreEqual(streamId, snapshotDocument.StreamId);
            Assert.AreEqual(version, snapshotDocument.Version);
            Assert.AreEqual(version + EveneumDocument.GetOrderingFraction(DocumentType.Snapshot), snapshotDocument.SortOrder);

            if (snapshotMetadata == null)
            {
                Assert.IsNull(snapshotDocument.MetadataType);
                Assert.IsFalse(snapshotDocument.Metadata.HasValues);
            }
            else
            {
                Assert.AreEqual(snapshotMetadata.GetType().AssemblyQualifiedName, snapshotDocument.MetadataType);
                Assert.AreEqual(JToken.FromObject(snapshotMetadata), snapshotDocument.Metadata);
            }

            Assert.AreEqual(snapshot.GetType().AssemblyQualifiedName, snapshotDocument.BodyType);
            Assert.AreEqual(JToken.FromObject(snapshot), snapshotDocument.Body);
            Assert.False(snapshotDocument.Deleted);
            Assert.IsNotNull(snapshotDocument.ETag);
        }
 internal void SerializeHeaderMetadata(EveneumDocument header, object metadata)
 {
     if (metadata != null)
     {
         header.MetadataType = this.TypeProvider.GetIdentifierForType(metadata.GetType());
         header.Metadata     = JToken.FromObject(metadata, this.JsonSerializer);
     }
 }
Esempio n. 8
0
        public async Task LoadEvents(string sql, Func <IReadOnlyCollection <EventData>, Task> callback, CancellationToken cancellationToken = default)
        {
            var query = this.Client.CreateDocumentQuery <Document>(this.DocumentCollectionUri, sql, new FeedOptions {
                PartitionKey = this.PartitionKey, MaxItemCount = -1
            }).AsDocumentQuery();

            do
            {
                var page = await query.ExecuteNextAsync <Document>(cancellationToken);

                await callback(page.Select(x => EveneumDocument.Parse(x, this.JsonSerializerSettings)).Where(x => !x.Deleted).OfType <EventDocument>().Select(Deserialize).ToList());
            }while (query.HasMoreResults);
        }
Esempio n. 9
0
        public static async Task <IReadOnlyCollection <EveneumDocument> > All(this IDocumentQuery <Document> query, JsonSerializerSettings jsonSerializerSettings)
        {
            var documents = new List <EveneumDocument>();

            while (query.HasMoreResults)
            {
                var page = await query.ExecuteNextAsync <Document>();

                documents.AddRange(page.Select(x => EveneumDocument.Parse(x, jsonSerializerSettings)));
            }

            return(documents);
        }
Esempio n. 10
0
        public async Task DeleteStream(string streamId, ulong expectedVersion, CancellationToken cancellationToken = default)
        {
            var header = new HeaderDocument
            {
                Partition = this.Partition,
                StreamId  = streamId,
                Version   = expectedVersion
            };

            var existingHeader = await this.ReadHeader(streamId, cancellationToken);

            if (existingHeader.Deleted)
            {
                throw new StreamNotFoundException(streamId);
            }

            if (existingHeader.Version != expectedVersion)
            {
                throw new OptimisticConcurrencyException(streamId, expectedVersion, existingHeader.Version);
            }

            string etag = existingHeader.ETag;

            var query = this.Client.CreateDocumentQuery <EveneumDocument>(this.DocumentCollectionUri, new FeedOptions {
                PartitionKey = this.PartitionKey
            })
                        .Where(x => x.StreamId == streamId)
                        .AsDocumentQuery();

            while (query.HasMoreResults)
            {
                var page = await query.ExecuteNextAsync <Document>(cancellationToken);

                foreach (var document in page)
                {
                    if (this.DeleteMode == DeleteMode.SoftDelete)
                    {
                        var doc = EveneumDocument.Parse(document, this.JsonSerializerSettings);
                        doc.Deleted = true;
                        await this.Client.UpsertDocumentAsync(this.DocumentCollectionUri, doc, new RequestOptions { PartitionKey = this.PartitionKey }, disableAutomaticIdGeneration : true, cancellationToken);
                    }
                    else
                    {
                        await this.Client.DeleteDocumentAsync(document.SelfLink, new RequestOptions { PartitionKey = this.PartitionKey }, cancellationToken);
                    }
                }
            }
        }
Esempio n. 11
0
        private static async Task <List <TDocument> > Query <TDocument>(IDocumentClient client, string database, string collection, string query, FeedOptions feedOptions)
            where TDocument : EveneumDocument
        {
            var documentQuery = client.CreateDocumentQuery <Document>(UriFactory.CreateDocumentCollectionUri(database, collection), query, feedOptions).AsDocumentQuery();

            var documents = new List <TDocument>();

            do
            {
                var page = await documentQuery.ExecuteNextAsync <Document>();

                documents.AddRange(page.Select(x => EveneumDocument.Parse(x, new JsonSerializerSettings())).OfType <TDocument>());
            }while (documentQuery.HasMoreResults);

            return(documents);
        }
Esempio n. 12
0
        public async Task ThenTheHeaderVersionWithNoMetadataIsPersisted(ulong version)
        {
            var headerDocuments = await CosmosSetup.QueryAllDocumentsInStream(this.Context.Client, this.Context.Database, this.Context.Container, this.Context.StreamId, DocumentType.Header);

            Assert.AreEqual(1, headerDocuments.Count);

            var headerDocument = headerDocuments[0];

            Assert.AreEqual(DocumentType.Header, headerDocument.DocumentType);
            Assert.AreEqual(this.Context.StreamId, headerDocument.StreamId);
            Assert.AreEqual(version, headerDocument.Version);
            Assert.AreEqual(version + EveneumDocument.GetOrderingFraction(DocumentType.Header), headerDocument.SortOrder);
            Assert.IsNull(headerDocument.MetadataType);
            Assert.IsFalse(headerDocument.Metadata.HasValues);
            Assert.NotNull(headerDocument.ETag);
            Assert.False(headerDocument.Deleted);
        }
        internal EveneumDocument SerializeSnapshot(object snapshot, object metadata, ulong version, string streamId)
        {
            var document = new EveneumDocument(DocumentType.Snapshot)
            {
                StreamId = streamId,
                Version  = version,
                BodyType = this.TypeProvider.GetIdentifierForType(snapshot.GetType()),
                Body     = JToken.FromObject(snapshot, this.JsonSerializer)
            };

            if (metadata != null)
            {
                document.MetadataType = this.TypeProvider.GetIdentifierForType(metadata.GetType());
                document.Metadata     = JToken.FromObject(metadata, this.JsonSerializer);
            }

            return(document);
        }
        internal EveneumDocument SerializeEvent(EventData @event, string streamId)
        {
            var document = new EveneumDocument(DocumentType.Event)
            {
                StreamId = streamId,
                Version  = @event.Version,
                BodyType = this.TypeProvider.GetIdentifierForType(@event.Body.GetType()),
                Body     = JToken.FromObject(@event.Body, this.JsonSerializer)
            };

            if (@event.Metadata != null)
            {
                document.MetadataType = this.TypeProvider.GetIdentifierForType(@event.Metadata.GetType());
                document.Metadata     = JToken.FromObject(@event.Metadata, this.JsonSerializer);
            }

            return(document);
        }
Esempio n. 15
0
        public async Task ThenTheHeaderVersionWithNoMetadataIsPersisted(ulong version)
        {
            var headerDocumentResponse = await this.Context.Client.ReadDocumentAsync <HeaderDocument>(UriFactory.CreateDocumentUri(this.Context.Database, this.Context.Collection, ScenarioContext.Current.GetStreamId()), new RequestOptions { PartitionKey = this.Context.PartitionKey });

            Assert.IsNotNull(headerDocumentResponse.Document);

            var headerDocument = headerDocumentResponse.Document;

            Assert.AreEqual(this.Context.Partition, headerDocument.Partition);
            Assert.AreEqual(DocumentType.Header, headerDocument.DocumentType);
            Assert.AreEqual(ScenarioContext.Current.GetStreamId(), headerDocument.StreamId);
            Assert.AreEqual(version, headerDocument.Version);
            Assert.AreEqual(version + EveneumDocument.GetOrderingFraction(DocumentType.Header), headerDocument.SortOrder);
            Assert.IsNull(headerDocument.MetadataType);
            Assert.IsFalse(headerDocument.Metadata.HasValues);
            Assert.NotNull(headerDocument.ETag);
            Assert.False(headerDocument.Deleted);
        }
Esempio n. 16
0
        public async Task ThenTheHeaderVersionWithMetadataIsPersisted(ulong version)
        {
            var headerDocuments = await CosmosSetup.QueryAllDocumentsInStream(this.Context.Client, this.Context.Database, this.Context.Container, this.Context.StreamId, DocumentType.Header);

            Assert.AreEqual(1, headerDocuments.Count);

            var headerDocument = headerDocuments[0];

            Assert.AreEqual(DocumentType.Header, headerDocument.DocumentType);
            Assert.AreEqual(this.Context.StreamId, headerDocument.StreamId);
            Assert.AreEqual(version, headerDocument.Version);
            Assert.AreEqual(version + EveneumDocument.GetOrderingFraction(DocumentType.Header), headerDocument.SortOrder);
            Assert.AreEqual(this.Context.EventStoreOptions.TypeProvider.GetIdentifierForType(typeof(SampleMetadata)), headerDocument.MetadataType);
            Assert.NotNull(headerDocument.Metadata);
            Assert.AreEqual(JToken.FromObject(this.Context.HeaderMetadata), headerDocument.Metadata);
            Assert.NotNull(headerDocument.ETag);
            Assert.False(headerDocument.Deleted);
        }
Esempio n. 17
0
        public async Task ThenNewEventsAreAppended()
        {
            var streamId         = this.Context.StreamId;
            var currentDocuments = await CosmosSetup.QueryAllDocumentsInStream(this.Context.Client, this.Context.Database, this.Context.Container, streamId, DocumentType.Event);

            var existingDocumentIds = this.Context.ExistingDocuments.Select(x => x.Id);

            var newEventDocuments = currentDocuments.Where(x => !existingDocumentIds.Contains(x.Id)).ToList();

            var newEvents = this.Context.NewEvents;

            Assert.AreEqual(newEventDocuments.Count, newEvents.Length);

            foreach (var newEvent in newEvents)
            {
                var eventDocument = newEventDocuments.Find(x => x.Id == EveneumDocument.GenerateEventId(streamId, newEvent.Version));

                Assert.IsNotNull(eventDocument);
                Assert.AreEqual(DocumentType.Event, eventDocument.DocumentType);
                Assert.AreEqual(streamId, eventDocument.StreamId);
                Assert.AreEqual(this.Context.EventStoreOptions.TypeProvider.GetIdentifierForType(newEvent.Body.GetType()), eventDocument.BodyType);
                Assert.NotNull(eventDocument.Body);
                Assert.AreEqual(JToken.FromObject(newEvent.Body, JsonSerializer.Create(this.Context.JsonSerializerSettings)), eventDocument.Body);
                Assert.NotNull(eventDocument.ETag);
                Assert.False(eventDocument.Deleted);

                if (newEvent.Metadata == null)
                {
                    Assert.IsNull(eventDocument.MetadataType);
                    Assert.IsNull(eventDocument.Metadata);
                }
                else
                {
                    Assert.AreEqual(this.Context.EventStoreOptions.TypeProvider.GetIdentifierForType(newEvent.Metadata.GetType()), eventDocument.MetadataType);
                    Assert.NotNull(eventDocument.Metadata);
                    Assert.AreEqual(JToken.FromObject(newEvent.Metadata, JsonSerializer.Create(this.Context.JsonSerializerSettings)), eventDocument.Metadata);
                }
            }
        }
Esempio n. 18
0
        public async Task ThenTheSnapshotForVersionIsPersisted(ulong version)
        {
            var streamId = this.Context.StreamId;
            var snapshot = this.Context.Snapshot;

            var snapshotDocuments = await CosmosSetup.QueryAllDocumentsInStream(this.Context.Client, this.Context.Database, this.Context.Container, this.Context.StreamId, DocumentType.Snapshot);

            Assert.IsNotEmpty(snapshotDocuments);

            var snapshotDocument = snapshotDocuments.Find(x => x.Version == version);

            Assert.IsNotNull(snapshotDocument);

            var snapshotMetadata = this.Context.SnapshotMetadata;

            Assert.AreEqual(DocumentType.Snapshot, snapshotDocument.DocumentType);
            Assert.AreEqual(streamId, snapshotDocument.StreamId);
            Assert.AreEqual(version, snapshotDocument.Version);
            Assert.AreEqual(version + EveneumDocument.GetOrderingFraction(DocumentType.Snapshot), snapshotDocument.SortOrder);

            if (snapshotMetadata == null)
            {
                Assert.IsNull(snapshotDocument.MetadataType);
                Assert.IsFalse(snapshotDocument.Metadata.HasValues);
            }
            else
            {
                Assert.AreEqual(snapshotMetadata.GetType().AssemblyQualifiedName, snapshotDocument.MetadataType);
                Assert.AreEqual(JToken.FromObject(snapshotMetadata), snapshotDocument.Metadata);
            }

            Assert.AreEqual(snapshot.GetType().AssemblyQualifiedName, snapshotDocument.BodyType);
            Assert.AreEqual(JToken.FromObject(snapshot), snapshotDocument.Body);
            Assert.False(snapshotDocument.Deleted);
            Assert.IsNotNull(snapshotDocument.ETag);
        }
Esempio n. 19
0
        public async Task <Stream?> ReadStream(string streamId, CancellationToken cancellationToken = default)
        {
            if (streamId == null)
            {
                throw new ArgumentNullException(nameof(streamId));
            }

            var sql   = $"SELECT * FROM x WHERE x.{nameof(EveneumDocument.StreamId)} = '{streamId}' ORDER BY x.{nameof(EveneumDocument.SortOrder)} DESC";
            var query = this.Client.CreateDocumentQuery <Document>(this.DocumentCollectionUri, sql, new FeedOptions {
                PartitionKey = this.PartitionKey
            }).AsDocumentQuery();

            var documents     = new List <EveneumDocument>();
            var finishLoading = false;

            do
            {
                var page = await query.ExecuteNextAsync <Document>(cancellationToken);

                foreach (var document in page)
                {
                    var eveneumDoc = EveneumDocument.Parse(document, this.JsonSerializerSettings);

                    if (eveneumDoc is HeaderDocument && eveneumDoc.Deleted)
                    {
                        throw new StreamNotFoundException(streamId);
                    }

                    if (eveneumDoc.Deleted)
                    {
                        continue;
                    }

                    documents.Add(eveneumDoc);

                    if (eveneumDoc is SnapshotDocument)
                    {
                        finishLoading = true;
                        break;
                    }
                }

                if (finishLoading)
                {
                    break;
                }
            }while (query.HasMoreResults);

            if (documents.Count == 0)
            {
                return(null);
            }

            var headerDocument = documents.First() as HeaderDocument;
            var events         = documents.OfType <EventDocument>().Select(this.Deserialize).Reverse().ToArray();
            var snapshot       = documents.OfType <SnapshotDocument>().Select(this.Deserialize).Cast <Snapshot?>().FirstOrDefault();

            object metadata = null;

            if (!string.IsNullOrEmpty(headerDocument.MetadataType))
            {
                metadata = headerDocument.Metadata.ToObject(this.TypeCache.Resolve(headerDocument.MetadataType), this.JsonSerializer);
            }

            return(new Stream(streamId, headerDocument.Version, metadata, events, snapshot));
        }
Esempio n. 20
0
        public async Task <Response> ReplaceEvent(EventData newEvent, CancellationToken cancellationToken = default)
        {
            try
            {
                var response = await this.Container.ReplaceItemAsync(this.Serializer.SerializeEvent(newEvent, newEvent.StreamId), EveneumDocument.GenerateEventId(newEvent.StreamId, newEvent.Version), new PartitionKey(newEvent.StreamId), cancellationToken : cancellationToken);

                return(new Response(response.RequestCharge));
            }
            catch (CosmosException ex)
            {
                throw new WriteException(newEvent.StreamId, ex.RequestCharge, ex.Message, ex.StatusCode, ex);
            }
        }
Esempio n. 21
0
 public DocumentResponse(EveneumDocument document, double requestCharge)
     : base(requestCharge)
 {
     this.Document = document;
 }
Esempio n. 22
0
        public async Task <Response> WriteToStream(string streamId, EventData[] events, ulong?expectedVersion = null, object metadata = null, CancellationToken cancellationToken = default)
        {
            if (!this.IsInitialized)
            {
                throw new NotInitializedException();
            }

            var    transaction   = this.Container.CreateTransactionalBatch(new PartitionKey(streamId));
            double requestCharge = 0;

            // Existing stream
            if (expectedVersion.HasValue)
            {
                var headerResponse = await this.ReadHeader(streamId, cancellationToken);

                var header = headerResponse.Document;
                requestCharge += headerResponse.RequestCharge;

                if (header.Version != expectedVersion)
                {
                    throw new OptimisticConcurrencyException(streamId, expectedVersion.Value, header.Version);
                }

                header.Version += (ulong)events.Length;

                this.Serializer.SerializeHeaderMetadata(header, metadata);

                transaction.ReplaceItem(header.Id, header, new TransactionalBatchItemRequestOptions {
                    IfMatchEtag = header.ETag
                });
            }
            else
            {
                var header = new EveneumDocument(DocumentType.Header)
                {
                    StreamId = streamId, Version = (ulong)events.Length
                };

                this.Serializer.SerializeHeaderMetadata(header, metadata);

                transaction.CreateItem(header);
            }

            var firstBatch = events.Take(this.BatchSize - 1).Select(@event => this.Serializer.SerializeEvent(@event, streamId));

            foreach (var document in firstBatch)
            {
                transaction.CreateItem(document);
            }

            var response = await transaction.ExecuteAsync(cancellationToken);

            requestCharge += response.RequestCharge;

            if (response.StatusCode == System.Net.HttpStatusCode.Conflict)
            {
                throw new StreamAlreadyExistsException(streamId);
            }
            else
            if (!response.IsSuccessStatusCode)
            {
                throw new WriteException(response.ErrorMessage, response.StatusCode);
            }

            foreach (var batch in events.Skip(this.BatchSize - 1).Select(@event => this.Serializer.SerializeEvent(@event, streamId)).Batch(this.BatchSize))
            {
                transaction = this.Container.CreateTransactionalBatch(new PartitionKey(streamId));

                foreach (var document in batch)
                {
                    transaction.CreateItem(document);
                }

                response = await transaction.ExecuteAsync(cancellationToken);

                requestCharge += response.RequestCharge;

                if (!response.IsSuccessStatusCode)
                {
                    throw new WriteException(response.ErrorMessage, response.StatusCode);
                }
            }

            return(new Response(requestCharge));
        }