diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9dc17a9..43f2613 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,17 +10,9 @@ jobs: strategy: matrix: os: [ubuntu-latest] - php: [8.3, 8.4] - laravel: [11.*, 12.*] + php: [8.5] dependency-version: [prefer-stable] - exclude: - - php: 8.1 - laravel: 11.* - - php: 8.1 - laravel: 12.* - - php: 8.4 - laravel: 10.* - name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.dependency-version }} - ubuntu-latest + name: P${{ matrix.php }} - ${{ matrix.dependency-version }} - ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v3 @@ -29,7 +21,7 @@ jobs: uses: actions/cache@v4 with: path: ~/.composer/cache/files - key: dependencies-laravel-${{ matrix.laravel }}-php-${{ matrix.php }}-composer-${{ hashFiles('composer.json') }} + key: dependencies-php-${{ matrix.php }}-composer-${{ hashFiles('composer.json') }} - name: Setup PHP uses: shivammathur/setup-php@v2 diff --git a/.gitignore b/.gitignore index cdb09cc..9c9acfa 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,5 @@ /composer.lock /yarn.lock /package-lock.json +/.phpunit.cache +/.phpunit.result.cache diff --git a/composer.json b/composer.json index 7bfe8d6..72ac428 100644 --- a/composer.json +++ b/composer.json @@ -1,25 +1,21 @@ { "name": "transformstudios/front", "require": { - "php": "^8.3", + "php": "^8.5", "pixelfear/composer-dist-plugin": "^0.1.4", - "statamic/cms": "^5.0 || ^6.0", - "stillat/proteus": "^4.0" + "statamic/cms": "^6.0" }, "require-dev": { "mockery/mockery": "^1.3.1", "nunomaduro/collision": "^8.1", - "orchestra/testbench": "^9.0 || ^10", + "orchestra/testbench": "^10.0", "spatie/laravel-ray": "^1.24", "pestphp/pest": "^4.1" }, "autoload": { "psr-4": { "TransformStudios\\Front\\": "src" - }, - "files": [ - "src/helpers.php" - ] + } }, "autoload-dev": { "psr-4": { diff --git a/config/front.php b/config/front.php index 24d9c67..2ff0485 100644 --- a/config/front.php +++ b/config/front.php @@ -1,18 +1,10 @@ env('FRONT_API_TOKEN'), 'secret_key' => env('FRONT_SECRET_KEY'), 'chat' => [ 'id' => env('FRONT_CHAT_ID'), 'show_on_control_panel' => env('FRONT_SHOW_ON_CONTROL_PANEL', true), 'show_on_front_end' => env('FRONT_SHOW_ON_FRONT_END', false), ], - 'logging' => [ - 'conversation_id' => env('FRONT_CONVERSATION_ID'), - 'level' => env('FRONT_LOGGING_LEVEL', 'error'), - ], - 'notifications' => [ - 'channel' => env('FRONT_CHANNEL'), - ], ]; diff --git a/src/Logging/LogHandler.php b/src/Logging/LogHandler.php deleted file mode 100644 index 961b66a..0000000 --- a/src/Logging/LogHandler.php +++ /dev/null @@ -1,91 +0,0 @@ -toArray(); - } - - if (! $error = Arr::get($record, 'context.exception')) { - $errors = collect([ - 'Request URL: '.request()->fullUrl(), - 'Request data: '.json_encode(request()->input()), - 'Error: '.json_encode($record), - ]); - - front() - ->post( - "/conversations/$conversation/comments", - ['body' => Str::limit($errors->implode(PHP_EOL), 10000)] - )->throw(); - - return; - } - - front() - ->post( - "/conversations/$conversation/comments", - $this->convertErrorToFrontMessage($error, Arr::except(Arr::get($record, 'context', []), 'exception')) - )->throw(); - } - - private function convertErrorToFrontMessage(Throwable $error, array $context): array - { - return ['body' => $this->formatErrorLines($error, $context)->implode(PHP_EOL)]; - } - - private function formatContext(array $context): Collection - { - return collect($context) - ->map(fn ($value, string $key) => '* '.$key.': '.(is_string($value) ? $value : json_encode($value))) - ->values(); - } - - private function formatErrorLines(Throwable $error, array $context): Collection - { - return collect([ - 'Request URL: '.request()->fullUrl(), - 'Request data: '.json_encode(request()->input()), - '**'.$error->getMessage().'**', - '* '.$error->getFile().' ('.$error->getLine().')', - ]) - ->merge($this->formatContext($context)) - ->merge($this->formatStackTrace($error)); - } - - private function formatStackTrace(Throwable $error): Collection - { - return collect($error->getTrace()) - ->take(10) - ->map(function (array $traceItem) { - if (! $file = Arr::get($traceItem, 'file')) { - return '* '.json_encode($traceItem); - } - - return '* '.$file.' ('.Arr::get($traceItem, 'line').')'; - }); - } -} diff --git a/src/Logging/Logger.php b/src/Logging/Logger.php deleted file mode 100644 index a9ad6c9..0000000 --- a/src/Logging/Logger.php +++ /dev/null @@ -1,14 +0,0 @@ -data($notification); - - if ($conversationId = Cache::pull($key = $notification->key)) { - return $this->post('conversations', $conversationId, $data)->successful(); - } - - $response = $this->post('channels', config('front.notifications.channel'), $data); - Cache::forever($key, $this->getConversationId($response)); - - return $response->successful(); - } - - private function data(BaseNotification $notification): array - { - return [ - 'body' => $notification->renderedView, - 'options' => ['archive' => false], - 'subject' => $notification->subject, - 'to' => $notification->users->map(fn (User $user) => $user->email())->all(), - ]; - } - - private function post(string $segment, string $id, array $data): Response - { - return front() - ->post("/$segment/$id/messages", $data) - ->throw(); - } - - private function getConversationId(Response $response): string - { - return last(explode('/', Arr::get($response, '_links.related.conversation'))); - } -} diff --git a/src/ServiceProvider.php b/src/ServiceProvider.php index a13c003..33dd752 100644 --- a/src/ServiceProvider.php +++ b/src/ServiceProvider.php @@ -2,14 +2,11 @@ namespace TransformStudios\Front; -use Illuminate\Notifications\ChannelManager; -use Illuminate\Support\Facades\Notification; use Illuminate\Support\Facades\View; use Statamic\Facades\User as UserFacade; use Statamic\Providers\AddonServiceProvider; use Statamic\Statamic; use Statamic\Support\Arr; -use TransformStudios\Front\Notifications\Channel; class ServiceProvider extends AddonServiceProvider { @@ -37,13 +34,6 @@ public function bootAddon() // ); } - public function register() - { - Notification::resolved( - fn (ChannelManager $service) => $service->extend('front', fn ($app) => new Channel) - ); - } - private function bootScript() { if (! config('front.chat.show_on_control_panel')) { diff --git a/src/UpdateScripts/AddToLoggingConfig.php b/src/UpdateScripts/AddToLoggingConfig.php deleted file mode 100644 index 1ad8ee0..0000000 --- a/src/UpdateScripts/AddToLoggingConfig.php +++ /dev/null @@ -1,30 +0,0 @@ -set('channels.front', [ - 'driver' => 'custom', - 'via' => \TransformStudios\Front\Logging\Logger::class, - 'level' => ConfigWriter::f()->env('FRONT_LOGGING_LEVEL', 'error'), - ])->save(); - } -} diff --git a/src/helpers.php b/src/helpers.php deleted file mode 100644 index 4616b14..0000000 --- a/src/helpers.php +++ /dev/null @@ -1,10 +0,0 @@ -baseUrl('https://api2.frontapp.com'); -} diff --git a/tests/Http/Controllers/ConfigControllerTest.php b/tests/Http/Controllers/ConfigControllerTest.php new file mode 100644 index 0000000..918c00e --- /dev/null +++ b/tests/Http/Controllers/ConfigControllerTest.php @@ -0,0 +1,36 @@ +set('front.secret_key', 'secret'); + config()->set('front.chat.id', 'chat-id'); + config()->set('front.chat.show_on_front_end', true); +}); + +it('returns user details when configured', function () { + $user = tap(User::make()->email('erin@transformstudios.com')->set('name', 'Erin'))->save(); + + $this->actingAs($user) + ->getJson('/!/front/config') + ->assertOk() + ->assertExactJson([ + 'configured' => true, + 'chatId' => 'chat-id', + 'email' => 'erin@transformstudios.com', + 'name' => 'Erin', + 'hash' => hash_hmac('sha256', 'erin@transformstudios.com', 'secret'), + ]); +}); + +it('returns nulls for a guest', function () { + $this->getJson('/!/front/config') + ->assertOk() + ->assertJson(['configured' => true, 'email' => null, 'name' => null, 'hash' => null]); +}); + +it('is not configured when hidden on the front end', function () { + config()->set('front.chat.show_on_front_end', false); + + $this->getJson('/!/front/config')->assertOk()->assertJson(['configured' => false]); +}); diff --git a/tests/Logging/LogHandlerTest.php b/tests/Logging/LogHandlerTest.php deleted file mode 100644 index 4ff29d3..0000000 --- a/tests/Logging/LogHandlerTest.php +++ /dev/null @@ -1,51 +0,0 @@ -set('front.logging.conversation_id', 'cnv_id'); - Http::preventStrayRequests(); - Http::fake(['https://api2.frontapp.com/conversations/cnv_id/comments' => Http::response([], 200)]); -}); - -it('adds the log context to the comment', function () { - (new LogHandler(['level' => 'error']))->write(record(context: [ - 'exception' => new Exception('boom'), - 'redis' => ['loading' => 1, 'uptime_in_seconds' => 12], - 'userId' => 7, - ])); - - Http::assertSent(function ($request) { - expect($request['body']) - ->toContain('* redis: {"loading":1,"uptime_in_seconds":12}') - ->toContain('* userId: 7'); - - return true; - }); -}); - -it('leaves the exception out of the rendered context', function () { - (new LogHandler(['level' => 'error']))->write(record(context: ['exception' => new Exception('boom')])); - - Http::assertSent(function ($request) { - expect($request['body']) - ->toContain('**boom**') - ->not->toContain('* exception:'); - - return true; - }); -}); - -function record(array $context): LogRecord -{ - return new LogRecord( - datetime: new DateTimeImmutable, - channel: 'testing', - level: Level::Error, - message: 'boom', - context: $context, - ); -} diff --git a/tests/Notifications/ChannelTest.php b/tests/Notifications/ChannelTest.php deleted file mode 100755 index 10d5663..0000000 --- a/tests/Notifications/ChannelTest.php +++ /dev/null @@ -1,60 +0,0 @@ -set('front.notifications.channel', 'test-channel'); - Http::preventStrayRequests(); - Http::fake([ - 'https://api2.frontapp.com/channels/test-channel/messages' => Http::response([ - '_links' => [ - 'related' => [ - 'conversation' => 'https://transform-studios.api.frontapp.com/conversations/cnv_id', - ], - ], - ], 200), - 'https://api2.frontapp.com/conversations/cnv_id/messages' => Http::response([], 200), - ]); -}); - -test('can send front message', function () { - $users = collect([makeUser('erin@transformstudios.com'), makeUser('erin@silentz.co')]); - $notification = new TestNotification('some-key', 'Monitor Alert: Error Detected', '', $users); - - expect((new Channel)->send(new AnonymousNotifiable, $notification))->toBeTrue(); -}); - -it('stores the conversation id', function () { - $notification = new TestNotification('some-key', 'Monitor Alert: Error Detected', '', collect([makeUser('foo@bar.com')])); - - expect(Cache::get('some-key'))->toBeNull(); - expect((new Channel)->send(new AnonymousNotifiable, $notification))->toBeTrue(); - expect(Cache::get('some-key'))->toEqual('cnv_id'); -}); - -it('removes the conversation id when alert cleared', function () { - $notification = new TestNotification('some-key', 'Monitor Alert: Error Cleared', '', collect([makeUser('foo@bar.com')])); - - Cache::put('some-key', 'cnv_id'); - expect(Cache::get('some-key'))->not->toBeNull(); - expect((new Channel)->send(new AnonymousNotifiable, $notification))->toBeTrue(); - expect(Cache::get('some-key'))->toBeNull(); -}); - -it('adds to the conversation when conversation id exists', function () { - $user = makeUser('foo@bar.com'); - $notification = new TestNotification('some-key', 'Monitor Alert: Error Detected', '', collect([$user])); - $anotherNotification = new TestNotification('some-key', 'Monitor Alert: Error Cleared', '', collect([$user])); - $channel = new Channel; - - expect($channel->send(new AnonymousNotifiable, $notification))->toBeTrue(); - expect($channel->send(new AnonymousNotifiable, $anotherNotification))->toBeTrue(); -}); - -class TestNotification extends BaseNotification {} diff --git a/tests/Pest.php b/tests/Pest.php index 7a0bcda..5b9d742 100644 --- a/tests/Pest.php +++ b/tests/Pest.php @@ -1,7 +1,5 @@ extend(TransformStudios\Front\Tests\TestCase::class)->in(__DIR__); - -/* -|-------------------------------------------------------------------------- -| Expectations -|-------------------------------------------------------------------------- -| -| When you're writing tests, you often need to check that values meet certain conditions. The -| "expect()" function gives you access to a set of "expectations" methods that you can use -| to assert different things. Of course, you may extend the Expectation API at any time. -| -*/ - -expect()->extend('toBeOne', function () { - return $this->toBe(1); -}); - -/* -|-------------------------------------------------------------------------- -| Functions -|-------------------------------------------------------------------------- -| -| While Pest is very powerful out-of-the-box, you may have some testing code specific to your -| project that you don't want to repeat in every file. Here you can also expose helpers as -| global functions to help you to reduce the number of lines of code in your test files. -| -*/ - -function makeUser(string $email): User -{ - return tap(User::make()->email($email))->save(); -}