55namespace Qase \PhpCommons \Client ;
66
77use Exception ;
8+ use GuzzleHttp \Client ;
89use Qase \APIClientV2 \Api \ResultsApi ;
910use Qase \APIClientV2 \Configuration ;
1011use Qase \APIClientV2 \Model \CreateResultsRequestV2 ;
2223use Qase \PhpCommons \Models \Relation ;
2324use Qase \PhpCommons \Models \Result ;
2425use Qase \PhpCommons \Models \Step ;
26+ use Qase \PhpCommons \Utils \HostInfo ;
2527
2628class ApiClientV2 extends ApiClientV1
2729{
2830 private Configuration $ clientV2Config ;
31+ private Client $ clientV2 ;
2932
30- public function __construct (LoggerInterface $ logger , TestopsConfig $ config )
33+ public function __construct (LoggerInterface $ logger , TestopsConfig $ config, string $ framework = "" , string $ reporterName = "" , array $ hostData = [] )
3134 {
3235 parent ::__construct ($ logger , $ config );
3336
@@ -40,6 +43,12 @@ public function __construct(LoggerInterface $logger, TestopsConfig $config)
4043 } else {
4144 $ this ->clientV2Config ->setHost ('https://api- ' . $ host . '/v2 ' );
4245 }
46+
47+ // Create GuzzleHttp Client with default headers
48+ $ headers = $ this ->buildHeaders ($ framework , $ reporterName , $ hostData );
49+ $ this ->clientV2 = new Client ([
50+ 'headers ' => $ headers
51+ ]);
4352 }
4453
4554 public function sendResults (string $ code , int $ runId , array $ results ): void
@@ -56,7 +65,7 @@ public function sendResults(string $code, int $runId, array $results): void
5665
5766 $ this ->logger ->debug ("Send results to project: " . json_encode ($ model ));
5867
59- $ resultsApi = new ResultsApi ($ this ->client , $ this ->clientV2Config );
68+ $ resultsApi = new ResultsApi ($ this ->clientV2 , $ this ->clientV2Config );
6069 $ resultsApi ->createResultsV2 ($ code , $ runId , $ model );
6170 } catch (Exception $ e ) {
6271 $ this ->logger ->error ("Error send results to project: " . $ code . ', run: ' . $ runId );
@@ -168,4 +177,135 @@ public function runUpdateExternalIssue(string $code, string $type, array $links)
168177 // Delegate to parent class (ApiClientV1) implementation
169178 parent ::runUpdateExternalIssue ($ code , $ type , $ links );
170179 }
180+
181+ /**
182+ * Build X-Client and X-Platform headers based on HostInfo data
183+ *
184+ * @param string $framework Framework name
185+ * @param string $reporterName Reporter name
186+ * @param array $hostData Host data from HostInfo
187+ * @return array Headers array
188+ */
189+ private function buildHeaders (string $ framework = "" , string $ reporterName = "" , array $ hostData = []): array
190+ {
191+ $ headers = [];
192+
193+ // If hostData is empty, try to get it from HostInfo (fallback for backward compatibility)
194+ if (empty ($ hostData )) {
195+ $ hostInfo = new HostInfo ();
196+ $ hostData = $ hostInfo ->getHostInfo ($ framework , $ reporterName );
197+ }
198+
199+ // Build X-Client header
200+ $ xClientParts = [];
201+
202+ if (!empty ($ reporterName )) {
203+ $ xClientParts [] = 'reporter= ' . $ reporterName ;
204+ }
205+
206+ if (!empty ($ hostData ['reporter ' ])) {
207+ $ reporterVersion = $ this ->normalizeVersion ($ hostData ['reporter ' ]);
208+ if (!empty ($ reporterVersion )) {
209+ $ xClientParts [] = 'reporter_version=v ' . $ reporterVersion ;
210+ }
211+ }
212+
213+ if (!empty ($ framework )) {
214+ $ xClientParts [] = 'framework= ' . $ framework ;
215+ }
216+
217+ if (!empty ($ hostData ['framework ' ])) {
218+ $ frameworkVersion = $ this ->normalizeVersion ($ hostData ['framework ' ]);
219+ if (!empty ($ frameworkVersion )) {
220+ $ xClientParts [] = 'framework_version=v ' . $ frameworkVersion ;
221+ }
222+ }
223+
224+ if (!empty ($ hostData ['apiClientV1 ' ])) {
225+ $ clientV1Version = $ this ->normalizeVersion ($ hostData ['apiClientV1 ' ]);
226+ if (!empty ($ clientV1Version )) {
227+ $ xClientParts [] = 'client_version_v1=v ' . $ clientV1Version ;
228+ }
229+ }
230+
231+ if (!empty ($ hostData ['apiClientV2 ' ])) {
232+ $ clientV2Version = $ this ->normalizeVersion ($ hostData ['apiClientV2 ' ]);
233+ if (!empty ($ clientV2Version )) {
234+ $ xClientParts [] = 'client_version_v2=v ' . $ clientV2Version ;
235+ }
236+ }
237+
238+ if (!empty ($ hostData ['commons ' ])) {
239+ $ commonsVersion = $ this ->normalizeVersion ($ hostData ['commons ' ]);
240+ if (!empty ($ commonsVersion )) {
241+ $ xClientParts [] = 'core_version=v ' . $ commonsVersion ;
242+ }
243+ }
244+
245+ if (!empty ($ xClientParts )) {
246+ $ headers ['X-Client ' ] = implode ('; ' , $ xClientParts );
247+ }
248+
249+ // Build X-Platform header
250+ $ xPlatformParts = [];
251+
252+ if (!empty ($ hostData ['system ' ])) {
253+ $ osName = ucfirst ($ hostData ['system ' ]);
254+ $ xPlatformParts [] = 'os= ' . $ osName ;
255+ }
256+
257+ if (!empty ($ hostData ['arch ' ])) {
258+ $ xPlatformParts [] = 'arch= ' . $ hostData ['arch ' ];
259+ }
260+
261+ if (!empty ($ hostData ['php ' ])) {
262+ $ xPlatformParts [] = 'php= ' . $ hostData ['php ' ];
263+ }
264+
265+ if (!empty ($ hostData ['composer ' ])) {
266+ $ xPlatformParts [] = 'composer= ' . $ hostData ['composer ' ];
267+ }
268+
269+ if (!empty ($ xPlatformParts )) {
270+ $ headers ['X-Platform ' ] = implode ('; ' , $ xPlatformParts );
271+ }
272+
273+ return $ headers ;
274+ }
275+
276+ /**
277+ * Normalize version string by removing constraints and prefixes
278+ *
279+ * @param string $version Version string from composer.json/composer.lock
280+ * @return string Normalized version (e.g., "1.0.0" from "^1.0.0" or "v1.0.0")
281+ */
282+ private function normalizeVersion (string $ version ): string
283+ {
284+ if (empty ($ version )) {
285+ return '' ;
286+ }
287+
288+ // Remove version constraints (^, ~, >=, etc.)
289+ $ version = preg_replace ('/^[^0-9]*/ ' , '' , $ version );
290+
291+ // Remove 'v' prefix if present
292+ $ version = ltrim ($ version , 'v ' );
293+
294+ // Extract version number (e.g., "1.0.0" from "1.0.0.0" or "1.0.0-dev")
295+ if (preg_match ('/^(\d+\.\d+\.\d+)/ ' , $ version , $ matches )) {
296+ return $ matches [1 ];
297+ }
298+
299+ // If no match, try to extract at least major.minor
300+ if (preg_match ('/^(\d+\.\d+)/ ' , $ version , $ matches )) {
301+ return $ matches [1 ] . '.0 ' ;
302+ }
303+
304+ // If still no match, try to extract at least major
305+ if (preg_match ('/^(\d+)/ ' , $ version , $ matches )) {
306+ return $ matches [1 ] . '.0.0 ' ;
307+ }
308+
309+ return '' ;
310+ }
171311}
0 commit comments