Compare commits
	
		
			No commits in common. "master" and "feature_usetreno_api" have entirely different histories.
		
	
	
		
			master
			...
			feature_us
		
	
		
							
								
								
									
										30
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										30
									
								
								README.md
									
									
									
									
									
								
							| @ -1,34 +1,6 @@ | ||||
| symfony_example_app | ||||
| =================== | ||||
| 
 | ||||
| Generátor QR kódů pro platby z https://topapi.top-test.cz | ||||
| 
 | ||||
| Quick setup | ||||
| ----------- | ||||
| ```bash | ||||
| # git clone https://git.nanobyte.cz/nanobyte-public/symfony_example_app.git | ||||
| # cd symfony_example_app | ||||
| # docker compose run --build php-fpm composer install | ||||
| # docker compose up --build | ||||
| ``` | ||||
| 
 | ||||
| - aplikace je dostupná na http://localhost:8000/ | ||||
| - na adrese http://localhost:3000/explore je běžící grafana (s loki a tempo) | ||||
| - aplikace je nastavená (i na lokálu, běžně bych to nastavil až na devech/stage/PROD) aby posílala logy do lokiho a tracing do tempa pomocí otel protokolu | ||||
| 
 | ||||
| TODO: | ||||
| ----- | ||||
| - [ ] Chybí speciální slovník nebo vypnutí slovníku pro testy | ||||
| - [ ] V reálný aplikaci bych použil Mockery, nicméně tady mě to přijde zbytečný | ||||
| - [ ] Nastavení cache ideálně v memcached/redis etc. | ||||
| - [ ] Vyhezkat OTEL logs, OTEL tracing | ||||
| - [ ] CI pipelines | ||||
| - [ ] k8s deployment | ||||
| - [ ] prometheus country na počet requestů/api (počet 200OK/500ERR) | ||||
| - [ ] Je dost na zvážení zda nezobrazit výsledek remote validace (response_create_400.json) a nenechat uživatele špatné hodnoty opravit. Ovšem znamená to že lokální validátory jsou špatně, chybu by bylo vhodné zalogovat do Sentry (do logu etc.) a opravit ji... | ||||
| - [ ] Na produkci bych statický content rozhodně netlačil přes app container ale přes static nginx container (asset-map:compile -> copy do nginx static containeru) | ||||
| - [ ] xdebug v dockeru | ||||
| 
 | ||||
| Poznámky | ||||
| -------- | ||||
| - Nejsem kodér (a javascript developer), nevypadá to nijak extra ;-) | ||||
| - [ ] Vylepsit OTEL logs | ||||
|  | ||||
| @ -31,7 +31,7 @@ services: | ||||
|         arguments: | ||||
|             $available_currencies: '%app.currencies%' | ||||
| 
 | ||||
|     App\Service\StubQRCodeProvider: | ||||
|     App\Service\StubQRCodeGenerator: | ||||
|         arguments: | ||||
|             $imagePath: '%kernel.project_dir%/assets/images/wip.png' | ||||
| 
 | ||||
| @ -47,7 +47,7 @@ services: | ||||
|             $retryCount: 2 | ||||
|             $retryWaitSeconds: 0.5 | ||||
| 
 | ||||
|     App\Service\CachedQRCodeProvider: | ||||
|     App\Service\CachedQRCodeGenerator: | ||||
|         arguments: | ||||
|             $innerGenerator: '@App\Service\Remote\UsetrenoQRCodeProvider' | ||||
|             $cacheDuration: 'PT60S' | ||||
| @ -55,7 +55,7 @@ services: | ||||
| 
 | ||||
|     App\Service\CurrencyListerInterface: '@App\Service\StaticCurrencyLister' | ||||
|     App\Service\QRCodeQROptionsProviderInterface: '@App\Service\QRCodeQROptionsDefaultProvider' | ||||
|     App\Service\QRCodeProviderInterface: '@App\Service\CachedQRCodeProvider' | ||||
|     App\Service\QRCodeGeneratorInterface: '@App\Service\CachedQRCodeGenerator' | ||||
| 
 | ||||
|     # add more service definitions when explicit configuration is needed | ||||
|     # please note that last definitions always *replace* previous ones | ||||
|  | ||||
| @ -5,7 +5,7 @@ namespace App\Controller; | ||||
| use App\Entity\Input\QRCode\QRCode; | ||||
| use App\Form\Type\QRCodeType; | ||||
| use App\Service\DTO\QRCodeEntityConverter; | ||||
| use App\Service\QRCodeProviderInterface; | ||||
| use App\Service\QRCodeGeneratorInterface; | ||||
| use App\Service\QRCodeQROptionsProviderInterface; | ||||
| use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; | ||||
| use Symfony\Component\HttpFoundation\Request; | ||||
| @ -17,7 +17,7 @@ class IndexController extends AbstractController { | ||||
|     #[Route('/', name: 'homepage')]
 | ||||
|     public function indexAction( | ||||
|         Request $request, | ||||
|         QRCodeProviderInterface $qrCodeGenerator, | ||||
|         QRCodeGeneratorInterface $qrCodeGenerator, | ||||
|     ): Response | ||||
|     { | ||||
|         $qrCodeImage = null; | ||||
|  | ||||
| @ -23,7 +23,7 @@ class QRCodeMoneyType extends AbstractType | ||||
|     public function buildForm(FormBuilderInterface $builder, array $options): void | ||||
|     { | ||||
|         $builder | ||||
|             ->add('amount', NumberType::class, ['invalid_message' => 'messages.not_valid_amount']) | ||||
|             ->add('amount', NumberType::class) | ||||
|             ->add('currency', ChoiceType::class, [ | ||||
|                 'choices' => array_combine( | ||||
|                     iterator_to_array($this->currencyLister->getCurrencies()), | ||||
|  | ||||
| @ -5,7 +5,7 @@ namespace App\Service; | ||||
| 
 | ||||
| use App\Entity\DTO\QRCode\QRCode; | ||||
| 
 | ||||
| interface CacheableQRCodeProviderInterface extends QRCodeProviderInterface | ||||
| interface CacheableQRCodeGeneratorInterface extends QRCodeGeneratorInterface | ||||
| { | ||||
|     // We can't calculate cache key directly from QRCode
 | ||||
|     public function getCacheKey(QRCode $entity): string; | ||||
| @ -10,14 +10,14 @@ use Symfony\Contracts\Cache\CacheInterface; | ||||
| use Symfony\Contracts\Cache\ItemInterface; | ||||
| 
 | ||||
| 
 | ||||
| final readonly class CachedQRCodeProvider implements QRCodeProviderInterface | ||||
| final readonly class CachedQRCodeGenerator implements QRCodeGeneratorInterface | ||||
| { | ||||
|     private \DateInterval $cacheDuration; | ||||
| 
 | ||||
|     /** | ||||
|      * @throws Exception | ||||
|      */ | ||||
|     public function __construct(private CacheableQRCodeProviderInterface $innerGenerator, private CacheInterface $cache, private LoggerInterface $logger, string $cacheDuration) | ||||
|     public function __construct(private CacheableQRCodeGeneratorInterface $innerGenerator, private CacheInterface $cache, private LoggerInterface $logger, string $cacheDuration) | ||||
|     { | ||||
|         $this->cacheDuration = new \DateInterval($cacheDuration); | ||||
|     } | ||||
| @ -6,7 +6,6 @@ use App\Entity\DTO\QRCode\QRCode; | ||||
| use App\Entity\DTO\QRCode\QRCodeMoney; | ||||
| use App\Entity\DTO\QRCode\QRCodePaymentIdentification; | ||||
| use App\Entity\DTO\QRCode\QRCodeQROptions; | ||||
| use Nubium\Exception\ThisShouldNeverHappenException; | ||||
| use Symfony\Component\Validator\Validator\ValidatorInterface; | ||||
| 
 | ||||
| final readonly class QRCodeEntityConverter | ||||
| @ -21,11 +20,11 @@ final readonly class QRCodeEntityConverter | ||||
|         } | ||||
| 
 | ||||
|         return new QRCode( | ||||
|             $code->getIban() ?? throw new ThisShouldNeverHappenException("iban not set"), | ||||
|             \DateTimeImmutable::createFromMutable($code->getDueDate() ?? throw new ThisShouldNeverHappenException("due date not set")), | ||||
|             $code->getMessage() ?? throw new ThisShouldNeverHappenException("message not set"), | ||||
|             $this->convertMoney($code->getMoney() ?? throw new ThisShouldNeverHappenException("money not set")), | ||||
|             $this->convertCodeOptions($code->getCodeQROptions() ?? throw new ThisShouldNeverHappenException("codeQROptions not set")), | ||||
|             $code->getIban() ?? throw new \InvalidArgumentException("iban not set"), | ||||
|             \DateTimeImmutable::createFromMutable($code->getDueDate() ?? throw new \InvalidArgumentException("due date not set")), | ||||
|             $code->getMessage() ?? throw new \InvalidArgumentException("message not set"), | ||||
|             $this->convertMoney($code->getMoney() ?? throw new \InvalidArgumentException("money not set")), | ||||
|             $this->convertCodeOptions($code->getCodeQROptions() ?? throw new \InvalidArgumentException("codeQROptions not set")), | ||||
|             $this->convertIdentification($code->getPaymentIdentification()) | ||||
|         ); | ||||
|     } | ||||
| @ -33,8 +32,8 @@ final readonly class QRCodeEntityConverter | ||||
|     protected function convertMoney(\App\Entity\Input\QRCode\QRCodeMoney $money): QRCodeMoney | ||||
|     { | ||||
|         return new QRCodeMoney( | ||||
|             $money->getAmount() ?? throw new ThisShouldNeverHappenException("amount not set"), | ||||
|             $money->getCurrency() ?? throw new ThisShouldNeverHappenException("currency not set") | ||||
|             $money->getAmount() ?? throw new \InvalidArgumentException("amount not set"), | ||||
|             $money->getCurrency() ?? throw new \InvalidArgumentException("currency not set") | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -6,7 +6,7 @@ namespace App\Service; | ||||
| use App\Entity\DTO\QRCode\QRCode; | ||||
| use App\Service\Exception\QRCodeGeneratorException; | ||||
| 
 | ||||
| interface QRCodeProviderInterface | ||||
| interface QRCodeGeneratorInterface | ||||
| { | ||||
|     /** | ||||
|      * Generates QR code from entity | ||||
| @ -4,7 +4,7 @@ declare(strict_types=1); | ||||
| namespace App\Service\Remote; | ||||
| 
 | ||||
| use App\Entity\DTO\QRCode\QRCode; | ||||
| use App\Service\CacheableQRCodeProviderInterface; | ||||
| use App\Service\CacheableQRCodeGeneratorInterface; | ||||
| use App\Service\Remote\Edge\QRCodeEntityConverter; | ||||
| use App\Service\Remote\Exception\UsetrenoQRCodeException; | ||||
| use App\Service\Remote\Exception\UsetrenoQRCodeRemoteServerErrorException; | ||||
| @ -12,7 +12,7 @@ use Nubium\Exception\ThisShouldNeverHappenException; | ||||
| use Psr\Log\LoggerInterface; | ||||
| use Symfony\Component\HttpClient\Exception\TransportException; | ||||
| 
 | ||||
| class UsetrenoQRCodeProvider implements CacheableQRCodeProviderInterface | ||||
| class UsetrenoQRCodeProvider implements CacheableQRCodeGeneratorInterface | ||||
| { | ||||
|     use RetryingFailClientTrait; | ||||
| 
 | ||||
|  | ||||
| @ -5,7 +5,7 @@ namespace App\Service; | ||||
| 
 | ||||
| use App\Entity\DTO\QRCode\QRCode; | ||||
| 
 | ||||
| readonly final class StubQRCodeProvider implements QRCodeProviderInterface | ||||
| readonly final class StubQRCodeGenerator implements QRCodeGeneratorInterface | ||||
| { | ||||
|     public function __construct(private string $imagePath) {} | ||||
| 
 | ||||
| @ -1,12 +0,0 @@ | ||||
| {% extends 'base.html.twig' %} | ||||
| 
 | ||||
| {% block body %} | ||||
|     <p> | ||||
|         <div class="container"> | ||||
|             <div style="text-align: center;"> | ||||
|                 <h1>{{ "page not found" | trans }}</h1> | ||||
|                 <a href="{{ path('homepage') }}">{{ "return to the homepage" | trans }}</a> | ||||
|             </div> | ||||
|         </div> | ||||
|     </p> | ||||
| {% endblock %} | ||||
| @ -1,13 +0,0 @@ | ||||
| {% extends 'base.html.twig' %} | ||||
| 
 | ||||
| {% block body %} | ||||
|     <p> | ||||
|     <div class="container"> | ||||
|         <div style="text-align: center;"> | ||||
|             <h1>{{ "internal server error" | trans }}</h1> | ||||
|             {{ "our developers are doing their best to fix it. please try again later." | trans }} | ||||
|             <a href="{{ path('homepage') }}">{{ "return to the homepage" | trans }}</a> | ||||
|         </div> | ||||
|     </div> | ||||
|     </p> | ||||
| {% endblock %} | ||||
| @ -3,7 +3,7 @@ declare(strict_types=1); | ||||
| 
 | ||||
| namespace App\Tests\Controller; | ||||
| 
 | ||||
| use App\Service\QRCodeProviderInterface; | ||||
| use App\Service\QRCodeGeneratorInterface; | ||||
| use Symfony\Bundle\FrameworkBundle\KernelBrowser; | ||||
| use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; | ||||
| 
 | ||||
| @ -76,7 +76,7 @@ class IndexControllerTest extends WebTestCase | ||||
| 
 | ||||
|     private function generateClientForSubmitTest(): KernelBrowser | ||||
|     { | ||||
|         $mock = $this->createMock(QRCodeProviderInterface::class); | ||||
|         $mock = $this->createMock(QRCodeGeneratorInterface::class); | ||||
|         $mock->expects($this->any()) | ||||
|             ->method('generateQRCodeFromEntity') | ||||
|             ->will($this->returnValue('foo')); | ||||
| @ -85,7 +85,7 @@ class IndexControllerTest extends WebTestCase | ||||
|         $client = static::createClient(); | ||||
|         $client->disableReboot(); | ||||
| 
 | ||||
|         self::getContainer()->set('App\Service\QRCodeProviderInterface', $mock); | ||||
|         self::getContainer()->set('App\Service\QRCodeGeneratorInterface', $mock); | ||||
| 
 | ||||
|         return $client; | ||||
|     } | ||||
|  | ||||
| @ -9,7 +9,3 @@ Generate qr code: "Vygenerovat QR kód" | ||||
| Iban: "IBAN" | ||||
| QR code generator: "Generátor bankovních QR kódů" | ||||
| You can generate another QR code here: "Další QR kód si můžete vygenerovat zde" | ||||
| page not found: 'Stránka nenalezena' | ||||
| return to the homepage: 'Zpět na homepage' | ||||
| "our developers are doing their best to fix it. please try again later.": "Naši programátoři dělají maximum aby to opravili. Zkuste to prosím později." | ||||
| internal server error: 'Něco se pokazilo :-(' | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user