Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Serginyu authored Dec 9, 2021
1 parent a6e9238 commit 6b7ef76
Show file tree
Hide file tree
Showing 10 changed files with 327 additions and 7 deletions.
4 changes: 4 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
FROM composer/composer:1.10.23

RUN apk add --no-cache icu-dev && \
docker-php-ext-install sockets intl
58 changes: 54 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,16 @@ Laravel Queue Job
[![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/softonic/laravel-queue-job.svg?style=flat-square)](http://isitmaintained.com/project/softonic/laravel-queue-job "Average time to resolve an issue")
[![Percentage of issues still open](http://isitmaintained.com/badge/open/softonic/laravel-queue-job.svg?style=flat-square)](http://isitmaintained.com/project/softonic/laravel-queue-job "Percentage of issues still open")

Custom Job implementation for vyuldashev@laravel-queue-rabbitmq library
Custom Job implementation for [vyuldashev/laravel-queue-rabbitmq](https://github.com/vyuldashev/laravel-queue-rabbitmq) library

:warning: This library works on [vyuldashev/laravel-queue-rabbitmq](https://github.com/vyuldashev/laravel-queue-rabbitmq).
If you have questions about how to configure connections, feel free to read the [vyuldashev/laravel-queue-rabbitmq](https://github.com/vyuldashev/laravel-queue-rabbitmq) documentation.

Main features
-------------

*
* Add support to have multiple Handlers for the same Routing key.
* Assign your Routing keys with your Handlers in the queue config file.

Installation
-------------
Expand All @@ -23,12 +27,58 @@ You can require the last version of the package using composer
composer require softonic/laravel-queue-job
```

### Configuration
Usage
-------------

Replace your RabbitMQJob class in the queue config file.
```
'connections' => [
// ...
```php
'rabbitmq' => [
// ...
'options' => [
'queue' => [
// ...
'job' => \Softonic\LaravelQueueJob\RabbitMQJob::class,
],
],
],
// ...
],
```

Add your message_handlers mapping in queue config file:

```
'message_handlers' => [
TestHandler::class => [ // Handler
'#.test_v1.created.testevent', // Routing keys
'#.test_v1.replaced.testevent',
'#.test_v1.updated.testevent',
'global.test_v1.updated.testevent',
// ...
],
AnotherTestHandler::class => [
'#.test_v1.created.testevent',
'global.test_v1.updated.testevent',
// ...
],
// ...
],
```

Testing with artisan
-------

Your
``
php artisan queue:work {connection-name} --queue={queue-name}
``

Testing
-------

Expand Down
7 changes: 5 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,17 @@
"issues": "https://github.com/softonic/laravel-queue-job/issues"
},
"require": {
"php": ">=8.0"
"php": "^8.0",
"laravel/framework": "^8.75",
"vladimir-yuldashev/laravel-queue-rabbitmq": "^11.3"
},
"require-dev": {
"phpunit/phpunit": "^9.0",
"mockery/mockery": "^1.0",
"friendsofphp/php-cs-fixer": "^2.4",
"squizlabs/php_codesniffer": "^3",
"rector/rector": "^0.11.20"
"rector/rector": "^0.11.20",
"orchestra/testbench": "^6.23"
},
"autoload": {
"psr-4": {
Expand Down
2 changes: 1 addition & 1 deletion docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ services:
composer:
volumes:
- ./:/app
image: softonic/composer-rector:latest
build: ./
Empty file removed src/.gitkeep
Empty file.
66 changes: 66 additions & 0 deletions src/RabbitMQJob.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php

namespace Softonic\LaravelQueueJob;

use Illuminate\Support\Facades\Config;
use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs\RabbitMQJob as BaseJob;

class RabbitMQJob extends BaseJob
{
private const HANDLER_NOT_CONFIGURED_MESSAGE = 'HandlerNotConfigured';

private array $messageHandlers = [];

/**
* Get the decoded body of the job.
*
* @return array
*/
public function payload()
{
$this->messageHandlers = $this->getMessageHandlers($this->getRabbitMQMessage()->getRoutingKey());

return [
'job' => $this->messageHandlers[0] ?? self::HANDLER_NOT_CONFIGURED_MESSAGE,
'data' => json_decode($this->getRawBody(), true),
];
}

public function fire()
{
$payload = $this->payload();

foreach ($this->messageHandlers as $messageHandler) {
$messageHandler::dispatch($payload['data']);
}

$this->instance = $this;
$this->delete();
}

private function getMessageHandlers(string $routingKey): array
{
$messageHandlers = [];
foreach (Config::get('queue')['message_handlers'] as $job => $messageHandlerRoutingKeys) {
$messageHandlerRoutingKeysRegex = $this->getRoutingKeysRegex($messageHandlerRoutingKeys);

if (preg_match($messageHandlerRoutingKeysRegex, $routingKey)) {
$messageHandlers[] = $job;
}
}

return $messageHandlers;
}

private function getRoutingKeysRegex(array $messageHandlerRoutingKeys): string
{
$messageHandlerRoutingKeysFlattened = implode('|', $messageHandlerRoutingKeys);
$messageHandlerRoutingKeysRegex = preg_replace(
['/\./', '/#/'],
['\\.', '.*'],
$messageHandlerRoutingKeysFlattened
);

return '/' . $messageHandlerRoutingKeysRegex . '/';
}
}
Empty file removed tests/.gitkeep
Empty file.
175 changes: 175 additions & 0 deletions tests/RabbitMQJobTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
<?php

namespace Softonic\LaravelQueueJob;

use Illuminate\Container\Container;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Queue;
use Orchestra\Testbench\TestCase;
use PhpAmqpLib\Message\AMQPMessage;
use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\RabbitMQQueue;

class RabbitMQJobTest extends TestCase
{
private Container $containerMock;
private RabbitMQQueue $rabbitmqMock;
private AMQPMessage $amqpMessageMock;

private const MESSAGE_HANDLERS = [
'message_handlers' => [
TestHandlerOne::class => [
'#.test_v1.handle_test_1',
'#.test_v1.handle_test_2',
'#.test_v1.handle_test_3',
],
TestHandlerTwo::class => [
'#.test_v1.handle_test_1',
'#.test_v1.handle_test_2',
'#.test_v1.handle_test_4',
],
],
];

public function setUp(): void
{
parent::setUp();

Queue::fake();

$this->containerMock = \Mockery::mock(Container::class);
$this->rabbitmqMock = \Mockery::mock(RabbitMQQueue::class);
$this->amqpMessageMock = \Mockery::mock(AMQPMessage::class);

Config::set('queue', self::MESSAGE_HANDLERS);
}

/** @test */
public function whenGetPayloadItShouldReturnTheFirstJobFound()
{
$this->amqpMessageMock
->shouldReceive('getRoutingKey')
->andReturn('#.test_v1.handle_test_1');

$this->amqpMessageMock
->shouldReceive('getBody')
->andReturn(json_encode([]));

$rabbitMqJob = new RabbitMQJob(
$this->containerMock,
$this->rabbitmqMock,
$this->amqpMessageMock,
'test-connection',
'test-queue'
);

$this->assertEquals(TestHandlerOne::class, $rabbitMqJob->payload()['job']);
}

/** @test */
public function whenGetPayloadItShouldReturnTheCorrectJob()
{
$this->amqpMessageMock
->shouldReceive('getRoutingKey')
->andReturn('#.test_v1.handle_test_4');

$this->amqpMessageMock
->shouldReceive('getBody')
->andReturn(json_encode([]));

$rabbitMqJob = new RabbitMQJob(
$this->containerMock,
$this->rabbitmqMock,
$this->amqpMessageMock,
'test-connection',
'test-queue'
);

$this->assertEquals(TestHandlerTwo::class, $rabbitMqJob->payload()['job']);
}

/** @test */
public function whenGetPayloadItShouldReturnTheExpectedPayload()
{
$this->amqpMessageMock
->shouldReceive('getRoutingKey')
->andReturn('#.test_v1.handle_test_1');

$this->amqpMessageMock
->shouldReceive('getBody')
->andReturn(json_encode(['id' => 'test_1', 'version' => 'v1']));

$rabbitMqJob = new RabbitMQJob(
$this->containerMock,
$this->rabbitmqMock,
$this->amqpMessageMock,
'test-connection',
'test-queue'
);

$this->assertEquals(
[
'job' => TestHandlerOne::class,
'data' => [
'id' => 'test_1',
'version' => 'v1',
],
],
$rabbitMqJob->payload()
);
}

/** @test */
public function whenFireItShouldDispatchAllHandlers()
{
$this->amqpMessageMock
->shouldReceive('getRoutingKey')
->andReturn('#.test_v1.handle_test_1');

$this->amqpMessageMock
->shouldReceive('getBody')
->andReturn(json_encode(['id' => 'test_1', 'version' => 'v1']));

$this->rabbitmqMock
->shouldReceive('ack')->once();

$rabbitMqJob = new RabbitMQJob(
$this->containerMock,
$this->rabbitmqMock,
$this->amqpMessageMock,
'test-connection',
'test-queue'
);

$rabbitMqJob->fire();

Queue::assertPushed(TestHandlerOne::class);
Queue::assertPushed(TestHandlerTwo::class);
}

/** @test */
public function whenFireWithoutAssignedHandlerItShouldNotDispatchAnyHandler()
{
$this->amqpMessageMock
->shouldReceive('getRoutingKey')
->andReturn('#.non_existent_key');

$this->amqpMessageMock
->shouldReceive('getBody')
->andReturn('');

$this->rabbitmqMock
->shouldReceive('ack')->once();

$rabbitMqJob = new RabbitMQJob(
$this->containerMock,
$this->rabbitmqMock,
$this->amqpMessageMock,
'test-connection',
'test-queue'
);

$rabbitMqJob->fire();

Queue::assertNothingPushed();
}
}
11 changes: 11 additions & 0 deletions tests/TestHandlerOne.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace Softonic\LaravelQueueJob;

use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;

class TestHandlerOne implements ShouldQueue
{
use Dispatchable;
}
11 changes: 11 additions & 0 deletions tests/TestHandlerTwo.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace Softonic\LaravelQueueJob;

use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;

class TestHandlerTwo implements ShouldQueue
{
use Dispatchable;
}

0 comments on commit 6b7ef76

Please sign in to comment.