Browse Source

add endpoint for grafana

set-sast-config-1
fedy95 4 years ago
parent
commit
a3f754c7c7
  1. 2
      .gitignore
  2. 1
      app/.gitignore
  3. 8
      app/composer.json
  4. 195
      app/composer.lock
  5. 45
      app/src/Controller/Api/v1/MattermostController.php
  6. 64
      app/src/Model/Grafana/EvalMatches.php
  7. 211
      app/src/Model/Grafana/GrafanaMessage.php
  8. 12
      app/src/Service/Mattermost/MattermostService.php
  9. 3
      app/src/Service/Mattermost/MattermostServiceInterface.php
  10. 6
      app/symfony.lock
  11. 2
      app/tests/Unit/Controller/Api/v1/MattermostControllerTest.php
  12. 2
      app/tests/Unit/KernelTest.php
  13. 110
      app/tests/Unit/Model/Grafana/EvalMatchesTest.php
  14. 313
      app/tests/Unit/Model/Grafana/GrafanaMessageTest.php
  15. 28
      app/tests/Unit/Service/Mattermost/MattermostServiceTest.php
  16. 10
      app/tests/Unit/UnitTester.php
  17. 17
      devops/docker/php/Dockerfile
  18. 1
      devops/docker/php/xdebug.ini
  19. 10
      docker-compose-local.yml

2
.gitignore

@ -0,0 +1,2 @@
/app/.phpunit.result.cache
/.phpunit.result.cache

1
app/.gitignore

@ -1,3 +1,2 @@
/.env /.env
/.phpunit.result.cache
/phpunit.xml /phpunit.xml

8
app/composer.json

@ -10,12 +10,16 @@
"doctrine/annotations": "^1.13", "doctrine/annotations": "^1.13",
"guzzlehttp/guzzle": "^7.3", "guzzlehttp/guzzle": "^7.3",
"nelmio/api-doc-bundle": "^4.6", "nelmio/api-doc-bundle": "^4.6",
"phpdocumentor/reflection-docblock": "^5.2",
"symfony/asset": "5.3.*", "symfony/asset": "5.3.*",
"symfony/console": "5.3.*", "symfony/console": "5.3.*",
"symfony/dotenv": "5.3.*", "symfony/dotenv": "5.3.*",
"symfony/flex": "^1.16", "symfony/flex": "^1.16",
"symfony/framework-bundle": "5.3.*", "symfony/framework-bundle": "5.3.*",
"symfony/property-access": "5.3.*",
"symfony/property-info": "5.3.*",
"symfony/runtime": "5.3.*", "symfony/runtime": "5.3.*",
"symfony/serializer": "5.3.*",
"symfony/twig-bundle": "5.3.*", "symfony/twig-bundle": "5.3.*",
"symfony/yaml": "5.3.*", "symfony/yaml": "5.3.*",
"twig/extra-bundle": "^2.12 || ^3.0", "twig/extra-bundle": "^2.12 || ^3.0",
@ -79,6 +83,10 @@
"cache:clear": "symfony-cmd", "cache:clear": "symfony-cmd",
"assets:install %PUBLIC_DIR%": "symfony-cmd" "assets:install %PUBLIC_DIR%": "symfony-cmd"
}, },
"cs:fix": [
"@json-cs-fix",
"@php-cs-fix"
],
"json-cs-fix": "composer normalize", "json-cs-fix": "composer normalize",
"lint:php": [ "lint:php": [
"parallel-lint --no-progress --no-colors --blame ./bin/console", "parallel-lint --no-progress --no-colors --blame ./bin/console",

195
app/composer.lock

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "42ca436544abf8d5ea35614dcf79d64e",
"content-hash": "1fa5d80ef6ea75d8cbdc00ef247597e7",
"packages": [ "packages": [
{ {
"name": "doctrine/annotations", "name": "doctrine/annotations",
@ -3114,6 +3114,87 @@
], ],
"time": "2021-05-21T13:25:03+00:00" "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", "name": "symfony/property-info",
"version": "v5.3.7", "version": "v5.3.7",
@ -3371,6 +3452,108 @@
], ],
"time": "2021-06-30T13:49:12+00:00" "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", "name": "symfony/service-contracts",
"version": "v2.4.0", "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": ">=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-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-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/ezplatform-user": ">=1,<1.0.1",
"ezsystems/ezpublish-kernel": "<=6.13.8.1|>=7,<=7.5.15.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", "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", "friendsoftypo3/mediace": ">=7.6.2,<7.6.5",
"froala/wysiwyg-editor": "<3.2.7", "froala/wysiwyg-editor": "<3.2.7",
"fuel/core": "<1.8.1", "fuel/core": "<1.8.1",
"getgrav/grav": "<=1.7.10",
"getgrav/grav": "<1.7.21",
"getkirby/cms": "<=3.5.6", "getkirby/cms": "<=3.5.6",
"getkirby/panel": "<2.5.14", "getkirby/panel": "<2.5.14",
"gilacms/gila": "<=1.11.4",
"gos/web-socket-bundle": "<1.10.4|>=2,<2.6.1|>=3,<3.3", "gos/web-socket-bundle": "<1.10.4|>=2,<2.6.1|>=3,<3.3",
"gree/jose": "<=2.2", "gree/jose": "<=2.2",
"gregwar/rst": "<1.0.3", "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", "guzzlehttp/guzzle": ">=4-rc.2,<4.2.4|>=5,<5.3.1|>=6,<6.2.1",
"helloxz/imgurl": "<=2.31", "helloxz/imgurl": "<=2.31",
"ibexa/post-install": "<=1.0.4", "ibexa/post-install": "<=1.0.4",
@ -6023,6 +6207,7 @@
"laravel/framework": "<6.20.26|>=7,<8.40", "laravel/framework": "<6.20.26|>=7,<8.40",
"laravel/socialite": ">=1,<1.0.99|>=2,<2.0.10", "laravel/socialite": ">=1,<1.0.99|>=2,<2.0.10",
"lavalite/cms": "<=5.8", "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/commonmark": "<0.18.3",
"league/flysystem": "<1.1.4|>=2,<2.1.1", "league/flysystem": "<1.1.4|>=2,<2.1.1",
"lexik/jwt-authentication-bundle": "<2.10.7|>=2.11,<2.11.3", "lexik/jwt-authentication-bundle": "<2.10.7|>=2.11,<2.11.3",
@ -6186,6 +6371,7 @@
"theonedemon/phpwhois": "<=4.2.5", "theonedemon/phpwhois": "<=4.2.5",
"titon/framework": ">=0,<9.9.99", "titon/framework": ">=0,<9.9.99",
"topthink/think": "<=6.0.9", "topthink/think": "<=6.0.9",
"topthink/thinkphp": "<=3.2.3",
"tribalsystems/zenario": "<8.8.53370", "tribalsystems/zenario": "<8.8.53370",
"truckersmp/phpwhois": "<=4.3.1", "truckersmp/phpwhois": "<=4.3.1",
"twig/twig": "<1.38|>=2,<2.7", "twig/twig": "<1.38|>=2,<2.7",
@ -6204,6 +6390,7 @@
"verot/class.upload.php": "<=1.0.3|>=2,<=2.0.4", "verot/class.upload.php": "<=1.0.3|>=2,<=2.0.4",
"vrana/adminer": "<4.7.9", "vrana/adminer": "<4.7.9",
"wallabag/tcpdf": "<6.2.22", "wallabag/tcpdf": "<6.2.22",
"web-auth/webauthn-framework": ">=3.3,<3.3.4",
"webcoast/deferred-image-processing": "<1.0.2", "webcoast/deferred-image-processing": "<1.0.2",
"wikimedia/parsoid": "<0.12.2", "wikimedia/parsoid": "<0.12.2",
"willdurand/js-translation-bundle": "<2.1.1", "willdurand/js-translation-bundle": "<2.1.1",

45
app/src/Controller/Api/v1/MattermostController.php

@ -4,49 +4,68 @@ declare(strict_types=1);
namespace App\Controller\Api\v1; namespace App\Controller\Api\v1;
use App\Model\Grafana\GrafanaMessage;
use App\Service\Mattermost\MattermostServiceInterface; use App\Service\Mattermost\MattermostServiceInterface;
use Nelmio\ApiDocBundle\Annotation\Model;
use OpenApi\Annotations as OA; use OpenApi\Annotations as OA;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Serializer\SerializerInterface;
use Throwable; use Throwable;
class MattermostController extends AbstractController class MattermostController extends AbstractController
{ {
protected LoggerInterface $logger;
protected SerializerInterface $serializer;
protected MattermostServiceInterface $mattermostService; 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; $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 <br>
* - https://grafana.com/docs/grafana/latest/alerting/old-alerting/notifications/#webhook <br>
* - 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\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( * @OA\Response(
* response=200, * response=200,
* description="Success delivery" * description="Success delivery"
* ) * )
*/ */
public function sendMessage(Request $request): Response
public function sendMessageFromGrafana(Request $request): Response
{ {
try { 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()); return new Response($response->getBody()->getContents());
} catch (Throwable $e) { } catch (Throwable $e) {
$this->logger->critical($e->getMessage());
return new Response( return new Response(
$e->getMessage(), $e->getMessage(),
0 === $e->getCode() ? Response::HTTP_INTERNAL_SERVER_ERROR : $e->getCode() 0 === $e->getCode() ? Response::HTTP_INTERNAL_SERVER_ERROR : $e->getCode()

64
app/src/Model/Grafana/EvalMatches.php

@ -0,0 +1,64 @@
<?php
declare(strict_types=1);
namespace App\Model\Grafana;
use OpenApi\Annotations as OA;
/**
* @OA\Schema(
* required={"value", "metric", "tags"}
* )
*/
class EvalMatches
{
/**
* @OA\Property(description="", example="1")
*/
protected int $value;
/**
* @OA\Property(description="", example="metric")
*/
protected string $metric;
/**
* @OA\Property(description="")
*/
protected ?string $tags;
public function getValue(): int
{
return $this->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;
}
}

211
app/src/Model/Grafana/GrafanaMessage.php

@ -0,0 +1,211 @@
<?php
declare(strict_types=1);
namespace App\Model\Grafana;
use OpenApi\Annotations as OA;
/**
* @OA\Schema(
* required={
* "dashboardId",
* "evalMatches",
* "imageUrl",
* "message",
* "orgId",
* "panelId",
* "ruleId",
* "ruleName",
* "ruleUrl",
* "state",
* "title"
* }
* )
*/
class GrafanaMessage
{
/**
* @OA\Property(example="1")
*/
protected int $dashboardId;
/**
* @var EvalMatches[]
* @OA\Property()
*/
protected array $evalMatches;
/**
* @OA\Property(example="https://grafana.com/assets/img/blog/mixed_styles.png")
*/
protected string $imageUrl;
/**
* @OA\Property(example="Notification Message")
*/
protected string $message;
/**
* @OA\Property(example="1")
*/
protected int $orgId;
/**
* @OA\Property(example="2")
*/
protected int $panelId;
/**
* @OA\Property(example="3")
*/
protected int $ruleId;
/**
* @OA\Property(example="Panel Title alert")
*/
protected string $ruleName;
/**
* @OA\Property(example="http://localhost:3000/d/hZ7BuVbWz/test-dashboard?fullscreen\u0026edit\u0026tab=alert\u0026panelId=2\u0026orgId=1")
*/
protected string $ruleUrl;
/**
* @OA\Property(example="alerting")
*/
protected string $state;
/**
* @OA\Property(example="[Alerting] Panel Title alert")
*/
protected string $title;
public function getDashboardId(): int
{
return $this->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;
}
}

12
app/src/Service/Mattermost/MattermostService.php

@ -4,6 +4,7 @@ declare(strict_types=1);
namespace App\Service\Mattermost; namespace App\Service\Mattermost;
use App\Model\Grafana\GrafanaMessage;
use App\Service\BaseService; use App\Service\BaseService;
use GuzzleHttp\Exception\GuzzleException; use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Psr7\Request; use GuzzleHttp\Psr7\Request;
@ -34,10 +35,17 @@ class MattermostService extends BaseService implements MattermostServiceInterfac
* @throws GuzzleException * @throws GuzzleException
* @throws \JsonException * @throws \JsonException
*/ */
public function sendMessage(string $message): ResponseInterface
public function sendGrafanaMessage(GrafanaMessage $grafanaMessage): ResponseInterface
{ {
$request = new Request('POST', $this->mattermostUri); $request = new Request('POST', $this->mattermostUri);
$text = sprintf(
"%s\n %s\n %s\n\n",
$grafanaMessage->getTitle(),
$grafanaMessage->getMessage(),
$grafanaMessage->getImageUrl()
);
$options = [ $options = [
'headers' => [ 'headers' => [
'Content-Type' => 'application/json', 'Content-Type' => 'application/json',
@ -46,7 +54,7 @@ class MattermostService extends BaseService implements MattermostServiceInterfac
'channel' => $this->channelName, 'channel' => $this->channelName,
'username' => $this->botName, 'username' => $this->botName,
'icon_url' => $this->botIcon, 'icon_url' => $this->botIcon,
'text' => $message,
'text' => $text,
], JSON_THROW_ON_ERROR), ], JSON_THROW_ON_ERROR),
]; ];

3
app/src/Service/Mattermost/MattermostServiceInterface.php

@ -4,9 +4,10 @@ declare(strict_types=1);
namespace App\Service\Mattermost; namespace App\Service\Mattermost;
use App\Model\Grafana\GrafanaMessage;
use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ResponseInterface;
interface MattermostServiceInterface interface MattermostServiceInterface
{ {
public function sendMessage(string $message): ResponseInterface;
public function sendGrafanaMessage(GrafanaMessage $grafanaMessage): ResponseInterface;
} }

6
app/symfony.lock

@ -345,6 +345,9 @@
"symfony/process": { "symfony/process": {
"version": "v5.3.7" "version": "v5.3.7"
}, },
"symfony/property-access": {
"version": "v5.3.8"
},
"symfony/property-info": { "symfony/property-info": {
"version": "v5.3.7" "version": "v5.3.7"
}, },
@ -364,6 +367,9 @@
"symfony/runtime": { "symfony/runtime": {
"version": "v5.3.4" "version": "v5.3.4"
}, },
"symfony/serializer": {
"version": "v5.3.8"
},
"symfony/service-contracts": { "symfony/service-contracts": {
"version": "v2.4.0" "version": "v2.4.0"
}, },

2
app/tests/Unit/Controller/Api/v1/MattermostControllerTest.php

@ -8,7 +8,7 @@ use App\Controller\Api\v1\MattermostController;
use App\Tests\Unit\UnitTester; use App\Tests\Unit\UnitTester;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
class MattermostControllerTest extends UnitTester
final class MattermostControllerTest extends UnitTester
{ {
public function testIsExtends(): void public function testIsExtends(): void
{ {

2
app/tests/Unit/KernelTest.php

@ -8,7 +8,7 @@ use App\Kernel;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpKernel\Kernel as BaseKernel; use Symfony\Component\HttpKernel\Kernel as BaseKernel;
class KernelTest extends TestCase
final class KernelTest extends TestCase
{ {
public function testIsExtends(): void public function testIsExtends(): void
{ {

110
app/tests/Unit/Model/Grafana/EvalMatchesTest.php

@ -0,0 +1,110 @@
<?php
declare(strict_types=1);
namespace App\Tests\Unit\Model\Grafana;
use App\Model\Grafana\EvalMatches;
use App\Tests\Unit\UnitTester;
use ReflectionException;
final class EvalMatchesTest extends UnitTester
{
/**
* @throws ReflectionException
*/
public function testGetValue(): void
{
$evalMatches = new EvalMatches();
$value = $this->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()],
];
}
}

313
app/tests/Unit/Model/Grafana/GrafanaMessageTest.php

@ -0,0 +1,313 @@
<?php
declare(strict_types=1);
namespace App\Tests\Unit\Model\Grafana;
use App\Model\Grafana\EvalMatches;
use App\Model\Grafana\GrafanaMessage;
use App\Tests\Unit\UnitTester;
use ReflectionException;
final class GrafanaMessageTest extends UnitTester
{
/**
* @throws ReflectionException
*/
public function testGetDashboardId(): void
{
$grafanaMessage = new GrafanaMessage();
$value = $this->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)
);
}
}

28
app/tests/Unit/Service/Mattermost/MattermostServiceTest.php

@ -4,6 +4,7 @@ declare(strict_types=1);
namespace App\Tests\Unit\Service\Mattermost; namespace App\Tests\Unit\Service\Mattermost;
use App\Model\Grafana\GrafanaMessage;
use App\Service\BaseService; use App\Service\BaseService;
use App\Service\Mattermost\MattermostService; use App\Service\Mattermost\MattermostService;
use App\Service\Mattermost\MattermostServiceInterface; use App\Service\Mattermost\MattermostServiceInterface;
@ -12,8 +13,9 @@ use GuzzleHttp\Client;
use GuzzleHttp\Psr7\Request; use GuzzleHttp\Psr7\Request;
use App\Tests\Unit\UnitTester; use App\Tests\Unit\UnitTester;
use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ResponseInterface;
use ReflectionException;
class MattermostServiceTest extends UnitTester
final class MattermostServiceTest extends UnitTester
{ {
public function testIsExtends(): void 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 $mattermostUrl,
string $mattermostUri, string $mattermostUri,
string $channelName, string $channelName,
string $botName, string $botName,
string $botIcon, string $botIcon,
string $message,
GrafanaMessage $grafanaMessage,
array $options array $options
): void ): void
{ {
@ -62,20 +64,22 @@ class MattermostServiceTest extends UnitTester
self::assertEquals( self::assertEquals(
$responseMock, $responseMock,
$mattermostService->sendMessage($message)
$mattermostService->sendGrafanaMessage($grafanaMessage)
); );
} }
/** /**
* @throws \JsonException * @throws \JsonException
*/ */
public function sendMessageDataProvider(): array
public function sendGrafanaMessageDataProvider(): array
{ {
$faker = (Factory::create('en_EN')); $faker = (Factory::create('en_EN'));
$channelName = $faker->sha256(); $channelName = $faker->sha256();
$botName = $faker->sha256(); $botName = $faker->sha256();
$botIcon = $faker->sha256(); $botIcon = $faker->sha256();
$title = $faker->sha256();
$message = $faker->sha256(); $message = $faker->sha256();
$imageUrl = $faker->sha256();
return [ return [
[ [
@ -84,7 +88,11 @@ class MattermostServiceTest extends UnitTester
'channelName' => $channelName, 'channelName' => $channelName,
'botName' => $botName, 'botName' => $botName,
'botIcon' => $botIcon, 'botIcon' => $botIcon,
'message' => $message,
'message' =>
(new GrafanaMessage())
->setTitle($title)
->setMessage($message)
->setImageUrl($imageUrl),
'options' => [ 'options' => [
'headers' => [ 'headers' => [
'Content-Type' => 'application/json', 'Content-Type' => 'application/json',
@ -93,7 +101,9 @@ class MattermostServiceTest extends UnitTester
'channel' => $channelName, 'channel' => $channelName,
'username' => $botName, 'username' => $botName,
'icon_url' => $botIcon, 'icon_url' => $botIcon,
'text' => $message,
'text' => sprintf("%s\n %s\n %s\n\n",
$title, $message, $imageUrl
),
], JSON_THROW_ON_ERROR), ], JSON_THROW_ON_ERROR),
] ]
] ]

10
app/tests/Unit/UnitTester.php

@ -4,6 +4,8 @@ declare(strict_types=1);
namespace App\Tests\Unit; namespace App\Tests\Unit;
use Faker\Factory;
use Faker\Generator;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use ReflectionClass; use ReflectionClass;
use ReflectionException; use ReflectionException;
@ -11,6 +13,14 @@ use ReflectionProperty;
class UnitTester extends TestCase 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 * @throws ReflectionException
*/ */

17
devops/docker/php/Dockerfile

@ -1,19 +1,13 @@
FROM php:8.0-fpm-alpine3.14 AS base FROM php:8.0-fpm-alpine3.14 AS base
RUN apk update && apk upgrade && \ 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 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 RUN mkdir -p /var/www/localhost
WORKDIR /var/www/localhost WORKDIR /var/www/localhost
COPY ./app .
FROM base AS test
FROM base AS pre_test
ARG XDEBUG_VERSION=3.0.4 ARG XDEBUG_VERSION=3.0.4
RUN curl --proto =https --tlsv1.2 "https://pecl.php.net/get/xdebug-${XDEBUG_VERSION}.tgz" && \ RUN curl --proto =https --tlsv1.2 "https://pecl.php.net/get/xdebug-${XDEBUG_VERSION}.tgz" && \
pecl install 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 && \ rm -rf /tmp/pear && \
docker-php-ext-enable xdebug docker-php-ext-enable xdebug
COPY devops/docker/php/xdebug.ini /usr/local/etc/php/conf.d/xdebug.ini 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 COPY env/app/app.env-dist .env
RUN composer install --no-interaction --optimize-autoloader RUN composer install --no-interaction --optimize-autoloader
FROM test AS local
FROM pre_test AS local
ARG HOME=/home/fedy95 ARG HOME=/home/fedy95
ARG USER=fedy95 ARG USER=fedy95
ARG UID=1000 ARG UID=1000

1
devops/docker/php/xdebug.ini

@ -1,4 +1,3 @@
zend_extension=xdebug.so
xdebug.client_host=host.docker.internal xdebug.client_host=host.docker.internal
xdebug.mode=coverage,debug,profile xdebug.mode=coverage,debug,profile
xdebug.discover_client_host=1 xdebug.discover_client_host=1

10
docker-compose-local.yml

@ -1,6 +1,10 @@
--- ---
version: "3.4" version: "3.4"
networks:
monitoring_monitor-net:
external: true
services: services:
app: app:
build: build:
@ -11,11 +15,13 @@ services:
environment: environment:
COMPOSER_MEMORY_LIMIT: "-1" COMPOSER_MEMORY_LIMIT: "-1"
PHP_IDE_CONFIG: "serverName=notification-provider_php"
PHP_IDE_CONFIG: "serverName=notification-provider_app"
volumes: volumes:
- ./app:/var/www/localhost - ./app:/var/www/localhost
expose: expose:
- "9000" - "9000"
networks:
- monitoring_monitor-net
nginx: nginx:
image: nginx:1.21-alpine 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 - ./devops/docker/nginx/etc/nginx/conf.d/default.conf:/etc/nginx/conf.d/default.conf
ports: ports:
- "8054:80" - "8054:80"
networks:
- monitoring_monitor-net
... ...
Loading…
Cancel
Save