diff --git a/src/Activity.php b/src/Activity.php index dec936e..bb230e0 100644 --- a/src/Activity.php +++ b/src/Activity.php @@ -18,8 +18,8 @@ use LimitIterator; use SplFileObject; use Throwable; +use Workflow\Middleware\ActivityMiddleware; use Workflow\Middleware\WithoutOverlappingMiddleware; -use Workflow\Middleware\WorkflowMiddleware; use Workflow\Models\StoredWorkflow; use Workflow\Serializers\Y; @@ -106,7 +106,7 @@ public function middleware() 0, $this->timeout ), - new WorkflowMiddleware(), + new ActivityMiddleware(), ]; } diff --git a/src/ChildWorkflow.php b/src/ChildWorkflow.php new file mode 100644 index 0000000..d2e18ff --- /dev/null +++ b/src/ChildWorkflow.php @@ -0,0 +1,67 @@ +onConnection($connection); + $this->onQueue($queue); + } + + public function handle() + { + $workflow = $this->parentWorkflow->toWorkflow(); + + try { + if ($this->parentWorkflow->logs()->whereIndex($this->index)->exists()) { + $workflow->resume(); + } else { + $workflow->next($this->index, $this->now, $this->storedWorkflow->class, $this->return); + } + } catch (\Spatie\ModelStates\Exceptions\TransitionNotFound) { + if ($workflow->running()) { + $this->release(); + } + } + } + + public function middleware() + { + return [ + new WithoutOverlappingMiddleware($this->parentWorkflow->id, WithoutOverlappingMiddleware::ACTIVITY), + ]; + } +} diff --git a/src/Exception.php b/src/Exception.php index efb7a02..3d7b4cd 100644 --- a/src/Exception.php +++ b/src/Exception.php @@ -4,10 +4,28 @@ namespace Workflow; +use Illuminate\Bus\Queueable; +use Illuminate\Contracts\Queue\ShouldBeEncrypted; +use Illuminate\Contracts\Queue\ShouldQueue; +use Illuminate\Foundation\Bus\Dispatchable; +use Illuminate\Queue\InteractsWithQueue; +use Illuminate\Queue\SerializesModels; +use Workflow\Middleware\WithoutOverlappingMiddleware; use Workflow\Models\StoredWorkflow; -class Exception extends Activity +final class Exception implements ShouldBeEncrypted, ShouldQueue { + use Dispatchable; + use InteractsWithQueue; + use Queueable; + use SerializesModels; + + public $tries = PHP_INT_MAX; + + public $maxExceptions = PHP_INT_MAX; + + public $timeout = 0; + public function __construct( public int $index, public string $now, @@ -24,10 +42,25 @@ public function __construct( public function handle() { - if ($this->storedWorkflow->logs()->whereIndex($this->index)->exists()) { - return; + $workflow = $this->storedWorkflow->toWorkflow(); + + try { + if ($this->storedWorkflow->logs()->whereIndex($this->index)->exists()) { + $workflow->resume(); + } else { + $workflow->next($this->index, $this->now, self::class, $this->exception); + } + } catch (\Spatie\ModelStates\Exceptions\TransitionNotFound) { + if ($workflow->running()) { + $this->release(); + } } + } - return $this->exception; + public function middleware() + { + return [ + new WithoutOverlappingMiddleware($this->storedWorkflow->id, WithoutOverlappingMiddleware::ACTIVITY), + ]; } } diff --git a/src/Middleware/WorkflowMiddleware.php b/src/Middleware/ActivityMiddleware.php similarity index 98% rename from src/Middleware/WorkflowMiddleware.php rename to src/Middleware/ActivityMiddleware.php index 7c69e62..f769722 100644 --- a/src/Middleware/WorkflowMiddleware.php +++ b/src/Middleware/ActivityMiddleware.php @@ -11,7 +11,7 @@ use Workflow\Events\ActivityFailed; use Workflow\Events\ActivityStarted; -final class WorkflowMiddleware +final class ActivityMiddleware { public function handle($job, $next): void { @@ -33,6 +33,7 @@ public function handle($job, $next): void try { $job->storedWorkflow->toWorkflow() ->next($job->index, $job->now, $job::class, $result); + ActivityCompleted::dispatch( $job->storedWorkflow->id, $uuid, @@ -59,6 +60,7 @@ public function handle($job, $next): void 'snippet' => array_slice(iterator_to_array($iterator), 0, 7), ]), now() ->format('Y-m-d\TH:i:s.u\Z')); + throw $throwable; } } diff --git a/src/Workflow.php b/src/Workflow.php index 3d7902c..c82743b 100644 --- a/src/Workflow.php +++ b/src/Workflow.php @@ -226,8 +226,13 @@ public function handle(): void ); if ($parentWorkflow) { - $parentWorkflow->toWorkflow() - ->next($parentWorkflow->pivot->parent_index, $this->now, $this->storedWorkflow->class, $return); + ChildWorkflow::dispatch( + $parentWorkflow->pivot->parent_index, + $this->now, + $this->storedWorkflow, + $return, + $parentWorkflow + ); } } } diff --git a/tests/Unit/Middleware/WorkflowMiddlewareTest.php b/tests/Unit/Middleware/ActivityMiddlewareTest.php similarity index 93% rename from tests/Unit/Middleware/WorkflowMiddlewareTest.php rename to tests/Unit/Middleware/ActivityMiddlewareTest.php index 26037e0..9c387a4 100644 --- a/tests/Unit/Middleware/WorkflowMiddlewareTest.php +++ b/tests/Unit/Middleware/ActivityMiddlewareTest.php @@ -14,14 +14,14 @@ use Workflow\Events\ActivityCompleted; use Workflow\Events\ActivityFailed; use Workflow\Events\ActivityStarted; -use Workflow\Middleware\WorkflowMiddleware; +use Workflow\Middleware\ActivityMiddleware; use Workflow\Models\StoredWorkflow; use Workflow\States\WorkflowCompletedStatus; use Workflow\States\WorkflowRunningStatus; use Workflow\States\WorkflowWaitingStatus; use Workflow\WorkflowStub; -final class WorkflowMiddlewareTest extends TestCase +final class ActivityMiddlewareTest extends TestCase { public function testMiddleware(): void { @@ -42,7 +42,7 @@ public function testMiddleware(): void ->toDateTimeString(); $activity->storedWorkflow = $storedWorkflow; - $middleware = new WorkflowMiddleware(); + $middleware = new ActivityMiddleware(); $middleware->handle($activity, static function ($job) { return true; @@ -72,7 +72,7 @@ public function testAlreadyCompleted(): void ->toDateTimeString(); $activity->storedWorkflow = $storedWorkflow; - $middleware = new WorkflowMiddleware(); + $middleware = new ActivityMiddleware(); $middleware->handle($activity, static function ($job) { return true; @@ -106,7 +106,7 @@ public function testAlreadyRunning(): void ->toDateTimeString(); $activity->storedWorkflow = $storedWorkflow; - $middleware = new WorkflowMiddleware(); + $middleware = new ActivityMiddleware(); $middleware->handle($activity, static function ($job) { return true; @@ -137,7 +137,7 @@ public function testException(): void ->toDateTimeString(); $activity->storedWorkflow = $storedWorkflow; - $middleware = new WorkflowMiddleware(); + $middleware = new ActivityMiddleware(); try { $middleware->handle($activity, static function ($job) { diff --git a/tests/Unit/WorkflowTest.php b/tests/Unit/WorkflowTest.php index 9e81b54..d59b157 100644 --- a/tests/Unit/WorkflowTest.php +++ b/tests/Unit/WorkflowTest.php @@ -24,13 +24,16 @@ public function testException(): void { $exception = new \Exception('test'); $workflow = WorkflowStub::load(WorkflowStub::make(TestWorkflow::class)->id()); + $storedWorkflow = StoredWorkflow::findOrFail($workflow->id()); + $storedWorkflow->arguments = Y::serialize([]); + $storedWorkflow->save(); $activity = new Exception(0, now()->toDateTimeString(), StoredWorkflow::findOrFail( $workflow->id() ), $exception); - $result = $activity->handle(); + $activity->handle(); - $this->assertSame($exception, $result); + $this->assertSame(Exception::class, $storedWorkflow->logs()->first()->class); } public function testExceptionAlreadyLogged(): void @@ -38,6 +41,8 @@ public function testExceptionAlreadyLogged(): void $exception = new \Exception('test'); $workflow = WorkflowStub::load(WorkflowStub::make(TestWorkflow::class)->id()); $storedWorkflow = StoredWorkflow::findOrFail($workflow->id()); + $storedWorkflow->arguments = Y::serialize([]); + $storedWorkflow->save(); $activity = new Exception(0, now()->toDateTimeString(), StoredWorkflow::findOrFail( $workflow->id() ), $exception); @@ -50,9 +55,9 @@ public function testExceptionAlreadyLogged(): void 'result' => Y::serialize($exception), ]); - $result = $activity->handle(); + $activity->handle(); - $this->assertNull($result); + $this->assertSame(1, $storedWorkflow->logs()->count()); } public function testParent(): void @@ -108,4 +113,59 @@ public function testParent(): void $this->assertSame(WorkflowCompletedStatus::class, $childWorkflow->status()); $this->assertSame('other', $childWorkflow->output()); } + + public function testParentPending(): void + { + Carbon::setTestNow('2022-01-01'); + + $parentWorkflow = WorkflowStub::load(WorkflowStub::make(TestParentWorkflow::class)->id()); + + $storedParentWorkflow = StoredWorkflow::findOrFail($parentWorkflow->id()); + $storedParentWorkflow->arguments = Y::serialize([]); + $storedParentWorkflow->status = WorkflowPendingStatus::class; + $storedParentWorkflow->save(); + + $storedParentWorkflow->logs() + ->create([ + 'index' => 0, + 'now' => now(), + 'class' => TestChildWorkflow::class, + 'result' => Y::serialize('child_workflow'), + ]); + + $storedParentWorkflow->logs() + ->create([ + 'index' => 1, + 'now' => now(), + 'class' => TestActivity::class, + 'result' => Y::serialize('activity'), + ]); + + $childWorkflow = WorkflowStub::load(WorkflowStub::make(TestChildWorkflow::class)->id()); + + $storedChildWorkflow = StoredWorkflow::findOrFail($childWorkflow->id()); + $storedChildWorkflow->arguments = Y::serialize([]); + $storedChildWorkflow->status = WorkflowPendingStatus::class; + $storedChildWorkflow->save(); + $storedChildWorkflow->parents() + ->attach($storedParentWorkflow, [ + 'parent_index' => 0, + 'parent_now' => now(), + ]); + + $storedChildWorkflow->logs() + ->create([ + 'index' => 0, + 'now' => now(), + 'class' => TestOtherActivity::class, + 'result' => Y::serialize('other'), + ]); + + (new (TestChildWorkflow::class)($storedChildWorkflow))->handle(); + (new (TestParentWorkflow::class)($storedParentWorkflow))->handle(); + + $this->assertSame('2022-01-01 00:00:00', WorkflowStub::now()->toDateTimeString()); + $this->assertSame(WorkflowCompletedStatus::class, $childWorkflow->status()); + $this->assertSame('other', $childWorkflow->output()); + } }
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: