/** * @param WorkflowMessage $workflowMessage * @return void */ public function handleWorkflowMessage(WorkflowMessage $workflowMessage) { if (!MessageNameUtils::isProcessingCommand($workflowMessage->messageName())) { $this->workflowEngine->dispatch(LogMessage::logUnsupportedMessageReceived($workflowMessage)); } try { if ($workflowMessage->messageType() === MessageNameUtils::COLLECT_DATA) { $processingMessage = $this->handleCollectData($workflowMessage); if (!$processingMessage instanceof ProcessingMessage) { throw new \RuntimeException(sprintf("%s::handleCollectData method returned %s instead of a ProcessingMessage", get_called_class(), is_object($processingMessage) ? get_class($processingMessage) : gettype($processingMessage))); } } else { if ($workflowMessage->messageType() === MessageNameUtils::PROCESS_DATA) { $processingMessage = $this->handleProcessData($workflowMessage); if (!$processingMessage instanceof ProcessingMessage) { throw new \RuntimeException(sprintf("%s::handleProcessData method returned %s instead of a ProcessingMessage", get_called_class(), is_object($processingMessage) ? get_class($processingMessage) : gettype($processingMessage))); } } else { $this->workflowEngine->dispatch(LogMessage::logUnsupportedMessageReceived($workflowMessage)); return; } } $this->workflowEngine->dispatch($processingMessage); return; } catch (\Exception $ex) { $this->workflowEngine->dispatch(LogMessage::logException($ex, $workflowMessage)); return; } }
/** * @param WorkflowEngine $workflowEngine */ protected function startWithoutMessage(WorkflowEngine $workflowEngine) { $taskListEntry = $this->taskList->getNextNotStartedTaskListEntry(); if ($taskListEntry) { $this->recordThat(TaskEntryMarkedAsRunning::at($taskListEntry->taskListPosition())); $task = $taskListEntry->task(); if (!$task instanceof CollectData && !$task instanceof RunSubProcess) { $this->receiveMessage(LogMessage::logNoMessageReceivedFor($task, $taskListEntry->taskListPosition()), $workflowEngine); return; } $this->performTask($task, $taskListEntry->taskListPosition(), $workflowEngine); } }
/** * If workflow message handler receives a collect-data message it forwards the message to this * method and uses the returned ProcessingMessage as response * * @param WorkflowMessage $workflowMessage * @return ProcessingMessage */ protected function handleCollectData(WorkflowMessage $workflowMessage) { if ($workflowMessage->payload()->getTypeClass() === 'Prooph\\ProcessingExample\\Type\\SourceUser') { $userData = (include __DIR__ . '/../../data/user-source-data.php'); if (!$userData) { return LogMessage::logErrorMsg("Could not read user data from examples/data/user-source-data.php. Please check the permissions", $workflowMessage); } $sourceUser = SourceUser::fromNativeValue($userData); return $workflowMessage->answerWith($sourceUser); } else { return LogMessage::logErrorMsg(sprintf('%s: Unknown type %s received', __CLASS__, $workflowMessage->payload()->getTypeClass()), $workflowMessage); } }
/** * If workflow message handler receives a process-data message it forwards the message to this * method and uses the returned ProcessingMessage as response * * @param WorkflowMessage $workflowMessage * @return ProcessingMessage */ protected function handleProcessData(WorkflowMessage $workflowMessage) { if (array_key_exists($workflowMessage->payload()->getTypeClass(), $this->getSupportedMessagesByTypeMap())) { $dataAsJsonString = json_encode($workflowMessage->payload()); $answer = $workflowMessage->answerWithDataProcessingCompleted(); try { \Zend\Stdlib\ErrorHandler::start(); if (!file_put_contents(__DIR__ . '/../../data/target-data.txt', $dataAsJsonString)) { \Zend\Stdlib\ErrorHandler::stop(true); } } catch (\Exception $ex) { $answer = \Prooph\Processing\Message\LogMessage::logException($ex, $workflowMessage); } return $answer; } else { return LogMessage::logErrorMsg(sprintf('%s: Unknown type %s received', __CLASS__, $workflowMessage->payload()->getTypeClass()), $workflowMessage); } }
/** * @param RemoteMessage $message * @return LogMessage|WorkflowMessage|StartSubProcess|SubProcessFinished * @throws \InvalidArgumentException */ public function translateToProcessingMessage(RemoteMessage $message) { if (MessageNameUtils::isWorkflowMessage($message->name())) { return WorkflowMessage::fromServiceBusMessage($message); } else { if (MessageNameUtils::isProcessingLogMessage($message->name())) { return LogMessage::fromServiceBusMessage($message); } else { if (StartSubProcess::MSG_NAME === $message->name()) { return StartSubProcess::fromServiceBusMessage($message); } else { if (SubProcessFinished::MSG_NAME === $message->name()) { return SubProcessFinished::fromServiceBusMessage($message); } } } } throw new \InvalidArgumentException(sprintf('Message with name %s can not be translated. Unknown type provided.', $message->name())); }
public function provideMessages() { $localTaskListPosition = TaskListPosition::at(TaskListId::linkWith(NodeName::defaultName(), ProcessId::generate()), 1); $remoteTaskListPosition = TaskListPosition::at(TaskListId::linkWith(NodeName::fromString("remote-system"), ProcessId::generate()), 1); $wfMessageWithoutTaskListPosition = WorkflowMessage::collectDataOf(TestUser::prototype(), 'test-case', 'localhost'); $expectedDataWfMessageWithoutTaskListPosition = ['message_id' => $wfMessageWithoutTaskListPosition->uuid()->toString(), 'message_name' => $wfMessageWithoutTaskListPosition->messageName(), 'version' => $wfMessageWithoutTaskListPosition->version(), 'task_list_position' => null, 'process_id' => null, 'status' => MessageStatus::PENDING, 'failure_msg' => null]; $wfMessageWithTaskListPosition = WorkflowMessage::collectDataOf(TestUser::prototype(), 'test-case', 'localhost'); $wfMessageWithTaskListPosition->connectToProcessTask($localTaskListPosition); $expectedDataWfMessageWithTaskListPosition = ['message_id' => $wfMessageWithTaskListPosition->uuid()->toString(), 'message_name' => $wfMessageWithTaskListPosition->messageName(), 'version' => $wfMessageWithTaskListPosition->version(), 'task_list_position' => $wfMessageWithTaskListPosition->processTaskListPosition()->toString(), 'process_id' => $wfMessageWithTaskListPosition->processTaskListPosition()->taskListId()->processId()->toString(), 'status' => MessageStatus::PENDING, 'failure_msg' => null]; $logMessage = LogMessage::logDebugMsg("Log message", $wfMessageWithTaskListPosition); $expectedDataLogMessage = ['message_id' => $logMessage->uuid()->toString(), 'message_name' => $logMessage->messageName(), 'version' => 1, 'task_list_position' => $logMessage->processTaskListPosition()->toString(), 'process_id' => $logMessage->processTaskListPosition()->taskListId()->processId()->toString(), 'status' => MessageStatus::PENDING, 'failure_msg' => null]; $startSubProcess = StartSubProcess::at($localTaskListPosition, ["process_type" => "faked"], false, "remote-system"); $expectedDataStartSubProcess = ['message_id' => $startSubProcess->uuid()->toString(), 'message_name' => $startSubProcess->messageName(), 'version' => $startSubProcess->version(), 'task_list_position' => $startSubProcess->parentTaskListPosition()->toString(), 'process_id' => $startSubProcess->parentTaskListPosition()->taskListId()->processId()->toString(), 'status' => MessageStatus::PENDING, 'failure_msg' => null]; $wfMessageWithRemoteTaskListPosition = WorkflowMessage::collectDataOf(TestUser::prototype(), 'test-case', 'localhost'); $wfMessageWithRemoteTaskListPosition->connectToProcessTask($remoteTaskListPosition); $logMessageSubProcess = LogMessage::logErrorMsg("Faked error", $wfMessageWithRemoteTaskListPosition); $subProcessFinished = SubProcessFinished::record(NodeName::fromString("remote-system"), $remoteTaskListPosition->taskListId()->processId(), false, $logMessageSubProcess, $localTaskListPosition); $expectedDataSubProcessFinished = ['message_id' => $subProcessFinished->uuid()->toString(), 'message_name' => $subProcessFinished->messageName(), 'version' => $subProcessFinished->version(), 'task_list_position' => $logMessageSubProcess->processTaskListPosition()->toString(), 'process_id' => $logMessageSubProcess->processTaskListPosition()->taskListId()->processId()->toString(), 'status' => MessageStatus::PENDING, 'failure_msg' => null]; return [[$wfMessageWithoutTaskListPosition, $expectedDataWfMessageWithoutTaskListPosition], [$wfMessageWithoutTaskListPosition->toServiceBusMessage(), $expectedDataWfMessageWithoutTaskListPosition], [$wfMessageWithTaskListPosition, $expectedDataWfMessageWithTaskListPosition], [$wfMessageWithTaskListPosition->toServiceBusMessage(), $expectedDataWfMessageWithTaskListPosition], [$logMessage, $expectedDataLogMessage], [$logMessage->toServiceBusMessage(), $expectedDataLogMessage], [$startSubProcess, $expectedDataStartSubProcess], [$startSubProcess->toServiceBusMessage(), $expectedDataStartSubProcess], [$subProcessFinished, $expectedDataSubProcessFinished], [$subProcessFinished->toServiceBusMessage(), $expectedDataSubProcessFinished]]; }
/** * @test */ public function it_translates_to_service_bus_message_and_back() { $nodeName = NodeName::fromString('other_machine'); $subProcessId = ProcessId::generate(); $parentTaskListPosition = TaskListPosition::at(TaskListId::linkWith(NodeName::defaultName(), ProcessId::generate()), 1); $wfMessage = $this->getUserDataCollectedTestMessage(); $wfMessage->connectToProcessTask(TaskListPosition::at(TaskListId::linkWith($nodeName, $subProcessId), 1)); $message = LogMessage::logDebugMsg("Processing finished", $wfMessage); $event = SubProcessFinished::record($nodeName, $subProcessId, true, $message, $parentTaskListPosition); $sbMessage = $event->toServiceBusMessage(); $this->assertInstanceOf('Prooph\\Common\\Messaging\\RemoteMessage', $sbMessage); $copyOfEvent = SubProcessFinished::fromServiceBusMessage($sbMessage); $this->assertInstanceOf('Prooph\\Processing\\Processor\\Event\\SubProcessFinished', $copyOfEvent); $this->assertTrue($nodeName->equals($copyOfEvent->processorNodeName())); $this->assertTrue($parentTaskListPosition->equals($copyOfEvent->parentTaskListPosition())); $this->assertEquals($parentTaskListPosition->taskListId()->nodeName()->toString(), $copyOfEvent->target()); $this->assertTrue($subProcessId->equals($copyOfEvent->subProcessId())); $this->assertTrue($copyOfEvent->succeed()); $this->assertEquals($message->technicalMsg(), $copyOfEvent->lastMessage()->technicalMsg()); }
/** * @test */ public function it_sends_a_sub_process_finished_event_via_message_dispatcher_to_a_handler() { $taskListPosition = TaskListPosition::at(TaskListId::linkWith(NodeName::defaultName(), ProcessId::generate()), 1); $wfMessage = $this->getUserDataCollectedTestMessage(); $wfMessage->connectToProcessTask($taskListPosition); $logMessage = LogMessage::logDebugMsg("Just a fake event", $wfMessage); $subProcessFinished = SubProcessFinished::record(NodeName::defaultName(), $taskListPosition->taskListId()->processId(), true, $logMessage, $taskListPosition); $eventBus = new EventBus(); $eventRouter = new EventRouter(); $eventRouter->route(SubProcessFinished::MSG_NAME)->to($this->messageDispatcher); $eventBus->utilize($eventRouter); $eventBus->utilize(new ForwardToRemoteMessageDispatcherStrategy(new FromProcessingMessageTranslator())); $eventBus->dispatch($subProcessFinished); /** @var $receivedMessage SubProcessFinished */ $receivedMessage = $this->receivedMessage; $this->assertInstanceOf(get_class($subProcessFinished), $receivedMessage); $this->assertTrue($taskListPosition->taskListId()->processId()->equals($receivedMessage->subProcessId())); $this->assertTrue($taskListPosition->equals($receivedMessage->parentTaskListPosition())); $this->assertTrue($subProcessFinished->uuid()->equals($receivedMessage->uuid())); $this->assertTrue($logMessage->uuid()->equals($receivedMessage->lastMessage()->uuid())); $this->assertEquals($logMessage->technicalMsg(), $receivedMessage->lastMessage()->technicalMsg()); $this->assertEquals($subProcessFinished->createdAt()->format('Y-m-d H:i:s'), $receivedMessage->createdAt()->format('Y-m-d H:i:s')); }
/** * If workflow message handler receives a process-data message it forwards the message to this * method and uses the returned ProcessingMessage as response * * @param WorkflowMessage $workflowMessage * @return ProcessingMessage */ protected function handleProcessData(WorkflowMessage $workflowMessage) { try { return $this->processData($workflowMessage); } catch (\Exception $ex) { ErrorHandler::stop(); return LogMessage::logException($ex, $workflowMessage); } }
/** * @param WorkflowMessage $message * @param bool $forceInsert * @return LogMessage|WorkflowMessage */ private function updateOrInsertPayload(WorkflowMessage $message, $forceInsert = false) { $processingType = $message->payload()->getTypeClass(); /** @var $desc Description */ $desc = $processingType::buildDescription(); $successful = 0; $failed = 0; $failedMessages = []; if ($desc->nativeType() == NativeType::COLLECTION) { /** @var $prototype Prototype */ $prototype = $processingType::prototype(); $itemProto = $prototype->typeProperties()['item']->typePrototype(); $typeObj = $message->payload()->toType(); if ($typeObj) { $this->connection->beginTransaction(); $insertStmt = null; /** @var $tableRow TableRow */ foreach ($typeObj as $i => $tableRow) { if (!$tableRow instanceof TableRow) { return LogMessage::logUnsupportedMessageReceived($message); } try { $insertStmt = $this->updateOrInsertTableRow($tableRow, $forceInsert, $insertStmt); $successful++; } catch (\Exception $e) { $datasetIndex = $tableRow->description()->hasIdentifier() ? $tableRow->description()->identifierName() . " = " . $tableRow->property($tableRow->description()->identifierName())->value() : $i; $failed++; $failedMessages[] = sprintf('Dataset %s: %s', $datasetIndex, $e->getMessage()); } } $this->connection->commit(); } $report = [MessageMetadata::SUCCESSFUL_ITEMS => $successful, MessageMetadata::FAILED_ITEMS => $failed, MessageMetadata::FAILED_MESSAGES => $failedMessages]; if ($failed > 0) { return LogMessage::logItemsProcessingFailed($successful, $failed, $failedMessages, $message); } else { return $message->answerWithDataProcessingCompleted($report); } } else { $tableRow = $message->payload()->toType(); if (!$tableRow instanceof TableRow) { return LogMessage::logUnsupportedMessageReceived($message); } $this->updateOrInsertTableRow($tableRow, $forceInsert); return $message->answerWithDataProcessingCompleted(); } }
/** * @param TaskListPosition $taskListPosition * @param WorkflowMessage|LogMessage $lastAnswer * @throws \RuntimeException If process cannot be found * @throws \Exception If error occurs during processing */ private function continueProcessAt(TaskListPosition $taskListPosition, $lastAnswer) { $process = $this->processRepository->get($taskListPosition->taskListId()->processId()); if (is_null($process)) { throw new \RuntimeException(sprintf("Last received message %s (%s) contains unknown processId. A process with id %s cannot be found!", $lastAnswer->getMessageName(), $lastAnswer->uuid()->toString(), $taskListPosition->taskListId()->processId()->toString())); } $this->beginTransaction(); try { $process->receiveMessage($lastAnswer, $this->workflowEngine); $this->commitTransaction(); } catch (\Exception $ex) { $this->rollbackTransaction(); throw $ex; } if ($process->isFinished()) { $this->processorEventQueue->enqueue($this->events()->getNewActionEvent('process_did_finish', $this, ['process_id' => $process->processId()->toString(), 'finished_at' => $lastAnswer->createdAt()->format(\DateTime::ISO8601), 'succeed' => $process->isSuccessfulDone()])); } if ($process->isSubProcess() && $process->isFinished()) { if ($process->isSuccessfulDone()) { $this->informParentProcessAboutSubProcess($process, true, $lastAnswer); } else { if (!$lastAnswer instanceof LogMessage) { $lastAnswer = LogMessage::logException(new \RuntimeException("Sub process failed but last message was not a LogMessage"), $process->parentTaskListPosition()); } if (!$lastAnswer->isError()) { $lastAnswer = LogMessage::logErrorMsg($lastAnswer->technicalMsg(), $lastAnswer); } $this->informParentProcessAboutSubProcess($process, false, $lastAnswer); } } }
protected function setLogFromArray(array $log) { foreach ($log as $sbMessageArr) { $sbMessage = RemoteMessage::fromArray($sbMessageArr); $this->log[] = LogMessage::fromServiceBusMessage($sbMessage); } }
/** * Start or continue the process with the help of given WorkflowEngine and optionally with given WorkflowMessage * * @param WorkflowEngine $workflowEngine * @param WorkflowMessage $workflowMessage * @throws \RuntimeException * @return void */ public function perform(WorkflowEngine $workflowEngine, WorkflowMessage $workflowMessage = null) { $taskListEntry = $this->taskList->getNextNotStartedTaskListEntry(); if (is_null($taskListEntry)) { throw new \RuntimeException('ChunkProcess::perform was called but there are no tasks configured!'); } if (is_null($workflowMessage)) { $this->recordThat(TaskEntryMarkedAsRunning::at($taskListEntry->taskListPosition())); $this->receiveMessage(LogMessage::logNoMessageReceivedFor($taskListEntry->task(), $taskListEntry->taskListPosition()), $workflowEngine); return; } $workflowMessage = $workflowMessage->reconnectToProcessTask($taskListEntry->taskListPosition()); if (count($this->taskList->getAllTaskListEntries()) > 1) { $this->recordThat(TaskEntryMarkedAsRunning::at($taskListEntry->taskListPosition())); $this->logErrorMsg('The ChunkProcess can only handle a single RunSubProcess task but there are more tasks configured', $workflowMessage, $workflowEngine); return; } if (!$taskListEntry->task() instanceof RunSubProcess) { $this->recordThat(TaskEntryMarkedAsRunning::at($taskListEntry->taskListPosition())); $this->logErrorMsg(sprintf('The ChunkProcess can only handle a RunSubProcess task but there is a %s task configured', get_class($taskListEntry->task())), $workflowMessage, $workflowEngine); return; } if (!MessageNameUtils::isProcessingEvent($workflowMessage->messageName())) { $this->recordThat(TaskEntryMarkedAsRunning::at($taskListEntry->taskListPosition())); $this->receiveMessage(LogMessage::logWrongMessageReceivedFor($taskListEntry->task(), $taskListEntry->taskListPosition(), $workflowMessage), $workflowEngine); return; } $collection = $workflowMessage->payload()->toType(); if (!$collection instanceof CollectionType) { $this->logErrorMsg(sprintf('The ChunkProcess requires a Prooph\\ProcessingType\\CollectionType as payload of the incoming message, but it is a %s type given', $workflowMessage->payload()->getTypeClass()), $workflowMessage, $workflowEngine); return; } $metadata = $workflowMessage->metadata(); if (!isset($metadata[self::META_OFFSET])) { $this->logErrorMsg('The ChunkProcess requires a offset key in the message metadata.', $workflowMessage, $workflowEngine); return; } if ((int) $metadata[self::META_OFFSET] !== 0) { $this->logErrorMsg('The ChunkProcess requires that the first chunk starts with a offset of 0.', $workflowMessage, $workflowEngine); return; } if (!isset($metadata[self::META_LIMIT])) { $this->logErrorMsg('The ChunkProcess requires a limit key in the message metadata.', $workflowMessage, $workflowEngine); return; } if ((int) $metadata[self::META_LIMIT] <= 0) { $this->logErrorMsg('The ChunkProcess requires that metadata.limit is greater than 0.', $workflowMessage, $workflowEngine); return; } if (!isset($metadata[self::META_TOTAL_ITEMS])) { $this->logErrorMsg('The ChunkProcess requires a total_items key in the message metadata.', $workflowMessage, $workflowEngine); return; } $this->startSubProcessForEachChunk($workflowMessage, $workflowEngine); }
/** * @test */ public function it_dispatches_a_sub_process_finished_event() { $taskListPosition = TaskListPosition::at(TaskListId::linkWith(NodeName::defaultName(), ProcessId::generate()), 1); $wfMessage = $this->getUserDataCollectedTestMessage(); $wfMessage->connectToProcessTask($taskListPosition); $logMessage = LogMessage::logDebugMsg("Just a fake event", $wfMessage); $subProcessFinished = SubProcessFinished::record(NodeName::defaultName(), $taskListPosition->taskListId()->processId(), true, $logMessage, $taskListPosition); $this->workflowEngine->dispatch($subProcessFinished); $this->assertSame($subProcessFinished, $this->receivedMessage); }
/** * @param LogMessage $message * @return MessageLogEntry */ public static function logLogMessage(LogMessage $message) { return self::createFromMessageProps($message->uuid(), $message->messageName(), 1, $message->processTaskListPosition()); }
/** * @param LogMessageReceived $event */ protected function whenLogMessageReceived(LogMessageReceived $event) { $taskListEntry = $this->taskList->getTaskListEntryAtPosition($event->taskListPosition()); $sbMessage = RemoteMessage::fromArray($event->payload()['message']); $taskListEntry->logMessage(LogMessage::fromServiceBusMessage($sbMessage)); }
/** * @test */ public function it_is_capable_of_logging_messages() { $taskListEntry = $this->getTestTaskListEntry(); $wfMessage = $this->getUserDataCollectedTestMessage(); $wfMessage->connectToProcessTask($taskListEntry->taskListPosition()); $info = LogMessage::logInfoDataProcessingStarted($wfMessage); $taskListEntry->logMessage($info); $warning = LogMessage::logWarningMsg("Just a warning", $wfMessage); $taskListEntry->logMessage($warning); $log = $taskListEntry->messageLog(); $this->assertEquals(2, count($log)); $this->assertSame($info, $log[0]); $this->assertSame($warning, $log[1]); }
/** * @test */ function it_can_log_a_items_processing_failed_message_with_a_failed_msg_for_each_failed_item() { $wfMessage = $this->getTestWorkflowMessage(); $successfulItems = 3; $failedItems = 2; $failedMsgs = ['Processing failed!', 'Processing failed, too!']; $logMsg = LogMessage::logItemsProcessingFailed($successfulItems, $failedItems, $failedMsgs, $wfMessage); $this->assertTrue($logMsg->isError()); $this->assertEquals(LogMessage::ERROR_ITEMS_PROCESSING_FAILED, $logMsg->msgCode()); $this->assertEquals('Processing for 2 of 5 items failed', $logMsg->technicalMsg()); $msgParams = $logMsg->msgParams(); $this->assertTrue(isset($msgParams[LogMessage::MSG_PARAM_SUCCESSFUL_ITEMS])); $this->assertTrue(isset($msgParams[LogMessage::MSG_PARAM_FAILED_ITEMS])); $this->assertTrue(isset($msgParams[LogMessage::MSG_PARAM_FAILED_MESSAGES])); $this->assertEquals($successfulItems, $msgParams[LogMessage::MSG_PARAM_SUCCESSFUL_ITEMS]); $this->assertEquals($failedItems, $msgParams[LogMessage::MSG_PARAM_FAILED_ITEMS]); $this->assertEquals($failedMsgs, $msgParams[LogMessage::MSG_PARAM_FAILED_MESSAGES]); }
/** * If workflow message handler receives a process-data message it forwards the message to this * method and uses the returned ProcessingMessage as response * * @param WorkflowMessage $workflowMessage * @return ProcessingMessage */ protected function handleProcessData(WorkflowMessage $workflowMessage) { $this->lastProcessDataMessage = $workflowMessage; return LogMessage::logDebugMsg("Process data message received", $workflowMessage->processTaskListPosition()); }
/** * @test */ public function it_marks_task_of_parent_process_as_failed_when_sub_process_is_finished_with_error() { $this->setUpOtherMachine(); $wfMessage = $this->getUserDataCollectedTestMessage(); /** * Change type to scenario 2 type, so that @see \Prooph\ProcessingTest\TestCase::getTestProcessFactory * set up the right process */ $wfMessage->changeProcessingType('Prooph\\ProcessingTest\\Mock\\UserDictionaryS2'); $this->getTestWorkflowProcessor()->receiveMessage($wfMessage); $receivedMessage = $this->otherMachineWorkflowMessageHandler->lastWorkflowMessage(); $this->assertNotNull($receivedMessage); $error = LogMessage::logErrorMsg("Simulated error", $receivedMessage); $this->getOtherMachineWorkflowProcessor()->receiveMessage($error); $this->assertNotNull($this->lastPostCommitEvent); $expectedEventNamesLocalhost = ['Prooph\\Processing\\Processor\\Event\\ProcessWasSetUp', 'Prooph\\Processing\\Processor\\Task\\Event\\TaskEntryMarkedAsRunning', 'Prooph\\Processing\\Processor\\Task\\Event\\LogMessageReceived', 'Prooph\\Processing\\Processor\\Task\\Event\\TaskEntryMarkedAsFailed']; $expectedEventNamesOtherMachine = ['Prooph\\Processing\\Processor\\Event\\ProcessWasSetUp', 'Prooph\\Processing\\Processor\\Task\\Event\\TaskEntryMarkedAsRunning', 'Prooph\\Processing\\Processor\\Task\\Event\\LogMessageReceived', 'Prooph\\Processing\\Processor\\Task\\Event\\TaskEntryMarkedAsFailed']; $this->assertEquals($expectedEventNamesLocalhost, $this->eventNameLog); $this->assertEquals($expectedEventNamesOtherMachine, $this->otherMachineEventNameLog); }
/** * Start or continue the process with the help of given WorkflowEngine and optionally with given WorkflowMessage * * @param WorkflowEngine $workflowEngine * @param WorkflowMessage $workflowMessage * @throws \RuntimeException * @throws \BadMethodCallException * @return void */ public function perform(WorkflowEngine $workflowEngine, WorkflowMessage $workflowMessage = null) { $taskListEntry = $this->taskList->getNextNotStartedTaskListEntry(); if (is_null($taskListEntry)) { throw new \RuntimeException('ForEachProcess::perform was called but there are no tasks configured!'); } if (is_null($workflowMessage)) { $this->recordThat(TaskEntryMarkedAsRunning::at($taskListEntry->taskListPosition())); $this->receiveMessage(LogMessage::logNoMessageReceivedFor($taskListEntry->task(), $taskListEntry->taskListPosition()), $workflowEngine); return; } $workflowMessage = $workflowMessage->reconnectToProcessTask($taskListEntry->taskListPosition()); if (count($this->taskList->getAllTaskListEntries()) > 1) { $this->recordThat(TaskEntryMarkedAsRunning::at($taskListEntry->taskListPosition())); $this->receiveMessage(LogMessage::logErrorMsg('The ForEachProcess can only handle a single RunSubProcess task but there are more tasks configured', $workflowMessage), $workflowEngine); return; } if (!$taskListEntry->task() instanceof RunSubProcess) { $this->recordThat(TaskEntryMarkedAsRunning::at($taskListEntry->taskListPosition())); $this->receiveMessage(LogMessage::logErrorMsg(sprintf('The ForEachProcess can only handle a RunSubProcess task but there is a %s task configured', get_class($taskListEntry->task())), $workflowMessage), $workflowEngine); return; } if (!MessageNameUtils::isProcessingEvent($workflowMessage->messageName())) { $this->recordThat(TaskEntryMarkedAsRunning::at($taskListEntry->taskListPosition())); $this->receiveMessage(LogMessage::logWrongMessageReceivedFor($taskListEntry->task(), $taskListEntry->taskListPosition(), $workflowMessage), $workflowEngine); return; } $collection = $workflowMessage->payload()->toType(); if (!$collection instanceof CollectionType) { $this->receiveMessage(LogMessage::logErrorMsg(sprintf('The ForEachProcess requires a Prooph\\ProcessingType\\CollectionType as payload of the incoming message, but it is a %s type given', $workflowMessage->payload()->getTypeClass()), $workflowMessage), $workflowEngine); return; } $this->startSubProcessForEachItem($collection, $workflowEngine); }
/** * @return WorkflowMessage|LogMessage * @throws \RuntimeException */ public function lastMessage() { $sbMessage = RemoteMessage::fromArray($this->payload['last_message']); if (MessageNameUtils::isProcessingLogMessage($sbMessage->name())) { return LogMessage::fromServiceBusMessage($sbMessage); } if (MessageNameUtils::isWorkflowMessage($sbMessage->name())) { return WorkflowMessage::fromServiceBusMessage($sbMessage); } throw new \RuntimeException(sprintf("Sub process %s has received last a message with name %s that has no known message format", $this->processorNodeName() . '::' . $this->subProcessId(), $sbMessage->name())); }
/** * The workflow engine provides access to the communication layer of the processing system. * Communication is based on ProophServiceBus. We use the idea of CQRS to decouple the * workflow processor from workflow message handlers which are responsible for processing * single tasks. A workflow message handler is normally the glue component which connects * Prooph\Processing with an external system. It receives commands from the processor like collect data or * process data and send events back to tell the processor what's happened. * * Each target (external system) gets a command bus and an event bus assigned. These are the communication * channels between the processor and the target. * You can use the full power of ProophServiceBus so a channel can be a local bus, a link to a messaging infrastructure, * a link to a worker queue, or a http remote interface. * * @return \Prooph\Processing\Processor\RegistryWorkflowEngine */ function _set_up_workflow_engine() { $commandBus = new \Prooph\ServiceBus\CommandBus(); $eventBus = new \Prooph\ServiceBus\EventBus(); $commandRouter = new \Prooph\ServiceBus\Router\CommandRouter(); //For our scenario it is enough to use a closure as workflow message handler //In a production system this should be a class loadable by Zend\ServiceManager //See the more complex scenarios to get an idea how such a set up can be look like. $commandRouter->route(\Prooph\Processing\Message\MessageNameUtils::getProcessDataCommandName('Prooph\\ProcessingExample\\Type\\SourceUser'))->to(function (\Prooph\Processing\Message\WorkflowMessage $message) use($eventBus) { $dataAsJsonString = json_encode($message->payload()); $answer = $message->answerWithDataProcessingCompleted(); try { \Zend\Stdlib\ErrorHandler::start(); if (!file_put_contents('data/target-data.txt', $dataAsJsonString)) { \Zend\Stdlib\ErrorHandler::stop(true); } } catch (\Exception $ex) { $answer = \Prooph\Processing\Message\LogMessage::logException($ex, $message); } $eventBus->dispatch($answer); }); $commandBus->utilize($commandRouter); $commandBus->utilize(new \Prooph\ServiceBus\InvokeStrategy\CallbackStrategy()); $workflowEngine = new \Prooph\Processing\Processor\RegistryWorkflowEngine(); $workflowEngine->registerCommandBus($commandBus, ['target-file-writer']); $workflowEngine->registerEventBus($eventBus, [\Prooph\Processing\Processor\Definition::SERVICE_WORKFLOW_PROCESSOR]); return $workflowEngine; }
public static function record(LogMessage $logMessage) { return self::at($logMessage->processTaskListPosition(), array('message' => $logMessage->toServiceBusMessage()->toArray())); }