diff --git a/.redis/DemoData.txt b/.redis/DemoData.txt new file mode 100644 index 0000000..1d12747 --- /dev/null +++ b/.redis/DemoData.txt @@ -0,0 +1,109 @@ +*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"}} +*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 diff --git a/CHANGELOG.md b/CHANGELOG.md index 5cbe317..d67826d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,18 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/) and [Keep a CHANGELOG](http://keepachangelog.com). +## [2.1.0] - YYYY-MM-DD + +### Added + +**Related to [issue #9]:** + +* Output handling for keys of type "set" +* Output handling for keys of type "zset" (sorted sets) +* Output handling for keys of type "list" +* New output of all elements/members/fields in lists, (sorted) sets and hashes +* HyperLogLog prettifier that adds a hint on HLL encoded values + ## [2.0.0] - 2018-04-03 ### Added @@ -65,3 +77,5 @@ First stable release. [1.1.0]: https://github.com/hollodotme/readis/compare/v1.0.1...v1.1.0 [1.0.1]: https://github.com/hollodotme/readis/compare/v1.0.0...v1.0.1 [1.0.0]: https://github.com/hollodotme/readis/tree/v1.0.0 + +[issue #9]: https://github.com/hollodotme/readis/issues/9 \ No newline at end of file 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/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/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/Constants/KeyType.php b/src/Application/ReadModel/Constants/KeyType.php new file mode 100644 index 0000000..cce389a --- /dev/null +++ b/src/Application/ReadModel/Constants/KeyType.php @@ -0,0 +1,16 @@ +keyData = $keyData; + $this->rawKeyData = $rawKeyData; + $this->score = $score; + } + + public function getKeyData() : string + { + return $this->keyData; + } + + 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/DTO/KeyName.php b/src/Application/ReadModel/DTO/KeyName.php new file mode 100644 index 0000000..304bf4d --- /dev/null +++ b/src/Application/ReadModel/DTO/KeyName.php @@ -0,0 +1,35 @@ +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/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 @@ +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 4a7bec9..bd72e4e 100644 --- a/src/Application/ReadModel/QueryHandlers/FetchKeyInformationQueryHandler.php +++ b/src/Application/ReadModel/QueryHandlers/FetchKeyInformationQueryHandler.php @@ -2,16 +2,49 @@ 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\Prettifiers\HyperLogLogPrettifier; 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(), + new HyperLogLogPrettifier() + ); + } + + /** + * @param FetchKeyInformationQuery $query + * + * @throws KeyTypeNotImplemented + * @return FetchKeyInformationResult + */ public function handle( FetchKeyInformationQuery $query ) : FetchKeyInformationResult { try @@ -22,24 +55,12 @@ public function handle( FetchKeyInformationQuery $query ) : FetchKeyInformationR $manager->selectDatabase( $query->getDatabase() ); - $prettifier = new PrettifierChain(); - $prettifier->addPrettifiers( new JsonPrettifier() ); - - if ( null === $query->getHashKey() ) - { - $rawKeyData = $manager->getValue( $query->getKeyName() ) ?: ''; - $keyData = $prettifier->prettify( $rawKeyData ); - } - else - { - $rawKeyData = $manager->getHashValue( $query->getKeyName(), $query->getHashKey() ) ?: ''; - $keyData = $prettifier->prettify( $rawKeyData ); - } - $keyInfo = $manager->getKeyInfoObject( $query->getKeyName() ); + $keyName = new KeyName( $query->getKeyName(), $query->getSubKey() ); + $keyData = $this->getKeyData( $manager, $keyInfo, $keyName ); + $result = new FetchKeyInformationResult(); - $result->setRawKeyData( $rawKeyData ); $result->setKeyData( $keyData ); $result->setKeyInfo( $keyInfo ); @@ -57,4 +78,291 @@ public function handle( FetchKeyInformationQuery $query ) : FetchKeyInformationR ); } } + + /** + * @param ServerManager $manager + * @param ProvidesKeyInfo $keyInfo + * @param ProvidesKeyName $keyName + * + * @throws ConnectionFailedException + * @throws KeyTypeNotImplemented + * @return ProvidesKeyData + */ + private function getKeyData( + ServerManager $manager, + ProvidesKeyInfo $keyInfo, + 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() + ); + } + + /** + * @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 ); + } } diff --git a/src/Application/ReadModel/Results/FetchKeyInformationResult.php b/src/Application/ReadModel/Results/FetchKeyInformationResult.php index 6336c6b..c3aa0f8 100644 --- a/src/Application/ReadModel/Results/FetchKeyInformationResult.php +++ b/src/Application/ReadModel/Results/FetchKeyInformationResult.php @@ -2,45 +2,33 @@ namespace hollodotme\Readis\Application\ReadModel\Results; -use hollodotme\Readis\Application\Interfaces\ProvidesKeyInformation; +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 ProvidesKeyInformation */ + /** @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; } - 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 8ae2ec0..2ac4e30 100644 --- a/src/Application/ReadModel/Results/FindKeysInDatabaseResult.php +++ b/src/Application/ReadModel/Results/FindKeysInDatabaseResult.php @@ -2,19 +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|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/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 ); } ); } diff --git a/src/Application/Web/Server/Read/AjaxKeyDetailsRequestHandler.php b/src/Application/Web/Server/Read/AjaxKeyDetailsRequestHandler.php index e081350..4d45bbe 100644 --- a/src/Application/Web/Server/Read/AjaxKeyDetailsRequestHandler.php +++ b/src/Application/Web/Server/Read/AjaxKeyDetailsRequestHandler.php @@ -23,10 +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', '' ) ) ?: null; - $database = (int)$input->get( 'database', 0 ); + $subKey = base64_decode( $input->get( 'subKey', '' ) ); + if ( '' === $subKey ) + { + $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() ) @@ -38,10 +42,9 @@ public function handle( ProvidesReadRequestData $request ) } $data = [ - 'rawKeyData' => $result->getRawKeyData(), - '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 ec84e27..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 @@
diff --git a/src/Application/Web/Server/Read/Pages/Includes/KeyList.twig b/src/Application/Web/Server/Read/Pages/Includes/KeyList.twig index 0a13c8a..543431c 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 member,score in keyInfo.getSubItems %} +
    3. + + Member {{ loop.index0 }} + + + | Score: {{ score }} + +
    4. + {% endfor %} +
    + {% elseif keyInfo.getType == 'list' %} + + {{ keyInfo.getName }} + + ({{ keyInfo.countSubItems }} elements) + + +
      +
    1. + + (show all elements) + +
    2. + {% for index,value in keyInfo.getSubItems %} +
    3. + + List element + +
    4. + {% endfor %}
    {% else %} redis->multi()->type( $key )->pttl( $key )->exec(); @@ -130,13 +130,41 @@ 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 ); + 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, [] ); } /** @@ -163,4 +191,29 @@ public function getHashValue( string $key, string $hashKey ) /** @noinspection PhpUndefinedMethodInspection */ 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 + * + * @throws ConnectionFailedException + * @return mixed + */ + public function getListValue( string $key, int $index ) + { + /** @noinspection PhpUndefinedMethodInspection */ + return $this->redis->lindex( $key, $index ); + } } 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 { 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 ); } /**