From 7c26c4eecc7342d29b590e3ca0a7db8e21544514 Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Thu, 10 May 2018 15:04:45 +0200 Subject: [PATCH 01/62] Update dependencies --- composer.lock | 203 ++++++++++++++++++++++++++------------------------ 1 file changed, 104 insertions(+), 99 deletions(-) diff --git a/composer.lock b/composer.lock index 92022b8..cee5dd1 100644 --- a/composer.lock +++ b/composer.lock @@ -1,10 +1,10 @@ { "_readme": [ "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "26ed0ae9d781d9252a4493088431d563", + "content-hash": "9f3aaee9fe35df3d98eb71a8f1c29140", "packages": [ { "name": "icehawk/icehawk", @@ -77,16 +77,16 @@ }, { "name": "symfony/polyfill-mbstring", - "version": "v1.7.0", + "version": "v1.8.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "78be803ce01e55d3491c1397cf1c64beb9c1b63b" + "reference": "3296adf6a6454a050679cde90f95350ad604b171" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/78be803ce01e55d3491c1397cf1c64beb9c1b63b", - "reference": "78be803ce01e55d3491c1397cf1c64beb9c1b63b", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/3296adf6a6454a050679cde90f95350ad604b171", + "reference": "3296adf6a6454a050679cde90f95350ad604b171", "shasum": "" }, "require": { @@ -98,7 +98,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.7-dev" + "dev-master": "1.8-dev" } }, "autoload": { @@ -132,20 +132,20 @@ "portable", "shim" ], - "time": "2018-01-30T19:27:44+00:00" + "time": "2018-04-26T10:06:28+00:00" }, { "name": "twig/twig", - "version": "v2.4.7", + "version": "v2.4.8", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "69aacd44dbbaa3199d5afb68605c996d577896fc" + "reference": "7b604c89da162034bdf4bb66310f358d313dd16d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/69aacd44dbbaa3199d5afb68605c996d577896fc", - "reference": "69aacd44dbbaa3199d5afb68605c996d577896fc", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/7b604c89da162034bdf4bb66310f358d313dd16d", + "reference": "7b604c89da162034bdf4bb66310f358d313dd16d", "shasum": "" }, "require": { @@ -198,7 +198,7 @@ "keywords": [ "templating" ], - "time": "2018-03-20T04:31:17+00:00" + "time": "2018-04-02T09:24:19+00:00" } ], "packages-dev": [ @@ -208,17 +208,17 @@ "source": { "type": "git", "url": "https://github.com/Roave/SecurityAdvisories.git", - "reference": "4a272b63452f4d8ff0d48a759e59db9359a9b5b7" + "reference": "bc0c7361e137f205dc5775583701743b5c4edcda" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/4a272b63452f4d8ff0d48a759e59db9359a9b5b7", - "reference": "4a272b63452f4d8ff0d48a759e59db9359a9b5b7", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/bc0c7361e137f205dc5775583701743b5c4edcda", + "reference": "bc0c7361e137f205dc5775583701743b5c4edcda", "shasum": "" }, "conflict": { "3f/pygmentize": "<1.2", - "adodb/adodb-php": "<5.20.6", + "adodb/adodb-php": "<5.20.12", "amphp/artax": "<1.0.6|>=2,<2.0.6", "amphp/http": "<1.0.1", "asymmetricrypt/asymmetricrypt": ">=0,<9.9.99", @@ -230,8 +230,8 @@ "codeigniter/framework": "<=3.0.6", "composer/composer": "<=1.0.0-alpha11", "contao-components/mediaelement": ">=2.14.2,<2.21.1", - "contao/core": ">=2,<3.5.32", - "contao/core-bundle": ">=4,<4.4.8", + "contao/core": ">=2,<3.5.35", + "contao/core-bundle": ">=4,<4.4.18|>=4.5,<4.5.8", "contao/listing-bundle": ">=4,<4.4.8", "contao/newsletter-bundle": ">=4,<4.1", "doctrine/annotations": ">=1,<1.2.7", @@ -244,20 +244,23 @@ "doctrine/mongodb-odm-bundle": ">=2,<3.0.1", "doctrine/orm": ">=2,<2.4.8|>=2.5,<2.5.1", "dompdf/dompdf": ">=0.6,<0.6.2", - "drupal/core": ">=8,<8.4.5", - "drupal/drupal": ">=8,<8.4.5", + "drupal/core": ">=7,<7.59|>=8,<8.4.8|>=8.5,<8.5.3", + "drupal/drupal": ">=7,<7.59|>=8,<8.4.8|>=8.5,<8.5.3", "erusev/parsedown": "<1.7", "ezsystems/ezpublish-legacy": ">=5.3,<5.3.12.3|>=5.4,<5.4.11.3|>=2017.8,<2017.8.1.1|>=2017.12,<2017.12.2.1", "firebase/php-jwt": "<2", "friendsofsymfony/rest-bundle": ">=1.2,<1.2.2", "friendsofsymfony/user-bundle": ">=1.2,<1.3.5", + "fuel/core": "<=1.8", "gree/jose": "<=2.2", "gregwar/rst": "<1.0.3", "guzzlehttp/guzzle": ">=6,<6.2.1|>=4.0.0-rc2,<4.2.4|>=5,<5.3.1", - "illuminate/auth": ">=4,<4.0.99|>=4.1,<4.1.26", + "illuminate/auth": ">=4,<4.0.99|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.10", "illuminate/database": ">=4,<4.0.99|>=4.1,<4.1.29", + "illuminate/encryption": ">=4,<=4.0.11|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.40|>=5.6,<5.6.15", "joomla/session": "<1.3.1", - "laravel/framework": ">=4,<4.0.99|>=4.1,<4.1.29", + "kreait/firebase-php": ">=3.2,<3.8.1", + "laravel/framework": ">=4,<4.0.99|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.40|>=5.6,<5.6.15", "laravel/socialite": ">=1,<1.0.99|>=2,<2.0.10", "magento/magento1ce": ">=1.5.0.1,<1.9.3.2", "magento/magento1ee": ">=1.9,<1.14.3.2", @@ -270,6 +273,7 @@ "padraic/humbug_get_contents": "<1.1.2", "pagarme/pagarme-php": ">=0,<3", "paragonie/random_compat": "<2", + "paypal/merchant-sdk-php": "<3.12", "phpmailer/phpmailer": ">=5,<5.2.24", "phpunit/phpunit": ">=4.8.19,<4.8.28|>=5.0.10,<5.6.3", "phpxmlrpc/extras": "<0.6.1", @@ -285,6 +289,7 @@ "simplesamlphp/saml2": "<1.10.6|>=2,<2.3.8|>=3,<3.1.4", "simplesamlphp/simplesamlphp": "<1.15.2", "simplesamlphp/simplesamlphp-module-infocard": "<1.0.1", + "slim/slim": "<2.6", "socalnick/scn-social-auth": "<1.15.2", "squizlabs/php_codesniffer": ">=1,<2.8.1|>=3,<3.0.1", "stormpath/sdk": ">=0,<9.9.99", @@ -359,87 +364,87 @@ } ], "description": "Prevents installation of composer packages with known security vulnerabilities: no API, simply require it", - "time": "2018-03-22T21:16:54+00:00" + "time": "2018-05-09T13:33:28+00:00" }, - { - "name": "tm/tooly-composer-script", - "version": "1.2.6", - "source": { - "type": "git", - "url": "https://github.com/tommy-muehle/tooly-composer-script.git", - "reference": "53cc0551122145b4ae4c523e64f3628329ef8ea5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/tommy-muehle/tooly-composer-script/zipball/53cc0551122145b4ae4c523e64f3628329ef8ea5", - "reference": "53cc0551122145b4ae4c523e64f3628329ef8ea5", - "shasum": "" - }, - "require": { - "php": ">=5.6.0" - }, - "require-dev": { - "codeclimate/php-test-reporter": "dev-master", - "composer/composer": "1.*", - "mikey179/vfsstream": "1.6.*", - "php-mock/php-mock-phpunit": "1.1.*", - "symfony/console": "^3.0", - "tm/gpg-verifier": "1.*" - }, - "suggest": { - "tm/gpg-verifier": "Allows verification over GPG for PHAR tools." - }, - "type": "library", - "extra": { - "tools": { - "phpunit": { - "url": "https://phar.phpunit.de/phpunit-5.5.0.phar" + { + "name": "tm/tooly-composer-script", + "version": "1.2.6", + "source": { + "type": "git", + "url": "https://github.com/tommy-muehle/tooly-composer-script.git", + "reference": "53cc0551122145b4ae4c523e64f3628329ef8ea5" }, - "phpcpd": { - "url": "https://phar.phpunit.de/phpcpd-2.0.4.phar" + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/tommy-muehle/tooly-composer-script/zipball/53cc0551122145b4ae4c523e64f3628329ef8ea5", + "reference": "53cc0551122145b4ae4c523e64f3628329ef8ea5", + "shasum": "" }, - "phpcs": { - "url": "https://squizlabs.github.io/PHP_CodeSniffer/phpcs.phar", - "force-replace": true + "require": { + "php": ">=5.6.0" }, - "phpmd": { - "url": "http://static.phpmd.org/php/latest/phpmd.phar", - "force-replace": true + "require-dev": { + "codeclimate/php-test-reporter": "dev-master", + "composer/composer": "1.*", + "mikey179/vfsstream": "1.6.*", + "php-mock/php-mock-phpunit": "1.1.*", + "symfony/console": "^3.0", + "tm/gpg-verifier": "1.*" }, - "security-checker": { - "url": "http://get.sensiolabs.org/security-checker.phar", - "force-replace": true - } - } - }, - "autoload": { - "psr-4": { - "Tooly\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Tommy Muehle", - "email": "tommy.muehle@gmail.com", - "homepage": "https://tommy-muehle.de" - } - ], - "description": "Simple composer script to manage phar files.", - "homepage": "https://github.com/tommy-muehle/tooly-composer-script", - "keywords": [ - "composer", - "composer-phar", - "composer-script", - "gpg-verification", - "phar", - "phar-handling", - "phar-management" - ], - "time": "2018-03-20T21:26:34+00:00" + "suggest": { + "tm/gpg-verifier": "Allows verification over GPG for PHAR tools." + }, + "type": "library", + "extra": { + "tools": { + "phpunit": { + "url": "https://phar.phpunit.de/phpunit-5.5.0.phar" + }, + "phpcpd": { + "url": "https://phar.phpunit.de/phpcpd-2.0.4.phar" + }, + "phpcs": { + "url": "https://squizlabs.github.io/PHP_CodeSniffer/phpcs.phar", + "force-replace": true + }, + "phpmd": { + "url": "http://static.phpmd.org/php/latest/phpmd.phar", + "force-replace": true + }, + "security-checker": { + "url": "http://get.sensiolabs.org/security-checker.phar", + "force-replace": true + } + } + }, + "autoload": { + "psr-4": { + "Tooly\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Tommy Muehle", + "email": "tommy.muehle@gmail.com", + "homepage": "https://tommy-muehle.de" + } + ], + "description": "Simple composer script to manage phar files.", + "homepage": "https://github.com/tommy-muehle/tooly-composer-script", + "keywords": [ + "composer", + "composer-phar", + "composer-script", + "gpg-verification", + "phar", + "phar-handling", + "phar-management" + ], + "time": "2018-03-20T21:26:34+00:00" } ], "aliases": [], From d6a0f20fefb625dab41b8d2b16edb883d044553f Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Fri, 11 May 2018 14:24:01 +0200 Subject: [PATCH 02/62] Update CI settings for faster build --- .travis.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.travis.yml b/.travis.yml index a4ba757..d0054c3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,13 @@ php: - 7.2 - 7.2snapshot +git: + depth: 5 + +cache: + directories: + - $HOME/.composer/cache/files + branches: only: - master From c8893ac2d5682ceee35a1fb31ec3ba6ac91a538d Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Fri, 11 May 2018 14:41:16 +0200 Subject: [PATCH 03/62] Throw exception, if servers config file was not found --- src/Exceptions/ServersConfigNotFound.php | 8 ++++++++ .../Configs/ServerConfigList.php | 16 +++++++++++++++- .../Configs/ServerConfigListTest.php | 18 +++++++++++++++++- 3 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 src/Exceptions/ServersConfigNotFound.php diff --git a/src/Exceptions/ServersConfigNotFound.php b/src/Exceptions/ServersConfigNotFound.php new file mode 100644 index 0000000..67c7bb2 --- /dev/null +++ b/src/Exceptions/ServersConfigNotFound.php @@ -0,0 +1,8 @@ +loadServerConfigs( $data ); } - public static function fromConfigFile() : self + /** + * @param null|string $configFile + * + * @throws ServersConfigNotFound + * @return ServerConfigList + */ + public static function fromConfigFile( ?string $configFile = null ) : self { + $serversConfigFile = $configFile ?? dirname( __DIR__, 3 ) . '/config/servers.php'; + if ( !file_exists( $serversConfigFile ) ) + { + throw new ServersConfigNotFound( 'Could not find servers config at ' . $serversConfigFile ); + } + return new self( (array)include __DIR__ . '/../../../config/servers.php' ); } diff --git a/tests/Unit/Infrastructure/Configs/ServerConfigListTest.php b/tests/Unit/Infrastructure/Configs/ServerConfigListTest.php index 6d41ece..3ea4aca 100644 --- a/tests/Unit/Infrastructure/Configs/ServerConfigListTest.php +++ b/tests/Unit/Infrastructure/Configs/ServerConfigListTest.php @@ -3,6 +3,7 @@ namespace hollodotme\Readis\Tests\Unit\Infrastructure\Configs; use hollodotme\Readis\Exceptions\ServerConfigNotFound; +use hollodotme\Readis\Exceptions\ServersConfigNotFound; use hollodotme\Readis\Infrastructure\Configs\ServerConfigList; use hollodotme\Readis\Infrastructure\Interfaces\ProvidesServerConfig; use PHPUnit\Framework\ExpectationFailedException; @@ -22,7 +23,11 @@ final class ServerConfigListTest extends TestCase * * @dataProvider serverConfigsProvider */ - public function testCanGetServerConfigForKey( array $serverConfigs, string $serverKey, array $expectedServerConfig ) : void + public function testCanGetServerConfigForKey( + array $serverConfigs, + string $serverKey, + array $expectedServerConfig + ) : void { $serverConfigList = new ServerConfigList( $serverConfigs ); $serverConfig = $serverConfigList->getServerConfig( $serverKey ); @@ -104,4 +109,15 @@ public function testThrowsExceptionForUnknownServerKey( array $serverConfigs ) : $serverConfigList->getServerConfig( '3' ); } + + /** + * @throws ServersConfigNotFound + */ + public function testThrowsExceptionIfServersConfigFileNotFound() : void + { + $this->expectException( ServersConfigNotFound::class ); + $this->expectExceptionMessage( 'Could not find servers config at /path/to/servers.php' ); + + ServerConfigList::fromConfigFile( '/path/to/servers.php' ); + } } From 421cd1cf3ddde6995e00d2f5f29883f8b560a7f9 Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Fri, 11 May 2018 14:48:08 +0200 Subject: [PATCH 04/62] Fix finally included file --- src/Infrastructure/Configs/ServerConfigList.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Infrastructure/Configs/ServerConfigList.php b/src/Infrastructure/Configs/ServerConfigList.php index 10de2a9..40a57a3 100644 --- a/src/Infrastructure/Configs/ServerConfigList.php +++ b/src/Infrastructure/Configs/ServerConfigList.php @@ -32,7 +32,8 @@ public static function fromConfigFile( ?string $configFile = null ) : self throw new ServersConfigNotFound( 'Could not find servers config at ' . $serversConfigFile ); } - return new self( (array)include __DIR__ . '/../../../config/servers.php' ); + /** @noinspection PhpIncludeInspection */ + return new self( (array)require $serversConfigFile ); } private function loadServerConfigs( array $serverConfigList ) : void From b20ffab7eec8a46ee2e4d83fcb386d18c7624a28 Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Fri, 11 May 2018 14:48:43 +0200 Subject: [PATCH 05/62] Throw exception if app config file not found --- src/Application/Configs/AppConfig.php | 20 +++++++++++++++++-- src/Exceptions/ApplicationConfigNotFound.php | 8 ++++++++ .../Application/Configs/AppConfigTest.php | 12 +++++++++++ 3 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 src/Exceptions/ApplicationConfigNotFound.php diff --git a/src/Application/Configs/AppConfig.php b/src/Application/Configs/AppConfig.php index 60bdf15..c65e207 100644 --- a/src/Application/Configs/AppConfig.php +++ b/src/Application/Configs/AppConfig.php @@ -2,6 +2,8 @@ namespace hollodotme\Readis\Application\Configs; +use hollodotme\Readis\Exceptions\ApplicationConfigNotFound; +use function file_exists; use const PHP_URL_PATH; final class AppConfig @@ -14,9 +16,23 @@ public function __construct( array $data ) $this->configData = $data; } - public static function fromConfigFile() : self + /** + * @param null|string $configFile + * + * @throws ApplicationConfigNotFound + * @return AppConfig + */ + public static function fromConfigFile( ?string $configFile = null ) : self { - return new self( (array)include __DIR__ . '/../../../config/app.php' ); + $appConfigFile = $configFile ?? dirname( __DIR__, 3 ) . '/config/app.php'; + + if ( !file_exists( $appConfigFile ) ) + { + throw new ApplicationConfigNotFound( 'Could not find application config at ' . $appConfigFile ); + } + + /** @noinspection PhpIncludeInspection */ + return new self( (array)require $appConfigFile ); } public function getBaseUrl() : string diff --git a/src/Exceptions/ApplicationConfigNotFound.php b/src/Exceptions/ApplicationConfigNotFound.php new file mode 100644 index 0000000..5739374 --- /dev/null +++ b/src/Exceptions/ApplicationConfigNotFound.php @@ -0,0 +1,8 @@ +expectException( ApplicationConfigNotFound::class ); + $this->expectExceptionMessage( 'Could not find application config at /path/to/app.php' ); + + AppConfig::fromConfigFile( '/path/to/app.php' ); + } } From ca55686fcb7904ae7fffae468ec09c314e11c9ec Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Fri, 11 May 2018 14:58:42 +0200 Subject: [PATCH 06/62] Throw exception if no servers were configured --- src/Exceptions/NoServersConfigured.php | 8 ++++++++ src/Infrastructure/Configs/ServerConfigList.php | 7 +++++++ 2 files changed, 15 insertions(+) create mode 100644 src/Exceptions/NoServersConfigured.php diff --git a/src/Exceptions/NoServersConfigured.php b/src/Exceptions/NoServersConfigured.php new file mode 100644 index 0000000..62f6ee3 --- /dev/null +++ b/src/Exceptions/NoServersConfigured.php @@ -0,0 +1,8 @@ +servers ) ) + { + throw new NoServersConfigured( 'No servers were configured.' ); + } + return $this->servers; } From f55ea90ca3414130268303764b12fcc3b4d29988 Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Fri, 11 May 2018 14:59:40 +0200 Subject: [PATCH 07/62] Add final read responder with proper error handling --- src/Application/Configs/IceHawkConfig.php | 9 +++++-- .../FinalResponders/FinalReadResponder.php | 27 +++++++++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 src/Application/FinalResponders/FinalReadResponder.php diff --git a/src/Application/Configs/IceHawkConfig.php b/src/Application/Configs/IceHawkConfig.php index ecc4adb..2d6bfa5 100644 --- a/src/Application/Configs/IceHawkConfig.php +++ b/src/Application/Configs/IceHawkConfig.php @@ -2,6 +2,7 @@ namespace hollodotme\Readis\Application\Configs; +use hollodotme\Readis\Application\FinalResponders\FinalReadResponder; use hollodotme\Readis\Application\Web\Server\Read\AjaxKeyDetailsRequestHandler; use hollodotme\Readis\Application\Web\Server\Read\AjaxSearchKeysRequestHandler; use hollodotme\Readis\Application\Web\Server\Read\ServerDetailsRequestHandler; @@ -10,12 +11,12 @@ use hollodotme\Readis\Traits\EnvInjecting; use IceHawk\IceHawk\Defaults\Traits\DefaultCookieProviding; use IceHawk\IceHawk\Defaults\Traits\DefaultEventSubscribing; -use IceHawk\IceHawk\Defaults\Traits\DefaultFinalReadResponding; use IceHawk\IceHawk\Defaults\Traits\DefaultFinalWriteResponding; use IceHawk\IceHawk\Defaults\Traits\DefaultRequestBypassing; use IceHawk\IceHawk\Defaults\Traits\DefaultRequestInfoProviding; use IceHawk\IceHawk\Defaults\Traits\DefaultWriteRouting; use IceHawk\IceHawk\Interfaces\ConfiguresIceHawk; +use IceHawk\IceHawk\Interfaces\RespondsFinallyToReadRequest; use IceHawk\IceHawk\Routing\Patterns\NamedRegExp; use IceHawk\IceHawk\Routing\ReadRoute; use function preg_quote; @@ -25,7 +26,6 @@ final class IceHawkConfig implements ConfiguresIceHawk use EnvInjecting; use DefaultCookieProviding; use DefaultEventSubscribing; - use DefaultFinalReadResponding; use DefaultFinalWriteResponding; use DefaultRequestBypassing; use DefaultRequestInfoProviding; @@ -66,4 +66,9 @@ private function buildReadRoutes() : void '^' . $quotedBaseUri . '/server/(?\d+)/database/(?\d+)/keys/(?.+)/?$' => AjaxKeyDetailsRequestHandler::class, ]; } + + public function getFinalReadResponder() : RespondsFinallyToReadRequest + { + return new FinalReadResponder(); + } } diff --git a/src/Application/FinalResponders/FinalReadResponder.php b/src/Application/FinalResponders/FinalReadResponder.php new file mode 100644 index 0000000..8f79729 --- /dev/null +++ b/src/Application/FinalResponders/FinalReadResponder.php @@ -0,0 +1,27 @@ + $throwable->getMessage(), + ]; + + (new TwigPage())->respond( 'Theme/Error.twig', $data, HttpCode::INTERNAL_SERVER_ERROR ); + } +} \ No newline at end of file From f8ae02405d86bb71e60c19fa3a7a5cec91f9bcd0 Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Fri, 11 May 2018 15:02:34 +0200 Subject: [PATCH 08/62] Fix missing intialization of server config list --- src/Infrastructure/Configs/ServerConfigList.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Infrastructure/Configs/ServerConfigList.php b/src/Infrastructure/Configs/ServerConfigList.php index 874720c..26bbf06 100644 --- a/src/Infrastructure/Configs/ServerConfigList.php +++ b/src/Infrastructure/Configs/ServerConfigList.php @@ -39,6 +39,7 @@ public static function fromConfigFile( ?string $configFile = null ) : self private function loadServerConfigs( array $serverConfigList ) : void { + $this->servers = []; foreach ( $serverConfigList as $serverConfig ) { $this->servers[] = new ServerConfig( From 2bc9ef935b0b07594bc94625e2444c8c1e84289e Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Fri, 11 May 2018 16:48:45 +0200 Subject: [PATCH 09/62] Add redis configs and data directory --- .gitignore | 2 + .redis/data/.gitkeep | 1 + .redis/redis.default.conf | 1052 +++++++++++++++++++++++++++++++++++++ .redis/redis.test.conf | 1 + docker-compose.yml | 4 + 5 files changed, 1060 insertions(+) create mode 100644 .redis/data/.gitkeep create mode 100644 .redis/redis.default.conf create mode 100644 .redis/redis.test.conf diff --git a/.gitignore b/.gitignore index 21248a5..a04f976 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ # local configs /config/servers.php /config/app.php +/.redis/data/* +!.gitkeep \ No newline at end of file diff --git a/.redis/data/.gitkeep b/.redis/data/.gitkeep new file mode 100644 index 0000000..b01b3ce --- /dev/null +++ b/.redis/data/.gitkeep @@ -0,0 +1 @@ +# Keep this directory \ No newline at end of file diff --git a/.redis/redis.default.conf b/.redis/redis.default.conf new file mode 100644 index 0000000..0f4d7f9 --- /dev/null +++ b/.redis/redis.default.conf @@ -0,0 +1,1052 @@ +# Redis configuration file example. +# +# Note that in order to read the configuration file, Redis must be +# started with the file path as first argument: +# +# ./redis-server /path/to/redis.conf + +# Note on units: when memory size is needed, it is possible to specify +# it in the usual form of 1k 5GB 4M and so forth: +# +# 1k => 1000 bytes +# 1kb => 1024 bytes +# 1m => 1000000 bytes +# 1mb => 1024*1024 bytes +# 1g => 1000000000 bytes +# 1gb => 1024*1024*1024 bytes +# +# units are case insensitive so 1GB 1Gb 1gB are all the same. + +################################## INCLUDES ################################### + +# Include one or more other config files here. This is useful if you +# have a standard template that goes to all Redis servers but also need +# to customize a few per-server settings. Include files can include +# other files, so use this wisely. +# +# Notice option "include" won't be rewritten by command "CONFIG REWRITE" +# from admin or Redis Sentinel. Since Redis always uses the last processed +# line as value of a configuration directive, you'd better put includes +# at the beginning of this file to avoid overwriting config change at runtime. +# +# If instead you are interested in using includes to override configuration +# options, it is better to use include as the last line. +# +# include /path/to/local.conf +# include /path/to/other.conf + +################################## NETWORK ##################################### + +# By default, if no "bind" configuration directive is specified, Redis listens +# for connections from all the network interfaces available on the server. +# It is possible to listen to just one or multiple selected interfaces using +# the "bind" configuration directive, followed by one or more IP addresses. +# +# Examples: +# +# bind 192.168.1.100 10.0.0.1 +# bind 127.0.0.1 ::1 +# +# ~~~ WARNING ~~~ If the computer running Redis is directly exposed to the +# internet, binding to all the interfaces is dangerous and will expose the +# instance to everybody on the internet. So by default we uncomment the +# following bind directive, that will force Redis to listen only into +# the IPv4 lookback interface address (this means Redis will be able to +# accept connections only from clients running into the same computer it +# is running). +# +# IF YOU ARE SURE YOU WANT YOUR INSTANCE TO LISTEN TO ALL THE INTERFACES +# JUST COMMENT THE FOLLOWING LINE. +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +bind 0.0.0.0 + +# Protected mode is a layer of security protection, in order to avoid that +# Redis instances left open on the internet are accessed and exploited. +# +# When protected mode is on and if: +# +# 1) The server is not binding explicitly to a set of addresses using the +# "bind" directive. +# 2) No password is configured. +# +# The server only accepts connections from clients connecting from the +# IPv4 and IPv6 loopback addresses 127.0.0.1 and ::1, and from Unix domain +# sockets. +# +# By default protected mode is enabled. You should disable it only if +# you are sure you want clients from other hosts to connect to Redis +# even if no authentication is configured, nor a specific set of interfaces +# are explicitly listed using the "bind" directive. +protected-mode yes + +# Accept connections on the specified port, default is 6379 (IANA #815344). +# If port 0 is specified Redis will not listen on a TCP socket. +port 6379 + +# TCP listen() backlog. +# +# In high requests-per-second environments you need an high backlog in order +# to avoid slow clients connections issues. Note that the Linux kernel +# will silently truncate it to the value of /proc/sys/net/core/somaxconn so +# make sure to raise both the value of somaxconn and tcp_max_syn_backlog +# in order to get the desired effect. +tcp-backlog 511 + +# Unix socket. +# +# Specify the path for the Unix socket that will be used to listen for +# incoming connections. There is no default, so Redis will not listen +# on a unix socket when not specified. +# +# unixsocket /tmp/redis.sock +# unixsocketperm 700 + +# Close the connection after a client is idle for N seconds (0 to disable) +timeout 0 + +# TCP keepalive. +# +# If non-zero, use SO_KEEPALIVE to send TCP ACKs to clients in absence +# of communication. This is useful for two reasons: +# +# 1) Detect dead peers. +# 2) Take the connection alive from the point of view of network +# equipment in the middle. +# +# On Linux, the specified value (in seconds) is the period used to send ACKs. +# Note that to close the connection the double of the time is needed. +# On other kernels the period depends on the kernel configuration. +# +# A reasonable value for this option is 300 seconds, which is the new +# Redis default starting with Redis 3.2.1. +tcp-keepalive 300 + +################################# GENERAL ##################################### + +# By default Redis does not run as a daemon. Use 'yes' if you need it. +# Note that Redis will write a pid file in /var/run/redis.pid when daemonized. +daemonize no + +# If you run Redis from upstart or systemd, Redis can interact with your +# supervision tree. Options: +# supervised no - no supervision interaction +# supervised upstart - signal upstart by putting Redis into SIGSTOP mode +# supervised systemd - signal systemd by writing READY=1 to $NOTIFY_SOCKET +# supervised auto - detect upstart or systemd method based on +# UPSTART_JOB or NOTIFY_SOCKET environment variables +# Note: these supervision methods only signal "process is ready." +# They do not enable continuous liveness pings back to your supervisor. +supervised no + +# If a pid file is specified, Redis writes it where specified at startup +# and removes it at exit. +# +# When the server runs non daemonized, no pid file is created if none is +# specified in the configuration. When the server is daemonized, the pid file +# is used even if not specified, defaulting to "/var/run/redis.pid". +# +# Creating a pid file is best effort: if Redis is not able to create it +# nothing bad happens, the server will start and run normally. +pidfile /var/run/redis_6379.pid + +# Specify the server verbosity level. +# This can be one of: +# debug (a lot of information, useful for development/testing) +# verbose (many rarely useful info, but not a mess like the debug level) +# notice (moderately verbose, what you want in production probably) +# warning (only very important / critical messages are logged) +loglevel notice + +# Specify the log file name. Also the empty string can be used to force +# Redis to log on the standard output. Note that if you use standard +# output for logging but daemonize, logs will be sent to /dev/null +logfile "" + +# To enable logging to the system logger, just set 'syslog-enabled' to yes, +# and optionally update the other syslog parameters to suit your needs. +# syslog-enabled no + +# Specify the syslog identity. +# syslog-ident redis + +# Specify the syslog facility. Must be USER or between LOCAL0-LOCAL7. +# syslog-facility local0 + +# Set the number of databases. The default database is DB 0, you can select +# a different one on a per-connection basis using SELECT where +# dbid is a number between 0 and 'databases'-1 +databases 16 + +################################ SNAPSHOTTING ################################ +# +# Save the DB on disk: +# +# save +# +# Will save the DB if both the given number of seconds and the given +# number of write operations against the DB occurred. +# +# In the example below the behaviour will be to save: +# after 900 sec (15 min) if at least 1 key changed +# after 300 sec (5 min) if at least 10 keys changed +# after 60 sec if at least 10000 keys changed +# +# Note: you can disable saving completely by commenting out all "save" lines. +# +# It is also possible to remove all the previously configured save +# points by adding a save directive with a single empty string argument +# like in the following example: +# +# save "" + +save 900 1 +save 300 10 +save 60 10000 + +# By default Redis will stop accepting writes if RDB snapshots are enabled +# (at least one save point) and the latest background save failed. +# This will make the user aware (in a hard way) that data is not persisting +# on disk properly, otherwise chances are that no one will notice and some +# disaster will happen. +# +# If the background saving process will start working again Redis will +# automatically allow writes again. +# +# However if you have setup your proper monitoring of the Redis server +# and persistence, you may want to disable this feature so that Redis will +# continue to work as usual even if there are problems with disk, +# permissions, and so forth. +stop-writes-on-bgsave-error yes + +# Compress string objects using LZF when dump .rdb databases? +# For default that's set to 'yes' as it's almost always a win. +# If you want to save some CPU in the saving child set it to 'no' but +# the dataset will likely be bigger if you have compressible values or keys. +rdbcompression yes + +# Since version 5 of RDB a CRC64 checksum is placed at the end of the file. +# This makes the format more resistant to corruption but there is a performance +# hit to pay (around 10%) when saving and loading RDB files, so you can disable it +# for maximum performances. +# +# RDB files created with checksum disabled have a checksum of zero that will +# tell the loading code to skip the check. +rdbchecksum yes + +# The filename where to dump the DB +dbfilename dump.rdb + +# The working directory. +# +# The DB will be written inside this directory, with the filename specified +# above using the 'dbfilename' configuration directive. +# +# The Append Only File will also be created inside this directory. +# +# Note that you must specify a directory here, not a file name. +dir ./ + +################################# REPLICATION ################################# + +# Master-Slave replication. Use slaveof to make a Redis instance a copy of +# another Redis server. A few things to understand ASAP about Redis replication. +# +# 1) Redis replication is asynchronous, but you can configure a master to +# stop accepting writes if it appears to be not connected with at least +# a given number of slaves. +# 2) Redis slaves are able to perform a partial resynchronization with the +# master if the replication link is lost for a relatively small amount of +# time. You may want to configure the replication backlog size (see the next +# sections of this file) with a sensible value depending on your needs. +# 3) Replication is automatic and does not need user intervention. After a +# network partition slaves automatically try to reconnect to masters +# and resynchronize with them. +# +# slaveof + +# If the master is password protected (using the "requirepass" configuration +# directive below) it is possible to tell the slave to authenticate before +# starting the replication synchronization process, otherwise the master will +# refuse the slave request. +# +# masterauth + +# When a slave loses its connection with the master, or when the replication +# is still in progress, the slave can act in two different ways: +# +# 1) if slave-serve-stale-data is set to 'yes' (the default) the slave will +# still reply to client requests, possibly with out of date data, or the +# data set may just be empty if this is the first synchronization. +# +# 2) if slave-serve-stale-data is set to 'no' the slave will reply with +# an error "SYNC with master in progress" to all the kind of commands +# but to INFO and SLAVEOF. +# +slave-serve-stale-data yes + +# You can configure a slave instance to accept writes or not. Writing against +# a slave instance may be useful to store some ephemeral data (because data +# written on a slave will be easily deleted after resync with the master) but +# may also cause problems if clients are writing to it because of a +# misconfiguration. +# +# Since Redis 2.6 by default slaves are read-only. +# +# Note: read only slaves are not designed to be exposed to untrusted clients +# on the internet. It's just a protection layer against misuse of the instance. +# Still a read only slave exports by default all the administrative commands +# such as CONFIG, DEBUG, and so forth. To a limited extent you can improve +# security of read only slaves using 'rename-command' to shadow all the +# administrative / dangerous commands. +slave-read-only yes + +# Replication SYNC strategy: disk or socket. +# +# ------------------------------------------------------- +# WARNING: DISKLESS REPLICATION IS EXPERIMENTAL CURRENTLY +# ------------------------------------------------------- +# +# New slaves and reconnecting slaves that are not able to continue the replication +# process just receiving differences, need to do what is called a "full +# synchronization". An RDB file is transmitted from the master to the slaves. +# The transmission can happen in two different ways: +# +# 1) Disk-backed: The Redis master creates a new process that writes the RDB +# file on disk. Later the file is transferred by the parent +# process to the slaves incrementally. +# 2) Diskless: The Redis master creates a new process that directly writes the +# RDB file to slave sockets, without touching the disk at all. +# +# With disk-backed replication, while the RDB file is generated, more slaves +# can be queued and served with the RDB file as soon as the current child producing +# the RDB file finishes its work. With diskless replication instead once +# the transfer starts, new slaves arriving will be queued and a new transfer +# will start when the current one terminates. +# +# When diskless replication is used, the master waits a configurable amount of +# time (in seconds) before starting the transfer in the hope that multiple slaves +# will arrive and the transfer can be parallelized. +# +# With slow disks and fast (large bandwidth) networks, diskless replication +# works better. +repl-diskless-sync no + +# When diskless replication is enabled, it is possible to configure the delay +# the server waits in order to spawn the child that transfers the RDB via socket +# to the slaves. +# +# This is important since once the transfer starts, it is not possible to serve +# new slaves arriving, that will be queued for the next RDB transfer, so the server +# waits a delay in order to let more slaves arrive. +# +# The delay is specified in seconds, and by default is 5 seconds. To disable +# it entirely just set it to 0 seconds and the transfer will start ASAP. +repl-diskless-sync-delay 5 + +# Slaves send PINGs to server in a predefined interval. It's possible to change +# this interval with the repl_ping_slave_period option. The default value is 10 +# seconds. +# +# repl-ping-slave-period 10 + +# The following option sets the replication timeout for: +# +# 1) Bulk transfer I/O during SYNC, from the point of view of slave. +# 2) Master timeout from the point of view of slaves (data, pings). +# 3) Slave timeout from the point of view of masters (REPLCONF ACK pings). +# +# It is important to make sure that this value is greater than the value +# specified for repl-ping-slave-period otherwise a timeout will be detected +# every time there is low traffic between the master and the slave. +# +# repl-timeout 60 + +# Disable TCP_NODELAY on the slave socket after SYNC? +# +# If you select "yes" Redis will use a smaller number of TCP packets and +# less bandwidth to send data to slaves. But this can add a delay for +# the data to appear on the slave side, up to 40 milliseconds with +# Linux kernels using a default configuration. +# +# If you select "no" the delay for data to appear on the slave side will +# be reduced but more bandwidth will be used for replication. +# +# By default we optimize for low latency, but in very high traffic conditions +# or when the master and slaves are many hops away, turning this to "yes" may +# be a good idea. +repl-disable-tcp-nodelay no + +# Set the replication backlog size. The backlog is a buffer that accumulates +# slave data when slaves are disconnected for some time, so that when a slave +# wants to reconnect again, often a full resync is not needed, but a partial +# resync is enough, just passing the portion of data the slave missed while +# disconnected. +# +# The bigger the replication backlog, the longer the time the slave can be +# disconnected and later be able to perform a partial resynchronization. +# +# The backlog is only allocated once there is at least a slave connected. +# +# repl-backlog-size 1mb + +# After a master has no longer connected slaves for some time, the backlog +# will be freed. The following option configures the amount of seconds that +# need to elapse, starting from the time the last slave disconnected, for +# the backlog buffer to be freed. +# +# A value of 0 means to never release the backlog. +# +# repl-backlog-ttl 3600 + +# The slave priority is an integer number published by Redis in the INFO output. +# It is used by Redis Sentinel in order to select a slave to promote into a +# master if the master is no longer working correctly. +# +# A slave with a low priority number is considered better for promotion, so +# for instance if there are three slaves with priority 10, 100, 25 Sentinel will +# pick the one with priority 10, that is the lowest. +# +# However a special priority of 0 marks the slave as not able to perform the +# role of master, so a slave with priority of 0 will never be selected by +# Redis Sentinel for promotion. +# +# By default the priority is 100. +slave-priority 100 + +# It is possible for a master to stop accepting writes if there are less than +# N slaves connected, having a lag less or equal than M seconds. +# +# The N slaves need to be in "online" state. +# +# The lag in seconds, that must be <= the specified value, is calculated from +# the last ping received from the slave, that is usually sent every second. +# +# This option does not GUARANTEE that N replicas will accept the write, but +# will limit the window of exposure for lost writes in case not enough slaves +# are available, to the specified number of seconds. +# +# For example to require at least 3 slaves with a lag <= 10 seconds use: +# +# min-slaves-to-write 3 +# min-slaves-max-lag 10 +# +# Setting one or the other to 0 disables the feature. +# +# By default min-slaves-to-write is set to 0 (feature disabled) and +# min-slaves-max-lag is set to 10. + +# A Redis master is able to list the address and port of the attached +# slaves in different ways. For example the "INFO replication" section +# offers this information, which is used, among other tools, by +# Redis Sentinel in order to discover slave instances. +# Another place where this info is available is in the output of the +# "ROLE" command of a masteer. +# +# The listed IP and address normally reported by a slave is obtained +# in the following way: +# +# IP: The address is auto detected by checking the peer address +# of the socket used by the slave to connect with the master. +# +# Port: The port is communicated by the slave during the replication +# handshake, and is normally the port that the slave is using to +# list for connections. +# +# However when port forwarding or Network Address Translation (NAT) is +# used, the slave may be actually reachable via different IP and port +# pairs. The following two options can be used by a slave in order to +# report to its master a specific set of IP and port, so that both INFO +# and ROLE will report those values. +# +# There is no need to use both the options if you need to override just +# the port or the IP address. +# +# slave-announce-ip 5.5.5.5 +# slave-announce-port 1234 + +################################## SECURITY ################################### + +# Require clients to issue AUTH before processing any other +# commands. This might be useful in environments in which you do not trust +# others with access to the host running redis-server. +# +# This should stay commented out for backward compatibility and because most +# people do not need auth (e.g. they run their own servers). +# +# Warning: since Redis is pretty fast an outside user can try up to +# 150k passwords per second against a good box. This means that you should +# use a very strong password otherwise it will be very easy to break. +# +# requirepass foobared + +# Command renaming. +# +# It is possible to change the name of dangerous commands in a shared +# environment. For instance the CONFIG command may be renamed into something +# hard to guess so that it will still be available for internal-use tools +# but not available for general clients. +# +# Example: +# +# rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c52 +# +# It is also possible to completely kill a command by renaming it into +# an empty string: +# +# rename-command CONFIG "" +# +# Please note that changing the name of commands that are logged into the +# AOF file or transmitted to slaves may cause problems. + +################################### LIMITS #################################### + +# Set the max number of connected clients at the same time. By default +# this limit is set to 10000 clients, however if the Redis server is not +# able to configure the process file limit to allow for the specified limit +# the max number of allowed clients is set to the current file limit +# minus 32 (as Redis reserves a few file descriptors for internal uses). +# +# Once the limit is reached Redis will close all the new connections sending +# an error 'max number of clients reached'. +# +# maxclients 10000 + +# Don't use more memory than the specified amount of bytes. +# When the memory limit is reached Redis will try to remove keys +# according to the eviction policy selected (see maxmemory-policy). +# +# If Redis can't remove keys according to the policy, or if the policy is +# set to 'noeviction', Redis will start to reply with errors to commands +# that would use more memory, like SET, LPUSH, and so on, and will continue +# to reply to read-only commands like GET. +# +# This option is usually useful when using Redis as an LRU cache, or to set +# a hard memory limit for an instance (using the 'noeviction' policy). +# +# WARNING: If you have slaves attached to an instance with maxmemory on, +# the size of the output buffers needed to feed the slaves are subtracted +# from the used memory count, so that network problems / resyncs will +# not trigger a loop where keys are evicted, and in turn the output +# buffer of slaves is full with DELs of keys evicted triggering the deletion +# of more keys, and so forth until the database is completely emptied. +# +# In short... if you have slaves attached it is suggested that you set a lower +# limit for maxmemory so that there is some free RAM on the system for slave +# output buffers (but this is not needed if the policy is 'noeviction'). +# +# maxmemory + +# MAXMEMORY POLICY: how Redis will select what to remove when maxmemory +# is reached. You can select among five behaviors: +# +# volatile-lru -> remove the key with an expire set using an LRU algorithm +# allkeys-lru -> remove any key according to the LRU algorithm +# volatile-random -> remove a random key with an expire set +# allkeys-random -> remove a random key, any key +# volatile-ttl -> remove the key with the nearest expire time (minor TTL) +# noeviction -> don't expire at all, just return an error on write operations +# +# Note: with any of the above policies, Redis will return an error on write +# operations, when there are no suitable keys for eviction. +# +# At the date of writing these commands are: set setnx setex append +# incr decr rpush lpush rpushx lpushx linsert lset rpoplpush sadd +# sinter sinterstore sunion sunionstore sdiff sdiffstore zadd zincrby +# zunionstore zinterstore hset hsetnx hmset hincrby incrby decrby +# getset mset msetnx exec sort +# +# The default is: +# +# maxmemory-policy noeviction + +# LRU and minimal TTL algorithms are not precise algorithms but approximated +# algorithms (in order to save memory), so you can tune it for speed or +# accuracy. For default Redis will check five keys and pick the one that was +# used less recently, you can change the sample size using the following +# configuration directive. +# +# The default of 5 produces good enough results. 10 Approximates very closely +# true LRU but costs a bit more CPU. 3 is very fast but not very accurate. +# +# maxmemory-samples 5 + +############################## APPEND ONLY MODE ############################### + +# By default Redis asynchronously dumps the dataset on disk. This mode is +# good enough in many applications, but an issue with the Redis process or +# a power outage may result into a few minutes of writes lost (depending on +# the configured save points). +# +# The Append Only File is an alternative persistence mode that provides +# much better durability. For instance using the default data fsync policy +# (see later in the config file) Redis can lose just one second of writes in a +# dramatic event like a server power outage, or a single write if something +# wrong with the Redis process itself happens, but the operating system is +# still running correctly. +# +# AOF and RDB persistence can be enabled at the same time without problems. +# If the AOF is enabled on startup Redis will load the AOF, that is the file +# with the better durability guarantees. +# +# Please check http://redis.io/topics/persistence for more information. + +appendonly no + +# The name of the append only file (default: "appendonly.aof") + +appendfilename "appendonly.aof" + +# The fsync() call tells the Operating System to actually write data on disk +# instead of waiting for more data in the output buffer. Some OS will really flush +# data on disk, some other OS will just try to do it ASAP. +# +# Redis supports three different modes: +# +# no: don't fsync, just let the OS flush the data when it wants. Faster. +# always: fsync after every write to the append only log. Slow, Safest. +# everysec: fsync only one time every second. Compromise. +# +# The default is "everysec", as that's usually the right compromise between +# speed and data safety. It's up to you to understand if you can relax this to +# "no" that will let the operating system flush the output buffer when +# it wants, for better performances (but if you can live with the idea of +# some data loss consider the default persistence mode that's snapshotting), +# or on the contrary, use "always" that's very slow but a bit safer than +# everysec. +# +# More details please check the following article: +# http://antirez.com/post/redis-persistence-demystified.html +# +# If unsure, use "everysec". + +# appendfsync always +appendfsync everysec +# appendfsync no + +# When the AOF fsync policy is set to always or everysec, and a background +# saving process (a background save or AOF log background rewriting) is +# performing a lot of I/O against the disk, in some Linux configurations +# Redis may block too long on the fsync() call. Note that there is no fix for +# this currently, as even performing fsync in a different thread will block +# our synchronous write(2) call. +# +# In order to mitigate this problem it's possible to use the following option +# that will prevent fsync() from being called in the main process while a +# BGSAVE or BGREWRITEAOF is in progress. +# +# This means that while another child is saving, the durability of Redis is +# the same as "appendfsync none". In practical terms, this means that it is +# possible to lose up to 30 seconds of log in the worst scenario (with the +# default Linux settings). +# +# If you have latency problems turn this to "yes". Otherwise leave it as +# "no" that is the safest pick from the point of view of durability. + +no-appendfsync-on-rewrite no + +# Automatic rewrite of the append only file. +# Redis is able to automatically rewrite the log file implicitly calling +# BGREWRITEAOF when the AOF log size grows by the specified percentage. +# +# This is how it works: Redis remembers the size of the AOF file after the +# latest rewrite (if no rewrite has happened since the restart, the size of +# the AOF at startup is used). +# +# This base size is compared to the current size. If the current size is +# bigger than the specified percentage, the rewrite is triggered. Also +# you need to specify a minimal size for the AOF file to be rewritten, this +# is useful to avoid rewriting the AOF file even if the percentage increase +# is reached but it is still pretty small. +# +# Specify a percentage of zero in order to disable the automatic AOF +# rewrite feature. + +auto-aof-rewrite-percentage 100 +auto-aof-rewrite-min-size 64mb + +# An AOF file may be found to be truncated at the end during the Redis +# startup process, when the AOF data gets loaded back into memory. +# This may happen when the system where Redis is running +# crashes, especially when an ext4 filesystem is mounted without the +# data=ordered option (however this can't happen when Redis itself +# crashes or aborts but the operating system still works correctly). +# +# Redis can either exit with an error when this happens, or load as much +# data as possible (the default now) and start if the AOF file is found +# to be truncated at the end. The following option controls this behavior. +# +# If aof-load-truncated is set to yes, a truncated AOF file is loaded and +# the Redis server starts emitting a log to inform the user of the event. +# Otherwise if the option is set to no, the server aborts with an error +# and refuses to start. When the option is set to no, the user requires +# to fix the AOF file using the "redis-check-aof" utility before to restart +# the server. +# +# Note that if the AOF file will be found to be corrupted in the middle +# the server will still exit with an error. This option only applies when +# Redis will try to read more data from the AOF file but not enough bytes +# will be found. +aof-load-truncated yes + +################################ LUA SCRIPTING ############################### + +# Max execution time of a Lua script in milliseconds. +# +# If the maximum execution time is reached Redis will log that a script is +# still in execution after the maximum allowed time and will start to +# reply to queries with an error. +# +# When a long running script exceeds the maximum execution time only the +# SCRIPT KILL and SHUTDOWN NOSAVE commands are available. The first can be +# used to stop a script that did not yet called write commands. The second +# is the only way to shut down the server in the case a write command was +# already issued by the script but the user doesn't want to wait for the natural +# termination of the script. +# +# Set it to 0 or a negative value for unlimited execution without warnings. +lua-time-limit 5000 + +################################ REDIS CLUSTER ############################### +# +# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +# WARNING EXPERIMENTAL: Redis Cluster is considered to be stable code, however +# in order to mark it as "mature" we need to wait for a non trivial percentage +# of users to deploy it in production. +# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +# +# Normal Redis instances can't be part of a Redis Cluster; only nodes that are +# started as cluster nodes can. In order to start a Redis instance as a +# cluster node enable the cluster support uncommenting the following: +# +# cluster-enabled yes + +# Every cluster node has a cluster configuration file. This file is not +# intended to be edited by hand. It is created and updated by Redis nodes. +# Every Redis Cluster node requires a different cluster configuration file. +# Make sure that instances running in the same system do not have +# overlapping cluster configuration file names. +# +# cluster-config-file nodes-6379.conf + +# Cluster node timeout is the amount of milliseconds a node must be unreachable +# for it to be considered in failure state. +# Most other internal time limits are multiple of the node timeout. +# +# cluster-node-timeout 15000 + +# A slave of a failing master will avoid to start a failover if its data +# looks too old. +# +# There is no simple way for a slave to actually have a exact measure of +# its "data age", so the following two checks are performed: +# +# 1) If there are multiple slaves able to failover, they exchange messages +# in order to try to give an advantage to the slave with the best +# replication offset (more data from the master processed). +# Slaves will try to get their rank by offset, and apply to the start +# of the failover a delay proportional to their rank. +# +# 2) Every single slave computes the time of the last interaction with +# its master. This can be the last ping or command received (if the master +# is still in the "connected" state), or the time that elapsed since the +# disconnection with the master (if the replication link is currently down). +# If the last interaction is too old, the slave will not try to failover +# at all. +# +# The point "2" can be tuned by user. Specifically a slave will not perform +# the failover if, since the last interaction with the master, the time +# elapsed is greater than: +# +# (node-timeout * slave-validity-factor) + repl-ping-slave-period +# +# So for example if node-timeout is 30 seconds, and the slave-validity-factor +# is 10, and assuming a default repl-ping-slave-period of 10 seconds, the +# slave will not try to failover if it was not able to talk with the master +# for longer than 310 seconds. +# +# A large slave-validity-factor may allow slaves with too old data to failover +# a master, while a too small value may prevent the cluster from being able to +# elect a slave at all. +# +# For maximum availability, it is possible to set the slave-validity-factor +# to a value of 0, which means, that slaves will always try to failover the +# master regardless of the last time they interacted with the master. +# (However they'll always try to apply a delay proportional to their +# offset rank). +# +# Zero is the only value able to guarantee that when all the partitions heal +# the cluster will always be able to continue. +# +# cluster-slave-validity-factor 10 + +# Cluster slaves are able to migrate to orphaned masters, that are masters +# that are left without working slaves. This improves the cluster ability +# to resist to failures as otherwise an orphaned master can't be failed over +# in case of failure if it has no working slaves. +# +# Slaves migrate to orphaned masters only if there are still at least a +# given number of other working slaves for their old master. This number +# is the "migration barrier". A migration barrier of 1 means that a slave +# will migrate only if there is at least 1 other working slave for its master +# and so forth. It usually reflects the number of slaves you want for every +# master in your cluster. +# +# Default is 1 (slaves migrate only if their masters remain with at least +# one slave). To disable migration just set it to a very large value. +# A value of 0 can be set but is useful only for debugging and dangerous +# in production. +# +# cluster-migration-barrier 1 + +# By default Redis Cluster nodes stop accepting queries if they detect there +# is at least an hash slot uncovered (no available node is serving it). +# This way if the cluster is partially down (for example a range of hash slots +# are no longer covered) all the cluster becomes, eventually, unavailable. +# It automatically returns available as soon as all the slots are covered again. +# +# However sometimes you want the subset of the cluster which is working, +# to continue to accept queries for the part of the key space that is still +# covered. In order to do so, just set the cluster-require-full-coverage +# option to no. +# +# cluster-require-full-coverage yes + +# In order to setup your cluster make sure to read the documentation +# available at http://redis.io web site. + +################################## SLOW LOG ################################### + +# The Redis Slow Log is a system to log queries that exceeded a specified +# execution time. The execution time does not include the I/O operations +# like talking with the client, sending the reply and so forth, +# but just the time needed to actually execute the command (this is the only +# stage of command execution where the thread is blocked and can not serve +# other requests in the meantime). +# +# You can configure the slow log with two parameters: one tells Redis +# what is the execution time, in microseconds, to exceed in order for the +# command to get logged, and the other parameter is the length of the +# slow log. When a new command is logged the oldest one is removed from the +# queue of logged commands. + +# The following time is expressed in microseconds, so 1000000 is equivalent +# to one second. Note that a negative number disables the slow log, while +# a value of zero forces the logging of every command. +slowlog-log-slower-than 10000 + +# There is no limit to this length. Just be aware that it will consume memory. +# You can reclaim memory used by the slow log with SLOWLOG RESET. +slowlog-max-len 128 + +################################ LATENCY MONITOR ############################## + +# The Redis latency monitoring subsystem samples different operations +# at runtime in order to collect data related to possible sources of +# latency of a Redis instance. +# +# Via the LATENCY command this information is available to the user that can +# print graphs and obtain reports. +# +# The system only logs operations that were performed in a time equal or +# greater than the amount of milliseconds specified via the +# latency-monitor-threshold configuration directive. When its value is set +# to zero, the latency monitor is turned off. +# +# By default latency monitoring is disabled since it is mostly not needed +# if you don't have latency issues, and collecting data has a performance +# impact, that while very small, can be measured under big load. Latency +# monitoring can easily be enabled at runtime using the command +# "CONFIG SET latency-monitor-threshold " if needed. +latency-monitor-threshold 0 + +############################# EVENT NOTIFICATION ############################## + +# Redis can notify Pub/Sub clients about events happening in the key space. +# This feature is documented at http://redis.io/topics/notifications +# +# For instance if keyspace events notification is enabled, and a client +# performs a DEL operation on key "foo" stored in the Database 0, two +# messages will be published via Pub/Sub: +# +# PUBLISH __keyspace@0__:foo del +# PUBLISH __keyevent@0__:del foo +# +# It is possible to select the events that Redis will notify among a set +# of classes. Every class is identified by a single character: +# +# K Keyspace events, published with __keyspace@__ prefix. +# E Keyevent events, published with __keyevent@__ prefix. +# g Generic commands (non-type specific) like DEL, EXPIRE, RENAME, ... +# $ String commands +# l List commands +# s Set commands +# h Hash commands +# z Sorted set commands +# x Expired events (events generated every time a key expires) +# e Evicted events (events generated when a key is evicted for maxmemory) +# A Alias for g$lshzxe, so that the "AKE" string means all the events. +# +# The "notify-keyspace-events" takes as argument a string that is composed +# of zero or multiple characters. The empty string means that notifications +# are disabled. +# +# Example: to enable list and generic events, from the point of view of the +# event name, use: +# +# notify-keyspace-events Elg +# +# Example 2: to get the stream of the expired keys subscribing to channel +# name __keyevent@0__:expired use: +# +# notify-keyspace-events Ex +# +# By default all notifications are disabled because most users don't need +# this feature and the feature has some overhead. Note that if you don't +# specify at least one of K or E, no events will be delivered. +notify-keyspace-events "" + +############################### ADVANCED CONFIG ############################### + +# Hashes are encoded using a memory efficient data structure when they have a +# small number of entries, and the biggest entry does not exceed a given +# threshold. These thresholds can be configured using the following directives. +hash-max-ziplist-entries 512 +hash-max-ziplist-value 64 + +# Lists are also encoded in a special way to save a lot of space. +# The number of entries allowed per internal list node can be specified +# as a fixed maximum size or a maximum number of elements. +# For a fixed maximum size, use -5 through -1, meaning: +# -5: max size: 64 Kb <-- not recommended for normal workloads +# -4: max size: 32 Kb <-- not recommended +# -3: max size: 16 Kb <-- probably not recommended +# -2: max size: 8 Kb <-- good +# -1: max size: 4 Kb <-- good +# Positive numbers mean store up to _exactly_ that number of elements +# per list node. +# The highest performing option is usually -2 (8 Kb size) or -1 (4 Kb size), +# but if your use case is unique, adjust the settings as necessary. +list-max-ziplist-size -2 + +# Lists may also be compressed. +# Compress depth is the number of quicklist ziplist nodes from *each* side of +# the list to *exclude* from compression. The head and tail of the list +# are always uncompressed for fast push/pop operations. Settings are: +# 0: disable all list compression +# 1: depth 1 means "don't start compressing until after 1 node into the list, +# going from either the head or tail" +# So: [head]->node->node->...->node->[tail] +# [head], [tail] will always be uncompressed; inner nodes will compress. +# 2: [head]->[next]->node->node->...->node->[prev]->[tail] +# 2 here means: don't compress head or head->next or tail->prev or tail, +# but compress all nodes between them. +# 3: [head]->[next]->[next]->node->node->...->node->[prev]->[prev]->[tail] +# etc. +list-compress-depth 0 + +# Sets have a special encoding in just one case: when a set is composed +# of just strings that happen to be integers in radix 10 in the range +# of 64 bit signed integers. +# The following configuration setting sets the limit in the size of the +# set in order to use this special memory saving encoding. +set-max-intset-entries 512 + +# Similarly to hashes and lists, sorted sets are also specially encoded in +# order to save a lot of space. This encoding is only used when the length and +# elements of a sorted set are below the following limits: +zset-max-ziplist-entries 128 +zset-max-ziplist-value 64 + +# HyperLogLog sparse representation bytes limit. The limit includes the +# 16 bytes header. When an HyperLogLog using the sparse representation crosses +# this limit, it is converted into the dense representation. +# +# A value greater than 16000 is totally useless, since at that point the +# dense representation is more memory efficient. +# +# The suggested value is ~ 3000 in order to have the benefits of +# the space efficient encoding without slowing down too much PFADD, +# which is O(N) with the sparse encoding. The value can be raised to +# ~ 10000 when CPU is not a concern, but space is, and the data set is +# composed of many HyperLogLogs with cardinality in the 0 - 15000 range. +hll-sparse-max-bytes 3000 + +# Active rehashing uses 1 millisecond every 100 milliseconds of CPU time in +# order to help rehashing the main Redis hash table (the one mapping top-level +# keys to values). The hash table implementation Redis uses (see dict.c) +# performs a lazy rehashing: the more operation you run into a hash table +# that is rehashing, the more rehashing "steps" are performed, so if the +# server is idle the rehashing is never complete and some more memory is used +# by the hash table. +# +# The default is to use this millisecond 10 times every second in order to +# actively rehash the main dictionaries, freeing memory when possible. +# +# If unsure: +# use "activerehashing no" if you have hard latency requirements and it is +# not a good thing in your environment that Redis can reply from time to time +# to queries with 2 milliseconds delay. +# +# use "activerehashing yes" if you don't have such hard requirements but +# want to free memory asap when possible. +activerehashing yes + +# The client output buffer limits can be used to force disconnection of clients +# that are not reading data from the server fast enough for some reason (a +# common reason is that a Pub/Sub client can't consume messages as fast as the +# publisher can produce them). +# +# The limit can be set differently for the three different classes of clients: +# +# normal -> normal clients including MONITOR clients +# slave -> slave clients +# pubsub -> clients subscribed to at least one pubsub channel or pattern +# +# The syntax of every client-output-buffer-limit directive is the following: +# +# client-output-buffer-limit +# +# A client is immediately disconnected once the hard limit is reached, or if +# the soft limit is reached and remains reached for the specified number of +# seconds (continuously). +# So for instance if the hard limit is 32 megabytes and the soft limit is +# 16 megabytes / 10 seconds, the client will get disconnected immediately +# if the size of the output buffers reach 32 megabytes, but will also get +# disconnected if the client reaches 16 megabytes and continuously overcomes +# the limit for 10 seconds. +# +# By default normal clients are not limited because they don't receive data +# without asking (in a push way), but just after a request, so only +# asynchronous clients may create a scenario where data is requested faster +# than it can read. +# +# Instead there is a default limit for pubsub and slave clients, since +# subscribers and slaves receive data in a push fashion. +# +# Both the hard or the soft limit can be disabled by setting them to zero. +client-output-buffer-limit normal 0 0 0 +client-output-buffer-limit slave 256mb 64mb 60 +client-output-buffer-limit pubsub 32mb 8mb 60 + +# Redis calls an internal function to perform many background tasks, like +# closing connections of clients in timeout, purging expired keys that are +# never requested, and so forth. +# +# Not all tasks are performed with the same frequency, but Redis checks for +# tasks to perform according to the specified "hz" value. +# +# By default "hz" is set to 10. Raising the value will use more CPU when +# Redis is idle, but at the same time will make Redis more responsive when +# there are many keys expiring at the same time, and timeouts may be +# handled with more precision. +# +# The range is between 1 and 500, however a value over 100 is usually not +# a good idea. Most users should use the default of 10 and raise this up to +# 100 only in environments where very low latency is required. +hz 10 + +# When a child rewrites the AOF file, if the following option is enabled +# the file will be fsync-ed every 32 MB of data generated. This is useful +# in order to commit the file to the disk more incrementally and avoid +# big latency spikes. +aof-rewrite-incremental-fsync yes \ No newline at end of file diff --git a/.redis/redis.test.conf b/.redis/redis.test.conf new file mode 100644 index 0000000..7ec3ba0 --- /dev/null +++ b/.redis/redis.test.conf @@ -0,0 +1 @@ +databases 1 \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 0cc05e0..502afec 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,3 +4,7 @@ services: image: redis:3 ports: - 6379:6379 + volumes: + - .redis/data:/data + - .redis/redis.default.conf:/usr/local/etc/redis/redis.conf + command: /usr/local/etc/redis/redis.conf From 28c019d2d0d28313c07027c5f0ed65c4e2b8f3c5 Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Thu, 10 May 2018 15:04:45 +0200 Subject: [PATCH 10/62] Update dependencies --- composer.lock | 203 ++++++++++++++++++++++++++------------------------ 1 file changed, 104 insertions(+), 99 deletions(-) diff --git a/composer.lock b/composer.lock index 92022b8..cee5dd1 100644 --- a/composer.lock +++ b/composer.lock @@ -1,10 +1,10 @@ { "_readme": [ "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "26ed0ae9d781d9252a4493088431d563", + "content-hash": "9f3aaee9fe35df3d98eb71a8f1c29140", "packages": [ { "name": "icehawk/icehawk", @@ -77,16 +77,16 @@ }, { "name": "symfony/polyfill-mbstring", - "version": "v1.7.0", + "version": "v1.8.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "78be803ce01e55d3491c1397cf1c64beb9c1b63b" + "reference": "3296adf6a6454a050679cde90f95350ad604b171" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/78be803ce01e55d3491c1397cf1c64beb9c1b63b", - "reference": "78be803ce01e55d3491c1397cf1c64beb9c1b63b", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/3296adf6a6454a050679cde90f95350ad604b171", + "reference": "3296adf6a6454a050679cde90f95350ad604b171", "shasum": "" }, "require": { @@ -98,7 +98,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.7-dev" + "dev-master": "1.8-dev" } }, "autoload": { @@ -132,20 +132,20 @@ "portable", "shim" ], - "time": "2018-01-30T19:27:44+00:00" + "time": "2018-04-26T10:06:28+00:00" }, { "name": "twig/twig", - "version": "v2.4.7", + "version": "v2.4.8", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "69aacd44dbbaa3199d5afb68605c996d577896fc" + "reference": "7b604c89da162034bdf4bb66310f358d313dd16d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/69aacd44dbbaa3199d5afb68605c996d577896fc", - "reference": "69aacd44dbbaa3199d5afb68605c996d577896fc", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/7b604c89da162034bdf4bb66310f358d313dd16d", + "reference": "7b604c89da162034bdf4bb66310f358d313dd16d", "shasum": "" }, "require": { @@ -198,7 +198,7 @@ "keywords": [ "templating" ], - "time": "2018-03-20T04:31:17+00:00" + "time": "2018-04-02T09:24:19+00:00" } ], "packages-dev": [ @@ -208,17 +208,17 @@ "source": { "type": "git", "url": "https://github.com/Roave/SecurityAdvisories.git", - "reference": "4a272b63452f4d8ff0d48a759e59db9359a9b5b7" + "reference": "bc0c7361e137f205dc5775583701743b5c4edcda" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/4a272b63452f4d8ff0d48a759e59db9359a9b5b7", - "reference": "4a272b63452f4d8ff0d48a759e59db9359a9b5b7", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/bc0c7361e137f205dc5775583701743b5c4edcda", + "reference": "bc0c7361e137f205dc5775583701743b5c4edcda", "shasum": "" }, "conflict": { "3f/pygmentize": "<1.2", - "adodb/adodb-php": "<5.20.6", + "adodb/adodb-php": "<5.20.12", "amphp/artax": "<1.0.6|>=2,<2.0.6", "amphp/http": "<1.0.1", "asymmetricrypt/asymmetricrypt": ">=0,<9.9.99", @@ -230,8 +230,8 @@ "codeigniter/framework": "<=3.0.6", "composer/composer": "<=1.0.0-alpha11", "contao-components/mediaelement": ">=2.14.2,<2.21.1", - "contao/core": ">=2,<3.5.32", - "contao/core-bundle": ">=4,<4.4.8", + "contao/core": ">=2,<3.5.35", + "contao/core-bundle": ">=4,<4.4.18|>=4.5,<4.5.8", "contao/listing-bundle": ">=4,<4.4.8", "contao/newsletter-bundle": ">=4,<4.1", "doctrine/annotations": ">=1,<1.2.7", @@ -244,20 +244,23 @@ "doctrine/mongodb-odm-bundle": ">=2,<3.0.1", "doctrine/orm": ">=2,<2.4.8|>=2.5,<2.5.1", "dompdf/dompdf": ">=0.6,<0.6.2", - "drupal/core": ">=8,<8.4.5", - "drupal/drupal": ">=8,<8.4.5", + "drupal/core": ">=7,<7.59|>=8,<8.4.8|>=8.5,<8.5.3", + "drupal/drupal": ">=7,<7.59|>=8,<8.4.8|>=8.5,<8.5.3", "erusev/parsedown": "<1.7", "ezsystems/ezpublish-legacy": ">=5.3,<5.3.12.3|>=5.4,<5.4.11.3|>=2017.8,<2017.8.1.1|>=2017.12,<2017.12.2.1", "firebase/php-jwt": "<2", "friendsofsymfony/rest-bundle": ">=1.2,<1.2.2", "friendsofsymfony/user-bundle": ">=1.2,<1.3.5", + "fuel/core": "<=1.8", "gree/jose": "<=2.2", "gregwar/rst": "<1.0.3", "guzzlehttp/guzzle": ">=6,<6.2.1|>=4.0.0-rc2,<4.2.4|>=5,<5.3.1", - "illuminate/auth": ">=4,<4.0.99|>=4.1,<4.1.26", + "illuminate/auth": ">=4,<4.0.99|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.10", "illuminate/database": ">=4,<4.0.99|>=4.1,<4.1.29", + "illuminate/encryption": ">=4,<=4.0.11|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.40|>=5.6,<5.6.15", "joomla/session": "<1.3.1", - "laravel/framework": ">=4,<4.0.99|>=4.1,<4.1.29", + "kreait/firebase-php": ">=3.2,<3.8.1", + "laravel/framework": ">=4,<4.0.99|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.40|>=5.6,<5.6.15", "laravel/socialite": ">=1,<1.0.99|>=2,<2.0.10", "magento/magento1ce": ">=1.5.0.1,<1.9.3.2", "magento/magento1ee": ">=1.9,<1.14.3.2", @@ -270,6 +273,7 @@ "padraic/humbug_get_contents": "<1.1.2", "pagarme/pagarme-php": ">=0,<3", "paragonie/random_compat": "<2", + "paypal/merchant-sdk-php": "<3.12", "phpmailer/phpmailer": ">=5,<5.2.24", "phpunit/phpunit": ">=4.8.19,<4.8.28|>=5.0.10,<5.6.3", "phpxmlrpc/extras": "<0.6.1", @@ -285,6 +289,7 @@ "simplesamlphp/saml2": "<1.10.6|>=2,<2.3.8|>=3,<3.1.4", "simplesamlphp/simplesamlphp": "<1.15.2", "simplesamlphp/simplesamlphp-module-infocard": "<1.0.1", + "slim/slim": "<2.6", "socalnick/scn-social-auth": "<1.15.2", "squizlabs/php_codesniffer": ">=1,<2.8.1|>=3,<3.0.1", "stormpath/sdk": ">=0,<9.9.99", @@ -359,87 +364,87 @@ } ], "description": "Prevents installation of composer packages with known security vulnerabilities: no API, simply require it", - "time": "2018-03-22T21:16:54+00:00" + "time": "2018-05-09T13:33:28+00:00" }, - { - "name": "tm/tooly-composer-script", - "version": "1.2.6", - "source": { - "type": "git", - "url": "https://github.com/tommy-muehle/tooly-composer-script.git", - "reference": "53cc0551122145b4ae4c523e64f3628329ef8ea5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/tommy-muehle/tooly-composer-script/zipball/53cc0551122145b4ae4c523e64f3628329ef8ea5", - "reference": "53cc0551122145b4ae4c523e64f3628329ef8ea5", - "shasum": "" - }, - "require": { - "php": ">=5.6.0" - }, - "require-dev": { - "codeclimate/php-test-reporter": "dev-master", - "composer/composer": "1.*", - "mikey179/vfsstream": "1.6.*", - "php-mock/php-mock-phpunit": "1.1.*", - "symfony/console": "^3.0", - "tm/gpg-verifier": "1.*" - }, - "suggest": { - "tm/gpg-verifier": "Allows verification over GPG for PHAR tools." - }, - "type": "library", - "extra": { - "tools": { - "phpunit": { - "url": "https://phar.phpunit.de/phpunit-5.5.0.phar" + { + "name": "tm/tooly-composer-script", + "version": "1.2.6", + "source": { + "type": "git", + "url": "https://github.com/tommy-muehle/tooly-composer-script.git", + "reference": "53cc0551122145b4ae4c523e64f3628329ef8ea5" }, - "phpcpd": { - "url": "https://phar.phpunit.de/phpcpd-2.0.4.phar" + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/tommy-muehle/tooly-composer-script/zipball/53cc0551122145b4ae4c523e64f3628329ef8ea5", + "reference": "53cc0551122145b4ae4c523e64f3628329ef8ea5", + "shasum": "" }, - "phpcs": { - "url": "https://squizlabs.github.io/PHP_CodeSniffer/phpcs.phar", - "force-replace": true + "require": { + "php": ">=5.6.0" }, - "phpmd": { - "url": "http://static.phpmd.org/php/latest/phpmd.phar", - "force-replace": true + "require-dev": { + "codeclimate/php-test-reporter": "dev-master", + "composer/composer": "1.*", + "mikey179/vfsstream": "1.6.*", + "php-mock/php-mock-phpunit": "1.1.*", + "symfony/console": "^3.0", + "tm/gpg-verifier": "1.*" }, - "security-checker": { - "url": "http://get.sensiolabs.org/security-checker.phar", - "force-replace": true - } - } - }, - "autoload": { - "psr-4": { - "Tooly\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Tommy Muehle", - "email": "tommy.muehle@gmail.com", - "homepage": "https://tommy-muehle.de" - } - ], - "description": "Simple composer script to manage phar files.", - "homepage": "https://github.com/tommy-muehle/tooly-composer-script", - "keywords": [ - "composer", - "composer-phar", - "composer-script", - "gpg-verification", - "phar", - "phar-handling", - "phar-management" - ], - "time": "2018-03-20T21:26:34+00:00" + "suggest": { + "tm/gpg-verifier": "Allows verification over GPG for PHAR tools." + }, + "type": "library", + "extra": { + "tools": { + "phpunit": { + "url": "https://phar.phpunit.de/phpunit-5.5.0.phar" + }, + "phpcpd": { + "url": "https://phar.phpunit.de/phpcpd-2.0.4.phar" + }, + "phpcs": { + "url": "https://squizlabs.github.io/PHP_CodeSniffer/phpcs.phar", + "force-replace": true + }, + "phpmd": { + "url": "http://static.phpmd.org/php/latest/phpmd.phar", + "force-replace": true + }, + "security-checker": { + "url": "http://get.sensiolabs.org/security-checker.phar", + "force-replace": true + } + } + }, + "autoload": { + "psr-4": { + "Tooly\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Tommy Muehle", + "email": "tommy.muehle@gmail.com", + "homepage": "https://tommy-muehle.de" + } + ], + "description": "Simple composer script to manage phar files.", + "homepage": "https://github.com/tommy-muehle/tooly-composer-script", + "keywords": [ + "composer", + "composer-phar", + "composer-script", + "gpg-verification", + "phar", + "phar-handling", + "phar-management" + ], + "time": "2018-03-20T21:26:34+00:00" } ], "aliases": [], From be584d857cf18511e436c2a1019672b11e704fcb Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Fri, 11 May 2018 14:24:01 +0200 Subject: [PATCH 11/62] Update CI settings for faster build --- .travis.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.travis.yml b/.travis.yml index a4ba757..d0054c3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,13 @@ php: - 7.2 - 7.2snapshot +git: + depth: 5 + +cache: + directories: + - $HOME/.composer/cache/files + branches: only: - master From 978aead687905c449db25846c8334f081ff817c5 Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Fri, 11 May 2018 14:41:16 +0200 Subject: [PATCH 12/62] Throw exception, if servers config file was not found --- src/Exceptions/ServersConfigNotFound.php | 8 ++++++++ .../Configs/ServerConfigList.php | 16 +++++++++++++++- .../Configs/ServerConfigListTest.php | 18 +++++++++++++++++- 3 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 src/Exceptions/ServersConfigNotFound.php diff --git a/src/Exceptions/ServersConfigNotFound.php b/src/Exceptions/ServersConfigNotFound.php new file mode 100644 index 0000000..67c7bb2 --- /dev/null +++ b/src/Exceptions/ServersConfigNotFound.php @@ -0,0 +1,8 @@ +loadServerConfigs( $data ); } - public static function fromConfigFile() : self + /** + * @param null|string $configFile + * + * @throws ServersConfigNotFound + * @return ServerConfigList + */ + public static function fromConfigFile( ?string $configFile = null ) : self { + $serversConfigFile = $configFile ?? dirname( __DIR__, 3 ) . '/config/servers.php'; + if ( !file_exists( $serversConfigFile ) ) + { + throw new ServersConfigNotFound( 'Could not find servers config at ' . $serversConfigFile ); + } + return new self( (array)include __DIR__ . '/../../../config/servers.php' ); } diff --git a/tests/Unit/Infrastructure/Configs/ServerConfigListTest.php b/tests/Unit/Infrastructure/Configs/ServerConfigListTest.php index 6d41ece..3ea4aca 100644 --- a/tests/Unit/Infrastructure/Configs/ServerConfigListTest.php +++ b/tests/Unit/Infrastructure/Configs/ServerConfigListTest.php @@ -3,6 +3,7 @@ namespace hollodotme\Readis\Tests\Unit\Infrastructure\Configs; use hollodotme\Readis\Exceptions\ServerConfigNotFound; +use hollodotme\Readis\Exceptions\ServersConfigNotFound; use hollodotme\Readis\Infrastructure\Configs\ServerConfigList; use hollodotme\Readis\Infrastructure\Interfaces\ProvidesServerConfig; use PHPUnit\Framework\ExpectationFailedException; @@ -22,7 +23,11 @@ final class ServerConfigListTest extends TestCase * * @dataProvider serverConfigsProvider */ - public function testCanGetServerConfigForKey( array $serverConfigs, string $serverKey, array $expectedServerConfig ) : void + public function testCanGetServerConfigForKey( + array $serverConfigs, + string $serverKey, + array $expectedServerConfig + ) : void { $serverConfigList = new ServerConfigList( $serverConfigs ); $serverConfig = $serverConfigList->getServerConfig( $serverKey ); @@ -104,4 +109,15 @@ public function testThrowsExceptionForUnknownServerKey( array $serverConfigs ) : $serverConfigList->getServerConfig( '3' ); } + + /** + * @throws ServersConfigNotFound + */ + public function testThrowsExceptionIfServersConfigFileNotFound() : void + { + $this->expectException( ServersConfigNotFound::class ); + $this->expectExceptionMessage( 'Could not find servers config at /path/to/servers.php' ); + + ServerConfigList::fromConfigFile( '/path/to/servers.php' ); + } } From 132d85d886cc86e995da4c5d6570a1a37a44e1e2 Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Fri, 11 May 2018 14:48:08 +0200 Subject: [PATCH 13/62] Fix finally included file --- src/Infrastructure/Configs/ServerConfigList.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Infrastructure/Configs/ServerConfigList.php b/src/Infrastructure/Configs/ServerConfigList.php index 10de2a9..40a57a3 100644 --- a/src/Infrastructure/Configs/ServerConfigList.php +++ b/src/Infrastructure/Configs/ServerConfigList.php @@ -32,7 +32,8 @@ public static function fromConfigFile( ?string $configFile = null ) : self throw new ServersConfigNotFound( 'Could not find servers config at ' . $serversConfigFile ); } - return new self( (array)include __DIR__ . '/../../../config/servers.php' ); + /** @noinspection PhpIncludeInspection */ + return new self( (array)require $serversConfigFile ); } private function loadServerConfigs( array $serverConfigList ) : void From 17a329121f71d886205ef418fb2154113c8953cb Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Fri, 11 May 2018 14:48:43 +0200 Subject: [PATCH 14/62] Throw exception if app config file not found --- src/Application/Configs/AppConfig.php | 20 +++++++++++++++++-- src/Exceptions/ApplicationConfigNotFound.php | 8 ++++++++ .../Application/Configs/AppConfigTest.php | 12 +++++++++++ 3 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 src/Exceptions/ApplicationConfigNotFound.php diff --git a/src/Application/Configs/AppConfig.php b/src/Application/Configs/AppConfig.php index 60bdf15..c65e207 100644 --- a/src/Application/Configs/AppConfig.php +++ b/src/Application/Configs/AppConfig.php @@ -2,6 +2,8 @@ namespace hollodotme\Readis\Application\Configs; +use hollodotme\Readis\Exceptions\ApplicationConfigNotFound; +use function file_exists; use const PHP_URL_PATH; final class AppConfig @@ -14,9 +16,23 @@ public function __construct( array $data ) $this->configData = $data; } - public static function fromConfigFile() : self + /** + * @param null|string $configFile + * + * @throws ApplicationConfigNotFound + * @return AppConfig + */ + public static function fromConfigFile( ?string $configFile = null ) : self { - return new self( (array)include __DIR__ . '/../../../config/app.php' ); + $appConfigFile = $configFile ?? dirname( __DIR__, 3 ) . '/config/app.php'; + + if ( !file_exists( $appConfigFile ) ) + { + throw new ApplicationConfigNotFound( 'Could not find application config at ' . $appConfigFile ); + } + + /** @noinspection PhpIncludeInspection */ + return new self( (array)require $appConfigFile ); } public function getBaseUrl() : string diff --git a/src/Exceptions/ApplicationConfigNotFound.php b/src/Exceptions/ApplicationConfigNotFound.php new file mode 100644 index 0000000..5739374 --- /dev/null +++ b/src/Exceptions/ApplicationConfigNotFound.php @@ -0,0 +1,8 @@ +expectException( ApplicationConfigNotFound::class ); + $this->expectExceptionMessage( 'Could not find application config at /path/to/app.php' ); + + AppConfig::fromConfigFile( '/path/to/app.php' ); + } } From 0bd544bb8ceabaf1b3bd6f6023f8c28885e60752 Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Fri, 11 May 2018 14:58:42 +0200 Subject: [PATCH 15/62] Throw exception if no servers were configured --- src/Exceptions/NoServersConfigured.php | 8 ++++++++ src/Infrastructure/Configs/ServerConfigList.php | 7 +++++++ 2 files changed, 15 insertions(+) create mode 100644 src/Exceptions/NoServersConfigured.php diff --git a/src/Exceptions/NoServersConfigured.php b/src/Exceptions/NoServersConfigured.php new file mode 100644 index 0000000..62f6ee3 --- /dev/null +++ b/src/Exceptions/NoServersConfigured.php @@ -0,0 +1,8 @@ +servers ) ) + { + throw new NoServersConfigured( 'No servers were configured.' ); + } + return $this->servers; } From 051742c9ffbcd5cf3a1d0b92933fb9edbfda224d Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Fri, 11 May 2018 14:59:40 +0200 Subject: [PATCH 16/62] Add final read responder with proper error handling --- src/Application/Configs/IceHawkConfig.php | 9 +++++-- .../FinalResponders/FinalReadResponder.php | 27 +++++++++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 src/Application/FinalResponders/FinalReadResponder.php diff --git a/src/Application/Configs/IceHawkConfig.php b/src/Application/Configs/IceHawkConfig.php index 463d61c..2f93a7b 100644 --- a/src/Application/Configs/IceHawkConfig.php +++ b/src/Application/Configs/IceHawkConfig.php @@ -2,6 +2,7 @@ namespace hollodotme\Readis\Application\Configs; +use hollodotme\Readis\Application\FinalResponders\FinalReadResponder; use hollodotme\Readis\Application\Web\Server\Read\AjaxKeyDetailsRequestHandler; use hollodotme\Readis\Application\Web\Server\Read\AjaxSearchKeysRequestHandler; use hollodotme\Readis\Application\Web\Server\Read\ServerDetailsRequestHandler; @@ -10,12 +11,12 @@ use hollodotme\Readis\Traits\EnvInjecting; use IceHawk\IceHawk\Defaults\Traits\DefaultCookieProviding; use IceHawk\IceHawk\Defaults\Traits\DefaultEventSubscribing; -use IceHawk\IceHawk\Defaults\Traits\DefaultFinalReadResponding; use IceHawk\IceHawk\Defaults\Traits\DefaultFinalWriteResponding; use IceHawk\IceHawk\Defaults\Traits\DefaultRequestBypassing; use IceHawk\IceHawk\Defaults\Traits\DefaultRequestInfoProviding; use IceHawk\IceHawk\Defaults\Traits\DefaultWriteRouting; use IceHawk\IceHawk\Interfaces\ConfiguresIceHawk; +use IceHawk\IceHawk\Interfaces\RespondsFinallyToReadRequest; use IceHawk\IceHawk\Routing\Patterns\NamedRegExp; use IceHawk\IceHawk\Routing\ReadRoute; use function preg_quote; @@ -25,7 +26,6 @@ final class IceHawkConfig implements ConfiguresIceHawk use EnvInjecting; use DefaultCookieProviding; use DefaultEventSubscribing; - use DefaultFinalReadResponding; use DefaultFinalWriteResponding; use DefaultRequestBypassing; use DefaultRequestInfoProviding; @@ -68,4 +68,9 @@ private function buildReadRoutes() : void '^' . $quotedBaseUri . '/server/(?\d+)/database/(?\d+)/keys/(?.+)/?$' => AjaxKeyDetailsRequestHandler::class, ]; } + + public function getFinalReadResponder() : RespondsFinallyToReadRequest + { + return new FinalReadResponder(); + } } diff --git a/src/Application/FinalResponders/FinalReadResponder.php b/src/Application/FinalResponders/FinalReadResponder.php new file mode 100644 index 0000000..8f79729 --- /dev/null +++ b/src/Application/FinalResponders/FinalReadResponder.php @@ -0,0 +1,27 @@ + $throwable->getMessage(), + ]; + + (new TwigPage())->respond( 'Theme/Error.twig', $data, HttpCode::INTERNAL_SERVER_ERROR ); + } +} \ No newline at end of file From c1bae596c577ea5b6b276c15610365fcbd71a42a Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Fri, 11 May 2018 15:02:34 +0200 Subject: [PATCH 17/62] Fix missing intialization of server config list --- src/Infrastructure/Configs/ServerConfigList.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Infrastructure/Configs/ServerConfigList.php b/src/Infrastructure/Configs/ServerConfigList.php index 874720c..26bbf06 100644 --- a/src/Infrastructure/Configs/ServerConfigList.php +++ b/src/Infrastructure/Configs/ServerConfigList.php @@ -39,6 +39,7 @@ public static function fromConfigFile( ?string $configFile = null ) : self private function loadServerConfigs( array $serverConfigList ) : void { + $this->servers = []; foreach ( $serverConfigList as $serverConfig ) { $this->servers[] = new ServerConfig( From c6bf4d6946098e1f0f6b38ff2598d54636c553f0 Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Fri, 11 May 2018 16:48:45 +0200 Subject: [PATCH 18/62] Add redis configs and data directory --- .gitignore | 2 + .redis/data/.gitkeep | 1 + .redis/redis.default.conf | 1052 +++++++++++++++++++++++++++++++++++++ .redis/redis.test.conf | 1 + docker-compose.yml | 5 +- 5 files changed, 1060 insertions(+), 1 deletion(-) create mode 100644 .redis/data/.gitkeep create mode 100644 .redis/redis.default.conf create mode 100644 .redis/redis.test.conf diff --git a/.gitignore b/.gitignore index 21248a5..a04f976 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ # local configs /config/servers.php /config/app.php +/.redis/data/* +!.gitkeep \ No newline at end of file diff --git a/.redis/data/.gitkeep b/.redis/data/.gitkeep new file mode 100644 index 0000000..b01b3ce --- /dev/null +++ b/.redis/data/.gitkeep @@ -0,0 +1 @@ +# Keep this directory \ No newline at end of file diff --git a/.redis/redis.default.conf b/.redis/redis.default.conf new file mode 100644 index 0000000..0f4d7f9 --- /dev/null +++ b/.redis/redis.default.conf @@ -0,0 +1,1052 @@ +# Redis configuration file example. +# +# Note that in order to read the configuration file, Redis must be +# started with the file path as first argument: +# +# ./redis-server /path/to/redis.conf + +# Note on units: when memory size is needed, it is possible to specify +# it in the usual form of 1k 5GB 4M and so forth: +# +# 1k => 1000 bytes +# 1kb => 1024 bytes +# 1m => 1000000 bytes +# 1mb => 1024*1024 bytes +# 1g => 1000000000 bytes +# 1gb => 1024*1024*1024 bytes +# +# units are case insensitive so 1GB 1Gb 1gB are all the same. + +################################## INCLUDES ################################### + +# Include one or more other config files here. This is useful if you +# have a standard template that goes to all Redis servers but also need +# to customize a few per-server settings. Include files can include +# other files, so use this wisely. +# +# Notice option "include" won't be rewritten by command "CONFIG REWRITE" +# from admin or Redis Sentinel. Since Redis always uses the last processed +# line as value of a configuration directive, you'd better put includes +# at the beginning of this file to avoid overwriting config change at runtime. +# +# If instead you are interested in using includes to override configuration +# options, it is better to use include as the last line. +# +# include /path/to/local.conf +# include /path/to/other.conf + +################################## NETWORK ##################################### + +# By default, if no "bind" configuration directive is specified, Redis listens +# for connections from all the network interfaces available on the server. +# It is possible to listen to just one or multiple selected interfaces using +# the "bind" configuration directive, followed by one or more IP addresses. +# +# Examples: +# +# bind 192.168.1.100 10.0.0.1 +# bind 127.0.0.1 ::1 +# +# ~~~ WARNING ~~~ If the computer running Redis is directly exposed to the +# internet, binding to all the interfaces is dangerous and will expose the +# instance to everybody on the internet. So by default we uncomment the +# following bind directive, that will force Redis to listen only into +# the IPv4 lookback interface address (this means Redis will be able to +# accept connections only from clients running into the same computer it +# is running). +# +# IF YOU ARE SURE YOU WANT YOUR INSTANCE TO LISTEN TO ALL THE INTERFACES +# JUST COMMENT THE FOLLOWING LINE. +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +bind 0.0.0.0 + +# Protected mode is a layer of security protection, in order to avoid that +# Redis instances left open on the internet are accessed and exploited. +# +# When protected mode is on and if: +# +# 1) The server is not binding explicitly to a set of addresses using the +# "bind" directive. +# 2) No password is configured. +# +# The server only accepts connections from clients connecting from the +# IPv4 and IPv6 loopback addresses 127.0.0.1 and ::1, and from Unix domain +# sockets. +# +# By default protected mode is enabled. You should disable it only if +# you are sure you want clients from other hosts to connect to Redis +# even if no authentication is configured, nor a specific set of interfaces +# are explicitly listed using the "bind" directive. +protected-mode yes + +# Accept connections on the specified port, default is 6379 (IANA #815344). +# If port 0 is specified Redis will not listen on a TCP socket. +port 6379 + +# TCP listen() backlog. +# +# In high requests-per-second environments you need an high backlog in order +# to avoid slow clients connections issues. Note that the Linux kernel +# will silently truncate it to the value of /proc/sys/net/core/somaxconn so +# make sure to raise both the value of somaxconn and tcp_max_syn_backlog +# in order to get the desired effect. +tcp-backlog 511 + +# Unix socket. +# +# Specify the path for the Unix socket that will be used to listen for +# incoming connections. There is no default, so Redis will not listen +# on a unix socket when not specified. +# +# unixsocket /tmp/redis.sock +# unixsocketperm 700 + +# Close the connection after a client is idle for N seconds (0 to disable) +timeout 0 + +# TCP keepalive. +# +# If non-zero, use SO_KEEPALIVE to send TCP ACKs to clients in absence +# of communication. This is useful for two reasons: +# +# 1) Detect dead peers. +# 2) Take the connection alive from the point of view of network +# equipment in the middle. +# +# On Linux, the specified value (in seconds) is the period used to send ACKs. +# Note that to close the connection the double of the time is needed. +# On other kernels the period depends on the kernel configuration. +# +# A reasonable value for this option is 300 seconds, which is the new +# Redis default starting with Redis 3.2.1. +tcp-keepalive 300 + +################################# GENERAL ##################################### + +# By default Redis does not run as a daemon. Use 'yes' if you need it. +# Note that Redis will write a pid file in /var/run/redis.pid when daemonized. +daemonize no + +# If you run Redis from upstart or systemd, Redis can interact with your +# supervision tree. Options: +# supervised no - no supervision interaction +# supervised upstart - signal upstart by putting Redis into SIGSTOP mode +# supervised systemd - signal systemd by writing READY=1 to $NOTIFY_SOCKET +# supervised auto - detect upstart or systemd method based on +# UPSTART_JOB or NOTIFY_SOCKET environment variables +# Note: these supervision methods only signal "process is ready." +# They do not enable continuous liveness pings back to your supervisor. +supervised no + +# If a pid file is specified, Redis writes it where specified at startup +# and removes it at exit. +# +# When the server runs non daemonized, no pid file is created if none is +# specified in the configuration. When the server is daemonized, the pid file +# is used even if not specified, defaulting to "/var/run/redis.pid". +# +# Creating a pid file is best effort: if Redis is not able to create it +# nothing bad happens, the server will start and run normally. +pidfile /var/run/redis_6379.pid + +# Specify the server verbosity level. +# This can be one of: +# debug (a lot of information, useful for development/testing) +# verbose (many rarely useful info, but not a mess like the debug level) +# notice (moderately verbose, what you want in production probably) +# warning (only very important / critical messages are logged) +loglevel notice + +# Specify the log file name. Also the empty string can be used to force +# Redis to log on the standard output. Note that if you use standard +# output for logging but daemonize, logs will be sent to /dev/null +logfile "" + +# To enable logging to the system logger, just set 'syslog-enabled' to yes, +# and optionally update the other syslog parameters to suit your needs. +# syslog-enabled no + +# Specify the syslog identity. +# syslog-ident redis + +# Specify the syslog facility. Must be USER or between LOCAL0-LOCAL7. +# syslog-facility local0 + +# Set the number of databases. The default database is DB 0, you can select +# a different one on a per-connection basis using SELECT where +# dbid is a number between 0 and 'databases'-1 +databases 16 + +################################ SNAPSHOTTING ################################ +# +# Save the DB on disk: +# +# save +# +# Will save the DB if both the given number of seconds and the given +# number of write operations against the DB occurred. +# +# In the example below the behaviour will be to save: +# after 900 sec (15 min) if at least 1 key changed +# after 300 sec (5 min) if at least 10 keys changed +# after 60 sec if at least 10000 keys changed +# +# Note: you can disable saving completely by commenting out all "save" lines. +# +# It is also possible to remove all the previously configured save +# points by adding a save directive with a single empty string argument +# like in the following example: +# +# save "" + +save 900 1 +save 300 10 +save 60 10000 + +# By default Redis will stop accepting writes if RDB snapshots are enabled +# (at least one save point) and the latest background save failed. +# This will make the user aware (in a hard way) that data is not persisting +# on disk properly, otherwise chances are that no one will notice and some +# disaster will happen. +# +# If the background saving process will start working again Redis will +# automatically allow writes again. +# +# However if you have setup your proper monitoring of the Redis server +# and persistence, you may want to disable this feature so that Redis will +# continue to work as usual even if there are problems with disk, +# permissions, and so forth. +stop-writes-on-bgsave-error yes + +# Compress string objects using LZF when dump .rdb databases? +# For default that's set to 'yes' as it's almost always a win. +# If you want to save some CPU in the saving child set it to 'no' but +# the dataset will likely be bigger if you have compressible values or keys. +rdbcompression yes + +# Since version 5 of RDB a CRC64 checksum is placed at the end of the file. +# This makes the format more resistant to corruption but there is a performance +# hit to pay (around 10%) when saving and loading RDB files, so you can disable it +# for maximum performances. +# +# RDB files created with checksum disabled have a checksum of zero that will +# tell the loading code to skip the check. +rdbchecksum yes + +# The filename where to dump the DB +dbfilename dump.rdb + +# The working directory. +# +# The DB will be written inside this directory, with the filename specified +# above using the 'dbfilename' configuration directive. +# +# The Append Only File will also be created inside this directory. +# +# Note that you must specify a directory here, not a file name. +dir ./ + +################################# REPLICATION ################################# + +# Master-Slave replication. Use slaveof to make a Redis instance a copy of +# another Redis server. A few things to understand ASAP about Redis replication. +# +# 1) Redis replication is asynchronous, but you can configure a master to +# stop accepting writes if it appears to be not connected with at least +# a given number of slaves. +# 2) Redis slaves are able to perform a partial resynchronization with the +# master if the replication link is lost for a relatively small amount of +# time. You may want to configure the replication backlog size (see the next +# sections of this file) with a sensible value depending on your needs. +# 3) Replication is automatic and does not need user intervention. After a +# network partition slaves automatically try to reconnect to masters +# and resynchronize with them. +# +# slaveof + +# If the master is password protected (using the "requirepass" configuration +# directive below) it is possible to tell the slave to authenticate before +# starting the replication synchronization process, otherwise the master will +# refuse the slave request. +# +# masterauth + +# When a slave loses its connection with the master, or when the replication +# is still in progress, the slave can act in two different ways: +# +# 1) if slave-serve-stale-data is set to 'yes' (the default) the slave will +# still reply to client requests, possibly with out of date data, or the +# data set may just be empty if this is the first synchronization. +# +# 2) if slave-serve-stale-data is set to 'no' the slave will reply with +# an error "SYNC with master in progress" to all the kind of commands +# but to INFO and SLAVEOF. +# +slave-serve-stale-data yes + +# You can configure a slave instance to accept writes or not. Writing against +# a slave instance may be useful to store some ephemeral data (because data +# written on a slave will be easily deleted after resync with the master) but +# may also cause problems if clients are writing to it because of a +# misconfiguration. +# +# Since Redis 2.6 by default slaves are read-only. +# +# Note: read only slaves are not designed to be exposed to untrusted clients +# on the internet. It's just a protection layer against misuse of the instance. +# Still a read only slave exports by default all the administrative commands +# such as CONFIG, DEBUG, and so forth. To a limited extent you can improve +# security of read only slaves using 'rename-command' to shadow all the +# administrative / dangerous commands. +slave-read-only yes + +# Replication SYNC strategy: disk or socket. +# +# ------------------------------------------------------- +# WARNING: DISKLESS REPLICATION IS EXPERIMENTAL CURRENTLY +# ------------------------------------------------------- +# +# New slaves and reconnecting slaves that are not able to continue the replication +# process just receiving differences, need to do what is called a "full +# synchronization". An RDB file is transmitted from the master to the slaves. +# The transmission can happen in two different ways: +# +# 1) Disk-backed: The Redis master creates a new process that writes the RDB +# file on disk. Later the file is transferred by the parent +# process to the slaves incrementally. +# 2) Diskless: The Redis master creates a new process that directly writes the +# RDB file to slave sockets, without touching the disk at all. +# +# With disk-backed replication, while the RDB file is generated, more slaves +# can be queued and served with the RDB file as soon as the current child producing +# the RDB file finishes its work. With diskless replication instead once +# the transfer starts, new slaves arriving will be queued and a new transfer +# will start when the current one terminates. +# +# When diskless replication is used, the master waits a configurable amount of +# time (in seconds) before starting the transfer in the hope that multiple slaves +# will arrive and the transfer can be parallelized. +# +# With slow disks and fast (large bandwidth) networks, diskless replication +# works better. +repl-diskless-sync no + +# When diskless replication is enabled, it is possible to configure the delay +# the server waits in order to spawn the child that transfers the RDB via socket +# to the slaves. +# +# This is important since once the transfer starts, it is not possible to serve +# new slaves arriving, that will be queued for the next RDB transfer, so the server +# waits a delay in order to let more slaves arrive. +# +# The delay is specified in seconds, and by default is 5 seconds. To disable +# it entirely just set it to 0 seconds and the transfer will start ASAP. +repl-diskless-sync-delay 5 + +# Slaves send PINGs to server in a predefined interval. It's possible to change +# this interval with the repl_ping_slave_period option. The default value is 10 +# seconds. +# +# repl-ping-slave-period 10 + +# The following option sets the replication timeout for: +# +# 1) Bulk transfer I/O during SYNC, from the point of view of slave. +# 2) Master timeout from the point of view of slaves (data, pings). +# 3) Slave timeout from the point of view of masters (REPLCONF ACK pings). +# +# It is important to make sure that this value is greater than the value +# specified for repl-ping-slave-period otherwise a timeout will be detected +# every time there is low traffic between the master and the slave. +# +# repl-timeout 60 + +# Disable TCP_NODELAY on the slave socket after SYNC? +# +# If you select "yes" Redis will use a smaller number of TCP packets and +# less bandwidth to send data to slaves. But this can add a delay for +# the data to appear on the slave side, up to 40 milliseconds with +# Linux kernels using a default configuration. +# +# If you select "no" the delay for data to appear on the slave side will +# be reduced but more bandwidth will be used for replication. +# +# By default we optimize for low latency, but in very high traffic conditions +# or when the master and slaves are many hops away, turning this to "yes" may +# be a good idea. +repl-disable-tcp-nodelay no + +# Set the replication backlog size. The backlog is a buffer that accumulates +# slave data when slaves are disconnected for some time, so that when a slave +# wants to reconnect again, often a full resync is not needed, but a partial +# resync is enough, just passing the portion of data the slave missed while +# disconnected. +# +# The bigger the replication backlog, the longer the time the slave can be +# disconnected and later be able to perform a partial resynchronization. +# +# The backlog is only allocated once there is at least a slave connected. +# +# repl-backlog-size 1mb + +# After a master has no longer connected slaves for some time, the backlog +# will be freed. The following option configures the amount of seconds that +# need to elapse, starting from the time the last slave disconnected, for +# the backlog buffer to be freed. +# +# A value of 0 means to never release the backlog. +# +# repl-backlog-ttl 3600 + +# The slave priority is an integer number published by Redis in the INFO output. +# It is used by Redis Sentinel in order to select a slave to promote into a +# master if the master is no longer working correctly. +# +# A slave with a low priority number is considered better for promotion, so +# for instance if there are three slaves with priority 10, 100, 25 Sentinel will +# pick the one with priority 10, that is the lowest. +# +# However a special priority of 0 marks the slave as not able to perform the +# role of master, so a slave with priority of 0 will never be selected by +# Redis Sentinel for promotion. +# +# By default the priority is 100. +slave-priority 100 + +# It is possible for a master to stop accepting writes if there are less than +# N slaves connected, having a lag less or equal than M seconds. +# +# The N slaves need to be in "online" state. +# +# The lag in seconds, that must be <= the specified value, is calculated from +# the last ping received from the slave, that is usually sent every second. +# +# This option does not GUARANTEE that N replicas will accept the write, but +# will limit the window of exposure for lost writes in case not enough slaves +# are available, to the specified number of seconds. +# +# For example to require at least 3 slaves with a lag <= 10 seconds use: +# +# min-slaves-to-write 3 +# min-slaves-max-lag 10 +# +# Setting one or the other to 0 disables the feature. +# +# By default min-slaves-to-write is set to 0 (feature disabled) and +# min-slaves-max-lag is set to 10. + +# A Redis master is able to list the address and port of the attached +# slaves in different ways. For example the "INFO replication" section +# offers this information, which is used, among other tools, by +# Redis Sentinel in order to discover slave instances. +# Another place where this info is available is in the output of the +# "ROLE" command of a masteer. +# +# The listed IP and address normally reported by a slave is obtained +# in the following way: +# +# IP: The address is auto detected by checking the peer address +# of the socket used by the slave to connect with the master. +# +# Port: The port is communicated by the slave during the replication +# handshake, and is normally the port that the slave is using to +# list for connections. +# +# However when port forwarding or Network Address Translation (NAT) is +# used, the slave may be actually reachable via different IP and port +# pairs. The following two options can be used by a slave in order to +# report to its master a specific set of IP and port, so that both INFO +# and ROLE will report those values. +# +# There is no need to use both the options if you need to override just +# the port or the IP address. +# +# slave-announce-ip 5.5.5.5 +# slave-announce-port 1234 + +################################## SECURITY ################################### + +# Require clients to issue AUTH before processing any other +# commands. This might be useful in environments in which you do not trust +# others with access to the host running redis-server. +# +# This should stay commented out for backward compatibility and because most +# people do not need auth (e.g. they run their own servers). +# +# Warning: since Redis is pretty fast an outside user can try up to +# 150k passwords per second against a good box. This means that you should +# use a very strong password otherwise it will be very easy to break. +# +# requirepass foobared + +# Command renaming. +# +# It is possible to change the name of dangerous commands in a shared +# environment. For instance the CONFIG command may be renamed into something +# hard to guess so that it will still be available for internal-use tools +# but not available for general clients. +# +# Example: +# +# rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c52 +# +# It is also possible to completely kill a command by renaming it into +# an empty string: +# +# rename-command CONFIG "" +# +# Please note that changing the name of commands that are logged into the +# AOF file or transmitted to slaves may cause problems. + +################################### LIMITS #################################### + +# Set the max number of connected clients at the same time. By default +# this limit is set to 10000 clients, however if the Redis server is not +# able to configure the process file limit to allow for the specified limit +# the max number of allowed clients is set to the current file limit +# minus 32 (as Redis reserves a few file descriptors for internal uses). +# +# Once the limit is reached Redis will close all the new connections sending +# an error 'max number of clients reached'. +# +# maxclients 10000 + +# Don't use more memory than the specified amount of bytes. +# When the memory limit is reached Redis will try to remove keys +# according to the eviction policy selected (see maxmemory-policy). +# +# If Redis can't remove keys according to the policy, or if the policy is +# set to 'noeviction', Redis will start to reply with errors to commands +# that would use more memory, like SET, LPUSH, and so on, and will continue +# to reply to read-only commands like GET. +# +# This option is usually useful when using Redis as an LRU cache, or to set +# a hard memory limit for an instance (using the 'noeviction' policy). +# +# WARNING: If you have slaves attached to an instance with maxmemory on, +# the size of the output buffers needed to feed the slaves are subtracted +# from the used memory count, so that network problems / resyncs will +# not trigger a loop where keys are evicted, and in turn the output +# buffer of slaves is full with DELs of keys evicted triggering the deletion +# of more keys, and so forth until the database is completely emptied. +# +# In short... if you have slaves attached it is suggested that you set a lower +# limit for maxmemory so that there is some free RAM on the system for slave +# output buffers (but this is not needed if the policy is 'noeviction'). +# +# maxmemory + +# MAXMEMORY POLICY: how Redis will select what to remove when maxmemory +# is reached. You can select among five behaviors: +# +# volatile-lru -> remove the key with an expire set using an LRU algorithm +# allkeys-lru -> remove any key according to the LRU algorithm +# volatile-random -> remove a random key with an expire set +# allkeys-random -> remove a random key, any key +# volatile-ttl -> remove the key with the nearest expire time (minor TTL) +# noeviction -> don't expire at all, just return an error on write operations +# +# Note: with any of the above policies, Redis will return an error on write +# operations, when there are no suitable keys for eviction. +# +# At the date of writing these commands are: set setnx setex append +# incr decr rpush lpush rpushx lpushx linsert lset rpoplpush sadd +# sinter sinterstore sunion sunionstore sdiff sdiffstore zadd zincrby +# zunionstore zinterstore hset hsetnx hmset hincrby incrby decrby +# getset mset msetnx exec sort +# +# The default is: +# +# maxmemory-policy noeviction + +# LRU and minimal TTL algorithms are not precise algorithms but approximated +# algorithms (in order to save memory), so you can tune it for speed or +# accuracy. For default Redis will check five keys and pick the one that was +# used less recently, you can change the sample size using the following +# configuration directive. +# +# The default of 5 produces good enough results. 10 Approximates very closely +# true LRU but costs a bit more CPU. 3 is very fast but not very accurate. +# +# maxmemory-samples 5 + +############################## APPEND ONLY MODE ############################### + +# By default Redis asynchronously dumps the dataset on disk. This mode is +# good enough in many applications, but an issue with the Redis process or +# a power outage may result into a few minutes of writes lost (depending on +# the configured save points). +# +# The Append Only File is an alternative persistence mode that provides +# much better durability. For instance using the default data fsync policy +# (see later in the config file) Redis can lose just one second of writes in a +# dramatic event like a server power outage, or a single write if something +# wrong with the Redis process itself happens, but the operating system is +# still running correctly. +# +# AOF and RDB persistence can be enabled at the same time without problems. +# If the AOF is enabled on startup Redis will load the AOF, that is the file +# with the better durability guarantees. +# +# Please check http://redis.io/topics/persistence for more information. + +appendonly no + +# The name of the append only file (default: "appendonly.aof") + +appendfilename "appendonly.aof" + +# The fsync() call tells the Operating System to actually write data on disk +# instead of waiting for more data in the output buffer. Some OS will really flush +# data on disk, some other OS will just try to do it ASAP. +# +# Redis supports three different modes: +# +# no: don't fsync, just let the OS flush the data when it wants. Faster. +# always: fsync after every write to the append only log. Slow, Safest. +# everysec: fsync only one time every second. Compromise. +# +# The default is "everysec", as that's usually the right compromise between +# speed and data safety. It's up to you to understand if you can relax this to +# "no" that will let the operating system flush the output buffer when +# it wants, for better performances (but if you can live with the idea of +# some data loss consider the default persistence mode that's snapshotting), +# or on the contrary, use "always" that's very slow but a bit safer than +# everysec. +# +# More details please check the following article: +# http://antirez.com/post/redis-persistence-demystified.html +# +# If unsure, use "everysec". + +# appendfsync always +appendfsync everysec +# appendfsync no + +# When the AOF fsync policy is set to always or everysec, and a background +# saving process (a background save or AOF log background rewriting) is +# performing a lot of I/O against the disk, in some Linux configurations +# Redis may block too long on the fsync() call. Note that there is no fix for +# this currently, as even performing fsync in a different thread will block +# our synchronous write(2) call. +# +# In order to mitigate this problem it's possible to use the following option +# that will prevent fsync() from being called in the main process while a +# BGSAVE or BGREWRITEAOF is in progress. +# +# This means that while another child is saving, the durability of Redis is +# the same as "appendfsync none". In practical terms, this means that it is +# possible to lose up to 30 seconds of log in the worst scenario (with the +# default Linux settings). +# +# If you have latency problems turn this to "yes". Otherwise leave it as +# "no" that is the safest pick from the point of view of durability. + +no-appendfsync-on-rewrite no + +# Automatic rewrite of the append only file. +# Redis is able to automatically rewrite the log file implicitly calling +# BGREWRITEAOF when the AOF log size grows by the specified percentage. +# +# This is how it works: Redis remembers the size of the AOF file after the +# latest rewrite (if no rewrite has happened since the restart, the size of +# the AOF at startup is used). +# +# This base size is compared to the current size. If the current size is +# bigger than the specified percentage, the rewrite is triggered. Also +# you need to specify a minimal size for the AOF file to be rewritten, this +# is useful to avoid rewriting the AOF file even if the percentage increase +# is reached but it is still pretty small. +# +# Specify a percentage of zero in order to disable the automatic AOF +# rewrite feature. + +auto-aof-rewrite-percentage 100 +auto-aof-rewrite-min-size 64mb + +# An AOF file may be found to be truncated at the end during the Redis +# startup process, when the AOF data gets loaded back into memory. +# This may happen when the system where Redis is running +# crashes, especially when an ext4 filesystem is mounted without the +# data=ordered option (however this can't happen when Redis itself +# crashes or aborts but the operating system still works correctly). +# +# Redis can either exit with an error when this happens, or load as much +# data as possible (the default now) and start if the AOF file is found +# to be truncated at the end. The following option controls this behavior. +# +# If aof-load-truncated is set to yes, a truncated AOF file is loaded and +# the Redis server starts emitting a log to inform the user of the event. +# Otherwise if the option is set to no, the server aborts with an error +# and refuses to start. When the option is set to no, the user requires +# to fix the AOF file using the "redis-check-aof" utility before to restart +# the server. +# +# Note that if the AOF file will be found to be corrupted in the middle +# the server will still exit with an error. This option only applies when +# Redis will try to read more data from the AOF file but not enough bytes +# will be found. +aof-load-truncated yes + +################################ LUA SCRIPTING ############################### + +# Max execution time of a Lua script in milliseconds. +# +# If the maximum execution time is reached Redis will log that a script is +# still in execution after the maximum allowed time and will start to +# reply to queries with an error. +# +# When a long running script exceeds the maximum execution time only the +# SCRIPT KILL and SHUTDOWN NOSAVE commands are available. The first can be +# used to stop a script that did not yet called write commands. The second +# is the only way to shut down the server in the case a write command was +# already issued by the script but the user doesn't want to wait for the natural +# termination of the script. +# +# Set it to 0 or a negative value for unlimited execution without warnings. +lua-time-limit 5000 + +################################ REDIS CLUSTER ############################### +# +# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +# WARNING EXPERIMENTAL: Redis Cluster is considered to be stable code, however +# in order to mark it as "mature" we need to wait for a non trivial percentage +# of users to deploy it in production. +# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +# +# Normal Redis instances can't be part of a Redis Cluster; only nodes that are +# started as cluster nodes can. In order to start a Redis instance as a +# cluster node enable the cluster support uncommenting the following: +# +# cluster-enabled yes + +# Every cluster node has a cluster configuration file. This file is not +# intended to be edited by hand. It is created and updated by Redis nodes. +# Every Redis Cluster node requires a different cluster configuration file. +# Make sure that instances running in the same system do not have +# overlapping cluster configuration file names. +# +# cluster-config-file nodes-6379.conf + +# Cluster node timeout is the amount of milliseconds a node must be unreachable +# for it to be considered in failure state. +# Most other internal time limits are multiple of the node timeout. +# +# cluster-node-timeout 15000 + +# A slave of a failing master will avoid to start a failover if its data +# looks too old. +# +# There is no simple way for a slave to actually have a exact measure of +# its "data age", so the following two checks are performed: +# +# 1) If there are multiple slaves able to failover, they exchange messages +# in order to try to give an advantage to the slave with the best +# replication offset (more data from the master processed). +# Slaves will try to get their rank by offset, and apply to the start +# of the failover a delay proportional to their rank. +# +# 2) Every single slave computes the time of the last interaction with +# its master. This can be the last ping or command received (if the master +# is still in the "connected" state), or the time that elapsed since the +# disconnection with the master (if the replication link is currently down). +# If the last interaction is too old, the slave will not try to failover +# at all. +# +# The point "2" can be tuned by user. Specifically a slave will not perform +# the failover if, since the last interaction with the master, the time +# elapsed is greater than: +# +# (node-timeout * slave-validity-factor) + repl-ping-slave-period +# +# So for example if node-timeout is 30 seconds, and the slave-validity-factor +# is 10, and assuming a default repl-ping-slave-period of 10 seconds, the +# slave will not try to failover if it was not able to talk with the master +# for longer than 310 seconds. +# +# A large slave-validity-factor may allow slaves with too old data to failover +# a master, while a too small value may prevent the cluster from being able to +# elect a slave at all. +# +# For maximum availability, it is possible to set the slave-validity-factor +# to a value of 0, which means, that slaves will always try to failover the +# master regardless of the last time they interacted with the master. +# (However they'll always try to apply a delay proportional to their +# offset rank). +# +# Zero is the only value able to guarantee that when all the partitions heal +# the cluster will always be able to continue. +# +# cluster-slave-validity-factor 10 + +# Cluster slaves are able to migrate to orphaned masters, that are masters +# that are left without working slaves. This improves the cluster ability +# to resist to failures as otherwise an orphaned master can't be failed over +# in case of failure if it has no working slaves. +# +# Slaves migrate to orphaned masters only if there are still at least a +# given number of other working slaves for their old master. This number +# is the "migration barrier". A migration barrier of 1 means that a slave +# will migrate only if there is at least 1 other working slave for its master +# and so forth. It usually reflects the number of slaves you want for every +# master in your cluster. +# +# Default is 1 (slaves migrate only if their masters remain with at least +# one slave). To disable migration just set it to a very large value. +# A value of 0 can be set but is useful only for debugging and dangerous +# in production. +# +# cluster-migration-barrier 1 + +# By default Redis Cluster nodes stop accepting queries if they detect there +# is at least an hash slot uncovered (no available node is serving it). +# This way if the cluster is partially down (for example a range of hash slots +# are no longer covered) all the cluster becomes, eventually, unavailable. +# It automatically returns available as soon as all the slots are covered again. +# +# However sometimes you want the subset of the cluster which is working, +# to continue to accept queries for the part of the key space that is still +# covered. In order to do so, just set the cluster-require-full-coverage +# option to no. +# +# cluster-require-full-coverage yes + +# In order to setup your cluster make sure to read the documentation +# available at http://redis.io web site. + +################################## SLOW LOG ################################### + +# The Redis Slow Log is a system to log queries that exceeded a specified +# execution time. The execution time does not include the I/O operations +# like talking with the client, sending the reply and so forth, +# but just the time needed to actually execute the command (this is the only +# stage of command execution where the thread is blocked and can not serve +# other requests in the meantime). +# +# You can configure the slow log with two parameters: one tells Redis +# what is the execution time, in microseconds, to exceed in order for the +# command to get logged, and the other parameter is the length of the +# slow log. When a new command is logged the oldest one is removed from the +# queue of logged commands. + +# The following time is expressed in microseconds, so 1000000 is equivalent +# to one second. Note that a negative number disables the slow log, while +# a value of zero forces the logging of every command. +slowlog-log-slower-than 10000 + +# There is no limit to this length. Just be aware that it will consume memory. +# You can reclaim memory used by the slow log with SLOWLOG RESET. +slowlog-max-len 128 + +################################ LATENCY MONITOR ############################## + +# The Redis latency monitoring subsystem samples different operations +# at runtime in order to collect data related to possible sources of +# latency of a Redis instance. +# +# Via the LATENCY command this information is available to the user that can +# print graphs and obtain reports. +# +# The system only logs operations that were performed in a time equal or +# greater than the amount of milliseconds specified via the +# latency-monitor-threshold configuration directive. When its value is set +# to zero, the latency monitor is turned off. +# +# By default latency monitoring is disabled since it is mostly not needed +# if you don't have latency issues, and collecting data has a performance +# impact, that while very small, can be measured under big load. Latency +# monitoring can easily be enabled at runtime using the command +# "CONFIG SET latency-monitor-threshold " if needed. +latency-monitor-threshold 0 + +############################# EVENT NOTIFICATION ############################## + +# Redis can notify Pub/Sub clients about events happening in the key space. +# This feature is documented at http://redis.io/topics/notifications +# +# For instance if keyspace events notification is enabled, and a client +# performs a DEL operation on key "foo" stored in the Database 0, two +# messages will be published via Pub/Sub: +# +# PUBLISH __keyspace@0__:foo del +# PUBLISH __keyevent@0__:del foo +# +# It is possible to select the events that Redis will notify among a set +# of classes. Every class is identified by a single character: +# +# K Keyspace events, published with __keyspace@__ prefix. +# E Keyevent events, published with __keyevent@__ prefix. +# g Generic commands (non-type specific) like DEL, EXPIRE, RENAME, ... +# $ String commands +# l List commands +# s Set commands +# h Hash commands +# z Sorted set commands +# x Expired events (events generated every time a key expires) +# e Evicted events (events generated when a key is evicted for maxmemory) +# A Alias for g$lshzxe, so that the "AKE" string means all the events. +# +# The "notify-keyspace-events" takes as argument a string that is composed +# of zero or multiple characters. The empty string means that notifications +# are disabled. +# +# Example: to enable list and generic events, from the point of view of the +# event name, use: +# +# notify-keyspace-events Elg +# +# Example 2: to get the stream of the expired keys subscribing to channel +# name __keyevent@0__:expired use: +# +# notify-keyspace-events Ex +# +# By default all notifications are disabled because most users don't need +# this feature and the feature has some overhead. Note that if you don't +# specify at least one of K or E, no events will be delivered. +notify-keyspace-events "" + +############################### ADVANCED CONFIG ############################### + +# Hashes are encoded using a memory efficient data structure when they have a +# small number of entries, and the biggest entry does not exceed a given +# threshold. These thresholds can be configured using the following directives. +hash-max-ziplist-entries 512 +hash-max-ziplist-value 64 + +# Lists are also encoded in a special way to save a lot of space. +# The number of entries allowed per internal list node can be specified +# as a fixed maximum size or a maximum number of elements. +# For a fixed maximum size, use -5 through -1, meaning: +# -5: max size: 64 Kb <-- not recommended for normal workloads +# -4: max size: 32 Kb <-- not recommended +# -3: max size: 16 Kb <-- probably not recommended +# -2: max size: 8 Kb <-- good +# -1: max size: 4 Kb <-- good +# Positive numbers mean store up to _exactly_ that number of elements +# per list node. +# The highest performing option is usually -2 (8 Kb size) or -1 (4 Kb size), +# but if your use case is unique, adjust the settings as necessary. +list-max-ziplist-size -2 + +# Lists may also be compressed. +# Compress depth is the number of quicklist ziplist nodes from *each* side of +# the list to *exclude* from compression. The head and tail of the list +# are always uncompressed for fast push/pop operations. Settings are: +# 0: disable all list compression +# 1: depth 1 means "don't start compressing until after 1 node into the list, +# going from either the head or tail" +# So: [head]->node->node->...->node->[tail] +# [head], [tail] will always be uncompressed; inner nodes will compress. +# 2: [head]->[next]->node->node->...->node->[prev]->[tail] +# 2 here means: don't compress head or head->next or tail->prev or tail, +# but compress all nodes between them. +# 3: [head]->[next]->[next]->node->node->...->node->[prev]->[prev]->[tail] +# etc. +list-compress-depth 0 + +# Sets have a special encoding in just one case: when a set is composed +# of just strings that happen to be integers in radix 10 in the range +# of 64 bit signed integers. +# The following configuration setting sets the limit in the size of the +# set in order to use this special memory saving encoding. +set-max-intset-entries 512 + +# Similarly to hashes and lists, sorted sets are also specially encoded in +# order to save a lot of space. This encoding is only used when the length and +# elements of a sorted set are below the following limits: +zset-max-ziplist-entries 128 +zset-max-ziplist-value 64 + +# HyperLogLog sparse representation bytes limit. The limit includes the +# 16 bytes header. When an HyperLogLog using the sparse representation crosses +# this limit, it is converted into the dense representation. +# +# A value greater than 16000 is totally useless, since at that point the +# dense representation is more memory efficient. +# +# The suggested value is ~ 3000 in order to have the benefits of +# the space efficient encoding without slowing down too much PFADD, +# which is O(N) with the sparse encoding. The value can be raised to +# ~ 10000 when CPU is not a concern, but space is, and the data set is +# composed of many HyperLogLogs with cardinality in the 0 - 15000 range. +hll-sparse-max-bytes 3000 + +# Active rehashing uses 1 millisecond every 100 milliseconds of CPU time in +# order to help rehashing the main Redis hash table (the one mapping top-level +# keys to values). The hash table implementation Redis uses (see dict.c) +# performs a lazy rehashing: the more operation you run into a hash table +# that is rehashing, the more rehashing "steps" are performed, so if the +# server is idle the rehashing is never complete and some more memory is used +# by the hash table. +# +# The default is to use this millisecond 10 times every second in order to +# actively rehash the main dictionaries, freeing memory when possible. +# +# If unsure: +# use "activerehashing no" if you have hard latency requirements and it is +# not a good thing in your environment that Redis can reply from time to time +# to queries with 2 milliseconds delay. +# +# use "activerehashing yes" if you don't have such hard requirements but +# want to free memory asap when possible. +activerehashing yes + +# The client output buffer limits can be used to force disconnection of clients +# that are not reading data from the server fast enough for some reason (a +# common reason is that a Pub/Sub client can't consume messages as fast as the +# publisher can produce them). +# +# The limit can be set differently for the three different classes of clients: +# +# normal -> normal clients including MONITOR clients +# slave -> slave clients +# pubsub -> clients subscribed to at least one pubsub channel or pattern +# +# The syntax of every client-output-buffer-limit directive is the following: +# +# client-output-buffer-limit +# +# A client is immediately disconnected once the hard limit is reached, or if +# the soft limit is reached and remains reached for the specified number of +# seconds (continuously). +# So for instance if the hard limit is 32 megabytes and the soft limit is +# 16 megabytes / 10 seconds, the client will get disconnected immediately +# if the size of the output buffers reach 32 megabytes, but will also get +# disconnected if the client reaches 16 megabytes and continuously overcomes +# the limit for 10 seconds. +# +# By default normal clients are not limited because they don't receive data +# without asking (in a push way), but just after a request, so only +# asynchronous clients may create a scenario where data is requested faster +# than it can read. +# +# Instead there is a default limit for pubsub and slave clients, since +# subscribers and slaves receive data in a push fashion. +# +# Both the hard or the soft limit can be disabled by setting them to zero. +client-output-buffer-limit normal 0 0 0 +client-output-buffer-limit slave 256mb 64mb 60 +client-output-buffer-limit pubsub 32mb 8mb 60 + +# Redis calls an internal function to perform many background tasks, like +# closing connections of clients in timeout, purging expired keys that are +# never requested, and so forth. +# +# Not all tasks are performed with the same frequency, but Redis checks for +# tasks to perform according to the specified "hz" value. +# +# By default "hz" is set to 10. Raising the value will use more CPU when +# Redis is idle, but at the same time will make Redis more responsive when +# there are many keys expiring at the same time, and timeouts may be +# handled with more precision. +# +# The range is between 1 and 500, however a value over 100 is usually not +# a good idea. Most users should use the default of 10 and raise this up to +# 100 only in environments where very low latency is required. +hz 10 + +# When a child rewrites the AOF file, if the following option is enabled +# the file will be fsync-ed every 32 MB of data generated. This is useful +# in order to commit the file to the disk more incrementally and avoid +# big latency spikes. +aof-rewrite-incremental-fsync yes \ No newline at end of file diff --git a/.redis/redis.test.conf b/.redis/redis.test.conf new file mode 100644 index 0000000..7ec3ba0 --- /dev/null +++ b/.redis/redis.test.conf @@ -0,0 +1 @@ +databases 1 \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 9698a38..9c441de 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,4 +5,7 @@ services: ports: - 6379:6379 volumes: - - ./:/repo \ No newline at end of file + - .redis/data:/data + - .redis/redis.default.conf:/usr/local/etc/redis/redis.conf + - ./:/repo + command: /usr/local/etc/redis/redis.conf \ No newline at end of file From 35c45d14859d7f247f6cb535a6204a1bfc679f6d Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Sun, 13 May 2018 00:27:19 +0200 Subject: [PATCH 19/62] Add interface for key data builders --- .../ReadModel/Interfaces/BuildsKeyData.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/Application/ReadModel/Interfaces/BuildsKeyData.php diff --git a/src/Application/ReadModel/Interfaces/BuildsKeyData.php b/src/Application/ReadModel/Interfaces/BuildsKeyData.php new file mode 100644 index 0000000..04819f6 --- /dev/null +++ b/src/Application/ReadModel/Interfaces/BuildsKeyData.php @@ -0,0 +1,12 @@ + Date: Sun, 13 May 2018 00:27:34 +0200 Subject: [PATCH 20/62] Add composite key data builder --- .../KeyDataBuilders/KeyDataBuilder.php | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 src/Application/ReadModel/KeyDataBuilders/KeyDataBuilder.php diff --git a/src/Application/ReadModel/KeyDataBuilders/KeyDataBuilder.php b/src/Application/ReadModel/KeyDataBuilders/KeyDataBuilder.php new file mode 100644 index 0000000..f167cf1 --- /dev/null +++ b/src/Application/ReadModel/KeyDataBuilders/KeyDataBuilder.php @@ -0,0 +1,46 @@ +builders = array_merge( [$builder], $builders ); + } + + public function canBuildKeyData( ProvidesKeyInfo $keyInfo, ProvidesKeyName $keyName ) : bool + { + return true; + } + + /** + * @param ProvidesKeyInfo $keyInfo + * @param ProvidesKeyName $keyName + * + * @throws KeyTypeNotImplemented + * @return ProvidesKeyData + */ + public function buildKeyData( ProvidesKeyInfo $keyInfo, ProvidesKeyName $keyName ) : ProvidesKeyData + { + foreach ( $this->builders as $builder ) + { + if ( $builder->canBuildKeyData( $keyInfo, $keyName ) ) + { + return $builder->buildKeyData( $keyInfo, $keyName ); + } + } + + throw new KeyTypeNotImplemented( 'Key type not implemented or supported: ' . $keyInfo->getType() ); + } +} \ No newline at end of file From 388499be0bc40fb6d6241dda18877b78820e9515 Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Sun, 13 May 2018 00:27:50 +0200 Subject: [PATCH 21/62] Add hash key data builder --- .../KeyDataBuilders/HashKeyDataBuilder.php | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 src/Application/ReadModel/KeyDataBuilders/HashKeyDataBuilder.php diff --git a/src/Application/ReadModel/KeyDataBuilders/HashKeyDataBuilder.php b/src/Application/ReadModel/KeyDataBuilders/HashKeyDataBuilder.php new file mode 100644 index 0000000..9cdf91e --- /dev/null +++ b/src/Application/ReadModel/KeyDataBuilders/HashKeyDataBuilder.php @@ -0,0 +1,75 @@ +manager = $manager; + $this->prettifier = $prettifier; + } + + public function canBuildKeyData( ProvidesKeyInfo $keyInfo, ProvidesKeyName $keyName ) : bool + { + return !$keyName->hasSubKey() && (KeyType::HASH === $keyInfo->getType()); + } + + /** + * @param ProvidesKeyInfo $keyInfo + * @param ProvidesKeyName $keyName + * + * @throws ConnectionFailedException + * @return ProvidesKeyData + */ + public function buildKeyData( ProvidesKeyInfo $keyInfo, ProvidesKeyName $keyName ) : ProvidesKeyData + { + $rawFields = []; + $hashValues = $this->manager->getAllHashValues( $keyName->getKeyName() ); + + foreach ( $hashValues as $hashKey => $hashValue ) + { + $rawFields[] = $this->getFieldOutput( $hashKey, $hashValue ); + } + + $prettyFields = []; + foreach ( $hashValues as $hashKey => $hashValue ) + { + $prettyHashValue = $this->prettifier->prettify( $hashValue ); + $prettyFields[] = $this->getFieldOutput( $hashKey, $prettyHashValue ); + } + + $rawKeyData = implode( self::FIELD_SEPARATOR, $rawFields ); + $keyData = implode( self::FIELD_SEPARATOR, $prettyFields ); + + return new KeyData( $keyData, $rawKeyData ); + } + + private function getFieldOutput( string $hashKey, string $hashValue ) : string + { + return sprintf( + "Hash key %s:\n%s\n%s", + $hashKey, + str_repeat( '=', 10 + strlen( (string)$hashKey ) ), + $hashValue + ); + } +} \ No newline at end of file From 88ea7fdc98bed85ecd564daa5136ca5b3efc9179 Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Sun, 13 May 2018 00:28:02 +0200 Subject: [PATCH 22/62] Add hash sub key data builder --- .../KeyDataBuilders/HashSubKeyDataBuilder.php | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 src/Application/ReadModel/KeyDataBuilders/HashSubKeyDataBuilder.php diff --git a/src/Application/ReadModel/KeyDataBuilders/HashSubKeyDataBuilder.php b/src/Application/ReadModel/KeyDataBuilders/HashSubKeyDataBuilder.php new file mode 100644 index 0000000..9626762 --- /dev/null +++ b/src/Application/ReadModel/KeyDataBuilders/HashSubKeyDataBuilder.php @@ -0,0 +1,48 @@ +manager = $manager; + $this->prettifier = $prettifier; + } + + public function canBuildKeyData( ProvidesKeyInfo $keyInfo, ProvidesKeyName $keyName ) : bool + { + return $keyName->hasSubKey() && (KeyType::HASH === $keyInfo->getType()); + } + + /** + * @param ProvidesKeyInfo $keyInfo + * @param ProvidesKeyName $keyName + * + * @throws ConnectionFailedException + * @return ProvidesKeyData + */ + public function buildKeyData( ProvidesKeyInfo $keyInfo, ProvidesKeyName $keyName ) : ProvidesKeyData + { + $rawKeyData = $this->manager->getHashValue( $keyName->getKeyName(), $keyName->getSubKey() ) ?: ''; + $keyData = $this->prettifier->prettify( $rawKeyData ); + + return new KeyData( $keyData, $rawKeyData ); + } +} \ No newline at end of file From d7ad75b9f9766892a65d7c56263ba7ee5d623ed0 Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Sun, 13 May 2018 00:28:12 +0200 Subject: [PATCH 23/62] Add list key data builder --- .../KeyDataBuilders/ListKeyDataBuilder.php | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 src/Application/ReadModel/KeyDataBuilders/ListKeyDataBuilder.php diff --git a/src/Application/ReadModel/KeyDataBuilders/ListKeyDataBuilder.php b/src/Application/ReadModel/KeyDataBuilders/ListKeyDataBuilder.php new file mode 100644 index 0000000..e2b6824 --- /dev/null +++ b/src/Application/ReadModel/KeyDataBuilders/ListKeyDataBuilder.php @@ -0,0 +1,65 @@ +manager = $manager; + $this->prettifier = $prettifier; + } + + public function canBuildKeyData( ProvidesKeyInfo $keyInfo, ProvidesKeyName $keyName ) : bool + { + return !$keyName->hasSubKey() && (KeyType::LIST === $keyInfo->getType()); + } + + public function buildKeyData( ProvidesKeyInfo $keyInfo, ProvidesKeyName $keyName ) : ProvidesKeyData + { + $rawElements = []; + foreach ( $keyInfo->getSubItems() as $index => $element ) + { + $rawElements[] = $this->getElementOutput( $index, $element ); + } + + $prettyElements = []; + foreach ( $keyInfo->getSubItems() as $index => $element ) + { + $prettyElement = $this->prettifier->prettify( $element ); + $prettyElements[] = $this->getElementOutput( $index, $prettyElement ); + } + + $rawKeyData = implode( self::ELEMENT_SEPARATOR, $rawElements ); + $keyData = implode( self::ELEMENT_SEPARATOR, $prettyElements ); + + return new KeyData( $keyData, $rawKeyData ); + } + + private function getElementOutput( int $index, string $element ) : string + { + return sprintf( + "Element %d:\n%s\n%s", + $index, + str_repeat( '=', 9 + strlen( (string)$index ) ), + $element + ); + } +} \ No newline at end of file From 5aa9b9c26888fb3da912100dcd4a697758fd8982 Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Sun, 13 May 2018 00:28:21 +0200 Subject: [PATCH 24/62] Add list sub key data builder --- .../KeyDataBuilders/ListSubKeyDataBuilder.php | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 src/Application/ReadModel/KeyDataBuilders/ListSubKeyDataBuilder.php diff --git a/src/Application/ReadModel/KeyDataBuilders/ListSubKeyDataBuilder.php b/src/Application/ReadModel/KeyDataBuilders/ListSubKeyDataBuilder.php new file mode 100644 index 0000000..aae5c88 --- /dev/null +++ b/src/Application/ReadModel/KeyDataBuilders/ListSubKeyDataBuilder.php @@ -0,0 +1,48 @@ +manager = $manager; + $this->prettifier = $prettifier; + } + + public function canBuildKeyData( ProvidesKeyInfo $keyInfo, ProvidesKeyName $keyName ) : bool + { + return $keyName->hasSubKey() && (KeyType::LIST === $keyInfo->getType()); + } + + /** + * @param ProvidesKeyInfo $keyInfo + * @param ProvidesKeyName $keyName + * + * @throws ConnectionFailedException + * @return ProvidesKeyData + */ + public function buildKeyData( ProvidesKeyInfo $keyInfo, ProvidesKeyName $keyName ) : ProvidesKeyData + { + $rawKeyData = $this->manager->getListValue( $keyName->getKeyName(), (int)$keyName->getSubKey() ) ?: ''; + $keyData = $this->prettifier->prettify( $rawKeyData ); + + return new KeyData( $keyData, $rawKeyData ); + } +} \ No newline at end of file From 215d5dba0b5d7b8bbcbaf905fc8eb7840ff6c1ab Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Sun, 13 May 2018 00:28:29 +0200 Subject: [PATCH 25/62] Add set key data builder --- .../KeyDataBuilders/SetKeyDataBuilder.php | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 src/Application/ReadModel/KeyDataBuilders/SetKeyDataBuilder.php diff --git a/src/Application/ReadModel/KeyDataBuilders/SetKeyDataBuilder.php b/src/Application/ReadModel/KeyDataBuilders/SetKeyDataBuilder.php new file mode 100644 index 0000000..f25b327 --- /dev/null +++ b/src/Application/ReadModel/KeyDataBuilders/SetKeyDataBuilder.php @@ -0,0 +1,65 @@ +manager = $manager; + $this->prettifier = $prettifier; + } + + public function canBuildKeyData( ProvidesKeyInfo $keyInfo, ProvidesKeyName $keyName ) : bool + { + return !$keyName->hasSubKey() && (KeyType::SET === $keyInfo->getType()); + } + + public function buildKeyData( ProvidesKeyInfo $keyInfo, ProvidesKeyName $keyName ) : ProvidesKeyData + { + $rawMembers = []; + foreach ( $keyInfo->getSubItems() as $index => $member ) + { + $rawMembers[] = $this->getMemberOutput( $index, $member ); + } + + $prettyMembers = []; + foreach ( $keyInfo->getSubItems() as $index => $member ) + { + $prettyMember = $this->prettifier->prettify( $member ); + $prettyMembers[] = $this->getMemberOutput( $index, $prettyMember ); + } + + $rawKeyData = implode( self::MEMBER_SEPARATOR, $rawMembers ); + $keyData = implode( self::MEMBER_SEPARATOR, $prettyMembers ); + + return new KeyData( $keyData, $rawKeyData ); + } + + private function getMemberOutput( int $index, string $member ) : string + { + return sprintf( + "Member %d:\n%s\n%s", + $index, + str_repeat( '=', 8 + strlen( (string)$index ) ), + $member + ); + } +} \ No newline at end of file From 7ff2c9d8d93e4795d02b7b5815372f5dc41315f1 Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Sun, 13 May 2018 00:28:37 +0200 Subject: [PATCH 26/62] Add set sub key data builder --- .../KeyDataBuilders/SetSubKeyDataBuilder.php | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 src/Application/ReadModel/KeyDataBuilders/SetSubKeyDataBuilder.php diff --git a/src/Application/ReadModel/KeyDataBuilders/SetSubKeyDataBuilder.php b/src/Application/ReadModel/KeyDataBuilders/SetSubKeyDataBuilder.php new file mode 100644 index 0000000..5111eb0 --- /dev/null +++ b/src/Application/ReadModel/KeyDataBuilders/SetSubKeyDataBuilder.php @@ -0,0 +1,40 @@ +manager = $manager; + $this->prettifier = $prettifier; + } + + public function canBuildKeyData( ProvidesKeyInfo $keyInfo, ProvidesKeyName $keyName ) : bool + { + return $keyName->hasSubKey() && (KeyType::SET === $keyInfo->getType()); + } + + public function buildKeyData( ProvidesKeyInfo $keyInfo, ProvidesKeyName $keyName ) : ProvidesKeyData + { + $rawKeyData = $keyInfo->getSubItems()[ (int)$keyName->getSubKey() ]; + $keyData = $this->prettifier->prettify( $rawKeyData ); + + return new KeyData( $keyData, $rawKeyData ); + } +} \ No newline at end of file From 2c0ebdb2eef6fe2764b55b53a7d6e14f00a0a6ae Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Sun, 13 May 2018 00:28:47 +0200 Subject: [PATCH 27/62] Add sorted set key data builder --- .../SortedSetKeyDataBuilder.php | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 src/Application/ReadModel/KeyDataBuilders/SortedSetKeyDataBuilder.php diff --git a/src/Application/ReadModel/KeyDataBuilders/SortedSetKeyDataBuilder.php b/src/Application/ReadModel/KeyDataBuilders/SortedSetKeyDataBuilder.php new file mode 100644 index 0000000..1e2f85a --- /dev/null +++ b/src/Application/ReadModel/KeyDataBuilders/SortedSetKeyDataBuilder.php @@ -0,0 +1,69 @@ +manager = $manager; + $this->prettifier = $prettifier; + } + + public function canBuildKeyData( ProvidesKeyInfo $keyInfo, ProvidesKeyName $keyName ) : bool + { + return !$keyName->hasSubKey() && (KeyType::SORTED_SET === $keyInfo->getType()); + } + + public function buildKeyData( ProvidesKeyInfo $keyInfo, ProvidesKeyName $keyName ) : ProvidesKeyData + { + $setMembers = $keyInfo->getSubItems(); + $rawMembers = []; + $index = 0; + foreach ( $setMembers as $member => $score ) + { + $rawMembers[] = $this->getMemberOutput( $index++, $member, $score ); + } + + $prettyMembers = []; + $index = 0; + foreach ( $setMembers as $member => $score ) + { + $prettyMember = $this->prettifier->prettify( $member ); + $prettyMembers[] = $this->getMemberOutput( $index++, $prettyMember, $score ); + } + + $rawKeyData = implode( self::MEMBER_SEPARATOR, $rawMembers ); + $keyData = implode( self::MEMBER_SEPARATOR, $prettyMembers ); + + return new KeyData( $keyData, $rawKeyData ); + } + + private function getMemberOutput( int $index, string $member, float $score ) : string + { + return sprintf( + "Member %d (Score: %s):\n%s\n%s", + $index, + (string)$score, + str_repeat( '=', 18 + strlen( (string)$index ) + strlen( (string)$score ) ), + $member + ); + } +} \ No newline at end of file From f9907fb0f5adaefc573200d5515f49ca34c47ee7 Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Sun, 13 May 2018 00:28:56 +0200 Subject: [PATCH 28/62] Add sorted set sub key data builder --- .../SortedSetSubKeyDataBuilder.php | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 src/Application/ReadModel/KeyDataBuilders/SortedSetSubKeyDataBuilder.php diff --git a/src/Application/ReadModel/KeyDataBuilders/SortedSetSubKeyDataBuilder.php b/src/Application/ReadModel/KeyDataBuilders/SortedSetSubKeyDataBuilder.php new file mode 100644 index 0000000..414c397 --- /dev/null +++ b/src/Application/ReadModel/KeyDataBuilders/SortedSetSubKeyDataBuilder.php @@ -0,0 +1,59 @@ +manager = $manager; + $this->prettifier = $prettifier; + } + + public function canBuildKeyData( ProvidesKeyInfo $keyInfo, ProvidesKeyName $keyName ) : bool + { + return $keyName->hasSubKey() && (KeyType::SORTED_SET === $keyInfo->getType()); + } + + /** + * @param ProvidesKeyInfo $keyInfo + * @param ProvidesKeyName $keyName + * + * @throws RuntimeException + * @return ProvidesKeyData + */ + public function buildKeyData( ProvidesKeyInfo $keyInfo, ProvidesKeyName $keyName ) : ProvidesKeyData + { + $i = 0; + foreach ( $keyInfo->getSubItems() as $member => $score ) + { + if ( (int)$keyName->getSubKey() !== $i++ ) + { + continue; + } + + $rawKeyData = $member; + $keyData = $this->prettifier->prettify( $member ); + + return new KeyData( $keyData, $rawKeyData, $score ); + } + + throw new RuntimeException( 'Could not find key in sorted set anymore.' ); + } +} \ No newline at end of file From 9ac60c1b22821932c2b2977bf9ff9dea19af2f8d Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Sun, 13 May 2018 00:29:06 +0200 Subject: [PATCH 29/62] Add string key data builder --- .../KeyDataBuilders/StringKeyDataBuilder.php | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 src/Application/ReadModel/KeyDataBuilders/StringKeyDataBuilder.php diff --git a/src/Application/ReadModel/KeyDataBuilders/StringKeyDataBuilder.php b/src/Application/ReadModel/KeyDataBuilders/StringKeyDataBuilder.php new file mode 100644 index 0000000..83fb0f9 --- /dev/null +++ b/src/Application/ReadModel/KeyDataBuilders/StringKeyDataBuilder.php @@ -0,0 +1,48 @@ +manager = $manager; + $this->prettifier = $prettifier; + } + + public function canBuildKeyData( ProvidesKeyInfo $keyInfo, ProvidesKeyName $keyName ) : bool + { + return !$keyName->hasSubKey() && (KeyType::STRING === $keyInfo->getType()); + } + + /** + * @param ProvidesKeyInfo $keyInfo + * @param ProvidesKeyName $keyName + * + * @throws ConnectionFailedException + * @return ProvidesKeyData + */ + public function buildKeyData( ProvidesKeyInfo $keyInfo, ProvidesKeyName $keyName ) : ProvidesKeyData + { + $rawKeyData = $this->manager->getValue( $keyName->getKeyName() ) ?: ''; + $keyData = $this->prettifier->prettify( $rawKeyData ); + + return new KeyData( $keyData, $rawKeyData ); + } +} \ No newline at end of file From 373545e7f73af9b177707060c61860991c740939 Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Sun, 13 May 2018 00:29:27 +0200 Subject: [PATCH 30/62] Replace internal methods with composite key data builder --- .../FetchKeyInformationQueryHandler.php | 297 ++---------------- 1 file changed, 22 insertions(+), 275 deletions(-) diff --git a/src/Application/ReadModel/QueryHandlers/FetchKeyInformationQueryHandler.php b/src/Application/ReadModel/QueryHandlers/FetchKeyInformationQueryHandler.php index bd72e4e..eb74250 100644 --- a/src/Application/ReadModel/QueryHandlers/FetchKeyInformationQueryHandler.php +++ b/src/Application/ReadModel/QueryHandlers/FetchKeyInformationQueryHandler.php @@ -3,13 +3,21 @@ namespace hollodotme\Readis\Application\ReadModel\QueryHandlers; use hollodotme\Readis\Application\Interfaces\ProvidesKeyInfo; -use hollodotme\Readis\Application\ReadModel\Constants\KeyType; use hollodotme\Readis\Application\ReadModel\Constants\ResultType; -use hollodotme\Readis\Application\ReadModel\DTO\KeyData; use hollodotme\Readis\Application\ReadModel\DTO\KeyName; use hollodotme\Readis\Application\ReadModel\Interfaces\PrettifiesString; use hollodotme\Readis\Application\ReadModel\Interfaces\ProvidesKeyData; use hollodotme\Readis\Application\ReadModel\Interfaces\ProvidesKeyName; +use hollodotme\Readis\Application\ReadModel\KeyDataBuilders\HashKeyDataBuilder; +use hollodotme\Readis\Application\ReadModel\KeyDataBuilders\HashSubKeyDataBuilder; +use hollodotme\Readis\Application\ReadModel\KeyDataBuilders\KeyDataBuilder; +use hollodotme\Readis\Application\ReadModel\KeyDataBuilders\ListKeyDataBuilder; +use hollodotme\Readis\Application\ReadModel\KeyDataBuilders\ListSubKeyDataBuilder; +use hollodotme\Readis\Application\ReadModel\KeyDataBuilders\SetKeyDataBuilder; +use hollodotme\Readis\Application\ReadModel\KeyDataBuilders\SetSubKeyDataBuilder; +use hollodotme\Readis\Application\ReadModel\KeyDataBuilders\SortedSetKeyDataBuilder; +use hollodotme\Readis\Application\ReadModel\KeyDataBuilders\SortedSetSubKeyDataBuilder; +use hollodotme\Readis\Application\ReadModel\KeyDataBuilders\StringKeyDataBuilder; use hollodotme\Readis\Application\ReadModel\Prettifiers\HyperLogLogPrettifier; use hollodotme\Readis\Application\ReadModel\Prettifiers\JsonPrettifier; use hollodotme\Readis\Application\ReadModel\Prettifiers\PrettifierChain; @@ -20,8 +28,6 @@ use hollodotme\Readis\Infrastructure\Redis\Exceptions\ConnectionFailedException; use hollodotme\Readis\Infrastructure\Redis\ServerManager; use hollodotme\Readis\Interfaces\ProvidesInfrastructure; -use function implode; -use function str_repeat; final class FetchKeyInformationQueryHandler extends AbstractQueryHandler { @@ -42,7 +48,6 @@ public function __construct( ProvidesInfrastructure $env ) /** * @param FetchKeyInformationQuery $query * - * @throws KeyTypeNotImplemented * @return FetchKeyInformationResult */ public function handle( FetchKeyInformationQuery $query ) : FetchKeyInformationResult @@ -66,7 +71,7 @@ public function handle( FetchKeyInformationQuery $query ) : FetchKeyInformationR return $result; } - catch ( ServerConfigNotFound $e ) + catch ( ServerConfigNotFound | KeyTypeNotImplemented $e ) { return new FetchKeyInformationResult( ResultType::FAILURE, $e->getMessage() ); } @@ -84,7 +89,6 @@ public function handle( FetchKeyInformationQuery $query ) : FetchKeyInformationR * @param ProvidesKeyInfo $keyInfo * @param ProvidesKeyName $keyName * - * @throws ConnectionFailedException * @throws KeyTypeNotImplemented * @return ProvidesKeyData */ @@ -94,275 +98,18 @@ private function getKeyData( ProvidesKeyName $keyName ) : ProvidesKeyData { - if ( $keyName->hasSubKey() ) - { - return $this->getSubKeyData( $manager, $keyInfo, $keyName ); - } - - return $this->getWholeKeyData( $manager, $keyInfo, $keyName ); - } - - /** - * @param ServerManager $manager - * @param ProvidesKeyInfo $keyInfo - * @param ProvidesKeyName $keyName - * - * @throws ConnectionFailedException - * @throws KeyTypeNotImplemented - * @return ProvidesKeyData - */ - private function getSubKeyData( - ServerManager $manager, - ProvidesKeyInfo $keyInfo, - ProvidesKeyName $keyName - ) : ProvidesKeyData - { - if ( KeyType::HASH === $keyInfo->getType() ) - { - $rawKeyData = $manager->getHashValue( $keyName->getKeyName(), $keyName->getSubKey() ) ?: ''; - $keyData = $this->prettifier->prettify( $rawKeyData ); - - return new KeyData( $keyData, $rawKeyData ); - } - - if ( KeyType::LIST === $keyInfo->getType() ) - { - $rawKeyData = $manager->getListValue( $keyName->getKeyName(), (int)$keyName->getSubKey() ) ?: ''; - $keyData = $this->prettifier->prettify( $rawKeyData ); - - return new KeyData( $keyData, $rawKeyData ); - } - - if ( KeyType::SET === $keyInfo->getType() ) - { - $rawKeyData = $keyInfo->getSubItems()[ (int)$keyName->getSubKey() ]; - $keyData = $this->prettifier->prettify( $rawKeyData ); - - return new KeyData( $keyData, $rawKeyData ); - } - - if ( KeyType::SORTED_SET === $keyInfo->getType() ) - { - $i = 0; - foreach ( $keyInfo->getSubItems() as $member => $score ) - { - if ( (int)$keyName->getSubKey() !== $i++ ) - { - continue; - } - - $rawKeyData = $member; - $keyData = $this->prettifier->prettify( $member ); - - return new KeyData( $keyData, $rawKeyData, $score ); - } - } - - throw new KeyTypeNotImplemented( - 'Key type not implemented or does not support sub keys: ' . $keyInfo->getType() + $keyDataBuilder = new KeyDataBuilder( + new HashKeyDataBuilder( $manager, $this->prettifier ), + new HashSubKeyDataBuilder( $manager, $this->prettifier ), + new ListKeyDataBuilder( $manager, $this->prettifier ), + new ListSubKeyDataBuilder( $manager, $this->prettifier ), + new SetKeyDataBuilder( $manager, $this->prettifier ), + new SetSubKeyDataBuilder( $manager, $this->prettifier ), + new SortedSetKeyDataBuilder( $manager, $this->prettifier ), + new SortedSetSubKeyDataBuilder( $manager, $this->prettifier ), + new StringKeyDataBuilder( $manager, $this->prettifier ) ); - } - - /** - * @param ServerManager $manager - * @param ProvidesKeyInfo $keyInfo - * @param ProvidesKeyName $keyName - * - * @throws ConnectionFailedException - * @throws KeyTypeNotImplemented - * @return ProvidesKeyData - */ - private function getWholeKeyData( - ServerManager $manager, - ProvidesKeyInfo $keyInfo, - ProvidesKeyName $keyName - ) : ProvidesKeyData - { - if ( KeyType::HASH === $keyInfo->getType() ) - { - return $this->getKeyDataForWholeHash( $manager, $keyName ); - } - - if ( KeyType::LIST === $keyInfo->getType() ) - { - return $this->getKeyDataForWholeList( $keyInfo ); - } - - if ( KeyType::SET === $keyInfo->getType() ) - { - return $this->getKeyDataForWholeSet( $keyInfo ); - } - - if ( KeyType::SORTED_SET === $keyInfo->getType() ) - { - return $this->getKeyDataForWholeSortedSet( $keyInfo ); - } - - if ( KeyType::STRING === $keyInfo->getType() ) - { - return $this->getKeyDataForString( $manager, $keyName ); - } - - throw new KeyTypeNotImplemented( 'Key type not implemented: ' . $keyInfo->getType() ); - } - - /** - * @param ServerManager $manager - * @param ProvidesKeyName $keyName - * - * @throws ConnectionFailedException - * @return ProvidesKeyData - */ - private function getKeyDataForWholeHash( ServerManager $manager, ProvidesKeyName $keyName ) : ProvidesKeyData - { - $rawListItems = []; - $hashValues = $manager->getAllHashValues( $keyName->getKeyName() ); - - foreach ( $hashValues as $hashKey => $hashValue ) - { - $rawListItems[] = sprintf( - "Hash key %s:\n%s\n%s", - $hashKey, - str_repeat( '=', 10 + strlen( (string)$hashKey ) ), - $hashValue - ); - } - - $prettyListItems = []; - foreach ( $hashValues as $hashKey => $hashValue ) - { - $prettyListItem = $this->prettifier->prettify( $hashValue ); - $prettyListItems[] = sprintf( - "Hash key %s:\n%s\n%s", - $hashKey, - str_repeat( '=', 10 + strlen( (string)$hashKey ) ), - $prettyListItem - ); - } - - $rawKeyData = implode( "\n\n---\n\n", $rawListItems ); - $keyData = implode( "\n\n---\n\n", $prettyListItems ); - - return new KeyData( $keyData, $rawKeyData ); - } - - private function getKeyDataForWholeList( ProvidesKeyInfo $keyInfo ) : ProvidesKeyData - { - $rawListItems = []; - foreach ( $keyInfo->getSubItems() as $index => $listItem ) - { - $rawListItems[] = sprintf( - "Element %d:\n%s\n%s", - $index, - str_repeat( '=', 9 + strlen( (string)$index ) ), - $listItem - ); - } - - $prettyListItems = []; - foreach ( $keyInfo->getSubItems() as $index => $listItem ) - { - $prettyListItem = $this->prettifier->prettify( $listItem ); - $prettyListItems[] = sprintf( - "Element %d:\n%s\n%s", - $index, - str_repeat( '=', 9 + strlen( (string)$index ) ), - $prettyListItem - ); - } - - $rawKeyData = implode( "\n\n---\n\n", $rawListItems ); - $keyData = implode( "\n\n---\n\n", $prettyListItems ); - - return new KeyData( $keyData, $rawKeyData ); - } - - private function getKeyDataForWholeSet( ProvidesKeyInfo $keyInfo ) : ProvidesKeyData - { - $rawMembers = []; - foreach ( $keyInfo->getSubItems() as $index => $member ) - { - $rawMembers[] = sprintf( - "Member %d:\n%s\n%s", - $index, - str_repeat( '=', 8 + strlen( (string)$index ) ), - $member - ); - } - - $prettyMembers = []; - foreach ( $keyInfo->getSubItems() as $index => $member ) - { - $prettyMember = $this->prettifier->prettify( $member ); - $prettyMembers[] = sprintf( - "Member %d:\n%s\n%s", - $index, - str_repeat( '=', 8 + strlen( (string)$index ) ), - $prettyMember - ); - } - - $rawKeyData = implode( "\n\n---\n\n", $rawMembers ); - $keyData = implode( "\n\n---\n\n", $prettyMembers ); - - return new KeyData( $keyData, $rawKeyData ); - } - - /** - * @param ProvidesKeyInfo $keyInfo - * - * @return ProvidesKeyData - */ - private function getKeyDataForWholeSortedSet( ProvidesKeyInfo $keyInfo ) : ProvidesKeyData - { - $setMembers = $keyInfo->getSubItems(); - $rawMembers = []; - $i = 0; - foreach ( $setMembers as $member => $score ) - { - $rawMembers[] = sprintf( - "Member %d (Score: %s):\n%s\n%s", - $i, - (string)$score, - str_repeat( '=', 18 + strlen( (string)$i ) + strlen( (string)$score ) ), - $member - ); - $i++; - } - - $prettyMembers = []; - $i = 0; - foreach ( $setMembers as $member => $score ) - { - $prettyMember = $this->prettifier->prettify( $member ); - $prettyMembers[] = sprintf( - "Member %d (Score: %s):\n%s\n%s", - $i, - (string)$score, - str_repeat( '=', 18 + strlen( (string)$i ) + strlen( (string)$score ) ), - $prettyMember - ); - $i++; - } - - $rawKeyData = implode( "\n\n---\n\n", $rawMembers ); - $keyData = implode( "\n\n---\n\n", $prettyMembers ); - - return new KeyData( $keyData, $rawKeyData ); - } - - /** - * @param ServerManager $manager - * @param ProvidesKeyName $keyName - * - * @throws ConnectionFailedException - * @return ProvidesKeyData - */ - private function getKeyDataForString( ServerManager $manager, ProvidesKeyName $keyName ) : ProvidesKeyData - { - $rawKeyData = $manager->getValue( $keyName->getKeyName() ) ?: ''; - $keyData = $this->prettifier->prettify( $rawKeyData ); - return new KeyData( $keyData, $rawKeyData ); + return $keyDataBuilder->buildKeyData( $keyInfo, $keyName ); } } From 64e2d41bf3196c2d778dc44bd6e4f26a9b6ac060 Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Sun, 13 May 2018 00:59:14 +0200 Subject: [PATCH 31/62] Add getter for server manager for server key --- src/Env.php | 14 ++++++++++++++ src/Interfaces/ProvidesInfrastructure.php | 2 ++ 2 files changed, 16 insertions(+) diff --git a/src/Env.php b/src/Env.php index def7220..00b79ea 100644 --- a/src/Env.php +++ b/src/Env.php @@ -48,4 +48,18 @@ function () use ( $serverConfig ) } ); } + + /** + * @param string $serverKey + * + * @throws Exceptions\ServerConfigNotFound + * @return ServerManager + */ + public function getServerManagerForServerKey( string $serverKey ) : ServerManager + { + $serverConfigList = $this->getServerConfigList(); + $serverConfig = $serverConfigList->getServerConfig( $serverKey ); + + return $this->getServerManager( $serverConfig ); + } } diff --git a/src/Interfaces/ProvidesInfrastructure.php b/src/Interfaces/ProvidesInfrastructure.php index d352bbf..06d954b 100644 --- a/src/Interfaces/ProvidesInfrastructure.php +++ b/src/Interfaces/ProvidesInfrastructure.php @@ -14,4 +14,6 @@ public function getAppConfig() : AppConfig; public function getServerConfigList() : ProvidesServerConfigList; public function getServerManager( ProvidesServerConfig $serverConfig ) : ServerManager; + + public function getServerManagerForServerKey( string $serverKey ) : ServerManager; } From ae6974f6ae1b275a4bfeed1eb2783869897e78a6 Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Sun, 13 May 2018 00:59:49 +0200 Subject: [PATCH 32/62] Refactor find key information query handler dependency injection --- .../Queries/FetchKeyInformationQuery.php | 17 +--- .../FetchKeyInformationQueryHandler.php | 78 +++++++------------ .../Read/AjaxKeyDetailsRequestHandler.php | 5 +- 3 files changed, 34 insertions(+), 66 deletions(-) diff --git a/src/Application/ReadModel/Queries/FetchKeyInformationQuery.php b/src/Application/ReadModel/Queries/FetchKeyInformationQuery.php index 29bcddb..24a9536 100644 --- a/src/Application/ReadModel/Queries/FetchKeyInformationQuery.php +++ b/src/Application/ReadModel/Queries/FetchKeyInformationQuery.php @@ -4,9 +4,6 @@ final class FetchKeyInformationQuery { - /** @var string */ - private $serverKey; - /** @var int */ private $database; @@ -16,17 +13,11 @@ final class FetchKeyInformationQuery /** @var null|string */ private $subKey; - public function __construct( string $serverKey, int $database, string $keyName, ?string $subKey ) - { - $this->serverKey = $serverKey; - $this->database = $database; - $this->keyName = $keyName; - $this->subKey = $subKey; - } - - public function getServerKey() : string + public function __construct( int $database, string $keyName, ?string $subKey ) { - return $this->serverKey; + $this->database = $database; + $this->keyName = $keyName; + $this->subKey = $subKey; } public function getDatabase() : int diff --git a/src/Application/ReadModel/QueryHandlers/FetchKeyInformationQueryHandler.php b/src/Application/ReadModel/QueryHandlers/FetchKeyInformationQueryHandler.php index eb74250..a22f359 100644 --- a/src/Application/ReadModel/QueryHandlers/FetchKeyInformationQueryHandler.php +++ b/src/Application/ReadModel/QueryHandlers/FetchKeyInformationQueryHandler.php @@ -2,12 +2,9 @@ namespace hollodotme\Readis\Application\ReadModel\QueryHandlers; -use hollodotme\Readis\Application\Interfaces\ProvidesKeyInfo; use hollodotme\Readis\Application\ReadModel\Constants\ResultType; use hollodotme\Readis\Application\ReadModel\DTO\KeyName; -use hollodotme\Readis\Application\ReadModel\Interfaces\PrettifiesString; -use hollodotme\Readis\Application\ReadModel\Interfaces\ProvidesKeyData; -use hollodotme\Readis\Application\ReadModel\Interfaces\ProvidesKeyName; +use hollodotme\Readis\Application\ReadModel\Interfaces\BuildsKeyData; use hollodotme\Readis\Application\ReadModel\KeyDataBuilders\HashKeyDataBuilder; use hollodotme\Readis\Application\ReadModel\KeyDataBuilders\HashSubKeyDataBuilder; use hollodotme\Readis\Application\ReadModel\KeyDataBuilders\KeyDataBuilder; @@ -24,25 +21,38 @@ use hollodotme\Readis\Application\ReadModel\Queries\FetchKeyInformationQuery; use hollodotme\Readis\Application\ReadModel\Results\FetchKeyInformationResult; use hollodotme\Readis\Exceptions\KeyTypeNotImplemented; -use hollodotme\Readis\Exceptions\ServerConfigNotFound; use hollodotme\Readis\Infrastructure\Redis\Exceptions\ConnectionFailedException; use hollodotme\Readis\Infrastructure\Redis\ServerManager; -use hollodotme\Readis\Interfaces\ProvidesInfrastructure; -final class FetchKeyInformationQueryHandler extends AbstractQueryHandler +final class FetchKeyInformationQueryHandler { - /** @var PrettifiesString */ - private $prettifier; + /** @var ServerManager */ + private $manager; - public function __construct( ProvidesInfrastructure $env ) + /** @var BuildsKeyData */ + private $keyDataBuilder; + + public function __construct( ServerManager $manager ) { - parent::__construct( $env ); + $this->manager = $manager; - $this->prettifier = new PrettifierChain(); - $this->prettifier->addPrettifiers( + $prettifier = new PrettifierChain(); + $prettifier->addPrettifiers( new JsonPrettifier(), new HyperLogLogPrettifier() ); + + $this->keyDataBuilder = new KeyDataBuilder( + new HashKeyDataBuilder( $manager, $prettifier ), + new HashSubKeyDataBuilder( $manager, $prettifier ), + new ListKeyDataBuilder( $manager, $prettifier ), + new ListSubKeyDataBuilder( $manager, $prettifier ), + new SetKeyDataBuilder( $manager, $prettifier ), + new SetSubKeyDataBuilder( $manager, $prettifier ), + new SortedSetKeyDataBuilder( $manager, $prettifier ), + new SortedSetSubKeyDataBuilder( $manager, $prettifier ), + new StringKeyDataBuilder( $manager, $prettifier ) + ); } /** @@ -54,16 +64,11 @@ public function handle( FetchKeyInformationQuery $query ) : FetchKeyInformationR { try { - $serverConfigList = $this->getEnv()->getServerConfigList(); - $serverConfig = $serverConfigList->getServerConfig( $query->getServerKey() ); - $manager = $this->getEnv()->getServerManager( $serverConfig ); - - $manager->selectDatabase( $query->getDatabase() ); - - $keyInfo = $manager->getKeyInfoObject( $query->getKeyName() ); + $this->manager->selectDatabase( $query->getDatabase() ); + $keyInfo = $this->manager->getKeyInfoObject( $query->getKeyName() ); $keyName = new KeyName( $query->getKeyName(), $query->getSubKey() ); - $keyData = $this->getKeyData( $manager, $keyInfo, $keyName ); + $keyData = $this->keyDataBuilder->buildKeyData( $keyInfo, $keyName ); $result = new FetchKeyInformationResult(); $result->setKeyData( $keyData ); @@ -71,7 +76,7 @@ public function handle( FetchKeyInformationQuery $query ) : FetchKeyInformationR return $result; } - catch ( ServerConfigNotFound | KeyTypeNotImplemented $e ) + catch ( KeyTypeNotImplemented $e ) { return new FetchKeyInformationResult( ResultType::FAILURE, $e->getMessage() ); } @@ -83,33 +88,4 @@ public function handle( FetchKeyInformationQuery $query ) : FetchKeyInformationR ); } } - - /** - * @param ServerManager $manager - * @param ProvidesKeyInfo $keyInfo - * @param ProvidesKeyName $keyName - * - * @throws KeyTypeNotImplemented - * @return ProvidesKeyData - */ - private function getKeyData( - ServerManager $manager, - ProvidesKeyInfo $keyInfo, - ProvidesKeyName $keyName - ) : ProvidesKeyData - { - $keyDataBuilder = new KeyDataBuilder( - new HashKeyDataBuilder( $manager, $this->prettifier ), - new HashSubKeyDataBuilder( $manager, $this->prettifier ), - new ListKeyDataBuilder( $manager, $this->prettifier ), - new ListSubKeyDataBuilder( $manager, $this->prettifier ), - new SetKeyDataBuilder( $manager, $this->prettifier ), - new SetSubKeyDataBuilder( $manager, $this->prettifier ), - new SortedSetKeyDataBuilder( $manager, $this->prettifier ), - new SortedSetSubKeyDataBuilder( $manager, $this->prettifier ), - new StringKeyDataBuilder( $manager, $this->prettifier ) - ); - - return $keyDataBuilder->buildKeyData( $keyInfo, $keyName ); - } } diff --git a/src/Application/Web/Server/Read/AjaxKeyDetailsRequestHandler.php b/src/Application/Web/Server/Read/AjaxKeyDetailsRequestHandler.php index 4d45bbe..618842c 100644 --- a/src/Application/Web/Server/Read/AjaxKeyDetailsRequestHandler.php +++ b/src/Application/Web/Server/Read/AjaxKeyDetailsRequestHandler.php @@ -29,9 +29,10 @@ public function handle( ProvidesReadRequestData $request ) $subKey = null; } $database = (int)$input->get( 'database', 0 ); + $manager = $this->getEnv()->getServerManagerForServerKey( $serverKey ); - $query = new FetchKeyInformationQuery( $serverKey, $database, $key, $subKey ); - $result = (new FetchKeyInformationQueryHandler( $this->getEnv() ))->handle( $query ); + $query = new FetchKeyInformationQuery( $database, $key, $subKey ); + $result = (new FetchKeyInformationQueryHandler( $manager ))->handle( $query ); if ( $result->failed() ) { From 751577885b163327b6c1f62bb0fa3d7cd8dbbffa Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Sun, 13 May 2018 02:18:33 +0200 Subject: [PATCH 33/62] Fix naming of fields in hashes --- .../ReadModel/KeyDataBuilders/HashKeyDataBuilder.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Application/ReadModel/KeyDataBuilders/HashKeyDataBuilder.php b/src/Application/ReadModel/KeyDataBuilders/HashKeyDataBuilder.php index 9cdf91e..fe562b1 100644 --- a/src/Application/ReadModel/KeyDataBuilders/HashKeyDataBuilder.php +++ b/src/Application/ReadModel/KeyDataBuilders/HashKeyDataBuilder.php @@ -66,9 +66,9 @@ public function buildKeyData( ProvidesKeyInfo $keyInfo, ProvidesKeyName $keyName private function getFieldOutput( string $hashKey, string $hashValue ) : string { return sprintf( - "Hash key %s:\n%s\n%s", + "Field %s:\n%s\n%s", $hashKey, - str_repeat( '=', 10 + strlen( (string)$hashKey ) ), + str_repeat( '=', 7 + strlen( (string)$hashKey ) ), $hashValue ); } From 363d0deb3e6e7bf87b9b1f414da101f8202b0536 Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Sun, 13 May 2018 02:18:53 +0200 Subject: [PATCH 34/62] Add more test data --- .../AbstractQueryHandlerTest.php | 57 +++++++++++++++++-- 1 file changed, 52 insertions(+), 5 deletions(-) diff --git a/tests/Integration/Application/ReadModel/QueryHandlers/AbstractQueryHandlerTest.php b/tests/Integration/Application/ReadModel/QueryHandlers/AbstractQueryHandlerTest.php index 940a415..161f1bb 100644 --- a/tests/Integration/Application/ReadModel/QueryHandlers/AbstractQueryHandlerTest.php +++ b/tests/Integration/Application/ReadModel/QueryHandlers/AbstractQueryHandlerTest.php @@ -2,6 +2,7 @@ namespace hollodotme\Readis\Tests\Integration\Application\ReadModel\QueryHandlers; +use hollodotme\Readis\Exceptions\NoServersConfigured; use hollodotme\Readis\Exceptions\ServerConfigNotFound; use hollodotme\Readis\Infrastructure\Configs\ServerConfigList; use hollodotme\Readis\Infrastructure\Redis\ServerConnection; @@ -22,8 +23,36 @@ protected function setUp() : void $this->redis->slowlog( 'reset' ); $this->redis->select( 0 ); - $this->redis->set( 'unit', 'test' ); - $this->redis->hSet( 'test', 'unit', '{"json": {"key": "value"}}' ); + $this->redis->set( 'string', '{"json": {"key": "value"}}' ); + $this->redis->hSet( 'hash', 'field', 'value' ); + $this->redis->hSet( 'hash', 'json', '{"json": {"key": "value"}}' ); + $this->redis->rPush( 'list', 'one', 'two', '{"json": {"key": "value"}}' ); + $this->redis->sAdd( 'set', 'one', 'two', '{"json": {"key": "value"}}' ); + /** @noinspection PhpMethodParametersCountMismatchInspection */ + $this->redis->zAdd( + 'sorted set', + 1, + 'one', + 2, + 'two', + 2, + 'two again', + 3, + '{"json": {"key": "value"}}' + ); + /** @noinspection PhpUndefinedMethodInspection */ + $this->redis->geoAdd( + 'geo', + 13.361389, + 38.115556, + 'Palermo', + 15.087269, + 37.502669, + 'Catania' + ); + /** @noinspection PhpMethodParametersCountMismatchInspection */ + /** @noinspection PhpParamsInspection */ + $this->redis->rawCommand( 'PFADD', 'hyperLogLog', 'a', 'b', 'c', 'd', 'e', 'f' ); } protected function tearDown() : void @@ -35,6 +64,7 @@ protected function tearDown() : void /** * @param string $serverKey * + * @throws NoServersConfigured * @return ProvidesInfrastructure * @throws ServerConfigNotFound */ @@ -44,18 +74,35 @@ protected function getEnvMock( string $serverKey ) : ProvidesInfrastructure $serverConfig = $serverConfigList->getServerConfig( $serverKey > count( $serverConfigList->getServerConfigs() ) ? '0' : $serverKey ); - $serverConnection = new ServerConnection( $serverConfig ); $env = $this->getMockBuilder( ProvidesInfrastructure::class )->getMockForAbstractClass(); $env->method( 'getServerConfigList' )->willReturn( $serverConfigList ); $env->method( 'getServerManager' ) - ->with( $serverConfig ) - ->willReturn( new ServerManager( $serverConnection ) ); + ->with( $serverConfig ) + ->willReturn( $this->getServerManagerMock( $serverKey ) ); /** @var ProvidesInfrastructure $env */ return $env; } + /** + * @param string $serverKey + * + * @throws ServerConfigNotFound + * @throws NoServersConfigured + * @return ServerManager + */ + protected function getServerManagerMock( string $serverKey ) : ServerManager + { + $serverConfigList = $this->getServerConfigListMock(); + $serverConfig = $serverConfigList->getServerConfig( + $serverKey > count( $serverConfigList->getServerConfigs() ) ? '0' : $serverKey + ); + $serverConnection = new ServerConnection( $serverConfig ); + + return new ServerManager( $serverConnection ); + } + protected function getServerConfigListMock() : ServerConfigList { return new ServerConfigList( From ae4e0968cedd7500f994755955ee3644819e6a81 Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Sun, 13 May 2018 02:19:19 +0200 Subject: [PATCH 35/62] Update integration tests --- .../FetchKeyInformationQueryHandlerTest.php | 220 +++++++++++++++--- .../FindKeysInDatabaseQueryHandlerTest.php | 8 +- 2 files changed, 198 insertions(+), 30 deletions(-) diff --git a/tests/Integration/Application/ReadModel/QueryHandlers/FetchKeyInformationQueryHandlerTest.php b/tests/Integration/Application/ReadModel/QueryHandlers/FetchKeyInformationQueryHandlerTest.php index 949682a..2929063 100644 --- a/tests/Integration/Application/ReadModel/QueryHandlers/FetchKeyInformationQueryHandlerTest.php +++ b/tests/Integration/Application/ReadModel/QueryHandlers/FetchKeyInformationQueryHandlerTest.php @@ -4,16 +4,17 @@ use hollodotme\Readis\Application\ReadModel\Queries\FetchKeyInformationQuery; use hollodotme\Readis\Application\ReadModel\QueryHandlers\FetchKeyInformationQueryHandler; -use hollodotme\Readis\Exceptions\KeyTypeNotImplemented; +use hollodotme\Readis\Exceptions\NoServersConfigured; use hollodotme\Readis\Exceptions\ServerConfigNotFound; use PHPUnit\Framework\ExpectationFailedException; use SebastianBergmann\RecursionContext\InvalidArgumentException; +use function preg_quote; final class FetchKeyInformationQueryHandlerTest extends AbstractQueryHandlerTest { /** * @param string $key - * @param null|string $hashKey + * @param null|string $subKey * @param string $expectedKeyType * @param string $expectedKeyData * @param string $expectedRawKeyData @@ -22,13 +23,13 @@ final class FetchKeyInformationQueryHandlerTest extends AbstractQueryHandlerTest * * @throws ExpectationFailedException * @throws InvalidArgumentException - * @throws KeyTypeNotImplemented + * @throws NoServersConfigured * @throws ServerConfigNotFound * @dataProvider keyInfoProvider */ public function testCanFetchKeyInformation( string $key, - ?string $hashKey, + ?string $subKey, string $expectedKeyType, string $expectedKeyData, string $expectedRawKeyData, @@ -38,8 +39,8 @@ public function testCanFetchKeyInformation( { $serverKey = '0'; - $query = new FetchKeyInformationQuery( $serverKey, 0, $key, $hashKey ); - $result = (new FetchKeyInformationQueryHandler( $this->getEnvMock( $serverKey ) ))->handle( $query ); + $query = new FetchKeyInformationQuery( 0, $key, $subKey ); + $result = (new FetchKeyInformationQueryHandler( $this->getServerManagerMock( $serverKey ) ))->handle( $query ); $this->assertTrue( $result->succeeded() ); $this->assertFalse( $result->failed() ); @@ -47,71 +48,236 @@ public function testCanFetchKeyInformation( $keyInfo = $result->getKeyInfo(); $keyData = $result->getKeyData(); + $keyDataPattern = '#' . preg_quote( $expectedKeyData, '#s' ) . '#'; + $rawKeyDataPattern = '#' . preg_quote( $expectedRawKeyData, '#s' ) . '#'; + $this->assertSame( $expectedKeyType, $keyInfo->getType() ); - $this->assertSame( $expectedKeyData, $keyData->getKeyData() ); - $this->assertSame( $expectedRawKeyData, $keyData->getRawKeyData() ); + $this->assertRegExp( $keyDataPattern, $keyData->getKeyData() ); + $this->assertRegExp( $rawKeyDataPattern, $keyData->getRawKeyData() ); $this->assertSame( $expectedHasScore, $keyData->hasScore() ); - $this->assertSame( $expectedScore, $keyData->getScore() ); + $this->assertSame( (string)$expectedScore, (string)$keyData->getScore() ); } public function keyInfoProvider() : array { return [ [ - 'key' => 'unit', - 'hashKey' => null, + 'key' => 'string', + 'subKey' => null, 'expectedType' => 'string', - 'expectedKeyData' => 'test', - 'expectedRawKeyData' => 'test', + 'expectedKeyData' => "{\n \"json\": {\n \"key\": \"value\"\n }\n}", + 'expectedRawKeyData' => '{"json": {"key": "value"}}', + 'expectedHasScore' => false, + 'expectedScore' => null, + ], + [ + 'key' => 'hash', + 'subKey' => 'field', + 'expectedType' => 'hash', + 'expectedKeyData' => 'value', + 'expectedRawKeyData' => 'value', 'expectedHasScore' => false, 'expectedScore' => null, ], [ - 'key' => 'test', - 'hashKey' => 'unit', + 'key' => 'hash', + 'subKey' => 'json', 'expectedType' => 'hash', 'expectedKeyData' => "{\n \"json\": {\n \"key\": \"value\"\n }\n}", 'expectedRawKeyData' => '{"json": {"key": "value"}}', 'expectedHasScore' => false, 'expectedScore' => null, ], + [ + 'key' => 'hash', + 'subKey' => null, + 'expectedType' => 'hash', + 'expectedKeyData' => "Field field:\n============\nvalue\n\n---\n\nField json:\n===========\n{\n \"json\": {\n \"key\": \"value\"\n }\n}", + 'expectedRawKeyData' => "Field field:\n============\nvalue\n\n---\n\nField json:\n===========\n{\"json\": {\"key\": \"value\"}}", + 'expectedHasScore' => false, + 'expectedScore' => null, + ], + [ + 'key' => 'list', + 'subKey' => '0', + 'expectedType' => 'list', + 'expectedKeyData' => 'one', + 'expectedRawKeyData' => 'one', + 'expectedHasScore' => false, + 'expectedScore' => null, + ], + [ + 'key' => 'list', + 'subKey' => '1', + 'expectedType' => 'list', + 'expectedKeyData' => 'two', + 'expectedRawKeyData' => 'two', + 'expectedHasScore' => false, + 'expectedScore' => null, + ], + [ + 'key' => 'list', + 'subKey' => '2', + 'expectedType' => 'list', + 'expectedKeyData' => "{\n \"json\": {\n \"key\": \"value\"\n }\n}", + 'expectedRawKeyData' => '{"json": {"key": "value"}}', + 'expectedHasScore' => false, + 'expectedScore' => null, + ], + [ + 'key' => 'list', + 'subKey' => null, + 'expectedType' => 'list', + 'expectedKeyData' => "Element 0:\n==========\none\n\n---\n\nElement 1:\n==========\ntwo\n\n---\n\nElement 2:\n==========\n{\n \"json\": {\n \"key\": \"value\"\n }\n}", + 'expectedRawKeyData' => "Element 0:\n==========\none\n\n---\n\nElement 1:\n==========\ntwo\n\n---\n\nElement 2:\n==========\n{\"json\": {\"key\": \"value\"}}", + 'expectedHasScore' => false, + 'expectedScore' => null, + ], + [ + 'key' => 'set', + 'subKey' => '0', + 'expectedType' => 'set', + 'expectedKeyData' => 'two', + 'expectedRawKeyData' => 'two', + 'expectedHasScore' => false, + 'expectedScore' => null, + ], + [ + 'key' => 'set', + 'subKey' => '1', + 'expectedType' => 'set', + 'expectedKeyData' => "{\n \"json\": {\n \"key\": \"value\"\n }\n}", + 'expectedRawKeyData' => '{"json": {"key": "value"}}', + 'expectedHasScore' => false, + 'expectedScore' => null, + ], + [ + 'key' => 'set', + 'subKey' => '2', + 'expectedType' => 'set', + 'expectedKeyData' => 'one', + 'expectedRawKeyData' => 'one', + 'expectedHasScore' => false, + 'expectedScore' => null, + ], + [ + 'key' => 'set', + 'subKey' => null, + 'expectedType' => 'set', + 'expectedKeyData' => "Member 0:\n=========\ntwo\n\n---\n\nMember 1:\n=========\n{\n \"json\": {\n \"key\": \"value\"\n }\n}\n\n---\n\nMember 2:\n=========\none", + 'expectedRawKeyData' => "Member 0:\n=========\ntwo\n\n---\n\nMember 1:\n=========\n{\"json\": {\"key\": \"value\"}}\n\n---\n\nMember 2:\n=========\none", + 'expectedHasScore' => false, + 'expectedScore' => null, + ], + [ + 'key' => 'sorted set', + 'subKey' => '0', + 'expectedType' => 'zset', + 'expectedKeyData' => 'one', + 'expectedRawKeyData' => 'one', + 'expectedHasScore' => true, + 'expectedScore' => 1, + ], + [ + 'key' => 'sorted set', + 'subKey' => '1', + 'expectedType' => 'zset', + 'expectedKeyData' => 'two', + 'expectedRawKeyData' => 'two', + 'expectedHasScore' => true, + 'expectedScore' => 2, + ], + [ + 'key' => 'sorted set', + 'subKey' => '2', + 'expectedType' => 'zset', + 'expectedKeyData' => 'two again', + 'expectedRawKeyData' => 'two again', + 'expectedHasScore' => true, + 'expectedScore' => 2, + ], + [ + 'key' => 'sorted set', + 'subKey' => '3', + 'expectedType' => 'zset', + 'expectedKeyData' => "{\n \"json\": {\n \"key\": \"value\"\n }\n}", + 'expectedRawKeyData' => '{"json": {"key": "value"}}', + 'expectedHasScore' => true, + 'expectedScore' => 3, + ], + [ + 'key' => 'sorted set', + 'subKey' => null, + 'expectedType' => 'zset', + 'expectedKeyData' => "Member 0 (Score: 1):\n====================\none\n\n---\n\nMember 1 (Score: 2):\n====================\ntwo\n\n---\n\nMember 2 (Score: 2):\n====================\ntwo again\n\n---\n\nMember 3 (Score: 3):\n====================\n{\n \"json\": {\n \"key\": \"value\"\n }\n}", + 'expectedRawKeyData' => "Member 0 (Score: 1):\n====================\none\n\n---\n\nMember 1 (Score: 2):\n====================\ntwo\n\n---\n\nMember 2 (Score: 2):\n====================\ntwo again\n\n---\n\nMember 3 (Score: 3):\n====================\n{\"json\": {\"key\": \"value\"}}", + 'expectedHasScore' => false, + 'expectedScore' => null, + ], + [ + 'key' => 'geo', + 'subKey' => '0', + 'expectedType' => 'zset', + 'expectedKeyData' => 'Palermo', + 'expectedRawKeyData' => 'Palermo', + 'expectedHasScore' => true, + 'expectedScore' => 3479099956230700, + ], + [ + 'key' => 'geo', + 'subKey' => '1', + 'expectedType' => 'zset', + 'expectedKeyData' => 'Catania', + 'expectedRawKeyData' => 'Catania', + 'expectedHasScore' => true, + 'expectedScore' => 3479447370796900, + ], + [ + 'key' => 'hyperLogLog', + 'subKey' => null, + 'expectedType' => 'string', + 'expectedKeyData' => '(HyperLogLog encoded value)', + 'expectedRawKeyData' => 'HYLL', + 'expectedHasScore' => false, + 'expectedScore' => null, + ], ]; } /** * @throws ExpectationFailedException * @throws InvalidArgumentException + * @throws NoServersConfigured * @throws ServerConfigNotFound - * @throws KeyTypeNotImplemented */ - public function testResultFailsIfServerConfigNotFound() : void + public function testResultFailsIfKeyIsUnknown() : void { - $serverKey = '3'; - $key = 'some-key'; - $hashKey = null; + $serverKey = '0'; + $key = 'unknown-key'; + $subKey = null; - $query = new FetchKeyInformationQuery( $serverKey, 0, $key, $hashKey ); - $result = (new FetchKeyInformationQueryHandler( $this->getEnvMock( $serverKey ) ))->handle( $query ); + $query = new FetchKeyInformationQuery( 0, $key, $subKey ); + $result = (new FetchKeyInformationQueryHandler( $this->getServerManagerMock( $serverKey ) ))->handle( $query ); $this->assertFalse( $result->succeeded() ); $this->assertTrue( $result->failed() ); - $this->assertSame( 'Server config not found for server key: 3', $result->getMessage() ); + $this->assertSame( 'Key type not implemented or supported: unknown', $result->getMessage() ); } /** * @throws ExpectationFailedException * @throws InvalidArgumentException + * @throws NoServersConfigured * @throws ServerConfigNotFound - * @throws KeyTypeNotImplemented */ public function testResultFailsIfConnectionToServerFailed() : void { $serverKey = '1'; $key = 'some-key'; - $hashKey = null; + $subKey = null; - $query = new FetchKeyInformationQuery( $serverKey, 0, $key, $hashKey ); - $result = (new FetchKeyInformationQueryHandler( $this->getEnvMock( $serverKey ) ))->handle( $query ); + $query = new FetchKeyInformationQuery( 0, $key, $subKey ); + $result = (new FetchKeyInformationQueryHandler( $this->getServerManagerMock( $serverKey ) ))->handle( $query ); $this->assertFalse( $result->succeeded() ); $this->assertTrue( $result->failed() ); diff --git a/tests/Integration/Application/ReadModel/QueryHandlers/FindKeysInDatabaseQueryHandlerTest.php b/tests/Integration/Application/ReadModel/QueryHandlers/FindKeysInDatabaseQueryHandlerTest.php index 534d97f..92af332 100644 --- a/tests/Integration/Application/ReadModel/QueryHandlers/FindKeysInDatabaseQueryHandlerTest.php +++ b/tests/Integration/Application/ReadModel/QueryHandlers/FindKeysInDatabaseQueryHandlerTest.php @@ -5,6 +5,7 @@ use hollodotme\Readis\Application\Interfaces\ProvidesKeyInfo; use hollodotme\Readis\Application\ReadModel\Queries\FindKeysInDatabaseQuery; use hollodotme\Readis\Application\ReadModel\QueryHandlers\FindKeysInDatabaseQueryHandler; +use hollodotme\Readis\Exceptions\NoServersConfigured; use hollodotme\Readis\Exceptions\ServerConfigNotFound; use PHPUnit\Framework\ExpectationFailedException; use SebastianBergmann\RecursionContext\InvalidArgumentException; @@ -21,6 +22,7 @@ final class FindKeysInDatabaseQueryHandlerTest extends AbstractQueryHandlerTest * @throws ExpectationFailedException * @throws InvalidArgumentException * @throws ServerConfigNotFound + * @throws NoServersConfigured * @dataProvider keySearchProvider */ public function testCanFindKeysInDatabase( @@ -48,7 +50,7 @@ public function keySearchProvider() : array 'database' => 0, 'searchPattern' => '*', 'limit' => null, - 'expectedKeyCount' => 2, + 'expectedKeyCount' => 7, ], [ 'serverKey' => '0', @@ -60,14 +62,14 @@ public function keySearchProvider() : array [ 'serverKey' => '0', 'database' => 0, - 'searchPattern' => '*ni*', + 'searchPattern' => '*as*', 'limit' => null, 'expectedKeyCount' => 1, ], [ 'serverKey' => '0', 'database' => 0, - 'searchPattern' => '*es*', + 'searchPattern' => '*tri*', 'limit' => null, 'expectedKeyCount' => 1, ], From 8dadb032bd68e9cc4a8d973b392ebf9dd853eaf1 Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Sun, 13 May 2018 02:23:44 +0200 Subject: [PATCH 36/62] Fix docker compose config --- docker-compose.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 9c441de..29bd5af 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,7 +5,7 @@ services: ports: - 6379:6379 volumes: - - .redis/data:/data - - .redis/redis.default.conf:/usr/local/etc/redis/redis.conf + - .redis/data:/data + - .redis/redis.default.conf:/usr/local/etc/redis/redis.conf - ./:/repo - command: /usr/local/etc/redis/redis.conf \ No newline at end of file + command: /usr/local/etc/redis/redis.conf From 28b9bb716d9b82b3c009bd754e34d96e79f9ab07 Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Sun, 13 May 2018 12:43:19 +0200 Subject: [PATCH 37/62] Refactor key info sub items and key data builders for more efficiency --- .../KeyDataBuilders/ListKeyDataBuilder.php | 14 +++- .../KeyDataBuilders/ListSubKeyDataBuilder.php | 2 +- .../KeyDataBuilders/SetKeyDataBuilder.php | 14 +++- .../KeyDataBuilders/SetSubKeyDataBuilder.php | 10 ++- .../SortedSetKeyDataBuilder.php | 15 +++- .../SortedSetSubKeyDataBuilder.php | 6 +- .../Server/Read/Pages/Includes/KeyList.twig | 8 +- src/Infrastructure/Redis/ServerManager.php | 83 ++++++++++++++++--- 8 files changed, 125 insertions(+), 27 deletions(-) diff --git a/src/Application/ReadModel/KeyDataBuilders/ListKeyDataBuilder.php b/src/Application/ReadModel/KeyDataBuilders/ListKeyDataBuilder.php index e2b6824..b1123a4 100644 --- a/src/Application/ReadModel/KeyDataBuilders/ListKeyDataBuilder.php +++ b/src/Application/ReadModel/KeyDataBuilders/ListKeyDataBuilder.php @@ -9,6 +9,7 @@ use hollodotme\Readis\Application\ReadModel\Interfaces\PrettifiesString; use hollodotme\Readis\Application\ReadModel\Interfaces\ProvidesKeyData; use hollodotme\Readis\Application\ReadModel\Interfaces\ProvidesKeyName; +use hollodotme\Readis\Infrastructure\Redis\Exceptions\ConnectionFailedException; use hollodotme\Readis\Infrastructure\Redis\ServerManager; final class ListKeyDataBuilder implements BuildsKeyData @@ -32,16 +33,25 @@ public function canBuildKeyData( ProvidesKeyInfo $keyInfo, ProvidesKeyName $keyN return !$keyName->hasSubKey() && (KeyType::LIST === $keyInfo->getType()); } + /** + * @param ProvidesKeyInfo $keyInfo + * @param ProvidesKeyName $keyName + * + * @throws ConnectionFailedException + * @return ProvidesKeyData + */ public function buildKeyData( ProvidesKeyInfo $keyInfo, ProvidesKeyName $keyName ) : ProvidesKeyData { + $elements = $this->manager->getAllListElements( $keyName->getKeyName() ); + $rawElements = []; - foreach ( $keyInfo->getSubItems() as $index => $element ) + foreach ( $elements as $index => $element ) { $rawElements[] = $this->getElementOutput( $index, $element ); } $prettyElements = []; - foreach ( $keyInfo->getSubItems() as $index => $element ) + foreach ( $elements as $index => $element ) { $prettyElement = $this->prettifier->prettify( $element ); $prettyElements[] = $this->getElementOutput( $index, $prettyElement ); diff --git a/src/Application/ReadModel/KeyDataBuilders/ListSubKeyDataBuilder.php b/src/Application/ReadModel/KeyDataBuilders/ListSubKeyDataBuilder.php index aae5c88..57eae83 100644 --- a/src/Application/ReadModel/KeyDataBuilders/ListSubKeyDataBuilder.php +++ b/src/Application/ReadModel/KeyDataBuilders/ListSubKeyDataBuilder.php @@ -40,7 +40,7 @@ public function canBuildKeyData( ProvidesKeyInfo $keyInfo, ProvidesKeyName $keyN */ public function buildKeyData( ProvidesKeyInfo $keyInfo, ProvidesKeyName $keyName ) : ProvidesKeyData { - $rawKeyData = $this->manager->getListValue( $keyName->getKeyName(), (int)$keyName->getSubKey() ) ?: ''; + $rawKeyData = $this->manager->getListElement( $keyName->getKeyName(), (int)$keyName->getSubKey() ) ?: ''; $keyData = $this->prettifier->prettify( $rawKeyData ); return new KeyData( $keyData, $rawKeyData ); diff --git a/src/Application/ReadModel/KeyDataBuilders/SetKeyDataBuilder.php b/src/Application/ReadModel/KeyDataBuilders/SetKeyDataBuilder.php index f25b327..6fbbb16 100644 --- a/src/Application/ReadModel/KeyDataBuilders/SetKeyDataBuilder.php +++ b/src/Application/ReadModel/KeyDataBuilders/SetKeyDataBuilder.php @@ -9,6 +9,7 @@ use hollodotme\Readis\Application\ReadModel\Interfaces\PrettifiesString; use hollodotme\Readis\Application\ReadModel\Interfaces\ProvidesKeyData; use hollodotme\Readis\Application\ReadModel\Interfaces\ProvidesKeyName; +use hollodotme\Readis\Infrastructure\Redis\Exceptions\ConnectionFailedException; use hollodotme\Readis\Infrastructure\Redis\ServerManager; final class SetKeyDataBuilder implements BuildsKeyData @@ -32,16 +33,25 @@ public function canBuildKeyData( ProvidesKeyInfo $keyInfo, ProvidesKeyName $keyN return !$keyName->hasSubKey() && (KeyType::SET === $keyInfo->getType()); } + /** + * @param ProvidesKeyInfo $keyInfo + * @param ProvidesKeyName $keyName + * + * @throws ConnectionFailedException + * @return ProvidesKeyData + */ public function buildKeyData( ProvidesKeyInfo $keyInfo, ProvidesKeyName $keyName ) : ProvidesKeyData { + $members = $this->manager->getAllSetMembers( $keyName->getKeyName() ); + $rawMembers = []; - foreach ( $keyInfo->getSubItems() as $index => $member ) + foreach ( $members as $index => $member ) { $rawMembers[] = $this->getMemberOutput( $index, $member ); } $prettyMembers = []; - foreach ( $keyInfo->getSubItems() as $index => $member ) + foreach ( $members as $index => $member ) { $prettyMember = $this->prettifier->prettify( $member ); $prettyMembers[] = $this->getMemberOutput( $index, $prettyMember ); diff --git a/src/Application/ReadModel/KeyDataBuilders/SetSubKeyDataBuilder.php b/src/Application/ReadModel/KeyDataBuilders/SetSubKeyDataBuilder.php index 5111eb0..6a018b5 100644 --- a/src/Application/ReadModel/KeyDataBuilders/SetSubKeyDataBuilder.php +++ b/src/Application/ReadModel/KeyDataBuilders/SetSubKeyDataBuilder.php @@ -9,6 +9,7 @@ use hollodotme\Readis\Application\ReadModel\Interfaces\PrettifiesString; use hollodotme\Readis\Application\ReadModel\Interfaces\ProvidesKeyData; use hollodotme\Readis\Application\ReadModel\Interfaces\ProvidesKeyName; +use hollodotme\Readis\Infrastructure\Redis\Exceptions\ConnectionFailedException; use hollodotme\Readis\Infrastructure\Redis\ServerManager; final class SetSubKeyDataBuilder implements BuildsKeyData @@ -30,9 +31,16 @@ public function canBuildKeyData( ProvidesKeyInfo $keyInfo, ProvidesKeyName $keyN return $keyName->hasSubKey() && (KeyType::SET === $keyInfo->getType()); } + /** + * @param ProvidesKeyInfo $keyInfo + * @param ProvidesKeyName $keyName + * + * @throws ConnectionFailedException + * @return ProvidesKeyData + */ public function buildKeyData( ProvidesKeyInfo $keyInfo, ProvidesKeyName $keyName ) : ProvidesKeyData { - $rawKeyData = $keyInfo->getSubItems()[ (int)$keyName->getSubKey() ]; + $rawKeyData = $this->manager->getSetMember( $keyName->getKeyName(), (int)$keyName->getSubKey() ); $keyData = $this->prettifier->prettify( $rawKeyData ); return new KeyData( $keyData, $rawKeyData ); diff --git a/src/Application/ReadModel/KeyDataBuilders/SortedSetKeyDataBuilder.php b/src/Application/ReadModel/KeyDataBuilders/SortedSetKeyDataBuilder.php index 1e2f85a..46d99d6 100644 --- a/src/Application/ReadModel/KeyDataBuilders/SortedSetKeyDataBuilder.php +++ b/src/Application/ReadModel/KeyDataBuilders/SortedSetKeyDataBuilder.php @@ -9,6 +9,7 @@ use hollodotme\Readis\Application\ReadModel\Interfaces\PrettifiesString; use hollodotme\Readis\Application\ReadModel\Interfaces\ProvidesKeyData; use hollodotme\Readis\Application\ReadModel\Interfaces\ProvidesKeyName; +use hollodotme\Readis\Infrastructure\Redis\Exceptions\ConnectionFailedException; use hollodotme\Readis\Infrastructure\Redis\ServerManager; final class SortedSetKeyDataBuilder implements BuildsKeyData @@ -32,19 +33,27 @@ public function canBuildKeyData( ProvidesKeyInfo $keyInfo, ProvidesKeyName $keyN return !$keyName->hasSubKey() && (KeyType::SORTED_SET === $keyInfo->getType()); } + /** + * @param ProvidesKeyInfo $keyInfo + * @param ProvidesKeyName $keyName + * + * @throws ConnectionFailedException + * @return ProvidesKeyData + */ public function buildKeyData( ProvidesKeyInfo $keyInfo, ProvidesKeyName $keyName ) : ProvidesKeyData { - $setMembers = $keyInfo->getSubItems(); + $members = $this->manager->getAllSortedSetMembers( $keyName->getKeyName() ); + $rawMembers = []; $index = 0; - foreach ( $setMembers as $member => $score ) + foreach ( $members as $member => $score ) { $rawMembers[] = $this->getMemberOutput( $index++, $member, $score ); } $prettyMembers = []; $index = 0; - foreach ( $setMembers as $member => $score ) + foreach ( $members as $member => $score ) { $prettyMember = $this->prettifier->prettify( $member ); $prettyMembers[] = $this->getMemberOutput( $index++, $prettyMember, $score ); diff --git a/src/Application/ReadModel/KeyDataBuilders/SortedSetSubKeyDataBuilder.php b/src/Application/ReadModel/KeyDataBuilders/SortedSetSubKeyDataBuilder.php index 414c397..1ca91f6 100644 --- a/src/Application/ReadModel/KeyDataBuilders/SortedSetSubKeyDataBuilder.php +++ b/src/Application/ReadModel/KeyDataBuilders/SortedSetSubKeyDataBuilder.php @@ -40,8 +40,10 @@ public function canBuildKeyData( ProvidesKeyInfo $keyInfo, ProvidesKeyName $keyN */ public function buildKeyData( ProvidesKeyInfo $keyInfo, ProvidesKeyName $keyName ) : ProvidesKeyData { - $i = 0; - foreach ( $keyInfo->getSubItems() as $member => $score ) + $members = $this->manager->getAllSortedSetMembers( $keyName->getKeyName() ); + $i = 0; + + foreach ( $members as $member => $score ) { if ( (int)$keyName->getSubKey() !== $i++ ) { diff --git a/src/Application/Web/Server/Read/Pages/Includes/KeyList.twig b/src/Application/Web/Server/Read/Pages/Includes/KeyList.twig index 543431c..d8a4ef1 100644 --- a/src/Application/Web/Server/Read/Pages/Includes/KeyList.twig +++ b/src/Application/Web/Server/Read/Pages/Includes/KeyList.twig @@ -77,11 +77,11 @@ (show all members) - {% for member,score in keyInfo.getSubItems %} + {% for index,score in keyInfo.getSubItems %}
  • - - Member {{ loop.index0 }} + Member {{ index }} | Score: {{ score }} @@ -104,7 +104,7 @@ (show all elements)
  • - {% for index,value in keyInfo.getSubItems %} + {% for index in keyInfo.getSubItems %}
  • diff --git a/src/Infrastructure/Redis/ServerManager.php b/src/Infrastructure/Redis/ServerManager.php index aec7b1d..a75e72c 100644 --- a/src/Infrastructure/Redis/ServerManager.php +++ b/src/Infrastructure/Redis/ServerManager.php @@ -140,7 +140,7 @@ public function getKeyInfoObject( string $key ) : ProvidesKeyInfo $listLength = $this->redis->llen( $key ); /** @noinspection PhpUndefinedMethodInspection */ - $subItems = $this->redis->lrange( $key, 0, $listLength - 1 ); + $subItems = range( 0, $listLength - 1 ); return new KeyInfo( $key, $type, $ttl, $subItems ); } @@ -148,7 +148,7 @@ public function getKeyInfoObject( string $key ) : ProvidesKeyInfo if ( $type === Redis::REDIS_SET ) { /** @noinspection PhpUndefinedMethodInspection */ - $subItems = $this->redis->smembers( $key ); + $subItems = range( 0, $this->redis->scard( $key ) - 1 ); return new KeyInfo( $key, $type, $ttl, $subItems ); } @@ -159,7 +159,10 @@ public function getKeyInfoObject( string $key ) : ProvidesKeyInfo $setLength = $this->redis->zcard( $key ); /** @noinspection PhpUndefinedMethodInspection */ - $subItems = $this->redis->zrange( $key, 0, $setLength - 1, true ); + $subItems = array_combine( + range( 0, $setLength - 1 ), + array_values( $this->redis->zrange( $key, 0, $setLength - 1, true ) ) + ); return new KeyInfo( $key, $type, $ttl, $subItems ); } @@ -170,26 +173,26 @@ public function getKeyInfoObject( string $key ) : ProvidesKeyInfo /** * @param string $key * - * @return bool|string + * @return string * @throws ConnectionFailedException */ - public function getValue( string $key ) + public function getValue( string $key ) : string { /** @noinspection PhpUndefinedMethodInspection */ - return $this->redis->get( $key ); + return (string)$this->redis->get( $key ); } /** * @param string $key * @param string $hashKey * - * @return bool|string + * @return string * @throws ConnectionFailedException */ - public function getHashValue( string $key, string $hashKey ) + public function getHashValue( string $key, string $hashKey ) : string { /** @noinspection PhpUndefinedMethodInspection */ - return $this->redis->hGet( $key, $hashKey ); + return (string)$this->redis->hGet( $key, $hashKey ); } /** @@ -209,11 +212,67 @@ public function getAllHashValues( string $key ) : array * @param int $index * * @throws ConnectionFailedException - * @return mixed + * @return string */ - public function getListValue( string $key, int $index ) + public function getListElement( string $key, int $index ) : string { /** @noinspection PhpUndefinedMethodInspection */ - return $this->redis->lindex( $key, $index ); + return (string)$this->redis->lindex( $key, $index ); + } + + /** + * @param string $key + * + * @throws ConnectionFailedException + * @return array + */ + public function getAllListElements( string $key ) : array + { + /** @noinspection PhpUndefinedMethodInspection */ + $count = $this->redis->llen( $key ); + + /** @noinspection PhpUndefinedMethodInspection */ + return (array)$this->redis->lrange( $key, 0, $count - 1 ); + } + + /** + * @param string $key + * @param int $index + * + * @throws ConnectionFailedException + * @return string + */ + public function getSetMember( string $key, int $index ) : string + { + $members = $this->getAllSetMembers( $key ); + + return $members[ $index ]; + } + + /** + * @param string $key + * + * @throws ConnectionFailedException + * @return array + */ + public function getAllSetMembers( string $key ) : array + { + /** @noinspection PhpUndefinedMethodInspection */ + return (array)$this->redis->smembers( $key ); + } + + /** + * @param string $key + * + * @throws ConnectionFailedException + * @return array + */ + public function getAllSortedSetMembers( string $key ) : array + { + /** @noinspection PhpUndefinedMethodInspection */ + $setLength = $this->redis->zcard( $key ); + + /** @noinspection PhpUndefinedMethodInspection */ + return (array)$this->redis->zrange( $key, 0, $setLength - 1, true ); } } From b7a6b2a3ba70b7231c5b28cb5ced23a94b5682ca Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Sun, 13 May 2018 12:43:47 +0200 Subject: [PATCH 38/62] Remove set data sets in data provided --- .../FetchKeyInformationQueryHandlerTest.php | 36 ------------------- 1 file changed, 36 deletions(-) diff --git a/tests/Integration/Application/ReadModel/QueryHandlers/FetchKeyInformationQueryHandlerTest.php b/tests/Integration/Application/ReadModel/QueryHandlers/FetchKeyInformationQueryHandlerTest.php index 2929063..97d0fa8 100644 --- a/tests/Integration/Application/ReadModel/QueryHandlers/FetchKeyInformationQueryHandlerTest.php +++ b/tests/Integration/Application/ReadModel/QueryHandlers/FetchKeyInformationQueryHandlerTest.php @@ -133,42 +133,6 @@ public function keyInfoProvider() : array 'expectedHasScore' => false, 'expectedScore' => null, ], - [ - 'key' => 'set', - 'subKey' => '0', - 'expectedType' => 'set', - 'expectedKeyData' => 'two', - 'expectedRawKeyData' => 'two', - 'expectedHasScore' => false, - 'expectedScore' => null, - ], - [ - 'key' => 'set', - 'subKey' => '1', - 'expectedType' => 'set', - 'expectedKeyData' => "{\n \"json\": {\n \"key\": \"value\"\n }\n}", - 'expectedRawKeyData' => '{"json": {"key": "value"}}', - 'expectedHasScore' => false, - 'expectedScore' => null, - ], - [ - 'key' => 'set', - 'subKey' => '2', - 'expectedType' => 'set', - 'expectedKeyData' => 'one', - 'expectedRawKeyData' => 'one', - 'expectedHasScore' => false, - 'expectedScore' => null, - ], - [ - 'key' => 'set', - 'subKey' => null, - 'expectedType' => 'set', - 'expectedKeyData' => "Member 0:\n=========\ntwo\n\n---\n\nMember 1:\n=========\n{\n \"json\": {\n \"key\": \"value\"\n }\n}\n\n---\n\nMember 2:\n=========\none", - 'expectedRawKeyData' => "Member 0:\n=========\ntwo\n\n---\n\nMember 1:\n=========\n{\"json\": {\"key\": \"value\"}}\n\n---\n\nMember 2:\n=========\none", - 'expectedHasScore' => false, - 'expectedScore' => null, - ], [ 'key' => 'sorted set', 'subKey' => '0', From bacc996b7eedd5d3fa21b162036cc5b817f62416 Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Sun, 13 May 2018 13:18:07 +0200 Subject: [PATCH 39/62] Move server manager tests to integration tests --- .../Infrastructure/Redis/ServerManagerTest.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) rename tests/{Unit => Integration}/Infrastructure/Redis/ServerManagerTest.php (97%) diff --git a/tests/Unit/Infrastructure/Redis/ServerManagerTest.php b/tests/Integration/Infrastructure/Redis/ServerManagerTest.php similarity index 97% rename from tests/Unit/Infrastructure/Redis/ServerManagerTest.php rename to tests/Integration/Infrastructure/Redis/ServerManagerTest.php index fe73560..9a7384a 100644 --- a/tests/Unit/Infrastructure/Redis/ServerManagerTest.php +++ b/tests/Integration/Infrastructure/Redis/ServerManagerTest.php @@ -1,6 +1,6 @@ getServerConnectionMock( 'localhost', 9999 ) ); $this->expectException( ConnectionFailedException::class ); - $this->expectExceptionMessage( 'host: localhost, port: 9999, timeout: 2.5, retryInterval: 100, using auth: no' ); + $this->expectExceptionMessage( + 'host: localhost, port: 9999, timeout: 2.5, retryInterval: 100, using auth: no' + ); $serverManager->getKeys( '*' ); } From dc962496b88783b7a2f1be1d6efd68c7be4ce62b Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Sun, 13 May 2018 13:18:24 +0200 Subject: [PATCH 40/62] Add HyperLogLog prettifier tests --- .../Prettifiers/HyperLogLogPrettifierTest.php | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 tests/Unit/Application/ReadModel/Prettifiers/HyperLogLogPrettifierTest.php diff --git a/tests/Unit/Application/ReadModel/Prettifiers/HyperLogLogPrettifierTest.php b/tests/Unit/Application/ReadModel/Prettifiers/HyperLogLogPrettifierTest.php new file mode 100644 index 0000000..721a8d9 --- /dev/null +++ b/tests/Unit/Application/ReadModel/Prettifiers/HyperLogLogPrettifierTest.php @@ -0,0 +1,69 @@ +assertSame( $expectedResult, $prettifier->canPrettify( $value ) ); + } + + public function canPrettifyProvider() : array + { + return [ + [ + 'value' => 'Some random value', + 'expectedResult' => false, + ], + [ + 'value' => 'HYLL Some random value', + 'expectedResult' => true, + ], + ]; + } + + /** + * @param string $value + * @param string $expectedPrettyValue + * + * @throws \PHPUnit\Framework\ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @dataProvider prettifyDataProvider + */ + public function testCanGetPrettifiedValue( string $value, string $expectedPrettyValue ) : void + { + $prettifier = new HyperLogLogPrettifier(); + + $this->assertSame( $expectedPrettyValue, $prettifier->prettify( $value ) ); + } + + public function prettifyDataProvider() : array + { + return [ + [ + 'value' => 'Some random value', + 'expectedPrettyValue' => "Some random value\n\n(HyperLogLog encoded value)", + ], + [ + 'value' => 'HYLL Some random value', + 'expectedPrettyValue' => "HYLL Some random value\n\n(HyperLogLog encoded value)", + ], + ]; + } +} From ea5032ce13b514c924b33bf5ccc94e6c705f2add Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Sun, 13 May 2018 13:18:40 +0200 Subject: [PATCH 41/62] Add key data builder tests --- .../KeyDataBuilders/KeyDataBuilderTest.php | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 tests/Unit/Application/ReadModel/KeyDataBuilders/KeyDataBuilderTest.php diff --git a/tests/Unit/Application/ReadModel/KeyDataBuilders/KeyDataBuilderTest.php b/tests/Unit/Application/ReadModel/KeyDataBuilders/KeyDataBuilderTest.php new file mode 100644 index 0000000..11b59a2 --- /dev/null +++ b/tests/Unit/Application/ReadModel/KeyDataBuilders/KeyDataBuilderTest.php @@ -0,0 +1,109 @@ +getBuilderMock() ); + + $keyInfoStub = $this->getMockBuilder( ProvidesKeyInfo::class )->getMockForAbstractClass(); + $keyInfoStub->method( 'getType' )->willReturn( 'hash' ); + $keysNameStub = $this->getMockBuilder( ProvidesKeyName::class )->getMockForAbstractClass(); + + /** @var ProvidesKeyInfo $keyInfoStub */ + /** @var ProvidesKeyName $keysNameStub */ + $keyData = $keyDataBuilder->buildKeyData( $keyInfoStub, $keysNameStub ); + + $this->assertSame( 'keyData', $keyData->getKeyData() ); + $this->assertSame( 'rawKeyData', $keyData->getRawKeyData() ); + $this->assertFalse( $keyData->hasScore() ); + $this->assertNull( $keyData->getScore() ); + } + + private function getBuilderMock() : BuildsKeyData + { + return new class implements BuildsKeyData + { + public function canBuildKeyData( ProvidesKeyInfo $keyInfo, ProvidesKeyName $keyName ) : bool + { + return ($keyInfo->getType() !== 'unknown'); + } + + public function buildKeyData( ProvidesKeyInfo $keyInfo, ProvidesKeyName $keyName ) : ProvidesKeyData + { + return new class implements ProvidesKeyData + { + public function getKeyData() : string + { + return 'keyData'; + } + + public function getRawKeyData() : string + { + return 'rawKeyData'; + } + + public function hasScore() : bool + { + return false; + } + + public function getScore() : ?float + { + return null; + } + }; + } + }; + } + + /** + * @throws KeyTypeNotImplemented + */ + public function testBuildKeyDataThrowsExceptionForUnknownKeyType() + { + $keyDataBuilder = new KeyDataBuilder( $this->getBuilderMock() ); + + $keyInfoStub = $this->getMockBuilder( ProvidesKeyInfo::class )->getMockForAbstractClass(); + $keyInfoStub->method( 'getType' )->willReturn( 'unknown' ); + $keysNameStub = $this->getMockBuilder( ProvidesKeyName::class )->getMockForAbstractClass(); + + $this->expectException( KeyTypeNotImplemented::class ); + $this->expectExceptionMessage( 'Key type not implemented or supported: unknown' ); + + /** @var ProvidesKeyInfo $keyInfoStub */ + /** @var ProvidesKeyName $keysNameStub */ + $keyDataBuilder->buildKeyData( $keyInfoStub, $keysNameStub ); + } + + /** + * @throws \PHPUnit\Framework\ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function testCanBuildKeyData() + { + $keyDataBuilder = new KeyDataBuilder( $this->getBuilderMock() ); + + /** @var ProvidesKeyInfo $keyInfoStub */ + $keyInfoStub = $this->getMockBuilder( ProvidesKeyInfo::class )->getMockForAbstractClass(); + /** @var ProvidesKeyName $keysNameStub */ + $keysNameStub = $this->getMockBuilder( ProvidesKeyName::class )->getMockForAbstractClass(); + + $this->assertTrue( $keyDataBuilder->canBuildKeyData( $keyInfoStub, $keysNameStub ) ); + } +} From 90f1592ff78135eca869fc06380fd9845c52e146 Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Sun, 13 May 2018 13:50:58 +0200 Subject: [PATCH 42/62] Supress unknown HTML code for inspections --- src/Application/Configs/IceHawkConfig.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Application/Configs/IceHawkConfig.php b/src/Application/Configs/IceHawkConfig.php index 2f93a7b..15cc27d 100644 --- a/src/Application/Configs/IceHawkConfig.php +++ b/src/Application/Configs/IceHawkConfig.php @@ -1,4 +1,6 @@ - Date: Sun, 13 May 2018 13:51:54 +0200 Subject: [PATCH 43/62] Supress RedisException not thrown for inspections --- src/Infrastructure/Redis/RedisWrapper.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Infrastructure/Redis/RedisWrapper.php b/src/Infrastructure/Redis/RedisWrapper.php index 284e298..bd1e31f 100644 --- a/src/Infrastructure/Redis/RedisWrapper.php +++ b/src/Infrastructure/Redis/RedisWrapper.php @@ -58,6 +58,7 @@ private function connectToServer() : void $this->connectionData->getRetryInterval() ); } + /** @noinspection PhpRedundantCatchClauseInspection */ catch ( RedisException $e ) { throw (new ConnectionFailedException())->withConnectionData( $this->connectionData ); From 41709b48b6209bc10e2bd690ae07460282171f84 Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Sun, 13 May 2018 13:52:50 +0200 Subject: [PATCH 44/62] Extract interface ProvidesRedisData for class ServerManager --- .../Interfaces/ProvidesRedisData.php | 134 ++++++++++++++++++ .../KeyDataBuilders/HashKeyDataBuilder.php | 6 +- .../KeyDataBuilders/HashSubKeyDataBuilder.php | 6 +- .../KeyDataBuilders/ListKeyDataBuilder.php | 6 +- .../KeyDataBuilders/ListSubKeyDataBuilder.php | 6 +- .../KeyDataBuilders/SetKeyDataBuilder.php | 6 +- .../KeyDataBuilders/SetSubKeyDataBuilder.php | 6 +- .../SortedSetKeyDataBuilder.php | 6 +- .../SortedSetSubKeyDataBuilder.php | 6 +- .../KeyDataBuilders/StringKeyDataBuilder.php | 6 +- .../FetchKeyInformationQueryHandler.php | 6 +- 11 files changed, 164 insertions(+), 30 deletions(-) create mode 100644 src/Application/Interfaces/ProvidesRedisData.php diff --git a/src/Application/Interfaces/ProvidesRedisData.php b/src/Application/Interfaces/ProvidesRedisData.php new file mode 100644 index 0000000..f2bd7a2 --- /dev/null +++ b/src/Application/Interfaces/ProvidesRedisData.php @@ -0,0 +1,134 @@ +manager = $manager; $this->prettifier = $prettifier; diff --git a/src/Application/ReadModel/KeyDataBuilders/HashSubKeyDataBuilder.php b/src/Application/ReadModel/KeyDataBuilders/HashSubKeyDataBuilder.php index 9626762..d1c89df 100644 --- a/src/Application/ReadModel/KeyDataBuilders/HashSubKeyDataBuilder.php +++ b/src/Application/ReadModel/KeyDataBuilders/HashSubKeyDataBuilder.php @@ -3,6 +3,7 @@ namespace hollodotme\Readis\Application\ReadModel\KeyDataBuilders; use hollodotme\Readis\Application\Interfaces\ProvidesKeyInfo; +use hollodotme\Readis\Application\Interfaces\ProvidesRedisData; use hollodotme\Readis\Application\ReadModel\Constants\KeyType; use hollodotme\Readis\Application\ReadModel\DTO\KeyData; use hollodotme\Readis\Application\ReadModel\Interfaces\BuildsKeyData; @@ -10,17 +11,16 @@ use hollodotme\Readis\Application\ReadModel\Interfaces\ProvidesKeyData; use hollodotme\Readis\Application\ReadModel\Interfaces\ProvidesKeyName; use hollodotme\Readis\Infrastructure\Redis\Exceptions\ConnectionFailedException; -use hollodotme\Readis\Infrastructure\Redis\ServerManager; final class HashSubKeyDataBuilder implements BuildsKeyData { - /** @var ServerManager */ + /** @var ProvidesRedisData */ private $manager; /** @var PrettifiesString */ private $prettifier; - public function __construct( ServerManager $manager, PrettifiesString $prettifier ) + public function __construct( ProvidesRedisData $manager, PrettifiesString $prettifier ) { $this->manager = $manager; $this->prettifier = $prettifier; diff --git a/src/Application/ReadModel/KeyDataBuilders/ListKeyDataBuilder.php b/src/Application/ReadModel/KeyDataBuilders/ListKeyDataBuilder.php index b1123a4..5ee21f4 100644 --- a/src/Application/ReadModel/KeyDataBuilders/ListKeyDataBuilder.php +++ b/src/Application/ReadModel/KeyDataBuilders/ListKeyDataBuilder.php @@ -3,6 +3,7 @@ namespace hollodotme\Readis\Application\ReadModel\KeyDataBuilders; use hollodotme\Readis\Application\Interfaces\ProvidesKeyInfo; +use hollodotme\Readis\Application\Interfaces\ProvidesRedisData; use hollodotme\Readis\Application\ReadModel\Constants\KeyType; use hollodotme\Readis\Application\ReadModel\DTO\KeyData; use hollodotme\Readis\Application\ReadModel\Interfaces\BuildsKeyData; @@ -10,19 +11,18 @@ use hollodotme\Readis\Application\ReadModel\Interfaces\ProvidesKeyData; use hollodotme\Readis\Application\ReadModel\Interfaces\ProvidesKeyName; use hollodotme\Readis\Infrastructure\Redis\Exceptions\ConnectionFailedException; -use hollodotme\Readis\Infrastructure\Redis\ServerManager; final class ListKeyDataBuilder implements BuildsKeyData { private const ELEMENT_SEPARATOR = "\n\n---\n\n"; - /** @var ServerManager */ + /** @var ProvidesRedisData */ private $manager; /** @var PrettifiesString */ private $prettifier; - public function __construct( ServerManager $manager, PrettifiesString $prettifier ) + public function __construct( ProvidesRedisData $manager, PrettifiesString $prettifier ) { $this->manager = $manager; $this->prettifier = $prettifier; diff --git a/src/Application/ReadModel/KeyDataBuilders/ListSubKeyDataBuilder.php b/src/Application/ReadModel/KeyDataBuilders/ListSubKeyDataBuilder.php index 57eae83..2013789 100644 --- a/src/Application/ReadModel/KeyDataBuilders/ListSubKeyDataBuilder.php +++ b/src/Application/ReadModel/KeyDataBuilders/ListSubKeyDataBuilder.php @@ -3,6 +3,7 @@ namespace hollodotme\Readis\Application\ReadModel\KeyDataBuilders; use hollodotme\Readis\Application\Interfaces\ProvidesKeyInfo; +use hollodotme\Readis\Application\Interfaces\ProvidesRedisData; use hollodotme\Readis\Application\ReadModel\Constants\KeyType; use hollodotme\Readis\Application\ReadModel\DTO\KeyData; use hollodotme\Readis\Application\ReadModel\Interfaces\BuildsKeyData; @@ -10,17 +11,16 @@ use hollodotme\Readis\Application\ReadModel\Interfaces\ProvidesKeyData; use hollodotme\Readis\Application\ReadModel\Interfaces\ProvidesKeyName; use hollodotme\Readis\Infrastructure\Redis\Exceptions\ConnectionFailedException; -use hollodotme\Readis\Infrastructure\Redis\ServerManager; final class ListSubKeyDataBuilder implements BuildsKeyData { - /** @var ServerManager */ + /** @var ProvidesRedisData */ private $manager; /** @var PrettifiesString */ private $prettifier; - public function __construct( ServerManager $manager, PrettifiesString $prettifier ) + public function __construct( ProvidesRedisData $manager, PrettifiesString $prettifier ) { $this->manager = $manager; $this->prettifier = $prettifier; diff --git a/src/Application/ReadModel/KeyDataBuilders/SetKeyDataBuilder.php b/src/Application/ReadModel/KeyDataBuilders/SetKeyDataBuilder.php index 6fbbb16..c6aadee 100644 --- a/src/Application/ReadModel/KeyDataBuilders/SetKeyDataBuilder.php +++ b/src/Application/ReadModel/KeyDataBuilders/SetKeyDataBuilder.php @@ -3,6 +3,7 @@ namespace hollodotme\Readis\Application\ReadModel\KeyDataBuilders; use hollodotme\Readis\Application\Interfaces\ProvidesKeyInfo; +use hollodotme\Readis\Application\Interfaces\ProvidesRedisData; use hollodotme\Readis\Application\ReadModel\Constants\KeyType; use hollodotme\Readis\Application\ReadModel\DTO\KeyData; use hollodotme\Readis\Application\ReadModel\Interfaces\BuildsKeyData; @@ -10,19 +11,18 @@ use hollodotme\Readis\Application\ReadModel\Interfaces\ProvidesKeyData; use hollodotme\Readis\Application\ReadModel\Interfaces\ProvidesKeyName; use hollodotme\Readis\Infrastructure\Redis\Exceptions\ConnectionFailedException; -use hollodotme\Readis\Infrastructure\Redis\ServerManager; final class SetKeyDataBuilder implements BuildsKeyData { private const MEMBER_SEPARATOR = "\n\n---\n\n"; - /** @var ServerManager */ + /** @var ProvidesRedisData */ private $manager; /** @var PrettifiesString */ private $prettifier; - public function __construct( ServerManager $manager, PrettifiesString $prettifier ) + public function __construct( ProvidesRedisData $manager, PrettifiesString $prettifier ) { $this->manager = $manager; $this->prettifier = $prettifier; diff --git a/src/Application/ReadModel/KeyDataBuilders/SetSubKeyDataBuilder.php b/src/Application/ReadModel/KeyDataBuilders/SetSubKeyDataBuilder.php index 6a018b5..488a53a 100644 --- a/src/Application/ReadModel/KeyDataBuilders/SetSubKeyDataBuilder.php +++ b/src/Application/ReadModel/KeyDataBuilders/SetSubKeyDataBuilder.php @@ -3,6 +3,7 @@ namespace hollodotme\Readis\Application\ReadModel\KeyDataBuilders; use hollodotme\Readis\Application\Interfaces\ProvidesKeyInfo; +use hollodotme\Readis\Application\Interfaces\ProvidesRedisData; use hollodotme\Readis\Application\ReadModel\Constants\KeyType; use hollodotme\Readis\Application\ReadModel\DTO\KeyData; use hollodotme\Readis\Application\ReadModel\Interfaces\BuildsKeyData; @@ -10,17 +11,16 @@ use hollodotme\Readis\Application\ReadModel\Interfaces\ProvidesKeyData; use hollodotme\Readis\Application\ReadModel\Interfaces\ProvidesKeyName; use hollodotme\Readis\Infrastructure\Redis\Exceptions\ConnectionFailedException; -use hollodotme\Readis\Infrastructure\Redis\ServerManager; final class SetSubKeyDataBuilder implements BuildsKeyData { - /** @var ServerManager */ + /** @var ProvidesRedisData */ private $manager; /** @var PrettifiesString */ private $prettifier; - public function __construct( ServerManager $manager, PrettifiesString $prettifier ) + public function __construct( ProvidesRedisData $manager, PrettifiesString $prettifier ) { $this->manager = $manager; $this->prettifier = $prettifier; diff --git a/src/Application/ReadModel/KeyDataBuilders/SortedSetKeyDataBuilder.php b/src/Application/ReadModel/KeyDataBuilders/SortedSetKeyDataBuilder.php index 46d99d6..781520d 100644 --- a/src/Application/ReadModel/KeyDataBuilders/SortedSetKeyDataBuilder.php +++ b/src/Application/ReadModel/KeyDataBuilders/SortedSetKeyDataBuilder.php @@ -3,6 +3,7 @@ namespace hollodotme\Readis\Application\ReadModel\KeyDataBuilders; use hollodotme\Readis\Application\Interfaces\ProvidesKeyInfo; +use hollodotme\Readis\Application\Interfaces\ProvidesRedisData; use hollodotme\Readis\Application\ReadModel\Constants\KeyType; use hollodotme\Readis\Application\ReadModel\DTO\KeyData; use hollodotme\Readis\Application\ReadModel\Interfaces\BuildsKeyData; @@ -10,19 +11,18 @@ use hollodotme\Readis\Application\ReadModel\Interfaces\ProvidesKeyData; use hollodotme\Readis\Application\ReadModel\Interfaces\ProvidesKeyName; use hollodotme\Readis\Infrastructure\Redis\Exceptions\ConnectionFailedException; -use hollodotme\Readis\Infrastructure\Redis\ServerManager; final class SortedSetKeyDataBuilder implements BuildsKeyData { private const MEMBER_SEPARATOR = "\n\n---\n\n"; - /** @var ServerManager */ + /** @var ProvidesRedisData */ private $manager; /** @var PrettifiesString */ private $prettifier; - public function __construct( ServerManager $manager, PrettifiesString $prettifier ) + public function __construct( ProvidesRedisData $manager, PrettifiesString $prettifier ) { $this->manager = $manager; $this->prettifier = $prettifier; diff --git a/src/Application/ReadModel/KeyDataBuilders/SortedSetSubKeyDataBuilder.php b/src/Application/ReadModel/KeyDataBuilders/SortedSetSubKeyDataBuilder.php index 1ca91f6..a0ef530 100644 --- a/src/Application/ReadModel/KeyDataBuilders/SortedSetSubKeyDataBuilder.php +++ b/src/Application/ReadModel/KeyDataBuilders/SortedSetSubKeyDataBuilder.php @@ -3,6 +3,7 @@ namespace hollodotme\Readis\Application\ReadModel\KeyDataBuilders; use hollodotme\Readis\Application\Interfaces\ProvidesKeyInfo; +use hollodotme\Readis\Application\Interfaces\ProvidesRedisData; use hollodotme\Readis\Application\ReadModel\Constants\KeyType; use hollodotme\Readis\Application\ReadModel\DTO\KeyData; use hollodotme\Readis\Application\ReadModel\Interfaces\BuildsKeyData; @@ -10,17 +11,16 @@ use hollodotme\Readis\Application\ReadModel\Interfaces\ProvidesKeyData; use hollodotme\Readis\Application\ReadModel\Interfaces\ProvidesKeyName; use hollodotme\Readis\Exceptions\RuntimeException; -use hollodotme\Readis\Infrastructure\Redis\ServerManager; final class SortedSetSubKeyDataBuilder implements BuildsKeyData { - /** @var ServerManager */ + /** @var ProvidesRedisData */ private $manager; /** @var PrettifiesString */ private $prettifier; - public function __construct( ServerManager $manager, PrettifiesString $prettifier ) + public function __construct( ProvidesRedisData $manager, PrettifiesString $prettifier ) { $this->manager = $manager; $this->prettifier = $prettifier; diff --git a/src/Application/ReadModel/KeyDataBuilders/StringKeyDataBuilder.php b/src/Application/ReadModel/KeyDataBuilders/StringKeyDataBuilder.php index 83fb0f9..6439d92 100644 --- a/src/Application/ReadModel/KeyDataBuilders/StringKeyDataBuilder.php +++ b/src/Application/ReadModel/KeyDataBuilders/StringKeyDataBuilder.php @@ -3,6 +3,7 @@ namespace hollodotme\Readis\Application\ReadModel\KeyDataBuilders; use hollodotme\Readis\Application\Interfaces\ProvidesKeyInfo; +use hollodotme\Readis\Application\Interfaces\ProvidesRedisData; use hollodotme\Readis\Application\ReadModel\Constants\KeyType; use hollodotme\Readis\Application\ReadModel\DTO\KeyData; use hollodotme\Readis\Application\ReadModel\Interfaces\BuildsKeyData; @@ -10,17 +11,16 @@ use hollodotme\Readis\Application\ReadModel\Interfaces\ProvidesKeyData; use hollodotme\Readis\Application\ReadModel\Interfaces\ProvidesKeyName; use hollodotme\Readis\Infrastructure\Redis\Exceptions\ConnectionFailedException; -use hollodotme\Readis\Infrastructure\Redis\ServerManager; final class StringKeyDataBuilder implements BuildsKeyData { - /** @var ServerManager */ + /** @var ProvidesRedisData */ private $manager; /** @var PrettifiesString */ private $prettifier; - public function __construct( ServerManager $manager, PrettifiesString $prettifier ) + public function __construct( ProvidesRedisData $manager, PrettifiesString $prettifier ) { $this->manager = $manager; $this->prettifier = $prettifier; diff --git a/src/Application/ReadModel/QueryHandlers/FetchKeyInformationQueryHandler.php b/src/Application/ReadModel/QueryHandlers/FetchKeyInformationQueryHandler.php index a22f359..21a8c95 100644 --- a/src/Application/ReadModel/QueryHandlers/FetchKeyInformationQueryHandler.php +++ b/src/Application/ReadModel/QueryHandlers/FetchKeyInformationQueryHandler.php @@ -2,6 +2,7 @@ namespace hollodotme\Readis\Application\ReadModel\QueryHandlers; +use hollodotme\Readis\Application\Interfaces\ProvidesRedisData; use hollodotme\Readis\Application\ReadModel\Constants\ResultType; use hollodotme\Readis\Application\ReadModel\DTO\KeyName; use hollodotme\Readis\Application\ReadModel\Interfaces\BuildsKeyData; @@ -22,17 +23,16 @@ use hollodotme\Readis\Application\ReadModel\Results\FetchKeyInformationResult; use hollodotme\Readis\Exceptions\KeyTypeNotImplemented; use hollodotme\Readis\Infrastructure\Redis\Exceptions\ConnectionFailedException; -use hollodotme\Readis\Infrastructure\Redis\ServerManager; final class FetchKeyInformationQueryHandler { - /** @var ServerManager */ + /** @var ProvidesRedisData */ private $manager; /** @var BuildsKeyData */ private $keyDataBuilder; - public function __construct( ServerManager $manager ) + public function __construct( ProvidesRedisData $manager ) { $this->manager = $manager; From 5f7777a530b34f101304a2c3c31bb831d309f8b2 Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Sun, 13 May 2018 13:53:00 +0200 Subject: [PATCH 45/62] Extract interface ProvidesRedisData for class ServerManager --- src/Env.php | 7 ++++--- src/Infrastructure/Redis/ServerManager.php | 3 ++- src/Interfaces/ProvidesInfrastructure.php | 6 +++--- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/Env.php b/src/Env.php index 00b79ea..eb0dbbd 100644 --- a/src/Env.php +++ b/src/Env.php @@ -3,6 +3,7 @@ namespace hollodotme\Readis; use hollodotme\Readis\Application\Configs\AppConfig; +use hollodotme\Readis\Application\Interfaces\ProvidesRedisData; use hollodotme\Readis\Infrastructure\Configs\ServerConfigList; use hollodotme\Readis\Infrastructure\Interfaces\ProvidesServerConfig; use hollodotme\Readis\Infrastructure\Interfaces\ProvidesServerConfigList; @@ -34,7 +35,7 @@ function () ); } - public function getServerManager( ProvidesServerConfig $serverConfig ) : ServerManager + public function getServerManager( ProvidesServerConfig $serverConfig ) : ProvidesRedisData { $name = sprintf( 'serverManager-%s:%d', $serverConfig->getHost(), $serverConfig->getPort() ); @@ -53,9 +54,9 @@ function () use ( $serverConfig ) * @param string $serverKey * * @throws Exceptions\ServerConfigNotFound - * @return ServerManager + * @return ProvidesRedisData */ - public function getServerManagerForServerKey( string $serverKey ) : ServerManager + public function getServerManagerForServerKey( string $serverKey ) : ProvidesRedisData { $serverConfigList = $this->getServerConfigList(); $serverConfig = $serverConfigList->getServerConfig( $serverKey ); diff --git a/src/Infrastructure/Redis/ServerManager.php b/src/Infrastructure/Redis/ServerManager.php index a75e72c..4e39381 100644 --- a/src/Infrastructure/Redis/ServerManager.php +++ b/src/Infrastructure/Redis/ServerManager.php @@ -3,6 +3,7 @@ namespace hollodotme\Readis\Infrastructure\Redis; use hollodotme\Readis\Application\Interfaces\ProvidesKeyInfo; +use hollodotme\Readis\Application\Interfaces\ProvidesRedisData; use hollodotme\Readis\Application\Interfaces\ProvidesSlowLogData; use hollodotme\Readis\Infrastructure\Interfaces\ProvidesConnectionData; use hollodotme\Readis\Infrastructure\Redis\DTO\KeyInfo; @@ -12,7 +13,7 @@ use function array_map; use function array_slice; -final class ServerManager +final class ServerManager implements ProvidesRedisData { /** @var RedisWrapper */ private $redis; diff --git a/src/Interfaces/ProvidesInfrastructure.php b/src/Interfaces/ProvidesInfrastructure.php index 06d954b..1079f30 100644 --- a/src/Interfaces/ProvidesInfrastructure.php +++ b/src/Interfaces/ProvidesInfrastructure.php @@ -3,9 +3,9 @@ namespace hollodotme\Readis\Interfaces; use hollodotme\Readis\Application\Configs\AppConfig; +use hollodotme\Readis\Application\Interfaces\ProvidesRedisData; use hollodotme\Readis\Infrastructure\Interfaces\ProvidesServerConfig; use hollodotme\Readis\Infrastructure\Interfaces\ProvidesServerConfigList; -use hollodotme\Readis\Infrastructure\Redis\ServerManager; interface ProvidesInfrastructure { @@ -13,7 +13,7 @@ public function getAppConfig() : AppConfig; public function getServerConfigList() : ProvidesServerConfigList; - public function getServerManager( ProvidesServerConfig $serverConfig ) : ServerManager; + public function getServerManager( ProvidesServerConfig $serverConfig ) : ProvidesRedisData; - public function getServerManagerForServerKey( string $serverKey ) : ServerManager; + public function getServerManagerForServerKey( string $serverKey ) : ProvidesRedisData; } From aa49903e5436ec13ccf45bd96b2339f144c4533a Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Sun, 13 May 2018 13:53:57 +0200 Subject: [PATCH 46/62] Replace class name with interface for server manager --- .../ReadModel/QueryHandlers/AbstractQueryHandlerTest.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/Integration/Application/ReadModel/QueryHandlers/AbstractQueryHandlerTest.php b/tests/Integration/Application/ReadModel/QueryHandlers/AbstractQueryHandlerTest.php index 161f1bb..b7ebc1c 100644 --- a/tests/Integration/Application/ReadModel/QueryHandlers/AbstractQueryHandlerTest.php +++ b/tests/Integration/Application/ReadModel/QueryHandlers/AbstractQueryHandlerTest.php @@ -2,6 +2,7 @@ namespace hollodotme\Readis\Tests\Integration\Application\ReadModel\QueryHandlers; +use hollodotme\Readis\Application\Interfaces\ProvidesRedisData; use hollodotme\Readis\Exceptions\NoServersConfigured; use hollodotme\Readis\Exceptions\ServerConfigNotFound; use hollodotme\Readis\Infrastructure\Configs\ServerConfigList; @@ -90,9 +91,9 @@ protected function getEnvMock( string $serverKey ) : ProvidesInfrastructure * * @throws ServerConfigNotFound * @throws NoServersConfigured - * @return ServerManager + * @return ProvidesRedisData */ - protected function getServerManagerMock( string $serverKey ) : ServerManager + protected function getServerManagerMock( string $serverKey ) : ProvidesRedisData { $serverConfigList = $this->getServerConfigListMock(); $serverConfig = $serverConfigList->getServerConfig( From 86e097e64d39a5892e8bbbb612bbed438b4ff9cc Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Sun, 13 May 2018 13:55:49 +0200 Subject: [PATCH 47/62] Add string key data builder tests --- .../AbstractKeyDataBuilderTest.php | 51 +++++++++++++++++ .../StringKeyDataBuilderTest.php | 56 +++++++++++++++++++ 2 files changed, 107 insertions(+) create mode 100644 tests/Unit/Application/ReadModel/KeyDataBuilders/AbstractKeyDataBuilderTest.php create mode 100644 tests/Unit/Application/ReadModel/KeyDataBuilders/StringKeyDataBuilderTest.php diff --git a/tests/Unit/Application/ReadModel/KeyDataBuilders/AbstractKeyDataBuilderTest.php b/tests/Unit/Application/ReadModel/KeyDataBuilders/AbstractKeyDataBuilderTest.php new file mode 100644 index 0000000..7360040 --- /dev/null +++ b/tests/Unit/Application/ReadModel/KeyDataBuilders/AbstractKeyDataBuilderTest.php @@ -0,0 +1,51 @@ +manager = $this->getMockBuilder( ProvidesRedisData::class ) + ->getMockForAbstractClass(); + $this->manager->method( 'getValue' )->with( 'string' )->willReturn( '{"json": {"key": "value"}}' ); + + $this->prettifier = new class implements PrettifiesString + { + public function canPrettify( string $data ) : bool + { + return true; + } + + public function prettify( string $data ) : string + { + return 'Pretty: ' . $data; + } + }; + } + + protected function tearDown() : void + { + $this->manager = null; + } + + final protected function getManager() : ProvidesRedisData + { + return $this->manager; + } + + final protected function getPrettifier() : PrettifiesString + { + return $this->prettifier; + } +} \ No newline at end of file diff --git a/tests/Unit/Application/ReadModel/KeyDataBuilders/StringKeyDataBuilderTest.php b/tests/Unit/Application/ReadModel/KeyDataBuilders/StringKeyDataBuilderTest.php new file mode 100644 index 0000000..6603284 --- /dev/null +++ b/tests/Unit/Application/ReadModel/KeyDataBuilders/StringKeyDataBuilderTest.php @@ -0,0 +1,56 @@ +getManager(), $this->getPrettifier() ); + + $keyInfoStub = $this->getMockBuilder( ProvidesKeyInfo::class )->getMockForAbstractClass(); + $keyInfoStub->method( 'getType' )->willReturn( 'string' ); + $keysNameStub = $this->getMockBuilder( ProvidesKeyName::class )->getMockForAbstractClass(); + $keysNameStub->method( 'getKeyName' )->willReturn( 'string' ); + + /** @var ProvidesKeyInfo $keyInfoStub */ + /** @var ProvidesKeyName $keysNameStub */ + $keyData = $keyDataBuilder->buildKeyData( $keyInfoStub, $keysNameStub ); + + $expectedKeyData = 'Pretty: {"json": {"key": "value"}}'; + $expectedRawKeyData = '{"json": {"key": "value"}}'; + + $this->assertSame( $expectedKeyData, $keyData->getKeyData() ); + $this->assertSame( $expectedRawKeyData, $keyData->getRawKeyData() ); + $this->assertFalse( $keyData->hasScore() ); + $this->assertNull( $keyData->getScore() ); + } + + /** + * @throws \PHPUnit\Framework\ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function testCanBuildKeyData() + { + $keyDataBuilder = new StringKeyDataBuilder( $this->getManager(), $this->getPrettifier() ); + + $keyInfoStub = $this->getMockBuilder( ProvidesKeyInfo::class )->getMockForAbstractClass(); + $keyInfoStub->method( 'getType' )->willReturn( 'string' ); + $keysNameStub = $this->getMockBuilder( ProvidesKeyName::class )->getMockForAbstractClass(); + $keysNameStub->method( 'hasSubKey' )->willReturn( false ); + + /** @var ProvidesKeyInfo $keyInfoStub */ + /** @var ProvidesKeyName $keysNameStub */ + $this->assertTrue( $keyDataBuilder->canBuildKeyData( $keyInfoStub, $keysNameStub ) ); + } +} From 42bd6cc65c0b9e0aa51b6ebb6780973debe6f37c Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Sun, 13 May 2018 13:57:30 +0200 Subject: [PATCH 48/62] Add negative string key data builder tests --- .../StringKeyDataBuilderTest.php | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/Unit/Application/ReadModel/KeyDataBuilders/StringKeyDataBuilderTest.php b/tests/Unit/Application/ReadModel/KeyDataBuilders/StringKeyDataBuilderTest.php index 6603284..8b2494d 100644 --- a/tests/Unit/Application/ReadModel/KeyDataBuilders/StringKeyDataBuilderTest.php +++ b/tests/Unit/Application/ReadModel/KeyDataBuilders/StringKeyDataBuilderTest.php @@ -53,4 +53,28 @@ public function testCanBuildKeyData() /** @var ProvidesKeyName $keysNameStub */ $this->assertTrue( $keyDataBuilder->canBuildKeyData( $keyInfoStub, $keysNameStub ) ); } + + /** + * @throws \PHPUnit\Framework\ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function testCanNotBuildKeyData() + { + $keyDataBuilder = new StringKeyDataBuilder( $this->getManager(), $this->getPrettifier() ); + + $keyInfoStub = $this->getMockBuilder( ProvidesKeyInfo::class )->getMockForAbstractClass(); + $keyInfoStub->method( 'getType' )->willReturn( 'string' ); + $keysNameStub = $this->getMockBuilder( ProvidesKeyName::class )->getMockForAbstractClass(); + $keysNameStub->method( 'hasSubKey' )->willReturn( true ); + + /** @var ProvidesKeyInfo $keyInfoStub */ + /** @var ProvidesKeyName $keysNameStub */ + + $this->assertFalse( $keyDataBuilder->canBuildKeyData( $keyInfoStub, $keysNameStub ) ); + + $keyInfoStub->method( 'getType' )->willReturn( 'hash' ); + $keysNameStub->method( 'hasSubKey' )->willReturn( false ); + + $this->assertFalse( $keyDataBuilder->canBuildKeyData( $keyInfoStub, $keysNameStub ) ); + } } From 3ee1d78d14883a0081bc74039ef9b0e0b87a916c Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Sun, 13 May 2018 14:08:23 +0200 Subject: [PATCH 49/62] Add sorted set sub key data builder tests --- .../AbstractKeyDataBuilderTest.php | 8 ++ .../SortedSetSubKeyDataBuilderTest.php | 105 ++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 tests/Unit/Application/ReadModel/KeyDataBuilders/SortedSetSubKeyDataBuilderTest.php diff --git a/tests/Unit/Application/ReadModel/KeyDataBuilders/AbstractKeyDataBuilderTest.php b/tests/Unit/Application/ReadModel/KeyDataBuilders/AbstractKeyDataBuilderTest.php index 7360040..1c8ac79 100644 --- a/tests/Unit/Application/ReadModel/KeyDataBuilders/AbstractKeyDataBuilderTest.php +++ b/tests/Unit/Application/ReadModel/KeyDataBuilders/AbstractKeyDataBuilderTest.php @@ -19,6 +19,14 @@ protected function setUp() : void $this->manager = $this->getMockBuilder( ProvidesRedisData::class ) ->getMockForAbstractClass(); $this->manager->method( 'getValue' )->with( 'string' )->willReturn( '{"json": {"key": "value"}}' ); + $this->manager->method( 'getAllSortedSetMembers' ) + ->with( 'sorted set' ) + ->willReturn( + [ + 'one' => 1.0, + '{"json": {"key": "value"}}' => 2.0, + ] + ); $this->prettifier = new class implements PrettifiesString { diff --git a/tests/Unit/Application/ReadModel/KeyDataBuilders/SortedSetSubKeyDataBuilderTest.php b/tests/Unit/Application/ReadModel/KeyDataBuilders/SortedSetSubKeyDataBuilderTest.php new file mode 100644 index 0000000..e5f763e --- /dev/null +++ b/tests/Unit/Application/ReadModel/KeyDataBuilders/SortedSetSubKeyDataBuilderTest.php @@ -0,0 +1,105 @@ +getManager(), $this->getPrettifier() ); + + $keyInfoStub = $this->getMockBuilder( ProvidesKeyInfo::class )->getMockForAbstractClass(); + $keyInfoStub->method( 'getType' )->willReturn( 'zset' ); + $keysNameStub = $this->getMockBuilder( ProvidesKeyName::class )->getMockForAbstractClass(); + $keysNameStub->method( 'getKeyName' )->willReturn( 'sorted set' ); + $keysNameStub->method( 'getSubKey' )->willReturn( '1' ); + + /** @var ProvidesKeyInfo $keyInfoStub */ + /** @var ProvidesKeyName $keysNameStub */ + + $keyData = $keyDataBuilder->buildKeyData( $keyInfoStub, $keysNameStub ); + + $expectedKeyData = 'Pretty: {"json": {"key": "value"}}'; + $expectedRawKeyData = '{"json": {"key": "value"}}'; + + $this->assertSame( $expectedKeyData, $keyData->getKeyData() ); + $this->assertSame( $expectedRawKeyData, $keyData->getRawKeyData() ); + $this->assertTrue( $keyData->hasScore() ); + $this->assertSame( 2.0, $keyData->getScore() ); + } + + /** + * @throws RuntimeException + */ + public function testBuildKeyDataThrowsExceptionForNotExistingSubKey() : void + { + $keyDataBuilder = new SortedSetSubKeyDataBuilder( $this->getManager(), $this->getPrettifier() ); + + $keyInfoStub = $this->getMockBuilder( ProvidesKeyInfo::class )->getMockForAbstractClass(); + $keyInfoStub->method( 'getType' )->willReturn( 'zset' ); + $keysNameStub = $this->getMockBuilder( ProvidesKeyName::class )->getMockForAbstractClass(); + $keysNameStub->method( 'getKeyName' )->willReturn( 'sorted set' ); + $keysNameStub->method( 'getSubKey' )->willReturn( '2' ); + + $this->expectException( RuntimeException::class ); + $this->expectExceptionMessage( 'Could not find key in sorted set anymore.' ); + + /** @var ProvidesKeyInfo $keyInfoStub */ + /** @var ProvidesKeyName $keysNameStub */ + + $keyDataBuilder->buildKeyData( $keyInfoStub, $keysNameStub ); + } + + /** + * @throws \PHPUnit\Framework\ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function testCanBuildKeyData() + { + $keyDataBuilder = new SortedSetSubKeyDataBuilder( $this->getManager(), $this->getPrettifier() ); + + $keyInfoStub = $this->getMockBuilder( ProvidesKeyInfo::class )->getMockForAbstractClass(); + $keyInfoStub->method( 'getType' )->willReturn( 'zset' ); + $keysNameStub = $this->getMockBuilder( ProvidesKeyName::class )->getMockForAbstractClass(); + $keysNameStub->method( 'hasSubKey' )->willReturn( true ); + + /** @var ProvidesKeyInfo $keyInfoStub */ + /** @var ProvidesKeyName $keysNameStub */ + + $this->assertTrue( $keyDataBuilder->canBuildKeyData( $keyInfoStub, $keysNameStub ) ); + } + + /** + * @throws \PHPUnit\Framework\ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function testCanNotBuildKeyData() + { + $keyDataBuilder = new SortedSetSubKeyDataBuilder( $this->getManager(), $this->getPrettifier() ); + + $keyInfoStub = $this->getMockBuilder( ProvidesKeyInfo::class )->getMockForAbstractClass(); + $keyInfoStub->method( 'getType' )->willReturn( 'zset' ); + $keysNameStub = $this->getMockBuilder( ProvidesKeyName::class )->getMockForAbstractClass(); + $keysNameStub->method( 'hasSubKey' )->willReturn( false ); + + /** @var ProvidesKeyInfo $keyInfoStub */ + /** @var ProvidesKeyName $keysNameStub */ + + $this->assertFalse( $keyDataBuilder->canBuildKeyData( $keyInfoStub, $keysNameStub ) ); + + $keyInfoStub->method( 'getType' )->willReturn( 'string' ); + $keysNameStub->method( 'hasSubKey' )->willReturn( true ); + + $this->assertFalse( $keyDataBuilder->canBuildKeyData( $keyInfoStub, $keysNameStub ) ); + } +} From e4c63d27df2385427d126271f001d0c3e977f3a2 Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Sun, 13 May 2018 14:46:06 +0200 Subject: [PATCH 50/62] Add runtime exception if member not found in set --- src/Infrastructure/Redis/ServerManager.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Infrastructure/Redis/ServerManager.php b/src/Infrastructure/Redis/ServerManager.php index 4e39381..bafa963 100644 --- a/src/Infrastructure/Redis/ServerManager.php +++ b/src/Infrastructure/Redis/ServerManager.php @@ -5,6 +5,7 @@ use hollodotme\Readis\Application\Interfaces\ProvidesKeyInfo; use hollodotme\Readis\Application\Interfaces\ProvidesRedisData; use hollodotme\Readis\Application\Interfaces\ProvidesSlowLogData; +use hollodotme\Readis\Exceptions\RuntimeException; use hollodotme\Readis\Infrastructure\Interfaces\ProvidesConnectionData; use hollodotme\Readis\Infrastructure\Redis\DTO\KeyInfo; use hollodotme\Readis\Infrastructure\Redis\DTO\SlowLogEntry; @@ -241,12 +242,18 @@ public function getAllListElements( string $key ) : array * @param int $index * * @throws ConnectionFailedException + * @throws RuntimeException * @return string */ public function getSetMember( string $key, int $index ) : string { $members = $this->getAllSetMembers( $key ); + if ( !isset( $members[ $index ] ) ) + { + throw new RuntimeException( 'Could not find key in set anymore.' ); + } + return $members[ $index ]; } From 653fefc916340c775d7d1427dfa282d02b23c3a3 Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Sun, 13 May 2018 14:47:09 +0200 Subject: [PATCH 51/62] Fix variable naming, add return types --- .../SortedSetSubKeyDataBuilderTest.php | 46 +++++++++---------- .../StringKeyDataBuilderTest.php | 34 +++++++------- 2 files changed, 40 insertions(+), 40 deletions(-) diff --git a/tests/Unit/Application/ReadModel/KeyDataBuilders/SortedSetSubKeyDataBuilderTest.php b/tests/Unit/Application/ReadModel/KeyDataBuilders/SortedSetSubKeyDataBuilderTest.php index e5f763e..037f4f0 100644 --- a/tests/Unit/Application/ReadModel/KeyDataBuilders/SortedSetSubKeyDataBuilderTest.php +++ b/tests/Unit/Application/ReadModel/KeyDataBuilders/SortedSetSubKeyDataBuilderTest.php @@ -14,20 +14,20 @@ final class SortedSetSubKeyDataBuilderTest extends AbstractKeyDataBuilderTest * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException * @throws \hollodotme\Readis\Exceptions\RuntimeException */ - public function testBuildKeyData() + public function testBuildKeyData() : void { $keyDataBuilder = new SortedSetSubKeyDataBuilder( $this->getManager(), $this->getPrettifier() ); $keyInfoStub = $this->getMockBuilder( ProvidesKeyInfo::class )->getMockForAbstractClass(); $keyInfoStub->method( 'getType' )->willReturn( 'zset' ); - $keysNameStub = $this->getMockBuilder( ProvidesKeyName::class )->getMockForAbstractClass(); - $keysNameStub->method( 'getKeyName' )->willReturn( 'sorted set' ); - $keysNameStub->method( 'getSubKey' )->willReturn( '1' ); + $keyNameStub = $this->getMockBuilder( ProvidesKeyName::class )->getMockForAbstractClass(); + $keyNameStub->method( 'getKeyName' )->willReturn( 'sorted set' ); + $keyNameStub->method( 'getSubKey' )->willReturn( '1' ); /** @var ProvidesKeyInfo $keyInfoStub */ - /** @var ProvidesKeyName $keysNameStub */ + /** @var ProvidesKeyName $keyNameStub */ - $keyData = $keyDataBuilder->buildKeyData( $keyInfoStub, $keysNameStub ); + $keyData = $keyDataBuilder->buildKeyData( $keyInfoStub, $keyNameStub ); $expectedKeyData = 'Pretty: {"json": {"key": "value"}}'; $expectedRawKeyData = '{"json": {"key": "value"}}'; @@ -47,59 +47,59 @@ public function testBuildKeyDataThrowsExceptionForNotExistingSubKey() : void $keyInfoStub = $this->getMockBuilder( ProvidesKeyInfo::class )->getMockForAbstractClass(); $keyInfoStub->method( 'getType' )->willReturn( 'zset' ); - $keysNameStub = $this->getMockBuilder( ProvidesKeyName::class )->getMockForAbstractClass(); - $keysNameStub->method( 'getKeyName' )->willReturn( 'sorted set' ); - $keysNameStub->method( 'getSubKey' )->willReturn( '2' ); + $keyNameStub = $this->getMockBuilder( ProvidesKeyName::class )->getMockForAbstractClass(); + $keyNameStub->method( 'getKeyName' )->willReturn( 'sorted set' ); + $keyNameStub->method( 'getSubKey' )->willReturn( '2' ); $this->expectException( RuntimeException::class ); $this->expectExceptionMessage( 'Could not find key in sorted set anymore.' ); /** @var ProvidesKeyInfo $keyInfoStub */ - /** @var ProvidesKeyName $keysNameStub */ + /** @var ProvidesKeyName $keyNameStub */ - $keyDataBuilder->buildKeyData( $keyInfoStub, $keysNameStub ); + $keyDataBuilder->buildKeyData( $keyInfoStub, $keyNameStub ); } /** * @throws \PHPUnit\Framework\ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException */ - public function testCanBuildKeyData() + public function testCanBuildKeyData() : void { $keyDataBuilder = new SortedSetSubKeyDataBuilder( $this->getManager(), $this->getPrettifier() ); $keyInfoStub = $this->getMockBuilder( ProvidesKeyInfo::class )->getMockForAbstractClass(); $keyInfoStub->method( 'getType' )->willReturn( 'zset' ); - $keysNameStub = $this->getMockBuilder( ProvidesKeyName::class )->getMockForAbstractClass(); - $keysNameStub->method( 'hasSubKey' )->willReturn( true ); + $keyNameStub = $this->getMockBuilder( ProvidesKeyName::class )->getMockForAbstractClass(); + $keyNameStub->method( 'hasSubKey' )->willReturn( true ); /** @var ProvidesKeyInfo $keyInfoStub */ - /** @var ProvidesKeyName $keysNameStub */ + /** @var ProvidesKeyName $keyNameStub */ - $this->assertTrue( $keyDataBuilder->canBuildKeyData( $keyInfoStub, $keysNameStub ) ); + $this->assertTrue( $keyDataBuilder->canBuildKeyData( $keyInfoStub, $keyNameStub ) ); } /** * @throws \PHPUnit\Framework\ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException */ - public function testCanNotBuildKeyData() + public function testCanNotBuildKeyData() : void { $keyDataBuilder = new SortedSetSubKeyDataBuilder( $this->getManager(), $this->getPrettifier() ); $keyInfoStub = $this->getMockBuilder( ProvidesKeyInfo::class )->getMockForAbstractClass(); $keyInfoStub->method( 'getType' )->willReturn( 'zset' ); - $keysNameStub = $this->getMockBuilder( ProvidesKeyName::class )->getMockForAbstractClass(); - $keysNameStub->method( 'hasSubKey' )->willReturn( false ); + $keyNameStub = $this->getMockBuilder( ProvidesKeyName::class )->getMockForAbstractClass(); + $keyNameStub->method( 'hasSubKey' )->willReturn( false ); /** @var ProvidesKeyInfo $keyInfoStub */ - /** @var ProvidesKeyName $keysNameStub */ + /** @var ProvidesKeyName $keyNameStub */ - $this->assertFalse( $keyDataBuilder->canBuildKeyData( $keyInfoStub, $keysNameStub ) ); + $this->assertFalse( $keyDataBuilder->canBuildKeyData( $keyInfoStub, $keyNameStub ) ); $keyInfoStub->method( 'getType' )->willReturn( 'string' ); - $keysNameStub->method( 'hasSubKey' )->willReturn( true ); + $keyNameStub->method( 'hasSubKey' )->willReturn( true ); - $this->assertFalse( $keyDataBuilder->canBuildKeyData( $keyInfoStub, $keysNameStub ) ); + $this->assertFalse( $keyDataBuilder->canBuildKeyData( $keyInfoStub, $keyNameStub ) ); } } diff --git a/tests/Unit/Application/ReadModel/KeyDataBuilders/StringKeyDataBuilderTest.php b/tests/Unit/Application/ReadModel/KeyDataBuilders/StringKeyDataBuilderTest.php index 8b2494d..51e0a93 100644 --- a/tests/Unit/Application/ReadModel/KeyDataBuilders/StringKeyDataBuilderTest.php +++ b/tests/Unit/Application/ReadModel/KeyDataBuilders/StringKeyDataBuilderTest.php @@ -14,18 +14,18 @@ final class StringKeyDataBuilderTest extends AbstractKeyDataBuilderTest * @throws \PHPUnit\Framework\ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException */ - public function testBuildKeyData() + public function testBuildKeyData() : void { $keyDataBuilder = new StringKeyDataBuilder( $this->getManager(), $this->getPrettifier() ); $keyInfoStub = $this->getMockBuilder( ProvidesKeyInfo::class )->getMockForAbstractClass(); $keyInfoStub->method( 'getType' )->willReturn( 'string' ); - $keysNameStub = $this->getMockBuilder( ProvidesKeyName::class )->getMockForAbstractClass(); - $keysNameStub->method( 'getKeyName' )->willReturn( 'string' ); + $keyNameStub = $this->getMockBuilder( ProvidesKeyName::class )->getMockForAbstractClass(); + $keyNameStub->method( 'getKeyName' )->willReturn( 'string' ); /** @var ProvidesKeyInfo $keyInfoStub */ - /** @var ProvidesKeyName $keysNameStub */ - $keyData = $keyDataBuilder->buildKeyData( $keyInfoStub, $keysNameStub ); + /** @var ProvidesKeyName $keyNameStub */ + $keyData = $keyDataBuilder->buildKeyData( $keyInfoStub, $keyNameStub ); $expectedKeyData = 'Pretty: {"json": {"key": "value"}}'; $expectedRawKeyData = '{"json": {"key": "value"}}'; @@ -40,41 +40,41 @@ public function testBuildKeyData() * @throws \PHPUnit\Framework\ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException */ - public function testCanBuildKeyData() + public function testCanBuildKeyData() : void { $keyDataBuilder = new StringKeyDataBuilder( $this->getManager(), $this->getPrettifier() ); $keyInfoStub = $this->getMockBuilder( ProvidesKeyInfo::class )->getMockForAbstractClass(); $keyInfoStub->method( 'getType' )->willReturn( 'string' ); - $keysNameStub = $this->getMockBuilder( ProvidesKeyName::class )->getMockForAbstractClass(); - $keysNameStub->method( 'hasSubKey' )->willReturn( false ); + $keyNameStub = $this->getMockBuilder( ProvidesKeyName::class )->getMockForAbstractClass(); + $keyNameStub->method( 'hasSubKey' )->willReturn( false ); /** @var ProvidesKeyInfo $keyInfoStub */ - /** @var ProvidesKeyName $keysNameStub */ - $this->assertTrue( $keyDataBuilder->canBuildKeyData( $keyInfoStub, $keysNameStub ) ); + /** @var ProvidesKeyName $keyNameStub */ + $this->assertTrue( $keyDataBuilder->canBuildKeyData( $keyInfoStub, $keyNameStub ) ); } /** * @throws \PHPUnit\Framework\ExpectationFailedException * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException */ - public function testCanNotBuildKeyData() + public function testCanNotBuildKeyData() : void { $keyDataBuilder = new StringKeyDataBuilder( $this->getManager(), $this->getPrettifier() ); $keyInfoStub = $this->getMockBuilder( ProvidesKeyInfo::class )->getMockForAbstractClass(); $keyInfoStub->method( 'getType' )->willReturn( 'string' ); - $keysNameStub = $this->getMockBuilder( ProvidesKeyName::class )->getMockForAbstractClass(); - $keysNameStub->method( 'hasSubKey' )->willReturn( true ); + $keyNameStub = $this->getMockBuilder( ProvidesKeyName::class )->getMockForAbstractClass(); + $keyNameStub->method( 'hasSubKey' )->willReturn( true ); /** @var ProvidesKeyInfo $keyInfoStub */ - /** @var ProvidesKeyName $keysNameStub */ + /** @var ProvidesKeyName $keyNameStub */ - $this->assertFalse( $keyDataBuilder->canBuildKeyData( $keyInfoStub, $keysNameStub ) ); + $this->assertFalse( $keyDataBuilder->canBuildKeyData( $keyInfoStub, $keyNameStub ) ); $keyInfoStub->method( 'getType' )->willReturn( 'hash' ); - $keysNameStub->method( 'hasSubKey' )->willReturn( false ); + $keyNameStub->method( 'hasSubKey' )->willReturn( false ); - $this->assertFalse( $keyDataBuilder->canBuildKeyData( $keyInfoStub, $keysNameStub ) ); + $this->assertFalse( $keyDataBuilder->canBuildKeyData( $keyInfoStub, $keyNameStub ) ); } } From 4e41fb2d21a38bdd185bb9866f439cdbbca1e2ac Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Sun, 13 May 2018 14:48:46 +0200 Subject: [PATCH 52/62] Add set sub key data builder tests --- .../AbstractKeyDataBuilderTest.php | 16 +++ .../SetSubKeyDataBuilderTest.php | 105 ++++++++++++++++++ 2 files changed, 121 insertions(+) create mode 100644 tests/Unit/Application/ReadModel/KeyDataBuilders/SetSubKeyDataBuilderTest.php diff --git a/tests/Unit/Application/ReadModel/KeyDataBuilders/AbstractKeyDataBuilderTest.php b/tests/Unit/Application/ReadModel/KeyDataBuilders/AbstractKeyDataBuilderTest.php index 1c8ac79..904c0c8 100644 --- a/tests/Unit/Application/ReadModel/KeyDataBuilders/AbstractKeyDataBuilderTest.php +++ b/tests/Unit/Application/ReadModel/KeyDataBuilders/AbstractKeyDataBuilderTest.php @@ -4,6 +4,7 @@ use hollodotme\Readis\Application\Interfaces\ProvidesRedisData; use hollodotme\Readis\Application\ReadModel\Interfaces\PrettifiesString; +use hollodotme\Readis\Exceptions\RuntimeException; use PHPUnit\Framework\TestCase; abstract class AbstractKeyDataBuilderTest extends TestCase @@ -28,6 +29,21 @@ protected function setUp() : void ] ); + $this->manager->method( 'getSetMember' ) + ->will( + $this->returnCallback( + function ( string $key, int $index ) + { + if ( 0 === $index ) + { + return '{"json": {"key": "value"}}'; + } + + throw new RuntimeException( 'Could not find key in set anymore.' ); + } + ) + ); + $this->prettifier = new class implements PrettifiesString { public function canPrettify( string $data ) : bool diff --git a/tests/Unit/Application/ReadModel/KeyDataBuilders/SetSubKeyDataBuilderTest.php b/tests/Unit/Application/ReadModel/KeyDataBuilders/SetSubKeyDataBuilderTest.php new file mode 100644 index 0000000..a8c0d86 --- /dev/null +++ b/tests/Unit/Application/ReadModel/KeyDataBuilders/SetSubKeyDataBuilderTest.php @@ -0,0 +1,105 @@ +getManager(), $this->getPrettifier() ); + + $keyInfoStub = $this->getMockBuilder( ProvidesKeyInfo::class )->getMockForAbstractClass(); + $keyInfoStub->method( 'getType' )->willReturn( 'set' ); + $keyNameStub = $this->getMockBuilder( ProvidesKeyName::class )->getMockForAbstractClass(); + $keyNameStub->method( 'getKeyName' )->willReturn( 'set' ); + $keyNameStub->method( 'getSubKey' )->willReturn( '0' ); + + /** @var ProvidesKeyInfo $keyInfoStub */ + /** @var ProvidesKeyName $keyNameStub */ + + $keyData = $keyDataBuilder->buildKeyData( $keyInfoStub, $keyNameStub ); + + $expectedKeyData = 'Pretty: {"json": {"key": "value"}}'; + $expectedRawKeyData = '{"json": {"key": "value"}}'; + + $this->assertSame( $expectedKeyData, $keyData->getKeyData() ); + $this->assertSame( $expectedRawKeyData, $keyData->getRawKeyData() ); + $this->assertFalse( $keyData->hasScore() ); + $this->assertNull( $keyData->getScore() ); + } + + /** + * @throws RuntimeException + */ + public function testBuildKeyDataThrowsExceptionForNotExistingSubKey() : void + { + $keyDataBuilder = new SetSubKeyDataBuilder( $this->getManager(), $this->getPrettifier() ); + + $keyInfoStub = $this->getMockBuilder( ProvidesKeyInfo::class )->getMockForAbstractClass(); + $keyInfoStub->method( 'getType' )->willReturn( 'set' ); + $keyNameStub = $this->getMockBuilder( ProvidesKeyName::class )->getMockForAbstractClass(); + $keyNameStub->method( 'getKeyName' )->willReturn( 'set' ); + $keyNameStub->method( 'getSubKey' )->willReturn( '1' ); + + $this->expectException( RuntimeException::class ); + $this->expectExceptionMessage( 'Could not find key in set anymore.' ); + + /** @var ProvidesKeyInfo $keyInfoStub */ + /** @var ProvidesKeyName $keyNameStub */ + + $keyDataBuilder->buildKeyData( $keyInfoStub, $keyNameStub ); + } + + /** + * @throws \PHPUnit\Framework\ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function testCanBuildKeyData() : void + { + $keyDataBuilder = new SetSubKeyDataBuilder( $this->getManager(), $this->getPrettifier() ); + + $keyInfoStub = $this->getMockBuilder( ProvidesKeyInfo::class )->getMockForAbstractClass(); + $keyInfoStub->method( 'getType' )->willReturn( 'set' ); + $keyNameStub = $this->getMockBuilder( ProvidesKeyName::class )->getMockForAbstractClass(); + $keyNameStub->method( 'hasSubKey' )->willReturn( true ); + + /** @var ProvidesKeyInfo $keyInfoStub */ + /** @var ProvidesKeyName $keyNameStub */ + + $this->assertTrue( $keyDataBuilder->canBuildKeyData( $keyInfoStub, $keyNameStub ) ); + } + + /** + * @throws \PHPUnit\Framework\ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function testCanNotBuildKeyData() : void + { + $keyDataBuilder = new SetSubKeyDataBuilder( $this->getManager(), $this->getPrettifier() ); + + $keyInfoStub = $this->getMockBuilder( ProvidesKeyInfo::class )->getMockForAbstractClass(); + $keyInfoStub->method( 'getType' )->willReturn( 'set' ); + $keyNameStub = $this->getMockBuilder( ProvidesKeyName::class )->getMockForAbstractClass(); + $keyNameStub->method( 'hasSubKey' )->willReturn( false ); + + /** @var ProvidesKeyInfo $keyInfoStub */ + /** @var ProvidesKeyName $keyNameStub */ + + $this->assertFalse( $keyDataBuilder->canBuildKeyData( $keyInfoStub, $keyNameStub ) ); + + $keyInfoStub->method( 'getType' )->willReturn( 'string' ); + $keyNameStub->method( 'hasSubKey' )->willReturn( true ); + + $this->assertFalse( $keyDataBuilder->canBuildKeyData( $keyInfoStub, $keyNameStub ) ); + } +} From 12b650b2fab1cc9a026787bfc8ea97c4607b6a2e Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Sun, 13 May 2018 14:57:09 +0200 Subject: [PATCH 53/62] Fix exception message --- src/Infrastructure/Redis/ServerManager.php | 2 +- .../ReadModel/KeyDataBuilders/SetSubKeyDataBuilderTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Infrastructure/Redis/ServerManager.php b/src/Infrastructure/Redis/ServerManager.php index bafa963..df801b2 100644 --- a/src/Infrastructure/Redis/ServerManager.php +++ b/src/Infrastructure/Redis/ServerManager.php @@ -251,7 +251,7 @@ public function getSetMember( string $key, int $index ) : string if ( !isset( $members[ $index ] ) ) { - throw new RuntimeException( 'Could not find key in set anymore.' ); + throw new RuntimeException( 'Could not find member in set anymore.' ); } return $members[ $index ]; diff --git a/tests/Unit/Application/ReadModel/KeyDataBuilders/SetSubKeyDataBuilderTest.php b/tests/Unit/Application/ReadModel/KeyDataBuilders/SetSubKeyDataBuilderTest.php index a8c0d86..19e2230 100644 --- a/tests/Unit/Application/ReadModel/KeyDataBuilders/SetSubKeyDataBuilderTest.php +++ b/tests/Unit/Application/ReadModel/KeyDataBuilders/SetSubKeyDataBuilderTest.php @@ -52,7 +52,7 @@ public function testBuildKeyDataThrowsExceptionForNotExistingSubKey() : void $keyNameStub->method( 'getSubKey' )->willReturn( '1' ); $this->expectException( RuntimeException::class ); - $this->expectExceptionMessage( 'Could not find key in set anymore.' ); + $this->expectExceptionMessage( 'Could not find member in set anymore.' ); /** @var ProvidesKeyInfo $keyInfoStub */ /** @var ProvidesKeyName $keyNameStub */ From 1c6dabd07319ff1ea22c89787fa47fb748dd2fa0 Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Sun, 13 May 2018 14:57:35 +0200 Subject: [PATCH 54/62] Add list sub key data builder tests --- .../AbstractKeyDataBuilderTest.php | 19 +++- .../ListSubKeyDataBuilderTest.php | 105 ++++++++++++++++++ 2 files changed, 122 insertions(+), 2 deletions(-) create mode 100644 tests/Unit/Application/ReadModel/KeyDataBuilders/ListSubKeyDataBuilderTest.php diff --git a/tests/Unit/Application/ReadModel/KeyDataBuilders/AbstractKeyDataBuilderTest.php b/tests/Unit/Application/ReadModel/KeyDataBuilders/AbstractKeyDataBuilderTest.php index 904c0c8..146ca23 100644 --- a/tests/Unit/Application/ReadModel/KeyDataBuilders/AbstractKeyDataBuilderTest.php +++ b/tests/Unit/Application/ReadModel/KeyDataBuilders/AbstractKeyDataBuilderTest.php @@ -34,12 +34,27 @@ protected function setUp() : void $this->returnCallback( function ( string $key, int $index ) { - if ( 0 === $index ) + if ( 'set' === $key && 0 === $index ) { return '{"json": {"key": "value"}}'; } - throw new RuntimeException( 'Could not find key in set anymore.' ); + throw new RuntimeException( 'Could not find member in set anymore.' ); + } + ) + ); + + $this->manager->method( 'getListElement' ) + ->will( + $this->returnCallback( + function ( string $key, int $index ) + { + if ( 'list' === $key && 0 === $index ) + { + return '{"json": {"key": "value"}}'; + } + + throw new RuntimeException( 'Could not find element in list anymore.' ); } ) ); diff --git a/tests/Unit/Application/ReadModel/KeyDataBuilders/ListSubKeyDataBuilderTest.php b/tests/Unit/Application/ReadModel/KeyDataBuilders/ListSubKeyDataBuilderTest.php new file mode 100644 index 0000000..07b8479 --- /dev/null +++ b/tests/Unit/Application/ReadModel/KeyDataBuilders/ListSubKeyDataBuilderTest.php @@ -0,0 +1,105 @@ +getManager(), $this->getPrettifier() ); + + $keyInfoStub = $this->getMockBuilder( ProvidesKeyInfo::class )->getMockForAbstractClass(); + $keyInfoStub->method( 'getType' )->willReturn( 'list' ); + $keyNameStub = $this->getMockBuilder( ProvidesKeyName::class )->getMockForAbstractClass(); + $keyNameStub->method( 'getKeyName' )->willReturn( 'list' ); + $keyNameStub->method( 'getSubKey' )->willReturn( '0' ); + + /** @var ProvidesKeyInfo $keyInfoStub */ + /** @var ProvidesKeyName $keyNameStub */ + + $keyData = $keyDataBuilder->buildKeyData( $keyInfoStub, $keyNameStub ); + + $expectedKeyData = 'Pretty: {"json": {"key": "value"}}'; + $expectedRawKeyData = '{"json": {"key": "value"}}'; + + $this->assertSame( $expectedKeyData, $keyData->getKeyData() ); + $this->assertSame( $expectedRawKeyData, $keyData->getRawKeyData() ); + $this->assertFalse( $keyData->hasScore() ); + $this->assertNull( $keyData->getScore() ); + } + + /** + * @throws RuntimeException + */ + public function testBuildKeyDataThrowsExceptionForNotExistingSubKey() : void + { + $keyDataBuilder = new ListSubKeyDataBuilder( $this->getManager(), $this->getPrettifier() ); + + $keyInfoStub = $this->getMockBuilder( ProvidesKeyInfo::class )->getMockForAbstractClass(); + $keyInfoStub->method( 'getType' )->willReturn( 'list' ); + $keyNameStub = $this->getMockBuilder( ProvidesKeyName::class )->getMockForAbstractClass(); + $keyNameStub->method( 'getKeyName' )->willReturn( 'list' ); + $keyNameStub->method( 'getSubKey' )->willReturn( '1' ); + + $this->expectException( RuntimeException::class ); + $this->expectExceptionMessage( 'Could not find element in list anymore.' ); + + /** @var ProvidesKeyInfo $keyInfoStub */ + /** @var ProvidesKeyName $keyNameStub */ + + $keyDataBuilder->buildKeyData( $keyInfoStub, $keyNameStub ); + } + + /** + * @throws \PHPUnit\Framework\ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function testCanBuildKeyData() : void + { + $keyDataBuilder = new ListSubKeyDataBuilder( $this->getManager(), $this->getPrettifier() ); + + $keyInfoStub = $this->getMockBuilder( ProvidesKeyInfo::class )->getMockForAbstractClass(); + $keyInfoStub->method( 'getType' )->willReturn( 'list' ); + $keyNameStub = $this->getMockBuilder( ProvidesKeyName::class )->getMockForAbstractClass(); + $keyNameStub->method( 'hasSubKey' )->willReturn( true ); + + /** @var ProvidesKeyInfo $keyInfoStub */ + /** @var ProvidesKeyName $keyNameStub */ + + $this->assertTrue( $keyDataBuilder->canBuildKeyData( $keyInfoStub, $keyNameStub ) ); + } + + /** + * @throws \PHPUnit\Framework\ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function testCanNotBuildKeyData() : void + { + $keyDataBuilder = new ListSubKeyDataBuilder( $this->getManager(), $this->getPrettifier() ); + + $keyInfoStub = $this->getMockBuilder( ProvidesKeyInfo::class )->getMockForAbstractClass(); + $keyInfoStub->method( 'getType' )->willReturn( 'list' ); + $keyNameStub = $this->getMockBuilder( ProvidesKeyName::class )->getMockForAbstractClass(); + $keyNameStub->method( 'hasSubKey' )->willReturn( false ); + + /** @var ProvidesKeyInfo $keyInfoStub */ + /** @var ProvidesKeyName $keyNameStub */ + + $this->assertFalse( $keyDataBuilder->canBuildKeyData( $keyInfoStub, $keyNameStub ) ); + + $keyInfoStub->method( 'getType' )->willReturn( 'string' ); + $keyNameStub->method( 'hasSubKey' )->willReturn( true ); + + $this->assertFalse( $keyDataBuilder->canBuildKeyData( $keyInfoStub, $keyNameStub ) ); + } +} From 411a872bc157e41224b9478e43a439cef5c11267 Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Sun, 13 May 2018 15:08:16 +0200 Subject: [PATCH 55/62] Add hash sub key data builder tests --- .../AbstractKeyDataBuilderTest.php | 15 +++ .../HashSubKeyDataBuilderTest.php | 105 ++++++++++++++++++ 2 files changed, 120 insertions(+) create mode 100644 tests/Unit/Application/ReadModel/KeyDataBuilders/HashSubKeyDataBuilderTest.php diff --git a/tests/Unit/Application/ReadModel/KeyDataBuilders/AbstractKeyDataBuilderTest.php b/tests/Unit/Application/ReadModel/KeyDataBuilders/AbstractKeyDataBuilderTest.php index 146ca23..382e066 100644 --- a/tests/Unit/Application/ReadModel/KeyDataBuilders/AbstractKeyDataBuilderTest.php +++ b/tests/Unit/Application/ReadModel/KeyDataBuilders/AbstractKeyDataBuilderTest.php @@ -59,6 +59,21 @@ function ( string $key, int $index ) ) ); + $this->manager->method( 'getHashValue' ) + ->will( + $this->returnCallback( + function ( string $key, string $hashKey ) + { + if ( 'hash' === $key && 'json' === $hashKey ) + { + return '{"json": {"key": "value"}}'; + } + + throw new RuntimeException( 'Could not find field in hash anymore.' ); + } + ) + ); + $this->prettifier = new class implements PrettifiesString { public function canPrettify( string $data ) : bool diff --git a/tests/Unit/Application/ReadModel/KeyDataBuilders/HashSubKeyDataBuilderTest.php b/tests/Unit/Application/ReadModel/KeyDataBuilders/HashSubKeyDataBuilderTest.php new file mode 100644 index 0000000..7dc9634 --- /dev/null +++ b/tests/Unit/Application/ReadModel/KeyDataBuilders/HashSubKeyDataBuilderTest.php @@ -0,0 +1,105 @@ +getManager(), $this->getPrettifier() ); + + $keyInfoStub = $this->getMockBuilder( ProvidesKeyInfo::class )->getMockForAbstractClass(); + $keyInfoStub->method( 'getType' )->willReturn( 'hash' ); + $keyNameStub = $this->getMockBuilder( ProvidesKeyName::class )->getMockForAbstractClass(); + $keyNameStub->method( 'getKeyName' )->willReturn( 'hash' ); + $keyNameStub->method( 'getSubKey' )->willReturn( 'json' ); + + /** @var ProvidesKeyInfo $keyInfoStub */ + /** @var ProvidesKeyName $keyNameStub */ + + $keyData = $keyDataBuilder->buildKeyData( $keyInfoStub, $keyNameStub ); + + $expectedKeyData = 'Pretty: {"json": {"key": "value"}}'; + $expectedRawKeyData = '{"json": {"key": "value"}}'; + + $this->assertSame( $expectedKeyData, $keyData->getKeyData() ); + $this->assertSame( $expectedRawKeyData, $keyData->getRawKeyData() ); + $this->assertFalse( $keyData->hasScore() ); + $this->assertNull( $keyData->getScore() ); + } + + /** + * @throws RuntimeException + */ + public function testBuildKeyDataThrowsExceptionForNotExistingSubKey() : void + { + $keyDataBuilder = new HashSubKeyDataBuilder( $this->getManager(), $this->getPrettifier() ); + + $keyInfoStub = $this->getMockBuilder( ProvidesKeyInfo::class )->getMockForAbstractClass(); + $keyInfoStub->method( 'getType' )->willReturn( 'hash' ); + $keyNameStub = $this->getMockBuilder( ProvidesKeyName::class )->getMockForAbstractClass(); + $keyNameStub->method( 'getKeyName' )->willReturn( 'hash' ); + $keyNameStub->method( 'getSubKey' )->willReturn( 'not-existing' ); + + $this->expectException( RuntimeException::class ); + $this->expectExceptionMessage( 'Could not find field in hash anymore.' ); + + /** @var ProvidesKeyInfo $keyInfoStub */ + /** @var ProvidesKeyName $keyNameStub */ + + $keyDataBuilder->buildKeyData( $keyInfoStub, $keyNameStub ); + } + + /** + * @throws \PHPUnit\Framework\ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function testCanBuildKeyData() : void + { + $keyDataBuilder = new HashSubKeyDataBuilder( $this->getManager(), $this->getPrettifier() ); + + $keyInfoStub = $this->getMockBuilder( ProvidesKeyInfo::class )->getMockForAbstractClass(); + $keyInfoStub->method( 'getType' )->willReturn( 'hash' ); + $keyNameStub = $this->getMockBuilder( ProvidesKeyName::class )->getMockForAbstractClass(); + $keyNameStub->method( 'hasSubKey' )->willReturn( true ); + + /** @var ProvidesKeyInfo $keyInfoStub */ + /** @var ProvidesKeyName $keyNameStub */ + + $this->assertTrue( $keyDataBuilder->canBuildKeyData( $keyInfoStub, $keyNameStub ) ); + } + + /** + * @throws \PHPUnit\Framework\ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function testCanNotBuildKeyData() : void + { + $keyDataBuilder = new HashSubKeyDataBuilder( $this->getManager(), $this->getPrettifier() ); + + $keyInfoStub = $this->getMockBuilder( ProvidesKeyInfo::class )->getMockForAbstractClass(); + $keyInfoStub->method( 'getType' )->willReturn( 'hash' ); + $keyNameStub = $this->getMockBuilder( ProvidesKeyName::class )->getMockForAbstractClass(); + $keyNameStub->method( 'hasSubKey' )->willReturn( false ); + + /** @var ProvidesKeyInfo $keyInfoStub */ + /** @var ProvidesKeyName $keyNameStub */ + + $this->assertFalse( $keyDataBuilder->canBuildKeyData( $keyInfoStub, $keyNameStub ) ); + + $keyInfoStub->method( 'getType' )->willReturn( 'string' ); + $keyNameStub->method( 'hasSubKey' )->willReturn( true ); + + $this->assertFalse( $keyDataBuilder->canBuildKeyData( $keyInfoStub, $keyNameStub ) ); + } +} From daf6bf643f3735e370bee433848ddacc861c9868 Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Sun, 13 May 2018 18:24:09 +0200 Subject: [PATCH 56/62] Add hash key data builder tests --- .../AbstractKeyDataBuilderTest.php | 7 ++ .../HashKeyDataBuilderTest.php | 87 +++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 tests/Unit/Application/ReadModel/KeyDataBuilders/HashKeyDataBuilderTest.php diff --git a/tests/Unit/Application/ReadModel/KeyDataBuilders/AbstractKeyDataBuilderTest.php b/tests/Unit/Application/ReadModel/KeyDataBuilders/AbstractKeyDataBuilderTest.php index 382e066..195d7f0 100644 --- a/tests/Unit/Application/ReadModel/KeyDataBuilders/AbstractKeyDataBuilderTest.php +++ b/tests/Unit/Application/ReadModel/KeyDataBuilders/AbstractKeyDataBuilderTest.php @@ -74,6 +74,13 @@ function ( string $key, string $hashKey ) ) ); + $this->manager->method( 'getAllHashValues' )->willReturn( + [ + 'field' => 'value', + 'json' => '{"json": {"key": "value"}}', + ] + ); + $this->prettifier = new class implements PrettifiesString { public function canPrettify( string $data ) : bool diff --git a/tests/Unit/Application/ReadModel/KeyDataBuilders/HashKeyDataBuilderTest.php b/tests/Unit/Application/ReadModel/KeyDataBuilders/HashKeyDataBuilderTest.php new file mode 100644 index 0000000..59b31fa --- /dev/null +++ b/tests/Unit/Application/ReadModel/KeyDataBuilders/HashKeyDataBuilderTest.php @@ -0,0 +1,87 @@ +getManager(), $this->getPrettifier() ); + + $keyInfoStub = $this->getMockBuilder( ProvidesKeyInfo::class )->getMockForAbstractClass(); + $keyInfoStub->method( 'getType' )->willReturn( 'hash' ); + $keyNameStub = $this->getMockBuilder( ProvidesKeyName::class )->getMockForAbstractClass(); + $keyNameStub->method( 'getKeyName' )->willReturn( 'hash' ); + $keyNameStub->method( 'hasSubKey' )->willReturn( false ); + $keyNameStub->method( 'getSubKey' )->willReturn( null ); + + /** @var ProvidesKeyInfo $keyInfoStub */ + /** @var ProvidesKeyName $keyNameStub */ + + $keyData = $keyDataBuilder->buildKeyData( $keyInfoStub, $keyNameStub ); + + $expectedKeyData = "Field field:\n============\nPretty: value" + . "\n\n---\n\n" + . "Field json:\n===========\nPretty: {\"json\": {\"key\": \"value\"}}"; + $expectedRawKeyData = "Field field:\n============\nvalue" + . "\n\n---\n\n" + . "Field json:\n===========\n{\"json\": {\"key\": \"value\"}}"; + + $this->assertSame( $expectedKeyData, $keyData->getKeyData() ); + $this->assertSame( $expectedRawKeyData, $keyData->getRawKeyData() ); + $this->assertFalse( $keyData->hasScore() ); + $this->assertNull( $keyData->getScore() ); + } + + /** + * @throws \PHPUnit\Framework\ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function testCanBuildKeyData() : void + { + $keyDataBuilder = new HashKeyDataBuilder( $this->getManager(), $this->getPrettifier() ); + + $keyInfoStub = $this->getMockBuilder( ProvidesKeyInfo::class )->getMockForAbstractClass(); + $keyInfoStub->method( 'getType' )->willReturn( 'hash' ); + $keyNameStub = $this->getMockBuilder( ProvidesKeyName::class )->getMockForAbstractClass(); + $keyNameStub->method( 'hasSubKey' )->willReturn( false ); + + /** @var ProvidesKeyInfo $keyInfoStub */ + /** @var ProvidesKeyName $keyNameStub */ + + $this->assertTrue( $keyDataBuilder->canBuildKeyData( $keyInfoStub, $keyNameStub ) ); + } + + /** + * @throws \PHPUnit\Framework\ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function testCanNotBuildKeyData() : void + { + $keyDataBuilder = new HashKeyDataBuilder( $this->getManager(), $this->getPrettifier() ); + + $keyInfoStub = $this->getMockBuilder( ProvidesKeyInfo::class )->getMockForAbstractClass(); + $keyInfoStub->method( 'getType' )->willReturn( 'hash' ); + $keyNameStub = $this->getMockBuilder( ProvidesKeyName::class )->getMockForAbstractClass(); + $keyNameStub->method( 'hasSubKey' )->willReturn( true ); + + /** @var ProvidesKeyInfo $keyInfoStub */ + /** @var ProvidesKeyName $keyNameStub */ + + $this->assertFalse( $keyDataBuilder->canBuildKeyData( $keyInfoStub, $keyNameStub ) ); + + $keyInfoStub->method( 'getType' )->willReturn( 'string' ); + $keyNameStub->method( 'hasSubKey' )->willReturn( false ); + + $this->assertFalse( $keyDataBuilder->canBuildKeyData( $keyInfoStub, $keyNameStub ) ); + } +} From 32df4f888994d36a4347c1c4ba739f24710ca10d Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Sun, 13 May 2018 18:31:48 +0200 Subject: [PATCH 57/62] Add list key data builder tests --- .../AbstractKeyDataBuilderTest.php | 7 ++ .../ListKeyDataBuilderTest.php | 88 +++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 tests/Unit/Application/ReadModel/KeyDataBuilders/ListKeyDataBuilderTest.php diff --git a/tests/Unit/Application/ReadModel/KeyDataBuilders/AbstractKeyDataBuilderTest.php b/tests/Unit/Application/ReadModel/KeyDataBuilders/AbstractKeyDataBuilderTest.php index 195d7f0..99f3016 100644 --- a/tests/Unit/Application/ReadModel/KeyDataBuilders/AbstractKeyDataBuilderTest.php +++ b/tests/Unit/Application/ReadModel/KeyDataBuilders/AbstractKeyDataBuilderTest.php @@ -81,6 +81,13 @@ function ( string $key, string $hashKey ) ] ); + $this->manager->method( 'getAllListElements' )->willReturn( + [ + 'value', + '{"json": {"key": "value"}}', + ] + ); + $this->prettifier = new class implements PrettifiesString { public function canPrettify( string $data ) : bool diff --git a/tests/Unit/Application/ReadModel/KeyDataBuilders/ListKeyDataBuilderTest.php b/tests/Unit/Application/ReadModel/KeyDataBuilders/ListKeyDataBuilderTest.php new file mode 100644 index 0000000..4e4e069 --- /dev/null +++ b/tests/Unit/Application/ReadModel/KeyDataBuilders/ListKeyDataBuilderTest.php @@ -0,0 +1,88 @@ +getManager(), $this->getPrettifier() ); + + $keyInfoStub = $this->getMockBuilder( ProvidesKeyInfo::class )->getMockForAbstractClass(); + $keyInfoStub->method( 'getType' )->willReturn( 'list' ); + $keyNameStub = $this->getMockBuilder( ProvidesKeyName::class )->getMockForAbstractClass(); + $keyNameStub->method( 'getKeyName' )->willReturn( 'list' ); + $keyNameStub->method( 'hasSubKey' )->willReturn( false ); + $keyNameStub->method( 'getSubKey' )->willReturn( null ); + + /** @var ProvidesKeyInfo $keyInfoStub */ + /** @var ProvidesKeyName $keyNameStub */ + + $keyData = $keyDataBuilder->buildKeyData( $keyInfoStub, $keyNameStub ); + + $expectedKeyData = "Element 0:\n==========\nPretty: value" + . "\n\n---\n\n" + . "Element 1:\n==========\nPretty: {\"json\": {\"key\": \"value\"}}"; + $expectedRawKeyData = "Element 0:\n==========\nvalue" + . "\n\n---\n\n" + . "Element 1:\n==========\n{\"json\": {\"key\": \"value\"}}"; + + $this->assertSame( $expectedKeyData, $keyData->getKeyData() ); + $this->assertSame( $expectedRawKeyData, $keyData->getRawKeyData() ); + $this->assertFalse( $keyData->hasScore() ); + $this->assertNull( $keyData->getScore() ); + } + + /** + * @throws \PHPUnit\Framework\ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function testCanBuildKeyData() : void + { + $keyDataBuilder = new ListKeyDataBuilder( $this->getManager(), $this->getPrettifier() ); + + $keyInfoStub = $this->getMockBuilder( ProvidesKeyInfo::class )->getMockForAbstractClass(); + $keyInfoStub->method( 'getType' )->willReturn( 'list' ); + $keyNameStub = $this->getMockBuilder( ProvidesKeyName::class )->getMockForAbstractClass(); + $keyNameStub->method( 'hasSubKey' )->willReturn( false ); + $keyNameStub->method( 'getSubKey' )->willReturn( null ); + + /** @var ProvidesKeyInfo $keyInfoStub */ + /** @var ProvidesKeyName $keyNameStub */ + + $this->assertTrue( $keyDataBuilder->canBuildKeyData( $keyInfoStub, $keyNameStub ) ); + } + + /** + * @throws \PHPUnit\Framework\ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function testCanNotBuildKeyData() : void + { + $keyDataBuilder = new ListKeyDataBuilder( $this->getManager(), $this->getPrettifier() ); + + $keyInfoStub = $this->getMockBuilder( ProvidesKeyInfo::class )->getMockForAbstractClass(); + $keyInfoStub->method( 'getType' )->willReturn( 'list' ); + $keyNameStub = $this->getMockBuilder( ProvidesKeyName::class )->getMockForAbstractClass(); + $keyNameStub->method( 'hasSubKey' )->willReturn( true ); + + /** @var ProvidesKeyInfo $keyInfoStub */ + /** @var ProvidesKeyName $keyNameStub */ + + $this->assertFalse( $keyDataBuilder->canBuildKeyData( $keyInfoStub, $keyNameStub ) ); + + $keyInfoStub->method( 'getType' )->willReturn( 'string' ); + $keyNameStub->method( 'hasSubKey' )->willReturn( false ); + + $this->assertFalse( $keyDataBuilder->canBuildKeyData( $keyInfoStub, $keyNameStub ) ); + } +} From ae7b7691c494ae0cfdb3920de8f5afa4246cd499 Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Sun, 13 May 2018 18:41:47 +0200 Subject: [PATCH 58/62] Add set key data builder tests --- .../AbstractKeyDataBuilderTest.php | 7 ++ .../KeyDataBuilders/SetKeyDataBuilderTest.php | 87 +++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 tests/Unit/Application/ReadModel/KeyDataBuilders/SetKeyDataBuilderTest.php diff --git a/tests/Unit/Application/ReadModel/KeyDataBuilders/AbstractKeyDataBuilderTest.php b/tests/Unit/Application/ReadModel/KeyDataBuilders/AbstractKeyDataBuilderTest.php index 99f3016..f3589a7 100644 --- a/tests/Unit/Application/ReadModel/KeyDataBuilders/AbstractKeyDataBuilderTest.php +++ b/tests/Unit/Application/ReadModel/KeyDataBuilders/AbstractKeyDataBuilderTest.php @@ -88,6 +88,13 @@ function ( string $key, string $hashKey ) ] ); + $this->manager->method( 'getAllSetMembers' )->willReturn( + [ + 'value', + '{"json": {"key": "value"}}', + ] + ); + $this->prettifier = new class implements PrettifiesString { public function canPrettify( string $data ) : bool diff --git a/tests/Unit/Application/ReadModel/KeyDataBuilders/SetKeyDataBuilderTest.php b/tests/Unit/Application/ReadModel/KeyDataBuilders/SetKeyDataBuilderTest.php new file mode 100644 index 0000000..0d39c40 --- /dev/null +++ b/tests/Unit/Application/ReadModel/KeyDataBuilders/SetKeyDataBuilderTest.php @@ -0,0 +1,87 @@ +getManager(), $this->getPrettifier() ); + + $keyInfoStub = $this->getMockBuilder( ProvidesKeyInfo::class )->getMockForAbstractClass(); + $keyInfoStub->method( 'getType' )->willReturn( 'set' ); + $keyNameStub = $this->getMockBuilder( ProvidesKeyName::class )->getMockForAbstractClass(); + $keyNameStub->method( 'getKeyName' )->willReturn( 'set' ); + $keyNameStub->method( 'hasSubKey' )->willReturn( false ); + $keyNameStub->method( 'getSubKey' )->willReturn( null ); + + /** @var ProvidesKeyInfo $keyInfoStub */ + /** @var ProvidesKeyName $keyNameStub */ + + $keyData = $keyDataBuilder->buildKeyData( $keyInfoStub, $keyNameStub ); + + $expectedKeyData = "Member 0:\n=========\nPretty: value" + . "\n\n---\n\n" + . "Member 1:\n=========\nPretty: {\"json\": {\"key\": \"value\"}}"; + $expectedRawKeyData = "Member 0:\n=========\nvalue" + . "\n\n---\n\n" + . "Member 1:\n=========\n{\"json\": {\"key\": \"value\"}}"; + + $this->assertSame( $expectedKeyData, $keyData->getKeyData() ); + $this->assertSame( $expectedRawKeyData, $keyData->getRawKeyData() ); + $this->assertFalse( $keyData->hasScore() ); + $this->assertNull( $keyData->getScore() ); + } + + /** + * @throws \PHPUnit\Framework\ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function testCanBuildKeyData() : void + { + $keyDataBuilder = new SetKeyDataBuilder( $this->getManager(), $this->getPrettifier() ); + + $keyInfoStub = $this->getMockBuilder( ProvidesKeyInfo::class )->getMockForAbstractClass(); + $keyInfoStub->method( 'getType' )->willReturn( 'set' ); + $keyNameStub = $this->getMockBuilder( ProvidesKeyName::class )->getMockForAbstractClass(); + $keyNameStub->method( 'hasSubKey' )->willReturn( false ); + + /** @var ProvidesKeyInfo $keyInfoStub */ + /** @var ProvidesKeyName $keyNameStub */ + + $this->assertTrue( $keyDataBuilder->canBuildKeyData( $keyInfoStub, $keyNameStub ) ); + } + + /** + * @throws \PHPUnit\Framework\ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function testCanNotBuildKeyData() : void + { + $keyDataBuilder = new SetKeyDataBuilder( $this->getManager(), $this->getPrettifier() ); + + $keyInfoStub = $this->getMockBuilder( ProvidesKeyInfo::class )->getMockForAbstractClass(); + $keyInfoStub->method( 'getType' )->willReturn( 'set' ); + $keyNameStub = $this->getMockBuilder( ProvidesKeyName::class )->getMockForAbstractClass(); + $keyNameStub->method( 'hasSubKey' )->willReturn( true ); + + /** @var ProvidesKeyInfo $keyInfoStub */ + /** @var ProvidesKeyName $keyNameStub */ + + $this->assertFalse( $keyDataBuilder->canBuildKeyData( $keyInfoStub, $keyNameStub ) ); + + $keyInfoStub->method( 'getType' )->willReturn( 'string' ); + $keyNameStub->method( 'hasSubKey' )->willReturn( false ); + + $this->assertFalse( $keyDataBuilder->canBuildKeyData( $keyInfoStub, $keyNameStub ) ); + } +} From 07fdb6921b8c2c8a873f0f17e0aa9365ef76748c Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Sun, 13 May 2018 18:54:21 +0200 Subject: [PATCH 59/62] Add sorted set key data builder tests --- .../SortedSetKeyDataBuilderTest.php | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 tests/Unit/Application/ReadModel/KeyDataBuilders/SortedSetKeyDataBuilderTest.php diff --git a/tests/Unit/Application/ReadModel/KeyDataBuilders/SortedSetKeyDataBuilderTest.php b/tests/Unit/Application/ReadModel/KeyDataBuilders/SortedSetKeyDataBuilderTest.php new file mode 100644 index 0000000..08e196d --- /dev/null +++ b/tests/Unit/Application/ReadModel/KeyDataBuilders/SortedSetKeyDataBuilderTest.php @@ -0,0 +1,87 @@ +getManager(), $this->getPrettifier() ); + + $keyInfoStub = $this->getMockBuilder( ProvidesKeyInfo::class )->getMockForAbstractClass(); + $keyInfoStub->method( 'getType' )->willReturn( 'zset' ); + $keyNameStub = $this->getMockBuilder( ProvidesKeyName::class )->getMockForAbstractClass(); + $keyNameStub->method( 'getKeyName' )->willReturn( 'sorted set' ); + $keyNameStub->method( 'hasSubKey' )->willReturn( false ); + $keyNameStub->method( 'getSubKey' )->willReturn( null ); + + /** @var ProvidesKeyInfo $keyInfoStub */ + /** @var ProvidesKeyName $keyNameStub */ + + $keyData = $keyDataBuilder->buildKeyData( $keyInfoStub, $keyNameStub ); + + $expectedKeyData = "Member 0 (Score: 1):\n====================\nPretty: one" + . "\n\n---\n\n" + . "Member 1 (Score: 2):\n====================\nPretty: {\"json\": {\"key\": \"value\"}}"; + $expectedRawKeyData = "Member 0 (Score: 1):\n====================\none" + . "\n\n---\n\n" + . "Member 1 (Score: 2):\n====================\n{\"json\": {\"key\": \"value\"}}"; + + $this->assertSame( $expectedKeyData, $keyData->getKeyData() ); + $this->assertSame( $expectedRawKeyData, $keyData->getRawKeyData() ); + $this->assertFalse( $keyData->hasScore() ); + $this->assertNull( $keyData->getScore() ); + } + + /** + * @throws \PHPUnit\Framework\ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function testCanBuildKeyData() : void + { + $keyDataBuilder = new SortedSetKeyDataBuilder( $this->getManager(), $this->getPrettifier() ); + + $keyInfoStub = $this->getMockBuilder( ProvidesKeyInfo::class )->getMockForAbstractClass(); + $keyInfoStub->method( 'getType' )->willReturn( 'zset' ); + $keyNameStub = $this->getMockBuilder( ProvidesKeyName::class )->getMockForAbstractClass(); + $keyNameStub->method( 'hasSubKey' )->willReturn( false ); + + /** @var ProvidesKeyInfo $keyInfoStub */ + /** @var ProvidesKeyName $keyNameStub */ + + $this->assertTrue( $keyDataBuilder->canBuildKeyData( $keyInfoStub, $keyNameStub ) ); + } + + /** + * @throws \PHPUnit\Framework\ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function testCanNotBuildKeyData() : void + { + $keyDataBuilder = new SortedSetKeyDataBuilder( $this->getManager(), $this->getPrettifier() ); + + $keyInfoStub = $this->getMockBuilder( ProvidesKeyInfo::class )->getMockForAbstractClass(); + $keyInfoStub->method( 'getType' )->willReturn( 'zset' ); + $keyNameStub = $this->getMockBuilder( ProvidesKeyName::class )->getMockForAbstractClass(); + $keyNameStub->method( 'hasSubKey' )->willReturn( true ); + + /** @var ProvidesKeyInfo $keyInfoStub */ + /** @var ProvidesKeyName $keyNameStub */ + + $this->assertFalse( $keyDataBuilder->canBuildKeyData( $keyInfoStub, $keyNameStub ) ); + + $keyInfoStub->method( 'getType' )->willReturn( 'string' ); + $keyNameStub->method( 'hasSubKey' )->willReturn( false ); + + $this->assertFalse( $keyDataBuilder->canBuildKeyData( $keyInfoStub, $keyNameStub ) ); + } +} From 71d5e6f70ecea41a1ccdee5ac2b36ad01d4fea2f Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Sun, 13 May 2018 19:13:54 +0200 Subject: [PATCH 60/62] Refactor dependency injection of fetch server info query handler --- .../ReadModel/DTO/ServerInformation.php | 11 ------ .../Queries/FetchServerInformationQuery.php | 19 ---------- .../FetchServerInformationQueryHandler.php | 36 +++++++++---------- .../Read/ServerDetailsRequestHandler.php | 9 ++--- ...FetchServerInformationQueryHandlerTest.php | 35 ++++-------------- 5 files changed, 27 insertions(+), 83 deletions(-) delete mode 100644 src/Application/ReadModel/Queries/FetchServerInformationQuery.php diff --git a/src/Application/ReadModel/DTO/ServerInformation.php b/src/Application/ReadModel/DTO/ServerInformation.php index dc0fee4..8a1779c 100644 --- a/src/Application/ReadModel/DTO/ServerInformation.php +++ b/src/Application/ReadModel/DTO/ServerInformation.php @@ -2,14 +2,10 @@ namespace hollodotme\Readis\Application\ReadModel\DTO; -use hollodotme\Readis\Infrastructure\Interfaces\ProvidesServerConfig; use hollodotme\Readis\Infrastructure\Redis\DTO\SlowLogEntry; final class ServerInformation { - /** @var array */ - private $server; - /** @var array */ private $serverConfig; @@ -23,25 +19,18 @@ final class ServerInformation private $serverInfo; public function __construct( - ProvidesServerConfig $server, array $serverConfig, int $slowLogCount, array $slowLogEntries, array $serverInfo ) { - $this->server = $server; $this->serverConfig = $serverConfig; $this->slowLogCount = $slowLogCount; $this->slowLogEntries = $slowLogEntries; $this->serverInfo = $serverInfo; } - public function getServer() : ProvidesServerConfig - { - return $this->server; - } - public function getServerConfig() : array { return $this->serverConfig; diff --git a/src/Application/ReadModel/Queries/FetchServerInformationQuery.php b/src/Application/ReadModel/Queries/FetchServerInformationQuery.php deleted file mode 100644 index c025e88..0000000 --- a/src/Application/ReadModel/Queries/FetchServerInformationQuery.php +++ /dev/null @@ -1,19 +0,0 @@ -serverKey = $serverKey; - } - - public function getServerKey() : string - { - return $this->serverKey; - } -} diff --git a/src/Application/ReadModel/QueryHandlers/FetchServerInformationQueryHandler.php b/src/Application/ReadModel/QueryHandlers/FetchServerInformationQueryHandler.php index bc6317c..df03fc3 100644 --- a/src/Application/ReadModel/QueryHandlers/FetchServerInformationQueryHandler.php +++ b/src/Application/ReadModel/QueryHandlers/FetchServerInformationQueryHandler.php @@ -2,36 +2,36 @@ namespace hollodotme\Readis\Application\ReadModel\QueryHandlers; +use hollodotme\Readis\Application\Interfaces\ProvidesRedisData; use hollodotme\Readis\Application\ReadModel\Constants\ResultType; use hollodotme\Readis\Application\ReadModel\DTO\ServerInformation; -use hollodotme\Readis\Application\ReadModel\Queries\FetchServerInformationQuery; use hollodotme\Readis\Application\ReadModel\Results\FetchServerInformationResult; -use hollodotme\Readis\Exceptions\ServerConfigNotFound; use hollodotme\Readis\Infrastructure\Redis\Exceptions\ConnectionFailedException; -final class FetchServerInformationQueryHandler extends AbstractQueryHandler +final class FetchServerInformationQueryHandler { + /** @var ProvidesRedisData */ + private $serverManager; + + public function __construct( ProvidesRedisData $serverManager ) + { + $this->serverManager = $serverManager; + } + /** - * @param FetchServerInformationQuery $query - * - * @return FetchServerInformationResult * @throws \Exception + * @return FetchServerInformationResult */ - public function handle( FetchServerInformationQuery $query ) : FetchServerInformationResult + public function handle() : FetchServerInformationResult { try { - $serverConfigList = $this->getEnv()->getServerConfigList(); - $server = $serverConfigList->getServerConfig( $query->getServerKey() ); - $serverManager = $this->getEnv()->getServerManager( $server ); - - $serverConfig = $serverManager->getServerConfig(); - $slowLogCount = $serverManager->getSlowLogCount(); - $slowLogsEntries = $serverManager->getSlowLogEntries(); - $serverInfo = $serverManager->getServerInfo(); + $serverConfig = $this->serverManager->getServerConfig(); + $slowLogCount = $this->serverManager->getSlowLogCount(); + $slowLogsEntries = $this->serverManager->getSlowLogEntries(); + $serverInfo = $this->serverManager->getServerInfo(); $serverInformation = new ServerInformation( - $server, $serverConfig, $slowLogCount, $slowLogsEntries, @@ -43,10 +43,6 @@ public function handle( FetchServerInformationQuery $query ) : FetchServerInform return $result; } - catch ( ServerConfigNotFound $e ) - { - return new FetchServerInformationResult( ResultType::FAILURE, $e->getMessage() ); - } catch ( ConnectionFailedException $e ) { return new FetchServerInformationResult( diff --git a/src/Application/Web/Server/Read/ServerDetailsRequestHandler.php b/src/Application/Web/Server/Read/ServerDetailsRequestHandler.php index c76fc32..ea6dff9 100644 --- a/src/Application/Web/Server/Read/ServerDetailsRequestHandler.php +++ b/src/Application/Web/Server/Read/ServerDetailsRequestHandler.php @@ -2,7 +2,6 @@ namespace hollodotme\Readis\Application\Web\Server\Read; -use hollodotme\Readis\Application\ReadModel\Queries\FetchServerInformationQuery; use hollodotme\Readis\Application\ReadModel\QueryHandlers\FetchServerInformationQueryHandler; use hollodotme\Readis\Application\Web\AbstractRequestHandler; use hollodotme\Readis\Exceptions\RuntimeException; @@ -25,8 +24,10 @@ public function handle( ProvidesReadRequestData $request ) $database = (string)$input->get( 'database', '0' ); $serverKey = (string)$input->get( 'serverKey', '0' ); - $query = new FetchServerInformationQuery( $serverKey ); - $result = (new FetchServerInformationQueryHandler( $this->getEnv() ))->handle( $query ); + $server = $this->getEnv()->getServerConfigList()->getServerConfig( $serverKey ); + $serverManager = $this->getEnv()->getServerManagerForServerKey( $serverKey ); + + $result = (new FetchServerInformationQueryHandler( $serverManager ))->handle(); if ( $result->failed() ) { @@ -42,7 +43,7 @@ public function handle( ProvidesReadRequestData $request ) 'appConfig' => $appConfig, 'database' => $database, 'serverKey' => $serverKey, - 'server' => $serverInformation->getServer(), + 'server' => $server, 'serverConfig' => $serverInformation->getServerConfig(), 'slowLogCount' => $serverInformation->getSlowLogCount(), 'slowLogEntries' => $serverInformation->getSlowLogEntries(), diff --git a/tests/Integration/Application/ReadModel/QueryHandlers/FetchServerInformationQueryHandlerTest.php b/tests/Integration/Application/ReadModel/QueryHandlers/FetchServerInformationQueryHandlerTest.php index 63b157e..eb2b84c 100644 --- a/tests/Integration/Application/ReadModel/QueryHandlers/FetchServerInformationQueryHandlerTest.php +++ b/tests/Integration/Application/ReadModel/QueryHandlers/FetchServerInformationQueryHandlerTest.php @@ -2,7 +2,6 @@ namespace hollodotme\Readis\Tests\Integration\Application\ReadModel\QueryHandlers; -use hollodotme\Readis\Application\ReadModel\Queries\FetchServerInformationQuery; use hollodotme\Readis\Application\ReadModel\QueryHandlers\FetchServerInformationQueryHandler; use hollodotme\Readis\Exceptions\ServerConfigNotFound; use PHPUnit\Framework\ExpectationFailedException; @@ -16,42 +15,20 @@ final class FetchServerInformationQueryHandlerTest extends AbstractQueryHandlerT */ public function testCanFetchServerInformation() : void { - $serverKey = '0'; - $serverConfigList = $this->getServerConfigListMock(); + $serverKey = '0'; + $serverManager = $this->getServerManagerMock( $serverKey ); - $query = new FetchServerInformationQuery( $serverKey ); - $result = (new FetchServerInformationQueryHandler( $this->getEnvMock( $serverKey ) ))->handle( $query ); + $result = (new FetchServerInformationQueryHandler( $serverManager ))->handle(); $this->assertTrue( $result->succeeded() ); $this->assertFalse( $result->failed() ); - $expectedServerConfig = $serverConfigList->getServerConfig( $serverKey ); - - $this->assertEquals( $expectedServerConfig, $result->getServerInformation()->getServer() ); $this->assertNotEmpty( $result->getServerInformation()->getServerConfig() ); $this->assertNotEmpty( $result->getServerInformation()->getServerInfo() ); $this->assertSame( 0, $result->getServerInformation()->getSlowLogCount() ); $this->assertCount( 0, $result->getServerInformation()->getSlowLogEntries() ); } - /** - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * @throws ServerConfigNotFound - * @throws \Exception - */ - public function testResultFailsIfServerConfigNotFound() : void - { - $serverKey = '3'; - - $query = new FetchServerInformationQuery( $serverKey ); - $result = (new FetchServerInformationQueryHandler( $this->getEnvMock( $serverKey ) ))->handle( $query ); - - $this->assertFalse( $result->succeeded() ); - $this->assertTrue( $result->failed() ); - $this->assertSame( 'Server config not found for server key: 3', $result->getMessage() ); - } - /** * @throws ExpectationFailedException * @throws InvalidArgumentException @@ -60,10 +37,10 @@ public function testResultFailsIfServerConfigNotFound() : void */ public function testResultFailsIfConnectionToServerFailed() : void { - $serverKey = '1'; + $serverKey = '1'; + $serverManager = $this->getServerManagerMock( $serverKey ); - $query = new FetchServerInformationQuery( $serverKey ); - $result = (new FetchServerInformationQueryHandler( $this->getEnvMock( $serverKey ) ))->handle( $query ); + $result = (new FetchServerInformationQueryHandler( $serverManager ))->handle(); $this->assertFalse( $result->succeeded() ); $this->assertTrue( $result->failed() ); From 2a3f4457e3543ba9b3a61f9db32c658ef8a2d8ca Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Sun, 13 May 2018 19:22:00 +0200 Subject: [PATCH 61/62] Refactor dependency injection of find keys in database query handler --- .../Queries/FindKeysInDatabaseQuery.php | 11 +------ .../QueryHandlers/AbstractQueryHandler.php | 10 ------ .../FindKeysInDatabaseQueryHandler.php | 24 +++++++------- .../Read/AjaxSearchKeysRequestHandler.php | 6 ++-- .../FindKeysInDatabaseQueryHandlerTest.php | 31 +++++-------------- 5 files changed, 25 insertions(+), 57 deletions(-) delete mode 100644 src/Application/ReadModel/QueryHandlers/AbstractQueryHandler.php diff --git a/src/Application/ReadModel/Queries/FindKeysInDatabaseQuery.php b/src/Application/ReadModel/Queries/FindKeysInDatabaseQuery.php index 99ca784..100315d 100644 --- a/src/Application/ReadModel/Queries/FindKeysInDatabaseQuery.php +++ b/src/Application/ReadModel/Queries/FindKeysInDatabaseQuery.php @@ -4,9 +4,6 @@ final class FindKeysInDatabaseQuery { - /** @var string */ - private $serverKey; - /** @var int */ private $database; @@ -16,19 +13,13 @@ final class FindKeysInDatabaseQuery /** @var null|int */ private $limit; - public function __construct( string $serverKey, int $database, string $searchPattern, ?int $limit ) + public function __construct( int $database, string $searchPattern, ?int $limit ) { - $this->serverKey = $serverKey; $this->database = $database; $this->searchPattern = $searchPattern; $this->limit = $limit; } - public function getServerKey() : string - { - return $this->serverKey; - } - public function getDatabase() : int { return $this->database; diff --git a/src/Application/ReadModel/QueryHandlers/AbstractQueryHandler.php b/src/Application/ReadModel/QueryHandlers/AbstractQueryHandler.php deleted file mode 100644 index ec6df2d..0000000 --- a/src/Application/ReadModel/QueryHandlers/AbstractQueryHandler.php +++ /dev/null @@ -1,10 +0,0 @@ -serverManager = $serverManager; + } + public function handle( FindKeysInDatabaseQuery $query ) : FindKeysInDatabaseResult { try { - $serverConfigList = $this->getEnv()->getServerConfigList(); - $serverConfig = $serverConfigList->getServerConfig( $query->getServerKey() ); - $manager = $this->getEnv()->getServerManager( $serverConfig ); - - $manager->selectDatabase( $query->getDatabase() ); - $keyInfoObjects = $manager->getKeyInfoObjects( $query->getSearchPattern(), $query->getLimit() ); + $this->serverManager->selectDatabase( $query->getDatabase() ); + $keyInfoObjects = $this->serverManager->getKeyInfoObjects( $query->getSearchPattern(), $query->getLimit() ); $result = new FindKeysInDatabaseResult(); $result->setKeyInfoObjects( ...$keyInfoObjects ); return $result; } - catch ( ServerConfigNotFound $e ) - { - return new FindKeysInDatabaseResult( ResultType::FAILURE, $e->getMessage() ); - } catch ( ConnectionFailedException $e ) { return new FindKeysInDatabaseResult( diff --git a/src/Application/Web/Server/Read/AjaxSearchKeysRequestHandler.php b/src/Application/Web/Server/Read/AjaxSearchKeysRequestHandler.php index 3086c2b..6e738b2 100644 --- a/src/Application/Web/Server/Read/AjaxSearchKeysRequestHandler.php +++ b/src/Application/Web/Server/Read/AjaxSearchKeysRequestHandler.php @@ -27,8 +27,10 @@ public function handle( ProvidesReadRequestData $request ) $searchPattern = ((string)$input->get( 'searchPattern', '*' )) ?: '*'; $appConfig = $this->getEnv()->getAppConfig(); - $query = new FindKeysInDatabaseQuery( $serverKey, $database, $searchPattern, $limit ); - $result = (new FindKeysInDatabaseQueryHandler( $this->getEnv() ))->handle( $query ); + $serverManager = $this->getEnv()->getServerManagerForServerKey( $serverKey ); + + $query = new FindKeysInDatabaseQuery( $database, $searchPattern, $limit ); + $result = (new FindKeysInDatabaseQueryHandler( $serverManager ))->handle( $query ); if ( $result->failed() ) { diff --git a/tests/Integration/Application/ReadModel/QueryHandlers/FindKeysInDatabaseQueryHandlerTest.php b/tests/Integration/Application/ReadModel/QueryHandlers/FindKeysInDatabaseQueryHandlerTest.php index 92af332..3b4e794 100644 --- a/tests/Integration/Application/ReadModel/QueryHandlers/FindKeysInDatabaseQueryHandlerTest.php +++ b/tests/Integration/Application/ReadModel/QueryHandlers/FindKeysInDatabaseQueryHandlerTest.php @@ -33,8 +33,10 @@ public function testCanFindKeysInDatabase( int $expectedKeyCount ) : void { - $query = new FindKeysInDatabaseQuery( $serverKey, $database, $searchPattern, $limit ); - $result = (new FindKeysInDatabaseQueryHandler( $this->getEnvMock( $serverKey ) ))->handle( $query ); + $serverManager = $this->getServerManagerMock( $serverKey ); + + $query = new FindKeysInDatabaseQuery( $database, $searchPattern, $limit ); + $result = (new FindKeysInDatabaseQueryHandler( $serverManager ))->handle( $query ); $this->assertTrue( $result->succeeded() ); $this->assertFalse( $result->failed() ); @@ -90,24 +92,6 @@ public function keySearchProvider() : array ]; } - /** - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * @throws ServerConfigNotFound - * @throws \Exception - */ - public function testResultFailsIfServerConfigNotFound() : void - { - $serverKey = '3'; - - $query = new FindKeysInDatabaseQuery( $serverKey, 0, '*', null ); - $result = (new FindKeysInDatabaseQueryHandler( $this->getEnvMock( $serverKey ) ))->handle( $query ); - - $this->assertFalse( $result->succeeded() ); - $this->assertTrue( $result->failed() ); - $this->assertSame( 'Server config not found for server key: 3', $result->getMessage() ); - } - /** * @throws ExpectationFailedException * @throws InvalidArgumentException @@ -116,10 +100,11 @@ public function testResultFailsIfServerConfigNotFound() : void */ public function testResultFailsIfConnectionToServerFailed() : void { - $serverKey = '1'; + $serverKey = '1'; + $serverManager = $this->getServerManagerMock( $serverKey ); - $query = new FindKeysInDatabaseQuery( $serverKey, 0, '*', null ); - $result = (new FindKeysInDatabaseQueryHandler( $this->getEnvMock( $serverKey ) ))->handle( $query ); + $query = new FindKeysInDatabaseQuery( 0, '*', null ); + $result = (new FindKeysInDatabaseQueryHandler( $serverManager ))->handle( $query ); $this->assertFalse( $result->succeeded() ); $this->assertTrue( $result->failed() ); From 0d2e13dc56fbffeafe560e7424fd488728fbf5b1 Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Sun, 13 May 2018 19:27:22 +0200 Subject: [PATCH 62/62] Add missing test for server config list --- .../Configs/ServerConfigListTest.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/Unit/Infrastructure/Configs/ServerConfigListTest.php b/tests/Unit/Infrastructure/Configs/ServerConfigListTest.php index 3ea4aca..34f87f9 100644 --- a/tests/Unit/Infrastructure/Configs/ServerConfigListTest.php +++ b/tests/Unit/Infrastructure/Configs/ServerConfigListTest.php @@ -2,6 +2,7 @@ namespace hollodotme\Readis\Tests\Unit\Infrastructure\Configs; +use hollodotme\Readis\Exceptions\NoServersConfigured; use hollodotme\Readis\Exceptions\ServerConfigNotFound; use hollodotme\Readis\Exceptions\ServersConfigNotFound; use hollodotme\Readis\Infrastructure\Configs\ServerConfigList; @@ -84,6 +85,7 @@ public function serverConfigsProvider() : array * * @throws ExpectationFailedException * @throws InvalidArgumentException + * @throws NoServersConfigured * * @dataProvider serverConfigsProvider */ @@ -94,6 +96,19 @@ public function testCanGetServerCongigs( array $serverConfigs ) : void $this->assertContainsOnlyInstancesOf( ProvidesServerConfig::class, $serverConfigList->getServerConfigs() ); } + /** + * @throws NoServersConfigured + */ + public function testThrowsExceptionIfNoServersWereConfigured() : void + { + $serverConfigList = new ServerConfigList( [] ); + + $this->expectException( NoServersConfigured::class ); + $this->expectExceptionMessage( 'No servers were configured.' ); + + $serverConfigList->getServerConfigs(); + } + /** * @param array $serverConfigs *