diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..76a5bb0 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,18 @@ +# the different stages of this Dockerfile are meant to be built into separate images +# https://docs.docker.com/develop/develop-images/multistage-build/#stop-at-a-specific-build-stage +# https://docs.docker.com/compose/compose-file/#target + + +# https://docs.docker.com/engine/reference/builder/#understand-how-arg-and-from-interact +ARG PHP_VERSION=7.3 +ARG CADDY_VERSION=2.5.1 +# ARG GITHUB_TOKEN + +# "php" stage +FROM php:${PHP_VERSION}-fpm-alpine AS api_platform_php + +COPY --from=composer:2.5 /usr/bin/composer /usr/bin/composer + +WORKDIR /app + +COPY . . \ No newline at end of file diff --git a/GpsConsumer.php b/GpsConsumer.php index e2a1be2..d986bca 100644 --- a/GpsConsumer.php +++ b/GpsConsumer.php @@ -110,7 +110,7 @@ private function getSubscription(): Subscription private function convertMessage(GoogleMessage $message): GpsMessage { - $gpsMessage = GpsMessage::jsonUnserialize($message->data()); + $gpsMessage = GpsMessage::messageUnserialize($message->data(), $message->attributes()); $gpsMessage->setNativeMessage($message); return $gpsMessage; diff --git a/GpsMessage.php b/GpsMessage.php index b4a11dc..fdb31f6 100644 --- a/GpsMessage.php +++ b/GpsMessage.php @@ -154,15 +154,30 @@ public function jsonSerialize(): array ]; } - public static function jsonUnserialize(string $json): self + public static function messageUnserialize(string $message, array $attributes) : self + { + if($attributes['ce-datacontenttype'] === 'application/protobuf') { + return self::protobufUnserialize($message, $attributes); + } + + return self::jsonUnserialize($message, $attributes); + } + + private static function jsonUnserialize(string $json, array $attributes): self { $data = json_decode($json, true); if (\JSON_ERROR_NONE !== json_last_error()) { throw new \InvalidArgumentException(sprintf('The malformed json given. Error %s and message %s', json_last_error(), json_last_error_msg())); } - return new self($data['body'] ?? $json, $data['properties'] ?? [], $data['headers'] ?? []); + return new self($data['body'] ?? $json, $data['properties'] ?? $attributes, $data['headers'] ?? []); + } + + private static function protobufUnserialize(string $protobuf, array $attributes): self + { + return new self($protobuf, $data['properties'] ?? $attributes, $data['headers'] ?? []); } + public function getNativeMessage(): ?GoogleMessage { diff --git a/Tests/Fixtures/Test/AddTest.php b/Tests/Fixtures/Test/AddTest.php new file mode 100644 index 0000000..cc19378 --- /dev/null +++ b/Tests/Fixtures/Test/AddTest.php @@ -0,0 +1,58 @@ +test.AddTest + */ +class AddTest extends \Google\Protobuf\Internal\Message +{ + /** + * Generated from protobuf field string description = 2; + */ + protected $description = ''; + + /** + * Constructor. + * + * @param array $data { + * Optional. Data for populating the Message object. + * + * @type string $description + * } + */ + public function __construct($data = NULL) { + \Enqueue\Gps\Tests\Test\Metadata\Test::initOnce(); + parent::__construct($data); + } + + /** + * Generated from protobuf field string description = 2; + * @return string + */ + public function getDescription() + { + return $this->description; + } + + /** + * Generated from protobuf field string description = 2; + * @param string $var + * @return $this + */ + public function setDescription($var) + { + GPBUtil::checkString($var, True); + $this->description = $var; + + return $this; + } + +} + diff --git a/Tests/Fixtures/Test/Metadata/Test.php b/Tests/Fixtures/Test/Metadata/Test.php new file mode 100644 index 0000000..429341c --- /dev/null +++ b/Tests/Fixtures/Test/Metadata/Test.php @@ -0,0 +1,28 @@ +internalAddGeneratedFile( + ' +� +events/test/test.prototest" +AddTest + description ( B�Z9github.com/urbansportsclub/arena/build/gen/go/events/Test�Usg.Events.Test�USC\\Arena\\Events\\Test�USC\\Arena\\Events\\Test\\Metadatabproto3' + , true); + + static::$is_initialized = true; + } +} + diff --git a/Tests/Fixtures/TestMessage.php b/Tests/Fixtures/TestMessage.php new file mode 100644 index 0000000..f07714c --- /dev/null +++ b/Tests/Fixtures/TestMessage.php @@ -0,0 +1,6 @@ +assertSame('the body', $message->getBody()); } + public function testShouldReceiveMessagProtobuf() + { + $body = '2test+customer_6115118118117248@example.com"4test+customer_611511118118117248@example.com*&App\Tests\Entity\Entity497709'; + $attributes = [ + 'ce-datacontenttype' => 'application/protobuf', + ]; + + $nativeMessage = new Message([ + 'data' => $body, + 'attributes' => $attributes, + ], []); + + $subscription = $this->createSubscriptionMock(); + $subscription + ->expects($this->once()) + ->method('pull') + ->with($this->identicalTo([ + 'maxMessages' => 1, + 'requestTimeout' => 12.345, + ])) + ->willReturn([$nativeMessage]); + + $client = $this->createPubSubClientMock(); + $client + ->expects($this->once()) + ->method('subscription') + ->willReturn($subscription); + + $context = $this->createContextMock(); + $context + ->expects($this->once()) + ->method('getClient') + ->willReturn($client); + + $consumer = new GpsConsumer($context, new GpsQueue('queue-name')); + + $message = $consumer->receive(12345); + + $this->assertInstanceOf(GpsMessage::class, $message); + $this->assertSame($body, $message->getBody()); + $this->assertSame($attributes, $message->getProperties()); + } + /** * @return \PHPUnit\Framework\MockObject\MockObject|GpsContext */ diff --git a/Tests/GpsMessageTest.php b/Tests/GpsMessageTest.php index a43a223..f20ad7a 100644 --- a/Tests/GpsMessageTest.php +++ b/Tests/GpsMessageTest.php @@ -32,7 +32,7 @@ public function testCouldBeUnserializedFromJson() //guard $this->assertNotEmpty($json); - $unserializedMessage = GpsMessage::jsonUnserialize($json); + $unserializedMessage = GpsMessage::messageUnserialize($json, []); $this->assertInstanceOf(GpsMessage::class, $unserializedMessage); $this->assertEquals($message, $unserializedMessage); @@ -42,7 +42,7 @@ public function testMessageEntityCouldBeUnserializedFromJson() { $json = '{"body":"theBody","properties":{"thePropFoo":"thePropFooVal"},"headers":{"theHeaderFoo":"theHeaderFooVal"}}'; - $unserializedMessage = GpsMessage::jsonUnserialize($json); + $unserializedMessage = GpsMessage::messageUnserialize($json, []); $this->assertInstanceOf(GpsMessage::class, $unserializedMessage); $decoded = json_decode($json, true); @@ -55,7 +55,7 @@ public function testMessagePayloadCouldBeUnserializedFromJson() { $json = '{"theBodyPropFoo":"theBodyPropVal"}'; - $unserializedMessage = GpsMessage::jsonUnserialize($json); + $unserializedMessage = GpsMessage::messageUnserialize($json, []); $this->assertInstanceOf(GpsMessage::class, $unserializedMessage); $this->assertEquals($json, $unserializedMessage->getBody()); @@ -68,6 +68,25 @@ public function testThrowIfMalformedJsonGivenOnUnsterilizedFromJson() $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('The malformed json given.'); - GpsMessage::jsonUnserialize('{]'); + GpsMessage::messageUnserialize('{]', []); + } + + public function testCouldBeUnserializedFromProtobuf() + { + $protobufMessage = '2test+customer_6115118118117248@example.com"4test+customer_611511118118117248@example.com*&App\Tests\Entity\Entity497709'; + + $message = new GpsMessage( + $protobufMessage, + [ + 'ce-datacontenttype' => 'application/protobuf', + ] + ); + + $unserializedMessage = GpsMessage::messageUnserialize($protobufMessage, [ + 'ce-datacontenttype' => 'application/protobuf', + ]); + + $this->assertInstanceOf(GpsMessage::class, $unserializedMessage); + $this->assertEquals($message, $unserializedMessage); } } diff --git a/composer.json b/composer.json index 2da6b74..0405597 100644 --- a/composer.json +++ b/composer.json @@ -6,10 +6,12 @@ "homepage": "https://enqueue.forma-pro.com/", "license": "MIT", "require": { - "php": "^7.4|^8.0", + "php": "^7.3|^7.4|^8.0", "queue-interop/queue-interop": "^0.8", "google/cloud-pubsub": "^1.4.3", - "enqueue/dsn": "^0.10" + "enqueue/dsn": "^0.10", + "google/protobuf": "^3.25", + "grpc/grpc": "^1.57" }, "require-dev": { "phpunit/phpunit": "^9.5", diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..e20fd90 --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,16 @@ +version: "3.4" + +services: + php: + build: + context: . + dockerfile: Dockerfile + restart: unless-stopped + volumes: + - .:/app + command: sh -c "composer install && php-fpm" + healthcheck: + interval: 10s + timeout: 3s + retries: 3 + start_period: 30s \ No newline at end of file