generated from dev/symfony-5-cli-template
29 changed files with 1740 additions and 424 deletions
-
8README.md
-
14composer.json
-
1639composer.lock
-
2config/bundles.php
-
11config/packages/dev/monolog.yaml
-
7config/packages/prod/deprecations.yaml
-
16config/packages/prod/monolog.yaml
-
12config/packages/test/monolog.yaml
-
2config/packages/test/twig.yaml
-
2config/packages/twig.yaml
-
1config/services.yaml
-
2devops/dist/.env-dist
-
4devops/docker/.env
-
4devops/docker/php/Dockerfile
-
1devops/docker/redis/redis.conf
-
29docker-compose.yaml
-
42src/Controller/IndexController.php
-
24src/Form/WeatherFormType.php
-
57src/Service/Endpoint/WeatherHistory.php
-
14src/Service/Endpoint/WeatherHistoryInterface.php
-
59symfony.lock
-
66templates/base.html.twig
-
10tests/_support/ApiTester.php
-
9tests/_support/Helper/Api.php
-
2tests/acceptance.suite.yml
-
60tests/acceptance/Controller/IndexControllerCest.php
-
23tests/acceptance/Controller/PageControllerCest.php
-
8tests/api.suite.yml
-
36tests/api/Controller/PageControllerCest.php
1639
composer.lock
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,11 @@ |
|||
monolog: |
|||
handlers: |
|||
main: |
|||
type: stream |
|||
path: "%kernel.logs_dir%/%kernel.environment%.log" |
|||
level: debug |
|||
channels: ["!event"] |
|||
console: |
|||
type: console |
|||
process_psr_3_messages: false |
|||
channels: ["!event", "!doctrine", "!console"] |
@ -0,0 +1,7 @@ |
|||
monolog: |
|||
channels: [deprecation] |
|||
handlers: |
|||
deprecation: |
|||
type: stream |
|||
channels: [deprecation] |
|||
path: "%kernel.logs_dir%/%kernel.environment%.deprecations.log" |
@ -0,0 +1,16 @@ |
|||
monolog: |
|||
handlers: |
|||
main: |
|||
type: fingers_crossed |
|||
action_level: error |
|||
handler: nested |
|||
excluded_http_codes: [404, 405] |
|||
buffer_size: 50 |
|||
nested: |
|||
type: stream |
|||
path: "%kernel.logs_dir%/%kernel.environment%.log" |
|||
level: debug |
|||
console: |
|||
type: console |
|||
process_psr_3_messages: false |
|||
channels: ["!event", "!doctrine"] |
@ -0,0 +1,12 @@ |
|||
monolog: |
|||
handlers: |
|||
main: |
|||
type: fingers_crossed |
|||
action_level: error |
|||
handler: nested |
|||
excluded_http_codes: [404, 405] |
|||
channels: ["!event"] |
|||
nested: |
|||
type: stream |
|||
path: "%kernel.logs_dir%/%kernel.environment%.log" |
|||
level: debug |
@ -0,0 +1,2 @@ |
|||
twig: |
|||
strict_variables: true |
@ -0,0 +1,2 @@ |
|||
twig: |
|||
default_path: '%kernel.project_dir%/templates' |
@ -1,4 +1,4 @@ |
|||
APP_ENV=dev |
|||
APP_SECRET=a1def1573d162b7d97ed994df9d37949 |
|||
|
|||
DATABASE_URL=mysql://root:rootroot@mysql:3306/template?serverVersion=5.7 |
|||
WEATHER_HISTORY_SERVER=http://weather_history_nginx_1:80 |
@ -1 +0,0 @@ |
|||
bind 0.0.0.0 |
@ -0,0 +1,24 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
namespace App\Form; |
|||
|
|||
use Symfony\Component\Form\AbstractType; |
|||
use Symfony\Component\Form\Extension\Core\Type\DateType; |
|||
use Symfony\Component\Form\Extension\Core\Type\SubmitType; |
|||
use Symfony\Component\Form\FormBuilderInterface; |
|||
|
|||
class WeatherFormType extends AbstractType |
|||
{ |
|||
public function buildForm(FormBuilderInterface $builder, array $options) |
|||
{ |
|||
$builder |
|||
->add('date', DateType::class, [ |
|||
'widget' => 'single_text', |
|||
'format' => 'yyyy-MM-dd', |
|||
]) |
|||
->add('send', SubmitType::class) |
|||
; |
|||
} |
|||
} |
@ -0,0 +1,57 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
namespace App\Service\Endpoint; |
|||
|
|||
use DateTime; |
|||
use GuzzleHttp\Client; |
|||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; |
|||
|
|||
class WeatherHistory implements WeatherHistoryInterface |
|||
{ |
|||
private Client $client; |
|||
|
|||
private string $weatherHistoryServer; |
|||
|
|||
public function __construct(ParameterBagInterface $parameterBag) |
|||
{ |
|||
$this->client = new Client(); |
|||
|
|||
$this->weatherHistoryServer = $parameterBag->get('weather_history_server'); |
|||
} |
|||
|
|||
public function getByDate(DateTime $date): array |
|||
{ |
|||
$url = sprintf('%s/json-rpc', $this->weatherHistoryServer); |
|||
$response = $this->client->request('POST', $url, [ |
|||
'headers' => [ |
|||
'Accept-Encoding' => 'gzip', |
|||
'Content-Type' => 'text/plain', |
|||
], |
|||
'body' => sprintf( |
|||
'{"jsonrpc": "2.0", "method": "weather.getByDate", "params": {"date": "%s"}, "id": 1}', |
|||
$date->format('Y-m-d') |
|||
), |
|||
]); |
|||
|
|||
return json_decode($response->getBody()->getContents(), true); |
|||
} |
|||
|
|||
public function getHistory(int $numberOfDays = 30): array |
|||
{ |
|||
$url = sprintf('%s/json-rpc', $this->weatherHistoryServer); |
|||
$response = $this->client->request('POST', $url, [ |
|||
'headers' => [ |
|||
'Accept-Encoding' => 'gzip', |
|||
'Content-Type' => 'text/plain', |
|||
], |
|||
'body' => sprintf( |
|||
'{"jsonrpc": "2.0", "method": "weather.getHistory", "params": {"lastDays": %d}, "id": 1}', |
|||
$numberOfDays |
|||
), |
|||
]); |
|||
|
|||
return json_decode($response->getBody()->getContents(), true); |
|||
} |
|||
} |
@ -0,0 +1,14 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
namespace App\Service\Endpoint; |
|||
|
|||
use DateTime; |
|||
|
|||
interface WeatherHistoryInterface |
|||
{ |
|||
public function getByDate(DateTime $date): array; |
|||
|
|||
public function getHistory(int $numberOfDays = 30): array; |
|||
} |
@ -0,0 +1,66 @@ |
|||
<!DOCTYPE html> |
|||
<html> |
|||
<head> |
|||
<meta charset="UTF-8"> |
|||
<title>{% block title %}Welcome!{% endblock %}</title> |
|||
{% block stylesheets %}{% endblock %} |
|||
|
|||
{% block javascripts %}{% endblock %} |
|||
</head> |
|||
<body> |
|||
{% block body %} |
|||
{% if weatherForm is defined and weatherForm %} |
|||
<h3>Погода за день</h3> |
|||
|
|||
{{ form_errors(weatherForm) }} |
|||
{{ form_start(weatherForm) }} |
|||
{{ form_widget(weatherForm) }} |
|||
{{ form_end(weatherForm) }} |
|||
|
|||
{% if weather.error.code is defined and weather.error.code |
|||
and weather.error.message is defined and weather.error.message |
|||
%} |
|||
<p> {{ weather.error.code }} {{ weather.error.message }}.</p> |
|||
{% else %} |
|||
{% if weather.result.temp is defined and weather.result.temp %} |
|||
<p>Температура {{ weather.result.temp }} градусов</p> |
|||
{% else %} |
|||
<p>Температура за указанный день не указана</p> |
|||
{% endif %} |
|||
{% endif %} |
|||
{% endif %} |
|||
|
|||
{% if history is defined and history %} |
|||
<h3>Погода за последние 30 дней</h3> |
|||
{% if history.error.code is defined and history.error.code |
|||
and history.error.message is defined and history.error.message |
|||
%} |
|||
<p>{{ history.error.code }} {{ history.error.message }}</p> |
|||
{% else %} |
|||
{% if history.result is defined and history.result %} |
|||
<table border="1"> |
|||
<tr> |
|||
<th>Дата</th> |
|||
<th>Температура</th> |
|||
</tr> |
|||
{% for element in history.result %} |
|||
<tr> |
|||
<td> |
|||
{% if element.date_at is defined and element.date_at %} |
|||
{{ element.date_at }} |
|||
{% endif %} |
|||
</td> |
|||
<td> |
|||
{% if element.temp is defined and element.temp %} |
|||
{{ element.temp }} |
|||
{% endif %} |
|||
</td> |
|||
</tr> |
|||
{% endfor %} |
|||
</table> |
|||
{% endif %} |
|||
{% endif %} |
|||
{% endif %} |
|||
{% endblock %} |
|||
</body> |
|||
</html> |
@ -1,10 +0,0 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
namespace App\Tests; |
|||
|
|||
class ApiTester extends \Codeception\Actor |
|||
{ |
|||
use _generated\ApiTesterActions; |
|||
} |
@ -1,9 +0,0 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
namespace App\Tests\Helper; |
|||
|
|||
class Api extends \Codeception\Module |
|||
{ |
|||
} |
@ -0,0 +1,60 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
namespace App\Tests\acceptance\Controller; |
|||
|
|||
use App\Tests\AcceptanceTester; |
|||
use Codeception\Example; |
|||
use DateTime; |
|||
|
|||
class IndexControllerCest |
|||
{ |
|||
private AcceptanceTester $tester; |
|||
|
|||
public function _before(AcceptanceTester $tester): void |
|||
{ |
|||
$this->tester = $tester; |
|||
} |
|||
|
|||
public function testBaseAvailability(): void |
|||
{ |
|||
$this->tester->amOnPage('/'); |
|||
$this->tester->seeResponseCodeIs(200); |
|||
} |
|||
|
|||
/** |
|||
* @dataProvider dataDateForm |
|||
*/ |
|||
public function testDateForm(Example $example): void |
|||
{ |
|||
$this->tester->amOnPage('/'); |
|||
$this->tester->fillField("//input[@id='weather_form_date']", $example['fillField']); |
|||
$this->tester->click('Send'); |
|||
$this->tester->seeCurrentUrlEquals('/'); |
|||
$this->tester->seeResponseCodeIs(200); |
|||
$this->tester->see($example['see']); |
|||
} |
|||
|
|||
protected function dataDateForm(): array |
|||
{ |
|||
return [ |
|||
[ |
|||
'fillField' => (new DateTime())->format('Y-m-d'), |
|||
'see' => 'градусов', |
|||
], |
|||
[ |
|||
'fillField' => '9999-01-01', |
|||
'see' => 'Температура за указанный день не указана', |
|||
], |
|||
]; |
|||
} |
|||
|
|||
public function testWeatherTable(): void |
|||
{ |
|||
$this->tester->amOnPage('/'); |
|||
$this->tester->see('Погода за последние 30 дней'); |
|||
$this->tester->see('Дата', '//th'); |
|||
$this->tester->see('Температура', '//th'); |
|||
} |
|||
} |
@ -1,23 +0,0 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
namespace App\Tests\acceptance\Controller; |
|||
|
|||
use App\Tests\AcceptanceTester; |
|||
|
|||
class PageControllerCest |
|||
{ |
|||
private AcceptanceTester $tester; |
|||
|
|||
public function _before(AcceptanceTester $tester): void |
|||
{ |
|||
$this->tester = $tester; |
|||
} |
|||
|
|||
public function testBaseAvailability(): void |
|||
{ |
|||
$this->tester->amOnPage('/'); |
|||
$this->tester->seeResponseCodeIs(400); |
|||
} |
|||
} |
@ -1,8 +0,0 @@ |
|||
actor: ApiTester |
|||
modules: |
|||
enabled: |
|||
- REST: |
|||
url: http://nginx/ |
|||
depends: PhpBrowser |
|||
part: Json |
|||
- \App\Tests\Helper\Api |
@ -1,36 +0,0 @@ |
|||
<?php |
|||
|
|||
declare(strict_types=1); |
|||
|
|||
namespace App\Tests\api\Controller; |
|||
|
|||
use App\Tests\ApiTester; |
|||
use Codeception\Example; |
|||
use Iterator; |
|||
|
|||
class PageControllerCest |
|||
{ |
|||
private ApiTester $tester; |
|||
|
|||
public function _before(ApiTester $tester): void |
|||
{ |
|||
$this->tester = $tester; |
|||
} |
|||
|
|||
/** |
|||
* @dataProvider dataTestBaseAvailability |
|||
*/ |
|||
public function testBaseAvailability(Example $example): void |
|||
{ |
|||
$this->tester->sendGet($example['url']); |
|||
$this->tester->seeResponseCodeIs(400); |
|||
$this->tester->seeResponseIsJson(); |
|||
} |
|||
|
|||
protected static function dataTestBaseAvailability(): Iterator |
|||
{ |
|||
yield [ |
|||
'url' => '/', |
|||
]; |
|||
} |
|||
} |
Reference in new issue