From 6fc673c9ddceff0d8dfcd7564044bf5c90ce72f3 Mon Sep 17 00:00:00 2001 From: Palaniappan P Date: Thu, 21 Nov 2024 17:50:15 +0530 Subject: [PATCH 1/9] Updated :fire: PHP/Laravel extensions/NodeJs/npm :green_heart: dependencies and asset build to latest stable versions! Happy coding :apple:,\n Updated dependencies: Changelogs summary: - nunomaduro/termwind updated from v2.2.0 to v2.3.0 minor See changes: https://github.com/nunomaduro/termwind/compare/v2.2.0...v2.3.0 Release notes: https://github.com/nunomaduro/termwind/releases/tag/v2.3.0 --- tests/TestCase.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/TestCase.php b/tests/TestCase.php index 3a68c90..7e4a703 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -9,7 +9,7 @@ class TestCase extends Orchestra { - public function setUp(): void + protected function setUp(): void { parent::setUp(); } From 9657859d0fa79cd07b247568467528625f6ab9b1 Mon Sep 17 00:00:00 2001 From: Palaniappan P Date: Fri, 3 Jan 2025 14:49:10 +0530 Subject: [PATCH 2/9] Updated :fire: PHP/Laravel extensions/NodeJs/npm :green_heart: dependencies and asset build to latest stable versions! Happy coding :apple:,\n Updated dependencies: Changelogs summary: - aws/aws-sdk-php updated from 3.336.6 to 3.336.7 patch See changes: https://github.com/aws/aws-sdk-php/compare/3.336.6...3.336.7 Release notes: https://github.com/aws/aws-sdk-php/releases/tag/3.336.7 - symfony/http-foundation updated from v7.2.0 to v7.2.2 patch See changes: https://github.com/symfony/http-foundation/compare/v7.2.0...v7.2.2 Release notes: https://github.com/symfony/http-foundation/releases/tag/v7.2.2 - symfony/http-kernel updated from v7.2.1 to v7.2.2 patch See changes: https://github.com/symfony/http-kernel/compare/v7.2.1...v7.2.2 Release notes: https://github.com/symfony/http-kernel/releases/tag/v7.2.2 - symfony/finder updated from v7.2.0 to v7.2.2 patch See changes: https://github.com/symfony/finder/compare/v7.2.0...v7.2.2 Release notes: https://github.com/symfony/finder/releases/tag/v7.2.2 - symfony/translation updated from v7.2.0 to v7.2.2 patch See changes: https://github.com/symfony/translation/compare/v7.2.0...v7.2.2 Release notes: https://github.com/symfony/translation/releases/tag/v7.2.2 - laravel/framework updated from v11.36.1 to v11.37.0 minor See changes: https://github.com/laravel/framework/compare/v11.36.1...v11.37.0 Release notes: https://github.com/laravel/framework/releases/tag/v11.37.0 --- src/SqsQueueReaderServiceProvider.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/SqsQueueReaderServiceProvider.php b/src/SqsQueueReaderServiceProvider.php index 0e87e3a..6942a19 100644 --- a/src/SqsQueueReaderServiceProvider.php +++ b/src/SqsQueueReaderServiceProvider.php @@ -64,7 +64,7 @@ private function removeMessages(array $data, $queue, string $connection): void $config = Config::get('queue.connections.' . $connection); $sqsClientConfig = [ - //'profile' => 'default', + // 'profile' => 'default', 'region' => Config::get('queue.connections.' . $connection . '.region'), 'version' => '2012-11-05', 'http' => [ @@ -80,7 +80,7 @@ private function removeMessages(array $data, $queue, string $connection): void $client = new SqsClient($sqsClientConfig); foreach ($batchIds as $batch) { - //Deletes up to ten messages from the specified queue. + // Deletes up to ten messages from the specified queue. try { $result = $client->deleteMessageBatch([ 'Entries' => $batch, @@ -96,7 +96,7 @@ private function removeMessages(array $data, $queue, string $connection): void throw new \RuntimeException('Cannot delete some messages, consult log for more info!'); } - //Log::info('Message remove report:', [$result]); + // Log::info('Message remove report:', [$result]); } catch (AwsException $e) { Log::error('AWS SQS client error:', [$e->getMessage()]); } From d17f60106d4a9bd83f16424dd195151da3fa4587 Mon Sep 17 00:00:00 2001 From: Palaniappan P Date: Mon, 22 Sep 2025 11:23:19 +0530 Subject: [PATCH 3/9] improved dev tools --- CLAUDE.md | 84 +++++++++++++++++++++++++++++++++++++++++++++++++++ composer.json | 26 +++++++++------- 2 files changed, 99 insertions(+), 11 deletions(-) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..7f3781e --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,84 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Development Commands + +### Testing +- `composer test` - Run tests using TestBench without coverage +- `composer test-coverage` - Run PHPUnit with HTML coverage report +- `./vendor/bin/testbench package:test --no-coverage` - Direct TestBench command + +### Code Quality +- `composer analyse` - Run PHPStan static analysis (level 4) +- `composer format` - Format code using Laravel Pint with custom ruleset +- `vendor/bin/pint` - Direct Pint formatting command +- `vendor/bin/phpstan analyse` - Direct PHPStan command + +### Package Development +- `php artisan vendor:publish --provider="palPalani\SqsQueueReader\SqsQueueReaderServiceProvider" --tag="config"` - Publish configuration file + +## Architecture Overview + +This is a Laravel package that extends SQS queue functionality to handle raw JSON payloads from external sources (webhooks, third-party APIs) without requiring Laravel's specific job format. + +### Core Components + +**Queue Driver (`sqs-json`)** +- Custom SQS connector (`src/Sqs/Connector.php`) extends Laravel's SqsConnector +- Custom queue implementation (`src/Sqs/Queue.php`) extends Laravel's SqsQueue +- Handles both single and batch message processing +- Automatically formats raw JSON messages into Laravel job format + +**Service Provider (`src/SqsQueueReaderServiceProvider.php`)** +- Registers the `sqs-json` queue driver +- Handles automatic message deletion after processing +- Manages batch message cleanup via SQS API + +**Dispatcher Job (`src/Jobs/DispatcherJob.php`)** +- Utility for dispatching plain JSON or Laravel-formatted messages +- Supports both structured (`setPlain(false)`) and plain JSON (`setPlain(true)`) modes + +### Configuration System + +**Queue Handlers (`config/sqs-queue-reader.php`)** +- Maps queue names to handler classes and message counts +- Supports multiple queues with different handlers +- Falls back to default handler for unmapped queues +- Configurable message batch sizes (1-10 messages per poll) + +**Queue Connection Setup** +- Add `sqs-json` driver to `config/queue.php` +- Use standard AWS SQS configuration (key, secret, region, prefix, queue) +- Set `QUEUE_DRIVER=sqs-json` in environment + +### Message Processing Flow + +1. **Incoming Messages**: Raw JSON from external sources (Stripe, Mailgun, etc.) +2. **Queue Processing**: `Queue::pop()` retrieves and formats messages +3. **Handler Mapping**: Uses queue name to determine handler class and batch size +4. **Job Creation**: Wraps raw payload in Laravel job format with UUID +5. **Batch Handling**: Multiple messages processed together when count > 1 +6. **Cleanup**: Automatic SQS message deletion after successful processing + +### Testing Framework + +- Uses Orchestra Testbench for Laravel package testing +- Configured for strict testing (warnings/notices as failures) +- Coverage reports generated in `build/coverage/` +- Test files in `tests/` directory + +### Code Standards + +- PHP 8.3+ with strict types declared +- Laravel Pint formatting with custom rules (PER-CS, PHP 8.3 migration) +- PHPStan level 4 analysis with Octane compatibility checks +- PSR-4 autoloading: `palPalani\SqsQueueReader\` + +### Key Features + +- **Multi-message Processing**: Configurable batch sizes for high-throughput scenarios +- **Handler Flexibility**: Different job classes per queue +- **AWS Integration**: Direct SQS API usage for batch operations +- **Laravel Compatibility**: Works with Laravel 11-12, PHP 8.3+ +- **Plain JSON Support**: Handles raw webhook payloads without Laravel job wrapper \ No newline at end of file diff --git a/composer.json b/composer.json index 966403b..e562100 100644 --- a/composer.json +++ b/composer.json @@ -22,21 +22,24 @@ } ], "require": { - "php": "^8.2", - "illuminate/contracts": "^9.0|^10.0|^11.0|^12.0", - "illuminate/support": "^9.0|^10.0|^11.0|^12.0", - "illuminate/queue": "^9.0|^10.0|^11.0|^12.0", - "illuminate/bus": "^9.0|^10.0|^11.0|^12.0", + "php": "^8.3", + "illuminate/contracts": "^11.0|^12.0", + "illuminate/support": "^11.0|^12.0", + "illuminate/queue": "^11.0|^12.0", + "illuminate/bus": "^11.0|^12.0", "aws/aws-sdk-php": "^3.250" }, "require-dev": { - "larastan/larastan": "^2.0", + "larastan/larastan": "^2.0|^3.0", "laravel/pint": "^1.2", - "nunomaduro/collision": "^6.3|^7.0|^8.1", - "orchestra/testbench": "^7.15|^8.0|^9.0|^10.0", - "phpstan/extension-installer": "^1.2", - "phpstan/phpstan-deprecation-rules": "^1.0", - "phpunit/phpunit": "^9.5|^10.0|^11.0" + "nunomaduro/collision": "^7.0|^8.1", + "orchestra/testbench": "^8.0|^9.0|^10.0", + "pestphp/pest": "^4.0", + "pestphp/pest-plugin-arch": "^4.0", + "pestphp/pest-plugin-laravel": "^4.0", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan-deprecation-rules": "^2.0", + "phpstan/phpstan-phpunit": "^2.0" }, "autoload": { "psr-4": { @@ -57,6 +60,7 @@ "config": { "sort-packages": true, "allow-plugins": { + "pestphp/pest-plugin": true, "phpstan/extension-installer": true } }, From 6762df3f2c429a497595234716a80252ce70674e Mon Sep 17 00:00:00 2001 From: Palaniappan P Date: Mon, 22 Sep 2025 11:35:19 +0530 Subject: [PATCH 4/9] Enhance SqsServiceProvider --- src/SqsQueueReaderServiceProvider.php | 258 +++++++++++++++++++------- 1 file changed, 195 insertions(+), 63 deletions(-) diff --git a/src/SqsQueueReaderServiceProvider.php b/src/SqsQueueReaderServiceProvider.php index 6942a19..7046d6e 100644 --- a/src/SqsQueueReaderServiceProvider.php +++ b/src/SqsQueueReaderServiceProvider.php @@ -6,100 +6,232 @@ use Aws\Exception\AwsException; use Aws\Sqs\SqsClient; +use Illuminate\Contracts\Config\Repository as ConfigRepository; +use Illuminate\Contracts\Events\Dispatcher; +use Illuminate\Contracts\Queue\Job; use Illuminate\Queue\Events\JobProcessed; +use Illuminate\Queue\QueueManager; use Illuminate\Support\Arr; -use Illuminate\Support\Facades\Config; use Illuminate\Support\Facades\Log; -use Illuminate\Support\Facades\Queue; use Illuminate\Support\ServiceProvider; +use InvalidArgumentException; use palPalani\SqsQueueReader\Sqs\Connector; +use RuntimeException; class SqsQueueReaderServiceProvider extends ServiceProvider { + private const CONFIG_KEY = 'sqs-queue-reader'; + + private const CONFIG_PATH = __DIR__ . '/../config/sqs-queue-reader.php'; + + private const SQS_VERSION = '2012-11-05'; + + private const BATCH_SIZE = 10; + + private const DEFAULT_TIMEOUT = 30; + public function boot(): void { if ($this->app->runningInConsole()) { - $this->publishes([ - __DIR__ . '/../config/sqs-queue-reader.php' => config_path('sqs-queue-reader.php'), - ], 'config'); - - Queue::after(function (JobProcessed $event) { - $connections = Config::get('queue.connections'); - if (\in_array($event->connectionName, array_keys($connections), true)) { - $queue = $event->job->getQueue(); - - $queueId = explode('/', $queue); - $queueId = array_pop($queueId); - - $count = (\array_key_exists($queueId, Config::get('sqs-queue-reader.handlers'))) - ? Config::get('sqs-queue-reader.handlers')[$queueId]['count'] - : Config::get('sqs-queue-reader.default-handler')['count']; - - if ($count === 1) { - $event->job->delete(); - } else { - $this->removeMessages($event->job->payload(), $queue, $event->connectionName); - } - } - }); + $this->publishConfiguration(); } + + $this->registerJobEventListener(); } public function register(): void { - $this->mergeConfigFrom(__DIR__ . '/../config/sqs-queue-reader.php', 'sqs-queue-reader'); + $this->mergeConfigFrom(self::CONFIG_PATH, self::CONFIG_KEY); + + $this->app->booted(fn() => $this->registerQueueDriver()); + } + + private function publishConfiguration(): void + { + $this->publishes([ + self::CONFIG_PATH => config_path(self::CONFIG_KEY . '.php'), + ], 'config'); + } - $this->app->booted(function () { - $this->app['queue']->extend('sqs-json', static function () { - return new Connector; - }); - }); + private function registerQueueDriver(): void + { + /** @var QueueManager $queueManager */ + $queueManager = $this->app['queue']; + + $queueManager->extend('sqs-json', static fn() => new Connector); } - private function removeMessages(array $data, $queue, string $connection): void + private function registerJobEventListener(): void { - $batchIds = array_column($data['data'], 'batchIds'); - $batchIds = array_chunk($batchIds, 10); + /** @var Dispatcher $eventDispatcher */ + $eventDispatcher = $this->app['events']; + + $eventDispatcher->listen(JobProcessed::class, $this->handleJobProcessed(...)); + } + + private function handleJobProcessed(JobProcessed $event): void + { + if (! $this->shouldProcessJob($event)) { + return; + } + + $messageCount = $this->getMessageCount($event->job); + + if ($messageCount === 1) { + $event->job->delete(); + + return; + } + + $this->removeBatchMessages($event->job, $event->connectionName); + } + + private function shouldProcessJob(JobProcessed $event): bool + { + /** @var ConfigRepository $config */ + $config = $this->app['config']; + + $connections = $config->get('queue.connections', []); + + return array_key_exists($event->connectionName, $connections); + } + + private function getMessageCount(Job $job): int + { + $queueId = $this->extractQueueId($job->getQueue()); + + /** @var ConfigRepository $config */ + $config = $this->app['config']; + + $handlers = $config->get(self::CONFIG_KEY . '.handlers', []); + + if (array_key_exists($queueId, $handlers)) { + return (int) $handlers[$queueId]['count']; + } - $config = Config::get('queue.connections.' . $connection); + $defaultHandler = $config->get(self::CONFIG_KEY . '.default-handler', []); - $sqsClientConfig = [ - // 'profile' => 'default', - 'region' => Config::get('queue.connections.' . $connection . '.region'), - 'version' => '2012-11-05', + return (int) ($defaultHandler['count'] ?? 1); + } + + private function extractQueueId(string $queue): string + { + $segments = explode('/', $queue); + + return array_pop($segments) ?: throw new InvalidArgumentException("Invalid queue format: {$queue}"); + } + + private function removeBatchMessages(Job $job, string $connectionName): void + { + try { + $payload = $job->payload(); + $batchIds = $this->extractBatchIds($payload); + + if (empty($batchIds)) { + return; + } + + $sqsClient = $this->createSqsClient($connectionName); + $this->deleteBatchMessages($sqsClient, $batchIds, $job->getQueue()); + + } catch (AwsException $exception) { + Log::error('AWS SQS client error during message removal', [ + 'error' => $exception->getMessage(), + 'connection' => $connectionName, + 'queue' => $job->getQueue(), + ]); + } catch (RuntimeException $exception) { + Log::error('Failed to remove SQS messages', [ + 'error' => $exception->getMessage(), + 'connection' => $connectionName, + 'queue' => $job->getQueue(), + ]); + } + } + + /** + * @param array $payload + * @return array> + */ + private function extractBatchIds(array $payload): array + { + if (! isset($payload['data']['data'])) { + return []; + } + + $batchIds = array_column($payload['data']['data'], 'batchIds'); + + return array_chunk($batchIds, self::BATCH_SIZE); + } + + private function createSqsClient(string $connectionName): SqsClient + { + /** @var ConfigRepository $config */ + $config = $this->app['config']; + + $connectionConfig = $config->get("queue.connections.{$connectionName}"); + + if (! is_array($connectionConfig)) { + throw new InvalidArgumentException("Invalid connection configuration for: {$connectionName}"); + } + + $sqsConfig = [ + 'region' => $connectionConfig['region'] ?? throw new InvalidArgumentException("Missing region for connection: {$connectionName}"), + 'version' => self::SQS_VERSION, 'http' => [ - 'timeout' => 30, - 'connect_timeout' => 30, + 'timeout' => self::DEFAULT_TIMEOUT, + 'connect_timeout' => self::DEFAULT_TIMEOUT, ], ]; - if (isset($config['key'], $config['secret'])) { - $sqsClientConfig['credentials'] = Arr::only($config, ['key', 'secret']); + if (isset($connectionConfig['key'], $connectionConfig['secret'])) { + $sqsConfig['credentials'] = Arr::only($connectionConfig, ['key', 'secret']); } - $client = new SqsClient($sqsClientConfig); + return new SqsClient($sqsConfig); + } + /** + * @param array> $batchIds + */ + private function deleteBatchMessages(SqsClient $client, array $batchIds, string $queueUrl): void + { foreach ($batchIds as $batch) { - // Deletes up to ten messages from the specified queue. - try { - $result = $client->deleteMessageBatch([ - 'Entries' => $batch, - 'QueueUrl' => $queue, - ]); - - if (isset($result['Failed'])) { - $msg = ''; - foreach ($result['Failed'] as $failed) { - $msg .= sprintf('Deleting message failed, code = %s, id = %s, msg = %s, senderfault = %s', $failed['Code'], $failed['Id'], $failed['Message'], $failed['SenderFault']); - } - Log::error('Cannot delete some SQS messages: ', [$msg]); - - throw new \RuntimeException('Cannot delete some messages, consult log for more info!'); - } - // Log::info('Message remove report:', [$result]); - } catch (AwsException $e) { - Log::error('AWS SQS client error:', [$e->getMessage()]); + $result = $client->deleteMessageBatch([ + 'Entries' => $batch, + 'QueueUrl' => $queueUrl, + ]); + + if (isset($result['Failed']) && ! empty($result['Failed'])) { + $this->handleFailedDeletions($result['Failed']); } } } + + /** + * @param array> $failedDeletions + */ + private function handleFailedDeletions(array $failedDeletions): void + { + $errorMessages = []; + + foreach ($failedDeletions as $failed) { + $errorMessages[] = sprintf( + 'Code: %s, ID: %s, Message: %s, Sender Fault: %s', + $failed['Code'] ?? 'unknown', + $failed['Id'] ?? 'unknown', + $failed['Message'] ?? 'unknown', + $failed['SenderFault'] ?? 'unknown' + ); + } + + $combinedMessage = implode(' | ', $errorMessages); + + Log::error('Failed to delete SQS messages', [ + 'failed_deletions' => $failedDeletions, + 'error_summary' => $combinedMessage, + ]); + + throw new RuntimeException('Failed to delete some SQS messages. Check logs for details.'); + } } From 4579ae5831ce597fb45455256c8b4caf9b9fbf9b Mon Sep 17 00:00:00 2001 From: Palaniappan P Date: Mon, 22 Sep 2025 11:50:27 +0530 Subject: [PATCH 5/9] Enhance SqsServiceProvider --- src/Sqs/Queue.php | 313 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 216 insertions(+), 97 deletions(-) diff --git a/src/Sqs/Queue.php b/src/Sqs/Queue.php index 8baf7f5..466125d 100644 --- a/src/Sqs/Queue.php +++ b/src/Sqs/Queue.php @@ -5,182 +5,301 @@ namespace palPalani\SqsQueueReader\Sqs; use Aws\Exception\AwsException; +use Illuminate\Contracts\Queue\Job; use Illuminate\Queue\Jobs\SqsJob; use Illuminate\Queue\SqsQueue; use Illuminate\Support\Facades\Config; use Illuminate\Support\Str; use JsonException; use palPalani\SqsQueueReader\Jobs\DispatcherJob; +use RuntimeException; /** - * Class CustomSqsQueue + * Custom SQS Queue implementation for handling raw JSON payloads from external sources. + * + * This queue extends Laravel's SqsQueue to support: + * - Raw JSON message processing + * - Single and batch message handling + * - Custom handler class routing based on queue configuration */ class Queue extends SqsQueue { /** * Create a payload string from the given job and data. * - * @param object|string $job - * @param string $queue - * @param mixed $data + * @param object|string $job The job instance or class name + * @param ?string $queue The queue name + * @param mixed $data Additional job data * - * @throws JsonException + * @throws JsonException When JSON encoding fails */ - protected function createPayload($job, $queue = null, $data = '', $delay = null): string + protected function createPayload($job, $queue = null, $data = ''): string { if (! $job instanceof DispatcherJob) { return parent::createPayload($job, $queue, $data); } - $handlerJob = $this->getClass($queue) . '@handle'; + if ($job->isPlain()) { + return json_encode($job->getPayload(), JSON_THROW_ON_ERROR); + } + + $handlerClass = $this->getHandlerClass($queue); - return $job->isPlain() ? \json_encode($job->getPayload(), JSON_THROW_ON_ERROR) : \json_encode([ - 'job' => $handlerJob, + return json_encode([ + 'job' => "{$handlerClass}@handle", 'data' => $job->getPayload(), ], JSON_THROW_ON_ERROR); } - private function getClass($queue = null): string + /** + * Get the handler class for the specified queue. + * + * @param ?string $queue The queue URL or name + * @return string The fully qualified handler class name + */ + private function getHandlerClass(?string $queue = null): string + { + $queueId = $this->extractQueueId($queue); + $handlers = Config::get('sqs-queue-reader.handlers', []); + $defaultHandler = Config::get('sqs-queue-reader.default-handler'); + + if ($queueId && array_key_exists($queueId, $handlers)) { + return $handlers[$queueId]['class']; + } + + return $defaultHandler['class']; + } + + /** + * Extract queue ID from queue URL or return null for default queue. + * + * @param ?string $queue The queue URL or name + * @return ?string The extracted queue ID + */ + private function extractQueueId(?string $queue): ?string { if (! $queue) { - return Config::get('sqs-queue-reader.default-handler')['class']; + return null; } - $queueId = explode('/', $queue); - $queueId = array_pop($queueId); + $parts = explode('/', $queue); - return (\array_key_exists($queueId, Config::get('sqs-queue-reader.handlers'))) - ? Config::get('sqs-queue-reader.handlers')[$queueId]['class'] - : Config::get('sqs-queue-reader.default-handler')['class']; + return array_pop($parts); + } + + /** + * Get queue configuration for the specified queue. + * + * @param ?string $queue The queue URL or name + * @return array{class: string, count: int} Queue configuration + */ + private function getQueueConfig(?string $queue): array + { + $queueId = $this->extractQueueId($queue); + $handlers = Config::get('sqs-queue-reader.handlers', []); + $defaultHandler = Config::get('sqs-queue-reader.default-handler'); + + if ($queueId && array_key_exists($queueId, $handlers)) { + return $handlers[$queueId]; + } + + return $defaultHandler; } /** * Pop the next job off of the queue. * - * @param string $queue - * @return \Illuminate\Contracts\Queue\Job|null + * @param ?string $queue The queue name + * @return ?Job The next job or null if no jobs available * - * @throws JsonException + * @throws JsonException When JSON processing fails + * @throws RuntimeException When SQS operation fails */ public function pop($queue = null) { - $queue = $this->getQueue($queue); - - $queueId = explode('/', $queue); - $queueId = array_pop($queueId); - - $count = (\array_key_exists($queueId, Config::get('sqs-queue-reader.handlers'))) - ? Config::get('sqs-queue-reader.handlers')[$queueId]['count'] - : Config::get('sqs-queue-reader.default-handler')['count']; + $queueUrl = $this->getQueue($queue); + $queueConfig = $this->getQueueConfig($queueUrl); try { - $response = $this->sqs->receiveMessage([ - 'QueueUrl' => $queue, - 'AttributeNames' => ['ApproximateReceiveCount'], - 'MaxNumberOfMessages' => $count, - 'MessageAttributeNames' => ['All'], - ]); - - if (isset($response['Messages']) && count($response['Messages']) > 0) { - $class = (\array_key_exists($queueId, $this->container['config']->get('sqs-queue-reader.handlers'))) - ? $this->container['config']->get('sqs-queue-reader.handlers')[$queueId]['class'] - : $this->container['config']->get('sqs-queue-reader.default-handler')['class']; - - if ($count === 1) { - $response = $this->modifySinglePayload($response['Messages'][0], $class); - } else { - $response = $this->modifyMultiplePayload($response['Messages'], $class); - } - - return new SqsJob($this->container, $this->sqs, $response, $this->connectionName, $queue); + $response = $this->receiveMessages($queueUrl, $queueConfig['count']); + + if (empty($response['Messages'])) { + return; } - } catch (AwsException $e) { - $msg = 'Line: ' . $e->getLine() . ', ' . $e->getFile() . ', ' . $e->getMessage(); - throw new \RuntimeException('Aws SQS error: ' . $msg); + $messages = $response['Messages']; + $handlerClass = $queueConfig['class']; + + $processedResponse = $this->processMessages($messages, $handlerClass); + + return new SqsJob( + $this->container, + $this->sqs, + $processedResponse, + $this->connectionName, + $queueUrl + ); + } catch (AwsException $e) { + throw new RuntimeException( + sprintf( + 'AWS SQS error: %s (File: %s, Line: %d)', + $e->getMessage(), + $e->getFile(), + $e->getLine() + ), + $e->getCode(), + $e + ); } } /** - * @throws JsonException + * Receive messages from SQS queue. + * + * @param string $queueUrl The SQS queue URL + * @param int $maxMessages Maximum number of messages to receive + * @return array SQS response containing messages + * + * @throws AwsException When SQS operation fails */ - private function modifySinglePayload(array|string $payload, string $class): array|string + private function receiveMessages(string $queueUrl, int $maxMessages): array { - if (! is_array($payload)) { - $payload = \json_decode($payload, true, 512, JSON_THROW_ON_ERROR); - } + $result = $this->sqs->receiveMessage([ + 'QueueUrl' => $queueUrl, + 'AttributeNames' => ['ApproximateReceiveCount'], + 'MaxNumberOfMessages' => $maxMessages, + 'MessageAttributeNames' => ['All'], + ]); - $body = \json_decode($payload['Body'], true, 512, JSON_THROW_ON_ERROR); + return $result->toArray(); + } - $payload['Body'] = \json_encode([ + /** + * Process received messages into Laravel job format. + * + * @param array $messages Array of SQS messages + * @param string $handlerClass The handler class name + * @return array Processed message data + * + * @throws JsonException When JSON processing fails + */ + private function processMessages(array $messages, string $handlerClass): array + { + return count($messages) === 1 + ? $this->processSingleMessage($messages[0], $handlerClass) + : $this->processMultipleMessages($messages, $handlerClass); + } + + /** + * Process a single SQS message into Laravel job format. + * + * @param array $message The SQS message data + * @param string $handlerClass The handler class name + * @return array Processed message data + * + * @throws JsonException When JSON processing fails + */ + private function processSingleMessage(array $message, string $handlerClass): array + { + $messageBody = $this->decodeMessageBody($message['Body']); + + $message['Body'] = json_encode([ 'uuid' => (string) Str::uuid(), - 'job' => $class . '@handle', - 'data' => $body['data'] ?? $body, + 'job' => "{$handlerClass}@handle", + 'data' => $messageBody['data'] ?? $messageBody, ], JSON_THROW_ON_ERROR); - return $payload; + return $message; } /** - * @throws JsonException + * Process multiple SQS messages into Laravel batch job format. + * + * @param array $messages Array of SQS message data + * @param string $handlerClass The handler class name + * @return array Processed batch message data + * + * @throws JsonException When JSON processing fails */ - private function modifyMultiplePayload(array|string $payload, string $class): array + private function processMultipleMessages(array $messages, string $handlerClass): array { - if (! is_array($payload)) { - $payload = \json_decode($payload, true, 512, JSON_THROW_ON_ERROR); - } + $batchData = []; + $lastMessage = end($messages); - $body = []; - $attributes = []; - $messageId = null; - $receiptHandle = null; - - foreach ($payload as $k => $item) { - try { - $message = \json_decode($item['Body'], true, 512, JSON_THROW_ON_ERROR); - } catch (JsonException $e) { - $message = []; - } + foreach ($messages as $index => $message) { + $messageBody = $this->safeDecodeMessageBody($message['Body']); - $body[$k] = [ - 'messages' => $message, - 'attributes' => $item['Attributes'], + $batchData[$index] = [ + 'messages' => $messageBody, + 'attributes' => $message['Attributes'] ?? [], 'batchIds' => [ - 'Id' => $item['MessageId'], - 'ReceiptHandle' => $item['ReceiptHandle'], + 'Id' => $message['MessageId'], + 'ReceiptHandle' => $message['ReceiptHandle'], ], ]; - $attributes = $item['Attributes']; - $messageId = $item['MessageId']; - $receiptHandle = $item['ReceiptHandle']; } return [ - 'MessageId' => $messageId, - 'ReceiptHandle' => $receiptHandle, - 'Body' => \json_encode([ + 'MessageId' => $lastMessage['MessageId'], + 'ReceiptHandle' => $lastMessage['ReceiptHandle'], + 'Body' => json_encode([ 'uuid' => (string) Str::uuid(), - 'job' => $class . '@handle', - 'data' => $body, + 'job' => "{$handlerClass}@handle", + 'data' => $batchData, ], JSON_THROW_ON_ERROR), - 'Attributes' => $attributes, + 'Attributes' => $lastMessage['Attributes'] ?? [], ]; } /** - * @param string $payload - * @param string|null $queue + * Decode message body JSON with error handling. * - * @throws JsonException + * @param string $messageBody The raw message body + * @return array The decoded message data + * + * @throws JsonException When JSON decoding fails */ - public function pushRaw($payload, $queue = null, array $options = []): mixed + private function decodeMessageBody(string $messageBody): array { - $payload = \json_decode($payload, true, 512, JSON_THROW_ON_ERROR); + return json_decode($messageBody, true, 512, JSON_THROW_ON_ERROR); + } - if (isset($payload['data'], $payload['job'])) { - $payload = $payload['data']; + /** + * Safely decode message body JSON, returning empty array on failure. + * + * @param string $messageBody The raw message body + * @return array The decoded message data or empty array + */ + private function safeDecodeMessageBody(string $messageBody): array + { + try { + return $this->decodeMessageBody($messageBody); + } catch (JsonException) { + return []; } + } + + /** + * Push a raw payload onto the queue. + * + * @param string $payload The raw JSON payload + * @param ?string $queue The queue name + * @param array $options Additional options + * @return mixed The result of the push operation + * + * @throws JsonException When JSON processing fails + */ + public function pushRaw($payload, $queue = null, array $options = []) + { + $decodedPayload = json_decode($payload, true, 512, JSON_THROW_ON_ERROR); + + // Extract data from Laravel job format if present + if (isset($decodedPayload['data'], $decodedPayload['job'])) { + $decodedPayload = $decodedPayload['data']; + } + + $processedPayload = json_encode($decodedPayload, JSON_THROW_ON_ERROR); - return parent::pushRaw(\json_encode($payload, JSON_THROW_ON_ERROR), $queue, $options); + return parent::pushRaw($processedPayload, $queue, $options); } } From 589e45c6d90726914bdf4b36bdbab97838547823 Mon Sep 17 00:00:00 2001 From: Palaniappan P Date: Mon, 22 Sep 2025 11:51:36 +0530 Subject: [PATCH 6/9] fixed warning --- src/Sqs/Queue.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Sqs/Queue.php b/src/Sqs/Queue.php index 466125d..f313e97 100644 --- a/src/Sqs/Queue.php +++ b/src/Sqs/Queue.php @@ -33,7 +33,7 @@ class Queue extends SqsQueue * * @throws JsonException When JSON encoding fails */ - protected function createPayload($job, $queue = null, $data = ''): string + protected function createPayload($job, $queue = null, $data = '', $delay = null): string { if (! $job instanceof DispatcherJob) { return parent::createPayload($job, $queue, $data); From 57bce0ebcd90f900aae7aa7382c98b5bf74558ce Mon Sep 17 00:00:00 2001 From: Palaniappan P Date: Mon, 22 Sep 2025 11:54:40 +0530 Subject: [PATCH 7/9] remove unused config -n --- psalm.xml.dist | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 psalm.xml.dist diff --git a/psalm.xml.dist b/psalm.xml.dist deleted file mode 100644 index 4b85ca0..0000000 --- a/psalm.xml.dist +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - From d0d9a3ab212dea4ab4c0e4e3da9b802d990c9f5d Mon Sep 17 00:00:00 2001 From: Palaniappan P Date: Mon, 22 Sep 2025 12:00:09 +0530 Subject: [PATCH 8/9] Improved readme --- README.md | 123 ++++++++++++++++++++++++++---------------------------- 1 file changed, 59 insertions(+), 64 deletions(-) diff --git a/README.md b/README.md index 32a7e5a..4a1e3b4 100644 --- a/README.md +++ b/README.md @@ -1,54 +1,54 @@ -# Custom SQS queue reader for Laravel +# Custom SQS Queue Reader for Laravel

Custom SQS queue reader for Laravel

-The Laravel SQS Queue Reader is a powerful extension designed to seamlessly integrate external webhooks into your Laravel application. By leveraging the reliability and scalability of Amazon Simple Queue Service (SQS), this extension ensures that your application efficiently processes incoming webhooks, minimizing downtime and enhancing overall performance. +The Laravel SQS Queue Reader is an extension designed to seamlessly integrate external webhooks into your Laravel application. By leveraging the reliability and scalability of Amazon Simple Queue Service (SQS), this package ensures your application efficiently processes incoming webhooks, minimizing downtime and enhancing overall performance. -### Key Features: +### Key Features -#### Effortless Webhook Integration: +#### Effortless Webhook Integration -Easily integrate external webhooks into your Laravel application without compromising on performance. +Easily integrate external webhooks into your Laravel application without compromising performance. -#### Queue-Based Processing: +#### Queue-Based Processing -Harness the power of Amazon SQS to queue incoming webhooks, allowing for asynchronous and parallel processing, ensuring optimal response times. +Harness Amazon SQS to queue incoming webhooks for asynchronous and parallel processing, ensuring optimal response times. -#### Reliability and Scalability: +#### Reliability and Scalability -SQS provides a robust and scalable infrastructure, ensuring that your application can handle varying webhook loads without compromising on stability. +SQS provides a robust and scalable infrastructure so your application can handle varying webhook loads without compromising stability. -#### Seamless Laravel Integration: +#### Seamless Laravel Integration -Designed as a Laravel extension, the Webhook Queue Reader seamlessly integrates into your Laravel project, following Laravel's coding standards and conventions. +Designed as a Laravel extension, the Queue Reader integrates cleanly into your project, following Laravel's coding standards and conventions. -#### Configurable Settings: +#### Configurable Settings -Customize the extension's settings to align with your application's requirements, including queue names, visibility timeout, and other SQS-specific configurations. +Customize settings to align with your application's requirements, including queue names, visibility timeout, and other SQS-specific configurations. -#### Detailed Logging: +#### Detailed Logging -Gain insights into the webhook processing flow with detailed logging, helping you troubleshoot and monitor the system effectively. +Gain insights into the webhook processing flow with detailed logging to help troubleshoot and monitor effectively. -### How It Works: +### How It Works -#### Webhook Registration: +#### Webhook Registration Register external webhooks with your Laravel application by providing the webhook URL. -#### SQS Queue Integration: +#### SQS Queue Integration -Incoming webhooks are efficiently processed through the SQS queue, ensuring optimal handling of webhook payloads. +Incoming webhooks are processed through the SQS queue, ensuring optimal handling of webhook payloads. -#### Asynchronous Processing: +#### Asynchronous Processing -Leverage the asynchronous processing capabilities of SQS to handle webhooks in the background, preventing any impact on your application's response times. +Leverage asynchronous processing to handle webhooks in the background, preventing impact on your application's response times. -#### Automatic Retries: +#### Automatic Retries -Benefit from SQS's automatic retries, ensuring that failed webhook processing attempts are retried without manual intervention. +Benefit from SQS automatic retries, ensuring failed webhook processing attempts are retried without manual intervention. [![Latest Version on Packagist](https://img.shields.io/packagist/v/palpalani/laravel-sqs-queue-json-reader.svg?style=for-the-badge)](https://packagist.org/packages/palpalani/laravel-sqs-queue-json-reader) [![GitHub Tests Action Status](https://img.shields.io/github/actions/workflow/status/palpalani/laravel-sqs-queue-json-reader/run-tests.yml?branch=main&label=tests&style=for-the-badge)](https://github.com/palpalani/laravel-sqs-queue-json-reader/actions?query=workflow%3Arun-tests+branch%3Amain) @@ -60,28 +60,27 @@ Benefit from SQS's automatic retries, ensuring that failed webhook processing at --> PHP 8.1 -Custom SQS queue reader for Laravel projects that supports raw JSON payloads and reads multiple messages. Laravel expects SQS messages to be generated in a specific format that includes job handler class and a serialized job. +Custom SQS queue reader for Laravel projects that supports raw JSON payloads and reads multiple messages. Laravel expects SQS messages to be generated in a specific format that includes a job handler class and a serialized job. -Note: Implemented to read multiple messages from queue. +Note: Implemented to read multiple messages from the queue. -This library is very useful when you want to parse messages from 3rd party -applications such as stripe webhooks, shopify webhooks, mailgun web hooks, custom JSON messages and so on. +This library is useful when you want to parse messages from 3rd-party applications such as Stripe webhooks, Shopify webhooks, Mailgun webhooks, custom JSON messages, and more. ## Getting Started -Install Custom SQS queue reader for Laravel via composer: +Install via Composer: ```bash composer require palpalani/laravel-sqs-queue-json-reader ``` -You can publish the config file and Configure your SQS settings in the Laravel configuration file. +Publish the config file and configure your SQS settings in the Laravel configuration file: ```bash php artisan vendor:publish --provider="palPalani\SqsQueueReader\SqsQueueReaderServiceProvider" --tag="config" ``` -This is the contents of the published config file: +This is the content of the published config file: ```php /** @@ -113,11 +112,11 @@ return [ ]; ``` -If the queue is not found in 'handlers' array, SQS payload is passed to default handler. +If the queue is not found in the `handlers` array, the SQS payload is passed to the default handler. Register your webhooks with your Laravel application. -Add `sqs-json` connection to your config/queue.php, Example: +Add an `sqs-json` connection to your `config/queue.php`. Example: ```php [ @@ -133,17 +132,17 @@ Add `sqs-json` connection to your config/queue.php, Example: ] ``` -In your .env file, choose sqs-json as your new default queue driver: +In your `.env` file, choose `sqs-json` as your default queue connection: ``` -QUEUE_DRIVER=sqs-json +QUEUE_CONNECTION=sqs-json ``` Enjoy seamless, reliable, and scalable webhook processing! ## Dispatching to SQS -If you plan to push plain messages from Laravel, you can rely on DispatcherJob: +If you plan to push plain messages from Laravel, you can rely on `DispatcherJob`: ```php use palPalani\SqsQueueReader\Jobs\DispatcherJob; @@ -168,22 +167,21 @@ class ExampleController extends Controller } } ``` -Above code will push the following JSON object to SQS queue: +The above code will push the following JSON object to the SQS queue: ```json -{"job":"App\\Jobs\\SqsHandler@handle","data":{"music":"Sample SQS message","singer":"AR. Rahman","time":1464511672}} +{"job":"App\\Jobs\\SqsHandler@handle","data":{"music":"Ponni nathi from PS-1","singer":"AR. Rahman","time":1464511672}} ``` -'job' field is not used, actually. It's just kept for compatibility with Laravel -Framework. +The `job` field is not used; it is kept for compatibility with the Laravel framework. -### Processing job +### Processing Job -Run the following commnd for testing the dispatched job. +Run the following command to test the dispatched job. `php artisan queue:work sqs-json` -For `production`, use supervisor with the following configuration. +For production, use Supervisor with the following configuration. ``` [program:sqs-json-reader] @@ -202,14 +200,11 @@ stopwaitsecs=3600 priority=1000 ``` -If you are using multiple connection, then duplicate above supervisor -configutation and change the connection name. +If you are using multiple connections, duplicate the above Supervisor configuration and change the connection name. ### Receiving from SQS -If a 3rd-party application or API Gateway to SQS implementation is creating -custom-format JSON messages, just add a -handler in the config file and implement a handler class as follows: +If a 3rd-party application or an API Gateway-to-SQS implementation is creating custom-format JSON messages, add a handler in the config file and implement a handler class as follows: ```php use Illuminate\Contracts\Queue\Job as LaravelJob; @@ -240,28 +235,28 @@ Note: Ensure that your Laravel application is configured with the necessary AWS credentials and permissions to interact with SQS. -Enhance your Laravel application's webhook processing capabilities with the Laravel Webhook Queue Reader. Efficient, reliable, and designed for optimal performance! +Enhance your Laravel application's webhook processing capabilities with the Laravel SQS Queue Reader. Efficient, reliable, and designed for optimal performance! -For more information about AWS SQS check [offical docs](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-configure-queue-parameters.html). +For more information about AWS SQS, check the [official docs](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-configure-queue-parameters.html). ## Testing -We already configured the script, just run the command: +We already configured the scripts; just run the command: ```bash composer test ``` -For test coverage format, run the command: +For test coverage, run the command: ```bash composer test-coverage ``` -For code analyse, run the command: +For code analysis, run the command: ```bash composer analyse ``` -For code format, run the command: +For code formatting, run the command: ```bash composer format @@ -275,14 +270,14 @@ Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed re Please see [CONTRIBUTING](.github/CONTRIBUTING.md) for details. -If you want to contribute, then you may want to test it in a real Laravel project: +If you want to contribute, you may want to test it in a real Laravel project: - Fork this repository to your GitHub account. - Create a Laravel app locally. - Clone your fork in your Laravel app's root directory. - In the `/laravel-sqs-queue-json-reader` directory, create a branch for your fix, e.g. `feature/awesome-feature`. -Install the packages in your app's `composer.json`: +Install the package in your app's `composer.json`: ```jsonc { @@ -303,21 +298,21 @@ Install the packages in your app's `composer.json`: Now, run `composer update`. -## Other Laravel packages +## Other Laravel Packages -[GrumPHP rector task](https://github.com/palpalani/grumphp-rector-task) GrumPHP with a task that runs [RectorPHP](https://github.com/rectorphp/rector-src) for your Laravel projects. +[GrumPHP Rector task](https://github.com/palpalani/grumphp-rector-task) GrumPHP with a task that runs [RectorPHP](https://github.com/rectorphp/rector-src) for your Laravel projects. -[Email Deny List (blacklist) Check - IP Deny List (blacklist) Check](https://github.com/palpalani/laravel-dns-deny-list-check) Deny list (blacklist) checker will test a mail server IP address against over 50 DNS based email blacklists. (Commonly called Realtime blacklist, DNSBL or RBL). +[Email Deny List (blacklist) Check - IP Deny List (blacklist) Check](https://github.com/palpalani/laravel-dns-deny-list-check) Deny list (blacklist) checker that tests a mail server IP address against 50+ DNS-based email blacklists (commonly called Realtime blacklist, DNSBL, or RBL). -[Spamassassin spam score of emails](https://github.com/palpalani/laravel-spamassassin-score) Checks the spam score of email contents using spamassassin database. +[SpamAssassin spam score of emails](https://github.com/palpalani/laravel-spamassassin-score) Checks the spam score of email contents using the SpamAssassin database. -[Laravel Login Notifications](https://github.com/palpalani/laravel-login-notifications) A login event notification for Laravel projects. By default, it will send notification only on production environment only. +[Laravel Login Notifications](https://github.com/palpalani/laravel-login-notifications) A login event notification for Laravel projects. By default, it sends notifications only in the production environment. -[Laravel Toastr](https://github.com/palpalani/laravel-toastr) Implements toastr.js for Laravel. Toastr.js is a Javascript library for non-blocking notifications. +[Laravel Toastr](https://github.com/palpalani/laravel-toastr) Implements toastr.js for Laravel. Toastr.js is a JavaScript library for non-blocking notifications. -[Beast](https://github.com/palpalani/beast) Beast is Screenshot as a Service using Nodejs, Chrome and Aws Lamda. Convert a webpage to an image using headless Chrome Takes screenshot of any given URL/Html content and returns base64 encoded buffer. +[Beast](https://github.com/palpalani/beast) Beast is Screenshot as a Service using Node.js, Chrome, and AWS Lambda. Convert a webpage to an image using headless Chrome; takes a screenshot of any given URL/HTML content and returns a base64-encoded buffer. -[eCommerce Product Recommendations](https://github.com/palpalani/eCommerce-Product-Recommendations) Analyse order history of customers and recommend products for new customers which enables higher sales volume. +[eCommerce Product Recommendations](https://github.com/palpalani/eCommerce-Product-Recommendations) Analyze order history of customers and recommend products for new customers to enable higher sales volume. ## Security Vulnerabilities @@ -330,7 +325,7 @@ Please review [our security policy](../../security/policy) on how to report secu ## Need Help? -If you spot a bug or have a question or feature request, please [submit a detailed issue](https://github.com/palpalani/laravel-sqs-queue-json-reader/issues), and wait for assistance. +If you spot a bug or have a question or feature request, please [submit a detailed issue](https://github.com/palpalani/laravel-sqs-queue-json-reader/issues) and wait for assistance. ## License From a871b89a0018f29b4aea2b0b31a510abecf73f19 Mon Sep 17 00:00:00 2001 From: Palaniappan P Date: Mon, 22 Sep 2025 16:14:36 +0530 Subject: [PATCH 9/9] fix: tests --- phpstan.neon.dist | 3 +- phpunit.xml.dist | 5 --- src/SqsQueueReaderServiceProvider.php | 2 +- tests/Pest.php | 7 +++ tests/QueueTest.php | 61 ++++++++++++++------------- 5 files changed, 41 insertions(+), 37 deletions(-) create mode 100644 tests/Pest.php diff --git a/phpstan.neon.dist b/phpstan.neon.dist index cc5235c..1b2cfff 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -7,5 +7,4 @@ parameters: - src tmpDir: build/phpstan checkOctaneCompatibility: true - checkModelProperties: true - checkMissingIterableValueType: false \ No newline at end of file + checkModelProperties: true \ No newline at end of file diff --git a/phpunit.xml.dist b/phpunit.xml.dist index edf5050..b97569e 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -3,12 +3,8 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd" backupGlobals="false" - backupStaticAttributes="false" bootstrap="vendor/autoload.php" colors="true" - convertErrorsToExceptions="true" - convertNoticesToExceptions="true" - convertWarningsToExceptions="true" processIsolation="false" stopOnFailure="false" executionOrder="random" @@ -16,7 +12,6 @@ failOnRisky="true" failOnEmptyTestSuite="true" beStrictAboutOutputDuringTests="true" - verbose="true" > diff --git a/src/SqsQueueReaderServiceProvider.php b/src/SqsQueueReaderServiceProvider.php index 7046d6e..facf142 100644 --- a/src/SqsQueueReaderServiceProvider.php +++ b/src/SqsQueueReaderServiceProvider.php @@ -151,7 +151,7 @@ private function removeBatchMessages(Job $job, string $connectionName): void /** * @param array $payload - * @return array> + * @return list>> */ private function extractBatchIds(array $payload): array { diff --git a/tests/Pest.php b/tests/Pest.php new file mode 100644 index 0000000..2d065fb --- /dev/null +++ b/tests/Pest.php @@ -0,0 +1,7 @@ +in(__DIR__); diff --git a/tests/QueueTest.php b/tests/QueueTest.php index caf91bc..f89c46f 100644 --- a/tests/QueueTest.php +++ b/tests/QueueTest.php @@ -2,42 +2,45 @@ declare(strict_types=1); -namespace palPalani\SqsQueueReader\Tests; - use palPalani\SqsQueueReader\Jobs\DispatcherJob; use palPalani\SqsQueueReader\Sqs\Queue; -use function PHPUnit\Framework\assertTrue; +it('can create payload for dispatcher job with default handler', function () { + $content = [ + 'test' => 'test', + ]; + + $job = new DispatcherJob($content); + + $queue = Mockery::mock(Queue::class) + ->makePartial() + ->shouldAllowMockingProtectedMethods(); -/** - * Class QueueTest - */ -class QueueTest extends TestCase -{ - /** - * @test - */ - public function class_named_is_derived_from_queue_name(): void - { - $content = [ - 'test' => 'test', - ]; + $payload = $queue->createPayload($job); + $decodedPayload = json_decode($payload, true); - $job = new DispatcherJob($content); + expect($payload)->toBeString() + ->and($decodedPayload)->toBeArray() + ->and($decodedPayload['job'])->toBe('App\Jobs\SqsHandler@handle') + ->and($decodedPayload['data'])->toHaveKey('job') + ->and($decodedPayload['data'])->toHaveKey('data') + ->and($decodedPayload['data']['data'])->toBe($content); +}); - $queue = $this->getMockBuilder(Queue::class) - ->disableOriginalConstructor() - ->getMock(); +it('can create plain payload for dispatcher job', function () { + $content = [ + 'test' => 'test', + ]; - $method = new \ReflectionMethod( - Queue::class, - 'createPayload' - ); + $job = new DispatcherJob($content); + $job->setPlain(true); - $method->setAccessible(true); + $queue = Mockery::mock(Queue::class) + ->makePartial() + ->shouldAllowMockingProtectedMethods(); - $method->invokeArgs($queue, [$job]); + $payload = $queue->createPayload($job); - assertTrue(true); - } -} + expect($payload)->toBeString() + ->and(json_decode($payload, true))->toBe($content); +});