Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
"scripts": {
"test": "phpunit"
},
"version": "2.1.9",
"version": "2.1.10",
"config": {
"platform": {
"php": "8.0"
Expand Down
26 changes: 13 additions & 13 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

144 changes: 142 additions & 2 deletions src/Client/ApiClientV2.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Qase\PhpCommons\Client;

use Exception;
use GuzzleHttp\Client;
use Qase\APIClientV2\Api\ResultsApi;
use Qase\APIClientV2\Configuration;
use Qase\APIClientV2\Model\CreateResultsRequestV2;
Expand All @@ -22,12 +23,14 @@
use Qase\PhpCommons\Models\Relation;
use Qase\PhpCommons\Models\Result;
use Qase\PhpCommons\Models\Step;
use Qase\PhpCommons\Utils\HostInfo;

class ApiClientV2 extends ApiClientV1
{
private Configuration $clientV2Config;
private Client $clientV2;

public function __construct(LoggerInterface $logger, TestopsConfig $config)
public function __construct(LoggerInterface $logger, TestopsConfig $config, string $framework = "", string $reporterName = "", array $hostData = [])
{
parent::__construct($logger, $config);

Expand All @@ -40,6 +43,12 @@ public function __construct(LoggerInterface $logger, TestopsConfig $config)
} else {
$this->clientV2Config->setHost('https://api-' . $host . '/v2');
}

// Create GuzzleHttp Client with default headers
$headers = $this->buildHeaders($framework, $reporterName, $hostData);
$this->clientV2 = new Client([
'headers' => $headers
]);
}

public function sendResults(string $code, int $runId, array $results): void
Expand All @@ -56,7 +65,7 @@ public function sendResults(string $code, int $runId, array $results): void

$this->logger->debug("Send results to project: " . json_encode($model));

$resultsApi = new ResultsApi($this->client, $this->clientV2Config);
$resultsApi = new ResultsApi($this->clientV2, $this->clientV2Config);
$resultsApi->createResultsV2($code, $runId, $model);
} catch (Exception $e) {
$this->logger->error("Error send results to project: " . $code . ', run: ' . $runId);
Expand Down Expand Up @@ -168,4 +177,135 @@ public function runUpdateExternalIssue(string $code, string $type, array $links)
// Delegate to parent class (ApiClientV1) implementation
parent::runUpdateExternalIssue($code, $type, $links);
}

/**
* Build X-Client and X-Platform headers based on HostInfo data
*
* @param string $framework Framework name
* @param string $reporterName Reporter name
* @param array $hostData Host data from HostInfo
* @return array Headers array
*/
private function buildHeaders(string $framework = "", string $reporterName = "", array $hostData = []): array
{
$headers = [];

// If hostData is empty, try to get it from HostInfo (fallback for backward compatibility)
if (empty($hostData)) {
$hostInfo = new HostInfo();
$hostData = $hostInfo->getHostInfo($framework, $reporterName);
}

// Build X-Client header
$xClientParts = [];

if (!empty($reporterName)) {
$xClientParts[] = 'reporter=' . $reporterName;
}

if (!empty($hostData['reporter'])) {
$reporterVersion = $this->normalizeVersion($hostData['reporter']);
if (!empty($reporterVersion)) {
$xClientParts[] = 'reporter_version=v' . $reporterVersion;
}
}

if (!empty($framework)) {
$xClientParts[] = 'framework=' . $framework;
}

if (!empty($hostData['framework'])) {
$frameworkVersion = $this->normalizeVersion($hostData['framework']);
if (!empty($frameworkVersion)) {
$xClientParts[] = 'framework_version=v' . $frameworkVersion;
}
}

if (!empty($hostData['apiClientV1'])) {
$clientV1Version = $this->normalizeVersion($hostData['apiClientV1']);
if (!empty($clientV1Version)) {
$xClientParts[] = 'client_version_v1=v' . $clientV1Version;
}
}

if (!empty($hostData['apiClientV2'])) {
$clientV2Version = $this->normalizeVersion($hostData['apiClientV2']);
if (!empty($clientV2Version)) {
$xClientParts[] = 'client_version_v2=v' . $clientV2Version;
}
}

if (!empty($hostData['commons'])) {
$commonsVersion = $this->normalizeVersion($hostData['commons']);
if (!empty($commonsVersion)) {
$xClientParts[] = 'core_version=v' . $commonsVersion;
}
}

if (!empty($xClientParts)) {
$headers['X-Client'] = implode(';', $xClientParts);
}

// Build X-Platform header
$xPlatformParts = [];

if (!empty($hostData['system'])) {
$osName = ucfirst($hostData['system']);
$xPlatformParts[] = 'os=' . $osName;
}

if (!empty($hostData['arch'])) {
$xPlatformParts[] = 'arch=' . $hostData['arch'];
}

if (!empty($hostData['php'])) {
$xPlatformParts[] = 'php=' . $hostData['php'];
}

if (!empty($hostData['composer'])) {
$xPlatformParts[] = 'composer=' . $hostData['composer'];
}

if (!empty($xPlatformParts)) {
$headers['X-Platform'] = implode(';', $xPlatformParts);
}

return $headers;
}

/**
* Normalize version string by removing constraints and prefixes
*
* @param string $version Version string from composer.json/composer.lock
* @return string Normalized version (e.g., "1.0.0" from "^1.0.0" or "v1.0.0")
*/
private function normalizeVersion(string $version): string
{
if (empty($version)) {
return '';
}

// Remove version constraints (^, ~, >=, etc.)
$version = preg_replace('/^[^0-9]*/', '', $version);

// Remove 'v' prefix if present
$version = ltrim($version, 'v');

// Extract version number (e.g., "1.0.0" from "1.0.0.0" or "1.0.0-dev")
if (preg_match('/^(\d+\.\d+\.\d+)/', $version, $matches)) {
return $matches[1];
}

// If no match, try to extract at least major.minor
if (preg_match('/^(\d+\.\d+)/', $version, $matches)) {
return $matches[1] . '.0';
}

// If still no match, try to extract at least major
if (preg_match('/^(\d+)/', $version, $matches)) {
return $matches[1] . '.0.0';
}

return '';
}
}
12 changes: 6 additions & 6 deletions src/Reporters/ReporterFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ public static function create(String $framework = "", String $reporterName = "")
$hostData = $hostInfo->getHostInfo($framework, $reporterName);
$logger->debug("Host data: " . json_encode($hostData));
$state = new StateManager();
$reporter = self::createInternalReporter($logger, $config, $state);
$fallbackReporter = self::createInternalReporter($logger, $config, $state, true);
$reporter = self::createInternalReporter($logger, $config, $state, false, $framework, $reporterName, $hostData);
$fallbackReporter = self::createInternalReporter($logger, $config, $state, true, $framework, $reporterName, $hostData);

// Create status mapping utility
$statusMapping = new StatusMapping($logger);
Expand All @@ -37,12 +37,12 @@ public static function create(String $framework = "", String $reporterName = "")
return new CoreReporter($logger, $reporter, $fallbackReporter, $config->getRootSuite(), $statusMapping);
}

private static function createInternalReporter(LoggerInterface $logger, QaseConfig $config, StateInterface $state, bool $fallback = false): ?InternalReporterInterface
private static function createInternalReporter(LoggerInterface $logger, QaseConfig $config, StateInterface $state, bool $fallback = false, string $framework = "", string $reporterName = "", array $hostData = []): ?InternalReporterInterface
{
$mode = $fallback ? $config->getFallback() : $config->getMode();

if ($mode === 'testops') {
return self::prepareTestopsReporter($logger, $config, $state);
return self::prepareTestopsReporter($logger, $config, $state, $framework, $reporterName, $hostData);
}

if ($mode === 'report') {
Expand All @@ -52,9 +52,9 @@ private static function createInternalReporter(LoggerInterface $logger, QaseConf
return null;
}

private static function prepareTestopsReporter(LoggerInterface $logger, QaseConfig $config, StateInterface $state): InternalReporterInterface
private static function prepareTestopsReporter(LoggerInterface $logger, QaseConfig $config, StateInterface $state, string $framework = "", string $reporterName = "", array $hostData = []): InternalReporterInterface
{
$client = new ApiClientV2($logger, $config->testops);
$client = new ApiClientV2($logger, $config->testops, $framework, $reporterName, $hostData);
return new TestOpsReporter($client, $config, $state, $logger);
}
}