mirror of
https://github.com/doctrine/KeyValueStore.git
synced 2026-04-24 08:58:21 +02:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7a8465631f | |||
| 76b76d42e9 | |||
| 9fe32a6388 | |||
| 1089486506 | |||
| e32cd91113 | |||
| 8f07e47320 | |||
| 186c8e04d2 | |||
| fe7dcc5c79 | |||
| 218a870dd3 | |||
| b62b9ea098 | |||
| 3800fa4d5c | |||
| a50dade76d | |||
| 6f154f5378 |
+19
-6
@@ -1,15 +1,10 @@
|
|||||||
language: php
|
language: php
|
||||||
|
|
||||||
services:
|
services:
|
||||||
|
- docker
|
||||||
- mongodb
|
- mongodb
|
||||||
- redis-server
|
- redis-server
|
||||||
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
-
|
|
||||||
dist: precise
|
|
||||||
php: 5.5
|
|
||||||
|
|
||||||
php:
|
php:
|
||||||
- 5.6
|
- 5.6
|
||||||
- 7.0
|
- 7.0
|
||||||
@@ -17,7 +12,25 @@ php:
|
|||||||
- 7.2
|
- 7.2
|
||||||
- 7.3
|
- 7.3
|
||||||
|
|
||||||
|
addons:
|
||||||
|
apt:
|
||||||
|
sources:
|
||||||
|
-
|
||||||
|
key_url: 'https://packagecloud.io/gpg.key'
|
||||||
|
sourceline: 'deb https://packagecloud.io/basho/riak/ubuntu/ trusty main'
|
||||||
|
-
|
||||||
|
key_url: 'https://packagecloud.io/gpg.key'
|
||||||
|
sourceline: 'deb-src https://packagecloud.io/basho/riak/ubuntu/ trusty main'
|
||||||
|
update: true
|
||||||
|
|
||||||
|
cache:
|
||||||
|
apt: true
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
|
- docker run -d -p 8000:8000 amazon/dynamodb-local
|
||||||
|
- sudo apt-get install -y --allow-unauthenticated riak
|
||||||
|
- sudo service riak start
|
||||||
|
- pecl install --force mongodb
|
||||||
- if [[ ${TRAVIS_PHP_VERSION:0:1} != "7" ]]; then sh ./tests/travis.sh; fi
|
- if [[ ${TRAVIS_PHP_VERSION:0:1} != "7" ]]; then sh ./tests/travis.sh; fi
|
||||||
- composer self-update
|
- composer self-update
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
# Upgrade to 0.3
|
||||||
|
|
||||||
|
## BC Break: Fixed MongoDB storage usage
|
||||||
|
|
||||||
|
Before v0.3 the storage name associated to a class wasn't used when the storage is `MongoDbStorage`.
|
||||||
|
In order to be consistent with other storage drivers, the `storageName` is now used for the collection name when storing and data.
|
||||||
|
To get the same behavior as in older versions, pass the collection name given in the constructor arguments as storage name.
|
||||||
+3
-3
@@ -9,13 +9,13 @@
|
|||||||
"doctrine/couchdb": "^1.0.0-beta4",
|
"doctrine/couchdb": "^1.0.0-beta4",
|
||||||
"phpunit/phpunit": "^4.8|^5.0",
|
"phpunit/phpunit": "^4.8|^5.0",
|
||||||
"aws/aws-sdk-php": "^3.8",
|
"aws/aws-sdk-php": "^3.8",
|
||||||
"riak/riak-client": "dev-master"
|
"php-riak/riak-client": "^1.0@alpha",
|
||||||
|
"mongodb/mongodb": "^1.4"
|
||||||
},
|
},
|
||||||
"suggest": {
|
"suggest": {
|
||||||
"aws/aws-sdk-php": "to use the DynamoDB storage",
|
"aws/aws-sdk-php": "to use the DynamoDB storage",
|
||||||
"doctrine/couchdb": "to use the CouchDB storage",
|
"doctrine/couchdb": "to use the CouchDB storage",
|
||||||
"ext-couchbase": "to use the Couchbase storage",
|
"ext-couchbase": "to use the Couchbase storage"
|
||||||
"riak/riak-client": "to use the Riak storage"
|
|
||||||
},
|
},
|
||||||
"description": "Simple Key-Value Store Abstraction Layer that maps to PHP objects, allowing for many backends.",
|
"description": "Simple Key-Value Store Abstraction Layer that maps to PHP objects, allowing for many backends.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ This guide covers getting started with the Doctrine Key Value Store.
|
|||||||
|
|
||||||
To use the KeyValueStore you actually need:
|
To use the KeyValueStore you actually need:
|
||||||
|
|
||||||
- PHP 5.5 or above
|
- PHP 5.6 or above
|
||||||
- Composer Package Manager (`Install Composer
|
- Composer Package Manager (`Install Composer
|
||||||
<http://getcomposer.org/doc/00-intro.md>`_)
|
<http://getcomposer.org/doc/00-intro.md>`_)
|
||||||
|
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ So far the following drivers exist (and are documented here):
|
|||||||
* Microsoft Windows Azure Table
|
* Microsoft Windows Azure Table
|
||||||
* Couchbase
|
* Couchbase
|
||||||
* CouchDB
|
* CouchDB
|
||||||
|
* DynamoDB
|
||||||
* MongoDB
|
* MongoDB
|
||||||
* Riak
|
* Riak
|
||||||
|
|
||||||
@@ -209,51 +210,42 @@ See the `AWS docs <http://docs.aws.amazon.com/amazondynamodb/latest/developergui
|
|||||||
|
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
$sdk = new \Aws\Sdk([...]);
|
$client = DynamoDbClient::factory([...])
|
||||||
$client = $sdk->createDynamoDb();
|
|
||||||
|
|
||||||
$storage = new DynamoDbStorage(
|
$storage = new DynamoDbStorage($client);
|
||||||
$client,
|
|
||||||
// Optional key name, defaults to Id.
|
|
||||||
null,
|
|
||||||
// Optional table name/ key name pairs.
|
|
||||||
// This example uses a table called Awesome keyed by MyKey.
|
|
||||||
['storage_keys' => ['Awesome' => 'MyKey']]
|
|
||||||
);
|
|
||||||
|
|
||||||
MongoDB
|
MongoDB
|
||||||
-------
|
-------
|
||||||
|
|
||||||
Mongo support is provided using a `Mongo <http://php.net/manual/en/class.mongo.php>`_
|
MongoDB is based on `mongodb/mongodb <https://github.com/mongodb/mongo-php-library>`_:
|
||||||
instance, the collection name and the database name.
|
MongoDB support is provided using a `Database <https://docs.mongodb.com/php-library/current/reference/class/MongoDBDatabase/>`_
|
||||||
|
instance.
|
||||||
Both the options ``collection`` and ``database`` are required.
|
|
||||||
|
|
||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
|
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use MongoDB\Client;
|
||||||
use Doctrine\KeyValueStore\Storage\MongoDbStorage;
|
use Doctrine\KeyValueStore\Storage\MongoDbStorage;
|
||||||
|
|
||||||
$conn = new \Mongo(/* connection parameters and options */);
|
$client = new Client(/* connection parameters and options */);
|
||||||
|
|
||||||
$storage = new MongoDbStorage($conn, array(
|
$storage = new MongoDbStorage($client->your_database);
|
||||||
'collection' => 'your_collection',
|
|
||||||
'database' => 'your_database',
|
|
||||||
));
|
|
||||||
|
|
||||||
Riak
|
Riak
|
||||||
----
|
----
|
||||||
|
|
||||||
Riak support is provided through the library `riak/riak-client <https://github.com/nacmartin/riak-client>`_ :
|
Riak support is provided through the library `php-riak/riak-client <https://github.com/php-riak/riak-client>`_ :
|
||||||
|
|
||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
|
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
use Doctrine\KeyValueStore\Storage\RiakStorage;
|
use Doctrine\KeyValueStore\Storage\RiakStorage;
|
||||||
use Riak\Client;
|
use Riak\Client\RiakClientBuilder;
|
||||||
|
|
||||||
$conn = new Riak(/* connection parameters */);
|
$conn = (new RiakClientBuilder())
|
||||||
|
->withNodeUri(/* connection DNS */)
|
||||||
|
->build();
|
||||||
|
|
||||||
$storage = new RiakStorage($conn);
|
$storage = new RiakStorage($conn);
|
||||||
|
|||||||
@@ -22,40 +22,21 @@ namespace Doctrine\KeyValueStore\Storage;
|
|||||||
|
|
||||||
use Aws\DynamoDb\DynamoDbClient;
|
use Aws\DynamoDb\DynamoDbClient;
|
||||||
use Aws\DynamoDb\Marshaler;
|
use Aws\DynamoDb\Marshaler;
|
||||||
use Doctrine\KeyValueStore\InvalidArgumentException;
|
use Doctrine\Common\Cache\ArrayCache;
|
||||||
|
use Doctrine\Common\Cache\Cache;
|
||||||
use Doctrine\KeyValueStore\NotFoundException;
|
use Doctrine\KeyValueStore\NotFoundException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DyanmoDb storage
|
* DynamoDb storage.
|
||||||
*
|
*
|
||||||
* @author Stan Lemon <stosh1985@gmail.com>
|
* @author Stan Lemon <stosh1985@gmail.com>
|
||||||
*/
|
*/
|
||||||
class DynamoDbStorage implements Storage
|
class DynamoDbStorage implements Storage
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The key that DynamoDb uses to indicate the name of the table.
|
* @var DynamoDbClient
|
||||||
*/
|
*/
|
||||||
const TABLE_NAME_KEY = 'TableName';
|
private $client;
|
||||||
|
|
||||||
/**
|
|
||||||
* The key that DynamoDb uses to indicate whether or not to do a consistent read.
|
|
||||||
*/
|
|
||||||
const CONSISTENT_READ_KEY = 'ConsistentRead';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The key that is used to refer to the DynamoDb table key.
|
|
||||||
*/
|
|
||||||
const TABLE_KEY = 'Key';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The key that is used to refer to the marshaled item for DynamoDb table.
|
|
||||||
*/
|
|
||||||
const TABLE_ITEM_KEY = 'Item';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var \Aws\DynamoDb\DynamoDbClient
|
|
||||||
*/
|
|
||||||
protected $client;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Marshaler
|
* @var Marshaler
|
||||||
@@ -63,158 +44,27 @@ class DynamoDbStorage implements Storage
|
|||||||
private $marshaler;
|
private $marshaler;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string
|
* @var Cache
|
||||||
*/
|
*/
|
||||||
private $defaultKeyName = 'Id';
|
private $descriptionCache;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A associative array where the key is the table name and the value is the name of the key.
|
* @param DynamoDbClient $client The client for connecting to AWS DynamoDB
|
||||||
*
|
* @param Marshaler|null $marshaler (optional) Marshaller for converting data to/from DynamoDB format
|
||||||
* @var array
|
* @param Cache|null $descriptionCache Cache used to store tables description
|
||||||
*/
|
|
||||||
private $tableKeys = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param DynamoDbClient $client The client for connecting to AWS DynamoDB.
|
|
||||||
* @param Marshaler|null $marshaler (optional) Marshaller for converting data to/from DynamoDB format.
|
|
||||||
* @param string $defaultKeyName (optional) Default name to use for keys.
|
|
||||||
* @param array $tableKeys $tableKeys (optional) An associative array for keys representing table names and values
|
|
||||||
* representing key names for those tables.
|
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
DynamoDbClient $client,
|
DynamoDbClient $client,
|
||||||
Marshaler $marshaler = null,
|
Marshaler $marshaler = null,
|
||||||
$defaultKeyName = null,
|
Cache $descriptionCache = null
|
||||||
array $tableKeys = []
|
|
||||||
) {
|
) {
|
||||||
$this->client = $client;
|
$this->client = $client;
|
||||||
$this->marshaler = $marshaler ?: new Marshaler();
|
$this->marshaler = $marshaler ?: new Marshaler();
|
||||||
|
$this->descriptionCache = $descriptionCache ?: new ArrayCache();
|
||||||
if ($defaultKeyName !== null) {
|
|
||||||
$this->setDefaultKeyName($defaultKeyName);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($tableKeys as $table => $keyName) {
|
|
||||||
$this->setKeyForTable($table, $keyName);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates a DynamoDB key name.
|
* {@inheritdoc}
|
||||||
*
|
|
||||||
* @param $name mixed The name to validate.
|
|
||||||
*
|
|
||||||
* @throws InvalidArgumentException When the key name is invalid.
|
|
||||||
*/
|
|
||||||
private function validateKeyName($name)
|
|
||||||
{
|
|
||||||
if (! is_string($name)) {
|
|
||||||
throw InvalidArgumentException::invalidType('key', 'string', $name);
|
|
||||||
}
|
|
||||||
|
|
||||||
$len = strlen($name);
|
|
||||||
if ($len > 255 || $len < 1) {
|
|
||||||
throw InvalidArgumentException::invalidLength('name', 1, 255);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validates a DynamoDB table name.
|
|
||||||
*
|
|
||||||
* @see http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Limits.html
|
|
||||||
*
|
|
||||||
* @param $name string The table name to validate.
|
|
||||||
*
|
|
||||||
* @throws InvalidArgumentException When the name is invalid.
|
|
||||||
*/
|
|
||||||
private function validateTableName($name)
|
|
||||||
{
|
|
||||||
if (! is_string($name)) {
|
|
||||||
throw InvalidArgumentException::invalidType('key', 'string', $name);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! preg_match('/^[a-z0-9_.-]{3,255}$/i', $name)) {
|
|
||||||
throw InvalidArgumentException::invalidTableName($name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the default key name for storage tables.
|
|
||||||
*
|
|
||||||
* @param $name string The default name to use for the key.
|
|
||||||
*
|
|
||||||
* @throws InvalidArgumentException When the key name is invalid.
|
|
||||||
*/
|
|
||||||
private function setDefaultKeyName($name)
|
|
||||||
{
|
|
||||||
$this->validateKeyName($name);
|
|
||||||
$this->defaultKeyName = $name;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves the default key name.
|
|
||||||
*
|
|
||||||
* @return string The default key name.
|
|
||||||
*/
|
|
||||||
public function getDefaultKeyName()
|
|
||||||
{
|
|
||||||
return $this->defaultKeyName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets a key name for a specific table.
|
|
||||||
*
|
|
||||||
* @param $table string The name of the table.
|
|
||||||
* @param $key string The name of the string.
|
|
||||||
*
|
|
||||||
* @throws InvalidArgumentException When the key or table name is invalid.
|
|
||||||
*/
|
|
||||||
private function setKeyForTable($table, $key)
|
|
||||||
{
|
|
||||||
$this->validateTableName($table);
|
|
||||||
$this->validateKeyName($key);
|
|
||||||
$this->tableKeys[$table] = $key;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves a specific name for a key for a given table. The default is returned if this table does not have
|
|
||||||
* an actual override.
|
|
||||||
*
|
|
||||||
* @param string $tableName The name of the table.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
private function getKeyNameForTable($tableName)
|
|
||||||
{
|
|
||||||
return isset($this->tableKeys[$tableName]) ?
|
|
||||||
$this->tableKeys[$tableName] :
|
|
||||||
$this->defaultKeyName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prepares a key to be in a valid format for lookups for DynamoDB. If passing an array, that means that the key
|
|
||||||
* is the name of the key and the value is the actual value for the lookup.
|
|
||||||
*
|
|
||||||
* @param string $storageName Table name.
|
|
||||||
* @param string $key Key name.
|
|
||||||
*
|
|
||||||
* @return array The key in DynamoDB format.
|
|
||||||
*/
|
|
||||||
private function prepareKey($storageName, $key)
|
|
||||||
{
|
|
||||||
if (is_array($key)) {
|
|
||||||
$keyValue = reset($key);
|
|
||||||
$keyName = key($key);
|
|
||||||
} else {
|
|
||||||
$keyValue = $key;
|
|
||||||
$keyName = $this->getKeyNameForTable($storageName);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->marshaler->marshalItem([$keyName => $keyValue]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
*/
|
||||||
public function supportsPartialUpdates()
|
public function supportsPartialUpdates()
|
||||||
{
|
{
|
||||||
@@ -222,7 +72,7 @@ class DynamoDbStorage implements Storage
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function supportsCompositePrimaryKeys()
|
public function supportsCompositePrimaryKeys()
|
||||||
{
|
{
|
||||||
@@ -230,7 +80,7 @@ class DynamoDbStorage implements Storage
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function requiresCompositePrimaryKeys()
|
public function requiresCompositePrimaryKeys()
|
||||||
{
|
{
|
||||||
@@ -238,55 +88,98 @@ class DynamoDbStorage implements Storage
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* Prepares a key to be in a valid format for lookups for DynamoDB. If passing an array, that means that the key
|
||||||
|
* is the name of the key and the value is the actual value for the lookup.
|
||||||
|
*
|
||||||
|
* @param string $storageName Table name
|
||||||
|
* @param array|string $key Key name
|
||||||
|
*
|
||||||
|
* @return array The key in DynamoDB format
|
||||||
|
*/
|
||||||
|
private function prepareKey($storageName, $key)
|
||||||
|
{
|
||||||
|
if (! $this->descriptionCache->contains($storageName)) {
|
||||||
|
$result = $this->client->describeTable([
|
||||||
|
'TableName' => $storageName,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$keys = isset($result['Table']['KeySchema'])
|
||||||
|
? $result['Table']['KeySchema']
|
||||||
|
: [];
|
||||||
|
$keys = array_column($keys, 'AttributeName') ?: [];
|
||||||
|
|
||||||
|
$this->descriptionCache->save($storageName, $keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
$keys = isset($keys) ? $keys : $this->descriptionCache->fetch($storageName);
|
||||||
|
$keys = array_combine($keys, array_fill(0, (count($keys) - 1) ?: 1, $key));
|
||||||
|
|
||||||
|
if (!is_array($key)) {
|
||||||
|
$key = [
|
||||||
|
$storageName => $key,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$keys = array_intersect_assoc($keys, $key) ?: $keys;
|
||||||
|
|
||||||
|
return $this->marshaler->marshalItem($keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function insert($storageName, $key, array $data)
|
public function insert($storageName, $key, array $data)
|
||||||
{
|
{
|
||||||
$this->client->putItem([
|
$this->client->putItem([
|
||||||
self::TABLE_NAME_KEY => $storageName,
|
'TableName' => $storageName,
|
||||||
self::TABLE_ITEM_KEY => $this->prepareKey($storageName, $key) + $this->marshaler->marshalItem($this->prepareData($data)),
|
'Item' => $this->prepareKey($storageName, $key) + $this->marshaler->marshalItem($data),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function update($storageName, $key, array $data)
|
public function update($storageName, $key, array $data)
|
||||||
{
|
{
|
||||||
//We are using PUT so we just replace the original item, if the key
|
// we are using PUT so we just replace the original item, if the key
|
||||||
//does not exist, it will be created.
|
// does not exist, it will be created.
|
||||||
$this->insert($storageName, $key, $this->prepareData($data));
|
$this->insert($storageName, $key, $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function delete($storageName, $key)
|
public function delete($storageName, $key)
|
||||||
{
|
{
|
||||||
$this->client->deleteItem([
|
$this->client->deleteItem([
|
||||||
self::TABLE_NAME_KEY => $storageName,
|
'Key' => $this->prepareKey($storageName, $key),
|
||||||
self::TABLE_KEY => $this->prepareKey($storageName, $key),
|
'TableName' => $storageName,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function find($storageName, $key)
|
public function find($storageName, $key)
|
||||||
{
|
{
|
||||||
|
$keys = $this->prepareKey($storageName, $key);
|
||||||
|
|
||||||
$item = $this->client->getItem([
|
$item = $this->client->getItem([
|
||||||
self::TABLE_NAME_KEY => $storageName,
|
'ConsistentRead' => true,
|
||||||
self::CONSISTENT_READ_KEY => true,
|
'Key' => $keys,
|
||||||
self::TABLE_KEY => $this->prepareKey($storageName, $key),
|
'TableName' => $storageName,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (! $item) {
|
if (! $item->hasKey('Item')) {
|
||||||
throw NotFoundException::notFoundByKey($key);
|
throw NotFoundException::notFoundByKey($key);
|
||||||
}
|
}
|
||||||
|
|
||||||
$item = $item->get(self::TABLE_ITEM_KEY);
|
$item = $item->get('Item');
|
||||||
|
|
||||||
return $this->marshaler->unmarshalItem($item);
|
$result = $this->marshaler->unmarshalItem($item);
|
||||||
|
$result = array_diff_key($result, $keys);
|
||||||
|
|
||||||
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -298,25 +191,4 @@ class DynamoDbStorage implements Storage
|
|||||||
{
|
{
|
||||||
return 'dynamodb';
|
return 'dynamodb';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Prepare data by removing empty item attributes.
|
|
||||||
*
|
|
||||||
* @param array $data
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
protected function prepareData($data)
|
|
||||||
{
|
|
||||||
$callback = function ($value) {
|
|
||||||
return $value !== null && $value !== [] && $value !== '';
|
|
||||||
};
|
|
||||||
|
|
||||||
foreach ($data as &$value) {
|
|
||||||
if (is_array($value)) {
|
|
||||||
$value = $this->prepareData($value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return array_filter($data, $callback);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
namespace Doctrine\KeyValueStore\Storage;
|
namespace Doctrine\KeyValueStore\Storage;
|
||||||
|
|
||||||
use Doctrine\KeyValueStore\NotFoundException;
|
use Doctrine\KeyValueStore\NotFoundException;
|
||||||
|
use MongoDB\Database;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MongoDb storage
|
* MongoDb storage
|
||||||
@@ -30,57 +31,16 @@ use Doctrine\KeyValueStore\NotFoundException;
|
|||||||
class MongoDbStorage implements Storage
|
class MongoDbStorage implements Storage
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var \Mongo
|
* @var Database
|
||||||
*/
|
*/
|
||||||
protected $mongo;
|
private $database;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var array
|
* @param Database $database
|
||||||
*/
|
*/
|
||||||
protected $dbOptions;
|
public function __construct(Database $database)
|
||||||
|
|
||||||
/**
|
|
||||||
* @var \MongoCollection
|
|
||||||
*/
|
|
||||||
protected $collection;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
*
|
|
||||||
* @param \Mongo $mongo
|
|
||||||
* @param array $dbOptions
|
|
||||||
*/
|
|
||||||
public function __construct(\Mongo $mongo, array $dbOptions = [])
|
|
||||||
{
|
{
|
||||||
$this->mongo = $mongo;
|
$this->database = $database;
|
||||||
$this->dbOptions = array_merge([
|
|
||||||
'database' => '',
|
|
||||||
'collection' => '',
|
|
||||||
], $dbOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize the mongodb collection
|
|
||||||
*
|
|
||||||
* @throws \RuntimeException
|
|
||||||
*/
|
|
||||||
public function initialize()
|
|
||||||
{
|
|
||||||
if (null !== $this->collection) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (empty($this->dbOptions['database'])) {
|
|
||||||
throw new \RuntimeException('The option "database" must be set');
|
|
||||||
}
|
|
||||||
if (empty($this->dbOptions['collection'])) {
|
|
||||||
throw new \RuntimeException('The option "collection" must be set');
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->collection = $this
|
|
||||||
->mongo
|
|
||||||
->selectDB($this->dbOptions['database'])
|
|
||||||
->selectCollection($this->dbOptions['collection']);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -112,14 +72,12 @@ class MongoDbStorage implements Storage
|
|||||||
*/
|
*/
|
||||||
public function insert($storageName, $key, array $data)
|
public function insert($storageName, $key, array $data)
|
||||||
{
|
{
|
||||||
$this->initialize();
|
$this->database
|
||||||
|
->selectCollection($storageName)
|
||||||
$value = [
|
->insertOne([
|
||||||
'key' => $key,
|
'key' => $key,
|
||||||
'value' => $data,
|
'value' => $data,
|
||||||
];
|
]);
|
||||||
|
|
||||||
$this->collection->insert($value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -127,14 +85,14 @@ class MongoDbStorage implements Storage
|
|||||||
*/
|
*/
|
||||||
public function update($storageName, $key, array $data)
|
public function update($storageName, $key, array $data)
|
||||||
{
|
{
|
||||||
$this->initialize();
|
$this->database
|
||||||
|
->selectCollection($storageName)
|
||||||
$value = [
|
->replaceOne([
|
||||||
'key' => $key,
|
'key' => $key,
|
||||||
'value' => $data,
|
], [
|
||||||
];
|
'key' => $key,
|
||||||
|
'value' => $data,
|
||||||
$this->collection->update(['key' => $key], $value);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -142,9 +100,11 @@ class MongoDbStorage implements Storage
|
|||||||
*/
|
*/
|
||||||
public function delete($storageName, $key)
|
public function delete($storageName, $key)
|
||||||
{
|
{
|
||||||
$this->initialize();
|
$this->database
|
||||||
|
->selectCollection($storageName)
|
||||||
$this->collection->remove(['key' => $key]);
|
->deleteOne([
|
||||||
|
'key' => $key,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -152,15 +112,23 @@ class MongoDbStorage implements Storage
|
|||||||
*/
|
*/
|
||||||
public function find($storageName, $key)
|
public function find($storageName, $key)
|
||||||
{
|
{
|
||||||
$this->initialize();
|
$result = $this->database
|
||||||
|
->selectCollection($storageName, [
|
||||||
|
'typeMap' => [
|
||||||
|
'array' => 'array',
|
||||||
|
'document' => 'array',
|
||||||
|
'root' => 'array',
|
||||||
|
],
|
||||||
|
])
|
||||||
|
->findOne([
|
||||||
|
'key' => $key,
|
||||||
|
]);
|
||||||
|
|
||||||
$value = $this->collection->findOne(['key' => $key], ['value']);
|
if (! $result || ! $result['value']) {
|
||||||
|
throw new NotFoundException();
|
||||||
if ($value) {
|
|
||||||
return $value['value'];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new NotFoundException();
|
return $result['value'];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -21,7 +21,14 @@
|
|||||||
namespace Doctrine\KeyValueStore\Storage;
|
namespace Doctrine\KeyValueStore\Storage;
|
||||||
|
|
||||||
use Doctrine\KeyValueStore\NotFoundException;
|
use Doctrine\KeyValueStore\NotFoundException;
|
||||||
use Riak\Client;
|
use Riak\Client\Command\Kv\DeleteValue;
|
||||||
|
use Riak\Client\Command\Kv\FetchValue;
|
||||||
|
use Riak\Client\Command\Kv\StoreValue;
|
||||||
|
use Riak\Client\Core\Query\RiakLocation;
|
||||||
|
use Riak\Client\Core\Query\RiakNamespace;
|
||||||
|
use Riak\Client\Core\Query\RiakObject;
|
||||||
|
use Riak\Client\RiakClient;
|
||||||
|
use Riak\Client\RiakException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Markus Bachmann <markus.bachmann@bachi.biz>
|
* @author Markus Bachmann <markus.bachmann@bachi.biz>
|
||||||
@@ -29,17 +36,11 @@ use Riak\Client;
|
|||||||
class RiakStorage implements Storage
|
class RiakStorage implements Storage
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var \Riak\Client
|
* @var RiakClient
|
||||||
*/
|
*/
|
||||||
protected $client;
|
private $client;
|
||||||
|
|
||||||
/**
|
public function __construct(RiakClient $riak)
|
||||||
* Constructor
|
|
||||||
*
|
|
||||||
* @param \Riak\Client $riak
|
|
||||||
* @param string $bucketName
|
|
||||||
*/
|
|
||||||
public function __construct(Client $riak)
|
|
||||||
{
|
{
|
||||||
$this->client = $riak;
|
$this->client = $riak;
|
||||||
}
|
}
|
||||||
@@ -68,14 +69,25 @@ class RiakStorage implements Storage
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function store($storageName, $key, array $data)
|
||||||
|
{
|
||||||
|
$location = $this->getRiakLocation($storageName, $key);
|
||||||
|
|
||||||
|
$riakObject = new RiakObject();
|
||||||
|
$riakObject->setContentType('application/json');
|
||||||
|
$riakObject->setValue(json_encode($data));
|
||||||
|
|
||||||
|
$store = StoreValue::builder($location, $riakObject)->build();
|
||||||
|
|
||||||
|
$this->client->execute($store);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function insert($storageName, $key, array $data)
|
public function insert($storageName, $key, array $data)
|
||||||
{
|
{
|
||||||
$bucket = $this->client->bucket($storageName);
|
$this->store($storageName, $key, $data);
|
||||||
$object = $bucket->newObject($key, $data);
|
|
||||||
$object->store();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -83,12 +95,7 @@ class RiakStorage implements Storage
|
|||||||
*/
|
*/
|
||||||
public function update($storageName, $key, array $data)
|
public function update($storageName, $key, array $data)
|
||||||
{
|
{
|
||||||
$bucket = $this->client->bucket($storageName);
|
$this->store($storageName, $key, $data);
|
||||||
/** @var $object \Riak\Object */
|
|
||||||
$object = $bucket->get($key);
|
|
||||||
|
|
||||||
$object->setData($data);
|
|
||||||
$object->store();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -96,17 +103,15 @@ class RiakStorage implements Storage
|
|||||||
*/
|
*/
|
||||||
public function delete($storageName, $key)
|
public function delete($storageName, $key)
|
||||||
{
|
{
|
||||||
$bucket = $this->client->bucket($storageName);
|
$location = $this->getRiakLocation($storageName, $key);
|
||||||
|
|
||||||
/** @var $object \Riak\Object */
|
$delete = DeleteValue::builder($location)->build();
|
||||||
$object = $bucket->get($key);
|
|
||||||
|
|
||||||
if (! $object->exists()) {
|
try {
|
||||||
// object does not exist, do nothing
|
$this->client->execute($delete);
|
||||||
return;
|
} catch (RiakException $exception) {
|
||||||
|
// deletion can fail silent
|
||||||
}
|
}
|
||||||
|
|
||||||
$object->delete();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -114,16 +119,29 @@ class RiakStorage implements Storage
|
|||||||
*/
|
*/
|
||||||
public function find($storageName, $key)
|
public function find($storageName, $key)
|
||||||
{
|
{
|
||||||
$bucket = $this->client->bucket($storageName);
|
$location = $this->getRiakLocation($storageName, $key);
|
||||||
|
|
||||||
/** @var $object \Riak\Object */
|
// fetch object
|
||||||
$object = $bucket->get($key);
|
$fetch = FetchValue::builder($location)->build();
|
||||||
|
|
||||||
if (! $object->exists()) {
|
try {
|
||||||
throw new NotFoundException;
|
$result = $this->client->execute($fetch);
|
||||||
|
} catch (RiakException $exception) {
|
||||||
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
return $object->getData();
|
$json = (string) $result
|
||||||
|
->getValue()
|
||||||
|
->getValue();
|
||||||
|
|
||||||
|
return json_decode($json, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getRiakLocation($storageName, $key)
|
||||||
|
{
|
||||||
|
$namespace = new RiakNamespace('default', $storageName);
|
||||||
|
|
||||||
|
return new RiakLocation($namespace, $key);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -16,5 +16,7 @@
|
|||||||
<var name="DOCTRINE_KEYVALUE_AZURE_AUTHSCHEMA" value="sharedlite" />
|
<var name="DOCTRINE_KEYVALUE_AZURE_AUTHSCHEMA" value="sharedlite" />
|
||||||
<var name="DOCTRINE_KEYVALUE_AZURE_NAME" value="" />
|
<var name="DOCTRINE_KEYVALUE_AZURE_NAME" value="" />
|
||||||
<var name="DOCTRINE_KEYVALUE_AZURE_KEY" value="" />
|
<var name="DOCTRINE_KEYVALUE_AZURE_KEY" value="" />
|
||||||
|
<env name="RIAK_DNS" value="" />
|
||||||
|
<env name="DYNAMODB_DNS" value="" />
|
||||||
</php>
|
</php>
|
||||||
</phpunit>
|
</phpunit>
|
||||||
|
|||||||
@@ -0,0 +1,157 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* This software consists of voluntary contributions made by many individuals
|
||||||
|
* and is licensed under the MIT license. For more information, see
|
||||||
|
* <http://www.doctrine-project.org>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Doctrine\Tests\KeyValueStore\Storage;
|
||||||
|
|
||||||
|
use Doctrine\KeyValueStore\Storage\DynamoDbStorage;
|
||||||
|
use Aws\DynamoDb\DynamoDbClient;
|
||||||
|
use Doctrine\KeyValueStore\NotFoundException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \Doctrine\KeyValueStore\Storage\DynamoDbStorage
|
||||||
|
*/
|
||||||
|
class DynamoDbStorageTest extends \PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
const DATA = [
|
||||||
|
'author' => 'John Doe',
|
||||||
|
'title' => 'example book',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var DynamoDbClient|null
|
||||||
|
*/
|
||||||
|
private static $client;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var DynamoDbStorage
|
||||||
|
*/
|
||||||
|
private $storage;
|
||||||
|
|
||||||
|
public static function setUpBeforeClass()
|
||||||
|
{
|
||||||
|
$dns = getenv('DYNAMODB_DNS');
|
||||||
|
|
||||||
|
if (empty($dns)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static::$client = DynamoDbClient::factory(array(
|
||||||
|
'credentials' => [
|
||||||
|
'key' => 'YOUR_KEY',
|
||||||
|
'secret' => 'YOUR_SECRET',
|
||||||
|
],
|
||||||
|
'region' => 'us-west-2',
|
||||||
|
'endpoint' => $dns,
|
||||||
|
'version' => 'latest',
|
||||||
|
'retries' => 1,
|
||||||
|
));
|
||||||
|
|
||||||
|
try {
|
||||||
|
static::$client->deleteTable([
|
||||||
|
'TableName' => 'dynamodb',
|
||||||
|
]);
|
||||||
|
} catch (\Exception $exception) {
|
||||||
|
// table does not exist
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
static::$client->createTable(array(
|
||||||
|
'TableName' => 'dynamodb',
|
||||||
|
'AttributeDefinitions' => array(
|
||||||
|
array(
|
||||||
|
'AttributeName' => 'id',
|
||||||
|
'AttributeType' => 'S',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'KeySchema' => array(
|
||||||
|
array(
|
||||||
|
'AttributeName' => 'id',
|
||||||
|
'KeyType' => 'HASH',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'ProvisionedThroughput' => array(
|
||||||
|
'ReadCapacityUnits' => 10,
|
||||||
|
'WriteCapacityUnits' => 20,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
} catch (\Exception $exception) {
|
||||||
|
static::$client = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function setUp()
|
||||||
|
{
|
||||||
|
if (! static::$client) {
|
||||||
|
$this->markTestSkipped('DynamoDB is required.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->storage = new DynamoDbStorage(static::$client);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testInsertAndFind()
|
||||||
|
{
|
||||||
|
$this->storage->insert('dynamodb', 'testInsertAndFind', self::DATA);
|
||||||
|
|
||||||
|
$data = $this->storage->find('dynamodb', 'testInsertAndFind');
|
||||||
|
|
||||||
|
$this->assertEquals(self::DATA, $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUpdate()
|
||||||
|
{
|
||||||
|
$this->storage->insert('dynamodb', 'testUpdate', self::DATA);
|
||||||
|
|
||||||
|
$newData = [
|
||||||
|
'foo' => 'bar',
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->storage->update('dynamodb', 'testUpdate', $newData);
|
||||||
|
|
||||||
|
$data = $this->storage->find('dynamodb', 'testUpdate');
|
||||||
|
$this->assertEquals($newData, $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @depends testInsertAndFind
|
||||||
|
*/
|
||||||
|
public function testFindWithNotExistKey()
|
||||||
|
{
|
||||||
|
$this->setExpectedException(NotFoundException::class);
|
||||||
|
$this->storage->find('dynamodb', 'not-existing-key');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @depends testInsertAndFind
|
||||||
|
* @depends testFindWithNotExistKey
|
||||||
|
*/
|
||||||
|
public function testDelete()
|
||||||
|
{
|
||||||
|
$this->storage->insert('dynamodb', 'testDelete', self::DATA);
|
||||||
|
$this->storage->delete('dynamodb', 'testDelete');
|
||||||
|
|
||||||
|
$this->setExpectedException(NotFoundException::class);
|
||||||
|
$this->storage->find('dynamodb', 'testDelete');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetName()
|
||||||
|
{
|
||||||
|
$this->assertEquals('dynamodb', $this->storage->getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,346 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*
|
|
||||||
* This software consists of voluntary contributions made by many individuals
|
|
||||||
* and is licensed under the MIT license. For more information, see
|
|
||||||
* <http://www.doctrine-project.org>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Doctrine\Tests\KeyValueStore\Storage;
|
|
||||||
|
|
||||||
use Doctrine\KeyValueStore\Storage\DynamoDbStorage;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @covers \Doctrine\KeyValueStore\Storage\DynamoDbStorage
|
|
||||||
*/
|
|
||||||
class DynamoDbTest extends \PHPUnit_Framework_TestCase
|
|
||||||
{
|
|
||||||
private function getDynamoDbMock($methods = [])
|
|
||||||
{
|
|
||||||
$client = $this->getMockBuilder('Aws\DynamoDb\DynamoDbClient')->disableOriginalConstructor();
|
|
||||||
|
|
||||||
if (count($methods)) {
|
|
||||||
$client->setMethods($methods);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $client->getMock();
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getDynamoDbResultMock($methods = [])
|
|
||||||
{
|
|
||||||
$result = $this->getMockBuilder('Aws\Result')->disableOriginalConstructor();
|
|
||||||
|
|
||||||
if (count($methods)) {
|
|
||||||
$result->setMethods($methods);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $result->getMock();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testTheStorageName()
|
|
||||||
{
|
|
||||||
$client = $this->getDynamoDbMock();
|
|
||||||
|
|
||||||
$storage = new DynamoDbStorage($client);
|
|
||||||
$this->assertSame('dynamodb', $storage->getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testDefaultKeyName()
|
|
||||||
{
|
|
||||||
$client = $this->getDynamoDbMock();
|
|
||||||
$storage = new DynamoDbStorage($client);
|
|
||||||
$this->assertAttributeSame('Id', 'defaultKeyName', $storage);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testThatTableKeysInitiallyEmpty()
|
|
||||||
{
|
|
||||||
$client = $this->getDynamoDbMock();
|
|
||||||
$storage = new DynamoDbStorage($client);
|
|
||||||
$this->assertAttributeSame([], 'tableKeys', $storage);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testDefaultKeyCannotBeSomethingOtherThanString()
|
|
||||||
{
|
|
||||||
$client = $this->getDynamoDbMock();
|
|
||||||
$this->setExpectedException(
|
|
||||||
'\Doctrine\KeyValueStore\KeyValueStoreException',
|
|
||||||
'The key must be a string, got "array" instead.'
|
|
||||||
);
|
|
||||||
new DynamoDbStorage($client, null, []);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testTableKeysMustAllBeStringsOrElse()
|
|
||||||
{
|
|
||||||
$client = $this->getDynamoDbMock();
|
|
||||||
$this->setExpectedException(
|
|
||||||
'\Doctrine\KeyValueStore\KeyValueStoreException',
|
|
||||||
'The key must be a string, got "object" instead.'
|
|
||||||
);
|
|
||||||
new DynamoDbStorage($client, null, null, ['mytable' => 'hello', 'yourtable' => new \stdClass()]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testKeyNameMustBeUnder255Bytes()
|
|
||||||
{
|
|
||||||
$client = $this->getDynamoDbMock();
|
|
||||||
$this->setExpectedException(
|
|
||||||
'\Doctrine\KeyValueStore\KeyValueStoreException',
|
|
||||||
'The name must be at least 1 but no more than 255 chars.'
|
|
||||||
);
|
|
||||||
new DynamoDbStorage($client, null, str_repeat('a', 256));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function invalidTableNames()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
['a2'],
|
|
||||||
['yo%'],
|
|
||||||
['что'],
|
|
||||||
['h@llo'],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function validTableNames()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
['MyTable'],
|
|
||||||
['This_is0k-...'],
|
|
||||||
['hello_world'],
|
|
||||||
['...........00....'],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
private function invokeMethod($methodName, $obj, array $args = null)
|
|
||||||
{
|
|
||||||
$relf = new \ReflectionObject($obj);
|
|
||||||
$method = $relf->getMethod($methodName);
|
|
||||||
$method->setAccessible(true);
|
|
||||||
|
|
||||||
if ($args) {
|
|
||||||
return $method->invokeArgs($obj, $args);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $method->invoke($obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testTableNameMustBeAString()
|
|
||||||
{
|
|
||||||
$client = $this->getDynamoDbMock();
|
|
||||||
$storage = new DynamoDbStorage($client);
|
|
||||||
$this->setExpectedException('\Doctrine\KeyValueStore\InvalidArgumentException');
|
|
||||||
$this->invokeMethod('setKeyForTable', $storage, [[], 'Id']);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dataProvider invalidTableNames
|
|
||||||
*/
|
|
||||||
public function testTableNameValidatesAgainstInvalidTableNames($tableName)
|
|
||||||
{
|
|
||||||
$client = $this->getDynamoDbMock();
|
|
||||||
$storage = new DynamoDbStorage($client);
|
|
||||||
$this->setExpectedException('\Doctrine\KeyValueStore\KeyValueStoreException');
|
|
||||||
$this->invokeMethod('setKeyForTable', $storage, [$tableName, 'Id']);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dataProvider validTableNames
|
|
||||||
*/
|
|
||||||
public function testTableNameValidatesAgainstValidTableNames($tableName)
|
|
||||||
{
|
|
||||||
$client = $this->getDynamoDbMock();
|
|
||||||
$storage = new DynamoDbStorage($client);
|
|
||||||
$this->invokeMethod('setKeyForTable', $storage, [$tableName, 'Id']);
|
|
||||||
|
|
||||||
$this->assertAttributeSame([$tableName => 'Id'], 'tableKeys', $storage);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testThatYouCanHaveMultipleTablesWithOverrides()
|
|
||||||
{
|
|
||||||
$client = $this->getDynamoDbMock();
|
|
||||||
$storage = new DynamoDbStorage($client);
|
|
||||||
$this->invokeMethod('setKeyForTable', $storage, ['Aaa', '2']);
|
|
||||||
$this->invokeMethod('setKeyForTable', $storage, ['Bbb', '1']);
|
|
||||||
|
|
||||||
$this->assertAttributeSame(['Aaa' => '2', 'Bbb' => '1'], 'tableKeys', $storage);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testGetterForDefaultKeyName()
|
|
||||||
{
|
|
||||||
$client = $this->getDynamoDbMock();
|
|
||||||
$storage = new DynamoDbStorage($client, null, 'CustomKey');
|
|
||||||
$this->assertSame('CustomKey', $storage->getDefaultKeyName());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testGetWillReturnDefaultKeyForUnrecognizedTableName()
|
|
||||||
{
|
|
||||||
$client = $this->getDynamoDbMock();
|
|
||||||
$storage = new DynamoDbStorage($client, null, 'CustomKey');
|
|
||||||
$this->assertSame('CustomKey', $this->invokeMethod('getKeyNameForTable', $storage, ['whatever_this_is']));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testGetWillReturnCorrectKeyForRecognizedTableName()
|
|
||||||
{
|
|
||||||
$client = $this->getDynamoDbMock();
|
|
||||||
$storage = new DynamoDbStorage($client, null, 'CustomKey', ['MyTable' => 'Yesss']);
|
|
||||||
$this->assertSame('Yesss', $this->invokeMethod('getKeyNameForTable', $storage, ['MyTable']));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testThatSomeStorageHasDifferentKey()
|
|
||||||
{
|
|
||||||
$client = $this->getDynamoDbMock();
|
|
||||||
|
|
||||||
$storage = new DynamoDbStorage($client, null, 'sauce', ['this' => 'that', 'yolo' => 'now']);
|
|
||||||
|
|
||||||
$this->assertSame(['that' => ['N' => '111']], $this->invokeMethod('prepareKey', $storage, ['this', 111]));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testThatSomeStorageUsesDefaultKey()
|
|
||||||
{
|
|
||||||
$client = $this->getDynamoDbMock();
|
|
||||||
|
|
||||||
$storage = new DynamoDbStorage($client, null, 'sauce', ['this' => 'that', 'yolo' => 'now']);
|
|
||||||
|
|
||||||
$this->assertSame(['sauce' => ['S' => 'hello']], $this->invokeMethod('prepareKey', $storage, ['MyTable', 'hello']));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testInsertingCallsAPutItem()
|
|
||||||
{
|
|
||||||
$client = $this->getDynamoDbMock(['putItem']);
|
|
||||||
|
|
||||||
$client->expects($this->once())->method('putItem')->with($this->equalTo([
|
|
||||||
'TableName' => 'MyTable',
|
|
||||||
'Item' => [
|
|
||||||
'Id' => ['S' => 'stuff'],
|
|
||||||
'hi' => ['S' => 'there'],
|
|
||||||
'yo' => ['BOOL' => false],
|
|
||||||
],
|
|
||||||
]));
|
|
||||||
|
|
||||||
$storage = new DynamoDbStorage($client);
|
|
||||||
$storage->insert('MyTable', 'stuff', ['hi' => 'there', 'yo' => false]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testInsertingPreparesNestedAttributes()
|
|
||||||
{
|
|
||||||
$client = $this->getDynamoDbMock(['putItem']);
|
|
||||||
|
|
||||||
$client->expects($this->once())->method('putItem')->with($this->equalTo([
|
|
||||||
'TableName' => 'MyTable',
|
|
||||||
'Item' => [
|
|
||||||
'Id' => ['S' => 'stuff'],
|
|
||||||
'hi' => ['S' => 'there'],
|
|
||||||
'what' => ['L' => [
|
|
||||||
['S' => 'Yep'],
|
|
||||||
]],
|
|
||||||
'yo' => ['BOOL' => false],
|
|
||||||
],
|
|
||||||
]));
|
|
||||||
|
|
||||||
$storage = new DynamoDbStorage($client);
|
|
||||||
$storage->insert('MyTable', 'stuff', ['hi' => 'there', 'yo' => false, 'what' => ['Yep', '']]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testUpdateActuallyAlsoCallsInsert()
|
|
||||||
{
|
|
||||||
$client = $this->getDynamoDbMock(['putItem']);
|
|
||||||
|
|
||||||
$client->expects($this->once())->method('putItem')->with($this->equalTo([
|
|
||||||
'TableName' => 'MyTable',
|
|
||||||
'Item' => [
|
|
||||||
'Id' => ['S' => 'stuff'],
|
|
||||||
'hi' => ['S' => 'there'],
|
|
||||||
'yo' => ['BOOL' => false],
|
|
||||||
],
|
|
||||||
]));
|
|
||||||
|
|
||||||
$storage = new DynamoDbStorage($client);
|
|
||||||
$storage->update('MyTable', 'stuff', ['hi' => 'there', 'yo' => false]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testDeleteItem()
|
|
||||||
{
|
|
||||||
$client = $this->getDynamoDbMock(['deleteItem']);
|
|
||||||
|
|
||||||
$client->expects($this->once())->method('deleteItem')->with($this->equalTo([
|
|
||||||
'TableName' => 'MyTable',
|
|
||||||
'Key' => ['Id' => ['S' => 'abc123']],
|
|
||||||
]));
|
|
||||||
|
|
||||||
$storage = new DynamoDbStorage($client);
|
|
||||||
$storage->delete('MyTable', 'abc123');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testDeleteItemWithKeyValuePair()
|
|
||||||
{
|
|
||||||
$client = $this->getDynamoDbMock(['deleteItem']);
|
|
||||||
|
|
||||||
$client->expects($this->once())->method('deleteItem')->with($this->equalTo([
|
|
||||||
'TableName' => 'MyTable',
|
|
||||||
'Key' => ['Id' => ['S' => 'abc123']],
|
|
||||||
]));
|
|
||||||
|
|
||||||
$storage = new DynamoDbStorage($client);
|
|
||||||
$storage->delete('MyTable', ['Id' => 'abc123']);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testPassingArrayAsKeyIsAPassthruToInsert()
|
|
||||||
{
|
|
||||||
$client = $this->getDynamoDbMock(['deleteItem']);
|
|
||||||
|
|
||||||
$client->expects($this->once())->method('deleteItem')->with($this->equalTo([
|
|
||||||
'TableName' => 'MyTable',
|
|
||||||
'Key' => ['Id' => ['S' => 'abc123']],
|
|
||||||
]));
|
|
||||||
|
|
||||||
$storage = new DynamoDbStorage($client);
|
|
||||||
$storage->delete('MyTable', 'abc123');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testTryingToFindAnItemThatDoesNotExist()
|
|
||||||
{
|
|
||||||
$client = $this->getDynamoDbMock(['getItem']);
|
|
||||||
$client->expects($this->once())->method('getItem')->with($this->equalTo([
|
|
||||||
'TableName' => 'MyTable',
|
|
||||||
'ConsistentRead' => true,
|
|
||||||
'Key' => ['Id' => ['N' => '1000']],
|
|
||||||
]))->willReturn(null);
|
|
||||||
|
|
||||||
$storage = new DynamoDbStorage($client);
|
|
||||||
$this->setExpectedException(
|
|
||||||
'\Doctrine\KeyValueStore\NotFoundException',
|
|
||||||
'Could not find an item with key: 1000'
|
|
||||||
);
|
|
||||||
$storage->find('MyTable', 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testFindAnItemThatExists()
|
|
||||||
{
|
|
||||||
$result = $this->getDynamoDbResultMock(['get']);
|
|
||||||
$result->expects($this->once())->method('get')->with('Item')->willReturn([
|
|
||||||
'hello' => ['S' => 'world'],
|
|
||||||
]);
|
|
||||||
|
|
||||||
$client = $this->getDynamoDbMock(['getItem']);
|
|
||||||
$client->expects($this->once())->method('getItem')->with($this->equalTo([
|
|
||||||
'TableName' => 'MyTable',
|
|
||||||
'ConsistentRead' => true,
|
|
||||||
'Key' => ['Id' => ['N' => '1000']],
|
|
||||||
]))->willReturn($result);
|
|
||||||
|
|
||||||
$storage = new DynamoDbStorage($client);
|
|
||||||
$actualResult = $storage->find('MyTable', 1000);
|
|
||||||
|
|
||||||
$this->assertSame(['hello' => 'world'], $actualResult);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -20,40 +20,34 @@
|
|||||||
|
|
||||||
namespace Doctrine\Tests\KeyValueStore\Storage;
|
namespace Doctrine\Tests\KeyValueStore\Storage;
|
||||||
|
|
||||||
|
use Doctrine\KeyValueStore\NotFoundException;
|
||||||
use Doctrine\KeyValueStore\Storage\MongoDbStorage;
|
use Doctrine\KeyValueStore\Storage\MongoDbStorage;
|
||||||
|
use MongoDB\Client;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MongoDb storage testcase
|
* MongoDb storage testcase
|
||||||
*
|
*
|
||||||
* @author Markus Bachmann <markus.bachmann@bachi.biz>
|
* @author Markus Bachmann <markus.bachmann@bachi.biz>
|
||||||
*
|
*
|
||||||
* @requires extension mongo
|
* @covers \Doctrine\KeyValueStore\Storage\MongoDbStorage
|
||||||
|
* @requires extension mongodb
|
||||||
*/
|
*/
|
||||||
class MongoDbStorageTest extends \PHPUnit_Framework_TestCase
|
class MongoDbStorageTest extends \PHPUnit_Framework_TestCase
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @var Client
|
||||||
|
*/
|
||||||
|
private $client;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var MongoDbStorage
|
||||||
|
*/
|
||||||
|
private $storage;
|
||||||
|
|
||||||
protected function setUp()
|
protected function setUp()
|
||||||
{
|
{
|
||||||
$this->mongo = $this
|
$this->client = new Client();
|
||||||
->getMockBuilder('\Mongo')
|
$this->storage = new MongoDbStorage($this->client->test);
|
||||||
->disableOriginalConstructor()
|
|
||||||
->getMock();
|
|
||||||
|
|
||||||
$this->mongodb = $this->getMockBuilder('\MongoDB')->disableOriginalConstructor()->getMock();
|
|
||||||
|
|
||||||
$this->mongo->expects($this->any())
|
|
||||||
->method('selectDB')
|
|
||||||
->will($this->returnValue($this->mongodb));
|
|
||||||
|
|
||||||
$this->collection = $this->getMockBuilder('MongoCollection')->disableOriginalConstructor()->getMock();
|
|
||||||
|
|
||||||
$this->mongodb->expects($this->once())
|
|
||||||
->method('selectCollection')
|
|
||||||
->will($this->returnValue($this->collection));
|
|
||||||
|
|
||||||
$this->storage = new MongoDbStorage($this->mongo, [
|
|
||||||
'collection' => 'test',
|
|
||||||
'database' => 'test',
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testInsert()
|
public function testInsert()
|
||||||
@@ -63,20 +57,21 @@ class MongoDbStorageTest extends \PHPUnit_Framework_TestCase
|
|||||||
'title' => 'example book',
|
'title' => 'example book',
|
||||||
];
|
];
|
||||||
|
|
||||||
$dbDataset = [];
|
$this->storage->insert('mongodb', 'testInsert', $data);
|
||||||
|
|
||||||
$this->collection->expects($this->once())
|
$result = $this->client
|
||||||
->method('insert')
|
->test
|
||||||
->will($this->returnCallback(function ($data) use (&$dbDataset) {
|
->mongodb
|
||||||
$dbDataset[] = $data;
|
->findOne([
|
||||||
}));
|
'key' => 'testInsert',
|
||||||
|
]);
|
||||||
|
|
||||||
$this->storage->insert('mongodb', '1', $data);
|
$this->assertSame($data, $result['value']->getArrayCopy());
|
||||||
$this->assertCount(1, $dbDataset);
|
|
||||||
|
|
||||||
$this->assertEquals([['key' => '1', 'value' => $data]], $dbDataset);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @depends testInsert
|
||||||
|
*/
|
||||||
public function testUpdate()
|
public function testUpdate()
|
||||||
{
|
{
|
||||||
$data = [
|
$data = [
|
||||||
@@ -84,80 +79,67 @@ class MongoDbStorageTest extends \PHPUnit_Framework_TestCase
|
|||||||
'title' => 'example book',
|
'title' => 'example book',
|
||||||
];
|
];
|
||||||
|
|
||||||
$dbDataset = [];
|
$this->storage->insert('mongodb', 'testUpdate', [
|
||||||
|
'foo' => 'bar',
|
||||||
|
]);
|
||||||
|
$this->storage->update('mongodb', 'testUpdate', $data);
|
||||||
|
|
||||||
$this->collection->expects($this->once())
|
$result = $this->client
|
||||||
->method('update')
|
->test
|
||||||
->will($this->returnCallback(function ($citeria, $data) use (&$dbDataset) {
|
->mongodb
|
||||||
$dbDataset = [$citeria, $data];
|
->findOne([
|
||||||
}));
|
'key' => 'testUpdate',
|
||||||
|
]);
|
||||||
|
|
||||||
$this->storage->update('mongodb', '1', $data);
|
$this->assertSame($data, $result['value']->getArrayCopy());
|
||||||
|
|
||||||
$this->assertEquals(['key' => '1'], $dbDataset[0]);
|
|
||||||
$this->assertEquals(['key' => '1', 'value' => $data], $dbDataset[1]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @depends testInsert
|
||||||
|
*/
|
||||||
public function testDelete()
|
public function testDelete()
|
||||||
{
|
{
|
||||||
$dataset = [
|
$this->storage->insert('mongodb', 'testDelete', [
|
||||||
[
|
'foo' => 'bar',
|
||||||
'key' => 'foobar',
|
]);
|
||||||
'value' => [
|
|
||||||
'author' => 'John Doe',
|
|
||||||
'title' => 'example book',
|
|
||||||
],
|
|
||||||
],
|
|
||||||
];
|
|
||||||
|
|
||||||
$this->collection->expects($this->once())
|
$this->storage->delete('mongodb', 'testDelete');
|
||||||
->method('remove')
|
|
||||||
->will($this->returnCallback(function ($citeria) use (&$dataset) {
|
|
||||||
foreach ($dataset as $key => $row) {
|
|
||||||
if ($row['key'] === $citeria['key']) {
|
|
||||||
unset($dataset[$key]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
));
|
|
||||||
|
|
||||||
$this->storage->delete('test', 'foobar');
|
$result = $this->client
|
||||||
|
->test
|
||||||
|
->mongodb
|
||||||
|
->findOne([
|
||||||
|
'key' => 'testDelete',
|
||||||
|
]);
|
||||||
|
|
||||||
$this->assertCount(0, $dataset);
|
$this->assertNull($result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @depends testInsert
|
||||||
|
*/
|
||||||
public function testFind()
|
public function testFind()
|
||||||
{
|
{
|
||||||
$dataset = [
|
$dataset = [
|
||||||
[
|
'author' => 'John Doe',
|
||||||
'key' => 'foobar',
|
'title' => 'example book',
|
||||||
'value' => [
|
|
||||||
'author' => 'John Doe',
|
|
||||||
'title' => 'example book',
|
|
||||||
],
|
|
||||||
],
|
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->collection->expects($this->once())
|
$this->storage->insert('mongodb', 'testFind', $dataset);
|
||||||
->method('findOne')
|
|
||||||
->will($this->returnCallback(function ($citeria, $fields) use (&$dataset) {
|
|
||||||
foreach ($dataset as $key => $row) {
|
|
||||||
if ($row['key'] === $citeria['key']) {
|
|
||||||
return $row;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
));
|
|
||||||
|
|
||||||
$data = $this->storage->find('test', 'foobar');
|
$data = $this->storage->find('mongodb', 'testFind');
|
||||||
|
|
||||||
$this->assertEquals($dataset[0]['value'], $data);
|
$this->assertEquals($dataset, $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFindWithNotExistKey()
|
||||||
|
{
|
||||||
|
$this->setExpectedException(NotFoundException::class);
|
||||||
|
$this->storage->find('mongodb', 'not-existing-key');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetName()
|
public function testGetName()
|
||||||
{
|
{
|
||||||
$this->storage->initialize();
|
|
||||||
|
|
||||||
$this->assertEquals('mongodb', $this->storage->getName());
|
$this->assertEquals('mongodb', $this->storage->getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,34 +20,46 @@
|
|||||||
|
|
||||||
namespace Doctrine\Tests\KeyValueStore\Storage;
|
namespace Doctrine\Tests\KeyValueStore\Storage;
|
||||||
|
|
||||||
|
use Doctrine\KeyValueStore\NotFoundException;
|
||||||
use Doctrine\KeyValueStore\Storage\RiakStorage;
|
use Doctrine\KeyValueStore\Storage\RiakStorage;
|
||||||
|
use PHPUnit_Framework_TestCase;
|
||||||
|
use Riak\Client\Command\Kv\Builder\ListKeysBuilder;
|
||||||
|
use Riak\Client\Command\Kv\FetchValue;
|
||||||
|
use Riak\Client\Core\Query\RiakLocation;
|
||||||
|
use Riak\Client\Core\Query\RiakNamespace;
|
||||||
|
use Riak\Client\Core\Query\RiakObject;
|
||||||
|
use Riak\Client\Core\Transport\RiakTransportException;
|
||||||
|
use Riak\Client\RiakClient;
|
||||||
|
use Riak\Client\RiakClientBuilder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Markus Bachmann <markus.bachmann@bachi.biz>
|
* @author Markus Bachmann <markus.bachmann@bachi.biz>
|
||||||
*/
|
*/
|
||||||
class RiakStorageTest extends \PHPUnit_Framework_TestCase
|
class RiakStorageTest extends PHPUnit_Framework_TestCase
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @var RiakClient
|
||||||
|
*/
|
||||||
|
private $client;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var RiakStorage
|
* @var RiakStorage
|
||||||
*/
|
*/
|
||||||
private $storage;
|
private $storage;
|
||||||
|
|
||||||
/**
|
protected function setUp()
|
||||||
* @var \PHPUnit_Framework_MockObject_MockObject
|
|
||||||
*/
|
|
||||||
private $riak;
|
|
||||||
|
|
||||||
protected function setup()
|
|
||||||
{
|
{
|
||||||
if (PHP_MAJOR_VERSION >= 7) {
|
$dns = getenv('RIAK_DNS');
|
||||||
$this->markTestSkipped('Riak extension is not available for PHP versions >= 7');
|
|
||||||
|
if (empty($dns)) {
|
||||||
|
$this->markTestSkipped('Missing Riak DNS');
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->riak = $this->getMockBuilder('Riak\\Client')
|
$this->client = (new RiakClientBuilder())
|
||||||
->disableOriginalConstructor()
|
->withNodeUri($dns)
|
||||||
->getMock();
|
->build();
|
||||||
|
|
||||||
$this->storage = new RiakStorage($this->riak);
|
$this->storage = new RiakStorage($this->client);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSupportsPartialUpdates()
|
public function testSupportsPartialUpdates()
|
||||||
@@ -67,186 +79,124 @@ class RiakStorageTest extends \PHPUnit_Framework_TestCase
|
|||||||
|
|
||||||
public function testInsert()
|
public function testInsert()
|
||||||
{
|
{
|
||||||
$bucket = $this->getMockBuilder('Riak\Bucket')
|
$data = [
|
||||||
->disableOriginalConstructor()
|
'title' => 'Riak test',
|
||||||
->getMock();
|
];
|
||||||
|
|
||||||
$this->riak->expects($this->once())
|
$this->storage->insert('riak-test', 'foobar', $data);
|
||||||
->method('bucket')
|
|
||||||
->will($this->returnValue($bucket));
|
|
||||||
|
|
||||||
$objectMock = $this->getMockBuilder('Riak\Object')
|
$location = $this->getRiakLocation();
|
||||||
->disableOriginalConstructor()
|
|
||||||
->getMock();
|
|
||||||
|
|
||||||
$objectMock->expects($this->once())
|
$fetch = FetchValue::builder($location)->build();
|
||||||
->method('store');
|
|
||||||
|
|
||||||
$that = $this;
|
$json = (string) $this->client
|
||||||
$bucket->expects($this->once())
|
->execute($fetch)
|
||||||
->method('newObject')
|
->getValue()
|
||||||
->will($this->returnCallback(function ($key, $data) use ($objectMock, $that) {
|
->getValue();
|
||||||
$that->assertEquals('foobar', $key);
|
|
||||||
$that->assertEquals(['title' => 'Riak test'], $data);
|
|
||||||
return $objectMock;
|
|
||||||
}));
|
|
||||||
|
|
||||||
$this->storage->insert('riak-test', 'foobar', ['title' => 'Riak test']);
|
$this->assertSame($data, json_decode($json, true));
|
||||||
}
|
|
||||||
|
|
||||||
public function testUpdate()
|
|
||||||
{
|
|
||||||
$objectMock = $this->getMockBuilder('Riak\Object')
|
|
||||||
->disableOriginalConstructor()
|
|
||||||
->getMock();
|
|
||||||
|
|
||||||
$bucket = $this->getMockBuilder('Riak\Bucket')
|
|
||||||
->disableOriginalConstructor()
|
|
||||||
->getMock();
|
|
||||||
|
|
||||||
$this->riak->expects($this->once())
|
|
||||||
->method('bucket')
|
|
||||||
->will($this->returnValue($bucket));
|
|
||||||
|
|
||||||
$bucket->expects($this->once())
|
|
||||||
->method('get')
|
|
||||||
->will($this->returnValue($objectMock));
|
|
||||||
|
|
||||||
$that = $this;
|
|
||||||
$objectMock->expects($this->once())
|
|
||||||
->method('setData')
|
|
||||||
->will($this->returnCallback(function ($data) use ($that) {
|
|
||||||
$that->assertEquals(['title' => 'Riak cookbook'], $data);
|
|
||||||
}));
|
|
||||||
|
|
||||||
$objectMock->expects($this->once())
|
|
||||||
->method('store');
|
|
||||||
|
|
||||||
$this->storage->update('riak-test', 'foobar', ['title' => 'Riak cookbook']);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testDelete()
|
|
||||||
{
|
|
||||||
$objectMock = $this->getMockBuilder('Riak\Object')
|
|
||||||
->disableOriginalConstructor()
|
|
||||||
->getMock();
|
|
||||||
|
|
||||||
$bucket = $this->getMockBuilder('Riak\Bucket')
|
|
||||||
->disableOriginalConstructor()
|
|
||||||
->getMock();
|
|
||||||
|
|
||||||
$this->riak->expects($this->once())
|
|
||||||
->method('bucket')
|
|
||||||
->will($this->returnValue($bucket));
|
|
||||||
|
|
||||||
$bucket->expects($this->once())
|
|
||||||
->method('get')
|
|
||||||
->with('foobar')
|
|
||||||
->will($this->returnValue($objectMock));
|
|
||||||
|
|
||||||
$objectMock->expects($this->once())
|
|
||||||
->method('exists')
|
|
||||||
->will($this->returnValue(true));
|
|
||||||
|
|
||||||
$objectMock->expects($this->once())
|
|
||||||
->method('delete');
|
|
||||||
|
|
||||||
$this->storage->delete('riak-test', 'foobar');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testDeleteWithNotExistKey()
|
|
||||||
{
|
|
||||||
$objectMock = $this->getMockBuilder('Riak\Object')
|
|
||||||
->disableOriginalConstructor()
|
|
||||||
->getMock();
|
|
||||||
|
|
||||||
$bucket = $this->getMockBuilder('Riak\Bucket')
|
|
||||||
->disableOriginalConstructor()
|
|
||||||
->getMock();
|
|
||||||
|
|
||||||
$this->riak->expects($this->once())
|
|
||||||
->method('bucket')
|
|
||||||
->will($this->returnValue($bucket));
|
|
||||||
|
|
||||||
$bucket->expects($this->once())
|
|
||||||
->method('get')
|
|
||||||
->with('foobar')
|
|
||||||
->will($this->returnValue($objectMock));
|
|
||||||
|
|
||||||
$objectMock->expects($this->once())
|
|
||||||
->method('exists')
|
|
||||||
->will($this->returnValue(false));
|
|
||||||
|
|
||||||
$objectMock->expects($this->never())
|
|
||||||
->method('delete');
|
|
||||||
|
|
||||||
$this->storage->delete('riak-test', 'foobar');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testFind()
|
|
||||||
{
|
|
||||||
$objectMock = $this->getMockBuilder('Riak\Object')
|
|
||||||
->disableOriginalConstructor()
|
|
||||||
->getMock();
|
|
||||||
|
|
||||||
$bucket = $this->getMockBuilder('Riak\Bucket')
|
|
||||||
->disableOriginalConstructor()
|
|
||||||
->getMock();
|
|
||||||
|
|
||||||
$this->riak->expects($this->once())
|
|
||||||
->method('bucket')
|
|
||||||
->will($this->returnValue($bucket));
|
|
||||||
|
|
||||||
$bucket->expects($this->once())
|
|
||||||
->method('get')
|
|
||||||
->with('foobar')
|
|
||||||
->will($this->returnValue($objectMock));
|
|
||||||
|
|
||||||
$objectMock->expects($this->once())
|
|
||||||
->method('exists')
|
|
||||||
->will($this->returnValue(true));
|
|
||||||
|
|
||||||
$objectMock->expects($this->once())
|
|
||||||
->method('getData')
|
|
||||||
->will($this->returnValue(['title' => 'Riak Test']));
|
|
||||||
|
|
||||||
$this->assertEquals(['title' => 'Riak Test'], $this->storage->find('riaktest', 'foobar'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @expectedException Doctrine\KeyValueStore\NotFoundException
|
* @depends testInsert
|
||||||
*/
|
*/
|
||||||
|
public function testUpdate()
|
||||||
|
{
|
||||||
|
$data = [
|
||||||
|
'title' => 'Riak update',
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->storage->insert('riak-test', 'foobar', [
|
||||||
|
'title' => 'Riak insert',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$location = $this->getRiakLocation();
|
||||||
|
|
||||||
|
$this->assertTotalBucketKeys(1, $location);
|
||||||
|
|
||||||
|
$this->storage->update('riak-test', 'foobar', $data);
|
||||||
|
|
||||||
|
$fetch = FetchValue::builder($location)->build();
|
||||||
|
|
||||||
|
$json = (string) $this->client
|
||||||
|
->execute($fetch)
|
||||||
|
->getValue()
|
||||||
|
->getValue();
|
||||||
|
|
||||||
|
$this->assertSame($data, json_decode($json, true));
|
||||||
|
$this->assertTotalBucketKeys(1, $location);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @depends testInsert
|
||||||
|
*/
|
||||||
|
public function testDelete()
|
||||||
|
{
|
||||||
|
$this->testInsert();
|
||||||
|
|
||||||
|
$this->storage->delete('riak-test', 'foobar');
|
||||||
|
|
||||||
|
$location = $this->getRiakLocation();
|
||||||
|
|
||||||
|
$fetch = FetchValue::builder($location)->build();
|
||||||
|
|
||||||
|
$this->setExpectedException(RiakTransportException::class);
|
||||||
|
$this->client->execute($fetch);
|
||||||
|
|
||||||
|
$this->assertTotalBucketKeys(0, $location);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @depends testDelete
|
||||||
|
*/
|
||||||
|
public function testDeleteWithNotExistKey()
|
||||||
|
{
|
||||||
|
$this->storage->delete('riak-test', 'foobar');
|
||||||
|
$this->storage->delete('riak-test', 'foobar');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @depends testInsert
|
||||||
|
*/
|
||||||
|
public function testFind()
|
||||||
|
{
|
||||||
|
$data = [
|
||||||
|
'title' => 'Riak test',
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->storage->insert('riak-test', 'foobar', $data);
|
||||||
|
|
||||||
|
$result = $this->storage->find('riak-test', 'foobar');
|
||||||
|
|
||||||
|
$this->assertSame($data, $result);
|
||||||
|
}
|
||||||
|
|
||||||
public function testFindWithNotExistKey()
|
public function testFindWithNotExistKey()
|
||||||
{
|
{
|
||||||
$objectMock = $this->getMockBuilder('Riak\Object')
|
$this->setExpectedException(NotFoundException::class);
|
||||||
->disableOriginalConstructor()
|
$this->storage->find('riak-test', 'foobar-1');
|
||||||
->getMock();
|
|
||||||
|
|
||||||
$bucket = $this->getMockBuilder('Riak\Bucket')
|
|
||||||
->disableOriginalConstructor()
|
|
||||||
->getMock();
|
|
||||||
|
|
||||||
$this->riak->expects($this->once())
|
|
||||||
->method('bucket')
|
|
||||||
->will($this->returnValue($bucket));
|
|
||||||
|
|
||||||
$bucket->expects($this->once())
|
|
||||||
->method('get')
|
|
||||||
->with('foobar')
|
|
||||||
->will($this->returnValue($objectMock));
|
|
||||||
|
|
||||||
$objectMock->expects($this->once())
|
|
||||||
->method('exists')
|
|
||||||
->will($this->returnValue(false));
|
|
||||||
|
|
||||||
$objectMock->expects($this->never())
|
|
||||||
->method('getData');
|
|
||||||
|
|
||||||
$this->storage->find('riak-test', 'foobar');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetName()
|
public function testGetName()
|
||||||
{
|
{
|
||||||
$this->assertEquals('riak', $this->storage->getName());
|
$this->assertEquals('riak', $this->storage->getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function assertTotalBucketKeys($expectedTotal, $location)
|
||||||
|
{
|
||||||
|
$command = (new ListKeysBuilder($location->getNamespace()))->build();
|
||||||
|
|
||||||
|
$iterator = $this->client
|
||||||
|
->execute($command)
|
||||||
|
->getIterator();
|
||||||
|
|
||||||
|
$this->assertCount($expectedTotal, iterator_to_array($iterator));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getRiakLocation()
|
||||||
|
{
|
||||||
|
$namespace = new RiakNamespace('default', 'riak-test');
|
||||||
|
|
||||||
|
return new RiakLocation($namespace, 'foobar');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,5 @@ sudo apt-get install -y libuv-dev libssl-dev
|
|||||||
cd /tmp && git clone https://github.com/datastax/php-driver.git && cd php-driver && git submodule update --init
|
cd /tmp && git clone https://github.com/datastax/php-driver.git && cd php-driver && git submodule update --init
|
||||||
cd ext && ./install.sh && cd "$TRAVIS_BUILD_DIR"
|
cd ext && ./install.sh && cd "$TRAVIS_BUILD_DIR"
|
||||||
echo "extension=cassandra.so" >> `php --ini | grep "Loaded Configuration" | sed -e "s|.*:\s*||"`
|
echo "extension=cassandra.so" >> `php --ini | grep "Loaded Configuration" | sed -e "s|.*:\s*||"`
|
||||||
# PHP extensions
|
|
||||||
yes | pecl install mongo
|
|
||||||
# PECL extensions
|
# PECL extensions
|
||||||
echo "extension = redis.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
|
echo "extension = redis.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
|
||||||
|
|||||||
Reference in New Issue
Block a user