Hi everyone,
I wanted to share an approach that can be used when time tracking information is stored on child Tasks in Azure DevOps, but needs to be synchronized to a parent work item and ultimately sent to Jira via Exalate.
Overview
In many Azure DevOps projects, users log time against child Tasks rather than against the parent User Story or PBI. Since the parent work item does not automatically contain the aggregated time spent, we may need to retrieve this information manually using Azure DevOps REST APIs.
One possible approach is to use Exalate’s httpClient() to inspect the work item’s relationships, identify its child Tasks, retrieve their details, and calculate the total time spent.
Retrieving Work Item Information
For example, the following call can be used to retrieve information related to a work item:
def res = httpClient.get("/Demo1/_apis/wit/workItems/${workItem.id}/revisions", true)
debug.error("${res}")
Note: Replace Demo1 with your Azure DevOps project name. If the project name contains spaces, replace them with %20.
Depending on the use case, you may also retrieve the work item itself and inspect its relations section.
Identifying Child Work Items
The returned payload contains relationship information that can be parsed to identify links such as:
-
Parent
-
Child
-
Related
-
Successor
-
Predecessor
For a parent work item, look for links of type:
System.LinkTypes.Hierarchy-Forward
These typically represent child work items.
Example structure:
{
"rel": "System.LinkTypes.Hierarchy-Forward",
"url": "https://dev.azure.com/.../workItems/12345"
}
From these links, the child Task IDs can be extracted.
Retrieving Child Task Details
Once the child IDs are known, additional REST API calls can be made to retrieve each child Task.
The response can then be inspected for fields such as:
Microsoft.VSTS.Scheduling.CompletedWork
Microsoft.VSTS.Scheduling.RemainingWork
Microsoft.VSTS.Scheduling.OriginalEstimate
For example:
def child = httpClient.get(
"/Demo1/_apis/wit/workItems/${childId}",
true
)
The values can then be accumulated:
totalCompletedWork += child.fields["Microsoft.VSTS.Scheduling.CompletedWork"] ?: 0
Sending the Aggregated Value to Jira
After processing all child Tasks, the total can be stored in a custom key:
replica.totalTimeSpent = totalCompletedWork
and synchronized to Jira:
issue.customFields."Time Spent".value = replica.totalTimeSpent
Important Consideration
While this approach can successfully retrieve and aggregate child Task values, it does not solve the synchronization trigger problem.
When a child Task’s time spent changes, the parent work item is not automatically updated. Since Exalate synchronizes based on changes to the synchronized entity, the parent work item may not be queued for synchronization when only a child Task is modified.
Therefore, an additional mechanism may be required to update or “touch” the parent whenever a child Task changes, allowing Exalate to detect the change and trigger a new synchronization cycle.
I hope this helps anyone exploring parent-child aggregation scenarios in Azure DevOps. I’d be interested to hear how others have implemented similar solutions and whether there are alternative approaches that work well in production environments.
Thanks, Dhiren