Dispatch post-trigger automations asynchronously via job
This commit is contained in:
parent
6d3a375add
commit
ab7c7c8bdf
4 changed files with 91 additions and 51 deletions
33
app/Jobs/Automation/DispatchPostTriggerAutomationsJob.php
Normal file
33
app/Jobs/Automation/DispatchPostTriggerAutomationsJob.php
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Jobs\Automation;
|
||||
|
||||
use App\Actions\Automation\Trigger\DispatchPostTriggerAutomations;
|
||||
use App\Enums\Automation\Trigger\Type as TriggerType;
|
||||
use App\Models\Post;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class DispatchPostTriggerAutomationsJob implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public int $tries = 1;
|
||||
|
||||
public function __construct(
|
||||
public Post $post,
|
||||
public TriggerType $triggerType,
|
||||
) {
|
||||
$this->onQueue('automations');
|
||||
}
|
||||
|
||||
public function handle(DispatchPostTriggerAutomations $dispatch): void
|
||||
{
|
||||
$dispatch($this->post, $this->triggerType);
|
||||
}
|
||||
}
|
||||
|
|
@ -4,31 +4,29 @@
|
|||
|
||||
namespace App\Observers;
|
||||
|
||||
use App\Actions\Automation\Trigger\DispatchPostTriggerAutomations;
|
||||
use App\Enums\Automation\Trigger\Type as TriggerType;
|
||||
use App\Enums\Post\Status as PostStatus;
|
||||
use App\Jobs\Automation\DispatchPostTriggerAutomationsJob;
|
||||
use App\Models\Post;
|
||||
|
||||
class PostObserver
|
||||
{
|
||||
public function __construct(private DispatchPostTriggerAutomations $dispatch) {}
|
||||
|
||||
public function saved(Post $post): void
|
||||
{
|
||||
if (! $post->wasChanged('status')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$status = $post->status;
|
||||
|
||||
if ($status === PostStatus::Published) {
|
||||
($this->dispatch)($post, TriggerType::PostPublished);
|
||||
$triggerType = match ($post->status) {
|
||||
PostStatus::Published => TriggerType::PostPublished,
|
||||
PostStatus::Scheduled => TriggerType::PostScheduled,
|
||||
default => null,
|
||||
};
|
||||
|
||||
if ($triggerType === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($status === PostStatus::Scheduled) {
|
||||
($this->dispatch)($post, TriggerType::PostScheduled);
|
||||
}
|
||||
DispatchPostTriggerAutomationsJob::dispatch($post, $triggerType)->afterCommit();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { computed, ref } from 'vue';
|
|||
import { Avatar } from '@/components/ui/avatar';
|
||||
import { getMediaValidationWarning } from '@/composables/useMedia';
|
||||
import { getPlatformLogo } from '@/composables/usePlatformLogo';
|
||||
import { ContentType } from '@/enums/content-type';
|
||||
import { ContentType } from '@/types/content-type';
|
||||
import type { MediaItem } from '@/types/media';
|
||||
|
||||
interface SocialAccount {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,10 @@
|
|||
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Actions\Automation\Trigger\DispatchPostTriggerAutomations;
|
||||
use App\Enums\Automation\Trigger\Type as TriggerType;
|
||||
use App\Enums\Post\Status as PostStatus;
|
||||
use App\Jobs\Automation\DispatchPostTriggerAutomationsJob;
|
||||
use App\Jobs\Automation\ProcessAutomationNode;
|
||||
use App\Models\Automation;
|
||||
use App\Models\AutomationRun;
|
||||
|
|
@ -12,7 +15,42 @@
|
|||
|
||||
beforeEach(fn () => Bus::fake());
|
||||
|
||||
it('dispatches a run when a post becomes published in the workspace', function () {
|
||||
it('dispatches the trigger job when a post becomes published', function () {
|
||||
$post = Post::factory()->create(['status' => PostStatus::Draft]);
|
||||
$post->update(['status' => PostStatus::Published]);
|
||||
|
||||
Bus::assertDispatched(
|
||||
DispatchPostTriggerAutomationsJob::class,
|
||||
fn (DispatchPostTriggerAutomationsJob $job) => $job->post->is($post)
|
||||
&& $job->triggerType === TriggerType::PostPublished,
|
||||
);
|
||||
});
|
||||
|
||||
it('dispatches the trigger job when a post becomes scheduled', function () {
|
||||
$post = Post::factory()->create(['status' => PostStatus::Draft]);
|
||||
$post->update(['status' => PostStatus::Scheduled]);
|
||||
|
||||
Bus::assertDispatched(
|
||||
DispatchPostTriggerAutomationsJob::class,
|
||||
fn (DispatchPostTriggerAutomationsJob $job) => $job->triggerType === TriggerType::PostScheduled,
|
||||
);
|
||||
});
|
||||
|
||||
it('does not dispatch for status changes other than published or scheduled', function () {
|
||||
$post = Post::factory()->create(['status' => PostStatus::Draft]);
|
||||
$post->update(['status' => PostStatus::Publishing]);
|
||||
|
||||
Bus::assertNotDispatched(DispatchPostTriggerAutomationsJob::class);
|
||||
});
|
||||
|
||||
it('does not dispatch when the status did not change', function () {
|
||||
$post = Post::factory()->create(['status' => PostStatus::Draft]);
|
||||
$post->update(['content' => 'edited body']);
|
||||
|
||||
Bus::assertNotDispatched(DispatchPostTriggerAutomationsJob::class);
|
||||
});
|
||||
|
||||
it('creates a run and advances to the next node for a matching active automation', function () {
|
||||
$workspace = Workspace::factory()->create();
|
||||
$automation = Automation::factory()->active()->for($workspace)->create([
|
||||
'nodes' => [
|
||||
|
|
@ -22,8 +60,9 @@
|
|||
'connections' => [['id' => 'e1', 'source' => 'trigger_1', 'target' => 'end_1']],
|
||||
]);
|
||||
|
||||
$post = Post::factory()->for($workspace)->create(['status' => PostStatus::Draft]);
|
||||
$post->update(['status' => PostStatus::Published]);
|
||||
$post = Post::factory()->for($workspace)->create(['status' => PostStatus::Published]);
|
||||
|
||||
app(DispatchPostTriggerAutomations::class)($post, TriggerType::PostPublished);
|
||||
|
||||
$runs = AutomationRun::where('automation_id', $automation->id)->get();
|
||||
expect($runs)->toHaveCount(1);
|
||||
|
|
@ -33,23 +72,7 @@
|
|||
Bus::assertDispatched(ProcessAutomationNode::class);
|
||||
});
|
||||
|
||||
it('dispatches when a post becomes scheduled', function () {
|
||||
$workspace = Workspace::factory()->create();
|
||||
$automation = Automation::factory()->active()->for($workspace)->create([
|
||||
'nodes' => [
|
||||
['id' => 'trigger_1', 'type' => 'trigger', 'position' => ['x' => 0, 'y' => 0], 'data' => ['trigger_type' => 'post_scheduled']],
|
||||
['id' => 'end_1', 'type' => 'end', 'position' => ['x' => 200, 'y' => 0], 'data' => []],
|
||||
],
|
||||
'connections' => [['id' => 'e1', 'source' => 'trigger_1', 'target' => 'end_1']],
|
||||
]);
|
||||
|
||||
$post = Post::factory()->for($workspace)->create(['status' => PostStatus::Draft]);
|
||||
$post->update(['status' => PostStatus::Scheduled]);
|
||||
|
||||
expect(AutomationRun::where('automation_id', $automation->id)->count())->toBe(1);
|
||||
});
|
||||
|
||||
it('does not dispatch for automations in a different workspace', function () {
|
||||
it('does not create a run for automations in a different workspace', function () {
|
||||
$workspaceA = Workspace::factory()->create();
|
||||
$workspaceB = Workspace::factory()->create();
|
||||
|
||||
|
|
@ -61,28 +84,13 @@
|
|||
'connections' => [['id' => 'e1', 'source' => 'trigger_1', 'target' => 'end_1']],
|
||||
]);
|
||||
|
||||
$post = Post::factory()->for($workspaceB)->create(['status' => PostStatus::Draft]);
|
||||
$post->update(['status' => PostStatus::Published]);
|
||||
$post = Post::factory()->for($workspaceB)->create(['status' => PostStatus::Published]);
|
||||
|
||||
app(DispatchPostTriggerAutomations::class)($post, TriggerType::PostPublished);
|
||||
|
||||
expect(AutomationRun::where('automation_id', $automation->id)->count())->toBe(0);
|
||||
});
|
||||
|
||||
it('does not dispatch when status change is not to Published or Scheduled', function () {
|
||||
$workspace = Workspace::factory()->create();
|
||||
Automation::factory()->active()->for($workspace)->create([
|
||||
'nodes' => [
|
||||
['id' => 'trigger_1', 'type' => 'trigger', 'position' => ['x' => 0, 'y' => 0], 'data' => ['trigger_type' => 'post_published']],
|
||||
['id' => 'end_1', 'type' => 'end', 'position' => ['x' => 200, 'y' => 0], 'data' => []],
|
||||
],
|
||||
'connections' => [['id' => 'e1', 'source' => 'trigger_1', 'target' => 'end_1']],
|
||||
]);
|
||||
|
||||
$post = Post::factory()->for($workspace)->create(['status' => PostStatus::Draft]);
|
||||
$post->update(['status' => PostStatus::Publishing]);
|
||||
|
||||
expect(AutomationRun::count())->toBe(0);
|
||||
});
|
||||
|
||||
it('skips paused automations', function () {
|
||||
$workspace = Workspace::factory()->create();
|
||||
Automation::factory()->paused()->for($workspace)->create([
|
||||
|
|
@ -93,8 +101,9 @@
|
|||
'connections' => [['id' => 'e1', 'source' => 'trigger_1', 'target' => 'end_1']],
|
||||
]);
|
||||
|
||||
$post = Post::factory()->for($workspace)->create(['status' => PostStatus::Draft]);
|
||||
$post->update(['status' => PostStatus::Published]);
|
||||
$post = Post::factory()->for($workspace)->create(['status' => PostStatus::Published]);
|
||||
|
||||
app(DispatchPostTriggerAutomations::class)($post, TriggerType::PostPublished);
|
||||
|
||||
expect(AutomationRun::count())->toBe(0);
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue