Beispiel #1
0
        private void SetupEvent(IEvent? @event)
        {
            var storedEvent =
                new StoredEvent("stream", "0", -1,
                                new EventData("type", new EnvelopeHeaders(), "payload"));

            var storedEvents = new List <StoredEvent>
            {
                storedEvent
            };

            if (@event != null)
            {
                A.CallTo(() => eventDataFormatter.ParseIfKnown(storedEvent))
                .Returns(Envelope.Create(@event));
            }
            else
            {
                A.CallTo(() => eventDataFormatter.ParseIfKnown(storedEvent))
                .Returns(null);
            }

            A.CallTo(() => eventStore.QueryAllAsync("^asset\\-", null, int.MaxValue, default))
            .Returns(storedEvents.ToAsyncEnumerable());
        }
Beispiel #2
0
        private void SetupEventStore(Dictionary <DomainId, List <MyEvent> > streams)
        {
            var storedStreams = new Dictionary <string, IReadOnlyList <StoredEvent> >();

            foreach (var(id, stream) in streams)
            {
                var storedStream = new List <StoredEvent>();

                var i = 0;

                foreach (var @event in stream)
                {
                    var eventData   = new EventData("Type", new EnvelopeHeaders(), "Payload");
                    var eventStored = new StoredEvent(id.ToString(), i.ToString(), i, eventData);

                    storedStream.Add(eventStored);

                    A.CallTo(() => eventDataFormatter.Parse(eventStored))
                    .Returns(new Envelope <IEvent>(@event));

                    A.CallTo(() => eventDataFormatter.ParseIfKnown(eventStored))
                    .Returns(new Envelope <IEvent>(@event));

                    i++;
                }

                storedStreams[id.ToString()] = storedStream;
            }

            var streamNames = streams.Keys.Select(x => x.ToString()).ToArray();

            A.CallTo(() => eventStore.QueryManyAsync(A <IEnumerable <string> > .That.IsSameSequenceAs(streamNames)))
            .Returns(storedStreams);
        }
        public EventConsumerGrainTests()
        {
            grainState.Value = new EventConsumerState
            {
                Position = initialPosition
            };

            consumerName = eventConsumer.GetType().Name;

            A.CallTo(() => eventStore.CreateSubscription(A <IEventSubscriber> ._, A <string> ._, A <string> ._))
            .Returns(eventSubscription);

            A.CallTo(() => eventConsumer.Name)
            .Returns(consumerName);

            A.CallTo(() => eventConsumer.Handles(A <StoredEvent> ._))
            .Returns(true);

            A.CallTo(() => eventConsumer.On(A <IEnumerable <Envelope <IEvent> > > ._))
            .Invokes((IEnumerable <Envelope <IEvent> > events) =>
            {
                foreach (var @event in events)
                {
                    eventConsumer.On(@event).Wait();
                }
            });

            A.CallTo(() => eventSubscription.Sender)
            .Returns(eventSubscription);

            storedEvent = new StoredEvent("Stream", Guid.NewGuid().ToString(), 123, eventData);

            A.CallTo(() => formatter.ParseIfKnown(storedEvent))
            .Returns(envelope);

            var log = A.Fake <ILogger <EventConsumerGrain> >();

            sut = new MyEventConsumerGrain(
                x => eventConsumer,
                grainState,
                eventStore,
                formatter,
                log);
        }
        public async Task Should_ignore_old_events()
        {
            var storedEvent = new StoredEvent("1", "1", 0, new EventData("Type", new EnvelopeHeaders(), "Payload"));

            A.CallTo(() => eventStore.QueryAsync(key.ToString(), 0, A <CancellationToken> ._))
            .Returns(new List <StoredEvent> {
                storedEvent
            });

            A.CallTo(() => eventDataFormatter.ParseIfKnown(storedEvent))
            .Returns(null);

            var persistedEvents = Save.Events();
            var persistence     = sut.WithEventSourcing(None.Type, key, persistedEvents.Write);

            await persistence.ReadAsync();

            Assert.Empty(persistedEvents);
            Assert.Equal(0, persistence.Version);
        }
        public EventConsumerGrainTests()
        {
            grainState.Value = new EventConsumerState
            {
                Position = initialPosition
            };

            consumerName = eventConsumer.GetType().Name;

            A.CallTo(() => eventStore.CreateSubscription(A <IEventSubscriber> ._, A <string> ._, A <string> ._))
            .Returns(eventSubscription);

            A.CallTo(() => eventConsumer.Name)
            .Returns(consumerName);

            A.CallTo(() => eventConsumer.Handles(A <StoredEvent> ._))
            .Returns(true);

            A.CallTo(() => eventConsumer.On(A <IEnumerable <Envelope <IEvent> > > ._))
            .Invokes((IEnumerable <Envelope <IEvent> > events) =>
            {
                foreach (var @event in events)
                {
                    eventConsumer.On(@event).Wait();
                }
            });

            A.CallTo(() => eventSubscription.Sender)
            .Returns(eventSubscription);

            A.CallTo(() => formatter.ParseIfKnown(A <StoredEvent> .That.Matches(x => x.Data == eventData)))
            .Returns(envelope);

            sut = new MyEventConsumerGrain(
                x => eventConsumer,
                grainState,
                eventStore,
                formatter,
                log);
        }
        public async Task <List <SimulatedRuleEvent> > SimulateAsync(NamedId <DomainId> appId, DomainId ruleId, Rule rule,
                                                                     CancellationToken ct = default)
        {
            Guard.NotNull(rule);

            var context = new RuleContext
            {
                AppId          = appId,
                Rule           = rule,
                RuleId         = ruleId,
                IncludeSkipped = true,
                IncludeStale   = true
            };

            var simulatedEvents = new List <SimulatedRuleEvent>(MaxSimulatedEvents);

            var fromNow = SystemClock.Instance.GetCurrentInstant().Minus(Duration.FromDays(7));

            await foreach (var storedEvent in eventStore.QueryAllReverseAsync($"^([a-zA-Z0-9]+)\\-{appId.Id}", fromNow, MaxSimulatedEvents, ct))
            {
                var @event = eventDataFormatter.ParseIfKnown(storedEvent);

                if (@event?.Payload is AppEvent appEvent)
                {
                    // Also create jobs for rules with failing conditions because we want to show them in th table.
                    await foreach (var result in ruleService.CreateJobsAsync(@event, context, ct))
                    {
                        var eventName = result.Job?.EventName;

                        if (string.IsNullOrWhiteSpace(eventName))
                        {
                            eventName = ruleService.GetName(appEvent);
                        }

                        simulatedEvents.Add(new SimulatedRuleEvent
                        {
                            ActionData    = result.Job?.ActionData,
                            ActionName    = result.Job?.ActionName,
                            EnrichedEvent = result.EnrichedEvent,
                            Error         = result.EnrichmentError?.Message,
                            Event         = @event.Payload,
                            EventId       = @event.Headers.EventId(),
                            EventName     = eventName,
                            SkipReason    = result.SkipReason
                        });
                    }
                }
            }

            return(simulatedEvents);
        }
        private void SetupEvent(IEvent? @event)
        {
            var storedEvent = new StoredEvent("stream", "0", -1, new EventData("type", new EnvelopeHeaders(), "payload"));

            if (@event != null)
            {
                A.CallTo(() => eventDataFormatter.ParseIfKnown(storedEvent))
                .Returns(Envelope.Create(@event));
            }
            else
            {
                A.CallTo(() => eventDataFormatter.ParseIfKnown(storedEvent))
                .Returns(null);
            }

            A.CallTo(() => eventStore.QueryAsync(A <Func <StoredEvent, Task> > ._, "^asset\\-", null, default))
            .Invokes(x =>
            {
                var callback = x.GetArgument <Func <StoredEvent, Task> >(0) !;

                callback(storedEvent).Wait();
            });
        }
Beispiel #8
0
        private async Task EnqueueFromEventsAsync(State currentState, RuleContext context,
                                                  CancellationToken ct)
        {
            var errors = 0;

            var filter = $"^([a-z]+)\\-{Key}";

            await foreach (var storedEvent in eventStore.QueryAllAsync(filter, currentState.Position, ct: ct))
            {
                try
                {
                    var @event = eventDataFormatter.ParseIfKnown(storedEvent);

                    if (@event != null)
                    {
                        var jobs = ruleService.CreateJobsAsync(@event, context, ct);

                        await foreach (var job in jobs.WithCancellation(ct))
                        {
                            if (job.Job != null && job.SkipReason == SkipReason.None)
                            {
                                await ruleEventRepository.EnqueueAsync(job.Job, job.EnrichmentError, ct);
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    errors++;

                    if (errors >= MaxErrors)
                    {
                        throw;
                    }

                    log.LogWarning(ex, w => w
                                   .WriteProperty("action", "runRule")
                                   .WriteProperty("status", "failedPartially"));
                }
                finally
                {
                    currentState.Position = storedEvent.EventPosition;
                }

                await state.WriteAsync();
            }
        }
Beispiel #9
0
        private async Task EnqueueFromEventsAsync(State currentState, IRuleEntity rule, CancellationToken ct)
        {
            var errors = 0;

            await eventStore.QueryAsync(async storedEvent =>
            {
                try
                {
                    var @event = eventDataFormatter.ParseIfKnown(storedEvent);

                    if (@event != null)
                    {
                        var jobs = await ruleService.CreateJobsAsync(rule.RuleDef, rule.Id, @event, false);

                        foreach (var(job, ex) in jobs)
                        {
                            await ruleEventRepository.EnqueueAsync(job, ex);
                        }
                    }
                }
                catch (Exception ex)
                {
                    errors++;

                    if (errors >= MaxErrors)
                    {
                        throw;
                    }

                    log.LogWarning(ex, w => w
                                   .WriteProperty("action", "runRule")
                                   .WriteProperty("status", "failedPartially"));
                }
                finally
                {
                    currentState.Position = storedEvent.EventPosition;
                }

                await state.WriteAsync();
            }, $"^([a-z]+)\\-{Key}", currentState.Position, ct);
        }
Beispiel #10
0
        public async Task RepairAsync(CancellationToken ct = default)
        {
            await eventStore.QueryAsync(async storedEvent =>
            {
                var @event = eventDataFormatter.ParseIfKnown(storedEvent);

                if (@event != null)
                {
                    switch (@event.Payload)
                    {
                    case AssetCreated assetCreated:
                        await TryRepairAsync(assetCreated.AppId, assetCreated.AssetId, assetCreated.FileVersion, ct);
                        break;

                    case AssetUpdated assetUpdated:
                        await TryRepairAsync(assetUpdated.AppId, assetUpdated.AssetId, assetUpdated.FileVersion, ct);
                        break;
                    }
                }
            }, "^asset\\-", ct : ct);
        }
        public async Task <List <SimulatedRuleEvent> > SimulateAsync(IRuleEntity rule, CancellationToken ct)
        {
            Guard.NotNull(rule, nameof(rule));

            var context = GetContext(rule);

            var result = new List <SimulatedRuleEvent>(MaxSimulatedEvents);

            var fromNow = SystemClock.Instance.GetCurrentInstant().Minus(Duration.FromDays(7));

            await foreach (var storedEvent in eventStore.QueryAllReverseAsync($"^([a-z]+)\\-{rule.AppId.Id}", fromNow, MaxSimulatedEvents, ct))
            {
                var @event = eventDataFormatter.ParseIfKnown(storedEvent);

                if (@event?.Payload is AppEvent appEvent)
                {
                    await foreach (var(job, exception, skip) in ruleService.CreateJobsAsync(@event, context, ct))
                    {
                        var name = job?.EventName;

                        if (string.IsNullOrWhiteSpace(name))
                        {
                            name = ruleService.GetName(appEvent);
                        }

                        var simulationResult = new SimulatedRuleEvent(
                            name,
                            job?.ActionName,
                            job?.ActionData,
                            exception?.Message,
                            skip);

                        result.Add(simulationResult);
                    }
                }
            }

            return(result);
        }
        private async Task ReadEventsAsync()
        {
            if (UseEventSourcing())
            {
                var events = await eventStore.QueryAsync(GetStreamName(), versionEvents + 1);

                foreach (var @event in events)
                {
                    versionEvents++;

                    if (@event.EventStreamNumber != versionEvents)
                    {
                        throw new InvalidOperationException("Events must follow the snapshot version in consecutive order with no gaps.");
                    }

                    var parsedEvent = eventDataFormatter.ParseIfKnown(@event);

                    if (applyEvent != null && parsedEvent != null)
                    {
                        applyEvent(parsedEvent);
                    }
                }
            }
        }
        private async Task RebuildAppIndexes()
        {
            var appsByName = new Dictionary <string, DomainId>();
            var appsByUser = new Dictionary <string, HashSet <DomainId> >();

            bool HasApp(NamedId <DomainId> appId, bool consistent, out DomainId id)
            {
                return(appsByName !.TryGetValue(appId.Name, out id) && (!consistent || id == appId.Id));
            }

            HashSet <DomainId> Index(string contributorId)
            {
                return(appsByUser !.GetOrAddNew(contributorId));
            }

            void RemoveApp(NamedId <DomainId> appId, bool consistent)
            {
                if (HasApp(appId, consistent, out var id))
                {
                    foreach (var apps in appsByUser !.Values)
                    {
                        apps.Remove(id);
                    }

                    appsByName !.Remove(appId.Name);
                }
            }

            await foreach (var storedEvent in eventStore.QueryAllAsync("^app\\-"))
            {
                var @event = eventDataFormatter.ParseIfKnown(storedEvent);

                if (@event != null)
                {
                    switch (@event.Payload)
                    {
                    case AppCreated created:
                    {
                        RemoveApp(created.AppId, false);

                        appsByName[created.Name] = created.AppId.Id;
                        break;
                    }

                    case AppContributorAssigned contributorAssigned:
                    {
                        if (HasApp(contributorAssigned.AppId, true, out _))
                        {
                            Index(contributorAssigned.ContributorId).Add(contributorAssigned.AppId.Id);
                        }

                        break;
                    }

                    case AppContributorRemoved contributorRemoved:
                        Index(contributorRemoved.ContributorId).Remove(contributorRemoved.AppId.Id);
                        break;

                    case AppArchived archived:
                        RemoveApp(archived.AppId, true);
                        break;
                    }
                }
            }

            await indexApps.RebuildAsync(appsByName);

            foreach (var(contributorId, apps) in appsByUser)
            {
                await indexApps.RebuildByContributorsAsync(contributorId, apps);
            }
        }
Beispiel #14
0
        private async Task ProcessAsync(State job, CancellationToken ct)
        {
            try
            {
                currentReminder = await RegisterOrUpdateReminder("KeepAlive", TimeSpan.Zero, TimeSpan.FromMinutes(2));

                var rules = await appProvider.GetRulesAsync(DomainId.Create(Key));

                var rule = rules.Find(x => x.Id == job.RuleId);

                if (rule == null)
                {
                    throw new InvalidOperationException("Cannot find rule.");
                }

                await eventStore.QueryAsync(async storedEvent =>
                {
                    try
                    {
                        var @event = eventDataFormatter.ParseIfKnown(storedEvent);

                        if (@event != null)
                        {
                            var jobs = await ruleService.CreateJobsAsync(rule.RuleDef, rule.Id, @event, false);

                            foreach (var(job, _) in jobs)
                            {
                                await ruleEventRepository.EnqueueAsync(job, job.Created, ct);
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        log.LogWarning(ex, w => w
                                       .WriteProperty("action", "runRule")
                                       .WriteProperty("status", "failedPartially3"));
                    }
                    finally
                    {
                        job.Position = storedEvent.EventPosition;
                    }

                    await state.WriteAsync();
                }, $"\\-{Key}", job.Position, ct);
            }
            catch (OperationCanceledException)
            {
                return;
            }
            catch (Exception ex)
            {
                log.LogError(ex, w => w
                             .WriteProperty("action", "runRule")
                             .WriteProperty("status", "failed")
                             .WriteProperty("ruleId", job.RuleId?.ToString()));
            }
            finally
            {
                if (!isStopping)
                {
                    job.RuleId   = null;
                    job.Position = null;

                    await state.WriteAsync();

                    if (currentReminder != null)
                    {
                        await UnregisterReminder(currentReminder);

                        currentReminder = null;
                    }

                    currentJobToken?.Dispose();
                    currentJobToken = null;
                }
            }
        }
        public async Task UpdateAsync()
        {
            var apps = new Dictionary <NamedId <DomainId>, Dictionary <DomainId, (string Name, string Pattern, string?Message)> >();

            await eventStore.QueryAsync(storedEvent =>
            {
                var @event = eventDataFormatter.ParseIfKnown(storedEvent);

                if (@event != null)
                {
                    switch (@event.Payload)
                    {
                    case AppPatternAdded patternAdded:
                        {
                            var patterns = apps.GetOrAddNew(patternAdded.AppId);

                            patterns[patternAdded.PatternId] = (patternAdded.Name, patternAdded.Pattern, patternAdded.Message);
                            break;
                        }

                    case AppPatternUpdated patternUpdated:
                        {
                            var patterns = apps.GetOrAddNew(patternUpdated.AppId);

                            patterns[patternUpdated.PatternId] = (patternUpdated.Name, patternUpdated.Pattern, patternUpdated.Message);
                            break;
                        }

                    case AppPatternDeleted patternDeleted:
                        {
                            var patterns = apps.GetOrAddNew(patternDeleted.AppId);

                            patterns.Remove(patternDeleted.PatternId);
                            break;
                        }

                    case AppArchived appArchived:
                        {
                            apps.Remove(appArchived.AppId);
                            break;
                        }
                    }
                }

                return(Task.CompletedTask);
            }, "^app\\-");

            var actor = RefToken.Client("Migrator");

            foreach (var(appId, patterns) in apps)
            {
                if (patterns.Count > 0)
                {
                    var settings = new AppSettings
                    {
                        Patterns = patterns.Values.Select(x => new Pattern(x.Name, x.Pattern)
                        {
                            Message = x.Message
                        }).ToReadOnlyCollection()
                    };

                    await commandBus.PublishAsync(new UpdateAppSettings
                    {
                        AppId    = appId,
                        Settings = settings,
                        FromRule = true,
                        Actor    = actor
                    });
                }
            }
        }