From 08259ad67d63dfe1d019de615c80ac0b98bbefea Mon Sep 17 00:00:00 2001 From: Marlin Forbes Date: Fri, 25 Oct 2024 13:01:57 +0200 Subject: [PATCH] wip --- .gitignore | 1 + composer.json | 5 +- phpunit.xml | 25 ++--- src/ServiceBusChannel.php | 183 ++++++++++++++++++-------------- src/ServiceBusEvent.php | 95 +++++++++-------- tests/ServiceBusChannelTest.php | 18 ++-- tests/ServiceBusEventTest.php | 2 +- tests/TestCase.php | 15 +++ tests/TestNotification.php | 12 +-- 9 files changed, 197 insertions(+), 159 deletions(-) diff --git a/.gitignore b/.gitignore index 4a3c8a0..e4edbce 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ /.envrc /.idea/ +/.phpunit.cache/ /.phpunit.result.cache /composer.lock /vendor diff --git a/composer.json b/composer.json index 53af61c..804bb53 100644 --- a/composer.json +++ b/composer.json @@ -17,7 +17,10 @@ "ext-pcre": "*", "ext-simplexml": "*", "illuminate/notifications": "^9 || ^10 || ^11", - "illuminate/support": "^9 || ^10 || ^11" + "illuminate/support": "^9 || ^10 || ^11", + "guzzlehttp/guzzle": "^7", + "guzzlehttp/promises": "^2", + "guzzlehttp/psr7": "^1 || ^2" }, "require-dev": { "ext-dom": "*", diff --git a/phpunit.xml b/phpunit.xml index 1d9eada..da7de78 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,23 +1,5 @@ - - - - src/ - - + tests @@ -30,4 +12,9 @@ + + + src/ + + diff --git a/src/ServiceBusChannel.php b/src/ServiceBusChannel.php index 1e50163..0a862b1 100644 --- a/src/ServiceBusChannel.php +++ b/src/ServiceBusChannel.php @@ -2,13 +2,15 @@ namespace Ringierimu\ServiceBusNotificationsChannel; -use Illuminate\Http\Client\RequestException; +use GuzzleHttp\Client; +use GuzzleHttp\Exception\GuzzleException; +use GuzzleHttp\Exception\RequestException; use Illuminate\Notifications\Notification; use Illuminate\Support\Arr; use Illuminate\Support\Facades\Cache; -use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Log; use Ringierimu\ServiceBusNotificationsChannel\Exceptions\CouldNotSendNotification; +use Throwable; /** * Class ServiceBusChannel. @@ -29,8 +31,13 @@ class ServiceBusChannel */ public function __construct(array $config = []) { - $this->config = $config ?: config("services.service_bus"); - $this->client = Http::baseUrl(Arr::get($this->config, "endpoint")); + $this->config = $config ?: config('services.service_bus'); + + $this->client = new Client( + [ + 'base_uri' => Arr::get($this->config, 'endpoint'), + ] + ); } /** @@ -40,6 +47,8 @@ public function __construct(array $config = []) * @param Notification $notification * * @throws CouldNotSendNotification + * @throws GuzzleException + * @throws Throwable */ public function send($notifiable, Notification $notification) { @@ -47,15 +56,20 @@ public function send($notifiable, Notification $notification) $event = $notification->toServiceBus($notifiable); $eventType = $event->getEventType(); $params = $event->getParams(); - $dontReport = Arr::get($this->config, "dont_report", []); + $dontReport = Arr::get($this->config, 'dont_report', []); - if (Arr::get($this->config, "enabled") == false) { + if (Arr::get($this->config, 'enabled') == false) { if (!in_array($eventType, $dontReport)) { - Log::debug("$eventType service bus notification [disabled]", [ - "event" => $eventType, - "params" => $params, - "tags" => ["service-bus"], - ]); + Log::debug( + "$eventType service bus notification [disabled]", + [ + 'event' => $eventType, + 'params' => $params, + 'tags' => [ + 'service-bus', + ], + ] + ); } return; @@ -64,32 +78,46 @@ public function send($notifiable, Notification $notification) $token = $this->getToken(); $headers = [ - "Accept" => "application/json", - "Content-type" => "application/json", - "x-api-key" => $token, + 'Accept' => 'application/json', + 'Content-type' => 'application/json', + 'x-api-key' => $token, ]; try { - $response = $this->client - ->withHeaders($headers) - ->post($this->getUrl("events"), [$params]) - ->throw(); - - Log::info("$eventType service bus notification", [ - "event" => $eventType, - "params" => $params, - "tags" => ["service-bus"], - "status" => $response->status(), - ]); - } catch (RequestException $exception) { - $status = $exception->response->status(); + $response = $this->client->request( + 'POST', + $this->getUrl('events'), + [ + 'headers' => $headers, + 'json' => [$params], + ] + ); - if (in_array($status, [401, 403])) { - Log::info("{$status} received. Logging in and retrying.", [ - "event" => $eventType, - "params" => $params, - "tags" => ["service-bus"], - ]); + Log::info( + "$eventType service bus notification", + [ + 'event' => $eventType, + 'params' => $params, + 'tags' => [ + 'service-bus', + ], + 'status' => $response->getStatusCode(), + ] + ); + } catch (RequestException $exception) { + $code = $exception->getCode(); + + if (in_array($code, [401, 403])) { + Log::info( + "$code received. Logging in and retrying.", + [ + 'event' => $eventType, + 'params' => $params, + 'tags' => [ + 'service-bus', + ], + ] + ); // clear the invalid token // Cache::forget($this->generateTokenKey()); @@ -111,53 +139,51 @@ public function send($notifiable, Notification $notification) /** * @throws CouldNotSendNotification + * @throws GuzzleException * * @return string */ private function getToken(): string { - return Cache::rememberForever($this->generateTokenKey(), function () { - try { - $version = intval($this->config["version"]); - - if ($version < 2) { - $response = $this->client - ->post( - $this->getUrl("login"), - Arr::only($this->config, [ - "username", - "password", - "venture_config_id", - ]) - ) - ->throw(); - } else { - $response = $this->client - ->post( - $this->getUrl("login"), - Arr::only($this->config, [ - "username", - "password", - "node_id", - ]) - ) - ->throw(); + return Cache::rememberForever( + $this->generateTokenKey(), + function () { + try { + $version = intval($this->config['version']); + + if ($version < 2) { + $response = $this->client->request( + 'POST', + $this->getUrl('login'), + [ + 'json' => Arr::only($this->config, ['username', 'password', 'venture_config_id']), + ] + ); + } else { + $response = $this->client->request( + 'POST', + $this->getUrl('login'), + [ + 'json' => Arr::only($this->config, ['username', 'password', 'node_id']), + ] + ); + } + + $body = json_decode((string) $response->getBody(), true); + + $code = (int) Arr::get($body, 'code', $response->getStatusCode()); + + switch ($code) { + case 200: + return $body['token']; + default: + throw CouldNotSendNotification::loginFailed($response); + } + } catch (RequestException $exception) { + throw CouldNotSendNotification::requestFailed($exception); } - - $body = $response->json(); - - $code = (int) Arr::get($body, "code", $response->status()); - - switch ($code) { - case 200: - return $body["token"]; - default: - throw CouldNotSendNotification::loginFailed($response); - } - } catch (RequestException $exception) { - throw CouldNotSendNotification::requestFailed($exception); } - }); + ); } /** @@ -172,15 +198,18 @@ private function getUrl(string $endpoint): string public function generateTokenKey() { - $version = intval($this->config["version"]); + $version = intval($this->config['version']); if ($version < 2) { return md5( - "service-bus-token" . - Arr::get($this->config, "venture_config_id") + 'service-bus-token' . + Arr::get($this->config, 'venture_config_id') ); } - return md5("service-bus-token" . Arr::get($this->config, "node_id")); + return md5( + 'service-bus-token' . + Arr::get($this->config, 'node_id') + ); } } diff --git a/src/ServiceBusEvent.php b/src/ServiceBusEvent.php index aa7d902..80b8e3a 100644 --- a/src/ServiceBusEvent.php +++ b/src/ServiceBusEvent.php @@ -2,11 +2,11 @@ namespace Ringierimu\ServiceBusNotificationsChannel; +use Carbon\Carbon; use Exception; use Illuminate\Http\Request; use Illuminate\Http\Resources\Json\JsonResource; -use Illuminate\Support\Carbon; -use Illuminate\Support\Str; +use Ramsey\Uuid\Uuid; use Ringierimu\ServiceBusNotificationsChannel\Exceptions\InvalidConfigException; use Throwable; @@ -25,13 +25,13 @@ class ServiceBusEvent { public static $actionTypes = [ - "user", - "admin", - "api", - "system", - "app", - "migration", - "other", + 'user', + 'admin', + 'api', + 'system', + 'app', + 'migration', + 'other', ]; protected $eventType; @@ -53,9 +53,9 @@ class ServiceBusEvent public function __construct(string $eventType, array $config = []) { $this->eventType = $eventType; - $this->config = $config ?: config("services.service_bus"); + $this->config = $config ?: config('services.service_bus'); $this->createdAt = Carbon::now(); - $this->reference = (string) Str::uuid(); + $this->reference = $this->generateUUID(); } /** @@ -127,10 +127,7 @@ public function withAction(string $type, string $reference): self $this->actionType = $type; $this->actionReference = $reference; } else { - throw new InvalidConfigException( - "Action type must be on of the following: " . - print_r(self::$actionTypes, true) - ); + throw new InvalidConfigException('Action type must be on of the following: ' . print_r(self::$actionTypes, true)); } return $this; @@ -178,21 +175,13 @@ public function withResources(string $resourceName, array $resource) * * @return this */ - public function withResource( - string $resourceName, - $resource, - Request $request = null - ): self { + public function withResource(string $resourceName, $resource, Request $request = null): self + { if (!is_array($resource)) { if ($resource instanceof JsonResource) { $resource = $resource->toArray($request); } else { - throw new Exception( - "Unhandled resource type: " . - $resourceName . - " " . - json_encode($resource) - ); + throw new Exception('Unhandled resource type: ' . $resourceName . ' ' . json_encode($resource)); } } @@ -238,7 +227,7 @@ public function createdAt(Carbon $createdAtDate): self */ protected function getCulture(): string { - return $this->culture ?? $this->config["culture"]; + return $this->culture ?? $this->config['culture']; } /** @@ -251,6 +240,18 @@ protected function getPayload(): array return $this->payload; } + /** + * Generates a v4 UUID. + * + * @throws Throwable + * + * @return string + */ + private function generateUUID(): string + { + return Uuid::uuid4()->toString(); + } + /** * Return the event as an array that can be sent to the service. * @@ -260,33 +261,33 @@ protected function getPayload(): array */ public function getParams(): array { - $version = intval($this->config["version"]); + $version = intval($this->config['version']); if ($version < 2) { return [ - "events" => [$this->eventType], - "venture_reference" => $this->reference, - "reference" => $this->reference, - "venture_config_id" => $this->config["venture_config_id"], - "from" => $this->config["venture_config_id"], - "created_at" => $this->createdAt->toISOString(), - "culture" => $this->getCulture(), - "action_type" => $this->actionType, - "action_reference" => $this->actionReference, - "version" => $this->config["version"], - "route" => $this->route, - "payload" => $this->getPayload(), + 'events' => [$this->eventType], + 'venture_reference' => $this->reference, + 'reference' => $this->reference, + 'venture_config_id' => $this->config['venture_config_id'], + 'from' => $this->config['venture_config_id'], + 'created_at' => $this->createdAt->toISOString(), + 'culture' => $this->getCulture(), + 'action_type' => $this->actionType, + 'action_reference' => $this->actionReference, + 'version' => $this->config['version'], + 'route' => $this->route, + 'payload' => $this->getPayload(), ]; } return [ - "events" => [$this->eventType], - "reference" => $this->reference, - "from" => $this->config["from"] ?? $this->config["node_id"], - "created_at" => $this->createdAt->toISOString(), - "version" => $this->config["version"], - "route" => $this->route, - "payload" => $this->getPayload(), + 'events' => [$this->eventType], + 'reference' => $this->reference, + 'from' => $this->config['from'] ?? $this->config['node_id'], + 'created_at' => $this->createdAt->toISOString(), + 'version' => $this->config['version'], + 'route' => $this->route, + 'payload' => $this->getPayload(), ]; } diff --git a/tests/ServiceBusChannelTest.php b/tests/ServiceBusChannelTest.php index 5c68db2..247e930 100644 --- a/tests/ServiceBusChannelTest.php +++ b/tests/ServiceBusChannelTest.php @@ -2,11 +2,16 @@ namespace Ringierimu\ServiceBusNotificationsChannel\Tests; +use GuzzleHttp\Client; +use GuzzleHttp\Exception\GuzzleException; use Illuminate\Notifications\AnonymousNotifiable; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Log; +use Mockery; +use Mockery\MockInterface; use Ringierimu\ServiceBusNotificationsChannel\Exceptions\CouldNotSendNotification; use Ringierimu\ServiceBusNotificationsChannel\ServiceBusChannel; +use stdClass; use Throwable; /** @@ -14,15 +19,8 @@ */ class ServiceBusChannelTest extends TestCase { - public function testShouldCreateServiceBusChannelInstance() - { - $this->mockAll(); - $serviceChannel = new ServiceBusChannel(config_v2()); - - $this->assertNotNull($serviceChannel); - } - /** + * @throws GuzzleException * @throws CouldNotSendNotification * @throws Throwable */ @@ -57,5 +55,9 @@ private function mockAll() Log::shouldReceive("info")->once()->andReturnNull(); Log::shouldReceive("error")->once()->andReturnNull(); + + Mockery::mock(Client::class, function (MockInterface $mock) { + $mock->shouldReceive("execute")->andReturn(new stdClass())->once(); + }); } } diff --git a/tests/ServiceBusEventTest.php b/tests/ServiceBusEventTest.php index 6dffedf..a80b503 100644 --- a/tests/ServiceBusEventTest.php +++ b/tests/ServiceBusEventTest.php @@ -2,7 +2,7 @@ namespace Ringierimu\ServiceBusNotificationsChannel\Tests; -use Illuminate\Support\Carbon; +use Carbon\Carbon; use Ringierimu\ServiceBusNotificationsChannel\Exceptions\InvalidConfigException; use Ringierimu\ServiceBusNotificationsChannel\ServiceBusEvent; use Throwable; diff --git a/tests/TestCase.php b/tests/TestCase.php index 8feda44..fe014cc 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -2,6 +2,7 @@ namespace Ringierimu\ServiceBusNotificationsChannel\Tests; +use Illuminate\Config\Repository; use Ringierimu\ServiceBusNotificationsChannel\ServiceBusServiceProvider; class TestCase extends \Orchestra\Testbench\TestCase @@ -10,4 +11,18 @@ protected function getPackageProviders($app) { return [ServiceBusServiceProvider::class]; } + + protected function defineEnvironment($app) + { + tap($app["config"], function (Repository $config) { + $config->set("services.service_bus", [ + "enabled" => true, + "node_id" => "123456789", + "username" => "username", + "password" => "password", + "version" => "2.0.0", + "endpoint" => "https://bus.staging.ritdu.tech/v1/", + ]); + }); + } } diff --git a/tests/TestNotification.php b/tests/TestNotification.php index 320464b..6d1d866 100644 --- a/tests/TestNotification.php +++ b/tests/TestNotification.php @@ -2,8 +2,8 @@ namespace Ringierimu\ServiceBusNotificationsChannel\Tests; +use Carbon\Carbon; use Illuminate\Notifications\Notification; -use Illuminate\Support\Carbon; use Ringierimu\ServiceBusNotificationsChannel\Exceptions\InvalidConfigException; use Ringierimu\ServiceBusNotificationsChannel\ServiceBusEvent; @@ -19,12 +19,12 @@ class TestNotification extends Notification */ public function toServiceBus() { - return ServiceBusEvent::create("test") - ->withAction("other", uniqid()) - ->withCulture("en") + return ServiceBusEvent::create('test') + ->withAction('other', uniqid()) + ->withCulture('en') ->withReference(uniqid()) - ->withRoute("api") + ->withRoute('api') ->createdAt(Carbon::now()) - ->withResource("resources", ["data"]); + ->withResources('resources', ['data']); } }