13 Commits

Author SHA1 Message Date
Emanuele Minotto 7a8465631f Merge pull request #96 from doctrine/dynamodb-refactoring
DynamoDB internal refactoring
2019-11-15 19:45:19 +01:00
Emanuele Minotto 76b76d42e9 Include DynamoDB tests on Travis 2019-10-20 14:36:57 +02:00
Emanuele Minotto 9fe32a6388 Add DynamoDB to documentation 2019-10-20 14:36:57 +02:00
Emanuele Minotto 1089486506 DynamoDbStorage internal refactoring 2019-10-20 14:36:57 +02:00
Emanuele Minotto e32cd91113 Merge pull request #94 from doctrine/issues/91
RiakStorage upgrade
2019-09-20 21:42:29 +02:00
EmanueleMinotto 8f07e47320 Replace riak client with php-riak/riak-client 2019-09-20 21:28:57 +02:00
EmanueleMinotto 186c8e04d2 Add riak to Travis 2019-09-11 21:55:39 +02:00
Emanuele Minotto fe7dcc5c79 Merge pull request #95 from doctrine/issues/93
MongoDbStorage upgrade
2019-09-11 21:42:11 +02:00
EmanueleMinotto 218a870dd3 Create UPGRADE.md 2019-09-11 21:36:23 +02:00
EmanueleMinotto b62b9ea098 Drop PHP 5.5 support 2019-09-11 21:36:23 +02:00
EmanueleMinotto 3800fa4d5c Add mongo to Travis 2019-09-11 21:36:23 +02:00
EmanueleMinotto a50dade76d Replace legacy Mongo class with MongoDB Client 2019-09-11 21:36:23 +02:00
EmanueleMinotto 6f154f5378 Add mongodb/mongodb dependency 2019-09-08 08:23:09 +02:00
14 changed files with 565 additions and 952 deletions
+19 -6
View File
@@ -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
+7
View File
@@ -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
View File
@@ -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",
+1 -1
View File
@@ -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>`_)
+14 -22
View File
@@ -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);
} }
/** /**
+2
View File
@@ -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');
}
} }
-2
View File
@@ -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