/// <inheritdoc/> public async Task <FilterValidationResult> Validate(TypeFilterWithEventSourcePartitionDefinition persistedDefinition, IFilterProcessor <TypeFilterWithEventSourcePartitionDefinition> filter, StreamPosition lastUnprocessedEvent, CancellationToken cancellationToken) { try { var changedEventTypes = GetChangedEventTypes(persistedDefinition, filter.Definition); if (EventTypesHaveNotChanged(changedEventTypes)) { return(FilterValidationResult.Succeeded()); } var streamTypesFetcher = await _eventFetchers.GetTypeFetcherFor( filter.Scope, new EventLogStreamDefinition(), cancellationToken).ConfigureAwait(false); var typesInSourceStream = await streamTypesFetcher.FetchInRange( new StreamPositionRange(StreamPosition.Start, lastUnprocessedEvent), cancellationToken).ConfigureAwait(false); return(SourceStreamContainsChangedEventTypes(typesInSourceStream, changedEventTypes) ? FilterValidationResult.Failed("The new filter definition has added or removed event types that have already been filtered") : FilterValidationResult.Succeeded()); } catch (Exception exception) { return(FilterValidationResult.Failed(exception.Message)); } }
/// <inheritdoc/> public FilterValidationResult DefinitionsAreEqual(IFilterDefinition persisted, IFilterDefinition registered) { if (persisted.Partitioned != registered.Partitioned) { return(FilterValidationResult.Failed($"The new stream generated from the filter will not match the old stream. {(persisted.Partitioned ? "The previous filter is partitioned while the new filter is not" : "The previous filter is not partitioned while the new filter is")}")); } if (persisted.Public != registered.Public) { return(FilterValidationResult.Failed($"The new stream generated from the filter will not match the old stream. {(persisted.Public ? "The previous filter is public while the new filter is not" : "The previous filter is not public while the new filter is")}")); } return(FilterValidationResult.Succeeded()); }
static bool EventsAreNotEqual(IFilterDefinition filterDefinition, StreamEvent newEvent, StreamEvent oldEvent, out FilterValidationResult failedResult) { failedResult = default; if (newEvent.Event.EventLogSequenceNumber != oldEvent.Event.EventLogSequenceNumber) { failedResult = FilterValidationResult.Failed($"Event in new stream at position {newEvent.Position} is event {newEvent.Event.EventLogSequenceNumber} while the event in the old stream is event {oldEvent.Event.EventLogSequenceNumber}"); return(true); } if (filterDefinition.Partitioned && newEvent.Partition != oldEvent.Partition) { failedResult = FilterValidationResult.Failed($"Event in new stream at position {newEvent.Position} has is in partition {newEvent.Partition} while the event in the old stream is in partition {oldEvent.Partition}"); return(true); } return(false); }
static bool FilterDefinitionHasBeenPersisted(Try <IFilterDefinition> tryGetFilterDefinition, out IFilterDefinition persistedDefinition, out FilterValidationResult validationResult) { if (!tryGetFilterDefinition.Success) { validationResult = tryGetFilterDefinition.Exception is StreamDefinitionDoesNotExist ? FilterValidationResult.Succeeded() : FilterValidationResult.Failed(tryGetFilterDefinition.Exception.Message); persistedDefinition = default; return(false); } persistedDefinition = tryGetFilterDefinition.Result; validationResult = default; return(true); }
/// <inheritdoc/> public async Task <FilterValidationResult> Validate <TDefinition>(IFilterProcessor <TDefinition> filter, CancellationToken cancellationToken) where TDefinition : IFilterDefinition { var tryGetProcessorState = await _streamProcessorStates .TryGetFor(new StreamProcessorId(filter.Scope, filter.Definition.TargetStream.Value, filter.Definition.SourceStream), cancellationToken) .ConfigureAwait(false); if (!StreamProcessorHasProcessedEvents(tryGetProcessorState, out var validationResult, out var lastUnprocessedEvent)) { return(validationResult); } _logger.TryGetFilterDefinition(filter.Identifier, _tenant); var tryGetFilterDefinition = await _filterDefinitions.TryGetFromStream(filter.Scope, filter.Definition.TargetStream, cancellationToken).ConfigureAwait(false); if (!FilterDefinitionHasBeenPersisted(tryGetFilterDefinition, out var persistedDefinition, out validationResult)) { _logger.NoPersistedFilterDefinition(filter.Identifier, _tenant); return(validationResult); } var definitionResult = _definitionComparer.DefinitionsAreEqual(persistedDefinition, filter.Definition); if (!definitionResult.Success) { return(definitionResult); } if (FilterDefinitionTypeHasChanged(persistedDefinition, filter.Definition)) { return(FilterValidationResult.Failed("Filter definition type has changed")); } _logger.FindingFilterValidator(filter.Identifier); if (!TryGetValidatorFor <TDefinition>(out var validator)) { return(FilterValidationResult.Failed($"No available filter validator for type {filter.Definition.GetType()}")); } _logger.ValidatingFilter(filter.Identifier); return(await validator.Validate((TDefinition)persistedDefinition, filter, lastUnprocessedEvent, cancellationToken).ConfigureAwait(false)); }
static bool StreamProcessorHasProcessedEvents(Try <IStreamProcessorState> tryGetState, out FilterValidationResult validationResult, out StreamPosition lastUnprocessedEvent) { if (!tryGetState.Success) { validationResult = tryGetState.Exception is StreamProcessorStateDoesNotExist ? FilterValidationResult.Succeeded() : FilterValidationResult.Failed(tryGetState.Exception.Message); lastUnprocessedEvent = default; return(false); } lastUnprocessedEvent = tryGetState.Result.Position; if (lastUnprocessedEvent == StreamPosition.Start) { validationResult = FilterValidationResult.Succeeded(); return(false); } validationResult = default; return(true); }
async Task <FilterValidationResult> PerformValidation(IFilterProcessor <IFilterDefinition> filter, StreamPosition lastUnprocessedEvent, CancellationToken cancellationToken) { try { var streamDefinition = new StreamDefinition(filter.Definition); var oldStreamEventsFetcher = await _eventFetchers.GetRangeFetcherFor( filter.Scope, streamDefinition, cancellationToken).ConfigureAwait(false); var eventLogFetcher = await _eventFetchers.GetRangeFetcherFor( filter.Scope, new EventLogStreamDefinition(), cancellationToken).ConfigureAwait(false); var oldStream = oldStreamEventsFetcher.FetchRange( new StreamPositionRange(StreamPosition.Start, ulong.MaxValue), cancellationToken); var eventLogStream = eventLogFetcher.FetchRange(new StreamPositionRange(StreamPosition.Start, lastUnprocessedEvent), cancellationToken); await using var oldStreamEnumerator = oldStream.GetAsyncEnumerator(cancellationToken); var newStreamPosition = 0; await foreach (var eventFromEventLog in eventLogStream.WithCancellation(cancellationToken)) { var filteringResult = await filter.Filter(eventFromEventLog.Event, PartitionId.None, filter.Identifier, eventFromEventLog.Event.ExecutionContext, cancellationToken).ConfigureAwait(false); if (filteringResult is FailedFiltering failedResult) { return(FilterValidationResult.Failed(failedResult.FailureReason)); } if (!filteringResult.IsIncluded) { continue; } if (!await oldStreamEnumerator.MoveNextAsync()) { return(FilterValidationResult.Failed("The number of events included in the new stream generated from the filter does not match the old stream.")); } var oldStreamEvent = oldStreamEnumerator.Current; var filteredEvent = new StreamEvent( eventFromEventLog.Event, new StreamPosition((ulong)newStreamPosition++), filter.Definition.TargetStream, filteringResult.Partition, streamDefinition.Partitioned); if (EventsAreNotEqual(filter.Definition, filteredEvent, oldStreamEvent, out var failedValidation)) { return(failedValidation); } } if (await oldStreamEnumerator.MoveNextAsync()) { return(FilterValidationResult.Failed($"The number of events included in the new stream generated from the filter does not match the old stream.")); } return(FilterValidationResult.Succeeded()); } catch (Exception exception) { return(FilterValidationResult.Failed(exception.Message)); } }
/// <summary> /// Initializes a new instance of the <see cref="FilterRegistrationResult"/> class. /// </summary> /// <param name="streamProcessor">The <see cref="StreamProcessors" />.</param> public FilterRegistrationResult(StreamProcessor streamProcessor) { StreamProcessor = streamProcessor; FilterValidationResult = FilterValidationResult.Succeeded(); Success = streamProcessor != default; }
/// <summary> /// Initializes a new instance of the <see cref="FilterRegistrationResult"/> class. /// </summary> /// <param name="filterValidationResult">The failed <see cref="FilterValidationResult" />.</param> public FilterRegistrationResult(FilterValidationResult filterValidationResult) { FilterValidationResult = filterValidationResult; }