Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .github/workflows/php.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ jobs:
DB_USERNAME: root
DB_PASSWORD: password
QUEUE_CONNECTION: redis
QUEUE_FAILED_DRIVER: "null"
REDIS_HOST: 127.0.0.1
REDIS_PASSWORD:
REDIS_PORT: 6379
Expand All @@ -90,10 +91,18 @@ jobs:
DB_USERNAME: root
DB_PASSWORD: password
QUEUE_CONNECTION: redis
QUEUE_FAILED_DRIVER: "null"
REDIS_HOST: 127.0.0.1
REDIS_PASSWORD:
REDIS_PORT: 6379

- name: Upload laravel.log if tests fail
if: failure()
uses: actions/upload-artifact@v4
with:
name: laravel-log
path: vendor/orchestra/testbench-core/laravel/storage/logs/laravel.log

- name: Code Coverage
run: |
vendor/bin/phpunit --testdox --coverage-clover=coverage.clover --testsuite unit
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ build
composer.lock
vendor
coverage
coverage.xml
.env
.phpunit.cache
.phpunit.result.cache
Expand Down
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"feature": "phpunit --testdox --testsuite feature",
"unit": "phpunit --testdox --testsuite unit",
"test": "phpunit --testdox",
"coverage": "XDEBUG_MODE=coverage phpunit --testdox --testsuite unit --coverage-clover coverage.xml",
"post-autoload-dump": [
"@clear",
"@prepare"
Expand Down
9 changes: 9 additions & 0 deletions src/ContinuedWorkflow.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

declare(strict_types=1);

namespace Workflow;

final class ContinuedWorkflow
{
}
52 changes: 50 additions & 2 deletions src/Models/StoredWorkflow.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Prunable;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Spatie\ModelStates\HasStates;
use Workflow\States\WorkflowContinuedStatus;
use Workflow\States\WorkflowStatus;
use Workflow\WorkflowStub;

Expand All @@ -18,6 +20,16 @@ class StoredWorkflow extends Model
use HasStates;
use Prunable;

/**
* @var int
*/
public const CONTINUE_PARENT_INDEX = PHP_INT_MAX;

/**
* @var int
*/
public const ACTIVE_WORKFLOW_INDEX = PHP_INT_MAX - 1;

/**
* @var string
*/
Expand Down Expand Up @@ -66,7 +78,7 @@ public function exceptions(): \Illuminate\Database\Eloquent\Relations\HasMany
->orderBy('id');
}

public function parents(): \Illuminate\Database\Eloquent\Relations\BelongsToMany
public function parents(): BelongsToMany
{
return $this->belongsToMany(
config('workflows.stored_workflow_model', self::class),
Expand All @@ -76,7 +88,7 @@ public function parents(): \Illuminate\Database\Eloquent\Relations\BelongsToMany
)->withPivot(['parent_index', 'parent_now']);
}

public function children(): \Illuminate\Database\Eloquent\Relations\BelongsToMany
public function children(): BelongsToMany
{
return $this->belongsToMany(
config('workflows.stored_workflow_model', self::class),
Expand All @@ -86,6 +98,42 @@ public function children(): \Illuminate\Database\Eloquent\Relations\BelongsToMan
)->withPivot(['parent_index', 'parent_now']);
}

public function continuedWorkflows(): BelongsToMany
{
return $this->belongsToMany(
config('workflows.stored_workflow_model', self::class),
config('workflows.workflow_relationships_table', 'workflow_relationships'),
'parent_workflow_id',
'child_workflow_id'
)->wherePivot('parent_index', self::CONTINUE_PARENT_INDEX)
->withPivot(['parent_index', 'parent_now'])
->orderBy('child_workflow_id');
}

public function activeWorkflow(): BelongsToMany
{
return $this->belongsToMany(
config('workflows.stored_workflow_model', self::class),
config('workflows.workflow_relationships_table', 'workflow_relationships'),
'parent_workflow_id',
'child_workflow_id'
)->wherePivot('parent_index', self::ACTIVE_WORKFLOW_INDEX)
->withPivot(['parent_index', 'parent_now'])
->orderBy('child_workflow_id');
}

public function active(): self
{
$active = $this->fresh();

if ($active->status::class === WorkflowContinuedStatus::class) {
$active = $this->activeWorkflow()
->first();
}

return $active;
}

public function prunable(): Builder
{
return static::where('status', 'completed')
Expand Down
10 changes: 10 additions & 0 deletions src/States/WorkflowContinuedStatus.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

declare(strict_types=1);

namespace Workflow\States;

final class WorkflowContinuedStatus extends WorkflowStatus
{
public static string $name = 'continued';
}
1 change: 1 addition & 0 deletions src/States/WorkflowStatus.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public static function config(): StateConfig
->allowTransition(WorkflowPendingStatus::class, WorkflowFailedStatus::class)
->allowTransition(WorkflowPendingStatus::class, WorkflowRunningStatus::class)
->allowTransition(WorkflowRunningStatus::class, WorkflowCompletedStatus::class)
->allowTransition(WorkflowRunningStatus::class, WorkflowContinuedStatus::class)
->allowTransition(WorkflowRunningStatus::class, WorkflowFailedStatus::class)
->allowTransition(WorkflowRunningStatus::class, WorkflowWaitingStatus::class)
->allowTransition(WorkflowWaitingStatus::class, WorkflowFailedStatus::class)
Expand Down
73 changes: 73 additions & 0 deletions src/Traits/Continues.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?php

declare(strict_types=1);

namespace Workflow\Traits;

use React\Promise\PromiseInterface;
use function React\Promise\resolve;
use Workflow\ContinuedWorkflow;
use Workflow\Models\StoredWorkflow;

trait Continues
{
public static function continueAsNew(...$arguments): PromiseInterface
{
$context = self::$context;

if (! $context->replaying) {
$parentWorkflow = $context->storedWorkflow->parents()
->wherePivot('parent_index', '!=', StoredWorkflow::CONTINUE_PARENT_INDEX)
->wherePivot('parent_index', '!=', StoredWorkflow::ACTIVE_WORKFLOW_INDEX)
->withPivot('parent_index')
->first();

$newWorkflow = self::make($context->storedWorkflow->class);

if ($parentWorkflow) {
$parentWorkflow->children()
->attach($newWorkflow->storedWorkflow, [
'parent_index' => $parentWorkflow->pivot->parent_index,
'parent_now' => $context->now,
]);

$parentWorkflow->children()
->wherePivot('parent_index', $parentWorkflow->pivot->parent_index)
->detach($context->storedWorkflow);
}

$newWorkflow->storedWorkflow->parents()
->attach($context->storedWorkflow, [
'parent_index' => StoredWorkflow::CONTINUE_PARENT_INDEX,
'parent_now' => $context->now,
]);

$rootWorkflow = $context->storedWorkflow->parents()
->wherePivot('parent_index', StoredWorkflow::ACTIVE_WORKFLOW_INDEX)->first();

if ($rootWorkflow) {
$rootWorkflow->children()
->attach($newWorkflow->storedWorkflow, [
'parent_index' => StoredWorkflow::ACTIVE_WORKFLOW_INDEX,
'parent_now' => $context->now,
]);

$rootWorkflow->children()
->wherePivot('parent_index', StoredWorkflow::ACTIVE_WORKFLOW_INDEX)
->detach($context->storedWorkflow);
} else {
$context->storedWorkflow->children()
->attach($newWorkflow->storedWorkflow, [
'parent_index' => StoredWorkflow::ACTIVE_WORKFLOW_INDEX,
'parent_now' => $context->now,
]);
}

$newWorkflow->start(...$arguments);
}

self::$context = $context;

return resolve(new ContinuedWorkflow());
}
}
10 changes: 10 additions & 0 deletions src/Workflow.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
use Workflow\Models\StoredWorkflow;
use Workflow\Serializers\Serializer;
use Workflow\States\WorkflowCompletedStatus;
use Workflow\States\WorkflowContinuedStatus;
use Workflow\States\WorkflowRunningStatus;
use Workflow\States\WorkflowWaitingStatus;
use Workflow\Traits\Sagas;
Expand Down Expand Up @@ -79,6 +80,8 @@ public function query($method)
public function middleware()
{
$parentWorkflow = $this->storedWorkflow->parents()
->wherePivot('parent_index', '!=', StoredWorkflow::CONTINUE_PARENT_INDEX)
->wherePivot('parent_index', '!=', StoredWorkflow::ACTIVE_WORKFLOW_INDEX)
->first();

if ($parentWorkflow) {
Expand Down Expand Up @@ -121,6 +124,8 @@ public function handle(): void
}

$parentWorkflow = $this->storedWorkflow->parents()
->wherePivot('parent_index', '!=', StoredWorkflow::CONTINUE_PARENT_INDEX)
->wherePivot('parent_index', '!=', StoredWorkflow::ACTIVE_WORKFLOW_INDEX)
->first();

$log = $this->storedWorkflow->logs()
Expand Down Expand Up @@ -214,6 +219,11 @@ public function handle(): void
throw new Exception('Workflow failed.', 0, $th);
}

if ($return instanceof ContinuedWorkflow) {
$this->storedWorkflow->status->transitionTo(WorkflowContinuedStatus::class);
return;
}

$this->storedWorkflow->output = Serializer::serialize($return);

$this->storedWorkflow->status->transitionTo(WorkflowCompletedStatus::class);
Expand Down
32 changes: 21 additions & 11 deletions src/WorkflowStub.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
use Workflow\States\WorkflowPendingStatus;
use Workflow\Traits\Awaits;
use Workflow\Traits\AwaitWithTimeouts;
use Workflow\Traits\Continues;
use Workflow\Traits\Fakes;
use Workflow\Traits\SideEffects;
use Workflow\Traits\Timers;
Expand All @@ -28,6 +29,7 @@ final class WorkflowStub
{
use Awaits;
use AwaitWithTimeouts;
use Continues;
use Fakes;
use Macroable;
use SideEffects;
Expand All @@ -54,20 +56,22 @@ public function __call($method, $arguments)
->map(static fn ($method) => $method->getName())
->contains($method)
) {
$this->storedWorkflow->signals()
$activeWorkflow = $this->storedWorkflow->active();

$activeWorkflow->signals()
->create([
'method' => $method,
'arguments' => Serializer::serialize($arguments),
]);

$this->storedWorkflow->toWorkflow();
$activeWorkflow->toWorkflow();

if (static::faked()) {
$this->resume();
return;
}

return Signal::dispatch($this->storedWorkflow, self::connection(), self::queue());
return Signal::dispatch($activeWorkflow, self::connection(), self::queue());
}

if (collect((new ReflectionClass($this->storedWorkflow->class))->getMethods())
Expand All @@ -76,9 +80,11 @@ public function __call($method, $arguments)
->map(static fn ($method) => $method->getName())
->contains($method)
) {
return (new $this->storedWorkflow->class(
$this->storedWorkflow,
...Serializer::unserialize($this->storedWorkflow->arguments),
$activeWorkflow = $this->storedWorkflow->active();

return (new $activeWorkflow->class(
$activeWorkflow,
...Serializer::unserialize($activeWorkflow->arguments),
))
->query($method);
}
Expand Down Expand Up @@ -140,21 +146,25 @@ public function id()

public function logs()
{
return $this->storedWorkflow->logs;
return $this->storedWorkflow->active()
->logs;
}

public function exceptions()
{
return $this->storedWorkflow->exceptions;
return $this->storedWorkflow->active()
->exceptions;
}

public function output()
{
if ($this->storedWorkflow->fresh()->output === null) {
$activeWorkflow = $this->storedWorkflow->active();

if ($activeWorkflow->output === null) {
return null;
}

return Serializer::unserialize($this->storedWorkflow->fresh()->output);
return Serializer::unserialize($activeWorkflow->output);
}

public function completed(): bool
Expand All @@ -179,7 +189,7 @@ public function running(): bool

public function status(): string|bool
{
return $this->storedWorkflow->fresh()
return $this->storedWorkflow->active()
->status::class;
}

Expand Down
1 change: 1 addition & 0 deletions tests/.env.feature
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ DB_USERNAME=laravel
DB_PASSWORD=laravel

QUEUE_CONNECTION=redis
QUEUE_FAILED_DRIVER=null

REDIS_HOST=redis
REDIS_PASSWORD=
Expand Down
1 change: 1 addition & 0 deletions tests/.env.unit
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ DB_USERNAME=laravel
DB_PASSWORD=laravel

QUEUE_CONNECTION=sync
QUEUE_FAILED_DRIVER=null

REDIS_HOST=redis
REDIS_PASSWORD=
Expand Down
Loading