diff --git a/src/Client.php b/src/Client.php index a4f8a084..070ab24d 100644 --- a/src/Client.php +++ b/src/Client.php @@ -90,6 +90,11 @@ class Client */ public NLSearchModels $nlSearchModels; + /** + * @var SynonymSets + */ + public SynonymSets $synonymSets; + /** * @var ApiCall */ @@ -121,6 +126,7 @@ public function __construct(array $config) $this->stemming = new Stemming($this->apiCall); $this->conversations = new Conversations($this->apiCall); $this->nlSearchModels = new NLSearchModels($this->apiCall); + $this->synonymSets = new SynonymSets($this->apiCall); } /** @@ -234,4 +240,12 @@ public function getNLSearchModels(): NLSearchModels { return $this->nlSearchModels; } + + /** + * @return SynonymSets + */ + public function getSynonymSets(): SynonymSets + { + return $this->synonymSets; + } } diff --git a/src/SynonymSet.php b/src/SynonymSet.php new file mode 100644 index 00000000..95aa4775 --- /dev/null +++ b/src/SynonymSet.php @@ -0,0 +1,77 @@ +synonymSetName = $synonymSetName; + $this->apiCall = $apiCall; + } + + /** + * @return string + */ + private function endPointPath(): string + { + return sprintf( + '%s/%s', + SynonymSets::RESOURCE_PATH, + encodeURIComponent($this->synonymSetName) + ); + } + + /** + * @param array $params + * + * @return array + * @throws TypesenseClientError|HttpClientException + */ + public function upsert(array $params): array + { + return $this->apiCall->put($this->endPointPath(), $params); + } + + /** + * @return array + * @throws TypesenseClientError|HttpClientException + */ + public function retrieve(): array + { + return $this->apiCall->get($this->endPointPath(), []); + } + + /** + * @return array + * @throws TypesenseClientError|HttpClientException + */ + public function delete(): array + { + return $this->apiCall->delete($this->endPointPath()); + } +} \ No newline at end of file diff --git a/src/SynonymSets.php b/src/SynonymSets.php new file mode 100644 index 00000000..e99f1200 --- /dev/null +++ b/src/SynonymSets.php @@ -0,0 +1,93 @@ +apiCall = $apiCall; + } + + /** + * @param string $synonymSetName + * @param array $config + * + * @return array + * @throws TypesenseClientError|HttpClientException + */ + public function upsert(string $synonymSetName, array $config): array + { + return $this->apiCall->put(sprintf('%s/%s', static::RESOURCE_PATH, encodeURIComponent($synonymSetName)), $config); + } + + /** + * @return array + * @throws TypesenseClientError|HttpClientException + */ + public function retrieve(): array + { + return $this->apiCall->get(static::RESOURCE_PATH, []); + } + + /** + * @inheritDoc + */ + public function offsetExists($synonymSetName): bool + { + return isset($this->synonymSets[$synonymSetName]); + } + + /** + * @inheritDoc + */ + public function offsetGet($synonymSetName): SynonymSet + { + if (!isset($this->synonymSets[$synonymSetName])) { + $this->synonymSets[$synonymSetName] = new SynonymSet($synonymSetName, $this->apiCall); + } + + return $this->synonymSets[$synonymSetName]; + } + + /** + * @inheritDoc + */ + public function offsetSet($synonymSetName, $value): void + { + $this->synonymSets[$synonymSetName] = $value; + } + + /** + * @inheritDoc + */ + public function offsetUnset($synonymSetName): void + { + unset($this->synonymSets[$synonymSetName]); + } +} \ No newline at end of file diff --git a/tests/Feature/AnalyticsEventsTest.php b/tests/Feature/AnalyticsEventsTest.php index c1fa70d6..6b9a433c 100644 --- a/tests/Feature/AnalyticsEventsTest.php +++ b/tests/Feature/AnalyticsEventsTest.php @@ -3,6 +3,7 @@ namespace Feature; use Tests\TestCase; +use Exception; class AnalyticsEventsTest extends TestCase { @@ -11,6 +12,11 @@ class AnalyticsEventsTest extends TestCase protected function setUp(): void { parent::setUp(); + + if ($this->isV30OrAbove()) { + $this->markTestSkipped('Analytics is deprecated in Typesense v30+'); + } + $this->client()->collections->create([ "name" => "products", "fields" => [ @@ -52,7 +58,13 @@ protected function setUp(): void protected function tearDown(): void { parent::tearDown(); - $this->client()->analytics->rules()->{'product_queries_aggregation'}->delete(); + + if (!$this->isV30OrAbove()) { + try { + $this->client()->analytics->rules()->{'product_queries_aggregation'}->delete(); + } catch (Exception $e) { + } + } } public function testCanCreateAnEvent(): void diff --git a/tests/Feature/AnalyticsRulesTest.php b/tests/Feature/AnalyticsRulesTest.php index 882ef896..e5d84ead 100644 --- a/tests/Feature/AnalyticsRulesTest.php +++ b/tests/Feature/AnalyticsRulesTest.php @@ -4,6 +4,7 @@ use Tests\TestCase; use Typesense\Exceptions\ObjectNotFound; +use Exception; class AnalyticsRulesTest extends TestCase { @@ -26,14 +27,26 @@ class AnalyticsRulesTest extends TestCase protected function setUp(): void { parent::setUp(); + + if ($this->isV30OrAbove()) { + $this->markTestSkipped('Analytics is deprecated in Typesense v30+'); + } + $this->ruleUpsertResponse = $this->client()->analytics->rules()->upsert($this->ruleName, $this->ruleConfiguration); } protected function tearDown(): void { - $rules = $this->client()->analytics->rules()->retrieve(); - foreach ($rules['rules'] as $rule) { - $this->client()->analytics->rules()->{$rule['name']}->delete(); + if (!$this->isV30OrAbove()) { + try { + $rules = $this->client()->analytics->rules()->retrieve(); + if (is_array($rules) && isset($rules['rules'])) { + foreach ($rules['rules'] as $rule) { + $this->client()->analytics->rules()->{$rule['name']}->delete(); + } + } + } catch (Exception $e) { + } } } diff --git a/tests/Feature/SynonymSetsTest.php b/tests/Feature/SynonymSetsTest.php new file mode 100644 index 00000000..8a6ff9ea --- /dev/null +++ b/tests/Feature/SynonymSetsTest.php @@ -0,0 +1,61 @@ + [ + [ + 'id' => 'dummy', + 'synonyms' => ['foo', 'bar', 'baz'], + 'root' => '', + ], + ], + ]; + + protected function setUp(): void + { + parent::setUp(); + + if (!$this->isV30OrAbove()) { + $this->markTestSkipped('SynonymSets is only supported in Typesense v30+'); + } + + $this->synonymSets = $this->client()->synonymSets; + $this->upsertResponse = $this->synonymSets->upsert('test-synonym-set', $this->synonymSetData); + } + + + public function testCanUpsertASynonymSet(): void + { + $this->assertEquals($this->synonymSetData['synonyms'], $this->upsertResponse['synonyms']); + } + + public function testCanRetrieveAllSynonymSets(): void + { + $returnData = $this->synonymSets->retrieve(); + $this->assertCount(1, $returnData); + } + + public function testCanRetrieveASpecificSynonymSet(): void + { + $returnData = $this->synonymSets['test-synonym-set']->retrieve(); + $this->assertEquals($this->synonymSetData['synonyms'], $returnData['synonyms']); + } + + public function testCanDeleteASynonymSet(): void + { + $returnData = $this->synonymSets['test-synonym-set']->delete(); + $this->assertEquals('test-synonym-set', $returnData['name']); + + $this->expectException(ObjectNotFound::class); + $this->synonymSets['test-synonym-set']->retrieve(); + } +} \ No newline at end of file diff --git a/tests/Feature/SynonymsTest.php b/tests/Feature/SynonymsTest.php index 6cd5db05..5a61b871 100644 --- a/tests/Feature/SynonymsTest.php +++ b/tests/Feature/SynonymsTest.php @@ -17,6 +17,11 @@ class SynonymsTest extends TestCase protected function setUp(): void { parent::setUp(); + + if ($this->isV30OrAbove()) { + $this->markTestSkipped('Synonyms is deprecated in Typesense v30+, use SynonymSets instead'); + } + $this->setUpCollection('books'); $this->synonyms = $this->client()->collections['books']->synonyms; diff --git a/tests/TestCase.php b/tests/TestCase.php index 6a26447b..b9852b11 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -6,6 +6,7 @@ use Typesense\Client; use Mockery; use Typesense\ApiCall; +use Exception; abstract class TestCase extends BaseTestCase { @@ -98,4 +99,25 @@ protected function tearDownTypesense(): void $this->typesenseClient->collections[$collection['name']]->delete(); } } + + protected function isV30OrAbove(): bool + { + try { + $debug = $this->typesenseClient->debug->retrieve(); + $version = $debug['version']; + + if ($version === 'nightly') { + return true; + } + + if (preg_match('/^v(\d+)/', $version, $matches)) { + $majorVersion = (int) $matches[1]; + return $majorVersion >= 30; + } + + return false; + } catch (Exception $e) { + return false; + } + } }