Skip to content

Commit b2e6a7d

Browse files
authored
Introduce JobParameterAccessor to help job components access the required execution parameters (#26)
* Introduce JobParameterAccessor to help job components access the required execution parameters * Refactored FlatFileReader & FlatFileWriter to use a JobParameterAccessorInterface instead of fetching file path from JobParameters * Introduce more job parameter accessors, mostly decorators able to move in JobExecution graph * Added more tests to cover FlatFileReader & FlatFileWriter * Add missing tests cases for job parameters accessors * Allow extra variables from constructor in ReplaceWithVariablesParameterAccessor * Fixed PHPStan issue with array type
1 parent d9c2e44 commit b2e6a7d

32 files changed

+1194
-97
lines changed

src/batch-box-spout/src/FlatFileReader.php

Lines changed: 20 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -5,33 +5,35 @@
55
namespace Yokai\Batch\Bridge\Box\Spout;
66

77
use Box\Spout\Common\Entity\Row;
8+
use Box\Spout\Common\Type;
89
use Box\Spout\Reader\Common\Creator\ReaderFactory;
910
use Box\Spout\Reader\CSV\Reader as CsvReader;
1011
use Box\Spout\Reader\SheetInterface;
1112
use Yokai\Batch\Exception\InvalidArgumentException;
12-
use Yokai\Batch\Exception\UndefinedJobParameterException;
1313
use Yokai\Batch\Exception\UnexpectedValueException;
1414
use Yokai\Batch\Job\Item\ItemReaderInterface;
1515
use Yokai\Batch\Job\JobExecutionAwareInterface;
1616
use Yokai\Batch\Job\JobExecutionAwareTrait;
17+
use Yokai\Batch\Job\Parameters\JobParameterAccessorInterface;
1718

1819
final class FlatFileReader implements
1920
ItemReaderInterface,
2021
JobExecutionAwareInterface
2122
{
2223
use JobExecutionAwareTrait;
2324

24-
public const SOURCE_FILE_PARAMETER = 'sourceFile';
25-
2625
public const HEADERS_MODE_SKIP = 'skip';
2726
public const HEADERS_MODE_COMBINE = 'combine';
2827
public const HEADERS_MODE_NONE = 'none';
29-
public const AVAILABLE_HEADERS_MODES = [
28+
29+
private const HEADERS_MODES = [
3030
self::HEADERS_MODE_SKIP,
3131
self::HEADERS_MODE_COMBINE,
3232
self::HEADERS_MODE_NONE,
3333
];
3434

35+
private const TYPES = [Type::CSV, Type::XLSX, Type::ODS];
36+
3537
/**
3638
* @var string
3739
*/
@@ -53,23 +55,26 @@ final class FlatFileReader implements
5355
private ?array $headers;
5456

5557
/**
56-
* @var string|null
58+
* @var JobParameterAccessorInterface
5759
*/
58-
private ?string $filePath;
60+
private JobParameterAccessorInterface $filePath;
5961

6062
/**
6163
* @phpstan-param array{delimiter?: string, enclosure?: string} $options
6264
* @phpstan-param list<string>|null $headers
6365
*/
6466
public function __construct(
6567
string $type,
68+
JobParameterAccessorInterface $filePath,
6669
array $options = [],
6770
string $headersMode = self::HEADERS_MODE_NONE,
68-
array $headers = null,
69-
string $filePath = null
71+
array $headers = null
7072
) {
71-
if (!in_array($headersMode, self::AVAILABLE_HEADERS_MODES, true)) {
72-
throw UnexpectedValueException::enum(self::AVAILABLE_HEADERS_MODES, $headersMode, 'Invalid header mode.');
73+
if (!in_array($type, self::TYPES, true)) {
74+
throw UnexpectedValueException::enum(self::TYPES, $type, 'Invalid type.');
75+
}
76+
if (!in_array($headersMode, self::HEADERS_MODES, true)) {
77+
throw UnexpectedValueException::enum(self::HEADERS_MODES, $headersMode, 'Invalid header mode.');
7378
}
7479
if ($headers !== null && $headersMode === self::HEADERS_MODE_COMBINE) {
7580
throw new InvalidArgumentException(
@@ -78,10 +83,10 @@ public function __construct(
7883
}
7984

8085
$this->type = $type;
86+
$this->filePath = $filePath;
8187
$this->options = $options;
8288
$this->headersMode = $headersMode;
8389
$this->headers = $headers;
84-
$this->filePath = $filePath;
8590
}
8691

8792
/**
@@ -90,10 +95,11 @@ public function __construct(
9095
public function read(): iterable
9196
{
9297
$reader = ReaderFactory::createFromType($this->type);
93-
if ($reader instanceof CsvReader && isset($this->options['delimiter'])) {
94-
$reader->setFieldDelimiter($this->options['delimiter']);
98+
if ($reader instanceof CsvReader) {
99+
$reader->setFieldDelimiter($this->options['delimiter'] ?? ',');
100+
$reader->setFieldEnclosure($this->options['enclosure'] ?? '"');
95101
}
96-
$reader->open($this->getFilePath());
102+
$reader->open((string)$this->filePath->get($this->jobExecution));
97103

98104
$headers = $this->headers;
99105

@@ -123,17 +129,4 @@ public function read(): iterable
123129

124130
$reader->close();
125131
}
126-
127-
protected function getFilePath(): string
128-
{
129-
if ($this->filePath) {
130-
return $this->filePath;
131-
}
132-
133-
try {
134-
return (string)$this->jobExecution->getParameter(self::SOURCE_FILE_PARAMETER);
135-
} catch (UndefinedJobParameterException $exception) {
136-
return (string)$this->jobExecution->getRootExecution()->getParameter(self::SOURCE_FILE_PARAMETER);
137-
}
138-
}
139132
}

src/batch-box-spout/src/FlatFileWriter.php

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace Yokai\Batch\Bridge\Box\Spout;
66

7+
use Box\Spout\Common\Type;
78
use Box\Spout\Writer\Common\Creator\WriterEntityFactory;
89
use Box\Spout\Writer\Common\Creator\WriterFactory;
910
use Box\Spout\Writer\CSV\Writer as CsvWriter;
@@ -16,6 +17,7 @@
1617
use Yokai\Batch\Job\Item\ItemWriterInterface;
1718
use Yokai\Batch\Job\JobExecutionAwareInterface;
1819
use Yokai\Batch\Job\JobExecutionAwareTrait;
20+
use Yokai\Batch\Job\Parameters\JobParameterAccessorInterface;
1921

2022
final class FlatFileWriter implements
2123
ItemWriterInterface,
@@ -25,13 +27,18 @@ final class FlatFileWriter implements
2527
{
2628
use JobExecutionAwareTrait;
2729

28-
public const OUTPUT_FILE_PARAMETER = 'outputFile';
30+
private const TYPES = [Type::CSV, Type::XLSX, Type::ODS];
2931

3032
/**
3133
* @var string
3234
*/
3335
private string $type;
3436

37+
/**
38+
* @var JobParameterAccessorInterface
39+
*/
40+
private JobParameterAccessorInterface $filePath;
41+
3542
/**
3643
* @phpstan-var list<string>|null
3744
*/
@@ -47,11 +54,6 @@ final class FlatFileWriter implements
4754
*/
4855
private bool $headersAdded = false;
4956

50-
/**
51-
* @var string|null
52-
*/
53-
private ?string $filePath;
54-
5557
/**
5658
* @phpstan-var array{delimiter?: string, enclosure?: string}
5759
*/
@@ -61,11 +63,19 @@ final class FlatFileWriter implements
6163
* @phpstan-param list<string>|null $headers
6264
* @phpstan-param array{delimiter?: string, enclosure?: string} $options
6365
*/
64-
public function __construct(string $type, array $headers = null, string $filePath = null, array $options = [])
65-
{
66+
public function __construct(
67+
string $type,
68+
JobParameterAccessorInterface $filePath,
69+
array $headers = null,
70+
array $options = []
71+
) {
72+
if (!in_array($type, self::TYPES, true)) {
73+
throw UnexpectedValueException::enum(self::TYPES, $type, 'Invalid type.');
74+
}
75+
6676
$this->type = $type;
67-
$this->headers = $headers;
6877
$this->filePath = $filePath;
78+
$this->headers = $headers;
6979
$this->options = $options;
7080
}
7181

@@ -74,7 +84,7 @@ public function __construct(string $type, array $headers = null, string $filePat
7484
*/
7585
public function initialize(): void
7686
{
77-
$path = $this->getFilePath();
87+
$path = (string)$this->filePath->get($this->jobExecution);
7888
$dir = dirname($path);
7989
if (!@is_dir($dir) && !@mkdir($dir, 0777, true)) {
8090
throw new RuntimeException(
@@ -127,9 +137,4 @@ public function flush(): void
127137
$this->writer = null;
128138
$this->headersAdded = false;
129139
}
130-
131-
protected function getFilePath(): string
132-
{
133-
return $this->filePath ?: (string)$this->jobExecution->getParameter(self::OUTPUT_FILE_PARAMETER);
134-
}
135140
}

src/batch-box-spout/tests/FlatFileReaderTest.php

Lines changed: 98 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,27 +5,32 @@
55
namespace Yokai\Batch\Tests\Bridge\Box\Spout;
66

77
use Box\Spout\Common\Type;
8+
use Generator;
89
use PHPUnit\Framework\TestCase;
910
use Yokai\Batch\Bridge\Box\Spout\FlatFileReader;
11+
use Yokai\Batch\Exception\CannotAccessParameterException;
1012
use Yokai\Batch\Exception\InvalidArgumentException;
11-
use Yokai\Batch\Exception\UndefinedJobParameterException;
13+
use Yokai\Batch\Exception\UnexpectedValueException;
14+
use Yokai\Batch\Job\Parameters\JobExecutionParameterAccessor;
15+
use Yokai\Batch\Job\Parameters\StaticValueParameterAccessor;
1216
use Yokai\Batch\JobExecution;
13-
use Yokai\Batch\JobParameters;
1417

1518
class FlatFileReaderTest extends TestCase
1619
{
1720
/**
1821
* @dataProvider combination
1922
*/
20-
public function testRead(string $type, string $headersMode, ?array $headers, array $expected)
21-
{
22-
$jobExecution = JobExecution::createRoot(
23-
'123456789',
24-
'parent',
25-
null,
26-
new JobParameters([FlatFileReader::SOURCE_FILE_PARAMETER => __DIR__ . '/fixtures/sample.' . $type])
27-
);
28-
$reader = new FlatFileReader($type, [], $headersMode, $headers);
23+
public function testRead(
24+
string $type,
25+
string $headersMode,
26+
?array $headers,
27+
array $expected,
28+
array $options = [],
29+
string $file = null
30+
): void {
31+
$file ??= __DIR__ . '/fixtures/sample.' . $type;
32+
$jobExecution = JobExecution::createRoot('123456789', 'parent');
33+
$reader = new FlatFileReader($type, new StaticValueParameterAccessor($file), $options, $headersMode, $headers);
2934
$reader->setJobExecution($jobExecution);
3035

3136
/** @var \Iterator $got */
@@ -34,37 +39,60 @@ public function testRead(string $type, string $headersMode, ?array $headers, arr
3439
self::assertSame($expected, iterator_to_array($got));
3540
}
3641

42+
public function testInvalidType(): void
43+
{
44+
$this->expectException(UnexpectedValueException::class);
45+
46+
new FlatFileReader('invalid type', new StaticValueParameterAccessor('/path/to/file'));
47+
}
48+
3749
/**
3850
* @dataProvider types
3951
*/
40-
public function testInvalidConstruction(string $type)
52+
public function testInvalidHeadersMode(string $type): void
53+
{
54+
$this->expectException(UnexpectedValueException::class);
55+
56+
new FlatFileReader($type, new StaticValueParameterAccessor('/path/to/file'), [], 'invalid header mode');
57+
}
58+
59+
/**
60+
* @dataProvider types
61+
*/
62+
public function testInvalidHeadersCombineAndHeader(string $type): void
4163
{
4264
$this->expectException(InvalidArgumentException::class);
4365

44-
new FlatFileReader($type, [], FlatFileReader::HEADERS_MODE_COMBINE, ['nom', 'prenom']);
66+
new FlatFileReader(
67+
$type,
68+
new StaticValueParameterAccessor('/path/to/file'),
69+
[],
70+
FlatFileReader::HEADERS_MODE_COMBINE,
71+
['nom', 'prenom']
72+
);
4573
}
4674

4775
/**
4876
* @dataProvider types
4977
*/
50-
public function testMissingFileToRead(string $type)
78+
public function testMissingFileToRead(string $type): void
5179
{
52-
$this->expectException(UndefinedJobParameterException::class);
80+
$this->expectException(CannotAccessParameterException::class);
5381

54-
$reader = new FlatFileReader($type);
82+
$reader = new FlatFileReader($type, new JobExecutionParameterAccessor('undefined'));
5583
$reader->setJobExecution(JobExecution::createRoot('123456789', 'parent'));
5684

5785
iterator_to_array($reader->read());
5886
}
5987

60-
public function types()
88+
public function types(): Generator
6189
{
6290
foreach ([Type::CSV, Type::XLSX, Type::ODS] as $type) {
6391
yield [$type];
6492
}
6593
}
6694

67-
public function combination()
95+
public function combination(): Generator
6896
{
6997
foreach ($this->types() as [$type]) {
7098
yield [
@@ -120,6 +148,58 @@ public function combination()
120148
['prenom' => 'Jack', 'nom' => 'Doe'],
121149
],
122150
];
151+
152+
if ($type === Type::CSV) {
153+
yield [
154+
$type,
155+
FlatFileReader::HEADERS_MODE_COMBINE,
156+
null,
157+
[
158+
['firstName' => 'John', 'lastName' => 'Doe'],
159+
['firstName' => 'Jane', 'lastName' => 'Doe'],
160+
['firstName' => 'Jack', 'lastName' => 'Doe'],
161+
],
162+
['delimiter' => '|'],
163+
__DIR__ . '/fixtures/sample-pipe.csv'
164+
];
165+
yield [
166+
$type,
167+
FlatFileReader::HEADERS_MODE_NONE,
168+
null,
169+
[
170+
['firstName', 'lastName'],
171+
['John', 'Doe'],
172+
['Jane', 'Doe'],
173+
['Jack', 'Doe'],
174+
],
175+
['delimiter' => '|'],
176+
__DIR__ . '/fixtures/sample-pipe.csv'
177+
];
178+
yield [
179+
$type,
180+
FlatFileReader::HEADERS_MODE_SKIP,
181+
null,
182+
[
183+
['John', 'Doe'],
184+
['Jane', 'Doe'],
185+
['Jack', 'Doe'],
186+
],
187+
['delimiter' => '|'],
188+
__DIR__ . '/fixtures/sample-pipe.csv'
189+
];
190+
yield [
191+
$type,
192+
FlatFileReader::HEADERS_MODE_SKIP,
193+
['prenom', 'nom'],
194+
[
195+
['prenom' => 'John', 'nom' => 'Doe'],
196+
['prenom' => 'Jane', 'nom' => 'Doe'],
197+
['prenom' => 'Jack', 'nom' => 'Doe'],
198+
],
199+
['delimiter' => '|'],
200+
__DIR__ . '/fixtures/sample-pipe.csv'
201+
];
202+
}
123203
}
124204
}
125205
}

0 commit comments

Comments
 (0)