public virtual RuleJob CreateJob(Rule rule, Envelope <IEvent> @event) { Guard.NotNull(rule, nameof(rule)); Guard.NotNull(@event, nameof(@event)); if (!(@event.Payload is AppEvent appEvent)) { return(null); } var actionType = rule.Action.GetType(); if (!ruleTriggerHandlers.TryGetValue(rule.Trigger.GetType(), out var triggerHandler)) { return(null); } if (!ruleActionHandlers.TryGetValue(actionType, out var actionHandler)) { return(null); } var appEventEnvelope = @event.To <AppEvent>(); if (!triggerHandler.Triggers(appEventEnvelope, rule.Trigger)) { return(null); } var eventName = CreateEventName(appEvent); var now = clock.GetCurrentInstant(); var actionName = typeNameRegistry.GetName(actionType); var actionData = actionHandler.CreateJob(appEventEnvelope, eventName, rule.Action); var eventTime = @event.Headers.Contains(CommonHeaders.Timestamp) ? @event.Headers.Timestamp() : now; var job = new RuleJob { JobId = Guid.NewGuid(), ActionName = actionName, ActionData = actionData.Data, AppId = appEvent.AppId.Id, Created = now, EventName = eventName, Expires = eventTime.Plus(Constants.ExpirationTime), Description = actionData.Description }; if (job.Expires < now) { return(null); } return(job); }
public async Task EnqueueAsync(RuleJob job, Instant?nextAttempt, CancellationToken ct = default) { var entity = MongoRuleEventEntity.FromJob(job, nextAttempt); await Collection.InsertOneIfNotExistsAsync(entity, ct); }
public async Task Should_update_repositories_with_jobs_from_service() { var @event = Envelope.Create <IEvent>(new ContentCreated { AppId = appId }); var rule1 = CreateRule(); var rule2 = CreateRule(); var job1 = new RuleJob { Created = now }; A.CallTo(() => appProvider.GetRulesAsync(appId.Id)) .Returns(new List <IRuleEntity> { rule1, rule2 }); A.CallTo(() => ruleService.CreateJobsAsync(rule1.RuleDef, rule1.Id, @event, true)) .Returns(new List <(RuleJob, Exception?)> { (job1, null) }); A.CallTo(() => ruleService.CreateJobsAsync(rule2.RuleDef, rule2.Id, @event, true)) .Returns(new List <(RuleJob, Exception?)>()); await sut.On(@event); A.CallTo(() => ruleEventRepository.EnqueueAsync(job1, now, default)) .MustHaveHappened(); }
public async Task Should_update_repository_when_enqueing() { var @event = Envelope.Create <IEvent>(new ContentCreated { AppId = appId }); var rule = CreateRule(); var job = new RuleJob { Created = now }; A.CallTo(() => ruleService.CreateJobsAsync(rule.RuleDef, rule.Id, @event, true)) .Returns(new List <(RuleJob, Exception?)> { (job, null) }); await sut.Enqueue(rule.RuleDef, rule.Id, @event); A.CallTo(() => ruleEventRepository.EnqueueAsync(job, now, default)) .MustHaveHappened(); A.CallTo(() => localCache.StartContext()) .MustHaveHappened(); }
public Task EnqueueAsync(RuleJob job, Instant nextAttempt) { var entity = SimpleMapper.Map(job, new MongoRuleEventEntity { Job = job, Created = nextAttempt, NextAttempt = nextAttempt }); return(Collection.InsertOneIfNotExistsAsync(entity)); }
public async Task EnqueueAsync(RuleJob job, Instant?nextAttempt, CancellationToken ct = default) { var entity = SimpleMapper.Map(job, new MongoRuleEventEntity { Job = job, Created = job.Created, NextAttempt = nextAttempt }); await Collection.InsertOneIfNotExistsAsync(entity, ct); }
public Task UpdateAsync(RuleJob job, RuleJobUpdate update) { Guard.NotNull(job, nameof(job)); Guard.NotNull(update, nameof(update)); return(Task.WhenAll( UpdateStatisticsAsync(job, update), UpdateEventAsync(job, update))); }
public Task UpdateAsync(RuleJob job, RuleJobUpdate update, CancellationToken ct = default) { Guard.NotNull(job); Guard.NotNull(update); return(Task.WhenAll( UpdateStatisticsAsync(job, update, ct), UpdateEventAsync(job, update, ct))); }
private Task UpdateEventAsync(RuleJob job, RuleJobUpdate update) { return(Collection.UpdateOneAsync(x => x.DocumentId == job.Id, Update .Set(x => x.Result, update.ExecutionResult) .Set(x => x.LastDump, update.ExecutionDump) .Set(x => x.JobResult, update.JobResult) .Set(x => x.NextAttempt, update.JobNext) .Inc(x => x.NumCalls, 1))); }
private async Task UpdateStatisticsAsync(RuleJob job, RuleJobUpdate update) { if (update.ExecutionResult == RuleResult.Success) { await statisticsCollection.IncrementSuccess(job.AppId, job.RuleId, update.Finished); } else { await statisticsCollection.IncrementFailed(job.AppId, job.RuleId, update.Finished); } }
public async Task Should_update_repositories_on_with_jobs_from_sender() { var @event = Envelope.Create(new ContentCreated { AppId = appId }); var rule1 = new Rule(new ContentChangedTrigger(), new TestAction { Url = new Uri("https://squidex.io") }); var rule2 = new Rule(new ContentChangedTrigger(), new TestAction { Url = new Uri("https://squidex.io") }); var rule3 = new Rule(new ContentChangedTrigger(), new TestAction { Url = new Uri("https://squidex.io") }); var job1 = new RuleJob { Created = now }; var job2 = new RuleJob { Created = now }; var ruleEntity1 = A.Fake <IRuleEntity>(); var ruleEntity2 = A.Fake <IRuleEntity>(); var ruleEntity3 = A.Fake <IRuleEntity>(); A.CallTo(() => ruleEntity1.RuleDef).Returns(rule1); A.CallTo(() => ruleEntity2.RuleDef).Returns(rule2); A.CallTo(() => ruleEntity3.RuleDef).Returns(rule3); A.CallTo(() => appProvider.GetRulesAsync(appId.Id)) .Returns(new List <IRuleEntity> { ruleEntity1, ruleEntity2, ruleEntity3 }); A.CallTo(() => ruleService.CreateJobAsync(rule1, @event)) .Returns(job1); A.CallTo(() => ruleService.CreateJobAsync(rule2, @event)) .Returns(job2); A.CallTo(() => ruleService.CreateJobAsync(rule3, @event)) .Returns(Task.FromResult <RuleJob>(null)); await sut.On(@event); A.CallTo(() => ruleEventRepository.EnqueueAsync(job1, now)) .MustHaveHappened(); A.CallTo(() => ruleEventRepository.EnqueueAsync(job2, now)) .MustHaveHappened(); }
private Task UpdateEventAsync(RuleJob job, RuleJobUpdate update, CancellationToken ct = default) { return(Collection.UpdateOneAsync(x => x.JobId == job.Id, Update .Set(x => x.Result, update.ExecutionResult) .Set(x => x.LastDump, update.ExecutionDump) .Set(x => x.JobResult, update.JobResult) .Set(x => x.NextAttempt, update.JobNext) .Inc(x => x.NumCalls, 1), cancellationToken: ct)); }
private async Task UpdateStatisticsAsync(RuleJob job, RuleJobUpdate update, CancellationToken ct = default) { if (update.ExecutionResult == RuleResult.Success) { await statisticsCollection.IncrementSuccessAsync(job.AppId, job.RuleId, update.Finished, ct); } else { await statisticsCollection.IncrementFailedAsync(job.AppId, job.RuleId, update.Finished, ct); } }
public async Task EnqueueAsync(RuleJob job, Instant?nextAttempt) { var entity = new MongoRuleEventEntity { Job = job, Created = job.Created, NextAttempt = nextAttempt }; SimpleMapper.Map(job, entity); entity.DocumentId = job.Id; await Collection.InsertOneIfNotExistsAsync(entity); }
public async Task Should_update_repositories_with_jobs_from_service() { var @event = Envelope.Create <IEvent>(new ContentCreated { AppId = appId }); var job1 = new RuleJob { Created = now }; SetupRules(@event, job1); await sut.On(@event); A.CallTo(() => ruleEventRepository.EnqueueAsync(job1, (Exception?)null)) .MustHaveHappened(); }
public async Task Should_not_eqneue_when_event_restored() { var @event = Envelope.Create <IEvent>(new ContentCreated { AppId = appId }); var job1 = new RuleJob { Created = now }; SetupRules(@event, job1); await sut.On(@event.SetRestored(true)); A.CallTo(() => ruleEventRepository.EnqueueAsync(A <RuleJob> ._, A <Exception?> ._)) .MustNotHaveHappened(); }
private void SetupRules(Envelope <IEvent> @event, RuleJob job1) { var rule1 = CreateRule(); var rule2 = CreateRule(); A.CallTo(() => appProvider.GetRulesAsync(appId.Id)) .Returns(new List <IRuleEntity> { rule1, rule2 }); A.CallTo(() => ruleService.CreateJobsAsync(rule1.RuleDef, rule1.Id, @event, true)) .Returns(new List <(RuleJob, Exception?)> { (job1, null) }); A.CallTo(() => ruleService.CreateJobsAsync(rule2.RuleDef, rule2.Id, @event, true)) .Returns(new List <(RuleJob, Exception?)>()); }
public async Task MarkSentAsync(RuleJob job, string dump, RuleResult result, RuleJobResult jobResult, TimeSpan elapsed, Instant finished, Instant?nextCall) { if (result == RuleResult.Success) { await statisticsCollection.IncrementSuccess(job.AppId, job.RuleId, finished); } else { await statisticsCollection.IncrementFailed(job.AppId, job.RuleId, finished); } await Collection.UpdateOneAsync(x => x.Id == job.Id, Update .Set(x => x.Result, result) .Set(x => x.LastDump, dump) .Set(x => x.JobResult, jobResult) .Set(x => x.NextAttempt, nextCall) .Inc(x => x.NumCalls, 1)); }
private IRuleEventEntity CreateEvent(int numCalls, string actionName, RuleJobData actionData) { var @event = A.Fake <IRuleEventEntity>(); var job = new RuleJob { RuleId = Guid.NewGuid(), ActionData = actionData, ActionName = actionName, Created = now }; A.CallTo(() => @event.Id).Returns(Guid.NewGuid()); A.CallTo(() => @event.Job).Returns(job); A.CallTo(() => @event.Created).Returns(now); A.CallTo(() => @event.NumCalls).Returns(numCalls); return(@event); }
private IRuleEventEntity CreateEvent(int numCalls, string actionName, string actionData, Guid id) { var @event = A.Fake <IRuleEventEntity>(); var job = new RuleJob { Id = id, ActionData = actionData, ActionName = actionName, Created = clock.GetCurrentInstant() }; A.CallTo(() => @event.Id).Returns(id); A.CallTo(() => @event.Job).Returns(job); A.CallTo(() => @event.Created).Returns(clock.GetCurrentInstant()); A.CallTo(() => @event.NumCalls).Returns(numCalls); return(@event); }
public async Task Should_update_repository_when_enqueing() { var @event = Envelope.Create <IEvent>(new ContentCreated { AppId = appId }); var rule = CreateRule(); var job = new RuleJob { Created = now }; A.CallTo(() => ruleService.CreateJobAsync(rule.RuleDef, rule.Id, @event)) .Returns(job); await sut.Enqueue(rule.RuleDef, rule.Id, @event); A.CallTo(() => ruleEventRepository.EnqueueAsync(job, now)) .MustHaveHappened(); }
private async Task <JobResult> CreateJobAsync(IRuleActionHandler actionHandler, EnrichedEvent enrichedEvent, RuleContext context, Instant now) { var actionName = typeNameRegistry.GetName(context.Rule.Action.GetType()); var expires = now.Plus(Constants.ExpirationTime); var job = new RuleJob { Id = DomainId.NewGuid(), ActionData = string.Empty, ActionName = actionName, AppId = enrichedEvent.AppId.Id, Created = now, EventName = enrichedEvent.Name, ExecutionPartition = enrichedEvent.Partition, Expires = expires, RuleId = context.RuleId }; try { var(description, data) = await actionHandler.CreateJobAsync(enrichedEvent, context.Rule.Action); var json = jsonSerializer.Serialize(data); job.ActionData = json; job.ActionName = actionName; job.Description = description; return(new JobResult { Job = job, EnrichedEvent = enrichedEvent }); } catch (Exception ex) { job.Description = "Failed to create job"; return(JobResult.Failed(ex, enrichedEvent, job)); } }
public async Task UpdateAsync(RuleJob job, RuleJobUpdate update) { Guard.NotNull(job); Guard.NotNull(update); if (update.ExecutionResult == RuleResult.Success) { await statisticsCollection.IncrementSuccess(job.AppId, job.RuleId, update.Finished); } else { await statisticsCollection.IncrementFailed(job.AppId, job.RuleId, update.Finished); } await Collection.UpdateOneAsync(x => x.Id == job.Id, Update .Set(x => x.Result, update.ExecutionResult) .Set(x => x.LastDump, update.ExecutionDump) .Set(x => x.JobResult, update.JobResult) .Set(x => x.NextAttempt, update.JobNext) .Inc(x => x.NumCalls, 1)); }
private async Task <(RuleJob, Exception?)> CreateJobAsync(Rule rule, DomainId ruleId, IRuleActionHandler actionHandler, Instant now, EnrichedEvent enrichedEvent) { var actionName = typeNameRegistry.GetName(rule.Action.GetType()); var expires = now.Plus(Constants.ExpirationTime); var job = new RuleJob { Id = DomainId.NewGuid(), ActionData = string.Empty, ActionName = actionName, AppId = enrichedEvent.AppId.Id, Created = now, EventName = enrichedEvent.Name, ExecutionPartition = enrichedEvent.Partition, Expires = expires, RuleId = ruleId }; try { var(description, data) = await actionHandler.CreateJobAsync(enrichedEvent, rule.Action); var json = jsonSerializer.Serialize(data); job.ActionData = json; job.ActionName = actionName; job.Description = description; return(job, null); } catch (Exception ex) { job.Description = "Failed to create job"; return(job, ex); } }
public async Task EnqueueAsync(RuleJob job, Instant nextAttempt) { var entity = SimpleMapper.Map(job, new MongoRuleEventEntity { Job = job, Created = nextAttempt, NextAttempt = nextAttempt }); if (job.EventId != default) { entity.Id = job.EventId; } else { entity.Id = Guid.NewGuid(); } try { await Collection.InsertOneIfNotExistsAsync(entity); } catch (MongoWriteException ex) when(ex.WriteError.Category == ServerErrorCategory.DuplicateKey) { throw new UniqueConstraintException(); } }
public virtual async Task <RuleJob> CreateJobAsync(Rule rule, Envelope <IEvent> @event) { Guard.NotNull(rule, nameof(rule)); Guard.NotNull(@event, nameof(@event)); if (!rule.IsEnabled) { return(null); } if (!(@event.Payload is AppEvent appEvent)) { return(null); } var actionType = rule.Action.GetType(); if (!ruleTriggerHandlers.TryGetValue(rule.Trigger.GetType(), out var triggerHandler)) { return(null); } if (!ruleActionHandlers.TryGetValue(actionType, out var actionHandler)) { return(null); } var appEventEnvelope = @event.To <AppEvent>(); if (!triggerHandler.Triggers(appEventEnvelope, rule.Trigger)) { return(null); } var now = clock.GetCurrentInstant(); var eventTime = @event.Headers.ContainsKey(CommonHeaders.Timestamp) ? @event.Headers.Timestamp() : now; var expires = eventTime.Plus(Constants.ExpirationTime); if (expires < now) { return(null); } var enrichedEvent = await eventEnricher.EnrichAsync(appEventEnvelope); var actionName = typeNameRegistry.GetName(actionType); var actionData = await actionHandler.CreateJobAsync(enrichedEvent, rule.Action); var json = jsonSerializer.Serialize(actionData.Data); var job = new RuleJob { JobId = Guid.NewGuid(), ActionName = actionName, ActionData = json, AggregateId = enrichedEvent.AggregateId, AppId = appEvent.AppId.Id, Created = now, EventName = enrichedEvent.Name, Expires = expires, Description = actionData.Description }; return(job); }
private static Instant?ComputeJobInvoke(RuleResult result, IRuleEventEntity @event, RuleJob job) { if (result != RuleResult.Success) { switch (@event.NumCalls) { case 0: return(job.Created.Plus(Duration.FromMinutes(5))); case 1: return(job.Created.Plus(Duration.FromHours(1))); case 2: return(job.Created.Plus(Duration.FromHours(6))); case 3: return(job.Created.Plus(Duration.FromHours(12))); } } return(null); }
public virtual async Task <RuleJob> CreateJobAsync(Rule rule, Guid ruleId, Envelope <IEvent> @event) { Guard.NotNull(rule, nameof(rule)); Guard.NotNull(@event, nameof(@event)); try { if (!rule.IsEnabled) { return(null); } if (!(@event.Payload is AppEvent)) { return(null); } var typed = @event.To <AppEvent>(); var actionType = rule.Action.GetType(); if (!ruleTriggerHandlers.TryGetValue(rule.Trigger.GetType(), out var triggerHandler)) { return(null); } if (!ruleActionHandlers.TryGetValue(actionType, out var actionHandler)) { return(null); } var now = clock.GetCurrentInstant(); var eventTime = @event.Headers.ContainsKey(CommonHeaders.Timestamp) ? @event.Headers.Timestamp() : now; var expires = eventTime.Plus(Constants.ExpirationTime); if (expires < now) { return(null); } if (!triggerHandler.Trigger(typed.Payload, rule.Trigger, ruleId)) { return(null); } var appEventEnvelope = @event.To <AppEvent>(); var enrichedEvent = await triggerHandler.CreateEnrichedEventAsync(appEventEnvelope); if (enrichedEvent == null) { return(null); } await eventEnricher.EnrichAsync(enrichedEvent, typed); if (!triggerHandler.Trigger(enrichedEvent, rule.Trigger)) { return(null); } var actionName = typeNameRegistry.GetName(actionType); var actionData = await actionHandler.CreateJobAsync(enrichedEvent, rule.Action); var json = jsonSerializer.Serialize(actionData.Data); var job = new RuleJob { JobId = Guid.NewGuid(), ActionName = actionName, ActionData = json, AppId = enrichedEvent.AppId.Id, Created = now, EventName = enrichedEvent.Name, ExecutionPartition = enrichedEvent.Partition, Expires = expires, Description = actionData.Description }; return(job); } catch (Exception ex) { log.LogError(ex, w => w .WriteProperty("action", "createRuleJob") .WriteProperty("status", "Failed")); return(null); } }
public virtual async Task <JobList> CreateJobsAsync(Rule rule, DomainId ruleId, Envelope <IEvent> @event, bool ignoreStale = true) { Guard.NotNull(rule, nameof(rule)); Guard.NotNull(@event, nameof(@event)); var result = new JobList(); try { if (!rule.IsEnabled) { return(result); } if (!(@event.Payload is AppEvent)) { return(result); } var typed = @event.To <AppEvent>(); if (typed.Payload.FromRule) { return(result); } var actionType = rule.Action.GetType(); if (!ruleTriggerHandlers.TryGetValue(rule.Trigger.GetType(), out var triggerHandler)) { return(result); } if (!ruleActionHandlers.TryGetValue(actionType, out var actionHandler)) { return(result); } var now = clock.GetCurrentInstant(); var eventTime = @event.Headers.ContainsKey(CommonHeaders.Timestamp) ? @event.Headers.Timestamp() : now; if (ignoreStale && eventTime.Plus(Constants.StaleTime) < now) { return(result); } var expires = now.Plus(Constants.ExpirationTime); if (!triggerHandler.Trigger(typed.Payload, rule.Trigger, ruleId)) { return(result); } var appEventEnvelope = @event.To <AppEvent>(); var enrichedEvents = await triggerHandler.CreateEnrichedEventsAsync(appEventEnvelope); foreach (var enrichedEvent in enrichedEvents) { try { await eventEnricher.EnrichAsync(enrichedEvent, typed); if (!triggerHandler.Trigger(enrichedEvent, rule.Trigger)) { continue; } var actionName = typeNameRegistry.GetName(actionType); var job = new RuleJob { Id = DomainId.NewGuid(), ActionData = string.Empty, ActionName = actionName, AppId = enrichedEvent.AppId.Id, Created = now, EventName = enrichedEvent.Name, ExecutionPartition = enrichedEvent.Partition, Expires = expires, RuleId = ruleId }; try { var(description, data) = await actionHandler.CreateJobAsync(enrichedEvent, rule.Action); var json = jsonSerializer.Serialize(data); job.ActionData = json; job.ActionName = actionName; job.Description = description; result.Add((job, null)); } catch (Exception ex) { job.Description = "Failed to create job"; result.Add((job, ex)); } } catch (Exception ex) { log.LogError(ex, w => w .WriteProperty("action", "createRuleJobFromEvent") .WriteProperty("status", "Failed")); } } } catch (Exception ex) { log.LogError(ex, w => w .WriteProperty("action", "createRuleJob") .WriteProperty("status", "Failed")); } return(result); }