Skip to content

Commit

Permalink
Fixed Azure Exporter (#28)
Browse files Browse the repository at this point in the history
* Fixed Azure exporter

* Updated tests

---------

Co-authored-by: Dmitry.Gridnev <[email protected]>
  • Loading branch information
gibiw and Dmitry.Gridnev authored Nov 15, 2023
1 parent c639dfd commit 1a5ee9e
Show file tree
Hide file tree
Showing 10 changed files with 146 additions and 127 deletions.
10 changes: 9 additions & 1 deletion Migrators/AllureExporter/App.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,15 @@ public void Run(string[] args)
{
_logger.LogInformation("Starting application");

_exportService.ExportProject().Wait();
try
{
_exportService.ExportProject().Wait();
}
catch (Exception e)
{
_logger.LogError(e, "Error occurred during export");
throw;
}

_logger.LogInformation("Ending application");
}
Expand Down
10 changes: 9 additions & 1 deletion Migrators/AzureExporter/App.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,15 @@ public void Run(string[] args)
{
_logger.LogInformation("Starting application");

_service.ExportProject().Wait();
try
{
_service.ExportProject().Wait();
}
catch (Exception e)
{
_logger.LogError(e, "Error occurred during export");
throw;
}

_logger.LogInformation("Ending application");
}
Expand Down
77 changes: 32 additions & 45 deletions Migrators/AzureExporter/Client/Client.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
using Microsoft.TeamFoundation.WorkItemTracking.WebApi;
using Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models;
using Microsoft.VisualStudio.Services.Common;
using Microsoft.VisualStudio.Services.TestManagement.TestPlanning.WebApi;
using Microsoft.VisualStudio.Services.WebApi;

namespace AzureExporter.Client;
Expand All @@ -17,7 +16,6 @@ public class Client : IClient
private readonly ILogger<Client> _logger;

private readonly ProjectHttpClient _projectClient;
private readonly TestPlanHttpClient _testPlanClient;
private readonly WorkItemTrackingHttpClient _workItemTrackingClient;
private readonly WorkHttpClient _workHttpClient;
private readonly string _projectName;
Expand Down Expand Up @@ -52,7 +50,6 @@ public Client(ILogger<Client> logger, IConfiguration configuration)

var connection = new VssConnection(new Uri(url), new VssBasicCredential(string.Empty, token));
_projectClient = connection.GetClient<ProjectHttpClient>();
_testPlanClient = connection.GetClient<TestPlanHttpClient>();
_workItemTrackingClient = connection.GetClient<WorkItemTrackingHttpClient>();
_workHttpClient = connection.GetClient<WorkHttpClient>();
}
Expand All @@ -75,29 +72,6 @@ public async Task<AzureProject> GetProject()
};
}

public async Task<PagedList<TestPlan>> GetTestPlansByProjectId(Guid id)
{
var testPlans = _testPlanClient.GetTestPlansAsync(project: id).Result;

return testPlans;
}

public async Task<PagedList<TestSuite>> GetTestSuitesByProjectIdAndTestPlanId(Guid projectId, int planId)
{
var testSuites = _testPlanClient.GetTestSuitesForPlanAsync(project: projectId, planId: planId).Result;

return testSuites;
}

public async Task<PagedList<TestCase>> GetTestCaseListByProjectIdAndTestPlanIdAndSuiteId(Guid projectId, int planId,
int suiteId)
{
var testCases = _testPlanClient.GetTestCaseListAsync(project: projectId, planId: planId, suiteId: suiteId)
.Result;

return testCases;
}

public async Task<List<int>> GetWorkItemIds(string workItemType)
{
var wiql = new Wiql
Expand All @@ -115,9 +89,13 @@ public async Task<List<int>> GetWorkItemIds(string workItemType)

public async Task<AzureWorkItem> GetWorkItemById(int id)
{
_logger.LogInformation("Getting work item with ID {Id}", id);

var workItem = _workItemTrackingClient.GetWorkItemAsync(_projectName, id, expand: WorkItemExpand.All).Result;

var result = new AzureWorkItem()
_logger.LogDebug("Work item: {@WorkItem}", workItem);

var result = new AzureWorkItem
{
Id = workItem.Id!.Value,
Title = GetValueOfField(workItem.Fields, "System.Title"),
Expand All @@ -127,22 +105,26 @@ public async Task<AzureWorkItem> GetWorkItemById(int id)
Steps = GetValueOfField(workItem.Fields, "Microsoft.VSTS.TCM.Steps"),
IterationPath = GetValueOfField(workItem.Fields, "System.IterationPath"),
Tags = GetValueOfField(workItem.Fields, "System.Tags"),
Links = workItem.Relations
.Where(r => r.Rel == "ArtifactLink")
.Select(r => new AzureLink
{
Title = GetValueOfField(r.Attributes, "name"),
Url = r.Url
})
.ToList(),
Attachments = workItem.Relations
.Where(r => r.Rel == "AttachedFile")
.Select(r => new AzureAttachment
{
Id = new Guid(r.Url[^36..]),
Name = GetValueOfField(r.Attributes, "name"),
})
.ToList(),
Links = workItem.Relations == null
? new List<AzureLink>()
: workItem.Relations
.Where(r => r.Rel == "ArtifactLink")
.Select(r => new AzureLink
{
Title = GetValueOfField(r.Attributes, "name"),
Url = r.Url
})
.ToList(),
Attachments = workItem.Relations == null
? new List<AzureAttachment>()
: workItem.Relations
.Where(r => r.Rel == "AttachedFile")
.Select(r => new AzureAttachment
{
Id = new Guid(r.Url[^36..]),
Name = GetValueOfField(r.Attributes, "name"),
})
.ToList(),
Parameters = new AzureParameters
{
Keys = GetValueOfField(workItem.Fields, "Microsoft.VSTS.TCM.Parameters"),
Expand All @@ -155,11 +137,16 @@ public async Task<AzureWorkItem> GetWorkItemById(int id)

public async Task<List<string>> GetIterations(Guid projectId)
{
var iterations = _workHttpClient.GetTeamIterationsAsync(new TeamContext(projectId)).Result;
_logger.LogInformation("Getting iterations");

return iterations
var iterations = _workHttpClient.GetTeamIterationsAsync(new TeamContext(projectId)).Result;
var paths = iterations
.Select(i => i.Path)
.ToList();

_logger.LogDebug("Got iterations: {@Iterations}", paths);

return paths;
}

public async Task<byte[]> GetAttachmentById(Guid id)
Expand Down
77 changes: 25 additions & 52 deletions Migrators/AzureExporter/Services/AttributeService.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using AzureExporter.Client;
using Microsoft.Extensions.Logging;
using Models;
using Attribute = Models.Attribute;
Expand All @@ -9,70 +8,44 @@ namespace AzureExporter.Services;
public class AttributeService : IAttributeService
{
private readonly ILogger<AttributeService> _logger;
private readonly IClient _client;

public AttributeService(ILogger<AttributeService> logger, IClient client)
public AttributeService(ILogger<AttributeService> logger)
{
_logger = logger;
_client = client;
}

public async Task<List<Attribute>> GetCustomAttributes(Guid projectId)
{
_logger.LogInformation("Getting custom attributes");

var attributes = new List<Attribute>();

var iterations = await _client.GetIterations(projectId);

attributes.Add(new Attribute
{
Id = Guid.NewGuid(),
Name = Constants.IterationAttributeName,
Type = AttributeType.Options,
IsActive = true,
IsRequired = false,
Options = ConvertIterations(iterations)
});

attributes.Add(new Attribute
{
Id = Guid.NewGuid(),
Name = Constants.StateAttributeName,
Type = AttributeType.Options,
IsActive = true,
IsRequired = false,
Options = new List<string>
{
"Active",
"Closed",
"Design",
"Ready"
}
});

return attributes;
}

private static List<string> ConvertIterations(IEnumerable<string> iterations)
{
var iterationList = new List<string>();

foreach (var iteration in iterations)
var attributes = new List<Attribute>
{
var iter = iteration.Split('\\');
if (iter.Length == 1)
new()
{
iterationList.Add(iteration);
continue;
}

for (var i = iter.Length; i > 1; i--)
Id = Guid.NewGuid(),
Name = Constants.IterationAttributeName,
Type = AttributeType.String,
IsActive = true,
IsRequired = false,
Options = new List<string>()
},
new()
{
iterationList.Add(string.Join("\\", iter.Take(i)));
Id = Guid.NewGuid(),
Name = Constants.StateAttributeName,
Type = AttributeType.Options,
IsActive = true,
IsRequired = false,
Options = new List<string>
{
"Active",
"Closed",
"Design",
"Ready"
}
}
}
};

return iterationList;
return attributes;
}
}
45 changes: 37 additions & 8 deletions Migrators/AzureExporter/Services/LinkService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,22 +38,51 @@ public LinkService(ILogger<LinkService> logger, IConfiguration configuration)

public List<Link> CovertLinks(IEnumerable<AzureLink> links)
{
_logger.LogInformation("Converting links: {@Links}", links);

var convertedLinks = new List<Link>();

foreach (var link in links)
{
var decodedUrl = HttpUtility.UrlDecode(link.Url);
while (decodedUrl.Contains('%'))
{
decodedUrl = HttpUtility.UrlDecode(decodedUrl);
}

var urlParts = decodedUrl.Split('/');
var project = urlParts[6];
var suffix = link.Title.Equals("Branch")
? $"?version={urlParts[^1]}"
: $"/commit/{urlParts[^1]}";

var convertedLink = new Link
Link convertedLink;
if (link.Url.Contains("VersionControl/Changeset"))
{
Url = $"{_url}/{_projectName}/_git/{project}{suffix}",
Title = link.Title
};
convertedLink = new Link
{
Url = $"{_url}/{_projectName}/_versionControl/changeset/{urlParts.Last()}",
Title = link.Title
};
}
else if (link.Url.Contains("VersionControl/VersionedItem"))
{
convertedLink = new Link
{
Url =
$"{_url}/{_projectName}/_versionControl/?path=${decodedUrl.Split("VersionControl/VersionedItem/$").Last().Replace("changesetVersion", "version").Replace("deletionId=0", "_a=contents")}",
Title = link.Title
};
}
else
{
var project = urlParts[6];
var suffix = link.Title.Equals("Branch")
? $"?version={urlParts[^1]}"
: $"/commit/{urlParts[^1]}";

convertedLink = new Link
{
Url = $"{_url}/{_projectName}/_git/{project}{suffix}",
Title = link.Title
};
}

_logger.LogDebug("Converted link {@OldLink}: {@Link}", link, convertedLink);

Expand Down
26 changes: 16 additions & 10 deletions Migrators/AzureExporter/Services/ParameterService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public List<Dictionary<string, string>> ConvertParameters(AzureParameters parame
_logger.LogInformation("Converting parameters");
_logger.LogDebug("Parameters: {@Parameters}", parameters);

if (string.IsNullOrWhiteSpace(parameters.Keys))
if (string.IsNullOrWhiteSpace(parameters.Keys) || !parameters.Keys.Contains('<'))
{
_logger.LogDebug("No keys found in parameters");

Expand Down Expand Up @@ -53,14 +53,20 @@ private static List<string> ParseParameterKeys(string content)

private static List<Dictionary<string, string>> ParseParameterValues(string content)
{
var xmlDoc = XDocument.Parse(content);

var parameters = xmlDoc.Descendants("Table1")
.Select(table1Node => table1Node.Elements()
.ToDictionary(element => element.Name.LocalName,
element => string.IsNullOrWhiteSpace(element.Value) ? "Empty" : element.Value.Trim()))
.ToList();

return parameters;
try
{
var xmlDoc = XDocument.Parse(content);
var parameters = xmlDoc.Descendants("Table1")
.Select(table1Node => table1Node.Elements()
.ToDictionary(element => element.Name.LocalName,
element => string.IsNullOrWhiteSpace(element.Value) ? "Empty" : element.Value.Trim()))
.ToList();

return parameters;
}
catch (Exception)
{
return new List<Dictionary<string, string>>();
}
}
}
4 changes: 2 additions & 2 deletions Migrators/AzureExporter/Services/SharedStepService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,15 @@ public async Task<Dictionary<int, SharedStep>> ConvertSharedSteps(Guid projectId

var workItemIds = await _client.GetWorkItemIds(Constants.SharedStepType);

_logger.LogDebug("Found {@WorkItems} shared steps", workItemIds.Count);
_logger.LogDebug("Found {Count} shared steps: {@WorkItems}", workItemIds.Count, workItemIds);

var sharedSteps = new Dictionary<int, SharedStep>();

foreach (var workItemId in workItemIds)
{
var sharedStep = await _client.GetWorkItemById(workItemId);

_logger.LogDebug("Found shared step: {Id}", sharedStep.Id);
_logger.LogDebug("Found shared step with {Id}: {@SharedStep}", sharedStep.Id, sharedStep);

var steps = _stepService.ConvertSteps(sharedStep.Steps, new Dictionary<int, Guid>());

Expand Down
3 changes: 3 additions & 0 deletions Migrators/AzureExporter/Services/TestCaseService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ public async Task<List<TestCase>> ConvertTestCases(Guid projectId, Dictionary<in
_logger.LogDebug("Converting test case: {Id}", workItemId);

var testCase = await _client.GetWorkItemById(workItemId);

_logger.LogDebug("Found test case with {Id}: {@TestCase}", testCase.Id, testCase);

var steps = _stepService.ConvertSteps(testCase.Steps, sharedStepMap);

_logger.LogDebug("Found {@Steps} steps", steps.Count);
Expand Down
Loading

0 comments on commit 1a5ee9e

Please sign in to comment.