Migrate TFS "Repro Steps" field to the description
I recently decided, for a specific team project, to use the Description field instead of the Repro Steps field in the Bug work item. The main reason is to be able to create reports for both Product Backlog Items and Bugs, while being able to display the full description.
The first step was to migrate the content of the Repro Steps field into the Description field in order to retain data for existing items.
Since it is not possible to do field-to-field bulk update from the Web interface, I had to create a small console application to copy the data.
The main problem I encountered was about inline images. When you copy and paste a screenshot into an HTML field, it creates an img tag with Base64 data in the src attribute.
For an unknown reason, when the data in the src exceeds a certain limit (I don't know the actual value), the src attribute is blanked during the save. Images simply appear blank in the target field.
After some search, I came across this post from René van Osnabrugg. I tweaked the code to reach the final solution.
Basically, the code
Here is the complete code sample.
The first step was to migrate the content of the Repro Steps field into the Description field in order to retain data for existing items.
Since it is not possible to do field-to-field bulk update from the Web interface, I had to create a small console application to copy the data.
The main problem I encountered was about inline images. When you copy and paste a screenshot into an HTML field, it creates an img tag with Base64 data in the src attribute.
For an unknown reason, when the data in the src exceeds a certain limit (I don't know the actual value), the src attribute is blanked during the save. Images simply appear blank in the target field.
After some search, I came across this post from René van Osnabrugg. I tweaked the code to reach the final solution.
Basically, the code
- uses a Regex to find inline images in the Repro Steps field,
- extracts the binary data from the src attribute
- writes the images data on the local disk
- attaches the image to the work item
- replaces the inline image with a img tag linked to the tfs attachment API
- saves everything into the Description field
Here is the complete code sample.
var tfsCollectionUri = new Uri("tfs collection url");
var tfsCollection = new TfsTeamProjectCollection(tfsCollectionUri);
var workItemStore = tfsCollection.GetService<WorkItemStore>();
var items = workItemStore.Query("SELECT [System.Id], [System.Description] FROM WorkItems WHERE [System.TeamProject] = '@@@YourProject@@@' AND [System.WorkItemType] = 'Bug' AND [System.State] <> 'Removed'").Cast<WorkItem>();
foreach (var item in items)
{
item.Open();
var reproSteps = Convert.ToString(item.Fields["Microsoft.VSTS.TCM.ReproSteps"].Value);
// Get images from repro steps
var imageMatches = Regex.Matches(reproSteps, "<img src=\"data:image/png;base64,[^\\.]*\" alt=\"\">")
.Cast<Match>()
.Select(m => m.Value)
.Distinct();
foreach (var match in imageMatches)
{
var base64data = match.Replace("<img src=\"data:image/png;base64,", "")
.Replace("\" alt=\"\">", "");
// Write image to disk
var imageData = Convert.FromBase64String(base64data);
var imageName = "image-"+ item.Id + "-" + DateTime.Now.Ticks + ".png";
File.WriteAllBytes(imageName, imageData);
// Attach file to work item
int attachmentIndex = item.Attachments.Add(new Attachment(imageName, "Created from repro steps inline image"));
item.Save();
// Get the attachment ID
var attachmentGuid = item.Attachments[attachmentIndex].FileGuid.ToString();
// Replace inline image with attached version
reproSteps = reproSteps.Replace(match, String.Format("<img src=\"{0}/WorkItemTracking/v1.0/AttachFileHandler.ashx?FileNameGuid={1}&FileName={2}\"/>", tfsCollection.Uri.ToString(), attachmentGuid, imageName));
}
item.Description = reproSteps;
var errors = item.Validate();
if (errors.Count == 0)
{
item.Save(SaveFlags.MergeAll);
}
}
Comments
Post a Comment