// UC 6: Create a TR based on a Bug. // Return if the bug is updated or not. private void createTR( Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItem workItem, WorkItemChangedEvent notification, String user, ref Status status) { // Create the ECR callCreateTR(ECRMapper.mapFromWorkitem(workItem, null, ECRMapper.ACTION_CREATE), user, ref status); if (!status.OK) { return; } // UC 3 (one case): Update all changed attributes before handling any state change // Note: If adding an entry in History on create this should propagate to Progress Info, but // this caused Bug to appear dirty after Save, see issue 79. Given almost all fields are used on // create, it does not make sense to make extra update as below. TBD if we should re-add in a // changed form. //EnterpriseChangeRequest updatedEcr = ECRMapper.mapFromUpdatedWorkitem(workItem, status.TRAbout, notification); //if (updatedEcr != null) //{ // // Note: Failure to update will be Warning not Error, so status still OK. // callUpdateTR(updatedEcr, user, null, ref status); //} // Set the ECR in state Registered EnterpriseChangeRequest ecr = ECRMapper.mapFromWorkitem(workItem, status.TRAbout, ECRMapper.ACTION_REGISTER_ROUTE); callUpdateTR(ecr, user, ECRMapper.TR_STATE_REGISTERED_S, ref status); if (!status.OK) { return; } // Set the ECR in state Assigned if we have a defined owner ecr = ECRMapper.mapFromWorkitem(workItem, status.TRAbout, ECRMapper.ACTION_ASSIGN); if (ecr.GetOwner() != null && ecr.GetOwner().Length > 0) { callUpdateTR(ecr, user, ECRMapper.TR_STATE_ASSIGNED_S, ref status); if (!status.OK) { return; } } if (status.OK) { HandlerSettings.LogMessage( String.Format("Created TR based on workitem named: {0}", workItem.Title), HandlerSettings.LoggingLevel.INFO); } }
// Return if the workitem is updated (saved) by the handling code. public void handleEvent( Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItem workItem, String user, WorkItemChangedEvent notification) { Status status = new Status(); // If the user is the OSLC Provider functional user we will only update status // message if so needed. This as changes from the OSLC Provider originates from // external source = currently: MHWeb. Need revisit to allow multiple clients. String aboutStr = AttributesMapper.GetTfsValueForEcrKey(TFSMapper.ECM_ABOUT, workItem); if (!user.Equals(HandlerSettings.TFSProviderUser, StringComparison.OrdinalIgnoreCase)) { if (aboutStr.Length == 0) { // We do not have a linked TR, we need to create createTR(workItem, notification, user, ref status); } else { Uri about = new Uri(aboutStr); Status statusAssign = null; EnterpriseChangeRequest ecr = ECRMapper.mapFromWorkitem(workItem, about, ECRMapper.ACTION_ASSIGN); TeamFoundationIdentity assignedTo = HandlerSettings.GetSignumForAssignedTo(workItem); if (user != null && assignedTo != null && !user.Equals(HandlerSettings.GetUserFromSignum(assignedTo.UniqueName), StringComparison.OrdinalIgnoreCase)) { statusAssign = new Status(); ecr.SetOwner(user); callUpdateTR(ecr, user, ECRMapper.TR_STATE_ASSIGNED_S, ref statusAssign); } // We have a TR linked which might need to be updated updateTR(workItem, notification, user, about, ref status); if (statusAssign != null && statusAssign.OK) { ecr.SetOwner(HandlerSettings.GetUserFromSignum(assignedTo.UniqueName)); callUpdateTR(ecr, user, ECRMapper.TR_STATE_ASSIGNED_S, ref statusAssign); } } } // Handle update of Bug ECRMapper.updateBug(status, user, workItem); }
// ========================================================= // Handle the WorkItemChangedEvent event // Disconnect TR if conditions are matching. Return if workitem has been saved. public void handleDisconnectEvent( Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItem workItem, String user, WorkItemChangedEvent notification) { Uri about = ECRMapper.getDisconnectedTRLink(workItem, notification); if (about == null) { return; } EnterpriseChangeRequest ecr = ECRMapper.mapFromWorkitem( workItem, about, ECRMapper.ACTION_DISCONNECT); Status status = new Status(); callUpdateTR(ecr, user, null, ref status); if (!status.OK) { HandlerSettings.LogMessage( "Failed to disconnect TR from Bug.", HandlerSettings.LoggingLevel.WARN); } }
// Update the connected TR State. Return if the bug is updated or not. private void updateTRState( Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItem workItem, String oldState, String newState, String user, Uri about, ref Status status) { status.TRState = ECRMapper.getTRState(workItem); if (oldState.Equals(ECRMapper.BUG_STATE_ACTIVE, StringComparison.OrdinalIgnoreCase) && newState.Equals(ECRMapper.BUG_STATE_RESOLVED, StringComparison.OrdinalIgnoreCase)) { // UC 4: Updated TR based on the Bug's Active -> Resolve state change EnterpriseChangeRequest ecr = null; if (status.TRState.Equals(ECRMapper.TR_STATE_ASSIGNED)) { ecr = ECRMapper.mapFromWorkitem(workItem, about, ECRMapper.ACTION_PROPOSE); callUpdateTR(ecr, user, ECRMapper.TR_STATE_PROPOSED_S, ref status); if (!status.OK) { return; } } if (status.TRState.Equals(ECRMapper.TR_STATE_PROPOSED)) { String answerCode = ""; if (ecr != null) { answerCode = ecr.GetAnswerCode(); } else { answerCode = AttributesMapper.GetTfsValueForEcrKey(TFSMapper.ECM_ANSWER_CODE, workItem); } if (answerCode.Contains("A") || answerCode.Contains("D") || answerCode.Contains("B11")) { // In case Answer Code = A*, D*, or B11 go directly to Answer callUpdateTR(ECRMapper.mapFromWorkitem(workItem, about, ECRMapper.ACTION_ANSWER), user, ECRMapper.TR_STATE_TECH_ANSW_PROV_S, ref status); return; } else { callUpdateTR(ECRMapper.mapFromWorkitem(workItem, about, ECRMapper.ACTION_APPROVE), user, ECRMapper.TR_STATE_PROP_APPROV_S, ref status); } } String expectedState = ECRMapper.TR_STATE_PROP_APPROV; if (!status.TRState.Equals(expectedState)) { // Incorrect pre condition HandlerSettings.LogMessage( String.Format("Expected TR State: {0}, current TR state: {1}", expectedState, status.TRState), HandlerSettings.LoggingLevel.WARN); } // Note: Here we assume we are in trState PA, as we accepted AS, PP and PA. If we have not failed // before, this is where we will fail if any assumption was wrong -> error message. if (status.OK) { callUpdateTR(ECRMapper.mapFromWorkitem(workItem, about, ECRMapper.ACTION_VERIFY), user, ECRMapper.TR_STATE_TECH_ANSW_PROV_S, ref status); } } else if (oldState.Equals(ECRMapper.BUG_STATE_RESOLVED, StringComparison.OrdinalIgnoreCase) && newState.Equals(ECRMapper.BUG_STATE_CLOSED, StringComparison.OrdinalIgnoreCase)) { // Update a TR based on Close of the Bug's Resolve -> Close state change String expectedState = ECRMapper.TR_STATE_TECH_ANSW_PROV; if (!status.TRState.Equals(expectedState)) { // Incorrect pre condition HandlerSettings.LogMessage( String.Format("Expected TR State: {0}, current TR state: {1}", expectedState, status.TRState), HandlerSettings.LoggingLevel.WARN); } // TODO: Put the condition in configuration file etc. String answerCode = AttributesMapper.GetTfsValueForEcrKey(TFSMapper.ECM_ANSWER_CODE, workItem); if (answerCode.Equals("D4")) { // UC 9.3 - Bug set as Postponed callUpdateTR(ECRMapper.mapFromWorkitem(workItem, about, ECRMapper.ACTION_ACCEPT), user, ECRMapper.TR_STATE_POSTPONED_S, ref status); } else if (answerCode.Equals("Duplicate")) { // UC 9.4 - Bug set as Duplicate callUpdateTR(ECRMapper.mapFromWorkitem(workItem, about, ECRMapper.ACTION_DUPLICATE), user, null, ref status); } else { // UC 9.1 - Close of Bug from TFS callUpdateTR(ECRMapper.mapFromWorkitem(workItem, about, ECRMapper.ACTION_FINISH), user, ECRMapper.TR_STATE_FINISHED_S, ref status); } } else if (oldState.Equals(ECRMapper.BUG_STATE_RESOLVED, StringComparison.OrdinalIgnoreCase) && newState.Equals(ECRMapper.BUG_STATE_ACTIVE, StringComparison.OrdinalIgnoreCase)) { // UC 9.2: Update a TR based the Bug's Resolved -> Active state change callUpdateTR(ECRMapper.mapFromWorkitem(workItem, about, ECRMapper.ACTION_REJECT), user, ECRMapper.TR_STATE_REGISTERED_S, ref status); if (!status.OK) { return; } // If Bug is assigned to a user we need to drive change back to Assigned state EnterpriseChangeRequest ecr = ECRMapper.mapFromWorkitem(workItem, about, ECRMapper.ACTION_ASSIGN); if (ecr.GetOwner() != null && ecr.GetOwner().Length > 0) { callUpdateTR(ecr, user, ECRMapper.TR_STATE_ASSIGNED_S, ref status); } } else if (oldState.Equals(ECRMapper.BUG_STATE_CLOSED, StringComparison.OrdinalIgnoreCase) && newState.Equals(ECRMapper.BUG_STATE_ACTIVE, StringComparison.OrdinalIgnoreCase)) { // UC 9.2: Update a TR based the Bug's Closed -> Active state change // In case of Closed -> Active, this is only allowed for the "no action" answer codes // in MHWeb, so OK for this to fail if selecting the incorrect answer code. // Could block in Bug.xml if we want to prevent some cases. callUpdateTR(ECRMapper.mapFromWorkitem(workItem, about, ECRMapper.ACTION_REACTIVATE), user, ECRMapper.TR_STATE_REGISTERED_S, ref status); if (!status.OK) { return; } // If Bug is assigned to a user we need to drive change back to Assigned state EnterpriseChangeRequest ecr = ECRMapper.mapFromWorkitem(workItem, about, ECRMapper.ACTION_ASSIGN); if (ecr.GetOwner() != null && ecr.GetOwner().Length > 0) { callUpdateTR(ecr, user, ECRMapper.TR_STATE_ASSIGNED_S, ref status); } } else if (oldState.Equals(ECRMapper.BUG_STATE_ACTIVE, StringComparison.OrdinalIgnoreCase) && newState.Equals(ECRMapper.BUG_STATE_ACTIVE, StringComparison.OrdinalIgnoreCase)) { // UC 3 (one case): Handle Assign case - state change for TR, attribute change for Bug // Incorrect pre condition String expectedState = ECRMapper.TR_STATE_REGISTERED; if (!status.TRState.Equals(expectedState)) { HandlerSettings.LogMessage( String.Format("Expected TR State: {0}, current TR state: {1}", expectedState, status.TRState), HandlerSettings.LoggingLevel.WARN); } // Handle case when we are in state PR if (status.TRState.Equals(ECRMapper.TR_STATE_PRIVATE)) { callUpdateTR(ECRMapper.mapFromWorkitem( workItem, about, ECRMapper.ACTION_REGISTER_ROUTE), user, ECRMapper.TR_STATE_REGISTERED_S, ref status); } callUpdateTR(ECRMapper.mapFromWorkitem( workItem, about, ECRMapper.ACTION_ASSIGN), user, ECRMapper.TR_STATE_ASSIGNED_S, ref status); } }
// Update the connected TR. private void updateTR( Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItem workItem, WorkItemChangedEvent notification, String user, Uri about, ref Status status) { String oldState, newState, action; ECRMapper.getStatusChange(notification, workItem, out oldState, out newState, out action); if (action != null) { if (action.Equals(ECRMapper.ACTION_IGNORE)) { // Ignore all changes return; } if (action.Equals(ECRMapper.ACTION_DUPLICATE)) { // UC 9.4: Duplicate - Find Bug with duplicate id, get attached TR and send action duplicate // Update needs to be done before setting the Bug as Duplicate as the TR can't change after. updateTRFields(workItem, notification, user, about, ref status); EnterpriseChangeRequest ecr = ECRMapper.mapFromWorkitem(workItem, about, ECRMapper.ACTION_DUPLICATE); if (ecr.GetPrimaryTR() == null || ecr.GetPrimaryTR().Length == 0) { // Failed to find a Bug with a connected TR. Log error and return. String msg = String.Format( "Failed to find duplicate to Bug: {0}, or duplicate Bug had not TR Link set", workItem.Title); HandlerSettings.LogMessage(msg, HandlerSettings.LoggingLevel.ERROR); status.ErrorMessage = msg; return; } callUpdateTR(ecr, user, null, ref status); if (!status.OK) { // Log issue, but continue if e.g. a state change HandlerSettings.LogMessage( String.Format("Failed to set TR as duplicate based on Bug: {0}", workItem.Title), HandlerSettings.LoggingLevel.WARN); } // After setting Bug/TR as Duplicated, no futher changes should be propagated. return; } if (action.Equals(ECRMapper.ACTION_UNDUPLICATE)) { // UC 9.4: Unduplicate - Move TR back to Registered by sending action unduplicate EnterpriseChangeRequest ecr = ECRMapper.mapFromWorkitem(workItem, about, ECRMapper.ACTION_UNDUPLICATE); callUpdateTR(ecr, user, null, ref status); if (!status.OK) { // Log issue, but continue if e.g. a state change HandlerSettings.LogMessage( String.Format("Failed to unduplicate TR based on Bug ", workItem.Title), HandlerSettings.LoggingLevel.WARN); } // Update needs to be done after "Unduplicating" the Bug as the TR can't change before. updateTRFields(workItem, notification, user, about, ref status); // Return before handling a possible state change as states can be out of sync // (normal case) when Bug has been Duplicate. Bug then Resolved / Closed, and // TR in an Active state. User needs to set TR state to Active. return; } else if (action.Equals(ECRMapper.ACTION_CHANGE_PRODUCT)) { // UC 10.2: Update of the release - propagate info to TR EnterpriseChangeRequest ecr = ECRMapper.mapFromWorkitem(workItem, about, ECRMapper.ACTION_CHANGE_PRODUCT); bool noProduct = ecr.GetProduct().Length == 0; if (noProduct) { // No product found, so value is for internal release - add message in notebook ecr.SetNotebook("The referenced design item was moved to a product internal to Design"); } callUpdateTR(ecr, user, null, ref status); if (!status.OK) { // Log issue, but continue if e.g. a state change HandlerSettings.LogMessage( String.Format("Failed to change product on TR based on Bug ", workItem.Title), HandlerSettings.LoggingLevel.WARN); } } } updateTRFields(workItem, notification, user, about, ref status); if (newState != null) { // Handle state change updateTRState(workItem, oldState, newState, user, about, ref status);; } }