private void EnsureProjectNotChanged(TaskAggregate currentTask, TaskRelatedData newTaskData)
 {
     if (newTaskData.List.ProjectId != currentTask.ProjectId)
     {
         throw new ProhibitedException($"You can't replace task to list in other project during an update process");
     }
 }
        public async Task <TaskAggregate> CreateTaskAsync(TaskModel newTask,
                                                          IEnumerable <string> usersIds, IEnumerable <string> labelsIds, string requestId)
        {
            if (!(await CheckAndSaveRequestIdAsync(requestId)))
            {
                throw new AlreadyHandledException();
            }

            try
            {
                newTask.Init();

                TaskRelatedData taskData = await GetAndCheckTaskRelatedDataAsync(newTask, usersIds, labelsIds);

                var list = taskData.List;

                var taskCollections = new TaskCollections
                {
                    Members = usersIds,
                    Labels  = labelsIds
                };

                var membersRecords = _mapper.Map <IEnumerable <UserModel>, IEnumerable <TaskUserRecord> >(taskData.Users);
                var labelsRecords  = _mapper.Map <IEnumerable <LabelModel>, IEnumerable <TaskLabelRecord> >(taskData.Labels);

                var outboxMessage = OutboxMessageModel.Create(
                    new TaskCreatedMessage
                {
                    TaskId       = newTask.Id,
                    Title        = newTask.Title,
                    ProjectId    = list.ProjectId,
                    ProjectTitle = list.ProjectTitle,
                    ListId       = list.Id,
                    ListTitle    = list.Title,
                    Members      = membersRecords,
                    Labels       = labelsRecords
                }, Topics.Tasks, MessageActions.Created);

                var createdTask = await _tasksRepository.CreateTaskAsync(newTask, taskCollections, outboxMessage);

                List <Task> cacheInvalidationTasks = new List <Task>();
                foreach (var user in usersIds)
                {
                    string userCacheKey = string.Format(CacheSettings.UserTasksCacheKeyPattern, user);
                    cacheInvalidationTasks.Add(_cache.RemoveAsync(userCacheKey));
                }

                await Task.WhenAll(cacheInvalidationTasks);

                return(createdTask);
            }
            catch (Exception)
            {
                //rollback request id
                await _requestsRepository.DeleteRequestIdAsync(requestId);

                throw;
            }
        }
        public async Task <TaskAggregate> CreateTaskAsync(TaskModel newTask,
                                                          IEnumerable <string> usersIds, IEnumerable <string> labelsIds, string requestId)
        {
            if (!(await CheckAndSaveRequestIdAsync(requestId)))
            {
                throw new AlreadyHandledException();
            }

            try
            {
                newTask.Init();

                TaskRelatedData taskData = await GetAndCheckTaskRelatedDataAsync(newTask, usersIds, labelsIds);

                var list = taskData.List;

                var taskCollections = new TaskCollections
                {
                    Members = usersIds,
                    Labels  = labelsIds
                };

                var membersRecords = _mapper.Map <IEnumerable <UserModel>, IEnumerable <TaskUserRecord> >(taskData.Users);
                var labelsRecords  = _mapper.Map <IEnumerable <LabelModel>, IEnumerable <TaskLabelRecord> >(taskData.Labels);

                var outboxMessage = OutboxMessageModel.Create(
                    new TaskCreatedMessage
                {
                    TaskId       = newTask.Id,
                    Title        = newTask.Title,
                    ProjectId    = list.ProjectId,
                    ProjectTitle = list.ProjectTitle,
                    ListId       = list.Id,
                    ListTitle    = list.Title,
                    Members      = membersRecords,
                    Labels       = labelsRecords
                }, Topics.Tasks, MessageActions.Created);

                return(await _tasksRepository.CreateTaskAsync(newTask, taskCollections, outboxMessage));
            }
            catch (Exception)
            {
                //rollback request id
                await _requestsRepository.DeleteRequestIdAsync(requestId);

                throw;
            }
        }
        public async Task <TaskAggregate> UpdateTaskAsync(TaskModel updatingTask, IEnumerable <string> usersIds,
                                                          IEnumerable <string> labelsIds)
        {
            TaskAggregate currentTask = await _tasksRepository.GetTaskAsync(updatingTask.Id);

            if (currentTask == null)
            {
                throw new NotFoundException($"Task with id = {updatingTask.Id} not found");
            }

            if (currentTask.Version != updatingTask.Version)
            {
                throw new VersionsNotMatchException();
            }

            TaskRelatedData taskData = await GetAndCheckTaskRelatedDataAsync(updatingTask, usersIds, labelsIds);

            EnsureProjectNotChanged(currentTask, taskData);

            var(removingCollections, addingCollections) = DetermineAddedAndRemovedItems(currentTask, taskData);
            var list = taskData.List;

            var addedMembersRecords   = _mapper.Map <IEnumerable <UserModel>, IEnumerable <TaskUserRecord> >(addingCollections.Users);
            var removedMembersRecords = _mapper.Map <IEnumerable <UserModel>, IEnumerable <TaskUserRecord> >(removingCollections.Users);
            var addedLabelsRecords    = _mapper.Map <IEnumerable <LabelModel>, IEnumerable <TaskLabelRecord> >(addingCollections.Labels);
            var removedLabelsRecords  = _mapper.Map <IEnumerable <LabelModel>, IEnumerable <TaskLabelRecord> >(removingCollections.Labels);

            var outboxMessage = OutboxMessageModel.Create(
                new TaskUpdatedMessage
            {
                TaskId         = updatingTask.Id,
                Title          = updatingTask.Title,
                ProjectId      = list.ProjectId,
                ProjectTitle   = list.ProjectTitle,
                ListId         = list.Id,
                ListTitle      = list.Title,
                RemovedMembers = removedMembersRecords,
                AddedMembers   = addedMembersRecords,
                RemovedLabels  = removedLabelsRecords,
                AddedLabels    = addedLabelsRecords
            }, Topics.Tasks, MessageActions.Updated);


            return(await _tasksRepository.UpdateTaskAsync(updatingTask, addingCollections.ToTaskCollections(),
                                                          removingCollections.ToTaskCollections(), outboxMessage));
        }
        private (TaskRelatedData removingData, TaskRelatedData addingData) DetermineAddedAndRemovedItems(
            TaskAggregate currentTask, TaskRelatedData newData)
        {
            IEnumerable <UserModel>  addedMembers = newData.Users.Except <UserModel>(currentTask.Members, _modelsByIdComparer).ToList();
            IEnumerable <LabelModel> addedLabels  = newData.Labels.Except <LabelModel>(currentTask.Labels, _modelsByIdComparer).ToList();

            IEnumerable <UserModel>  removedMembers = currentTask.Members.Except <UserModel>(newData.Users, _modelsByIdComparer).ToList();
            IEnumerable <LabelModel> removedLabels  = currentTask.Labels.Except <LabelModel>(newData.Labels, _modelsByIdComparer).ToList();

            TaskRelatedData addingCollections = new TaskRelatedData
            {
                Users  = addedMembers,
                Labels = addedLabels
            };

            TaskRelatedData removingCollections = new TaskRelatedData
            {
                Users  = removedMembers,
                Labels = removedLabels
            };

            return(removingCollections, addingCollections);
        }
        public async Task <TaskAggregate> UpdateTaskAsync(TaskModel updatingTask, IEnumerable <string> usersIds,
                                                          IEnumerable <string> labelsIds)
        {
            TaskAggregate currentTask = await _tasksRepository.GetTaskAsync(updatingTask.Id);

            if (currentTask == null)
            {
                throw new NotFoundException($"Task with id = {updatingTask.Id} not found");
            }

            if (currentTask.Version != updatingTask.Version)
            {
                throw new VersionsNotMatchException();
            }

            TaskRelatedData taskData = await GetAndCheckTaskRelatedDataAsync(updatingTask, usersIds, labelsIds);

            EnsureProjectNotChanged(currentTask, taskData);

            var(removingCollections, addingCollections) = DetermineAddedAndRemovedItems(currentTask, taskData);
            var list = taskData.List;

            var addedMembersRecords   = _mapper.Map <IEnumerable <UserModel>, IEnumerable <TaskUserRecord> >(addingCollections.Users);
            var removedMembersRecords = _mapper.Map <IEnumerable <UserModel>, IEnumerable <TaskUserRecord> >(removingCollections.Users);
            var addedLabelsRecords    = _mapper.Map <IEnumerable <LabelModel>, IEnumerable <TaskLabelRecord> >(addingCollections.Labels);
            var removedLabelsRecords  = _mapper.Map <IEnumerable <LabelModel>, IEnumerable <TaskLabelRecord> >(removingCollections.Labels);

            var outboxMessage = OutboxMessageModel.Create(
                new TaskUpdatedMessage
            {
                TaskId         = updatingTask.Id,
                Title          = updatingTask.Title,
                ProjectId      = list.ProjectId,
                ProjectTitle   = list.ProjectTitle,
                ListId         = list.Id,
                ListTitle      = list.Title,
                RemovedMembers = removedMembersRecords,
                AddedMembers   = addedMembersRecords,
                RemovedLabels  = removedLabelsRecords,
                AddedLabels    = addedLabelsRecords
            }, Topics.Tasks, MessageActions.Updated);

            updatingTask.Version += 1;

            var updatedTask = await _tasksRepository.UpdateTaskAsync(updatingTask, addingCollections.ToTaskCollections(),
                                                                     removingCollections.ToTaskCollections(), outboxMessage);


            string cacheKey = string.Format(CacheSettings.TaskIdCacheKeyPattern, updatedTask.Id);

            List <Task> cacheInvalidationTasks = new List <Task>();

            cacheInvalidationTasks.Add(_cache.SetCacheValueAsync(cacheKey, updatedTask, CacheSettings.TaskIdCacheLifetime));

            foreach (var user in usersIds)
            {
                string userCacheKey = string.Format(CacheSettings.UserTasksCacheKeyPattern, user);
                cacheInvalidationTasks.Add(_cache.RemoveAsync(userCacheKey));
            }

            foreach (var user in removingCollections.Users)
            {
                string userCacheKey = string.Format(CacheSettings.UserTasksCacheKeyPattern, user.Id);
                cacheInvalidationTasks.Add(_cache.RemoveAsync(userCacheKey));
            }

            await Task.WhenAll(cacheInvalidationTasks);

            return(updatedTask);
        }