Skip to content

Commit

Permalink
Merge pull request #27 from testit-tms/feature/add_practitest_exporter
Browse files Browse the repository at this point in the history
Added PractiTestExporter.
  • Loading branch information
PavelButuzov authored Nov 16, 2023
2 parents 1a5ee9e + bb74570 commit 98e72c0
Show file tree
Hide file tree
Showing 34 changed files with 2,203 additions and 14 deletions.
50 changes: 49 additions & 1 deletion .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ env:
XRAY_DIR: ./Migrators/XRayExporter
TESTCOLLAB_DIR: ./Migrators/TestCollabExporter
TESTLINK_DIR: ./Migrators/TestLinkExporter
PRACTITEST_DIR: ./Migrators/PractiTestExporter

jobs:
importer_build:
Expand Down Expand Up @@ -390,4 +391,51 @@ jobs:
file: publish/${{ matrix.artifact_name }}
asset_name: ${{ matrix.asset_name }}
tag: ${{ github.ref }}
overwrite: true
overwrite: true

practitest_build:
needs: testcollab_build
runs-on: ubuntu-latest
strategy:
matrix:
include:
- os: win-x64
artifact_name: PractiTestExporter.exe
asset_name: PractiTestExporter-win-x64-${{ github.event.release.tag_name }}.exe
- os: linux-x64
artifact_name: PractiTestExporter
asset_name: PractiTestExporter-linux-x64-${{ github.event.release.tag_name }}
- os: osx-x64
artifact_name: PractiTestExporter
asset_name: PractiTestExporter-osx-x64-${{ github.event.release.tag_name }}
steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0

- name: Setup .NET
uses: actions/setup-dotnet@v3
with:
dotnet-version: 7

- name: Restore dependencies
run: dotnet restore ${{ env.PRACTITEST_DIR }}

- name: Publish project
run: |
dotnet publish \
-r ${{ matrix.os }} \
--configuration Release \
--self-contained true \
--output ./publish \
--no-restore ${{ env.PRACTITEST_DIR }}
- name: Upload binaries to release
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: publish/${{ matrix.artifact_name }}
asset_name: ${{ matrix.asset_name }}
tag: ${{ github.ref }}
overwrite: true
2 changes: 2 additions & 0 deletions .github/workflows/validate.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ jobs:
./Migrators/XRayExporter,
./Migrators/TestCollabExporter,
./Migrators/TestLinkExporter,
./Migrators/PractiTestExporter,
./Migrators/Importer
]
steps:
Expand Down Expand Up @@ -45,6 +46,7 @@ jobs:
./Migrators/XRayExporterTests,
./Migrators/TestCollabExporterTests,
./Migrators/TestLinkExporterTests,
./Migrators/PractiTestExporterTests,
./Migrators/ImporterTests
]
steps:
Expand Down
28 changes: 18 additions & 10 deletions Migrators/Migrators.sln
Original file line number Diff line number Diff line change
Expand Up @@ -37,19 +37,19 @@ EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ZephyrSquadExporterTests", "ZephyrSquadExporterTests\ZephyrSquadExporterTests.csproj", "{C2B65002-89C3-4FD6-A945-87379D552098}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestLinkExporter", "TestLinkExporter\TestLinkExporter.csproj", "{EDB0D0A9-2B1A-46FB-B0BE-F8166F497BF1}"
ProjectSection(ProjectDependencies) = postProject
{6ABE461F-B644-40A5-A525-4F59ED1C177A} = {6ABE461F-B644-40A5-A525-4F59ED1C177A}
{C03F7958-C302-4F57-B158-E1F0E8A1A302} = {C03F7958-C302-4F57-B158-E1F0E8A1A302}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestLinkExporterTests", "TestLinkExporterTests\TestLinkExporterTests.csproj", "{87D89020-4060-4DA3-BD90-99CA1DBEBD6C}"
ProjectSection(ProjectDependencies) = postProject
{EDB0D0A9-2B1A-46FB-B0BE-F8166F497BF1} = {EDB0D0A9-2B1A-46FB-B0BE-F8166F497BF1}
EndProjectSection
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestLinkExporterTests", "TestLinkExporterTests\TestLinkExporterTests.csproj", "{87D89020-4060-4DA3-BD90-99CA1DBEBD6C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XRayExporter", "XRayExporter\XRayExporter.csproj", "{1E3A8E07-1023-4A40-90DF-1A2CA6BB707C}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "XRayExporter", "XRayExporter\XRayExporter.csproj", "{1E3A8E07-1023-4A40-90DF-1A2CA6BB707C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XRayExporterTests", "XRayExporterTests\XRayExporterTests.csproj", "{563D5041-6E07-4D52-BBB9-C87245604E6F}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "XRayExporterTests", "XRayExporterTests\XRayExporterTests.csproj", "{563D5041-6E07-4D52-BBB9-C87245604E6F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PractiTestExporter", "PractiTestExporter\PractiTestExporter.csproj", "{B90D8A9F-CFCB-4F02-8B76-4C082FFA3D06}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PractiTestExporterTests", "PractiTestExporterTests\PractiTestExporterTests.csproj", "{AB081068-0DE0-4408-A858-5109CCF0C3B4}"
ProjectSection(ProjectDependencies) = postProject
{B90D8A9F-CFCB-4F02-8B76-4C082FFA3D06} = {B90D8A9F-CFCB-4F02-8B76-4C082FFA3D06}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestCollabExporter", "TestCollabExporter\TestCollabExporter.csproj", "{713AF0B4-B036-4879-B3C6-16071D0864B4}"
EndProject
Expand Down Expand Up @@ -125,6 +125,14 @@ Global
{563D5041-6E07-4D52-BBB9-C87245604E6F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{563D5041-6E07-4D52-BBB9-C87245604E6F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{563D5041-6E07-4D52-BBB9-C87245604E6F}.Release|Any CPU.Build.0 = Release|Any CPU
{B90D8A9F-CFCB-4F02-8B76-4C082FFA3D06}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B90D8A9F-CFCB-4F02-8B76-4C082FFA3D06}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B90D8A9F-CFCB-4F02-8B76-4C082FFA3D06}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B90D8A9F-CFCB-4F02-8B76-4C082FFA3D06}.Release|Any CPU.Build.0 = Release|Any CPU
{AB081068-0DE0-4408-A858-5109CCF0C3B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AB081068-0DE0-4408-A858-5109CCF0C3B4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AB081068-0DE0-4408-A858-5109CCF0C3B4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AB081068-0DE0-4408-A858-5109CCF0C3B4}.Release|Any CPU.Build.0 = Release|Any CPU
{713AF0B4-B036-4879-B3C6-16071D0864B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{713AF0B4-B036-4879-B3C6-16071D0864B4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{713AF0B4-B036-4879-B3C6-16071D0864B4}.Release|Any CPU.ActiveCfg = Release|Any CPU
Expand Down
25 changes: 25 additions & 0 deletions Migrators/PractiTestExporter/App.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using Microsoft.Extensions.Logging;
using PractiTestExporter.Services;

namespace PractiTestExporter;

public class App
{
private readonly ILogger<App> _logger;
private readonly IExportService _service;

public App(ILogger<App> logger, IExportService service)
{
_logger = logger;
_service = service;
}

public void Run(string[] args)
{
_logger.LogInformation("Starting application");

_service.ExportProject().Wait();

_logger.LogInformation("Ending application");
}
}
226 changes: 226 additions & 0 deletions Migrators/PractiTestExporter/Client/Client.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
using System.Text.Json;
using PractiTestExporter.Models;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;

namespace PractiTestExporter.Client;

public class Client : IClient
{
private readonly ILogger<Client> _logger;
private readonly HttpClient _httpClient;
private readonly string _projectId;
private const int requestDelay = 2;

public Client(ILogger<Client> logger, IConfiguration configuration)
{
_logger = logger;

var section = configuration.GetSection("practiTest");
var url = section["url"];
if (string.IsNullOrEmpty(url))
{
throw new ArgumentException("Url is not specified");
}

var token = section["token"];
if (string.IsNullOrEmpty(token))
{
throw new ArgumentException("Token is not specified");
}

var projectId = section["projectId"];
if (string.IsNullOrEmpty(projectId))
{
throw new ArgumentException("Project name is not specified");
}

_projectId = projectId;
_httpClient = new HttpClient();
_httpClient.BaseAddress = new Uri(url);
_httpClient.DefaultRequestHeaders.Add("PTToken", token);
}

public async Task<PractiTestProject> GetProject()
{
await Task.Delay(TimeSpan.FromSeconds(requestDelay));

_logger.LogInformation("Getting project with id {Id}", _projectId);

var response = await _httpClient.GetAsync($"api/v2/projects/{_projectId}.json");
if (!response.IsSuccessStatusCode)
{
_logger.LogError("Failed to get project. Status code: {StatusCode}. Response: {Response}",
response.StatusCode, await response.Content.ReadAsStringAsync());

throw new Exception($"Failed to get project. Status code: {response.StatusCode}");
}

var content = await response.Content.ReadAsStringAsync();
var project = JsonSerializer.Deserialize<PractiTestProject>(content);

if (project != null) return project;

_logger.LogError("Project not found");

throw new Exception("Project not found");
}

public async Task<List<PractiTestTestCase>> GetTestCases()
{
await Task.Delay(TimeSpan.FromSeconds(requestDelay));

_logger.LogInformation("Getting test cases from project {Id}", _projectId);

var response =
await _httpClient.GetAsync($"api/v2/projects/{_projectId}/tests.json");
if (!response.IsSuccessStatusCode)
{
_logger.LogError(
"Failed to get test case ids from project {Id}. Status code: {StatusCode}. Response: {Response}",
_projectId, response.StatusCode, await response.Content.ReadAsStringAsync());

throw new Exception(
$"Failed to get test case ids from project {_projectId}. Status code: {response.StatusCode}");
}

var content = await response.Content.ReadAsStringAsync();
var testCases = JsonSerializer.Deserialize<PractiTestTestCases>(content);

return testCases is { Data.Count: 0 }
? new List<PractiTestTestCase>()
: testCases.Data;
}

public async Task<PractiTestTestCase> GetTestCaseById(string id)
{
await Task.Delay(TimeSpan.FromSeconds(requestDelay));

_logger.LogInformation("Getting test case by id {Id}", id);

var response =
await _httpClient.GetAsync($"api/v2/projects/{_projectId}/tests/{id}.json");
if (!response.IsSuccessStatusCode)
{
_logger.LogError(
"Failed to get test case by id {Id}. Status code: {StatusCode}. Response: {Response}",
id, response.StatusCode, await response.Content.ReadAsStringAsync());

throw new Exception(
$"Failed to get test case by id {id}. Status code: {response.StatusCode}");
}

var content = await response.Content.ReadAsStringAsync();
var testCase = JsonSerializer.Deserialize<SinglePractiTestTestCase>(content);

return testCase.Data;
}

public async Task<List<PractiTestStep>> GetStepsByTestCaseId(string testCaseId)
{
await Task.Delay(TimeSpan.FromSeconds(requestDelay));

_logger.LogInformation("Getting steps for test case with id {Id}", testCaseId);

var response = await _httpClient.GetAsync($"api/v2/projects/{_projectId}/steps.json?test-ids={testCaseId}");
if (!response.IsSuccessStatusCode)
{
_logger.LogError(
"Failed to get steps for test case with id {TestCaseId}. Status code: {StatusCode}. Response: {Response}",
testCaseId, response.StatusCode, await response.Content.ReadAsStringAsync());

throw new Exception(
$"Failed to get steps for test case with id {testCaseId}. Status code: {response.StatusCode}");
}

var content = await response.Content.ReadAsStringAsync();
var steps = JsonSerializer.Deserialize<PractiTestSteps>(content);

return steps is { Data.Count: 0 }
? new List<PractiTestStep>()
: steps.Data;
}

public async Task<List<PractiTestAttachment>> GetAttachmentsByEntityId(string entityType, string entityId)
{
await Task.Delay(TimeSpan.FromSeconds(requestDelay));

_logger.LogInformation("Getting attachments for {EntityType} with id {EntityId}", entityType, entityId);

var response = await _httpClient.GetAsync($"api/v2/projects/{_projectId}/attachments.json?entity={entityType}&entity-id={entityId}");

if (!response.IsSuccessStatusCode)
{
_logger.LogError(
"Failed to get attachments for {EntityType} with id {EntityId}. Status code: {StatusCode}. Response: {Response}",
entityType, entityId, response.StatusCode, await response.Content.ReadAsStringAsync());

throw new Exception(
$"Failed to get attachments for {entityType} with id {entityId}. Status code: {response.StatusCode}");
}

var content = await response.Content.ReadAsStringAsync();
var attachments = JsonSerializer.Deserialize<PractiTestAttachments>(content);

return attachments is { Data.Count: 0 }
? new List<PractiTestAttachment>()
: attachments.Data;
}

public async Task<byte[]> DownloadAttachmentById(string id)
{
await Task.Delay(TimeSpan.FromSeconds(requestDelay));

_logger.LogInformation("Downloading attachment by id {Id}", id);

return await _httpClient.GetByteArrayAsync($"api/v2/projects/{_projectId}/attachments/{id}");
}

public async Task<List<PractiTestCustomField>> GetCustomFields()
{
await Task.Delay(TimeSpan.FromSeconds(requestDelay));

_logger.LogInformation("Getting custom fields by project id {ProjectId}", _projectId);

var response = await _httpClient.GetAsync($"api/v2/projects/{_projectId}/custom_fields.json");
if (!response.IsSuccessStatusCode)
{
_logger.LogError(
"Failed to get custom fields by project id {ProjectId}. Status code: {StatusCode}. Response: {Response}",
_projectId, response.StatusCode, await response.Content.ReadAsStringAsync());

throw new Exception(
$"Failed to get custom fields by project id {_projectId}. Status code: {response.StatusCode}");
}

var content = await response.Content.ReadAsStringAsync();
var customFields = JsonSerializer.Deserialize<PractiTestCustomFields>(content);

return customFields is { Data.Count: 0 }
? new List<PractiTestCustomField>()
: customFields.Data;
}

public async Task<ListPractiTestCustomField> GetListCustomFieldById(string id)
{
await Task.Delay(TimeSpan.FromSeconds(requestDelay));

_logger.LogInformation("Getting custom field by id {Id}", id);

var response = await _httpClient.GetAsync($"api/v2/projects/{_projectId}/custom_fields//{id}.json");
if (!response.IsSuccessStatusCode)
{
_logger.LogError(
"Failed to get custom field by id {Id}. Status code: {StatusCode}. Response: {Response}",
id, response.StatusCode, await response.Content.ReadAsStringAsync());

throw new Exception(
$"Failed to get custom field by id {id}. Status code: {response.StatusCode}");
}

var content = await response.Content.ReadAsStringAsync();
var customField = JsonSerializer.Deserialize<SinglePractiTestCustomField>(content);

return customField.Data;
}
}
15 changes: 15 additions & 0 deletions Migrators/PractiTestExporter/Client/IClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using PractiTestExporter.Models;

namespace PractiTestExporter.Client;

public interface IClient
{
Task<PractiTestProject> GetProject();
Task<List<PractiTestTestCase>> GetTestCases();
Task<PractiTestTestCase> GetTestCaseById(string id);
Task<List<PractiTestStep>> GetStepsByTestCaseId(string testCaseId);
Task<List<PractiTestAttachment>> GetAttachmentsByEntityId(string entityType, string entityId);
Task<byte[]> DownloadAttachmentById(string id);
Task<List<PractiTestCustomField>> GetCustomFields();
Task<ListPractiTestCustomField> GetListCustomFieldById(string id);
}
Loading

0 comments on commit 98e72c0

Please sign in to comment.