From d39922ee152c99e0a47ce2823ad955bec187cda9 Mon Sep 17 00:00:00 2001 From: roxblnfk Date: Thu, 6 Feb 2025 21:26:15 +0400 Subject: [PATCH] Add memo test Fix internal issues Workflow::info's memo is now converted to values array --- src/Internal/Transport/Request/UpsertMemo.php | 2 +- .../Request/UpsertSearchAttributes.php | 2 +- .../Request/UpsertTypedSearchAttributes.php | 2 +- .../Transport/Router/StartWorkflow.php | 29 ++++ src/Internal/Workflow/WorkflowContext.php | 12 ++ src/Workflow.php | 2 +- tests/Acceptance/Extra/Workflow/MemoTest.php | 128 ++++++++++++++++++ 7 files changed, 173 insertions(+), 4 deletions(-) create mode 100644 tests/Acceptance/Extra/Workflow/MemoTest.php diff --git a/src/Internal/Transport/Request/UpsertMemo.php b/src/Internal/Transport/Request/UpsertMemo.php index 62345b50..9fdc97f7 100644 --- a/src/Internal/Transport/Request/UpsertMemo.php +++ b/src/Internal/Transport/Request/UpsertMemo.php @@ -16,7 +16,7 @@ final class UpsertMemo extends Request public function __construct( private readonly array $memo, ) { - parent::__construct(self::NAME, ['memo' => $memo]); + parent::__construct(self::NAME, ['memo' => (object) $memo]); } /** diff --git a/src/Internal/Transport/Request/UpsertSearchAttributes.php b/src/Internal/Transport/Request/UpsertSearchAttributes.php index b704f4ce..257bd802 100644 --- a/src/Internal/Transport/Request/UpsertSearchAttributes.php +++ b/src/Internal/Transport/Request/UpsertSearchAttributes.php @@ -16,7 +16,7 @@ final class UpsertSearchAttributes extends Request public function __construct( private readonly array $searchAttributes, ) { - parent::__construct(self::NAME, ['searchAttributes' => $searchAttributes]); + parent::__construct(self::NAME, ['searchAttributes' => (object) $searchAttributes]); } /** diff --git a/src/Internal/Transport/Request/UpsertTypedSearchAttributes.php b/src/Internal/Transport/Request/UpsertTypedSearchAttributes.php index 651910fa..e3c0c0f8 100644 --- a/src/Internal/Transport/Request/UpsertTypedSearchAttributes.php +++ b/src/Internal/Transport/Request/UpsertTypedSearchAttributes.php @@ -18,7 +18,7 @@ final class UpsertTypedSearchAttributes extends Request public function __construct( private readonly array $searchAttributes, ) { - parent::__construct(self::NAME, ['search_attributes' => $this->prepareSearchAttributes()]); + parent::__construct(self::NAME, ['search_attributes' => (object) $this->prepareSearchAttributes()]); } /** diff --git a/src/Internal/Transport/Router/StartWorkflow.php b/src/Internal/Transport/Router/StartWorkflow.php index 7c028e42..25a36118 100644 --- a/src/Internal/Transport/Router/StartWorkflow.php +++ b/src/Internal/Transport/Router/StartWorkflow.php @@ -12,6 +12,7 @@ namespace Temporal\Internal\Transport\Router; use React\Promise\Deferred; +use Temporal\Api\Common\V1\Memo; use Temporal\Api\Common\V1\SearchAttributes; use Temporal\Common\TypedSearchAttributes; use Temporal\DataConverter\EncodedCollection; @@ -58,8 +59,10 @@ public function handle(ServerRequestInterface $request, array $headers, Deferred // Search Attributes and Typed Search Attributes $searchAttributes = $this->convertSearchAttributes($options['info']['SearchAttributes'] ?? null); + $memo = $this->convertMemo($options['info']['Memo'] ?? null); $options['info']['SearchAttributes'] = $searchAttributes?->getValues(); $options['info']['TypedSearchAttributes'] = $this->prepareTypedSA($options['search_attributes'] ?? null); + $options['info']['Memo'] = $memo?->getValues(); /** @var Input $input */ $input = $this->services->marshaller->unmarshal($options, new Input()); @@ -156,6 +159,32 @@ private function convertSearchAttributes(?array $param): ?EncodedCollection } } + private function convertMemo(?array $param): ?EncodedCollection + { + if (!\is_array($param)) { + return null; + } + + if ($param === []) { + return EncodedCollection::empty(); + } + + try { + $memo = (new Memo()); + $memo->mergeFromJsonString( + \json_encode($param), + true, + ); + + return EncodedCollection::fromPayloadCollection( + $memo->getFields(), + $this->services->dataConverter, + ); + } catch (\Throwable) { + return null; + } + } + private function prepareTypedSA(?array $param): TypedSearchAttributes { return $param === null diff --git a/src/Internal/Workflow/WorkflowContext.php b/src/Internal/Workflow/WorkflowContext.php index b6b21096..c75a23ac 100644 --- a/src/Internal/Workflow/WorkflowContext.php +++ b/src/Internal/Workflow/WorkflowContext.php @@ -454,6 +454,10 @@ public function upsertMemo(array $values): void { $this->callsInterceptor->with( function (UpsertMemoInput $input): PromiseInterface { + if ($input->memo === []) { + return resolve(); + } + $result = $this->request(new UpsertMemo($input->memo), false); /** @psalm-suppress UnsupportedPropertyReferenceUsage $memo */ @@ -479,6 +483,10 @@ public function upsertSearchAttributes(array $searchAttributes): void { $this->callsInterceptor->with( function (UpsertSearchAttributesInput $input): PromiseInterface { + if ($input->searchAttributes === []) { + return resolve(); + } + $result = $this->request(new UpsertSearchAttributes($input->searchAttributes), false); /** @psalm-suppress UnsupportedPropertyReferenceUsage $sa */ @@ -503,6 +511,10 @@ public function upsertTypedSearchAttributes(SearchAttributeUpdate ...$updates): { $this->callsInterceptor->with( function (UpsertTypedSearchAttributesInput $input): PromiseInterface { + if ($input->updates === []) { + return resolve(); + } + $result = $this->request(new UpsertTypedSearchAttributes($input->updates), false); // Merge changes diff --git a/src/Workflow.php b/src/Workflow.php index 0921896a..030a1737 100644 --- a/src/Workflow.php +++ b/src/Workflow.php @@ -937,7 +937,7 @@ public static function upsertSearchAttributes(array $searchAttributes): void */ public static function upsertTypedSearchAttributes(SearchAttributeUpdate ...$updates): void { - $updates === [] or self::getCurrentContext()->upsertTypedSearchAttributes(...$updates); + self::getCurrentContext()->upsertTypedSearchAttributes(...$updates); } /** diff --git a/tests/Acceptance/Extra/Workflow/MemoTest.php b/tests/Acceptance/Extra/Workflow/MemoTest.php new file mode 100644 index 00000000..811aef09 --- /dev/null +++ b/tests/Acceptance/Extra/Workflow/MemoTest.php @@ -0,0 +1,128 @@ + 'value1', + 'key2' => 'value2', + 'key3' => ['foo' => 'bar'], + 42 => 'value4', + ], + )] WorkflowStubInterface $stub, + ): void { + try { + $stub->update('setMemo', []); + + // Get Search Attributes using Client API + $clientMemo = $stub->describe()->info->memo->getValues(); + + // Complete workflow + /** @see TestWorkflow::exit */ + $stub->signal('exit'); + } catch (\Throwable $e) { + $stub->terminate('test failed'); + throw $e; + } + + // Get Memo from Workflow + $result = $stub->getResult(); + + $expected = [ + 'key1' => 'value1', + 'key2' => 'value2', + 'key3' => (object) ['foo' => 'bar'], + 42 => 'value4', + ]; + $this->assertEquals($expected, $clientMemo); + $this->assertEquals($expected, (array) $result); + } + + #[Test] + public function overrideAddAndRemove( + #[Stub( + type: 'Extra_Workflow_Memo', + memo: [ + 'key1' => 'value1', + 'key2' => 'value2', + 'key3' => ['foo' => 'bar'], + ], + )] WorkflowStubInterface $stub, + ): void { + try { + $stub->update('setMemo', [ + 'key2' => null, + 'key3' => 42, + 'key4' => 'value4', + ]); + + // Get Search Attributes using Client API + $clientMemo = $stub->describe()->info->memo->getValues(); + + // Complete workflow + /** @see TestWorkflow::exit */ + $stub->signal('exit'); + } catch (\Throwable $e) { + $stub->terminate('test failed'); + throw $e; + } + + // Get Memo from Workflow + $result = $stub->getResult(); + + $expected = [ + 'key1' => 'value1', + 'key3' => 42, + 'key4' => 'value4', + ]; + $this->assertEquals($expected, $clientMemo); + $this->assertEquals($expected, (array) $result); + } +} + +#[WorkflowInterface] +class TestWorkflow +{ + private bool $exit = false; + + #[WorkflowMethod(name: "Extra_Workflow_Memo")] + public function handle() + { + yield Workflow::await( + fn(): bool => $this->exit, + ); + + tr(Workflow::getInfo()->memo); + + return Workflow::getInfo()->memo; + } + + #[Workflow\UpdateMethod] + public function setMemo(array $memo): void + { + Workflow::upsertMemo($memo); + } + + #[Workflow\SignalMethod] + public function exit(): void + { + $this->exit = true; + } +}