/** * @return ChapterTemplate[] */ private function getTestChapterTemplates() { $startCondition = $this->prophesize(MessageCondition::class); $startCondition->isTrueFor(Argument::type(FileWasUploaded::class))->willReturn(true); $startCondition->isTrueFor(Argument::not(Argument::type(FileWasUploaded::class)))->willReturn(false); $isDoneCondition = $this->prophesize(MessageCondition::class); $isDoneCondition->isTrueFor(Argument::type(FileWasProcessed::class))->willReturn(true); $isDoneCondition->isTrueFor(Argument::not(Argument::type(FileWasProcessed::class)))->willReturn(false); $payloadGenerator = $this->prophesize(PayloadGenerator::class); $payloadGenerator->__invoke(Argument::any(), Argument::any(), Argument::any())->will(function ($args) { list($previousMessage, $payload, $metadata) = $args; $payload['filename'] = $previousMessage->payload()['filename']; }); $commandTemplate = new MessageTemplate(ProcessFile::class, new FQCNMessageFactory(), [], [], $payloadGenerator->reveal()); $chapterTemplate = new ChapterTemplate(ChapterName::fromString('File Processor'), $commandTemplate, $startCondition->reveal(), $isDoneCondition->reveal()); return [$chapterTemplate]; }
$commandBus->utilize($commandRouter); //Enable transaction handling based on command dispatch $transactionManager = new \Prooph\EventStoreBusBridge\TransactionManager($eventStore); $commandBus->utilize($transactionManager); $delayedCommandBus = new \Prooph\Done\Infrastructure\DelayedCommandBus($commandBus); //The delayed command bus queues commands until next EventStore.commit.post event occurs $delayedCommandBus->setUp($eventStore); //As a Story can handle and dispatch any message implementing Prooph\Common\Messaging\Message //it requires a message factory and message converter to be able to create and log messages correctly $messageFactory = new \Prooph\Common\Messaging\FQCNMessageFactory(); $messageConverter = new \Prooph\Common\Messaging\NoOpMessageConverter(); //Set up the story $storyName = \Prooph\Done\Shared\StoryName::fromString('Json to CSV Conversion'); //Set up the StoryTeller - basically a StoryTeller is a generic message handler but specifically //designed to handle a single Story configured with so called ChapterTemplates $jsonToCSVStoryTeller = new \Prooph\Done\Story\StoryTeller($storyName, [new \Prooph\Done\Story\ChapterTemplate(\Prooph\Done\Story\ChapterName::fromString('Merge Json Files'), new \Prooph\Done\Message\MessageTemplate(\ProophExample\Done\JsonToCSVStory\MergeJsonFilesChapter\MergeJsonFiles::class, $messageFactory, ['json_files_directory' => __DIR__ . DIRECTORY_SEPARATOR . 'data', 'to_filename' => 'users.json', 'file_pattern' => '/^user[\\d]{1,}\\.json$/']), new \ProophExample\Done\JsonToCSVStory\IsStartStoryCommand(), new \ProophExample\Done\JsonToCSVStory\IsInfoSuccessEvent()), new \Prooph\Done\Story\ChapterTemplate(\Prooph\Done\Story\ChapterName::fromString('Convert Json to CSV'), new \Prooph\Done\Message\MessageTemplate(\ProophExample\Done\JsonToCSVStory\JsonToCSVChapter\ConvertJsonToCSV::class, $messageFactory, [], [], function (\Prooph\Done\ChapterLogger\Event\Info $previousMessage, \ArrayObject $payload, \ArrayObject $metadata) { //We extract the generated filename from the previous event //to use it as input file for the next chapter command $inputFile = $previousMessage->payload()['context']['to_file_path']; $outputFile = str_replace('.json', '.csv', $inputFile); //Payload and metadata for the next chapter command are passed as \ArrayObjects //to the PayloadGenerator callback so that the callback can manipulate them both $payload['input_file'] = $inputFile; $payload['output_file'] = $outputFile; }), new \ProophExample\Done\JsonToCSVStory\IsInfoSuccessEvent(), new \ProophExample\Done\JsonToCSVStory\IsInfoSuccessEvent())], $storyLog, $delayedCommandBus, $messageFactory, $messageConverter); //We route the start command of the story to the responsible story teller $commandRouter->route(\ProophExample\Done\JsonToCSVStory\StartStory::class)->to($jsonToCSVStoryTeller); //As we perform the chapter in the same process as handling the Story //We can use the StoryTellerBackend and pass it to the ChapterCommandHandlers //If the chapters would be handled in child processes we would use //a DoneBackend implementation that uses an event bus + async message producer
/** * @test */ public function it_uses_is_done_condition_to_check_if_chapter_is_done_based_on_received_message() { $fileWasUploaded = new FileWasUploaded(); $startCondition = $this->prophesize(MessageCondition::class); $isDoneCondition = $this->prophesize(MessageCondition::class); $isDoneCondition->isTrueFor(Argument::is($fileWasUploaded))->willReturn(true); $commandTemplate = new MessageTemplate(ProcessFile::class, new FQCNMessageFactory()); $chapterTemplate = new ChapterTemplate(ChapterName::fromString('File Processor'), $commandTemplate, $startCondition->reveal(), $isDoneCondition->reveal()); $this->assertTrue($chapterTemplate->isDoneBy($fileWasUploaded)); }
/** * @param ChapterName $other * @return bool */ public function equals(ChapterName $other) { return $this->toString() === $other->toString(); }
/** * @return ChapterName */ private function getChapterName() { return ChapterName::fromString('Test Chapter'); }
/** * @param array $payload * @return ChapterStatus */ private static function payloadToChapterStatus(array $payload) { return ChapterStatus::fromHistoryStatus(ChapterName::fromString($payload['chapter_name']), ChapterNumber::fromString($payload['chapter_number']), $payload['status'], $payload['retry_count'], $payload['retry_limit']); }
/** * @param \Prooph\Done\Story\ChapterName $chapterName * @param StoryName $storyName * @return ChapterName */ public static function isDuplicateForStory(\Prooph\Done\Story\ChapterName $chapterName, StoryName $storyName) { return new self(sprintf('Duplicate Chapter name %s for story %s detected', $chapterName->toString(), $storyName->toString())); }
/** * @param ChapterName $chapterName * @param StoryName $storyName * @return Story */ public static function cannotFindMatchingTemplateForChapterName(ChapterName $chapterName, StoryName $storyName) { return new self(sprintf('Story %s cannot find matching template for chapter name %s', $storyName->toString(), $chapterName->toString())); }