Compare commits
	
		
			1 Commits
		
	
	
		
			70080bf80a
			...
			acd9bfc2b3
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| acd9bfc2b3 | 
@ -10,7 +10,6 @@
 | 
			
		||||
        "ext-iconv": "*",
 | 
			
		||||
        "grpc/grpc": "^1.57",
 | 
			
		||||
        "guzzlehttp/promises": "*",
 | 
			
		||||
        "nubium/this-should-never-happen-exception": "^1.0",
 | 
			
		||||
        "nyholm/psr7": "*",
 | 
			
		||||
        "open-telemetry/exporter-otlp": "^1.0",
 | 
			
		||||
        "open-telemetry/opentelemetry-auto-symfony": "^1.0@beta",
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										43
									
								
								composer.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										43
									
								
								composer.lock
									
									
									
										generated
									
									
									
								
							@ -4,7 +4,7 @@
 | 
			
		||||
        "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
 | 
			
		||||
        "This file is @generated automatically"
 | 
			
		||||
    ],
 | 
			
		||||
    "content-hash": "60708df22ad1ee4eca712d46f8e02c3b",
 | 
			
		||||
    "content-hash": "61ab226932ff1cf0567a22bbf802e20c",
 | 
			
		||||
    "packages": [
 | 
			
		||||
        {
 | 
			
		||||
            "name": "clue/stream-filter",
 | 
			
		||||
@ -658,47 +658,6 @@
 | 
			
		||||
            ],
 | 
			
		||||
            "time": "2023-10-27T15:32:31+00:00"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "nubium/this-should-never-happen-exception",
 | 
			
		||||
            "version": "v1.0",
 | 
			
		||||
            "source": {
 | 
			
		||||
                "type": "git",
 | 
			
		||||
                "url": "https://github.com/nubium/this-should-never-happen-exception.git",
 | 
			
		||||
                "reference": "3ed1b6f725881c527050c235e2503a8300427b86"
 | 
			
		||||
            },
 | 
			
		||||
            "dist": {
 | 
			
		||||
                "type": "zip",
 | 
			
		||||
                "url": "https://api.github.com/repos/nubium/this-should-never-happen-exception/zipball/3ed1b6f725881c527050c235e2503a8300427b86",
 | 
			
		||||
                "reference": "3ed1b6f725881c527050c235e2503a8300427b86",
 | 
			
		||||
                "shasum": ""
 | 
			
		||||
            },
 | 
			
		||||
            "require-dev": {
 | 
			
		||||
                "jakub-onderka/php-parallel-lint": "~1.0",
 | 
			
		||||
                "phpstan/phpstan": "~0.9"
 | 
			
		||||
            },
 | 
			
		||||
            "type": "library",
 | 
			
		||||
            "autoload": {
 | 
			
		||||
                "psr-4": {
 | 
			
		||||
                    "Nubium\\": "src/"
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            "notification-url": "https://packagist.org/downloads/",
 | 
			
		||||
            "license": [
 | 
			
		||||
                "MIT"
 | 
			
		||||
            ],
 | 
			
		||||
            "authors": [
 | 
			
		||||
                {
 | 
			
		||||
                    "name": "Jiri Travnicek",
 | 
			
		||||
                    "email": "jiri.travnicek@nubium.cz"
 | 
			
		||||
                }
 | 
			
		||||
            ],
 | 
			
		||||
            "description": "Extend this exception and throw it anytime something unexpected happens.",
 | 
			
		||||
            "support": {
 | 
			
		||||
                "issues": "https://github.com/nubium/this-should-never-happen-exception/issues",
 | 
			
		||||
                "source": "https://github.com/nubium/this-should-never-happen-exception/tree/master"
 | 
			
		||||
            },
 | 
			
		||||
            "time": "2018-03-27T10:16:09+00:00"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "nyholm/psr7",
 | 
			
		||||
            "version": "1.8.1",
 | 
			
		||||
 | 
			
		||||
@ -39,13 +39,6 @@ services:
 | 
			
		||||
        arguments:
 | 
			
		||||
            $username: '%app.usetreno.username%'
 | 
			
		||||
            $password: '%app.usetreno.password%'
 | 
			
		||||
            $retryCount: 2
 | 
			
		||||
            $retryWaitSeconds: 0.5
 | 
			
		||||
 | 
			
		||||
    App\Service\Remote\UsetrenoQRCodeProvider:
 | 
			
		||||
        arguments:
 | 
			
		||||
            $retryCount: 2
 | 
			
		||||
            $retryWaitSeconds: 0.5
 | 
			
		||||
 | 
			
		||||
    App\Service\CachedQRCodeGenerator:
 | 
			
		||||
        arguments:
 | 
			
		||||
 | 
			
		||||
@ -11,7 +11,7 @@ use Symfony\Component\Routing\Attribute\Route;
 | 
			
		||||
class ExceptionController extends AbstractController
 | 
			
		||||
{
 | 
			
		||||
    #[Route("give-me-error-please/exception")]
 | 
			
		||||
    public function makeException(): void {
 | 
			
		||||
    public function makeException() {
 | 
			
		||||
        throw new \InvalidArgumentException("There is exception");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,4 @@
 | 
			
		||||
<?php
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace App\Entity\Remote\Usetreno;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,9 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace App\Service\Remote\Exception;
 | 
			
		||||
 | 
			
		||||
class UsetrenoQRCodeRemoteServerErrorException extends UsetrenoQRCodeException
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -1,59 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace App\Service\Remote;
 | 
			
		||||
 | 
			
		||||
use App\Service\Remote\Exception\AuthorizeException;
 | 
			
		||||
use Exception;
 | 
			
		||||
use Psr\Log\LoggerInterface;
 | 
			
		||||
use Symfony\Component\HttpClient\Exception\TransportException;
 | 
			
		||||
 | 
			
		||||
trait RetryingFailClientTrait
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * Lepsi zopakovat request kvuli drobnemu vypadku site nebo sluzby (u nedestruktivni operace)
 | 
			
		||||
     * nez hodit klientovi rovnou 500
 | 
			
		||||
     *
 | 
			
		||||
     * @param int $count
 | 
			
		||||
     * @param float $sleep
 | 
			
		||||
     * @param array<string> $catchableExceptions
 | 
			
		||||
     * @param LoggerInterface $logger
 | 
			
		||||
     * @param callable $callback
 | 
			
		||||
     * @return mixed
 | 
			
		||||
     * @throws Exception
 | 
			
		||||
     */
 | 
			
		||||
    protected function retryingFailRequest(
 | 
			
		||||
        int $count,
 | 
			
		||||
        float $sleep,
 | 
			
		||||
        array $catchableExceptions,
 | 
			
		||||
        LoggerInterface $logger,
 | 
			
		||||
        callable $callback
 | 
			
		||||
    ): mixed {
 | 
			
		||||
        for ($i = 0; ; $i++) {
 | 
			
		||||
            try {
 | 
			
		||||
                return $callback();
 | 
			
		||||
            } catch (Exception $e) {
 | 
			
		||||
                foreach ($catchableExceptions as $exceptionClass) {
 | 
			
		||||
                    if ($e instanceof $exceptionClass) {
 | 
			
		||||
                        $logger->error("transport: fail request retrying... got catchable exception", [
 | 
			
		||||
                            'exception' => $e,
 | 
			
		||||
                            'try' => $i
 | 
			
		||||
                        ]);
 | 
			
		||||
 | 
			
		||||
                        usleep((int) ($sleep * 1_000_000));
 | 
			
		||||
 | 
			
		||||
                        if ($i == $count) {
 | 
			
		||||
                            throw $e;
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        continue 2;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                throw $e;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // phpstan fail
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -5,9 +5,7 @@ namespace App\Service\Remote;
 | 
			
		||||
 | 
			
		||||
use App\Entity\Remote\Usetreno\AuthRequest;
 | 
			
		||||
use App\Service\Remote\Exception\AuthorizeException;
 | 
			
		||||
use Nubium\Exception\ThisShouldNeverHappenException;
 | 
			
		||||
use Psr\Log\LoggerInterface;
 | 
			
		||||
use Symfony\Component\HttpClient\Exception\TransportException;
 | 
			
		||||
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
 | 
			
		||||
use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
 | 
			
		||||
use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
 | 
			
		||||
@ -18,16 +16,12 @@ use Symfony\Contracts\HttpClient\ResponseStreamInterface;
 | 
			
		||||
 | 
			
		||||
class UsetrenoHttpClient implements HttpClientInterface
 | 
			
		||||
{
 | 
			
		||||
    use RetryingFailClientTrait;
 | 
			
		||||
 | 
			
		||||
    const AUTHORIZE_API = "https://topapi.top-test.cz/chameleon/api/v1/token";
 | 
			
		||||
 | 
			
		||||
    protected ?string $authorizationToken = null;
 | 
			
		||||
 | 
			
		||||
    public function __construct(protected HttpClientInterface $innerClient, protected readonly LoggerInterface $logger, protected readonly string $username,
 | 
			
		||||
                                protected readonly string $password,
 | 
			
		||||
                                protected readonly float $retryWaitSeconds,
 | 
			
		||||
                                protected readonly int $retryCount) { }
 | 
			
		||||
                                protected readonly string $password) { }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param string $method
 | 
			
		||||
@ -94,8 +88,6 @@ class UsetrenoHttpClient implements HttpClientInterface
 | 
			
		||||
            "AUTHORIZE_API" => static::AUTHORIZE_API
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        $responseData = $this->retryingFailRequest($this->retryCount, $this->retryWaitSeconds,
 | 
			
		||||
            [AuthorizeException::class, TransportException::class], $this->logger, function() {
 | 
			
		||||
        $rq = $this->innerClient->request(
 | 
			
		||||
            "POST",
 | 
			
		||||
            static::AUTHORIZE_API,
 | 
			
		||||
@ -118,13 +110,6 @@ class UsetrenoHttpClient implements HttpClientInterface
 | 
			
		||||
            throw new AuthorizeException("Return code is not 200 OK (got: code: $statusCode)");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
            return $responseData;
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        if (!is_string($responseData)) {
 | 
			
		||||
            throw new ThisShouldNeverHappenException("responseData is not a string");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $this->authorizationToken = $this->processAuthorizeResponse($responseData);
 | 
			
		||||
        $this->innerClient = $this->innerClient->withOptions([
 | 
			
		||||
            'headers' => [
 | 
			
		||||
 | 
			
		||||
@ -7,23 +7,16 @@ use App\Entity\DTO\QRCode\QRCode;
 | 
			
		||||
use App\Service\CacheableQRCodeGeneratorInterface;
 | 
			
		||||
use App\Service\Remote\Edge\QRCodeEntityConverter;
 | 
			
		||||
use App\Service\Remote\Exception\UsetrenoQRCodeException;
 | 
			
		||||
use App\Service\Remote\Exception\UsetrenoQRCodeRemoteServerErrorException;
 | 
			
		||||
use Nubium\Exception\ThisShouldNeverHappenException;
 | 
			
		||||
use Psr\Log\LoggerInterface;
 | 
			
		||||
use Symfony\Component\HttpClient\Exception\TransportException;
 | 
			
		||||
 | 
			
		||||
class UsetrenoQRCodeProvider implements CacheableQRCodeGeneratorInterface
 | 
			
		||||
{
 | 
			
		||||
    use RetryingFailClientTrait;
 | 
			
		||||
 | 
			
		||||
    const QRCODE_API = 'https://topapi.top-test.cz/chameleon/api/v1/qr-code/create-for-bank-account-payment';
 | 
			
		||||
    const QRCODE_METHOD = 'POST';
 | 
			
		||||
 | 
			
		||||
    public function __construct(protected readonly LoggerInterface $logger,
 | 
			
		||||
                                protected readonly UsetrenoHttpClient $client,
 | 
			
		||||
                                protected readonly QRCodeEntityConverter $codeEntityConverter,
 | 
			
		||||
                                protected readonly float $retryWaitSeconds,
 | 
			
		||||
                                protected readonly int $retryCount) { }
 | 
			
		||||
                                protected readonly QRCodeEntityConverter $codeEntityConverter) { }
 | 
			
		||||
 | 
			
		||||
    public function generateQRCodeFromEntity(QRCode $entity): string
 | 
			
		||||
    {
 | 
			
		||||
@ -34,9 +27,6 @@ class UsetrenoQRCodeProvider implements CacheableQRCodeGeneratorInterface
 | 
			
		||||
            "edgeEntity" => $edgeEntity
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        $responseData = $this->retryingFailRequest($this->retryCount, $this->retryWaitSeconds,
 | 
			
		||||
            [TransportException::class, UsetrenoQRCodeRemoteServerErrorException::class], $this->logger,
 | 
			
		||||
            function() use ($edgeEntity) {
 | 
			
		||||
        $response = $this->client->request(static::QRCODE_METHOD, static::QRCODE_API, [
 | 
			
		||||
            'json' => $edgeEntity,
 | 
			
		||||
        ]);
 | 
			
		||||
@ -51,10 +41,6 @@ class UsetrenoQRCodeProvider implements CacheableQRCodeGeneratorInterface
 | 
			
		||||
                "content" => $responseData,
 | 
			
		||||
            ]);
 | 
			
		||||
 | 
			
		||||
                    if ($statusCode > 500) {
 | 
			
		||||
                        throw new UsetrenoQRCodeRemoteServerErrorException("Return code is not 200 OK (got: code: $statusCode)");
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
            throw new UsetrenoQRCodeException("Return code is not 200 OK (got: code: $statusCode)");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -62,13 +48,6 @@ class UsetrenoQRCodeProvider implements CacheableQRCodeGeneratorInterface
 | 
			
		||||
            "responseContent" => $responseData,
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
                return $responseData;
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        if (!is_string($responseData)) {
 | 
			
		||||
            throw new ThisShouldNeverHappenException("responseData is not a string");
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        return $this->parseBase64String($this->processQRCodeResponseEntity($responseData));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,56 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace App\Tests\Service\Remote;
 | 
			
		||||
 | 
			
		||||
use App\Service\Remote\RetryingFailClientTrait;
 | 
			
		||||
use App\Tests\Common\LoggerTrait;
 | 
			
		||||
use PHPUnit\Framework\TestCase;
 | 
			
		||||
use Psr\Log\LoggerInterface;
 | 
			
		||||
 | 
			
		||||
class RetryingFailClientTraitTest extends TestCase {
 | 
			
		||||
    use LoggerTrait;
 | 
			
		||||
 | 
			
		||||
    private int $callCount = 0;
 | 
			
		||||
 | 
			
		||||
    protected function setUp(): void
 | 
			
		||||
    {
 | 
			
		||||
        parent::setUp(); // TODO: Change the autogenerated stub
 | 
			
		||||
        $this->callCount = 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testSuccess() {
 | 
			
		||||
        $trait = new class {
 | 
			
		||||
            use RetryingFailClientTrait {
 | 
			
		||||
                retryingFailRequest as public; // make the method public
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        $result = $trait->retryingFailRequest(2, 0, [\RuntimeException::class], $this->getLogger(), function () {
 | 
			
		||||
            $this->callCount = $this->callCount + 1;
 | 
			
		||||
            return 'foo';
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        $this->assertEquals(1, $this->callCount);
 | 
			
		||||
        $this->assertEquals('foo', $result);
 | 
			
		||||
    }
 | 
			
		||||
    public function testRetyingFail() {
 | 
			
		||||
        $trait = new class {
 | 
			
		||||
            use RetryingFailClientTrait {
 | 
			
		||||
                retryingFailRequest as public; // make the method public
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            $trait->retryingFailRequest(2, 0, [\RuntimeException::class], $this->getLogger(), function () {
 | 
			
		||||
                $this->callCount = $this->callCount + 1;
 | 
			
		||||
                throw new \RuntimeException("test");
 | 
			
		||||
            });
 | 
			
		||||
        } catch (\RuntimeException) {
 | 
			
		||||
            // do nothing
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $this->assertEquals(3, $this->callCount);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -24,7 +24,7 @@ class UsetrenoHttpClientTest extends TestCase
 | 
			
		||||
        ]);
 | 
			
		||||
        $authorizedRequestResponse = clone $authMockResponse;
 | 
			
		||||
        $mockedClient = new MockHttpClient([$authMockResponse, $authorizedRequestResponse]);
 | 
			
		||||
        $client = new UsetrenoHttpClient($mockedClient, $this->getLogger(), "foo", "bar", 0, 0);
 | 
			
		||||
        $client = new UsetrenoHttpClient($mockedClient, $this->getLogger(), "foo", "bar");
 | 
			
		||||
        $client->request("POST", "https://www.root.cz/");
 | 
			
		||||
        $this->assertEquals("https://topapi.top-test.cz/chameleon/api/v1/token", $authMockResponse->getRequestUrl());
 | 
			
		||||
        $headers = $authorizedRequestResponse->getRequestOptions()['headers'];
 | 
			
		||||
@ -48,7 +48,7 @@ class UsetrenoHttpClientTest extends TestCase
 | 
			
		||||
 | 
			
		||||
        $authorizedRequestResponse = clone $authMockResponse;
 | 
			
		||||
        $mockedClient = new MockHttpClient([$authMockResponse, $authorizedRequestResponse]);
 | 
			
		||||
        $client = new UsetrenoHttpClient($mockedClient, $this->getLogger(), "foo", "bar", 0, 0);
 | 
			
		||||
        $client = new UsetrenoHttpClient($mockedClient, $this->getLogger(), "foo", "bar");
 | 
			
		||||
        $client->request("POST", "https://www.root.cz/");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -75,7 +75,7 @@ class UsetrenoQRCodeProviderTest extends TestCase
 | 
			
		||||
            ->method('convert')
 | 
			
		||||
            ->will($this->returnValue($edgeEntity));
 | 
			
		||||
 | 
			
		||||
        return new UsetrenoQRCodeProvider($this->getLogger(), $mock, $converterMock, 0, 0);
 | 
			
		||||
        return new UsetrenoQRCodeProvider($this->getLogger(), $mock, $converterMock);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function createQRCodeEntityPair() {
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user