Skip to content

Commit a40600c

Browse files
AUDULjeremycr
andauthored
Stream exceptions (#87)
* Add streamed exceptions --------- Co-authored-by: Jérémy J. <jeremy@code-rhapsodie.fr>
1 parent 3dc75e4 commit a40600c

30 files changed

Lines changed: 173 additions & 136 deletions

.github/workflows/build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ jobs:
1212
runs-on: ubuntu-latest
1313
permissions: read-all
1414
steps:
15-
- uses: actions/checkout@v2
15+
- uses: actions/checkout@v6
1616
with:
1717
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
1818
- uses: sonarsource/sonarqube-scan-action@master

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ jobs:
77
runs-on: ubuntu-latest
88

99
steps:
10-
- uses: actions/checkout@v4
10+
- uses: actions/checkout@v6
1111
- uses: php-actions/composer@v6 # or alternative dependency management
1212
- uses: php-actions/phpunit@v4
1313
- name: Run PHP CS Fixer

.php-cs-fixer.dist.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
$config->setRules([
1010
'@Symfony' => true,
1111
'@Symfony:risky' => true,
12+
'@PHP8x2Migration:risky' => true,
1213
'@PSR12' => true,
1314
'array_syntax' => [
1415
'syntax' => 'short',

CHANGELOG.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
# Version 6.0.0
2+
* Add managing exceptions with a stream
3+
14
# Version 5.5.0
25
* Added cleanup commands
36
* Updated for PHP 8.2
@@ -6,7 +9,7 @@
69
* Fix File Exceptions integration
710

811
# Version 5.4.0
9-
* Add possibility to save exceptions in file
12+
* Add the possibility to save exceptions in the file
1013

1114
# Version 5.3.1
1215
* Fix interface naming

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ providing an easy way to create import / export dataflow.
55

66
| Dataflow | Symfony | Support |
77
|----------|--------------------------|---------|
8+
| 6.x | ^7.4 | yes |
89
| 5.x | ^7.3 | yes |
910
| 4.x | 3.4 \| 4.x \| 5.x \| 6.x | yes |
1011
| 3.x | 3.4 \| 4.x \| 5.x | no |

Tests/Processor/JobProcessorTest.php

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use CodeRhapsodie\DataflowBundle\Entity\Job;
88
use CodeRhapsodie\DataflowBundle\Event\Events;
99
use CodeRhapsodie\DataflowBundle\Event\ProcessingEvent;
10+
use CodeRhapsodie\DataflowBundle\ExceptionsHandler\ExceptionHandlerInterface;
1011
use CodeRhapsodie\DataflowBundle\Gateway\JobGateway;
1112
use CodeRhapsodie\DataflowBundle\Processor\JobProcessor;
1213
use CodeRhapsodie\DataflowBundle\Registry\DataflowTypeRegistryInterface;
@@ -22,15 +23,17 @@ class JobProcessorTest extends TestCase
2223
private DataflowTypeRegistryInterface|MockObject $registry;
2324
private EventDispatcherInterface|MockObject $dispatcher;
2425
private JobGateway|MockObject $jobGateway;
26+
private ExceptionHandlerInterface|MockObject $exceptionHandler;
2527

2628
protected function setUp(): void
2729
{
2830
$this->repository = $this->createMock(JobRepository::class);
2931
$this->registry = $this->createMock(DataflowTypeRegistryInterface::class);
3032
$this->dispatcher = $this->createMock(EventDispatcherInterface::class);
3133
$this->jobGateway = $this->createMock(JobGateway::class);
34+
$this->exceptionHandler = $this->createMock(ExceptionHandlerInterface::class);
3235

33-
$this->processor = new JobProcessor($this->repository, $this->registry, $this->dispatcher, $this->jobGateway);
36+
$this->processor = new JobProcessor($this->repository, $this->registry, $this->dispatcher, $this->jobGateway, $this->exceptionHandler);
3437
}
3538

3639
public function testProcess()
@@ -64,7 +67,7 @@ public function testProcess()
6467
->willReturn($dataflowType)
6568
;
6669

67-
$bag = [new \Exception('message1')];
70+
$bag = 1;
6871

6972
$result = new Result('name', new \DateTimeImmutable(), $end = new \DateTimeImmutable(), $count = 10, $bag);
7073

@@ -85,6 +88,6 @@ public function testProcess()
8588
$this->assertGreaterThanOrEqual($now, $job->getStartTime());
8689
$this->assertSame(Job::STATUS_COMPLETED, $job->getStatus());
8790
$this->assertSame($end, $job->getEndTime());
88-
$this->assertSame($count - count($bag), $job->getCount());
91+
$this->assertSame($count - $bag, $job->getCount());
8992
}
9093
}

composer.json

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -47,16 +47,16 @@
4747
"doctrine/doctrine-bundle": "^2.0",
4848
"monolog/monolog": "^2.0||^3.0",
4949
"psr/log": "^1.1||^2.0||^3.0",
50-
"symfony/config": "^7.3",
51-
"symfony/console": "^7.3",
52-
"symfony/dependency-injection": "^7.3",
53-
"symfony/event-dispatcher": "^7.3",
54-
"symfony/http-kernel": "^7.3",
55-
"symfony/lock": "^7.3",
56-
"symfony/monolog-bridge": "^7.3",
57-
"symfony/options-resolver": "^7.3",
58-
"symfony/validator": "^7.3",
59-
"symfony/yaml": "^7.3"
50+
"symfony/config": "^7.4",
51+
"symfony/console": "^7.4",
52+
"symfony/dependency-injection": "^7.4",
53+
"symfony/event-dispatcher": "^7.4",
54+
"symfony/http-kernel": "^7.4",
55+
"symfony/lock": "^7.4",
56+
"symfony/monolog-bridge": "^7.4",
57+
"symfony/options-resolver": "^7.4",
58+
"symfony/validator": "^7.4",
59+
"symfony/yaml": "^7.4"
6060
},
6161
"require-dev": {
6262
"amphp/amp": "^2.5",
@@ -65,7 +65,7 @@
6565
"phpunit/phpunit": "^11",
6666
"portphp/portphp": "^1.9",
6767
"rector/rector": "^2.0",
68-
"symfony/messenger": "^7.3"
68+
"symfony/messenger": "^7.4"
6969
},
7070
"suggest": {
7171
"amphp/amp": "Provide asynchronous steps for your dataflows",

src/DataflowType/AbstractDataflowType.php

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,11 @@
55
namespace CodeRhapsodie\DataflowBundle\DataflowType;
66

77
use CodeRhapsodie\DataflowBundle\Repository\JobRepository;
8-
use Psr\Log\LoggerAwareInterface;
98
use Psr\Log\LoggerAwareTrait;
109
use Psr\Log\LoggerInterface;
1110
use Symfony\Component\OptionsResolver\OptionsResolver;
1211

13-
abstract class AbstractDataflowType implements DataflowTypeInterface, LoggerAwareInterface, AutoUpdateCountInterface
12+
abstract class AbstractDataflowType implements DataflowTypeInterface, AutoUpdateCountInterface
1413
{
1514
use LoggerAwareTrait;
1615

@@ -36,7 +35,7 @@ public function process(array $options, ?int $jobId = null): Result
3635

3736
$builder = $this->createDataflowBuilder();
3837
$builder->setName($this->getLabel());
39-
$builder->addAfterItemProcessor(function (int|string $index, mixed $item, int $count) use ($jobId) {
38+
$builder->addAfterItemProcessor(function (int|string $index, mixed $item, int $count) use ($jobId): void {
4039
if ($jobId === null || $this->saveDate > new \DateTime()) {
4140
return;
4241
}
@@ -46,7 +45,7 @@ public function process(array $options, ?int $jobId = null): Result
4645
});
4746
$this->buildDataflow($builder, $options);
4847
$dataflow = $builder->getDataflow();
49-
if ($dataflow instanceof LoggerAwareInterface && $this->logger instanceof LoggerInterface) {
48+
if ($this->logger instanceof LoggerInterface) {
5049
$dataflow->setLogger($this->logger);
5150
}
5251

src/DataflowType/Dataflow/AMPAsyncDataflow.php

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,9 @@
1616

1717
use CodeRhapsodie\DataflowBundle\DataflowType\Result;
1818
use CodeRhapsodie\DataflowBundle\DataflowType\Writer\WriterInterface;
19-
use Psr\Log\LoggerAwareInterface;
2019
use Psr\Log\LoggerAwareTrait;
2120

22-
class AMPAsyncDataflow implements DataflowInterface, LoggerAwareInterface
21+
class AMPAsyncDataflow implements DataflowInterface
2322
{
2423
use LoggerAwareTrait;
2524

@@ -65,7 +64,7 @@ public function addWriter(WriterInterface $writer): self
6564
public function process(): Result
6665
{
6766
$count = 0;
68-
$exceptions = [];
67+
$countExceptions = 0;
6968
$startTime = new \DateTime();
7069

7170
try {
@@ -82,7 +81,7 @@ public function process(): Result
8281
}
8382
});
8483

85-
$watcherId = Loop::repeat($this->loopInterval, function () use ($deferred, &$resolved, $producer, &$count, &$exceptions) {
84+
$watcherId = Loop::repeat($this->loopInterval, function () use ($deferred, &$resolved, $producer, &$count, &$countExceptions) {
8685
if (yield $producer->advance()) {
8786
$it = $producer->getCurrent();
8887
[$index, $item] = $it;
@@ -93,7 +92,7 @@ public function process(): Result
9392
}
9493

9594
foreach ($this->states as $state) {
96-
$this->processState($state, $count, $exceptions);
95+
$this->processState($state, $count, $countExceptions);
9796
}
9897
});
9998

@@ -104,18 +103,14 @@ public function process(): Result
104103
$writer->finish();
105104
}
106105
} catch (\Throwable $e) {
107-
$exceptions[] = $e;
106+
++$countExceptions;
108107
$this->logException($e);
109108
}
110109

111-
return new Result($this->name, $startTime, new \DateTime(), $count, $exceptions);
110+
return new Result($this->name, $startTime, new \DateTime(), $count, $countExceptions);
112111
}
113112

114-
/**
115-
* @param int $count internal count reference
116-
* @param array $exceptions internal exceptions
117-
*/
118-
private function processState(mixed $state, int &$count, array &$exceptions): void
113+
private function processState(mixed $state, int &$count, int &$countExceptions): void
119114
{
120115
[$readIndex, $stepIndex, $item] = $state;
121116
if ($stepIndex < \count($this->steps)) {
@@ -127,9 +122,9 @@ private function processState(mixed $state, int &$count, array &$exceptions): vo
127122
$this->stepsJobs[$stepIndex][$readIndex] = true;
128123
/** @var Promise<void> $promise */
129124
$promise = coroutine($step)($item);
130-
$promise->onResolve(function (?\Throwable $exception = null, $newItem = null) use ($stepIndex, $readIndex, &$exceptions) {
125+
$promise->onResolve(function (?\Throwable $exception = null, $newItem = null) use ($stepIndex, $readIndex, &$countExceptions): void {
131126
if ($exception) {
132-
$exceptions[$stepIndex] = $exception;
127+
++$countExceptions;
133128
$this->logException($exception, (string) $stepIndex);
134129
} elseif ($newItem === false) {
135130
unset($this->states[$readIndex]);

src/DataflowType/Dataflow/Dataflow.php

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,9 @@
66

77
use CodeRhapsodie\DataflowBundle\DataflowType\Result;
88
use CodeRhapsodie\DataflowBundle\DataflowType\Writer\WriterInterface;
9-
use Psr\Log\LoggerAwareInterface;
109
use Psr\Log\LoggerAwareTrait;
1110

12-
class Dataflow implements DataflowInterface, LoggerAwareInterface
11+
class Dataflow implements DataflowInterface
1312
{
1413
use LoggerAwareTrait;
1514

@@ -73,7 +72,7 @@ public function setAfterItemProcessors(array $processors): self
7372
public function process(): Result
7473
{
7574
$count = 0;
76-
$exceptions = [];
75+
$countExceptions = 0;
7776
$startTime = new \DateTime();
7877

7978
try {
@@ -91,10 +90,10 @@ public function process(): Result
9190
$exceptionIndex = (string) ($this->customExceptionIndex)($item, $index);
9291
}
9392
} catch (\Throwable $e2) {
94-
$exceptions[$index] = $e2;
93+
++$countExceptions;
9594
$this->logException($e2, $index);
9695
}
97-
$exceptions[$exceptionIndex] = $e;
96+
++$countExceptions;
9897
$this->logException($e, $exceptionIndex);
9998
}
10099

@@ -109,11 +108,11 @@ public function process(): Result
109108
$writer->finish();
110109
}
111110
} catch (\Throwable $e) {
112-
$exceptions[] = $e;
111+
++$countExceptions;
113112
$this->logException($e);
114113
}
115114

116-
return new Result($this->name, $startTime, new \DateTime(), $count, $exceptions);
115+
return new Result($this->name, $startTime, new \DateTime(), $count, $countExceptions);
117116
}
118117

119118
private function processItem(mixed $item): void

0 commit comments

Comments
 (0)