diff --git a/.docs/README.md b/.docs/README.md index 9e44aad..4b6225f 100644 --- a/.docs/README.md +++ b/.docs/README.md @@ -18,7 +18,11 @@ config.neon: ```neon extensions: + # Nette 3.0+ rabbitmq: Contributte\RabbitMQ\DI\RabbitMQExtension + + # Nette 2.4 + rabbitmq: Contributte\RabbitMQ\DI\RabbitMQExtension24 ``` ### Example configuration @@ -40,7 +44,7 @@ rabbitmq: testQueue: connection: default # force queue declare on first queue operation during request - # autoCreate: true + # autoCreate: true exchanges: testExchange: @@ -75,7 +79,7 @@ tracy: ### Declaring Queues and Exchanges -Since v3.0, all queues and exchanges are by default declared on demand using the console command: +Since v3.0, all queues and exchanges are by default declared on demand using the console command: ```bash php index.php rabbitmq:declareQueuesAndExchanges @@ -83,12 +87,12 @@ php index.php rabbitmq:declareQueuesAndExchanges It's intended to be a part of the deploy process to make sure all the queues and exchanges are prepared for use. -If you need to override this behavior (for example only declare queues that are used during a request and nothing else), +If you need to override this behavior (for example only declare queues that are used during a request and nothing else), just add the `autoCreate: true` parameter to queue or exchange of your choice. -You may also want to declare the queues and exchanges via rabbitmq management interface or a script but if you fail to -do so, don't run the declare console command and don't specify `autoCreate: true`, exceptions will be thrown -when accessing undeclared queues/exchanges. +You may also want to declare the queues and exchanges via rabbitmq management interface or a script but if you fail to +do so, don't run the declare console command and don't specify `autoCreate: true`, exceptions will be thrown when +accessing undeclared queues/exchanges. ### Publishing messages @@ -201,7 +205,8 @@ final class LongRunningTestQueue ### Consuming messages -Your consumer callback has to return a confirmation that particular message has been acknowledges (or different states - unack, reject). +Your consumer callback has to return a confirmation that particular message has been acknowledges (or different states - +unack, reject). TestConsumer.php @@ -234,7 +239,8 @@ final class TestConsumer implements IConsumer ### Running a consumer trough CLI -There are two consumer commands prepared. `rabbitmq:consumer` wiil consume messages for specified amount of time (in seconds), to run indefinitely skip this parameter. Following command will be consuming messages for one hour: +There are two consumer commands prepared. `rabbitmq:consumer` wiil consume messages for specified amount of time (in +seconds), to run indefinitely skip this parameter. Following command will be consuming messages for one hour: ```bash php index.php rabbitmq:consumer testConsumer 3600 @@ -246,7 +252,6 @@ Following command will be consuming messages indefinitely: php index.php rabbitmq:consumer testConsumer ``` - `rabbitmq:staticConsumer` will consume particular amount of messages. Following example will consume just 20 messages: ```bash diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..559fa33 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,16 @@ +# EditorConfig is awesome: http://EditorConfig.org + +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +indent_style = tab +indent_size = tab +tab_width = 4 + +[{*.yml,*.md}] +indent_style = space +indent_size = 2 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..12910b6 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,11 @@ +# Not archived +.docs export-ignore +tests export-ignore +.editorconfig export-ignore +.gitattributes export-ignore +.gitignore export-ignore +.travis.yml export-ignore +Makefile export-ignore +phpstan.neon export-ignore +README.md export-ignore +ruleset.xml export-ignore diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..f3fa11b --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,2 @@ +github: [f3l1x] +custom: ["https://contributte.org/partners.html"] diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index bb5cadb..a5708ac 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -1,11 +1,19 @@ name: "build" -on: [push, pull_request] +on: + pull_request: + paths-ignore: + - ".docs/**" + push: + branches: + - "master" + schedule: + - cron: "0 8 * * 1" # At 08:00 on Monday env: extensions: "json" cache-version: "1" - composer-version: "v1" + composer-version: "v2" composer-install: "composer update --no-interaction --no-progress --no-suggest --prefer-dist --prefer-stable" jobs: @@ -128,6 +136,15 @@ jobs: name: "Tests" runs-on: "${{ matrix.operating-system }}" + services: + mysql: + image: mysql:5.7 + env: + MYSQL_ALLOW_EMPTY_PASSWORD: yes + MYSQL_DATABASE: tests + ports: + - 3306:3306 + strategy: matrix: php-version: ["7.4", "8.0"] @@ -137,9 +154,6 @@ jobs: - php-version: "7.4" operating-system: "ubuntu-latest" composer-args: "--prefer-lowest" - - php-version: "8.0" - operating-system: "ubuntu-latest" - composer-args: "" fail-fast: false continue-on-error: "${{ matrix.php-version == '8.0' }}" @@ -192,3 +206,79 @@ jobs: - name: "Tests" run: "make tests" + + tests-code-coverage: + name: "Tests with code coverage" + runs-on: "${{ matrix.operating-system }}" + + services: + mysql: + image: mysql:5.7 + env: + MYSQL_ALLOW_EMPTY_PASSWORD: yes + MYSQL_DATABASE: tests + ports: + - 3306:3306 + + strategy: + matrix: + php-version: ["7.4"] + operating-system: ["ubuntu-latest"] + fail-fast: false + + if: "github.event_name == 'push'" + + steps: + - name: "Checkout" + uses: "actions/checkout@v2" + + - name: "Setup PHP cache environment" + id: "extcache" + uses: "shivammathur/cache-extensions@v1" + with: + php-version: "${{ matrix.php-version }}" + extensions: "${{ env.extensions }}" + key: "${{ env.cache-version }}" + + - name: "Cache PHP extensions" + uses: "actions/cache@v2" + with: + path: "${{ steps.extcache.outputs.dir }}" + key: "${{ steps.extcache.outputs.key }}" + restore-keys: "${{ steps.extcache.outputs.key }}" + + - name: "Install PHP" + uses: "shivammathur/setup-php@v2" + with: + php-version: "${{ matrix.php-version }}" + extensions: "${{ env.extensions }}" + tools: "composer:${{ env.composer-version }} " + + - name: "Setup problem matchers for PHP" + run: 'echo "::add-matcher::${{ runner.tool_cache }}/php.json"' + + - name: "Get Composer cache directory" + id: "composercache" + run: 'echo "::set-output name=dir::$(composer config cache-files-dir)"' + + - name: "Cache PHP dependencies" + uses: "actions/cache@v2" + with: + path: "${{ steps.composercache.outputs.dir }}" + key: "${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}" + restore-keys: "${{ runner.os }}-composer-" + + - name: "Install dependencies" + run: "${{ env.composer-install }}" + + - name: "Tests" + run: "make coverage-clover" + + - name: "Coveralls.io" + env: + CI_NAME: github + CI: true + COVERALLS_REPO_TOKEN: "${{ secrets.GITHUB_TOKEN }}" + run: | + wget https://github.com/php-coveralls/php-coveralls/releases/download/v2.1.0/php-coveralls.phar + php php-coveralls.phar --verbose --config tests/.coveralls.yml diff --git a/.gitignore b/.gitignore index b0e5cb2..a379b0b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,9 @@ -.idea -composer.lock +# Editor +/.idea + +# Tests +/tests/temp + +# Composer /vendor -/tests/Cases/output +/composer.lock diff --git a/Makefile b/Makefile index efffefd..c3200d9 100644 --- a/Makefile +++ b/Makefile @@ -16,3 +16,9 @@ phpstan: tests: vendor/bin/tester -s -p php --colors 1 -C tests/Cases + +coverage-clover: + vendor/bin/tester -s -p phpdbg --colors 1 -C --coverage ./coverage.xml --coverage-src ./src ./tests/Cases + +coverage-html: + vendor/bin/tester -s -p phpdbg --colors 1 -C --coverage ./coverage.html --coverage-src ./src ./tests/Cases diff --git a/composer.json b/composer.json index edf8548..e93b690 100644 --- a/composer.json +++ b/composer.json @@ -27,19 +27,21 @@ "php": ">=7.4", "bunny/bunny": "^0.2.4 || ^0.3 || ^0.4 || ^0.5", "symfony/console": "~3.3 || ^4.0 || ^5.0", - "nette/di": "^3.0", - "nette/utils": "^3.2" + "nette/di": "^2.4.17 || ^3.0.7", + "nette/utils": "^2.5.4 || ^3.2.0" }, "require-dev": { - "nette/tester": "^2.3.1", - "nette/neon": "^3.0", - "mockery/mockery": "^1.4", + "ninjify/nunjuck": "^0.4", + "nette/neon": "^2.4.3 || ^3.2.1", + "mockery/mockery": "^1.3.3", "gamee/php-code-checker-rules": "^2.0", "tracy/tracy": "^2.5" }, "suggest": { "tracy/tracy": "Allows using tracy bar panel" }, + "minimum-stability": "dev", + "prefer-stable": true, "extra": { "branch-alias": { "dev-master": "9.0.x-dev" diff --git a/src/DI/Helpers/AbstractHelper.php b/src/DI/Helpers/AbstractHelper.php index 77891bc..9b2eb78 100644 --- a/src/DI/Helpers/AbstractHelper.php +++ b/src/DI/Helpers/AbstractHelper.php @@ -4,7 +4,7 @@ namespace Contributte\RabbitMQ\DI\Helpers; -use Contributte\RabbitMQ\DI\RabbitMQExtension; +use Nette\DI\CompilerExtension; abstract class AbstractHelper { @@ -13,10 +13,10 @@ abstract class AbstractHelper * @var array */ protected array $defaults = []; - protected RabbitMQExtension $extension; + protected CompilerExtension $extension; - public function __construct(RabbitMQExtension $extension) + public function __construct(CompilerExtension $extension) { $this->extension = $extension; } diff --git a/src/DI/RabbitMQExtension24.php b/src/DI/RabbitMQExtension24.php new file mode 100644 index 0000000..d4cb906 --- /dev/null +++ b/src/DI/RabbitMQExtension24.php @@ -0,0 +1,107 @@ + [], + 'queues' => [], + 'exchanges' => [], + 'producers' => [], + 'consumers' => [], + ]; + private ConnectionsHelper $connectionsHelper; + private QueuesHelper $queuesHelper; + private ProducersHelper $producersHelper; + private ExchangesHelper $exchangesHelper; + private ConsumersHelper $consumersHelper; + + + public function __construct() + { + $this->connectionsHelper = new ConnectionsHelper($this); + $this->queuesHelper = new QueuesHelper($this); + $this->exchangesHelper = new ExchangesHelper($this); + $this->producersHelper = new ProducersHelper($this); + $this->consumersHelper = new ConsumersHelper($this); + } + + + /** + * @throws \InvalidArgumentException + */ + public function loadConfiguration(): void + { + $config = $this->validateConfig($this->defaults); + $builder = $this->getContainerBuilder(); + + /** + * Connections + */ + $this->connectionsHelper->setup($builder, $config['connections']); + + /** + * Queues + */ + $this->queuesHelper->setup($builder, $config['queues']); + + /** + * Exchanges + */ + $this->exchangesHelper->setup($builder, $config['exchanges']); + + /** + * Producers + */ + $this->producersHelper->setup($builder, $config['producers']); + + /** + * Consumers + */ + $this->consumersHelper->setup($builder, $config['consumers']); + + /** + * Register Client class + */ + $builder->addDefinition($this->prefix('client')) + ->setFactory(Client::class); + + $this->setupConsoleCommand(); + } + + + public function setupConsoleCommand(): void + { + $builder = $this->getContainerBuilder(); + + $builder->addDefinition($this->prefix('console.consumerCommand')) + ->setFactory(ConsumerCommand::class) + ->setTags(['console.command' => 'rabbitmq:consumer']); + + $builder->addDefinition($this->prefix('console.staticConsumerCommand')) + ->setFactory(StaticConsumerCommand::class) + ->setTags(['console.command' => 'rabbitmq:staticConsumer']); + + $builder->addDefinition($this->prefix('console.declareQueuesExchangesCommand')) + ->setFactory(DeclareQueuesAndExchangesCommand::class) + ->setTags(['console.command' => 'rabbitmq:declareQueuesAndExchanges']); + } +} diff --git a/tests/.coveralls.yml b/tests/.coveralls.yml new file mode 100644 index 0000000..8450382 --- /dev/null +++ b/tests/.coveralls.yml @@ -0,0 +1,4 @@ +# for php-coveralls +service_name: github-actions +coverage_clover: coverage.xml +json_path: coverage.json diff --git a/tests/.gitignore b/tests/.gitignore new file mode 100644 index 0000000..e3886ad --- /dev/null +++ b/tests/.gitignore @@ -0,0 +1,3 @@ +output +/test.log +/tmp diff --git a/tests/Cases/DI/RabbitMQExtension24Test.phpt b/tests/Cases/DI/RabbitMQExtension24Test.phpt new file mode 100644 index 0000000..0c00a40 --- /dev/null +++ b/tests/Cases/DI/RabbitMQExtension24Test.phpt @@ -0,0 +1,48 @@ +load(function (Compiler $compiler): void { + $compiler->addExtension('rabbitmq', new RabbitMQExtension24()); + $compiler->addConfig(NeonLoader::load(' + rabbitmq: + connections: + default: + user: guest + password: guest + host: localhost + port: 5672 + lazy: false + ')); + $compiler->addDependencies([__FILE__]); + }, __METHOD__); + + /** @var Container $container */ + $container = new $class(); + + Assert::type(ConnectionFactory::class, $container->getByType(ConnectionFactory::class)); + } + +} + +(new RabbitMQExtension24Test())->run(); diff --git a/tests/Cases/DI/RabbitMQExtensionTest.phpt b/tests/Cases/DI/RabbitMQExtensionTest.phpt new file mode 100644 index 0000000..378b5d2 --- /dev/null +++ b/tests/Cases/DI/RabbitMQExtensionTest.phpt @@ -0,0 +1,48 @@ +load(function (Compiler $compiler): void { + $compiler->addExtension('rabbitmq', new RabbitMQExtension24()); + $compiler->addConfig(NeonLoader::load(' + rabbitmq: + connections: + default: + user: guest + password: guest + host: localhost + port: 5672 + lazy: false + ')); + $compiler->addDependencies([__FILE__]); + }, __METHOD__); + + /** @var Container $container */ + $container = new $class(); + + Assert::type(ConnectionFactory::class, $container->getByType(ConnectionFactory::class)); + } + +} + +(new RabbitMQExtensionTest())->run(); diff --git a/tests/Cases/FooTest.php b/tests/Cases/FooTest.php deleted file mode 100644 index bc6bb59..0000000 --- a/tests/Cases/FooTest.php +++ /dev/null @@ -1,21 +0,0 @@ -run(); diff --git a/tests/Cases/ProducerTest.phpt b/tests/Cases/ProducerTest.phpt index 5f1b16b..9f486b6 100644 --- a/tests/Cases/ProducerTest.phpt +++ b/tests/Cases/ProducerTest.phpt @@ -295,13 +295,11 @@ final class ProducerTest extends TestCase $channelMock = new ChannelMock(); $connectionMock = \Mockery::mock(IConnection::class) - ->shouldReceive('getChannel')->andReturn($channelMock)->getMock() - ; + ->shouldReceive('getChannel')->andReturn($channelMock)->getMock(); $queueMock = \Mockery::mock(IQueue::class) ->shouldReceive('getConnection')->andReturn($connectionMock)->getMock() - ->shouldReceive('getName')->andReturn($testQueueName)->getMock() - ; + ->shouldReceive('getName')->andReturn($testQueueName)->getMock(); $producer = new Producer( null, @@ -319,13 +317,11 @@ final class ProducerTest extends TestCase $channelMock = new ChannelMock(); $connectionMock = \Mockery::mock(IConnection::class) - ->shouldReceive('getChannel')->andReturn($channelMock)->getMock() - ; + ->shouldReceive('getChannel')->andReturn($channelMock)->getMock(); $exchangeMock = \Mockery::mock(IExchange::class) ->shouldReceive('getConnection')->andReturn($connectionMock)->getMock() - ->shouldReceive('getName')->andReturn($testExchange)->getMock() - ; + ->shouldReceive('getName')->andReturn($testExchange)->getMock(); $producer = new Producer( $exchangeMock, diff --git a/tests/Toolkit/NeonLoader.php b/tests/Toolkit/NeonLoader.php new file mode 100644 index 0000000..2fb593e --- /dev/null +++ b/tests/Toolkit/NeonLoader.php @@ -0,0 +1,21 @@ +process((array) Neon::decode($str)); + } + +} diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 719bc9f..f9ee4bc 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -1,12 +1,13 @@ -