public static ApiGeoTask ToApiGeoTask(this GeoTask from)
        {
            if (from is null)
            {
                return(null);
            }

            var to = new ApiGeoTask
            {
                Description      = from.Description,
                Id               = from.Id,
                PlanFinishAt     = from.PlanFinishAt,
                PlanStartAt      = from.PlanStartAt,
                ProjectId        = from.ProjectId,
                ResponsibleActor = from.ResponsibleActor.ToApiActor(),
                Title            = from.Title,
                CreatedAt        = from.CreatedAt,
                CreatedBy        = from.CreatedBy.ToApiActor(),
                IsArchived       = from.IsArchived,
                Status           = from.Status,
                StatusChangedAt  = from.StatusChangedAt
            };

            to.AssistentActors.AddRange(from.AssistentActors.Select(x => x.ToApiActor()));
            to.GeosIds.AddRange(from.GeosIds);
            to.History.AddRange(from.History.Select(x => x.ToApiGeoTaskHistory()));
            to.ObserverActors.AddRange(from.ObserverActors.Select(x => x.ToApiActor()));

            return(to);
        }
Example #2
0
        public static Dictionary <string, object> ToDictionary
            (this GeoTask from)
        {
            if (from is null)
            {
                return(null);
            }

            return(new Dictionary <string, object>
            {
                { nameof(from.AssistentActors),
                  String.Join(',', from.AssistentActors.Select(x => x.Id)) },
                { nameof(from.CreatedAt), from.CreatedAt },
                { nameof(from.CreatedBy),
                  String.Join(',', from.CreatedBy
                              .ToDictionary()
                              .Select(x => $"{x.Key}={x.Value}")) },
                { nameof(from.Description), from.Description },
                { nameof(from.GeosIds),
                  String.Join(',', from.GeosIds) },
                { nameof(from.History),
                  String.Join(',', from.History.Select(x => x.Id)) },
                { nameof(from.Id), from.Id },
                { nameof(from.IsArchived), from.IsArchived },
                { nameof(from.ObserverActors),
                  String.Join(',', from.ObserverActors.Select(x => x.Id)) },
                { nameof(from.PlanFinishAt), from.PlanFinishAt },
                { nameof(from.PlanStartAt), from.PlanStartAt },
                { nameof(from.ProjectId), from.ProjectId },
                { nameof(from.ResponsibleActor), from.ResponsibleActor?.Id },
                { nameof(from.Status), from.Status?.Id },
                { nameof(from.StatusChangedAt), from.StatusChangedAt },
                { nameof(from.Title), from.Title },
            });
        }
        public static DbGeoTask ToDbGeoTask(this GeoTask from)
        {
            if (from is null)
            {
                return(null);
            }

            var to = new DbGeoTask
            {
                CreatedAt          = from.CreatedAt,
                Description        = from.Description,
                CreatedById        = from.CreatedBy?.Id,
                Id                 = from.Id,
                IsArchived         = from.IsArchived,
                PlanFinishAt       = from.PlanFinishAt,
                PlanStartAt        = from.PlanStartAt,
                ProjectId          = from.ProjectId,
                Status             = from.Status,
                ResponsibleActorId = from.ResponsibleActor?.Id,
                StatusChangedAt    = from.StatusChangedAt,
                Title              = from.Title
            };

            to.AssistentActorsIds.AddRange
                (from.AssistentActors.Select(x => x.Id));
            to.GeosIds.AddRange(from.GeosIds);
            to.History.AddRange(from.History);
            to.ObserverActorsIds.AddRange
                (from.ObserverActors.Select(x => x.Id));

            return(to);
        }
        private async Task <CreateResult> CheckPermission(GeoTask task,
                                                          Actor actor, Project project)
        {
            ActorRole currentActorProjectRole = null;

            if (project != null && actor != null)
            {
                project.ProjectActorRoles?
                .TryGetValue(actor.Id, out currentActorProjectRole);
            }
            var checkModel = new CheckCreatePermissionModel <GeoTask>
            {
                Entity           = task,
                Actor            = actor,
                ProjectActorRole = currentActorProjectRole
            };
            var validator       = new GeoTaskCreatePermissionValidator();
            var validatorResult = await validator
                                  .ValidateAsync(checkModel)
                                  .ConfigureAwait(false);

            if (!validatorResult.IsValid)
            {
                return(ErrorResult(validatorResult.Errors
                                   .Select(x => x.ErrorMessage)));
            }
            return(new CreateResult {
                Success = true, Id = task.Id
            });
        }
        private async Task <GeoTask> BuildUpdatedGeoTask
            (GeoTaskUpdateCommand command, GeoTask oldGeoTask,
            Actor currentActor)
        {
            var newGeoTask = new GeoTask
            {
                CreatedAt    = oldGeoTask.CreatedAt,
                CreatedBy    = oldGeoTask.CreatedBy,
                Description  = command.Description,
                Id           = oldGeoTask.Id,
                IsArchived   = command.IsArchived,
                PlanFinishAt = command.PlanFinishAt,
                PlanStartAt  = command.PlanStartAt,
                Title        = command.Title,
                Status       = command.Status,
            };

            var allActors = await GetActorsAsync(
                command.AssistentActorsIds
                .Concat(command.ObserverActorsIds)
                .Append(command.ResponsibleActorId)
                ).ConfigureAwait(false);

            newGeoTask.AssistentActors.AddRange(
                command.AssistentActorsIds
                .Select(x => allActors.FirstOrDefault(a => a.Id == x))
                .Where(a => a != null));
            newGeoTask.ObserverActors.AddRange(
                command.ObserverActorsIds
                .Select(x => allActors.FirstOrDefault(a => a.Id == x))
                .Where(a => a != null));
            newGeoTask.ResponsibleActor = allActors
                                          .FirstOrDefault(a => a.Id == command.ResponsibleActorId);

            // Check that Geo Ids exist in repository
            newGeoTask.GeosIds.AddRange(await GetGeosIdsAsync(command.GeosIds)
                                        .ConfigureAwait(false));

            newGeoTask.History.AddRange(oldGeoTask.History);

            newGeoTask.ProjectId = NewProject?.Id;

            if (oldGeoTask.Status != command.Status)
            {
                newGeoTask.StatusChangedAt = DateTime.UtcNow;
            }
            else
            {
                newGeoTask.StatusChangedAt = oldGeoTask.StatusChangedAt;
            }

            newGeoTask.History.Add(BuildNewHistoryElement
                                       (newGeoTask, oldGeoTask, command, currentActor));
            return(newGeoTask);
        }
        private GeoTaskHistory BuildNewHistoryElement(GeoTask newGeoTask,
                                                      GeoTask oldGeoTask, GeoTaskUpdateCommand command,
                                                      Actor currentActor)
        {
            var historyRecord = new GeoTaskHistory()
            {
                ChangedAt   = DateTime.UtcNow,
                ChangedBy   = currentActor,
                Description = command.MessageDescription,
                Title       = command.MessageTitle
            };

            historyRecord.Operations
            .AddRange(newGeoTask.ToHistoryOperations(oldGeoTask));
            return(historyRecord);
        }
Example #7
0
        public static EntityResponse <GeoTask> ToGeoTaskResponse
            (this GeoTask from)
        {
            if (from is null)
            {
                return(null);
            }

            var response = new EntityResponse <GeoTask>
            {
                Entity  = from,
                Success = true
            };

            return(response);
        }
Example #8
0
        public static GeoTask ToGeoTask(this DbGeoTask from,
                                        Dictionary <string, Actor> actors)
        {
            actors.TryGetValue(from.CreatedById, out var creator);
            actors.TryGetValue(from.ResponsibleActorId, out var responsible);
            var assistents = from.AssistentActorsIds
                             .Select(x =>
            {
                actors.TryGetValue(x, out var actor);
                return(actor);
            })
                             .Where(x => x != null)
                             .ToList();
            var observers = from.ObserverActorsIds
                            .Select(x =>
            {
                actors.TryGetValue(x, out var actor);
                return(actor);
            })
                            .Where(x => x != null)
                            .ToList();

            var to = new GeoTask
            {
                CreatedAt        = from.CreatedAt,
                Description      = from.Description,
                Id               = from.Id,
                IsArchived       = from.IsArchived,
                PlanFinishAt     = from.PlanFinishAt,
                PlanStartAt      = from.PlanStartAt,
                ProjectId        = from.ProjectId,
                Status           = from.Status,
                StatusChangedAt  = from.StatusChangedAt,
                Title            = from.Title,
                CreatedBy        = creator,
                ResponsibleActor = responsible,
            };

            to.AssistentActors.AddRange(assistents);
            to.ObserverActors.AddRange(observers);
            to.History.AddRange(from.History);
            to.GeosIds.AddRange(from.GeosIds);
            return(to);
        }
        private async Task <ValidationResult> CheckPermissionAsync
            (GeoTask oldTask, GeoTask newTask, Actor currentActor,
            ActorRole oldProjectRole, ActorRole newProjectRole)
        {
            var checkModel = new CheckUpdatePermissionModel <GeoTask>
            {
                Actor              = currentActor,
                OldProjectRole     = oldProjectRole,
                NewProjectRole     = newProjectRole,
                EntityBeforeUpdate = oldTask,
                EntityAfterUpdate  = newTask
            };
            var validator       = new GeoTaskUpdatePermissionValidator();
            var validatorResult = await validator
                                  .ValidateAsync(checkModel)
                                  .ConfigureAwait(false);

            return(validatorResult);
        }
Example #10
0
        public async Task GetIdReturnsOkResultWhenSuccessAnswerAsync()
        {
            // Arrange
            var entityId  = "0000000000000000";
            var appEntity = new GeoTask()
            {
                Id         = entityId,
                IsArchived = false,
                Title      = "Test GeoTask"
            };
            var apiEntity = new ApiGeoTask()
            {
                Id         = entityId,
                IsArchived = false,
                Title      = "Test GeoTask"
            };
            var mediator = new Mock <IMediator>();

            mediator.Setup(x => x.Send(It.IsAny <EntityQuery <GeoTask> >(),
                                       It.IsAny <CancellationToken>()))
            .ReturnsAsync(
                new EntityResponse <GeoTask>()
            {
                Success = true,
                Entity  = appEntity
            })
            .Verifiable("Query was not sent.");
            var controller = BuildController(mediator);

            // Act
            var controllerAnswer = await controller.Get(entityId);

            // Assert
            Assert.IsType <OkObjectResult>(controllerAnswer);
            Assert.Equal(apiEntity.Id,
                         (((OkObjectResult)controllerAnswer).Value as ApiGeoTask).Id);
            mediator.Verify(x => x.Send(It.IsAny <EntityQuery <GeoTask> >(),
                                        It.IsAny <CancellationToken>()));
        }
        public async Task <CreateResult> Handle(GeoTaskCreateCommand command,
                                                CancellationToken cancellationToken)
        {
            if (command is null)
            {
                Logger.LogWarning(AppLogEvent.HandleArgumentError,
                                  "Handle create task got empty command");
                return(ErrorResult("Empty Geo Task Create command"));
            }

            var validator        = new GeoTaskCreateCommandValidator();
            var validationResult = await validator
                                   .ValidateAsync(command)
                                   .ConfigureAwait(false);

            if (!validationResult.IsValid)
            {
                Logger.LogWarning(AppLogEvent.RequestValidationError,
                                  "Validation command error. Command = {command}. " +
                                  "Error = {Error}", command.ToDictionary(),
                                  validationResult.Errors);
                return(ErrorResult
                           (validationResult.Errors.Select(x => x.ErrorMessage)));
            }

            try
            {
                var geoTask = new GeoTask()
                {
                    CreatedAt       = DateTime.UtcNow,
                    Description     = command.Description,
                    StatusChangedAt = DateTime.UtcNow,
                    IsArchived      = command.IsArchived,
                    PlanFinishAt    = command.PlanFinishAt,
                    PlanStartAt     = command.PlanStartAt,
                    Status          = GeoTaskStatus.New,
                    Title           = command.Title,
                };

                // Load from repository all mentioned actors
                var allActors = await GetActors(
                    command.AssistentActorsIds
                    .Concat(command.ObserverActorsIds)
                    .Append(command.ResponsibleActorId)
                    ).ConfigureAwait(false);

                // Add to the new entity only exist in the repository actors
                geoTask.AssistentActors.AddRange(
                    command.AssistentActorsIds
                    .Select(x => allActors
                            .FirstOrDefault(a => a.Id == x))
                    .Where(a => a != null));
                geoTask.ObserverActors.AddRange(
                    command.ObserverActorsIds
                    .Select(x => allActors
                            .FirstOrDefault(a => a.Id == x))
                    .Where(a => a != null));
                geoTask.ResponsibleActor = allActors
                                           .FirstOrDefault(a => a.Id == command.ResponsibleActorId);

                // Check that Geo Ids exist in repository
                geoTask.GeosIds.AddRange(await GetGeosIds(command.GeosIds)
                                         .ConfigureAwait(false));

                // Get Actor for current user by user name
                var creatorResponse = await Mediator
                                      .Send(new DbGetActorByNameRequest
                                            (command.CurrentPrincipal?.Identity?.Name))
                                      .ConfigureAwait(false);

                Actor createdBy = null;
                if (creatorResponse.Success)
                {
                    createdBy = creatorResponse.Entity;
                }

                geoTask.CreatedBy = createdBy;

                Project project = await GetProject(command.ProjectId)
                                  .ConfigureAwait(false);

                geoTask.ProjectId = project?.Id;

                var historyRec = new GeoTaskHistory
                {
                    ChangedAt = DateTime.UtcNow,
                    ChangedBy = createdBy,
                };
                historyRec.Operations.Add(new Operation
                {
                    OperationType = addOperation,
                    Path          = "/",
                    NewValue      = geoTask.ToDictionary()
                });
                geoTask.History.Add(historyRec);

                var validatorBeforeSave        = new GeoTaskBeforeSaveValidator();
                var validationBeforeSaveResult = await validatorBeforeSave
                                                 .ValidateAsync(geoTask)
                                                 .ConfigureAwait(false);

                if (!validationBeforeSaveResult.IsValid)
                {
                    Logger.LogWarning(AppLogEvent.RequestNotValid,
                                      "GeoTask validation error. Entity={Entity}. " +
                                      "Error={Error}.", geoTask.ToDictionary(),
                                      validationBeforeSaveResult.Errors);
                    return(ErrorResult(validationBeforeSaveResult.Errors
                                       .Select(x => x.ErrorMessage)));
                }

                var checkPermissionResult = await CheckPermission
                                                (geoTask, createdBy, project)
                                            .ConfigureAwait(false);

                if (!checkPermissionResult.Success)
                {
                    Logger.LogWarning(AppLogEvent.SecurityNotPassed,
                                      "GeoTask check create permission error. " +
                                      "Entity={Entity}. CurrentActor={CurrentActor}. " +
                                      "Project={Project}. Error={Error}.",
                                      geoTask.ToDictionary(), createdBy?.ToDictionary(),
                                      project?.ToDictionary(), checkPermissionResult.Errors);
                    return(checkPermissionResult);
                }

                return(await Mediator
                       .Send(new DbCreateCommand <GeoTask>(geoTask))
                       .ConfigureAwait(false));
            }
            catch (Exception e)
            {
                Logger.LogError(AppLogEvent.HandleErrorResponse, e,
                                "Call repository exception");
                return(ErrorResult("Not found"));
            }
        }
        public async Task <UpdateResult> Handle(GeoTaskUpdateCommand command,
                                                CancellationToken cancellationToken)
        {
            try
            {
                Logger.LogInformation(AppLogEvent.HandleRequest,
                                      "Handle update task {Command}", command.ToDictionary());

                if (command is null || string.IsNullOrWhiteSpace(command.Id))
                {
                    Logger.LogWarning(AppLogEvent.HandleArgumentError,
                                      "Handle update task command with empty request");
                    return(ErrorResult("Command empty argument"));
                }

                var validator        = new GeoTaskUpdateCommandValidator();
                var validationResult = await validator.ValidateAsync(command)
                                       .ConfigureAwait(false);

                if (!validationResult.IsValid)
                {
                    var validationErrors = validationResult.Errors
                                           .Select(x => x.ErrorMessage).ToList();
                    Logger.LogWarning(AppLogEvent.HandleArgumentError,
                                      "Task update command validation error. " +
                                      "Command={Command}. Error={Error}",
                                      command.ToDictionary(), validationErrors);
                    return(ErrorResult(validationErrors));
                }

                var oldGeoTaskResponse = await Mediator
                                         .Send(new DbGetEntityByIdRequest <GeoTask>(command.Id))
                                         .ConfigureAwait(false);

                if (!oldGeoTaskResponse.Success)
                {
                    Logger.LogWarning(AppLogEvent.HandleErrorResponse,
                                      "Get task for update error. Id={Id}. Error={Error}.",
                                      command.Id, oldGeoTaskResponse.Errors);
                    return(ErrorResult(oldGeoTaskResponse.Errors));
                }
                var oldGeoTask = oldGeoTaskResponse.Entity;

                var currentActorResponse = await Mediator
                                           .Send(new DbGetActorByNameRequest
                                                 (command.CurrentPrincipal?.Identity?.Name))
                                           .ConfigureAwait(false);

                var currentActor = currentActorResponse.Entity;

                OldProject = await GetProjectAsync(oldGeoTask.ProjectId)
                             .ConfigureAwait(false);

                // Check Project Id
                NewProject = null;
                if (command.ProjectId != OldProject?.Id)
                {
                    NewProject = await GetProjectAsync(command.ProjectId)
                                 .ConfigureAwait(false);
                }
                else
                {
                    NewProject = OldProject;
                }

                GeoTask newGeoTask = await BuildUpdatedGeoTask(command,
                                                               oldGeoTask, currentActor).ConfigureAwait(false);

                var validatorBeforeSave        = new GeoTaskBeforeSaveValidator();
                var validationBeforeSaveResult = await validatorBeforeSave
                                                 .ValidateAsync(newGeoTask)
                                                 .ConfigureAwait(false);

                if (!validationBeforeSaveResult.IsValid)
                {
                    Logger.LogWarning(AppLogEvent.RequestValidationError,
                                      "Update GeoTask validation error. " +
                                      "Entity={Entity}. Error={Error}.",
                                      newGeoTask.ToDictionary(),
                                      validationBeforeSaveResult.Errors);
                    return(ErrorResult(validationBeforeSaveResult.Errors
                                       .Select(x => x.ErrorMessage)));
                }

                ActorRole oldProjectRole = null;
                OldProject?.ProjectActorRoles?
                .TryGetValue(currentActor.Id, out oldProjectRole);
                ActorRole newProjectRole = null;
                if (oldGeoTask.ProjectId != newGeoTask.ProjectId)
                {
                    NewProject?.ProjectActorRoles?
                    .TryGetValue(currentActor.Id, out newProjectRole);
                }
                else
                {
                    newProjectRole = oldProjectRole;
                }

                var permissionValidateResult = await CheckPermissionAsync
                                                   (oldGeoTask, newGeoTask, currentActor,
                                                   oldProjectRole, newProjectRole)
                                               .ConfigureAwait(false);

                if (!permissionValidateResult.IsValid)
                {
                    Logger.LogWarning(AppLogEvent.SecurityNotPassed,
                                      "Current actor has no rights to update GeoTask. " +
                                      "Actor={Actor}. Entity before update={OldEntity}." +
                                      " Entity after update={NewEntity}. Error={Error}.",
                                      currentActor.ToDictionary(), oldGeoTask.ToDictionary(),
                                      newGeoTask.ToDictionary(),
                                      permissionValidateResult.Errors
                                      .Select(x => x.ErrorMessage));
                    return(ErrorResult(permissionValidateResult.Errors
                                       .Select(x => x.ErrorMessage)));
                }

                var updateResult = await Mediator
                                   .Send(new DbUpdateCommand <GeoTask>(newGeoTask))
                                   .ConfigureAwait(false);

                return(updateResult);
            }

            catch (Exception e)
            {
                Logger.LogError(AppLogEvent.HandleErrorResponse, e,
                                "Geo task update exception");
                return(ErrorResult("Geo task update exception"));
            }
        }
Example #13
0
        public static IEnumerable <Operation> ToHistoryOperations
            (this GeoTask newGeoTask, GeoTask oldGeoTask)
        {
            if (newGeoTask?.CreatedAt != oldGeoTask?.CreatedAt)
            {
                yield return(new Operation
                {
                    NewValue = newGeoTask?.CreatedAt,
                    OldValue = oldGeoTask?.CreatedAt,
                    OperationType = _TJsonPatchOperation.Replace,
                    Path = $"/{nameof(newGeoTask.CreatedAt)}"
                });
            }
            ;

            if (newGeoTask?.CreatedBy != oldGeoTask?.CreatedBy)
            {
                yield return(new Operation
                {
                    NewValue = newGeoTask?.CreatedBy,
                    OldValue = oldGeoTask?.CreatedBy,
                    OperationType = _TJsonPatchOperation.Replace,
                    Path = $"/{nameof(newGeoTask.CreatedBy)}"
                });
            }
            ;

            if (newGeoTask?.Description != oldGeoTask?.Description)
            {
                yield return(new Operation
                {
                    NewValue = newGeoTask?.Description,
                    OldValue = oldGeoTask?.Description,
                    OperationType = _TJsonPatchOperation.Replace,
                    Path = $"/{nameof(newGeoTask.Description)}"
                });
            }
            ;

            if (newGeoTask?.Id != oldGeoTask?.Id)
            {
                yield return(new Operation
                {
                    NewValue = newGeoTask?.Id,
                    OldValue = oldGeoTask?.Id,
                    OperationType = _TJsonPatchOperation.Replace,
                    Path = $"/{nameof(newGeoTask.Id)}"
                });
            }
            ;

            if (newGeoTask?.IsArchived != oldGeoTask?.IsArchived)
            {
                yield return(new Operation
                {
                    NewValue = newGeoTask?.IsArchived,
                    OldValue = oldGeoTask?.IsArchived,
                    OperationType = _TJsonPatchOperation.Replace,
                    Path = $"/{nameof(newGeoTask.IsArchived)}"
                });
            }
            ;

            if (newGeoTask?.PlanFinishAt != oldGeoTask?.PlanFinishAt)
            {
                yield return(new Operation
                {
                    NewValue = newGeoTask?.PlanFinishAt,
                    OldValue = oldGeoTask?.PlanFinishAt,
                    OperationType = _TJsonPatchOperation.Replace,
                    Path = $"/{nameof(newGeoTask.PlanFinishAt)}"
                });
            }
            ;

            if (newGeoTask?.PlanStartAt != oldGeoTask?.PlanStartAt)
            {
                yield return(new Operation
                {
                    NewValue = newGeoTask?.PlanStartAt,
                    OldValue = oldGeoTask?.PlanStartAt,
                    OperationType = _TJsonPatchOperation.Replace,
                    Path = $"/{nameof(newGeoTask.PlanStartAt)}"
                });
            }
            ;

            if (newGeoTask?.ProjectId != oldGeoTask?.ProjectId)
            {
                yield return(new Operation
                {
                    NewValue = newGeoTask?.ProjectId,
                    OldValue = oldGeoTask?.ProjectId,
                    OperationType = _TJsonPatchOperation.Replace,
                    Path = $"/{nameof(newGeoTask.ProjectId)}"
                });
            }
            ;

            if (newGeoTask?.ResponsibleActor != oldGeoTask?.ResponsibleActor)
            {
                yield return(new Operation
                {
                    NewValue = newGeoTask?.ResponsibleActor,
                    OldValue = oldGeoTask?.ResponsibleActor,
                    OperationType = _TJsonPatchOperation.Replace,
                    Path = $"/{nameof(newGeoTask.ResponsibleActor)}"
                });
            }
            ;

            if (newGeoTask?.Status != oldGeoTask?.Status)
            {
                yield return(new Operation
                {
                    NewValue = newGeoTask?.Status,
                    OldValue = oldGeoTask?.Status,
                    OperationType = _TJsonPatchOperation.Replace,
                    Path = $"/{nameof(newGeoTask.Status)}"
                });
            }
            ;

            if (newGeoTask?.StatusChangedAt != oldGeoTask?.StatusChangedAt)
            {
                yield return(new Operation
                {
                    NewValue = newGeoTask?.StatusChangedAt,
                    OldValue = oldGeoTask?.StatusChangedAt,
                    OperationType = _TJsonPatchOperation.Replace,
                    Path = $"/{nameof(newGeoTask.StatusChangedAt)}"
                });
            }
            ;

            if (newGeoTask?.Title != oldGeoTask?.Title)
            {
                yield return(new Operation
                {
                    NewValue = newGeoTask?.Title,
                    OldValue = oldGeoTask?.Title,
                    OperationType = _TJsonPatchOperation.Replace,
                    Path = $"/{nameof(newGeoTask.Title)}"
                });
            }
            ;

            var newAssistenceActorsSet = newGeoTask?.AssistentActors.ToHashSet()
                                         ?? new HashSet <Actor>();
            var oldAssistenceActorsSet = oldGeoTask?.AssistentActors.ToHashSet()
                                         ?? new HashSet <Actor>();
            var addedAssistenceActors = newAssistenceActorsSet
                                        .Except(oldAssistenceActorsSet);
            var removedAssistenceActors = oldAssistenceActorsSet
                                          .Except(newAssistenceActorsSet);

            foreach (var item in removedAssistenceActors)
            {
                yield return(new Operation
                {
                    OldValue = item,
                    OperationType = _TJsonPatchOperation.Remove,
                    Path = $"/{nameof(newGeoTask.AssistentActors)}"
                });
            }
            foreach (var item in addedAssistenceActors)
            {
                yield return(new Operation
                {
                    NewValue = item,
                    OperationType = _TJsonPatchOperation.Add,
                    Path = $"/{nameof(newGeoTask.AssistentActors)}"
                });
            }

            var newObserverActorsSet = newGeoTask?.ObserverActors.ToHashSet()
                                       ?? new HashSet <Actor>();
            var oldObserverActorsSet = oldGeoTask?.ObserverActors.ToHashSet()
                                       ?? new HashSet <Actor>();
            var addedObserverActors = newObserverActorsSet
                                      .Except(oldObserverActorsSet);
            var removedObserverActors = oldObserverActorsSet
                                        .Except(newObserverActorsSet);

            foreach (var item in removedObserverActors)
            {
                yield return(new Operation
                {
                    OldValue = item,
                    OperationType = _TJsonPatchOperation.Remove,
                    Path = $"/{nameof(newGeoTask.ObserverActors)}"
                });
            }
            foreach (var item in addedObserverActors)
            {
                yield return(new Operation
                {
                    NewValue = item,
                    OperationType = _TJsonPatchOperation.Add,
                    Path = $"/{nameof(newGeoTask.ObserverActors)}"
                });
            }

            var newGeosIdsSet = newGeoTask?.GeosIds.ToHashSet()
                                ?? new HashSet <string>();
            var oldGeosIdsSet = oldGeoTask?.GeosIds.ToHashSet()
                                ?? new HashSet <string>();
            var addedGeosIds   = newGeosIdsSet.Except(oldGeosIdsSet);
            var removedGeosIds = oldGeosIdsSet.Except(newGeosIdsSet);

            foreach (var item in removedGeosIds)
            {
                yield return(new Operation
                {
                    OldValue = item,
                    OperationType = _TJsonPatchOperation.Remove,
                    Path = $"/{nameof(newGeoTask.GeosIds)}"
                });
            }
            foreach (var item in addedGeosIds)
            {
                yield return(new Operation
                {
                    NewValue = item,
                    OperationType = _TJsonPatchOperation.Add,
                    Path = $"/{nameof(newGeoTask.GeosIds)}"
                });
            }
        }