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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Client/WorkflowClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,7 @@ public function newWorkflowStub(
?WorkflowOptions $options = null,
): object {
$workflow = $this->reader->fromClass($class);
$options = ($options ?? WorkflowOptions::new())->mergeWith($workflow->getMethodRetry());

return new WorkflowProxy(
$this,
Expand Down
2 changes: 2 additions & 0 deletions src/Common/CronSchedule.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
* (or retrying), then it will skip that schedule. Cron workflow will not stop
* until it is terminated or canceled.
*
* Applies to child workflows only. Workflows started via {@see \Temporal\Client\WorkflowClient} ignore it.
*
* @psalm-import-type DateIntervalValue from DateInterval
* @see DateInterval
*
Expand Down
9 changes: 5 additions & 4 deletions src/Common/MethodRetry.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@
/**
* Specifies a retry policy for a workflow or activity method. This annotation
* applies only to activity or workflow interface methods. For workflows
* currently used only for child workflow retries. Not required. When not used
* used for child workflow retries and for workflows started via
* {@see \Temporal\Client\WorkflowClient}. Not required. When not used
* either retries don't happen or they are configured through correspondent
* options. If {@see RetryOptions} are present on {@see ActivityOptions} or
* {@see ChildWorkflowOptions} the fields that are not default take precedence
* over parameters of this attribute/annotation.
* options. If {@see RetryOptions} are present on {@see ActivityOptions},
* {@see ChildWorkflowOptions} or {@see \Temporal\Client\WorkflowOptions} the fields that are
* not default take precedence over parameters of this attribute/annotation.
*
* @psalm-import-type DateIntervalValue from DateInterval
* @psalm-import-type ExceptionsList from RetryOptions
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?php

declare(strict_types=1);

namespace Temporal\Tests\Unit\Internal\Client;

use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\TestCase;
use Temporal\Api\Workflowservice\V1\StartWorkflowExecutionRequest;
use Temporal\Api\Workflowservice\V1\StartWorkflowExecutionResponse;
use Temporal\Client\GRPC\ServiceClientInterface;
use Temporal\Client\WorkflowClient;
use Temporal\Client\WorkflowOptions;
use Temporal\Common\CronSchedule;
use Temporal\Common\MethodRetry;
use Temporal\Common\RetryOptions;
use Temporal\Workflow\WorkflowInterface;
use Temporal\Workflow\WorkflowMethod;

#[WorkflowInterface]
interface RetriedCronWorkflow
{
#[WorkflowMethod(name: 'RetriedCronWorkflow')]
#[MethodRetry(maximumAttempts: 5, maximumInterval: '30 seconds')]
#[CronSchedule(interval: '0 * * * *')]
public function handle(): void;
}

#[CoversClass(WorkflowClient::class)]
final class WorkflowClientMethodRetryTestCase extends TestCase
{
public function testRetryPolicyComesFromMethodRetry(): void
{
$request = $this->startRequest(null);

self::assertSame(5, $request->getRetryPolicy()?->getMaximumAttempts());
self::assertSame(30, $request->getRetryPolicy()?->getMaximumInterval()?->getSeconds());
}

public function testUserRetryOptionsTakePrecedenceOverMethodRetry(): void
{
$request = $this->startRequest(
WorkflowOptions::new()->withRetryOptions(RetryOptions::new()->withMaximumAttempts(1)),
);

self::assertSame(1, $request->getRetryPolicy()?->getMaximumAttempts());
self::assertSame(30, $request->getRetryPolicy()?->getMaximumInterval()?->getSeconds());
}

public function testCronScheduleIsIgnored(): void
{
$request = $this->startRequest(null);

self::assertSame('', $request->getCronSchedule());
}

private function startRequest(?WorkflowOptions $options): StartWorkflowExecutionRequest
{
$captured = null;
$service = $this->createMock(ServiceClientInterface::class);
$service->method('withContext')->willReturnSelf();
$service->method('StartWorkflowExecution')->willReturnCallback(
static function (StartWorkflowExecutionRequest $request) use (&$captured): StartWorkflowExecutionResponse {
$captured = $request;
return (new StartWorkflowExecutionResponse())->setRunId('run-1');
},
);

$client = new WorkflowClient($service);
$client->start($client->newWorkflowStub(RetriedCronWorkflow::class, $options));

return $captured;
}
}
Loading