From 7f49900d9294bc05f053754215e2e19e95e9afb6 Mon Sep 17 00:00:00 2001 From: Walmir Silva Date: Fri, 25 Oct 2024 17:50:56 -0300 Subject: [PATCH] feat(processor-pipeline): add processingFailed method to ProcessorRuntimeException - Add new error code CODE_PROCESSING_FAILED (2606) - Add processingFailed static factory method - Support previous exception propagation - Maintain consistent error prefix pattern This addition enables proper error handling for: - Processing failures in attribute handlers - Pipeline execution errors - Property processing errors --- src/Exception/ProcessorRuntimeException.php | 21 ++-- src/Handler/AttributeHandler.php | 8 ++ src/Handler/ProcessorAttributeHandler.php | 103 ++++++++++++++++---- 3 files changed, 109 insertions(+), 23 deletions(-) diff --git a/src/Exception/ProcessorRuntimeException.php b/src/Exception/ProcessorRuntimeException.php index 7034206..293a31b 100644 --- a/src/Exception/ProcessorRuntimeException.php +++ b/src/Exception/ProcessorRuntimeException.php @@ -13,13 +13,13 @@ final class ProcessorRuntimeException extends RuntimeException private const CODE_INVALID_PROCESSOR = 2603; private const CODE_INVALID_CONTEXT = 2604; private const CODE_PROCESSOR_CONFIG_INVALID = 2605; - private const ERROR_PREFIX = 'PROCESSOR'; + private const CODE_PROCESSING_FAILED = 2606; public static function contextNotFound(string $context): self { return self::createException( self::CODE_CONTEXT_NOT_FOUND, - self::ERROR_PREFIX . '_CONTEXT_NOT_FOUND', + 'PROCESSOR_CONTEXT_NOT_FOUND', "Processor context '{$context}' not found" ); } @@ -28,7 +28,7 @@ public static function processorNotFound(string $processorName, string $context) { return self::createException( self::CODE_PROCESSOR_NOT_FOUND, - self::ERROR_PREFIX . '_NOT_FOUND', + 'PROCESSOR_NOT_FOUND', "Processor '{$processorName}' not found in context '{$context}'" ); } @@ -37,7 +37,7 @@ public static function invalidProcessor(string $processorName, string $details): { return self::createException( self::CODE_INVALID_PROCESSOR, - self::ERROR_PREFIX . '_INVALID', + 'PROCESSOR_INVALID', "Invalid processor '{$processorName}': {$details}" ); } @@ -46,7 +46,7 @@ public static function invalidContext(string $context, string $details): self { return self::createException( self::CODE_INVALID_CONTEXT, - self::ERROR_PREFIX . '_CONTEXT_INVALID', + 'PROCESSOR_CONTEXT_INVALID', "Invalid processor context '{$context}': {$details}" ); } @@ -55,8 +55,17 @@ public static function invalidConfiguration(string $processorName, string $detai { return self::createException( self::CODE_PROCESSOR_CONFIG_INVALID, - self::ERROR_PREFIX . '_CONFIG_INVALID', + 'PROCESSOR_CONFIG_INVALID', "Invalid processor configuration for '{$processorName}': {$details}" ); } + + public static function processingFailed(string $property): self + { + return self::createException( + self::CODE_PROCESSING_FAILED, + 'PROCESSOR_PROCESSING_FAILED', + "Processing failed for property '{$property}'", + ); + } } diff --git a/src/Handler/AttributeHandler.php b/src/Handler/AttributeHandler.php index 08f3853..53caf6e 100644 --- a/src/Handler/AttributeHandler.php +++ b/src/Handler/AttributeHandler.php @@ -126,4 +126,12 @@ public function getProcessingResultMessages(): array { return $this->processingResultMessages; } + + public function reset(): void + { + $this->processedPropertyValues = []; + $this->processingResultErrors = []; + $this->processingResultMessages = []; + $this->processorCache = []; + } } diff --git a/src/Handler/ProcessorAttributeHandler.php b/src/Handler/ProcessorAttributeHandler.php index 2bd1349..45412d6 100644 --- a/src/Handler/ProcessorAttributeHandler.php +++ b/src/Handler/ProcessorAttributeHandler.php @@ -5,42 +5,63 @@ namespace KaririCode\ProcessorPipeline\Handler; use KaririCode\Contract\Processor\ProcessorBuilder; -use KaririCode\ProcessorPipeline\AttributeHandler; +use KaririCode\Contract\Processor\ValidatableProcessor; use KaririCode\ProcessorPipeline\Exception\ProcessorRuntimeException; +use KaririCode\ProcessorPipeline\Processor\ProcessorConfigBuilder; +use KaririCode\ProcessorPipeline\Processor\ProcessorValidator; use KaririCode\ProcessorPipeline\Result\ProcessingResultCollection; +/** + * Handler for processing attributes with configured processors. + */ final class ProcessorAttributeHandler extends AttributeHandler { private ProcessingResultCollection $results; public function __construct( - string $identifier, - ProcessorBuilder $builder + private readonly string $identifier, + private readonly ProcessorBuilder $builder, + private readonly ProcessorValidator $validator = new ProcessorValidator(), + private readonly ProcessorConfigBuilder $configBuilder = new ProcessorConfigBuilder() ) { parent::__construct($identifier, $builder); $this->results = new ProcessingResultCollection(); } - public function processPropertyValue(string $property, mixed $value): mixed + public function handleAttribute(string $propertyName, object $attribute, mixed $value): mixed { - $processorSpecs = $this->getPropertyProcessors($property); + $result = parent::handleAttribute($propertyName, $attribute, $value); - if (empty($processorSpecs)) { - return $value; + if (null !== $result) { + $this->transferResults($propertyName); } - try { - $pipeline = $this->builder->buildPipeline( - $this->identifier, - $processorSpecs - ); + return $result; + } - $processedValue = $pipeline->process($value); - $this->results->setProcessedData($property, $processedValue); + /** + * Transfers results from parent handler to ProcessingResultCollection. + */ + private function transferResults(string $propertyName): void + { + $processedValues = parent::getProcessedPropertyValues(); + $errors = parent::getProcessingResultErrors(); - return $processedValue; - } catch (\Exception $e) { - throw ProcessorRuntimeException::processingFailed($property, $e); + if (isset($processedValues[$propertyName])) { + $this->results->setProcessedData( + $propertyName, + $processedValues[$propertyName]['value'] + ); + } + + if (isset($errors[$propertyName])) { + foreach ($errors[$propertyName] as $processorName => $error) { + $this->results->addError( + $propertyName, + $error['errorKey'] ?? 'processing_error', + $error['message'] ?? 'Unknown error' + ); + } } } @@ -57,18 +78,66 @@ public function getProcessingResultErrors(): array return $this->results->getErrors(); } + /** + * Checks if there are any processing errors. + */ public function hasErrors(): bool { return $this->results->hasErrors(); } + /** + * Gets the processing results collection. + */ public function getProcessingResults(): ProcessingResultCollection { return $this->results; } + /** + * Resets the processing state. + */ public function reset(): void { + parent::reset(); $this->results = new ProcessingResultCollection(); } + + protected function validateProcessors(array $processorsConfig, array $messages): array + { + $errors = []; + + foreach ($processorsConfig as $processorName => $config) { + $processor = $this->builder->build( + $this->identifier, + $processorName, + $config + ); + + if ($processor instanceof ValidatableProcessor && !$processor->isValid()) { + $errorKey = $processor->getErrorKey(); + $message = $messages[$processorName] ?? "Validation failed for $processorName"; + + $errors[$processorName] = [ + 'errorKey' => $errorKey, + 'message' => $message, + ]; + + $this->results->addError($processorName, $errorKey, $message); + } + } + + return $errors; + } + + protected function processValue(mixed $value, array $config): mixed + { + try { + return $this->builder + ->buildPipeline($this->identifier, $config) + ->process($value); + } catch (\Exception $e) { + throw ProcessorRuntimeException::processingFailed($value); + } + } }