/** * @param ActionEvent $actionEvent */ public function onEventStoreCommitPost(ActionEvent $actionEvent) { $this->queuedActionEvents[] = $actionEvent; if (!$this->inConfirmSelectMode) { $this->inConfirmSelectMode = true; while ($actionEvent = array_shift($this->queuedActionEvents)) { $fallback = new \ArrayIterator(); $recordedEvents = $actionEvent->getParam('recordedEvents', $fallback); if ($fallback !== $recordedEvents) { $this->producer->confirmSelect(); } $countRecordedEvents = 0; foreach ($recordedEvents as $recordedEvent) { $this->eventBus->dispatch($recordedEvent); $countRecordedEvents++; } if ($fallback !== $recordedEvents) { $this->producer->setConfirmCallback(function (int $deliveryTag, bool $multiple) use($countRecordedEvents) { return $deliveryTag !== $countRecordedEvents; }, function (int $deliveryTag, bool $multiple, bool $requeue) use(&$result) { throw new RuntimeException('Could not publish all events'); }); $this->producer->waitForConfirm($this->timeout); } } $this->inConfirmSelectMode = false; } }
protected function setUp() { parent::setUp(); $eventBus = new EventBus(); $commandBus = new CommandBus(); $commandRouter = new CommandRouter(); $commandRouter->route(StartSubProcess::MSG_NAME)->to(function (StartSubProcess $command) { $this->receivedMessage = $command; }); $commandRouter->route('processing-message-proophprocessingtestmockuserdictionary-collect-data')->to(function (WorkflowMessage $message) { $this->receivedMessage = $message; }); $commandBus->utilize($commandRouter); $commandBus->utilize(new ToProcessingMessageTranslator()); $commandBus->utilize(new CallbackStrategy()); $eventRouter = new EventRouter(); $eventRouter->route('processing-message-proophprocessingtestmockuserdictionary-data-collected')->to(function (WorkflowMessage $workflowMessage) { $this->receivedMessage = $workflowMessage; }); $eventRouter->route('processing-log-message')->to(function (LogMessage $logMessage) { $this->receivedMessage = $logMessage; }); $eventRouter->route(SubProcessFinished::MSG_NAME)->to(function (SubProcessFinished $event) { $this->receivedMessage = $event; }); $eventBus->utilize($eventRouter); $eventBus->utilize(new ToProcessingMessageTranslator()); $eventBus->utilize(new CallbackStrategy()); $this->workflowEngine = new RegistryWorkflowEngine(); $this->workflowEngine->registerCommandBus($commandBus, [NodeName::defaultName()->toString(), 'test-target', 'sub-processor']); $this->workflowEngine->registerEventBus($eventBus, [NodeName::defaultName()->toString()]); }
/** * Publish recorded events on the event bus * * @param ActionEvent $actionEvent */ public function onEventStoreCommitPost(ActionEvent $actionEvent) { $recordedEvents = $actionEvent->getParam('recordedEvents', new \ArrayIterator()); foreach ($recordedEvents as $recordedEvent) { $this->eventBus->dispatch($recordedEvent); } }
/** * @param BernardMessage $message */ public function routeMessage(BernardMessage $message) { $proophMessage = $message->getProophMessage(); if ($proophMessage->messageType() === Message::TYPE_COMMAND) { $this->commandBus->dispatch($proophMessage); } else { $this->eventBus->dispatch($proophMessage); } }
/** * @test */ public function it_invokes_processing_event_on_workflow_message_handler() { $userData = array('id' => 1, 'name' => 'Alex', 'address' => array('street' => 'Main Street', 'streetNumber' => 10, 'zip' => '12345', 'city' => 'Test City')); $user = UserDictionary::fromNativeValue($userData); $wfEvent = WorkflowMessage::newDataCollected($user, 'test-case', NodeName::defaultName()); $eventBus = new EventBus(); $eventRouter = new EventRouter(); $eventRouter->route($wfEvent->messageName())->to($this->workflowMessageHandler); $eventBus->utilize($eventRouter); $eventBus->utilize(new HandleWorkflowMessageInvokeStrategy()); $eventBus->dispatch($wfEvent); $this->assertSame($wfEvent, $this->workflowMessageHandler->lastWorkflowMessage()); }
/** * @interitdoc */ public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next) { $eventName = $request->getAttribute(self::NAME_ATTRIBUTE); if (null === $eventName) { return $next($request, $response, new RuntimeException(sprintf('Event name attribute ("%s") was not found in request.', self::NAME_ATTRIBUTE), Middleware::STATUS_CODE_BAD_REQUEST)); } try { $event = $this->eventFactory->createMessageFromArray($eventName, ['payload' => $request->getParsedBody(), 'metadata' => $this->metadataGatherer->getFromRequest($request)]); $this->eventBus->dispatch($event); return $response->withStatus(Middleware::STATUS_CODE_ACCEPTED); } catch (\Exception $e) { return $next($request, $response, new RuntimeException(sprintf('An error occurred during dispatching of event "%s"', $eventName), Middleware::STATUS_CODE_INTERNAL_SERVER_ERROR, $e)); } }
protected function setUp() { $this->workflowMessageHandler = new AbstractWorkflowMessageHandlerMock(); $eventBus = new EventBus(); $eventRouter = new EventRouter(); $eventRouter->route(MessageNameUtils::LOG_MESSAGE_NAME)->to(function (LogMessage $logMessage) { $this->lastProcessingMessage = $logMessage; }); $eventBus->utilize($eventRouter); $eventBus->utilize(new CallbackStrategy()); $workflowEngine = new RegistryWorkflowEngine(); $workflowEngine->registerEventBus($eventBus, [AbstractWorkflowEngine::LOCAL_CHANNEL, NodeName::defaultName()->toString()]); $this->workflowMessageHandler->useWorkflowEngine($workflowEngine); }
/** * @test */ public function it_confirms_select_one_action_event_after_the_other() { $actionEvent = $this->prophesize(ActionEvent::class); $iterator = new \ArrayIterator(['foo', 'bar']); $actionEvent->getParam('recordedEvents', new \ArrayIterator())->willReturn($iterator)->shouldBeCalled(); $producer = $this->prophesize(Producer::class); $producer->startTransaction()->shouldBeCalledTimes(2); $producer->commitTransaction()->shouldBeCalledTimes(2); $eventBus = new EventBus(); $plugin = new TransactionalEventPublisher($eventBus, $producer->reveal()); $eventBusCalls = []; $eventRouter = new EventRouter(); $eventRouter->route('foo')->to(function ($event) use($plugin, &$eventBusCalls) { $eventBusCalls[] = $event; $actionEvent = new DefaultActionEvent($event, null, ['recordedEvents' => new \ArrayIterator(['baz', 'bam', 'bat'])]); $plugin->onEventStoreCommitPost($actionEvent); }); $eventRouter->route('bar')->to(function ($event) use(&$eventBusCalls) { $eventBusCalls[] = $event; }); $eventRouter->route('baz')->to(function ($event) use(&$eventBusCalls) { $eventBusCalls[] = $event; }); $eventRouter->route('bam')->to(function ($event) use(&$eventBusCalls) { $eventBusCalls[] = $event; }); $eventRouter->route('bat')->to(function ($event) use(&$eventBusCalls) { $eventBusCalls[] = $event; }); $eventBus->utilize($eventRouter); $plugin->onEventStoreCommitPost($actionEvent->reveal()); $this->assertEquals(['foo', 'bar', 'baz', 'bam', 'bat'], $eventBusCalls); }
protected function setUp() { UsersFixture::createTableAndInsertUsers($this->getDbalConnection()); $this->messageReceiver = new StupidWorkflowProcessorMock(); $this->commandBus = new CommandBus(); $this->commandBus->utilize(new SingleTargetMessageRouter($this->messageReceiver)); $this->commandBus->utilize(new WorkflowProcessorInvokeStrategy()); $this->eventBus = new EventBus(); $this->eventBus->utilize(new SingleTargetMessageRouter($this->messageReceiver)); $this->eventBus->utilize(new WorkflowProcessorInvokeStrategy()); $this->tableGateway = new DoctrineTableGateway($this->getDbalConnection(), self::TEST_TABLE); $this->workflowEngine = new RegistryWorkflowEngine(); $this->workflowEngine->registerCommandBus($this->commandBus, [NodeName::defaultName()->toString(), 'test-case']); $this->workflowEngine->registerEventBus($this->eventBus, [NodeName::defaultName()->toString(), 'test-case']); $this->tableGateway->useWorkflowEngine($this->workflowEngine); }
/** * @interitdoc */ public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next) { $payload = null; $messageName = 'UNKNOWN'; try { $payload = $request->getParsedBody(); if (is_array($payload) && isset($payload['message_name'])) { $messageName = $payload['message_name']; } MessageDataAssertion::assert($payload); $message = $this->messageFactory->createMessageFromArray($payload['message_name'], $payload); switch ($message->messageType()) { case Message::TYPE_COMMAND: $this->commandBus->dispatch($message); return $response->withStatus(Middleware::STATUS_CODE_ACCEPTED); case Message::TYPE_EVENT: $this->eventBus->dispatch($message); return $response->withStatus(Middleware::STATUS_CODE_ACCEPTED); case Message::TYPE_QUERY: return $this->responseStrategy->fromPromise($this->queryBus->dispatch($message)); default: return $next($request, $response, new RuntimeException(sprintf('Invalid message type "%s" for message "%s".', $message->messageType(), $messageName), Middleware::STATUS_CODE_BAD_REQUEST)); } } catch (\Assert\InvalidArgumentException $e) { return $next($request, $response, new RuntimeException($e->getMessage(), Middleware::STATUS_CODE_BAD_REQUEST, $e)); } catch (\Exception $e) { return $next($request, $response, new RuntimeException(sprintf('An error occurred during dispatching of message "%s"', $messageName), Middleware::STATUS_CODE_INTERNAL_SERVER_ERROR, $e)); } }
protected function setUp() { $this->messageReceiver = new StupidWorkflowProcessorMock(); $this->commandBus = new CommandBus(); $this->commandBus->utilize(new SingleTargetMessageRouter($this->messageReceiver)); $this->commandBus->utilize(new WorkflowProcessorInvokeStrategy()); $this->eventBus = new EventBus(); $this->eventBus->utilize(new SingleTargetMessageRouter($this->messageReceiver)); $this->eventBus->utilize(new WorkflowProcessorInvokeStrategy()); $locationTranslator = new LocationTranslator(['temp' => sys_get_temp_dir(), 'testdata' => $this->getTestDataPath()]); $this->fileGateway = new FileGateway(Bootstrap::getServiceManager()->get('prooph.link.fileconnector.file_type_adapter_manager'), Bootstrap::getServiceManager()->get('prooph.link.fileconnector.filename_renderer'), $locationTranslator); $workflowEngine = new RegistryWorkflowEngine(); $workflowEngine->registerCommandBus($this->commandBus, [NodeName::defaultName()->toString(), 'file-connector']); $workflowEngine->registerEventBus($this->eventBus, [NodeName::defaultName()->toString(), 'file-connector']); $this->fileGateway->useWorkflowEngine($workflowEngine); $this->tempPath = sys_get_temp_dir() . DIRECTORY_SEPARATOR; }
/** * Perform a message */ public function perform() { $messageClass = $this->args['message_class']; /* @var $message \Prooph\Common\Messaging\RemoteMessage*/ $message = $messageClass::fromArray($this->args['message_data']); if ($message->header()->type() === MessageHeader::TYPE_COMMAND) { try { $this->commandBus->dispatch($message); } catch (CommandDispatchException $ex) { throw $ex->getPrevious(); } } else { try { $this->eventBus->dispatch($message); } catch (EventDispatchException $ex) { throw $ex->getPrevious(); } } }
/** * @param ActionEvent $actionEvent */ public function onEventStoreCommitPost(ActionEvent $actionEvent) { $this->queuedActionEvents[] = $actionEvent; if (!$this->inTransaction) { $this->inTransaction = true; while ($actionEvent = array_shift($this->queuedActionEvents)) { $fallback = new \ArrayIterator(); $recordedEvents = $actionEvent->getParam('recordedEvents', $fallback); if ($fallback !== $recordedEvents) { $this->producer->startTransaction(); } foreach ($recordedEvents as $recordedEvent) { $this->eventBus->dispatch($recordedEvent); } if ($fallback !== $recordedEvents) { $this->producer->commitTransaction(); } } $this->inTransaction = false; } }
/** * @test */ public function it_invokes_all_listeners() { $handler = new MessageHandler(); $this->eventBus->getActionEventEmitter()->attachListener(MessageBus::EVENT_ROUTE, function (ActionEvent $e) use($handler) { if ($e->getParam(MessageBus::EVENT_PARAM_MESSAGE_NAME) === CustomMessage::class) { $e->setParam(EventBus::EVENT_PARAM_EVENT_LISTENERS, [$handler, $handler]); } }); $customMessage = new CustomMessage("foo"); $this->eventBus->dispatch($customMessage); $this->assertSame($customMessage, $handler->getLastMessage()); $this->assertEquals(2, $handler->getInvokeCounter()); }
/** * @param Envelope $envelope * @param Queue $queue * @return DeliveryResult */ public function __invoke(Envelope $envelope, Queue $queue) : DeliveryResult { $data = json_decode($envelope->getBody(), true); if (!isset($data['created_at'])) { return DeliveryResult::MSG_REJECT(); } $data['created_at'] = DateTimeImmutable::createFromFormat('Y-m-d\\TH:i:s.u', $data['created_at'], new DateTimeZone('UTC')); if (false === $data['created_at']) { return DeliveryResult::MSG_REJECT(); } try { $event = $this->messageFactory->createMessageFromArray($envelope->getType(), $data); $this->eventBus->dispatch($event); } catch (\Throwable $e) { while ($e = $e->getPrevious()) { if ($e instanceof ConcurrencyException) { return DeliveryResult::MSG_REJECT_REQUEUE(); } } return DeliveryResult::MSG_REJECT(); } return DeliveryResult::MSG_ACK(); }
/** * @test */ public function it_sends_an_event_to_queue_pulls_it_with_consumer_and_forwards_it_to_event_bus() { $event = new SomethingDone(['data' => 'test event']); //The message dispatcher works with a ready-to-use bernard producer and one queue $messageProducer = new BernardMessageProducer($this->bernardProducer, 'test-queue'); //Normally you would send the event on a event bus. We skip this step here cause we are only //interested in the function of the message dispatcher $messageProducer($event); //Set up event bus which will receive the event message from the bernard consumer $consumerEventBus = new EventBus(); $somethingDoneListener = new MessageHandler(); $consumerEventBus->utilize(new EventRouter([$event->messageName() => [$somethingDoneListener]])); //We use a special bernard router which forwards all messages to a command bus or event bus depending on the //Prooph\ServiceBus\Message\MessageHeader::TYPE $bernardRouter = new BernardRouter(new CommandBus(), $consumerEventBus); $bernardConsumer = new Consumer($bernardRouter, new EventDispatcher()); //We use the same queue name here as we've defined for the message dispatcher above $bernardConsumer->tick($this->persistentFactory->create('test-queue')); $this->assertNotNull($somethingDoneListener->getLastMessage()); $this->assertEquals($event->payload(), $somethingDoneListener->getLastMessage()->payload()); }
public function publishChangesOf(ProcessingConfig $processingConfig) { foreach ($processingConfig->popRecordedEvents() as $recordedEvent) { $this->eventBus->dispatch($recordedEvent); } }
protected function setUpOtherMachine() { $this->otherMachineWorkflowMessageHandler = new TestWorkflowMessageHandler(); $commandBus = new CommandBus(); $this->otherMachineCommandRouter = new CommandRouter(); $this->otherMachineCommandRouter->route(MessageNameUtils::getCollectDataCommandName('Prooph\\ProcessingTest\\Mock\\UserDictionaryS2'))->to($this->otherMachineWorkflowMessageHandler); $this->otherMachineCommandRouter->route(MessageNameUtils::getProcessDataCommandName('Prooph\\ProcessingTest\\Mock\\TargetUserDictionary'))->to($this->otherMachineWorkflowMessageHandler); $commandBus->utilize($this->otherMachineCommandRouter); $commandBus->utilize(new HandleWorkflowMessageInvokeStrategy()); $commandBus->utilize(new WorkflowProcessorInvokeStrategy()); $commandBus->utilize(new ToProcessingMessageTranslator()); $this->otherMachineWorkflowEngine = new RegistryWorkflowEngine(); $this->otherMachineWorkflowEngine->registerCommandBus($commandBus, ['test-case', 'test-target', 'other_machine']); //Add second command bus to local workflow engine to forward StartSubProcess command to message dispatcher $this->otherMachineMessageDispatcher = new InMemoryRemoteMessageDispatcher($commandBus, new EventBus()); $parentNodeCommandBus = new CommandBus(); $parentCommandRouter = new CommandRouter(); $parentCommandRouter->route(StartSubProcess::MSG_NAME)->to($this->otherMachineMessageDispatcher); $parentNodeCommandBus->utilize($parentCommandRouter); $parentNodeCommandBus->utilize(new ForwardToRemoteMessageDispatcherStrategy(new FromProcessingMessageTranslator())); $this->workflowEngine->registerCommandBus($parentNodeCommandBus, ['other_machine']); $this->getOtherMachineWorkflowProcessor(); //Add event buses to handle SubProcessFinished event $parentNodeEventBus = new EventBus(); $parentNodeEventRouter = new EventRouter(); $parentNodeEventBus->utilize(new SingleTargetMessageRouter($this->getTestWorkflowProcessor())); $parentNodeEventBus->utilize(new ToProcessingMessageTranslator()); $parentNodeEventBus->utilize(new WorkflowProcessorInvokeStrategy()); $otherMachineEventBus = new EventBus(); $toParentNodeMessageDispatcher = new InMemoryRemoteMessageDispatcher(new CommandBus(), $parentNodeEventBus); $otherMachineEventBus->utilize(new SingleTargetMessageRouter($toParentNodeMessageDispatcher)); $otherMachineEventBus->utilize(new ForwardToRemoteMessageDispatcherStrategy(new FromProcessingMessageTranslator())); $this->otherMachineWorkflowEngine->registerEventBus($otherMachineEventBus, [Definition::DEFAULT_NODE_NAME]); }
/** * @test */ public function it_performs_next_task_after_receiving_answer_for_previous_task() { $task1 = CollectData::from('test-case', UserDictionary::prototype()); $task2 = ProcessData::address('test-target', ['Prooph\\ProcessingTest\\Mock\\UserDictionary']); $process = LinearProcess::setUp(NodeName::defaultName(), [$task1, $task2]); $wfm = WorkflowMessage::collectDataOf(UserDictionary::prototype(), NodeName::defaultName(), 'test-case'); $answer1 = $wfm->answerWith(UserDictionary::fromNativeValue(['id' => 1, 'name' => 'John Doe', 'address' => ['street' => 'Main Street', 'streetNumber' => 10, 'zip' => '12345', 'city' => 'Test City']])); $this->workflowMessageHandler->setNextAnswer($answer1); $taskListPosition = TaskListPosition::at(TaskListId::linkWith(NodeName::defaultName(), ProcessId::generate()), 2); //Fake follow up task execution $processDataMessage = $answer1->prepareDataProcessing($taskListPosition, 'test-handler'); //Remove TaskListPosition again $ref = new \ReflectionClass($processDataMessage); $taskListPositionProp = $ref->getProperty('processTaskListPosition'); $taskListPositionProp->setAccessible(true); $taskListPositionProp->setValue($processDataMessage, null); $this->commandRouter->route($processDataMessage->messageName())->to($this->workflowMessageHandler); $answer2 = $processDataMessage->answerWithDataProcessingCompleted(); $eventBus = new EventBus(); $eventRouter = new EventRouter([$answer1->messageName() => [function (WorkflowMessage $answer) use($process, $answer2) { $this->workflowMessageHandler->setNextAnswer($answer2); $process->receiveMessage($answer, $this->workflowEngine); }], $answer2->messageName() => [function (WorkflowMessage $answer) use($process) { $process->receiveMessage($answer, $this->workflowEngine); }]]); $eventBus->utilize($eventRouter)->utilize(new CallbackStrategy()); $workflowEngine = new RegistryWorkflowEngine(); $workflowEngine->registerEventBus($eventBus, [NodeName::defaultName()->toString()]); $this->workflowMessageHandler->useWorkflowEngine($workflowEngine); $process->perform($this->workflowEngine); $this->assertTrue($process->isFinished()); $this->assertTrue($process->isSuccessfulDone()); }
/** * @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')); }
/** * Dispatch a tick event * * @return array */ public function tickAction() { $this->eventBus->dispatch(TickOccurred::record()); return new JsonModel(['success' => true]); }
/** * @test */ public function it_queues_incoming_messages_during_active_transaction_to_avoid_nested_transactions() { $wfMessage = $this->getUserDataCollectedTestMessage(); $eventBus = new EventBus(); $eventBus->utilize(new SingleTargetMessageRouter($this->getTestWorkflowProcessor())); $eventBus->utilize(new WorkflowProcessorInvokeStrategy()); $workflowEngine = new RegistryWorkflowEngine(); $workflowEngine->registerEventBus($eventBus, [NodeName::defaultName()->toString()]); $this->workflowMessageHandler->useWorkflowEngine($workflowEngine); $nextAnswer = $wfMessage->prepareDataProcessing(TaskListPosition::at(TaskListId::linkWith(NodeName::defaultName(), ProcessId::generate()), 1), NodeName::defaultName())->answerWithDataProcessingCompleted(); $ref = new \ReflectionClass($nextAnswer); $refProp = $ref->getProperty('processTaskListPosition'); $refProp->setAccessible(true); $refProp->setValue($nextAnswer, null); $this->workflowMessageHandler->setNextAnswer($nextAnswer); //Without queueing incoming messages an exception will be thrown, cause the WorkflowMessageHandler answers //during active transaction and the WorkflowProcessor would try to load the not yet persisted process. $this->getTestWorkflowProcessor()->receiveMessage($wfMessage); $this->assertNotNull($this->lastPostCommitEvent); $recordedEvents = $this->lastPostCommitEvent->getRecordedEvents(); $eventNames = []; foreach ($recordedEvents as $recordedEvent) { $eventNames[] = $recordedEvent->messageName(); } $expectedEventNames = ['Prooph\\Processing\\Processor\\Task\\Event\\TaskEntryMarkedAsDone']; $this->assertEquals($expectedEventNames, $eventNames); }