blob: 9cce9c5135298af864418186977c5e8f91bbb887 [file] [log] [blame]
/*********************************************************************
* Copyright (c) 2013 Boeing
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Boeing - initial API and implementation
**********************************************************************/
package org.eclipse.osee.ats.core.workflow;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.eclipse.osee.ats.api.AtsApi;
import org.eclipse.osee.ats.api.IAtsWorkItem;
import org.eclipse.osee.ats.api.agile.IAgileBacklog;
import org.eclipse.osee.ats.api.agile.IAgileItem;
import org.eclipse.osee.ats.api.agile.IAgileSprint;
import org.eclipse.osee.ats.api.data.AtsArtifactTypes;
import org.eclipse.osee.ats.api.data.AtsAttributeTypes;
import org.eclipse.osee.ats.api.data.AtsRelationTypes;
import org.eclipse.osee.ats.api.review.IAtsAbstractReview;
import org.eclipse.osee.ats.api.team.IAtsTeamDefinition;
import org.eclipse.osee.ats.api.team.ITeamWorkflowProvider;
import org.eclipse.osee.ats.api.user.AtsUser;
import org.eclipse.osee.ats.api.util.IAtsChangeSet;
import org.eclipse.osee.ats.api.version.IAtsVersion;
import org.eclipse.osee.ats.api.workdef.IAtsStateDefinition;
import org.eclipse.osee.ats.api.workdef.IStateToken;
import org.eclipse.osee.ats.api.workdef.WidgetResult;
import org.eclipse.osee.ats.api.workflow.ActionResult;
import org.eclipse.osee.ats.api.workflow.IAtsAction;
import org.eclipse.osee.ats.api.workflow.IAtsGoal;
import org.eclipse.osee.ats.api.workflow.IAtsTask;
import org.eclipse.osee.ats.api.workflow.IAtsTeamWorkflow;
import org.eclipse.osee.ats.api.workflow.IAtsWorkItemService;
import org.eclipse.osee.ats.api.workflow.ITeamWorkflowProvidersLazy;
import org.eclipse.osee.ats.api.workflow.WorkItemType;
import org.eclipse.osee.ats.api.workflow.hooks.IAtsTransitionHook;
import org.eclipse.osee.ats.api.workflow.hooks.IAtsWorkItemHook;
import org.eclipse.osee.ats.api.workflow.note.IAtsWorkItemNotes;
import org.eclipse.osee.ats.api.workflow.transition.ITransitionHelper;
import org.eclipse.osee.ats.api.workflow.transition.TransitionData;
import org.eclipse.osee.ats.api.workflow.transition.TransitionResults;
import org.eclipse.osee.ats.core.agile.AgileBacklog;
import org.eclipse.osee.ats.core.agile.AgileSprint;
import org.eclipse.osee.ats.core.review.DecisionReviewOnTransitionToHook;
import org.eclipse.osee.ats.core.review.PeerReviewOnTransitionToHook;
import org.eclipse.osee.ats.core.review.hooks.AtsDecisionReviewPrepareWorkItemHook;
import org.eclipse.osee.ats.core.util.AtsObjects;
import org.eclipse.osee.ats.core.util.hooks.AtsNotificationTransitionHook;
import org.eclipse.osee.ats.core.validator.AtsXWidgetValidateManager;
import org.eclipse.osee.ats.core.workflow.hooks.AtsCommitBranchWhenCompleteHook;
import org.eclipse.osee.ats.core.workflow.hooks.AtsForceAssigneesToTeamLeadsWorkItemHook;
import org.eclipse.osee.ats.core.workflow.hooks.AtsPeerToPeerReviewReviewWorkItemHook;
import org.eclipse.osee.ats.core.workflow.note.ArtifactNote;
import org.eclipse.osee.ats.core.workflow.note.AtsWorkItemNotes;
import org.eclipse.osee.ats.core.workflow.transition.TransitionHelper;
import org.eclipse.osee.ats.core.workflow.transition.TransitionManager;
import org.eclipse.osee.ats.core.workflow.util.ChangeTypeUtil;
import org.eclipse.osee.framework.core.data.ArtifactId;
import org.eclipse.osee.framework.core.data.ArtifactToken;
import org.eclipse.osee.framework.jdk.core.type.OseeArgumentException;
import org.eclipse.osee.framework.jdk.core.type.OseeCoreException;
import org.eclipse.osee.framework.jdk.core.util.Collections;
import org.eclipse.osee.framework.jdk.core.util.Conditions;
import org.eclipse.osee.framework.jdk.core.util.GUID;
import org.eclipse.osee.framework.jdk.core.util.Strings;
/**
* @author Donald G. Dunne
*/
public class AtsWorkItemServiceImpl implements IAtsWorkItemService {
private final ITeamWorkflowProvidersLazy teamWorkflowProvidersLazy;
protected final AtsApi atsApi;
private static final String CANCEL_HYPERLINK_URL_CONFIG_KEY = "CancelHyperlinkUrl";
protected static Set<IAtsWorkItemHook> workflowHooks = new HashSet<>();
private static Set<IAtsTransitionHook> transitionHooks = new HashSet<>();
@Override
public void addTransitionHook(IAtsTransitionHook hook) {
transitionHooks.add(hook);
}
@Override
public void addWorkItemHook(IAtsWorkItemHook hook) {
workflowHooks.add(hook);
}
public AtsWorkItemServiceImpl() {
this(null, null);
// for osgi
}
public AtsWorkItemServiceImpl(AtsApi atsApi, ITeamWorkflowProvidersLazy teamWorkflowProvidersLazy) {
this.atsApi = atsApi;
this.teamWorkflowProvidersLazy = teamWorkflowProvidersLazy;
transitionHooks.add(new DecisionReviewOnTransitionToHook());
transitionHooks.add(new PeerReviewOnTransitionToHook());
transitionHooks.add(new AtsNotificationTransitionHook());
transitionHooks.add(new AtsDecisionReviewPrepareWorkItemHook());
transitionHooks.add(new AtsForceAssigneesToTeamLeadsWorkItemHook());
transitionHooks.add(new AtsPeerToPeerReviewReviewWorkItemHook());
transitionHooks.add(new AtsCommitBranchWhenCompleteHook());
}
@Override
public IStateToken getCurrentState(IAtsWorkItem workItem) {
ArtifactId artifact = atsApi.getArtifactResolver().get(workItem);
Conditions.checkNotNull(artifact, "workItem", "Can't Find Artifact matching [%s]", workItem.toString());
return workItem.getStateDefinition();
}
@Override
public Collection<IAtsAbstractReview> getReviews(IAtsTeamWorkflow teamWf) {
ArtifactId artifact = atsApi.getArtifactResolver().get(teamWf);
Conditions.checkNotNull(artifact, "teamWf", "Can't Find Artifact matching [%s]", teamWf.toString());
return atsApi.getRelationResolver().getRelated(teamWf, AtsRelationTypes.TeamWorkflowToReview_Review,
IAtsAbstractReview.class);
}
@Override
public Collection<IAtsAbstractReview> getReviews(IAtsTeamWorkflow teamWf, IStateToken state) {
ArtifactId artifact = atsApi.getArtifactResolver().get(teamWf);
Conditions.checkNotNull(artifact, "teamWf", "Can't Find Artifact matching [%s]", teamWf.toString());
List<IAtsAbstractReview> reviews = new LinkedList<>();
for (IAtsAbstractReview review : atsApi.getRelationResolver().getRelated(teamWf,
AtsRelationTypes.TeamWorkflowToReview_Review, IAtsAbstractReview.class)) {
if (atsApi.getAttributeResolver().getSoleAttributeValue(review, AtsAttributeTypes.RelatedToState, "").equals(
state.getName())) {
reviews.add(review);
}
}
return reviews;
}
@Override
public IAtsTeamWorkflow getFirstTeam(Object object) {
Collection<IAtsTeamWorkflow> related = getTeams(object);
return related.isEmpty() ? null : related.iterator().next();
}
@Override
public Collection<IAtsTeamWorkflow> getTeams(Object object) {
List<IAtsTeamWorkflow> teams = new LinkedList<>();
if (object instanceof IAtsAction) {
for (ArtifactToken teamWfArt : atsApi.getRelationResolver().getRelated((IAtsAction) object,
AtsRelationTypes.ActionToWorkflow_TeamWorkflow)) {
teams.add(atsApi.getWorkItemService().getTeamWf(teamWfArt));
}
} else if (object instanceof ActionResult) {
return Collections.castAll(AtsObjects.getArtifacts(((ActionResult) object).getTeamWfArts()));
} else {
throw new OseeArgumentException("Unhandled object type");
}
return teams;
}
@Override
public void clearImplementersCache(IAtsWorkItem workItem) {
atsApi.clearImplementersCache(workItem);
}
@Override
public Collection<WidgetResult> validateWidgetTransition(IAtsWorkItem workItem, IAtsStateDefinition toStateDef) {
return AtsXWidgetValidateManager.validateTransition(workItem, toStateDef, atsApi);
}
@Override
public Collection<IAtsTransitionHook> getTransitionHooks() {
return transitionHooks;
}
@Override
public String getTargetedVersionStr(IAtsTeamWorkflow teamWf) {
IAtsVersion targetedVersion = atsApi.getVersionService().getTargetedVersionByTeamWf(teamWf);
if (targetedVersion != null) {
return targetedVersion.getName();
}
return "";
}
@Override
public String getArtifactTypeShortName(IAtsTeamWorkflow teamWf) {
for (ITeamWorkflowProvider atsTeamWorkflow : teamWorkflowProvidersLazy.getProviders()) {
String typeName = atsTeamWorkflow.getArtifactTypeShortName(teamWf);
if (Strings.isValid(typeName)) {
return typeName;
}
}
return null;
}
@Override
public String getCombinedPcrId(IAtsWorkItem workItem) {
String id = "";
for (ITeamWorkflowProvider provider : TeamWorkflowProviders.getTeamWorkflowProviders()) {
try {
if (provider.isResponsibleFor(workItem)) {
String computedPcrId = provider.getComputedPcrId(workItem);
if (Strings.isValid(computedPcrId)) {
id = computedPcrId;
}
}
} catch (Exception ex) {
atsApi.getLogger().error(ex, "Error with provider %s", provider.toString());
}
}
if (Strings.isInValid(id)) {
String legacyPcrId =
atsApi.getAttributeResolver().getSoleAttributeValue(workItem, AtsAttributeTypes.LegacyPcrId, "");
if (Strings.isValid(legacyPcrId)) {
return String.format("%s - %s", workItem.getAtsId(), legacyPcrId);
} else {
id = workItem.getAtsId();
}
}
return id;
}
@Override
public IAtsWorkItemNotes getNotes(IAtsWorkItem workItem) {
return new AtsWorkItemNotes(new ArtifactNote(workItem, atsApi), atsApi);
}
@Override
public ITeamWorkflowProvidersLazy getTeamWorkflowProviders() {
return teamWorkflowProvidersLazy;
}
@Override
public void clearAssignees(IAtsWorkItem workItem, IAtsChangeSet changes) {
workItem.getStateMgr().clearAssignees();
changes.add(workItem);
}
@Override
public void setAssignees(IAtsWorkItem workItem, Set<AtsUser> assignees, IAtsChangeSet changes) {
workItem.getStateMgr().setAssignees(assignees);
changes.add(workItem);
}
@Override
public IAtsWorkItem getWorkItemByAnyId(String actionId) {
IAtsWorkItem workItem = null;
ArtifactToken artifact = null;
if (GUID.isValid(actionId)) {
artifact = atsApi.getQueryService().getArtifactByGuidOrSentinel(actionId);
} else if (Strings.isNumeric(actionId)) {
artifact = atsApi.getQueryService().getArtifact(Long.valueOf(actionId));
} else {
artifact = atsApi.getQueryService().getArtifactByAtsId(actionId);
}
if (artifact.isValid()) {
workItem = atsApi.getWorkItemService().getWorkItem(artifact);
}
return workItem;
}
@Override
public Collection<IAtsWorkItem> getWorkItems(Collection<? extends ArtifactToken> artifacts) {
List<IAtsWorkItem> workItems = new LinkedList<>();
for (ArtifactToken artifact : artifacts) {
IAtsWorkItem workItem = getWorkItem(artifact);
if (workItem != null) {
workItems.add(workItem);
}
}
return workItems;
}
@Override
public IAtsWorkItem getWorkItem(Long id) {
ArtifactToken art = atsApi.getQueryService().getArtifact(id);
if (art == null || art.isInvalid() || !art.isOfType(AtsArtifactTypes.AbstractWorkflowArtifact)) {
return null;
}
return getWorkItem(art);
}
@Override
public IAtsWorkItem getWorkItem(ArtifactToken artifact) {
IAtsWorkItem workItem = null;
try {
if (artifact.isOfType(AtsArtifactTypes.TeamWorkflow)) {
workItem = getTeamWf(artifact);
} else if (artifact.isOfType(AtsArtifactTypes.PeerToPeerReview) || artifact.isOfType(
AtsArtifactTypes.DecisionReview)) {
workItem = getReview(artifact);
} else if (artifact.isOfType(AtsArtifactTypes.Task)) {
workItem = getTask(artifact);
} else if (artifact.isOfType(AtsArtifactTypes.AgileBacklog)) {
// note, an agile backlog is also a goal type, so this has to be before the goal
workItem = getAgileBacklog(artifact);
} else if (artifact.isOfType(AtsArtifactTypes.Goal)) {
workItem = getGoal(artifact);
} else if (artifact.isOfType(AtsArtifactTypes.AgileSprint)) {
workItem = getAgileSprint(artifact);
} else {
throw new OseeArgumentException("Artifact %s must be of type IAtsWorkItem", artifact.toStringWithId());
}
} catch (OseeCoreException ex) {
atsApi.getLogger().error(ex, "Error getting work item for [%s]", artifact);
}
return workItem;
}
@Override
public IAtsTeamWorkflow getTeamWfNoCache(ArtifactToken artifact) {
if (artifact.isOfType(AtsArtifactTypes.TeamWorkflow)) {
return new TeamWorkflow(atsApi.getLogger(), atsApi, artifact);
}
return null;
}
@Override
public IAtsTeamWorkflow getTeamWf(ArtifactToken artifact) {
IAtsTeamWorkflow teamWf = null;
if (artifact instanceof IAtsTeamWorkflow) {
teamWf = (IAtsTeamWorkflow) artifact;
} else if (artifact.isOfType(AtsArtifactTypes.TeamWorkflow)) {
teamWf = new TeamWorkflow(atsApi.getLogger(), atsApi, artifact);
} else {
artifact = atsApi.getQueryService().getArtifact(artifact);
teamWf = new TeamWorkflow(atsApi.getLogger(), atsApi, artifact);
}
return teamWf;
}
@Override
public String getCancelUrl(IAtsWorkItem workItem, AtsApi atsApi) {
String cancelActionUrl = atsApi.getConfigValue(CANCEL_HYPERLINK_URL_CONFIG_KEY);
if (Strings.isValid(cancelActionUrl)) {
return cancelActionUrl.replaceFirst("ID", workItem.getIdString());
}
return null;
}
@Override
public IAgileSprint getAgileSprint(ArtifactToken artifact) {
IAgileSprint sprint = null;
if (artifact instanceof IAgileSprint) {
sprint = (IAgileSprint) artifact;
} else {
sprint = new AgileSprint(atsApi.getLogger(), atsApi, artifact);
}
return sprint;
}
@Override
public IAgileBacklog getAgileBacklog(ArtifactToken artifact) {
IAgileBacklog backlog = null;
if (artifact instanceof IAgileBacklog) {
backlog = (IAgileBacklog) artifact;
} else {
backlog = new AgileBacklog(atsApi.getLogger(), atsApi, artifact);
}
return backlog;
}
@Override
public IAgileItem getAgileItem(ArtifactToken artifact) {
IAgileItem item = null;
ArtifactToken art = atsApi.getQueryService().getArtifact(artifact);
if (art.isOfType(AtsArtifactTypes.AbstractWorkflowArtifact)) {
item = new org.eclipse.osee.ats.core.agile.AgileItem(atsApi.getLogger(), atsApi, art);
}
return item;
}
@Override
public IAtsWorkItem getWorkItemByAtsId(String atsId) {
return atsApi.getQueryService().createQuery(WorkItemType.WorkItem).andAttr(AtsAttributeTypes.AtsId,
atsId).getResults().getOneOrDefault(IAtsWorkItem.SENTINEL);
}
@Override
public IAtsGoal getGoal(ArtifactToken artifact) {
IAtsGoal goal = null;
if (artifact instanceof IAtsGoal) {
goal = (IAtsGoal) artifact;
} else if (artifact.isOfType(AtsArtifactTypes.Goal)) {
goal = new Goal(atsApi.getLogger(), atsApi, artifact);
}
return goal;
}
@Override
public IAtsTask getTask(ArtifactToken artifact) {
return atsApi.getTaskService().getTask(artifact);
}
@Override
public IAtsAbstractReview getReview(ArtifactToken artifact) {
return atsApi.getReviewService().getReview(artifact);
}
@Override
public IAtsAction getAction(ArtifactToken artifact) {
IAtsAction action = null;
if (artifact instanceof IAtsAction) {
action = (IAtsAction) artifact;
} else if (artifact.isOfType(AtsArtifactTypes.Action)) {
action = new Action(atsApi, artifact);
}
return action;
}
@Override
public String getHtmlUrl(IAtsWorkItem workItem, AtsApi atsApi) {
String actionUrl = atsApi.getConfigValue("ActionUrl_26_0");
if (Strings.isValid(actionUrl)) {
return actionUrl.replaceFirst("ID", workItem.getIdString());
}
return null;
}
@Override
public boolean isCancelHyperlinkConfigured() {
return Strings.isValid(atsApi.getConfigValue(CANCEL_HYPERLINK_URL_CONFIG_KEY));
}
@Override
public Collection<IAtsTeamWorkflow> getSiblings(IAtsTeamWorkflow teamWf, IAtsTeamDefinition fromTeamDef) {
List<IAtsTeamWorkflow> siblings = new ArrayList<IAtsTeamWorkflow>();
IAtsAction action = teamWf.getParentAction();
for (IAtsTeamWorkflow child : action.getTeamWorkflows()) {
if (child.getTeamDefinition().equals(fromTeamDef)) {
siblings.add(child);
}
}
return siblings;
}
@Override
public Collection<IAtsTeamWorkflow> getSiblings(IAtsTeamWorkflow teamWf) {
List<IAtsTeamWorkflow> siblings = new ArrayList<IAtsTeamWorkflow>();
IAtsAction action = teamWf.getParentAction();
for (IAtsTeamWorkflow child : action.getTeamWorkflows()) {
if (!child.equals(teamWf)) {
siblings.add(child);
}
}
return siblings;
}
@Override
public String getChangeTypeStr(IAtsWorkItem workItem) {
return ChangeTypeUtil.getChangeTypeStr(workItem, atsApi);
}
@Override
public Collection<IAtsWorkItemHook> getWorkItemHooks() {
return workflowHooks;
}
@Override
public void removeListener(IAtsTransitionHook hook) {
transitionHooks.remove(hook);
}
@Override
public IAtsStateDefinition getStateByName(IAtsWorkItem workItem, String name) {
for (IAtsStateDefinition stateDef : workItem.getWorkDefinition().getStates()) {
if (stateDef.getName().equals(name)) {
return stateDef;
}
}
return null;
}
@Override
public TransitionResults transition(TransitionData transData) {
IAtsChangeSet changes = atsApi.createChangeSet(transData.getName(), transData.getTransitionUser());
TransitionHelper helper = new TransitionHelper(transData, changes, atsApi);
TransitionManager transitionMgr = new TransitionManager(helper);
TransitionResults results = transitionMgr.handleAllAndPersist();
return results;
}
@Override
public TransitionResults transitionValidate(TransitionData transData) {
TransitionHelper helper = new TransitionHelper(transData, null, atsApi);
TransitionManager transitionMgr = new TransitionManager(helper);
TransitionResults results = transitionMgr.handleTransitionValidation(new TransitionResults());
return results;
}
@Override
public TransitionResults transition(ITransitionHelper helper) {
helper.setAtsApi(atsApi);
TransitionManager transitionMgr = new TransitionManager(helper);
TransitionResults results = transitionMgr.handleAllAndPersist();
return results;
}
}