I'm working on an application that exports and imports work items from Azure DevOps. To import work items into Azure DevOps, I have written the following code.
Would you be able to review and make suggestions? I'm unsure whether I've used the Async-Await pattern correctly.
namespace Export_Import_AzureDevOps.Factory
{
public class ImportFactory : IImportFactory
{
private ConcurrentDictionary<int, int> idMapper = new ConcurrentDictionary<int, int>();
private readonly ILogger<ImportFactory> _logger;
private readonly Devops _devopsConfiguration;
private readonly IImportService<WorkItemCore> _importWorkItemService;
private readonly IImportService<SprintCore> _importSprintService;
public ImportFactory(ILogger<ImportFactory> logger, IConfiguration configuration, IImportService<SprintCore> importSprintService, IImportService<WorkItemCore> importWorkItemService)
{
_logger = logger;
_devopsConfiguration = configuration.GetSection(nameof(Devops)).Get<Devops>();
_importSprintService = importSprintService;
_importWorkItemService = importWorkItemService;
}
private void PublishSprints(string id)
{
var sprintPublishURL = $"{_devopsConfiguration.Target.ProjectId}/{_devopsConfiguration.Target.TargetTeamId}/_apis/work/teamsettings/iterations?api-version={_devopsConfiguration.APIVersion}";
var jsonPublishSprintString = $@"{{""id"":""{id}""}}";
HttpContent publishSprintContent = new StringContent(jsonPublishSprintString, Encoding.UTF8, "application/json");
_importSprintService.Post(sprintPublishURL, publishSprintContent).ConfigureAwait(false);
}
private Task CreateSprints(Sprints sprints)
{
var sprintCreationURL = $"{_devopsConfiguration.Target.ProjectId}/_apis/wit/classificationNodes/Iterations?api-version={_devopsConfiguration.APIVersion}";
foreach (Sprint sprint in sprints.value)
{
var jsonString = JsonConvert.SerializeObject(sprint);
HttpContent content = new StringContent(jsonString, Encoding.UTF8, "application/json");
var result = _importWorkItemService.Post(sprintCreationURL, content).Result;
PublishSprints(result.identifier);
}
return Task.CompletedTask;
}
private int GetCurrentIdEquivalentId(int oldId)
{
return idMapper[oldId];
}
private void TryAddIdMapper(int oldId, int newId)
{
if (!idMapper.ContainsKey(oldId))
{
idMapper.TryAdd(oldId, newId);
}
}
private int FindParentId(WorkItemDetails workItemDetails)
{
var parentId = 0;
if (workItemDetails.relations != null)
{
var parentRelation = workItemDetails.relations.Where(relation => relation.attributes.name.Equals("Parent")).FirstOrDefault();
if (parentRelation != null)
{
parentId = Int32.Parse(parentRelation.url.Split("/")[parentRelation.url.Split("/").Length - 1]);
}
}
return parentId;
}
private void CreateWorkItem(string url, WorkItem workItem)
{
List<WorkItemOperation> operations = new List<WorkItemOperation>();
var parentId = FindParentId(workItem.details);
operations.Add(
new WorkItemOperation()
{
path = "/fields/System.Title",
value = workItem.details.fields.Title ?? string.Empty
}
);
operations.Add(
new WorkItemOperation()
{
path = "/fields/System.Description",
value = workItem.details.fields.Description ?? string.Empty
}
);
operations.Add(
new WorkItemOperation()
{
path = "/fields/Microsoft.VSTS.Common.AcceptanceCriteria",
value = workItem.details.fields.AcceptanceCriteria ?? string.Empty
}
);
operations.Add(
new WorkItemOperation()
{
path = "/fields/System.IterationPath",
value = workItem.details.fields.IterationPath.Replace(_devopsConfiguration.Source.ProjectName, _devopsConfiguration.Target.ProjectName)
}
);
if (parentId != 0)
{
operations.Add(
new WorkItemOperation()
{
path = "/relations/-",
value = new Relationship()
{
url = $"{_devopsConfiguration.Target.DevOpsOrgURL}{_devopsConfiguration.Target.ProjectId}/_apis/wit/workitems/{GetCurrentIdEquivalentId(parentId)}",
attributes = new RelationshipAttribute()
}
}
);
}
var jsonString = JsonConvert.SerializeObject(operations);
HttpContent content = new StringContent(jsonString, Encoding.UTF8, "application/json-patch+json");
var result = _importWorkItemService.Post(url, content).Result;
TryAddIdMapper(workItem.id, result.id);
}
private Task CreateWorkItems(Dictionary<string, WorkItemQueryResult> workItemCollection)
{
foreach (var workItemCategory in workItemCollection.Keys)
{
var url = $"{_devopsConfiguration.Target.ProjectId}/_apis/wit/workitems/%24{workItemCategory}?api-version={_devopsConfiguration.APIVersion}";
foreach (var workItem in workItemCollection[workItemCategory].workItems)
{
CreateWorkItem(url, workItem);
}
}
return Task.CompletedTask;
}
public async Task<Board> Import(IFormFile file)
{
string fileContent = null;
using (var reader = new StreamReader(file.OpenReadStream()))
{
fileContent = reader.ReadToEnd();
}
var board = JsonConvert.DeserializeObject<Board>(fileContent);
await CreateSprints(board.sprints);
await CreateWorkItems(board.workItemCollection);
return board;
}
}
}