Search code examples
azure-devopstfs-sdkazure-devops-rest-api

Backup workitems on VS Team Services


Is it possible to backup/export/download all work items in some way? I had a look at the REST API but it seems you can't execute f.e. a query through this API...


Solution

  • Ok, found a way to get the job done. I had a look again at the API Documentation. The page https://www.visualstudio.com/en-us/docs/integrate/api/wit/overview helped me out. You first need to do a

    GET https://{account}.visualstudio.com/defaultcollection/{project}/_apis/wit/queries/{folderpath}?api-version={version}&$expand=wiql
    

    From the resulting JSON you need to get the wiql part, which is the actual query. After this you need to do a

    POST https://{account}.visualstudio.com/defaultcollection/{project}/_apis/wit/wiql?api-version={version}
    

    Where the body is the JSON with { "query" = "YOURQUERY" }

    As a result you will receive a JSON containing all workitem urls/ids. You need to go through them and query each single workitem via

    GET URL?$expand=all
    

    Notice: add ?$expand=all only if you need the relations and attachments too. I put something together in for PowerShell. Note: I decided to hardcode the query and to remove the error handling to make it a little shorter.

    function loadJsonFile($fileName) 
    {
        return ConvertFrom-Json "$(Get-Content $fileName)"
    }
    function getLastItemFromURL($url) 
    {
        $absPath = ([System.Uri]$url).AbsolutePath
        $lastSlash = $absPath.LastIndexOf("/")
        $absPath.Substring($lastSlash+1)
    }
    function getWorkItemId($url)
    {
        getLastItemFromURL($url)
    }
    
    # make sure you enabled alternative credentials and access for them
    # you can get the value for YOURCODE i.e. via Fiddler
    $headers = @{Authorization="Basic YOURCODE"}
    
    # before this you would need to find the WIQL of the query; left to you
    $body = @{  
        "query" = "THEQUERYFROMTHEJSON"
    }
    $bodyJson = $body | ConvertTo-Json
    
    Invoke-RestMethod -method Post -ContentType application/json -Uri "https://{account}.visualstudio.com/defaultcollection/{project}/_apis/wit/wiql?api-version=1.0" -Headers $headers -Body $bodyJson -OutFile workitems.json
    
    $workItemsJson = $(loadJsonFile workitems.json)
    $workItems = $(foreach ($relation in $workItemsJson.workItemRelations)
    {
        $relation.target.url
        $relation.source.url
    }) | select -Unique | sort
    
    echo "Going to download the following ids from $(getWorkItemId $workItems[0])-$(getWorkItemId $workItems[-1])"
    
    # download the workitems
    foreach($workItemUrl in $workItems)
    {
        $workItemId = getWorkItemId $workItemUrl
        echo "Download ID: $workItemId"
    
        $workItemUrl = "$workItemUrl`?`$expand=all"
        $fileName = "workitem_$workItemId.json"
        Invoke-RestMethod -ContentType application/json -Uri "$workItemUrl" -Headers $headers -OutFile "$fileName"
    
        # download attachments
        $workItemJson = $(loadJsonFile "$fileName")
        foreach($relation in $workItemJson.relations)
        {
            if($relation.rel -eq "AttachedFile") 
            {
                $fileUrl = $relation.url
                Invoke-WebRequest $fileUrl -Headers $headers -OutFile $(getLastItemFromURL $fileUrl)
            }
        }
    }