From 3d8a299b56b4baeb0dae43f558dd30997efdfdb7 Mon Sep 17 00:00:00 2001 From: Dominique Feyer Date: Mon, 16 Nov 2015 14:43:21 +0100 Subject: [PATCH] [TASK] Reformat code to PSR-2 + normalize file header comment --- .styleci.yml | 6 + .../ElasticSearch/Annotations/Indexable.php | 47 +- .../ElasticSearch/Annotations/Mapping.php | 194 +++---- .../ElasticSearch/Annotations/Transform.php | 39 +- .../Command/IndexCommandController.php | 476 +++++++++--------- .../Command/MappingCommandController.php | 372 +++++++------- .../DocumentPropertiesMismatchException.php | 44 +- .../Domain/Factory/ClientFactory.php | 132 ++--- .../Domain/Factory/DocumentFactory.php | 86 ++-- .../Domain/Model/AbstractType.php | 247 ++++----- .../ElasticSearch/Domain/Model/Client.php | 157 +++--- .../Model/Client/ClientConfiguration.php | 147 +++--- .../ElasticSearch/Domain/Model/Document.php | 351 ++++++------- .../Domain/Model/GenericType.php | 38 +- .../ElasticSearch/Domain/Model/Index.php | 445 ++++++++-------- .../ElasticSearch/Domain/Model/Mapping.php | 287 ++++++----- .../ElasticSearch/Domain/Model/TypeGroup.php | 58 +-- Classes/Flowpack/ElasticSearch/Exception.php | 24 +- .../Indexer/Aspect/IndexerAspect.php | 75 +-- .../Indexer/Object/IndexInformer.php | 261 +++++----- .../Indexer/Object/ObjectIndexer.php | 339 +++++++------ .../Object/Signal/Doctrine/EmitterAdapter.php | 84 ++-- .../Object/Signal/EmitterAdapterInterface.php | 23 +- .../Indexer/Object/Signal/SignalEmitter.php | 70 +-- .../CollectionStringCastTransformer.php | 65 +-- .../Object/Transform/DateTransformer.php | 59 +-- .../Transform/ObjectIdentifierTransformer.php | 69 +-- .../Transform/StringCastTransformer.php | 57 +-- .../Object/Transform/TransformerFactory.php | 66 +-- .../Object/Transform/TransformerInterface.php | 49 +- .../Mapping/BackendMappingBuilder.php | 166 +++--- .../Mapping/EntityMappingBuilder.php | 251 ++++----- .../Mapping/MappingCollection.php | 195 +++---- Classes/Flowpack/ElasticSearch/Package.php | 89 ++-- .../ElasticSearch/Transfer/Exception.php | 110 ++-- .../Transfer/Exception/ApiException.php | 23 +- .../ElasticSearch/Transfer/RequestService.php | 114 ++--- .../ElasticSearch/Transfer/Response.php | 127 ++--- Tests/Functional/Domain/AbstractTest.php | 111 ++-- Tests/Functional/Domain/DocumentTest.php | 126 ++--- .../Fixtures/JustFewPropertiesToIndex.php | 50 +- Tests/Functional/Fixtures/Tweet.php | 132 ++--- Tests/Functional/Fixtures/TweetRepository.php | 24 +- Tests/Functional/Fixtures/TwitterType.php | 24 +- .../Indexer/Object/IndexInformerTest.php | 93 ++-- .../Indexer/Object/ObjectIndexerTest.php | 243 ++++----- .../Functional/Mapping/MappingBuilderTest.php | 62 +-- 47 files changed, 3214 insertions(+), 3093 deletions(-) create mode 100644 .styleci.yml diff --git a/.styleci.yml b/.styleci.yml new file mode 100644 index 0000000..14c4d58 --- /dev/null +++ b/.styleci.yml @@ -0,0 +1,6 @@ +preset: psr2 + +finder: + path: + - "Classes" + - "Tests" diff --git a/Classes/Flowpack/ElasticSearch/Annotations/Indexable.php b/Classes/Flowpack/ElasticSearch/Annotations/Indexable.php index c702682..0f54ab1 100644 --- a/Classes/Flowpack/ElasticSearch/Annotations/Indexable.php +++ b/Classes/Flowpack/ElasticSearch/Annotations/Indexable.php @@ -1,15 +1,15 @@ fields; - } -} \ No newline at end of file + /** + * @return array + */ + public function getFields() + { + return $this->fields; + } +} diff --git a/Classes/Flowpack/ElasticSearch/Annotations/Transform.php b/Classes/Flowpack/ElasticSearch/Annotations/Transform.php index b45ee2f..ab091d9 100644 --- a/Classes/Flowpack/ElasticSearch/Annotations/Transform.php +++ b/Classes/Flowpack/ElasticSearch/Annotations/Transform.php @@ -1,15 +1,15 @@ indexInformer->getAllIndexNames())) { + $this->outputFormatted("The index %s is not configured in the current application", array($indexName)); + $this->quit(1); + } - /** - * Create a new index in ElasticSearch - * - * @param string $indexName The name of the new index - * @param string $clientName The client name to use - */ - public function createCommand($indexName, $clientName = NULL) { - if (!in_array($indexName, $this->indexInformer->getAllIndexNames())) { - $this->outputFormatted("The index %s is not configured in the current application", array($indexName)); - $this->quit(1); - } + $client = $this->clientFactory->create($clientName); + try { + $index = new Index($indexName, $client); + if ($index->exists()) { + $this->outputFormatted("The index %s exists", array($indexName)); + $this->quit(1); + } + $index->create(); + $this->outputFormatted("Index %s created with success", array($indexName)); + } catch (Exception $exception) { + $this->outputFormatted("Unable to create an index named: %s", array($indexName)); + $this->quit(1); + } + } - $client = $this->clientFactory->create($clientName); - try { - $index = new Index($indexName, $client); - if ($index->exists()) { - $this->outputFormatted("The index %s exists", array($indexName)); - $this->quit(1); - } - $index->create(); - $this->outputFormatted("Index %s created with success", array($indexName)); - } catch (Exception $exception) { - $this->outputFormatted("Unable to create an index named: %s", array($indexName)); - $this->quit(1); - } - } + /** + * Update index settings + * + * @param string $indexName The name of the new index + * @param string $clientName The client name to use + */ + public function updateSettingsCommand($indexName, $clientName = null) + { + if (!in_array($indexName, $this->indexInformer->getAllIndexNames())) { + $this->outputFormatted("The index %s is not configured in the current application", array($indexName)); + $this->quit(1); + } - /** - * Update index settings - * - * @param string $indexName The name of the new index - * @param string $clientName The client name to use - */ - public function updateSettingsCommand($indexName, $clientName = NULL) { - if (!in_array($indexName, $this->indexInformer->getAllIndexNames())) { - $this->outputFormatted("The index %s is not configured in the current application", array($indexName)); - $this->quit(1); - } + $client = $this->clientFactory->create($clientName); + try { + $index = new Index($indexName, $client); + if (!$index->exists()) { + $this->outputFormatted("The index %s does not exists", array($indexName)); + $this->quit(1); + } + $index->updateSettings(); + $this->outputFormatted("Index settings %s updated with success", array($indexName)); + } catch (Exception $exception) { + $this->outputFormatted("Unable to update settings for %s index", array($indexName)); + $this->quit(1); + } + } - $client = $this->clientFactory->create($clientName); - try { - $index = new Index($indexName, $client); - if (!$index->exists()) { - $this->outputFormatted("The index %s does not exists", array($indexName)); - $this->quit(1); - } - $index->updateSettings(); - $this->outputFormatted("Index settings %s updated with success", array($indexName)); - } catch (Exception $exception) { - $this->outputFormatted("Unable to update settings for %s index", array($indexName)); - $this->quit(1); - } - } + /** + * Delete an index in ElasticSearch + * + * @param string $indexName The name of the index to be removed + * @param string $clientName The client name to use + */ + public function deleteCommand($indexName, $clientName = null) + { + if (!in_array($indexName, $this->indexInformer->getAllIndexNames())) { + $this->outputFormatted("The index %s is not configured in the current application", array($indexName)); + $this->quit(1); + } - /** - * Delete an index in ElasticSearch - * - * @param string $indexName The name of the index to be removed - * @param string $clientName The client name to use - */ - public function deleteCommand($indexName, $clientName = NULL) { - if (!in_array($indexName, $this->indexInformer->getAllIndexNames())) { - $this->outputFormatted("The index %s is not configured in the current application", array($indexName)); - $this->quit(1); - } + $client = $this->clientFactory->create($clientName); + try { + $index = new Index($indexName, $client); + if (!$index->exists()) { + $this->outputFormatted("The index %s does not exists", array($indexName)); + $this->quit(1); + } + $index->delete(); + $this->outputFormatted("Index %s deleted with success", array($indexName)); + } catch (Exception $exception) { + $this->outputFormatted("Unable to delete an index named: %s", array($indexName)); + $this->quit(1); + } + } - $client = $this->clientFactory->create($clientName); - try { - $index = new Index($indexName, $client); - if (!$index->exists()) { - $this->outputFormatted("The index %s does not exists", array($indexName)); - $this->quit(1); - } - $index->delete(); - $this->outputFormatted("Index %s deleted with success", array($indexName)); - } catch (Exception $exception) { - $this->outputFormatted("Unable to delete an index named: %s", array($indexName)); - $this->quit(1); - } - } + /** + * Refresh an index in ElasticSearch + * + * @param string $indexName The name of the index to be removed + * @param string $clientName The client name to use + */ + public function refreshCommand($indexName, $clientName = null) + { + if (!in_array($indexName, $this->indexInformer->getAllIndexNames())) { + $this->outputFormatted("The index %s is not configured in the current application", array($indexName)); + } - /** - * Refresh an index in ElasticSearch - * - * @param string $indexName The name of the index to be removed - * @param string $clientName The client name to use - */ - public function refreshCommand($indexName, $clientName = NULL) { - if (!in_array($indexName, $this->indexInformer->getAllIndexNames())) { - $this->outputFormatted("The index %s is not configured in the current application", array($indexName)); - } + $client = $this->clientFactory->create($clientName); + try { + $index = new Index($indexName, $client); + if (!$index->exists()) { + $this->outputFormatted("The index %s does not exists", array($indexName)); + $this->quit(1); + } + $index->refresh(); + $this->outputFormatted("Index %s refreshed with success", array($indexName)); + } catch (Exception $exception) { + $this->outputFormatted("Unable to refresh an index named: %s", array($indexName)); + $this->quit(1); + } + } - $client = $this->clientFactory->create($clientName); - try { - $index = new Index($indexName, $client); - if (!$index->exists()) { - $this->outputFormatted("The index %s does not exists", array($indexName)); - $this->quit(1); - } - $index->refresh(); - $this->outputFormatted("Index %s refreshed with success", array($indexName)); - } catch (Exception $exception) { - $this->outputFormatted("Unable to refresh an index named: %s", array($indexName)); - $this->quit(1); - } - } + /** + * List available document type + */ + public function showConfiguredTypesCommand() + { + $classesAndAnnotations = $this->indexInformer->getClassesAndAnnotations(); + $this->outputFormatted("Available document type"); + /** @var $annotation \Flowpack\ElasticSearch\Annotations\Indexable */ + foreach ($classesAndAnnotations as $className => $annotation) { + $this->outputFormatted("%s", array($className), 4); + } + } - /** - * List available document type - */ - public function showConfiguredTypesCommand() { - $classesAndAnnotations = $this->indexInformer->getClassesAndAnnotations(); - $this->outputFormatted("Available document type"); - /** @var $annotation \Flowpack\ElasticSearch\Annotations\Indexable */ - foreach ($classesAndAnnotations as $className => $annotation) { - $this->outputFormatted("%s", array($className), 4); - } - } + /** + * Shows the status of the current mapping + * + * @param string $object Class name of a domain object. If given, will only work on this single object + * @param boolean $conductUpdate Set to TRUE to conduct the required corrections + * @param string $clientName The client name to use + */ + public function statusCommand($object = null, $conductUpdate = false, $clientName = null) + { + $result = new ErrorResult(); - /** - * Shows the status of the current mapping - * - * @param string $object Class name of a domain object. If given, will only work on this single object - * @param boolean $conductUpdate Set to TRUE to conduct the required corrections - * @param string $clientName The client name to use - */ - public function statusCommand($object = NULL, $conductUpdate = FALSE, $clientName = NULL) { - $result = new ErrorResult(); + $client = $this->clientFactory->create($clientName); - $client = $this->clientFactory->create($clientName); + $classesAndAnnotations = $this->indexInformer->getClassesAndAnnotations(); + if ($object !== null) { + if (!isset($classesAndAnnotations[$object])) { + $this->outputFormatted("Error: Object '%s' is not configured correctly, check the Indexable annotation.", array($object)); + $this->quit(1); + } + $classesAndAnnotations = array($object => $classesAndAnnotations[$object]); + } + array_walk($classesAndAnnotations, function (Indexable $annotation, $className) use ($result, $client, $conductUpdate) { + $this->outputFormatted("Object \x1b[33m%s\x1b[0m", array($className), 4); + $this->outputFormatted("Index %s Type %s", array($annotation->indexName, $annotation->typeName), 8); + $count = $client->findIndex($annotation->indexName)->findType($annotation->typeName)->count(); + if ($count === null) { + $result->forProperty($className)->addError(new Error('ElasticSearch was unable to retrieve a count for the type "%s" at index "%s". Probably these don\' exist.', 1340289921, array($annotation->typeName, $annotation->indexName))); + } + $this->outputFormatted("Documents in Search: %s", array($count !== null ? $count : "\x1b[41mError\x1b[0m"), 8); - $classesAndAnnotations = $this->indexInformer->getClassesAndAnnotations(); - if ($object !== NULL) { - if (!isset($classesAndAnnotations[$object])) { - $this->outputFormatted("Error: Object '%s' is not configured correctly, check the Indexable annotation.", array($object)); - $this->quit(1); - } - $classesAndAnnotations = array($object => $classesAndAnnotations[$object]); - } - array_walk($classesAndAnnotations, function (Indexable $annotation, $className) use ($result, $client, $conductUpdate) { - $this->outputFormatted("Object \x1b[33m%s\x1b[0m", array($className), 4); - $this->outputFormatted("Index %s Type %s", array($annotation->indexName, $annotation->typeName), 8); - $count = $client->findIndex($annotation->indexName)->findType($annotation->typeName)->count(); - if ($count === NULL) { - $result->forProperty($className)->addError(new Error('ElasticSearch was unable to retrieve a count for the type "%s" at index "%s". Probably these don\' exist.', 1340289921, array($annotation->typeName, $annotation->indexName))); - } - $this->outputFormatted("Documents in Search: %s", array($count !== NULL ? $count : "\x1b[41mError\x1b[0m"), 8); + try { + $count = $this->persistenceManager->createQueryForType($className)->count(); + } catch (\Exception $exception) { + $count = null; + $result->forProperty($className)->addError(new Error('The persistence backend was unable to retrieve a count for the type "%s". The exception message was "%s".', 1340290088, array($className, $exception->getMessage()))); + } + $this->outputFormatted("Documents in Persistence: %s", array($count !== null ? $count : "\x1b[41mError\x1b[0m"), 8); + if (!$result->forProperty($className)->hasErrors()) { + $states = $this->getModificationsNeededStatesAndIdentifiers($client, $className); + if ($conductUpdate) { + $inserted = 0; + $updated = 0; + foreach ($states[ObjectIndexer::ACTION_TYPE_CREATE] as $identifier) { + try { + $this->objectIndexer->indexObject($this->persistenceManager->getObjectByIdentifier($identifier, $className)); + $inserted++; + } catch (\Exception $exception) { + $result->forProperty($className)->addError(new Error('An error occurred while trying to add an object to the ElasticSearch backend. The exception message was "%s".', 1340356330, array($exception->getMessage()))); + } + } + foreach ($states[ObjectIndexer::ACTION_TYPE_UPDATE] as $identifier) { + try { + $this->objectIndexer->indexObject($this->persistenceManager->getObjectByIdentifier($identifier, $className)); + $updated++; + } catch (\Exception $exception) { + $result->forProperty($className)->addError(new Error('An error occurred while trying to update an object to the ElasticSearch backend. The exception message was "%s".', 1340358590, array($exception->getMessage()))); + } + } + $this->outputFormatted("Objects inserted: %s", array($inserted), 8); + $this->outputFormatted("Objects updated: %s", array($updated), 8); + } else { + $this->outputFormatted("Modifications needed: create %d, update %d", array(count($states[ObjectIndexer::ACTION_TYPE_CREATE]), count($states[ObjectIndexer::ACTION_TYPE_UPDATE])), 8); + } + } + }); - try { - $count = $this->persistenceManager->createQueryForType($className)->count(); - } catch (\Exception $exception) { - $count = NULL; - $result->forProperty($className)->addError(new Error('The persistence backend was unable to retrieve a count for the type "%s". The exception message was "%s".', 1340290088, array($className, $exception->getMessage()))); - } - $this->outputFormatted("Documents in Persistence: %s", array($count !== NULL ? $count : "\x1b[41mError\x1b[0m"), 8); - if (!$result->forProperty($className)->hasErrors()) { - $states = $this->getModificationsNeededStatesAndIdentifiers($client, $className); - if ($conductUpdate) { - $inserted = 0; - $updated = 0; - foreach ($states[ObjectIndexer::ACTION_TYPE_CREATE] AS $identifier) { - try { - $this->objectIndexer->indexObject($this->persistenceManager->getObjectByIdentifier($identifier, $className)); - $inserted++; - } catch (\Exception $exception) { - $result->forProperty($className)->addError(new Error('An error occurred while trying to add an object to the ElasticSearch backend. The exception message was "%s".', 1340356330, array($exception->getMessage()))); - } - } - foreach ($states[ObjectIndexer::ACTION_TYPE_UPDATE] AS $identifier) { - try { - $this->objectIndexer->indexObject($this->persistenceManager->getObjectByIdentifier($identifier, $className)); - $updated++; - } catch (\Exception $exception) { - $result->forProperty($className)->addError(new Error('An error occurred while trying to update an object to the ElasticSearch backend. The exception message was "%s".', 1340358590, array($exception->getMessage()))); - } - } - $this->outputFormatted("Objects inserted: %s", array($inserted), 8); - $this->outputFormatted("Objects updated: %s", array($updated), 8); - } else { - $this->outputFormatted("Modifications needed: create %d, update %d", array(count($states[ObjectIndexer::ACTION_TYPE_CREATE]), count($states[ObjectIndexer::ACTION_TYPE_UPDATE])), 8); - } - } - }); + if ($result->hasErrors()) { + $this->outputLine(); + $this->outputLine('The following errors occurred:'); + /** @var $error \TYPO3\Flow\Error\Error */ + foreach ($result->getFlattenedErrors() as $className => $errors) { + foreach ($errors as $error) { + $this->outputLine(); + $this->outputFormatted("\x1b[41mError\x1b[0m for \x1b[33m%s\x1b[0m:", array($className), 8); + $this->outputFormatted((string)$error, array(), 4); + } + } + } + } - if ($result->hasErrors()) { - $this->outputLine(); - $this->outputLine('The following errors occurred:'); - /** @var $error \TYPO3\Flow\Error\Error */ - foreach ($result->getFlattenedErrors() as $className => $errors) { - foreach ($errors as $error) { - $this->outputLine(); - $this->outputFormatted("\x1b[41mError\x1b[0m for \x1b[33m%s\x1b[0m:", array($className), 8); - $this->outputFormatted((string)$error, array(), 4); - } - } - } - } + /** + * @param Client $client + * @param string $className + * + * @return array + */ + protected function getModificationsNeededStatesAndIdentifiers(Client $client, $className) + { + $query = $this->persistenceManager->createQueryForType($className); + $states = array( + ObjectIndexer::ACTION_TYPE_CREATE => array(), + ObjectIndexer::ACTION_TYPE_UPDATE => array(), + ObjectIndexer::ACTION_TYPE_DELETE => array(), + ); + foreach ($query->execute() as $object) { + $state = $this->objectIndexer->objectIndexActionRequired($object, $client); + $states[$state][] = $this->persistenceManager->getIdentifierByObject($object); + } - /** - * @param Client $client - * @param string $className - * - * @return array - */ - protected function getModificationsNeededStatesAndIdentifiers(Client $client, $className) { - $query = $this->persistenceManager->createQueryForType($className); - $states = array( - ObjectIndexer::ACTION_TYPE_CREATE => array(), - ObjectIndexer::ACTION_TYPE_UPDATE => array(), - ObjectIndexer::ACTION_TYPE_DELETE => array(), - ); - foreach ($query->execute() as $object) { - $state = $this->objectIndexer->objectIndexActionRequired($object, $client); - $states[$state][] = $this->persistenceManager->getIdentifierByObject($object); - } - - return $states; - } + return $states; + } } - diff --git a/Classes/Flowpack/ElasticSearch/Command/MappingCommandController.php b/Classes/Flowpack/ElasticSearch/Command/MappingCommandController.php index 6d9bcd0..69f056b 100644 --- a/Classes/Flowpack/ElasticSearch/Command/MappingCommandController.php +++ b/Classes/Flowpack/ElasticSearch/Command/MappingCommandController.php @@ -1,15 +1,15 @@ entityMappingBuilder->buildMappingInformation(); - $entityMappingCollection = $this->buildArrayFromMappingCollection($entityMappingCollection); - - $client = $this->clientFactory->create($clientName); - $this->backendMappingBuilder->setClient($client); - $backendMappingCollection = $this->backendMappingBuilder->buildMappingInformation(); - $backendMappingCollection = $this->buildArrayFromMappingCollection($backendMappingCollection); - - $this->printLegend(); - $this->outputFormatted('Mapping status:'); - $this->outputFormatted('---------------'); - - $mergedMappingCollection = array_merge_recursive($entityMappingCollection, $backendMappingCollection); - foreach ($mergedMappingCollection as $indexName => $typeSet) { - $this->outputFormatted('index %s:', array($this->markupDiffValue(isset($entityMappingCollection[$indexName]) ? $indexName : NULL, isset($backendMappingCollection[$indexName]) ? $indexName : NULL))); - foreach ($typeSet as $typeName => $mappingSet) { - $propertiesSet = $mappingSet['properties']; - $this->outputFormatted('type %s:', array($this->markupDiffValue(isset($entityMappingCollection[$indexName][$typeName]) ? $typeName : NULL, isset($backendMappingCollection[$indexName][$typeName]) ? $typeName : NULL)), 4); - foreach ($propertiesSet as $propertyName => $properties) { - $entityProperties = \TYPO3\Flow\Utility\Arrays::getValueByPath($entityMappingCollection, array($indexName, $typeName, 'properties', $propertyName)); - $backendProperties = \TYPO3\Flow\Utility\Arrays::getValueByPath($backendMappingCollection, array($indexName, $typeName, 'properties', $propertyName)); - - $this->outputFormatted('property %s:', array($this->markupDiffValue($entityProperties ? $propertyName : NULL, $backendProperties ? $propertyName : NULL)), 8); - foreach ($properties AS $key => $value) { - $keyMarkup = $this->markupDiffValue(isset($entityProperties[$key]) ? $key : NULL, isset($backendProperties[$key]) ? $key : NULL); - $valueMarkup = $this->markupDiffValue(isset($entityProperties[$key]) ? $entityProperties[$key] : NULL, isset($backendProperties[$key]) ? $backendProperties[$key] : NULL); - $this->outputFormatted("%s : %s", array($keyMarkup, $valueMarkup), 12); - } - } - $this->outputLine(); - } - $this->outputLine(); - } - - if (count($indicesWithoutTypeInformation = $this->backendMappingBuilder->getIndicesWithoutTypeInformation())) { - $this->outputFormatted("\x1b[43mNotice:\x1b[0m The following indices are present in the backend's mapping but having no type configuration, can hence be regarded as garbage:"); - foreach ($indicesWithoutTypeInformation AS $indexName) { - $this->outputFormatted('* %s', array($indexName), 4); - } - } - } - - /** - * This command will adjust the backend's mapping to the mapping the entity status prescribes. - * - * @param string $clientName The client name for the configuration. Defaults to the default client configured. - * @return void - */ - public function convergeCommand($clientName = NULL) { - $client = $this->clientFactory->create($clientName); - - $entityMappingCollection = $this->entityMappingBuilder->buildMappingInformation(); - $this->backendMappingBuilder->setClient($client); - $backendMappingCollection = $this->backendMappingBuilder->buildMappingInformation(); - - $additiveMappings = $entityMappingCollection->diffAgainstCollection($backendMappingCollection); - /** @var $mapping \Flowpack\ElasticSearch\Domain\Model\Mapping */ - foreach ($additiveMappings AS $mapping) { - $index = $mapping->getType()->getIndex(); - $index->setClient($client); - if (!$index->exists()) { - $this->outputFormatted('Index %s does not exist', array($index->getName())); - continue; - } - $this->outputLine('Attempt to apply properties to %s/%s: %s... ', array( - $index->getName(), - $mapping->getType()->getName(), - print_r($mapping->getProperties(), TRUE) - )); - $response = $mapping->apply(); - if ($response->getStatusCode() === 200) { - $this->outputFormatted('OK'); - } else { - $this->outputFormatted('NOT OK, response code was %d, response body was: %s', array($response->getStatusCode(), $response->getOriginalResponse()->getContent()), 4); - } - } - if (0 === $additiveMappings->count()) { - $this->outputLine('No mappings were to be applied.'); - } - } - - /** - * @return void - */ - protected function printLegend() { - $legendText = " -" . $this->markupDiffValue(NULL, 'something') . " defined in backend, but not in entities -" . $this->markupDiffValue('something', NULL) . " defined in entities, but not in backend +class MappingCommandController extends \TYPO3\Flow\Cli\CommandController +{ + /** + * @Flow\Inject + * @var \Flowpack\ElasticSearch\Mapping\EntityMappingBuilder + */ + protected $entityMappingBuilder; + + /** + * @Flow\Inject + * @var \Flowpack\ElasticSearch\Mapping\BackendMappingBuilder + */ + protected $backendMappingBuilder; + + /** + * @Flow\Inject + * @var \Flowpack\ElasticSearch\Domain\Factory\ClientFactory + */ + protected $clientFactory; + + /** + * Shows the status of the current mapping... + * + * @param string $clientName The client name for the configuration. Defaults to the default client configured. + * @return void + */ + public function showStatusCommand($clientName = null) + { + $entityMappingCollection = $this->entityMappingBuilder->buildMappingInformation(); + $entityMappingCollection = $this->buildArrayFromMappingCollection($entityMappingCollection); + + $client = $this->clientFactory->create($clientName); + $this->backendMappingBuilder->setClient($client); + $backendMappingCollection = $this->backendMappingBuilder->buildMappingInformation(); + $backendMappingCollection = $this->buildArrayFromMappingCollection($backendMappingCollection); + + $this->printLegend(); + $this->outputFormatted('Mapping status:'); + $this->outputFormatted('---------------'); + + $mergedMappingCollection = array_merge_recursive($entityMappingCollection, $backendMappingCollection); + foreach ($mergedMappingCollection as $indexName => $typeSet) { + $this->outputFormatted('index %s:', array($this->markupDiffValue(isset($entityMappingCollection[$indexName]) ? $indexName : null, isset($backendMappingCollection[$indexName]) ? $indexName : null))); + foreach ($typeSet as $typeName => $mappingSet) { + $propertiesSet = $mappingSet['properties']; + $this->outputFormatted('type %s:', array($this->markupDiffValue(isset($entityMappingCollection[$indexName][$typeName]) ? $typeName : null, isset($backendMappingCollection[$indexName][$typeName]) ? $typeName : null)), 4); + foreach ($propertiesSet as $propertyName => $properties) { + $entityProperties = \TYPO3\Flow\Utility\Arrays::getValueByPath($entityMappingCollection, array($indexName, $typeName, 'properties', $propertyName)); + $backendProperties = \TYPO3\Flow\Utility\Arrays::getValueByPath($backendMappingCollection, array($indexName, $typeName, 'properties', $propertyName)); + + $this->outputFormatted('property %s:', array($this->markupDiffValue($entityProperties ? $propertyName : null, $backendProperties ? $propertyName : null)), 8); + foreach ($properties as $key => $value) { + $keyMarkup = $this->markupDiffValue(isset($entityProperties[$key]) ? $key : null, isset($backendProperties[$key]) ? $key : null); + $valueMarkup = $this->markupDiffValue(isset($entityProperties[$key]) ? $entityProperties[$key] : null, isset($backendProperties[$key]) ? $backendProperties[$key] : null); + $this->outputFormatted("%s : %s", array($keyMarkup, $valueMarkup), 12); + } + } + $this->outputLine(); + } + $this->outputLine(); + } + + if (count($indicesWithoutTypeInformation = $this->backendMappingBuilder->getIndicesWithoutTypeInformation())) { + $this->outputFormatted("\x1b[43mNotice:\x1b[0m The following indices are present in the backend's mapping but having no type configuration, can hence be regarded as garbage:"); + foreach ($indicesWithoutTypeInformation as $indexName) { + $this->outputFormatted('* %s', array($indexName), 4); + } + } + } + + /** + * This command will adjust the backend's mapping to the mapping the entity status prescribes. + * + * @param string $clientName The client name for the configuration. Defaults to the default client configured. + * @return void + */ + public function convergeCommand($clientName = null) + { + $client = $this->clientFactory->create($clientName); + + $entityMappingCollection = $this->entityMappingBuilder->buildMappingInformation(); + $this->backendMappingBuilder->setClient($client); + $backendMappingCollection = $this->backendMappingBuilder->buildMappingInformation(); + + $additiveMappings = $entityMappingCollection->diffAgainstCollection($backendMappingCollection); + /** @var $mapping \Flowpack\ElasticSearch\Domain\Model\Mapping */ + foreach ($additiveMappings as $mapping) { + $index = $mapping->getType()->getIndex(); + $index->setClient($client); + if (!$index->exists()) { + $this->outputFormatted('Index %s does not exist', array($index->getName())); + continue; + } + $this->outputLine('Attempt to apply properties to %s/%s: %s... ', array( + $index->getName(), + $mapping->getType()->getName(), + print_r($mapping->getProperties(), true) + )); + $response = $mapping->apply(); + if ($response->getStatusCode() === 200) { + $this->outputFormatted('OK'); + } else { + $this->outputFormatted('NOT OK, response code was %d, response body was: %s', array($response->getStatusCode(), $response->getOriginalResponse()->getContent()), 4); + } + } + if (0 === $additiveMappings->count()) { + $this->outputLine('No mappings were to be applied.'); + } + } + + /** + * @return void + */ + protected function printLegend() + { + $legendText = " +" . $this->markupDiffValue(null, 'something') . " defined in backend, but not in entities +" . $this->markupDiffValue('something', null) . " defined in entities, but not in backend " . $this->markupDiffValue('something', 'something') . " defined both in entities and backend, all OK " . $this->markupDiffValue('something', 'different') . " different in entities and backend "; - $this->outputFormatted('Legend:'); - $this->outputFormatted($legendText, array(), 4); - } - - /** - * @param mixed $entityValue - * @param mixed $backendValue - * @return string - */ - protected function markupDiffValue($entityValue, $backendValue) { - $markup = ''; - if ($entityValue === NULL || $backendValue === NULL || $entityValue === $backendValue) { - $markup .= "\x1b[" . ($entityValue ? '31' : '30') . ';' . ($backendValue ? '42' : '0') . 'm'; - if (is_array($entityValue)) { - $entityValue = var_export($entityValue, TRUE); - } - if (is_array($backendValue)) { - $backendValue = var_export($backendValue, TRUE); - } - $markup .= $entityValue ? : $backendValue; - $markup .= "\x1b[0m"; - } else { - if (is_array($entityValue)) { - $entityValue = var_export($entityValue, TRUE); - } - if (is_array($backendValue)) { - $backendValue = var_export($backendValue, TRUE); - } - $markup .= "\x1b[31m" . $entityValue . "\x1b[0m"; - $markup .= "\x1b[30;42m" . $backendValue . "\x1b[0m"; - } - - return $markup; - } - - /** - * Traverses through mappingInformation array and aggregates by index and type names - * - * @param \Flowpack\ElasticSearch\Mapping\MappingCollection $mappingCollection - * @throws \Flowpack\ElasticSearch\Exception - * @return array with index names as keys, second level type names as keys - */ - protected function buildArrayFromMappingCollection(\Flowpack\ElasticSearch\Mapping\MappingCollection $mappingCollection) { - $return = array(); - - /** @var $mappingInformation \Flowpack\ElasticSearch\Domain\Model\Mapping */ - foreach ($mappingCollection as $mappingInformation) { - $indexName = $mappingInformation->getType()->getIndex()->getName(); - $typeName = $mappingInformation->getType()->getName(); - if (isset($return[$indexName][$typeName])) { - throw new \Flowpack\ElasticSearch\Exception('There was more than one mapping present in index %s with type %s, which must not happen.', 1339758480); - } - - $return[$indexName][$typeName]['mappingInstance'] = $mappingInformation; - $return[$indexName][$typeName]['properties'] = $mappingInformation->getProperties(); - } - - return $return; - } + $this->outputFormatted('Legend:'); + $this->outputFormatted($legendText, array(), 4); + } + + /** + * @param mixed $entityValue + * @param mixed $backendValue + * @return string + */ + protected function markupDiffValue($entityValue, $backendValue) + { + $markup = ''; + if ($entityValue === null || $backendValue === null || $entityValue === $backendValue) { + $markup .= "\x1b[" . ($entityValue ? '31' : '30') . ';' . ($backendValue ? '42' : '0') . 'm'; + if (is_array($entityValue)) { + $entityValue = var_export($entityValue, true); + } + if (is_array($backendValue)) { + $backendValue = var_export($backendValue, true); + } + $markup .= $entityValue ? : $backendValue; + $markup .= "\x1b[0m"; + } else { + if (is_array($entityValue)) { + $entityValue = var_export($entityValue, true); + } + if (is_array($backendValue)) { + $backendValue = var_export($backendValue, true); + } + $markup .= "\x1b[31m" . $entityValue . "\x1b[0m"; + $markup .= "\x1b[30;42m" . $backendValue . "\x1b[0m"; + } + + return $markup; + } + + /** + * Traverses through mappingInformation array and aggregates by index and type names + * + * @param \Flowpack\ElasticSearch\Mapping\MappingCollection $mappingCollection + * @throws \Flowpack\ElasticSearch\Exception + * @return array with index names as keys, second level type names as keys + */ + protected function buildArrayFromMappingCollection(\Flowpack\ElasticSearch\Mapping\MappingCollection $mappingCollection) + { + $return = array(); + + /** @var $mappingInformation \Flowpack\ElasticSearch\Domain\Model\Mapping */ + foreach ($mappingCollection as $mappingInformation) { + $indexName = $mappingInformation->getType()->getIndex()->getName(); + $typeName = $mappingInformation->getType()->getName(); + if (isset($return[$indexName][$typeName])) { + throw new \Flowpack\ElasticSearch\Exception('There was more than one mapping present in index %s with type %s, which must not happen.', 1339758480); + } + + $return[$indexName][$typeName]['mappingInstance'] = $mappingInformation; + $return[$indexName][$typeName]['properties'] = $mappingInformation->getProperties(); + } + + return $return; + } } - diff --git a/Classes/Flowpack/ElasticSearch/Domain/Exception/DocumentPropertiesMismatchException.php b/Classes/Flowpack/ElasticSearch/Domain/Exception/DocumentPropertiesMismatchException.php index 6704aed..fe75142 100644 --- a/Classes/Flowpack/ElasticSearch/Domain/Exception/DocumentPropertiesMismatchException.php +++ b/Classes/Flowpack/ElasticSearch/Domain/Exception/DocumentPropertiesMismatchException.php @@ -1,31 +1,31 @@ errorResult = $result; - } + /** + * @param \TYPO3\Flow\Error\Result $result + */ + public function setErrorResult(\TYPO3\Flow\Error\Result $result) + { + $this->errorResult = $result; + } } - diff --git a/Classes/Flowpack/ElasticSearch/Domain/Factory/ClientFactory.php b/Classes/Flowpack/ElasticSearch/Domain/Factory/ClientFactory.php index 4782702..3cad8c0 100644 --- a/Classes/Flowpack/ElasticSearch/Domain/Factory/ClientFactory.php +++ b/Classes/Flowpack/ElasticSearch/Domain/Factory/ClientFactory.php @@ -1,15 +1,15 @@ settings = $settings; - } + /** + * @param array $settings + */ + public function injectSettings(array $settings) + { + $this->settings = $settings; + } - /** - * @param string $bundle - * @param string $clientClassName - * @throws \Flowpack\ElasticSearch\Exception - * @return \Flowpack\ElasticSearch\Domain\Model\Client - */ - public function create($bundle = NULL, $clientClassName = 'Flowpack\ElasticSearch\Domain\Model\Client') { - if ($bundle === NULL) { - $bundle = 'default'; - } + /** + * @param string $bundle + * @param string $clientClassName + * @throws \Flowpack\ElasticSearch\Exception + * @return \Flowpack\ElasticSearch\Domain\Model\Client + */ + public function create($bundle = null, $clientClassName = 'Flowpack\ElasticSearch\Domain\Model\Client') + { + if ($bundle === null) { + $bundle = 'default'; + } - if (!isset($this->settings['clients'][$bundle]) || !is_array($this->settings['clients'][$bundle])) { - throw new \Flowpack\ElasticSearch\Exception('The inquired client settings bundle "' . $bundle . '" is not present in setting "Flowpack.ElasticSearch.clients".', 1338890487); - } - $clientsSettings = $this->settings['clients'][$bundle]; + if (!isset($this->settings['clients'][$bundle]) || !is_array($this->settings['clients'][$bundle])) { + throw new \Flowpack\ElasticSearch\Exception('The inquired client settings bundle "' . $bundle . '" is not present in setting "Flowpack.ElasticSearch.clients".', 1338890487); + } + $clientsSettings = $this->settings['clients'][$bundle]; - $clientConfigurations = $this->buildClientConfigurations($clientsSettings); + $clientConfigurations = $this->buildClientConfigurations($clientsSettings); - $client = new $clientClassName(); - $client->setClientConfigurations($clientConfigurations); - $client->setBundle($bundle); + $client = new $clientClassName(); + $client->setClientConfigurations($clientConfigurations); + $client->setBundle($bundle); - return $client; - } + return $client; + } - /** - * @param $clientsSettings - * - * @return array - * @throws \Flowpack\ElasticSearch\Exception - */ - protected function buildClientConfigurations($clientsSettings) { - $clientConfigurations = array(); - foreach ($clientsSettings AS $clientSettings) { - $configuration = new \Flowpack\ElasticSearch\Domain\Model\Client\ClientConfiguration(); - foreach ($clientSettings AS $settingKey => $settingValue) { - $setterMethodName = 'set' . ucfirst($settingKey); - try { - call_user_func(array($configuration, $setterMethodName), $settingValue); - } catch (\TYPO3\Flow\Error\Exception $exception) { - $exceptionMessage = 'Setting key "' . $settingKey . '" as client configuration value is not allowed. Refer to the Settings.yaml.example for the supported keys.'; - throw new \Flowpack\ElasticSearch\Exception($exceptionMessage, 1338886877, $exception); - } - } - $clientConfigurations[] = $configuration; - } + /** + * @param $clientsSettings + * + * @return array + * @throws \Flowpack\ElasticSearch\Exception + */ + protected function buildClientConfigurations($clientsSettings) + { + $clientConfigurations = array(); + foreach ($clientsSettings as $clientSettings) { + $configuration = new \Flowpack\ElasticSearch\Domain\Model\Client\ClientConfiguration(); + foreach ($clientSettings as $settingKey => $settingValue) { + $setterMethodName = 'set' . ucfirst($settingKey); + try { + call_user_func(array($configuration, $setterMethodName), $settingValue); + } catch (\TYPO3\Flow\Error\Exception $exception) { + $exceptionMessage = 'Setting key "' . $settingKey . '" as client configuration value is not allowed. Refer to the Settings.yaml.example for the supported keys.'; + throw new \Flowpack\ElasticSearch\Exception($exceptionMessage, 1338886877, $exception); + } + } + $clientConfigurations[] = $configuration; + } - return $clientConfigurations; - } + return $clientConfigurations; + } } - diff --git a/Classes/Flowpack/ElasticSearch/Domain/Factory/DocumentFactory.php b/Classes/Flowpack/ElasticSearch/Domain/Factory/DocumentFactory.php index c0ab782..ff9e68d 100644 --- a/Classes/Flowpack/ElasticSearch/Domain/Factory/DocumentFactory.php +++ b/Classes/Flowpack/ElasticSearch/Domain/Factory/DocumentFactory.php @@ -1,15 +1,15 @@ getTreatedContent(); - /** - * @param \Flowpack\ElasticSearch\Domain\Model\AbstractType $type - * @param string $id - * @param \Flowpack\ElasticSearch\Transfer\Response $response - * @throws \Flowpack\ElasticSearch\Domain\Exception\DocumentPropertiesMismatchException - * @return \Flowpack\ElasticSearch\Domain\Model\Document - */ - public function createFromResponse(Model\AbstractType $type, $id = NULL, \Flowpack\ElasticSearch\Transfer\Response $response) { - $content = $response->getTreatedContent(); + $verificationResults = new ErrorResult(); + if (isset($content['_index']) && $type->getIndex()->getName() !== $content['_index']) { + $error = new Error('The received index name "%s" does not match the expected one "%s".', 1340264838, array($content['_index'], $type->getIndex()->getName())); + $verificationResults->addError($error); + } + if (isset($content['_type']) && $type->getName() !== $content['_type']) { + $error = new Error('The received type name "%s" does not match the expected one "%s".', 1340265103, array($content['_type'], $type->getName())); + $verificationResults->addError($error); + } - $verificationResults = new ErrorResult(); - if (isset($content['_index']) && $type->getIndex()->getName() !== $content['_index']) { - $error = new Error('The received index name "%s" does not match the expected one "%s".', 1340264838, array($content['_index'], $type->getIndex()->getName())); - $verificationResults->addError($error); - } - if (isset($content['_type']) && $type->getName() !== $content['_type']) { - $error = new Error('The received type name "%s" does not match the expected one "%s".', 1340265103, array($content['_type'], $type->getName())); - $verificationResults->addError($error); - } + if (isset($content['_id']) && $id !== null && $id !== $content['_id']) { + $error = new Error('The received id "%s" does not match the expected one "%s".', 1340269758, array($content['_id'], $id)); + $verificationResults->addError($error); + } - if (isset($content['_id']) && $id !== NULL && $id !== $content['_id']) { - $error = new Error('The received id "%s" does not match the expected one "%s".', 1340269758, array($content['_id'], $id)); - $verificationResults->addError($error); - } + if ($verificationResults->hasErrors()) { + $exception = new \Flowpack\ElasticSearch\Domain\Exception\DocumentPropertiesMismatchException('The document\'s properties do not match the expected ones.', 1340265248); + $exception->setErrorResult($verificationResults); + throw $exception; + } - if ($verificationResults->hasErrors()) { - $exception = new \Flowpack\ElasticSearch\Domain\Exception\DocumentPropertiesMismatchException('The document\'s properties do not match the expected ones.', 1340265248); - $exception->setErrorResult($verificationResults); - throw $exception; - } + $version = $content['_version']; + $data = $content['_source']; - $version = $content['_version']; - $data = $content['_source']; - - return new Model\Document($type, $data, $id, $version); - } + return new Model\Document($type, $data, $id, $version); + } } - diff --git a/Classes/Flowpack/ElasticSearch/Domain/Model/AbstractType.php b/Classes/Flowpack/ElasticSearch/Domain/Model/AbstractType.php index 89d6d44..816a8e3 100644 --- a/Classes/Flowpack/ElasticSearch/Domain/Model/AbstractType.php +++ b/Classes/Flowpack/ElasticSearch/Domain/Model/AbstractType.php @@ -1,130 +1,137 @@ index = $index; - - if ($name === NULL) { - $this->name = str_replace('\\', '_', get_class($this)); - } else { - $this->name = $name; - } - } - - /** - * Gets this type's name. - * - * @return string - */ - public function getName() { - return $this->name; - } - - /** - * @return \Flowpack\ElasticSearch\Domain\Model\Index - */ - public function getIndex() { - return $this->index; - } - - /** - * Returns a document - * - * @param $id - * - * @return \Flowpack\ElasticSearch\Domain\Model\Document - */ - public function findDocumentById($id) { - $response = $this->request('GET', '/' . $id); - if ($response->getStatusCode() !== 200) { - return NULL; - } - $document = $this->documentFactory->createFromResponse($this, $id, $response); - - return $document; - } - - /** - * @param $id - * @return boolean ...whether the deletion is considered successful - */ - public function deleteDocumentById($id) { - $response = $this->request('DELETE', '/' . $id); - $treatedContent = $response->getTreatedContent(); - - return $response->getStatusCode() === 200 && $treatedContent['found'] === TRUE; - } - - /** - * @return integer - */ - public function count() { - $response = $this->request('GET', '/_count'); - if ($response->getStatusCode() !== 200) { - return NULL; - } - $treatedContent = $response->getTreatedContent(); - - return (integer)$treatedContent['count']; - } - - /** - * @param array $searchQuery The search query TODO: make it an object - * @return \Flowpack\ElasticSearch\Transfer\Response - */ - public function search(array $searchQuery) { - return $this->request('GET', '/_search', array(), json_encode($searchQuery)); - } - - /** - * @param string $method - * @param string $path - * @param array $arguments - * @param string $content - * - * @return \Flowpack\ElasticSearch\Transfer\Response - */ - public function request($method, $path = NULL, $arguments = array(), $content = NULL) { - $path = '/' . $this->name . ($path ?: ''); - - return $this->index->request($method, $path, $arguments, $content); - } +abstract class AbstractType +{ + /** + * @Flow\Inject + * @var \Flowpack\ElasticSearch\Domain\Factory\DocumentFactory + */ + protected $documentFactory; + + /** + * @var \Flowpack\ElasticSearch\Domain\Model\Index + */ + protected $index; + + /** + * @var string + */ + protected $name; + + /** + * @param Index $index + * @param string $name + */ + public function __construct(Index $index, $name = null) + { + $this->index = $index; + + if ($name === null) { + $this->name = str_replace('\\', '_', get_class($this)); + } else { + $this->name = $name; + } + } + + /** + * Gets this type's name. + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * @return \Flowpack\ElasticSearch\Domain\Model\Index + */ + public function getIndex() + { + return $this->index; + } + + /** + * Returns a document + * + * @param $id + * + * @return \Flowpack\ElasticSearch\Domain\Model\Document + */ + public function findDocumentById($id) + { + $response = $this->request('GET', '/' . $id); + if ($response->getStatusCode() !== 200) { + return null; + } + $document = $this->documentFactory->createFromResponse($this, $id, $response); + + return $document; + } + + /** + * @param $id + * @return boolean ...whether the deletion is considered successful + */ + public function deleteDocumentById($id) + { + $response = $this->request('DELETE', '/' . $id); + $treatedContent = $response->getTreatedContent(); + + return $response->getStatusCode() === 200 && $treatedContent['found'] === true; + } + + /** + * @return integer + */ + public function count() + { + $response = $this->request('GET', '/_count'); + if ($response->getStatusCode() !== 200) { + return null; + } + $treatedContent = $response->getTreatedContent(); + + return (integer)$treatedContent['count']; + } + + /** + * @param array $searchQuery The search query TODO: make it an object + * @return \Flowpack\ElasticSearch\Transfer\Response + */ + public function search(array $searchQuery) + { + return $this->request('GET', '/_search', array(), json_encode($searchQuery)); + } + + /** + * @param string $method + * @param string $path + * @param array $arguments + * @param string $content + * + * @return \Flowpack\ElasticSearch\Transfer\Response + */ + public function request($method, $path = null, $arguments = array(), $content = null) + { + $path = '/' . $this->name . ($path ?: ''); + + return $this->index->request($method, $path, $arguments, $content); + } } - diff --git a/Classes/Flowpack/ElasticSearch/Domain/Model/Client.php b/Classes/Flowpack/ElasticSearch/Domain/Model/Client.php index 7eddc16..3e9d422 100644 --- a/Classes/Flowpack/ElasticSearch/Domain/Model/Client.php +++ b/Classes/Flowpack/ElasticSearch/Domain/Model/Client.php @@ -1,96 +1,101 @@ bundle = $bundle; - } + /** + * @param string $bundle + */ + public function setBundle($bundle) + { + $this->bundle = $bundle; + } - /** - * @return string - */ - public function getBundle() { - return $this->bundle; - } + /** + * @return string + */ + public function getBundle() + { + return $this->bundle; + } - /** - * @param array $clientConfigurations - */ - public function setClientConfigurations($clientConfigurations) { - $this->clientConfigurations = $clientConfigurations; - } + /** + * @param array $clientConfigurations + */ + public function setClientConfigurations($clientConfigurations) + { + $this->clientConfigurations = $clientConfigurations; + } - /** - * @return array - */ - public function getClientConfigurations() { - return $this->clientConfigurations; - } + /** + * @return array + */ + public function getClientConfigurations() + { + return $this->clientConfigurations; + } - /** - * @param string $indexName - * @return \Flowpack\ElasticSearch\Domain\Model\Index - */ - public function findIndex($indexName) { - if (!array_key_exists($indexName, $this->indexCollection)) { - $this->indexCollection[$indexName] = new Index($indexName, $this); - } + /** + * @param string $indexName + * @return \Flowpack\ElasticSearch\Domain\Model\Index + */ + public function findIndex($indexName) + { + if (!array_key_exists($indexName, $this->indexCollection)) { + $this->indexCollection[$indexName] = new Index($indexName, $this); + } - return $this->indexCollection[$indexName]; - } + return $this->indexCollection[$indexName]; + } - /** - * Passes a request through to the request service - * - * @param string $method - * @param string $path - * @param array $arguments - * @param string|array $content - * - * @return \Flowpack\ElasticSearch\Transfer\Response - */ - public function request($method, $path = NULL, $arguments = array(), $content = NULL) { - return $this->requestService->request($method, $this, $path, $arguments, $content); - } + /** + * Passes a request through to the request service + * + * @param string $method + * @param string $path + * @param array $arguments + * @param string|array $content + * + * @return \Flowpack\ElasticSearch\Transfer\Response + */ + public function request($method, $path = null, $arguments = array(), $content = null) + { + return $this->requestService->request($method, $this, $path, $arguments, $content); + } } - diff --git a/Classes/Flowpack/ElasticSearch/Domain/Model/Client/ClientConfiguration.php b/Classes/Flowpack/ElasticSearch/Domain/Model/Client/ClientConfiguration.php index b1be5c4..2fabaee 100644 --- a/Classes/Flowpack/ElasticSearch/Domain/Model/Client/ClientConfiguration.php +++ b/Classes/Flowpack/ElasticSearch/Domain/Model/Client/ClientConfiguration.php @@ -1,89 +1,96 @@ host = $host; - } + /** + * @param string $host + */ + public function setHost($host) + { + $this->host = $host; + } - /** - * @return string - */ - public function getHost() { - return $this->host; - } + /** + * @return string + */ + public function getHost() + { + return $this->host; + } - /** - * @param int $port - */ - public function setPort($port) { - $this->port = $port; - } + /** + * @param int $port + */ + public function setPort($port) + { + $this->port = $port; + } - /** - * @return int - */ - public function getPort() { - return $this->port; - } + /** + * @return int + */ + public function getPort() + { + return $this->port; + } - /** - * @param string $scheme - */ - public function setScheme($scheme) { - $this->scheme = $scheme; - } + /** + * @param string $scheme + */ + public function setScheme($scheme) + { + $this->scheme = $scheme; + } - /** - * @return string - */ - public function getScheme() { - return $this->scheme; - } + /** + * @return string + */ + public function getScheme() + { + return $this->scheme; + } - /** - * @return \TYPO3\Flow\Http\Uri - */ - public function getUri() { - $uri = new \TYPO3\Flow\Http\Uri(''); - $uri->setScheme($this->scheme); - $uri->setHost($this->host); - $uri->setPort($this->port); + /** + * @return \TYPO3\Flow\Http\Uri + */ + public function getUri() + { + $uri = new \TYPO3\Flow\Http\Uri(''); + $uri->setScheme($this->scheme); + $uri->setHost($this->host); + $uri->setPort($this->port); - return $uri; - } -} \ No newline at end of file + return $uri; + } +} diff --git a/Classes/Flowpack/ElasticSearch/Domain/Model/Document.php b/Classes/Flowpack/ElasticSearch/Domain/Model/Document.php index 4d97e3c..7bbd742 100644 --- a/Classes/Flowpack/ElasticSearch/Domain/Model/Document.php +++ b/Classes/Flowpack/ElasticSearch/Domain/Model/Document.php @@ -1,180 +1,191 @@ type = $type; - $this->data = $data; - $this->id = $id; - $this->version = $version; - } - - /** - * When cloning (locally), the cloned object doesn't represent a stored one anymore, - * so reset id, version and the dirty state. - */ - public function __clone() { - $this->id = NULL; - $this->version = NULL; - $this->setDirty(); - } - - /** - * @param string $method - * @param string $path - * @param array $arguments - * @param string $content - * - * @return \Flowpack\ElasticSearch\Transfer\Response - */ - protected function request($method, $path = NULL, $arguments = array(), $content = NULL) { - return $this->type->request($method, $path, $arguments, $content); - } - - /** - * Stores this document. If ID is given, PUT will be used; else POST - * - * @throws \Flowpack\ElasticSearch\Exception - * @return void - */ - public function store() { - if ($this->id !== NULL) { - $method = 'PUT'; - $path = '/' . $this->id; - } else { - $method = 'POST'; - $path = ''; - } - $response = $this->request($method, $path, array(), json_encode($this->data)); - $treatedContent = $response->getTreatedContent(); - - $this->id = $treatedContent['_id']; - $this->version = $treatedContent['_version']; - $this->dirty = FALSE; - } - - /** - * @param boolean $dirty - */ - protected function setDirty($dirty = TRUE) { - $this->dirty = $dirty; - } - - /** - * @return boolean - */ - public function isDirty() { - return $this->dirty; - } - - /** - * @return integer - */ - public function getVersion() { - return $this->version; - } - - /** - * The contents of this document - * - * @return array - */ - public function getData() { - return $this->data; - } - - /** - * @param array $data - */ - public function setData($data) { - $this->data = $data; - $this->setDirty(); - } - - /** - * @return string - */ - public function getId() { - return $this->id; - } - - /** - * Gets a specific field's value from this' data - * - * @param string $fieldName - * @param boolean $silent - * - * @throws \Flowpack\ElasticSearch\Exception - * @return mixed - */ - public function getField($fieldName, $silent = FALSE) { - if (!array_key_exists($fieldName, $this->data) && $silent === FALSE) { - throw new \Flowpack\ElasticSearch\Exception(sprintf('The field %s was not present in data of document in %s/%s.', $fieldName, $this->type->getIndex()->getName(), $this->type->getName()), 1340274696); - } - - return $this->data[$fieldName]; - } - - /** - * @return \Flowpack\ElasticSearch\Domain\Model\AbstractType the type of this Document - */ - public function getType() { - return $this->type; - } +class Document +{ + /** + * @var \Flowpack\ElasticSearch\Domain\Model\AbstractType + */ + protected $type; + + /** + * The actual data to store to the document + * + * @var array + */ + protected $data; + + /** + * The version that has been assigned to this document. + * + * @var integer + */ + protected $version; + + /** + * @var string + */ + protected $id; + + /** + * Whether this document represents the state like it should be at the storage. + * With a fresh instance of this document, or a conducted change, this flag gets set to TRUE again. + * When retrieved from the storage, or successfully set to the storage, it's FALSE. + * + * @var boolean + */ + protected $dirty = true; + + /** + * @param \Flowpack\ElasticSearch\Domain\Model\AbstractType $type + * @param array $data + * @param string $id + * @param null $version + */ + public function __construct(AbstractType $type, array $data = null, $id = null, $version = null) + { + $this->type = $type; + $this->data = $data; + $this->id = $id; + $this->version = $version; + } + + /** + * When cloning (locally), the cloned object doesn't represent a stored one anymore, + * so reset id, version and the dirty state. + */ + public function __clone() + { + $this->id = null; + $this->version = null; + $this->setDirty(); + } + + /** + * @param string $method + * @param string $path + * @param array $arguments + * @param string $content + * + * @return \Flowpack\ElasticSearch\Transfer\Response + */ + protected function request($method, $path = null, $arguments = array(), $content = null) + { + return $this->type->request($method, $path, $arguments, $content); + } + + /** + * Stores this document. If ID is given, PUT will be used; else POST + * + * @throws \Flowpack\ElasticSearch\Exception + * @return void + */ + public function store() + { + if ($this->id !== null) { + $method = 'PUT'; + $path = '/' . $this->id; + } else { + $method = 'POST'; + $path = ''; + } + $response = $this->request($method, $path, array(), json_encode($this->data)); + $treatedContent = $response->getTreatedContent(); + + $this->id = $treatedContent['_id']; + $this->version = $treatedContent['_version']; + $this->dirty = false; + } + + /** + * @param boolean $dirty + */ + protected function setDirty($dirty = true) + { + $this->dirty = $dirty; + } + + /** + * @return boolean + */ + public function isDirty() + { + return $this->dirty; + } + + /** + * @return integer + */ + public function getVersion() + { + return $this->version; + } + + /** + * The contents of this document + * + * @return array + */ + public function getData() + { + return $this->data; + } + + /** + * @param array $data + */ + public function setData($data) + { + $this->data = $data; + $this->setDirty(); + } + + /** + * @return string + */ + public function getId() + { + return $this->id; + } + + /** + * Gets a specific field's value from this' data + * + * @param string $fieldName + * @param boolean $silent + * + * @throws \Flowpack\ElasticSearch\Exception + * @return mixed + */ + public function getField($fieldName, $silent = false) + { + if (!array_key_exists($fieldName, $this->data) && $silent === false) { + throw new \Flowpack\ElasticSearch\Exception(sprintf('The field %s was not present in data of document in %s/%s.', $fieldName, $this->type->getIndex()->getName(), $this->type->getName()), 1340274696); + } + + return $this->data[$fieldName]; + } + + /** + * @return \Flowpack\ElasticSearch\Domain\Model\AbstractType the type of this Document + */ + public function getType() + { + return $this->type; + } } - diff --git a/Classes/Flowpack/ElasticSearch/Domain/Model/GenericType.php b/Classes/Flowpack/ElasticSearch/Domain/Model/GenericType.php index 87da2fc..2457e22 100644 --- a/Classes/Flowpack/ElasticSearch/Domain/Model/GenericType.php +++ b/Classes/Flowpack/ElasticSearch/Domain/Model/GenericType.php @@ -1,29 +1,29 @@ settings = $settings; - } - - /** - * @param $name - * @param Client $client $client - * - * @throws \Flowpack\ElasticSearch\Exception - */ - public function __construct($name, Client $client = NULL) { - $name = trim($name); - if (strlen($name) < 1 || substr($name, 0, 1) === '_') { - throw new \Flowpack\ElasticSearch\Exception('The provided index name "' . $name . '" must not be empty and not start with an underscore.', 1340187948); - } elseif ($name !== strtolower($name)) { - throw new \Flowpack\ElasticSearch\Exception('The provided index name "' . $name . '" must be all lowercase.', 1340187956); - } - $this->name = $name; - $this->settingsKey = $name; - $this->client = $client; - } - - /** - * @param $typeName - * @return \Flowpack\ElasticSearch\Domain\Model\AbstractType - */ - public function findType($typeName) { - return new GenericType($this, $typeName); - } - - /** - * @param array $types - * - * @return TypeGroup - */ - public function findTypeGroup(array $types) { - return new TypeGroup($this, $types); - } - - /** - * @return boolean - */ - public function exists() { - $response = $this->request('HEAD'); - - return $response->getStatusCode() === 200; - } - - /** - * @return void - */ - public function create() { - $this->request('PUT', NULL, array(), json_encode($this->getSettings())); - } - - /** - * @return void - */ - public function updateSettings() { - $settings = $this->getSettings(); - $updatableSettings = array(); - foreach ($this->updatableSettings as $settingPath) { - $setting = Arrays::getValueByPath($settings, $settingPath); - if ($setting !== NULL) { - $updatableSettings = Arrays::setValueByPath($updatableSettings, $settingPath, $setting); - } - } - $this->request('PUT', '/_settings', array(), json_encode($updatableSettings)); - } - - /** - * @return void - */ - public function delete() { - $this->request('DELETE'); - } - - /** - * Refresh the index - * - * @return void - */ - public function refresh() { - $this->request('POST', '/_refresh'); - } - - /** - * @param string $method - * @param string $path - * @param array $arguments - * @param string $content - * - * @throws \Flowpack\ElasticSearch\Exception - * @return \Flowpack\ElasticSearch\Transfer\Response - */ - public function request($method, $path = NULL, $arguments = array(), $content = NULL) { - if ($this->client === NULL) { - throw new Exception('The client of the index "' . $this->name . '" is not set, hence no requests can be done.'); - } - $path = '/' . $this->name . ($path ?: ''); - - return $this->client->request($method, $path, $arguments, $content); - } - - /** - * @return string - */ - public function getName() { - return $this->name; - } - - /** - * @param Client $client - */ - public function setClient($client) { - $this->client = $client; - } - - /** - * @param $settingsKey - */ - public function setSettingsKey($settingsKey) { - $this->settingsKey = $settingsKey; - } - - /** - * @return array|null - */ - protected function getSettings() { - if ($this->client instanceof Client) { - $settings = Arrays::getValueByPath($this->settings, 'indexes.' . $this->client->getBundle() . '.' . $this->settingsKey) ?: Arrays::getValueByPath($this->settings, 'indexes.default' . '.' . $this->settingsKey); - } else { - $settings = Arrays::getValueByPath($this->settings, 'indexes.default' . '.' . $this->settingsKey); - } - return $settings; - } +class Index +{ + /** + * @var array + * @see http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/indices-update-settings.html + */ + protected $updatableSettings = array( + 'index.number_of_replicas', + 'index.auto_expand_replicas', + 'index.blocks.read_only', + 'index.blocks.read', + 'index.blocks.write', + 'index.blocks.metadata', + 'index.refresh_interval', + 'index.index_concurrency', + 'index.codec', + 'index.codec.bloom.load', + 'index.fail_on_merge_failure', + 'index.translog.flush_threshold_ops', + 'index.translog.flush_threshold_size', + 'index.translog.flush_threshold_period', + 'index.translog.disable_flush', + 'index.cache.filter.max_size', + 'index.cache.filter.expire', + 'index.gateway.snapshot_interval', + 'index.routing.allocation.include', + 'index.routing.allocation.exclude', + 'index.routing.allocation.require', + 'index.routing.allocation.disable_allocation', + 'index.routing.allocation.disable_new_allocation', + 'index.routing.allocation.disable_replica_allocation', + 'index.routing.allocation.enable', + 'index.routing.allocation.total_shards_per_node', + 'index.recovery.initial_shards', + 'index.gc_deletes', + 'index.ttl.disable_purge', + 'index.translog.fs.type', + 'index.compound_format', + 'index.compound_on_flush', + 'index.warmer.enabled' + ); + + /** + * @var string + */ + protected $name; + + /** + * @var string + */ + protected $settingsKey; + + /** + * The owner client of this index. Could be set later in order to allow creating pending Index objects + * + * @var Client + */ + protected $client; + + /** + * @var array + */ + protected $settings; + + /** + * Inject the settings + * + * @param array $settings + * @return void + */ + public function injectSettings(array $settings) + { + $this->settings = $settings; + } + + /** + * @param $name + * @param Client $client $client + * + * @throws \Flowpack\ElasticSearch\Exception + */ + public function __construct($name, Client $client = null) + { + $name = trim($name); + if (strlen($name) < 1 || substr($name, 0, 1) === '_') { + throw new \Flowpack\ElasticSearch\Exception('The provided index name "' . $name . '" must not be empty and not start with an underscore.', 1340187948); + } elseif ($name !== strtolower($name)) { + throw new \Flowpack\ElasticSearch\Exception('The provided index name "' . $name . '" must be all lowercase.', 1340187956); + } + $this->name = $name; + $this->settingsKey = $name; + $this->client = $client; + } + + /** + * @param $typeName + * @return \Flowpack\ElasticSearch\Domain\Model\AbstractType + */ + public function findType($typeName) + { + return new GenericType($this, $typeName); + } + + /** + * @param array $types + * + * @return TypeGroup + */ + public function findTypeGroup(array $types) + { + return new TypeGroup($this, $types); + } + + /** + * @return boolean + */ + public function exists() + { + $response = $this->request('HEAD'); + + return $response->getStatusCode() === 200; + } + + /** + * @return void + */ + public function create() + { + $this->request('PUT', null, array(), json_encode($this->getSettings())); + } + + /** + * @return void + */ + public function updateSettings() + { + $settings = $this->getSettings(); + $updatableSettings = array(); + foreach ($this->updatableSettings as $settingPath) { + $setting = Arrays::getValueByPath($settings, $settingPath); + if ($setting !== null) { + $updatableSettings = Arrays::setValueByPath($updatableSettings, $settingPath, $setting); + } + } + $this->request('PUT', '/_settings', array(), json_encode($updatableSettings)); + } + + /** + * @return void + */ + public function delete() + { + $this->request('DELETE'); + } + + /** + * Refresh the index + * + * @return void + */ + public function refresh() + { + $this->request('POST', '/_refresh'); + } + + /** + * @param string $method + * @param string $path + * @param array $arguments + * @param string $content + * + * @throws \Flowpack\ElasticSearch\Exception + * @return \Flowpack\ElasticSearch\Transfer\Response + */ + public function request($method, $path = null, $arguments = array(), $content = null) + { + if ($this->client === null) { + throw new Exception('The client of the index "' . $this->name . '" is not set, hence no requests can be done.'); + } + $path = '/' . $this->name . ($path ?: ''); + + return $this->client->request($method, $path, $arguments, $content); + } + + /** + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * @param Client $client + */ + public function setClient($client) + { + $this->client = $client; + } + + /** + * @param $settingsKey + */ + public function setSettingsKey($settingsKey) + { + $this->settingsKey = $settingsKey; + } + + /** + * @return array|null + */ + protected function getSettings() + { + if ($this->client instanceof Client) { + $settings = Arrays::getValueByPath($this->settings, 'indexes.' . $this->client->getBundle() . '.' . $this->settingsKey) ?: Arrays::getValueByPath($this->settings, 'indexes.default' . '.' . $this->settingsKey); + } else { + $settings = Arrays::getValueByPath($this->settings, 'indexes.default' . '.' . $this->settingsKey); + } + return $settings; + } } - diff --git a/Classes/Flowpack/ElasticSearch/Domain/Model/Mapping.php b/Classes/Flowpack/ElasticSearch/Domain/Model/Mapping.php index 08e3f52..df03a2c 100644 --- a/Classes/Flowpack/ElasticSearch/Domain/Model/Mapping.php +++ b/Classes/Flowpack/ElasticSearch/Domain/Model/Mapping.php @@ -1,15 +1,15 @@ type = $type; - } - - /** - * Gets a property setting by its path - * - * @param array|string $path - * @return mixed - */ - public function getPropertyByPath($path) { - return \TYPO3\Flow\Utility\Arrays::getValueByPath($this->properties, $path); - } - - /** - * Gets a property setting by its path - * - * @param array|string $path - * @param string $value - * @return void - */ - public function setPropertyByPath($path, $value) { - $this->properties = \TYPO3\Flow\Utility\Arrays::setValueByPath($this->properties, $path, $value); - } - - /** - * @return array - */ - public function getProperties() { - return $this->properties; - } - - /** - * @return \Flowpack\ElasticSearch\Domain\Model\AbstractType - */ - public function getType() { - return $this->type; - } - - /** - * Return the mapping which would be sent to the server as array - * - * @return array - */ - public function asArray() { - return array($this->type->getName() => Arrays::arrayMergeRecursiveOverrule(array( - 'dynamic_templates' => $this->getDynamicTemplates(), - 'properties' => $this->getProperties() - ), $this->fullMapping)); - } - - /** - * Sets this mapping to the server - */ - public function apply() { - $content = json_encode($this->asArray()); - $response = $this->type->request('PUT', '/_mapping', array(), $content); - - return $response; - } - - /** - * @return array - */ - public function getDynamicTemplates() { - return $this->dynamicTemplates; - } - - /** - * Dynamic templates allow to define mapping templates - * - * @param $dynamicTemplateName - * @param array $mappingConfiguration - */ - public function addDynamicTemplate($dynamicTemplateName, array $mappingConfiguration) { - $this->dynamicTemplates[] = array( - $dynamicTemplateName => $mappingConfiguration - ); - } - - /** - * This is the full / raw ElasticSearch mapping which is merged with the properties and dynamicTemplates. - * - * It can be used to specify arbitrary ElasticSearch mapping options, like f.e. configuring the _all field. - * - * @param array $fullMapping - */ - public function setFullMapping(array $fullMapping) { - $this->fullMapping = $fullMapping; - } - - /** - * see {@link setFullMapping} for documentation - * @return array - */ - public function getFullMapping() { - return $this->fullMapping; - } -} \ No newline at end of file +class Mapping +{ + /** + * @var \Flowpack\ElasticSearch\Domain\Model\AbstractType + */ + protected $type; + + /** + * @var array + */ + protected $properties = array(); + + /** + * see http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/mapping-root-object-type.html#_dynamic_templates + * @var array + */ + protected $dynamicTemplates = array(); + + /** + * This is the full / raw ElasticSearch mapping which is merged with the properties and dynamicTemplates. + * + * It can be used to specify arbitrary ElasticSearch mapping options, like f.e. configuring the _all field. + * + * @var array + */ + protected $fullMapping = array(); + + /** + * @param \Flowpack\ElasticSearch\Domain\Model\AbstractType $type + */ + public function __construct(AbstractType $type) + { + $this->type = $type; + } + + /** + * Gets a property setting by its path + * + * @param array|string $path + * @return mixed + */ + public function getPropertyByPath($path) + { + return \TYPO3\Flow\Utility\Arrays::getValueByPath($this->properties, $path); + } + + /** + * Gets a property setting by its path + * + * @param array|string $path + * @param string $value + * @return void + */ + public function setPropertyByPath($path, $value) + { + $this->properties = \TYPO3\Flow\Utility\Arrays::setValueByPath($this->properties, $path, $value); + } + + /** + * @return array + */ + public function getProperties() + { + return $this->properties; + } + + /** + * @return \Flowpack\ElasticSearch\Domain\Model\AbstractType + */ + public function getType() + { + return $this->type; + } + + /** + * Return the mapping which would be sent to the server as array + * + * @return array + */ + public function asArray() + { + return array($this->type->getName() => Arrays::arrayMergeRecursiveOverrule(array( + 'dynamic_templates' => $this->getDynamicTemplates(), + 'properties' => $this->getProperties() + ), $this->fullMapping)); + } + + /** + * Sets this mapping to the server + */ + public function apply() + { + $content = json_encode($this->asArray()); + $response = $this->type->request('PUT', '/_mapping', array(), $content); + + return $response; + } + + /** + * @return array + */ + public function getDynamicTemplates() + { + return $this->dynamicTemplates; + } + + /** + * Dynamic templates allow to define mapping templates + * + * @param $dynamicTemplateName + * @param array $mappingConfiguration + */ + public function addDynamicTemplate($dynamicTemplateName, array $mappingConfiguration) + { + $this->dynamicTemplates[] = array( + $dynamicTemplateName => $mappingConfiguration + ); + } + + /** + * This is the full / raw ElasticSearch mapping which is merged with the properties and dynamicTemplates. + * + * It can be used to specify arbitrary ElasticSearch mapping options, like f.e. configuring the _all field. + * + * @param array $fullMapping + */ + public function setFullMapping(array $fullMapping) + { + $this->fullMapping = $fullMapping; + } + + /** + * see {@link setFullMapping} for documentation + * @return array + */ + public function getFullMapping() + { + return $this->fullMapping; + } +} diff --git a/Classes/Flowpack/ElasticSearch/Domain/Model/TypeGroup.php b/Classes/Flowpack/ElasticSearch/Domain/Model/TypeGroup.php index ac51797..c6990eb 100644 --- a/Classes/Flowpack/ElasticSearch/Domain/Model/TypeGroup.php +++ b/Classes/Flowpack/ElasticSearch/Domain/Model/TypeGroup.php @@ -1,41 +1,41 @@ - */ - protected $types = array(); +class TypeGroup extends AbstractType +{ + /** + * @var array + */ + protected $types = array(); - /** - * @param Index $index - * @param array $types - */ - public function __construct(Index $index, array $types) { - parent::__construct($index); - $this->types = $types; + /** + * @param Index $index + * @param array $types + */ + public function __construct(Index $index, array $types) + { + parent::__construct($index); + $this->types = $types; - $names = array(); - foreach ($this->types as $type) { - $names[] = $type->getName(); - } - $this->name = implode(',', $names); - } + $names = array(); + foreach ($this->types as $type) { + $names[] = $type->getName(); + } + $this->name = implode(',', $names); + } } - diff --git a/Classes/Flowpack/ElasticSearch/Exception.php b/Classes/Flowpack/ElasticSearch/Exception.php index e333c67..833d39f 100644 --- a/Classes/Flowpack/ElasticSearch/Exception.php +++ b/Classes/Flowpack/ElasticSearch/Exception.php @@ -1,19 +1,19 @@ (add|update)())") + * @param \TYPO3\Flow\Aop\JoinPointInterface $joinPoint + * @return string + */ + public function updateObjectToIndex(\TYPO3\Flow\Aop\JoinPointInterface $joinPoint) + { + $arguments = $joinPoint->getMethodArguments(); + $object = reset($arguments); + $this->objectIndexer->indexObject($object); + } - /** - * @Flow\AfterReturning("setting(Flowpack.ElasticSearch.realtimeIndexing.enabled) && within(TYPO3\Flow\Persistence\PersistenceManagerInterface) && method(public .+->(add|update)())") - * @param \TYPO3\Flow\Aop\JoinPointInterface $joinPoint - * @return string - */ - public function updateObjectToIndex(\TYPO3\Flow\Aop\JoinPointInterface $joinPoint) { - $arguments = $joinPoint->getMethodArguments(); - $object = reset($arguments); - $this->objectIndexer->indexObject($object); - } - - /** - * @Flow\AfterReturning("setting(Flowpack.ElasticSearch.realtimeIndexing.enabled) && within(TYPO3\Flow\Persistence\PersistenceManagerInterface) && method(public .+->(remove)())") - * @param \TYPO3\Flow\Aop\JoinPointInterface $joinPoint - * @return string - */ - public function removeObjectFromIndex(\TYPO3\Flow\Aop\JoinPointInterface $joinPoint) { - $arguments = $joinPoint->getMethodArguments(); - $object = reset($arguments); - $this->objectIndexer->removeObject($object); - } + /** + * @Flow\AfterReturning("setting(Flowpack.ElasticSearch.realtimeIndexing.enabled) && within(TYPO3\Flow\Persistence\PersistenceManagerInterface) && method(public .+->(remove)())") + * @param \TYPO3\Flow\Aop\JoinPointInterface $joinPoint + * @return string + */ + public function removeObjectFromIndex(\TYPO3\Flow\Aop\JoinPointInterface $joinPoint) + { + $arguments = $joinPoint->getMethodArguments(); + $object = reset($arguments); + $this->objectIndexer->removeObject($object); + } } - diff --git a/Classes/Flowpack/ElasticSearch/Indexer/Object/IndexInformer.php b/Classes/Flowpack/ElasticSearch/Indexer/Object/IndexInformer.php index afa4421..92e6611 100644 --- a/Classes/Flowpack/ElasticSearch/Indexer/Object/IndexInformer.php +++ b/Classes/Flowpack/ElasticSearch/Indexer/Object/IndexInformer.php @@ -1,15 +1,15 @@ indexAnnotations = self::buildIndexClassesAndProperties($this->objectManager); - } - - /** - * Returns the to-index classes and their annotation - * - * @return array - */ - public function getClassesAndAnnotations() { - static $classesAndAnnotations; - if ($classesAndAnnotations === NULL) { - $classesAndAnnotations = array(); - foreach (array_keys($this->indexAnnotations) AS $className) { - $classesAndAnnotations[$className] = $this->indexAnnotations[$className]['annotation']; - } - } - - return $classesAndAnnotations; - } - - /** - * Returns all indexes name deplared in class annotations - * - * @return array - */ - public function getAllIndexNames() { - $indexes = array(); - foreach ($this->getClassesAndAnnotations() as $configuration) { - /** @var Indexable $configuration */ - $indexes[$configuration->indexName] = $configuration->indexName; - } - - return array_keys($indexes); - } - - /** - * @param string $className - * @return Indexable The annotation for this class - */ - public function getClassAnnotation($className) { - if (!isset($this->indexAnnotations[$className])) { - return NULL; - } - - return $this->indexAnnotations[$className]['annotation']; - } - - /** - * @param string $className - * @return array - */ - public function getClassProperties($className) { - if (!isset($this->indexAnnotations[$className])) { - return NULL; - } - - return $this->indexAnnotations[$className]['properties']; - } - - /** - * Creates the source array of what classes and properties have to be annotated. - * The returned array consists of class names, with a sub-key having both 'annotation' and 'properties' set. - * The annotation contains the class's annotation, while properties contains each property that has to be indexed. - * Each property might either have TRUE as value, or also an annotation instance, if given. - * - * @throws \Flowpack\ElasticSearch\Exception - * @param ObjectManagerInterface $objectManager - * @return array - * @Flow\CompileStatic - */ - static public function buildIndexClassesAndProperties($objectManager) { - /** @var ReflectionService $reflectionService */ - $reflectionService = $objectManager->get('TYPO3\Flow\Reflection\ReflectionService'); - - $indexAnnotations = array(); - - $annotationClassName = 'Flowpack\ElasticSearch\Annotations\Indexable'; - foreach ($reflectionService->getClassNamesByAnnotation($annotationClassName) AS $className) { - if ($reflectionService->isClassAbstract($className)) { - throw new \Flowpack\ElasticSearch\Exception(sprintf('The class with name "%s" is annotated with %s, but is abstract. Indexable classes must not be abstract.', $className, $annotationClassName), 1339595182); - } - $indexAnnotations[$className]['annotation'] = $reflectionService->getClassAnnotation($className, $annotationClassName); - - // if no single properties are set to be indexed, consider all properties to be indexed. - $annotatedProperties = $reflectionService->getPropertyNamesByAnnotation($className, $annotationClassName); - if (!empty($annotatedProperties)) { - $indexAnnotations[$className]['properties'] = $annotatedProperties; - } else { - foreach ($reflectionService->getClassPropertyNames($className) AS $propertyName) { - $indexAnnotations[$className]['properties'][] = $propertyName; - } - } - } - - return $indexAnnotations; - } +class IndexInformer +{ + /** + * @Flow\Inject + * @var ReflectionService + */ + protected $reflectionService; + + /** + * @Flow\Inject + * @var ObjectManagerInterface + */ + protected $objectManager; + + /** + * @var array + */ + protected $indexAnnotations = array(); + + /** + */ + public function initializeObject() + { + $this->indexAnnotations = self::buildIndexClassesAndProperties($this->objectManager); + } + + /** + * Returns the to-index classes and their annotation + * + * @return array + */ + public function getClassesAndAnnotations() + { + static $classesAndAnnotations; + if ($classesAndAnnotations === null) { + $classesAndAnnotations = array(); + foreach (array_keys($this->indexAnnotations) as $className) { + $classesAndAnnotations[$className] = $this->indexAnnotations[$className]['annotation']; + } + } + + return $classesAndAnnotations; + } + + /** + * Returns all indexes name deplared in class annotations + * + * @return array + */ + public function getAllIndexNames() + { + $indexes = array(); + foreach ($this->getClassesAndAnnotations() as $configuration) { + /** @var Indexable $configuration */ + $indexes[$configuration->indexName] = $configuration->indexName; + } + + return array_keys($indexes); + } + + /** + * @param string $className + * @return Indexable The annotation for this class + */ + public function getClassAnnotation($className) + { + if (!isset($this->indexAnnotations[$className])) { + return null; + } + + return $this->indexAnnotations[$className]['annotation']; + } + + /** + * @param string $className + * @return array + */ + public function getClassProperties($className) + { + if (!isset($this->indexAnnotations[$className])) { + return null; + } + + return $this->indexAnnotations[$className]['properties']; + } + + /** + * Creates the source array of what classes and properties have to be annotated. + * The returned array consists of class names, with a sub-key having both 'annotation' and 'properties' set. + * The annotation contains the class's annotation, while properties contains each property that has to be indexed. + * Each property might either have TRUE as value, or also an annotation instance, if given. + * + * @throws \Flowpack\ElasticSearch\Exception + * @param ObjectManagerInterface $objectManager + * @return array + * @Flow\CompileStatic + */ + public static function buildIndexClassesAndProperties($objectManager) + { + /** @var ReflectionService $reflectionService */ + $reflectionService = $objectManager->get('TYPO3\Flow\Reflection\ReflectionService'); + + $indexAnnotations = array(); + + $annotationClassName = 'Flowpack\ElasticSearch\Annotations\Indexable'; + foreach ($reflectionService->getClassNamesByAnnotation($annotationClassName) as $className) { + if ($reflectionService->isClassAbstract($className)) { + throw new \Flowpack\ElasticSearch\Exception(sprintf('The class with name "%s" is annotated with %s, but is abstract. Indexable classes must not be abstract.', $className, $annotationClassName), 1339595182); + } + $indexAnnotations[$className]['annotation'] = $reflectionService->getClassAnnotation($className, $annotationClassName); + + // if no single properties are set to be indexed, consider all properties to be indexed. + $annotatedProperties = $reflectionService->getPropertyNamesByAnnotation($className, $annotationClassName); + if (!empty($annotatedProperties)) { + $indexAnnotations[$className]['properties'] = $annotatedProperties; + } else { + foreach ($reflectionService->getClassPropertyNames($className) as $propertyName) { + $indexAnnotations[$className]['properties'][] = $propertyName; + } + } + } + + return $indexAnnotations; + } } - diff --git a/Classes/Flowpack/ElasticSearch/Indexer/Object/ObjectIndexer.php b/Classes/Flowpack/ElasticSearch/Indexer/Object/ObjectIndexer.php index ac5f103..6db2c40 100644 --- a/Classes/Flowpack/ElasticSearch/Indexer/Object/ObjectIndexer.php +++ b/Classes/Flowpack/ElasticSearch/Indexer/Object/ObjectIndexer.php @@ -1,15 +1,15 @@ getIndexTypeForObject($object, $client); - if ($type === NULL) { - return NULL; - } - $data = $this->getIndexablePropertiesAndValuesFromObject($object); - - $id = $this->persistenceManager->getIdentifierByObject($object); - $document = new Document($type, $data, $id); - $document->store(); - } - - /** - * Returns a multidimensional array with the indexable, probably transformed values of an object - * - * @param $object - * - * @return array - */ - protected function getIndexablePropertiesAndValuesFromObject($object) { - $className = $this->reflectionService->getClassNameByObject($object); - $data = array(); - foreach ($this->indexInformer->getClassProperties($className) AS $propertyName) { - $value = \TYPO3\Flow\Reflection\ObjectAccess::getProperty($object, $propertyName); - if (($transformAnnotation = $this->reflectionService->getPropertyAnnotation($className, $propertyName, 'Flowpack\ElasticSearch\Annotations\Transform')) !== NULL) { - $value = $this->transformerFactory->create($transformAnnotation->type)->transformByAnnotation($value, $transformAnnotation); - } - $data[$propertyName] = $value; - } - - return $data; - } - - /** - * @param $object - * @param string $signalInformation Signal information, if called from a signal - * @param \Flowpack\ElasticSearch\Domain\Model\Client $client - */ - public function removeObject($object, $signalInformation = NULL, Client $client = NULL) { - $type = $this->getIndexTypeForObject($object, $client); - if ($type === NULL) { - return; - } - $id = $this->persistenceManager->getIdentifierByObject($object); - $type->deleteDocumentById($id); - } - - /** - * Returns if, and what, treatment an object requires regarding the index state, - * i.e. it checks the given object against the index and tells whether deletion, update or creation is required. - * - * @param $object - * @param \Flowpack\ElasticSearch\Domain\Model\Client $client - * - * @return string one of this' ACTION_TYPE_* constants or NULL if no action is required - */ - public function objectIndexActionRequired($object, Client $client = NULL) { - $type = $this->getIndexTypeForObject($object, $client); - if ($type === NULL) { - return NULL; - } - $id = $this->persistenceManager->getIdentifierByObject($object); - $document = $type->findDocumentById($id); - if ($document !== NULL) { - $objectData = $this->getIndexablePropertiesAndValuesFromObject($object); - if (strcmp(json_encode($objectData), json_encode($document->getData())) === 0) { - $actionType = NULL; - } else { - $actionType = self::ACTION_TYPE_UPDATE; - } - } else { - $actionType = self::ACTION_TYPE_CREATE; - } - - return $actionType; - } - - /** - * Returns the ElasticSearch type for a specific object, by its annotation - * - * @param $object - * @param \Flowpack\ElasticSearch\Domain\Model\Client $client - * - * @return \Flowpack\ElasticSearch\Domain\Model\GenericType - */ - protected function getIndexTypeForObject($object, Client $client = NULL) { - if ($client === NULL) { - $client = $this->client; - } - $className = $this->reflectionService->getClassNameByObject($object); - $indexAnnotation = $this->indexInformer->getClassAnnotation($className); - if ($indexAnnotation === NULL) { - return NULL; - } - $index = $client->findIndex($indexAnnotation->indexName); - $type = new GenericType($index, $indexAnnotation->typeName); - - return $type; - } - - /** - * Returns the currently used client, used for functional testing - * - * @return \Flowpack\ElasticSearch\Domain\Model\Client - */ - public function getClient() { - return $this->client; - } +class ObjectIndexer +{ + /** + * Defined action names + */ + const ACTION_TYPE_CREATE = 'create'; + const ACTION_TYPE_UPDATE = 'update'; + const ACTION_TYPE_DELETE = 'delete'; + + /** + * @Flow\Inject + * @var \TYPO3\Flow\Persistence\PersistenceManagerInterface + */ + protected $persistenceManager; + + /** + * @Flow\Inject + * @var \TYPO3\Flow\Reflection\ReflectionService + */ + protected $reflectionService; + + /** + * @Flow\Inject + * @var \Flowpack\ElasticSearch\Indexer\Object\IndexInformer + */ + protected $indexInformer; + + /** + * @Flow\Inject + * @var \Flowpack\ElasticSearch\Indexer\Object\Transform\TransformerFactory + */ + protected $transformerFactory; + + /** + * The client will be injected via Object settings, but however, each member method is able to expect a specific client. + * + * @var \Flowpack\ElasticSearch\Domain\Model\Client + */ + protected $client; + + /** + * (Re-) indexes an object to the ElasticSearch index, no matter if the change is actually required. + * + * @param object $object + * @param string $signalInformation Signal information, if called from a signal + * @param \Flowpack\ElasticSearch\Domain\Model\Client $client + * + * @return void + */ + public function indexObject($object, $signalInformation = null, Client $client = null) + { + $type = $this->getIndexTypeForObject($object, $client); + if ($type === null) { + return null; + } + $data = $this->getIndexablePropertiesAndValuesFromObject($object); + + $id = $this->persistenceManager->getIdentifierByObject($object); + $document = new Document($type, $data, $id); + $document->store(); + } + + /** + * Returns a multidimensional array with the indexable, probably transformed values of an object + * + * @param $object + * + * @return array + */ + protected function getIndexablePropertiesAndValuesFromObject($object) + { + $className = $this->reflectionService->getClassNameByObject($object); + $data = array(); + foreach ($this->indexInformer->getClassProperties($className) as $propertyName) { + $value = \TYPO3\Flow\Reflection\ObjectAccess::getProperty($object, $propertyName); + if (($transformAnnotation = $this->reflectionService->getPropertyAnnotation($className, $propertyName, 'Flowpack\ElasticSearch\Annotations\Transform')) !== null) { + $value = $this->transformerFactory->create($transformAnnotation->type)->transformByAnnotation($value, $transformAnnotation); + } + $data[$propertyName] = $value; + } + + return $data; + } + + /** + * @param $object + * @param string $signalInformation Signal information, if called from a signal + * @param \Flowpack\ElasticSearch\Domain\Model\Client $client + */ + public function removeObject($object, $signalInformation = null, Client $client = null) + { + $type = $this->getIndexTypeForObject($object, $client); + if ($type === null) { + return; + } + $id = $this->persistenceManager->getIdentifierByObject($object); + $type->deleteDocumentById($id); + } + + /** + * Returns if, and what, treatment an object requires regarding the index state, + * i.e. it checks the given object against the index and tells whether deletion, update or creation is required. + * + * @param $object + * @param \Flowpack\ElasticSearch\Domain\Model\Client $client + * + * @return string one of this' ACTION_TYPE_* constants or NULL if no action is required + */ + public function objectIndexActionRequired($object, Client $client = null) + { + $type = $this->getIndexTypeForObject($object, $client); + if ($type === null) { + return null; + } + $id = $this->persistenceManager->getIdentifierByObject($object); + $document = $type->findDocumentById($id); + if ($document !== null) { + $objectData = $this->getIndexablePropertiesAndValuesFromObject($object); + if (strcmp(json_encode($objectData), json_encode($document->getData())) === 0) { + $actionType = null; + } else { + $actionType = self::ACTION_TYPE_UPDATE; + } + } else { + $actionType = self::ACTION_TYPE_CREATE; + } + + return $actionType; + } + + /** + * Returns the ElasticSearch type for a specific object, by its annotation + * + * @param $object + * @param \Flowpack\ElasticSearch\Domain\Model\Client $client + * + * @return \Flowpack\ElasticSearch\Domain\Model\GenericType + */ + protected function getIndexTypeForObject($object, Client $client = null) + { + if ($client === null) { + $client = $this->client; + } + $className = $this->reflectionService->getClassNameByObject($object); + $indexAnnotation = $this->indexInformer->getClassAnnotation($className); + if ($indexAnnotation === null) { + return null; + } + $index = $client->findIndex($indexAnnotation->indexName); + $type = new GenericType($index, $indexAnnotation->typeName); + + return $type; + } + + /** + * Returns the currently used client, used for functional testing + * + * @return \Flowpack\ElasticSearch\Domain\Model\Client + */ + public function getClient() + { + return $this->client; + } } - diff --git a/Classes/Flowpack/ElasticSearch/Indexer/Object/Signal/Doctrine/EmitterAdapter.php b/Classes/Flowpack/ElasticSearch/Indexer/Object/Signal/Doctrine/EmitterAdapter.php index 688106b..a84a87c 100644 --- a/Classes/Flowpack/ElasticSearch/Indexer/Object/Signal/Doctrine/EmitterAdapter.php +++ b/Classes/Flowpack/ElasticSearch/Indexer/Object/Signal/Doctrine/EmitterAdapter.php @@ -1,15 +1,15 @@ signalEmitter->emitObjectUpdated($eventArguments->getEntity()); - } - - /** - * @param \Doctrine\ORM\Event\LifecycleEventArgs $eventArguments - * @return void - */ - public function postPersist(LifecycleEventArgs $eventArguments) { - $this->signalEmitter->emitObjectPersisted($eventArguments->getEntity()); - } - - /** - * @param \Doctrine\ORM\Event\LifecycleEventArgs $eventArguments - * @return void - */ - public function postRemove(LifecycleEventArgs $eventArguments) { - $this->signalEmitter->emitObjectRemoved($eventArguments->getEntity()); - } +class EmitterAdapter implements \Flowpack\ElasticSearch\Indexer\Object\Signal\EmitterAdapterInterface +{ + /** + * @Flow\Inject + * @var \Flowpack\ElasticSearch\Indexer\Object\Signal\SignalEmitter + */ + protected $signalEmitter; + + /** + * @param \Doctrine\ORM\Event\LifecycleEventArgs $eventArguments + * @return void + */ + public function postUpdate(LifecycleEventArgs $eventArguments) + { + $this->signalEmitter->emitObjectUpdated($eventArguments->getEntity()); + } + + /** + * @param \Doctrine\ORM\Event\LifecycleEventArgs $eventArguments + * @return void + */ + public function postPersist(LifecycleEventArgs $eventArguments) + { + $this->signalEmitter->emitObjectPersisted($eventArguments->getEntity()); + } + + /** + * @param \Doctrine\ORM\Event\LifecycleEventArgs $eventArguments + * @return void + */ + public function postRemove(LifecycleEventArgs $eventArguments) + { + $this->signalEmitter->emitObjectRemoved($eventArguments->getEntity()); + } } - diff --git a/Classes/Flowpack/ElasticSearch/Indexer/Object/Signal/EmitterAdapterInterface.php b/Classes/Flowpack/ElasticSearch/Indexer/Object/Signal/EmitterAdapterInterface.php index 930bc74..a554f87 100644 --- a/Classes/Flowpack/ElasticSearch/Indexer/Object/Signal/EmitterAdapterInterface.php +++ b/Classes/Flowpack/ElasticSearch/Indexer/Object/Signal/EmitterAdapterInterface.php @@ -1,15 +1,15 @@ format($annotation->options['format'] ?: 'Y-m-d'); - } + /** + * @param \DateTime $source + * @param \Flowpack\ElasticSearch\Annotations\Transform $annotation + * + * @return string + */ + public function transformByAnnotation($source, \Flowpack\ElasticSearch\Annotations\Transform $annotation) + { + return $source->format($annotation->options['format'] ?: 'Y-m-d'); + } } - diff --git a/Classes/Flowpack/ElasticSearch/Indexer/Object/Transform/ObjectIdentifierTransformer.php b/Classes/Flowpack/ElasticSearch/Indexer/Object/Transform/ObjectIdentifierTransformer.php index cea5708..004b4d8 100644 --- a/Classes/Flowpack/ElasticSearch/Indexer/Object/Transform/ObjectIdentifierTransformer.php +++ b/Classes/Flowpack/ElasticSearch/Indexer/Object/Transform/ObjectIdentifierTransformer.php @@ -1,15 +1,15 @@ persistenceManager->getIdentifierByObject($source); + /** + * @param mixed $source + * @param \Flowpack\ElasticSearch\Annotations\Transform $annotation + * @return string + */ + public function transformByAnnotation($source, \Flowpack\ElasticSearch\Annotations\Transform $annotation) + { + if ($source != null) { + return $this->persistenceManager->getIdentifierByObject($source); } return ''; - } + } } - diff --git a/Classes/Flowpack/ElasticSearch/Indexer/Object/Transform/StringCastTransformer.php b/Classes/Flowpack/ElasticSearch/Indexer/Object/Transform/StringCastTransformer.php index 706ed96..8b55282 100644 --- a/Classes/Flowpack/ElasticSearch/Indexer/Object/Transform/StringCastTransformer.php +++ b/Classes/Flowpack/ElasticSearch/Indexer/Object/Transform/StringCastTransformer.php @@ -1,39 +1,40 @@ objectManager->get($annotatedTransformer); - if (!$transformer instanceof \Flowpack\ElasticSearch\Indexer\Object\Transform\TransformerInterface) { - throw new \Flowpack\ElasticSearch\Exception(sprintf('The transformer instance "%s" does not implement the TransformerInterface.', $annotatedTransformer), 1339598316); - } + /** + * @param string $annotatedTransformer Either a full qualified class name or a shortened one which is seeked in the current package. + * + * @throws \Flowpack\ElasticSearch\Exception + * @return \Flowpack\ElasticSearch\Indexer\Object\Transform\TransformerInterface + */ + public function create($annotatedTransformer) + { + if (!class_exists($annotatedTransformer)) { + $annotatedTransformer = 'Flowpack\ElasticSearch\Indexer\Object\Transform\\' . $annotatedTransformer . 'Transformer'; + } + $transformer = $this->objectManager->get($annotatedTransformer); + if (!$transformer instanceof \Flowpack\ElasticSearch\Indexer\Object\Transform\TransformerInterface) { + throw new \Flowpack\ElasticSearch\Exception(sprintf('The transformer instance "%s" does not implement the TransformerInterface.', $annotatedTransformer), 1339598316); + } - return $transformer; - } + return $transformer; + } } - diff --git a/Classes/Flowpack/ElasticSearch/Indexer/Object/Transform/TransformerInterface.php b/Classes/Flowpack/ElasticSearch/Indexer/Object/Transform/TransformerInterface.php index 9661569..8cbe22f 100644 --- a/Classes/Flowpack/ElasticSearch/Indexer/Object/Transform/TransformerInterface.php +++ b/Classes/Flowpack/ElasticSearch/Indexer/Object/Transform/TransformerInterface.php @@ -1,35 +1,34 @@ + */ + public function buildMappingInformation() + { + if (!$this->client instanceof Model\Client) { + throw new \Flowpack\ElasticSearch\Exception('No client was given for mapping retrieval. Set a client BackendMappingBuilder->setClient().', 1339678111); + } - /** - * Builds a Mapping collection from the annotation sources that are present - * - * @throws \Flowpack\ElasticSearch\Exception - * @return \Flowpack\ElasticSearch\Mapping\MappingCollection<\Flowpack\ElasticSearch\Domain\Model\Mapping> - */ - public function buildMappingInformation() { - if (!$this->client instanceof Model\Client) { - throw new \Flowpack\ElasticSearch\Exception('No client was given for mapping retrieval. Set a client BackendMappingBuilder->setClient().', 1339678111); - } + $this->indicesWithoutTypeInformation = array(); - $this->indicesWithoutTypeInformation = array(); + $response = $this->client->request('GET', '/_mapping'); + $mappingInformation = new MappingCollection(MappingCollection::TYPE_BACKEND); + $mappingInformation->setClient($this->client); + $indexNames = $this->indexInformer->getAllIndexNames(); - $response = $this->client->request('GET', '/_mapping'); - $mappingInformation = new MappingCollection(MappingCollection::TYPE_BACKEND); - $mappingInformation->setClient($this->client); - $indexNames = $this->indexInformer->getAllIndexNames(); + foreach ($response->getTreatedContent() as $indexName => $indexSettings) { + if (!in_array($indexName, $indexNames)) { + continue; + } + $index = new Model\Index($indexName); + if (empty($indexSettings)) { + $this->indicesWithoutTypeInformation[] = $indexName; + } + foreach ($indexSettings as $typeName => $typeSettings) { + $type = new Model\GenericType($index, $typeName); + $mapping = new Model\Mapping($type); + if (isset($typeSettings['properties'])) { + foreach ($typeSettings['properties'] as $propertyName => $propertySettings) { + foreach ($propertySettings as $key => $value) { + $mapping->setPropertyByPath(array($propertyName, $key), $value); + } + } + } + $mappingInformation->add($mapping); + } + } - foreach ($response->getTreatedContent() AS $indexName => $indexSettings) { - if (!in_array($indexName, $indexNames)) { - continue; - } - $index = new Model\Index($indexName); - if (empty($indexSettings)) { - $this->indicesWithoutTypeInformation[] = $indexName; - } - foreach ($indexSettings AS $typeName => $typeSettings) { - $type = new Model\GenericType($index, $typeName); - $mapping = new Model\Mapping($type); - if (isset($typeSettings['properties'])) { - foreach ($typeSettings['properties'] AS $propertyName => $propertySettings) { - foreach ($propertySettings AS $key => $value) { - $mapping->setPropertyByPath(array($propertyName, $key), $value); - } - } - } - $mappingInformation->add($mapping); - } - } + return $mappingInformation; + } - return $mappingInformation; - } + /** + * @param \Flowpack\ElasticSearch\Domain\Model\Client $client + */ + public function setClient(Model\Client $client) + { + $this->client = $client; + } - /** - * @param \Flowpack\ElasticSearch\Domain\Model\Client $client - */ - public function setClient(Model\Client $client) { - $this->client = $client; - } + /** + * @throws \Flowpack\ElasticSearch\Exception + * @return array + */ + public function getIndicesWithoutTypeInformation() + { + if ($this->indicesWithoutTypeInformation === null) { + throw new \Flowpack\ElasticSearch\Exception('For getting the indices having no mapping information attached, BackendMappingBuilder->buildMappingInformation() has to be run first.', 1339751812); + } - /** - * @throws \Flowpack\ElasticSearch\Exception - * @return array - */ - public function getIndicesWithoutTypeInformation() { - if ($this->indicesWithoutTypeInformation === NULL) { - throw new \Flowpack\ElasticSearch\Exception('For getting the indices having no mapping information attached, BackendMappingBuilder->buildMappingInformation() has to be run first.', 1339751812); - } - - return $this->indicesWithoutTypeInformation; - } + return $this->indicesWithoutTypeInformation; + } } - diff --git a/Classes/Flowpack/ElasticSearch/Mapping/EntityMappingBuilder.php b/Classes/Flowpack/ElasticSearch/Mapping/EntityMappingBuilder.php index cfadaef..d4a7945 100644 --- a/Classes/Flowpack/ElasticSearch/Mapping/EntityMappingBuilder.php +++ b/Classes/Flowpack/ElasticSearch/Mapping/EntityMappingBuilder.php @@ -1,15 +1,15 @@ - */ - public function buildMappingInformation() { - $mappings = new MappingCollection(MappingCollection::TYPE_ENTITY); - foreach ($this->indexInformer->getClassesAndAnnotations() as $className => $annotation) { - $mappings->add($this->buildMappingFromClassAndAnnotation($className, $annotation)); - } - - return $mappings; - } - - /** - * @param string $className - * @param \Flowpack\ElasticSearch\Annotations\Indexable $annotation - * @return Mapping - */ - protected function buildMappingFromClassAndAnnotation($className, \Flowpack\ElasticSearch\Annotations\Indexable $annotation) { - $index = new \Flowpack\ElasticSearch\Domain\Model\Index($annotation->indexName); - $type = new \Flowpack\ElasticSearch\Domain\Model\GenericType($index, $annotation->typeName); - $mapping = new Mapping($type); - foreach ($this->indexInformer->getClassProperties($className) AS $propertyName) { - $this->augmentMappingByProperty($mapping, $className, $propertyName); - } - - return $mapping; - } - - /** - * @param Mapping $mapping - * @param string $className - * @param string $propertyName - * - * @throws \Flowpack\ElasticSearch\Exception - * @return void - */ - protected function augmentMappingByProperty(Mapping $mapping, $className, $propertyName) { - list($propertyType) = $this->reflectionService->getPropertyTagValues($className, $propertyName, 'var'); - if (($transformAnnotation = $this->reflectionService->getPropertyAnnotation($className, $propertyName, 'Flowpack\ElasticSearch\Annotations\Transform')) !== NULL) { - $mappingType = $this->transformerFactory->create($transformAnnotation->type)->getTargetMappingType(); - } elseif (\TYPO3\Flow\Utility\TypeHandling::isSimpleType($propertyType)) { - $mappingType = $propertyType; - } elseif ($propertyType === '\DateTime') { - $mappingType = 'date'; - } else { - throw new \Flowpack\ElasticSearch\Exception('Mapping is only supported for simple types and DateTime objects; "' . $propertyType . '" given but without a Transform directive.'); - } - - $mapping->setPropertyByPath($propertyName, array('type' => $mappingType)); - - $annotation = $this->reflectionService->getPropertyAnnotation($className, $propertyName, 'Flowpack\ElasticSearch\Annotations\Mapping'); - - if ($annotation instanceof MappingAnnotation) { - $mapping->setPropertyByPath($propertyName, $this->processMappingAnnotation($annotation, $mapping->getPropertyByPath($propertyName))); - if ($annotation->getFields()) { - foreach ($annotation->getFields() as $multiFieldAnnotation) { - $multiFieldIndexName = trim($multiFieldAnnotation->index_name); - if ($multiFieldIndexName === '') { - throw new \Flowpack\ElasticSearch\Exception('Multi field require an unique index name "' . $className . '::' . $propertyName . '".'); - } - if (isset($multiFields[$multiFieldIndexName])) { - throw new \Flowpack\ElasticSearch\Exception('Duplicate index name in the same multi field is not allowed "' . $className . '::' . $propertyName . '".'); - } - $multiFieldAnnotation->type = $mappingType; - $multiFields[$multiFieldIndexName] = $this->processMappingAnnotation($multiFieldAnnotation); - } - $mapping->setPropertyByPath(array($propertyName, 'fields'), $multiFields); - } - } - } - - /** - * @param MappingAnnotation $annotation - * @param array $propertyMapping - * @return array - */ - protected function processMappingAnnotation(MappingAnnotation $annotation, $propertyMapping = array()) { - foreach ($annotation->getPropertiesArray() AS $mappingDirective => $directiveValue) { - if ($directiveValue === NULL) { - continue; - } - $propertyMapping = Arrays::setValueByPath($propertyMapping, $mappingDirective, $directiveValue); - } - - return $propertyMapping; - } +class EntityMappingBuilder +{ + /** + * @Flow\Inject + * @var \TYPO3\Flow\Reflection\ReflectionService + */ + protected $reflectionService; + + /** + * @Flow\Inject + * @var \TYPO3\Flow\Object\ObjectManagerInterface + */ + protected $objectManager; + + /** + * @Flow\Inject + * @var \Flowpack\ElasticSearch\Indexer\Object\Transform\TransformerFactory + */ + protected $transformerFactory; + + /** + * @Flow\Inject + * @var \Flowpack\ElasticSearch\Indexer\Object\IndexInformer + */ + protected $indexInformer; + + /** + * Builds a Mapping collection from the annotation sources that are present + * + * @return \Flowpack\ElasticSearch\Mapping\MappingCollection<\Flowpack\ElasticSearch\Domain\Mapping> + */ + public function buildMappingInformation() + { + $mappings = new MappingCollection(MappingCollection::TYPE_ENTITY); + foreach ($this->indexInformer->getClassesAndAnnotations() as $className => $annotation) { + $mappings->add($this->buildMappingFromClassAndAnnotation($className, $annotation)); + } + + return $mappings; + } + + /** + * @param string $className + * @param \Flowpack\ElasticSearch\Annotations\Indexable $annotation + * @return Mapping + */ + protected function buildMappingFromClassAndAnnotation($className, \Flowpack\ElasticSearch\Annotations\Indexable $annotation) + { + $index = new \Flowpack\ElasticSearch\Domain\Model\Index($annotation->indexName); + $type = new \Flowpack\ElasticSearch\Domain\Model\GenericType($index, $annotation->typeName); + $mapping = new Mapping($type); + foreach ($this->indexInformer->getClassProperties($className) as $propertyName) { + $this->augmentMappingByProperty($mapping, $className, $propertyName); + } + + return $mapping; + } + + /** + * @param Mapping $mapping + * @param string $className + * @param string $propertyName + * + * @throws \Flowpack\ElasticSearch\Exception + * @return void + */ + protected function augmentMappingByProperty(Mapping $mapping, $className, $propertyName) + { + list($propertyType) = $this->reflectionService->getPropertyTagValues($className, $propertyName, 'var'); + if (($transformAnnotation = $this->reflectionService->getPropertyAnnotation($className, $propertyName, 'Flowpack\ElasticSearch\Annotations\Transform')) !== null) { + $mappingType = $this->transformerFactory->create($transformAnnotation->type)->getTargetMappingType(); + } elseif (\TYPO3\Flow\Utility\TypeHandling::isSimpleType($propertyType)) { + $mappingType = $propertyType; + } elseif ($propertyType === '\DateTime') { + $mappingType = 'date'; + } else { + throw new \Flowpack\ElasticSearch\Exception('Mapping is only supported for simple types and DateTime objects; "' . $propertyType . '" given but without a Transform directive.'); + } + + $mapping->setPropertyByPath($propertyName, array('type' => $mappingType)); + + $annotation = $this->reflectionService->getPropertyAnnotation($className, $propertyName, 'Flowpack\ElasticSearch\Annotations\Mapping'); + + if ($annotation instanceof MappingAnnotation) { + $mapping->setPropertyByPath($propertyName, $this->processMappingAnnotation($annotation, $mapping->getPropertyByPath($propertyName))); + if ($annotation->getFields()) { + foreach ($annotation->getFields() as $multiFieldAnnotation) { + $multiFieldIndexName = trim($multiFieldAnnotation->index_name); + if ($multiFieldIndexName === '') { + throw new \Flowpack\ElasticSearch\Exception('Multi field require an unique index name "' . $className . '::' . $propertyName . '".'); + } + if (isset($multiFields[$multiFieldIndexName])) { + throw new \Flowpack\ElasticSearch\Exception('Duplicate index name in the same multi field is not allowed "' . $className . '::' . $propertyName . '".'); + } + $multiFieldAnnotation->type = $mappingType; + $multiFields[$multiFieldIndexName] = $this->processMappingAnnotation($multiFieldAnnotation); + } + $mapping->setPropertyByPath(array($propertyName, 'fields'), $multiFields); + } + } + } + + /** + * @param MappingAnnotation $annotation + * @param array $propertyMapping + * @return array + */ + protected function processMappingAnnotation(MappingAnnotation $annotation, $propertyMapping = array()) + { + foreach ($annotation->getPropertiesArray() as $mappingDirective => $directiveValue) { + if ($directiveValue === null) { + continue; + } + $propertyMapping = Arrays::setValueByPath($propertyMapping, $mappingDirective, $directiveValue); + } + + return $propertyMapping; + } } - diff --git a/Classes/Flowpack/ElasticSearch/Mapping/MappingCollection.php b/Classes/Flowpack/ElasticSearch/Mapping/MappingCollection.php index 09c556d..fd8dbea 100644 --- a/Classes/Flowpack/ElasticSearch/Mapping/MappingCollection.php +++ b/Classes/Flowpack/ElasticSearch/Mapping/MappingCollection.php @@ -1,115 +1,120 @@ type = $type; - } + /** + * @param string $type + */ + public function __construct($type = null) + { + parent::__construct(array()); + $this->type = $type; + } - /** - * Returns a new collection of mappings of this collection that are not member of the $complementCollection. - * - * @param MappingCollection $complementCollection - * @return \Flowpack\ElasticSearch\Mapping\MappingCollection - */ - public function diffAgainstCollection(MappingCollection $complementCollection) { - $returnMappings = new \Flowpack\ElasticSearch\Mapping\MappingCollection(); - foreach ($this AS $entityMapping) { - /** @var $entityMapping \Flowpack\ElasticSearch\Domain\Model\Mapping */ - $mapping = new \Flowpack\ElasticSearch\Domain\Model\Mapping(clone $entityMapping->getType()); - $saveMapping = FALSE; - foreach ($entityMapping->getProperties() AS $propertyName => $propertySettings) { - foreach ($propertySettings AS $entitySettingKey => $entitySettingValue) { - $backendSettingValue = $complementCollection->getMappingSetting($entityMapping, $propertyName, $entitySettingKey); - if ($entitySettingValue !== $backendSettingValue) { - $mapping->setPropertyByPath(array($propertyName, $entitySettingKey), $entitySettingValue); - $saveMapping = TRUE; - } - } - } - if ($saveMapping) { - $returnMappings->add($mapping); - } - } + /** + * Returns a new collection of mappings of this collection that are not member of the $complementCollection. + * + * @param MappingCollection $complementCollection + * @return \Flowpack\ElasticSearch\Mapping\MappingCollection + */ + public function diffAgainstCollection(MappingCollection $complementCollection) + { + $returnMappings = new \Flowpack\ElasticSearch\Mapping\MappingCollection(); + foreach ($this as $entityMapping) { + /** @var $entityMapping \Flowpack\ElasticSearch\Domain\Model\Mapping */ + $mapping = new \Flowpack\ElasticSearch\Domain\Model\Mapping(clone $entityMapping->getType()); + $saveMapping = false; + foreach ($entityMapping->getProperties() as $propertyName => $propertySettings) { + foreach ($propertySettings as $entitySettingKey => $entitySettingValue) { + $backendSettingValue = $complementCollection->getMappingSetting($entityMapping, $propertyName, $entitySettingKey); + if ($entitySettingValue !== $backendSettingValue) { + $mapping->setPropertyByPath(array($propertyName, $entitySettingKey), $entitySettingValue); + $saveMapping = true; + } + } + } + if ($saveMapping) { + $returnMappings->add($mapping); + } + } - return $returnMappings; - } + return $returnMappings; + } - /** - * Tells whether a member of this collection has a specific index/type/property settings value - * - * @param \Flowpack\ElasticSearch\Domain\Model\Mapping $inquirerMapping - * @param string $propertyName - * @param $settingKey - * - * @return mixed - */ - public function getMappingSetting(\Flowpack\ElasticSearch\Domain\Model\Mapping $inquirerMapping, $propertyName, $settingKey) { - foreach ($this as $memberMapping) { - /** @var $memberMapping \Flowpack\ElasticSearch\Domain\Model\Mapping */ - if ($inquirerMapping->getType()->getName() === $memberMapping->getType()->getName() - && $inquirerMapping->getType()->getIndex()->getName() === $memberMapping->getType()->getIndex()->getName()) { - return $memberMapping->getPropertyByPath(array($propertyName, $settingKey)); - } - } + /** + * Tells whether a member of this collection has a specific index/type/property settings value + * + * @param \Flowpack\ElasticSearch\Domain\Model\Mapping $inquirerMapping + * @param string $propertyName + * @param $settingKey + * + * @return mixed + */ + public function getMappingSetting(\Flowpack\ElasticSearch\Domain\Model\Mapping $inquirerMapping, $propertyName, $settingKey) + { + foreach ($this as $memberMapping) { + /** @var $memberMapping \Flowpack\ElasticSearch\Domain\Model\Mapping */ + if ($inquirerMapping->getType()->getName() === $memberMapping->getType()->getName() + && $inquirerMapping->getType()->getIndex()->getName() === $memberMapping->getType()->getIndex()->getName()) { + return $memberMapping->getPropertyByPath(array($propertyName, $settingKey)); + } + } - return NULL; - } + return null; + } - /** - * @param \Flowpack\ElasticSearch\Domain\Model\Client $client - */ - public function setClient(\Flowpack\ElasticSearch\Domain\Model\Client $client) { - $this->client = $client; - } + /** + * @param \Flowpack\ElasticSearch\Domain\Model\Client $client + */ + public function setClient(\Flowpack\ElasticSearch\Domain\Model\Client $client) + { + $this->client = $client; + } - /** - * @return \Flowpack\ElasticSearch\Domain\Model\Client - */ - public function getClient() { - return $this->client; - } + /** + * @return \Flowpack\ElasticSearch\Domain\Model\Client + */ + public function getClient() + { + return $this->client; + } - /** - * @return string - */ - public function getType() { - return $this->type; - } + /** + * @return string + */ + public function getType() + { + return $this->type; + } } - diff --git a/Classes/Flowpack/ElasticSearch/Package.php b/Classes/Flowpack/ElasticSearch/Package.php index 3d8de9e..8d03518 100644 --- a/Classes/Flowpack/ElasticSearch/Package.php +++ b/Classes/Flowpack/ElasticSearch/Package.php @@ -1,56 +1,57 @@ getSignalSlotDispatcher(); - $package = $this; - $dispatcher->connect('TYPO3\Flow\Core\Booting\Sequence', 'afterInvokeStep', function (\TYPO3\Flow\Core\Booting\Step $step) use ($package, $bootstrap) { - if ($step->getIdentifier() === 'typo3.flow:persistence') { - $package->prepareRealtimeIndexing($bootstrap); - } - }); - } + /** + * Invokes custom PHP code directly after the package manager has been initialized. + * + * @param \TYPO3\Flow\Core\Bootstrap $bootstrap The current bootstrap + * + * @return void + */ + public function boot(\TYPO3\Flow\Core\Bootstrap $bootstrap) + { + $dispatcher = $bootstrap->getSignalSlotDispatcher(); + $package = $this; + $dispatcher->connect('TYPO3\Flow\Core\Booting\Sequence', 'afterInvokeStep', function (\TYPO3\Flow\Core\Booting\Step $step) use ($package, $bootstrap) { + if ($step->getIdentifier() === 'typo3.flow:persistence') { + $package->prepareRealtimeIndexing($bootstrap); + } + }); + } - /** - * @param \TYPO3\Flow\Core\Bootstrap $bootstrap - */ - public function prepareRealtimeIndexing(\TYPO3\Flow\Core\Bootstrap $bootstrap) { - $this->configurationManager = $bootstrap->getObjectManager()->get('TYPO3\Flow\Configuration\ConfigurationManager'); - $settings = $this->configurationManager->getConfiguration(\TYPO3\Flow\Configuration\ConfigurationManager::CONFIGURATION_TYPE_SETTINGS, $this->getPackageKey()); - if (isset($settings['realtimeIndexing']['enabled']) && $settings['realtimeIndexing']['enabled'] === TRUE) { - $bootstrap->getSignalSlotDispatcher()->connect('Flowpack\ElasticSearch\Indexer\Object\Signal\SignalEmitter', 'objectUpdated', 'Flowpack\ElasticSearch\Indexer\Object\ObjectIndexer', 'indexObject'); - $bootstrap->getSignalSlotDispatcher()->connect('Flowpack\ElasticSearch\Indexer\Object\Signal\SignalEmitter', 'objectPersisted', 'Flowpack\ElasticSearch\Indexer\Object\ObjectIndexer', 'indexObject'); - $bootstrap->getSignalSlotDispatcher()->connect('Flowpack\ElasticSearch\Indexer\Object\Signal\SignalEmitter', 'objectRemoved', 'Flowpack\ElasticSearch\Indexer\Object\ObjectIndexer', 'removeObject'); - } - } + /** + * @param \TYPO3\Flow\Core\Bootstrap $bootstrap + */ + public function prepareRealtimeIndexing(\TYPO3\Flow\Core\Bootstrap $bootstrap) + { + $this->configurationManager = $bootstrap->getObjectManager()->get('TYPO3\Flow\Configuration\ConfigurationManager'); + $settings = $this->configurationManager->getConfiguration(\TYPO3\Flow\Configuration\ConfigurationManager::CONFIGURATION_TYPE_SETTINGS, $this->getPackageKey()); + if (isset($settings['realtimeIndexing']['enabled']) && $settings['realtimeIndexing']['enabled'] === true) { + $bootstrap->getSignalSlotDispatcher()->connect('Flowpack\ElasticSearch\Indexer\Object\Signal\SignalEmitter', 'objectUpdated', 'Flowpack\ElasticSearch\Indexer\Object\ObjectIndexer', 'indexObject'); + $bootstrap->getSignalSlotDispatcher()->connect('Flowpack\ElasticSearch\Indexer\Object\Signal\SignalEmitter', 'objectPersisted', 'Flowpack\ElasticSearch\Indexer\Object\ObjectIndexer', 'indexObject'); + $bootstrap->getSignalSlotDispatcher()->connect('Flowpack\ElasticSearch\Indexer\Object\Signal\SignalEmitter', 'objectRemoved', 'Flowpack\ElasticSearch\Indexer\Object\ObjectIndexer', 'removeObject'); + } + } } - diff --git a/Classes/Flowpack/ElasticSearch/Transfer/Exception.php b/Classes/Flowpack/ElasticSearch/Transfer/Exception.php index 952cb8f..34f30ab 100644 --- a/Classes/Flowpack/ElasticSearch/Transfer/Exception.php +++ b/Classes/Flowpack/ElasticSearch/Transfer/Exception.php @@ -1,62 +1,64 @@ response = $response; - $this->request = $request; - if ($request !== NULL) { - $message = sprintf("[%s %s]: %s\n\nRequest data: %s", - $request->getMethod(), - $request->getUri(), - $message . '; Response body: ' . $response->getContent(), - - $request->getContent() - ); - } - - parent::__construct($message, $code, $previous); - } - - /** - * @return \TYPO3\Flow\Http\Request - */ - public function getRequest() { - return $this->request; - } - - /** - * @return \TYPO3\Flow\Http\Response - */ - public function getResponse() { - return $this->response; - } -} +class Exception extends \Flowpack\ElasticSearch\Exception +{ + /** + * @var \TYPO3\Flow\Http\Response + */ + protected $response; + + /** + * @var \TYPO3\Flow\Http\Request + */ + protected $request; + + /** + * + */ + public function __construct($message, $code, \TYPO3\Flow\Http\Response $response, \TYPO3\Flow\Http\Request $request = null, \Exception $previous = null) + { + $this->response = $response; + $this->request = $request; + if ($request !== null) { + $message = sprintf("[%s %s]: %s\n\nRequest data: %s", + $request->getMethod(), + $request->getUri(), + $message . '; Response body: ' . $response->getContent(), + $request->getContent() + ); + } + + parent::__construct($message, $code, $previous); + } + + /** + * @return \TYPO3\Flow\Http\Request + */ + public function getRequest() + { + return $this->request; + } + + /** + * @return \TYPO3\Flow\Http\Response + */ + public function getResponse() + { + return $this->response; + } +} diff --git a/Classes/Flowpack/ElasticSearch/Transfer/Exception/ApiException.php b/Classes/Flowpack/ElasticSearch/Transfer/Exception/ApiException.php index a53cea7..da89b93 100644 --- a/Classes/Flowpack/ElasticSearch/Transfer/Exception/ApiException.php +++ b/Classes/Flowpack/ElasticSearch/Transfer/Exception/ApiException.php @@ -1,22 +1,21 @@ settings = $settings; - } + /** + * @param array $settings + * @return void + */ + public function injectSettings(array $settings) + { + $this->settings = $settings; + } - /** - * @return void - */ - public function initializeObject() { - $requestEngine = new CurlEngine(); - $requestEngine->setOption(CURLOPT_TIMEOUT, $this->settings['transfer']['connectionTimeout']); - $this->browser->setRequestEngine($requestEngine); - } + /** + * @return void + */ + public function initializeObject() + { + $requestEngine = new CurlEngine(); + $requestEngine->setOption(CURLOPT_TIMEOUT, $this->settings['transfer']['connectionTimeout']); + $this->browser->setRequestEngine($requestEngine); + } - /** - * @param string $method - * @param \Flowpack\ElasticSearch\Domain\Model\Client $client - * @param string $path - * @param array $arguments - * @param string|array $content - * - * @return \Flowpack\ElasticSearch\Transfer\Response - */ - public function request($method, \Flowpack\ElasticSearch\Domain\Model\Client $client, $path = NULL, $arguments = array(), $content = NULL) { - $clientConfigurations = $client->getClientConfigurations(); - $clientConfiguration = $clientConfigurations[0]; + /** + * @param string $method + * @param \Flowpack\ElasticSearch\Domain\Model\Client $client + * @param string $path + * @param array $arguments + * @param string|array $content + * + * @return \Flowpack\ElasticSearch\Transfer\Response + */ + public function request($method, \Flowpack\ElasticSearch\Domain\Model\Client $client, $path = null, $arguments = array(), $content = null) + { + $clientConfigurations = $client->getClientConfigurations(); + $clientConfiguration = $clientConfigurations[0]; - $uri = clone $clientConfiguration->getUri(); - if ($path !== NULL) { - $uri->setPath($uri->getPath() . $path); - } + $uri = clone $clientConfiguration->getUri(); + if ($path !== null) { + $uri->setPath($uri->getPath() . $path); + } - $response = $this->browser->request($uri, $method, $arguments, array(), array(), - is_array($content) ? json_encode($content) : $content); + $response = $this->browser->request($uri, $method, $arguments, array(), array(), + is_array($content) ? json_encode($content) : $content); - return new Response($response, $this->browser->getLastRequest()); - } + return new Response($response, $this->browser->getLastRequest()); + } } - diff --git a/Classes/Flowpack/ElasticSearch/Transfer/Response.php b/Classes/Flowpack/ElasticSearch/Transfer/Response.php index 950ec63..ddab01d 100644 --- a/Classes/Flowpack/ElasticSearch/Transfer/Response.php +++ b/Classes/Flowpack/ElasticSearch/Transfer/Response.php @@ -1,79 +1,82 @@ originalResponse = $response; - /** - * @param \TYPO3\Flow\Http\Response $response - * @param \TYPO3\Flow\Http\Request $request - * - * @throws \Flowpack\ElasticSearch\Transfer\Exception - * @throws \Flowpack\ElasticSearch\Transfer\Exception\ApiException - */ - public function __construct(\TYPO3\Flow\Http\Response $response, \TYPO3\Flow\Http\Request $request = NULL) { - $this->originalResponse = $response; + $treatedContent = json_decode($response->getContent(), true); - $treatedContent = json_decode($response->getContent(), TRUE); + if (strlen($response->getContent()) > 0) { + if ($treatedContent === null) { + throw new Exception('The request returned an invalid JSON string which was "' . $response->getContent() . '".', 1338976439, $response, $request); + } - if (strlen($response->getContent()) > 0) { - if ($treatedContent === NULL) { - throw new Exception('The request returned an invalid JSON string which was "' . $response->getContent() . '".', 1338976439, $response, $request); - } + if (array_key_exists('error', $treatedContent)) { + throw new Exception\ApiException($treatedContent['error'], 1338977435, $response, $request); + } + } - if (array_key_exists('error', $treatedContent)) { - throw new Exception\ApiException($treatedContent['error'], 1338977435, $response, $request); - } - } + $this->treatedContent = $treatedContent; + } - $this->treatedContent = $treatedContent; - } + /** + * Shortcut to response's getStatusCode + * + * @return integer + */ + public function getStatusCode() + { + return $this->originalResponse->getStatusCode(); + } - /** - * Shortcut to response's getStatusCode - * - * @return integer - */ - public function getStatusCode() { - return $this->originalResponse->getStatusCode(); - } + /** + * @return mixed + */ + public function getTreatedContent() + { + return $this->treatedContent; + } - /** - * @return mixed - */ - public function getTreatedContent() { - return $this->treatedContent; - } - - /** - * @return \TYPO3\Flow\Http\Response - */ - public function getOriginalResponse() { - return $this->originalResponse; - } + /** + * @return \TYPO3\Flow\Http\Response + */ + public function getOriginalResponse() + { + return $this->originalResponse; + } } - diff --git a/Tests/Functional/Domain/AbstractTest.php b/Tests/Functional/Domain/AbstractTest.php index ffd8842..7d2cf45 100644 --- a/Tests/Functional/Domain/AbstractTest.php +++ b/Tests/Functional/Domain/AbstractTest.php @@ -1,71 +1,72 @@ clientFactory = $this->objectManager->get('Flowpack\ElasticSearch\Domain\Factory\ClientFactory'); - $client = $this->clientFactory->create(); - $this->testingIndex = $client->findIndex('typo3_elasticsearch_functionaltests'); + $this->clientFactory = $this->objectManager->get('Flowpack\ElasticSearch\Domain\Factory\ClientFactory'); + $client = $this->clientFactory->create(); + $this->testingIndex = $client->findIndex('typo3_elasticsearch_functionaltests'); - if ($this->testingIndex->exists()) { - throw new \Exception('The index "typo3_elasticsearch_functionaltests" already existed, aborting.', 1338967487); - } else { - $this->testingIndex->create(); - $this->removeIndexOnTearDown = TRUE; - } + if ($this->testingIndex->exists()) { + throw new \Exception('The index "typo3_elasticsearch_functionaltests" already existed, aborting.', 1338967487); + } else { + $this->testingIndex->create(); + $this->removeIndexOnTearDown = true; + } - $this->additionalSetUp(); - } + $this->additionalSetUp(); + } - /** - * may be implemented by inheritors because setUp() is final. - */ - protected function additionalSetUp() { - } + /** + * may be implemented by inheritors because setUp() is final. + */ + protected function additionalSetUp() + { + } - /** - * set to final because this is an important step which may not be overridden. - */ - final public function tearDown() { - parent::tearDown(); + /** + * set to final because this is an important step which may not be overridden. + */ + final public function tearDown() + { + parent::tearDown(); - if ($this->removeIndexOnTearDown === TRUE) { - $this->testingIndex->delete(); - } - } + if ($this->removeIndexOnTearDown === true) { + $this->testingIndex->delete(); + } + } } - -?> \ No newline at end of file diff --git a/Tests/Functional/Domain/DocumentTest.php b/Tests/Functional/Domain/DocumentTest.php index c767a18..973f9cc 100644 --- a/Tests/Functional/Domain/DocumentTest.php +++ b/Tests/Functional/Domain/DocumentTest.php @@ -1,72 +1,76 @@ 'kimchy', - 'post_date' => '2009-11-15T14:12:12', - 'message' => 'trying out Elastic Search' - ) - ) - ); - } +class DocumentTest extends \Flowpack\ElasticSearch\Tests\Functional\Domain\AbstractTest +{ + /** + * Array that returns sample data. Intentionally returns only one record. + * @return array + */ + public function simpleDocumentDataProvider() + { + return array( + array( + array( + 'user' => 'kimchy', + 'post_date' => '2009-11-15T14:12:12', + 'message' => 'trying out Elastic Search' + ) + ) + ); + } - /** - * @dataProvider simpleDocumentDataProvider - * @test - */ - public function idOfFreshNewDocumentIsPopulatedAfterStoring(array $data = NULL) { - $document = new \Flowpack\ElasticSearch\Domain\Model\Document(new TwitterType($this->testingIndex), $data); - $this->assertNull($document->getId()); - $document->store(); - $this->assertRegExp('/\w+/', $document->getId()); - } + /** + * @dataProvider simpleDocumentDataProvider + * @test + */ + public function idOfFreshNewDocumentIsPopulatedAfterStoring(array $data = null) + { + $document = new \Flowpack\ElasticSearch\Domain\Model\Document(new TwitterType($this->testingIndex), $data); + $this->assertNull($document->getId()); + $document->store(); + $this->assertRegExp('/\w+/', $document->getId()); + } - /** - * @dataProvider simpleDocumentDataProvider - * @test - */ - public function versionOfFreshNewDocumentIsCreatedAfterStoringAndIncreasedAfterSubsequentStoring(array $data = NULL) { - $document = new \Flowpack\ElasticSearch\Domain\Model\Document(new TwitterType($this->testingIndex), $data); - $this->assertNull($document->getVersion()); - $document->store(); - $idAfterFirstStoring = $document->getId(); - $this->assertSame(1, $document->getVersion()); - $document->store(); - $this->assertSame(2, $document->getVersion()); - $this->assertSame($idAfterFirstStoring, $document->getId()); - } + /** + * @dataProvider simpleDocumentDataProvider + * @test + */ + public function versionOfFreshNewDocumentIsCreatedAfterStoringAndIncreasedAfterSubsequentStoring(array $data = null) + { + $document = new \Flowpack\ElasticSearch\Domain\Model\Document(new TwitterType($this->testingIndex), $data); + $this->assertNull($document->getVersion()); + $document->store(); + $idAfterFirstStoring = $document->getId(); + $this->assertSame(1, $document->getVersion()); + $document->store(); + $this->assertSame(2, $document->getVersion()); + $this->assertSame($idAfterFirstStoring, $document->getId()); + } - /** - * @dataProvider simpleDocumentDataProvider - * @test - */ - public function existingIdOfDocumentIsNotModifiedAfterStoring(array $data) { - $id = '42-1010-42'; - $document = new \Flowpack\ElasticSearch\Domain\Model\Document(new TwitterType($this->testingIndex), $data, $id); - $document->store(); - $this->assertSame($id, $document->getId()); - } -} \ No newline at end of file + /** + * @dataProvider simpleDocumentDataProvider + * @test + */ + public function existingIdOfDocumentIsNotModifiedAfterStoring(array $data) + { + $id = '42-1010-42'; + $document = new \Flowpack\ElasticSearch\Domain\Model\Document(new TwitterType($this->testingIndex), $data, $id); + $document->store(); + $this->assertSame($id, $document->getId()); + } +} diff --git a/Tests/Functional/Fixtures/JustFewPropertiesToIndex.php b/Tests/Functional/Fixtures/JustFewPropertiesToIndex.php index c2a740f..d02fd39 100644 --- a/Tests/Functional/Fixtures/JustFewPropertiesToIndex.php +++ b/Tests/Functional/Fixtures/JustFewPropertiesToIndex.php @@ -1,15 +1,15 @@ \ No newline at end of file diff --git a/Tests/Functional/Fixtures/Tweet.php b/Tests/Functional/Fixtures/Tweet.php index c7a09df..c0a0403 100644 --- a/Tests/Functional/Fixtures/Tweet.php +++ b/Tests/Functional/Fixtures/Tweet.php @@ -1,15 +1,15 @@ date = $date; + } - /** - * @param \DateTime $date - */ - public function setDate($date) { - $this->date = $date; - } + /** + * @param string $message + */ + public function setMessage($message) + { + $this->message = $message; + } - /** - * @param string $message - */ - public function setMessage($message) { - $this->message = $message; - } + /** + * @param string $username + */ + public function setUsername($username) + { + $this->username = $username; + } - /** - * @param string $username - */ - public function setUsername($username) { - $this->username = $username; - } + /** + * @return \DateTime + */ + public function getDate() + { + return $this->date; + } - /** - * @return \DateTime - */ - public function getDate() { - return $this->date; - } + /** + * @return string + */ + public function getMessage() + { + return $this->message; + } - /** - * @return string - */ - public function getMessage() { - return $this->message; - } - - /** - * @return string - */ - public function getUsername() { - return $this->username; - } + /** + * @return string + */ + public function getUsername() + { + return $this->username; + } } - -?> \ No newline at end of file diff --git a/Tests/Functional/Fixtures/TweetRepository.php b/Tests/Functional/Fixtures/TweetRepository.php index 88d9a6d..250cf1a 100644 --- a/Tests/Functional/Fixtures/TweetRepository.php +++ b/Tests/Functional/Fixtures/TweetRepository.php @@ -1,15 +1,15 @@ \ No newline at end of file diff --git a/Tests/Functional/Fixtures/TwitterType.php b/Tests/Functional/Fixtures/TwitterType.php index 679c58a..1c242b7 100644 --- a/Tests/Functional/Fixtures/TwitterType.php +++ b/Tests/Functional/Fixtures/TwitterType.php @@ -1,23 +1,21 @@ \ No newline at end of file diff --git a/Tests/Functional/Indexer/Object/IndexInformerTest.php b/Tests/Functional/Indexer/Object/IndexInformerTest.php index cf35840..43589ba 100644 --- a/Tests/Functional/Indexer/Object/IndexInformerTest.php +++ b/Tests/Functional/Indexer/Object/IndexInformerTest.php @@ -1,54 +1,59 @@ informer = $this->objectManager->get('Flowpack\ElasticSearch\Indexer\Object\IndexInformer'); - } + /** + */ + public function setUp() + { + parent::setUp(); + $this->informer = $this->objectManager->get('Flowpack\ElasticSearch\Indexer\Object\IndexInformer'); + } - /** - * @test - */ - public function classAnnotationTest() { - $actual = $this->informer->getClassAnnotation('Flowpack\ElasticSearch\Tests\Functional\Fixtures\JustFewPropertiesToIndex'); - $this->assertInstanceOf('Flowpack\ElasticSearch\Annotations\Indexable', $actual); - $this->assertSame('dummyindex', $actual->indexName); - $this->assertSame('sampletype', $actual->typeName); - } + /** + * @test + */ + public function classAnnotationTest() + { + $actual = $this->informer->getClassAnnotation('Flowpack\ElasticSearch\Tests\Functional\Fixtures\JustFewPropertiesToIndex'); + $this->assertInstanceOf('Flowpack\ElasticSearch\Annotations\Indexable', $actual); + $this->assertSame('dummyindex', $actual->indexName); + $this->assertSame('sampletype', $actual->typeName); + } - /** - * @test - */ - public function classWithOnlyOnePropertyAnnotatedHasOnlyThisPropertyToBeIndexed() { - $actual = $this->informer->getClassProperties('Flowpack\ElasticSearch\Tests\Functional\Fixtures\JustFewPropertiesToIndex'); - $this->assertCount(1, $actual); - } + /** + * @test + */ + public function classWithOnlyOnePropertyAnnotatedHasOnlyThisPropertyToBeIndexed() + { + $actual = $this->informer->getClassProperties('Flowpack\ElasticSearch\Tests\Functional\Fixtures\JustFewPropertiesToIndex'); + $this->assertCount(1, $actual); + } - /** - * @test - */ - public function classWithNoPropertyAnnotatedHasAllPropertiesToBeIndexed() { - $actual = $this->informer->getClassProperties('Flowpack\ElasticSearch\Tests\Functional\Fixtures\Tweet'); - $this->assertGreaterThan(1, $actual); - } -} \ No newline at end of file + /** + * @test + */ + public function classWithNoPropertyAnnotatedHasAllPropertiesToBeIndexed() + { + $actual = $this->informer->getClassProperties('Flowpack\ElasticSearch\Tests\Functional\Fixtures\Tweet'); + $this->assertGreaterThan(1, $actual); + } +} diff --git a/Tests/Functional/Indexer/Object/ObjectIndexerTest.php b/Tests/Functional/Indexer/Object/ObjectIndexerTest.php index b773ba8..35a9f82 100644 --- a/Tests/Functional/Indexer/Object/ObjectIndexerTest.php +++ b/Tests/Functional/Indexer/Object/ObjectIndexerTest.php @@ -1,130 +1,133 @@ testEntityRepository = new TweetRepository(); - $this->testClient = $this->objectManager->get('Flowpack\ElasticSearch\Indexer\Object\ObjectIndexer')->getClient(); - } - - /** - * @test - */ - public function persistingNewObjectTriggersIndexing() { - $testEntity = $this->createAndPersistTestEntity(); - $documentId = $this->persistenceManager->getIdentifierByObject($testEntity); - - $resultDocument = $this->testClient - ->findIndex('flow3_elasticsearch_functionaltests_twitter') - ->findType('tweet') - ->findDocumentById($documentId); - $resultData = $resultDocument->getData(); - - $this->assertEquals($testEntity->getMessage(), $resultData['message']); - $this->assertEquals($testEntity->getUsername(), $resultData['username']); - } - - /** - * @test - */ - public function updatingExistingObjectTriggersReindexing() { - $testEntity = $this->createAndPersistTestEntity(); - $identifier = $this->persistenceManager->getIdentifierByObject($testEntity); - - $initialVersion = $this->testClient - ->findIndex('flow3_elasticsearch_functionaltests_twitter') - ->findType('tweet') - ->findDocumentById($identifier) - ->getVersion(); - $this->assertInternalType('integer', $initialVersion); - - $persistedTestEntity = $this->testEntityRepository->findByIdentifier($identifier); - $persistedTestEntity->setMessage('changed message.'); - $this->testEntityRepository->update($persistedTestEntity); - $this->persistenceManager->persistAll(); - $this->persistenceManager->clearState(); - - $changedDocument = $this->testClient - ->findIndex('flow3_elasticsearch_functionaltests_twitter') - ->findType('tweet') - ->findDocumentById($identifier); - - $this->assertSame($initialVersion + 1, $changedDocument->getVersion()); - $this->assertSame($changedDocument->getField('message'), 'changed message.'); - } - - /** - * @test - */ - public function removingObjectTriggersIndexRemoval() { - $testEntity = $this->createAndPersistTestEntity(); - $identifier = $this->persistenceManager->getIdentifierByObject($testEntity); - - $initialDocument = $this->testClient - ->findIndex('flow3_elasticsearch_functionaltests_twitter') - ->findType('tweet') - ->findDocumentById($identifier); - $this->assertInstanceOf('Flowpack\ElasticSearch\Domain\Model\Document', $initialDocument); - - $persistedTestEntity = $this->testEntityRepository->findByIdentifier($identifier); - $this->testEntityRepository->remove($persistedTestEntity); - $this->persistenceManager->persistAll(); - $this->persistenceManager->clearState(); - - $foundDocument = $this->testClient - ->findIndex('flow3_elasticsearch_functionaltests_twitter') - ->findType('tweet') - ->findDocumentById($identifier); - $this->assertNull($foundDocument); - } - - /** - */ - protected function createAndPersistTestEntity() { - $testEntity = new Tweet(); - $testEntity->setDate(new \DateTime()); - $testEntity->setMessage('This is a test message ' . \TYPO3\Flow\Utility\Algorithms::generateRandomString(8)); - $testEntity->setUsername('Zak McKracken' . \TYPO3\Flow\Utility\Algorithms::generateRandomString(8)); - - $this->testEntityRepository->add($testEntity); - $this->persistenceManager->persistAll(); - $this->persistenceManager->clearState(); - return $testEntity; - } +class ObjectIndexerTest extends \TYPO3\Flow\Tests\FunctionalTestCase +{ + /** + * @var boolean + */ + protected static $testablePersistenceEnabled = true; + + /** + * @var TweetRepository + */ + protected $testEntityRepository; + + /** + * @var \Flowpack\ElasticSearch\Domain\Model\Client + */ + protected $testClient; + + /** + */ + public function setUp() + { + parent::setUp(); + $this->testEntityRepository = new TweetRepository(); + $this->testClient = $this->objectManager->get('Flowpack\ElasticSearch\Indexer\Object\ObjectIndexer')->getClient(); + } + + /** + * @test + */ + public function persistingNewObjectTriggersIndexing() + { + $testEntity = $this->createAndPersistTestEntity(); + $documentId = $this->persistenceManager->getIdentifierByObject($testEntity); + + $resultDocument = $this->testClient + ->findIndex('flow3_elasticsearch_functionaltests_twitter') + ->findType('tweet') + ->findDocumentById($documentId); + $resultData = $resultDocument->getData(); + + $this->assertEquals($testEntity->getMessage(), $resultData['message']); + $this->assertEquals($testEntity->getUsername(), $resultData['username']); + } + + /** + * @test + */ + public function updatingExistingObjectTriggersReindexing() + { + $testEntity = $this->createAndPersistTestEntity(); + $identifier = $this->persistenceManager->getIdentifierByObject($testEntity); + + $initialVersion = $this->testClient + ->findIndex('flow3_elasticsearch_functionaltests_twitter') + ->findType('tweet') + ->findDocumentById($identifier) + ->getVersion(); + $this->assertInternalType('integer', $initialVersion); + + $persistedTestEntity = $this->testEntityRepository->findByIdentifier($identifier); + $persistedTestEntity->setMessage('changed message.'); + $this->testEntityRepository->update($persistedTestEntity); + $this->persistenceManager->persistAll(); + $this->persistenceManager->clearState(); + + $changedDocument = $this->testClient + ->findIndex('flow3_elasticsearch_functionaltests_twitter') + ->findType('tweet') + ->findDocumentById($identifier); + + $this->assertSame($initialVersion + 1, $changedDocument->getVersion()); + $this->assertSame($changedDocument->getField('message'), 'changed message.'); + } + + /** + * @test + */ + public function removingObjectTriggersIndexRemoval() + { + $testEntity = $this->createAndPersistTestEntity(); + $identifier = $this->persistenceManager->getIdentifierByObject($testEntity); + + $initialDocument = $this->testClient + ->findIndex('flow3_elasticsearch_functionaltests_twitter') + ->findType('tweet') + ->findDocumentById($identifier); + $this->assertInstanceOf('Flowpack\ElasticSearch\Domain\Model\Document', $initialDocument); + + $persistedTestEntity = $this->testEntityRepository->findByIdentifier($identifier); + $this->testEntityRepository->remove($persistedTestEntity); + $this->persistenceManager->persistAll(); + $this->persistenceManager->clearState(); + + $foundDocument = $this->testClient + ->findIndex('flow3_elasticsearch_functionaltests_twitter') + ->findType('tweet') + ->findDocumentById($identifier); + $this->assertNull($foundDocument); + } + + /** + */ + protected function createAndPersistTestEntity() + { + $testEntity = new Tweet(); + $testEntity->setDate(new \DateTime()); + $testEntity->setMessage('This is a test message ' . \TYPO3\Flow\Utility\Algorithms::generateRandomString(8)); + $testEntity->setUsername('Zak McKracken' . \TYPO3\Flow\Utility\Algorithms::generateRandomString(8)); + + $this->testEntityRepository->add($testEntity); + $this->persistenceManager->persistAll(); + $this->persistenceManager->clearState(); + return $testEntity; + } } - -?> \ No newline at end of file diff --git a/Tests/Functional/Mapping/MappingBuilderTest.php b/Tests/Functional/Mapping/MappingBuilderTest.php index 77754e9..62b8ce6 100644 --- a/Tests/Functional/Mapping/MappingBuilderTest.php +++ b/Tests/Functional/Mapping/MappingBuilderTest.php @@ -1,38 +1,40 @@ mappingBuilder = $this->objectManager->get('Flowpack\ElasticSearch\Mapping\EntityMappingBuilder'); - } +class MappingBuilderTest extends \TYPO3\Flow\Tests\FunctionalTestCase +{ + /** + * @var \Flowpack\ElasticSearch\Mapping\EntityMappingBuilder + */ + protected $mappingBuilder; - /** - * @test - */ - public function basicTest() { - $information = $this->mappingBuilder->buildMappingInformation(); - $this->assertGreaterThanOrEqual(2, count($information)); - $this->assertInstanceOf('Flowpack\ElasticSearch\Domain\Model\Mapping', $information[0]); - } + /** + */ + public function setUp() + { + parent::setUp(); + $this->mappingBuilder = $this->objectManager->get('Flowpack\ElasticSearch\Mapping\EntityMappingBuilder'); + } -} \ No newline at end of file + /** + * @test + */ + public function basicTest() + { + $information = $this->mappingBuilder->buildMappingInformation(); + $this->assertGreaterThanOrEqual(2, count($information)); + $this->assertInstanceOf('Flowpack\ElasticSearch\Domain\Model\Mapping', $information[0]); + } +}