Skip to content

Commit

Permalink
Add a component to build JobParameters defaults at contruction time (#…
Browse files Browse the repository at this point in the history
…123)

* Add a component to build JobParameters defaults at contruction time

* wip

* Fixed symfony configuration structure

* Add tests for Job configuration

* Fix checkstyle
  • Loading branch information
yann-eugone authored Jan 8, 2025
1 parent c6a0600 commit cf1c154
Show file tree
Hide file tree
Showing 23 changed files with 474 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use Yokai\Batch\Bridge\Symfony\Console\CommandRunner;
use Yokai\Batch\Bridge\Symfony\Console\RunCommandJobLauncher;
use Yokai\Batch\Factory\JobExecutionFactory;
use Yokai\Batch\Factory\JobExecutionParametersBuilder\NullJobExecutionParametersBuilder;
use Yokai\Batch\Factory\UniqidJobExecutionIdGenerator;
use Yokai\Batch\Test\Storage\InMemoryJobExecutionStorage;

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

$launcher = new RunCommandJobLauncher(
new JobExecutionFactory(new UniqidJobExecutionIdGenerator()),
new JobExecutionFactory(new UniqidJobExecutionIdGenerator(), new NullJobExecutionParametersBuilder()),
$commandRunner->reveal(),
$storage = new InMemoryJobExecutionStorage(),
'test.log'
Expand Down
3 changes: 2 additions & 1 deletion src/batch-symfony-console/tests/RunJobCommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use Yokai\Batch\Bridge\Symfony\Console\RunJobCommand;
use Yokai\Batch\Exception\UnexpectedValueException;
use Yokai\Batch\Factory\JobExecutionFactory;
use Yokai\Batch\Factory\JobExecutionParametersBuilder\NullJobExecutionParametersBuilder;
use Yokai\Batch\Factory\UniqidJobExecutionIdGenerator;
use Yokai\Batch\Job\JobExecutionAccessor;
use Yokai\Batch\Job\JobExecutor;
Expand All @@ -38,7 +39,7 @@ protected function setUp(): void
$this->job = $this->prophesize(JobInterface::class);

$this->accessor = new JobExecutionAccessor(
new JobExecutionFactory(new UniqidJobExecutionIdGenerator()),
new JobExecutionFactory(new UniqidJobExecutionIdGenerator(), new NullJobExecutionParametersBuilder()),
new InMemoryJobExecutionStorage(),
);
$this->executor = new JobExecutor(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
* @phpstan-type Config array{
* storage: StorageConfig,
* launcher: LauncherConfig,
* parameters: ParametersConfig,
* ui: UserInterfaceConfig,
* }
* @phpstan-type StorageConfig array{
Expand All @@ -31,6 +32,10 @@
* default: string|null,
* launchers: array<string, string>,
* }
* @phpstan-type ParametersConfig array{
* global: array<string, mixed>,
* per_job: array<string, array<string, mixed>>,
* }
* @phpstan-type UserInterfaceConfig array{
* enabled: bool,
* security: array{
Expand Down Expand Up @@ -59,6 +64,7 @@ public function getConfigTreeBuilder(): TreeBuilder
->children()
->append($this->storage())
->append($this->launcher())
->append($this->parameters())
->append($this->ui())
->end()
;
Expand Down Expand Up @@ -131,6 +137,47 @@ private function launcher(): ArrayNodeDefinition
return $node;
}

private function parameters(): ArrayNodeDefinition
{
/** @var ArrayNodeDefinition $node */
$node = (new TreeBuilder('parameters'))->getRootNode();

$isStringAssociativeArray = function (mixed $value): bool {
if (!\is_array($value)) {
return false;
}

foreach ($value as $key => $unused) {
if (!\is_string($key)) {
return false;
}
}

return true;
};

$node
->addDefaultsIfNotSet()
->children()
->arrayNode('global')
->useAttributeAsKey('name')
->variablePrototype()
->end()
->end()
->arrayNode('per_job')
->useAttributeAsKey('name')
->variablePrototype()
->validate()
->ifTrue(fn(mixed $value) => !$isStringAssociativeArray($value))
->thenInvalid('Should be an array<string, mixed>.')
->end()
->end()
->end()
;

return $node;
}

private function ui(): ArrayNodeDefinition
{
/** @var ArrayNodeDefinition $node */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
use Yokai\Batch\Bridge\Symfony\Framework\UserInterface\Templating\ConfigurableTemplating;
use Yokai\Batch\Bridge\Symfony\Framework\UserInterface\Templating\SonataAdminTemplating;
use Yokai\Batch\Bridge\Symfony\Framework\UserInterface\Templating\TemplatingInterface;
use Yokai\Batch\Factory\JobExecutionParametersBuilder\PerJobJobExecutionParametersBuilder;
use Yokai\Batch\Factory\JobExecutionParametersBuilder\StaticJobExecutionParametersBuilder;
use Yokai\Batch\Launcher\JobLauncherInterface;
use Yokai\Batch\Storage\FilesystemJobExecutionStorage;
use Yokai\Batch\Storage\JobExecutionStorageInterface;
Expand All @@ -34,6 +36,7 @@
* @phpstan-import-type Config from Configuration
* @phpstan-import-type StorageConfig from Configuration
* @phpstan-import-type LauncherConfig from Configuration
* @phpstan-import-type ParametersConfig from Configuration
* @phpstan-import-type UserInterfaceConfig from Configuration
*/
final class YokaiBatchExtension extends Extension
Expand Down Expand Up @@ -64,6 +67,7 @@ public function load(array $configs, ContainerBuilder $container): void

$this->configureStorage($container, $config['storage']);
$this->configureLauncher($container, $config['launcher']);
$this->configureParameters($container, $config['parameters']);
$this->configureUserInterface($container, $loader, $config['ui']);

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

/**
* @param ParametersConfig $config
*/
private function configureParameters(ContainerBuilder $container, array $config): void
{
if ($config['global'] !== []) {
$container->register('yokai_batch.job_execution_parameters_builder.global')
->setClass(StaticJobExecutionParametersBuilder::class)
->setArgument('$parameters', $config['global'])
->addTag('yokai_batch.job_execution_parameters_builder');
}
if ($config['per_job'] !== []) {
$container->register('yokai_batch.job_execution_parameters_builder.per_job')
->setClass(PerJobJobExecutionParametersBuilder::class)
->setArgument('$perJobParameters', $config['per_job'])
->addTag('yokai_batch.job_execution_parameters_builder');
}
}

/**
* @param UserInterfaceConfig $config
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,8 @@

<service id="Yokai\Batch\Factory\JobExecutionIdGeneratorInterface"
alias="yokai_batch.job_execution_id_generator.uniqid"/>

<service id="Yokai\Batch\Factory\JobExecutionParametersBuilderInterface"
alias="yokai_batch.job_execution_parameters_builder.chain"/>
</services>
</container>
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<service id="yokai_batch.job_execution_factory"
class="Yokai\Batch\Factory\JobExecutionFactory">
<argument type="service" id="Yokai\Batch\Factory\JobExecutionIdGeneratorInterface"/>
<argument type="service" id="Yokai\Batch\Factory\JobExecutionParametersBuilderInterface"/>
</service>

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

<service id="yokai_batch.job_execution_id_generator.uniqid"
class="Yokai\Batch\Factory\UniqidJobExecutionIdGenerator"/>

<service id="yokai_batch.job_execution_parameters_builder.chain"
class="Yokai\Batch\Factory\JobExecutionParametersBuilder\ChainJobExecutionParametersBuilder">
<argument type="tagged_iterator" tag="yokai_batch.job_execution_parameters_builder"/>
</service>
</services>
</container>
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Exception;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Exception\LogicException;
Expand All @@ -17,6 +18,10 @@
use Yokai\Batch\Bridge\Symfony\Framework\UserInterface\Templating\SonataAdminTemplating;
use Yokai\Batch\Bridge\Symfony\Framework\UserInterface\Templating\TemplatingInterface;
use Yokai\Batch\Bridge\Symfony\Messenger\DispatchMessageJobLauncher;
use Yokai\Batch\Factory\JobExecutionParametersBuilder\ChainJobExecutionParametersBuilder;
use Yokai\Batch\Factory\JobExecutionParametersBuilder\PerJobJobExecutionParametersBuilder;
use Yokai\Batch\Factory\JobExecutionParametersBuilder\StaticJobExecutionParametersBuilder;
use Yokai\Batch\Factory\JobExecutionParametersBuilderInterface;
use Yokai\Batch\Launcher\JobLauncherInterface;
use Yokai\Batch\Launcher\SimpleJobLauncher;
use Yokai\Batch\Storage\JobExecutionStorageInterface;
Expand Down Expand Up @@ -387,4 +392,111 @@ public function errors(): \Generator
new ServiceNotFoundException('app.unknown'),
];
}

/**
* @dataProvider parameters
*/
public function testParameters(array $config, array|null $global, array|null $perJob): void
{
$container = $this->createContainer($config);

$globalService = $this->getDefinition($container, 'yokai_batch.job_execution_parameters_builder.global');
if ($global !== null) {
self::assertNotNull($globalService);
self::assertSame(StaticJobExecutionParametersBuilder::class, $globalService->getClass());
self::assertTrue($globalService->hasTag('yokai_batch.job_execution_parameters_builder'));
self::assertSame($global, $globalService->getArgument('$parameters'));
} else {
self::assertNull($globalService);
}
$perJobService = $this->getDefinition($container, 'yokai_batch.job_execution_parameters_builder.per_job');
if ($perJob !== null) {
self::assertNotNull($perJobService);
self::assertSame(PerJobJobExecutionParametersBuilder::class, $perJobService->getClass());
self::assertTrue($perJobService->hasTag('yokai_batch.job_execution_parameters_builder'));
self::assertSame($perJob, $perJobService->getArgument('$perJobParameters'));
} else {
self::assertNull($perJobService);
}
$defaultService = $this->getDefinition($container, JobExecutionParametersBuilderInterface::class);
self::assertNotNull($defaultService);
self::assertSame(ChainJobExecutionParametersBuilder::class, $defaultService->getClass());
$defaultServiceBuilders = $defaultService->getArgument(0);
self::assertTrue($defaultServiceBuilders instanceof TaggedIteratorArgument);
/** @var TaggedIteratorArgument $defaultServiceBuilders */
self::assertSame('yokai_batch.job_execution_parameters_builder', $defaultServiceBuilders->getTag());
}

public function parameters(): \Generator
{
yield 'Global parameters' => [
['parameters' => ['global' => ['global' => true]]],
['global' => true],
null,
];
yield 'Per job parameters' => [
['parameters' => ['per_job' => ['job.foo' => ['foo' => true], 'job.bar' => ['bar' => true]]]],
null,
['job.foo' => ['foo' => true], 'job.bar' => ['bar' => true]],
];
yield 'Global AND per job parameters' => [
['parameters' => [
'global' => ['global' => true],
'per_job' => ['job.foo' => ['foo' => true], 'job.bar' => ['bar' => true]],
]],
['global' => true],
['job.foo' => ['foo' => true], 'job.bar' => ['bar' => true]],
];
}

/**
* @dataProvider invalidParameters
*/
public function testInvalidParameters(array $config, \Exception $error): void
{
$this->expectExceptionObject($error);
$this->createContainer($config);
}

public function invalidParameters(): \Generator
{
yield 'Per job parameters value must be an array' => [
['parameters' => ['per_job' => ['job.foo' => 'string']]],
new InvalidConfigurationException(
'Invalid configuration for path "yokai_batch.parameters.per_job.job.foo": Should be an array<string, mixed>.'
),
];
yield 'Per job parameters value must be a string indexed array' => [
['parameters' => ['per_job' => ['job.foo' => [1, 2, 3]]]],
new InvalidConfigurationException(
'Invalid configuration for path "yokai_batch.parameters.per_job.job.foo": Should be an array<string, mixed>.'
),
];
}

private function createContainer(array $config, \Closure|null $configure = null): ContainerBuilder
{
$container = new ContainerBuilder();
if ($configure !== null) {
$configure($container);
}
$container->registerExtension(new YokaiBatchExtension());
$container->loadFromExtension('yokai_batch', $config);

$container->getCompilerPassConfig()->setOptimizationPasses([]);
$container->getCompilerPassConfig()->setRemovingPasses([]);
$container->getCompilerPassConfig()->setAfterRemovingPasses([]);
$container->compile();

return $container;
}

private function getDefinition(ContainerBuilder $container, string $id): Definition|null
{
try {
return $container->findDefinition($id);
} catch (ServiceNotFoundException) {
return null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use Yokai\Batch\Bridge\Symfony\Messenger\DispatchMessageJobLauncher;
use Yokai\Batch\Bridge\Symfony\Messenger\LaunchJobMessage;
use Yokai\Batch\Factory\JobExecutionFactory;
use Yokai\Batch\Factory\JobExecutionParametersBuilder\NullJobExecutionParametersBuilder;
use Yokai\Batch\Factory\UniqidJobExecutionIdGenerator;
use Yokai\Batch\Test\Factory\SequenceJobExecutionIdGenerator;
use Yokai\Batch\Test\Storage\InMemoryJobExecutionStorage;
Expand All @@ -21,7 +22,7 @@ final class DispatchMessageJobLauncherTest extends TestCase
public function testLaunch(): void
{
$jobLauncher = new DispatchMessageJobLauncher(
new JobExecutionFactory(new UniqidJobExecutionIdGenerator()),
new JobExecutionFactory(new UniqidJobExecutionIdGenerator(), new NullJobExecutionParametersBuilder()),
$storage = new InMemoryJobExecutionStorage(),
$messageBus = new BufferingMessageBus()
);
Expand All @@ -41,9 +42,12 @@ public function testLaunch(): void
public function testLaunchWithNoId(): void
{
$jobLauncher = new DispatchMessageJobLauncher(
new JobExecutionFactory(new SequenceJobExecutionIdGenerator(['123456789'])),
new JobExecutionFactory(
new SequenceJobExecutionIdGenerator(['123456789']),
new NullJobExecutionParametersBuilder(),
),
$storage = new InMemoryJobExecutionStorage(),
$messageBus = new BufferingMessageBus()
$messageBus = new BufferingMessageBus(),
);

$jobExecutionFromLauncher = $jobLauncher->launch('testing');
Expand All @@ -60,7 +64,7 @@ public function testLaunchWithNoId(): void
public function testLaunchAndMessengerFail(): void
{
$jobLauncher = new DispatchMessageJobLauncher(
new JobExecutionFactory(new UniqidJobExecutionIdGenerator()),
new JobExecutionFactory(new UniqidJobExecutionIdGenerator(), new NullJobExecutionParametersBuilder()),
$storage = new InMemoryJobExecutionStorage(),
new FailingMessageBus(new TransportException('This is a test'))
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Yokai\Batch\Bridge\Symfony\Messenger\LaunchJobMessage;
use Yokai\Batch\Bridge\Symfony\Messenger\LaunchJobMessageHandler;
use Yokai\Batch\Factory\JobExecutionFactory;
use Yokai\Batch\Factory\JobExecutionParametersBuilder\NullJobExecutionParametersBuilder;
use Yokai\Batch\Job\JobExecutionAccessor;
use Yokai\Batch\Job\JobExecutor;
use Yokai\Batch\Job\JobInterface;
Expand All @@ -35,7 +36,10 @@ public function execute(JobExecution $jobExecution): void
$jobExecutionStorage = new InMemoryJobExecutionStorage();
$handler = new LaunchJobMessageHandler(
new JobExecutionAccessor(
new JobExecutionFactory(new SequenceJobExecutionIdGenerator(['123456'])),
new JobExecutionFactory(
new SequenceJobExecutionIdGenerator(['123456']),
new NullJobExecutionParametersBuilder(),
),
$jobExecutionStorage,
),
new JobExecutor(
Expand Down
2 changes: 2 additions & 0 deletions src/batch/src/Factory/JobExecutionFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ final class JobExecutionFactory
{
public function __construct(
private JobExecutionIdGeneratorInterface $idGenerator,
private JobExecutionParametersBuilderInterface $parametersBuilder,
) {
}

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

Expand Down
Loading

0 comments on commit cf1c154

Please sign in to comment.