From 0da822772fb1c2369c83159ec2e1664c301c7191 Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Sat, 11 Jan 2025 10:39:50 -0600 Subject: [PATCH 01/43] Update failures-and-recovery.md --- docs/failures-and-recovery.md | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/docs/failures-and-recovery.md b/docs/failures-and-recovery.md index 43edcce9..7c4fb27e 100644 --- a/docs/failures-and-recovery.md +++ b/docs/failures-and-recovery.md @@ -41,11 +41,28 @@ class MyWorkflow extends Workflow } ``` +## Non-retryable Exceptions + +In certain cases, you may encounter exceptions that should not be retried. These are referred to as non-retryable exceptions. When an activity throws a non-retryable exception, the workflow will immediately mark the activity as failed and stop retrying. + +```php +use Workflow\Activity; +use Workflow\Exceptions\NonRetryableException; + +class MyNonRetryableActivity extends Activity +{ + public function execute() + { + throw new NonRetryableException('This is a non-retryable error'); + } +} +``` + ## Failing Activities The default value for `$tries` is 0 which means to retry forever. This is because the retry policy includes a backoff function which increases the delay between each retry attempt. This gives you time to fix the error without creating too many attempts. -There are two types of errors that can occur in a activity: recoverable errors and non-recoverable errors. Recoverable errors are temporary and can be resolved without intervention, such as a timeout or temporary network failure. Non-recoverable errors require manual intervention, such as a deployment or code change. +There are two types of failures that can occur in a activity: recoverable failures and non-recoverable failures. Recoverable failures are temporary and can be resolved without intervention, such as a timeout or temporary network failure. Non-recoverable failures require manual intervention, such as a deployment or code change. ## Recovery Process From 95ada4f19515cde6b423e9a15d1b6255835886e2 Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Thu, 20 Feb 2025 20:19:03 -0600 Subject: [PATCH 02/43] Create webhooks.md --- docs/features/webhooks.md | 129 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 docs/features/webhooks.md diff --git a/docs/features/webhooks.md b/docs/features/webhooks.md new file mode 100644 index 00000000..23b48e30 --- /dev/null +++ b/docs/features/webhooks.md @@ -0,0 +1,129 @@ +--- +sidebar_position: 11 +--- + +# Webhooks + +Laravel Workflow provides webhooks that allow external systems to start workflows and send signals dynamically. This feature enables seamless integration with external services, APIs, and automation tools. + +## Enabling Webhooks +To enable webhooks in Laravel Workflow, register the webhook routes in your application’s routes file (`routes/web.php` or `routes/api.php`): + +```php +use Workflow\Webhooks; + +Webhooks::routes(); +``` + +By default, webhooks will: +- Auto-discover workflows in the `app/Workflows` folder. +- Expose webhooks to workflows marked with `#[Webhook]` at the class level. +- Expose webhooks to signal methods marked with `#[Webhook]`. + +## Starting a Workflow via Webhook +To expose a workflow as a webhook, add the `#[Webhook]` attribute on the class itself. + +```php +use Workflow\Webhook; +use Workflow\Workflow; + +#[Webhook] +class OrderWorkflow extends Workflow +{ + public function execute($orderId) + { + // your code here + } +} +``` + +### Webhook URL +``` +POST /webhooks/start/order-workflow +``` + +### Example Request +```bash +curl -X POST "https://example.com/webhooks/start/order-workflow" \ + -H "Content-Type: application/json" \ + -d '{"orderId": 123}' +``` + +## Sending a Signal via Webhook +To allow external systems to send signals to a workflow, add the `#[Webhook]` attribute on the method. + +```php +use Workflow\SignalMethod; +use Workflow\Webhook; +use Workflow\Workflow; + +class OrderWorkflow extends Workflow +{ + protected bool $shipped = false; + + #[SignalMethod] + #[Webhook] + public function markAsShipped() + { + $this->shipped = true; + } +} +``` + +### Webhook URL +``` +POST /webhooks/signal/order-workflow/{workflowId}/mark-as-shipped +``` + +### Example Request +```bash +curl -X POST "https://example.com/webhooks/signal/order-workflow/1/mark-as-shipped" \ + -H "Content-Type: application/json" +``` + +## Webhook Authentication +By default, webhooks don't require authentication but this can be configured in `config/workflows.php`. + +### Authentication Methods +Laravel Workflow supports: +1. No Authentication (none) +2. Token-based Authentication (token) +3. HMAC Signature Verification (signature) + +### Token Authentication +For token authentication, webhooks require a valid API token in the request headers. The default header is `Authorization` but you can change this in the configuration settings. + +#### Example Request +```bash +curl -X POST "https://example.com/webhooks/start/order-workflow" \ + -H "Content-Type: application/json" \ + -H "Authorization: your-api-token" \ + -d '{"orderId": 123}' +``` + +### HMAC Signature Authentication +For HMAC authentication, Laravel Workflow verifies requests using a secret key. The default header is `X-Signature` but this can also be changed. + +#### Example Request +```bash +BODY='{"orderId": 123}' +SIGNATURE=$(echo -n "$BODY" | openssl dgst -sha256 -hmac "your-secret-key" | awk '{print $2}') + +curl -X POST "https://example.com/webhooks/start/order-workflow" \ + -H "Content-Type: application/json" \ + -H "X-Signature: $SIGNATURE" \ + -d "$BODY" +``` + +## Configuring Webhook Routes +By default, webhooks are accessible under `/webhooks`. You can customize the route path in `config/workflows.php`: + +```php +'webhooks_route' => 'api/webhooks', +``` + +After this change, webhooks will be accessible under: +``` +POST /api/webhooks/start/order-workflow +POST /api/webhooks/signal/order-workflow/{workflowId}/mark-as-shipped +``` From 4b95b86d6a7358ba0a878f165d239477f43be99d Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Thu, 20 Feb 2025 20:43:20 -0600 Subject: [PATCH 03/43] Create 2025-02-07-automating-qa-with-playwright-and-laravel-workflow.md --- ...qa-with-playwright-and-laravel-workflow.md | 209 ++++++++++++++++++ 1 file changed, 209 insertions(+) create mode 100644 blog/2025-02-07-automating-qa-with-playwright-and-laravel-workflow.md diff --git a/blog/2025-02-07-automating-qa-with-playwright-and-laravel-workflow.md b/blog/2025-02-07-automating-qa-with-playwright-and-laravel-workflow.md new file mode 100644 index 00000000..133d92d5 --- /dev/null +++ b/blog/2025-02-07-automating-qa-with-playwright-and-laravel-workflow.md @@ -0,0 +1,209 @@ +--- +slug: automating-qa-with-playwright-and-laravel-workflow +title: "Automating QA with Playwright and Laravel Workflow" +authors: + name: Richard + title: Core Team + url: https://github.com/rmcdaniel + image_url: https://github.com/rmcdaniel.png +tags: [playwright, laravel-workflow, automation, qa, testing] +--- + +Automating QA with Playwright and Laravel Workflow + +![captionless image](https://miro.medium.com/v2/resize:fit:1400/format:webp/1*b6eXVs5J3aRNzYAiqnS9Vw.png) + +Have you ever spent hours tracking down a frontend bug that only happens in production? When working with web applications, debugging frontend issues can be challenging. Console errors and unexpected UI behaviors often require careful inspection and reproducible test cases. Wouldn’t it be great if you could automate this process, capture errors, and even record a video of the session for later analysis? + +With **Playwright** and **Laravel Workflow**, you can achieve just that! In this post, I’ll walk you through an automated workflow that: + +* Loads a webpage and captures console errors. +* Records a video of the session. +* Converts the video to an MP4 format for easy sharing. +* Runs seamlessly in a **GitHub Codespace**. + +The Stack +========= + +* **Playwright**: A powerful browser automation tool for testing web applications. +* **Laravel Workflow**: A durable workflow engine for handling long-running, distributed processes. +* **FFmpeg**: Used to convert Playwright’s WebM recordings to MP4 format. + +![captionless image](https://miro.medium.com/v2/resize:fit:1400/format:webp/1*2AcR_sLHGToBWQx-SCSPHA.png) + +# 1. Capturing Errors and Video with Playwright + +The Playwright script automates a browser session, navigates to a given URL, and logs any console errors. It also records a video of the entire session. + +```javascript +import { chromium } from 'playwright'; +import path from 'path'; +import fs from 'fs'; + +(async () => { + const url = process.argv[2]; + const videoDir = path.resolve('./videos'); + + if (!fs.existsSync(videoDir)) { + fs.mkdirSync(videoDir, { recursive: true }); + } + + const browser = await chromium.launch({ args: ['--no-sandbox'] }); + const context = await browser.newContext({ + recordVideo: { dir: videoDir } + }); + + const page = await context.newPage(); + + let errors = []; + + page.on('console', msg => { + if (msg.type() === 'error') { + errors.push(msg.text()); + } + }); + + try { + await page.goto(url, { waitUntil: 'networkidle', timeout: 10000 }); + } catch (error) { + errors.push(`Page load error: ${error.message}`); + } + const video = await page.video().path(); + + await browser.close(); + + console.log(JSON.stringify({ errors, video })); +})(); +``` + +# 2. Running the Workflow + +A Laravel console command (`php artisan app:playwright`) starts the workflow which: + +* Runs the Playwright script and collects errors. +* Converts the video from `.webm` to `.mp4` using FFmpeg. +* Returns the errors and the final video file path. + +```php +namespace App\Console\Commands; + +use App\Workflows\Playwright\CheckConsoleErrorsWorkflow; +use Illuminate\Console\Command; +use Workflow\WorkflowStub; + +class Playwright extends Command +{ + protected $signature = 'app:playwright'; + protected $description = 'Runs a playwright workflow'; + public function handle() + { + $workflow = WorkflowStub::make(CheckConsoleErrorsWorkflow::class); + $workflow->start('https://example.com'); + while ($workflow->running()); + $this->info($workflow->output()['mp4']); + } +} +``` + +# 3. The Workflow + +```php +namespace App\Workflows\Playwright; + +use Workflow\ActivityStub; +use Workflow\Workflow; + +class CheckConsoleErrorsWorkflow extends Workflow +{ + public function execute(string $url) + { + $result = yield ActivityStub::make(CheckConsoleErrorsActivity::class, $url); + $mp4 = yield ActivityStub::make(ConvertVideoActivity::class, $result['video']); + return [ 'errors' => $result['errors'], + 'mp4' => $mp4, + ]; + } +} +``` + +# 4. Running Playwright + +```php +namespace App\Workflows\Playwright; + +use Illuminate\Support\Facades\Process; +use Workflow\Activity; + +class CheckConsoleErrorsActivity extends Activity +{ + public function execute(string $url) + { + $result = Process::run([ + 'node', base_path('playwright-script.js'), $url + ])->throw(); + return json_decode($result->output(), true); + } +} +``` + +# 5. Video Conversion with FFmpeg + +The Playwright recording is stored in WebM format, but we need an MP4 for wider compatibility. Laravel Workflow runs this process asynchronously. + +```php +namespace App\Workflows\Playwright; + +use Illuminate\Support\Facades\Process; +use Workflow\Activity; + +class ConvertVideoActivity extends Activity +{ + public function execute(string $webm) + { + $mp4 = str_replace('.webm', '.mp4', $webm); + Process::run([ + 'ffmpeg', '-i', $webm, '-c:v', 'libx264', '-preset', 'fast', '-crf', '23', '-c:a', 'aac', '-b:a', '128k', $mp4 + ])->throw(); + unlink($webm); + return $mp4; + } +} +``` + +## Try It Out in a GitHub Codespace 🚀 + +You don’t need to set up anything on your local machine. Everything is already configured in the **Laravel Workflow Sample App**. + +# Steps to Run the Playwright Workflow + +* Open the **Laravel Workflow Sample App** on GitHub: [laravel-workflow/sample-app](https://github.com/laravel-workflow/sample-app) +* Click **“Create codespace on main”** to start a pre-configured development environment. + +![captionless image](https://miro.medium.com/v2/resize:fit:1400/format:webp/1*063hPvkrvDQP6gU-VYb0Ug.png) + +* Once the Codespace is ready, run the following commands in the terminal: + +```bash +php artisan migrate +php artisan queue:work +``` + +* Then open a second terminal and run this command: + +```bash +php artisan app:playwright +``` + +That’s it! The workflow will execute, capture console errors, record a video, and convert it to MP4. You can find the video in the videos folder. Take a look at the sample app’s README.md for more information on other workflows and how to view the Waterline UI. + +# Conclusion + +By integrating Playwright with Laravel Workflow, we’ve automated frontend error detection and debugging. This setup allows teams to quickly identify and resolve issues, all while leveraging Laravel’s queue system to run tasks asynchronously. + +## 🔗 **Next Steps:** + +* Check out the [Laravel Workflow repo](https://github.com/laravel-workflow/laravel-workflow) on GitHub. +* Explore more workflows in the [sample app](https://github.com/laravel-workflow/sample-app). +* Join the community and share your workflows! + +Happy automating! 🚀 From 81831103dd7a287f060dbd3ca33bf9eceacf4185 Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Thu, 20 Feb 2025 20:45:24 -0600 Subject: [PATCH 04/43] Update 2025-02-07-automating-qa-with-playwright-and-laravel-workflow.md --- ...-02-07-automating-qa-with-playwright-and-laravel-workflow.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/blog/2025-02-07-automating-qa-with-playwright-and-laravel-workflow.md b/blog/2025-02-07-automating-qa-with-playwright-and-laravel-workflow.md index 133d92d5..02c36c09 100644 --- a/blog/2025-02-07-automating-qa-with-playwright-and-laravel-workflow.md +++ b/blog/2025-02-07-automating-qa-with-playwright-and-laravel-workflow.md @@ -9,8 +9,6 @@ authors: tags: [playwright, laravel-workflow, automation, qa, testing] --- -Automating QA with Playwright and Laravel Workflow - ![captionless image](https://miro.medium.com/v2/resize:fit:1400/format:webp/1*b6eXVs5J3aRNzYAiqnS9Vw.png) Have you ever spent hours tracking down a frontend bug that only happens in production? When working with web applications, debugging frontend issues can be challenging. Console errors and unexpected UI behaviors often require careful inspection and reproducible test cases. Wouldn’t it be great if you could automate this process, capture errors, and even record a video of the session for later analysis? From 7535652fb1ca3da21b5c9db1cca477e78304384a Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Thu, 20 Feb 2025 20:46:54 -0600 Subject: [PATCH 05/43] Update 2025-02-07-automating-qa-with-playwright-and-laravel-workflow.md --- ...omating-qa-with-playwright-and-laravel-workflow.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/blog/2025-02-07-automating-qa-with-playwright-and-laravel-workflow.md b/blog/2025-02-07-automating-qa-with-playwright-and-laravel-workflow.md index 02c36c09..fd036dcc 100644 --- a/blog/2025-02-07-automating-qa-with-playwright-and-laravel-workflow.md +++ b/blog/2025-02-07-automating-qa-with-playwright-and-laravel-workflow.md @@ -92,7 +92,9 @@ use Workflow\WorkflowStub; class Playwright extends Command { protected $signature = 'app:playwright'; + protected $description = 'Runs a playwright workflow'; + public function handle() { $workflow = WorkflowStub::make(CheckConsoleErrorsWorkflow::class); @@ -116,8 +118,11 @@ class CheckConsoleErrorsWorkflow extends Workflow public function execute(string $url) { $result = yield ActivityStub::make(CheckConsoleErrorsActivity::class, $url); + $mp4 = yield ActivityStub::make(ConvertVideoActivity::class, $result['video']); - return [ 'errors' => $result['errors'], + + return [ + 'errors' => $result['errors'], 'mp4' => $mp4, ]; } @@ -139,6 +144,7 @@ class CheckConsoleErrorsActivity extends Activity $result = Process::run([ 'node', base_path('playwright-script.js'), $url ])->throw(); + return json_decode($result->output(), true); } } @@ -159,10 +165,13 @@ class ConvertVideoActivity extends Activity public function execute(string $webm) { $mp4 = str_replace('.webm', '.mp4', $webm); + Process::run([ 'ffmpeg', '-i', $webm, '-c:v', 'libx264', '-preset', 'fast', '-crf', '23', '-c:a', 'aac', '-b:a', '128k', $mp4 ])->throw(); + unlink($webm); + return $mp4; } } From 1d78dbe32a6345cda68b5d7300058e305141f198 Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Thu, 20 Feb 2025 20:51:47 -0600 Subject: [PATCH 06/43] Create 2023-08-18-microservice-communication-with-laravel-workflow.md --- ...ice-communication-with-laravel-workflow.md | 201 ++++++++++++++++++ 1 file changed, 201 insertions(+) create mode 100644 blog/2023-08-18-microservice-communication-with-laravel-workflow.md diff --git a/blog/2023-08-18-microservice-communication-with-laravel-workflow.md b/blog/2023-08-18-microservice-communication-with-laravel-workflow.md new file mode 100644 index 00000000..49a5dfad --- /dev/null +++ b/blog/2023-08-18-microservice-communication-with-laravel-workflow.md @@ -0,0 +1,201 @@ +--- +slug: microservice-communication-with-laravel-workflow +title: "Microservice Communication with Laravel Workflow" +authors: + name: Richard + title: Core Team + url: https://github.com/rmcdaniel + image_url: https://github.com/rmcdaniel.png +tags: [microservices, laravel-workflow, communication, distributed-systems] +--- + +![captionless image](https://miro.medium.com/v2/resize:fit:1400/format:webp/1*nCy08NPtCpERqC09SVBFfg.jpeg) + +In the evolving landscape of microservices, communication has always been a focal point. Microservices can interact in various ways, be it through HTTP/REST calls, using messaging protocols like RabbitMQ or Kafka, or even employing more recent technologies like gRPC. Yet, regardless of the communication method, the goal remains the same: seamless, efficient, and robust interactions. Today, we’ll explore how Laravel Workflow can fit into this picture and optimize the communication between microservices in a unique way. + +## The Challenge + +In a microservices architecture, decoupling is the name of the game. You want each service to have a single responsibility, to be maintainable, and to be independently deployable. Yet, in the world of workflows, this becomes challenging. How do you split a workflow from its activity and yet ensure they communicate seamlessly? + +## Laravel Workflow to the Rescue! + +[Laravel Workflow](https://github.com/laravel-workflow/laravel-workflow) handles the discovery and orchestration for you! With a shared database and queue connection, you can have your workflow in one Laravel app and its activity logic in another. + +### Defining Workflows and Activities + +#### 1. Create a workflow. +```php +use Workflow\ActivityStub; +use Workflow\Workflow; + +class MyWorkflow extends Workflow +{ + public function execute($name) + { + $result = yield ActivityStub::make(MyActivity::class, $name); + return $result; + } +} +``` + +#### 2. Create an activity. +```php +use Workflow\Activity; + +class MyActivity extends Activity +{ + public function execute($name) + { + return "Hello, {$name}!"; + } +} +``` + +#### 3. Run the workflow. +```php +use Workflow\WorkflowStub; + +$workflow = WorkflowStub::make(MyWorkflow::class); +$workflow->start('world'); +while ($workflow->running()); +$workflow->output(); +// Output: 'Hello, world!' +``` + +The workflow will manage the activity and handle any failures, retries, etc. Think of workflows like job chaining on steroids because you can have conditional logic, loops, return a result that can be used in the next activity, and write everything in typical PHP code that is failure tolerant. + +## Balancing Shared and Dedicated Resources + +When working with microservices, it’s common for each service to have its dedicated resources, such as databases, caches, and queues. However, to facilitate communication between workflows and activities across services, a shared connection (like a database or queue) becomes essential. This shared connection acts as a bridge for data and task exchanges while ensuring: + +1. **Isolation**: Dedicated resources prevent cascading failures. +2. **Performance**: Each service can be optimized independently. +3. **Security**: Isolation limits potential attack vectors. + +## Step-By-Step Integration + +### 1. Install `laravel-workflow` in all microservices. +Follow the [installation guide](https://laravel-workflow.com/docs/installation/). + +### 2. Create a shared database/redis connection in all microservices. +```php +// config/database.php +'connections' => [ + 'shared' => [ + 'driver' => 'mysql', + 'host' => env('SHARED_DB_HOST', '127.0.0.1'), + 'database' => env('SHARED_DB_DATABASE', 'forge'), + 'username' => env('SHARED_DB_USERNAME', 'forge'), + 'password' => env('SHARED_DB_PASSWORD', ''), + ], +], +``` + +### 3. Configure a shared queue connection. +```php +// config/queue.php +'connections' => [ + 'shared' => [ + 'driver' => 'redis', + 'connection' => 'shared', + 'queue' => env('SHARED_REDIS_QUEUE', 'default'), + ], +], +``` + +### 4. Ensure only one microservice publishes Laravel Workflow migrations. +Update the migration to use the shared database connection. +```php +// database/migrations/..._create_workflows_table.php +class CreateWorkflowsTable extends Migration +{ + protected $connection = 'shared'; +} +``` + +### 5. Extend workflow models in each microservice to use the shared connection. +```php +// app/Models/StoredWorkflow.php +namespace App\Models; +use Workflow\Models\StoredWorkflow as BaseStoredWorkflow; + +class StoredWorkflow extends BaseStoredWorkflow +{ + protected $connection = 'shared'; +} +``` + +### 6. Publish Laravel Workflow config and update it with shared models. +```sh +php artisan vendor:publish --provider="Workflow\Providers\WorkflowServiceProvider" --tag="config" +``` + +### 7. Set workflows and activities to use the shared queue. +```php +// app/Workflows/MyWorkflow.php +class MyWorkflow extends Workflow +{ + public $connection = 'shared'; + public $queue = 'workflow'; +} +``` +```php +// app/Workflows/MyActivity.php +class MyActivity extends Activity +{ + public $connection = 'shared'; + public $queue = 'activity'; +} +``` + +### 8. Ensure microservices define empty counterparts for workflow and activity classes. +#### In the workflow microservice: +```php +class MyWorkflow extends Workflow +{ + public $connection = 'shared'; + public $queue = 'workflow'; + public function execute($name) + { + yield ActivityStub::make(MyActivity::class, $name); + } +} +class MyActivity extends Activity +{ + public $connection = 'shared'; + public $queue = 'activity'; +} +``` + +#### In the activity microservice: +```php +class MyWorkflow extends Workflow +{ + public $connection = 'shared'; + public $queue = 'workflow'; +} +class MyActivity extends Activity +{ + public $connection = 'shared'; + public $queue = 'activity'; + public function execute($name) + { + return "Hello, {$name}!"; + } +} +``` + +### 9. Ensure all microservices have the same `APP_KEY` in their `.env` file. +This is crucial for proper job serialization across services. + +### 10. Run queue workers in each microservice. +```sh +php artisan queue:work shared --queue=workflow +php artisan queue:work shared --queue=activity +``` + +## Conclusion +By following the steps above, you can ensure seamless interactions between microservices while maintaining modularity and scalability. Laravel Workflow takes care of the discovery and orchestration for you. 🚀 + +Thanks for reading! + From 8c8e40af4624a348def32f3199b69f4a022efd89 Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Thu, 20 Feb 2025 20:54:17 -0600 Subject: [PATCH 07/43] Create 2023-08-20-ai-image-moderation-with-laravel-workflow.md --- ...-image-moderation-with-laravel-workflow.md | 203 ++++++++++++++++++ 1 file changed, 203 insertions(+) create mode 100644 blog/2023-08-20-ai-image-moderation-with-laravel-workflow.md diff --git a/blog/2023-08-20-ai-image-moderation-with-laravel-workflow.md b/blog/2023-08-20-ai-image-moderation-with-laravel-workflow.md new file mode 100644 index 00000000..b6a4434e --- /dev/null +++ b/blog/2023-08-20-ai-image-moderation-with-laravel-workflow.md @@ -0,0 +1,203 @@ +--- +slug: ai-image-moderation-with-laravel-workflow +title: "AI Image Moderation with Laravel Workflow" +authors: + name: Richard + title: Core Team + url: https://github.com/rmcdaniel + image_url: https://github.com/rmcdaniel.png +tags: [ai, image-moderation, laravel-workflow, automation] +--- + +![captionless image](https://miro.medium.com/v2/resize:fit:1400/format:webp/1*Sz-f9McEdB5UIlr55GOjyw.png) + +## Introduction + +Before we begin, let’s understand the scenario. We are building an image moderation system where: + +1. Every image undergoes an initial AI check to determine if it’s safe. +2. If the AI deems the image unsafe, it’s automatically logged and deleted. +3. If it’s potentially safe, a human moderator is alerted to further review the image. They have the option to approve or reject the image. +4. Approved images are moved to a public location, whereas rejected images are deleted. + +## Laravel Workflow + +Laravel Workflow is designed to streamline and organize complex processes in applications. It allows developers to define, manage, and execute workflows seamlessly. You can find installation instructions [here](https://github.com/laravel-workflow/laravel-workflow). + +## ClarifAI API + +ClarifAI provides AI-powered moderation tools for analyzing visual content. They offer a [free plan](https://www.clarifai.com/pricing) with up to 1,000 actions per month. + +### 1. Store your credentials in `.env`. +```ini +CLARIFAI_API_KEY=key +CLARIFAI_APP=my-application +CLARIFAI_WORKFLOW=my-workflow +CLARIFAI_USER=username +``` + +### 2. Add the service to `config/services.php`. +```php +'clarifai' => [ + 'api_key' => env('CLARIFAI_API_KEY'), + 'app' => env('CLARIFAI_APP'), + 'workflow' => env('CLARIFAI_WORKFLOW'), + 'user' => env('CLARIFAI_USER'), +], +``` + +### 3. Create a service at `app/Services/ClarifAI.php`. +```php +namespace App\Services; +use Illuminate\Support\Facades\Http; +class ClarifAI +{ + private $apiKey; + private $apiUrl; + public function __construct() + { + $app = config('services.clarifai.app'); + $workflow = config('services.clarifai.workflow'); + $user = config('services.clarifai.user'); + $this->apiKey = config('services.clarifai.api_key'); + $this->apiUrl = "https://api.clarifai.com/v2/users/{$user}/apps/{$app}/workflows/{$workflow}/results/"; + } + public function checkImage(string $image): bool + { + $response = Http::withToken($this->apiKey, 'Key') + ->post($this->apiUrl, ['inputs' => [ + ['data' => ['image' => ['base64' => base64_encode($image)]]], + ]]); + return collect($response->json('results.0.outputs.0.data.concepts', [])) + ->filter(fn ($value) => $value['name'] === 'safe') + ->map(fn ($value) => round((float) $value['value']) > 0) + ->first() ?? false; + } +} +``` + +## Creating the Workflow + +```php +namespace App\Workflows; +use Workflow\ActivityStub; +use Workflow\SignalMethod; +use Workflow\WorkflowStub; +use Workflow\Workflow; + +class ImageModerationWorkflow extends Workflow +{ + private bool $approved = false; + private bool $rejected = false; + + #[SignalMethod] + public function approve() + { + $this->approved = true; + } + + #[SignalMethod] + public function reject() + { + $this->rejected = true; + } + + public function execute($imagePath) + { + $safe = yield from $this->check($imagePath); + if (! $safe) { + yield from $this->unsafe($imagePath); + return 'unsafe'; + } + yield from $this->moderate($imagePath); + return $this->approved ? 'approved' : 'rejected'; + } + + private function check($imagePath) + { + return yield ActivityStub::make(AutomatedImageCheckActivity::class, $imagePath); + } + + private function unsafe($imagePath) + { + yield ActivityStub::all([ + ActivityStub::make(LogUnsafeImageActivity::class, $imagePath), + ActivityStub::make(DeleteImageActivity::class, $imagePath), + ]); + } + + private function moderate($imagePath) + { + while (true) { + yield ActivityStub::make(NotifyImageModeratorActivity::class, $imagePath); + $signaled = yield WorkflowStub::awaitWithTimeout('24 hours', fn () => $this->approved || $this->rejected); + if ($signaled) break; + } + } +} +``` + +## Activities + +### Automated Image Check +```php +namespace App\Workflows; +use App\Services\ClarifAI; +use Illuminate\Support\Facades\Storage; +use Workflow\Activity; +class AutomatedImageCheckActivity extends Activity +{ + public function execute($imagePath) + { + return app(ClarifAI::class) + ->checkImage(Storage::get($imagePath)); + } +} +``` + +### Logging Unsafe Images +```php +namespace App\Workflows; +use Illuminate\Support\Facades\Log; +use Workflow\Activity; +class LogUnsafeImageActivity extends Activity +{ + public function execute($imagePath) + { + Log::info('Unsafe image detected at: ' . $imagePath); + } +} +``` + +### Deleting Images +```php +namespace App\Workflows; +use Illuminate\Support\Facades\Storage; +use Workflow\Activity; +class DeleteImageActivity extends Activity +{ + public function execute($imagePath) + { + Storage::delete($imagePath); + } +} +``` + +## Starting and Signaling the Workflow +```php +$workflow = WorkflowStub::make(ImageModerationWorkflow::class); +$workflow->start('tmp/good.jpg'); +``` + +For approvals or rejections: +```php +$workflow = WorkflowStub::load($id); +$workflow->approve(); +// or +$workflow->reject(); +``` + +## Conclusion + +[Laravel Workflow](https://github.com/laravel-workflow/laravel-workflow) provides a structured approach to handle complex processes like image moderation. It supports asynchronous processing, external API integrations, and modular design for scalability. Thanks for reading! + From 5b5c354e4bb65344ff0a2c11ec797121964b91bb Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Thu, 20 Feb 2025 20:56:51 -0600 Subject: [PATCH 08/43] Create 2023-08-28-extending-laravel-workflow-to-support-spatie-laravel-tags.md --- ...workflow-to-support-spatie-laravel-tags.md | 125 ++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 blog/2023-08-28-extending-laravel-workflow-to-support-spatie-laravel-tags.md diff --git a/blog/2023-08-28-extending-laravel-workflow-to-support-spatie-laravel-tags.md b/blog/2023-08-28-extending-laravel-workflow-to-support-spatie-laravel-tags.md new file mode 100644 index 00000000..350d57c8 --- /dev/null +++ b/blog/2023-08-28-extending-laravel-workflow-to-support-spatie-laravel-tags.md @@ -0,0 +1,125 @@ +--- +slug: extending-laravel-workflow-to-support-spatie-laravel-tags +title: "Extending Laravel Workflow to Support Spatie Laravel Tags" +authors: + name: Richard + title: Core Team + url: https://github.com/rmcdaniel + image_url: https://github.com/rmcdaniel.png +tags: [laravel, workflow, spatie, tags, automation] +--- + +![captionless image](https://miro.medium.com/v2/resize:fit:1400/format:webp/1*4YFhkvL6nZ3ny4NjGe6sMQ.png) + +## One of the strengths of the Laravel ecosystem is its flexibility, thanks to a myriad of community-driven packages that enhance the framework’s capabilities. The `laravel-workflow` and `spatie/laravel-tags` packages are two such examples, and in this post, we'll integrate them together to make workflows taggable. + +## Installation Instructions + +Before diving into the code, let’s ensure both libraries are properly installed: + +1. Install [Laravel Workflow](https://github.com/laravel-workflow/laravel-workflow) and [Spatie Laravel Tags](https://github.com/spatie/laravel-tags). +```sh +composer require laravel-workflow/laravel-workflow spatie/laravel-tags +``` + +2. Both packages include migrations that must be published. +```sh +php artisan vendor:publish --provider="Workflow\Providers\WorkflowServiceProvider" --tag="migrations" +php artisan vendor:publish --provider="Spatie\Tags\TagsServiceProvider" --tag="tags-migrations" +``` + +3. Run the migrations. +```sh +php artisan migrate +``` + +## Publishing Configuration + +To extend Laravel Workflow, publish its configuration file: +```sh +php artisan vendor:publish --provider="Workflow\Providers\WorkflowServiceProvider" --tag="config" +``` + +## Extending Workflows to Support Tags + +We need to extend the `StoredWorkflow` model of `laravel-workflow` to support tagging. + +```php +namespace App\Models; + +use Spatie\Tags\HasTags; +use Workflow\Models\StoredWorkflow as BaseStoredWorkflow; +use Workflow\WorkflowStub; + +class StoredWorkflow extends BaseStoredWorkflow +{ + use HasTags; + + public static function tag(WorkflowStub $workflow, $tag): void + { + $storedWorkflow = static::find($workflow->id()); + if ($storedWorkflow) { + $storedWorkflow->attachTag($tag); + } + } + + public static function findByTag($tag): ?WorkflowStub + { + $storedWorkflow = static::withAnyTags([$tag])->first(); + if ($storedWorkflow) { + return WorkflowStub::fromStoredWorkflow($storedWorkflow); + } + } +} +``` + +## Modify the Configuration + +In `config/workflow.php`, update this line: +```php +'stored_workflow_model' => Workflow\Models\StoredWorkflow::class, +``` +To: +```php +'stored_workflow_model' => App\Models\StoredWorkflow::class, +``` +This ensures Laravel Workflow uses the extended model. + +## Running Tagged Workflows + +With the taggable `StoredWorkflow` ready, create a console command to create, tag, retrieve, and run a workflow. + +```php +namespace App\Console\Commands; + +use App\Models\StoredWorkflow; +use App\Workflows\Simple\SimpleWorkflow; +use Illuminate\Console\Command; +use Workflow\WorkflowStub; + +class Workflow extends Command +{ + protected $signature = 'workflow'; + + protected $description = 'Runs a workflow'; + + public function handle() + { + // Create a workflow and tag it + $workflow = WorkflowStub::make(SimpleWorkflow::class); + StoredWorkflow::tag($workflow, 'tag1'); + + // Find the workflow by tag and start it + $workflow = StoredWorkflow::findByTag('tag1'); + $workflow->start(); + + while ($workflow->running()); + + $this->info($workflow->output()); + } +} +``` + +## Conclusion + +By integrating `laravel-workflow` with `spatie/laravel-tags`, we've enabled tagging for workflows, making management more intuitive in larger applications. Thanks to Laravel’s extensible nature, endless possibilities await developers leveraging these powerful packages. From d8586d6b7e33920f63c8d4b0717f68fe4bc94dfa Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Thu, 20 Feb 2025 20:58:07 -0600 Subject: [PATCH 09/43] Update 2023-08-20-ai-image-moderation-with-laravel-workflow.md --- ...ai-image-moderation-with-laravel-workflow.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/blog/2023-08-20-ai-image-moderation-with-laravel-workflow.md b/blog/2023-08-20-ai-image-moderation-with-laravel-workflow.md index b6a4434e..246619b4 100644 --- a/blog/2023-08-20-ai-image-moderation-with-laravel-workflow.md +++ b/blog/2023-08-20-ai-image-moderation-with-laravel-workflow.md @@ -49,11 +49,14 @@ CLARIFAI_USER=username ### 3. Create a service at `app/Services/ClarifAI.php`. ```php namespace App\Services; + use Illuminate\Support\Facades\Http; + class ClarifAI { private $apiKey; private $apiUrl; + public function __construct() { $app = config('services.clarifai.app'); @@ -62,12 +65,14 @@ class ClarifAI $this->apiKey = config('services.clarifai.api_key'); $this->apiUrl = "https://api.clarifai.com/v2/users/{$user}/apps/{$app}/workflows/{$workflow}/results/"; } + public function checkImage(string $image): bool { $response = Http::withToken($this->apiKey, 'Key') ->post($this->apiUrl, ['inputs' => [ ['data' => ['image' => ['base64' => base64_encode($image)]]], ]]); + return collect($response->json('results.0.outputs.0.data.concepts', [])) ->filter(fn ($value) => $value['name'] === 'safe') ->map(fn ($value) => round((float) $value['value']) > 0) @@ -80,6 +85,7 @@ class ClarifAI ```php namespace App\Workflows; + use Workflow\ActivityStub; use Workflow\SignalMethod; use Workflow\WorkflowStub; @@ -105,11 +111,14 @@ class ImageModerationWorkflow extends Workflow public function execute($imagePath) { $safe = yield from $this->check($imagePath); + if (! $safe) { yield from $this->unsafe($imagePath); return 'unsafe'; } + yield from $this->moderate($imagePath); + return $this->approved ? 'approved' : 'rejected'; } @@ -130,7 +139,9 @@ class ImageModerationWorkflow extends Workflow { while (true) { yield ActivityStub::make(NotifyImageModeratorActivity::class, $imagePath); + $signaled = yield WorkflowStub::awaitWithTimeout('24 hours', fn () => $this->approved || $this->rejected); + if ($signaled) break; } } @@ -142,9 +153,11 @@ class ImageModerationWorkflow extends Workflow ### Automated Image Check ```php namespace App\Workflows; + use App\Services\ClarifAI; use Illuminate\Support\Facades\Storage; use Workflow\Activity; + class AutomatedImageCheckActivity extends Activity { public function execute($imagePath) @@ -158,8 +171,10 @@ class AutomatedImageCheckActivity extends Activity ### Logging Unsafe Images ```php namespace App\Workflows; + use Illuminate\Support\Facades\Log; use Workflow\Activity; + class LogUnsafeImageActivity extends Activity { public function execute($imagePath) @@ -172,8 +187,10 @@ class LogUnsafeImageActivity extends Activity ### Deleting Images ```php namespace App\Workflows; + use Illuminate\Support\Facades\Storage; use Workflow\Activity; + class DeleteImageActivity extends Activity { public function execute($imagePath) From 9d39dcff0b0dc5984c15a7d32f393b1931cff818 Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Thu, 20 Feb 2025 20:58:47 -0600 Subject: [PATCH 10/43] Update 2023-08-18-microservice-communication-with-laravel-workflow.md --- ...3-08-18-microservice-communication-with-laravel-workflow.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/blog/2023-08-18-microservice-communication-with-laravel-workflow.md b/blog/2023-08-18-microservice-communication-with-laravel-workflow.md index 49a5dfad..608d20e8 100644 --- a/blog/2023-08-18-microservice-communication-with-laravel-workflow.md +++ b/blog/2023-08-18-microservice-communication-with-laravel-workflow.md @@ -117,6 +117,7 @@ class CreateWorkflowsTable extends Migration ```php // app/Models/StoredWorkflow.php namespace App\Models; + use Workflow\Models\StoredWorkflow as BaseStoredWorkflow; class StoredWorkflow extends BaseStoredWorkflow @@ -155,6 +156,7 @@ class MyWorkflow extends Workflow { public $connection = 'shared'; public $queue = 'workflow'; + public function execute($name) { yield ActivityStub::make(MyActivity::class, $name); @@ -178,6 +180,7 @@ class MyActivity extends Activity { public $connection = 'shared'; public $queue = 'activity'; + public function execute($name) { return "Hello, {$name}!"; From 64ca2ec9af7bbe7beda84456c5d4db3b5e305a1b Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Thu, 20 Feb 2025 20:59:53 -0600 Subject: [PATCH 11/43] Update 2023-08-28-extending-laravel-workflow-to-support-spatie-laravel-tags.md --- ...extending-laravel-workflow-to-support-spatie-laravel-tags.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blog/2023-08-28-extending-laravel-workflow-to-support-spatie-laravel-tags.md b/blog/2023-08-28-extending-laravel-workflow-to-support-spatie-laravel-tags.md index 350d57c8..de2d5a97 100644 --- a/blog/2023-08-28-extending-laravel-workflow-to-support-spatie-laravel-tags.md +++ b/blog/2023-08-28-extending-laravel-workflow-to-support-spatie-laravel-tags.md @@ -11,7 +11,7 @@ tags: [laravel, workflow, spatie, tags, automation] ![captionless image](https://miro.medium.com/v2/resize:fit:1400/format:webp/1*4YFhkvL6nZ3ny4NjGe6sMQ.png) -## One of the strengths of the Laravel ecosystem is its flexibility, thanks to a myriad of community-driven packages that enhance the framework’s capabilities. The `laravel-workflow` and `spatie/laravel-tags` packages are two such examples, and in this post, we'll integrate them together to make workflows taggable. +One of the strengths of the Laravel ecosystem is its flexibility, thanks to a myriad of community-driven packages that enhance the framework’s capabilities. The `laravel-workflow` and `spatie/laravel-tags` packages are two such examples, and in this post, we'll integrate them together to make workflows taggable. ## Installation Instructions From 948b3d812c07f5bc11303e0ee8a8f2d2ee7fcfb9 Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Thu, 20 Feb 2025 21:01:52 -0600 Subject: [PATCH 12/43] Update 2025-02-07-automating-qa-with-playwright-and-laravel-workflow.md --- ...2-07-automating-qa-with-playwright-and-laravel-workflow.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blog/2025-02-07-automating-qa-with-playwright-and-laravel-workflow.md b/blog/2025-02-07-automating-qa-with-playwright-and-laravel-workflow.md index fd036dcc..4bc33f43 100644 --- a/blog/2025-02-07-automating-qa-with-playwright-and-laravel-workflow.md +++ b/blog/2025-02-07-automating-qa-with-playwright-and-laravel-workflow.md @@ -177,7 +177,7 @@ class ConvertVideoActivity extends Activity } ``` -## Try It Out in a GitHub Codespace 🚀 +## 🚀 Try It Out in a GitHub Codespace You don’t need to set up anything on your local machine. Everything is already configured in the **Laravel Workflow Sample App**. @@ -207,7 +207,7 @@ That’s it! The workflow will execute, capture console errors, record a video, By integrating Playwright with Laravel Workflow, we’ve automated frontend error detection and debugging. This setup allows teams to quickly identify and resolve issues, all while leveraging Laravel’s queue system to run tasks asynchronously. -## 🔗 **Next Steps:** +## 🔗 **Next Steps** * Check out the [Laravel Workflow repo](https://github.com/laravel-workflow/laravel-workflow) on GitHub. * Explore more workflows in the [sample app](https://github.com/laravel-workflow/sample-app). From 84af73a4ae6616187557aa42256fe8d84b813919 Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Thu, 20 Feb 2025 21:03:12 -0600 Subject: [PATCH 13/43] Update 2025-02-07-automating-qa-with-playwright-and-laravel-workflow.md --- ...-02-07-automating-qa-with-playwright-and-laravel-workflow.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blog/2025-02-07-automating-qa-with-playwright-and-laravel-workflow.md b/blog/2025-02-07-automating-qa-with-playwright-and-laravel-workflow.md index 4bc33f43..d43bc8ab 100644 --- a/blog/2025-02-07-automating-qa-with-playwright-and-laravel-workflow.md +++ b/blog/2025-02-07-automating-qa-with-playwright-and-laravel-workflow.md @@ -6,7 +6,7 @@ authors: title: Core Team url: https://github.com/rmcdaniel image_url: https://github.com/rmcdaniel.png -tags: [playwright, laravel-workflow, automation, qa, testing] +tags: [playwright, workflow, automation, qa, testing] --- ![captionless image](https://miro.medium.com/v2/resize:fit:1400/format:webp/1*b6eXVs5J3aRNzYAiqnS9Vw.png) From 38d4ad6918dc5f8bccc56c484d9a5f77758a17ff Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Thu, 20 Feb 2025 21:03:25 -0600 Subject: [PATCH 14/43] Update 2023-08-20-ai-image-moderation-with-laravel-workflow.md --- blog/2023-08-20-ai-image-moderation-with-laravel-workflow.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blog/2023-08-20-ai-image-moderation-with-laravel-workflow.md b/blog/2023-08-20-ai-image-moderation-with-laravel-workflow.md index 246619b4..031149fc 100644 --- a/blog/2023-08-20-ai-image-moderation-with-laravel-workflow.md +++ b/blog/2023-08-20-ai-image-moderation-with-laravel-workflow.md @@ -6,7 +6,7 @@ authors: title: Core Team url: https://github.com/rmcdaniel image_url: https://github.com/rmcdaniel.png -tags: [ai, image-moderation, laravel-workflow, automation] +tags: [ai, image-moderation, workflow, automation] --- ![captionless image](https://miro.medium.com/v2/resize:fit:1400/format:webp/1*Sz-f9McEdB5UIlr55GOjyw.png) From 02a8b901d20a27dc62592ad37598611a60aa1104 Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Thu, 20 Feb 2025 21:03:37 -0600 Subject: [PATCH 15/43] Update 2023-08-18-microservice-communication-with-laravel-workflow.md --- ...23-08-18-microservice-communication-with-laravel-workflow.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blog/2023-08-18-microservice-communication-with-laravel-workflow.md b/blog/2023-08-18-microservice-communication-with-laravel-workflow.md index 608d20e8..d29d0f6e 100644 --- a/blog/2023-08-18-microservice-communication-with-laravel-workflow.md +++ b/blog/2023-08-18-microservice-communication-with-laravel-workflow.md @@ -6,7 +6,7 @@ authors: title: Core Team url: https://github.com/rmcdaniel image_url: https://github.com/rmcdaniel.png -tags: [microservices, laravel-workflow, communication, distributed-systems] +tags: [microservices, workflow, communication, distributed-systems] --- ![captionless image](https://miro.medium.com/v2/resize:fit:1400/format:webp/1*nCy08NPtCpERqC09SVBFfg.jpeg) From ab896c2c3cb664dedb271156e515ac8d3e7d2431 Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Thu, 20 Feb 2025 22:09:55 -0600 Subject: [PATCH 16/43] Update publishing-config.md --- docs/configuration/publishing-config.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/docs/configuration/publishing-config.md b/docs/configuration/publishing-config.md index 542f0421..81e1d35e 100644 --- a/docs/configuration/publishing-config.md +++ b/docs/configuration/publishing-config.md @@ -56,3 +56,20 @@ It should now look like this. ```php 'base_model' => MongoDB\Laravel\Eloquent\Model::class, ``` + +## Changing Serializer + +This setting allows you to optionally use the Base64 serializer instead of Y (kind of like yEnc encoding where it only gets rid of null bytes). The tradeoff is between speed and size. Base64 is faster but adds more overhead. Y is slower but a lot smaller. If you change this it will only affect new workflows and old workflows will revert to whatever they were encoded with to ensure compatibility. + +The default serializer setting in `workflows.php` is: + +```php +'serializer' => Workflow\Serializers\Y::class, +``` + +To use Base64 instead, update it to: + +```php +'serializer' => Workflow\Serializers\Base64::class, +``` + From bd4fbddade435abc04f51f8a594d357066bec8fb Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Thu, 20 Feb 2025 23:39:43 -0600 Subject: [PATCH 17/43] Update webhooks.md --- docs/features/webhooks.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/docs/features/webhooks.md b/docs/features/webhooks.md index 23b48e30..479d68f4 100644 --- a/docs/features/webhooks.md +++ b/docs/features/webhooks.md @@ -81,6 +81,37 @@ curl -X POST "https://example.com/webhooks/signal/order-workflow/1/mark-as-shipp -H "Content-Type: application/json" ``` +## Webhook URL Helper +The `$this->webhookUrl()` helper generates webhook URLs for starting workflows or sending signals. + +``` +$activity->webhookUrl(); +$activity->webhookUrl('signalMethod'); +``` + +Parameters +- string $signalMethod = '' (optional) + +If empty, returns the URL for starting the workflow. + +If provided, returns the URL for sending a signal to an active workflow instance. + +``` +use Workflow\Activity; + +class ShipOrderActivity extends Activity +{ + public function execute(string $email, StoredWorkflow $storedWorkflow): void + { + $startUrl = $this->webhookUrl(); + // $startUrl = '/webhooks/start/order-workflow'; + + $signalUrl = $this->webhookUrl('markAsShipped'); + // $signalUrl = '/webhooks/signal/order-workflow/{workflowId}/mark-as-shipped'; + } +} +``` + ## Webhook Authentication By default, webhooks don't require authentication but this can be configured in `config/workflows.php`. From 7bef7f6bcb6626b43b066590d5c465726e1564a9 Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Fri, 21 Feb 2025 00:06:10 -0600 Subject: [PATCH 18/43] Update webhooks.md --- docs/features/webhooks.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/features/webhooks.md b/docs/features/webhooks.md index 479d68f4..35a672c7 100644 --- a/docs/features/webhooks.md +++ b/docs/features/webhooks.md @@ -85,8 +85,8 @@ curl -X POST "https://example.com/webhooks/signal/order-workflow/1/mark-as-shipp The `$this->webhookUrl()` helper generates webhook URLs for starting workflows or sending signals. ``` -$activity->webhookUrl(); -$activity->webhookUrl('signalMethod'); +$this->webhookUrl(); +$this->webhookUrl('signalMethod'); ``` Parameters From 4b1fbf628d10d25d1434f1a9b24c6961e7109951 Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Fri, 21 Feb 2025 10:52:16 -0600 Subject: [PATCH 19/43] Update webhooks.md --- docs/features/webhooks.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/features/webhooks.md b/docs/features/webhooks.md index 35a672c7..9677223c 100644 --- a/docs/features/webhooks.md +++ b/docs/features/webhooks.md @@ -150,11 +150,11 @@ curl -X POST "https://example.com/webhooks/start/order-workflow" \ By default, webhooks are accessible under `/webhooks`. You can customize the route path in `config/workflows.php`: ```php -'webhooks_route' => 'api/webhooks', +'webhooks_route' => 'workflows', ``` After this change, webhooks will be accessible under: ``` -POST /api/webhooks/start/order-workflow -POST /api/webhooks/signal/order-workflow/{workflowId}/mark-as-shipped +POST /workflows/start/order-workflow +POST /workflows/signal/order-workflow/{workflowId}/mark-as-shipped ``` From fa7cf547e72321bf6406df2961ba85eb5c4dc84f Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Sat, 22 Feb 2025 20:08:31 -0600 Subject: [PATCH 20/43] Update timers.md --- docs/features/timers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/features/timers.md b/docs/features/timers.md index 717d98cb..3894d17f 100644 --- a/docs/features/timers.md +++ b/docs/features/timers.md @@ -29,4 +29,4 @@ class MyWorkflow extends Workflow You may also specify the time to wait as a string e.g. '5 seconds' or '30 minutes'. -**Important:** When using timers, you should not use the `Carbon::now()` method to get the current time. Instead, you should use the `WorkflowStub::now()` method, which will return the current time as seen by the workflow system. This is important because the actual time may not match the time as seen by your application. +**Important:** When using timers, you should not use the `Carbon::now()` method to get the current time. Instead, you should use the `WorkflowStub::now()` method, which will return the current time as seen by the workflow system. This is important because the actual time may not match the time as seen by your application. When comparing timestamps in workflows (e.g., timing how long an operation takes), always use `WorkflowStub::sideEffect(fn () => WorkflowStub::now())` instead of directly calling `WorkflowStub::now()`. This ensures the timestamp remains consistent across workflow replay. From 86d9436dd76d875fb85e3804617696ae34f2a68a Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Sat, 22 Feb 2025 20:17:27 -0600 Subject: [PATCH 21/43] Update timers.md --- docs/features/timers.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/features/timers.md b/docs/features/timers.md index 3894d17f..c987a147 100644 --- a/docs/features/timers.md +++ b/docs/features/timers.md @@ -29,4 +29,12 @@ class MyWorkflow extends Workflow You may also specify the time to wait as a string e.g. '5 seconds' or '30 minutes'. -**Important:** When using timers, you should not use the `Carbon::now()` method to get the current time. Instead, you should use the `WorkflowStub::now()` method, which will return the current time as seen by the workflow system. This is important because the actual time may not match the time as seen by your application. When comparing timestamps in workflows (e.g., timing how long an operation takes), always use `WorkflowStub::sideEffect(fn () => WorkflowStub::now())` instead of directly calling `WorkflowStub::now()`. This ensures the timestamp remains consistent across workflow replay. +**Important:** When using timers, do not use `Carbon::now()` to get the current time. Instead, use `WorkflowStub::now()`, which returns the current time as seen by the workflow system. This is crucial because the actual time may not match your application's system clock. + +Additionally, when measuring elapsed time in workflows (e.g., tracking how long an operation takes), always get your start time with: + +```php +$start = yield WorkflowStub::sideEffect(fn () => WorkflowStub::now()); +``` + +This ensures a checkpoint is created, preventing replay-related inconsistencies and guaranteeing accurate time calculations. From af2c2618faef3839d0ba8ee0ef4053f87241ef6c Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Sat, 22 Feb 2025 20:21:29 -0600 Subject: [PATCH 22/43] Update timers.md --- docs/features/timers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/features/timers.md b/docs/features/timers.md index c987a147..bf54a4ce 100644 --- a/docs/features/timers.md +++ b/docs/features/timers.md @@ -31,7 +31,7 @@ You may also specify the time to wait as a string e.g. '5 seconds' or '30 minute **Important:** When using timers, do not use `Carbon::now()` to get the current time. Instead, use `WorkflowStub::now()`, which returns the current time as seen by the workflow system. This is crucial because the actual time may not match your application's system clock. -Additionally, when measuring elapsed time in workflows (e.g., tracking how long an operation takes), always get your start time with: +Additionally, when measuring elapsed time in workflows (e.g., tracking how long an activity takes), always get your start time with: ```php $start = yield WorkflowStub::sideEffect(fn () => WorkflowStub::now()); From df2936b72d3dc40abab55c3be75feca7cd15d3b1 Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Sun, 23 Feb 2025 07:17:23 -0600 Subject: [PATCH 23/43] Update timers.md --- docs/features/timers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/features/timers.md b/docs/features/timers.md index bf54a4ce..178600bf 100644 --- a/docs/features/timers.md +++ b/docs/features/timers.md @@ -37,4 +37,4 @@ Additionally, when measuring elapsed time in workflows (e.g., tracking how long $start = yield WorkflowStub::sideEffect(fn () => WorkflowStub::now()); ``` -This ensures a checkpoint is created, preventing replay-related inconsistencies and guaranteeing accurate time calculations. +This ensures an event log is created, preventing replay-related inconsistencies and guaranteeing accurate time calculations. From 3b2dd10623238e265878228cfe05ae7257d87579 Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Sun, 23 Feb 2025 07:48:39 -0600 Subject: [PATCH 24/43] Update webhooks.md --- docs/features/webhooks.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/features/webhooks.md b/docs/features/webhooks.md index 9677223c..97d79627 100644 --- a/docs/features/webhooks.md +++ b/docs/features/webhooks.md @@ -101,7 +101,7 @@ use Workflow\Activity; class ShipOrderActivity extends Activity { - public function execute(string $email, StoredWorkflow $storedWorkflow): void + public function execute(string $email): void { $startUrl = $this->webhookUrl(); // $startUrl = '/webhooks/start/order-workflow'; From 4603d607badd4c29340b65a8ac0e7bc1390dddd9 Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Mon, 24 Feb 2025 22:00:28 -0600 Subject: [PATCH 25/43] Update sample-app.md --- docs/sample-app.md | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/docs/sample-app.md b/docs/sample-app.md index e178c8be..95c20695 100644 --- a/docs/sample-app.md +++ b/docs/sample-app.md @@ -4,7 +4,7 @@ sidebar_position: 7 # Sample App -[This](https://github.com/laravel-workflow/sample-app) is a sample Laravel 11 application with example workflows that you can run inside a GitHub codespace. +[This](https://github.com/laravel-workflow/sample-app) is a sample Laravel 12 application with example workflows that you can run inside a GitHub codespace.
@@ -16,17 +16,11 @@ Create a codespace from the main branch of [this](https://github.com/laravel-wor **Step 2** -Wait for the codespace to build. This should take between 5 to 10 minutes. - -image - -**Step 3** - -Once the codespace has been created. You will see the editor and the terminal at the bottom. +Wait for the codespace to build. This should take between 5 to 10 minutes. Once the codespace has been created. You will see the editor and the terminal at the bottom. image -**Step 4** +**Step 3** Run the migrations to create the necessary database tables. @@ -34,7 +28,7 @@ Run the migrations to create the necessary database tables. php artisan migrate ``` -**Step 5** +**Step 4** Start the queue worker. This will enable the processing of workflows and activities. @@ -42,13 +36,13 @@ Start the queue worker. This will enable the processing of workflows and activit php artisan queue:work ``` -**Step 6** +**Step 5** Create a new terminal window. image -**Step 7** +**Step 6** Start the example workflow inside the new terminal window. @@ -56,7 +50,7 @@ Start the example workflow inside the new terminal window. php artisan app:workflow ``` -**Step 8** +**Step 7** You can view the waterline dashboard via the mapped port. @@ -66,7 +60,7 @@ Add `/waterline/dashboard` to the URL e.g. `https://[your-codespace-name]-80.pre image -**Step 9** +**Step 8** Run the workflow and activity tests. From 30e1207744dcaa4515925122def25b3b86cf3ba5 Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Tue, 25 Feb 2025 00:25:59 -0600 Subject: [PATCH 26/43] Update timers.md --- docs/features/timers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/features/timers.md b/docs/features/timers.md index 178600bf..3f0f1ff7 100644 --- a/docs/features/timers.md +++ b/docs/features/timers.md @@ -31,7 +31,7 @@ You may also specify the time to wait as a string e.g. '5 seconds' or '30 minute **Important:** When using timers, do not use `Carbon::now()` to get the current time. Instead, use `WorkflowStub::now()`, which returns the current time as seen by the workflow system. This is crucial because the actual time may not match your application's system clock. -Additionally, when measuring elapsed time in workflows (e.g., tracking how long an activity takes), always get your start time with: +Additionally, when measuring elapsed time in workflows (e.g., tracking how long an activity takes), always get your start and end times with: ```php $start = yield WorkflowStub::sideEffect(fn () => WorkflowStub::now()); From 71d1b5403648082597c1e49697fb25f8e7b452a5 Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Tue, 25 Feb 2025 09:53:43 -0600 Subject: [PATCH 27/43] Update sample-app.md --- docs/sample-app.md | 51 ++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 27 deletions(-) diff --git a/docs/sample-app.md b/docs/sample-app.md index 95c20695..4c7cd08d 100644 --- a/docs/sample-app.md +++ b/docs/sample-app.md @@ -6,62 +6,59 @@ sidebar_position: 7 [This](https://github.com/laravel-workflow/sample-app) is a sample Laravel 12 application with example workflows that you can run inside a GitHub codespace. -
+### Step 1 +Create a codespace from the main branch of this repo. -**Step 1** +image -Create a codespace from the main branch of [this](https://github.com/laravel-workflow/sample-app) repo. +### Step 2 +Once the codespace has been created, wait for the codespace to build. This should take between 5 to 10 minutes. -image -**Step 2** +### Step 3 +Once it is done. You will see the editor and the terminal at the bottom. -Wait for the codespace to build. This should take between 5 to 10 minutes. Once the codespace has been created. You will see the editor and the terminal at the bottom. +image -image +### Step 4 +Run composer install. -**Step 3** +```bash +composer install +``` -Run the migrations to create the necessary database tables. +### Step 5 +Run the init command to setup the app, install extra dependencies and run the migrations. ```bash -php artisan migrate +php artisan app:init ``` -**Step 4** - +### Step 6 Start the queue worker. This will enable the processing of workflows and activities. ```bash php artisan queue:work ``` -**Step 5** - +### Step 7 Create a new terminal window. -image - -**Step 6** +image +### Step 8 Start the example workflow inside the new terminal window. ```bash php artisan app:workflow ``` -**Step 7** - -You can view the waterline dashboard via the mapped port. - -image - -Add `/waterline/dashboard` to the URL e.g. `https://[your-codespace-name]-80.preview.app.github.dev/waterline/dashboard` - -image +### Step 9 +You can view the waterline dashboard at https://[your-codespace-name]-80.preview.app.github.dev/waterline/dashboard. -**Step 8** +image +### Step 10 Run the workflow and activity tests. ```bash From e36371a25bfe636f14053b49c580b16acea7d079 Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Tue, 25 Feb 2025 10:55:07 -0600 Subject: [PATCH 28/43] Update sample-app.md --- docs/sample-app.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/sample-app.md b/docs/sample-app.md index 4c7cd08d..b8e36887 100644 --- a/docs/sample-app.md +++ b/docs/sample-app.md @@ -6,19 +6,19 @@ sidebar_position: 7 [This](https://github.com/laravel-workflow/sample-app) is a sample Laravel 12 application with example workflows that you can run inside a GitHub codespace. + ### Step 1 Create a codespace from the main branch of this repo. -image +![image](https://user-images.githubusercontent.com/1130888/233664377-f300ad50-5436-4bb8-b172-c52e12047264.png) ### Step 2 Once the codespace has been created, wait for the codespace to build. This should take between 5 to 10 minutes. - ### Step 3 Once it is done. You will see the editor and the terminal at the bottom. -image +![image](https://user-images.githubusercontent.com/1130888/233665550-1a4f2098-2919-4108-ac9f-bef1a9f2f47c.png) ### Step 4 Run composer install. @@ -44,7 +44,7 @@ php artisan queue:work ### Step 7 Create a new terminal window. -image +![image](https://user-images.githubusercontent.com/1130888/233666917-029247c7-9e6c-46de-b304-27473fd34517.png) ### Step 8 Start the example workflow inside the new terminal window. @@ -56,7 +56,7 @@ php artisan app:workflow ### Step 9 You can view the waterline dashboard at https://[your-codespace-name]-80.preview.app.github.dev/waterline/dashboard. -image +![image](https://user-images.githubusercontent.com/1130888/233669600-3340ada6-5f73-4602-8d82-a81a9d43f883.png) ### Step 10 Run the workflow and activity tests. From 88804f56f4d58b6bb81d0363131e6d018cd0ce7f Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Tue, 25 Feb 2025 12:16:18 -0600 Subject: [PATCH 29/43] Update sample-app.md --- docs/sample-app.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/sample-app.md b/docs/sample-app.md index b8e36887..56c1bba1 100644 --- a/docs/sample-app.md +++ b/docs/sample-app.md @@ -7,58 +7,58 @@ sidebar_position: 7 [This](https://github.com/laravel-workflow/sample-app) is a sample Laravel 12 application with example workflows that you can run inside a GitHub codespace. -### Step 1 +*Step 1* Create a codespace from the main branch of this repo. ![image](https://user-images.githubusercontent.com/1130888/233664377-f300ad50-5436-4bb8-b172-c52e12047264.png) -### Step 2 +*Step 2* Once the codespace has been created, wait for the codespace to build. This should take between 5 to 10 minutes. -### Step 3 +*Step 3* Once it is done. You will see the editor and the terminal at the bottom. ![image](https://user-images.githubusercontent.com/1130888/233665550-1a4f2098-2919-4108-ac9f-bef1a9f2f47c.png) -### Step 4 +*Step 4* Run composer install. ```bash composer install ``` -### Step 5 +*Step 5* Run the init command to setup the app, install extra dependencies and run the migrations. ```bash php artisan app:init ``` -### Step 6 +*Step 6* Start the queue worker. This will enable the processing of workflows and activities. ```bash php artisan queue:work ``` -### Step 7 +*Step 7* Create a new terminal window. ![image](https://user-images.githubusercontent.com/1130888/233666917-029247c7-9e6c-46de-b304-27473fd34517.png) -### Step 8 +*Step 8* Start the example workflow inside the new terminal window. ```bash php artisan app:workflow ``` -### Step 9 +*Step 9* You can view the waterline dashboard at https://[your-codespace-name]-80.preview.app.github.dev/waterline/dashboard. ![image](https://user-images.githubusercontent.com/1130888/233669600-3340ada6-5f73-4602-8d82-a81a9d43f883.png) -### Step 10 +*Step 10* Run the workflow and activity tests. ```bash From 2f08bc692a8fc9bb2aa57e22606905a8bc16cf52 Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Tue, 25 Feb 2025 12:21:46 -0600 Subject: [PATCH 30/43] Update sample-app.md --- docs/sample-app.md | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/docs/sample-app.md b/docs/sample-app.md index 56c1bba1..daaee159 100644 --- a/docs/sample-app.md +++ b/docs/sample-app.md @@ -7,58 +7,68 @@ sidebar_position: 7 [This](https://github.com/laravel-workflow/sample-app) is a sample Laravel 12 application with example workflows that you can run inside a GitHub codespace. -*Step 1* +**Step 1** + Create a codespace from the main branch of this repo. ![image](https://user-images.githubusercontent.com/1130888/233664377-f300ad50-5436-4bb8-b172-c52e12047264.png) -*Step 2* +**Step 2** + Once the codespace has been created, wait for the codespace to build. This should take between 5 to 10 minutes. -*Step 3* +**Step 3** + Once it is done. You will see the editor and the terminal at the bottom. ![image](https://user-images.githubusercontent.com/1130888/233665550-1a4f2098-2919-4108-ac9f-bef1a9f2f47c.png) -*Step 4* +**Step 4** + Run composer install. ```bash composer install ``` -*Step 5* +**Step 5** + Run the init command to setup the app, install extra dependencies and run the migrations. ```bash php artisan app:init ``` -*Step 6* +**Step 6** + Start the queue worker. This will enable the processing of workflows and activities. ```bash php artisan queue:work ``` -*Step 7* +**Step 7** + Create a new terminal window. ![image](https://user-images.githubusercontent.com/1130888/233666917-029247c7-9e6c-46de-b304-27473fd34517.png) -*Step 8* +**Step 8** + Start the example workflow inside the new terminal window. ```bash php artisan app:workflow ``` -*Step 9* +**Step 9** + You can view the waterline dashboard at https://[your-codespace-name]-80.preview.app.github.dev/waterline/dashboard. ![image](https://user-images.githubusercontent.com/1130888/233669600-3340ada6-5f73-4602-8d82-a81a9d43f883.png) -*Step 10* +**Step 10** + Run the workflow and activity tests. ```bash From ef9773e2e1052abebb500cf70875f8c7ffc0b0ae Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Wed, 26 Feb 2025 23:49:07 -0600 Subject: [PATCH 31/43] Update microservices.md --- docs/configuration/microservices.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/configuration/microservices.md b/docs/configuration/microservices.md index 23637f26..dcb745a7 100644 --- a/docs/configuration/microservices.md +++ b/docs/configuration/microservices.md @@ -17,15 +17,15 @@ Below is a guide on configuring a shared MySQL database and Redis connection: 'connections' => [ 'shared' => [ 'driver' => 'mysql', - 'url' => env('SHARED_DATABASE_URL'), + 'url' => env('SHARED_DB_URL'), 'host' => env('SHARED_DB_HOST', '127.0.0.1'), 'port' => env('SHARED_DB_PORT', '3306'), - 'database' => env('SHARED_DB_DATABASE', 'forge'), - 'username' => env('SHARED_DB_USERNAME', 'forge'), + 'database' => env('SHARED_DB_DATABASE', 'laravel'), + 'username' => env('SHARED_DB_USERNAME', 'root'), 'password' => env('SHARED_DB_PASSWORD', ''), 'unix_socket' => env('SHARED_DB_SOCKET', ''), - 'charset' => 'utf8mb4', - 'collation' => 'utf8mb4_unicode_ci', + 'charset' => env('SHARED_DB_CHARSET', 'utf8mb4'), + 'collation' => env('SHARED_DB_COLLATION', 'utf8mb4_unicode_ci'), 'prefix' => '', 'prefix_indexes' => true, 'strict' => true, From 857bd18e4b668e00c8b09538c8ba7901850238dc Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Wed, 26 Feb 2025 23:50:43 -0600 Subject: [PATCH 32/43] Update microservices.md From 37728301ab9c02131f7010c5e2831194e761c3c7 Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Wed, 26 Feb 2025 23:52:26 -0600 Subject: [PATCH 33/43] Update microservices.md --- docs/configuration/microservices.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/configuration/microservices.md b/docs/configuration/microservices.md index dcb745a7..fe22e66b 100644 --- a/docs/configuration/microservices.md +++ b/docs/configuration/microservices.md @@ -54,9 +54,9 @@ Below is a guide on configuring a shared MySQL database and Redis connection: 'connections' => [ 'shared' => [ 'driver' => 'redis', - 'connection' => 'shared', + 'connection' => env('SHARED_REDIS_QUEUE_CONNECTION', 'default'), 'queue' => env('SHARED_REDIS_QUEUE', 'default'), - 'retry_after' => 90, + 'retry_after' => (int) env('SHARED_REDIS_QUEUE_RETRY_AFTER', 90), 'block_for' => null, 'after_commit' => false, ], From f1f067a0134fc758ca24b2a490d53d06f80b0f8d Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Thu, 27 Feb 2025 13:27:04 -0600 Subject: [PATCH 34/43] Update installation.md --- docs/installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installation.md b/docs/installation.md index b9948021..22dbba7c 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -8,7 +8,7 @@ sidebar_position: 2 Laravel Workflow requires the following to run: -- PHP 8 or later +- PHP 8.1 or later - Laravel 9 or later Laravel Workflow can be used with any queue driver that Laravel supports (except the `sync` driver), including: From 8486325e16a2fec4426f2a28b50a28be558895a3 Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Tue, 25 Mar 2025 10:40:57 -0500 Subject: [PATCH 35/43] Update installation.md --- docs/installation.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/installation.md b/docs/installation.md index 22dbba7c..73781aa1 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -18,9 +18,9 @@ Laravel Workflow can be used with any queue driver that Laravel supports (except - Database - Redis -Each queue driver has its own [prerequisites](https://laravel.com/docs/10.x/queues#driver-prerequisites). +Each queue driver has its own [prerequisites](https://laravel.com/docs/12.x/queues#driver-prerequisites). -Laravel Workflow also requires a cache driver that supports [locks](https://laravel.com/docs/10.x/cache#atomic-locks). +Laravel Workflow also requires a cache driver that supports [locks](https://laravel.com/docs/12.x/cache#atomic-locks). > NOTE: The Amazon SQS queue driver in Laravel has a limitation of 15 minutes (900 seconds) for the maximum delay of a message. This means that if a workflow uses the `WorkflowStub::timer()` or `WorkflowStub::awaitWithTimeout()` method with a value greater than 900 seconds, it will not be able to hibernate for that long and the workflow will fail. This is a limitation of the SQS driver and not a limitation of Laravel Workflow itself. If you are using Laravel Workflow with the SQS driver then you should be aware of this limitation and avoid using values greater than 900 seconds. Alternatively, you can use a different queue driver that does not have this limitation, such as the Redis driver. @@ -46,4 +46,4 @@ php artisan migrate ## Running Workers -Laravel Workflow uses queues to run workflows and activities in the background. You will need to either run the `queue:work` [command](https://laravel.com/docs/10.x/queues#the-queue-work-command) or use [Horizon](https://laravel.com/docs/10.x/horizon) to run your queue workers. Without a queue worker, workflows and activities will not be processed. You cannot use the sync driver with queue workers. +Laravel Workflow uses queues to run workflows and activities in the background. You will need to either run the `queue:work` [command](https://laravel.com/docs/12.x/queues#the-queue-work-command) or use [Horizon](https://laravel.com/docs/12.x/horizon) to run your queue workers. Without a queue worker, workflows and activities will not be processed. You cannot use the sync driver with queue workers. From 1d13e564fea553bec2cf330d427f4ab06e019047 Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Sun, 6 Apr 2025 12:01:21 -0500 Subject: [PATCH 36/43] Update monitoring.md --- docs/monitoring.md | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/docs/monitoring.md b/docs/monitoring.md index 17a1cc33..264f1dbb 100644 --- a/docs/monitoring.md +++ b/docs/monitoring.md @@ -17,31 +17,3 @@ sidebar_position: 11 ![workflow](https://user-images.githubusercontent.com/1130888/202866616-98a214d3-a916-4ae1-952e-ca8267ddf4a7.png) Refer to https://github.com/laravel-workflow/waterline for installation and configuration instructions. - -## TaskValve - -[TaskValve](https://taskvalve.com/pricing) is a cloud-based monitoring service with additional features such as failure alerts and a Slack integration. The free tier supports monitoring up to 10,000 workflows per month. - -### Dashboard View - -![taskvalve dashboard](https://github.com/laravel-workflow/laravel-workflow.github.io/assets/1130888/7724359a-7511-49a3-8089-fabeafda2acd) - -### Workflow View - -![workflow view](https://github.com/laravel-workflow/laravel-workflow.github.io/assets/1130888/8015951b-3773-4494-8efa-87becd8737e6) - -### Notifications View - -![notifications view](https://github.com/laravel-workflow/laravel-workflow.github.io/assets/1130888/bb3707a1-c1df-4d5c-804a-5299bd357877) - -### Configuration - -[Publish the configuration file](https://laravel-workflow.com/docs/configuration/publishing-config) and then update your .env with the following settings: - -``` -WORKFLOW_MONITOR=true -WORKFLOW_MONITOR_URL=https://api.taskvalve.com -WORKFLOW_MONITOR_API_KEY=[YOUR_API_KEY_GOES_HERE] -``` - -Laravel Workflow will now report workflows and activities to TaskValve and you can view them via their [website](https://taskvalve.com/pricing). From a1b1ca09130e96803c009474877a871fbb413122 Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Sun, 6 Apr 2025 12:19:56 -0500 Subject: [PATCH 37/43] Update introduction.md --- docs/introduction.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/introduction.md b/docs/introduction.md index 46a895bc..14c00c2e 100644 --- a/docs/introduction.md +++ b/docs/introduction.md @@ -6,7 +6,7 @@ sidebar_position: 1 ## What is Laravel Workflow? -Laravel Workflow is a durable workflow engine that allows developers to write long running persistent distributed workflows (orchestrations) in PHP powered by Laravel Queues. It provides a simple and intuitive way to define complex asynchronous processes, such as data pipelines and microservices, as a sequence of activities that run in parallel or in series. +Laravel Workflow is a durable workflow engine that allows developers to write long running persistent distributed workflows (orchestrations) in PHP powered by Laravel Queues. It provides a simple and intuitive way to define complex asynchronous processes, such as agentic workflows (AI-driven), data pipelines, and microservices, as a sequence of activities that run in parallel or in series. Laravel Workflow is built on top of Laravel, the popular PHP web framework, and uses its queue system and database layer to store and manage workflow data and state. It is designed to be scalable, reliable, and easy to use, with a focus on simplicity and maintainability. From 5934eb41b8f34d2510a4f74b8d09929c981b8f0e Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Fri, 18 Apr 2025 13:11:53 -0500 Subject: [PATCH 38/43] Update docusaurus.config.js --- docusaurus.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docusaurus.config.js b/docusaurus.config.js index 0f23b47b..54b8e35d 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -125,7 +125,7 @@ const config = { ], }, ], - copyright: `Copyright © ${new Date().getFullYear()} TaskValve.`, + copyright: `Copyright © ${new Date().getFullYear()} Workflow.`, }, prism: { theme: lightCodeTheme, From b696c3f683fb2e39db1fbc45aedd56a6830ffa3b Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Sun, 15 Jun 2025 16:05:00 -0500 Subject: [PATCH 39/43] Update webhooks.md --- docs/features/webhooks.md | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/docs/features/webhooks.md b/docs/features/webhooks.md index 97d79627..7123859c 100644 --- a/docs/features/webhooks.md +++ b/docs/features/webhooks.md @@ -113,13 +113,14 @@ class ShipOrderActivity extends Activity ``` ## Webhook Authentication -By default, webhooks don't require authentication but this can be configured in `config/workflows.php`. +By default, webhooks don't require authentication, but you can configure one of several strategies in `config/workflows.php`. ### Authentication Methods Laravel Workflow supports: 1. No Authentication (none) 2. Token-based Authentication (token) 3. HMAC Signature Verification (signature) +4. Custom Authentication (custom) ### Token Authentication For token authentication, webhooks require a valid API token in the request headers. The default header is `Authorization` but you can change this in the configuration settings. @@ -146,6 +147,30 @@ curl -X POST "https://example.com/webhooks/start/order-workflow" \ -d "$BODY" ``` +### Custom Authentication +To use a custom authenticator, create a class that implements the following interface: + +```php +use Illuminate\Http\Request; + +interface WebhookAuthenticator { + public function validate(Request $request): Request; +} +``` + +Then configure it in `config/workflows.php`: + +```php +'webhook_auth' => [ + 'method' => 'custom', + 'custom' => [ + 'class' => App\Your\CustomAuthenticator::class, + ], +], +``` + +The `validate()` method should return the `Request` if valid, or call `abort(401)` if unauthorized. + ## Configuring Webhook Routes By default, webhooks are accessible under `/webhooks`. You can customize the route path in `config/workflows.php`: From c2f25938d7cfca557d75cfc929afb0fdc7a8564e Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Sun, 15 Jun 2025 16:15:16 -0500 Subject: [PATCH 40/43] Update webhooks.md --- docs/features/webhooks.md | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/docs/features/webhooks.md b/docs/features/webhooks.md index 7123859c..727f9d40 100644 --- a/docs/features/webhooks.md +++ b/docs/features/webhooks.md @@ -148,13 +148,24 @@ curl -X POST "https://example.com/webhooks/start/order-workflow" \ ``` ### Custom Authentication -To use a custom authenticator, create a class that implements the following interface: +To use a custom authenticator, create a class that implements the `WebhookAuthenticator` interface: ```php use Illuminate\Http\Request; +use Workflow\Auth\WebhookAuthenticator; -interface WebhookAuthenticator { - public function validate(Request $request): Request; +class CustomAuthenticator implements WebhookAuthenticator +{ + public function validate(Request $request): Request + { + $allow = true; + + if ($allow) { + return $request; + } else { + abort(401, 'Unauthorized'); + } + } } ``` From a04188adf5b11884c00ee69e98b0f2dacd46c2ac Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Sat, 6 Sep 2025 21:42:37 -0500 Subject: [PATCH 41/43] Document 'Continue As New' pattern in Laravel Workflow Added documentation for the 'Continue As New' pattern in Laravel Workflow, including usage examples and benefits. --- docs/features/continue-as-new.md | 42 ++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 docs/features/continue-as-new.md diff --git a/docs/features/continue-as-new.md b/docs/features/continue-as-new.md new file mode 100644 index 00000000..7491bb55 --- /dev/null +++ b/docs/features/continue-as-new.md @@ -0,0 +1,42 @@ +--- +sidebar_position: 12 +--- + +# Continue As New + +Laravel Workflow supports the **Continue As New** pattern, allowing a running workflow to restart itself with new arguments. +This is useful when you need to: + +* Prevent unbounded workflow history growth. +* Model iterative loops or recursive workflows. +* Split long-running workflows into smaller, manageable executions while preserving continuity. + +## Using `continueAsNew` + +To restart a workflow as new, call the static method `WorkflowStub::continueAsNew(...)` from within the workflow’s `execute()` method. + +```php +use Workflow\ActivityStub; +use Workflow\Workflow; +use Workflow\WorkflowStub; + +class CounterWorkflow extends Workflow +{ + public function execute(int $count = 0, int $max = 3) + { + $result = yield ActivityStub::make(CountActivity::class, $count); + + if ($count >= $max) { + return 'workflow_' . $result; + } + + return yield WorkflowStub::continueAsNew($count + 1, $max); + } +} +``` + +In this example: + +* The workflow executes an activity each iteration. +* If the maximum count has not been reached, it continues as new with incremented arguments. +* The final result is returned only when the loop completes. From c951dd6ca17cdb8904b70c70e1b2db3ed10b8025 Mon Sep 17 00:00:00 2001 From: Andrew Broberg Date: Thu, 18 Sep 2025 08:39:44 +1000 Subject: [PATCH 42/43] docs: Fix documentation on how to use signals --- docs/features/signals.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/features/signals.md b/docs/features/signals.md index 14ca788a..2acc24b3 100644 --- a/docs/features/signals.md +++ b/docs/features/signals.md @@ -29,7 +29,7 @@ To trigger a signal on a workflow, call the method on the workflow instance. The ```php use Workflow\WorkflowStub; -$workflow = WorkflowStub::make(MyWorkflow::class); +$workflow = WorkflowStub::load($workflowId); $workflow->setReady(true); ``` From 349156dd0cc8babfc394641fa323f3a0d8ccf44a Mon Sep 17 00:00:00 2001 From: Richard McDaniel Date: Thu, 23 Oct 2025 17:00:40 -0500 Subject: [PATCH 43/43] Remove base model change instructions from docs Removed instructions for changing the base model in the publishing configuration documentation. --- docs/configuration/publishing-config.md | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/docs/configuration/publishing-config.md b/docs/configuration/publishing-config.md index 81e1d35e..4ba1cbd2 100644 --- a/docs/configuration/publishing-config.md +++ b/docs/configuration/publishing-config.md @@ -41,22 +41,6 @@ In the `workflows.php` config file you can update the model classes to use your 'stored_workflow_timer_model' => App\Models\StoredWorkflowTimer::class, ``` -## Changing Base Model - -By default, the workflow models extend `Illuminate\Database\Eloquent\Model` but some packages like https://github.com/mongodb/laravel-mongodb require you to extend their model, such as in this example, `MongoDB\Laravel\Eloquent\Model`. - -This can be changed by updating the `base_model` setting. - -```php -'base_model' => Illuminate\Database\Eloquent\Model::class, -``` - -It should now look like this. - -```php -'base_model' => MongoDB\Laravel\Eloquent\Model::class, -``` - ## Changing Serializer This setting allows you to optionally use the Base64 serializer instead of Y (kind of like yEnc encoding where it only gets rid of null bytes). The tradeoff is between speed and size. Base64 is faster but adds more overhead. Y is slower but a lot smaller. If you change this it will only affect new workflows and old workflows will revert to whatever they were encoded with to ensure compatibility.