diff --git a/src/DataConverter/DataConverter.php b/src/DataConverter/DataConverter.php index d3ea48b55..09e1dacd0 100644 --- a/src/DataConverter/DataConverter.php +++ b/src/DataConverter/DataConverter.php @@ -33,6 +33,7 @@ public static function createDefault(): DataConverterInterface return new DataConverter( new NullConverter(), new BinaryConverter(), + new RawValueConverter(), new ProtoJsonConverter(), new ProtoConverter(), new JsonConverter(), @@ -47,19 +48,18 @@ public function fromPayload(Payload $payload, $type) $encoding = $meta[EncodingKeys::METADATA_ENCODING_KEY]; if (!isset($this->converters[$encoding])) { - throw new DataConverterException(\sprintf('Undefined payload encoding %s', $encoding)); + throw new DataConverterException(\sprintf('Undefined payload encoding "%s"', $encoding)); } $type = Type::create($type); - if (\in_array($type->getName(), [Type::TYPE_VOID, Type::TYPE_NULL, Type::TYPE_FALSE, Type::TYPE_TRUE], true)) { - return match ($type->getName()) { - Type::TYPE_VOID, Type::TYPE_NULL => null, - Type::TYPE_TRUE => true, - Type::TYPE_FALSE => false, - }; - } - return $this->converters[$encoding]->fromPayload($payload, $type); + return match ($type->getName()) { + Type::TYPE_VOID, + Type::TYPE_NULL => null, + Type::TYPE_TRUE => true, + Type::TYPE_FALSE => false, + default => $this->converters[$encoding]->fromPayload($payload, $type), + }; } /** diff --git a/src/DataConverter/EncodingKeys.php b/src/DataConverter/EncodingKeys.php index a858c1818..1d84a294d 100644 --- a/src/DataConverter/EncodingKeys.php +++ b/src/DataConverter/EncodingKeys.php @@ -17,6 +17,7 @@ final class EncodingKeys public const METADATA_MESSAGE_TYPE = 'messageType'; public const METADATA_ENCODING_NULL = 'binary/null'; public const METADATA_ENCODING_RAW = 'binary/plain'; + public const METADATA_ENCODING_RAW_VALUE = 'binary'; public const METADATA_ENCODING_JSON = 'json/plain'; public const METADATA_ENCODING_PROTOBUF_JSON = 'json/protobuf'; public const METADATA_ENCODING_PROTOBUF = 'binary/protobuf'; diff --git a/src/DataConverter/RawValue.php b/src/DataConverter/RawValue.php new file mode 100644 index 000000000..9cac7ec2e --- /dev/null +++ b/src/DataConverter/RawValue.php @@ -0,0 +1,29 @@ +payload = $data; + } + + public function getPayload(): Payload + { + return $this->payload; + } +} diff --git a/src/DataConverter/RawValueConverter.php b/src/DataConverter/RawValueConverter.php new file mode 100644 index 000000000..49aa1af82 --- /dev/null +++ b/src/DataConverter/RawValueConverter.php @@ -0,0 +1,39 @@ +getPayload(); + $payload->setMetadata([EncodingKeys::METADATA_ENCODING_KEY => EncodingKeys::METADATA_ENCODING_RAW_VALUE]); + + return $payload; + } + + public function fromPayload(Payload $payload, Type $type): RawValue + { + return new RawValue(clone $payload); + } +} diff --git a/src/DataConverter/Type.php b/src/DataConverter/Type.php index 70af788ee..1ecb30567 100644 --- a/src/DataConverter/Type.php +++ b/src/DataConverter/Type.php @@ -79,25 +79,14 @@ public static function fromReflectionType(\ReflectionType $type): self */ public static function create($type): Type { - switch (true) { - case $type instanceof ReturnType: - return new self($type->name, $type->nullable); - - case $type instanceof self: - return $type; - - case \is_string($type): - return new self($type); - - case $type instanceof \ReflectionClass: - return self::fromReflectionClass($type); - - case $type instanceof \ReflectionType: - return self::fromReflectionType($type); - - default: - return new self(); - } + return match (true) { + $type instanceof ReturnType => new self($type->name, $type->nullable), + $type instanceof self => $type, + \is_string($type) => new self($type), + $type instanceof \ReflectionClass => self::fromReflectionClass($type), + $type instanceof \ReflectionType => self::fromReflectionType($type), + default => new self(), + }; } public function getName(): string diff --git a/tests/Acceptance/App/Feature/ClientFactory.php b/tests/Acceptance/App/Feature/ClientFactory.php index d645f3e30..5f6dddf4c 100644 --- a/tests/Acceptance/App/Feature/ClientFactory.php +++ b/tests/Acceptance/App/Feature/ClientFactory.php @@ -4,6 +4,7 @@ namespace Temporal\Tests\Acceptance\App\Feature; +use Temporal\DataConverter\RawValueConverter; use Temporal\Tests\Acceptance\App\Attribute\Client; use Temporal\Tests\Acceptance\App\Runtime\State; use Psr\Container\ContainerInterface; @@ -52,6 +53,7 @@ public function workflowClient(\ReflectionParameter $context): WorkflowClientInt if ($attribute->payloadConverters !== []) { $converters = [ new NullConverter(), + new RawValueConverter(), new BinaryConverter(), new ProtoConverter(), new ProtoJsonConverter(), diff --git a/tests/Acceptance/Extra/DataConverter/RawValueTest.php b/tests/Acceptance/Extra/DataConverter/RawValueTest.php new file mode 100644 index 000000000..8833a7d3a --- /dev/null +++ b/tests/Acceptance/Extra/DataConverter/RawValueTest.php @@ -0,0 +1,61 @@ +getResult(); + + self::assertInstanceOf(RawValue::class, $result); + self::assertInstanceOf(Payload::class, $result->getPayload()); + self::assertSame('hello world', $result->getPayload()->getData()); + } +} + +#[WorkflowInterface] +class FeatureWorkflow +{ + #[WorkflowMethod('Extra_DataConverter_RawValue')] + public function run() + { + $rawValue = new RawValue(new Payload(['data' => 'hello world'])); + + $activity = Workflow::newActivityStub( + RawValueActivity::class, + ActivityOptions::new() + ->withScheduleToCloseTimeout('1 minute'), + ); + + return yield $activity->bypass($rawValue); + } +} + +#[ActivityInterface(prefix: 'RawValueActivity.')] +class RawValueActivity +{ + #[ActivityMethod] + public function bypass(RawValue $arg) + { + return $arg; + } +} diff --git a/tests/Acceptance/worker.php b/tests/Acceptance/worker.php index 15f2f9582..bd473f80e 100644 --- a/tests/Acceptance/worker.php +++ b/tests/Acceptance/worker.php @@ -21,6 +21,7 @@ use Temporal\DataConverter\NullConverter; use Temporal\DataConverter\ProtoConverter; use Temporal\DataConverter\ProtoJsonConverter; +use Temporal\DataConverter\RawValueConverter; use Temporal\Internal\Support\StackRenderer; use Temporal\Testing\Command; use Temporal\Tests\Acceptance\App\Runtime\Feature; @@ -52,6 +53,7 @@ $converters = [ new NullConverter(), new BinaryConverter(), + new RawValueConverter(), new ProtoJsonConverter(), new ProtoConverter(), new JsonConverter(), diff --git a/tests/Unit/DataConverter/RawValueConverterTest.php b/tests/Unit/DataConverter/RawValueConverterTest.php new file mode 100644 index 000000000..283618c1b --- /dev/null +++ b/tests/Unit/DataConverter/RawValueConverterTest.php @@ -0,0 +1,54 @@ + 1]); + $message = new RawValue($innerPayload); + + $payload = DataConverter::createDefault()->toPayload($message); + + self::assertSame($innerPayload, $payload); + self::assertSame(EncodingKeys::METADATA_ENCODING_RAW_VALUE, $payload->getMetadata()[EncodingKeys::METADATA_ENCODING_KEY]); + } + + public function testRawPayloadDecoding(): void + { + $innerPayload = new Payload(['data' => 1]); + $message = new RawValue($innerPayload); + + $encoded = DataConverter::createDefault()->toPayload($message); + $decoded = DataConverter::createDefault()->fromPayload($encoded, RawValue::class); + + self::assertInstanceOf(RawValue::class, $decoded); + self::assertSame($decoded->getPayload(), $encoded); + } + + protected function create(): DataConverterInterface + { + return DataConverter::createDefault(); + } +}