diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 0313d4b7..bba575e8 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest services: typesense: - image: typesense/typesense:27.0.rc21 + image: typesense/typesense:28.0.rc36 ports: - 8108:8108/tcp volumes: diff --git a/src/Client.php b/src/Client.php index 086fa493..6821147b 100644 --- a/src/Client.php +++ b/src/Client.php @@ -75,6 +75,11 @@ class Client */ public Analytics $analytics; + /** + * @var Stemming + */ + public Stemming $stemming; + /** * @var Conversations */ @@ -108,6 +113,7 @@ public function __construct(array $config) $this->multiSearch = new MultiSearch($this->apiCall); $this->presets = new Presets($this->apiCall); $this->analytics = new Analytics($this->apiCall); + $this->stemming = new Stemming($this->apiCall); $this->conversations = new Conversations($this->apiCall); } @@ -199,6 +205,14 @@ public function getAnalytics(): Analytics return $this->analytics; } + /** + * @return Stemming + */ + public function getStemming(): Stemming + { + return $this->stemming; + } + /** * @return Conversations */ diff --git a/src/Conversations.php b/src/Conversations.php index 3ec1d309..c0ccfe92 100644 --- a/src/Conversations.php +++ b/src/Conversations.php @@ -37,15 +37,6 @@ public function __construct(ApiCall $apiCall) $this->typesenseModels = new ConversationModels($this->apiCall); } - /** - * @return array - * @throws TypesenseClientError|HttpClientException - */ - public function retrieve(): array - { - return $this->apiCall->get(static::RESOURCE_PATH, []); - } - /** * @return Models */ diff --git a/src/Stemming.php b/src/Stemming.php new file mode 100644 index 00000000..0b72be33 --- /dev/null +++ b/src/Stemming.php @@ -0,0 +1,26 @@ +apiCall = $apiCall; + } + + public function dictionaries() + { + if (!isset($this->typesenseDictionaries)) { + $this->typesenseDictionaries = new StemmingDictionaries($this->apiCall); + } + return $this->typesenseDictionaries; + } +} diff --git a/src/StemmingDictionaries.php b/src/StemmingDictionaries.php new file mode 100644 index 00000000..1a5d66c2 --- /dev/null +++ b/src/StemmingDictionaries.php @@ -0,0 +1,99 @@ +apiCall = $apiCall; + } + + public function __get($id) + { + if (!isset($this->typesenseDictionaries[$id])) { + $this->typesenseDictionaries[$id] = new StemmingDictionary($id, $this->apiCall); + } + return $this->typesenseDictionaries[$id]; + } + + public function upsert($id, $wordRootCombinations) + { + $dictionaryInJSONLFormat = is_array($wordRootCombinations) ? implode( + "\n", + array_map( + static fn(array $wordRootCombo) => json_encode($wordRootCombo, JSON_THROW_ON_ERROR), + $wordRootCombinations + ) + ) : $wordRootCombinations; + + $resultsInJSONLFormat = $this->apiCall->post($this->endpoint_path("import"), $dictionaryInJSONLFormat, false, ["id" => $id]); + + return is_array($wordRootCombinations) ? array_map( + static function ($item) { + return json_decode($item, true, 512, JSON_THROW_ON_ERROR); + }, + array_filter( + explode("\n", $resultsInJSONLFormat), + 'strlen' + ) + ) : $resultsInJSONLFormat; + } + + public function retrieve() + { + $response = $this->apiCall->get(StemmingDictionaries::RESOURCE_PATH, []); + + // If response is null, return empty dictionaries structure + if ($response === null) { + return ['dictionaries' => []]; + } + return $response; + } + + private function endpoint_path($operation = null) + { + return $operation === null ? self::RESOURCE_PATH : self::RESOURCE_PATH . "/" . encodeURIComponent($operation); + } + + /** + * @inheritDoc + */ + public function offsetExists($offset): bool + { + return isset($this->typesenseDictionaries[$offset]); + } + + /** + * @inheritDoc + */ + public function offsetGet($offset): StemmingDictionary + { + if (!isset($this->typesenseDictionaries[$offset])) { + $this->typesenseDictionaries[$offset] = new StemmingDictionary($offset, $this->apiCall); + } + + return $this->typesenseDictionaries[$offset]; + } + + /** + * @inheritDoc + */ + public function offsetSet($offset, $value): void + { + $this->typesenseDictionaries[$offset] = $value; + } + + /** + * @inheritDoc + */ + public function offsetUnset($offset): void + { + unset($this->typesenseDictionaries[$offset]); + } +} diff --git a/src/StemmingDictionary.php b/src/StemmingDictionary.php new file mode 100644 index 00000000..6fbdb5c1 --- /dev/null +++ b/src/StemmingDictionary.php @@ -0,0 +1,25 @@ +id = $id; + $this->apiCall = $apiCall; + } + + public function retrieve() + { + return $this->apiCall->get($this->endpointPath(), []); + } + + private function endpointPath() + { + return StemmingDictionaries::RESOURCE_PATH . '/' . encodeURIComponent($this->id); + } +} diff --git a/tests/Feature/ConversationTest.php b/tests/Feature/ConversationTest.php index 01fc495c..26449705 100644 --- a/tests/Feature/ConversationTest.php +++ b/tests/Feature/ConversationTest.php @@ -37,6 +37,6 @@ public function testCanDeleteAConversation(): void private function endPointPath(): string { - return sprintf('%s/%s', ConversationsTest::RESOURCE_PATH, $this->id); + return sprintf('%s/%s', "/conversations", $this->id); } } diff --git a/tests/Feature/ConversationsTest.php b/tests/Feature/ConversationsTest.php deleted file mode 100644 index 81ab63fc..00000000 --- a/tests/Feature/ConversationsTest.php +++ /dev/null @@ -1,19 +0,0 @@ -mockApiCall()->allows()->get(static::RESOURCE_PATH, [])->andReturns([]); - $this->mockConversations()->retrieve(); - - $response = $this->client()->conversations->retrieve(); - $this->assertEquals([], $response); - } -} diff --git a/tests/Feature/StemmingDictionariesTest.php b/tests/Feature/StemmingDictionariesTest.php new file mode 100644 index 00000000..1ef240d7 --- /dev/null +++ b/tests/Feature/StemmingDictionariesTest.php @@ -0,0 +1,47 @@ + "exampleRoot1", "word" => "exampleWord1"], + ["root" => "exampleRoot2", "word" => "exampleWord2"] + ]; + + private $dictionaryUpsertResponse = null; + + protected function setUp(): void + { + parent::setUp(); + + $this->client()->stemming->dictionaries()->upsert( + $this->dictionaryId, + $this->dictionary + ); + } + + public function testCanUpsertADictionary(): void + { + $this->dictionaryUpsertResponse = $this->client()->stemming->dictionaries()->upsert($this->dictionaryId, $this->dictionary); + $this->assertEquals($this->dictionary, $this->dictionaryUpsertResponse); + } + + public function testCanRetrieveADictionary(): void + { + $returnData = $this->client()->stemming->dictionaries()[$this->dictionaryId]->retrieve(); + $this->assertEquals($returnData['id'], $this->dictionaryId); + } + + + public function testCanRetrieveAllDicitionaries(): void + { + $returnData = $this->client()->stemming->dictionaries()->retrieve(); + $this->assertCount(1, $returnData['dictionaries']); + $this->assertEquals($returnData['dictionaries'][0], $this->dictionaryId); + } +}