Skip to content

Commit cf1c154

Browse files
authored
Add a component to build JobParameters defaults at contruction time (#123)
* Add a component to build JobParameters defaults at contruction time * wip * Fixed symfony configuration structure * Add tests for Job configuration * Fix checkstyle
1 parent c6a0600 commit cf1c154

23 files changed

+474
-15
lines changed

src/batch-symfony-console/tests/RunCommandJobLauncherTest.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use Yokai\Batch\Bridge\Symfony\Console\CommandRunner;
1212
use Yokai\Batch\Bridge\Symfony\Console\RunCommandJobLauncher;
1313
use Yokai\Batch\Factory\JobExecutionFactory;
14+
use Yokai\Batch\Factory\JobExecutionParametersBuilder\NullJobExecutionParametersBuilder;
1415
use Yokai\Batch\Factory\UniqidJobExecutionIdGenerator;
1516
use Yokai\Batch\Test\Storage\InMemoryJobExecutionStorage;
1617

@@ -29,7 +30,7 @@ public function testLaunch(): void
2930
->shouldBeCalledTimes(1);
3031

3132
$launcher = new RunCommandJobLauncher(
32-
new JobExecutionFactory(new UniqidJobExecutionIdGenerator()),
33+
new JobExecutionFactory(new UniqidJobExecutionIdGenerator(), new NullJobExecutionParametersBuilder()),
3334
$commandRunner->reveal(),
3435
$storage = new InMemoryJobExecutionStorage(),
3536
'test.log'

src/batch-symfony-console/tests/RunJobCommandTest.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Yokai\Batch\Bridge\Symfony\Console\RunJobCommand;
1515
use Yokai\Batch\Exception\UnexpectedValueException;
1616
use Yokai\Batch\Factory\JobExecutionFactory;
17+
use Yokai\Batch\Factory\JobExecutionParametersBuilder\NullJobExecutionParametersBuilder;
1718
use Yokai\Batch\Factory\UniqidJobExecutionIdGenerator;
1819
use Yokai\Batch\Job\JobExecutionAccessor;
1920
use Yokai\Batch\Job\JobExecutor;
@@ -38,7 +39,7 @@ protected function setUp(): void
3839
$this->job = $this->prophesize(JobInterface::class);
3940

4041
$this->accessor = new JobExecutionAccessor(
41-
new JobExecutionFactory(new UniqidJobExecutionIdGenerator()),
42+
new JobExecutionFactory(new UniqidJobExecutionIdGenerator(), new NullJobExecutionParametersBuilder()),
4243
new InMemoryJobExecutionStorage(),
4344
);
4445
$this->executor = new JobExecutor(

src/batch-symfony-framework/src/DependencyInjection/Configuration.php

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
* @phpstan-type Config array{
1515
* storage: StorageConfig,
1616
* launcher: LauncherConfig,
17+
* parameters: ParametersConfig,
1718
* ui: UserInterfaceConfig,
1819
* }
1920
* @phpstan-type StorageConfig array{
@@ -31,6 +32,10 @@
3132
* default: string|null,
3233
* launchers: array<string, string>,
3334
* }
35+
* @phpstan-type ParametersConfig array{
36+
* global: array<string, mixed>,
37+
* per_job: array<string, array<string, mixed>>,
38+
* }
3439
* @phpstan-type UserInterfaceConfig array{
3540
* enabled: bool,
3641
* security: array{
@@ -59,6 +64,7 @@ public function getConfigTreeBuilder(): TreeBuilder
5964
->children()
6065
->append($this->storage())
6166
->append($this->launcher())
67+
->append($this->parameters())
6268
->append($this->ui())
6369
->end()
6470
;
@@ -131,6 +137,47 @@ private function launcher(): ArrayNodeDefinition
131137
return $node;
132138
}
133139

140+
private function parameters(): ArrayNodeDefinition
141+
{
142+
/** @var ArrayNodeDefinition $node */
143+
$node = (new TreeBuilder('parameters'))->getRootNode();
144+
145+
$isStringAssociativeArray = function (mixed $value): bool {
146+
if (!\is_array($value)) {
147+
return false;
148+
}
149+
150+
foreach ($value as $key => $unused) {
151+
if (!\is_string($key)) {
152+
return false;
153+
}
154+
}
155+
156+
return true;
157+
};
158+
159+
$node
160+
->addDefaultsIfNotSet()
161+
->children()
162+
->arrayNode('global')
163+
->useAttributeAsKey('name')
164+
->variablePrototype()
165+
->end()
166+
->end()
167+
->arrayNode('per_job')
168+
->useAttributeAsKey('name')
169+
->variablePrototype()
170+
->validate()
171+
->ifTrue(fn(mixed $value) => !$isStringAssociativeArray($value))
172+
->thenInvalid('Should be an array<string, mixed>.')
173+
->end()
174+
->end()
175+
->end()
176+
;
177+
178+
return $node;
179+
}
180+
134181
private function ui(): ArrayNodeDefinition
135182
{
136183
/** @var ArrayNodeDefinition $node */

src/batch-symfony-framework/src/DependencyInjection/YokaiBatchExtension.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
use Yokai\Batch\Bridge\Symfony\Framework\UserInterface\Templating\ConfigurableTemplating;
2323
use Yokai\Batch\Bridge\Symfony\Framework\UserInterface\Templating\SonataAdminTemplating;
2424
use Yokai\Batch\Bridge\Symfony\Framework\UserInterface\Templating\TemplatingInterface;
25+
use Yokai\Batch\Factory\JobExecutionParametersBuilder\PerJobJobExecutionParametersBuilder;
26+
use Yokai\Batch\Factory\JobExecutionParametersBuilder\StaticJobExecutionParametersBuilder;
2527
use Yokai\Batch\Launcher\JobLauncherInterface;
2628
use Yokai\Batch\Storage\FilesystemJobExecutionStorage;
2729
use Yokai\Batch\Storage\JobExecutionStorageInterface;
@@ -34,6 +36,7 @@
3436
* @phpstan-import-type Config from Configuration
3537
* @phpstan-import-type StorageConfig from Configuration
3638
* @phpstan-import-type LauncherConfig from Configuration
39+
* @phpstan-import-type ParametersConfig from Configuration
3740
* @phpstan-import-type UserInterfaceConfig from Configuration
3841
*/
3942
final class YokaiBatchExtension extends Extension
@@ -64,6 +67,7 @@ public function load(array $configs, ContainerBuilder $container): void
6467

6568
$this->configureStorage($container, $config['storage']);
6669
$this->configureLauncher($container, $config['launcher']);
70+
$this->configureParameters($container, $config['parameters']);
6771
$this->configureUserInterface($container, $loader, $config['ui']);
6872

6973
$container->registerAliasForArgument('yokai_batch.logger', LoggerInterface::class, 'yokaiBatchLogger');
@@ -189,6 +193,25 @@ private function configureLauncher(ContainerBuilder $container, array $config):
189193
);
190194
}
191195

196+
/**
197+
* @param ParametersConfig $config
198+
*/
199+
private function configureParameters(ContainerBuilder $container, array $config): void
200+
{
201+
if ($config['global'] !== []) {
202+
$container->register('yokai_batch.job_execution_parameters_builder.global')
203+
->setClass(StaticJobExecutionParametersBuilder::class)
204+
->setArgument('$parameters', $config['global'])
205+
->addTag('yokai_batch.job_execution_parameters_builder');
206+
}
207+
if ($config['per_job'] !== []) {
208+
$container->register('yokai_batch.job_execution_parameters_builder.per_job')
209+
->setClass(PerJobJobExecutionParametersBuilder::class)
210+
->setArgument('$perJobParameters', $config['per_job'])
211+
->addTag('yokai_batch.job_execution_parameters_builder');
212+
}
213+
}
214+
192215
/**
193216
* @param UserInterfaceConfig $config
194217
*/

src/batch-symfony-framework/src/Resources/services/global/alias.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,8 @@
2121

2222
<service id="Yokai\Batch\Factory\JobExecutionIdGeneratorInterface"
2323
alias="yokai_batch.job_execution_id_generator.uniqid"/>
24+
25+
<service id="Yokai\Batch\Factory\JobExecutionParametersBuilderInterface"
26+
alias="yokai_batch.job_execution_parameters_builder.chain"/>
2427
</services>
2528
</container>

src/batch-symfony-framework/src/Resources/services/global/core.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
<service id="yokai_batch.job_execution_factory"
1111
class="Yokai\Batch\Factory\JobExecutionFactory">
1212
<argument type="service" id="Yokai\Batch\Factory\JobExecutionIdGeneratorInterface"/>
13+
<argument type="service" id="Yokai\Batch\Factory\JobExecutionParametersBuilderInterface"/>
1314
</service>
1415

1516
<service id="yokai_batch.job_registry"
@@ -32,5 +33,10 @@
3233

3334
<service id="yokai_batch.job_execution_id_generator.uniqid"
3435
class="Yokai\Batch\Factory\UniqidJobExecutionIdGenerator"/>
36+
37+
<service id="yokai_batch.job_execution_parameters_builder.chain"
38+
class="Yokai\Batch\Factory\JobExecutionParametersBuilder\ChainJobExecutionParametersBuilder">
39+
<argument type="tagged_iterator" tag="yokai_batch.job_execution_parameters_builder"/>
40+
</service>
3541
</services>
3642
</container>

src/batch-symfony-framework/tests/DependencyInjection/YokaiBatchExtensionTest.php

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use Exception;
88
use PHPUnit\Framework\TestCase;
99
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
10+
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
1011
use Symfony\Component\DependencyInjection\ContainerBuilder;
1112
use Symfony\Component\DependencyInjection\Definition;
1213
use Symfony\Component\DependencyInjection\Exception\LogicException;
@@ -17,6 +18,10 @@
1718
use Yokai\Batch\Bridge\Symfony\Framework\UserInterface\Templating\SonataAdminTemplating;
1819
use Yokai\Batch\Bridge\Symfony\Framework\UserInterface\Templating\TemplatingInterface;
1920
use Yokai\Batch\Bridge\Symfony\Messenger\DispatchMessageJobLauncher;
21+
use Yokai\Batch\Factory\JobExecutionParametersBuilder\ChainJobExecutionParametersBuilder;
22+
use Yokai\Batch\Factory\JobExecutionParametersBuilder\PerJobJobExecutionParametersBuilder;
23+
use Yokai\Batch\Factory\JobExecutionParametersBuilder\StaticJobExecutionParametersBuilder;
24+
use Yokai\Batch\Factory\JobExecutionParametersBuilderInterface;
2025
use Yokai\Batch\Launcher\JobLauncherInterface;
2126
use Yokai\Batch\Launcher\SimpleJobLauncher;
2227
use Yokai\Batch\Storage\JobExecutionStorageInterface;
@@ -387,4 +392,111 @@ public function errors(): \Generator
387392
new ServiceNotFoundException('app.unknown'),
388393
];
389394
}
395+
396+
/**
397+
* @dataProvider parameters
398+
*/
399+
public function testParameters(array $config, array|null $global, array|null $perJob): void
400+
{
401+
$container = $this->createContainer($config);
402+
403+
$globalService = $this->getDefinition($container, 'yokai_batch.job_execution_parameters_builder.global');
404+
if ($global !== null) {
405+
self::assertNotNull($globalService);
406+
self::assertSame(StaticJobExecutionParametersBuilder::class, $globalService->getClass());
407+
self::assertTrue($globalService->hasTag('yokai_batch.job_execution_parameters_builder'));
408+
self::assertSame($global, $globalService->getArgument('$parameters'));
409+
} else {
410+
self::assertNull($globalService);
411+
}
412+
$perJobService = $this->getDefinition($container, 'yokai_batch.job_execution_parameters_builder.per_job');
413+
if ($perJob !== null) {
414+
self::assertNotNull($perJobService);
415+
self::assertSame(PerJobJobExecutionParametersBuilder::class, $perJobService->getClass());
416+
self::assertTrue($perJobService->hasTag('yokai_batch.job_execution_parameters_builder'));
417+
self::assertSame($perJob, $perJobService->getArgument('$perJobParameters'));
418+
} else {
419+
self::assertNull($perJobService);
420+
}
421+
$defaultService = $this->getDefinition($container, JobExecutionParametersBuilderInterface::class);
422+
self::assertNotNull($defaultService);
423+
self::assertSame(ChainJobExecutionParametersBuilder::class, $defaultService->getClass());
424+
$defaultServiceBuilders = $defaultService->getArgument(0);
425+
self::assertTrue($defaultServiceBuilders instanceof TaggedIteratorArgument);
426+
/** @var TaggedIteratorArgument $defaultServiceBuilders */
427+
self::assertSame('yokai_batch.job_execution_parameters_builder', $defaultServiceBuilders->getTag());
428+
}
429+
430+
public function parameters(): \Generator
431+
{
432+
yield 'Global parameters' => [
433+
['parameters' => ['global' => ['global' => true]]],
434+
['global' => true],
435+
null,
436+
];
437+
yield 'Per job parameters' => [
438+
['parameters' => ['per_job' => ['job.foo' => ['foo' => true], 'job.bar' => ['bar' => true]]]],
439+
null,
440+
['job.foo' => ['foo' => true], 'job.bar' => ['bar' => true]],
441+
];
442+
yield 'Global AND per job parameters' => [
443+
['parameters' => [
444+
'global' => ['global' => true],
445+
'per_job' => ['job.foo' => ['foo' => true], 'job.bar' => ['bar' => true]],
446+
]],
447+
['global' => true],
448+
['job.foo' => ['foo' => true], 'job.bar' => ['bar' => true]],
449+
];
450+
}
451+
452+
/**
453+
* @dataProvider invalidParameters
454+
*/
455+
public function testInvalidParameters(array $config, \Exception $error): void
456+
{
457+
$this->expectExceptionObject($error);
458+
$this->createContainer($config);
459+
}
460+
461+
public function invalidParameters(): \Generator
462+
{
463+
yield 'Per job parameters value must be an array' => [
464+
['parameters' => ['per_job' => ['job.foo' => 'string']]],
465+
new InvalidConfigurationException(
466+
'Invalid configuration for path "yokai_batch.parameters.per_job.job.foo": Should be an array<string, mixed>.'
467+
),
468+
];
469+
yield 'Per job parameters value must be a string indexed array' => [
470+
['parameters' => ['per_job' => ['job.foo' => [1, 2, 3]]]],
471+
new InvalidConfigurationException(
472+
'Invalid configuration for path "yokai_batch.parameters.per_job.job.foo": Should be an array<string, mixed>.'
473+
),
474+
];
475+
}
476+
477+
private function createContainer(array $config, \Closure|null $configure = null): ContainerBuilder
478+
{
479+
$container = new ContainerBuilder();
480+
if ($configure !== null) {
481+
$configure($container);
482+
}
483+
$container->registerExtension(new YokaiBatchExtension());
484+
$container->loadFromExtension('yokai_batch', $config);
485+
486+
$container->getCompilerPassConfig()->setOptimizationPasses([]);
487+
$container->getCompilerPassConfig()->setRemovingPasses([]);
488+
$container->getCompilerPassConfig()->setAfterRemovingPasses([]);
489+
$container->compile();
490+
491+
return $container;
492+
}
493+
494+
private function getDefinition(ContainerBuilder $container, string $id): Definition|null
495+
{
496+
try {
497+
return $container->findDefinition($id);
498+
} catch (ServiceNotFoundException) {
499+
return null;
500+
}
501+
}
390502
}

src/batch-symfony-messenger/tests/DispatchMessageJobLauncherTest.php

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use Yokai\Batch\Bridge\Symfony\Messenger\DispatchMessageJobLauncher;
1111
use Yokai\Batch\Bridge\Symfony\Messenger\LaunchJobMessage;
1212
use Yokai\Batch\Factory\JobExecutionFactory;
13+
use Yokai\Batch\Factory\JobExecutionParametersBuilder\NullJobExecutionParametersBuilder;
1314
use Yokai\Batch\Factory\UniqidJobExecutionIdGenerator;
1415
use Yokai\Batch\Test\Factory\SequenceJobExecutionIdGenerator;
1516
use Yokai\Batch\Test\Storage\InMemoryJobExecutionStorage;
@@ -21,7 +22,7 @@ final class DispatchMessageJobLauncherTest extends TestCase
2122
public function testLaunch(): void
2223
{
2324
$jobLauncher = new DispatchMessageJobLauncher(
24-
new JobExecutionFactory(new UniqidJobExecutionIdGenerator()),
25+
new JobExecutionFactory(new UniqidJobExecutionIdGenerator(), new NullJobExecutionParametersBuilder()),
2526
$storage = new InMemoryJobExecutionStorage(),
2627
$messageBus = new BufferingMessageBus()
2728
);
@@ -41,9 +42,12 @@ public function testLaunch(): void
4142
public function testLaunchWithNoId(): void
4243
{
4344
$jobLauncher = new DispatchMessageJobLauncher(
44-
new JobExecutionFactory(new SequenceJobExecutionIdGenerator(['123456789'])),
45+
new JobExecutionFactory(
46+
new SequenceJobExecutionIdGenerator(['123456789']),
47+
new NullJobExecutionParametersBuilder(),
48+
),
4549
$storage = new InMemoryJobExecutionStorage(),
46-
$messageBus = new BufferingMessageBus()
50+
$messageBus = new BufferingMessageBus(),
4751
);
4852

4953
$jobExecutionFromLauncher = $jobLauncher->launch('testing');
@@ -60,7 +64,7 @@ public function testLaunchWithNoId(): void
6064
public function testLaunchAndMessengerFail(): void
6165
{
6266
$jobLauncher = new DispatchMessageJobLauncher(
63-
new JobExecutionFactory(new UniqidJobExecutionIdGenerator()),
67+
new JobExecutionFactory(new UniqidJobExecutionIdGenerator(), new NullJobExecutionParametersBuilder()),
6468
$storage = new InMemoryJobExecutionStorage(),
6569
new FailingMessageBus(new TransportException('This is a test'))
6670
);

src/batch-symfony-messenger/tests/LaunchJobMessageHandlerTest.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use Yokai\Batch\Bridge\Symfony\Messenger\LaunchJobMessage;
1010
use Yokai\Batch\Bridge\Symfony\Messenger\LaunchJobMessageHandler;
1111
use Yokai\Batch\Factory\JobExecutionFactory;
12+
use Yokai\Batch\Factory\JobExecutionParametersBuilder\NullJobExecutionParametersBuilder;
1213
use Yokai\Batch\Job\JobExecutionAccessor;
1314
use Yokai\Batch\Job\JobExecutor;
1415
use Yokai\Batch\Job\JobInterface;
@@ -35,7 +36,10 @@ public function execute(JobExecution $jobExecution): void
3536
$jobExecutionStorage = new InMemoryJobExecutionStorage();
3637
$handler = new LaunchJobMessageHandler(
3738
new JobExecutionAccessor(
38-
new JobExecutionFactory(new SequenceJobExecutionIdGenerator(['123456'])),
39+
new JobExecutionFactory(
40+
new SequenceJobExecutionIdGenerator(['123456']),
41+
new NullJobExecutionParametersBuilder(),
42+
),
3943
$jobExecutionStorage,
4044
),
4145
new JobExecutor(

src/batch/src/Factory/JobExecutionFactory.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ final class JobExecutionFactory
1414
{
1515
public function __construct(
1616
private JobExecutionIdGeneratorInterface $idGenerator,
17+
private JobExecutionParametersBuilderInterface $parametersBuilder,
1718
) {
1819
}
1920

@@ -24,6 +25,7 @@ public function __construct(
2425
*/
2526
public function create(string $name, array $configuration = []): JobExecution
2627
{
28+
$configuration = $configuration + $this->parametersBuilder->build($name);
2729
/** @var string $id */
2830
$id = $configuration['_id'] ??= $this->idGenerator->generate();
2931

0 commit comments

Comments
 (0)