Skip to content

Commit b8b013f

Browse files
Merge pull request #2 from php-task/alexander-schranz-feature/service-tasks
Add tagged service task compilerpass
2 parents 3296668 + 68f68b9 commit b8b013f

File tree

12 files changed

+345
-5
lines changed

12 files changed

+345
-5
lines changed

src/Command/RunHandlerCommand.php

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php
2+
3+
namespace Task\TaskBundle\Command;
4+
5+
use Symfony\Component\Console\Command\Command;
6+
use Symfony\Component\Console\Input\InputArgument;
7+
use Symfony\Component\Console\Input\InputInterface;
8+
use Symfony\Component\Console\Output\OutputInterface;
9+
use Task\Handler\RegistryInterface;
10+
use Task\SchedulerInterface;
11+
12+
/**
13+
* Run pending tasks.
14+
*
15+
* @author Alexander Schranz <alexander.schranz@massiveart.com>
16+
*/
17+
class RunHandlerCommand extends Command
18+
{
19+
/**
20+
* @var RegistryInterface
21+
*/
22+
private $registry;
23+
24+
public function __construct(RegistryInterface $registry)
25+
{
26+
parent::__construct('task:run:handler');
27+
28+
$this->registry = $registry;
29+
}
30+
31+
/**
32+
* {@inheritdoc}
33+
*/
34+
protected function configure()
35+
{
36+
$this->setDescription('Run pending tasks')
37+
->addArgument('handler', InputArgument::REQUIRED)
38+
->addArgument('workload', InputArgument::OPTIONAL);
39+
}
40+
41+
/**
42+
* {@inheritdoc}
43+
*/
44+
protected function execute(InputInterface $input, OutputInterface $output)
45+
{
46+
$handler = $input->getArgument('handler');
47+
$workload = $input->getArgument('workload');
48+
49+
$output->writeln(sprintf('Run command "%s" with workload "%s"', $handler, $workload));
50+
51+
$result = $this->registry->run($handler, $workload);
52+
53+
$output->writeln(sprintf('Result: "%s"', $result));
54+
}
55+
}

src/DependencyInjection/HandlerCompilerPass.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ class HandlerCompilerPass implements CompilerPassInterface
1616
const REGISTRY_ID = 'task.handler_registry';
1717
const HANDLER_TAG = 'task.handler';
1818
const ADD_FUNCTION_NAME = 'add';
19+
const HANDLER_NAME_ATTRIBUTE = 'handler-name';
1920

2021
/**
2122
* {@inheritdoc}
@@ -33,7 +34,7 @@ public function process(ContainerBuilder $container)
3334
foreach ($tags as $attributes) {
3435
$definition->addMethodCall(
3536
self::ADD_FUNCTION_NAME,
36-
[$attributes['handler-name'], new Reference($id)]
37+
[$attributes[self::HANDLER_NAME_ATTRIBUTE], new Reference($id)]
3738
);
3839
}
3940
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<?php
2+
3+
namespace Task\TaskBundle\DependencyInjection;
4+
5+
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
6+
use Symfony\Component\DependencyInjection\ContainerBuilder;
7+
8+
/**
9+
* Compiler pass which collects worker services.
10+
*
11+
* @author @wachterjohannes <johannes.wachter@massiveart.com>
12+
*/
13+
class TaskCompilerPass implements CompilerPassInterface
14+
{
15+
const SCHEDULER_ID = 'task.scheduler';
16+
const INTERVAL_TAG = 'task.interval';
17+
const KEY_ATTRIBUTE = 'key';
18+
const INTERVAL_ATTRIBUTE = 'interval';
19+
const WORKLOAD_ATTRIBUTE = 'workload';
20+
const CREATE_FUNCTION_NAME = 'createTaskAndSchedule';
21+
22+
/**
23+
* {@inheritdoc}
24+
*/
25+
public function process(ContainerBuilder $container)
26+
{
27+
if (!$container->has(self::SCHEDULER_ID)) {
28+
return;
29+
}
30+
31+
$schedulerDefinition = $container->getDefinition(self::SCHEDULER_ID);
32+
33+
$taggedServices = $container->findTaggedServiceIds(self::INTERVAL_TAG);
34+
foreach ($taggedServices as $id => $tags) {
35+
$handlerDefinition = $container->getDefinition($id);
36+
$tag = $handlerDefinition->getTag(HandlerCompilerPass::HANDLER_TAG);
37+
38+
// TODO handle also multiple handler tag here
39+
$handler = $tag[0][HandlerCompilerPass::HANDLER_NAME_ATTRIBUTE];
40+
41+
// remove all tasks with $id and not completed
42+
foreach ($tags as $attributes) {
43+
$interval = $attributes[self::INTERVAL_ATTRIBUTE];
44+
$workload = isset($attributes[self::WORKLOAD_ATTRIBUTE]) ? $attributes[self::WORKLOAD_ATTRIBUTE] : null;
45+
$key = isset($attributes[self::KEY_ATTRIBUTE]) ? $attributes[self::KEY_ATTRIBUTE] : null;
46+
47+
if (!$key) {
48+
$key = $handler . '_' . $interval . '_' . serialize($workload);
49+
}
50+
51+
$schedulerDefinition->addMethodCall(
52+
self::CREATE_FUNCTION_NAME,
53+
[
54+
$handler,
55+
$workload,
56+
$interval,
57+
$key,
58+
]
59+
);
60+
}
61+
}
62+
}
63+
}

src/Entity/Task.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ class Task
1616
*/
1717
private $uuid;
1818

19+
/**
20+
* @var string
21+
*/
22+
private $key;
23+
1924
/**
2025
* @var TaskInterface
2126
*/
@@ -102,4 +107,21 @@ public function setCompleted($completed)
102107
{
103108
$this->completed = $completed;
104109
}
110+
111+
/**
112+
* @return string
113+
*/
114+
public function getKey()
115+
{
116+
return $this->key;
117+
}
118+
119+
/**
120+
* @param string $key
121+
* @return $this
122+
*/
123+
public function setKey($key)
124+
{
125+
$this->key = $key;
126+
}
105127
}

src/Resources/config/command.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@
99
<tag name="console.command"/>
1010
</service>
1111

12+
<service id="task.command.run.handler" class="Task\TaskBundle\Command\RunHandlerCommand">
13+
<argument type="service" id="task.handler_registry"/>
14+
15+
<tag name="console.command"/>
16+
</service>
17+
1218
<service id="task.command.schedule_task" class="Task\TaskBundle\Command\ScheduleTaskCommand">
1319
<argument type="service" id="task.scheduler"/>
1420

src/Resources/config/doctrine/Task.orm.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
<field name="task" type="object"/>
1414
<field name="uuid" type="string" length="100"/>
15+
<field name="key" type="string" length="255" nullable="true" column="task_key"/>
1516
<field name="executionDate" type="datetime"/>
1617
<field name="completed" type="boolean"/>
1718

src/Storage/DoctrineStorage.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,22 @@ public function __construct(EntityManagerInterface $entityManager, TaskRepositor
3232
public function store(TaskInterface $task)
3333
{
3434
$entity = new TaskEntity();
35+
36+
if ($task->getKey()) {
37+
$oldEntity = $this->taskRepository->findOneBy(
38+
[
39+
'key' => $task->getKey(),
40+
'completed' => false,
41+
]
42+
);
43+
44+
if ($oldEntity) {
45+
// TODO update task (warning execution date should not be changed)
46+
47+
return;
48+
}
49+
}
50+
3551
$this->setTask($entity, $task);
3652

3753
$this->entityManager->persist($entity);
@@ -89,6 +105,7 @@ private function setTask(TaskEntity $entity, TaskInterface $task)
89105
{
90106
$entity->setTask($task);
91107
$entity->setUuid($task->getUuid());
108+
$entity->setKey($task->getKey());
92109
$entity->setCompleted($task->isCompleted());
93110
$entity->setExecutionDate($task->getExecutionDate());
94111
}

src/TaskBundle.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use Symfony\Component\DependencyInjection\ContainerBuilder;
66
use Symfony\Component\HttpKernel\Bundle\Bundle;
77
use Task\TaskBundle\DependencyInjection\HandlerCompilerPass;
8+
use Task\TaskBundle\DependencyInjection\TaskCompilerPass;
89

910
/**
1011
* Integrates php-task into symfony.
@@ -18,5 +19,6 @@ public function build(ContainerBuilder $container)
1819
parent::build($container);
1920

2021
$container->addCompilerPass(new HandlerCompilerPass());
22+
$container->addCompilerPass(new TaskCompilerPass());
2123
}
2224
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
3+
namespace Unit\Command;
4+
5+
use Prophecy\Argument;
6+
use Symfony\Component\Console\Input\InputInterface;
7+
use Symfony\Component\Console\Output\OutputInterface;
8+
use Task\Handler\RegistryInterface;
9+
use Task\TaskBundle\Command\RunHandlerCommand;
10+
11+
class RunHandlerCommandTest extends \PHPUnit_Framework_TestCase
12+
{
13+
public function testConfigure()
14+
{
15+
$scheduler = $this->prophesize(RegistryInterface::class);
16+
$command = new RunHandlerCommand($scheduler->reveal());
17+
18+
$this->assertEquals('task:run:handler', $command->getName());
19+
}
20+
21+
public function runProvider()
22+
{
23+
return [
24+
['test-handler', 'test-workload'],
25+
['test-handler-1', 'test-workload-1'],
26+
];
27+
}
28+
29+
/**
30+
* @dataProvider runProvider
31+
*/
32+
public function testRun($handlerName, $workload)
33+
{
34+
$input = $this->prophesize(InputInterface::class);
35+
$output = $this->prophesize(OutputInterface::class);
36+
37+
$input->bind(Argument::any())->willReturn(true);
38+
$input->validate()->willReturn(true);
39+
$input->isInteractive()->willReturn(false);
40+
$input->hasArgument('command')->willReturn(false);
41+
42+
$input->getArgument('handler')->willReturn($handlerName);
43+
$input->getArgument('workload')->willReturn($workload);
44+
45+
$registry = $this->prophesize(RegistryInterface::class);
46+
$command = new RunHandlerCommand($registry->reveal());
47+
48+
$command->run($input->reveal(), $output->reveal());
49+
50+
$registry->run($handlerName, $workload)->shouldBeCalledTimes(1);
51+
}
52+
}

tests/Unit/DependencyInjection/HandlerCompilerPassTest.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@ public function testProcess()
2121
->willReturn(
2222
[
2323
'id-1' => [
24-
['handler-name' => 'name-1'],
24+
[HandlerCompilerPass::HANDLER_NAME_ATTRIBUTE => 'name-1'],
2525
],
2626
'id-2' => [
27-
['handler-name' => 'name-2-1'],
28-
['handler-name' => 'name-2-2'],
27+
[HandlerCompilerPass::HANDLER_NAME_ATTRIBUTE => 'name-2-1'],
28+
[HandlerCompilerPass::HANDLER_NAME_ATTRIBUTE => 'name-2-2'],
2929
],
3030
]
3131
);
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<?php
2+
3+
namespace Unit\DependencyInjection;
4+
5+
use Prophecy\Argument;
6+
use Symfony\Component\DependencyInjection\ContainerBuilder;
7+
use Symfony\Component\DependencyInjection\Definition;
8+
use Task\TaskBundle\DependencyInjection\HandlerCompilerPass;
9+
use Task\TaskBundle\DependencyInjection\TaskCompilerPass;
10+
11+
class TaskCompilerPassTest extends \PHPUnit_Framework_TestCase
12+
{
13+
public function testProcess()
14+
{
15+
$containerBuilder = $this->prophesize(ContainerBuilder::class);
16+
$schedulerDefinition = $this->prophesize(Definition::class);
17+
$handler1Definition = $this->prophesize(Definition::class);
18+
$handler2Definition = $this->prophesize(Definition::class);
19+
20+
$containerBuilder->has(TaskCompilerPass::SCHEDULER_ID)->willReturn(true);
21+
$containerBuilder->getDefinition(TaskCompilerPass::SCHEDULER_ID)->willReturn($schedulerDefinition->reveal());
22+
$containerBuilder->getDefinition('id-1')->willReturn($handler1Definition->reveal());
23+
$containerBuilder->getDefinition('id-2')->willReturn($handler2Definition->reveal());
24+
25+
$containerBuilder->findTaggedServiceIds(TaskCompilerPass::INTERVAL_TAG)
26+
->willReturn(
27+
[
28+
'id-1' => [
29+
[
30+
TaskCompilerPass::INTERVAL_ATTRIBUTE => 'daily',
31+
TaskCompilerPass::WORKLOAD_ATTRIBUTE => 'test-workload',
32+
TaskCompilerPass::KEY_ATTRIBUTE => 'test-key'
33+
],
34+
],
35+
'id-2' => [
36+
[
37+
TaskCompilerPass::INTERVAL_ATTRIBUTE => 'daily',
38+
TaskCompilerPass::KEY_ATTRIBUTE => 'test-key-1'
39+
],
40+
[
41+
TaskCompilerPass::INTERVAL_ATTRIBUTE => 'daily',
42+
TaskCompilerPass::WORKLOAD_ATTRIBUTE => 'test-workload-2'
43+
],
44+
],
45+
]
46+
);
47+
48+
$handler1Definition->getTag(HandlerCompilerPass::HANDLER_TAG)->willReturn(
49+
[[HandlerCompilerPass::HANDLER_NAME_ATTRIBUTE => 'handler-1']]
50+
);
51+
$handler2Definition->getTag(HandlerCompilerPass::HANDLER_TAG)->willReturn(
52+
[[HandlerCompilerPass::HANDLER_NAME_ATTRIBUTE => 'handler-2']]
53+
);
54+
55+
$compilerPass = new TaskCompilerPass();
56+
$compilerPass->process($containerBuilder->reveal());
57+
58+
$schedulerDefinition->addMethodCall(
59+
TaskCompilerPass::CREATE_FUNCTION_NAME,
60+
['handler-1', 'test-workload', 'daily', 'test-key']
61+
)->shouldBeCalledTimes(1);
62+
$schedulerDefinition->addMethodCall(
63+
TaskCompilerPass::CREATE_FUNCTION_NAME,
64+
['handler-2', null, 'daily', 'test-key-1']
65+
)->shouldBeCalledTimes(1);
66+
$schedulerDefinition->addMethodCall(
67+
TaskCompilerPass::CREATE_FUNCTION_NAME,
68+
['handler-2', 'test-workload-2', 'daily', 'handler-2_daily_s:15:"test-workload-2";']
69+
)->shouldBeCalledTimes(1);
70+
}
71+
}

0 commit comments

Comments
 (0)