Advanced examples

Advanced Rule examples #

Backlog work items: Auto-activate parent #

This is a more advanced version which has no hard coded work item type names. It moves a parent work item to active state (activates parent), if a child gets activated and both parent and child are backlog work items

//Method to check if a 'workItem' is in a 'Progress' state
bool IsInProgress(WorkItemWrapper workItem, BacklogWorkItemTypeStates workItemType)
{
    var concreteStateNames = workItemType?.StateCategoryStateNames
                                            .Where(category => string.Equals("InProgress", category.Key, StringComparison.OrdinalIgnoreCase))
                                            .SelectMany(category => category.Value);

    return concreteStateNames?.Contains(workItem.State) ?? false;
}

// First simple check if there is a parent
var parentWorkItem = self.Parent;
if (parentWorkItem == null)
{
    return "No Parent";
}

// now get the backlog work item types with their state to category mapping
var backlogWorkItems = await store.GetBacklogWorkItemTypesAndStates();
var backlogWorkItemsLookup = backlogWorkItems.ToDictionary(itemType => itemType.Name, itemType => itemType);

// Check if we are a back log work item and we are in an InProgress state
var workItemType = backlogWorkItemsLookup.GetValueOrDefault(self.WorkItemType);
if (!IsInProgress(self, workItemType))
{
    return workItemType == null ? "No Backlog work item type" : $"work item not <InProgress> (State={self.State})";
}

// Check if parent already in progress state and a back log work
var parentWorkItemType = backlogWorkItemsLookup.GetValueOrDefault(parentWorkItem.WorkItemType);
if (IsInProgress(parentWorkItem, parentWorkItemType))
{
    return parentWorkItemType == null ? "No Backlog work item type" : $"work item already <InProgress> (State={parentWorkItem.State})";
}

// Now set the parent to a state of the InProgress category
parentWorkItem.State = parentWorkItemType.StateCategoryStateNames["InProgress"].First();
return $"updated Parent {parentWorkItem.WorkItemType} #{parentWorkItem.Id} to State='{parentWorkItem.State}'";

Backlog work items: Auto-Resolve parent #

This is more similar to classic TFS Aggregator. It moves a parent backlog work item to Resolved state, if all children are closed or terminated.

//base method to check state
bool IsBacklogWorkItemInState(WorkItemWrapper workItem, BacklogWorkItemTypeStates workItemType, IEnumerable<string> expectedStateCategories)
{
    bool IsInExpectedStateCategory(KeyValuePair<string, string[]> category)
    {
        return expectedStateCategories.Any(expectedStateCategroy => string.Equals(expectedStateCategroy, category.Key, StringComparison.OrdinalIgnoreCase));
    }

    var concreteStateNames = workItemType?.StateCategoryStateNames
                                            .Where(IsInExpectedStateCategory)
                                            .SelectMany(category => category.Value);


    return concreteStateNames?.Contains(workItem.State) ?? false;
}

//Method to check if a 'workItem' is in a 'Removed' or 'Completed' state
bool IsRemovedOrCompleted(WorkItemWrapper workItem, BacklogWorkItemTypeStates workItemType)
{
    var expectedStateCategories = new string[]
                                {
                                    "Completed",
                                    "Removed",
                                };

    return IsBacklogWorkItemInState(workItem, workItemType, expectedStateCategories);
}


var parentWorkItem = self.Parent;
if (parentWorkItem == null)
{
    return "No Parent";
}

var backlogWorkItems = await store.GetBacklogWorkItemTypesAndStates();
var backlogWorkItemsLookup = backlogWorkItems.ToDictionary(itemType => itemType.Name, itemType => itemType);

var workItemType = backlogWorkItemsLookup.GetValueOrDefault(self.WorkItemType);
if (!IsRemovedOrCompleted(self, workItemType))
{
    return workItemType == null ? "No Backlog work item type" : $"work item not <Removed> or <Completed> (State={self.State})";
}

var parentWorkItemType = backlogWorkItemsLookup.GetValueOrDefault(parentWorkItem.WorkItemType);
if (IsRemovedOrCompleted(parentWorkItem, parentWorkItemType))
{
    return parentWorkItem == null ? "No Backlog work item type" : $"work item already <Removed> or <Completed> (State={parentWorkItem.State})";
}

if (!parentWorkItem.Children.All(item => IsRemovedOrCompleted(item, backlogWorkItemsLookup.GetValueOrDefault(item.WorkItemType))))
{
    return $"Not all child work items <Removed> or <Completed>: {string.Join(",", parentWorkItem.Children.Select(item => $"#{item.Id}={item.State}"))}";
}

var progressStates = parentWorkItemType.StateCategoryStateNames["InProgress"];
parentWorkItem.State = progressStates.Last();
return $"updated Parent #{parentWorkItem.Id} to State='{parentWorkItem.State}'";