Friday, May 10, 2013

Restoring state of a work item to it's last state when migrating or copying it to another project programmically

When you migrating a work item from a source project to a target project, you would want that work item to retain it's last state and reason. But you can't directly assign the last state to the migrated work item unless that state can be reached from the begin state by one transition. Because when migrating a work item what always happens is creating a new work item and assign field values of the source work item to new one. Therefore target work item's state will be the first state of it's work flow (You can see this by checking from the workflow of that work item type). So if you want to assign the last state, you have to traverse the work item through it's work flow to it's final state. As work item can perform one transition for once, you will need to save work item between transitions.

The other problem is how you can find a path between from first state to last state. one way to do this is querying work flow and trying to find out next states reachable from current states and move along the way. But this is tricky as you might find yourself in a closed corner and have to backtrack to find another path. But there is an easy way to get the required state transitions, as source work item is available for us. We can get  all the state transitions of a work item by work item history. therefore we get state transitions from source work item and replicate them in the target work item.


Code:

public void writeWorkItems(WorkItemCollection workItemCollection)
        {
        //creating new workitems for source work items
foreach (WorkItem workItem in workItemCollection)
            {

  WorkItem newWorkItem = null;
newWorkItem = new WorkItem(workItemTypes[workItem.Type.Name]);
------
-----
updateToLatestStatus(workItem, newWorkItem);
newWorkItem.Save();
   }
}

public void updateToLatestStatus(WorkItem oldWorkItem, WorkItem newWorkItem)

        {
            Queue<string> result = new Queue<string>();
            string previousState = null;
            string originalState = (string)newWorkItem.Fields["State"].Value;
            string sourceState = (string)oldWorkItem.Fields["State"].Value;
            string sourceFinalReason = (string)oldWorkItem.Fields["Reason"].Value;

            //try to change the status directly
            newWorkItem.Open();
            newWorkItem.Fields["State"].Value = oldWorkItem.Fields["State"].Value;
            //System.Diagnostics.Debug.WriteLine(newWorkItem.Type.Name + "      " + newWorkItem.Fields["State"].Value);

            //if status can't be changed directly... 
            if (newWorkItem.Fields["State"].Status != FieldStatus.Valid)
            {
                //get the state transition history of the source work item.
                foreach (Revision revision in oldWorkItem.Revisions)
                {
                    // Get Status          
                    if (!revision.Fields["State"].Value.Equals(previousState))
                    {
                        previousState = revision.Fields["State"].Value.ToString();
                        result.Enqueue(previousState);
                    }

                }

                int i = 1;
                previousState = originalState;
                //traverse new work item through old work items's transition states
                foreach (String currentStatus in result)
                {
                    bool success = false;
                    if (i != result.Count)
                    {
                        success = ChangeWorkItemStatus(newWorkItem, previousState, currentStatus);
                        previousState = currentStatus;
                    }
                    else
                    {
                        success = ChangeWorkItemStatus(newWorkItem, previousState, currentStatus, sourceFinalReason);
                    }
                    i++;
                    // If we could not do the incremental state change then we are done.  We will have to go back to the orginal...
                    if (!success)
                        break;
                }
            }
            else
            {
                // Just save it off if we can.
                bool success = ChangeWorkItemStatus(newWorkItem, originalState, sourceState);
            }
        }

        private bool ChangeWorkItemStatus(WorkItem workItem, string orginalSourceState, string destState)
        {
            //Try to save the new state.  If that fails then we also go back to the orginal state.
            try
            {
                workItem.Open();
                workItem.Fields["State"].Value = destState;
                workItem.Save();
                return true;
            }
            catch (Exception)
            {
                logger.Info(String.Format("Failed to save state for workItem : {0}  type:{1} state from {2} to {3}", workItem.Id, workItem.Type.Name, orginalSourceState, destState));
                logger.Info(String.Format("rolling workItem status to original state {0}", orginalSourceState));
                //Revert back to the original value.
                workItem.Fields["State"].Value = orginalSourceState;
                return false;
            }
        }

        //save final state transition and set final reason.
        private bool ChangeWorkItemStatus(WorkItem workItem, string orginalSourceState, string destState, string reason)
        {
            //Try to save the new state.  If that fails then we also go back to the orginal state.
            try
            {
                workItem.Open();
                workItem.Fields["State"].Value = destState;
                workItem.Fields["Reason"].Value = reason;

                ArrayList list = workItem.Validate();
                workItem.Save();

                return true;
            }
            catch (Exception)
            {
                logger.Info(String.Format("Failed to save state for workItem: {0}  type:{1} state from {2} to {3}", workItem.Id.ToString(), workItem.Type.Name, orginalSourceState, destState));
                logger.Info(String.Format("rolling workItem status to original state {0}", orginalSourceState));
                //Revert back to the original value.
                workItem.Fields["State"].Value = orginalSourceState;
                return false;
            }
        }




No comments:

Post a Comment