Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 46 |
|
0.00% |
0 / 14 |
CRAP | |
0.00% |
0 / 1 |
SequentialBackgroundProcess | |
0.00% |
0 / 46 |
|
0.00% |
0 / 14 |
420 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
2 | |||
getPayload | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getErrors | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getId | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getVersion | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getStep | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getRetries | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getStartTime | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getCompletionTime | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getStatus | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getResult | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
provideAllowedMethods | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
cancel | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
runStep | |
0.00% |
0 / 21 |
|
0.00% |
0 / 1 |
30 |
1 | <?php |
2 | namespace Apie\Core\BackgroundProcess; |
3 | |
4 | use Apie\Core\ApieLib; |
5 | use Apie\Core\Attributes\AlwaysDisabled; |
6 | use Apie\Core\Attributes\Context; |
7 | use Apie\Core\Attributes\FakeCount; |
8 | use Apie\Core\Attributes\StaticCheck; |
9 | use Apie\Core\Context\ApieContext; |
10 | use Apie\Core\ContextConstants; |
11 | use Apie\Core\Dto\MessageAndTimestamp; |
12 | use Apie\Core\Entities\EntityInterface; |
13 | use Apie\Core\Entities\EntityWithStatesInterface; |
14 | use Apie\Core\Identifiers\PascalCaseSlug; |
15 | use Apie\Core\Identifiers\Ulid; |
16 | use Apie\Core\Lists\ItemHashmap; |
17 | use Apie\Core\Lists\ItemList; |
18 | use Apie\Core\Lists\MessageAndTimestampList; |
19 | use Apie\Core\Lists\StringList; |
20 | use Apie\Core\ValueObjects\DatabaseText; |
21 | use DateTimeInterface; |
22 | use ReflectionClass; |
23 | use Throwable; |
24 | |
25 | #[FakeCount(0)] |
26 | class SequentialBackgroundProcess implements EntityWithStatesInterface |
27 | { |
28 | private int $version; |
29 | private int $step; |
30 | private int $retries = 0; |
31 | private DateTimeInterface $startTime; |
32 | private ?DateTimeInterface $completionTime = null; |
33 | private DatabaseText $className; |
34 | private BackgroundProcessStatus $status = BackgroundProcessStatus::Active; |
35 | private SequentialBackgroundProcessIdentifier $id; |
36 | private mixed $result = null; |
37 | private MessageAndTimestampList $errors; |
38 | |
39 | #[StaticCheck(new AlwaysDisabled())] |
40 | public function __construct( |
41 | BackgroundProcessDeclaration $backgroundProcessDeclaration, |
42 | private ItemHashmap|ItemList $payload |
43 | ) { |
44 | $this->className = new DatabaseText(get_debug_type($backgroundProcessDeclaration)); |
45 | $this->version = $backgroundProcessDeclaration->getCurrentVersion(); |
46 | $this->step = 0; |
47 | $this->startTime = ApieLib::getPsrClock()->now(); |
48 | $this->id = new SequentialBackgroundProcessIdentifier( |
49 | new PascalCaseSlug((new ReflectionClass($backgroundProcessDeclaration))->getShortName()), |
50 | Ulid::createRandom() |
51 | ); |
52 | $this->errors = new MessageAndTimestampList(); |
53 | } |
54 | |
55 | public function getPayload(): ItemHashmap|ItemList |
56 | { |
57 | return $this->payload; |
58 | } |
59 | |
60 | public function getErrors(): MessageAndTimestampList |
61 | { |
62 | return $this->errors; |
63 | } |
64 | |
65 | public function getId(): SequentialBackgroundProcessIdentifier |
66 | { |
67 | return $this->id; |
68 | } |
69 | |
70 | public function getVersion(): int |
71 | { |
72 | return $this->version; |
73 | } |
74 | |
75 | public function getStep(): int |
76 | { |
77 | return $this->step; |
78 | } |
79 | |
80 | public function getRetries(): int |
81 | { |
82 | return $this->retries; |
83 | } |
84 | |
85 | public function getStartTime(): DateTimeInterface |
86 | { |
87 | return $this->startTime; |
88 | } |
89 | |
90 | public function getCompletionTime(): ?DateTimeInterface |
91 | { |
92 | return $this->completionTime; |
93 | } |
94 | |
95 | public function getStatus(): BackgroundProcessStatus |
96 | { |
97 | return $this->status; |
98 | } |
99 | |
100 | public function getResult(): mixed |
101 | { |
102 | return $this->result; |
103 | } |
104 | |
105 | public function provideAllowedMethods(): StringList |
106 | { |
107 | if ($this->status === BackgroundProcessStatus::Active) { |
108 | return new StringList(['cancel', 'runStep']); |
109 | } |
110 | |
111 | return new StringList([]); |
112 | } |
113 | |
114 | public function cancel(): void |
115 | { |
116 | if ($this->status !== BackgroundProcessStatus::Active) { |
117 | throw new \LogicException('Process ' . $this->id . ' can not be executed!'); |
118 | } |
119 | $this->status = BackgroundProcessStatus::Canceled; |
120 | } |
121 | |
122 | public function runStep(#[Context()] ApieContext $apieContext): void |
123 | { |
124 | if ($this->status !== BackgroundProcessStatus::Active) { |
125 | throw new \LogicException('Process ' . $this->id . ' can not be executed!'); |
126 | } |
127 | $apieContext = $apieContext->withContext(ContextConstants::BACKGROUND_PROCESS, $this->result); |
128 | $maxRetries = 1; |
129 | try { |
130 | $className = $this->className->toNative(); |
131 | $maxRetries = $className::getMaxRetries($this->version); |
132 | $steps = array_values($className::retrieveDeclaration($this->version)); |
133 | if (isset($steps[$this->step])) { |
134 | $this->result = call_user_func($steps[$this->step], $apieContext, $this->payload); |
135 | $this->step++; |
136 | $this->retries = 0; |
137 | } else { |
138 | $this->completionTime = ApieLib::getPsrClock()->now(); |
139 | $this->status = BackgroundProcessStatus::Finished; |
140 | } |
141 | } catch (Throwable $error) { |
142 | $this->errors[] = new MessageAndTimestamp( |
143 | $error->getMessage(), |
144 | ApieLib::getPsrClock()->now() |
145 | ); |
146 | $this->retries++; |
147 | if ($this->retries >= $maxRetries) { |
148 | $this->status = BackgroundProcessStatus::TooManyErrors; |
149 | } |
150 | } |
151 | } |
152 | } |