Skip to content

Commit

Permalink
Merge branch '2.0' into feature/refactoring
Browse files Browse the repository at this point in the history
# Conflicts:
#	composer.json
#	psalm.xml
#	src/Attribute/AssignWorker.php
#	src/Generator/Utils.php
#	tests/src/Commands/MakePresetCommandTest.php
#	tests/src/Generator/PhpCodePrinterTest.php
#	tests/src/Preset/PresetRegistryTest.php
  • Loading branch information
butschster committed Dec 19, 2023
2 parents e0e3d49 + 05c7410 commit 11ef03e
Show file tree
Hide file tree
Showing 8 changed files with 135 additions and 31 deletions.
2 changes: 0 additions & 2 deletions .github/workflows/phpunit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,5 @@ jobs:
extensions: sockets, grpc
os: >-
['ubuntu-latest']
php: >-
['8.1', '8.2']
stability: >-
['prefer-stable']
2 changes: 0 additions & 2 deletions .github/workflows/psalm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,3 @@ jobs:
with:
os: >-
['ubuntu-latest']
php: >-
['8.1']
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
"require-dev": {
"spiral/framework": "^3.0",
"spiral/testing": "^2.6",
"vimeo/psalm": "^5.0"
"vimeo/psalm": "^5.17"
},
"autoload": {
"psr-4": {
Expand Down
10 changes: 6 additions & 4 deletions psalm.xml
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
<?xml version="1.0"?>
<psalm
errorLevel="2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://getpsalm.org/schema/config"
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
errorLevel="2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://getpsalm.org/schema/config"
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
findUnusedBaselineEntry="true"
findUnusedCode="false"
>
<projectFiles>
<directory name="src"/>
Expand Down
9 changes: 7 additions & 2 deletions src/Attribute/AssignWorker.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,16 @@

namespace Spiral\TemporalBridge\Attribute;

#[\Attribute(\Attribute::TARGET_CLASS)]
use Spiral\Attributes\NamedArgumentConstructor;

/**
* @psalm-suppress DeprecatedClass
*/
#[\Attribute(\Attribute::TARGET_CLASS), NamedArgumentConstructor]
final class AssignWorker
{
public function __construct(
public readonly string $name
public readonly string $name,
) {
}
}
30 changes: 23 additions & 7 deletions src/Dispatcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public function __construct(
private readonly RoadRunnerMode $mode,
private readonly ReaderInterface $reader,
private readonly TemporalConfig $config,
private readonly Container $container
private readonly Container $container,
) {
}

Expand All @@ -42,9 +42,12 @@ public function serve(): void
$factory = $this->container->get(WorkerFactoryInterface::class);
$registry = $this->container->get(WorkersRegistryInterface::class);

$hasDeclarations = false;
foreach ($declarations as $type => $declaration) {
// Worker that listens on a task queue and hosts both workflow and activity implementations.
$worker = $registry->get($this->resolveQueueName($declaration));
$queueName = $this->resolveQueueName($declaration) ?? $this->config->getDefaultWorker();

$worker = $registry->get($queueName);

if ($type === WorkflowInterface::class) {
// Workflows are stateful. So you need a type to create instances.
Expand All @@ -55,23 +58,36 @@ public function serve(): void
// Workflows are stateful. So you need a type to create instances.
$worker->registerActivity(
$declaration->getName(),
fn(ReflectionClass $class): object => $this->container->make($class->getName())
fn(ReflectionClass $class): object => $this->container->make($class->getName()),
);
}
$hasDeclarations = true;
}

if (!$hasDeclarations) {
$registry->get(WorkerFactoryInterface::DEFAULT_TASK_QUEUE);
}

// start primary loop
$factory->run();
}

private function resolveQueueName(\ReflectionClass $declaration): string
private function resolveQueueName(\ReflectionClass $declaration): ?string
{
$assignWorker = $this->reader->firstClassMetadata($declaration, AssignWorker::class);

if ($assignWorker === null) {
return $this->config->getDefaultWorker();
if ($assignWorker !== null) {
return $assignWorker->name;
}

$parents = $declaration->getInterfaceNames();
foreach ($parents as $parent) {
$queueName = $this->resolveQueueName(new \ReflectionClass($parent));
if ($queueName !== null) {
return $queueName;
}
}

return $assignWorker->name;
return null;
}
}
109 changes: 97 additions & 12 deletions tests/src/DispatcherTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,34 +8,115 @@
use Spiral\RoadRunnerBridge\RoadRunnerMode;
use Spiral\TemporalBridge\Attribute\AssignWorker;
use Spiral\TemporalBridge\Config\TemporalConfig;
use Spiral\TemporalBridge\DeclarationLocatorInterface;
use Spiral\TemporalBridge\Dispatcher;
use Spiral\TemporalBridge\Tests\App\SomeWorkflow;
use Spiral\TemporalBridge\WorkersRegistryInterface;
use Temporal\Worker\WorkerFactoryInterface;
use Temporal\Worker\WorkerInterface;
use Temporal\Workflow\WorkflowInterface;

final class DispatcherTest extends TestCase
{
public function testResolvingQueueName(): void
private \ReflectionMethod $method;
private Dispatcher $dispatcher;

protected function setUp(): void
{
$dispatcher = new Dispatcher(
parent::setUp();

$this->dispatcher = new Dispatcher(
RoadRunnerMode::Temporal,
new AttributeReader(),
new TemporalConfig(['defaultWorker' => 'foo']),
$this->getContainer(),
);

$ref = new \ReflectionClass($dispatcher);
$method = $ref->getMethod('resolveQueueName');
$method->setAccessible(true);
$ref = new \ReflectionClass($this->dispatcher);
$this->method = $ref->getMethod('resolveQueueName');
$this->method->setAccessible(true);
}

$queue = $method->invoke(
$dispatcher,
new \ReflectionClass(ActivityInterfaceWithAttribute::class)
public function testResolvingQueueNameWithAttributeOnClass(): void
{
$queue = $this->method->invoke(
$this->dispatcher,
new \ReflectionClass(ActivityInterfaceWithAttribute::class),
);

$this->assertSame('worker1', $queue);
}

public function testResolvingQueueNameWithAttributeOnParentClass(): void
{
$queue = $this->method->invoke(
$this->dispatcher,
new \ReflectionClass(ActivityClass::class),
);

$this->assertSame('worker1', $queue);
}

public function testResolvingQueueNameWithoutAttribute(): void
{
$queue = $this->method->invoke(
$this->dispatcher,
new \ReflectionClass(ActivityInterfaceWithoutAttribute::class),
);

$this->assertNull($queue);
}

public function testServeWithoutDeclarations(): void
{
$dispatcher = new Dispatcher(
RoadRunnerMode::Temporal,
new AttributeReader(),
new TemporalConfig(),
$this->getContainer(),
);

$locator = $this->mockContainer(DeclarationLocatorInterface::class);
$locator->shouldReceive('getDeclarations')->once()->andReturn([]);

$registry = $this->mockContainer(WorkersRegistryInterface::class);
$registry
->shouldReceive('get')
->once()
->with(WorkerFactoryInterface::DEFAULT_TASK_QUEUE)
->andReturn($this->createMock(WorkerInterface::class));

$queue = $method->invoke(
$dispatcher,
new \ReflectionClass(ActivityInterfaceWithoutAttribute::class)
$factory = $this->mockContainer(WorkerFactoryInterface::class);
$factory->shouldReceive('run')->once();

$dispatcher->serve();
}

public function testServeWithDeclarations(): void
{
$dispatcher = new Dispatcher(
RoadRunnerMode::Temporal,
new AttributeReader(),
new TemporalConfig(),
$this->getContainer(),
);
$this->assertSame('foo', $queue);

$locator = $this->mockContainer(DeclarationLocatorInterface::class);
$locator->shouldReceive('getDeclarations')->once()->andReturn([
WorkflowInterface::class => new \ReflectionClass(SomeWorkflow::class),
]);

$registry = $this->mockContainer(WorkersRegistryInterface::class);
$registry
->shouldReceive('get')
->once()
->with('worker2')
->andReturn($this->createMock(WorkerInterface::class));

$factory = $this->mockContainer(WorkerFactoryInterface::class);
$factory->shouldReceive('run')->once();

$dispatcher->serve();
}
}

Expand All @@ -48,3 +129,7 @@ interface ActivityInterfaceWithAttribute
interface ActivityInterfaceWithoutAttribute
{
}

class ActivityClass implements ActivityInterfaceWithAttribute
{
}
2 changes: 1 addition & 1 deletion tests/src/WorkersRegistryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public function testRegisterWorker(): void
public function testRegisterWorkerWithExistsName(): void
{
$this->expectException(WorkersRegistryException::class);
$this->expectErrorMessage('Temporal worker with given name `foo` has already been registered.');
$this->expectExceptionMessage('Temporal worker with given name `foo` has already been registered.');

$registry = new WorkersRegistry(
$this->createMock(WorkerFactoryInterface::class),
Expand Down

0 comments on commit 11ef03e

Please sign in to comment.