/** * Handle the assertion that the given result object has no errors * * @param \TYPO3\Flow\Error\Result $result * @param boolean $expectSuccess * @return void */ protected function assertSuccess(\TYPO3\Flow\Error\Result $result, $expectSuccess = true) { if ($expectSuccess === true) { $this->assertFalse($result->hasErrors()); } else { $this->assertTrue($result->hasErrors()); } }
/** * set validation results * * @param \TYPO3\Flow\Error\Result $results */ public function setValidationResults(\TYPO3\Flow\Error\Result $results) { foreach ($results->getFlattenedErrors() as $property => $errors) { $this->errors[$property] = array(); foreach ($errors as $error) { $this->errors[$property][] = array('code' => $error->getCode(), 'message' => $error->getMessage()); } } }
/** * @test */ public function returnsAndRendersThenChildIfResultsHaveErrors() { $result = new Result(); $result->addError(new Error('I am an error', 1386163707)); /** @var $requestMock \PHPUnit_Framework_MockObject_MockObject */ $requestMock = $this->request; $requestMock->expects($this->once())->method('getInternalArgument')->with('__submittedArgumentValidationResults')->will($this->returnValue($result)); $this->viewHelper->expects($this->once())->method('renderThenChild')->will($this->returnValue('ThenChild')); $this->assertEquals('ThenChild', $this->viewHelper->render()); }
/** * Checks if the given value is valid according to the property validators. * * @param mixed $object The value that should be validated * @return void * @api */ protected function isValid($object) { $messages = new ErrorResult(); foreach ($this->propertyValidators as $propertyName => $validators) { $propertyValue = $this->getPropertyValue($object, $propertyName); $result = $this->checkProperty($propertyValue, $validators); if ($result !== null) { $messages->forProperty($propertyName)->merge($result); } } $this->result = $messages; }
/** * Assigns errors to the view and converts them to a format that Ext JS * understands. * * @param \TYPO3\Flow\Error\Result $result Errors e.g. from mapping results */ public function assignErrors(\TYPO3\Flow\Error\Result $result) { $errors = $result->getFlattenedErrors(); $output = array(); foreach ($errors as $propertyPath => $propertyErrors) { $message = ''; foreach ($propertyErrors as $propertyError) { $message .= $propertyError->getMessage(); } $output[$propertyPath] = $message; } $this->assign('value', array('errors' => $output, 'success' => FALSE)); }
/** * Sets the notices cache to TRUE and propagates the information * upwards the Result-Object Tree * * @return void */ protected function setNoticesExist() { $this->noticesExist = TRUE; if ($this->parent !== NULL) { $this->parent->setNoticesExist(); } }
/** * Builds a Mapping Collection from the configured node types * * @param \Flowpack\ElasticSearch\Domain\Model\Index $index * @return \Flowpack\ElasticSearch\Mapping\MappingCollection<\Flowpack\ElasticSearch\Domain\Model\Mapping> */ public function buildMappingInformation(Index $index) { $this->lastMappingErrors = new \TYPO3\Flow\Error\Result(); $mappings = new MappingCollection(MappingCollection::TYPE_ENTITY); /** @var NodeType $nodeType */ foreach ($this->nodeTypeManager->getNodeTypes() as $nodeTypeName => $nodeType) { if ($nodeTypeName === 'unstructured' || $nodeType->isAbstract()) { continue; } $type = $index->findType(self::convertNodeTypeNameToMappingName($nodeTypeName)); $mapping = new Mapping($type); // http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/mapping-root-object-type.html#_dynamic_templates // 'not_analyzed' is necessary $mapping->addDynamicTemplate('dimensions', array('path_match' => '__dimensionCombinations.*', 'match_mapping_type' => 'string', 'mapping' => array('type' => 'string', 'index' => 'not_analyzed'))); foreach ($nodeType->getProperties() as $propertyName => $propertyConfiguration) { if (isset($propertyConfiguration['search']) && isset($propertyConfiguration['search']['elasticSearchMapping'])) { if (is_array($propertyConfiguration['search']['elasticSearchMapping'])) { $mapping->setPropertyByPath($propertyName, $propertyConfiguration['search']['elasticSearchMapping']); } } elseif (isset($propertyConfiguration['type']) && isset($this->defaultConfigurationPerType[$propertyConfiguration['type']]['elasticSearchMapping'])) { if (is_array($this->defaultConfigurationPerType[$propertyConfiguration['type']]['elasticSearchMapping'])) { $mapping->setPropertyByPath($propertyName, $this->defaultConfigurationPerType[$propertyConfiguration['type']]['elasticSearchMapping']); } } else { $this->lastMappingErrors->addWarning(new \TYPO3\Flow\Error\Warning('Node Type "' . $nodeTypeName . '" - property "' . $propertyName . '": No ElasticSearch Mapping found.')); } } $mappings->add($mapping); } return $mappings; }
/** * @test */ public function mergeShouldMergeTwoResults() { $notice1 = $this->getMockMessage('Notice'); $notice2 = $this->getMockMessage('Notice'); $notice3 = $this->getMockMessage('Notice'); $warning1 = $this->getMockMessage('Warning'); $warning2 = $this->getMockMessage('Warning'); $warning3 = $this->getMockMessage('Warning'); $error1 = $this->getMockMessage('Error'); $error2 = $this->getMockMessage('Error'); $error3 = $this->getMockMessage('Error'); $otherResult = new \TYPO3\Flow\Error\Result(); $otherResult->addNotice($notice1); $otherResult->forProperty('foo.bar')->addNotice($notice2); $this->result->forProperty('foo')->addNotice($notice3); $otherResult->addWarning($warning1); $this->result->addWarning($warning2); $this->result->addWarning($warning3); $otherResult->forProperty('foo')->addError($error1); $otherResult->forProperty('foo')->addError($error2); $otherResult->addError($error3); $this->result->merge($otherResult); $this->assertSame(array($notice1), $this->result->getNotices(), 'Notices are not merged correctly without recursion'); $this->assertSame(array($notice3), $this->result->forProperty('foo')->getNotices(), 'Original sub-notices are overridden.'); $this->assertSame(array($notice2), $this->result->forProperty('foo')->forProperty('bar')->getNotices(), 'Sub-notices are not copied.'); $this->assertSame(array($warning2, $warning3, $warning1), $this->result->getWarnings()); $this->assertSame(array($error3), $this->result->getErrors()); $this->assertSame(array($error1, $error2), $this->result->forProperty('foo')->getErrors()); }
/** * @test */ public function hiddenFieldsAreNotRenderedIfPropertyMappingErrorsOccurred() { $mockResource = $this->getMock('TYPO3\\Flow\\Resource\\Resource'); $mockResource->expects($this->any())->method('getFilename')->will($this->returnValue('theResourceFilename')); $mockResource->expects($this->any())->method('getResourcePointer')->will($this->returnValue('theResourcePointer')); $this->viewHelper->expects($this->any())->method('getValue')->will($this->returnValue($mockResource)); $this->mockMappingResult->expects($this->atLeastOnce())->method('hasErrors')->will($this->returnValue(TRUE)); $expectedResult = ''; $this->viewHelper->initialize(); $actualResult = $this->viewHelper->render(); $this->assertSame($expectedResult, $actualResult); }
/** * @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); } 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; } $version = $content['_version']; $data = $content['_source']; return new Model\Document($type, $data, $id, $version); }
/** * @param mixed $value * @return mixed */ public function process($value) { if ($this->dataType !== null) { $value = $this->propertyMapper->convert($value, $this->dataType, $this->propertyMappingConfiguration); $messages = $this->propertyMapper->getMessages(); } else { $messages = new \TYPO3\Flow\Error\Result(); } $validationResult = $this->validator->validate($value); $messages->merge($validationResult); $this->processingMessages->merge($messages); return $value; }
/** * Internal function which actually does the property mapping. * * @param mixed $source the source data to map. MUST be a simple type, NO object allowed! * @param string $targetType The type of the target; can be either a class name or a simple type. * @param \TYPO3\Flow\Property\PropertyMappingConfigurationInterface $configuration Configuration for the property mapping. * @param array $currentPropertyPath The property path currently being mapped; used for knowing the context in case an exception is thrown. * @return mixed an instance of $targetType * @throws \TYPO3\Flow\Property\Exception\TypeConverterException * @throws \TYPO3\Flow\Property\Exception\InvalidPropertyMappingConfigurationException */ protected function doMapping($source, $targetType, \TYPO3\Flow\Property\PropertyMappingConfigurationInterface $configuration, &$currentPropertyPath) { if (is_object($source)) { $targetClass = TypeHandling::truncateElementType($targetType); if ($source instanceof $targetClass) { return $source; } } if ($source === null) { $source = ''; } $typeConverter = $this->findTypeConverter($source, $targetType, $configuration); $targetType = $typeConverter->getTargetTypeForSource($source, $targetType, $configuration); if (!is_object($typeConverter) || !$typeConverter instanceof \TYPO3\Flow\Property\TypeConverterInterface) { throw new Exception\TypeConverterException('Type converter for "' . $source . '" -> "' . $targetType . '" not found.'); } $convertedChildProperties = array(); foreach ($typeConverter->getSourceChildPropertiesToBeConverted($source) as $sourcePropertyName => $sourcePropertyValue) { $targetPropertyName = $configuration->getTargetPropertyName($sourcePropertyName); if ($configuration->shouldSkip($targetPropertyName)) { continue; } if (!$configuration->shouldMap($targetPropertyName)) { if ($configuration->shouldSkipUnknownProperties()) { continue; } throw new Exception\InvalidPropertyMappingConfigurationException('It is not allowed to map property "' . $targetPropertyName . '". You need to use $propertyMappingConfiguration->allowProperties(\'' . $targetPropertyName . '\') to enable mapping of this property.', 1335969887); } $targetPropertyType = $typeConverter->getTypeOfChildProperty($targetType, $targetPropertyName, $configuration); $subConfiguration = $configuration->getConfigurationFor($targetPropertyName); $currentPropertyPath[] = $targetPropertyName; $targetPropertyValue = $this->doMapping($sourcePropertyValue, $targetPropertyType, $subConfiguration, $currentPropertyPath); array_pop($currentPropertyPath); if (!$targetPropertyValue instanceof \TYPO3\Flow\Error\Error) { $convertedChildProperties[$targetPropertyName] = $targetPropertyValue; } } $result = $typeConverter->convertFrom($source, $targetType, $convertedChildProperties, $configuration); if ($result instanceof \TYPO3\Flow\Error\Error) { $this->messages->forProperty(implode('.', $currentPropertyPath))->addError($result); } return $result; }
/** * Validate a single configuration type * * @param string $configurationType the configuration typr to validate * @param string $path configuration path to validate, or NULL. * @param array $loadedSchemaFiles will be filled with a list of loaded schema files * @return \TYPO3\Flow\Error\Result * @throws Exception\SchemaValidationException */ protected function validateSingleType($configurationType, $path, &$loadedSchemaFiles) { $availableConfigurationTypes = $this->configurationManager->getAvailableConfigurationTypes(); if (in_array($configurationType, $availableConfigurationTypes) === FALSE) { throw new Exception\SchemaValidationException('The configuration type "' . $configurationType . '" was not found. Only the following configuration types are supported: "' . implode('", "', $availableConfigurationTypes) . '"', 1364984886); } $configuration = $this->configurationManager->getConfiguration($configurationType); // find schema files for the given type and path $schemaFileInfos = array(); $activePackages = $this->packageManager->getActivePackages(); foreach ($activePackages as $package) { $packageKey = $package->getPackageKey(); $packageSchemaPath = \TYPO3\Flow\Utility\Files::concatenatePaths(array($package->getResourcesPath(), 'Private/Schema')); if (is_dir($packageSchemaPath)) { $packageSchemaFiles = \TYPO3\Flow\Utility\Files::readDirectoryRecursively($packageSchemaPath, '.schema.yaml'); foreach ($packageSchemaFiles as $schemaFile) { $schemaName = substr($schemaFile, strlen($packageSchemaPath) + 1, -strlen('.schema.yaml')); $schemaNameParts = explode('.', str_replace('/', '.', $schemaName), 2); $schemaType = $schemaNameParts[0]; $schemaPath = isset($schemaNameParts[1]) ? $schemaNameParts[1] : NULL; if ($schemaType === $configurationType && ($path === NULL || strpos($schemaPath, $path) === 0)) { $schemaFileInfos[] = array('file' => $schemaFile, 'name' => $schemaName, 'path' => $schemaPath, 'packageKey' => $packageKey); } } } } if (count($schemaFileInfos) === 0) { throw new Exception\SchemaValidationException('No schema files found for configuration type "' . $configurationType . '"' . ($path !== NULL ? ' and path "' . $path . '".' : '.'), 1364985056); } $result = new Result(); foreach ($schemaFileInfos as $schemaFileInfo) { $loadedSchemaFiles[] = $schemaFileInfo['file']; if ($schemaFileInfo['path'] !== NULL) { $data = \TYPO3\Flow\Utility\Arrays::getValueByPath($configuration, $schemaFileInfo['path']); } else { $data = $configuration; } if (empty($data)) { throw new Exception\SchemaValidationException('The schema file "' . $schemaFileInfo['file'] . '" is empty.', 1364985445); } else { $parsedSchema = \Symfony\Component\Yaml\Yaml::parse($schemaFileInfo['file']); $validationResultForSingleSchema = $this->schemaValidator->validate($data, $parsedSchema); if ($schemaFileInfo['path'] !== NULL) { $result->forProperty($schemaFileInfo['path'])->merge($validationResultForSingleSchema); } else { $result->merge($validationResultForSingleSchema); } } } return $result; }
/** * @return boolean TRUE if the argument is valid, FALSE otherwise * @api */ public function isValid() { return !$this->validationResults->hasErrors(); }
/** * Creates a new validation error object and adds it to $this->errors * * @param string $message The error message * @param integer $code The error code (a unix timestamp) * @param array $arguments Arguments to be replaced in message * @return void * @api */ protected function addError($message, $code, array $arguments = array()) { $this->result->addError(new \TYPO3\Flow\Validation\Error($message, $code, $arguments)); }
/** * Get all property mapping / validation errors * * @return Result */ public function getValidationResults() { $results = new Result(); foreach ($this as $argument) { $argumentValidationResults = $argument->getValidationResults(); if ($argumentValidationResults === null) { continue; } $results->forProperty($argument->getName())->merge($argumentValidationResults); } return $results; }
/** * Validate a single configuration type * * @param string $configurationType the configuration typr to validate * @param string $path configuration path to validate, or NULL. * @param array $loadedSchemaFiles will be filled with a list of loaded schema files * @return \TYPO3\Flow\Error\Result * @throws Exception\SchemaValidationException */ protected function validateSingleType($configurationType, $path, &$loadedSchemaFiles) { $availableConfigurationTypes = $this->configurationManager->getAvailableConfigurationTypes(); if (in_array($configurationType, $availableConfigurationTypes) === false) { throw new Exception\SchemaValidationException('The configuration type "' . $configurationType . '" was not found. Only the following configuration types are supported: "' . implode('", "', $availableConfigurationTypes) . '"', 1364984886); } $configuration = $this->configurationManager->getConfiguration($configurationType); // find schema files for the given type and path $schemaFileInfos = []; $activePackages = $this->packageManager->getActivePackages(); foreach ($activePackages as $package) { $packageKey = $package->getPackageKey(); $packageSchemaPath = Files::concatenatePaths([$package->getResourcesPath(), 'Private/Schema']); if (is_dir($packageSchemaPath)) { foreach (Files::getRecursiveDirectoryGenerator($packageSchemaPath, '.schema.yaml') as $schemaFile) { $schemaName = substr($schemaFile, strlen($packageSchemaPath) + 1, -strlen('.schema.yaml')); $schemaNameParts = explode('.', str_replace('/', '.', $schemaName), 2); $schemaType = $schemaNameParts[0]; $schemaPath = isset($schemaNameParts[1]) ? $schemaNameParts[1] : null; if ($schemaType === $configurationType && ($path === null || strpos($schemaPath, $path) === 0)) { $schemaFileInfos[] = ['file' => $schemaFile, 'name' => $schemaName, 'path' => $schemaPath, 'packageKey' => $packageKey]; } } } } if (count($schemaFileInfos) === 0) { throw new Exception\SchemaValidationException('No schema files found for configuration type "' . $configurationType . '"' . ($path !== null ? ' and path "' . $path . '".' : '.'), 1364985056); } $result = new Result(); foreach ($schemaFileInfos as $schemaFileInfo) { $loadedSchemaFiles[] = $schemaFileInfo['file']; if ($schemaFileInfo['path'] !== null) { $data = Arrays::getValueByPath($configuration, $schemaFileInfo['path']); } else { $data = $configuration; } if (empty($data)) { $result->addNotice(new Notice('No configuration found, skipping schema "%s".', 1364985445, [substr($schemaFileInfo['file'], strlen(FLOW_PATH_ROOT))])); } else { $parsedSchema = Yaml::parse($schemaFileInfo['file']); $validationResultForSingleSchema = $this->schemaValidator->validate($data, $parsedSchema); if ($schemaFileInfo['path'] !== null) { $result->forProperty($schemaFileInfo['path'])->merge($validationResultForSingleSchema); } else { $result->merge($validationResultForSingleSchema); } } } return $result; }
/** * 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); $classesAndAnnotations = $this->indexInformer->getClassesAndAnnotations(); if ($object !== null) { if (!isset($classesAndAnnotations[$object])) { $this->outputFormatted("Error: Object '<b>%s</b>' 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 [33m%s[0m", array($className), 4); $this->outputFormatted("Index <b>%s</b> Type <b>%s</b>", 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: <b>%s</b>", array($count !== null ? $count : "[41mError[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: <b>%s</b>", array($count !== null ? $count : "[41mError[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: <b>%s</b>", array($inserted), 8); $this->outputFormatted("Objects updated: <b>%s</b>", array($updated), 8); } else { $this->outputFormatted("Modifications needed: <b>create</b> %d, <b>update</b> %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("<b>[41mError[0m</b> for [33m%s[0m:", array($className), 8); $this->outputFormatted((string) $error, array(), 4); } } } }
/** * Assert that the validation result object has no errors * * @param \TYPO3\Flow\Error\Result $result * @param string $message */ protected function assertNotHasErrors(\TYPO3\Flow\Error\Result $result, $message = '') { self::assertThat($result->hasErrors(), self::isFalse(), $message); }
/** * Creates a new validation error object and adds it to $this->errors * * @param string $message The error message * @param integer $code The error code (a unix timestamp) * @param array $arguments Arguments to be replaced in message * @return void * @api */ protected function addError($message, $code, array $arguments = []) { $this->result->addError(new ValidationError($message, $code, $arguments)); }
/** * Validate a null value with the given schema * * @param mixed $value * @param array $schema * @return ErrorResult */ protected function validateNullType($value, array $schema) { $result = new ErrorResult(); if ($value !== null) { $result->addError($this->createError('type=NULL', 'type=' . gettype($value))); } return $result; }