Jazz Forum Welcome to the Jazz Community Forum Connect and collaborate with IBM Engineering experts and users

What other options do I have to setWorkflowAction other than relying on WorkitemOperation?

The javadoc API says:

commit(WorkItemWorkingCopy[] workingCopies, org.eclipse.core.runtime.IProgressMonitor monitor) 
          Commits the operation for the given working copies.

But I don't see any examples in the Jazz biosphere that actually use it.  I'm having a problem where my code to update a workitem runs with (apparently) no issue, but I'm not seeing the updates reflected on the work items.

Specifically, I'm trying to extend WorkItemOperation to change the status of a custom work item type.  I can change other fields using the same code but when I try to change the workflow action, it doesn't work.  Here is what I'm using:

public class UpdateWorkItemStatus implements WorkitemResultProcessor {
private static Logger log = LoggerFactory
.getLogger(UpdateWorkItemStatus.class.getName());
String newaction ="";
IProgressMonitor monitor = null;
IProjectArea project_area = null;

public UpdateWorkItemStatus(IProjectArea pa,String statusaction,IProgressMonitor mon){
project_area = pa;
monitor = mon;
newaction = statusaction;
mon.subTask("Preparing to update");
}

public IProgressMonitor getMonitor() {
return monitor;
}
public String getNewaction() {
return newaction;
}
public void execute(IWorkItemHandle wh) throws RtcException {
StatusAssign chgstatus = new StatusAssign(newaction);
try {
log.trace("Creating StatusAssign object to update status");
chgstatus.run(wh,monitor);
} catch (TeamRepositoryException e) {
throw new RtcException("Problem setting status to '"+getNewaction()+"': "+e.getMessage());
}
}

class StatusAssign extends WorkItemOperation {
String newstatus_action = "";
public StatusAssign(String action) {
super("Updating workitem...", IWorkItem.FULL_PROFILE);
newstatus_action = action;
}
public StatusAssign(String name, ItemProfile profile) {
super(name, profile);
}
@Override
protected void execute(WorkItemWorkingCopy workingCopy,IProgressMonitor monitor) throws TeamRepositoryException {
String msg = String.format("Updating workitem %s: setting status to '%s'",workingCopy.getWorkItem().getId(),newstatus_action);
monitor.subTask(msg);
log.debug("Current workflow action is {}",workingCopy.getWorkflowAction());
                        // this workflow action does not stick
workingCopy.setWorkflowAction(newstatus_action);
// this part works
Timestamp testdate = new Timestamp(new Date().getTime());
workingCopy.getWorkItem().setDueDate(testdate);
                        // this part works, too
workingCopy.getWorkItem().setTarget(IterationHelper.getIteration(
(ITeamRepository) project_area.getOrigin(), (IProjectArea) project_area.getFullState()
, "HP MBE O 2013 11 04"));
}
}
}
Because it hasn't been working, I've been doing more and more arcane things trying to get it to work, including the forbidden setState2 and now wondering what the commit is for.  If the problem is that I'm doing something wrong, then my original question is wrong! 


Any ideas?


- Andy

0 votes

Comments

How are you setting the value for the work item??

If you set the value in the OperationParticipant then then the values will not be persisted.

Sorry, I'm using the plain java API.  I will add more details to the question. 


Accepted answer

Permanent link
Won't fit in a comment. This would be another approach:
	private static String changeStatus(ITeamRepository repo,
			Integer workItemId, Boolean buildStatus, String actionOK,
			String actionKO, IProgressMonitor monitor)
			throws TeamRepositoryException {

		IWorkItemClient workItemClient = (IWorkItemClient) repo
				.getClientLibrary(IWorkItemClient.class);
		IWorkItemCommon workItemCommon = (IWorkItemCommon) repo
				.getClientLibrary(IWorkItemCommon.class);

		IWorkItem wi = workItemClient.findWorkItemById(workItemId,
				IWorkItem.FULL_PROFILE, monitor);
		if (null == wi) {
			return ("work item " + workItemId + " cannot be found");
		}
		IDetailedStatus status = null;
		String actionName = buildStatus ? actionOK : actionKO;
		IWorkItemWorkingCopyManager copyManager = workItemClient
				.getWorkItemWorkingCopyManager();

		try {
			copyManager.connect(wi, IWorkItem.FULL_PROFILE, monitor);
			WorkItemWorkingCopy wc = copyManager.getWorkingCopy(wi);
			// wc.getWorkItem().getsetState2(arg0)
			IWorkflowInfo wfInfo = (IWorkflowInfo) workItemClient
					.findWorkflowInfo(wi, monitor);
			Identifier[] actionList = wfInfo.getActionIds(wi
					.getState2());
			String actionId = null;
			for (int i = 0; i < actionList.length; i++) {
				Identifier action = actionList[i];
				String name = wfInfo.getActionName(action);
				if (name != null) {
					if (name.equals(actionName)) {
						actionId = action.getStringIdentifier();
						break;
					}
				}
			}
			if (null == actionId) {
				wc.setWorkflowAction(null);
			} else {
				wc.setWorkflowAction(actionId);
				// wc.setWorkflowAction(null);
			}
			status = wc.save(monitor);
			if (status.getCode() != org.eclipse.core.runtime.IStatus.OK) {
				return "Error: " + status.getDetails();
			}
		} catch (Exception e) {
			return "Unexpected error: " + e.getMessage();
		} finally {
			copyManager.disconnect(wi);
		}
		return null;
	}
Andy Jewell selected this answer as the correct answer

0 votes

Comments

Thanks, Ralph, will try that right now.  On thing I've been wondering about (since setWorkflowAction(string) was not working for me), is the action name the simple/display name of the action or is it some other underlying stringified name of the action Identifier? 

I would suggest to read https://rsjazz.wordpress.com/2012/11/26/manipulating-work-item-states/ to understand how that works and how to get the action.

Yes, I have that read but actually you can't tell from the article nor any other examples I've read what the literal action name is that's being passed.


However, it's a moot point because your last sample worked for me!  I'm a bit disappointed because the WorkitemOperation approach is so much cleaner but the nice thing about this approach is. .. that it works!  Really appreciate your help, Ralph!!

As far as I can see from jumping through my blog entry it is the ID of the action. That is not necessarily the same as the display name (as usually) especially for the shipped templates. You would have to look into the process XML, unfortunately. Here an example screen shot:



I am not sure if I created the path helper you can download on the blog to accept the name instead. should be easy enough to to though. Please also note, the action will only work if you are in a state where this action is valid.

1 vote

If the last code snippet works, the operation should also work. The code below is (if you look at the WorkitemOpetration source code the same code essentially WorkItemOperation runs. The first example is, I believe, a work item operation that does exactly what you want. I think I created it as an example for exactly that purpose. If it does not work, there can be reasons as
- WorkflowAction not found (passed the name not the ID)
- WorkflowAction not applicable in this state
- Another operational behavior that prevents you from changing the state.

I would be surprised if there are other reasons why it should not work.

This is great, info, thank you.  I guess this code works because of the looping done to associate the action name with the action identifier whereas in my original WorkItemOperation code, I was just passing the action name.  I will try updating the WorkItemOperation code with the ID and see if it works.

 Bahdaboom, that worked :)  I'm going to file a documentation bug on this, that was a bit frustrating but thanks for your help!

Why would you? This is all over the place. You can never use the name of anything. You have to find the ID. Enumeration Literals https://rsjazz.wordpress.com/2012/08/20/manipulationg-work-item-enumeration-values/ attributes https://rsjazz.wordpress.com/2013/01/02/working-with-work-item-attributes/, wherever you look at.

One of the reasons is the API is the API used for developing the client or the server and they know the ID's and only display the display name for convenience.

I disagree, having the API reader assume a string argument is an ID, a name, a fully resolve classname, an alias, or whatever other string type is obvious is not a good practice for someone writing an API.  It's a simple change and has already been made.

Well, you can create your enhancement request.

However, as far as I am aware, the API is not explicitely written as an API for an external user, it is the internal API exposed. And in that case display names, that can be in Chinese, or German or French, or any other language, and that can be changed by the user, are not a good, reliable identifier. Therefore other identifiers, that are more constant are used all over the place. There are other issues with identifiers that don't necessarily have to be unique as well.

showing 5 of 10 show 5 more comments

3 other answers

Permanent link
Andy, looking at it, the commit operation basically is one of the last steps in the WorkItemOperation and it saves the work item working-copies. That is about it.

If you want to change the state of the work item,you can provide the work item with a workflowAction ( .setWorkFlowAction). See http://rsjazz.wordpress.com/2012/11/26/manipulating-work-item-states/ for more details. There is a deprecated method .setState2() on IWorkItem that you could use to directly set a state. However, that is a bit unsafe, because you could trigger a state change that does not exist and it might also avoid all the behavior defined related to the workflow.

1 vote

Comments

Yes, thanks, Ralph, I am using setWorkflowAction but unfortunately, it's not working and I can't figure out why.  I'll have to try setState2.

Where do you use the setWorkflowAction in the operation? This code used to work for me:

    private static class WorkItemSetWorkflowActionModification extends
            WorkItemOperation {

    private String fWorkFlowAtion;

    public WorkItemSetWorkflowActionModification(String workFlowAtion) {
        super("Modifying Work Item State", IWorkItem.FULL_PROFILE);
        fWorkFlowAtion = workFlowAtion;
    }

    @Override
    protected void execute(WorkItemWorkingCopy workingCopy,
            IProgressMonitor monitor) throws TeamRepositoryException {
        workingCopy.setWorkflowAction(fWorkFlowAtion);
    }

    public void setfWorkFlowAtion(String fWorkFlowAtion) {
        this.fWorkFlowAtion = fWorkFlowAtion;
    }
}


Permanent link
Andy, it would help if you describe what and where (client API/Server API) you are trying to use the API.

I only implicitly use that API. On the client I usually use a WorkItemOperation that overwrites some method as described for example here: https://rsjazz.wordpress.com/2012/08/01/uploading-attachments-to-work-items/ or here https://jazz.net/wiki/bin/view/Main/ProgrammaticWorkItemCreation. I think that eventually uses the commit() method.

In the server API I use typically saveWorkItem2() or saveWorkItem3() as described in e.g. this blog: https://rsjazz.wordpress.com/2012/07/31/rtc-update-parent-duration-estimation-and-effort-participant/

0 votes

Comments

Yes, those articles are pretty much the basis of everything I've done... :)  Will add more details to the original question. 


Permanent link
 Updated WorkitemOperation that finds the correct action identifier based on the passed name.  Not using the path as Ralph mentions above, just brute force:
@Override
protected void execute(WorkItemWorkingCopy workingCopy,IProgressMonitor monitor) throws TeamRepositoryException {
    String msg = String.format("Updating workitem %s: setting status to '%s'",workingCopy.getWorkItem().getId(),newstatus_action);
    monitor.subTask(msg);
    log.debug("Current workflow action is {}",workingCopy.getWorkflowAction());
    ITeamRepository repo = (ITeamRepository) workingCopy.getWorkItem().getOrigin();
    IWorkItemClient client = (IWorkItemClient) repo.getClientLibrary(IWorkItemClient.class);
    IWorkflowInfo wf = client.findWorkflowInfo(workingCopy.getWorkItem(), monitor);
    Identifier<IWorkflowAction>[] actionList = wf.getActionIds(workingCopy.getWorkItem().getState2());
    String actionId = null;
    for (int i = 0; i < actionList.length; i++) {
    Identifier<IWorkflowAction> action = actionList[i];
    String action_name = wf.getActionName(action);
    if (action_name != null) {
      if (action_name.equals(newstatus_action)) {
      actionId = action.getStringIdentifier();
      break;
      }
    } else {
      log.error("identifier for action {} was null",newstatus_action);
    }
    }
    workingCopy.setWorkflowAction(actionId);
  }
}

0 votes

Your answer

Register or log in to post your answer.

Dashboards and work items are no longer publicly available, so some links may be invalid. We now provide similar information through other means. Learn more here.

Search context
Follow this question

By Email: 

Once you sign in you will be able to subscribe for any updates here.

By RSS:

Answers
Answers and Comments
Question details
× 10,927
× 169

Question asked: Jan 07 '14, 11:49 a.m.

Question was seen: 5,435 times

Last updated: Jan 14 '14, 11:05 a.m.

Confirmation Cancel Confirm