diff --git a/src/Client/Schedule/Policy/SchedulePolicies.php b/src/Client/Schedule/Policy/SchedulePolicies.php index 7099c53e1..5790b2687 100644 --- a/src/Client/Schedule/Policy/SchedulePolicies.php +++ b/src/Client/Schedule/Policy/SchedulePolicies.php @@ -6,6 +6,7 @@ use Google\Protobuf\Duration; use Temporal\Internal\Marshaller\Meta\Marshal; +use Temporal\Internal\Marshaller\Type\DateIntervalType; use Temporal\Internal\Support\DateInterval; use Temporal\Internal\Traits\CloneWith; @@ -34,10 +35,12 @@ final class SchedulePolicies * If the Temporal server misses an action due to one or more components * being down, and comes back up, the action will be run if the scheduled * time is within this window from the current time. - * This value defaults to 60 seconds, and can't be less than 10 seconds. + * When unset, this value is omitted so the Temporal Server applies its + * default (currently one year). An explicitly set value can't be less than + * 10 seconds. */ - #[Marshal(name: 'catchup_window', of: Duration::class)] - public readonly \DateInterval $catchupWindow; + #[Marshal(name: 'catchup_window', type: DateIntervalType::class, of: Duration::class, nullable: true)] + public readonly ?\DateInterval $catchupWindow; /** * If true, and a Workflow run fails or times out, pause the Schedule. @@ -50,7 +53,7 @@ final class SchedulePolicies private function __construct() { $this->overlapPolicy = ScheduleOverlapPolicy::Unspecified; - $this->catchupWindow = new \DateInterval('PT60S'); + $this->catchupWindow = null; $this->pauseOnFailure = false; } @@ -75,7 +78,9 @@ public function withOverlapPolicy(ScheduleOverlapPolicy $overlapPolicy): self * If the Temporal server misses an action due to one or more components * being down, and comes back up, the action will be run if the scheduled * time is within this window from the current time. - * This value defaults to 60 seconds, and can't be less than 10 seconds. + * When unset, this value is omitted so the Temporal Server applies its + * default (currently one year). An explicitly set value can't be less than + * 10 seconds. * * @param DateIntervalValue $interval */ diff --git a/src/Internal/Mapper/ScheduleMapper.php b/src/Internal/Mapper/ScheduleMapper.php index 020213642..e7c9fe22f 100644 --- a/src/Internal/Mapper/ScheduleMapper.php +++ b/src/Internal/Mapper/ScheduleMapper.php @@ -41,7 +41,7 @@ public function toMessage(Schedule $dto): \Temporal\Api\Schedule\V1\Schedule $array = $this->marshaller->marshal($dto); $array['policies']['overlap_policy'] = $dto->policies->overlapPolicy->value; - $array['policies'] = new SchedulePolicies($array['policies'] ?? []); + $array['policies'] = new SchedulePolicies(self::cleanArray($array['policies'] ?? [])); $array['spec'] = $this->prepareSpec($array['spec'] ?? []); $array['state'] = new ScheduleState($array['state'] ?? []); isset($array['action']) and $array['action'] = $this->prepareAction($dto->action, $array['action']); diff --git a/src/Internal/Marshaller/ProtoToArrayConverter.php b/src/Internal/Marshaller/ProtoToArrayConverter.php index c5881dd76..62c8042ce 100644 --- a/src/Internal/Marshaller/ProtoToArrayConverter.php +++ b/src/Internal/Marshaller/ProtoToArrayConverter.php @@ -51,14 +51,11 @@ private function getMapper(Message $message): \Closure 'U.u', \sprintf('%d.%d', $input->getSeconds(), $input->getNanos() / 1000), ), - Duration::class => static function (Duration $input): \DateInterval { - $now = new \DateTimeImmutable('@0'); - return $now->diff( - $now->modify( - \sprintf('+%d seconds +%d microseconds', $input->getSeconds(), $input->getNanos() / 1000), - ), - ); - }, + Duration::class => static fn(Duration $input): \DateInterval => + \Temporal\Internal\Support\DateInterval::parse( + (int) $input->getSeconds() * 1_000_000 + \intdiv($input->getNanos(), 1_000), + \Temporal\Internal\Support\DateInterval::FORMAT_MICROSECONDS, + ), SearchAttributes::class => fn(SearchAttributes $input): EncodedCollection => EncodedCollection::fromPayloadCollection( $input->getIndexedFields(), diff --git a/tests/Acceptance/Harness/Schedule/BasicTest.php b/tests/Acceptance/Harness/Schedule/BasicTest.php index 5ae807661..1282ca38f 100644 --- a/tests/Acceptance/Harness/Schedule/BasicTest.php +++ b/tests/Acceptance/Harness/Schedule/BasicTest.php @@ -16,6 +16,7 @@ use Temporal\Client\Schedule\Spec\ScheduleSpec; use Temporal\Client\ScheduleClientInterface; use Temporal\Client\WorkflowClientInterface; +use Temporal\Internal\Support\DateInterval; use Temporal\Tests\Acceptance\App\Runtime\Feature; use Temporal\Tests\Acceptance\App\Runtime\State; use Temporal\Tests\Acceptance\App\TestCase; @@ -65,6 +66,12 @@ public static function check( $action = $description->schedule->action; self::assertInstanceOf(StartWorkflowAction::class, $action); self::assertSame($workflowId, $action->workflowId); + $catchupWindow = $description->schedule->policies->catchupWindow; + self::assertNotNull($catchupWindow); + self::assertSame( + 365 * 24 * 60 * 60, + (int) DateInterval::parse($catchupWindow)->totalSeconds, + ); // Confirm simple list $found = false; diff --git a/tests/Unit/Internal/Marshaller/ProtoToArrayConverterTestCase.php b/tests/Unit/Internal/Marshaller/ProtoToArrayConverterTestCase.php new file mode 100644 index 000000000..b1296931a --- /dev/null +++ b/tests/Unit/Internal/Marshaller/ProtoToArrayConverterTestCase.php @@ -0,0 +1,31 @@ +convert( + (new Duration())->setSeconds(365 * 24 * 60 * 60), + ); + + self::assertInstanceOf(\DateInterval::class, $interval); + self::assertSame( + 365 * 24 * 60 * 60, + (int) DateInterval::parse($interval)->totalSeconds, + ); + } +} diff --git a/tests/Unit/Schedule/Mapper/WorkflowExecutionInfoMapperTestCase.php b/tests/Unit/Schedule/Mapper/WorkflowExecutionInfoMapperTestCase.php index 110af0972..a34a5c69c 100644 --- a/tests/Unit/Schedule/Mapper/WorkflowExecutionInfoMapperTestCase.php +++ b/tests/Unit/Schedule/Mapper/WorkflowExecutionInfoMapperTestCase.php @@ -221,7 +221,7 @@ public function testToMessageEmptyValues(): void $this->assertEmpty($spec->getTimezoneName()); // Test Policies - $this->assertEquals(60, $policies->getCatchupWindow()->getSeconds()); + $this->assertNull($policies->getCatchupWindow()); $this->assertFalse($policies->getPauseOnFailure()); $this->assertEquals(ScheduleOverlapPolicy::SCHEDULE_OVERLAP_POLICY_UNSPECIFIED, $policies->getOverlapPolicy());