From 2bff128bfd6b828912d204961033d34f6c90d613 Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Fri, 11 May 2018 18:11:51 +0200 Subject: [PATCH 01/23] Add key type constants --- src/Application/ReadModel/Constants/KeyType.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 src/Application/ReadModel/Constants/KeyType.php diff --git a/src/Application/ReadModel/Constants/KeyType.php b/src/Application/ReadModel/Constants/KeyType.php new file mode 100644 index 0000000..bac8674 --- /dev/null +++ b/src/Application/ReadModel/Constants/KeyType.php @@ -0,0 +1,14 @@ + Date: Fri, 11 May 2018 18:13:26 +0200 Subject: [PATCH 02/23] Add interface and implementation for key name DTO --- src/Application/ReadModel/DTO/KeyName.php | 21 +++++++++++++++++++ .../ReadModel/Interfaces/ProvidesKeyName.php | 8 +++++++ 2 files changed, 29 insertions(+) create mode 100644 src/Application/ReadModel/DTO/KeyName.php create mode 100644 src/Application/ReadModel/Interfaces/ProvidesKeyName.php diff --git a/src/Application/ReadModel/DTO/KeyName.php b/src/Application/ReadModel/DTO/KeyName.php new file mode 100644 index 0000000..0c92578 --- /dev/null +++ b/src/Application/ReadModel/DTO/KeyName.php @@ -0,0 +1,21 @@ +keyName = $keyName; + } + + public function getKeyName() : string + { + return $this->keyName; + } +} \ No newline at end of file diff --git a/src/Application/ReadModel/Interfaces/ProvidesKeyName.php b/src/Application/ReadModel/Interfaces/ProvidesKeyName.php new file mode 100644 index 0000000..1a610da --- /dev/null +++ b/src/Application/ReadModel/Interfaces/ProvidesKeyName.php @@ -0,0 +1,8 @@ + Date: Fri, 11 May 2018 18:13:44 +0200 Subject: [PATCH 03/23] Add interface and implementation for hash key name DTO --- .../ReadModel/DTO/HashKeyNames.php | 30 +++++++++++++++++++ .../Interfaces/ProvidesHashKeyNames.php | 8 +++++ 2 files changed, 38 insertions(+) create mode 100644 src/Application/ReadModel/DTO/HashKeyNames.php create mode 100644 src/Application/ReadModel/Interfaces/ProvidesHashKeyNames.php diff --git a/src/Application/ReadModel/DTO/HashKeyNames.php b/src/Application/ReadModel/DTO/HashKeyNames.php new file mode 100644 index 0000000..06f02e6 --- /dev/null +++ b/src/Application/ReadModel/DTO/HashKeyNames.php @@ -0,0 +1,30 @@ +keyName = $keyName; + $this->hashKeyName = $hashKeyName; + } + + public function getKeyName() : string + { + return $this->keyName; + } + + public function getHashKeyName() : string + { + return $this->hashKeyName; + } +} \ No newline at end of file diff --git a/src/Application/ReadModel/Interfaces/ProvidesHashKeyNames.php b/src/Application/ReadModel/Interfaces/ProvidesHashKeyNames.php new file mode 100644 index 0000000..0f31be7 --- /dev/null +++ b/src/Application/ReadModel/Interfaces/ProvidesHashKeyNames.php @@ -0,0 +1,8 @@ + Date: Fri, 11 May 2018 18:14:02 +0200 Subject: [PATCH 04/23] Add interface and implementation for key data DTO --- src/Application/ReadModel/DTO/KeyData.php | 30 +++++++++++++++++++ .../ReadModel/Interfaces/ProvidesKeyData.php | 10 +++++++ 2 files changed, 40 insertions(+) create mode 100644 src/Application/ReadModel/DTO/KeyData.php create mode 100644 src/Application/ReadModel/Interfaces/ProvidesKeyData.php diff --git a/src/Application/ReadModel/DTO/KeyData.php b/src/Application/ReadModel/DTO/KeyData.php new file mode 100644 index 0000000..d1b4489 --- /dev/null +++ b/src/Application/ReadModel/DTO/KeyData.php @@ -0,0 +1,30 @@ +keyData = $keyData; + $this->rawKeyData = $rawKeyData; + } + + public function getKeyData() : string + { + return $this->keyData; + } + + public function getRawKeyData() : string + { + return $this->rawKeyData; + } +} \ No newline at end of file diff --git a/src/Application/ReadModel/Interfaces/ProvidesKeyData.php b/src/Application/ReadModel/Interfaces/ProvidesKeyData.php new file mode 100644 index 0000000..7eb8b32 --- /dev/null +++ b/src/Application/ReadModel/Interfaces/ProvidesKeyData.php @@ -0,0 +1,10 @@ + Date: Fri, 11 May 2018 18:14:32 +0200 Subject: [PATCH 05/23] Cast input to string for base64encode filter --- src/Application/Web/Responses/TwigPage.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Application/Web/Responses/TwigPage.php b/src/Application/Web/Responses/TwigPage.php index cfc81e1..6955ac0 100644 --- a/src/Application/Web/Responses/TwigPage.php +++ b/src/Application/Web/Responses/TwigPage.php @@ -101,7 +101,7 @@ private function getBase64Encoder( string $name ) : TwigFilter $name, function ( $value ) { - return base64_encode( $value ); + return base64_encode( (string)$value ); } ); } From a129176d417404503aefb5975122d070d9d7379e Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Fri, 11 May 2018 18:15:07 +0200 Subject: [PATCH 06/23] Add key list output for keys of type "list" --- .../Server/Read/Pages/Includes/KeyList.twig | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/Application/Web/Server/Read/Pages/Includes/KeyList.twig b/src/Application/Web/Server/Read/Pages/Includes/KeyList.twig index 0a13c8a..66005a2 100644 --- a/src/Application/Web/Server/Read/Pages/Includes/KeyList.twig +++ b/src/Application/Web/Server/Read/Pages/Includes/KeyList.twig @@ -32,6 +32,30 @@ {% endfor %} + {% elseif keyInfo.getType == 'list' %} + + {{ keyInfo.getName }} + + ({{ keyInfo.countSubItems }} elements) + + + {% else %} From 5b636532577669d94bf56e27f71c1a6f0ba07cf8 Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Fri, 11 May 2018 18:15:45 +0200 Subject: [PATCH 07/23] Add sub items to keys of type "list", add getListValue --- src/Infrastructure/Redis/ServerManager.php | 28 +++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/src/Infrastructure/Redis/ServerManager.php b/src/Infrastructure/Redis/ServerManager.php index 9eca909..bb40a61 100644 --- a/src/Infrastructure/Redis/ServerManager.php +++ b/src/Infrastructure/Redis/ServerManager.php @@ -130,13 +130,22 @@ public function getKeyInfoObject( string $key ) : ProvidesKeyInformation { /** @noinspection PhpUndefinedMethodInspection */ $subItems = $this->redis->hKeys( $key ); + + return new KeyInfo( $key, $type, $ttl, $subItems ); } - else + + if ( $type === Redis::REDIS_LIST ) { - $subItems = []; + /** @noinspection PhpUndefinedMethodInspection */ + $listLength = $this->redis->llen( $key ); + + /** @noinspection PhpUndefinedMethodInspection */ + $subItems = $this->redis->lrange( $key, 0, $listLength - 1 ); + + return new KeyInfo( $key, $type, $ttl, $subItems ); } - return new KeyInfo( $key, $type, $ttl, $subItems ); + return new KeyInfo( $key, $type, $ttl, [] ); } /** @@ -163,4 +172,17 @@ public function getHashValue( string $key, string $hashKey ) /** @noinspection PhpUndefinedMethodInspection */ return $this->redis->hGet( $key, $hashKey ); } + + /** + * @param string $key + * @param int $index + * + * @throws ConnectionFailedException + * @return mixed + */ + public function getListValue( string $key, int $index ) + { + /** @noinspection PhpUndefinedMethodInspection */ + return $this->redis->lindex( $key, $index ); + } } From e7f444c00654efbf17f1b248ce758742601935c8 Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Fri, 11 May 2018 18:17:02 +0200 Subject: [PATCH 08/23] Fix hash key fallback value --- .../Web/Server/Read/AjaxKeyDetailsRequestHandler.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Application/Web/Server/Read/AjaxKeyDetailsRequestHandler.php b/src/Application/Web/Server/Read/AjaxKeyDetailsRequestHandler.php index e081350..8010f24 100644 --- a/src/Application/Web/Server/Read/AjaxKeyDetailsRequestHandler.php +++ b/src/Application/Web/Server/Read/AjaxKeyDetailsRequestHandler.php @@ -23,8 +23,12 @@ public function handle( ProvidesReadRequestData $request ) $input = $request->getInput(); $serverKey = (string)$input->get( 'serverKey', '0' ); $key = base64_decode( (string)$input->get( 'keyName' ) ); - $hashKey = base64_decode( $input->get( 'hashKey', '' ) ) ?: null; - $database = (int)$input->get( 'database', 0 ); + $hashKey = base64_decode( $input->get( 'hashKey', '' ) ); + if ( '' === $hashKey ) + { + $hashKey = null; + } + $database = (int)$input->get( 'database', 0 ); $query = new FetchKeyInformationQuery( $serverKey, $database, $key, $hashKey ); $result = (new FetchKeyInformationQueryHandler( $this->getEnv() ))->handle( $query ); From f5c4555834a4676d864a5843d46ad1da3aec1864 Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Fri, 11 May 2018 18:18:00 +0200 Subject: [PATCH 09/23] Add missing doc block --- src/Application/ReadModel/Results/FindKeysInDatabaseResult.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Application/ReadModel/Results/FindKeysInDatabaseResult.php b/src/Application/ReadModel/Results/FindKeysInDatabaseResult.php index 8ae2ec0..911bf56 100644 --- a/src/Application/ReadModel/Results/FindKeysInDatabaseResult.php +++ b/src/Application/ReadModel/Results/FindKeysInDatabaseResult.php @@ -9,6 +9,9 @@ final class FindKeysInDatabaseResult extends AbstractResult /** @var array|ProvidesKeyInformation[] */ private $keyInfoObjects; + /** + * @return array|ProvidesKeyInformation[] + */ public function getKeyInfoObjects() : array { return $this->keyInfoObjects; From 3e3bd99846f1abaf764c1eaf7da67d20644bdce6 Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Fri, 11 May 2018 18:20:02 +0200 Subject: [PATCH 10/23] Refactor key details output handling, #9 * Move retrieval of key value to private method * Add output handling for all elements of keys of type "list" * Add output handling for single element of keys of type "list" --- .../FetchKeyInformationQueryHandler.php | 102 +++++++++++++++--- 1 file changed, 89 insertions(+), 13 deletions(-) diff --git a/src/Application/ReadModel/QueryHandlers/FetchKeyInformationQueryHandler.php b/src/Application/ReadModel/QueryHandlers/FetchKeyInformationQueryHandler.php index 4a7bec9..8660c64 100644 --- a/src/Application/ReadModel/QueryHandlers/FetchKeyInformationQueryHandler.php +++ b/src/Application/ReadModel/QueryHandlers/FetchKeyInformationQueryHandler.php @@ -2,13 +2,24 @@ namespace hollodotme\Readis\Application\ReadModel\QueryHandlers; +use hollodotme\Readis\Application\Interfaces\ProvidesKeyInformation; +use hollodotme\Readis\Application\ReadModel\Constants\KeyType; use hollodotme\Readis\Application\ReadModel\Constants\ResultType; +use hollodotme\Readis\Application\ReadModel\DTO\HashKeyNames; +use hollodotme\Readis\Application\ReadModel\DTO\KeyData; +use hollodotme\Readis\Application\ReadModel\DTO\KeyName; +use hollodotme\Readis\Application\ReadModel\Interfaces\ProvidesHashKeyNames; +use hollodotme\Readis\Application\ReadModel\Interfaces\ProvidesKeyData; +use hollodotme\Readis\Application\ReadModel\Interfaces\ProvidesKeyName; use hollodotme\Readis\Application\ReadModel\Prettifiers\JsonPrettifier; use hollodotme\Readis\Application\ReadModel\Prettifiers\PrettifierChain; use hollodotme\Readis\Application\ReadModel\Queries\FetchKeyInformationQuery; use hollodotme\Readis\Application\ReadModel\Results\FetchKeyInformationResult; use hollodotme\Readis\Exceptions\ServerConfigNotFound; use hollodotme\Readis\Infrastructure\Redis\Exceptions\ConnectionFailedException; +use hollodotme\Readis\Infrastructure\Redis\ServerManager; +use function implode; +use function str_repeat; final class FetchKeyInformationQueryHandler extends AbstractQueryHandler { @@ -22,25 +33,19 @@ public function handle( FetchKeyInformationQuery $query ) : FetchKeyInformationR $manager->selectDatabase( $query->getDatabase() ); - $prettifier = new PrettifierChain(); - $prettifier->addPrettifiers( new JsonPrettifier() ); + $keyInfo = $manager->getKeyInfoObject( $query->getKeyName() ); - if ( null === $query->getHashKey() ) - { - $rawKeyData = $manager->getValue( $query->getKeyName() ) ?: ''; - $keyData = $prettifier->prettify( $rawKeyData ); - } - else + $keyName = new KeyName( $query->getKeyName() ); + if ( null !== $query->getHashKey() ) { - $rawKeyData = $manager->getHashValue( $query->getKeyName(), $query->getHashKey() ) ?: ''; - $keyData = $prettifier->prettify( $rawKeyData ); + $keyName = new HashKeyNames( $query->getKeyName(), $query->getHashKey() ); } - $keyInfo = $manager->getKeyInfoObject( $query->getKeyName() ); + $keyData = $this->getKeyData( $manager, $keyInfo, $keyName ); $result = new FetchKeyInformationResult(); - $result->setRawKeyData( $rawKeyData ); - $result->setKeyData( $keyData ); + $result->setRawKeyData( $keyData->getRawKeyData() ); + $result->setKeyData( $keyData->getKeyData() ); $result->setKeyInfo( $keyInfo ); return $result; @@ -57,4 +62,75 @@ public function handle( FetchKeyInformationQuery $query ) : FetchKeyInformationR ); } } + + /** + * @param ServerManager $manager + * @param ProvidesKeyInformation $keyInfo + * @param ProvidesKeyName $keyName + * + * @throws ConnectionFailedException + * @return ProvidesKeyData + */ + private function getKeyData( + ServerManager $manager, + ProvidesKeyInformation $keyInfo, + ProvidesKeyName $keyName + ) : + ProvidesKeyData + { + $prettifier = new PrettifierChain(); + $prettifier->addPrettifiers( new JsonPrettifier() ); + + if ( KeyType::HASH === $keyInfo->getType() && $keyName instanceof ProvidesHashKeyNames ) + { + $rawKeyData = $manager->getHashValue( $keyName->getKeyName(), $keyName->getHashKeyName() ) ?: ''; + $keyData = $prettifier->prettify( $rawKeyData ); + + return new KeyData( $keyData, $rawKeyData ); + } + + if ( KeyType::LIST === $keyInfo->getType() && $keyName instanceof ProvidesHashKeyNames ) + { + $rawKeyData = $manager->getListValue( $keyName->getKeyName(), (int)$keyName->getHashKeyName() ) ?: ''; + $keyData = $prettifier->prettify( $rawKeyData ); + + return new KeyData( $keyData, $rawKeyData ); + } + + if ( KeyType::LIST === $keyInfo->getType() ) + { + $rawListItems = []; + foreach ( $keyInfo->getSubItems() as $index => $listItem ) + { + $rawListItems[] = sprintf( + "Element %d:\n%s\n%s", + $index, + str_repeat( '=', 8 + strlen( (string)$index ) ), + $listItem + ); + } + + $prettyListItems = []; + foreach ( $keyInfo->getSubItems() as $index => $listItem ) + { + $prettyListItem = $prettifier->prettify( $listItem ); + $prettyListItems[] = sprintf( + "Element %d:\n%s\n%s", + $index, + str_repeat( '=', 8 + strlen( (string)$index ) ), + $prettyListItem + ); + } + + $rawKeyData = implode( "\n\n---\n\n", $rawListItems ); + $keyData = implode( "\n\n---\n\n", $prettyListItems ); + + return new KeyData( $keyData, $rawKeyData ); + } + + $rawKeyData = $manager->getValue( $keyName->getKeyName() ) ?: ''; + $keyData = $prettifier->prettify( $rawKeyData ); + + return new KeyData( $keyData, $rawKeyData ); + } } From 13990760b6a6c314df1347818fcd8beec4b5e6f9 Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Fri, 11 May 2018 22:30:25 +0200 Subject: [PATCH 11/23] Add exception for not implemented key types --- src/Exceptions/KeyTypeNotImplemented.php | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 src/Exceptions/KeyTypeNotImplemented.php diff --git a/src/Exceptions/KeyTypeNotImplemented.php b/src/Exceptions/KeyTypeNotImplemented.php new file mode 100644 index 0000000..b23fbb5 --- /dev/null +++ b/src/Exceptions/KeyTypeNotImplemented.php @@ -0,0 +1,8 @@ + Date: Fri, 11 May 2018 22:31:18 +0200 Subject: [PATCH 12/23] Rename ProvidesKeyInformation to ProvidesKeyInfo --- .../{ProvidesKeyInformation.php => ProvidesKeyInfo.php} | 2 +- .../ReadModel/Results/FetchKeyInformationResult.php | 8 ++++---- .../ReadModel/Results/FindKeysInDatabaseResult.php | 8 ++++---- src/Infrastructure/Redis/DTO/KeyInfo.php | 4 ++-- .../QueryHandlers/FindKeysInDatabaseQueryHandlerTest.php | 4 ++-- tests/Unit/Infrastructure/Redis/ServerManagerTest.php | 4 ++-- 6 files changed, 15 insertions(+), 15 deletions(-) rename src/Application/Interfaces/{ProvidesKeyInformation.php => ProvidesKeyInfo.php} (89%) diff --git a/src/Application/Interfaces/ProvidesKeyInformation.php b/src/Application/Interfaces/ProvidesKeyInfo.php similarity index 89% rename from src/Application/Interfaces/ProvidesKeyInformation.php rename to src/Application/Interfaces/ProvidesKeyInfo.php index 351e82a..316bb3e 100644 --- a/src/Application/Interfaces/ProvidesKeyInformation.php +++ b/src/Application/Interfaces/ProvidesKeyInfo.php @@ -2,7 +2,7 @@ namespace hollodotme\Readis\Application\Interfaces; -interface ProvidesKeyInformation +interface ProvidesKeyInfo { public function getName() : string; diff --git a/src/Application/ReadModel/Results/FetchKeyInformationResult.php b/src/Application/ReadModel/Results/FetchKeyInformationResult.php index 6336c6b..88a6ca3 100644 --- a/src/Application/ReadModel/Results/FetchKeyInformationResult.php +++ b/src/Application/ReadModel/Results/FetchKeyInformationResult.php @@ -2,7 +2,7 @@ namespace hollodotme\Readis\Application\ReadModel\Results; -use hollodotme\Readis\Application\Interfaces\ProvidesKeyInformation; +use hollodotme\Readis\Application\Interfaces\ProvidesKeyInfo; final class FetchKeyInformationResult extends AbstractResult { @@ -12,7 +12,7 @@ final class FetchKeyInformationResult extends AbstractResult /** @var string */ private $keyData; - /** @var ProvidesKeyInformation */ + /** @var ProvidesKeyInfo */ private $keyInfo; public function getRawKeyData() : string @@ -35,12 +35,12 @@ public function setKeyData( string $keyData ) : void $this->keyData = $keyData; } - public function getKeyInfo() : ProvidesKeyInformation + public function getKeyInfo() : ProvidesKeyInfo { return $this->keyInfo; } - public function setKeyInfo( ProvidesKeyInformation $keyInfo ) : void + public function setKeyInfo( ProvidesKeyInfo $keyInfo ) : void { $this->keyInfo = $keyInfo; } diff --git a/src/Application/ReadModel/Results/FindKeysInDatabaseResult.php b/src/Application/ReadModel/Results/FindKeysInDatabaseResult.php index 911bf56..2ac4e30 100644 --- a/src/Application/ReadModel/Results/FindKeysInDatabaseResult.php +++ b/src/Application/ReadModel/Results/FindKeysInDatabaseResult.php @@ -2,22 +2,22 @@ namespace hollodotme\Readis\Application\ReadModel\Results; -use hollodotme\Readis\Application\Interfaces\ProvidesKeyInformation; +use hollodotme\Readis\Application\Interfaces\ProvidesKeyInfo; final class FindKeysInDatabaseResult extends AbstractResult { - /** @var array|ProvidesKeyInformation[] */ + /** @var array|ProvidesKeyInfo[] */ private $keyInfoObjects; /** - * @return array|ProvidesKeyInformation[] + * @return array|ProvidesKeyInfo[] */ public function getKeyInfoObjects() : array { return $this->keyInfoObjects; } - public function setKeyInfoObjects( ProvidesKeyInformation ...$keyInfoObjects ) : void + public function setKeyInfoObjects( ProvidesKeyInfo ...$keyInfoObjects ) : void { $this->keyInfoObjects = $keyInfoObjects; } diff --git a/src/Infrastructure/Redis/DTO/KeyInfo.php b/src/Infrastructure/Redis/DTO/KeyInfo.php index 32bc9de..1b43ed9 100644 --- a/src/Infrastructure/Redis/DTO/KeyInfo.php +++ b/src/Infrastructure/Redis/DTO/KeyInfo.php @@ -2,11 +2,11 @@ namespace hollodotme\Readis\Infrastructure\Redis\DTO; -use hollodotme\Readis\Application\Interfaces\ProvidesKeyInformation; +use hollodotme\Readis\Application\Interfaces\ProvidesKeyInfo; use Redis; use function count; -final class KeyInfo implements ProvidesKeyInformation +final class KeyInfo implements ProvidesKeyInfo { /** @var string */ private $name; diff --git a/tests/Integration/Application/ReadModel/QueryHandlers/FindKeysInDatabaseQueryHandlerTest.php b/tests/Integration/Application/ReadModel/QueryHandlers/FindKeysInDatabaseQueryHandlerTest.php index abd7b0e..534d97f 100644 --- a/tests/Integration/Application/ReadModel/QueryHandlers/FindKeysInDatabaseQueryHandlerTest.php +++ b/tests/Integration/Application/ReadModel/QueryHandlers/FindKeysInDatabaseQueryHandlerTest.php @@ -2,7 +2,7 @@ namespace hollodotme\Readis\Tests\Integration\Application\ReadModel\QueryHandlers; -use hollodotme\Readis\Application\Interfaces\ProvidesKeyInformation; +use hollodotme\Readis\Application\Interfaces\ProvidesKeyInfo; use hollodotme\Readis\Application\ReadModel\Queries\FindKeysInDatabaseQuery; use hollodotme\Readis\Application\ReadModel\QueryHandlers\FindKeysInDatabaseQueryHandler; use hollodotme\Readis\Exceptions\ServerConfigNotFound; @@ -37,7 +37,7 @@ public function testCanFindKeysInDatabase( $this->assertTrue( $result->succeeded() ); $this->assertFalse( $result->failed() ); $this->assertCount( $expectedKeyCount, $result->getKeyInfoObjects() ); - $this->assertContainsOnlyInstancesOf( ProvidesKeyInformation::class, $result->getKeyInfoObjects() ); + $this->assertContainsOnlyInstancesOf( ProvidesKeyInfo::class, $result->getKeyInfoObjects() ); } public function keySearchProvider() : array diff --git a/tests/Unit/Infrastructure/Redis/ServerManagerTest.php b/tests/Unit/Infrastructure/Redis/ServerManagerTest.php index 51a29a6..fe73560 100644 --- a/tests/Unit/Infrastructure/Redis/ServerManagerTest.php +++ b/tests/Unit/Infrastructure/Redis/ServerManagerTest.php @@ -2,7 +2,7 @@ namespace hollodotme\Readis\Tests\Unit\Infrastructure\Redis; -use hollodotme\Readis\Application\Interfaces\ProvidesKeyInformation; +use hollodotme\Readis\Application\Interfaces\ProvidesKeyInfo; use hollodotme\Readis\Application\Interfaces\ProvidesSlowLogData; use hollodotme\Readis\Infrastructure\Interfaces\ProvidesConnectionData; use hollodotme\Readis\Infrastructure\Redis\Exceptions\ConnectionFailedException; @@ -219,7 +219,7 @@ public function testCanGetKeyInfoObjects() : void $serverManager->selectDatabase( 0 ); $keyInfos = $serverManager->getKeyInfoObjects( '*', 100 ); - $this->assertContainsOnlyInstancesOf( ProvidesKeyInformation::class, $keyInfos ); + $this->assertContainsOnlyInstancesOf( ProvidesKeyInfo::class, $keyInfos ); } /** From d25d12112c3569031611712d6e9b491bf43f5f8c Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Fri, 11 May 2018 22:31:58 +0200 Subject: [PATCH 13/23] Remove separate hash key name DTO, add sub key to key name DTO --- .../ReadModel/DTO/HashKeyNames.php | 30 ------------------- src/Application/ReadModel/DTO/KeyName.php | 16 +++++++++- .../ReadModel/Interfaces/ProvidesKeyName.php | 4 +++ 3 files changed, 19 insertions(+), 31 deletions(-) delete mode 100644 src/Application/ReadModel/DTO/HashKeyNames.php diff --git a/src/Application/ReadModel/DTO/HashKeyNames.php b/src/Application/ReadModel/DTO/HashKeyNames.php deleted file mode 100644 index 06f02e6..0000000 --- a/src/Application/ReadModel/DTO/HashKeyNames.php +++ /dev/null @@ -1,30 +0,0 @@ -keyName = $keyName; - $this->hashKeyName = $hashKeyName; - } - - public function getKeyName() : string - { - return $this->keyName; - } - - public function getHashKeyName() : string - { - return $this->hashKeyName; - } -} \ No newline at end of file diff --git a/src/Application/ReadModel/DTO/KeyName.php b/src/Application/ReadModel/DTO/KeyName.php index 0c92578..304bf4d 100644 --- a/src/Application/ReadModel/DTO/KeyName.php +++ b/src/Application/ReadModel/DTO/KeyName.php @@ -9,13 +9,27 @@ final class KeyName implements ProvidesKeyName /** @var string */ private $keyName; - public function __construct( string $keyName ) + /** @var null|string */ + private $subKey; + + public function __construct( string $keyName, ?string $subKey = null ) { $this->keyName = $keyName; + $this->subKey = $subKey; } public function getKeyName() : string { return $this->keyName; } + + public function hasSubKey() : bool + { + return (null !== $this->subKey); + } + + public function getSubKey() : ?string + { + return $this->subKey; + } } \ No newline at end of file diff --git a/src/Application/ReadModel/Interfaces/ProvidesKeyName.php b/src/Application/ReadModel/Interfaces/ProvidesKeyName.php index 1a610da..8d8c1f5 100644 --- a/src/Application/ReadModel/Interfaces/ProvidesKeyName.php +++ b/src/Application/ReadModel/Interfaces/ProvidesKeyName.php @@ -5,4 +5,8 @@ interface ProvidesKeyName { public function getKeyName() : string; + + public function hasSubKey() : bool; + + public function getSubKey() : ?string; } \ No newline at end of file From 3303d72c5bfdf40e6c3d2257e26693f49ba75175 Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Fri, 11 May 2018 22:34:30 +0200 Subject: [PATCH 14/23] Add output handling for keys of type set, #9 * Add show all hash fields to hashes * Refactor retrieval of key output * Add error handling * Prepare output handling of sorted sets --- .../FetchKeyInformationQueryHandler.php | 264 ++++++++++++++---- .../Server/Read/Pages/Includes/KeyList.twig | 69 ++++- src/Infrastructure/Redis/ServerManager.php | 39 ++- 3 files changed, 313 insertions(+), 59 deletions(-) diff --git a/src/Application/ReadModel/QueryHandlers/FetchKeyInformationQueryHandler.php b/src/Application/ReadModel/QueryHandlers/FetchKeyInformationQueryHandler.php index 8660c64..900e7f0 100644 --- a/src/Application/ReadModel/QueryHandlers/FetchKeyInformationQueryHandler.php +++ b/src/Application/ReadModel/QueryHandlers/FetchKeyInformationQueryHandler.php @@ -2,27 +2,45 @@ namespace hollodotme\Readis\Application\ReadModel\QueryHandlers; -use hollodotme\Readis\Application\Interfaces\ProvidesKeyInformation; +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\HashKeyNames; use hollodotme\Readis\Application\ReadModel\DTO\KeyData; use hollodotme\Readis\Application\ReadModel\DTO\KeyName; -use hollodotme\Readis\Application\ReadModel\Interfaces\ProvidesHashKeyNames; +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\Prettifiers\JsonPrettifier; use hollodotme\Readis\Application\ReadModel\Prettifiers\PrettifierChain; 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; use function implode; use function str_repeat; final class FetchKeyInformationQueryHandler extends AbstractQueryHandler { + /** @var PrettifiesString */ + private $prettifier; + + public function __construct( ProvidesInfrastructure $env ) + { + parent::__construct( $env ); + + $this->prettifier = new PrettifierChain(); + $this->prettifier->addPrettifiers( new JsonPrettifier() ); + } + + /** + * @param FetchKeyInformationQuery $query + * + * @throws KeyTypeNotImplemented + * @return FetchKeyInformationResult + */ public function handle( FetchKeyInformationQuery $query ) : FetchKeyInformationResult { try @@ -35,12 +53,7 @@ public function handle( FetchKeyInformationQuery $query ) : FetchKeyInformationR $keyInfo = $manager->getKeyInfoObject( $query->getKeyName() ); - $keyName = new KeyName( $query->getKeyName() ); - if ( null !== $query->getHashKey() ) - { - $keyName = new HashKeyNames( $query->getKeyName(), $query->getHashKey() ); - } - + $keyName = new KeyName( $query->getKeyName(), $query->getHashKey() ); $keyData = $this->getKeyData( $manager, $keyInfo, $keyName ); $result = new FetchKeyInformationResult(); @@ -64,72 +77,223 @@ public function handle( FetchKeyInformationQuery $query ) : FetchKeyInformationR } /** - * @param ServerManager $manager - * @param ProvidesKeyInformation $keyInfo - * @param ProvidesKeyName $keyName + * @param ServerManager $manager + * @param ProvidesKeyInfo $keyInfo + * @param ProvidesKeyName $keyName * * @throws ConnectionFailedException + * @throws KeyTypeNotImplemented * @return ProvidesKeyData */ private function getKeyData( ServerManager $manager, - ProvidesKeyInformation $keyInfo, + ProvidesKeyInfo $keyInfo, ProvidesKeyName $keyName - ) : - ProvidesKeyData + ) : ProvidesKeyData { - $prettifier = new PrettifierChain(); - $prettifier->addPrettifiers( new JsonPrettifier() ); + if ( $keyName->hasSubKey() ) + { + return $this->getSubKeyData( $manager, $keyInfo, $keyName ); + } + + return $this->getSingleKeyData( $manager, $keyInfo, $keyName ); + } - if ( KeyType::HASH === $keyInfo->getType() && $keyName instanceof ProvidesHashKeyNames ) + /** + * @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->getHashKeyName() ) ?: ''; - $keyData = $prettifier->prettify( $rawKeyData ); + $rawKeyData = $manager->getHashValue( $keyName->getKeyName(), $keyName->getSubKey() ) ?: ''; + $keyData = $this->prettifier->prettify( $rawKeyData ); return new KeyData( $keyData, $rawKeyData ); } - if ( KeyType::LIST === $keyInfo->getType() && $keyName instanceof ProvidesHashKeyNames ) + if ( KeyType::LIST === $keyInfo->getType() ) { - $rawKeyData = $manager->getListValue( $keyName->getKeyName(), (int)$keyName->getHashKeyName() ) ?: ''; - $keyData = $prettifier->prettify( $rawKeyData ); + $rawKeyData = $manager->getListValue( $keyName->getKeyName(), (int)$keyName->getSubKey() ) ?: ''; + $keyData = $this->prettifier->prettify( $rawKeyData ); return new KeyData( $keyData, $rawKeyData ); } - if ( KeyType::LIST === $keyInfo->getType() ) + if ( KeyType::SET === $keyInfo->getType() ) { - $rawListItems = []; - foreach ( $keyInfo->getSubItems() as $index => $listItem ) - { - $rawListItems[] = sprintf( - "Element %d:\n%s\n%s", - $index, - str_repeat( '=', 8 + strlen( (string)$index ) ), - $listItem - ); - } - - $prettyListItems = []; - foreach ( $keyInfo->getSubItems() as $index => $listItem ) - { - $prettyListItem = $prettifier->prettify( $listItem ); - $prettyListItems[] = sprintf( - "Element %d:\n%s\n%s", - $index, - str_repeat( '=', 8 + strlen( (string)$index ) ), - $prettyListItem - ); - } - - $rawKeyData = implode( "\n\n---\n\n", $rawListItems ); - $keyData = implode( "\n\n---\n\n", $prettyListItems ); + $rawKeyData = $keyInfo->getSubItems()[ (int)$keyName->getSubKey() ]; + $keyData = $this->prettifier->prettify( $rawKeyData ); return new KeyData( $keyData, $rawKeyData ); } + throw new KeyTypeNotImplemented( + 'Key type not implemented or does not support sub keys: ' . $keyInfo->getType() + ); + } + + /** + * @param ServerManager $manager + * @param ProvidesKeyInfo $keyInfo + * @param ProvidesKeyName $keyName + * + * @throws ConnectionFailedException + * @throws KeyTypeNotImplemented + * @return ProvidesKeyData + */ + private function getSingleKeyData( + 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::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( '=', 8 + 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( '=', 8 + 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( '=', 9 + 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( '=', 9 + strlen( (string)$index ) ), + $prettyMember + ); + } + + $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 = $prettifier->prettify( $rawKeyData ); + $keyData = $this->prettifier->prettify( $rawKeyData ); return new KeyData( $keyData, $rawKeyData ); } diff --git a/src/Application/Web/Server/Read/Pages/Includes/KeyList.twig b/src/Application/Web/Server/Read/Pages/Includes/KeyList.twig index 66005a2..c68d218 100644 --- a/src/Application/Web/Server/Read/Pages/Includes/KeyList.twig +++ b/src/Application/Web/Server/Read/Pages/Includes/KeyList.twig @@ -19,10 +19,16 @@ role="button"> {{ keyInfo.getName }} - ({{ keyInfo.countSubItems }} sub items) + ({{ keyInfo.countSubItems }} fields) -
    + + {% elseif keyInfo.getType == 'set' %} + + {{ keyInfo.getName }} + + ({{ keyInfo.countSubItems }} members) + + + + {% elseif keyInfo.getType == 'zset' %} + + {{ keyInfo.getName }} + + ({{ keyInfo.countSubItems }} members) + + +
      +
    1. + + (show all members) + +
    2. + {% for score,member in keyInfo.getSubItems %} +
    3. + + + Score: {{ member }} + + + + Member {{ loop.index0 }} + +
    4. + {% endfor %}
    {% elseif keyInfo.getType == 'list' %} - +
{% else %} diff --git a/src/Infrastructure/Redis/ServerManager.php b/src/Infrastructure/Redis/ServerManager.php index bb40a61..aec7b1d 100644 --- a/src/Infrastructure/Redis/ServerManager.php +++ b/src/Infrastructure/Redis/ServerManager.php @@ -2,7 +2,7 @@ namespace hollodotme\Readis\Infrastructure\Redis; -use hollodotme\Readis\Application\Interfaces\ProvidesKeyInformation; +use hollodotme\Readis\Application\Interfaces\ProvidesKeyInfo; use hollodotme\Readis\Application\Interfaces\ProvidesSlowLogData; use hollodotme\Readis\Infrastructure\Interfaces\ProvidesConnectionData; use hollodotme\Readis\Infrastructure\Redis\DTO\KeyInfo; @@ -99,7 +99,7 @@ public function getKeys( string $keyPattern = '*' ) : array * @param string $keyPattern * @param int|null $limit * - * @return array|ProvidesKeyInformation[] + * @return array|ProvidesKeyInfo[] * @throws ConnectionFailedException */ public function getKeyInfoObjects( string $keyPattern, ?int $limit ) : array @@ -118,10 +118,10 @@ public function getKeyInfoObjects( string $keyPattern, ?int $limit ) : array /** * @param string $key * - * @return ProvidesKeyInformation + * @return ProvidesKeyInfo * @throws ConnectionFailedException */ - public function getKeyInfoObject( string $key ) : ProvidesKeyInformation + public function getKeyInfoObject( string $key ) : ProvidesKeyInfo { /** @noinspection PhpUndefinedMethodInspection */ [$type, $ttl] = $this->redis->multi()->type( $key )->pttl( $key )->exec(); @@ -145,6 +145,25 @@ public function getKeyInfoObject( string $key ) : ProvidesKeyInformation return new KeyInfo( $key, $type, $ttl, $subItems ); } + if ( $type === Redis::REDIS_SET ) + { + /** @noinspection PhpUndefinedMethodInspection */ + $subItems = $this->redis->smembers( $key ); + + return new KeyInfo( $key, $type, $ttl, $subItems ); + } + + if ( $type === Redis::REDIS_ZSET ) + { + /** @noinspection PhpUndefinedMethodInspection */ + $setLength = $this->redis->zcard( $key ); + + /** @noinspection PhpUndefinedMethodInspection */ + $subItems = $this->redis->zrange( $key, 0, $setLength - 1, true ); + + return new KeyInfo( $key, $type, $ttl, $subItems ); + } + return new KeyInfo( $key, $type, $ttl, [] ); } @@ -173,6 +192,18 @@ public function getHashValue( string $key, string $hashKey ) return $this->redis->hGet( $key, $hashKey ); } + /** + * @param string $key + * + * @throws ConnectionFailedException + * @return array + */ + public function getAllHashValues( string $key ) : array + { + /** @noinspection PhpUndefinedMethodInspection */ + return (array)$this->redis->hGetAll( $key ); + } + /** * @param string $key * @param int $index From b321721449243870cc3986de54cfc52aef1dee0e Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Sat, 12 May 2018 14:54:58 +0200 Subject: [PATCH 15/23] Add redis demo data protocol --- .redis/DemoData.txt | 73 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 .redis/DemoData.txt diff --git a/.redis/DemoData.txt b/.redis/DemoData.txt new file mode 100644 index 0000000..f814c3a --- /dev/null +++ b/.redis/DemoData.txt @@ -0,0 +1,73 @@ +*8 +$4 +ZADD +$4 +zset +$1 +1 +$3 +one +$1 +2 +$3 +two +$1 +3 +$25 +{"json":{"key": "value"}} +*4 +$4 +ZADD +$4 +zset +$1 +2 +$9 +two again +*3 +$3 +SET +$6 +string +$25 +{"json":{"key": "value"}} +*5 +$4 +SADD +$3 +set +$3 +one +$3 +two +$25 +{"json":{"key": "value"}} +*5 +$5 +RPUSH +$4 +list +$3 +one +$1 +2 +$25 +{"json":{"key": "value"}} +*4 +$4 +HSET +$4 +hash +$5 +field +$5 +value +*4 +$4 +HSET +$4 +hash +$4 +json +$25 +{"json":{"key": "value"}} \ No newline at end of file From cf42a98a4520d46e9c3ba850a6b1dfe0c0278760 Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Sat, 12 May 2018 14:55:17 +0200 Subject: [PATCH 16/23] Add sorted set to key types --- src/Application/ReadModel/Constants/KeyType.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Application/ReadModel/Constants/KeyType.php b/src/Application/ReadModel/Constants/KeyType.php index bac8674..cce389a 100644 --- a/src/Application/ReadModel/Constants/KeyType.php +++ b/src/Application/ReadModel/Constants/KeyType.php @@ -4,11 +4,13 @@ abstract class KeyType { - public const HASH = 'hash'; + public const HASH = 'hash'; - public const LIST = 'list'; + public const LIST = 'list'; - public const SET = 'set'; + public const SET = 'set'; - public const STRING = 'string'; + public const SORTED_SET = 'zset'; + + public const STRING = 'string'; } \ No newline at end of file From 6196ce9ad8cb2ac273a41856aeb222455bb148ae Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Sat, 12 May 2018 14:55:37 +0200 Subject: [PATCH 17/23] Add optional score value to key data DTO --- src/Application/ReadModel/DTO/KeyData.php | 17 ++++++++++++++++- .../ReadModel/Interfaces/ProvidesKeyData.php | 4 ++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/Application/ReadModel/DTO/KeyData.php b/src/Application/ReadModel/DTO/KeyData.php index d1b4489..9e127b0 100644 --- a/src/Application/ReadModel/DTO/KeyData.php +++ b/src/Application/ReadModel/DTO/KeyData.php @@ -12,10 +12,14 @@ final class KeyData implements ProvidesKeyData /** @var string */ private $rawKeyData; - public function __construct( string $keyData, string $rawKeyData ) + /** @var null|float */ + private $score; + + public function __construct( string $keyData, string $rawKeyData, ?float $score = null ) { $this->keyData = $keyData; $this->rawKeyData = $rawKeyData; + $this->score = $score; } public function getKeyData() : string @@ -27,4 +31,15 @@ public function getRawKeyData() : string { return $this->rawKeyData; } + + public function hasScore() : bool + { + return (null !== $this->score); + } + + public function getScore() : ?float + { + return $this->score; + } + } \ No newline at end of file diff --git a/src/Application/ReadModel/Interfaces/ProvidesKeyData.php b/src/Application/ReadModel/Interfaces/ProvidesKeyData.php index 7eb8b32..ff66198 100644 --- a/src/Application/ReadModel/Interfaces/ProvidesKeyData.php +++ b/src/Application/ReadModel/Interfaces/ProvidesKeyData.php @@ -7,4 +7,8 @@ interface ProvidesKeyData public function getKeyData() : string; public function getRawKeyData() : string; + + public function hasScore() : bool; + + public function getScore() : ?float; } \ No newline at end of file From 2685567cbfff208c2ceb85b0ed813ad9c27f57a9 Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Sat, 12 May 2018 14:57:38 +0200 Subject: [PATCH 18/23] Add output handling for keys of type zset, #9 * Update find key info result object * Add mass and single output for sorted sets --- docker-compose.yml | 2 + .../FetchKeyInformationQueryHandler.php | 74 +++++++++++++++++-- .../Results/FetchKeyInformationResult.php | 20 +---- .../Read/AjaxKeyDetailsRequestHandler.php | 1 - .../Server/Read/Pages/Includes/KeyData.twig | 16 ++-- .../Server/Read/Pages/Includes/KeyList.twig | 10 +-- 6 files changed, 87 insertions(+), 36 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 0cc05e0..9698a38 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,3 +4,5 @@ services: image: redis:3 ports: - 6379:6379 + volumes: + - ./:/repo \ No newline at end of file diff --git a/src/Application/ReadModel/QueryHandlers/FetchKeyInformationQueryHandler.php b/src/Application/ReadModel/QueryHandlers/FetchKeyInformationQueryHandler.php index 900e7f0..3af20f4 100644 --- a/src/Application/ReadModel/QueryHandlers/FetchKeyInformationQueryHandler.php +++ b/src/Application/ReadModel/QueryHandlers/FetchKeyInformationQueryHandler.php @@ -57,8 +57,7 @@ public function handle( FetchKeyInformationQuery $query ) : FetchKeyInformationR $keyData = $this->getKeyData( $manager, $keyInfo, $keyName ); $result = new FetchKeyInformationResult(); - $result->setRawKeyData( $keyData->getRawKeyData() ); - $result->setKeyData( $keyData->getKeyData() ); + $result->setKeyData( $keyData ); $result->setKeyInfo( $keyInfo ); return $result; @@ -96,7 +95,7 @@ private function getKeyData( return $this->getSubKeyData( $manager, $keyInfo, $keyName ); } - return $this->getSingleKeyData( $manager, $keyInfo, $keyName ); + return $this->getWholeKeyData( $manager, $keyInfo, $keyName ); } /** @@ -138,6 +137,23 @@ private function getSubKeyData( 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() ); @@ -152,7 +168,7 @@ private function getSubKeyData( * @throws KeyTypeNotImplemented * @return ProvidesKeyData */ - private function getSingleKeyData( + private function getWholeKeyData( ServerManager $manager, ProvidesKeyInfo $keyInfo, ProvidesKeyName $keyName @@ -173,6 +189,11 @@ private function getSingleKeyData( return $this->getKeyDataForWholeSet( $keyInfo ); } + if ( KeyType::SORTED_SET === $keyInfo->getType() ) + { + return $this->getKeyDataForWholeSortedSet( $keyInfo ); + } + if ( KeyType::STRING === $keyInfo->getType() ) { return $this->getKeyDataForString( $manager, $keyName ); @@ -198,7 +219,7 @@ private function getKeyDataForWholeHash( ServerManager $manager, ProvidesKeyName $rawListItems[] = sprintf( "Hash key %s:\n%s\n%s", $hashKey, - str_repeat( '=', 8 + strlen( (string)$hashKey ) ), + str_repeat( '=', 10 + strlen( (string)$hashKey ) ), $hashValue ); } @@ -210,7 +231,7 @@ private function getKeyDataForWholeHash( ServerManager $manager, ProvidesKeyName $prettyListItems[] = sprintf( "Hash key %s:\n%s\n%s", $hashKey, - str_repeat( '=', 8 + strlen( (string)$hashKey ) ), + str_repeat( '=', 10 + strlen( (string)$hashKey ) ), $prettyListItem ); } @@ -260,7 +281,7 @@ private function getKeyDataForWholeSet( ProvidesKeyInfo $keyInfo ) : ProvidesKey $rawMembers[] = sprintf( "Member %d:\n%s\n%s", $index, - str_repeat( '=', 9 + strlen( (string)$index ) ), + str_repeat( '=', 8 + strlen( (string)$index ) ), $member ); } @@ -272,9 +293,46 @@ private function getKeyDataForWholeSet( ProvidesKeyInfo $keyInfo ) : ProvidesKey $prettyMembers[] = sprintf( "Member %d:\n%s\n%s", $index, - str_repeat( '=', 9 + strlen( (string)$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 ); + } + + private function getKeyDataForWholeSortedSet( ProvidesKeyInfo $keyInfo ) : ProvidesKeyData + { + $rawMembers = []; + $i = 0; + foreach ( $keyInfo->getSubItems() as $member => $score ) + { + $rawMembers[] = sprintf( + "Member %d (Score: %d):\n%s\n%s", + $i, + $score, + str_repeat( '=', 18 + strlen( (string)$i ) + strlen( (string)$score ) ), + $member + ); + $i++; + } + + $prettyMembers = []; + $i = 0; + foreach ( $keyInfo->getSubItems() as $member => $score ) + { + $prettyMember = $this->prettifier->prettify( $member ); + $prettyMembers[] = sprintf( + "Member %d (Score: %d):\n%s\n%s", + $i, + $score, + str_repeat( '=', 18 + strlen( (string)$i ) + strlen( (string)$score ) ), $prettyMember ); + $i++; } $rawKeyData = implode( "\n\n---\n\n", $rawMembers ); diff --git a/src/Application/ReadModel/Results/FetchKeyInformationResult.php b/src/Application/ReadModel/Results/FetchKeyInformationResult.php index 88a6ca3..c3aa0f8 100644 --- a/src/Application/ReadModel/Results/FetchKeyInformationResult.php +++ b/src/Application/ReadModel/Results/FetchKeyInformationResult.php @@ -3,34 +3,22 @@ namespace hollodotme\Readis\Application\ReadModel\Results; use hollodotme\Readis\Application\Interfaces\ProvidesKeyInfo; +use hollodotme\Readis\Application\ReadModel\Interfaces\ProvidesKeyData; final class FetchKeyInformationResult extends AbstractResult { - /** @var string */ - private $rawKeyData; - - /** @var string */ + /** @var ProvidesKeyData */ private $keyData; /** @var ProvidesKeyInfo */ private $keyInfo; - public function getRawKeyData() : string - { - return $this->rawKeyData; - } - - public function setRawKeyData( string $rawKeyData ) : void - { - $this->rawKeyData = $rawKeyData; - } - - public function getKeyData() : string + public function getKeyData() : ProvidesKeyData { return $this->keyData; } - public function setKeyData( string $keyData ) : void + public function setKeyData( ProvidesKeyData $keyData ) : void { $this->keyData = $keyData; } diff --git a/src/Application/Web/Server/Read/AjaxKeyDetailsRequestHandler.php b/src/Application/Web/Server/Read/AjaxKeyDetailsRequestHandler.php index 8010f24..8f38e72 100644 --- a/src/Application/Web/Server/Read/AjaxKeyDetailsRequestHandler.php +++ b/src/Application/Web/Server/Read/AjaxKeyDetailsRequestHandler.php @@ -42,7 +42,6 @@ public function handle( ProvidesReadRequestData $request ) } $data = [ - 'rawKeyData' => $result->getRawKeyData(), 'keyData' => $result->getKeyData(), 'keyInfo' => $result->getKeyInfo(), 'hashKey' => $hashKey, diff --git a/src/Application/Web/Server/Read/Pages/Includes/KeyData.twig b/src/Application/Web/Server/Read/Pages/Includes/KeyData.twig index ec84e27..16a0134 100644 --- a/src/Application/Web/Server/Read/Pages/Includes/KeyData.twig +++ b/src/Application/Web/Server/Read/Pages/Includes/KeyData.twig @@ -9,7 +9,7 @@
diff --git a/src/Application/Web/Server/Read/Pages/Includes/KeyList.twig b/src/Application/Web/Server/Read/Pages/Includes/KeyList.twig index c68d218..6ae71b8 100644 --- a/src/Application/Web/Server/Read/Pages/Includes/KeyList.twig +++ b/src/Application/Web/Server/Read/Pages/Includes/KeyList.twig @@ -77,17 +77,15 @@ (show all members) - {% for score,member in keyInfo.getSubItems %} + {% for member,score in keyInfo.getSubItems %}
  • - - - Score: {{ member }} - - Member {{ loop.index0 }} + + | Score: {{ score }} +
  • {% endfor %} From 42fbea95d111f3ec23a204b5a37bbbb61770cffb Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Sat, 12 May 2018 15:04:03 +0200 Subject: [PATCH 19/23] Update find key information query handler tests --- .../FetchKeyInformationQueryHandlerTest.php | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/tests/Integration/Application/ReadModel/QueryHandlers/FetchKeyInformationQueryHandlerTest.php b/tests/Integration/Application/ReadModel/QueryHandlers/FetchKeyInformationQueryHandlerTest.php index 6777650..949682a 100644 --- a/tests/Integration/Application/ReadModel/QueryHandlers/FetchKeyInformationQueryHandlerTest.php +++ b/tests/Integration/Application/ReadModel/QueryHandlers/FetchKeyInformationQueryHandlerTest.php @@ -4,6 +4,7 @@ use hollodotme\Readis\Application\ReadModel\Queries\FetchKeyInformationQuery; use hollodotme\Readis\Application\ReadModel\QueryHandlers\FetchKeyInformationQueryHandler; +use hollodotme\Readis\Exceptions\KeyTypeNotImplemented; use hollodotme\Readis\Exceptions\ServerConfigNotFound; use PHPUnit\Framework\ExpectationFailedException; use SebastianBergmann\RecursionContext\InvalidArgumentException; @@ -16,11 +17,13 @@ final class FetchKeyInformationQueryHandlerTest extends AbstractQueryHandlerTest * @param string $expectedKeyType * @param string $expectedKeyData * @param string $expectedRawKeyData + * @param bool $expectedHasScore + * @param float|null $expectedScore * * @throws ExpectationFailedException * @throws InvalidArgumentException + * @throws KeyTypeNotImplemented * @throws ServerConfigNotFound - * * @dataProvider keyInfoProvider */ public function testCanFetchKeyInformation( @@ -28,7 +31,9 @@ public function testCanFetchKeyInformation( ?string $hashKey, string $expectedKeyType, string $expectedKeyData, - string $expectedRawKeyData + string $expectedRawKeyData, + bool $expectedHasScore, + ?float $expectedScore ) : void { $serverKey = '0'; @@ -39,13 +44,14 @@ public function testCanFetchKeyInformation( $this->assertTrue( $result->succeeded() ); $this->assertFalse( $result->failed() ); - $keyInfo = $result->getKeyInfo(); - $keyData = $result->getKeyData(); - $rawKeyData = $result->getRawKeyData(); + $keyInfo = $result->getKeyInfo(); + $keyData = $result->getKeyData(); $this->assertSame( $expectedKeyType, $keyInfo->getType() ); - $this->assertSame( $expectedKeyData, $keyData ); - $this->assertSame( $expectedRawKeyData, $rawKeyData ); + $this->assertSame( $expectedKeyData, $keyData->getKeyData() ); + $this->assertSame( $expectedRawKeyData, $keyData->getRawKeyData() ); + $this->assertSame( $expectedHasScore, $keyData->hasScore() ); + $this->assertSame( $expectedScore, $keyData->getScore() ); } public function keyInfoProvider() : array @@ -57,6 +63,8 @@ public function keyInfoProvider() : array 'expectedType' => 'string', 'expectedKeyData' => 'test', 'expectedRawKeyData' => 'test', + 'expectedHasScore' => false, + 'expectedScore' => null, ], [ 'key' => 'test', @@ -64,6 +72,8 @@ public function keyInfoProvider() : array 'expectedType' => 'hash', 'expectedKeyData' => "{\n \"json\": {\n \"key\": \"value\"\n }\n}", 'expectedRawKeyData' => '{"json": {"key": "value"}}', + 'expectedHasScore' => false, + 'expectedScore' => null, ], ]; } @@ -72,6 +82,7 @@ public function keyInfoProvider() : array * @throws ExpectationFailedException * @throws InvalidArgumentException * @throws ServerConfigNotFound + * @throws KeyTypeNotImplemented */ public function testResultFailsIfServerConfigNotFound() : void { @@ -91,6 +102,7 @@ public function testResultFailsIfServerConfigNotFound() : void * @throws ExpectationFailedException * @throws InvalidArgumentException * @throws ServerConfigNotFound + * @throws KeyTypeNotImplemented */ public function testResultFailsIfConnectionToServerFailed() : void { From 7a3b812b102b4b8462526e1b64b2884ef8309929 Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Sat, 12 May 2018 15:59:03 +0200 Subject: [PATCH 20/23] Add geo and HyperLogLog demo data --- .redis/DemoData.txt | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/.redis/DemoData.txt b/.redis/DemoData.txt index f814c3a..1d12747 100644 --- a/.redis/DemoData.txt +++ b/.redis/DemoData.txt @@ -70,4 +70,40 @@ hash $4 json $25 -{"json":{"key": "value"}} \ No newline at end of file +{"json":{"key": "value"}} +*8 +$6 +GEOADD +$3 +geo +$9 +13.361389 +$9 +38.115556 +$7 +Palermo +$9 +15.087269 +$9 +37.502669 +$7 +Catania +*9 +$5 +PFADD +$11 +hyperLogLog +$1 +a +$1 +b +$1 +c +$1 +d +$1 +e +$1 +f +$1 +g From 3f2c49e6e4264f01d815ea3131a0ca4c036abe59 Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Sat, 12 May 2018 16:00:36 +0200 Subject: [PATCH 21/23] Add HyperLogLog prettifier --- .../Prettifiers/HyperLogLogPrettifier.php | 19 +++++++++++++++ .../FetchKeyInformationQueryHandler.php | 24 +++++++++++++------ 2 files changed, 36 insertions(+), 7 deletions(-) create mode 100644 src/Application/ReadModel/Prettifiers/HyperLogLogPrettifier.php diff --git a/src/Application/ReadModel/Prettifiers/HyperLogLogPrettifier.php b/src/Application/ReadModel/Prettifiers/HyperLogLogPrettifier.php new file mode 100644 index 0000000..c5304f9 --- /dev/null +++ b/src/Application/ReadModel/Prettifiers/HyperLogLogPrettifier.php @@ -0,0 +1,19 @@ +prettifier = new PrettifierChain(); - $this->prettifier->addPrettifiers( new JsonPrettifier() ); + $this->prettifier->addPrettifiers( + new JsonPrettifier(), + new HyperLogLogPrettifier() + ); } /** @@ -304,16 +308,22 @@ private function getKeyDataForWholeSet( ProvidesKeyInfo $keyInfo ) : ProvidesKey return new KeyData( $keyData, $rawKeyData ); } + /** + * @param ProvidesKeyInfo $keyInfo + * + * @return ProvidesKeyData + */ private function getKeyDataForWholeSortedSet( ProvidesKeyInfo $keyInfo ) : ProvidesKeyData { + $setMembers = $keyInfo->getSubItems(); $rawMembers = []; $i = 0; - foreach ( $keyInfo->getSubItems() as $member => $score ) + foreach ( $setMembers as $member => $score ) { $rawMembers[] = sprintf( - "Member %d (Score: %d):\n%s\n%s", + "Member %d (Score: %s):\n%s\n%s", $i, - $score, + (string)$score, str_repeat( '=', 18 + strlen( (string)$i ) + strlen( (string)$score ) ), $member ); @@ -322,13 +332,13 @@ private function getKeyDataForWholeSortedSet( ProvidesKeyInfo $keyInfo ) : Provi $prettyMembers = []; $i = 0; - foreach ( $keyInfo->getSubItems() as $member => $score ) + foreach ( $setMembers as $member => $score ) { $prettyMember = $this->prettifier->prettify( $member ); $prettyMembers[] = sprintf( - "Member %d (Score: %d):\n%s\n%s", + "Member %d (Score: %s):\n%s\n%s", $i, - $score, + (string)$score, str_repeat( '=', 18 + strlen( (string)$i ) + strlen( (string)$score ) ), $prettyMember ); From 21b7b1d9fcff30f744f7a7333e9a5a19337132fe Mon Sep 17 00:00:00 2001 From: Holger Woltersdorf Date: Sat, 12 May 2018 16:11:34 +0200 Subject: [PATCH 22/23] Rename hashKey to subKey where needed --- src/Application/Configs/IceHawkConfig.php | 14 ++++++++------ .../ReadModel/Queries/FetchKeyInformationQuery.php | 10 +++++----- .../FetchKeyInformationQueryHandler.php | 2 +- .../Server/Read/AjaxKeyDetailsRequestHandler.php | 14 +++++++------- .../Web/Server/Read/Pages/Includes/KeyData.twig | 2 +- .../Web/Server/Read/Pages/Includes/KeyList.twig | 6 +++--- 6 files changed, 25 insertions(+), 23 deletions(-) diff --git a/src/Application/Configs/IceHawkConfig.php b/src/Application/Configs/IceHawkConfig.php index ecc4adb..463d61c 100644 --- a/src/Application/Configs/IceHawkConfig.php +++ b/src/Application/Configs/IceHawkConfig.php @@ -58,12 +58,14 @@ private function buildReadRoutes() : void $quotedBaseUri = preg_quote( $baseUrl, '!' ); $this->readRoutes = [ - '^' . $quotedBaseUri . '/?$' => ServerSelectionRequestHandler::class, - '^' . $quotedBaseUri . '/server/(?\d+)/stats/?$' => ServerStatsRequestHandler::class, - '^' . $quotedBaseUri . '/server/(?\d+)(?:/database/(?\d+))?/?$' => ServerDetailsRequestHandler::class, - '^' . $quotedBaseUri . '/server/(?\d+)/database/(?\d+)/keys/?$' => AjaxSearchKeysRequestHandler::class, - '^' . $quotedBaseUri . '/server/(?\d+)/database/(?\d+)/keys/(?.+)/hash/(?.+)/?$' => AjaxKeyDetailsRequestHandler::class, - '^' . $quotedBaseUri . '/server/(?\d+)/database/(?\d+)/keys/(?.+)/?$' => AjaxKeyDetailsRequestHandler::class, + '^' . $quotedBaseUri . '/?$' => ServerSelectionRequestHandler::class, + '^' . $quotedBaseUri . '/server/(?\d+)/stats/?$' => ServerStatsRequestHandler::class, + '^' . $quotedBaseUri . '/server/(?\d+)(?:/database/(?\d+))?/?$' => ServerDetailsRequestHandler::class, + '^' . $quotedBaseUri . '/server/(?\d+)/database/(?\d+)/keys/?$' => AjaxSearchKeysRequestHandler::class, + '^' + . $quotedBaseUri + . '/server/(?\d+)/database/(?\d+)/keys/(?.+)/hash/(?.+)/?$' => AjaxKeyDetailsRequestHandler::class, + '^' . $quotedBaseUri . '/server/(?\d+)/database/(?\d+)/keys/(?.+)/?$' => AjaxKeyDetailsRequestHandler::class, ]; } } diff --git a/src/Application/ReadModel/Queries/FetchKeyInformationQuery.php b/src/Application/ReadModel/Queries/FetchKeyInformationQuery.php index 940ee9c..29bcddb 100644 --- a/src/Application/ReadModel/Queries/FetchKeyInformationQuery.php +++ b/src/Application/ReadModel/Queries/FetchKeyInformationQuery.php @@ -14,14 +14,14 @@ final class FetchKeyInformationQuery private $keyName; /** @var null|string */ - private $hashKey; + private $subKey; - public function __construct( string $serverKey, int $database, string $keyName, ?string $hashKey ) + public function __construct( string $serverKey, int $database, string $keyName, ?string $subKey ) { $this->serverKey = $serverKey; $this->database = $database; $this->keyName = $keyName; - $this->hashKey = $hashKey; + $this->subKey = $subKey; } public function getServerKey() : string @@ -39,8 +39,8 @@ public function getKeyName() : string return $this->keyName; } - public function getHashKey() : ?string + public function getSubKey() : ?string { - return $this->hashKey; + return $this->subKey; } } diff --git a/src/Application/ReadModel/QueryHandlers/FetchKeyInformationQueryHandler.php b/src/Application/ReadModel/QueryHandlers/FetchKeyInformationQueryHandler.php index 7dd3f88..bd72e4e 100644 --- a/src/Application/ReadModel/QueryHandlers/FetchKeyInformationQueryHandler.php +++ b/src/Application/ReadModel/QueryHandlers/FetchKeyInformationQueryHandler.php @@ -57,7 +57,7 @@ public function handle( FetchKeyInformationQuery $query ) : FetchKeyInformationR $keyInfo = $manager->getKeyInfoObject( $query->getKeyName() ); - $keyName = new KeyName( $query->getKeyName(), $query->getHashKey() ); + $keyName = new KeyName( $query->getKeyName(), $query->getSubKey() ); $keyData = $this->getKeyData( $manager, $keyInfo, $keyName ); $result = new FetchKeyInformationResult(); diff --git a/src/Application/Web/Server/Read/AjaxKeyDetailsRequestHandler.php b/src/Application/Web/Server/Read/AjaxKeyDetailsRequestHandler.php index 8f38e72..4d45bbe 100644 --- a/src/Application/Web/Server/Read/AjaxKeyDetailsRequestHandler.php +++ b/src/Application/Web/Server/Read/AjaxKeyDetailsRequestHandler.php @@ -23,14 +23,14 @@ public function handle( ProvidesReadRequestData $request ) $input = $request->getInput(); $serverKey = (string)$input->get( 'serverKey', '0' ); $key = base64_decode( (string)$input->get( 'keyName' ) ); - $hashKey = base64_decode( $input->get( 'hashKey', '' ) ); - if ( '' === $hashKey ) + $subKey = base64_decode( $input->get( 'subKey', '' ) ); + if ( '' === $subKey ) { - $hashKey = null; + $subKey = null; } $database = (int)$input->get( 'database', 0 ); - $query = new FetchKeyInformationQuery( $serverKey, $database, $key, $hashKey ); + $query = new FetchKeyInformationQuery( $serverKey, $database, $key, $subKey ); $result = (new FetchKeyInformationQueryHandler( $this->getEnv() ))->handle( $query ); if ( $result->failed() ) @@ -42,9 +42,9 @@ public function handle( ProvidesReadRequestData $request ) } $data = [ - 'keyData' => $result->getKeyData(), - 'keyInfo' => $result->getKeyInfo(), - 'hashKey' => $hashKey, + 'keyData' => $result->getKeyData(), + 'keyInfo' => $result->getKeyInfo(), + 'subKey' => $subKey, ]; (new TwigPage())->respond( 'Server/Read/Pages/Includes/KeyData.twig', $data ); diff --git a/src/Application/Web/Server/Read/Pages/Includes/KeyData.twig b/src/Application/Web/Server/Read/Pages/Includes/KeyData.twig index 16a0134..f0e7148 100644 --- a/src/Application/Web/Server/Read/Pages/Includes/KeyData.twig +++ b/src/Application/Web/Server/Read/Pages/Includes/KeyData.twig @@ -1,7 +1,7 @@