diff --git a/phpunit.xml b/phpunit.xml index 0f46c2d..363e4da 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,21 +1,28 @@ - - - - src - - - - - tests/src - - - - - - + + + + tests/src + + + + + + + + + src + + diff --git a/src/Bootloader/TemporalBridgeBootloader.php b/src/Bootloader/TemporalBridgeBootloader.php index deecddd..b346329 100644 --- a/src/Bootloader/TemporalBridgeBootloader.php +++ b/src/Bootloader/TemporalBridgeBootloader.php @@ -23,7 +23,7 @@ use Spiral\TemporalBridge\WorkerFactoryInterface; use Spiral\TemporalBridge\WorkersRegistry; use Spiral\TemporalBridge\WorkersRegistryInterface; -use Spiral\Tokenizer\ClassesInterface; +use Spiral\Tokenizer\TokenizerListenerRegistryInterface; use Temporal\Client\GRPC\ServiceClient; use Temporal\Client\WorkflowClient; use Temporal\Client\WorkflowClientInterface; @@ -56,15 +56,46 @@ public function defineDependencies(): array public function defineSingletons(): array { return [ - TemporalWorkerFactoryInterface::class => [self::class, 'initWorkerFactory'], + TemporalWorkerFactoryInterface::class => static fn( + DataConverterInterface $dataConverter, + ): TemporalWorkerFactoryInterface => new TemporalWorkerFactory( + dataConverter: $dataConverter, + rpc: Goridge::create(), + ), WorkerFactoryInterface::class => WorkerFactory::class, - DeclarationLocatorInterface::class => [self::class, 'initDeclarationLocator'], - WorkflowClientInterface::class => [self::class, 'initWorkflowClient'], + + DeclarationLocatorInterface::class => static fn() => new DeclarationLocator( + reader: new AttributeReader(), + ), + + WorkflowClientInterface::class => static fn( + TemporalConfig $config, + DataConverterInterface $dataConverter, + PipelineProvider $pipelineProvider, + ServiceClientInterface $serviceClient, + ): WorkflowClientInterface => new WorkflowClient( + serviceClient: $serviceClient, + options: $config->getClientOptions(), + converter: $dataConverter, + interceptorProvider: $pipelineProvider, + ), WorkersRegistryInterface::class => WorkersRegistry::class, - ScheduleClientInterface::class => [self::class, 'initScheduleClient'], - DataConverterInterface::class => [self::class, 'initDataConverter'], + + ScheduleClientInterface::class => static fn( + TemporalConfig $config, + DataConverterInterface $dataConverter, + ServiceClientInterface $serviceClient, + ): ScheduleClientInterface => new ScheduleClient( + serviceClient: $serviceClient, + options: $config->getClientOptions(), + converter: $dataConverter, + ), + + DataConverterInterface::class => static fn() => DataConverter::createDefault(), PipelineProvider::class => [self::class, 'initPipelineProvider'], - ServiceClientInterface::class => [self::class, 'initServiceClient'], + ServiceClientInterface::class => static fn( + TemporalConfig $config, + ): ServiceClientInterface => ServiceClient::create($config->getAddress()), ]; } @@ -78,9 +109,11 @@ public function init( AbstractKernel $kernel, EnvironmentInterface $env, ConsoleBootloader $console, + TokenizerListenerRegistryInterface $tokenizer, + DeclarationLocator $locator, ): void { $this->initConfig($env); - + $tokenizer->addListener($locator); $console->addCommand(Commands\InfoCommand::class); $kernel->addDispatcher($this->factory->make(Dispatcher::class)); } @@ -133,41 +166,6 @@ protected function initConfig(EnvironmentInterface $env): void ); } - protected function initWorkflowClient( - TemporalConfig $config, - DataConverterInterface $dataConverter, - PipelineProvider $pipelineProvider, - ServiceClientInterface $serviceClient, - ): WorkflowClientInterface { - return new WorkflowClient( - serviceClient: $serviceClient, - options: $config->getClientOptions(), - converter: $dataConverter, - interceptorProvider: $pipelineProvider, - ); - } - - protected function initDataConverter(): DataConverterInterface - { - return DataConverter::createDefault(); - } - - protected function initWorkerFactory(DataConverterInterface $dataConverter,): TemporalWorkerFactoryInterface - { - return new TemporalWorkerFactory( - dataConverter: $dataConverter, - rpc: Goridge::create(), - ); - } - - protected function initDeclarationLocator(ClassesInterface $classes,): DeclarationLocatorInterface - { - return new DeclarationLocator( - classes: $classes, - reader: new AttributeReader(), - ); - } - protected function initPipelineProvider(TemporalConfig $config, FactoryInterface $factory): PipelineProvider { /** @var Interceptor[] $interceptors */ @@ -182,21 +180,4 @@ protected function initPipelineProvider(TemporalConfig $config, FactoryInterface return new SimplePipelineProvider($interceptors); } - - protected function initServiceClient(TemporalConfig $config): ServiceClientInterface - { - return ServiceClient::create($config->getAddress()); - } - - protected function initScheduleClient( - TemporalConfig $config, - DataConverterInterface $dataConverter, - ServiceClientInterface $serviceClient, - ): ScheduleClientInterface { - return new ScheduleClient( - serviceClient: $serviceClient, - options: $config->getClientOptions(), - converter: $dataConverter, - ); - } } diff --git a/src/Commands/InfoCommand.php b/src/Commands/InfoCommand.php index 0687747..61efe46 100644 --- a/src/Commands/InfoCommand.php +++ b/src/Commands/InfoCommand.php @@ -72,7 +72,10 @@ public function perform( foreach ($workflows as $workflow) { $table->addRow([ \sprintf('%s', $workflow['name']), - $workflow['class'] . "\n" . \sprintf('%s', \str_replace($rootDir, '', $workflow['file'])), + $workflow['class'] . "\n" . \sprintf( + '%s', + self::normalizePath($rootDir, $workflow['file']), + ), $workflow['task_queue'], ]); } @@ -100,4 +103,18 @@ public function perform( return self::SUCCESS; } + + /** + * @param non-empty-string $rootDir + * @param non-empty-string $file + */ + private static function normalizePath(string $rootDir, string $file): string + { + $file = \str_replace('\\', '/', $file); + $rootDir = \str_replace('\\', '/', $rootDir); + + return \str_starts_with($file, $rootDir) + ? \substr($file, \strlen($rootDir)) + : $file; + } } diff --git a/src/DeclarationLocator.php b/src/DeclarationLocator.php index 066e48d..4f44444 100644 --- a/src/DeclarationLocator.php +++ b/src/DeclarationLocator.php @@ -5,32 +5,50 @@ namespace Spiral\TemporalBridge; use Spiral\Attributes\ReaderInterface; -use Spiral\Tokenizer\ClassesInterface; +use Spiral\Core\Attribute\Singleton; +use Spiral\Tokenizer\Attribute\TargetAttribute; +use Spiral\Tokenizer\TokenizationListenerInterface; use Temporal\Activity\ActivityInterface; use Temporal\Workflow\WorkflowInterface; -final class DeclarationLocator implements DeclarationLocatorInterface +#[Singleton] +#[TargetAttribute(WorkflowInterface::class)] +#[TargetAttribute(ActivityInterface::class)] +final class DeclarationLocator implements DeclarationLocatorInterface, TokenizationListenerInterface { + private array $declarations = []; + public function __construct( - private readonly ClassesInterface $classes, - private readonly ReaderInterface $reader + private readonly ReaderInterface $reader, ) { } public function getDeclarations(): iterable { - foreach ($this->classes->getClasses() as $class) { - if ($class->isAbstract() || $class->isInterface() || $class->isEnum()) { - continue; + foreach ($this->declarations as $type => $classes) { + foreach ($classes as $class) { + yield $type => $class; } + } + } - foreach (\array_merge($class->getInterfaces(), [$class]) as $type) { - if ($this->reader->firstClassMetadata($type, WorkflowInterface::class) !== null) { - yield WorkflowInterface::class => $class; - } elseif ($this->reader->firstClassMetadata($type, ActivityInterface::class) !== null) { - yield ActivityInterface::class => $class; - } + public function listen(\ReflectionClass $class): void + { + if ($class->isAbstract() || $class->isInterface() || $class->isEnum()) { + return; + } + + foreach (\array_merge($class->getInterfaces(), [$class]) as $type) { + if ($this->reader->firstClassMetadata($type, WorkflowInterface::class) !== null) { + $this->declarations[WorkflowInterface::class][] = $class; + } elseif ($this->reader->firstClassMetadata($type, ActivityInterface::class) !== null) { + $this->declarations[ActivityInterface::class][] = $class; } } } + + public function finalize(): void + { + // do nothing + } } diff --git a/tests/src/Commands/InfoCommandTest.php b/tests/src/Commands/InfoCommandTest.php index dd13b63..9d31edb 100644 --- a/tests/src/Commands/InfoCommandTest.php +++ b/tests/src/Commands/InfoCommandTest.php @@ -31,7 +31,7 @@ public function testInfo(): void { $result = $this->runCommand('temporal:info'); - $this->assertSame( + $this->assertStringEqualsStringIgnoringLineEndings( <<<'OUTPUT' Workflows @@ -57,7 +57,7 @@ public function testInfoWithActivities(): void '--show-activities' => true, ]); - $this->assertSame( + $this->assertStringEqualsStringIgnoringLineEndings( <<<'OUTPUT' Workflows diff --git a/tests/src/DeclarationLocatorTest.php b/tests/src/DeclarationLocatorTest.php index e05b63c..e22d860 100644 --- a/tests/src/DeclarationLocatorTest.php +++ b/tests/src/DeclarationLocatorTest.php @@ -20,19 +20,14 @@ protected function setUp(): void { parent::setUp(); - $this->locator = new DeclarationLocator( - $this->classes = m::mock(ClassesInterface::class), - new AttributeReader() - ); + $this->locator = new DeclarationLocator(new AttributeReader()); } public function testEnumClassesShouldBeSkipped(): void { - $this->classes->shouldReceive('getClasses')->once()->andReturn([ - new \ReflectionClass(TestEnum::class), - new \ReflectionClass(TestAbstractClass::class), - new \ReflectionClass(TestInterface::class), - ]); + $this->locator->listen(new \ReflectionClass(TestEnum::class)); + $this->locator->listen(new \ReflectionClass(TestAbstractClass::class)); + $this->locator->listen(new \ReflectionClass(TestInterface::class)); $result = []; @@ -45,15 +40,13 @@ public function testEnumClassesShouldBeSkipped(): void public function testWorkflowsShouldBeRegistered(): void { - $this->classes->shouldReceive('getClasses')->once()->andReturn([ - new \ReflectionClass(TestEnum::class), - new \ReflectionClass(TestAbstractClass::class), - new \ReflectionClass(TestInterface::class), - $workflow1 = new \ReflectionClass(TestWorkflowClass::class), - $workflow2 = new \ReflectionClass(TestWorkflowClassWithInterface::class), - $activity1 = new \ReflectionClass(TestActivityClass::class), - $activity2 = new \ReflectionClass(TestActivityClassWithInterface::class), - ]); + $this->locator->listen(new \ReflectionClass(TestEnum::class)); + $this->locator->listen(new \ReflectionClass(TestAbstractClass::class)); + $this->locator->listen(new \ReflectionClass(TestInterface::class)); + $this->locator->listen($workflow1 = new \ReflectionClass(TestWorkflowClass::class)); + $this->locator->listen($workflow2 = new \ReflectionClass(TestWorkflowClassWithInterface::class)); + $this->locator->listen($activity1 = new \ReflectionClass(TestActivityClass::class)); + $this->locator->listen($activity2 = new \ReflectionClass(TestActivityClassWithInterface::class)); $result = [];