diff --git a/src/PubNub/Endpoints/MessagePersistance/FetchMessages.php b/src/PubNub/Endpoints/MessagePersistance/FetchMessages.php new file mode 100644 index 00000000..4fd2e698 --- /dev/null +++ b/src/PubNub/Endpoints/MessagePersistance/FetchMessages.php @@ -0,0 +1,234 @@ + 'start', + 'end' => 'end', + 'count' => 'max', + 'includeMeta' => 'include_meta', + 'includeUuid' => 'include_uuid', + 'includeMessageType' => 'include_message_type', + 'includeType' => 'include_type', + 'includeSpaceId' => 'include_space_id', + ]; + + public function channels(...$channel) + { + if (is_array($channel[0])) { + $this->channels = $channel[0]; + } elseif (strpos($channel[0], ',')) { + $this->channels = array_map('trim', explode(',', $channel[0])); + } else { + $this->channels = $channel; + } + return $this; + } + + public function start($start) + { + $this->start = $start; + return $this; + } + + public function end($end) + { + $this->end = $end; + return $this; + } + + public function count($count) + { + $this->count = $count; + return $this; + } + + public function includeMeta($includeMeta) + { + $this->includeMeta = $includeMeta; + return $this; + } + + public function includeUuid($includeUuid) + { + $this->includeUuid = $includeUuid; + return $this; + } + + public function includeMessageType($includeMessageType) + { + $this->includeMessageType = $includeMessageType; + return $this; + } + + public function includeMessageActions($includeMessageActions) + { + $this->includeMessageActions = $includeMessageActions; + return $this; + } + + public function includeType($includeType) + { + $this->includeType = $includeType; + return $this; + } + + public function includeSpaceId($includeSpaceId) + { + $this->includeSpaceId = $includeSpaceId; + return $this; + } + + /** + * @throws PubNubValidationException + */ + protected function validateParams() + { + if (!is_array($this->channels) || count($this->channels) === 0) { + throw new PubNubValidationException("Channel Missing"); + } + + $this->validateSubscribeKey(); + $this->validatePublishKey(); + } + + /** + * @return array + */ + protected function customParams() + { + $params = []; + foreach ($this->customParamMapping as $customParam => $requestParam) { + if (isset($this->$customParam)) { + $params[$requestParam] = $this->$customParam; + } + } + + return $params; + } + + /** + * @return string + * @throws PubNubBuildRequestException + */ + protected function buildPath() + { + $withActions = $this->includeMessageActions ? '-with-actions' : ''; + $channelList = $this->includeMessageActions + ? PubNubUtil::urlEncode($this->channels[0]) + : implode(',', array_map(fn($channel) => PubNubUtil::urlEncode($channel), $this->channels)); + + return sprintf( + self::GET_PATH, + $withActions, + $this->pubnub->getConfiguration()->getSubscribeKey(), + $channelList, + ); + } + + public function sync(): PNFetchMessagesResult + { + return parent::sync(); + } + + /** + * @param array $json Decoded json + * @return PNPublishResult + */ + protected function createResponse($json) + { + return PNFetchMessagesResult::fromJson( + $json, + $this->pubnub->getCrypto(), + isset($this->start) ? $this->start : null, + isset($this->end) ? $this->end : null + ); + } + + /** + * @return bool + */ + protected function isAuthRequired() + { + return true; + } + + protected function buildData() + { + return null; + } + + /** + * @return int + */ + protected function getRequestTimeout() + { + return $this->pubnub->getConfiguration()->getNonSubscribeRequestTimeout(); + } + + /** + * @return int + */ + protected function getConnectTimeout() + { + return $this->pubnub->getConfiguration()->getConnectTimeout(); + } + + /** + * @return string + */ + protected function httpMethod() + { + return PNHttpMethod::GET; + } + + /** + * @return int + */ + protected function getOperationType() + { + return PNOperationType::PNFetchMessagesOperation; + } + + /** + * @return string + */ + protected function getName() + { + return "Fetch Messages"; + } +} diff --git a/src/PubNub/Models/Consumer/MessagePersistence/PNFetchMessagesItemResult.php b/src/PubNub/Models/Consumer/MessagePersistence/PNFetchMessagesItemResult.php new file mode 100644 index 00000000..c65deb55 --- /dev/null +++ b/src/PubNub/Models/Consumer/MessagePersistence/PNFetchMessagesItemResult.php @@ -0,0 +1,111 @@ +message = $message; + $this->timetoken = $timetoken; + } + + public function setMetadata(mixed $metadata) + { + $this->metadata = $metadata; + return $this; + } + + public function setActions(mixed $actions) + { + $this->actions = $actions; + return $this; + } + + public function setUuid(string $uuid) + { + $this->uuid = $uuid; + return $this; + } + + public function setMessageType(string $messageType) + { + $this->messageType = $messageType; + return $this; + } + + public function getMessage(): mixed + { + return $this->message; + } + + public function getTimetoken(): string + { + return $this->timetoken; + } + + public function getMetadata(): mixed + { + return $this->metadata; + } + + public function getActions(): mixed + { + return $this->actions; + } + + public function getUuid(): string + { + return $this->uuid; + } + + public function getMessageType(): string + { + return $this->messageType; + } + + public static function fromJson($json, $crypto): static + { + $message = $json['message']; + if ($crypto) { + $message = $crypto->decrypt($message); + } + $item = new static( + $message, + $json['timetoken'], + ); + + if (isset($json['uuid'])) { + $item->setUuid($json['uuid']); + } + + if (isset($json['message_type'])) { + $item->setMessageType($json['message_type']); + } + + if (isset($json['meta'])) { + $item->setMetadata($json['meta']); + } + + if (isset($json['actions'])) { + $item->setActions($json['actions']); + } else { + $item->setActions([]); + } + + return $item; + } + + public function __toString(): string + { + return sprintf("Fetch message item with tt: %s and content: %s", $this->timetoken, $this->message); + } +} diff --git a/src/PubNub/Models/Consumer/MessagePersistence/PNFetchMessagesResult.php b/src/PubNub/Models/Consumer/MessagePersistence/PNFetchMessagesResult.php new file mode 100644 index 00000000..4e82d5af --- /dev/null +++ b/src/PubNub/Models/Consumer/MessagePersistence/PNFetchMessagesResult.php @@ -0,0 +1,62 @@ +channels = $channels; + $this->startTimetoken = $startTimetoken; + $this->endTimetoken = $endTimetoken; + } + + public function __toString() + { + return sprintf("Fetch messages result for range %d..%d", $this->startTimetoken, $this->endTimetoken); + } + + public static function fromJson($jsonInput, $crypto, $startTimetoken, $endTimetoken) + { + $channels = []; + + foreach ($jsonInput['channels'] as $channel => $messages) { + foreach ($messages as $item) { + $channels[$channel][] = PNFetchMessagesItemResult::fromJson($item, $crypto); + } + } + return new static($channels, $startTimetoken, $endTimetoken); + } + + public function getChannels() + { + return $this->channels; + } + + /** + * @return int + */ + public function getStartTimetoken() + { + return $this->startTimetoken; + } + + /** + * @return int + */ + public function getEndTimetoken() + { + return $this->endTimetoken; + } +} diff --git a/src/PubNub/PubNub.php b/src/PubNub/PubNub.php index 419fa7fa..72c6cd6f 100644 --- a/src/PubNub/PubNub.php +++ b/src/PubNub/PubNub.php @@ -17,6 +17,7 @@ use PubNub\Endpoints\History; use PubNub\Endpoints\HistoryDelete; use PubNub\Endpoints\MessageCount; +use PubNub\Endpoints\MessagePersistance\FetchMessages; use PubNub\Endpoints\Objects\Channel\SetChannelMetadata; use PubNub\Endpoints\Objects\Channel\GetChannelMetadata; use PubNub\Endpoints\Objects\Channel\GetAllChannelMetadata; @@ -560,12 +561,12 @@ public function setToken($token) return $this->tokenManager->setToken($token); } - public function getCrypto(): CryptoModule + public function getCrypto(): CryptoModule | null { if ($this->cryptoModule) { return $this->cryptoModule; } else { - return $this->configuration->getCrypto(); + return $this->configuration->getCryptoSafe(); } } @@ -573,4 +574,9 @@ public function setCrypto(CryptoModule $cryptoModule) { $this->cryptoModule = $cryptoModule; } + + public function fetchMessages(): FetchMessages + { + return new FetchMessages($this); + } } diff --git a/tests/integrational/FetchMessagesTest.php b/tests/integrational/FetchMessagesTest.php new file mode 100644 index 00000000..2d5360d5 --- /dev/null +++ b/tests/integrational/FetchMessagesTest.php @@ -0,0 +1,130 @@ +pubnub->publish() + ->channel(self::CHANNEL_NAME) + ->message('hello ' . self::CHANNEL_NAME . ' channel. First message') + ->meta(['FIRST_MESSAGE' => true]) + ->sync(); + + $this->startTimetoken = $firstMessage->getTimetoken(); + $middleMessage = round(self::MESSAGE_COUNT / 2); + + for ($i = 2; $i <= self::MESSAGE_COUNT; $i++) { + $messageResult = $this->pubnub->publish() + ->channel(self::CHANNEL_NAME) + ->message('hello ' . self::CHANNEL_NAME . ' channel. Message: ' . $i) + ->sync(); + + if ($i == $middleMessage) { + $this->middleTimetoken = $messageResult->getTimetoken(); + } + + $this->endTimetoken = $messageResult->getTimetoken(); + }; + + $this->pubnub_enc->publish() + ->channel(self::ENCRYPTED_CHANNEL_NAME) + ->message('Hey. This one is a secret ;-)') + ->sync(); + } + + public function tearDown(): void + { + parent::tearDown(); + $this->pubnub->deleteMessages() + ->channel(self::CHANNEL_NAME) + ->sync(); + $this->pubnub->deleteMessages() + ->channel(self::ENCRYPTED_CHANNEL_NAME) + ->sync(); + } + + public function testFetchMessages() + { + $this->caseFetchWithDefaults(); + $this->caseFetchWithCount(); + $this->caseFetchWithStartEnd(); + $this->caseFetchEncrypted(); + } + + protected function caseFetchWithDefaults() + { + $messages = $this->pubnub->fetchMessages() + ->channels(self::CHANNEL_NAME) + ->count(100) + ->sync(); + + $this->assertInstanceOf(PNFetchMessagesResult::class, $messages); + + $this->assertEquals( + self::MESSAGE_COUNT, + count($messages->getChannels()[self::CHANNEL_NAME]) + ); + } + + protected function caseFetchWithCount() + { + $messages = $this->pubnub->fetchMessages() + ->channels(self::CHANNEL_NAME) + ->count(5) + ->sync(); + + $this->assertInstanceOf(PNFetchMessagesResult::class, $messages); + + $this->assertEquals(5, count($messages->getChannels()[self::CHANNEL_NAME])); + } + + protected function caseFetchWithStartEnd() + { + $messages = $this->pubnub->fetchMessages() + ->channels(self::CHANNEL_NAME) + ->start($this->middleTimetoken - 100) + ->end($this->middleTimetoken + 100) + ->sync(); + + $this->assertInstanceOf(PNFetchMessagesResult::class, $messages); + $middleMessage = round(self::MESSAGE_COUNT / 2); + $this->assertEquals(1, count($messages->getChannels()[self::CHANNEL_NAME])); + $this->assertEquals( + 'hello ' . self::CHANNEL_NAME . ' channel. Message: ' . $middleMessage, + $messages->getChannels()[self::CHANNEL_NAME][0]->getMessage() + ); + } + + protected function caseFetchEncrypted() + { + + $messages = $this->pubnub_enc->fetchMessages() + ->channels(self::ENCRYPTED_CHANNEL_NAME) + ->sync(); + + $this->assertInstanceOf(PNFetchMessagesResult::class, $messages); + $this->assertEquals(1, count($messages->getChannels()[self::ENCRYPTED_CHANNEL_NAME])); + + $this->assertEquals( + 'Hey. This one is a secret ;-)', + $messages->getChannels()[self::ENCRYPTED_CHANNEL_NAME][0]->getMessage() + ); + } +}