diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2e2f690 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/app/.phpunit.result.cache +/.phpunit.result.cache diff --git a/app/.gitignore b/app/.gitignore index cc1eb0b..f6ed8cb 100644 --- a/app/.gitignore +++ b/app/.gitignore @@ -1,3 +1,2 @@ /.env -/.phpunit.result.cache /phpunit.xml diff --git a/app/composer.json b/app/composer.json index 817f300..3aadfdd 100644 --- a/app/composer.json +++ b/app/composer.json @@ -10,12 +10,16 @@ "doctrine/annotations": "^1.13", "guzzlehttp/guzzle": "^7.3", "nelmio/api-doc-bundle": "^4.6", + "phpdocumentor/reflection-docblock": "^5.2", "symfony/asset": "5.3.*", "symfony/console": "5.3.*", "symfony/dotenv": "5.3.*", "symfony/flex": "^1.16", "symfony/framework-bundle": "5.3.*", + "symfony/property-access": "5.3.*", + "symfony/property-info": "5.3.*", "symfony/runtime": "5.3.*", + "symfony/serializer": "5.3.*", "symfony/twig-bundle": "5.3.*", "symfony/yaml": "5.3.*", "twig/extra-bundle": "^2.12 || ^3.0", @@ -79,6 +83,10 @@ "cache:clear": "symfony-cmd", "assets:install %PUBLIC_DIR%": "symfony-cmd" }, + "cs:fix": [ + "@json-cs-fix", + "@php-cs-fix" + ], "json-cs-fix": "composer normalize", "lint:php": [ "parallel-lint --no-progress --no-colors --blame ./bin/console", diff --git a/app/composer.lock b/app/composer.lock index add63c3..9f4142c 100644 --- a/app/composer.lock +++ b/app/composer.lock @@ -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": "42ca436544abf8d5ea35614dcf79d64e", + "content-hash": "1fa5d80ef6ea75d8cbdc00ef247597e7", "packages": [ { "name": "doctrine/annotations", @@ -3114,6 +3114,87 @@ ], "time": "2021-05-21T13:25:03+00:00" }, + { + "name": "symfony/property-access", + "version": "v5.3.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/property-access.git", + "reference": "2fbab5f95ddb6b8e85f38a6a8a04a17c0acc4d66" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/property-access/zipball/2fbab5f95ddb6b8e85f38a6a8a04a17c0acc4d66", + "reference": "2fbab5f95ddb6b8e85f38a6a8a04a17c0acc4d66", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1", + "symfony/polyfill-php80": "^1.16", + "symfony/property-info": "^5.2" + }, + "require-dev": { + "symfony/cache": "^4.4|^5.0" + }, + "suggest": { + "psr/cache-implementation": "To cache access methods." + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\PropertyAccess\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides functions to read and write from/to an object or array using a simple string notation", + "homepage": "https://symfony.com", + "keywords": [ + "access", + "array", + "extraction", + "index", + "injection", + "object", + "property", + "property path", + "reflection" + ], + "support": { + "source": "https://github.com/symfony/property-access/tree/v5.3.8" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-09-10T11:55:24+00:00" + }, { "name": "symfony/property-info", "version": "v5.3.7", @@ -3371,6 +3452,108 @@ ], "time": "2021-06-30T13:49:12+00:00" }, + { + "name": "symfony/serializer", + "version": "v5.3.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/serializer.git", + "reference": "a877799b1e94f792208bea68295f6675027c92be" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/serializer/zipball/a877799b1e94f792208bea68295f6675027c92be", + "reference": "a877799b1e94f792208bea68295f6675027c92be", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-php80": "^1.16" + }, + "conflict": { + "doctrine/annotations": "<1.12", + "phpdocumentor/reflection-docblock": "<3.2.2", + "phpdocumentor/type-resolver": "<1.4.0", + "symfony/dependency-injection": "<4.4", + "symfony/property-access": "<4.4", + "symfony/property-info": "<5.3", + "symfony/yaml": "<4.4" + }, + "require-dev": { + "doctrine/annotations": "^1.12", + "phpdocumentor/reflection-docblock": "^3.2|^4.0|^5.0", + "symfony/cache": "^4.4|^5.0", + "symfony/config": "^4.4|^5.0", + "symfony/dependency-injection": "^4.4|^5.0", + "symfony/error-handler": "^4.4|^5.0", + "symfony/filesystem": "^4.4|^5.0", + "symfony/form": "^4.4|^5.0", + "symfony/http-foundation": "^4.4|^5.0", + "symfony/http-kernel": "^4.4|^5.0", + "symfony/mime": "^4.4|^5.0", + "symfony/property-access": "^4.4.9|^5.0.9", + "symfony/property-info": "^5.3", + "symfony/uid": "^5.1", + "symfony/validator": "^4.4|^5.0", + "symfony/var-dumper": "^4.4|^5.0", + "symfony/var-exporter": "^4.4|^5.0", + "symfony/yaml": "^4.4|^5.0" + }, + "suggest": { + "psr/cache-implementation": "For using the metadata cache.", + "symfony/config": "For using the XML mapping loader.", + "symfony/mime": "For using a MIME type guesser within the DataUriNormalizer.", + "symfony/property-access": "For using the ObjectNormalizer.", + "symfony/property-info": "To deserialize relations.", + "symfony/var-exporter": "For using the metadata compiler.", + "symfony/yaml": "For using the default YAML mapping loader." + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Serializer\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Handles serializing and deserializing data structures, including object graphs, into array structures or other formats like XML and JSON.", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/serializer/tree/v5.3.8" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-09-17T08:55:39+00:00" + }, { "name": "symfony/service-contracts", "version": "v2.4.0", @@ -5965,7 +6148,7 @@ "ezsystems/ezplatform-admin-ui": ">=1.3,<1.3.5|>=1.4,<1.4.6", "ezsystems/ezplatform-admin-ui-assets": ">=4,<4.2.1|>=5,<5.0.1|>=5.1,<5.1.1", "ezsystems/ezplatform-kernel": "<=1.2.5|>=1.3,<=1.3.1", - "ezsystems/ezplatform-rest": ">=1.2,<=1.2.2|>=1.3,<=1.3.1", + "ezsystems/ezplatform-rest": ">=1.2,<=1.2.2|>=1.3,<1.3.8", "ezsystems/ezplatform-user": ">=1,<1.0.1", "ezsystems/ezpublish-kernel": "<=6.13.8.1|>=7,<=7.5.15.1", "ezsystems/ezpublish-legacy": "<=2017.12.7.3|>=2018.6,<=2019.3.5.1", @@ -5990,13 +6173,14 @@ "friendsoftypo3/mediace": ">=7.6.2,<7.6.5", "froala/wysiwyg-editor": "<3.2.7", "fuel/core": "<1.8.1", - "getgrav/grav": "<=1.7.10", + "getgrav/grav": "<1.7.21", "getkirby/cms": "<=3.5.6", "getkirby/panel": "<2.5.14", + "gilacms/gila": "<=1.11.4", "gos/web-socket-bundle": "<1.10.4|>=2,<2.6.1|>=3,<3.3", "gree/jose": "<=2.2", "gregwar/rst": "<1.0.3", - "grumpydictator/firefly-iii": "<5.6", + "grumpydictator/firefly-iii": "<5.6.1", "guzzlehttp/guzzle": ">=4-rc.2,<4.2.4|>=5,<5.3.1|>=6,<6.2.1", "helloxz/imgurl": "<=2.31", "ibexa/post-install": "<=1.0.4", @@ -6023,6 +6207,7 @@ "laravel/framework": "<6.20.26|>=7,<8.40", "laravel/socialite": ">=1,<1.0.99|>=2,<2.0.10", "lavalite/cms": "<=5.8", + "lcobucci/jwt": ">=3.4,<3.4.6|>=4,<4.0.4|>=4.1,<4.1.5", "league/commonmark": "<0.18.3", "league/flysystem": "<1.1.4|>=2,<2.1.1", "lexik/jwt-authentication-bundle": "<2.10.7|>=2.11,<2.11.3", @@ -6186,6 +6371,7 @@ "theonedemon/phpwhois": "<=4.2.5", "titon/framework": ">=0,<9.9.99", "topthink/think": "<=6.0.9", + "topthink/thinkphp": "<=3.2.3", "tribalsystems/zenario": "<8.8.53370", "truckersmp/phpwhois": "<=4.3.1", "twig/twig": "<1.38|>=2,<2.7", @@ -6204,6 +6390,7 @@ "verot/class.upload.php": "<=1.0.3|>=2,<=2.0.4", "vrana/adminer": "<4.7.9", "wallabag/tcpdf": "<6.2.22", + "web-auth/webauthn-framework": ">=3.3,<3.3.4", "webcoast/deferred-image-processing": "<1.0.2", "wikimedia/parsoid": "<0.12.2", "willdurand/js-translation-bundle": "<2.1.1", diff --git a/app/src/Controller/Api/v1/MattermostController.php b/app/src/Controller/Api/v1/MattermostController.php index f163402..718a477 100644 --- a/app/src/Controller/Api/v1/MattermostController.php +++ b/app/src/Controller/Api/v1/MattermostController.php @@ -4,49 +4,68 @@ declare(strict_types=1); namespace App\Controller\Api\v1; +use App\Model\Grafana\GrafanaMessage; use App\Service\Mattermost\MattermostServiceInterface; +use Nelmio\ApiDocBundle\Annotation\Model; use OpenApi\Annotations as OA; +use Psr\Log\LoggerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; +use Symfony\Component\Serializer\SerializerInterface; use Throwable; class MattermostController extends AbstractController { + protected LoggerInterface $logger; + protected SerializerInterface $serializer; protected MattermostServiceInterface $mattermostService; - public function __construct(MattermostServiceInterface $mattermostService) - { + public function __construct( + LoggerInterface $logger, + SerializerInterface $serializer, + MattermostServiceInterface $mattermostService, + ) { + $this->logger = $logger; + $this->serializer = $serializer; $this->mattermostService = $mattermostService; } /** - * Send message to a mattermost chat. + * Send message to a mattermost chat from grafana. * - * Docs https://docs.mattermost.com/developer/webhooks-incoming.html + * Docs
+ * - https://grafana.com/docs/grafana/latest/alerting/old-alerting/notifications/#webhook
+ * - https://docs.mattermost.com/developer/webhooks-incoming.html * - * @Route("/api/v1/send_message", methods={"POST"}) + * @Route("/api/v1/send_message/grafana", methods={"POST"}) * @OA\Tag(name="mattermost") - * @OA\Parameter( - * name="message", - * in="query", - * required=true, - * @OA\Schema(type="string") + * @OA\RequestBody( + * @OA\JsonContent( + * ref=@Model(type=GrafanaMessage::class) + * ) * ) * @OA\Response( * response=200, * description="Success delivery" * ) */ - public function sendMessage(Request $request): Response + public function sendMessageFromGrafana(Request $request): Response { try { - $message = $request->query->get('message'); - $response = $this->mattermostService->sendMessage($message); + $this->logger->notice($request->getContent()); + $message = $this->serializer->deserialize( + $request->getContent(), + GrafanaMessage::class, + 'json' + ); + $response = $this->mattermostService->sendGrafanaMessage($message); return new Response($response->getBody()->getContents()); } catch (Throwable $e) { + $this->logger->critical($e->getMessage()); + return new Response( $e->getMessage(), 0 === $e->getCode() ? Response::HTTP_INTERNAL_SERVER_ERROR : $e->getCode() diff --git a/app/src/Model/Grafana/EvalMatches.php b/app/src/Model/Grafana/EvalMatches.php new file mode 100644 index 0000000..e39e589 --- /dev/null +++ b/app/src/Model/Grafana/EvalMatches.php @@ -0,0 +1,64 @@ +value; + } + + public function setValue(int $value): self + { + $this->value = $value; + + return $this; + } + + public function getMetric(): string + { + return $this->metric; + } + + public function setMetric(string $metric): self + { + $this->metric = $metric; + + return $this; + } + + public function getTags(): ?string + { + return $this->tags; + } + + public function setTags(?string $tags): self + { + $this->tags = $tags; + + return $this; + } +} diff --git a/app/src/Model/Grafana/GrafanaMessage.php b/app/src/Model/Grafana/GrafanaMessage.php new file mode 100644 index 0000000..fee06b9 --- /dev/null +++ b/app/src/Model/Grafana/GrafanaMessage.php @@ -0,0 +1,211 @@ +dashboardId; + } + + public function setDashboardId(int $dashboardId): self + { + $this->dashboardId = $dashboardId; + + return $this; + } + + /** + * @return EvalMatches[] + */ + public function getEvalMatches(): array + { + return $this->evalMatches; + } + + /** + * @param EvalMatches[] $evalMatches + */ + public function setEvalMatches(array $evalMatches): self + { + $this->evalMatches = $evalMatches; + + return $this; + } + + public function getImageUrl(): string + { + return $this->imageUrl; + } + + public function setImageUrl(string $imageUrl): self + { + $this->imageUrl = $imageUrl; + + return $this; + } + + public function getMessage(): string + { + return $this->message; + } + + public function setMessage(string $message): self + { + $this->message = $message; + + return $this; + } + + public function getOrgId(): int + { + return $this->orgId; + } + + public function setOrgId(int $orgId): self + { + $this->orgId = $orgId; + + return $this; + } + + public function getPanelId(): int + { + return $this->panelId; + } + + public function setPanelId(int $panelId): self + { + $this->panelId = $panelId; + + return $this; + } + + public function getRuleId(): int + { + return $this->ruleId; + } + + public function setRuleId(int $ruleId): self + { + $this->ruleId = $ruleId; + + return $this; + } + + public function getRuleName(): string + { + return $this->ruleName; + } + + public function setRuleName(string $ruleName): self + { + $this->ruleName = $ruleName; + + return $this; + } + + public function getRuleUrl(): string + { + return $this->ruleUrl; + } + + public function setRuleUrl(string $ruleUrl): self + { + $this->ruleUrl = $ruleUrl; + + return $this; + } + + public function getState(): string + { + return $this->state; + } + + public function setState(string $state): self + { + $this->state = $state; + + return $this; + } + + public function getTitle(): string + { + return $this->title; + } + + public function setTitle(string $title): self + { + $this->title = $title; + + return $this; + } +} diff --git a/app/src/Service/Mattermost/MattermostService.php b/app/src/Service/Mattermost/MattermostService.php index 84d0f52..3173cc5 100644 --- a/app/src/Service/Mattermost/MattermostService.php +++ b/app/src/Service/Mattermost/MattermostService.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace App\Service\Mattermost; +use App\Model\Grafana\GrafanaMessage; use App\Service\BaseService; use GuzzleHttp\Exception\GuzzleException; use GuzzleHttp\Psr7\Request; @@ -34,10 +35,17 @@ class MattermostService extends BaseService implements MattermostServiceInterfac * @throws GuzzleException * @throws \JsonException */ - public function sendMessage(string $message): ResponseInterface + public function sendGrafanaMessage(GrafanaMessage $grafanaMessage): ResponseInterface { $request = new Request('POST', $this->mattermostUri); + $text = sprintf( + "%s\n %s\n %s\n\n", + $grafanaMessage->getTitle(), + $grafanaMessage->getMessage(), + $grafanaMessage->getImageUrl() + ); + $options = [ 'headers' => [ 'Content-Type' => 'application/json', @@ -46,7 +54,7 @@ class MattermostService extends BaseService implements MattermostServiceInterfac 'channel' => $this->channelName, 'username' => $this->botName, 'icon_url' => $this->botIcon, - 'text' => $message, + 'text' => $text, ], JSON_THROW_ON_ERROR), ]; diff --git a/app/src/Service/Mattermost/MattermostServiceInterface.php b/app/src/Service/Mattermost/MattermostServiceInterface.php index a58753c..7803abb 100644 --- a/app/src/Service/Mattermost/MattermostServiceInterface.php +++ b/app/src/Service/Mattermost/MattermostServiceInterface.php @@ -4,9 +4,10 @@ declare(strict_types=1); namespace App\Service\Mattermost; +use App\Model\Grafana\GrafanaMessage; use Psr\Http\Message\ResponseInterface; interface MattermostServiceInterface { - public function sendMessage(string $message): ResponseInterface; + public function sendGrafanaMessage(GrafanaMessage $grafanaMessage): ResponseInterface; } diff --git a/app/symfony.lock b/app/symfony.lock index f4c44b4..4fd2912 100644 --- a/app/symfony.lock +++ b/app/symfony.lock @@ -345,6 +345,9 @@ "symfony/process": { "version": "v5.3.7" }, + "symfony/property-access": { + "version": "v5.3.8" + }, "symfony/property-info": { "version": "v5.3.7" }, @@ -364,6 +367,9 @@ "symfony/runtime": { "version": "v5.3.4" }, + "symfony/serializer": { + "version": "v5.3.8" + }, "symfony/service-contracts": { "version": "v2.4.0" }, diff --git a/app/tests/Unit/Controller/Api/v1/MattermostControllerTest.php b/app/tests/Unit/Controller/Api/v1/MattermostControllerTest.php index f61b0b5..ef678c7 100644 --- a/app/tests/Unit/Controller/Api/v1/MattermostControllerTest.php +++ b/app/tests/Unit/Controller/Api/v1/MattermostControllerTest.php @@ -8,7 +8,7 @@ use App\Controller\Api\v1\MattermostController; use App\Tests\Unit\UnitTester; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; -class MattermostControllerTest extends UnitTester +final class MattermostControllerTest extends UnitTester { public function testIsExtends(): void { diff --git a/app/tests/Unit/KernelTest.php b/app/tests/Unit/KernelTest.php index 320b367..d224d4e 100644 --- a/app/tests/Unit/KernelTest.php +++ b/app/tests/Unit/KernelTest.php @@ -8,7 +8,7 @@ use App\Kernel; use PHPUnit\Framework\TestCase; use Symfony\Component\HttpKernel\Kernel as BaseKernel; -class KernelTest extends TestCase +final class KernelTest extends TestCase { public function testIsExtends(): void { diff --git a/app/tests/Unit/Model/Grafana/EvalMatchesTest.php b/app/tests/Unit/Model/Grafana/EvalMatchesTest.php new file mode 100644 index 0000000..ca8d8a3 --- /dev/null +++ b/app/tests/Unit/Model/Grafana/EvalMatchesTest.php @@ -0,0 +1,110 @@ +faker->randomNumber(); + + $property = $this->getClassProperty($evalMatches, 'value'); + $property->setValue($evalMatches, $value); + + self::assertEquals($value, $evalMatches->getValue()); + } + + /** + * @throws ReflectionException + */ + public function testSetValue(): void + { + $evalMatches = new EvalMatches(); + $value = $this->faker->randomNumber(); + + self::assertEquals($evalMatches, $evalMatches->setValue($value)); + self::assertEquals( + $value, + ($this->getClassProperty($evalMatches, 'value')) + ->getValue($evalMatches) + ); + } + + /** + * @throws ReflectionException + */ + public function testGetMetric(): void + { + $evalMatches = new EvalMatches(); + $value = $this->faker->sha256(); + + $property = $this->getClassProperty($evalMatches, 'metric'); + $property->setValue($evalMatches, $value); + + self::assertEquals($value, $evalMatches->getMetric()); + } + + /** + * @throws ReflectionException + */ + public function testSetMetric(): void + { + $evalMatches = new EvalMatches(); + $value = $this->faker->sha256(); + + self::assertEquals($evalMatches, $evalMatches->setMetric($value)); + self::assertEquals( + $value, + ($this->getClassProperty($evalMatches, 'metric')) + ->getValue($evalMatches) + ); + } + + /** + * @dataProvider dataProviderTags + * @throws ReflectionException + */ + public function testGetTags(?string $value): void + { + $evalMatches = new EvalMatches(); + + $property = $this->getClassProperty($evalMatches, 'tags'); + $property->setValue($evalMatches, $value); + + self::assertEquals($value, $evalMatches->getTags()); + } + + /** + * @dataProvider dataProviderTags + * @throws ReflectionException + */ + public function testSetTags(?string $value): void + { + $evalMatches = new EvalMatches(); + + self::assertEquals($evalMatches, $evalMatches->setTags($value)); + self::assertEquals( + $value, + ($this->getClassProperty($evalMatches, 'tags')) + ->getValue($evalMatches) + ); + } + + public function dataProviderTags(): array + { + return [ + ['tags' => null], + ['tags' => $this->faker->sha256()], + ]; + } +} diff --git a/app/tests/Unit/Model/Grafana/GrafanaMessageTest.php b/app/tests/Unit/Model/Grafana/GrafanaMessageTest.php new file mode 100644 index 0000000..183b146 --- /dev/null +++ b/app/tests/Unit/Model/Grafana/GrafanaMessageTest.php @@ -0,0 +1,313 @@ +faker->randomNumber(); + + $property = $this->getClassProperty($grafanaMessage, 'dashboardId'); + $property->setValue($grafanaMessage, $value); + + self::assertEquals($value, $grafanaMessage->getDashboardId()); + } + + /** + * @throws ReflectionException + */ + public function testSetDashboardId(): void + { + $grafanaMessage = new GrafanaMessage(); + $value = $this->faker->randomNumber(); + + self::assertEquals($grafanaMessage, $grafanaMessage->setDashboardId($value)); + self::assertEquals( + $value, + ($this->getClassProperty($grafanaMessage, 'dashboardId')) + ->getValue($grafanaMessage) + ); + } + + /** + * @throws ReflectionException + */ + public function testGetEvalMatches(): void + { + $grafanaMessage = new GrafanaMessage(); + $value = [new EvalMatches()]; + + $property = $this->getClassProperty($grafanaMessage, 'evalMatches'); + $property->setValue($grafanaMessage, $value); + + self::assertEquals($value, $grafanaMessage->getEvalMatches()); + } + + /** + * @throws ReflectionException + */ + public function testSetEvalMatches(): void + { + $grafanaMessage = new GrafanaMessage(); + $value = [new EvalMatches()]; + + self::assertEquals($grafanaMessage, $grafanaMessage->setEvalMatches($value)); + self::assertEquals( + $value, + ($this->getClassProperty($grafanaMessage, 'evalMatches')) + ->getValue($grafanaMessage) + ); + } + + /** + * @throws ReflectionException + */ + public function testGetImageUrl(): void + { + $grafanaMessage = new GrafanaMessage(); + $value = $this->faker->sha256(); + + $property = $this->getClassProperty($grafanaMessage, 'imageUrl'); + $property->setValue($grafanaMessage, $value); + + self::assertEquals($value, $grafanaMessage->getImageUrl()); + } + + /** + * @throws ReflectionException + */ + public function testSetImageUrl(): void + { + $grafanaMessage = new GrafanaMessage(); + $value = $this->faker->sha256(); + + self::assertEquals($grafanaMessage, $grafanaMessage->setImageUrl($value)); + self::assertEquals( + $value, + ($this->getClassProperty($grafanaMessage, 'imageUrl')) + ->getValue($grafanaMessage) + ); + } + + /** + * @throws ReflectionException + */ + public function testGetMessage(): void + { + $grafanaMessage = new GrafanaMessage(); + $value = $this->faker->sha256(); + + $property = $this->getClassProperty($grafanaMessage, 'message'); + $property->setValue($grafanaMessage, $value); + + self::assertEquals($value, $grafanaMessage->getMessage()); + } + + /** + * @throws ReflectionException + */ + public function testSetMessage(): void + { + $grafanaMessage = new GrafanaMessage(); + $value = $this->faker->sha256(); + + self::assertEquals($grafanaMessage, $grafanaMessage->setMessage($value)); + self::assertEquals( + $value, + ($this->getClassProperty($grafanaMessage, 'message')) + ->getValue($grafanaMessage) + ); + } + + /** + * @throws ReflectionException + */ + public function testGetOrgId(): void + { + $grafanaMessage = new GrafanaMessage(); + $value = $this->faker->randomNumber(); + + $property = $this->getClassProperty($grafanaMessage, 'orgId'); + $property->setValue($grafanaMessage, $value); + + self::assertEquals($value, $grafanaMessage->getOrgId()); + } + + /** + * @throws ReflectionException + */ + public function testSetOrgId(): void + { + $grafanaMessage = new GrafanaMessage(); + $value = $this->faker->randomNumber(); + + self::assertEquals($grafanaMessage, $grafanaMessage->setOrgId($value)); + self::assertEquals( + $value, + ($this->getClassProperty($grafanaMessage, 'orgId')) + ->getValue($grafanaMessage) + ); + } + + /** + * @throws ReflectionException + */ + public function testGetPanelId(): void + { + $grafanaMessage = new GrafanaMessage(); + $value = $this->faker->randomNumber(); + + $property = $this->getClassProperty($grafanaMessage, 'panelId'); + $property->setValue($grafanaMessage, $value); + + self::assertEquals($value, $grafanaMessage->getPanelId()); + } + + /** + * @throws ReflectionException + */ + public function testSetPanelId(): void + { + $grafanaMessage = new GrafanaMessage(); + $value = $this->faker->randomNumber(); + + self::assertEquals($grafanaMessage, $grafanaMessage->setPanelId($value)); + self::assertEquals( + $value, + ($this->getClassProperty($grafanaMessage, 'panelId')) + ->getValue($grafanaMessage) + ); + } + + /** + * @throws ReflectionException + */ + public function testGetRuleId(): void + { + $grafanaMessage = new GrafanaMessage(); + $value = $this->faker->randomNumber(); + + $property = $this->getClassProperty($grafanaMessage, 'ruleId'); + $property->setValue($grafanaMessage, $value); + + self::assertEquals($value, $grafanaMessage->getRuleId()); + } + + /** + * @throws ReflectionException + */ + public function testSetRuleId(): void + { + $grafanaMessage = new GrafanaMessage(); + $value = $this->faker->randomNumber(); + + self::assertEquals($grafanaMessage, $grafanaMessage->setRuleId($value)); + self::assertEquals( + $value, + ($this->getClassProperty($grafanaMessage, 'ruleId')) + ->getValue($grafanaMessage) + ); + } + + /** + * @throws ReflectionException + */ + public function testGetRuleName(): void + { + $grafanaMessage = new GrafanaMessage(); + $value = $this->faker->sha256(); + + $property = $this->getClassProperty($grafanaMessage, 'ruleName'); + $property->setValue($grafanaMessage, $value); + + self::assertEquals($value, $grafanaMessage->getRuleName()); + } + + /** + * @throws ReflectionException + */ + public function testSetRuleName(): void + { + $grafanaMessage = new GrafanaMessage(); + $value = $this->faker->sha256(); + + self::assertEquals($grafanaMessage, $grafanaMessage->setRuleName($value)); + self::assertEquals( + $value, + ($this->getClassProperty($grafanaMessage, 'ruleName')) + ->getValue($grafanaMessage) + ); + } + + /** + * @throws ReflectionException + */ + public function testGetState(): void + { + $grafanaMessage = new GrafanaMessage(); + $value = $this->faker->sha256(); + + $property = $this->getClassProperty($grafanaMessage, 'state'); + $property->setValue($grafanaMessage, $value); + + self::assertEquals($value, $grafanaMessage->getState()); + } + + /** + * @throws ReflectionException + */ + public function testSetState(): void + { + $grafanaMessage = new GrafanaMessage(); + $value = $this->faker->sha256(); + + self::assertEquals($grafanaMessage, $grafanaMessage->setState($value)); + self::assertEquals( + $value, + ($this->getClassProperty($grafanaMessage, 'state')) + ->getValue($grafanaMessage) + ); + } + + /** + * @throws ReflectionException + */ + public function testGetTitle(): void + { + $grafanaMessage = new GrafanaMessage(); + $value = $this->faker->sha256(); + + $property = $this->getClassProperty($grafanaMessage, 'title'); + $property->setValue($grafanaMessage, $value); + + self::assertEquals($value, $grafanaMessage->getTitle()); + } + + /** + * @throws ReflectionException + */ + public function testSetTitle(): void + { + $grafanaMessage = new GrafanaMessage(); + $value = $this->faker->sha256(); + + self::assertEquals($grafanaMessage, $grafanaMessage->setTitle($value)); + self::assertEquals( + $value, + ($this->getClassProperty($grafanaMessage, 'title')) + ->getValue($grafanaMessage) + ); + } +} diff --git a/app/tests/Unit/Service/Mattermost/MattermostServiceTest.php b/app/tests/Unit/Service/Mattermost/MattermostServiceTest.php index 71c9ec2..0d1ab8f 100644 --- a/app/tests/Unit/Service/Mattermost/MattermostServiceTest.php +++ b/app/tests/Unit/Service/Mattermost/MattermostServiceTest.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace App\Tests\Unit\Service\Mattermost; +use App\Model\Grafana\GrafanaMessage; use App\Service\BaseService; use App\Service\Mattermost\MattermostService; use App\Service\Mattermost\MattermostServiceInterface; @@ -12,8 +13,9 @@ use GuzzleHttp\Client; use GuzzleHttp\Psr7\Request; use App\Tests\Unit\UnitTester; use Psr\Http\Message\ResponseInterface; +use ReflectionException; -class MattermostServiceTest extends UnitTester +final class MattermostServiceTest extends UnitTester { public function testIsExtends(): void { @@ -30,16 +32,16 @@ class MattermostServiceTest extends UnitTester } /** - * @dataProvider sendMessageDataProvider - * @throws \ReflectionException + * @dataProvider sendGrafanaMessageDataProvider + * @throws ReflectionException */ - public function testSendMessage( + public function testSendGrafanaMessage( string $mattermostUrl, string $mattermostUri, string $channelName, string $botName, string $botIcon, - string $message, + GrafanaMessage $grafanaMessage, array $options ): void { @@ -62,20 +64,22 @@ class MattermostServiceTest extends UnitTester self::assertEquals( $responseMock, - $mattermostService->sendMessage($message) + $mattermostService->sendGrafanaMessage($grafanaMessage) ); } /** * @throws \JsonException */ - public function sendMessageDataProvider(): array + public function sendGrafanaMessageDataProvider(): array { $faker = (Factory::create('en_EN')); $channelName = $faker->sha256(); $botName = $faker->sha256(); $botIcon = $faker->sha256(); + $title = $faker->sha256(); $message = $faker->sha256(); + $imageUrl = $faker->sha256(); return [ [ @@ -84,7 +88,11 @@ class MattermostServiceTest extends UnitTester 'channelName' => $channelName, 'botName' => $botName, 'botIcon' => $botIcon, - 'message' => $message, + 'message' => + (new GrafanaMessage()) + ->setTitle($title) + ->setMessage($message) + ->setImageUrl($imageUrl), 'options' => [ 'headers' => [ 'Content-Type' => 'application/json', @@ -93,7 +101,9 @@ class MattermostServiceTest extends UnitTester 'channel' => $channelName, 'username' => $botName, 'icon_url' => $botIcon, - 'text' => $message, + 'text' => sprintf("%s\n %s\n %s\n\n", + $title, $message, $imageUrl + ), ], JSON_THROW_ON_ERROR), ] ] diff --git a/app/tests/Unit/UnitTester.php b/app/tests/Unit/UnitTester.php index 9b6d1b8..c366a33 100644 --- a/app/tests/Unit/UnitTester.php +++ b/app/tests/Unit/UnitTester.php @@ -4,6 +4,8 @@ declare(strict_types=1); namespace App\Tests\Unit; +use Faker\Factory; +use Faker\Generator; use PHPUnit\Framework\TestCase; use ReflectionClass; use ReflectionException; @@ -11,6 +13,14 @@ use ReflectionProperty; class UnitTester extends TestCase { + protected Generator $faker; + + public function __construct(?string $name = null, array $data = [], $dataName = '') + { + parent::__construct($name, $data, $dataName); + $this->faker = (Factory::create('en_EN')); + } + /** * @throws ReflectionException */ diff --git a/devops/docker/php/Dockerfile b/devops/docker/php/Dockerfile index 7cd1273..463ff64 100644 --- a/devops/docker/php/Dockerfile +++ b/devops/docker/php/Dockerfile @@ -1,19 +1,13 @@ FROM php:8.0-fpm-alpine3.14 AS base - RUN apk update && apk upgrade && \ - apk add --no-cache $PHPIZE_DEPS libzip-dev && \ + apk add --no-cache $PHPIZE_DEPS libzip-dev composer && \ docker-php-ext-install zip pcntl -RUN php -r "readfile('https://getcomposer.org/installer');" \ - | php -- --install-dir=/usr/local/bin --version=2.1.6 --filename=composer - RUN mkdir -p /var/www/localhost WORKDIR /var/www/localhost -COPY ./app . - -FROM base AS test +FROM base AS pre_test ARG XDEBUG_VERSION=3.0.4 RUN curl --proto =https --tlsv1.2 "https://pecl.php.net/get/xdebug-${XDEBUG_VERSION}.tgz" && \ pecl install xdebug-${XDEBUG_VERSION}.tgz && \ @@ -21,12 +15,15 @@ RUN curl --proto =https --tlsv1.2 "https://pecl.php.net/get/xdebug-${XDEBUG_VERS rm -rf /tmp/pear && \ docker-php-ext-enable xdebug COPY devops/docker/php/xdebug.ini /usr/local/etc/php/conf.d/xdebug.ini + + +FROM pre_test AS test +COPY ./app . COPY env/app/app.env-dist .env RUN composer install --no-interaction --optimize-autoloader -FROM test AS local - +FROM pre_test AS local ARG HOME=/home/fedy95 ARG USER=fedy95 ARG UID=1000 diff --git a/devops/docker/php/xdebug.ini b/devops/docker/php/xdebug.ini index 2ce43e1..53544cd 100644 --- a/devops/docker/php/xdebug.ini +++ b/devops/docker/php/xdebug.ini @@ -1,4 +1,3 @@ -zend_extension=xdebug.so xdebug.client_host=host.docker.internal xdebug.mode=coverage,debug,profile xdebug.discover_client_host=1 diff --git a/docker-compose-local.yml b/docker-compose-local.yml index 9c2a0a3..8f1de49 100644 --- a/docker-compose-local.yml +++ b/docker-compose-local.yml @@ -1,6 +1,10 @@ --- version: "3.4" +networks: + monitoring_monitor-net: + external: true + services: app: build: @@ -11,11 +15,13 @@ services: environment: COMPOSER_MEMORY_LIMIT: "-1" - PHP_IDE_CONFIG: "serverName=notification-provider_php" + PHP_IDE_CONFIG: "serverName=notification-provider_app" volumes: - ./app:/var/www/localhost expose: - "9000" + networks: + - monitoring_monitor-net nginx: image: nginx:1.21-alpine @@ -28,4 +34,6 @@ services: - ./devops/docker/nginx/etc/nginx/conf.d/default.conf:/etc/nginx/conf.d/default.conf ports: - "8054:80" + networks: + - monitoring_monitor-net ...