Skip to content
Merged
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
6 changes: 0 additions & 6 deletions psalm-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -374,9 +374,6 @@
<code><![CDATA[getStartWorkflow]]></code>
<code><![CDATA[getUpdateWorkflow]]></code>
</PossiblyNullReference>
<UndefinedInterfaceMethod>
<code><![CDATA[toHeader]]></code>
</UndefinedInterfaceMethod>
</file>
<file src="src/Internal/Client/WorkflowStub.php">
<ArgumentTypeCoercion>
Expand Down Expand Up @@ -411,9 +408,6 @@
<code><![CDATA[hasResult]]></code>
<code><![CDATA[toProtoWorkflowExecution]]></code>
</PossiblyNullReference>
<UndefinedInterfaceMethod>
<code><![CDATA[toHeader]]></code>
</UndefinedInterfaceMethod>
</file>
<file src="src/Internal/Declaration/Dispatcher/AutowiredPayloads.php">
<PossiblyInvalidArgument>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
final class KeywordListValue extends SearchAttributeKey
{
/**
* @param iterable<string|\Stringable> $value
* @param array<string|\Stringable> $value
*/
public function valueSet(array $value): SearchAttributeUpdate
{
Expand Down
7 changes: 6 additions & 1 deletion src/Internal/Client/WorkflowStarter.php
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,12 @@ function (UpdateWithStartInput $input): UpdateWithStartOutput {
$i = new \Temporal\Api\Update\V1\Input();
$i->setName($input->updateInput->updateName);
$input->updateInput->arguments->isEmpty() or $i->setArgs($input->updateInput->arguments->toPayloads());
$input->updateInput->header->isEmpty() or $i->setHeader($input->updateInput->header->toHeader());
$header = $input->updateInput->header;
\assert($header instanceof Header);
if ($header->getDataConverter() === null) {
$header->setDataConverter($this->converter);
}
$header->isEmpty() or $i->setHeader($header->toHeader());
$r->setInput($i);

$ops = [
Expand Down
7 changes: 6 additions & 1 deletion src/Internal/Client/WorkflowStub.php
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,12 @@ static function (
$input->arguments->setDataConverter($converter);
$input->arguments->setSerializationContext($context);
$input->arguments->isEmpty() or $i->setArgs($input->arguments->toPayloads());
$input->header->isEmpty() or $i->setHeader($input->header->toHeader());
$header = $input->header;
\assert($header instanceof Header);
if ($header->getDataConverter() === null) {
$header->setDataConverter($converter);
}
$header->isEmpty() or $i->setHeader($header->toHeader());
$r->setInput($i);

try {
Expand Down
118 changes: 118 additions & 0 deletions tests/Unit/Client/UpdateHeaderTestCase.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
<?php

declare(strict_types=1);

namespace Temporal\Tests\Unit\Client;

use PHPUnit\Framework\Attributes\DataProvider;
use Temporal\Api\Common\V1\Payload;
use Temporal\Api\Update\V1\UpdateRef;
use Temporal\Api\Workflowservice\V1\ExecuteMultiOperationRequest;
use Temporal\Api\Workflowservice\V1\ExecuteMultiOperationResponse;
use Temporal\Api\Workflowservice\V1\ExecuteMultiOperationResponse\Response;
use Temporal\Api\Workflowservice\V1\StartWorkflowExecutionResponse;
use Temporal\Api\Workflowservice\V1\UpdateWorkflowExecutionRequest;
use Temporal\Api\Workflowservice\V1\UpdateWorkflowExecutionResponse;
use Temporal\Client\GRPC\Context;
use Temporal\Client\GRPC\ServiceClientInterface;
use Temporal\Client\WorkflowClient;
use Temporal\DataConverter\DataConverterInterface;
use Temporal\Interceptor\Header;
use Temporal\Interceptor\SimplePipelineProvider;
use Temporal\Interceptor\WorkflowClient\UpdateInput;
use Temporal\Interceptor\WorkflowClient\UpdateWithStartInput;
use Temporal\Interceptor\WorkflowClientCallsInterceptor;
use Temporal\Tests\Unit\AbstractUnit;

final class UpdateHeaderTestCase extends AbstractUnit
{
/**
* @return iterable<string, array{bool, bool}>
*/
public static function headers(): iterable
{
yield 'update, client converter' => [false, false];
yield 'update with start, client converter' => [true, false];
yield 'update, explicit converter' => [false, true];
yield 'update with start, explicit converter' => [true, true];
}

#[DataProvider('headers')]
public function testHeaderConversion(bool $withStart, bool $explicitConverter): void
{
$converter = $this->createMock(DataConverterInterface::class);
$headerConverter = $explicitConverter ? $this->createMock(DataConverterInterface::class) : $converter;
$encoded = new Payload(['metadata' => ['encoding' => 'custom/header'], 'data' => 'converted']);
$raw = new Payload(['metadata' => ['encoding' => 'custom/raw'], 'data' => "\x00raw\xff"]);
$rawBytes = $raw->serializeToString();

$headerConverter->expects(self::once())->method('toPayload')->with('value')->willReturn($encoded);
$headerConverter->expects(self::never())->method('fromPayload');
if ($explicitConverter) {
$converter->expects(self::never())->method('toPayload');
$converter->expects(self::never())->method('fromPayload');
}
$header = $explicitConverter
? Header::fromPayloadCollection(['raw' => $raw], $headerConverter)->withValue('value', 'value')
: Header::fromValues(['value' => 'value'])->withValue('raw', $raw);

$interceptor = $this->createMock(WorkflowClientCallsInterceptor::class);
$interceptor->method('update')->willReturnCallback(
static fn(UpdateInput $input, callable $next) => $next($input->with(header: $header)),
);
$interceptor->method('updateWithStart')->willReturnCallback(
static fn(UpdateWithStartInput $input, callable $next) => $next(
$input->with(updateInput: $input->updateInput->with(header: $header)),
),
);

$service = $this->createMock(ServiceClientInterface::class);
$service->method('getContext')->willReturn(Context::default());
$service->method('withContext')->willReturnSelf();
$response = (new UpdateWorkflowExecutionResponse())->setUpdateRef(
(new UpdateRef())->setUpdateId('update-id'),
);
$checkHeader = static function (UpdateWorkflowExecutionRequest $request) use ($encoded, $rawBytes): void {
$fields = $request->getRequest()->getInput()->getHeader()->getFields();
self::assertCount(2, $fields);
self::assertSame($encoded->serializeToString(), $fields['value']->serializeToString());
self::assertSame($rawBytes, $fields['raw']->serializeToString());
};
if ($withStart) {
$service->expects(self::once())->method('ExecuteMultiOperation')->willReturnCallback(
static function (ExecuteMultiOperationRequest $request) use ($checkHeader, $response) {
$checkHeader($request->getOperations()[1]->getUpdateWorkflow());

return (new ExecuteMultiOperationResponse())->setResponses([
(new Response())->setStartWorkflow(
(new StartWorkflowExecutionResponse())->setRunId('run-id'),
),
(new Response())->setUpdateWorkflow($response),
]);
},
);
} else {
$service->expects(self::once())->method('UpdateWorkflowExecution')->willReturnCallback(
static function (UpdateWorkflowExecutionRequest $request) use ($checkHeader, $response) {
$checkHeader($request);

return $response;
},
);
}
$client = new WorkflowClient(
$service,
converter: $converter,
interceptorProvider: new SimplePipelineProvider([$interceptor]),
);

if ($withStart) {
$client->updateWithStart($client->newUntypedWorkflowStub('workflow'), 'update');
} else {
$client->newUntypedRunningWorkflowStub('workflow-id')->startUpdate('update');
}

self::assertSame($headerConverter, $header->getDataConverter());
self::assertSame($rawBytes, $raw->serializeToString());
}
}
Loading