Verified Commit 22fb1acb authored by Elias Häußler's avatar Elias Häußler 🐛
Browse files

[FEATURE] Use symfony/http-client and nyholm/psr-7 for requests

Resolves: #1
parent 064a4d3e
Pipeline #710 failed with stages
in 1 minute and 46 seconds
...@@ -20,16 +20,20 @@ ...@@ -20,16 +20,20 @@
}, },
"require": { "require": {
"php": "^7.1", "php": "^7.1",
"ext-json": "*",
"composer-plugin-api": "^1.0 || ^2.0", "composer-plugin-api": "^1.0 || ^2.0",
"eliashaeussler/composer-update-check": "~0.4", "eliashaeussler/composer-update-check": "~0.4",
"guzzlehttp/guzzle": "^6.0 || ^7.0", "nyholm/psr7": "^1.0",
"php-http/httplug": "^2.0",
"psr/http-client": "^1.0",
"spatie/emoji": "^2.0", "spatie/emoji": "^2.0",
"symfony/http-client": "^4.4 || ^5.0",
"symfony/mailer": "^4.4 || ^5.0" "symfony/mailer": "^4.4 || ^5.0"
}, },
"require-dev": { "require-dev": {
"composer/composer": "^1.0 || ^2.0", "composer/composer": "^1.0 || ^2.0",
"friendsofphp/php-cs-fixer": "^2.17", "friendsofphp/php-cs-fixer": "^2.17",
"nyholm/psr7": "^1.3", "guzzlehttp/guzzle": "^6.5 || ^7.0",
"phpunit/phpunit": "^7.0 || ^8.0 || ^9.0", "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0",
"rpkamp/mailhog-client": "^0.3 || ^0.4 || ^0.5" "rpkamp/mailhog-client": "^0.3 || ^0.4 || ^0.5"
}, },
......
...@@ -24,13 +24,14 @@ namespace EliasHaeussler\ComposerUpdateReporter\Service; ...@@ -24,13 +24,14 @@ namespace EliasHaeussler\ComposerUpdateReporter\Service;
use Composer\IO\IOInterface; use Composer\IO\IOInterface;
use EliasHaeussler\ComposerUpdateCheck\Package\OutdatedPackage; use EliasHaeussler\ComposerUpdateCheck\Package\OutdatedPackage;
use EliasHaeussler\ComposerUpdateCheck\Package\UpdateCheckResult; use EliasHaeussler\ComposerUpdateCheck\Package\UpdateCheckResult;
use GuzzleHttp\Client; use EliasHaeussler\ComposerUpdateReporter\Traits\RemoteServiceTrait;
use GuzzleHttp\Exception\GuzzleException; use Nyholm\Psr7\Factory\Psr17Factory;
use GuzzleHttp\Psr7\Uri; use Nyholm\Psr7\Uri;
use GuzzleHttp\RequestOptions; use Psr\Http\Client\ClientExceptionInterface;
use Psr\Http\Message\UriInterface; use Psr\Http\Message\UriInterface;
use Spatie\Emoji\Emoji; use Spatie\Emoji\Emoji;
use Spatie\Emoji\Exceptions\UnknownCharacter; use Spatie\Emoji\Exceptions\UnknownCharacter;
use Symfony\Component\HttpClient\Psr18Client;
/** /**
* GitLab * GitLab
...@@ -40,6 +41,8 @@ use Spatie\Emoji\Exceptions\UnknownCharacter; ...@@ -40,6 +41,8 @@ use Spatie\Emoji\Exceptions\UnknownCharacter;
*/ */
class GitLab implements ServiceInterface class GitLab implements ServiceInterface
{ {
use RemoteServiceTrait;
/** /**
* @var UriInterface * @var UriInterface
*/ */
...@@ -50,11 +53,6 @@ class GitLab implements ServiceInterface ...@@ -50,11 +53,6 @@ class GitLab implements ServiceInterface
*/ */
private $authorizationKey; private $authorizationKey;
/**
* @var Client
*/
private $client;
/** /**
* @var bool * @var bool
*/ */
...@@ -64,12 +62,8 @@ class GitLab implements ServiceInterface ...@@ -64,12 +62,8 @@ class GitLab implements ServiceInterface
{ {
$this->uri = $uri; $this->uri = $uri;
$this->authorizationKey = $authorizationKey; $this->authorizationKey = $authorizationKey;
$this->client = new Client([ $this->requestFactory = new Psr17Factory();
'base_uri' => (string) $this->uri, $this->client = new Psr18Client();
RequestOptions::HEADERS => [
'Authorization' => 'Bearer ' . $this->authorizationKey,
],
]);
$this->validateUri(); $this->validateUri();
$this->validateAuthorizationKey(); $this->validateAuthorizationKey();
...@@ -117,7 +111,7 @@ class GitLab implements ServiceInterface ...@@ -117,7 +111,7 @@ class GitLab implements ServiceInterface
/** /**
* @inheritDoc * @inheritDoc
* @throws GuzzleException * @throws ClientExceptionInterface
*/ */
public function report(UpdateCheckResult $result, IOInterface $io): bool public function report(UpdateCheckResult $result, IOInterface $io): bool
{ {
...@@ -141,7 +135,7 @@ class GitLab implements ServiceInterface ...@@ -141,7 +135,7 @@ class GitLab implements ServiceInterface
if (!$this->json) { if (!$this->json) {
$io->write(Emoji::rocket() . ' Sending report to GitLab...'); $io->write(Emoji::rocket() . ' Sending report to GitLab...');
} }
$response = $this->client->post('', [RequestOptions::JSON => $payload]); $response = $this->sendRequest($payload, ['Authorization' => 'Bearer ' . $this->authorizationKey,]);
$successful = $response->getStatusCode() < 400; $successful = $response->getStatusCode() < 400;
// Print report state // Print report state
......
...@@ -25,13 +25,14 @@ use Composer\IO\IOInterface; ...@@ -25,13 +25,14 @@ use Composer\IO\IOInterface;
use EliasHaeussler\ComposerUpdateCheck\Package\OutdatedPackage; use EliasHaeussler\ComposerUpdateCheck\Package\OutdatedPackage;
use EliasHaeussler\ComposerUpdateCheck\Package\UpdateCheckResult; use EliasHaeussler\ComposerUpdateCheck\Package\UpdateCheckResult;
use EliasHaeussler\ComposerUpdateReporter\Traits\PackageProviderLinkTrait; use EliasHaeussler\ComposerUpdateReporter\Traits\PackageProviderLinkTrait;
use GuzzleHttp\Client; use EliasHaeussler\ComposerUpdateReporter\Traits\RemoteServiceTrait;
use GuzzleHttp\Exception\GuzzleException; use Nyholm\Psr7\Factory\Psr17Factory;
use GuzzleHttp\Psr7\Uri; use Nyholm\Psr7\Uri;
use GuzzleHttp\RequestOptions; use Psr\Http\Client\ClientExceptionInterface;
use Psr\Http\Message\UriInterface; use Psr\Http\Message\UriInterface;
use Spatie\Emoji\Emoji; use Spatie\Emoji\Emoji;
use Spatie\Emoji\Exceptions\UnknownCharacter; use Spatie\Emoji\Exceptions\UnknownCharacter;
use Symfony\Component\HttpClient\Psr18Client;
/** /**
* Mattermost * Mattermost
...@@ -42,6 +43,7 @@ use Spatie\Emoji\Exceptions\UnknownCharacter; ...@@ -42,6 +43,7 @@ use Spatie\Emoji\Exceptions\UnknownCharacter;
class Mattermost implements ServiceInterface class Mattermost implements ServiceInterface
{ {
use PackageProviderLinkTrait; use PackageProviderLinkTrait;
use RemoteServiceTrait;
/** /**
* @var UriInterface * @var UriInterface
...@@ -58,11 +60,6 @@ class Mattermost implements ServiceInterface ...@@ -58,11 +60,6 @@ class Mattermost implements ServiceInterface
*/ */
private $username; private $username;
/**
* @var Client
*/
private $client;
/** /**
* @var bool * @var bool
*/ */
...@@ -73,7 +70,8 @@ class Mattermost implements ServiceInterface ...@@ -73,7 +70,8 @@ class Mattermost implements ServiceInterface
$this->uri = $uri; $this->uri = $uri;
$this->channelName = $channelName; $this->channelName = $channelName;
$this->username = $username; $this->username = $username;
$this->client = new Client(['base_uri' => (string) $this->uri]); $this->requestFactory = new Psr17Factory();
$this->client = new Psr18Client();
$this->validateUri(); $this->validateUri();
$this->validateChannelName(); $this->validateChannelName();
...@@ -129,7 +127,7 @@ class Mattermost implements ServiceInterface ...@@ -129,7 +127,7 @@ class Mattermost implements ServiceInterface
/** /**
* @inheritDoc * @inheritDoc
* @throws GuzzleException * @throws ClientExceptionInterface
*/ */
public function report(UpdateCheckResult $result, IOInterface $io): bool public function report(UpdateCheckResult $result, IOInterface $io): bool
{ {
...@@ -161,7 +159,7 @@ class Mattermost implements ServiceInterface ...@@ -161,7 +159,7 @@ class Mattermost implements ServiceInterface
if (!$this->json) { if (!$this->json) {
$io->write(Emoji::rocket() . ' Sending report to Mattermost...'); $io->write(Emoji::rocket() . ' Sending report to Mattermost...');
} }
$response = $this->client->post('', [RequestOptions::JSON => $payload]); $response = $this->sendRequest($payload);
$successful = $response->getStatusCode() < 400; $successful = $response->getStatusCode() < 400;
// Print report state // Print report state
......
...@@ -25,13 +25,14 @@ use Composer\IO\IOInterface; ...@@ -25,13 +25,14 @@ use Composer\IO\IOInterface;
use EliasHaeussler\ComposerUpdateCheck\Package\OutdatedPackage; use EliasHaeussler\ComposerUpdateCheck\Package\OutdatedPackage;
use EliasHaeussler\ComposerUpdateCheck\Package\UpdateCheckResult; use EliasHaeussler\ComposerUpdateCheck\Package\UpdateCheckResult;
use EliasHaeussler\ComposerUpdateReporter\Traits\PackageProviderLinkTrait; use EliasHaeussler\ComposerUpdateReporter\Traits\PackageProviderLinkTrait;
use GuzzleHttp\Client; use EliasHaeussler\ComposerUpdateReporter\Traits\RemoteServiceTrait;
use GuzzleHttp\Exception\GuzzleException; use Nyholm\Psr7\Factory\Psr17Factory;
use GuzzleHttp\Psr7\Uri; use Nyholm\Psr7\Uri;
use GuzzleHttp\RequestOptions; use Psr\Http\Client\ClientExceptionInterface;
use Psr\Http\Message\UriInterface; use Psr\Http\Message\UriInterface;
use Spatie\Emoji\Emoji; use Spatie\Emoji\Emoji;
use Spatie\Emoji\Exceptions\UnknownCharacter; use Spatie\Emoji\Exceptions\UnknownCharacter;
use Symfony\Component\HttpClient\Psr18Client;
/** /**
* Slack * Slack
...@@ -42,17 +43,13 @@ use Spatie\Emoji\Exceptions\UnknownCharacter; ...@@ -42,17 +43,13 @@ use Spatie\Emoji\Exceptions\UnknownCharacter;
class Slack implements ServiceInterface class Slack implements ServiceInterface
{ {
use PackageProviderLinkTrait; use PackageProviderLinkTrait;
use RemoteServiceTrait;
/** /**
* @var UriInterface * @var UriInterface
*/ */
private $uri; private $uri;
/**
* @var Client
*/
private $client;
/** /**
* @var bool * @var bool
*/ */
...@@ -61,7 +58,8 @@ class Slack implements ServiceInterface ...@@ -61,7 +58,8 @@ class Slack implements ServiceInterface
public function __construct(UriInterface $uri) public function __construct(UriInterface $uri)
{ {
$this->uri = $uri; $this->uri = $uri;
$this->client = new Client(['base_uri' => (string) $this->uri]); $this->requestFactory = new Psr17Factory();
$this->client = new Psr18Client();
$this->validateUri(); $this->validateUri();
} }
...@@ -96,7 +94,7 @@ class Slack implements ServiceInterface ...@@ -96,7 +94,7 @@ class Slack implements ServiceInterface
/** /**
* @inheritDoc * @inheritDoc
* @throws GuzzleException * @throws ClientExceptionInterface
*/ */
public function report(UpdateCheckResult $result, IOInterface $io): bool public function report(UpdateCheckResult $result, IOInterface $io): bool
{ {
...@@ -119,7 +117,7 @@ class Slack implements ServiceInterface ...@@ -119,7 +117,7 @@ class Slack implements ServiceInterface
if (!$this->json) { if (!$this->json) {
$io->write(Emoji::rocket() . ' Sending report to Slack...'); $io->write(Emoji::rocket() . ' Sending report to Slack...');
} }
$response = $this->client->post('', [RequestOptions::JSON => $payload]); $response = $this->sendRequest($payload);
$successful = $response->getStatusCode() < 400; $successful = $response->getStatusCode() < 400;
// Print report state // Print report state
......
...@@ -25,12 +25,14 @@ use Composer\IO\IOInterface; ...@@ -25,12 +25,14 @@ use Composer\IO\IOInterface;
use EliasHaeussler\ComposerUpdateCheck\Package\OutdatedPackage; use EliasHaeussler\ComposerUpdateCheck\Package\OutdatedPackage;
use EliasHaeussler\ComposerUpdateCheck\Package\UpdateCheckResult; use EliasHaeussler\ComposerUpdateCheck\Package\UpdateCheckResult;
use EliasHaeussler\ComposerUpdateReporter\Traits\PackageProviderLinkTrait; use EliasHaeussler\ComposerUpdateReporter\Traits\PackageProviderLinkTrait;
use GuzzleHttp\Client; use EliasHaeussler\ComposerUpdateReporter\Traits\RemoteServiceTrait;
use GuzzleHttp\Psr7\Uri; use Nyholm\Psr7\Factory\Psr17Factory;
use GuzzleHttp\RequestOptions; use Nyholm\Psr7\Uri;
use Psr\Http\Client\ClientExceptionInterface;
use Psr\Http\Message\UriInterface; use Psr\Http\Message\UriInterface;
use Spatie\Emoji\Emoji; use Spatie\Emoji\Emoji;
use Spatie\Emoji\Exceptions\UnknownCharacter; use Spatie\Emoji\Exceptions\UnknownCharacter;
use Symfony\Component\HttpClient\Psr18Client;
/** /**
* Teams * Teams
...@@ -41,17 +43,13 @@ use Spatie\Emoji\Exceptions\UnknownCharacter; ...@@ -41,17 +43,13 @@ use Spatie\Emoji\Exceptions\UnknownCharacter;
class Teams implements ServiceInterface class Teams implements ServiceInterface
{ {
use PackageProviderLinkTrait; use PackageProviderLinkTrait;
use RemoteServiceTrait;
/** /**
* @var UriInterface * @var UriInterface
*/ */
private $uri; private $uri;
/**
* @var Client
*/
private $client;
/** /**
* @var bool * @var bool
*/ */
...@@ -60,7 +58,8 @@ class Teams implements ServiceInterface ...@@ -60,7 +58,8 @@ class Teams implements ServiceInterface
public function __construct(UriInterface $uri) public function __construct(UriInterface $uri)
{ {
$this->uri = $uri; $this->uri = $uri;
$this->client = new Client(['base_uri' => (string)$this->uri]); $this->requestFactory = new Psr17Factory();
$this->client = new Psr18Client();
$this->validateUri(); $this->validateUri();
} }
...@@ -93,6 +92,10 @@ class Teams implements ServiceInterface ...@@ -93,6 +92,10 @@ class Teams implements ServiceInterface
return is_array($extra) && (bool)($extra['enable'] ?? false); return is_array($extra) && (bool)($extra['enable'] ?? false);
} }
/**
* @inheritDoc
* @throws ClientExceptionInterface
*/
public function report(UpdateCheckResult $result, IOInterface $io): bool public function report(UpdateCheckResult $result, IOInterface $io): bool
{ {
$outdatedPackages = $result->getOutdatedPackages(); $outdatedPackages = $result->getOutdatedPackages();
...@@ -118,7 +121,7 @@ class Teams implements ServiceInterface ...@@ -118,7 +121,7 @@ class Teams implements ServiceInterface
if (!$this->json) { if (!$this->json) {
$io->write(Emoji::rocket() . ' Sending report to MS Teams...'); $io->write(Emoji::rocket() . ' Sending report to MS Teams...');
} }
$response = $this->client->post('', [RequestOptions::JSON => $payload]); $response = $this->sendRequest($payload);
$successful = $response->getStatusCode() < 400; $successful = $response->getStatusCode() < 400;
// Print report state // Print report state
......
<?php
declare(strict_types=1);
/*
* This file is part of the Composer package "eliashaeussler/composer-update-reporter".
*
* Copyright (C) 2021 Elias Häußler <elias@haeussler.dev>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
namespace EliasHaeussler\ComposerUpdateReporter\Traits;
use Nyholm\Psr7\Stream;
use Psr\Http\Client\ClientExceptionInterface;
use Psr\Http\Client\ClientInterface;
use Psr\Http\Message\RequestFactoryInterface;
use Psr\Http\Message\ResponseInterface;
/**
* RemoteServiceTrait
*
* @author Elias Häußler <elias@haeussler.dev>
* @license GPL-3.0-or-later
*/
trait RemoteServiceTrait
{
/**
* @var RequestFactoryInterface
*/
private $requestFactory;
/**
* @var ClientInterface
*/
private $client;
/**
* @param array $payload
* @param array $headers
* @return ResponseInterface
* @throws ClientExceptionInterface
*/
protected function sendRequest(array $payload, array $headers = []): ResponseInterface
{
$request = $this->requestFactory->createRequest('POST', $this->uri)
->withBody(Stream::create(json_encode($payload)));
$headers = array_merge($headers, ['Accept' => 'application/json']);
foreach ($headers as $name => $value) {
$request = $request->withHeader($name, $value);
}
return $this->client->sendRequest($request);
}
public function setClient(ClientInterface $client): self
{
$this->client = $client;
return $this;
}
}
<?php
declare(strict_types=1);
/*
* This file is part of the Composer package "eliashaeussler/composer-update-reporter".
*
* Copyright (C) 2021 Elias Häußler <elias@haeussler.dev>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
namespace EliasHaeussler\ComposerUpdateReporter\Tests\Unit;
use EliasHaeussler\ComposerUpdateReporter\Util;
use GuzzleHttp\Client;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Middleware;
use Psr\Http\Client\ClientInterface;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
/**
* ClientMockTrait
*
* @author Elias Häußler <elias@haeussler.dev>
* @license GPL-3.0-or-later
*/
trait ClientMockTrait
{
/**
* @var MockHandler
*/
private $mockHandler;
/**
* @var array{request: RequestInterface, response: ResponseInterface|null, error: string|mixed, options: array}
*/
private $requestContainer = [];
private function getClient(): ClientInterface
{
$this->mockHandler = $this->mockHandler ?? new MockHandler();
$this->mockHandler->reset();
$history = Middleware::history($this->requestContainer);
$handlerStack = HandlerStack::create($this->mockHandler);
$handlerStack->push($history);
return new Client(['handler' => $handlerStack]);
}
protected function assertPayloadOfLastRequestContainsSubset(array $expectedPayloadSubset): void
{
$payload = $this->getPayloadOfLastRequest();
self::assertSame([], Util::arrayDiffRecursive($expectedPayloadSubset, $payload));
}
protected function getPayloadOfLastRequest(): array
{
self::assertNotEmpty($this->requestContainer, 'Unable to find last request');
/** @var RequestInterface $request */
$request = end($this->requestContainer)['request'];
self::assertInstanceOf(RequestInterface::class, $request);
$request->getBody()->rewind();
return json_decode($request->getBody()->getContents(), true);
}
}
...@@ -26,13 +26,11 @@ use EliasHaeussler\ComposerUpdateCheck\Package\OutdatedPackage; ...@@ -26,13 +26,11 @@ use EliasHaeussler\ComposerUpdateCheck\Package\OutdatedPackage;
use EliasHaeussler\ComposerUpdateCheck\Package\UpdateCheckResult; use EliasHaeussler\ComposerUpdateCheck\Package\UpdateCheckResult;
use EliasHaeussler\ComposerUpdateReporter\Service\GitLab; use EliasHaeussler\ComposerUpdateReporter\Service\GitLab;
use EliasHaeussler\ComposerUpdateReporter\Tests\Unit\AbstractTestCase; use EliasHaeussler\ComposerUpdateReporter\Tests\Unit\AbstractTestCase;
use EliasHaeussler\ComposerUpdateReporter\Tests\Unit\ClientMockTrait;
use EliasHaeussler\ComposerUpdateReporter\Tests\Unit\TestEnvironmentTrait; use EliasHaeussler\ComposerUpdateReporter\Tests\Unit\TestEnvironmentTrait;
use GuzzleHttp\Client; use Nyholm\Psr7\Response;
use GuzzleHttp\Exception\GuzzleException; use Nyholm\Psr7\Uri;
use GuzzleHttp\Psr7\Response; use Psr\Http\Client\ClientExceptionInterface;
use GuzzleHttp\Psr7\Uri;
use GuzzleHttp\RequestOptions;
use Prophecy\Argument;
/** /**
* GitLabTest * GitLabTest
...@@ -42,6 +40,7 @@ use Prophecy\Argument; ...@@ -42,6 +40,7 @@ use Prophecy\Argument;
*/ */
class GitLabTest extends AbstractTestCase class GitLabTest extends AbstractTestCase
{ {
use ClientMockTrait;
use TestEnvironmentTrait; use TestEnvironmentTrait;
/** /**
...@@ -174,7 +173,7 @@ class GitLabTest extends AbstractTestCase ...@@ -174,7 +173,7 @@ class GitLabTest extends AbstractTestCase
/** /**
* @test * @test
* @throws GuzzleException * @throws ClientExceptionInterface
*/ */
public function reportSkipsReportIfNoPackagesAreOutdated(): void public function reportSkipsReportIfNoPackagesAreOutdated(): void