diff --git a/.github/workflows/run-coverage.yml b/.github/workflows/run-coverage.yml new file mode 100644 index 0000000..3a4f738 --- /dev/null +++ b/.github/workflows/run-coverage.yml @@ -0,0 +1,80 @@ +name: Coverage + +on: + push: + paths: + - "**.php" + - ".github/workflows/run-tests-pest.yml" + - "phpunit.xml.dist" + - "composer.json" + - "composer.lock" + +jobs: + test: + runs-on: ${{ matrix.os }} + timeout-minutes: 5 + strategy: + max-parallel: 1 + fail-fast: true + matrix: + os: [ubuntu-latest] + php: [8.1] + stability: [prefer-stable] + + name: P${{ matrix.php }} - ${{ matrix.stability }} - ${{ matrix.os }} + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick, fileinfo + coverage: pcov + + - name: Setup mailpit + run: | + docker run -d \ + --restart unless-stopped \ + --name=mailpit \ + -p 8025:8025 \ + -p 1025:1025 \ + axllent/mailpit + + - name: Setup problem matchers + run: | + echo "::add-matcher::${{ runner.tool_cache }}/php.json" + echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" + + - name: Install dependencies + run: composer update --${{ matrix.stability }} --prefer-dist --no-interaction + + - name: List Installed Dependencies + run: composer show -D + + - name: Create .env + run: | + touch .env + echo NOTIFIER_DISCORD_WEBHOOK=${{ secrets.NOTIFIER_DISCORD_WEBHOOK }} >> .env + echo NOTIFIER_SLACK_WEBHOOK=${{ secrets.NOTIFIER_SLACK_WEBHOOK }} >> .env + echo NOTIFIER_MAIL_MAILER=${{ secrets.NOTIFIER_MAIL_MAILER }} >> .env + echo NOTIFIER_MAIL_HOST=${{ secrets.NOTIFIER_MAIL_HOST }} >> .env + echo NOTIFIER_MAIL_PORT=${{ secrets.NOTIFIER_MAIL_PORT }} >> .env + echo NOTIFIER_MAIL_USERNAME=${{ secrets.NOTIFIER_MAIL_USERNAME }} >> .env + echo NOTIFIER_MAIL_PASSWORD=${{ secrets.NOTIFIER_MAIL_PASSWORD }} >> .env + echo NOTIFIER_MAIL_ENCRYPTION=${{ secrets.NOTIFIER_MAIL_ENCRYPTION }} >> .env + echo NOTIFIER_MAIL_FROM_ADDRESS=${{ secrets.NOTIFIER_MAIL_FROM_ADDRESS }} >> .env + echo NOTIFIER_MAIL_TO_ADDRESS=${{ secrets.NOTIFIER_MAIL_TO_ADDRESS }} >> .env + echo NOTIFIER_REQUEST=${{ secrets.NOTIFIER_REQUEST }} >> .env + echo NOTIFIER_MOCK=false >> .env + + - name: Execute tests + run: vendor/bin/pest --coverage + + - name: Send code coverage + uses: codecov/codecov-action@v4 + with: + token: ${{ secrets.CODECOV_TOKEN }} + verbose: false diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index e4a2d74..7cca238 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -83,10 +83,4 @@ jobs: echo NOTIFIER_MAIL_TO_NAME=${{ secrets.NOTIFIER_MAIL_TO_NAME }} >> .env - name: Execute tests - run: vendor/bin/pest --coverage - - - name: Send code coverage - uses: codecov/codecov-action@v4 - with: - token: ${{ secrets.CODECOV_TOKEN }} - verbose: false + run: vendor/bin/pest --ci diff --git a/README.md b/README.md index ca4475c..60e2acb 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,8 @@ Notifier for Laravel is a package to add some useful classes to send notifications (with `Notifier::class`) and monitoring (with `Journal::class`). The configuration file is totally optional, if you have multiple webhooks, you can create your own configs to send notifications. +Based on [`kiwilan/php-notifier`](https://github.com/kiwilan/php-notifier). + Works for [Discord webhooks](https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks), [Slack webhooks](https://api.slack.com/messaging/webhooks) and emails with [`symfony/mailer`](https://symfony.com/doc/current/mailer.html). ## About @@ -84,6 +86,11 @@ return [ 'webhook' => env('NOTIFIER_SLACK_WEBHOOK', null), ], + 'http' => [ + // Default HTTP URL to send request. + 'url' => env('NOTIFIER_HTTP_URL', null), + ], + // This feature use `filament/notifications` package, not included in this package. 'to_database' => [ // Default user model for notification. @@ -170,7 +177,7 @@ class Handler extends ExceptionHandler Notifier is an alternative to [Laravel Notifications](https://laravel.com/docs/10.x/notifications). > [!NOTE] -> If `app.debug` is `true`, `debug` level logs will be written for sending and sent notifications. +> If `notifier.journal.debug` is `true`, `debug` level logs will be written for sending and sent notifications. > In all cases, `error` level logs will be written for sending errors. #### Discord diff --git a/composer.json b/composer.json index d3eb392..f4f16ab 100644 --- a/composer.json +++ b/composer.json @@ -22,8 +22,8 @@ "require": { "php": "^8.1", "illuminate/contracts": "^10.0", - "spatie/laravel-package-tools": "^1.14.0", - "symfony/mailer": "^6.4" + "kiwilan/php-notifier": "^0.0.31", + "spatie/laravel-package-tools": "^1.14.0" }, "require-dev": { "filament/notifications": "^3.2", @@ -42,13 +42,13 @@ }, "autoload": { "psr-4": { - "Kiwilan\\Notifier\\": "src/", - "Kiwilan\\Notifier\\Database\\Factories\\": "database/factories/" + "Kiwilan\\LaravelNotifier\\": "src/", + "Kiwilan\\LaravelNotifier\\Database\\Factories\\": "database/factories/" } }, "autoload-dev": { "psr-4": { - "Kiwilan\\Notifier\\Tests\\": "tests/", + "Kiwilan\\LaravelNotifier\\Tests\\": "tests/", "Workbench\\App\\": "workbench/app/" } }, @@ -81,10 +81,10 @@ "extra": { "laravel": { "providers": [ - "Kiwilan\\Notifier\\NotifierServiceProvider" + "Kiwilan\\LaravelNotifier\\LaravelNotifierServiceProvider" ], "aliases": { - "Notifier": "Kiwilan\\Notifier\\Facades\\Notifier" + "LaravelNotifier": "Kiwilan\\LaravelNotifier\\Facades\\Notifier" } } }, diff --git a/config/notifier.php b/config/notifier.php index 8043e7c..8c2e08f 100644 --- a/config/notifier.php +++ b/config/notifier.php @@ -37,6 +37,11 @@ 'webhook' => env('NOTIFIER_SLACK_WEBHOOK', null), ], + 'http' => [ + // Default HTTP URL to send request. + 'url' => env('NOTIFIER_HTTP_URL', null), + ], + // This feature use `filament/notifications` package, not included in this package. 'to_database' => [ // Default user model for notification. @@ -46,6 +51,7 @@ ], 'journal' => [ + 'debug' => env('NOTIFIER_JOURNAL_DEBUG', false), // Write error logs with `error_log` function, in addition to Laravel log. 'use_error_log' => env('NOTIFIER_JOURNAL_USE_ERROR_LOG', true), ], diff --git a/src/Commands/NotifierCommand.php b/src/Commands/NotifierCommand.php index 97b5ffe..7a89a33 100644 --- a/src/Commands/NotifierCommand.php +++ b/src/Commands/NotifierCommand.php @@ -1,9 +1,9 @@ data, JSON_PRETTY_PRINT); error_log("Journal: {$this->level} - {$this->message} - {$data}"); } + + // dump($this->message, $this->data); } /** @@ -149,6 +152,7 @@ public function toNotifier(string $type): self 'mail' => Notifier::mail(), 'slack' => Notifier::slack(), 'discord' => Notifier::discord(), + 'http' => Notifier::http(), default => null, }; diff --git a/src/NotifierServiceProvider.php b/src/LaravelNotifierServiceProvider.php similarity index 74% rename from src/NotifierServiceProvider.php rename to src/LaravelNotifierServiceProvider.php index 203f70f..eca2d44 100644 --- a/src/NotifierServiceProvider.php +++ b/src/LaravelNotifierServiceProvider.php @@ -1,12 +1,12 @@ type = 'mail'; - $self->requestData = []; - - return NotifierMail::make(); + return NotifierMail::make() + ->autoConfig($this->autoMail()) + ->logSending(function (array $data) { + $this->logSending('mail', $data); + }) + ->logError(function (string $reason, array $data = []) { + $this->logError('mail', $reason, $data); + }) + ->logSent(function (array $data) { + $this->logSent('mail', $data); + }); } /** @@ -39,18 +39,19 @@ public static function mail(): NotifierMail * * @see https://api.slack.com/messaging/webhooks */ - public static function slack(?string $webhook = null): NotifierSlack + public function slack(?string $webhook = null, ?string $client = null): NotifierSlack { - $self = new self(); - $self->type = 'slack'; - if (! $webhook) { $webhook = config('notifier.slack.webhook'); } - $self->checkWebhook($webhook); - - return NotifierSlack::make($webhook); + return NotifierSlack::make($webhook, $this->setClient($client)) + ->logError(function (string $reason, array $data = []) { + $this->logError('slack', $reason, $data); + }) + ->logSent(function (array $data) { + $this->logSent('slack', $data); + }); } /** @@ -60,55 +61,112 @@ public static function slack(?string $webhook = null): NotifierSlack * * @see https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks */ - public static function discord(?string $webhook = null): NotifierDiscord + public function discord(?string $webhook = null, ?string $client = null): NotifierDiscord { - $self = new self(); - $self->type = 'discord'; - if (! $webhook) { $webhook = config('notifier.discord.webhook'); } - $self->checkWebhook($webhook); + return NotifierDiscord::make($webhook, $this->setClient($client)) + ->logError(function (string $reason, array $data = []) { + $this->logError('discord', $reason, $data); + }) + ->logSent(function (array $data) { + $this->logSent('discord', $data); + }); + } + + /** + * Send notification to HTTP endpoint. + * + * @param string $url HTTP endpoint URL + */ + public function http(?string $url = null, ?string $client = null): NotifierHttp + { + if (! $url) { + $url = config('notifier.http.url'); + } - return NotifierDiscord::make($webhook); + if (! $url) { + throw new \Exception('Notifier: HTTP URL is not set'); + } + + return NotifierHttp::make($url, $this->setClient($client)) + ->logError(function (string $reason, array $data = []) { + $this->logError('http', $reason, $data); + }) + ->logSent(function (array $data) { + $this->logSent('http', $data); + }); } - protected function logSending(string $message): void + protected function logSending(string $type, array $data = []): void { - if (config('app.debug') === true) { - Log::debug("Notifier: sending {$this->type} notification: {$message}..."); + if ($this->isDebug()) { + Journal::debug("Notifier for {$type} sending", $data); } } - protected function logError(string $reason, array $data = []): void + protected function logError(string $type, string $reason, array $data = []): void { - Log::error("Notifier: notification failed: {$reason}", $data); + Journal::error("Notifier for {$type} failed: {$reason}", $data); } - protected function logSent(): void + protected function logSent(string $type, array $data = []): void { - if (config('app.debug') === true) { - Log::debug("Notifier: {$this->type} notification sent"); + if ($this->isDebug()) { + Journal::debug("Notifier for {$type} sent", $data); } } - protected function checkWebhook(?string $webhook = null): void + private function isDebug(): bool { - if (! $webhook) { - throw new \InvalidArgumentException("Notifier: {$this->type} webhook URL is required"); + $dotenv = config('notifier.journal.debug'); + + if ($dotenv === 'true' || $dotenv === true) { + return true; } + + return false; } - /** - * @param string|string[] $message - */ - protected function arrayToString(array|string $message): string + private function setClient(?string $client = null): string { - if (is_string($message)) { - return $message; + if (! $client) { + $client = config('notifier.client', 'stream'); } - return implode(PHP_EOL, $message); + return $client; + } + + /** + * Use default mailer from `.env` file. + */ + private function autoMail(): array + { + $laravel_override = config('notifier.mail.laravel_override'); + $mailer = $laravel_override ? config('mail.mailer') : config('notifier.mail.mailer'); + $host = $laravel_override ? config('mail.host') : config('notifier.mail.host'); + $port = $laravel_override ? config('mail.port') : config('notifier.mail.port'); + $encryption = $laravel_override ? config('mail.encryption') : config('notifier.mail.encryption'); + $username = $laravel_override ? config('mail.username') : config('notifier.mail.username'); + $password = $laravel_override ? config('mail.password') : config('notifier.mail.password'); + $from = $laravel_override + ? new Address(config('mail.from.address'), config('mail.from.name')) + : new Address(config('notifier.mail.from.address'), config('notifier.mail.from.name')); + $to = [new Address(config('notifier.mail.to.address'), config('notifier.mail.to.name'))]; + $subject = config('notifier.mail.subject'); + + return [ + 'mailer' => $mailer, + 'host' => $host, + 'port' => $port, + 'encryption' => $encryption, + 'username' => $username, + 'password' => $password, + 'from' => $from, + 'to' => $to, + 'subject' => $subject, + ]; } } diff --git a/src/Notifier/Discord/NotifierDiscordContainer.php b/src/Notifier/Discord/NotifierDiscordContainer.php deleted file mode 100644 index b7f4b19..0000000 --- a/src/Notifier/Discord/NotifierDiscordContainer.php +++ /dev/null @@ -1,42 +0,0 @@ -isSuccess; - } - - public function send(): static - { - $this->request = NotifierRequest::make($this->webhook) - ->requestData($this->toArray()) - ->send(); - - $this->isSuccess = $this->request->getStatusCode() === 204; - - if ($this->isSuccess) { - Log::error("Notifier: discord notification failed with HTTP {$this->request->getStatusCode()}", [ - $this->request->toArray(), - ]); - } - - return $this; - } -} diff --git a/src/Notifier/Discord/NotifierDiscordMessage.php b/src/Notifier/Discord/NotifierDiscordMessage.php deleted file mode 100644 index 7684cee..0000000 --- a/src/Notifier/Discord/NotifierDiscordMessage.php +++ /dev/null @@ -1,53 +0,0 @@ -webhook = $webhook; - - return $self; - } - - public function user(string $username, ?string $avatarUrl = null): self - { - $this->username = $username; - - if ($avatarUrl) { - $this->avatarUrl = $avatarUrl; - } - - return $this; - } - - public function toArray(): array - { - $data = []; - - if ($this->username) { - $data['username'] = $this->username; - } - - if ($this->avatarUrl) { - $data['avatar_url'] = $this->avatarUrl; - } - - $data['content'] = $this->message ?? 'Empty message.'; - - return $data; - } -} diff --git a/src/Notifier/Discord/NotifierDiscordRich.php b/src/Notifier/Discord/NotifierDiscordRich.php deleted file mode 100644 index 0316d9b..0000000 --- a/src/Notifier/Discord/NotifierDiscordRich.php +++ /dev/null @@ -1,224 +0,0 @@ -webhook = $webhook; - - return $self; - } - - public function user(string $username, ?string $avatarUrl = null): self - { - $this->username = $username; - - if ($avatarUrl) { - $this->avatarUrl = $avatarUrl; - } - - return $this; - } - - public function author(string $name, ?string $url = null, ?string $iconUrl = null): self - { - $this->authorName = $name; - - if ($url) { - $this->authorUrl = $url; - } - - if ($iconUrl) { - $this->authorIconUrl = $iconUrl; - } - - return $this; - } - - public function url(string $url): self - { - $this->url = $url; - - return $this; - } - - public function title($title): self - { - $this->title = $title; - - return $this; - } - - public function timestamp(Carbon|DateTime $timestamp): self - { - if ($timestamp instanceof Carbon) { - $timestamp = $timestamp->toDateTime(); - } - - $this->timestamp = $timestamp->format(DateTime::ATOM); - - return $this; - } - - public function footer(string $footer, ?string $iconUrl = null): self - { - $this->footerText = $footer; - - if ($iconUrl) { - $this->footerIconUrl = $iconUrl; - } - - return $this; - } - - /** - * Set a color to rich embed, you can use shortcut methods like `success`, `warning`, `error` - * - * @param string $color Add hex color code (with or without `#` prefix) - */ - public function color(string $color): self - { - if (str_contains($color, '#')) { - $color = str_replace('#', '', $color); - } - - $this->color = $color; - - return $this; - } - - /** - * Set a green color to rich embed - */ - public function colorSuccess(): self - { - $this->color = $this->getShortcutColor('success'); - - return $this; - } - - /** - * Set a yellow color to rich embed - */ - public function colorWarning(): self - { - $this->color = $this->getShortcutColor('warning'); - - return $this; - } - - /** - * Set a red color to rich embed - */ - public function colorError(): self - { - $this->color = $this->getShortcutColor('error'); - - return $this; - } - - private function getShortcutColor(string $color): string - { - return match ($color) { - 'success' => '22c55e', - 'warning' => 'eab308', - 'error' => 'ef4444', - default => '22c55e', - }; - } - - /** - * Add fields to rich embed - * - * @param array{name: string, value: string|int} $fields Array of fields, each field should have `name` and `value` - * @param bool $inline Set to `true` if you want to display fields inline - */ - public function fields(array $fields, bool $inline = false): self - { - foreach ($fields as $field) { - $this->fields[] = [ - 'name' => $field['name'] ?? 'Field', - 'value' => $field['value'] ?? 'Value', - 'inline' => $inline, - ]; - } - - return $this; - } - - public function thumbnail(string $url): self - { - $this->thumbnail = $url; - - return $this; - } - - public function image(string $url): self - { - $this->image = $url; - - return $this; - } - - public function toArray(): array - { - return [ - 'username' => $this->username, - 'avatar_url' => $this->avatarUrl, - 'embeds' => [ - [ - 'author' => [ - 'name' => $this->authorName, - 'url' => $this->authorUrl, - 'icon_url' => $this->authorIconUrl, - ], - 'title' => $this->title, - 'url' => $this->url, - 'type' => 'rich', - 'description' => $this->description, - 'fields' => $this->fields, - 'color' => hexdec($this->color), - 'thumbnail' => [ - 'url' => $this->thumbnail, - ], - 'image' => [ - 'url' => $this->image, - ], - 'footer' => [ - 'text' => $this->footerText, - 'icon_url' => $this->footerIconUrl, - ], - 'timestamp' => $this->timestamp, - ], - ], - ]; - } -} diff --git a/src/Notifier/NotifierDiscord.php b/src/Notifier/NotifierDiscord.php deleted file mode 100644 index 658578b..0000000 --- a/src/Notifier/NotifierDiscord.php +++ /dev/null @@ -1,45 +0,0 @@ -arrayToString($message); - - return NotifierDiscordMessage::create($this->webhook, $message); - } - - /** - * @param string[]|string $message - */ - public function rich(array|string $message): NotifierDiscordRich - { - $message = $this->arrayToString($message); - - return NotifierDiscordRich::create($this->webhook, $message); - } -} diff --git a/src/Notifier/NotifierMail.php b/src/Notifier/NotifierMail.php deleted file mode 100644 index 1f2b23d..0000000 --- a/src/Notifier/NotifierMail.php +++ /dev/null @@ -1,315 +0,0 @@ -mailer = $mailer; - - return $this; - } - - /** - * @param string $host Mailer host, default `mailpit` - */ - public function host(string $host): self - { - $this->host = $host; - - return $this; - } - - /** - * @param int $port Mailer port, default `1025` - */ - public function port(int $port): self - { - $this->port = $port; - - return $this; - } - - /** - * @param string $encryption Mailer encryption, default `tls` - */ - public function encryption(string $encryption): self - { - $this->encryption = $encryption; - - return $this; - } - - public function credentials(string $username, string $password): self - { - $this->username = $username; - $this->password = $password; - - return $this; - } - - /** - * Use default mailer from `.env` file. - */ - private function auto(): self - { - if (config('notifier.mail.laravel_override')) { - if (! $this->mailer) { - $this->mailer = config('mail.mailer'); - } - - if (! $this->host) { - $this->host = config('mail.host'); - } - - if (! $this->port) { - $this->port = config('mail.port'); - } - - if (! $this->encryption) { - $this->encryption = config('mail.encryption'); - } - - if (! $this->username) { - $this->username = config('mail.username'); - } - - if (! $this->password) { - $this->password = config('mail.password'); - } - - if (! $this->from) { - $this->from = new Address(config('mail.from.address'), config('mail.from.name')); - } - - return $this; - } - - if (! $this->mailer) { - $this->mailer = config('notifier.mail.mailer'); - } - - if (! $this->host) { - $this->host = config('notifier.mail.host'); - } - - if (! $this->port) { - $this->port = config('notifier.mail.port'); - } - - if (! $this->encryption) { - $this->encryption = config('notifier.mail.encryption'); - } - - if (! $this->username) { - $this->username = config('notifier.mail.username'); - } - - if (! $this->password) { - $this->password = config('notifier.mail.password'); - } - - if (! $this->from) { - $this->from = new Address(config('notifier.mail.from.address'), config('notifier.mail.from.name')); - } - - if (count($this->to) === 0) { - $this->to = [new Address(config('notifier.mail.to.address'), config('notifier.mail.to.name'))]; - } - - if (! $this->subject) { - $this->subject = config('notifier.mail.subject'); - } - - if (! $this->html) { - $this->html = $this->message; - } - - return $this; - } - - /** - * @param Address[]|string $to Array of `Address` object - * @param string|null $name Useful if `$to` is a string - */ - public function to(array|string $to, ?string $name = null): self - { - if (is_string($to)) { - $to = [new Address($to, $name)]; - } - - $this->to = $to; - - return $this; - } - - public function from(string $from, ?string $name = null): self - { - $this->from = new Address($from, $name ?? ''); - - return $this; - } - - public function replyTo(string $replyTo, ?string $name = null): self - { - $this->replyTo = new Address($replyTo, $name); - - return $this; - } - - public function subject(string $subject): self - { - $this->subject = $subject; - - return $this; - } - - public function message(array|string $message): self - { - $this->message = $this->arrayToString($message); - - return $this; - } - - /** - * @param string|string[] $html - */ - public function html(array|string $html): self - { - $this->html = $this->arrayToString($html); - - return $this; - } - - /** - * Add attachment to the email. - * - * @param string $path File path - * @param string|null $name File name - */ - public function addAttachment(string $path, ?string $name = null): self - { - $this->attachments[] = [ - 'path' => $path, - 'name' => $name, - ]; - - return $this; - } - - public function isSuccess(): bool - { - return $this->isSuccess; - } - - public function send(): self - { - $this->auto(); - $this->mailTransport = Transport::fromDsn("{$this->mailer}://{$this->host}:{$this->port}"); - $this->mailMailer = new Mailer($this->mailTransport); - - $this->logSending("{$this->mailer}://{$this->host}:{$this->port}"); - - $this->mailEmail = (new Email()) - ->to(...$this->to) - ->from($this->from); - - if ($this->replyTo) { - $this->mailEmail->replyTo($this->replyTo); - } - - if ($this->subject) { - $this->mailEmail->subject($this->subject); - } - - if ($this->message) { - $this->mailEmail->text($this->message); - } - - if ($this->html) { - $this->mailEmail->html($this->html); - } - - if (! $this->html) { - $this->mailEmail->html($this->message); - } - - if (count($this->attachments) > 0) { - foreach ($this->attachments as $attachment) { - $this->mailEmail->attachFromPath($attachment['path'], $attachment['name']); - } - } - - try { - $this->mailMailer->send($this->mailEmail); - } catch (\Throwable $th) { - $this->logError($th->getMessage(), $this->toArray()); - - return $this; - } - - $this->isSuccess = true; - $this->logSent(); - - return $this; - } - - public function toArray(): array - { - return [ - 'mailer' => $this->mailer, - 'host' => $this->host, - 'port' => $this->port, - 'encryption' => $this->encryption, - 'username' => $this->username, - 'password' => $this->password, - 'to' => $this->to, - 'from' => $this->from, - 'replyTo' => $this->replyTo, - 'subject' => $this->subject, - 'message' => $this->message, - 'html' => $this->html, - 'attachments' => $this->attachments, - ]; - } -} diff --git a/src/Notifier/NotifierSlack.php b/src/Notifier/NotifierSlack.php deleted file mode 100644 index 6f79d50..0000000 --- a/src/Notifier/NotifierSlack.php +++ /dev/null @@ -1,56 +0,0 @@ -arrayToString($message); - - return NotifierSlackMessage::create($this->webhook, $message); - } - - /** - * @param string[]|string $message - */ - public function attachment(array|string $message): NotifierSlackAttachment - { - $message = $this->arrayToString($message); - - return NotifierSlackAttachment::create($this->webhook, $message); - } - - /** - * @param string[]|string $message - */ - public function blocks(array|string $message): NotifierSlackBlocks - { - $message = $this->arrayToString($message); - - return NotifierSlackBlocks::create($this->webhook, $message); - } -} diff --git a/src/Notifier/Slack/NotifierSlackAttachment.php b/src/Notifier/Slack/NotifierSlackAttachment.php deleted file mode 100644 index 33f55b5..0000000 --- a/src/Notifier/Slack/NotifierSlackAttachment.php +++ /dev/null @@ -1,64 +0,0 @@ -webhook = $webhook; - - return $self; - } - - public function toArray(): array - { - return [ - 'text' => $this->text, - 'attachments' => [ - [ - 'mrkdwn_in' => ['text'], - 'color' => '#36a64f', - 'pretext' => 'Optional pre-text that appears above the attachment block', - 'author_name' => 'author_name', - 'author_link' => 'http://flickr.com/bobby/', - 'author_icon' => 'https://raw.githubusercontent.com/kiwilan/notifier-laravel/main/docs/banner.jpg', - 'title' => 'title', - 'title_link' => 'https://api.slack.com/', - 'text' => 'Optional `text` that appears within the attachment', - 'fields' => [ - [ - 'title' => "A field's title", - 'value' => "This field's value", - 'short' => false, - ], - [ - 'title' => "A short field's title", - 'value' => "This field's value", - 'short' => true, - ], - [ - 'title' => "A short field's title", - 'value' => "This field's value", - 'short' => true, - ], - ], - 'image_url' => 'https://raw.githubusercontent.com/kiwilan/notifier-laravel/main/docs/banner.jpg', - 'footer' => 'footer', - 'footer_icon' => 'https://raw.githubusercontent.com/kiwilan/notifier-laravel/main/docs/banner.jpg', - 'ts' => 123456789, - ], - ], - ]; - } -} diff --git a/src/Notifier/Slack/NotifierSlackBlocks.php b/src/Notifier/Slack/NotifierSlackBlocks.php deleted file mode 100644 index ec8f603..0000000 --- a/src/Notifier/Slack/NotifierSlackBlocks.php +++ /dev/null @@ -1,102 +0,0 @@ -webhook = $webhook; - - return $self; - } - - public function toArray(): array - { - return [ - 'blocks' => [ - [ - 'type' => 'section', - 'text' => [ - 'type' => 'mrkdwn', - 'text' => 'Danny Torrence left the following review for your property:', - ], - 'fields' => [ - [ - 'type' => 'mrkdwn', - 'text' => '*Markdown!*', - ], - [ - 'type' => 'plain_text', - 'text' => str_repeat('a', 1997).'...', - ], - [ - 'type' => 'mrkdwn', - 'text' => 'Location: 123 Main Street, New York, NY 10010', - ], - ], - ], - [ - 'type' => 'divider', - ], - [ - 'type' => 'actions', - 'elements' => [ - [ - 'type' => 'button', - 'text' => [ - 'type' => 'plain_text', - 'text' => 'Example Button', - ], - 'action_id' => 'button_example-button', - ], - [ - 'type' => 'button', - 'text' => [ - 'type' => 'plain_text', - 'text' => 'Scary Button', - ], - 'action_id' => 'button_scary-button', - 'style' => 'danger', - ], - ], - ], - [ - 'type' => 'header', - 'text' => [ - 'type' => 'plain_text', - 'text' => 'Budget Performance', - ], - ], - [ - 'type' => 'image', - 'image_url' => 'https://raw.githubusercontent.com/kiwilan/notifier-laravel/main/docs/banner.jpg', - 'alt_text' => 'notifier banner', - ], - [ - 'type' => 'section', - 'block_id' => 'section567', - 'text' => [ - 'type' => 'mrkdwn', - 'text' => 'Markdown *can* be so fun!', - ], - 'accessory' => [ - 'type' => 'image', - 'image_url' => 'https://raw.githubusercontent.com/kiwilan/notifier-laravel/main/docs/banner.jpg', - 'alt_text' => 'notifier banner', - ], - ], - ], - ]; - } -} diff --git a/src/Notifier/Slack/NotifierSlackContainer.php b/src/Notifier/Slack/NotifierSlackContainer.php deleted file mode 100644 index 3e50630..0000000 --- a/src/Notifier/Slack/NotifierSlackContainer.php +++ /dev/null @@ -1,42 +0,0 @@ -isSuccess; - } - - public function send(): static - { - $this->request = NotifierRequest::make($this->webhook) - ->requestData($this->toArray()) - ->send(); - - $this->isSuccess = $this->request->getStatusCode() === 200; - - if ($this->isSuccess) { - Log::error("Notifier: slack notification failed with HTTP {$this->request->getStatusCode()}", [ - $this->request->toArray(), - ]); - } - - return $this; - } -} diff --git a/src/Notifier/Slack/NotifierSlackMessage.php b/src/Notifier/Slack/NotifierSlackMessage.php deleted file mode 100644 index 930fd7b..0000000 --- a/src/Notifier/Slack/NotifierSlackMessage.php +++ /dev/null @@ -1,30 +0,0 @@ -webhook = $webhook; - - return $self; - } - - public function toArray(): array - { - return [ - 'text' => $this->text, - ]; - } -} diff --git a/src/Utils/NotifierHelpers.php b/src/Utils/NotifierHelpers.php deleted file mode 100644 index 2c08d34..0000000 --- a/src/Utils/NotifierHelpers.php +++ /dev/null @@ -1,19 +0,0 @@ - $length) { - $string = substr($string, 0, $length - 20).'...'; - } - - return $string; - } -} diff --git a/src/Utils/NotifierRequest.php b/src/Utils/NotifierRequest.php deleted file mode 100644 index 73900e5..0000000 --- a/src/Utils/NotifierRequest.php +++ /dev/null @@ -1,262 +0,0 @@ -mode = 'stream'; - $this->modeAuto = false; - - return $this; - } - - /** - * Use cURL to send HTTP request. - */ - public function useCurl(): self - { - $this->mode = 'curl'; - $this->modeAuto = false; - - return $this; - } - - /** - * Use Guzzle to send HTTP request. - */ - public function useGuzzle(): self - { - $this->mode = 'guzzle'; - $this->modeAuto = false; - - return $this; - } - - /** - * Set the request data. - */ - public function requestData(array $data): self - { - $this->request_data = $data; - - return $this; - } - - /** - * Set the request method: `GET`, `POST`, `PUT`, `PATCH`, `DELETE`. - */ - public function method(string $method): self - { - $this->method = strtoupper($method); - - return $this; - } - - public function headers(array $headers): self - { - $this->headers = $headers; - - return $this; - } - - public function asJson(): self - { - $this->json = true; - - return $this; - } - - public function asForm(): self - { - $this->json = false; - - return $this; - } - - /** - * Send HTTP request. - */ - public function send(): self - { - try { - if ($this->modeAuto) { - $this->mode = config('notifier.client'); - } - - if ($this->mode === 'stream') { - $this->stream(); - } elseif ($this->mode === 'curl') { - $this->curl(); - } elseif ($this->mode === 'guzzle') { - $this->guzzle(); - } - } catch (\Throwable $th) { - $this->status_code = 500; - $this->response_body = [ - 'error' => $th->getMessage(), - ]; - } - - return $this; - } - - /** - * Send HTTP request using stream. - */ - private function stream(): void - { - $headers = $this->headers; - if ($this->json) { - $headers[] = 'Content-Type: application/json'; - } else { - $headers[] = 'Content-Type: application/x-www-form-urlencoded'; - } - - $context = stream_context_create([ - 'http' => [ - 'method' => $this->method, - 'header' => implode("\r\n", $headers), - 'content' => $this->json ? json_encode($this->request_data) : http_build_query($this->request_data), - ], - ]); - $response = file_get_contents($this->url, false, $context); - $headers = $http_response_header; - - $this->status_code = (int) explode(' ', $headers[0])[1]; - $this->response_body = json_decode($response, true); - } - - /** - * Send HTTP request using cURL. - */ - private function curl(): void - { - $ch = curl_init($this->url); - $headers = $this->headers; - if ($this->json) { - $headers[] = 'Content-Type: application/json'; - } - - curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); - - if ($this->method === 'POST') { - curl_setopt($ch, CURLOPT_POST, 1); - } elseif ($this->method === 'PUT') { - curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT'); - } elseif ($this->method === 'PATCH') { - curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PATCH'); - } elseif ($this->method === 'DELETE') { - curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE'); - } - - curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($this->request_data)); - curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); - curl_setopt($ch, CURLOPT_HEADER, 0); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - - $response = curl_exec($ch); - - $this->status_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); - $this->response_body = json_decode($response, true); - - curl_close($ch); - } - - /** - * Send HTTP request using Guzzle. - */ - private function guzzle(): void - { - if (! \Composer\InstalledVersions::isInstalled('guzzlehttp/guzzle')) { - throw new \Exception('Package `guzzlehttp/guzzle` not installed, see: https://github.com/guzzle/guzzle'); - } - - $client = new \GuzzleHttp\Client(); - $body = $this->json ? 'json' : 'form_params'; - $response = $client->request($this->method, $this->url, [ - $body => $this->request_data, - ]); - - $this->status_code = $response->getStatusCode(); - $this->response_body = json_decode($response->getBody()->getContents(), true); - } - - /** - * Get the request mode: `stream`, `curl`, or `guzzle`. - */ - public function getMode(): string - { - return $this->mode; - } - - /** - * Get the request URL. - */ - public function getUrl(): string - { - return $this->url; - } - - /** - * Get the request data. - */ - public function getRequestData(): array - { - return $this->request_data; - } - - /** - * Get the response body. - */ - public function getResponseBody(): array - { - return $this->response_body; - } - - /** - * Get the status code. - */ - public function getStatusCode(): ?int - { - return $this->status_code; - } - - public function toArray(): array - { - return [ - 'mode' => $this->mode, - 'url' => $this->url, - 'request_data' => $this->request_data, - 'response_body' => $this->response_body, - 'status_code' => $this->status_code, - ]; - } -} diff --git a/tests/JournalTest.php b/tests/JournalTest.php index 5917251..8d5caad 100644 --- a/tests/JournalTest.php +++ b/tests/JournalTest.php @@ -1,11 +1,12 @@ discord(); - - expect($facade)->toEqual($instance); - expect($facade)->toBeInstanceOf(NotifierNotifier::class); -}); - -it('can use clients', function () { - Config::set('notifier.client', 'stream'); - - $notifier = Notifier::discord() - ->message('Hello, Discord!') - ->send(); - expect($notifier->isSuccess())->toBeTrue(); - - Config::set('notifier.client', 'curl'); - - $notifier = Notifier::discord() - ->message('Hello, Discord!') - ->send(); - expect($notifier->isSuccess())->toBeTrue(); - - Config::set('notifier.client', 'guzzle'); - - $notifier = Notifier::discord() - ->message('Hello, Discord!') - ->send(); - expect($notifier->isSuccess())->toBeTrue(); -}); - it('can use', function () { $notifier = Notifier::discord() ->message('Hello, Discord!') @@ -133,17 +101,15 @@ it('can use command', function () { $success = Artisan::call('notifier', [ - 'message' => 'Hello, Discord!', + 'message' => 'Hello, Discord with webhook!', '--type' => 'discord', '--webhook' => config('notifier.discord.webhook'), ]); - expect($success)->toBe(0); $success = Artisan::call('notifier', [ - 'message' => 'Hello, Discord!', + 'message' => 'Hello, Discord with config!', '--type' => 'discord', ]); - expect($success)->toBe(0); }); diff --git a/tests/NotifierExceptionTest.php b/tests/NotifierExceptionTest.php deleted file mode 100644 index ded6dea..0000000 --- a/tests/NotifierExceptionTest.php +++ /dev/null @@ -1,8 +0,0 @@ - Notifier::discord())->toThrow(Exception::class); - expect(fn () => Notifier::slack())->toThrow(Exception::class); -}); diff --git a/tests/NotifierHttpTest.php b/tests/NotifierHttpTest.php new file mode 100644 index 0000000..8896331 --- /dev/null +++ b/tests/NotifierHttpTest.php @@ -0,0 +1,18 @@ +send(); + expect($http->getRequest()->getStatusCode())->toBe(200); + + $url = 'https://jsonplaceholder.typicode.com/post'; + $http = Notifier::http($url)->send(); + expect($http->getRequest()->getStatusCode())->toBe(500); +}); diff --git a/tests/NotifierMailTest.php b/tests/NotifierMailTest.php index 3d9a81d..f6fe5d4 100644 --- a/tests/NotifierMailTest.php +++ b/tests/NotifierMailTest.php @@ -2,7 +2,7 @@ use Illuminate\Support\Facades\Artisan; use Illuminate\Support\Facades\Config; -use Kiwilan\Notifier\Facades\Notifier; +use Kiwilan\LaravelNotifier\Facades\Notifier; beforeEach(function () { Config::set('notifier.mail.mailer', dotenv()['NOTIFIER_MAIL_MAILER']); @@ -26,8 +26,8 @@ ->port(config('notifier.mail.port')) ->credentials(config('notifier.mail.username'), config('notifier.mail.password')) ->encryption(config('notifier.mail.encryption')) - ->from(config('notifier.mail.from.address'), config('notifier.mail.from.name')) - ->to(config('notifier.mail.to.address'), config('notifier.mail.to.name')) + ->from('from@mail.com', config('notifier.mail.from.name')) + ->to('to@mail.com', config('notifier.mail.to.name')) ->send(); expect($notifier->isSuccess())->toBeTrue(); diff --git a/tests/NotifierSlackDefaultTest.php b/tests/NotifierSlackDefaultTest.php deleted file mode 100644 index 36c2f3b..0000000 --- a/tests/NotifierSlackDefaultTest.php +++ /dev/null @@ -1,121 +0,0 @@ - 'Content', -// ]; - -// $legacy = [ -// 'username' => 'Ghostbot', -// 'icon_emoji' => ':ghost:', -// 'channel' => '#random', -// 'text' => 'Content', -// ]; - -// $attachments = [ -// 'text' => [ -// 'type' => 'mrkdwn', -// 'text' => '*Content with attachment*', -// ], -// 'attachments' => [ -// [ -// 'title' => 'Laravel', -// 'title_link' => 'https://laravel.com', -// 'text' => 'Attachment Content', -// 'fallback' => 'Attachment Fallback', -// 'fields' => [ -// [ -// 'title' => 'Project', -// 'value' => 'Laravel', -// 'short' => true, -// ], -// ], -// 'mrkdwn_in' => ['text'], -// 'footer' => 'Laravel', -// 'footer_icon' => 'https://laravel.com/fake.png', -// 'author_name' => 'Author', -// 'author_link' => 'https://laravel.com/fake_author', -// 'author_icon' => 'https://laravel.com/fake_author.png', -// 'ts' => 1234567890, -// ], -// ], -// ]; - -// $blocks = [ -// [ -// 'blocks' => [ -// [ -// 'type' => 'section', -// 'text' => [ -// 'type' => 'mrkdwn', -// 'text' => 'Danny Torrence left the following review for your property:', -// ], -// 'fields' => [ -// [ -// 'type' => 'mrkdwn', -// 'text' => '*Markdown!*', -// ], -// [ -// 'type' => 'plain_text', -// 'text' => str_repeat('a', 1997).'...', -// ], -// [ -// 'type' => 'mrkdwn', -// 'text' => 'Location: 123 Main Street, New York, NY 10010', -// ], -// ], -// ], -// [ -// 'type' => 'divider', -// ], -// [ -// 'type' => 'actions', -// 'elements' => [ -// [ -// 'type' => 'button', -// 'text' => [ -// 'type' => 'plain_text', -// 'text' => 'Example Button', -// ], -// 'action_id' => 'button_example-button', -// ], -// [ -// 'type' => 'button', -// 'text' => [ -// 'type' => 'plain_text', -// 'text' => 'Scary Button', -// ], -// 'action_id' => 'button_scary-button', -// 'style' => 'danger', -// ], -// ], -// ], -// [ -// 'type' => 'header', -// 'text' => [ -// 'type' => 'plain_text', -// 'text' => 'Budget Performance', -// ], -// ], -// [ -// 'type' => 'image', -// 'image_url' => 'https://raw.githubusercontent.com/kiwilan/notifier-laravel/main/docs/banner.jpg', -// 'alt_text' => 'notifier banner', -// ], -// [ -// 'type' => 'section', -// 'block_id' => 'section567', -// 'text' => [ -// 'type' => 'mrkdwn', -// 'text' => 'Markdown *can* be so fun!', -// ], -// 'accessory' => [ -// 'type' => 'image', -// 'image_url' => 'https://raw.githubusercontent.com/kiwilan/notifier-laravel/main/docs/banner.jpg', -// 'alt_text' => 'notifier banner', -// ], -// ], -// ], -// ], -// ]; -// }); diff --git a/tests/NotifierSlackTest.php b/tests/NotifierSlackTest.php index 2dbdce8..1842227 100644 --- a/tests/NotifierSlackTest.php +++ b/tests/NotifierSlackTest.php @@ -2,21 +2,12 @@ use Illuminate\Support\Facades\Artisan; use Illuminate\Support\Facades\Config; -use Kiwilan\Notifier\Facades\Notifier; -use Kiwilan\Notifier\Notifier as NotifierNotifier; +use Kiwilan\LaravelNotifier\Facades\Notifier; beforeEach(function () { Config::set('notifier.slack.webhook', dotenv()['NOTIFIER_SLACK_WEBHOOK']); }); -it('can use instance', function () { - $facade = Notifier::slack(); - $instance = (new NotifierNotifier())->slack(); - - expect($facade)->toEqual($instance); - expect($facade)->toBeInstanceOf(NotifierNotifier::class); -}); - it('can use', function () { $notifier = Notifier::slack() ->message('Hello, Slack!') @@ -44,17 +35,15 @@ it('can use command', function () { $success = Artisan::call('notifier', [ - 'message' => 'Hello, Slack!', + 'message' => 'Hello, Slack with webhook!', '--type' => 'slack', '--webhook' => config('notifier.slack.webhook'), ]); - expect($success)->toBe(0); $success = Artisan::call('notifier', [ - 'message' => 'Hello, Slack!', + 'message' => 'Hello, Slack with config!', '--type' => 'slack', ]); - expect($success)->toBe(0); }); diff --git a/tests/Pest.php b/tests/Pest.php index c80bf82..f565504 100644 --- a/tests/Pest.php +++ b/tests/Pest.php @@ -1,23 +1,9 @@ in(__DIR__); -/** - * @return array{ - * NOTIFIER_DISCORD_WEBHOOK: string, - * NOTIFIER_SLACK_WEBHOOK: string, - * NOTIFIER_MAIL_MAILER: string, - * NOTIFIER_MAIL_HOST: string, - * NOTIFIER_MAIL_PORT: string, - * NOTIFIER_MAIL_USERNAME: string, - * NOTIFIER_MAIL_PASSWORD: string, - * NOTIFIER_MAIL_ENCRYPTION: string, - * NOTIFIER_MAIL_FROM_ADDRESS: string, - * NOTIFIER_MAIL_FROM_NAME: string, - * } - */ function dotenv(): array { $path = __DIR__.'/../'; @@ -58,11 +44,9 @@ function getLog(): string default => 'php --info | grep error', }; - // dump($cmd); $output = exec($cmd); $log_path_regex = '/error_log => (.*)/'; preg_match($log_path_regex, $output, $matches); - // dump($output); return $matches[1]; } diff --git a/tests/TestCase.php b/tests/TestCase.php index 7e7a2b6..d9a20f1 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -1,9 +1,9 @@