Skip to content

Commit

Permalink
Fixed importing test cases without required attributes.
Browse files Browse the repository at this point in the history
  • Loading branch information
pavel.butuzov committed Jul 25, 2024
1 parent 9b4c91f commit a4741d1
Show file tree
Hide file tree
Showing 4 changed files with 165 additions and 6 deletions.
140 changes: 138 additions & 2 deletions Migrators/Importer/Client/Client.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Models;
using System;
using System.Xml.Linq;
using TestIT.ApiClient.Api;
using TestIT.ApiClient.Client;
using TestIT.ApiClient.Model;
Expand All @@ -15,13 +17,15 @@ public class Client : IClient
private readonly ILogger<Client> _logger;
private readonly AttachmentsApi _attachments;
private readonly ProjectsApi _projectsApi;
private readonly ProjectAttributesApi _projectAttributesApi;
private readonly ProjectSectionsApi _projectSectionsApi;
private readonly SectionsApi _sectionsApi;
private readonly CustomAttributesApi _customAttributes;
private readonly WorkItemsApi _workItemsApi;
private readonly CustomAttributesApi _customAttributesApi;
private readonly ParametersApi _parametersApi;
private bool _importToExistingProject = false;
private readonly bool _importToExistingProject;
private readonly string _projectName;

private const int TenMinutes = 60000;

Expand Down Expand Up @@ -49,6 +53,13 @@ public Client(ILogger<Client> logger, IConfiguration configuration)
certValidation = bool.Parse(certValidationStr);
}

_projectName = tmsSection["projectName"];
if (!string.IsNullOrEmpty(_projectName))
{
_logger.LogInformation("Import by custom project name {Name}", _projectName);
}

_importToExistingProject = false;
var importToExistingProjectStr = tmsSection["importToExistingProject"];
if (!string.IsNullOrEmpty(importToExistingProjectStr))
{
Expand All @@ -64,6 +75,7 @@ public Client(ILogger<Client> logger, IConfiguration configuration)

_attachments = new AttachmentsApi(new HttpClient(), cfg, httpClientHandler);
_projectsApi = new ProjectsApi(new HttpClient(), cfg, httpClientHandler);
_projectAttributesApi = new ProjectAttributesApi(new HttpClient(), cfg, httpClientHandler);
_projectSectionsApi = new ProjectSectionsApi(new HttpClient(), cfg, httpClientHandler);
_sectionsApi = new SectionsApi(new HttpClient(), cfg, httpClientHandler);
_customAttributes = new CustomAttributesApi(new HttpClient(), cfg, httpClientHandler);
Expand All @@ -74,6 +86,11 @@ public Client(ILogger<Client> logger, IConfiguration configuration)

public async Task<Guid> GetProject(string name)
{
if (!string.IsNullOrEmpty(_projectName))
{
name = _projectName;
}

_logger.LogInformation("Getting project {Name}", name);

try
Expand Down Expand Up @@ -111,6 +128,11 @@ public async Task<Guid> GetProject(string name)

public async Task<Guid> CreateProject(string name)
{
if (!string.IsNullOrEmpty(_projectName))
{
name = _projectName;
}

_logger.LogInformation("Creating project {Name}", name);

try
Expand Down Expand Up @@ -372,7 +394,7 @@ public async Task ImportTestCase(Guid projectId, Guid parentSectionId, TmsTestCa
WorkItemId = s.SharedStepId,
TestData = s.TestData
}).ToList(),
Attributes = testCase.Attributes.Where(x => x.Value != null)
Attributes = testCase.Attributes
.ToDictionary(keySelector: a => a.Id.ToString(),
elementSelector: a => (object)a.Value),
Tags = testCase.Tags.Select(t => new TagPostModel(t)).ToList(),
Expand Down Expand Up @@ -463,6 +485,89 @@ public async Task<List<TmsAttribute>> GetProjectAttributes()
}
}

public async Task<List<TmsAttribute>> GetRequiredProjectAttributesByProjectId(Guid projectId)
{
_logger.LogInformation("Getting required project attributes by project id {Id}", projectId);

try
{
var attributes = await _projectAttributesApi.SearchAttributesInProjectAsync(
projectId: projectId.ToString(), searchAttributesInProjectRequest: new SearchAttributesInProjectRequest(
name: "",
isRequired: true,
types: new List<CustomAttributeTypesEnum>()
{
CustomAttributeTypesEnum.String,
CustomAttributeTypesEnum.Options,
CustomAttributeTypesEnum.MultipleOptions,
CustomAttributeTypesEnum.User,
CustomAttributeTypesEnum.Datetime
}
));

var requiredAttributes = attributes
.Select(a => new TmsAttribute
{
Id = a.Id,
Name = a.Name,
Type = a.Type.ToString(),
IsEnabled = a.IsEnabled,
IsRequired = a.IsRequired,
IsGlobal = a.IsGlobal,
Options = a.Options.Select(o => new TmsAttributeOptions
{
Id = o.Id,
Value = o.Value,
IsDefault = o.IsDefault
}).ToList()
}).ToList();

_logger.LogDebug("Got required project attributes by project id {id}: {@Attributes}", projectId, requiredAttributes);

return requiredAttributes;
}
catch (Exception e)
{
_logger.LogError("Could not get required project attributes by project id {Id}: {Message}", projectId, e.Message);
throw;
}
}

public async Task<TmsAttribute> GetProjectAttributeById(Guid id)
{
_logger.LogInformation("Getting project attribute by id {Id}", id);

try
{
var attribute = await _customAttributes.ApiV2CustomAttributesIdGetAsync(id);

var customAttribute = new TmsAttribute
{
Id = attribute.Id,
Name = attribute.Name,
Type = attribute.Type.ToString(),
IsEnabled = attribute.IsEnabled,
IsRequired = attribute.IsRequired,
IsGlobal = attribute.IsGlobal,
Options = attribute.Options.Select(o => new TmsAttributeOptions
{
Id = o.Id,
Value = o.Value,
IsDefault = o.IsDefault
}).ToList()
};

_logger.LogDebug("Got project attribute by id {id}: {@Attribute}", id, customAttribute);

return customAttribute;
}
catch (Exception e)
{
_logger.LogError("Could not get project attribute by id {Id}: {Message}", id, e.Message);
throw;
}
}

public async Task AddAttributesToProject(Guid projectId, IEnumerable<Guid> attributeIds)
{
_logger.LogInformation("Adding attributes to project");
Expand Down Expand Up @@ -520,6 +625,37 @@ public async Task<TmsAttribute> UpdateAttribute(TmsAttribute attribute)
}
}

public async Task UpdateProjectAttribute(Guid projectId, TmsAttribute attribute)
{
_logger.LogInformation("Updating project attribute {Name}", attribute.Name);

try
{
var model = new UpdateProjectsAttributeRequest(id: attribute.Id, name: attribute.Name)
{
IsEnabled = attribute.IsEnabled,
IsRequired = attribute.IsRequired,
Options = attribute.Options.Select(o => new CustomAttributeOptionModel()
{
Id = o.Id,
Value = o.Value,
IsDefault = o.IsDefault
}).ToList()
};

_logger.LogDebug("Updating attribute {@Model}", model);

await _projectAttributesApi.UpdateProjectsAttributeAsync(
projectId: projectId.ToString(), updateProjectsAttributeRequest: model);
}

catch (Exception e)
{
_logger.LogError("Could not update attribute {Name}: {Message}", attribute.Name, e.Message);
throw;
}
}

public async Task<Guid> UploadAttachment(string fileName, Stream content)
{
_logger.LogDebug("Uploading attachment {Name}", fileName);
Expand Down
3 changes: 3 additions & 0 deletions Migrators/Importer/Client/IClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,11 @@ public interface IClient
Task ImportTestCase(Guid projectId, Guid parentSectionId, TmsTestCase testCase);
Task<Guid> GetRootSectionId(Guid projectId);
Task<List<TmsAttribute>> GetProjectAttributes();
Task<List<TmsAttribute>> GetRequiredProjectAttributesByProjectId(Guid projectId);
Task<TmsAttribute> GetProjectAttributeById(Guid id);
Task AddAttributesToProject(Guid projectId, IEnumerable<Guid> attributeIds);
Task<TmsAttribute> UpdateAttribute(TmsAttribute attribute);
Task UpdateProjectAttribute(Guid projectId, TmsAttribute attribute);
Task<Guid> UploadAttachment(string fileName, Stream content);
Task<TmsParameter> CreateParameter(Parameter parameter);
Task<List<TmsParameter>> GetParameter(string name);
Expand Down
6 changes: 4 additions & 2 deletions Migrators/Importer/Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ You can download the latest version of the Importer from the [releases](https://
"url" : "https://testit.software/",
"privateToken" : "cmZzWDkYTfBvNvVMcXhzN3Vy",
"certValidation" : true,
"importToExistingProject" : false
"importToExistingProject" : false,
"projectName" : "CustomProjectName"
}
}
```
Expand All @@ -28,7 +29,8 @@ Where:
- tms.url - url to the Test IT server
- tms.privateToken - token for access to the Test IT server
- tms.certValidation - enable/disable certificate validation (Default value - true)
- tms.importToExistingProject - enable/disable import to existing project in Test IT server (Default value - false)
- tms.importToExistingProject - enable/disable import to existing project in the Test IT server (Default value - false)
- tms.projectName - custom name of the project in the Test IT server (Default value - name of the project in the export system)

2. Run the Importer with the following command:

Expand Down
22 changes: 20 additions & 2 deletions Migrators/Importer/Services/AttributeService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,19 @@ public async Task<Dictionary<Guid, TmsAttribute>> ImportAttributes(Guid projectI
_logger.LogInformation("Importing attributes");

var projectAttributes = await _client.GetProjectAttributes();
var unusedRequiredProjectAttributes = await _client.GetRequiredProjectAttributesByProjectId(projectId);

var attributesMap = new Dictionary<Guid, TmsAttribute>();

foreach (var attribute in attributes)
{
var requiredProjectAttribute = unusedRequiredProjectAttributes.FirstOrDefault(x => x.Name == attribute.Name);

if (requiredProjectAttribute != null && requiredProjectAttribute.Type == attribute.Type.ToString())
{
unusedRequiredProjectAttributes.Remove(requiredProjectAttribute);
}

var attributeIsNotImported = true;

do
Expand All @@ -49,7 +57,7 @@ public async Task<Dictionary<Guid, TmsAttribute>> ImportAttributes(Guid projectI
{
_logger.LogInformation("Attribute {Name} already exists with id {Id}",
attribute.Name,
attribute.Id);
projectAttribute.Id);

if (string.Equals(projectAttribute.Type, "options", StringComparison.InvariantCultureIgnoreCase) ||
string.Equals(projectAttribute.Type, "multipleOptions", StringComparison.InvariantCultureIgnoreCase))
Expand All @@ -68,7 +76,8 @@ public async Task<Dictionary<Guid, TmsAttribute>> ImportAttributes(Guid projectI
}
}

projectAttribute = await _client.UpdateAttribute(projectAttribute);
await _client.UpdateAttribute(projectAttribute);
projectAttribute = await _client.GetProjectAttributeById(projectAttribute.Id);
}

attributesMap.Add(attribute.Id, projectAttribute);
Expand All @@ -85,6 +94,15 @@ public async Task<Dictionary<Guid, TmsAttribute>> ImportAttributes(Guid projectI
while (attributeIsNotImported);
}

foreach (var unusedRequiredProjectAttribute in unusedRequiredProjectAttributes)
{
_logger.LogInformation("Required project attribute {Name} is not used when importing test cases. Set as optional", unusedRequiredProjectAttribute.Name);

unusedRequiredProjectAttribute.IsRequired = false;

await _client.UpdateProjectAttribute(projectId, unusedRequiredProjectAttribute);
}

_logger.LogInformation("Importing attributes finished");
_logger.LogDebug("Attributes map: {@AttributesMap}", attributesMap);

Expand Down

0 comments on commit a4741d1

Please sign in to comment.