Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

declare(strict_types=1);

namespace Yokai\Batch\Bridge\Symfony\Framework\DependencyInjection\CompilerPass;

use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Exception\LogicException;
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
use Yokai\Batch\Launcher\JobLauncherInterface;

/**
* This compiler pass ensure that service behind {@see JobLauncherInterface} is having the proper interface.
*/
final class ConfigureLauncherPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container): void
{
$templatingActualService = (string)$container->getAlias(JobLauncherInterface::class);

try {
$launcherService = $container->findDefinition($templatingActualService);
} catch (ServiceNotFoundException $exception) {
throw new LogicException(
message: \sprintf(
'Job launcher service "%s" does not exists.',
$templatingActualService,
),
previous: $exception,
);
}

$jobLauncherServiceClass = (string)$launcherService->getClass();
if (!\is_a($jobLauncherServiceClass, JobLauncherInterface::class, true)) {
throw new LogicException(
\sprintf(
'Job launcher service "%s" must implements interface "%s".',
$templatingActualService,
JobLauncherInterface::class,
),
);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php

declare(strict_types=1);

namespace Yokai\Batch\Bridge\Symfony\Framework\DependencyInjection\CompilerPass;

use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Exception\LogicException;
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
use Yokai\Batch\Storage\JobExecutionStorageInterface;
use Yokai\Batch\Storage\ListableJobExecutionStorageInterface;
use Yokai\Batch\Storage\QueryableJobExecutionStorageInterface;

/**
* This compiler pass ensure that service behind {@see JobExecutionStorageInterface} is having the proper interface.
* Also, if that service implements some optional interfaces, we create some autowiring alias for these.
*/
final class ConfigureStoragePass implements CompilerPassInterface
{
public function process(ContainerBuilder $container): void
{
$jobExecutionStorageActualService = (string)$container->getAlias(JobExecutionStorageInterface::class);

try {
$jobExecutionStorageService = $container->getDefinition($jobExecutionStorageActualService);
} catch (ServiceNotFoundException $exception) {
throw new LogicException(
message: \sprintf(
'Job execution storage service "%s" does not exists.',
$jobExecutionStorageActualService,
),
previous: $exception,
);
}

$jobExecutionStorageServiceClass = (string)$jobExecutionStorageService->getClass();
if (!\is_a($jobExecutionStorageServiceClass, JobExecutionStorageInterface::class, true)) {
throw new LogicException(
\sprintf(
'Job execution storage service "%s" must implements interface "%s".',
$jobExecutionStorageActualService,
JobExecutionStorageInterface::class,
),
);
}

$optionalInterfaces = [
ListableJobExecutionStorageInterface::class,
QueryableJobExecutionStorageInterface::class,
];
foreach ($optionalInterfaces as $interface) {
if (\is_a($jobExecutionStorageServiceClass, $interface, true)) {
$container
->setAlias($interface, $jobExecutionStorageActualService)
->setPublic(true)
;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

declare(strict_types=1);

namespace Yokai\Batch\Bridge\Symfony\Framework\DependencyInjection\CompilerPass;

use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Exception\LogicException;
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
use Yokai\Batch\Bridge\Symfony\Framework\UserInterface\Templating\TemplatingInterface;

/**
* This compiler pass ensure that service behind {@see TemplatingInterface} is having the proper interface.
*/
final class ConfigureTemplatingPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container): void
{
$templatingActualService = (string)$container->getAlias(TemplatingInterface::class);

try {
$templatingService = $container->findDefinition($templatingActualService);
} catch (ServiceNotFoundException $exception) {
throw new LogicException(
message: \sprintf(
'UI templating service "%s" does not exists.',
$templatingActualService,
),
previous: $exception,
);
}

$templatingServiceClass = (string)$templatingService->getClass();
if (!\is_a($templatingServiceClass, TemplatingInterface::class, true)) {
throw new LogicException(
\sprintf(
'UI templating service "%s" must implements interface "%s".',
$templatingActualService,
TemplatingInterface::class,
),
);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

namespace Yokai\Batch\Bridge\Symfony\Framework\DependencyInjection;

use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Exception\LogicException;
use Symfony\Component\DependencyInjection\Reference;
Expand All @@ -24,7 +23,7 @@ final class JobLauncherDefinitionFactory
/**
* Build a service definition from DSN string.
*/
public static function fromDsn(ContainerBuilder $container, string $dsn): Definition
public static function fromDsn(string $dsn): Definition|Reference
{
$dsnParts = \parse_url($dsn);
$launcherType = $dsnParts['scheme'] ?? null;
Expand All @@ -35,7 +34,7 @@ public static function fromDsn(ContainerBuilder $container, string $dsn): Defini
'simple' => self::simple(),
'console' => self::console($launcherConfig),
'messenger' => self::messenger(),
'service' => self::service($container, $launcherConfig),
'service' => self::service($launcherConfig),
default => throw new LogicException('Unsupported job launcher type "' . $launcherType . '".'),
};
}
Expand Down Expand Up @@ -78,12 +77,12 @@ private static function messenger(): Definition
/**
* @param array<string, string> $config
*/
private static function service(ContainerBuilder $container, array $config): Definition
private static function service(array $config): Reference
{
$service = $config['service'] ?? throw new LogicException(
'Missing "service" parameter to configure the job launcher.',
);

return $container->getDefinition($service);
return new Reference($service);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
use Symfony\Component\Config\Loader as ConfigLoader;
use Symfony\Component\Config\Loader\LoaderInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Exception\LogicException;
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
use Symfony\Component\DependencyInjection\Loader as DependencyInjectionLoader;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\Form\AbstractType;
Expand All @@ -27,8 +27,6 @@
use Yokai\Batch\Launcher\JobLauncherInterface;
use Yokai\Batch\Storage\FilesystemJobExecutionStorage;
use Yokai\Batch\Storage\JobExecutionStorageInterface;
use Yokai\Batch\Storage\ListableJobExecutionStorageInterface;
use Yokai\Batch\Storage\QueryableJobExecutionStorageInterface;

/**
* Dependency injection extension for yokai/batch Symfony Bundle.
Expand Down Expand Up @@ -123,47 +121,10 @@ private function configureStorage(ContainerBuilder $container, array $config): v
$defaultStorage = 'yokai_batch.storage.filesystem';
}

try {
$defaultStorageDefinition = $container->getDefinition($defaultStorage);
} catch (ServiceNotFoundException $exception) {
throw new LogicException(
\sprintf('Configured default job execution storage service "%s" does not exists.', $defaultStorage),
0,
$exception
);
}

$defaultStorageClass = $defaultStorageDefinition->getClass();
if ($defaultStorageClass === null) {
throw new LogicException(
\sprintf('Job execution storage service "%s", has no class.', $defaultStorage)
);
}

$interfaces = [
JobExecutionStorageInterface::class => true,
ListableJobExecutionStorageInterface::class => false,
QueryableJobExecutionStorageInterface::class => false,
];
foreach ($interfaces as $interface => $required) {
if (!\is_a($defaultStorageClass, $interface, true)) {
if ($required) {
throw new LogicException(
\sprintf(
'Job execution storage service "%s", is of class "%s", and must implements interface "%s".',
$defaultStorage,
$defaultStorageClass,
$interface
)
);
}
continue;
}
$container
->setAlias($interface, $defaultStorage)
->setPublic(true)
;
}
$container
->setAlias(JobExecutionStorageInterface::class, $defaultStorage)
->setPublic(true)
;
}

/**
Expand All @@ -179,17 +140,24 @@ private function configureLauncher(ContainerBuilder $container, array $config):
));
}

$launcherIdPerLauncherName = [];
foreach ($config['launchers'] as $name => $dsn) {
$definition = JobLauncherDefinitionFactory::fromDsn($container, $dsn);
$launcherId = 'yokai_batch.job_launcher.' . $name;
$container->setDefinition($launcherId, $definition);
$definitionOrReference = JobLauncherDefinitionFactory::fromDsn($dsn);
if ($definitionOrReference instanceof Definition) {
$launcherId = 'yokai_batch.job_launcher.' . $name;
$container->setDefinition($launcherId, $definitionOrReference);
} else {
$launcherId = (string)$definitionOrReference;
}

$launcherIdPerLauncherName[$name] = $launcherId;
$parameterName = $name . 'JobLauncher';
$container->registerAliasForArgument($launcherId, LoggerInterface::class, $parameterName);
}

$container->setAlias(
JobLauncherInterface::class,
'yokai_batch.job_launcher.' . $config['default'],
$launcherIdPerLauncherName[$config['default']],
);
}

Expand Down Expand Up @@ -240,25 +208,6 @@ private function configureUserInterface(ContainerBuilder $container, LoaderInter

$templating = $config['templating'];
if ($templating['service'] !== null) {
try {
$templatingClass = $container->getDefinition($templating['service'])->getClass();
if ($templatingClass === null || !\is_a($templatingClass, TemplatingInterface::class, true)) {
throw new LogicException(
\sprintf(
'Configured UI templating service "%s" must implements interface "%s".',
$templating['service'],
TemplatingInterface::class,
),
);
}
} catch (ServiceNotFoundException $exception) {
throw new LogicException(
\sprintf('Configured UI templating service "%s" does not exists.', $templating['service']),
0,
$exception
);
}

$container->setAlias(TemplatingInterface::class, $templating['service']);
} elseif ($templating['prefix'] !== null) {
$container->register('yokai_batch.ui.templating', ConfigurableTemplating::class)
Expand Down
6 changes: 6 additions & 0 deletions src/batch-symfony-framework/src/YokaiBatchBundle.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@

use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\Bundle;
use Yokai\Batch\Bridge\Symfony\Framework\DependencyInjection\CompilerPass\ConfigureLauncherPass;
use Yokai\Batch\Bridge\Symfony\Framework\DependencyInjection\CompilerPass\ConfigureStoragePass;
use Yokai\Batch\Bridge\Symfony\Framework\DependencyInjection\CompilerPass\ConfigureTemplatingPass;
use Yokai\Batch\Bridge\Symfony\Framework\DependencyInjection\CompilerPass\RegisterJobsCompilerPass;

/**
Expand All @@ -15,6 +18,9 @@ final class YokaiBatchBundle extends Bundle
{
public function build(ContainerBuilder $container): void
{
$container->addCompilerPass(new ConfigureLauncherPass());
$container->addCompilerPass(new ConfigureStoragePass());
$container->addCompilerPass(new ConfigureTemplatingPass());
$container->addCompilerPass(new RegisterJobsCompilerPass());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php

declare(strict_types=1);

namespace DependencyInjection\CompilerPass;

use PHPUnit\Framework\TestCase;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Exception\LogicException;
use Yokai\Batch\Bridge\Symfony\Framework\DependencyInjection\CompilerPass\ConfigureLauncherPass;
use Yokai\Batch\Launcher\JobLauncherInterface;
use Yokai\Batch\Launcher\SimpleJobLauncher;

final class ConfigureLauncherPassTest extends TestCase
{
public function testNominal(): void
{
$this->process(function (ContainerBuilder $container) {
$container->register('yokai_batch.launcher.simple', SimpleJobLauncher::class);
$container->setAlias(JobLauncherInterface::class, 'yokai_batch.launcher.simple');
});

self::assertTrue(true, 'No exception was raised');
}

public function testMissingService(): void
{
$this->expectExceptionObject(
new LogicException('Job launcher service "app.yokai_batch_job_launcher" does not exists.')
);
$this->process(function (ContainerBuilder $container) {
$container->setAlias(JobLauncherInterface::class, 'app.yokai_batch_job_launcher');
});
}

public function testInvalidService(): void
{
$this->expectExceptionObject(
new LogicException(
'Job launcher service "app.yokai_batch_job_launcher" must implements interface "' . JobLauncherInterface::class . '".',
),
);
$this->process(function (ContainerBuilder $container) {
$container->register('app.yokai_batch_job_launcher', self::class);
$container->setAlias(JobLauncherInterface::class, 'app.yokai_batch_job_launcher');
});
}

private function process(\Closure $configure): void
{
$container = new ContainerBuilder();
$configure($container);
(new ConfigureLauncherPass())->process($container);
}
}
Loading
Loading