Skip to content

Commit 9fe827c

Browse files
authored
Add a way for JobExecution storage to be setup before used (#107)
* Add a way for JobExecution storage to be setup before used * Fixed code style * Test DoctrineDBALJobExecutionStorage::createSchema deprecated method * Fixed wrong usage of magic constants * Removed deprecation test * Proper test without deprecation
1 parent 91d1785 commit 9fe827c

File tree

8 files changed

+222
-15
lines changed

8 files changed

+222
-15
lines changed

src/batch-doctrine-dbal/src/DoctrineDBALJobExecutionStorage.php

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,15 @@
2020
use Yokai\Batch\Storage\JobExecutionStorageInterface;
2121
use Yokai\Batch\Storage\Query;
2222
use Yokai\Batch\Storage\QueryableJobExecutionStorageInterface;
23+
use Yokai\Batch\Storage\SetupableJobExecutionStorageInterface;
2324

2425
/**
2526
* This {@see JobExecutionStorageInterface} will store
2627
* {@see JobExecution} in an SQL database using doctrine/dbal.
2728
*/
28-
final class DoctrineDBALJobExecutionStorage implements QueryableJobExecutionStorageInterface
29+
final class DoctrineDBALJobExecutionStorage implements
30+
QueryableJobExecutionStorageInterface,
31+
SetupableJobExecutionStorageInterface
2932
{
3033
private const DEFAULT_OPTIONS = [
3134
'table' => 'yokai_batch_job_execution',
@@ -54,7 +57,7 @@ public function __construct(ConnectionRegistry $doctrine, array $options)
5457
/**
5558
* Create required table for this storage.
5659
*/
57-
public function createSchema(): void
60+
public function setup(): void
5861
{
5962
$assetFilter = $this->connection->getConfiguration()->getSchemaAssetsFilter();
6063
$this->connection->getConfiguration()->setSchemaAssetsFilter(null);
@@ -88,6 +91,24 @@ public function createSchema(): void
8891
$this->connection->getConfiguration()->setSchemaAssetsFilter($assetFilter);
8992
}
9093

94+
/**
95+
* Create required table for this storage.
96+
* @deprecated
97+
*/
98+
public function createSchema(): void
99+
{
100+
@\trigger_error(
101+
\sprintf(
102+
'Since yokai/batch-doctrine-dbal 0.5.8: ' .
103+
'Method "%s()" is deprecated and will be removed in 0.6.0. Use %s::setup() instead.',
104+
__METHOD__,
105+
__CLASS__,
106+
),
107+
\E_USER_DEPRECATED,
108+
);
109+
$this->setup();
110+
}
111+
91112
public function store(JobExecution $execution): void
92113
{
93114
try {

src/batch-doctrine-dbal/tests/DoctrineDBALJobExecutionStorageTest.php

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public function testCreateStandardTable(): void
3434
$schemaManager = $this->connection->getSchemaManager();
3535

3636
self::assertFalse($schemaManager->tablesExist(['yokai_batch_job_execution']));
37-
$this->createStorage()->createSchema();
37+
$this->createStorage()->setup();
3838
self::assertTrue($schemaManager->tablesExist(['yokai_batch_job_execution']));
3939

4040
$columns = $schemaManager->listTableColumns('yokai_batch_job_execution');
@@ -61,7 +61,7 @@ public function testCreateCustomTable(): void
6161
$schemaManager = $this->connection->getSchemaManager();
6262

6363
self::assertFalse($schemaManager->tablesExist(['acme_job_executions']));
64-
$this->createStorage(['table' => 'acme_job_executions'])->createSchema();
64+
$this->createStorage(['table' => 'acme_job_executions'])->setup();
6565
self::assertTrue($schemaManager->tablesExist(['acme_job_executions']));
6666

6767
$columns = $schemaManager->listTableColumns('acme_job_executions');
@@ -86,7 +86,7 @@ public function testCreateCustomTable(): void
8686
public function testStoreInsert(): void
8787
{
8888
$storage = $this->createStorage();
89-
$storage->createSchema();
89+
$storage->setup();
9090

9191
$export = JobExecution::createRoot('123', 'export', new BatchStatus(BatchStatus::RUNNING));
9292
$export->setStartTime(new DateTimeImmutable('2021-09-23 11:05:00'));
@@ -122,7 +122,7 @@ public function testStoreInsert(): void
122122
public function testStoreUpdate(): void
123123
{
124124
$storage = $this->createStorage();
125-
$storage->createSchema();
125+
$storage->setup();
126126
$storage->store($execution = JobExecution::createRoot('123', 'export'));
127127
$execution->setStatus(BatchStatus::COMPLETED);
128128
$storage->store($execution);
@@ -138,7 +138,7 @@ public function testStoreFailing(): void
138138
$this->expectException(CannotStoreJobExecutionException::class);
139139

140140
$storage = $this->createStorage();
141-
/** not calling {@see DoctrineDBALJobExecutionStorage::createSchema} will cause table to not exists */
141+
/** not calling {@see DoctrineDBALJobExecutionStorage::setup} will cause table to not exists */
142142
$storage->store(JobExecution::createRoot('123', 'export'));
143143
}
144144

@@ -147,7 +147,7 @@ public function testRemove(): void
147147
$this->expectException(JobExecutionNotFoundException::class);
148148

149149
$storage = $this->createStorage();
150-
$storage->createSchema();
150+
$storage->setup();
151151
$storage->store($execution = JobExecution::createRoot('123', 'export'));
152152
$storage->remove($execution);
153153

@@ -159,14 +159,14 @@ public function testRemoveFailing(): void
159159
$this->expectException(CannotRemoveJobExecutionException::class);
160160

161161
$storage = $this->createStorage();
162-
/** not calling {@see DoctrineDBALJobExecutionStorage::createSchema} will cause table to not exists */
162+
/** not calling {@see DoctrineDBALJobExecutionStorage::setup} will cause table to not exists */
163163
$storage->remove(JobExecution::createRoot('123', 'export'));
164164
}
165165

166166
public function testRetrieve(): void
167167
{
168168
$storage = $this->createStorage();
169-
$storage->createSchema();
169+
$storage->setup();
170170
$storage->store(JobExecution::createRoot('123', 'export'));
171171
$storage->store(JobExecution::createRoot('456', 'import'));
172172

@@ -184,7 +184,7 @@ public function testRetrieveNotFound(): void
184184
$this->expectException(JobExecutionNotFoundException::class);
185185

186186
$storage = $this->createStorage();
187-
$storage->createSchema();
187+
$storage->setup();
188188
$storage->store(JobExecution::createRoot('123', 'export'));
189189

190190
$storage->retrieve('export', '456');
@@ -195,7 +195,7 @@ public function testRetrieveFailing(): void
195195
$this->expectException(JobExecutionNotFoundException::class);
196196

197197
$storage = $this->createStorage();
198-
/** not calling {@see DoctrineDBALJobExecutionStorage::createSchema} will cause table to not exists */
198+
/** not calling {@see DoctrineDBALJobExecutionStorage::setup} will cause table to not exists */
199199
$storage->retrieve('export', '456');
200200
}
201201

@@ -217,7 +217,7 @@ public function testRetrieveInvalid(array $data, Throwable $error): void
217217
$data['logs'] ??= '';
218218

219219
$storage = $this->createStorage();
220-
$storage->createSchema();
220+
$storage->setup();
221221
$this->connection->insert('yokai_batch_job_execution', $data);
222222
$storage->retrieve('export', '123');
223223
}
@@ -249,7 +249,7 @@ public function retrieveInvalid(): \Generator
249249
public function testList(): void
250250
{
251251
$storage = $this->createStorage();
252-
$storage->createSchema();
252+
$storage->setup();
253253
$this->loadFixtures($storage);
254254

255255
self::assertExecutionIds(['123'], $storage->list('export'));
@@ -262,7 +262,7 @@ public function testList(): void
262262
public function testQuery(QueryBuilder $queryBuilder, array $expectedCouples): void
263263
{
264264
$storage = $this->createStorage();
265-
$storage->createSchema();
265+
$storage->setup();
266266
$this->loadFixtures($storage);
267267

268268
self::assertExecutions($expectedCouples, $storage->query($queryBuilder->getQuery()));
@@ -343,6 +343,14 @@ public function queries(): Generator
343343
];
344344
}
345345

346+
public function testCreateSchemaDeprecated(): void
347+
{
348+
$schemaManager = $this->connection->getSchemaManager();
349+
self::assertFalse($schemaManager->tablesExist(['yokai_batch_job_execution']));
350+
$this->createStorage()->createSchema();
351+
self::assertTrue($schemaManager->tablesExist(['yokai_batch_job_execution']));
352+
}
353+
346354
public static function assertExecutionIds(array $ids, iterable $executions): void
347355
{
348356
$actualIds = [];
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Yokai\Batch\Bridge\Symfony\Console;
6+
7+
use Symfony\Component\Console\Attribute\AsCommand;
8+
use Symfony\Component\Console\Command\Command;
9+
use Symfony\Component\Console\Input\InputInterface;
10+
use Symfony\Component\Console\Output\OutputInterface;
11+
use Symfony\Component\Console\Style\SymfonyStyle;
12+
use Yokai\Batch\Storage\JobExecutionStorageInterface;
13+
use Yokai\Batch\Storage\SetupableJobExecutionStorageInterface;
14+
15+
/**
16+
* Prepare the required infrastructure for the job execution storage.
17+
*/
18+
#[AsCommand(name: 'yokai:batch:setup-storage', description: 'Prepare the required infrastructure for the storage')]
19+
final class SetupStorageCommand extends Command
20+
{
21+
public function __construct(
22+
private JobExecutionStorageInterface $storage,
23+
) {
24+
parent::__construct();
25+
}
26+
27+
protected function configure(): void
28+
{
29+
$this
30+
->setHelp(
31+
<<<EOF
32+
The <info>%command.name%</info> command setups the job execution storage:
33+
34+
<info>php %command.full_name%</info>
35+
EOF
36+
)
37+
;
38+
}
39+
40+
protected function execute(InputInterface $input, OutputInterface $output): int
41+
{
42+
$io = new SymfonyStyle($input, $output);
43+
44+
if ($this->storage instanceof SetupableJobExecutionStorageInterface) {
45+
$this->storage->setup();
46+
$io->success('The storage was set up successfully.');
47+
} else {
48+
$io->note('The storage does not support setup.');
49+
}
50+
51+
return self::SUCCESS;
52+
}
53+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Yokai\Batch\Tests\Bridge\Symfony\Console;
6+
7+
use PHPUnit\Framework\TestCase;
8+
use Symfony\Component\Console\Tester\CommandTester;
9+
use Yokai\Batch\Bridge\Symfony\Console\SetupStorageCommand;
10+
use Yokai\Batch\Exception\JobExecutionNotFoundException;
11+
use Yokai\Batch\JobExecution;
12+
use Yokai\Batch\Storage\JobExecutionStorageInterface;
13+
use Yokai\Batch\Storage\SetupableJobExecutionStorageInterface;
14+
15+
final class SetupStorageCommandTest extends TestCase
16+
{
17+
public function testSetupRequired(): void
18+
{
19+
$this->execute(
20+
$storage = new class() implements
21+
JobExecutionStorageInterface,
22+
SetupableJobExecutionStorageInterface {
23+
public bool $wasSetup = false;
24+
25+
public function setup(): void
26+
{
27+
$this->wasSetup = true;
28+
}
29+
30+
public function store(JobExecution $execution): void
31+
{
32+
}
33+
34+
public function remove(JobExecution $execution): void
35+
{
36+
}
37+
38+
public function retrieve(string $jobName, string $executionId): JobExecution
39+
{
40+
throw new JobExecutionNotFoundException($jobName, $executionId);
41+
}
42+
},
43+
'[OK] The storage was set up successfully.',
44+
);
45+
self::assertTrue($storage->wasSetup);
46+
}
47+
48+
public function testSetupNotRequired(): void
49+
{
50+
$this->execute(
51+
new class() implements JobExecutionStorageInterface {
52+
public function store(JobExecution $execution): void
53+
{
54+
}
55+
56+
public function remove(JobExecution $execution): void
57+
{
58+
}
59+
60+
public function retrieve(string $jobName, string $executionId): JobExecution
61+
{
62+
throw new JobExecutionNotFoundException($jobName, $executionId);
63+
}
64+
},
65+
'! [NOTE] The storage does not support setup.',
66+
);
67+
}
68+
69+
private function execute(JobExecutionStorageInterface $storage, string $expected): void
70+
{
71+
$tester = new CommandTester(new SetupStorageCommand($storage));
72+
$tester->execute([]);
73+
$tester->assertCommandIsSuccessful();
74+
self::assertSame($expected, \trim($tester->getDisplay(true)));
75+
}
76+
}

src/batch-symfony-framework/composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"php": "^8.0",
1515
"composer-runtime-api": "^2.0",
1616
"symfony/config": "^5.0|^6.0",
17+
"symfony/console": "^5.0|^6.0",
1718
"symfony/dependency-injection": "^5.0|^6.0",
1819
"symfony/http-kernel": "^5.0|^6.0",
1920
"symfony/framework-bundle": "^5.0|^6.0",

src/batch-symfony-framework/src/Resources/services/symfony/console/command.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,11 @@
1313
<argument type="service" id="yokai_batch.job_executor"/>
1414
<tag name="console.command"/>
1515
</service>
16+
17+
<service id="yokai_batch.cli.setup_storage_command"
18+
class="Yokai\Batch\Bridge\Symfony\Console\SetupStorageCommand">
19+
<argument type="service" id="Yokai\Batch\Storage\JobExecutionStorageInterface"/>
20+
<tag name="console.command"/>
21+
</service>
1622
</services>
1723
</container>
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Yokai\Batch\Tests\Bridge\Symfony\Framework;
6+
7+
use Symfony\Bundle\FrameworkBundle\Console\Application;
8+
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
9+
10+
final class CliTest extends KernelTestCase
11+
{
12+
public function testRegisteredCommands(): void
13+
{
14+
$names = \array_keys(
15+
(new Application(self::bootKernel()))->all('yokai'),
16+
);
17+
\sort($names);
18+
self::assertSame(
19+
[
20+
'yokai:batch:run',
21+
'yokai:batch:setup-storage',
22+
],
23+
$names,
24+
);
25+
}
26+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Yokai\Batch\Storage;
6+
7+
/**
8+
* A job execution having this interface tells the developers it should be setuped before being used.
9+
*/
10+
interface SetupableJobExecutionStorageInterface
11+
{
12+
/**
13+
* Setup the storage.
14+
*/
15+
public function setup(): void;
16+
}

0 commit comments

Comments
 (0)