private Result MergeSpanEquipment(CommandContext cmdContext, Guid routeNodeId, SpanEquipmentWithConnectsHolder firstSpanEquipment, SpanEquipmentWithConnectsHolder secondSpanEquipment)
        {
            // Merge the first span equipment with the second one
            var firstSpanEquipmentAR = _eventStore.Aggregates.Load <SpanEquipmentAR>(firstSpanEquipment.SpanEquipment.Id);

            var firstSpanEquipmentConnectResult = firstSpanEquipmentAR.Merge(
                cmdContext: cmdContext,
                routeNodeId: routeNodeId,
                secondSpanEquipment.SpanEquipment
                );

            if (firstSpanEquipmentConnectResult.IsFailed)
            {
                return(firstSpanEquipmentConnectResult);
            }

            var firstSpanEquipmentWalk  = GetInterestInformation(firstSpanEquipment.SpanEquipment);
            var secondSpanEquipmentWalk = GetInterestInformation(secondSpanEquipment.SpanEquipment);

            // Update interest of the first span equipment to cover both span equipments
            var newSegmentIds = MergeWalks(firstSpanEquipmentWalk, secondSpanEquipmentWalk);

            var updateWalkOfInterestCommand       = new UpdateWalkOfInterest(firstSpanEquipment.SpanEquipment.WalkOfInterestId, newSegmentIds);
            var updateWalkOfInterestCommandResult = _commandDispatcher.HandleAsync <UpdateWalkOfInterest, Result <RouteNetworkInterest> >(updateWalkOfInterestCommand).Result;

            if (updateWalkOfInterestCommandResult.IsFailed)
            {
                return(updateWalkOfInterestCommandResult);
            }

            // Remove the second span equipment
            var secondSpanEquipmentAR = _eventStore.Aggregates.Load <SpanEquipmentAR>(secondSpanEquipment.SpanEquipment.Id);
            var removeSpanEquipment   = secondSpanEquipmentAR.Remove(cmdContext);

            if (removeSpanEquipment.IsSuccess)
            {
                // Remember to remove the walk of interest as well
                var unregisterInterestCmd       = new UnregisterInterest(secondSpanEquipment.SpanEquipment.WalkOfInterestId);
                var unregisterInterestCmdResult = _commandDispatcher.HandleAsync <UnregisterInterest, Result>(unregisterInterestCmd).Result;

                if (unregisterInterestCmdResult.IsFailed)
                {
                    throw new ApplicationException($"Failed to unregister interest: {secondSpanEquipment.SpanEquipment.WalkOfInterestId} of span equipment: {secondSpanEquipment.SpanEquipment.Id} in RemoveSpanStructureFromSpanEquipmentCommandHandler Error: {unregisterInterestCmdResult.Errors.First().Message}");
                }
            }

            _eventStore.Aggregates.Store(firstSpanEquipmentAR);
            _eventStore.Aggregates.Store(secondSpanEquipmentAR);

            NotifyExternalServicesAboutMerge(firstSpanEquipment.SpanEquipment.Id, updateWalkOfInterestCommandResult.Value.RouteNetworkElementRefs.ToArray());

            return(Result.Ok());
        }
        public Task <Result <RouteNetworkInterest> > HandleAsync(UpdateWalkOfInterest command)
        {
            var interestAR = _eventStore.Aggregates.Load <InterestAR>(command.InterestId);

            var interestProjection = _eventStore.Projections.Get <InterestsProjection>();

            var walkOfInterest = new RouteNetworkInterest(command.InterestId, RouteNetworkInterestKindEnum.WalkOfInterest, command.WalkIds);

            var walkValidator = new WalkValidator(_routeNetworkRepository);

            var commandContext = new CommandContext(command.CorrelationId, command.CmdId, command.UserContext);

            var updateInterestResult = interestAR.UpdateRouteNetworkElements(commandContext, walkOfInterest, interestProjection, walkValidator);

            if (updateInterestResult.IsSuccess)
            {
                _eventStore.Aggregates.Store(interestAR);
            }

            return(Task.FromResult(updateInterestResult));
        }
        public Task <Result> HandleAsync(MoveSpanEquipment command)
        {
            var utilityNetwork = _eventStore.Projections.Get <UtilityNetworkProjection>();

            // Because the client is allowed to provide either a span equipment or segment id, we need look it up via the utility network graph
            if (!utilityNetwork.TryGetEquipment <SpanEquipment>(command.SpanEquipmentOrSegmentId, out SpanEquipment spanEquipment))
            {
                return(Task.FromResult(Result.Fail(new MoveSpanEquipmentError(MoveSpanEquipmentErrorCodes.SPAN_EQUIPMENT_NOT_FOUND, $"Cannot find any span equipment or segment in the utility graph with id: {command.SpanEquipmentOrSegmentId}"))));
            }

            // Get interest information from existing span equipment
            var existingWalk = GetInterestInformation(spanEquipment);

            // Validate the new walk
            var newWalkValidationResult = _commandDispatcher.HandleAsync <ValidateWalkOfInterest, Result <ValidatedRouteNetworkWalk> >(new ValidateWalkOfInterest(command.NewWalkIds)).Result;

            // If the new walk fails to validate, return the error to the client
            if (newWalkValidationResult.IsFailed)
            {
                return(Task.FromResult(Result.Fail(newWalkValidationResult.Errors.First())));
            }

            var newWalk = newWalkValidationResult.Value;

            // If the walk has not changed return error as well
            if (existingWalk.Equals(newWalk))
            {
                return(Task.FromResult(Result.Fail(new MoveSpanEquipmentError(MoveSpanEquipmentErrorCodes.NEW_WALK_EQUALS_EXISTING_WALK, $"The new walk specified is not different from the existing walk of the span equipment."))));
            }

            // Reverse new walk if one of its ends are opposite of existing walk ends
            if (newWalk.FromNodeId == existingWalk.ToNodeId || newWalk.ToNodeId == existingWalk.FromNodeId)
            {
                newWalk = newWalk.Reverse();
            }

            // Try to do the move of the span equipment
            var spanEquipmentAR = _eventStore.Aggregates.Load <SpanEquipmentAR>(spanEquipment.Id);

            var commandContext = new CommandContext(command.CorrelationId, command.CmdId, command.UserContext);

            var moveSpanEquipmentResult = spanEquipmentAR.Move(commandContext, newWalk, existingWalk);

            if (moveSpanEquipmentResult.IsFailed)
            {
                return(Task.FromResult(Result.Fail(moveSpanEquipmentResult.Errors.First())));
            }

            // If we got to here, then the span equipment move was validated fine, so we can update the walk of interest
            var newSegmentIds = new RouteNetworkElementIdList();

            newSegmentIds.AddRange(newWalk.SegmentIds);

            var updateWalkOfInterestCommand = new UpdateWalkOfInterest(spanEquipment.WalkOfInterestId, newSegmentIds)
            {
                UserContext = commandContext.UserContext
            };

            var updateWalkOfInterestCommandResult = _commandDispatcher.HandleAsync <UpdateWalkOfInterest, Result <RouteNetworkInterest> >(updateWalkOfInterestCommand).Result;

            if (updateWalkOfInterestCommandResult.IsFailed)
            {
                throw new ApplicationException($"Got unexpected error result: {updateWalkOfInterestCommandResult.Errors.First().Message} trying to update walk of interest belonging to span equipment with id: {spanEquipment.Id} while processing the MoveSpanEquipment command: " + JsonConvert.SerializeObject(command));
            }

            _eventStore.Aggregates.Store(spanEquipmentAR);

            NotifyExternalServicesAboutSpanEquipmentChange(spanEquipment.Id, existingWalk, newWalk);

            return(Task.FromResult(moveSpanEquipmentResult));
        }
        private void HandleSplitCommand(RouteNetworkEditOperationOccuredEvent request, Events.RouteNetworkCommand command, ITransaction trans)
        {
            if (!_networkState.IsLoadMode)
            {
                Guid correlationId = Guid.NewGuid();

                _logger.LogInformation($"Route network event handler is processing split, event command id: {command.CmdId}, correlation id: {correlationId}, invoked by user: {request.UserName}");

                RouteSegmentAdded?  firstAddedSegmentEvent  = null;
                RouteSegmentAdded?  secondAddedSegmentEvent = null;
                RouteSegmentRemoved?removedSegmentEvent     = null;

                RouteSegmentAdded?fromAddedSegmentEvent;
                RouteSegmentAdded?toAddedSegmentEvent;

                Guid?splitNodeId;

                foreach (var routeNetworkEvent in command.RouteNetworkEvents)
                {
                    if (routeNetworkEvent is RouteSegmentAdded && firstAddedSegmentEvent == null)
                    {
                        firstAddedSegmentEvent = routeNetworkEvent as RouteSegmentAdded;
                    }
                    else if (routeNetworkEvent is RouteSegmentAdded && firstAddedSegmentEvent != null && secondAddedSegmentEvent == null)
                    {
                        secondAddedSegmentEvent = routeNetworkEvent as RouteSegmentAdded;
                    }
                    else if (routeNetworkEvent is RouteSegmentRemoved)
                    {
                        removedSegmentEvent = routeNetworkEvent as RouteSegmentRemoved;
                    }
                }


                // Only proceed if we manage to get all the needed information from the split command
                if (firstAddedSegmentEvent != null && secondAddedSegmentEvent != null && removedSegmentEvent != null)
                {
                    if (firstAddedSegmentEvent.ToNodeId == secondAddedSegmentEvent.FromNodeId)
                    {
                        fromAddedSegmentEvent = firstAddedSegmentEvent;
                        toAddedSegmentEvent   = secondAddedSegmentEvent;
                        splitNodeId           = firstAddedSegmentEvent.ToNodeId;
                    }
                    else
                    {
                        toAddedSegmentEvent   = firstAddedSegmentEvent;
                        fromAddedSegmentEvent = secondAddedSegmentEvent;
                        splitNodeId           = firstAddedSegmentEvent.FromNodeId;
                    }

                    // Find all interests of the deleted route segment
                    var interestsProjection = _eventStore.Projections.Get <InterestsProjection>();

                    var interestRelationsResult = interestsProjection.GetInterestsByRouteNetworkElementId(removedSegmentEvent.SegmentId);

                    if (interestRelationsResult.IsFailed)
                    {
                        _logger.LogError($"Split handler: Failed with error: {interestRelationsResult.Errors.First().Message} trying to get interest related by removed segment with id: {removedSegmentEvent.SegmentId} processing split command: " + JsonConvert.SerializeObject(command));
                        return;
                    }

                    foreach (var interest in interestRelationsResult.Value)
                    {
                        var firstAddedSegmentInterestRelationsResult = interestsProjection.GetInterestsByRouteNetworkElementId(firstAddedSegmentEvent.SegmentId);

                        if (firstAddedSegmentInterestRelationsResult.IsFailed)
                        {
                            _logger.LogError($"Split handler: Failed with error: {interestRelationsResult.Errors.First().Message} trying to get interest related by added segment with id: {firstAddedSegmentEvent.SegmentId} processing split command: " + JsonConvert.SerializeObject(command));
                            return;
                        }

                        var newRouteNetworkElementIdList = CreateNewRouteNetworkElementIdListFromSplit(interest.Item1.RouteNetworkElementRefs, removedSegmentEvent.SegmentId, splitNodeId.Value, fromAddedSegmentEvent, toAddedSegmentEvent);

                        var updateWalkOfInterestCommand = new UpdateWalkOfInterest(Guid.NewGuid(), new UserContext(request.UserName, request.WorkTaskMrid ?? Guid.Empty), interest.Item1.Id, newRouteNetworkElementIdList);

                        _logger.LogInformation($"Route network split handler is invoking {updateWalkOfInterestCommand.GetType().Name} command with id: {updateWalkOfInterestCommand.CmdId}, correlation id: {correlationId}");

                        var updateWalkOfInterestCommandResult = _commandDispatcher.HandleAsync <UpdateWalkOfInterest, Result <RouteNetworkInterest> >(updateWalkOfInterestCommand).Result;

                        if (updateWalkOfInterestCommandResult.IsFailed)
                        {
                            _logger.LogError($"Split handler: Failed error: {updateWalkOfInterestCommandResult.Errors.First().Message} trying to update interest with id: {interest.Item1.Id} processing split command: " + JsonConvert.SerializeObject(command));
                        }
                    }
                }
                else
                {
                    _logger.LogError("Split handler: Can't find needed information in event. Expected two RouteSegmentAdded and one RouteSegmentRemoved event. Split command being processed: " + JsonConvert.SerializeObject(command));
                }
            }
        }
예제 #5
0
        public async void UpdateInterestWithInvalidWalk_ShouldFail()
        {
            // Route network subset used in this test:
            // (CO_1) <- (S1) -> (HH_1)
            var interestId = Guid.NewGuid();

            var walk = new RouteNetworkElementIdList()
            {
                TestRouteNetwork.S1
            };

            // Walk with hole
            var updatedWalk = new RouteNetworkElementIdList()
            {
                TestRouteNetwork.S2, TestRouteNetwork.S5
            };

            var routeNetworkQuery_CO1 = new GetRouteNetworkDetails(new RouteNetworkElementIdList()
            {
                TestRouteNetwork.CO_1
            })
            {
                RelatedInterestFilter = RelatedInterestFilterOptions.ReferencesFromRouteElementAndInterestObjects
            };

            var routeNetworkQuery_HH2 = new GetRouteNetworkDetails(new RouteNetworkElementIdList()
            {
                TestRouteNetwork.HH_2
            })
            {
                RelatedInterestFilter = RelatedInterestFilterOptions.ReferencesFromRouteElementAndInterestObjects
            };

            // Act
            var registerWalkOfInterestCommand       = new RegisterWalkOfInterest(Guid.NewGuid(), new UserContext("test", Guid.Empty), interestId, walk);
            var registerWalkOfInterestCommandResult = await _commandDispatcher.HandleAsync <RegisterWalkOfInterest, Result <RouteNetworkInterest> >(registerWalkOfInterestCommand);

            Result <GetRouteNetworkDetailsResult> routeNetworkQueryResultBefore = await _queryDispatcher.HandleAsync <GetRouteNetworkDetails, Result <GetRouteNetworkDetailsResult> >(routeNetworkQuery_CO1);

            var updateWalkOfInterestCommand       = new UpdateWalkOfInterest(Guid.NewGuid(), new UserContext("test", Guid.Empty), interestId, updatedWalk);
            var updateWalkOfInterestCommandResult = await _commandDispatcher.HandleAsync <UpdateWalkOfInterest, Result <RouteNetworkInterest> >(updateWalkOfInterestCommand);

            Result <GetRouteNetworkDetailsResult> co1_routeNetworkQueryResultAfter = await _queryDispatcher.HandleAsync <GetRouteNetworkDetails, Result <GetRouteNetworkDetailsResult> >(routeNetworkQuery_CO1);

            Result <GetRouteNetworkDetailsResult> hh2_routeNetworkQueryResultAfter = await _queryDispatcher.HandleAsync <GetRouteNetworkDetails, Result <GetRouteNetworkDetailsResult> >(routeNetworkQuery_HH2);

            // Assert command result
            registerWalkOfInterestCommandResult.IsSuccess.Should().BeTrue();
            updateWalkOfInterestCommandResult.IsFailed.Should().BeTrue();
            routeNetworkQueryResultBefore.IsSuccess.Should().BeTrue();
            co1_routeNetworkQueryResultAfter.IsSuccess.Should().BeTrue();
            hh2_routeNetworkQueryResultAfter.IsSuccess.Should().BeTrue();

            // Assert query result
            routeNetworkQueryResultBefore.Value.Interests.ContainsKey(interestId).Should().BeTrue();
            routeNetworkQueryResultBefore.Value.Interests[interestId].RouteNetworkElementRefs.Count.Should().Be(3);

            // Make sure index is *not* updated to include new interest at HH 2
            hh2_routeNetworkQueryResultAfter.Value.Interests.ContainsKey(interestId).Should().BeFalse();

            // Make sure index is still holding interest at CO 1
            co1_routeNetworkQueryResultAfter.Value.Interests.ContainsKey(interestId).Should().BeTrue();
        }