/jira-project/jira-components/jira-core/src/main/java/com/atlassian/jira/bulkedit/operation/BulkMigrateOperation.java
Java | 288 lines | 220 code | 39 blank | 29 comment | 54 complexity | 76aba144e1e6c3fb1d9939ce333dcba1 MD5 | raw file
Possible License(s): Apache-2.0
- package com.atlassian.jira.bulkedit.operation;
- import com.atlassian.collectors.CollectorsUtil;
- import com.atlassian.jira.config.SubTaskManager;
- import com.atlassian.jira.issue.Issue;
- import com.atlassian.jira.issue.IssueManager;
- import com.atlassian.jira.issue.MutableIssue;
- import com.atlassian.jira.issue.link.IssueLink;
- import com.atlassian.jira.task.context.Context;
- import com.atlassian.jira.user.ApplicationUser;
- import com.atlassian.jira.util.ErrorCollection;
- import com.atlassian.jira.util.I18nHelper;
- import com.atlassian.jira.web.bean.BulkEditBean;
- import com.atlassian.jira.web.bean.MultiBulkMoveBean;
- import com.atlassian.jira.workflow.WorkflowException;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import java.util.Collection;
- import java.util.HashSet;
- import java.util.Objects;
- import java.util.Optional;
- import java.util.Set;
- /**
- * Operation to Move issues from differing contexts to multiple target contexts.
- */
- public class BulkMigrateOperation implements ProgressAwareBulkOperation {
- public static final String OPERATION_NAME = "BulkMigrate";
- public static final String NAME_KEY = "bulk.move.operation.name";
- private static final String DESCRIPTION_KEY = "bulk.move.operation.description";
- private static final Logger log = LoggerFactory.getLogger(BulkMigrateOperation.class);
- private final BulkMoveOperation bulkMoveOperation;
- private final SubTaskManager subTaskManager;
- private final IssueManager issueManager;
- public BulkMigrateOperation(BulkMoveOperation bulkMoveOperation, SubTaskManager subTaskManager, IssueManager issueManager) {
- this.bulkMoveOperation = bulkMoveOperation;
- this.subTaskManager = subTaskManager;
- this.issueManager = issueManager;
- }
- @Override
- public boolean canPerform(final BulkEditBean bulkEditBean, final ApplicationUser remoteUser) {
- return bulkMoveOperation.canPerform(bulkEditBean, remoteUser);
- }
- @Override
- public void perform(final BulkEditBean rootBulkEditBean, final ApplicationUser applicationUser, Context taskContext)
- throws BulkOperationException {
- try {
- MultiBulkMoveBean multiBulkMoveBean = rootBulkEditBean.getRelatedMultiBulkMoveBean();
- for (final Object value : multiBulkMoveBean.getBulkEditBeans().values()) {
- BulkEditBean bulkEditBean = (BulkEditBean) value;
- log.debug("Performing move for project " + bulkEditBean.getTargetProject().getName() +
- " issue type: " + bulkEditBean.getTargetIssueType().getName());
- //This move changes the security level of the subtask, however the subtask move below, will overwrite this again
- // See JRA-13937 - Bulk Move does not update the Security Level of subtasks for more details
- bulkMoveOperation.moveIssuesAndIndex(bulkEditBean, applicationUser, taskContext);
- MultiBulkMoveBean relatedMultiBulkMoveBean = bulkEditBean.getRelatedMultiBulkMoveBean();
- if (relatedMultiBulkMoveBean != null && relatedMultiBulkMoveBean.getBulkEditBeans() != null) {
- for (final Object o : relatedMultiBulkMoveBean.getBulkEditBeans().values()) {
- BulkEditBean subTaskBulkEditBean = (BulkEditBean) o;
- log.info("subTaskBulkEditBean move for project " +
- subTaskBulkEditBean.getTargetProject().getName() + " issue type: " +
- subTaskBulkEditBean.getTargetIssueType().getName());
- bulkMoveOperation.moveIssuesAndIndex(subTaskBulkEditBean, applicationUser, taskContext);
- }
- }
- }
- } catch (Exception e) {
- throw new BulkOperationException(e);
- }
- }
- public Optional<Issue> isConcurrentIssueUpdate(BulkEditBean rootBulkEditBean) {
- final MultiBulkMoveBean multiBulkMoveBean = rootBulkEditBean.getRelatedMultiBulkMoveBean();
- for (final Object value : multiBulkMoveBean.getBulkEditBeans().values()) {
- BulkEditBean bulkEditBean = (BulkEditBean) value;
- Optional<Issue> updated = isConcurrentIssueUpdateSingleBulkEditBean(bulkEditBean);
- if( updated.isPresent() ) {
- return updated;
- }
- MultiBulkMoveBean relatedMultiBulkMoveBean = bulkEditBean.getRelatedMultiBulkMoveBean();
- if (relatedMultiBulkMoveBean != null && relatedMultiBulkMoveBean.getBulkEditBeans() != null) {
- for (final Object o : relatedMultiBulkMoveBean.getBulkEditBeans().values()) {
- BulkEditBean subTaskBulkEditBean = (BulkEditBean) o;
- updated = isConcurrentIssueUpdateSingleBulkEditBean(subTaskBulkEditBean);
- if( updated.isPresent() ) {
- return updated;
- }
- }
- }
- }
- return Optional.empty();
- }
- public Optional<Issue> isConcurrentIssueUpdateSingleBulkEditBean(BulkEditBean bulkEditBean) {
- final Collection selectedIssues = bulkEditBean.getSelectedIssues();
- for (final Object selectedIssue : selectedIssues) {
- final MutableIssue issue = (MutableIssue) selectedIssue;
- final MutableIssue newIssue = (MutableIssue) bulkEditBean.getTargetIssueObjects().get(issue);
- if( isConcurrentIssueUpdate(bulkEditBean, issue, newIssue) ) {
- return Optional.of(issue);
- }
- }
- return Optional.empty();
- }
- private boolean isConcurrentIssueUpdate(final BulkEditBean bulkEditBean, final MutableIssue issue, final MutableIssue newIssue) {
- if (!issue.isSubTask()) {
- if (newIssue.isSubTask() || !issue.getProjectId().equals(newIssue.getProjectId())) {
- final Set<Long> oldSubtaskIds = bulkEditBean.getSubTaskOfSelectedIssues().stream().filter(st -> st.getParentId().equals(issue.getId())).map(Issue::getId).collect(CollectorsUtil.toImmutableSet());
- final Set<Long> actualSubtasksIds = subTaskManager.getSubTaskIssueLinks(issue.getId()).stream().map(IssueLink::getDestinationId).collect(CollectorsUtil.toImmutableSet());
- if (!oldSubtaskIds.equals(actualSubtasksIds)) {
- String errorMsg = String.format("Unable to move parent issue '%d [%s]' - subtask have changed from %s to %s",
- issue.getId(), issue.getKey(), oldSubtaskIds.toString(), actualSubtasksIds);
- log.warn(errorMsg);
- return true;
- }
- }
- }
- if (isNewParentIssueChanged(issue, newIssue)) {
- String errorMsg = String.format("Concurrent update of new parent issue '%d [%s]' occured during move of '%d [%s]' issue",
- newIssue.getParentObject().getId(), newIssue.getParentObject().getKey(),
- issue.getId(), issue.getKey());
- log.warn(errorMsg);
- return true;
- }
- MutableIssue issueFromDb = issueManager.getIssueObject(issue.getId());
- if (isIssueChanged(issue, issueFromDb)) {
- String errorMsg = String.format("Concurrent update of issue '%d [%s]' occured during move",
- issue.getId(), issue.getKey());
- log.warn(errorMsg);
- return true;
- }
- return false;
- }
- /**
- * Checks if moved issue have changed during the bulk move process.
- * If parent issue, project, security level, issue type, workflow is changed then return true.
- *
- * @param issue Issue to perform check on.
- * @return true if issue have changed during the bulk move, false otherwise.
- */
- private boolean isIssueChanged(final Issue issue, final Issue issueFromDb) {
- if (issueFromDb == null) {
- return true;
- }
- final boolean projectChanged = !Objects.equals(issue.getProjectId(), issueFromDb.getProjectId());
- boolean securityLevelChanged = false;
- if (!issueFromDb.isSubTask()) {
- securityLevelChanged = !Objects.equals(issue.getSecurityLevelId(), issueFromDb.getSecurityLevelId());
- }
- final boolean changedIssueType = !issue.getIssueTypeId().equals(issueFromDb.getIssueTypeId());
- final boolean parentIssueChanged = !Objects.equals(issue.getParentId(), issueFromDb.getParentId());
- final boolean changedWorkflow = !issue.getWorkflowId().equals(issueFromDb.getWorkflowId());
- return securityLevelChanged || projectChanged || parentIssueChanged || changedIssueType || changedWorkflow;
- }
- /**
- * Checks that the parent issue has not changed.
- * <p>
- * <p>
- * When moved issue changes parent issue then we need to check if parent issue is not changed between processing
- * form and actual bulk move operation.
- * </p>
- *
- * @param issueOld original issue
- * @param issueNew destination issue
- * @return true if parent issue is changed and any value from: project id, issue type, security level, workflow id
- * has changed since form processing (concurrent update to the parent issue)
- */
- private boolean isNewParentIssueChanged(final MutableIssue issueOld, final MutableIssue issueNew) {
- Issue oldParent = issueOld.getParentObject();
- Issue newParent = issueNew.getParentObject();
- if ((oldParent == null && newParent == null) || (oldParent != null && newParent == null)) {
- //no destination parent = no change in destination parent
- return false;
- } else if (newParentSetOrParentChanged(oldParent, newParent)) {
- //set new parent issue, or change parent
- MutableIssue newParentFromDb = issueManager.getIssueObject(issueNew.getParentObject().getId());
- return isIssueChanged(newParent, newParentFromDb);
- }
- return false;
- }
- private boolean newParentSetOrParentChanged(final Issue oldParent, final Issue newParent) {
- return (oldParent == null && newParent != null) ||
- (oldParent != null && newParent != null && !oldParent.getId().equals(newParent.getId()));
- }
- @Override
- public int getNumberOfTasks(final BulkEditBean rootBulkEditBean) {
- int count = 0;
- for (final Object o1 : rootBulkEditBean.getRelatedMultiBulkMoveBean().getBulkEditBeans().values()) {
- BulkEditBean bulkEditBean = (BulkEditBean) o1;
- count += bulkEditBean.getSelectedIssues().size();
- MultiBulkMoveBean relatedMultiBulkMoveBean = bulkEditBean.getRelatedMultiBulkMoveBean();
- if (relatedMultiBulkMoveBean != null && relatedMultiBulkMoveBean.getBulkEditBeans() != null) {
- for (final Object o : relatedMultiBulkMoveBean.getBulkEditBeans().values()) {
- BulkEditBean subTaskBulkEditBean = (BulkEditBean) o;
- count += subTaskBulkEditBean.getSelectedIssues().size();
- }
- }
- }
- return count;
- }
- public void chooseContext(BulkEditBean rootBulkEditBean, ApplicationUser applicationUser, I18nHelper i18nHelper,
- ErrorCollection errors) {
- Set<String> allIssuesByKey = new HashSet<>();
- // Loop through the child BulkEditBeans and do a chooseContext() on each.
- for (final Object o : rootBulkEditBean.getRelatedMultiBulkMoveBean().getBulkEditBeans().values()) {
- BulkEditBean bulkEditBean = (BulkEditBean) o;
- bulkMoveOperation.chooseContext(bulkEditBean, applicationUser, i18nHelper, errors);
- allIssuesByKey.addAll(bulkEditBean.getSelectedIssues().stream().map(Issue::getKey).collect(CollectorsUtil.toImmutableSet()));
- }
- //check if no selected parent issue is in currently processed issues
- for (final Object o : rootBulkEditBean.getRelatedMultiBulkMoveBean().getBulkEditBeans().values()) {
- BulkEditBean bulkEditBean = (BulkEditBean) o;
- Optional.ofNullable(bulkEditBean.getParentIssueKey()).filter(key -> allIssuesByKey.contains(key))
- .ifPresent(
- key -> errors.addError(bulkEditBean.getKey() + "parentIssueKey", i18nHelper.getText("convert.issue.to.subtask.error.parentincurrentgroup"))
- );
- }
- }
- public void chooseContextNoValidate(BulkEditBean rootBulkEditBean, ApplicationUser applicationUser) {
- bulkMoveOperation
- .chooseContextNoValidate(rootBulkEditBean.getRelatedMultiBulkMoveBean().getCurrentBulkEditBean(),
- applicationUser);
- }
- public boolean isStatusValid(BulkEditBean rootBulkEditBean) {
- return bulkMoveOperation.isStatusValid(rootBulkEditBean.getRelatedMultiBulkMoveBean().getCurrentBulkEditBean());
- }
- public void setStatusFields(BulkEditBean rootBulkEditBean) throws WorkflowException {
- bulkMoveOperation.setStatusFields(rootBulkEditBean.getRelatedMultiBulkMoveBean().getCurrentBulkEditBean());
- }
- public void validatePopulateFields(BulkEditBean rootBulkEditBean, I18nHelper i18nHelper, ErrorCollection errors) {
- bulkMoveOperation.validatePopulateFields(
- rootBulkEditBean.getRelatedMultiBulkMoveBean().getCurrentBulkEditBean(), errors, i18nHelper);
- }
- @Override
- public String getNameKey() {
- return NAME_KEY;
- }
- @Override
- public String getDescriptionKey() {
- return DESCRIPTION_KEY;
- }
- @Override
- public String getOperationName() {
- return OPERATION_NAME;
- }
- @Override
- public String getCannotPerformMessageKey() {
- return BulkMoveOperation.CANNOT_PERFORM_MESSAGE_KEY;
- }
- public BulkMoveOperation getBulkMoveOperation() {
- return bulkMoveOperation;
- }
- }