From 36a76af5490fd1a87c12585cc333e935a38ab60a Mon Sep 17 00:00:00 2001 From: Jasper Smet Date: Fri, 11 May 2018 10:54:08 +0200 Subject: [PATCH 01/19] Fix resolving of document entities --- src/Index.php | 2 +- tests/TestCase/IndexTest.php | 13 +++++++++++++ .../TestPlugin/src/Model/Document/Comment.php | 8 ++++++++ 3 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 tests/testapp/Plugin/TestPlugin/src/Model/Document/Comment.php diff --git a/src/Index.php b/src/Index.php index c4ab9c34..dd092875 100644 --- a/src/Index.php +++ b/src/Index.php @@ -806,7 +806,7 @@ public function entityClass($name = null) return $this->_documentClass = $default; } - $alias = Inflector::singularize(substr(array_pop($parts), 0, -4)); + $alias = Inflector::singularize(substr(array_pop($parts), 0, -5)); $name = implode('\\', array_slice($parts, 0, -1)) . '\Document\\' . $alias; if (!class_exists($name)) { return $this->_documentClass = $default; diff --git a/tests/TestCase/IndexTest.php b/tests/TestCase/IndexTest.php index b5274db7..938415bf 100644 --- a/tests/TestCase/IndexTest.php +++ b/tests/TestCase/IndexTest.php @@ -17,6 +17,7 @@ use Cake\Datasource\ConnectionManager; use Cake\ElasticSearch\Document; use Cake\ElasticSearch\Index; +use Cake\ElasticSearch\IndexRegistry; use Cake\TestSuite\TestCase; /** @@ -80,6 +81,18 @@ public function testEntityClassDefault() $this->assertEquals('\Cake\ElasticSearch\Document', $this->index->entityClass()); } + /** + * Test a custom entityClass. + * + * @return void + */ + public function testEntityClassCustom() + { + $index = IndexRegistry::get('TestPlugin.Comments'); + + $this->assertEquals('TestPlugin\Model\Document\Comment', $index->entityClass()); + } + /** * Tests that using a simple string for entityClass will try to * load the class from the App namespace diff --git a/tests/testapp/Plugin/TestPlugin/src/Model/Document/Comment.php b/tests/testapp/Plugin/TestPlugin/src/Model/Document/Comment.php new file mode 100644 index 00000000..8fe91f34 --- /dev/null +++ b/tests/testapp/Plugin/TestPlugin/src/Model/Document/Comment.php @@ -0,0 +1,8 @@ + Date: Fri, 11 May 2018 12:03:24 +0200 Subject: [PATCH 02/19] Add support for a registry alias so DocumentContext is properly preserved --- src/Index.php | 45 ++++++++++++++++++++++++++++++++---- src/IndexRegistry.php | 1 + tests/TestCase/IndexTest.php | 13 +++++++++++ 3 files changed, 55 insertions(+), 4 deletions(-) diff --git a/src/Index.php b/src/Index.php index dd092875..56195265 100644 --- a/src/Index.php +++ b/src/Index.php @@ -88,6 +88,13 @@ class Index implements RepositoryInterface, EventListenerInterface, EventDispatc */ protected $_type; + /** + * Registry key used to create this index object + * + * @var string + */ + protected $_registryAlias; + /** * The name of the class that represent a single document for this type * @@ -125,6 +132,9 @@ class Index implements RepositoryInterface, EventListenerInterface, EventDispatc */ public function __construct(array $config = []) { + if (!empty($config['registryAlias'])) { + $this->setRegistryAlias($config['registryAlias']); + } if (!empty($config['connection'])) { $this->setConnection($config['connection']); } @@ -255,6 +265,33 @@ public function connection($conn = null) return $this->getConnection(); } + /** + * Sets the index registry key used to create this table instance. + * + * @param string $registryAlias The key used to access this object. + * @return $this + */ + public function setRegistryAlias($registryAlias) + { + $this->_registryAlias = $registryAlias; + + return $this; + } + + /** + * Returns the index registry key used to create this table instance. + * + * @return string + */ + public function getRegistryAlias() + { + if ($this->_registryAlias === null) { + $this->_registryAlias = $this->getAlias(); + } + + return $this->_registryAlias; + } + /** * Sets the index name * @@ -460,7 +497,7 @@ public function get($primaryKey, $options = []) 'markNew' => false, 'markClean' => true, 'useSetters' => false, - 'source' => $this->getName(), + 'source' => $this->getRegistryAlias(), ]; $data = $result->getData(); $data['id'] = $result->getId(); @@ -613,7 +650,7 @@ public function saveMany($entities, $options = []) $entities[$key]->id = $doc->getId(); $entities[$key]->_version = $doc->getVersion(); $entities[$key]->isNew(false); - $entities[$key]->source($this->getName()); + $entities[$key]->source($this->getRegistryAlias()); $entities[$key]->clean(); $this->dispatchEvent('Model.afterSave', [ @@ -677,7 +714,7 @@ public function save(EntityInterface $entity, $options = []) $entity->id = $doc->getId(); $entity->_version = $doc->getVersion(); $entity->isNew(false); - $entity->source($this->getName()); + $entity->source($this->getRegistryAlias()); $entity->clean(); $this->dispatchEvent('Model.afterSave', [ @@ -760,7 +797,7 @@ public function newEntity($data = null, array $options = []) if ($data === null) { $class = $this->entityClass(); - return new $class([], ['source' => $this->getName()]); + return new $class([], ['source' => $this->getRegistryAlias()]); } return $this->marshaller()->one($data, $options); diff --git a/src/IndexRegistry.php b/src/IndexRegistry.php index a1525b02..e6474f84 100644 --- a/src/IndexRegistry.php +++ b/src/IndexRegistry.php @@ -88,6 +88,7 @@ public static function get($alias, array $options = []) $connectionName = $options['className']::defaultConnectionName(); $options['connection'] = ConnectionManager::get($connectionName); } + $options['registryAlias'] = $alias; static::$instances[$alias] = new $options['className']($options); return static::$instances[$alias]; diff --git a/tests/TestCase/IndexTest.php b/tests/TestCase/IndexTest.php index 938415bf..d8cfb4d0 100644 --- a/tests/TestCase/IndexTest.php +++ b/tests/TestCase/IndexTest.php @@ -671,6 +671,19 @@ public function testAlias() $this->assertEquals('articles', $this->index->getAlias()); } + /** + * Test the alias method. + * + * @return void + */ + public function testRegistryAlias() + { + $index = IndexRegistry::get('TestPlugin.Comments'); + + $this->assertEquals('articles', $this->index->getRegistryAlias()); + $this->assertEquals('TestPlugin.Comments', $index->getRegistryAlias()); + } + /** * Test hasField() * From 3a2e9666f4b11b6d7c2f4a0ff464bf03d5a26c14 Mon Sep 17 00:00:00 2001 From: Jasper Smet Date: Fri, 11 May 2018 14:40:48 +0200 Subject: [PATCH 03/19] Also set proper registry alias in ResultSet entities --- src/ResultSet.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ResultSet.php b/src/ResultSet.php index 4b7146fe..ca7aebff 100644 --- a/src/ResultSet.php +++ b/src/ResultSet.php @@ -70,7 +70,7 @@ public function __construct($resultSet, $query) $this->embeds[$embed->property()] = $embed; } $this->entityClass = $repo->entityClass(); - $this->repoName = $repo->getName(); + $this->repoName = $repo->getRegistryAlias(); parent::__construct($resultSet); } From f79123f7633b81a197fd3c961cbb07e794f5f74e Mon Sep 17 00:00:00 2001 From: Jasper Smet Date: Fri, 11 May 2018 15:45:29 +0200 Subject: [PATCH 04/19] Adding a setLogger method that overrides the one from Elastica\Client, this fixes an exception when using a custom logger (like debug_kit SqlLogPanel), deprecated the old logger method --- src/Datasource/Connection.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/Datasource/Connection.php b/src/Datasource/Connection.php index 5ed830b6..69809bc4 100644 --- a/src/Datasource/Connection.php +++ b/src/Datasource/Connection.php @@ -150,8 +150,21 @@ public function config() * * @param object $instance logger object instance * @return object logger instance + * @deprecated 2.0 Will be replaced by setLogger() */ public function logger($instance = null) + { + $this->setLogger($instance); + } + + /** + * Sets the logger object instance. When called with no arguments + * it returns the currently setup logger instance. + * + * @param object $instance logger object instance + * @return object logger instance + */ + public function setLogger($instance = null) { if ($instance === null) { return $this->_logger; From 8f62af809a03c55b30c41b549be486ceae38e023 Mon Sep 17 00:00:00 2001 From: Jasper Smet Date: Fri, 11 May 2018 15:57:26 +0200 Subject: [PATCH 05/19] Also add getLogger() method and fix phpcs problem --- src/Datasource/Connection.php | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/Datasource/Connection.php b/src/Datasource/Connection.php index 69809bc4..22ef9288 100644 --- a/src/Datasource/Connection.php +++ b/src/Datasource/Connection.php @@ -154,24 +154,33 @@ public function config() */ public function logger($instance = null) { - $this->setLogger($instance); + if ($instance === null) { + return $this->_logger; + } + $this->_logger = $instance; } /** - * Sets the logger object instance. When called with no arguments - * it returns the currently setup logger instance. + * Sets the logger object instance. * * @param object $instance logger object instance - * @return object logger instance + * @return void */ - public function setLogger($instance = null) + public function setLogger($instance) { - if ($instance === null) { - return $this->_logger; - } $this->_logger = $instance; } + /** + * Get the logger object instance. + * + * @return object logger instance + */ + public function getLogger() + { + return $this->_logger; + } + /** * Log requests to Elastic Search. * From 32138716e1aece465f024fb626e45303f0d48996 Mon Sep 17 00:00:00 2001 From: Jasper Smet Date: Sat, 12 May 2018 12:09:42 +0200 Subject: [PATCH 06/19] Initial cake 3.6 compatibility --- .gitignore | 2 + composer.json | 6 +- src/Association/Embedded.php | 15 +++++ src/Datasource/Connection.php | 30 ++++++---- src/Document.php | 2 +- src/Index.php | 45 ++++++++++++-- src/Marshaller.php | 16 ++--- src/Query.php | 8 +-- src/ResultSet.php | 2 +- src/TestSuite/TestFixture.php | 1 + src/View/Form/DocumentContext.php | 16 ++--- tests/TestCase/Datasource/ConnectionTest.php | 2 +- tests/TestCase/IndexRegistryTest.php | 16 ++--- tests/TestCase/IndexTest.php | 58 +++++++++---------- tests/TestCase/MarshallerTest.php | 44 +++++++------- tests/TestCase/QueryTest.php | 2 +- tests/TestCase/ResultSetTest.php | 6 +- .../View/Form/DocumentContextTest.php | 24 ++++---- tests/init.php | 6 +- 19 files changed, 180 insertions(+), 121 deletions(-) diff --git a/.gitignore b/.gitignore index 987e2a25..e707314c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ composer.lock vendor +.vscode +.env diff --git a/composer.json b/composer.json index 5ed6698f..e9d588f4 100644 --- a/composer.json +++ b/composer.json @@ -18,13 +18,13 @@ "source": "https://github.com/cakephp/elastic-search" }, "require": { - "ruflin/elastica": "^6.0" + "ruflin/elastica": "^6.0", + "cakephp/cakephp": "^3.6" }, "require-dev": { "cakephp/cakephp-codesniffer": "^3.0", "psr/log": "~1.0", - "phpunit/phpunit": "^6.0", - "cakephp/cakephp": "~3.4.0" + "phpunit/phpunit": "^6.0" }, "autoload": { "psr-4": { diff --git a/src/Association/Embedded.php b/src/Association/Embedded.php index f9a83587..89e5bab6 100644 --- a/src/Association/Embedded.php +++ b/src/Association/Embedded.php @@ -126,6 +126,21 @@ public function entityClass($name = null) * @return string */ public function alias() + { + deprecationWarning( + 'Embedded::alias() is deprecated. ' . + 'Use Embedded::getAlias() instead.' + ); + + return $this->alias; + } + + /** + * Get the alias for this embed. + * + * @return string + */ + public function getAlias() { return $this->alias; } diff --git a/src/Datasource/Connection.php b/src/Datasource/Connection.php index 22ef9288..3584a886 100644 --- a/src/Datasource/Connection.php +++ b/src/Datasource/Connection.php @@ -62,7 +62,7 @@ public function __construct(array $config = [], $callback = null) * * @return \Cake\ElasticSearch\Datasource\SchemaCollection */ - public function schemaCollection() + public function getSchemaCollection() { return new SchemaCollection($this); } @@ -150,10 +150,15 @@ public function config() * * @param object $instance logger object instance * @return object logger instance - * @deprecated 2.0 Will be replaced by setLogger() + * @deprecated use setLogger()/getLogger instead */ public function logger($instance = null) { + deprecationWarning( + 'Connection::logger() is deprecated. ' . + 'Use Connection::setLogger()/getLogger() instead.' + ); + if ($instance === null) { return $this->_logger; } @@ -173,11 +178,16 @@ public function setLogger($instance) /** * Get the logger object instance. - * + + * @param object $instance logger object instance * @return object logger instance */ - public function getLogger() + public function getLogger($instance = null) { + if ($instance === null) { + $this->_logger = Log::engine('elasticsearch') ?: new ElasticaLog(); + } + return $this->_logger; } @@ -193,10 +203,6 @@ protected function _log($context) return; } - if (!isset($this->_logger) || $this->_logger instanceof NullLogger) { - $this->_logger = Log::engine('elasticsearch') ?: new ElasticaLog(); - } - if ($context instanceof Request) { $contextArray = $context->toArray(); $logData = [ @@ -214,10 +220,12 @@ protected function _log($context) $loggedQuery = new LoggedQuery(); $loggedQuery->query = $data; - if ($this->_logger instanceof \Psr\Log\LoggerInterface) { - $this->_logger->log('debug', $loggedQuery); + $logger = $this->getLogger(); + + if ($logger instanceof \Psr\Log\LoggerInterface) { + $logger->log('debug', $loggedQuery); } else { - $this->_logger->log($loggedQuery); + $logger->log($loggedQuery); } } } diff --git a/src/Document.php b/src/Document.php index 86f720ea..fc86de84 100644 --- a/src/Document.php +++ b/src/Document.php @@ -66,7 +66,7 @@ public function __construct($data = [], $options = []) ]; if (!empty($options['source'])) { - $this->source($options['source']); + $this->setSource($options['source']); } if ($options['markNew'] !== null) { diff --git a/src/Index.php b/src/Index.php index 56195265..e57e39f3 100644 --- a/src/Index.php +++ b/src/Index.php @@ -258,6 +258,11 @@ public function getConnection() */ public function connection($conn = null) { + deprecationWarning( + 'Index::connection() is deprecated. ' . + 'Use Index::setConnection()/getConnection() instead.' + ); + if ($conn !== null) { return $this->setConnection($conn); } @@ -279,7 +284,7 @@ public function setRegistryAlias($registryAlias) } /** - * Returns the index registry key used to create this table instance. + * Returns the index registry key used to create this instance. * * @return string */ @@ -332,6 +337,11 @@ public function getName() */ public function name($name = null) { + deprecationWarning( + 'Index::name() is deprecated. ' . + 'Use Index::setName()/getName() instead.' + ); + if ($name !== null) { $this->setName($name); } @@ -346,8 +356,26 @@ public function name($name = null) * * @return string */ + public function getTable() + { + return $this->getName(); + } + + /** + * Get the index name, as required by QueryTrait + * + * This method is just an alias of name(). + * + * @return string + * @deprecated Use getTable() instead + */ public function table() { + deprecationWarning( + 'Index::table() is deprecated. ' . + 'Use Index::getTable() instead.' + ); + return $this->getName(); } @@ -362,7 +390,12 @@ public function table() */ public function alias($alias = null) { - return $this->name($alias); + deprecationWarning( + 'Index::table() is deprecated. ' . + 'Use Index::setAlias()/getAlias() instead.' + ); + + return $this->getName($alias); } /** @@ -623,7 +656,7 @@ public function saveMany($entities, $options = []) 'options' => $options ]); - if ($event->isStopped() || $entity->errors()) { + if ($event->isStopped() || $entity->getErrors()) { return false; } @@ -650,7 +683,7 @@ public function saveMany($entities, $options = []) $entities[$key]->id = $doc->getId(); $entities[$key]->_version = $doc->getVersion(); $entities[$key]->isNew(false); - $entities[$key]->source($this->getRegistryAlias()); + $entities[$key]->setSource($this->getRegistryAlias()); $entities[$key]->clean(); $this->dispatchEvent('Model.afterSave', [ @@ -690,7 +723,7 @@ public function save(EntityInterface $entity, $options = []) return $event->result; } - if ($entity->errors()) { + if ($entity->getErrors()) { return false; } @@ -714,7 +747,7 @@ public function save(EntityInterface $entity, $options = []) $entity->id = $doc->getId(); $entity->_version = $doc->getVersion(); $entity->isNew(false); - $entity->source($this->getRegistryAlias()); + $entity->setSource($this->getRegistryAlias()); $entity->clean(); $this->dispatchEvent('Model.afterSave', [ diff --git a/src/Marshaller.php b/src/Marshaller.php index b30a73cc..7b9b7045 100644 --- a/src/Marshaller.php +++ b/src/Marshaller.php @@ -61,25 +61,25 @@ public function one(array $data, array $options = []) { $entityClass = $this->index->entityClass(); $entity = new $entityClass(); - $entity->source($this->index->getName()); + $entity->setSource($this->index->getRegistryAlias()); $options += ['associated' => []]; list($data, $options) = $this->_prepareDataAndOptions($data, $options); if (isset($options['accessibleFields'])) { foreach ((array)$options['accessibleFields'] as $key => $value) { - $entity->accessible($key, $value); + $entity->setAccess($key, $value); } } $errors = $this->_validate($data, $options, true); - $entity->errors($errors); + $entity->setErrors($errors); foreach (array_keys($errors) as $badKey) { unset($data[$badKey]); } foreach ($this->index->embedded() as $embed) { $property = $embed->property(); - if (in_array($embed->alias(), $options['associated']) && + if (in_array($embed->getAlias(), $options['associated']) && isset($data[$property]) ) { $data[$property] = $this->newNested($embed, $data[$property]); @@ -209,7 +209,7 @@ public function merge(EntityInterface $entity, array $data, array $options = []) $options += ['associated' => []]; list($data, $options) = $this->_prepareDataAndOptions($data, $options); $errors = $this->_validate($data, $options, $entity->isNew()); - $entity->errors($errors); + $entity->setErrors($errors); foreach (array_keys($errors) as $badKey) { unset($data[$badKey]); @@ -217,7 +217,7 @@ public function merge(EntityInterface $entity, array $data, array $options = []) foreach ($this->index->embedded() as $embed) { $property = $embed->property(); - if (in_array($embed->alias(), $options['associated']) && + if (in_array($embed->getAlias(), $options['associated']) && isset($data[$property]) ) { $data[$property] = $this->mergeNested($embed, $entity->{$property}, $data[$property]); @@ -307,10 +307,10 @@ protected function _validate($data, $options, $isNew) } if ($options['validate'] === true) { - $options['validate'] = $this->index->validator('default'); + $options['validate'] = $this->index->getValidator('default'); } if (is_string($options['validate'])) { - $options['validate'] = $this->index->validator($options['validate']); + $options['validate'] = $this->index->getValidator($options['validate']); } if (!is_object($options['validate'])) { throw new RuntimeException( diff --git a/src/Query.php b/src/Query.php index b3c5f62f..db715570 100644 --- a/src/Query.php +++ b/src/Query.php @@ -556,8 +556,8 @@ public function withMinScore($score) */ protected function _execute() { - $connection = $this->_repository->connection(); - $index = $this->_repository->name(); + $connection = $this->_repository->getConnection(); + $index = $this->_repository->getName(); $type = $connection->getIndex($index)->getType($this->_repository->getType()); $query = $this->compileQuery(); @@ -644,8 +644,8 @@ public function aliasFields($fields, $defaultAlias = null) */ public function count() { - $connection = $this->_repository->connection(); - $index = $this->_repository->name(); + $connection = $this->_repository->getConnection(); + $index = $this->_repository->getName(); $type = $connection->getIndex($index)->getType($this->_repository->getType()); $query = clone $this->compileQuery(); diff --git a/src/ResultSet.php b/src/ResultSet.php index ca7aebff..07310acf 100644 --- a/src/ResultSet.php +++ b/src/ResultSet.php @@ -65,7 +65,7 @@ class ResultSet extends IteratorIterator implements Countable, JsonSerializable public function __construct($resultSet, $query) { $this->resultSet = $resultSet; - $repo = $query->repository(); + $repo = $query->getRepository(); foreach ($repo->embedded() as $embed) { $this->embeds[$embed->property()] = $embed; } diff --git a/src/TestSuite/TestFixture.php b/src/TestSuite/TestFixture.php index e778bf7c..32a448c9 100644 --- a/src/TestSuite/TestFixture.php +++ b/src/TestSuite/TestFixture.php @@ -77,6 +77,7 @@ class TestFixture implements FixtureInterface */ public function create(ConnectionInterface $db) { + debug($db); if (empty($this->schema)) { return; } diff --git a/src/View/Form/DocumentContext.php b/src/View/Form/DocumentContext.php index 69bbb38a..7a65650b 100644 --- a/src/View/Form/DocumentContext.php +++ b/src/View/Form/DocumentContext.php @@ -17,7 +17,7 @@ use Cake\Collection\Collection; use Cake\ElasticSearch\Document; use Cake\ElasticSearch\IndexRegistry; -use Cake\Network\Request; +use Cake\Http\ServerRequest; use Cake\Utility\Inflector; use Cake\View\Form\ContextInterface; use RuntimeException; @@ -31,7 +31,7 @@ class DocumentContext implements ContextInterface /** * The request object. * - * @var \Cake\Network\Request + * @var \Cake\Http\ServerRequest */ protected $_request; @@ -60,10 +60,10 @@ class DocumentContext implements ContextInterface /** * Constructor. * - * @param \Cake\Network\Request $request The request object. + * @param \Cake\Http\ServerRequest $request The request object. * @param array $context Context info. */ - public function __construct(Request $request, array $context) + public function __construct(ServerRequest $request, array $context) { $this->_request = $request; $context += [ @@ -101,7 +101,7 @@ protected function _prepare() $isDocument = $entity instanceof Document; if ($isDocument) { - $index = $entity->source(); + $index = $entity->getSource(); } if (!$index && $isDocument && get_class($entity) !== 'Cake\ElasticSearch\Document') { list(, $entityClass) = namespaceSplit(get_class($entity)); @@ -164,7 +164,7 @@ public function isCreate() */ public function val($field) { - $val = $this->_request->data($field); + $val = $this->_request->getData($field); if ($val !== null) { return $val; } @@ -297,7 +297,7 @@ public function isRequired($field) */ protected function getValidator() { - return $this->_context['index']->validator($this->_context['validator']); + return $this->_context['index']->getValidator($this->_context['validator']); } /** @@ -345,7 +345,7 @@ public function error($field) $entity = $this->entity($parts); if ($entity instanceof Document) { - return $entity->errors(array_pop($parts)); + return $entity->getError(array_pop($parts)); } return []; diff --git a/tests/TestCase/Datasource/ConnectionTest.php b/tests/TestCase/Datasource/ConnectionTest.php index c12ad9ca..72220e5a 100644 --- a/tests/TestCase/Datasource/ConnectionTest.php +++ b/tests/TestCase/Datasource/ConnectionTest.php @@ -79,7 +79,7 @@ public function testQueryLogging() { $logger = $this->getMockBuilder('Cake\Log\Engine\BaseLog')->setMethods(['log'])->getMock(); $logger->expects($this->once())->method('log'); - Log::config('elasticsearch', $logger); + Log::setConfig('elasticsearch', $logger); $connection = ConnectionManager::get('test'); $connection->logQueries(true); diff --git a/tests/TestCase/IndexRegistryTest.php b/tests/TestCase/IndexRegistryTest.php index aa2ab905..68ed0e5c 100644 --- a/tests/TestCase/IndexRegistryTest.php +++ b/tests/TestCase/IndexRegistryTest.php @@ -87,11 +87,11 @@ public function testGet() 'name' => 'my_articles', ]); $this->assertInstanceOf('Cake\ElasticSearch\Index', $result); - $this->assertEquals('my_articles', $result->name()); + $this->assertEquals('my_articles', $result->getName()); $result2 = IndexRegistry::get('Articles'); $this->assertSame($result, $result2); - $this->assertEquals('my_articles', $result->name()); + $this->assertEquals('my_articles', $result->getName()); } /** @@ -103,27 +103,27 @@ public function testGetFallbacks() { $result = IndexRegistry::get('Droids'); $this->assertInstanceOf('Cake\ElasticSearch\Index', $result); - $this->assertEquals('droids', $result->name()); + $this->assertEquals('droids', $result->getName()); $result = IndexRegistry::get('R2D2', ['className' => 'Droids']); $this->assertInstanceOf('Cake\ElasticSearch\Index', $result); - $this->assertEquals('r2_d2', $result->name(), 'The name should be derived from the alias'); + $this->assertEquals('r2_d2', $result->getName(), 'The name should be derived from the alias'); $result = IndexRegistry::get('C3P0', ['className' => 'Droids', 'name' => 'droids']); $this->assertInstanceOf('Cake\ElasticSearch\Index', $result); - $this->assertEquals('droids', $result->name(), 'The name should be taken from options'); + $this->assertEquals('droids', $result->getName(), 'The name should be taken from options'); $result = IndexRegistry::get('Funky.Chipmunks'); $this->assertInstanceOf('Cake\ElasticSearch\Index', $result); - $this->assertEquals('chipmunks', $result->name(), 'The name should be derived from the alias'); + $this->assertEquals('chipmunks', $result->getName(), 'The name should be derived from the alias'); $result = IndexRegistry::get('Awesome', ['className' => 'Funky.Monkies']); $this->assertInstanceOf('Cake\ElasticSearch\Index', $result); - $this->assertEquals('awesome', $result->name(), 'The name should be derived from the alias'); + $this->assertEquals('awesome', $result->getName(), 'The name should be derived from the alias'); $result = IndexRegistry::get('Stuff', ['className' => 'Cake\ElasticSearch\Index']); $this->assertInstanceOf('Cake\ElasticSearch\Index', $result); - $this->assertEquals('stuff', $result->name(), 'The name should be derived from the alias'); + $this->assertEquals('stuff', $result->getName(), 'The name should be derived from the alias'); } /** diff --git a/tests/TestCase/IndexTest.php b/tests/TestCase/IndexTest.php index d8cfb4d0..f5f24053 100644 --- a/tests/TestCase/IndexTest.php +++ b/tests/TestCase/IndexTest.php @@ -47,7 +47,7 @@ public function testFindAll() { $query = $this->index->find('all'); $this->assertInstanceOf('Cake\ElasticSearch\Query', $query); - $this->assertSame($this->index, $query->repository()); + $this->assertSame($this->index, $query->getRepository()); } /** @@ -68,7 +68,7 @@ public function testFindAllWithFirstOrFail() */ public function testTable() { - $this->assertSame('articles', $this->index->table()); + $this->assertSame('articles', $this->index->getTable()); } /** @@ -179,9 +179,9 @@ public function testGet() $result = $index->get('foo', ['bar' => 'baz']); $this->assertInstanceOf('Cake\ElasticSearch\Document', $result); $this->assertEquals(['a' => 'b', 'id' => 'foo'], $result->toArray()); - $this->assertFalse($result->dirty()); + $this->assertFalse($result->isDirty()); $this->assertFalse($result->isNew()); - $this->assertEquals('foo', $result->source()); + $this->assertEquals('foo', $result->getSource()); } /** @@ -204,7 +204,7 @@ public function testNewEntity() $result = $index->newEntity($data); $this->assertInstanceOf('Cake\ElasticSearch\Document', $result); $this->assertSame($data, $result->toArray()); - $this->assertEquals('articles', $result->source()); + $this->assertEquals('articles', $result->getSource()); } /** @@ -278,12 +278,12 @@ public function testSaveNew() $this->assertNotEmpty($doc->id, 'Should get an id'); $this->assertNotEmpty($doc->_version, 'Should get a version'); $this->assertFalse($doc->isNew(), 'Not new anymore.'); - $this->assertFalse($doc->dirty(), 'Not dirty anymore.'); + $this->assertFalse($doc->isDirty(), 'Not dirty anymore.'); $result = $this->index->get($doc->id); $this->assertEquals($doc->title, $result->title); $this->assertEquals($doc->body, $result->body); - $this->assertEquals('articles', $result->source()); + $this->assertEquals('articles', $result->getSource()); } /** @@ -300,8 +300,8 @@ public function testSaveUpdate() ], ['markNew' => false]); $this->assertSame($doc, $this->index->save($doc)); $this->assertFalse($doc->isNew(), 'Not new.'); - $this->assertFalse($doc->dirty(), 'Not dirty anymore.'); - $this->assertEquals('articles', $doc->source()); + $this->assertFalse($doc->isDirty(), 'Not dirty anymore.'); + $this->assertEquals('articles', $doc->getSource()); } /** @@ -316,7 +316,7 @@ public function testSaveDoesNotSaveDocumentWithErrors() 'title' => 'A brand new article', 'body' => 'Some new content' ], ['markNew' => false]); - $doc->errors(['title' => ['bad news']]); + $doc->setErrors(['title' => ['bad news']]); $this->assertFalse($this->index->save($doc), 'Should not save.'); } @@ -331,7 +331,7 @@ public function testSaveEvents() $doc->title = 'A new title'; $called = 0; - $this->index->eventManager()->on( + $this->index->getEventManager()->on( 'Model.beforeSave', function ($event, $entity, $options) use ($doc, &$called) { $called++; @@ -339,14 +339,14 @@ function ($event, $entity, $options) use ($doc, &$called) { $this->assertInstanceOf('ArrayObject', $options); } ); - $this->index->eventManager()->on( + $this->index->getEventManager()->on( 'Model.afterSave', function ($event, $entity, $options) use ($doc, &$called) { $called++; $this->assertInstanceOf('ArrayObject', $options); $this->assertSame($doc, $entity); $this->assertFalse($doc->isNew(), 'Should not be new'); - $this->assertFalse($doc->dirty(), 'Should not be dirty'); + $this->assertFalse($doc->isDirty(), 'Should not be dirty'); } ); $this->index->save($doc); @@ -362,12 +362,12 @@ public function testSaveBeforeSaveAbort() { $doc = $this->index->get(1); $doc->title = 'new title'; - $this->index->eventManager()->on('Model.beforeSave', function ($event, $entity, $options) use ($doc) { + $this->index->getEventManager()->on('Model.beforeSave', function ($event, $entity, $options) use ($doc) { $event->stopPropagation(); return 'kaboom'; }); - $this->index->eventManager()->on('Model.afterSave', function () { + $this->index->getEventManager()->on('Model.afterSave', function () { $this->fail('Should not be fired'); }); $this->assertSame('kaboom', $this->index->save($doc)); @@ -425,7 +425,7 @@ public function testSaveEmbedMany() */ public function testSaveWithRulesCreate() { - $this->index->eventManager()->on('Model.buildRules', function ($event, $rules) { + $this->index->getEventManager()->on('Model.buildRules', function ($event, $rules) { $rules->addCreate(function ($doc) { return 'Did not work'; }, ['errorField' => 'name']); @@ -447,7 +447,7 @@ public function testSaveWithRulesCreate() */ public function testSaveWithRulesUpdate() { - $this->index->eventManager()->on('Model.buildRules', function ($event, $rules) { + $this->index->getEventManager()->on('Model.buildRules', function ($event, $rules) { $rules->addUpdate(function ($doc) { return 'Did not work'; }, ['errorField' => 'name']); @@ -504,7 +504,7 @@ public function testDeleteRules() $doc = $this->index->get(1); $this->assertFalse($this->index->delete($doc)); - $this->assertNotEmpty($doc->errors('title')); + $this->assertNotEmpty($doc->getError('title')); } /** @@ -516,7 +516,7 @@ public function testDeleteEvents() { $called = 0; $doc = $this->index->get(1); - $this->index->eventManager()->on( + $this->index->getEventManager()->on( 'Model.beforeDelete', function ($event, $entity, $options) use ($doc, &$called) { $called++; @@ -524,7 +524,7 @@ function ($event, $entity, $options) use ($doc, &$called) { $this->assertInstanceOf('ArrayObject', $options); } ); - $this->index->eventManager()->on( + $this->index->getEventManager()->on( 'Model.afterDelete', function ($event, $entity, $options) use ($doc, &$called) { $called++; @@ -544,12 +544,12 @@ function ($event, $entity, $options) use ($doc, &$called) { public function testDeleteBeforeDeleteAbort() { $doc = $this->index->get(1); - $this->index->eventManager()->on('Model.beforeDelete', function ($event, $entity, $options) use ($doc) { + $this->index->getEventManager()->on('Model.beforeDelete', function ($event, $entity, $options) use ($doc) { $event->stopPropagation(); return 'kaboom'; }); - $this->index->eventManager()->on('Model.afterDelete', function () { + $this->index->getEventManager()->on('Model.afterDelete', function () { $this->fail('Should not be fired'); }); $this->assertSame('kaboom', $this->index->delete($doc)); @@ -575,11 +575,11 @@ public function testDeleteMissing() */ public function testValidatorSetAndGet() { - $result = $this->index->validator(); + $result = $this->index->getValidator(); $this->assertInstanceOf('Cake\Validation\Validator', $result); - $this->assertSame($result, $this->index->validator(), 'validator instances are persistent'); - $this->assertSame($this->index, $result->provider('collection'), 'index bound as provider'); + $this->assertSame($result, $this->index->getValidator(), 'validator instances are persistent'); + $this->assertSame($this->index, $result->getProvider('collection'), 'index bound as provider'); } /** @@ -590,12 +590,12 @@ public function testValidatorSetAndGet() public function testValidatorTriggerEvent() { $called = 0; - $this->index->eventManager()->on('Model.buildValidator', function ($event, $validator, $name) use (&$called) { + $this->index->getEventManager()->on('Model.buildValidator', function ($event, $validator, $name) use (&$called) { $called++; $this->assertInstanceOf('Cake\Validation\Validator', $validator); $this->assertEquals('default', $name); }); - $this->index->validator(); + $this->index->getValidator(); $this->assertEquals(1, $called, 'Event not triggered'); } @@ -647,7 +647,7 @@ public function testDeleteAllOnlySome() */ public function testAddRules() { - $this->index->eventManager()->on('Model.buildRules', function ($event, $rules) { + $this->index->getEventManager()->on('Model.buildRules', function ($event, $rules) { $rules->add(function ($doc) { return false; }); @@ -729,6 +729,6 @@ public function testOwnEvents() ->setMethods(['beforeSave']) ->getMock(); - $this->assertCount(1, $index->eventManager()->listeners('Model.beforeSave')); + $this->assertCount(1, $index->getEventManager()->listeners('Model.beforeSave')); } } diff --git a/tests/TestCase/MarshallerTest.php b/tests/TestCase/MarshallerTest.php index 1eaf1e5a..9015a438 100644 --- a/tests/TestCase/MarshallerTest.php +++ b/tests/TestCase/MarshallerTest.php @@ -91,7 +91,7 @@ public function testOneValidationErrorsSet() 'body' => 'Elastic text', 'user_id' => 1, ]; - $this->index->validator() + $this->index->getValidator() ->add('title', 'numbery', ['rule' => 'numeric']); $marshaller = new Marshaller($this->index); @@ -101,7 +101,7 @@ public function testOneValidationErrorsSet() $this->assertNull($result->title, 'Invalid fields are not set.'); $this->assertSame($data['body'], $result->body); $this->assertSame($data['user_id'], $result->user_id); - $this->assertNotEmpty($result->errors('title'), 'Should have an error.'); + $this->assertNotEmpty($result->getErrors('title'), 'Should have an error.'); } /** @@ -165,7 +165,7 @@ public function testOneBeforeMarshalEvent() 'user_id' => 1, ]; $called = 0; - $this->index->eventManager()->on( + $this->index->getEventManager()->on( 'Model.beforeMarshal', function ($event, $data, $options) use (&$called) { $called++; @@ -191,7 +191,7 @@ public function testOneBeforeMarshalEventMutateData() 'body' => 'Elastic text', 'user_id' => 1, ]; - $this->index->eventManager()->on('Model.beforeMarshal', function ($event, $data, $options) { + $this->index->getEventManager()->on('Model.beforeMarshal', function ($event, $data, $options) { $data['title'] = 'Mutated'; }); $marshaller = new Marshaller($this->index); @@ -311,9 +311,9 @@ public function testMerge() $this->assertSame($result, $doc, 'Object should be the same.'); $this->assertSame($data['title'], $doc->title, 'title should be the same.'); $this->assertSame($data['body'], $doc->body, 'body should be the same.'); - $this->assertTrue($doc->dirty('title')); - $this->assertTrue($doc->dirty('body')); - $this->assertFalse($doc->dirty('user_id')); + $this->assertTrue($doc->isDirty('title')); + $this->assertTrue($doc->isDirty('body')); + $this->assertFalse($doc->isDirty('user_id')); $this->assertFalse($doc->isNew(), 'Should not end up new'); } @@ -329,7 +329,7 @@ public function testMergeValidationErrorsSet() 'body' => 'Elastic text', 'user_id' => 1, ]; - $this->index->validator() + $this->index->getValidator() ->add('title', 'numbery', ['rule' => 'numeric']); $doc = $this->index->get(1); @@ -338,7 +338,7 @@ public function testMergeValidationErrorsSet() $this->assertInstanceOf('Cake\ElasticSearch\Document', $result); $this->assertSame('First article', $result->title, 'Invalid fields are not modified.'); - $this->assertNotEmpty($result->errors('title'), 'Should have an error.'); + $this->assertNotEmpty($result->getErrors('title'), 'Should have an error.'); } /** @@ -349,7 +349,7 @@ public function testMergeValidationErrorsSet() public function testMergeFieldList() { $doc = $this->index->get(1); - $doc->accessible('*', false); + $doc->setAccess('*', false); $data = [ 'title' => 'New title', @@ -361,8 +361,8 @@ public function testMergeFieldList() $this->assertSame($result, $doc, 'Object should be the same.'); $this->assertSame($data['title'], $doc->title, 'title should be the same.'); $this->assertNotEquals($data['body'], $doc->body, 'body should be the same.'); - $this->assertTrue($doc->dirty('title')); - $this->assertFalse($doc->dirty('body')); + $this->assertTrue($doc->isDirty('title')); + $this->assertFalse($doc->isDirty('body')); } /** @@ -378,7 +378,7 @@ public function testMergeBeforeMarshalEvent() 'user_id' => 1, ]; $called = 0; - $this->index->eventManager()->on( + $this->index->getEventManager()->on( 'Model.beforeMarshal', function ($event, $data, $options) use (&$called) { $called++; @@ -405,7 +405,7 @@ public function testMergeBeforeMarshalEventMutateData() 'body' => 'Elastic text', 'user_id' => 1, ]; - $this->index->eventManager()->on('Model.beforeMarshal', function ($event, $data, $options) { + $this->index->getEventManager()->on('Model.beforeMarshal', function ($event, $data, $options) { $data['title'] = 'Mutated'; }); $marshaller = new Marshaller($this->index); @@ -628,12 +628,12 @@ public function testMergeManySomeNew() $this->assertCount(2, $result); $this->assertEquals($data[0]['title'], $result[0]->title); $this->assertFalse($result[0]->isNew()); - $this->assertTrue($result[0]->dirty()); - $this->assertTrue($result[0]->dirty('title')); + $this->assertTrue($result[0]->isDirty()); + $this->assertTrue($result[0]->isDirty('title')); $this->assertTrue($result[1]->isNew()); - $this->assertTrue($result[1]->dirty()); - $this->assertTrue($result[1]->dirty('title')); + $this->assertTrue($result[1]->isDirty()); + $this->assertTrue($result[1]->isDirty('title')); } /** @@ -661,13 +661,13 @@ public function testMergeManyDropsUnknownEntities() $this->assertCount(2, $result); $this->assertEquals($data[0], $result[0]->toArray()); $this->assertTrue($result[0]->isNew()); - $this->assertTrue($result[0]->dirty()); - $this->assertTrue($result[0]->dirty('title')); + $this->assertTrue($result[0]->isDirty()); + $this->assertTrue($result[0]->isDirty('title')); $this->assertEquals($data[1], $result[1]->toArray()); $this->assertTrue($result[1]->isNew()); - $this->assertTrue($result[1]->dirty()); - $this->assertTrue($result[1]->dirty('title')); + $this->assertTrue($result[1]->isDirty()); + $this->assertTrue($result[1]->isDirty('title')); } /** diff --git a/tests/TestCase/QueryTest.php b/tests/TestCase/QueryTest.php index 2654229a..721ff2f4 100644 --- a/tests/TestCase/QueryTest.php +++ b/tests/TestCase/QueryTest.php @@ -35,7 +35,7 @@ public function testConstruct() { $index = new Index(); $query = new Query($index); - $this->assertSame($index, $query->repository()); + $this->assertSame($index, $query->getRepository()); } /** diff --git a/tests/TestCase/ResultSetTest.php b/tests/TestCase/ResultSetTest.php index 7736128c..bfa4c23a 100644 --- a/tests/TestCase/ResultSetTest.php +++ b/tests/TestCase/ResultSetTest.php @@ -43,7 +43,7 @@ public function testConstructor() $query = $this->getMockBuilder('Cake\ElasticSearch\Query') ->setConstructorArgs([$type]) ->getMock(); - $query->expects($this->once())->method('repository') + $query->expects($this->once())->method('getRepository') ->will($this->returnValue($type)); $type->expects($this->once()) @@ -84,7 +84,7 @@ public function testCurrent($resultSets) $document = $resultSet->current(); $this->assertInstanceOf(__NAMESPACE__ . '\MyTestDocument', $document); $this->assertSame($data + ['id' => 99], $document->toArray()); - $this->assertFalse($document->dirty()); + $this->assertFalse($document->isDirty()); $this->assertFalse($document->isNew()); $this->assertEquals('things', $document->type()); } @@ -114,7 +114,7 @@ public function testDecoratedMethods() ->setConstructorArgs([$type]) ->getMock(); - $query->expects($this->once())->method('repository') + $query->expects($this->once())->method('getRepository') ->will($this->returnValue($type)); $requireParam = ['getAggregation' => 'foo']; diff --git a/tests/TestCase/View/Form/DocumentContextTest.php b/tests/TestCase/View/Form/DocumentContextTest.php index 409be686..d475b30b 100644 --- a/tests/TestCase/View/Form/DocumentContextTest.php +++ b/tests/TestCase/View/Form/DocumentContextTest.php @@ -22,7 +22,7 @@ use Cake\ElasticSearch\Index; use Cake\ElasticSearch\IndexRegistry; use Cake\ElasticSearch\View\Form\DocumentContext; -use Cake\Network\Request; +use Cake\Http\ServerRequest; use Cake\TestSuite\TestCase; use Cake\Validation\Validator; @@ -57,7 +57,7 @@ class DocumentContextTest extends TestCase public function setUp() { parent::setUp(); - $this->request = new Request(); + $this->request = new ServerRequest(); $this->textField = 'text'; } @@ -139,14 +139,14 @@ public static function collectionProvider() 'body' => 'Stuff', 'user' => new Document(['username' => 'mark']) ]); - $one->errors('title', 'Required field'); + $one->setError('title', 'Required field'); $two = new Article([ 'title' => 'Second post', 'body' => 'Some text', 'user' => new Document(['username' => 'jose']) ]); - $two->errors('body', 'Not long enough'); + $two->setError('body', 'Not long enough'); return [ 'array' => [[$one, $two]], @@ -334,11 +334,11 @@ public function testError() 'title' => 'My title', 'user' => new Document(['username' => 'Mark']), ]); - $row->errors('title', []); - $row->errors('body', 'Gotta have one'); - $row->errors('user_id', ['Required field']); + $row->setError('title', []); + $row->setError('body', 'Gotta have one'); + $row->setError('user_id', ['Required field']); - $row->user->errors('username', ['Required']); + $row->user->setError('username', ['Required']); $context = new DocumentContext($this->request, [ 'entity' => $row, @@ -370,8 +370,8 @@ public function testErrorAssociatedHasMany() new Document(['comment' => 'Second comment']), ] ]); - $row->comments[0]->errors('comment', ['Is required']); - $row->comments[0]->errors('article_id', ['Is required']); + $row->comments[0]->setError('comment', ['Is required']); + $row->comments[0]->setError('article_id', ['Is required']); $context = new DocumentContext($this->request, [ 'entity' => $row, @@ -465,7 +465,7 @@ protected function setupIndex() $articles->embedOne('User'); $articles->embedMany('Comments'); - $articles->validator()->add('title', 'notblank', [ + $articles->getValidator()->add('title', 'notblank', [ 'rule' => 'notBlank' ]); @@ -473,7 +473,7 @@ protected function setupIndex() $validator->add('body', 'notblank', [ 'rule' => 'notBlank' ]); - $articles->validator('alternate', $validator); + $articles->setValidator('alternate', $validator); return $articles; } diff --git a/tests/init.php b/tests/init.php index 4ca895df..ece0e8ad 100644 --- a/tests/init.php +++ b/tests/init.php @@ -31,7 +31,7 @@ ] ]); -Cache::config('_cake_core_', [ +Cache::setConfig('_cake_core_', [ 'className' => 'File', 'path' => sys_get_temp_dir(), ]); @@ -40,5 +40,5 @@ putenv('db_dsn=Cake\ElasticSearch\Datasource\Connection://127.0.0.1:9200?driver=Cake\ElasticSearch\Datasource\Connection'); } -ConnectionManager::config('test', ['url' => getenv('db_dsn')]); -ConnectionManager::config('test_elastic', ['url' => getenv('db_dsn')]); +ConnectionManager::setConfig('test', ['url' => getenv('db_dsn')]); +ConnectionManager::setConfig('test_elastic', ['url' => getenv('db_dsn')]); From dce44482f2ccab3a9e8049dcd14f524c797df285 Mon Sep 17 00:00:00 2001 From: Jasper Smet Date: Sat, 12 May 2018 12:50:56 +0200 Subject: [PATCH 07/19] Revert Connection getLogger/setLogger for now as these cause build errors on php 7.1 (due to overriding Elastica's setLogger() method --- src/Datasource/Connection.php | 44 ++++++----------------------------- 1 file changed, 7 insertions(+), 37 deletions(-) diff --git a/src/Datasource/Connection.php b/src/Datasource/Connection.php index 3584a886..bc2f20ee 100644 --- a/src/Datasource/Connection.php +++ b/src/Datasource/Connection.php @@ -150,47 +150,15 @@ public function config() * * @param object $instance logger object instance * @return object logger instance - * @deprecated use setLogger()/getLogger instead */ public function logger($instance = null) { - deprecationWarning( - 'Connection::logger() is deprecated. ' . - 'Use Connection::setLogger()/getLogger() instead.' - ); - if ($instance === null) { return $this->_logger; } $this->_logger = $instance; } - /** - * Sets the logger object instance. - * - * @param object $instance logger object instance - * @return void - */ - public function setLogger($instance) - { - $this->_logger = $instance; - } - - /** - * Get the logger object instance. - - * @param object $instance logger object instance - * @return object logger instance - */ - public function getLogger($instance = null) - { - if ($instance === null) { - $this->_logger = Log::engine('elasticsearch') ?: new ElasticaLog(); - } - - return $this->_logger; - } - /** * Log requests to Elastic Search. * @@ -203,6 +171,10 @@ protected function _log($context) return; } + if (!isset($this->_logger) || $this->_logger instanceof NullLogger) { + $this->_logger = Log::engine('elasticsearch') ?: new ElasticaLog(); + } + if ($context instanceof Request) { $contextArray = $context->toArray(); $logData = [ @@ -220,12 +192,10 @@ protected function _log($context) $loggedQuery = new LoggedQuery(); $loggedQuery->query = $data; - $logger = $this->getLogger(); - - if ($logger instanceof \Psr\Log\LoggerInterface) { - $logger->log('debug', $loggedQuery); + if ($this->_logger instanceof \Psr\Log\LoggerInterface) { + $this->_logger->log('debug', $loggedQuery); } else { - $logger->log($loggedQuery); + $this->_logger->log($loggedQuery); } } } From a29d00fbfd9925977752726e79f901c159dd9fad Mon Sep 17 00:00:00 2001 From: Jasper Smet Date: Sat, 12 May 2018 13:22:27 +0200 Subject: [PATCH 08/19] Remove bootstrap.php move to the new Plugin style Update documentation accordingly Also mention possibility to use a connection URL --- README.md | 30 ++++++++++++++++++++----- config/bootstrap.php | 37 ------------------------------- src/Plugin.php | 52 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 42 deletions(-) delete mode 100644 config/bootstrap.php create mode 100644 src/Plugin.php diff --git a/README.md b/README.md index 166c7e68..151f44b9 100644 --- a/README.md +++ b/README.md @@ -26,12 +26,19 @@ And run `php composer.phar update` After installing, you should tell your application to load the plugin: ```php -// in config/bootstrap.php -Plugin::load('Cake/ElasticSearch'); +use Cake\ElasticSearch\Plugin as ElasticSearchPlugin; -// If you want the plugin to automatically configure the Elastic model provider -// and FormHelper do the following: -Plugin::load('Cake/ElasticSearch', ['bootstrap' => true]); +class Application extends BaseApplication +{ + public function bootstrap() + { + $this->addPlugin(ElasticSearchPlugin::class); + + // If you want the plugin to automatically configure the Elastic model provider + // and FormHelper do the following: + // $this->addPlugin(ElasticSearchPlugin::class, [ 'bootstrap' => true ]); + } +} ``` ## Defining a connection @@ -51,6 +58,19 @@ a connection: ], ] ``` +As an alternative you could use a link format if you like to use enviroment variables for example. + +```php + 'Datasources' => [ + // other datasources + 'elastic' => [ + 'url' => env('ELASTIC_URL', null) + ] + ] + + // and make sure the folowing env variable is available: + // ELASTIC_URL="Cake\ElasticSearch\Datasource\Connection://127.0.0.1:9200?driver=Cake\ElasticSearch\Datasource\Connection" +``` You can enable request logging by setting the `log` config option to true. By default, `Elastica\Log` will be used, which logs via `error_log`. You can also diff --git a/config/bootstrap.php b/config/bootstrap.php deleted file mode 100644 index e9adf1be..00000000 --- a/config/bootstrap.php +++ /dev/null @@ -1,37 +0,0 @@ -on('View.beforeRender', function ($event) { - $view = $event->subject(); - $view->Form->addContextProvider('elastic', function ($request, $data) { - $first = null; - if (is_array($data['entity']) || $data['entity'] instanceof Traversable) { - $first = (new Collection($data['entity']))->first(); - } - if ($data['entity'] instanceof Document || $first instanceof Document) { - return new DocumentContext($request, $data); - } - }); -}); diff --git a/src/Plugin.php b/src/Plugin.php new file mode 100644 index 00000000..ab6c8d63 --- /dev/null +++ b/src/Plugin.php @@ -0,0 +1,52 @@ +on('View.beforeRender', function ($event) { + $view = $event->getSubject(); + $view->Form->addContextProvider('elastic', function ($request, $data) { + $first = null; + if (is_array($data['entity']) || $data['entity'] instanceof Traversable) { + $first = (new Collection($data['entity']))->first(); + } + if ($data['entity'] instanceof Document || $first instanceof Document) { + return new DocumentContext($request, $data); + } + }); + }); + } +} From 517e6f669cd64769ebb72deab4cda35c627934e5 Mon Sep 17 00:00:00 2001 From: Jasper Smet Date: Sat, 12 May 2018 14:02:28 +0200 Subject: [PATCH 09/19] Improve docs Remove debug statement --- .gitignore | 1 + README.md | 20 +++++++++++++++++++- src/Plugin.php | 1 + src/TestSuite/TestFixture.php | 1 - 4 files changed, 21 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index e707314c..a5136e29 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ composer.lock +phpunit.xml vendor .vscode .env diff --git a/README.md b/README.md index 151f44b9..7e792053 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ You can install Elasticsearch into your project using following to your `composer.json` file: "require": { - "cakephp/elastic-search": "^1.0" + "cakephp/elastic-search": "^2.0-beta" } And run `php composer.phar update` @@ -61,6 +61,7 @@ a connection: As an alternative you could use a link format if you like to use enviroment variables for example. ```php +// in config/app.php 'Datasources' => [ // other datasources 'elastic' => [ @@ -88,6 +89,23 @@ use Cake\ElasticSearch\IndexRegistry; $comments = IndexRegistry::get('Comments'); ``` +If you have loaded the plugin with bootstrap enabled you could load indexes using the model factory in your controllers +```php +class SomeController extends AppController +{ + public function initialize() + { + $this->loadModel('Comments', 'elastic'); + } + + public function index() + { + $comments = $this->Comments->find(); + } + + ... +``` + Each `Index` object need a correspondent Elasticsearch _index_, just like most of `ORM\Table` needs a database _table_. In the above example, if you have defined a class as `CommentsIndex` and the `IndexRegistry` can found it, the `$comments` will receive a initialized object with inner configurations of connection and index. But if you don't have that class, a default one will be initialized and the index name on Elasticsearch mapped to the class. diff --git a/src/Plugin.php b/src/Plugin.php index ab6c8d63..4150fc9a 100644 --- a/src/Plugin.php +++ b/src/Plugin.php @@ -16,6 +16,7 @@ use Cake\Collection\Collection; use Cake\Core\BasePlugin; +use Cake\Core\PluginApplicationInterface; use Cake\Datasource\FactoryLocator; use Cake\ElasticSearch\Document; use Cake\ElasticSearch\View\Form\DocumentContext; diff --git a/src/TestSuite/TestFixture.php b/src/TestSuite/TestFixture.php index 32a448c9..e778bf7c 100644 --- a/src/TestSuite/TestFixture.php +++ b/src/TestSuite/TestFixture.php @@ -77,7 +77,6 @@ class TestFixture implements FixtureInterface */ public function create(ConnectionInterface $db) { - debug($db); if (empty($this->schema)) { return; } From 5991de709d88184d1ec9300b8b635a2f24047a5b Mon Sep 17 00:00:00 2001 From: Jasper Smet Date: Sun, 13 May 2018 11:46:59 +0200 Subject: [PATCH 10/19] RulesChecker use should be from Datasource, not ORM --- src/Index.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Index.php b/src/Index.php index e57e39f3..757fde6b 100644 --- a/src/Index.php +++ b/src/Index.php @@ -19,6 +19,7 @@ use Cake\Datasource\EntityInterface; use Cake\Datasource\RepositoryInterface; use Cake\Datasource\RulesAwareTrait; +use Cake\Datasource\RulesChecker; use Cake\ElasticSearch\Association\EmbedMany; use Cake\ElasticSearch\Association\EmbedOne; use Cake\ElasticSearch\Datasource\MappingSchema; @@ -26,7 +27,6 @@ use Cake\Event\EventDispatcherTrait; use Cake\Event\EventListenerInterface; use Cake\Event\EventManager; -use Cake\ORM\RulesChecker; use Cake\Utility\Inflector; use Cake\Validation\ValidatorAwareTrait; use Elastica\Document as ElasticaDocument; From 6dbe3ce1880534be0667d9cd7ed1dca9f580b09f Mon Sep 17 00:00:00 2001 From: Jasper Smet Date: Sun, 13 May 2018 16:34:38 +0200 Subject: [PATCH 11/19] Pass QueryLogger to Psr\Log\LoggerInterface compatible Adapter --- src/Datasource/Connection.php | 128 +++++++++++++------ src/Datasource/Log/QueryLoggerAdapter.php | 68 ++++++++++ tests/TestCase/Datasource/ConnectionTest.php | 22 +++- 3 files changed, 180 insertions(+), 38 deletions(-) create mode 100644 src/Datasource/Log/QueryLoggerAdapter.php diff --git a/src/Datasource/Connection.php b/src/Datasource/Connection.php index bc2f20ee..d84fd6fb 100644 --- a/src/Datasource/Connection.php +++ b/src/Datasource/Connection.php @@ -16,13 +16,14 @@ use Cake\Database\Log\LoggedQuery; use Cake\Datasource\ConnectionInterface; +use Cake\ElasticSearch\Datasource\Log\QueryLoggerAdapter; use Cake\Log\Log; -use Elastica\Client; +use Elastica\Client as ElasticaClient; use Elastica\Log as ElasticaLog; use Elastica\Request; use Psr\Log\NullLogger; -class Connection extends Client implements ConnectionInterface +class Connection implements ConnectionInterface { /** * Whether or not query logging is enabled. @@ -38,6 +39,27 @@ class Connection extends Client implements ConnectionInterface */ protected $configName = ''; + /** + * Elastica client instance + * + * @var \Elastica\Client; + */ + protected $_client; + + /** + * Logger object instance. + * + * @var \Cake\Database\Log\QueryLogger|\Psr\Log\LoggerInterface + */ + protected $_logger; + + /** + * NullLooger object instance + * + * @var \Psr\Log\NullLogger + */ + protected $_nullLogger; + /** * Constructor. * @@ -53,7 +75,25 @@ public function __construct(array $config = [], $callback = null) if (isset($config['log'])) { $this->logQueries((bool)$config['log']); } - parent::__construct($config, $callback); + + $this->_client = new ElasticaClient($config, $callback, $this->getLogger()); + } + + /** + * Pass remaining methods to the elastica client (if they exist) + * And set the current logger based on current logQueries value + * + * @param string $name Method name + * @param array $attributes Method attributes + * @return mixed + */ + public function __call($name, $attributes) + { + if (method_exists($this->_client, $name)) { + $this->_client->setLogger($this->getLogger()); + + return call_user_func_array([$this->_client, $name], $attributes); + } } /** @@ -112,6 +152,7 @@ public function logQueries($enable = null) if ($enable === null) { return $this->logQueries; } + $this->logQueries = $enable; } @@ -141,61 +182,74 @@ public function disableConstraints(callable $callback) */ public function config() { - return $this->_config; + return $this->_client->getConfig(); } /** - * Sets the logger object instance. When called with no arguments - * it returns the currently setup logger instance. + * Sets a logger * - * @param object $instance logger object instance - * @return object logger instance + * @param \Cake\Database\Log\QueryLogger|\Psr\Log\LoggerInterface $logger Logger instance + * @return $this */ - public function logger($instance = null) + public function setLogger($logger) { - if ($instance === null) { - return $this->_logger; + $this->_logger = $logger; + + if ($this->_logger instanceof \Cake\Database\Log\QueryLogger) { + $this->_logger = new QueryLoggerAdapter($this->_logger); } - $this->_logger = $instance; + + return $this; } /** - * Log requests to Elastic Search. + * Get the logger object * - * @param Request|array $context The context of the request made. - * @return void + * @return \Cake\Database\Log\QueryLogger logger instance */ - protected function _log($context) + public function getLogger() { - if (!$this->logQueries) { - return; + if (!$this->logQueries()) { + return $this->getNullLogger(); } - if (!isset($this->_logger) || $this->_logger instanceof NullLogger) { + if ($this->_logger === null) { $this->_logger = Log::engine('elasticsearch') ?: new ElasticaLog(); } - if ($context instanceof Request) { - $contextArray = $context->toArray(); - $logData = [ - 'method' => $contextArray['method'], - 'path' => $contextArray['path'], - 'data' => $contextArray['data'] - ]; - } elseif ($context instanceof \Exception) { - $logData = ['message' => $context->getMessage()]; - } else { - $logData = ['message' => 'Unknown']; + return $this->_logger; + } + + /** + * Return instance of the NullLogger + * + * @return \Psr\Log\NullLogger + */ + public function getNullLogger() + { + if (!$this->_nullLogger) { + $this->_nullLogger = new NullLogger; } - $data = json_encode($logData, JSON_PRETTY_PRINT); - $loggedQuery = new LoggedQuery(); - $loggedQuery->query = $data; + return $this->_nullLogger; + } + + /** + * {@inheritDoc} + * + * @deprecated Use getLogger() and setLogger() instead. + */ + public function logger($instance = null) + { + deprecationWarning( + 'Connection::logger() is deprecated. ' . + 'Use Connection::setLogger()/getLogger() instead.' + ); - if ($this->_logger instanceof \Psr\Log\LoggerInterface) { - $this->_logger->log('debug', $loggedQuery); - } else { - $this->_logger->log($loggedQuery); + if ($instance === null) { + return $this->getLogger(); } + + $this->setLogger($instance); } } diff --git a/src/Datasource/Log/QueryLoggerAdapter.php b/src/Datasource/Log/QueryLoggerAdapter.php new file mode 100644 index 00000000..10475fda --- /dev/null +++ b/src/Datasource/Log/QueryLoggerAdapter.php @@ -0,0 +1,68 @@ +_queryLogger = $logger; + } + + /** + * Format log messages from the Elastica client _log method + * + * @param string $level The log level + * @param string $message The log message + * @param array $context log context + * @return void + */ + public function log($level, $message, array $context = []) + { + $logData = $context; + + if (LogLevel::DEBUG && isset($context['request'])) { + $logData = [ + 'method' => $context['request']['method'], + 'path' => $context['request']['path'], + 'data' => $context['request']['data'] + ]; + } + + $loggedQuery = new LoggedQuery(); + $loggedQuery->query = json_encode($logData, JSON_PRETTY_PRINT); + + $this->_queryLogger->log($loggedQuery); + } +} diff --git a/tests/TestCase/Datasource/ConnectionTest.php b/tests/TestCase/Datasource/ConnectionTest.php index 72220e5a..4cb3891d 100644 --- a/tests/TestCase/Datasource/ConnectionTest.php +++ b/tests/TestCase/Datasource/ConnectionTest.php @@ -75,10 +75,11 @@ public function testConstructLogOption() * * @return void */ - public function testQueryLogging() + public function testQueryLoggingWithBaseLog() { $logger = $this->getMockBuilder('Cake\Log\Engine\BaseLog')->setMethods(['log'])->getMock(); $logger->expects($this->once())->method('log'); + Log::setConfig('elasticsearch', $logger); $connection = ConnectionManager::get('test'); @@ -88,4 +89,23 @@ public function testQueryLogging() $this->assertNotEmpty($result); } + + /** + * Ensure that logging queries works. + * + * @return void + */ + public function testQueryLogger() + { + $logger = $this->getMockBuilder('Cake\Database\Log\QueryLogger')->setMethods(['log'])->getMock(); + $logger->expects($this->once())->method('log'); + + $connection = ConnectionManager::get('test'); + $connection->setLogger($logger); + $connection->logQueries(true); + $result = $connection->request('_stats'); + $connection->logQueries(false); + + $this->assertNotEmpty($result); + } } From 5f79c07d409bfde0a5a48e3f52cffea85711fa83 Mon Sep 17 00:00:00 2001 From: Jasper Smet Date: Mon, 14 May 2018 11:46:11 +0200 Subject: [PATCH 12/19] Fix docblock --- src/Datasource/Log/QueryLoggerAdapter.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Datasource/Log/QueryLoggerAdapter.php b/src/Datasource/Log/QueryLoggerAdapter.php index 10475fda..dc1bd2af 100644 --- a/src/Datasource/Log/QueryLoggerAdapter.php +++ b/src/Datasource/Log/QueryLoggerAdapter.php @@ -27,13 +27,13 @@ class QueryLoggerAdapter extends AbstractLogger /** * Holds the QueryLogger instance * - * @var \Cake\Database\Log\LoggedQuery + * @var \Cake\Database\Log\QueryLogger */ protected $_queryLogger; /** * Constructor, set the QueryLogger instance - * @param \Cake\Database\Log\LoggedQuery $logger Instance of the QueryLogger + * @param \Cake\Database\Log\QueryLogger $logger Instance of the QueryLogger */ public function __construct(QueryLogger $logger) { From 7976b0e28dfcd4f296ded9a1e8f3281e0ee8ac10 Mon Sep 17 00:00:00 2001 From: Jasper Smet Date: Mon, 14 May 2018 14:55:57 +0200 Subject: [PATCH 13/19] Update docs, plugins bootstrap by default these days --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7e792053..b04ef4e3 100644 --- a/README.md +++ b/README.md @@ -34,9 +34,9 @@ class Application extends BaseApplication { $this->addPlugin(ElasticSearchPlugin::class); - // If you want the plugin to automatically configure the Elastic model provider + // If you want to disable to automatically configure the Elastic model provider // and FormHelper do the following: - // $this->addPlugin(ElasticSearchPlugin::class, [ 'bootstrap' => true ]); + // $this->addPlugin(ElasticSearchPlugin::class, [ 'bootstrap' => false ]); } } ``` From 41d86c2eb2ba7fdf63c6763517a17f43f1eb328a Mon Sep 17 00:00:00 2001 From: Jasper Smet Date: Tue, 15 May 2018 12:18:00 +0200 Subject: [PATCH 14/19] Add some extra assertions for log log messages --- .gitignore | 33 +++++++++++++++++++- tests/TestCase/Datasource/ConnectionTest.php | 16 +++++++++- 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index a5136e29..157080ec 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,36 @@ composer.lock phpunit.xml vendor -.vscode .env +composer.phar + +# Tool specific files # +####################### +# vim +*~ +*.swp +*.swo +# sublime text & textmate +*.sublime-* +*.stTheme.cache +*.tmlanguage.cache +*.tmPreferences.cache +# Eclipse +.settings/* +# JetBrains, aka PHPStorm, IntelliJ IDEA +.idea/* +# NetBeans +nbproject/* +# Visual Studio Code +.vscode + +# OS generated files # +###################### +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +Icon? +ehthumbs.db +Thumbs.db diff --git a/tests/TestCase/Datasource/ConnectionTest.php b/tests/TestCase/Datasource/ConnectionTest.php index 4cb3891d..23924ab5 100644 --- a/tests/TestCase/Datasource/ConnectionTest.php +++ b/tests/TestCase/Datasource/ConnectionTest.php @@ -14,6 +14,7 @@ */ namespace Cake\ElasticSearch\Test\Datasource; +use Cake\Database\Log\LoggedQuery; use Cake\Datasource\ConnectionManager; use Cake\ElasticSearch\Datasource\Connection; use Cake\Log\Log; @@ -78,7 +79,11 @@ public function testConstructLogOption() public function testQueryLoggingWithBaseLog() { $logger = $this->getMockBuilder('Cake\Log\Engine\BaseLog')->setMethods(['log'])->getMock(); - $logger->expects($this->once())->method('log'); + $logger->expects($this->once())->method('log')->with( + $this->equalTo('debug'), + $this->equalTo('Elastica Request'), + $this->anything() + ); Log::setConfig('elasticsearch', $logger); @@ -100,6 +105,15 @@ public function testQueryLogger() $logger = $this->getMockBuilder('Cake\Database\Log\QueryLogger')->setMethods(['log'])->getMock(); $logger->expects($this->once())->method('log'); + $query = new LoggedQuery; + $query->query = json_encode([ + 'method' => 'GET', + 'path' => '_stats', + 'data' => [] + ], JSON_PRETTY_PRINT); + + $logger->expects($this->once())->method('log')->with($query); + $connection = ConnectionManager::get('test'); $connection->setLogger($logger); $connection->logQueries(true); From 80e64efbf850494ebbd49ec0d81b26f2a2ea96a8 Mon Sep 17 00:00:00 2001 From: Jasper Smet Date: Tue, 15 May 2018 18:47:09 +0200 Subject: [PATCH 15/19] A custom (ElasticLogger) logger is now always used to format messages coming from the ES client. --- src/Datasource/Connection.php | 44 ++---- src/Datasource/Log/ElasticLogger.php | 133 +++++++++++++++++++ src/Datasource/Log/QueryLoggerAdapter.php | 68 ---------- tests/TestCase/Datasource/ConnectionTest.php | 10 +- 4 files changed, 150 insertions(+), 105 deletions(-) create mode 100644 src/Datasource/Log/ElasticLogger.php delete mode 100644 src/Datasource/Log/QueryLoggerAdapter.php diff --git a/src/Datasource/Connection.php b/src/Datasource/Connection.php index d84fd6fb..9d1c9846 100644 --- a/src/Datasource/Connection.php +++ b/src/Datasource/Connection.php @@ -16,12 +16,11 @@ use Cake\Database\Log\LoggedQuery; use Cake\Datasource\ConnectionInterface; -use Cake\ElasticSearch\Datasource\Log\QueryLoggerAdapter; +use Cake\ElasticSearch\Datasource\Log\ElasticLogger; +use Cake\Log\Engine\FileLog; use Cake\Log\Log; use Elastica\Client as ElasticaClient; -use Elastica\Log as ElasticaLog; use Elastica\Request; -use Psr\Log\NullLogger; class Connection implements ConnectionInterface { @@ -53,13 +52,6 @@ class Connection implements ConnectionInterface */ protected $_logger; - /** - * NullLooger object instance - * - * @var \Psr\Log\NullLogger - */ - protected $_nullLogger; - /** * Constructor. * @@ -90,8 +82,6 @@ public function __construct(array $config = [], $callback = null) public function __call($name, $attributes) { if (method_exists($this->_client, $name)) { - $this->_client->setLogger($this->getLogger()); - return call_user_func_array([$this->_client, $name], $attributes); } } @@ -188,17 +178,19 @@ public function config() /** * Sets a logger * - * @param \Cake\Database\Log\QueryLogger|\Psr\Log\LoggerInterface $logger Logger instance + * @param \Cake\Database\Log\QueryLogger|\Cake\Log\Engine\BaseLog $logger Logger instance * @return $this */ public function setLogger($logger) { - $this->_logger = $logger; + if (!$this->_logger) { + $this->_logger = new ElasticLogger($logger, $this); - if ($this->_logger instanceof \Cake\Database\Log\QueryLogger) { - $this->_logger = new QueryLoggerAdapter($this->_logger); + return $this; } + $this->_logger->setLogger($logger); + return $this; } @@ -209,31 +201,13 @@ public function setLogger($logger) */ public function getLogger() { - if (!$this->logQueries()) { - return $this->getNullLogger(); - } - if ($this->_logger === null) { - $this->_logger = Log::engine('elasticsearch') ?: new ElasticaLog(); + $this->setLogger(Log::engine('elasticsearch') ?: new FileLog); } return $this->_logger; } - /** - * Return instance of the NullLogger - * - * @return \Psr\Log\NullLogger - */ - public function getNullLogger() - { - if (!$this->_nullLogger) { - $this->_nullLogger = new NullLogger; - } - - return $this->_nullLogger; - } - /** * {@inheritDoc} * diff --git a/src/Datasource/Log/ElasticLogger.php b/src/Datasource/Log/ElasticLogger.php new file mode 100644 index 00000000..744c253c --- /dev/null +++ b/src/Datasource/Log/ElasticLogger.php @@ -0,0 +1,133 @@ +setLogger($logger); + $this->_connection = $connection; + } + + /** + * Set the current cake logger + * + * @param \Cake\Database\Log\QueryLogger|\Cake\Log\Engine\BaseLog $logger Set logger instance to pass logging data to + * @return $this + */ + public function setLogger($logger) + { + $this->_logger = $logger; + + return $this; + } + + /** + * Return the current logger + * @return \Cake\Database\Log\QueryLogger|\Cake\Log\Engine\BaseLog|\Psr\Log\NullLogger [description] + */ + public function getLogger() + { + return $this->_logger; + } + + /** + * Format log messages from the Elastica client _log method + * + * @param string $level The log level + * @param string $message The log message + * @param array $context log context + * @return void + */ + public function log($level, $message, array $context = []) + { + if ($this->_connection->logQueries()) { + $this->_log($level, $message, $context); + } + } + + /** + * Format log messages from the Elastica client and pass + * them to the cake defined logger instance + * + * Elastica's log parameters + * ------------------------- + * error: + * message: "Elastica Request Failure" + * context: [ exception, request, retry ] + * debug (request): + * message: "Elastica Request" + * context: [ request, response, responseStatus ] + * debug (fallback?): + * message: "Elastica Request" + * context: [ message ] + * + * @param string $level The log level + * @param string $message The log message + * @param array $context log context + * @return void + */ + protected function _log($level, $message, array $context = []) + { + $logData = $context; + $logger = $this->getLogger(); + + if (LogLevel::DEBUG && isset($context['request'])) { + $logData = [ + 'method' => $context['request']['method'], + 'path' => $context['request']['path'], + 'data' => $context['request']['data'] + ]; + } + + $logData = json_encode($logData, JSON_PRETTY_PRINT); + + if ($logger instanceof QueryLogger) { + $message = new LoggedQuery(); + $message->query = $logData; + $logger->log($message); + } else { + $logger->log($level, $logData, $context); + } + } +} diff --git a/src/Datasource/Log/QueryLoggerAdapter.php b/src/Datasource/Log/QueryLoggerAdapter.php deleted file mode 100644 index dc1bd2af..00000000 --- a/src/Datasource/Log/QueryLoggerAdapter.php +++ /dev/null @@ -1,68 +0,0 @@ -_queryLogger = $logger; - } - - /** - * Format log messages from the Elastica client _log method - * - * @param string $level The log level - * @param string $message The log message - * @param array $context log context - * @return void - */ - public function log($level, $message, array $context = []) - { - $logData = $context; - - if (LogLevel::DEBUG && isset($context['request'])) { - $logData = [ - 'method' => $context['request']['method'], - 'path' => $context['request']['path'], - 'data' => $context['request']['data'] - ]; - } - - $loggedQuery = new LoggedQuery(); - $loggedQuery->query = json_encode($logData, JSON_PRETTY_PRINT); - - $this->_queryLogger->log($loggedQuery); - } -} diff --git a/tests/TestCase/Datasource/ConnectionTest.php b/tests/TestCase/Datasource/ConnectionTest.php index 23924ab5..03eac42a 100644 --- a/tests/TestCase/Datasource/ConnectionTest.php +++ b/tests/TestCase/Datasource/ConnectionTest.php @@ -79,10 +79,16 @@ public function testConstructLogOption() public function testQueryLoggingWithBaseLog() { $logger = $this->getMockBuilder('Cake\Log\Engine\BaseLog')->setMethods(['log'])->getMock(); + + $message = json_encode([ + 'method' => 'GET', + 'path' => '_stats', + 'data' => [] + ], JSON_PRETTY_PRINT); + $logger->expects($this->once())->method('log')->with( $this->equalTo('debug'), - $this->equalTo('Elastica Request'), - $this->anything() + $this->equalTo($message) ); Log::setConfig('elasticsearch', $logger); From 916fd42344abc55a1d068eab500fc464142b81ae Mon Sep 17 00:00:00 2001 From: Jasper Smet Date: Tue, 15 May 2018 19:13:36 +0200 Subject: [PATCH 16/19] Set default logging engine to 'debug' and improve docs to reflect changes --- README.md | 8 ++++---- src/Datasource/Connection.php | 7 ++++++- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index b04ef4e3..6d83e013 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ As an alternative you could use a link format if you like to use enviroment vari ``` You can enable request logging by setting the `log` config option to true. By -default, `Elastica\Log` will be used, which logs via `error_log`. You can also +default the `debug` Log profile will be used. You can also define an `elasticsearch` log profile in `Cake\Log\Log` to customize where Elasticsearch query logs will go. Query logging is done at a 'debug' level. @@ -106,13 +106,13 @@ class SomeController extends AppController ... ``` -Each `Index` object need a correspondent Elasticsearch _index_, just like most of `ORM\Table` needs a database _table_. +Each `Index` object needs a correspondent Elasticsearch _index_, just like most of `ORM\Table` needs a database _table_. -In the above example, if you have defined a class as `CommentsIndex` and the `IndexRegistry` can found it, the `$comments` will receive a initialized object with inner configurations of connection and index. But if you don't have that class, a default one will be initialized and the index name on Elasticsearch mapped to the class. +In the above example, if you have defined a class as `CommentsIndex` and the `IndexRegistry` can find it, the `$comments` will receive a initialized object with inner configurations of connection and index. But if you don't have that class, a default one will be initialized and the index name on Elasticsearch mapped to the class. ## Defining a Index class -Creating your own `Index` allow you to define name of internal _index_ of Elasticsearch, and it mapping type. As you has to [use only one mapping type for each _index_](https://www.elastic.co/guide/en/elasticsearch/reference/master/removal-of-types.html), you can use the same name for both (this is the default behavior when _type_ is undefined). +Creating your own `Index` allows you to define the name of internal _index_ for Elasticsearch, and it mapping type. As you have to [use only one mapping type for each _index_](https://www.elastic.co/guide/en/elasticsearch/reference/master/removal-of-types.html), you can use the same name for both (this is the default behavior when _type_ is undefined). Index types will be removed from ES 7 and up. ```php use Cake\ElasticSearch\Index; diff --git a/src/Datasource/Connection.php b/src/Datasource/Connection.php index 9d1c9846..bb92ce5c 100644 --- a/src/Datasource/Connection.php +++ b/src/Datasource/Connection.php @@ -202,7 +202,12 @@ public function setLogger($logger) public function getLogger() { if ($this->_logger === null) { - $this->setLogger(Log::engine('elasticsearch') ?: new FileLog); + $engine = Log::engine('elasticsearch'); + if (!$engine) { + $engine = Log::engine('debug'); + } + + $this->setLogger($engine); } return $this->_logger; From d4f948c067c6db23f60753ae9742a378acdaade0 Mon Sep 17 00:00:00 2001 From: Jasper Smet Date: Tue, 15 May 2018 20:39:40 +0200 Subject: [PATCH 17/19] getLogger should return the logger that was set in setLogger, not the ElasticLogger instance --- src/Datasource/Connection.php | 39 +++++++++++++++----- src/Datasource/Log/ElasticLogger.php | 7 ++-- tests/TestCase/Datasource/ConnectionTest.php | 8 ++-- tests/init.php | 11 ++++++ 4 files changed, 48 insertions(+), 17 deletions(-) diff --git a/src/Datasource/Connection.php b/src/Datasource/Connection.php index bb92ce5c..0e865706 100644 --- a/src/Datasource/Connection.php +++ b/src/Datasource/Connection.php @@ -20,6 +20,7 @@ use Cake\Log\Engine\FileLog; use Cake\Log\Log; use Elastica\Client as ElasticaClient; +use Elastica\Log as ElasticaLog; use Elastica\Request; class Connection implements ConnectionInterface @@ -52,6 +53,12 @@ class Connection implements ConnectionInterface */ protected $_logger; + /** + * Instance of ElasticLogger + * @var \Cake\ElasticSearch\Datasource\Log\ElasticLogger + */ + protected $_esLogger; + /** * Constructor. * @@ -68,7 +75,7 @@ public function __construct(array $config = [], $callback = null) $this->logQueries((bool)$config['log']); } - $this->_client = new ElasticaClient($config, $callback, $this->getLogger()); + $this->_client = new ElasticaClient($config, $callback, $this->getEsLogger()); } /** @@ -183,28 +190,26 @@ public function config() */ public function setLogger($logger) { - if (!$this->_logger) { - $this->_logger = new ElasticLogger($logger, $this); - - return $this; - } - - $this->_logger->setLogger($logger); + $this->_logger = $logger; + $this->getEsLogger()->setLogger($logger); return $this; } /** * Get the logger object + * Will set the default logger to elasticsearch if found, or debug + * If none of the above are found the default Es logger will be used. * * @return \Cake\Database\Log\QueryLogger logger instance */ public function getLogger() { if ($this->_logger === null) { - $engine = Log::engine('elasticsearch'); + $engine = Log::engine('elasticsearch') ?: Log::engine('debug'); + if (!$engine) { - $engine = Log::engine('debug'); + $engine = new ElasticaLog; } $this->setLogger($engine); @@ -213,6 +218,20 @@ public function getLogger() return $this->_logger; } + /** + * Return instance of ElasticLogger + * + * @return \Cake\ElasticSearch\Datasource\Log\ElasticLogger + */ + public function getEsLogger() + { + if ($this->_esLogger === null) { + $this->_esLogger = new ElasticLogger($this->getLogger(), $this); + } + + return $this->_esLogger; + } + /** * {@inheritDoc} * diff --git a/src/Datasource/Log/ElasticLogger.php b/src/Datasource/Log/ElasticLogger.php index 744c253c..24e2af26 100644 --- a/src/Datasource/Log/ElasticLogger.php +++ b/src/Datasource/Log/ElasticLogger.php @@ -110,7 +110,6 @@ public function log($level, $message, array $context = []) protected function _log($level, $message, array $context = []) { $logData = $context; - $logger = $this->getLogger(); if (LogLevel::DEBUG && isset($context['request'])) { $logData = [ @@ -122,12 +121,12 @@ protected function _log($level, $message, array $context = []) $logData = json_encode($logData, JSON_PRETTY_PRINT); - if ($logger instanceof QueryLogger) { + if ($this->getLogger() instanceof QueryLogger) { $message = new LoggedQuery(); $message->query = $logData; - $logger->log($message); + $this->getLogger()->log($message); } else { - $logger->log($level, $logData, $context); + $this->getLogger()->log($level, $logData, $context); } } } diff --git a/tests/TestCase/Datasource/ConnectionTest.php b/tests/TestCase/Datasource/ConnectionTest.php index 03eac42a..f7fa6ef0 100644 --- a/tests/TestCase/Datasource/ConnectionTest.php +++ b/tests/TestCase/Datasource/ConnectionTest.php @@ -68,7 +68,9 @@ public function testConstructLogOption() $opts = ['log' => true]; $connection = new Connection($opts); + $this->assertTrue($connection->logQueries()); + $this->assertInstanceOf('\Cake\Log\Engine\FileLog', $connection->getLogger()); } /** @@ -76,9 +78,9 @@ public function testConstructLogOption() * * @return void */ - public function testQueryLoggingWithBaseLog() + public function testLoggerFileLog() { - $logger = $this->getMockBuilder('Cake\Log\Engine\BaseLog')->setMethods(['log'])->getMock(); + $logger = $this->getMockBuilder('Cake\Log\Engine\FileLog')->setMethods(['log'])->getMock(); $message = json_encode([ 'method' => 'GET', @@ -106,7 +108,7 @@ public function testQueryLoggingWithBaseLog() * * @return void */ - public function testQueryLogger() + public function testLoggerQueryLogger() { $logger = $this->getMockBuilder('Cake\Database\Log\QueryLogger')->setMethods(['log'])->getMock(); $logger->expects($this->once())->method('log'); diff --git a/tests/init.php b/tests/init.php index ece0e8ad..602ea89a 100644 --- a/tests/init.php +++ b/tests/init.php @@ -19,10 +19,13 @@ require CAKE . 'basics.php'; define('APP', __DIR__); +define('TMP', sys_get_temp_dir() . DS); +define('LOGS', TMP . 'logs' . DS); use Cake\Cache\Cache; use Cake\Core\Configure; use Cake\Datasource\ConnectionManager; +use Cake\Log\Log; Configure::write('App', [ 'namespace' => 'App', @@ -36,6 +39,14 @@ 'path' => sys_get_temp_dir(), ]); +Log::setConfig([ + 'debug' => [ + 'engine' => 'Cake\Log\Engine\FileLog', + 'levels' => ['notice', 'info', 'debug'], + 'file' => 'debug', + ] +]); + if (!getenv('db_dsn')) { putenv('db_dsn=Cake\ElasticSearch\Datasource\Connection://127.0.0.1:9200?driver=Cake\ElasticSearch\Datasource\Connection'); } From 048ffac867148349dbd1bd0fc12cbb693c9e7354 Mon Sep 17 00:00:00 2001 From: Jasper Smet Date: Tue, 15 May 2018 20:50:37 +0200 Subject: [PATCH 18/19] Add took and numRows data to the LoggedQuery --- src/Datasource/Log/ElasticLogger.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/Datasource/Log/ElasticLogger.php b/src/Datasource/Log/ElasticLogger.php index 24e2af26..8398bf81 100644 --- a/src/Datasource/Log/ElasticLogger.php +++ b/src/Datasource/Log/ElasticLogger.php @@ -122,8 +122,19 @@ protected function _log($level, $message, array $context = []) $logData = json_encode($logData, JSON_PRETTY_PRINT); if ($this->getLogger() instanceof QueryLogger) { + $took = $numRows = 0; + if (isset($context['response']['took'])) { + $took = $context['response']['took']; + } + if (isset($context['response']['hits']['total'])) { + $numRows = $context['response']['hits']['total']; + } + $message = new LoggedQuery(); $message->query = $logData; + $message->took = $took; + $message->numRows = $numRows; + $this->getLogger()->log($message); } else { $this->getLogger()->log($level, $logData, $context); From 170d0c6002899db898874be80404f6868f967a92 Mon Sep 17 00:00:00 2001 From: Jasper Smet Date: Tue, 15 May 2018 20:56:36 +0200 Subject: [PATCH 19/19] Fix composer.json, not sure why cake made it in the resuired list --- composer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index e9d588f4..4d59dbae 100644 --- a/composer.json +++ b/composer.json @@ -18,10 +18,10 @@ "source": "https://github.com/cakephp/elastic-search" }, "require": { - "ruflin/elastica": "^6.0", - "cakephp/cakephp": "^3.6" + "ruflin/elastica": "^6.0" }, "require-dev": { + "cakephp/cakephp": "^3.6", "cakephp/cakephp-codesniffer": "^3.0", "psr/log": "~1.0", "phpunit/phpunit": "^6.0"